From 406a98aff893570eef1f2d217b87a5997a7a60d6 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Thu, 6 Oct 2022 16:05:56 -0500 Subject: Cleanup: Move eight modifier files to C++ In preparation for moving mesh runtime data to C++. --- source/blender/modifiers/CMakeLists.txt | 16 +- source/blender/modifiers/MOD_modifiertypes.h | 2 +- source/blender/modifiers/intern/MOD_multires.c | 519 -------------- source/blender/modifiers/intern/MOD_multires.cc | 520 ++++++++++++++ source/blender/modifiers/intern/MOD_util.c | 328 --------- source/blender/modifiers/intern/MOD_util.cc | 330 +++++++++ source/blender/modifiers/intern/MOD_uvproject.c | 380 ---------- source/blender/modifiers/intern/MOD_uvproject.cc | 382 ++++++++++ source/blender/modifiers/intern/MOD_uvwarp.c | 339 --------- source/blender/modifiers/intern/MOD_uvwarp.cc | 338 +++++++++ .../blender/modifiers/intern/MOD_weighted_normal.c | 758 -------------------- .../modifiers/intern/MOD_weighted_normal.cc | 774 +++++++++++++++++++++ .../blender/modifiers/intern/MOD_weightvg_util.h | 8 + source/blender/modifiers/intern/MOD_weightvgedit.c | 423 ----------- .../blender/modifiers/intern/MOD_weightvgedit.cc | 422 +++++++++++ source/blender/modifiers/intern/MOD_weightvgmix.c | 522 -------------- source/blender/modifiers/intern/MOD_weightvgmix.cc | 524 ++++++++++++++ .../modifiers/intern/MOD_weightvgproximity.c | 767 -------------------- .../modifiers/intern/MOD_weightvgproximity.cc | 770 ++++++++++++++++++++ 19 files changed, 4077 insertions(+), 4045 deletions(-) delete mode 100644 source/blender/modifiers/intern/MOD_multires.c create mode 100644 source/blender/modifiers/intern/MOD_multires.cc delete mode 100644 source/blender/modifiers/intern/MOD_util.c create mode 100644 source/blender/modifiers/intern/MOD_util.cc delete mode 100644 source/blender/modifiers/intern/MOD_uvproject.c create mode 100644 source/blender/modifiers/intern/MOD_uvproject.cc delete mode 100644 source/blender/modifiers/intern/MOD_uvwarp.c create mode 100644 source/blender/modifiers/intern/MOD_uvwarp.cc delete mode 100644 source/blender/modifiers/intern/MOD_weighted_normal.c create mode 100644 source/blender/modifiers/intern/MOD_weighted_normal.cc delete mode 100644 source/blender/modifiers/intern/MOD_weightvgedit.c create mode 100644 source/blender/modifiers/intern/MOD_weightvgedit.cc delete mode 100644 source/blender/modifiers/intern/MOD_weightvgmix.c create mode 100644 source/blender/modifiers/intern/MOD_weightvgmix.cc delete mode 100644 source/blender/modifiers/intern/MOD_weightvgproximity.c create mode 100644 source/blender/modifiers/intern/MOD_weightvgproximity.cc diff --git a/source/blender/modifiers/CMakeLists.txt b/source/blender/modifiers/CMakeLists.txt index 90ae3c8093c..e3a88e61cbd 100644 --- a/source/blender/modifiers/CMakeLists.txt +++ b/source/blender/modifiers/CMakeLists.txt @@ -63,7 +63,7 @@ set(SRC intern/MOD_meshdeform.c intern/MOD_meshsequencecache.cc intern/MOD_mirror.c - intern/MOD_multires.c + intern/MOD_multires.cc intern/MOD_nodes.cc intern/MOD_none.c intern/MOD_normal_edit.cc @@ -86,18 +86,18 @@ set(SRC intern/MOD_surfacedeform.c intern/MOD_triangulate.c intern/MOD_ui_common.c - intern/MOD_util.c - intern/MOD_uvproject.c - intern/MOD_uvwarp.c + intern/MOD_util.cc + intern/MOD_uvproject.cc + intern/MOD_uvwarp.cc intern/MOD_volume_displace.cc intern/MOD_volume_to_mesh.cc intern/MOD_warp.c intern/MOD_wave.cc - intern/MOD_weighted_normal.c + intern/MOD_weighted_normal.cc intern/MOD_weightvg_util.c - intern/MOD_weightvgedit.c - intern/MOD_weightvgmix.c - intern/MOD_weightvgproximity.c + intern/MOD_weightvgedit.cc + intern/MOD_weightvgmix.cc + intern/MOD_weightvgproximity.cc intern/MOD_weld.cc intern/MOD_wireframe.c diff --git a/source/blender/modifiers/MOD_modifiertypes.h b/source/blender/modifiers/MOD_modifiertypes.h index 70d6cb9ff76..62b496017c4 100644 --- a/source/blender/modifiers/MOD_modifiertypes.h +++ b/source/blender/modifiers/MOD_modifiertypes.h @@ -76,7 +76,7 @@ extern ModifierTypeInfo modifierType_MeshToVolume; extern ModifierTypeInfo modifierType_VolumeDisplace; extern ModifierTypeInfo modifierType_VolumeToMesh; -/* MOD_util.c */ +/* MOD_util.cc */ /** * Only called by `BKE_modifier.h/modifier.cc` diff --git a/source/blender/modifiers/intern/MOD_multires.c b/source/blender/modifiers/intern/MOD_multires.c deleted file mode 100644 index 87108836a90..00000000000 --- a/source/blender/modifiers/intern/MOD_multires.c +++ /dev/null @@ -1,519 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later - * Copyright 2005 Blender Foundation. All rights reserved. */ - -/** \file - * \ingroup modifiers - */ - -#include - -#include "MEM_guardedalloc.h" - -#include "BLI_utildefines.h" - -#include "BLT_translation.h" - -#include "DNA_defaults.h" -#include "DNA_mesh_types.h" -#include "DNA_object_types.h" -#include "DNA_scene_types.h" -#include "DNA_screen_types.h" - -#include "BKE_cdderivedmesh.h" -#include "BKE_context.h" -#include "BKE_mesh.h" -#include "BKE_modifier.h" -#include "BKE_multires.h" -#include "BKE_paint.h" -#include "BKE_screen.h" -#include "BKE_subdiv.h" -#include "BKE_subdiv_ccg.h" -#include "BKE_subdiv_deform.h" -#include "BKE_subdiv_mesh.h" -#include "BKE_subsurf.h" - -#include "UI_interface.h" -#include "UI_resources.h" - -#include "RNA_access.h" -#include "RNA_prototypes.h" - -#include "WM_types.h" /* For subdivide operator UI. */ - -#include "DEG_depsgraph_query.h" - -#include "MOD_modifiertypes.h" -#include "MOD_ui_common.h" - -typedef struct MultiresRuntimeData { - /* Cached subdivision surface descriptor, with topology and settings. */ - struct Subdiv *subdiv; -} MultiresRuntimeData; - -static void initData(ModifierData *md) -{ - MultiresModifierData *mmd = (MultiresModifierData *)md; - - BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(mmd, modifier)); - - MEMCPY_STRUCT_AFTER(mmd, DNA_struct_default_get(MultiresModifierData), modifier); - - /* Open subdivision panels by default. */ - md->ui_expand_flag = UI_PANEL_DATA_EXPAND_ROOT | UI_SUBPANEL_DATA_EXPAND_1; -} - -static void requiredDataMask(ModifierData *md, CustomData_MeshMasks *r_cddata_masks) -{ - MultiresModifierData *mmd = (MultiresModifierData *)md; - if (mmd->flags & eMultiresModifierFlag_UseCustomNormals) { - r_cddata_masks->lmask |= CD_MASK_NORMAL; - r_cddata_masks->lmask |= CD_MASK_CUSTOMLOOPNORMAL; - } -} - -static bool dependsOnNormals(ModifierData *md) -{ - MultiresModifierData *mmd = (MultiresModifierData *)md; - if (mmd->flags & eMultiresModifierFlag_UseCustomNormals) { - return true; - } - return false; -} - -static void copyData(const ModifierData *md_src, ModifierData *md_dst, const int flag) -{ - BKE_modifier_copydata_generic(md_src, md_dst, flag); -} - -static void freeRuntimeData(void *runtime_data_v) -{ - if (runtime_data_v == NULL) { - return; - } - MultiresRuntimeData *runtime_data = (MultiresRuntimeData *)runtime_data_v; - if (runtime_data->subdiv != NULL) { - BKE_subdiv_free(runtime_data->subdiv); - } - MEM_freeN(runtime_data); -} - -static void freeData(ModifierData *md) -{ - MultiresModifierData *mmd = (MultiresModifierData *)md; - freeRuntimeData(mmd->modifier.runtime); -} - -static MultiresRuntimeData *multires_ensure_runtime(MultiresModifierData *mmd) -{ - MultiresRuntimeData *runtime_data = (MultiresRuntimeData *)mmd->modifier.runtime; - if (runtime_data == NULL) { - runtime_data = MEM_callocN(sizeof(*runtime_data), "subsurf runtime"); - mmd->modifier.runtime = runtime_data; - } - return runtime_data; -} - -/* Main goal of this function is to give usable subdivision surface descriptor - * which matches settings and topology. */ -static Subdiv *subdiv_descriptor_ensure(MultiresModifierData *mmd, - const SubdivSettings *subdiv_settings, - const Mesh *mesh) -{ - MultiresRuntimeData *runtime_data = (MultiresRuntimeData *)mmd->modifier.runtime; - Subdiv *subdiv = BKE_subdiv_update_from_mesh(runtime_data->subdiv, subdiv_settings, mesh); - runtime_data->subdiv = subdiv; - return subdiv; -} - -/* Subdivide into fully qualified mesh. */ - -static Mesh *multires_as_mesh(MultiresModifierData *mmd, - const ModifierEvalContext *ctx, - Mesh *mesh, - Subdiv *subdiv) -{ - Mesh *result = mesh; - const bool use_render_params = (ctx->flag & MOD_APPLY_RENDER); - const bool ignore_simplify = (ctx->flag & MOD_APPLY_IGNORE_SIMPLIFY); - const bool ignore_control_edges = (ctx->flag & MOD_APPLY_TO_BASE_MESH); - const Scene *scene = DEG_get_evaluated_scene(ctx->depsgraph); - Object *object = ctx->object; - SubdivToMeshSettings mesh_settings; - BKE_multires_subdiv_mesh_settings_init(&mesh_settings, - scene, - object, - mmd, - use_render_params, - ignore_simplify, - ignore_control_edges); - if (mesh_settings.resolution < 3) { - return result; - } - BKE_subdiv_displacement_attach_from_multires(subdiv, mesh, mmd); - result = BKE_subdiv_to_mesh(subdiv, &mesh_settings, mesh); - return result; -} - -/* Subdivide into CCG. */ - -static void multires_ccg_settings_init(SubdivToCCGSettings *settings, - const MultiresModifierData *mmd, - const ModifierEvalContext *ctx, - Mesh *mesh) -{ - const bool has_mask = CustomData_has_layer(&mesh->ldata, CD_GRID_PAINT_MASK); - const bool use_render_params = (ctx->flag & MOD_APPLY_RENDER); - const bool ignore_simplify = (ctx->flag & MOD_APPLY_IGNORE_SIMPLIFY); - const Scene *scene = DEG_get_evaluated_scene(ctx->depsgraph); - Object *object = ctx->object; - const int level = multires_get_level(scene, object, mmd, use_render_params, ignore_simplify); - settings->resolution = (1 << level) + 1; - settings->need_normal = true; - settings->need_mask = has_mask; -} - -static Mesh *multires_as_ccg(MultiresModifierData *mmd, - const ModifierEvalContext *ctx, - Mesh *mesh, - Subdiv *subdiv) -{ - Mesh *result = mesh; - SubdivToCCGSettings ccg_settings; - multires_ccg_settings_init(&ccg_settings, mmd, ctx, mesh); - if (ccg_settings.resolution < 3) { - return result; - } - BKE_subdiv_displacement_attach_from_multires(subdiv, mesh, mmd); - result = BKE_subdiv_to_ccg_mesh(subdiv, &ccg_settings, mesh); - - /* NOTE: CCG becomes an owner of Subdiv descriptor, so can not share - * this pointer. Not sure if it's needed, but might have a second look - * on the ownership model here. */ - MultiresRuntimeData *runtime_data = mmd->modifier.runtime; - runtime_data->subdiv = NULL; - - return result; -} - -static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mesh) -{ - Mesh *result = mesh; -#if !defined(WITH_OPENSUBDIV) - BKE_modifier_set_error(ctx->object, md, "Disabled, built without OpenSubdiv"); - return result; -#endif - MultiresModifierData *mmd = (MultiresModifierData *)md; - SubdivSettings subdiv_settings; - BKE_multires_subdiv_settings_init(&subdiv_settings, mmd); - if (subdiv_settings.level == 0) { - return result; - } - MultiresRuntimeData *runtime_data = multires_ensure_runtime(mmd); - Subdiv *subdiv = subdiv_descriptor_ensure(mmd, &subdiv_settings, mesh); - if (subdiv == NULL) { - /* Happens on bad topology, ut also on empty input mesh. */ - return result; - } - const bool use_clnors = mmd->flags & eMultiresModifierFlag_UseCustomNormals && - mesh->flag & ME_AUTOSMOOTH && - CustomData_has_layer(&mesh->ldata, CD_CUSTOMLOOPNORMAL); - /* NOTE: Orco needs final coordinates on CPU side, which are expected to be - * accessible via MVert. For this reason we do not evaluate multires to - * grids when orco is requested. */ - const bool for_orco = (ctx->flag & MOD_APPLY_ORCO) != 0; - /* Needed when rendering or baking will in sculpt mode. */ - const bool for_render = (ctx->flag & MOD_APPLY_RENDER) != 0; - - const bool sculpt_base_mesh = mmd->flags & eMultiresModifierFlag_UseSculptBaseMesh; - - if ((ctx->object->mode & OB_MODE_SCULPT) && !for_orco && !for_render && !sculpt_base_mesh) { - /* NOTE: CCG takes ownership over Subdiv. */ - result = multires_as_ccg(mmd, ctx, mesh, subdiv); - result->runtime.subdiv_ccg_tot_level = mmd->totlvl; - /* TODO(sergey): Usually it is sculpt stroke's update variants which - * takes care of this, but is possible that we need this before the - * stroke: i.e. when exiting blender right after stroke is done. - * Annoying and not so much black-boxed as far as sculpting goes, and - * surely there is a better way of solving this. */ - if (ctx->object->sculpt != NULL) { - SculptSession *sculpt_session = ctx->object->sculpt; - sculpt_session->subdiv_ccg = result->runtime.subdiv_ccg; - sculpt_session->multires.active = true; - sculpt_session->multires.modifier = mmd; - sculpt_session->multires.level = mmd->sculptlvl; - sculpt_session->totvert = mesh->totvert; - sculpt_session->totpoly = mesh->totpoly; - sculpt_session->mvert = NULL; - sculpt_session->mpoly = NULL; - sculpt_session->mloop = NULL; - } - // BKE_subdiv_stats_print(&subdiv->stats); - } - else { - if (use_clnors) { - /* If custom normals are present and the option is turned on calculate the split - * normals and clear flag so the normals get interpolated to the result mesh. */ - BKE_mesh_calc_normals_split(mesh); - CustomData_clear_layer_flag(&mesh->ldata, CD_NORMAL, CD_FLAG_TEMPORARY); - } - - result = multires_as_mesh(mmd, ctx, mesh, subdiv); - - if (use_clnors) { - float(*lnors)[3] = CustomData_get_layer(&result->ldata, CD_NORMAL); - BLI_assert(lnors != NULL); - BKE_mesh_set_custom_normals(result, lnors); - CustomData_set_layer_flag(&mesh->ldata, CD_NORMAL, CD_FLAG_TEMPORARY); - CustomData_set_layer_flag(&result->ldata, CD_NORMAL, CD_FLAG_TEMPORARY); - } - // BKE_subdiv_stats_print(&subdiv->stats); - if (subdiv != runtime_data->subdiv) { - BKE_subdiv_free(subdiv); - } - } - return result; -} - -static void deformMatrices(ModifierData *md, - const ModifierEvalContext *ctx, - Mesh *mesh, - float (*vertex_cos)[3], - float (*deform_matrices)[3][3], - int verts_num) - -{ -#if !defined(WITH_OPENSUBDIV) - BKE_modifier_set_error(ctx->object, md, "Disabled, built without OpenSubdiv"); - return; -#endif - - /* Subsurf does not require extra space mapping, keep matrices as is. */ - (void)deform_matrices; - - MultiresModifierData *mmd = (MultiresModifierData *)md; - - SubdivSettings subdiv_settings; - BKE_multires_subdiv_settings_init(&subdiv_settings, mmd); - if (subdiv_settings.level == 0) { - return; - } - - SubdivToCCGSettings ccg_settings; - multires_ccg_settings_init(&ccg_settings, mmd, ctx, mesh); - if (ccg_settings.resolution < 3) { - return; - } - - MultiresRuntimeData *runtime_data = multires_ensure_runtime(mmd); - Subdiv *subdiv = subdiv_descriptor_ensure(mmd, &subdiv_settings, mesh); - if (subdiv == NULL) { - /* Happens on bad topology, ut also on empty input mesh. */ - return; - } - BKE_subdiv_displacement_attach_from_multires(subdiv, mesh, mmd); - BKE_subdiv_deform_coarse_vertices(subdiv, mesh, vertex_cos, verts_num); - if (subdiv != runtime_data->subdiv) { - BKE_subdiv_free(subdiv); - } -} - -static void panel_draw(const bContext *C, Panel *panel) -{ - uiLayout *col; - uiLayout *layout = panel->layout; - - PointerRNA *ptr = modifier_panel_get_property_pointers(panel, NULL); - - uiLayoutSetPropSep(layout, true); - - col = uiLayoutColumn(layout, true); - uiItemR(col, ptr, "levels", 0, IFACE_("Level Viewport"), ICON_NONE); - uiItemR(col, ptr, "sculpt_levels", 0, IFACE_("Sculpt"), ICON_NONE); - uiItemR(col, ptr, "render_levels", 0, IFACE_("Render"), ICON_NONE); - - const bool is_sculpt_mode = CTX_data_active_object(C)->mode & OB_MODE_SCULPT; - uiBlock *block = uiLayoutGetBlock(panel->layout); - UI_block_lock_set(block, !is_sculpt_mode, IFACE_("Sculpt Base Mesh")); - uiItemR(col, ptr, "use_sculpt_base_mesh", 0, IFACE_("Sculpt Base Mesh"), ICON_NONE); - UI_block_lock_clear(block); - - uiItemR(layout, ptr, "show_only_control_edges", 0, NULL, ICON_NONE); - - modifier_panel_end(layout, ptr); -} - -static void subdivisions_panel_draw(const bContext *UNUSED(C), Panel *panel) -{ - uiLayout *row; - uiLayout *layout = panel->layout; - - PointerRNA ob_ptr; - PointerRNA *ptr = modifier_panel_get_property_pointers(panel, &ob_ptr); - - uiLayoutSetEnabled(layout, RNA_enum_get(&ob_ptr, "mode") != OB_MODE_EDIT); - - MultiresModifierData *mmd = (MultiresModifierData *)ptr->data; - - /** - * Changing some of the properties can not be done once there is an - * actual displacement stored for this multi-resolution modifier. - * This check will disallow changes for those properties. - * This check is a bit stupid but it should be sufficient for the usual - * multi-resolution usage. It might become less strict and only disallow - * modifications if there is CD_MDISPS layer, or if there is actual - * non-zero displacement, but such checks will be too slow to be done - * on every redraw. - */ - - PointerRNA op_ptr; - uiItemFullO(layout, - "OBJECT_OT_multires_subdivide", - IFACE_("Subdivide"), - ICON_NONE, - NULL, - WM_OP_EXEC_DEFAULT, - 0, - &op_ptr); - RNA_enum_set(&op_ptr, "mode", MULTIRES_SUBDIVIDE_CATMULL_CLARK); - RNA_string_set(&op_ptr, "modifier", ((ModifierData *)mmd)->name); - - row = uiLayoutRow(layout, false); - uiItemFullO(row, - "OBJECT_OT_multires_subdivide", - IFACE_("Simple"), - ICON_NONE, - NULL, - WM_OP_EXEC_DEFAULT, - 0, - &op_ptr); - RNA_enum_set(&op_ptr, "mode", MULTIRES_SUBDIVIDE_SIMPLE); - RNA_string_set(&op_ptr, "modifier", ((ModifierData *)mmd)->name); - uiItemFullO(row, - "OBJECT_OT_multires_subdivide", - IFACE_("Linear"), - ICON_NONE, - NULL, - WM_OP_EXEC_DEFAULT, - 0, - &op_ptr); - RNA_enum_set(&op_ptr, "mode", MULTIRES_SUBDIVIDE_LINEAR); - RNA_string_set(&op_ptr, "modifier", ((ModifierData *)mmd)->name); - - uiItemS(layout); - - uiItemO(layout, IFACE_("Unsubdivide"), ICON_NONE, "OBJECT_OT_multires_unsubdivide"); - uiItemO(layout, IFACE_("Delete Higher"), ICON_NONE, "OBJECT_OT_multires_higher_levels_delete"); -} - -static void shape_panel_draw(const bContext *UNUSED(C), Panel *panel) -{ - uiLayout *row; - uiLayout *layout = panel->layout; - - PointerRNA ob_ptr; - modifier_panel_get_property_pointers(panel, &ob_ptr); - - uiLayoutSetEnabled(layout, RNA_enum_get(&ob_ptr, "mode") != OB_MODE_EDIT); - - row = uiLayoutRow(layout, false); - uiItemO(row, IFACE_("Reshape"), ICON_NONE, "OBJECT_OT_multires_reshape"); - uiItemO(row, IFACE_("Apply Base"), ICON_NONE, "OBJECT_OT_multires_base_apply"); -} - -static void generate_panel_draw(const bContext *UNUSED(C), Panel *panel) -{ - uiLayout *col, *row; - uiLayout *layout = panel->layout; - - PointerRNA *ptr = modifier_panel_get_property_pointers(panel, NULL); - MultiresModifierData *mmd = (MultiresModifierData *)ptr->data; - - bool is_external = RNA_boolean_get(ptr, "is_external"); - - if (mmd->totlvl == 0) { - uiItemO( - layout, IFACE_("Rebuild Subdivisions"), ICON_NONE, "OBJECT_OT_multires_rebuild_subdiv"); - } - - col = uiLayoutColumn(layout, false); - row = uiLayoutRow(col, false); - if (is_external) { - uiItemO(row, IFACE_("Pack External"), ICON_NONE, "OBJECT_OT_multires_external_pack"); - uiLayoutSetPropSep(col, true); - row = uiLayoutRow(col, false); - uiItemR(row, ptr, "filepath", 0, NULL, ICON_NONE); - } - else { - uiItemO(col, IFACE_("Save External..."), ICON_NONE, "OBJECT_OT_multires_external_save"); - } -} - -static void advanced_panel_draw(const bContext *UNUSED(C), Panel *panel) -{ - uiLayout *col; - uiLayout *layout = panel->layout; - - PointerRNA *ptr = modifier_panel_get_property_pointers(panel, NULL); - - bool has_displacement = RNA_int_get(ptr, "total_levels") != 0; - - uiLayoutSetPropSep(layout, true); - - uiLayoutSetActive(layout, !has_displacement); - - uiItemR(layout, ptr, "quality", 0, NULL, ICON_NONE); - - col = uiLayoutColumn(layout, false); - uiLayoutSetActive(col, true); - uiItemR(col, ptr, "uv_smooth", 0, NULL, ICON_NONE); - uiItemR(col, ptr, "boundary_smooth", 0, NULL, ICON_NONE); - - uiItemR(layout, ptr, "use_creases", 0, NULL, ICON_NONE); - uiItemR(layout, ptr, "use_custom_normals", 0, NULL, ICON_NONE); -} - -static void panelRegister(ARegionType *region_type) -{ - PanelType *panel_type = modifier_panel_register(region_type, eModifierType_Multires, panel_draw); - modifier_subpanel_register( - region_type, "subdivide", "Subdivision", NULL, subdivisions_panel_draw, panel_type); - modifier_subpanel_register(region_type, "shape", "Shape", NULL, shape_panel_draw, panel_type); - modifier_subpanel_register( - region_type, "generate", "Generate", NULL, generate_panel_draw, panel_type); - modifier_subpanel_register( - region_type, "advanced", "Advanced", NULL, advanced_panel_draw, panel_type); -} - -ModifierTypeInfo modifierType_Multires = { - /* name */ N_("Multires"), - /* structName */ "MultiresModifierData", - /* structSize */ sizeof(MultiresModifierData), - /* srna */ &RNA_MultiresModifier, - /* type */ eModifierTypeType_Constructive, - /* flags */ eModifierTypeFlag_AcceptsMesh | eModifierTypeFlag_SupportsMapping | - eModifierTypeFlag_RequiresOriginalData, - /* icon */ ICON_MOD_MULTIRES, - - /* copyData */ copyData, - - /* deformVerts */ NULL, - /* deformMatrices */ deformMatrices, - /* deformVertsEM */ NULL, - /* deformMatricesEM */ NULL, - /* modifyMesh */ modifyMesh, - /* modifyGeometrySet */ NULL, - - /* initData */ initData, - /* requiredDataMask */ requiredDataMask, - /* freeData */ freeData, - /* isDisabled */ NULL, - /* updateDepsgraph */ NULL, - /* dependsOnTime */ NULL, - /* dependsOnNormals */ dependsOnNormals, - /* foreachIDLink */ NULL, - /* foreachTexLink */ NULL, - /* freeRuntimeData */ freeRuntimeData, - /* panelRegister */ panelRegister, - /* blendWrite */ NULL, - /* blendRead */ NULL, -}; diff --git a/source/blender/modifiers/intern/MOD_multires.cc b/source/blender/modifiers/intern/MOD_multires.cc new file mode 100644 index 00000000000..6342ff384a4 --- /dev/null +++ b/source/blender/modifiers/intern/MOD_multires.cc @@ -0,0 +1,520 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2005 Blender Foundation. All rights reserved. */ + +/** \file + * \ingroup modifiers + */ + +#include + +#include "MEM_guardedalloc.h" + +#include "BLI_utildefines.h" + +#include "BLT_translation.h" + +#include "DNA_defaults.h" +#include "DNA_mesh_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" +#include "DNA_screen_types.h" + +#include "BKE_cdderivedmesh.h" +#include "BKE_context.h" +#include "BKE_mesh.h" +#include "BKE_modifier.h" +#include "BKE_multires.h" +#include "BKE_paint.h" +#include "BKE_screen.h" +#include "BKE_subdiv.h" +#include "BKE_subdiv_ccg.h" +#include "BKE_subdiv_deform.h" +#include "BKE_subdiv_mesh.h" +#include "BKE_subsurf.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "RNA_access.h" +#include "RNA_prototypes.h" + +#include "WM_types.h" /* For subdivide operator UI. */ + +#include "DEG_depsgraph_query.h" + +#include "MOD_modifiertypes.h" +#include "MOD_ui_common.h" + +struct MultiresRuntimeData { + /* Cached subdivision surface descriptor, with topology and settings. */ + struct Subdiv *subdiv; +}; + +static void initData(ModifierData *md) +{ + MultiresModifierData *mmd = (MultiresModifierData *)md; + + BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(mmd, modifier)); + + MEMCPY_STRUCT_AFTER(mmd, DNA_struct_default_get(MultiresModifierData), modifier); + + /* Open subdivision panels by default. */ + md->ui_expand_flag = UI_PANEL_DATA_EXPAND_ROOT | UI_SUBPANEL_DATA_EXPAND_1; +} + +static void requiredDataMask(ModifierData *md, CustomData_MeshMasks *r_cddata_masks) +{ + MultiresModifierData *mmd = (MultiresModifierData *)md; + if (mmd->flags & eMultiresModifierFlag_UseCustomNormals) { + r_cddata_masks->lmask |= CD_MASK_NORMAL; + r_cddata_masks->lmask |= CD_MASK_CUSTOMLOOPNORMAL; + } +} + +static bool dependsOnNormals(ModifierData *md) +{ + MultiresModifierData *mmd = (MultiresModifierData *)md; + if (mmd->flags & eMultiresModifierFlag_UseCustomNormals) { + return true; + } + return false; +} + +static void copyData(const ModifierData *md_src, ModifierData *md_dst, const int flag) +{ + BKE_modifier_copydata_generic(md_src, md_dst, flag); +} + +static void freeRuntimeData(void *runtime_data_v) +{ + if (runtime_data_v == nullptr) { + return; + } + MultiresRuntimeData *runtime_data = (MultiresRuntimeData *)runtime_data_v; + if (runtime_data->subdiv != nullptr) { + BKE_subdiv_free(runtime_data->subdiv); + } + MEM_freeN(runtime_data); +} + +static void freeData(ModifierData *md) +{ + MultiresModifierData *mmd = (MultiresModifierData *)md; + freeRuntimeData(mmd->modifier.runtime); +} + +static MultiresRuntimeData *multires_ensure_runtime(MultiresModifierData *mmd) +{ + MultiresRuntimeData *runtime_data = (MultiresRuntimeData *)mmd->modifier.runtime; + if (runtime_data == nullptr) { + runtime_data = static_cast( + MEM_callocN(sizeof(*runtime_data), __func__)); + mmd->modifier.runtime = runtime_data; + } + return runtime_data; +} + +/* Main goal of this function is to give usable subdivision surface descriptor + * which matches settings and topology. */ +static Subdiv *subdiv_descriptor_ensure(MultiresModifierData *mmd, + const SubdivSettings *subdiv_settings, + const Mesh *mesh) +{ + MultiresRuntimeData *runtime_data = (MultiresRuntimeData *)mmd->modifier.runtime; + Subdiv *subdiv = BKE_subdiv_update_from_mesh(runtime_data->subdiv, subdiv_settings, mesh); + runtime_data->subdiv = subdiv; + return subdiv; +} + +/* Subdivide into fully qualified mesh. */ + +static Mesh *multires_as_mesh(MultiresModifierData *mmd, + const ModifierEvalContext *ctx, + Mesh *mesh, + Subdiv *subdiv) +{ + Mesh *result = mesh; + const bool use_render_params = (ctx->flag & MOD_APPLY_RENDER); + const bool ignore_simplify = (ctx->flag & MOD_APPLY_IGNORE_SIMPLIFY); + const bool ignore_control_edges = (ctx->flag & MOD_APPLY_TO_BASE_MESH); + const Scene *scene = DEG_get_evaluated_scene(ctx->depsgraph); + Object *object = ctx->object; + SubdivToMeshSettings mesh_settings; + BKE_multires_subdiv_mesh_settings_init(&mesh_settings, + scene, + object, + mmd, + use_render_params, + ignore_simplify, + ignore_control_edges); + if (mesh_settings.resolution < 3) { + return result; + } + BKE_subdiv_displacement_attach_from_multires(subdiv, mesh, mmd); + result = BKE_subdiv_to_mesh(subdiv, &mesh_settings, mesh); + return result; +} + +/* Subdivide into CCG. */ + +static void multires_ccg_settings_init(SubdivToCCGSettings *settings, + const MultiresModifierData *mmd, + const ModifierEvalContext *ctx, + Mesh *mesh) +{ + const bool has_mask = CustomData_has_layer(&mesh->ldata, CD_GRID_PAINT_MASK); + const bool use_render_params = (ctx->flag & MOD_APPLY_RENDER); + const bool ignore_simplify = (ctx->flag & MOD_APPLY_IGNORE_SIMPLIFY); + const Scene *scene = DEG_get_evaluated_scene(ctx->depsgraph); + Object *object = ctx->object; + const int level = multires_get_level(scene, object, mmd, use_render_params, ignore_simplify); + settings->resolution = (1 << level) + 1; + settings->need_normal = true; + settings->need_mask = has_mask; +} + +static Mesh *multires_as_ccg(MultiresModifierData *mmd, + const ModifierEvalContext *ctx, + Mesh *mesh, + Subdiv *subdiv) +{ + Mesh *result = mesh; + SubdivToCCGSettings ccg_settings; + multires_ccg_settings_init(&ccg_settings, mmd, ctx, mesh); + if (ccg_settings.resolution < 3) { + return result; + } + BKE_subdiv_displacement_attach_from_multires(subdiv, mesh, mmd); + result = BKE_subdiv_to_ccg_mesh(subdiv, &ccg_settings, mesh); + + /* NOTE: CCG becomes an owner of Subdiv descriptor, so can not share + * this pointer. Not sure if it's needed, but might have a second look + * on the ownership model here. */ + MultiresRuntimeData *runtime_data = static_cast(mmd->modifier.runtime); + runtime_data->subdiv = nullptr; + + return result; +} + +static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mesh) +{ + Mesh *result = mesh; +#if !defined(WITH_OPENSUBDIV) + BKE_modifier_set_error(ctx->object, md, "Disabled, built without OpenSubdiv"); + return result; +#endif + MultiresModifierData *mmd = (MultiresModifierData *)md; + SubdivSettings subdiv_settings; + BKE_multires_subdiv_settings_init(&subdiv_settings, mmd); + if (subdiv_settings.level == 0) { + return result; + } + MultiresRuntimeData *runtime_data = multires_ensure_runtime(mmd); + Subdiv *subdiv = subdiv_descriptor_ensure(mmd, &subdiv_settings, mesh); + if (subdiv == nullptr) { + /* Happens on bad topology, ut also on empty input mesh. */ + return result; + } + const bool use_clnors = mmd->flags & eMultiresModifierFlag_UseCustomNormals && + mesh->flag & ME_AUTOSMOOTH && + CustomData_has_layer(&mesh->ldata, CD_CUSTOMLOOPNORMAL); + /* NOTE: Orco needs final coordinates on CPU side, which are expected to be + * accessible via MVert. For this reason we do not evaluate multires to + * grids when orco is requested. */ + const bool for_orco = (ctx->flag & MOD_APPLY_ORCO) != 0; + /* Needed when rendering or baking will in sculpt mode. */ + const bool for_render = (ctx->flag & MOD_APPLY_RENDER) != 0; + + const bool sculpt_base_mesh = mmd->flags & eMultiresModifierFlag_UseSculptBaseMesh; + + if ((ctx->object->mode & OB_MODE_SCULPT) && !for_orco && !for_render && !sculpt_base_mesh) { + /* NOTE: CCG takes ownership over Subdiv. */ + result = multires_as_ccg(mmd, ctx, mesh, subdiv); + result->runtime.subdiv_ccg_tot_level = mmd->totlvl; + /* TODO(sergey): Usually it is sculpt stroke's update variants which + * takes care of this, but is possible that we need this before the + * stroke: i.e. when exiting blender right after stroke is done. + * Annoying and not so much black-boxed as far as sculpting goes, and + * surely there is a better way of solving this. */ + if (ctx->object->sculpt != nullptr) { + SculptSession *sculpt_session = ctx->object->sculpt; + sculpt_session->subdiv_ccg = result->runtime.subdiv_ccg; + sculpt_session->multires.active = true; + sculpt_session->multires.modifier = mmd; + sculpt_session->multires.level = mmd->sculptlvl; + sculpt_session->totvert = mesh->totvert; + sculpt_session->totpoly = mesh->totpoly; + sculpt_session->mvert = nullptr; + sculpt_session->mpoly = nullptr; + sculpt_session->mloop = nullptr; + } + // BKE_subdiv_stats_print(&subdiv->stats); + } + else { + if (use_clnors) { + /* If custom normals are present and the option is turned on calculate the split + * normals and clear flag so the normals get interpolated to the result mesh. */ + BKE_mesh_calc_normals_split(mesh); + CustomData_clear_layer_flag(&mesh->ldata, CD_NORMAL, CD_FLAG_TEMPORARY); + } + + result = multires_as_mesh(mmd, ctx, mesh, subdiv); + + if (use_clnors) { + float(*lnors)[3] = static_cast(CustomData_get_layer(&result->ldata, CD_NORMAL)); + BLI_assert(lnors != nullptr); + BKE_mesh_set_custom_normals(result, lnors); + CustomData_set_layer_flag(&mesh->ldata, CD_NORMAL, CD_FLAG_TEMPORARY); + CustomData_set_layer_flag(&result->ldata, CD_NORMAL, CD_FLAG_TEMPORARY); + } + // BKE_subdiv_stats_print(&subdiv->stats); + if (subdiv != runtime_data->subdiv) { + BKE_subdiv_free(subdiv); + } + } + return result; +} + +static void deformMatrices(ModifierData *md, + const ModifierEvalContext *ctx, + Mesh *mesh, + float (*vertex_cos)[3], + float (*deform_matrices)[3][3], + int verts_num) + +{ +#if !defined(WITH_OPENSUBDIV) + BKE_modifier_set_error(ctx->object, md, "Disabled, built without OpenSubdiv"); + return; +#endif + + /* Subsurf does not require extra space mapping, keep matrices as is. */ + (void)deform_matrices; + + MultiresModifierData *mmd = (MultiresModifierData *)md; + + SubdivSettings subdiv_settings; + BKE_multires_subdiv_settings_init(&subdiv_settings, mmd); + if (subdiv_settings.level == 0) { + return; + } + + SubdivToCCGSettings ccg_settings; + multires_ccg_settings_init(&ccg_settings, mmd, ctx, mesh); + if (ccg_settings.resolution < 3) { + return; + } + + MultiresRuntimeData *runtime_data = multires_ensure_runtime(mmd); + Subdiv *subdiv = subdiv_descriptor_ensure(mmd, &subdiv_settings, mesh); + if (subdiv == nullptr) { + /* Happens on bad topology, ut also on empty input mesh. */ + return; + } + BKE_subdiv_displacement_attach_from_multires(subdiv, mesh, mmd); + BKE_subdiv_deform_coarse_vertices(subdiv, mesh, vertex_cos, verts_num); + if (subdiv != runtime_data->subdiv) { + BKE_subdiv_free(subdiv); + } +} + +static void panel_draw(const bContext *C, Panel *panel) +{ + uiLayout *col; + uiLayout *layout = panel->layout; + + PointerRNA *ptr = modifier_panel_get_property_pointers(panel, nullptr); + + uiLayoutSetPropSep(layout, true); + + col = uiLayoutColumn(layout, true); + uiItemR(col, ptr, "levels", 0, IFACE_("Level Viewport"), ICON_NONE); + uiItemR(col, ptr, "sculpt_levels", 0, IFACE_("Sculpt"), ICON_NONE); + uiItemR(col, ptr, "render_levels", 0, IFACE_("Render"), ICON_NONE); + + const bool is_sculpt_mode = CTX_data_active_object(C)->mode & OB_MODE_SCULPT; + uiBlock *block = uiLayoutGetBlock(panel->layout); + UI_block_lock_set(block, !is_sculpt_mode, IFACE_("Sculpt Base Mesh")); + uiItemR(col, ptr, "use_sculpt_base_mesh", 0, IFACE_("Sculpt Base Mesh"), ICON_NONE); + UI_block_lock_clear(block); + + uiItemR(layout, ptr, "show_only_control_edges", 0, nullptr, ICON_NONE); + + modifier_panel_end(layout, ptr); +} + +static void subdivisions_panel_draw(const bContext *UNUSED(C), Panel *panel) +{ + uiLayout *row; + uiLayout *layout = panel->layout; + + PointerRNA ob_ptr; + PointerRNA *ptr = modifier_panel_get_property_pointers(panel, &ob_ptr); + + uiLayoutSetEnabled(layout, RNA_enum_get(&ob_ptr, "mode") != OB_MODE_EDIT); + + MultiresModifierData *mmd = (MultiresModifierData *)ptr->data; + + /** + * Changing some of the properties can not be done once there is an + * actual displacement stored for this multi-resolution modifier. + * This check will disallow changes for those properties. + * This check is a bit stupid but it should be sufficient for the usual + * multi-resolution usage. It might become less strict and only disallow + * modifications if there is CD_MDISPS layer, or if there is actual + * non-zero displacement, but such checks will be too slow to be done + * on every redraw. + */ + + PointerRNA op_ptr; + uiItemFullO(layout, + "OBJECT_OT_multires_subdivide", + IFACE_("Subdivide"), + ICON_NONE, + nullptr, + WM_OP_EXEC_DEFAULT, + 0, + &op_ptr); + RNA_enum_set(&op_ptr, "mode", MULTIRES_SUBDIVIDE_CATMULL_CLARK); + RNA_string_set(&op_ptr, "modifier", ((ModifierData *)mmd)->name); + + row = uiLayoutRow(layout, false); + uiItemFullO(row, + "OBJECT_OT_multires_subdivide", + IFACE_("Simple"), + ICON_NONE, + nullptr, + WM_OP_EXEC_DEFAULT, + 0, + &op_ptr); + RNA_enum_set(&op_ptr, "mode", MULTIRES_SUBDIVIDE_SIMPLE); + RNA_string_set(&op_ptr, "modifier", ((ModifierData *)mmd)->name); + uiItemFullO(row, + "OBJECT_OT_multires_subdivide", + IFACE_("Linear"), + ICON_NONE, + nullptr, + WM_OP_EXEC_DEFAULT, + 0, + &op_ptr); + RNA_enum_set(&op_ptr, "mode", MULTIRES_SUBDIVIDE_LINEAR); + RNA_string_set(&op_ptr, "modifier", ((ModifierData *)mmd)->name); + + uiItemS(layout); + + uiItemO(layout, IFACE_("Unsubdivide"), ICON_NONE, "OBJECT_OT_multires_unsubdivide"); + uiItemO(layout, IFACE_("Delete Higher"), ICON_NONE, "OBJECT_OT_multires_higher_levels_delete"); +} + +static void shape_panel_draw(const bContext *UNUSED(C), Panel *panel) +{ + uiLayout *row; + uiLayout *layout = panel->layout; + + PointerRNA ob_ptr; + modifier_panel_get_property_pointers(panel, &ob_ptr); + + uiLayoutSetEnabled(layout, RNA_enum_get(&ob_ptr, "mode") != OB_MODE_EDIT); + + row = uiLayoutRow(layout, false); + uiItemO(row, IFACE_("Reshape"), ICON_NONE, "OBJECT_OT_multires_reshape"); + uiItemO(row, IFACE_("Apply Base"), ICON_NONE, "OBJECT_OT_multires_base_apply"); +} + +static void generate_panel_draw(const bContext *UNUSED(C), Panel *panel) +{ + uiLayout *col, *row; + uiLayout *layout = panel->layout; + + PointerRNA *ptr = modifier_panel_get_property_pointers(panel, nullptr); + MultiresModifierData *mmd = (MultiresModifierData *)ptr->data; + + bool is_external = RNA_boolean_get(ptr, "is_external"); + + if (mmd->totlvl == 0) { + uiItemO( + layout, IFACE_("Rebuild Subdivisions"), ICON_NONE, "OBJECT_OT_multires_rebuild_subdiv"); + } + + col = uiLayoutColumn(layout, false); + row = uiLayoutRow(col, false); + if (is_external) { + uiItemO(row, IFACE_("Pack External"), ICON_NONE, "OBJECT_OT_multires_external_pack"); + uiLayoutSetPropSep(col, true); + row = uiLayoutRow(col, false); + uiItemR(row, ptr, "filepath", 0, nullptr, ICON_NONE); + } + else { + uiItemO(col, IFACE_("Save External..."), ICON_NONE, "OBJECT_OT_multires_external_save"); + } +} + +static void advanced_panel_draw(const bContext *UNUSED(C), Panel *panel) +{ + uiLayout *col; + uiLayout *layout = panel->layout; + + PointerRNA *ptr = modifier_panel_get_property_pointers(panel, nullptr); + + bool has_displacement = RNA_int_get(ptr, "total_levels") != 0; + + uiLayoutSetPropSep(layout, true); + + uiLayoutSetActive(layout, !has_displacement); + + uiItemR(layout, ptr, "quality", 0, nullptr, ICON_NONE); + + col = uiLayoutColumn(layout, false); + uiLayoutSetActive(col, true); + uiItemR(col, ptr, "uv_smooth", 0, nullptr, ICON_NONE); + uiItemR(col, ptr, "boundary_smooth", 0, nullptr, ICON_NONE); + + uiItemR(layout, ptr, "use_creases", 0, nullptr, ICON_NONE); + uiItemR(layout, ptr, "use_custom_normals", 0, nullptr, ICON_NONE); +} + +static void panelRegister(ARegionType *region_type) +{ + PanelType *panel_type = modifier_panel_register(region_type, eModifierType_Multires, panel_draw); + modifier_subpanel_register( + region_type, "subdivide", "Subdivision", nullptr, subdivisions_panel_draw, panel_type); + modifier_subpanel_register(region_type, "shape", "Shape", nullptr, shape_panel_draw, panel_type); + modifier_subpanel_register( + region_type, "generate", "Generate", nullptr, generate_panel_draw, panel_type); + modifier_subpanel_register( + region_type, "advanced", "Advanced", nullptr, advanced_panel_draw, panel_type); +} + +ModifierTypeInfo modifierType_Multires = { + /* name */ N_("Multires"), + /* structName */ "MultiresModifierData", + /* structSize */ sizeof(MultiresModifierData), + /* srna */ &RNA_MultiresModifier, + /* type */ eModifierTypeType_Constructive, + /* flags */ eModifierTypeFlag_AcceptsMesh | eModifierTypeFlag_SupportsMapping | + eModifierTypeFlag_RequiresOriginalData, + /* icon */ ICON_MOD_MULTIRES, + + /* copyData */ copyData, + + /* deformVerts */ nullptr, + /* deformMatrices */ deformMatrices, + /* deformVertsEM */ nullptr, + /* deformMatricesEM */ nullptr, + /* modifyMesh */ modifyMesh, + /* modifyGeometrySet */ nullptr, + + /* initData */ initData, + /* requiredDataMask */ requiredDataMask, + /* freeData */ freeData, + /* isDisabled */ nullptr, + /* updateDepsgraph */ nullptr, + /* dependsOnTime */ nullptr, + /* dependsOnNormals */ dependsOnNormals, + /* foreachIDLink */ nullptr, + /* foreachTexLink */ nullptr, + /* freeRuntimeData */ freeRuntimeData, + /* panelRegister */ panelRegister, + /* blendWrite */ nullptr, + /* blendRead */ nullptr, +}; diff --git a/source/blender/modifiers/intern/MOD_util.c b/source/blender/modifiers/intern/MOD_util.c deleted file mode 100644 index bc1a04d65ce..00000000000 --- a/source/blender/modifiers/intern/MOD_util.c +++ /dev/null @@ -1,328 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later - * Copyright 2005 Blender Foundation. All rights reserved. */ - -/** \file - * \ingroup modifiers - */ - -#include - -#include "BLI_utildefines.h" - -#include "BLI_bitmap.h" -#include "BLI_math_matrix.h" -#include "BLI_math_vector.h" - -#include "DNA_image_types.h" -#include "DNA_mesh_types.h" -#include "DNA_meshdata_types.h" -#include "DNA_modifier_types.h" -#include "DNA_object_types.h" -#include "DNA_scene_types.h" - -#include "BKE_action.h" /* BKE_pose_channel_find_name */ -#include "BKE_deform.h" -#include "BKE_editmesh.h" -#include "BKE_image.h" -#include "BKE_lattice.h" -#include "BKE_lib_id.h" -#include "BKE_mesh.h" -#include "BKE_mesh_wrapper.h" -#include "BKE_object.h" - -#include "BKE_modifier.h" - -#include "DEG_depsgraph.h" -#include "DEG_depsgraph_query.h" - -#include "MOD_modifiertypes.h" -#include "MOD_util.h" - -#include "MEM_guardedalloc.h" - -#include "bmesh.h" - -void MOD_init_texture(MappingInfoModifierData *dmd, const ModifierEvalContext *ctx) -{ - Tex *tex = dmd->texture; - - if (tex == NULL) { - return; - } - - if (tex->ima && BKE_image_is_animated(tex->ima)) { - BKE_image_user_frame_calc(tex->ima, &tex->iuser, DEG_get_ctime(ctx->depsgraph)); - } -} - -/* TODO: to be renamed to get_texture_coords once we are done with moving modifiers to Mesh. */ -void MOD_get_texture_coords(MappingInfoModifierData *dmd, - const ModifierEvalContext *UNUSED(ctx), - Object *ob, - Mesh *mesh, - float (*cos)[3], - float (*r_texco)[3]) -{ - const int verts_num = mesh->totvert; - int i; - int texmapping = dmd->texmapping; - float mapref_imat[4][4]; - - if (texmapping == MOD_DISP_MAP_OBJECT) { - if (dmd->map_object != NULL) { - Object *map_object = dmd->map_object; - if (dmd->map_bone[0] != '\0') { - bPoseChannel *pchan = BKE_pose_channel_find_name(map_object->pose, dmd->map_bone); - if (pchan) { - float mat_bone_world[4][4]; - mul_m4_m4m4(mat_bone_world, map_object->obmat, pchan->pose_mat); - invert_m4_m4(mapref_imat, mat_bone_world); - } - else { - invert_m4_m4(mapref_imat, map_object->obmat); - } - } - else { - invert_m4_m4(mapref_imat, map_object->obmat); - } - } - else { /* if there is no map object, default to local */ - texmapping = MOD_DISP_MAP_LOCAL; - } - } - - /* UVs need special handling, since they come from faces */ - if (texmapping == MOD_DISP_MAP_UV) { - if (CustomData_has_layer(&mesh->ldata, CD_MLOOPUV)) { - const MPoly *mpoly = BKE_mesh_polys(mesh); - const MPoly *mp; - const MLoop *mloop = BKE_mesh_loops(mesh); - BLI_bitmap *done = BLI_BITMAP_NEW(verts_num, __func__); - const int polys_num = mesh->totpoly; - char uvname[MAX_CUSTOMDATA_LAYER_NAME]; - - CustomData_validate_layer_name(&mesh->ldata, CD_MLOOPUV, dmd->uvlayer_name, uvname); - const MLoopUV *mloop_uv = CustomData_get_layer_named(&mesh->ldata, CD_MLOOPUV, uvname); - - /* verts are given the UV from the first face that uses them */ - for (i = 0, mp = mpoly; i < polys_num; i++, mp++) { - uint fidx = mp->totloop - 1; - - do { - uint lidx = mp->loopstart + fidx; - uint vidx = mloop[lidx].v; - - if (!BLI_BITMAP_TEST(done, vidx)) { - /* remap UVs from [0, 1] to [-1, 1] */ - r_texco[vidx][0] = (mloop_uv[lidx].uv[0] * 2.0f) - 1.0f; - r_texco[vidx][1] = (mloop_uv[lidx].uv[1] * 2.0f) - 1.0f; - BLI_BITMAP_ENABLE(done, vidx); - } - - } while (fidx--); - } - - MEM_freeN(done); - return; - } - - /* if there are no UVs, default to local */ - texmapping = MOD_DISP_MAP_LOCAL; - } - - const MVert *mv = BKE_mesh_verts(mesh); - for (i = 0; i < verts_num; i++, mv++, r_texco++) { - switch (texmapping) { - case MOD_DISP_MAP_LOCAL: - copy_v3_v3(*r_texco, cos != NULL ? *cos : mv->co); - break; - case MOD_DISP_MAP_GLOBAL: - mul_v3_m4v3(*r_texco, ob->obmat, cos != NULL ? *cos : mv->co); - break; - case MOD_DISP_MAP_OBJECT: - mul_v3_m4v3(*r_texco, ob->obmat, cos != NULL ? *cos : mv->co); - mul_m4_v3(mapref_imat, *r_texco); - break; - } - if (cos != NULL) { - cos++; - } - } -} - -void MOD_previous_vcos_store(ModifierData *md, const float (*vert_coords)[3]) -{ - while ((md = md->next) && md->type == eModifierType_Armature) { - ArmatureModifierData *amd = (ArmatureModifierData *)md; - if (amd->multi && amd->vert_coords_prev == NULL) { - amd->vert_coords_prev = MEM_dupallocN(vert_coords); - } - else { - break; - } - } - /* lattice/mesh modifier too */ -} - -Mesh *MOD_deform_mesh_eval_get(Object *ob, - struct BMEditMesh *em, - Mesh *mesh, - const float (*vertexCos)[3], - const int verts_num, - const bool use_orco) -{ - if (mesh != NULL) { - /* pass */ - } - else if (ob->type == OB_MESH) { - if (em) { - mesh = BKE_mesh_wrapper_from_editmesh_with_coords(em, NULL, vertexCos, ob->data); - } - else { - /* TODO(sybren): after modifier conversion of DM to Mesh is done, check whether - * we really need a copy here. Maybe the CoW ob->data can be directly used. */ - Mesh *mesh_prior_modifiers = BKE_object_get_pre_modified_mesh(ob); - mesh = (Mesh *)BKE_id_copy_ex(NULL, - &mesh_prior_modifiers->id, - NULL, - (LIB_ID_COPY_LOCALIZE | LIB_ID_COPY_CD_REFERENCE)); - mesh->runtime.deformed_only = 1; - } - - if (em != NULL) { - /* pass */ - } - /* TODO(sybren): after modifier conversion of DM to Mesh is done, check whether - * we really need vertexCos here. */ - else if (vertexCos) { - BKE_mesh_vert_coords_apply(mesh, vertexCos); - } - - if (use_orco) { - BKE_mesh_orco_ensure(ob, mesh); - } - } - else if (ELEM(ob->type, OB_FONT, OB_CURVES_LEGACY, OB_SURF)) { - /* TODO(sybren): get evaluated mesh from depsgraph once - * that's properly generated for curves. */ - mesh = BKE_mesh_new_nomain_from_curve(ob); - - /* Currently, that may not be the case every time - * (texts e.g. tend to give issues, - * also when deforming curve points instead of generated curve geometry... ). */ - if (mesh != NULL && mesh->totvert != verts_num) { - BKE_id_free(NULL, mesh); - mesh = NULL; - } - } - - if (mesh && mesh->runtime.wrapper_type == ME_WRAPPER_TYPE_MDATA) { - BLI_assert(mesh->totvert == verts_num); - } - - return mesh; -} - -void MOD_get_vgroup( - Object *ob, struct Mesh *mesh, const char *name, const MDeformVert **dvert, int *defgrp_index) -{ - if (mesh) { - *defgrp_index = BKE_id_defgroup_name_index(&mesh->id, name); - if (*defgrp_index != -1) { - *dvert = BKE_mesh_deform_verts(mesh); - } - else { - *dvert = NULL; - } - } - else { - *defgrp_index = BKE_object_defgroup_name_index(ob, name); - if (*defgrp_index != -1 && ob->type == OB_LATTICE) { - *dvert = BKE_lattice_deform_verts_get(ob); - } - else { - *dvert = NULL; - } - } -} - -void MOD_depsgraph_update_object_bone_relation(struct DepsNodeHandle *node, - Object *object, - const char *bonename, - const char *description) -{ - if (object == NULL) { - return; - } - if (bonename[0] != '\0' && object->type == OB_ARMATURE) { - DEG_add_object_relation(node, object, DEG_OB_COMP_EVAL_POSE, description); - } - else { - DEG_add_object_relation(node, object, DEG_OB_COMP_TRANSFORM, description); - } -} - -void modifier_type_init(ModifierTypeInfo *types[]) -{ -#define INIT_TYPE(typeName) (types[eModifierType_##typeName] = &modifierType_##typeName) - INIT_TYPE(None); - INIT_TYPE(Curve); - INIT_TYPE(Lattice); - INIT_TYPE(Subsurf); - INIT_TYPE(Build); - INIT_TYPE(Array); - INIT_TYPE(Mirror); - INIT_TYPE(EdgeSplit); - INIT_TYPE(Bevel); - INIT_TYPE(Displace); - INIT_TYPE(UVProject); - INIT_TYPE(Decimate); - INIT_TYPE(Smooth); - INIT_TYPE(Cast); - INIT_TYPE(Wave); - INIT_TYPE(Armature); - INIT_TYPE(Hook); - INIT_TYPE(Softbody); - INIT_TYPE(Cloth); - INIT_TYPE(Collision); - INIT_TYPE(Boolean); - INIT_TYPE(MeshDeform); - INIT_TYPE(Ocean); - INIT_TYPE(ParticleSystem); - INIT_TYPE(ParticleInstance); - INIT_TYPE(Explode); - INIT_TYPE(Shrinkwrap); - INIT_TYPE(Mask); - INIT_TYPE(SimpleDeform); - INIT_TYPE(Multires); - INIT_TYPE(Surface); - INIT_TYPE(Fluid); - INIT_TYPE(ShapeKey); - INIT_TYPE(Solidify); - INIT_TYPE(Screw); - INIT_TYPE(Warp); - INIT_TYPE(WeightVGEdit); - INIT_TYPE(WeightVGMix); - INIT_TYPE(WeightVGProximity); - INIT_TYPE(DynamicPaint); - INIT_TYPE(Remesh); - INIT_TYPE(Skin); - INIT_TYPE(LaplacianSmooth); - INIT_TYPE(Triangulate); - INIT_TYPE(UVWarp); - INIT_TYPE(MeshCache); - INIT_TYPE(LaplacianDeform); - INIT_TYPE(Wireframe); - INIT_TYPE(Weld); - INIT_TYPE(DataTransfer); - INIT_TYPE(NormalEdit); - INIT_TYPE(CorrectiveSmooth); - INIT_TYPE(MeshSequenceCache); - INIT_TYPE(SurfaceDeform); - INIT_TYPE(WeightedNormal); - INIT_TYPE(MeshToVolume); - INIT_TYPE(VolumeDisplace); - INIT_TYPE(VolumeToMesh); - INIT_TYPE(Nodes); -#undef INIT_TYPE -} diff --git a/source/blender/modifiers/intern/MOD_util.cc b/source/blender/modifiers/intern/MOD_util.cc new file mode 100644 index 00000000000..7d66f75b20f --- /dev/null +++ b/source/blender/modifiers/intern/MOD_util.cc @@ -0,0 +1,330 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2005 Blender Foundation. All rights reserved. */ + +/** \file + * \ingroup modifiers + */ + +#include + +#include "BLI_utildefines.h" + +#include "BLI_bitmap.h" +#include "BLI_math_matrix.h" +#include "BLI_math_vector.h" + +#include "DNA_image_types.h" +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_modifier_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" + +#include "BKE_action.h" /* BKE_pose_channel_find_name */ +#include "BKE_deform.h" +#include "BKE_editmesh.h" +#include "BKE_image.h" +#include "BKE_lattice.h" +#include "BKE_lib_id.h" +#include "BKE_mesh.h" +#include "BKE_mesh_wrapper.h" +#include "BKE_object.h" + +#include "BKE_modifier.h" + +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_query.h" + +#include "MOD_modifiertypes.h" +#include "MOD_util.h" + +#include "MEM_guardedalloc.h" + +#include "bmesh.h" + +void MOD_init_texture(MappingInfoModifierData *dmd, const ModifierEvalContext *ctx) +{ + Tex *tex = dmd->texture; + + if (tex == nullptr) { + return; + } + + if (tex->ima && BKE_image_is_animated(tex->ima)) { + BKE_image_user_frame_calc(tex->ima, &tex->iuser, DEG_get_ctime(ctx->depsgraph)); + } +} + +/* TODO: to be renamed to get_texture_coords once we are done with moving modifiers to Mesh. */ +void MOD_get_texture_coords(MappingInfoModifierData *dmd, + const ModifierEvalContext *UNUSED(ctx), + Object *ob, + Mesh *mesh, + float (*cos)[3], + float (*r_texco)[3]) +{ + const int verts_num = mesh->totvert; + int i; + int texmapping = dmd->texmapping; + float mapref_imat[4][4]; + + if (texmapping == MOD_DISP_MAP_OBJECT) { + if (dmd->map_object != nullptr) { + Object *map_object = dmd->map_object; + if (dmd->map_bone[0] != '\0') { + bPoseChannel *pchan = BKE_pose_channel_find_name(map_object->pose, dmd->map_bone); + if (pchan) { + float mat_bone_world[4][4]; + mul_m4_m4m4(mat_bone_world, map_object->obmat, pchan->pose_mat); + invert_m4_m4(mapref_imat, mat_bone_world); + } + else { + invert_m4_m4(mapref_imat, map_object->obmat); + } + } + else { + invert_m4_m4(mapref_imat, map_object->obmat); + } + } + else { /* if there is no map object, default to local */ + texmapping = MOD_DISP_MAP_LOCAL; + } + } + + /* UVs need special handling, since they come from faces */ + if (texmapping == MOD_DISP_MAP_UV) { + if (CustomData_has_layer(&mesh->ldata, CD_MLOOPUV)) { + const MPoly *mpoly = BKE_mesh_polys(mesh); + const MPoly *mp; + const MLoop *mloop = BKE_mesh_loops(mesh); + BLI_bitmap *done = BLI_BITMAP_NEW(verts_num, __func__); + const int polys_num = mesh->totpoly; + char uvname[MAX_CUSTOMDATA_LAYER_NAME]; + + CustomData_validate_layer_name(&mesh->ldata, CD_MLOOPUV, dmd->uvlayer_name, uvname); + const MLoopUV *mloop_uv = static_cast( + CustomData_get_layer_named(&mesh->ldata, CD_MLOOPUV, uvname)); + + /* verts are given the UV from the first face that uses them */ + for (i = 0, mp = mpoly; i < polys_num; i++, mp++) { + uint fidx = mp->totloop - 1; + + do { + uint lidx = mp->loopstart + fidx; + uint vidx = mloop[lidx].v; + + if (!BLI_BITMAP_TEST(done, vidx)) { + /* remap UVs from [0, 1] to [-1, 1] */ + r_texco[vidx][0] = (mloop_uv[lidx].uv[0] * 2.0f) - 1.0f; + r_texco[vidx][1] = (mloop_uv[lidx].uv[1] * 2.0f) - 1.0f; + BLI_BITMAP_ENABLE(done, vidx); + } + + } while (fidx--); + } + + MEM_freeN(done); + return; + } + + /* if there are no UVs, default to local */ + texmapping = MOD_DISP_MAP_LOCAL; + } + + const MVert *mv = BKE_mesh_verts(mesh); + for (i = 0; i < verts_num; i++, mv++, r_texco++) { + switch (texmapping) { + case MOD_DISP_MAP_LOCAL: + copy_v3_v3(*r_texco, cos != nullptr ? *cos : mv->co); + break; + case MOD_DISP_MAP_GLOBAL: + mul_v3_m4v3(*r_texco, ob->obmat, cos != nullptr ? *cos : mv->co); + break; + case MOD_DISP_MAP_OBJECT: + mul_v3_m4v3(*r_texco, ob->obmat, cos != nullptr ? *cos : mv->co); + mul_m4_v3(mapref_imat, *r_texco); + break; + } + if (cos != nullptr) { + cos++; + } + } +} + +void MOD_previous_vcos_store(ModifierData *md, const float (*vert_coords)[3]) +{ + while ((md = md->next) && md->type == eModifierType_Armature) { + ArmatureModifierData *amd = (ArmatureModifierData *)md; + if (amd->multi && amd->vert_coords_prev == nullptr) { + amd->vert_coords_prev = static_cast(MEM_dupallocN(vert_coords)); + } + else { + break; + } + } + /* lattice/mesh modifier too */ +} + +Mesh *MOD_deform_mesh_eval_get(Object *ob, + struct BMEditMesh *em, + Mesh *mesh, + const float (*vertexCos)[3], + const int verts_num, + const bool use_orco) +{ + if (mesh != nullptr) { + /* pass */ + } + else if (ob->type == OB_MESH) { + if (em) { + mesh = BKE_mesh_wrapper_from_editmesh_with_coords( + em, nullptr, vertexCos, static_cast(ob->data)); + } + else { + /* TODO(sybren): after modifier conversion of DM to Mesh is done, check whether + * we really need a copy here. Maybe the CoW ob->data can be directly used. */ + Mesh *mesh_prior_modifiers = BKE_object_get_pre_modified_mesh(ob); + mesh = (Mesh *)BKE_id_copy_ex(nullptr, + &mesh_prior_modifiers->id, + nullptr, + (LIB_ID_COPY_LOCALIZE | LIB_ID_COPY_CD_REFERENCE)); + mesh->runtime.deformed_only = 1; + } + + if (em != nullptr) { + /* pass */ + } + /* TODO(sybren): after modifier conversion of DM to Mesh is done, check whether + * we really need vertexCos here. */ + else if (vertexCos) { + BKE_mesh_vert_coords_apply(mesh, vertexCos); + } + + if (use_orco) { + BKE_mesh_orco_ensure(ob, mesh); + } + } + else if (ELEM(ob->type, OB_FONT, OB_CURVES_LEGACY, OB_SURF)) { + /* TODO(sybren): get evaluated mesh from depsgraph once + * that's properly generated for curves. */ + mesh = BKE_mesh_new_nomain_from_curve(ob); + + /* Currently, that may not be the case every time + * (texts e.g. tend to give issues, + * also when deforming curve points instead of generated curve geometry... ). */ + if (mesh != nullptr && mesh->totvert != verts_num) { + BKE_id_free(nullptr, mesh); + mesh = nullptr; + } + } + + if (mesh && mesh->runtime.wrapper_type == ME_WRAPPER_TYPE_MDATA) { + BLI_assert(mesh->totvert == verts_num); + } + + return mesh; +} + +void MOD_get_vgroup( + Object *ob, struct Mesh *mesh, const char *name, const MDeformVert **dvert, int *defgrp_index) +{ + if (mesh) { + *defgrp_index = BKE_id_defgroup_name_index(&mesh->id, name); + if (*defgrp_index != -1) { + *dvert = BKE_mesh_deform_verts(mesh); + } + else { + *dvert = nullptr; + } + } + else { + *defgrp_index = BKE_object_defgroup_name_index(ob, name); + if (*defgrp_index != -1 && ob->type == OB_LATTICE) { + *dvert = BKE_lattice_deform_verts_get(ob); + } + else { + *dvert = nullptr; + } + } +} + +void MOD_depsgraph_update_object_bone_relation(struct DepsNodeHandle *node, + Object *object, + const char *bonename, + const char *description) +{ + if (object == nullptr) { + return; + } + if (bonename[0] != '\0' && object->type == OB_ARMATURE) { + DEG_add_object_relation(node, object, DEG_OB_COMP_EVAL_POSE, description); + } + else { + DEG_add_object_relation(node, object, DEG_OB_COMP_TRANSFORM, description); + } +} + +void modifier_type_init(ModifierTypeInfo *types[]) +{ +#define INIT_TYPE(typeName) (types[eModifierType_##typeName] = &modifierType_##typeName) + INIT_TYPE(None); + INIT_TYPE(Curve); + INIT_TYPE(Lattice); + INIT_TYPE(Subsurf); + INIT_TYPE(Build); + INIT_TYPE(Array); + INIT_TYPE(Mirror); + INIT_TYPE(EdgeSplit); + INIT_TYPE(Bevel); + INIT_TYPE(Displace); + INIT_TYPE(UVProject); + INIT_TYPE(Decimate); + INIT_TYPE(Smooth); + INIT_TYPE(Cast); + INIT_TYPE(Wave); + INIT_TYPE(Armature); + INIT_TYPE(Hook); + INIT_TYPE(Softbody); + INIT_TYPE(Cloth); + INIT_TYPE(Collision); + INIT_TYPE(Boolean); + INIT_TYPE(MeshDeform); + INIT_TYPE(Ocean); + INIT_TYPE(ParticleSystem); + INIT_TYPE(ParticleInstance); + INIT_TYPE(Explode); + INIT_TYPE(Shrinkwrap); + INIT_TYPE(Mask); + INIT_TYPE(SimpleDeform); + INIT_TYPE(Multires); + INIT_TYPE(Surface); + INIT_TYPE(Fluid); + INIT_TYPE(ShapeKey); + INIT_TYPE(Solidify); + INIT_TYPE(Screw); + INIT_TYPE(Warp); + INIT_TYPE(WeightVGEdit); + INIT_TYPE(WeightVGMix); + INIT_TYPE(WeightVGProximity); + INIT_TYPE(DynamicPaint); + INIT_TYPE(Remesh); + INIT_TYPE(Skin); + INIT_TYPE(LaplacianSmooth); + INIT_TYPE(Triangulate); + INIT_TYPE(UVWarp); + INIT_TYPE(MeshCache); + INIT_TYPE(LaplacianDeform); + INIT_TYPE(Wireframe); + INIT_TYPE(Weld); + INIT_TYPE(DataTransfer); + INIT_TYPE(NormalEdit); + INIT_TYPE(CorrectiveSmooth); + INIT_TYPE(MeshSequenceCache); + INIT_TYPE(SurfaceDeform); + INIT_TYPE(WeightedNormal); + INIT_TYPE(MeshToVolume); + INIT_TYPE(VolumeDisplace); + INIT_TYPE(VolumeToMesh); + INIT_TYPE(Nodes); +#undef INIT_TYPE +} diff --git a/source/blender/modifiers/intern/MOD_uvproject.c b/source/blender/modifiers/intern/MOD_uvproject.c deleted file mode 100644 index 64e025ea56e..00000000000 --- a/source/blender/modifiers/intern/MOD_uvproject.c +++ /dev/null @@ -1,380 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later - * Copyright 2005 Blender Foundation. All rights reserved. */ - -/** \file - * \ingroup modifiers - */ - -/* UV Project modifier: Generates UVs projected from an object */ - -#include "BLI_utildefines.h" - -#include "BLI_math.h" -#include "BLI_uvproject.h" - -#include "BLT_translation.h" - -#include "DNA_camera_types.h" -#include "DNA_defaults.h" -#include "DNA_mesh_types.h" -#include "DNA_meshdata_types.h" -#include "DNA_object_types.h" -#include "DNA_screen_types.h" - -#include "BKE_camera.h" -#include "BKE_context.h" -#include "BKE_lib_query.h" -#include "BKE_material.h" -#include "BKE_mesh.h" -#include "BKE_screen.h" - -#include "UI_interface.h" -#include "UI_resources.h" - -#include "RNA_access.h" -#include "RNA_prototypes.h" - -#include "MOD_modifiertypes.h" -#include "MOD_ui_common.h" - -#include "MEM_guardedalloc.h" - -#include "DEG_depsgraph.h" -#include "DEG_depsgraph_build.h" -#include "DEG_depsgraph_query.h" - -static void initData(ModifierData *md) -{ - UVProjectModifierData *umd = (UVProjectModifierData *)md; - - BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(umd, modifier)); - - MEMCPY_STRUCT_AFTER(umd, DNA_struct_default_get(UVProjectModifierData), modifier); -} - -static void requiredDataMask(ModifierData *UNUSED(md), CustomData_MeshMasks *r_cddata_masks) -{ - /* ask for UV coordinates */ - r_cddata_masks->lmask |= CD_MASK_MLOOPUV; -} - -static void foreachIDLink(ModifierData *md, Object *ob, IDWalkFunc walk, void *userData) -{ - UVProjectModifierData *umd = (UVProjectModifierData *)md; - for (int i = 0; i < MOD_UVPROJECT_MAXPROJECTORS; i++) { - walk(userData, ob, (ID **)&umd->projectors[i], IDWALK_CB_NOP); - } -} - -static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphContext *ctx) -{ - UVProjectModifierData *umd = (UVProjectModifierData *)md; - bool do_add_own_transform = false; - for (int i = 0; i < umd->projectors_num; i++) { - if (umd->projectors[i] != NULL) { - DEG_add_object_relation( - ctx->node, umd->projectors[i], DEG_OB_COMP_TRANSFORM, "UV Project Modifier"); - do_add_own_transform = true; - } - } - if (do_add_own_transform) { - DEG_add_depends_on_transform_relation(ctx->node, "UV Project Modifier"); - } -} - -typedef struct Projector { - Object *ob; /* object this projector is derived from */ - float projmat[4][4]; /* projection matrix */ - float normal[3]; /* projector normal in world space */ - void *uci; /* optional uv-project info (panorama projection) */ -} Projector; - -static Mesh *uvprojectModifier_do(UVProjectModifierData *umd, - const ModifierEvalContext *UNUSED(ctx), - Object *ob, - Mesh *mesh) -{ - float(*coords)[3], (*co)[3]; - MLoopUV *mloop_uv; - int i, verts_num, polys_num, loops_num; - const MPoly *mp; - Projector projectors[MOD_UVPROJECT_MAXPROJECTORS]; - int projectors_num = 0; - char uvname[MAX_CUSTOMDATA_LAYER_NAME]; - float aspx = umd->aspectx ? umd->aspectx : 1.0f; - float aspy = umd->aspecty ? umd->aspecty : 1.0f; - float scax = umd->scalex ? umd->scalex : 1.0f; - float scay = umd->scaley ? umd->scaley : 1.0f; - int free_uci = 0; - - for (i = 0; i < umd->projectors_num; i++) { - if (umd->projectors[i] != NULL) { - projectors[projectors_num++].ob = umd->projectors[i]; - } - } - - if (projectors_num == 0) { - return mesh; - } - - /* Create a new layer if no UV Maps are available - * (e.g. if a preceding modifier could not preserve it). */ - if (!CustomData_has_layer(&mesh->ldata, CD_MLOOPUV)) { - CustomData_add_layer_named( - &mesh->ldata, CD_MLOOPUV, CD_SET_DEFAULT, NULL, mesh->totloop, umd->uvlayer_name); - } - - /* make sure we're using an existing layer */ - CustomData_validate_layer_name(&mesh->ldata, CD_MLOOPUV, umd->uvlayer_name, uvname); - - /* calculate a projection matrix and normal for each projector */ - for (i = 0; i < projectors_num; i++) { - float tmpmat[4][4]; - float offsetmat[4][4]; - Camera *cam = NULL; - /* calculate projection matrix */ - invert_m4_m4(projectors[i].projmat, projectors[i].ob->obmat); - - projectors[i].uci = NULL; - - if (projectors[i].ob->type == OB_CAMERA) { - cam = (Camera *)projectors[i].ob->data; - if (cam->type == CAM_PANO) { - projectors[i].uci = BLI_uvproject_camera_info(projectors[i].ob, NULL, aspx, aspy); - BLI_uvproject_camera_info_scale(projectors[i].uci, scax, scay); - free_uci = 1; - } - else { - CameraParams params; - - /* setup parameters */ - BKE_camera_params_init(¶ms); - BKE_camera_params_from_object(¶ms, projectors[i].ob); - - /* Compute matrix, view-plane, etc. */ - BKE_camera_params_compute_viewplane(¶ms, 1, 1, aspx, aspy); - - /* scale the view-plane */ - params.viewplane.xmin *= scax; - params.viewplane.xmax *= scax; - params.viewplane.ymin *= scay; - params.viewplane.ymax *= scay; - - BKE_camera_params_compute_matrix(¶ms); - mul_m4_m4m4(tmpmat, params.winmat, projectors[i].projmat); - } - } - else { - copy_m4_m4(tmpmat, projectors[i].projmat); - } - - unit_m4(offsetmat); - mul_mat3_m4_fl(offsetmat, 0.5); - offsetmat[3][0] = offsetmat[3][1] = offsetmat[3][2] = 0.5; - - mul_m4_m4m4(projectors[i].projmat, offsetmat, tmpmat); - - /* Calculate world-space projector normal (for best projector test). */ - projectors[i].normal[0] = 0; - projectors[i].normal[1] = 0; - projectors[i].normal[2] = 1; - mul_mat3_m4_v3(projectors[i].ob->obmat, projectors[i].normal); - } - - polys_num = mesh->totpoly; - loops_num = mesh->totloop; - - /* make sure we are not modifying the original UV map */ - mloop_uv = CustomData_duplicate_referenced_layer_named( - &mesh->ldata, CD_MLOOPUV, uvname, loops_num); - - coords = BKE_mesh_vert_coords_alloc(mesh, &verts_num); - - /* Convert coords to world-space. */ - for (i = 0, co = coords; i < verts_num; i++, co++) { - mul_m4_v3(ob->obmat, *co); - } - - /* if only one projector, project coords to UVs */ - if (projectors_num == 1 && projectors[0].uci == NULL) { - for (i = 0, co = coords; i < verts_num; i++, co++) { - mul_project_m4_v3(projectors[0].projmat, *co); - } - } - - const MPoly *polys = BKE_mesh_polys(mesh); - const MLoop *loops = BKE_mesh_loops(mesh); - - /* apply coords as UVs */ - for (i = 0, mp = polys; i < polys_num; i++, mp++) { - if (projectors_num == 1) { - if (projectors[0].uci) { - uint fidx = mp->totloop - 1; - do { - uint lidx = mp->loopstart + fidx; - uint vidx = loops[lidx].v; - BLI_uvproject_from_camera(mloop_uv[lidx].uv, coords[vidx], projectors[0].uci); - } while (fidx--); - } - else { - /* apply transformed coords as UVs */ - uint fidx = mp->totloop - 1; - do { - uint lidx = mp->loopstart + fidx; - uint vidx = loops[lidx].v; - copy_v2_v2(mloop_uv[lidx].uv, coords[vidx]); - } while (fidx--); - } - } - else { - /* multiple projectors, select the closest to face normal direction */ - float face_no[3]; - int j; - Projector *best_projector; - float best_dot; - - /* get the untransformed face normal */ - BKE_mesh_calc_poly_normal_coords( - mp, loops + mp->loopstart, (const float(*)[3])coords, face_no); - - /* find the projector which the face points at most directly - * (projector normal with largest dot product is best) - */ - best_dot = dot_v3v3(projectors[0].normal, face_no); - best_projector = &projectors[0]; - - for (j = 1; j < projectors_num; j++) { - float tmp_dot = dot_v3v3(projectors[j].normal, face_no); - if (tmp_dot > best_dot) { - best_dot = tmp_dot; - best_projector = &projectors[j]; - } - } - - if (best_projector->uci) { - uint fidx = mp->totloop - 1; - do { - uint lidx = mp->loopstart + fidx; - uint vidx = loops[lidx].v; - BLI_uvproject_from_camera(mloop_uv[lidx].uv, coords[vidx], best_projector->uci); - } while (fidx--); - } - else { - uint fidx = mp->totloop - 1; - do { - uint lidx = mp->loopstart + fidx; - uint vidx = loops[lidx].v; - mul_v2_project_m4_v3(mloop_uv[lidx].uv, best_projector->projmat, coords[vidx]); - } while (fidx--); - } - } - } - - MEM_freeN(coords); - - if (free_uci) { - int j; - for (j = 0; j < projectors_num; j++) { - if (projectors[j].uci) { - MEM_freeN(projectors[j].uci); - } - } - } - - mesh->runtime.is_original_bmesh = false; - - return mesh; -} - -static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mesh) -{ - Mesh *result; - UVProjectModifierData *umd = (UVProjectModifierData *)md; - - result = uvprojectModifier_do(umd, ctx, ctx->object, mesh); - - return result; -} - -static void panel_draw(const bContext *UNUSED(C), Panel *panel) -{ - uiLayout *sub; - uiLayout *layout = panel->layout; - - PointerRNA ob_ptr; - PointerRNA *ptr = modifier_panel_get_property_pointers(panel, &ob_ptr); - - PointerRNA obj_data_ptr = RNA_pointer_get(&ob_ptr, "data"); - - uiLayoutSetPropSep(layout, true); - - uiItemPointerR(layout, ptr, "uv_layer", &obj_data_ptr, "uv_layers", NULL, ICON_NONE); - - /* Aspect and Scale are only used for camera projectors. */ - bool has_camera = false; - RNA_BEGIN (ptr, projector_ptr, "projectors") { - PointerRNA ob_projector = RNA_pointer_get(&projector_ptr, "object"); - if (!RNA_pointer_is_null(&ob_projector) && RNA_enum_get(&ob_projector, "type") == OB_CAMERA) { - has_camera = true; - break; - } - } - RNA_END; - - sub = uiLayoutColumn(layout, true); - uiLayoutSetActive(sub, has_camera); - uiItemR(sub, ptr, "aspect_x", 0, NULL, ICON_NONE); - uiItemR(sub, ptr, "aspect_y", 0, IFACE_("Y"), ICON_NONE); - - sub = uiLayoutColumn(layout, true); - uiLayoutSetActive(sub, has_camera); - uiItemR(sub, ptr, "scale_x", 0, NULL, ICON_NONE); - uiItemR(sub, ptr, "scale_y", 0, IFACE_("Y"), ICON_NONE); - - uiItemR(layout, ptr, "projector_count", 0, IFACE_("Projectors"), ICON_NONE); - RNA_BEGIN (ptr, projector_ptr, "projectors") { - uiItemR(layout, &projector_ptr, "object", 0, NULL, ICON_NONE); - } - RNA_END; - - modifier_panel_end(layout, ptr); -} - -static void panelRegister(ARegionType *region_type) -{ - modifier_panel_register(region_type, eModifierType_UVProject, panel_draw); -} - -ModifierTypeInfo modifierType_UVProject = { - /* name */ N_("UVProject"), - /* structName */ "UVProjectModifierData", - /* structSize */ sizeof(UVProjectModifierData), - /* srna */ &RNA_UVProjectModifier, - /* type */ eModifierTypeType_NonGeometrical, - /* flags */ eModifierTypeFlag_AcceptsMesh | eModifierTypeFlag_SupportsMapping | - eModifierTypeFlag_SupportsEditmode | eModifierTypeFlag_EnableInEditmode, - /* icon */ ICON_MOD_UVPROJECT, - - /* copyData */ BKE_modifier_copydata_generic, - - /* deformVerts */ NULL, - /* deformMatrices */ NULL, - /* deformVertsEM */ NULL, - /* deformMatricesEM */ NULL, - /* modifyMesh */ modifyMesh, - /* modifyGeometrySet */ NULL, - - /* initData */ initData, - /* requiredDataMask */ requiredDataMask, - /* freeData */ NULL, - /* isDisabled */ NULL, - /* updateDepsgraph */ updateDepsgraph, - /* dependsOnTime */ NULL, - /* dependsOnNormals */ NULL, - /* foreachIDLink */ foreachIDLink, - /* foreachTexLink */ NULL, - /* freeRuntimeData */ NULL, - /* panelRegister */ panelRegister, - /* blendWrite */ NULL, - /* blendRead */ NULL, -}; diff --git a/source/blender/modifiers/intern/MOD_uvproject.cc b/source/blender/modifiers/intern/MOD_uvproject.cc new file mode 100644 index 00000000000..248b7b48a7c --- /dev/null +++ b/source/blender/modifiers/intern/MOD_uvproject.cc @@ -0,0 +1,382 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2005 Blender Foundation. All rights reserved. */ + +/** \file + * \ingroup modifiers + */ + +/* UV Project modifier: Generates UVs projected from an object */ + +#include "BLI_utildefines.h" + +#include "BLI_math.h" +#include "BLI_uvproject.h" + +#include "BLT_translation.h" + +#include "DNA_camera_types.h" +#include "DNA_defaults.h" +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_object_types.h" +#include "DNA_screen_types.h" + +#include "BKE_camera.h" +#include "BKE_context.h" +#include "BKE_lib_query.h" +#include "BKE_material.h" +#include "BKE_mesh.h" +#include "BKE_screen.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "RNA_access.h" +#include "RNA_prototypes.h" + +#include "MOD_modifiertypes.h" +#include "MOD_ui_common.h" + +#include "MEM_guardedalloc.h" + +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_build.h" +#include "DEG_depsgraph_query.h" + +static void initData(ModifierData *md) +{ + UVProjectModifierData *umd = (UVProjectModifierData *)md; + + BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(umd, modifier)); + + MEMCPY_STRUCT_AFTER(umd, DNA_struct_default_get(UVProjectModifierData), modifier); +} + +static void requiredDataMask(ModifierData *UNUSED(md), CustomData_MeshMasks *r_cddata_masks) +{ + /* ask for UV coordinates */ + r_cddata_masks->lmask |= CD_MASK_MLOOPUV; +} + +static void foreachIDLink(ModifierData *md, Object *ob, IDWalkFunc walk, void *userData) +{ + UVProjectModifierData *umd = (UVProjectModifierData *)md; + for (int i = 0; i < MOD_UVPROJECT_MAXPROJECTORS; i++) { + walk(userData, ob, (ID **)&umd->projectors[i], IDWALK_CB_NOP); + } +} + +static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphContext *ctx) +{ + UVProjectModifierData *umd = (UVProjectModifierData *)md; + bool do_add_own_transform = false; + for (int i = 0; i < umd->projectors_num; i++) { + if (umd->projectors[i] != nullptr) { + DEG_add_object_relation( + ctx->node, umd->projectors[i], DEG_OB_COMP_TRANSFORM, "UV Project Modifier"); + do_add_own_transform = true; + } + } + if (do_add_own_transform) { + DEG_add_depends_on_transform_relation(ctx->node, "UV Project Modifier"); + } +} + +struct Projector { + Object *ob; /* object this projector is derived from */ + float projmat[4][4]; /* projection matrix */ + float normal[3]; /* projector normal in world space */ + void *uci; /* optional uv-project info (panorama projection) */ +}; + +static Mesh *uvprojectModifier_do(UVProjectModifierData *umd, + const ModifierEvalContext *UNUSED(ctx), + Object *ob, + Mesh *mesh) +{ + float(*coords)[3], (*co)[3]; + int i, verts_num, polys_num, loops_num; + const MPoly *mp; + Projector projectors[MOD_UVPROJECT_MAXPROJECTORS]; + int projectors_num = 0; + char uvname[MAX_CUSTOMDATA_LAYER_NAME]; + float aspx = umd->aspectx ? umd->aspectx : 1.0f; + float aspy = umd->aspecty ? umd->aspecty : 1.0f; + float scax = umd->scalex ? umd->scalex : 1.0f; + float scay = umd->scaley ? umd->scaley : 1.0f; + int free_uci = 0; + + for (i = 0; i < umd->projectors_num; i++) { + if (umd->projectors[i] != nullptr) { + projectors[projectors_num++].ob = umd->projectors[i]; + } + } + + if (projectors_num == 0) { + return mesh; + } + + /* Create a new layer if no UV Maps are available + * (e.g. if a preceding modifier could not preserve it). */ + if (!CustomData_has_layer(&mesh->ldata, CD_MLOOPUV)) { + CustomData_add_layer_named( + &mesh->ldata, CD_MLOOPUV, CD_SET_DEFAULT, nullptr, mesh->totloop, umd->uvlayer_name); + } + + /* make sure we're using an existing layer */ + CustomData_validate_layer_name(&mesh->ldata, CD_MLOOPUV, umd->uvlayer_name, uvname); + + /* calculate a projection matrix and normal for each projector */ + for (i = 0; i < projectors_num; i++) { + float tmpmat[4][4]; + float offsetmat[4][4]; + Camera *cam = nullptr; + /* calculate projection matrix */ + invert_m4_m4(projectors[i].projmat, projectors[i].ob->obmat); + + projectors[i].uci = nullptr; + + if (projectors[i].ob->type == OB_CAMERA) { + cam = (Camera *)projectors[i].ob->data; + if (cam->type == CAM_PANO) { + projectors[i].uci = BLI_uvproject_camera_info(projectors[i].ob, nullptr, aspx, aspy); + BLI_uvproject_camera_info_scale( + static_cast(projectors[i].uci), scax, scay); + free_uci = 1; + } + else { + CameraParams params; + + /* setup parameters */ + BKE_camera_params_init(¶ms); + BKE_camera_params_from_object(¶ms, projectors[i].ob); + + /* Compute matrix, view-plane, etc. */ + BKE_camera_params_compute_viewplane(¶ms, 1, 1, aspx, aspy); + + /* scale the view-plane */ + params.viewplane.xmin *= scax; + params.viewplane.xmax *= scax; + params.viewplane.ymin *= scay; + params.viewplane.ymax *= scay; + + BKE_camera_params_compute_matrix(¶ms); + mul_m4_m4m4(tmpmat, params.winmat, projectors[i].projmat); + } + } + else { + copy_m4_m4(tmpmat, projectors[i].projmat); + } + + unit_m4(offsetmat); + mul_mat3_m4_fl(offsetmat, 0.5); + offsetmat[3][0] = offsetmat[3][1] = offsetmat[3][2] = 0.5; + + mul_m4_m4m4(projectors[i].projmat, offsetmat, tmpmat); + + /* Calculate world-space projector normal (for best projector test). */ + projectors[i].normal[0] = 0; + projectors[i].normal[1] = 0; + projectors[i].normal[2] = 1; + mul_mat3_m4_v3(projectors[i].ob->obmat, projectors[i].normal); + } + + polys_num = mesh->totpoly; + loops_num = mesh->totloop; + + /* make sure we are not modifying the original UV map */ + MLoopUV *mloop_uv = static_cast( + CustomData_duplicate_referenced_layer_named(&mesh->ldata, CD_MLOOPUV, uvname, loops_num)); + + coords = BKE_mesh_vert_coords_alloc(mesh, &verts_num); + + /* Convert coords to world-space. */ + for (i = 0, co = coords; i < verts_num; i++, co++) { + mul_m4_v3(ob->obmat, *co); + } + + /* if only one projector, project coords to UVs */ + if (projectors_num == 1 && projectors[0].uci == nullptr) { + for (i = 0, co = coords; i < verts_num; i++, co++) { + mul_project_m4_v3(projectors[0].projmat, *co); + } + } + + const MPoly *polys = BKE_mesh_polys(mesh); + const MLoop *loops = BKE_mesh_loops(mesh); + + /* apply coords as UVs */ + for (i = 0, mp = polys; i < polys_num; i++, mp++) { + if (projectors_num == 1) { + if (projectors[0].uci) { + uint fidx = mp->totloop - 1; + do { + uint lidx = mp->loopstart + fidx; + uint vidx = loops[lidx].v; + BLI_uvproject_from_camera( + mloop_uv[lidx].uv, coords[vidx], static_cast(projectors[0].uci)); + } while (fidx--); + } + else { + /* apply transformed coords as UVs */ + uint fidx = mp->totloop - 1; + do { + uint lidx = mp->loopstart + fidx; + uint vidx = loops[lidx].v; + copy_v2_v2(mloop_uv[lidx].uv, coords[vidx]); + } while (fidx--); + } + } + else { + /* multiple projectors, select the closest to face normal direction */ + float face_no[3]; + int j; + Projector *best_projector; + float best_dot; + + /* get the untransformed face normal */ + BKE_mesh_calc_poly_normal_coords( + mp, loops + mp->loopstart, (const float(*)[3])coords, face_no); + + /* find the projector which the face points at most directly + * (projector normal with largest dot product is best) + */ + best_dot = dot_v3v3(projectors[0].normal, face_no); + best_projector = &projectors[0]; + + for (j = 1; j < projectors_num; j++) { + float tmp_dot = dot_v3v3(projectors[j].normal, face_no); + if (tmp_dot > best_dot) { + best_dot = tmp_dot; + best_projector = &projectors[j]; + } + } + + if (best_projector->uci) { + uint fidx = mp->totloop - 1; + do { + uint lidx = mp->loopstart + fidx; + uint vidx = loops[lidx].v; + BLI_uvproject_from_camera( + mloop_uv[lidx].uv, coords[vidx], static_cast(best_projector->uci)); + } while (fidx--); + } + else { + uint fidx = mp->totloop - 1; + do { + uint lidx = mp->loopstart + fidx; + uint vidx = loops[lidx].v; + mul_v2_project_m4_v3(mloop_uv[lidx].uv, best_projector->projmat, coords[vidx]); + } while (fidx--); + } + } + } + + MEM_freeN(coords); + + if (free_uci) { + int j; + for (j = 0; j < projectors_num; j++) { + if (projectors[j].uci) { + MEM_freeN(projectors[j].uci); + } + } + } + + mesh->runtime.is_original_bmesh = false; + + return mesh; +} + +static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mesh) +{ + Mesh *result; + UVProjectModifierData *umd = (UVProjectModifierData *)md; + + result = uvprojectModifier_do(umd, ctx, ctx->object, mesh); + + return result; +} + +static void panel_draw(const bContext *UNUSED(C), Panel *panel) +{ + uiLayout *sub; + uiLayout *layout = panel->layout; + + PointerRNA ob_ptr; + PointerRNA *ptr = modifier_panel_get_property_pointers(panel, &ob_ptr); + + PointerRNA obj_data_ptr = RNA_pointer_get(&ob_ptr, "data"); + + uiLayoutSetPropSep(layout, true); + + uiItemPointerR(layout, ptr, "uv_layer", &obj_data_ptr, "uv_layers", nullptr, ICON_NONE); + + /* Aspect and Scale are only used for camera projectors. */ + bool has_camera = false; + RNA_BEGIN (ptr, projector_ptr, "projectors") { + PointerRNA ob_projector = RNA_pointer_get(&projector_ptr, "object"); + if (!RNA_pointer_is_null(&ob_projector) && RNA_enum_get(&ob_projector, "type") == OB_CAMERA) { + has_camera = true; + break; + } + } + RNA_END; + + sub = uiLayoutColumn(layout, true); + uiLayoutSetActive(sub, has_camera); + uiItemR(sub, ptr, "aspect_x", 0, nullptr, ICON_NONE); + uiItemR(sub, ptr, "aspect_y", 0, IFACE_("Y"), ICON_NONE); + + sub = uiLayoutColumn(layout, true); + uiLayoutSetActive(sub, has_camera); + uiItemR(sub, ptr, "scale_x", 0, nullptr, ICON_NONE); + uiItemR(sub, ptr, "scale_y", 0, IFACE_("Y"), ICON_NONE); + + uiItemR(layout, ptr, "projector_count", 0, IFACE_("Projectors"), ICON_NONE); + RNA_BEGIN (ptr, projector_ptr, "projectors") { + uiItemR(layout, &projector_ptr, "object", 0, nullptr, ICON_NONE); + } + RNA_END; + + modifier_panel_end(layout, ptr); +} + +static void panelRegister(ARegionType *region_type) +{ + modifier_panel_register(region_type, eModifierType_UVProject, panel_draw); +} + +ModifierTypeInfo modifierType_UVProject = { + /* name */ N_("UVProject"), + /* structName */ "UVProjectModifierData", + /* structSize */ sizeof(UVProjectModifierData), + /* srna */ &RNA_UVProjectModifier, + /* type */ eModifierTypeType_NonGeometrical, + /* flags */ eModifierTypeFlag_AcceptsMesh | eModifierTypeFlag_SupportsMapping | + eModifierTypeFlag_SupportsEditmode | eModifierTypeFlag_EnableInEditmode, + /* icon */ ICON_MOD_UVPROJECT, + + /* copyData */ BKE_modifier_copydata_generic, + + /* deformVerts */ nullptr, + /* deformMatrices */ nullptr, + /* deformVertsEM */ nullptr, + /* deformMatricesEM */ nullptr, + /* modifyMesh */ modifyMesh, + /* modifyGeometrySet */ nullptr, + + /* initData */ initData, + /* requiredDataMask */ requiredDataMask, + /* freeData */ nullptr, + /* isDisabled */ nullptr, + /* updateDepsgraph */ updateDepsgraph, + /* dependsOnTime */ nullptr, + /* dependsOnNormals */ nullptr, + /* foreachIDLink */ foreachIDLink, + /* foreachTexLink */ nullptr, + /* freeRuntimeData */ nullptr, + /* panelRegister */ panelRegister, + /* blendWrite */ nullptr, + /* blendRead */ nullptr, +}; diff --git a/source/blender/modifiers/intern/MOD_uvwarp.c b/source/blender/modifiers/intern/MOD_uvwarp.c deleted file mode 100644 index b72a15ef1ec..00000000000 --- a/source/blender/modifiers/intern/MOD_uvwarp.c +++ /dev/null @@ -1,339 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -/** \file - * \ingroup modifiers - */ - -#include - -#include "BLI_utildefines.h" - -#include "BLI_math.h" -#include "BLI_task.h" - -#include "BLT_translation.h" - -#include "DNA_defaults.h" -#include "DNA_mesh_types.h" -#include "DNA_meshdata_types.h" -#include "DNA_object_types.h" -#include "DNA_screen_types.h" - -#include "BKE_action.h" /* BKE_pose_channel_find_name */ -#include "BKE_context.h" -#include "BKE_deform.h" -#include "BKE_lib_query.h" -#include "BKE_mesh.h" -#include "BKE_modifier.h" -#include "BKE_screen.h" - -#include "UI_interface.h" -#include "UI_resources.h" - -#include "RNA_access.h" -#include "RNA_prototypes.h" - -#include "DEG_depsgraph_query.h" - -#include "MOD_ui_common.h" -#include "MOD_util.h" - -static void uv_warp_from_mat4_pair(float uv_dst[2], - const float uv_src[2], - const float warp_mat[4][4]) -{ - float tuv[3] = {0.0f}; - - copy_v2_v2(tuv, uv_src); - mul_m4_v3(warp_mat, tuv); - copy_v2_v2(uv_dst, tuv); -} - -static void initData(ModifierData *md) -{ - UVWarpModifierData *umd = (UVWarpModifierData *)md; - - BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(umd, modifier)); - - MEMCPY_STRUCT_AFTER(umd, DNA_struct_default_get(UVWarpModifierData), modifier); -} - -static void requiredDataMask(ModifierData *md, CustomData_MeshMasks *r_cddata_masks) -{ - UVWarpModifierData *umd = (UVWarpModifierData *)md; - - /* ask for vertexgroups if we need them */ - if (umd->vgroup_name[0] != '\0') { - r_cddata_masks->vmask |= CD_MASK_MDEFORMVERT; - } -} - -static void matrix_from_obj_pchan(float mat[4][4], Object *ob, const char *bonename) -{ - bPoseChannel *pchan = BKE_pose_channel_find_name(ob->pose, bonename); - if (pchan) { - mul_m4_m4m4(mat, ob->obmat, pchan->pose_mat); - } - else { - copy_m4_m4(mat, ob->obmat); - } -} - -typedef struct UVWarpData { - const MPoly *mpoly; - const MLoop *mloop; - MLoopUV *mloopuv; - - const MDeformVert *dvert; - int defgrp_index; - - float (*warp_mat)[4]; - bool invert_vgroup; -} UVWarpData; - -static void uv_warp_compute(void *__restrict userdata, - const int i, - const TaskParallelTLS *__restrict UNUSED(tls)) -{ - const UVWarpData *data = userdata; - - const MPoly *mp = &data->mpoly[i]; - const MLoop *ml = &data->mloop[mp->loopstart]; - MLoopUV *mluv = &data->mloopuv[mp->loopstart]; - - const MDeformVert *dvert = data->dvert; - const int defgrp_index = data->defgrp_index; - - float(*warp_mat)[4] = data->warp_mat; - - int l; - - if (dvert) { - for (l = 0; l < mp->totloop; l++, ml++, mluv++) { - float uv[2]; - const float weight = data->invert_vgroup ? - 1.0f - BKE_defvert_find_weight(&dvert[ml->v], defgrp_index) : - BKE_defvert_find_weight(&dvert[ml->v], defgrp_index); - - uv_warp_from_mat4_pair(uv, mluv->uv, warp_mat); - interp_v2_v2v2(mluv->uv, mluv->uv, uv, weight); - } - } - else { - for (l = 0; l < mp->totloop; l++, ml++, mluv++) { - uv_warp_from_mat4_pair(mluv->uv, mluv->uv, warp_mat); - } - } -} - -static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mesh) -{ - UVWarpModifierData *umd = (UVWarpModifierData *)md; - int polys_num, loops_num; - MLoopUV *mloopuv; - const MDeformVert *dvert; - int defgrp_index; - char uvname[MAX_CUSTOMDATA_LAYER_NAME]; - float warp_mat[4][4]; - const int axis_u = umd->axis_u; - const int axis_v = umd->axis_v; - const bool invert_vgroup = (umd->flag & MOD_UVWARP_INVERT_VGROUP) != 0; - - /* make sure there are UV Maps available */ - if (!CustomData_has_layer(&mesh->ldata, CD_MLOOPUV)) { - return mesh; - } - - if (!ELEM(NULL, umd->object_src, umd->object_dst)) { - float mat_src[4][4]; - float mat_dst[4][4]; - float imat_dst[4][4]; - float shuf_mat[4][4]; - - /* make sure anything moving UVs is available */ - matrix_from_obj_pchan(mat_src, umd->object_src, umd->bone_src); - matrix_from_obj_pchan(mat_dst, umd->object_dst, umd->bone_dst); - - invert_m4_m4(imat_dst, mat_dst); - mul_m4_m4m4(warp_mat, imat_dst, mat_src); - - /* apply warp */ - if (!is_zero_v2(umd->center)) { - float mat_cent[4][4]; - float imat_cent[4][4]; - - unit_m4(mat_cent); - mat_cent[3][axis_u] = umd->center[0]; - mat_cent[3][axis_v] = umd->center[1]; - - invert_m4_m4(imat_cent, mat_cent); - - mul_m4_m4m4(warp_mat, warp_mat, imat_cent); - mul_m4_m4m4(warp_mat, mat_cent, warp_mat); - } - - const int shuf_indices[4] = {axis_u, axis_v, -1, 3}; - shuffle_m4(shuf_mat, shuf_indices); - mul_m4_m4m4(warp_mat, shuf_mat, warp_mat); - transpose_m4(shuf_mat); - mul_m4_m4m4(warp_mat, warp_mat, shuf_mat); - } - else { - unit_m4(warp_mat); - } - - /* Apply direct 2d transform. */ - translate_m4(warp_mat, umd->center[0], umd->center[1], 0.0f); - const float scale[3] = {umd->scale[0], umd->scale[1], 1.0f}; - rescale_m4(warp_mat, scale); - rotate_m4(warp_mat, 'Z', umd->rotation); - translate_m4(warp_mat, umd->offset[0], umd->offset[1], 0.0f); - translate_m4(warp_mat, -umd->center[0], -umd->center[1], 0.0f); - - /* make sure we're using an existing layer */ - CustomData_validate_layer_name(&mesh->ldata, CD_MLOOPUV, umd->uvlayer_name, uvname); - - const MPoly *polys = BKE_mesh_polys(mesh); - const MLoop *loops = BKE_mesh_loops(mesh); - polys_num = mesh->totpoly; - loops_num = mesh->totloop; - - /* make sure we are not modifying the original UV map */ - mloopuv = CustomData_duplicate_referenced_layer_named( - &mesh->ldata, CD_MLOOPUV, uvname, loops_num); - MOD_get_vgroup(ctx->object, mesh, umd->vgroup_name, &dvert, &defgrp_index); - - UVWarpData data = { - .mpoly = polys, - .mloop = loops, - .mloopuv = mloopuv, - .dvert = dvert, - .defgrp_index = defgrp_index, - .warp_mat = warp_mat, - .invert_vgroup = invert_vgroup, - }; - TaskParallelSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = (polys_num > 1000); - BLI_task_parallel_range(0, polys_num, &data, uv_warp_compute, &settings); - - mesh->runtime.is_original_bmesh = false; - - return mesh; -} - -static void foreachIDLink(ModifierData *md, Object *ob, IDWalkFunc walk, void *userData) -{ - UVWarpModifierData *umd = (UVWarpModifierData *)md; - - walk(userData, ob, (ID **)&umd->object_dst, IDWALK_CB_NOP); - walk(userData, ob, (ID **)&umd->object_src, IDWALK_CB_NOP); -} - -static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphContext *ctx) -{ - UVWarpModifierData *umd = (UVWarpModifierData *)md; - - MOD_depsgraph_update_object_bone_relation( - ctx->node, umd->object_src, umd->bone_src, "UVWarp Modifier"); - MOD_depsgraph_update_object_bone_relation( - ctx->node, umd->object_dst, umd->bone_dst, "UVWarp Modifier"); - - DEG_add_depends_on_transform_relation(ctx->node, "UVWarp Modifier"); -} - -static void panel_draw(const bContext *UNUSED(C), Panel *panel) -{ - uiLayout *col; - uiLayout *layout = panel->layout; - - PointerRNA ob_ptr; - PointerRNA *ptr = modifier_panel_get_property_pointers(panel, &ob_ptr); - - PointerRNA warp_obj_ptr; - PointerRNA obj_data_ptr = RNA_pointer_get(&ob_ptr, "data"); - - uiLayoutSetPropSep(layout, true); - - uiItemPointerR(layout, ptr, "uv_layer", &obj_data_ptr, "uv_layers", NULL, ICON_NONE); - - col = uiLayoutColumn(layout, false); - uiItemR(col, ptr, "center", 0, NULL, ICON_NONE); - - col = uiLayoutColumn(layout, false); - uiItemR(col, ptr, "axis_u", 0, IFACE_("Axis U"), ICON_NONE); - uiItemR(col, ptr, "axis_v", 0, IFACE_("V"), ICON_NONE); - - col = uiLayoutColumn(layout, false); - uiItemR(col, ptr, "object_from", 0, NULL, ICON_NONE); - warp_obj_ptr = RNA_pointer_get(ptr, "object_from"); - if (!RNA_pointer_is_null(&warp_obj_ptr) && RNA_enum_get(&warp_obj_ptr, "type") == OB_ARMATURE) { - PointerRNA warp_obj_data_ptr = RNA_pointer_get(&warp_obj_ptr, "data"); - uiItemPointerR(col, ptr, "bone_from", &warp_obj_data_ptr, "bones", NULL, ICON_NONE); - } - - uiItemR(col, ptr, "object_to", 0, IFACE_("To"), ICON_NONE); - warp_obj_ptr = RNA_pointer_get(ptr, "object_to"); - if (!RNA_pointer_is_null(&warp_obj_ptr) && RNA_enum_get(&warp_obj_ptr, "type") == OB_ARMATURE) { - PointerRNA warp_obj_data_ptr = RNA_pointer_get(&warp_obj_ptr, "data"); - uiItemPointerR(col, ptr, "bone_to", &warp_obj_data_ptr, "bones", NULL, ICON_NONE); - } - - modifier_vgroup_ui(layout, ptr, &ob_ptr, "vertex_group", "invert_vertex_group", NULL); - - modifier_panel_end(layout, ptr); -} - -static void transform_panel_draw(const bContext *UNUSED(C), Panel *panel) -{ - uiLayout *layout = panel->layout; - - PointerRNA *ptr = modifier_panel_get_property_pointers(panel, NULL); - - uiLayoutSetPropSep(layout, true); - - uiItemR(layout, ptr, "offset", 0, NULL, ICON_NONE); - uiItemR(layout, ptr, "scale", 0, NULL, ICON_NONE); - uiItemR(layout, ptr, "rotation", 0, NULL, ICON_NONE); -} - -static void panelRegister(ARegionType *region_type) -{ - PanelType *panel_type = modifier_panel_register(region_type, eModifierType_UVWarp, panel_draw); - modifier_subpanel_register( - region_type, "offset", "Transform", NULL, transform_panel_draw, panel_type); -} - -ModifierTypeInfo modifierType_UVWarp = { - /* name */ N_("UVWarp"), - /* structName */ "UVWarpModifierData", - /* structSize */ sizeof(UVWarpModifierData), - /* srna */ &RNA_UVWarpModifier, - /* type */ eModifierTypeType_NonGeometrical, - /* flags */ eModifierTypeFlag_AcceptsMesh | eModifierTypeFlag_SupportsEditmode | - eModifierTypeFlag_EnableInEditmode, - /* icon */ ICON_MOD_UVPROJECT, /* TODO: Use correct icon. */ - - /* copyData */ BKE_modifier_copydata_generic, - - /* deformVerts */ NULL, - /* deformMatrices */ NULL, - /* deformVertsEM */ NULL, - /* deformMatricesEM */ NULL, - /* modifyMesh */ modifyMesh, - /* modifyGeometrySet */ NULL, - - /* initData */ initData, - /* requiredDataMask */ requiredDataMask, - /* freeData */ NULL, - /* isDisabled */ NULL, - /* updateDepsgraph */ updateDepsgraph, - /* dependsOnTime */ NULL, - /* dependsOnNormals */ NULL, - /* foreachIDLink */ foreachIDLink, - /* foreachTexLink */ NULL, - /* freeRuntimeData */ NULL, - /* panelRegister */ panelRegister, - /* blendWrite */ NULL, - /* blendRead */ NULL, -}; diff --git a/source/blender/modifiers/intern/MOD_uvwarp.cc b/source/blender/modifiers/intern/MOD_uvwarp.cc new file mode 100644 index 00000000000..85fd8946d0b --- /dev/null +++ b/source/blender/modifiers/intern/MOD_uvwarp.cc @@ -0,0 +1,338 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup modifiers + */ + +#include + +#include "BLI_utildefines.h" + +#include "BLI_math.h" +#include "BLI_task.h" + +#include "BLT_translation.h" + +#include "DNA_defaults.h" +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_object_types.h" +#include "DNA_screen_types.h" + +#include "BKE_action.h" /* BKE_pose_channel_find_name */ +#include "BKE_context.h" +#include "BKE_deform.h" +#include "BKE_lib_query.h" +#include "BKE_mesh.h" +#include "BKE_modifier.h" +#include "BKE_screen.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "RNA_access.h" +#include "RNA_prototypes.h" + +#include "DEG_depsgraph_query.h" + +#include "MOD_ui_common.h" +#include "MOD_util.h" + +static void uv_warp_from_mat4_pair(float uv_dst[2], + const float uv_src[2], + const float warp_mat[4][4]) +{ + float tuv[3] = {0.0f}; + + copy_v2_v2(tuv, uv_src); + mul_m4_v3(warp_mat, tuv); + copy_v2_v2(uv_dst, tuv); +} + +static void initData(ModifierData *md) +{ + UVWarpModifierData *umd = (UVWarpModifierData *)md; + + BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(umd, modifier)); + + MEMCPY_STRUCT_AFTER(umd, DNA_struct_default_get(UVWarpModifierData), modifier); +} + +static void requiredDataMask(ModifierData *md, CustomData_MeshMasks *r_cddata_masks) +{ + UVWarpModifierData *umd = (UVWarpModifierData *)md; + + /* ask for vertexgroups if we need them */ + if (umd->vgroup_name[0] != '\0') { + r_cddata_masks->vmask |= CD_MASK_MDEFORMVERT; + } +} + +static void matrix_from_obj_pchan(float mat[4][4], Object *ob, const char *bonename) +{ + bPoseChannel *pchan = BKE_pose_channel_find_name(ob->pose, bonename); + if (pchan) { + mul_m4_m4m4(mat, ob->obmat, pchan->pose_mat); + } + else { + copy_m4_m4(mat, ob->obmat); + } +} + +struct UVWarpData { + const MPoly *mpoly; + const MLoop *mloop; + MLoopUV *mloopuv; + + const MDeformVert *dvert; + int defgrp_index; + + float (*warp_mat)[4]; + bool invert_vgroup; +}; + +static void uv_warp_compute(void *__restrict userdata, + const int i, + const TaskParallelTLS *__restrict UNUSED(tls)) +{ + const UVWarpData *data = static_cast(userdata); + + const MPoly *mp = &data->mpoly[i]; + const MLoop *ml = &data->mloop[mp->loopstart]; + MLoopUV *mluv = &data->mloopuv[mp->loopstart]; + + const MDeformVert *dvert = data->dvert; + const int defgrp_index = data->defgrp_index; + + float(*warp_mat)[4] = data->warp_mat; + + int l; + + if (dvert) { + for (l = 0; l < mp->totloop; l++, ml++, mluv++) { + float uv[2]; + const float weight = data->invert_vgroup ? + 1.0f - BKE_defvert_find_weight(&dvert[ml->v], defgrp_index) : + BKE_defvert_find_weight(&dvert[ml->v], defgrp_index); + + uv_warp_from_mat4_pair(uv, mluv->uv, warp_mat); + interp_v2_v2v2(mluv->uv, mluv->uv, uv, weight); + } + } + else { + for (l = 0; l < mp->totloop; l++, ml++, mluv++) { + uv_warp_from_mat4_pair(mluv->uv, mluv->uv, warp_mat); + } + } +} + +static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mesh) +{ + UVWarpModifierData *umd = (UVWarpModifierData *)md; + int polys_num, loops_num; + const MDeformVert *dvert; + int defgrp_index; + char uvname[MAX_CUSTOMDATA_LAYER_NAME]; + float warp_mat[4][4]; + const int axis_u = umd->axis_u; + const int axis_v = umd->axis_v; + const bool invert_vgroup = (umd->flag & MOD_UVWARP_INVERT_VGROUP) != 0; + + /* make sure there are UV Maps available */ + if (!CustomData_has_layer(&mesh->ldata, CD_MLOOPUV)) { + return mesh; + } + + if (!ELEM(nullptr, umd->object_src, umd->object_dst)) { + float mat_src[4][4]; + float mat_dst[4][4]; + float imat_dst[4][4]; + float shuf_mat[4][4]; + + /* make sure anything moving UVs is available */ + matrix_from_obj_pchan(mat_src, umd->object_src, umd->bone_src); + matrix_from_obj_pchan(mat_dst, umd->object_dst, umd->bone_dst); + + invert_m4_m4(imat_dst, mat_dst); + mul_m4_m4m4(warp_mat, imat_dst, mat_src); + + /* apply warp */ + if (!is_zero_v2(umd->center)) { + float mat_cent[4][4]; + float imat_cent[4][4]; + + unit_m4(mat_cent); + mat_cent[3][axis_u] = umd->center[0]; + mat_cent[3][axis_v] = umd->center[1]; + + invert_m4_m4(imat_cent, mat_cent); + + mul_m4_m4m4(warp_mat, warp_mat, imat_cent); + mul_m4_m4m4(warp_mat, mat_cent, warp_mat); + } + + const int shuf_indices[4] = {axis_u, axis_v, -1, 3}; + shuffle_m4(shuf_mat, shuf_indices); + mul_m4_m4m4(warp_mat, shuf_mat, warp_mat); + transpose_m4(shuf_mat); + mul_m4_m4m4(warp_mat, warp_mat, shuf_mat); + } + else { + unit_m4(warp_mat); + } + + /* Apply direct 2d transform. */ + translate_m4(warp_mat, umd->center[0], umd->center[1], 0.0f); + const float scale[3] = {umd->scale[0], umd->scale[1], 1.0f}; + rescale_m4(warp_mat, scale); + rotate_m4(warp_mat, 'Z', umd->rotation); + translate_m4(warp_mat, umd->offset[0], umd->offset[1], 0.0f); + translate_m4(warp_mat, -umd->center[0], -umd->center[1], 0.0f); + + /* make sure we're using an existing layer */ + CustomData_validate_layer_name(&mesh->ldata, CD_MLOOPUV, umd->uvlayer_name, uvname); + + const MPoly *polys = BKE_mesh_polys(mesh); + const MLoop *loops = BKE_mesh_loops(mesh); + polys_num = mesh->totpoly; + loops_num = mesh->totloop; + + /* make sure we are not modifying the original UV map */ + MLoopUV *mloopuv = static_cast( + CustomData_duplicate_referenced_layer_named(&mesh->ldata, CD_MLOOPUV, uvname, loops_num)); + MOD_get_vgroup(ctx->object, mesh, umd->vgroup_name, &dvert, &defgrp_index); + + UVWarpData data{}; + data.mpoly = polys; + data.mloop = loops; + data.mloopuv = mloopuv; + data.dvert = dvert; + data.defgrp_index = defgrp_index; + data.warp_mat = warp_mat; + data.invert_vgroup = invert_vgroup; + + TaskParallelSettings settings; + BLI_parallel_range_settings_defaults(&settings); + settings.use_threading = (polys_num > 1000); + BLI_task_parallel_range(0, polys_num, &data, uv_warp_compute, &settings); + + mesh->runtime.is_original_bmesh = false; + + return mesh; +} + +static void foreachIDLink(ModifierData *md, Object *ob, IDWalkFunc walk, void *userData) +{ + UVWarpModifierData *umd = (UVWarpModifierData *)md; + + walk(userData, ob, (ID **)&umd->object_dst, IDWALK_CB_NOP); + walk(userData, ob, (ID **)&umd->object_src, IDWALK_CB_NOP); +} + +static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphContext *ctx) +{ + UVWarpModifierData *umd = (UVWarpModifierData *)md; + + MOD_depsgraph_update_object_bone_relation( + ctx->node, umd->object_src, umd->bone_src, "UVWarp Modifier"); + MOD_depsgraph_update_object_bone_relation( + ctx->node, umd->object_dst, umd->bone_dst, "UVWarp Modifier"); + + DEG_add_depends_on_transform_relation(ctx->node, "UVWarp Modifier"); +} + +static void panel_draw(const bContext *UNUSED(C), Panel *panel) +{ + uiLayout *col; + uiLayout *layout = panel->layout; + + PointerRNA ob_ptr; + PointerRNA *ptr = modifier_panel_get_property_pointers(panel, &ob_ptr); + + PointerRNA warp_obj_ptr; + PointerRNA obj_data_ptr = RNA_pointer_get(&ob_ptr, "data"); + + uiLayoutSetPropSep(layout, true); + + uiItemPointerR(layout, ptr, "uv_layer", &obj_data_ptr, "uv_layers", nullptr, ICON_NONE); + + col = uiLayoutColumn(layout, false); + uiItemR(col, ptr, "center", 0, nullptr, ICON_NONE); + + col = uiLayoutColumn(layout, false); + uiItemR(col, ptr, "axis_u", 0, IFACE_("Axis U"), ICON_NONE); + uiItemR(col, ptr, "axis_v", 0, IFACE_("V"), ICON_NONE); + + col = uiLayoutColumn(layout, false); + uiItemR(col, ptr, "object_from", 0, nullptr, ICON_NONE); + warp_obj_ptr = RNA_pointer_get(ptr, "object_from"); + if (!RNA_pointer_is_null(&warp_obj_ptr) && RNA_enum_get(&warp_obj_ptr, "type") == OB_ARMATURE) { + PointerRNA warp_obj_data_ptr = RNA_pointer_get(&warp_obj_ptr, "data"); + uiItemPointerR(col, ptr, "bone_from", &warp_obj_data_ptr, "bones", nullptr, ICON_NONE); + } + + uiItemR(col, ptr, "object_to", 0, IFACE_("To"), ICON_NONE); + warp_obj_ptr = RNA_pointer_get(ptr, "object_to"); + if (!RNA_pointer_is_null(&warp_obj_ptr) && RNA_enum_get(&warp_obj_ptr, "type") == OB_ARMATURE) { + PointerRNA warp_obj_data_ptr = RNA_pointer_get(&warp_obj_ptr, "data"); + uiItemPointerR(col, ptr, "bone_to", &warp_obj_data_ptr, "bones", nullptr, ICON_NONE); + } + + modifier_vgroup_ui(layout, ptr, &ob_ptr, "vertex_group", "invert_vertex_group", nullptr); + + modifier_panel_end(layout, ptr); +} + +static void transform_panel_draw(const bContext *UNUSED(C), Panel *panel) +{ + uiLayout *layout = panel->layout; + + PointerRNA *ptr = modifier_panel_get_property_pointers(panel, nullptr); + + uiLayoutSetPropSep(layout, true); + + uiItemR(layout, ptr, "offset", 0, nullptr, ICON_NONE); + uiItemR(layout, ptr, "scale", 0, nullptr, ICON_NONE); + uiItemR(layout, ptr, "rotation", 0, nullptr, ICON_NONE); +} + +static void panelRegister(ARegionType *region_type) +{ + PanelType *panel_type = modifier_panel_register(region_type, eModifierType_UVWarp, panel_draw); + modifier_subpanel_register( + region_type, "offset", "Transform", nullptr, transform_panel_draw, panel_type); +} + +ModifierTypeInfo modifierType_UVWarp = { + /* name */ N_("UVWarp"), + /* structName */ "UVWarpModifierData", + /* structSize */ sizeof(UVWarpModifierData), + /* srna */ &RNA_UVWarpModifier, + /* type */ eModifierTypeType_NonGeometrical, + /* flags */ eModifierTypeFlag_AcceptsMesh | eModifierTypeFlag_SupportsEditmode | + eModifierTypeFlag_EnableInEditmode, + /* icon */ ICON_MOD_UVPROJECT, /* TODO: Use correct icon. */ + + /* copyData */ BKE_modifier_copydata_generic, + + /* deformVerts */ nullptr, + /* deformMatrices */ nullptr, + /* deformVertsEM */ nullptr, + /* deformMatricesEM */ nullptr, + /* modifyMesh */ modifyMesh, + /* modifyGeometrySet */ nullptr, + + /* initData */ initData, + /* requiredDataMask */ requiredDataMask, + /* freeData */ nullptr, + /* isDisabled */ nullptr, + /* updateDepsgraph */ updateDepsgraph, + /* dependsOnTime */ nullptr, + /* dependsOnNormals */ nullptr, + /* foreachIDLink */ foreachIDLink, + /* foreachTexLink */ nullptr, + /* freeRuntimeData */ nullptr, + /* panelRegister */ panelRegister, + /* blendWrite */ nullptr, + /* blendRead */ nullptr, +}; diff --git a/source/blender/modifiers/intern/MOD_weighted_normal.c b/source/blender/modifiers/intern/MOD_weighted_normal.c deleted file mode 100644 index 69210c85862..00000000000 --- a/source/blender/modifiers/intern/MOD_weighted_normal.c +++ /dev/null @@ -1,758 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -/** \file - * \ingroup modifiers - */ - -#include "MEM_guardedalloc.h" - -#include "BLI_bitmap.h" -#include "BLI_linklist.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_object_types.h" -#include "DNA_scene_types.h" -#include "DNA_screen_types.h" - -#include "BKE_context.h" -#include "BKE_deform.h" -#include "BKE_lib_id.h" -#include "BKE_mesh.h" -#include "BKE_screen.h" - -#include "UI_interface.h" -#include "UI_resources.h" - -#include "RNA_access.h" -#include "RNA_prototypes.h" - -#include "MOD_modifiertypes.h" -#include "MOD_ui_common.h" -#include "MOD_util.h" - -#include "bmesh.h" - -#define CLNORS_VALID_VEC_LEN (1e-6f) - -typedef struct ModePair { - float val; /* Contains mode based value (face area / corner angle). */ - int index; /* Index value per poly or per loop. */ -} ModePair; - -/* Sorting function used in modifier, sorts in decreasing order. */ -static int modepair_cmp_by_val_inverse(const void *p1, const void *p2) -{ - ModePair *r1 = (ModePair *)p1; - ModePair *r2 = (ModePair *)p2; - - return (r1->val < r2->val) ? 1 : ((r1->val > r2->val) ? -1 : 0); -} - -/* There will be one of those per vertex - * (simple case, computing one normal per vertex), or per smooth fan. */ -typedef struct WeightedNormalDataAggregateItem { - float normal[3]; - - int loops_num; /* Count number of loops using this item so far. */ - float curr_val; /* Current max val for this item. */ - int curr_strength; /* Current max strength encountered for this item. */ -} WeightedNormalDataAggregateItem; - -#define NUM_CACHED_INVERSE_POWERS_OF_WEIGHT 128 - -typedef struct WeightedNormalData { - const int verts_num; - const int edges_num; - const int loops_num; - const int polys_num; - - const MVert *mvert; - const float (*vert_normals)[3]; - MEdge *medge; - - const MLoop *mloop; - short (*clnors)[2]; - const bool has_clnors; /* True if clnors already existed, false if we had to create them. */ - const float split_angle; - - const MPoly *mpoly; - const float (*polynors)[3]; - const int *poly_strength; - - const MDeformVert *dvert; - const int defgrp_index; - const bool use_invert_vgroup; - - const float weight; - const short mode; - - /* Lower-level, internal processing data. */ - float cached_inverse_powers_of_weight[NUM_CACHED_INVERSE_POWERS_OF_WEIGHT]; - - WeightedNormalDataAggregateItem *items_data; - - ModePair *mode_pair; - - int *loop_to_poly; -} WeightedNormalData; - -/** - * Check strength of given poly compared to those found so far for that given item - * (vertex or smooth fan), and reset matching item_data in case we get a stronger new strength. - */ -static bool check_item_poly_strength(WeightedNormalData *wn_data, - WeightedNormalDataAggregateItem *item_data, - const int mp_index) -{ - BLI_assert(wn_data->poly_strength != NULL); - - const int mp_strength = wn_data->poly_strength[mp_index]; - - if (mp_strength > item_data->curr_strength) { - item_data->curr_strength = mp_strength; - item_data->curr_val = 0.0f; - item_data->loops_num = 0; - zero_v3(item_data->normal); - } - - return mp_strength == item_data->curr_strength; -} - -static void aggregate_item_normal(WeightedNormalModifierData *wnmd, - WeightedNormalData *wn_data, - WeightedNormalDataAggregateItem *item_data, - const int mv_index, - const int mp_index, - const float curr_val, - const bool use_face_influence) -{ - const float(*polynors)[3] = wn_data->polynors; - - const MDeformVert *dvert = wn_data->dvert; - const int defgrp_index = wn_data->defgrp_index; - const bool use_invert_vgroup = wn_data->use_invert_vgroup; - - const float weight = wn_data->weight; - - float *cached_inverse_powers_of_weight = wn_data->cached_inverse_powers_of_weight; - - const bool has_vgroup = dvert != NULL; - const bool vert_of_group = has_vgroup && - BKE_defvert_find_index(&dvert[mv_index], defgrp_index) != NULL; - - if (has_vgroup && - ((vert_of_group && use_invert_vgroup) || (!vert_of_group && !use_invert_vgroup))) { - return; - } - - if (use_face_influence && !check_item_poly_strength(wn_data, item_data, mp_index)) { - return; - } - - /* If item's curr_val is 0 init it to present value. */ - if (item_data->curr_val == 0.0f) { - item_data->curr_val = curr_val; - } - if (!compare_ff(item_data->curr_val, curr_val, wnmd->thresh)) { - /* item's curr_val and present value differ more than threshold, update. */ - item_data->loops_num++; - item_data->curr_val = curr_val; - } - - /* Exponentially divided weight for each normal - * (since a few values will be used by most cases, we cache those). */ - const int loops_num = item_data->loops_num; - if (loops_num < NUM_CACHED_INVERSE_POWERS_OF_WEIGHT && - cached_inverse_powers_of_weight[loops_num] == 0.0f) { - cached_inverse_powers_of_weight[loops_num] = 1.0f / powf(weight, loops_num); - } - const float inverted_n_weight = loops_num < NUM_CACHED_INVERSE_POWERS_OF_WEIGHT ? - cached_inverse_powers_of_weight[loops_num] : - 1.0f / powf(weight, loops_num); - - madd_v3_v3fl(item_data->normal, polynors[mp_index], curr_val * inverted_n_weight); -} - -static void apply_weights_vertex_normal(WeightedNormalModifierData *wnmd, - WeightedNormalData *wn_data) -{ - const int verts_num = wn_data->verts_num; - const int edges_num = wn_data->edges_num; - const int loops_num = wn_data->loops_num; - const int polys_num = wn_data->polys_num; - - const MVert *mvert = wn_data->mvert; - MEdge *medge = wn_data->medge; - - const MLoop *mloop = wn_data->mloop; - short(*clnors)[2] = wn_data->clnors; - int *loop_to_poly = wn_data->loop_to_poly; - - const MPoly *mpoly = wn_data->mpoly; - const float(*polynors)[3] = wn_data->polynors; - const int *poly_strength = wn_data->poly_strength; - - const MDeformVert *dvert = wn_data->dvert; - - const short mode = wn_data->mode; - ModePair *mode_pair = wn_data->mode_pair; - - const bool has_clnors = wn_data->has_clnors; - const float split_angle = wn_data->split_angle; - MLoopNorSpaceArray lnors_spacearr = {NULL}; - - const bool keep_sharp = (wnmd->flag & MOD_WEIGHTEDNORMAL_KEEP_SHARP) != 0; - const bool use_face_influence = (wnmd->flag & MOD_WEIGHTEDNORMAL_FACE_INFLUENCE) != 0 && - poly_strength != NULL; - const bool has_vgroup = dvert != NULL; - - float(*loop_normals)[3] = NULL; - - WeightedNormalDataAggregateItem *items_data = NULL; - int items_num = 0; - if (keep_sharp) { - BLI_bitmap *done_loops = BLI_BITMAP_NEW(loops_num, __func__); - - /* This will give us loop normal spaces, - * we do not actually care about computed loop_normals for now... */ - loop_normals = MEM_calloc_arrayN((size_t)loops_num, sizeof(*loop_normals), __func__); - BKE_mesh_normals_loop_split(mvert, - wn_data->vert_normals, - verts_num, - medge, - edges_num, - mloop, - loop_normals, - loops_num, - mpoly, - polynors, - polys_num, - true, - split_angle, - &lnors_spacearr, - has_clnors ? clnors : NULL, - loop_to_poly); - - items_num = lnors_spacearr.spaces_num; - items_data = MEM_calloc_arrayN((size_t)items_num, sizeof(*items_data), __func__); - - /* In this first loop, we assign each WeightedNormalDataAggregateItem - * to its smooth fan of loops (aka lnor space). */ - const MPoly *mp; - int mp_index; - int item_index; - for (mp = mpoly, mp_index = 0, item_index = 0; mp_index < polys_num; mp++, mp_index++) { - int ml_index = mp->loopstart; - const int ml_end_index = ml_index + mp->totloop; - - for (; ml_index < ml_end_index; ml_index++) { - if (BLI_BITMAP_TEST(done_loops, ml_index)) { - /* Smooth fan of this loop has already been processed, skip it. */ - continue; - } - BLI_assert(item_index < items_num); - - WeightedNormalDataAggregateItem *itdt = &items_data[item_index]; - itdt->curr_strength = FACE_STRENGTH_WEAK; - - MLoopNorSpace *lnor_space = lnors_spacearr.lspacearr[ml_index]; - lnor_space->user_data = itdt; - - if (!(lnor_space->flags & MLNOR_SPACE_IS_SINGLE)) { - for (LinkNode *lnode = lnor_space->loops; lnode; lnode = lnode->next) { - const int ml_fan_index = POINTER_AS_INT(lnode->link); - BLI_BITMAP_ENABLE(done_loops, ml_fan_index); - } - } - else { - BLI_BITMAP_ENABLE(done_loops, ml_index); - } - - item_index++; - } - } - - MEM_freeN(done_loops); - } - else { - items_num = verts_num; - items_data = MEM_calloc_arrayN((size_t)items_num, sizeof(*items_data), __func__); - if (use_face_influence) { - for (int item_index = 0; item_index < items_num; item_index++) { - items_data[item_index].curr_strength = FACE_STRENGTH_WEAK; - } - } - } - wn_data->items_data = items_data; - - switch (mode) { - case MOD_WEIGHTEDNORMAL_MODE_FACE: - for (int i = 0; i < polys_num; i++) { - const int mp_index = mode_pair[i].index; - const float mp_val = mode_pair[i].val; - - int ml_index = mpoly[mp_index].loopstart; - const int ml_index_end = ml_index + mpoly[mp_index].totloop; - for (; ml_index < ml_index_end; ml_index++) { - const int mv_index = mloop[ml_index].v; - WeightedNormalDataAggregateItem *item_data = - keep_sharp ? lnors_spacearr.lspacearr[ml_index]->user_data : &items_data[mv_index]; - - aggregate_item_normal( - wnmd, wn_data, item_data, mv_index, mp_index, mp_val, use_face_influence); - } - } - break; - case MOD_WEIGHTEDNORMAL_MODE_ANGLE: - case MOD_WEIGHTEDNORMAL_MODE_FACE_ANGLE: - BLI_assert(loop_to_poly != NULL); - - for (int i = 0; i < loops_num; i++) { - const int ml_index = mode_pair[i].index; - const float ml_val = mode_pair[i].val; - - const int mp_index = loop_to_poly[ml_index]; - const int mv_index = mloop[ml_index].v; - WeightedNormalDataAggregateItem *item_data = - keep_sharp ? lnors_spacearr.lspacearr[ml_index]->user_data : &items_data[mv_index]; - - aggregate_item_normal( - wnmd, wn_data, item_data, mv_index, mp_index, ml_val, use_face_influence); - } - break; - default: - BLI_assert_unreachable(); - } - - /* Validate computed weighted normals. */ - for (int item_index = 0; item_index < items_num; item_index++) { - if (normalize_v3(items_data[item_index].normal) < CLNORS_VALID_VEC_LEN) { - zero_v3(items_data[item_index].normal); - } - } - - if (keep_sharp) { - /* Set loop normals for normal computed for each lnor space (smooth fan). - * Note that loop_normals is already populated with clnors - * (before this modifier is applied, at start of this function), - * so no need to recompute them here. */ - for (int ml_index = 0; ml_index < loops_num; ml_index++) { - WeightedNormalDataAggregateItem *item_data = lnors_spacearr.lspacearr[ml_index]->user_data; - if (!is_zero_v3(item_data->normal)) { - copy_v3_v3(loop_normals[ml_index], item_data->normal); - } - } - - BKE_mesh_normals_loop_custom_set(mvert, - wn_data->vert_normals, - verts_num, - medge, - edges_num, - mloop, - loop_normals, - loops_num, - mpoly, - polynors, - polys_num, - clnors); - } - else { - /* TODO: Ideally, we could add an option to `BKE_mesh_normals_loop_custom_[from_verts_]set()` - * to keep current clnors instead of resetting them to default auto-computed ones, - * when given new custom normal is zero-vec. - * But this is not exactly trivial change, better to keep this optimization for later... - */ - if (!has_vgroup) { - /* NOTE: in theory, we could avoid this extra allocation & copying... - * But think we can live with it for now, - * and it makes code simpler & cleaner. */ - float(*vert_normals)[3] = MEM_calloc_arrayN( - (size_t)verts_num, sizeof(*loop_normals), __func__); - - for (int ml_index = 0; ml_index < loops_num; ml_index++) { - const int mv_index = mloop[ml_index].v; - copy_v3_v3(vert_normals[mv_index], items_data[mv_index].normal); - } - - BKE_mesh_normals_loop_custom_from_verts_set(mvert, - wn_data->vert_normals, - vert_normals, - verts_num, - medge, - edges_num, - mloop, - loops_num, - mpoly, - polynors, - polys_num, - clnors); - - MEM_freeN(vert_normals); - } - else { - loop_normals = MEM_calloc_arrayN((size_t)loops_num, sizeof(*loop_normals), __func__); - - BKE_mesh_normals_loop_split(mvert, - wn_data->vert_normals, - verts_num, - medge, - edges_num, - mloop, - loop_normals, - loops_num, - mpoly, - polynors, - polys_num, - true, - split_angle, - NULL, - has_clnors ? clnors : NULL, - loop_to_poly); - - for (int ml_index = 0; ml_index < loops_num; ml_index++) { - const int item_index = mloop[ml_index].v; - if (!is_zero_v3(items_data[item_index].normal)) { - copy_v3_v3(loop_normals[ml_index], items_data[item_index].normal); - } - } - - BKE_mesh_normals_loop_custom_set(mvert, - wn_data->vert_normals, - verts_num, - medge, - edges_num, - mloop, - loop_normals, - loops_num, - mpoly, - polynors, - polys_num, - clnors); - } - } - - if (keep_sharp) { - BKE_lnor_spacearr_free(&lnors_spacearr); - } - MEM_SAFE_FREE(loop_normals); -} - -static void wn_face_area(WeightedNormalModifierData *wnmd, WeightedNormalData *wn_data) -{ - const int polys_num = wn_data->polys_num; - - const MVert *mvert = wn_data->mvert; - const MLoop *mloop = wn_data->mloop; - const MPoly *mpoly = wn_data->mpoly; - - const MPoly *mp; - int mp_index; - - ModePair *face_area = MEM_malloc_arrayN((size_t)polys_num, sizeof(*face_area), __func__); - - ModePair *f_area = face_area; - for (mp_index = 0, mp = mpoly; mp_index < polys_num; mp_index++, mp++, f_area++) { - f_area->val = BKE_mesh_calc_poly_area(mp, &mloop[mp->loopstart], mvert); - f_area->index = mp_index; - } - - qsort(face_area, polys_num, sizeof(*face_area), modepair_cmp_by_val_inverse); - - wn_data->mode_pair = face_area; - apply_weights_vertex_normal(wnmd, wn_data); -} - -static void wn_corner_angle(WeightedNormalModifierData *wnmd, WeightedNormalData *wn_data) -{ - const int loops_num = wn_data->loops_num; - const int polys_num = wn_data->polys_num; - - const MVert *mvert = wn_data->mvert; - const MLoop *mloop = wn_data->mloop; - const MPoly *mpoly = wn_data->mpoly; - - const MPoly *mp; - int mp_index; - - int *loop_to_poly = MEM_malloc_arrayN((size_t)loops_num, sizeof(*loop_to_poly), __func__); - - ModePair *corner_angle = MEM_malloc_arrayN((size_t)loops_num, sizeof(*corner_angle), __func__); - - for (mp_index = 0, mp = mpoly; mp_index < polys_num; mp_index++, mp++) { - const MLoop *ml_start = &mloop[mp->loopstart]; - - float *index_angle = MEM_malloc_arrayN((size_t)mp->totloop, sizeof(*index_angle), __func__); - BKE_mesh_calc_poly_angles(mp, ml_start, mvert, index_angle); - - ModePair *c_angl = &corner_angle[mp->loopstart]; - float *angl = index_angle; - for (int ml_index = mp->loopstart; ml_index < mp->loopstart + mp->totloop; - ml_index++, c_angl++, angl++) { - c_angl->val = (float)M_PI - *angl; - c_angl->index = ml_index; - - loop_to_poly[ml_index] = mp_index; - } - MEM_freeN(index_angle); - } - - qsort(corner_angle, loops_num, sizeof(*corner_angle), modepair_cmp_by_val_inverse); - - wn_data->loop_to_poly = loop_to_poly; - wn_data->mode_pair = corner_angle; - apply_weights_vertex_normal(wnmd, wn_data); -} - -static void wn_face_with_angle(WeightedNormalModifierData *wnmd, WeightedNormalData *wn_data) -{ - const int loops_num = wn_data->loops_num; - const int polys_num = wn_data->polys_num; - - const MVert *mvert = wn_data->mvert; - const MLoop *mloop = wn_data->mloop; - const MPoly *mpoly = wn_data->mpoly; - - const MPoly *mp; - int mp_index; - - int *loop_to_poly = MEM_malloc_arrayN((size_t)loops_num, sizeof(*loop_to_poly), __func__); - - ModePair *combined = MEM_malloc_arrayN((size_t)loops_num, sizeof(*combined), __func__); - - for (mp_index = 0, mp = mpoly; mp_index < polys_num; mp_index++, mp++) { - const MLoop *ml_start = &mloop[mp->loopstart]; - - float face_area = BKE_mesh_calc_poly_area(mp, ml_start, mvert); - float *index_angle = MEM_malloc_arrayN((size_t)mp->totloop, sizeof(*index_angle), __func__); - BKE_mesh_calc_poly_angles(mp, ml_start, mvert, index_angle); - - ModePair *cmbnd = &combined[mp->loopstart]; - float *angl = index_angle; - for (int ml_index = mp->loopstart; ml_index < mp->loopstart + mp->totloop; - ml_index++, cmbnd++, angl++) { - /* In this case val is product of corner angle and face area. */ - cmbnd->val = ((float)M_PI - *angl) * face_area; - cmbnd->index = ml_index; - - loop_to_poly[ml_index] = mp_index; - } - MEM_freeN(index_angle); - } - - qsort(combined, loops_num, sizeof(*combined), modepair_cmp_by_val_inverse); - - wn_data->loop_to_poly = loop_to_poly; - wn_data->mode_pair = combined; - apply_weights_vertex_normal(wnmd, wn_data); -} - -static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mesh) -{ - WeightedNormalModifierData *wnmd = (WeightedNormalModifierData *)md; - Object *ob = ctx->object; - - /* XXX TODO(Rohan Rathi): - * Once we fully switch to Mesh evaluation of modifiers, - * we can expect to get that flag from the COW copy. - * But for now, it is lost in the DM intermediate step, - * so we need to directly check orig object's data. */ -#if 0 - if (!(mesh->flag & ME_AUTOSMOOTH)) -#else - if (!(((Mesh *)ob->data)->flag & ME_AUTOSMOOTH)) -#endif - { - BKE_modifier_set_error( - ctx->object, (ModifierData *)wnmd, "Enable 'Auto Smooth' in Object Data Properties"); - return mesh; - } - - Mesh *result; - result = (Mesh *)BKE_id_copy_ex(NULL, &mesh->id, NULL, LIB_ID_COPY_LOCALIZE); - - const int verts_num = result->totvert; - const int edges_num = result->totedge; - const int loops_num = result->totloop; - const int polys_num = result->totpoly; - const MVert *mvert = BKE_mesh_verts(result); - MEdge *medge = BKE_mesh_edges_for_write(result); - const MPoly *mpoly = BKE_mesh_polys(result); - const MLoop *mloop = BKE_mesh_loops(result); - - /* Right now: - * If weight = 50 then all faces are given equal weight. - * If weight > 50 then more weight given to faces with larger vals (face area / corner angle). - * If weight < 50 then more weight given to faces with lesser vals. However current calculation - * does not converge to min/max. - */ - float weight = ((float)wnmd->weight) / 50.0f; - if (wnmd->weight == 100) { - weight = (float)SHRT_MAX; - } - else if (wnmd->weight == 1) { - weight = 1 / (float)SHRT_MAX; - } - else if ((weight - 1) * 25 > 1) { - weight = (weight - 1) * 25; - } - - const float split_angle = mesh->smoothresh; - short(*clnors)[2] = CustomData_get_layer(&result->ldata, CD_CUSTOMLOOPNORMAL); - - /* Keep info whether we had clnors, - * it helps when generating clnor spaces and default normals. */ - const bool has_clnors = clnors != NULL; - if (!clnors) { - clnors = CustomData_add_layer( - &result->ldata, CD_CUSTOMLOOPNORMAL, CD_SET_DEFAULT, NULL, loops_num); - } - - const MDeformVert *dvert; - int defgrp_index; - MOD_get_vgroup(ctx->object, mesh, wnmd->defgrp_name, &dvert, &defgrp_index); - - WeightedNormalData wn_data = { - .verts_num = verts_num, - .edges_num = edges_num, - .loops_num = loops_num, - .polys_num = polys_num, - - .mvert = mvert, - .vert_normals = BKE_mesh_vertex_normals_ensure(result), - .medge = medge, - - .mloop = mloop, - .clnors = clnors, - .has_clnors = has_clnors, - .split_angle = split_angle, - - .mpoly = mpoly, - .polynors = BKE_mesh_poly_normals_ensure(mesh), - .poly_strength = CustomData_get_layer_named( - &result->pdata, CD_PROP_INT32, MOD_WEIGHTEDNORMALS_FACEWEIGHT_CDLAYER_ID), - - .dvert = dvert, - .defgrp_index = defgrp_index, - .use_invert_vgroup = (wnmd->flag & MOD_WEIGHTEDNORMAL_INVERT_VGROUP) != 0, - - .weight = weight, - .mode = wnmd->mode, - }; - - switch (wnmd->mode) { - case MOD_WEIGHTEDNORMAL_MODE_FACE: - wn_face_area(wnmd, &wn_data); - break; - case MOD_WEIGHTEDNORMAL_MODE_ANGLE: - wn_corner_angle(wnmd, &wn_data); - break; - case MOD_WEIGHTEDNORMAL_MODE_FACE_ANGLE: - wn_face_with_angle(wnmd, &wn_data); - break; - } - - MEM_SAFE_FREE(wn_data.loop_to_poly); - MEM_SAFE_FREE(wn_data.mode_pair); - MEM_SAFE_FREE(wn_data.items_data); - - result->runtime.is_original_bmesh = false; - - return result; -} - -static void initData(ModifierData *md) -{ - WeightedNormalModifierData *wnmd = (WeightedNormalModifierData *)md; - - BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(wnmd, modifier)); - - MEMCPY_STRUCT_AFTER(wnmd, DNA_struct_default_get(WeightedNormalModifierData), modifier); -} - -static void requiredDataMask(ModifierData *md, CustomData_MeshMasks *r_cddata_masks) -{ - WeightedNormalModifierData *wnmd = (WeightedNormalModifierData *)md; - - r_cddata_masks->lmask = CD_MASK_CUSTOMLOOPNORMAL; - - if (wnmd->defgrp_name[0] != '\0') { - r_cddata_masks->vmask |= CD_MASK_MDEFORMVERT; - } - - if (wnmd->flag & MOD_WEIGHTEDNORMAL_FACE_INFLUENCE) { - r_cddata_masks->pmask |= CD_MASK_PROP_INT32; - } -} - -static bool dependsOnNormals(ModifierData *UNUSED(md)) -{ - return true; -} - -static void panel_draw(const bContext *UNUSED(C), Panel *panel) -{ - uiLayout *col; - uiLayout *layout = panel->layout; - - PointerRNA ob_ptr; - PointerRNA *ptr = modifier_panel_get_property_pointers(panel, &ob_ptr); - - uiLayoutSetPropSep(layout, true); - - uiItemR(layout, ptr, "mode", 0, NULL, ICON_NONE); - - uiItemR(layout, ptr, "weight", 0, IFACE_("Weight"), ICON_NONE); - uiItemR(layout, ptr, "thresh", 0, IFACE_("Threshold"), ICON_NONE); - - col = uiLayoutColumn(layout, false); - uiItemR(col, ptr, "keep_sharp", 0, NULL, ICON_NONE); - uiItemR(col, ptr, "use_face_influence", 0, NULL, ICON_NONE); - - modifier_vgroup_ui(layout, ptr, &ob_ptr, "vertex_group", "invert_vertex_group", NULL); - - modifier_panel_end(layout, ptr); -} - -static void panelRegister(ARegionType *region_type) -{ - modifier_panel_register(region_type, eModifierType_WeightedNormal, panel_draw); -} - -ModifierTypeInfo modifierType_WeightedNormal = { - /* name */ N_("WeightedNormal"), - /* structName */ "WeightedNormalModifierData", - /* structSize */ sizeof(WeightedNormalModifierData), - /* srna */ &RNA_WeightedNormalModifier, - /* type */ eModifierTypeType_Constructive, - /* flags */ eModifierTypeFlag_AcceptsMesh | eModifierTypeFlag_SupportsMapping | - eModifierTypeFlag_SupportsEditmode | eModifierTypeFlag_EnableInEditmode, - /* icon */ ICON_MOD_VERTEX_WEIGHT, - - /* copyData */ BKE_modifier_copydata_generic, - - /* deformVerts */ NULL, - /* deformMatrices */ NULL, - /* deformVertsEM */ NULL, - /* deformMatricesEM */ NULL, - /* modifyMesh */ modifyMesh, - /* modifyGeometrySet */ NULL, - - /* initData */ initData, - /* requiredDataMask */ requiredDataMask, - /* freeData */ NULL, - /* isDisabled */ NULL, - /* updateDepsgraph */ NULL, - /* dependsOnTime */ NULL, - /* dependsOnNormals */ dependsOnNormals, - /* foreachIDLink */ NULL, - /* foreachTexLink */ NULL, - /* freeRuntimeData */ NULL, - /* panelRegister */ panelRegister, - /* blendWrite */ NULL, - /* blendRead */ NULL, -}; diff --git a/source/blender/modifiers/intern/MOD_weighted_normal.cc b/source/blender/modifiers/intern/MOD_weighted_normal.cc new file mode 100644 index 00000000000..cb81781479a --- /dev/null +++ b/source/blender/modifiers/intern/MOD_weighted_normal.cc @@ -0,0 +1,774 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup modifiers + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_bitmap.h" +#include "BLI_linklist.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_object_types.h" +#include "DNA_scene_types.h" +#include "DNA_screen_types.h" + +#include "BKE_context.h" +#include "BKE_deform.h" +#include "BKE_lib_id.h" +#include "BKE_mesh.h" +#include "BKE_screen.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "RNA_access.h" +#include "RNA_prototypes.h" + +#include "MOD_modifiertypes.h" +#include "MOD_ui_common.h" +#include "MOD_util.h" + +#include "bmesh.h" + +#define CLNORS_VALID_VEC_LEN (1e-6f) + +struct ModePair { + float val; /* Contains mode based value (face area / corner angle). */ + int index; /* Index value per poly or per loop. */ +}; + +/* Sorting function used in modifier, sorts in decreasing order. */ +static int modepair_cmp_by_val_inverse(const void *p1, const void *p2) +{ + ModePair *r1 = (ModePair *)p1; + ModePair *r2 = (ModePair *)p2; + + return (r1->val < r2->val) ? 1 : ((r1->val > r2->val) ? -1 : 0); +} + +/* There will be one of those per vertex + * (simple case, computing one normal per vertex), or per smooth fan. */ +struct WeightedNormalDataAggregateItem { + float normal[3]; + + int loops_num; /* Count number of loops using this item so far. */ + float curr_val; /* Current max val for this item. */ + int curr_strength; /* Current max strength encountered for this item. */ +}; + +#define NUM_CACHED_INVERSE_POWERS_OF_WEIGHT 128 + +struct WeightedNormalData { + int verts_num; + int edges_num; + int loops_num; + int polys_num; + + const MVert *mvert; + const float (*vert_normals)[3]; + MEdge *medge; + + const MLoop *mloop; + short (*clnors)[2]; + bool has_clnors; /* True if clnors already existed, false if we had to create them. */ + float split_angle; + + const MPoly *mpoly; + const float (*polynors)[3]; + const int *poly_strength; + + const MDeformVert *dvert; + int defgrp_index; + bool use_invert_vgroup; + + float weight; + short mode; + + /* Lower-level, internal processing data. */ + float cached_inverse_powers_of_weight[NUM_CACHED_INVERSE_POWERS_OF_WEIGHT]; + + WeightedNormalDataAggregateItem *items_data; + + ModePair *mode_pair; + + int *loop_to_poly; +}; + +/** + * Check strength of given poly compared to those found so far for that given item + * (vertex or smooth fan), and reset matching item_data in case we get a stronger new strength. + */ +static bool check_item_poly_strength(WeightedNormalData *wn_data, + WeightedNormalDataAggregateItem *item_data, + const int mp_index) +{ + BLI_assert(wn_data->poly_strength != nullptr); + + const int mp_strength = wn_data->poly_strength[mp_index]; + + if (mp_strength > item_data->curr_strength) { + item_data->curr_strength = mp_strength; + item_data->curr_val = 0.0f; + item_data->loops_num = 0; + zero_v3(item_data->normal); + } + + return mp_strength == item_data->curr_strength; +} + +static void aggregate_item_normal(WeightedNormalModifierData *wnmd, + WeightedNormalData *wn_data, + WeightedNormalDataAggregateItem *item_data, + const int mv_index, + const int mp_index, + const float curr_val, + const bool use_face_influence) +{ + const float(*polynors)[3] = wn_data->polynors; + + const MDeformVert *dvert = wn_data->dvert; + const int defgrp_index = wn_data->defgrp_index; + const bool use_invert_vgroup = wn_data->use_invert_vgroup; + + const float weight = wn_data->weight; + + float *cached_inverse_powers_of_weight = wn_data->cached_inverse_powers_of_weight; + + const bool has_vgroup = dvert != nullptr; + const bool vert_of_group = has_vgroup && + BKE_defvert_find_index(&dvert[mv_index], defgrp_index) != nullptr; + + if (has_vgroup && + ((vert_of_group && use_invert_vgroup) || (!vert_of_group && !use_invert_vgroup))) { + return; + } + + if (use_face_influence && !check_item_poly_strength(wn_data, item_data, mp_index)) { + return; + } + + /* If item's curr_val is 0 init it to present value. */ + if (item_data->curr_val == 0.0f) { + item_data->curr_val = curr_val; + } + if (!compare_ff(item_data->curr_val, curr_val, wnmd->thresh)) { + /* item's curr_val and present value differ more than threshold, update. */ + item_data->loops_num++; + item_data->curr_val = curr_val; + } + + /* Exponentially divided weight for each normal + * (since a few values will be used by most cases, we cache those). */ + const int loops_num = item_data->loops_num; + if (loops_num < NUM_CACHED_INVERSE_POWERS_OF_WEIGHT && + cached_inverse_powers_of_weight[loops_num] == 0.0f) { + cached_inverse_powers_of_weight[loops_num] = 1.0f / powf(weight, loops_num); + } + const float inverted_n_weight = loops_num < NUM_CACHED_INVERSE_POWERS_OF_WEIGHT ? + cached_inverse_powers_of_weight[loops_num] : + 1.0f / powf(weight, loops_num); + + madd_v3_v3fl(item_data->normal, polynors[mp_index], curr_val * inverted_n_weight); +} + +static void apply_weights_vertex_normal(WeightedNormalModifierData *wnmd, + WeightedNormalData *wn_data) +{ + const int verts_num = wn_data->verts_num; + const int edges_num = wn_data->edges_num; + const int loops_num = wn_data->loops_num; + const int polys_num = wn_data->polys_num; + + const MVert *mvert = wn_data->mvert; + MEdge *medge = wn_data->medge; + + const MLoop *mloop = wn_data->mloop; + short(*clnors)[2] = wn_data->clnors; + int *loop_to_poly = wn_data->loop_to_poly; + + const MPoly *mpoly = wn_data->mpoly; + const float(*polynors)[3] = wn_data->polynors; + const int *poly_strength = wn_data->poly_strength; + + const MDeformVert *dvert = wn_data->dvert; + + const short mode = wn_data->mode; + ModePair *mode_pair = wn_data->mode_pair; + + const bool has_clnors = wn_data->has_clnors; + const float split_angle = wn_data->split_angle; + MLoopNorSpaceArray lnors_spacearr = {nullptr}; + + const bool keep_sharp = (wnmd->flag & MOD_WEIGHTEDNORMAL_KEEP_SHARP) != 0; + const bool use_face_influence = (wnmd->flag & MOD_WEIGHTEDNORMAL_FACE_INFLUENCE) != 0 && + poly_strength != nullptr; + const bool has_vgroup = dvert != nullptr; + + float(*loop_normals)[3] = nullptr; + + WeightedNormalDataAggregateItem *items_data = nullptr; + int items_num = 0; + if (keep_sharp) { + BLI_bitmap *done_loops = BLI_BITMAP_NEW(loops_num, __func__); + + /* This will give us loop normal spaces, + * we do not actually care about computed loop_normals for now... */ + loop_normals = static_cast( + MEM_calloc_arrayN((size_t)loops_num, sizeof(*loop_normals), __func__)); + BKE_mesh_normals_loop_split(mvert, + wn_data->vert_normals, + verts_num, + medge, + edges_num, + mloop, + loop_normals, + loops_num, + mpoly, + polynors, + polys_num, + true, + split_angle, + &lnors_spacearr, + has_clnors ? clnors : nullptr, + loop_to_poly); + + items_num = lnors_spacearr.spaces_num; + items_data = static_cast( + MEM_calloc_arrayN((size_t)items_num, sizeof(*items_data), __func__)); + + /* In this first loop, we assign each WeightedNormalDataAggregateItem + * to its smooth fan of loops (aka lnor space). */ + const MPoly *mp; + int mp_index; + int item_index; + for (mp = mpoly, mp_index = 0, item_index = 0; mp_index < polys_num; mp++, mp_index++) { + int ml_index = mp->loopstart; + const int ml_end_index = ml_index + mp->totloop; + + for (; ml_index < ml_end_index; ml_index++) { + if (BLI_BITMAP_TEST(done_loops, ml_index)) { + /* Smooth fan of this loop has already been processed, skip it. */ + continue; + } + BLI_assert(item_index < items_num); + + WeightedNormalDataAggregateItem *itdt = &items_data[item_index]; + itdt->curr_strength = FACE_STRENGTH_WEAK; + + MLoopNorSpace *lnor_space = lnors_spacearr.lspacearr[ml_index]; + lnor_space->user_data = itdt; + + if (!(lnor_space->flags & MLNOR_SPACE_IS_SINGLE)) { + for (LinkNode *lnode = lnor_space->loops; lnode; lnode = lnode->next) { + const int ml_fan_index = POINTER_AS_INT(lnode->link); + BLI_BITMAP_ENABLE(done_loops, ml_fan_index); + } + } + else { + BLI_BITMAP_ENABLE(done_loops, ml_index); + } + + item_index++; + } + } + + MEM_freeN(done_loops); + } + else { + items_num = verts_num; + items_data = static_cast( + MEM_calloc_arrayN((size_t)items_num, sizeof(*items_data), __func__)); + if (use_face_influence) { + for (int item_index = 0; item_index < items_num; item_index++) { + items_data[item_index].curr_strength = FACE_STRENGTH_WEAK; + } + } + } + wn_data->items_data = items_data; + + switch (mode) { + case MOD_WEIGHTEDNORMAL_MODE_FACE: + for (int i = 0; i < polys_num; i++) { + const int mp_index = mode_pair[i].index; + const float mp_val = mode_pair[i].val; + + int ml_index = mpoly[mp_index].loopstart; + const int ml_index_end = ml_index + mpoly[mp_index].totloop; + for (; ml_index < ml_index_end; ml_index++) { + const int mv_index = mloop[ml_index].v; + WeightedNormalDataAggregateItem *item_data = + keep_sharp ? static_cast( + lnors_spacearr.lspacearr[ml_index]->user_data) : + &items_data[mv_index]; + + aggregate_item_normal( + wnmd, wn_data, item_data, mv_index, mp_index, mp_val, use_face_influence); + } + } + break; + case MOD_WEIGHTEDNORMAL_MODE_ANGLE: + case MOD_WEIGHTEDNORMAL_MODE_FACE_ANGLE: + BLI_assert(loop_to_poly != nullptr); + + for (int i = 0; i < loops_num; i++) { + const int ml_index = mode_pair[i].index; + const float ml_val = mode_pair[i].val; + + const int mp_index = loop_to_poly[ml_index]; + const int mv_index = mloop[ml_index].v; + WeightedNormalDataAggregateItem *item_data = + keep_sharp ? static_cast( + lnors_spacearr.lspacearr[ml_index]->user_data) : + &items_data[mv_index]; + + aggregate_item_normal( + wnmd, wn_data, item_data, mv_index, mp_index, ml_val, use_face_influence); + } + break; + default: + BLI_assert_unreachable(); + } + + /* Validate computed weighted normals. */ + for (int item_index = 0; item_index < items_num; item_index++) { + if (normalize_v3(items_data[item_index].normal) < CLNORS_VALID_VEC_LEN) { + zero_v3(items_data[item_index].normal); + } + } + + if (keep_sharp) { + /* Set loop normals for normal computed for each lnor space (smooth fan). + * Note that loop_normals is already populated with clnors + * (before this modifier is applied, at start of this function), + * so no need to recompute them here. */ + for (int ml_index = 0; ml_index < loops_num; ml_index++) { + WeightedNormalDataAggregateItem *item_data = static_cast( + lnors_spacearr.lspacearr[ml_index]->user_data); + if (!is_zero_v3(item_data->normal)) { + copy_v3_v3(loop_normals[ml_index], item_data->normal); + } + } + + BKE_mesh_normals_loop_custom_set(mvert, + wn_data->vert_normals, + verts_num, + medge, + edges_num, + mloop, + loop_normals, + loops_num, + mpoly, + polynors, + polys_num, + clnors); + } + else { + /* TODO: Ideally, we could add an option to `BKE_mesh_normals_loop_custom_[from_verts_]set()` + * to keep current clnors instead of resetting them to default auto-computed ones, + * when given new custom normal is zero-vec. + * But this is not exactly trivial change, better to keep this optimization for later... + */ + if (!has_vgroup) { + /* NOTE: in theory, we could avoid this extra allocation & copying... + * But think we can live with it for now, + * and it makes code simpler & cleaner. */ + float(*vert_normals)[3] = static_cast( + MEM_calloc_arrayN((size_t)verts_num, sizeof(*loop_normals), __func__)); + + for (int ml_index = 0; ml_index < loops_num; ml_index++) { + const int mv_index = mloop[ml_index].v; + copy_v3_v3(vert_normals[mv_index], items_data[mv_index].normal); + } + + BKE_mesh_normals_loop_custom_from_verts_set(mvert, + wn_data->vert_normals, + vert_normals, + verts_num, + medge, + edges_num, + mloop, + loops_num, + mpoly, + polynors, + polys_num, + clnors); + + MEM_freeN(vert_normals); + } + else { + loop_normals = static_cast( + MEM_calloc_arrayN((size_t)loops_num, sizeof(*loop_normals), __func__)); + + BKE_mesh_normals_loop_split(mvert, + wn_data->vert_normals, + verts_num, + medge, + edges_num, + mloop, + loop_normals, + loops_num, + mpoly, + polynors, + polys_num, + true, + split_angle, + nullptr, + has_clnors ? clnors : nullptr, + loop_to_poly); + + for (int ml_index = 0; ml_index < loops_num; ml_index++) { + const int item_index = mloop[ml_index].v; + if (!is_zero_v3(items_data[item_index].normal)) { + copy_v3_v3(loop_normals[ml_index], items_data[item_index].normal); + } + } + + BKE_mesh_normals_loop_custom_set(mvert, + wn_data->vert_normals, + verts_num, + medge, + edges_num, + mloop, + loop_normals, + loops_num, + mpoly, + polynors, + polys_num, + clnors); + } + } + + if (keep_sharp) { + BKE_lnor_spacearr_free(&lnors_spacearr); + } + MEM_SAFE_FREE(loop_normals); +} + +static void wn_face_area(WeightedNormalModifierData *wnmd, WeightedNormalData *wn_data) +{ + const int polys_num = wn_data->polys_num; + + const MVert *mvert = wn_data->mvert; + const MLoop *mloop = wn_data->mloop; + const MPoly *mpoly = wn_data->mpoly; + + const MPoly *mp; + int mp_index; + + ModePair *face_area = static_cast( + MEM_malloc_arrayN((size_t)polys_num, sizeof(*face_area), __func__)); + + ModePair *f_area = face_area; + for (mp_index = 0, mp = mpoly; mp_index < polys_num; mp_index++, mp++, f_area++) { + f_area->val = BKE_mesh_calc_poly_area(mp, &mloop[mp->loopstart], mvert); + f_area->index = mp_index; + } + + qsort(face_area, polys_num, sizeof(*face_area), modepair_cmp_by_val_inverse); + + wn_data->mode_pair = face_area; + apply_weights_vertex_normal(wnmd, wn_data); +} + +static void wn_corner_angle(WeightedNormalModifierData *wnmd, WeightedNormalData *wn_data) +{ + const int loops_num = wn_data->loops_num; + const int polys_num = wn_data->polys_num; + + const MVert *mvert = wn_data->mvert; + const MLoop *mloop = wn_data->mloop; + const MPoly *mpoly = wn_data->mpoly; + + const MPoly *mp; + int mp_index; + + int *loop_to_poly = static_cast( + MEM_malloc_arrayN((size_t)loops_num, sizeof(*loop_to_poly), __func__)); + + ModePair *corner_angle = static_cast( + MEM_malloc_arrayN((size_t)loops_num, sizeof(*corner_angle), __func__)); + + for (mp_index = 0, mp = mpoly; mp_index < polys_num; mp_index++, mp++) { + const MLoop *ml_start = &mloop[mp->loopstart]; + + float *index_angle = static_cast( + MEM_malloc_arrayN((size_t)mp->totloop, sizeof(*index_angle), __func__)); + BKE_mesh_calc_poly_angles(mp, ml_start, mvert, index_angle); + + ModePair *c_angl = &corner_angle[mp->loopstart]; + float *angl = index_angle; + for (int ml_index = mp->loopstart; ml_index < mp->loopstart + mp->totloop; + ml_index++, c_angl++, angl++) { + c_angl->val = (float)M_PI - *angl; + c_angl->index = ml_index; + + loop_to_poly[ml_index] = mp_index; + } + MEM_freeN(index_angle); + } + + qsort(corner_angle, loops_num, sizeof(*corner_angle), modepair_cmp_by_val_inverse); + + wn_data->loop_to_poly = loop_to_poly; + wn_data->mode_pair = corner_angle; + apply_weights_vertex_normal(wnmd, wn_data); +} + +static void wn_face_with_angle(WeightedNormalModifierData *wnmd, WeightedNormalData *wn_data) +{ + const int loops_num = wn_data->loops_num; + const int polys_num = wn_data->polys_num; + + const MVert *mvert = wn_data->mvert; + const MLoop *mloop = wn_data->mloop; + const MPoly *mpoly = wn_data->mpoly; + + const MPoly *mp; + int mp_index; + + int *loop_to_poly = static_cast( + MEM_malloc_arrayN((size_t)loops_num, sizeof(*loop_to_poly), __func__)); + + ModePair *combined = static_cast( + MEM_malloc_arrayN((size_t)loops_num, sizeof(*combined), __func__)); + + for (mp_index = 0, mp = mpoly; mp_index < polys_num; mp_index++, mp++) { + const MLoop *ml_start = &mloop[mp->loopstart]; + + float face_area = BKE_mesh_calc_poly_area(mp, ml_start, mvert); + float *index_angle = static_cast( + MEM_malloc_arrayN((size_t)mp->totloop, sizeof(*index_angle), __func__)); + BKE_mesh_calc_poly_angles(mp, ml_start, mvert, index_angle); + + ModePair *cmbnd = &combined[mp->loopstart]; + float *angl = index_angle; + for (int ml_index = mp->loopstart; ml_index < mp->loopstart + mp->totloop; + ml_index++, cmbnd++, angl++) { + /* In this case val is product of corner angle and face area. */ + cmbnd->val = ((float)M_PI - *angl) * face_area; + cmbnd->index = ml_index; + + loop_to_poly[ml_index] = mp_index; + } + MEM_freeN(index_angle); + } + + qsort(combined, loops_num, sizeof(*combined), modepair_cmp_by_val_inverse); + + wn_data->loop_to_poly = loop_to_poly; + wn_data->mode_pair = combined; + apply_weights_vertex_normal(wnmd, wn_data); +} + +static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mesh) +{ + WeightedNormalModifierData *wnmd = (WeightedNormalModifierData *)md; + Object *ob = ctx->object; + + /* XXX TODO(Rohan Rathi): + * Once we fully switch to Mesh evaluation of modifiers, + * we can expect to get that flag from the COW copy. + * But for now, it is lost in the DM intermediate step, + * so we need to directly check orig object's data. */ +#if 0 + if (!(mesh->flag & ME_AUTOSMOOTH)) +#else + if (!(((Mesh *)ob->data)->flag & ME_AUTOSMOOTH)) +#endif + { + BKE_modifier_set_error( + ctx->object, (ModifierData *)wnmd, "Enable 'Auto Smooth' in Object Data Properties"); + return mesh; + } + + Mesh *result; + result = (Mesh *)BKE_id_copy_ex(nullptr, &mesh->id, nullptr, LIB_ID_COPY_LOCALIZE); + + const int verts_num = result->totvert; + const int edges_num = result->totedge; + const int loops_num = result->totloop; + const int polys_num = result->totpoly; + const MVert *mvert = BKE_mesh_verts(result); + MEdge *medge = BKE_mesh_edges_for_write(result); + const MPoly *mpoly = BKE_mesh_polys(result); + const MLoop *mloop = BKE_mesh_loops(result); + + /* Right now: + * If weight = 50 then all faces are given equal weight. + * If weight > 50 then more weight given to faces with larger vals (face area / corner angle). + * If weight < 50 then more weight given to faces with lesser vals. However current calculation + * does not converge to min/max. + */ + float weight = ((float)wnmd->weight) / 50.0f; + if (wnmd->weight == 100) { + weight = (float)SHRT_MAX; + } + else if (wnmd->weight == 1) { + weight = 1 / (float)SHRT_MAX; + } + else if ((weight - 1) * 25 > 1) { + weight = (weight - 1) * 25; + } + + const float split_angle = mesh->smoothresh; + short(*clnors)[2] = static_cast( + CustomData_get_layer(&result->ldata, CD_CUSTOMLOOPNORMAL)); + + /* Keep info whether we had clnors, + * it helps when generating clnor spaces and default normals. */ + const bool has_clnors = clnors != nullptr; + if (!clnors) { + clnors = static_cast(CustomData_add_layer( + &result->ldata, CD_CUSTOMLOOPNORMAL, CD_SET_DEFAULT, nullptr, loops_num)); + } + + const MDeformVert *dvert; + int defgrp_index; + MOD_get_vgroup(ctx->object, mesh, wnmd->defgrp_name, &dvert, &defgrp_index); + + WeightedNormalData wn_data{}; + wn_data.verts_num = verts_num; + wn_data.edges_num = edges_num; + wn_data.loops_num = loops_num; + wn_data.polys_num = polys_num; + + wn_data.mvert = mvert; + wn_data.vert_normals = BKE_mesh_vertex_normals_ensure(result); + wn_data.medge = medge; + + wn_data.mloop = mloop; + wn_data.clnors = clnors; + wn_data.has_clnors = has_clnors; + wn_data.split_angle = split_angle; + + wn_data.mpoly = mpoly; + wn_data.polynors = BKE_mesh_poly_normals_ensure(mesh); + wn_data.poly_strength = static_cast(CustomData_get_layer_named( + &result->pdata, CD_PROP_INT32, MOD_WEIGHTEDNORMALS_FACEWEIGHT_CDLAYER_ID)); + + wn_data.dvert = dvert; + wn_data.defgrp_index = defgrp_index; + wn_data.use_invert_vgroup = (wnmd->flag & MOD_WEIGHTEDNORMAL_INVERT_VGROUP) != 0; + + wn_data.weight = weight; + wn_data.mode = wnmd->mode; + + switch (wnmd->mode) { + case MOD_WEIGHTEDNORMAL_MODE_FACE: + wn_face_area(wnmd, &wn_data); + break; + case MOD_WEIGHTEDNORMAL_MODE_ANGLE: + wn_corner_angle(wnmd, &wn_data); + break; + case MOD_WEIGHTEDNORMAL_MODE_FACE_ANGLE: + wn_face_with_angle(wnmd, &wn_data); + break; + } + + MEM_SAFE_FREE(wn_data.loop_to_poly); + MEM_SAFE_FREE(wn_data.mode_pair); + MEM_SAFE_FREE(wn_data.items_data); + + result->runtime.is_original_bmesh = false; + + return result; +} + +static void initData(ModifierData *md) +{ + WeightedNormalModifierData *wnmd = (WeightedNormalModifierData *)md; + + BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(wnmd, modifier)); + + MEMCPY_STRUCT_AFTER(wnmd, DNA_struct_default_get(WeightedNormalModifierData), modifier); +} + +static void requiredDataMask(ModifierData *md, CustomData_MeshMasks *r_cddata_masks) +{ + WeightedNormalModifierData *wnmd = (WeightedNormalModifierData *)md; + + r_cddata_masks->lmask = CD_MASK_CUSTOMLOOPNORMAL; + + if (wnmd->defgrp_name[0] != '\0') { + r_cddata_masks->vmask |= CD_MASK_MDEFORMVERT; + } + + if (wnmd->flag & MOD_WEIGHTEDNORMAL_FACE_INFLUENCE) { + r_cddata_masks->pmask |= CD_MASK_PROP_INT32; + } +} + +static bool dependsOnNormals(ModifierData *UNUSED(md)) +{ + return true; +} + +static void panel_draw(const bContext *UNUSED(C), Panel *panel) +{ + uiLayout *col; + uiLayout *layout = panel->layout; + + PointerRNA ob_ptr; + PointerRNA *ptr = modifier_panel_get_property_pointers(panel, &ob_ptr); + + uiLayoutSetPropSep(layout, true); + + uiItemR(layout, ptr, "mode", 0, nullptr, ICON_NONE); + + uiItemR(layout, ptr, "weight", 0, IFACE_("Weight"), ICON_NONE); + uiItemR(layout, ptr, "thresh", 0, IFACE_("Threshold"), ICON_NONE); + + col = uiLayoutColumn(layout, false); + uiItemR(col, ptr, "keep_sharp", 0, nullptr, ICON_NONE); + uiItemR(col, ptr, "use_face_influence", 0, nullptr, ICON_NONE); + + modifier_vgroup_ui(layout, ptr, &ob_ptr, "vertex_group", "invert_vertex_group", nullptr); + + modifier_panel_end(layout, ptr); +} + +static void panelRegister(ARegionType *region_type) +{ + modifier_panel_register(region_type, eModifierType_WeightedNormal, panel_draw); +} + +ModifierTypeInfo modifierType_WeightedNormal = { + /* name */ N_("WeightedNormal"), + /* structName */ "WeightedNormalModifierData", + /* structSize */ sizeof(WeightedNormalModifierData), + /* srna */ &RNA_WeightedNormalModifier, + /* type */ eModifierTypeType_Constructive, + /* flags */ eModifierTypeFlag_AcceptsMesh | eModifierTypeFlag_SupportsMapping | + eModifierTypeFlag_SupportsEditmode | eModifierTypeFlag_EnableInEditmode, + /* 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 */ nullptr, + /* updateDepsgraph */ nullptr, + /* dependsOnTime */ nullptr, + /* dependsOnNormals */ dependsOnNormals, + /* foreachIDLink */ nullptr, + /* foreachTexLink */ nullptr, + /* freeRuntimeData */ nullptr, + /* panelRegister */ panelRegister, + /* blendWrite */ nullptr, + /* blendRead */ nullptr, +}; diff --git a/source/blender/modifiers/intern/MOD_weightvg_util.h b/source/blender/modifiers/intern/MOD_weightvg_util.h index 2d86518aaa0..f6ff03fae18 100644 --- a/source/blender/modifiers/intern/MOD_weightvg_util.h +++ b/source/blender/modifiers/intern/MOD_weightvg_util.h @@ -7,6 +7,10 @@ #pragma once +#ifdef __cplusplus +extern "C" { +#endif + struct CurveMapping; struct MDeformVert; struct MDeformWeight; @@ -101,3 +105,7 @@ void weightvg_update_vg(struct MDeformVert *dvert, * Common vertex weight mask interface elements for the modifier panels. */ void weightvg_ui_common(const bContext *C, PointerRNA *ob_ptr, PointerRNA *ptr, uiLayout *layout); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/modifiers/intern/MOD_weightvgedit.c b/source/blender/modifiers/intern/MOD_weightvgedit.c deleted file mode 100644 index fa147201dc3..00000000000 --- a/source/blender/modifiers/intern/MOD_weightvgedit.c +++ /dev/null @@ -1,423 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later - * Copyright 2011 by Bastien Montagne. All rights reserved. */ - -/** \file - * \ingroup modifiers - */ - -#include - -#include "BLI_utildefines.h" - -#include "BLI_ghash.h" -#include "BLI_listbase.h" -#include "BLI_rand.h" - -#include "BLT_translation.h" - -#include "DNA_color_types.h" /* CurveMapping. */ -#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_colortools.h" /* CurveMapping. */ -#include "BKE_context.h" -#include "BKE_deform.h" -#include "BKE_lib_query.h" -#include "BKE_mesh.h" -#include "BKE_modifier.h" -#include "BKE_screen.h" -#include "BKE_texture.h" /* Texture masking. */ - -#include "UI_interface.h" -#include "UI_resources.h" - -#include "BLO_read_write.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" - -/************************************** - * Modifiers functions. * - **************************************/ -static void initData(ModifierData *md) -{ - WeightVGEditModifierData *wmd = (WeightVGEditModifierData *)md; - - BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(wmd, modifier)); - - MEMCPY_STRUCT_AFTER(wmd, DNA_struct_default_get(WeightVGEditModifierData), modifier); - - wmd->cmap_curve = BKE_curvemapping_add(1, 0.0, 0.0, 1.0, 1.0); - BKE_curvemapping_init(wmd->cmap_curve); -} - -static void freeData(ModifierData *md) -{ - WeightVGEditModifierData *wmd = (WeightVGEditModifierData *)md; - BKE_curvemapping_free(wmd->cmap_curve); -} - -static void copyData(const ModifierData *md, ModifierData *target, const int flag) -{ - const WeightVGEditModifierData *wmd = (const WeightVGEditModifierData *)md; - WeightVGEditModifierData *twmd = (WeightVGEditModifierData *)target; - - BKE_modifier_copydata_generic(md, target, flag); - - twmd->cmap_curve = BKE_curvemapping_copy(wmd->cmap_curve); -} - -static void requiredDataMask(ModifierData *md, CustomData_MeshMasks *r_cddata_masks) -{ - WeightVGEditModifierData *wmd = (WeightVGEditModifierData *)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(struct Scene *UNUSED(scene), ModifierData *md) -{ - WeightVGEditModifierData *wmd = (WeightVGEditModifierData *)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) -{ - WeightVGEditModifierData *wmd = (WeightVGEditModifierData *)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) -{ - WeightVGEditModifierData *wmd = (WeightVGEditModifierData *)md; - bool need_transform_relation = false; - - if (wmd->mask_texture != NULL) { - DEG_add_generic_id_relation(ctx->node, &wmd->mask_texture->id, "WeightVGEdit Modifier"); - - if (wmd->mask_tex_map_obj != NULL && 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, "WeightVGEdit 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, "WeightVGEdit Modifier"); - } -} - -static bool isDisabled(const struct Scene *UNUSED(scene), - ModifierData *md, - bool UNUSED(useRenderParams)) -{ - WeightVGEditModifierData *wmd = (WeightVGEditModifierData *)md; - /* If no vertex group, bypass. */ - return (wmd->defgrp_name[0] == '\0'); -} - -static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mesh) -{ - BLI_assert(mesh != NULL); - - WeightVGEditModifierData *wmd = (WeightVGEditModifierData *)md; - - MDeformWeight **dw = NULL; - float *org_w; /* Array original weights. */ - float *new_w; /* Array new weights. */ - int i; - const bool invert_vgroup_mask = (wmd->edit_flags & MOD_WVG_EDIT_INVERT_VGROUP_MASK) != 0; - - /* Flags. */ - const bool do_add = (wmd->edit_flags & MOD_WVG_EDIT_ADD2VG) != 0; - const bool do_rem = (wmd->edit_flags & MOD_WVG_EDIT_REMFVG) != 0; - /* Only do weight-preview in Object, Sculpt and Pose modes! */ -#if 0 - const bool do_prev = (wmd->modifier.mode & eModifierMode_DoWeightPreview); -#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); - if (defgrp_index == -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 NULL. */ - if (!has_mdef) { - /* If this modifier is not allowed to add vertices, just return. */ - if (!do_add) { - return mesh; - } - } - - MDeformVert *dvert = BKE_mesh_deform_verts_for_write(mesh); - - /* Ultimate security check. */ - if (!dvert) { - return mesh; - } - - /* Get org weights, assuming 0.0 for vertices not in given vgroup. */ - org_w = MEM_malloc_arrayN(verts_num, sizeof(float), "WeightVGEdit Modifier, org_w"); - new_w = MEM_malloc_arrayN(verts_num, sizeof(float), "WeightVGEdit Modifier, new_w"); - dw = MEM_malloc_arrayN(verts_num, sizeof(MDeformWeight *), "WeightVGEdit Modifier, dw"); - for (i = 0; i < verts_num; i++) { - dw[i] = BKE_defvert_find_index(&dvert[i], defgrp_index); - if (dw[i]) { - org_w[i] = new_w[i] = dw[i]->weight; - } - else { - org_w[i] = new_w[i] = wmd->default_weight; - } - } - - /* Do mapping. */ - const bool do_invert_mapping = (wmd->edit_flags & MOD_WVG_INVERT_FALLOFF) != 0; - const bool do_normalize = (wmd->edit_flags & MOD_WVG_EDIT_WEIGHTS_NORMALIZE) != 0; - if (do_invert_mapping || wmd->falloff_type != MOD_WVG_MAPPING_NONE) { - RNG *rng = NULL; - - if (wmd->falloff_type == MOD_WVG_MAPPING_RANDOM) { - rng = BLI_rng_new_srandom(BLI_ghashutil_strhash(ctx->object->id.name + 2)); - } - - weightvg_do_map(verts_num, new_w, wmd->falloff_type, do_invert_mapping, wmd->cmap_curve, rng); - - if (rng) { - BLI_rng_free(rng); - } - } - - /* Do masking. */ - struct Scene *scene = DEG_get_evaluated_scene(ctx->depsgraph); - weightvg_do_mask(ctx, - verts_num, - NULL, - 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/remove from vgroup. */ - weightvg_update_vg(dvert, - defgrp_index, - dw, - verts_num, - NULL, - org_w, - do_add, - wmd->add_threshold, - do_rem, - wmd->rem_threshold, - 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, 0, NULL); - } -#endif - - /* Freeing stuff. */ - MEM_freeN(org_w); - MEM_freeN(new_w); - MEM_freeN(dw); - - mesh->runtime.is_original_bmesh = false; - - /* Return the vgroup-modified mesh. */ - return mesh; -} - -static void panel_draw(const bContext *UNUSED(C), Panel *panel) -{ - uiLayout *sub, *col, *row; - uiLayout *layout = panel->layout; - - PointerRNA ob_ptr; - PointerRNA *ptr = modifier_panel_get_property_pointers(panel, &ob_ptr); - - uiLayoutSetPropSep(layout, true); - - col = uiLayoutColumn(layout, true); - uiItemPointerR(col, ptr, "vertex_group", &ob_ptr, "vertex_groups", NULL, ICON_NONE); - - uiItemR(layout, ptr, "default_weight", UI_ITEM_R_SLIDER, NULL, ICON_NONE); - - col = uiLayoutColumnWithHeading(layout, false, IFACE_("Group Add")); - row = uiLayoutRow(col, true); - uiLayoutSetPropDecorate(row, false); - sub = uiLayoutRow(row, true); - uiItemR(sub, ptr, "use_add", 0, "", ICON_NONE); - sub = uiLayoutRow(sub, true); - uiLayoutSetActive(sub, RNA_boolean_get(ptr, "use_add")); - uiLayoutSetPropSep(sub, false); - uiItemR(sub, ptr, "add_threshold", UI_ITEM_R_SLIDER, IFACE_("Threshold"), ICON_NONE); - uiItemDecoratorR(row, ptr, "add_threshold", 0); - - col = uiLayoutColumnWithHeading(layout, false, IFACE_("Group Remove")); - row = uiLayoutRow(col, true); - uiLayoutSetPropDecorate(row, false); - sub = uiLayoutRow(row, true); - uiItemR(sub, ptr, "use_remove", 0, "", ICON_NONE); - sub = uiLayoutRow(sub, true); - uiLayoutSetActive(sub, RNA_boolean_get(ptr, "use_remove")); - uiLayoutSetPropSep(sub, false); - uiItemR(sub, ptr, "remove_threshold", UI_ITEM_R_SLIDER, IFACE_("Threshold"), ICON_NONE); - uiItemDecoratorR(row, ptr, "remove_threshold", 0); - - uiItemR(layout, ptr, "normalize", 0, NULL, ICON_NONE); - - modifier_panel_end(layout, ptr); -} - -static void falloff_panel_draw(const bContext *UNUSED(C), Panel *panel) -{ - uiLayout *row, *sub; - uiLayout *layout = panel->layout; - - PointerRNA ob_ptr; - PointerRNA *ptr = modifier_panel_get_property_pointers(panel, &ob_ptr); - - uiLayoutSetPropSep(layout, true); - - row = uiLayoutRow(layout, true); - uiItemR(row, ptr, "falloff_type", 0, IFACE_("Type"), ICON_NONE); - sub = uiLayoutRow(row, true); - uiLayoutSetPropSep(sub, false); - uiItemR(row, ptr, "invert_falloff", 0, "", ICON_ARROW_LEFTRIGHT); - if (RNA_enum_get(ptr, "falloff_type") == MOD_WVG_MAPPING_CURVE) { - uiTemplateCurveMapping(layout, ptr, "map_curve", 0, false, false, false, false); - } -} - -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_WeightVGEdit, panel_draw); - modifier_subpanel_register( - region_type, "falloff", "Falloff", NULL, falloff_panel_draw, panel_type); - modifier_subpanel_register( - region_type, "influence", "Influence", NULL, influence_panel_draw, panel_type); -} - -static void blendWrite(BlendWriter *writer, const ID *UNUSED(id_owner), const ModifierData *md) -{ - const WeightVGEditModifierData *wmd = (const WeightVGEditModifierData *)md; - - BLO_write_struct(writer, WeightVGEditModifierData, wmd); - - if (wmd->cmap_curve) { - BKE_curvemapping_blend_write(writer, wmd->cmap_curve); - } -} - -static void blendRead(BlendDataReader *reader, ModifierData *md) -{ - WeightVGEditModifierData *wmd = (WeightVGEditModifierData *)md; - - BLO_read_data_address(reader, &wmd->cmap_curve); - if (wmd->cmap_curve) { - BKE_curvemapping_blend_read(reader, wmd->cmap_curve); - } -} - -ModifierTypeInfo modifierType_WeightVGEdit = { - /* name */ N_("VertexWeightEdit"), - /* structName */ "WeightVGEditModifierData", - /* structSize */ sizeof(WeightVGEditModifierData), - /* srna */ &RNA_VertexWeightEditModifier, - /* type */ eModifierTypeType_NonGeometrical, - /* flags */ eModifierTypeFlag_AcceptsMesh | eModifierTypeFlag_SupportsMapping | - eModifierTypeFlag_SupportsEditmode | eModifierTypeFlag_UsesPreview, - /* icon */ ICON_MOD_VERTEX_WEIGHT, - - /* copyData */ copyData, - - /* deformVerts */ NULL, - /* deformMatrices */ NULL, - /* deformVertsEM */ NULL, - /* deformMatricesEM */ NULL, - /* modifyMesh */ modifyMesh, - /* modifyGeometrySet */ NULL, - - /* initData */ initData, - /* requiredDataMask */ requiredDataMask, - /* freeData */ freeData, - /* isDisabled */ isDisabled, - /* updateDepsgraph */ updateDepsgraph, - /* dependsOnTime */ dependsOnTime, - /* dependsOnNormals */ NULL, - /* foreachIDLink */ foreachIDLink, - /* foreachTexLink */ foreachTexLink, - /* freeRuntimeData */ NULL, - /* panelRegister */ panelRegister, - /* blendWrite */ blendWrite, - /* blendRead */ blendRead, -}; diff --git a/source/blender/modifiers/intern/MOD_weightvgedit.cc b/source/blender/modifiers/intern/MOD_weightvgedit.cc new file mode 100644 index 00000000000..bcfd47491d5 --- /dev/null +++ b/source/blender/modifiers/intern/MOD_weightvgedit.cc @@ -0,0 +1,422 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2011 by Bastien Montagne. All rights reserved. */ + +/** \file + * \ingroup modifiers + */ + +#include + +#include "BLI_utildefines.h" + +#include "BLI_ghash.h" +#include "BLI_listbase.h" +#include "BLI_rand.h" + +#include "BLT_translation.h" + +#include "DNA_color_types.h" /* CurveMapping. */ +#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_colortools.h" /* CurveMapping. */ +#include "BKE_context.h" +#include "BKE_deform.h" +#include "BKE_lib_query.h" +#include "BKE_mesh.h" +#include "BKE_modifier.h" +#include "BKE_screen.h" +#include "BKE_texture.h" /* Texture masking. */ + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "BLO_read_write.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" + +/************************************** + * Modifiers functions. * + **************************************/ +static void initData(ModifierData *md) +{ + WeightVGEditModifierData *wmd = (WeightVGEditModifierData *)md; + + BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(wmd, modifier)); + + MEMCPY_STRUCT_AFTER(wmd, DNA_struct_default_get(WeightVGEditModifierData), modifier); + + wmd->cmap_curve = BKE_curvemapping_add(1, 0.0, 0.0, 1.0, 1.0); + BKE_curvemapping_init(wmd->cmap_curve); +} + +static void freeData(ModifierData *md) +{ + WeightVGEditModifierData *wmd = (WeightVGEditModifierData *)md; + BKE_curvemapping_free(wmd->cmap_curve); +} + +static void copyData(const ModifierData *md, ModifierData *target, const int flag) +{ + const WeightVGEditModifierData *wmd = (const WeightVGEditModifierData *)md; + WeightVGEditModifierData *twmd = (WeightVGEditModifierData *)target; + + BKE_modifier_copydata_generic(md, target, flag); + + twmd->cmap_curve = BKE_curvemapping_copy(wmd->cmap_curve); +} + +static void requiredDataMask(ModifierData *md, CustomData_MeshMasks *r_cddata_masks) +{ + WeightVGEditModifierData *wmd = (WeightVGEditModifierData *)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 *UNUSED(scene), ModifierData *md) +{ + WeightVGEditModifierData *wmd = (WeightVGEditModifierData *)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) +{ + WeightVGEditModifierData *wmd = (WeightVGEditModifierData *)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) +{ + WeightVGEditModifierData *wmd = (WeightVGEditModifierData *)md; + bool need_transform_relation = false; + + if (wmd->mask_texture != nullptr) { + DEG_add_generic_id_relation(ctx->node, &wmd->mask_texture->id, "WeightVGEdit 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, "WeightVGEdit 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, "WeightVGEdit Modifier"); + } +} + +static bool isDisabled(const Scene *UNUSED(scene), ModifierData *md, bool UNUSED(useRenderParams)) +{ + WeightVGEditModifierData *wmd = (WeightVGEditModifierData *)md; + /* If no vertex group, bypass. */ + return (wmd->defgrp_name[0] == '\0'); +} + +static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mesh) +{ + BLI_assert(mesh != nullptr); + + WeightVGEditModifierData *wmd = (WeightVGEditModifierData *)md; + + MDeformWeight **dw = nullptr; + float *org_w; /* Array original weights. */ + float *new_w; /* Array new weights. */ + int i; + const bool invert_vgroup_mask = (wmd->edit_flags & MOD_WVG_EDIT_INVERT_VGROUP_MASK) != 0; + + /* Flags. */ + const bool do_add = (wmd->edit_flags & MOD_WVG_EDIT_ADD2VG) != 0; + const bool do_rem = (wmd->edit_flags & MOD_WVG_EDIT_REMFVG) != 0; + /* Only do weight-preview in Object, Sculpt and Pose modes! */ +#if 0 + const bool do_prev = (wmd->modifier.mode & eModifierMode_DoWeightPreview); +#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); + if (defgrp_index == -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 this modifier is not allowed to add vertices, just return. */ + if (!do_add) { + return mesh; + } + } + + MDeformVert *dvert = BKE_mesh_deform_verts_for_write(mesh); + + /* Ultimate security check. */ + if (!dvert) { + return mesh; + } + + /* Get org weights, assuming 0.0 for vertices not in given vgroup. */ + org_w = static_cast(MEM_malloc_arrayN(verts_num, sizeof(float), __func__)); + new_w = static_cast(MEM_malloc_arrayN(verts_num, sizeof(float), __func__)); + dw = static_cast( + MEM_malloc_arrayN(verts_num, sizeof(MDeformWeight *), __func__)); + for (i = 0; i < verts_num; i++) { + dw[i] = BKE_defvert_find_index(&dvert[i], defgrp_index); + if (dw[i]) { + org_w[i] = new_w[i] = dw[i]->weight; + } + else { + org_w[i] = new_w[i] = wmd->default_weight; + } + } + + /* Do mapping. */ + const bool do_invert_mapping = (wmd->edit_flags & MOD_WVG_INVERT_FALLOFF) != 0; + const bool do_normalize = (wmd->edit_flags & MOD_WVG_EDIT_WEIGHTS_NORMALIZE) != 0; + if (do_invert_mapping || wmd->falloff_type != MOD_WVG_MAPPING_NONE) { + RNG *rng = nullptr; + + if (wmd->falloff_type == MOD_WVG_MAPPING_RANDOM) { + rng = BLI_rng_new_srandom(BLI_ghashutil_strhash(ctx->object->id.name + 2)); + } + + weightvg_do_map(verts_num, new_w, wmd->falloff_type, do_invert_mapping, wmd->cmap_curve, rng); + + if (rng) { + BLI_rng_free(rng); + } + } + + /* Do masking. */ + Scene *scene = DEG_get_evaluated_scene(ctx->depsgraph); + weightvg_do_mask(ctx, + verts_num, + nullptr, + 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/remove from vgroup. */ + weightvg_update_vg(dvert, + defgrp_index, + dw, + verts_num, + nullptr, + org_w, + do_add, + wmd->add_threshold, + do_rem, + wmd->rem_threshold, + 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, 0, nullptr); + } +#endif + + /* Freeing stuff. */ + MEM_freeN(org_w); + MEM_freeN(new_w); + MEM_freeN(dw); + + mesh->runtime.is_original_bmesh = false; + + /* Return the vgroup-modified mesh. */ + return mesh; +} + +static void panel_draw(const bContext *UNUSED(C), Panel *panel) +{ + uiLayout *sub, *col, *row; + uiLayout *layout = panel->layout; + + PointerRNA ob_ptr; + PointerRNA *ptr = modifier_panel_get_property_pointers(panel, &ob_ptr); + + uiLayoutSetPropSep(layout, true); + + col = uiLayoutColumn(layout, true); + uiItemPointerR(col, ptr, "vertex_group", &ob_ptr, "vertex_groups", nullptr, ICON_NONE); + + uiItemR(layout, ptr, "default_weight", UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + + col = uiLayoutColumnWithHeading(layout, false, IFACE_("Group Add")); + row = uiLayoutRow(col, true); + uiLayoutSetPropDecorate(row, false); + sub = uiLayoutRow(row, true); + uiItemR(sub, ptr, "use_add", 0, "", ICON_NONE); + sub = uiLayoutRow(sub, true); + uiLayoutSetActive(sub, RNA_boolean_get(ptr, "use_add")); + uiLayoutSetPropSep(sub, false); + uiItemR(sub, ptr, "add_threshold", UI_ITEM_R_SLIDER, IFACE_("Threshold"), ICON_NONE); + uiItemDecoratorR(row, ptr, "add_threshold", 0); + + col = uiLayoutColumnWithHeading(layout, false, IFACE_("Group Remove")); + row = uiLayoutRow(col, true); + uiLayoutSetPropDecorate(row, false); + sub = uiLayoutRow(row, true); + uiItemR(sub, ptr, "use_remove", 0, "", ICON_NONE); + sub = uiLayoutRow(sub, true); + uiLayoutSetActive(sub, RNA_boolean_get(ptr, "use_remove")); + uiLayoutSetPropSep(sub, false); + uiItemR(sub, ptr, "remove_threshold", UI_ITEM_R_SLIDER, IFACE_("Threshold"), ICON_NONE); + uiItemDecoratorR(row, ptr, "remove_threshold", 0); + + uiItemR(layout, ptr, "normalize", 0, nullptr, ICON_NONE); + + modifier_panel_end(layout, ptr); +} + +static void falloff_panel_draw(const bContext *UNUSED(C), Panel *panel) +{ + uiLayout *row, *sub; + uiLayout *layout = panel->layout; + + PointerRNA ob_ptr; + PointerRNA *ptr = modifier_panel_get_property_pointers(panel, &ob_ptr); + + uiLayoutSetPropSep(layout, true); + + row = uiLayoutRow(layout, true); + uiItemR(row, ptr, "falloff_type", 0, IFACE_("Type"), ICON_NONE); + sub = uiLayoutRow(row, true); + uiLayoutSetPropSep(sub, false); + uiItemR(row, ptr, "invert_falloff", 0, "", ICON_ARROW_LEFTRIGHT); + if (RNA_enum_get(ptr, "falloff_type") == MOD_WVG_MAPPING_CURVE) { + uiTemplateCurveMapping(layout, ptr, "map_curve", 0, false, false, false, false); + } +} + +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_WeightVGEdit, panel_draw); + modifier_subpanel_register( + region_type, "falloff", "Falloff", nullptr, falloff_panel_draw, panel_type); + modifier_subpanel_register( + region_type, "influence", "Influence", nullptr, influence_panel_draw, panel_type); +} + +static void blendWrite(BlendWriter *writer, const ID *UNUSED(id_owner), const ModifierData *md) +{ + const WeightVGEditModifierData *wmd = (const WeightVGEditModifierData *)md; + + BLO_write_struct(writer, WeightVGEditModifierData, wmd); + + if (wmd->cmap_curve) { + BKE_curvemapping_blend_write(writer, wmd->cmap_curve); + } +} + +static void blendRead(BlendDataReader *reader, ModifierData *md) +{ + WeightVGEditModifierData *wmd = (WeightVGEditModifierData *)md; + + BLO_read_data_address(reader, &wmd->cmap_curve); + if (wmd->cmap_curve) { + BKE_curvemapping_blend_read(reader, wmd->cmap_curve); + } +} + +ModifierTypeInfo modifierType_WeightVGEdit = { + /* name */ N_("VertexWeightEdit"), + /* structName */ "WeightVGEditModifierData", + /* structSize */ sizeof(WeightVGEditModifierData), + /* srna */ &RNA_VertexWeightEditModifier, + /* type */ eModifierTypeType_NonGeometrical, + /* flags */ eModifierTypeFlag_AcceptsMesh | eModifierTypeFlag_SupportsMapping | + eModifierTypeFlag_SupportsEditmode | eModifierTypeFlag_UsesPreview, + /* icon */ ICON_MOD_VERTEX_WEIGHT, + + /* copyData */ copyData, + + /* deformVerts */ nullptr, + /* deformMatrices */ nullptr, + /* deformVertsEM */ nullptr, + /* deformMatricesEM */ nullptr, + /* modifyMesh */ modifyMesh, + /* modifyGeometrySet */ nullptr, + + /* initData */ initData, + /* requiredDataMask */ requiredDataMask, + /* freeData */ freeData, + /* isDisabled */ isDisabled, + /* updateDepsgraph */ updateDepsgraph, + /* dependsOnTime */ dependsOnTime, + /* dependsOnNormals */ nullptr, + /* foreachIDLink */ foreachIDLink, + /* foreachTexLink */ foreachTexLink, + /* freeRuntimeData */ nullptr, + /* panelRegister */ panelRegister, + /* blendWrite */ blendWrite, + /* blendRead */ blendRead, +}; diff --git a/source/blender/modifiers/intern/MOD_weightvgmix.c b/source/blender/modifiers/intern/MOD_weightvgmix.c deleted file mode 100644 index 957ea3b6c8f..00000000000 --- a/source/blender/modifiers/intern/MOD_weightvgmix.c +++ /dev/null @@ -1,522 +0,0 @@ -/* 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.h" -#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(struct Scene *UNUSED(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 != NULL) { - DEG_add_generic_id_relation(ctx->node, &wmd->mask_texture->id, "WeightVGMix Modifier"); - - if (wmd->mask_tex_map_obj != NULL && 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 struct Scene *UNUSED(scene), - ModifierData *md, - bool UNUSED(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 != NULL); - - WeightVGMixModifierData *wmd = (WeightVGMixModifierData *)md; - - MDeformWeight **dw1, **tdw1, **dw2, **tdw2; - float *org_w; - float *new_w; - int *tidx, *indices = NULL; - 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 NULL. */ - 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 = MEM_malloc_arrayN(verts_num, sizeof(int), "WeightVGMix Modifier, tidx"); - tdw1 = MEM_malloc_arrayN(verts_num, sizeof(MDeformWeight *), "WeightVGMix Modifier, tdw1"); - tdw2 = MEM_malloc_arrayN(verts_num, sizeof(MDeformWeight *), "WeightVGMix Modifier, tdw2"); - 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) : - NULL; - 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) : - NULL; - 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) : - NULL; - 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) : - NULL; - 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) : - NULL; - } - 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 = MEM_malloc_arrayN(index_num, sizeof(int), "WeightVGMix Modifier, indices"); - memcpy(indices, tidx, sizeof(int) * index_num); - dw1 = MEM_malloc_arrayN(index_num, sizeof(MDeformWeight *), "WeightVGMix Modifier, dw1"); - memcpy(dw1, tdw1, sizeof(MDeformWeight *) * index_num); - MEM_freeN(tdw1); - dw2 = MEM_malloc_arrayN(index_num, sizeof(MDeformWeight *), "WeightVGMix Modifier, dw2"); - 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 = MEM_malloc_arrayN(index_num, sizeof(float), "WeightVGMix Modifier, org_w"); - new_w = MEM_malloc_arrayN(index_num, sizeof(float), "WeightVGMix Modifier, new_w"); - - /* 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. */ - struct 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 *UNUSED(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", NULL); - 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, NULL, ICON_NONE); - uiItemR(layout, ptr, "default_weight_b", 0, IFACE_("B"), ICON_NONE); - - uiItemS(layout); - - uiItemR(layout, ptr, "mix_set", 0, NULL, ICON_NONE); - uiItemR(layout, ptr, "mix_mode", 0, NULL, ICON_NONE); - - uiItemR(layout, ptr, "normalize", 0, NULL, 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", NULL, 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 */ NULL, - /* deformMatrices */ NULL, - /* deformVertsEM */ NULL, - /* deformMatricesEM */ NULL, - /* modifyMesh */ modifyMesh, - /* modifyGeometrySet */ NULL, - - /* initData */ initData, - /* requiredDataMask */ requiredDataMask, - /* freeData */ NULL, - /* isDisabled */ isDisabled, - /* updateDepsgraph */ updateDepsgraph, - /* dependsOnTime */ dependsOnTime, - /* dependsOnNormals */ NULL, - /* foreachIDLink */ foreachIDLink, - /* foreachTexLink */ foreachTexLink, - /* freeRuntimeData */ NULL, - /* panelRegister */ panelRegister, - /* blendWrite */ NULL, - /* blendRead */ NULL, -}; diff --git a/source/blender/modifiers/intern/MOD_weightvgmix.cc b/source/blender/modifiers/intern/MOD_weightvgmix.cc new file mode 100644 index 00000000000..720f9fac948 --- /dev/null +++ b/source/blender/modifiers/intern/MOD_weightvgmix.cc @@ -0,0 +1,524 @@ +/* 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.h" +#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 *UNUSED(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 *UNUSED(scene), ModifierData *md, bool UNUSED(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(MEM_malloc_arrayN(verts_num, sizeof(int), __func__)); + tdw1 = static_cast( + MEM_malloc_arrayN(verts_num, sizeof(MDeformWeight *), __func__)); + tdw2 = static_cast( + 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(MEM_malloc_arrayN(index_num, sizeof(int), __func__)); + memcpy(indices, tidx, sizeof(int) * index_num); + dw1 = static_cast( + MEM_malloc_arrayN(index_num, sizeof(MDeformWeight *), __func__)); + memcpy(dw1, tdw1, sizeof(MDeformWeight *) * index_num); + MEM_freeN(tdw1); + dw2 = static_cast( + 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(MEM_malloc_arrayN(index_num, sizeof(float), __func__)); + new_w = static_cast(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 *UNUSED(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, +}; diff --git a/source/blender/modifiers/intern/MOD_weightvgproximity.c b/source/blender/modifiers/intern/MOD_weightvgproximity.c deleted file mode 100644 index ead12d9f08d..00000000000 --- a/source/blender/modifiers/intern/MOD_weightvgproximity.c +++ /dev/null @@ -1,767 +0,0 @@ -/* 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_ghash.h" -#include "BLI_listbase.h" -#include "BLI_math.h" -#include "BLI_rand.h" -#include "BLI_task.h" - -#include "BLT_translation.h" - -#include "DNA_color_types.h" /* CurveMapping. */ -#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_bvhutils.h" -#include "BKE_colortools.h" /* CurveMapping. */ -#include "BKE_context.h" -#include "BKE_curve.h" -#include "BKE_customdata.h" -#include "BKE_deform.h" -#include "BKE_lib_id.h" -#include "BKE_lib_query.h" -#include "BKE_mesh.h" -#include "BKE_mesh_wrapper.h" -#include "BKE_modifier.h" -#include "BKE_screen.h" -#include "BKE_texture.h" /* Texture masking. */ - -#include "UI_interface.h" -#include "UI_resources.h" - -#include "BLO_read_write.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" - -//#define USE_TIMEIT - -#ifdef USE_TIMEIT -# include "PIL_time.h" -# include "PIL_time_utildefines.h" -#endif - -/************************************** - * Util functions. * - **************************************/ - -/* Util macro. */ -#define OUT_OF_MEMORY() ((void)printf("WeightVGProximity: Out of memory.\n")) - -typedef struct Vert2GeomData { - /* Read-only data */ - float (*v_cos)[3]; - - const SpaceTransform *loc2trgt; - - BVHTreeFromMesh *treeData[3]; - - /* Write data, but not needing locking (two different threads will never write same index). */ - float *dist[3]; -} Vert2GeomData; - -/** - * Data which is localized to each computed chunk - * (i.e. thread-safe, and with continuous subset of index range). - */ -typedef struct Vert2GeomDataChunk { - /* Read-only data */ - float last_hit_co[3][3]; - bool is_init[3]; -} Vert2GeomDataChunk; - -/** - * Callback used by BLI_task 'for loop' helper. - */ -static void vert2geom_task_cb_ex(void *__restrict userdata, - const int iter, - const TaskParallelTLS *__restrict tls) -{ - Vert2GeomData *data = userdata; - Vert2GeomDataChunk *data_chunk = tls->userdata_chunk; - - float tmp_co[3]; - int i; - - /* Convert the vertex to tree coordinates. */ - copy_v3_v3(tmp_co, data->v_cos[iter]); - BLI_space_transform_apply(data->loc2trgt, tmp_co); - - for (i = 0; i < ARRAY_SIZE(data->dist); i++) { - if (data->dist[i]) { - BVHTreeNearest nearest = {0}; - - /* Note that we use local proximity heuristics (to reduce the nearest search). - * - * If we already had an hit before in same chunk of tasks (i.e. previous vertex by index), - * 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 pruning of the search tree. - */ - nearest.dist_sq = data_chunk->is_init[i] ? - len_squared_v3v3(tmp_co, data_chunk->last_hit_co[i]) : - FLT_MAX; - nearest.index = -1; - - /* Compute and store result. If invalid (-1 idx), keep FLT_MAX dist. */ - BLI_bvhtree_find_nearest(data->treeData[i]->tree, - tmp_co, - &nearest, - data->treeData[i]->nearest_callback, - data->treeData[i]); - data->dist[i][iter] = sqrtf(nearest.dist_sq); - - if (nearest.index != -1) { - copy_v3_v3(data_chunk->last_hit_co[i], nearest.co); - data_chunk->is_init[i] = true; - } - } - } -} - -/** - * Find nearest vertex and/or edge and/or face, for each vertex (adapted from shrinkwrap.c). - */ -static void get_vert2geom_distance(int verts_num, - float (*v_cos)[3], - float *dist_v, - float *dist_e, - float *dist_f, - Mesh *target, - const SpaceTransform *loc2trgt) -{ - Vert2GeomData data = {0}; - Vert2GeomDataChunk data_chunk = {{{0}}}; - - BVHTreeFromMesh treeData_v = {NULL}; - BVHTreeFromMesh treeData_e = {NULL}; - BVHTreeFromMesh treeData_f = {NULL}; - - if (dist_v) { - /* Create a BVH-tree of the given target's verts. */ - BKE_bvhtree_from_mesh_get(&treeData_v, target, BVHTREE_FROM_VERTS, 2); - if (treeData_v.tree == NULL) { - OUT_OF_MEMORY(); - return; - } - } - if (dist_e) { - /* Create a BVH-tree of the given target's edges. */ - BKE_bvhtree_from_mesh_get(&treeData_e, target, BVHTREE_FROM_EDGES, 2); - if (treeData_e.tree == NULL) { - OUT_OF_MEMORY(); - return; - } - } - if (dist_f) { - /* Create a BVH-tree of the given target's faces. */ - BKE_bvhtree_from_mesh_get(&treeData_f, target, BVHTREE_FROM_LOOPTRI, 2); - if (treeData_f.tree == NULL) { - OUT_OF_MEMORY(); - return; - } - } - - data.v_cos = v_cos; - data.loc2trgt = loc2trgt; - data.treeData[0] = &treeData_v; - data.treeData[1] = &treeData_e; - data.treeData[2] = &treeData_f; - data.dist[0] = dist_v; - data.dist[1] = dist_e; - data.dist[2] = dist_f; - - TaskParallelSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = (verts_num > 10000); - settings.userdata_chunk = &data_chunk; - settings.userdata_chunk_size = sizeof(data_chunk); - BLI_task_parallel_range(0, verts_num, &data, vert2geom_task_cb_ex, &settings); - - 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 verts_num, float (*v_cos)[3], float *dist, Object *ob, Object *obr) -{ - /* Vertex and ref object coordinates. */ - float v_wco[3]; - uint i = verts_num; - - 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 optional "smoothing" mapping. - */ -static void do_map(Object *ob, - float *weights, - const int nidx, - const float min_d, - const float max_d, - short mode, - const bool do_invert_mapping, - CurveMapping *cmap) -{ - const float range_inv = 1.0f / (max_d - min_d); /* invert since multiplication is faster */ - uint 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 (do_invert_mapping || mode != MOD_WVG_MAPPING_NONE) { - RNG *rng = NULL; - - if (mode == MOD_WVG_MAPPING_RANDOM) { - rng = BLI_rng_new_srandom(BLI_ghashutil_strhash(ob->id.name + 2)); - } - - weightvg_do_map(nidx, weights, mode, do_invert_mapping, cmap, rng); - - if (rng) { - BLI_rng_free(rng); - } - } -} - -/************************************** - * Modifiers functions. * - **************************************/ -static void initData(ModifierData *md) -{ - WeightVGProximityModifierData *wmd = (WeightVGProximityModifierData *)md; - - BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(wmd, modifier)); - - MEMCPY_STRUCT_AFTER(wmd, DNA_struct_default_get(WeightVGProximityModifierData), modifier); - - wmd->cmap_curve = BKE_curvemapping_add(1, 0.0, 0.0, 1.0, 1.0); - BKE_curvemapping_init(wmd->cmap_curve); -} - -static void freeData(ModifierData *md) -{ - WeightVGProximityModifierData *wmd = (WeightVGProximityModifierData *)md; - BKE_curvemapping_free(wmd->cmap_curve); -} - -static void copyData(const ModifierData *md, ModifierData *target, const int flag) -{ - const WeightVGProximityModifierData *wmd = (const WeightVGProximityModifierData *)md; - WeightVGProximityModifierData *twmd = (WeightVGProximityModifierData *)target; - - BKE_modifier_copydata_generic(md, target, flag); - - twmd->cmap_curve = BKE_curvemapping_copy(wmd->cmap_curve); -} - -static void requiredDataMask(ModifierData *md, CustomData_MeshMasks *r_cddata_masks) -{ - WeightVGProximityModifierData *wmd = (WeightVGProximityModifierData *)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(struct Scene *UNUSED(scene), ModifierData *md) -{ - WeightVGProximityModifierData *wmd = (WeightVGProximityModifierData *)md; - - if (wmd->mask_texture) { - return BKE_texture_dependsOnTime(wmd->mask_texture); - } - return 0; -} - -static void foreachIDLink(ModifierData *md, Object *ob, IDWalkFunc walk, void *userData) -{ - WeightVGProximityModifierData *wmd = (WeightVGProximityModifierData *)md; - - walk(userData, ob, (ID **)&wmd->mask_texture, IDWALK_CB_USER); - walk(userData, ob, (ID **)&wmd->proximity_ob_target, IDWALK_CB_NOP); - 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) -{ - WeightVGProximityModifierData *wmd = (WeightVGProximityModifierData *)md; - bool need_transform_relation = false; - - if (wmd->proximity_ob_target != NULL) { - DEG_add_object_relation( - ctx->node, wmd->proximity_ob_target, DEG_OB_COMP_TRANSFORM, "WeightVGProximity Modifier"); - if (wmd->proximity_ob_target->data != NULL && - wmd->proximity_mode == MOD_WVG_PROXIMITY_GEOMETRY) { - DEG_add_object_relation( - ctx->node, wmd->proximity_ob_target, DEG_OB_COMP_GEOMETRY, "WeightVGProximity Modifier"); - } - need_transform_relation = true; - } - - if (wmd->mask_texture != NULL) { - DEG_add_generic_id_relation(ctx->node, &wmd->mask_texture->id, "WeightVGProximity Modifier"); - - if (wmd->mask_tex_map_obj != NULL && 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, "WeightVGProximity 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, "WeightVGProximity Modifier"); - } -} - -static bool isDisabled(const struct Scene *UNUSED(scene), - ModifierData *md, - bool UNUSED(useRenderParams)) -{ - WeightVGProximityModifierData *wmd = (WeightVGProximityModifierData *)md; - /* If no vertex group, bypass. */ - if (wmd->defgrp_name[0] == '\0') { - return true; - } - /* If no target object, bypass. */ - return (wmd->proximity_ob_target == NULL); -} - -static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mesh) -{ - BLI_assert(mesh != NULL); - - WeightVGProximityModifierData *wmd = (WeightVGProximityModifierData *)md; - MDeformWeight **dw, **tdw; - float(*v_cos)[3] = NULL; /* The vertices coordinates. */ - Object *ob = ctx->object; - Object *obr = NULL; /* Our target object. */ - int defgrp_index; - float *tw = NULL; - float *org_w = NULL; - float *new_w = NULL; - int *tidx, *indices = NULL; - int index_num = 0; - int i; - const bool invert_vgroup_mask = (wmd->proximity_flags & MOD_WVG_PROXIMITY_INVERT_VGROUP_MASK) != - 0; - const bool do_normalize = (wmd->proximity_flags & MOD_WVG_PROXIMITY_WEIGHTS_NORMALIZE) != 0; - /* Flags. */ -#if 0 - const bool do_prev = (wmd->modifier.mode & eModifierMode_DoWeightPreview) != 0; -#endif - -#ifdef USE_TIMEIT - TIMEIT_START(perf); -#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 our target object. */ - obr = wmd->proximity_ob_target; - if (obr == NULL) { - return mesh; - } - - /* Get vgroup idx from its name. */ - defgrp_index = BKE_id_defgroup_name_index(&mesh->id, wmd->defgrp_name); - if (defgrp_index == -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 NULL. */ - /* As this modifier never add vertices to vgroup, just return. */ - if (!has_mdef) { - 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 (all vertices in vgroup), and get their relevant weight. */ - tidx = MEM_malloc_arrayN(verts_num, sizeof(int), "WeightVGProximity Modifier, tidx"); - tw = MEM_malloc_arrayN(verts_num, sizeof(float), "WeightVGProximity Modifier, tw"); - tdw = MEM_malloc_arrayN(verts_num, sizeof(MDeformWeight *), "WeightVGProximity Modifier, tdw"); - for (i = 0; i < verts_num; i++) { - MDeformWeight *_dw = BKE_defvert_find_index(&dvert[i], defgrp_index); - if (_dw) { - tidx[index_num] = i; - tw[index_num] = _dw->weight; - tdw[index_num++] = _dw; - } - } - /* If no vertices found, return org data! */ - if (index_num == 0) { - MEM_freeN(tidx); - MEM_freeN(tw); - MEM_freeN(tdw); - return mesh; - } - if (index_num != verts_num) { - indices = MEM_malloc_arrayN(index_num, sizeof(int), "WeightVGProximity Modifier, indices"); - memcpy(indices, tidx, sizeof(int) * index_num); - org_w = MEM_malloc_arrayN(index_num, sizeof(float), "WeightVGProximity Modifier, org_w"); - memcpy(org_w, tw, sizeof(float) * index_num); - dw = MEM_malloc_arrayN(index_num, sizeof(MDeformWeight *), "WeightVGProximity Modifier, dw"); - memcpy(dw, tdw, sizeof(MDeformWeight *) * index_num); - MEM_freeN(tw); - MEM_freeN(tdw); - } - else { - org_w = tw; - dw = tdw; - } - new_w = MEM_malloc_arrayN(index_num, sizeof(float), "WeightVGProximity Modifier, new_w"); - MEM_freeN(tidx); - - /* Get our vertex coordinates. */ - if (index_num != verts_num) { - float(*tv_cos)[3] = BKE_mesh_vert_coords_alloc(mesh, NULL); - v_cos = MEM_malloc_arrayN(index_num, sizeof(float[3]), "WeightVGProximity Modifier, v_cos"); - for (i = 0; i < index_num; i++) { - copy_v3_v3(v_cos[i], tv_cos[indices[i]]); - } - MEM_freeN(tv_cos); - } - else { - v_cos = BKE_mesh_vert_coords_alloc(mesh, NULL); - } - - /* Compute wanted distances. */ - if (wmd->proximity_mode == MOD_WVG_PROXIMITY_OBJECT) { - const float dist = get_ob2ob_distance(ob, obr); - for (i = 0; i < index_num; i++) { - new_w[i] = dist; - } - } - else if (wmd->proximity_mode == MOD_WVG_PROXIMITY_GEOMETRY) { - const bool use_trgt_verts = (wmd->proximity_flags & MOD_WVG_PROXIMITY_GEOM_VERTS) != 0; - const bool use_trgt_edges = (wmd->proximity_flags & MOD_WVG_PROXIMITY_GEOM_EDGES) != 0; - const bool use_trgt_faces = (wmd->proximity_flags & MOD_WVG_PROXIMITY_GEOM_FACES) != 0; - - if (use_trgt_verts || use_trgt_edges || use_trgt_faces) { - Mesh *target_mesh = BKE_modifier_get_evaluated_mesh_from_evaluated_object(obr); - - /* We must check that we do have a valid target_mesh! */ - if (target_mesh != NULL) { - - /* TODO: edit-mode versions of the BVH lookup functions are available so it could be - * avoided. */ - BKE_mesh_wrapper_ensure_mdata(target_mesh); - - SpaceTransform loc2trgt; - float *dists_v = use_trgt_verts ? MEM_malloc_arrayN(index_num, sizeof(float), "dists_v") : - NULL; - float *dists_e = use_trgt_edges ? MEM_malloc_arrayN(index_num, sizeof(float), "dists_e") : - NULL; - float *dists_f = use_trgt_faces ? MEM_malloc_arrayN(index_num, sizeof(float), "dists_f") : - NULL; - - BLI_SPACE_TRANSFORM_SETUP(&loc2trgt, ob, obr); - get_vert2geom_distance( - index_num, v_cos, dists_v, dists_e, dists_f, target_mesh, &loc2trgt); - for (i = 0; i < index_num; i++) { - new_w[i] = dists_v ? dists_v[i] : FLT_MAX; - if (dists_e) { - new_w[i] = min_ff(dists_e[i], new_w[i]); - } - if (dists_f) { - new_w[i] = min_ff(dists_f[i], new_w[i]); - } - } - - MEM_SAFE_FREE(dists_v); - MEM_SAFE_FREE(dists_e); - MEM_SAFE_FREE(dists_f); - } - /* Else, fall back to default obj2vert behavior. */ - else { - get_vert2ob_distance(index_num, v_cos, new_w, ob, obr); - } - } - else { - get_vert2ob_distance(index_num, v_cos, new_w, ob, obr); - } - } - - /* Map distances to weights. */ - do_map(ob, - new_w, - index_num, - wmd->min_dist, - wmd->max_dist, - wmd->falloff_type, - (wmd->proximity_flags & MOD_WVG_PROXIMITY_INVERT_FALLOFF) != 0, - wmd->cmap_curve); - - /* Do masking. */ - struct Scene *scene = DEG_get_evaluated_scene(ctx->depsgraph); - weightvg_do_mask(ctx, - index_num, - indices, - org_w, - new_w, - ob, - 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 vgroup. Note we never add nor remove vertices from vgroup here. */ - weightvg_update_vg( - dvert, defgrp_index, dw, index_num, indices, org_w, false, 0.0f, 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(dw); - MEM_freeN(v_cos); - MEM_SAFE_FREE(indices); - -#ifdef USE_TIMEIT - TIMEIT_END(perf); -#endif - - mesh->runtime.is_original_bmesh = false; - - /* Return the vgroup-modified mesh. */ - return mesh; -} - -static void panel_draw(const bContext *UNUSED(C), Panel *panel) -{ - uiLayout *col; - uiLayout *layout = panel->layout; - - PointerRNA ob_ptr; - PointerRNA *ptr = modifier_panel_get_property_pointers(panel, &ob_ptr); - - uiLayoutSetPropSep(layout, true); - - uiItemPointerR(layout, ptr, "vertex_group", &ob_ptr, "vertex_groups", NULL, ICON_NONE); - - uiItemR(layout, ptr, "target", 0, NULL, ICON_NONE); - - uiItemS(layout); - - uiItemR(layout, ptr, "proximity_mode", 0, NULL, ICON_NONE); - if (RNA_enum_get(ptr, "proximity_mode") == MOD_WVG_PROXIMITY_GEOMETRY) { - uiItemR(layout, ptr, "proximity_geometry", UI_ITEM_R_EXPAND, IFACE_("Geometry"), ICON_NONE); - } - - col = uiLayoutColumn(layout, true); - uiItemR(col, ptr, "min_dist", 0, NULL, ICON_NONE); - uiItemR(col, ptr, "max_dist", 0, NULL, ICON_NONE); - - uiItemR(layout, ptr, "normalize", 0, NULL, ICON_NONE); -} - -static void falloff_panel_draw(const bContext *UNUSED(C), Panel *panel) -{ - uiLayout *row, *sub; - uiLayout *layout = panel->layout; - - PointerRNA ob_ptr; - PointerRNA *ptr = modifier_panel_get_property_pointers(panel, &ob_ptr); - - uiLayoutSetPropSep(layout, true); - - row = uiLayoutRow(layout, true); - uiItemR(row, ptr, "falloff_type", 0, IFACE_("Type"), ICON_NONE); - sub = uiLayoutRow(row, true); - uiLayoutSetPropSep(sub, false); - uiItemR(row, ptr, "invert_falloff", 0, "", ICON_ARROW_LEFTRIGHT); - if (RNA_enum_get(ptr, "falloff_type") == MOD_WVG_MAPPING_CURVE) { - uiTemplateCurveMapping(layout, ptr, "map_curve", 0, false, false, false, false); - } - 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_WeightVGProximity, panel_draw); - modifier_subpanel_register( - region_type, "falloff", "Falloff", NULL, falloff_panel_draw, panel_type); - modifier_subpanel_register( - region_type, "influence", "Influence", NULL, influence_panel_draw, panel_type); -} - -static void blendWrite(BlendWriter *writer, const ID *UNUSED(id_owner), const ModifierData *md) -{ - const WeightVGProximityModifierData *wmd = (const WeightVGProximityModifierData *)md; - - BLO_write_struct(writer, WeightVGProximityModifierData, wmd); - - if (wmd->cmap_curve) { - BKE_curvemapping_blend_write(writer, wmd->cmap_curve); - } -} - -static void blendRead(BlendDataReader *reader, ModifierData *md) -{ - WeightVGProximityModifierData *wmd = (WeightVGProximityModifierData *)md; - - BLO_read_data_address(reader, &wmd->cmap_curve); - if (wmd->cmap_curve) { - BKE_curvemapping_blend_read(reader, wmd->cmap_curve); - } -} - -ModifierTypeInfo modifierType_WeightVGProximity = { - /* name */ N_("VertexWeightProximity"), - /* structName */ "WeightVGProximityModifierData", - /* structSize */ sizeof(WeightVGProximityModifierData), - /* srna */ &RNA_VertexWeightProximityModifier, - /* type */ eModifierTypeType_NonGeometrical, - /* flags */ eModifierTypeFlag_AcceptsMesh | eModifierTypeFlag_SupportsMapping | - eModifierTypeFlag_SupportsEditmode | eModifierTypeFlag_UsesPreview, - /* icon */ ICON_MOD_VERTEX_WEIGHT, - - /* copyData */ copyData, - - /* deformVerts */ NULL, - /* deformMatrices */ NULL, - /* deformVertsEM */ NULL, - /* deformMatricesEM */ NULL, - /* modifyMesh */ modifyMesh, - /* modifyGeometrySet */ NULL, - - /* initData */ initData, - /* requiredDataMask */ requiredDataMask, - /* freeData */ freeData, - /* isDisabled */ isDisabled, - /* updateDepsgraph */ updateDepsgraph, - /* dependsOnTime */ dependsOnTime, - /* dependsOnNormals */ NULL, - /* foreachIDLink */ foreachIDLink, - /* foreachTexLink */ foreachTexLink, - /* freeRuntimeData */ NULL, - /* panelRegister */ panelRegister, - /* blendWrite */ blendWrite, - /* blendRead */ blendRead, -}; diff --git a/source/blender/modifiers/intern/MOD_weightvgproximity.cc b/source/blender/modifiers/intern/MOD_weightvgproximity.cc new file mode 100644 index 00000000000..faee2b9e6d5 --- /dev/null +++ b/source/blender/modifiers/intern/MOD_weightvgproximity.cc @@ -0,0 +1,770 @@ +/* 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_ghash.h" +#include "BLI_listbase.h" +#include "BLI_math.h" +#include "BLI_rand.h" +#include "BLI_task.h" + +#include "BLT_translation.h" + +#include "DNA_color_types.h" /* CurveMapping. */ +#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_bvhutils.h" +#include "BKE_colortools.h" /* CurveMapping. */ +#include "BKE_context.h" +#include "BKE_curve.h" +#include "BKE_customdata.h" +#include "BKE_deform.h" +#include "BKE_lib_id.h" +#include "BKE_lib_query.h" +#include "BKE_mesh.h" +#include "BKE_mesh_wrapper.h" +#include "BKE_modifier.h" +#include "BKE_screen.h" +#include "BKE_texture.h" /* Texture masking. */ + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "BLO_read_write.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" + +//#define USE_TIMEIT + +#ifdef USE_TIMEIT +# include "PIL_time.h" +# include "PIL_time_utildefines.h" +#endif + +/************************************** + * Util functions. * + **************************************/ + +/* Util macro. */ +#define OUT_OF_MEMORY() ((void)printf("WeightVGProximity: Out of memory.\n")) + +struct Vert2GeomData { + /* Read-only data */ + float (*v_cos)[3]; + + const SpaceTransform *loc2trgt; + + BVHTreeFromMesh *treeData[3]; + + /* Write data, but not needing locking (two different threads will never write same index). */ + float *dist[3]; +}; + +/** + * Data which is localized to each computed chunk + * (i.e. thread-safe, and with continuous subset of index range). + */ +struct Vert2GeomDataChunk { + /* Read-only data */ + float last_hit_co[3][3]; + bool is_init[3]; +}; + +/** + * Callback used by BLI_task 'for loop' helper. + */ +static void vert2geom_task_cb_ex(void *__restrict userdata, + const int iter, + const TaskParallelTLS *__restrict tls) +{ + Vert2GeomData *data = static_cast(userdata); + Vert2GeomDataChunk *data_chunk = static_cast(tls->userdata_chunk); + + float tmp_co[3]; + int i; + + /* Convert the vertex to tree coordinates. */ + copy_v3_v3(tmp_co, data->v_cos[iter]); + BLI_space_transform_apply(data->loc2trgt, tmp_co); + + for (i = 0; i < ARRAY_SIZE(data->dist); i++) { + if (data->dist[i]) { + BVHTreeNearest nearest = {0}; + + /* Note that we use local proximity heuristics (to reduce the nearest search). + * + * If we already had an hit before in same chunk of tasks (i.e. previous vertex by index), + * 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 pruning of the search tree. + */ + nearest.dist_sq = data_chunk->is_init[i] ? + len_squared_v3v3(tmp_co, data_chunk->last_hit_co[i]) : + FLT_MAX; + nearest.index = -1; + + /* Compute and store result. If invalid (-1 idx), keep FLT_MAX dist. */ + BLI_bvhtree_find_nearest(data->treeData[i]->tree, + tmp_co, + &nearest, + data->treeData[i]->nearest_callback, + data->treeData[i]); + data->dist[i][iter] = sqrtf(nearest.dist_sq); + + if (nearest.index != -1) { + copy_v3_v3(data_chunk->last_hit_co[i], nearest.co); + data_chunk->is_init[i] = true; + } + } + } +} + +/** + * Find nearest vertex and/or edge and/or face, for each vertex (adapted from shrinkwrap.c). + */ +static void get_vert2geom_distance(int verts_num, + float (*v_cos)[3], + float *dist_v, + float *dist_e, + float *dist_f, + Mesh *target, + const SpaceTransform *loc2trgt) +{ + Vert2GeomData data{}; + Vert2GeomDataChunk data_chunk = {{{0}}}; + + BVHTreeFromMesh treeData_v = {nullptr}; + BVHTreeFromMesh treeData_e = {nullptr}; + BVHTreeFromMesh treeData_f = {nullptr}; + + if (dist_v) { + /* Create a BVH-tree of the given target's verts. */ + BKE_bvhtree_from_mesh_get(&treeData_v, target, BVHTREE_FROM_VERTS, 2); + if (treeData_v.tree == nullptr) { + OUT_OF_MEMORY(); + return; + } + } + if (dist_e) { + /* Create a BVH-tree of the given target's edges. */ + BKE_bvhtree_from_mesh_get(&treeData_e, target, BVHTREE_FROM_EDGES, 2); + if (treeData_e.tree == nullptr) { + OUT_OF_MEMORY(); + return; + } + } + if (dist_f) { + /* Create a BVH-tree of the given target's faces. */ + BKE_bvhtree_from_mesh_get(&treeData_f, target, BVHTREE_FROM_LOOPTRI, 2); + if (treeData_f.tree == nullptr) { + OUT_OF_MEMORY(); + return; + } + } + + data.v_cos = v_cos; + data.loc2trgt = loc2trgt; + data.treeData[0] = &treeData_v; + data.treeData[1] = &treeData_e; + data.treeData[2] = &treeData_f; + data.dist[0] = dist_v; + data.dist[1] = dist_e; + data.dist[2] = dist_f; + + TaskParallelSettings settings; + BLI_parallel_range_settings_defaults(&settings); + settings.use_threading = (verts_num > 10000); + settings.userdata_chunk = &data_chunk; + settings.userdata_chunk_size = sizeof(data_chunk); + BLI_task_parallel_range(0, verts_num, &data, vert2geom_task_cb_ex, &settings); + + 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 verts_num, float (*v_cos)[3], float *dist, Object *ob, Object *obr) +{ + /* Vertex and ref object coordinates. */ + float v_wco[3]; + uint i = verts_num; + + 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 optional "smoothing" mapping. + */ +static void do_map(Object *ob, + float *weights, + const int nidx, + const float min_d, + const float max_d, + short mode, + const bool do_invert_mapping, + CurveMapping *cmap) +{ + const float range_inv = 1.0f / (max_d - min_d); /* invert since multiplication is faster */ + uint 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 (do_invert_mapping || mode != MOD_WVG_MAPPING_NONE) { + RNG *rng = nullptr; + + if (mode == MOD_WVG_MAPPING_RANDOM) { + rng = BLI_rng_new_srandom(BLI_ghashutil_strhash(ob->id.name + 2)); + } + + weightvg_do_map(nidx, weights, mode, do_invert_mapping, cmap, rng); + + if (rng) { + BLI_rng_free(rng); + } + } +} + +/************************************** + * Modifiers functions. * + **************************************/ +static void initData(ModifierData *md) +{ + WeightVGProximityModifierData *wmd = (WeightVGProximityModifierData *)md; + + BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(wmd, modifier)); + + MEMCPY_STRUCT_AFTER(wmd, DNA_struct_default_get(WeightVGProximityModifierData), modifier); + + wmd->cmap_curve = BKE_curvemapping_add(1, 0.0, 0.0, 1.0, 1.0); + BKE_curvemapping_init(wmd->cmap_curve); +} + +static void freeData(ModifierData *md) +{ + WeightVGProximityModifierData *wmd = (WeightVGProximityModifierData *)md; + BKE_curvemapping_free(wmd->cmap_curve); +} + +static void copyData(const ModifierData *md, ModifierData *target, const int flag) +{ + const WeightVGProximityModifierData *wmd = (const WeightVGProximityModifierData *)md; + WeightVGProximityModifierData *twmd = (WeightVGProximityModifierData *)target; + + BKE_modifier_copydata_generic(md, target, flag); + + twmd->cmap_curve = BKE_curvemapping_copy(wmd->cmap_curve); +} + +static void requiredDataMask(ModifierData *md, CustomData_MeshMasks *r_cddata_masks) +{ + WeightVGProximityModifierData *wmd = (WeightVGProximityModifierData *)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 *UNUSED(scene), ModifierData *md) +{ + WeightVGProximityModifierData *wmd = (WeightVGProximityModifierData *)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) +{ + WeightVGProximityModifierData *wmd = (WeightVGProximityModifierData *)md; + + walk(userData, ob, (ID **)&wmd->mask_texture, IDWALK_CB_USER); + walk(userData, ob, (ID **)&wmd->proximity_ob_target, IDWALK_CB_NOP); + 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) +{ + WeightVGProximityModifierData *wmd = (WeightVGProximityModifierData *)md; + bool need_transform_relation = false; + + if (wmd->proximity_ob_target != nullptr) { + DEG_add_object_relation( + ctx->node, wmd->proximity_ob_target, DEG_OB_COMP_TRANSFORM, "WeightVGProximity Modifier"); + if (wmd->proximity_ob_target->data != nullptr && + wmd->proximity_mode == MOD_WVG_PROXIMITY_GEOMETRY) { + DEG_add_object_relation( + ctx->node, wmd->proximity_ob_target, DEG_OB_COMP_GEOMETRY, "WeightVGProximity Modifier"); + } + need_transform_relation = true; + } + + if (wmd->mask_texture != nullptr) { + DEG_add_generic_id_relation(ctx->node, &wmd->mask_texture->id, "WeightVGProximity 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, "WeightVGProximity 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, "WeightVGProximity Modifier"); + } +} + +static bool isDisabled(const Scene *UNUSED(scene), ModifierData *md, bool UNUSED(useRenderParams)) +{ + WeightVGProximityModifierData *wmd = (WeightVGProximityModifierData *)md; + /* If no vertex group, bypass. */ + if (wmd->defgrp_name[0] == '\0') { + return true; + } + /* If no target object, bypass. */ + return (wmd->proximity_ob_target == nullptr); +} + +static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mesh) +{ + BLI_assert(mesh != nullptr); + + WeightVGProximityModifierData *wmd = (WeightVGProximityModifierData *)md; + MDeformWeight **dw, **tdw; + float(*v_cos)[3] = nullptr; /* The vertices coordinates. */ + Object *ob = ctx->object; + Object *obr = nullptr; /* Our target object. */ + int defgrp_index; + float *tw = nullptr; + float *org_w = nullptr; + float *new_w = nullptr; + int *tidx, *indices = nullptr; + int index_num = 0; + int i; + const bool invert_vgroup_mask = (wmd->proximity_flags & MOD_WVG_PROXIMITY_INVERT_VGROUP_MASK) != + 0; + const bool do_normalize = (wmd->proximity_flags & MOD_WVG_PROXIMITY_WEIGHTS_NORMALIZE) != 0; + /* Flags. */ +#if 0 + const bool do_prev = (wmd->modifier.mode & eModifierMode_DoWeightPreview) != 0; +#endif + +#ifdef USE_TIMEIT + TIMEIT_START(perf); +#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 our target object. */ + obr = wmd->proximity_ob_target; + if (obr == nullptr) { + return mesh; + } + + /* Get vgroup idx from its name. */ + defgrp_index = BKE_id_defgroup_name_index(&mesh->id, wmd->defgrp_name); + if (defgrp_index == -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. */ + /* As this modifier never add vertices to vgroup, just return. */ + if (!has_mdef) { + 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 (all vertices in vgroup), and get their relevant weight. */ + tidx = static_cast(MEM_malloc_arrayN(verts_num, sizeof(int), __func__)); + tw = static_cast(MEM_malloc_arrayN(verts_num, sizeof(float), __func__)); + tdw = static_cast( + MEM_malloc_arrayN(verts_num, sizeof(MDeformWeight *), __func__)); + for (i = 0; i < verts_num; i++) { + MDeformWeight *_dw = BKE_defvert_find_index(&dvert[i], defgrp_index); + if (_dw) { + tidx[index_num] = i; + tw[index_num] = _dw->weight; + tdw[index_num++] = _dw; + } + } + /* If no vertices found, return org data! */ + if (index_num == 0) { + MEM_freeN(tidx); + MEM_freeN(tw); + MEM_freeN(tdw); + return mesh; + } + if (index_num != verts_num) { + indices = static_cast(MEM_malloc_arrayN(index_num, sizeof(int), __func__)); + memcpy(indices, tidx, sizeof(int) * index_num); + org_w = static_cast(MEM_malloc_arrayN(index_num, sizeof(float), __func__)); + memcpy(org_w, tw, sizeof(float) * index_num); + dw = static_cast( + MEM_malloc_arrayN(index_num, sizeof(MDeformWeight *), __func__)); + memcpy(dw, tdw, sizeof(MDeformWeight *) * index_num); + MEM_freeN(tw); + MEM_freeN(tdw); + } + else { + org_w = tw; + dw = tdw; + } + new_w = static_cast(MEM_malloc_arrayN(index_num, sizeof(float), __func__)); + MEM_freeN(tidx); + + /* Get our vertex coordinates. */ + if (index_num != verts_num) { + float(*tv_cos)[3] = BKE_mesh_vert_coords_alloc(mesh, nullptr); + v_cos = static_cast(MEM_malloc_arrayN(index_num, sizeof(float[3]), __func__)); + for (i = 0; i < index_num; i++) { + copy_v3_v3(v_cos[i], tv_cos[indices[i]]); + } + MEM_freeN(tv_cos); + } + else { + v_cos = BKE_mesh_vert_coords_alloc(mesh, nullptr); + } + + /* Compute wanted distances. */ + if (wmd->proximity_mode == MOD_WVG_PROXIMITY_OBJECT) { + const float dist = get_ob2ob_distance(ob, obr); + for (i = 0; i < index_num; i++) { + new_w[i] = dist; + } + } + else if (wmd->proximity_mode == MOD_WVG_PROXIMITY_GEOMETRY) { + const bool use_trgt_verts = (wmd->proximity_flags & MOD_WVG_PROXIMITY_GEOM_VERTS) != 0; + const bool use_trgt_edges = (wmd->proximity_flags & MOD_WVG_PROXIMITY_GEOM_EDGES) != 0; + const bool use_trgt_faces = (wmd->proximity_flags & MOD_WVG_PROXIMITY_GEOM_FACES) != 0; + + if (use_trgt_verts || use_trgt_edges || use_trgt_faces) { + Mesh *target_mesh = BKE_modifier_get_evaluated_mesh_from_evaluated_object(obr); + + /* We must check that we do have a valid target_mesh! */ + if (target_mesh != nullptr) { + + /* TODO: edit-mode versions of the BVH lookup functions are available so it could be + * avoided. */ + BKE_mesh_wrapper_ensure_mdata(target_mesh); + + SpaceTransform loc2trgt; + float *dists_v = use_trgt_verts ? static_cast(MEM_malloc_arrayN( + index_num, sizeof(float), __func__)) : + nullptr; + float *dists_e = use_trgt_edges ? static_cast(MEM_malloc_arrayN( + index_num, sizeof(float), __func__)) : + nullptr; + float *dists_f = use_trgt_faces ? static_cast(MEM_malloc_arrayN( + index_num, sizeof(float), __func__)) : + nullptr; + + BLI_SPACE_TRANSFORM_SETUP(&loc2trgt, ob, obr); + get_vert2geom_distance( + index_num, v_cos, dists_v, dists_e, dists_f, target_mesh, &loc2trgt); + for (i = 0; i < index_num; i++) { + new_w[i] = dists_v ? dists_v[i] : FLT_MAX; + if (dists_e) { + new_w[i] = min_ff(dists_e[i], new_w[i]); + } + if (dists_f) { + new_w[i] = min_ff(dists_f[i], new_w[i]); + } + } + + MEM_SAFE_FREE(dists_v); + MEM_SAFE_FREE(dists_e); + MEM_SAFE_FREE(dists_f); + } + /* Else, fall back to default obj2vert behavior. */ + else { + get_vert2ob_distance(index_num, v_cos, new_w, ob, obr); + } + } + else { + get_vert2ob_distance(index_num, v_cos, new_w, ob, obr); + } + } + + /* Map distances to weights. */ + do_map(ob, + new_w, + index_num, + wmd->min_dist, + wmd->max_dist, + wmd->falloff_type, + (wmd->proximity_flags & MOD_WVG_PROXIMITY_INVERT_FALLOFF) != 0, + wmd->cmap_curve); + + /* Do masking. */ + Scene *scene = DEG_get_evaluated_scene(ctx->depsgraph); + weightvg_do_mask(ctx, + index_num, + indices, + org_w, + new_w, + ob, + 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 vgroup. Note we never add nor remove vertices from vgroup here. */ + weightvg_update_vg( + dvert, defgrp_index, dw, index_num, indices, org_w, false, 0.0f, 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(dw); + MEM_freeN(v_cos); + MEM_SAFE_FREE(indices); + +#ifdef USE_TIMEIT + TIMEIT_END(perf); +#endif + + mesh->runtime.is_original_bmesh = false; + + /* Return the vgroup-modified mesh. */ + return mesh; +} + +static void panel_draw(const bContext *UNUSED(C), Panel *panel) +{ + uiLayout *col; + uiLayout *layout = panel->layout; + + PointerRNA ob_ptr; + PointerRNA *ptr = modifier_panel_get_property_pointers(panel, &ob_ptr); + + uiLayoutSetPropSep(layout, true); + + uiItemPointerR(layout, ptr, "vertex_group", &ob_ptr, "vertex_groups", nullptr, ICON_NONE); + + uiItemR(layout, ptr, "target", 0, nullptr, ICON_NONE); + + uiItemS(layout); + + uiItemR(layout, ptr, "proximity_mode", 0, nullptr, ICON_NONE); + if (RNA_enum_get(ptr, "proximity_mode") == MOD_WVG_PROXIMITY_GEOMETRY) { + uiItemR(layout, ptr, "proximity_geometry", UI_ITEM_R_EXPAND, IFACE_("Geometry"), ICON_NONE); + } + + col = uiLayoutColumn(layout, true); + uiItemR(col, ptr, "min_dist", 0, nullptr, ICON_NONE); + uiItemR(col, ptr, "max_dist", 0, nullptr, ICON_NONE); + + uiItemR(layout, ptr, "normalize", 0, nullptr, ICON_NONE); +} + +static void falloff_panel_draw(const bContext *UNUSED(C), Panel *panel) +{ + uiLayout *row, *sub; + uiLayout *layout = panel->layout; + + PointerRNA ob_ptr; + PointerRNA *ptr = modifier_panel_get_property_pointers(panel, &ob_ptr); + + uiLayoutSetPropSep(layout, true); + + row = uiLayoutRow(layout, true); + uiItemR(row, ptr, "falloff_type", 0, IFACE_("Type"), ICON_NONE); + sub = uiLayoutRow(row, true); + uiLayoutSetPropSep(sub, false); + uiItemR(row, ptr, "invert_falloff", 0, "", ICON_ARROW_LEFTRIGHT); + if (RNA_enum_get(ptr, "falloff_type") == MOD_WVG_MAPPING_CURVE) { + uiTemplateCurveMapping(layout, ptr, "map_curve", 0, false, false, false, false); + } + 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_WeightVGProximity, panel_draw); + modifier_subpanel_register( + region_type, "falloff", "Falloff", nullptr, falloff_panel_draw, panel_type); + modifier_subpanel_register( + region_type, "influence", "Influence", nullptr, influence_panel_draw, panel_type); +} + +static void blendWrite(BlendWriter *writer, const ID *UNUSED(id_owner), const ModifierData *md) +{ + const WeightVGProximityModifierData *wmd = (const WeightVGProximityModifierData *)md; + + BLO_write_struct(writer, WeightVGProximityModifierData, wmd); + + if (wmd->cmap_curve) { + BKE_curvemapping_blend_write(writer, wmd->cmap_curve); + } +} + +static void blendRead(BlendDataReader *reader, ModifierData *md) +{ + WeightVGProximityModifierData *wmd = (WeightVGProximityModifierData *)md; + + BLO_read_data_address(reader, &wmd->cmap_curve); + if (wmd->cmap_curve) { + BKE_curvemapping_blend_read(reader, wmd->cmap_curve); + } +} + +ModifierTypeInfo modifierType_WeightVGProximity = { + /* name */ N_("VertexWeightProximity"), + /* structName */ "WeightVGProximityModifierData", + /* structSize */ sizeof(WeightVGProximityModifierData), + /* srna */ &RNA_VertexWeightProximityModifier, + /* type */ eModifierTypeType_NonGeometrical, + /* flags */ eModifierTypeFlag_AcceptsMesh | eModifierTypeFlag_SupportsMapping | + eModifierTypeFlag_SupportsEditmode | eModifierTypeFlag_UsesPreview, + /* icon */ ICON_MOD_VERTEX_WEIGHT, + + /* copyData */ copyData, + + /* deformVerts */ nullptr, + /* deformMatrices */ nullptr, + /* deformVertsEM */ nullptr, + /* deformMatricesEM */ nullptr, + /* modifyMesh */ modifyMesh, + /* modifyGeometrySet */ nullptr, + + /* initData */ initData, + /* requiredDataMask */ requiredDataMask, + /* freeData */ freeData, + /* isDisabled */ isDisabled, + /* updateDepsgraph */ updateDepsgraph, + /* dependsOnTime */ dependsOnTime, + /* dependsOnNormals */ nullptr, + /* foreachIDLink */ foreachIDLink, + /* foreachTexLink */ foreachTexLink, + /* freeRuntimeData */ nullptr, + /* panelRegister */ panelRegister, + /* blendWrite */ blendWrite, + /* blendRead */ blendRead, +}; -- cgit v1.2.3