diff --git a/src/ObjCommon.lua b/src/ObjCommon.lua index d1e28826..93bf6d44 100644 --- a/src/ObjCommon.lua +++ b/src/ObjCommon.lua @@ -54,4 +54,5 @@ function ObjCommon:project() self:include(includes) Utils:include(includes) + eigen:include(includes) end diff --git a/src/ObjCommon/XModel/XModelCommon.cpp b/src/ObjCommon/XModel/XModelCommon.cpp index f44e7165..083f845c 100644 --- a/src/ObjCommon/XModel/XModelCommon.cpp +++ b/src/ObjCommon/XModel/XModelCommon.cpp @@ -1,5 +1,9 @@ #include "XModelCommon.h" +#pragma warning(push, 0) +#include +#pragma warning(pop) + #include #include #include @@ -48,6 +52,41 @@ void XModelMaterial::ApplyDefaults() phong = -1; } +void XModelCommon::CalculateBoneLocalsFromGlobals() +{ + const auto boneCount = m_bones.size(); + for (auto boneIndex = 0u; boneIndex < boneCount; boneIndex++) + { + auto& bone = m_bones[boneIndex]; + + Eigen::Vector3f translation(bone.globalOffset[0], bone.globalOffset[1], bone.globalOffset[2]); + Eigen::Quaternionf rotation(bone.globalRotation.w, bone.globalRotation.x, bone.globalRotation.y, bone.globalRotation.z); + + if (bone.parentIndex) + { + assert(boneIndex > *bone.parentIndex); + const auto& parentBone = m_bones[*bone.parentIndex]; + + const Eigen::Vector3f parentTranslation(parentBone.globalOffset[0], parentBone.globalOffset[1], parentBone.globalOffset[2]); + const Eigen::Quaternionf parentRotation( + parentBone.globalRotation.w, parentBone.globalRotation.x, parentBone.globalRotation.y, parentBone.globalRotation.z); + const auto inverseParentRotation = parentRotation.inverse(); + + translation -= parentTranslation; + translation = inverseParentRotation * translation; + rotation = inverseParentRotation * rotation; + } + + bone.localOffset[0] = translation.x(); + bone.localOffset[1] = translation.y(); + bone.localOffset[2] = translation.z(); + bone.localRotation.x = rotation.x(); + bone.localRotation.y = rotation.y(); + bone.localRotation.z = rotation.z(); + bone.localRotation.w = rotation.w(); + } +} + bool operator==(const VertexMergerPos& lhs, const VertexMergerPos& rhs) { const auto coordinatesMatch = std::fabs(lhs.x - rhs.x) < std::numeric_limits::epsilon() diff --git a/src/ObjCommon/XModel/XModelCommon.h b/src/ObjCommon/XModel/XModelCommon.h index c5f0843c..46ebf3bb 100644 --- a/src/ObjCommon/XModel/XModelCommon.h +++ b/src/ObjCommon/XModel/XModelCommon.h @@ -111,6 +111,8 @@ struct XModelCommon std::vector m_vertices; std::vector m_vertex_bone_weights; XModelVertexBoneWeightCollection m_bone_weight_data; + + void CalculateBoneLocalsFromGlobals(); }; struct VertexMergerPos diff --git a/src/ObjLoading/XModel/Gltf/GltfLoader.cpp b/src/ObjLoading/XModel/Gltf/GltfLoader.cpp index 8ffe97a7..ab6c6885 100644 --- a/src/ObjLoading/XModel/Gltf/GltfLoader.cpp +++ b/src/ObjLoading/XModel/Gltf/GltfLoader.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include using namespace gltf; @@ -40,6 +41,59 @@ namespace return !(lhs == rhs); } }; + + void RhcToLhcCoordinates(float (&coords)[3]) + { + const float two[3]{coords[0], coords[1], coords[2]}; + + coords[0] = two[0]; + coords[1] = -two[2]; + coords[2] = two[1]; + } + + void RhcToLhcScale(float (&coords)[3]) + { + const float two[3]{coords[0], coords[1], coords[2]}; + + coords[0] = two[0]; + coords[1] = two[2]; + coords[2] = two[1]; + } + + void RhcToLhcQuaternion(XModelQuaternion& quat) + { + Eigen::Quaternionf eigenQuat(quat.w, quat.x, quat.y, quat.z); + const Eigen::Quaternionf eigenRotationQuat(Eigen::AngleAxisf(std::numbers::pi_v / 2.f, Eigen::Vector3f::UnitX())); + + eigenQuat = eigenRotationQuat * eigenQuat; + + quat.x = eigenQuat.x(); + quat.y = eigenQuat.y(); + quat.z = eigenQuat.z(); + quat.w = eigenQuat.w(); + } + + void RhcToLhcIndices(unsigned (&indices)[3]) + { + const unsigned two[3]{indices[0], indices[1], indices[2]}; + + indices[0] = two[2]; + indices[1] = two[1]; + indices[2] = two[0]; + } + + void RhcToLhcMatrix(Eigen::Matrix4f& matrix) + { + const Eigen::Matrix4f convertMatrix({ + {1.0, 0.0, 0.0, 0.0}, + {0.0, 0.0, -1.0, 0.0}, + {0.0, 1.0, 0.0, 0.0}, + {0.0, 0.0, 0.0, 1.0} + }); + + const auto result = convertMatrix * matrix; + matrix = result; + } } // namespace template<> struct std::hash @@ -268,21 +322,15 @@ namespace unsigned joints[4]; float weights[4]; - float coordinates[3]; - float normal[3]; - if (!positionAccessor->GetFloatVec3(vertexIndex, coordinates) || !normalAccessor->GetFloatVec3(vertexIndex, normal) + if (!positionAccessor->GetFloatVec3(vertexIndex, vertex.coordinates) || !normalAccessor->GetFloatVec3(vertexIndex, vertex.normal) || !colorAccessor->GetFloatVec4(vertexIndex, vertex.color) || !uvAccessor->GetFloatVec2(vertexIndex, vertex.uv) || !jointsAccessor->GetUnsignedVec4(vertexIndex, joints) || !weightsAccessor->GetFloatVec4(vertexIndex, weights)) { return false; } - vertex.coordinates[0] = coordinates[0]; - vertex.coordinates[1] = -coordinates[2]; - vertex.coordinates[2] = coordinates[1]; - vertex.normal[0] = normal[0]; - vertex.normal[1] = -normal[2]; - vertex.normal[2] = normal[1]; + RhcToLhcCoordinates(vertex.coordinates); + RhcToLhcCoordinates(vertex.normal); common.m_vertices.emplace_back(vertex); @@ -351,11 +399,12 @@ namespace { return false; } + RhcToLhcIndices(indices); object.m_faces.emplace_back(XModelFace{ - vertexOffset + indices[2], - vertexOffset + indices[1], vertexOffset + indices[0], + vertexOffset + indices[1], + vertexOffset + indices[2], }); } @@ -412,7 +461,7 @@ namespace return std::nullopt; } - static void ApplyNodeMatrixTRS(XModelBone& bone, const JsonNode& node) + static void ApplyNodeMatrixTRS(const JsonNode& node, float (&localOffsetRhc)[3], float (&localRotationRhc)[4], float (&scaleRhc)[3]) { const auto matrix = Eigen::Matrix4f({ {(*node.matrix)[0], (*node.matrix)[4], (*node.matrix)[8], (*node.matrix)[12]}, @@ -423,63 +472,63 @@ namespace Eigen::Affine3f transform(matrix); const auto translation = transform.translation(); - bone.localOffset[0] = translation.x(); - bone.localOffset[1] = -translation.z(); - bone.localOffset[2] = translation.y(); + localOffsetRhc[0] = translation.x(); + localOffsetRhc[1] = translation.y(); + localOffsetRhc[2] = translation.z(); const auto rotation = transform.rotation(); const auto rotationQuat = Eigen::Quaternionf(rotation); - bone.localRotation.x = rotationQuat.x(); - bone.localRotation.y = -rotationQuat.z(); - bone.localRotation.z = rotationQuat.y(); - bone.localRotation.w = rotationQuat.w(); + localRotationRhc[0] = rotationQuat.x(); + localRotationRhc[1] = rotationQuat.y(); + localRotationRhc[2] = rotationQuat.z(); + localRotationRhc[3] = rotationQuat.w(); - bone.scale[0] = matrix.block<3, 1>(0, 0).norm(); - bone.scale[1] = matrix.block<3, 1>(0, 1).norm(); - bone.scale[2] = matrix.block<3, 1>(0, 2).norm(); + scaleRhc[0] = matrix.block<3, 1>(0, 0).norm(); + scaleRhc[1] = matrix.block<3, 1>(0, 1).norm(); + scaleRhc[2] = matrix.block<3, 1>(0, 2).norm(); } - static void ApplyNodeSeparateTRS(XModelBone& bone, const JsonNode& node) + static void ApplyNodeSeparateTRS(const JsonNode& node, float (&localOffsetRhc)[3], float (&localRotationRhc)[4], float (&scaleRhc)[3]) { if (node.translation) { - bone.localOffset[0] = (*node.translation)[0]; - bone.localOffset[1] = -(*node.translation)[2]; - bone.localOffset[2] = (*node.translation)[1]; + localOffsetRhc[0] = (*node.translation)[0]; + localOffsetRhc[1] = (*node.translation)[1]; + localOffsetRhc[2] = (*node.translation)[2]; } else { - bone.localOffset[0] = 0.0f; - bone.localOffset[1] = 0.0f; - bone.localOffset[2] = 0.0f; + localOffsetRhc[0] = 0.0f; + localOffsetRhc[1] = 0.0f; + localOffsetRhc[2] = 0.0f; } if (node.rotation) { - bone.localRotation.x = (*node.rotation)[0]; - bone.localRotation.y = -(*node.rotation)[2]; - bone.localRotation.z = (*node.rotation)[1]; - bone.localRotation.w = (*node.rotation)[3]; + localRotationRhc[0] = (*node.rotation)[0]; + localRotationRhc[1] = (*node.rotation)[1]; + localRotationRhc[2] = (*node.rotation)[2]; + localRotationRhc[3] = (*node.rotation)[3]; } else { - bone.localRotation.x = 0.0f; - bone.localRotation.y = 0.0f; - bone.localRotation.z = 0.0f; - bone.localRotation.w = 1.0f; + localRotationRhc[0] = 0.0f; + localRotationRhc[1] = 0.0f; + localRotationRhc[2] = 0.0f; + localRotationRhc[3] = 1.0f; } if (node.scale) { - bone.scale[0] = (*node.scale)[0]; - bone.scale[1] = (*node.scale)[1]; - bone.scale[2] = (*node.scale)[2]; + scaleRhc[0] = (*node.scale)[0]; + scaleRhc[1] = (*node.scale)[1]; + scaleRhc[2] = (*node.scale)[2]; } else { - bone.scale[0] = 1.0f; - bone.scale[1] = 1.0f; - bone.scale[2] = 1.0f; + scaleRhc[0] = 1.0f; + scaleRhc[1] = 1.0f; + scaleRhc[2] = 1.0f; } } @@ -489,8 +538,8 @@ namespace const unsigned skinBoneOffset, const unsigned nodeIndex, const std::optional parentIndex, - const float (&parentOffset)[3], - const XModelQuaternion& parentRotation, + const Eigen::Vector3f& parentTranslationEigenRhc, + const Eigen::Quaternionf& parentRotationEigenRhc, const float (&parentScale)[3]) { if (!jRoot.nodes || nodeIndex >= jRoot.nodes->size()) @@ -507,36 +556,42 @@ namespace bone.name = node.name.value_or(std::string()); bone.parentIndex = parentIndex; + float localOffsetRhc[3]; + float localRotationRhc[4]; + float localScaleRhc[3]; if (node.matrix) - ApplyNodeMatrixTRS(bone, node); + ApplyNodeMatrixTRS(node, localOffsetRhc, localRotationRhc, localScaleRhc); else - ApplyNodeSeparateTRS(bone, node); + ApplyNodeSeparateTRS(node, localOffsetRhc, localRotationRhc, localScaleRhc); - bone.scale[0] *= parentScale[0]; - bone.scale[1] *= parentScale[1]; - bone.scale[2] *= parentScale[2]; + bone.scale[0] = localScaleRhc[0] * parentScale[0]; + bone.scale[1] = localScaleRhc[1] * parentScale[1]; + bone.scale[2] = localScaleRhc[2] * parentScale[2]; + RhcToLhcScale(bone.scale); - const auto localRotationEigen = Eigen::Quaternionf(bone.localRotation.w, bone.localRotation.x, bone.localRotation.y, bone.localRotation.z); - const auto parentRotationEigen = Eigen::Quaternionf(parentRotation.w, parentRotation.x, parentRotation.y, parentRotation.z); - const auto globalRotationEigen = (parentRotationEigen * localRotationEigen).normalized(); + const Eigen::Vector3f localTranslationEigen(localOffsetRhc[0], localOffsetRhc[1], localOffsetRhc[2]); + const Eigen::Quaternionf localRotationEigen(localRotationRhc[3], localRotationRhc[0], localRotationRhc[1], localRotationRhc[2]); - const Eigen::Vector3f localTranslationEigen(bone.localOffset[0], bone.localOffset[1], bone.localOffset[2]); - const Eigen::Vector3f parentTranslationEigen(parentOffset[0], parentOffset[1], parentOffset[2]); - const auto globalTranslationEigen = (parentRotationEigen * localTranslationEigen) + parentTranslationEigen; - bone.globalOffset[0] = globalTranslationEigen.x(); - bone.globalOffset[1] = globalTranslationEigen.y(); - bone.globalOffset[2] = globalTranslationEigen.z(); + const Eigen::Quaternionf globalRotationEigenRhc((parentRotationEigenRhc * localRotationEigen).normalized()); + const Eigen::Vector3f globalTranslationEigenRhc((parentRotationEigenRhc * localTranslationEigen) + parentTranslationEigenRhc); - bone.globalRotation.x = globalRotationEigen.x(); - bone.globalRotation.y = globalRotationEigen.y(); - bone.globalRotation.z = globalRotationEigen.z(); - bone.globalRotation.w = globalRotationEigen.w(); + bone.globalOffset[0] = globalTranslationEigenRhc.x(); + bone.globalOffset[1] = globalTranslationEigenRhc.y(); + bone.globalOffset[2] = globalTranslationEigenRhc.z(); + RhcToLhcCoordinates(bone.globalOffset); + + bone.globalRotation.x = globalRotationEigenRhc.x(); + bone.globalRotation.y = globalRotationEigenRhc.y(); + bone.globalRotation.z = globalRotationEigenRhc.z(); + bone.globalRotation.w = globalRotationEigenRhc.w(); + RhcToLhcQuaternion(bone.globalRotation); if (node.children) { for (const auto childIndex : *node.children) { - if (!ConvertJoint(jRoot, skin, common, skinBoneOffset, childIndex, commonBoneOffset, bone.globalOffset, bone.globalRotation, bone.scale)) + if (!ConvertJoint( + jRoot, skin, common, skinBoneOffset, childIndex, commonBoneOffset, globalTranslationEigenRhc, globalRotationEigenRhc, bone.scale)) return false; } } @@ -555,11 +610,15 @@ namespace const auto skinBoneOffset = static_cast(common.m_bones.size()); common.m_bones.resize(skinBoneOffset + skin.joints.size()); - constexpr float defaultTranslation[3]{0.0f, 0.0f, 0.0f}; - constexpr XModelQuaternion defaultRotation{.x = 0.0f, .y = 0.0f, .z = 0.0f, .w = 1.0f}; + const Eigen::Vector3f defaultTranslation(0.0f, 0.0f, 0.0f); + const Eigen::Quaternionf defaultRotation(1.0f, 0.0f, 0.0f, 0.0f); constexpr float defaultScale[3]{1.0f, 1.0f, 1.0f}; - return ConvertJoint(jRoot, skin, common, skinBoneOffset, rootNode, std::nullopt, defaultTranslation, defaultRotation, defaultScale); + if (!ConvertJoint(jRoot, skin, common, skinBoneOffset, rootNode, std::nullopt, defaultTranslation, defaultRotation, defaultScale)) + return false; + + common.CalculateBoneLocalsFromGlobals(); + return true; } void ConvertObjects(const JsonRoot& jRoot, XModelCommon& common)