diff options
author | Benoit Bolsee <benoit.bolsee@online.be> | 2011-09-09 16:21:41 +0400 |
---|---|---|
committer | Benoit Bolsee <benoit.bolsee@online.be> | 2011-09-09 16:21:41 +0400 |
commit | c1c4743696c67f562eda31e90d03c72eaec04567 (patch) | |
tree | 12c992e4160d7ff878f05f33549ff24b0ae3faa1 /source/blender/modifiers | |
parent | dbd6658d737b1592a633ddf6397be14e50e434d9 (diff) | |
parent | 01744abd8187d1566b336bf38033673aa05b6786 (diff) |
svn merge -r 39975:40061 https://svn.blender.org/svnroot/bf-blender/trunk/blender
Diffstat (limited to 'source/blender/modifiers')
-rw-r--r-- | source/blender/modifiers/CMakeLists.txt | 5 | ||||
-rw-r--r-- | source/blender/modifiers/MOD_modifiertypes.h | 3 | ||||
-rw-r--r-- | source/blender/modifiers/intern/MOD_util.c | 3 | ||||
-rw-r--r-- | source/blender/modifiers/intern/MOD_weightvg_util.c | 306 | ||||
-rw-r--r-- | source/blender/modifiers/intern/MOD_weightvg_util.h | 90 | ||||
-rw-r--r-- | source/blender/modifiers/intern/MOD_weightvgedit.c | 326 | ||||
-rw-r--r-- | source/blender/modifiers/intern/MOD_weightvgmix.c | 466 | ||||
-rw-r--r-- | source/blender/modifiers/intern/MOD_weightvgproximity.c | 567 |
8 files changed, 1766 insertions, 0 deletions
diff --git a/source/blender/modifiers/CMakeLists.txt b/source/blender/modifiers/CMakeLists.txt index 52755f02056..d06ada737ee 100644 --- a/source/blender/modifiers/CMakeLists.txt +++ b/source/blender/modifiers/CMakeLists.txt @@ -86,11 +86,16 @@ set(SRC intern/MOD_uvproject.c intern/MOD_warp.c intern/MOD_wave.c + intern/MOD_weightvg_util.c + intern/MOD_weightvgedit.c + intern/MOD_weightvgmix.c + intern/MOD_weightvgproximity.c MOD_modifiertypes.h intern/MOD_boolean_util.h intern/MOD_fluidsim_util.h intern/MOD_util.h + intern/MOD_weightvg_util.h ) if(WITH_MOD_BOOLEAN) diff --git a/source/blender/modifiers/MOD_modifiertypes.h b/source/blender/modifiers/MOD_modifiertypes.h index 3d8ad7b4128..0ae9768c0e6 100644 --- a/source/blender/modifiers/MOD_modifiertypes.h +++ b/source/blender/modifiers/MOD_modifiertypes.h @@ -73,6 +73,9 @@ extern ModifierTypeInfo modifierType_Solidify; extern ModifierTypeInfo modifierType_Screw; extern ModifierTypeInfo modifierType_Warp; extern ModifierTypeInfo modifierType_NavMesh; +extern ModifierTypeInfo modifierType_WeightVGEdit; +extern ModifierTypeInfo modifierType_WeightVGMix; +extern ModifierTypeInfo modifierType_WeightVGProximity; /* MOD_util.c */ void modifier_type_init(ModifierTypeInfo *types[]); diff --git a/source/blender/modifiers/intern/MOD_util.c b/source/blender/modifiers/intern/MOD_util.c index 74fc022c343..a2fe947a523 100644 --- a/source/blender/modifiers/intern/MOD_util.c +++ b/source/blender/modifiers/intern/MOD_util.c @@ -296,5 +296,8 @@ void modifier_type_init(ModifierTypeInfo *types[]) INIT_TYPE(Screw); INIT_TYPE(Warp); INIT_TYPE(NavMesh); + INIT_TYPE(WeightVGEdit); + INIT_TYPE(WeightVGMix); + INIT_TYPE(WeightVGProximity); #undef INIT_TYPE } diff --git a/source/blender/modifiers/intern/MOD_weightvg_util.c b/source/blender/modifiers/intern/MOD_weightvg_util.c new file mode 100644 index 00000000000..98615c70553 --- /dev/null +++ b/source/blender/modifiers/intern/MOD_weightvg_util.c @@ -0,0 +1,306 @@ +/* + * $Id$ + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2011 by Bastien Montagne. + * All rights reserved. + * + * Contributor(s): None yet. + * + * ***** END GPL LICENSE BLOCK ***** + * + */ + +/* + * XXX I'd like to make modified weights visible in WeightPaint mode, + * but couldn't figure a way to do this... + * Maybe this will need changes in mesh_calc_modifiers (DerivedMesh.c)? + * Or the WeightPaint mode code itself? + */ + +#include "BLI_math.h" +#include "BLI_rand.h" +#include "BLI_string.h" +#include "BLI_utildefines.h" + +#include "DNA_color_types.h" /* CurveMapping. */ +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_modifier_types.h" +#include "DNA_object_types.h" + +#include "BKE_cdderivedmesh.h" +#include "BKE_colortools.h" /* CurveMapping. */ +#include "BKE_deform.h" +#include "BKE_mesh.h" +#include "BKE_modifier.h" +#include "BKE_texture.h" /* Texture masking. */ + +#include "depsgraph_private.h" +#include "MEM_guardedalloc.h" +#include "MOD_util.h" +#include "MOD_weightvg_util.h" +#include "RE_shader_ext.h" /* Texture masking. */ + +/* Maps new_w weights in place, using either one of the predifined functions, or a custom curve. + * Return values are in new_w. + * If indices is not NULL, it must be a table of same length as org_w and new_w, mapping to the real + * vertex index (in case the weight tables do not cover the whole vertices...). + * cmap might be NULL, in which case curve mapping mode will return unmodified data. + */ +void weightvg_do_map(int num, float *new_w, short falloff_type, CurveMapping *cmap) +{ + int i; + + /* Return immediately, if we have nothing to do! */ + /* Also security checks... */ + if(((falloff_type == MOD_WVG_MAPPING_CURVE) && (cmap == NULL)) + || !ELEM7(falloff_type, MOD_WVG_MAPPING_CURVE, MOD_WVG_MAPPING_SHARP, MOD_WVG_MAPPING_SMOOTH, + MOD_WVG_MAPPING_ROOT, MOD_WVG_MAPPING_SPHERE, MOD_WVG_MAPPING_RANDOM, + MOD_WVG_MAPPING_STEP)) + return; + + /* Map each weight (vertex) to its new value, accordingly to the chosen mode. */ + for(i = 0; i < num; ++i) { + float fac = new_w[i]; + + /* Code borrowed from the warp modifier. */ + /* Closely matches PROP_SMOOTH and similar. */ + switch(falloff_type) { + case MOD_WVG_MAPPING_CURVE: + fac = curvemapping_evaluateF(cmap, 0, fac); + break; + case MOD_WVG_MAPPING_SHARP: + fac = fac*fac; + break; + case MOD_WVG_MAPPING_SMOOTH: + fac = 3.0f*fac*fac - 2.0f*fac*fac*fac; + break; + case MOD_WVG_MAPPING_ROOT: + fac = (float)sqrt(fac); + break; + case MOD_WVG_MAPPING_SPHERE: + fac = (float)sqrt(2*fac - fac * fac); + break; + case MOD_WVG_MAPPING_RANDOM: + BLI_srand(BLI_rand()); /* random seed */ + fac = BLI_frand()*fac; + break; + case MOD_WVG_MAPPING_STEP: + fac = (fac >= 0.5f)?1.0f:0.0f; + break; + } + + new_w[i] = fac; + } +} + +/* Applies new_w weights to org_w ones, using either a texture, vgroup or constant value as factor. + * Return values are in org_w. + * If indices is not NULL, it must be a table of same length as org_w and new_w, mapping to the real + * vertex index (in case the weight tables do not cover the whole vertices...). + * XXX The standard “factor” value is assumed in [0.0, 1.0] range. Else, weird results might appear. + */ +void weightvg_do_mask(int num, const int *indices, float *org_w, const float *new_w, + Object *ob, DerivedMesh *dm, float fact, const char defgrp_name[32], + Tex *texture, int tex_use_channel, int tex_mapping, + Object *tex_map_object, const char *tex_uvlayer_name) +{ + int ref_didx; + int i; + + /* If influence factor is null, nothing to do! */ + if (fact == 0.0f) return; + + /* If we want to mask vgroup weights from a texture. */ + if (texture) { + /* The texture coordinates. */ + float (*tex_co)[3]; + /* See mapping note below... */ + MappingInfoModifierData t_map; + float (*v_co)[3]; + + /* Use new generic get_texture_coords, but do not modify our DNA struct for it... + * XXX Why use a ModifierData stuff here ? Why not a simple, generic struct for parameters ? + * What e.g. if a modifier wants to use several textures ? + * Why use only v_co, and not MVert (or both) ? + */ + t_map.texture = texture; + t_map.map_object = tex_map_object; + BLI_strncpy(t_map.uvlayer_name, tex_uvlayer_name, sizeof(t_map.uvlayer_name)); + t_map.texmapping = tex_mapping; + v_co = MEM_mallocN(sizeof(*v_co) * num, "WeightVG Modifier, TEX mode, v_co"); + dm->getVertCos(dm, v_co); + tex_co = MEM_callocN(sizeof(*tex_co) * num, "WeightVG Modifier, TEX mode, tex_co"); + get_texture_coords(&t_map, ob, dm, v_co, tex_co, num); + MEM_freeN(v_co); + + /* For each weight (vertex), make the mix between org and new weights. */ + for(i = 0; i < num; ++i) { + int idx = indices ? indices[i] : i; + TexResult texres; + float h, s, v; /* For HSV color space. */ + + texres.nor = NULL; + get_texture_value(texture, tex_co[idx], &texres); + /* Get the good channel value... */ + switch(tex_use_channel) { + case MOD_WVG_MASK_TEX_USE_INT: + org_w[i] = (new_w[i] * texres.tin * fact) + (org_w[i] * (1.0f - (texres.tin*fact))); + break; + case MOD_WVG_MASK_TEX_USE_RED: + org_w[i] = (new_w[i] * texres.tr * fact) + (org_w[i] * (1.0f - (texres.tr*fact))); + break; + case MOD_WVG_MASK_TEX_USE_GREEN: + org_w[i] = (new_w[i] * texres.tg * fact) + (org_w[i] * (1.0f - (texres.tg*fact))); + break; + case MOD_WVG_MASK_TEX_USE_BLUE: + org_w[i] = (new_w[i] * texres.tb * fact) + (org_w[i] * (1.0f - (texres.tb*fact))); + break; + case MOD_WVG_MASK_TEX_USE_HUE: + rgb_to_hsv(texres.tr, texres.tg, texres.tb, &h, &s, &v); + org_w[i] = (new_w[i] * h * fact) + (org_w[i] * (1.0f - (h*fact))); + break; + case MOD_WVG_MASK_TEX_USE_SAT: + rgb_to_hsv(texres.tr, texres.tg, texres.tb, &h, &s, &v); + org_w[i] = (new_w[i] * s * fact) + (org_w[i] * (1.0f - (s*fact))); + break; + case MOD_WVG_MASK_TEX_USE_VAL: + rgb_to_hsv(texres.tr, texres.tg, texres.tb, &h, &s, &v); + org_w[i] = (new_w[i] * v * fact) + (org_w[i] * (1.0f - (v*fact))); + break; + case MOD_WVG_MASK_TEX_USE_ALPHA: + org_w[i] = (new_w[i] * texres.ta * fact) + (org_w[i] * (1.0f - (texres.ta*fact))); + break; + default: + org_w[i] = (new_w[i] * texres.tin * fact) + (org_w[i] * (1.0f - (texres.tin*fact))); + break; + } + } + + MEM_freeN(tex_co); + } + else if ((ref_didx = defgroup_name_index(ob, defgrp_name)) != -1) { + MDeformVert *dvert = NULL; + + /* Check whether we want to set vgroup weights from a constant weight factor or a vertex + * group. + */ + /* Get vgroup idx from its name. */ + + /* Proceed only if vgroup is valid, else use constant factor. */ + /* Get actual dverts (ie vertex group data). */ + dvert = dm->getVertDataArray(dm, CD_MDEFORMVERT); + /* Proceed only if vgroup is valid, else assume factor = O. */ + if (dvert == NULL) return; + + /* For each weight (vertex), make the mix between org and new weights. */ + for (i = 0; i < num; i++) { + int idx = indices ? indices[i] : i; + const float f= defvert_find_weight(&dvert[idx], ref_didx) * fact; + org_w[i] = (new_w[i] * f) + (org_w[i] * (1.0f-f)); + /* If that vertex is not in ref vgroup, assume null factor, and hence do nothing! */ + } + } + else { + /* Default "influence" behavior. */ + /* For each weight (vertex), make the mix between org and new weights. */ + const float ifact= 1.0f - fact; + for (i = 0; i < num; i++) { + org_w[i] = (new_w[i] * fact) + (org_w[i] * ifact); + } + } +} + +/* Applies weights to given vgroup (defgroup), and optionnaly add/remove vertices from the group. + * If indices is not NULL, it must be a table of same length as weights, mapping to the real + * vertex index (in case the weight table does not cover the whole vertices...). + */ +void weightvg_update_vg(MDeformVert *dvert, int defgrp_idx, int num, + const int *indices, const float *weights, int do_add, + float add_thresh, int do_rem, float rem_thresh) +{ + int i; + + for (i = 0; i < num; i++) { + int j; + int add2vg = do_add; + float w = weights[i]; + MDeformVert *dv = &dvert[indices ? indices[i] : i]; + MDeformWeight *newdw; + + /* Never allow weights out of [0.0, 1.0] range. */ + CLAMP(w, 0.0f, 1.0f); + + /* Let's first check to see if this vert is already in the weight group – if so + * let's update it, or remove it if needed. + */ + for (j = 0; j < dv->totweight; j++) { + /* If this weight corresponds to the deform group, update the value or, + * if lower than rem_threshold, remove the vertex from the vgroup. + */ + if (dv->dw[j].def_nr == defgrp_idx) { + /* Remove the vertex from this vgroup if needed. */ + if (do_rem && w < rem_thresh) { + /* TODO, move this into deform.c to make into a generic function */ + + dv->totweight--; + /* If there are still other deform weights attached to this vert then remove + * this deform weight, and reshuffle the others. + */ + if(dv->totweight) { + newdw = MEM_mallocN(sizeof(MDeformWeight)*(dv->totweight), "deformWeight"); + if(dv->dw){ + memcpy(newdw, dv->dw, sizeof(MDeformWeight)*j); + memcpy(newdw+j, dv->dw+j+1, sizeof(MDeformWeight)*(dv->totweight-j)); + MEM_freeN(dv->dw); + } + dv->dw = newdw; + } + /* If there are no other deform weights left then just remove this one. */ + else { + MEM_freeN(dv->dw); + dv->dw = NULL; + } + } + /* Else, just set the new computed weight. */ + else { + dv->dw[j].weight = w; + } + add2vg = FALSE; + break; + } + } + + /* If the vert wasn't in the deform group, add it if needed! + */ + if ((add2vg == TRUE) && w > add_thresh) { + /* TODO, mvoe into deform.c and make into a generic function, this assumes the vertex + * groups have already been checked, so this has to remain low level */ + newdw = MEM_callocN(sizeof(MDeformWeight)*(dv->totweight+1), "WeightVGEdit Modifier, deformWeight"); + if(dv->dw) { + memcpy(newdw, dv->dw, sizeof(MDeformWeight)*dv->totweight); + MEM_freeN(dv->dw); + } + dv->dw = newdw; + dv->dw[dv->totweight].weight = w; + dv->dw[dv->totweight].def_nr = defgrp_idx; + dv->totweight++; + } + } +} diff --git a/source/blender/modifiers/intern/MOD_weightvg_util.h b/source/blender/modifiers/intern/MOD_weightvg_util.h new file mode 100644 index 00000000000..ce3520f1900 --- /dev/null +++ b/source/blender/modifiers/intern/MOD_weightvg_util.h @@ -0,0 +1,90 @@ +/* + * $Id$ + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2011 by Bastien Montagne. + * All rights reserved. + * + * Contributor(s): None yet. + * + * ***** END GPL LICENSE BLOCK ***** + * + */ + +/** \file blender/modifiers/intern/MOD_util.h + * \ingroup modifiers + */ + + +#ifndef MOD_WEIGHTVG_UTIL_H +#define MOD_WEIGHTVG_UTIL_H + +/* so modifier types match their defines */ +#include "MOD_modifiertypes.h" + +struct CurveMapping; +struct DerivedMesh; +struct Object; +struct Tex; + +/* + * XXX I'd like to make modified weights visible in WeightPaint mode, + * but couldn't figure a way to do this... + * Maybe this will need changes in mesh_calc_modifiers (DerivedMesh.c)? + * Or the WeightPaint mode code itself? + */ + +/************************************** + * Util functions. * + **************************************/ + +/* We cannot divide by zero (what a surprise...). + * So if -MOD_WEIGHTVGROUP_DIVMODE_ZEROFLOOR < weightf < MOD_WEIGHTVGROUP_DIVMODE_ZEROFLOOR, + * we clamp weightf to this value (or its negative version). + * Also used to avoid null power factor. + */ +#define MOD_WVG_ZEROFLOOR 1.0e-32f + +/* Maps new_w weights in place, using either one of the predifined functions, or a custom curve. + * Return values are in new_w. + * If indices is not NULL, it must be a table of same length as org_w and new_w, mapping to the real + * vertex index (in case the weight tables do not cover the whole vertices...). + * cmap might be NULL, in which case curve mapping mode will return unmodified data. + */ +void weightvg_do_map(int num, float *new_w, short mode, struct CurveMapping *cmap); + +/* Applies new_w weights to org_w ones, using either a texture, vgroup or constant value as factor. + * Return values are in org_w. + * If indices is not NULL, it must be a table of same length as org_w and new_w, mapping to the real + * vertex index (in case the weight tables do not cover the whole vertices...). + * XXX The standard “factor” value is assumed in [0.0, 1.0] range. Else, weird results might appear. + */ +void weightvg_do_mask(int num, const int *indices, float *org_w, const float *new_w, Object *ob, + DerivedMesh *dm, float fact, const char defgrp_name[32], Tex *texture, + int tex_use_channel, int tex_mapping, Object *tex_map_object, + const char *tex_uvlayer_name); + +/* Applies weights to given vgroup (defgroup), and optionnaly add/remove vertices from the group. + * If indices is not NULL, it must be a table of same length as weights, mapping to the real + * vertex index (in case the weight table does not cover the whole vertices...). + */ +void weightvg_update_vg(MDeformVert *dvert, int defgrp_idx, int num, + const int *indices, const float *weights, int do_add, + float add_thresh, int do_rem, float rem_thresh); + +#endif /* MOD_WEIGHTVG_UTIL_H */ diff --git a/source/blender/modifiers/intern/MOD_weightvgedit.c b/source/blender/modifiers/intern/MOD_weightvgedit.c new file mode 100644 index 00000000000..fb6d4dc10e6 --- /dev/null +++ b/source/blender/modifiers/intern/MOD_weightvgedit.c @@ -0,0 +1,326 @@ +/* + * $Id$ + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2011 by Bastien Montagne. + * All rights reserved. + * + * Contributor(s): None yet. + * + * ***** END GPL LICENSE BLOCK ***** + * + */ + +/* + * XXX I'd like to make modified weights visible in WeightPaint mode, + * but couldn't figure a way to do this... + * Maybe this will need changes in mesh_calc_modifiers (DerivedMesh.c)? + * Or the WeightPaint mode code itself? + */ + +#include "BLI_utildefines.h" +#include "BLI_math.h" +#include "BLI_string.h" + +#include "DNA_color_types.h" /* CurveMapping. */ +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_modifier_types.h" +#include "DNA_object_types.h" + +#include "BKE_cdderivedmesh.h" +#include "BKE_colortools.h" /* CurveMapping. */ +#include "BKE_deform.h" +#include "BKE_mesh.h" +#include "BKE_modifier.h" +#include "BKE_texture.h" /* Texture masking. */ + +#include "depsgraph_private.h" +#include "MEM_guardedalloc.h" +#include "MOD_util.h" +#include "MOD_weightvg_util.h" + +/************************************** + * Modifiers functions. * + **************************************/ +static void initData(ModifierData *md) +{ + WeightVGEditModifierData *wmd = (WeightVGEditModifierData*) md; + wmd->edit_flags = 0; + wmd->falloff_type = MOD_WVG_MAPPING_NONE; + wmd->default_weight = 0.0f; + + wmd->cmap_curve = curvemapping_add(1, 0.0, 0.0, 1.0, 1.0); + curvemapping_initialize(wmd->cmap_curve); + + wmd->rem_threshold = 0.01f; + wmd->add_threshold = 0.01f; + + wmd->mask_constant = 1.0f; + wmd->mask_tex_use_channel = MOD_WVG_MASK_TEX_USE_INT; /* Use intensity by default. */ + wmd->mask_tex_mapping = MOD_DISP_MAP_LOCAL; +} + +static void freeData(ModifierData *md) +{ + WeightVGEditModifierData *wmd = (WeightVGEditModifierData*) md; + curvemapping_free(wmd->cmap_curve); +} + +static void copyData(ModifierData *md, ModifierData *target) +{ + WeightVGEditModifierData *wmd = (WeightVGEditModifierData*) md; + WeightVGEditModifierData *twmd = (WeightVGEditModifierData*) target; + + BLI_strncpy(twmd->defgrp_name, wmd->defgrp_name, sizeof(twmd->defgrp_name)); + + twmd->edit_flags = wmd->edit_flags; + twmd->falloff_type = wmd->falloff_type; + twmd->default_weight = wmd->default_weight; + + twmd->cmap_curve = curvemapping_copy(wmd->cmap_curve); + + twmd->add_threshold = wmd->add_threshold; + twmd->rem_threshold = wmd->rem_threshold; + + twmd->mask_constant = wmd->mask_constant; + BLI_strncpy(twmd->mask_defgrp_name, wmd->mask_defgrp_name, sizeof(twmd->mask_defgrp_name)); + twmd->mask_texture = wmd->mask_texture; + twmd->mask_tex_use_channel = wmd->mask_tex_use_channel; + twmd->mask_tex_mapping = wmd->mask_tex_mapping; + twmd->mask_tex_map_obj = wmd->mask_tex_map_obj; + BLI_strncpy(twmd->mask_tex_uvlayer_name, wmd->mask_tex_uvlayer_name, sizeof(twmd->mask_tex_uvlayer_name)); +} + +static CustomDataMask requiredDataMask(Object *UNUSED(ob), ModifierData *md) +{ + WeightVGEditModifierData *wmd = (WeightVGEditModifierData*) md; + CustomDataMask dataMask = 0; + + /* We need vertex groups! */ + dataMask |= CD_MASK_MDEFORMVERT; + + /* Ask for UV coordinates if we need them. */ + if(wmd->mask_tex_mapping == MOD_DISP_MAP_UV) + dataMask |= CD_MASK_MTFACE; + + return dataMask; +} + +static int dependsOnTime(ModifierData *md) +{ + WeightVGEditModifierData *wmd = (WeightVGEditModifierData*) md; + + if(wmd->mask_texture) + return BKE_texture_dependsOnTime(wmd->mask_texture); + return 0; +} + +static void foreachObjectLink(ModifierData *md, Object *ob, + void (*walk)(void *userData, Object *ob, Object **obpoin), + void *userData) +{ + WeightVGEditModifierData *wmd = (WeightVGEditModifierData*) md; + walk(userData, ob, &wmd->mask_tex_map_obj); +} + +static void foreachIDLink(ModifierData *md, Object *ob, IDWalkFunc walk, void *userData) +{ + WeightVGEditModifierData *wmd = (WeightVGEditModifierData*) md; + + walk(userData, ob, (ID **)&wmd->mask_texture); + + foreachObjectLink(md, ob, (ObjectWalkFunc)walk, userData); +} + +static void foreachTexLink(ModifierData *md, Object *ob, TexWalkFunc walk, void *userData) +{ + walk(userData, ob, md, "mask_texture"); +} + +static void updateDepgraph(ModifierData *md, DagForest *forest, struct Scene *UNUSED(scene), + Object *UNUSED(ob), DagNode *obNode) +{ + WeightVGEditModifierData *wmd = (WeightVGEditModifierData*) md; + DagNode *curNode; + + if(wmd->mask_tex_map_obj && wmd->mask_tex_mapping == MOD_DISP_MAP_OBJECT) { + curNode = dag_get_node(forest, wmd->mask_tex_map_obj); + + dag_add_relation(forest, curNode, obNode, DAG_RL_DATA_DATA|DAG_RL_OB_DATA, + "WeightVGEdit Modifier"); + } + + if(wmd->mask_tex_mapping == MOD_DISP_MAP_GLOBAL) + dag_add_relation(forest, obNode, obNode, DAG_RL_DATA_DATA|DAG_RL_OB_DATA, + "WeightVGEdit Modifier"); +} + +static int isDisabled(ModifierData *md, int UNUSED(useRenderParams)) +{ + WeightVGEditModifierData *wmd = (WeightVGEditModifierData*) md; + /* If no vertex group, bypass. */ + return (wmd->defgrp_name[0] == '\0'); +} + +static DerivedMesh *applyModifier(ModifierData *md, Object *ob, DerivedMesh *derivedData, + int UNUSED(useRenderParams), int UNUSED(isFinalCalc)) +{ + WeightVGEditModifierData *wmd = (WeightVGEditModifierData*) md; + DerivedMesh *dm = derivedData, *ret = NULL; +#if 0 + Mesh *ob_m = NULL; +#endif + MDeformVert *dvert = NULL; + float *org_w; /* Array original weights. */ + float *new_w; /* Array new weights. */ + int numVerts; + int defgrp_idx; + int i; + char rel_ret = 0; /* Boolean, whether we have to release ret dm or not, when not using it! */ + /* Flags. */ + int do_add = (wmd->edit_flags & MOD_WVG_EDIT_ADD2VG) != 0; + int do_rem = (wmd->edit_flags & MOD_WVG_EDIT_REMFVG) != 0; + + /* Get number of verts. */ + numVerts = dm->getNumVerts(dm); + + /* Check if we can just return the original mesh. + * Must have verts and therefore verts assigned to vgroups to do anything useful! + */ + if ((numVerts == 0) || (ob->defbase.first == NULL)) + return dm; + + /* Get vgroup idx from its name. */ + defgrp_idx = defgroup_name_index(ob, wmd->defgrp_name); + if (defgrp_idx < 0) + return dm; + + /* XXX All this to avoid copying dm when not needed... However, it nearly doubles compute + * time! See scene 5 of the WeighVG test file... + */ +#if 0 + /* Get actual dverts (ie vertex group data). */ + dvert = dm->getVertDataArray(dm, CD_MDEFORMVERT); + /* If no dverts, return unmodified data... */ + if (dvert == NULL) + return dm; + + /* Get org mesh, only to test whether affected cdata layer has already been copied + * somewhere up in the modifiers stack. + */ + ob_m = get_mesh(ob); + if (ob_m == NULL) + return dm; + + /* Create a copy of our dmesh, only if our affected cdata layer is the same as org mesh. */ + if (dvert == CustomData_get_layer(&ob_m->vdata, CD_MDEFORMVERT)) { + /* XXX Seems to create problems with weightpaint mode??? + * I'm missing something here, I guess... + */ +// DM_set_only_copy(dm, CD_MASK_MDEFORMVERT); /* Only copy defgroup layer. */ + ret = CDDM_copy(dm); + dvert = ret->getVertDataArray(ret, CD_MDEFORMVERT); + if (dvert == NULL) { + ret->release(ret); + return dm; + } + rel_ret = 1; + } + else + ret = dm; +#else + ret = CDDM_copy(dm); + rel_ret = 1; + dvert = ret->getVertDataArray(ret, CD_MDEFORMVERT); + if (dvert == NULL) { + if (rel_ret) + ret->release(ret); + return dm; + } +#endif + + /* Get org weights, assuming 0.0 for vertices not in given vgroup. */ + org_w = MEM_mallocN(sizeof(float) * numVerts, "WeightVGEdit Modifier, org_w"); + new_w = MEM_mallocN(sizeof(float) * numVerts, "WeightVGEdit Modifier, org_w"); + for (i = 0; i < numVerts; i++) { + MDeformWeight *dw= defvert_find_index(&dvert[i], defgrp_idx); + org_w[i] = new_w[i] = wmd->default_weight; + + if(dw) { + org_w[i] = new_w[i] = dw->weight; + } + } + + /* Do mapping. */ + if (wmd->falloff_type != MOD_WVG_MAPPING_NONE) { + weightvg_do_map(numVerts, new_w, wmd->falloff_type, wmd->cmap_curve); + } + + /* Do masking. */ + weightvg_do_mask(numVerts, NULL, org_w, new_w, ob, ret, wmd->mask_constant, + wmd->mask_defgrp_name, wmd->mask_texture, wmd->mask_tex_use_channel, + wmd->mask_tex_mapping, wmd->mask_tex_map_obj, wmd->mask_tex_uvlayer_name); + + /* Update/add/remove from vgroup. */ + weightvg_update_vg(dvert, defgrp_idx, numVerts, NULL, org_w, do_add, wmd->add_threshold, + do_rem, wmd->rem_threshold); + + /* Freeing stuff. */ + MEM_freeN(org_w); + MEM_freeN(new_w); + + /* Return the vgroup-modified mesh. */ + return ret; +} + +static DerivedMesh *applyModifierEM(ModifierData *md, Object *ob, + struct EditMesh *UNUSED(editData), + DerivedMesh *derivedData) +{ + return applyModifier(md, ob, derivedData, 0, 1); +} + + +ModifierTypeInfo modifierType_WeightVGEdit = { + /* name */ "VertexWeightEdit", + /* structName */ "WeightVGEditModifierData", + /* structSize */ sizeof(WeightVGEditModifierData), + /* type */ eModifierTypeType_Nonconstructive, + /* flags */ eModifierTypeFlag_AcceptsMesh +/* |eModifierTypeFlag_SupportsMapping*/ + |eModifierTypeFlag_SupportsEditmode, + + /* copyData */ copyData, + /* deformVerts */ NULL, + /* deformMatrices */ NULL, + /* deformVertsEM */ NULL, + /* deformMatricesEM */ NULL, + /* applyModifier */ applyModifier, + /* applyModifierEM */ applyModifierEM, + /* initData */ initData, + /* requiredDataMask */ requiredDataMask, + /* freeData */ freeData, + /* isDisabled */ isDisabled, + /* updateDepgraph */ updateDepgraph, + /* dependsOnTime */ dependsOnTime, + /* dependsOnNormals */ NULL, + /* foreachObjectLink */ foreachObjectLink, + /* foreachIDLink */ foreachIDLink, + /* foreachTexLink */ foreachTexLink, +}; diff --git a/source/blender/modifiers/intern/MOD_weightvgmix.c b/source/blender/modifiers/intern/MOD_weightvgmix.c new file mode 100644 index 00000000000..f1422a342eb --- /dev/null +++ b/source/blender/modifiers/intern/MOD_weightvgmix.c @@ -0,0 +1,466 @@ +/* + * $Id$ + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2011 by Bastien Montagne. + * All rights reserved. + * + * Contributor(s): None yet. + * + * ***** END GPL LICENSE BLOCK ***** + * + */ + +/* + * XXX I'd like to make modified weights visible in WeightPaint mode, + * but couldn't figure a way to do this... + * Maybe this will need changes in mesh_calc_modifiers (DerivedMesh.c)? + * Or the WeightPaint mode code itself? + */ + +#include "BLI_utildefines.h" +#include "BLI_math.h" +#include "BLI_string.h" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_modifier_types.h" +#include "DNA_object_types.h" + +#include "BKE_cdderivedmesh.h" +#include "BKE_deform.h" +#include "BKE_mesh.h" +#include "BKE_modifier.h" +#include "BKE_texture.h" /* Texture masking. */ + +#include "depsgraph_private.h" +#include "MEM_guardedalloc.h" +#include "MOD_util.h" +#include "MOD_weightvg_util.h" + + +/** + * This mixes the old weight with the new weight factor. + */ +static float mix_weight(float weight, float weight2, char mix_mode) +{ +#if 0 + /* + * XXX Don't know why, but the switch version takes many CPU time, + * and produces lag in realtime playback... + */ + switch (mix_mode) + { + case MOD_WVG_MIX_ADD: + return (weight + weight2); + case MOD_WVG_MIX_SUB: + return (weight - weight2); + case MOD_WVG_MIX_MUL: + return (weight * weight2); + case MOD_WVG_MIX_DIV: + /* Avoid dividing by zero (or really small values). */ + if (0.0 <= weight2 < MOD_WVG_ZEROFLOOR) + weight2 = MOD_WVG_ZEROFLOOR; + else if (-MOD_WVG_ZEROFLOOR < weight2) + weight2 = -MOD_WVG_ZEROFLOOR; + return (weight / weight2); + case MOD_WVG_MIX_DIF: + return (weight < weight2 ? weight2 - weight : weight - weight2); + case MOD_WVG_MIX_AVG: + return (weight + weight2) / 2.0; + case MOD_WVG_MIX_SET: + default: + return weight2; + } +#endif + if (mix_mode == MOD_WVG_MIX_SET) + return weight2; + else if (mix_mode == MOD_WVG_MIX_ADD) + return (weight + weight2); + else if (mix_mode == MOD_WVG_MIX_SUB) + return (weight - weight2); + else if (mix_mode == MOD_WVG_MIX_MUL) + return (weight * weight2); + else if (mix_mode == MOD_WVG_MIX_DIV) { + /* Avoid dividing by zero (or really small values). */ + if (weight2 < 0.0f && weight2 > -MOD_WVG_ZEROFLOOR) + weight2 = -MOD_WVG_ZEROFLOOR; + else if (weight2 >= 0.0f && weight2 < MOD_WVG_ZEROFLOOR) + weight2 = MOD_WVG_ZEROFLOOR; + return (weight / weight2); + } + else if (mix_mode == MOD_WVG_MIX_DIF) + return (weight < weight2 ? weight2 - weight : weight - weight2); + else if (mix_mode == MOD_WVG_MIX_AVG) + return (weight + weight2) * 0.5f; + else return weight2; +} + +/************************************** + * Modifiers functions. * + **************************************/ +static void initData(ModifierData *md) +{ + WeightVGMixModifierData *wmd = (WeightVGMixModifierData*) md; + + wmd->default_weight_a = 0.0f; + wmd->default_weight_b = 0.0f; + wmd->mix_mode = MOD_WVG_MIX_SET; + wmd->mix_set = MOD_WVG_SET_AND; + + wmd->mask_constant = 1.0f; + wmd->mask_tex_use_channel = MOD_WVG_MASK_TEX_USE_INT; /* Use intensity by default. */ + wmd->mask_tex_mapping = MOD_DISP_MAP_LOCAL; +} + +static void copyData(ModifierData *md, ModifierData *target) +{ + WeightVGMixModifierData *wmd = (WeightVGMixModifierData*) md; + WeightVGMixModifierData *twmd = (WeightVGMixModifierData*) target; + + BLI_strncpy(twmd->defgrp_name_a, wmd->defgrp_name_a, sizeof(twmd->defgrp_name_a)); + BLI_strncpy(twmd->defgrp_name_b, wmd->defgrp_name_b, sizeof(twmd->defgrp_name_b)); + twmd->default_weight_a = wmd->default_weight_a; + twmd->default_weight_b = wmd->default_weight_b; + twmd->mix_mode = wmd->mix_mode; + twmd->mix_set = wmd->mix_set; + + twmd->mask_constant = wmd->mask_constant; + BLI_strncpy(twmd->mask_defgrp_name, wmd->mask_defgrp_name, sizeof(twmd->mask_defgrp_name)); + twmd->mask_texture = wmd->mask_texture; + twmd->mask_tex_use_channel = wmd->mask_tex_use_channel; + twmd->mask_tex_mapping = wmd->mask_tex_mapping; + twmd->mask_tex_map_obj = wmd->mask_tex_map_obj; + BLI_strncpy(twmd->mask_tex_uvlayer_name, wmd->mask_tex_uvlayer_name, sizeof(twmd->mask_tex_uvlayer_name)); +} + +static CustomDataMask requiredDataMask(Object *UNUSED(ob), ModifierData *md) +{ + WeightVGMixModifierData *wmd = (WeightVGMixModifierData*) md; + CustomDataMask dataMask = 0; + + /* We need vertex groups! */ + dataMask |= CD_MASK_MDEFORMVERT; + + /* Ask for UV coordinates if we need them. */ + if(wmd->mask_tex_mapping == MOD_DISP_MAP_UV) + dataMask |= CD_MASK_MTFACE; + + return dataMask; +} + +static int dependsOnTime(ModifierData *md) +{ + WeightVGMixModifierData *wmd = (WeightVGMixModifierData*) md; + + if(wmd->mask_texture) + return BKE_texture_dependsOnTime(wmd->mask_texture); + return 0; +} + +static void foreachObjectLink(ModifierData *md, Object *ob, + void (*walk)(void *userData, Object *ob, Object **obpoin), + void *userData) +{ + WeightVGMixModifierData *wmd = (WeightVGMixModifierData*) md; + walk(userData, ob, &wmd->mask_tex_map_obj); +} + +static void foreachIDLink(ModifierData *md, Object *ob, IDWalkFunc walk, void *userData) +{ + WeightVGMixModifierData *wmd = (WeightVGMixModifierData*) md; + + walk(userData, ob, (ID **)&wmd->mask_texture); + + foreachObjectLink(md, ob, (ObjectWalkFunc)walk, userData); +} + +static void foreachTexLink(ModifierData *md, Object *ob, TexWalkFunc walk, void *userData) +{ + walk(userData, ob, md, "mask_texture"); +} + +static void updateDepgraph(ModifierData *md, DagForest *forest, struct Scene *UNUSED(scene), + Object *UNUSED(ob), DagNode *obNode) +{ + WeightVGMixModifierData *wmd = (WeightVGMixModifierData*) md; + DagNode *curNode; + + if(wmd->mask_tex_map_obj && wmd->mask_tex_mapping == MOD_DISP_MAP_OBJECT) { + curNode = dag_get_node(forest, wmd->mask_tex_map_obj); + + dag_add_relation(forest, curNode, obNode, DAG_RL_DATA_DATA|DAG_RL_OB_DATA, + "WeightVGMix Modifier"); + } + + if(wmd->mask_tex_mapping == MOD_DISP_MAP_GLOBAL) + dag_add_relation(forest, obNode, obNode, DAG_RL_DATA_DATA|DAG_RL_OB_DATA, + "WeightVGMix Modifier"); +} + +static int isDisabled(ModifierData *md, int UNUSED(useRenderParams)) +{ + WeightVGMixModifierData *wmd = (WeightVGMixModifierData*) md; + /* If no vertex group, bypass. */ + return (wmd->defgrp_name_a[0] == '\0'); +} + +static DerivedMesh *applyModifier(ModifierData *md, Object *ob, DerivedMesh *derivedData, + int UNUSED(useRenderParams), int UNUSED(isFinalCalc)) +{ + WeightVGMixModifierData *wmd = (WeightVGMixModifierData*) md; + DerivedMesh *dm = derivedData, *ret = NULL; +#if 0 + Mesh *ob_m = NULL; +#endif + MDeformVert *dvert = NULL; + int numVerts; + int defgrp_idx, defgrp_idx2 = -1; + float *org_w; + float *new_w; + int *tidx, *indices = NULL; + int numIdx = 0; + int i, j; + char rel_ret = 0; /* Boolean, whether we have to release ret dm or not, when not using it! */ + + /* Get number of verts. */ + numVerts = dm->getNumVerts(dm); + + /* Check if we can just return the original mesh. + * Must have verts and therefore verts assigned to vgroups to do anything useful! + */ + if ((numVerts == 0) || (ob->defbase.first == NULL)) + return dm; + + /* Get vgroup idx from its name. */ + defgrp_idx = defgroup_name_index(ob, wmd->defgrp_name_a); + if (defgrp_idx < 0) + return dm; + /* Get seconf vgroup idx from its name, if given. */ + if (wmd->defgrp_name_b[0] != (char)0) { + defgrp_idx2 = defgroup_name_index(ob, wmd->defgrp_name_b); + if (defgrp_idx2 < 0) + return dm; + } + + /* XXX All this to avoid copying dm when not needed... However, it nearly doubles compute + * time! See scene 5 of the WeighVG test file... + */ +#if 0 + /* Get actual dverts (ie vertex group data). */ + dvert = dm->getVertDataArray(dm, CD_MDEFORMVERT); + /* If no dverts, return unmodified data... */ + if (dvert == NULL) + return dm; + + /* Get org mesh, only to test whether affected cdata layer has already been copied + * somewhere up in the modifiers stack. + */ + ob_m = get_mesh(ob); + if (ob_m == NULL) + return dm; + + /* Create a copy of our dmesh, only if our affected cdata layer is the same as org mesh. */ + if (dvert == CustomData_get_layer(&ob_m->vdata, CD_MDEFORMVERT)) { + /* XXX Seems to create problems with weightpaint mode??? + * I'm missing something here, I guess... + */ +// DM_set_only_copy(dm, CD_MASK_MDEFORMVERT); /* Only copy defgroup layer. */ + ret = CDDM_copy(dm); + dvert = ret->getVertDataArray(ret, CD_MDEFORMVERT); + if (dvert == NULL) { + ret->release(ret); + return dm; + } + rel_ret = 1; + } + else + ret = dm; +#else + ret = CDDM_copy(dm); + rel_ret = 1; + dvert = ret->getVertDataArray(ret, CD_MDEFORMVERT); + if (dvert == NULL) { + if (rel_ret) + ret->release(ret); + return dm; + } +#endif + + /* Find out which vertices to work on. */ + tidx = MEM_mallocN(sizeof(int) * numVerts, "WeightVGMix Modifier, tidx"); + switch (wmd->mix_set) { + case MOD_WVG_SET_A: + /* All vertices in first vgroup. */ + for (i = 0; i < numVerts; i++) { + for (j = 0; j < dvert[i].totweight; j++) { + if(dvert[i].dw[j].def_nr == defgrp_idx) { + tidx[numIdx++] = i; + break; + } + } + } + break; + case MOD_WVG_SET_B: + /* All vertices in second vgroup. */ + for (i = 0; i < numVerts; i++) { + for (j = 0; j < dvert[i].totweight; j++) { + if(dvert[i].dw[j].def_nr == defgrp_idx2) { + tidx[numIdx++] = i; + break; + } + } + } + break; + case MOD_WVG_SET_OR: + /* All vertices in one vgroup or the other. */ + for (i = 0; i < numVerts; i++) { + for (j = 0; j < dvert[i].totweight; j++) { + if(dvert[i].dw[j].def_nr == defgrp_idx || dvert[i].dw[j].def_nr == defgrp_idx2) { + tidx[numIdx++] = i; + break; + } + } + } + break; + case MOD_WVG_SET_AND: + /* All vertices in both vgroups. */ + for (i = 0; i < numVerts; i++) { + int idx1 = FALSE; + int idx2 = FALSE; + for (j = 0; j < dvert[i].totweight; j++) { + if(dvert[i].dw[j].def_nr == defgrp_idx) { + if (idx2 == TRUE) { + tidx[numIdx++] = i; + break; + } + else { + idx1 = TRUE; + } + } + else if(dvert[i].dw[j].def_nr == defgrp_idx2) { + if (idx1 == TRUE) { + tidx[numIdx++] = i; + break; + } + else { + idx2 = TRUE; + } + } + } + } + break; + case MOD_WVG_SET_ALL: + default: + /* Use all vertices, no need to do anything here. */ + break; + } + if (numIdx) { + indices = MEM_mallocN(sizeof(int) * numIdx, "WeightVGMix Modifier, indices"); + memcpy(indices, tidx, sizeof(int) * numIdx); + } + else + numIdx = numVerts; + MEM_freeN(tidx); + + org_w = MEM_mallocN(sizeof(float) * numIdx, "WeightVGMix Modifier, org_w"); + new_w = MEM_mallocN(sizeof(float) * numIdx, "WeightVGMix Modifier, new_w"); + + /* Mix weights. */ + for (i = 0; i < numIdx; i++) { + float weight2 = 0.0; + int w1 = FALSE; + int w2 = FALSE; + int idx = indices ? indices[i] : i; + for (j = 0; j < dvert[idx].totweight; j++) { + if(dvert[idx].dw[j].def_nr == defgrp_idx) { + org_w[i] = dvert[idx].dw[j].weight; + w1 = TRUE; + if (w2 == TRUE) + break; + } + else if(dvert[idx].dw[j].def_nr == defgrp_idx2) { + weight2 = dvert[idx].dw[j].weight; + w2 = TRUE; + if (w1 == TRUE) + break; + } + } + if (w1 == FALSE) + org_w[i] = wmd->default_weight_a; + if (w2 == FALSE) + weight2 = wmd->default_weight_b; + new_w[i] = mix_weight(org_w[i], weight2, wmd->mix_mode); + } + + /* Do masking. */ + weightvg_do_mask(numIdx, indices, org_w, new_w, ob, ret, wmd->mask_constant, + wmd->mask_defgrp_name, wmd->mask_texture, wmd->mask_tex_use_channel, + wmd->mask_tex_mapping, wmd->mask_tex_map_obj, wmd->mask_tex_uvlayer_name); + + /* Update (add to) vgroup. + * XXX Depending on the MOD_WVG_SET_xxx option chosen, we might have to add vertices to vgroup. + */ + weightvg_update_vg(dvert, defgrp_idx, numIdx, indices, org_w, TRUE, -FLT_MAX, 0, 0.0f); + + /* Freeing stuff. */ + MEM_freeN(org_w); + MEM_freeN(new_w); + + if (indices) + MEM_freeN(indices); + + /* Return the vgroup-modified mesh. */ + return ret; +} + +static DerivedMesh *applyModifierEM(ModifierData *md, Object *ob, + struct EditMesh *UNUSED(editData), + DerivedMesh *derivedData) +{ + return applyModifier(md, ob, derivedData, 0, 1); +} + + +ModifierTypeInfo modifierType_WeightVGMix = { + /* name */ "VertexWeightMix", + /* structName */ "WeightVGMixModifierData", + /* structSize */ sizeof(WeightVGMixModifierData), + /* type */ eModifierTypeType_Nonconstructive, + /* flags */ eModifierTypeFlag_AcceptsMesh +/* |eModifierTypeFlag_SupportsMapping*/ + |eModifierTypeFlag_SupportsEditmode, + + /* copyData */ copyData, + /* deformVerts */ NULL, + /* deformMatrices */ NULL, + /* deformVertsEM */ NULL, + /* deformMatricesEM */ NULL, + /* applyModifier */ applyModifier, + /* applyModifierEM */ applyModifierEM, + /* initData */ initData, + /* requiredDataMask */ requiredDataMask, + /* freeData */ NULL, + /* isDisabled */ isDisabled, + /* updateDepgraph */ updateDepgraph, + /* dependsOnTime */ dependsOnTime, + /* dependsOnNormals */ NULL, + /* foreachObjectLink */ foreachObjectLink, + /* foreachIDLink */ foreachIDLink, + /* foreachTexLink */ foreachTexLink, +}; diff --git a/source/blender/modifiers/intern/MOD_weightvgproximity.c b/source/blender/modifiers/intern/MOD_weightvgproximity.c new file mode 100644 index 00000000000..af3f71d6099 --- /dev/null +++ b/source/blender/modifiers/intern/MOD_weightvgproximity.c @@ -0,0 +1,567 @@ +/* + * $Id$ + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2011 by Bastien Montagne. + * All rights reserved. + * + * Contributor(s): None yet. + * + * ***** END GPL LICENSE BLOCK ***** + * + */ + +/* + * XXX I'd like to make modified weights visible in WeightPaint mode, + * but couldn't figure a way to do this... + * Maybe this will need changes in mesh_calc_modifiers (DerivedMesh.c)? + * Or the WeightPaint mode code itself? + */ + +#include "BLI_editVert.h" +#include "BLI_math.h" +#include "BLI_string.h" +#include "BLI_utildefines.h" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_modifier_types.h" +#include "DNA_object_types.h" + +#include "BKE_cdderivedmesh.h" +#include "BKE_deform.h" +#include "BKE_mesh.h" +#include "BKE_modifier.h" +#include "BKE_shrinkwrap.h" /* For SpaceTransform stuff. */ +#include "BKE_texture.h" /* Texture masking. */ + +#include "depsgraph_private.h" +#include "MEM_guardedalloc.h" +#include "MOD_util.h" +#include "MOD_weightvg_util.h" + +/************************************** + * Util functions. * + **************************************/ + +/* Util macro. */ +#define OUT_OF_MEMORY() ((void)printf("WeightVGProximity: Out of memory.\n")) + +/** + * Returns the squared distance between two given points. + */ +static float squared_dist(const float *a, const float *b) +{ + float tmp[3]; + VECSUB(tmp, a, b); + return INPR(tmp, tmp); +} + +/** + * Find nearest vertex and/or edge and/or face, for each vertex (adapted from shrinkwrap.c). + */ +static void get_vert2geom_distance(int numVerts, float (*v_cos)[3], + float *dist_v, float *dist_e, float *dist_f, + DerivedMesh *target, const SpaceTransform *loc2trgt) +{ + int i; + BVHTreeFromMesh treeData_v = NULL_BVHTreeFromMesh; + BVHTreeFromMesh treeData_e = NULL_BVHTreeFromMesh; + BVHTreeFromMesh treeData_f = NULL_BVHTreeFromMesh; + BVHTreeNearest nearest_v = NULL_BVHTreeNearest; + BVHTreeNearest nearest_e = NULL_BVHTreeNearest; + BVHTreeNearest nearest_f = NULL_BVHTreeNearest; + + if (dist_v) { + /* Create a bvh-tree of the given target's verts. */ + bvhtree_from_mesh_verts(&treeData_v, target, 0.0, 2, 6); + if(treeData_v.tree == NULL) { + OUT_OF_MEMORY(); + return; + } + } + if (dist_e) { + /* Create a bvh-tree of the given target's edges. */ + bvhtree_from_mesh_edges(&treeData_e, target, 0.0, 2, 6); + if(treeData_e.tree == NULL) { + OUT_OF_MEMORY(); + return; + } + } + if (dist_f) { + /* Create a bvh-tree of the given target's faces. */ + bvhtree_from_mesh_faces(&treeData_f, target, 0.0, 2, 6); + if(treeData_f.tree == NULL) { + OUT_OF_MEMORY(); + return; + } + } + + /* Setup nearest. */ + nearest_v.index = nearest_e.index = nearest_f.index = -1; + /*nearest_v.dist = nearest_e.dist = nearest_f.dist = FLT_MAX;*/ + /* Find the nearest vert/edge/face. */ +#ifndef __APPLE__ +#pragma omp parallel for default(none) private(i) firstprivate(nearest_v,nearest_e,nearest_f) \ + shared(treeData_v,treeData_e,treeData_f,numVerts,v_cos,dist_v,dist_e, \ + dist_f,loc2trgt) \ + schedule(static) +#endif + for (i = 0; i < numVerts; ++i) { + float tmp_co[3]; + + /* Convert the vertex to tree coordinates. */ + VECCOPY(tmp_co, v_cos[i]); + space_transform_apply(loc2trgt, tmp_co); + + /* Use local proximity heuristics (to reduce the nearest search). + * + * If we already had an hit before, we assume this vertex is going to have a close hit to + * that other vertex, so we can initiate the "nearest.dist" with the expected value to that + * last hit. + * This will lead in prunning of the search tree. + */ + if (dist_v) { + nearest_v.dist = nearest_v.index != -1 ? squared_dist(tmp_co, nearest_v.co) : FLT_MAX; + /* Compute and store result. If invalid (-1 idx), keep FLT_MAX dist. */ + BLI_bvhtree_find_nearest(treeData_v.tree, tmp_co, &nearest_v, treeData_v.nearest_callback, &treeData_v); + dist_v[i] = sqrtf(nearest_v.dist); + } + if (dist_e) { + nearest_e.dist = nearest_e.index != -1 ? squared_dist(tmp_co, nearest_e.co) : FLT_MAX; + /* Compute and store result. If invalid (-1 idx), keep FLT_MAX dist. */ + BLI_bvhtree_find_nearest(treeData_e.tree, tmp_co, &nearest_e, treeData_e.nearest_callback, &treeData_e); + dist_e[i] = sqrtf(nearest_e.dist); + } + if (dist_f) { + nearest_f.dist = nearest_f.index != -1 ? squared_dist(tmp_co, nearest_f.co) : FLT_MAX; + /* Compute and store result. If invalid (-1 idx), keep FLT_MAX dist. */ + BLI_bvhtree_find_nearest(treeData_f.tree, tmp_co, &nearest_f, treeData_f.nearest_callback, &treeData_f); + dist_f[i] = sqrtf(nearest_f.dist); + } + } + + if (dist_v) + free_bvhtree_from_mesh(&treeData_v); + if (dist_e) + free_bvhtree_from_mesh(&treeData_e); + if (dist_f) + free_bvhtree_from_mesh(&treeData_f); +} + +/** + * Returns the real distance between a vertex and another reference object. + * Note that it works in final world space (i.e. with constraints etc. applied). + */ +static void get_vert2ob_distance(int numVerts, float (*v_cos)[3], float *dist, + Object* ob, Object* obr) +{ + /* Vertex and ref object coordinates. */ + float v_wco[3]; + unsigned int i= numVerts; + + while(i-- > 0) { + /* Get world-coordinates of the vertex (constraints and anim included). */ + mul_v3_m4v3(v_wco, ob->obmat, v_cos[i]); + /* Return distance between both coordinates. */ + dist[i] = len_v3v3(v_wco, obr->obmat[3]); + } +} + +/** + * Returns the real distance between an object and another reference object. + * Note that it works in final world space (i.e. with constraints etc. applied). + */ +static float get_ob2ob_distance(const Object* ob, const Object* obr) +{ + return len_v3v3(ob->obmat[3], obr->obmat[3]); +} + +/** + * Maps distances to weights, with an optionnal “smoothing” mapping. + */ +void do_map(float *weights, const int nidx, const float min_d, const float max_d, short mode) +{ + const float range_inv= 1.0f / (max_d - min_d); /* invert since multiplication is faster */ + unsigned int i= nidx; + if(max_d == min_d) { + while (i-- > 0) { + weights[i] = (weights[i] >= max_d) ? 1.0f : 0.0f; /* "Step" behavior... */ + } + } + else if(max_d > min_d) { + while (i-- > 0) { + if (weights[i] >= max_d) weights[i]= 1.0f; /* most likely case first */ + else if(weights[i] <= min_d) weights[i]= 0.0f; + else weights[i]= (weights[i] - min_d) * range_inv; + } + } + else { + while (i-- > 0) { + if (weights[i] <= max_d) weights[i]= 1.0f; /* most likely case first */ + else if(weights[i] >= min_d) weights[i]= 0.0f; + else weights[i]= (weights[i] - min_d) * range_inv; + } + } + + if(!ELEM(mode, MOD_WVG_MAPPING_NONE, MOD_WVG_MAPPING_CURVE)) { + weightvg_do_map(nidx, weights, mode, NULL); + } +} + +/************************************** + * Modifiers functions. * + **************************************/ +static void initData(ModifierData *md) +{ + WeightVGProximityModifierData *wmd = (WeightVGProximityModifierData*) md; + + wmd->proximity_mode = MOD_WVG_PROXIMITY_OBJECT; + wmd->proximity_flags = MOD_WVG_PROXIMITY_GEOM_VERTS; + + wmd->falloff_type = MOD_WVG_MAPPING_NONE; + + wmd->mask_constant = 1.0f; + wmd->mask_tex_use_channel = MOD_WVG_MASK_TEX_USE_INT; /* Use intensity by default. */ + wmd->mask_tex_mapping = MOD_DISP_MAP_LOCAL; + wmd->max_dist = 1.0f; /* vert arbitrary distance, but don't use 0 */ +} + +static void copyData(ModifierData *md, ModifierData *target) +{ + WeightVGProximityModifierData *wmd = (WeightVGProximityModifierData*) md; + WeightVGProximityModifierData *twmd = (WeightVGProximityModifierData*) target; + + BLI_strncpy(twmd->defgrp_name, wmd->defgrp_name, sizeof(twmd->defgrp_name)); + twmd->proximity_mode = wmd->proximity_mode; + twmd->proximity_flags = wmd->proximity_flags; + twmd->proximity_ob_target = wmd->proximity_ob_target; + + twmd->falloff_type = wmd->falloff_type; + + twmd->mask_constant = wmd->mask_constant; + BLI_strncpy(twmd->mask_defgrp_name, wmd->mask_defgrp_name, sizeof(twmd->mask_defgrp_name)); + twmd->mask_texture = wmd->mask_texture; + twmd->mask_tex_use_channel = wmd->mask_tex_use_channel; + twmd->mask_tex_mapping = wmd->mask_tex_mapping; + twmd->mask_tex_map_obj = wmd->mask_tex_map_obj; + BLI_strncpy(twmd->mask_tex_uvlayer_name, wmd->mask_tex_uvlayer_name, sizeof(twmd->mask_tex_uvlayer_name)); + twmd->min_dist = wmd->min_dist; + twmd->max_dist = wmd->max_dist; +} + +static CustomDataMask requiredDataMask(Object *UNUSED(ob), ModifierData *md) +{ + WeightVGProximityModifierData *wmd = (WeightVGProximityModifierData*) md; + CustomDataMask dataMask = 0; + + /* We need vertex groups! */ + dataMask |= CD_MASK_MDEFORMVERT; + + /* Ask for UV coordinates if we need them. */ + if(wmd->mask_tex_mapping == MOD_DISP_MAP_UV) + dataMask |= CD_MASK_MTFACE; + + return dataMask; +} + +static int dependsOnTime(ModifierData *md) +{ + WeightVGProximityModifierData *wmd = (WeightVGProximityModifierData*) md; + + if(wmd->mask_texture) + return BKE_texture_dependsOnTime(wmd->mask_texture); + return 0; +} + +static void foreachObjectLink(ModifierData *md, Object *ob, + void (*walk)(void *userData, Object *ob, Object **obpoin), + void *userData) +{ + WeightVGProximityModifierData *wmd = (WeightVGProximityModifierData*) md; + walk(userData, ob, &wmd->proximity_ob_target); + walk(userData, ob, &wmd->mask_tex_map_obj); +} + +static void foreachIDLink(ModifierData *md, Object *ob, IDWalkFunc walk, void *userData) +{ + WeightVGProximityModifierData *wmd = (WeightVGProximityModifierData*) md; + + walk(userData, ob, (ID **)&wmd->mask_texture); + + foreachObjectLink(md, ob, (ObjectWalkFunc)walk, userData); +} + +static void foreachTexLink(ModifierData *md, Object *ob, TexWalkFunc walk, void *userData) +{ + walk(userData, ob, md, "mask_texture"); +} + +static void updateDepgraph(ModifierData *md, DagForest *forest, struct Scene *UNUSED(scene), + Object *UNUSED(ob), DagNode *obNode) +{ + WeightVGProximityModifierData *wmd = (WeightVGProximityModifierData*) md; + DagNode *curNode; + + if (wmd->proximity_ob_target) { + curNode = dag_get_node(forest, wmd->proximity_ob_target); + dag_add_relation(forest, curNode, obNode, DAG_RL_DATA_DATA|DAG_RL_OB_DATA, + "WeightVGProximity Modifier"); + } + + if(wmd->mask_tex_map_obj && wmd->mask_tex_mapping == MOD_DISP_MAP_OBJECT) { + curNode = dag_get_node(forest, wmd->mask_tex_map_obj); + + dag_add_relation(forest, curNode, obNode, DAG_RL_DATA_DATA|DAG_RL_OB_DATA, + "WeightVGProximity Modifier"); + } + + if(wmd->mask_tex_mapping == MOD_DISP_MAP_GLOBAL) + dag_add_relation(forest, obNode, obNode, DAG_RL_DATA_DATA|DAG_RL_OB_DATA, + "WeightVGProximity Modifier"); +} + +static int isDisabled(ModifierData *md, int UNUSED(useRenderParams)) +{ + WeightVGProximityModifierData *wmd = (WeightVGProximityModifierData*) md; + /* If no vertex group, bypass. */ + if (wmd->defgrp_name[0] == '\0') return 1; + /* If no target object, bypass. */ + return (wmd->proximity_ob_target == NULL); +} + +static DerivedMesh *applyModifier(ModifierData *md, Object *ob, DerivedMesh *derivedData, + int UNUSED(useRenderParams), int UNUSED(isFinalCalc)) +{ + WeightVGProximityModifierData *wmd = (WeightVGProximityModifierData*) md; + DerivedMesh *dm = derivedData, *ret = NULL; +#if 0 + Mesh *ob_m = NULL; +#endif + MDeformVert *dvert = NULL; + int numVerts; + float (*v_cos)[3] = NULL; /* The vertices coordinates. */ + Object *obr = NULL; /* Our target object. */ + int defgrp_idx; + float *tw = NULL; + float *org_w = NULL; + float *new_w =NULL; + int *tidx, *indices = NULL; + int numIdx = 0; + int i, j; + char rel_ret = 0; /* Boolean, whether we have to release ret dm or not, when not using it! */ + + /* Get number of verts. */ + numVerts = dm->getNumVerts(dm); + + /* Check if we can just return the original mesh. + * Must have verts and therefore verts assigned to vgroups to do anything useful! + */ + if ((numVerts == 0) || (ob->defbase.first == NULL)) + return dm; + + /* Get our target object. */ + obr = wmd->proximity_ob_target; + if (obr == NULL) + return dm; + + /* Get vgroup idx from its name. */ + defgrp_idx = defgroup_name_index(ob, wmd->defgrp_name); + if (defgrp_idx < 0) + return dm; + + /* XXX All this to avoid copying dm when not needed... However, it nearly doubles compute + * time! See scene 5 of the WeighVG test file... + */ +#if 0 + /* Get actual dverts (ie vertex group data). */ + dvert = dm->getVertDataArray(dm, CD_MDEFORMVERT); + /* If no dverts, return unmodified data... */ + if (dvert == NULL) + return dm; + + /* Get org mesh, only to test whether affected cdata layer has already been copied + * somewhere up in the modifiers stack. + */ + ob_m = get_mesh(ob); + if (ob_m == NULL) + return dm; + + /* Create a copy of our dmesh, only if our affected cdata layer is the same as org mesh. */ + if (dvert == CustomData_get_layer(&ob_m->vdata, CD_MDEFORMVERT)) { + /* XXX Seems to create problems with weightpaint mode??? + * I'm missing something here, I guess... + */ +// DM_set_only_copy(dm, CD_MASK_MDEFORMVERT); /* Only copy defgroup layer. */ + ret = CDDM_copy(dm); + dvert = ret->getVertDataArray(ret, CD_MDEFORMVERT); + if (dvert == NULL) { + ret->release(ret); + return dm; + } + rel_ret = 1; + } + else + ret = dm; +#else + ret = CDDM_copy(dm); + rel_ret = 1; + dvert = ret->getVertDataArray(ret, CD_MDEFORMVERT); + if (dvert == NULL) { + if (rel_ret) + ret->release(ret); + return dm; + } +#endif + + /* Find out which vertices to work on (all vertices in vgroup), and get their relevant weight. + */ + tidx = MEM_mallocN(sizeof(int) * numVerts, "WeightVGProximity Modifier, tidx"); + tw = MEM_mallocN(sizeof(float) * numVerts, "WeightVGProximity Modifier, tw"); + for (i = 0; i < numVerts; i++) { + for (j = 0; j < dvert[i].totweight; j++) { + if(dvert[i].dw[j].def_nr == defgrp_idx) { + tidx[numIdx] = i; + tw[numIdx++] = dvert[i].dw[j].weight; + break; + } + } + } + indices = MEM_mallocN(sizeof(int) * numIdx, "WeightVGProximity Modifier, indices"); + memcpy(indices, tidx, sizeof(int) * numIdx); + org_w = MEM_mallocN(sizeof(float) * numIdx, "WeightVGProximity Modifier, org_w"); + new_w = MEM_mallocN(sizeof(float) * numIdx, "WeightVGProximity Modifier, new_w"); + memcpy(org_w, tw, sizeof(float) * numIdx); + MEM_freeN(tidx); + MEM_freeN(tw); + + /* Get our vertex coordinates. */ + v_cos = MEM_mallocN(sizeof(float[3]) * numIdx, "WeightVGProximity Modifier, v_cos"); + for (i = 0; i < numIdx; i++) + ret->getVertCo(ret, indices[i], v_cos[i]); + + /* Compute wanted distances. */ + if (wmd->proximity_mode == MOD_WVG_PROXIMITY_OBJECT) { + float dist = get_ob2ob_distance(ob, obr); + for(i = 0; i < numIdx; i++) + new_w[i] = dist; + } + else if (wmd->proximity_mode == MOD_WVG_PROXIMITY_GEOMETRY) { + const short use_trgt_verts = (wmd->proximity_flags & MOD_WVG_PROXIMITY_GEOM_VERTS); + const short use_trgt_edges = (wmd->proximity_flags & MOD_WVG_PROXIMITY_GEOM_EDGES); + const short use_trgt_faces = (wmd->proximity_flags & MOD_WVG_PROXIMITY_GEOM_FACES); + + if (use_trgt_verts || use_trgt_edges || use_trgt_faces) { + DerivedMesh *target_dm = obr->derivedFinal; + if (!target_dm) { + if (ELEM3(obr->type, OB_CURVE, OB_SURF, OB_FONT)) + target_dm = CDDM_from_curve(obr); + else if (obr->type == OB_MESH) { + Mesh *me = (Mesh*)obr->data; + if (me->edit_mesh) + target_dm = CDDM_from_editmesh((EditMesh*)me->edit_mesh, me); + else + target_dm = CDDM_from_mesh(me, obr); + } + } + + /* We must check that we do have a valid target_dm! */ + if (target_dm) { + SpaceTransform loc2trgt; + float *dists_v = use_trgt_verts ? MEM_mallocN(sizeof(float) * numIdx, "WeightVGProximity Modifier, dists_v") : NULL; + float *dists_e = use_trgt_edges ? MEM_mallocN(sizeof(float) * numIdx, "WeightVGProximity Modifier, dists_e") : NULL; + float *dists_f = use_trgt_faces ? MEM_mallocN(sizeof(float) * numIdx, "WeightVGProximity Modifier, dists_f") : NULL; + + space_transform_setup(&loc2trgt, ob, obr); + get_vert2geom_distance(numIdx, v_cos, dists_v, dists_e, dists_f, + target_dm, &loc2trgt); + for(i = 0; i < numIdx; i++) { + new_w[i] = dists_v ? dists_v[i] : FLT_MAX; + new_w[i] = dists_e ? minf(dists_e[i], new_w[i]) : new_w[i]; + new_w[i] = dists_f ? minf(dists_f[i], new_w[i]) : new_w[i]; + } + if(dists_v) MEM_freeN(dists_v); + if(dists_e) MEM_freeN(dists_e); + if(dists_f) MEM_freeN(dists_f); + } + /* Else, fall back to default obj2vert behavior. */ + else { + get_vert2ob_distance(numIdx, v_cos, new_w, ob, obr); + } + } + else { + get_vert2ob_distance(numIdx, v_cos, new_w, ob, obr); + } + } + + /* Map distances to weights. */ + do_map(new_w, numIdx, wmd->min_dist, wmd->max_dist, wmd->falloff_type); + + /* Do masking. */ + weightvg_do_mask(numIdx, indices, org_w, new_w, ob, ret, wmd->mask_constant, + wmd->mask_defgrp_name, wmd->mask_texture, wmd->mask_tex_use_channel, + wmd->mask_tex_mapping, wmd->mask_tex_map_obj, wmd->mask_tex_uvlayer_name); + + /* Update vgroup. Note we never add nor remove vertices from vgroup here. */ + weightvg_update_vg(dvert, defgrp_idx, numIdx, indices, org_w, 0, 0.0f, 0, 0.0f); + + /* Freeing stuff. */ + MEM_freeN(org_w); + MEM_freeN(new_w); + MEM_freeN(indices); + MEM_freeN(v_cos); + + /* Return the vgroup-modified mesh. */ + return ret; +} + +static DerivedMesh *applyModifierEM(ModifierData *md, Object *ob, + struct EditMesh *UNUSED(editData), + DerivedMesh *derivedData) +{ + return applyModifier(md, ob, derivedData, 0, 1); +} + + +ModifierTypeInfo modifierType_WeightVGProximity = { + /* name */ "VertexWeightProximity", + /* structName */ "WeightVGProximityModifierData", + /* structSize */ sizeof(WeightVGProximityModifierData), + /* type */ eModifierTypeType_Nonconstructive, + /* flags */ eModifierTypeFlag_AcceptsMesh +/* |eModifierTypeFlag_SupportsMapping*/ + |eModifierTypeFlag_SupportsEditmode, + + /* copyData */ copyData, + /* deformVerts */ NULL, + /* deformMatrices */ NULL, + /* deformVertsEM */ NULL, + /* deformMatricesEM */ NULL, + /* applyModifier */ applyModifier, + /* applyModifierEM */ applyModifierEM, + /* initData */ initData, + /* requiredDataMask */ requiredDataMask, + /* freeData */ NULL, + /* isDisabled */ isDisabled, + /* updateDepgraph */ updateDepgraph, + /* dependsOnTime */ dependsOnTime, + /* dependsOnNormals */ NULL, + /* foreachObjectLink */ foreachObjectLink, + /* foreachIDLink */ foreachIDLink, + /* foreachTexLink */ foreachTexLink, +}; |