From e5425b566d0f25f60b5895c4025c183fd67c7d9c Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Mon, 17 Oct 2022 11:39:40 +0200 Subject: Geometry Nodes: separate Instances from InstancesComponent This makes instance handling more consistent with all the other geometry component types. For example, `MeshComponent` contains a `Mesh *` and now `InstancesComponent` has a `Instances *`. Differential Revision: https://developer.blender.org/D16137 --- source/blender/blenkernel/BKE_geometry_fields.hh | 13 +- source/blender/blenkernel/BKE_geometry_set.hh | 251 ++------------ source/blender/blenkernel/BKE_instances.hh | 270 +++++++++++++++ source/blender/blenkernel/CMakeLists.txt | 2 + .../intern/geometry_component_instances.cc | 385 ++++----------------- .../blender/blenkernel/intern/geometry_fields.cc | 16 +- source/blender/blenkernel/intern/geometry_set.cc | 58 +++- .../blenkernel/intern/geometry_set_instances.cc | 45 ++- source/blender/blenkernel/intern/instances.cc | 348 +++++++++++++++++++ source/blender/blenkernel/intern/object_dupli.cc | 17 +- .../space_spreadsheet/spreadsheet_column.cc | 3 +- .../spreadsheet_data_source_geometry.cc | 49 +-- .../space_spreadsheet/spreadsheet_layout.cc | 13 +- .../space_spreadsheet/spreadsheet_row_filter.cc | 16 +- .../blender/geometry/intern/realize_instances.cc | 46 +-- .../geometry/nodes/node_geo_collection_info.cc | 18 +- .../geometry/nodes/node_geo_delete_geometry.cc | 7 +- .../geometry/nodes/node_geo_duplicate_elements.cc | 32 +- .../nodes/node_geo_geometry_to_instance.cc | 12 +- .../nodes/node_geo_input_instance_rotation.cc | 9 +- .../nodes/node_geo_input_instance_scale.cc | 9 +- .../geometry/nodes/node_geo_instance_on_points.cc | 42 ++- .../geometry/nodes/node_geo_instances_to_points.cc | 5 +- .../nodes/geometry/nodes/node_geo_join_geometry.cc | 35 +- .../nodes/geometry/nodes/node_geo_object_info.cc | 10 +- .../geometry/nodes/node_geo_rotate_instances.cc | 17 +- .../geometry/nodes/node_geo_scale_instances.cc | 17 +- .../geometry/nodes/node_geo_string_to_curves.cc | 24 +- .../nodes/geometry/nodes/node_geo_transform.cc | 19 +- .../geometry/nodes/node_geo_translate_instances.cc | 19 +- source/blender/nodes/intern/geometry_nodes_log.cc | 2 +- 31 files changed, 1027 insertions(+), 782 deletions(-) create mode 100644 source/blender/blenkernel/BKE_instances.hh create mode 100644 source/blender/blenkernel/intern/instances.cc (limited to 'source') diff --git a/source/blender/blenkernel/BKE_geometry_fields.hh b/source/blender/blenkernel/BKE_geometry_fields.hh index 988e0017f04..2eef67dba98 100644 --- a/source/blender/blenkernel/BKE_geometry_fields.hh +++ b/source/blender/blenkernel/BKE_geometry_fields.hh @@ -75,14 +75,14 @@ class PointCloudFieldContext : public fn::FieldContext { class InstancesFieldContext : public fn::FieldContext { private: - const InstancesComponent &instances_; + const Instances &instances_; public: - InstancesFieldContext(const InstancesComponent &instances) : instances_(instances) + InstancesFieldContext(const Instances &instances) : instances_(instances) { } - const InstancesComponent &instances() const + const Instances &instances() const { return instances_; } @@ -128,13 +128,13 @@ class GeometryFieldContext : public fn::FieldContext { const Mesh *mesh() const; const CurvesGeometry *curves() const; const PointCloud *pointcloud() const; - const InstancesComponent *instances() const; + const Instances *instances() const; private: GeometryFieldContext(const Mesh &mesh, eAttrDomain domain); GeometryFieldContext(const CurvesGeometry &curves, eAttrDomain domain); GeometryFieldContext(const PointCloud &points); - GeometryFieldContext(const InstancesComponent &instances); + GeometryFieldContext(const Instances &instances); }; class GeometryFieldInput : public fn::FieldInput { @@ -187,8 +187,7 @@ class InstancesFieldInput : public fn::FieldInput { GVArray get_varray_for_context(const fn::FieldContext &context, IndexMask mask, ResourceScope &scope) const override; - virtual GVArray get_varray_for_context(const InstancesComponent &instances, - IndexMask mask) const = 0; + virtual GVArray get_varray_for_context(const Instances &instances, IndexMask mask) const = 0; }; class AttributeFieldInput : public GeometryFieldInput { diff --git a/source/blender/blenkernel/BKE_geometry_set.hh b/source/blender/blenkernel/BKE_geometry_set.hh index 2ef9556afc7..b488806c8e7 100644 --- a/source/blender/blenkernel/BKE_geometry_set.hh +++ b/source/blender/blenkernel/BKE_geometry_set.hh @@ -43,6 +43,7 @@ enum class GeometryOwnershipType { namespace blender::bke { class ComponentAttributeProviders; class CurvesEditHints; +class Instances; } // namespace blender::bke class GeometryComponent; @@ -246,6 +247,12 @@ struct GeometrySet { */ static GeometrySet create_with_curves( Curves *curves, GeometryOwnershipType ownership = GeometryOwnershipType::Owned); + /** + * Create a new geometry set that only contains the given instances. + */ + static GeometrySet create_with_instances( + blender::bke::Instances *instances, + GeometryOwnershipType ownership = GeometryOwnershipType::Owned); /* Utility methods for access. */ /** @@ -293,6 +300,10 @@ struct GeometrySet { * Returns a read-only curves data-block or null. */ const Curves *get_curves_for_read() const; + /** + * Returns read-only instances or null. + */ + const blender::bke::Instances *get_instances_for_read() const; /** * Returns read-only curve edit hints or null. */ @@ -314,6 +325,10 @@ struct GeometrySet { * Returns a mutable curves data-block or null. No ownership is transferred. */ Curves *get_curves_for_write(); + /** + * Returns mutable instances or null. No ownership is transferred. + */ + blender::bke::Instances *get_instances_for_write(); /** * Returns mutable curve edit hints or null. */ @@ -339,6 +354,11 @@ struct GeometrySet { */ void replace_curves(Curves *curves, GeometryOwnershipType ownership = GeometryOwnershipType::Owned); + /** + * Clear the existing instances and replace them with the given one. + */ + void replace_instances(blender::bke::Instances *instances, + GeometryOwnershipType ownership = GeometryOwnershipType::Owned); private: /** @@ -515,244 +535,35 @@ class CurveComponent : public GeometryComponent { }; /** - * Holds a reference to conceptually unique geometry or a pointer to object/collection data - * that is instanced with a transform in #InstancesComponent. - */ -class InstanceReference { - public: - enum class Type { - /** - * An empty instance. This allows an `InstanceReference` to be default constructed without - * being in an invalid state. There might also be other use cases that we haven't explored much - * yet (such as changing the instance later on, and "disabling" some instances). - */ - None, - Object, - Collection, - GeometrySet, - }; - - private: - Type type_ = Type::None; - /** Depending on the type this is either null, an Object or Collection pointer. */ - void *data_ = nullptr; - std::unique_ptr geometry_set_; - - public: - InstanceReference() = default; - - InstanceReference(Object &object) : type_(Type::Object), data_(&object) - { - } - - InstanceReference(Collection &collection) : type_(Type::Collection), data_(&collection) - { - } - - InstanceReference(GeometrySet geometry_set) - : type_(Type::GeometrySet), - geometry_set_(std::make_unique(std::move(geometry_set))) - { - } - - InstanceReference(const InstanceReference &other) : type_(other.type_), data_(other.data_) - { - if (other.geometry_set_) { - geometry_set_ = std::make_unique(*other.geometry_set_); - } - } - - InstanceReference(InstanceReference &&other) - : type_(other.type_), data_(other.data_), geometry_set_(std::move(other.geometry_set_)) - { - other.type_ = Type::None; - other.data_ = nullptr; - } - - InstanceReference &operator=(const InstanceReference &other) - { - if (this == &other) { - return *this; - } - this->~InstanceReference(); - new (this) InstanceReference(other); - return *this; - } - - InstanceReference &operator=(InstanceReference &&other) - { - if (this == &other) { - return *this; - } - this->~InstanceReference(); - new (this) InstanceReference(std::move(other)); - return *this; - } - - Type type() const - { - return type_; - } - - Object &object() const - { - BLI_assert(type_ == Type::Object); - return *(Object *)data_; - } - - Collection &collection() const - { - BLI_assert(type_ == Type::Collection); - return *(Collection *)data_; - } - - const GeometrySet &geometry_set() const - { - BLI_assert(type_ == Type::GeometrySet); - return *geometry_set_; - } - - bool owns_direct_data() const - { - if (type_ != Type::GeometrySet) { - /* The object and collection instances are not direct data. */ - return true; - } - return geometry_set_->owns_direct_data(); - } - - void ensure_owns_direct_data() - { - if (type_ != Type::GeometrySet) { - return; - } - geometry_set_->ensure_owns_direct_data(); - } - - uint64_t hash() const - { - return blender::get_default_hash_2(data_, geometry_set_.get()); - } - - friend bool operator==(const InstanceReference &a, const InstanceReference &b) - { - return a.data_ == b.data_ && a.geometry_set_.get() == b.geometry_set_.get(); - } -}; - -/** - * A geometry component that stores instances. The instance data can be any type described by - * #InstanceReference. Geometry instances can even contain instances themselves, for nested - * instancing. Each instance has an index into an array of unique instance data, and a transform. - * The component can also store generic attributes for each instance. - * - * The component works differently from other geometry components in that it stores - * data about instancing directly, rather than owning a pointer to a separate data structure. - * - * This component is not responsible for handling the interface to a render engine, or other - * areas that work with all visible geometry, that is handled by the dependency graph iterator - * (see `DEG_depsgraph_query.h`). + * A geometry component that stores #Instances. */ class InstancesComponent : public GeometryComponent { private: - /** - * Indexed set containing information about the data that is instanced. - * Actual instances store an index ("handle") into this set. - */ - blender::VectorSet references_; - - /** Index into `references_`. Determines what data is instanced. */ - blender::Vector instance_reference_handles_; - /** Transformation of the instances. */ - blender::Vector instance_transforms_; - - /* These almost unique ids are generated based on the `id` attribute, which might not contain - * unique ids at all. They are *almost* unique, because under certain very unlikely - * circumstances, they are not unique. Code using these ids should not crash when they are not - * unique but can generally expect them to be unique. */ - mutable std::mutex almost_unique_ids_mutex_; - mutable blender::Array almost_unique_ids_; - - blender::bke::CustomDataAttributes attributes_; + blender::bke::Instances *instances_ = nullptr; + GeometryOwnershipType ownership_ = GeometryOwnershipType::Owned; public: InstancesComponent(); - ~InstancesComponent() = default; + ~InstancesComponent(); GeometryComponent *copy() const override; void clear(); - void reserve(int min_capacity); - /** - * Resize the transform, handles, and attributes to the specified capacity. - * - * \note This function should be used carefully, only when it's guaranteed - * that the data will be filled. - */ - void resize(int capacity); + const blender::bke::Instances *get_for_read() const; + blender::bke::Instances *get_for_write(); - /** - * Returns a handle for the given reference. - * If the reference exists already, the handle of the existing reference is returned. - * Otherwise a new handle is added. - */ - int add_reference(const InstanceReference &reference); - /** - * Add a reference to the instance reference with an index specified by the #instance_handle - * argument. For adding many instances, using #resize and accessing the transform array directly - * is preferred. - */ - void add_instance(int instance_handle, const blender::float4x4 &transform); - - blender::Span references() const; - void remove_unused_references(); - - /** - * If references have a collection or object type, convert them into geometry instances - * recursively. After that, the geometry sets can be edited. There may still be instances of - * other types of they can't be converted to geometry sets. - */ - void ensure_geometry_instances(); - /** - * With write access to the instances component, the data in the instanced geometry sets can be - * changed. This is a function on the component rather than each reference to ensure `const` - * correctness for that reason. - */ - GeometrySet &geometry_set_from_reference(int reference_index); - - blender::Span instance_reference_handles() const; - blender::MutableSpan instance_reference_handles(); - blender::MutableSpan instance_transforms(); - blender::Span instance_transforms() const; - - int instances_num() const; - int references_num() const; - - /** - * Remove the indices that are not contained in the mask input, and remove unused instance - * references afterwards. - */ - void remove_instances(const blender::IndexMask mask); - - blender::Span almost_unique_ids() const; - - blender::bke::CustomDataAttributes &instance_attributes(); - const blender::bke::CustomDataAttributes &instance_attributes() const; - - std::optional attributes() const final; - std::optional attributes_for_write() final; - - void foreach_referenced_geometry( - blender::FunctionRef callback) const; + void replace(blender::bke::Instances *instances, + GeometryOwnershipType ownership = GeometryOwnershipType::Owned); bool is_empty() const final; bool owns_direct_data() const override; void ensure_owns_direct_data() override; - static constexpr inline GeometryComponentType static_type = GEO_COMPONENT_TYPE_INSTANCES; + std::optional attributes() const final; + std::optional attributes_for_write() final; - private: + static constexpr inline GeometryComponentType static_type = GEO_COMPONENT_TYPE_INSTANCES; }; /** diff --git a/source/blender/blenkernel/BKE_instances.hh b/source/blender/blenkernel/BKE_instances.hh new file mode 100644 index 00000000000..69b6f944838 --- /dev/null +++ b/source/blender/blenkernel/BKE_instances.hh @@ -0,0 +1,270 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +/** \file + * \ingroup bke + * + * #Instances is a container for geometry instances. It fullfills some key requirements: + * - Support nested instances. + * - Support instance attributes. + * - Support referencing different kinds of instances (objects, collections, geometry sets). + * - Support efficiently iterating over the instanced geometries, i.e. without have to iterate over + * all instances. + * + * #Instances has an ordered set of #InstanceReference. An #InstanceReference contains information + * about a particular instanced geometry. Each #InstanceReference has a handle (integer index) + * which is then stored per instance. Many instances can use the same #InstanceReference. + */ + +#include + +#include "BLI_float4x4.hh" +#include "BLI_vector.hh" +#include "BLI_vector_set.hh" + +#include "BKE_attribute.hh" + +class GeometrySet; +struct Object; +struct Collection; + +namespace blender::bke { + +/** + * Holds a reference to conceptually unique geometry or a pointer to object/collection data + * that is instanced with a transform in #Instances. + */ +class InstanceReference { + public: + enum class Type { + /** + * An empty instance. This allows an `InstanceReference` to be default constructed without + * being in an invalid state. There might also be other use cases that we haven't explored + * much yet (such as changing the instance later on, and "disabling" some instances). + */ + None, + Object, + Collection, + GeometrySet, + }; + + private: + Type type_ = Type::None; + /** Depending on the type this is either null, an Object or Collection pointer. */ + void *data_ = nullptr; + std::unique_ptr geometry_set_; + + public: + InstanceReference() = default; + InstanceReference(Object &object); + InstanceReference(Collection &collection); + InstanceReference(GeometrySet geometry_set); + + InstanceReference(const InstanceReference &other); + InstanceReference(InstanceReference &&other); + + InstanceReference &operator=(const InstanceReference &other); + InstanceReference &operator=(InstanceReference &&other); + + Type type() const; + Object &object() const; + Collection &collection() const; + const GeometrySet &geometry_set() const; + + bool owns_direct_data() const; + void ensure_owns_direct_data(); + + uint64_t hash() const; + friend bool operator==(const InstanceReference &a, const InstanceReference &b); +}; + +class Instances { + private: + /** + * Indexed set containing information about the data that is instanced. + * Actual instances store an index ("handle") into this set. + */ + blender::VectorSet references_; + + /** Indices into `references_`. Determines what data is instanced. */ + blender::Vector reference_handles_; + /** Transformation of the instances. */ + blender::Vector transforms_; + + /* These almost unique ids are generated based on the `id` attribute, which might not contain + * unique ids at all. They are *almost* unique, because under certain very unlikely + * circumstances, they are not unique. Code using these ids should not crash when they are not + * unique but can generally expect them to be unique. */ + mutable std::mutex almost_unique_ids_mutex_; + mutable blender::Array almost_unique_ids_; + + CustomDataAttributes attributes_; + + public: + Instances() = default; + Instances(const Instances &other); + + void reserve(int min_capacity); + /** + * Resize the transform, handles, and attributes to the specified capacity. + * + * \note This function should be used carefully, only when it's guaranteed + * that the data will be filled. + */ + void resize(int capacity); + + /** + * Returns a handle for the given reference. + * If the reference exists already, the handle of the existing reference is returned. + * Otherwise a new handle is added. + */ + int add_reference(const InstanceReference &reference); + /** + * Add a reference to the instance reference with an index specified by the #instance_handle + * argument. For adding many instances, using #resize and accessing the transform array + * directly is preferred. + */ + void add_instance(int instance_handle, const blender::float4x4 &transform); + + blender::Span references() const; + void remove_unused_references(); + + /** + * If references have a collection or object type, convert them into geometry instances + * recursively. After that, the geometry sets can be edited. There may still be instances of + * other types of they can't be converted to geometry sets. + */ + void ensure_geometry_instances(); + /** + * With write access to the instances component, the data in the instanced geometry sets can be + * changed. This is a function on the component rather than each reference to ensure `const` + * correctness for that reason. + */ + GeometrySet &geometry_set_from_reference(int reference_index); + + blender::Span reference_handles() const; + blender::MutableSpan reference_handles(); + blender::MutableSpan transforms(); + blender::Span transforms() const; + + int instances_num() const; + int references_num() const; + + /** + * Remove the indices that are not contained in the mask input, and remove unused instance + * references afterwards. + */ + void remove(const blender::IndexMask mask); + /** + * Get an id for every instance. These can be used for e.g. motion blur. + */ + blender::Span almost_unique_ids() const; + + blender::bke::AttributeAccessor attributes() const; + blender::bke::MutableAttributeAccessor attributes_for_write(); + + CustomDataAttributes &custom_data_attributes(); + const CustomDataAttributes &custom_data_attributes() const; + + void foreach_referenced_geometry( + blender::FunctionRef callback) const; + + bool owns_direct_data() const; + void ensure_owns_direct_data(); +}; + +/* -------------------------------------------------------------------- */ +/** \name #InstanceReference Inline Methods + * \{ */ + +inline InstanceReference::InstanceReference(Object &object) : type_(Type::Object), data_(&object) +{ +} + +inline InstanceReference::InstanceReference(Collection &collection) + : type_(Type::Collection), data_(&collection) +{ +} + +inline InstanceReference::InstanceReference(const InstanceReference &other) + : type_(other.type_), data_(other.data_) +{ + if (other.geometry_set_) { + geometry_set_ = std::make_unique(*other.geometry_set_); + } +} + +inline InstanceReference::InstanceReference(InstanceReference &&other) + : type_(other.type_), data_(other.data_), geometry_set_(std::move(other.geometry_set_)) +{ + other.type_ = Type::None; + other.data_ = nullptr; +} + +inline InstanceReference &InstanceReference::operator=(const InstanceReference &other) +{ + if (this == &other) { + return *this; + } + this->~InstanceReference(); + new (this) InstanceReference(other); + return *this; +} + +inline InstanceReference &InstanceReference::operator=(InstanceReference &&other) +{ + if (this == &other) { + return *this; + } + this->~InstanceReference(); + new (this) InstanceReference(std::move(other)); + return *this; +} + +inline InstanceReference::Type InstanceReference::type() const +{ + return type_; +} + +inline Object &InstanceReference::object() const +{ + BLI_assert(type_ == Type::Object); + return *(Object *)data_; +} + +inline Collection &InstanceReference::collection() const +{ + BLI_assert(type_ == Type::Collection); + return *(Collection *)data_; +} + +inline const GeometrySet &InstanceReference::geometry_set() const +{ + BLI_assert(type_ == Type::GeometrySet); + return *geometry_set_; +} + +inline CustomDataAttributes &Instances::custom_data_attributes() +{ + return attributes_; +} + +inline const CustomDataAttributes &Instances::custom_data_attributes() const +{ + return attributes_; +} + +inline uint64_t InstanceReference::hash() const +{ + return blender::get_default_hash_2(data_, geometry_set_.get()); +} + +inline bool operator==(const InstanceReference &a, const InstanceReference &b) +{ + return a.data_ == b.data_ && a.geometry_set_.get() == b.geometry_set_.get(); +} + +/** \} */ + +} // namespace blender::bke diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index 97bdff217d0..7d43fa7e6af 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -163,6 +163,7 @@ set(SRC intern/image_gpu.cc intern/image_partial_update.cc intern/image_save.cc + intern/instances.cc intern/ipo.c intern/kelvinlet.c intern/key.c @@ -398,6 +399,7 @@ set(SRC BKE_image_partial_update.hh BKE_image_save.h BKE_image_wrappers.hh + BKE_instances.hh BKE_ipo.h BKE_kelvinlet.h BKE_key.h diff --git a/source/blender/blenkernel/intern/geometry_component_instances.cc b/source/blender/blenkernel/intern/geometry_component_instances.cc index 0b5f7cbf902..be1d9524509 100644 --- a/source/blender/blenkernel/intern/geometry_component_instances.cc +++ b/source/blender/blenkernel/intern/geometry_component_instances.cc @@ -16,6 +16,7 @@ #include "BKE_attribute_math.hh" #include "BKE_geometry_set.hh" #include "BKE_geometry_set_instances.hh" +#include "BKE_instances.hh" #include "attribute_access_intern.hh" @@ -29,8 +30,8 @@ using blender::MutableSpan; using blender::Set; using blender::Span; using blender::VectorSet; - -BLI_CPP_TYPE_MAKE(InstanceReference, InstanceReference, CPPTypeFlags::None) +using blender::bke::InstanceReference; +using blender::bke::Instances; /* -------------------------------------------------------------------- */ /** \name Geometry Component Implementation @@ -40,339 +41,76 @@ InstancesComponent::InstancesComponent() : GeometryComponent(GEO_COMPONENT_TYPE_ { } -GeometryComponent *InstancesComponent::copy() const -{ - InstancesComponent *new_component = new InstancesComponent(); - new_component->instance_reference_handles_ = instance_reference_handles_; - new_component->instance_transforms_ = instance_transforms_; - new_component->references_ = references_; - new_component->attributes_ = attributes_; - return new_component; -} - -void InstancesComponent::reserve(int min_capacity) +InstancesComponent::~InstancesComponent() { - instance_reference_handles_.reserve(min_capacity); - instance_transforms_.reserve(min_capacity); - attributes_.reallocate(min_capacity); + this->clear(); } -void InstancesComponent::resize(int capacity) +GeometryComponent *InstancesComponent::copy() const { - instance_reference_handles_.resize(capacity); - instance_transforms_.resize(capacity); - attributes_.reallocate(capacity); + InstancesComponent *new_component = new InstancesComponent(); + if (instances_ != nullptr) { + new_component->instances_ = new Instances(*instances_); + new_component->ownership_ = GeometryOwnershipType::Owned; + } + return new_component; } void InstancesComponent::clear() { - instance_reference_handles_.clear(); - instance_transforms_.clear(); - attributes_.clear(); - references_.clear(); -} - -void InstancesComponent::add_instance(const int instance_handle, const float4x4 &transform) -{ - BLI_assert(instance_handle >= 0); - BLI_assert(instance_handle < references_.size()); - instance_reference_handles_.append(instance_handle); - instance_transforms_.append(transform); - attributes_.reallocate(this->instances_num()); -} - -blender::Span InstancesComponent::instance_reference_handles() const -{ - return instance_reference_handles_; -} - -blender::MutableSpan InstancesComponent::instance_reference_handles() -{ - return instance_reference_handles_; -} - -blender::MutableSpan InstancesComponent::instance_transforms() -{ - return instance_transforms_; -} -blender::Span InstancesComponent::instance_transforms() const -{ - return instance_transforms_; -} - -GeometrySet &InstancesComponent::geometry_set_from_reference(const int reference_index) -{ - /* If this assert fails, it means #ensure_geometry_instances must be called first or that the - * reference can't be converted to a geometry set. */ - BLI_assert(references_[reference_index].type() == InstanceReference::Type::GeometrySet); - - /* The const cast is okay because the instance's hash in the set - * is not changed by adjusting the data inside the geometry set. */ - return const_cast(references_[reference_index].geometry_set()); -} - -int InstancesComponent::add_reference(const InstanceReference &reference) -{ - return references_.index_of_or_add_as(reference); -} - -blender::Span InstancesComponent::references() const -{ - return references_; -} - -template -static void copy_data_based_on_mask(Span src, MutableSpan dst, IndexMask mask) -{ - BLI_assert(src.data() != dst.data()); - using namespace blender; - threading::parallel_for(mask.index_range(), 1024, [&](IndexRange range) { - for (const int i : range) { - dst[i] = src[mask[i]]; - } - }); -} - -void InstancesComponent::remove_instances(const IndexMask mask) -{ - using namespace blender; - if (mask.is_range() && mask.as_range().start() == 0) { - /* Deleting from the end of the array can be much faster since no data has to be shifted. */ - this->resize(mask.size()); - this->remove_unused_references(); - return; + BLI_assert(this->is_mutable()); + if (ownership_ == GeometryOwnershipType::Owned) { + delete instances_; } - - Vector new_handles(mask.size()); - copy_data_based_on_mask(this->instance_reference_handles(), new_handles, mask); - instance_reference_handles_ = std::move(new_handles); - Vector new_transforms(mask.size()); - copy_data_based_on_mask(this->instance_transforms(), new_transforms, mask); - instance_transforms_ = std::move(new_transforms); - - const bke::CustomDataAttributes &src_attributes = attributes_; - - bke::CustomDataAttributes dst_attributes; - dst_attributes.reallocate(mask.size()); - - src_attributes.foreach_attribute( - [&](const bke::AttributeIDRef &id, const bke::AttributeMetaData &meta_data) { - if (!id.should_be_kept()) { - return true; - } - - GSpan src = *src_attributes.get_for_read(id); - dst_attributes.create(id, meta_data.data_type); - GMutableSpan dst = *dst_attributes.get_for_write(id); - - attribute_math::convert_to_static_type(src.type(), [&](auto dummy) { - using T = decltype(dummy); - copy_data_based_on_mask(src.typed(), dst.typed(), mask); - }); - return true; - }, - ATTR_DOMAIN_INSTANCE); - - attributes_ = std::move(dst_attributes); - this->remove_unused_references(); + instances_ = nullptr; } -void InstancesComponent::remove_unused_references() +bool InstancesComponent::is_empty() const { - using namespace blender; - using namespace blender::bke; - - const int tot_instances = this->instances_num(); - const int tot_references_before = references_.size(); - - if (tot_instances == 0) { - /* If there are no instances, no reference is needed. */ - references_.clear(); - return; - } - if (tot_references_before == 1) { - /* There is only one reference and at least one instance. So the only existing reference is - * used. Nothing to do here. */ - return; - } - - Array usage_by_handle(tot_references_before, false); - std::mutex mutex; - - /* Loop over all instances to see which references are used. */ - threading::parallel_for(IndexRange(tot_instances), 1000, [&](IndexRange range) { - /* Use local counter to avoid lock contention. */ - Array local_usage_by_handle(tot_references_before, false); - - for (const int i : range) { - const int handle = instance_reference_handles_[i]; - BLI_assert(handle >= 0 && handle < tot_references_before); - local_usage_by_handle[handle] = true; - } - - std::lock_guard lock{mutex}; - for (const int i : IndexRange(tot_references_before)) { - usage_by_handle[i] |= local_usage_by_handle[i]; - } - }); - - if (!usage_by_handle.as_span().contains(false)) { - /* All references are used. */ - return; - } - - /* Create new references and a mapping for the handles. */ - Vector handle_mapping; - VectorSet new_references; - int next_new_handle = 0; - bool handles_have_to_be_updated = false; - for (const int old_handle : IndexRange(tot_references_before)) { - if (!usage_by_handle[old_handle]) { - /* Add some dummy value. It won't be read again. */ - handle_mapping.append(-1); - } - else { - const InstanceReference &reference = references_[old_handle]; - handle_mapping.append(next_new_handle); - new_references.add_new(reference); - if (old_handle != next_new_handle) { - handles_have_to_be_updated = true; - } - next_new_handle++; + if (instances_ != nullptr) { + if (instances_->instances_num() > 0) { + return false; } } - references_ = new_references; - - if (!handles_have_to_be_updated) { - /* All remaining handles are the same as before, so they don't have to be updated. This happens - * when unused handles are only at the end. */ - return; - } - - /* Update handles of instances. */ - threading::parallel_for(IndexRange(tot_instances), 1000, [&](IndexRange range) { - for (const int i : range) { - instance_reference_handles_[i] = handle_mapping[instance_reference_handles_[i]]; - } - }); -} - -int InstancesComponent::instances_num() const -{ - return instance_transforms_.size(); -} - -int InstancesComponent::references_num() const -{ - return references_.size(); -} - -bool InstancesComponent::is_empty() const -{ - return this->instance_reference_handles_.size() == 0; + return true; } bool InstancesComponent::owns_direct_data() const { - for (const InstanceReference &reference : references_) { - if (!reference.owns_direct_data()) { - return false; - } + if (instances_ != nullptr) { + return instances_->owns_direct_data(); } return true; } void InstancesComponent::ensure_owns_direct_data() { - BLI_assert(this->is_mutable()); - for (const InstanceReference &const_reference : references_) { - /* Const cast is fine because we are not changing anything that would change the hash of the - * reference. */ - InstanceReference &reference = const_cast(const_reference); - reference.ensure_owns_direct_data(); + if (instances_ != nullptr) { + instances_->ensure_owns_direct_data(); } } -static blender::Array generate_unique_instance_ids(Span original_ids) +const blender::bke::Instances *InstancesComponent::get_for_read() const { - using namespace blender; - Array unique_ids(original_ids.size()); - - Set used_unique_ids; - used_unique_ids.reserve(original_ids.size()); - Vector instances_with_id_collision; - for (const int instance_index : original_ids.index_range()) { - const int original_id = original_ids[instance_index]; - if (used_unique_ids.add(original_id)) { - /* The original id has not been used by another instance yet. */ - unique_ids[instance_index] = original_id; - } - else { - /* The original id of this instance collided with a previous instance, it needs to be looked - * at again in a second pass. Don't generate a new random id here, because this might collide - * with other existing ids. */ - instances_with_id_collision.append(instance_index); - } - } - - Map generator_by_original_id; - for (const int instance_index : instances_with_id_collision) { - const int original_id = original_ids[instance_index]; - RandomNumberGenerator &rng = generator_by_original_id.lookup_or_add_cb(original_id, [&]() { - RandomNumberGenerator rng; - rng.seed_random(original_id); - return rng; - }); - - const int max_iteration = 100; - for (int iteration = 0;; iteration++) { - /* Try generating random numbers until an unused one has been found. */ - const int random_id = rng.get_int32(); - if (used_unique_ids.add(random_id)) { - /* This random id is not used by another instance. */ - unique_ids[instance_index] = random_id; - break; - } - if (iteration == max_iteration) { - /* It seems to be very unlikely that we ever run into this case (assuming there are less - * than 2^30 instances). However, if that happens, it's better to use an id that is not - * unique than to be stuck in an infinite loop. */ - unique_ids[instance_index] = original_id; - break; - } - } - } - - return unique_ids; + return instances_; } -blender::Span InstancesComponent::almost_unique_ids() const +blender::bke::Instances *InstancesComponent::get_for_write() { - std::lock_guard lock(almost_unique_ids_mutex_); - std::optional instance_ids_gspan = attributes_.get_for_read("id"); - if (instance_ids_gspan) { - Span instance_ids = instance_ids_gspan->typed(); - if (almost_unique_ids_.size() != instance_ids.size()) { - almost_unique_ids_ = generate_unique_instance_ids(instance_ids); - } - } - else { - almost_unique_ids_.reinitialize(this->instances_num()); - for (const int i : almost_unique_ids_.index_range()) { - almost_unique_ids_[i] = i; - } + BLI_assert(this->is_mutable()); + if (ownership_ == GeometryOwnershipType::ReadOnly) { + instances_ = new Instances(*instances_); + ownership_ = GeometryOwnershipType::Owned; } - return almost_unique_ids_; + return instances_; } -blender::bke::CustomDataAttributes &InstancesComponent::instance_attributes() +void InstancesComponent::replace(Instances *instances, GeometryOwnershipType ownership) { - return this->attributes_; -} - -const blender::bke::CustomDataAttributes &InstancesComponent::instance_attributes() const -{ - return this->attributes_; + BLI_assert(this->is_mutable()); + this->clear(); + instances_ = instances; + ownership_ = ownership; } namespace blender::bke { @@ -397,16 +135,21 @@ class InstancePositionAttributeProvider final : public BuiltinAttributeProvider GVArray try_get_for_read(const void *owner) const final { - const InstancesComponent &instances_component = *static_cast( - owner); - Span transforms = instances_component.instance_transforms(); + const Instances *instances = static_cast(owner); + if (instances == nullptr) { + return {}; + } + Span transforms = instances->transforms(); return VArray::ForDerivedSpan(transforms); } GAttributeWriter try_get_for_write(void *owner) const final { - InstancesComponent &instances_component = *static_cast(owner); - MutableSpan transforms = instances_component.instance_transforms(); + Instances *instances = static_cast(owner); + if (instances == nullptr) { + return {}; + } + MutableSpan transforms = instances->transforms(); return {VMutableArray::ForDerivedSpan(transforms), @@ -434,16 +177,16 @@ static ComponentAttributeProviders create_attribute_providers_for_instances() static InstancePositionAttributeProvider position; static CustomDataAccessInfo instance_custom_data_access = { [](void *owner) -> CustomData * { - InstancesComponent &inst = *static_cast(owner); - return &inst.instance_attributes().data; + Instances *instances = static_cast(owner); + return &instances->custom_data_attributes().data; }, [](const void *owner) -> const CustomData * { - const InstancesComponent &inst = *static_cast(owner); - return &inst.instance_attributes().data; + const Instances *instances = static_cast(owner); + return &instances->custom_data_attributes().data; }, [](const void *owner) -> int { - const InstancesComponent &inst = *static_cast(owner); - return inst.instances_num(); + const Instances *instances = static_cast(owner); + return instances->instances_num(); }}; /** @@ -479,10 +222,10 @@ static AttributeAccessorFunctions get_instances_accessor_functions() if (owner == nullptr) { return 0; } - const InstancesComponent &instances = *static_cast(owner); + const Instances *instances = static_cast(owner); switch (domain) { case ATTR_DOMAIN_INSTANCE: - return instances.instances_num(); + return instances->instances_num(); default: return 0; } @@ -508,18 +251,30 @@ static const AttributeAccessorFunctions &get_instances_accessor_functions_ref() return fn; } +blender::bke::AttributeAccessor Instances::attributes() const +{ + return blender::bke::AttributeAccessor(this, + blender::bke::get_instances_accessor_functions_ref()); +} + +blender::bke::MutableAttributeAccessor Instances::attributes_for_write() +{ + return blender::bke::MutableAttributeAccessor( + this, blender::bke::get_instances_accessor_functions_ref()); +} + } // namespace blender::bke std::optional InstancesComponent::attributes() const { - return blender::bke::AttributeAccessor(this, + return blender::bke::AttributeAccessor(instances_, blender::bke::get_instances_accessor_functions_ref()); } std::optional InstancesComponent::attributes_for_write() { return blender::bke::MutableAttributeAccessor( - this, blender::bke::get_instances_accessor_functions_ref()); + instances_, blender::bke::get_instances_accessor_functions_ref()); } /** \} */ diff --git a/source/blender/blenkernel/intern/geometry_fields.cc b/source/blender/blenkernel/intern/geometry_fields.cc index b492af4af77..82ffda57398 100644 --- a/source/blender/blenkernel/intern/geometry_fields.cc +++ b/source/blender/blenkernel/intern/geometry_fields.cc @@ -4,6 +4,7 @@ #include "BKE_curves.hh" #include "BKE_geometry_fields.hh" #include "BKE_geometry_set.hh" +#include "BKE_instances.hh" #include "BKE_mesh.h" #include "BKE_pointcloud.h" #include "BKE_type_conversions.hh" @@ -64,7 +65,7 @@ GeometryFieldContext::GeometryFieldContext(const GeometryComponent &component, case GEO_COMPONENT_TYPE_INSTANCES: { const InstancesComponent &instances_component = static_cast( component); - geometry_ = &instances_component; + geometry_ = instances_component.get_for_read(); break; } case GEO_COMPONENT_TYPE_VOLUME: @@ -86,7 +87,7 @@ GeometryFieldContext::GeometryFieldContext(const PointCloud &points) : geometry_(&points), type_(GEO_COMPONENT_TYPE_POINT_CLOUD), domain_(ATTR_DOMAIN_POINT) { } -GeometryFieldContext::GeometryFieldContext(const InstancesComponent &instances) +GeometryFieldContext::GeometryFieldContext(const Instances &instances) : geometry_(&instances), type_(GEO_COMPONENT_TYPE_INSTANCES), domain_(ATTR_DOMAIN_INSTANCE) { } @@ -102,7 +103,7 @@ std::optional GeometryFieldContext::attributes() const if (const PointCloud *pointcloud = this->pointcloud()) { return pointcloud->attributes(); } - if (const InstancesComponent *instances = this->instances()) { + if (const Instances *instances = this->instances()) { return instances->attributes(); } return {}; @@ -124,11 +125,10 @@ const PointCloud *GeometryFieldContext::pointcloud() const static_cast(geometry_) : nullptr; } -const InstancesComponent *GeometryFieldContext::instances() const +const Instances *GeometryFieldContext::instances() const { - return this->type() == GEO_COMPONENT_TYPE_INSTANCES ? - static_cast(geometry_) : - nullptr; + return this->type() == GEO_COMPONENT_TYPE_INSTANCES ? static_cast(geometry_) : + nullptr; } GVArray GeometryFieldInput::get_varray_for_context(const fn::FieldContext &context, @@ -230,7 +230,7 @@ GVArray InstancesFieldInput::get_varray_for_context(const fn::FieldContext &cont { if (const GeometryFieldContext *geometry_context = dynamic_cast( &context)) { - if (const InstancesComponent *instances = geometry_context->instances()) { + if (const Instances *instances = geometry_context->instances()) { return this->get_varray_for_context(*instances, mask); } } diff --git a/source/blender/blenkernel/intern/geometry_set.cc b/source/blender/blenkernel/intern/geometry_set.cc index 46ff8141504..90568a8a080 100644 --- a/source/blender/blenkernel/intern/geometry_set.cc +++ b/source/blender/blenkernel/intern/geometry_set.cc @@ -9,6 +9,7 @@ #include "BKE_attribute.h" #include "BKE_curves.hh" #include "BKE_geometry_set.hh" +#include "BKE_instances.hh" #include "BKE_lib_id.h" #include "BKE_mesh.h" #include "BKE_mesh_wrapper.h" @@ -31,6 +32,8 @@ using blender::MutableSpan; using blender::Span; using blender::StringRef; using blender::Vector; +using blender::bke::InstanceReference; +using blender::bke::Instances; /* -------------------------------------------------------------------- */ /** \name Geometry Component @@ -256,8 +259,7 @@ std::ostream &operator<<(std::ostream &stream, const GeometrySet &geometry_set) parts.append(std::to_string(BKE_volume_num_grids(volume)) + " volume grids"); } if (geometry_set.has_instances()) { - parts.append(std::to_string( - geometry_set.get_component_for_read()->instances_num()) + + parts.append(std::to_string(geometry_set.get_instances_for_read()->instances_num()) + " instances"); } if (geometry_set.get_curve_edit_hints_for_read()) { @@ -338,6 +340,12 @@ const Curves *GeometrySet::get_curves_for_read() const return (component == nullptr) ? nullptr : component->get_for_read(); } +const Instances *GeometrySet::get_instances_for_read() const +{ + const InstancesComponent *component = this->get_component_for_read(); + return (component == nullptr) ? nullptr : component->get_for_read(); +} + const blender::bke::CurvesEditHints *GeometrySet::get_curve_edit_hints_for_read() const { const GeometryComponentEditData *component = @@ -354,7 +362,8 @@ bool GeometrySet::has_pointcloud() const bool GeometrySet::has_instances() const { const InstancesComponent *component = this->get_component_for_read(); - return component != nullptr && component->instances_num() >= 1; + return component != nullptr && component->get_for_read() != nullptr && + component->get_for_read()->instances_num() >= 1; } bool GeometrySet::has_volume() const @@ -428,6 +437,14 @@ GeometrySet GeometrySet::create_with_curves(Curves *curves, GeometryOwnershipTyp return geometry_set; } +GeometrySet GeometrySet::create_with_instances(Instances *instances, + GeometryOwnershipType ownership) +{ + GeometrySet geometry_set; + geometry_set.replace_instances(instances, ownership); + return geometry_set; +} + void GeometrySet::replace_mesh(Mesh *mesh, GeometryOwnershipType ownership) { if (mesh == nullptr) { @@ -456,6 +473,20 @@ void GeometrySet::replace_curves(Curves *curves, GeometryOwnershipType ownership component.replace(curves, ownership); } +void GeometrySet::replace_instances(Instances *instances, GeometryOwnershipType ownership) +{ + if (instances == nullptr) { + this->remove(); + return; + } + if (instances == this->get_instances_for_read()) { + return; + } + this->remove(); + InstancesComponent &component = this->get_component_for_write(); + component.replace(instances, ownership); +} + void GeometrySet::replace_pointcloud(PointCloud *pointcloud, GeometryOwnershipType ownership) { if (pointcloud == nullptr) { @@ -508,6 +539,12 @@ Curves *GeometrySet::get_curves_for_write() return component == nullptr ? nullptr : component->get_for_write(); } +Instances *GeometrySet::get_instances_for_write() +{ + InstancesComponent *component = this->get_component_ptr(); + return component == nullptr ? nullptr : component->get_for_write(); +} + blender::bke::CurvesEditHints *GeometrySet::get_curve_edit_hints_for_write() { if (!this->has()) { @@ -539,7 +576,7 @@ void GeometrySet::attribute_foreach(const Span component_ } } if (include_instances && this->has_instances()) { - const InstancesComponent &instances = *this->get_component_for_read(); + const Instances &instances = *this->get_instances_for_read(); instances.foreach_referenced_geometry([&](const GeometrySet &instance_geometry_set) { instance_geometry_set.attribute_foreach(component_types, include_instances, callback); }); @@ -611,7 +648,7 @@ static void gather_component_types_recursive(const GeometrySet &geometry_set, if (!include_instances) { return; } - const InstancesComponent *instances = geometry_set.get_component_for_read(); + const blender::bke::Instances *instances = geometry_set.get_instances_for_read(); if (instances == nullptr) { return; } @@ -638,12 +675,11 @@ static void gather_mutable_geometry_sets(GeometrySet &geometry_set, } /* In the future this can be improved by deduplicating instance references across different * instances. */ - InstancesComponent &instances_component = - geometry_set.get_component_for_write(); - instances_component.ensure_geometry_instances(); - for (const int handle : instances_component.references().index_range()) { - if (instances_component.references()[handle].type() == InstanceReference::Type::GeometrySet) { - GeometrySet &instance_geometry = instances_component.geometry_set_from_reference(handle); + Instances &instances = *geometry_set.get_instances_for_write(); + instances.ensure_geometry_instances(); + for (const int handle : instances.references().index_range()) { + if (instances.references()[handle].type() == InstanceReference::Type::GeometrySet) { + GeometrySet &instance_geometry = instances.geometry_set_from_reference(handle); gather_mutable_geometry_sets(instance_geometry, r_geometry_sets); } } diff --git a/source/blender/blenkernel/intern/geometry_set_instances.cc b/source/blender/blenkernel/intern/geometry_set_instances.cc index 0ae49a586f1..e078991187d 100644 --- a/source/blender/blenkernel/intern/geometry_set_instances.cc +++ b/source/blender/blenkernel/intern/geometry_set_instances.cc @@ -2,6 +2,7 @@ #include "BKE_collection.h" #include "BKE_geometry_set_instances.hh" +#include "BKE_instances.hh" #include "BKE_material.h" #include "BKE_mesh.h" #include "BKE_mesh_wrapper.h" @@ -62,12 +63,11 @@ GeometrySet object_get_evaluated_geometry_set(const Object &object) return geometry_set; } if (object.type == OB_EMPTY && object.instance_collection != nullptr) { - GeometrySet geometry_set; Collection &collection = *object.instance_collection; - InstancesComponent &instances = geometry_set.get_component_for_write(); - const int handle = instances.add_reference(collection); - instances.add_instance(handle, float4x4::identity()); - return geometry_set; + std::unique_ptr instances = std::make_unique(); + const int handle = instances->add_reference(collection); + instances->add_instance(handle, float4x4::identity()); + return GeometrySet::create_with_instances(instances.release()); } /* Return by value since there is not always an existing geometry set owned elsewhere to use. */ @@ -115,12 +115,11 @@ static void geometry_set_collect_recursive(const GeometrySet &geometry_set, r_sets.append({geometry_set, {transform}}); if (geometry_set.has_instances()) { - const InstancesComponent &instances_component = - *geometry_set.get_component_for_read(); + const Instances &instances = *geometry_set.get_instances_for_read(); - Span transforms = instances_component.instance_transforms(); - Span handles = instances_component.instance_reference_handles(); - Span references = instances_component.references(); + Span transforms = instances.transforms(); + Span handles = instances.reference_handles(); + Span references = instances.references(); for (const int i : transforms.index_range()) { const InstanceReference &reference = references[handles[i]]; const float4x4 instance_transform = transform * transforms[i]; @@ -156,9 +155,7 @@ void geometry_set_gather_instances(const GeometrySet &geometry_set, geometry_set_collect_recursive(geometry_set, float4x4::identity(), r_instance_groups); } -} // namespace blender::bke - -void InstancesComponent::foreach_referenced_geometry( +void Instances::foreach_referenced_geometry( blender::FunctionRef callback) const { using namespace blender::bke; @@ -191,7 +188,7 @@ void InstancesComponent::foreach_referenced_geometry( } } -void InstancesComponent::ensure_geometry_instances() +void Instances::ensure_geometry_instances() { using namespace blender; using namespace blender::bke; @@ -211,9 +208,7 @@ void InstancesComponent::ensure_geometry_instances() const Object &object = reference.object(); GeometrySet object_geometry_set = object_get_evaluated_geometry_set(object); if (object_geometry_set.has_instances()) { - InstancesComponent &component = - object_geometry_set.get_component_for_write(); - component.ensure_geometry_instances(); + object_geometry_set.get_instances_for_write()->ensure_geometry_instances(); } new_references.add_new(std::move(object_geometry_set)); break; @@ -221,22 +216,22 @@ void InstancesComponent::ensure_geometry_instances() case InstanceReference::Type::Collection: { /* Create a new reference that contains a geometry set that contains all objects from the * collection as instances. */ - GeometrySet collection_geometry_set; - InstancesComponent &component = - collection_geometry_set.get_component_for_write(); + std::unique_ptr instances = std::make_unique(); Collection &collection = reference.collection(); FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (&collection, object) { - const int handle = component.add_reference(*object); - component.add_instance(handle, object->obmat); - float4x4 &transform = component.instance_transforms().last(); + const int handle = instances->add_reference(*object); + instances->add_instance(handle, object->obmat); + float4x4 &transform = instances->transforms().last(); sub_v3_v3(transform.values[3], collection.instance_offset); } FOREACH_COLLECTION_OBJECT_RECURSIVE_END; - component.ensure_geometry_instances(); - new_references.add_new(std::move(collection_geometry_set)); + instances->ensure_geometry_instances(); + new_references.add_new(GeometrySet::create_with_instances(instances.release())); break; } } } references_ = std::move(new_references); } + +} // namespace blender::bke diff --git a/source/blender/blenkernel/intern/instances.cc b/source/blender/blenkernel/intern/instances.cc new file mode 100644 index 00000000000..dd759453630 --- /dev/null +++ b/source/blender/blenkernel/intern/instances.cc @@ -0,0 +1,348 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BLI_cpp_type_make.hh" +#include "BLI_rand.hh" +#include "BLI_task.hh" + +#include "BKE_attribute_math.hh" +#include "BKE_geometry_set.hh" +#include "BKE_instances.hh" + +BLI_CPP_TYPE_MAKE(InstanceReference, blender::bke::InstanceReference, CPPTypeFlags::None) + +namespace blender::bke { + +InstanceReference::InstanceReference(GeometrySet geometry_set) + : type_(Type::GeometrySet), + geometry_set_(std::make_unique(std::move(geometry_set))) +{ +} + +void InstanceReference::ensure_owns_direct_data() +{ + if (type_ != Type::GeometrySet) { + return; + } + geometry_set_->ensure_owns_direct_data(); +} + +bool InstanceReference::owns_direct_data() const +{ + if (type_ != Type::GeometrySet) { + /* The object and collection instances are not direct data. */ + return true; + } + return geometry_set_->owns_direct_data(); +} + +Instances::Instances(const Instances &other) + : references_(other.references_), + reference_handles_(other.reference_handles_), + transforms_(other.transforms_), + almost_unique_ids_(other.almost_unique_ids_), + attributes_(other.attributes_) +{ +} + +void Instances::reserve(int min_capacity) +{ + reference_handles_.reserve(min_capacity); + transforms_.reserve(min_capacity); + attributes_.reallocate(min_capacity); +} + +void Instances::resize(int capacity) +{ + reference_handles_.resize(capacity); + transforms_.resize(capacity); + attributes_.reallocate(capacity); +} + +void Instances::add_instance(const int instance_handle, const float4x4 &transform) +{ + BLI_assert(instance_handle >= 0); + BLI_assert(instance_handle < references_.size()); + reference_handles_.append(instance_handle); + transforms_.append(transform); + attributes_.reallocate(this->instances_num()); +} + +blender::Span Instances::reference_handles() const +{ + return reference_handles_; +} + +blender::MutableSpan Instances::reference_handles() +{ + return reference_handles_; +} + +blender::MutableSpan Instances::transforms() +{ + return transforms_; +} +blender::Span Instances::transforms() const +{ + return transforms_; +} + +GeometrySet &Instances::geometry_set_from_reference(const int reference_index) +{ + /* If this assert fails, it means #ensure_geometry_instances must be called first or that the + * reference can't be converted to a geometry set. */ + BLI_assert(references_[reference_index].type() == InstanceReference::Type::GeometrySet); + + /* The const cast is okay because the instance's hash in the set + * is not changed by adjusting the data inside the geometry set. */ + return const_cast(references_[reference_index].geometry_set()); +} + +int Instances::add_reference(const InstanceReference &reference) +{ + return references_.index_of_or_add_as(reference); +} + +blender::Span Instances::references() const +{ + return references_; +} + +template +static void copy_data_based_on_mask(Span src, MutableSpan dst, IndexMask mask) +{ + BLI_assert(src.data() != dst.data()); + using namespace blender; + threading::parallel_for(mask.index_range(), 1024, [&](IndexRange range) { + for (const int i : range) { + dst[i] = src[mask[i]]; + } + }); +} + +void Instances::remove(const IndexMask mask) +{ + using namespace blender; + if (mask.is_range() && mask.as_range().start() == 0) { + /* Deleting from the end of the array can be much faster since no data has to be shifted. */ + this->resize(mask.size()); + this->remove_unused_references(); + return; + } + + Vector new_handles(mask.size()); + copy_data_based_on_mask(this->reference_handles(), new_handles, mask); + reference_handles_ = std::move(new_handles); + Vector new_transforms(mask.size()); + copy_data_based_on_mask(this->transforms(), new_transforms, mask); + transforms_ = std::move(new_transforms); + + const bke::CustomDataAttributes &src_attributes = attributes_; + + bke::CustomDataAttributes dst_attributes; + dst_attributes.reallocate(mask.size()); + + src_attributes.foreach_attribute( + [&](const bke::AttributeIDRef &id, const bke::AttributeMetaData &meta_data) { + if (!id.should_be_kept()) { + return true; + } + + GSpan src = *src_attributes.get_for_read(id); + dst_attributes.create(id, meta_data.data_type); + GMutableSpan dst = *dst_attributes.get_for_write(id); + + attribute_math::convert_to_static_type(src.type(), [&](auto dummy) { + using T = decltype(dummy); + copy_data_based_on_mask(src.typed(), dst.typed(), mask); + }); + return true; + }, + ATTR_DOMAIN_INSTANCE); + + attributes_ = std::move(dst_attributes); + this->remove_unused_references(); +} + +void Instances::remove_unused_references() +{ + using namespace blender; + using namespace blender::bke; + + const int tot_instances = this->instances_num(); + const int tot_references_before = references_.size(); + + if (tot_instances == 0) { + /* If there are no instances, no reference is needed. */ + references_.clear(); + return; + } + if (tot_references_before == 1) { + /* There is only one reference and at least one instance. So the only existing reference is + * used. Nothing to do here. */ + return; + } + + Array usage_by_handle(tot_references_before, false); + std::mutex mutex; + + /* Loop over all instances to see which references are used. */ + threading::parallel_for(IndexRange(tot_instances), 1000, [&](IndexRange range) { + /* Use local counter to avoid lock contention. */ + Array local_usage_by_handle(tot_references_before, false); + + for (const int i : range) { + const int handle = reference_handles_[i]; + BLI_assert(handle >= 0 && handle < tot_references_before); + local_usage_by_handle[handle] = true; + } + + std::lock_guard lock{mutex}; + for (const int i : IndexRange(tot_references_before)) { + usage_by_handle[i] |= local_usage_by_handle[i]; + } + }); + + if (!usage_by_handle.as_span().contains(false)) { + /* All references are used. */ + return; + } + + /* Create new references and a mapping for the handles. */ + Vector handle_mapping; + VectorSet new_references; + int next_new_handle = 0; + bool handles_have_to_be_updated = false; + for (const int old_handle : IndexRange(tot_references_before)) { + if (!usage_by_handle[old_handle]) { + /* Add some dummy value. It won't be read again. */ + handle_mapping.append(-1); + } + else { + const InstanceReference &reference = references_[old_handle]; + handle_mapping.append(next_new_handle); + new_references.add_new(reference); + if (old_handle != next_new_handle) { + handles_have_to_be_updated = true; + } + next_new_handle++; + } + } + references_ = new_references; + + if (!handles_have_to_be_updated) { + /* All remaining handles are the same as before, so they don't have to be updated. This happens + * when unused handles are only at the end. */ + return; + } + + /* Update handles of instances. */ + threading::parallel_for(IndexRange(tot_instances), 1000, [&](IndexRange range) { + for (const int i : range) { + reference_handles_[i] = handle_mapping[reference_handles_[i]]; + } + }); +} + +int Instances::instances_num() const +{ + return transforms_.size(); +} + +int Instances::references_num() const +{ + return references_.size(); +} + +bool Instances::owns_direct_data() const +{ + for (const InstanceReference &reference : references_) { + if (!reference.owns_direct_data()) { + return false; + } + } + return true; +} + +void Instances::ensure_owns_direct_data() +{ + for (const InstanceReference &const_reference : references_) { + /* Const cast is fine because we are not changing anything that would change the hash of the + * reference. */ + InstanceReference &reference = const_cast(const_reference); + reference.ensure_owns_direct_data(); + } +} + +static blender::Array generate_unique_instance_ids(Span original_ids) +{ + using namespace blender; + Array unique_ids(original_ids.size()); + + Set used_unique_ids; + used_unique_ids.reserve(original_ids.size()); + Vector instances_with_id_collision; + for (const int instance_index : original_ids.index_range()) { + const int original_id = original_ids[instance_index]; + if (used_unique_ids.add(original_id)) { + /* The original id has not been used by another instance yet. */ + unique_ids[instance_index] = original_id; + } + else { + /* The original id of this instance collided with a previous instance, it needs to be looked + * at again in a second pass. Don't generate a new random id here, because this might collide + * with other existing ids. */ + instances_with_id_collision.append(instance_index); + } + } + + Map generator_by_original_id; + for (const int instance_index : instances_with_id_collision) { + const int original_id = original_ids[instance_index]; + RandomNumberGenerator &rng = generator_by_original_id.lookup_or_add_cb(original_id, [&]() { + RandomNumberGenerator rng; + rng.seed_random(original_id); + return rng; + }); + + const int max_iteration = 100; + for (int iteration = 0;; iteration++) { + /* Try generating random numbers until an unused one has been found. */ + const int random_id = rng.get_int32(); + if (used_unique_ids.add(random_id)) { + /* This random id is not used by another instance. */ + unique_ids[instance_index] = random_id; + break; + } + if (iteration == max_iteration) { + /* It seems to be very unlikely that we ever run into this case (assuming there are less + * than 2^30 instances). However, if that happens, it's better to use an id that is not + * unique than to be stuck in an infinite loop. */ + unique_ids[instance_index] = original_id; + break; + } + } + } + + return unique_ids; +} + +blender::Span Instances::almost_unique_ids() const +{ + std::lock_guard lock(almost_unique_ids_mutex_); + std::optional instance_ids_gspan = attributes_.get_for_read("id"); + if (instance_ids_gspan) { + Span instance_ids = instance_ids_gspan->typed(); + if (almost_unique_ids_.size() != instance_ids.size()) { + almost_unique_ids_ = generate_unique_instance_ids(instance_ids); + } + } + else { + almost_unique_ids_.reinitialize(this->instances_num()); + for (const int i : almost_unique_ids_.index_range()) { + almost_unique_ids_[i] = i; + } + } + return almost_unique_ids_; +} + +} // namespace blender::bke diff --git a/source/blender/blenkernel/intern/object_dupli.cc b/source/blender/blenkernel/intern/object_dupli.cc index 96da99af97e..d43eff6f9b4 100644 --- a/source/blender/blenkernel/intern/object_dupli.cc +++ b/source/blender/blenkernel/intern/object_dupli.cc @@ -41,6 +41,7 @@ #include "BKE_geometry_set.hh" #include "BKE_global.h" #include "BKE_idprop.h" +#include "BKE_instances.hh" #include "BKE_lattice.h" #include "BKE_main.h" #include "BKE_mesh.h" @@ -70,6 +71,8 @@ using blender::float3; using blender::float4x4; using blender::Span; using blender::Vector; +using blender::bke::InstanceReference; +using blender::bke::Instances; namespace geo_log = blender::nodes::geo_eval_log; /* -------------------------------------------------------------------- */ @@ -874,8 +877,8 @@ static void make_duplis_geometry_set_impl(const DupliContext *ctx, } const bool creates_duplis_for_components = component_index >= 1; - const InstancesComponent *component = geometry_set.get_component_for_read(); - if (component == nullptr) { + const Instances *instances = geometry_set.get_instances_for_read(); + if (instances == nullptr) { return; } @@ -890,13 +893,13 @@ static void make_duplis_geometry_set_impl(const DupliContext *ctx, instances_ctx = &new_instances_ctx; } - Span instance_offset_matrices = component->instance_transforms(); - Span instance_reference_handles = component->instance_reference_handles(); - Span almost_unique_ids = component->almost_unique_ids(); - Span references = component->references(); + Span instance_offset_matrices = instances->transforms(); + Span reference_handles = instances->reference_handles(); + Span almost_unique_ids = instances->almost_unique_ids(); + Span references = instances->references(); for (int64_t i : instance_offset_matrices.index_range()) { - const InstanceReference &reference = references[instance_reference_handles[i]]; + const InstanceReference &reference = references[reference_handles[i]]; const int id = almost_unique_ids[i]; const DupliContext *ctx_for_instance = instances_ctx; diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_column.cc b/source/blender/editors/space_spreadsheet/spreadsheet_column.cc index 46e98acb8e8..af41225f42a 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_column.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_column.cc @@ -12,6 +12,7 @@ #include "BLI_string_ref.hh" #include "BKE_geometry_set.hh" +#include "BKE_instances.hh" #include "spreadsheet_column.hh" #include "spreadsheet_column_values.hh" @@ -44,7 +45,7 @@ eSpreadsheetColumnValueType cpp_type_to_column_type(const CPPType &type) if (type.is()) { return SPREADSHEET_VALUE_TYPE_STRING; } - if (type.is()) { + if (type.is()) { return SPREADSHEET_VALUE_TYPE_INSTANCES; } if (type.is()) { diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc index b2f9cfc6395..59a8daf4f4a 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc @@ -10,6 +10,7 @@ #include "BKE_editmesh.h" #include "BKE_geometry_fields.hh" #include "BKE_global.h" +#include "BKE_instances.hh" #include "BKE_lib_id.h" #include "BKE_mesh.h" #include "BKE_mesh_wrapper.h" @@ -143,29 +144,31 @@ std::unique_ptr GeometryDataSource::get_column_values( } if (component_->type() == GEO_COMPONENT_TYPE_INSTANCES) { - const InstancesComponent &instances = static_cast(*component_); - if (STREQ(column_id.name, "Name")) { - Span reference_handles = instances.instance_reference_handles(); - Span references = instances.references(); - return std::make_unique( - column_id.name, - VArray::ForFunc(domain_num, - [reference_handles, references](int64_t index) { - return references[reference_handles[index]]; - })); - } - Span transforms = instances.instance_transforms(); - if (STREQ(column_id.name, "Rotation")) { - return std::make_unique( - column_id.name, VArray::ForFunc(domain_num, [transforms](int64_t index) { - return transforms[index].to_euler(); - })); - } - if (STREQ(column_id.name, "Scale")) { - return std::make_unique( - column_id.name, VArray::ForFunc(domain_num, [transforms](int64_t index) { - return transforms[index].scale(); - })); + if (const bke::Instances *instances = + static_cast(*component_).get_for_read()) { + if (STREQ(column_id.name, "Name")) { + Span reference_handles = instances->reference_handles(); + Span references = instances->references(); + return std::make_unique( + column_id.name, + VArray::ForFunc( + domain_num, [reference_handles, references](int64_t index) { + return references[reference_handles[index]]; + })); + } + Span transforms = instances->transforms(); + if (STREQ(column_id.name, "Rotation")) { + return std::make_unique( + column_id.name, VArray::ForFunc(domain_num, [transforms](int64_t index) { + return transforms[index].to_euler(); + })); + } + if (STREQ(column_id.name, "Scale")) { + return std::make_unique( + column_id.name, VArray::ForFunc(domain_num, [transforms](int64_t index) { + return transforms[index].scale(); + })); + } } } else if (G.debug_value == 4001 && component_->type() == GEO_COMPONENT_TYPE_MESH) { diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc b/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc index b4b8417c172..06eb338bd00 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc @@ -6,6 +6,7 @@ #include "BLI_math_vec_types.hh" #include "BKE_geometry_set.hh" +#include "BKE_instances.hh" #include "spreadsheet_column_values.hh" #include "spreadsheet_layout.hh" @@ -197,10 +198,10 @@ class SpreadsheetLayoutDrawer : public SpreadsheetDrawer { const ColorGeometry4b value = data.get(real_index); this->draw_byte_color(params, value); } - else if (data.type().is()) { - const InstanceReference value = data.get(real_index); + else if (data.type().is()) { + const bke::InstanceReference value = data.get(real_index); switch (value.type()) { - case InstanceReference::Type::Object: { + case bke::InstanceReference::Type::Object: { const Object &object = value.object(); uiDefIconTextBut(params.block, UI_BTYPE_LABEL, @@ -219,7 +220,7 @@ class SpreadsheetLayoutDrawer : public SpreadsheetDrawer { nullptr); break; } - case InstanceReference::Type::Collection: { + case bke::InstanceReference::Type::Collection: { Collection &collection = value.collection(); uiDefIconTextBut(params.block, UI_BTYPE_LABEL, @@ -238,7 +239,7 @@ class SpreadsheetLayoutDrawer : public SpreadsheetDrawer { nullptr); break; } - case InstanceReference::Type::GeometrySet: { + case bke::InstanceReference::Type::GeometrySet: { uiDefIconTextBut(params.block, UI_BTYPE_LABEL, 0, @@ -256,7 +257,7 @@ class SpreadsheetLayoutDrawer : public SpreadsheetDrawer { nullptr); break; } - case InstanceReference::Type::None: { + case bke::InstanceReference::Type::None: { break; } } diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_row_filter.cc b/source/blender/editors/space_spreadsheet/spreadsheet_row_filter.cc index 96827692a25..3586389b00b 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_row_filter.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_row_filter.cc @@ -14,6 +14,8 @@ #include "RNA_access.h" +#include "BKE_instances.hh" + #include "spreadsheet_data_source_geometry.hh" #include "spreadsheet_intern.hh" #include "spreadsheet_layout.hh" @@ -280,22 +282,22 @@ static void apply_row_filter(const SpreadsheetRowFilter &row_filter, } } } - else if (column_data.type().is()) { + else if (column_data.type().is()) { const StringRef value = row_filter.value_string; apply_filter_operation( - column_data.typed(), - [&](const InstanceReference cell) { + column_data.typed(), + [&](const bke::InstanceReference cell) { switch (cell.type()) { - case InstanceReference::Type::Object: { + case bke::InstanceReference::Type::Object: { return value == (reinterpret_cast(cell.object()).name + 2); } - case InstanceReference::Type::Collection: { + case bke::InstanceReference::Type::Collection: { return value == (reinterpret_cast(cell.collection()).name + 2); } - case InstanceReference::Type::GeometrySet: { + case bke::InstanceReference::Type::GeometrySet: { return false; } - case InstanceReference::Type::None: { + case bke::InstanceReference::Type::None: { return false; } } diff --git a/source/blender/geometry/intern/realize_instances.cc b/source/blender/geometry/intern/realize_instances.cc index 7b3c307cf37..daafe0e4aa7 100644 --- a/source/blender/geometry/intern/realize_instances.cc +++ b/source/blender/geometry/intern/realize_instances.cc @@ -17,6 +17,7 @@ #include "BKE_curves.hh" #include "BKE_deform.h" #include "BKE_geometry_set_instances.hh" +#include "BKE_instances.hh" #include "BKE_material.h" #include "BKE_mesh.h" #include "BKE_pointcloud.h" @@ -30,6 +31,8 @@ using blender::bke::AttributeMetaData; using blender::bke::custom_data_type_to_cpp_type; using blender::bke::CustomDataAttributes; using blender::bke::GSpanAttributeWriter; +using blender::bke::InstanceReference; +using blender::bke::Instances; using blender::bke::object_get_evaluated_geometry_set; using blender::bke::SpanAttributeWriter; @@ -370,11 +373,11 @@ static void gather_realize_tasks_recursive(GatherTasksInfo &gather_info, */ static Vector> prepare_attribute_fallbacks( GatherTasksInfo &gather_info, - const InstancesComponent &instances_component, + const Instances &instances, const OrderedAttributes &ordered_attributes) { Vector> attributes_to_override; - const CustomDataAttributes &attributes = instances_component.instance_attributes(); + const CustomDataAttributes &attributes = instances.custom_data_attributes(); attributes.foreach_attribute( [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { const int attribute_index = ordered_attributes.ids.index_of_try(attribute_id); @@ -394,7 +397,7 @@ static Vector> prepare_attribute_fallbacks( } /* Convert the attribute on the instances component to the expected attribute type. */ std::unique_ptr> temporary_array = std::make_unique>( - to_type, instances_component.instances_num()); + to_type, instances.instances_num()); 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)); @@ -450,17 +453,17 @@ static void foreach_geometry_in_reference( } static void gather_realize_tasks_for_instances(GatherTasksInfo &gather_info, - const InstancesComponent &instances_component, + const Instances &instances, const float4x4 &base_transform, const InstanceContext &base_instance_context) { - const Span references = instances_component.references(); - const Span handles = instances_component.instance_reference_handles(); - const Span transforms = instances_component.instance_transforms(); + const Span references = instances.references(); + const Span handles = instances.reference_handles(); + const Span transforms = instances.transforms(); Span stored_instance_ids; if (gather_info.create_id_attribute_on_any_component) { - std::optional ids = instances_component.instance_attributes().get_for_read("id"); + std::optional ids = instances.custom_data_attributes().get_for_read("id"); if (ids.has_value()) { stored_instance_ids = ids->typed(); } @@ -469,11 +472,11 @@ static void gather_realize_tasks_for_instances(GatherTasksInfo &gather_info, /* Prepare attribute fallbacks. */ InstanceContext instance_context = base_instance_context; Vector> pointcloud_attributes_to_override = prepare_attribute_fallbacks( - gather_info, instances_component, gather_info.pointclouds.attributes); + gather_info, instances, gather_info.pointclouds.attributes); Vector> mesh_attributes_to_override = prepare_attribute_fallbacks( - gather_info, instances_component, gather_info.meshes.attributes); + gather_info, instances, gather_info.meshes.attributes); Vector> curve_attributes_to_override = prepare_attribute_fallbacks( - gather_info, instances_component, gather_info.curves.attributes); + gather_info, instances, gather_info.curves.attributes); for (const int i : transforms.index_range()) { const int handle = handles[i]; @@ -584,8 +587,11 @@ static void gather_realize_tasks_recursive(GatherTasksInfo &gather_info, case GEO_COMPONENT_TYPE_INSTANCES: { const InstancesComponent &instances_component = *static_cast( component); - gather_realize_tasks_for_instances( - gather_info, instances_component, base_transform, base_instance_context); + const Instances *instances = instances_component.get_for_read(); + if (instances != nullptr && instances->instances_num() > 0) { + gather_realize_tasks_for_instances( + gather_info, *instances, base_transform, base_instance_context); + } break; } case GEO_COMPONENT_TYPE_VOLUME: { @@ -645,8 +651,7 @@ static void gather_pointclouds_to_realize(const GeometrySet &geometry_set, r_pointclouds.add(pointcloud); } } - if (const InstancesComponent *instances = - geometry_set.get_component_for_read()) { + if (const Instances *instances = geometry_set.get_instances_for_read()) { instances->foreach_referenced_geometry([&](const GeometrySet &instance_geometry_set) { gather_pointclouds_to_realize(instance_geometry_set, r_pointclouds); }); @@ -827,8 +832,7 @@ static void gather_meshes_to_realize(const GeometrySet &geometry_set, r_meshes.add(mesh); } } - if (const InstancesComponent *instances = - geometry_set.get_component_for_read()) { + if (const Instances *instances = geometry_set.get_instances_for_read()) { instances->foreach_referenced_geometry([&](const GeometrySet &instance_geometry_set) { gather_meshes_to_realize(instance_geometry_set, r_meshes); }); @@ -1148,8 +1152,7 @@ static void gather_curves_to_realize(const GeometrySet &geometry_set, r_curves.add(curves); } } - if (const InstancesComponent *instances = - geometry_set.get_component_for_read()) { + if (const Instances *instances = geometry_set.get_instances_for_read()) { instances->foreach_referenced_geometry([&](const GeometrySet &instance_geometry_set) { gather_curves_to_realize(instance_geometry_set, r_curves); }); @@ -1415,9 +1418,8 @@ static void execute_realize_curve_tasks(const RealizeInstancesOptions &options, static void remove_id_attribute_from_instances(GeometrySet &geometry_set) { geometry_set.modify_geometry_sets([&](GeometrySet &sub_geometry) { - if (sub_geometry.has()) { - InstancesComponent &component = sub_geometry.get_component_for_write(); - component.instance_attributes().remove("id"); + if (Instances *instances = sub_geometry.get_instances_for_write()) { + instances->custom_data_attributes().remove("id"); } }); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_collection_info.cc b/source/blender/nodes/geometry/nodes/node_geo_collection_info.cc index c4bee09e07b..df677e1c399 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_collection_info.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_collection_info.cc @@ -8,6 +8,7 @@ #include "UI_resources.h" #include "BKE_collection.h" +#include "BKE_instances.hh" #include "node_geometry_util.hh" @@ -69,8 +70,7 @@ static void node_geo_exec(GeoNodeExecParams params) const bool use_relative_transform = (storage.transform_space == GEO_NODE_TRANSFORM_SPACE_RELATIVE); - GeometrySet geometry_set_out; - InstancesComponent &instances = geometry_set_out.get_component_for_write(); + std::unique_ptr instances = std::make_unique(); const bool separate_children = params.get_input("Separate Children"); if (separate_children) { @@ -84,7 +84,7 @@ static void node_geo_exec(GeoNodeExecParams params) children_objects.append(collection_object->ob); } - instances.reserve(children_collections.size() + children_objects.size()); + instances->reserve(children_collections.size() + children_objects.size()); Vector entries; entries.reserve(children_collections.size() + children_objects.size()); @@ -99,11 +99,11 @@ static void node_geo_exec(GeoNodeExecParams params) sub_v3_v3(transform.values[3], collection->instance_offset); } } - const int handle = instances.add_reference(*child_collection); + const int handle = instances->add_reference(*child_collection); entries.append({handle, &(child_collection->id.name[2]), transform}); } for (Object *child_object : children_objects) { - const int handle = instances.add_reference(*child_object); + const int handle = instances->add_reference(*child_object); float4x4 transform = float4x4::identity(); if (!reset_children) { if (use_relative_transform) { @@ -123,7 +123,7 @@ static void node_geo_exec(GeoNodeExecParams params) return BLI_strcasecmp_natural(a.name, b.name) < 0; }); for (const InstanceListEntry &entry : entries) { - instances.add_instance(entry.handle, entry.transform); + instances->add_instance(entry.handle, entry.transform); } } else { @@ -133,11 +133,11 @@ static void node_geo_exec(GeoNodeExecParams params) mul_m4_m4_pre(transform.values, self_object->imat); } - const int handle = instances.add_reference(*collection); - instances.add_instance(handle, transform); + const int handle = instances->add_reference(*collection); + instances->add_instance(handle, transform); } - params.set_output("Geometry", geometry_set_out); + params.set_output("Geometry", GeometrySet::create_with_instances(instances.release())); } } // namespace blender::nodes::node_geo_collection_info_cc diff --git a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc index 86c8cd64489..a433a0df9b0 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc @@ -12,6 +12,7 @@ #include "BKE_attribute_math.hh" #include "BKE_curves.hh" #include "BKE_customdata.h" +#include "BKE_instances.hh" #include "BKE_mesh.h" #include "BKE_pointcloud.h" @@ -392,8 +393,8 @@ static void separate_point_cloud_selection(GeometrySet &geometry_set, static void delete_selected_instances(GeometrySet &geometry_set, const Field &selection_field) { - InstancesComponent &instances = geometry_set.get_component_for_write(); - bke::GeometryFieldContext field_context{instances, ATTR_DOMAIN_INSTANCE}; + bke::Instances &instances = *geometry_set.get_instances_for_write(); + bke::InstancesFieldContext field_context{instances}; fn::FieldEvaluator evaluator{field_context, instances.instances_num()}; evaluator.set_selection(selection_field); @@ -404,7 +405,7 @@ static void delete_selected_instances(GeometrySet &geometry_set, return; } - instances.remove_instances(selection); + instances.remove(selection); } static void compute_selected_verts_from_vertex_selection(const Span vertex_selection, diff --git a/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc b/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc index 82d7e1d3652..70583625a7a 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc @@ -11,6 +11,7 @@ #include "BKE_attribute_math.hh" #include "BKE_curves.hh" +#include "BKE_instances.hh" #include "BKE_mesh.h" #include "BKE_pointcloud.h" @@ -1031,10 +1032,9 @@ static void duplicate_instances(GeometrySet &geometry_set, return; } - const InstancesComponent &src_instances = - *geometry_set.get_component_for_read(); + const bke::Instances &src_instances = *geometry_set.get_instances_for_read(); - bke::GeometryFieldContext field_context{src_instances, ATTR_DOMAIN_INSTANCE}; + bke::InstancesFieldContext field_context{src_instances}; FieldEvaluator evaluator{field_context, src_instances.instances_num()}; evaluator.add(count_field); evaluator.set_selection(selection_field); @@ -1048,20 +1048,20 @@ static void duplicate_instances(GeometrySet &geometry_set, return; } - GeometrySet dst_geometry; - InstancesComponent &dst_instances = dst_geometry.get_component_for_write(); - dst_instances.resize(offsets.last()); + std::unique_ptr dst_instances = std::make_unique(); + + dst_instances->resize(offsets.last()); for (const int i_selection : selection.index_range()) { const IndexRange range = range_for_offsets_index(offsets, i_selection); if (range.size() == 0) { continue; } - const int old_handle = src_instances.instance_reference_handles()[i_selection]; - const InstanceReference reference = src_instances.references()[old_handle]; - const int new_handle = dst_instances.add_reference(reference); - const float4x4 transform = src_instances.instance_transforms()[i_selection]; - dst_instances.instance_transforms().slice(range).fill(transform); - dst_instances.instance_reference_handles().slice(range).fill(new_handle); + const int old_handle = src_instances.reference_handles()[i_selection]; + const bke::InstanceReference reference = src_instances.references()[old_handle]; + const int new_handle = dst_instances->add_reference(reference); + const float4x4 transform = src_instances.transforms()[i_selection]; + dst_instances->transforms().slice(range).fill(transform); + dst_instances->reference_handles().slice(range).fill(new_handle); } copy_attributes_without_id(geometry_set, @@ -1069,18 +1069,18 @@ static void duplicate_instances(GeometrySet &geometry_set, ATTR_DOMAIN_INSTANCE, offsets, selection, - *src_instances.attributes(), - *dst_instances.attributes_for_write()); + src_instances.attributes(), + dst_instances->attributes_for_write()); if (attribute_outputs.duplicate_index) { - create_duplicate_index_attribute(*dst_instances.attributes_for_write(), + create_duplicate_index_attribute(dst_instances->attributes_for_write(), ATTR_DOMAIN_INSTANCE, selection, attribute_outputs, offsets); } - geometry_set = std::move(dst_geometry); + geometry_set = GeometrySet::create_with_instances(dst_instances.release()); } /** \} */ diff --git a/source/blender/nodes/geometry/nodes/node_geo_geometry_to_instance.cc b/source/blender/nodes/geometry/nodes/node_geo_geometry_to_instance.cc index 8e64209a418..45808ff9996 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_geometry_to_instance.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_geometry_to_instance.cc @@ -1,5 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ +#include "BKE_instances.hh" + #include "node_geometry_util.hh" namespace blender::nodes::node_geo_geometry_to_instance_cc { @@ -13,15 +15,13 @@ static void node_declare(NodeDeclarationBuilder &b) static void node_geo_exec(GeoNodeExecParams params) { Vector geometries = params.extract_input>("Geometry"); - GeometrySet instances_geometry; - InstancesComponent &instances_component = - instances_geometry.get_component_for_write(); + std::unique_ptr instances = std::make_unique(); for (GeometrySet &geometry : geometries) { geometry.ensure_owns_direct_data(); - const int handle = instances_component.add_reference(std::move(geometry)); - instances_component.add_instance(handle, float4x4::identity()); + const int handle = instances->add_reference(std::move(geometry)); + instances->add_instance(handle, float4x4::identity()); } - params.set_output("Instances", std::move(instances_geometry)); + params.set_output("Instances", GeometrySet::create_with_instances(instances.release())); } } // namespace blender::nodes::node_geo_geometry_to_instance_cc diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_instance_rotation.cc b/source/blender/nodes/geometry/nodes/node_geo_input_instance_rotation.cc index 75d43d2f771..f78815ebe74 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_instance_rotation.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_instance_rotation.cc @@ -2,6 +2,8 @@ #include "node_geometry_util.hh" +#include "BKE_instances.hh" + namespace blender::nodes::node_geo_input_instance_rotation_cc { static void node_declare(NodeDeclarationBuilder &b) @@ -15,12 +17,9 @@ class InstanceRotationFieldInput final : public bke::InstancesFieldInput { { } - GVArray get_varray_for_context(const InstancesComponent &instances, - const IndexMask /*mask*/) const final + GVArray get_varray_for_context(const bke::Instances &instances, IndexMask /*mask*/) const final { - auto rotation_fn = [&](const int i) -> float3 { - return instances.instance_transforms()[i].to_euler(); - }; + auto rotation_fn = [&](const int i) -> float3 { return instances.transforms()[i].to_euler(); }; return VArray::ForFunc(instances.instances_num(), rotation_fn); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_instance_scale.cc b/source/blender/nodes/geometry/nodes/node_geo_input_instance_scale.cc index dbb98d7e393..12ac48f8f11 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_instance_scale.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_instance_scale.cc @@ -2,6 +2,8 @@ #include "node_geometry_util.hh" +#include "BKE_instances.hh" + namespace blender::nodes::node_geo_input_instance_scale_cc { static void node_declare(NodeDeclarationBuilder &b) @@ -15,12 +17,9 @@ class InstanceScaleFieldInput final : public bke::InstancesFieldInput { { } - GVArray get_varray_for_context(const InstancesComponent &instances, - const IndexMask /*mask*/) const final + GVArray get_varray_for_context(const bke::Instances &instances, IndexMask /*mask*/) const final { - auto scale_fn = [&](const int i) -> float3 { - return instances.instance_transforms()[i].scale(); - }; + auto scale_fn = [&](const int i) -> float3 { return instances.transforms()[i].scale(); }; return VArray::ForFunc(instances.instances_num(), scale_fn); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc b/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc index c6f214e72ac..affeb43e33b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc @@ -9,6 +9,7 @@ #include "UI_resources.h" #include "BKE_attribute_math.hh" +#include "BKE_instances.hh" #include "node_geometry_util.hh" @@ -43,7 +44,7 @@ static void node_declare(NodeDeclarationBuilder &b) } static void add_instances_from_component( - InstancesComponent &dst_component, + bke::Instances &dst_component, const GeometryComponent &src_component, const GeometrySet &instance, const GeoNodeExecParams ¶ms, @@ -80,25 +81,23 @@ static void add_instances_from_component( const int select_len = selection.index_range().size(); dst_component.resize(start_len + select_len); - MutableSpan dst_handles = dst_component.instance_reference_handles().slice(start_len, - select_len); - MutableSpan dst_transforms = dst_component.instance_transforms().slice(start_len, - select_len); + MutableSpan dst_handles = dst_component.reference_handles().slice(start_len, select_len); + MutableSpan dst_transforms = dst_component.transforms().slice(start_len, select_len); VArray positions = src_component.attributes()->lookup_or_default( "position", domain, {0, 0, 0}); - const InstancesComponent *src_instances = instance.get_component_for_read(); + const bke::Instances *src_instances = instance.get_instances_for_read(); /* Maps handles from the source instances to handles on the new instance. */ Array handle_mapping; /* Only fill #handle_mapping when it may be used below. */ if (src_instances != nullptr && (!pick_instance.is_single() || pick_instance.get_internal_single())) { - Span src_references = src_instances->references(); + Span src_references = src_instances->references(); handle_mapping.reinitialize(src_references.size()); for (const int src_instance_handle : src_references.index_range()) { - const InstanceReference &reference = src_references[src_instance_handle]; + const bke::InstanceReference &reference = src_references[src_instance_handle]; const int dst_instance_handle = dst_component.add_reference(reference); handle_mapping[src_instance_handle] = dst_instance_handle; } @@ -106,7 +105,7 @@ static void add_instances_from_component( const int full_instance_handle = dst_component.add_reference(instance); /* Add this reference last, because it is the most likely one to be removed later on. */ - const int empty_reference_handle = dst_component.add_reference(InstanceReference()); + const int empty_reference_handle = dst_component.add_reference(bke::InstanceReference()); threading::parallel_for(selection.index_range(), 1024, [&](IndexRange selection_range) { for (const int range_i : selection_range) { @@ -129,12 +128,11 @@ static void add_instances_from_component( const int index = mod_i(original_index, std::max(src_instances_num, 1)); if (index < src_instances_num) { /* Get the reference to the source instance. */ - const int src_handle = src_instances->instance_reference_handles()[index]; + const int src_handle = src_instances->reference_handles()[index]; dst_handle = handle_mapping[src_handle]; /* Take transforms of the source instance into account. */ - mul_m4_m4_post(dst_transform.values, - src_instances->instance_transforms()[index].values); + mul_m4_m4_post(dst_transform.values, src_instances->transforms()[index].values); } } } @@ -157,7 +155,7 @@ static void add_instances_from_component( } } - bke::CustomDataAttributes &instance_attributes = dst_component.instance_attributes(); + bke::CustomDataAttributes &instance_attributes = dst_component.custom_data_attributes(); for (const auto item : attributes_to_propagate.items()) { const AttributeIDRef &attribute_id = item.key; const AttributeKind attribute_kind = item.value; @@ -196,7 +194,15 @@ static void node_geo_exec(GeoNodeExecParams params) instance.ensure_owns_direct_data(); geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { - InstancesComponent &instances = geometry_set.get_component_for_write(); + /* It's important not to invalidate the existing #InstancesComponent because it owns references + * to other geometry sets that are processed by this node. */ + InstancesComponent &instances_component = + geometry_set.get_component_for_write(); + bke::Instances *dst_instances = instances_component.get_for_write(); + if (dst_instances == nullptr) { + dst_instances = new bke::Instances(); + instances_component.replace(dst_instances); + } const Array types{ GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_CURVE}; @@ -208,14 +214,13 @@ static void node_geo_exec(GeoNodeExecParams params) for (const GeometryComponentType type : types) { if (geometry_set.has(type)) { - add_instances_from_component(instances, + add_instances_from_component(*dst_instances, *geometry_set.get_component_for_read(type), instance, params, attributes_to_propagate); } } - geometry_set.remove_geometry_during_modify(); }); @@ -223,8 +228,9 @@ static void node_geo_exec(GeoNodeExecParams params) * process them needlessly. * This should eventually be moved into the loop above, but currently this is quite tricky * because it might remove references that the loop still wants to iterate over. */ - InstancesComponent &instances = geometry_set.get_component_for_write(); - instances.remove_unused_references(); + if (bke::Instances *instances = geometry_set.get_instances_for_write()) { + instances->remove_unused_references(); + } params.set_output("Instances", std::move(geometry_set)); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc b/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc index d4072a05e5f..acd00d119ab 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc @@ -3,6 +3,7 @@ #include "DNA_pointcloud_types.h" #include "BKE_attribute_math.hh" +#include "BKE_instances.hh" #include "BKE_pointcloud.h" #include "node_geometry_util.hh" @@ -27,7 +28,7 @@ static void convert_instances_to_points(GeometrySet &geometry_set, Field radius_field, const Field selection_field) { - const InstancesComponent &instances = *geometry_set.get_component_for_read(); + const bke::Instances &instances = *geometry_set.get_instances_for_read(); const bke::InstancesFieldContext context{instances}; fn::FieldEvaluator evaluator{context, instances.instances_num()}; @@ -70,7 +71,7 @@ static void convert_instances_to_points(GeometrySet &geometry_set, const AttributeIDRef &attribute_id = item.key; const AttributeKind attribute_kind = item.value; - const GVArray src = instances.attributes()->lookup_or_default( + const GVArray src = instances.attributes().lookup_or_default( attribute_id, ATTR_DOMAIN_INSTANCE, attribute_kind.data_type); BLI_assert(src); GSpanAttributeWriter dst = point_attributes.lookup_or_add_for_write_only_span( 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 74d1b5561bb..ec9b9faf4ec 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc @@ -2,6 +2,8 @@ #include "GEO_realize_instances.hh" +#include "BKE_instances.hh" + #include "node_geometry_util.hh" namespace blender::nodes::node_geo_join_geometry_cc { @@ -97,31 +99,36 @@ static void join_attributes(Span src_components, static void join_components(Span src_components, GeometrySet &result) { - InstancesComponent &dst_component = result.get_component_for_write(); + std::unique_ptr dst_instances = std::make_unique(); int tot_instances = 0; for (const InstancesComponent *src_component : src_components) { - tot_instances += src_component->instances_num(); + tot_instances += src_component->get_for_read()->instances_num(); } - dst_component.reserve(tot_instances); + dst_instances->reserve(tot_instances); for (const InstancesComponent *src_component : src_components) { - Span src_references = src_component->references(); + const bke::Instances &src_instances = *src_component->get_for_read(); + + Span src_references = src_instances.references(); Array handle_map(src_references.size()); for (const int src_handle : src_references.index_range()) { - handle_map[src_handle] = dst_component.add_reference(src_references[src_handle]); + handle_map[src_handle] = dst_instances->add_reference(src_references[src_handle]); } - Span src_transforms = src_component->instance_transforms(); - Span src_reference_handles = src_component->instance_reference_handles(); + Span src_transforms = src_instances.transforms(); + Span src_reference_handles = src_instances.reference_handles(); for (const int i : src_transforms.index_range()) { const int src_handle = src_reference_handles[i]; const int dst_handle = handle_map[src_handle]; const float4x4 &transform = src_transforms[i]; - dst_component.add_instance(dst_handle, transform); + dst_instances->add_instance(dst_handle, transform); } } + + result.replace_instances(dst_instances.release()); + InstancesComponent &dst_component = result.get_component_for_write(); join_attributes(to_base_components(src_components), dst_component, {"position"}); } @@ -151,25 +158,23 @@ static void join_component_type(Span src_geometry_sets, GeometrySet return; } - GeometrySet instances_geometry_set; - InstancesComponent &instances = - instances_geometry_set.get_component_for_write(); - if constexpr (is_same_any_v) { join_components(components, result); } else { + std::unique_ptr instances = std::make_unique(); for (const Component *component : components) { GeometrySet tmp_geo; tmp_geo.add(*component); - const int handle = instances.add_reference(InstanceReference{tmp_geo}); - instances.add_instance(handle, float4x4::identity()); + const int handle = instances->add_reference(bke::InstanceReference{tmp_geo}); + instances->add_instance(handle, float4x4::identity()); } geometry::RealizeInstancesOptions options; options.keep_original_ids = true; options.realize_instance_attributes = false; - GeometrySet joined_components = geometry::realize_instances(instances_geometry_set, options); + GeometrySet joined_components = geometry::realize_instances( + GeometrySet::create_with_instances(instances.release()), options); result.add(joined_components.get_component_for_write()); } } diff --git a/source/blender/nodes/geometry/nodes/node_geo_object_info.cc b/source/blender/nodes/geometry/nodes/node_geo_object_info.cc index 3ce16fac464..bf064c6fcbe 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_object_info.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_object_info.cc @@ -3,6 +3,7 @@ #include "BLI_math_matrix.h" #include "BKE_geometry_set_instances.hh" +#include "BKE_instances.hh" #include "UI_interface.h" #include "UI_resources.h" @@ -68,14 +69,15 @@ static void node_geo_exec(GeoNodeExecParams params) GeometrySet geometry_set; if (params.get_input("As Instance")) { - InstancesComponent &instances = geometry_set.get_component_for_write(); - const int handle = instances.add_reference(*object); + std::unique_ptr instances = std::make_unique(); + const int handle = instances->add_reference(*object); if (transform_space_relative) { - instances.add_instance(handle, transform); + instances->add_instance(handle, transform); } else { - instances.add_instance(handle, float4x4::identity()); + instances->add_instance(handle, float4x4::identity()); } + geometry_set = GeometrySet::create_with_instances(instances.release()); } else { geometry_set = bke::object_get_evaluated_geometry_set(*object); diff --git a/source/blender/nodes/geometry/nodes/node_geo_rotate_instances.cc b/source/blender/nodes/geometry/nodes/node_geo_rotate_instances.cc index 4ed94e67e74..fac92a7500c 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_rotate_instances.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_rotate_instances.cc @@ -2,6 +2,8 @@ #include "BLI_task.hh" +#include "BKE_instances.hh" + #include "node_geometry_util.hh" namespace blender::nodes::node_geo_rotate_instances_cc { @@ -16,10 +18,10 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output(N_("Instances")); } -static void rotate_instances(GeoNodeExecParams ¶ms, InstancesComponent &instances_component) +static void rotate_instances(GeoNodeExecParams ¶ms, bke::Instances &instances) { - const bke::InstancesFieldContext context{instances_component}; - fn::FieldEvaluator evaluator{context, instances_component.instances_num()}; + const bke::InstancesFieldContext context{instances}; + fn::FieldEvaluator evaluator{context, instances.instances_num()}; evaluator.set_selection(params.extract_input>("Selection")); evaluator.add(params.extract_input>("Rotation")); evaluator.add(params.extract_input>("Pivot Point")); @@ -31,14 +33,14 @@ static void rotate_instances(GeoNodeExecParams ¶ms, InstancesComponent &inst const VArray pivots = evaluator.get_evaluated(1); const VArray local_spaces = evaluator.get_evaluated(2); - MutableSpan instance_transforms = instances_component.instance_transforms(); + MutableSpan transforms = instances.transforms(); threading::parallel_for(selection.index_range(), 512, [&](IndexRange range) { for (const int i_selection : range) { const int i = selection[i_selection]; const float3 pivot = pivots[i]; const float3 euler = rotations[i]; - float4x4 &instance_transform = instance_transforms[i]; + float4x4 &instance_transform = transforms[i]; float4x4 rotation_matrix; float3 used_pivot; @@ -81,9 +83,8 @@ static void rotate_instances(GeoNodeExecParams ¶ms, InstancesComponent &inst static void node_geo_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input("Instances"); - if (geometry_set.has_instances()) { - InstancesComponent &instances = geometry_set.get_component_for_write(); - rotate_instances(params, instances); + if (bke::Instances *instances = geometry_set.get_instances_for_write()) { + rotate_instances(params, *instances); } params.set_output("Instances", std::move(geometry_set)); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_scale_instances.cc b/source/blender/nodes/geometry/nodes/node_geo_scale_instances.cc index 21fe724e194..dacb130337f 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_scale_instances.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_scale_instances.cc @@ -2,6 +2,8 @@ #include "BLI_task.hh" +#include "BKE_instances.hh" + #include "node_geometry_util.hh" namespace blender::nodes::node_geo_scale_instances_cc { @@ -19,10 +21,10 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output(N_("Instances")); } -static void scale_instances(GeoNodeExecParams ¶ms, InstancesComponent &instances_component) +static void scale_instances(GeoNodeExecParams ¶ms, bke::Instances &instances) { - const bke::InstancesFieldContext context{instances_component}; - fn::FieldEvaluator evaluator{context, instances_component.instances_num()}; + const bke::InstancesFieldContext context{instances}; + fn::FieldEvaluator evaluator{context, instances.instances_num()}; evaluator.set_selection(params.extract_input>("Selection")); evaluator.add(params.extract_input>("Scale")); evaluator.add(params.extract_input>("Center")); @@ -34,13 +36,13 @@ static void scale_instances(GeoNodeExecParams ¶ms, InstancesComponent &insta const VArray pivots = evaluator.get_evaluated(1); const VArray local_spaces = evaluator.get_evaluated(2); - MutableSpan instance_transforms = instances_component.instance_transforms(); + MutableSpan transforms = instances.transforms(); threading::parallel_for(selection.index_range(), 512, [&](IndexRange range) { for (const int i_selection : range) { const int i = selection[i_selection]; const float3 pivot = pivots[i]; - float4x4 &instance_transform = instance_transforms[i]; + float4x4 &instance_transform = transforms[i]; if (local_spaces[i]) { instance_transform *= float4x4::from_location(pivot); @@ -61,9 +63,8 @@ static void scale_instances(GeoNodeExecParams ¶ms, InstancesComponent &insta static void node_geo_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input("Instances"); - if (geometry_set.has_instances()) { - InstancesComponent &instances = geometry_set.get_component_for_write(); - scale_instances(params, instances); + if (bke::Instances *instances = geometry_set.get_instances_for_write()) { + scale_instances(params, *instances); } params.set_output("Instances", std::move(geometry_set)); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc b/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc index bbdabc09099..769a63f58cf 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc @@ -6,6 +6,7 @@ #include "BKE_curve.h" #include "BKE_curve_legacy_convert.hh" #include "BKE_curves.hh" +#include "BKE_instances.hh" #include "BKE_vfont.h" #include "BLI_hash.h" @@ -270,7 +271,7 @@ static std::optional get_text_layout(GeoNodeExecParams ¶ms) /* Returns a mapping of UTF-32 character code to instance handle. */ static Map create_curve_instances(GeoNodeExecParams ¶ms, TextLayout &layout, - InstancesComponent &instances) + bke::Instances &instances) { VFont *vfont = reinterpret_cast(params.node().id); Map handles; @@ -315,13 +316,13 @@ static Map create_curve_instances(GeoNodeExecParams ¶ms, return handles; } -static void add_instances_from_handles(InstancesComponent &instances, +static void add_instances_from_handles(bke::Instances &instances, const Map &char_handles, const TextLayout &layout) { instances.resize(layout.positions.size()); - MutableSpan handles = instances.instance_reference_handles(); - MutableSpan transforms = instances.instance_transforms(); + MutableSpan handles = instances.reference_handles(); + MutableSpan transforms = instances.transforms(); threading::parallel_for(IndexRange(layout.positions.size()), 256, [&](IndexRange range) { for (const int i : range) { @@ -333,9 +334,9 @@ static void add_instances_from_handles(InstancesComponent &instances, static void create_attributes(GeoNodeExecParams ¶ms, const TextLayout &layout, - InstancesComponent &instances) + bke::Instances &instances) { - MutableAttributeAccessor attributes = *instances.attributes_for_write(); + MutableAttributeAccessor attributes = instances.attributes_for_write(); if (params.output_is_required("Line")) { StrongAnonymousAttributeID line_id = StrongAnonymousAttributeID("Line"); @@ -385,13 +386,12 @@ static void node_geo_exec(GeoNodeExecParams params) } /* Create and add instances. */ - GeometrySet geometry_set_out; - InstancesComponent &instances = geometry_set_out.get_component_for_write(); - Map char_handles = create_curve_instances(params, *layout, instances); - add_instances_from_handles(instances, char_handles, *layout); - create_attributes(params, *layout, instances); + std::unique_ptr instances = std::make_unique(); + Map char_handles = create_curve_instances(params, *layout, *instances); + add_instances_from_handles(*instances, char_handles, *layout); + create_attributes(params, *layout, *instances); - params.set_output("Curve Instances", std::move(geometry_set_out)); + params.set_output("Curve Instances", GeometrySet::create_with_instances(instances.release())); } } // namespace blender::nodes::node_geo_string_to_curves_cc diff --git a/source/blender/nodes/geometry/nodes/node_geo_transform.cc b/source/blender/nodes/geometry/nodes/node_geo_transform.cc index 4130cad3bda..3c8a3f3ca76 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_transform.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_transform.cc @@ -11,6 +11,7 @@ #include "DNA_volume_types.h" #include "BKE_curves.hh" +#include "BKE_instances.hh" #include "BKE_mesh.h" #include "BKE_pointcloud.h" #include "BKE_volume.h" @@ -67,18 +68,18 @@ static void transform_pointcloud(PointCloud &pointcloud, const float4x4 &transfo position.finish(); } -static void translate_instances(InstancesComponent &instances, const float3 translation) +static void translate_instances(bke::Instances &instances, const float3 translation) { - MutableSpan transforms = instances.instance_transforms(); + MutableSpan transforms = instances.transforms(); for (float4x4 &transform : transforms) { add_v3_v3(transform.ptr()[3], translation); } } -static void transform_instances(InstancesComponent &instances, const float4x4 &transform) +static void transform_instances(bke::Instances &instances, const float4x4 &transform) { - MutableSpan instance_transforms = instances.instance_transforms(); - for (float4x4 &instance_transform : instance_transforms) { + MutableSpan transforms = instances.transforms(); + for (float4x4 &instance_transform : transforms) { instance_transform = transform * instance_transform; } } @@ -185,8 +186,8 @@ static void translate_geometry_set(GeoNodeExecParams ¶ms, if (Volume *volume = geometry.get_volume_for_write()) { translate_volume(params, *volume, translation, depsgraph); } - if (geometry.has_instances()) { - translate_instances(geometry.get_component_for_write(), translation); + if (bke::Instances *instances = geometry.get_instances_for_write()) { + translate_instances(*instances, translation); } if (bke::CurvesEditHints *curve_edit_hints = geometry.get_curve_edit_hints_for_write()) { translate_curve_edit_hints(*curve_edit_hints, translation); @@ -210,8 +211,8 @@ void transform_geometry_set(GeoNodeExecParams ¶ms, if (Volume *volume = geometry.get_volume_for_write()) { transform_volume(params, *volume, transform, depsgraph); } - if (geometry.has_instances()) { - transform_instances(geometry.get_component_for_write(), transform); + if (bke::Instances *instances = geometry.get_instances_for_write()) { + transform_instances(*instances, transform); } if (bke::CurvesEditHints *curve_edit_hints = geometry.get_curve_edit_hints_for_write()) { transform_curve_edit_hints(*curve_edit_hints, transform); diff --git a/source/blender/nodes/geometry/nodes/node_geo_translate_instances.cc b/source/blender/nodes/geometry/nodes/node_geo_translate_instances.cc index 3e9fe99adb0..23052abddc4 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_translate_instances.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_translate_instances.cc @@ -2,6 +2,8 @@ #include "BLI_task.hh" +#include "BKE_instances.hh" + #include "node_geometry_util.hh" namespace blender::nodes::node_geo_translate_instances_cc { @@ -15,10 +17,10 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output(N_("Instances")); } -static void translate_instances(GeoNodeExecParams ¶ms, InstancesComponent &instances_component) +static void translate_instances(GeoNodeExecParams ¶ms, bke::Instances &instances) { - const bke::InstancesFieldContext context{instances_component}; - fn::FieldEvaluator evaluator{context, instances_component.instances_num()}; + const bke::InstancesFieldContext context{instances}; + fn::FieldEvaluator evaluator{context, instances.instances_num()}; evaluator.set_selection(params.extract_input>("Selection")); evaluator.add(params.extract_input>("Translation")); evaluator.add(params.extract_input>("Local Space")); @@ -28,16 +30,16 @@ static void translate_instances(GeoNodeExecParams ¶ms, InstancesComponent &i const VArray translations = evaluator.get_evaluated(0); const VArray local_spaces = evaluator.get_evaluated(1); - MutableSpan instance_transforms = instances_component.instance_transforms(); + MutableSpan transforms = instances.transforms(); threading::parallel_for(selection.index_range(), 1024, [&](IndexRange range) { for (const int i_selection : range) { const int i = selection[i_selection]; if (local_spaces[i]) { - instance_transforms[i] *= float4x4::from_location(translations[i]); + transforms[i] *= float4x4::from_location(translations[i]); } else { - add_v3_v3(instance_transforms[i].values[3], translations[i]); + add_v3_v3(transforms[i].values[3], translations[i]); } } }); @@ -46,9 +48,8 @@ static void translate_instances(GeoNodeExecParams ¶ms, InstancesComponent &i static void node_geo_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input("Instances"); - if (geometry_set.has_instances()) { - InstancesComponent &instances = geometry_set.get_component_for_write(); - translate_instances(params, instances); + if (bke::Instances *instances = geometry_set.get_instances_for_write()) { + translate_instances(params, *instances); } params.set_output("Instances", std::move(geometry_set)); } diff --git a/source/blender/nodes/intern/geometry_nodes_log.cc b/source/blender/nodes/intern/geometry_nodes_log.cc index 167bfef0f83..0f122307328 100644 --- a/source/blender/nodes/intern/geometry_nodes_log.cc +++ b/source/blender/nodes/intern/geometry_nodes_log.cc @@ -101,7 +101,7 @@ GeometryInfoLog::GeometryInfoLog(const GeometrySet &geometry_set) case GEO_COMPONENT_TYPE_INSTANCES: { const InstancesComponent &instances_component = *(const InstancesComponent *)component; InstancesInfo &info = this->instances_info.emplace(); - info.instances_num = instances_component.instances_num(); + info.instances_num = instances_component.attribute_domain_size(ATTR_DOMAIN_INSTANCE); break; } case GEO_COMPONENT_TYPE_EDIT: { -- cgit v1.2.3