4 * ***** BEGIN GPL LICENSE BLOCK *****
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 * The Original Code is Copyright (C) 2011 by Bastien Montagne.
21 * All rights reserved.
23 * Contributor(s): None yet.
25 * ***** END GPL LICENSE BLOCK *****
30 * XXX I'd like to make modified weights visible in WeightPaint mode,
31 * but couldn't figure a way to do this...
32 * Maybe this will need changes in mesh_calc_modifiers (DerivedMesh.c)?
33 * Or the WeightPaint mode code itself?
36 #include "BLI_editVert.h"
38 #include "BLI_string.h"
39 #include "BLI_utildefines.h"
41 #include "DNA_mesh_types.h"
42 #include "DNA_meshdata_types.h"
43 #include "DNA_modifier_types.h"
44 #include "DNA_object_types.h"
46 #include "BKE_cdderivedmesh.h"
47 #include "BKE_deform.h"
49 #include "BKE_modifier.h"
50 #include "BKE_shrinkwrap.h" /* For SpaceTransform stuff. */
51 #include "BKE_texture.h" /* Texture masking. */
53 #include "depsgraph_private.h"
54 #include "MEM_guardedalloc.h"
56 #include "MOD_weightvg_util.h"
58 /**************************************
60 **************************************/
63 #define OUT_OF_MEMORY() ((void)printf("WeightVGProximity: Out of memory.\n"))
66 * Returns the squared distance between two given points.
68 static float squared_dist(const float *a, const float *b)
72 return INPR(tmp, tmp);
76 * Find nearest vertex and/or edge and/or face, for each vertex (adapted from shrinkwrap.c).
78 static void get_vert2geom_distance(int numVerts, float (*v_cos)[3],
79 float *dist_v, float *dist_e, float *dist_f,
80 DerivedMesh *target, const SpaceTransform *loc2trgt)
83 BVHTreeFromMesh treeData_v = NULL_BVHTreeFromMesh;
84 BVHTreeFromMesh treeData_e = NULL_BVHTreeFromMesh;
85 BVHTreeFromMesh treeData_f = NULL_BVHTreeFromMesh;
86 BVHTreeNearest nearest_v = NULL_BVHTreeNearest;
87 BVHTreeNearest nearest_e = NULL_BVHTreeNearest;
88 BVHTreeNearest nearest_f = NULL_BVHTreeNearest;
91 /* Create a bvh-tree of the given target's verts. */
92 bvhtree_from_mesh_verts(&treeData_v, target, 0.0, 2, 6);
93 if(treeData_v.tree == NULL) {
99 /* Create a bvh-tree of the given target's edges. */
100 bvhtree_from_mesh_edges(&treeData_e, target, 0.0, 2, 6);
101 if(treeData_e.tree == NULL) {
107 /* Create a bvh-tree of the given target's faces. */
108 bvhtree_from_mesh_faces(&treeData_f, target, 0.0, 2, 6);
109 if(treeData_f.tree == NULL) {
116 nearest_v.index = nearest_e.index = nearest_f.index = -1;
117 /*nearest_v.dist = nearest_e.dist = nearest_f.dist = FLT_MAX;*/
118 /* Find the nearest vert/edge/face. */
120 #pragma omp parallel for default(none) private(i) firstprivate(nearest_v,nearest_e,nearest_f) \
121 shared(treeData_v,treeData_e,treeData_f,numVerts,v_cos,dist_v,dist_e, \
125 for (i = 0; i < numVerts; ++i) {
128 /* Convert the vertex to tree coordinates. */
129 VECCOPY(tmp_co, v_cos[i]);
130 space_transform_apply(loc2trgt, tmp_co);
132 /* Use local proximity heuristics (to reduce the nearest search).
134 * If we already had an hit before, we assume this vertex is going to have a close hit to
135 * that other vertex, so we can initiate the "nearest.dist" with the expected value to that
137 * This will lead in prunning of the search tree.
140 nearest_v.dist = nearest_v.index != -1 ? squared_dist(tmp_co, nearest_v.co) : FLT_MAX;
141 /* Compute and store result. If invalid (-1 idx), keep FLT_MAX dist. */
142 BLI_bvhtree_find_nearest(treeData_v.tree, tmp_co, &nearest_v, treeData_v.nearest_callback, &treeData_v);
143 dist_v[i] = sqrtf(nearest_v.dist);
146 nearest_e.dist = nearest_e.index != -1 ? squared_dist(tmp_co, nearest_e.co) : FLT_MAX;
147 /* Compute and store result. If invalid (-1 idx), keep FLT_MAX dist. */
148 BLI_bvhtree_find_nearest(treeData_e.tree, tmp_co, &nearest_e, treeData_e.nearest_callback, &treeData_e);
149 dist_e[i] = sqrtf(nearest_e.dist);
152 nearest_f.dist = nearest_f.index != -1 ? squared_dist(tmp_co, nearest_f.co) : FLT_MAX;
153 /* Compute and store result. If invalid (-1 idx), keep FLT_MAX dist. */
154 BLI_bvhtree_find_nearest(treeData_f.tree, tmp_co, &nearest_f, treeData_f.nearest_callback, &treeData_f);
155 dist_f[i] = sqrtf(nearest_f.dist);
160 free_bvhtree_from_mesh(&treeData_v);
162 free_bvhtree_from_mesh(&treeData_e);
164 free_bvhtree_from_mesh(&treeData_f);
168 * Returns the real distance between a vertex and another reference object.
169 * Note that it works in final world space (i.e. with constraints etc. applied).
171 static void get_vert2ob_distance(int numVerts, float (*v_cos)[3], float *dist,
172 Object* ob, Object* obr)
174 /* Vertex and ref object coordinates. */
176 unsigned int i= numVerts;
179 /* Get world-coordinates of the vertex (constraints and anim included). */
180 mul_v3_m4v3(v_wco, ob->obmat, v_cos[i]);
181 /* Return distance between both coordinates. */
182 dist[i] = len_v3v3(v_wco, obr->obmat[3]);
187 * Returns the real distance between an object and another reference object.
188 * Note that it works in final world space (i.e. with constraints etc. applied).
190 static float get_ob2ob_distance(const Object* ob, const Object* obr)
192 return len_v3v3(ob->obmat[3], obr->obmat[3]);
196 * Maps distances to weights, with an optionnal “smoothing” mapping.
198 void do_map(float *weights, const int nidx, const float min_d, const float max_d, short mode)
200 const float range_inv= 1.0f / (max_d - min_d); /* invert since multiplication is faster */
201 unsigned int i= nidx;
204 weights[i] = (weights[i] >= max_d) ? 1.0f : 0.0f; /* "Step" behavior... */
207 else if(max_d > min_d) {
209 if (weights[i] >= max_d) weights[i]= 1.0f; /* most likely case first */
210 else if(weights[i] <= min_d) weights[i]= 0.0f;
211 else weights[i]= (weights[i] - min_d) * range_inv;
216 if (weights[i] <= max_d) weights[i]= 1.0f; /* most likely case first */
217 else if(weights[i] >= min_d) weights[i]= 0.0f;
218 else weights[i]= (weights[i] - min_d) * range_inv;
222 if(!ELEM(mode, MOD_WVG_MAPPING_NONE, MOD_WVG_MAPPING_CURVE)) {
223 weightvg_do_map(nidx, weights, mode, NULL);
227 /**************************************
228 * Modifiers functions. *
229 **************************************/
230 static void initData(ModifierData *md)
232 WeightVGProximityModifierData *wmd = (WeightVGProximityModifierData*) md;
234 wmd->proximity_mode = MOD_WVG_PROXIMITY_OBJECT;
235 wmd->proximity_flags = MOD_WVG_PROXIMITY_GEOM_VERTS;
237 wmd->falloff_type = MOD_WVG_MAPPING_NONE;
239 wmd->mask_constant = 1.0f;
240 wmd->mask_tex_use_channel = MOD_WVG_MASK_TEX_USE_INT; /* Use intensity by default. */
241 wmd->mask_tex_mapping = MOD_DISP_MAP_LOCAL;
242 wmd->max_dist = 1.0f; /* vert arbitrary distance, but don't use 0 */
245 static void copyData(ModifierData *md, ModifierData *target)
247 WeightVGProximityModifierData *wmd = (WeightVGProximityModifierData*) md;
248 WeightVGProximityModifierData *twmd = (WeightVGProximityModifierData*) target;
250 BLI_strncpy(twmd->defgrp_name, wmd->defgrp_name, sizeof(twmd->defgrp_name));
251 twmd->proximity_mode = wmd->proximity_mode;
252 twmd->proximity_flags = wmd->proximity_flags;
253 twmd->proximity_ob_target = wmd->proximity_ob_target;
255 twmd->falloff_type = wmd->falloff_type;
257 twmd->mask_constant = wmd->mask_constant;
258 BLI_strncpy(twmd->mask_defgrp_name, wmd->mask_defgrp_name, sizeof(twmd->mask_defgrp_name));
259 twmd->mask_texture = wmd->mask_texture;
260 twmd->mask_tex_use_channel = wmd->mask_tex_use_channel;
261 twmd->mask_tex_mapping = wmd->mask_tex_mapping;
262 twmd->mask_tex_map_obj = wmd->mask_tex_map_obj;
263 BLI_strncpy(twmd->mask_tex_uvlayer_name, wmd->mask_tex_uvlayer_name, sizeof(twmd->mask_tex_uvlayer_name));
264 twmd->min_dist = wmd->min_dist;
265 twmd->max_dist = wmd->max_dist;
268 static CustomDataMask requiredDataMask(Object *UNUSED(ob), ModifierData *md)
270 WeightVGProximityModifierData *wmd = (WeightVGProximityModifierData*) md;
271 CustomDataMask dataMask = 0;
273 /* We need vertex groups! */
274 dataMask |= CD_MASK_MDEFORMVERT;
276 /* Ask for UV coordinates if we need them. */
277 if(wmd->mask_tex_mapping == MOD_DISP_MAP_UV)
278 dataMask |= CD_MASK_MTFACE;
283 static int dependsOnTime(ModifierData *md)
285 WeightVGProximityModifierData *wmd = (WeightVGProximityModifierData*) md;
287 if(wmd->mask_texture)
288 return BKE_texture_dependsOnTime(wmd->mask_texture);
292 static void foreachObjectLink(ModifierData *md, Object *ob,
293 void (*walk)(void *userData, Object *ob, Object **obpoin),
296 WeightVGProximityModifierData *wmd = (WeightVGProximityModifierData*) md;
297 walk(userData, ob, &wmd->proximity_ob_target);
298 walk(userData, ob, &wmd->mask_tex_map_obj);
301 static void foreachIDLink(ModifierData *md, Object *ob, IDWalkFunc walk, void *userData)
303 WeightVGProximityModifierData *wmd = (WeightVGProximityModifierData*) md;
305 walk(userData, ob, (ID **)&wmd->mask_texture);
307 foreachObjectLink(md, ob, (ObjectWalkFunc)walk, userData);
310 static void foreachTexLink(ModifierData *md, Object *ob, TexWalkFunc walk, void *userData)
312 walk(userData, ob, md, "mask_texture");
315 static void updateDepgraph(ModifierData *md, DagForest *forest, struct Scene *UNUSED(scene),
316 Object *UNUSED(ob), DagNode *obNode)
318 WeightVGProximityModifierData *wmd = (WeightVGProximityModifierData*) md;
321 if (wmd->proximity_ob_target) {
322 curNode = dag_get_node(forest, wmd->proximity_ob_target);
323 dag_add_relation(forest, curNode, obNode, DAG_RL_DATA_DATA|DAG_RL_OB_DATA,
324 "WeightVGProximity Modifier");
327 if(wmd->mask_tex_map_obj && wmd->mask_tex_mapping == MOD_DISP_MAP_OBJECT) {
328 curNode = dag_get_node(forest, wmd->mask_tex_map_obj);
330 dag_add_relation(forest, curNode, obNode, DAG_RL_DATA_DATA|DAG_RL_OB_DATA,
331 "WeightVGProximity Modifier");
334 if(wmd->mask_tex_mapping == MOD_DISP_MAP_GLOBAL)
335 dag_add_relation(forest, obNode, obNode, DAG_RL_DATA_DATA|DAG_RL_OB_DATA,
336 "WeightVGProximity Modifier");
339 static int isDisabled(ModifierData *md, int UNUSED(useRenderParams))
341 WeightVGProximityModifierData *wmd = (WeightVGProximityModifierData*) md;
342 /* If no vertex group, bypass. */
343 if (wmd->defgrp_name[0] == '\0') return 1;
344 /* If no target object, bypass. */
345 return (wmd->proximity_ob_target == NULL);
348 static DerivedMesh *applyModifier(ModifierData *md, Object *ob, DerivedMesh *derivedData,
349 int UNUSED(useRenderParams), int UNUSED(isFinalCalc))
351 WeightVGProximityModifierData *wmd = (WeightVGProximityModifierData*) md;
352 DerivedMesh *dm = derivedData, *ret = NULL;
356 MDeformVert *dvert = NULL;
358 float (*v_cos)[3] = NULL; /* The vertices coordinates. */
359 Object *obr = NULL; /* Our target object. */
364 int *tidx, *indices = NULL;
367 char rel_ret = 0; /* Boolean, whether we have to release ret dm or not, when not using it! */
369 /* Get number of verts. */
370 numVerts = dm->getNumVerts(dm);
372 /* Check if we can just return the original mesh.
373 * Must have verts and therefore verts assigned to vgroups to do anything useful!
375 if ((numVerts == 0) || (ob->defbase.first == NULL))
378 /* Get our target object. */
379 obr = wmd->proximity_ob_target;
383 /* Get vgroup idx from its name. */
384 defgrp_idx = defgroup_name_index(ob, wmd->defgrp_name);
388 /* XXX All this to avoid copying dm when not needed... However, it nearly doubles compute
389 * time! See scene 5 of the WeighVG test file...
392 /* Get actual dverts (ie vertex group data). */
393 dvert = dm->getVertDataArray(dm, CD_MDEFORMVERT);
394 /* If no dverts, return unmodified data... */
398 /* Get org mesh, only to test whether affected cdata layer has already been copied
399 * somewhere up in the modifiers stack.
405 /* Create a copy of our dmesh, only if our affected cdata layer is the same as org mesh. */
406 if (dvert == CustomData_get_layer(&ob_m->vdata, CD_MDEFORMVERT)) {
407 /* XXX Seems to create problems with weightpaint mode???
408 * I'm missing something here, I guess...
410 // DM_set_only_copy(dm, CD_MASK_MDEFORMVERT); /* Only copy defgroup layer. */
412 dvert = ret->getVertDataArray(ret, CD_MDEFORMVERT);
422 ret = CDDM_copy(dm, 0);
424 dvert = ret->getVertDataArray(ret, CD_MDEFORMVERT);
432 /* Find out which vertices to work on (all vertices in vgroup), and get their relevant weight.
434 tidx = MEM_mallocN(sizeof(int) * numVerts, "WeightVGProximity Modifier, tidx");
435 tw = MEM_mallocN(sizeof(float) * numVerts, "WeightVGProximity Modifier, tw");
436 for (i = 0; i < numVerts; i++) {
437 for (j = 0; j < dvert[i].totweight; j++) {
438 if(dvert[i].dw[j].def_nr == defgrp_idx) {
440 tw[numIdx++] = dvert[i].dw[j].weight;
445 indices = MEM_mallocN(sizeof(int) * numIdx, "WeightVGProximity Modifier, indices");
446 memcpy(indices, tidx, sizeof(int) * numIdx);
447 org_w = MEM_mallocN(sizeof(float) * numIdx, "WeightVGProximity Modifier, org_w");
448 new_w = MEM_mallocN(sizeof(float) * numIdx, "WeightVGProximity Modifier, new_w");
449 memcpy(org_w, tw, sizeof(float) * numIdx);
453 /* Get our vertex coordinates. */
454 v_cos = MEM_mallocN(sizeof(float[3]) * numIdx, "WeightVGProximity Modifier, v_cos");
455 for (i = 0; i < numIdx; i++)
456 ret->getVertCo(ret, indices[i], v_cos[i]);
458 /* Compute wanted distances. */
459 if (wmd->proximity_mode == MOD_WVG_PROXIMITY_OBJECT) {
460 float dist = get_ob2ob_distance(ob, obr);
461 for(i = 0; i < numIdx; i++)
464 else if (wmd->proximity_mode == MOD_WVG_PROXIMITY_GEOMETRY) {
465 const short use_trgt_verts = (wmd->proximity_flags & MOD_WVG_PROXIMITY_GEOM_VERTS);
466 const short use_trgt_edges = (wmd->proximity_flags & MOD_WVG_PROXIMITY_GEOM_EDGES);
467 const short use_trgt_faces = (wmd->proximity_flags & MOD_WVG_PROXIMITY_GEOM_FACES);
469 if (use_trgt_verts || use_trgt_edges || use_trgt_faces) {
470 DerivedMesh *target_dm = obr->derivedFinal;
472 if (ELEM3(obr->type, OB_CURVE, OB_SURF, OB_FONT))
473 target_dm = CDDM_from_curve(obr);
474 else if (obr->type == OB_MESH) {
475 Mesh *me = (Mesh*)obr->data;
477 target_dm = CDDM_from_BMEditMesh(me->edit_btmesh, me, 0);
479 target_dm = CDDM_from_mesh(me, obr);
483 /* We must check that we do have a valid target_dm! */
485 SpaceTransform loc2trgt;
486 float *dists_v = use_trgt_verts ? MEM_mallocN(sizeof(float) * numIdx, "WeightVGProximity Modifier, dists_v") : NULL;
487 float *dists_e = use_trgt_edges ? MEM_mallocN(sizeof(float) * numIdx, "WeightVGProximity Modifier, dists_e") : NULL;
488 float *dists_f = use_trgt_faces ? MEM_mallocN(sizeof(float) * numIdx, "WeightVGProximity Modifier, dists_f") : NULL;
490 space_transform_setup(&loc2trgt, ob, obr);
491 get_vert2geom_distance(numIdx, v_cos, dists_v, dists_e, dists_f,
492 target_dm, &loc2trgt);
493 for(i = 0; i < numIdx; i++) {
494 new_w[i] = dists_v ? dists_v[i] : FLT_MAX;
495 new_w[i] = dists_e ? minf(dists_e[i], new_w[i]) : new_w[i];
496 new_w[i] = dists_f ? minf(dists_f[i], new_w[i]) : new_w[i];
498 if(dists_v) MEM_freeN(dists_v);
499 if(dists_e) MEM_freeN(dists_e);
500 if(dists_f) MEM_freeN(dists_f);
502 /* Else, fall back to default obj2vert behavior. */
504 get_vert2ob_distance(numIdx, v_cos, new_w, ob, obr);
508 get_vert2ob_distance(numIdx, v_cos, new_w, ob, obr);
512 /* Map distances to weights. */
513 do_map(new_w, numIdx, wmd->min_dist, wmd->max_dist, wmd->falloff_type);
516 weightvg_do_mask(numIdx, indices, org_w, new_w, ob, ret, wmd->mask_constant,
517 wmd->mask_defgrp_name, wmd->mask_texture, wmd->mask_tex_use_channel,
518 wmd->mask_tex_mapping, wmd->mask_tex_map_obj, wmd->mask_tex_uvlayer_name);
520 /* Update vgroup. Note we never add nor remove vertices from vgroup here. */
521 weightvg_update_vg(dvert, defgrp_idx, numIdx, indices, org_w, 0, 0.0f, 0, 0.0f);
529 /* Return the vgroup-modified mesh. */
533 static DerivedMesh *applyModifierEM(ModifierData *md, Object *ob,
534 struct BMEditMesh *UNUSED(editData),
535 DerivedMesh *derivedData)
537 return applyModifier(md, ob, derivedData, 0, 1);
541 ModifierTypeInfo modifierType_WeightVGProximity = {
542 /* name */ "VertexWeightProximity",
543 /* structName */ "WeightVGProximityModifierData",
544 /* structSize */ sizeof(WeightVGProximityModifierData),
545 /* type */ eModifierTypeType_Nonconstructive,
546 /* flags */ eModifierTypeFlag_AcceptsMesh
547 /* |eModifierTypeFlag_SupportsMapping*/
548 |eModifierTypeFlag_SupportsEditmode,
550 /* copyData */ copyData,
551 /* deformVerts */ NULL,
552 /* deformMatrices */ NULL,
553 /* deformVertsEM */ NULL,
554 /* deformMatricesEM */ NULL,
555 /* applyModifier */ applyModifier,
556 /* applyModifierEM */ applyModifierEM,
557 /* initData */ initData,
558 /* requiredDataMask */ requiredDataMask,
560 /* isDisabled */ isDisabled,
561 /* updateDepgraph */ updateDepgraph,
562 /* dependsOnTime */ dependsOnTime,
563 /* dependsOnNormals */ NULL,
564 /* foreachObjectLink */ foreachObjectLink,
565 /* foreachIDLink */ foreachIDLink,
566 /* foreachTexLink */ foreachTexLink,