mirror of
https://github.com/Laupetin/OpenAssetTools.git
synced 2025-09-12 11:37:27 +00:00
fix: make ObjLoader load updated gltf bone data
This commit is contained in:
@@ -54,4 +54,5 @@ function ObjCommon:project()
|
||||
|
||||
self:include(includes)
|
||||
Utils:include(includes)
|
||||
eigen:include(includes)
|
||||
end
|
||||
|
@@ -1,5 +1,9 @@
|
||||
#include "XModelCommon.h"
|
||||
|
||||
#pragma warning(push, 0)
|
||||
#include <Eigen>
|
||||
#pragma warning(pop)
|
||||
|
||||
#include <cmath>
|
||||
#include <format>
|
||||
#include <limits>
|
||||
@@ -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<float>::epsilon()
|
||||
|
@@ -111,6 +111,8 @@ struct XModelCommon
|
||||
std::vector<XModelVertex> m_vertices;
|
||||
std::vector<XModelVertexBoneWeights> m_vertex_bone_weights;
|
||||
XModelVertexBoneWeightCollection m_bone_weight_data;
|
||||
|
||||
void CalculateBoneLocalsFromGlobals();
|
||||
};
|
||||
|
||||
struct VertexMergerPos
|
||||
|
@@ -14,6 +14,7 @@
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <numbers>
|
||||
#include <string>
|
||||
|
||||
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<float> / 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<AccessorsForVertex>
|
||||
@@ -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<unsigned> 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<unsigned>(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)
|
||||
|
Reference in New Issue
Block a user