Fix #114244: Smooth operators destroy sharp edge tags

After the replacement of auto smooth with a modifier, sharp edges are
always used, so the "shade smooth", "shade flat", and "smooth by angle"
operators cleared the attribute. However, often users spend significant
time manually tagging edges sharp, and the operators make it too easy to
lose that data.

To keep the old behavior by default, add an option called "Keep Sharp
Edges". Though this can make the operators "ineffective" at their goal
of changing the way the meshes look, or result in redundant data stored
on the mesh, it's a much safer default, especially as users get used to
the new workflow.

Pull Request: https://projects.blender.org/blender/blender/pulls/117069
This commit is contained in:
Hans Goudey 2024-01-15 14:05:24 +01:00 committed by Hans Goudey
parent 2975abbf3e
commit f9fbf832f5
3 changed files with 41 additions and 23 deletions

@ -319,8 +319,8 @@ void mesh_vert_normals_assign(Mesh &mesh, Span<float3> vert_normals);
/** Set mesh vertex normals to known-correct values, avoiding future lazy computation. */
void mesh_vert_normals_assign(Mesh &mesh, Vector<float3> vert_normals);
void mesh_smooth_set(Mesh &mesh, bool use_smooth);
void mesh_sharp_edges_set_from_angle(Mesh &mesh, float angle);
void mesh_smooth_set(Mesh &mesh, bool use_smooth, bool keep_sharp_edges = false);
void mesh_sharp_edges_set_from_angle(Mesh &mesh, float angle, bool keep_sharp_edges = false);
/** Make edge and face visibility consistent with vertices. */
void mesh_hide_vert_flush(Mesh &mesh);

@ -1128,34 +1128,34 @@ void BKE_mesh_material_remap(Mesh *mesh, const uint *remap, uint remap_len)
namespace blender::bke {
void mesh_smooth_set(Mesh &mesh, const bool use_smooth)
void mesh_smooth_set(Mesh &mesh, const bool use_smooth, const bool keep_sharp_edges)
{
MutableAttributeAccessor attributes = mesh.attributes_for_write();
if (use_smooth) {
if (!keep_sharp_edges) {
attributes.remove("sharp_edge");
attributes.remove("sharp_face");
}
else {
attributes.remove("sharp_edge");
SpanAttributeWriter<bool> sharp_faces = attributes.lookup_or_add_for_write_only_span<bool>(
"sharp_face", AttrDomain::Face);
sharp_faces.span.fill(true);
sharp_faces.finish();
attributes.remove("sharp_face");
if (!use_smooth) {
attributes.add<bool>("sharp_face",
AttrDomain::Face,
AttributeInitVArray(VArray<bool>::ForSingle(true, mesh.faces_num)));
}
}
void mesh_sharp_edges_set_from_angle(Mesh &mesh, const float angle)
void mesh_sharp_edges_set_from_angle(Mesh &mesh, const float angle, const bool keep_sharp_edges)
{
MutableAttributeAccessor attributes = mesh.attributes_for_write();
if (angle >= M_PI) {
attributes.remove("sharp_edge");
attributes.remove("sharp_face");
mesh_smooth_set(mesh, true, keep_sharp_edges);
return;
}
if (angle == 0.0f) {
mesh_smooth_set(mesh, false);
mesh_smooth_set(mesh, false, keep_sharp_edges);
return;
}
if (!keep_sharp_edges) {
attributes.remove("sharp_edge");
}
SpanAttributeWriter<bool> sharp_edges = attributes.lookup_or_add_for_write_span<bool>(
"sharp_edge", AttrDomain::Edge);
const VArraySpan<bool> sharp_faces = *attributes.lookup<bool>("sharp_face", AttrDomain::Face);

@ -1616,12 +1616,12 @@ static int shade_smooth_exec(bContext *C, wmOperator *op)
bool changed = false;
if (ob->type == OB_MESH) {
bke::mesh_smooth_set(*static_cast<Mesh *>(ob->data), use_smooth || use_smooth_by_angle);
if (use_smooth || use_smooth_by_angle) {
if (use_smooth_by_angle) {
const float angle = RNA_float_get(op->ptr, "angle");
bke::mesh_sharp_edges_set_from_angle(*static_cast<Mesh *>(ob->data), angle);
}
Mesh &mesh = *static_cast<Mesh *>(ob->data);
const bool keep_sharp_edges = RNA_boolean_get(op->ptr, "keep_sharp_edges");
bke::mesh_smooth_set(mesh, use_smooth || use_smooth_by_angle, keep_sharp_edges);
if (use_smooth_by_angle) {
const float angle = RNA_float_get(op->ptr, "angle");
bke::mesh_sharp_edges_set_from_angle(mesh, angle, keep_sharp_edges);
}
BKE_mesh_batch_cache_dirty_tag(static_cast<Mesh *>(ob->data), BKE_MESH_BATCH_DIRTY_ALL);
changed = true;
@ -1671,7 +1671,7 @@ void OBJECT_OT_shade_flat(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Shade Flat";
ot->description = "Render and display faces uniform, using Face Normals";
ot->description = "Render and display faces uniform, using face normals";
ot->idname = "OBJECT_OT_shade_flat";
/* api callbacks */
@ -1680,13 +1680,19 @@ void OBJECT_OT_shade_flat(wmOperatorType *ot)
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
RNA_def_boolean(ot->srna,
"keep_sharp_edges",
true,
"Keep Sharp Edges",
"Don't remove sharp edges, which are redundant with faces shaded smooth");
}
void OBJECT_OT_shade_smooth(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Shade Smooth";
ot->description = "Render and display faces smooth, using interpolated Vertex Normals";
ot->description = "Render and display faces smooth, using interpolated vertex normals";
ot->idname = "OBJECT_OT_shade_smooth";
/* api callbacks */
@ -1695,6 +1701,12 @@ void OBJECT_OT_shade_smooth(wmOperatorType *ot)
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
RNA_def_boolean(ot->srna,
"keep_sharp_edges",
true,
"Keep Sharp Edges",
"Don't remove sharp edges. Tagged edges will remain sharp");
}
void OBJECT_OT_shade_smooth_by_angle(wmOperatorType *ot)
@ -1714,6 +1726,12 @@ void OBJECT_OT_shade_smooth_by_angle(wmOperatorType *ot)
RNA_def_property_float_default(prop, DEG2RADF(30.0f));
RNA_def_property_ui_text(
prop, "Angle", "Maximum angle between face normals that will be considered as smooth");
RNA_def_boolean(ot->srna,
"keep_sharp_edges",
true,
"Keep Sharp Edges",
"Only add sharp edges instead of clearing existing tags first");
}
/** \} */