Blender V5.0
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_curves.hh"
11#include "BKE_grease_pencil.hh"
12#include "BKE_instances.hh"
13
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(NODE_DEFAULT_INPUT_ID_INDEX_FIELD, {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 bke::GeometrySet::GatheredAttributes &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(
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 int i : attributes_to_propagate.names.index_range()) {
158 if (ELEM(attributes_to_propagate.names[i], "position", ".reference_index")) {
159 continue;
160 }
161 const StringRef id = attributes_to_propagate.names[i];
162 const bke::AttrType data_type = attributes_to_propagate.kinds[i].data_type;
163 const bke::GAttributeReader src = src_attributes.lookup(id, AttrDomain::Point, data_type);
164 if (!src) {
165 /* Domain interpolation can fail if the source domain is empty. */
166 continue;
167 }
168
169 if (!dst_attributes.contains(id)) {
170 if (src.varray.size() == dst_component.instances_num() && src.sharing_info &&
171 src.varray.is_span())
172 {
174 *src.sharing_info);
175 dst_attributes.add(id, AttrDomain::Instance, data_type, init);
176 continue;
177 }
178 dst_attributes.add(id, AttrDomain::Instance, data_type, bke::AttributeInitConstruct());
179 }
180
181 GSpanAttributeWriter dst = dst_attributes.lookup_for_write_span(id);
182 array_utils::gather(src.varray, selection, dst.span.slice(start_len, select_len));
183 dst.finish();
184 }
185}
186
188{
189 GeometrySet geometry_set = params.extract_input<GeometrySet>("Points");
190 GeometrySet instance = params.get_input<GeometrySet>("Instance");
191 instance.ensure_owns_direct_data();
192 const NodeAttributeFilter &attribute_filter = params.get_attribute_filter("Instances");
193
194 geometry::foreach_real_geometry(geometry_set, [&](GeometrySet &geometry_set) {
195 bke::Instances *dst_instances = new bke::Instances();
196
197 const Array<GeometryComponent::Type> types{GeometryComponent::Type::Mesh,
198 GeometryComponent::Type::PointCloud,
199 GeometryComponent::Type::Curve};
200
201 bke::GeometrySet::GatheredAttributes attributes_to_propagate;
203 GeometryComponent::Type::Instance,
204 false,
205 attribute_filter,
206 attributes_to_propagate);
207
208 for (const GeometryComponent::Type type : types) {
209 if (geometry_set.has(type)) {
210 const GeometryComponent &component = *geometry_set.get_component(type);
211 const bke::GeometryFieldContext field_context{component, AttrDomain::Point};
212 add_instances_from_component(*dst_instances,
213 *component.attributes(),
214 instance,
215 field_context,
216 params,
217 attributes_to_propagate);
218 }
219 }
220 if (geometry_set.has_grease_pencil()) {
221 using namespace bke::greasepencil;
222 const GreasePencil &grease_pencil = *geometry_set.get_grease_pencil();
223 bke::Instances *instances_per_layer = new bke::Instances();
224 for (const int layer_index : grease_pencil.layers().index_range()) {
225 const Layer &layer = grease_pencil.layer(layer_index);
226 const Drawing *drawing = grease_pencil.get_eval_drawing(layer);
227 if (drawing == nullptr) {
228 continue;
229 }
230 const float4x4 &layer_transform = layer.local_transform();
231 const bke::CurvesGeometry &src_curves = drawing->strokes();
232 if (src_curves.is_empty()) {
233 /* Add an empty reference so the number of layers and instances match.
234 * This makes it easy to reconstruct the layers afterwards and keep their attributes.
235 * Although in this particular case we don't propagate the attributes. */
236 const int handle = instances_per_layer->add_reference(bke::InstanceReference());
237 instances_per_layer->add_instance(handle, layer_transform);
238 continue;
239 }
240 /* TODO: Attributes are not propagating from the curves or the points. */
241 bke::Instances *layer_instances = new bke::Instances();
242 const bke::GreasePencilLayerFieldContext field_context(
243 grease_pencil, AttrDomain::Point, layer_index);
244 add_instances_from_component(*layer_instances,
245 src_curves.attributes(),
246 instance,
247 field_context,
248 params,
249 attributes_to_propagate);
250 GeometrySet temp_set = GeometrySet::from_instances(layer_instances);
251 const int handle = instances_per_layer->add_reference(bke::InstanceReference{temp_set});
252 instances_per_layer->add_instance(handle, layer_transform);
253 }
254
255 bke::copy_attributes(geometry_set.get_grease_pencil()->attributes(),
258 attribute_filter,
259 instances_per_layer->attributes_for_write());
261 {GeometrySet::from_instances(dst_instances),
262 GeometrySet::from_instances(instances_per_layer)},
263 attribute_filter);
264 dst_instances = new_instances.get_component_for_write<InstancesComponent>().release();
265 }
266 geometry_set.keep_only({GeometryComponent::Type::Edit});
267 geometry_set.replace_instances(dst_instances);
268 });
269
270 /* Unused references may have been added above. Remove those now so that other nodes don't
271 * process them needlessly.
272 * This should eventually be moved into the loop above, but currently this is quite tricky
273 * because it might remove references that the loop still wants to iterate over. */
274 if (bke::Instances *instances = geometry_set.get_instances_for_write()) {
275 instances->remove_unused_references();
276 }
277
278 params.set_output("Instances", std::move(geometry_set));
279}
280
281static void node_register()
282{
283 static blender::bke::bNodeType ntype;
284
285 geo_node_type_base(&ntype, "GeometryNodeInstanceOnPoints", GEO_NODE_INSTANCE_ON_POINTS);
286 ntype.ui_name = "Instance on Points";
287 ntype.ui_description =
288 "Generate a reference to geometry at each of the input points, without duplicating its "
289 "underlying data";
290 ntype.enum_name_legacy = "INSTANCE_ON_POINTS";
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:461
#define GEO_NODE_INSTANCE_ON_POINTS
MINLINE int mod_i(int i, int n)
void mul_m4_m4_post(float R[4][4], const float B[4][4])
#define ELEM(...)
#define TIP_(msgid)
@ NODE_DEFAULT_INPUT_ID_INDEX_FIELD
#define NOD_REGISTER_NODE(REGISTER_FUNC)
@ PROP_XYZ
Definition RNA_types.hh:269
void init()
long long int int64_t
constexpr MutableSpan slice(const int64_t start, const int64_t size) const
Definition BLI_span.hh:573
void reinitialize(const int64_t new_size)
Definition BLI_array.hh:419
GMutableSpan slice(const int64_t start, int64_t size) const
const void * data() const
constexpr int64_t size() const
constexpr int64_t size() const
Definition BLI_span.hh:252
constexpr IndexRange index_range() const
Definition BLI_span.hh:401
bool contains(StringRef attribute_id) const
GAttributeReader lookup(const StringRef attribute_id) const
int domain_size(const AttrDomain domain) const
AttributeAccessor attributes() const
virtual std::optional< AttributeAccessor > attributes() const
MutableSpan< int > reference_handles_for_write()
Definition instances.cc:222
int add_reference(const InstanceReference &reference)
Definition instances.cc:261
void add_instance(int instance_handle, const float4x4 &transform)
Definition instances.cc:204
void resize(int capacity)
Definition instances.cc:191
bke::MutableAttributeAccessor attributes_for_write()
Definition instances.cc:69
int instances_num() const
Definition instances.cc:393
MutableSpan< float4x4 > transforms_for_write()
Definition instances.cc:235
bool add(const StringRef attribute_id, const AttrDomain domain, const AttrType data_type, const AttributeInit &initializer)
GSpanAttributeWriter lookup_for_write_span(StringRef attribute_id)
const bke::CurvesGeometry & strokes() const
void foreach_index(Fn &&fn) const
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:2416
void copy_attributes(const AttributeAccessor src_attributes, AttrDomain src_domain, AttrDomain dst_domain, const AttributeFilter &attribute_filter, MutableAttributeAccessor dst_attributes)
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)
void foreach_real_geometry(bke::GeometrySet &geometry, FunctionRef< void(bke::GeometrySet &geometry_set)> fn)
MatT from_loc_rot_scale(const typename MatT::loc_type &location, const RotationT &rotation, const VecBase< typename MatT::base_type, ScaleDim > &scale)
static void node_declare(NodeDeclarationBuilder &b)
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 bke::GeometrySet::GatheredAttributes &attributes_to_propagate)
MatBase< float, 4, 4 > float4x4
VecBase< float, 3 > float3
void geo_node_type_base(blender::bke::bNodeType *ntype, std::string idname, const std::optional< int16_t > legacy_type)
static void init(bNodeTree *, bNode *node)
const c_style_mat & ptr() const
const ImplicitSharingInfo * sharing_info
Vector< AttributeDomainAndType, 16 > kinds
void keep_only(Span< GeometryComponent::Type > component_types)
GeometryComponent & get_component_for_write(GeometryComponent::Type component_type)
void replace_instances(Instances *instances, GeometryOwnershipType ownership=GeometryOwnershipType::Owned)
Instances * get_instances_for_write()
const GreasePencil * get_grease_pencil() const
bool has(const GeometryComponent::Type component_type) const
const GeometryComponent * get_component(GeometryComponent::Type component_type) const
const Instances * get_instances() const
void gather_attributes_for_propagation(Span< GeometryComponent::Type > component_types, GeometryComponent::Type dst_component_type, bool include_instances, const AttributeFilter &attribute_filter, GatheredAttributes &r_attributes) const
Defines a node type.
Definition BKE_node.hh:238
std::string ui_description
Definition BKE_node.hh:244
NodeGeometryExecFunction geometry_node_execute
Definition BKE_node.hh:354
const char * enum_name_legacy
Definition BKE_node.hh:247
NodeDeclareFunction declare
Definition BKE_node.hh:362
static GeometrySet from_instances(Instances *instances, GeometryOwnershipType ownership=GeometryOwnershipType::Owned)
i
Definition text_draw.cc:230