USD: implement native Unicode support

Make use of USD's new UTF-8 support to allow our import/export code to
accept and generate appropriate USD files. This has been a long standing
shortcoming since USD's introduction, with incomplete and complicated
DCC-specific workarounds often attempted.

Summary of changes
- Export gets a new "Allow Unicode" option defaulting to "false". The
  new Unicode USD files are not backward compatible. DCCs using older
  versions of USD (before 24.03) will not be able to load such files so
  we want to provide this as an opt-in option for now.
- Every location which used to call either `USDHierarchyIterator::make_valid_name`
  or `pxr::TfMakeValidIdentifier` will now go through a new `make_safe_name`
  API instead
- Export code is responsible for passing in the `allow_unicode` option
- Import code will always pass in `true` meaning Blender will happily
  accept both existing and new Unicode USD files

Strangely, USD does not provide a convenient way of making valid UTF-8
identifiers and they left their old API unchanged. We had to roll our
own per their advice: https://forum.aousd.org/t/how-to-make-a-unicode-identifier-valid/1435

Pull Request: https://projects.blender.org/blender/blender/pulls/122471
This commit is contained in:
Jesse Yurkovich
2024-06-04 20:53:57 +02:00
committed by Jesse Yurkovich
parent f5131cdee0
commit 9ad2c7df0b
20 changed files with 168 additions and 39 deletions

View File

@@ -105,6 +105,9 @@ if(WITH_ALEMBIC)
endif()
if(WITH_USD)
list(APPEND INC_SYS
${USD_INCLUDE_DIRS}
)
list(APPEND LIB
bf_io_usd
)

View File

@@ -41,6 +41,8 @@
# include "io_utils.hh"
# include "usd.hh"
# include <pxr/pxr.h>
# include <string>
# include <utility>
@@ -259,6 +261,12 @@ static int wm_usd_export_exec(bContext *C, wmOperator *op)
const int usdz_downscale_custom_size = RNA_int_get(op->ptr, "usdz_downscale_custom_size");
# if PXR_VERSION >= 2403
const bool allow_unicode = RNA_boolean_get(op->ptr, "allow_unicode");
# else
const bool allow_unicode = false;
# endif
char root_prim_path[FILE_MAX];
RNA_string_get(op->ptr, "root_prim_path", root_prim_path);
process_prim_path(root_prim_path);
@@ -299,6 +307,7 @@ static int wm_usd_export_exec(bContext *C, wmOperator *op)
export_volumes,
usdz_downscale_size,
usdz_downscale_custom_size,
allow_unicode,
};
STRNCPY(params.root_prim_path, root_prim_path);
@@ -337,6 +346,9 @@ static void wm_usd_export_draw(bContext *C, wmOperator *op)
row = uiLayoutRow(col, true);
uiItemR(row, ptr, "author_blender_name", UI_ITEM_NONE, nullptr, ICON_NONE);
uiLayoutSetActive(row, RNA_boolean_get(op->ptr, "export_custom_properties"));
# if PXR_VERSION >= 2403
uiItemR(col, ptr, "allow_unicode", UI_ITEM_NONE, nullptr, ICON_NONE);
# endif
uiItemR(col, ptr, "convert_world_material", UI_ITEM_NONE, nullptr, ICON_NONE);
@@ -645,6 +657,16 @@ void WM_OT_usd_export(wmOperatorType *ot)
"Currently works for simple materials, consisting of an environment texture "
"connected to a background shader, with an optional vector multiply of the texture color");
# if PXR_VERSION >= 2403
RNA_def_boolean(
ot->srna,
"allow_unicode",
false,
"Allow Unicode",
"Preserve UTF-8 encoded characters when writing USD prim and property names "
"(requires software utilizing USD 24.03 or greater when opening the resulting files)");
# endif
RNA_def_boolean(ot->srna, "export_meshes", true, "Meshes", "Export all meshes");
RNA_def_boolean(ot->srna, "export_lights", true, "Lights", "Export all lights");

View File

@@ -97,6 +97,7 @@ set(SRC
intern/usd_hook.cc
intern/usd_light_convert.cc
intern/usd_mesh_utils.cc
intern/usd_utils.cc
intern/usd_writer_abstract.cc
intern/usd_writer_armature.cc
@@ -143,6 +144,7 @@ set(SRC
intern/usd_hook.hh
intern/usd_light_convert.hh
intern/usd_mesh_utils.hh
intern/usd_utils.hh
intern/usd_writer_abstract.hh
intern/usd_writer_armature.hh

View File

@@ -3,6 +3,7 @@
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "usd_armature_utils.hh"
#include "usd_utils.hh"
#include "BKE_armature.hh"
#include "BKE_modifier.hh"
@@ -95,22 +96,23 @@ void get_armature_bone_names(const Object *ob_arm,
visit_bones(ob_arm, visitor);
}
pxr::TfToken build_usd_joint_path(const Bone *bone)
pxr::TfToken build_usd_joint_path(const Bone *bone, bool allow_unicode)
{
std::string path(pxr::TfMakeValidIdentifier(bone->name));
std::string path(bone->name);
const Bone *parent = bone->parent;
while (parent) {
path = pxr::TfMakeValidIdentifier(parent->name) + std::string("/") + path;
path = parent->name + std::string("/") + path;
parent = parent->parent;
}
return pxr::TfToken(path);
return pxr::TfToken(make_safe_name(path, allow_unicode));
}
void create_pose_joints(pxr::UsdSkelAnimation &skel_anim,
const Object &obj,
const Map<StringRef, const Bone *> *deform_map)
const Map<StringRef, const Bone *> *deform_map,
bool allow_unicode)
{
BLI_assert(obj.pose);
@@ -126,7 +128,7 @@ void create_pose_joints(pxr::UsdSkelAnimation &skel_anim,
continue;
}
joints.push_back(build_usd_joint_path(pchan->bone));
joints.push_back(build_usd_joint_path(pchan->bone, allow_unicode));
}
}

View File

@@ -46,9 +46,10 @@ void get_armature_bone_names(const Object *ob_arm, bool use_deform, Vector<std::
* in the hierarchy.
*
* \param bone: The bone whose path will be queried.
* \param allow_unicode: Whether to allow unicode bone names to be used
* \return The path to the joint.
*/
pxr::TfToken build_usd_joint_path(const Bone *bone);
pxr::TfToken build_usd_joint_path(const Bone *bone, bool allow_unicode);
/**
* Sets the USD joint paths as an attribute on the given USD animation,
@@ -61,10 +62,12 @@ pxr::TfToken build_usd_joint_path(const Bone *bone);
* is not null, assume only deform bones are to be
* exported and bones not found in this map will be
* skipped
* \param allow_unicode: Whether to allow unicode bone names to be used
*/
void create_pose_joints(pxr::UsdSkelAnimation &skel_anim,
const Object &obj,
const Map<StringRef, const Bone *> *deform_map);
const Map<StringRef, const Bone *> *deform_map,
bool allow_unicode);
/**
* Return the modifier of the given type enabled for the given dependency graph's

View File

@@ -3,6 +3,7 @@
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "usd_blend_shape_utils.hh"
#include "usd_utils.hh"
#include <pxr/usd/usdGeom/primvarsAPI.h>
#include <pxr/usd/usdSkel/animMapper.h>
@@ -190,7 +191,8 @@ bool is_mesh_with_shape_keys(const Object *obj)
void create_blend_shapes(pxr::UsdStageRefPtr stage,
const Object *obj,
const pxr::UsdPrim &mesh_prim)
const pxr::UsdPrim &mesh_prim,
bool allow_unicode)
{
const Key *key = get_mesh_shape_key(obj);
@@ -229,7 +231,7 @@ void create_blend_shapes(pxr::UsdStageRefPtr stage,
continue;
}
pxr::TfToken name(pxr::TfMakeValidIdentifier(kb->name));
pxr::TfToken name(make_safe_name(kb->name, allow_unicode));
blendshape_names.push_back(name);
pxr::SdfPath path = mesh_prim.GetPath().AppendChild(name);

View File

@@ -44,10 +44,12 @@ bool is_mesh_with_shape_keys(const Object *obj);
* \param stage: The stage
* \param obj: The mesh object whose shape keys will be converted to blend shapes
* \param mesh_prim: The USD mesh that will be assigned the blend shape targets
* \param allow_unicode: Whether to allow unicode encoded characters in the blend shape name
*/
void create_blend_shapes(pxr::UsdStageRefPtr stage,
const Object *obj,
const pxr::UsdPrim &mesh_prim);
const pxr::UsdPrim &mesh_prim,
bool allow_unicode);
/**
* Return the current weight values of the given key.

View File

@@ -8,6 +8,7 @@
#include "usd_hierarchy_iterator.hh"
#include "usd_skel_convert.hh"
#include "usd_skel_root_utils.hh"
#include "usd_utils.hh"
#include "usd_writer_abstract.hh"
#include "usd_writer_armature.hh"
#include "usd_writer_camera.hh"
@@ -21,8 +22,6 @@
#include <string>
#include <pxr/base/tf/stringUtils.h>
#include "BKE_main.hh"
#include "BLI_assert.h"
@@ -78,7 +77,7 @@ void USDHierarchyIterator::release_writer(AbstractHierarchyWriter *writer)
std::string USDHierarchyIterator::make_valid_name(const std::string &name) const
{
return pxr::TfMakeValidIdentifier(name);
return make_safe_name(name, params_.allow_unicode);
}
void USDHierarchyIterator::process_usd_skel() const

View File

@@ -3,9 +3,9 @@
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "usd_reader_material.hh"
#include "usd_reader_utils.hh"
#include "usd_asset_utils.hh"
#include "usd_reader_utils.hh"
#include "usd_utils.hh"
#include "BKE_appdir.hh"
#include "BKE_image.h"
@@ -1359,7 +1359,7 @@ void build_material_map(const Main *bmain, blender::Map<std::string, Material *>
BLI_assert_msg(r_mat_map, "...");
LISTBASE_FOREACH (Material *, material, &bmain->materials) {
std::string usd_name = pxr::TfMakeValidIdentifier(material->id.name + 2);
std::string usd_name = make_safe_name(material->id.name + 2, true);
r_mat_map->lookup_or_add_default(usd_name) = material;
}
}

View File

@@ -11,6 +11,7 @@
#include "usd_mesh_utils.hh"
#include "usd_reader_material.hh"
#include "usd_skel_convert.hh"
#include "usd_utils.hh"
#include "BKE_attribute.hh"
#include "BKE_customdata.hh"
@@ -84,6 +85,7 @@ static void assign_materials(Main *bmain,
blender::Map<std::string, Material *> &mat_name_to_mat,
blender::Map<std::string, std::string> &usd_path_to_mat_name)
{
using namespace blender::io::usd;
if (!(stage && bmain && ob)) {
return;
}
@@ -92,7 +94,7 @@ static void assign_materials(Main *bmain,
return;
}
blender::io::usd::USDMaterialReader mat_reader(params, bmain);
USDMaterialReader mat_reader(params, bmain);
for (const auto item : mat_index_map.items()) {
Material *assigned_mat = blender::io::usd::find_existing_material(
@@ -120,10 +122,10 @@ static void assign_materials(Main *bmain,
continue;
}
const std::string mat_name = pxr::TfMakeValidIdentifier(assigned_mat->id.name + 2);
const std::string mat_name = make_safe_name(assigned_mat->id.name + 2, true);
mat_name_to_mat.lookup_or_add_default(mat_name) = assigned_mat;
if (params.mtl_name_collision_mode == blender::io::usd::USD_MTL_NAME_COLLISION_MAKE_UNIQUE) {
if (params.mtl_name_collision_mode == USD_MTL_NAME_COLLISION_MAKE_UNIQUE) {
/* Record the name of the Blender material we created for the USD material
* with the given path. */
usd_path_to_mat_name.lookup_or_add_default(item.key.GetAsString()) = mat_name;

View File

@@ -17,6 +17,7 @@
#include "usd_reader_skeleton.hh"
#include "usd_reader_volume.hh"
#include "usd_reader_xform.hh"
#include "usd_utils.hh"
#include <pxr/pxr.h>
#include <pxr/usd/usdGeom/camera.h>
@@ -566,7 +567,7 @@ void USDStageReader::import_all_materials(Main *bmain)
Material *new_mtl = mtl_reader.add_material(usd_mtl);
BLI_assert_msg(new_mtl, "Failed to create material");
const std::string mtl_name = pxr::TfMakeValidIdentifier(new_mtl->id.name + 2);
const std::string mtl_name = make_safe_name(new_mtl->id.name + 2, true);
settings_.mat_name_to_mat.lookup_or_add_default(mtl_name) = new_mtl;
if (params_.mtl_name_collision_mode == USD_MTL_NAME_COLLISION_MAKE_UNIQUE) {

View File

@@ -0,0 +1,52 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "usd_utils.hh"
#include "BLI_string_utf8.h"
#include <pxr/base/tf/stringUtils.h>
#if PXR_VERSION >= 2403
# include <pxr/base/tf/unicodeUtils.h>
#endif
namespace blender::io::usd {
std::string make_safe_name(const std::string &name, [[maybe_unused]] bool allow_unicode)
{
#if PXR_VERSION >= 2403
if (!allow_unicode) {
return pxr::TfMakeValidIdentifier(name);
}
if (name.empty()) {
return "_";
}
std::string buf;
buf.resize(name.size()); // We won't be exceeding the size of the incoming string
bool first = true;
size_t offset = 0;
for (auto cp : pxr::TfUtf8CodePointView{name}) {
constexpr pxr::TfUtf8CodePoint cp_underscore = pxr::TfUtf8CodePointFromAscii('_');
const bool cp_allowed = first ? (cp == cp_underscore || pxr::TfIsUtf8CodePointXidStart(cp)) :
pxr::TfIsUtf8CodePointXidContinue(cp);
if (!cp_allowed) {
offset += BLI_str_utf8_from_unicode(uint32_t('_'), buf.data() + offset, buf.size() - offset);
}
else {
offset += BLI_str_utf8_from_unicode(cp.AsUInt32(), buf.data() + offset, buf.size() - offset);
}
first = false;
}
return buf;
#else
return pxr::TfMakeValidIdentifier(name);
#endif
}
} // namespace blender::io::usd

View File

@@ -0,0 +1,19 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include <string>
namespace blender::io::usd {
/**
* Return a valid USD identifier based on the passed in string.
*
* \param name: Incoming name to sanitize
* \param allow_unicode: Whether to allow unicode encoded characters in the USD identifier
* \return A valid USD identifier
*/
std::string make_safe_name(const std::string &name, bool allow_unicode);
} // namespace blender::io::usd

View File

@@ -2,9 +2,9 @@
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "usd_writer_abstract.hh"
#include "usd_utils.hh"
#include "usd_writer_material.hh"
#include <pxr/base/tf/stringUtils.h>
#include <pxr/usd/usdGeom/bboxCache.h>
#include <pxr/usd/usdGeom/scope.h>
@@ -213,7 +213,8 @@ pxr::UsdShadeMaterial USDAbstractWriter::ensure_usd_material(const HierarchyCont
pxr::UsdStageRefPtr stage = usd_export_context_.stage;
/* Construct the material. */
pxr::TfToken material_name(pxr::TfMakeValidIdentifier(material->id.name + 2));
pxr::TfToken material_name(
make_safe_name(material->id.name + 2, usd_export_context_.export_params.allow_unicode));
pxr::SdfPath usd_path = pxr::UsdGeomScope::Define(stage, get_material_library_path())
.GetPath()
.AppendChild(material_name);
@@ -325,7 +326,8 @@ void USDAbstractWriter::write_user_properties(const pxr::UsdPrim &prim,
continue;
}
std::string prop_name = pxr::TfMakeValidIdentifier(prop->name);
std::string prop_name = make_safe_name(prop->name,
usd_export_context_.export_params.allow_unicode);
std::string full_prop_name = "userProperties:" + prop_name;
pxr::TfToken prop_token = pxr::TfToken(full_prop_name);

View File

@@ -48,7 +48,8 @@ static pxr::GfMatrix4d parent_relative_pose_mat(const bPoseChannel *pchan)
static void initialize(const Object *obj,
pxr::UsdSkelSkeleton &skel,
pxr::UsdSkelAnimation &skel_anim,
const blender::Map<blender::StringRef, const Bone *> *deform_bones)
const blender::Map<blender::StringRef, const Bone *> *deform_bones,
bool allow_unicode)
{
using namespace blender::io::usd;
@@ -68,7 +69,7 @@ static void initialize(const Object *obj,
return;
}
joints.push_back(build_usd_joint_path(bone));
joints.push_back(build_usd_joint_path(bone, allow_unicode));
const pxr::GfMatrix4f arm_mat(bone->arm_mat);
bind_xforms.push_back(pxr::GfMatrix4d(arm_mat));
@@ -97,7 +98,7 @@ static void initialize(const Object *obj,
if (skel_anim) {
usd_skel_api.CreateAnimationSourceRel().SetTargets(
pxr::SdfPathVector({pxr::SdfPath(usdtokens::Anim)}));
create_pose_joints(skel_anim, *obj, deform_bones);
create_pose_joints(skel_anim, *obj, deform_bones, allow_unicode);
}
}
@@ -166,13 +167,14 @@ void USDArmatureWriter::do_write(HierarchyContext &context)
}
}
const bool allow_unicode = usd_export_context_.export_params.allow_unicode;
Map<StringRef, const Bone *> *deform_map = usd_export_context_.export_params.only_deform_bones ?
&deform_map_ :
nullptr;
if (!this->frame_has_been_written_) {
init_deform_bones_map(context.object, deform_map);
initialize(context.object, skel, skel_anim, deform_map);
initialize(context.object, skel, skel_anim, deform_map, allow_unicode);
}
if (usd_export_context_.export_params.export_animation) {

View File

@@ -16,6 +16,7 @@
#include "usd_attribute_utils.hh"
#include "usd_hierarchy_iterator.hh"
#include "usd_utils.hh"
#include "usd_writer_curves.hh"
#include "BLI_array_utils.hh"
@@ -405,7 +406,8 @@ void USDCurvesWriter::write_generic_data(const bke::CurvesGeometry &curves,
}
const pxr::UsdTimeCode timecode = get_export_time_code();
const pxr::TfToken pv_name(pxr::TfMakeValidIdentifier(attribute_id.name()));
const pxr::TfToken pv_name(
make_safe_name(attribute_id.name(), usd_export_context_.export_params.allow_unicode));
const pxr::UsdGeomPrimvarsAPI pv_api = pxr::UsdGeomPrimvarsAPI(usd_curves);
pxr::UsdGeomPrimvar pv_attr = pv_api.CreatePrimvar(pv_name, *pv_type, *pv_interp);
@@ -425,7 +427,8 @@ void USDCurvesWriter::write_uv_data(const bke::CurvesGeometry &curves,
}
const pxr::UsdTimeCode timecode = get_export_time_code();
const pxr::TfToken pv_name(pxr::TfMakeValidIdentifier(attribute_id.name()));
const pxr::TfToken pv_name(
make_safe_name(attribute_id.name(), usd_export_context_.export_params.allow_unicode));
const pxr::UsdGeomPrimvarsAPI pv_api = pxr::UsdGeomPrimvarsAPI(usd_curves);
pxr::UsdGeomPrimvar pv_uv = pv_api.CreatePrimvar(

View File

@@ -3,9 +3,10 @@
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "usd_writer_material.hh"
#include "usd_exporter_context.hh"
#include "usd_hook.hh"
#include "usd_utils.hh"
#include "usd_writer_abstract.hh"
#include "BKE_image.h"
#include "BKE_image_format.h"
@@ -472,7 +473,8 @@ static void create_uvmap_shader(const USDExporterContext &usd_export_context,
if (uv_node && uv_node->storage) {
NodeShaderUVMap *shader_uv_map = static_cast<NodeShaderUVMap *>(uv_node->storage);
/* We need to make valid here because actual uv primvar has been. */
uv_name = pxr::TfMakeValidIdentifier(shader_uv_map->uv_map);
uv_name = make_safe_name(shader_uv_map->uv_map,
usd_export_context.export_params.allow_unicode);
}
uv_shader.CreateInput(usdtokens::varname, pxr::SdfValueTypeNames->String).Set(uv_name);
@@ -778,7 +780,7 @@ static pxr::UsdShadeShader create_usd_preview_shader(const USDExporterContext &u
const int type)
{
pxr::SdfPath shader_path = material.GetPath().AppendChild(
pxr::TfToken(pxr::TfMakeValidIdentifier(name)));
pxr::TfToken(make_safe_name(name, usd_export_context.export_params.allow_unicode)));
pxr::UsdShadeShader shader = pxr::UsdShadeShader::Define(usd_export_context.stage, shader_path);
switch (type) {

View File

@@ -7,6 +7,7 @@
#include "usd_attribute_utils.hh"
#include "usd_blend_shape_utils.hh"
#include "usd_skel_convert.hh"
#include "usd_utils.hh"
#include <pxr/usd/usdGeom/mesh.h>
#include <pxr/usd/usdGeom/primvarsAPI.h>
@@ -261,7 +262,8 @@ void USDGenericMeshWriter::write_generic_data(const Mesh *mesh,
}
const pxr::UsdTimeCode timecode = get_export_time_code();
const pxr::TfToken pv_name(pxr::TfMakeValidIdentifier(attribute_id.name()));
const pxr::TfToken pv_name(
make_safe_name(attribute_id.name(), usd_export_context_.export_params.allow_unicode));
const pxr::UsdGeomPrimvarsAPI pv_api = pxr::UsdGeomPrimvarsAPI(usd_mesh);
pxr::UsdGeomPrimvar pv_attr = pv_api.CreatePrimvar(pv_name, *pv_type, *pv_interp);
@@ -282,7 +284,8 @@ void USDGenericMeshWriter::write_uv_data(const Mesh *mesh,
}
const pxr::UsdTimeCode timecode = get_export_time_code();
const pxr::TfToken pv_name(pxr::TfMakeValidIdentifier(attribute_id.name()));
const pxr::TfToken pv_name(
make_safe_name(attribute_id.name(), usd_export_context_.export_params.allow_unicode));
const pxr::UsdGeomPrimvarsAPI pv_api = pxr::UsdGeomPrimvarsAPI(usd_mesh);
pxr::UsdGeomPrimvar pv_uv = pv_api.CreatePrimvar(
@@ -303,7 +306,8 @@ void USDGenericMeshWriter::write_color_data(const Mesh *mesh,
}
const pxr::UsdTimeCode timecode = get_export_time_code();
const pxr::TfToken pv_name(pxr::TfMakeValidIdentifier(attribute_id.name()));
const pxr::TfToken pv_name(
make_safe_name(attribute_id.name(), usd_export_context_.export_params.allow_unicode));
const pxr::UsdGeomPrimvarsAPI pv_api = pxr::UsdGeomPrimvarsAPI(usd_mesh);
/* Varying type depends on original domain. */
@@ -876,7 +880,10 @@ void USDMeshWriter::init_blend_shapes(const HierarchyContext &context)
return;
}
create_blend_shapes(this->usd_export_context_.stage, context.object, mesh_prim);
create_blend_shapes(this->usd_export_context_.stage,
context.object,
mesh_prim,
usd_export_context_.export_params.allow_unicode);
}
void USDMeshWriter::do_write(HierarchyContext &context)

View File

@@ -4,6 +4,7 @@
#include "usd_writer_volume.hh"
#include "usd_hierarchy_iterator.hh"
#include "usd_utils.hh"
#include <pxr/base/tf/pathUtils.h>
#include <pxr/usd/usdVol/openVDBAsset.h>
@@ -73,7 +74,8 @@ void USDVolumeWriter::do_write(HierarchyContext &context)
for (const int i : IndexRange(num_grids)) {
const bke::VolumeGridData *grid = BKE_volume_grid_get(volume, i);
const std::string grid_name = bke::volume_grid::get_name(*grid);
const std::string grid_id = pxr::TfMakeValidIdentifier(grid_name);
const std::string grid_id = make_safe_name(grid_name,
usd_export_context_.export_params.allow_unicode);
const pxr::SdfPath grid_path = volume_path.AppendPath(pxr::SdfPath(grid_id));
pxr::UsdVolOpenVDBAsset usd_grid = pxr::UsdVolOpenVDBAsset::Define(stage, grid_path);
usd_grid.GetFieldNameAttr().Set(pxr::TfToken(grid_name), timecode);

View File

@@ -129,9 +129,11 @@ struct USDExportParams {
bool export_cameras = true;
bool export_curves = true;
bool export_volumes = true;
eUSDZTextureDownscaleSize usdz_downscale_size = eUSDZTextureDownscaleSize::USD_TEXTURE_SIZE_KEEP;
int usdz_downscale_custom_size = 128;
bool allow_unicode = false;
char root_prim_path[1024] = ""; /* FILE_MAX */
char collection[MAX_IDPROP_NAME] = "";