Blender V4.3
node_geo_split_to_instances.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
6
8#include "GEO_randomize.hh"
9
10#include "BKE_curves.hh"
11#include "BKE_instances.hh"
12#include "BKE_pointcloud.hh"
13
14#include "NOD_rna_define.hh"
15
16#include "UI_interface.hh"
17#include "UI_resources.hh"
18
19#include "RNA_enum_types.hh"
20
21#include "BLI_array_utils.hh"
22
24
26{
27 b.add_input<decl::Geometry>("Geometry")
28 .supported_type({GeometryComponent::Type::Mesh,
32 b.add_input<decl::Bool>("Selection").default_value(true).field_on_all().hide_value();
33 b.add_input<decl::Int>("Group ID").field_on_all().hide_value();
34 b.add_output<decl::Geometry>("Instances")
35 .propagate_all()
36 .description("All geometry groups as separate instances");
37 b.add_output<decl::Int>("Group ID")
38 .field_on_all()
39 .description("The group ID of each group instance");
40}
41
42static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
43{
44 uiLayoutSetPropSep(layout, true);
45 uiLayoutSetPropDecorate(layout, false);
46 uiItemR(layout, ptr, "domain", UI_ITEM_NONE, "", ICON_NONE);
47}
48
49static void ensure_group_geometries(Map<int, std::unique_ptr<GeometrySet>> &geometry_by_group_id,
50 const Span<int> group_ids)
51{
52 for (const int group_id : group_ids) {
53 geometry_by_group_id.lookup_or_add_cb(group_id,
54 []() { return std::make_unique<GeometrySet>(); });
55 }
56}
57
59 std::optional<bke::GeometryFieldContext> field_context;
60 std::optional<FieldEvaluator> field_evaluator;
61
63
66};
67
71[[nodiscard]] static bool do_common_split(
72 const GeometryComponent &src_component,
73 const AttrDomain domain,
74 const Field<bool> &selection_field,
75 const Field<int> &group_id_field,
76 Map<int, std::unique_ptr<GeometrySet>> &geometry_by_group_id,
77 SplitGroups &r_groups)
78{
79 const int domain_size = src_component.attribute_domain_size(domain);
80
81 r_groups.field_context.emplace(src_component, domain);
82 FieldEvaluator &field_evaluator = r_groups.field_evaluator.emplace(*r_groups.field_context,
83 domain_size);
84 field_evaluator.set_selection(selection_field);
85 field_evaluator.add(group_id_field);
86 field_evaluator.evaluate();
87
88 const IndexMask selection = field_evaluator.get_evaluated_selection_as_mask();
89 if (selection.is_empty()) {
90 return true;
91 }
92
94 selection, field_evaluator.get_evaluated<int>(0), r_groups.memory, r_groups.group_ids);
95
96 ensure_group_geometries(geometry_by_group_id, r_groups.group_ids);
97 return false;
98}
99
100static void split_mesh_groups(const MeshComponent &component,
101 const AttrDomain domain,
102 const Field<bool> &selection_field,
103 const Field<int> &group_id_field,
104 const AttributeFilter &attribute_filter,
105 Map<int, std::unique_ptr<GeometrySet>> &geometry_by_group_id)
106{
107 SplitGroups split_groups;
108 if (do_common_split(
109 component, domain, selection_field, group_id_field, geometry_by_group_id, split_groups))
110 {
111 return;
112 }
113 const Mesh &src_mesh = *component.get();
114 const int domain_size = component.attribute_domain_size(domain);
115
116 threading::EnumerableThreadSpecific<Array<bool>> group_selection_per_thread{
117 [&]() { return Array<bool>(domain_size, false); }};
118
119 threading::parallel_for(split_groups.group_masks.index_range(), 16, [&](const IndexRange range) {
120 /* Need task isolation because of the thread local variable. */
121 threading::isolate_task([&]() {
122 MutableSpan<bool> group_selection = group_selection_per_thread.local();
123 const VArray<bool> group_selection_varray = VArray<bool>::ForSpan(group_selection);
124 for (const int group_index : range) {
125 const IndexMask &mask = split_groups.group_masks[group_index];
126 index_mask::masked_fill(group_selection, true, mask);
127 const int group_id = split_groups.group_ids[group_index];
128
129 /* Using #mesh_copy_selection here is not ideal, because it can lead to O(n^2) behavior
130 * when there are many groups. */
131 std::optional<Mesh *> group_mesh_opt = geometry::mesh_copy_selection(
132 src_mesh, group_selection_varray, domain, attribute_filter);
133 GeometrySet &group_geometry = *geometry_by_group_id.lookup(group_id);
134 if (group_mesh_opt.has_value()) {
135 if (Mesh *group_mesh = *group_mesh_opt) {
136 group_geometry.replace_mesh(group_mesh);
137 }
138 else {
139 group_geometry.replace_mesh(nullptr);
140 }
141 }
142 else {
143 group_geometry.add(component);
144 }
145
146 index_mask::masked_fill(group_selection, false, mask);
147 }
148 });
149 });
150}
151
152static void split_pointcloud_groups(const PointCloudComponent &component,
153 const Field<bool> &selection_field,
154 const Field<int> &group_id_field,
155 const AttributeFilter &attribute_filter,
156 Map<int, std::unique_ptr<GeometrySet>> &geometry_by_group_id)
157{
158 SplitGroups split_groups;
159 if (do_common_split(component,
160 AttrDomain::Point,
161 selection_field,
162 group_id_field,
163 geometry_by_group_id,
164 split_groups))
165 {
166 return;
167 }
168 const PointCloud &src_pointcloud = *component.get();
169 threading::parallel_for(split_groups.group_ids.index_range(), 16, [&](const IndexRange range) {
170 for (const int group_index : range) {
171 const IndexMask &mask = split_groups.group_masks[group_index];
172 const int group_id = split_groups.group_ids[group_index];
173
174 PointCloud *group_pointcloud = BKE_pointcloud_new_nomain(mask.size());
175
176 const AttributeAccessor src_attributes = src_pointcloud.attributes();
177 MutableAttributeAccessor dst_attributes = group_pointcloud->attributes_for_write();
178 bke::gather_attributes(src_attributes,
179 AttrDomain::Point,
180 AttrDomain::Point,
181 attribute_filter,
182 mask,
183 dst_attributes);
184
185 GeometrySet &group_geometry = *geometry_by_group_id.lookup(group_id);
186 group_geometry.replace_pointcloud(group_pointcloud);
187 }
188 });
189}
190
191static void split_curve_groups(const bke::CurveComponent &component,
192 const AttrDomain domain,
193 const Field<bool> &selection_field,
194 const Field<int> &group_id_field,
195 const AttributeFilter &attribute_filter,
196 Map<int, std::unique_ptr<GeometrySet>> &geometry_by_group_id)
197{
198 SplitGroups split_groups;
199 if (do_common_split(
200 component, domain, selection_field, group_id_field, geometry_by_group_id, split_groups))
201 {
202 return;
203 }
204 const bke::CurvesGeometry &src_curves = component.get()->geometry.wrap();
205 threading::parallel_for(split_groups.group_ids.index_range(), 16, [&](const IndexRange range) {
206 for (const int group_index : range) {
207 const IndexMask &mask = split_groups.group_masks[group_index];
208 const int group_id = split_groups.group_ids[group_index];
209
210 bke::CurvesGeometry group_curves;
211 if (domain == AttrDomain::Point) {
212 group_curves = bke::curves_copy_point_selection(src_curves, mask, attribute_filter);
213 }
214 else {
215 group_curves = bke::curves_copy_curve_selection(src_curves, mask, attribute_filter);
216 }
217 Curves *group_curves_id = bke::curves_new_nomain(std::move(group_curves));
218 GeometrySet &group_geometry = *geometry_by_group_id.lookup(group_id);
219 group_geometry.replace_curves(group_curves_id);
220 }
221 });
222}
223
224static void split_instance_groups(const InstancesComponent &component,
225 const Field<bool> &selection_field,
226 const Field<int> &group_id_field,
227 const AttributeFilter &attribute_filter,
228 Map<int, std::unique_ptr<GeometrySet>> &geometry_by_group_id)
229{
230 SplitGroups split_groups;
231 if (do_common_split(component,
232 AttrDomain::Instance,
233 selection_field,
234 group_id_field,
235 geometry_by_group_id,
236 split_groups))
237 {
238 return;
239 }
240 const bke::Instances &src_instances = *component.get();
241 threading::parallel_for(split_groups.group_ids.index_range(), 16, [&](const IndexRange range) {
242 for (const int group_index : range) {
243 const IndexMask &mask = split_groups.group_masks[group_index];
244 const int group_id = split_groups.group_ids[group_index];
245
246 bke::Instances *group_instances = new bke::Instances();
247 group_instances->resize(mask.size());
248
249 for (const bke::InstanceReference &reference : src_instances.references()) {
250 group_instances->add_reference(reference);
251 }
252
253 bke::gather_attributes(src_instances.attributes(),
254 AttrDomain::Instance,
255 AttrDomain::Instance,
256 attribute_filter,
257 mask,
258 group_instances->attributes_for_write());
259 group_instances->remove_unused_references();
260
261 GeometrySet &group_geometry = *geometry_by_group_id.lookup(group_id);
262 group_geometry.replace_instances(group_instances);
263 }
264 });
265}
266
268{
269 const bNode &node = params.node();
270 const AttrDomain domain = AttrDomain(node.custom1);
271
272 GeometrySet src_geometry = params.extract_input<GeometrySet>("Geometry");
273 const Field<bool> selection_field = params.extract_input<Field<bool>>("Selection");
274 const Field<int> group_id_field = params.extract_input<Field<int>>("Group ID");
275
276 const NodeAttributeFilter &attribute_filter = params.get_attribute_filter("Instances");
277
278 Map<int, std::unique_ptr<GeometrySet>> geometry_by_group_id;
279
280 if (src_geometry.has_mesh() &&
281 ELEM(domain, AttrDomain::Point, AttrDomain::Edge, AttrDomain::Face))
282 {
283 const auto &component = *src_geometry.get_component<MeshComponent>();
284 split_mesh_groups(component,
285 domain,
286 selection_field,
287 group_id_field,
288 attribute_filter,
289 geometry_by_group_id);
290 }
291 if (src_geometry.has_pointcloud() && domain == AttrDomain::Point) {
292 const auto &component = *src_geometry.get_component<PointCloudComponent>();
294 component, selection_field, group_id_field, attribute_filter, geometry_by_group_id);
295 }
296 if (src_geometry.has_curves() && ELEM(domain, AttrDomain::Point, AttrDomain::Curve)) {
297 const auto &component = *src_geometry.get_component<bke::CurveComponent>();
298 split_curve_groups(component,
299 domain,
300 selection_field,
301 group_id_field,
302 attribute_filter,
303 geometry_by_group_id);
304 }
305 if (src_geometry.has_instances() && domain == AttrDomain::Instance) {
306 const auto &component = *src_geometry.get_component<bke::InstancesComponent>();
308 component, selection_field, group_id_field, attribute_filter, geometry_by_group_id);
309 }
310
311 bke::Instances *dst_instances = new bke::Instances();
312 GeometrySet dst_geometry = GeometrySet::from_instances(dst_instances);
313 const int total_groups_num = geometry_by_group_id.size();
314 dst_instances->resize(total_groups_num);
315
316 std::optional<std::string> dst_group_id_attribute_id =
317 params.get_output_anonymous_attribute_id_if_needed("Group ID");
318 if (dst_group_id_attribute_id) {
319 SpanAttributeWriter<int> dst_group_id =
321 *dst_group_id_attribute_id, AttrDomain::Instance);
322 std::copy(geometry_by_group_id.keys().begin(),
323 geometry_by_group_id.keys().end(),
324 dst_group_id.span.begin());
325 dst_group_id.finish();
326 }
327
328 dst_instances->transforms_for_write().fill(float4x4::identity());
329 array_utils::fill_index_range(dst_instances->reference_handles_for_write());
330
331 for (auto item : geometry_by_group_id.items()) {
332 std::unique_ptr<GeometrySet> &group_geometry = item.value;
333 dst_instances->add_reference(std::move(group_geometry));
334 }
335
336 dst_geometry.name = src_geometry.name;
337
338 geometry::debug_randomize_instance_order(dst_instances);
339
340 params.set_output("Instances", std::move(dst_geometry));
341}
342
343static void node_rna(StructRNA *srna)
344{
346 "domain",
347 "Domain",
348 "Attribute domain for the Selection and Group ID inputs",
351 int(AttrDomain::Point));
352}
353
354static void node_register()
355{
356 static blender::bke::bNodeType ntype;
358 &ntype, GEO_NODE_SPLIT_TO_INSTANCES, "Split to Instances", NODE_CLASS_GEOMETRY);
360 ntype.declare = node_declare;
363
364 node_rna(ntype.rna_ext.srna);
365}
366NOD_REGISTER_NODE(node_register)
367
368} // namespace blender::nodes::node_geo_split_to_instances_cc
Low-level operations for curves.
#define NODE_CLASS_GEOMETRY
Definition BKE_node.hh:418
General operations for point clouds.
#define ELEM(...)
#define NOD_REGISTER_NODE(REGISTER_FUNC)
#define NOD_inline_enum_accessors(member)
void uiLayoutSetPropSep(uiLayout *layout, bool is_sep)
#define UI_ITEM_NONE
void uiLayoutSetPropDecorate(uiLayout *layout, bool is_sep)
void uiItemR(uiLayout *layout, PointerRNA *ptr, const char *propname, eUI_Item_Flag flag, const char *name, int icon)
SubIterator begin() const
Definition BLI_map.hh:730
SubIterator end() const
Definition BLI_map.hh:740
KeyIterator keys() const
Definition BLI_map.hh:837
int64_t size() const
Definition BLI_map.hh:927
ItemIterator items() const
Definition BLI_map.hh:864
IndexRange index_range() const
int attribute_domain_size(AttrDomain domain) const
MutableSpan< int > reference_handles_for_write()
Definition instances.cc:214
int add_reference(const InstanceReference &reference)
Definition instances.cc:263
void resize(int capacity)
Definition instances.cc:189
bke::MutableAttributeAccessor attributes_for_write()
MutableSpan< float4x4 > transforms_for_write()
Definition instances.cc:232
GSpanAttributeWriter lookup_or_add_for_write_span(StringRef attribute_id, AttrDomain domain, eCustomDataType data_type, const AttributeInit &initializer=AttributeInitDefaultValue())
void set_selection(Field< bool > selection)
Definition FN_field.hh:385
int add(GField field, GVArray *varray_ptr)
Definition field.cc:756
IndexMask get_evaluated_selection_as_mask() const
Definition field.cc:822
const GVArray & get_evaluated(const int field_index) const
Definition FN_field.hh:450
static Vector< IndexMask, 4 > from_group_ids(const VArray< int > &group_ids, IndexMaskMemory &memory, VectorSet< int > &r_index_by_group_id)
local_group_size(16, 16) .push_constant(Type b
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
void node_register_type(bNodeType *ntype)
Definition node.cc:1708
static void node_declare(NodeDeclarationBuilder &b)
static void split_pointcloud_groups(const PointCloudComponent &component, const Field< bool > &selection_field, const Field< int > &group_id_field, const AttributeFilter &attribute_filter, Map< int, std::unique_ptr< GeometrySet > > &geometry_by_group_id)
static bool do_common_split(const GeometryComponent &src_component, const AttrDomain domain, const Field< bool > &selection_field, const Field< int > &group_id_field, Map< int, std::unique_ptr< GeometrySet > > &geometry_by_group_id, SplitGroups &r_groups)
static void node_layout(uiLayout *layout, bContext *, PointerRNA *ptr)
static void split_mesh_groups(const MeshComponent &component, const AttrDomain domain, const Field< bool > &selection_field, const Field< int > &group_id_field, const AttributeFilter &attribute_filter, Map< int, std::unique_ptr< GeometrySet > > &geometry_by_group_id)
static void split_instance_groups(const InstancesComponent &component, const Field< bool > &selection_field, const Field< int > &group_id_field, const AttributeFilter &attribute_filter, Map< int, std::unique_ptr< GeometrySet > > &geometry_by_group_id)
static void ensure_group_geometries(Map< int, std::unique_ptr< GeometrySet > > &geometry_by_group_id, const Span< int > group_ids)
static void split_curve_groups(const bke::CurveComponent &component, const AttrDomain domain, const Field< bool > &selection_field, const Field< int > &group_id_field, const AttributeFilter &attribute_filter, Map< int, std::unique_ptr< GeometrySet > > &geometry_by_group_id)
PropertyRNA * RNA_def_node_enum(StructRNA *srna, const char *identifier, const char *ui_name, const char *ui_description, const EnumPropertyItem *static_items, const EnumRNAAccessors accessors, std::optional< int > default_value, const EnumPropertyItemFunc item_func, const bool allow_animation)
void parallel_for(const IndexRange range, const int64_t grain_size, const Function &function, const TaskSizeHints &size_hints=detail::TaskSizeHints_Static(1))
Definition BLI_task.hh:95
void geo_node_type_base(blender::bke::bNodeType *ntype, int type, const char *name, short nclass)
const EnumPropertyItem rna_enum_attribute_domain_without_corner_items[]
CurvesGeometry geometry
StructRNA * srna
Definition RNA_types.hh:780
const GeometryComponent * get_component(GeometryComponent::Type component_type) const
Defines a node type.
Definition BKE_node.hh:218
NodeGeometryExecFunction geometry_node_execute
Definition BKE_node.hh:339
void(* draw_buttons)(uiLayout *, bContext *C, PointerRNA *ptr)
Definition BKE_node.hh:238
NodeDeclareFunction declare
Definition BKE_node.hh:347
PointerRNA * ptr
Definition wm_files.cc:4126