[Bf-blender-cvs] [8ca67ef025c] universal-scene-description: USD export: Skel Root validation.

Michael Kowalski noreply at git.blender.org
Tue Dec 7 18:53:51 CET 2021


Commit: 8ca67ef025c097f05478e7005eeb387bd1bb0c4b
Author: Michael Kowalski
Date:   Tue Dec 7 12:42:42 2021 -0500
Branches: universal-scene-description
https://developer.blender.org/rB8ca67ef025c097f05478e7005eeb387bd1bb0c4b

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.

===================================================================

M	source/blender/editors/io/io_usd.c
M	source/blender/io/usd/intern/usd_capi_export.cc
M	source/blender/io/usd/intern/usd_writer_skel_root.cc
M	source/blender/io/usd/intern/usd_writer_skel_root.h
M	source/blender/io/usd/usd.h

===================================================================

diff --git a/source/blender/editors/io/io_usd.c b/source/blender/editors/io/io_usd.c
index dd5f28e253a..0a1441b3dd1 100644
--- a/source/blender/editors/io/io_usd.c
+++ b/source/blender/editors/io/io_usd.c
@@ -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,
diff --git a/source/blender/io/usd/intern/usd_capi_export.cc b/source/blender/io/usd/intern/usd_capi_export.cc
index a7c1f57095c..3a5c191131b 100644
--- a/source/blender/io/usd/intern/usd_capi_export.cc
+++ b/source/blender/io/usd/intern/usd_capi_export.cc
@@ -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(
diff --git a/source/blender/io/usd/intern/usd_writer_skel_root.cc b/source/blender/io/usd/intern/usd_writer_skel_root.cc
index ee241b9d18c..ea9dcde5570 100644
--- a/source/blender/io/usd/intern/usd_writer_skel_root.cc
+++ b/source/blender/io/usd/intern/usd_writer_skel_root.cc
@@ -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 &params)
+{
+  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
diff --git a/source/blender/io/usd/intern/usd_writer_skel_root.h b/source/blender/io/usd/intern/usd_writer_skel_root.h
index 7a33721a79e..4686374bb40 100644
--- a/source/blender/io/usd/intern/usd_writer_skel_root.h
+++ b/source/blender/io/usd/intern/usd_writer_skel_root.h
@@ -24,6 +24,8 @@
 
 namespace blender::io::usd {
 
+void validate_skel_roots(pxr::UsdStageRefPtr stage, const USDExportParams &params);
+
 class USDSkelRootWriter : public USDTransformWriter {
 
  public:
diff --git a/source/blender/io/usd/usd.h b/source/blender/io/usd/usd.h
index 99e99ab34d6..c0441e2eace 100644
--- a/source/blender/io/usd/usd.h
+++ b/source/blender/io/usd/usd.h
@@ -132,6 +132,7 @@ struct USDExportParams {
   bool generate_cycles_shaders;
   bool export_armatures;
   eUSDXformOpMode xform_op_mode;
+  bool fix_skel_root;
 };
 
 struct USDImportParams {



More information about the Bf-blender-cvs mailing list