Files
test/source/blender/blenkernel/intern/instances_attributes.cc

180 lines
6.4 KiB
C++

/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BKE_instances.hh"
#include "attribute_access_intern.hh"
#include "attribute_storage_access.hh"
namespace blender::bke {
static void tag_component_reference_index_changed(void *owner)
{
Instances &instances = *static_cast<Instances *>(owner);
instances.tag_reference_handles_changed();
}
static const auto &changed_tags()
{
static Map<StringRef, AttrUpdateOnChange> attributes{
{".reference_index", tag_component_reference_index_changed},
};
return attributes;
}
static const auto &builtin_attributes()
{
static auto attributes = []() {
Map<StringRef, AttrBuiltinInfo> map;
/**
* 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.
*/
AttrBuiltinInfo id(bke::AttrDomain::Instance, bke::AttrType::Int32);
map.add_new("id", std::move(id));
AttrBuiltinInfo instance_transform(bke::AttrDomain::Instance, bke::AttrType::Float4x4);
instance_transform.deletable = false;
map.add_new("instance_transform", std::move(instance_transform));
/** Indices into `Instances::references_`. Determines what data is instanced. */
AttrBuiltinInfo reference_index(bke::AttrDomain::Instance, bke::AttrType::Int32);
reference_index.deletable = false;
map.add_new(".reference_index", std::move(reference_index));
return map;
}();
return attributes;
}
static constexpr AttributeAccessorFunctions get_instances_accessor_functions()
{
AttributeAccessorFunctions fn{};
fn.domain_supported = [](const void * /*owner*/, const AttrDomain domain) {
return domain == AttrDomain::Instance;
};
fn.domain_size = [](const void *owner, const AttrDomain domain) {
return domain == AttrDomain::Instance ?
static_cast<const Instances *>(owner)->instances_num() :
0;
};
fn.builtin_domain_and_type = [](const void * /*owner*/,
const StringRef name) -> std::optional<AttributeDomainAndType> {
const AttrBuiltinInfo *info = builtin_attributes().lookup_ptr(name);
if (!info) {
return std::nullopt;
}
return AttributeDomainAndType{info->domain, info->type};
};
fn.get_builtin_default = [](const void * /*owner*/, StringRef name) -> GPointer {
const AttrBuiltinInfo &info = builtin_attributes().lookup(name);
return info.default_value;
};
fn.lookup = [](const void *owner, const StringRef name) -> GAttributeReader {
const Instances &instances = *static_cast<const Instances *>(owner);
const AttributeStorage &storage = instances.attribute_storage();
const Attribute *attribute = storage.lookup(name);
if (!attribute) {
return {};
}
return attribute_to_reader(*attribute, AttrDomain::Instance, instances.instances_num());
};
fn.adapt_domain = [](const void * /*owner*/,
const GVArray &varray,
const AttrDomain from_domain,
const AttrDomain to_domain) {
if (from_domain == to_domain && from_domain == AttrDomain::Instance) {
return varray;
}
return GVArray{};
};
fn.foreach_attribute = [](const void *owner,
const FunctionRef<void(const AttributeIter &)> fn,
const AttributeAccessor &accessor) {
const Instances &instances = *static_cast<const Instances *>(owner);
const AttributeStorage &storage = instances.attribute_storage();
storage.foreach_with_stop([&](const Attribute &attribute) {
const auto get_fn = [&]() {
return attribute_to_reader(attribute, AttrDomain::Instance, instances.instances_num());
};
AttributeIter iter(attribute.name(), attribute.domain(), attribute.data_type(), get_fn);
iter.is_builtin = builtin_attributes().contains(attribute.name());
iter.accessor = &accessor;
fn(iter);
return !iter.is_stopped();
});
};
fn.lookup_validator = [](const void * /*owner*/, const StringRef name) -> AttributeValidator {
const AttrBuiltinInfo *info = builtin_attributes().lookup_ptr(name);
if (!info) {
return {};
}
return info->validator;
};
fn.lookup_for_write = [](void *owner, const StringRef name) -> GAttributeWriter {
Instances &instances = *static_cast<Instances *>(owner);
AttributeStorage &storage = instances.attribute_storage();
Attribute *attribute = storage.lookup(name);
if (!attribute) {
return {};
}
return attribute_to_writer(&instances, changed_tags(), instances.instances_num(), *attribute);
};
fn.remove = [](void *owner, const StringRef name) -> bool {
Instances &instances = *static_cast<Instances *>(owner);
AttributeStorage &storage = instances.attribute_storage();
if (const AttrBuiltinInfo *info = builtin_attributes().lookup_ptr(name)) {
if (!info->deletable) {
return false;
}
}
const std::optional<AttrUpdateOnChange> fn = changed_tags().lookup_try(name);
const bool removed = storage.remove(name);
if (!removed) {
return false;
}
if (fn) {
(*fn)(owner);
}
return true;
};
fn.add = [](void *owner,
const StringRef name,
const AttrDomain domain,
const AttrType type,
const AttributeInit &initializer) {
Instances &instances = *static_cast<Instances *>(owner);
const int domain_size = instances.instances_num();
AttributeStorage &storage = instances.attribute_storage();
if (const AttrBuiltinInfo *info = builtin_attributes().lookup_ptr(name)) {
if (info->domain != domain || info->type != type) {
return false;
}
}
if (storage.lookup(name)) {
return false;
}
storage.add(name, domain, type, attribute_init_to_data(type, domain_size, initializer));
if (initializer.type != AttributeInit::Type::Construct) {
if (const std::optional<AttrUpdateOnChange> fn = changed_tags().lookup_try(name)) {
(*fn)(owner);
}
}
return true;
};
return fn;
}
const AttributeAccessorFunctions &instance_attribute_accessor_functions()
{
static const AttributeAccessorFunctions fn = get_instances_accessor_functions();
return fn;
}
} // namespace blender::bke