Blender V4.3
usd_reader_pointinstancer.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
7#include "BKE_attribute.hh"
8#include "BKE_modifier.hh"
9#include "BKE_node.hh"
10#include "BKE_node_runtime.hh"
12#include "BKE_object.hh"
13#include "BKE_pointcloud.hh"
15#include "BLI_string.h"
16
18#include "DNA_node_types.h"
19
20#include <pxr/usd/usdGeom/pointInstancer.h>
21
22namespace blender::io::usd {
23
27static bNode *add_input_named_attrib_node(bNodeTree *ntree, const char *name, int8_t prop_type)
28{
29 bNode *node = bke::node_add_static_node(nullptr, ntree, GEO_NODE_INPUT_NAMED_ATTRIBUTE);
30 auto *storage = reinterpret_cast<NodeGeometryInputNamedAttribute *>(node->storage);
31 storage->data_type = prop_type;
32
33 bNodeSocket *socket = bke::node_find_socket(node, SOCK_IN, "Name");
34 bNodeSocketValueString *str_value = static_cast<bNodeSocketValueString *>(socket->default_value);
35 BLI_strncpy(str_value->value, name, MAX_NAME);
36 return node;
37}
38
40 const USDImportParams &import_params,
41 const ImportSettings &settings)
42 : USDXformReader(prim, import_params, settings)
43{
44}
45
47{
48 return prim_.IsValid() && prim_.IsA<pxr::UsdGeomPointInstancer>();
49}
50
51void USDPointInstancerReader::create_object(Main *bmain, const double /*motionSampleTime*/)
52{
53 void *point_cloud = BKE_pointcloud_add(bmain, name_.c_str());
55 this->object_->data = point_cloud;
56}
57
58void USDPointInstancerReader::read_object_data(Main *bmain, const double motionSampleTime)
59{
60 PointCloud *base_point_cloud = static_cast<PointCloud *>(object_->data);
61
62 pxr::UsdGeomPointInstancer point_instancer_prim(prim_);
63
64 if (!point_instancer_prim) {
65 return;
66 }
67
68 pxr::VtArray<pxr::GfVec3f> positions;
69 pxr::VtArray<pxr::GfVec3f> scales;
70 pxr::VtArray<pxr::GfQuath> orientations;
71 pxr::VtArray<int> proto_indices;
72 std::vector<double> time_samples;
73
74 point_instancer_prim.GetPositionsAttr().GetTimeSamples(&time_samples);
75
76 double sample_time = motionSampleTime;
77
78 if (!time_samples.empty()) {
79 sample_time = time_samples[0];
80 }
81
82 point_instancer_prim.GetPositionsAttr().Get(&positions, sample_time);
83 point_instancer_prim.GetScalesAttr().Get(&scales, sample_time);
84 point_instancer_prim.GetOrientationsAttr().Get(&orientations, sample_time);
85 point_instancer_prim.GetProtoIndicesAttr().Get(&proto_indices, sample_time);
86
87 std::vector<bool> mask = point_instancer_prim.ComputeMaskAtTime(sample_time);
88
89 PointCloud *point_cloud = BKE_pointcloud_new_nomain(positions.size());
90
91 MutableSpan<float3> point_positions = point_cloud->positions_for_write();
92 point_positions.copy_from(Span(positions.data(), positions.size()).cast<float3>());
93
94 bke::MutableAttributeAccessor attributes = point_cloud->attributes_for_write();
95
96 bke::SpanAttributeWriter<float3> scales_attribute =
97 attributes.lookup_or_add_for_write_only_span<float3>("scale", bke::AttrDomain::Point);
98
99 /* Here and below, handle the case where instancing attributes are empty or
100 * not of the expected size. */
101 if (scales.size() < positions.size()) {
102 scales_attribute.span.fill(float3(1.0f));
103 }
104
105 for (const int i : IndexRange(std::min(scales.size(), positions.size()))) {
106 scales_attribute.span[i] = float3(scales[i][0], scales[i][1], scales[i][2]);
107 }
108
109 scales_attribute.finish();
110
111 bke::SpanAttributeWriter<math::Quaternion> orientations_attribute =
112 attributes.lookup_or_add_for_write_only_span<math::Quaternion>("orientation",
114
115 if (orientations.size() < positions.size()) {
116 orientations_attribute.span.fill(math::Quaternion::identity());
117 }
118
119 for (const int i : IndexRange(std::min(orientations.size(), positions.size()))) {
120 orientations_attribute.span[i] = math::Quaternion(orientations[i].GetReal(),
121 orientations[i].GetImaginary()[0],
122 orientations[i].GetImaginary()[1],
123 orientations[i].GetImaginary()[2]);
124 }
125
126 orientations_attribute.finish();
127
128 bke::SpanAttributeWriter<int> proto_indices_attribute =
129 attributes.lookup_or_add_for_write_only_span<int>("proto_index", bke::AttrDomain::Point);
130
131 if (proto_indices.size() < positions.size()) {
132 proto_indices_attribute.span.fill(0);
133 }
134
135 for (const int i : IndexRange(std::min(proto_indices.size(), positions.size()))) {
136 proto_indices_attribute.span[i] = proto_indices[i];
137 }
138
139 proto_indices_attribute.finish();
140
141 bke::SpanAttributeWriter<bool> mask_attribute =
142 attributes.lookup_or_add_for_write_only_span<bool>("mask", bke::AttrDomain::Point);
143
144 if (mask.size() < positions.size()) {
145 mask_attribute.span.fill(true);
146 }
147
148 for (const int i : IndexRange(std::min(mask.size(), positions.size()))) {
149 mask_attribute.span[i] = mask[i];
150 }
151
152 mask_attribute.finish();
153
154 BKE_pointcloud_nomain_to_pointcloud(point_cloud, base_point_cloud);
155
159
160 NodesModifierData &nmd = *reinterpret_cast<NodesModifierData *>(md);
161 nmd.node_group = bke::node_tree_add_tree(bmain, "Instances", "GeometryNodeTree");
162
163 bNodeTree *ntree = nmd.node_group;
164
165 ntree->tree_interface.add_socket(
166 "Geometry", "", "NodeSocketGeometry", NODE_INTERFACE_SOCKET_OUTPUT, nullptr);
167 ntree->tree_interface.add_socket(
168 "Geometry", "", "NodeSocketGeometry", NODE_INTERFACE_SOCKET_INPUT, nullptr);
169 bNode *group_input = bke::node_add_static_node(nullptr, ntree, NODE_GROUP_INPUT);
170 group_input->locx = -400.0f;
171 bNode *group_output = bke::node_add_static_node(nullptr, ntree, NODE_GROUP_OUTPUT);
172 group_output->locx = 500.0f;
173 group_output->flag |= NODE_DO_OUTPUT;
174
175 bNode *instance_on_points_node = bke::node_add_static_node(
176 nullptr, ntree, GEO_NODE_INSTANCE_ON_POINTS);
177 instance_on_points_node->locx = 300.0f;
178 bNodeSocket *socket = bke::node_find_socket(instance_on_points_node, SOCK_IN, "Pick Instance");
179 socket->default_value_typed<bNodeSocketValueBoolean>()->value = true;
180
181 bNode *mask_attrib_node = add_input_named_attrib_node(ntree, "mask", CD_PROP_BOOL);
182 mask_attrib_node->locx = 100.0f;
183 mask_attrib_node->locy = -100.0f;
184
185 bNode *collection_info_node = bke::node_add_static_node(
186 nullptr, ntree, GEO_NODE_COLLECTION_INFO);
187 collection_info_node->locx = 100.0f;
188 collection_info_node->locy = -300.0f;
189 socket = bke::node_find_socket(collection_info_node, SOCK_IN, "Separate Children");
190 socket->default_value_typed<bNodeSocketValueBoolean>()->value = true;
191
192 bNode *indices_attrib_node = add_input_named_attrib_node(ntree, "proto_index", CD_PROP_INT32);
193 indices_attrib_node->locx = 100.0f;
194 indices_attrib_node->locy = -500.0f;
195
196 bNode *rotation_attrib_node = add_input_named_attrib_node(
197 ntree, "orientation", CD_PROP_QUATERNION);
198 rotation_attrib_node->locx = 100.0f;
199 rotation_attrib_node->locy = -700.0f;
200
201 bNode *scale_attrib_node = add_input_named_attrib_node(ntree, "scale", CD_PROP_FLOAT3);
202 scale_attrib_node->locx = 100.0f;
203 scale_attrib_node->locy = -900.0f;
204
205 bke::node_add_link(ntree,
206 group_input,
207 static_cast<bNodeSocket *>(group_input->outputs.first),
208 instance_on_points_node,
209 bke::node_find_socket(instance_on_points_node, SOCK_IN, "Points"));
210
211 bke::node_add_link(ntree,
212 mask_attrib_node,
213 bke::node_find_socket(mask_attrib_node, SOCK_OUT, "Attribute"),
214 instance_on_points_node,
215 bke::node_find_socket(instance_on_points_node, SOCK_IN, "Selection"));
216
217 bke::node_add_link(ntree,
218 indices_attrib_node,
219 bke::node_find_socket(indices_attrib_node, SOCK_OUT, "Attribute"),
220 instance_on_points_node,
221 bke::node_find_socket(instance_on_points_node, SOCK_IN, "Instance Index"));
222
223 bke::node_add_link(ntree,
224 scale_attrib_node,
225 bke::node_find_socket(scale_attrib_node, SOCK_OUT, "Attribute"),
226 instance_on_points_node,
227 bke::node_find_socket(instance_on_points_node, SOCK_IN, "Scale"));
228
229 bke::node_add_link(ntree,
230 rotation_attrib_node,
231 bke::node_find_socket(rotation_attrib_node, SOCK_OUT, "Attribute"),
232 instance_on_points_node,
233 bke::node_find_socket(instance_on_points_node, SOCK_IN, "Rotation"));
234
235 bke::node_add_link(ntree,
236 collection_info_node,
237 bke::node_find_socket(collection_info_node, SOCK_OUT, "Instances"),
238 instance_on_points_node,
239 bke::node_find_socket(instance_on_points_node, SOCK_IN, "Instance"));
240
241 bke::node_add_link(ntree,
242 instance_on_points_node,
243 bke::node_find_socket(instance_on_points_node, SOCK_OUT, "Instances"),
244 group_output,
245 static_cast<bNodeSocket *>(group_output->inputs.first));
246
247 BKE_ntree_update_main_tree(bmain, ntree, nullptr);
248
250
251 USDXformReader::read_object_data(bmain, motionSampleTime);
252}
253
254pxr::SdfPathVector USDPointInstancerReader::proto_paths() const
255{
256 pxr::SdfPathVector paths;
257
258 pxr::UsdGeomPointInstancer point_instancer_prim(prim_);
259
260 if (!point_instancer_prim) {
261 return paths;
262 }
263
264 point_instancer_prim.GetPrototypesRel().GetTargets(&paths);
265
266 return paths;
267}
268
270{
271 /* create_object() should have been called already. */
273
275 if (!md) {
277 return;
278 }
279
280 NodesModifierData *nmd = reinterpret_cast<NodesModifierData *>(md);
281
282 bNodeTree *ntree = nmd->node_group;
283 if (!ntree) {
285 return;
286 }
287
288 bNode *collection_node = bke::node_find_node_by_name(ntree, "Collection Info");
289 if (!collection_node) {
291 return;
292 }
293
294 bNodeSocket *sock = bke::node_find_socket(collection_node, SOCK_IN, "Collection");
295 if (!sock) {
297 return;
298 }
299
300 bNodeSocketValueCollection *socket_data = static_cast<bNodeSocketValueCollection *>(
301 sock->default_value);
302
303 if (socket_data->value != &coll) {
304 socket_data->value = &coll;
305 BKE_ntree_update_main_tree(bmain, ntree, nullptr);
306 }
307}
308
309} // namespace blender::io::usd
ModifierData * BKE_modifiers_findby_type(const Object *ob, ModifierType type)
void BKE_modifiers_persistent_uid_init(const Object &object, ModifierData &md)
ModifierData * BKE_modifier_new(int type)
#define NODE_GROUP_OUTPUT
Definition BKE_node.hh:806
#define NODE_GROUP_INPUT
Definition BKE_node.hh:805
void BKE_ntree_update_main_tree(Main *bmain, bNodeTree *ntree, NodeTreeUpdateExtraParams *params)
General operations, lookup, etc. for blender objects.
void BKE_object_modifier_set_active(Object *ob, ModifierData *md)
Object * BKE_object_add_only_object(Main *bmain, int type, const char *name) ATTR_RETURNS_NONNULL
General operations for point clouds.
void BKE_pointcloud_nomain_to_pointcloud(PointCloud *pointcloud_src, PointCloud *pointcloud_dst)
void * BKE_pointcloud_add(Main *bmain, const char *name)
PointCloud * BKE_pointcloud_new_nomain(int totpoint)
#define BLI_assert_unreachable()
Definition BLI_assert.h:97
#define BLI_assert(a)
Definition BLI_assert.h:50
void BLI_addtail(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:110
char * BLI_strncpy(char *__restrict dst, const char *__restrict src, size_t dst_maxncpy) ATTR_NONNULL(1
Object groups, one object can be in many groups at once.
@ CD_PROP_FLOAT3
@ CD_PROP_QUATERNION
@ CD_PROP_INT32
#define MAX_NAME
Definition DNA_defs.h:50
@ eModifierType_Nodes
@ NODE_DO_OUTPUT
@ SOCK_OUT
@ SOCK_IN
@ OB_POINTCLOUD
constexpr void copy_from(Span< T > values) const
Definition BLI_span.hh:726
Span< NewT > constexpr cast() const
Definition BLI_span.hh:419
USDPointInstancerReader(const pxr::UsdPrim &prim, const USDImportParams &import_params, const ImportSettings &settings)
void read_object_data(Main *bmain, double motionSampleTime) override
void create_object(Main *bmain, double motionSampleTime) override
void set_collection(Main *bmain, Collection &coll)
void read_object_data(Main *bmain, double motionSampleTime) override
OperationNode * node
bNode * node_add_static_node(const bContext *C, bNodeTree *ntree, int type)
Definition node.cc:2642
bNodeLink * node_add_link(bNodeTree *ntree, bNode *fromnode, bNodeSocket *fromsock, bNode *tonode, bNodeSocket *tosock)
Definition node.cc:2912
bNodeTree * node_tree_add_tree(Main *bmain, const char *name, const char *idname)
Definition node.cc:3226
bNodeSocket * node_find_socket(bNode *node, eNodeSocketInOut in_out, StringRef identifier)
Definition node.cc:1829
bNode * node_find_node_by_name(bNodeTree *ntree, const char *name)
Definition node.cc:2437
static bNode * add_input_named_attrib_node(bNodeTree *ntree, const char *name, int8_t prop_type)
QuaternionBase< float > Quaternion
VecBase< float, 3 > float3
signed char int8_t
Definition stdint.h:75
void * first
struct bNodeTree * node_group
ListBase modifiers
struct Collection * value
void * default_value
bNodeTreeInterface tree_interface
float locy
ListBase inputs
float locx
ListBase outputs