mirror of
https://github.com/Laupetin/OpenAssetTools.git
synced 2025-09-12 19:47:27 +00:00
Merge pull request #493 from Laupetin/fix/bad-gltf-math
fix: bad gltf math
This commit is contained in:
@@ -54,4 +54,5 @@ function ObjCommon:project()
|
|||||||
|
|
||||||
self:include(includes)
|
self:include(includes)
|
||||||
Utils:include(includes)
|
Utils:include(includes)
|
||||||
|
eigen:include(includes)
|
||||||
end
|
end
|
||||||
|
@@ -1,5 +1,13 @@
|
|||||||
#include "XModelCommon.h"
|
#include "XModelCommon.h"
|
||||||
|
|
||||||
|
#pragma warning(push, 0)
|
||||||
|
// clang-format off: Order of includes is important
|
||||||
|
#include <bit> // Eigen uses std::bit_cast without including header themselves...
|
||||||
|
#include <Eigen>
|
||||||
|
// clang-format on
|
||||||
|
#pragma warning(pop)
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <format>
|
#include <format>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
@@ -48,6 +56,41 @@ void XModelMaterial::ApplyDefaults()
|
|||||||
phong = -1;
|
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)
|
bool operator==(const VertexMergerPos& lhs, const VertexMergerPos& rhs)
|
||||||
{
|
{
|
||||||
const auto coordinatesMatch = std::fabs(lhs.x - rhs.x) < std::numeric_limits<float>::epsilon()
|
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<XModelVertex> m_vertices;
|
||||||
std::vector<XModelVertexBoneWeights> m_vertex_bone_weights;
|
std::vector<XModelVertexBoneWeights> m_vertex_bone_weights;
|
||||||
XModelVertexBoneWeightCollection m_bone_weight_data;
|
XModelVertexBoneWeightCollection m_bone_weight_data;
|
||||||
|
|
||||||
|
void CalculateBoneLocalsFromGlobals();
|
||||||
};
|
};
|
||||||
|
|
||||||
struct VertexMergerPos
|
struct VertexMergerPos
|
||||||
|
@@ -14,6 +14,7 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
|
#include <numbers>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
using namespace gltf;
|
using namespace gltf;
|
||||||
@@ -22,24 +23,65 @@ namespace
|
|||||||
{
|
{
|
||||||
struct AccessorsForVertex
|
struct AccessorsForVertex
|
||||||
{
|
{
|
||||||
unsigned positionAccessor;
|
|
||||||
std::optional<unsigned> normalAccessor;
|
|
||||||
std::optional<unsigned> colorAccessor;
|
|
||||||
std::optional<unsigned> uvAccessor;
|
|
||||||
std::optional<unsigned> jointsAccessor;
|
|
||||||
std::optional<unsigned> weightsAccessor;
|
|
||||||
|
|
||||||
friend bool operator==(const AccessorsForVertex& lhs, const AccessorsForVertex& rhs)
|
friend bool operator==(const AccessorsForVertex& lhs, const AccessorsForVertex& rhs)
|
||||||
{
|
{
|
||||||
return lhs.positionAccessor == rhs.positionAccessor && lhs.normalAccessor == rhs.normalAccessor && lhs.colorAccessor == rhs.colorAccessor
|
return lhs.m_position_accessor == rhs.m_position_accessor && lhs.m_normal_accessor == rhs.m_normal_accessor
|
||||||
&& lhs.uvAccessor == rhs.uvAccessor && lhs.jointsAccessor == rhs.jointsAccessor && lhs.weightsAccessor == rhs.weightsAccessor;
|
&& lhs.m_color_accessor == rhs.m_color_accessor && lhs.m_uv_accessor == rhs.m_uv_accessor && lhs.m_joints_accessor == rhs.m_joints_accessor
|
||||||
|
&& lhs.m_weights_accessor == rhs.m_weights_accessor;
|
||||||
}
|
}
|
||||||
|
|
||||||
friend bool operator!=(const AccessorsForVertex& lhs, const AccessorsForVertex& rhs)
|
friend bool operator!=(const AccessorsForVertex& lhs, const AccessorsForVertex& rhs)
|
||||||
{
|
{
|
||||||
return !(lhs == rhs);
|
return !(lhs == rhs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned m_position_accessor;
|
||||||
|
std::optional<unsigned> m_normal_accessor;
|
||||||
|
std::optional<unsigned> m_color_accessor;
|
||||||
|
std::optional<unsigned> m_uv_accessor;
|
||||||
|
std::optional<unsigned> m_joints_accessor;
|
||||||
|
std::optional<unsigned> m_weights_accessor;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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];
|
||||||
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
template<> struct std::hash<AccessorsForVertex>
|
template<> struct std::hash<AccessorsForVertex>
|
||||||
@@ -47,12 +89,12 @@ template<> struct std::hash<AccessorsForVertex>
|
|||||||
std::size_t operator()(const AccessorsForVertex& v) const noexcept
|
std::size_t operator()(const AccessorsForVertex& v) const noexcept
|
||||||
{
|
{
|
||||||
std::size_t seed = 0x7E42C0E6;
|
std::size_t seed = 0x7E42C0E6;
|
||||||
seed ^= (seed << 6) + (seed >> 2) + 0x47B15429 + static_cast<std::size_t>(v.positionAccessor);
|
seed ^= (seed << 6) + (seed >> 2) + 0x47B15429 + static_cast<std::size_t>(v.m_position_accessor);
|
||||||
seed ^= (seed << 6) + (seed >> 2) + 0x66847B5C + std::hash<std::optional<unsigned>>()(v.normalAccessor);
|
seed ^= (seed << 6) + (seed >> 2) + 0x66847B5C + std::hash<std::optional<unsigned>>()(v.m_normal_accessor);
|
||||||
seed ^= (seed << 6) + (seed >> 2) + 0x77399D60 + std::hash<std::optional<unsigned>>()(v.colorAccessor);
|
seed ^= (seed << 6) + (seed >> 2) + 0x77399D60 + std::hash<std::optional<unsigned>>()(v.m_color_accessor);
|
||||||
seed ^= (seed << 6) + (seed >> 2) + 0x477AF9AB + std::hash<std::optional<unsigned>>()(v.uvAccessor);
|
seed ^= (seed << 6) + (seed >> 2) + 0x477AF9AB + std::hash<std::optional<unsigned>>()(v.m_uv_accessor);
|
||||||
seed ^= (seed << 6) + (seed >> 2) + 0x4421B4D9 + std::hash<std::optional<unsigned>>()(v.jointsAccessor);
|
seed ^= (seed << 6) + (seed >> 2) + 0x4421B4D9 + std::hash<std::optional<unsigned>>()(v.m_joints_accessor);
|
||||||
seed ^= (seed << 6) + (seed >> 2) + 0x13C2EBA1 + std::hash<std::optional<unsigned>>()(v.weightsAccessor);
|
seed ^= (seed << 6) + (seed >> 2) + 0x13C2EBA1 + std::hash<std::optional<unsigned>>()(v.m_weights_accessor);
|
||||||
return seed;
|
return seed;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -83,21 +125,22 @@ namespace
|
|||||||
|
|
||||||
struct ObjectToLoad
|
struct ObjectToLoad
|
||||||
{
|
{
|
||||||
unsigned meshIndex;
|
|
||||||
std::optional<unsigned> skinIndex;
|
|
||||||
|
|
||||||
ObjectToLoad(const unsigned meshIndex, const std::optional<unsigned> skinIndex)
|
ObjectToLoad(const unsigned meshIndex, const std::optional<unsigned> skinIndex)
|
||||||
: meshIndex(meshIndex),
|
: m_mesh_index(meshIndex),
|
||||||
skinIndex(skinIndex)
|
m_skin_index(skinIndex)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned m_mesh_index;
|
||||||
|
std::optional<unsigned> m_skin_index;
|
||||||
};
|
};
|
||||||
|
|
||||||
class GltfLoaderImpl final : public Loader
|
class GltfLoaderImpl final : public Loader
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit GltfLoaderImpl(const Input* input)
|
GltfLoaderImpl(const Input& input, const bool useBadRotationFormulas)
|
||||||
: m_input(input)
|
: m_input(input),
|
||||||
|
m_bad_rotation_formulas(useBadRotationFormulas)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -143,7 +186,7 @@ namespace
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
std::deque<unsigned> nodeQueue;
|
std::deque<unsigned> nodeQueue;
|
||||||
std::vector<unsigned> rootNodes = GetRootNodes(jRoot);
|
const std::vector<unsigned> rootNodes = GetRootNodes(jRoot);
|
||||||
|
|
||||||
for (const auto rootNode : rootNodes)
|
for (const auto rootNode : rootNodes)
|
||||||
nodeQueue.emplace_back(rootNode);
|
nodeQueue.emplace_back(rootNode);
|
||||||
@@ -204,9 +247,9 @@ namespace
|
|||||||
unsigned CreateVertices(XModelCommon& common, const AccessorsForVertex& accessorsForVertex)
|
unsigned CreateVertices(XModelCommon& common, const AccessorsForVertex& accessorsForVertex)
|
||||||
{
|
{
|
||||||
// clang-format off
|
// clang-format off
|
||||||
auto* positionAccessor = GetAccessorForIndex(
|
const auto* positionAccessor = GetAccessorForIndex(
|
||||||
"POSITION",
|
"POSITION",
|
||||||
accessorsForVertex.positionAccessor,
|
accessorsForVertex.m_position_accessor,
|
||||||
{JsonAccessorType::VEC3},
|
{JsonAccessorType::VEC3},
|
||||||
{JsonAccessorComponentType::FLOAT}
|
{JsonAccessorComponentType::FLOAT}
|
||||||
).value_or(nullptr);
|
).value_or(nullptr);
|
||||||
@@ -217,41 +260,41 @@ namespace
|
|||||||
OnesAccessor onesAccessor(vertexCount);
|
OnesAccessor onesAccessor(vertexCount);
|
||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
auto* normalAccessor = GetAccessorForIndex(
|
const auto* normalAccessor = GetAccessorForIndex(
|
||||||
"NORMAL",
|
"NORMAL",
|
||||||
accessorsForVertex.normalAccessor,
|
accessorsForVertex.m_normal_accessor,
|
||||||
{JsonAccessorType::VEC3},
|
{JsonAccessorType::VEC3},
|
||||||
{JsonAccessorComponentType::FLOAT}
|
{JsonAccessorComponentType::FLOAT}
|
||||||
).value_or(&nullAccessor);
|
).value_or(&nullAccessor);
|
||||||
VerifyAccessorVertexCount("NORMAL", normalAccessor, vertexCount);
|
VerifyAccessorVertexCount("NORMAL", normalAccessor, vertexCount);
|
||||||
|
|
||||||
auto* uvAccessor = GetAccessorForIndex(
|
const auto* uvAccessor = GetAccessorForIndex(
|
||||||
"TEXCOORD_0",
|
"TEXCOORD_0",
|
||||||
accessorsForVertex.uvAccessor,
|
accessorsForVertex.m_uv_accessor,
|
||||||
{JsonAccessorType::VEC2},
|
{JsonAccessorType::VEC2},
|
||||||
{JsonAccessorComponentType::FLOAT, JsonAccessorComponentType::UNSIGNED_BYTE, JsonAccessorComponentType::UNSIGNED_SHORT}
|
{JsonAccessorComponentType::FLOAT, JsonAccessorComponentType::UNSIGNED_BYTE, JsonAccessorComponentType::UNSIGNED_SHORT}
|
||||||
).value_or(&nullAccessor);
|
).value_or(&nullAccessor);
|
||||||
VerifyAccessorVertexCount("TEXCOORD_0", uvAccessor, vertexCount);
|
VerifyAccessorVertexCount("TEXCOORD_0", uvAccessor, vertexCount);
|
||||||
|
|
||||||
auto* colorAccessor = GetAccessorForIndex(
|
const auto* colorAccessor = GetAccessorForIndex(
|
||||||
"COLOR_0",
|
"COLOR_0",
|
||||||
accessorsForVertex.colorAccessor,
|
accessorsForVertex.m_color_accessor,
|
||||||
{JsonAccessorType::VEC3, JsonAccessorType::VEC4},
|
{JsonAccessorType::VEC3, JsonAccessorType::VEC4},
|
||||||
{JsonAccessorComponentType::FLOAT, JsonAccessorComponentType::UNSIGNED_BYTE, JsonAccessorComponentType::UNSIGNED_SHORT}
|
{JsonAccessorComponentType::FLOAT, JsonAccessorComponentType::UNSIGNED_BYTE, JsonAccessorComponentType::UNSIGNED_SHORT}
|
||||||
).value_or(&onesAccessor);
|
).value_or(&onesAccessor);
|
||||||
VerifyAccessorVertexCount("COLOR_0", colorAccessor, vertexCount);
|
VerifyAccessorVertexCount("COLOR_0", colorAccessor, vertexCount);
|
||||||
|
|
||||||
auto* jointsAccessor = GetAccessorForIndex(
|
const auto* jointsAccessor = GetAccessorForIndex(
|
||||||
"JOINTS_0",
|
"JOINTS_0",
|
||||||
accessorsForVertex.jointsAccessor,
|
accessorsForVertex.m_joints_accessor,
|
||||||
{JsonAccessorType::VEC4},
|
{JsonAccessorType::VEC4},
|
||||||
{JsonAccessorComponentType::UNSIGNED_BYTE, JsonAccessorComponentType::UNSIGNED_SHORT}
|
{JsonAccessorComponentType::UNSIGNED_BYTE, JsonAccessorComponentType::UNSIGNED_SHORT}
|
||||||
).value_or(&nullAccessor);
|
).value_or(&nullAccessor);
|
||||||
VerifyAccessorVertexCount("JOINTS_0", jointsAccessor, vertexCount);
|
VerifyAccessorVertexCount("JOINTS_0", jointsAccessor, vertexCount);
|
||||||
|
|
||||||
auto* weightsAccessor = GetAccessorForIndex(
|
const auto* weightsAccessor = GetAccessorForIndex(
|
||||||
"WEIGHTS_0",
|
"WEIGHTS_0",
|
||||||
accessorsForVertex.weightsAccessor,
|
accessorsForVertex.m_weights_accessor,
|
||||||
{JsonAccessorType::VEC4},
|
{JsonAccessorType::VEC4},
|
||||||
{JsonAccessorComponentType::FLOAT, JsonAccessorComponentType::UNSIGNED_BYTE, JsonAccessorComponentType::UNSIGNED_SHORT}
|
{JsonAccessorComponentType::FLOAT, JsonAccessorComponentType::UNSIGNED_BYTE, JsonAccessorComponentType::UNSIGNED_SHORT}
|
||||||
).value_or(&nullAccessor);
|
).value_or(&nullAccessor);
|
||||||
@@ -268,21 +311,15 @@ namespace
|
|||||||
unsigned joints[4];
|
unsigned joints[4];
|
||||||
float weights[4];
|
float weights[4];
|
||||||
|
|
||||||
float coordinates[3];
|
if (!positionAccessor->GetFloatVec3(vertexIndex, vertex.coordinates) || !normalAccessor->GetFloatVec3(vertexIndex, vertex.normal)
|
||||||
float normal[3];
|
|
||||||
if (!positionAccessor->GetFloatVec3(vertexIndex, coordinates) || !normalAccessor->GetFloatVec3(vertexIndex, normal)
|
|
||||||
|| !colorAccessor->GetFloatVec4(vertexIndex, vertex.color) || !uvAccessor->GetFloatVec2(vertexIndex, vertex.uv)
|
|| !colorAccessor->GetFloatVec4(vertexIndex, vertex.color) || !uvAccessor->GetFloatVec2(vertexIndex, vertex.uv)
|
||||||
|| !jointsAccessor->GetUnsignedVec4(vertexIndex, joints) || !weightsAccessor->GetFloatVec4(vertexIndex, weights))
|
|| !jointsAccessor->GetUnsignedVec4(vertexIndex, joints) || !weightsAccessor->GetFloatVec4(vertexIndex, weights))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
vertex.coordinates[0] = coordinates[0];
|
RhcToLhcCoordinates(vertex.coordinates);
|
||||||
vertex.coordinates[1] = -coordinates[2];
|
RhcToLhcCoordinates(vertex.normal);
|
||||||
vertex.coordinates[2] = coordinates[1];
|
|
||||||
vertex.normal[0] = normal[0];
|
|
||||||
vertex.normal[1] = -normal[2];
|
|
||||||
vertex.normal[2] = normal[1];
|
|
||||||
|
|
||||||
common.m_vertices.emplace_back(vertex);
|
common.m_vertices.emplace_back(vertex);
|
||||||
|
|
||||||
@@ -315,13 +352,13 @@ namespace
|
|||||||
if (!primitives.attributes.POSITION)
|
if (!primitives.attributes.POSITION)
|
||||||
throw GltfLoadException("Requires primitives attribute POSITION");
|
throw GltfLoadException("Requires primitives attribute POSITION");
|
||||||
|
|
||||||
AccessorsForVertex accessorsForVertex{
|
const AccessorsForVertex accessorsForVertex{
|
||||||
.positionAccessor = *primitives.attributes.POSITION,
|
.m_position_accessor = *primitives.attributes.POSITION,
|
||||||
.normalAccessor = primitives.attributes.NORMAL,
|
.m_normal_accessor = primitives.attributes.NORMAL,
|
||||||
.colorAccessor = primitives.attributes.COLOR_0,
|
.m_color_accessor = primitives.attributes.COLOR_0,
|
||||||
.uvAccessor = primitives.attributes.TEXCOORD_0,
|
.m_uv_accessor = primitives.attributes.TEXCOORD_0,
|
||||||
.jointsAccessor = primitives.attributes.JOINTS_0,
|
.m_joints_accessor = primitives.attributes.JOINTS_0,
|
||||||
.weightsAccessor = primitives.attributes.WEIGHTS_0,
|
.m_weights_accessor = primitives.attributes.WEIGHTS_0,
|
||||||
};
|
};
|
||||||
|
|
||||||
const auto existingVertices = m_vertex_offset_for_accessors.find(accessorsForVertex);
|
const auto existingVertices = m_vertex_offset_for_accessors.find(accessorsForVertex);
|
||||||
@@ -351,11 +388,12 @@ namespace
|
|||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
RhcToLhcIndices(indices);
|
||||||
|
|
||||||
object.m_faces.emplace_back(XModelFace{
|
object.m_faces.emplace_back(XModelFace{
|
||||||
vertexOffset + indices[2],
|
|
||||||
vertexOffset + indices[1],
|
|
||||||
vertexOffset + indices[0],
|
vertexOffset + indices[0],
|
||||||
|
vertexOffset + indices[1],
|
||||||
|
vertexOffset + indices[2],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -412,7 +450,7 @@ namespace
|
|||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ApplyNodeMatrixTRS(XModelBone& bone, const JsonNode& node)
|
void ApplyNodeMatrixTRS(const JsonNode& node, float (&localOffsetRhc)[3], float (&localRotationRhc)[4], float (&scaleRhc)[3])
|
||||||
{
|
{
|
||||||
const auto matrix = Eigen::Matrix4f({
|
const auto matrix = Eigen::Matrix4f({
|
||||||
{(*node.matrix)[0], (*node.matrix)[4], (*node.matrix)[8], (*node.matrix)[12]},
|
{(*node.matrix)[0], (*node.matrix)[4], (*node.matrix)[8], (*node.matrix)[12]},
|
||||||
@@ -423,75 +461,102 @@ namespace
|
|||||||
Eigen::Affine3f transform(matrix);
|
Eigen::Affine3f transform(matrix);
|
||||||
|
|
||||||
const auto translation = transform.translation();
|
const auto translation = transform.translation();
|
||||||
bone.localOffset[0] = translation.x();
|
|
||||||
bone.localOffset[1] = -translation.z();
|
localOffsetRhc[0] = translation.x();
|
||||||
bone.localOffset[2] = translation.y();
|
localOffsetRhc[1] = translation.y();
|
||||||
|
localOffsetRhc[2] = translation.z();
|
||||||
|
if (m_bad_rotation_formulas)
|
||||||
|
RhcToLhcCoordinates(localOffsetRhc);
|
||||||
|
|
||||||
const auto rotation = transform.rotation();
|
const auto rotation = transform.rotation();
|
||||||
const auto rotationQuat = Eigen::Quaternionf(rotation);
|
const auto rotationQuat = Eigen::Quaternionf(rotation);
|
||||||
bone.localRotation.x = rotationQuat.x();
|
if (!m_bad_rotation_formulas)
|
||||||
bone.localRotation.y = -rotationQuat.z();
|
|
||||||
bone.localRotation.z = rotationQuat.y();
|
|
||||||
bone.localRotation.w = 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();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ApplyNodeSeparateTRS(XModelBone& bone, const JsonNode& node)
|
|
||||||
{
|
|
||||||
if (node.translation)
|
|
||||||
{
|
{
|
||||||
bone.localOffset[0] = (*node.translation)[0];
|
localRotationRhc[0] = rotationQuat.x();
|
||||||
bone.localOffset[1] = -(*node.translation)[2];
|
localRotationRhc[1] = rotationQuat.y();
|
||||||
bone.localOffset[2] = (*node.translation)[1];
|
localRotationRhc[2] = rotationQuat.z();
|
||||||
|
localRotationRhc[3] = rotationQuat.w();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
bone.localOffset[0] = 0.0f;
|
// Backwards compatibility
|
||||||
bone.localOffset[1] = 0.0f;
|
localRotationRhc[0] = rotationQuat.x();
|
||||||
bone.localOffset[2] = 0.0f;
|
localRotationRhc[1] = -rotationQuat.z();
|
||||||
|
localRotationRhc[2] = rotationQuat.y();
|
||||||
|
localRotationRhc[3] = rotationQuat.w();
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ApplyNodeSeparateTRS(const JsonNode& node, float (&localOffsetRhc)[3], float (&localRotationRhc)[4], float (&scaleRhc)[3])
|
||||||
|
{
|
||||||
|
if (node.translation)
|
||||||
|
{
|
||||||
|
localOffsetRhc[0] = (*node.translation)[0];
|
||||||
|
localOffsetRhc[1] = (*node.translation)[1];
|
||||||
|
localOffsetRhc[2] = (*node.translation)[2];
|
||||||
|
if (m_bad_rotation_formulas)
|
||||||
|
RhcToLhcCoordinates(localOffsetRhc);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
localOffsetRhc[0] = 0.0f;
|
||||||
|
localOffsetRhc[1] = 0.0f;
|
||||||
|
localOffsetRhc[2] = 0.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (node.rotation)
|
if (node.rotation)
|
||||||
{
|
{
|
||||||
bone.localRotation.x = (*node.rotation)[0];
|
if (!m_bad_rotation_formulas)
|
||||||
bone.localRotation.y = -(*node.rotation)[2];
|
{
|
||||||
bone.localRotation.z = (*node.rotation)[1];
|
localRotationRhc[0] = (*node.rotation)[0];
|
||||||
bone.localRotation.w = (*node.rotation)[3];
|
localRotationRhc[1] = (*node.rotation)[1];
|
||||||
|
localRotationRhc[2] = (*node.rotation)[2];
|
||||||
|
localRotationRhc[3] = (*node.rotation)[3];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Backwards compatibility
|
||||||
|
localRotationRhc[0] = (*node.rotation)[0];
|
||||||
|
localRotationRhc[1] = -(*node.rotation)[2];
|
||||||
|
localRotationRhc[2] = (*node.rotation)[1];
|
||||||
|
localRotationRhc[3] = (*node.rotation)[3];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
bone.localRotation.x = 0.0f;
|
localRotationRhc[0] = 0.0f;
|
||||||
bone.localRotation.y = 0.0f;
|
localRotationRhc[1] = 0.0f;
|
||||||
bone.localRotation.z = 0.0f;
|
localRotationRhc[2] = 0.0f;
|
||||||
bone.localRotation.w = 1.0f;
|
localRotationRhc[3] = 1.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (node.scale)
|
if (node.scale)
|
||||||
{
|
{
|
||||||
bone.scale[0] = (*node.scale)[0];
|
scaleRhc[0] = (*node.scale)[0];
|
||||||
bone.scale[1] = (*node.scale)[1];
|
scaleRhc[1] = (*node.scale)[1];
|
||||||
bone.scale[2] = (*node.scale)[2];
|
scaleRhc[2] = (*node.scale)[2];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
bone.scale[0] = 1.0f;
|
scaleRhc[0] = 1.0f;
|
||||||
bone.scale[1] = 1.0f;
|
scaleRhc[1] = 1.0f;
|
||||||
bone.scale[2] = 1.0f;
|
scaleRhc[2] = 1.0f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool ConvertJoint(const JsonRoot& jRoot,
|
bool ConvertJoint(const JsonRoot& jRoot,
|
||||||
const JsonSkin& skin,
|
const JsonSkin& skin,
|
||||||
XModelCommon& common,
|
XModelCommon& common,
|
||||||
const unsigned skinBoneOffset,
|
const unsigned skinBoneOffset,
|
||||||
const unsigned nodeIndex,
|
const unsigned nodeIndex,
|
||||||
const std::optional<unsigned> parentIndex,
|
const std::optional<unsigned> parentIndex,
|
||||||
const float (&parentOffset)[3],
|
const Eigen::Vector3f& parentTranslationEigenRhc,
|
||||||
const XModelQuaternion& parentRotation,
|
const Eigen::Quaternionf& parentRotationEigenRhc,
|
||||||
const float (&parentScale)[3])
|
const float (&parentScale)[3])
|
||||||
{
|
{
|
||||||
if (!jRoot.nodes || nodeIndex >= jRoot.nodes->size())
|
if (!jRoot.nodes || nodeIndex >= jRoot.nodes->size())
|
||||||
return false;
|
return false;
|
||||||
@@ -507,36 +572,45 @@ namespace
|
|||||||
bone.name = node.name.value_or(std::string());
|
bone.name = node.name.value_or(std::string());
|
||||||
bone.parentIndex = parentIndex;
|
bone.parentIndex = parentIndex;
|
||||||
|
|
||||||
|
float localOffsetRhc[3];
|
||||||
|
float localRotationRhc[4];
|
||||||
|
float localScaleRhc[3];
|
||||||
if (node.matrix)
|
if (node.matrix)
|
||||||
ApplyNodeMatrixTRS(bone, node);
|
ApplyNodeMatrixTRS(node, localOffsetRhc, localRotationRhc, localScaleRhc);
|
||||||
else
|
else
|
||||||
ApplyNodeSeparateTRS(bone, node);
|
ApplyNodeSeparateTRS(node, localOffsetRhc, localRotationRhc, localScaleRhc);
|
||||||
|
|
||||||
bone.scale[0] *= parentScale[0];
|
bone.scale[0] = localScaleRhc[0] * parentScale[0];
|
||||||
bone.scale[1] *= parentScale[1];
|
bone.scale[1] = localScaleRhc[1] * parentScale[1];
|
||||||
bone.scale[2] *= parentScale[2];
|
bone.scale[2] = localScaleRhc[2] * parentScale[2];
|
||||||
|
if (!m_bad_rotation_formulas)
|
||||||
|
RhcToLhcScale(bone.scale);
|
||||||
|
|
||||||
const auto localRotationEigen = Eigen::Quaternionf(bone.localRotation.w, bone.localRotation.x, bone.localRotation.y, bone.localRotation.z);
|
const Eigen::Vector3f localTranslationEigen(localOffsetRhc[0], localOffsetRhc[1], localOffsetRhc[2]);
|
||||||
const auto parentRotationEigen = Eigen::Quaternionf(parentRotation.w, parentRotation.x, parentRotation.y, parentRotation.z);
|
const Eigen::Quaternionf localRotationEigen(localRotationRhc[3], localRotationRhc[0], localRotationRhc[1], localRotationRhc[2]);
|
||||||
const auto globalRotationEigen = (parentRotationEigen * localRotationEigen).normalized();
|
|
||||||
|
|
||||||
const Eigen::Vector3f localTranslationEigen(bone.localOffset[0], bone.localOffset[1], bone.localOffset[2]);
|
const Eigen::Quaternionf globalRotationEigenRhc((parentRotationEigenRhc * localRotationEigen).normalized());
|
||||||
const Eigen::Vector3f parentTranslationEigen(parentOffset[0], parentOffset[1], parentOffset[2]);
|
const Eigen::Vector3f globalTranslationEigenRhc((parentRotationEigenRhc * localTranslationEigen) + parentTranslationEigenRhc);
|
||||||
const auto globalTranslationEigen = (parentRotationEigen * localTranslationEigen) + parentTranslationEigen;
|
|
||||||
bone.globalOffset[0] = globalTranslationEigen.x();
|
|
||||||
bone.globalOffset[1] = globalTranslationEigen.y();
|
|
||||||
bone.globalOffset[2] = globalTranslationEigen.z();
|
|
||||||
|
|
||||||
bone.globalRotation.x = globalRotationEigen.x();
|
bone.globalOffset[0] = globalTranslationEigenRhc.x();
|
||||||
bone.globalRotation.y = globalRotationEigen.y();
|
bone.globalOffset[1] = globalTranslationEigenRhc.y();
|
||||||
bone.globalRotation.z = globalRotationEigen.z();
|
bone.globalOffset[2] = globalTranslationEigenRhc.z();
|
||||||
bone.globalRotation.w = globalRotationEigen.w();
|
if (!m_bad_rotation_formulas)
|
||||||
|
RhcToLhcCoordinates(bone.globalOffset);
|
||||||
|
|
||||||
|
bone.globalRotation.x = globalRotationEigenRhc.x();
|
||||||
|
bone.globalRotation.y = globalRotationEigenRhc.y();
|
||||||
|
bone.globalRotation.z = globalRotationEigenRhc.z();
|
||||||
|
bone.globalRotation.w = globalRotationEigenRhc.w();
|
||||||
|
if (!m_bad_rotation_formulas)
|
||||||
|
RhcToLhcQuaternion(bone.globalRotation);
|
||||||
|
|
||||||
if (node.children)
|
if (node.children)
|
||||||
{
|
{
|
||||||
for (const auto childIndex : *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;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -544,7 +618,7 @@ namespace
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool ConvertSkin(const JsonRoot& jRoot, const JsonSkin& skin, XModelCommon& common)
|
bool ConvertSkin(const JsonRoot& jRoot, const JsonSkin& skin, XModelCommon& common)
|
||||||
{
|
{
|
||||||
if (skin.joints.empty())
|
if (skin.joints.empty())
|
||||||
return true;
|
return true;
|
||||||
@@ -555,11 +629,15 @@ namespace
|
|||||||
const auto skinBoneOffset = static_cast<unsigned>(common.m_bones.size());
|
const auto skinBoneOffset = static_cast<unsigned>(common.m_bones.size());
|
||||||
common.m_bones.resize(skinBoneOffset + skin.joints.size());
|
common.m_bones.resize(skinBoneOffset + skin.joints.size());
|
||||||
|
|
||||||
constexpr float defaultTranslation[3]{0.0f, 0.0f, 0.0f};
|
const Eigen::Vector3f defaultTranslation(0.0f, 0.0f, 0.0f);
|
||||||
constexpr XModelQuaternion defaultRotation{.x = 0.0f, .y = 0.0f, .z = 0.0f, .w = 1.0f};
|
const Eigen::Quaternionf defaultRotation(1.0f, 0.0f, 0.0f, 0.0f);
|
||||||
constexpr float defaultScale[3]{1.0f, 1.0f, 1.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)
|
void ConvertObjects(const JsonRoot& jRoot, XModelCommon& common)
|
||||||
@@ -571,26 +649,26 @@ namespace
|
|||||||
|
|
||||||
for (const auto& loadObject : m_load_objects)
|
for (const auto& loadObject : m_load_objects)
|
||||||
{
|
{
|
||||||
if (loadObject.skinIndex && jRoot.skins)
|
if (loadObject.m_skin_index && jRoot.skins)
|
||||||
{
|
{
|
||||||
if (alreadyLoadedSkinIndex)
|
if (alreadyLoadedSkinIndex)
|
||||||
{
|
{
|
||||||
if (*alreadyLoadedSkinIndex != *loadObject.skinIndex)
|
if (*alreadyLoadedSkinIndex != *loadObject.m_skin_index)
|
||||||
throw GltfLoadException("Only scenes with at most one skin are supported");
|
throw GltfLoadException("Only scenes with at most one skin are supported");
|
||||||
|
|
||||||
// Do not load already loaded skin
|
// Do not load already loaded skin
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
const auto& skin = jRoot.skins.value()[*loadObject.skinIndex];
|
const auto& skin = jRoot.skins.value()[*loadObject.m_skin_index];
|
||||||
if (!ConvertSkin(jRoot, skin, common))
|
if (!ConvertSkin(jRoot, skin, common))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
alreadyLoadedSkinIndex = *loadObject.skinIndex;
|
alreadyLoadedSkinIndex = *loadObject.m_skin_index;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto& mesh = jRoot.meshes.value()[loadObject.meshIndex];
|
const auto& mesh = jRoot.meshes.value()[loadObject.m_mesh_index];
|
||||||
|
|
||||||
common.m_objects.reserve(common.m_objects.size() + mesh.primitives.size());
|
common.m_objects.reserve(common.m_objects.size() + mesh.primitives.size());
|
||||||
for (const auto& primitives : mesh.primitives)
|
for (const auto& primitives : mesh.primitives)
|
||||||
@@ -636,7 +714,7 @@ namespace
|
|||||||
{
|
{
|
||||||
const void* embeddedBufferPtr = nullptr;
|
const void* embeddedBufferPtr = nullptr;
|
||||||
size_t embeddedBufferSize = 0u;
|
size_t embeddedBufferSize = 0u;
|
||||||
if (!m_input->GetEmbeddedBuffer(embeddedBufferPtr, embeddedBufferSize) || embeddedBufferSize == 0u)
|
if (!m_input.GetEmbeddedBuffer(embeddedBufferPtr, embeddedBufferSize) || embeddedBufferSize == 0u)
|
||||||
throw GltfLoadException("Buffer tried to access embedded data when there is none");
|
throw GltfLoadException("Buffer tried to access embedded data when there is none");
|
||||||
|
|
||||||
m_buffers.emplace_back(std::make_unique<EmbeddedBuffer>(embeddedBufferPtr, embeddedBufferSize));
|
m_buffers.emplace_back(std::make_unique<EmbeddedBuffer>(embeddedBufferPtr, embeddedBufferSize));
|
||||||
@@ -716,7 +794,7 @@ namespace
|
|||||||
JsonRoot jRoot;
|
JsonRoot jRoot;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
jRoot = m_input->GetJson().get<JsonRoot>();
|
jRoot = m_input.GetJson().get<JsonRoot>();
|
||||||
}
|
}
|
||||||
catch (const nlohmann::json::exception& e)
|
catch (const nlohmann::json::exception& e)
|
||||||
{
|
{
|
||||||
@@ -746,16 +824,18 @@ namespace
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const Input* m_input;
|
const Input& m_input;
|
||||||
std::vector<ObjectToLoad> m_load_objects;
|
std::vector<ObjectToLoad> m_load_objects;
|
||||||
std::unordered_map<AccessorsForVertex, unsigned> m_vertex_offset_for_accessors;
|
std::unordered_map<AccessorsForVertex, unsigned> m_vertex_offset_for_accessors;
|
||||||
std::vector<std::unique_ptr<Accessor>> m_accessors;
|
std::vector<std::unique_ptr<Accessor>> m_accessors;
|
||||||
std::vector<std::unique_ptr<BufferView>> m_buffer_views;
|
std::vector<std::unique_ptr<BufferView>> m_buffer_views;
|
||||||
std::vector<std::unique_ptr<Buffer>> m_buffers;
|
std::vector<std::unique_ptr<Buffer>> m_buffers;
|
||||||
|
|
||||||
|
bool m_bad_rotation_formulas;
|
||||||
};
|
};
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
std::unique_ptr<Loader> Loader::CreateLoader(const Input* input)
|
std::unique_ptr<Loader> Loader::CreateLoader(const Input& input, bool useBadRotationFormulas)
|
||||||
{
|
{
|
||||||
return std::make_unique<GltfLoaderImpl>(input);
|
return std::make_unique<GltfLoaderImpl>(input, useBadRotationFormulas);
|
||||||
}
|
}
|
||||||
|
@@ -19,6 +19,13 @@ namespace gltf
|
|||||||
Loader& operator=(const Loader& other) = default;
|
Loader& operator=(const Loader& other) = default;
|
||||||
Loader& operator=(Loader&& other) noexcept = default;
|
Loader& operator=(Loader&& other) noexcept = default;
|
||||||
|
|
||||||
static std::unique_ptr<Loader> CreateLoader(const Input* input);
|
/**
|
||||||
|
* \brief Creates a loader capable of loading gltf-like files
|
||||||
|
* \param input The gltf input
|
||||||
|
* \param useBadRotationFormulas Old versions used bad formulas for converting into gltf space. Set to \c true to use them for loading to preserve
|
||||||
|
* backwards compatibility.
|
||||||
|
* \return
|
||||||
|
*/
|
||||||
|
static std::unique_ptr<Loader> CreateLoader(const Input& input, bool useBadRotationFormulas);
|
||||||
};
|
};
|
||||||
} // namespace gltf
|
} // namespace gltf
|
||||||
|
@@ -57,7 +57,8 @@ namespace
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
XModelLoader(MemoryManager& memory, ISearchPath& searchPath, ZoneScriptStrings& scriptStrings)
|
XModelLoader(MemoryManager& memory, ISearchPath& searchPath, ZoneScriptStrings& scriptStrings)
|
||||||
: m_memory(memory),
|
: m_gltf_bad_rotation_formulas(false),
|
||||||
|
m_memory(memory),
|
||||||
m_search_path(searchPath),
|
m_search_path(searchPath),
|
||||||
m_script_strings(scriptStrings)
|
m_script_strings(scriptStrings)
|
||||||
{
|
{
|
||||||
@@ -97,12 +98,22 @@ namespace
|
|||||||
jRoot.at("_type").get_to(type);
|
jRoot.at("_type").get_to(type);
|
||||||
jRoot.at("_version").get_to(version);
|
jRoot.at("_version").get_to(version);
|
||||||
|
|
||||||
if (type != "xmodel" || version != 1u)
|
if (type != "xmodel" || version < 1u || version > 2u)
|
||||||
{
|
{
|
||||||
std::cerr << std::format("Tried to load xmodel \"{}\" but did not find expected type material of version 1\n", xmodel.name);
|
std::cerr << std::format("Tried to load xmodel \"{}\" but did not find expected type material of version 1 or 2\n", xmodel.name);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (version == 1u)
|
||||||
|
{
|
||||||
|
m_gltf_bad_rotation_formulas = true;
|
||||||
|
std::cerr << std::format("DEPRECATED: XModel {} is version 1 that made use of bad GLTF bone rotations.\n", xmodel.name);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_gltf_bad_rotation_formulas = false;
|
||||||
|
}
|
||||||
|
|
||||||
const auto jXModel = jRoot.get<JsonXModel>();
|
const auto jXModel = jRoot.get<JsonXModel>();
|
||||||
return CreateXModelFromJson(jXModel, xmodel, context, registration);
|
return CreateXModelFromJson(jXModel, xmodel, context, registration);
|
||||||
}
|
}
|
||||||
@@ -119,7 +130,7 @@ namespace
|
|||||||
std::cerr << std::format("Cannot load xmodel \"{}\": {}\n", xmodel.name, message);
|
std::cerr << std::format("Cannot load xmodel \"{}\": {}\n", xmodel.name, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::unique_ptr<XModelCommon> LoadModelByExtension(std::istream& stream, const std::string& extension)
|
std::unique_ptr<XModelCommon> LoadModelByExtension(std::istream& stream, const std::string& extension) const
|
||||||
{
|
{
|
||||||
if (extension == ".glb")
|
if (extension == ".glb")
|
||||||
{
|
{
|
||||||
@@ -127,7 +138,7 @@ namespace
|
|||||||
if (!input.ReadGltfData(stream))
|
if (!input.ReadGltfData(stream))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
const auto loader = gltf::Loader::CreateLoader(&input);
|
const auto loader = gltf::Loader::CreateLoader(input, m_gltf_bad_rotation_formulas);
|
||||||
return loader->Load();
|
return loader->Load();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,7 +148,7 @@ namespace
|
|||||||
if (!input.ReadGltfData(stream))
|
if (!input.ReadGltfData(stream))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
const auto loader = gltf::Loader::CreateLoader(&input);
|
const auto loader = gltf::Loader::CreateLoader(input, m_gltf_bad_rotation_formulas);
|
||||||
return loader->Load();
|
return loader->Load();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1072,6 +1083,8 @@ namespace
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool m_gltf_bad_rotation_formulas;
|
||||||
|
|
||||||
std::vector<XSurface> m_surfaces;
|
std::vector<XSurface> m_surfaces;
|
||||||
std::vector<Material*> m_materials;
|
std::vector<Material*> m_materials;
|
||||||
|
|
||||||
|
@@ -18,16 +18,16 @@ protected:
|
|||||||
auto vertexOffset = 0u;
|
auto vertexOffset = 0u;
|
||||||
for (const auto& vertex : xmodel.m_vertices)
|
for (const auto& vertex : xmodel.m_vertices)
|
||||||
{
|
{
|
||||||
XModelVertexBoneWeights weights{0, 0};
|
XModelVertexBoneWeights weights{.weightOffset = 0, .weightCount = 0};
|
||||||
|
|
||||||
if (vertexOffset < xmodel.m_vertex_bone_weights.size())
|
if (vertexOffset < xmodel.m_vertex_bone_weights.size())
|
||||||
weights = xmodel.m_vertex_bone_weights[vertexOffset];
|
weights = xmodel.m_vertex_bone_weights[vertexOffset];
|
||||||
|
|
||||||
m_vertex_merger.Add(VertexMergerPos{vertex.coordinates[0],
|
m_vertex_merger.Add(VertexMergerPos{.x = vertex.coordinates[0],
|
||||||
vertex.coordinates[1],
|
.y = vertex.coordinates[1],
|
||||||
vertex.coordinates[2],
|
.z = vertex.coordinates[2],
|
||||||
&xmodel.m_bone_weight_data.weights[weights.weightOffset],
|
.weights = &xmodel.m_bone_weight_data.weights[weights.weightOffset],
|
||||||
weights.weightCount});
|
.weightCount = weights.weightCount});
|
||||||
|
|
||||||
vertexOffset++;
|
vertexOffset++;
|
||||||
}
|
}
|
||||||
|
@@ -10,6 +10,7 @@
|
|||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <format>
|
#include <format>
|
||||||
|
#include <numbers>
|
||||||
|
|
||||||
using namespace gltf;
|
using namespace gltf;
|
||||||
using namespace nlohmann;
|
using namespace nlohmann;
|
||||||
@@ -25,6 +26,50 @@ namespace
|
|||||||
float uv[2];
|
float uv[2];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void LhcToRhcCoordinates(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 LhcToRhcQuaternion(float (&quat)[4])
|
||||||
|
{
|
||||||
|
Eigen::Quaternionf eigenQuat(quat[3], quat[0], quat[1], quat[2]);
|
||||||
|
const Eigen::Quaternionf eigenRotationQuat(Eigen::AngleAxisf(-std::numbers::pi_v<float> / 2.f, Eigen::Vector3f::UnitX()));
|
||||||
|
|
||||||
|
eigenQuat = eigenRotationQuat * eigenQuat;
|
||||||
|
|
||||||
|
quat[0] = eigenQuat.x();
|
||||||
|
quat[1] = eigenQuat.y();
|
||||||
|
quat[2] = eigenQuat.z();
|
||||||
|
quat[3] = eigenQuat.w();
|
||||||
|
}
|
||||||
|
|
||||||
|
void LhcToRhcIndices(unsigned short* indices)
|
||||||
|
{
|
||||||
|
const unsigned short two[3]{indices[0], indices[1], indices[2]};
|
||||||
|
|
||||||
|
indices[0] = two[2];
|
||||||
|
indices[1] = two[1];
|
||||||
|
indices[2] = two[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
void LhcToRhcMatrix(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;
|
||||||
|
}
|
||||||
|
|
||||||
class GltfWriterImpl final : public gltf::Writer
|
class GltfWriterImpl final : public gltf::Writer
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -238,26 +283,39 @@ namespace
|
|||||||
JsonNode boneNode;
|
JsonNode boneNode;
|
||||||
const auto& bone = common.m_bones[boneIndex];
|
const auto& bone = common.m_bones[boneIndex];
|
||||||
|
|
||||||
Eigen::Vector3f translation(bone.globalOffset[0], bone.globalOffset[1], bone.globalOffset[2]);
|
float globalTranslationData[3]{bone.globalOffset[0], bone.globalOffset[1], bone.globalOffset[2]};
|
||||||
Eigen::Quaternionf rotation(bone.globalRotation.w, bone.globalRotation.x, bone.globalRotation.y, bone.globalRotation.z);
|
LhcToRhcCoordinates(globalTranslationData);
|
||||||
|
Eigen::Vector3f translation(globalTranslationData[0], globalTranslationData[1], globalTranslationData[2]);
|
||||||
|
|
||||||
|
float globalRotationData[4]{bone.globalRotation.x, bone.globalRotation.y, bone.globalRotation.z, bone.globalRotation.w};
|
||||||
|
LhcToRhcQuaternion(globalRotationData);
|
||||||
|
Eigen::Quaternionf rotation(globalRotationData[3], globalRotationData[0], globalRotationData[1], globalRotationData[2]);
|
||||||
|
|
||||||
if (bone.parentIndex)
|
if (bone.parentIndex)
|
||||||
{
|
{
|
||||||
const auto& parentBone = common.m_bones[*bone.parentIndex];
|
const auto& parentBone = common.m_bones[*bone.parentIndex];
|
||||||
const auto inverseParentRotation =
|
|
||||||
Eigen::Quaternionf(parentBone.globalRotation.w, parentBone.globalRotation.x, parentBone.globalRotation.y, parentBone.globalRotation.z)
|
|
||||||
.normalized()
|
|
||||||
.inverse()
|
|
||||||
.normalized();
|
|
||||||
|
|
||||||
translation -= Eigen::Vector3f(parentBone.globalOffset[0], parentBone.globalOffset[1], parentBone.globalOffset[2]);
|
float parentGlobalTranslationData[3]{parentBone.globalOffset[0], parentBone.globalOffset[1], parentBone.globalOffset[2]};
|
||||||
|
LhcToRhcCoordinates(parentGlobalTranslationData);
|
||||||
|
const Eigen::Vector3f parentTranslation(parentGlobalTranslationData[0], parentGlobalTranslationData[1], parentGlobalTranslationData[2]);
|
||||||
|
|
||||||
|
float parentGlobalRotationData[4]{
|
||||||
|
parentBone.globalRotation.x, parentBone.globalRotation.y, parentBone.globalRotation.z, parentBone.globalRotation.w};
|
||||||
|
LhcToRhcQuaternion(parentGlobalRotationData);
|
||||||
|
const Eigen::Quaternionf parentRotation(
|
||||||
|
parentGlobalRotationData[3], parentGlobalRotationData[0], parentGlobalRotationData[1], parentGlobalRotationData[2]);
|
||||||
|
const auto inverseParentRotation = parentRotation.inverse();
|
||||||
|
|
||||||
|
translation -= parentTranslation;
|
||||||
translation = inverseParentRotation * translation;
|
translation = inverseParentRotation * translation;
|
||||||
rotation = inverseParentRotation * rotation;
|
rotation = inverseParentRotation * rotation;
|
||||||
}
|
}
|
||||||
rotation.normalize();
|
rotation.normalize();
|
||||||
|
|
||||||
boneNode.name = bone.name;
|
boneNode.name = bone.name;
|
||||||
boneNode.translation = std::to_array({translation.x(), translation.z(), -translation.y()});
|
|
||||||
boneNode.rotation = std::to_array({rotation.x(), rotation.z(), -rotation.y(), rotation.w()});
|
boneNode.translation = std::to_array({translation.x(), translation.y(), translation.z()});
|
||||||
|
boneNode.rotation = std::to_array({rotation.x(), rotation.y(), rotation.z(), rotation.w()});
|
||||||
|
|
||||||
std::vector<unsigned> children;
|
std::vector<unsigned> children;
|
||||||
for (auto maybeChildIndex = 0u; maybeChildIndex < boneCount; maybeChildIndex++)
|
for (auto maybeChildIndex = 0u; maybeChildIndex < boneCount; maybeChildIndex++)
|
||||||
@@ -471,8 +529,9 @@ namespace
|
|||||||
auto* vertex = reinterpret_cast<GltfVertex*>(&bufferData[currentBufferOffset]);
|
auto* vertex = reinterpret_cast<GltfVertex*>(&bufferData[currentBufferOffset]);
|
||||||
|
|
||||||
vertex->coordinates[0] = commonVertex.coordinates[0];
|
vertex->coordinates[0] = commonVertex.coordinates[0];
|
||||||
vertex->coordinates[1] = commonVertex.coordinates[2];
|
vertex->coordinates[1] = commonVertex.coordinates[1];
|
||||||
vertex->coordinates[2] = -commonVertex.coordinates[1];
|
vertex->coordinates[2] = commonVertex.coordinates[2];
|
||||||
|
LhcToRhcCoordinates(vertex->coordinates);
|
||||||
|
|
||||||
minPosition[0] = std::min(minPosition[0], vertex->coordinates[0]);
|
minPosition[0] = std::min(minPosition[0], vertex->coordinates[0]);
|
||||||
minPosition[1] = std::min(minPosition[1], vertex->coordinates[1]);
|
minPosition[1] = std::min(minPosition[1], vertex->coordinates[1]);
|
||||||
@@ -482,8 +541,9 @@ namespace
|
|||||||
maxPosition[2] = std::max(maxPosition[2], vertex->coordinates[2]);
|
maxPosition[2] = std::max(maxPosition[2], vertex->coordinates[2]);
|
||||||
|
|
||||||
vertex->normal[0] = commonVertex.normal[0];
|
vertex->normal[0] = commonVertex.normal[0];
|
||||||
vertex->normal[1] = commonVertex.normal[2];
|
vertex->normal[1] = commonVertex.normal[1];
|
||||||
vertex->normal[2] = -commonVertex.normal[1];
|
vertex->normal[2] = commonVertex.normal[2];
|
||||||
|
LhcToRhcCoordinates(vertex->normal);
|
||||||
|
|
||||||
vertex->uv[0] = commonVertex.uv[0];
|
vertex->uv[0] = commonVertex.uv[0];
|
||||||
vertex->uv[1] = commonVertex.uv[1];
|
vertex->uv[1] = commonVertex.uv[1];
|
||||||
@@ -531,11 +591,13 @@ namespace
|
|||||||
auto* inverseBindMatrixData = reinterpret_cast<float*>(&bufferData[currentBufferOffset]);
|
auto* inverseBindMatrixData = reinterpret_cast<float*>(&bufferData[currentBufferOffset]);
|
||||||
for (const auto& bone : xmodel.m_bones)
|
for (const auto& bone : xmodel.m_bones)
|
||||||
{
|
{
|
||||||
const auto translation = Eigen::Translation3f(bone.globalOffset[0], bone.globalOffset[2], -bone.globalOffset[1]);
|
const auto translation = Eigen::Translation3f(bone.globalOffset[0], bone.globalOffset[1], bone.globalOffset[2]);
|
||||||
const auto rotation = Eigen::Quaternionf(bone.globalRotation.w, bone.globalRotation.x, bone.globalRotation.z, -bone.globalRotation.y);
|
const auto rotation = Eigen::Quaternionf(bone.globalRotation.w, bone.globalRotation.x, bone.globalRotation.y, bone.globalRotation.z);
|
||||||
|
const auto bindMatrixTransform = translation * rotation;
|
||||||
|
auto bindMatrix = bindMatrixTransform.matrix();
|
||||||
|
|
||||||
const auto bindMatrix = (translation * rotation);
|
LhcToRhcMatrix(bindMatrix);
|
||||||
const auto inverseBindMatrix = bindMatrix.matrix().inverse();
|
const auto inverseBindMatrix = bindMatrix.inverse();
|
||||||
|
|
||||||
// GLTF matrix is column major
|
// GLTF matrix is column major
|
||||||
inverseBindMatrixData[0] = inverseBindMatrix(0, 0);
|
inverseBindMatrixData[0] = inverseBindMatrix(0, 0);
|
||||||
@@ -565,9 +627,10 @@ namespace
|
|||||||
for (const auto& face : object.m_faces)
|
for (const auto& face : object.m_faces)
|
||||||
{
|
{
|
||||||
auto* faceIndices = reinterpret_cast<unsigned short*>(&bufferData[currentBufferOffset]);
|
auto* faceIndices = reinterpret_cast<unsigned short*>(&bufferData[currentBufferOffset]);
|
||||||
faceIndices[0] = static_cast<unsigned short>(face.vertexIndex[2]);
|
faceIndices[0] = static_cast<unsigned short>(face.vertexIndex[0]);
|
||||||
faceIndices[1] = static_cast<unsigned short>(face.vertexIndex[1]);
|
faceIndices[1] = static_cast<unsigned short>(face.vertexIndex[1]);
|
||||||
faceIndices[2] = static_cast<unsigned short>(face.vertexIndex[0]);
|
faceIndices[2] = static_cast<unsigned short>(face.vertexIndex[2]);
|
||||||
|
LhcToRhcIndices(faceIndices);
|
||||||
|
|
||||||
currentBufferOffset += sizeof(unsigned short) * 3u;
|
currentBufferOffset += sizeof(unsigned short) * 3u;
|
||||||
}
|
}
|
||||||
|
@@ -532,6 +532,12 @@ namespace
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CanOmitDefaultArmature()
|
||||||
|
{
|
||||||
|
return ObjWriting::Configuration.ModelOutputFormat != ObjWriting::Configuration_t::ModelOutputFormat_e::XMODEL_EXPORT
|
||||||
|
&& ObjWriting::Configuration.ModelOutputFormat != ObjWriting::Configuration_t::ModelOutputFormat_e::XMODEL_BIN;
|
||||||
|
}
|
||||||
|
|
||||||
void PopulateXModelWriter(XModelCommon& out, const AssetDumpingContext& context, const unsigned lod, const XModel* model)
|
void PopulateXModelWriter(XModelCommon& out, const AssetDumpingContext& context, const unsigned lod, const XModel* model)
|
||||||
{
|
{
|
||||||
DistinctMapper<Material*> materialMapper(model->numsurfs);
|
DistinctMapper<Material*> materialMapper(model->numsurfs);
|
||||||
@@ -543,7 +549,7 @@ namespace
|
|||||||
AddXModelVertices(out, model, lod);
|
AddXModelVertices(out, model, lod);
|
||||||
AddXModelFaces(out, model, lod);
|
AddXModelFaces(out, model, lod);
|
||||||
|
|
||||||
if (!HasDefaultArmature(model, lod))
|
if (!CanOmitDefaultArmature() || !HasDefaultArmature(model, lod))
|
||||||
{
|
{
|
||||||
AddXModelBones(out, context, model);
|
AddXModelBones(out, context, model);
|
||||||
AddXModelVertexBoneWeights(out, model, lod);
|
AddXModelVertexBoneWeights(out, model, lod);
|
||||||
@@ -684,7 +690,7 @@ namespace
|
|||||||
|
|
||||||
jRoot["$schema"] = "http://openassettools.dev/schema/xmodel.v1.json";
|
jRoot["$schema"] = "http://openassettools.dev/schema/xmodel.v1.json";
|
||||||
jRoot["_type"] = "xmodel";
|
jRoot["_type"] = "xmodel";
|
||||||
jRoot["_version"] = 1;
|
jRoot["_version"] = 2;
|
||||||
jRoot["_game"] = GAME_LOWER;
|
jRoot["_game"] = GAME_LOWER;
|
||||||
|
|
||||||
m_stream << std::setw(4) << jRoot << "\n";
|
m_stream << std::setw(4) << jRoot << "\n";
|
||||||
|
Reference in New Issue
Block a user