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