diff options
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); } |