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