Geometry Nodes: Move instance reference indices to a builtin attribute #117951
|
@ -19,13 +19,13 @@
|
|||
* which is then stored per instance. Many instances can use the same #InstanceReference.
|
||||
*/
|
||||
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
|
||||
#include "BLI_array.hh"
|
||||
#include "BLI_function_ref.hh"
|
||||
#include "BLI_index_mask_fwd.hh"
|
||||
#include "BLI_math_matrix_types.hh"
|
||||
#include "BLI_shared_cache.hh"
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
#include "DNA_customdata_types.h"
|
||||
|
@ -99,19 +99,16 @@ class Instances {
|
|||
*/
|
||||
Vector<InstanceReference> references_;
|
||||
|
||||
/** Indices into `references_`. Determines what data is instanced. */
|
||||
Vector<int> reference_handles_;
|
||||
/** Transformation of the instances. */
|
||||
Vector<float4x4> transforms_;
|
||||
|
||||
CustomData attributes_;
|
||||
|
||||
/* 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 Array<int> almost_unique_ids_;
|
||||
|
||||
CustomData attributes_;
|
||||
mutable SharedCache<Array<int>> almost_unique_ids_cache_;
|
||||
|
||||
public:
|
||||
Instances();
|
||||
|
@ -161,7 +158,7 @@ class Instances {
|
|||
GeometrySet &geometry_set_from_reference(int reference_index);
|
||||
|
||||
Span<int> reference_handles() const;
|
||||
MutableSpan<int> reference_handles();
|
||||
MutableSpan<int> reference_handles_for_write();
|
||||
MutableSpan<float4x4> transforms();
|
||||
Span<float4x4> transforms() const;
|
||||
|
||||
|
@ -189,6 +186,11 @@ class Instances {
|
|||
|
||||
bool owns_direct_data() const;
|
||||
void ensure_owns_direct_data();
|
||||
|
||||
void tag_reference_handles_changed()
|
||||
{
|
||||
almost_unique_ids_cache_.tag_dirty();
|
||||
}
|
||||
};
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
|
|
@ -129,6 +129,9 @@ bool allow_procedural_attribute_access(StringRef attribute_name)
|
|||
if (attribute_name.startswith(".uv")) {
|
||||
return false;
|
||||
}
|
||||
if (attribute_name == ".reference_index") {
|
||||
return false;
|
||||
}
|
||||
if (attribute_name.startswith("." UV_VERTSEL_NAME ".")) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -724,19 +724,25 @@ static std::unique_ptr<Instances> try_load_instances(const DictionaryValue &io_g
|
|||
return {};
|
||||
}
|
||||
|
||||
const auto *io_handles = io_instances->lookup_dict("handles");
|
||||
if (!io_handles) {
|
||||
return {};
|
||||
}
|
||||
if (!read_blob_simple_gspan(blob_reader, *io_handles, instances->reference_handles())) {
|
||||
return {};
|
||||
}
|
||||
|
||||
MutableAttributeAccessor attributes = instances->attributes_for_write();
|
||||
if (!load_attributes(*io_attributes, attributes, blob_reader, blob_sharing)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!attributes.contains(".reference_index")) {
|
||||
/* Try reading the reference index attribute from the old bake format from before it was an
|
||||
* attribute. */
|
||||
const auto *io_handles = io_instances->lookup_dict("handles");
|
||||
if (!io_handles) {
|
||||
return {};
|
||||
}
|
||||
if (!read_blob_simple_gspan(
|
||||
blob_reader, *io_handles, instances->reference_handles_for_write()))
|
||||
{
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
return instances;
|
||||
}
|
||||
|
||||
|
@ -969,9 +975,6 @@ static std::shared_ptr<DictionaryValue> serialize_geometry_set(const GeometrySet
|
|||
|
||||
io_instances->append(
|
||||
"transforms", write_blob_simple_gspan(blob_writer, blob_sharing, instances.transforms()));
|
||||
io_instances->append(
|
||||
"handles",
|
||||
write_blob_simple_gspan(blob_writer, blob_sharing, instances.reference_handles()));
|
||||
|
||||
auto io_attributes = serialize_attributes(
|
||||
instances.attributes(), blob_writer, blob_sharing, {"position"});
|
||||
|
|
|
@ -168,6 +168,12 @@ class InstancePositionAttributeProvider final : public BuiltinAttributeProvider
|
|||
}
|
||||
};
|
||||
|
||||
static void tag_component_reference_index_changed(void *owner)
|
||||
{
|
||||
Instances &instances = *static_cast<Instances *>(owner);
|
||||
instances.tag_reference_handles_changed();
|
||||
}
|
||||
|
||||
static ComponentAttributeProviders create_attribute_providers_for_instances()
|
||||
{
|
||||
static InstancePositionAttributeProvider position;
|
||||
|
@ -200,10 +206,20 @@ static ComponentAttributeProviders create_attribute_providers_for_instances()
|
|||
instance_custom_data_access,
|
||||
nullptr);
|
||||
|
||||
/** Indices into `Instances::references_`. Determines what data is instanced. */
|
||||
static BuiltinCustomDataLayerProvider reference_index(".reference_index",
|
||||
AttrDomain::Instance,
|
||||
CD_PROP_INT32,
|
||||
CD_PROP_INT32,
|
||||
BuiltinAttributeProvider::Creatable,
|
||||
BuiltinAttributeProvider::NonDeletable,
|
||||
instance_custom_data_access,
|
||||
tag_component_reference_index_changed);
|
||||
|
||||
static CustomDataAttributeProvider instance_custom_data(AttrDomain::Instance,
|
||||
instance_custom_data_access);
|
||||
|
||||
return ComponentAttributeProviders({&position, &id}, {&instance_custom_data});
|
||||
return ComponentAttributeProviders({&position, &id, &reference_index}, {&instance_custom_data});
|
||||
}
|
||||
|
||||
static AttributeAccessorFunctions get_instances_accessor_functions()
|
||||
|
|
|
@ -51,19 +51,17 @@ Instances::Instances()
|
|||
|
||||
Instances::Instances(Instances &&other)
|
||||
: references_(std::move(other.references_)),
|
||||
reference_handles_(std::move(other.reference_handles_)),
|
||||
transforms_(std::move(other.transforms_)),
|
||||
almost_unique_ids_(std::move(other.almost_unique_ids_)),
|
||||
attributes_(other.attributes_)
|
||||
attributes_(other.attributes_),
|
||||
almost_unique_ids_cache_(std::move(other.almost_unique_ids_cache_))
|
||||
{
|
||||
CustomData_reset(&other.attributes_);
|
||||
}
|
||||
|
||||
Instances::Instances(const Instances &other)
|
||||
: references_(other.references_),
|
||||
reference_handles_(other.reference_handles_),
|
||||
transforms_(other.transforms_),
|
||||
almost_unique_ids_(other.almost_unique_ids_)
|
||||
almost_unique_ids_cache_(other.almost_unique_ids_cache_)
|
||||
{
|
||||
CustomData_copy(&other.attributes_, &attributes_, CD_MASK_ALL, other.instances_num());
|
||||
}
|
||||
|
@ -96,7 +94,6 @@ Instances &Instances::operator=(Instances &&other)
|
|||
void Instances::resize(int capacity)
|
||||
{
|
||||
const int old_size = this->instances_num();
|
||||
reference_handles_.resize(capacity);
|
||||
transforms_.resize(capacity);
|
||||
CustomData_realloc(&attributes_, old_size, capacity, CD_SET_DEFAULT);
|
||||
}
|
||||
|
@ -106,19 +103,27 @@ void Instances::add_instance(const int instance_handle, const float4x4 &transfor
|
|||
BLI_assert(instance_handle >= 0);
|
||||
BLI_assert(instance_handle < references_.size());
|
||||
const int old_size = this->instances_num();
|
||||
reference_handles_.append(instance_handle);
|
||||
transforms_.append(transform);
|
||||
CustomData_realloc(&attributes_, old_size, transforms_.size());
|
||||
this->reference_handles_for_write().last() = instance_handle;
|
||||
}
|
||||
|
||||
Span<int> Instances::reference_handles() const
|
||||
{
|
||||
return reference_handles_;
|
||||
return {static_cast<const int *>(
|
||||
CustomData_get_layer_named(&attributes_, CD_PROP_INT32, ".reference_index")),
|
||||
this->instances_num()};
|
||||
}
|
||||
|
||||
MutableSpan<int> Instances::reference_handles()
|
||||
MutableSpan<int> Instances::reference_handles_for_write()
|
||||
{
|
||||
return reference_handles_;
|
||||
int *data = static_cast<int *>(CustomData_get_layer_named_for_write(
|
||||
&attributes_, CD_PROP_INT32, ".reference_index", this->instances_num()));
|
||||
if (!data) {
|
||||
data = static_cast<int *>(CustomData_add_layer_named(
|
||||
&attributes_, CD_PROP_INT32, CD_SET_DEFAULT, this->instances_num(), ".reference_index"));
|
||||
}
|
||||
return {data, this->instances_num()};
|
||||
}
|
||||
|
||||
MutableSpan<float4x4> Instances::transforms()
|
||||
|
@ -178,10 +183,7 @@ void Instances::remove(const IndexMask &mask,
|
|||
|
||||
Instances new_instances;
|
||||
new_instances.references_ = std::move(references_);
|
||||
new_instances.reference_handles_.resize(new_size);
|
||||
new_instances.transforms_.resize(new_size);
|
||||
array_utils::gather(
|
||||
reference_handles_.as_span(), mask, new_instances.reference_handles_.as_mutable_span());
|
||||
array_utils::gather(transforms_.as_span(), mask, new_instances.transforms_.as_mutable_span());
|
||||
|
||||
gather_attributes(this->attributes(),
|
||||
|
@ -212,6 +214,8 @@ void Instances::remove_unused_references()
|
|||
return;
|
||||
}
|
||||
|
||||
const Span<int> reference_handles = this->reference_handles();
|
||||
|
||||
Array<bool> usage_by_handle(tot_references_before, false);
|
||||
std::mutex mutex;
|
||||
|
||||
|
@ -221,7 +225,7 @@ void Instances::remove_unused_references()
|
|||
Array<bool> local_usage_by_handle(tot_references_before, false);
|
||||
|
||||
for (const int i : range) {
|
||||
const int handle = reference_handles_[i];
|
||||
const int handle = reference_handles[i];
|
||||
BLI_assert(handle >= 0 && handle < tot_references_before);
|
||||
local_usage_by_handle[handle] = true;
|
||||
}
|
||||
|
@ -266,11 +270,14 @@ void Instances::remove_unused_references()
|
|||
}
|
||||
|
||||
/* 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]];
|
||||
}
|
||||
});
|
||||
{
|
||||
const MutableSpan<int> reference_handles = this->reference_handles_for_write();
|
||||
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
|
||||
|
@ -357,21 +364,20 @@ static Array<int> generate_unique_instance_ids(Span<int> original_ids)
|
|||
|
||||
Span<int> Instances::almost_unique_ids() const
|
||||
{
|
||||
std::lock_guard lock(almost_unique_ids_mutex_);
|
||||
bke::AttributeReader<int> instance_ids_attribute = this->attributes().lookup<int>("id");
|
||||
if (instance_ids_attribute) {
|
||||
Span<int> instance_ids = instance_ids_attribute.varray.get_internal_span();
|
||||
if (almost_unique_ids_.size() != instance_ids.size()) {
|
||||
almost_unique_ids_ = generate_unique_instance_ids(instance_ids);
|
||||
almost_unique_ids_cache_.ensure([&](Array<int> &r_data) {
|
||||
bke::AttributeReader<int> instance_ids_attribute = this->attributes().lookup<int>("id");
|
||||
if (instance_ids_attribute) {
|
||||
Span<int> instance_ids = instance_ids_attribute.varray.get_internal_span();
|
||||
if (r_data.size() != instance_ids.size()) {
|
||||
r_data = 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;
|
||||
else {
|
||||
r_data.reinitialize(this->instances_num());
|
||||
array_utils::fill_index_range(r_data.as_mutable_span());
|
||||
}
|
||||
}
|
||||
return almost_unique_ids_;
|
||||
});
|
||||
return almost_unique_ids_cache_.data();
|
||||
}
|
||||
|
||||
} // namespace blender::bke
|
||||
|
|
|
@ -110,7 +110,7 @@ static void join_instances(const Span<const GeometryComponent *> src_components,
|
|||
dst_instances->resize(offsets.total_size());
|
||||
|
||||
MutableSpan<float4x4> all_transforms = dst_instances->transforms();
|
||||
MutableSpan<int> all_handles = dst_instances->reference_handles();
|
||||
MutableSpan<int> all_handles = dst_instances->reference_handles_for_write();
|
||||
|
||||
for (const int i : src_components.index_range()) {
|
||||
const auto &src_component = static_cast<const bke::InstancesComponent &>(*src_components[i]);
|
||||
|
@ -131,7 +131,7 @@ static void join_instances(const Span<const GeometryComponent *> src_components,
|
|||
|
||||
result.replace_instances(dst_instances.release());
|
||||
auto &dst_component = result.get_component_for_write<bke::InstancesComponent>();
|
||||
join_attributes(src_components, dst_component, {"position"});
|
||||
join_attributes(src_components, dst_component, {"position", ".reference_index"});
|
||||
}
|
||||
|
||||
static void join_volumes(const Span<const GeometryComponent *> /*src_components*/,
|
||||
|
|
|
@ -253,7 +253,7 @@ void debug_randomize_instance_order(bke::Instances *instances)
|
|||
new_transforms[new_i] = old_transforms[old_i];
|
||||
}
|
||||
|
||||
instances->reference_handles().copy_from(new_reference_handles);
|
||||
instances->reference_handles_for_write().copy_from(new_reference_handles);
|
||||
instances->transforms().copy_from(new_transforms);
|
||||
}
|
||||
|
||||
|
|
|
@ -210,10 +210,6 @@ static void reorder_instaces_exec(const bke::Instances &src_instances,
|
|||
old_by_new_map,
|
||||
dst_instances.attributes_for_write());
|
||||
|
||||
const Span<int> old_reference_handles = src_instances.reference_handles();
|
||||
MutableSpan<int> new_reference_handles = dst_instances.reference_handles();
|
||||
array_utils::gather(old_reference_handles, old_by_new_map, new_reference_handles);
|
||||
|
||||
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);
|
||||
|
|
|
@ -956,13 +956,13 @@ static void duplicate_instances(GeometrySet &geometry_set,
|
|||
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);
|
||||
dst_instances->reference_handles_for_write().slice(range).fill(new_handle);
|
||||
}
|
||||
|
||||
bke::gather_attributes_to_groups(src_instances.attributes(),
|
||||
AttrDomain::Instance,
|
||||
propagation_info,
|
||||
{"id"},
|
||||
{"id", ".reference_index"},
|
||||
duplicates,
|
||||
selection,
|
||||
dst_instances->attributes_for_write());
|
||||
|
|
|
@ -85,7 +85,8 @@ 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.reference_handles().slice(start_len, select_len);
|
||||
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);
|
||||
|
||||
const VArraySpan positions = *src_attributes.lookup<float3>("position");
|
||||
|
@ -213,6 +214,7 @@ static void node_geo_exec(GeoNodeExecParams params)
|
|||
propagation_info,
|
||||
attributes_to_propagate);
|
||||
attributes_to_propagate.remove("position");
|
||||
attributes_to_propagate.remove(".reference_index");
|
||||
|
||||
for (const GeometryComponent::Type type : types) {
|
||||
if (geometry_set.has(type)) {
|
||||
|
|
|
@ -264,8 +264,6 @@ static void split_instance_groups(const InstancesComponent &component,
|
|||
}
|
||||
|
||||
array_utils::gather(src_instances.transforms(), mask, group_instances->transforms());
|
||||
array_utils::gather(
|
||||
src_instances.reference_handles(), mask, group_instances->reference_handles());
|
||||
bke::gather_attributes(src_instances.attributes(),
|
||||
AttrDomain::Instance,
|
||||
propagation_info,
|
||||
|
@ -343,7 +341,7 @@ static void node_geo_exec(GeoNodeExecParams params)
|
|||
}
|
||||
|
||||
dst_instances->transforms().fill(float4x4::identity());
|
||||
array_utils::fill_index_range(dst_instances->reference_handles());
|
||||
array_utils::fill_index_range(dst_instances->reference_handles_for_write());
|
||||
|
||||
for (auto item : geometry_by_group_id.items()) {
|
||||
std::unique_ptr<GeometrySet> &group_geometry = item.value;
|
||||
|
|
|
@ -319,7 +319,7 @@ static void add_instances_from_handles(bke::Instances &instances,
|
|||
const TextLayout &layout)
|
||||
{
|
||||
instances.resize(layout.positions.size());
|
||||
MutableSpan<int> handles = instances.reference_handles();
|
||||
MutableSpan<int> handles = instances.reference_handles_for_write();
|
||||
MutableSpan<float4x4> transforms = instances.transforms();
|
||||
|
||||
threading::parallel_for(IndexRange(layout.positions.size()), 256, [&](IndexRange range) {
|
||||
|
|
Loading…
Reference in New Issue