Files
test/source/blender/modifiers/intern/MOD_bevel.cc

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

451 lines
15 KiB
C++
Raw Normal View History

/* SPDX-FileCopyrightText: 2005 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup modifiers
*/
2018-06-17 17:04:27 +02:00
#include <algorithm>
#include "MEM_guardedalloc.h"
#include "BLI_math_vector.h"
#include "BLI_utildefines.h"
#include "BLT_translation.h"
#include "DNA_curveprofile_types.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.hh"
#include "BKE_curveprofile.h"
2024-01-29 18:57:16 -05:00
#include "BKE_deform.hh"
#include "BKE_mesh.hh"
2023-11-14 09:30:40 +01:00
#include "BKE_modifier.hh"
#include "BKE_screen.hh"
#include "UI_interface.hh"
#include "UI_resources.hh"
#include "RNA_access.hh"
#include "RNA_prototypes.h"
#include "MOD_ui_common.hh"
#include "MOD_util.hh"
#include "BLO_read_write.hh"
Geometry: add utility to check for bad geometry element index dependence Sometimes .blend files have compatibility issues between Blender versions, because .blend files depended on the specific order of geometry elements generated by some nodes/modifiers (#112746, #113018). While we make guarantees about the order in some places, that is relatively rare, because it makes future improvements much harder. The functionality in this patch makes it easier for users to notice when they depend on things that are not expected to be stable between Blender builds. This is achieved by adding a new global flag which indicates whether some algorithms should randomize their output. The functionality can be toggled on or off by searching for `Set Geometry Randomization`. If there are no differences (or acceptable minor ones) when the flag is on or off, one can be reasonably sure that one does not on unspecified behavior (can't be 100% sure though, because randomization might be missing in some places). If there are big differences, one should consider fixing the file before it comes to an actual breakage in the next Blender version. Currently, the setting is only available when `Developer Extras` is turned on, because the setting is in no menu. With this patch, if we get bug reports with compatibility issues caused by depending on indices, one of the following three cases should always apply: * We actually accidentally broke something, which requires a fix commit. * Turning on geometry randomization shows that the .blend file depends on things it shouldn't depend on. In this case the user has to fix the file. * We are missing geometry randomization somewhere, which requires a fix commit. Pull Request: https://projects.blender.org/blender/blender/pulls/113030
2023-09-29 21:44:36 +02:00
#include "GEO_randomize.hh"
#include "bmesh.hh"
#include "bmesh_tools.hh"
#include "DEG_depsgraph_query.hh"
static void init_data(ModifierData *md)
{
2012-05-06 13:38:33 +00:00
BevelModifierData *bmd = (BevelModifierData *)md;
BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(bmd, modifier));
MEMCPY_STRUCT_AFTER(bmd, DNA_struct_default_get(BevelModifierData), modifier);
bmd->custom_profile = BKE_curveprofile_add(PROF_PRESET_LINE);
}
static void copy_data(const ModifierData *md_src, ModifierData *md_dst, const int flag)
{
const BevelModifierData *bmd_src = (const BevelModifierData *)md_src;
BevelModifierData *bmd_dst = (BevelModifierData *)md_dst;
BKE_modifier_copydata_generic(md_src, md_dst, flag);
bmd_dst->custom_profile = BKE_curveprofile_copy(bmd_src->custom_profile);
}
static void required_data_mask(ModifierData *md, CustomData_MeshMasks *r_cddata_masks)
{
BevelModifierData *bmd = (BevelModifierData *)md;
/* Ask for vertex-groups if we need them. */
if (bmd->defgrp_name[0] != '\0') {
r_cddata_masks->vmask |= CD_MASK_MDEFORMVERT;
}
}
/*
* This calls the new bevel code (added since 2.64)
*/
static Mesh *modify_mesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mesh)
{
Mesh *result;
BMesh *bm;
BMIter iter;
BMEdge *e;
BMVert *v;
float weight, weight2;
int vgroup = -1;
const MDeformVert *dvert = nullptr;
2012-05-06 13:38:33 +00:00
BevelModifierData *bmd = (BevelModifierData *)md;
Cleanup: Internal degrees removal. This patch changes most of the reamining degrees usage in internal code into radians. I let a few which I know off asside, for reasons explained below - and I'm not sure to have found out all of them. WARNING: this introduces forward incompatibility, which means files saved from this version won't open 100% correctly in previous versions (a few angle properties would use radians values as degrees...). Details: - Data: -- Lamp.spotsize: Game engine exposed this setting in degrees, to not break the API here I kept it as such (using getter/setter functions), still using radians internally. -- Mesh.smoothresh: Didn't touch to this one, as we will hopefully replace it completely by loop normals currently in dev. - Modifiers: -- EdgeSplitModifierData.split_angle, BevelModifierData.bevel_angle: Done. - Postprocessing: -- WipeVars.angle (sequencer's effect), NodeBokehImage.angle, NodeBoxMask.rotation, NodeEllipseMask.rotation: Done. - BGE: -- bConstraintActuator: Orientation type done (the minloc[0] & maxloc[0] cases). Did not touch to 'limit location' type, it can also limit rotation, but it exposes through RNA the same limit_min/limit_max, which hence can be either distance or angle values, depending on the mode. Will leave this to BGE team. -- bSoundActuator.cone_outer_angle_3d, bSoundActuator.cone_inner_angle_3d: Done (note I kept degrees in BGE itself, as it seems this is the expected value here...). -- bRadarSensor.angle: Done. Reviewers: brecht, campbellbarton, sergey, gaiaclary, dfelinto, moguri, jbakker, lukastoenne, howardt Reviewed By: brecht, campbellbarton, sergey, gaiaclary, moguri, jbakker, lukastoenne, howardt Thanks to all! Differential Revision: http://developer.blender.org/D59
2013-12-03 20:09:25 +01:00
const float threshold = cosf(bmd->bevel_angle + 0.000000175f);
const bool do_clamp = !(bmd->flags & MOD_BEVEL_OVERLAP_OK);
const int offset_type = bmd->val_flags;
const int profile_type = bmd->profile_type;
const float value = bmd->value;
const int mat = std::clamp(int(bmd->mat), -1, ctx->object->totcol - 1);
const bool loop_slide = (bmd->flags & MOD_BEVEL_EVEN_WIDTHS) == 0;
const bool mark_seam = (bmd->edge_flags & MOD_BEVEL_MARK_SEAM);
const bool mark_sharp = (bmd->edge_flags & MOD_BEVEL_MARK_SHARP);
bool harden_normals = (bmd->flags & MOD_BEVEL_HARDEN_NORMALS);
const int face_strength_mode = bmd->face_str_mode;
const int miter_outer = bmd->miter_outer;
const int miter_inner = bmd->miter_inner;
const float spread = bmd->spread;
const bool invert_vgroup = (bmd->flags & MOD_BEVEL_INVERT_VGROUP) != 0;
BMeshCreateParams create_params{};
BMeshFromMeshParams convert_params{};
convert_params.calc_face_normal = true;
convert_params.calc_vert_normal = true;
convert_params.add_key_index = false;
convert_params.use_shapekey = false;
convert_params.active_shapekey = 0;
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);
if ((bmd->lim_flags & MOD_BEVEL_VGROUP) && bmd->defgrp_name[0]) {
2018-06-29 19:02:19 +02:00
MOD_get_vgroup(ctx->object, mesh, bmd->defgrp_name, &dvert, &vgroup);
}
const int bweight_offset_vert = CustomData_get_offset_named(
&bm->vdata, CD_PROP_FLOAT, "bevel_weight_vert");
const int bweight_offset_edge = CustomData_get_offset_named(
&bm->edata, CD_PROP_FLOAT, "bevel_weight_edge");
if (bmd->affect_type == MOD_BEVEL_AFFECT_VERTICES) {
2013-08-17 08:21:40 +00:00
BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
if (bmd->lim_flags & MOD_BEVEL_WEIGHT) {
weight = bweight_offset_vert == -1 ? 0.0f : BM_ELEM_CD_GET_FLOAT(v, bweight_offset_vert);
if (weight == 0.0f) {
continue;
}
}
else if (vgroup != -1) {
weight = invert_vgroup ?
1.0f -
BKE_defvert_array_find_weight_safe(dvert, BM_elem_index_get(v), vgroup) :
BKE_defvert_array_find_weight_safe(dvert, BM_elem_index_get(v), vgroup);
/* Check is against 0.5 rather than != 0.0 because cascaded bevel modifiers will
* interpolate weights for newly created vertices, and may cause unexpected "selection" */
if (weight < 0.5f) {
continue;
}
}
BM_elem_flag_enable(v, BM_ELEM_TAG);
}
}
else if (bmd->lim_flags & MOD_BEVEL_ANGLE) {
BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
/* check for 1 edge having 2 face users */
BMLoop *l_a, *l_b;
if (BM_edge_loop_pair(e, &l_a, &l_b)) {
if (dot_v3v3(l_a->f->no, l_b->f->no) < threshold) {
BM_elem_flag_enable(e, BM_ELEM_TAG);
BM_elem_flag_enable(e->v1, BM_ELEM_TAG);
BM_elem_flag_enable(e->v2, BM_ELEM_TAG);
}
}
}
}
else {
/* crummy, is there a way just to operator on all? - campbell */
BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
if (BM_edge_is_manifold(e)) {
if (bmd->lim_flags & MOD_BEVEL_WEIGHT) {
weight = bweight_offset_edge == -1 ? 0.0f : BM_ELEM_CD_GET_FLOAT(e, bweight_offset_edge);
if (weight == 0.0f) {
continue;
}
}
else if (vgroup != -1) {
weight = invert_vgroup ?
1.0f - BKE_defvert_array_find_weight_safe(
dvert, BM_elem_index_get(e->v1), vgroup) :
BKE_defvert_array_find_weight_safe(dvert, BM_elem_index_get(e->v1), vgroup);
weight2 = invert_vgroup ? 1.0f - BKE_defvert_array_find_weight_safe(
dvert, BM_elem_index_get(e->v2), vgroup) :
BKE_defvert_array_find_weight_safe(
dvert, BM_elem_index_get(e->v2), vgroup);
if (weight < 0.5f || weight2 < 0.5f) {
continue;
}
}
BM_elem_flag_enable(e, BM_ELEM_TAG);
BM_elem_flag_enable(e->v1, BM_ELEM_TAG);
BM_elem_flag_enable(e->v2, BM_ELEM_TAG);
}
}
}
BM_mesh_bevel(bm,
value,
offset_type,
profile_type,
bmd->res,
bmd->profile,
bmd->affect_type,
bmd->lim_flags & MOD_BEVEL_WEIGHT,
do_clamp,
dvert,
vgroup,
mat,
loop_slide,
mark_seam,
mark_sharp,
harden_normals,
face_strength_mode,
miter_outer,
miter_inner,
spread,
bmd->custom_profile,
bmd->vmesh_method);
result = BKE_mesh_from_bmesh_for_eval_nomain(bm, nullptr, mesh);
2023-12-10 16:33:54 +11:00
/* Make sure we never allocated these. */
BLI_assert(bm->vtoolflagpool == nullptr && bm->etoolflagpool == nullptr &&
bm->ftoolflagpool == nullptr);
BM_mesh_free(bm);
Geometry: add utility to check for bad geometry element index dependence Sometimes .blend files have compatibility issues between Blender versions, because .blend files depended on the specific order of geometry elements generated by some nodes/modifiers (#112746, #113018). While we make guarantees about the order in some places, that is relatively rare, because it makes future improvements much harder. The functionality in this patch makes it easier for users to notice when they depend on things that are not expected to be stable between Blender builds. This is achieved by adding a new global flag which indicates whether some algorithms should randomize their output. The functionality can be toggled on or off by searching for `Set Geometry Randomization`. If there are no differences (or acceptable minor ones) when the flag is on or off, one can be reasonably sure that one does not on unspecified behavior (can't be 100% sure though, because randomization might be missing in some places). If there are big differences, one should consider fixing the file before it comes to an actual breakage in the next Blender version. Currently, the setting is only available when `Developer Extras` is turned on, because the setting is in no menu. With this patch, if we get bug reports with compatibility issues caused by depending on indices, one of the following three cases should always apply: * We actually accidentally broke something, which requires a fix commit. * Turning on geometry randomization shows that the .blend file depends on things it shouldn't depend on. In this case the user has to fix the file. * We are missing geometry randomization somewhere, which requires a fix commit. Pull Request: https://projects.blender.org/blender/blender/pulls/113030
2023-09-29 21:44:36 +02:00
blender::geometry::debug_randomize_mesh_order(result);
return result;
}
static bool depends_on_normals(ModifierData * /*md*/)
{
return true;
}
static void free_data(ModifierData *md)
{
BevelModifierData *bmd = (BevelModifierData *)md;
BKE_curveprofile_free(bmd->custom_profile);
}
static bool is_disabled(const Scene * /*scene*/, ModifierData *md, bool /*use_render_params*/)
{
BevelModifierData *bmd = (BevelModifierData *)md;
return (bmd->value == 0.0f);
}
static void panel_draw(const bContext * /*C*/, Panel *panel)
{
uiLayout *col, *sub;
uiLayout *layout = panel->layout;
PointerRNA ob_ptr;
PointerRNA *ptr = modifier_panel_get_property_pointers(panel, &ob_ptr);
bool edge_bevel = RNA_enum_get(ptr, "affect") != MOD_BEVEL_AFFECT_VERTICES;
uiItemR(layout, ptr, "affect", UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
uiLayoutSetPropSep(layout, true);
col = uiLayoutColumn(layout, false);
uiItemR(col, ptr, "offset_type", UI_ITEM_NONE, nullptr, ICON_NONE);
if (RNA_enum_get(ptr, "offset_type") == BEVEL_AMT_PERCENT) {
uiItemR(col, ptr, "width_pct", UI_ITEM_NONE, nullptr, ICON_NONE);
}
else {
uiItemR(col, ptr, "width", UI_ITEM_NONE, IFACE_("Amount"), ICON_NONE);
}
uiItemR(layout, ptr, "segments", UI_ITEM_NONE, nullptr, ICON_NONE);
uiItemS(layout);
col = uiLayoutColumn(layout, false);
uiItemR(col, ptr, "limit_method", UI_ITEM_NONE, nullptr, ICON_NONE);
int limit_method = RNA_enum_get(ptr, "limit_method");
if (limit_method == MOD_BEVEL_ANGLE) {
sub = uiLayoutColumn(col, false);
uiLayoutSetActive(sub, edge_bevel);
uiItemR(col, ptr, "angle_limit", UI_ITEM_NONE, nullptr, ICON_NONE);
}
else if (limit_method == MOD_BEVEL_VGROUP) {
modifier_vgroup_ui(col, ptr, &ob_ptr, "vertex_group", "invert_vertex_group", nullptr);
}
modifier_panel_end(layout, ptr);
}
static void profile_panel_draw(const bContext * /*C*/, Panel *panel)
{
uiLayout *row;
uiLayout *layout = panel->layout;
PointerRNA *ptr = modifier_panel_get_property_pointers(panel, nullptr);
int profile_type = RNA_enum_get(ptr, "profile_type");
int miter_inner = RNA_enum_get(ptr, "miter_inner");
int miter_outer = RNA_enum_get(ptr, "miter_outer");
bool edge_bevel = RNA_enum_get(ptr, "affect") != MOD_BEVEL_AFFECT_VERTICES;
uiItemR(layout, ptr, "profile_type", UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
uiLayoutSetPropSep(layout, true);
if (ELEM(profile_type, MOD_BEVEL_PROFILE_SUPERELLIPSE, MOD_BEVEL_PROFILE_CUSTOM)) {
row = uiLayoutRow(layout, false);
uiLayoutSetActive(
row,
profile_type == MOD_BEVEL_PROFILE_SUPERELLIPSE ||
(profile_type == MOD_BEVEL_PROFILE_CUSTOM && edge_bevel &&
!((miter_inner == MOD_BEVEL_MITER_SHARP) && (miter_outer == MOD_BEVEL_MITER_SHARP))));
uiItemR(row,
ptr,
"profile",
UI_ITEM_R_SLIDER,
(profile_type == MOD_BEVEL_PROFILE_SUPERELLIPSE) ? IFACE_("Shape") :
IFACE_("Miter Shape"),
ICON_NONE);
if (profile_type == MOD_BEVEL_PROFILE_CUSTOM) {
uiLayout *sub = uiLayoutColumn(layout, false);
uiLayoutSetPropDecorate(sub, false);
uiTemplateCurveProfile(sub, ptr, "custom_profile");
}
}
}
static void geometry_panel_draw(const bContext * /*C*/, Panel *panel)
{
uiLayout *row;
uiLayout *layout = panel->layout;
PointerRNA *ptr = modifier_panel_get_property_pointers(panel, nullptr);
bool edge_bevel = RNA_enum_get(ptr, "affect") != MOD_BEVEL_AFFECT_VERTICES;
uiLayoutSetPropSep(layout, true);
row = uiLayoutRow(layout, false);
uiLayoutSetActive(row, edge_bevel);
uiItemR(row, ptr, "miter_outer", UI_ITEM_NONE, IFACE_("Miter Outer"), ICON_NONE);
row = uiLayoutRow(layout, false);
uiLayoutSetActive(row, edge_bevel);
uiItemR(row, ptr, "miter_inner", UI_ITEM_NONE, IFACE_("Inner"), ICON_NONE);
if (RNA_enum_get(ptr, "miter_inner") == BEVEL_MITER_ARC) {
row = uiLayoutRow(layout, false);
uiLayoutSetActive(row, edge_bevel);
uiItemR(row, ptr, "spread", UI_ITEM_NONE, nullptr, ICON_NONE);
}
uiItemS(layout);
row = uiLayoutRow(layout, false);
uiLayoutSetActive(row, edge_bevel);
uiItemR(row, ptr, "vmesh_method", UI_ITEM_NONE, IFACE_("Intersections"), ICON_NONE);
uiItemR(layout, ptr, "use_clamp_overlap", UI_ITEM_NONE, nullptr, ICON_NONE);
row = uiLayoutRow(layout, false);
uiLayoutSetActive(row, edge_bevel);
uiItemR(row, ptr, "loop_slide", UI_ITEM_NONE, nullptr, ICON_NONE);
}
static void shading_panel_draw(const bContext * /*C*/, Panel *panel)
{
uiLayout *col;
uiLayout *layout = panel->layout;
PointerRNA *ptr = modifier_panel_get_property_pointers(panel, nullptr);
bool edge_bevel = RNA_enum_get(ptr, "affect") != MOD_BEVEL_AFFECT_VERTICES;
uiLayoutSetPropSep(layout, true);
uiItemR(layout, ptr, "harden_normals", UI_ITEM_NONE, nullptr, ICON_NONE);
col = uiLayoutColumnWithHeading(layout, true, IFACE_("Mark"));
uiLayoutSetActive(col, edge_bevel);
uiItemR(col, ptr, "mark_seam", UI_ITEM_NONE, IFACE_("Seam"), ICON_NONE);
uiItemR(col, ptr, "mark_sharp", UI_ITEM_NONE, IFACE_("Sharp"), ICON_NONE);
uiItemR(layout, ptr, "material", UI_ITEM_NONE, nullptr, ICON_NONE);
uiItemR(layout, ptr, "face_strength_mode", UI_ITEM_NONE, nullptr, ICON_NONE);
}
static void panel_register(ARegionType *region_type)
{
PanelType *panel_type = modifier_panel_register(region_type, eModifierType_Bevel, panel_draw);
modifier_subpanel_register(
region_type, "profile", "Profile", nullptr, profile_panel_draw, panel_type);
modifier_subpanel_register(
region_type, "geometry", "Geometry", nullptr, geometry_panel_draw, panel_type);
modifier_subpanel_register(
region_type, "shading", "Shading", nullptr, shading_panel_draw, panel_type);
}
static void blend_write(BlendWriter *writer, const ID * /*id_owner*/, const ModifierData *md)
{
const BevelModifierData *bmd = (const BevelModifierData *)md;
BLO_write_struct(writer, BevelModifierData, bmd);
if (bmd->custom_profile) {
BKE_curveprofile_blend_write(writer, bmd->custom_profile);
}
}
static void blend_read(BlendDataReader *reader, ModifierData *md)
{
BevelModifierData *bmd = (BevelModifierData *)md;
BLO_read_data_address(reader, &bmd->custom_profile);
if (bmd->custom_profile) {
BKE_curveprofile_blend_read(reader, bmd->custom_profile);
}
}
ModifierTypeInfo modifierType_Bevel = {
/*idname*/ "Bevel",
/*name*/ N_("Bevel"),
/*struct_name*/ "BevelModifierData",
/*struct_size*/ sizeof(BevelModifierData),
/*srna*/ &RNA_BevelModifier,
/*type*/ ModifierTypeType::Constructive,
/*flags*/ eModifierTypeFlag_AcceptsMesh | eModifierTypeFlag_SupportsEditmode |
2018-07-11 22:36:44 +05:30
eModifierTypeFlag_EnableInEditmode | eModifierTypeFlag_AcceptsCVs,
/*icon*/ ICON_MOD_BEVEL,
/*copy_data*/ copy_data,
/*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*/ free_data,
/*is_disabled*/ is_disabled,
/*update_depsgraph*/ nullptr,
/*depends_on_time*/ nullptr,
/*depends_on_normals*/ depends_on_normals,
/*foreach_ID_link*/ nullptr,
/*foreach_tex_link*/ nullptr,
/*free_runtime_data*/ nullptr,
/*panel_register*/ panel_register,
/*blend_write*/ blend_write,
/*blend_read*/ blend_read,
/*foreach_cache*/ nullptr,
};