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:
@@ -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);
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user