|
Blender
V2.59
|
00001 /* 00002 * $Id: MOD_cast.c 38300 2011-07-11 09:15:20Z blendix $ 00003 * 00004 * ***** BEGIN GPL LICENSE BLOCK ***** 00005 * 00006 * This program is free software; you can redistribute it and/or 00007 * modify it under the terms of the GNU General Public License 00008 * as published by the Free Software Foundation; either version 2 00009 * of the License, or (at your option) any later version. 00010 * 00011 * This program is distributed in the hope that it will be useful, 00012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00014 * GNU General Public License for more details. 00015 * 00016 * You should have received a copy of the GNU General Public License 00017 * along with this program; if not, write to the Free Software Foundation, 00018 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 00019 * 00020 * The Original Code is Copyright (C) 2005 by the Blender Foundation. 00021 * All rights reserved. 00022 * 00023 * Contributor(s): Daniel Dunbar 00024 * Ton Roosendaal, 00025 * Ben Batt, 00026 * Brecht Van Lommel, 00027 * Campbell Barton 00028 * 00029 * ***** END GPL LICENSE BLOCK ***** 00030 * 00031 */ 00032 00038 #include "DNA_meshdata_types.h" 00039 #include "DNA_object_types.h" 00040 00041 #include "BLI_math.h" 00042 #include "BLI_utildefines.h" 00043 00044 00045 #include "BKE_deform.h" 00046 #include "BKE_DerivedMesh.h" 00047 #include "BKE_modifier.h" 00048 00049 00050 #include "depsgraph_private.h" 00051 00052 #include "MOD_util.h" 00053 00054 static void initData(ModifierData *md) 00055 { 00056 CastModifierData *cmd = (CastModifierData*) md; 00057 00058 cmd->fac = 0.5f; 00059 cmd->radius = 0.0f; 00060 cmd->size = 0.0f; 00061 cmd->flag = MOD_CAST_X | MOD_CAST_Y | MOD_CAST_Z 00062 | MOD_CAST_SIZE_FROM_RADIUS; 00063 cmd->type = MOD_CAST_TYPE_SPHERE; 00064 cmd->defgrp_name[0] = '\0'; 00065 cmd->object = NULL; 00066 } 00067 00068 00069 static void copyData(ModifierData *md, ModifierData *target) 00070 { 00071 CastModifierData *cmd = (CastModifierData*) md; 00072 CastModifierData *tcmd = (CastModifierData*) target; 00073 00074 tcmd->fac = cmd->fac; 00075 tcmd->radius = cmd->radius; 00076 tcmd->size = cmd->size; 00077 tcmd->flag = cmd->flag; 00078 tcmd->type = cmd->type; 00079 tcmd->object = cmd->object; 00080 strncpy(tcmd->defgrp_name, cmd->defgrp_name, 32); 00081 } 00082 00083 static int isDisabled(ModifierData *md, int UNUSED(useRenderParams)) 00084 { 00085 CastModifierData *cmd = (CastModifierData*) md; 00086 short flag; 00087 00088 flag = cmd->flag & (MOD_CAST_X|MOD_CAST_Y|MOD_CAST_Z); 00089 00090 if((cmd->fac == 0.0f) || flag == 0) return 1; 00091 00092 return 0; 00093 } 00094 00095 static CustomDataMask requiredDataMask(Object *UNUSED(ob), ModifierData *md) 00096 { 00097 CastModifierData *cmd = (CastModifierData *)md; 00098 CustomDataMask dataMask = 0; 00099 00100 /* ask for vertexgroups if we need them */ 00101 if(cmd->defgrp_name[0]) dataMask |= CD_MASK_MDEFORMVERT; 00102 00103 return dataMask; 00104 } 00105 00106 static void foreachObjectLink( 00107 ModifierData *md, Object *ob, 00108 void (*walk)(void *userData, Object *ob, Object **obpoin), 00109 void *userData) 00110 { 00111 CastModifierData *cmd = (CastModifierData*) md; 00112 00113 walk (userData, ob, &cmd->object); 00114 } 00115 00116 static void updateDepgraph(ModifierData *md, DagForest *forest, 00117 struct Scene *UNUSED(scene), 00118 Object *UNUSED(ob), 00119 DagNode *obNode) 00120 { 00121 CastModifierData *cmd = (CastModifierData*) md; 00122 00123 if (cmd->object) { 00124 DagNode *curNode = dag_get_node(forest, cmd->object); 00125 00126 dag_add_relation(forest, curNode, obNode, DAG_RL_OB_DATA, 00127 "Cast Modifier"); 00128 } 00129 } 00130 00131 static void sphere_do( 00132 CastModifierData *cmd, Object *ob, DerivedMesh *dm, 00133 float (*vertexCos)[3], int numVerts) 00134 { 00135 MDeformVert *dvert = NULL; 00136 00137 Object *ctrl_ob = NULL; 00138 00139 int i, defgrp_index; 00140 int has_radius = 0; 00141 short flag, type; 00142 float fac, facm, len = 0.0f; 00143 float vec[3], center[3] = {0.0f, 0.0f, 0.0f}; 00144 float mat[4][4], imat[4][4]; 00145 00146 fac = cmd->fac; 00147 facm = 1.0f - fac; 00148 00149 flag = cmd->flag; 00150 type = cmd->type; /* projection type: sphere or cylinder */ 00151 00152 if (type == MOD_CAST_TYPE_CYLINDER) 00153 flag &= ~MOD_CAST_Z; 00154 00155 ctrl_ob = cmd->object; 00156 00157 /* spherify's center is {0, 0, 0} (the ob's own center in its local 00158 * space), by default, but if the user defined a control object, 00159 * we use its location, transformed to ob's local space */ 00160 if (ctrl_ob) { 00161 if(flag & MOD_CAST_USE_OB_TRANSFORM) { 00162 invert_m4_m4(ctrl_ob->imat, ctrl_ob->obmat); 00163 mul_m4_m4m4(mat, ob->obmat, ctrl_ob->imat); 00164 invert_m4_m4(imat, mat); 00165 } 00166 00167 invert_m4_m4(ob->imat, ob->obmat); 00168 mul_v3_m4v3(center, ob->imat, ctrl_ob->obmat[3]); 00169 } 00170 00171 /* now we check which options the user wants */ 00172 00173 /* 1) (flag was checked in the "if (ctrl_ob)" block above) */ 00174 /* 2) cmd->radius > 0.0f: only the vertices within this radius from 00175 * the center of the effect should be deformed */ 00176 if (cmd->radius > FLT_EPSILON) has_radius = 1; 00177 00178 /* 3) if we were given a vertex group name, 00179 * only those vertices should be affected */ 00180 modifier_get_vgroup(ob, dm, cmd->defgrp_name, &dvert, &defgrp_index); 00181 00182 if(flag & MOD_CAST_SIZE_FROM_RADIUS) { 00183 len = cmd->radius; 00184 } 00185 else { 00186 len = cmd->size; 00187 } 00188 00189 if(len <= 0) { 00190 for (i = 0; i < numVerts; i++) { 00191 len += len_v3v3(center, vertexCos[i]); 00192 } 00193 len /= numVerts; 00194 00195 if (len == 0.0f) len = 10.0f; 00196 } 00197 00198 /* ready to apply the effect, one vertex at a time; 00199 * tiny optimization: the code is separated (with parts repeated) 00200 * in two possible cases: 00201 * with or w/o a vgroup. With lots of if's in the code below, 00202 * further optimizations are possible, if needed */ 00203 if (dvert) { /* with a vgroup */ 00204 float fac_orig = fac; 00205 for (i = 0; i < numVerts; i++) { 00206 MDeformWeight *dw = NULL; 00207 int j; 00208 float tmp_co[3]; 00209 00210 copy_v3_v3(tmp_co, vertexCos[i]); 00211 if(ctrl_ob) { 00212 if(flag & MOD_CAST_USE_OB_TRANSFORM) { 00213 mul_m4_v3(mat, tmp_co); 00214 } else { 00215 sub_v3_v3(tmp_co, center); 00216 } 00217 } 00218 00219 copy_v3_v3(vec, tmp_co); 00220 00221 if (type == MOD_CAST_TYPE_CYLINDER) 00222 vec[2] = 0.0f; 00223 00224 if (has_radius) { 00225 if (len_v3(vec) > cmd->radius) continue; 00226 } 00227 00228 for (j = 0; j < dvert[i].totweight; ++j) { 00229 if(dvert[i].dw[j].def_nr == defgrp_index) { 00230 dw = &dvert[i].dw[j]; 00231 break; 00232 } 00233 } 00234 if (!dw) continue; 00235 00236 fac = fac_orig * dw->weight; 00237 facm = 1.0f - fac; 00238 00239 normalize_v3(vec); 00240 00241 if (flag & MOD_CAST_X) 00242 tmp_co[0] = fac*vec[0]*len + facm*tmp_co[0]; 00243 if (flag & MOD_CAST_Y) 00244 tmp_co[1] = fac*vec[1]*len + facm*tmp_co[1]; 00245 if (flag & MOD_CAST_Z) 00246 tmp_co[2] = fac*vec[2]*len + facm*tmp_co[2]; 00247 00248 if(ctrl_ob) { 00249 if(flag & MOD_CAST_USE_OB_TRANSFORM) { 00250 mul_m4_v3(imat, tmp_co); 00251 } else { 00252 add_v3_v3(tmp_co, center); 00253 } 00254 } 00255 00256 copy_v3_v3(vertexCos[i], tmp_co); 00257 } 00258 return; 00259 } 00260 00261 /* no vgroup */ 00262 for (i = 0; i < numVerts; i++) { 00263 float tmp_co[3]; 00264 00265 copy_v3_v3(tmp_co, vertexCos[i]); 00266 if(ctrl_ob) { 00267 if(flag & MOD_CAST_USE_OB_TRANSFORM) { 00268 mul_m4_v3(mat, tmp_co); 00269 } else { 00270 sub_v3_v3(tmp_co, center); 00271 } 00272 } 00273 00274 copy_v3_v3(vec, tmp_co); 00275 00276 if (type == MOD_CAST_TYPE_CYLINDER) 00277 vec[2] = 0.0f; 00278 00279 if (has_radius) { 00280 if (len_v3(vec) > cmd->radius) continue; 00281 } 00282 00283 normalize_v3(vec); 00284 00285 if (flag & MOD_CAST_X) 00286 tmp_co[0] = fac*vec[0]*len + facm*tmp_co[0]; 00287 if (flag & MOD_CAST_Y) 00288 tmp_co[1] = fac*vec[1]*len + facm*tmp_co[1]; 00289 if (flag & MOD_CAST_Z) 00290 tmp_co[2] = fac*vec[2]*len + facm*tmp_co[2]; 00291 00292 if(ctrl_ob) { 00293 if(flag & MOD_CAST_USE_OB_TRANSFORM) { 00294 mul_m4_v3(imat, tmp_co); 00295 } else { 00296 add_v3_v3(tmp_co, center); 00297 } 00298 } 00299 00300 copy_v3_v3(vertexCos[i], tmp_co); 00301 } 00302 } 00303 00304 static void cuboid_do( 00305 CastModifierData *cmd, Object *ob, DerivedMesh *dm, 00306 float (*vertexCos)[3], int numVerts) 00307 { 00308 MDeformVert *dvert = NULL; 00309 Object *ctrl_ob = NULL; 00310 00311 int i, defgrp_index; 00312 int has_radius = 0; 00313 short flag; 00314 float fac, facm; 00315 float min[3], max[3], bb[8][3]; 00316 float center[3] = {0.0f, 0.0f, 0.0f}; 00317 float mat[4][4], imat[4][4]; 00318 00319 fac = cmd->fac; 00320 facm = 1.0f - fac; 00321 00322 flag = cmd->flag; 00323 00324 ctrl_ob = cmd->object; 00325 00326 /* now we check which options the user wants */ 00327 00328 /* 1) (flag was checked in the "if (ctrl_ob)" block above) */ 00329 /* 2) cmd->radius > 0.0f: only the vertices within this radius from 00330 * the center of the effect should be deformed */ 00331 if (cmd->radius > FLT_EPSILON) has_radius = 1; 00332 00333 /* 3) if we were given a vertex group name, 00334 * only those vertices should be affected */ 00335 modifier_get_vgroup(ob, dm, cmd->defgrp_name, &dvert, &defgrp_index); 00336 00337 if (ctrl_ob) { 00338 if(flag & MOD_CAST_USE_OB_TRANSFORM) { 00339 invert_m4_m4(ctrl_ob->imat, ctrl_ob->obmat); 00340 mul_m4_m4m4(mat, ob->obmat, ctrl_ob->imat); 00341 invert_m4_m4(imat, mat); 00342 } 00343 00344 invert_m4_m4(ob->imat, ob->obmat); 00345 mul_v3_m4v3(center, ob->imat, ctrl_ob->obmat[3]); 00346 } 00347 00348 if((flag & MOD_CAST_SIZE_FROM_RADIUS) && has_radius) { 00349 for(i = 0; i < 3; i++) { 00350 min[i] = -cmd->radius; 00351 max[i] = cmd->radius; 00352 } 00353 } else if(!(flag & MOD_CAST_SIZE_FROM_RADIUS) && cmd->size > 0) { 00354 for(i = 0; i < 3; i++) { 00355 min[i] = -cmd->size; 00356 max[i] = cmd->size; 00357 } 00358 } else { 00359 /* get bound box */ 00360 /* We can't use the object's bound box because other modifiers 00361 * may have changed the vertex data. */ 00362 INIT_MINMAX(min, max); 00363 00364 /* Cast's center is the ob's own center in its local space, 00365 * by default, but if the user defined a control object, we use 00366 * its location, transformed to ob's local space. */ 00367 if (ctrl_ob) { 00368 float vec[3]; 00369 00370 /* let the center of the ctrl_ob be part of the bound box: */ 00371 DO_MINMAX(center, min, max); 00372 00373 for (i = 0; i < numVerts; i++) { 00374 sub_v3_v3v3(vec, vertexCos[i], center); 00375 DO_MINMAX(vec, min, max); 00376 } 00377 } 00378 else { 00379 for (i = 0; i < numVerts; i++) { 00380 DO_MINMAX(vertexCos[i], min, max); 00381 } 00382 } 00383 00384 /* we want a symmetric bound box around the origin */ 00385 if (fabs(min[0]) > fabs(max[0])) max[0] = fabs(min[0]); 00386 if (fabs(min[1]) > fabs(max[1])) max[1] = fabs(min[1]); 00387 if (fabs(min[2]) > fabs(max[2])) max[2] = fabs(min[2]); 00388 min[0] = -max[0]; 00389 min[1] = -max[1]; 00390 min[2] = -max[2]; 00391 } 00392 00393 /* building our custom bounding box */ 00394 bb[0][0] = bb[2][0] = bb[4][0] = bb[6][0] = min[0]; 00395 bb[1][0] = bb[3][0] = bb[5][0] = bb[7][0] = max[0]; 00396 bb[0][1] = bb[1][1] = bb[4][1] = bb[5][1] = min[1]; 00397 bb[2][1] = bb[3][1] = bb[6][1] = bb[7][1] = max[1]; 00398 bb[0][2] = bb[1][2] = bb[2][2] = bb[3][2] = min[2]; 00399 bb[4][2] = bb[5][2] = bb[6][2] = bb[7][2] = max[2]; 00400 00401 /* ready to apply the effect, one vertex at a time; 00402 * tiny optimization: the code is separated (with parts repeated) 00403 * in two possible cases: 00404 * with or w/o a vgroup. With lots of if's in the code below, 00405 * further optimizations are possible, if needed */ 00406 if (dvert) { /* with a vgroup */ 00407 float fac_orig = fac; 00408 for (i = 0; i < numVerts; i++) { 00409 MDeformWeight *dw = NULL; 00410 int j, octant, coord; 00411 float d[3], dmax, apex[3], fbb; 00412 float tmp_co[3]; 00413 00414 copy_v3_v3(tmp_co, vertexCos[i]); 00415 if(ctrl_ob) { 00416 if(flag & MOD_CAST_USE_OB_TRANSFORM) { 00417 mul_m4_v3(mat, tmp_co); 00418 } else { 00419 sub_v3_v3(tmp_co, center); 00420 } 00421 } 00422 00423 if (has_radius) { 00424 if (fabsf(tmp_co[0]) > cmd->radius || 00425 fabsf(tmp_co[1]) > cmd->radius || 00426 fabsf(tmp_co[2]) > cmd->radius) continue; 00427 } 00428 00429 for (j = 0; j < dvert[i].totweight; ++j) { 00430 if(dvert[i].dw[j].def_nr == defgrp_index) { 00431 dw = &dvert[i].dw[j]; 00432 break; 00433 } 00434 } 00435 if (!dw) continue; 00436 00437 fac = fac_orig * dw->weight; 00438 facm = 1.0f - fac; 00439 00440 /* The algo used to project the vertices to their 00441 * bounding box (bb) is pretty simple: 00442 * for each vertex v: 00443 * 1) find in which octant v is in; 00444 * 2) find which outer "wall" of that octant is closer to v; 00445 * 3) calculate factor (var fbb) to project v to that wall; 00446 * 4) project. */ 00447 00448 /* find in which octant this vertex is in */ 00449 octant = 0; 00450 if (tmp_co[0] > 0.0f) octant += 1; 00451 if (tmp_co[1] > 0.0f) octant += 2; 00452 if (tmp_co[2] > 0.0f) octant += 4; 00453 00454 /* apex is the bb's vertex at the chosen octant */ 00455 copy_v3_v3(apex, bb[octant]); 00456 00457 /* find which bb plane is closest to this vertex ... */ 00458 d[0] = tmp_co[0] / apex[0]; 00459 d[1] = tmp_co[1] / apex[1]; 00460 d[2] = tmp_co[2] / apex[2]; 00461 00462 /* ... (the closest has the higher (closer to 1) d value) */ 00463 dmax = d[0]; 00464 coord = 0; 00465 if (d[1] > dmax) { 00466 dmax = d[1]; 00467 coord = 1; 00468 } 00469 if (d[2] > dmax) { 00470 /* dmax = d[2]; */ /* commented, we don't need it */ 00471 coord = 2; 00472 } 00473 00474 /* ok, now we know which coordinate of the vertex to use */ 00475 00476 if (fabsf(tmp_co[coord]) < FLT_EPSILON) /* avoid division by zero */ 00477 continue; 00478 00479 /* finally, this is the factor we wanted, to project the vertex 00480 * to its bounding box (bb) */ 00481 fbb = apex[coord] / tmp_co[coord]; 00482 00483 /* calculate the new vertex position */ 00484 if (flag & MOD_CAST_X) 00485 tmp_co[0] = facm * tmp_co[0] + fac * tmp_co[0] * fbb; 00486 if (flag & MOD_CAST_Y) 00487 tmp_co[1] = facm * tmp_co[1] + fac * tmp_co[1] * fbb; 00488 if (flag & MOD_CAST_Z) 00489 tmp_co[2] = facm * tmp_co[2] + fac * tmp_co[2] * fbb; 00490 00491 if(ctrl_ob) { 00492 if(flag & MOD_CAST_USE_OB_TRANSFORM) { 00493 mul_m4_v3(imat, tmp_co); 00494 } else { 00495 add_v3_v3(tmp_co, center); 00496 } 00497 } 00498 00499 copy_v3_v3(vertexCos[i], tmp_co); 00500 } 00501 return; 00502 } 00503 00504 /* no vgroup (check previous case for comments about the code) */ 00505 for (i = 0; i < numVerts; i++) { 00506 int octant, coord; 00507 float d[3], dmax, fbb, apex[3]; 00508 float tmp_co[3]; 00509 00510 copy_v3_v3(tmp_co, vertexCos[i]); 00511 if(ctrl_ob) { 00512 if(flag & MOD_CAST_USE_OB_TRANSFORM) { 00513 mul_m4_v3(mat, tmp_co); 00514 } else { 00515 sub_v3_v3(tmp_co, center); 00516 } 00517 } 00518 00519 if (has_radius) { 00520 if (fabsf(tmp_co[0]) > cmd->radius || 00521 fabsf(tmp_co[1]) > cmd->radius || 00522 fabsf(tmp_co[2]) > cmd->radius) continue; 00523 } 00524 00525 octant = 0; 00526 if (tmp_co[0] > 0.0f) octant += 1; 00527 if (tmp_co[1] > 0.0f) octant += 2; 00528 if (tmp_co[2] > 0.0f) octant += 4; 00529 00530 copy_v3_v3(apex, bb[octant]); 00531 00532 d[0] = tmp_co[0] / apex[0]; 00533 d[1] = tmp_co[1] / apex[1]; 00534 d[2] = tmp_co[2] / apex[2]; 00535 00536 dmax = d[0]; 00537 coord = 0; 00538 if (d[1] > dmax) { 00539 dmax = d[1]; 00540 coord = 1; 00541 } 00542 if (d[2] > dmax) { 00543 /* dmax = d[2]; */ /* commented, we don't need it */ 00544 coord = 2; 00545 } 00546 00547 if (fabsf(tmp_co[coord]) < FLT_EPSILON) 00548 continue; 00549 00550 fbb = apex[coord] / tmp_co[coord]; 00551 00552 if (flag & MOD_CAST_X) 00553 tmp_co[0] = facm * tmp_co[0] + fac * tmp_co[0] * fbb; 00554 if (flag & MOD_CAST_Y) 00555 tmp_co[1] = facm * tmp_co[1] + fac * tmp_co[1] * fbb; 00556 if (flag & MOD_CAST_Z) 00557 tmp_co[2] = facm * tmp_co[2] + fac * tmp_co[2] * fbb; 00558 00559 if(ctrl_ob) { 00560 if(flag & MOD_CAST_USE_OB_TRANSFORM) { 00561 mul_m4_v3(imat, tmp_co); 00562 } else { 00563 add_v3_v3(tmp_co, center); 00564 } 00565 } 00566 00567 copy_v3_v3(vertexCos[i], tmp_co); 00568 } 00569 } 00570 00571 static void deformVerts(ModifierData *md, Object *ob, 00572 DerivedMesh *derivedData, 00573 float (*vertexCos)[3], 00574 int numVerts, 00575 int UNUSED(useRenderParams), 00576 int UNUSED(isFinalCalc)) 00577 { 00578 DerivedMesh *dm = NULL; 00579 CastModifierData *cmd = (CastModifierData *)md; 00580 00581 dm = get_dm(ob, NULL, derivedData, NULL, 0); 00582 00583 if (cmd->type == MOD_CAST_TYPE_CUBOID) { 00584 cuboid_do(cmd, ob, dm, vertexCos, numVerts); 00585 } else { /* MOD_CAST_TYPE_SPHERE or MOD_CAST_TYPE_CYLINDER */ 00586 sphere_do(cmd, ob, dm, vertexCos, numVerts); 00587 } 00588 00589 if(dm != derivedData) 00590 dm->release(dm); 00591 } 00592 00593 static void deformVertsEM( 00594 ModifierData *md, Object *ob, struct EditMesh *editData, 00595 DerivedMesh *derivedData, float (*vertexCos)[3], int numVerts) 00596 { 00597 DerivedMesh *dm = get_dm(ob, editData, derivedData, NULL, 0); 00598 CastModifierData *cmd = (CastModifierData *)md; 00599 00600 if (cmd->type == MOD_CAST_TYPE_CUBOID) { 00601 cuboid_do(cmd, ob, dm, vertexCos, numVerts); 00602 } else { /* MOD_CAST_TYPE_SPHERE or MOD_CAST_TYPE_CYLINDER */ 00603 sphere_do(cmd, ob, dm, vertexCos, numVerts); 00604 } 00605 00606 if(dm != derivedData) 00607 dm->release(dm); 00608 } 00609 00610 00611 ModifierTypeInfo modifierType_Cast = { 00612 /* name */ "Cast", 00613 /* structName */ "CastModifierData", 00614 /* structSize */ sizeof(CastModifierData), 00615 /* type */ eModifierTypeType_OnlyDeform, 00616 /* flags */ eModifierTypeFlag_AcceptsCVs 00617 | eModifierTypeFlag_SupportsEditmode, 00618 00619 /* copyData */ copyData, 00620 /* deformVerts */ deformVerts, 00621 /* deformMatrices */ NULL, 00622 /* deformVertsEM */ deformVertsEM, 00623 /* deformMatricesEM */ NULL, 00624 /* applyModifier */ NULL, 00625 /* applyModifierEM */ NULL, 00626 /* initData */ initData, 00627 /* requiredDataMask */ requiredDataMask, 00628 /* freeData */ NULL, 00629 /* isDisabled */ isDisabled, 00630 /* updateDepgraph */ updateDepgraph, 00631 /* dependsOnTime */ NULL, 00632 /* dependsOnNormals */ NULL, 00633 /* foreachObjectLink */ foreachObjectLink, 00634 /* foreachIDLink */ NULL, 00635 };