34#include <pxr/base/gf/math.h>
35#include <pxr/base/gf/matrix3f.h>
36#include <pxr/base/gf/quatd.h>
37#include <pxr/base/gf/quatf.h>
38#include <pxr/base/gf/range3f.h>
39#include <pxr/base/gf/rotation.h>
40#include <pxr/base/gf/vec3d.h>
41#include <pxr/base/gf/vec3f.h>
42#include <pxr/base/vt/array.h>
43#include <pxr/usd/usdGeom/pointInstancer.h>
44#include <pxr/usd/usdGeom/primvarsAPI.h>
45#include <pxr/usd/usdGeom/scope.h>
46#include <pxr/usd/usdGeom/xform.h>
50#include <fmt/format.h>
56 std::set<std::pair<pxr::SdfPath, Object *>> &prototype_paths,
57 std::unique_ptr<USDAbstractWriter> base_writer)
59 base_writer_(std::move(base_writer)),
60 prototype_paths_(prototype_paths)
68 base_writer_->write(context);
79 Object *object_eval = context.object;
89 const pxr::UsdGeomPointInstancer usd_instancer = pxr::UsdGeomPointInstancer::Define(stage,
96 if (transforms.
size() != instance_num) {
99 "Instances number '%d' doesn't match transforms size '%d'",
101 int(transforms.
size()));
106 pxr::UsdAttribute position_attr = usd_instancer.CreatePositionsAttr();
107 pxr::VtArray<pxr::GfVec3f> positions(instance_num);
108 for (
int i = 0;
i < instance_num;
i++) {
110 positions[
i] = pxr::GfVec3f(
pos.x,
pos.y,
pos.z);
115 pxr::UsdAttribute orientations_attr = usd_instancer.CreateOrientationsAttr();
116 pxr::VtArray<pxr::GfQuath> orientation(instance_num);
117 for (
int i = 0;
i < instance_num;
i++) {
120 orientation[
i] = pxr::GfQuath(quat.
w, pxr::GfVec3h(quat.
x, quat.
y, quat.
z));
125 pxr::UsdAttribute scales_attr = usd_instancer.CreateScalesAttr();
126 pxr::VtArray<pxr::GfVec3f> scales(instance_num);
127 for (
int i = 0;
i < instance_num;
i++) {
130 scales[
i] = pxr::GfVec3f(scale_vec.x, scale_vec.y, scale_vec.z);
145 this->write_attribute_data(iter, usd_instancer, timecode);
149 const pxr::SdfPath protoParentPath =
usd_path.AppendChild(pxr::TfToken(
"Prototypes"));
150 pxr::UsdPrim prototypesOver = stage->DefinePrim(protoParentPath);
151 pxr::SdfPathVector proto_wrapper_paths;
153 std::map<std::string, int> proto_index_map;
154 std::map<std::string, pxr::SdfPath> proto_path_map;
156 if (!prototype_paths_.empty() && usd_instancer) {
159 for (
const std::pair<pxr::SdfPath, Object *> &entry : prototype_paths_) {
160 const pxr::SdfPath &source_path = entry.first;
163 if (source_path.IsEmpty()) {
167 const pxr::SdfPath proto_path = protoParentPath.AppendChild(
168 pxr::TfToken(proto_name_ +
"_" + std::to_string(iter)));
170 pxr::UsdPrim prim = stage->DefinePrim(proto_path);
174 stage->DefinePrim(source_path);
175 prim.GetReferences().AddReference(pxr::SdfReference(
"", source_path));
176 proto_wrapper_paths.push_back(proto_path);
179 proto_index_map[ob_name] = iter;
180 proto_path_map[ob_name] = proto_path;
184 usd_instancer.GetPrototypesRel().SetTargets(proto_wrapper_paths);
185 stage->GetRootLayer()->Save();
190 pxr::UsdAttribute proto_indices_attr = usd_instancer.CreateProtoIndicesAttr();
191 pxr::VtArray<int> proto_indices;
192 std::vector<std::pair<int, int>> collection_instance_object_count_map;
196 std::map<std::string, int> final_proto_index_map;
198 for (
int i = 0;
i < instance_num;
i++) {
201 process_instance_reference(reference,
204 final_proto_index_map,
208 collection_instance_object_count_map);
214 if (!collection_instance_object_count_map.empty()) {
215 handle_collection_prototypes(
216 usd_instancer, timecode, instance_num, collection_instance_object_count_map);
224 compact_prototypes(usd_instancer, timecode, proto_wrapper_paths);
226 stage->GetRootLayer()->Save();
229void USDPointInstancerWriter::process_instance_reference(
232 std::map<std::string, int> &proto_index_map,
233 std::map<std::string, int> &final_proto_index_map,
234 std::map<std::string, pxr::SdfPath> &proto_path_map,
235 pxr::UsdStageRefPtr stage,
236 pxr::VtArray<int> &proto_indices,
237 std::vector<std::pair<int, int>> &collection_instance_object_count_map)
239 switch (reference.
type()) {
244 if (proto_index_map.find(ob_name) != proto_index_map.end()) {
245 proto_indices.push_back(proto_index_map[ob_name]);
247 final_proto_index_map[ob_name] = proto_index_map[ob_name];
262 if (proto_index_map.find(ob_name) != proto_index_map.end()) {
264 proto_indices.push_back(proto_index_map[ob_name]);
266 final_proto_index_map[ob_name] = proto_index_map[ob_name];
270 collection_instance_object_count_map.push_back(std::make_pair(instance_index, object_num));
275 bke::GeometrySet geometry_set = reference.
geometry_set();
276 std::string set_name = geometry_set.
name;
278 if (proto_index_map.find(set_name) != proto_index_map.end()) {
279 proto_indices.push_back(proto_index_map[set_name]);
281 final_proto_index_map[set_name] = proto_index_map[set_name];
285 for (
const bke::GeometryComponent *comp : components) {
286 if (
const bke::Instances *instances =
287 static_cast<const bke::InstancesComponent &
>(*comp).get())
289 Span<int> ref_handles = instances->reference_handles();
290 Span<bke::InstanceReference> refs = instances->references();
296 if (proto_index_map.find(set_name) == proto_index_map.end()) {
297 for (
int index = 0; index < ref_handles.
size(); ++index) {
298 const bke::InstanceReference &child_ref = refs[ref_handles[index]];
302 process_instance_reference(child_ref,
305 final_proto_index_map,
309 collection_instance_object_count_map);
315 Span<float4x4> transforms = instances->transforms();
316 if (transforms.
size() == 1) {
317 if (proto_path_map.find(set_name) != proto_path_map.end()) {
318 override_transform(stage, proto_path_map[set_name], transforms[0]);
332void USDPointInstancerWriter::compact_prototypes(
const pxr::UsdGeomPointInstancer &usd_instancer,
333 const pxr::UsdTimeCode timecode,
334 const pxr::SdfPathVector &proto_paths)
336 pxr::UsdAttribute proto_indices_attr = usd_instancer.GetProtoIndicesAttr();
337 pxr::VtArray<int> proto_indices;
338 if (!proto_indices_attr.Get(&proto_indices, timecode)) {
343 std::set<int> used_proto_indices(proto_indices.begin(), proto_indices.end());
345 std::map<int, int> remap;
347 for (
int i = 0;
i < proto_paths.size(); ++
i) {
348 if (used_proto_indices.count(
i)) {
349 remap[
i] = new_index++;
354 for (
int &idx : proto_indices) {
357 proto_indices_attr.Set(proto_indices, timecode);
359 pxr::SdfPathVector compact_proto_paths;
360 for (
int i = 0;
i < proto_paths.size(); ++
i) {
361 if (used_proto_indices.count(
i)) {
362 compact_proto_paths.push_back(proto_paths[
i]);
366 usd_instancer.GetPrototypesRel().SetTargets(compact_proto_paths);
369void USDPointInstancerWriter::override_transform(pxr::UsdStageRefPtr stage,
370 const pxr::SdfPath &proto_path,
375 pxr::GfVec3d override_position(
pos.x,
pos.y,
pos.z);
379 pxr::GfVec3f override_rotation(euler.
x, euler.
y, euler.
z);
383 pxr::GfVec3f override_scale(scale_vec.
x, scale_vec.
y, scale_vec.
z);
385 pxr::UsdPrim prim = stage->GetPrimAtPath(proto_path);
390 pxr::UsdGeomXformable xformable(prim);
391 xformable.ClearXformOpOrder();
392 xformable.AddTranslateOp().Set(override_position);
393 xformable.AddRotateXYZOp().Set(override_rotation);
394 xformable.AddScaleOp().Set(override_scale);
398static pxr::VtArray<T>
DuplicateArray(
const pxr::VtArray<T> &original,
size_t copies)
400 pxr::VtArray<T> newArray;
401 size_t originalSize = original.size();
402 newArray.resize(originalSize * copies);
403 for (
size_t i = 0;
i < copies; ++
i) {
404 std::copy(original.begin(), original.end(), newArray.begin() +
i * originalSize);
409template<
typename T,
typename GetterFunc,
typename CreatorFunc>
411 const CreatorFunc &creator,
413 const pxr::UsdTimeCode &timecode)
415 pxr::VtArray<T> values;
416 if (getter().Get(&values, timecode) && !values.empty()) {
418 creator().Set(newValues, timecode);
422template<
typename T,
typename GetterFunc,
typename CreatorFunc>
424 const CreatorFunc &creator,
425 const std::vector<std::pair<int, int>> &instance_object_map,
426 const pxr::UsdTimeCode &timecode)
437 pxr::VtArray<T> original_values;
438 if (!getter().Get(&original_values, timecode) || original_values.empty()) {
442 pxr::VtArray<T> expanded_values;
443 for (
const auto &[instance_index, object_count] : instance_object_map) {
444 if (instance_index <
static_cast<int>(original_values.size())) {
445 for (
int i = 0;
i < object_count; ++
i) {
446 expanded_values.push_back(original_values[instance_index]);
451 creator().Set(expanded_values, timecode);
454void USDPointInstancerWriter::handle_collection_prototypes(
455 const pxr::UsdGeomPointInstancer &usd_instancer,
456 const pxr::UsdTimeCode timecode,
458 const std::vector<std::pair<int, int>> &collection_instance_object_count_map)
461 if (usd_instancer.GetPositionsAttr().HasAuthoredValue()) {
463 [&]() {
return usd_instancer.CreatePositionsAttr(); },
464 collection_instance_object_count_map,
467 if (usd_instancer.GetOrientationsAttr().HasAuthoredValue()) {
469 [&]() {
return usd_instancer.GetOrientationsAttr(); },
470 [&]() {
return usd_instancer.CreateOrientationsAttr(); },
471 collection_instance_object_count_map,
474 if (usd_instancer.GetScalesAttr().HasAuthoredValue()) {
476 [&]() {
return usd_instancer.CreateScalesAttr(); },
477 collection_instance_object_count_map,
480 if (usd_instancer.GetVelocitiesAttr().HasAuthoredValue()) {
482 [&]() {
return usd_instancer.GetVelocitiesAttr(); },
483 [&]() {
return usd_instancer.CreateVelocitiesAttr(); },
484 collection_instance_object_count_map,
487 if (usd_instancer.GetAngularVelocitiesAttr().HasAuthoredValue()) {
489 [&]() {
return usd_instancer.GetAngularVelocitiesAttr(); },
490 [&]() {
return usd_instancer.CreateAngularVelocitiesAttr(); },
491 collection_instance_object_count_map,
496 const pxr::UsdGeomPrimvarsAPI primvars_api(usd_instancer);
497 std::vector<pxr::UsdGeomPrimvar> primvars = primvars_api.GetPrimvars();
498 for (
const pxr::UsdGeomPrimvar &primvar : primvars) {
499 if (!primvar.HasAuthoredValue()) {
502 const pxr::TfToken name = primvar.GetPrimvarName();
503 const pxr::SdfValueTypeName type = primvar.GetTypeName();
504 const pxr::TfToken
interp = primvar.GetInterpolation();
505 auto create = [&]() {
return primvars_api.CreatePrimvar(name, type,
interp); };
507 if (type == pxr::SdfValueTypeNames->FloatArray) {
509 [&]() {
return primvar; },
create, collection_instance_object_count_map, timecode);
511 else if (type == pxr::SdfValueTypeNames->IntArray) {
513 [&]() {
return primvar; },
create, collection_instance_object_count_map, timecode);
515 else if (type == pxr::SdfValueTypeNames->UCharArray) {
517 [&]() {
return primvar; },
create, collection_instance_object_count_map, timecode);
519 else if (type == pxr::SdfValueTypeNames->Float2Array) {
521 [&]() {
return primvar; },
create, collection_instance_object_count_map, timecode);
523 else if (type == pxr::SdfValueTypeNames->Float3Array ||
524 type == pxr::SdfValueTypeNames->Color3fArray ||
525 type == pxr::SdfValueTypeNames->Color4fArray)
528 [&]() {
return primvar; },
create, collection_instance_object_count_map, timecode);
530 else if (type == pxr::SdfValueTypeNames->QuatfArray) {
532 [&]() {
return primvar; },
create, collection_instance_object_count_map, timecode);
534 else if (type == pxr::SdfValueTypeNames->BoolArray) {
536 [&]() {
return primvar; },
create, collection_instance_object_count_map, timecode);
538 else if (type == pxr::SdfValueTypeNames->StringArray) {
540 [&]() {
return primvar; },
create, collection_instance_object_count_map, timecode);
549 pxr::UsdAttribute proto_indices_attr = usd_instancer.GetProtoIndicesAttr();
550 if (!proto_indices_attr.HasAuthoredValue()) {
551 std::vector<int> index;
552 for (
int i = 0;
i < prototype_paths_.size();
i++) {
553 std::vector<int> current_proto_index(instance_num,
i);
554 index.insert(index.end(), current_proto_index.begin(), current_proto_index.end());
557 proto_indices_attr.Set(pxr::VtArray<int>(index.begin(), index.end()));
561void USDPointInstancerWriter::write_attribute_data(
const bke::AttributeIter &attr,
562 const pxr::UsdGeomPointInstancer &usd_instancer,
563 const pxr::UsdTimeCode timecode)
570 "Attribute '%s' (Blender domain %d, type %d) cannot be converted to USD",
577 const GVArray attribute = *attr.get();
578 if (attribute.is_empty()) {
582 if (attr.name ==
"mask") {
583 pxr::UsdAttribute idsAttr = usd_instancer.GetIdsAttr();
585 idsAttr = usd_instancer.CreateIdsAttr();
588 pxr::UsdAttribute invisibleIdsAttr = usd_instancer.GetInvisibleIdsAttr();
589 if (!invisibleIdsAttr) {
590 invisibleIdsAttr = usd_instancer.CreateInvisibleIdsAttr();
593 const GVArray attribute = *attr.get();
595 std::vector<int8_t> mask_values(attribute.size());
596 attribute.materialize(IndexMask(attribute.size()), mask_values.data());
598 pxr::VtArray<int64_t> ids;
599 pxr::VtArray<int64_t> invisibleIds;
600 ids.reserve(mask_values.size());
604 if (mask_values[
i] == 0) {
605 invisibleIds.push_back(
i);
613 const pxr::TfToken pv_name(
615 const pxr::UsdGeomPrimvarsAPI pv_api = pxr::UsdGeomPrimvarsAPI(usd_instancer);
617 pxr::UsdGeomPrimvar pv_attr = pv_api.CreatePrimvar(pv_name, *pv_type);
#define FOREACH_COLLECTION_OBJECT_RECURSIVE_END
#define FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN(_collection, _object)
const char * BKE_id_name(const ID &id)
General operations, lookup, etc. for blender objects.
void BKE_reportf(ReportList *reports, eReportType type, const char *format,...) ATTR_PRINTF_FORMAT(3
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
constexpr int64_t size() const
constexpr int64_t size() const
void foreach_attribute(const FunctionRef< void(const AttributeIter &)> fn) const
virtual std::optional< AttributeAccessor > attributes() const
GeometrySet & geometry_set()
Collection & collection() const
const Instances * get() const
Span< int > reference_handles() const
Span< float4x4 > transforms() const
Span< InstanceReference > references() const
int instances_num() const
const pxr::SdfPath & usd_path() const
ReportList * reports() 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, std::set< std::pair< pxr::SdfPath, Object * > > &prototype_paths, std::unique_ptr< USDAbstractWriter > base_writer)
virtual void do_write(HierarchyContext &context) override
ccl_device_inline float interp(const float a, const float b, const float t)
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)
static void ExpandAttributePerInstance(const GetterFunc &getter, const CreatorFunc &creator, const std::vector< std::pair< int, int > > &instance_object_map, const pxr::UsdTimeCode &timecode)
void set_attribute(const pxr::UsdAttribute &attr, const USDT value, pxr::UsdTimeCode timecode, pxr::UsdUtilsSparseValueWriter &value_writer)
void copy_blender_attribute_to_primvar(const GVArray &attribute, const eCustomDataType data_type, const pxr::UsdTimeCode timecode, const pxr::UsdGeomPrimvar &primvar, pxr::UsdUtilsSparseValueWriter &value_writer)
static pxr::VtArray< T > DuplicateArray(const pxr::VtArray< T > &original, size_t copies)
std::string make_safe_name(const StringRef name, bool allow_unicode)
static void DuplicatePerInstanceAttribute(const GetterFunc &getter, const CreatorFunc &creator, size_t copies, const pxr::UsdTimeCode &timecode)
std::optional< pxr::SdfValueTypeName > convert_blender_type_to_usd(const eCustomDataType blender_type, bool use_color3f_type)
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
static MatBase identity()
const GeometryComponent * get_component(GeometryComponent::Type component_type) const