Blender V5.0
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_geometry_set.hh"
9#include "BKE_modifier.hh"
10#include "BKE_node.hh"
12#include "BKE_node_runtime.hh"
14#include "BKE_object.hh"
15#include "BKE_pointcloud.hh"
16#include "BLI_listbase.h"
18#include "BLI_string.h"
19
21#include "DNA_node_types.h"
22
23#include <pxr/usd/usdGeom/pointInstancer.h>
24
25namespace blender::io::usd {
26
30static bNode *add_input_named_attrib_node(bNodeTree *ntree, const char *name, int8_t prop_type)
31{
33 auto *storage = reinterpret_cast<NodeGeometryInputNamedAttribute *>(node->storage);
34 storage->data_type = prop_type;
35
36 bNodeSocket *socket = bke::node_find_socket(*node, SOCK_IN, "Name");
37 bNodeSocketValueString *str_value = static_cast<bNodeSocketValueString *>(socket->default_value);
38 BLI_strncpy(str_value->value, name, MAX_NAME);
39 return node;
40}
41
43{
44 PointCloud *pointcloud = BKE_pointcloud_add(bmain, name_.c_str());
46 this->object_->data = pointcloud;
47}
48
51 const char ** /*r_err_str*/)
52{
53 pxr::VtArray<pxr::GfVec3f> usd_positions;
54 pxr::VtArray<pxr::GfVec3f> usd_scales;
55 pxr::VtArray<pxr::GfQuath> usd_orientations;
56 pxr::VtArray<int> usd_proto_indices;
57 std::vector<bool> usd_mask = point_instancer_prim_.ComputeMaskAtTime(params.motion_sample_time);
58
59 point_instancer_prim_.GetPositionsAttr().Get(&usd_positions, params.motion_sample_time);
60 point_instancer_prim_.GetScalesAttr().Get(&usd_scales, params.motion_sample_time);
61 point_instancer_prim_.GetOrientationsAttr().Get(&usd_orientations, params.motion_sample_time);
62 point_instancer_prim_.GetProtoIndicesAttr().Get(&usd_proto_indices, params.motion_sample_time);
63
64 PointCloud *pointcloud = geometry_set.get_pointcloud_for_write();
65 if (pointcloud->totpoint != usd_positions.size()) {
66 /* Size changed so we must reallocate. */
67 pointcloud = BKE_pointcloud_new_nomain(usd_positions.size());
68 }
69
70 MutableSpan<float3> point_positions = pointcloud->positions_for_write();
71 point_positions.copy_from(Span(usd_positions.cdata(), usd_positions.size()).cast<float3>());
72
73 bke::MutableAttributeAccessor attributes = pointcloud->attributes_for_write();
74
75 bke::SpanAttributeWriter<float3> scales_attribute =
77
78 /* Here and below, handle the case where instancing attributes are empty or
79 * not of the expected size. */
80 if (usd_scales.size() < usd_positions.size()) {
81 scales_attribute.span.fill(float3(1.0f));
82 }
83
84 Span<pxr::GfVec3f> scales = Span(usd_scales.cdata(), usd_scales.size());
85 for (const int i : IndexRange(std::min(usd_scales.size(), usd_positions.size()))) {
86 scales_attribute.span[i] = float3(scales[i][0], scales[i][1], scales[i][2]);
87 }
88
89 scales_attribute.finish();
90
91 bke::SpanAttributeWriter<math::Quaternion> orientations_attribute =
94
95 if (usd_orientations.size() < usd_positions.size()) {
96 orientations_attribute.span.fill(math::Quaternion::identity());
97 }
98
99 Span<pxr::GfQuath> orientations = Span(usd_orientations.cdata(), usd_orientations.size());
100 for (const int i : IndexRange(std::min(usd_orientations.size(), usd_positions.size()))) {
101 orientations_attribute.span[i] = math::Quaternion(orientations[i].GetReal(),
102 orientations[i].GetImaginary()[0],
103 orientations[i].GetImaginary()[1],
104 orientations[i].GetImaginary()[2]);
105 }
106
107 orientations_attribute.finish();
108
109 bke::SpanAttributeWriter<int> proto_indices_attribute =
110 attributes.lookup_or_add_for_write_only_span<int>("proto_index", bke::AttrDomain::Point);
111
112 if (usd_proto_indices.size() < usd_positions.size()) {
113 proto_indices_attribute.span.fill(0);
114 }
115
116 Span<int> proto_indices = Span(usd_proto_indices.cdata(), usd_proto_indices.size());
117 for (const int i : IndexRange(std::min(usd_proto_indices.size(), usd_positions.size()))) {
118 proto_indices_attribute.span[i] = proto_indices[i];
119 }
120
121 proto_indices_attribute.finish();
122
123 bke::SpanAttributeWriter<bool> mask_attribute =
125
126 if (usd_mask.size() < usd_positions.size()) {
127 mask_attribute.span.fill(true);
128 }
129
130 for (const int i : IndexRange(std::min(usd_mask.size(), usd_positions.size()))) {
131 mask_attribute.span[i] = usd_mask[i];
132 }
133
134 mask_attribute.finish();
135
136 geometry_set.replace_pointcloud(pointcloud);
137}
138
139void USDPointInstancerReader::read_object_data(Main *bmain, const pxr::UsdTimeCode time)
140{
141 PointCloud *pointcloud = static_cast<PointCloud *>(object_->data);
142
145
146 const USDMeshReadParams params = create_mesh_read_params(time.GetValue(),
147 import_params_.mesh_read_flag);
148
149 read_geometry(geometry_set, params, nullptr);
150
151 PointCloud *read_pointcloud =
152 geometry_set.get_component_for_write<bke::PointCloudComponent>().release();
153
154 if (read_pointcloud != pointcloud) {
155 BKE_pointcloud_nomain_to_pointcloud(read_pointcloud, pointcloud);
156 }
157
158 if (is_animated()) {
159 /* If the point cloud has time-varying data, we add the cache modifier. */
161 }
162
164 BLI_addtail(&object_->modifiers, md);
166
167 NodesModifierData &nmd = *reinterpret_cast<NodesModifierData *>(md);
168 nmd.node_group = bke::node_tree_add_tree(bmain, "Instances", "GeometryNodeTree");
169
170 bNodeTree *ntree = nmd.node_group;
171
172 ntree->tree_interface.add_socket(
173 "Geometry", "", "NodeSocketGeometry", NODE_INTERFACE_SOCKET_OUTPUT, nullptr);
174 ntree->tree_interface.add_socket(
175 "Geometry", "", "NodeSocketGeometry", NODE_INTERFACE_SOCKET_INPUT, nullptr);
176 bNode *group_input = bke::node_add_static_node(nullptr, *ntree, NODE_GROUP_INPUT);
177 group_input->location[0] = -400.0f;
178 bNode *group_output = bke::node_add_static_node(nullptr, *ntree, NODE_GROUP_OUTPUT);
179 group_output->location[0] = 500.0f;
180 group_output->flag |= NODE_DO_OUTPUT;
181
182 bNode *instance_on_points_node = bke::node_add_static_node(
183 nullptr, *ntree, GEO_NODE_INSTANCE_ON_POINTS);
184 instance_on_points_node->location[0] = 300.0f;
185 bNodeSocket *socket = bke::node_find_socket(*instance_on_points_node, SOCK_IN, "Pick Instance");
186 socket->default_value_typed<bNodeSocketValueBoolean>()->value = true;
187
188 bNode *mask_attrib_node = add_input_named_attrib_node(ntree, "mask", CD_PROP_BOOL);
189 mask_attrib_node->location[0] = 100.0f;
190 mask_attrib_node->location[1] = -100.0f;
191
192 bNode *collection_info_node = bke::node_add_static_node(
193 nullptr, *ntree, GEO_NODE_COLLECTION_INFO);
194 collection_info_node->location[0] = 100.0f;
195 collection_info_node->location[1] = -300.0f;
196 socket = bke::node_find_socket(*collection_info_node, SOCK_IN, "Separate Children");
197 socket->default_value_typed<bNodeSocketValueBoolean>()->value = true;
198
199 bNode *indices_attrib_node = add_input_named_attrib_node(ntree, "proto_index", CD_PROP_INT32);
200 indices_attrib_node->location[0] = 100.0f;
201 indices_attrib_node->location[1] = -500.0f;
202
203 bNode *rotation_attrib_node = add_input_named_attrib_node(
204 ntree, "orientation", CD_PROP_QUATERNION);
205 rotation_attrib_node->location[0] = 100.0f;
206 rotation_attrib_node->location[1] = -700.0f;
207
208 bNode *scale_attrib_node = add_input_named_attrib_node(ntree, "scale", CD_PROP_FLOAT3);
209 scale_attrib_node->location[0] = 100.0f;
210 scale_attrib_node->location[1] = -900.0f;
211
212 bke::node_add_link(*ntree,
213 *group_input,
214 *static_cast<bNodeSocket *>(group_input->outputs.first),
215 *instance_on_points_node,
216 *bke::node_find_socket(*instance_on_points_node, SOCK_IN, "Points"));
217
218 bke::node_add_link(*ntree,
219 *mask_attrib_node,
220 *bke::node_find_socket(*mask_attrib_node, SOCK_OUT, "Attribute"),
221 *instance_on_points_node,
222 *bke::node_find_socket(*instance_on_points_node, SOCK_IN, "Selection"));
223
224 bke::node_add_link(*ntree,
225 *indices_attrib_node,
226 *bke::node_find_socket(*indices_attrib_node, SOCK_OUT, "Attribute"),
227 *instance_on_points_node,
228 *bke::node_find_socket(*instance_on_points_node, SOCK_IN, "Instance Index"));
229
230 bke::node_add_link(*ntree,
231 *scale_attrib_node,
232 *bke::node_find_socket(*scale_attrib_node, SOCK_OUT, "Attribute"),
233 *instance_on_points_node,
234 *bke::node_find_socket(*instance_on_points_node, SOCK_IN, "Scale"));
235
236 bke::node_add_link(*ntree,
237 *rotation_attrib_node,
238 *bke::node_find_socket(*rotation_attrib_node, SOCK_OUT, "Attribute"),
239 *instance_on_points_node,
240 *bke::node_find_socket(*instance_on_points_node, SOCK_IN, "Rotation"));
241
242 bke::node_add_link(*ntree,
243 *collection_info_node,
244 *bke::node_find_socket(*collection_info_node, SOCK_OUT, "Instances"),
245 *instance_on_points_node,
246 *bke::node_find_socket(*instance_on_points_node, SOCK_IN, "Instance"));
247
248 bke::node_add_link(*ntree,
249 *instance_on_points_node,
250 *bke::node_find_socket(*instance_on_points_node, SOCK_OUT, "Instances"),
251 *group_output,
252 *static_cast<bNodeSocket *>(group_output->inputs.first));
253
255
257
259}
260
261pxr::SdfPathVector USDPointInstancerReader::proto_paths() const
262{
263 pxr::SdfPathVector paths;
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;
307 }
308}
309
311{
312 bool is_animated = false;
313 is_animated |= point_instancer_prim_.GetPositionsAttr().ValueMightBeTimeVarying();
314 is_animated |= point_instancer_prim_.GetScalesAttr().ValueMightBeTimeVarying();
315 is_animated |= point_instancer_prim_.GetOrientationsAttr().ValueMightBeTimeVarying();
316 is_animated |= point_instancer_prim_.GetProtoIndicesAttr().ValueMightBeTimeVarying();
317
318 return is_animated;
319}
320
321} // 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:815
#define NODE_GROUP_INPUT
Definition BKE_node.hh:814
#define GEO_NODE_INPUT_NAMED_ATTRIBUTE
#define GEO_NODE_INSTANCE_ON_POINTS
#define GEO_NODE_COLLECTION_INFO
void BKE_ntree_update_after_single_tree_change(Main &bmain, bNodeTree &modified_tree, const NodeTreeUpdateExtraParams &params={})
void BKE_ntree_update_tag_socket_property(bNodeTree *ntree, bNodeSocket *socket)
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.
PointCloud * BKE_pointcloud_add(Main *bmain, const char *name)
void BKE_pointcloud_nomain_to_pointcloud(PointCloud *pointcloud_src, PointCloud *pointcloud_dst)
PointCloud * BKE_pointcloud_new_nomain(int totpoint)
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define BLI_assert(a)
Definition BLI_assert.h:46
void BLI_addtail(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:111
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:739
Span< NewT > constexpr cast() const
Definition BLI_span.hh:418
GSpanAttributeWriter lookup_or_add_for_write_only_span(StringRef attribute_id, AttrDomain domain, AttrType data_type)
void read_geometry(bke::GeometrySet &geometry_set, USDMeshReadParams params, const char **r_err_str) override
void set_collection(Main *bmain, Collection &coll)
void read_object_data(Main *bmain, pxr::UsdTimeCode time) override
const USDImportParams & import_params_
void read_object_data(Main *bmain, pxr::UsdTimeCode time) override
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
bNode * node_find_node_by_name(bNodeTree &ntree, StringRefNull name)
Definition node.cc:3275
bNodeSocket * node_find_socket(bNode &node, eNodeSocketInOut in_out, StringRef identifier)
Definition node.cc:2532
bNode * node_add_static_node(const bContext *C, bNodeTree &ntree, int type)
Definition node.cc:3500
bNodeLink & node_add_link(bNodeTree &ntree, bNode &fromnode, bNodeSocket &fromsock, bNode &tonode, bNodeSocket &tosock)
Definition node.cc:3810
bNodeTree * node_tree_add_tree(Main *bmain, StringRef name, StringRef idname)
Definition node.cc:4085
USDMeshReadParams create_mesh_read_params(const double motion_sample_time, const int read_flags)
static bNode * add_input_named_attrib_node(bNodeTree *ntree, const char *name, int8_t prop_type)
QuaternionBase< float > Quaternion
VecBase< float, 3 > float3
const char * name
void * first
struct bNodeTree * node_group
struct Collection * value
void * default_value
bNodeTreeInterface tree_interface
float location[2]
ListBase inputs
void * storage
ListBase outputs
GeometryComponent & get_component_for_write(GeometryComponent::Type component_type)
PointCloud * get_pointcloud_for_write()
void replace_pointcloud(PointCloud *pointcloud, GeometryOwnershipType ownership=GeometryOwnershipType::Owned)
static GeometrySet from_pointcloud(PointCloud *pointcloud, GeometryOwnershipType ownership=GeometryOwnershipType::Owned)
i
Definition text_draw.cc:230