Blender V4.3
node_geo_instance_on_points.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#include "BLI_math_matrix.h"
7#include "BLI_math_matrix.hh"
8#include "BLI_task.hh"
9
10#include "BKE_attribute_math.hh"
11#include "BKE_curves.hh"
12#include "BKE_grease_pencil.hh"
13#include "BKE_instances.hh"
14
16
17#include "node_geometry_util.hh"
18
20
22{
23 b.add_input<decl::Geometry>("Points").description("Points to instance on");
24 b.add_input<decl::Bool>("Selection").default_value(true).field_on({0}).hide_value();
25 b.add_input<decl::Geometry>("Instance").description("Geometry that is instanced on the points");
26 b.add_input<decl::Bool>("Pick Instance")
27 .field_on({0})
28 .description(
29 "Choose instances from the \"Instance\" input at each point instead of instancing the "
30 "entire geometry");
31 b.add_input<decl::Int>("Instance Index")
32 .implicit_field_on(implicit_field_inputs::id_or_index, {0})
33 .description(
34 "Index of the instance used for each point. This is only used when Pick Instances "
35 "is on. By default the point index is used");
36 b.add_input<decl::Rotation>("Rotation").field_on({0}).description("Rotation of the instances");
37 b.add_input<decl::Vector>("Scale")
38 .default_value({1.0f, 1.0f, 1.0f})
39 .subtype(PROP_XYZ)
40 .field_on({0})
41 .description("Scale of the instances");
42
43 b.add_output<decl::Geometry>("Instances").propagate_all();
44}
45
47 bke::Instances &dst_component,
48 const AttributeAccessor &src_attributes,
49 const GeometrySet &instance,
50 const fn::FieldContext &field_context,
52 const Map<StringRef, AttributeKind> &attributes_to_propagate)
53{
54 const AttrDomain domain = AttrDomain::Point;
55 const int domain_num = src_attributes.domain_size(domain);
56
57 VArray<bool> pick_instance;
60 VArray<float3> scales;
61
62 const Field<bool> selection_field = params.get_input<Field<bool>>("Selection");
63 fn::FieldEvaluator evaluator{field_context, domain_num};
64 evaluator.set_selection(selection_field);
65 /* The evaluator could use the component's stable IDs as a destination directly, but only the
66 * selected indices should be copied. */
67 evaluator.add(params.get_input<Field<bool>>("Pick Instance"), &pick_instance);
68 evaluator.add(params.get_input<Field<int>>("Instance Index"), &indices);
69 evaluator.add(params.get_input<Field<math::Quaternion>>("Rotation"), &rotations);
70 evaluator.add(params.get_input<Field<float3>>("Scale"), &scales);
71 evaluator.evaluate();
72
73 const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
74 if (selection.is_empty()) {
75 return;
76 }
77
78 /* The initial size of the component might be non-zero when this function is called for multiple
79 * component types. */
80 const int start_len = dst_component.instances_num();
81 const int select_len = selection.index_range().size();
82 dst_component.resize(start_len + select_len);
83
84 MutableSpan<int> dst_handles = dst_component.reference_handles_for_write().slice(start_len,
85 select_len);
86 MutableSpan<float4x4> dst_transforms = dst_component.transforms_for_write().slice(start_len,
87 select_len);
88
89 const VArraySpan positions = *src_attributes.lookup<float3>("position");
90
91 const bke::Instances *src_instances = instance.get_instances();
92
93 /* Maps handles from the source instances to handles on the new instance. */
94 Array<int> handle_mapping;
95 /* Only fill #handle_mapping when it may be used below. */
96 if (src_instances != nullptr &&
97 (!pick_instance.is_single() || pick_instance.get_internal_single()))
98 {
99 Span<bke::InstanceReference> src_references = src_instances->references();
100 handle_mapping.reinitialize(src_references.size());
101 for (const int src_instance_handle : src_references.index_range()) {
102 const bke::InstanceReference &reference = src_references[src_instance_handle];
103 const int dst_instance_handle = dst_component.add_reference(reference);
104 handle_mapping[src_instance_handle] = dst_instance_handle;
105 }
106 }
107
108 const int full_instance_handle = dst_component.add_reference(instance);
109 /* Add this reference last, because it is the most likely one to be removed later on. */
110 const int empty_reference_handle = dst_component.add_reference(bke::InstanceReference());
111
112 selection.foreach_index(GrainSize(1024), [&](const int64_t i, const int64_t range_i) {
113 /* Compute base transform for every instances. */
114 float4x4 &dst_transform = dst_transforms[range_i];
115 dst_transform = math::from_loc_rot_scale<float4x4>(positions[i], rotations[i], scales[i]);
116
117 /* Reference that will be used by this new instance. */
118 int dst_handle = empty_reference_handle;
119
120 const bool use_individual_instance = pick_instance[i];
121 if (use_individual_instance) {
122 if (src_instances != nullptr) {
123 const int src_instances_num = src_instances->instances_num();
124 const int original_index = indices[i];
125 /* Use #mod_i instead of `%` to get the desirable wrap around behavior where -1
126 * refers to the last element. */
127 const int index = mod_i(original_index, std::max(src_instances_num, 1));
128 if (index < src_instances_num) {
129 /* Get the reference to the source instance. */
130 const int src_handle = src_instances->reference_handles()[index];
131 dst_handle = handle_mapping[src_handle];
132
133 /* Take transforms of the source instance into account. */
134 mul_m4_m4_post(dst_transform.ptr(), src_instances->transforms()[index].ptr());
135 }
136 }
137 }
138 else {
139 /* Use entire source geometry as instance. */
140 dst_handle = full_instance_handle;
141 }
142 /* Set properties of new instance. */
143 dst_handles[range_i] = dst_handle;
144 });
145
146 if (pick_instance.is_single()) {
147 if (pick_instance.get_internal_single()) {
148 if (instance.has_realized_data()) {
149 params.error_message_add(
150 NodeWarningType::Info,
151 TIP_("Realized geometry is not used when pick instances is true"));
152 }
153 }
154 }
155
156 bke::MutableAttributeAccessor dst_attributes = dst_component.attributes_for_write();
157 for (const auto item : attributes_to_propagate.items()) {
158 const StringRef id = item.key;
159 const eCustomDataType data_type = item.value.data_type;
160 const bke::GAttributeReader src = src_attributes.lookup(id, AttrDomain::Point, data_type);
161 if (!src) {
162 /* Domain interpolation can fail if the source domain is empty. */
163 continue;
164 }
165
166 if (!dst_attributes.contains(id)) {
167 if (src.varray.size() == dst_component.instances_num() && src.sharing_info &&
168 src.varray.is_span())
169 {
171 *src.sharing_info);
172 dst_attributes.add(id, AttrDomain::Instance, data_type, init);
173 continue;
174 }
175 dst_attributes.add(id, AttrDomain::Instance, data_type, bke::AttributeInitConstruct());
176 }
177
178 GSpanAttributeWriter dst = dst_attributes.lookup_for_write_span(id);
179 array_utils::gather(src.varray, selection, dst.span.slice(start_len, select_len));
180 dst.finish();
181 }
182}
183
185{
186 GeometrySet geometry_set = params.extract_input<GeometrySet>("Points");
187 GeometrySet instance = params.get_input<GeometrySet>("Instance");
188 instance.ensure_owns_direct_data();
189 const NodeAttributeFilter &attribute_filter = params.get_attribute_filter("Instances");
190
191 geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
192 /* It's important not to invalidate the existing #InstancesComponent because it owns references
193 * to other geometry sets that are processed by this node. */
194 InstancesComponent &instances_component =
196 bke::Instances *dst_instances = instances_component.get_for_write();
197 if (dst_instances == nullptr) {
198 dst_instances = new bke::Instances();
199 instances_component.replace(dst_instances);
200 }
201
205
206 Map<StringRef, AttributeKind> attributes_to_propagate;
207 geometry_set.gather_attributes_for_propagation(types,
209 false,
210 attribute_filter,
211 attributes_to_propagate);
212 attributes_to_propagate.remove("position");
213 attributes_to_propagate.remove(".reference_index");
214
215 for (const GeometryComponent::Type type : types) {
216 if (geometry_set.has(type)) {
217 const GeometryComponent &component = *geometry_set.get_component(type);
218 const bke::GeometryFieldContext field_context{component, AttrDomain::Point};
219 add_instances_from_component(*dst_instances,
220 *component.attributes(),
221 instance,
222 field_context,
223 params,
224 attributes_to_propagate);
225 }
226 }
227 if (geometry_set.has_grease_pencil()) {
228 using namespace bke::greasepencil;
229 const GreasePencil &grease_pencil = *geometry_set.get_grease_pencil();
230 bke::Instances *instances = new bke::Instances();
231 for (const int layer_index : grease_pencil.layers().index_range()) {
232 const Drawing *drawing = grease_pencil.get_eval_drawing(grease_pencil.layer(layer_index));
233 if (drawing == nullptr) {
234 continue;
235 }
236 const bke::CurvesGeometry &src_curves = drawing->strokes();
237 if (src_curves.curves_num() == 0) {
238 /* Add an empty reference so the number of layers and instances match.
239 * This makes it easy to reconstruct the layers afterwards and keep their attributes.
240 * Although in this particular case we don't propagate the attributes. */
241 const int handle = instances->add_reference(bke::InstanceReference());
242 instances->add_instance(handle, float4x4::identity());
243 continue;
244 }
245 /* TODO: Attributes are not propagating from the curves or the points. */
246 bke::Instances *layer_instances = new bke::Instances();
247 const bke::GreasePencilLayerFieldContext field_context(
248 grease_pencil, AttrDomain::Point, layer_index);
249 add_instances_from_component(*layer_instances,
250 src_curves.attributes(),
251 instance,
252 field_context,
253 params,
254 attributes_to_propagate);
255 GeometrySet temp_set = GeometrySet::from_instances(layer_instances);
256 const int handle = instances->add_reference(bke::InstanceReference{temp_set});
257 instances->add_instance(handle, float4x4::identity());
258 }
260 geometry_set.get_grease_pencil()->attributes(),
261 instances->attributes_for_write(),
262 attribute_filter);
265 GeometrySet::from_instances(instances)},
266 attribute_filter);
267 instances_component.replace(
269
270 geometry_set.replace_grease_pencil(nullptr);
271 }
272 geometry_set.remove_geometry_during_modify();
273 });
274
275 /* Unused references may have been added above. Remove those now so that other nodes don't
276 * process them needlessly.
277 * This should eventually be moved into the loop above, but currently this is quite tricky
278 * because it might remove references that the loop still wants to iterate over. */
279 if (bke::Instances *instances = geometry_set.get_instances_for_write()) {
280 instances->remove_unused_references();
281 }
282
283 params.set_output("Instances", std::move(geometry_set));
284}
285
286static void node_register()
287{
288 static blender::bke::bNodeType ntype;
289
291 &ntype, GEO_NODE_INSTANCE_ON_POINTS, "Instance on Points", NODE_CLASS_GEOMETRY);
292 ntype.declare = node_declare;
295}
297
298} // namespace blender::nodes::node_geo_instance_on_points_cc
Low-level operations for curves.
Low-level operations for grease pencil.
#define NODE_CLASS_GEOMETRY
Definition BKE_node.hh:418
MINLINE int mod_i(int i, int n)
void mul_m4_m4_post(float R[4][4], const float B[4][4])
#define TIP_(msgid)
#define NOD_REGISTER_NODE(REGISTER_FUNC)
@ PROP_XYZ
Definition RNA_types.hh:172
void init()
void reinitialize(const int64_t new_size)
Definition BLI_array.hh:388
GMutableSpan slice(const int64_t start, int64_t size) const
const void * data() const
bool remove(const Key &key)
Definition BLI_map.hh:344
ItemIterator items() const
Definition BLI_map.hh:864
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
GAttributeReader lookup(const StringRef attribute_id) const
int domain_size(const AttrDomain domain) const
bool contains(const StringRef attribute_id) const
virtual std::optional< AttributeAccessor > attributes() const
void replace(Instances *instances, GeometryOwnershipType ownership=GeometryOwnershipType::Owned)
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()
int instances_num() const
Definition instances.cc:390
MutableSpan< float4x4 > transforms_for_write()
Definition instances.cc:232
bool add(const StringRef attribute_id, const AttrDomain domain, const eCustomDataType data_type, const AttributeInit &initializer)
GSpanAttributeWriter lookup_for_write_span(StringRef attribute_id)
const bke::CurvesGeometry & strokes() const
local_group_size(16, 16) .push_constant(Type b
static ushort indices[]
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
static char ** types
Definition makesdna.cc:71
void gather(const GVArray &src, const IndexMask &indices, GMutableSpan dst, int64_t grain_size=4096)
void node_register_type(bNodeType *ntype)
Definition node.cc:1708
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)
MatT from_loc_rot_scale(const typename MatT::loc_type &location, const RotationT &rotation, const VecBase< typename MatT::base_type, ScaleDim > &scale)
void id_or_index(const bNode &, void *r_value)
static void add_instances_from_component(bke::Instances &dst_component, const AttributeAccessor &src_attributes, const GeometrySet &instance, const fn::FieldContext &field_context, const GeoNodeExecParams &params, const Map< StringRef, AttributeKind > &attributes_to_propagate)
static void node_declare(NodeDeclarationBuilder &b)
void geo_node_type_base(blender::bke::bNodeType *ntype, int type, const char *name, short nclass)
__int64 int64_t
Definition stdint.h:89
const c_style_mat & ptr() const
const ImplicitSharingInfo * sharing_info
GeometryComponent & get_component_for_write(GeometryComponent::Type component_type)
static GeometrySet from_instances(Instances *instances, GeometryOwnershipType ownership=GeometryOwnershipType::Owned)
Instances * get_instances_for_write()
const GreasePencil * get_grease_pencil() const
void gather_attributes_for_propagation(Span< GeometryComponent::Type > component_types, GeometryComponent::Type dst_component_type, bool include_instances, const AttributeFilter &attribute_filter, Map< StringRef, AttributeKind > &r_attributes) const
bool has(const GeometryComponent::Type component_type) const
const GeometryComponent * get_component(GeometryComponent::Type component_type) const
static void propagate_attributes_from_layer_to_instances(const AttributeAccessor src_attributes, MutableAttributeAccessor dst_attributes, const AttributeFilter &attribute_filter)
void modify_geometry_sets(ForeachSubGeometryCallback callback)
void replace_grease_pencil(GreasePencil *grease_pencil, GeometryOwnershipType ownership=GeometryOwnershipType::Owned)
Defines a node type.
Definition BKE_node.hh:218
NodeGeometryExecFunction geometry_node_execute
Definition BKE_node.hh:339
NodeDeclareFunction declare
Definition BKE_node.hh:347