Blender V5.0
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::AttributeDomainAndType;
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 == bke::AttrType::String) {
29 return;
30 }
31 info.add(iter.name, AttributeDomainAndType{iter.domain, iter.data_type});
32 });
33 }
34
35 return info;
36}
37
38static void fill_new_attribute(const Span<const GeometryComponent *> src_components,
39 const StringRef attribute_id,
40 const bke::AttrType data_type,
41 const bke::AttrDomain domain,
42 GMutableSpan dst_span)
43{
44 const CPPType &cpp_type = bke::attribute_type_to_cpp_type(data_type);
45
46 int offset = 0;
47 for (const GeometryComponent *component : src_components) {
48 const int domain_num = component->attribute_domain_size(domain);
49 if (domain_num == 0) {
50 continue;
51 }
52 GVArray read_attribute = *component->attributes()->lookup_or_default(
53 attribute_id, domain, data_type, nullptr);
54
55 GVArraySpan src_span{read_attribute};
56 const void *src_buffer = src_span.data();
57 void *dst_buffer = dst_span[offset];
58 cpp_type.copy_assign_n(src_buffer, dst_buffer, domain_num);
59
60 offset += domain_num;
61 }
62}
63
66 const Span<StringRef> ignored_attributes)
67{
69 ignored_attributes);
70
71 for (const int i : info.names.index_range()) {
72 const StringRef attribute_id = info.names[i];
73 const AttributeDomainAndType &meta_data = info.kinds[i];
74
75 bke::GSpanAttributeWriter write_attribute =
76 result.attributes_for_write()->lookup_or_add_for_write_only_span(
77 attribute_id, meta_data.domain, meta_data.data_type);
78 if (!write_attribute) {
79 continue;
80 }
82 src_components, attribute_id, meta_data.data_type, meta_data.domain, write_attribute.span);
83 write_attribute.finish();
84 }
85}
86
87static void join_instances(const Span<const GeometryComponent *> src_components,
88 const bool allow_merging_instance_references,
90{
91 Array<int> offsets_data(src_components.size() + 1);
92 for (const int i : src_components.index_range()) {
93 const auto &src_component = static_cast<const bke::InstancesComponent &>(*src_components[i]);
94 offsets_data[i] = src_component.get()->instances_num();
95 }
97
98 std::unique_ptr<bke::Instances> dst_instances = std::make_unique<bke::Instances>();
99 dst_instances->resize(offsets.total_size());
100
101 MutableSpan<int> all_handles = dst_instances->reference_handles_for_write();
102
103 Map<std::reference_wrapper<const bke::InstanceReference>, int> new_handle_by_src_reference_cache;
104
105 for (const int i : src_components.index_range()) {
106 const auto &src_component = static_cast<const bke::InstancesComponent &>(*src_components[i]);
107 const bke::Instances &src_instances = *src_component.get();
108
109 const Span<bke::InstanceReference> src_references = src_instances.references();
110 Array<int> handle_map(src_references.size());
111 for (const int src_handle : src_references.index_range()) {
112 const bke::InstanceReference &src_reference = src_references[src_handle];
113 if (allow_merging_instance_references) {
114 handle_map[src_handle] = new_handle_by_src_reference_cache.lookup_or_add_cb(
115 src_reference, [&]() { return dst_instances->add_new_reference(src_reference); });
116 }
117 else {
118 handle_map[src_handle] = dst_instances->add_new_reference(src_reference);
119 }
120 }
121
122 const IndexRange dst_range = offsets[i];
123
124 const Span<int> src_handles = src_instances.reference_handles();
125 array_utils::gather(handle_map.as_span(), src_handles, all_handles.slice(dst_range));
126 }
127
128 result.replace_instances(dst_instances.release());
129 auto &dst_component = result.get_component_for_write<bke::InstancesComponent>();
130 join_attributes(src_components, dst_component, {".reference_index"});
131}
132
133static void join_volumes(const Span<const GeometryComponent *> /*src_components*/,
134 GeometrySet & /*result*/)
135{
136 /* Not yet supported. Joining volume grids with the same name requires resampling of at least one
137 * of the grids. The cell size of the resulting volume has to be determined somehow. */
138}
139
140static void join_component_type(const bke::GeometryComponent::Type component_type,
141 const Span<GeometrySet> src_geometry_sets,
142 const bke::AttributeFilter &attribute_filter,
143 const bool allow_merging_instance_references,
145{
147 for (const GeometrySet &geometry_set : src_geometry_sets) {
148 const GeometryComponent *component = geometry_set.get_component(component_type);
149 if (component != nullptr && !component->is_empty()) {
150 components.append(component);
151 }
152 }
153
154 if (components.is_empty()) {
155 return;
156 }
157 if (components.size() == 1) {
158 result.add(*components.first());
159 return;
160 }
161
162 switch (component_type) {
163 case bke::GeometryComponent::Type::Instance:
164 join_instances(components, allow_merging_instance_references, result);
165 return;
166 case bke::GeometryComponent::Type::Volume:
167 join_volumes(components, result);
168 return;
169 default:
170 break;
171 }
172
173 std::unique_ptr<bke::Instances> instances = std::make_unique<bke::Instances>();
174 instances->resize(components.size());
175 instances->transforms_for_write().fill(float4x4::identity());
176 MutableSpan<int> handles = instances->reference_handles_for_write();
177 Map<const GeometryComponent *, int> handle_by_component;
178 for (const int i : components.index_range()) {
179 const GeometryComponent *component = components[i];
180 handles[i] = handle_by_component.lookup_or_add_cb(component, [&]() {
181 GeometrySet tmp_geo;
182 tmp_geo.add(*components[i]);
183 return instances->add_new_reference(bke::InstanceReference{tmp_geo});
184 });
185 }
186
188 options.keep_original_ids = true;
189 options.realize_instance_attributes = false;
190 options.attribute_filter = attribute_filter;
191 GeometrySet joined_components =
193 result.add(joined_components.get_component_for_write(component_type));
194}
195
197 const Span<GeometrySet> geometries,
198 const bke::AttributeFilter &attribute_filter,
199 const std::optional<Span<GeometryComponent::Type>> &component_types_to_join,
200 const bool allow_merging_instance_references)
201{
203 result.name = geometries.is_empty() ? "" : geometries[0].name;
204 static const Array<GeometryComponent::Type> supported_types(
205 {GeometryComponent::Type::Mesh,
206 GeometryComponent::Type::PointCloud,
207 GeometryComponent::Type::Instance,
208 GeometryComponent::Type::Volume,
209 GeometryComponent::Type::Curve,
210 GeometryComponent::Type::GreasePencil,
211 GeometryComponent::Type::Edit});
212
213 const Span<GeometryComponent::Type> types_to_join = component_types_to_join.has_value() ?
214 *component_types_to_join :
216 supported_types);
217
218 for (const GeometryComponent::Type type : types_to_join) {
220 type, geometries, attribute_filter, allow_merging_instance_references, result);
221 }
222
223 return result;
224}
225
226} // namespace blender::geometry
Span< T > as_span() const
Definition BLI_array.hh:243
void copy_assign_n(const void *src, void *dst, int64_t n) const
const void * data() const
Value & lookup_or_add_cb(const Key &key, const CreateValueF &create_value)
Definition BLI_map.hh:620
constexpr MutableSpan slice(const int64_t start, const int64_t size) const
Definition BLI_span.hh:573
constexpr int64_t size() const
Definition BLI_span.hh:252
constexpr IndexRange index_range() const
Definition BLI_span.hh:401
constexpr bool is_empty() const
Definition BLI_span.hh:260
constexpr bool contains(const T &value) const
Definition BLI_span.hh:277
int64_t size() const
void append(const T &value)
bool is_empty() const
IndexRange index_range() const
const T & first() const
virtual bool is_empty() const
Span< int > reference_handles() const
Definition instances.cc:215
Span< InstanceReference > references() const
Definition instances.cc:275
CCL_NAMESPACE_BEGIN struct Options options
void gather(const GVArray &src, const IndexMask &indices, GMutableSpan dst, int64_t grain_size=4096)
const CPPType & attribute_type_to_cpp_type(AttrType type)
static void join_instances(const Span< const GeometryComponent * > src_components, const bool allow_merging_instance_references, 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, bool allow_merging_instance_references=true)
static void fill_new_attribute(const Span< const GeometryComponent * > src_components, const StringRef attribute_id, const bke::AttrType data_type, const bke::AttrDomain domain, GMutableSpan dst_span)
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 GeometrySet::GatheredAttributes get_final_attribute_info(const Span< const GeometryComponent * > components, const Span< StringRef > ignored_attributes)
RealizeInstancesResult realize_instances(bke::GeometrySet geometry_set, const RealizeInstancesOptions &options)
static void join_component_type(const bke::GeometryComponent::Type component_type, const Span< GeometrySet > src_geometry_sets, const bke::AttributeFilter &attribute_filter, const bool allow_merging_instance_references, GeometrySet &result)
OffsetIndices< int > accumulate_counts_to_offsets(MutableSpan< int > counts_to_offsets, int start_offset=0)
GeometryComponent & get_component_for_write(GeometryComponent::Type component_type)
void add(const GeometryComponent &component)
void add(const StringRef name, const AttributeDomainAndType &kind)
static GeometrySet from_instances(Instances *instances, GeometryOwnershipType ownership=GeometryOwnershipType::Owned)
i
Definition text_draw.cc:230
ParamHandle ** handles