This repository has been archived on 2023-10-09. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
blender-archive/source/blender/modifiers/intern/MOD_weightvgmix.cc
Hans Goudey 1dc57a89e9 Mesh: Move functions to C++ header
Refactoring mesh code, it has become clear that local cleanups and
simplifications are limited by the need to keep a C public API for
mesh functions. This change makes code more obvious and makes further
refactoring much easier.

- Add a new `BKE_mesh.hh` header for a C++ only mesh API
- Introduce a new `blender::bke::mesh` namespace, documented here:
  https://wiki.blender.org/wiki/Source/Objects/Mesh#Namespaces
- Move some functions to the new namespace, cleaning up their arguments
- Move code to `Array` and `float3` where necessary to use the new API
- Define existing inline mesh data access functions to the new header
- Keep some C API functions where necessary because of RNA
- Move all C++ files to use the new header, which includes the old one

In the future it may make sense to split up `BKE_mesh.hh` more, but for
now keeping the same name as the existing header keeps things simple.

Pull Request: blender/blender#105416
2023-03-12 22:29:15 +01:00

525 lines
16 KiB
C++

/* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright 2011 by Bastien Montagne. All rights reserved. */
/** \file
* \ingroup modifiers
*/
#include "BLI_utildefines.h"
#include "BLI_listbase.h"
#include "BLI_math.h"
#include "BLT_translation.h"
#include "DNA_defaults.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_modifier_types.h"
#include "DNA_object_types.h"
#include "DNA_screen_types.h"
#include "BKE_context.h"
#include "BKE_customdata.h"
#include "BKE_deform.h"
#include "BKE_lib_query.h"
#include "BKE_mesh.hh"
#include "BKE_modifier.h"
#include "BKE_screen.h"
#include "BKE_texture.h" /* Texture masking. */
#include "UI_interface.h"
#include "UI_resources.h"
#include "RNA_access.h"
#include "RNA_prototypes.h"
#include "DEG_depsgraph_build.h"
#include "DEG_depsgraph_query.h"
#include "MEM_guardedalloc.h"
#include "MOD_modifiertypes.h"
#include "MOD_ui_common.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;
}
if (mix_mode == MOD_WVG_MIX_ADD) {
return (weight + weight2);
}
if (mix_mode == MOD_WVG_MIX_SUB) {
return (weight - weight2);
}
if (mix_mode == MOD_WVG_MIX_MUL) {
return (weight * weight2);
}
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);
}
if (mix_mode == MOD_WVG_MIX_DIF) {
return (weight < weight2 ? weight2 - weight : weight - weight2);
}
if (mix_mode == MOD_WVG_MIX_AVG) {
return (weight + weight2) * 0.5f;
}
if (mix_mode == MOD_WVG_MIX_MIN) {
return (weight < weight2 ? weight : weight2);
}
if (mix_mode == MOD_WVG_MIX_MAX) {
return (weight > weight2 ? weight : weight2);
}
return weight2;
}
/**************************************
* Modifiers functions. *
**************************************/
static void initData(ModifierData *md)
{
WeightVGMixModifierData *wmd = (WeightVGMixModifierData *)md;
BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(wmd, modifier));
MEMCPY_STRUCT_AFTER(wmd, DNA_struct_default_get(WeightVGMixModifierData), modifier);
}
static void requiredDataMask(ModifierData *md, CustomData_MeshMasks *r_cddata_masks)
{
WeightVGMixModifierData *wmd = (WeightVGMixModifierData *)md;
/* We need vertex groups! */
r_cddata_masks->vmask |= CD_MASK_MDEFORMVERT;
/* Ask for UV coordinates if we need them. */
if (wmd->mask_tex_mapping == MOD_DISP_MAP_UV) {
r_cddata_masks->fmask |= CD_MASK_MTFACE;
}
/* No need to ask for CD_PREVIEW_MLOOPCOL... */
}
static bool dependsOnTime(Scene * /*scene*/, ModifierData *md)
{
WeightVGMixModifierData *wmd = (WeightVGMixModifierData *)md;
if (wmd->mask_texture) {
return BKE_texture_dependsOnTime(wmd->mask_texture);
}
return false;
}
static void foreachIDLink(ModifierData *md, Object *ob, IDWalkFunc walk, void *userData)
{
WeightVGMixModifierData *wmd = (WeightVGMixModifierData *)md;
walk(userData, ob, (ID **)&wmd->mask_texture, IDWALK_CB_USER);
walk(userData, ob, (ID **)&wmd->mask_tex_map_obj, IDWALK_CB_NOP);
}
static void foreachTexLink(ModifierData *md, Object *ob, TexWalkFunc walk, void *userData)
{
walk(userData, ob, md, "mask_texture");
}
static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphContext *ctx)
{
WeightVGMixModifierData *wmd = (WeightVGMixModifierData *)md;
bool need_transform_relation = false;
if (wmd->mask_texture != nullptr) {
DEG_add_generic_id_relation(ctx->node, &wmd->mask_texture->id, "WeightVGMix Modifier");
if (wmd->mask_tex_map_obj != nullptr && wmd->mask_tex_mapping == MOD_DISP_MAP_OBJECT) {
MOD_depsgraph_update_object_bone_relation(
ctx->node, wmd->mask_tex_map_obj, wmd->mask_tex_map_bone, "WeightVGMix Modifier");
need_transform_relation = true;
}
else if (wmd->mask_tex_mapping == MOD_DISP_MAP_GLOBAL) {
need_transform_relation = true;
}
}
if (need_transform_relation) {
DEG_add_depends_on_transform_relation(ctx->node, "WeightVGMix Modifier");
}
}
static bool isDisabled(const Scene * /*scene*/, ModifierData *md, bool /*useRenderParams*/)
{
WeightVGMixModifierData *wmd = (WeightVGMixModifierData *)md;
/* If no vertex group, bypass. */
return (wmd->defgrp_name_a[0] == '\0');
}
static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mesh)
{
BLI_assert(mesh != nullptr);
WeightVGMixModifierData *wmd = (WeightVGMixModifierData *)md;
MDeformWeight **dw1, **tdw1, **dw2, **tdw2;
float *org_w;
float *new_w;
int *tidx, *indices = nullptr;
int index_num = 0;
int i;
const bool invert_vgroup_mask = (wmd->flag & MOD_WVG_MIX_INVERT_VGROUP_MASK) != 0;
const bool do_normalize = (wmd->flag & MOD_WVG_MIX_WEIGHTS_NORMALIZE) != 0;
/*
* Note that we only invert the weight values within provided vgroups, the selection based on
* which vertex is affected because it belongs or not to a group remains unchanged.
* In other words, vertices not belonging to a group won't be affected, even though their
* inverted 'virtual' weight would be 1.0f.
*/
const bool invert_vgroup_a = (wmd->flag & MOD_WVG_MIX_INVERT_VGROUP_A) != 0;
const bool invert_vgroup_b = (wmd->flag & MOD_WVG_MIX_INVERT_VGROUP_B) != 0;
/* Flags. */
#if 0
const bool do_prev = (wmd->modifier.mode & eModifierMode_DoWeightPreview) != 0;
#endif
/* Get number of verts. */
const int verts_num = mesh->totvert;
/* Check if we can just return the original mesh.
* Must have verts and therefore verts assigned to vgroups to do anything useful!
*/
if ((verts_num == 0) || BLI_listbase_is_empty(&mesh->vertex_group_names)) {
return mesh;
}
/* Get vgroup idx from its name. */
const int defgrp_index = BKE_id_defgroup_name_index(&mesh->id, wmd->defgrp_name_a);
if (defgrp_index == -1) {
return mesh;
}
/* Get second vgroup idx from its name, if given. */
int defgrp_index_other = -1;
if (wmd->defgrp_name_b[0] != '\0') {
defgrp_index_other = BKE_id_defgroup_name_index(&mesh->id, wmd->defgrp_name_b);
if (defgrp_index_other == -1) {
return mesh;
}
}
const bool has_mdef = CustomData_has_layer(&mesh->vdata, CD_MDEFORMVERT);
/* If no vertices were ever added to an object's vgroup, dvert might be nullptr. */
if (!has_mdef) {
/* If not affecting all vertices, just return. */
if (wmd->mix_set != MOD_WVG_SET_ALL) {
return mesh;
}
}
MDeformVert *dvert = BKE_mesh_deform_verts_for_write(mesh);
/* Ultimate security check. */
if (!dvert) {
return mesh;
}
/* Find out which vertices to work on. */
tidx = static_cast<int *>(MEM_malloc_arrayN(verts_num, sizeof(int), __func__));
tdw1 = static_cast<MDeformWeight **>(
MEM_malloc_arrayN(verts_num, sizeof(MDeformWeight *), __func__));
tdw2 = static_cast<MDeformWeight **>(
MEM_malloc_arrayN(verts_num, sizeof(MDeformWeight *), __func__));
switch (wmd->mix_set) {
case MOD_WVG_SET_A:
/* All vertices in first vgroup. */
for (i = 0; i < verts_num; i++) {
MDeformWeight *dw = BKE_defvert_find_index(&dvert[i], defgrp_index);
if (dw) {
tdw1[index_num] = dw;
tdw2[index_num] = (defgrp_index_other >= 0) ?
BKE_defvert_find_index(&dvert[i], defgrp_index_other) :
nullptr;
tidx[index_num++] = i;
}
}
break;
case MOD_WVG_SET_B:
/* All vertices in second vgroup. */
for (i = 0; i < verts_num; i++) {
MDeformWeight *dw = (defgrp_index_other >= 0) ?
BKE_defvert_find_index(&dvert[i], defgrp_index_other) :
nullptr;
if (dw) {
tdw1[index_num] = BKE_defvert_find_index(&dvert[i], defgrp_index);
tdw2[index_num] = dw;
tidx[index_num++] = i;
}
}
break;
case MOD_WVG_SET_OR:
/* All vertices in one vgroup or the other. */
for (i = 0; i < verts_num; i++) {
MDeformWeight *adw = BKE_defvert_find_index(&dvert[i], defgrp_index);
MDeformWeight *bdw = (defgrp_index_other >= 0) ?
BKE_defvert_find_index(&dvert[i], defgrp_index_other) :
nullptr;
if (adw || bdw) {
tdw1[index_num] = adw;
tdw2[index_num] = bdw;
tidx[index_num++] = i;
}
}
break;
case MOD_WVG_SET_AND:
/* All vertices in both vgroups. */
for (i = 0; i < verts_num; i++) {
MDeformWeight *adw = BKE_defvert_find_index(&dvert[i], defgrp_index);
MDeformWeight *bdw = (defgrp_index_other >= 0) ?
BKE_defvert_find_index(&dvert[i], defgrp_index_other) :
nullptr;
if (adw && bdw) {
tdw1[index_num] = adw;
tdw2[index_num] = bdw;
tidx[index_num++] = i;
}
}
break;
case MOD_WVG_SET_ALL:
default:
/* Use all vertices. */
for (i = 0; i < verts_num; i++) {
tdw1[i] = BKE_defvert_find_index(&dvert[i], defgrp_index);
tdw2[i] = (defgrp_index_other >= 0) ?
BKE_defvert_find_index(&dvert[i], defgrp_index_other) :
nullptr;
}
index_num = -1;
break;
}
if (index_num == 0) {
/* Use no vertices! Hence, return org data. */
MEM_freeN(tdw1);
MEM_freeN(tdw2);
MEM_freeN(tidx);
return mesh;
}
if (index_num != -1) {
indices = static_cast<int *>(MEM_malloc_arrayN(index_num, sizeof(int), __func__));
memcpy(indices, tidx, sizeof(int) * index_num);
dw1 = static_cast<MDeformWeight **>(
MEM_malloc_arrayN(index_num, sizeof(MDeformWeight *), __func__));
memcpy(dw1, tdw1, sizeof(MDeformWeight *) * index_num);
MEM_freeN(tdw1);
dw2 = static_cast<MDeformWeight **>(
MEM_malloc_arrayN(index_num, sizeof(MDeformWeight *), __func__));
memcpy(dw2, tdw2, sizeof(MDeformWeight *) * index_num);
MEM_freeN(tdw2);
}
else {
/* Use all vertices. */
index_num = verts_num;
/* Just copy MDeformWeight pointers arrays, they will be freed at the end. */
dw1 = tdw1;
dw2 = tdw2;
}
MEM_freeN(tidx);
org_w = static_cast<float *>(MEM_malloc_arrayN(index_num, sizeof(float), __func__));
new_w = static_cast<float *>(MEM_malloc_arrayN(index_num, sizeof(float), __func__));
/* Mix weights. */
for (i = 0; i < index_num; i++) {
float weight2;
if (invert_vgroup_a) {
org_w[i] = 1.0f - (dw1[i] ? dw1[i]->weight : wmd->default_weight_a);
}
else {
org_w[i] = dw1[i] ? dw1[i]->weight : wmd->default_weight_a;
}
if (invert_vgroup_b) {
weight2 = 1.0f - (dw2[i] ? dw2[i]->weight : wmd->default_weight_b);
}
else {
weight2 = dw2[i] ? dw2[i]->weight : wmd->default_weight_b;
}
new_w[i] = mix_weight(org_w[i], weight2, wmd->mix_mode);
}
/* Do masking. */
Scene *scene = DEG_get_evaluated_scene(ctx->depsgraph);
weightvg_do_mask(ctx,
index_num,
indices,
org_w,
new_w,
ctx->object,
mesh,
wmd->mask_constant,
wmd->mask_defgrp_name,
scene,
wmd->mask_texture,
wmd->mask_tex_use_channel,
wmd->mask_tex_mapping,
wmd->mask_tex_map_obj,
wmd->mask_tex_map_bone,
wmd->mask_tex_uvlayer_name,
invert_vgroup_mask);
/* 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_index,
dw1,
index_num,
indices,
org_w,
true,
-FLT_MAX,
false,
0.0f,
do_normalize);
/* If weight preview enabled... */
#if 0 /* XXX Currently done in mod stack :/ */
if (do_prev) {
DM_update_weight_mcol(ob, dm, 0, org_w, index_num, indices);
}
#endif
/* Freeing stuff. */
MEM_freeN(org_w);
MEM_freeN(new_w);
MEM_freeN(dw1);
MEM_freeN(dw2);
MEM_SAFE_FREE(indices);
mesh->runtime->is_original_bmesh = false;
/* Return the vgroup-modified mesh. */
return mesh;
}
static void panel_draw(const bContext * /*C*/, Panel *panel)
{
uiLayout *layout = panel->layout;
PointerRNA ob_ptr;
PointerRNA *ptr = modifier_panel_get_property_pointers(panel, &ob_ptr);
uiLayoutSetPropSep(layout, true);
modifier_vgroup_ui(layout, ptr, &ob_ptr, "vertex_group_a", "invert_vertex_group_a", nullptr);
modifier_vgroup_ui(layout, ptr, &ob_ptr, "vertex_group_b", "invert_vertex_group_b", IFACE_("B"));
uiItemS(layout);
uiItemR(layout, ptr, "default_weight_a", 0, nullptr, ICON_NONE);
uiItemR(layout, ptr, "default_weight_b", 0, IFACE_("B"), ICON_NONE);
uiItemS(layout);
uiItemR(layout, ptr, "mix_set", 0, nullptr, ICON_NONE);
uiItemR(layout, ptr, "mix_mode", 0, nullptr, ICON_NONE);
uiItemR(layout, ptr, "normalize", 0, nullptr, ICON_NONE);
modifier_panel_end(layout, ptr);
}
static void influence_panel_draw(const bContext *C, Panel *panel)
{
uiLayout *layout = panel->layout;
PointerRNA ob_ptr;
PointerRNA *ptr = modifier_panel_get_property_pointers(panel, &ob_ptr);
weightvg_ui_common(C, &ob_ptr, ptr, layout);
}
static void panelRegister(ARegionType *region_type)
{
PanelType *panel_type = modifier_panel_register(
region_type, eModifierType_WeightVGMix, panel_draw);
modifier_subpanel_register(
region_type, "influence", "Influence", nullptr, influence_panel_draw, panel_type);
}
ModifierTypeInfo modifierType_WeightVGMix = {
/*name*/ N_("VertexWeightMix"),
/*structName*/ "WeightVGMixModifierData",
/*structSize*/ sizeof(WeightVGMixModifierData),
/*srna*/ &RNA_VertexWeightMixModifier,
/*type*/ eModifierTypeType_NonGeometrical,
/*flags*/ eModifierTypeFlag_AcceptsMesh | eModifierTypeFlag_SupportsMapping |
eModifierTypeFlag_SupportsEditmode | eModifierTypeFlag_UsesPreview,
/*icon*/ ICON_MOD_VERTEX_WEIGHT,
/*copyData*/ BKE_modifier_copydata_generic,
/*deformVerts*/ nullptr,
/*deformMatrices*/ nullptr,
/*deformVertsEM*/ nullptr,
/*deformMatricesEM*/ nullptr,
/*modifyMesh*/ modifyMesh,
/*modifyGeometrySet*/ nullptr,
/*initData*/ initData,
/*requiredDataMask*/ requiredDataMask,
/*freeData*/ nullptr,
/*isDisabled*/ isDisabled,
/*updateDepsgraph*/ updateDepsgraph,
/*dependsOnTime*/ dependsOnTime,
/*dependsOnNormals*/ nullptr,
/*foreachIDLink*/ foreachIDLink,
/*foreachTexLink*/ foreachTexLink,
/*freeRuntimeData*/ nullptr,
/*panelRegister*/ panelRegister,
/*blendWrite*/ nullptr,
/*blendRead*/ nullptr,
};