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:
2022-10-17 11:39:40 +02:00
parent db40b62252
commit e5425b566d
31 changed files with 1036 additions and 791 deletions

View File

@@ -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 {

View File

@@ -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;
};
/**

View 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

View File

@@ -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

View File

@@ -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());
}
/** \} */

View File

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

View File

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

View File

@@ -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

View 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

View File

@@ -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;

View File

@@ -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>()) {

View File

@@ -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) {

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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");
}
});
}

View File

@@ -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

View File

@@ -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,

View File

@@ -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());
}
/** \} */

View File

@@ -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

View File

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

View File

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

View File

@@ -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 &params,
@@ -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));
}

View File

@@ -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(

View File

@@ -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>());
}
}

View File

@@ -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);

View File

@@ -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 &params, InstancesComponent &instances_component)
static void rotate_instances(GeoNodeExecParams &params, 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 &params, 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 &params, 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));
}

View File

@@ -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 &params, InstancesComponent &instances_component)
static void scale_instances(GeoNodeExecParams &params, 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 &params, 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 &params, 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));
}

View File

@@ -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 &params)
/* Returns a mapping of UTF-32 character code to instance handle. */
static Map<int, int> create_curve_instances(GeoNodeExecParams &params,
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 &params,
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 &params,
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

View File

@@ -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 &params,
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 &params,
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);

View File

@@ -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 &params, InstancesComponent &instances_component)
static void translate_instances(GeoNodeExecParams &params, 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 &params, 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 &params, 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));
}

View File

@@ -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: {