Blender  V3.3
node_geo_instance_on_points.cc
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 
3 #include "DNA_collection_types.h"
4 
5 #include "BLI_hash.h"
6 #include "BLI_task.hh"
7 
8 #include "UI_interface.h"
9 #include "UI_resources.h"
10 
11 #include "BKE_attribute_math.hh"
12 
13 #include "node_geometry_util.hh"
14 
16 
18 {
19  b.add_input<decl::Geometry>(N_("Points")).description(N_("Points to instance on"));
20  b.add_input<decl::Bool>(N_("Selection")).default_value(true).supports_field().hide_value();
21  b.add_input<decl::Geometry>(N_("Instance"))
22  .description(N_("Geometry that is instanced on the points"));
23  b.add_input<decl::Bool>(N_("Pick Instance"))
24  .supports_field()
25  .description(N_("Choose instances from the \"Instance\" input at each point instead of "
26  "instancing the entire geometry"));
27  b.add_input<decl::Int>(N_("Instance Index"))
28  .implicit_field()
29  .description(N_(
30  "Index of the instance that used for each point. This is only used when Pick Instances "
31  "is on. By default the point index is used"));
32  b.add_input<decl::Vector>(N_("Rotation"))
33  .subtype(PROP_EULER)
34  .supports_field()
35  .description(N_("Rotation of the instances"));
36  b.add_input<decl::Vector>(N_("Scale"))
37  .default_value({1.0f, 1.0f, 1.0f})
38  .subtype(PROP_XYZ)
39  .supports_field()
40  .description(N_("Scale of the instances"));
41 
42  b.add_output<decl::Geometry>(N_("Instances"));
43 }
44 
46  InstancesComponent &dst_component,
47  const GeometryComponent &src_component,
48  const GeometrySet &instance,
50  const Map<AttributeIDRef, AttributeKind> &attributes_to_propagate)
51 {
52  const eAttrDomain domain = ATTR_DOMAIN_POINT;
53  const int domain_num = src_component.attribute_domain_size(domain);
54 
55  VArray<bool> pick_instance;
58  VArray<float3> scales;
59 
60  GeometryComponentFieldContext field_context{src_component, domain};
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<float3>>("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.instance_reference_handles().slice(start_len,
84  select_len);
85  MutableSpan<float4x4> dst_transforms = dst_component.instance_transforms().slice(start_len,
86  select_len);
87 
88  VArray<float3> positions = src_component.attributes()->lookup_or_default<float3>(
89  "position", domain, {0, 0, 0});
90 
91  const InstancesComponent *src_instances = instance.get_component_for_read<InstancesComponent>();
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  Span<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 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(InstanceReference());
110 
111  threading::parallel_for(selection.index_range(), 1024, [&](IndexRange selection_range) {
112  for (const int range_i : selection_range) {
113  const int64_t i = selection[range_i];
114 
115  /* Compute base transform for every instances. */
116  float4x4 &dst_transform = dst_transforms[range_i];
117  dst_transform = float4x4::from_loc_eul_scale(positions[i], rotations[i], scales[i]);
118 
119  /* Reference that will be used by this new instance. */
120  int dst_handle = empty_reference_handle;
121 
122  const bool use_individual_instance = pick_instance[i];
123  if (use_individual_instance) {
124  if (src_instances != nullptr) {
125  const int src_instances_num = src_instances->instances_num();
126  const int original_index = indices[i];
127  /* Use #mod_i instead of `%` to get the desirable wrap around behavior where -1
128  * refers to the last element. */
129  const int index = mod_i(original_index, std::max(src_instances_num, 1));
130  if (index < src_instances_num) {
131  /* Get the reference to the source instance. */
132  const int src_handle = src_instances->instance_reference_handles()[index];
133  dst_handle = handle_mapping[src_handle];
134 
135  /* Take transforms of the source instance into account. */
136  mul_m4_m4_post(dst_transform.values,
137  src_instances->instance_transforms()[index].values);
138  }
139  }
140  }
141  else {
142  /* Use entire source geometry as instance. */
143  dst_handle = full_instance_handle;
144  }
145  /* Set properties of new instance. */
146  dst_handles[range_i] = dst_handle;
147  }
148  });
149 
150  if (pick_instance.is_single()) {
151  if (pick_instance.get_internal_single()) {
152  if (instance.has_realized_data()) {
153  params.error_message_add(
155  TIP_("Realized geometry is not used when pick instances is true"));
156  }
157  }
158  }
159 
160  bke::CustomDataAttributes &instance_attributes = dst_component.instance_attributes();
161  for (const auto item : attributes_to_propagate.items()) {
162  const AttributeIDRef &attribute_id = item.key;
163  const AttributeKind attribute_kind = item.value;
164 
165  const GVArray src_attribute = src_component.attributes()->lookup_or_default(
166  attribute_id, ATTR_DOMAIN_POINT, attribute_kind.data_type);
167  BLI_assert(src_attribute);
168  std::optional<GMutableSpan> dst_attribute_opt = instance_attributes.get_for_write(
169  attribute_id);
170  if (!dst_attribute_opt) {
171  if (!instance_attributes.create(attribute_id, attribute_kind.data_type)) {
172  continue;
173  }
174  dst_attribute_opt = instance_attributes.get_for_write(attribute_id);
175  }
176  BLI_assert(dst_attribute_opt);
177  const GMutableSpan dst_attribute = dst_attribute_opt->slice(start_len, select_len);
178  threading::parallel_for(selection.index_range(), 1024, [&](IndexRange selection_range) {
179  attribute_math::convert_to_static_type(attribute_kind.data_type, [&](auto dummy) {
180  using T = decltype(dummy);
181  VArray<T> src = src_attribute.typed<T>();
182  MutableSpan<T> dst = dst_attribute.typed<T>();
183  for (const int range_i : selection_range) {
184  const int i = selection[range_i];
185  dst[range_i] = src[i];
186  }
187  });
188  });
189  }
190 }
191 
193 {
194  GeometrySet geometry_set = params.extract_input<GeometrySet>("Points");
195  GeometrySet instance = params.get_input<GeometrySet>("Instance");
196  instance.ensure_owns_direct_data();
197 
198  geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
200 
203 
204  Map<AttributeIDRef, AttributeKind> attributes_to_propagate;
206  types, GEO_COMPONENT_TYPE_INSTANCES, false, attributes_to_propagate);
207  attributes_to_propagate.remove("position");
208 
209  for (const GeometryComponentType type : types) {
210  if (geometry_set.has(type)) {
212  *geometry_set.get_component_for_read(type),
213  instance,
214  params,
215  attributes_to_propagate);
216  }
217  }
218 
219  geometry_set.remove_geometry_during_modify();
220  });
221 
222  /* Unused references may have been added above. Remove those now so that other nodes don't
223  * process them needlessly.
224  * This should eventually be moved into the loop above, but currently this is quite tricky
225  * because it might remove references that the loop still wants to iterate over. */
227  instances.remove_unused_references();
228 
229  params.set_output("Instances", std::move(geometry_set));
230 }
231 
232 } // namespace blender::nodes::node_geo_instance_on_points_cc
233 
235 {
237 
238  static bNodeType ntype;
239 
241  &ntype, GEO_NODE_INSTANCE_ON_POINTS, "Instance on Points", NODE_CLASS_GEOMETRY);
244  nodeRegisterType(&ntype);
245 }
eAttrDomain
Definition: BKE_attribute.h:25
@ ATTR_DOMAIN_POINT
Definition: BKE_attribute.h:27
GeometryComponentType
@ GEO_COMPONENT_TYPE_MESH
@ GEO_COMPONENT_TYPE_POINT_CLOUD
@ GEO_COMPONENT_TYPE_INSTANCES
@ GEO_COMPONENT_TYPE_CURVE
#define NODE_CLASS_GEOMETRY
Definition: BKE_node.h:359
#define GEO_NODE_INSTANCE_ON_POINTS
Definition: BKE_node.h:1434
void nodeRegisterType(struct bNodeType *ntype)
Definition: node.cc:1357
#define BLI_assert(a)
Definition: BLI_assert.h:46
#define TIP_(msgid)
Object groups, one object can be in many groups at once.
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum type
in reality light always falls off quadratically Particle Info
in reality light always falls off quadratically Particle Retrieve the data of the particle that spawned the object instance
@ PROP_XYZ
Definition: RNA_types.h:162
@ PROP_EULER
Definition: RNA_types.h:159
int attribute_domain_size(eAttrDomain domain) const
Definition: geometry_set.cc:63
virtual std::optional< blender::bke::AttributeAccessor > attributes() const
Definition: geometry_set.cc:75
blender::Span< int > instance_reference_handles() const
int add_reference(const InstanceReference &reference)
blender::MutableSpan< blender::float4x4 > instance_transforms()
bool is_empty() const
IndexRange index_range() const
constexpr int64_t size() const
bool remove(const Key &key)
Definition: BLI_map.hh:323
constexpr Span slice(int64_t start, int64_t size) const
Definition: BLI_span.hh:142
constexpr int64_t size() const
Definition: BLI_span.hh:240
constexpr IndexRange index_range() const
Definition: BLI_span.hh:401
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
ccl_gpu_kernel_postfix int ccl_global int * indices
static char ** types
Definition: makesdna.c:67
static int domain_num(const CurvesGeometry &curves, const eAttrDomain domain)
static void add_instances_from_component(InstancesComponent &dst_component, const GeometryComponent &src_component, const GeometrySet &instance, const GeoNodeExecParams &params, const Map< AttributeIDRef, AttributeKind > &attributes_to_propagate)
static void node_declare(NodeDeclarationBuilder &b)
void parallel_for(IndexRange range, int64_t grain_size, const Function &function)
Definition: BLI_task.hh:51
static const pxr::TfToken b("b", pxr::TfToken::Immortal)
MutableSpan< float3 > positions
MutableSpan< float3 > rotations
void register_node_type_geo_instance_on_points()
void geo_node_type_base(bNodeType *ntype, int type, const char *name, short nclass)
GeometryComponent & get_component_for_write(GeometryComponentType component_type)
bool has(const GeometryComponentType component_type) const
const GeometryComponent * get_component_for_read(GeometryComponentType component_type) const
void gather_attributes_for_propagation(blender::Span< GeometryComponentType > component_types, GeometryComponentType dst_component_type, bool include_instances, blender::Map< blender::bke::AttributeIDRef, blender::bke::AttributeKind > &r_attributes) const
void modify_geometry_sets(ForeachSubGeometryCallback callback)
void remove_geometry_during_modify()
Defines a node type.
Definition: BKE_node.h:226
NodeGeometryExecFunction geometry_node_execute
Definition: BKE_node.h:316
NodeDeclareFunction declare
Definition: BKE_node.h:324
#define N_(msgid)