Cleanup: move join geometry functionality to separate file

This allows us to reuse the functionality more easily.
This commit is contained in:
Jacques Lucke
2023-10-06 18:31:27 +02:00
parent 6279be4157
commit 57e914fc8a
4 changed files with 222 additions and 180 deletions

View File

@@ -18,6 +18,7 @@ set(SRC
intern/add_curves_on_mesh.cc
intern/curve_constraints.cc
intern/fillet_curves.cc
intern/join_geometries.cc
intern/mesh_copy_selection.cc
intern/mesh_merge_by_distance.cc
intern/mesh_primitive_cuboid.cc
@@ -43,6 +44,7 @@ set(SRC
GEO_add_curves_on_mesh.hh
GEO_curve_constraints.hh
GEO_fillet_curves.hh
GEO_join_geometries.hh
GEO_mesh_copy_selection.hh
GEO_mesh_merge_by_distance.hh
GEO_mesh_primitive_cuboid.hh

View File

@@ -0,0 +1,15 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include "BKE_anonymous_attribute_id.hh"
#include "BKE_geometry_set.hh"
namespace blender::geometry {
bke::GeometrySet join_geometries(Span<bke::GeometrySet> geometries,
const bke::AnonymousAttributePropagationInfo &propagation_info);
}

View File

@@ -0,0 +1,203 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#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;
template<typename Component>
static Array<const GeometryComponent *> to_base_components(Span<const Component *> components)
{
return components;
}
static Map<AttributeIDRef, AttributeMetaData> get_final_attribute_info(
Span<const GeometryComponent *> components, 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 = blender::bke::attribute_data_type_highest_complexity(
{meta_data_final->data_type, meta_data.data_type});
meta_data_final->domain = blender::bke::attribute_domain_highest_priority(
{meta_data_final->domain, meta_data.domain});
});
return true;
});
}
return info;
}
static void fill_new_attribute(Span<const GeometryComponent *> src_components,
const AttributeIDRef &attribute_id,
const eCustomDataType data_type,
const eAttrDomain 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(Span<const GeometryComponent *> src_components,
GeometryComponent &result,
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_components(Span<const bke::InstancesComponent *> src_components,
GeometrySet &result)
{
std::unique_ptr<bke::Instances> dst_instances = std::make_unique<bke::Instances>();
int tot_instances = 0;
for (const bke::InstancesComponent *src_component : src_components) {
tot_instances += src_component->get()->instances_num();
}
dst_instances->reserve(tot_instances);
for (const bke::InstancesComponent *src_component : src_components) {
const bke::Instances &src_instances = *src_component->get();
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]);
}
Span<float4x4> src_transforms = src_instances.transforms();
Span<int> src_reference_handles = src_instances.reference_handles();
for (const int i : src_transforms.index_range()) {
const int src_handle = src_reference_handles[i];
const int dst_handle = handle_map[src_handle];
const float4x4 &transform = src_transforms[i];
dst_instances->add_instance(dst_handle, transform);
}
}
result.replace_instances(dst_instances.release());
bke::InstancesComponent &dst_component =
result.get_component_for_write<bke::InstancesComponent>();
join_attributes(to_base_components(src_components), dst_component, {"position"});
}
static void join_components(Span<const bke::VolumeComponent *> /*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. */
}
template<typename Component>
static void join_component_type(Span<GeometrySet> src_geometry_sets,
GeometrySet &result,
const bke::AnonymousAttributePropagationInfo &propagation_info)
{
Vector<const Component *> components;
for (const GeometrySet &geometry_set : src_geometry_sets) {
const Component *component = geometry_set.get_component<Component>();
if (component != nullptr && !component->is_empty()) {
components.append(component);
}
}
if (components.size() == 0) {
return;
}
if (components.size() == 1) {
result.add(*components[0]);
return;
}
if constexpr (is_same_any_v<Component, bke::InstancesComponent, bke::VolumeComponent>) {
join_components(components, result);
}
else {
std::unique_ptr<bke::Instances> instances = std::make_unique<bke::Instances>();
for (const Component *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 = geometry::realize_instances(
GeometrySet::from_instances(instances.release()), options);
result.add(joined_components.get_component_for_write<Component>());
}
}
GeometrySet join_geometries(Span<GeometrySet> geometries,
const bke::AnonymousAttributePropagationInfo &propagation_info)
{
GeometrySet result;
join_component_type<bke::MeshComponent>(geometries, result, propagation_info);
join_component_type<bke::PointCloudComponent>(geometries, result, propagation_info);
join_component_type<bke::InstancesComponent>(geometries, result, propagation_info);
join_component_type<bke::VolumeComponent>(geometries, result, propagation_info);
join_component_type<bke::CurveComponent>(geometries, result, propagation_info);
join_component_type<bke::GeometryComponentEditData>(geometries, result, propagation_info);
return result;
}
} // namespace blender::geometry

View File

@@ -2,7 +2,7 @@
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "GEO_realize_instances.hh"
#include "GEO_join_geometries.hh"
#include "BKE_instances.hh"
@@ -16,177 +16,6 @@ static void node_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Geometry>("Geometry").propagate_all();
}
template<typename Component>
static Array<const GeometryComponent *> to_base_components(Span<const Component *> components)
{
return components;
}
static Map<AttributeIDRef, AttributeMetaData> get_final_attribute_info(
Span<const GeometryComponent *> components, 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 = blender::bke::attribute_data_type_highest_complexity(
{meta_data_final->data_type, meta_data.data_type});
meta_data_final->domain = blender::bke::attribute_domain_highest_priority(
{meta_data_final->domain, meta_data.domain});
});
return true;
});
}
return info;
}
static void fill_new_attribute(Span<const GeometryComponent *> src_components,
const AttributeIDRef &attribute_id,
const eCustomDataType data_type,
const eAttrDomain 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(Span<const GeometryComponent *> src_components,
GeometryComponent &result,
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;
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_components(Span<const InstancesComponent *> src_components, GeometrySet &result)
{
std::unique_ptr<bke::Instances> dst_instances = std::make_unique<bke::Instances>();
int tot_instances = 0;
for (const InstancesComponent *src_component : src_components) {
tot_instances += src_component->get()->instances_num();
}
dst_instances->reserve(tot_instances);
for (const InstancesComponent *src_component : src_components) {
const bke::Instances &src_instances = *src_component->get();
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]);
}
Span<float4x4> src_transforms = src_instances.transforms();
Span<int> src_reference_handles = src_instances.reference_handles();
for (const int i : src_transforms.index_range()) {
const int src_handle = src_reference_handles[i];
const int dst_handle = handle_map[src_handle];
const float4x4 &transform = src_transforms[i];
dst_instances->add_instance(dst_handle, transform);
}
}
result.replace_instances(dst_instances.release());
InstancesComponent &dst_component = result.get_component_for_write<InstancesComponent>();
join_attributes(to_base_components(src_components), dst_component, {"position"});
}
static void join_components(Span<const VolumeComponent *> /*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. */
}
template<typename Component>
static void join_component_type(Span<GeometrySet> src_geometry_sets,
GeometrySet &result,
const AnonymousAttributePropagationInfo &propagation_info)
{
Vector<const Component *> components;
for (const GeometrySet &geometry_set : src_geometry_sets) {
const Component *component = geometry_set.get_component<Component>();
if (component != nullptr && !component->is_empty()) {
components.append(component);
}
}
if (components.size() == 0) {
return;
}
if (components.size() == 1) {
result.add(*components[0]);
return;
}
if constexpr (is_same_any_v<Component, InstancesComponent, VolumeComponent>) {
join_components(components, result);
}
else {
std::unique_ptr<bke::Instances> instances = std::make_unique<bke::Instances>();
for (const Component *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());
}
geometry::RealizeInstancesOptions options;
options.keep_original_ids = true;
options.realize_instance_attributes = false;
options.propagation_info = propagation_info;
GeometrySet joined_components = geometry::realize_instances(
GeometrySet::from_instances(instances.release()), options);
result.add(joined_components.get_component_for_write<Component>());
}
}
static void node_geo_exec(GeoNodeExecParams params)
{
Vector<GeometrySet> geometry_sets = params.extract_input<Vector<GeometrySet>>("Geometry");
@@ -198,14 +27,7 @@ static void node_geo_exec(GeoNodeExecParams params)
GeometryComponentEditData::remember_deformed_curve_positions_if_necessary(geometry);
}
GeometrySet geometry_set_result;
join_component_type<MeshComponent>(geometry_sets, geometry_set_result, propagation_info);
join_component_type<PointCloudComponent>(geometry_sets, geometry_set_result, propagation_info);
join_component_type<InstancesComponent>(geometry_sets, geometry_set_result, propagation_info);
join_component_type<VolumeComponent>(geometry_sets, geometry_set_result, propagation_info);
join_component_type<CurveComponent>(geometry_sets, geometry_set_result, propagation_info);
join_component_type<GeometryComponentEditData>(
geometry_sets, geometry_set_result, propagation_info);
GeometrySet geometry_set_result = geometry::join_geometries(geometry_sets, propagation_info);
params.set_output("Geometry", std::move(geometry_set_result));
}