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