Blender V5.0
usd_writer_pointinstancer.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2025 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
7#include "usd_utils.hh"
8
10#include "BKE_collection.hh"
11#include "BKE_geometry_set.hh"
13#include "BKE_instances.hh"
14#include "BKE_lib_id.hh"
15#include "BKE_report.hh"
16
17#include "BLI_math_euler.hh"
18#include "BLI_math_matrix.hh"
19
21#include "DNA_layer_types.h"
22#include "DNA_object_types.h"
23
24#include <pxr/base/gf/quatf.h>
25#include <pxr/base/gf/vec3d.h>
26#include <pxr/base/gf/vec3f.h>
27#include <pxr/base/vt/array.h>
28#include <pxr/usd/usdGeom/pointInstancer.h>
29#include <pxr/usd/usdGeom/primvarsAPI.h>
30
32
33namespace blender::io::usd {
34
36 const USDExporterContext &ctx,
37 const Set<std::pair<pxr::SdfPath, Object *>> &prototype_paths,
38 std::unique_ptr<USDAbstractWriter> base_writer)
39 : USDAbstractWriter(ctx),
40 base_writer_(std::move(base_writer)),
41 prototype_paths_(prototype_paths)
42{
43}
44
46{
47 /* Write the base data first (e.g., mesh, curves, points) */
48 if (base_writer_) {
49 base_writer_->write(context);
50
51 if (usd_export_context_.add_skel_mapping_fn &&
52 (usd_export_context_.export_params.export_armatures ||
53 usd_export_context_.export_params.export_shapekeys))
54 {
55 usd_export_context_.add_skel_mapping_fn(context.object, base_writer_->usd_path());
56 }
57 }
58
59 const pxr::UsdStageRefPtr stage = usd_export_context_.stage;
60 const Object *object_eval = context.object;
61 bke::GeometrySet instance_geometry_set = bke::object_get_evaluated_geometry_set(*object_eval);
62
63 const bke::GeometryComponent *component = instance_geometry_set.get_component(
65
66 const bke::Instances *instances = static_cast<const bke::InstancesComponent &>(*component).get();
67
68 int instance_num = instances->instances_num();
69 const pxr::SdfPath &usd_path = usd_export_context_.usd_path;
70 const pxr::UsdGeomPointInstancer usd_instancer = pxr::UsdGeomPointInstancer::Define(stage,
71 usd_path);
72 const pxr::UsdTimeCode time = get_export_time_code();
73
74 Span<float4x4> transforms = instances->transforms();
75 BLI_assert(transforms.size() >= instance_num);
76
77 if (transforms.size() != instance_num) {
78 BKE_reportf(this->reports(),
80 "Instances number '%d' does not match transforms size '%d'",
81 instance_num,
82 int(transforms.size()));
83 return;
84 }
85
86 /* evaluated positions */
87 pxr::UsdAttribute position_attr = usd_instancer.CreatePositionsAttr();
88 pxr::VtArray<pxr::GfVec3f> positions(instance_num);
89 for (int i = 0; i < instance_num; i++) {
90 const float3 &pos = transforms[i].location();
91 positions[i] = pxr::GfVec3f(pos.x, pos.y, pos.z);
92 }
93 blender::io::usd::set_attribute(position_attr, positions, time, usd_value_writer_);
94
95 /* orientations */
96 pxr::UsdAttribute orientations_attr = usd_instancer.CreateOrientationsAttr();
97 pxr::VtArray<pxr::GfQuath> orientation(instance_num);
98 for (int i = 0; i < instance_num; i++) {
99 const float3 euler = float3(math::to_euler(math::normalize(transforms[i])));
101 orientation[i] = pxr::GfQuath(quat.w, pxr::GfVec3h(quat.x, quat.y, quat.z));
102 }
103 blender::io::usd::set_attribute(orientations_attr, orientation, time, usd_value_writer_);
104
105 /* scales */
106 pxr::UsdAttribute scales_attr = usd_instancer.CreateScalesAttr();
107 pxr::VtArray<pxr::GfVec3f> scales(instance_num);
108 for (int i = 0; i < instance_num; i++) {
109 const MatBase<float, 4, 4> &mat = transforms[i];
110 blender::float3 scale_vec = math::to_scale<true>(mat);
111 scales[i] = pxr::GfVec3f(scale_vec.x, scale_vec.y, scale_vec.z);
112 }
113 blender::io::usd::set_attribute(scales_attr, scales, time, usd_value_writer_);
114
115 /* other attr */
116 bke::AttributeAccessor attributes_eval = *component->attributes();
117 attributes_eval.foreach_attribute([&](const bke::AttributeIter &iter) {
118 if (iter.name[0] == '.' || blender::bke::attribute_name_is_anonymous(iter.name) ||
119 ELEM(iter.name, "instance_transform") || ELEM(iter.name, "scale") ||
120 ELEM(iter.name, "orientation") || ELEM(iter.name, "mask") ||
121 ELEM(iter.name, "proto_index") || ELEM(iter.name, "id"))
122 {
123 return;
124 }
125
126 this->write_attribute_data(iter, usd_instancer, time);
127 });
128
129 /* prototypes relations */
130 const pxr::SdfPath protoParentPath = usd_path.AppendChild(pxr::TfToken("Prototypes"));
131 pxr::UsdPrim prototypesOver = stage->DefinePrim(protoParentPath);
132 pxr::SdfPathVector proto_wrapper_paths;
133
134 Map<std::string, int> proto_index_map;
135 Map<std::string, pxr::SdfPath> proto_path_map;
136
137 if (!prototype_paths_.is_empty() && usd_instancer) {
138 int iter = 0;
139
140 for (const std::pair<pxr::SdfPath, Object *> &entry : prototype_paths_) {
141 const pxr::SdfPath &source_path = entry.first;
142 Object *obj = entry.second;
143
144 if (source_path.IsEmpty()) {
145 continue;
146 }
147
148 const pxr::SdfPath proto_path = protoParentPath.AppendChild(
149 pxr::TfToken("Prototype_" + std::to_string(iter)));
150
151 pxr::UsdPrim prim = stage->DefinePrim(proto_path);
152
153 /* To avoid USD error of Unresolved reference prim path, make sure the referenced path
154 * exists. */
155 stage->DefinePrim(source_path);
156 prim.GetReferences().AddReference(pxr::SdfReference("", source_path));
157 proto_wrapper_paths.push_back(proto_path);
158
159 std::string ob_name = BKE_id_name(obj->id);
160 proto_index_map.add_new(ob_name, iter);
161 proto_path_map.add_new(ob_name, proto_path);
162
163 ++iter;
164 }
165 usd_instancer.GetPrototypesRel().SetTargets(proto_wrapper_paths);
166 }
167
168 /* proto indices */
169 /* must be the last to populate */
170 pxr::UsdAttribute proto_indices_attr = usd_instancer.CreateProtoIndicesAttr();
171 pxr::VtArray<int> proto_indices;
172 Vector<std::pair<int, int>> collection_instance_object_count_map;
173
174 Span<int> reference_handles = instances->reference_handles();
175 Span<bke::InstanceReference> references = instances->references();
176 Map<std::string, int> final_proto_index_map;
177
178 for (int i = 0; i < instance_num; i++) {
179 bke::InstanceReference reference = references[reference_handles[i]];
180
181 process_instance_reference(reference,
182 i,
183 proto_index_map,
184 final_proto_index_map,
185 proto_path_map,
186 stage,
187 proto_indices,
188 collection_instance_object_count_map);
189 }
190
191 blender::io::usd::set_attribute(proto_indices_attr, proto_indices, time, usd_value_writer_);
192
193 /* Handle Collection Prototypes */
194 if (!collection_instance_object_count_map.is_empty()) {
195 handle_collection_prototypes(
196 usd_instancer, time, instance_num, collection_instance_object_count_map);
197 }
198
199 /* Clean unused prototype. When finding prototype paths under the context of a point instancer,
200 * all the prototypes are collected, even those used by lower-level nested child PointInstancers.
201 * It can happen that different levels in nested PointInstancers share the same prototypes, but
202 * if not, we need to clean the extra prototypes from the prototype relationship for a cleaner
203 * USD export. */
204 compact_prototypes(usd_instancer, time, proto_wrapper_paths);
205}
206
207void USDPointInstancerWriter::process_instance_reference(
208 const bke::InstanceReference &reference,
209 int instance_index,
210 Map<std::string, int> &proto_index_map,
211 Map<std::string, int> &final_proto_index_map,
212 Map<std::string, pxr::SdfPath> &proto_path_map,
213 pxr::UsdStageRefPtr stage,
214 pxr::VtArray<int> &proto_indices,
215 Vector<std::pair<int, int>> &collection_instance_object_count_map)
216{
217 /* TODO: Verify logic around the `add_overwrite` calls below. Using `add_new` will trigger
218 * asserts because multiple items are being added to the map with the same key. Original code
219 * was using std::map and repeatedly reassigning with `final_proto_index_map[ob_name] = ...` */
220 switch (reference.type()) {
222 Object &object = reference.object();
223 std::string ob_name = BKE_id_name(object.id);
224
225 if (proto_index_map.contains(ob_name)) {
226 proto_indices.push_back(proto_index_map.lookup(ob_name));
227
228 final_proto_index_map.add_overwrite(ob_name, proto_index_map.lookup(ob_name));
229
230 /* If the reference is Object, clear prototype's local transform to identity to avoid
231 * double transforms. The PointInstancer will fully control instance placement. */
232 override_transform(stage, proto_path_map.lookup(ob_name), float4x4::identity());
233 }
234 break;
235 }
236
238 Collection &collection = reference.collection();
239 int object_num = 0;
240 FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (&collection, object) {
241 std::string ob_name = BKE_id_name(object->id);
242
243 if (proto_index_map.contains(ob_name)) {
244 object_num += 1;
245 proto_indices.push_back(proto_index_map.lookup(ob_name));
246
247 final_proto_index_map.add_overwrite(ob_name, proto_index_map.lookup(ob_name));
248 }
249 }
251 collection_instance_object_count_map.append(std::make_pair(instance_index, object_num));
252 break;
253 }
254
256 bke::GeometrySet geometry_set = reference.geometry_set();
257 std::string set_name = geometry_set.name;
258
259 if (proto_index_map.contains(set_name)) {
260 proto_indices.push_back(proto_index_map.lookup(set_name));
261
262 final_proto_index_map.add_overwrite(set_name, proto_index_map.lookup(set_name));
263 }
264
265 Vector<const bke::GeometryComponent *> components = geometry_set.get_components();
266 for (const bke::GeometryComponent *comp : components) {
267 if (const bke::Instances *instances =
268 static_cast<const bke::InstancesComponent &>(*comp).get())
269 {
270 Span<int> ref_handles = instances->reference_handles();
271 Span<bke::InstanceReference> refs = instances->references();
272
273 /* If the top-level GeometrySet is not in proto_index_map, recursively traverse child
274 * InstanceReferences to resolve prototype indices. If the name matches proto_index_map,
275 * skip traversal to avoid duplicates, since GeometrySet names may overlap with object
276 * names. */
277 if (!proto_index_map.contains(set_name)) {
278 for (int index = 0; index < ref_handles.size(); ++index) {
279 const bke::InstanceReference &child_ref = refs[ref_handles[index]];
280
281 /* Recursively traverse nested GeometrySets to resolve prototype indices for all
282 * instances. */
283 process_instance_reference(child_ref,
284 instance_index,
285 proto_index_map,
286 final_proto_index_map,
287 proto_path_map,
288 stage,
289 proto_indices,
290 collection_instance_object_count_map);
291 }
292 }
293
294 /* If the reference is GeometrySet, then override the transform with the transform of the
295 * Instance inside this GeometrySet. */
296 Span<float4x4> transforms = instances->transforms();
297 if (transforms.size() == 1) {
298 if (proto_path_map.contains(set_name)) {
299 override_transform(stage, proto_path_map.lookup(set_name), transforms[0]);
300 }
301 }
302 }
303 }
304 break;
305 }
306
308 default:
309 break;
310 }
311}
312
313void USDPointInstancerWriter::compact_prototypes(const pxr::UsdGeomPointInstancer &usd_instancer,
314 const pxr::UsdTimeCode time,
315 const pxr::SdfPathVector &proto_paths) const
316{
317 pxr::UsdAttribute proto_indices_attr = usd_instancer.GetProtoIndicesAttr();
318 pxr::VtArray<int> proto_indices;
319 if (!proto_indices_attr.Get(&proto_indices, time)) {
320 return;
321 }
322
323 /* Find actually used prototype indices. */
324 Set<int> used_proto_indices;
325 used_proto_indices.add_multiple(Span(proto_indices.cbegin(), proto_indices.size()));
326
327 Map<int, int> remap;
328 int new_index = 0;
329 for (int i = 0; i < proto_paths.size(); ++i) {
330 if (used_proto_indices.contains(i)) {
331 remap.add(i, new_index++);
332 }
333 }
334
335 /* Remap protoIndices. */
336 for (int &idx : proto_indices) {
337 idx = remap.lookup(idx);
338 }
339 proto_indices_attr.Set(proto_indices, time);
340
341 pxr::SdfPathVector compact_proto_paths;
342 for (int i = 0; i < proto_paths.size(); ++i) {
343 if (used_proto_indices.contains(i)) {
344 compact_proto_paths.push_back(proto_paths[i]);
345 }
346 }
347
348 usd_instancer.GetPrototypesRel().SetTargets(compact_proto_paths);
349}
350
351void USDPointInstancerWriter::override_transform(const pxr::UsdStageRefPtr stage,
352 const pxr::SdfPath &proto_path,
353 const float4x4 &transform) const
354{
355 pxr::UsdPrim prim = stage->GetPrimAtPath(proto_path);
356 if (!prim) {
357 return;
358 }
359
360 // Extract translation
361 const float3 &pos = transform.location();
362 pxr::GfVec3d override_position(pos.x, pos.y, pos.z);
363
364 // Extract rotation
366 pxr::GfVec3f override_rotation(euler.x, euler.y, euler.z);
367
368 // Extract scale
369 const float3 scale_vec = math::to_scale<true>(transform);
370 pxr::GfVec3f override_scale(scale_vec.x, scale_vec.y, scale_vec.z);
371
372 pxr::UsdGeomXformable xformable(prim);
373 xformable.ClearXformOpOrder();
374 xformable.AddTranslateOp().Set(override_position);
375 xformable.AddRotateXYZOp().Set(override_rotation);
376 xformable.AddScaleOp().Set(override_scale);
377}
378
379template<typename T>
380static pxr::VtArray<T> DuplicateArray(const pxr::VtArray<T> &original, size_t copies)
381{
382 pxr::VtArray<T> newArray;
383 size_t originalSize = original.size();
384 newArray.resize(originalSize * copies);
385 for (size_t i = 0; i < copies; ++i) {
386 std::copy(original.begin(), original.end(), newArray.begin() + i * originalSize);
387 }
388 return newArray;
389}
390
391template<typename T, typename GetterFunc, typename CreatorFunc>
392static void DuplicatePerInstanceAttribute(const GetterFunc &getter,
393 const CreatorFunc &creator,
394 size_t copies,
395 const pxr::UsdTimeCode &time)
396{
397 pxr::VtArray<T> values;
398 if (getter().Get(&values, time) && !values.empty()) {
399 auto newValues = DuplicateArray(values, copies);
400 creator().Set(newValues, time);
401 }
402}
403
404template<typename T, typename GetterFunc, typename CreatorFunc>
405static void ExpandAttributePerInstance(const GetterFunc &getter,
406 const CreatorFunc &creator,
407 const Span<std::pair<int, int>> instance_object_map,
408 const pxr::UsdTimeCode &time)
409{
410 /* MARK: Handle Collection Prototypes
411 * ----------------------------------
412 * In Blender, a Collection is not an actual Object type. When exporting, the iterator
413 * flattens the Collection hierarchy, treating each object inside the Collection as an
414 * individual prototype. However, all these prototypes share the same instance attributes
415 * (e.g., positions, orientations, scales).
416 *
417 * To ensure correct arrangement, reading, and drawing in OpenUSD, we need to explicitly
418 * duplicate the instance attributes across all prototypes derived from the Collection. */
419 pxr::VtArray<T> original_values;
420 if (!getter().Get(&original_values, time) || original_values.empty()) {
421 return;
422 }
423
424 pxr::VtArray<T> expanded_values;
425 for (const auto &[instance_index, object_count] : instance_object_map) {
426 if (instance_index < int(original_values.size())) {
427 for (int i = 0; i < object_count; ++i) {
428 expanded_values.push_back(original_values[instance_index]);
429 }
430 }
431 }
432
433 creator().Set(expanded_values, time);
434}
435
436void USDPointInstancerWriter::handle_collection_prototypes(
437 const pxr::UsdGeomPointInstancer &usd_instancer,
438 const pxr::UsdTimeCode time,
439 const int instance_num,
440 const Span<std::pair<int, int>> collection_instance_object_count_map) const
441{
442 // Duplicate attributes
443 if (usd_instancer.GetPositionsAttr().HasAuthoredValue()) {
444 ExpandAttributePerInstance<pxr::GfVec3f>([&]() { return usd_instancer.GetPositionsAttr(); },
445 [&]() { return usd_instancer.CreatePositionsAttr(); },
446 collection_instance_object_count_map,
447 time);
448 }
449 if (usd_instancer.GetOrientationsAttr().HasAuthoredValue()) {
451 [&]() { return usd_instancer.GetOrientationsAttr(); },
452 [&]() { return usd_instancer.CreateOrientationsAttr(); },
453 collection_instance_object_count_map,
454 time);
455 }
456 if (usd_instancer.GetScalesAttr().HasAuthoredValue()) {
457 ExpandAttributePerInstance<pxr::GfVec3f>([&]() { return usd_instancer.GetScalesAttr(); },
458 [&]() { return usd_instancer.CreateScalesAttr(); },
459 collection_instance_object_count_map,
460 time);
461 }
462 if (usd_instancer.GetVelocitiesAttr().HasAuthoredValue()) {
464 [&]() { return usd_instancer.GetVelocitiesAttr(); },
465 [&]() { return usd_instancer.CreateVelocitiesAttr(); },
466 collection_instance_object_count_map,
467 time);
468 }
469 if (usd_instancer.GetAngularVelocitiesAttr().HasAuthoredValue()) {
471 [&]() { return usd_instancer.GetAngularVelocitiesAttr(); },
472 [&]() { return usd_instancer.CreateAngularVelocitiesAttr(); },
473 collection_instance_object_count_map,
474 time);
475 }
476
477 // Duplicate Primvars
478 const pxr::UsdGeomPrimvarsAPI primvars_api(usd_instancer);
479 for (const pxr::UsdGeomPrimvar &primvar : primvars_api.GetPrimvars()) {
480 if (!primvar.HasAuthoredValue()) {
481 continue;
482 }
483 const pxr::TfToken pv_name = primvar.GetPrimvarName();
484 const pxr::SdfValueTypeName pv_type = primvar.GetTypeName();
485 const pxr::TfToken pv_interp = primvar.GetInterpolation();
486 auto create = [&]() { return primvars_api.CreatePrimvar(pv_name, pv_type, pv_interp); };
487
488 if (pv_type == pxr::SdfValueTypeNames->FloatArray) {
490 [&]() { return primvar; }, create, collection_instance_object_count_map, time);
491 }
492 else if (pv_type == pxr::SdfValueTypeNames->IntArray) {
494 [&]() { return primvar; }, create, collection_instance_object_count_map, time);
495 }
496 else if (pv_type == pxr::SdfValueTypeNames->UCharArray) {
498 [&]() { return primvar; }, create, collection_instance_object_count_map, time);
499 }
500 else if (pv_type == pxr::SdfValueTypeNames->Float2Array) {
502 [&]() { return primvar; }, create, collection_instance_object_count_map, time);
503 }
504 else if (ELEM(pv_type,
505 pxr::SdfValueTypeNames->Float3Array,
506 pxr::SdfValueTypeNames->Color3fArray,
507 pxr::SdfValueTypeNames->Color4fArray))
508 {
510 [&]() { return primvar; }, create, collection_instance_object_count_map, time);
511 }
512 else if (pv_type == pxr::SdfValueTypeNames->QuatfArray) {
514 [&]() { return primvar; }, create, collection_instance_object_count_map, time);
515 }
516 else if (pv_type == pxr::SdfValueTypeNames->BoolArray) {
518 [&]() { return primvar; }, create, collection_instance_object_count_map, time);
519 }
520 else if (pv_type == pxr::SdfValueTypeNames->StringArray) {
522 [&]() { return primvar; }, create, collection_instance_object_count_map, time);
523 }
524 }
525
526 /* MARK: Ensure Instance Indices Exist
527 * -----------------------------------
528 * If the PointInstancer has no authored instance indices, manually generate a default
529 * sequence of indices to ensure the PointInstancer functions correctly in OpenUSD.
530 * This guarantees that each instance can correctly reference its prototype. */
531 pxr::UsdAttribute proto_indices_attr = usd_instancer.GetProtoIndicesAttr();
532 if (!proto_indices_attr.HasAuthoredValue()) {
533 Vector<int> index;
534 for (int i = 0; i < prototype_paths_.size(); i++) {
535 index.append_n_times(i, instance_num);
536 }
537
538 proto_indices_attr.Set(pxr::VtArray<int>(index.begin(), index.end()));
539 }
540}
541
542void USDPointInstancerWriter::write_attribute_data(const bke::AttributeIter &attr,
543 const pxr::UsdGeomPointInstancer &usd_instancer,
544 const pxr::UsdTimeCode time)
545{
546 const std::optional<pxr::SdfValueTypeName> pv_type = convert_blender_type_to_usd(attr.data_type);
547
548 if (!pv_type) {
549 BKE_reportf(this->reports(),
551 "Attribute '%s' (Blender domain %d, type %d) cannot be converted to USD",
552 attr.name.c_str(),
553 int(attr.domain),
554 int(attr.data_type));
555 return;
556 }
557
558 const GVArray attribute = *attr.get();
559 if (attribute.is_empty()) {
560 return;
561 }
562
563 if (attr.name == "mask") {
564 pxr::UsdAttribute idsAttr = usd_instancer.GetIdsAttr();
565 if (!idsAttr) {
566 idsAttr = usd_instancer.CreateIdsAttr();
567 }
568
569 pxr::UsdAttribute invisibleIdsAttr = usd_instancer.GetInvisibleIdsAttr();
570 if (!invisibleIdsAttr) {
571 invisibleIdsAttr = usd_instancer.CreateInvisibleIdsAttr();
572 }
573
574 Vector<bool> mask_values(attribute.size());
575 attribute.materialize(IndexMask(attribute.size()), mask_values.data());
576
577 pxr::VtArray<int64_t> ids;
578 pxr::VtArray<int64_t> invisibleIds;
579 ids.reserve(mask_values.size());
580
581 for (int64_t i = 0; i < mask_values.size(); i++) {
582 ids.push_back(i);
583 if (!mask_values[i]) {
584 invisibleIds.push_back(i);
585 }
586 }
587
589 blender::io::usd::set_attribute(invisibleIdsAttr, invisibleIds, time, usd_value_writer_);
590 }
591
592 const pxr::TfToken pv_name(
593 make_safe_name(attr.name, usd_export_context_.export_params.allow_unicode));
594 const pxr::UsdGeomPrimvarsAPI pv_api = pxr::UsdGeomPrimvarsAPI(usd_instancer);
595
596 pxr::UsdGeomPrimvar pv_attr = pv_api.CreatePrimvar(pv_name, *pv_type);
597
598 copy_blender_attribute_to_primvar(attribute, attr.data_type, time, pv_attr, usd_value_writer_);
599}
600
601} // namespace blender::io::usd
#define FOREACH_COLLECTION_OBJECT_RECURSIVE_END
#define FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN(_collection, _object)
const char * BKE_id_name(const ID &id)
void BKE_reportf(ReportList *reports, eReportType type, const char *format,...) ATTR_PRINTF_FORMAT(3
@ RPT_ERROR
Definition BKE_report.hh:39
@ RPT_WARNING
Definition BKE_report.hh:38
#define BLI_assert(a)
Definition BLI_assert.h:46
#define ELEM(...)
Object groups, one object can be in many groups at once.
Object is a sort of wrapper for general info.
SIMD_FORCE_INLINE btVector3 transform(const btVector3 &point) const
long long int int64_t
bool add(const Key &key, const Value &value)
Definition BLI_map.hh:295
const Value & lookup(const Key &key) const
Definition BLI_map.hh:545
bool contains(const Key &key) const
Definition BLI_set.hh:310
void add_multiple(Span< Key > keys)
Definition BLI_set.hh:287
constexpr int64_t size() const
Definition BLI_span.hh:252
T * end()
T * begin()
void append_n_times(const T &value, const int64_t n)
bool add_overwrite(const Key &key, const Value &value)
Definition BLI_map.hh:325
const Value & lookup(const Key &key) const
Definition BLI_map.hh:545
void add_new(const Key &key, const Value &value)
Definition BLI_map.hh:265
bool contains(const Key &key) const
Definition BLI_map.hh:353
constexpr int64_t size() const
Definition BLI_span.hh:252
bool is_empty() const
void foreach_attribute(const FunctionRef< void(const AttributeIter &)> fn) const
virtual std::optional< AttributeAccessor > attributes() const
Collection & collection() const
Span< int > reference_handles() const
Definition instances.cc:215
Span< float4x4 > transforms() const
Definition instances.cc:228
Span< InstanceReference > references() const
Definition instances.cc:275
int instances_num() const
Definition instances.cc:393
const pxr::SdfPath & usd_path() const
pxr::UsdTimeCode get_export_time_code() const
pxr::UsdUtilsSparseValueWriter usd_value_writer_
USDAbstractWriter(const USDExporterContext &usd_export_context)
const USDExporterContext usd_export_context_
USDPointInstancerWriter(const USDExporterContext &ctx, const blender::Set< std::pair< pxr::SdfPath, Object * > > &prototype_paths, std::unique_ptr< USDAbstractWriter > base_writer)
void do_write(HierarchyContext &context) override
uint pos
std::unique_ptr< IDProperty, IDPropertyDeleter > create(StringRef prop_name, int32_t value, eIDPropertyFlag flags={})
Allocate a new IDProperty of type IDP_INT, set its name and value.
bool attribute_name_is_anonymous(const StringRef name)
GeometrySet object_get_evaluated_geometry_set(const Object &object, bool apply_subdiv=true)
void copy_blender_attribute_to_primvar(const GVArray &attribute, const bke::AttrType data_type, const pxr::UsdTimeCode time, const pxr::UsdGeomPrimvar &primvar, pxr::UsdUtilsSparseValueWriter &value_writer)
std::optional< pxr::SdfValueTypeName > convert_blender_type_to_usd(const bke::AttrType blender_type, bool use_color3f_type)
static pxr::VtArray< T > DuplicateArray(const pxr::VtArray< T > &original, size_t copies)
std::string make_safe_name(const StringRef name, bool allow_unicode)
Definition usd_utils.cc:18
void set_attribute(const pxr::UsdAttribute &attr, const USDT value, pxr::UsdTimeCode time, pxr::UsdUtilsSparseValueWriter &value_writer)
static void ExpandAttributePerInstance(const GetterFunc &getter, const CreatorFunc &creator, const Span< std::pair< int, int > > instance_object_map, const pxr::UsdTimeCode &time)
static void DuplicatePerInstanceAttribute(const GetterFunc &getter, const CreatorFunc &creator, size_t copies, const pxr::UsdTimeCode &time)
QuaternionBase< float > Quaternion
QuaternionBase< T > to_quaternion(const AxisAngleBase< T, AngleT > &axis_angle)
EulerXYZBase< float > EulerXYZ
MatBase< T, NumCol, NumRow > normalize(const MatBase< T, NumCol, NumRow > &a)
VecBase< T, 3 > to_scale(const MatBase< T, NumCol, NumRow > &mat)
Euler3Base< T > to_euler(const AxisAngleBase< T, AngleT > &axis_angle, EulerOrder order)
MatBase< float, 4, 4 > float4x4
VecBase< float, 3 > float3
const GeometryComponent * get_component(GeometryComponent::Type component_type) const
float z
Definition sky_math.h:136
float y
Definition sky_math.h:136
float x
Definition sky_math.h:136
i
Definition text_draw.cc:230