Files
test/source/blender/modifiers/intern/MOD_decimate.cc
Brecht Van Lommel ee1a460f42 Revert "Refactor: Add and use MEMCPY_STRUCT_AFTER_CHECKED"
Needs more review, see #138830.

This reverts commit 5ac631d02b.
2025-05-20 17:32:49 +02:00

308 lines
9.0 KiB
C++

/* SPDX-FileCopyrightText: 2005 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup modifiers
*/
#include "BLI_string.h"
#include "BLI_utildefines.h"
#include "BLT_translation.hh"
#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 "MEM_guardedalloc.h"
#include "BKE_deform.hh"
#include "BKE_mesh.hh"
#include "UI_interface.hh"
#include "UI_resources.hh"
#include "RNA_access.hh"
#include "RNA_prototypes.hh"
#include "DEG_depsgraph_query.hh"
#include "GEO_randomize.hh"
#include "bmesh.hh"
#include "bmesh_tools.hh"
// #define USE_TIMEIT
#ifdef USE_TIMEIT
# include "BLI_time.h"
# include "BLI_time_utildefines.h"
#endif
#include "MOD_ui_common.hh"
#include "MOD_util.hh"
static void init_data(ModifierData *md)
{
DecimateModifierData *dmd = (DecimateModifierData *)md;
BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(dmd, modifier));
MEMCPY_STRUCT_AFTER(dmd, DNA_struct_default_get(DecimateModifierData), modifier);
}
static void required_data_mask(ModifierData *md, CustomData_MeshMasks *r_cddata_masks)
{
DecimateModifierData *dmd = (DecimateModifierData *)md;
/* Ask for vertex-groups if we need them. */
if (dmd->defgrp_name[0] != '\0' && (dmd->defgrp_factor > 0.0f)) {
r_cddata_masks->vmask |= CD_MASK_MDEFORMVERT;
}
}
static DecimateModifierData *getOriginalModifierData(const DecimateModifierData *dmd,
const ModifierEvalContext *ctx)
{
Object *ob_orig = DEG_get_original(ctx->object);
return (DecimateModifierData *)BKE_modifiers_findby_name(ob_orig, dmd->modifier.name);
}
static void updateFaceCount(const ModifierEvalContext *ctx,
DecimateModifierData *dmd,
int face_count)
{
dmd->face_count = face_count;
if (DEG_is_active(ctx->depsgraph)) {
/* update for display only */
DecimateModifierData *dmd_orig = getOriginalModifierData(dmd, ctx);
dmd_orig->face_count = face_count;
}
}
static Mesh *modify_mesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *meshData)
{
DecimateModifierData *dmd = (DecimateModifierData *)md;
Mesh *mesh = meshData, *result = nullptr;
BMesh *bm;
bool calc_vert_normal;
bool calc_face_normal;
float *vweights = nullptr;
#ifdef USE_TIMEIT
TIMEIT_START(decim);
#endif
/* Set up front so we don't show invalid info in the UI. */
updateFaceCount(ctx, dmd, mesh->faces_num);
switch (dmd->mode) {
case MOD_DECIM_MODE_COLLAPSE:
if (dmd->percent == 1.0f) {
return mesh;
}
calc_face_normal = true;
calc_vert_normal = true;
break;
case MOD_DECIM_MODE_UNSUBDIV:
if (dmd->iter == 0) {
return mesh;
}
calc_face_normal = false;
calc_vert_normal = false;
break;
case MOD_DECIM_MODE_DISSOLVE:
if (dmd->angle == 0.0f) {
return mesh;
}
calc_face_normal = true;
calc_vert_normal = false;
break;
default:
return mesh;
}
if (dmd->face_count <= 3) {
BKE_modifier_set_error(ctx->object, md, "Modifier requires more than 3 input faces");
return mesh;
}
if (dmd->mode == MOD_DECIM_MODE_COLLAPSE) {
if (dmd->defgrp_name[0] && (dmd->defgrp_factor > 0.0f)) {
const MDeformVert *dvert;
int defgrp_index;
MOD_get_vgroup(ctx->object, mesh, dmd->defgrp_name, &dvert, &defgrp_index);
if (dvert) {
const uint vert_tot = mesh->verts_num;
uint i;
vweights = MEM_malloc_arrayN<float>(vert_tot, __func__);
if (dmd->flag & MOD_DECIM_FLAG_INVERT_VGROUP) {
for (i = 0; i < vert_tot; i++) {
vweights[i] = 1.0f - BKE_defvert_find_weight(&dvert[i], defgrp_index);
}
}
else {
for (i = 0; i < vert_tot; i++) {
vweights[i] = BKE_defvert_find_weight(&dvert[i], defgrp_index);
}
}
}
}
}
BMeshCreateParams create_params{};
BMeshFromMeshParams convert_params{};
convert_params.calc_face_normal = calc_face_normal;
convert_params.calc_vert_normal = calc_vert_normal;
convert_params.cd_mask_extra.vmask = CD_MASK_ORIGINDEX;
convert_params.cd_mask_extra.emask = CD_MASK_ORIGINDEX;
convert_params.cd_mask_extra.pmask = CD_MASK_ORIGINDEX;
bm = BKE_mesh_to_bmesh_ex(mesh, &create_params, &convert_params);
switch (dmd->mode) {
case MOD_DECIM_MODE_COLLAPSE: {
const bool do_triangulate = (dmd->flag & MOD_DECIM_FLAG_TRIANGULATE) != 0;
const int symmetry_axis = (dmd->flag & MOD_DECIM_FLAG_SYMMETRY) ? dmd->symmetry_axis : -1;
const float symmetry_eps = 0.00002f;
BM_mesh_decimate_collapse(bm,
dmd->percent,
vweights,
dmd->defgrp_factor,
do_triangulate,
symmetry_axis,
symmetry_eps);
break;
}
case MOD_DECIM_MODE_UNSUBDIV: {
BM_mesh_decimate_unsubdivide(bm, dmd->iter);
break;
}
case MOD_DECIM_MODE_DISSOLVE: {
const bool do_dissolve_boundaries = (dmd->flag & MOD_DECIM_FLAG_ALL_BOUNDARY_VERTS) != 0;
BM_mesh_decimate_dissolve(bm, dmd->angle, do_dissolve_boundaries, (BMO_Delimit)dmd->delimit);
break;
}
}
if (vweights) {
MEM_freeN(vweights);
}
updateFaceCount(ctx, dmd, bm->totface);
/* Make sure we never allocated these. */
BLI_assert(bm->vtoolflagpool == nullptr && bm->etoolflagpool == nullptr &&
bm->ftoolflagpool == nullptr);
result = BKE_mesh_from_bmesh_for_eval_nomain(bm, nullptr, mesh);
BM_mesh_free(bm);
blender::geometry::debug_randomize_mesh_order(result);
#ifdef USE_TIMEIT
TIMEIT_END(decim);
#endif
return result;
}
static void panel_draw(const bContext * /*C*/, Panel *panel)
{
uiLayout *sub, *row;
uiLayout *layout = panel->layout;
PointerRNA ob_ptr;
PointerRNA *ptr = modifier_panel_get_property_pointers(panel, &ob_ptr);
int decimate_type = RNA_enum_get(ptr, "decimate_type");
char count_info[64];
SNPRINTF(count_info, RPT_("Face Count: %d"), RNA_int_get(ptr, "face_count"));
layout->prop(ptr, "decimate_type", UI_ITEM_R_EXPAND, std::nullopt, ICON_NONE);
uiLayoutSetPropSep(layout, true);
if (decimate_type == MOD_DECIM_MODE_COLLAPSE) {
layout->prop(ptr, "ratio", UI_ITEM_R_SLIDER, std::nullopt, ICON_NONE);
row = &layout->row(true, IFACE_("Symmetry"));
uiLayoutSetPropDecorate(row, false);
sub = &row->row(true);
sub->prop(ptr, "use_symmetry", UI_ITEM_NONE, "", ICON_NONE);
sub = &sub->row(true);
uiLayoutSetActive(sub, RNA_boolean_get(ptr, "use_symmetry"));
sub->prop(ptr, "symmetry_axis", UI_ITEM_R_EXPAND, std::nullopt, ICON_NONE);
uiItemDecoratorR(row, ptr, "symmetry_axis", 0);
layout->prop(ptr, "use_collapse_triangulate", UI_ITEM_NONE, std::nullopt, ICON_NONE);
modifier_vgroup_ui(layout, ptr, &ob_ptr, "vertex_group", "invert_vertex_group", std::nullopt);
sub = &layout->row(true);
bool has_vertex_group = RNA_string_length(ptr, "vertex_group") != 0;
uiLayoutSetActive(sub, has_vertex_group);
sub->prop(ptr, "vertex_group_factor", UI_ITEM_NONE, std::nullopt, ICON_NONE);
}
else if (decimate_type == MOD_DECIM_MODE_UNSUBDIV) {
layout->prop(ptr, "iterations", UI_ITEM_NONE, std::nullopt, ICON_NONE);
}
else { /* decimate_type == MOD_DECIM_MODE_DISSOLVE. */
layout->prop(ptr, "angle_limit", UI_ITEM_NONE, std::nullopt, ICON_NONE);
uiLayout *col = &layout->column(false);
col->prop(ptr, "delimit", UI_ITEM_NONE, std::nullopt, ICON_NONE);
layout->prop(ptr, "use_dissolve_boundaries", UI_ITEM_NONE, std::nullopt, ICON_NONE);
}
layout->label(count_info, ICON_NONE);
modifier_error_message_draw(layout, ptr);
}
static void panel_register(ARegionType *region_type)
{
modifier_panel_register(region_type, eModifierType_Decimate, panel_draw);
}
ModifierTypeInfo modifierType_Decimate = {
/*idname*/ "Decimate",
/*name*/ N_("Decimate"),
/*struct_name*/ "DecimateModifierData",
/*struct_size*/ sizeof(DecimateModifierData),
/*srna*/ &RNA_DecimateModifier,
/*type*/ ModifierTypeType::Nonconstructive,
/*flags*/ eModifierTypeFlag_AcceptsMesh | eModifierTypeFlag_AcceptsCVs,
/*icon*/ ICON_MOD_DECIM,
/*copy_data*/ BKE_modifier_copydata_generic,
/*deform_verts*/ nullptr,
/*deform_matrices*/ nullptr,
/*deform_verts_EM*/ nullptr,
/*deform_matrices_EM*/ nullptr,
/*modify_mesh*/ modify_mesh,
/*modify_geometry_set*/ nullptr,
/*init_data*/ init_data,
/*required_data_mask*/ required_data_mask,
/*free_data*/ nullptr,
/*is_disabled*/ nullptr,
/*update_depsgraph*/ nullptr,
/*depends_on_time*/ nullptr,
/*depends_on_normals*/ nullptr,
/*foreach_ID_link*/ nullptr,
/*foreach_tex_link*/ nullptr,
/*free_runtime_data*/ nullptr,
/*panel_register*/ panel_register,
/*blend_write*/ nullptr,
/*blend_read*/ nullptr,
/*foreach_cache*/ nullptr,
};