Fix: Various issues with auto smooth versioning

This PR fixes several issues with the versioning that replaces the old
auto smooth flag with a modifier.

One issue is that the flag wasn't cleared in the initial versioning
code. That means some objects have the replacement modifier but their
meshes still have the flag set. The fix for that is to make the
versioning idempotent by trying to find an existing node group before
adding a new one. The versioning is now re-run on all objects to clear
the flags. Flags on all meshes are cleared too, even unused meshes.
That could cause loss of the auto-smooth when the mesh is linked from
a different blend-file, but that situation should be very rare.

Another issue was that the versioning wasn't run when linking objects.
That was simple to solve by adding the versioning where the proxy
versioning already existed for that case.

Finally, arguably the largest issue was that the the newly added node
groups were always added as local data-blocks. When linking, having
library data-blocks point to local data-blocks not in that library is
quite bad and breaks assumptions around Blender. This is solved by
having an auto smooth node group per library.

Resolves #119516, #119455, #119447

Pull Request: https://projects.blender.org/blender/blender/pulls/119539
This commit is contained in:
Hans Goudey
2024-03-18 20:15:36 +01:00
committed by Hans Goudey
parent 745fd2a2cb
commit 5bfe6ad8f8
3 changed files with 137 additions and 12 deletions

View File

@@ -1495,6 +1495,7 @@ void BKE_blendfile_link(BlendfileLinkAppendContext *lapp_context, ReportList *re
if ((lapp_context->params->flag & FILE_LINK) != 0) {
blendfile_link_append_proxies_convert(lapp_context->params->bmain, reports);
BKE_main_mesh_legacy_convert_auto_smooth(*lapp_context->params->bmain);
}
BKE_main_namemap_clear(lapp_context->params->bmain);

View File

@@ -35,6 +35,7 @@
#include "BKE_customdata.hh"
#include "BKE_global.h"
#include "BKE_idprop.hh"
#include "BKE_lib_id.hh"
#include "BKE_main.hh"
#include "BKE_mesh.hh"
#include "BKE_mesh_legacy_convert.hh"
@@ -2235,17 +2236,105 @@ static bNodeTree *add_auto_smooth_node_tree(Main &bmain)
nodeSetSelected(node, false);
}
BKE_ntree_update_main_tree(&bmain, group, nullptr);
return group;
}
static ModifierData *create_auto_smooth_modifier(Object &object,
const FunctionRef<bNodeTree *()> get_node_group,
const float angle)
static VectorSet<const bNodeSocket *> build_socket_indices(const Span<const bNode *> nodes)
{
VectorSet<const bNodeSocket *> result;
for (const bNode *node : nodes) {
LISTBASE_FOREACH (const bNodeSocket *, socket, &node->inputs) {
result.add_new(socket);
}
LISTBASE_FOREACH (const bNodeSocket *, socket, &node->outputs) {
result.add_new(socket);
}
}
return result;
}
/* Checks if the node group is the same as the one generated by #create_auto_smooth_modifier. */
static bool is_auto_smooth_node_tree(const bNodeTree &group)
{
if (group.type != NTREE_GEOMETRY) {
return false;
}
const Span<const bNode *> nodes = group.all_nodes();
if (nodes.size() != 10) {
return false;
}
if (!group.geometry_node_asset_traits) {
return false;
}
if (group.geometry_node_asset_traits->flag != GEO_NODE_ASSET_MODIFIER) {
return false;
}
const std::array<StringRef, 10> idnames({"NodeGroupOutput",
"NodeGroupInput",
"NodeGroupInput",
"GeometryNodeSetShadeSmooth",
"GeometryNodeSetShadeSmooth",
"GeometryNodeInputMeshEdgeAngle",
"GeometryNodeInputEdgeSmooth",
"GeometryNodeInputShadeSmooth",
"FunctionNodeBooleanMath",
"FunctionNodeCompare"});
for (const int i : nodes.index_range()) {
if (nodes[i]->idname != idnames[i]) {
return false;
}
}
if (nodes[3]->custom1 != int16_t(bke::AttrDomain::Edge)) {
return false;
}
if (static_cast<bNodeSocket *>(nodes[4]->inputs.last)
->default_value_typed<bNodeSocketValueBoolean>()
->value != true)
{
return false;
}
if (nodes[4]->custom1 != int16_t(bke::AttrDomain::Face)) {
return false;
}
if (nodes[8]->custom1 != NODE_BOOLEAN_MATH_AND) {
return false;
}
if (static_cast<NodeFunctionCompare *>(nodes[9]->storage)->operation != NODE_COMPARE_LESS_EQUAL)
{
return false;
}
if (BLI_listbase_count(&group.links) != 9) {
return false;
}
const std::array<int, 9> link_from_socket_indices({16, 15, 3, 36, 19, 5, 18, 11, 22});
const std::array<int, 9> link_to_socket_indices({23, 0, 24, 20, 21, 8, 9, 12, 10});
const VectorSet<const bNodeSocket *> socket_indices = build_socket_indices(nodes);
int i;
LISTBASE_FOREACH_INDEX (const bNodeLink *, link, &group.links, i) {
if (socket_indices.index_of(link->fromsock) != link_from_socket_indices[i]) {
return false;
}
if (socket_indices.index_of(link->tosock) != link_to_socket_indices[i]) {
return false;
}
}
return true;
}
static ModifierData *create_auto_smooth_modifier(
Object &object,
const FunctionRef<bNodeTree *(Library *library)> get_node_group,
const float angle)
{
auto *md = reinterpret_cast<NodesModifierData *>(BKE_modifier_new(eModifierType_Nodes));
STRNCPY(md->modifier.name, DATA_("Auto Smooth"));
BKE_modifier_unique_name(&object.modifiers, &md->modifier);
md->node_group = get_node_group();
md->node_group = get_node_group(object.id.lib);
id_us_plus(&md->node_group->id);
md->settings.properties = idprop::create_group("Nodes Modifier Settings").release();
IDProperty *angle_prop = idprop::create("Socket_1", angle).release();
@@ -2265,14 +2354,43 @@ static ModifierData *create_auto_smooth_modifier(Object &object,
void BKE_main_mesh_legacy_convert_auto_smooth(Main &bmain)
{
using namespace blender;
using namespace blender::bke;
/* Add the node group lazily and share it among all objects in the main database. */
bNodeTree *node_group = nullptr;
const auto add_node_group = [&]() {
node_group = add_auto_smooth_node_tree(bmain);
BKE_ntree_update_main_tree(&bmain, node_group, nullptr);
return node_group;
/* Add the node group lazily and share it among all objects in the same library. */
Map<Library *, bNodeTree *> group_by_library;
const auto add_node_group = [&](Library *library) {
if (bNodeTree **group = group_by_library.lookup_ptr(library)) {
/* Node tree has already been found/created for this versioning call. */
return *group;
}
/* Try to find an existing group added by previous versioning to avoid adding duplicates. */
LISTBASE_FOREACH (bNodeTree *, existing_group, &bmain.nodetrees) {
if (existing_group->id.lib != library) {
continue;
}
if (is_auto_smooth_node_tree(*existing_group)) {
group_by_library.add_new(library, existing_group);
return existing_group;
}
}
bNodeTree *new_group = add_auto_smooth_node_tree(bmain);
/* Remove the default user. The count is tracked manually when assigning to modifiers. */
id_us_min(&new_group->id);
if (new_group->id.lib != library) {
/* Move the node group to the requested library so that library data-blocks don't point to
* local data-blocks. This requires making sure the name is unique in that library and
* changing the name maps to be consistent with the new state. */
new_group->id.lib = library;
BKE_id_new_name_validate(&bmain, &bmain.nodetrees, &new_group->id, nullptr, false);
if (library) {
new_group->id.tag |= LIB_TAG_INDIRECT;
}
}
group_by_library.add_new(library, new_group);
return new_group;
};
LISTBASE_FOREACH (Object *, object, &bmain.objects) {
@@ -2284,7 +2402,6 @@ void BKE_main_mesh_legacy_convert_auto_smooth(Main &bmain)
if (!(mesh->flag & ME_AUTOSMOOTH_LEGACY)) {
continue;
}
mesh->flag &= ~ME_AUTOSMOOTH_LEGACY;
/* Auto-smooth disabled sharp edge tagging when the evaluated mesh had custom normals.
* When the original mesh has custom normals, that's a good sign the evaluated mesh will
@@ -2333,6 +2450,10 @@ void BKE_main_mesh_legacy_convert_auto_smooth(Main &bmain)
BLI_addtail(&object->modifiers, new_md);
}
}
LISTBASE_FOREACH (Mesh *, mesh, &bmain.meshes) {
mesh->flag &= ~ME_AUTOSMOOTH_LEGACY;
}
}
namespace blender::bke {

View File

@@ -532,7 +532,10 @@ void do_versions_after_setup(Main *new_bmain, BlendFileReadReport *reports)
BKE_lib_override_library_main_hierarchy_root_ensure(new_bmain);
}
if (!blendfile_or_libraries_versions_atleast(new_bmain, 401, 2)) {
if (!blendfile_or_libraries_versions_atleast(new_bmain, 402, 22)) {
/* Initial auto smooth versioning started at (401, 2), but a bug caused the legacy flag to not
* be cleared, so it is re-run in a later version when the bug is fixed and the versioning has
* been made idempotent. */
BKE_main_mesh_legacy_convert_auto_smooth(*new_bmain);
}
}