diff --git a/src/tydra/render-data.cc b/src/tydra/render-data.cc index bacf47e7..f095b95d 100644 --- a/src/tydra/render-data.cc +++ b/src/tydra/render-data.cc @@ -11,6 +11,7 @@ // - [ ] linear sRGB <-> linear DisplayP3 // - [x] Compute tangentes and binormals // - [x] displayColor, displayOpacity primvar(vertex color) +// - [ ] Support SkelAnimation and Skeleton // - [ ] Support Inbetween BlendShape // - [ ] Support material binding collection(Collection API) // - [ ] Support multiple skel animation @@ -3578,6 +3579,7 @@ bool RenderSceneConverter::ConvertMesh( if (skelPath.is_valid()) { // TODO + // GetSkeletonImpl } } diff --git a/src/tydra/render-data.hh b/src/tydra/render-data.hh index 398c193c..5820d97c 100644 --- a/src/tydra/render-data.hh +++ b/src/tydra/render-data.hh @@ -1721,7 +1721,7 @@ class RenderSceneConverter { /// Convert SkelAnimation to Tydra Animation. /// /// @param[in] abs_path USD Path to SkelAnimation Prim - /// @param[in] skelAnim SkelAnimation + /// @param[in] skelAnim SkelAnimatio /// @param[in] anim_out UVTexture /// bool ConvertSkelAnimation(const RenderSceneConverterEnv &env, diff --git a/src/tydra/scene-access.cc b/src/tydra/scene-access.cc index c5500a6c..773d83ca 100644 --- a/src/tydra/scene-access.cc +++ b/src/tydra/scene-access.cc @@ -2408,7 +2408,7 @@ bool GetGeomPrimvar(const Stage &stage, const GPrim *gprim, const std::string &v constexpr auto kPrimvars = "primvars:"; constexpr auto kIndices = ":indices"; - + std::string primvar_name = kPrimvars + varname; const auto it = gprim->props.find(primvar_name); @@ -2420,7 +2420,7 @@ bool GetGeomPrimvar(const Stage &stage, const GPrim *gprim, const std::string &v const Attribute &attr = it->second.get_attribute(); if (attr.is_connection()) { - // follow targetPath to get Attribute + // follow targetPath to get Attribute Attribute terminal_attr; bool ret = tydra::GetTerminalAttribute(stage, attr, primvar_name, &terminal_attr, err); if (!ret) { @@ -2461,7 +2461,7 @@ bool GetGeomPrimvar(const Stage &stage, const GPrim *gprim, const std::string &v } if (indexAttr.is_connection()) { - // follow targetPath to get Attribute + // follow targetPath to get Attribute Attribute terminal_indexAttr; bool ret = tydra::GetTerminalAttribute(stage, indexAttr, index_name, &terminal_indexAttr, err); if (!ret) { @@ -2474,7 +2474,7 @@ bool GetGeomPrimvar(const Stage &stage, const GPrim *gprim, const std::string &v if (!tss.from_timesamples(ts)) { PUSH_ERROR_AND_RETURN(fmt::format("Index Attribute seems not an timesamples with int[] type: {}", index_name)); } - + primvar.set_indices(tss); } else if (terminal_indexAttr.is_value()) { @@ -2489,14 +2489,14 @@ bool GetGeomPrimvar(const Stage &stage, const GPrim *gprim, const std::string &v primvar.set_indices(indices); } - + } else if (indexAttr.is_timesamples()) { const auto &ts = indexAttr.get_var().ts_raw(); TypedTimeSamples> tss; if (!tss.from_timesamples(ts)) { PUSH_ERROR_AND_RETURN(fmt::format("Index Attribute seems not an timesamples with int[] type: {}", index_name)); } - + primvar.set_indices(tss); } else if (indexAttr.is_blocked()) { // Value blocked. e.g. `float2[] primvars:st:indices = None` @@ -2597,7 +2597,7 @@ bool GetTerminalAttributeImpl( } else if (prop.is_attribute()) { (*value) = prop.get_attribute(); - + } else { // ??? PUSH_ERROR_AND_RETURN( @@ -2657,7 +2657,7 @@ bool GetTerminalAttribute( return GetTerminalAttributeImpl(stage, *targetPrim, targetPrimPropName, value, err, visited_paths); - + } else { PUSH_ERROR_AND_RETURN(targetPrimRet.error()); } @@ -2671,6 +2671,121 @@ bool GetTerminalAttribute( } +namespace detail { + +bool BuildSkelHierarchyImpl(/* inout */std::set &visitSet, const int parentJointId, const std::vector &parentJointIds, const std::vector &jointNames, const std::vector bindTransforms, const std::vector &restTransforms, SkelNode &node, std::string *err = nullptr) { + + // Simple linear search + for(size_t i = 0; i < parentJointIds.size(); i++) { + if (visitSet.count(i)) { + continue; + } + + int parentJointIdOfCurrIdx = parentJointIds[i]; + if (parentJointId == parentJointIdOfCurrIdx) { + } + } + + return true; +} + +} // namespace detail + +bool BuildSkelHierarchy(const Skeleton &skel, + SkelNode &dst, std::string *err) { + + if (!skel.joints.authored()) { + PUSH_ERROR_AND_RETURN(fmt::format("Skeleton.joints attrbitue is not authored: {}", skel.name)); + } + + if (!skel.jointNames.authored()) { + PUSH_ERROR_AND_RETURN(fmt::format("Skeleton.jointNames attrbitue is not authored: {}", skel.name)); + } + + std::vector joints; + if (!skel.joints.get_value(&joints)) { + PUSH_ERROR_AND_RETURN(fmt::format("Failed to get Skeleton.joints attrbitue: {}", skel.name)); + } + + if (joints.empty()) { + PUSH_ERROR_AND_RETURN(fmt::format("Skeleton.joints attrbitue is empty: {}", skel.name)); + } + + std::vector jointNames; + if (!skel.jointNames.get_value(&jointNames)) { + PUSH_ERROR_AND_RETURN(fmt::format("Failed to get Skeleton.jointNames attrbitue: {}", skel.name)); + } + + if (joints.size() != jointNames.size()) { + PUSH_ERROR_AND_RETURN(fmt::format("Skeleton.joints.size {} must be equal to Skeleton.jointNames.size {}: {}", joints.size(), jointNames.size(), skel.name)); + } + + std::vector restTransforms; + if (skel.restTransforms.authored()) { + if (!skel.restTransforms.get_value(&restTransforms)) { + PUSH_ERROR_AND_RETURN(fmt::format("Failed to get Skeleton.restTransforms attrbitue: {}", skel.name)); + } + } else { + // TODO: Report error when `restTransforms` attribute is omitted? + restTransforms.assign(joints.size(), value::matrix4d::identity()); + } + + if (joints.size() != restTransforms.size()) { + PUSH_ERROR_AND_RETURN(fmt::format("Skeleton.joints.size {} must be equal to Skeleton.restTransforms.size {}: {}", joints.size(), restTransforms.size(), skel.name)); + } + + + std::vector bindTransforms; + if (skel.bindTransforms.authored()) { + if (!skel.bindTransforms.get_value(&bindTransforms)) { + PUSH_ERROR_AND_RETURN(fmt::format("Failed to get Skeleton.bindTransforms attrbitue: {}", skel.name)); + } + } else { + // TODO: Report error when `restTransforms` attribute is omitted? + restTransforms.assign(joints.size(), value::matrix4d::identity()); + } + + if (joints.size() != bindTransforms.size()) { + PUSH_ERROR_AND_RETURN(fmt::format("Skeleton.joints.size {} must be equal to Skeleton.bindTransforms.size {}: {}", joints.size(), bindTransforms.size(), skel.name)); + } + + // Get flattened representation of joint hierarchy with BuildSkelTopology. + // For root node, parentJointId = -1. + std::vector parentJointIds; + if (!BuildSkelTopology(joints, parentJointIds, err)) { + return false; + } + + // Just in case. Chek if topology is single-rooted. + auto nroots = std::count_if(parentJointIds.begin(), parentJointIds.end(), [](int x) { + return x == -1; + }); + + if (nroots == 0) { + PUSH_ERROR_AND_RETURN(fmt::format("Invalid Skel topology. No root joint found: {}", skel.name)); + } + + if (nroots != 1) { + PUSH_ERROR_AND_RETURN(fmt::format("Invalid Skel topology. Topology must be single-rooted, but it has {} roots: {}", nroots, skel.name)); + } + + + std::set visitSet; + + SkelNode root; + // Construct hierachy from flattened id array. + if (!detail::BuildSkelHierarchyImpl(visitSet, -1, parentJointIds, jointNames, bindTransforms, restTransforms, root, err)){ + return false; + } + + dst = root; + + return true; +} + + +} + } // namespace tydra } // namespace tinyusdz diff --git a/src/tydra/scene-access.hh b/src/tydra/scene-access.hh index cc6a5cf9..e23ef827 100644 --- a/src/tydra/scene-access.hh +++ b/src/tydra/scene-access.hh @@ -517,46 +517,38 @@ bool IsPathIncluded(const CollectionMembershipQuery &query, const Stage &stage, // struct SkelNode { - std::string jointElementName; // elementName(leaf node name) of jointPath. + //std::string jointElementName; // elementName(leaf node name) of jointPath. std::string jointPath; // joints in UsdSkel. Relative or Absolute Prim // path(e.g. "root/head", "/root/head") std::string jointName; // jointNames in UsdSkel int jointId{-1}; // jointIndex(array index in UsdSkel joints) + value::matrix4d bindTransform{value::matrix4d::identity()}; value::matrix4d restTransform{value::matrix4d::identity()}; - int parentNodeIndex{-1}; + //int parentNodeIndex{-1}; - std::vector childNodeIndices; + std::vector children; }; class SkelHierarchy { public: SkelHierarchy() = default; - const std::string &name() const { return _name; } + std::string prim_name; // Skeleleton Prim name + std::string abs_path; // Absolute path to Skeleleton Prim + std::string display_name; // `displayName` Prim meta - bool get_root(SkelNode &dst) { - if (_skel_nodes.empty()) { - _err += "SkelNode is Empty\n"; - return false; - } - - dst = _skel_nodes[0]; - return true; - } + SkelNode root_node; private: - std::string _warm; - std::string _err; - std::string _name; // Skeleleton Prim name - std::vector _skel_nodes; // [0] = root node. + }; /// /// Extract skeleleton info from Skeleton and build skeleton(bone) hierarchy. /// -bool BuildSkelHierarchy(const Stage &stage, const Skeleton &skel, - SkelHierarchy &dst, std::string *err = nullptr); +bool BuildSkelHierarchy(const Skeleton &skel, + SkelNode &dst, std::string *err = nullptr); // // For USDZ AR extensions