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