diff options
Diffstat (limited to 'source/blender')
46 files changed, 1067 insertions, 237 deletions
diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index efe44ec657b..ee9c7a964d9 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -17,7 +17,7 @@ extern "C" { */ /* Blender major and minor version. */ -#define BLENDER_VERSION 303 +#define BLENDER_VERSION 304 /* Blender patch version for bugfix releases. */ #define BLENDER_VERSION_PATCH 0 /** Blender release cycle stage: alpha/beta/rc/release. */ @@ -25,7 +25,7 @@ extern "C" { /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION -#define BLENDER_FILE_SUBVERSION 5 +#define BLENDER_FILE_SUBVERSION 0 /* Minimum Blender version that supports reading file written with the current * version. Older Blender versions will test this and show a warning if the file diff --git a/source/blender/blenkernel/BKE_lib_id.h b/source/blender/blenkernel/BKE_lib_id.h index 59c842d614e..f4265dfd004 100644 --- a/source/blender/blenkernel/BKE_lib_id.h +++ b/source/blender/blenkernel/BKE_lib_id.h @@ -254,6 +254,8 @@ enum { LIB_ID_FREE_NO_DEG_TAG = 1 << 8, /** Do not attempt to remove freed ID from UI data/notifiers/... */ LIB_ID_FREE_NO_UI_USER = 1 << 9, + /** Do not remove freed ID's name from a potential runtime namemap. */ + LIB_ID_FREE_NO_NAMEMAP_REMOVE = 1 << 10, }; void BKE_libblock_free_datablock(struct ID *id, int flag) ATTR_NONNULL(); diff --git a/source/blender/blenkernel/BKE_main_namemap.h b/source/blender/blenkernel/BKE_main_namemap.h index d201e45a2c9..d6f184b4b30 100644 --- a/source/blender/blenkernel/BKE_main_namemap.h +++ b/source/blender/blenkernel/BKE_main_namemap.h @@ -46,6 +46,21 @@ bool BKE_main_namemap_get_name(struct Main *bmain, struct ID *id, char *name) AT void BKE_main_namemap_remove_name(struct Main *bmain, struct ID *id, const char *name) ATTR_NONNULL(); +/** + * Check that all ID names in given `bmain` are unique (per ID type and library), and that existing + * name maps are consistent with existing relevant IDs. + * + * This is typically called within an assert, or in tests. + */ +bool BKE_main_namemap_validate(struct Main *bmain) ATTR_NONNULL(); + +/** Same as #BKE_main_namemap_validate, but also fixes any issue by re-generating all name maps, + * and ensuring again all ID names are unique. + * + * This is typically only used in `do_versions` code to fix broken files. + */ +bool BKE_main_namemap_validate_and_fix(struct Main *bmain) ATTR_NONNULL(); + #ifdef __cplusplus } #endif diff --git a/source/blender/blenkernel/BKE_mesh.h b/source/blender/blenkernel/BKE_mesh.h index 6c61068b1c2..17f541b362e 100644 --- a/source/blender/blenkernel/BKE_mesh.h +++ b/source/blender/blenkernel/BKE_mesh.h @@ -330,7 +330,7 @@ void BKE_mesh_vert_coords_apply_with_mat4(struct Mesh *mesh, const float mat[4][4]); void BKE_mesh_vert_coords_apply(struct Mesh *mesh, const float (*vert_coords)[3]); -/* *** mesh_tessellate.c *** */ +/* *** mesh_tessellate.cc *** */ /** * Calculate tessellation into #MLoopTri which exist only for this purpose. diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index ea43fba1a85..90dbec7ec52 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1501,11 +1501,14 @@ struct TexResult; #define GEO_NODE_INPUT_INSTANCE_SCALE 1160 #define GEO_NODE_VOLUME_CUBE 1161 #define GEO_NODE_POINTS 1162 -#define GEO_NODE_FIELD_ON_DOMAIN 1163 +#define GEO_NODE_INTERPOLATE_DOMAIN 1163 #define GEO_NODE_MESH_TO_VOLUME 1164 #define GEO_NODE_UV_UNWRAP 1165 #define GEO_NODE_UV_PACK_ISLANDS 1166 #define GEO_NODE_DEFORM_CURVES_ON_SURFACE 1167 +#define GEO_NODE_INPUT_SHORTEST_EDGE_PATHS 1168 +#define GEO_NODE_EDGE_PATHS_TO_CURVES 1169 +#define GEO_NODE_EDGE_PATHS_TO_SELECTION 1170 /** \} */ diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index 2ac42037198..f76f7f5a968 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -212,7 +212,7 @@ set(SRC intern/mesh_runtime.cc intern/mesh_sample.cc intern/mesh_tangent.c - intern/mesh_tessellate.c + intern/mesh_tessellate.cc intern/mesh_validate.cc intern/mesh_wrapper.cc intern/modifier.c diff --git a/source/blender/blenkernel/intern/curves_geometry.cc b/source/blender/blenkernel/intern/curves_geometry.cc index 6486be4afe0..7fb4e37f956 100644 --- a/source/blender/blenkernel/intern/curves_geometry.cc +++ b/source/blender/blenkernel/intern/curves_geometry.cc @@ -1163,6 +1163,10 @@ static CurvesGeometry copy_with_removed_points(const CurvesGeometry &curves, } CurvesGeometry new_curves{new_point_count, new_curve_count}; + Vector<bke::AttributeTransferData> point_attributes = bke::retrieve_attributes_for_transfer( + curves.attributes(), new_curves.attributes_for_write(), ATTR_DOMAIN_MASK_POINT); + Vector<bke::AttributeTransferData> curve_attributes = bke::retrieve_attributes_for_transfer( + curves.attributes(), new_curves.attributes_for_write(), ATTR_DOMAIN_MASK_CURVE); threading::parallel_invoke( 256 < new_point_count * new_curve_count, @@ -1170,8 +1174,7 @@ static CurvesGeometry copy_with_removed_points(const CurvesGeometry &curves, [&]() { new_curves.offsets_for_write().copy_from(new_curve_offsets); }, [&]() { /* Copy over point attributes. */ - for (auto &attribute : bke::retrieve_attributes_for_transfer( - curves.attributes(), new_curves.attributes_for_write(), ATTR_DOMAIN_MASK_POINT)) { + for (bke::AttributeTransferData &attribute : point_attributes) { threading::parallel_for(copy_point_ranges.index_range(), 128, [&](IndexRange range) { for (const int range_i : range) { const IndexRange src_range = copy_point_ranges[range_i]; @@ -1182,24 +1185,29 @@ static CurvesGeometry copy_with_removed_points(const CurvesGeometry &curves, {copy_point_range_dst_offsets[range_i], src_range.size()}); } }); - attribute.dst.finish(); } - + }, + [&]() { /* Copy over curve attributes. * In some cases points are just dissolved, so the the number of * curves will be the same. That could be optimized in the future. */ - for (auto &attribute : bke::retrieve_attributes_for_transfer( - curves.attributes(), new_curves.attributes_for_write(), ATTR_DOMAIN_MASK_CURVE)) { + for (bke::AttributeTransferData &attribute : curve_attributes) { if (new_curves.curves_num() == curves.curves_num()) { attribute.dst.span.copy_from(attribute.src); } else { copy_with_map(attribute.src, new_curve_orig_indices, attribute.dst.span); } - attribute.dst.finish(); } }); + for (bke::AttributeTransferData &attribute : point_attributes) { + attribute.dst.finish(); + } + for (bke::AttributeTransferData &attribute : curve_attributes) { + attribute.dst.finish(); + } + return new_curves; } @@ -1236,6 +1244,10 @@ static CurvesGeometry copy_with_removed_curves(const CurvesGeometry &curves, } CurvesGeometry new_curves{new_tot_points, new_tot_curves}; + Vector<bke::AttributeTransferData> point_attributes = bke::retrieve_attributes_for_transfer( + curves.attributes(), new_curves.attributes_for_write(), ATTR_DOMAIN_MASK_POINT); + Vector<bke::AttributeTransferData> curve_attributes = bke::retrieve_attributes_for_transfer( + curves.attributes(), new_curves.attributes_for_write(), ATTR_DOMAIN_MASK_CURVE); threading::parallel_invoke( 256 < new_tot_points * new_tot_curves, @@ -1267,8 +1279,7 @@ static CurvesGeometry copy_with_removed_curves(const CurvesGeometry &curves, }, [&]() { /* Copy over point attributes. */ - for (auto &attribute : bke::retrieve_attributes_for_transfer( - curves.attributes(), new_curves.attributes_for_write(), ATTR_DOMAIN_MASK_POINT)) { + for (bke::AttributeTransferData &attribute : point_attributes) { threading::parallel_for(old_curve_ranges.index_range(), 128, [&](IndexRange range) { for (const int range_i : range) { copy_between_buffers(attribute.src.type(), @@ -1278,11 +1289,11 @@ static CurvesGeometry copy_with_removed_curves(const CurvesGeometry &curves, new_point_ranges[range_i]); } }); - attribute.dst.finish(); } + }, + [&]() { /* Copy over curve attributes. */ - for (auto &attribute : bke::retrieve_attributes_for_transfer( - curves.attributes(), new_curves.attributes_for_write(), ATTR_DOMAIN_MASK_CURVE)) { + for (bke::AttributeTransferData &attribute : curve_attributes) { threading::parallel_for(old_curve_ranges.index_range(), 128, [&](IndexRange range) { for (const int range_i : range) { copy_between_buffers(attribute.src.type(), @@ -1292,10 +1303,16 @@ static CurvesGeometry copy_with_removed_curves(const CurvesGeometry &curves, new_curve_ranges[range_i]); } }); - attribute.dst.finish(); } }); + for (bke::AttributeTransferData &attribute : point_attributes) { + attribute.dst.finish(); + } + for (bke::AttributeTransferData &attribute : curve_attributes) { + attribute.dst.finish(); + } + return new_curves; } diff --git a/source/blender/blenkernel/intern/lib_id.c b/source/blender/blenkernel/intern/lib_id.c index affa1e72ad0..3778e308db6 100644 --- a/source/blender/blenkernel/intern/lib_id.c +++ b/source/blender/blenkernel/intern/lib_id.c @@ -179,6 +179,10 @@ void BKE_lib_id_clear_library_data(Main *bmain, ID *id, const int flags) const bool id_in_mainlist = (id->tag & LIB_TAG_NO_MAIN) == 0 && (id->flag & LIB_EMBEDDED_DATA) == 0; + if (id_in_mainlist) { + BKE_main_namemap_remove_name(bmain, id, id->name + 2); + } + lib_id_library_local_paths(bmain, id->lib, id); id_fake_user_clear(id); @@ -1080,6 +1084,9 @@ void *BKE_libblock_alloc(Main *bmain, short type, const char *name, const int fl /* alphabetic insertion: is in new_id */ BKE_main_unlock(bmain); + /* This assert avoids having to keep name_map consistency when changing the library of an ID, + * if this check is not true anymore it will have to be done here too. */ + BLI_assert(bmain->curlib == NULL || bmain->curlib->runtime.name_map == NULL); /* This is important in 'readfile doversion after liblink' context mainly, but is a good * consistency change in general: ID created for a Main should get that main's current * library pointer. */ diff --git a/source/blender/blenkernel/intern/lib_id_delete.c b/source/blender/blenkernel/intern/lib_id_delete.c index 405b0be70f9..8d5699d7d49 100644 --- a/source/blender/blenkernel/intern/lib_id_delete.c +++ b/source/blender/blenkernel/intern/lib_id_delete.c @@ -152,7 +152,9 @@ void BKE_id_free_ex(Main *bmain, void *idv, int flag, const bool use_flag_from_i if ((flag & LIB_ID_FREE_NO_MAIN) == 0) { ListBase *lb = which_libbase(bmain, type); BLI_remlink(lb, id); - BKE_main_namemap_remove_name(bmain, id, id->name + 2); + if ((flag & LIB_ID_FREE_NO_NAMEMAP_REMOVE) == 0) { + BKE_main_namemap_remove_name(bmain, id, id->name + 2); + } } BKE_libblock_free_data(id, (flag & LIB_ID_FREE_NO_USER_REFCOUNT) == 0); diff --git a/source/blender/blenkernel/intern/lib_id_test.cc b/source/blender/blenkernel/intern/lib_id_test.cc index ea3f5395f1f..d12e0c52870 100644 --- a/source/blender/blenkernel/intern/lib_id_test.cc +++ b/source/blender/blenkernel/intern/lib_id_test.cc @@ -65,6 +65,7 @@ static void change_lib(Main *bmain, ID *id, Library *lib) } BKE_main_namemap_remove_name(bmain, id, id->name + 2); id->lib = lib; + BKE_main_namemap_get_name(bmain, id, id->name + 2); } static void change_name(Main *bmain, ID *id, const char *name) @@ -85,25 +86,27 @@ TEST(lib_id_main_sort, linked_ids_1) ID *id_a = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "OB_A")); ID *id_b = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "OB_B")); - id_a->lib = lib_a; + change_lib(ctx.bmain, id_a, lib_a); id_sort_by_name(&ctx.bmain->objects, id_a, nullptr); - id_b->lib = lib_a; + change_lib(ctx.bmain, id_b, lib_a); id_sort_by_name(&ctx.bmain->objects, id_b, nullptr); EXPECT_TRUE(ctx.bmain->objects.first == id_c); EXPECT_TRUE(ctx.bmain->objects.last == id_b); test_lib_id_main_sort_check_order({id_c, id_a, id_b}); - id_a->lib = lib_b; + change_lib(ctx.bmain, id_a, lib_b); id_sort_by_name(&ctx.bmain->objects, id_a, nullptr); EXPECT_TRUE(ctx.bmain->objects.first == id_c); EXPECT_TRUE(ctx.bmain->objects.last == id_a); test_lib_id_main_sort_check_order({id_c, id_b, id_a}); - id_b->lib = lib_b; + change_lib(ctx.bmain, id_b, lib_b); id_sort_by_name(&ctx.bmain->objects, id_b, nullptr); EXPECT_TRUE(ctx.bmain->objects.first == id_c); EXPECT_TRUE(ctx.bmain->objects.last == id_b); test_lib_id_main_sort_check_order({id_c, id_a, id_b}); + + EXPECT_TRUE(BKE_main_namemap_validate(ctx.bmain)); } TEST(lib_id_main_unique_name, local_ids_1) @@ -116,6 +119,8 @@ TEST(lib_id_main_unique_name, local_ids_1) ID *id_b = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "OB_B")); test_lib_id_main_sort_check_order({id_a, id_b, id_c}); + EXPECT_TRUE(BKE_main_namemap_validate(ctx.bmain)); + change_name(ctx.bmain, id_c, "OB_A"); EXPECT_STREQ(id_c->name + 2, "OB_A.001"); @@ -123,6 +128,8 @@ TEST(lib_id_main_unique_name, local_ids_1) EXPECT_TRUE(ctx.bmain->objects.first == id_a); EXPECT_TRUE(ctx.bmain->objects.last == id_b); test_lib_id_main_sort_check_order({id_a, id_c, id_b}); + + EXPECT_TRUE(BKE_main_namemap_validate(ctx.bmain)); } TEST(lib_id_main_unique_name, linked_ids_1) @@ -136,6 +143,8 @@ TEST(lib_id_main_unique_name, linked_ids_1) ID *id_a = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "OB_A")); ID *id_b = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "OB_B")); + EXPECT_TRUE(BKE_main_namemap_validate(ctx.bmain)); + change_lib(ctx.bmain, id_a, lib_a); id_sort_by_name(&ctx.bmain->objects, id_a, nullptr); change_lib(ctx.bmain, id_b, lib_a); @@ -148,6 +157,8 @@ TEST(lib_id_main_unique_name, linked_ids_1) EXPECT_TRUE(ctx.bmain->objects.last == id_b); test_lib_id_main_sort_check_order({id_c, id_a, id_b}); + EXPECT_TRUE(BKE_main_namemap_validate(ctx.bmain)); + change_lib(ctx.bmain, id_b, lib_b); id_sort_by_name(&ctx.bmain->objects, id_b, nullptr); change_name(ctx.bmain, id_b, "OB_A"); @@ -156,6 +167,8 @@ TEST(lib_id_main_unique_name, linked_ids_1) EXPECT_TRUE(ctx.bmain->objects.first == id_c); EXPECT_TRUE(ctx.bmain->objects.last == id_b); test_lib_id_main_sort_check_order({id_c, id_a, id_b}); + + EXPECT_TRUE(BKE_main_namemap_validate(ctx.bmain)); } TEST(lib_id_main_unique_name, ids_sorted_by_default) @@ -167,12 +180,14 @@ TEST(lib_id_main_unique_name, ids_sorted_by_default) ID *id_baz = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Baz")); ID *id_yes = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Yes")); test_lib_id_main_sort_check_order({id_bar, id_baz, id_foo, id_yes}); + + EXPECT_TRUE(BKE_main_namemap_validate(ctx.bmain)); } static ID *add_id_in_library(Main *bmain, const char *name, Library *lib) { ID *id = static_cast<ID *>(BKE_id_new(bmain, ID_OB, name)); - id->lib = lib; + change_lib(bmain, id, lib); id_sort_by_name(&bmain->objects, id, nullptr); return id; } @@ -195,6 +210,8 @@ TEST(lib_id_main_unique_name, ids_sorted_by_default_with_libraries) ID *id_yes = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Yes")); test_lib_id_main_sort_check_order({id_bar, id_baz, id_foo, id_yes, id_l1a, id_l1c, id_l2b}); + + EXPECT_TRUE(BKE_main_namemap_validate(ctx.bmain)); } TEST(lib_id_main_unique_name, name_too_long_handling) @@ -211,6 +228,8 @@ TEST(lib_id_main_unique_name, name_too_long_handling) EXPECT_STREQ(id_a->name + 2, "Long_Name_That_Does_Not_Fit_Into_Max_Name_Limit_And_Should_Get_"); EXPECT_STREQ(id_b->name + 2, "Another_Long_Name_That_Does_Not_Fit_And_Has_A_Number_Suffix.123"); EXPECT_STREQ(id_c->name + 2, "Name_That_Has_Too_Long_Number_Suffix.1234567890"); /* Unchanged */ + + EXPECT_TRUE(BKE_main_namemap_validate(ctx.bmain)); } TEST(lib_id_main_unique_name, create_equivalent_numeric_suffixes) @@ -244,6 +263,8 @@ TEST(lib_id_main_unique_name, create_equivalent_numeric_suffixes) EXPECT_STREQ(id_j->name + 2, "Foo..001"); EXPECT_STREQ(id_k->name + 2, "Foo..000"); + EXPECT_TRUE(BKE_main_namemap_validate(ctx.bmain)); + /* Now create their exact duplicates again, and check what happens. */ id_a = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo.123")); id_b = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo.000")); @@ -268,6 +289,8 @@ TEST(lib_id_main_unique_name, create_equivalent_numeric_suffixes) EXPECT_STREQ(id_i->name + 2, "Foo...001"); EXPECT_STREQ(id_j->name + 2, "Foo..003"); EXPECT_STREQ(id_k->name + 2, "Foo..004"); + + EXPECT_TRUE(BKE_main_namemap_validate(ctx.bmain)); } TEST(lib_id_main_unique_name, zero_suffix_is_never_assigned) @@ -283,6 +306,8 @@ TEST(lib_id_main_unique_name, zero_suffix_is_never_assigned) EXPECT_STREQ(id_002->name + 2, "Foo.002"); EXPECT_STREQ(id_001->name + 2, "Foo.001"); EXPECT_STREQ(id_003->name + 2, "Foo.003"); + + EXPECT_TRUE(BKE_main_namemap_validate(ctx.bmain)); } TEST(lib_id_main_unique_name, remove_after_dup_get_original_name) @@ -296,8 +321,12 @@ TEST(lib_id_main_unique_name, remove_after_dup_get_original_name) EXPECT_STREQ(id_b->name + 2, "Foo.001"); BKE_id_free(ctx.bmain, id_a); + EXPECT_TRUE(BKE_main_namemap_validate(ctx.bmain)); + id_a = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo")); EXPECT_STREQ(id_a->name + 2, "Foo"); + + EXPECT_TRUE(BKE_main_namemap_validate(ctx.bmain)); } TEST(lib_id_main_unique_name, name_number_suffix_assignment) @@ -316,11 +345,15 @@ TEST(lib_id_main_unique_name, name_number_suffix_assignment) EXPECT_STREQ(ids[1]->name + 2, "Foo.001"); EXPECT_STREQ(ids[total_object_count / 2 - 1]->name + 2, "Foo.599"); + EXPECT_TRUE(BKE_main_namemap_validate(ctx.bmain)); + /* Free some of the objects. */ BKE_id_free(ctx.bmain, ids[10]); BKE_id_free(ctx.bmain, ids[20]); BKE_id_free(ctx.bmain, ids[30]); + EXPECT_TRUE(BKE_main_namemap_validate(ctx.bmain)); + /* Create objects again; they should get suffixes that were just free'd up. */ ID *id_010 = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo")); EXPECT_STREQ(id_010->name + 2, "Foo.010"); @@ -335,6 +368,8 @@ TEST(lib_id_main_unique_name, name_number_suffix_assignment) ID *id_600 = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo")); EXPECT_STREQ(id_600->name + 2, "Foo.600"); + EXPECT_TRUE(BKE_main_namemap_validate(ctx.bmain)); + /* Max possible numeric suffix. */ ID *id_max = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo.999999999")); EXPECT_STREQ(id_max->name + 2, "Foo.999999999"); @@ -342,6 +377,8 @@ TEST(lib_id_main_unique_name, name_number_suffix_assignment) ID *id_max1 = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo.999999999")); EXPECT_STREQ(id_max1->name + 2, "Foo.601"); + EXPECT_TRUE(BKE_main_namemap_validate(ctx.bmain)); + /* Now create the rest of objects, to use all the suffixes up to 1k. * Once all the ones up to 1k are used, the logic will fall back to * "use largest number seen + 1", but the largest one is already the max @@ -358,6 +395,8 @@ TEST(lib_id_main_unique_name, name_number_suffix_assignment) EXPECT_STREQ(id_fo179->name + 2, "Fo.179"); ID *id_fo180 = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo.999999999")); EXPECT_STREQ(id_fo180->name + 2, "Fo.180"); + + EXPECT_TRUE(BKE_main_namemap_validate(ctx.bmain)); } TEST(lib_id_main_unique_name, renames_with_duplicates) @@ -372,6 +411,8 @@ TEST(lib_id_main_unique_name, renames_with_duplicates) EXPECT_STREQ(id_b->name + 2, "Foo.001"); EXPECT_STREQ(id_c->name + 2, "Bar"); + EXPECT_TRUE(BKE_main_namemap_validate(ctx.bmain)); + BKE_libblock_rename(ctx.bmain, id_a, "Foo.002"); EXPECT_STREQ(id_a->name + 2, "Foo.002"); BKE_libblock_rename(ctx.bmain, id_b, "Bar"); @@ -380,6 +421,8 @@ TEST(lib_id_main_unique_name, renames_with_duplicates) EXPECT_STREQ(id_c->name + 2, "Foo"); BKE_libblock_rename(ctx.bmain, id_b, "Bar"); EXPECT_STREQ(id_b->name + 2, "Bar"); + + EXPECT_TRUE(BKE_main_namemap_validate(ctx.bmain)); } TEST(lib_id_main_unique_name, names_are_unique_per_id_type) @@ -393,6 +436,8 @@ TEST(lib_id_main_unique_name, names_are_unique_per_id_type) EXPECT_STREQ(id_a->name + 2, "Foo"); EXPECT_STREQ(id_b->name + 2, "Foo"); /* Different types (OB & CA) can have the same name. */ EXPECT_STREQ(id_c->name + 2, "Foo.001"); + + EXPECT_TRUE(BKE_main_namemap_validate(ctx.bmain)); } TEST(lib_id_main_unique_name, name_huge_number_suffix) @@ -406,6 +451,8 @@ TEST(lib_id_main_unique_name, name_huge_number_suffix) /* Now create with the same name again: should get 001 suffix. */ ID *id_b = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "SuperLong.1234567890")); EXPECT_STREQ(id_b->name + 2, "SuperLong.001"); + + EXPECT_TRUE(BKE_main_namemap_validate(ctx.bmain)); } } // namespace blender::bke::tests diff --git a/source/blender/blenkernel/intern/lib_override.cc b/source/blender/blenkernel/intern/lib_override.cc index 0c5f59be768..4ad8d26cd2a 100644 --- a/source/blender/blenkernel/intern/lib_override.cc +++ b/source/blender/blenkernel/intern/lib_override.cc @@ -34,6 +34,7 @@ #include "BKE_lib_query.h" #include "BKE_lib_remap.h" #include "BKE_main.h" +#include "BKE_main_namemap.h" #include "BKE_node.h" #include "BKE_report.h" #include "BKE_scene.h" @@ -2669,6 +2670,8 @@ void BKE_lib_override_library_main_resync(Main *bmain, library->filepath); } } + + BLI_assert(BKE_main_namemap_validate(bmain)); } void BKE_lib_override_library_delete(Main *bmain, ID *id_root) @@ -3637,6 +3640,9 @@ void BKE_lib_override_library_update(Main *bmain, ID *local) return; } + /* Remove the pair (idname, lib) of this temp id from the name map. */ + BKE_main_namemap_remove_name(bmain, tmp_id, tmp_id->name + 2); + tmp_id->lib = local->lib; /* This ID name is problematic, since it is an 'rna name property' it should not be editable or @@ -3651,7 +3657,9 @@ void BKE_lib_override_library_update(Main *bmain, ID *local) Key *tmp_key = BKE_key_from_id(tmp_id); if (local_key != nullptr && tmp_key != nullptr) { tmp_key->id.flag |= (local_key->id.flag & LIB_EMBEDDED_DATA_LIB_OVERRIDE); + BKE_main_namemap_remove_name(bmain, &tmp_key->id, tmp_key->id.name + 2); tmp_key->id.lib = local_key->id.lib; + BLI_strncpy(tmp_key->id.name, local_key->id.name, sizeof(tmp_key->id.name)); } PointerRNA rnaptr_src, rnaptr_dst, rnaptr_storage_stack, *rnaptr_storage = nullptr; @@ -3688,8 +3696,10 @@ void BKE_lib_override_library_update(Main *bmain, ID *local) } /* Again, horribly inefficient in our case, we need something off-Main - * (aka more generic nolib copy/free stuff)! */ - BKE_id_free_ex(bmain, tmp_id, LIB_ID_FREE_NO_UI_USER, true); + * (aka more generic nolib copy/free stuff). + * NOTE: Do not remove this tmp_id's name from the namemap here, since this name actually still + * exists in `bmain`. */ + BKE_id_free_ex(bmain, tmp_id, LIB_ID_FREE_NO_UI_USER | LIB_ID_FREE_NO_NAMEMAP_REMOVE, true); if (GS(local->name) == ID_AR) { /* Fun times again, thanks to bone pointers in pose data of objects. We keep same ID addresses, @@ -3733,6 +3743,8 @@ void BKE_lib_override_library_main_update(Main *bmain) Main *orig_gmain = G_MAIN; G_MAIN = bmain; + BLI_assert(BKE_main_namemap_validate(bmain)); + FOREACH_MAIN_ID_BEGIN (bmain, id) { if (id->override_library != nullptr) { BKE_lib_override_library_update(bmain, id); @@ -3740,6 +3752,8 @@ void BKE_lib_override_library_main_update(Main *bmain) } FOREACH_MAIN_ID_END; + BLI_assert(BKE_main_namemap_validate(bmain)); + G_MAIN = orig_gmain; } diff --git a/source/blender/blenkernel/intern/main_namemap.cc b/source/blender/blenkernel/intern/main_namemap.cc index 00115d2a0be..a164633af09 100644 --- a/source/blender/blenkernel/intern/main_namemap.cc +++ b/source/blender/blenkernel/intern/main_namemap.cc @@ -5,6 +5,7 @@ */ #include "BKE_idtype.h" +#include "BKE_lib_id.h" #include "BKE_main.h" #include "BKE_main_namemap.h" @@ -22,6 +23,10 @@ #include "MEM_guardedalloc.h" +#include "CLG_log.h" + +static CLG_LogRef LOG = {"bke.main_namemap"}; + //#define DEBUG_PRINT_MEMORY_USAGE using namespace blender; @@ -363,3 +368,133 @@ void BKE_main_namemap_remove_name(struct Main *bmain, struct ID *id, const char } val->mark_unused(number); } + +struct Uniqueness_Key { + char name[MAX_ID_NAME]; + Library *lib; + uint64_t hash() const + { + return BLI_ghashutil_combine_hash(BLI_ghashutil_strhash_n(name, MAX_ID_NAME), + BLI_ghashutil_ptrhash(lib)); + } + bool operator==(const Uniqueness_Key &o) const + { + return lib == o.lib && !BLI_ghashutil_strcmp(name, o.name); + } +}; + +static bool main_namemap_validate_and_fix(Main *bmain, const bool do_fix) +{ + Set<Uniqueness_Key> id_names_libs; + bool is_valid = true; + ListBase *lb_iter; + FOREACH_MAIN_LISTBASE_BEGIN (bmain, lb_iter) { + LISTBASE_FOREACH_MUTABLE (ID *, id_iter, lb_iter) { + Uniqueness_Key key; + BLI_strncpy(key.name, id_iter->name, MAX_ID_NAME); + key.lib = id_iter->lib; + if (!id_names_libs.add(key)) { + is_valid = false; + CLOG_ERROR(&LOG, + "ID name '%s' (from library '%s') is found more than once", + id_iter->name, + id_iter->lib != nullptr ? id_iter->lib->filepath : "<None>"); + if (do_fix) { + /* NOTE: this may imply moving this ID in its listbase, however re-checking it later is + * not really an issue. */ + BKE_id_new_name_validate( + bmain, which_libbase(bmain, GS(id_iter->name)), id_iter, nullptr, true); + BLI_strncpy(key.name, id_iter->name, MAX_ID_NAME); + if (!id_names_libs.add(key)) { + CLOG_ERROR(&LOG, + "\tID has been renamed to '%s', but it still seems to be already in use", + id_iter->name); + } + else { + CLOG_WARN(&LOG, "\tID has been renamed to '%s'", id_iter->name); + } + } + } + + UniqueName_Map *name_map = get_namemap_for(bmain, id_iter, false); + if (name_map == nullptr) { + continue; + } + UniqueName_TypeMap *type_map = name_map->find_by_type(GS(id_iter->name)); + BLI_assert(type_map != nullptr); + + UniqueName_Key key_namemap; + /* Remove full name from the set. */ + BLI_strncpy(key_namemap.name, id_iter->name + 2, MAX_NAME); + if (!type_map->full_names.contains(key_namemap)) { + is_valid = false; + CLOG_ERROR(&LOG, + "ID name '%s' (from library '%s') exists in current Main, but is not listed in " + "the namemap", + id_iter->name, + id_iter->lib != nullptr ? id_iter->lib->filepath : "<None>"); + } + } + } + FOREACH_MAIN_LISTBASE_END; + + Library *lib = nullptr; + UniqueName_Map *name_map = bmain->name_map; + do { + if (name_map != nullptr) { + int i = 0; + for (short idcode = BKE_idtype_idcode_iter_step(&i); idcode != 0; + idcode = BKE_idtype_idcode_iter_step(&i)) { + UniqueName_TypeMap *type_map = name_map->find_by_type(idcode); + if (type_map != nullptr) { + for (const UniqueName_Key &id_name : type_map->full_names) { + Uniqueness_Key key; + *(reinterpret_cast<short *>(key.name)) = idcode; + BLI_strncpy(key.name + 2, id_name.name, MAX_NAME); + key.lib = lib; + if (!id_names_libs.contains(key)) { + is_valid = false; + CLOG_ERROR(&LOG, + "ID name '%s' (from library '%s') is listed in the namemap, but does not " + "exists in current Main", + key.name, + lib != nullptr ? lib->filepath : "<None>"); + } + } + } + } + } + lib = static_cast<Library *>((lib == nullptr) ? bmain->libraries.first : lib->id.next); + name_map = (lib != nullptr) ? lib->runtime.name_map : nullptr; + } while (lib != nullptr); + + if (is_valid || !do_fix) { + return is_valid; + } + + /* Clear all existing namemaps. */ + lib = nullptr; + UniqueName_Map **name_map_p = &bmain->name_map; + do { + BLI_assert(name_map_p != nullptr); + if (*name_map_p != nullptr) { + BKE_main_namemap_destroy(name_map_p); + } + lib = static_cast<Library *>((lib == nullptr) ? bmain->libraries.first : lib->id.next); + name_map_p = (lib != nullptr) ? &lib->runtime.name_map : nullptr; + } while (lib != nullptr); + + return is_valid; +} + +bool BKE_main_namemap_validate_and_fix(Main *bmain) +{ + const bool is_valid = main_namemap_validate_and_fix(bmain, true); + BLI_assert(main_namemap_validate_and_fix(bmain, false)); + return is_valid; +} + +bool BKE_main_namemap_validate(Main *bmain) +{ + return main_namemap_validate_and_fix(bmain, false); +} diff --git a/source/blender/blenkernel/intern/mesh_tessellate.c b/source/blender/blenkernel/intern/mesh_tessellate.cc index 173fb98912b..ab0aeb88ebc 100644 --- a/source/blender/blenkernel/intern/mesh_tessellate.c +++ b/source/blender/blenkernel/intern/mesh_tessellate.cc @@ -10,7 +10,7 @@ * \see bmesh_mesh_tessellate.c for the #BMesh equivalent of this file. */ -#include <limits.h> +#include <climits> #include "MEM_guardedalloc.h" @@ -120,13 +120,14 @@ BLI_INLINE void mesh_calc_tessellation_for_face_impl(const MLoop *mloop, const uint totfilltri = mp_totloop - 2; MemArena *pf_arena = *pf_arena_p; - if (UNLIKELY(pf_arena == NULL)) { + if (UNLIKELY(pf_arena == nullptr)) { pf_arena = *pf_arena_p = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__); } - uint(*tris)[3] = tris = BLI_memarena_alloc(pf_arena, sizeof(*tris) * (size_t)totfilltri); - float(*projverts)[2] = projverts = BLI_memarena_alloc( - pf_arena, sizeof(*projverts) * (size_t)mp_totloop); + uint(*tris)[3] = static_cast<uint(*)[3]>( + BLI_memarena_alloc(pf_arena, sizeof(*tris) * (size_t)totfilltri)); + float(*projverts)[2] = static_cast<float(*)[2]>( + BLI_memarena_alloc(pf_arena, sizeof(*projverts) * (size_t)mp_totloop)); ml = mloop + mp_loopstart; for (uint j = 0; j < mp_totloop; j++, ml++) { @@ -157,7 +158,7 @@ static void mesh_calc_tessellation_for_face(const MLoop *mloop, MemArena **pf_arena_p) { mesh_calc_tessellation_for_face_impl( - mloop, mpoly, mvert, poly_index, mlt, pf_arena_p, false, NULL); + mloop, mpoly, mvert, poly_index, mlt, pf_arena_p, false, nullptr); } static void mesh_calc_tessellation_for_face_with_normal(const MLoop *mloop, @@ -180,11 +181,11 @@ static void mesh_recalc_looptri__single_threaded(const MLoop *mloop, MLoopTri *mlooptri, const float (*poly_normals)[3]) { - MemArena *pf_arena = NULL; + MemArena *pf_arena = nullptr; const MPoly *mp = mpoly; uint tri_index = 0; - if (poly_normals != NULL) { + if (poly_normals != nullptr) { for (uint poly_index = 0; poly_index < (uint)totpoly; poly_index++, mp++) { mesh_calc_tessellation_for_face_with_normal(mloop, mpoly, @@ -206,7 +207,7 @@ static void mesh_recalc_looptri__single_threaded(const MLoop *mloop, if (pf_arena) { BLI_memarena_free(pf_arena); - pf_arena = NULL; + pf_arena = nullptr; } BLI_assert(tri_index == (uint)poly_to_tri_count(totpoly, totloop)); UNUSED_VARS_NDEBUG(totloop); @@ -232,8 +233,8 @@ static void mesh_calc_tessellation_for_face_fn(void *__restrict userdata, const int index, const TaskParallelTLS *__restrict tls) { - const struct TessellationUserData *data = userdata; - struct TessellationUserTLS *tls_data = tls->userdata_chunk; + const TessellationUserData *data = static_cast<const TessellationUserData *>(userdata); + TessellationUserTLS *tls_data = static_cast<TessellationUserTLS *>(tls->userdata_chunk); const int tri_index = poly_to_tri_count(index, data->mpoly[index].loopstart); mesh_calc_tessellation_for_face_impl(data->mloop, data->mpoly, @@ -242,15 +243,15 @@ static void mesh_calc_tessellation_for_face_fn(void *__restrict userdata, &data->mlooptri[tri_index], &tls_data->pf_arena, false, - NULL); + nullptr); } static void mesh_calc_tessellation_for_face_with_normal_fn(void *__restrict userdata, const int index, const TaskParallelTLS *__restrict tls) { - const struct TessellationUserData *data = userdata; - struct TessellationUserTLS *tls_data = tls->userdata_chunk; + const TessellationUserData *data = static_cast<const TessellationUserData *>(userdata); + TessellationUserTLS *tls_data = static_cast<TessellationUserTLS *>(tls->userdata_chunk); const int tri_index = poly_to_tri_count(index, data->mpoly[index].loopstart); mesh_calc_tessellation_for_face_impl(data->mloop, data->mpoly, @@ -265,7 +266,7 @@ static void mesh_calc_tessellation_for_face_with_normal_fn(void *__restrict user static void mesh_calc_tessellation_for_face_free_fn(const void *__restrict UNUSED(userdata), void *__restrict tls_v) { - struct TessellationUserTLS *tls_data = tls_v; + TessellationUserTLS *tls_data = static_cast<TessellationUserTLS *>(tls_v); if (tls_data->pf_arena) { BLI_memarena_free(tls_data->pf_arena); } @@ -279,15 +280,14 @@ static void mesh_recalc_looptri__multi_threaded(const MLoop *mloop, MLoopTri *mlooptri, const float (*poly_normals)[3]) { - struct TessellationUserTLS tls_data_dummy = {NULL}; + struct TessellationUserTLS tls_data_dummy = {nullptr}; - struct TessellationUserData data = { - .mloop = mloop, - .mpoly = mpoly, - .mvert = mvert, - .mlooptri = mlooptri, - .poly_normals = poly_normals, - }; + struct TessellationUserData data {}; + data.mloop = mloop; + data.mpoly = mpoly; + data.mvert = mvert; + data.mlooptri = mlooptri; + data.poly_normals = poly_normals; TaskParallelSettings settings; BLI_parallel_range_settings_defaults(&settings); @@ -313,10 +313,10 @@ void BKE_mesh_recalc_looptri(const MLoop *mloop, MLoopTri *mlooptri) { if (totloop < MESH_FACE_TESSELLATE_THREADED_LIMIT) { - mesh_recalc_looptri__single_threaded(mloop, mpoly, mvert, totloop, totpoly, mlooptri, NULL); + mesh_recalc_looptri__single_threaded(mloop, mpoly, mvert, totloop, totpoly, mlooptri, nullptr); } else { - mesh_recalc_looptri__multi_threaded(mloop, mpoly, mvert, totloop, totpoly, mlooptri, NULL); + mesh_recalc_looptri__multi_threaded(mloop, mpoly, mvert, totloop, totpoly, mlooptri, nullptr); } } @@ -328,7 +328,7 @@ void BKE_mesh_recalc_looptri_with_normals(const MLoop *mloop, MLoopTri *mlooptri, const float (*poly_normals)[3]) { - BLI_assert(poly_normals != NULL); + BLI_assert(poly_normals != nullptr); if (totloop < MESH_FACE_TESSELLATE_THREADED_LIMIT) { mesh_recalc_looptri__single_threaded( mloop, mpoly, mvert, totloop, totpoly, mlooptri, poly_normals); diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index c6f140b9260..05e16192f9a 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -4751,17 +4751,17 @@ static void registerGeometryNodes() register_node_type_geo_curve_trim(); register_node_type_geo_deform_curves_on_surface(); register_node_type_geo_delete_geometry(); - register_node_type_geo_duplicate_elements(); register_node_type_geo_distribute_points_on_faces(); register_node_type_geo_dual_mesh(); + register_node_type_geo_duplicate_elements(); + register_node_type_geo_edge_paths_to_curves(); + register_node_type_geo_edge_paths_to_selection(); register_node_type_geo_edge_split(); register_node_type_geo_extrude_mesh(); register_node_type_geo_field_at_index(); - register_node_type_geo_field_on_domain(); register_node_type_geo_flip_faces(); register_node_type_geo_geometry_to_instance(); register_node_type_geo_image_texture(); - register_node_type_geo_input_named_attribute(); register_node_type_geo_input_curve_handles(); register_node_type_geo_input_curve_tilt(); register_node_type_geo_input_id(); @@ -4778,17 +4778,20 @@ static void registerGeometryNodes() register_node_type_geo_input_mesh_face_neighbors(); register_node_type_geo_input_mesh_island(); register_node_type_geo_input_mesh_vertex_neighbors(); + register_node_type_geo_input_named_attribute(); register_node_type_geo_input_normal(); register_node_type_geo_input_position(); register_node_type_geo_input_radius(); register_node_type_geo_input_scene_time(); register_node_type_geo_input_shade_smooth(); + register_node_type_geo_input_shortest_edge_paths(); register_node_type_geo_input_spline_cyclic(); register_node_type_geo_input_spline_length(); register_node_type_geo_input_spline_resolution(); register_node_type_geo_input_tangent(); register_node_type_geo_instance_on_points(); register_node_type_geo_instances_to_points(); + register_node_type_geo_interpolate_domain(); register_node_type_geo_is_viewport(); register_node_type_geo_join_geometry(); register_node_type_geo_material_replace(); @@ -4807,9 +4810,9 @@ static void registerGeometryNodes() register_node_type_geo_mesh_to_points(); register_node_type_geo_mesh_to_volume(); register_node_type_geo_object_info(); - register_node_type_geo_points(); register_node_type_geo_points_to_vertices(); register_node_type_geo_points_to_volume(); + register_node_type_geo_points(); register_node_type_geo_proximity(); register_node_type_geo_raycast(); register_node_type_geo_realize_instances(); @@ -4839,11 +4842,11 @@ static void registerGeometryNodes() register_node_type_geo_transform(); register_node_type_geo_translate_instances(); register_node_type_geo_triangulate(); + register_node_type_geo_uv_pack_islands(); + register_node_type_geo_uv_unwrap(); register_node_type_geo_viewer(); register_node_type_geo_volume_cube(); register_node_type_geo_volume_to_mesh(); - register_node_type_geo_uv_pack_islands(); - register_node_type_geo_uv_unwrap(); } static void registerFunctionNodes() diff --git a/source/blender/blenlib/BLI_generic_virtual_array.hh b/source/blender/blenlib/BLI_generic_virtual_array.hh index 43ca16a894f..21549896f45 100644 --- a/source/blender/blenlib/BLI_generic_virtual_array.hh +++ b/source/blender/blenlib/BLI_generic_virtual_array.hh @@ -860,6 +860,7 @@ template<typename T> inline GVArray::GVArray(const VArray<T> &varray) * #this is destructed. */ if (info.type == CommonVArrayInfo::Type::Span && !info.may_have_ownership) { *this = GVArray::ForSpan(GSpan(CPPType::get<T>(), info.data, varray.size())); + return; } if (varray.try_assign_GVArray(*this)) { return; diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index fa3789ea590..49b14dc84a6 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -57,6 +57,7 @@ #include "BKE_lib_id.h" #include "BKE_lib_override.h" #include "BKE_main.h" +#include "BKE_main_namemap.h" #include "BKE_modifier.h" #include "BKE_node.h" #include "BKE_screen.h" @@ -863,6 +864,34 @@ void do_versions_after_linking_300(Main *bmain, ReportList *UNUSED(reports)) } } + if (!MAIN_VERSION_ATLEAST(bmain, 303, 6)) { + /* In the Dope Sheet, for every mode other than Timeline, open the Properties panel. */ + LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) { + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { + LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) { + if (sl->spacetype != SPACE_ACTION) { + continue; + } + + /* Skip the timeline, it shouldn't get its Properties panel opened. */ + SpaceAction *saction = (SpaceAction *)sl; + if (saction->mode == SACTCONT_TIMELINE) { + continue; + } + + const bool is_first_space = sl == area->spacedata.first; + ListBase *regionbase = is_first_space ? &area->regionbase : &sl->regionbase; + ARegion *region = BKE_region_find_in_listbase_by_type(regionbase, RGN_TYPE_UI); + if (region == NULL) { + continue; + } + + region->flag &= ~RGN_FLAG_HIDDEN; + } + } + } + } + /** * Versioning code until next subversion bump goes here. * @@ -875,34 +904,6 @@ void do_versions_after_linking_300(Main *bmain, ReportList *UNUSED(reports)) */ { /* Keep this block, even when empty. */ - - { - /* In the Dope Sheet, for every mode other than Timeline, open the Properties panel. */ - LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) { - LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { - LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) { - if (sl->spacetype != SPACE_ACTION) { - continue; - } - - /* Skip the timeline, it shouldn't get its Properties panel opened. */ - SpaceAction *saction = (SpaceAction *)sl; - if (saction->mode == SACTCONT_TIMELINE) { - continue; - } - - const bool is_first_space = sl == area->spacedata.first; - ListBase *regionbase = is_first_space ? &area->regionbase : &sl->regionbase; - ARegion *region = BKE_region_find_in_listbase_by_type(regionbase, RGN_TYPE_UI); - if (region == NULL) { - continue; - } - - region->flag &= ~RGN_FLAG_HIDDEN; - } - } - } - } } } @@ -3285,18 +3286,8 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) } } } - /** - * Versioning code until next subversion bump goes here. - * - * \note Be sure to check when bumping the version: - * - "versioning_userdef.c", #blo_do_versions_userdef - * - "versioning_userdef.c", #do_versions_theme - * - * \note Keep this message at the bottom of the function. - */ - { - /* Keep this block, even when empty. */ + if (!MAIN_VERSION_ATLEAST(bmain, 303, 6)) { /* Initialize brush curves sculpt settings. */ LISTBASE_FOREACH (Brush *, brush, &bmain->brushes) { if (brush->ob_mode != OB_MODE_SCULPT_CURVES) { @@ -3312,5 +3303,20 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) ob->dtx &= ~OB_DRAWBOUNDOX; } } + + BKE_main_namemap_validate_and_fix(bmain); + } + + /** + * Versioning code until next subversion bump goes here. + * + * \note Be sure to check when bumping the version: + * - "versioning_userdef.c", #blo_do_versions_userdef + * - "versioning_userdef.c", #do_versions_theme + * + * \note Keep this message at the bottom of the function. + */ + { + /* Keep this block, even when empty. */ } } diff --git a/source/blender/blenloader/intern/versioning_userdef.c b/source/blender/blenloader/intern/versioning_userdef.c index 4f62be26196..f8fb3b47570 100644 --- a/source/blender/blenloader/intern/versioning_userdef.c +++ b/source/blender/blenloader/intern/versioning_userdef.c @@ -83,6 +83,10 @@ static void do_versions_theme(const UserDef *userdef, bTheme *btheme) FROM_DEFAULT_V4_UCHAR(space_sequencer.list_text_hi); } + if (!USER_VERSION_ATLEAST(303, 6)) { + btheme->tui.wcol_view_item = U_theme_default.tui.wcol_view_item; + } + /** * Versioning code until next subversion bump goes here. * @@ -94,7 +98,6 @@ static void do_versions_theme(const UserDef *userdef, bTheme *btheme) */ { /* Keep this block, even when empty. */ - btheme->tui.wcol_view_item = U_theme_default.tui.wcol_view_item; } #undef FROM_DEFAULT_V4_UCHAR diff --git a/source/blender/bmesh/intern/bmesh_mesh_tessellate.c b/source/blender/bmesh/intern/bmesh_mesh_tessellate.c index 8d401ae1e5c..69510263ec9 100644 --- a/source/blender/bmesh/intern/bmesh_mesh_tessellate.c +++ b/source/blender/bmesh/intern/bmesh_mesh_tessellate.c @@ -6,7 +6,7 @@ * This file contains code for polygon tessellation * (creating triangles from polygons). * - * \see mesh_tessellate.c for the #Mesh equivalent of this file. + * \see mesh_tessellate.cc for the #Mesh equivalent of this file. */ #include "DNA_meshdata_types.h" diff --git a/source/blender/depsgraph/intern/eval/deg_eval.cc b/source/blender/depsgraph/intern/eval/deg_eval.cc index 89e1f1add18..9b2ce2bb707 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval.cc +++ b/source/blender/depsgraph/intern/eval/deg_eval.cc @@ -111,7 +111,8 @@ void evaluate_node(const DepsgraphEvalState *state, OperationNode *operation_nod * times. * This is a thread-safe modification as the node's flags are only read for a non-scheduled nodes * and this node has been scheduled. */ - operation_node->flag &= ~DEPSOP_FLAG_NEEDS_UPDATE; + operation_node->flag &= ~(DEPSOP_FLAG_DIRECTLY_MODIFIED | DEPSOP_FLAG_NEEDS_UPDATE | + DEPSOP_FLAG_USER_MODIFIED); } void deg_task_run_func(TaskPool *pool, void *taskdata) @@ -466,7 +467,7 @@ void deg_evaluate_on_refresh(Depsgraph *graph) deg_eval_stats_aggregate(graph); } - /* Clear any uncleared tags - just in case. */ + /* Clear any uncleared tags. */ deg_graph_clear_tags(graph); graph->is_evaluating = false; diff --git a/source/blender/depsgraph/intern/eval/deg_eval_flush.cc b/source/blender/depsgraph/intern/eval/deg_eval_flush.cc index de4e26aa4b5..1c313d42d8e 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_flush.cc +++ b/source/blender/depsgraph/intern/eval/deg_eval_flush.cc @@ -378,11 +378,6 @@ void deg_graph_flush_updates(Depsgraph *graph) void deg_graph_clear_tags(Depsgraph *graph) { - /* Go over all operation nodes, clearing tags. */ - for (OperationNode *node : graph->operations) { - node->flag &= ~(DEPSOP_FLAG_DIRECTLY_MODIFIED | DEPSOP_FLAG_NEEDS_UPDATE | - DEPSOP_FLAG_USER_MODIFIED); - } /* Clear any entry tags which haven't been flushed. */ graph->entry_tags.clear(); diff --git a/source/blender/depsgraph/intern/eval/deg_eval_visibility.cc b/source/blender/depsgraph/intern/eval/deg_eval_visibility.cc index 8bf26e45e2a..05f7631b0d4 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_visibility.cc +++ b/source/blender/depsgraph/intern/eval/deg_eval_visibility.cc @@ -57,6 +57,27 @@ void deg_graph_flush_visibility_flags(Depsgraph *graph) for (ComponentNode *comp_node : id_node->components.values()) { comp_node->possibly_affects_visible_id = id_node->is_visible_on_build; comp_node->affects_visible_id = id_node->is_visible_on_build && id_node->is_enabled_on_eval; + + /* Visibility component is always to be considered to have the same visibility as the + * `id_node->is_visible_on_build`. This is because the visibility is to be evaluated + * regardless of its current state as it might get changed due to animation. */ + if (comp_node->type == NodeType::VISIBILITY) { + comp_node->affects_visible_id = id_node->is_visible_on_build; + } + + /* Enforce "visibility" of the synchronization component. + * + * This component is never connected to other ID nodes, and hence can not be handled in the + * same way as other components needed for evaluation. It is only needed for proper + * evaluation of the ID node it belongs to. + * + * The design is such that the synchronization is supposed to happen whenever any part of the + * ID changed/evaluated. Here we mark the component as "visible" so that genetic recalc flag + * flushing and scheduling will handle the component in a generic manner. */ + if (comp_node->type == NodeType::SYNCHRONIZATION) { + comp_node->possibly_affects_visible_id = true; + comp_node->affects_visible_id = true; + } } } @@ -85,6 +106,13 @@ void deg_graph_flush_visibility_flags(Depsgraph *graph) if (rel->from->type == NodeType::OPERATION) { const OperationNode *op_to = reinterpret_cast<const OperationNode *>(rel->to); const ComponentNode *comp_to = op_to->owner; + + /* Ignore the synchronization target. + * It is always visible and should not affect on other components. */ + if (comp_to->type == NodeType::SYNCHRONIZATION) { + continue; + } + OperationNode *op_from = reinterpret_cast<OperationNode *>(rel->from); ComponentNode *comp_from = op_from->owner; diff --git a/source/blender/depsgraph/intern/node/deg_node_component.cc b/source/blender/depsgraph/intern/node/deg_node_component.cc index 32942f45a4a..f2d82e80fa6 100644 --- a/source/blender/depsgraph/intern/node/deg_node_component.cc +++ b/source/blender/depsgraph/intern/node/deg_node_component.cc @@ -218,10 +218,9 @@ void ComponentNode::clear_operations() void ComponentNode::tag_update(Depsgraph *graph, eUpdateSource source) { - OperationNode *entry_op = get_entry_operation(); - if (entry_op != nullptr && entry_op->flag & DEPSOP_FLAG_NEEDS_UPDATE) { - return; - } + /* Note that the node might already be tagged for an update due invisible state of the node + * during previous dependency evaluation. Here the node gets re-tagged, so we need to give + * the evaluated clues that evaluation needs to happen again. */ for (OperationNode *op_node : operations) { op_node->tag_update(graph, source); } diff --git a/source/blender/depsgraph/intern/node/deg_node_component.h b/source/blender/depsgraph/intern/node/deg_node_component.h index a616dfdfe4c..f7f38b88854 100644 --- a/source/blender/depsgraph/intern/node/deg_node_component.h +++ b/source/blender/depsgraph/intern/node/deg_node_component.h @@ -201,6 +201,7 @@ DEG_COMPONENT_NODE_DECLARE_GENERIC(ShadingParameters); DEG_COMPONENT_NODE_DECLARE_GENERIC(Transform); DEG_COMPONENT_NODE_DECLARE_NO_COW_TAG_ON_UPDATE(ObjectFromLayer); DEG_COMPONENT_NODE_DECLARE_GENERIC(Dupli); +DEG_COMPONENT_NODE_DECLARE_GENERIC(Synchronization); DEG_COMPONENT_NODE_DECLARE_GENERIC(Audio); DEG_COMPONENT_NODE_DECLARE_GENERIC(Armature); DEG_COMPONENT_NODE_DECLARE_GENERIC(GenericDatablock); @@ -208,25 +209,6 @@ DEG_COMPONENT_NODE_DECLARE_NO_COW_TAG_ON_UPDATE(Visibility); DEG_COMPONENT_NODE_DECLARE_GENERIC(Simulation); DEG_COMPONENT_NODE_DECLARE_GENERIC(NTreeOutput); -/* Synchronization Component. */ -struct SynchronizationComponentNode : public ComponentNode { - SynchronizationComponentNode() - { - /* Enforce "visibility" of the synchronization component. - * - * This component is never connected to other ID nodes, and hence can not be handled in the - * same way as other components needed for evaluation. It is only needed for proper - * evaluation of the ID node it belongs to. - * - * The design is such that the synchronization is supposed to happen whenever any part of the - * ID changed/evaluated. Here we mark the component as "visible" so that genetic recalc flag - * flushing and scheduling will handle the component in a generic manner. */ - affects_visible_id = true; - } - - DEG_COMPONENT_NODE_DECLARE; -}; - /* Bone Component */ struct BoneComponentNode : public ComponentNode { /** Initialize 'bone component' node - from pointer data given. */ diff --git a/source/blender/depsgraph/intern/node/deg_node_operation.cc b/source/blender/depsgraph/intern/node/deg_node_operation.cc index 3029379d141..de4bc0668cf 100644 --- a/source/blender/depsgraph/intern/node/deg_node_operation.cc +++ b/source/blender/depsgraph/intern/node/deg_node_operation.cc @@ -218,9 +218,13 @@ string OperationNode::full_identifier() const void OperationNode::tag_update(Depsgraph *graph, eUpdateSource source) { - if ((flag & DEPSOP_FLAG_NEEDS_UPDATE) == 0) { - graph->add_entry_tag(this); - } + /* Ensure that there is an entry tag for this update. + * + * Note that the node might already be tagged for an update due invisible state of the node + * during previous dependency evaluation. Here the node gets re-tagged, so we need to give + * the evaluated clues that evaluation needs to happen again. */ + graph->add_entry_tag(this); + /* Tag for update, but also note that this was the source of an update. */ flag |= (DEPSOP_FLAG_NEEDS_UPDATE | DEPSOP_FLAG_DIRECTLY_MODIFIED); switch (source) { diff --git a/source/blender/editors/animation/anim_channels_edit.c b/source/blender/editors/animation/anim_channels_edit.c index 8464f280c29..f371591c3a8 100644 --- a/source/blender/editors/animation/anim_channels_edit.c +++ b/source/blender/editors/animation/anim_channels_edit.c @@ -1498,7 +1498,8 @@ static int animchannels_rearrange_exec(bContext *C, wmOperator *op) int filter; /* get animdata blocks */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_ANIMDATA); + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_ANIMDATA | + ANIMFILTER_FCURVESONLY); ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); for (ale = anim_data.first; ale; ale = ale->next) { diff --git a/source/blender/editors/sculpt_paint/sculpt_transform.c b/source/blender/editors/sculpt_paint/sculpt_transform.c index 8856e3bf3db..3b810d02b84 100644 --- a/source/blender/editors/sculpt_paint/sculpt_transform.c +++ b/source/blender/editors/sculpt_paint/sculpt_transform.c @@ -289,9 +289,6 @@ static void sculpt_transform_radius_elastic(Sculpt *sd, Object *ob, const float flip_v3_v3(data.elastic_transform_pivot, ss->pivot_pos, symmpass); flip_v3_v3(data.elastic_transform_pivot_init, ss->init_pivot_pos, symmpass); - printf( - "%.2f %.2f %.2f\n", ss->init_pivot_pos[0], ss->init_pivot_pos[1], ss->init_pivot_pos[2]); - const int symm_area = SCULPT_get_vertex_symm_area(data.elastic_transform_pivot); copy_m4_m4(data.elastic_transform_mat, data.transform_mats[symm_area]); BLI_task_parallel_range( diff --git a/source/blender/editors/space_node/node_edit.cc b/source/blender/editors/space_node/node_edit.cc index ffc9befc81c..0b1f2037292 100644 --- a/source/blender/editors/space_node/node_edit.cc +++ b/source/blender/editors/space_node/node_edit.cc @@ -1080,6 +1080,10 @@ static int node_resize_modal(bContext *C, wmOperator *op, const wmEvent *event) } break; } + case EVT_ESCKEY: + node_resize_exit(C, op, true); + ED_region_tag_redraw(region); + return OPERATOR_CANCELLED; } return OPERATOR_RUNNING_MODAL; diff --git a/source/blender/geometry/GEO_mesh_to_curve.hh b/source/blender/geometry/GEO_mesh_to_curve.hh index f619aaff217..b0f24853dbc 100644 --- a/source/blender/geometry/GEO_mesh_to_curve.hh +++ b/source/blender/geometry/GEO_mesh_to_curve.hh @@ -21,4 +21,9 @@ namespace blender::geometry { */ bke::CurvesGeometry mesh_to_curve_convert(const Mesh &mesh, const IndexMask selection); +bke::CurvesGeometry create_curve_from_vert_indices(const Mesh &mesh, + Span<int> vert_indices, + Span<int> curve_offsets, + IndexRange cyclic_curves); + } // namespace blender::geometry diff --git a/source/blender/geometry/intern/mesh_to_curve_convert.cc b/source/blender/geometry/intern/mesh_to_curve_convert.cc index fdacb174462..350dfe73d57 100644 --- a/source/blender/geometry/intern/mesh_to_curve_convert.cc +++ b/source/blender/geometry/intern/mesh_to_curve_convert.cc @@ -30,10 +30,10 @@ static void copy_with_map(const VArray<T> &src, Span<int> map, MutableSpan<T> ds }); } -static bke::CurvesGeometry create_curve_from_vert_indices(const Mesh &mesh, - const Span<int> vert_indices, - const Span<int> curve_offsets, - const IndexRange cyclic_curves) +bke::CurvesGeometry create_curve_from_vert_indices(const Mesh &mesh, + const Span<int> vert_indices, + const Span<int> curve_offsets, + const IndexRange cyclic_curves) { bke::CurvesGeometry curves(vert_indices.size(), curve_offsets.size()); curves.offsets_for_write().drop_back(1).copy_from(curve_offsets); diff --git a/source/blender/geometry/intern/resample_curves.cc b/source/blender/geometry/intern/resample_curves.cc index 86c1980d9ee..d61941aa071 100644 --- a/source/blender/geometry/intern/resample_curves.cc +++ b/source/blender/geometry/intern/resample_curves.cc @@ -345,6 +345,7 @@ Curves *resample_to_evaluated(const CurveComponent &src_component, { const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap( src_component.get_for_read()->geometry); + src_curves.ensure_evaluated_offsets(); bke::GeometryComponentFieldContext field_context{src_component, ATTR_DOMAIN_CURVE}; fn::FieldEvaluator evaluator{field_context, src_curves.curves_num()}; diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h index 6bc68baa640..1ea6fbbaf83 100644 --- a/source/blender/makesdna/DNA_space_types.h +++ b/source/blender/makesdna/DNA_space_types.h @@ -8,6 +8,8 @@ #pragma once +#include "BLI_utildefines.h" + #include "DNA_asset_types.h" #include "DNA_color_types.h" /* for Histogram */ #include "DNA_defs.h" @@ -1028,6 +1030,7 @@ typedef enum eFileSel_Params_Flag { /** Enables filtering by asset catalog. */ FILE_FILTER_ASSET_CATALOG = (1 << 15), } eFileSel_Params_Flag; +ENUM_OPERATORS(eFileSel_Params_Flag, FILE_FILTER_ASSET_CATALOG); typedef enum eFileSel_Params_AssetCatalogVisibility { FILE_SHOW_ASSETS_ALL_CATALOGS, diff --git a/source/blender/makesrna/intern/CMakeLists.txt b/source/blender/makesrna/intern/CMakeLists.txt index 778c6a6bfdd..aaadd36341f 100644 --- a/source/blender/makesrna/intern/CMakeLists.txt +++ b/source/blender/makesrna/intern/CMakeLists.txt @@ -26,6 +26,7 @@ set(DEFSRC rna_context.c rna_curve.c rna_curveprofile.c + rna_curves.c rna_depsgraph.c rna_dynamicpaint.c rna_fcurve.c @@ -85,7 +86,6 @@ set(DEFSRC if(WITH_EXPERIMENTAL_FEATURES) add_definitions(-DWITH_SIMULATION_DATABLOCK) list(APPEND DEFSRC - rna_curves.c rna_simulation.c ) endif() diff --git a/source/blender/makesrna/intern/rna_action.c b/source/blender/makesrna/intern/rna_action.c index ac90ec69784..bcfb646ca19 100644 --- a/source/blender/makesrna/intern/rna_action.c +++ b/source/blender/makesrna/intern/rna_action.c @@ -696,6 +696,11 @@ static void rna_def_action_group(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Lock", "Action group is locked"); RNA_def_property_update(prop, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL); + prop = RNA_def_property(srna, "mute", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", AGRP_MUTED); + RNA_def_property_ui_text(prop, "Mute", "Action group is muted"); + RNA_def_property_update(prop, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL); + prop = RNA_def_property(srna, "show_expanded", PROP_BOOLEAN, PROP_NONE); RNA_def_property_flag(prop, PROP_NO_DEG_UPDATE); RNA_def_property_boolean_sdna(prop, NULL, "flag", AGRP_EXPANDED); diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index a3c5b14d6f5..24ba8f0fd30 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -10754,7 +10754,7 @@ static void def_geo_field_at_index(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_GeometryNode_socket_update"); } -static void def_geo_field_on_domain(StructRNA *srna) +static void def_geo_interpolate_domain(StructRNA *srna) { PropertyRNA *prop; diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h index 86c276fbd6f..e16cd7a253f 100644 --- a/source/blender/nodes/NOD_geometry.h +++ b/source/blender/nodes/NOD_geometry.h @@ -49,17 +49,17 @@ void register_node_type_geo_curve_to_points(void); void register_node_type_geo_curve_trim(void); void register_node_type_geo_deform_curves_on_surface(void); void register_node_type_geo_delete_geometry(void); -void register_node_type_geo_duplicate_elements(void); void register_node_type_geo_distribute_points_on_faces(void); void register_node_type_geo_dual_mesh(void); +void register_node_type_geo_duplicate_elements(void); +void register_node_type_geo_edge_paths_to_curves(void); +void register_node_type_geo_edge_paths_to_selection(void); void register_node_type_geo_edge_split(void); void register_node_type_geo_extrude_mesh(void); void register_node_type_geo_field_at_index(void); -void register_node_type_geo_field_on_domain(void); void register_node_type_geo_flip_faces(void); void register_node_type_geo_geometry_to_instance(void); void register_node_type_geo_image_texture(void); -void register_node_type_geo_input_named_attribute(void); void register_node_type_geo_input_curve_handles(void); void register_node_type_geo_input_curve_tilt(void); void register_node_type_geo_input_id(void); @@ -76,17 +76,20 @@ void register_node_type_geo_input_mesh_face_is_planar(void); void register_node_type_geo_input_mesh_face_neighbors(void); void register_node_type_geo_input_mesh_island(void); void register_node_type_geo_input_mesh_vertex_neighbors(void); +void register_node_type_geo_input_named_attribute(void); void register_node_type_geo_input_normal(void); void register_node_type_geo_input_position(void); void register_node_type_geo_input_radius(void); void register_node_type_geo_input_scene_time(void); void register_node_type_geo_input_shade_smooth(void); +void register_node_type_geo_input_shortest_edge_paths(void); void register_node_type_geo_input_spline_cyclic(void); void register_node_type_geo_input_spline_length(void); void register_node_type_geo_input_spline_resolution(void); void register_node_type_geo_input_tangent(void); void register_node_type_geo_instance_on_points(void); void register_node_type_geo_instances_to_points(void); +void register_node_type_geo_interpolate_domain(void); void register_node_type_geo_is_viewport(void); void register_node_type_geo_join_geometry(void); void register_node_type_geo_material_replace(void); @@ -105,9 +108,9 @@ void register_node_type_geo_mesh_to_curve(void); void register_node_type_geo_mesh_to_points(void); void register_node_type_geo_mesh_to_volume(void); void register_node_type_geo_object_info(void); -void register_node_type_geo_points(void); void register_node_type_geo_points_to_vertices(void); void register_node_type_geo_points_to_volume(void); +void register_node_type_geo_points(void); void register_node_type_geo_proximity(void); void register_node_type_geo_raycast(void); void register_node_type_geo_realize_instances(void); @@ -138,11 +141,11 @@ void register_node_type_geo_transfer_attribute(void); void register_node_type_geo_transform(void); void register_node_type_geo_translate_instances(void); void register_node_type_geo_triangulate(void); +void register_node_type_geo_uv_pack_islands(void); +void register_node_type_geo_uv_unwrap(void); void register_node_type_geo_viewer(void); void register_node_type_geo_volume_cube(void); void register_node_type_geo_volume_to_mesh(void); -void register_node_type_geo_uv_pack_islands(void); -void register_node_type_geo_uv_unwrap(void); #ifdef __cplusplus } diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index db0204c5426..1edb4e28d3a 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -279,6 +279,7 @@ DefNode(FunctionNode, FN_NODE_SLICE_STRING, 0, "SLICE_STRING", SliceString, "Sli DefNode(FunctionNode, FN_NODE_STRING_LENGTH, 0, "STRING_LENGTH", StringLength, "String Length", "") DefNode(FunctionNode, FN_NODE_VALUE_TO_STRING, 0, "VALUE_TO_STRING", ValueToString, "Value to String", "") +DefNode(GeometryNode, GEO_NODE_ACCUMULATE_FIELD, def_geo_accumulate_field, "ACCUMULATE_FIELD", AccumulateField, "Accumulate Field", "Add the values of an evaluated field together and output the running total for each element") DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_DOMAIN_SIZE, def_geo_attribute_domain_size, "ATTRIBUTE_DOMAIN_SIZE", AttributeDomainSize, "Domain Size", "Retrieve the number of elements in a geometry for each attribute domain") DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_STATISTIC, def_geo_attribute_statistic, "ATTRIBUTE_STATISTIC",AttributeStatistic, "Attribute Statistic","Calculate statistics about a data set from a field evaluated on a geometry") DefNode(GeometryNode, GEO_NODE_BOUNDING_BOX, 0, "BOUNDING_BOX", BoundBox, "Bounding Box", "Calculate the limits of a geometry's positions and generate a box mesh with those dimensions") @@ -303,19 +304,18 @@ DefNode(GeometryNode, GEO_NODE_CURVE_TO_MESH, 0, "CURVE_TO_MESH", CurveToMesh, " DefNode(GeometryNode, GEO_NODE_CURVE_TO_POINTS, def_geo_curve_to_points, "CURVE_TO_POINTS", CurveToPoints, "Curve to Points", "Generate a point cloud by sampling positions along curves") DefNode(GeometryNode, GEO_NODE_DEFORM_CURVES_ON_SURFACE, 0, "DEFORM_CURVES_ON_SURFACE", DeformCurvesOnSurface, "Deform Curves on Surface", "Translate and rotate curves based on changes between the object's original and evaluated surface mesh") DefNode(GeometryNode, GEO_NODE_DELETE_GEOMETRY, def_geo_delete_geometry, "DELETE_GEOMETRY", DeleteGeometry, "Delete Geometry", "Remove selected elements of a geometry") -DefNode(GeometryNode, GEO_NODE_DUPLICATE_ELEMENTS, def_geo_duplicate_elements, "DUPLICATE_ELEMENTS", DuplicateElements, "Duplicate Elements", "Generate an arbitrary number copies of each selected input element") DefNode(GeometryNode, GEO_NODE_DISTRIBUTE_POINTS_ON_FACES, def_geo_distribute_points_on_faces, "DISTRIBUTE_POINTS_ON_FACES", DistributePointsOnFaces, "Distribute Points on Faces", "Generate points spread out on the surface of a mesh") -DefNode(GeometryNode, GEO_NODE_ACCUMULATE_FIELD, def_geo_accumulate_field, "ACCUMULATE_FIELD", AccumulateField, "Accumulate Field", "Add the values of an evaluated field together and output the running total for each element") DefNode(GeometryNode, GEO_NODE_DUAL_MESH, 0, "DUAL_MESH", DualMesh, "Dual Mesh", "Convert Faces into vertices and vertices into faces") +DefNode(GeometryNode, GEO_NODE_DUPLICATE_ELEMENTS, def_geo_duplicate_elements, "DUPLICATE_ELEMENTS", DuplicateElements, "Duplicate Elements", "Generate an arbitrary number copies of each selected input element") +DefNode(GeometryNode, GEO_NODE_EDGE_PATHS_TO_CURVES, 0, "EDGE_PATHS_TO_CURVES", EdgePathsToCurves, "Edge Paths to Curves", "") +DefNode(GeometryNode, GEO_NODE_EDGE_PATHS_TO_SELECTION, 0, "EDGE_PATHS_TO_SELECTION", EdgePathsToSelection, "Edge Paths to Selection", "") DefNode(GeometryNode, GEO_NODE_EXTRUDE_MESH, def_geo_extrude_mesh, "EXTRUDE_MESH", ExtrudeMesh, "Extrude Mesh", "Generate new vertices, edges, or faces from selected elements and move them based on an offset while keeping them connected by their boundary") DefNode(GeometryNode, GEO_NODE_FIELD_AT_INDEX, def_geo_field_at_index, "FIELD_AT_INDEX", FieldAtIndex, "Field at Index", "Retrieve data of other elements in the context's geometry") -DefNode(GeometryNode, GEO_NODE_FIELD_ON_DOMAIN, def_geo_field_on_domain, "FIELD_ON_DOMAIN", FieldOnDomain, "Field on Domain", "Retrieve values from a field on a different domain besides the domain from the context") DefNode(GeometryNode, GEO_NODE_FILL_CURVE, def_geo_curve_fill, "FILL_CURVE", FillCurve, "Fill Curve", "Generate a mesh on the XY plane with faces on the inside of input curves") DefNode(GeometryNode, GEO_NODE_FILLET_CURVE, def_geo_curve_fillet, "FILLET_CURVE", FilletCurve, "Fillet Curve", "Round corners by generating circular arcs on each control point") DefNode(GeometryNode, GEO_NODE_FLIP_FACES, 0, "FLIP_FACES", FlipFaces, "Flip Faces", "Reverse the order of the vertices and edges of selected faces, flipping their normal direction") DefNode(GeometryNode, GEO_NODE_GEOMETRY_TO_INSTANCE, 0, "GEOMETRY_TO_INSTANCE", GeometryToInstance, "Geometry to Instance", "Convert each input geometry into an instance, which can be much faster than the Join Geometry node when the inputs are large") DefNode(GeometryNode, GEO_NODE_IMAGE_TEXTURE, def_geo_image_texture, "IMAGE_TEXTURE", ImageTexture, "Image Texture", "Sample values from an image texture") -DefNode(GeometryNode, GEO_NODE_INPUT_NAMED_ATTRIBUTE, def_geo_input_named_attribute, "INPUT_ATTRIBUTE", InputNamedAttribute, "Named Attribute", "Retrieve the data of a specified attribute") DefNode(GeometryNode, GEO_NODE_INPUT_CURVE_HANDLES, 0, "INPUT_CURVE_HANDLES",InputCurveHandlePositions,"Curve Handle Positions", "Retrieve the position of each Bézier control point's handles") DefNode(GeometryNode, GEO_NODE_INPUT_CURVE_TILT, 0, "INPUT_CURVE_TILT", InputCurveTilt, "Curve Tilt", "Retrieve the angle at each control point used to twist the curve's normal around its tangent") DefNode(GeometryNode, GEO_NODE_INPUT_ID, 0, "INPUT_ID", InputID, "ID", "Retrieve a stable random identifier value from the \"id\" attribute on the point domain, or the index if the attribute does not exist") @@ -332,17 +332,20 @@ DefNode(GeometryNode, GEO_NODE_INPUT_MESH_FACE_IS_PLANAR, 0, "MESH_FACE_IS_PLANA DefNode(GeometryNode, GEO_NODE_INPUT_MESH_FACE_NEIGHBORS, 0, "MESH_FACE_NEIGHBORS",InputMeshFaceNeighbors, "Face Neighbors", "Retrieve topology information relating to each face of a mesh") DefNode(GeometryNode, GEO_NODE_INPUT_MESH_ISLAND, 0, "MESH_ISLAND", InputMeshIsland, "Mesh Island", "Retrieve information about separate connected regions in a mesh") DefNode(GeometryNode, GEO_NODE_INPUT_MESH_VERTEX_NEIGHBORS, 0, "MESH_VERTEX_NEIGHBORS", InputMeshVertexNeighbors, "Vertex Neighbors", "Retrieve topology information relating to each vertex of a mesh") +DefNode(GeometryNode, GEO_NODE_INPUT_NAMED_ATTRIBUTE, def_geo_input_named_attribute, "INPUT_ATTRIBUTE", InputNamedAttribute, "Named Attribute", "Retrieve the data of a specified attribute") DefNode(GeometryNode, GEO_NODE_INPUT_NORMAL, 0, "INPUT_NORMAL", InputNormal, "Normal", "Retrieve a unit length vector indicating the direction pointing away from the geometry at each element") DefNode(GeometryNode, GEO_NODE_INPUT_POSITION, 0, "POSITION", InputPosition, "Position", "Retrieve a vector indicating the location of each element") DefNode(GeometryNode, GEO_NODE_INPUT_RADIUS, 0, "INPUT_RADIUS", InputRadius, "Radius", "Retrieve the radius at each point on curve or point cloud geometry") DefNode(GeometryNode, GEO_NODE_INPUT_SCENE_TIME, 0, "INPUT_SCENE_TIME", InputSceneTime, "Scene Time", "Retrieve the current time in the scene's animation in units of seconds or frames") DefNode(GeometryNode, GEO_NODE_INPUT_SHADE_SMOOTH, 0, "INPUT_SHADE_SMOOTH", InputShadeSmooth, "Is Shade Smooth", "Retrieve whether each face is marked for smooth shading") +DefNode(GeometryNode, GEO_NODE_INPUT_SHORTEST_EDGE_PATHS, 0, "SHORTEST_EDGE_PATHS", InputShortestEdgePaths, "Shortest Edge Paths", "") DefNode(GeometryNode, GEO_NODE_INPUT_SPLINE_CYCLIC, 0, "INPUT_SPLINE_CYCLIC",InputSplineCyclic, "Is Spline Cyclic", "Retrieve whether each spline endpoint connects to the beginning") DefNode(GeometryNode, GEO_NODE_INPUT_SPLINE_LENGTH, 0, "SPLINE_LENGTH", SplineLength, "Spline Length", "Retrieve the total length of each spline, as a distance or as a number of points") DefNode(GeometryNode, GEO_NODE_INPUT_SPLINE_RESOLUTION, 0, "INPUT_SPLINE_RESOLUTION", InputSplineResolution, "Spline Resolution", "Retrieve the number of evaluated points that will be generated for every control point on curves") DefNode(GeometryNode, GEO_NODE_INPUT_TANGENT, 0, "INPUT_TANGENT", InputTangent, "Curve Tangent", "Retrieve the direction of curves at each control point") DefNode(GeometryNode, GEO_NODE_INSTANCE_ON_POINTS, 0, "INSTANCE_ON_POINTS", InstanceOnPoints, "Instance on Points", "Generate a reference to geometry at each of the input points, without duplicating its underlying data") DefNode(GeometryNode, GEO_NODE_INSTANCES_TO_POINTS, 0, "INSTANCES_TO_POINTS",InstancesToPoints, "Instances to Points","Generate points at the origins of instances.\nNote: Nested instances are not affected by this node") +DefNode(GeometryNode, GEO_NODE_INTERPOLATE_DOMAIN, def_geo_interpolate_domain, "FIELD_ON_DOMAIN", FieldOnDomain, "Interpolate Domain", "Retrieve values from a field on a different domain besides the domain from the context") DefNode(GeometryNode, GEO_NODE_IS_VIEWPORT, 0, "IS_VIEWPORT", IsViewport, "Is Viewport", "Retrieve whether the nodes are being evaluated for the viewport rather than the final render") DefNode(GeometryNode, GEO_NODE_JOIN_GEOMETRY, 0, "JOIN_GEOMETRY", JoinGeometry, "Join Geometry", "Merge separately generated geometries into a single one") DefNode(GeometryNode, GEO_NODE_MATERIAL_SELECTION, 0, "MATERIAL_SELECTION", MaterialSelection, "Material Selection", "Provide a selection of faces that use the specified material") @@ -360,13 +363,13 @@ DefNode(GeometryNode, GEO_NODE_MESH_TO_CURVE, 0, "MESH_TO_CURVE", MeshToCurve, " DefNode(GeometryNode, GEO_NODE_MESH_TO_POINTS, def_geo_mesh_to_points, "MESH_TO_POINTS", MeshToPoints, "Mesh to Points", "Generate a point cloud from a mesh's vertices") DefNode(GeometryNode, GEO_NODE_MESH_TO_VOLUME, def_geo_mesh_to_volume, "MESH_TO_VOLUME", MeshToVolume, "Mesh To Volume", "Create a fog volume with the shape of the input mesh's surface") DefNode(GeometryNode, GEO_NODE_OBJECT_INFO, def_geo_object_info, "OBJECT_INFO", ObjectInfo, "Object Info", "Retrieve information from an object") -DefNode(GeometryNode, GEO_NODE_POINTS, 0, "POINTS", Points, "Points", "Generate a point cloud with positions and radii defined by fields") DefNode(GeometryNode, GEO_NODE_POINTS_TO_VERTICES, 0, "POINTS_TO_VERTICES", PointsToVertices, "Points to Vertices", "Generate a mesh vertex for each point cloud point") DefNode(GeometryNode, GEO_NODE_POINTS_TO_VOLUME, def_geo_points_to_volume, "POINTS_TO_VOLUME", PointsToVolume, "Points to Volume", "Generate a fog volume sphere around every point") +DefNode(GeometryNode, GEO_NODE_POINTS, 0, "POINTS", Points, "Points", "Generate a point cloud with positions and radii defined by fields") DefNode(GeometryNode, GEO_NODE_PROXIMITY, def_geo_proximity, "PROXIMITY", Proximity, "Geometry Proximity", "Compute the closest location on the target geometry") DefNode(GeometryNode, GEO_NODE_RAYCAST, def_geo_raycast, "RAYCAST", Raycast, "Raycast", "Cast rays from the context geometry onto a target geometry, and retrieve information from each hit point") -DefNode(GeometryNode, GEO_NODE_REMOVE_ATTRIBUTE, 0, "REMOVE_ATTRIBUTE", RemoveAttribute, "Remove Named Attribute", "Delete an attribute with a specified name from a geometry. Typically used to optimize performance") DefNode(GeometryNode, GEO_NODE_REALIZE_INSTANCES, def_geo_realize_instances,"REALIZE_INSTANCES", RealizeInstances, "Realize Instances", "Change the direction of the curve by swapping each spline's start and end data") +DefNode(GeometryNode, GEO_NODE_REMOVE_ATTRIBUTE, 0, "REMOVE_ATTRIBUTE", RemoveAttribute, "Remove Named Attribute", "Delete an attribute with a specified name from a geometry. Typically used to optimize performance") DefNode(GeometryNode, GEO_NODE_REPLACE_MATERIAL, 0, "REPLACE_MATERIAL", ReplaceMaterial, "Replace Material", "Swap one material with another") DefNode(GeometryNode, GEO_NODE_RESAMPLE_CURVE, def_geo_curve_resample, "RESAMPLE_CURVE", ResampleCurve, "Resample Curve", "Generate a poly spline for each input spline") DefNode(GeometryNode, GEO_NODE_REVERSE_CURVE, 0, "REVERSE_CURVE", ReverseCurve, "Reverse Curve", "Swap the start and end of splines") @@ -400,11 +403,11 @@ DefNode(GeometryNode, GEO_NODE_TRANSFORM, 0, "TRANSFORM", Transform, "Transform" DefNode(GeometryNode, GEO_NODE_TRANSLATE_INSTANCES, 0, "TRANSLATE_INSTANCES",TranslateInstances, "Translate Instances","Move top-level geometry instances in local or global space") DefNode(GeometryNode, GEO_NODE_TRIANGULATE, def_geo_triangulate, "TRIANGULATE", Triangulate, "Triangulate", "Convert all faces in a mesh to triangular faces") DefNode(GeometryNode, GEO_NODE_TRIM_CURVE, def_geo_curve_trim, "TRIM_CURVE", TrimCurve, "Trim Curve", "Shorten curves by removing portions at the start or end") +DefNode(GeometryNode, GEO_NODE_UV_PACK_ISLANDS, 0, "UV_PACK_ISLANDS", UVPackIslands, "Pack UV Islands", "Scale islands of a UV map and move them so they fill the UV space as much as possible") +DefNode(GeometryNode, GEO_NODE_UV_UNWRAP, def_geo_uv_unwrap, "UV_UNWRAP", UVUnwrap, "UV Unwrap", "Generate a UV map islands based on seam edges") DefNode(GeometryNode, GEO_NODE_VIEWER, def_geo_viewer, "VIEWER", Viewer, "Viewer", "Display the input data in the Spreadsheet Editor") DefNode(GeometryNode, GEO_NODE_VOLUME_CUBE, 0, "VOLUME_CUBE", VolumeCube, "Volume Cube", "Generate a dense volume with a field that controls the density at each grid voxel based on its position") DefNode(GeometryNode, GEO_NODE_VOLUME_TO_MESH, def_geo_volume_to_mesh, "VOLUME_TO_MESH", VolumeToMesh, "Volume to Mesh", "Generate a mesh on the \"surface\" of a volume") -DefNode(GeometryNode, GEO_NODE_UV_PACK_ISLANDS, 0, "UV_PACK_ISLANDS", UVPackIslands, "Pack UV Islands", "Scale islands of a UV map and move them so they fill the UV space as much as possible") -DefNode(GeometryNode, GEO_NODE_UV_UNWRAP, def_geo_uv_unwrap, "UV_UNWRAP", UVUnwrap, "UV Unwrap", "Generate a UV map islands based on seam edges") /* undefine macros */ #undef DefNode diff --git a/source/blender/nodes/geometry/CMakeLists.txt b/source/blender/nodes/geometry/CMakeLists.txt index d87f0312958..ddd8c8949b1 100644 --- a/source/blender/nodes/geometry/CMakeLists.txt +++ b/source/blender/nodes/geometry/CMakeLists.txt @@ -62,10 +62,11 @@ set(SRC nodes/node_geo_distribute_points_on_faces.cc nodes/node_geo_dual_mesh.cc nodes/node_geo_duplicate_elements.cc + nodes/node_geo_edge_paths_to_curves.cc + nodes/node_geo_edge_paths_to_selection.cc nodes/node_geo_edge_split.cc nodes/node_geo_extrude_mesh.cc nodes/node_geo_field_at_index.cc - nodes/node_geo_field_on_domain.cc nodes/node_geo_flip_faces.cc nodes/node_geo_geometry_to_instance.cc nodes/node_geo_image_texture.cc @@ -75,8 +76,8 @@ set(SRC nodes/node_geo_input_index.cc nodes/node_geo_input_instance_rotation.cc nodes/node_geo_input_instance_scale.cc - nodes/node_geo_input_material.cc nodes/node_geo_input_material_index.cc + nodes/node_geo_input_material.cc nodes/node_geo_input_mesh_edge_angle.cc nodes/node_geo_input_mesh_edge_neighbors.cc nodes/node_geo_input_mesh_edge_vertices.cc @@ -91,12 +92,14 @@ set(SRC nodes/node_geo_input_radius.cc nodes/node_geo_input_scene_time.cc nodes/node_geo_input_shade_smooth.cc + nodes/node_geo_input_shortest_edge_paths.cc nodes/node_geo_input_spline_cyclic.cc nodes/node_geo_input_spline_length.cc nodes/node_geo_input_spline_resolution.cc nodes/node_geo_input_tangent.cc nodes/node_geo_instance_on_points.cc nodes/node_geo_instances_to_points.cc + nodes/node_geo_interpolate_domain.cc nodes/node_geo_is_viewport.cc nodes/node_geo_join_geometry.cc nodes/node_geo_material_replace.cc @@ -115,9 +118,9 @@ set(SRC nodes/node_geo_mesh_to_points.cc nodes/node_geo_mesh_to_volume.cc nodes/node_geo_object_info.cc - nodes/node_geo_points.cc nodes/node_geo_points_to_vertices.cc nodes/node_geo_points_to_volume.cc + nodes/node_geo_points.cc nodes/node_geo_proximity.cc nodes/node_geo_raycast.cc nodes/node_geo_realize_instances.cc @@ -131,8 +134,8 @@ set(SRC nodes/node_geo_set_curve_radius.cc nodes/node_geo_set_curve_tilt.cc nodes/node_geo_set_id.cc - nodes/node_geo_set_material.cc nodes/node_geo_set_material_index.cc + nodes/node_geo_set_material.cc nodes/node_geo_set_point_radius.cc nodes/node_geo_set_position.cc nodes/node_geo_set_shade_smooth.cc diff --git a/source/blender/nodes/geometry/nodes/node_geo_edge_paths_to_curves.cc b/source/blender/nodes/geometry/nodes/node_geo_edge_paths_to_curves.cc new file mode 100644 index 00000000000..89abfa0aa88 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_edge_paths_to_curves.cc @@ -0,0 +1,113 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BKE_curves.hh" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "GEO_mesh_to_curve.hh" + +#include "node_geometry_util.hh" + +namespace blender::nodes::node_geo_edge_paths_to_curves_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>(N_("Mesh")).supported_type(GEO_COMPONENT_TYPE_MESH); + b.add_input<decl::Bool>(N_("Start Vertices")).default_value(true).hide_value().supports_field(); + b.add_input<decl::Int>(N_("Next Vertex Index")).default_value(-1).hide_value().supports_field(); + b.add_output<decl::Geometry>(N_("Curves")); +} + +static Curves *edge_paths_to_curves_convert(const Mesh &mesh, + const IndexMask start_verts_mask, + const Span<int> next_indices) +{ + const Span<MVert> mvert{mesh.mvert, mesh.totvert}; + Vector<int> vert_indices; + Vector<int> curve_offsets; + Array<bool> visited(mesh.totvert, false); + for (const int first_vert : start_verts_mask) { + const int second_vert = next_indices[first_vert]; + if (first_vert == second_vert) { + continue; + } + if (second_vert < 0 || second_vert >= mesh.totvert) { + continue; + } + + curve_offsets.append(vert_indices.size()); + + /* Iterate through path defined by #next_indices. */ + int current_vert = first_vert; + while (!visited[current_vert]) { + visited[current_vert] = true; + vert_indices.append(current_vert); + const int next_vert = next_indices[current_vert]; + if (next_vert < 0 || next_vert >= mesh.totvert) { + break; + } + current_vert = next_vert; + } + + /* Reset visited status. */ + const int points_in_curve_num = vert_indices.size() - curve_offsets.last(); + for (const int vert_in_curve : vert_indices.as_span().take_back(points_in_curve_num)) { + visited[vert_in_curve] = false; + } + } + + if (vert_indices.is_empty()) { + return nullptr; + } + Curves *curves_id = bke::curves_new_nomain( + geometry::create_curve_from_vert_indices(mesh, vert_indices, curve_offsets, IndexRange(0))); + return curves_id; +} + +static void node_geo_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input<GeometrySet>("Mesh"); + + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + if (!geometry_set.has_mesh()) { + geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES}); + return; + } + + const MeshComponent &component = *geometry_set.get_component_for_read<MeshComponent>(); + GeometryComponentFieldContext context{component, ATTR_DOMAIN_POINT}; + fn::FieldEvaluator evaluator{context, component.attribute_domain_size(ATTR_DOMAIN_POINT)}; + evaluator.add(params.get_input<Field<int>>("Next Vertex Index")); + evaluator.add(params.get_input<Field<bool>>("Start Vertices")); + evaluator.evaluate(); + const VArraySpan<int> next_vert = evaluator.get_evaluated<int>(0); + IndexMask start_verts = evaluator.get_evaluated_as_mask(1); + + if (start_verts.is_empty()) { + geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES}); + return; + } + + const Mesh &mesh = *component.get_for_read(); + geometry_set.replace_curves(edge_paths_to_curves_convert(mesh, start_verts, next_vert)); + geometry_set.keep_only({GEO_COMPONENT_TYPE_CURVE, GEO_COMPONENT_TYPE_INSTANCES}); + }); + + params.set_output("Curves", std::move(geometry_set)); +} + +} // namespace blender::nodes::node_geo_edge_paths_to_curves_cc + +void register_node_type_geo_edge_paths_to_curves() +{ + namespace file_ns = blender::nodes::node_geo_edge_paths_to_curves_cc; + + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_EDGE_PATHS_TO_CURVES, "Edge Paths to Curves", NODE_CLASS_GEOMETRY); + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_edge_paths_to_selection.cc b/source/blender/nodes/geometry/nodes/node_geo_edge_paths_to_selection.cc new file mode 100644 index 00000000000..efdf0911ec9 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_edge_paths_to_selection.cc @@ -0,0 +1,140 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BKE_attribute_math.hh" +#include "BKE_mesh.h" + +#include "BLI_map.hh" +#include "BLI_set.hh" +#include "BLI_task.hh" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "node_geometry_util.hh" + +#include <set> + +namespace blender::nodes::node_geo_edge_paths_to_selection_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Bool>(N_("Start Vertices")).default_value(true).hide_value().supports_field(); + b.add_input<decl::Int>(N_("Next Vertex Index")).default_value(-1).hide_value().supports_field(); + b.add_output<decl::Bool>(N_("Selection")).field_source(); +} + +static void edge_paths_to_selection(const Mesh &src_mesh, + const IndexMask start_selection, + const Span<int> next_indices, + MutableSpan<bool> r_selection) +{ + Array<bool> selection(src_mesh.totvert, false); + + for (const int start_vert : start_selection) { + selection[start_vert] = true; + } + + for (const int start_i : start_selection) { + int iter = start_i; + while (iter != next_indices[iter] && !selection[next_indices[iter]]) { + if (next_indices[iter] < 0 || next_indices[iter] >= src_mesh.totvert) { + break; + } + selection[next_indices[iter]] = true; + iter = next_indices[iter]; + } + } + + for (const int i : IndexRange(src_mesh.totedge)) { + const MEdge &edge = src_mesh.medge[i]; + if ((selection[edge.v1] && selection[edge.v2]) && + (edge.v1 == next_indices[edge.v2] || edge.v2 == next_indices[edge.v1])) { + r_selection[i] = true; + } + } +} + +class PathToEdgeSelectionFieldInput final : public GeometryFieldInput { + private: + Field<bool> start_vertices_; + Field<int> next_vertex_; + + public: + PathToEdgeSelectionFieldInput(Field<bool> start_vertices, Field<int> next_vertex) + : GeometryFieldInput(CPPType::get<bool>(), "Edge Selection"), + start_vertices_(start_vertices), + next_vertex_(next_vertex) + { + category_ = Category::Generated; + } + + GVArray get_varray_for_context(const GeometryComponent &component, + const eAttrDomain domain, + [[maybe_unused]] IndexMask mask) const final + { + if (component.type() != GEO_COMPONENT_TYPE_MESH) { + return {}; + } + + const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component); + const Mesh *mesh = mesh_component.get_for_read(); + if (mesh == nullptr) { + return {}; + } + + GeometryComponentFieldContext context{mesh_component, ATTR_DOMAIN_POINT}; + fn::FieldEvaluator evaluator{context, mesh_component.attribute_domain_size(ATTR_DOMAIN_POINT)}; + evaluator.add(next_vertex_); + evaluator.add(start_vertices_); + evaluator.evaluate(); + const VArraySpan<int> next_vert = evaluator.get_evaluated<int>(0); + const IndexMask start_verts = evaluator.get_evaluated_as_mask(1); + + if (start_verts.is_empty()) { + return {}; + } + + Array<bool> selection(mesh->totedge, false); + MutableSpan<bool> selection_span = selection.as_mutable_span(); + + edge_paths_to_selection(*mesh, start_verts, next_vert, selection_span); + + return mesh_component.attributes()->adapt_domain<bool>( + VArray<bool>::ForContainer(std::move(selection)), ATTR_DOMAIN_EDGE, domain); + } + + uint64_t hash() const override + { + return get_default_hash_2(start_vertices_, next_vertex_); + } + + bool is_equal_to(const fn::FieldNode &other) const override + { + return dynamic_cast<const PathToEdgeSelectionFieldInput *>(&other) != nullptr; + } +}; + +static void node_geo_exec(GeoNodeExecParams params) +{ + Field<bool> start_vertices = params.extract_input<Field<bool>>("Start Vertices"); + Field<int> next_vertex = params.extract_input<Field<int>>("Next Vertex Index"); + Field<bool> selection_field{ + std::make_shared<PathToEdgeSelectionFieldInput>(start_vertices, next_vertex)}; + params.set_output("Selection", std::move(selection_field)); +} + +} // namespace blender::nodes::node_geo_edge_paths_to_selection_cc + +void register_node_type_geo_edge_paths_to_selection() +{ + namespace file_ns = blender::nodes::node_geo_edge_paths_to_selection_cc; + + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_EDGE_PATHS_TO_SELECTION, "Edge Paths to Selection", NODE_CLASS_INPUT); + ntype.declare = file_ns::node_declare; + node_type_size(&ntype, 150, 100, 300); + ntype.geometry_node_execute = file_ns::node_geo_exec; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_field_at_index.cc b/source/blender/nodes/geometry/nodes/node_geo_field_at_index.cc index 7d44ac34f15..bde4af12d84 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_field_at_index.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_field_at_index.cc @@ -9,6 +9,8 @@ #include "BLI_task.hh" +#include "NOD_socket_search_link.hh" + namespace blender::nodes::node_geo_field_at_index_cc { static void node_declare(NodeDeclarationBuilder &b) @@ -70,6 +72,23 @@ static void node_update(bNodeTree *ntree, bNode *node) nodeSetSocketAvailability(ntree, sock_out_bool, data_type == CD_PROP_BOOL); } +static void node_gather_link_searches(GatherLinkSearchOpParams ¶ms) +{ + const NodeDeclaration &declaration = *params.node_type().fixed_declaration; + search_link_ops_for_declarations(params, declaration.inputs().take_front(1)); + + const bNodeType &node_type = params.node_type(); + const std::optional<eCustomDataType> type = node_data_type_to_custom_data_type( + (eNodeSocketDatatype)params.other_socket().type); + if (type && *type != CD_PROP_STRING) { + params.add_item(IFACE_("Value"), [node_type, type](LinkSearchOpParams ¶ms) { + bNode &node = params.add_node(node_type); + node.custom2 = *type; + params.update_and_connect_available_socket(node, "Value"); + }); + } +} + class FieldAtIndex final : public GeometryFieldInput { private: Field<int> index_field_; @@ -175,5 +194,6 @@ void register_node_type_geo_field_at_index() ntype.draw_buttons = file_ns::node_layout; ntype.initfunc = file_ns::node_init; ntype.updatefunc = file_ns::node_update; + ntype.gather_link_search_ops = file_ns::node_gather_link_searches; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_shortest_edge_paths.cc b/source/blender/nodes/geometry/nodes/node_geo_input_shortest_edge_paths.cc new file mode 100644 index 00000000000..89abaca3c66 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_input_shortest_edge_paths.cc @@ -0,0 +1,247 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include <queue> + +#include "BKE_curves.hh" + +#include "BLI_map.hh" +#include "BLI_math_vec_types.hh" +#include "BLI_set.hh" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "node_geometry_util.hh" + +namespace blender::nodes::node_geo_input_shortest_edge_paths_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Bool>(N_("End Vertex")).default_value(false).hide_value().supports_field(); + b.add_input<decl::Float>(N_("Edge Cost")).default_value(1.0f).hide_value().supports_field(); + b.add_output<decl::Int>(N_("Next Vertex Index")).field_source(); + b.add_output<decl::Float>(N_("Total Cost")).field_source(); +} + +typedef std::pair<float, int> VertPriority; + +struct EdgeVertMap { + Array<Vector<int>> edges_by_vertex_map; + + EdgeVertMap(const Mesh *mesh) + { + const Span<MEdge> edges{mesh->medge, mesh->totedge}; + edges_by_vertex_map.reinitialize(mesh->totvert); + for (const int edge_i : edges.index_range()) { + const MEdge &edge = edges[edge_i]; + edges_by_vertex_map[edge.v1].append(edge_i); + edges_by_vertex_map[edge.v2].append(edge_i); + } + } +}; + +static void shortest_paths(const Mesh *mesh, + EdgeVertMap &maps, + const IndexMask end_selection, + const VArray<float> &input_cost, + MutableSpan<int> r_next_index, + MutableSpan<float> r_cost) +{ + const Span<MVert> verts{mesh->mvert, mesh->totvert}; + const Span<MEdge> edges{mesh->medge, mesh->totedge}; + Array<bool> visited(mesh->totvert, false); + + std::priority_queue<VertPriority, std::vector<VertPriority>, std::greater<VertPriority>> queue; + + for (const int start_vert_i : end_selection) { + r_cost[start_vert_i] = 0.0f; + queue.emplace(0.0f, start_vert_i); + } + + while (!queue.empty()) { + const float cost_i = queue.top().first; + const int vert_i = queue.top().second; + queue.pop(); + if (visited[vert_i]) { + continue; + } + visited[vert_i] = true; + const Span<int> incident_edge_indices = maps.edges_by_vertex_map[vert_i]; + for (const int edge_i : incident_edge_indices) { + const MEdge &edge = edges[edge_i]; + const int neighbor_vert_i = edge.v1 + edge.v2 - vert_i; + if (visited[neighbor_vert_i]) { + continue; + } + const float edge_cost = std::max(0.0f, input_cost[edge_i]); + const float new_neighbour_cost = cost_i + edge_cost; + if (new_neighbour_cost < r_cost[neighbor_vert_i]) { + r_cost[neighbor_vert_i] = new_neighbour_cost; + r_next_index[neighbor_vert_i] = vert_i; + queue.emplace(new_neighbour_cost, neighbor_vert_i); + } + } + } +} + +class ShortestEdgePathsNextVertFieldInput final : public GeometryFieldInput { + private: + Field<bool> end_selection_; + Field<float> cost_; + + public: + ShortestEdgePathsNextVertFieldInput(Field<bool> end_selection, Field<float> cost) + : GeometryFieldInput(CPPType::get<int>(), "Shortest Edge Paths Next Vertex Field"), + end_selection_(end_selection), + cost_(cost) + { + category_ = Category::Generated; + } + + GVArray get_varray_for_context(const GeometryComponent &component, + const eAttrDomain domain, + [[maybe_unused]] IndexMask mask) const final + { + if (component.type() != GEO_COMPONENT_TYPE_MESH) { + return {}; + } + const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component); + const Mesh *mesh = mesh_component.get_for_read(); + if (mesh == nullptr) { + return {}; + } + GeometryComponentFieldContext edge_context{component, ATTR_DOMAIN_EDGE}; + fn::FieldEvaluator edge_evaluator{edge_context, mesh->totedge}; + edge_evaluator.add(cost_); + edge_evaluator.evaluate(); + const VArray<float> input_cost = edge_evaluator.get_evaluated<float>(0); + + GeometryComponentFieldContext point_context{component, ATTR_DOMAIN_POINT}; + fn::FieldEvaluator point_evaluator{point_context, mesh->totvert}; + point_evaluator.add(end_selection_); + point_evaluator.evaluate(); + const IndexMask end_selection = point_evaluator.get_evaluated_as_mask(0); + + Array<int> next_index(mesh->totvert, -1); + Array<float> cost(mesh->totvert, FLT_MAX); + + if (!end_selection.is_empty()) { + EdgeVertMap maps(mesh); + shortest_paths(mesh, maps, end_selection, input_cost, next_index, cost); + } + threading::parallel_for(next_index.index_range(), 1024, [&](const IndexRange range) { + for (const int i : range) { + if (next_index[i] == -1) { + next_index[i] = i; + } + } + }); + return component.attributes()->adapt_domain<int>( + VArray<int>::ForContainer(std::move(next_index)), ATTR_DOMAIN_POINT, domain); + } + + uint64_t hash() const override + { + /* Some random constant hash. */ + return 8466507837; + } + + bool is_equal_to(const fn::FieldNode &other) const override + { + return dynamic_cast<const ShortestEdgePathsNextVertFieldInput *>(&other) != nullptr; + } +}; + +class ShortestEdgePathsCostFieldInput final : public GeometryFieldInput { + private: + Field<bool> end_selection_; + Field<float> cost_; + + public: + ShortestEdgePathsCostFieldInput(Field<bool> end_selection, Field<float> cost) + : GeometryFieldInput(CPPType::get<float>(), "Shortest Edge Paths Cost Field"), + end_selection_(end_selection), + cost_(cost) + { + category_ = Category::Generated; + } + + GVArray get_varray_for_context(const GeometryComponent &component, + const eAttrDomain domain, + [[maybe_unused]] IndexMask mask) const final + { + if (component.type() != GEO_COMPONENT_TYPE_MESH) { + return {}; + } + const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component); + const Mesh *mesh = mesh_component.get_for_read(); + if (mesh == nullptr) { + return {}; + } + GeometryComponentFieldContext edge_context{component, ATTR_DOMAIN_EDGE}; + fn::FieldEvaluator edge_evaluator{edge_context, mesh->totedge}; + edge_evaluator.add(cost_); + edge_evaluator.evaluate(); + const VArray<float> input_cost = edge_evaluator.get_evaluated<float>(0); + + GeometryComponentFieldContext point_context{component, ATTR_DOMAIN_POINT}; + fn::FieldEvaluator point_evaluator{point_context, mesh->totvert}; + point_evaluator.add(end_selection_); + point_evaluator.evaluate(); + const IndexMask end_selection = point_evaluator.get_evaluated_as_mask(0); + + Array<int> next_index(mesh->totvert, -1); + Array<float> cost(mesh->totvert, FLT_MAX); + + if (!end_selection.is_empty()) { + EdgeVertMap maps(mesh); + shortest_paths(mesh, maps, end_selection, input_cost, next_index, cost); + } + threading::parallel_for(cost.index_range(), 1024, [&](const IndexRange range) { + for (const int i : range) { + if (cost[i] == FLT_MAX) { + cost[i] = 0; + } + } + }); + return component.attributes()->adapt_domain<float>( + VArray<float>::ForContainer(std::move(cost)), ATTR_DOMAIN_POINT, domain); + } + + uint64_t hash() const override + { + return get_default_hash_2(end_selection_, cost_); + } + + bool is_equal_to(const fn::FieldNode &other) const override + { + return dynamic_cast<const ShortestEdgePathsCostFieldInput *>(&other) != nullptr; + } +}; + +static void node_geo_exec(GeoNodeExecParams params) +{ + Field<bool> end_selection = params.extract_input<Field<bool>>("End Vertex"); + Field<float> cost = params.extract_input<Field<float>>("Edge Cost"); + + Field<int> next_vert_field{ + std::make_shared<ShortestEdgePathsNextVertFieldInput>(end_selection, cost)}; + Field<float> cost_field{std::make_shared<ShortestEdgePathsCostFieldInput>(end_selection, cost)}; + params.set_output("Next Vertex Index", std::move(next_vert_field)); + params.set_output("Total Cost", std::move(cost_field)); +} + +} // namespace blender::nodes::node_geo_input_shortest_edge_paths_cc + +void register_node_type_geo_input_shortest_edge_paths() +{ + namespace file_ns = blender::nodes::node_geo_input_shortest_edge_paths_cc; + + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_INPUT_SHORTEST_EDGE_PATHS, "Shortest Edge Paths", NODE_CLASS_INPUT); + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_field_on_domain.cc b/source/blender/nodes/geometry/nodes/node_geo_interpolate_domain.cc index 59e243db4a2..93203988552 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_field_on_domain.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_interpolate_domain.cc @@ -9,7 +9,9 @@ #include "BLI_task.hh" -namespace blender::nodes::node_geo_field_on_domain_cc { +#include "NOD_socket_search_link.hh" + +namespace blender::nodes::node_geo_interpolate_domain_cc { static void node_declare(NodeDeclarationBuilder &b) { @@ -67,14 +69,28 @@ static void node_update(bNodeTree *ntree, bNode *node) nodeSetSocketAvailability(ntree, sock_out_bool, data_type == CD_PROP_BOOL); } -class FieldOnDomain final : public GeometryFieldInput { +static void node_gather_link_searches(GatherLinkSearchOpParams ¶ms) +{ + const bNodeType &node_type = params.node_type(); + const std::optional<eCustomDataType> type = node_data_type_to_custom_data_type( + (eNodeSocketDatatype)params.other_socket().type); + if (type && *type != CD_PROP_STRING) { + params.add_item(IFACE_("Value"), [node_type, type](LinkSearchOpParams ¶ms) { + bNode &node = params.add_node(node_type); + node.custom2 = *type; + params.update_and_connect_available_socket(node, "Value"); + }); + } +} + +class InterpolateDomain final : public GeometryFieldInput { private: GField src_field_; eAttrDomain src_domain_; public: - FieldOnDomain(GField field, eAttrDomain domain) - : GeometryFieldInput(field.cpp_type(), "Field on Domain"), + InterpolateDomain(GField field, eAttrDomain domain) + : GeometryFieldInput(field.cpp_type(), "Interpolate Domain"), src_field_(std::move(field)), src_domain_(domain) { @@ -124,24 +140,26 @@ static void node_geo_exec(GeoNodeExecParams params) using T = decltype(dummy); static const std::string identifier = "Value_" + identifier_suffix(data_type); Field<T> src_field = params.extract_input<Field<T>>(identifier); - Field<T> dst_field{std::make_shared<FieldOnDomain>(std::move(src_field), domain)}; + Field<T> dst_field{std::make_shared<InterpolateDomain>(std::move(src_field), domain)}; params.set_output(identifier, std::move(dst_field)); }); } -} // namespace blender::nodes::node_geo_field_on_domain_cc +} // namespace blender::nodes::node_geo_interpolate_domain_cc -void register_node_type_geo_field_on_domain() +void register_node_type_geo_interpolate_domain() { - namespace file_ns = blender::nodes::node_geo_field_on_domain_cc; + namespace file_ns = blender::nodes::node_geo_interpolate_domain_cc; static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_FIELD_ON_DOMAIN, "Field on Domain", NODE_CLASS_CONVERTER); + geo_node_type_base( + &ntype, GEO_NODE_INTERPOLATE_DOMAIN, "Interpolate Domain", NODE_CLASS_CONVERTER); ntype.geometry_node_execute = file_ns::node_geo_exec; ntype.declare = file_ns::node_declare; ntype.draw_buttons = file_ns::node_layout; ntype.initfunc = file_ns::node_init; ntype.updatefunc = file_ns::node_update; + ntype.gather_link_search_ops = file_ns::node_gather_link_searches; nodeRegisterType(&ntype); } diff --git a/source/blender/windowmanager/CMakeLists.txt b/source/blender/windowmanager/CMakeLists.txt index 4c553d0edfe..7a36020cea5 100644 --- a/source/blender/windowmanager/CMakeLists.txt +++ b/source/blender/windowmanager/CMakeLists.txt @@ -36,7 +36,7 @@ set(INC set(SRC intern/wm.c intern/wm_cursors.c - intern/wm_dragdrop.c + intern/wm_dragdrop.cc intern/wm_draw.c intern/wm_event_query.c intern/wm_event_system.cc diff --git a/source/blender/windowmanager/intern/wm_dragdrop.c b/source/blender/windowmanager/intern/wm_dragdrop.cc index 36bd69a9b25..fa8cc842037 100644 --- a/source/blender/windowmanager/intern/wm_dragdrop.c +++ b/source/blender/windowmanager/intern/wm_dragdrop.cc @@ -7,7 +7,7 @@ * Our own drag-and-drop, drag state and drop boxes. */ -#include <string.h> +#include <cstring> #include "DNA_screen_types.h" #include "DNA_space_types.h" @@ -57,7 +57,7 @@ /* ****************************************************** */ -static ListBase dropboxes = {NULL, NULL}; +static ListBase dropboxes = {nullptr, nullptr}; static void wm_drag_free_asset_data(wmDragAsset **asset_data); @@ -65,14 +65,13 @@ static void wm_drag_free_asset_data(wmDragAsset **asset_data); /* these are part of blender's UI/space specs, and not like keymaps */ /* when editors become configurable, they can add own dropbox definitions */ -typedef struct wmDropBoxMap { +struct wmDropBoxMap { struct wmDropBoxMap *next, *prev; ListBase dropboxes; short spaceid, regionid; char idname[KMAP_MAX_NAME]; - -} wmDropBoxMap; +}; ListBase *WM_dropboxmap_find(const char *idname, int spaceid, int regionid) { @@ -84,7 +83,7 @@ ListBase *WM_dropboxmap_find(const char *idname, int spaceid, int regionid) } } - wmDropBoxMap *dm = MEM_callocN(sizeof(struct wmDropBoxMap), "dropmap list"); + wmDropBoxMap *dm = MEM_cnew<wmDropBoxMap>(__func__); BLI_strncpy(dm->idname, idname, KMAP_MAX_NAME); dm->spaceid = spaceid; dm->regionid = regionid; @@ -97,20 +96,20 @@ wmDropBox *WM_dropbox_add(ListBase *lb, const char *idname, bool (*poll)(bContext *, wmDrag *, const wmEvent *), void (*copy)(bContext *, wmDrag *, wmDropBox *), - void (*cancel)(struct Main *, wmDrag *, wmDropBox *), + void (*cancel)(Main *, wmDrag *, wmDropBox *), WMDropboxTooltipFunc tooltip) { - wmDropBox *drop = MEM_callocN(sizeof(wmDropBox), "wmDropBox"); + wmDropBox *drop = MEM_cnew<wmDropBox>(__func__); drop->poll = poll; drop->copy = copy; drop->cancel = cancel; drop->tooltip = tooltip; - drop->ot = WM_operatortype_find(idname, 0); + drop->ot = WM_operatortype_find(idname, false); - if (drop->ot == NULL) { + if (drop->ot == nullptr) { MEM_freeN(drop); printf("Error: dropbox with unknown operator: %s\n", idname); - return NULL; + return nullptr; } WM_operator_properties_alloc(&(drop->ptr), &(drop->properties), idname); @@ -170,25 +169,25 @@ static void wm_dropbox_invoke(bContext *C, wmDrag *drag) if (drop->on_drag_start) { drop->on_drag_start(C, drag); } - CTX_store_set(C, NULL); + CTX_store_set(C, nullptr); } } } wmDrag *WM_drag_data_create( - struct bContext *C, int icon, int type, void *poin, double value, unsigned int flags) + bContext *C, int icon, int type, void *poin, double value, unsigned int flags) { - wmDrag *drag = MEM_callocN(sizeof(struct wmDrag), "new drag"); + wmDrag *drag = MEM_cnew<wmDrag>(__func__); /* Keep track of future multi-touch drag too, add a mouse-pointer id or so. */ /* if multiple drags are added, they're drawn as list */ - drag->flags = flags; + drag->flags = static_cast<eWM_DragFlags>(flags); drag->icon = icon; drag->type = type; switch (type) { case WM_DRAG_PATH: - BLI_strncpy(drag->path, poin, FILE_MAX); + BLI_strncpy(drag->path, static_cast<const char *>(poin), FILE_MAX); /* As the path is being copied, free it immediately as `drag` won't "own" the data. */ if (flags & WM_DRAG_FREE_DATA) { MEM_freeN(poin); @@ -196,7 +195,7 @@ wmDrag *WM_drag_data_create( break; case WM_DRAG_ID: if (poin) { - WM_drag_add_local_ID(drag, poin, NULL); + WM_drag_add_local_ID(drag, static_cast<ID *>(poin), nullptr); } break; case WM_DRAG_ASSET: @@ -211,7 +210,7 @@ wmDrag *WM_drag_data_create( const AssetLibraryReference *asset_library = CTX_wm_asset_library_ref(C); ListBase asset_file_links = CTX_data_collection_get(C, "selected_asset_files"); LISTBASE_FOREACH (const CollectionPointerLink *, link, &asset_file_links) { - const FileDirEntry *asset_file = link->ptr.data; + const FileDirEntry *asset_file = static_cast<const FileDirEntry *>(link->ptr.data); const AssetHandle asset_handle = {asset_file}; WM_drag_add_asset_list_item(drag, C, asset_library, &asset_handle); } @@ -236,7 +235,7 @@ void WM_event_start_prepared_drag(bContext *C, wmDrag *drag) } void WM_event_start_drag( - struct bContext *C, int icon, int type, void *poin, double value, unsigned int flags) + bContext *C, int icon, int type, void *poin, double value, unsigned int flags) { wmDrag *drag = WM_drag_data_create(C, icon, type, poin, value, flags); WM_event_start_prepared_drag(C, drag); @@ -266,12 +265,12 @@ static bContextStore *wm_drop_ui_context_create(const bContext *C) { uiBut *active_but = UI_region_active_but_get(CTX_wm_region(C)); if (!active_but) { - return NULL; + return nullptr; } bContextStore *but_context = UI_but_context_get(active_but); if (!but_context) { - return NULL; + return nullptr; } return CTX_store_copy(but_context); @@ -283,7 +282,7 @@ static void wm_drop_ui_context_free(bContextStore **context_store) return; } CTX_store_free(*context_store); - *context_store = NULL; + *context_store = nullptr; } void WM_event_drag_image(wmDrag *drag, ImBuf *imb, float scale) @@ -294,14 +293,14 @@ void WM_event_drag_image(wmDrag *drag, ImBuf *imb, float scale) void WM_drag_data_free(int dragtype, void *poin) { - /* Don't require all the callers to have a NULL-check, just allow passing NULL. */ + /* Don't require all the callers to have a nullptr-check, just allow passing nullptr. */ if (!poin) { return; } /* Not too nice, could become a callback. */ if (dragtype == WM_DRAG_ASSET) { - wmDragAsset *asset_data = poin; + wmDragAsset *asset_data = static_cast<wmDragAsset *>(poin); wm_drag_free_asset_data(&asset_data); } else { @@ -331,17 +330,17 @@ void WM_drag_free(wmDrag *drag) MEM_freeN(drag); } -void WM_drag_free_list(struct ListBase *lb) +void WM_drag_free_list(ListBase *lb) { wmDrag *drag; - while ((drag = BLI_pophead(lb))) { + while ((drag = static_cast<wmDrag *>(BLI_pophead(lb)))) { WM_drag_free(drag); } } static char *dropbox_tooltip(bContext *C, wmDrag *drag, const int xy[2], wmDropBox *drop) { - char *tooltip = NULL; + char *tooltip = nullptr; if (drop->tooltip) { tooltip = drop->tooltip(C, drag, xy, drop); } @@ -361,7 +360,7 @@ static wmDropBox *dropbox_active(bContext *C, if (drag->drop_state.free_disabled_info) { MEM_SAFE_FREE(drag->drop_state.disabled_info); } - drag->drop_state.disabled_info = NULL; + drag->drop_state.disabled_info = nullptr; LISTBASE_FOREACH (wmEventHandler *, handler_base, handlers) { if (handler_base->type == WM_HANDLER_TYPE_DROPBOX) { @@ -381,7 +380,7 @@ static wmDropBox *dropbox_active(bContext *C, const wmOperatorCallContext opcontext = wm_drop_operator_context_get(drop); if (WM_operator_poll_context(C, drop->ot, opcontext)) { - CTX_store_set(C, NULL); + CTX_store_set(C, nullptr); return drop; } @@ -397,8 +396,8 @@ static wmDropBox *dropbox_active(bContext *C, } } } - CTX_store_set(C, NULL); - return NULL; + CTX_store_set(C, nullptr); + return nullptr; } /* return active operator tooltip/name when mouse is in box */ @@ -441,14 +440,14 @@ static void wm_drop_update_active(bContext *C, wmDrag *drag, const wmEvent *even if (drop != drop_prev) { if (drop_prev && drop_prev->draw_deactivate) { drop_prev->draw_deactivate(drop_prev, drag); - BLI_assert(drop_prev->draw_data == NULL); + BLI_assert(drop_prev->draw_data == nullptr); } if (drop && drop->draw_activate) { drop->draw_activate(drop, drag); } drag->drop_state.active_dropbox = drop; - drag->drop_state.area_from = drop ? CTX_wm_area(C) : NULL; - drag->drop_state.region_from = drop ? CTX_wm_region(C) : NULL; + drag->drop_state.area_from = drop ? CTX_wm_area(C) : nullptr; + drag->drop_state.region_from = drop ? CTX_wm_region(C) : nullptr; } if (!drag->drop_state.active_dropbox) { @@ -476,7 +475,7 @@ void wm_drop_prepare(bContext *C, wmDrag *drag, wmDropBox *drop) void wm_drop_end(bContext *C, wmDrag *UNUSED(drag), wmDropBox *UNUSED(drop)) { - CTX_store_set(C, NULL); + CTX_store_set(C, nullptr); } void wm_drags_check_ops(bContext *C, const wmEvent *event) @@ -511,7 +510,7 @@ void WM_drag_add_local_ID(wmDrag *drag, ID *id, ID *from_parent) /* Don't drag the same ID twice. */ LISTBASE_FOREACH (wmDragID *, drag_id, &drag->ids) { if (drag_id->id == id) { - if (drag_id->from_parent == NULL) { + if (drag_id->from_parent == nullptr) { drag_id->from_parent = from_parent; } return; @@ -523,7 +522,7 @@ void WM_drag_add_local_ID(wmDrag *drag, ID *id, ID *from_parent) } /* Add to list. */ - wmDragID *drag_id = MEM_callocN(sizeof(wmDragID), __func__); + wmDragID *drag_id = MEM_cnew<wmDragID>(__func__); drag_id->id = id; drag_id->from_parent = from_parent; BLI_addtail(&drag->ids, drag_id); @@ -532,26 +531,26 @@ void WM_drag_add_local_ID(wmDrag *drag, ID *id, ID *from_parent) ID *WM_drag_get_local_ID(const wmDrag *drag, short idcode) { if (drag->type != WM_DRAG_ID) { - return NULL; + return nullptr; } - wmDragID *drag_id = drag->ids.first; + wmDragID *drag_id = static_cast<wmDragID *>(drag->ids.first); if (!drag_id) { - return NULL; + return nullptr; } ID *id = drag_id->id; - return (idcode == 0 || GS(id->name) == idcode) ? id : NULL; + return (idcode == 0 || GS(id->name) == idcode) ? id : nullptr; } ID *WM_drag_get_local_ID_from_event(const wmEvent *event, short idcode) { if (event->custom != EVT_DATA_DRAGDROP) { - return NULL; + return nullptr; } - ListBase *lb = event->customdata; - return WM_drag_get_local_ID(lb->first, idcode); + ListBase *lb = static_cast<ListBase *>(event->customdata); + return WM_drag_get_local_ID(static_cast<const wmDrag *>(lb->first), idcode); } bool WM_drag_is_ID_type(const wmDrag *drag, int idcode) @@ -564,7 +563,7 @@ wmDragAsset *WM_drag_create_asset_data(const AssetHandle *asset, const char *path, int import_type) { - wmDragAsset *asset_drag = MEM_mallocN(sizeof(*asset_drag), "wmDragAsset"); + wmDragAsset *asset_drag = MEM_new<wmDragAsset>(__func__); BLI_strncpy(asset_drag->name, ED_asset_handle_get_name(asset), sizeof(asset_drag->name)); asset_drag->metadata = metadata; @@ -584,14 +583,14 @@ static void wm_drag_free_asset_data(wmDragAsset **asset_data) wmDragAsset *WM_drag_get_asset_data(const wmDrag *drag, int idcode) { if (drag->type != WM_DRAG_ASSET) { - return NULL; + return nullptr; } - wmDragAsset *asset_drag = drag->poin; - return (ELEM(idcode, 0, asset_drag->id_type)) ? asset_drag : NULL; + wmDragAsset *asset_drag = static_cast<wmDragAsset *>(drag->poin); + return (ELEM(idcode, 0, asset_drag->id_type)) ? asset_drag : nullptr; } -struct AssetMetaData *WM_drag_get_asset_meta_data(const wmDrag *drag, int idcode) +AssetMetaData *WM_drag_get_asset_meta_data(const wmDrag *drag, int idcode) { wmDragAsset *drag_asset = WM_drag_get_asset_data(drag, idcode); if (drag_asset) { @@ -603,17 +602,18 @@ struct AssetMetaData *WM_drag_get_asset_meta_data(const wmDrag *drag, int idcode return local_id->asset_data; } - return NULL; + return nullptr; } ID *WM_drag_asset_id_import(wmDragAsset *asset_drag, const int flag_extra) { /* Only support passing in limited flags. */ BLI_assert(flag_extra == (flag_extra & FILE_AUTOSELECT)); - eFileSel_Params_Flag flag = flag_extra | FILE_ACTIVE_COLLECTION; + eFileSel_Params_Flag flag = static_cast<eFileSel_Params_Flag>(flag_extra) | + FILE_ACTIVE_COLLECTION; const char *name = asset_drag->name; - ID_Type idtype = asset_drag->id_type; + ID_Type idtype = static_cast<ID_Type>(asset_drag->id_type); /* FIXME: Link/Append should happens in the operator called at the end of drop process, not from * here. */ @@ -651,7 +651,7 @@ ID *WM_drag_asset_id_import(wmDragAsset *asset_drag, const int flag_extra) } BLI_assert_unreachable(); - return NULL; + return nullptr; } bool WM_drag_asset_will_import_linked(const wmDrag *drag) @@ -667,7 +667,7 @@ bool WM_drag_asset_will_import_linked(const wmDrag *drag) ID *WM_drag_get_local_ID_or_import_from_asset(const wmDrag *drag, int idcode) { if (!ELEM(drag->type, WM_DRAG_ASSET, WM_DRAG_ID)) { - return NULL; + return nullptr; } if (drag->type == WM_DRAG_ID) { @@ -676,14 +676,14 @@ ID *WM_drag_get_local_ID_or_import_from_asset(const wmDrag *drag, int idcode) wmDragAsset *asset_drag = WM_drag_get_asset_data(drag, idcode); if (!asset_drag) { - return NULL; + return nullptr; } /* Link/append the asset. */ return WM_drag_asset_id_import(asset_drag, 0); } -void WM_drag_free_imported_drag_ID(struct Main *bmain, wmDrag *drag, wmDropBox *drop) +void WM_drag_free_imported_drag_ID(Main *bmain, wmDrag *drag, wmDropBox *drop) { if (drag->type != WM_DRAG_ASSET) { return; @@ -697,8 +697,8 @@ void WM_drag_free_imported_drag_ID(struct Main *bmain, wmDrag *drag, wmDropBox * /* Try to find the imported ID. For this to work either a "session_uuid" or "name" property must * have been defined (see #WM_operator_properties_id_lookup()). */ ID *id = WM_operator_properties_id_lookup_from_name_or_session_uuid( - bmain, drop->ptr, asset_drag->id_type); - if (id != NULL) { + bmain, drop->ptr, static_cast<ID_Type>(asset_drag->id_type)); + if (id != nullptr) { /* Do not delete the dragged ID if it has any user, otherwise if it is a 're-used' ID it will * cause T95636. Note that we need first to add the user that we want to remove in * #BKE_id_free_us. */ @@ -710,10 +710,10 @@ void WM_drag_free_imported_drag_ID(struct Main *bmain, wmDrag *drag, wmDropBox * wmDragAssetCatalog *WM_drag_get_asset_catalog_data(const wmDrag *drag) { if (drag->type != WM_DRAG_ASSET_CATALOG) { - return NULL; + return nullptr; } - return drag->poin; + return static_cast<wmDragAssetCatalog *>(drag->poin); } void WM_drag_add_asset_list_item( @@ -728,7 +728,7 @@ void WM_drag_add_asset_list_item( /* No guarantee that the same asset isn't added twice. */ /* Add to list. */ - wmDragAssetListItem *drag_asset = MEM_callocN(sizeof(*drag_asset), __func__); + wmDragAssetListItem *drag_asset = MEM_cnew<wmDragAssetListItem>(__func__); ID *local_id = ED_asset_handle_get_local_id(asset); if (local_id) { drag_asset->is_external = false; @@ -748,7 +748,7 @@ void WM_drag_add_asset_list_item( const ListBase *WM_drag_asset_list_get(const wmDrag *drag) { if (drag->type != WM_DRAG_ASSET_LIST) { - return NULL; + return nullptr; } return &drag->asset_items; @@ -889,7 +889,7 @@ static void wm_drag_draw_tooltip(bContext *C, wmWindow *win, wmDrag *drag, const int iconsize = UI_DPI_ICON_SIZE; int padding = 4 * UI_DPI_FAC; - char *tooltip = NULL; + char *tooltip = nullptr; if (drag->drop_state.active_dropbox) { tooltip = dropbox_tooltip(C, drag, xy, drag->drop_state.active_dropbox); } @@ -1013,7 +1013,7 @@ void wm_drags_draw(bContext *C, wmWindow *win) wm_drag_draw_default(C, win, drag, xy); } GPU_blend(GPU_BLEND_NONE); - CTX_wm_area_set(C, NULL); - CTX_wm_region_set(C, NULL); - CTX_store_set(C, NULL); + CTX_wm_area_set(C, nullptr); + CTX_wm_region_set(C, nullptr); + CTX_store_set(C, nullptr); } diff --git a/source/blender/windowmanager/intern/wm_event_system.cc b/source/blender/windowmanager/intern/wm_event_system.cc index 102441f1b4d..5e7fe4678f6 100644 --- a/source/blender/windowmanager/intern/wm_event_system.cc +++ b/source/blender/windowmanager/intern/wm_event_system.cc @@ -3568,7 +3568,7 @@ static void wm_event_drag_and_drop_test(wmWindowManager *wm, wmWindow *win, wmEv /* Clear drop icon. */ screen->do_draw_drag = true; - /* Restore cursor (disabled, see `wm_dragdrop.c`) */ + /* Restore cursor (disabled, see `wm_dragdrop.cc`) */ // WM_cursor_modal_restore(win); } } diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c index a4d5bed21da..45e8f8786df 100644 --- a/source/blender/windowmanager/intern/wm_files.c +++ b/source/blender/windowmanager/intern/wm_files.c @@ -71,6 +71,7 @@ #include "BKE_lib_override.h" #include "BKE_lib_remap.h" #include "BKE_main.h" +#include "BKE_main_namemap.h" #include "BKE_packedFile.h" #include "BKE_report.h" #include "BKE_scene.h" @@ -1004,6 +1005,8 @@ bool WM_file_read(bContext *C, const char *filepath, ReportList *reports) WM_cursor_wait(false); + BLI_assert(BKE_main_namemap_validate(CTX_data_main(C))); + return success; } |