- mask_tex_map_obj --> mask_tex_map_object
[blender.git] / source / blender / modifiers / intern / MOD_weightvgproximity.c
1 /*
2 * $Id$
3 *
4 * ***** BEGIN GPL LICENSE BLOCK *****
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software  Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 *
20 * The Original Code is Copyright (C) 2011 by Bastien Montagne.
21 * All rights reserved.
22 *
23 * Contributor(s): None yet.
24 *
25 * ***** END GPL LICENSE BLOCK *****
26 *
27 */
28
29 /*
30  * XXX I'd like to make modified weights visible in WeightPaint mode,
31  *     but couldn't figure a way to do this…
32  *     Maybe this will need changes in mesh_calc_modifiers (DerivedMesh.c)?
33  *     Or the WeightPaint mode code itself?
34  */
35
36 #include "BLI_utildefines.h"
37 #include "BLI_math.h"
38 #include "BLI_string.h"
39 #include "BLI_editVert.h"
40
41 #include "DNA_mesh_types.h"
42 #include "DNA_meshdata_types.h"
43 #include "DNA_modifier_types.h"
44 #include "DNA_object_types.h"
45
46 #include "BKE_cdderivedmesh.h"
47 #include "BKE_deform.h"
48 #include "BKE_mesh.h"
49 #include "BKE_modifier.h"
50 #include "BKE_shrinkwrap.h"       /* For SpaceTransform stuff. */
51 #include "BKE_texture.h"          /* Texture masking. */
52
53 #include "depsgraph_private.h"
54 #include "MEM_guardedalloc.h"
55 #include "MOD_util.h"
56 #include "MOD_weightvg_util.h"
57
58 /**************************************
59  * Util functions.                    *
60  **************************************/
61
62 /* Util macro. */
63 #define OUT_OF_MEMORY() ((void)printf("WeightVGProximity: Out of memory.\n"))
64
65 /**
66  * Returns the squared distance between two given points.
67  */
68 static float squared_dist(const float *a, const float *b)
69 {
70         float tmp[3];
71         VECSUB(tmp, a, b);
72         return INPR(tmp, tmp);
73 }
74
75 /**
76  * Find nearest vertex and/or edge and/or face, for each vertex (adapted from shrinkwrap.c).
77  */
78 static void get_vert2geom_distance(int numVerts, float (*v_cos)[3],
79                                    float *dist_v, float *dist_e, float *dist_f,
80                                    DerivedMesh *target, const SpaceTransform *loc2trgt)
81 {
82         int i;
83         BVHTreeFromMesh treeData_v = NULL_BVHTreeFromMesh;
84         BVHTreeFromMesh treeData_e = NULL_BVHTreeFromMesh;
85         BVHTreeFromMesh treeData_f = NULL_BVHTreeFromMesh;
86         BVHTreeNearest  nearest_v  = NULL_BVHTreeNearest;
87         BVHTreeNearest  nearest_e  = NULL_BVHTreeNearest;
88         BVHTreeNearest  nearest_f  = NULL_BVHTreeNearest;
89
90         if (dist_v) {
91                 /* Create a bvh-tree of the given target's verts. */
92                 bvhtree_from_mesh_verts(&treeData_v, target, 0.0, 2, 6);
93                 if(treeData_v.tree == NULL) {
94                         OUT_OF_MEMORY();
95                         return;
96                 }
97         }
98         if (dist_e) {
99                 /* Create a bvh-tree of the given target's edges. */
100                 bvhtree_from_mesh_edges(&treeData_e, target, 0.0, 2, 6);
101                 if(treeData_e.tree == NULL) {
102                         OUT_OF_MEMORY();
103                         return;
104                 }
105         }
106         if (dist_f) {
107                 /* Create a bvh-tree of the given target's faces. */
108                 bvhtree_from_mesh_faces(&treeData_f, target, 0.0, 2, 6);
109                 if(treeData_f.tree == NULL) {
110                         OUT_OF_MEMORY();
111                         return;
112                 }
113         }
114
115         /* Setup nearest. */
116         nearest_v.index = nearest_e.index = nearest_f.index = -1;
117         /*nearest_v.dist  = nearest_e.dist  = nearest_f.dist  = FLT_MAX;*/
118         /* Find the nearest vert/edge/face. */
119 #ifndef __APPLE__
120 #pragma omp parallel for default(none) private(i) firstprivate(nearest_v,nearest_e,nearest_f) \
121                          shared(treeData_v,treeData_e,treeData_f,numVerts,v_cos,dist_v,dist_e, \
122                                 dist_f,loc2trgt) \
123                          schedule(static)
124 #endif
125         for (i = 0; i < numVerts; ++i) {
126                 float tmp_co[3];
127
128                 /* Convert the vertex to tree coordinates. */
129                 VECCOPY(tmp_co, v_cos[i]);
130                 space_transform_apply(loc2trgt, tmp_co);
131
132                 /* Use local proximity heuristics (to reduce the nearest search).
133                  *
134                  * If we already had an hit before, we assume this vertex is going to have a close hit to
135                  * that other vertex, so we can initiate the "nearest.dist" with the expected value to that
136                  * last hit.
137                  * This will lead in prunning of the search tree.
138                  */
139                 if (dist_v) {
140                         nearest_v.dist = nearest_v.index != -1 ? squared_dist(tmp_co, nearest_v.co) : FLT_MAX;
141                         /* Compute and store result. If invalid (-1 idx), keep FLT_MAX dist. */
142                         BLI_bvhtree_find_nearest(treeData_v.tree, tmp_co, &nearest_v, treeData_v.nearest_callback, &treeData_v);
143                         dist_v[i] = sqrtf(nearest_v.dist);
144                 }
145                 if (dist_e) {
146                         nearest_e.dist = nearest_e.index != -1 ? squared_dist(tmp_co, nearest_e.co) : FLT_MAX;
147                         /* Compute and store result. If invalid (-1 idx), keep FLT_MAX dist. */
148                         BLI_bvhtree_find_nearest(treeData_e.tree, tmp_co, &nearest_e, treeData_e.nearest_callback, &treeData_e);
149                         dist_e[i] = sqrtf(nearest_e.dist);
150                 }
151                 if (dist_f) {
152                         nearest_f.dist = nearest_f.index != -1 ? squared_dist(tmp_co, nearest_f.co) : FLT_MAX;
153                         /* Compute and store result. If invalid (-1 idx), keep FLT_MAX dist. */
154                         BLI_bvhtree_find_nearest(treeData_f.tree, tmp_co, &nearest_f, treeData_f.nearest_callback, &treeData_f);
155                         dist_f[i] = sqrtf(nearest_f.dist);
156                 }
157         }
158
159         if (dist_v)
160                 free_bvhtree_from_mesh(&treeData_v);
161         if (dist_e)
162                 free_bvhtree_from_mesh(&treeData_e);
163         if (dist_f)
164                 free_bvhtree_from_mesh(&treeData_f);
165 }
166
167 /**
168  * Returns the real distance between a vertex and another reference object.
169  * Note that it works in final world space (i.e. with constraints etc. applied).
170  */
171 static void get_vert2ob_distance(int numVerts, float (*v_cos)[3], float *dist,
172                                  const Object* ob, const Object* obr)
173 {
174         /* Vertex and ref object coordinates. */
175         float v_wco[3],
176               or_wco[3],
177               or_wro[3][3], /*unused*/
178               or_wsz[3];    /*unused*/
179         int i;
180
181         /* Get world-coordinates of the reference object (constraints and anim included).
182          * We also get rotation and scale, even though we do not want them…
183          */
184         mat4_to_loc_rot_size(or_wco, or_wro, or_wsz, (float (*)[4])obr->obmat);
185
186         for (i = 0; i < numVerts; i++) {
187                 /* Get world-coordinates of the vertex (constraints and anim included). */
188                 mul_v3_m4v3(v_wco, (float (*)[4])ob->obmat, v_cos[i]);
189                 /* Return distance between both coordinates. */
190                 dist[i] = len_v3v3(v_wco, or_wco);
191         }
192 }
193
194 /**
195  * Returns the real distance between an object and another reference object.
196  * Note that it works in final world space (i.e. with constraints etc. applied).
197  */
198 static float get_ob2ob_distance(const Object* ob, const Object* obr)
199 {
200         return len_v3v3(ob->obmat[3], obr->obmat[3]); 
201 }
202
203 /**
204  * Maps distances to weights.
205  */
206 void do_map(float *weights, const int nidx, const float min_d, const float max_d)
207 {
208         int i;
209         float b = min_d / (min_d - max_d);
210         float a = -b / min_d;
211         for (i = 0; i < nidx; i++)
212                 weights[i] = a * weights[i] + b;
213 }
214
215 /*a min_d + b = 0.0*/
216 /*a max_d + b = 1.0*/
217 /*a min_d = -b*/
218 /*a = -b / min_d*/
219
220 /*max_d(-b/min_d) + b = 1.0*/
221 /*b((-max_d/min_d)+1.0) = 1.0*/
222 /*b = 1.0 / ((min_d-max_d)/min_d)*/
223 /*b = min_d/(min_d-max_d)*/
224 /**************************************
225  * Modifiers functions.               *
226  **************************************/
227 static void initData(ModifierData *md)
228 {
229         WeightVGProximityModifierData *wmd = (WeightVGProximityModifierData*) md;
230
231         wmd->proximity_mode       = MOD_WVG_PROXIMITY_OBJECT;
232         wmd->proximity_flags      = MOD_WVG_PROXIMITY_GEOM_VERTS;
233
234         wmd->mask_constant        = 1.0f;
235         wmd->mask_tex_use_channel = MOD_WVG_MASK_TEX_USE_INT; /* Use intensity by default. */
236         wmd->mask_tex_mapping     = MOD_DISP_MAP_LOCAL;
237         wmd->max_dist             = 1.0f; /* vert arbitrary distance, but don't use 0 */
238 }
239
240 static void copyData(ModifierData *md, ModifierData *target)
241 {
242         WeightVGProximityModifierData *wmd  = (WeightVGProximityModifierData*) md;
243         WeightVGProximityModifierData *twmd = (WeightVGProximityModifierData*) target;
244
245         BLI_strncpy(twmd->defgrp_name, wmd->defgrp_name, sizeof(twmd->defgrp_name));
246         twmd->proximity_mode         = wmd->proximity_mode;
247         twmd->proximity_flags        = wmd->proximity_flags;
248         twmd->proximity_ob_target    = wmd->proximity_ob_target;
249
250         twmd->mask_constant          = wmd->mask_constant;
251         BLI_strncpy(twmd->mask_defgrp_name, wmd->mask_defgrp_name, sizeof(twmd->mask_defgrp_name));
252         twmd->mask_texture           = wmd->mask_texture;
253         twmd->mask_tex_use_channel   = wmd->mask_tex_use_channel;
254         twmd->mask_tex_mapping       = wmd->mask_tex_mapping;
255         twmd->mask_tex_map_obj       = wmd->mask_tex_map_obj;
256         BLI_strncpy(twmd->mask_tex_uvlayer_name, wmd->mask_tex_uvlayer_name, sizeof(twmd->mask_tex_uvlayer_name));
257         twmd->min_dist               = wmd->min_dist;
258         twmd->max_dist               = wmd->max_dist;
259 }
260
261 static CustomDataMask requiredDataMask(Object *UNUSED(ob), ModifierData *md)
262 {
263         WeightVGProximityModifierData *wmd = (WeightVGProximityModifierData*) md;
264         CustomDataMask dataMask = 0;
265
266         /* We need vertex groups! */
267         dataMask |= CD_MASK_MDEFORMVERT;
268
269         /* Ask for UV coordinates if we need them. */
270         if(wmd->mask_tex_mapping == MOD_DISP_MAP_UV)
271                 dataMask |= CD_MASK_MTFACE;
272
273         return dataMask;
274 }
275
276 static int dependsOnTime(ModifierData *md)
277 {
278         WeightVGProximityModifierData *wmd = (WeightVGProximityModifierData*) md;
279
280         if(wmd->mask_texture)
281                 return BKE_texture_dependsOnTime(wmd->mask_texture);
282         return 0;
283 }
284
285 static void foreachObjectLink(ModifierData *md, Object *ob,
286                               void (*walk)(void *userData, Object *ob, Object **obpoin),
287                               void *userData)
288 {
289         WeightVGProximityModifierData *wmd = (WeightVGProximityModifierData*) md;
290         walk(userData, ob, &wmd->proximity_ob_target);
291         walk(userData, ob, &wmd->mask_tex_map_obj);
292 }
293
294 static void foreachIDLink(ModifierData *md, Object *ob, IDWalkFunc walk, void *userData)
295 {
296         WeightVGProximityModifierData *wmd = (WeightVGProximityModifierData*) md;
297
298         walk(userData, ob, (ID **)&wmd->mask_texture);
299
300         foreachObjectLink(md, ob, (ObjectWalkFunc)walk, userData);
301 }
302
303 static void foreachTexLink(ModifierData *md, Object *ob, TexWalkFunc walk, void *userData)
304 {
305         walk(userData, ob, md, "mask_texture");
306 }
307
308 static void updateDepgraph(ModifierData *md, DagForest *forest, struct Scene *UNUSED(scene),
309                            Object *UNUSED(ob), DagNode *obNode)
310 {
311         WeightVGProximityModifierData *wmd = (WeightVGProximityModifierData*) md;
312         DagNode *curNode;
313
314         if (wmd->proximity_ob_target) {
315                 curNode = dag_get_node(forest, wmd->proximity_ob_target);
316                 dag_add_relation(forest, curNode, obNode, DAG_RL_DATA_DATA|DAG_RL_OB_DATA,
317                                  "WeightVGProximity Modifier");
318         }
319
320         if(wmd->mask_tex_map_obj && wmd->mask_tex_mapping == MOD_DISP_MAP_OBJECT) {
321                 curNode = dag_get_node(forest, wmd->mask_tex_map_obj);
322
323                 dag_add_relation(forest, curNode, obNode, DAG_RL_DATA_DATA|DAG_RL_OB_DATA,
324                                  "WeightVGProximity Modifier");
325         }
326
327         if(wmd->mask_tex_mapping == MOD_DISP_MAP_GLOBAL)
328                 dag_add_relation(forest, obNode, obNode, DAG_RL_DATA_DATA|DAG_RL_OB_DATA,
329                                  "WeightVGProximity Modifier");
330 }
331
332 static int isDisabled(ModifierData *md, int UNUSED(useRenderParams))
333 {
334         WeightVGProximityModifierData *wmd = (WeightVGProximityModifierData*) md;
335         /* If no vertex group, bypass. */
336         if (wmd->defgrp_name == NULL) return 1;
337         /* If no target object, bypass. */
338         return (wmd->proximity_ob_target == NULL);
339 }
340
341 static DerivedMesh *applyModifier(ModifierData *md, Object *ob, DerivedMesh *derivedData,
342                                   int UNUSED(useRenderParams), int UNUSED(isFinalCalc))
343 {
344         WeightVGProximityModifierData *wmd = (WeightVGProximityModifierData*) md;
345         DerivedMesh *dm = derivedData, *ret = NULL;
346 #if 0
347         Mesh *ob_m = NULL;
348 #endif
349         MDeformVert *dvert = NULL;
350         int numVerts;
351         float (*v_cos)[3] = NULL; /* The vertices coordinates. */
352         Object *obr = NULL; /* Our target object. */
353         int defgrp_idx;
354         float *tw = NULL;
355         float *org_w = NULL;
356         float *new_w =NULL;
357         int *tidx, *indices = NULL;
358         int numIdx = 0;
359         int i, j;
360         char rel_ret = 0; /* Boolean, whether we have to release ret dm or not, when not using it! */
361
362         /* Get number of verts. */
363         numVerts = dm->getNumVerts(dm);
364
365         /* Check if we can just return the original mesh.
366          * Must have verts and therefore verts assigned to vgroups to do anything useful!
367          */
368         if ((numVerts == 0) || (ob->defbase.first == NULL))
369                 return dm;
370
371         /* Get our target object. */
372         obr = wmd->proximity_ob_target;
373         if (obr == NULL)
374                 return dm;
375
376         /* Get vgroup idx from its name. */
377         defgrp_idx = defgroup_name_index(ob, wmd->defgrp_name);
378         if (defgrp_idx < 0)
379                 return dm;
380
381         /* XXX All this to avoid copying dm when not needed… However, it nearly doubles compute
382          *     time! See scene 5 of the WeighVG test file…
383          */
384 #if 0
385         /* Get actual dverts (ie vertex group data). */
386         dvert = dm->getVertDataArray(dm, CD_MDEFORMVERT);
387         /* If no dverts, return unmodified data… */
388         if (dvert == NULL)
389                 return dm;
390
391         /* Get org mesh, only to test whether affected cdata layer has already been copied
392          * somewhere up in the modifiers stack.
393          */
394         ob_m = get_mesh(ob);
395         if (ob_m == NULL)
396                 return dm;
397
398         /* Create a copy of our dmesh, only if our affected cdata layer is the same as org mesh. */
399         if (dvert == CustomData_get_layer(&ob_m->vdata, CD_MDEFORMVERT)) {
400                 /* XXX Seems to create problems with weightpaint mode???
401                  *     I'm missing something here, I guess…
402                  */
403 //              DM_set_only_copy(dm, CD_MASK_MDEFORMVERT); /* Only copy defgroup layer. */
404                 ret = CDDM_copy(dm);
405                 dvert = ret->getVertDataArray(ret, CD_MDEFORMVERT);
406                 if (dvert == NULL) {
407                         ret->release(ret);
408                         return dm;
409                 }
410                 rel_ret = 1;
411         }
412         else
413                 ret = dm;
414 #else
415         ret = CDDM_copy(dm);
416         rel_ret = 1;
417         dvert = ret->getVertDataArray(ret, CD_MDEFORMVERT);
418         if (dvert == NULL) {
419                 if (rel_ret)
420                         ret->release(ret);
421                 return dm;
422         }
423 #endif
424
425         /* Find out which vertices to work on (all vertices in vgroup), and get their relevant weight.
426          */
427         tidx = MEM_mallocN(sizeof(int) * numVerts, "WeightVGProximity Modifier, tidx");
428         tw = MEM_mallocN(sizeof(float) * numVerts, "WeightVGProximity Modifier, tw");
429         for (i = 0; i < numVerts; i++) {
430                 for (j = 0; j < dvert[i].totweight; j++) {
431                         if(dvert[i].dw[j].def_nr == defgrp_idx) {
432                                 tidx[numIdx] = i;
433                                 tw[numIdx++] = dvert[i].dw[j].weight;
434                                 break;
435                         }
436                 }
437         }
438         indices = MEM_mallocN(sizeof(int) * numIdx, "WeightVGProximity Modifier, indices");
439         memcpy(indices, tidx, sizeof(int) * numIdx);
440         org_w = MEM_mallocN(sizeof(float) * numIdx, "WeightVGProximity Modifier, org_w");
441         new_w = MEM_mallocN(sizeof(float) * numIdx, "WeightVGProximity Modifier, new_w");
442         memcpy(org_w, tw, sizeof(float) * numIdx);
443         MEM_freeN(tidx);
444         MEM_freeN(tw);
445
446         /* Get our vertex coordinates. */
447         v_cos = MEM_mallocN(sizeof(float[3]) * numIdx, "WeightVGProximity Modifier, v_cos");
448         for (i = 0; i < numIdx; i++)
449                 ret->getVertCo(ret, indices[i], v_cos[i]);
450
451         /* Compute wanted distances. */
452         if (wmd->proximity_mode == MOD_WVG_PROXIMITY_OBJECT) {
453                 float dist = get_ob2ob_distance(ob, obr);
454                 for(i = 0; i < numIdx; i++)
455                         new_w[i] = dist;
456         }
457         else if (wmd->proximity_mode == MOD_WVG_PROXIMITY_GEOMETRY) {
458                 const short use_trgt_verts = (wmd->proximity_flags & MOD_WVG_PROXIMITY_GEOM_VERTS);
459                 const short use_trgt_edges = (wmd->proximity_flags & MOD_WVG_PROXIMITY_GEOM_EDGES);
460                 const short use_trgt_faces = (wmd->proximity_flags & MOD_WVG_PROXIMITY_GEOM_FACES);
461
462                 if (use_trgt_verts || use_trgt_edges || use_trgt_faces) {
463                         DerivedMesh *target_dm = obr->derivedFinal;
464                         if (!target_dm) {
465                                 if (ELEM3(obr->type, OB_CURVE, OB_SURF, OB_FONT))
466                                         target_dm = CDDM_from_curve(obr);
467                                 else if (obr->type == OB_MESH) {
468                                         Mesh *me = (Mesh*)obr->data;
469                                         if (me->edit_mesh)
470                                                 target_dm = CDDM_from_editmesh((EditMesh*)me->edit_mesh, me);
471                                         else
472                                                 target_dm = CDDM_from_mesh(me, obr);
473                                 }
474                         }
475
476                         /* We must check that we do have a valid target_dm! */
477                         if (target_dm) {
478                                 SpaceTransform loc2trgt;
479                                 float *dists_v = use_trgt_verts ? MEM_mallocN(sizeof(float) * numIdx, "WeightVGProximity Modifier, dists_v") : NULL;
480                                 float *dists_e = use_trgt_edges ? MEM_mallocN(sizeof(float) * numIdx, "WeightVGProximity Modifier, dists_e") : NULL;
481                                 float *dists_f = use_trgt_faces ? MEM_mallocN(sizeof(float) * numIdx, "WeightVGProximity Modifier, dists_f") : NULL;
482
483                                 space_transform_setup(&loc2trgt, ob, obr);
484                                 get_vert2geom_distance(numIdx, v_cos, dists_v, dists_e, dists_f,
485                                                        target_dm, &loc2trgt);
486                                 for(i = 0; i < numIdx; i++) {
487                                         new_w[i] = dists_v ? dists_v[i] : FLT_MAX;
488                                         new_w[i] = dists_e ? minf(dists_e[i], new_w[i]) : new_w[i];
489                                         new_w[i] = dists_f ? minf(dists_f[i], new_w[i]) : new_w[i];
490                                 }
491                         }
492                         /* Else, fall back to default obj2vert behavior. */
493                         else {
494                                 get_vert2ob_distance(numIdx, v_cos, new_w, ob, obr);
495                         }
496                 }
497                 else {
498                         get_vert2ob_distance(numIdx, v_cos, new_w, ob, obr);
499                 }
500         }
501
502         /* Do masking. */
503         weightvg_do_mask(numIdx, indices, org_w, new_w, ob, ret, wmd->mask_constant,
504                          wmd->mask_defgrp_name, wmd->mask_texture, wmd->mask_tex_use_channel,
505                          wmd->mask_tex_mapping, wmd->mask_tex_map_obj, wmd->mask_tex_uvlayer_name);
506
507         /* Map distances to weights. */
508         do_map(org_w, numIdx, wmd->min_dist, wmd->max_dist);
509
510         /* Update vgroup. Note we never add nor remove vertices from vgroup here. */
511         weightvg_update_vg(dvert, defgrp_idx, numIdx, indices, org_w, 0, 0.0f, 0, 0.0f);
512
513         /* Freeing stuff. */
514         if (org_w)
515                 MEM_freeN(org_w);
516         if (new_w)
517                 MEM_freeN(new_w);
518         if (indices)
519                 MEM_freeN(indices);
520         if (v_cos)
521                 MEM_freeN(v_cos);
522
523         /* Return the vgroup-modified mesh. */
524         return ret;
525 }
526
527 static DerivedMesh *applyModifierEM(ModifierData *md, Object *ob,
528                                     struct EditMesh *UNUSED(editData),
529                                     DerivedMesh *derivedData)
530 {
531         return applyModifier(md, ob, derivedData, 0, 1);
532 }
533
534
535 ModifierTypeInfo modifierType_WeightVGProximity = {
536         /* name */              "WeightVGProximity",
537         /* structName */        "WeightVGProximityModifierData",
538         /* structSize */        sizeof(WeightVGProximityModifierData),
539         /* type */              eModifierTypeType_Nonconstructive,
540         /* flags */             eModifierTypeFlag_AcceptsMesh
541 /*                             |eModifierTypeFlag_SupportsMapping*/
542                                |eModifierTypeFlag_SupportsEditmode,
543
544         /* copyData */          copyData,
545         /* deformVerts */       NULL,
546         /* deformMatrices */    NULL,
547         /* deformVertsEM */     NULL,
548         /* deformMatricesEM */  NULL,
549         /* applyModifier */     applyModifier,
550         /* applyModifierEM */   applyModifierEM,
551         /* initData */          initData,
552         /* requiredDataMask */  requiredDataMask,
553         /* freeData */          NULL,
554         /* isDisabled */        isDisabled,
555         /* updateDepgraph */    updateDepgraph,
556         /* dependsOnTime */     dependsOnTime,
557         /* dependsOnNormals */  NULL,
558         /* foreachObjectLink */ foreachObjectLink,
559         /* foreachIDLink */     foreachIDLink,
560         /* foreachTexLink */    foreachTexLink,
561 };