diff options
Diffstat (limited to 'source/blender/blenkernel/intern/geometry_component_instances.cc')
-rw-r--r-- | source/blender/blenkernel/intern/geometry_component_instances.cc | 395 |
1 files changed, 75 insertions, 320 deletions
diff --git a/source/blender/blenkernel/intern/geometry_component_instances.cc b/source/blender/blenkernel/intern/geometry_component_instances.cc index 3a065c3576b..be1d9524509 100644 --- a/source/blender/blenkernel/intern/geometry_component_instances.cc +++ b/source/blender/blenkernel/intern/geometry_component_instances.cc @@ -16,6 +16,7 @@ #include "BKE_attribute_math.hh" #include "BKE_geometry_set.hh" #include "BKE_geometry_set_instances.hh" +#include "BKE_instances.hh" #include "attribute_access_intern.hh" @@ -29,8 +30,8 @@ using blender::MutableSpan; using blender::Set; using blender::Span; using blender::VectorSet; - -BLI_CPP_TYPE_MAKE(InstanceReference, InstanceReference, CPPTypeFlags::None) +using blender::bke::InstanceReference; +using blender::bke::Instances; /* -------------------------------------------------------------------- */ /** \name Geometry Component Implementation @@ -40,339 +41,76 @@ InstancesComponent::InstancesComponent() : GeometryComponent(GEO_COMPONENT_TYPE_ { } -GeometryComponent *InstancesComponent::copy() const -{ - InstancesComponent *new_component = new InstancesComponent(); - new_component->instance_reference_handles_ = instance_reference_handles_; - new_component->instance_transforms_ = instance_transforms_; - new_component->references_ = references_; - new_component->attributes_ = attributes_; - return new_component; -} - -void InstancesComponent::reserve(int min_capacity) +InstancesComponent::~InstancesComponent() { - instance_reference_handles_.reserve(min_capacity); - instance_transforms_.reserve(min_capacity); - attributes_.reallocate(min_capacity); + this->clear(); } -void InstancesComponent::resize(int capacity) +GeometryComponent *InstancesComponent::copy() const { - instance_reference_handles_.resize(capacity); - instance_transforms_.resize(capacity); - attributes_.reallocate(capacity); + InstancesComponent *new_component = new InstancesComponent(); + if (instances_ != nullptr) { + new_component->instances_ = new Instances(*instances_); + new_component->ownership_ = GeometryOwnershipType::Owned; + } + return new_component; } void InstancesComponent::clear() { - instance_reference_handles_.clear(); - instance_transforms_.clear(); - attributes_.clear(); - references_.clear(); -} - -void InstancesComponent::add_instance(const int instance_handle, const float4x4 &transform) -{ - BLI_assert(instance_handle >= 0); - BLI_assert(instance_handle < references_.size()); - instance_reference_handles_.append(instance_handle); - instance_transforms_.append(transform); - attributes_.reallocate(this->instances_num()); -} - -blender::Span<int> InstancesComponent::instance_reference_handles() const -{ - return instance_reference_handles_; -} - -blender::MutableSpan<int> InstancesComponent::instance_reference_handles() -{ - return instance_reference_handles_; -} - -blender::MutableSpan<blender::float4x4> InstancesComponent::instance_transforms() -{ - return instance_transforms_; -} -blender::Span<blender::float4x4> InstancesComponent::instance_transforms() const -{ - return instance_transforms_; -} - -GeometrySet &InstancesComponent::geometry_set_from_reference(const int reference_index) -{ - /* If this assert fails, it means #ensure_geometry_instances must be called first or that the - * reference can't be converted to a geometry set. */ - BLI_assert(references_[reference_index].type() == InstanceReference::Type::GeometrySet); - - /* The const cast is okay because the instance's hash in the set - * is not changed by adjusting the data inside the geometry set. */ - return const_cast<GeometrySet &>(references_[reference_index].geometry_set()); -} - -int InstancesComponent::add_reference(const InstanceReference &reference) -{ - return references_.index_of_or_add_as(reference); -} - -blender::Span<InstanceReference> InstancesComponent::references() const -{ - return references_; -} - -template<typename T> -static void copy_data_based_on_mask(Span<T> src, MutableSpan<T> dst, IndexMask mask) -{ - BLI_assert(src.data() != dst.data()); - using namespace blender; - threading::parallel_for(mask.index_range(), 1024, [&](IndexRange range) { - for (const int i : range) { - dst[i] = src[mask[i]]; - } - }); -} - -void InstancesComponent::remove_instances(const IndexMask mask) -{ - using namespace blender; - if (mask.is_range() && mask.as_range().start() == 0) { - /* Deleting from the end of the array can be much faster since no data has to be shifted. */ - this->resize(mask.size()); - this->remove_unused_references(); - return; + BLI_assert(this->is_mutable()); + if (ownership_ == GeometryOwnershipType::Owned) { + delete instances_; } - - Vector<int> new_handles(mask.size()); - copy_data_based_on_mask<int>(this->instance_reference_handles(), new_handles, mask); - instance_reference_handles_ = std::move(new_handles); - Vector<float4x4> new_transforms(mask.size()); - copy_data_based_on_mask<float4x4>(this->instance_transforms(), new_transforms, mask); - instance_transforms_ = std::move(new_transforms); - - const bke::CustomDataAttributes &src_attributes = attributes_; - - bke::CustomDataAttributes dst_attributes; - dst_attributes.reallocate(mask.size()); - - src_attributes.foreach_attribute( - [&](const bke::AttributeIDRef &id, const bke::AttributeMetaData &meta_data) { - if (!id.should_be_kept()) { - return true; - } - - GSpan src = *src_attributes.get_for_read(id); - dst_attributes.create(id, meta_data.data_type); - GMutableSpan dst = *dst_attributes.get_for_write(id); - - attribute_math::convert_to_static_type(src.type(), [&](auto dummy) { - using T = decltype(dummy); - copy_data_based_on_mask<T>(src.typed<T>(), dst.typed<T>(), mask); - }); - return true; - }, - ATTR_DOMAIN_INSTANCE); - - attributes_ = std::move(dst_attributes); - this->remove_unused_references(); + instances_ = nullptr; } -void InstancesComponent::remove_unused_references() +bool InstancesComponent::is_empty() const { - using namespace blender; - using namespace blender::bke; - - const int tot_instances = this->instances_num(); - const int tot_references_before = references_.size(); - - if (tot_instances == 0) { - /* If there are no instances, no reference is needed. */ - references_.clear(); - return; - } - if (tot_references_before == 1) { - /* There is only one reference and at least one instance. So the only existing reference is - * used. Nothing to do here. */ - return; - } - - Array<bool> usage_by_handle(tot_references_before, false); - std::mutex mutex; - - /* Loop over all instances to see which references are used. */ - threading::parallel_for(IndexRange(tot_instances), 1000, [&](IndexRange range) { - /* Use local counter to avoid lock contention. */ - Array<bool> local_usage_by_handle(tot_references_before, false); - - for (const int i : range) { - const int handle = instance_reference_handles_[i]; - BLI_assert(handle >= 0 && handle < tot_references_before); - local_usage_by_handle[handle] = true; - } - - std::lock_guard lock{mutex}; - for (const int i : IndexRange(tot_references_before)) { - usage_by_handle[i] |= local_usage_by_handle[i]; - } - }); - - if (!usage_by_handle.as_span().contains(false)) { - /* All references are used. */ - return; - } - - /* Create new references and a mapping for the handles. */ - Vector<int> handle_mapping; - VectorSet<InstanceReference> new_references; - int next_new_handle = 0; - bool handles_have_to_be_updated = false; - for (const int old_handle : IndexRange(tot_references_before)) { - if (!usage_by_handle[old_handle]) { - /* Add some dummy value. It won't be read again. */ - handle_mapping.append(-1); - } - else { - const InstanceReference &reference = references_[old_handle]; - handle_mapping.append(next_new_handle); - new_references.add_new(reference); - if (old_handle != next_new_handle) { - handles_have_to_be_updated = true; - } - next_new_handle++; + if (instances_ != nullptr) { + if (instances_->instances_num() > 0) { + return false; } } - references_ = new_references; - - if (!handles_have_to_be_updated) { - /* All remaining handles are the same as before, so they don't have to be updated. This happens - * when unused handles are only at the end. */ - return; - } - - /* Update handles of instances. */ - threading::parallel_for(IndexRange(tot_instances), 1000, [&](IndexRange range) { - for (const int i : range) { - instance_reference_handles_[i] = handle_mapping[instance_reference_handles_[i]]; - } - }); -} - -int InstancesComponent::instances_num() const -{ - return instance_transforms_.size(); -} - -int InstancesComponent::references_num() const -{ - return references_.size(); -} - -bool InstancesComponent::is_empty() const -{ - return this->instance_reference_handles_.size() == 0; + return true; } bool InstancesComponent::owns_direct_data() const { - for (const InstanceReference &reference : references_) { - if (!reference.owns_direct_data()) { - return false; - } + if (instances_ != nullptr) { + return instances_->owns_direct_data(); } return true; } void InstancesComponent::ensure_owns_direct_data() { - BLI_assert(this->is_mutable()); - for (const InstanceReference &const_reference : references_) { - /* Const cast is fine because we are not changing anything that would change the hash of the - * reference. */ - InstanceReference &reference = const_cast<InstanceReference &>(const_reference); - reference.ensure_owns_direct_data(); + if (instances_ != nullptr) { + instances_->ensure_owns_direct_data(); } } -static blender::Array<int> generate_unique_instance_ids(Span<int> original_ids) +const blender::bke::Instances *InstancesComponent::get_for_read() const { - using namespace blender; - Array<int> unique_ids(original_ids.size()); - - Set<int> used_unique_ids; - used_unique_ids.reserve(original_ids.size()); - Vector<int> instances_with_id_collision; - for (const int instance_index : original_ids.index_range()) { - const int original_id = original_ids[instance_index]; - if (used_unique_ids.add(original_id)) { - /* The original id has not been used by another instance yet. */ - unique_ids[instance_index] = original_id; - } - else { - /* The original id of this instance collided with a previous instance, it needs to be looked - * at again in a second pass. Don't generate a new random id here, because this might collide - * with other existing ids. */ - instances_with_id_collision.append(instance_index); - } - } - - Map<int, RandomNumberGenerator> generator_by_original_id; - for (const int instance_index : instances_with_id_collision) { - const int original_id = original_ids[instance_index]; - RandomNumberGenerator &rng = generator_by_original_id.lookup_or_add_cb(original_id, [&]() { - RandomNumberGenerator rng; - rng.seed_random(original_id); - return rng; - }); - - const int max_iteration = 100; - for (int iteration = 0;; iteration++) { - /* Try generating random numbers until an unused one has been found. */ - const int random_id = rng.get_int32(); - if (used_unique_ids.add(random_id)) { - /* This random id is not used by another instance. */ - unique_ids[instance_index] = random_id; - break; - } - if (iteration == max_iteration) { - /* It seems to be very unlikely that we ever run into this case (assuming there are less - * than 2^30 instances). However, if that happens, it's better to use an id that is not - * unique than to be stuck in an infinite loop. */ - unique_ids[instance_index] = original_id; - break; - } - } - } - - return unique_ids; + return instances_; } -blender::Span<int> InstancesComponent::almost_unique_ids() const +blender::bke::Instances *InstancesComponent::get_for_write() { - std::lock_guard lock(almost_unique_ids_mutex_); - std::optional<GSpan> instance_ids_gspan = attributes_.get_for_read("id"); - if (instance_ids_gspan) { - Span<int> instance_ids = instance_ids_gspan->typed<int>(); - if (almost_unique_ids_.size() != instance_ids.size()) { - almost_unique_ids_ = generate_unique_instance_ids(instance_ids); - } - } - else { - almost_unique_ids_.reinitialize(this->instances_num()); - for (const int i : almost_unique_ids_.index_range()) { - almost_unique_ids_[i] = i; - } + BLI_assert(this->is_mutable()); + if (ownership_ == GeometryOwnershipType::ReadOnly) { + instances_ = new Instances(*instances_); + ownership_ = GeometryOwnershipType::Owned; } - return almost_unique_ids_; + return instances_; } -blender::bke::CustomDataAttributes &InstancesComponent::instance_attributes() +void InstancesComponent::replace(Instances *instances, GeometryOwnershipType ownership) { - return this->attributes_; -} - -const blender::bke::CustomDataAttributes &InstancesComponent::instance_attributes() const -{ - return this->attributes_; + BLI_assert(this->is_mutable()); + this->clear(); + instances_ = instances; + ownership_ = ownership; } namespace blender::bke { @@ -397,33 +135,38 @@ 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), domain_}; } - bool try_delete(void *UNUSED(owner)) const final + bool try_delete(void * /*owner*/) const final { return false; } - bool try_create(void *UNUSED(owner), const AttributeInit &UNUSED(initializer)) const final + bool try_create(void * /*owner*/, const AttributeInit & /*initializer*/) const final { return false; } - bool exists(const void *UNUSED(owner)) const final + bool exists(const void * /*owner*/) const final { return true; } @@ -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,18 +222,18 @@ 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; } }; - fn.domain_supported = [](const void *UNUSED(owner), const eAttrDomain domain) { + fn.domain_supported = [](const void * /*owner*/, const eAttrDomain domain) { return domain == ATTR_DOMAIN_INSTANCE; }; - fn.adapt_domain = [](const void *UNUSED(owner), + fn.adapt_domain = [](const void * /*owner*/, const blender::GVArray &varray, const eAttrDomain from_domain, const eAttrDomain to_domain) { @@ -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()); } /** \} */ |