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:
authorJacques Lucke <jacques@blender.org>2021-12-14 17:57:58 +0300
committerJacques Lucke <jacques@blender.org>2021-12-14 17:57:58 +0300
commitf5ce243a56a22d71830c8c131d736737613aaaa4 (patch)
tree7c4657f0a8c196b7db8dd9c15942e28a83301ebf
parent8e2c9f2dd3118bfdb69ccf0ab2b9f968a854aae4 (diff)
Geometry Nodes: support instance attributes when realizing instances
This patch refactors the instance-realization code and adds new functionality. * Named and anonymous attributes are propagated from instances to the realized geometry. If the same attribute exists on the geometry and on an instance, the attribute on the geometry has precedence. * The id attribute has special handling to avoid creating the same id on many output points. This is necessary to make e.g. the Random Value node work as expected afterwards. Realizing instance attributes has an effect on existing files, especially due to the id attribute. To avoid breaking existing files, the Realize Instances node now has a legacy option that is enabled for all already existing Realize Instances nodes. Removing this legacy behavior does affect some existing files (although not many). We can decide whether it's worth to remove the old behavior as a separate step. This refactor also improves performance when realizing instances. That is mainly due to multi-threading. See D13446 to get the file used for benchmarking. The curve code is not as optimized as it could be yet. That's mainly because the storage for these attributes might change soonish and it wasn't worth optimizing for the current storage format right now. ``` 1,000,000 x mesh vertex: 530 ms -> 130 ms 1,000,000 x simple cube: 1290 ms -> 190 ms 1,000,000 x point: 1000 ms -> 150 ms 1,000,000 x curve spiral: 1740 ms -> 330 ms 1,000,000 x curve line: 1110 ms -> 210 ms 10,000 x subdivided cylinder: 170 ms -> 40 ms 10 x subdivided spiral: 180 ms -> 180 ms ``` Differential Revision: https://developer.blender.org/D13446
-rw-r--r--source/blender/blenkernel/BKE_blender_version.h2
-rw-r--r--source/blender/blenkernel/BKE_geometry_set_instances.hh2
-rw-r--r--source/blender/blenkernel/BKE_spline.hh1
-rw-r--r--source/blender/blenkernel/BKE_type_conversions.hh2
-rw-r--r--source/blender/blenkernel/intern/curve_eval.cc7
-rw-r--r--source/blender/blenkernel/intern/geometry_set.cc9
-rw-r--r--source/blender/blenkernel/intern/geometry_set_instances.cc369
-rw-r--r--source/blender/blenkernel/intern/type_conversions.cc17
-rw-r--r--source/blender/blenloader/intern/versioning_300.c14
-rw-r--r--source/blender/geometry/CMakeLists.txt3
-rw-r--r--source/blender/geometry/GEO_realize_instances.hh52
-rw-r--r--source/blender/geometry/intern/realize_instances.cc1347
-rw-r--r--source/blender/makesdna/DNA_node_types.h4
-rw-r--r--source/blender/makesrna/intern/rna_nodetree.c11
-rw-r--r--source/blender/modifiers/CMakeLists.txt1
-rw-r--r--source/blender/nodes/NOD_geometry_exec.hh3
-rw-r--r--source/blender/nodes/NOD_static_types.h2
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_align_rotation_to_vector.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_clamp.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_color_ramp.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_combine_xyz.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_compare.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_convert.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_curve_map.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_fill.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_math.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_mix.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_proximity.cc4
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_randomize.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_sample_texture.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_separate_xyz.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_transfer.cc4
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_vector_math.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_vector_rotate.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_endpoints.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_reverse.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_select_by_handle_type.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_set_handles.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_spline_type.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_subdivide.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_to_points.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_delete_geometry.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_edge_split.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_material_assign.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_mesh_to_curve.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_point_instance.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_point_rotate.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_point_scale.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_point_separate.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_point_translate.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_points_to_volume.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_raycast.cc4
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_select_by_material.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_subdivision_surface.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc7
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_realize_instances.cc15
56 files changed, 1530 insertions, 418 deletions
diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h
index 83429b58faf..d0ab8be9a29 100644
--- a/source/blender/blenkernel/BKE_blender_version.h
+++ b/source/blender/blenkernel/BKE_blender_version.h
@@ -39,7 +39,7 @@ extern "C" {
/* Blender file format version. */
#define BLENDER_FILE_VERSION BLENDER_VERSION
-#define BLENDER_FILE_SUBVERSION 4
+#define BLENDER_FILE_SUBVERSION 5
/* 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_geometry_set_instances.hh b/source/blender/blenkernel/BKE_geometry_set_instances.hh
index 414cb750937..98120b07f2d 100644
--- a/source/blender/blenkernel/BKE_geometry_set_instances.hh
+++ b/source/blender/blenkernel/BKE_geometry_set_instances.hh
@@ -57,8 +57,6 @@ struct GeometryInstanceGroup {
void geometry_set_gather_instances(const GeometrySet &geometry_set,
Vector<GeometryInstanceGroup> &r_instance_groups);
-GeometrySet geometry_set_realize_instances(const GeometrySet &geometry_set);
-
/**
* Add information about all the attributes on every component of the type. The resulting info
* will contain the highest complexity data type and the highest priority domain among every
diff --git a/source/blender/blenkernel/BKE_spline.hh b/source/blender/blenkernel/BKE_spline.hh
index 2073ec065fa..ebdc4a0ca0b 100644
--- a/source/blender/blenkernel/BKE_spline.hh
+++ b/source/blender/blenkernel/BKE_spline.hh
@@ -705,6 +705,7 @@ struct CurveEval {
* \warning Call #reallocate on the spline's attributes after adding all splines.
*/
void add_spline(SplinePtr spline);
+ void add_splines(blender::MutableSpan<SplinePtr> splines);
void remove_splines(blender::IndexMask mask);
void translate(const blender::float3 &translation);
diff --git a/source/blender/blenkernel/BKE_type_conversions.hh b/source/blender/blenkernel/BKE_type_conversions.hh
index 27419b32de6..ebfb13cd08f 100644
--- a/source/blender/blenkernel/BKE_type_conversions.hh
+++ b/source/blender/blenkernel/BKE_type_conversions.hh
@@ -72,6 +72,8 @@ class DataTypeConversions {
const void *from_value,
void *to_value) const;
+ void convert_to_initialized_n(fn::GSpan from_span, fn::GMutableSpan to_span) const;
+
fn::GVArray try_convert(fn::GVArray varray, const CPPType &to_type) const;
fn::GVMutableArray try_convert(fn::GVMutableArray varray, const CPPType &to_type) const;
diff --git a/source/blender/blenkernel/intern/curve_eval.cc b/source/blender/blenkernel/intern/curve_eval.cc
index 02abb097175..38f736e6907 100644
--- a/source/blender/blenkernel/intern/curve_eval.cc
+++ b/source/blender/blenkernel/intern/curve_eval.cc
@@ -71,6 +71,13 @@ void CurveEval::add_spline(SplinePtr spline)
splines_.append(std::move(spline));
}
+void CurveEval::add_splines(MutableSpan<SplinePtr> splines)
+{
+ for (SplinePtr &spline : splines) {
+ this->add_spline(std::move(spline));
+ }
+}
+
void CurveEval::remove_splines(blender::IndexMask mask)
{
for (int i = mask.size() - 1; i >= 0; i--) {
diff --git a/source/blender/blenkernel/intern/geometry_set.cc b/source/blender/blenkernel/intern/geometry_set.cc
index 7830e897109..ef5609ec9a8 100644
--- a/source/blender/blenkernel/intern/geometry_set.cc
+++ b/source/blender/blenkernel/intern/geometry_set.cc
@@ -481,13 +481,18 @@ void GeometrySet::gather_attributes_for_propagation(
return;
}
+ AttributeDomain domain = meta_data.domain;
+ if (dst_component_type != GEO_COMPONENT_TYPE_INSTANCES && domain == ATTR_DOMAIN_INSTANCE) {
+ domain = ATTR_DOMAIN_POINT;
+ }
+
auto add_info = [&](AttributeKind *attribute_kind) {
- attribute_kind->domain = meta_data.domain;
+ attribute_kind->domain = domain;
attribute_kind->data_type = meta_data.data_type;
};
auto modify_info = [&](AttributeKind *attribute_kind) {
attribute_kind->domain = bke::attribute_domain_highest_priority(
- {attribute_kind->domain, meta_data.domain});
+ {attribute_kind->domain, domain});
attribute_kind->data_type = bke::attribute_data_type_highest_complexity(
{attribute_kind->data_type, meta_data.data_type});
};
diff --git a/source/blender/blenkernel/intern/geometry_set_instances.cc b/source/blender/blenkernel/intern/geometry_set_instances.cc
index 6d50dcd3c8c..4d84d5d899d 100644
--- a/source/blender/blenkernel/intern/geometry_set_instances.cc
+++ b/source/blender/blenkernel/intern/geometry_set_instances.cc
@@ -207,375 +207,6 @@ void geometry_set_gather_instances_attribute_info(Span<GeometryInstanceGroup> se
}
}
-static Mesh *join_mesh_topology_and_builtin_attributes(Span<GeometryInstanceGroup> set_groups)
-{
- int totverts = 0;
- int totloops = 0;
- int totedges = 0;
- int totpolys = 0;
- int64_t cd_dirty_vert = 0;
- int64_t cd_dirty_poly = 0;
- int64_t cd_dirty_edge = 0;
- int64_t cd_dirty_loop = 0;
- VectorSet<Material *> materials;
-
- for (const GeometryInstanceGroup &set_group : set_groups) {
- const GeometrySet &set = set_group.geometry_set;
- const int tot_transforms = set_group.transforms.size();
- if (set.has_mesh()) {
- const Mesh &mesh = *set.get_mesh_for_read();
- totverts += mesh.totvert * tot_transforms;
- totloops += mesh.totloop * tot_transforms;
- totedges += mesh.totedge * tot_transforms;
- totpolys += mesh.totpoly * tot_transforms;
- cd_dirty_vert |= mesh.runtime.cd_dirty_vert;
- cd_dirty_poly |= mesh.runtime.cd_dirty_poly;
- cd_dirty_edge |= mesh.runtime.cd_dirty_edge;
- cd_dirty_loop |= mesh.runtime.cd_dirty_loop;
- for (const int slot_index : IndexRange(mesh.totcol)) {
- Material *material = mesh.mat[slot_index];
- materials.add(material);
- }
- }
- }
-
- /* Don't create an empty mesh. */
- if ((totverts + totloops + totedges + totpolys) == 0) {
- return nullptr;
- }
-
- Mesh *new_mesh = BKE_mesh_new_nomain(totverts, totedges, 0, totloops, totpolys);
- /* Copy settings from the first input geometry set with a mesh. */
- for (const GeometryInstanceGroup &set_group : set_groups) {
- const GeometrySet &set = set_group.geometry_set;
- if (set.has_mesh()) {
- const Mesh &mesh = *set.get_mesh_for_read();
- BKE_mesh_copy_parameters_for_eval(new_mesh, &mesh);
- break;
- }
- }
- for (const int i : IndexRange(materials.size())) {
- Material *material = materials[i];
- BKE_id_material_eval_assign(&new_mesh->id, i + 1, material);
- }
- new_mesh->runtime.cd_dirty_vert = cd_dirty_vert;
- new_mesh->runtime.cd_dirty_poly = cd_dirty_poly;
- new_mesh->runtime.cd_dirty_edge = cd_dirty_edge;
- new_mesh->runtime.cd_dirty_loop = cd_dirty_loop;
-
- int vert_offset = 0;
- int loop_offset = 0;
- int edge_offset = 0;
- int poly_offset = 0;
- for (const GeometryInstanceGroup &set_group : set_groups) {
- const GeometrySet &set = set_group.geometry_set;
- if (set.has_mesh()) {
- const Mesh &mesh = *set.get_mesh_for_read();
-
- Array<int> material_index_map(mesh.totcol);
- for (const int i : IndexRange(mesh.totcol)) {
- Material *material = mesh.mat[i];
- const int new_material_index = materials.index_of(material);
- material_index_map[i] = new_material_index;
- }
-
- for (const float4x4 &transform : set_group.transforms) {
- for (const int i : IndexRange(mesh.totvert)) {
- const MVert &old_vert = mesh.mvert[i];
- MVert &new_vert = new_mesh->mvert[vert_offset + i];
-
- new_vert = old_vert;
-
- const float3 new_position = transform * float3(old_vert.co);
- copy_v3_v3(new_vert.co, new_position);
- }
- for (const int i : IndexRange(mesh.totedge)) {
- const MEdge &old_edge = mesh.medge[i];
- MEdge &new_edge = new_mesh->medge[edge_offset + i];
- new_edge = old_edge;
- new_edge.v1 += vert_offset;
- new_edge.v2 += vert_offset;
- }
- for (const int i : IndexRange(mesh.totloop)) {
- const MLoop &old_loop = mesh.mloop[i];
- MLoop &new_loop = new_mesh->mloop[loop_offset + i];
- new_loop = old_loop;
- new_loop.v += vert_offset;
- new_loop.e += edge_offset;
- }
- for (const int i : IndexRange(mesh.totpoly)) {
- const MPoly &old_poly = mesh.mpoly[i];
- MPoly &new_poly = new_mesh->mpoly[poly_offset + i];
- new_poly = old_poly;
- new_poly.loopstart += loop_offset;
- if (old_poly.mat_nr >= 0 && old_poly.mat_nr < mesh.totcol) {
- new_poly.mat_nr = material_index_map[new_poly.mat_nr];
- }
- else {
- /* The material index was invalid before. */
- new_poly.mat_nr = 0;
- }
- }
-
- vert_offset += mesh.totvert;
- loop_offset += mesh.totloop;
- edge_offset += mesh.totedge;
- poly_offset += mesh.totpoly;
- }
- }
- }
-
- /* A possible optimization is to only tag the normals dirty when there are transforms that change
- * normals. */
- BKE_mesh_normals_tag_dirty(new_mesh);
-
- return new_mesh;
-}
-
-static void join_attributes(Span<GeometryInstanceGroup> set_groups,
- Span<GeometryComponentType> component_types,
- const Map<AttributeIDRef, AttributeKind> &attribute_info,
- GeometryComponent &result)
-{
- for (Map<AttributeIDRef, AttributeKind>::Item entry : attribute_info.items()) {
- const AttributeIDRef attribute_id = entry.key;
- const AttributeDomain domain_output = entry.value.domain;
- const CustomDataType data_type_output = entry.value.data_type;
- const CPPType *cpp_type = bke::custom_data_type_to_cpp_type(data_type_output);
- BLI_assert(cpp_type != nullptr);
-
- result.attribute_try_create(
- entry.key, domain_output, data_type_output, AttributeInitDefault());
- WriteAttributeLookup write_attribute = result.attribute_try_get_for_write(attribute_id);
- if (!write_attribute || &write_attribute.varray.type() != cpp_type ||
- write_attribute.domain != domain_output) {
- continue;
- }
-
- fn::GVMutableArray_GSpan dst_span{write_attribute.varray};
-
- int offset = 0;
- for (const GeometryInstanceGroup &set_group : set_groups) {
- const GeometrySet &set = set_group.geometry_set;
- for (const GeometryComponentType component_type : component_types) {
- if (set.has(component_type)) {
- const GeometryComponent &component = *set.get_component_for_read(component_type);
- const int domain_size = component.attribute_domain_size(domain_output);
- if (domain_size == 0) {
- continue; /* Domain size is 0, so no need to increment the offset. */
- }
- GVArray source_attribute = component.attribute_try_get_for_read(
- attribute_id, domain_output, data_type_output);
-
- if (source_attribute) {
- fn::GVArray_GSpan src_span{source_attribute};
- const void *src_buffer = src_span.data();
- for (const int UNUSED(i) : set_group.transforms.index_range()) {
- void *dst_buffer = dst_span[offset];
- cpp_type->copy_assign_n(src_buffer, dst_buffer, domain_size);
- offset += domain_size;
- }
- }
- else {
- offset += domain_size * set_group.transforms.size();
- }
- }
- }
- }
-
- dst_span.save();
- }
-}
-
-static PointCloud *join_pointcloud_position_attribute(Span<GeometryInstanceGroup> set_groups)
-{
- /* Count the total number of points. */
- int totpoint = 0;
- for (const GeometryInstanceGroup &set_group : set_groups) {
- const GeometrySet &set = set_group.geometry_set;
- if (set.has<PointCloudComponent>()) {
- const PointCloudComponent &component = *set.get_component_for_read<PointCloudComponent>();
- totpoint += component.attribute_domain_size(ATTR_DOMAIN_POINT);
- }
- }
- if (totpoint == 0) {
- return nullptr;
- }
-
- PointCloud *new_pointcloud = BKE_pointcloud_new_nomain(totpoint);
- MutableSpan new_positions{(float3 *)new_pointcloud->co, new_pointcloud->totpoint};
-
- /* Transform each instance's point locations into the new point cloud. */
- int offset = 0;
- for (const GeometryInstanceGroup &set_group : set_groups) {
- const GeometrySet &set = set_group.geometry_set;
- const PointCloud *pointcloud = set.get_pointcloud_for_read();
- if (pointcloud == nullptr) {
- continue;
- }
- for (const float4x4 &transform : set_group.transforms) {
- for (const int i : IndexRange(pointcloud->totpoint)) {
- new_positions[offset + i] = transform * float3(pointcloud->co[i]);
- }
- offset += pointcloud->totpoint;
- }
- }
-
- return new_pointcloud;
-}
-
-static CurveEval *join_curve_splines_and_builtin_attributes(Span<GeometryInstanceGroup> set_groups)
-{
- Vector<SplinePtr> new_splines;
- for (const GeometryInstanceGroup &set_group : set_groups) {
- const GeometrySet &set = set_group.geometry_set;
- if (!set.has_curve()) {
- continue;
- }
-
- const CurveEval &source_curve = *set.get_curve_for_read();
- for (const SplinePtr &source_spline : source_curve.splines()) {
- for (const float4x4 &transform : set_group.transforms) {
- SplinePtr new_spline = source_spline->copy_without_attributes();
- new_spline->transform(transform);
- new_splines.append(std::move(new_spline));
- }
- }
- }
- if (new_splines.is_empty()) {
- return nullptr;
- }
-
- CurveEval *new_curve = new CurveEval();
- for (SplinePtr &new_spline : new_splines) {
- new_curve->add_spline(std::move(new_spline));
- }
-
- new_curve->attributes.reallocate(new_curve->splines().size());
- return new_curve;
-}
-
-static void join_instance_groups_mesh(Span<GeometryInstanceGroup> set_groups, GeometrySet &result)
-{
- Mesh *new_mesh = join_mesh_topology_and_builtin_attributes(set_groups);
- if (new_mesh == nullptr) {
- return;
- }
-
- MeshComponent &dst_component = result.get_component_for_write<MeshComponent>();
- dst_component.replace(new_mesh);
-
- /* Don't copy attributes that are stored directly in the mesh data structs. */
- Map<AttributeIDRef, AttributeKind> attributes;
- geometry_set_gather_instances_attribute_info(
- set_groups,
- {GEO_COMPONENT_TYPE_MESH},
- {"position", "material_index", "normal", "shade_smooth", "crease"},
- attributes);
- join_attributes(set_groups,
- {GEO_COMPONENT_TYPE_MESH},
- attributes,
- static_cast<GeometryComponent &>(dst_component));
-}
-
-static void join_instance_groups_pointcloud(Span<GeometryInstanceGroup> set_groups,
- GeometrySet &result)
-{
- PointCloud *new_pointcloud = join_pointcloud_position_attribute(set_groups);
- if (new_pointcloud == nullptr) {
- return;
- }
-
- PointCloudComponent &dst_component = result.get_component_for_write<PointCloudComponent>();
- dst_component.replace(new_pointcloud);
-
- Map<AttributeIDRef, AttributeKind> attributes;
- geometry_set_gather_instances_attribute_info(
- set_groups, {GEO_COMPONENT_TYPE_POINT_CLOUD}, {"position"}, attributes);
- join_attributes(set_groups,
- {GEO_COMPONENT_TYPE_POINT_CLOUD},
- attributes,
- static_cast<GeometryComponent &>(dst_component));
-}
-
-static void join_instance_groups_volume(Span<GeometryInstanceGroup> set_groups,
- GeometrySet &result)
-{
- /* Not yet supported; for now only return the first volume. Joining volume grids with the same
- * name requires resampling of at least one of the grids. The cell size of the resulting volume
- * has to be determined somehow. */
- for (const GeometryInstanceGroup &set_group : set_groups) {
- const GeometrySet &set = set_group.geometry_set;
- if (set.has<VolumeComponent>()) {
- result.add(*set.get_component_for_read<VolumeComponent>());
- return;
- }
- }
-}
-
-/**
- * Curve point domain attributes must be in the same order on every spline. The order might have
- * been different on separate instances, so ensure that all splines have the same order. Note that
- * because #Map is used, the order is not necessarily consistent every time, but it is the same for
- * every spline, and that's what matters.
- */
-static void sort_curve_point_attributes(const Map<AttributeIDRef, AttributeKind> &info,
- MutableSpan<SplinePtr> splines)
-{
- Vector<AttributeIDRef> new_order;
- for (Map<AttributeIDRef, AttributeKind>::Item item : info.items()) {
- if (item.value.domain == ATTR_DOMAIN_POINT) {
- /* Only sort attributes stored on splines. */
- new_order.append(item.key);
- }
- }
- for (SplinePtr &spline : splines) {
- spline->attributes.reorder(new_order);
- }
-}
-
-static void join_instance_groups_curve(Span<GeometryInstanceGroup> set_groups, GeometrySet &result)
-{
- CurveEval *curve = join_curve_splines_and_builtin_attributes(set_groups);
- if (curve == nullptr) {
- return;
- }
-
- CurveComponent &dst_component = result.get_component_for_write<CurveComponent>();
- dst_component.replace(curve);
-
- Map<AttributeIDRef, AttributeKind> attributes;
- geometry_set_gather_instances_attribute_info(
- set_groups,
- {GEO_COMPONENT_TYPE_CURVE},
- {"position", "radius", "tilt", "handle_left", "handle_right", "cyclic", "resolution"},
- attributes);
- join_attributes(set_groups,
- {GEO_COMPONENT_TYPE_CURVE},
- attributes,
- static_cast<GeometryComponent &>(dst_component));
- sort_curve_point_attributes(attributes, curve->splines());
- curve->assert_valid_point_attributes();
-}
-
-GeometrySet geometry_set_realize_instances(const GeometrySet &geometry_set)
-{
- if (!geometry_set.has_instances()) {
- return geometry_set;
- }
-
- GeometrySet new_geometry_set;
-
- Vector<GeometryInstanceGroup> set_groups;
- geometry_set_gather_instances(geometry_set, set_groups);
- join_instance_groups_mesh(set_groups, new_geometry_set);
- join_instance_groups_pointcloud(set_groups, new_geometry_set);
- join_instance_groups_volume(set_groups, new_geometry_set);
- join_instance_groups_curve(set_groups, new_geometry_set);
-
- return new_geometry_set;
-}
-
} // namespace blender::bke
void InstancesComponent::foreach_referenced_geometry(
diff --git a/source/blender/blenkernel/intern/type_conversions.cc b/source/blender/blenkernel/intern/type_conversions.cc
index ab42d508538..b23220286e6 100644
--- a/source/blender/blenkernel/intern/type_conversions.cc
+++ b/source/blender/blenkernel/intern/type_conversions.cc
@@ -239,6 +239,23 @@ void DataTypeConversions::convert_to_uninitialized(const CPPType &from_type,
functions->convert_single_to_uninitialized(from_value, to_value);
}
+void DataTypeConversions::convert_to_initialized_n(fn::GSpan from_span,
+ fn::GMutableSpan to_span) const
+{
+ const CPPType &from_type = from_span.type();
+ const CPPType &to_type = to_span.type();
+ BLI_assert(from_span.size() == to_span.size());
+ BLI_assert(this->is_convertible(from_type, to_type));
+ const fn::MultiFunction *fn = this->get_conversion_multi_function(
+ MFDataType::ForSingle(from_type), MFDataType::ForSingle(to_type));
+ fn::MFParamsBuilder params{*fn, from_span.size()};
+ params.add_readonly_single_input(from_span);
+ to_type.destruct_n(to_span.data(), to_span.size());
+ params.add_uninitialized_single_output(to_span);
+ fn::MFContextBuilder context;
+ fn->call_auto(IndexRange(from_span.size()), params, context);
+}
+
class GVArray_For_ConvertedGVArray : public fn::GVArrayImpl {
private:
fn::GVArray varray_;
diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c
index 49b44d40d79..a42a2d9b95f 100644
--- a/source/blender/blenloader/intern/versioning_300.c
+++ b/source/blender/blenloader/intern/versioning_300.c
@@ -2434,6 +2434,20 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
}
+ if (!MAIN_VERSION_ATLEAST(bmain, 301, 5)) {
+ LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) {
+ if (ntree->type != NTREE_GEOMETRY) {
+ continue;
+ }
+ LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
+ if (node->type != GEO_NODE_REALIZE_INSTANCES) {
+ continue;
+ }
+ node->custom1 |= GEO_NODE_REALIZE_INSTANCES_LEGACY_BEHAVIOR;
+ }
+ }
+ }
+
/**
* Versioning code until next subversion bump goes here.
*
diff --git a/source/blender/geometry/CMakeLists.txt b/source/blender/geometry/CMakeLists.txt
index 03f736d4dde..de508ddc540 100644
--- a/source/blender/geometry/CMakeLists.txt
+++ b/source/blender/geometry/CMakeLists.txt
@@ -31,7 +31,10 @@ set(INC
set(SRC
intern/mesh_to_curve_convert.cc
+ intern/realize_instances.cc
+
GEO_mesh_to_curve.hh
+ GEO_realize_instances.hh
)
set(LIB
diff --git a/source/blender/geometry/GEO_realize_instances.hh b/source/blender/geometry/GEO_realize_instances.hh
new file mode 100644
index 00000000000..ac16196667d
--- /dev/null
+++ b/source/blender/geometry/GEO_realize_instances.hh
@@ -0,0 +1,52 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#pragma once
+
+#include "BKE_geometry_set.hh"
+
+namespace blender::geometry {
+
+struct RealizeInstancesOptions {
+ /**
+ * The default is to generate new ids for every element (when there was any id attribute in the
+ * input). This avoids having a geometry that contains the same id many times.
+ * When this is `true` the ids on the original geometries are kept unchanged and ids on instances
+ * are ignored. Ids are zero initialized when the original geometry did not have an id.
+ */
+ bool keep_original_ids = false;
+ /**
+ * When `true` the output geometry will contain all the generic attributes that existed on
+ * instances. Otherwise, instance attributes are ignored.
+ */
+ bool realize_instance_attributes = true;
+};
+
+/**
+ * Join all instances into a single geometry component for each geometry type. For example, all
+ * mesh instances (including the already realized mesh) are joined into a single mesh. The output
+ * geometry set does not contain any instances. If the input did not contain any instances, it is
+ * returned directly.
+ *
+ * The `id` attribute has special handling. If there is an id attribute on any component, the
+ * output will contain an `id` attribute as well. The output id is generated by mixing/hashing ids
+ * of instances and of the instanced geometry data.
+ */
+GeometrySet realize_instances(GeometrySet geometry_set, const RealizeInstancesOptions &options);
+
+GeometrySet realize_instances_legacy(GeometrySet geometry_set);
+
+} // namespace blender::geometry
diff --git a/source/blender/geometry/intern/realize_instances.cc b/source/blender/geometry/intern/realize_instances.cc
new file mode 100644
index 00000000000..ae513bf88e9
--- /dev/null
+++ b/source/blender/geometry/intern/realize_instances.cc
@@ -0,0 +1,1347 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "GEO_realize_instances.hh"
+
+#include "DNA_collection_types.h"
+#include "DNA_layer_types.h"
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_object_types.h"
+#include "DNA_pointcloud_types.h"
+
+#include "BLI_noise.hh"
+#include "BLI_task.hh"
+
+#include "BKE_collection.h"
+#include "BKE_geometry_set_instances.hh"
+#include "BKE_material.h"
+#include "BKE_mesh.h"
+#include "BKE_pointcloud.h"
+#include "BKE_spline.hh"
+#include "BKE_type_conversions.hh"
+
+namespace blender::geometry {
+
+using blender::bke::AttributeIDRef;
+using blender::bke::custom_data_type_to_cpp_type;
+using blender::bke::CustomDataAttributes;
+using blender::bke::object_get_evaluated_geometry_set;
+using blender::bke::OutputAttribute;
+using blender::bke::OutputAttribute_Typed;
+using blender::bke::ReadAttributeLookup;
+using blender::fn::CPPType;
+using blender::fn::GArray;
+using blender::fn::GMutableSpan;
+using blender::fn::GSpan;
+using blender::fn::GVArray;
+using blender::fn::GVArray_GSpan;
+
+/**
+ * An ordered set of attribute ids. Attributes are ordered to avoid name lookups in many places.
+ * Once the attributes are ordered, they can just be referred to by index.
+ */
+struct OrderedAttributes {
+ VectorSet<AttributeIDRef> ids;
+ Vector<AttributeKind> kinds;
+
+ int size() const
+ {
+ return this->kinds.size();
+ }
+
+ IndexRange index_range() const
+ {
+ return this->kinds.index_range();
+ }
+};
+
+struct AttributeFallbacksArray {
+ /**
+ * Instance attribute values used as fallback when the geometry does not have the
+ * corresponding attributes itself. The pointers point to attributes stored in the instances
+ * component or in #r_temporary_arrays. The order depends on the corresponding #OrderedAttributes
+ * instance.
+ */
+ Array<const void *> array;
+
+ AttributeFallbacksArray(int size) : array(size, nullptr)
+ {
+ }
+};
+
+struct PointCloudRealizeInfo {
+ const PointCloud *pointcloud = nullptr;
+ /** Matches the order stored in #AllPointCloudsInfo.attributes. */
+ Array<std::optional<GVArray_GSpan>> attributes;
+ /** Id attribute on the point cloud. If there are no ids, this #Span is empty. */
+ Span<int> stored_ids;
+};
+
+struct RealizePointCloudTask {
+ /** Starting index in the final realized point cloud. */
+ int start_index;
+ /** Preprocessed information about the point cloud. */
+ const PointCloudRealizeInfo *pointcloud_info;
+ /** Transformation that is applied to all positions. */
+ float4x4 transform;
+ AttributeFallbacksArray attribute_fallbacks;
+ /** Only used when the output contains an output attribute. */
+ uint32_t id = 0;
+};
+
+/** Start indices in the final output mesh. */
+struct MeshElementStartIndices {
+ int vertex = 0;
+ int edge = 0;
+ int poly = 0;
+ int loop = 0;
+};
+
+struct MeshRealizeInfo {
+ const Mesh *mesh = nullptr;
+ /** Maps old material indices to new material indices. */
+ Array<int> material_index_map;
+ /** Matches the order in #AllMeshesInfo.attributes. */
+ Array<std::optional<GVArray_GSpan>> attributes;
+ /** Vertex ids stored on the mesh. If there are no ids, this #Span is empty. */
+ Span<int> stored_vertex_ids;
+};
+
+struct RealizeMeshTask {
+ MeshElementStartIndices start_indices;
+ const MeshRealizeInfo *mesh_info;
+ /** Transformation that is applied to all positions. */
+ float4x4 transform;
+ AttributeFallbacksArray attribute_fallbacks;
+ /** Only used when the output contains an output attribute. */
+ uint32_t id = 0;
+};
+
+struct RealizeCurveInfo {
+ const CurveEval *curve = nullptr;
+ /**
+ * Matches the order in #AllCurvesInfo.attributes. For point attributes, the `std::optional`
+ * will be empty.
+ */
+ Array<std::optional<GVArray_GSpan>> spline_attributes;
+};
+
+struct RealizeCurveTask {
+ /* Start index in the final curve. */
+ int start_spline_index = 0;
+ const RealizeCurveInfo *curve_info;
+ /* Transformation applied to the position of control points and handles. */
+ float4x4 transform;
+ AttributeFallbacksArray attribute_fallbacks;
+ /** Only used when the output contains an output attribute. */
+ uint32_t id = 0;
+};
+
+struct AllPointCloudsInfo {
+ /** Ordering of all attributes that are propagated to the output point cloud generically. */
+ OrderedAttributes attributes;
+ /** Ordering of the original point clouds that are joined. */
+ VectorSet<const PointCloud *> order;
+ /** Preprocessed data about every original point cloud. This is ordered by #order. */
+ Array<PointCloudRealizeInfo> realize_info;
+ bool create_id_attribute = false;
+};
+
+struct AllMeshesInfo {
+ /** Ordering of all attributes that are propagated to the output mesh generically. */
+ OrderedAttributes attributes;
+ /** Ordering of the original meshes that are joined. */
+ VectorSet<const Mesh *> order;
+ /** Preprocessed data about every original mesh. This is ordered by #order. */
+ Array<MeshRealizeInfo> realize_info;
+ /** Ordered materials on the output mesh. */
+ VectorSet<Material *> materials;
+ bool create_id_attribute = false;
+};
+
+struct AllCurvesInfo {
+ /** Ordering of all attributes that are propagated to the output curve generically. */
+ OrderedAttributes attributes;
+ /** Ordering of the original curves that are joined. */
+ VectorSet<const CurveEval *> order;
+ /** Preprocessed data about every original curve. This is ordered by #order. */
+ Array<RealizeCurveInfo> realize_info;
+ bool create_id_attribute = false;
+};
+
+/** Collects all tasks that need to be executed to realize all instances. */
+struct GatherTasks {
+ Vector<RealizePointCloudTask> pointcloud_tasks;
+ Vector<RealizeMeshTask> mesh_tasks;
+ Vector<RealizeCurveTask> curve_tasks;
+
+ /* Volumes only have very simple support currently. Only the first found volume is put into the
+ * output. */
+ UserCounter<VolumeComponent> first_volume;
+};
+
+/** Current offsets while during the gather operation. */
+struct GatherOffsets {
+ int pointcloud_offset = 0;
+ MeshElementStartIndices mesh_offsets;
+ int spline_offset = 0;
+};
+
+struct GatherTasksInfo {
+ /** Static information about all geometries that are joined. */
+ const AllPointCloudsInfo &pointclouds;
+ const AllMeshesInfo &meshes;
+ const AllCurvesInfo &curves;
+ bool create_id_attribute_on_any_component = false;
+
+ /**
+ * Under some circumstances, temporary arrays need to be allocated during the gather operation.
+ * For example, when an instance attribute has to be realized as a different data type. This
+ * array owns all the temporary arrays so that they can live until all processing is done.
+ * Use #std::unique_ptr to avoid depending on whether #GArray has an inline buffer or not.
+ */
+ Vector<std::unique_ptr<GArray<>>> &r_temporary_arrays;
+
+ /** All gathered tasks. */
+ GatherTasks r_tasks;
+ /** Current offsets while gathering tasks. */
+ GatherOffsets r_offsets;
+};
+
+/**
+ * Information about the parent instances in the current context.
+ */
+struct InstanceContext {
+ /** Ordered by #AllPointCloudsInfo.attributes. */
+ AttributeFallbacksArray pointclouds;
+ /** Ordered by #AllMeshesInfo.attributes. */
+ AttributeFallbacksArray meshes;
+ /** Ordered by #AllCurvesInfo.attributes. */
+ AttributeFallbacksArray curves;
+ /** Id mixed from all parent instances. */
+ uint32_t id = 0;
+
+ InstanceContext(const GatherTasksInfo &gather_info)
+ : pointclouds(gather_info.pointclouds.attributes.size()),
+ meshes(gather_info.meshes.attributes.size()),
+ curves(gather_info.curves.attributes.size())
+ {
+ }
+};
+
+/* -------------------------------------------------------------------- */
+/** \name Gather Realize Tasks
+ * \{ */
+
+/* Forward declaration. */
+static void gather_realize_tasks_recursive(GatherTasksInfo &gather_info,
+ const GeometrySet &geometry_set,
+ const float4x4 &base_transform,
+ const InstanceContext &base_instance_context);
+
+/**
+ * Checks which of the #ordered_attributes exist on the #instances_component. For each attribute
+ * that exists on the instances, a pair is returned that contains the attribute index and the
+ * corresponding attribute data.
+ */
+static Vector<std::pair<int, GSpan>> prepare_attribute_fallbacks(
+ GatherTasksInfo &gather_info,
+ const InstancesComponent &instances_component,
+ const OrderedAttributes &ordered_attributes)
+{
+ Vector<std::pair<int, GSpan>> attributes_to_override;
+ const CustomDataAttributes &attributes = instances_component.attributes();
+ attributes.foreach_attribute(
+ [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) {
+ const int attribute_index = ordered_attributes.ids.index_of_try(attribute_id);
+ if (attribute_index == -1) {
+ /* The attribute is not propagated to the final geometry. */
+ return true;
+ }
+ GSpan span = *attributes.get_for_read(attribute_id);
+ const CustomDataType expected_type = ordered_attributes.kinds[attribute_index].data_type;
+ if (meta_data.data_type != expected_type) {
+ const CPPType &from_type = span.type();
+ const CPPType &to_type = *custom_data_type_to_cpp_type(expected_type);
+ const bke::DataTypeConversions &conversions = bke::get_implicit_type_conversions();
+ if (!conversions.is_convertible(from_type, to_type)) {
+ /* Ignore the attribute because it can not be converted to the desired type. */
+ return true;
+ }
+ /* Convert the attribute on the instances component to the expected attribute type. */
+ std::unique_ptr<GArray<>> temporary_array = std::make_unique<GArray<>>(
+ to_type, instances_component.instances_amount());
+ conversions.convert_to_initialized_n(span, temporary_array->as_mutable_span());
+ span = temporary_array->as_span();
+ gather_info.r_temporary_arrays.append(std::move(temporary_array));
+ }
+ attributes_to_override.append({attribute_index, span});
+ return true;
+ },
+ ATTR_DOMAIN_INSTANCE);
+ return attributes_to_override;
+}
+
+/**
+ * Calls #fn for every geometry in the given #InstanceReference. Also passes on the transformation
+ * that is applied to every instance.
+ */
+static void foreach_geometry_in_reference(
+ const InstanceReference &reference,
+ const float4x4 &base_transform,
+ const uint32_t id,
+ FunctionRef<void(const GeometrySet &geometry_set, const float4x4 &transform, uint32_t id)> fn)
+{
+ switch (reference.type()) {
+ case InstanceReference::Type::Object: {
+ const Object &object = reference.object();
+ const GeometrySet object_geometry_set = object_get_evaluated_geometry_set(object);
+ fn(object_geometry_set, base_transform, id);
+ break;
+ }
+ case InstanceReference::Type::Collection: {
+ Collection &collection = reference.collection();
+ float4x4 offset_matrix = float4x4::identity();
+ sub_v3_v3(offset_matrix.values[3], collection.instance_offset);
+ int index = 0;
+ FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (&collection, object) {
+ const GeometrySet object_geometry_set = object_get_evaluated_geometry_set(*object);
+ const float4x4 matrix = base_transform * offset_matrix * object->obmat;
+ const int sub_id = noise::hash(id, index);
+ fn(object_geometry_set, matrix, sub_id);
+ index++;
+ }
+ FOREACH_COLLECTION_OBJECT_RECURSIVE_END;
+ break;
+ }
+ case InstanceReference::Type::GeometrySet: {
+ const GeometrySet &instance_geometry_set = reference.geometry_set();
+ fn(instance_geometry_set, base_transform, id);
+ break;
+ }
+ case InstanceReference::Type::None: {
+ break;
+ }
+ }
+}
+
+static void gather_realize_tasks_for_instances(GatherTasksInfo &gather_info,
+ const InstancesComponent &instances_component,
+ const float4x4 &base_transform,
+ const InstanceContext &base_instance_context)
+{
+ const Span<InstanceReference> references = instances_component.references();
+ const Span<int> handles = instances_component.instance_reference_handles();
+ const Span<float4x4> transforms = instances_component.instance_transforms();
+
+ Span<int> stored_instance_ids;
+ if (gather_info.create_id_attribute_on_any_component) {
+ std::optional<GSpan> ids = instances_component.attributes().get_for_read("id");
+ if (ids.has_value()) {
+ stored_instance_ids = ids->typed<int>();
+ }
+ }
+
+ /* Prepare attribute fallbacks. */
+ InstanceContext instance_context = base_instance_context;
+ Vector<std::pair<int, GSpan>> pointcloud_attributes_to_override = prepare_attribute_fallbacks(
+ gather_info, instances_component, gather_info.pointclouds.attributes);
+ Vector<std::pair<int, GSpan>> mesh_attributes_to_override = prepare_attribute_fallbacks(
+ gather_info, instances_component, gather_info.meshes.attributes);
+ Vector<std::pair<int, GSpan>> curve_attributes_to_override = prepare_attribute_fallbacks(
+ gather_info, instances_component, gather_info.curves.attributes);
+
+ for (const int i : transforms.index_range()) {
+ const int handle = handles[i];
+ const float4x4 &transform = transforms[i];
+ const InstanceReference &reference = references[handle];
+ const float4x4 new_base_transform = base_transform * transform;
+
+ /* Update attribute fallbacks for the current instance. */
+ for (const std::pair<int, GSpan> &pair : pointcloud_attributes_to_override) {
+ instance_context.pointclouds.array[pair.first] = pair.second[i];
+ }
+ for (const std::pair<int, GSpan> &pair : mesh_attributes_to_override) {
+ instance_context.meshes.array[pair.first] = pair.second[i];
+ }
+ for (const std::pair<int, GSpan> &pair : curve_attributes_to_override) {
+ instance_context.curves.array[pair.first] = pair.second[i];
+ }
+
+ uint32_t local_instance_id = 0;
+ if (gather_info.create_id_attribute_on_any_component) {
+ if (stored_instance_ids.is_empty()) {
+ local_instance_id = (uint32_t)i;
+ }
+ else {
+ local_instance_id = (uint32_t)stored_instance_ids[i];
+ }
+ }
+ const uint32_t instance_id = noise::hash(base_instance_context.id, local_instance_id);
+
+ /* Add realize tasks for all referenced geometry sets recursively. */
+ foreach_geometry_in_reference(reference,
+ new_base_transform,
+ instance_id,
+ [&](const GeometrySet &instance_geometry_set,
+ const float4x4 &transform,
+ const uint32_t id) {
+ instance_context.id = id;
+ gather_realize_tasks_recursive(gather_info,
+ instance_geometry_set,
+ transform,
+ instance_context);
+ });
+ }
+}
+
+/**
+ * Gather tasks for all geometries in the #geometry_set.
+ */
+static void gather_realize_tasks_recursive(GatherTasksInfo &gather_info,
+ const GeometrySet &geometry_set,
+ const float4x4 &base_transform,
+ const InstanceContext &base_instance_context)
+{
+ for (const GeometryComponent *component : geometry_set.get_components_for_read()) {
+ const GeometryComponentType type = component->type();
+ switch (type) {
+ case GEO_COMPONENT_TYPE_MESH: {
+ const MeshComponent &mesh_component = *static_cast<const MeshComponent *>(component);
+ const Mesh *mesh = mesh_component.get_for_read();
+ if (mesh != nullptr && mesh->totvert > 0) {
+ const int mesh_index = gather_info.meshes.order.index_of(mesh);
+ const MeshRealizeInfo &mesh_info = gather_info.meshes.realize_info[mesh_index];
+ gather_info.r_tasks.mesh_tasks.append({gather_info.r_offsets.mesh_offsets,
+ &mesh_info,
+ base_transform,
+ base_instance_context.meshes,
+ base_instance_context.id});
+ gather_info.r_offsets.mesh_offsets.vertex += mesh->totvert;
+ gather_info.r_offsets.mesh_offsets.edge += mesh->totedge;
+ gather_info.r_offsets.mesh_offsets.loop += mesh->totloop;
+ gather_info.r_offsets.mesh_offsets.poly += mesh->totpoly;
+ }
+ break;
+ }
+ case GEO_COMPONENT_TYPE_POINT_CLOUD: {
+ const PointCloudComponent &pointcloud_component =
+ *static_cast<const PointCloudComponent *>(component);
+ const PointCloud *pointcloud = pointcloud_component.get_for_read();
+ if (pointcloud != nullptr && pointcloud->totpoint > 0) {
+ const int pointcloud_index = gather_info.pointclouds.order.index_of(pointcloud);
+ const PointCloudRealizeInfo &pointcloud_info =
+ gather_info.pointclouds.realize_info[pointcloud_index];
+ gather_info.r_tasks.pointcloud_tasks.append({gather_info.r_offsets.pointcloud_offset,
+ &pointcloud_info,
+ base_transform,
+ base_instance_context.pointclouds,
+ base_instance_context.id});
+ gather_info.r_offsets.pointcloud_offset += pointcloud->totpoint;
+ }
+ break;
+ }
+ case GEO_COMPONENT_TYPE_CURVE: {
+ const CurveComponent &curve_component = *static_cast<const CurveComponent *>(component);
+ const CurveEval *curve = curve_component.get_for_read();
+ if (curve != nullptr && !curve->splines().is_empty()) {
+ const int curve_index = gather_info.curves.order.index_of(curve);
+ const RealizeCurveInfo &curve_info = gather_info.curves.realize_info[curve_index];
+ gather_info.r_tasks.curve_tasks.append({gather_info.r_offsets.spline_offset,
+ &curve_info,
+ base_transform,
+ base_instance_context.curves,
+ base_instance_context.id});
+ gather_info.r_offsets.spline_offset += curve->splines().size();
+ }
+ break;
+ }
+ case GEO_COMPONENT_TYPE_INSTANCES: {
+ const InstancesComponent &instances_component = *static_cast<const InstancesComponent *>(
+ component);
+ gather_realize_tasks_for_instances(
+ gather_info, instances_component, base_transform, base_instance_context);
+ break;
+ }
+ case GEO_COMPONENT_TYPE_VOLUME: {
+ const VolumeComponent *volume_component = static_cast<const VolumeComponent *>(component);
+ if (!gather_info.r_tasks.first_volume) {
+ volume_component->user_add();
+ gather_info.r_tasks.first_volume = const_cast<VolumeComponent *>(volume_component);
+ }
+ break;
+ }
+ }
+ }
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Point Cloud
+ * \{ */
+
+static OrderedAttributes gather_generic_pointcloud_attributes_to_propagate(
+ const GeometrySet &in_geometry_set, const RealizeInstancesOptions &options, bool &r_create_id)
+{
+ Vector<GeometryComponentType> src_component_types;
+ src_component_types.append(GEO_COMPONENT_TYPE_POINT_CLOUD);
+ if (options.realize_instance_attributes) {
+ src_component_types.append(GEO_COMPONENT_TYPE_INSTANCES);
+ }
+
+ Map<AttributeIDRef, AttributeKind> attributes_to_propagate;
+ in_geometry_set.gather_attributes_for_propagation(
+ src_component_types, GEO_COMPONENT_TYPE_POINT_CLOUD, true, attributes_to_propagate);
+ attributes_to_propagate.remove("position");
+ r_create_id = attributes_to_propagate.pop_try("id").has_value();
+ OrderedAttributes ordered_attributes;
+ for (const auto item : attributes_to_propagate.items()) {
+ ordered_attributes.ids.add_new(item.key);
+ ordered_attributes.kinds.append(item.value);
+ }
+ return ordered_attributes;
+}
+
+static void gather_pointclouds_to_realize(const GeometrySet &geometry_set,
+ VectorSet<const PointCloud *> &r_pointclouds)
+{
+ if (const PointCloud *pointcloud = geometry_set.get_pointcloud_for_read()) {
+ if (pointcloud->totpoint > 0) {
+ r_pointclouds.add(pointcloud);
+ }
+ }
+ if (const InstancesComponent *instances =
+ geometry_set.get_component_for_read<InstancesComponent>()) {
+ instances->foreach_referenced_geometry([&](const GeometrySet &instance_geometry_set) {
+ gather_pointclouds_to_realize(instance_geometry_set, r_pointclouds);
+ });
+ }
+}
+
+static AllPointCloudsInfo preprocess_pointclouds(const GeometrySet &geometry_set,
+ const RealizeInstancesOptions &options)
+{
+ AllPointCloudsInfo info;
+ info.attributes = gather_generic_pointcloud_attributes_to_propagate(
+ geometry_set, options, info.create_id_attribute);
+
+ gather_pointclouds_to_realize(geometry_set, info.order);
+ info.realize_info.reinitialize(info.order.size());
+ for (const int pointcloud_index : info.realize_info.index_range()) {
+ PointCloudRealizeInfo &pointcloud_info = info.realize_info[pointcloud_index];
+ const PointCloud *pointcloud = info.order[pointcloud_index];
+ pointcloud_info.pointcloud = pointcloud;
+
+ /* Access attributes. */
+ PointCloudComponent component;
+ component.replace(const_cast<PointCloud *>(pointcloud), GeometryOwnershipType::ReadOnly);
+ pointcloud_info.attributes.reinitialize(info.attributes.size());
+ for (const int attribute_index : info.attributes.index_range()) {
+ const AttributeIDRef &attribute_id = info.attributes.ids[attribute_index];
+ const CustomDataType data_type = info.attributes.kinds[attribute_index].data_type;
+ const AttributeDomain domain = info.attributes.kinds[attribute_index].domain;
+ if (component.attribute_exists(attribute_id)) {
+ GVArray attribute = component.attribute_get_for_read(attribute_id, domain, data_type);
+ pointcloud_info.attributes[attribute_index].emplace(std::move(attribute));
+ }
+ }
+ if (info.create_id_attribute) {
+ ReadAttributeLookup ids_lookup = component.attribute_try_get_for_read("id");
+ if (ids_lookup) {
+ pointcloud_info.stored_ids = ids_lookup.varray.get_internal_span().typed<int>();
+ }
+ }
+ }
+ return info;
+}
+
+static void execute_realize_pointcloud_task(const RealizeInstancesOptions &options,
+ const RealizePointCloudTask &task,
+ PointCloud &dst_pointcloud,
+ MutableSpan<GMutableSpan> dst_attribute_spans,
+ MutableSpan<int> all_dst_ids)
+{
+ const PointCloudRealizeInfo &pointcloud_info = *task.pointcloud_info;
+ const PointCloud &pointcloud = *pointcloud_info.pointcloud;
+ const Span<float3> src_positions{(float3 *)pointcloud.co, pointcloud.totpoint};
+ const IndexRange point_slice{task.start_index, pointcloud.totpoint};
+ MutableSpan<float3> dst_positions{(float3 *)dst_pointcloud.co + task.start_index,
+ pointcloud.totpoint};
+ MutableSpan<int> dst_ids = all_dst_ids.slice(task.start_index, pointcloud.totpoint);
+
+ /* Copy transformed positions. */
+ threading::parallel_for(IndexRange(pointcloud.totpoint), 1024, [&](const IndexRange range) {
+ for (const int i : range) {
+ dst_positions[i] = task.transform * src_positions[i];
+ }
+ });
+ /* Create point ids. */
+ if (!all_dst_ids.is_empty()) {
+ if (options.keep_original_ids) {
+ if (pointcloud_info.stored_ids.is_empty()) {
+ dst_ids.fill(0);
+ }
+ else {
+ dst_ids.copy_from(pointcloud_info.stored_ids);
+ }
+ }
+ else {
+ threading::parallel_for(IndexRange(pointcloud.totpoint), 1024, [&](const IndexRange range) {
+ if (pointcloud_info.stored_ids.is_empty()) {
+ for (const int i : range) {
+ dst_ids[i] = noise::hash(task.id, i);
+ }
+ }
+ else {
+ for (const int i : range) {
+ dst_ids[i] = noise::hash(task.id, pointcloud_info.stored_ids[i]);
+ }
+ }
+ });
+ }
+ }
+ /* Copy generic attributes. */
+ threading::parallel_for(
+ dst_attribute_spans.index_range(), 10, [&](const IndexRange attribute_range) {
+ for (const int attribute_index : attribute_range) {
+ GMutableSpan dst_span = dst_attribute_spans[attribute_index].slice(point_slice);
+ const CPPType &cpp_type = dst_span.type();
+ const void *attribute_fallback = task.attribute_fallbacks.array[attribute_index];
+ if (pointcloud_info.attributes[attribute_index].has_value()) {
+ /* Copy attribute from the original point cloud. */
+ const GSpan src_span = *pointcloud_info.attributes[attribute_index];
+ threading::parallel_for(
+ IndexRange(pointcloud.totpoint), 1024, [&](const IndexRange range) {
+ cpp_type.copy_assign_n(
+ src_span.slice(range).data(), dst_span.slice(range).data(), range.size());
+ });
+ }
+ else {
+ if (attribute_fallback == nullptr) {
+ attribute_fallback = cpp_type.default_value();
+ }
+ /* As the fallback value for the attribute. */
+ threading::parallel_for(
+ IndexRange(pointcloud.totpoint), 1024, [&](const IndexRange range) {
+ cpp_type.fill_assign_n(
+ attribute_fallback, dst_span.slice(range).data(), range.size());
+ });
+ }
+ }
+ });
+}
+
+static void execute_realize_pointcloud_tasks(const RealizeInstancesOptions &options,
+ const AllPointCloudsInfo &all_pointclouds_info,
+ const Span<RealizePointCloudTask> tasks,
+ const OrderedAttributes &ordered_attributes,
+ GeometrySet &r_realized_geometry)
+{
+ if (tasks.is_empty()) {
+ return;
+ }
+
+ const RealizePointCloudTask &last_task = tasks.last();
+ const PointCloud &last_pointcloud = *last_task.pointcloud_info->pointcloud;
+ const int tot_points = last_task.start_index + last_pointcloud.totpoint;
+
+ /* Allocate new point cloud. */
+ PointCloud *dst_pointcloud = BKE_pointcloud_new_nomain(tot_points);
+ PointCloudComponent &dst_component =
+ r_realized_geometry.get_component_for_write<PointCloudComponent>();
+ dst_component.replace(dst_pointcloud);
+
+ /* Prepare id attribute. */
+ OutputAttribute_Typed<int> point_ids;
+ MutableSpan<int> point_ids_span;
+ if (all_pointclouds_info.create_id_attribute) {
+ point_ids = dst_component.attribute_try_get_for_output_only<int>("id", ATTR_DOMAIN_POINT);
+ point_ids_span = point_ids.as_span();
+ }
+
+ /* Prepare generic output attributes. */
+ Vector<OutputAttribute> dst_attributes;
+ Vector<GMutableSpan> dst_attribute_spans;
+ for (const int attribute_index : ordered_attributes.index_range()) {
+ const AttributeIDRef &attribute_id = ordered_attributes.ids[attribute_index];
+ const CustomDataType data_type = ordered_attributes.kinds[attribute_index].data_type;
+ OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only(
+ attribute_id, ATTR_DOMAIN_POINT, data_type);
+ dst_attribute_spans.append(dst_attribute.as_span());
+ dst_attributes.append(std::move(dst_attribute));
+ }
+
+ /* Actually execute all tasks. */
+ threading::parallel_for(tasks.index_range(), 100, [&](const IndexRange task_range) {
+ for (const int task_index : task_range) {
+ const RealizePointCloudTask &task = tasks[task_index];
+ execute_realize_pointcloud_task(
+ options, task, *dst_pointcloud, dst_attribute_spans, point_ids_span);
+ }
+ });
+
+ /* Save modified attributes. */
+ for (OutputAttribute &dst_attribute : dst_attributes) {
+ dst_attribute.save();
+ }
+ if (point_ids) {
+ point_ids.save();
+ }
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Mesh
+ * \{ */
+
+static OrderedAttributes gather_generic_mesh_attributes_to_propagate(
+ const GeometrySet &in_geometry_set, const RealizeInstancesOptions &options, bool &r_create_id)
+{
+ Vector<GeometryComponentType> src_component_types;
+ src_component_types.append(GEO_COMPONENT_TYPE_MESH);
+ if (options.realize_instance_attributes) {
+ src_component_types.append(GEO_COMPONENT_TYPE_INSTANCES);
+ }
+
+ Map<AttributeIDRef, AttributeKind> attributes_to_propagate;
+ in_geometry_set.gather_attributes_for_propagation(
+ src_component_types, GEO_COMPONENT_TYPE_MESH, true, attributes_to_propagate);
+ attributes_to_propagate.remove("position");
+ attributes_to_propagate.remove("normal");
+ attributes_to_propagate.remove("material_index");
+ attributes_to_propagate.remove("shade_smooth");
+ attributes_to_propagate.remove("crease");
+ r_create_id = attributes_to_propagate.pop_try("id").has_value();
+ OrderedAttributes ordered_attributes;
+ for (const auto item : attributes_to_propagate.items()) {
+ ordered_attributes.ids.add_new(item.key);
+ ordered_attributes.kinds.append(item.value);
+ }
+ return ordered_attributes;
+}
+
+static void gather_meshes_to_realize(const GeometrySet &geometry_set,
+ VectorSet<const Mesh *> &r_meshes)
+{
+ if (const Mesh *mesh = geometry_set.get_mesh_for_read()) {
+ if (mesh->totvert > 0) {
+ r_meshes.add(mesh);
+ }
+ }
+ if (const InstancesComponent *instances =
+ geometry_set.get_component_for_read<InstancesComponent>()) {
+ instances->foreach_referenced_geometry([&](const GeometrySet &instance_geometry_set) {
+ gather_meshes_to_realize(instance_geometry_set, r_meshes);
+ });
+ }
+}
+
+static AllMeshesInfo preprocess_meshes(const GeometrySet &geometry_set,
+ const RealizeInstancesOptions &options)
+{
+ AllMeshesInfo info;
+ info.attributes = gather_generic_mesh_attributes_to_propagate(
+ geometry_set, options, info.create_id_attribute);
+
+ gather_meshes_to_realize(geometry_set, info.order);
+ for (const Mesh *mesh : info.order) {
+ for (const int slot_index : IndexRange(mesh->totcol)) {
+ Material *material = mesh->mat[slot_index];
+ info.materials.add(material);
+ }
+ }
+ info.realize_info.reinitialize(info.order.size());
+ for (const int mesh_index : info.realize_info.index_range()) {
+ MeshRealizeInfo &mesh_info = info.realize_info[mesh_index];
+ const Mesh *mesh = info.order[mesh_index];
+ mesh_info.mesh = mesh;
+
+ /* Create material index mapping. */
+ mesh_info.material_index_map.reinitialize(mesh->totcol);
+ for (const int old_slot_index : IndexRange(mesh->totcol)) {
+ Material *material = mesh->mat[old_slot_index];
+ const int new_slot_index = info.materials.index_of(material);
+ mesh_info.material_index_map[old_slot_index] = new_slot_index;
+ }
+
+ /* Access attributes. */
+ MeshComponent component;
+ component.replace(const_cast<Mesh *>(mesh), GeometryOwnershipType::ReadOnly);
+ mesh_info.attributes.reinitialize(info.attributes.size());
+ for (const int attribute_index : info.attributes.index_range()) {
+ const AttributeIDRef &attribute_id = info.attributes.ids[attribute_index];
+ const CustomDataType data_type = info.attributes.kinds[attribute_index].data_type;
+ const AttributeDomain domain = info.attributes.kinds[attribute_index].domain;
+ if (component.attribute_exists(attribute_id)) {
+ GVArray attribute = component.attribute_get_for_read(attribute_id, domain, data_type);
+ mesh_info.attributes[attribute_index].emplace(std::move(attribute));
+ }
+ }
+ if (info.create_id_attribute) {
+ ReadAttributeLookup ids_lookup = component.attribute_try_get_for_read("id");
+ if (ids_lookup) {
+ mesh_info.stored_vertex_ids = ids_lookup.varray.get_internal_span().typed<int>();
+ }
+ }
+ }
+ return info;
+}
+
+static void execute_realize_mesh_task(const RealizeInstancesOptions &options,
+ const RealizeMeshTask &task,
+ const OrderedAttributes &ordered_attributes,
+ Mesh &dst_mesh,
+ MutableSpan<GMutableSpan> dst_attribute_spans,
+ MutableSpan<int> all_dst_vertex_ids)
+{
+ const MeshRealizeInfo &mesh_info = *task.mesh_info;
+ const Mesh &mesh = *mesh_info.mesh;
+
+ const Span<MVert> src_verts{mesh.mvert, mesh.totvert};
+ const Span<MEdge> src_edges{mesh.medge, mesh.totedge};
+ const Span<MLoop> src_loops{mesh.mloop, mesh.totloop};
+ const Span<MPoly> src_polys{mesh.mpoly, mesh.totpoly};
+
+ MutableSpan<MVert> dst_verts{dst_mesh.mvert + task.start_indices.vertex, mesh.totvert};
+ MutableSpan<MEdge> dst_edges{dst_mesh.medge + task.start_indices.edge, mesh.totedge};
+ MutableSpan<MLoop> dst_loops{dst_mesh.mloop + task.start_indices.loop, mesh.totloop};
+ MutableSpan<MPoly> dst_polys{dst_mesh.mpoly + task.start_indices.poly, mesh.totpoly};
+
+ MutableSpan<int> dst_vertex_ids = all_dst_vertex_ids.slice(task.start_indices.vertex,
+ mesh.totvert);
+
+ const Span<int> material_index_map = mesh_info.material_index_map;
+
+ threading::parallel_for(IndexRange(mesh.totvert), 1024, [&](const IndexRange vert_range) {
+ for (const int i : vert_range) {
+ const MVert &src_vert = src_verts[i];
+ MVert &dst_vert = dst_verts[i];
+ dst_vert = src_vert;
+ copy_v3_v3(dst_vert.co, task.transform * float3(src_vert.co));
+ }
+ });
+ threading::parallel_for(IndexRange(mesh.totedge), 1024, [&](const IndexRange edge_range) {
+ for (const int i : edge_range) {
+ const MEdge &src_edge = src_edges[i];
+ MEdge &dst_edge = dst_edges[i];
+ dst_edge = src_edge;
+ dst_edge.v1 += task.start_indices.vertex;
+ dst_edge.v2 += task.start_indices.vertex;
+ }
+ });
+ threading::parallel_for(IndexRange(mesh.totloop), 1024, [&](const IndexRange loop_range) {
+ for (const int i : loop_range) {
+ const MLoop &src_loop = src_loops[i];
+ MLoop &dst_loop = dst_loops[i];
+ dst_loop = src_loop;
+ dst_loop.v += task.start_indices.vertex;
+ dst_loop.e += task.start_indices.edge;
+ }
+ });
+ threading::parallel_for(IndexRange(mesh.totpoly), 1024, [&](const IndexRange poly_range) {
+ for (const int i : poly_range) {
+ const MPoly &src_poly = src_polys[i];
+ MPoly &dst_poly = dst_polys[i];
+ dst_poly = src_poly;
+ dst_poly.loopstart += task.start_indices.loop;
+ if (src_poly.mat_nr >= 0 && src_poly.mat_nr < mesh.totcol) {
+ dst_poly.mat_nr = material_index_map[src_poly.mat_nr];
+ }
+ else {
+ /* The material index was invalid before. */
+ dst_poly.mat_nr = 0;
+ }
+ }
+ });
+ /* Create id attribute. */
+ if (!all_dst_vertex_ids.is_empty()) {
+ if (options.keep_original_ids) {
+ if (mesh_info.stored_vertex_ids.is_empty()) {
+ dst_vertex_ids.fill(0);
+ }
+ else {
+ dst_vertex_ids.copy_from(mesh_info.stored_vertex_ids);
+ }
+ }
+ else {
+ threading::parallel_for(IndexRange(mesh.totvert), 1024, [&](const IndexRange vert_range) {
+ if (mesh_info.stored_vertex_ids.is_empty()) {
+ for (const int i : vert_range) {
+ dst_vertex_ids[i] = noise::hash(task.id, i);
+ }
+ }
+ else {
+ for (const int i : vert_range) {
+ const int original_id = mesh_info.stored_vertex_ids[i];
+ dst_vertex_ids[i] = noise::hash(task.id, original_id);
+ }
+ }
+ });
+ }
+ }
+ /* Copy generic attributes. */
+ threading::parallel_for(
+ dst_attribute_spans.index_range(), 10, [&](const IndexRange attribute_range) {
+ for (const int attribute_index : attribute_range) {
+ const AttributeDomain domain = ordered_attributes.kinds[attribute_index].domain;
+ IndexRange element_slice;
+ switch (domain) {
+ case ATTR_DOMAIN_POINT:
+ element_slice = IndexRange(task.start_indices.vertex, mesh.totvert);
+ break;
+ case ATTR_DOMAIN_EDGE:
+ element_slice = IndexRange(task.start_indices.edge, mesh.totedge);
+ break;
+ case ATTR_DOMAIN_CORNER:
+ element_slice = IndexRange(task.start_indices.loop, mesh.totloop);
+ break;
+ case ATTR_DOMAIN_FACE:
+ element_slice = IndexRange(task.start_indices.poly, mesh.totpoly);
+ break;
+ default:
+ BLI_assert_unreachable();
+ }
+ GMutableSpan dst_span = dst_attribute_spans[attribute_index].slice(element_slice);
+ const CPPType &cpp_type = dst_span.type();
+ const void *attribute_fallback = task.attribute_fallbacks.array[attribute_index];
+ if (mesh_info.attributes[attribute_index].has_value()) {
+ const GSpan src_span = *mesh_info.attributes[attribute_index];
+ threading::parallel_for(
+ IndexRange(element_slice.size()), 1024, [&](const IndexRange sub_range) {
+ cpp_type.copy_assign_n(src_span.slice(sub_range).data(),
+ dst_span.slice(sub_range).data(),
+ sub_range.size());
+ });
+ }
+ else {
+ if (attribute_fallback == nullptr) {
+ attribute_fallback = cpp_type.default_value();
+ }
+ threading::parallel_for(
+ IndexRange(element_slice.size()), 1024, [&](const IndexRange sub_range) {
+ cpp_type.fill_assign_n(
+ attribute_fallback, dst_span.slice(sub_range).data(), sub_range.size());
+ });
+ }
+ }
+ });
+}
+
+static void execute_realize_mesh_tasks(const RealizeInstancesOptions &options,
+ const AllMeshesInfo &all_meshes_info,
+ const Span<RealizeMeshTask> tasks,
+ const OrderedAttributes &ordered_attributes,
+ const VectorSet<Material *> &ordered_materials,
+ GeometrySet &r_realized_geometry)
+{
+ if (tasks.is_empty()) {
+ return;
+ }
+
+ const RealizeMeshTask &last_task = tasks.last();
+ const Mesh &last_mesh = *last_task.mesh_info->mesh;
+ const int tot_vertices = last_task.start_indices.vertex + last_mesh.totvert;
+ const int tot_edges = last_task.start_indices.edge + last_mesh.totedge;
+ const int tot_loops = last_task.start_indices.loop + last_mesh.totloop;
+ const int tot_poly = last_task.start_indices.poly + last_mesh.totpoly;
+
+ Mesh *dst_mesh = BKE_mesh_new_nomain(tot_vertices, tot_edges, 0, tot_loops, tot_poly);
+ MeshComponent &dst_component = r_realized_geometry.get_component_for_write<MeshComponent>();
+ dst_component.replace(dst_mesh);
+
+ /* Copy settings from the first input geometry set with a mesh. */
+ const RealizeMeshTask &first_task = tasks.first();
+ const Mesh &first_mesh = *first_task.mesh_info->mesh;
+ BKE_mesh_copy_parameters_for_eval(dst_mesh, &first_mesh);
+
+ /* Add materials. */
+ for (const int i : IndexRange(ordered_materials.size())) {
+ Material *material = ordered_materials[i];
+ BKE_id_material_eval_assign(&dst_mesh->id, i + 1, material);
+ }
+
+ /* Prepare id attribute. */
+ OutputAttribute_Typed<int> vertex_ids;
+ MutableSpan<int> vertex_ids_span;
+ if (all_meshes_info.create_id_attribute) {
+ vertex_ids = dst_component.attribute_try_get_for_output_only<int>("id", ATTR_DOMAIN_POINT);
+ vertex_ids_span = vertex_ids.as_span();
+ }
+
+ /* Prepare generic output attributes. */
+ Vector<OutputAttribute> dst_attributes;
+ Vector<GMutableSpan> dst_attribute_spans;
+ for (const int attribute_index : ordered_attributes.index_range()) {
+ const AttributeIDRef &attribute_id = ordered_attributes.ids[attribute_index];
+ const AttributeDomain domain = ordered_attributes.kinds[attribute_index].domain;
+ const CustomDataType data_type = ordered_attributes.kinds[attribute_index].data_type;
+ OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only(
+ attribute_id, domain, data_type);
+ dst_attribute_spans.append(dst_attribute.as_span());
+ dst_attributes.append(std::move(dst_attribute));
+ }
+
+ /* Actually execute all tasks. */
+ threading::parallel_for(tasks.index_range(), 100, [&](const IndexRange task_range) {
+ for (const int task_index : task_range) {
+ const RealizeMeshTask &task = tasks[task_index];
+ execute_realize_mesh_task(
+ options, task, ordered_attributes, *dst_mesh, dst_attribute_spans, vertex_ids_span);
+ }
+ });
+
+ /* Save modified attributes. */
+ for (OutputAttribute &dst_attribute : dst_attributes) {
+ dst_attribute.save();
+ }
+ if (vertex_ids) {
+ vertex_ids.save();
+ }
+
+ BKE_mesh_normals_tag_dirty(dst_mesh);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Curve
+ * \{ */
+
+static OrderedAttributes gather_generic_curve_attributes_to_propagate(
+ const GeometrySet &in_geometry_set, const RealizeInstancesOptions &options, bool &r_create_id)
+{
+ Vector<GeometryComponentType> src_component_types;
+ src_component_types.append(GEO_COMPONENT_TYPE_CURVE);
+ if (options.realize_instance_attributes) {
+ src_component_types.append(GEO_COMPONENT_TYPE_INSTANCES);
+ }
+
+ Map<AttributeIDRef, AttributeKind> attributes_to_propagate;
+ in_geometry_set.gather_attributes_for_propagation(
+ src_component_types, GEO_COMPONENT_TYPE_CURVE, true, attributes_to_propagate);
+ attributes_to_propagate.remove("position");
+ attributes_to_propagate.remove("cyclic");
+ attributes_to_propagate.remove("resolution");
+ attributes_to_propagate.remove("tilt");
+ attributes_to_propagate.remove("radius");
+ attributes_to_propagate.remove("handle_right");
+ attributes_to_propagate.remove("handle_left");
+ r_create_id = attributes_to_propagate.pop_try("id").has_value();
+ OrderedAttributes ordered_attributes;
+ for (const auto item : attributes_to_propagate.items()) {
+ ordered_attributes.ids.add_new(item.key);
+ ordered_attributes.kinds.append(item.value);
+ }
+ return ordered_attributes;
+}
+
+static void gather_curves_to_realize(const GeometrySet &geometry_set,
+ VectorSet<const CurveEval *> &r_curves)
+{
+ if (const CurveEval *curve = geometry_set.get_curve_for_read()) {
+ if (!curve->splines().is_empty()) {
+ r_curves.add(curve);
+ }
+ }
+ if (const InstancesComponent *instances =
+ geometry_set.get_component_for_read<InstancesComponent>()) {
+ instances->foreach_referenced_geometry([&](const GeometrySet &instance_geometry_set) {
+ gather_curves_to_realize(instance_geometry_set, r_curves);
+ });
+ }
+}
+
+static AllCurvesInfo preprocess_curves(const GeometrySet &geometry_set,
+ const RealizeInstancesOptions &options)
+{
+ AllCurvesInfo info;
+ info.attributes = gather_generic_curve_attributes_to_propagate(
+ geometry_set, options, info.create_id_attribute);
+
+ gather_curves_to_realize(geometry_set, info.order);
+ info.realize_info.reinitialize(info.order.size());
+ for (const int curve_index : info.realize_info.index_range()) {
+ RealizeCurveInfo &curve_info = info.realize_info[curve_index];
+ const CurveEval *curve = info.order[curve_index];
+ curve_info.curve = curve;
+
+ /* Access attributes. */
+ CurveComponent component;
+ component.replace(const_cast<CurveEval *>(curve), GeometryOwnershipType::ReadOnly);
+ curve_info.spline_attributes.reinitialize(info.attributes.size());
+ for (const int attribute_index : info.attributes.index_range()) {
+ const AttributeDomain domain = info.attributes.kinds[attribute_index].domain;
+ if (domain != ATTR_DOMAIN_CURVE) {
+ continue;
+ }
+ const AttributeIDRef &attribute_id = info.attributes.ids[attribute_index];
+ const CustomDataType data_type = info.attributes.kinds[attribute_index].data_type;
+ if (component.attribute_exists(attribute_id)) {
+ GVArray attribute = component.attribute_get_for_read(attribute_id, domain, data_type);
+ curve_info.spline_attributes[attribute_index].emplace(std::move(attribute));
+ }
+ }
+ }
+ return info;
+}
+
+static void execute_realize_curve_task(const RealizeInstancesOptions &options,
+ const AllCurvesInfo &all_curves_info,
+ const RealizeCurveTask &task,
+ const OrderedAttributes &ordered_attributes,
+ MutableSpan<SplinePtr> dst_splines,
+ MutableSpan<GMutableSpan> dst_spline_attributes)
+{
+ const RealizeCurveInfo &curve_info = *task.curve_info;
+ const CurveEval &curve = *curve_info.curve;
+
+ const Span<SplinePtr> src_splines = curve.splines();
+
+ /* Initialize point attributes. */
+ threading::parallel_for(src_splines.index_range(), 100, [&](const IndexRange src_spline_range) {
+ for (const int src_spline_index : src_spline_range) {
+ const int dst_spline_index = src_spline_index + task.start_spline_index;
+ const Spline &src_spline = *src_splines[src_spline_index];
+ SplinePtr dst_spline = src_spline.copy_without_attributes();
+ dst_spline->transform(task.transform);
+ const int spline_size = dst_spline->size();
+
+ const CustomDataAttributes &src_point_attributes = src_spline.attributes;
+ CustomDataAttributes &dst_point_attributes = dst_spline->attributes;
+
+ /* Create point ids. */
+ if (all_curves_info.create_id_attribute) {
+ dst_point_attributes.create("id", CD_PROP_INT32);
+ MutableSpan<int> dst_point_ids = dst_point_attributes.get_for_write("id")->typed<int>();
+ std::optional<GSpan> src_point_ids_opt = src_point_attributes.get_for_read("id");
+ if (options.keep_original_ids) {
+ if (src_point_ids_opt.has_value()) {
+ const Span<int> src_point_ids = src_point_ids_opt->typed<int>();
+ dst_point_ids.copy_from(src_point_ids);
+ }
+ else {
+ dst_point_ids.fill(0);
+ }
+ }
+ else {
+ if (src_point_ids_opt.has_value()) {
+ const Span<int> src_point_ids = src_point_ids_opt->typed<int>();
+ for (const int i : IndexRange(dst_spline->size())) {
+ dst_point_ids[i] = noise::hash(task.id, src_point_ids[i]);
+ }
+ }
+ else {
+ for (const int i : IndexRange(dst_spline->size())) {
+ /* Mix spline index into the id, because otherwise points on different splines will
+ * get the same id. */
+ dst_point_ids[i] = noise::hash(task.id, src_spline_index, i);
+ }
+ }
+ }
+ }
+
+ /* Copy generic point attributes. */
+ for (const int attribute_index : ordered_attributes.index_range()) {
+ const AttributeDomain domain = ordered_attributes.kinds[attribute_index].domain;
+ if (domain != ATTR_DOMAIN_POINT) {
+ continue;
+ }
+ const CustomDataType data_type = ordered_attributes.kinds[attribute_index].data_type;
+ const CPPType &cpp_type = *custom_data_type_to_cpp_type(data_type);
+ const AttributeIDRef &attribute_id = ordered_attributes.ids[attribute_index];
+ const void *attribute_fallback = task.attribute_fallbacks.array[attribute_index];
+ const std::optional<GSpan> src_span_opt = src_point_attributes.get_for_read(attribute_id);
+ void *dst_buffer = MEM_malloc_arrayN(spline_size, cpp_type.size(), __func__);
+ if (src_span_opt.has_value()) {
+ const GSpan src_span = *src_span_opt;
+ cpp_type.copy_construct_n(src_span.data(), dst_buffer, spline_size);
+ }
+ else {
+ if (attribute_fallback == nullptr) {
+ attribute_fallback = cpp_type.default_value();
+ }
+ cpp_type.fill_construct_n(attribute_fallback, dst_buffer, spline_size);
+ }
+ dst_point_attributes.create_by_move(attribute_id, data_type, dst_buffer);
+ }
+
+ dst_splines[dst_spline_index] = std::move(dst_spline);
+ }
+ });
+ /* Initialize spline attributes. */
+ for (const int attribute_index : ordered_attributes.index_range()) {
+ const AttributeDomain domain = ordered_attributes.kinds[attribute_index].domain;
+ if (domain != ATTR_DOMAIN_CURVE) {
+ continue;
+ }
+ const CustomDataType data_type = ordered_attributes.kinds[attribute_index].data_type;
+ const CPPType &cpp_type = *custom_data_type_to_cpp_type(data_type);
+
+ GMutableSpan dst_span = dst_spline_attributes[attribute_index].slice(task.start_spline_index,
+ src_splines.size());
+ if (curve_info.spline_attributes[attribute_index].has_value()) {
+ const GSpan src_span = *curve_info.spline_attributes[attribute_index];
+ cpp_type.copy_construct_n(src_span.data(), dst_span.data(), src_splines.size());
+ }
+ else {
+ const void *attribute_fallback = task.attribute_fallbacks.array[attribute_index];
+ if (attribute_fallback == nullptr) {
+ attribute_fallback = cpp_type.default_value();
+ }
+ cpp_type.fill_construct_n(attribute_fallback, dst_span.data(), src_splines.size());
+ }
+ }
+}
+
+static void execute_realize_curve_tasks(const RealizeInstancesOptions &options,
+ const AllCurvesInfo &all_curves_info,
+ const Span<RealizeCurveTask> tasks,
+ const OrderedAttributes &ordered_attributes,
+ GeometrySet &r_realized_geometry)
+{
+ if (tasks.is_empty()) {
+ return;
+ }
+
+ const RealizeCurveTask &last_task = tasks.last();
+ const CurveEval &last_curve = *last_task.curve_info->curve;
+ const int tot_splines = last_task.start_spline_index + last_curve.splines().size();
+
+ Array<SplinePtr> dst_splines(tot_splines);
+
+ CurveEval *dst_curve = new CurveEval();
+ dst_curve->attributes.reallocate(tot_splines);
+ CustomDataAttributes &spline_attributes = dst_curve->attributes;
+
+ /* Prepare spline attributes. */
+ Vector<GMutableSpan> dst_spline_attributes;
+ for (const int attribute_index : ordered_attributes.index_range()) {
+ const AttributeIDRef &attribute_id = ordered_attributes.ids[attribute_index];
+ const CustomDataType data_type = ordered_attributes.kinds[attribute_index].data_type;
+ const AttributeDomain domain = ordered_attributes.kinds[attribute_index].domain;
+ if (domain == ATTR_DOMAIN_CURVE) {
+ spline_attributes.create(attribute_id, data_type);
+ dst_spline_attributes.append(*spline_attributes.get_for_write(attribute_id));
+ }
+ else {
+ dst_spline_attributes.append({CPPType::get<float>()});
+ }
+ }
+
+ /* Actually execute all tasks. */
+ threading::parallel_for(tasks.index_range(), 100, [&](const IndexRange task_range) {
+ for (const int task_index : task_range) {
+ const RealizeCurveTask &task = tasks[task_index];
+ execute_realize_curve_task(
+ options, all_curves_info, task, ordered_attributes, dst_splines, dst_spline_attributes);
+ }
+ });
+
+ dst_curve->add_splines(dst_splines);
+
+ CurveComponent &dst_component = r_realized_geometry.get_component_for_write<CurveComponent>();
+ dst_component.replace(dst_curve);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Realize Instances
+ * \{ */
+
+static void remove_id_attribute_from_instances(GeometrySet &geometry_set)
+{
+ geometry_set.modify_geometry_sets([&](GeometrySet &sub_geometry) {
+ if (sub_geometry.has<InstancesComponent>()) {
+ InstancesComponent &component = geometry_set.get_component_for_write<InstancesComponent>();
+ component.attributes().remove("id");
+ }
+ });
+}
+
+GeometrySet realize_instances(GeometrySet geometry_set, const RealizeInstancesOptions &options)
+{
+ /* The algorithm works in three steps:
+ * 1. Preprocess each unique geometry that is instanced (e.g. each `Mesh`).
+ * 2. Gather "tasks" that need to be executed to realize the instances. Each task corresponds to
+ * instances of the previously preprocessed geometry.
+ * 3. Execute all tasks in parallel.
+ */
+
+ if (!geometry_set.has_instances()) {
+ return geometry_set;
+ }
+
+ if (options.keep_original_ids) {
+ remove_id_attribute_from_instances(geometry_set);
+ }
+
+ AllPointCloudsInfo all_pointclouds_info = preprocess_pointclouds(geometry_set, options);
+ AllMeshesInfo all_meshes_info = preprocess_meshes(geometry_set, options);
+ AllCurvesInfo all_curves_info = preprocess_curves(geometry_set, options);
+
+ Vector<std::unique_ptr<GArray<>>> temporary_arrays;
+ const bool create_id_attribute = all_pointclouds_info.create_id_attribute ||
+ all_meshes_info.create_id_attribute ||
+ all_curves_info.create_id_attribute;
+ GatherTasksInfo gather_info = {all_pointclouds_info,
+ all_meshes_info,
+ all_curves_info,
+ create_id_attribute,
+ temporary_arrays};
+ const float4x4 transform = float4x4::identity();
+ InstanceContext attribute_fallbacks(gather_info);
+ gather_realize_tasks_recursive(gather_info, geometry_set, transform, attribute_fallbacks);
+
+ GeometrySet new_geometry_set;
+ execute_realize_pointcloud_tasks(options,
+ all_pointclouds_info,
+ gather_info.r_tasks.pointcloud_tasks,
+ all_pointclouds_info.attributes,
+ new_geometry_set);
+ execute_realize_mesh_tasks(options,
+ all_meshes_info,
+ gather_info.r_tasks.mesh_tasks,
+ all_meshes_info.attributes,
+ all_meshes_info.materials,
+ new_geometry_set);
+ execute_realize_curve_tasks(options,
+ all_curves_info,
+ gather_info.r_tasks.curve_tasks,
+ all_curves_info.attributes,
+ new_geometry_set);
+
+ if (gather_info.r_tasks.first_volume) {
+ new_geometry_set.add(*gather_info.r_tasks.first_volume);
+ }
+
+ return new_geometry_set;
+}
+
+GeometrySet realize_instances_legacy(GeometrySet geometry_set)
+{
+ RealizeInstancesOptions options;
+ options.keep_original_ids = true;
+ options.realize_instance_attributes = false;
+ return realize_instances(std::move(geometry_set), options);
+}
+
+/** \} */
+
+} // namespace blender::geometry
diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h
index b0ff10ebd1d..92b80344462 100644
--- a/source/blender/makesdna/DNA_node_types.h
+++ b/source/blender/makesdna/DNA_node_types.h
@@ -2290,6 +2290,10 @@ typedef enum GeometryNodeDeleteGeometryMode {
GEO_NODE_DELETE_GEOMETRY_MODE_ONLY_FACE = 2,
} GeometryNodeDeleteGeometryMode;
+typedef enum GeometryNodeRealizeInstancesFlag {
+ GEO_NODE_REALIZE_INSTANCES_LEGACY_BEHAVIOR = (1 << 0),
+} GeometryNodeRealizeInstancesFlag;
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c
index 984e87692f7..b9ddb6a772a 100644
--- a/source/blender/makesrna/intern/rna_nodetree.c
+++ b/source/blender/makesrna/intern/rna_nodetree.c
@@ -11275,6 +11275,17 @@ static void def_geo_viewer(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_GeometryNode_socket_update");
}
+static void def_geo_realize_instances(StructRNA *srna)
+{
+ PropertyRNA *prop;
+
+ prop = RNA_def_property(srna, "legacy_behavior", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "custom1", GEO_NODE_REALIZE_INSTANCES_LEGACY_BEHAVIOR);
+ RNA_def_property_ui_text(
+ prop, "Legacy Behavior", "Behave like before instance attributes existed");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_GeometryNode_socket_update");
+}
+
/* -------------------------------------------------------------------------- */
static void rna_def_shader_node(BlenderRNA *brna)
diff --git a/source/blender/modifiers/CMakeLists.txt b/source/blender/modifiers/CMakeLists.txt
index d9b9fa96d04..c30beaf001a 100644
--- a/source/blender/modifiers/CMakeLists.txt
+++ b/source/blender/modifiers/CMakeLists.txt
@@ -30,6 +30,7 @@ set(INC
../depsgraph
../editors/include
../functions
+ ../geometry
../makesdna
../makesrna
../nodes
diff --git a/source/blender/nodes/NOD_geometry_exec.hh b/source/blender/nodes/NOD_geometry_exec.hh
index 5f55794d88c..f225b3b94b2 100644
--- a/source/blender/nodes/NOD_geometry_exec.hh
+++ b/source/blender/nodes/NOD_geometry_exec.hh
@@ -28,6 +28,8 @@
#include "NOD_derived_node_tree.hh"
#include "NOD_geometry_nodes_eval_log.hh"
+#include "GEO_realize_instances.hh"
+
struct Depsgraph;
struct ModifierData;
@@ -36,7 +38,6 @@ namespace blender::nodes {
using bke::AnonymousAttributeFieldInput;
using bke::AttributeFieldInput;
using bke::AttributeIDRef;
-using bke::geometry_set_realize_instances;
using bke::GeometryComponentFieldContext;
using bke::GeometryFieldInput;
using bke::OutputAttribute;
diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h
index 6b576596492..84bc7bf0ceb 100644
--- a/source/blender/nodes/NOD_static_types.h
+++ b/source/blender/nodes/NOD_static_types.h
@@ -394,7 +394,7 @@ DefNode(GeometryNode, GEO_NODE_POINTS_TO_VERTICES, 0, "POINTS_TO_VERTICES", Poin
DefNode(GeometryNode, GEO_NODE_POINTS_TO_VOLUME, def_geo_points_to_volume, "POINTS_TO_VOLUME", PointsToVolume, "Points to Volume", "")
DefNode(GeometryNode, GEO_NODE_PROXIMITY, def_geo_proximity, "PROXIMITY", Proximity, "Geometry Proximity", "")
DefNode(GeometryNode, GEO_NODE_RAYCAST, def_geo_raycast, "RAYCAST", Raycast, "Raycast", "")
-DefNode(GeometryNode, GEO_NODE_REALIZE_INSTANCES, 0, "REALIZE_INSTANCES", RealizeInstances, "Realize Instances", "")
+DefNode(GeometryNode, GEO_NODE_REALIZE_INSTANCES, def_geo_realize_instances, "REALIZE_INSTANCES", RealizeInstances, "Realize Instances", "")
DefNode(GeometryNode, GEO_NODE_REPLACE_MATERIAL, 0, "REPLACE_MATERIAL", ReplaceMaterial, "Replace Material", "")
DefNode(GeometryNode, GEO_NODE_RESAMPLE_CURVE, def_geo_curve_resample, "RESAMPLE_CURVE", ResampleCurve, "Resample Curve", "")
DefNode(GeometryNode, GEO_NODE_REVERSE_CURVE, 0, "REVERSE_CURVE", ReverseCurve, "Reverse Curve", "")
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_align_rotation_to_vector.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_align_rotation_to_vector.cc
index 5a5b0dbee9a..4366c6f9234 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_align_rotation_to_vector.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_align_rotation_to_vector.cc
@@ -201,7 +201,7 @@ static void node_geo_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
- geometry_set = geometry_set_realize_instances(geometry_set);
+ geometry_set = geometry::realize_instances_legacy(geometry_set);
if (geometry_set.has<MeshComponent>()) {
align_rotations_on_component(geometry_set.get_component_for_write<MeshComponent>(), params);
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_clamp.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_clamp.cc
index 40af5b7ec82..7435152a966 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_clamp.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_clamp.cc
@@ -247,7 +247,7 @@ static void node_geo_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
- geometry_set = geometry_set_realize_instances(geometry_set);
+ geometry_set = geometry::realize_instances_legacy(geometry_set);
if (geometry_set.has<MeshComponent>()) {
clamp_attribute(geometry_set.get_component_for_write<MeshComponent>(), params);
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_color_ramp.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_color_ramp.cc
index 66790acb712..4efdf786e4e 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_color_ramp.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_color_ramp.cc
@@ -102,7 +102,7 @@ static void node_geo_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
- geometry_set = geometry_set_realize_instances(geometry_set);
+ geometry_set = geometry::realize_instances_legacy(geometry_set);
if (geometry_set.has<MeshComponent>()) {
execute_on_component(params, geometry_set.get_component_for_write<MeshComponent>());
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_combine_xyz.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_combine_xyz.cc
index f8320c57f92..7ba64c8db2b 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_combine_xyz.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_combine_xyz.cc
@@ -113,7 +113,7 @@ static void node_geo_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
- geometry_set = geometry_set_realize_instances(geometry_set);
+ geometry_set = geometry::realize_instances_legacy(geometry_set);
if (geometry_set.has<MeshComponent>()) {
combine_attributes(geometry_set.get_component_for_write<MeshComponent>(), params);
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_compare.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_compare.cc
index 03ff5327628..8aa1adb0cf3 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_compare.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_compare.cc
@@ -324,7 +324,7 @@ static void node_geo_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
- geometry_set = geometry_set_realize_instances(geometry_set);
+ geometry_set = geometry::realize_instances_legacy(geometry_set);
if (geometry_set.has<MeshComponent>()) {
attribute_compare_calc(geometry_set.get_component_for_write<MeshComponent>(), params);
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_convert.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_convert.cc
index d364ef0bb1d..28c133871f7 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_convert.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_convert.cc
@@ -132,7 +132,7 @@ static void node_geo_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
- geometry_set = geometry_set_realize_instances(geometry_set);
+ geometry_set = geometry::realize_instances_legacy(geometry_set);
const std::string result_name = params.extract_input<std::string>("Result");
const std::string source_name = params.extract_input<std::string>("Attribute");
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_curve_map.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_curve_map.cc
index 8c8b43825ec..0a7b5dd8463 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_curve_map.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_curve_map.cc
@@ -186,7 +186,7 @@ static void node_geo_exec(GeoNodeExecParams params)
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
- geometry_set = geometry_set_realize_instances(geometry_set);
+ geometry_set = geometry::realize_instances_legacy(geometry_set);
if (geometry_set.has<MeshComponent>()) {
execute_on_component(params, geometry_set.get_component_for_write<MeshComponent>());
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_fill.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_fill.cc
index e84c35eeada..a05bcf1bed8 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_fill.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_fill.cc
@@ -131,7 +131,7 @@ static void node_geo_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
- geometry_set = geometry_set_realize_instances(geometry_set);
+ geometry_set = geometry::realize_instances_legacy(geometry_set);
if (geometry_set.has<MeshComponent>()) {
fill_attribute(geometry_set.get_component_for_write<MeshComponent>(), params);
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_math.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_math.cc
index a689a972a8b..e0a829b4100 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_math.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_math.cc
@@ -285,7 +285,7 @@ static void node_geo_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
- geometry_set = geometry_set_realize_instances(geometry_set);
+ geometry_set = geometry::realize_instances_legacy(geometry_set);
if (geometry_set.has<MeshComponent>()) {
attribute_math_calc(geometry_set.get_component_for_write<MeshComponent>(), params);
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_mix.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_mix.cc
index e99c75dbd68..4a110e9690a 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_mix.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_mix.cc
@@ -226,7 +226,7 @@ static void node_geo_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
- geometry_set = geometry_set_realize_instances(geometry_set);
+ geometry_set = geometry::realize_instances_legacy(geometry_set);
if (geometry_set.has<MeshComponent>()) {
attribute_mix_calc(geometry_set.get_component_for_write<MeshComponent>(), params);
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_proximity.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_proximity.cc
index 9090503924e..080bf38a740 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_proximity.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_proximity.cc
@@ -206,11 +206,11 @@ static void node_geo_exec(GeoNodeExecParams params)
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
GeometrySet geometry_set_target = params.extract_input<GeometrySet>("Target");
- geometry_set = geometry_set_realize_instances(geometry_set);
+ geometry_set = geometry::realize_instances_legacy(geometry_set);
/* This isn't required. This node should be rewritten to handle instances
* for the target geometry set. However, the generic BVH API complicates this. */
- geometry_set_target = geometry_set_realize_instances(geometry_set_target);
+ geometry_set_target = geometry::realize_instances_legacy(geometry_set_target);
if (geometry_set.has<MeshComponent>()) {
attribute_calc_proximity(
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_randomize.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_randomize.cc
index 99adb079f91..ab2bc7b379c 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_randomize.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_randomize.cc
@@ -296,7 +296,7 @@ static void node_geo_exec(GeoNodeExecParams params)
const GeometryNodeAttributeRandomizeMode operation =
static_cast<GeometryNodeAttributeRandomizeMode>(storage.operation);
- geometry_set = geometry_set_realize_instances(geometry_set);
+ geometry_set = geometry::realize_instances_legacy(geometry_set);
if (geometry_set.has<MeshComponent>()) {
randomize_attribute_on_component(geometry_set.get_component_for_write<MeshComponent>(),
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_sample_texture.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_sample_texture.cc
index 70fa0af956f..bc18cb32e73 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_sample_texture.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_sample_texture.cc
@@ -104,7 +104,7 @@ static void node_geo_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
- geometry_set = geometry_set_realize_instances(geometry_set);
+ geometry_set = geometry::realize_instances_legacy(geometry_set);
if (geometry_set.has<MeshComponent>()) {
execute_on_component(geometry_set.get_component_for_write<MeshComponent>(), params);
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_separate_xyz.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_separate_xyz.cc
index 04de44446af..c5aac118baf 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_separate_xyz.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_separate_xyz.cc
@@ -134,7 +134,7 @@ static void node_geo_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
- geometry_set = geometry_set_realize_instances(geometry_set);
+ geometry_set = geometry::realize_instances_legacy(geometry_set);
if (geometry_set.has<MeshComponent>()) {
separate_attribute(geometry_set.get_component_for_write<MeshComponent>(), params);
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_transfer.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_transfer.cc
index 7814a259464..686edc80f62 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_transfer.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_transfer.cc
@@ -487,8 +487,8 @@ static void node_geo_exec(GeoNodeExecParams params)
return;
}
- dst_geometry_set = bke::geometry_set_realize_instances(dst_geometry_set);
- src_geometry_set = bke::geometry_set_realize_instances(src_geometry_set);
+ dst_geometry_set = geometry::realize_instances_legacy(dst_geometry_set);
+ src_geometry_set = geometry::realize_instances_legacy(src_geometry_set);
if (dst_geometry_set.has<MeshComponent>()) {
transfer_attribute(params,
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_vector_math.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_vector_math.cc
index 49376947914..68051e81f57 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_vector_math.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_vector_math.cc
@@ -533,7 +533,7 @@ static void node_geo_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
- geometry_set = geometry_set_realize_instances(geometry_set);
+ geometry_set = geometry::realize_instances_legacy(geometry_set);
if (geometry_set.has<MeshComponent>()) {
attribute_vector_math_calc(geometry_set.get_component_for_write<MeshComponent>(), params);
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_vector_rotate.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_vector_rotate.cc
index 309518e58ce..1ef50e69775 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_vector_rotate.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_vector_rotate.cc
@@ -311,7 +311,7 @@ static void node_geo_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
- geometry_set = geometry_set_realize_instances(geometry_set);
+ geometry_set = geometry::realize_instances_legacy(geometry_set);
if (geometry_set.has<MeshComponent>()) {
execute_on_component(params, geometry_set.get_component_for_write<MeshComponent>());
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_endpoints.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_endpoints.cc
index b0b53e8136c..e61dee4bee1 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_endpoints.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_endpoints.cc
@@ -149,7 +149,7 @@ static void node_geo_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
- geometry_set = bke::geometry_set_realize_instances(geometry_set);
+ geometry_set = geometry::realize_instances_legacy(geometry_set);
if (!geometry_set.has_curve()) {
params.set_default_remaining_outputs();
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_reverse.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_reverse.cc
index cfc67e13a8d..7c550495b41 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_reverse.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_reverse.cc
@@ -32,7 +32,7 @@ static void node_declare(NodeDeclarationBuilder &b)
static void node_geo_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve");
- geometry_set = bke::geometry_set_realize_instances(geometry_set);
+ geometry_set = geometry::realize_instances_legacy(geometry_set);
if (!geometry_set.has_curve()) {
params.set_output("Curve", geometry_set);
return;
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_select_by_handle_type.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_select_by_handle_type.cc
index a839c53c855..c702e9ff686 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_select_by_handle_type.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_select_by_handle_type.cc
@@ -101,7 +101,7 @@ static void node_geo_exec(GeoNodeExecParams params)
const GeometryNodeCurveHandleMode mode = (GeometryNodeCurveHandleMode)storage->mode;
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
- geometry_set = bke::geometry_set_realize_instances(geometry_set);
+ geometry_set = geometry::realize_instances_legacy(geometry_set);
CurveComponent &curve_component = geometry_set.get_component_for_write<CurveComponent>();
const CurveEval *curve = curve_component.get_for_read();
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_set_handles.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_set_handles.cc
index fbc56adf44c..1e476d01148 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_set_handles.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_set_handles.cc
@@ -70,7 +70,7 @@ static void node_geo_exec(GeoNodeExecParams params)
const GeometryNodeCurveHandleMode mode = (GeometryNodeCurveHandleMode)node_storage->mode;
GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve");
- geometry_set = bke::geometry_set_realize_instances(geometry_set);
+ geometry_set = geometry::realize_instances_legacy(geometry_set);
if (!geometry_set.has_curve()) {
params.set_output("Curve", geometry_set);
return;
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_spline_type.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_spline_type.cc
index 7c76f97c97f..f3599f4328f 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_spline_type.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_spline_type.cc
@@ -243,7 +243,7 @@ static void node_geo_exec(GeoNodeExecParams params)
const GeometryNodeSplineType output_type = (const GeometryNodeSplineType)storage->spline_type;
GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve");
- geometry_set = bke::geometry_set_realize_instances(geometry_set);
+ geometry_set = geometry::realize_instances_legacy(geometry_set);
if (!geometry_set.has_curve()) {
params.set_output("Curve", geometry_set);
return;
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_subdivide.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_subdivide.cc
index b7cddb927f8..9878402dd35 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_subdivide.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_subdivide.cc
@@ -351,7 +351,7 @@ static void node_geo_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
- geometry_set = bke::geometry_set_realize_instances(geometry_set);
+ geometry_set = geometry::realize_instances_legacy(geometry_set);
if (!geometry_set.has_curve()) {
params.set_output("Geometry", geometry_set);
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_to_points.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_to_points.cc
index a64443261ae..3bd03f3cee0 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_to_points.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_to_points.cc
@@ -296,7 +296,7 @@ static void node_geo_exec(GeoNodeExecParams params)
const GeometryNodeCurveResampleMode mode = (GeometryNodeCurveResampleMode)node_storage.mode;
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
- geometry_set = bke::geometry_set_realize_instances(geometry_set);
+ geometry_set = geometry::realize_instances_legacy(geometry_set);
if (!geometry_set.has_curve()) {
params.set_output("Geometry", GeometrySet());
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_delete_geometry.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_delete_geometry.cc
index 0ae5766a27d..abd75e710ae 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_delete_geometry.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_delete_geometry.cc
@@ -630,7 +630,7 @@ static void delete_mesh_selection(MeshComponent &component,
static void node_geo_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
- geometry_set = bke::geometry_set_realize_instances(geometry_set);
+ geometry_set = geometry::realize_instances_legacy(geometry_set);
const bool invert = params.extract_input<bool>("Invert");
const std::string selection_name = params.extract_input<std::string>("Selection");
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_edge_split.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_edge_split.cc
index 0a510105f5f..c046fda4686 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_edge_split.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_edge_split.cc
@@ -41,7 +41,7 @@ static void node_geo_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
- geometry_set = geometry_set_realize_instances(geometry_set);
+ geometry_set = geometry::realize_instances_legacy(geometry_set);
if (!geometry_set.has_mesh()) {
params.set_output("Geometry", std::move(geometry_set));
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_material_assign.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_material_assign.cc
index 00833e81c29..88e8374f5d6 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_material_assign.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_material_assign.cc
@@ -66,7 +66,7 @@ static void node_geo_exec(GeoNodeExecParams params)
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
- geometry_set = geometry_set_realize_instances(geometry_set);
+ geometry_set = geometry::realize_instances_legacy(geometry_set);
if (geometry_set.has<MeshComponent>()) {
MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>();
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_mesh_to_curve.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_mesh_to_curve.cc
index 8fd7d612021..2ff7410f3f6 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_mesh_to_curve.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_mesh_to_curve.cc
@@ -31,7 +31,7 @@ static void node_geo_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Mesh");
- geometry_set = bke::geometry_set_realize_instances(geometry_set);
+ geometry_set = geometry::realize_instances_legacy(geometry_set);
if (!geometry_set.has_mesh()) {
params.set_default_remaining_outputs();
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_point_instance.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_point_instance.cc
index c8d9a6e9c7c..8915a58feb1 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_point_instance.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_point_instance.cc
@@ -229,7 +229,7 @@ static void node_geo_exec(GeoNodeExecParams params)
/* TODO: This node should be able to instance on the input instances component
* rather than making the entire input geometry set real. */
- geometry_set = geometry_set_realize_instances(geometry_set);
+ geometry_set = geometry::realize_instances_legacy(geometry_set);
const Vector<InstanceReference> possible_references = get_instance_references(params);
if (possible_references.is_empty()) {
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_point_rotate.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_point_rotate.cc
index 7cdd03baa02..a0a7674797a 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_point_rotate.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_point_rotate.cc
@@ -203,7 +203,7 @@ static void node_geo_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
- geometry_set = geometry_set_realize_instances(geometry_set);
+ geometry_set = geometry::realize_instances_legacy(geometry_set);
if (geometry_set.has<MeshComponent>()) {
point_rotate_on_component(geometry_set.get_component_for_write<MeshComponent>(), params);
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_point_scale.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_point_scale.cc
index 4a6f19b8ed2..d38df124979 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_point_scale.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_point_scale.cc
@@ -105,7 +105,7 @@ static void node_geo_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
- geometry_set = geometry_set_realize_instances(geometry_set);
+ geometry_set = geometry::realize_instances_legacy(geometry_set);
if (geometry_set.has<MeshComponent>()) {
execute_on_component(params, geometry_set.get_component_for_write<MeshComponent>());
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_point_separate.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_point_separate.cc
index 5d18abbc828..9260928b311 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_point_separate.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_point_separate.cc
@@ -150,7 +150,7 @@ static void node_geo_exec(GeoNodeExecParams params)
/* TODO: This is not necessary-- the input geometry set can be read only,
* but it must be rewritten to handle instance groups. */
- geometry_set = geometry_set_realize_instances(geometry_set);
+ geometry_set = geometry::realize_instances_legacy(geometry_set);
if (params.lazy_output_is_required("Geometry 1")) {
params.set_output("Geometry 1",
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_point_translate.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_point_translate.cc
index 4e18c650199..c70478182ec 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_point_translate.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_point_translate.cc
@@ -57,7 +57,7 @@ static void node_geo_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
- geometry_set = geometry_set_realize_instances(geometry_set);
+ geometry_set = geometry::realize_instances_legacy(geometry_set);
if (geometry_set.has<MeshComponent>()) {
execute_on_component(params, geometry_set.get_component_for_write<MeshComponent>());
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_points_to_volume.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_points_to_volume.cc
index 2c34d0d781e..ec1ab67b530 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_points_to_volume.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_points_to_volume.cc
@@ -248,7 +248,7 @@ static void node_geo_exec(GeoNodeExecParams params)
GeometrySet geometry_set_out;
/* TODO: Read-only access to instances should be supported here, for now they are made real. */
- geometry_set_in = geometry_set_realize_instances(geometry_set_in);
+ geometry_set_in = geometry::realize_instances_legacy(geometry_set_in);
#ifdef WITH_OPENVDB
initialize_volume_component_from_points(geometry_set_in, geometry_set_out, params);
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_raycast.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_raycast.cc
index 83081933c64..599ffd617a5 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_raycast.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_raycast.cc
@@ -285,8 +285,8 @@ static void node_geo_exec(GeoNodeExecParams params)
const Array<std::string> hit_names = {params.extract_input<std::string>("Target Attribute")};
const Array<std::string> hit_output_names = {params.extract_input<std::string>("Hit Attribute")};
- geometry_set = bke::geometry_set_realize_instances(geometry_set);
- target_geometry_set = bke::geometry_set_realize_instances(target_geometry_set);
+ geometry_set = geometry::realize_instances_legacy(geometry_set);
+ target_geometry_set = geometry::realize_instances_legacy(target_geometry_set);
static const Array<GeometryComponentType> types = {
GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_CURVE};
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_select_by_material.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_select_by_material.cc
index 0119570b199..b61ba5ff67e 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_select_by_material.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_select_by_material.cc
@@ -60,7 +60,7 @@ static void node_geo_exec(GeoNodeExecParams params)
const std::string selection_name = params.extract_input<std::string>("Selection");
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
- geometry_set = geometry_set_realize_instances(geometry_set);
+ geometry_set = geometry::realize_instances_legacy(geometry_set);
if (geometry_set.has<MeshComponent>()) {
MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>();
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_subdivision_surface.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_subdivision_surface.cc
index dd9c26f3790..819ffb2c20c 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_subdivision_surface.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_subdivision_surface.cc
@@ -58,7 +58,7 @@ static void node_geo_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
- geometry_set = geometry_set_realize_instances(geometry_set);
+ geometry_set = geometry::realize_instances_legacy(geometry_set);
if (!geometry_set.has_mesh()) {
params.set_output("Geometry", geometry_set);
diff --git a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc
index 48bec4cf9da..bf7a9f49829 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc
@@ -24,6 +24,8 @@
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
+#include "GEO_realize_instances.hh"
+
#include "node_geometry_util.hh"
namespace blender::nodes::node_geo_join_geometry_cc {
@@ -190,7 +192,10 @@ static void join_component_type(Span<GeometrySet> src_geometry_sets, GeometrySet
instances.add_instance(handle, float4x4::identity());
}
- GeometrySet joined_components = bke::geometry_set_realize_instances(instances_geometry_set);
+ geometry::RealizeInstancesOptions options;
+ options.keep_original_ids = true;
+ options.realize_instance_attributes = false;
+ GeometrySet joined_components = geometry::realize_instances(instances_geometry_set, options);
result.add(joined_components.get_component_for_write<Component>());
}
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_realize_instances.cc b/source/blender/nodes/geometry/nodes/node_geo_realize_instances.cc
index 5b161e66d97..fad35389823 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_realize_instances.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_realize_instances.cc
@@ -16,6 +16,8 @@
#include "node_geometry_util.hh"
+#include "GEO_realize_instances.hh"
+
#include "UI_interface.h"
#include "UI_resources.h"
@@ -27,10 +29,20 @@ static void node_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Geometry>(N_("Geometry"));
}
+static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+{
+ uiItemR(layout, ptr, "legacy_behavior", 0, nullptr, ICON_NONE);
+}
+
static void node_geo_exec(GeoNodeExecParams params)
{
+ const bool legacy_behavior = params.node().custom1 & GEO_NODE_REALIZE_INSTANCES_LEGACY_BEHAVIOR;
+
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
- geometry_set = bke::geometry_set_realize_instances(geometry_set);
+ geometry::RealizeInstancesOptions options;
+ options.keep_original_ids = legacy_behavior;
+ options.realize_instance_attributes = !legacy_behavior;
+ geometry_set = geometry::realize_instances(geometry_set, options);
params.set_output("Geometry", std::move(geometry_set));
}
@@ -45,6 +57,7 @@ void register_node_type_geo_realize_instances()
geo_node_type_base(
&ntype, GEO_NODE_REALIZE_INSTANCES, "Realize Instances", NODE_CLASS_GEOMETRY, 0);
ntype.declare = file_ns::node_declare;
+ ntype.draw_buttons_ex = file_ns::node_layout;
ntype.geometry_node_execute = file_ns::node_geo_exec;
nodeRegisterType(&ntype);
}