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
This commit is contained in:
@@ -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 {
|
||||
|
||||
@@ -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<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;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
270
source/blender/blenkernel/BKE_instances.hh
Normal file
270
source/blender/blenkernel/BKE_instances.hh
Normal file
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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_
|
||||
{
|
||||
}
|
||||
|
||||
InstancesComponent::~InstancesComponent()
|
||||
{
|
||||
this->clear();
|
||||
}
|
||||
|
||||
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_;
|
||||
if (instances_ != nullptr) {
|
||||
new_component->instances_ = new Instances(*instances_);
|
||||
new_component->ownership_ = GeometryOwnershipType::Owned;
|
||||
}
|
||||
return new_component;
|
||||
}
|
||||
|
||||
void InstancesComponent::reserve(int min_capacity)
|
||||
{
|
||||
instance_reference_handles_.reserve(min_capacity);
|
||||
instance_transforms_.reserve(min_capacity);
|
||||
attributes_.reallocate(min_capacity);
|
||||
}
|
||||
|
||||
void InstancesComponent::resize(int capacity)
|
||||
{
|
||||
instance_reference_handles_.resize(capacity);
|
||||
instance_transforms_.resize(capacity);
|
||||
attributes_.reallocate(capacity);
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
void InstancesComponent::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 = 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++;
|
||||
}
|
||||
}
|
||||
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();
|
||||
instances_ = nullptr;
|
||||
}
|
||||
|
||||
bool InstancesComponent::is_empty() const
|
||||
{
|
||||
return this->instance_reference_handles_.size() == 0;
|
||||
}
|
||||
|
||||
bool InstancesComponent::owns_direct_data() const
|
||||
{
|
||||
for (const InstanceReference &reference : references_) {
|
||||
if (!reference.owns_direct_data()) {
|
||||
if (instances_ != nullptr) {
|
||||
if (instances_->instances_num() > 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InstancesComponent::owns_direct_data() const
|
||||
{
|
||||
if (instances_ != nullptr) {
|
||||
return instances_->owns_direct_data();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void InstancesComponent::ensure_owns_direct_data()
|
||||
{
|
||||
if (instances_ != nullptr) {
|
||||
instances_->ensure_owns_direct_data();
|
||||
}
|
||||
}
|
||||
|
||||
const blender::bke::Instances *InstancesComponent::get_for_read() const
|
||||
{
|
||||
return instances_;
|
||||
}
|
||||
|
||||
blender::bke::Instances *InstancesComponent::get_for_write()
|
||||
{
|
||||
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 (ownership_ == GeometryOwnershipType::ReadOnly) {
|
||||
instances_ = new Instances(*instances_);
|
||||
ownership_ = GeometryOwnershipType::Owned;
|
||||
}
|
||||
return instances_;
|
||||
}
|
||||
|
||||
static blender::Array<int> generate_unique_instance_ids(Span<int> original_ids)
|
||||
void InstancesComponent::replace(Instances *instances, GeometryOwnershipType ownership)
|
||||
{
|
||||
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> InstancesComponent::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_;
|
||||
}
|
||||
|
||||
blender::bke::CustomDataAttributes &InstancesComponent::instance_attributes()
|
||||
{
|
||||
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());
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
348
source/blender/blenkernel/intern/instances.cc
Normal file
348
source/blender/blenkernel/intern/instances.cc
Normal file
@@ -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
|
||||
@@ -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;
|
||||
|
||||
@@ -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<std::string>()) {
|
||||
return SPREADSHEET_VALUE_TYPE_STRING;
|
||||
}
|
||||
if (type.is<InstanceReference>()) {
|
||||
if (type.is<bke::InstanceReference>()) {
|
||||
return SPREADSHEET_VALUE_TYPE_INSTANCES;
|
||||
}
|
||||
if (type.is<ColorGeometry4b>()) {
|
||||
|
||||
@@ -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<ColumnValues> GeometryDataSource::get_column_values(
|
||||
}
|
||||
|
||||
if (component_->type() == GEO_COMPONENT_TYPE_INSTANCES) {
|
||||
const InstancesComponent &instances = static_cast<const InstancesComponent &>(*component_);
|
||||
if (STREQ(column_id.name, "Name")) {
|
||||
Span<int> reference_handles = instances.instance_reference_handles();
|
||||
Span<InstanceReference> references = instances.references();
|
||||
return std::make_unique<ColumnValues>(
|
||||
column_id.name,
|
||||
VArray<InstanceReference>::ForFunc(domain_num,
|
||||
[reference_handles, references](int64_t index) {
|
||||
return references[reference_handles[index]];
|
||||
}));
|
||||
}
|
||||
Span<float4x4> transforms = instances.instance_transforms();
|
||||
if (STREQ(column_id.name, "Rotation")) {
|
||||
return std::make_unique<ColumnValues>(
|
||||
column_id.name, VArray<float3>::ForFunc(domain_num, [transforms](int64_t index) {
|
||||
return transforms[index].to_euler();
|
||||
}));
|
||||
}
|
||||
if (STREQ(column_id.name, "Scale")) {
|
||||
return std::make_unique<ColumnValues>(
|
||||
column_id.name, VArray<float3>::ForFunc(domain_num, [transforms](int64_t index) {
|
||||
return transforms[index].scale();
|
||||
}));
|
||||
if (const bke::Instances *instances =
|
||||
static_cast<const InstancesComponent &>(*component_).get_for_read()) {
|
||||
if (STREQ(column_id.name, "Name")) {
|
||||
Span<int> reference_handles = instances->reference_handles();
|
||||
Span<bke::InstanceReference> references = instances->references();
|
||||
return std::make_unique<ColumnValues>(
|
||||
column_id.name,
|
||||
VArray<bke::InstanceReference>::ForFunc(
|
||||
domain_num, [reference_handles, references](int64_t index) {
|
||||
return references[reference_handles[index]];
|
||||
}));
|
||||
}
|
||||
Span<float4x4> transforms = instances->transforms();
|
||||
if (STREQ(column_id.name, "Rotation")) {
|
||||
return std::make_unique<ColumnValues>(
|
||||
column_id.name, VArray<float3>::ForFunc(domain_num, [transforms](int64_t index) {
|
||||
return transforms[index].to_euler();
|
||||
}));
|
||||
}
|
||||
if (STREQ(column_id.name, "Scale")) {
|
||||
return std::make_unique<ColumnValues>(
|
||||
column_id.name, VArray<float3>::ForFunc(domain_num, [transforms](int64_t index) {
|
||||
return transforms[index].scale();
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (G.debug_value == 4001 && component_->type() == GEO_COMPONENT_TYPE_MESH) {
|
||||
|
||||
@@ -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<ColorGeometry4b>(real_index);
|
||||
this->draw_byte_color(params, value);
|
||||
}
|
||||
else if (data.type().is<InstanceReference>()) {
|
||||
const InstanceReference value = data.get<InstanceReference>(real_index);
|
||||
else if (data.type().is<bke::InstanceReference>()) {
|
||||
const bke::InstanceReference value = data.get<bke::InstanceReference>(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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<InstanceReference>()) {
|
||||
else if (column_data.type().is<bke::InstanceReference>()) {
|
||||
const StringRef value = row_filter.value_string;
|
||||
apply_filter_operation(
|
||||
column_data.typed<InstanceReference>(),
|
||||
[&](const InstanceReference cell) {
|
||||
column_data.typed<bke::InstanceReference>(),
|
||||
[&](const bke::InstanceReference cell) {
|
||||
switch (cell.type()) {
|
||||
case InstanceReference::Type::Object: {
|
||||
case bke::InstanceReference::Type::Object: {
|
||||
return value == (reinterpret_cast<ID &>(cell.object()).name + 2);
|
||||
}
|
||||
case InstanceReference::Type::Collection: {
|
||||
case bke::InstanceReference::Type::Collection: {
|
||||
return value == (reinterpret_cast<ID &>(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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<std::pair<int, GSpan>> prepare_attribute_fallbacks(
|
||||
GatherTasksInfo &gather_info,
|
||||
const InstancesComponent &instances_component,
|
||||
const Instances &instances,
|
||||
const OrderedAttributes &ordered_attributes)
|
||||
{
|
||||
Vector<std::pair<int, GSpan>> 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<std::pair<int, GSpan>> prepare_attribute_fallbacks(
|
||||
}
|
||||
/* Convert the attribute on the instances component to the expected attribute type. */
|
||||
std::unique_ptr<GArray<>> temporary_array = std::make_unique<GArray<>>(
|
||||
to_type, instances_component.instances_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<InstanceReference> references = instances_component.references();
|
||||
const Span<int> handles = instances_component.instance_reference_handles();
|
||||
const Span<float4x4> transforms = instances_component.instance_transforms();
|
||||
const Span<InstanceReference> references = instances.references();
|
||||
const Span<int> handles = instances.reference_handles();
|
||||
const Span<float4x4> transforms = instances.transforms();
|
||||
|
||||
Span<int> stored_instance_ids;
|
||||
if (gather_info.create_id_attribute_on_any_component) {
|
||||
std::optional<GSpan> ids = instances_component.instance_attributes().get_for_read("id");
|
||||
std::optional<GSpan> ids = instances.custom_data_attributes().get_for_read("id");
|
||||
if (ids.has_value()) {
|
||||
stored_instance_ids = ids->typed<int>();
|
||||
}
|
||||
@@ -469,11 +472,11 @@ static void gather_realize_tasks_for_instances(GatherTasksInfo &gather_info,
|
||||
/* Prepare attribute fallbacks. */
|
||||
InstanceContext instance_context = base_instance_context;
|
||||
Vector<std::pair<int, GSpan>> pointcloud_attributes_to_override = prepare_attribute_fallbacks(
|
||||
gather_info, instances_component, gather_info.pointclouds.attributes);
|
||||
gather_info, instances, gather_info.pointclouds.attributes);
|
||||
Vector<std::pair<int, GSpan>> mesh_attributes_to_override = prepare_attribute_fallbacks(
|
||||
gather_info, instances_component, gather_info.meshes.attributes);
|
||||
gather_info, instances, gather_info.meshes.attributes);
|
||||
Vector<std::pair<int, GSpan>> 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<const InstancesComponent *>(
|
||||
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<InstancesComponent>()) {
|
||||
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<InstancesComponent>()) {
|
||||
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<InstancesComponent>()) {
|
||||
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>()) {
|
||||
InstancesComponent &component = sub_geometry.get_component_for_write<InstancesComponent>();
|
||||
component.instance_attributes().remove("id");
|
||||
if (Instances *instances = sub_geometry.get_instances_for_write()) {
|
||||
instances->custom_data_attributes().remove("id");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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<InstancesComponent>();
|
||||
std::unique_ptr<bke::Instances> instances = std::make_unique<bke::Instances>();
|
||||
|
||||
const bool separate_children = params.get_input<bool>("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<InstanceListEntry> 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
|
||||
|
||||
@@ -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<bool> &selection_field)
|
||||
{
|
||||
InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>();
|
||||
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<bool> vertex_selection,
|
||||
|
||||
@@ -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<InstancesComponent>();
|
||||
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<InstancesComponent>();
|
||||
dst_instances.resize(offsets.last());
|
||||
std::unique_ptr<bke::Instances> dst_instances = std::make_unique<bke::Instances>();
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
@@ -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<GeometrySet> geometries = params.extract_input<Vector<GeometrySet>>("Geometry");
|
||||
GeometrySet instances_geometry;
|
||||
InstancesComponent &instances_component =
|
||||
instances_geometry.get_component_for_write<InstancesComponent>();
|
||||
std::unique_ptr<bke::Instances> instances = std::make_unique<bke::Instances>();
|
||||
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
|
||||
|
||||
@@ -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<float3>::ForFunc(instances.instances_num(), rotation_fn);
|
||||
}
|
||||
|
||||
@@ -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<float3>::ForFunc(instances.instances_num(), scale_fn);
|
||||
}
|
||||
|
||||
@@ -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<int> dst_handles = dst_component.instance_reference_handles().slice(start_len,
|
||||
select_len);
|
||||
MutableSpan<float4x4> dst_transforms = dst_component.instance_transforms().slice(start_len,
|
||||
select_len);
|
||||
MutableSpan<int> dst_handles = dst_component.reference_handles().slice(start_len, select_len);
|
||||
MutableSpan<float4x4> dst_transforms = dst_component.transforms().slice(start_len, select_len);
|
||||
|
||||
VArray<float3> positions = src_component.attributes()->lookup_or_default<float3>(
|
||||
"position", domain, {0, 0, 0});
|
||||
|
||||
const InstancesComponent *src_instances = instance.get_component_for_read<InstancesComponent>();
|
||||
const bke::Instances *src_instances = instance.get_instances_for_read();
|
||||
|
||||
/* Maps handles from the source instances to handles on the new instance. */
|
||||
Array<int> 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<InstanceReference> src_references = src_instances->references();
|
||||
Span<bke::InstanceReference> 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<InstancesComponent>();
|
||||
/* 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<InstancesComponent>();
|
||||
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<GeometryComponentType> 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<InstancesComponent>();
|
||||
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));
|
||||
}
|
||||
|
||||
@@ -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<float> radius_field,
|
||||
const Field<bool> selection_field)
|
||||
{
|
||||
const InstancesComponent &instances = *geometry_set.get_component_for_read<InstancesComponent>();
|
||||
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(
|
||||
|
||||
@@ -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<const GeometryComponent *> src_components,
|
||||
|
||||
static void join_components(Span<const InstancesComponent *> src_components, GeometrySet &result)
|
||||
{
|
||||
InstancesComponent &dst_component = result.get_component_for_write<InstancesComponent>();
|
||||
std::unique_ptr<bke::Instances> dst_instances = std::make_unique<bke::Instances>();
|
||||
|
||||
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<InstanceReference> src_references = src_component->references();
|
||||
const bke::Instances &src_instances = *src_component->get_for_read();
|
||||
|
||||
Span<bke::InstanceReference> src_references = src_instances.references();
|
||||
Array<int> 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<float4x4> src_transforms = src_component->instance_transforms();
|
||||
Span<int> src_reference_handles = src_component->instance_reference_handles();
|
||||
Span<float4x4> src_transforms = src_instances.transforms();
|
||||
Span<int> 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<InstancesComponent>();
|
||||
join_attributes(to_base_components(src_components), dst_component, {"position"});
|
||||
}
|
||||
|
||||
@@ -151,25 +158,23 @@ static void join_component_type(Span<GeometrySet> src_geometry_sets, GeometrySet
|
||||
return;
|
||||
}
|
||||
|
||||
GeometrySet instances_geometry_set;
|
||||
InstancesComponent &instances =
|
||||
instances_geometry_set.get_component_for_write<InstancesComponent>();
|
||||
|
||||
if constexpr (is_same_any_v<Component, InstancesComponent, VolumeComponent>) {
|
||||
join_components(components, result);
|
||||
}
|
||||
else {
|
||||
std::unique_ptr<bke::Instances> instances = std::make_unique<bke::Instances>();
|
||||
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<Component>());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<bool>("As Instance")) {
|
||||
InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>();
|
||||
const int handle = instances.add_reference(*object);
|
||||
std::unique_ptr<bke::Instances> instances = std::make_unique<bke::Instances>();
|
||||
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);
|
||||
|
||||
@@ -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<decl::Geometry>(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<Field<bool>>("Selection"));
|
||||
evaluator.add(params.extract_input<Field<float3>>("Rotation"));
|
||||
evaluator.add(params.extract_input<Field<float3>>("Pivot Point"));
|
||||
@@ -31,14 +33,14 @@ static void rotate_instances(GeoNodeExecParams ¶ms, InstancesComponent &inst
|
||||
const VArray<float3> pivots = evaluator.get_evaluated<float3>(1);
|
||||
const VArray<bool> local_spaces = evaluator.get_evaluated<bool>(2);
|
||||
|
||||
MutableSpan<float4x4> instance_transforms = instances_component.instance_transforms();
|
||||
MutableSpan<float4x4> 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<GeometrySet>("Instances");
|
||||
if (geometry_set.has_instances()) {
|
||||
InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>();
|
||||
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));
|
||||
}
|
||||
|
||||
@@ -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<decl::Geometry>(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<Field<bool>>("Selection"));
|
||||
evaluator.add(params.extract_input<Field<float3>>("Scale"));
|
||||
evaluator.add(params.extract_input<Field<float3>>("Center"));
|
||||
@@ -34,13 +36,13 @@ static void scale_instances(GeoNodeExecParams ¶ms, InstancesComponent &insta
|
||||
const VArray<float3> pivots = evaluator.get_evaluated<float3>(1);
|
||||
const VArray<bool> local_spaces = evaluator.get_evaluated<bool>(2);
|
||||
|
||||
MutableSpan<float4x4> instance_transforms = instances_component.instance_transforms();
|
||||
MutableSpan<float4x4> 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<GeometrySet>("Instances");
|
||||
if (geometry_set.has_instances()) {
|
||||
InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>();
|
||||
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));
|
||||
}
|
||||
|
||||
@@ -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<TextLayout> get_text_layout(GeoNodeExecParams ¶ms)
|
||||
/* Returns a mapping of UTF-32 character code to instance handle. */
|
||||
static Map<int, int> create_curve_instances(GeoNodeExecParams ¶ms,
|
||||
TextLayout &layout,
|
||||
InstancesComponent &instances)
|
||||
bke::Instances &instances)
|
||||
{
|
||||
VFont *vfont = reinterpret_cast<VFont *>(params.node().id);
|
||||
Map<int, int> handles;
|
||||
@@ -315,13 +316,13 @@ static Map<int, int> 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<int, int> &char_handles,
|
||||
const TextLayout &layout)
|
||||
{
|
||||
instances.resize(layout.positions.size());
|
||||
MutableSpan<int> handles = instances.instance_reference_handles();
|
||||
MutableSpan<float4x4> transforms = instances.instance_transforms();
|
||||
MutableSpan<int> handles = instances.reference_handles();
|
||||
MutableSpan<float4x4> 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<InstancesComponent>();
|
||||
Map<int, int> char_handles = create_curve_instances(params, *layout, instances);
|
||||
add_instances_from_handles(instances, char_handles, *layout);
|
||||
create_attributes(params, *layout, instances);
|
||||
std::unique_ptr<bke::Instances> instances = std::make_unique<bke::Instances>();
|
||||
Map<int, int> 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
|
||||
|
||||
@@ -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<float4x4> transforms = instances.instance_transforms();
|
||||
MutableSpan<float4x4> 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<float4x4> instance_transforms = instances.instance_transforms();
|
||||
for (float4x4 &instance_transform : instance_transforms) {
|
||||
MutableSpan<float4x4> 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<InstancesComponent>(), 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<InstancesComponent>(), 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);
|
||||
|
||||
@@ -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<decl::Geometry>(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<Field<bool>>("Selection"));
|
||||
evaluator.add(params.extract_input<Field<float3>>("Translation"));
|
||||
evaluator.add(params.extract_input<Field<bool>>("Local Space"));
|
||||
@@ -28,16 +30,16 @@ static void translate_instances(GeoNodeExecParams ¶ms, InstancesComponent &i
|
||||
const VArray<float3> translations = evaluator.get_evaluated<float3>(0);
|
||||
const VArray<bool> local_spaces = evaluator.get_evaluated<bool>(1);
|
||||
|
||||
MutableSpan<float4x4> instance_transforms = instances_component.instance_transforms();
|
||||
MutableSpan<float4x4> 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<GeometrySet>("Instances");
|
||||
if (geometry_set.has_instances()) {
|
||||
InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>();
|
||||
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));
|
||||
}
|
||||
|
||||
@@ -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: {
|
||||
|
||||
Reference in New Issue
Block a user