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

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJacques Lucke <jacques@blender.org>2022-10-17 12:39:40 +0300
committerJacques Lucke <jacques@blender.org>2022-10-17 12:39:58 +0300
commite5425b566d0f25f60b5895c4025c183fd67c7d9c (patch)
treef38e7e86377709874daf17d86786581a45c7b75a /source/blender/blenkernel
parentdb40b62252e5a7716cd403a0574cc164163b2ce9 (diff)
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
Diffstat (limited to 'source/blender/blenkernel')
-rw-r--r--source/blender/blenkernel/BKE_geometry_fields.hh13
-rw-r--r--source/blender/blenkernel/BKE_geometry_set.hh251
-rw-r--r--source/blender/blenkernel/BKE_instances.hh270
-rw-r--r--source/blender/blenkernel/CMakeLists.txt2
-rw-r--r--source/blender/blenkernel/intern/geometry_component_instances.cc385
-rw-r--r--source/blender/blenkernel/intern/geometry_fields.cc16
-rw-r--r--source/blender/blenkernel/intern/geometry_set.cc58
-rw-r--r--source/blender/blenkernel/intern/geometry_set_instances.cc45
-rw-r--r--source/blender/blenkernel/intern/instances.cc348
-rw-r--r--source/blender/blenkernel/intern/object_dupli.cc17
10 files changed, 812 insertions, 593 deletions
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. */
/**
@@ -294,6 +301,10 @@ struct GeometrySet {
*/
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.
*/
const blender::bke::CurvesEditHints *get_curve_edit_hints_for_read() const;
@@ -315,6 +326,10 @@ struct GeometrySet {
*/
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.
*/
blender::bke::CurvesEditHints *get_curve_edit_hints_for_write();
@@ -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<GeometrySet> 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<GeometrySet>(std::move(geometry_set)))
- {
- }
-
- InstanceReference(const InstanceReference &other) : type_(other.type_), data_(other.data_)
- {
- if (other.geometry_set_) {
- geometry_set_ = std::make_unique<GeometrySet>(*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<InstanceReference> references_;
-
- /** Index into `references_`. Determines what data is instanced. */
- blender::Vector<int> instance_reference_handles_;
- /** Transformation of the instances. */
- blender::Vector<blender::float4x4> 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<int> 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<InstanceReference> 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<int> instance_reference_handles() const;
- blender::MutableSpan<int> instance_reference_handles();
- blender::MutableSpan<blender::float4x4> instance_transforms();
- blender::Span<blender::float4x4> 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<int> almost_unique_ids() const;
-
- blender::bke::CustomDataAttributes &instance_attributes();
- const blender::bke::CustomDataAttributes &instance_attributes() const;
-
- std::optional<blender::bke::AttributeAccessor> attributes() const final;
- std::optional<blender::bke::MutableAttributeAccessor> attributes_for_write() final;
-
- void foreach_referenced_geometry(
- blender::FunctionRef<void(const GeometrySet &geometry_set)> 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<blender::bke::AttributeAccessor> attributes() const final;
+ std::optional<blender::bke::MutableAttributeAccessor> 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 <mutex>
+
+#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<GeometrySet> 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<InstanceReference> references_;
+
+ /** Indices into `references_`. Determines what data is instanced. */
+ blender::Vector<int> reference_handles_;
+ /** Transformation of the instances. */
+ blender::Vector<blender::float4x4> 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<int> 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<InstanceReference> 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<int> reference_handles() const;
+ blender::MutableSpan<int> reference_handles();
+ blender::MutableSpan<blender::float4x4> transforms();
+ blender::Span<blender::float4x4> 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<int> 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<void(const GeometrySet &geometry_set)> 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<GeometrySet>(*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<int> InstancesComponent::instance_reference_handles() const
-{
- return instance_reference_handles_;
-}
-
-blender::MutableSpan<int> InstancesComponent::instance_reference_handles()
-{
- return instance_reference_handles_;
-}
-
-blender::MutableSpan<blender::float4x4> InstancesComponent::instance_transforms()
-{
- return instance_transforms_;
-}
-blender::Span<blender::float4x4> 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<GeometrySet &>(references_[reference_index].geometry_set());
-}
-
-int InstancesComponent::add_reference(const InstanceReference &reference)
-{
- return references_.index_of_or_add_as(reference);
-}
-
-blender::Span<InstanceReference> InstancesComponent::references() const
-{
- return references_;
-}
-
-template<typename T>
-static void copy_data_based_on_mask(Span<T> src, MutableSpan<T> 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<int> new_handles(mask.size());
- copy_data_based_on_mask<int>(this->instance_reference_handles(), new_handles, mask);
- instance_reference_handles_ = std::move(new_handles);
- Vector<float4x4> new_transforms(mask.size());
- copy_data_based_on_mask<float4x4>(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<T>(src.typed<T>(), dst.typed<T>(), 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<bool> 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<bool> 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<int> handle_mapping;
- VectorSet<InstanceReference> 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<InstanceReference &>(const_reference);
- reference.ensure_owns_direct_data();
+ if (instances_ != nullptr) {
+ instances_->ensure_owns_direct_data();
}
}
-static blender::Array<int> generate_unique_instance_ids(Span<int> original_ids)
+const blender::bke::Instances *InstancesComponent::get_for_read() const
{
- using namespace blender;
- Array<int> unique_ids(original_ids.size());
-
- Set<int> used_unique_ids;
- used_unique_ids.reserve(original_ids.size());
- Vector<int> 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<int, RandomNumberGenerator> 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<int> InstancesComponent::almost_unique_ids() const
+blender::bke::Instances *InstancesComponent::get_for_write()
{
- std::lock_guard lock(almost_unique_ids_mutex_);
- std::optional<GSpan> instance_ids_gspan = attributes_.get_for_read("id");
- if (instance_ids_gspan) {
- Span<int> instance_ids = instance_ids_gspan->typed<int>();
- 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<const InstancesComponent *>(
- owner);
- Span<float4x4> transforms = instances_component.instance_transforms();
+ const Instances *instances = static_cast<const Instances *>(owner);
+ if (instances == nullptr) {
+ return {};
+ }
+ Span<float4x4> transforms = instances->transforms();
return VArray<float3>::ForDerivedSpan<float4x4, get_transform_position>(transforms);
}
GAttributeWriter try_get_for_write(void *owner) const final
{
- InstancesComponent &instances_component = *static_cast<InstancesComponent *>(owner);
- MutableSpan<float4x4> transforms = instances_component.instance_transforms();
+ Instances *instances = static_cast<Instances *>(owner);
+ if (instances == nullptr) {
+ return {};
+ }
+ MutableSpan<float4x4> transforms = instances->transforms();
return {VMutableArray<float3>::ForDerivedSpan<float4x4,
get_transform_position,
set_transform_position>(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<InstancesComponent *>(owner);
- return &inst.instance_attributes().data;
+ Instances *instances = static_cast<Instances *>(owner);
+ return &instances->custom_data_attributes().data;
},
[](const void *owner) -> const CustomData * {
- const InstancesComponent &inst = *static_cast<const InstancesComponent *>(owner);
- return &inst.instance_attributes().data;
+ const Instances *instances = static_cast<const Instances *>(owner);
+ return &instances->custom_data_attributes().data;
},
[](const void *owner) -> int {
- const InstancesComponent &inst = *static_cast<const InstancesComponent *>(owner);
- return inst.instances_num();
+ const Instances *instances = static_cast<const Instances *>(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<const InstancesComponent *>(owner);
+ const Instances *instances = static_cast<const Instances *>(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<blender::bke::AttributeAccessor> InstancesComponent::attributes() const
{
- return blender::bke::AttributeAccessor(this,
+ return blender::bke::AttributeAccessor(instances_,
blender::bke::get_instances_accessor_functions_ref());
}
std::optional<blender::bke::MutableAttributeAccessor> 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<const InstancesComponent &>(
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<AttributeAccessor> 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<const PointCloud *>(geometry_) :
nullptr;
}
-const InstancesComponent *GeometryFieldContext::instances() const
+const Instances *GeometryFieldContext::instances() const
{
- return this->type() == GEO_COMPONENT_TYPE_INSTANCES ?
- static_cast<const InstancesComponent *>(geometry_) :
- nullptr;
+ return this->type() == GEO_COMPONENT_TYPE_INSTANCES ? static_cast<const Instances *>(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<const GeometryFieldContext *>(
&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<InstancesComponent>()->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<InstancesComponent>();
+ 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<InstancesComponent>();
- 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<InstancesComponent>();
+ return;
+ }
+ if (instances == this->get_instances_for_read()) {
+ return;
+ }
+ this->remove<InstancesComponent>();
+ InstancesComponent &component = this->get_component_for_write<InstancesComponent>();
+ 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<InstancesComponent>();
+ return component == nullptr ? nullptr : component->get_for_write();
+}
+
blender::bke::CurvesEditHints *GeometrySet::get_curve_edit_hints_for_write()
{
if (!this->has<GeometryComponentEditData>()) {
@@ -539,7 +576,7 @@ void GeometrySet::attribute_foreach(const Span<GeometryComponentType> component_
}
}
if (include_instances && this->has_instances()) {
- const InstancesComponent &instances = *this->get_component_for_read<InstancesComponent>();
+ 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<InstancesComponent>();
+ 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<InstancesComponent>();
- 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<InstancesComponent>();
- const int handle = instances.add_reference(collection);
- instances.add_instance(handle, float4x4::identity());
- return geometry_set;
+ std::unique_ptr<Instances> instances = std::make_unique<Instances>();
+ 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<InstancesComponent>();
+ const Instances &instances = *geometry_set.get_instances_for_read();
- Span<float4x4> transforms = instances_component.instance_transforms();
- Span<int> handles = instances_component.instance_reference_handles();
- Span<InstanceReference> references = instances_component.references();
+ Span<float4x4> transforms = instances.transforms();
+ Span<int> handles = instances.reference_handles();
+ Span<InstanceReference> 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<void(const GeometrySet &geometry_set)> 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<InstancesComponent>();
- 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<InstancesComponent>();
+ std::unique_ptr<Instances> instances = std::make_unique<Instances>();
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<GeometrySet>(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<int> Instances::reference_handles() const
+{
+ return reference_handles_;
+}
+
+blender::MutableSpan<int> Instances::reference_handles()
+{
+ return reference_handles_;
+}
+
+blender::MutableSpan<blender::float4x4> Instances::transforms()
+{
+ return transforms_;
+}
+blender::Span<blender::float4x4> 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<GeometrySet &>(references_[reference_index].geometry_set());
+}
+
+int Instances::add_reference(const InstanceReference &reference)
+{
+ return references_.index_of_or_add_as(reference);
+}
+
+blender::Span<InstanceReference> Instances::references() const
+{
+ return references_;
+}
+
+template<typename T>
+static void copy_data_based_on_mask(Span<T> src, MutableSpan<T> 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<int> new_handles(mask.size());
+ copy_data_based_on_mask<int>(this->reference_handles(), new_handles, mask);
+ reference_handles_ = std::move(new_handles);
+ Vector<float4x4> new_transforms(mask.size());
+ copy_data_based_on_mask<float4x4>(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<T>(src.typed<T>(), dst.typed<T>(), 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<bool> 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<bool> 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<int> handle_mapping;
+ VectorSet<InstanceReference> 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<InstanceReference &>(const_reference);
+ reference.ensure_owns_direct_data();
+ }
+}
+
+static blender::Array<int> generate_unique_instance_ids(Span<int> original_ids)
+{
+ using namespace blender;
+ Array<int> unique_ids(original_ids.size());
+
+ Set<int> used_unique_ids;
+ used_unique_ids.reserve(original_ids.size());
+ Vector<int> 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<int, RandomNumberGenerator> 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<int> Instances::almost_unique_ids() const
+{
+ std::lock_guard lock(almost_unique_ids_mutex_);
+ std::optional<GSpan> instance_ids_gspan = attributes_.get_for_read("id");
+ if (instance_ids_gspan) {
+ Span<int> instance_ids = instance_ids_gspan->typed<int>();
+ 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<InstancesComponent>();
- 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<float4x4> instance_offset_matrices = component->instance_transforms();
- Span<int> instance_reference_handles = component->instance_reference_handles();
- Span<int> almost_unique_ids = component->almost_unique_ids();
- Span<InstanceReference> references = component->references();
+ Span<float4x4> instance_offset_matrices = instances->transforms();
+ Span<int> reference_handles = instances->reference_handles();
+ Span<int> almost_unique_ids = instances->almost_unique_ids();
+ Span<InstanceReference> 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;