USD export: Skel Root validation.
Added function for verifying that skinned prims and skeletons are properly grouped under a common SkelRoot. Also added a Fix Skel Root export option to attempt to fix the hierarchy if the Skel Root is invalid.
This commit is contained in:
parent
8ef0925c83
commit
8ca67ef025
|
@ -313,6 +313,8 @@ static int wm_usd_export_exec(bContext *C, wmOperator *op)
|
|||
|
||||
const eUSDXformOpMode xform_op_mode = RNA_enum_get(op->ptr, "xform_op_mode");
|
||||
|
||||
const bool fix_skel_root = RNA_boolean_get(op->ptr, "fix_skel_root");;
|
||||
|
||||
struct USDExportParams params = {RNA_int_get(op->ptr, "start"),
|
||||
RNA_int_get(op->ptr, "end"),
|
||||
export_animation,
|
||||
|
@ -366,7 +368,8 @@ static int wm_usd_export_exec(bContext *C, wmOperator *op)
|
|||
convert_world_material,
|
||||
generate_cycles_shaders,
|
||||
export_armatures,
|
||||
xform_op_mode};
|
||||
xform_op_mode,
|
||||
fix_skel_root};
|
||||
|
||||
/* Take some defaults from the scene, if not specified explicitly. */
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
|
@ -513,6 +516,7 @@ static void wm_usd_export_draw(bContext *C, wmOperator *op)
|
|||
box = uiLayoutBox(layout);
|
||||
uiItemL(box, IFACE_("Experimental:"), ICON_NONE);
|
||||
uiItemR(box, ptr, "use_instancing", 0, NULL, ICON_NONE);
|
||||
uiItemR(box, ptr, "fix_skel_root", 0, NULL, ICON_NONE);
|
||||
}
|
||||
|
||||
void WM_OT_usd_export(struct wmOperatorType *ot)
|
||||
|
@ -648,6 +652,13 @@ void WM_OT_usd_export(struct wmOperatorType *ot)
|
|||
"When checked, instanced objects are exported as references in USD. "
|
||||
"When unchecked, instanced objects are exported as real objects");
|
||||
|
||||
RNA_def_boolean(ot->srna,
|
||||
"fix_skel_root",
|
||||
false,
|
||||
"Fix Skel Root",
|
||||
"If exporting armatures, attempt to automatically "
|
||||
"correct invalid USD Skel Root hierarchies");
|
||||
|
||||
RNA_def_enum(ot->srna,
|
||||
"evaluation_mode",
|
||||
rna_enum_usd_export_evaluation_mode_items,
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include "usd_light_convert.h"
|
||||
#include "usd_umm.h"
|
||||
#include "usd_writer_material.h"
|
||||
#include "usd_writer_skel_root.h"
|
||||
|
||||
#include <pxr/base/plug/registry.h>
|
||||
#include <pxr/pxr.h>
|
||||
|
@ -336,6 +337,10 @@ static void export_startjob(void *customdata,
|
|||
|
||||
iter.release_writers();
|
||||
|
||||
if (data->params.export_armatures) {
|
||||
validate_skel_roots(usd_stage, data->params);
|
||||
}
|
||||
|
||||
// Set Stage Default Prim Path
|
||||
if (strlen(data->params.default_prim_path) > 0) {
|
||||
std::string valid_default_prim_path = pxr::TfMakeValidIdentifier(
|
||||
|
|
|
@ -18,8 +18,14 @@
|
|||
*/
|
||||
#include "usd_writer_skel_root.h"
|
||||
|
||||
#include "WM_api.h"
|
||||
|
||||
#include <pxr/usd/usd/primRange.h>
|
||||
#include <pxr/usd/usdSkel/bindingAPI.h>
|
||||
#include <pxr/usd/usdSkel/root.h>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace blender::io::usd {
|
||||
|
||||
bool USDSkelRootWriter::is_under_skel_root() const
|
||||
|
@ -34,14 +40,13 @@ bool USDSkelRootWriter::is_under_skel_root() const
|
|||
|
||||
pxr::UsdPrim prim = usd_export_context_.stage->GetPrimAtPath(parent_path);
|
||||
|
||||
while (prim.IsValid()) {
|
||||
if (prim.IsA<pxr::UsdSkelRoot>()) {
|
||||
return true;
|
||||
}
|
||||
prim = prim.GetParent();
|
||||
if (!prim.IsValid()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
pxr::UsdSkelRoot root = pxr::UsdSkelRoot::Find(prim);
|
||||
|
||||
return static_cast<bool>(root);
|
||||
}
|
||||
|
||||
pxr::UsdGeomXformable USDSkelRootWriter::create_xformable() const
|
||||
|
@ -67,4 +72,107 @@ pxr::UsdGeomXformable USDSkelRootWriter::create_xformable() const
|
|||
return root;
|
||||
}
|
||||
|
||||
static pxr::UsdGeomXform get_xform_ancestor(const pxr::UsdPrim &prim1,
|
||||
const pxr::UsdPrim &prim2)
|
||||
{
|
||||
if (!prim1 || !prim2) {
|
||||
return pxr::UsdGeomXform();
|
||||
}
|
||||
|
||||
pxr::SdfPath prefix = prim1.GetPath().GetCommonPrefix(prim2.GetPath());
|
||||
|
||||
if (prefix.IsEmpty()) {
|
||||
return pxr::UsdGeomXform();
|
||||
}
|
||||
|
||||
pxr::UsdPrim ancestor = prim1.GetStage()->GetPrimAtPath(prefix);
|
||||
|
||||
if (!ancestor.IsA<pxr::UsdGeomXform>()) {
|
||||
ancestor = ancestor.GetParent();
|
||||
}
|
||||
|
||||
if (ancestor.IsA<pxr::UsdGeomXform>()) {
|
||||
return pxr::UsdGeomXform(ancestor);
|
||||
}
|
||||
|
||||
return pxr::UsdGeomXform();
|
||||
}
|
||||
|
||||
void validate_skel_roots(pxr::UsdStageRefPtr stage, const USDExportParams ¶ms)
|
||||
{
|
||||
if (!params.export_armatures || !stage) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool created_skel_root = false;
|
||||
|
||||
pxr::UsdPrimRange it = stage->Traverse();
|
||||
for (pxr::UsdPrim prim : it) {
|
||||
if (prim.HasAPI<pxr::UsdSkelBindingAPI>() && !prim.IsA<pxr::UsdSkelSkeleton>()) {
|
||||
|
||||
pxr::UsdSkelBindingAPI skel_bind_api(prim);
|
||||
if (skel_bind_api) {
|
||||
pxr::UsdSkelSkeleton skel;
|
||||
if (skel_bind_api.GetSkeleton(&skel)) {
|
||||
|
||||
if (!skel.GetPrim().IsValid()) {
|
||||
std::cout << "WARNING in validate_skel_roots(): invalid skeleton for prim " << prim.GetPath() << std::endl;
|
||||
continue;
|
||||
}
|
||||
|
||||
pxr::UsdSkelRoot prim_root = pxr::UsdSkelRoot::Find(prim);
|
||||
pxr::UsdSkelRoot arm_root = pxr::UsdSkelRoot::Find(skel.GetPrim());
|
||||
|
||||
bool common_root = false;
|
||||
|
||||
if (prim_root && arm_root && prim_root.GetPath() == arm_root.GetPath()) {
|
||||
common_root = true;
|
||||
}
|
||||
|
||||
if (!common_root) {
|
||||
WM_reportf(RPT_WARNING, "USD Export: skinned prim %s and skeleton %s do not share a common SkelRoot and may not bind correctly. See the documentation for possible solutions.\n",
|
||||
prim.GetPath().GetAsString().c_str(), skel.GetPrim().GetPath().GetAsString().c_str());
|
||||
std::cout << "WARNING: skinned prim " << prim.GetPath() << " and skeleton " << skel.GetPrim().GetPath()
|
||||
<< " do not share a common SkelRoot and may not bind correctly. See the documentation for possible solutions." << std::endl;
|
||||
|
||||
if (params.fix_skel_root) {
|
||||
std::cout << "Attempting to fix the Skel Root hierarchy." << std::endl;
|
||||
WM_reportf(RPT_WARNING, "Attempting to fix the Skel Root hierarchy. See the console for information");
|
||||
|
||||
if (pxr::UsdGeomXform xf = get_xform_ancestor(prim, skel.GetPrim())) {
|
||||
/* Enable skeletal processing by setting the type to UsdSkelRoot. */
|
||||
std::cout << "Converting Xform prim " << xf.GetPath() << " to a SkelRoot" << std::endl;
|
||||
|
||||
pxr::UsdSkelRoot::Define(stage, xf.GetPath());
|
||||
created_skel_root = true;
|
||||
}
|
||||
else {
|
||||
std::cout << "Couldn't find a commone Xform ancestor for skinned prim " << prim.GetPath()
|
||||
<< " and skeleton " << skel.GetPrim().GetPath() << " to convert to a USDSkelRoot\n";
|
||||
std::cout << "You might wish to group these objects under an Empty in the Blender scene.\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!created_skel_root) {
|
||||
return;
|
||||
}
|
||||
|
||||
it = stage->Traverse();
|
||||
for (pxr::UsdPrim prim : it) {
|
||||
if (prim.IsA<pxr::UsdSkelRoot>()) {
|
||||
if (pxr::UsdSkelRoot root = pxr::UsdSkelRoot::Find(prim.GetParent())) {
|
||||
/* This is a nested SkelRoot, so convert it to an Xform. */
|
||||
std::cout << "Converting nested SkelRoot " << prim.GetPath() << " to an Xform." << std::endl;
|
||||
pxr::UsdGeomXform::Define(stage, prim.GetPath());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace blender::io::usd
|
||||
|
|
|
@ -24,6 +24,8 @@
|
|||
|
||||
namespace blender::io::usd {
|
||||
|
||||
void validate_skel_roots(pxr::UsdStageRefPtr stage, const USDExportParams ¶ms);
|
||||
|
||||
class USDSkelRootWriter : public USDTransformWriter {
|
||||
|
||||
public:
|
||||
|
|
|
@ -132,6 +132,7 @@ struct USDExportParams {
|
|||
bool generate_cycles_shaders;
|
||||
bool export_armatures;
|
||||
eUSDXformOpMode xform_op_mode;
|
||||
bool fix_skel_root;
|
||||
};
|
||||
|
||||
struct USDImportParams {
|
||||
|
|
Loading…
Reference in New Issue