Changeset View
Changeset View
Standalone View
Standalone View
source/blender/blenkernel/intern/geometry_component_instances.cc
| Show All 31 Lines | |||||
| #include "attribute_access_intern.hh" | #include "attribute_access_intern.hh" | ||||
| using blender::float4x4; | using blender::float4x4; | ||||
| using blender::Map; | using blender::Map; | ||||
| using blender::MutableSpan; | using blender::MutableSpan; | ||||
| using blender::Set; | using blender::Set; | ||||
| using blender::Span; | using blender::Span; | ||||
| using blender::VectorSet; | using blender::VectorSet; | ||||
| using blender::fn::GSpan; | |||||
| /* -------------------------------------------------------------------- */ | /* -------------------------------------------------------------------- */ | ||||
| /** \name Geometry Component Implementation | /** \name Geometry Component Implementation | ||||
| * \{ */ | * \{ */ | ||||
| InstancesComponent::InstancesComponent() : GeometryComponent(GEO_COMPONENT_TYPE_INSTANCES) | InstancesComponent::InstancesComponent() : GeometryComponent(GEO_COMPONENT_TYPE_INSTANCES) | ||||
| { | { | ||||
| } | } | ||||
| GeometryComponent *InstancesComponent::copy() const | GeometryComponent *InstancesComponent::copy() const | ||||
| { | { | ||||
| InstancesComponent *new_component = new InstancesComponent(); | InstancesComponent *new_component = new InstancesComponent(); | ||||
| new_component->instance_reference_handles_ = instance_reference_handles_; | new_component->instance_reference_handles_ = instance_reference_handles_; | ||||
| new_component->instance_transforms_ = instance_transforms_; | new_component->instance_transforms_ = instance_transforms_; | ||||
| new_component->instance_ids_ = instance_ids_; | |||||
| new_component->references_ = references_; | new_component->references_ = references_; | ||||
| return new_component; | return new_component; | ||||
| } | } | ||||
| void InstancesComponent::reserve(int min_capacity) | void InstancesComponent::reserve(int min_capacity) | ||||
| { | { | ||||
| instance_reference_handles_.reserve(min_capacity); | instance_reference_handles_.reserve(min_capacity); | ||||
| instance_transforms_.reserve(min_capacity); | instance_transforms_.reserve(min_capacity); | ||||
| if (!instance_ids_.is_empty()) { | |||||
| this->instance_ids_ensure(); | |||||
| } | |||||
| attributes_.reallocate(min_capacity); | attributes_.reallocate(min_capacity); | ||||
| } | } | ||||
| /** | /** | ||||
| * Resize the transform, handles, and ID vectors to the specified capacity. | * Resize the transform, handles, and ID vectors to the specified capacity. | ||||
| * | * | ||||
| * \note This function should be used carefully, only when it's guaranteed | * \note This function should be used carefully, only when it's guaranteed | ||||
| * that the data will be filled. | * that the data will be filled. | ||||
| */ | */ | ||||
| void InstancesComponent::resize(int capacity) | void InstancesComponent::resize(int capacity) | ||||
| { | { | ||||
| instance_reference_handles_.resize(capacity); | instance_reference_handles_.resize(capacity); | ||||
| instance_transforms_.resize(capacity); | instance_transforms_.resize(capacity); | ||||
| if (!instance_ids_.is_empty()) { | |||||
| this->instance_ids_ensure(); | |||||
| } | |||||
| attributes_.reallocate(capacity); | attributes_.reallocate(capacity); | ||||
| } | } | ||||
| void InstancesComponent::clear() | void InstancesComponent::clear() | ||||
| { | { | ||||
| instance_reference_handles_.clear(); | instance_reference_handles_.clear(); | ||||
| instance_transforms_.clear(); | instance_transforms_.clear(); | ||||
| instance_ids_.clear(); | |||||
| attributes_.clear(); | attributes_.clear(); | ||||
| references_.clear(); | references_.clear(); | ||||
| } | } | ||||
| void InstancesComponent::add_instance(const int instance_handle, const float4x4 &transform) | void InstancesComponent::add_instance(const int instance_handle, const float4x4 &transform) | ||||
| { | { | ||||
| BLI_assert(instance_handle >= 0); | BLI_assert(instance_handle >= 0); | ||||
| BLI_assert(instance_handle < references_.size()); | BLI_assert(instance_handle < references_.size()); | ||||
| instance_reference_handles_.append(instance_handle); | instance_reference_handles_.append(instance_handle); | ||||
| instance_transforms_.append(transform); | instance_transforms_.append(transform); | ||||
| if (!instance_ids_.is_empty()) { | |||||
| this->instance_ids_ensure(); | |||||
| } | |||||
| attributes_.reallocate(this->instances_amount()); | attributes_.reallocate(this->instances_amount()); | ||||
| } | } | ||||
| blender::Span<int> InstancesComponent::instance_reference_handles() const | blender::Span<int> InstancesComponent::instance_reference_handles() const | ||||
| { | { | ||||
| return instance_reference_handles_; | return instance_reference_handles_; | ||||
| } | } | ||||
| blender::MutableSpan<int> InstancesComponent::instance_reference_handles() | blender::MutableSpan<int> InstancesComponent::instance_reference_handles() | ||||
| { | { | ||||
| return instance_reference_handles_; | return instance_reference_handles_; | ||||
| } | } | ||||
| blender::MutableSpan<blender::float4x4> InstancesComponent::instance_transforms() | blender::MutableSpan<blender::float4x4> InstancesComponent::instance_transforms() | ||||
| { | { | ||||
| return instance_transforms_; | return instance_transforms_; | ||||
| } | } | ||||
| blender::Span<blender::float4x4> InstancesComponent::instance_transforms() const | blender::Span<blender::float4x4> InstancesComponent::instance_transforms() const | ||||
| { | { | ||||
| return instance_transforms_; | return instance_transforms_; | ||||
| } | } | ||||
| blender::MutableSpan<int> InstancesComponent::instance_ids() | |||||
| { | |||||
| return instance_ids_; | |||||
| } | |||||
| blender::Span<int> InstancesComponent::instance_ids() const | |||||
| { | |||||
| return instance_ids_; | |||||
| } | |||||
| /** | |||||
| * Make sure the ID storage size matches the number of instances. By directly resizing the | |||||
| * component's vectors internally, it is possible to be in a situation where the IDs are not | |||||
| * empty but they do not have the correct size; this function resolves that. | |||||
| */ | |||||
| blender::MutableSpan<int> InstancesComponent::instance_ids_ensure() | |||||
| { | |||||
| instance_ids_.append_n_times(0, this->instances_amount() - instance_ids_.size()); | |||||
| return instance_ids_; | |||||
| } | |||||
| void InstancesComponent::instance_ids_clear() | |||||
| { | |||||
| instance_ids_.clear_and_make_inline(); | |||||
| } | |||||
| /** | /** | ||||
| * With write access to the instances component, the data in the instanced geometry sets can be | * 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` | * changed. This is a function on the component rather than each reference to ensure `const` | ||||
| * correctness for that reason. | * correctness for that reason. | ||||
| */ | */ | ||||
| GeometrySet &InstancesComponent::geometry_set_from_reference(const int reference_index) | 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 | /* If this assert fails, it means #ensure_geometry_instances must be called first or that the | ||||
| ▲ Show 20 Lines • Show All 187 Lines • ▼ Show 20 Lines | static blender::Array<int> generate_unique_instance_ids(Span<int> original_ids) | ||||
| } | } | ||||
| return unique_ids; | return unique_ids; | ||||
| } | } | ||||
| blender::Span<int> InstancesComponent::almost_unique_ids() const | blender::Span<int> InstancesComponent::almost_unique_ids() const | ||||
| { | { | ||||
| std::lock_guard lock(almost_unique_ids_mutex_); | std::lock_guard lock(almost_unique_ids_mutex_); | ||||
| if (instance_ids().is_empty()) { | std::optional<GSpan> instance_ids_gspan = attributes_.get_for_read("id"); | ||||
| almost_unique_ids_.reinitialize(this->instances_amount()); | if (instance_ids_gspan) { | ||||
| for (const int i : almost_unique_ids_.index_range()) { | Span<int> instance_ids = instance_ids_gspan->typed<int>(); | ||||
| almost_unique_ids_[i] = i; | if (almost_unique_ids_.size() != instance_ids.size()) { | ||||
| almost_unique_ids_ = generate_unique_instance_ids(instance_ids); | |||||
| } | } | ||||
| } | } | ||||
| else { | else { | ||||
| if (almost_unique_ids_.size() != instance_ids_.size()) { | almost_unique_ids_.reinitialize(this->instances_amount()); | ||||
| almost_unique_ids_ = generate_unique_instance_ids(instance_ids_); | for (const int i : almost_unique_ids_.index_range()) { | ||||
| almost_unique_ids_[i] = i; | |||||
| } | } | ||||
| } | } | ||||
| return almost_unique_ids_; | return almost_unique_ids_; | ||||
| } | } | ||||
| int InstancesComponent::attribute_domain_size(const AttributeDomain domain) const | int InstancesComponent::attribute_domain_size(const AttributeDomain domain) const | ||||
| { | { | ||||
| if (domain != ATTR_DOMAIN_INSTANCE) { | if (domain != ATTR_DOMAIN_INSTANCE) { | ||||
| ▲ Show 20 Lines • Show All 62 Lines • ▼ Show 20 Lines | public: | ||||
| } | } | ||||
| bool exists(const GeometryComponent &UNUSED(component)) const final | bool exists(const GeometryComponent &UNUSED(component)) const final | ||||
| { | { | ||||
| return true; | return true; | ||||
| } | } | ||||
| }; | }; | ||||
| class InstanceIDAttributeProvider final : public BuiltinAttributeProvider { | template<typename T> | ||||
| public: | static GVArray make_array_read_attribute(const void *data, const int domain_size) | ||||
| InstanceIDAttributeProvider() | |||||
| : BuiltinAttributeProvider( | |||||
| "id", ATTR_DOMAIN_INSTANCE, CD_PROP_INT32, Creatable, Writable, Deletable) | |||||
| { | { | ||||
| return VArray<T>::ForSpan(Span<T>((const T *)data, domain_size)); | |||||
| } | } | ||||
| GVArray try_get_for_read(const GeometryComponent &component) const final | template<typename T> | ||||
| static GVMutableArray make_array_write_attribute(void *data, const int domain_size) | |||||
| { | { | ||||
| const InstancesComponent &instances = static_cast<const InstancesComponent &>(component); | return VMutableArray<T>::ForSpan(MutableSpan<T>((T *)data, domain_size)); | ||||
| if (instances.instance_ids().is_empty()) { | |||||
| return {}; | |||||
| } | |||||
| return VArray<int>::ForSpan(instances.instance_ids()); | |||||
| } | } | ||||
| WriteAttributeLookup try_get_for_write(GeometryComponent &component) const final | |||||
| { | |||||
| InstancesComponent &instances = static_cast<InstancesComponent &>(component); | |||||
| if (instances.instance_ids().is_empty()) { | |||||
| return {}; | |||||
| } | |||||
| return {VMutableArray<int>::ForSpan(instances.instance_ids()), domain_}; | |||||
| } | |||||
| bool try_delete(GeometryComponent &component) const final | |||||
| { | |||||
| InstancesComponent &instances = static_cast<InstancesComponent &>(component); | |||||
| if (instances.instance_ids().is_empty()) { | |||||
| return false; | |||||
| } | |||||
| instances.instance_ids_clear(); | |||||
| return true; | |||||
| } | |||||
| bool try_create(GeometryComponent &component, const AttributeInit &initializer) const final | |||||
| { | |||||
| InstancesComponent &instances = static_cast<InstancesComponent &>(component); | |||||
| if (instances.instances_amount() == 0) { | |||||
| return false; | |||||
| } | |||||
| MutableSpan<int> ids = instances.instance_ids_ensure(); | |||||
| switch (initializer.type) { | |||||
| case AttributeInit::Type::Default: { | |||||
| ids.fill(0); | |||||
| break; | |||||
| } | |||||
| case AttributeInit::Type::VArray: { | |||||
| const GVArray &varray = static_cast<const AttributeInitVArray &>(initializer).varray; | |||||
| varray.materialize_to_uninitialized(varray.index_range(), ids.data()); | |||||
| break; | |||||
| } | |||||
| case AttributeInit::Type::MoveArray: { | |||||
| void *source_data = static_cast<const AttributeInitMove &>(initializer).data; | |||||
| ids.copy_from({static_cast<int *>(source_data), instances.instances_amount()}); | |||||
| MEM_freeN(source_data); | |||||
| break; | |||||
| } | |||||
| } | |||||
| return true; | |||||
| } | |||||
| bool exists(const GeometryComponent &component) const final | |||||
| { | |||||
| const InstancesComponent &instances = static_cast<const InstancesComponent &>(component); | |||||
| return !instances.instance_ids().is_empty(); | |||||
| } | |||||
| }; | |||||
| static ComponentAttributeProviders create_attribute_providers_for_instances() | static ComponentAttributeProviders create_attribute_providers_for_instances() | ||||
| { | { | ||||
| static InstancePositionAttributeProvider position; | static InstancePositionAttributeProvider position; | ||||
| static InstanceIDAttributeProvider id; | |||||
| static CustomDataAccessInfo instance_custom_data_access = { | static CustomDataAccessInfo instance_custom_data_access = { | ||||
| [](GeometryComponent &component) -> CustomData * { | [](GeometryComponent &component) -> CustomData * { | ||||
| InstancesComponent &inst = static_cast<InstancesComponent &>(component); | InstancesComponent &inst = static_cast<InstancesComponent &>(component); | ||||
| return &inst.attributes().data; | return &inst.attributes().data; | ||||
| }, | }, | ||||
| [](const GeometryComponent &component) -> const CustomData * { | [](const GeometryComponent &component) -> const CustomData * { | ||||
| const InstancesComponent &inst = static_cast<const InstancesComponent &>(component); | const InstancesComponent &inst = static_cast<const InstancesComponent &>(component); | ||||
| return &inst.attributes().data; | return &inst.attributes().data; | ||||
| }, | }, | ||||
| nullptr}; | nullptr}; | ||||
| /** | |||||
| * IDs of the instances. They are used for consistency over multiple frames for things like | |||||
| * motion blur. Proper stable ID data that actually helps when rendering can only be generated | |||||
| * in some situations, so this vector is allowed to be empty, in which case the index of each | |||||
| * instance will be used for the final ID. | |||||
| */ | |||||
| static BuiltinCustomDataLayerProvider id("id", | |||||
| ATTR_DOMAIN_POINT, | |||||
JacquesLucke: `ATTR_DOMAIN_INSTANCE`
Otherwise it breaks the spreadsheet. | |||||
| CD_PROP_INT32, | |||||
| CD_PROP_INT32, | |||||
| BuiltinAttributeProvider::Creatable, | |||||
| BuiltinAttributeProvider::Writable, | |||||
| BuiltinAttributeProvider::Deletable, | |||||
| instance_custom_data_access, | |||||
| make_array_read_attribute<int>, | |||||
| make_array_write_attribute<int>, | |||||
| nullptr); | |||||
| static CustomDataAttributeProvider instance_custom_data(ATTR_DOMAIN_INSTANCE, | static CustomDataAttributeProvider instance_custom_data(ATTR_DOMAIN_INSTANCE, | ||||
| instance_custom_data_access); | instance_custom_data_access); | ||||
| return ComponentAttributeProviders({&position, &id}, {&instance_custom_data}); | return ComponentAttributeProviders({&position, &id}, {&instance_custom_data}); | ||||
| } | } | ||||
| } // namespace blender::bke | } // namespace blender::bke | ||||
| const blender::bke::ComponentAttributeProviders *InstancesComponent::get_attribute_providers() | const blender::bke::ComponentAttributeProviders *InstancesComponent::get_attribute_providers() | ||||
| const | const | ||||
| { | { | ||||
| static blender::bke::ComponentAttributeProviders providers = | static blender::bke::ComponentAttributeProviders providers = | ||||
| blender::bke::create_attribute_providers_for_instances(); | blender::bke::create_attribute_providers_for_instances(); | ||||
| return &providers; | return &providers; | ||||
| } | } | ||||
| /** \} */ | /** \} */ | ||||
ATTR_DOMAIN_INSTANCE
Otherwise it breaks the spreadsheet.