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:
-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);
}