Blender V4.3
join_geometries.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#include "BLI_array_utils.hh"
6
9
10#include "BKE_instances.hh"
11
12namespace blender::geometry {
13
14using bke::AttributeMetaData;
15using bke::GeometryComponent;
16using bke::GeometrySet;
17
19 const Span<const GeometryComponent *> components, const Span<StringRef> ignored_attributes)
20{
22
23 for (const GeometryComponent *component : components) {
24 component->attributes()->foreach_attribute([&](const bke::AttributeIter &iter) {
25 if (ignored_attributes.contains(iter.name)) {
26 return;
27 }
28 if (iter.data_type == CD_PROP_STRING) {
29 return;
30 }
31 info.add_or_modify(
32 iter.name,
33 [&](AttributeMetaData *meta_data_final) {
34 *meta_data_final = {iter.domain, iter.data_type};
35 },
36 [&](AttributeMetaData *meta_data_final) {
37 meta_data_final->data_type = bke::attribute_data_type_highest_complexity(
38 {meta_data_final->data_type, iter.data_type});
39 meta_data_final->domain = bke::attribute_domain_highest_priority(
40 {meta_data_final->domain, iter.domain});
41 });
42 });
43 }
44
45 return info;
46}
47
48static void fill_new_attribute(const Span<const GeometryComponent *> src_components,
49 const StringRef attribute_id,
50 const eCustomDataType data_type,
51 const bke::AttrDomain domain,
52 GMutableSpan dst_span)
53{
54 const CPPType *cpp_type = bke::custom_data_type_to_cpp_type(data_type);
55 BLI_assert(cpp_type != nullptr);
56
57 int offset = 0;
58 for (const GeometryComponent *component : src_components) {
59 const int domain_num = component->attribute_domain_size(domain);
60 if (domain_num == 0) {
61 continue;
62 }
63 GVArray read_attribute = *component->attributes()->lookup_or_default(
64 attribute_id, domain, data_type, nullptr);
65
66 GVArraySpan src_span{read_attribute};
67 const void *src_buffer = src_span.data();
68 void *dst_buffer = dst_span[offset];
69 cpp_type->copy_assign_n(src_buffer, dst_buffer, domain_num);
70
71 offset += domain_num;
72 }
73}
74
76 GeometryComponent &result,
77 const Span<StringRef> ignored_attributes)
78{
80 ignored_attributes);
81
82 for (const MapItem<StringRef, AttributeMetaData> item : info.items()) {
83 const StringRef attribute_id = item.key;
84 const AttributeMetaData &meta_data = item.value;
85
86 bke::GSpanAttributeWriter write_attribute =
87 result.attributes_for_write()->lookup_or_add_for_write_only_span(
88 attribute_id, meta_data.domain, meta_data.data_type);
89 if (!write_attribute) {
90 continue;
91 }
93 src_components, attribute_id, meta_data.data_type, meta_data.domain, write_attribute.span);
94 write_attribute.finish();
95 }
96}
97
98static void join_instances(const Span<const GeometryComponent *> src_components,
99 GeometrySet &result)
100{
101 Array<int> offsets_data(src_components.size() + 1);
102 for (const int i : src_components.index_range()) {
103 const auto &src_component = static_cast<const bke::InstancesComponent &>(*src_components[i]);
104 offsets_data[i] = src_component.get()->instances_num();
105 }
106 const OffsetIndices offsets = offset_indices::accumulate_counts_to_offsets(offsets_data);
107
108 std::unique_ptr<bke::Instances> dst_instances = std::make_unique<bke::Instances>();
109 dst_instances->resize(offsets.total_size());
110
111 MutableSpan<int> all_handles = dst_instances->reference_handles_for_write();
112
113 Map<std::reference_wrapper<const bke::InstanceReference>, int> new_handle_by_src_reference;
114
115 for (const int i : src_components.index_range()) {
116 const auto &src_component = static_cast<const bke::InstancesComponent &>(*src_components[i]);
117 const bke::Instances &src_instances = *src_component.get();
118
119 const Span<bke::InstanceReference> src_references = src_instances.references();
120 Array<int> handle_map(src_references.size());
121 for (const int src_handle : src_references.index_range()) {
122 const bke::InstanceReference &src_reference = src_references[src_handle];
123 handle_map[src_handle] = new_handle_by_src_reference.lookup_or_add_cb(
124 src_reference, [&]() { return dst_instances->add_new_reference(src_reference); });
125 }
126
127 const IndexRange dst_range = offsets[i];
128
129 const Span<int> src_handles = src_instances.reference_handles();
130 array_utils::gather(handle_map.as_span(), src_handles, all_handles.slice(dst_range));
131 }
132
133 result.replace_instances(dst_instances.release());
134 auto &dst_component = result.get_component_for_write<bke::InstancesComponent>();
135 join_attributes(src_components, dst_component, {".reference_index"});
136}
137
138static void join_volumes(const Span<const GeometryComponent *> /*src_components*/,
139 GeometrySet & /*result*/)
140{
141 /* Not yet supported. Joining volume grids with the same name requires resampling of at least one
142 * of the grids. The cell size of the resulting volume has to be determined somehow. */
143}
144
145static void join_component_type(const bke::GeometryComponent::Type component_type,
146 const Span<GeometrySet> src_geometry_sets,
147 const bke::AttributeFilter &attribute_filter,
148 GeometrySet &result)
149{
151 for (const GeometrySet &geometry_set : src_geometry_sets) {
152 const GeometryComponent *component = geometry_set.get_component(component_type);
153 if (component != nullptr && !component->is_empty()) {
154 components.append(component);
155 }
156 }
157
158 if (components.is_empty()) {
159 return;
160 }
161 if (components.size() == 1) {
162 result.add(*components.first());
163 return;
164 }
165
166 switch (component_type) {
167 case bke::GeometryComponent::Type::Instance:
168 join_instances(components, result);
169 return;
170 case bke::GeometryComponent::Type::Volume:
171 join_volumes(components, result);
172 return;
173 default:
174 break;
175 }
176
177 std::unique_ptr<bke::Instances> instances = std::make_unique<bke::Instances>();
178 instances->resize(components.size());
179 instances->transforms_for_write().fill(float4x4::identity());
180 MutableSpan<int> handles = instances->reference_handles_for_write();
181 Map<const GeometryComponent *, int> handle_by_component;
182 for (const int i : components.index_range()) {
183 const GeometryComponent *component = components[i];
184 handles[i] = handle_by_component.lookup_or_add_cb(component, [&]() {
185 GeometrySet tmp_geo;
186 tmp_geo.add(*components[i]);
187 return instances->add_new_reference(bke::InstanceReference{tmp_geo});
188 });
189 }
190
192 options.keep_original_ids = true;
193 options.realize_instance_attributes = false;
194 options.attribute_filter = attribute_filter;
195 GeometrySet joined_components = realize_instances(
196 GeometrySet::from_instances(instances.release()), options);
197 result.add(joined_components.get_component_for_write(component_type));
198}
199
201 const Span<GeometrySet> geometries,
202 const bke::AttributeFilter &attribute_filter,
203 const std::optional<Span<GeometryComponent::Type>> &component_types_to_join)
204{
206 result.name = geometries.is_empty() ? "" : geometries[0].name;
207 static const Array<GeometryComponent::Type> supported_types(
208 {GeometryComponent::Type::Mesh,
209 GeometryComponent::Type::PointCloud,
210 GeometryComponent::Type::Instance,
211 GeometryComponent::Type::Volume,
212 GeometryComponent::Type::Curve,
213 GeometryComponent::Type::GreasePencil,
214 GeometryComponent::Type::Edit});
215
216 const Span<GeometryComponent::Type> types_to_join = component_types_to_join.has_value() ?
217 *component_types_to_join :
219 supported_types);
220
221 for (const GeometryComponent::Type type : types_to_join) {
222 join_component_type(type, geometries, attribute_filter, result);
223 }
224
225 return result;
226}
227
228} // namespace blender::geometry
#define BLI_assert(a)
Definition BLI_assert.h:50
@ CD_PROP_STRING
Span< T > as_span() const
Definition BLI_array.hh:232
void copy_assign_n(const void *src, void *dst, int64_t n) const
Value & lookup_or_add_cb(const Key &key, const CreateValueF &create_value)
Definition BLI_map.hh:582
ItemIterator items() const
Definition BLI_map.hh:864
auto add_or_modify(const Key &key, const CreateValueF &create_value, const ModifyValueF &modify_value) -> decltype(create_value(nullptr))
Definition BLI_map.hh:457
constexpr MutableSpan slice(const int64_t start, const int64_t size) const
Definition BLI_span.hh:574
constexpr int64_t size() const
Definition BLI_span.hh:253
constexpr IndexRange index_range() const
Definition BLI_span.hh:402
constexpr bool contains(const T &value) const
Definition BLI_span.hh:278
virtual bool is_empty() const
Span< int > reference_handles() const
Definition instances.cc:207
Span< InstanceReference > references() const
Definition instances.cc:277
CCL_NAMESPACE_BEGIN struct Options options
eCustomDataType attribute_data_type_highest_complexity(Span< eCustomDataType > data_types)
AttrDomain attribute_domain_highest_priority(Span< AttrDomain > domains)
static void fill_new_attribute(const Span< const GeometryComponent * > src_components, const StringRef attribute_id, const eCustomDataType data_type, const bke::AttrDomain domain, GMutableSpan dst_span)
static void join_instances(const Span< const GeometryComponent * > src_components, GeometrySet &result)
static void join_volumes(const Span< const GeometryComponent * >, GeometrySet &)
void join_attributes(const Span< const bke::GeometryComponent * > src_components, bke::GeometryComponent &r_result, const Span< StringRef > ignored_attributes={})
static void join_component_type(const bke::GeometryComponent::Type component_type, const Span< GeometrySet > src_geometry_sets, const bke::AttributeFilter &attribute_filter, GeometrySet &result)
bke::GeometrySet join_geometries(Span< bke::GeometrySet > geometries, const bke::AttributeFilter &attribute_filter, const std::optional< Span< bke::GeometryComponent::Type > > &component_types_to_join=std::nullopt)
static Map< StringRef, AttributeMetaData > get_final_attribute_info(const Span< const GeometryComponent * > components, const Span< StringRef > ignored_attributes)
bke::GeometrySet realize_instances(bke::GeometrySet geometry_set, const RealizeInstancesOptions &options)
GeometryComponent & get_component_for_write(GeometryComponent::Type component_type)
void add(const GeometryComponent &component)