Instances: Move transforms to attribute
Similar to 2e6223d90f, but potentially 16 times more effective.
The new attribute is named "instance_transform". It isn't displayed in the
spreadsheet since that wouldn't really be useful. This simplifies a lot of
code since it doesn't have to handle transforms specially anymore. But
complexity is added in the store named attribute node and attribute input
node to keep the old "position" attribute working for compatibility.
Pull Request: https://projects.blender.org/blender/blender/pulls/118531
This commit is contained in:
@@ -27,6 +27,7 @@
|
||||
#include "BLI_math_matrix_types.hh"
|
||||
#include "BLI_shared_cache.hh"
|
||||
#include "BLI_vector.hh"
|
||||
#include "BLI_virtual_array_fwd.hh"
|
||||
|
||||
#include "DNA_customdata_types.h"
|
||||
|
||||
@@ -99,8 +100,7 @@ class Instances {
|
||||
*/
|
||||
Vector<InstanceReference> references_;
|
||||
|
||||
/** Transformation of the instances. */
|
||||
Vector<float4x4> transforms_;
|
||||
int instances_num_ = 0;
|
||||
|
||||
CustomData attributes_;
|
||||
|
||||
@@ -159,8 +159,8 @@ class Instances {
|
||||
|
||||
Span<int> reference_handles() const;
|
||||
MutableSpan<int> reference_handles_for_write();
|
||||
MutableSpan<float4x4> transforms();
|
||||
Span<float4x4> transforms() const;
|
||||
MutableSpan<float4x4> transforms_for_write();
|
||||
|
||||
int instances_num() const;
|
||||
int references_num() const;
|
||||
@@ -193,6 +193,9 @@ class Instances {
|
||||
}
|
||||
};
|
||||
|
||||
VArray<float3> instance_position_varray(const Instances &instances);
|
||||
VMutableArray<float3> instance_position_varray_for_write(Instances &instances);
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name #InstanceReference Inline Methods
|
||||
* \{ */
|
||||
|
||||
@@ -716,14 +716,6 @@ static std::unique_ptr<Instances> try_load_instances(const DictionaryValue &io_g
|
||||
instances->add_reference(std::move(reference_geometry));
|
||||
}
|
||||
|
||||
const auto *io_transforms = io_instances->lookup_dict("transforms");
|
||||
if (!io_transforms) {
|
||||
return {};
|
||||
}
|
||||
if (!read_blob_simple_gspan(blob_reader, *io_transforms, instances->transforms())) {
|
||||
return {};
|
||||
}
|
||||
|
||||
MutableAttributeAccessor attributes = instances->attributes_for_write();
|
||||
if (!load_attributes(*io_attributes, attributes, blob_reader, blob_sharing)) {
|
||||
return {};
|
||||
@@ -743,6 +735,18 @@ static std::unique_ptr<Instances> try_load_instances(const DictionaryValue &io_g
|
||||
}
|
||||
}
|
||||
|
||||
if (!attributes.contains("instance_transform")) {
|
||||
/* Try reading the transform attribute from the old bake format from before it was an
|
||||
* attribute. */
|
||||
const auto *io_handles = io_instances->lookup_dict("transforms");
|
||||
if (!io_handles) {
|
||||
return {};
|
||||
}
|
||||
if (!read_blob_simple_gspan(blob_reader, *io_handles, instances->transforms_for_write())) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
return instances;
|
||||
}
|
||||
|
||||
@@ -973,11 +977,8 @@ static std::shared_ptr<DictionaryValue> serialize_geometry_set(const GeometrySet
|
||||
serialize_geometry_set(reference.geometry_set(), blob_writer, blob_sharing));
|
||||
}
|
||||
|
||||
io_instances->append(
|
||||
"transforms", write_blob_simple_gspan(blob_writer, blob_sharing, instances.transforms()));
|
||||
|
||||
auto io_attributes = serialize_attributes(
|
||||
instances.attributes(), blob_writer, blob_sharing, {"position"});
|
||||
instances.attributes(), blob_writer, blob_sharing, {});
|
||||
io_instances->append("attributes", io_attributes);
|
||||
}
|
||||
return io_geometry;
|
||||
|
||||
@@ -102,65 +102,6 @@ void InstancesComponent::replace(Instances *instances, GeometryOwnershipType own
|
||||
ownership_ = ownership;
|
||||
}
|
||||
|
||||
static float3 get_transform_position(const float4x4 &transform)
|
||||
{
|
||||
return transform.location();
|
||||
}
|
||||
|
||||
static void set_transform_position(float4x4 &transform, const float3 position)
|
||||
{
|
||||
transform.location() = position;
|
||||
}
|
||||
|
||||
class InstancePositionAttributeProvider final : public BuiltinAttributeProvider {
|
||||
public:
|
||||
InstancePositionAttributeProvider()
|
||||
: BuiltinAttributeProvider(
|
||||
"position", AttrDomain::Instance, CD_PROP_FLOAT3, NonCreatable, NonDeletable)
|
||||
{
|
||||
}
|
||||
|
||||
GAttributeReader try_get_for_read(const void *owner) const final
|
||||
{
|
||||
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),
|
||||
domain_,
|
||||
nullptr};
|
||||
}
|
||||
|
||||
GAttributeWriter try_get_for_write(void *owner) const final
|
||||
{
|
||||
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),
|
||||
domain_};
|
||||
}
|
||||
|
||||
bool try_delete(void * /*owner*/) const final
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool try_create(void * /*owner*/, const AttributeInit & /*initializer*/) const final
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool exists(const void * /*owner*/) const final
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
static void tag_component_reference_index_changed(void *owner)
|
||||
{
|
||||
Instances &instances = *static_cast<Instances *>(owner);
|
||||
@@ -169,7 +110,6 @@ static void tag_component_reference_index_changed(void *owner)
|
||||
|
||||
static ComponentAttributeProviders create_attribute_providers_for_instances()
|
||||
{
|
||||
static InstancePositionAttributeProvider position;
|
||||
static CustomDataAccessInfo instance_custom_data_access = {
|
||||
[](void *owner) -> CustomData * {
|
||||
Instances *instances = static_cast<Instances *>(owner);
|
||||
@@ -199,6 +139,15 @@ static ComponentAttributeProviders create_attribute_providers_for_instances()
|
||||
instance_custom_data_access,
|
||||
nullptr);
|
||||
|
||||
static BuiltinCustomDataLayerProvider instance_transform("instance_transform",
|
||||
AttrDomain::Instance,
|
||||
CD_PROP_FLOAT4X4,
|
||||
CD_PROP_FLOAT4X4,
|
||||
BuiltinAttributeProvider::Creatable,
|
||||
BuiltinAttributeProvider::NonDeletable,
|
||||
instance_custom_data_access,
|
||||
nullptr);
|
||||
|
||||
/** Indices into `Instances::references_`. Determines what data is instanced. */
|
||||
static BuiltinCustomDataLayerProvider reference_index(".reference_index",
|
||||
AttrDomain::Instance,
|
||||
@@ -212,7 +161,8 @@ static ComponentAttributeProviders create_attribute_providers_for_instances()
|
||||
static CustomDataAttributeProvider instance_custom_data(AttrDomain::Instance,
|
||||
instance_custom_data_access);
|
||||
|
||||
return ComponentAttributeProviders({&position, &id, &reference_index}, {&instance_custom_data});
|
||||
return ComponentAttributeProviders({&instance_transform, &id, &reference_index},
|
||||
{&instance_custom_data});
|
||||
}
|
||||
|
||||
static AttributeAccessorFunctions get_instances_accessor_functions()
|
||||
|
||||
@@ -396,9 +396,14 @@ GVArray AttributeFieldInput::get_varray_for_context(const GeometryFieldContext &
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (context.domain() == bke::AttrDomain::Instance && name_ == "position") {
|
||||
/* Special case for "position" which is no longer an attribute on instances. */
|
||||
return bke::instance_position_varray(*context.instances());
|
||||
}
|
||||
else if (auto attributes = context.attributes()) {
|
||||
return *attributes->lookup(name_, domain, data_type);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
@@ -127,13 +127,21 @@ void Instances::ensure_geometry_instances()
|
||||
* collection as instances. */
|
||||
std::unique_ptr<Instances> instances = std::make_unique<Instances>();
|
||||
Collection &collection = reference.collection();
|
||||
|
||||
Vector<Object *, 8> objects;
|
||||
FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (&collection, object) {
|
||||
const int handle = instances->add_reference(*object);
|
||||
instances->add_instance(handle, object->object_to_world());
|
||||
float4x4 &transform = instances->transforms().last();
|
||||
transform.location() -= collection.instance_offset;
|
||||
objects.append(object);
|
||||
}
|
||||
FOREACH_COLLECTION_OBJECT_RECURSIVE_END;
|
||||
|
||||
instances->resize(objects.size());
|
||||
MutableSpan<int> handles = instances->reference_handles_for_write();
|
||||
MutableSpan<float4x4> transforms = instances->transforms_for_write();
|
||||
for (const int i : objects.index_range()) {
|
||||
handles[i] = instances->add_reference(*objects[i]);
|
||||
transforms[i] = objects[i]->object_to_world();
|
||||
transforms[i].location() -= collection.instance_offset;
|
||||
}
|
||||
instances->ensure_geometry_instances();
|
||||
new_references.append(GeometrySet::from_instances(instances.release()));
|
||||
break;
|
||||
|
||||
@@ -50,7 +50,7 @@ Instances::Instances()
|
||||
|
||||
Instances::Instances(Instances &&other)
|
||||
: references_(std::move(other.references_)),
|
||||
transforms_(std::move(other.transforms_)),
|
||||
instances_num_(other.instances_num_),
|
||||
attributes_(other.attributes_),
|
||||
almost_unique_ids_cache_(std::move(other.almost_unique_ids_cache_))
|
||||
{
|
||||
@@ -59,15 +59,15 @@ Instances::Instances(Instances &&other)
|
||||
|
||||
Instances::Instances(const Instances &other)
|
||||
: references_(other.references_),
|
||||
transforms_(other.transforms_),
|
||||
instances_num_(other.instances_num_),
|
||||
almost_unique_ids_cache_(other.almost_unique_ids_cache_)
|
||||
{
|
||||
CustomData_copy(&other.attributes_, &attributes_, CD_MASK_ALL, other.instances_num());
|
||||
CustomData_copy(&other.attributes_, &attributes_, CD_MASK_ALL, other.instances_num_);
|
||||
}
|
||||
|
||||
Instances::~Instances()
|
||||
{
|
||||
CustomData_free(&attributes_, this->instances_num());
|
||||
CustomData_free(&attributes_, instances_num_);
|
||||
}
|
||||
|
||||
Instances &Instances::operator=(const Instances &other)
|
||||
@@ -92,46 +92,55 @@ Instances &Instances::operator=(Instances &&other)
|
||||
|
||||
void Instances::resize(int capacity)
|
||||
{
|
||||
const int old_size = this->instances_num();
|
||||
transforms_.resize(capacity);
|
||||
CustomData_realloc(&attributes_, old_size, capacity, CD_SET_DEFAULT);
|
||||
CustomData_realloc(&attributes_, instances_num_, capacity, CD_SET_DEFAULT);
|
||||
instances_num_ = capacity;
|
||||
}
|
||||
|
||||
void Instances::add_instance(const int instance_handle, const float4x4 &transform)
|
||||
{
|
||||
BLI_assert(instance_handle >= 0);
|
||||
BLI_assert(instance_handle < references_.size());
|
||||
const int old_size = this->instances_num();
|
||||
transforms_.append(transform);
|
||||
CustomData_realloc(&attributes_, old_size, transforms_.size());
|
||||
const int old_size = instances_num_;
|
||||
instances_num_++;
|
||||
CustomData_realloc(&attributes_, old_size, instances_num_);
|
||||
this->reference_handles_for_write().last() = instance_handle;
|
||||
this->transforms_for_write().last() = transform;
|
||||
}
|
||||
|
||||
Span<int> Instances::reference_handles() const
|
||||
{
|
||||
return {static_cast<const int *>(
|
||||
CustomData_get_layer_named(&attributes_, CD_PROP_INT32, ".reference_index")),
|
||||
this->instances_num()};
|
||||
instances_num_};
|
||||
}
|
||||
|
||||
MutableSpan<int> Instances::reference_handles_for_write()
|
||||
{
|
||||
int *data = static_cast<int *>(CustomData_get_layer_named_for_write(
|
||||
&attributes_, CD_PROP_INT32, ".reference_index", this->instances_num()));
|
||||
&attributes_, CD_PROP_INT32, ".reference_index", instances_num_));
|
||||
if (!data) {
|
||||
data = static_cast<int *>(CustomData_add_layer_named(
|
||||
&attributes_, CD_PROP_INT32, CD_SET_DEFAULT, this->instances_num(), ".reference_index"));
|
||||
&attributes_, CD_PROP_INT32, CD_SET_DEFAULT, instances_num_, ".reference_index"));
|
||||
}
|
||||
return {data, this->instances_num()};
|
||||
return {data, instances_num_};
|
||||
}
|
||||
|
||||
MutableSpan<float4x4> Instances::transforms()
|
||||
{
|
||||
return transforms_;
|
||||
}
|
||||
Span<float4x4> Instances::transforms() const
|
||||
{
|
||||
return transforms_;
|
||||
return {static_cast<const float4x4 *>(
|
||||
CustomData_get_layer_named(&attributes_, CD_PROP_FLOAT4X4, "instance_transform")),
|
||||
instances_num_};
|
||||
}
|
||||
|
||||
MutableSpan<float4x4> Instances::transforms_for_write()
|
||||
{
|
||||
float4x4 *data = static_cast<float4x4 *>(CustomData_get_layer_named_for_write(
|
||||
&attributes_, CD_PROP_FLOAT4X4, "instance_transform", instances_num_));
|
||||
if (!data) {
|
||||
data = static_cast<float4x4 *>(CustomData_add_layer_named(
|
||||
&attributes_, CD_PROP_FLOAT4X4, CD_SET_DEFAULT, instances_num_, "instance_transform"));
|
||||
}
|
||||
return {data, instances_num_};
|
||||
}
|
||||
|
||||
GeometrySet &Instances::geometry_set_from_reference(const int reference_index)
|
||||
@@ -178,17 +187,14 @@ void Instances::remove(const IndexMask &mask,
|
||||
return;
|
||||
}
|
||||
|
||||
const int new_size = mask.size();
|
||||
|
||||
Instances new_instances;
|
||||
new_instances.references_ = std::move(references_);
|
||||
new_instances.transforms_.resize(new_size);
|
||||
array_utils::gather(transforms_.as_span(), mask, new_instances.transforms_.as_mutable_span());
|
||||
new_instances.instances_num_ = mask.size();
|
||||
|
||||
gather_attributes(this->attributes(),
|
||||
AttrDomain::Instance,
|
||||
propagation_info,
|
||||
{"position"},
|
||||
{},
|
||||
mask,
|
||||
new_instances.attributes_for_write());
|
||||
|
||||
@@ -199,7 +205,7 @@ void Instances::remove(const IndexMask &mask,
|
||||
|
||||
void Instances::remove_unused_references()
|
||||
{
|
||||
const int tot_instances = this->instances_num();
|
||||
const int tot_instances = instances_num_;
|
||||
const int tot_references_before = references_.size();
|
||||
|
||||
if (tot_instances == 0) {
|
||||
@@ -281,7 +287,7 @@ void Instances::remove_unused_references()
|
||||
|
||||
int Instances::instances_num() const
|
||||
{
|
||||
return transforms_.size();
|
||||
return this->instances_num_;
|
||||
}
|
||||
|
||||
int Instances::references_num() const
|
||||
@@ -372,11 +378,33 @@ Span<int> Instances::almost_unique_ids() const
|
||||
}
|
||||
}
|
||||
else {
|
||||
r_data.reinitialize(this->instances_num());
|
||||
r_data.reinitialize(instances_num_);
|
||||
array_utils::fill_index_range(r_data.as_mutable_span());
|
||||
}
|
||||
});
|
||||
return almost_unique_ids_cache_.data();
|
||||
}
|
||||
|
||||
static float3 get_transform_position(const float4x4 &transform)
|
||||
{
|
||||
return transform.location();
|
||||
}
|
||||
|
||||
static void set_transform_position(float4x4 &transform, const float3 position)
|
||||
{
|
||||
transform.location() = position;
|
||||
}
|
||||
|
||||
VArray<float3> instance_position_varray(const Instances &instances)
|
||||
{
|
||||
return VArray<float3>::ForDerivedSpan<float4x4, get_transform_position>(instances.transforms());
|
||||
}
|
||||
|
||||
VMutableArray<float3> instance_position_varray_for_write(Instances &instances)
|
||||
{
|
||||
MutableSpan<float4x4> transforms = instances.transforms_for_write();
|
||||
return VMutableArray<float3>::
|
||||
ForDerivedSpan<float4x4, get_transform_position, set_transform_position>(transforms);
|
||||
}
|
||||
|
||||
} // namespace blender::bke
|
||||
|
||||
@@ -204,6 +204,13 @@ void GeometryDataSource::foreach_default_column_ids(
|
||||
if (!bke::allow_procedural_attribute_access(attribute_id.name())) {
|
||||
return true;
|
||||
}
|
||||
if (meta_data.domain == bke::AttrDomain::Instance &&
|
||||
attribute_id.name() == "instance_transform")
|
||||
{
|
||||
/* Don't display the instance transform attribute, since matrix visualization in the
|
||||
* spreadsheet isn't helpful. */
|
||||
return true;
|
||||
}
|
||||
SpreadsheetColumnID column_id;
|
||||
column_id.name = (char *)attribute_id.name().data();
|
||||
const bool is_front = attribute_id.name() == ".viewer";
|
||||
@@ -212,6 +219,7 @@ void GeometryDataSource::foreach_default_column_ids(
|
||||
});
|
||||
|
||||
if (component_->type() == bke::GeometryComponent::Type::Instance) {
|
||||
fn({(char *)"Position"}, false);
|
||||
fn({(char *)"Rotation"}, false);
|
||||
fn({(char *)"Scale"}, false);
|
||||
}
|
||||
@@ -257,6 +265,12 @@ std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values(
|
||||
}));
|
||||
}
|
||||
Span<float4x4> transforms = instances->transforms();
|
||||
if (STREQ(column_id.name, "Position")) {
|
||||
return std::make_unique<ColumnValues>(
|
||||
column_id.name, VArray<float3>::ForFunc(domain_num, [transforms](int64_t index) {
|
||||
return transforms[index].location();
|
||||
}));
|
||||
}
|
||||
if (STREQ(column_id.name, "Rotation")) {
|
||||
return std::make_unique<ColumnValues>(
|
||||
column_id.name, VArray<float3>::ForFunc(domain_num, [transforms](int64_t index) {
|
||||
|
||||
@@ -109,7 +109,6 @@ static void join_instances(const Span<const GeometryComponent *> src_components,
|
||||
std::unique_ptr<bke::Instances> dst_instances = std::make_unique<bke::Instances>();
|
||||
dst_instances->resize(offsets.total_size());
|
||||
|
||||
MutableSpan<float4x4> all_transforms = dst_instances->transforms();
|
||||
MutableSpan<int> all_handles = dst_instances->reference_handles_for_write();
|
||||
|
||||
for (const int i : src_components.index_range()) {
|
||||
@@ -126,12 +125,11 @@ static void join_instances(const Span<const GeometryComponent *> src_components,
|
||||
|
||||
const Span<int> src_handles = src_instances.reference_handles();
|
||||
array_utils::gather(handle_map.as_span(), src_handles, all_handles.slice(dst_range));
|
||||
array_utils::copy(src_instances.transforms(), all_transforms.slice(dst_range));
|
||||
}
|
||||
|
||||
result.replace_instances(dst_instances.release());
|
||||
auto &dst_component = result.get_component_for_write<bke::InstancesComponent>();
|
||||
join_attributes(src_components, dst_component, {"position", ".reference_index"});
|
||||
join_attributes(src_components, dst_component, {".reference_index"});
|
||||
}
|
||||
|
||||
static void join_volumes(const Span<const GeometryComponent *> /*src_components*/,
|
||||
@@ -174,11 +172,13 @@ static void join_component_type(const bke::GeometryComponent::Type component_typ
|
||||
}
|
||||
|
||||
std::unique_ptr<bke::Instances> instances = std::make_unique<bke::Instances>();
|
||||
for (const GeometryComponent *component : components) {
|
||||
instances->resize(components.size());
|
||||
instances->transforms_for_write().fill(float4x4::identity());
|
||||
MutableSpan<int> handles = instances->reference_handles_for_write();
|
||||
for (const int i : components.index_range()) {
|
||||
GeometrySet tmp_geo;
|
||||
tmp_geo.add(*component);
|
||||
const int handle = instances->add_reference(bke::InstanceReference{tmp_geo});
|
||||
instances->add_instance(handle, float4x4::identity());
|
||||
tmp_geo.add(*components[i]);
|
||||
handles[i] = instances->add_reference(bke::InstanceReference{tmp_geo});
|
||||
}
|
||||
|
||||
RealizeInstancesOptions options;
|
||||
|
||||
@@ -69,29 +69,6 @@ static void mix(GMutableSpan a, const GVArray &b, const float factor)
|
||||
});
|
||||
}
|
||||
|
||||
static void mix(MutableSpan<float4x4> a, const Span<float4x4> b, const float factor)
|
||||
{
|
||||
threading::parallel_for(a.index_range(), 1024, [&](const IndexRange range) {
|
||||
for (const int i : range) {
|
||||
a[i] = math::interpolate(a[i], b[i], factor);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static void mix_with_indices(MutableSpan<float4x4> a,
|
||||
const Span<float4x4> b,
|
||||
const Span<int> index_map,
|
||||
const float factor)
|
||||
{
|
||||
threading::parallel_for(a.index_range(), 1024, [&](const IndexRange range) {
|
||||
for (const int i : range) {
|
||||
if (index_map[i] != -1) {
|
||||
a[i] = math::interpolate(a[i], b[index_map[i]], factor);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static void mix_attributes(bke::MutableAttributeAccessor attributes_a,
|
||||
const bke::AttributeAccessor b_attributes,
|
||||
const Span<int> index_map,
|
||||
@@ -216,13 +193,7 @@ bke::GeometrySet mix_geometries(bke::GeometrySet a, const bke::GeometrySet &b, c
|
||||
index_map,
|
||||
bke::AttrDomain::Instance,
|
||||
factor,
|
||||
{"position"});
|
||||
if (index_map.is_empty()) {
|
||||
mix(instances_a->transforms(), instances_b->transforms(), factor);
|
||||
}
|
||||
else {
|
||||
mix_with_indices(instances_a->transforms(), instances_b->transforms(), index_map, factor);
|
||||
}
|
||||
{});
|
||||
}
|
||||
}
|
||||
return a;
|
||||
|
||||
@@ -233,27 +233,10 @@ void debug_randomize_instance_order(bke::Instances *instances)
|
||||
if (instances == nullptr || !use_debug_randomization()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const int instances_num = instances->instances_num();
|
||||
const int seed = seed_from_instances(*instances);
|
||||
const Array<int> new_by_old_map = get_permutation(instances_num, seed);
|
||||
|
||||
reorder_customdata(instances->custom_data_attributes(), new_by_old_map);
|
||||
|
||||
const Span<int> old_reference_handles = instances->reference_handles();
|
||||
const Span<float4x4> old_transforms = instances->transforms();
|
||||
|
||||
Vector<int> new_reference_handles(instances_num);
|
||||
Vector<float4x4> new_transforms(instances_num);
|
||||
|
||||
for (const int old_i : new_by_old_map.index_range()) {
|
||||
const int new_i = new_by_old_map[old_i];
|
||||
new_reference_handles[new_i] = old_reference_handles[old_i];
|
||||
new_transforms[new_i] = old_transforms[old_i];
|
||||
}
|
||||
|
||||
instances->reference_handles_for_write().copy_from(new_reference_handles);
|
||||
instances->transforms().copy_from(new_transforms);
|
||||
}
|
||||
|
||||
bool use_debug_randomization()
|
||||
|
||||
@@ -209,10 +209,6 @@ static void reorder_instaces_exec(const bke::Instances &src_instances,
|
||||
{},
|
||||
old_by_new_map,
|
||||
dst_instances.attributes_for_write());
|
||||
|
||||
const Span<float4x4> old_transforms = src_instances.transforms();
|
||||
MutableSpan<float4x4> new_transforms = dst_instances.transforms();
|
||||
array_utils::gather(old_transforms, old_by_new_map, new_transforms);
|
||||
}
|
||||
|
||||
static void clean_unused_attributes(const bke::AnonymousAttributePropagationInfo &propagation_info,
|
||||
|
||||
@@ -111,7 +111,7 @@ static void transform_greasepencil(GreasePencil &grease_pencil, const float4x4 &
|
||||
|
||||
static void translate_instances(bke::Instances &instances, const float3 translation)
|
||||
{
|
||||
MutableSpan<float4x4> transforms = instances.transforms();
|
||||
MutableSpan<float4x4> transforms = instances.transforms_for_write();
|
||||
threading::parallel_for(transforms.index_range(), 1024, [&](const IndexRange range) {
|
||||
for (float4x4 &instance_transform : transforms.slice(range)) {
|
||||
add_v3_v3(instance_transform.ptr()[3], translation);
|
||||
@@ -121,7 +121,7 @@ static void translate_instances(bke::Instances &instances, const float3 translat
|
||||
|
||||
static void transform_instances(bke::Instances &instances, const float4x4 &transform)
|
||||
{
|
||||
MutableSpan<float4x4> transforms = instances.transforms();
|
||||
MutableSpan<float4x4> transforms = instances.transforms_for_write();
|
||||
threading::parallel_for(transforms.index_range(), 1024, [&](const IndexRange range) {
|
||||
for (float4x4 &instance_transform : transforms.slice(range)) {
|
||||
instance_transform = transform * instance_transform;
|
||||
|
||||
@@ -954,8 +954,6 @@ static void duplicate_instances(GeometrySet &geometry_set,
|
||||
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_for_write().slice(range).fill(new_handle);
|
||||
}
|
||||
|
||||
|
||||
@@ -81,7 +81,8 @@ static void add_instances_from_component(
|
||||
|
||||
MutableSpan<int> dst_handles = dst_component.reference_handles_for_write().slice(start_len,
|
||||
select_len);
|
||||
MutableSpan<float4x4> dst_transforms = dst_component.transforms().slice(start_len, select_len);
|
||||
MutableSpan<float4x4> dst_transforms = dst_component.transforms_for_write().slice(start_len,
|
||||
select_len);
|
||||
|
||||
const VArraySpan positions = *src_attributes.lookup<float3>("position");
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ static void rotate_instances(GeoNodeExecParams ¶ms, bke::Instances &instance
|
||||
const VArray<float3> pivots = evaluator.get_evaluated<float3>(1);
|
||||
const VArray<bool> local_spaces = evaluator.get_evaluated<bool>(2);
|
||||
|
||||
MutableSpan<float4x4> transforms = instances.transforms();
|
||||
MutableSpan<float4x4> transforms = instances.transforms_for_write();
|
||||
|
||||
selection.foreach_index(GrainSize(512), [&](const int64_t i) {
|
||||
const float3 pivot = pivots[i];
|
||||
|
||||
@@ -38,7 +38,7 @@ static void scale_instances(GeoNodeExecParams ¶ms, bke::Instances &instances
|
||||
const VArray<float3> pivots = evaluator.get_evaluated<float3>(1);
|
||||
const VArray<bool> local_spaces = evaluator.get_evaluated<bool>(2);
|
||||
|
||||
MutableSpan<float4x4> transforms = instances.transforms();
|
||||
MutableSpan<float4x4> transforms = instances.transforms_for_write();
|
||||
|
||||
selection.foreach_index(GrainSize(512), [&](const int64_t i) {
|
||||
const float3 pivot = pivots[i];
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include "BKE_curves.hh"
|
||||
#include "BKE_grease_pencil.hh"
|
||||
#include "BKE_instances.hh"
|
||||
|
||||
#include "node_geometry_util.hh"
|
||||
|
||||
@@ -20,20 +21,25 @@ static void node_declare(NodeDeclarationBuilder &b)
|
||||
b.add_output<decl::Geometry>("Geometry").propagate_all();
|
||||
}
|
||||
|
||||
static void set_computed_position_and_offset(GeometryComponent &component,
|
||||
const VArray<float3> &in_positions,
|
||||
const VArray<float3> &in_offsets,
|
||||
const IndexMask &selection,
|
||||
MutableAttributeAccessor attributes)
|
||||
{
|
||||
/* Optimize the case when `in_positions` references the original positions array. */
|
||||
const bke::AttributeReader positions_read_only = attributes.lookup<float3>("position");
|
||||
bool positions_are_original = false;
|
||||
if (positions_read_only.varray.is_span() && in_positions.is_span()) {
|
||||
positions_are_original = positions_read_only.varray.get_internal_span().data() ==
|
||||
in_positions.get_internal_span().data();
|
||||
}
|
||||
constexpr GrainSize grain_size{10000};
|
||||
|
||||
static bool check_positions_are_original(const AttributeAccessor &attributes,
|
||||
const VArray<float3> &in_positions)
|
||||
{
|
||||
const bke::AttributeReader positions_read_only = attributes.lookup<float3>("position");
|
||||
if (positions_read_only.varray.is_span() && in_positions.is_span()) {
|
||||
return positions_read_only.varray.get_internal_span().data() ==
|
||||
in_positions.get_internal_span().data();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void write_offset_positions(const bool positions_are_original,
|
||||
const IndexMask &selection,
|
||||
const VArray<float3> &in_positions,
|
||||
const VArray<float3> &in_offsets,
|
||||
VMutableArray<float3> &out_positions)
|
||||
{
|
||||
if (positions_are_original) {
|
||||
if (const std::optional<float3> offset = in_offsets.get_if_single()) {
|
||||
if (math::is_zero(*offset)) {
|
||||
@@ -41,8 +47,32 @@ static void set_computed_position_and_offset(GeometryComponent &component,
|
||||
}
|
||||
}
|
||||
}
|
||||
const GrainSize grain_size{10000};
|
||||
|
||||
MutableVArraySpan<float3> out_positions_span = out_positions;
|
||||
if (positions_are_original) {
|
||||
devirtualize_varray(in_offsets, [&](const auto in_offsets) {
|
||||
selection.foreach_index_optimized<int>(
|
||||
grain_size, [&](const int i) { out_positions_span[i] += in_offsets[i]; });
|
||||
});
|
||||
}
|
||||
else {
|
||||
devirtualize_varray2(
|
||||
in_positions, in_offsets, [&](const auto in_positions, const auto in_offsets) {
|
||||
selection.foreach_index_optimized<int>(grain_size, [&](const int i) {
|
||||
out_positions_span[i] = in_positions[i] + in_offsets[i];
|
||||
});
|
||||
});
|
||||
}
|
||||
out_positions_span.save();
|
||||
}
|
||||
|
||||
static void set_computed_position_and_offset(GeometryComponent &component,
|
||||
const VArray<float3> &in_positions,
|
||||
const VArray<float3> &in_offsets,
|
||||
const IndexMask &selection,
|
||||
MutableAttributeAccessor attributes)
|
||||
{
|
||||
/* Optimize the case when `in_positions` references the original positions array. */
|
||||
switch (component.type()) {
|
||||
case GeometryComponent::Type::Curve: {
|
||||
if (attributes.contains("handle_right") && attributes.contains("handle_left")) {
|
||||
@@ -76,26 +106,30 @@ static void set_computed_position_and_offset(GeometryComponent &component,
|
||||
curves.calculate_bezier_auto_handles();
|
||||
break;
|
||||
}
|
||||
ATTR_FALLTHROUGH;
|
||||
AttributeWriter<float3> positions = attributes.lookup_for_write<float3>("position");
|
||||
write_offset_positions(check_positions_are_original(attributes, in_positions),
|
||||
selection,
|
||||
in_positions,
|
||||
in_offsets,
|
||||
positions.varray);
|
||||
positions.finish();
|
||||
break;
|
||||
}
|
||||
case GeometryComponent::Type::Instance: {
|
||||
/* Special case for "position" which is no longer an attribute on instances. */
|
||||
auto &instances_component = reinterpret_cast<bke::InstancesComponent &>(component);
|
||||
bke::Instances &instances = *instances_component.get_for_write();
|
||||
VMutableArray<float3> positions = bke::instance_position_varray_for_write(instances);
|
||||
write_offset_positions(false, selection, in_positions, in_offsets, positions);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
AttributeWriter<float3> positions = attributes.lookup_for_write<float3>("position");
|
||||
MutableVArraySpan<float3> out_positions_span = positions.varray;
|
||||
if (positions_are_original) {
|
||||
devirtualize_varray(in_offsets, [&](const auto in_offsets) {
|
||||
selection.foreach_index_optimized<int>(
|
||||
grain_size, [&](const int i) { out_positions_span[i] += in_offsets[i]; });
|
||||
});
|
||||
}
|
||||
else {
|
||||
devirtualize_varray2(
|
||||
in_positions, in_offsets, [&](const auto in_positions, const auto in_offsets) {
|
||||
selection.foreach_index_optimized<int>(grain_size, [&](const int i) {
|
||||
out_positions_span[i] = in_positions[i] + in_offsets[i];
|
||||
});
|
||||
});
|
||||
}
|
||||
out_positions_span.save();
|
||||
write_offset_positions(check_positions_are_original(attributes, in_positions),
|
||||
selection,
|
||||
in_positions,
|
||||
in_offsets,
|
||||
positions.varray);
|
||||
positions.finish();
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -246,7 +246,6 @@ static void split_instance_groups(const InstancesComponent &component,
|
||||
group_instances->add_reference(reference);
|
||||
}
|
||||
|
||||
array_utils::gather(src_instances.transforms(), mask, group_instances->transforms());
|
||||
bke::gather_attributes(src_instances.attributes(),
|
||||
AttrDomain::Instance,
|
||||
propagation_info,
|
||||
@@ -323,7 +322,7 @@ static void node_geo_exec(GeoNodeExecParams params)
|
||||
dst_group_id.finish();
|
||||
}
|
||||
|
||||
dst_instances->transforms().fill(float4x4::identity());
|
||||
dst_instances->transforms_for_write().fill(float4x4::identity());
|
||||
array_utils::fill_index_range(dst_instances->reference_handles_for_write());
|
||||
|
||||
for (auto item : geometry_by_group_id.items()) {
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "RNA_access.hh"
|
||||
#include "RNA_enum_types.hh"
|
||||
|
||||
#include "BKE_instances.hh"
|
||||
#include "BKE_mesh.hh"
|
||||
#include "BKE_type_conversions.hh"
|
||||
|
||||
@@ -119,9 +120,21 @@ static void node_geo_exec(GeoNodeExecParams params)
|
||||
if (geometry_set.has_instances()) {
|
||||
GeometryComponent &component = geometry_set.get_component_for_write(
|
||||
GeometryComponent::Type::Instance);
|
||||
if (!bke::try_capture_field_on_geometry(component, name, domain, selection, field)) {
|
||||
if (component.attribute_domain_size(domain) != 0) {
|
||||
failure.store(true);
|
||||
|
||||
if (name == "position" && data_type == CD_PROP_FLOAT3) {
|
||||
/* Special case for "position" which is no longer an attribute on instances. */
|
||||
bke::Instances &instances = *geometry_set.get_instances_for_write();
|
||||
bke::InstancesFieldContext context(instances);
|
||||
fn::FieldEvaluator evaluator{context, instances.instances_num()};
|
||||
evaluator.set_selection(selection);
|
||||
evaluator.add_with_destination(field, bke::instance_position_varray_for_write(instances));
|
||||
evaluator.evaluate();
|
||||
}
|
||||
else {
|
||||
if (!bke::try_capture_field_on_geometry(component, name, domain, selection, field)) {
|
||||
if (component.attribute_domain_size(domain) != 0) {
|
||||
failure.store(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -318,7 +318,7 @@ static void add_instances_from_handles(bke::Instances &instances,
|
||||
{
|
||||
instances.resize(layout.positions.size());
|
||||
MutableSpan<int> handles = instances.reference_handles_for_write();
|
||||
MutableSpan<float4x4> transforms = instances.transforms();
|
||||
MutableSpan<float4x4> transforms = instances.transforms_for_write();
|
||||
|
||||
threading::parallel_for(IndexRange(layout.positions.size()), 256, [&](IndexRange range) {
|
||||
for (const int i : range) {
|
||||
|
||||
@@ -34,7 +34,7 @@ static void translate_instances(GeoNodeExecParams ¶ms, bke::Instances &insta
|
||||
const VArray<float3> translations = evaluator.get_evaluated<float3>(0);
|
||||
const VArray<bool> local_spaces = evaluator.get_evaluated<bool>(1);
|
||||
|
||||
MutableSpan<float4x4> transforms = instances.transforms();
|
||||
MutableSpan<float4x4> transforms = instances.transforms_for_write();
|
||||
|
||||
selection.foreach_index(GrainSize(1024), [&](const int64_t i) {
|
||||
if (local_spaces[i]) {
|
||||
|
||||
Reference in New Issue
Block a user