USD Export: Added option to specify types of XForm ops written.

Different pipelines standardize on different XForm op setups (T-R-S,
T-orient quat-S, Matrix).  Having this options means that this standard
can be chosen at export time instead of having to patch on load.

Speaking from experience, this is a very helpful option.

Co-authored-by: kiki <charles@skeletalstudios.com>
Pull Request: https://projects.blender.org/blender/blender/pulls/121627
This commit is contained in:
Charles Wardlaw
2024-05-21 22:33:32 +02:00
committed by Jesse Yurkovich
parent ac33b9f693
commit 36f1a4f94f
4 changed files with 116 additions and 9 deletions

View File

@@ -129,6 +129,21 @@ const EnumPropertyItem rna_enum_usd_export_subdiv_mode_items[] = {
{0, nullptr, 0, nullptr, nullptr},
};
const EnumPropertyItem rna_enum_usd_xform_op_mode_items[] = {
{USD_XFORM_OP_TRS,
"TRS",
0,
"Translate, Rotate, Scale",
"Export with translate, rotate, and scale Xform operators"},
{USD_XFORM_OP_TOS,
"TOS",
0,
"Translate, Orient, Scale",
"Export with translate, orient quaternion, and scale Xform operators"},
{USD_XFORM_OP_MAT, "MAT", 0, "Matrix", "Export matrix operator"},
{0, nullptr, 0, nullptr, nullptr},
};
/* Stored in the wmOperator's customdata field to indicate it should run as a background job.
* This is set when the operator is invoked, and not set when it is only executed. */
enum { AS_BACKGROUND_JOB = 1 };
@@ -215,6 +230,8 @@ static int wm_usd_export_exec(bContext *C, wmOperator *op)
const int global_forward = RNA_enum_get(op->ptr, "export_global_forward_selection");
const int global_up = RNA_enum_get(op->ptr, "export_global_up_selection");
const eUSDXformOpMode xform_op_mode = eUSDXformOpMode(RNA_enum_get(op->ptr, "xform_op_mode"));
char root_prim_path[FILE_MAX];
RNA_string_get(op->ptr, "root_prim_path", root_prim_path);
process_prim_path(root_prim_path);
@@ -243,6 +260,7 @@ static int wm_usd_export_exec(bContext *C, wmOperator *op)
convert_orientation,
eIOAxis(global_forward),
eIOAxis(global_up),
xform_op_mode,
};
STRNCPY(params.root_prim_path, root_prim_path);
@@ -269,6 +287,9 @@ static void wm_usd_export_draw(bContext *C, wmOperator *op)
uiItemR(col, ptr, "visible_objects_only", UI_ITEM_NONE, nullptr, ICON_NONE);
}
col = uiLayoutColumn(box, true);
uiItemR(col, ptr, "xform_op_mode", UI_ITEM_NONE, nullptr, ICON_NONE);
col = uiLayoutColumn(box, true);
uiItemR(col, ptr, "export_animation", UI_ITEM_NONE, nullptr, ICON_NONE);
uiItemR(col, ptr, "export_hair", UI_ITEM_NONE, nullptr, ICON_NONE);
@@ -523,6 +544,13 @@ void WM_OT_usd_export(wmOperatorType *ot)
"Use relative paths to reference external files (i.e. textures, volumes) in "
"USD, otherwise use absolute paths");
RNA_def_enum(ot->srna,
"xform_op_mode",
rna_enum_usd_xform_op_mode_items,
USD_XFORM_OP_TRS,
"Xform Ops",
"The type of transform operators to write");
RNA_def_string(ot->srna,
"root_prim_path",
"/root",

View File

@@ -13,6 +13,7 @@
#include "BLI_math_matrix.h"
#include "BLI_math_rotation.h"
#include "BLI_string.h"
#include "BLI_vector.hh"
#include "CLG_log.h"
static CLG_LogRef LOG = {"io.usd"};
@@ -99,12 +100,7 @@ void USDTransformWriter::do_write(HierarchyContext &context)
/* USD Xforms are by default set with an identity transform; only write if necessary. */
if (!compare_m4m4(parent_relative_matrix, UNIT_M4, 0.000000001f)) {
if (!xformOp_) {
xformOp_ = xform.AddTransformOp();
}
pxr::GfMatrix4d mat_val(parent_relative_matrix);
usd_value_writer_.SetAttribute(xformOp_.GetAttr(), mat_val, get_export_time_code());
set_xform_ops(parent_relative_matrix, xform);
}
if (context.object) {
@@ -127,4 +123,82 @@ bool USDTransformWriter::check_is_animated(const HierarchyContext &context) cons
return BKE_object_moves_in_time(context.object, context.animation_check_include_parent);
}
void USDTransformWriter::set_xform_ops(float xf_matrix[4][4], pxr::UsdGeomXformable &xf)
{
if (!xf) {
return;
}
eUSDXformOpMode xfOpMode = usd_export_context_.export_params.xform_op_mode;
blender::Vector<pxr::UsdGeomXformOp> xformOps;
switch (xfOpMode) {
case USD_XFORM_OP_TRS:
xformOps.append(xf.AddTranslateOp());
xformOps.append(xf.AddRotateXYZOp());
xformOps.append(xf.AddScaleOp());
break;
case USD_XFORM_OP_TOS:
xformOps.append(xf.AddTranslateOp());
xformOps.append(xf.AddOrientOp());
xformOps.append(xf.AddScaleOp());
break;
case USD_XFORM_OP_MAT:
xformOps.append(xf.AddTransformOp());
break;
default:
CLOG_WARN(&LOG, "Warning: unknown XformOp type\n");
xformOps.append(xf.AddTransformOp());
break;
}
if (xformOps.is_empty()) {
/* Shouldn't happen. */
return;
}
pxr::UsdTimeCode time_code = get_export_time_code();
if (xformOps.size() == 1) {
pxr::GfMatrix4d mat_val(xf_matrix);
usd_value_writer_.SetAttribute(xformOps[0].GetAttr(), mat_val, time_code);
}
else if (xformOps.size() == 3) {
float loc[3];
float quat[4];
float scale[3];
mat4_decompose(loc, quat, scale, xf_matrix);
if (xfOpMode == USD_XFORM_OP_TRS) {
float rot[3];
quat_to_eul(rot, quat);
rot[0] *= 180.0 / M_PI;
rot[1] *= 180.0 / M_PI;
rot[2] *= 180.0 / M_PI;
pxr::GfVec3d loc_val(loc);
usd_value_writer_.SetAttribute(xformOps[0].GetAttr(), loc_val, time_code);
pxr::GfVec3f rot_val(rot);
usd_value_writer_.SetAttribute(xformOps[1].GetAttr(), rot_val, time_code);
pxr::GfVec3f scale_val(scale);
usd_value_writer_.SetAttribute(xformOps[2].GetAttr(), scale_val, time_code);
}
else if (xfOpMode == USD_XFORM_OP_TOS) {
pxr::GfVec3d loc_val(loc);
usd_value_writer_.SetAttribute(xformOps[0].GetAttr(), loc_val, time_code);
pxr::GfQuatf quat_val(quat[0], quat[1], quat[2], quat[3]);
usd_value_writer_.SetAttribute(xformOps[1].GetAttr(), quat_val, time_code);
pxr::GfVec3f scale_val(scale);
usd_value_writer_.SetAttribute(xformOps[2].GetAttr(), scale_val, time_code);
}
}
}
} // namespace blender::io::usd

View File

@@ -10,9 +10,6 @@
namespace blender::io::usd {
class USDTransformWriter : public USDAbstractWriter {
private:
pxr::UsdGeomXformOp xformOp_;
public:
USDTransformWriter(const USDExporterContext &ctx);
@@ -20,6 +17,7 @@ class USDTransformWriter : public USDAbstractWriter {
void do_write(HierarchyContext &context) override;
bool check_is_animated(const HierarchyContext &context) const override;
bool should_apply_root_xform(const HierarchyContext &context) const;
void set_xform_ops(float parent_relative_matrix[4][4], pxr::UsdGeomXformable &xf);
/* Subclasses may override this to create prims other than UsdGeomXform. */
virtual pxr::UsdGeomXformable create_xformable() const;

View File

@@ -78,6 +78,12 @@ enum eSubdivExportMode {
USD_SUBDIV_BEST_MATCH = 2,
};
typedef enum eUSDXformOpMode {
USD_XFORM_OP_TRS = 0,
USD_XFORM_OP_TOS = 1,
USD_XFORM_OP_MAT = 2,
} eUSDXformOpMode;
struct USDExportParams {
bool export_animation = false;
bool export_hair = true;
@@ -102,6 +108,7 @@ struct USDExportParams {
bool convert_orientation = false;
enum eIOAxis forward_axis = eIOAxis::IO_AXIS_NEGATIVE_Z;
enum eIOAxis up_axis = eIOAxis::IO_AXIS_Y;
eUSDXformOpMode xform_op_mode = eUSDXformOpMode::USD_XFORM_OP_TRS;
char root_prim_path[1024] = ""; /* FILE_MAX */
char collection[MAX_IDPROP_NAME] = "";