Files
test2/source/blender/geometry/intern/join_geometries.cc
Hans Goudey 19001c9e6c Cleanup: Move attribute domain enum to C++ header, use enum class
Each value is now out of the global namespace, so they can be shorter
and easier to read. Most of this commit just adds the necessary casting
and namespace specification. `enum class` can be forward declared since
it has a specified size. We will make use of that in the next commit.
2023-12-20 13:25:28 -05:00

211 lines
8.1 KiB
C++

/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BLI_array_utils.hh"
#include "GEO_join_geometries.hh"
#include "GEO_realize_instances.hh"
#include "BKE_instances.hh"
namespace blender::geometry {
using bke::AttributeIDRef;
using bke::AttributeMetaData;
using bke::GeometryComponent;
using bke::GeometrySet;
static Map<AttributeIDRef, AttributeMetaData> get_final_attribute_info(
const Span<const GeometryComponent *> components, const Span<StringRef> ignored_attributes)
{
Map<AttributeIDRef, AttributeMetaData> info;
for (const GeometryComponent *component : components) {
component->attributes()->for_all(
[&](const bke::AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) {
if (ignored_attributes.contains(attribute_id.name())) {
return true;
}
if (meta_data.data_type == CD_PROP_STRING) {
return true;
}
info.add_or_modify(
attribute_id,
[&](AttributeMetaData *meta_data_final) { *meta_data_final = meta_data; },
[&](AttributeMetaData *meta_data_final) {
meta_data_final->data_type = bke::attribute_data_type_highest_complexity(
{meta_data_final->data_type, meta_data.data_type});
meta_data_final->domain = bke::attribute_domain_highest_priority(
{meta_data_final->domain, meta_data.domain});
});
return true;
});
}
return info;
}
static void fill_new_attribute(const Span<const GeometryComponent *> src_components,
const AttributeIDRef &attribute_id,
const eCustomDataType data_type,
const bke::AttrDomain domain,
GMutableSpan dst_span)
{
const CPPType *cpp_type = bke::custom_data_type_to_cpp_type(data_type);
BLI_assert(cpp_type != nullptr);
int offset = 0;
for (const GeometryComponent *component : src_components) {
const int domain_num = component->attribute_domain_size(domain);
if (domain_num == 0) {
continue;
}
GVArray read_attribute = *component->attributes()->lookup_or_default(
attribute_id, domain, data_type, nullptr);
GVArraySpan src_span{read_attribute};
const void *src_buffer = src_span.data();
void *dst_buffer = dst_span[offset];
cpp_type->copy_assign_n(src_buffer, dst_buffer, domain_num);
offset += domain_num;
}
}
static void join_attributes(const Span<const GeometryComponent *> src_components,
GeometryComponent &result,
const Span<StringRef> ignored_attributes = {})
{
const Map<AttributeIDRef, AttributeMetaData> info = get_final_attribute_info(src_components,
ignored_attributes);
for (const MapItem<AttributeIDRef, AttributeMetaData> item : info.items()) {
const AttributeIDRef attribute_id = item.key;
const AttributeMetaData &meta_data = item.value;
bke::GSpanAttributeWriter write_attribute =
result.attributes_for_write()->lookup_or_add_for_write_only_span(
attribute_id, meta_data.domain, meta_data.data_type);
if (!write_attribute) {
continue;
}
fill_new_attribute(
src_components, attribute_id, meta_data.data_type, meta_data.domain, write_attribute.span);
write_attribute.finish();
}
}
static void join_instances(const Span<const GeometryComponent *> src_components,
GeometrySet &result)
{
Array<int> offsets_data(src_components.size() + 1);
for (const int i : src_components.index_range()) {
const auto &src_component = static_cast<const bke::InstancesComponent &>(*src_components[i]);
offsets_data[i] = src_component.get()->instances_num();
}
const OffsetIndices offsets = offset_indices::accumulate_counts_to_offsets(offsets_data);
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 (const int i : src_components.index_range()) {
const auto &src_component = static_cast<const bke::InstancesComponent &>(*src_components[i]);
const bke::Instances &src_instances = *src_component.get();
const Span<bke::InstanceReference> src_references = src_instances.references();
Array<int> handle_map(src_references.size());
for (const int src_handle : src_references.index_range()) {
handle_map[src_handle] = dst_instances->add_reference(src_references[src_handle]);
}
const IndexRange dst_range = offsets[i];
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"});
}
static void join_volumes(const Span<const GeometryComponent *> /*src_components*/,
GeometrySet & /*result*/)
{
/* Not yet supported. Joining volume grids with the same name requires resampling of at least one
* of the grids. The cell size of the resulting volume has to be determined somehow. */
}
static void join_component_type(const bke::GeometryComponent::Type component_type,
const Span<GeometrySet> src_geometry_sets,
const bke::AnonymousAttributePropagationInfo &propagation_info,
GeometrySet &result)
{
Vector<const GeometryComponent *> components;
for (const GeometrySet &geometry_set : src_geometry_sets) {
const GeometryComponent *component = geometry_set.get_component(component_type);
if (component != nullptr && !component->is_empty()) {
components.append(component);
}
}
if (components.is_empty()) {
return;
}
if (components.size() == 1) {
result.add(*components.first());
return;
}
switch (component_type) {
case bke::GeometryComponent::Type::Instance:
join_instances(components, result);
return;
case bke::GeometryComponent::Type::Volume:
join_volumes(components, result);
return;
default:
break;
}
std::unique_ptr<bke::Instances> instances = std::make_unique<bke::Instances>();
for (const GeometryComponent *component : components) {
GeometrySet tmp_geo;
tmp_geo.add(*component);
const int handle = instances->add_reference(bke::InstanceReference{tmp_geo});
instances->add_instance(handle, float4x4::identity());
}
RealizeInstancesOptions options;
options.keep_original_ids = true;
options.realize_instance_attributes = false;
options.propagation_info = propagation_info;
GeometrySet joined_components = realize_instances(
GeometrySet::from_instances(instances.release()), options);
result.add(joined_components.get_component_for_write(component_type));
}
GeometrySet join_geometries(const Span<GeometrySet> geometries,
const bke::AnonymousAttributePropagationInfo &propagation_info)
{
GeometrySet result;
static const Array<GeometryComponent::Type> supported_types({GeometryComponent::Type::Mesh,
GeometryComponent::Type::PointCloud,
GeometryComponent::Type::Instance,
GeometryComponent::Type::Volume,
GeometryComponent::Type::Curve,
GeometryComponent::Type::Edit});
for (const GeometryComponent::Type type : supported_types) {
join_component_type(type, geometries, propagation_info, result);
}
return result;
}
} // namespace blender::geometry