Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender')
-rw-r--r--source/blender/blenkernel/BKE_blender_version.h4
-rw-r--r--source/blender/blenkernel/BKE_lib_id.h2
-rw-r--r--source/blender/blenkernel/BKE_main_namemap.h15
-rw-r--r--source/blender/blenkernel/BKE_mesh.h2
-rw-r--r--source/blender/blenkernel/BKE_node.h5
-rw-r--r--source/blender/blenkernel/CMakeLists.txt2
-rw-r--r--source/blender/blenkernel/intern/curves_geometry.cc43
-rw-r--r--source/blender/blenkernel/intern/lib_id.c7
-rw-r--r--source/blender/blenkernel/intern/lib_id_delete.c4
-rw-r--r--source/blender/blenkernel/intern/lib_id_test.cc57
-rw-r--r--source/blender/blenkernel/intern/lib_override.cc18
-rw-r--r--source/blender/blenkernel/intern/main_namemap.cc135
-rw-r--r--source/blender/blenkernel/intern/mesh_tessellate.cc (renamed from source/blender/blenkernel/intern/mesh_tessellate.c)52
-rw-r--r--source/blender/blenkernel/intern/node.cc15
-rw-r--r--source/blender/blenlib/BLI_generic_virtual_array.hh1
-rw-r--r--source/blender/blenloader/intern/versioning_300.c84
-rw-r--r--source/blender/blenloader/intern/versioning_userdef.c5
-rw-r--r--source/blender/bmesh/intern/bmesh_mesh_tessellate.c2
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval.cc5
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_flush.cc5
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_visibility.cc28
-rw-r--r--source/blender/depsgraph/intern/node/deg_node_component.cc7
-rw-r--r--source/blender/depsgraph/intern/node/deg_node_component.h20
-rw-r--r--source/blender/depsgraph/intern/node/deg_node_operation.cc10
-rw-r--r--source/blender/editors/animation/anim_channels_edit.c3
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_transform.c3
-rw-r--r--source/blender/editors/space_node/node_edit.cc4
-rw-r--r--source/blender/geometry/GEO_mesh_to_curve.hh5
-rw-r--r--source/blender/geometry/intern/mesh_to_curve_convert.cc8
-rw-r--r--source/blender/geometry/intern/resample_curves.cc1
-rw-r--r--source/blender/makesdna/DNA_space_types.h3
-rw-r--r--source/blender/makesrna/intern/CMakeLists.txt2
-rw-r--r--source/blender/makesrna/intern/rna_action.c5
-rw-r--r--source/blender/makesrna/intern/rna_nodetree.c2
-rw-r--r--source/blender/nodes/NOD_geometry.h15
-rw-r--r--source/blender/nodes/NOD_static_types.h19
-rw-r--r--source/blender/nodes/geometry/CMakeLists.txt11
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_edge_paths_to_curves.cc113
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_edge_paths_to_selection.cc140
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_field_at_index.cc20
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_shortest_edge_paths.cc247
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_interpolate_domain.cc (renamed from source/blender/nodes/geometry/nodes/node_geo_field_on_domain.cc)36
-rw-r--r--source/blender/windowmanager/CMakeLists.txt2
-rw-r--r--source/blender/windowmanager/intern/wm_dragdrop.cc (renamed from source/blender/windowmanager/intern/wm_dragdrop.c)132
-rw-r--r--source/blender/windowmanager/intern/wm_event_system.cc2
-rw-r--r--source/blender/windowmanager/intern/wm_files.c3
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 &params)
+{
+ 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 &params) {
+ 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 &params)
+{
+ 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 &params) {
+ 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;
}