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