From d45f0ffab71eea4aea1e84c764f97bbe0ebb2285 Mon Sep 17 00:00:00 2001 From: Jan Date: Thu, 6 Jun 2024 19:44:21 +0200 Subject: [PATCH 01/26] chore: adjust XModelCommon numeric types --- src/ObjCommon/XModel/XModelCommon.h | 11 ++++---- .../IW3/AssetDumpers/AssetDumperXModel.cpp | 28 +++++++++---------- .../IW4/AssetDumpers/AssetDumperXModel.cpp | 28 +++++++++---------- .../IW5/AssetDumpers/AssetDumperXModel.cpp | 28 +++++++++---------- .../T5/AssetDumpers/AssetDumperXModel.cpp | 28 +++++++++---------- .../T6/AssetDumpers/AssetDumperXModel.cpp | 28 +++++++++---------- .../XModel/Export/XModelExportWriter.cpp | 6 ++-- src/ObjWriting/XModel/Gltf/GltfWriter.cpp | 6 ++-- 8 files changed, 82 insertions(+), 81 deletions(-) diff --git a/src/ObjCommon/XModel/XModelCommon.h b/src/ObjCommon/XModel/XModelCommon.h index 4bc42851..bccba75b 100644 --- a/src/ObjCommon/XModel/XModelCommon.h +++ b/src/ObjCommon/XModel/XModelCommon.h @@ -2,6 +2,7 @@ #include "Utils/DistinctMapper.h" +#include #include #include @@ -16,7 +17,7 @@ struct XModelQuaternion struct XModelBone { std::string name; - int parentIndex; + std::optional parentIndex; float scale[3]; float globalOffset[3]; float localOffset[3]; @@ -26,7 +27,7 @@ struct XModelBone struct XModelBoneWeight { - int boneIndex; + unsigned boneIndex; float weight; }; @@ -38,7 +39,7 @@ struct XModelVertexBoneWeightCollection struct XModelVertexBoneWeights { const XModelBoneWeight* weights; - size_t weightCount; + unsigned weightCount; }; struct XModelVertex @@ -51,7 +52,7 @@ struct XModelVertex struct XModelFace { - int vertexIndex[3]; + unsigned vertexIndex[3]; }; struct XModelMaterial @@ -97,7 +98,7 @@ struct XModelMaterial struct XModelObject { std::string name; - int materialIndex; + unsigned materialIndex; std::vector m_faces; }; diff --git a/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperXModel.cpp b/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperXModel.cpp index 76d37e32..bb414b49 100644 --- a/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperXModel.cpp +++ b/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperXModel.cpp @@ -112,10 +112,10 @@ namespace else bone.name = "INVALID_BONE_NAME"; - if (boneNum < model->numRootBones) - bone.parentIndex = -1; + if (boneNum >= model->numRootBones) + bone.parentIndex = boneNum - static_cast(model->parentList[boneNum - model->numRootBones]); else - bone.parentIndex = static_cast(boneNum - static_cast(model->parentList[boneNum - model->numRootBones])); + bone.parentIndex = std::nullopt; bone.scale[0] = 1.0f; bone.scale[1] = 1.0f; @@ -297,7 +297,7 @@ namespace const auto& vertList = surface.vertList[vertListIndex]; const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; - weightCollection.weights[weightOffset++] = XModelBoneWeight{static_cast(vertList.boneOffset / sizeof(DObjSkelMat)), 1.0f}; + weightCollection.weights[weightOffset++] = XModelBoneWeight{vertList.boneOffset / sizeof(DObjSkelMat), 1.0f}; for (auto vertListVertexOffset = 0u; vertListVertexOffset < vertList.vertCount; vertListVertexOffset++) { @@ -314,7 +314,7 @@ namespace for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[0]; vertIndex++) { const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; - const auto boneIndex0 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat)); + const auto boneIndex0 = surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat); weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex0, 1.0f}; vertsBlendOffset += 1; @@ -326,8 +326,8 @@ namespace for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[1]; vertIndex++) { const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; - const auto boneIndex0 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat)); - const auto boneIndex1 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat)); + const auto boneIndex0 = surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat); + const auto boneIndex1 = surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat); const auto boneWeight1 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 2]); const auto boneWeight0 = 1.0f - boneWeight1; @@ -343,10 +343,10 @@ namespace for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[2]; vertIndex++) { const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; - const auto boneIndex0 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat)); - const auto boneIndex1 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat)); + const auto boneIndex0 = surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat); + const auto boneIndex1 = surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat); const auto boneWeight1 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 2]); - const auto boneIndex2 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 3] / sizeof(DObjSkelMat)); + const auto boneIndex2 = surface.vertInfo.vertsBlend[vertsBlendOffset + 3] / sizeof(DObjSkelMat); const auto boneWeight2 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 4]); const auto boneWeight0 = 1.0f - boneWeight1 - boneWeight2; @@ -363,12 +363,12 @@ namespace for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[3]; vertIndex++) { const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; - const auto boneIndex0 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat)); - const auto boneIndex1 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat)); + const auto boneIndex0 = surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat); + const auto boneIndex1 = surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat); const auto boneWeight1 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 2]); - const auto boneIndex2 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 3] / sizeof(DObjSkelMat)); + const auto boneIndex2 = surface.vertInfo.vertsBlend[vertsBlendOffset + 3] / sizeof(DObjSkelMat); const auto boneWeight2 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 4]); - const auto boneIndex3 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 5] / sizeof(DObjSkelMat)); + const auto boneIndex3 = surface.vertInfo.vertsBlend[vertsBlendOffset + 5] / sizeof(DObjSkelMat); const auto boneWeight3 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 6]); const auto boneWeight0 = 1.0f - boneWeight1 - boneWeight2 - boneWeight3; diff --git a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperXModel.cpp b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperXModel.cpp index f0a6f89f..75ccbe5b 100644 --- a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperXModel.cpp +++ b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperXModel.cpp @@ -107,10 +107,10 @@ namespace else bone.name = "INVALID_BONE_NAME"; - if (boneNum < model->numRootBones) - bone.parentIndex = -1; + if (boneNum >= model->numRootBones) + bone.parentIndex = boneNum - static_cast(model->parentList[boneNum - model->numRootBones]); else - bone.parentIndex = static_cast(boneNum - static_cast(model->parentList[boneNum - model->numRootBones])); + bone.parentIndex = std::nullopt; bone.scale[0] = 1.0f; bone.scale[1] = 1.0f; @@ -280,7 +280,7 @@ namespace const auto& vertList = surface.vertList[vertListIndex]; const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; - weightCollection.weights[weightOffset++] = XModelBoneWeight{static_cast(vertList.boneOffset / sizeof(DObjSkelMat)), 1.0f}; + weightCollection.weights[weightOffset++] = XModelBoneWeight{vertList.boneOffset / sizeof(DObjSkelMat), 1.0f}; for (auto vertListVertexOffset = 0u; vertListVertexOffset < vertList.vertCount; vertListVertexOffset++) { @@ -297,7 +297,7 @@ namespace for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[0]; vertIndex++) { const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; - const auto boneIndex0 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat)); + const auto boneIndex0 = surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat); weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex0, 1.0f}; vertsBlendOffset += 1; @@ -309,8 +309,8 @@ namespace for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[1]; vertIndex++) { const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; - const auto boneIndex0 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat)); - const auto boneIndex1 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat)); + const auto boneIndex0 = surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat); + const auto boneIndex1 = surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat); const auto boneWeight1 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 2]); const auto boneWeight0 = 1.0f - boneWeight1; @@ -326,10 +326,10 @@ namespace for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[2]; vertIndex++) { const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; - const auto boneIndex0 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat)); - const auto boneIndex1 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat)); + const auto boneIndex0 = surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat); + const auto boneIndex1 = surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat); const auto boneWeight1 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 2]); - const auto boneIndex2 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 3] / sizeof(DObjSkelMat)); + const auto boneIndex2 = surface.vertInfo.vertsBlend[vertsBlendOffset + 3] / sizeof(DObjSkelMat); const auto boneWeight2 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 4]); const auto boneWeight0 = 1.0f - boneWeight1 - boneWeight2; @@ -346,12 +346,12 @@ namespace for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[3]; vertIndex++) { const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; - const auto boneIndex0 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat)); - const auto boneIndex1 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat)); + const auto boneIndex0 = surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat); + const auto boneIndex1 = surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat); const auto boneWeight1 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 2]); - const auto boneIndex2 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 3] / sizeof(DObjSkelMat)); + const auto boneIndex2 = surface.vertInfo.vertsBlend[vertsBlendOffset + 3] / sizeof(DObjSkelMat); const auto boneWeight2 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 4]); - const auto boneIndex3 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 5] / sizeof(DObjSkelMat)); + const auto boneIndex3 = surface.vertInfo.vertsBlend[vertsBlendOffset + 5] / sizeof(DObjSkelMat); const auto boneWeight3 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 6]); const auto boneWeight0 = 1.0f - boneWeight1 - boneWeight2 - boneWeight3; diff --git a/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperXModel.cpp b/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperXModel.cpp index 3e840866..3ff626e5 100644 --- a/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperXModel.cpp +++ b/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperXModel.cpp @@ -107,10 +107,10 @@ namespace else bone.name = "INVALID_BONE_NAME"; - if (boneNum < model->numRootBones) - bone.parentIndex = -1; + if (boneNum >= model->numRootBones) + bone.parentIndex = boneNum - static_cast(model->parentList[boneNum - model->numRootBones]); else - bone.parentIndex = static_cast(boneNum - static_cast(model->parentList[boneNum - model->numRootBones])); + bone.parentIndex = std::nullopt; bone.scale[0] = 1.0f; bone.scale[1] = 1.0f; @@ -280,7 +280,7 @@ namespace const auto& vertList = surface.vertList[vertListIndex]; const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; - weightCollection.weights[weightOffset++] = XModelBoneWeight{static_cast(vertList.boneOffset / sizeof(DObjSkelMat)), 1.0f}; + weightCollection.weights[weightOffset++] = XModelBoneWeight{vertList.boneOffset / sizeof(DObjSkelMat), 1.0f}; for (auto vertListVertexOffset = 0u; vertListVertexOffset < vertList.vertCount; vertListVertexOffset++) { @@ -297,7 +297,7 @@ namespace for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[0]; vertIndex++) { const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; - const auto boneIndex0 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat)); + const auto boneIndex0 = surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat); weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex0, 1.0f}; vertsBlendOffset += 1; @@ -309,8 +309,8 @@ namespace for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[1]; vertIndex++) { const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; - const auto boneIndex0 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat)); - const auto boneIndex1 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat)); + const auto boneIndex0 = surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat); + const auto boneIndex1 = surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat); const auto boneWeight1 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 2]); const auto boneWeight0 = 1.0f - boneWeight1; @@ -326,10 +326,10 @@ namespace for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[2]; vertIndex++) { const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; - const auto boneIndex0 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat)); - const auto boneIndex1 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat)); + const auto boneIndex0 = surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat); + const auto boneIndex1 = surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat); const auto boneWeight1 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 2]); - const auto boneIndex2 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 3] / sizeof(DObjSkelMat)); + const auto boneIndex2 = surface.vertInfo.vertsBlend[vertsBlendOffset + 3] / sizeof(DObjSkelMat); const auto boneWeight2 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 4]); const auto boneWeight0 = 1.0f - boneWeight1 - boneWeight2; @@ -346,12 +346,12 @@ namespace for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[3]; vertIndex++) { const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; - const auto boneIndex0 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat)); - const auto boneIndex1 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat)); + const auto boneIndex0 = surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat); + const auto boneIndex1 = surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat); const auto boneWeight1 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 2]); - const auto boneIndex2 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 3] / sizeof(DObjSkelMat)); + const auto boneIndex2 = surface.vertInfo.vertsBlend[vertsBlendOffset + 3] / sizeof(DObjSkelMat); const auto boneWeight2 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 4]); - const auto boneIndex3 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 5] / sizeof(DObjSkelMat)); + const auto boneIndex3 = surface.vertInfo.vertsBlend[vertsBlendOffset + 5] / sizeof(DObjSkelMat); const auto boneWeight3 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 6]); const auto boneWeight0 = 1.0f - boneWeight1 - boneWeight2 - boneWeight3; diff --git a/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperXModel.cpp b/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperXModel.cpp index 2dd3fb87..379a964b 100644 --- a/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperXModel.cpp +++ b/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperXModel.cpp @@ -111,10 +111,10 @@ namespace else bone.name = "INVALID_BONE_NAME"; - if (boneNum < model->numRootBones) - bone.parentIndex = -1; + if (boneNum >= model->numRootBones) + bone.parentIndex = boneNum - static_cast(model->parentList[boneNum - model->numRootBones]); else - bone.parentIndex = static_cast(boneNum - static_cast(model->parentList[boneNum - model->numRootBones])); + bone.parentIndex = std::nullopt; bone.scale[0] = 1.0f; bone.scale[1] = 1.0f; @@ -296,7 +296,7 @@ namespace const auto& vertList = surface.vertList[vertListIndex]; const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; - weightCollection.weights[weightOffset++] = XModelBoneWeight{static_cast(vertList.boneOffset / sizeof(DObjSkelMat)), 1.0f}; + weightCollection.weights[weightOffset++] = XModelBoneWeight{vertList.boneOffset / sizeof(DObjSkelMat), 1.0f}; for (auto vertListVertexOffset = 0u; vertListVertexOffset < vertList.vertCount; vertListVertexOffset++) { @@ -313,7 +313,7 @@ namespace for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[0]; vertIndex++) { const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; - const auto boneIndex0 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat)); + const auto boneIndex0 = surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat); weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex0, 1.0f}; vertsBlendOffset += 1; @@ -325,8 +325,8 @@ namespace for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[1]; vertIndex++) { const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; - const auto boneIndex0 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat)); - const auto boneIndex1 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat)); + const auto boneIndex0 = surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat); + const auto boneIndex1 = surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat); const auto boneWeight1 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 2]); const auto boneWeight0 = 1.0f - boneWeight1; @@ -342,10 +342,10 @@ namespace for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[2]; vertIndex++) { const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; - const auto boneIndex0 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat)); - const auto boneIndex1 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat)); + const auto boneIndex0 = surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat); + const auto boneIndex1 = surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat); const auto boneWeight1 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 2]); - const auto boneIndex2 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 3] / sizeof(DObjSkelMat)); + const auto boneIndex2 = surface.vertInfo.vertsBlend[vertsBlendOffset + 3] / sizeof(DObjSkelMat); const auto boneWeight2 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 4]); const auto boneWeight0 = 1.0f - boneWeight1 - boneWeight2; @@ -362,12 +362,12 @@ namespace for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[3]; vertIndex++) { const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; - const auto boneIndex0 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat)); - const auto boneIndex1 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat)); + const auto boneIndex0 = surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat); + const auto boneIndex1 = surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat); const auto boneWeight1 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 2]); - const auto boneIndex2 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 3] / sizeof(DObjSkelMat)); + const auto boneIndex2 = surface.vertInfo.vertsBlend[vertsBlendOffset + 3] / sizeof(DObjSkelMat); const auto boneWeight2 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 4]); - const auto boneIndex3 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 5] / sizeof(DObjSkelMat)); + const auto boneIndex3 = surface.vertInfo.vertsBlend[vertsBlendOffset + 5] / sizeof(DObjSkelMat); const auto boneWeight3 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 6]); const auto boneWeight0 = 1.0f - boneWeight1 - boneWeight2 - boneWeight3; diff --git a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperXModel.cpp b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperXModel.cpp index 722151d3..10da34dd 100644 --- a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperXModel.cpp +++ b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperXModel.cpp @@ -123,10 +123,10 @@ namespace else bone.name = "INVALID_BONE_NAME"; - if (boneNum < model->numRootBones) - bone.parentIndex = -1; - else + if (boneNum >= model->numRootBones) bone.parentIndex = static_cast(boneNum - static_cast(model->parentList[boneNum - model->numRootBones])); + else + bone.parentIndex = std::nullopt; bone.scale[0] = 1.0f; bone.scale[1] = 1.0f; @@ -317,7 +317,7 @@ namespace const auto& vertList = surface.vertList[vertListIndex]; const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; - weightCollection.weights[weightOffset++] = XModelBoneWeight{static_cast(vertList.boneOffset / sizeof(DObjSkelMat)), 1.0f}; + weightCollection.weights[weightOffset++] = XModelBoneWeight{vertList.boneOffset / sizeof(DObjSkelMat), 1.0f}; for (auto vertListVertexOffset = 0u; vertListVertexOffset < vertList.vertCount; vertListVertexOffset++) { @@ -334,7 +334,7 @@ namespace for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[0]; vertIndex++) { const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; - const auto boneIndex0 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat)); + const auto boneIndex0 = surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat); weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex0, 1.0f}; vertsBlendOffset += 1; @@ -346,8 +346,8 @@ namespace for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[1]; vertIndex++) { const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; - const auto boneIndex0 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat)); - const auto boneIndex1 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat)); + const auto boneIndex0 = surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat); + const auto boneIndex1 = surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat); const auto boneWeight1 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 2]); const auto boneWeight0 = 1.0f - boneWeight1; @@ -363,10 +363,10 @@ namespace for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[2]; vertIndex++) { const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; - const auto boneIndex0 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat)); - const auto boneIndex1 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat)); + const auto boneIndex0 = surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat); + const auto boneIndex1 = surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat); const auto boneWeight1 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 2]); - const auto boneIndex2 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 3] / sizeof(DObjSkelMat)); + const auto boneIndex2 = surface.vertInfo.vertsBlend[vertsBlendOffset + 3] / sizeof(DObjSkelMat); const auto boneWeight2 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 4]); const auto boneWeight0 = 1.0f - boneWeight1 - boneWeight2; @@ -383,12 +383,12 @@ namespace for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[3]; vertIndex++) { const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; - const auto boneIndex0 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat)); - const auto boneIndex1 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat)); + const auto boneIndex0 = surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat); + const auto boneIndex1 = surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat); const auto boneWeight1 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 2]); - const auto boneIndex2 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 3] / sizeof(DObjSkelMat)); + const auto boneIndex2 = surface.vertInfo.vertsBlend[vertsBlendOffset + 3] / sizeof(DObjSkelMat); const auto boneWeight2 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 4]); - const auto boneIndex3 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 5] / sizeof(DObjSkelMat)); + const auto boneIndex3 = surface.vertInfo.vertsBlend[vertsBlendOffset + 5] / sizeof(DObjSkelMat); const auto boneWeight3 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 6]); const auto boneWeight0 = 1.0f - boneWeight1 - boneWeight2 - boneWeight3; diff --git a/src/ObjWriting/XModel/Export/XModelExportWriter.cpp b/src/ObjWriting/XModel/Export/XModelExportWriter.cpp index 410e6d04..ace0c29e 100644 --- a/src/ObjWriting/XModel/Export/XModelExportWriter.cpp +++ b/src/ObjWriting/XModel/Export/XModelExportWriter.cpp @@ -46,10 +46,10 @@ protected: for (const auto& bone : xmodel.m_bones) { m_stream << "BONE " << boneNum << " "; - if (bone.parentIndex < 0) - m_stream << "-1"; + if (bone.parentIndex) + m_stream << *bone.parentIndex; else - m_stream << bone.parentIndex; + m_stream << "-1"; m_stream << " \"" << bone.name << "\"\n"; boneNum++; diff --git a/src/ObjWriting/XModel/Gltf/GltfWriter.cpp b/src/ObjWriting/XModel/Gltf/GltfWriter.cpp index dba87d69..94a1786f 100644 --- a/src/ObjWriting/XModel/Gltf/GltfWriter.cpp +++ b/src/ObjWriting/XModel/Gltf/GltfWriter.cpp @@ -227,9 +227,9 @@ namespace Eigen::Vector3f translation(bone.globalOffset[0], bone.globalOffset[2], -bone.globalOffset[1]); Eigen::Quaternionf rotation(bone.globalRotation.w, bone.globalRotation.x, bone.globalRotation.z, -bone.globalRotation.y); - if (bone.parentIndex >= 0) + if (bone.parentIndex) { - const auto& parentBone = xmodel.m_bones[bone.parentIndex]; + const auto& parentBone = xmodel.m_bones[*bone.parentIndex]; const auto inverseParentRotation = Eigen::Quaternionf(parentBone.globalRotation.w, parentBone.globalRotation.x, parentBone.globalRotation.z, -parentBone.globalRotation.y) .normalized() @@ -249,7 +249,7 @@ namespace std::vector children; for (auto maybeChildIndex = 0u; maybeChildIndex < boneCount; maybeChildIndex++) { - if (xmodel.m_bones[maybeChildIndex].parentIndex == static_cast(boneIndex)) + if (xmodel.m_bones[maybeChildIndex].parentIndex && *xmodel.m_bones[maybeChildIndex].parentIndex == boneIndex) children.emplace_back(maybeChildIndex + m_first_bone_node); } if (!children.empty()) From f8b5734f86243f22ebda0f11f9551d567dce81d9 Mon Sep 17 00:00:00 2001 From: Jan Date: Sun, 28 Jul 2024 20:46:06 +0200 Subject: [PATCH 02/26] chore: make XModelCommon use offset instead of pointer --- src/ObjCommon/XModel/XModelCommon.h | 2 +- .../Game/IW3/AssetDumpers/AssetDumperXModel.cpp | 12 ++++++------ .../Game/IW4/AssetDumpers/AssetDumperXModel.cpp | 12 ++++++------ .../Game/IW5/AssetDumpers/AssetDumperXModel.cpp | 12 ++++++------ .../Game/T5/AssetDumpers/AssetDumperXModel.cpp | 12 ++++++------ .../Game/T6/AssetDumpers/AssetDumperXModel.cpp | 12 ++++++------ src/ObjWriting/XModel/Export/XModelExportWriter.cpp | 8 ++++++-- src/ObjWriting/XModel/Gltf/GltfWriter.cpp | 7 ++++--- 8 files changed, 41 insertions(+), 36 deletions(-) diff --git a/src/ObjCommon/XModel/XModelCommon.h b/src/ObjCommon/XModel/XModelCommon.h index bccba75b..2caf68b3 100644 --- a/src/ObjCommon/XModel/XModelCommon.h +++ b/src/ObjCommon/XModel/XModelCommon.h @@ -38,7 +38,7 @@ struct XModelVertexBoneWeightCollection struct XModelVertexBoneWeights { - const XModelBoneWeight* weights; + unsigned weightOffset; unsigned weightCount; }; diff --git a/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperXModel.cpp b/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperXModel.cpp index bb414b49..c69a0f9e 100644 --- a/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperXModel.cpp +++ b/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperXModel.cpp @@ -295,7 +295,7 @@ namespace for (auto vertListIndex = 0u; vertListIndex < surface.vertListCount; vertListIndex++) { const auto& vertList = surface.vertList[vertListIndex]; - const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; + const auto boneWeightOffset = weightOffset; weightCollection.weights[weightOffset++] = XModelBoneWeight{vertList.boneOffset / sizeof(DObjSkelMat), 1.0f}; @@ -313,7 +313,7 @@ namespace // 1 bone weight for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[0]; vertIndex++) { - const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; + const auto boneWeightOffset = weightOffset; const auto boneIndex0 = surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat); weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex0, 1.0f}; @@ -325,7 +325,7 @@ namespace // 2 bone weights for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[1]; vertIndex++) { - const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; + const auto boneWeightOffset = weightOffset; const auto boneIndex0 = surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat); const auto boneIndex1 = surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat); const auto boneWeight1 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 2]); @@ -342,7 +342,7 @@ namespace // 3 bone weights for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[2]; vertIndex++) { - const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; + const auto boneWeightOffset = weightOffset; const auto boneIndex0 = surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat); const auto boneIndex1 = surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat); const auto boneWeight1 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 2]); @@ -362,7 +362,7 @@ namespace // 4 bone weights for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[3]; vertIndex++) { - const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; + const auto boneWeightOffset = weightOffset; const auto boneIndex0 = surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat); const auto boneIndex1 = surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat); const auto boneWeight1 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 2]); @@ -388,7 +388,7 @@ namespace for (; handledVertices < surface.vertCount; handledVertices++) { - out.m_vertex_bone_weights.emplace_back(nullptr, 0); + out.m_vertex_bone_weights.emplace_back(0, 0); } } } diff --git a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperXModel.cpp b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperXModel.cpp index 75ccbe5b..5a36bd6f 100644 --- a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperXModel.cpp +++ b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperXModel.cpp @@ -278,7 +278,7 @@ namespace for (auto vertListIndex = 0u; vertListIndex < surface.vertListCount; vertListIndex++) { const auto& vertList = surface.vertList[vertListIndex]; - const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; + const auto boneWeightOffset = weightOffset; weightCollection.weights[weightOffset++] = XModelBoneWeight{vertList.boneOffset / sizeof(DObjSkelMat), 1.0f}; @@ -296,7 +296,7 @@ namespace // 1 bone weight for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[0]; vertIndex++) { - const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; + const auto boneWeightOffset = weightOffset; const auto boneIndex0 = surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat); weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex0, 1.0f}; @@ -308,7 +308,7 @@ namespace // 2 bone weights for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[1]; vertIndex++) { - const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; + const auto boneWeightOffset = weightOffset; const auto boneIndex0 = surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat); const auto boneIndex1 = surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat); const auto boneWeight1 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 2]); @@ -325,7 +325,7 @@ namespace // 3 bone weights for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[2]; vertIndex++) { - const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; + const auto boneWeightOffset = weightOffset; const auto boneIndex0 = surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat); const auto boneIndex1 = surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat); const auto boneWeight1 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 2]); @@ -345,7 +345,7 @@ namespace // 4 bone weights for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[3]; vertIndex++) { - const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; + const auto boneWeightOffset = weightOffset; const auto boneIndex0 = surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat); const auto boneIndex1 = surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat); const auto boneWeight1 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 2]); @@ -371,7 +371,7 @@ namespace for (; handledVertices < surface.vertCount; handledVertices++) { - out.m_vertex_bone_weights.emplace_back(nullptr, 0); + out.m_vertex_bone_weights.emplace_back(0, 0); } } } diff --git a/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperXModel.cpp b/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperXModel.cpp index 3ff626e5..53b4bf5e 100644 --- a/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperXModel.cpp +++ b/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperXModel.cpp @@ -278,7 +278,7 @@ namespace for (auto vertListIndex = 0u; vertListIndex < surface.vertListCount; vertListIndex++) { const auto& vertList = surface.vertList[vertListIndex]; - const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; + const auto boneWeightOffset = weightOffset; weightCollection.weights[weightOffset++] = XModelBoneWeight{vertList.boneOffset / sizeof(DObjSkelMat), 1.0f}; @@ -296,7 +296,7 @@ namespace // 1 bone weight for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[0]; vertIndex++) { - const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; + const auto boneWeightOffset = weightOffset; const auto boneIndex0 = surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat); weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex0, 1.0f}; @@ -308,7 +308,7 @@ namespace // 2 bone weights for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[1]; vertIndex++) { - const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; + const auto boneWeightOffset = weightOffset; const auto boneIndex0 = surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat); const auto boneIndex1 = surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat); const auto boneWeight1 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 2]); @@ -325,7 +325,7 @@ namespace // 3 bone weights for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[2]; vertIndex++) { - const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; + const auto boneWeightOffset = weightOffset; const auto boneIndex0 = surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat); const auto boneIndex1 = surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat); const auto boneWeight1 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 2]); @@ -345,7 +345,7 @@ namespace // 4 bone weights for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[3]; vertIndex++) { - const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; + const auto boneWeightOffset = weightOffset; const auto boneIndex0 = surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat); const auto boneIndex1 = surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat); const auto boneWeight1 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 2]); @@ -371,7 +371,7 @@ namespace for (; handledVertices < surface.vertCount; handledVertices++) { - out.m_vertex_bone_weights.emplace_back(nullptr, 0); + out.m_vertex_bone_weights.emplace_back(0, 0); } } } diff --git a/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperXModel.cpp b/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperXModel.cpp index 379a964b..48d67550 100644 --- a/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperXModel.cpp +++ b/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperXModel.cpp @@ -294,7 +294,7 @@ namespace for (auto vertListIndex = 0u; vertListIndex < surface.vertListCount; vertListIndex++) { const auto& vertList = surface.vertList[vertListIndex]; - const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; + const auto boneWeightOffset = weightOffset; weightCollection.weights[weightOffset++] = XModelBoneWeight{vertList.boneOffset / sizeof(DObjSkelMat), 1.0f}; @@ -312,7 +312,7 @@ namespace // 1 bone weight for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[0]; vertIndex++) { - const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; + const auto boneWeightOffset = weightOffset; const auto boneIndex0 = surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat); weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex0, 1.0f}; @@ -324,7 +324,7 @@ namespace // 2 bone weights for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[1]; vertIndex++) { - const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; + const auto boneWeightOffset = weightOffset; const auto boneIndex0 = surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat); const auto boneIndex1 = surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat); const auto boneWeight1 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 2]); @@ -341,7 +341,7 @@ namespace // 3 bone weights for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[2]; vertIndex++) { - const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; + const auto boneWeightOffset = weightOffset; const auto boneIndex0 = surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat); const auto boneIndex1 = surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat); const auto boneWeight1 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 2]); @@ -361,7 +361,7 @@ namespace // 4 bone weights for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[3]; vertIndex++) { - const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; + const auto boneWeightOffset = weightOffset; const auto boneIndex0 = surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat); const auto boneIndex1 = surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat); const auto boneWeight1 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 2]); @@ -387,7 +387,7 @@ namespace for (; handledVertices < surface.vertCount; handledVertices++) { - out.m_vertex_bone_weights.emplace_back(nullptr, 0); + out.m_vertex_bone_weights.emplace_back(0, 0); } } } diff --git a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperXModel.cpp b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperXModel.cpp index 10da34dd..e5b501b3 100644 --- a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperXModel.cpp +++ b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperXModel.cpp @@ -315,7 +315,7 @@ namespace for (auto vertListIndex = 0u; vertListIndex < surface.vertListCount; vertListIndex++) { const auto& vertList = surface.vertList[vertListIndex]; - const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; + const auto boneWeightOffset = weightOffset; weightCollection.weights[weightOffset++] = XModelBoneWeight{vertList.boneOffset / sizeof(DObjSkelMat), 1.0f}; @@ -333,7 +333,7 @@ namespace // 1 bone weight for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[0]; vertIndex++) { - const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; + const auto boneWeightOffset = weightOffset; const auto boneIndex0 = surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat); weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex0, 1.0f}; @@ -345,7 +345,7 @@ namespace // 2 bone weights for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[1]; vertIndex++) { - const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; + const auto boneWeightOffset = weightOffset; const auto boneIndex0 = surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat); const auto boneIndex1 = surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat); const auto boneWeight1 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 2]); @@ -362,7 +362,7 @@ namespace // 3 bone weights for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[2]; vertIndex++) { - const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; + const auto boneWeightOffset = weightOffset; const auto boneIndex0 = surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat); const auto boneIndex1 = surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat); const auto boneWeight1 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 2]); @@ -382,7 +382,7 @@ namespace // 4 bone weights for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[3]; vertIndex++) { - const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; + const auto boneWeightOffset = weightOffset; const auto boneIndex0 = surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat); const auto boneIndex1 = surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat); const auto boneWeight1 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 2]); @@ -408,7 +408,7 @@ namespace for (; handledVertices < surface.vertCount; handledVertices++) { - out.m_vertex_bone_weights.emplace_back(nullptr, 0); + out.m_vertex_bone_weights.emplace_back(0, 0); } } } diff --git a/src/ObjWriting/XModel/Export/XModelExportWriter.cpp b/src/ObjWriting/XModel/Export/XModelExportWriter.cpp index ace0c29e..8cb6419d 100644 --- a/src/ObjWriting/XModel/Export/XModelExportWriter.cpp +++ b/src/ObjWriting/XModel/Export/XModelExportWriter.cpp @@ -18,12 +18,16 @@ protected: auto vertexOffset = 0u; for (const auto& vertex : xmodel.m_vertices) { - XModelVertexBoneWeights weights{nullptr, 0}; + XModelVertexBoneWeights weights{0, 0}; if (vertexOffset < xmodel.m_vertex_bone_weights.size()) weights = xmodel.m_vertex_bone_weights[vertexOffset]; - m_vertex_merger.Add(VertexMergerPos{vertex.coordinates[0], vertex.coordinates[1], vertex.coordinates[2], weights.weights, weights.weightCount}); + m_vertex_merger.Add(VertexMergerPos{vertex.coordinates[0], + vertex.coordinates[1], + vertex.coordinates[2], + &xmodel.m_bone_weight_data.weights[weights.weightOffset], + weights.weightCount}); vertexOffset++; } diff --git a/src/ObjWriting/XModel/Gltf/GltfWriter.cpp b/src/ObjWriting/XModel/Gltf/GltfWriter.cpp index 94a1786f..39e9f0ff 100644 --- a/src/ObjWriting/XModel/Gltf/GltfWriter.cpp +++ b/src/ObjWriting/XModel/Gltf/GltfWriter.cpp @@ -496,14 +496,15 @@ namespace auto* weights = reinterpret_cast(&bufferData[currentBufferOffset + sizeof(uint8_t) * 4u * xmodel.m_vertex_bone_weights.size()]); for (const auto& commonVertexWeights : xmodel.m_vertex_bone_weights) { - assert(commonVertexWeights.weights != nullptr); + assert(commonVertexWeights.weightOffset < xmodel.m_bone_weight_data.weights.size()); assert(commonVertexWeights.weightCount <= 4u); const auto commonVertexWeightCount = std::min(commonVertexWeights.weightCount, 4u); + const auto* commonVertexWeightData = &xmodel.m_bone_weight_data.weights[commonVertexWeights.weightOffset]; for (auto i = 0u; i < commonVertexWeightCount; i++) { - joints[i] = static_cast(commonVertexWeights.weights[i].boneIndex); - weights[i] = commonVertexWeights.weights[i].weight; + joints[i] = static_cast(commonVertexWeightData[i].boneIndex); + weights[i] = commonVertexWeightData[i].weight; } for (auto i = commonVertexWeightCount; i < 4u; i++) From 1f5050befa8b09cc787d3534c140feab47d8895a Mon Sep 17 00:00:00 2001 From: Jan Date: Sat, 25 May 2024 10:03:25 +0200 Subject: [PATCH 03/26] feat: first draft of loading gltf models for t6 --- raw/t5/partclassification.csv | 21 + raw/t5/partclassification_mp.csv | 19 + raw/t6/partclassification.csv | 21 + raw/t6/partclassification_mp.csv | 19 + raw/t6/partclassification_new.csv | 140 ++++ src/Common/Game/IW4/IW4_Assets.h | 2 +- src/Common/Game/IW5/IW5_Assets.h | 2 +- src/Common/Game/T5/T5_Assets.h | 59 +- src/Common/Game/T6/T6_Assets.h | 18 +- src/ObjCommon/Game/T6/Json/JsonXModel.h | 2 +- src/ObjCommon/XModel/Gltf/GltfConstants.h | 2 + src/ObjCommon/XModel/Gltf/JsonGltf.h | 1 + src/ObjLoading.lua | 4 +- .../T6/AssetLoaders/AssetLoaderXModel.cpp | 2 +- src/ObjLoading/Game/T6/ObjLoaderT6.cpp | 3 +- .../Game/T6/XModel/JsonXModelLoader.cpp | 451 +++++++++++- src/ObjLoading/XModel/Gltf/GltfBinInput.cpp | 128 ++++ src/ObjLoading/XModel/Gltf/GltfBinInput.h | 26 + src/ObjLoading/XModel/Gltf/GltfInput.h | 24 + src/ObjLoading/XModel/Gltf/GltfLoader.cpp | 688 ++++++++++++++++++ src/ObjLoading/XModel/Gltf/GltfLoader.h | 24 + src/ObjLoading/XModel/Gltf/GltfTextInput.cpp | 48 ++ src/ObjLoading/XModel/Gltf/GltfTextInput.h | 23 + .../XModel/Gltf/Internal/GltfAccessor.cpp | 355 +++++++++ .../XModel/Gltf/Internal/GltfAccessor.h | 111 +++ .../XModel/Gltf/Internal/GltfBuffer.cpp | 80 ++ .../XModel/Gltf/Internal/GltfBuffer.h | 54 ++ .../XModel/Gltf/Internal/GltfBufferView.cpp | 23 + .../XModel/Gltf/Internal/GltfBufferView.h | 19 + src/ObjLoading/XModel/XModelLoader.h | 18 + .../IW5/AssetDumpers/AssetDumperWeapon.cpp | 2 +- .../T5/AssetDumpers/AssetDumperXModel.cpp | 35 +- .../T6/AssetDumpers/AssetDumperWeapon.cpp | 4 +- .../AssetDumperWeaponAttachmentUnique.cpp | 4 +- .../T6/AssetDumpers/AssetDumperXModel.cpp | 54 +- .../Game/T6/XModel/JsonXModelWriter.cpp | 32 +- src/ObjWriting/XModel/Gltf/GltfTextOutput.cpp | 2 - .../Game/IW5/XAssets/WeaponCompleteDef.txt | 2 +- .../Game/T5/XAssets/WeaponVariantDef.txt | 2 +- .../Game/T6/XAssets/WeaponVariantDef.txt | 2 +- 40 files changed, 2459 insertions(+), 67 deletions(-) create mode 100644 raw/t5/partclassification.csv create mode 100644 raw/t5/partclassification_mp.csv create mode 100644 raw/t6/partclassification.csv create mode 100644 raw/t6/partclassification_mp.csv create mode 100644 raw/t6/partclassification_new.csv create mode 100644 src/ObjLoading/XModel/Gltf/GltfBinInput.cpp create mode 100644 src/ObjLoading/XModel/Gltf/GltfBinInput.h create mode 100644 src/ObjLoading/XModel/Gltf/GltfInput.h create mode 100644 src/ObjLoading/XModel/Gltf/GltfLoader.cpp create mode 100644 src/ObjLoading/XModel/Gltf/GltfLoader.h create mode 100644 src/ObjLoading/XModel/Gltf/GltfTextInput.cpp create mode 100644 src/ObjLoading/XModel/Gltf/GltfTextInput.h create mode 100644 src/ObjLoading/XModel/Gltf/Internal/GltfAccessor.cpp create mode 100644 src/ObjLoading/XModel/Gltf/Internal/GltfAccessor.h create mode 100644 src/ObjLoading/XModel/Gltf/Internal/GltfBuffer.cpp create mode 100644 src/ObjLoading/XModel/Gltf/Internal/GltfBuffer.h create mode 100644 src/ObjLoading/XModel/Gltf/Internal/GltfBufferView.cpp create mode 100644 src/ObjLoading/XModel/Gltf/Internal/GltfBufferView.h create mode 100644 src/ObjLoading/XModel/XModelLoader.h diff --git a/raw/t5/partclassification.csv b/raw/t5/partclassification.csv new file mode 100644 index 00000000..66ac687f --- /dev/null +++ b/raw/t5/partclassification.csv @@ -0,0 +1,21 @@ +J_Hip_RI,right_leg_upper +J_Hip_LE,left_leg_upper +J_Knee_RI,right_leg_lower +J_SpineUpper,torso_upper +J_Knee_LE,left_leg_lower +J_Ankle_RI,right_foot +J_Ankle_LE,left_foot +J_Clavicle_RI,torso_upper +J_Clavicle_LE,torso_upper +J_Shoulder_RI,right_arm_upper +J_Shoulder_LE,left_arm_upper +J_Neck,neck +J_Head,head +J_Elbow_RI,right_arm_lower +J_Elbow_LE,left_arm_lower +J_Wrist_RI,right_hand +J_Wrist_LE,left_hand +J_MainRoot,torso_lower +TAG_WEAPON_LEFT,gun +TAG_WEAPON_RIGHT,gun +J_Helmet,helmet diff --git a/raw/t5/partclassification_mp.csv b/raw/t5/partclassification_mp.csv new file mode 100644 index 00000000..87ee201f --- /dev/null +++ b/raw/t5/partclassification_mp.csv @@ -0,0 +1,19 @@ +J_Hip_RI,right_leg_upper +J_Hip_LE,left_leg_upper +J_Knee_RI,right_leg_lower +J_SpineUpper,torso_lower +J_SpineLower,torso_lower +J_MainRoot,torso_lower +J_Knee_LE,left_leg_lower +J_Ankle_RI,right_foot +J_Ankle_LE,left_foot +J_Clavicle_RI,torso_upper +J_Clavicle_LE,torso_upper +J_Shoulder_RI,right_arm_upper +J_Shoulder_LE,left_arm_upper +J_Neck,neck +J_Head,head +J_Elbow_RI,right_arm_lower +J_Elbow_LE,left_arm_lower +J_Wrist_RI,right_hand +J_Wrist_LE,left_hand diff --git a/raw/t6/partclassification.csv b/raw/t6/partclassification.csv new file mode 100644 index 00000000..66ac687f --- /dev/null +++ b/raw/t6/partclassification.csv @@ -0,0 +1,21 @@ +J_Hip_RI,right_leg_upper +J_Hip_LE,left_leg_upper +J_Knee_RI,right_leg_lower +J_SpineUpper,torso_upper +J_Knee_LE,left_leg_lower +J_Ankle_RI,right_foot +J_Ankle_LE,left_foot +J_Clavicle_RI,torso_upper +J_Clavicle_LE,torso_upper +J_Shoulder_RI,right_arm_upper +J_Shoulder_LE,left_arm_upper +J_Neck,neck +J_Head,head +J_Elbow_RI,right_arm_lower +J_Elbow_LE,left_arm_lower +J_Wrist_RI,right_hand +J_Wrist_LE,left_hand +J_MainRoot,torso_lower +TAG_WEAPON_LEFT,gun +TAG_WEAPON_RIGHT,gun +J_Helmet,helmet diff --git a/raw/t6/partclassification_mp.csv b/raw/t6/partclassification_mp.csv new file mode 100644 index 00000000..87ee201f --- /dev/null +++ b/raw/t6/partclassification_mp.csv @@ -0,0 +1,19 @@ +J_Hip_RI,right_leg_upper +J_Hip_LE,left_leg_upper +J_Knee_RI,right_leg_lower +J_SpineUpper,torso_lower +J_SpineLower,torso_lower +J_MainRoot,torso_lower +J_Knee_LE,left_leg_lower +J_Ankle_RI,right_foot +J_Ankle_LE,left_foot +J_Clavicle_RI,torso_upper +J_Clavicle_LE,torso_upper +J_Shoulder_RI,right_arm_upper +J_Shoulder_LE,left_arm_upper +J_Neck,neck +J_Head,head +J_Elbow_RI,right_arm_lower +J_Elbow_LE,left_arm_lower +J_Wrist_RI,right_hand +J_Wrist_LE,left_hand diff --git a/raw/t6/partclassification_new.csv b/raw/t6/partclassification_new.csv new file mode 100644 index 00000000..f22da27e --- /dev/null +++ b/raw/t6/partclassification_new.csv @@ -0,0 +1,140 @@ +j_helmet,helmet +head,02 +j_brow_le,02 +j_brow_ri,02 +j_cheek_le,02 +j_cheek_ri,02 +j_cheekpuff_le,02 +j_cheekpuff_ri,02 +j_eye_lid_bot_le,02 +j_eye_lid_bot_ri,02 +j_eye_lid_top_le,02 +j_eye_lid_top_ri,02 +j_eyeball_le,02 +j_eyeball_ri,02 +j_eyebrow_top_le,02 +j_eyebrow_top_ri,02 +j_eyelid_bottom_le,02 +j_eyelid_bottom_ri,02 +j_eyelid_top_le,02 +j_eyelid_top_ri,02 +j_head,02 +j_head_end,02 +j_helmet,02 +j_jaw,02 +j_levator_le,02 +j_levator_ri,02 +j_lip_bot_le,02 +j_lip_bot_ri,02 +j_lip_top_le,02 +j_lip_top_ri,02 +j_mouth_le,02 +j_mouth_ri,02 +j_nose,02 +le_ear,02 +le_ear_end,02 +ri_ear,02 +ri_ear_end,02 +tag_eye,02 +tag_mouth_fx,02 +j_neck,03 +j_neck_end,03 +neck,03 +j_clavicle_le,04 +j_clavicle_ri,04 +back_mid,05 +j_shoulderraise_le,05 +j_shoulderraise_ri,05 +j_spine4,05 +j_spineupper,05 +tag_inhand,05 +tag_weapon_chest,05 +torso_stabilizer,06 +j_elbow_bulge_ri,07 +j_shoulder_ri,07 +j_shouldertwist_ri,07 +shoulder,07 +j_elbow_bulge_le,08 +j_shoulder_le,08 +j_shouldertwist_le,08 +j_elbow_ri,09 +j_sleave_reshape_top_ri_1,09 +j_sleave_reshape_top_ri_2,09 +j_sleave_reshape_top_ri_3,09 +j_wristtwist_ri,09 +j_elbow_le,10 +j_sleave_reshape_bottom_le_1,10 +j_sleave_reshape_bottom_le_2,10 +j_sleave_reshape_top_le_1,10 +j_sleave_reshape_top_le_2,10 +j_sleave_reshape_top_le_3,10 +j_wristtwist_le,10 +j_index_ri_0,11 +j_index_ri_1,11 +j_index_ri_2,11 +j_index_ri_3,11 +j_mid_ri_0,11 +j_mid_ri_1,11 +j_mid_ri_2,11 +j_mid_ri_3,11 +j_palm_end_ri,11 +j_palm_ri,11 +j_pinky_ri_0,11 +j_pinky_ri_1,11 +j_pinky_ri_2,11 +j_pinky_ri_3,11 +j_pinkypalm_ri,11 +j_ring_ri_0,11 +j_ring_ri_1,11 +j_ring_ri_2,11 +j_ring_ri_3,11 +j_ringpalm_ri,11 +j_thumb_ri_0,11 +j_thumb_ri_1,11 +j_thumb_ri_2,11 +j_thumb_ri_3,11 +j_webbing_ri,11 +j_wrist_ri,11 +tag_weapon_right,11 +j_index_le_0,12 +j_index_le_1,12 +j_index_le_2,12 +j_index_le_3,12 +j_mid_le_0,12 +j_mid_le_1,12 +j_mid_le_2,12 +j_mid_le_3,12 +j_palm_end_le,12 +j_palm_le,12 +j_pinky_le_0,12 +j_pinky_le_1,12 +j_pinky_le_2,12 +j_pinky_le_3,12 +j_pinkypalm_le,12 +j_ring_le_0,12 +j_ring_le_1,12 +j_ring_le_2,12 +j_ring_le_3,12 +j_ringpalm_le,12 +j_thumb_le_0,12 +j_thumb_le_1,12 +j_thumb_le_2,12 +j_thumb_le_3,12 +j_webbing_le,12 +j_wrist_le,12 +tag_knife_attach,12 +j_hip_ri,13 +j_hip_le,14 +j_hiptwist_le,14 +j_knee_bulge_ri,15 +j_knee_ri,15 +j_knee_bulge_le,16 +j_knee_le,16 +j_ankle_ri,17 +j_ball_ri,17 +j_toe_ri,17 +j_ankle_le,18 +j_ball_le,18 +j_toe_le,18 +tag_stowed_back,20 +tag_weapon_left,20 \ No newline at end of file diff --git a/src/Common/Game/IW4/IW4_Assets.h b/src/Common/Game/IW4/IW4_Assets.h index fd2e0479..d6bde24b 100644 --- a/src/Common/Game/IW4/IW4_Assets.h +++ b/src/Common/Game/IW4/IW4_Assets.h @@ -4082,7 +4082,7 @@ namespace IW4 HITLOC_GUN = 0x12, HITLOC_SHIELD = 0x13, - HITLOC_NUM, + HITLOC_COUNT, }; struct snd_alias_list_name diff --git a/src/Common/Game/IW5/IW5_Assets.h b/src/Common/Game/IW5/IW5_Assets.h index e7555ea3..5c7e004c 100644 --- a/src/Common/Game/IW5/IW5_Assets.h +++ b/src/Common/Game/IW5/IW5_Assets.h @@ -3452,7 +3452,7 @@ namespace IW5 HITLOC_GUN = 0x12, HITLOC_SHIELD = 0x13, - HITLOC_NUM + HITLOC_COUNT }; enum materialSurfType_t diff --git a/src/Common/Game/T5/T5_Assets.h b/src/Common/Game/T5/T5_Assets.h index fd18616b..e2ef4741 100644 --- a/src/Common/Game/T5/T5_Assets.h +++ b/src/Common/Game/T5/T5_Assets.h @@ -108,9 +108,49 @@ namespace T5 MAX_XFILE_COUNT }; - typedef float vec2_t[2]; - typedef float vec3_t[3]; - typedef float vec4_t[4]; + union vec2_t + { + float v[2]; + + struct + { + float x; + float y; + }; + }; + + union vec3_t + { + struct + { + float x; + float y; + float z; + }; + + float v[3]; + }; + + union vec4_t + { + float v[4]; + + struct + { + float x; + float y; + float z; + float w; + }; + + struct + { + float r; + float g; + float b; + float a; + }; + }; union XAssetHeader { @@ -166,8 +206,6 @@ namespace T5 typedef tdef_align(128) float float_align128; typedef char cbrushedge_t; - typedef float vec2_t[2]; - typedef float vec3_t[3]; typedef tdef_align(128) unsigned int raw_uint128; struct PhysPreset @@ -619,6 +657,11 @@ namespace T5 PhysGeomList* geomList; }; + struct XModelQuat + { + int16_t v[4]; + }; + struct XModel { const char* name; @@ -628,8 +671,8 @@ namespace T5 char lodRampType; uint16_t* boneNames; char* parentList; - int16_t (*quats)[4]; - float (*trans)[4]; + XModelQuat* quats; + vec4_t* trans; char* partClassification; DObjAnimMat* baseMat; XSurface* surfs; @@ -3288,7 +3331,7 @@ namespace T5 HITLOC_L_FOOT = 0x11, HITLOC_GUN = 0x12, - HITLOC_NUM + HITLOC_COUNT }; struct flameTable diff --git a/src/Common/Game/T6/T6_Assets.h b/src/Common/Game/T6/T6_Assets.h index 54fb2a22..0ac7f846 100644 --- a/src/Common/Game/T6/T6_Assets.h +++ b/src/Common/Game/T6/T6_Assets.h @@ -601,6 +601,11 @@ namespace T6 XMODEL_LOD_RAMP_COUNT }; + struct XModelQuat + { + int16_t v[4]; + }; + struct XModel { const char* name; @@ -610,9 +615,9 @@ namespace T6 XModelLodRampType lodRampType; ScriptString* boneNames; unsigned char* parentList; - uint16_t (*quats)[4]; - float (*trans)[4]; - char* partClassification; + XModelQuat* quats; + vec4_t* trans; + unsigned char* partClassification; DObjAnimMat* baseMat; XSurface* surfs; Material** materialHandles; @@ -625,7 +630,7 @@ namespace T6 vec3_t mins; vec3_t maxs; uint16_t numLods; - uint16_t collLod; + int16_t collLod; float* himipInvSqRadii; int memUsage; unsigned int flags; @@ -4324,7 +4329,8 @@ namespace T6 MISSILE_GUIDANCE_TVGUIDED = 0x6, MISSILE_GUIDANCE_DRONE = 0x7, MISSILE_GUIDANCE_HEATSEEKING = 0x8, - MISSILE_GUIDANCE_COUNT = 0x9, + + MISSILE_GUIDANCE_COUNT }; enum hitLocation_t @@ -4351,7 +4357,7 @@ namespace T6 HITLOC_GUN = 0x13, HITLOC_SHIELD = 0x14, - HITLOC_NUM, + HITLOC_COUNT, }; struct WeaponDef diff --git a/src/ObjCommon/Game/T6/Json/JsonXModel.h b/src/ObjCommon/Game/T6/Json/JsonXModel.h index 28ac5563..5e725a32 100644 --- a/src/ObjCommon/Game/T6/Json/JsonXModel.h +++ b/src/ObjCommon/Game/T6/Json/JsonXModel.h @@ -24,7 +24,7 @@ namespace T6 { public: std::vector lods; - unsigned collLod; + std::optional collLod; std::optional physPreset; std::optional physConstraints; unsigned flags; diff --git a/src/ObjCommon/XModel/Gltf/GltfConstants.h b/src/ObjCommon/XModel/Gltf/GltfConstants.h index a83f4e2e..d4338588 100644 --- a/src/ObjCommon/XModel/Gltf/GltfConstants.h +++ b/src/ObjCommon/XModel/Gltf/GltfConstants.h @@ -3,6 +3,7 @@ #include "Utils/FileUtils.h" #include +#include namespace gltf { @@ -18,6 +19,7 @@ namespace gltf constexpr auto GLTF_JSON_CHUNK_DATA_OFFSET = 20u; constexpr auto GLTF_DATA_URI_PREFIX = "data:application/octet-stream;base64,"; + constexpr auto URI_PREFIX_LENGTH = std::char_traits::length(GLTF_DATA_URI_PREFIX); constexpr auto GLTF_ATTRIBUTE_POSITION = "POSITION"; constexpr auto GLTF_ATTRIBUTE_NORMAL = "NORMAL"; diff --git a/src/ObjCommon/XModel/Gltf/JsonGltf.h b/src/ObjCommon/XModel/Gltf/JsonGltf.h index 2f3a15d4..a0f112b5 100644 --- a/src/ObjCommon/XModel/Gltf/JsonGltf.h +++ b/src/ObjCommon/XModel/Gltf/JsonGltf.h @@ -258,6 +258,7 @@ namespace gltf public: std::optional POSITION; std::optional NORMAL; + std::optional COLOR_0; std::optional TEXCOORD_0; std::optional JOINTS_0; std::optional WEIGHTS_0; diff --git a/src/ObjLoading.lua b/src/ObjLoading.lua index 9bda05ab..9452e56e 100644 --- a/src/ObjLoading.lua +++ b/src/ObjLoading.lua @@ -18,6 +18,7 @@ function ObjLoading:link(links) links:linkto(minilzo) links:linkto(minizip) links:linkto(zlib) + links:linkto(libtomcrypt) end function ObjLoading:use() @@ -55,6 +56,7 @@ function ObjLoading:project() minilzo:include(includes) minizip:include(includes) zlib:include(includes) - json:include(includes) eigen:include(includes) + json:include(includes) + libtomcrypt:include(includes) end diff --git a/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderXModel.cpp b/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderXModel.cpp index 2ee2dc36..a2702425 100644 --- a/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderXModel.cpp +++ b/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderXModel.cpp @@ -38,7 +38,7 @@ bool AssetLoaderXModel::LoadFromRaw( if (LoadXModelAsJson(*file.m_stream, *xmodel, memory, manager, dependencies)) manager->AddAsset(assetName, xmodel, std::move(dependencies)); else - std::cerr << "Failed to load xmodel \"" << assetName << "\"\n"; + std::cerr << std::format("Failed to load xmodel \"{}\"\n", assetName); return true; } diff --git a/src/ObjLoading/Game/T6/ObjLoaderT6.cpp b/src/ObjLoading/Game/T6/ObjLoaderT6.cpp index 99b60ca8..160923b7 100644 --- a/src/ObjLoading/Game/T6/ObjLoaderT6.cpp +++ b/src/ObjLoading/Game/T6/ObjLoaderT6.cpp @@ -19,6 +19,7 @@ #include "AssetLoaders/AssetLoaderWeaponAttachment.h" #include "AssetLoaders/AssetLoaderWeaponAttachmentUnique.h" #include "AssetLoaders/AssetLoaderWeaponCamo.h" +#include "AssetLoaders/AssetLoaderXModel.h" #include "AssetLoaders/AssetLoaderZBarrier.h" #include "AssetLoading/AssetLoadingManager.h" #include "Game/T6/CommonT6.h" @@ -50,7 +51,7 @@ namespace T6 REGISTER_ASSET_LOADER(AssetLoaderPhysConstraints) REGISTER_ASSET_LOADER(BasicAssetLoader) REGISTER_ASSET_LOADER(BasicAssetLoader) - REGISTER_ASSET_LOADER(BasicAssetLoader) + REGISTER_ASSET_LOADER(AssetLoaderXModel) REGISTER_ASSET_LOADER(AssetLoaderMaterial) REGISTER_ASSET_LOADER(BasicAssetLoader) REGISTER_ASSET_LOADER(AssetLoaderGfxImage) diff --git a/src/ObjLoading/Game/T6/XModel/JsonXModelLoader.cpp b/src/ObjLoading/Game/T6/XModel/JsonXModelLoader.cpp index 2f142638..a592a73c 100644 --- a/src/ObjLoading/Game/T6/XModel/JsonXModelLoader.cpp +++ b/src/ObjLoading/Game/T6/XModel/JsonXModelLoader.cpp @@ -1,8 +1,22 @@ #include "JsonXModelLoader.h" +#include "Csv/CsvStream.h" #include "Game/T6/CommonT6.h" #include "Game/T6/Json/JsonXModel.h" +#include "ObjLoading.h" +#include "Utils/QuatInt16.h" +#include "Utils/StringUtils.h" +#include "XModel/Gltf/GltfBinInput.h" +#include "XModel/Gltf/GltfLoader.h" +#include "XModel/Gltf/GltfTextInput.h" +#include "XModel/XModelCommon.h" +#pragma warning(push, 0) +#include +#pragma warning(pop) + +#include +#include #include #include #include @@ -11,21 +25,110 @@ using namespace nlohmann; using namespace T6; +namespace fs = std::filesystem; + namespace { + const char* HITLOC_NAMES[]{ + "none", "helmet", "head", "neck", "torso_upper", "torso_middle", "torso_lower", "right_arm_upper", + "left_arm_upper", "right_arm_lower", "left_arm_lower", "right_hand", "left_hand", "right_leg_upper", "left_leg_upper", "right_leg_lower", + "left_leg_lower", "right_foot", "left_foot", "gun", "shield", + }; + static_assert(std::extent_v == HITLOC_COUNT); + + class PartClassificationState final : public IZoneAssetLoaderState + { + static constexpr auto PART_CLASSIFICATION_FILE = "partclassification.csv"; + + public: + PartClassificationState() + : m_loaded(false) + { + } + + bool Load(const IAssetLoadingManager& manager) + { + if (m_loaded) + return true; + + if (ObjLoading::Configuration.Verbose) + std::cout << "Loading part classification...\n"; + + const auto file = manager.GetAssetLoadingContext()->m_raw_search_path->Open(PART_CLASSIFICATION_FILE); + if (!file.IsOpen()) + { + std::cerr << std::format("Could not load part classification: Failed to open {}\n", PART_CLASSIFICATION_FILE); + return false; + } + + CsvInputStream csvStream(*file.m_stream); + std::vector row; + auto rowIndex = 0u; + while (csvStream.NextRow(row)) + { + if (!LoadRow(rowIndex++, row)) + return false; + } + + m_loaded = true; + + return false; + } + + [[nodiscard]] unsigned GetPartClassificationForBoneName(const std::string& boneName) const + { + const auto entry = m_part_classifications.find(boneName); + + return entry != m_part_classifications.end() ? entry->second : HITLOC_NONE; + } + + private: + bool LoadRow(const unsigned rowIndex, std::vector& row) + { + if (row.empty()) + return true; + + if (row.size() != 2) + { + std::cerr << "Could not load part classification: Invalid row\n"; + return false; + } + + utils::MakeStringLowerCase(row[0]); + utils::MakeStringLowerCase(row[1]); + + const auto foundHitLoc = std::ranges::find(HITLOC_NAMES, row[1]); + if (foundHitLoc == std::end(HITLOC_NAMES)) + { + std::cerr << std::format("Invalid hitloc name in row {}: {}\n", rowIndex + 1, row[1]); + return false; + } + + const auto hitLocNum = std::distance(std::begin(HITLOC_NAMES), foundHitLoc); + + m_part_classifications.emplace(row[0], hitLocNum); + return true; + } + + bool m_loaded; + std::unordered_map m_part_classifications; + }; + class JsonLoader { public: JsonLoader(std::istream& stream, MemoryManager& memory, IAssetLoadingManager& manager, std::set& dependencies) : m_stream(stream), m_memory(memory), + m_script_strings(manager.GetAssetLoadingContext()->m_zone->m_script_strings), m_manager(manager), + m_part_classification_state(*m_manager.GetAssetLoadingContext()->GetZoneAssetLoaderState()), m_dependencies(dependencies) { } - bool Load(XModel& xmodel) const + bool Load(XModel& xmodel) { const auto jRoot = json::parse(m_stream); std::string type; @@ -36,7 +139,7 @@ namespace if (type != "xmodel" || version != 1u) { - std::cerr << "Tried to load xmodel \"" << xmodel.name << "\" but did not find expected type material of version 1\n"; + std::cerr << std::format("Tried to load xmodel \"{}\" but did not find expected type material of version 1\n", xmodel.name); return false; } @@ -56,12 +159,344 @@ namespace private: static void PrintError(const XModel& xmodel, const std::string& message) { - std::cerr << "Cannot load xmodel \"" << xmodel.name << "\": " << message << "\n"; + std::cerr << std::format("Cannot load xmodel \"{}\": {}\n", xmodel.name, message); } - bool CreateXModelFromJson(const JsonXModel& jXModel, XModel& xmodel) const + static std::unique_ptr LoadModelByExtension(std::istream& stream, const std::string& extension) { - xmodel.collLod = static_cast(jXModel.collLod); + if (extension == ".glb") + { + gltf::BinInput input; + if (!input.ReadGltfData(stream)) + return nullptr; + + const auto loader = gltf::Loader::CreateLoader(&input); + return loader->Load(); + } + + if (extension == ".gltf") + { + gltf::TextInput input; + if (!input.ReadGltfData(stream)) + return nullptr; + + const auto loader = gltf::Loader::CreateLoader(&input); + return loader->Load(); + } + + return nullptr; + } + + static void ApplyBasePose(DObjAnimMat& baseMat, const XModelBone& bone) + { + baseMat.trans.x = bone.globalOffset[0]; + baseMat.trans.y = bone.globalOffset[1]; + baseMat.trans.z = bone.globalOffset[2]; + baseMat.quat.x = bone.globalRotation.x; + baseMat.quat.y = bone.globalRotation.y; + baseMat.quat.z = bone.globalRotation.z; + baseMat.quat.w = bone.globalRotation.w; + + const auto quatNormSquared = Eigen::Quaternionf(baseMat.quat.w, baseMat.quat.x, baseMat.quat.y, baseMat.quat.z).squaredNorm(); + if (std::abs(quatNormSquared) < std::numeric_limits::epsilon()) + { + baseMat.quat.w = 1.0f; + baseMat.transWeight = 2.0f; + } + else + { + baseMat.transWeight = 2.0f / quatNormSquared; + } + } + + static void CalculateBoneBounds(XBoneInfo& info, const unsigned boneIndex, const XModelCommon& common) + { + if (common.m_vertex_bone_weights.empty()) + return; + + info.bounds[0].x = 0.0f; + info.bounds[0].y = 0.0f; + info.bounds[0].z = 0.0f; + info.bounds[1].x = 0.0f; + info.bounds[1].y = 0.0f; + info.bounds[1].z = 0.0f; + info.offset.x = 0.0f; + info.offset.y = 0.0f; + info.offset.z = 0.0f; + info.radiusSquared = 0.0f; + + const auto vertexCount = common.m_vertex_bone_weights.size(); + for (auto vertexIndex = 0u; vertexIndex < vertexCount; vertexIndex++) + { + const auto& vertex = common.m_vertices[vertexIndex]; + const auto& vertexWeights = common.m_vertex_bone_weights[vertexIndex]; + const auto* weights = &common.m_bone_weight_data.weights[vertexWeights.weightOffset]; + for (auto weightIndex = 0u; weightIndex < vertexWeights.weightCount; weightIndex++) + { + const auto& weight = weights[weightIndex]; + if (weight.boneIndex != boneIndex) + continue; + + info.bounds[0].x = std::min(info.bounds[0].x, vertex.coordinates[0]); + info.bounds[0].y = std::min(info.bounds[0].y, vertex.coordinates[1]); + info.bounds[0].z = std::min(info.bounds[0].z, vertex.coordinates[2]); + info.bounds[1].x = std::max(info.bounds[1].x, vertex.coordinates[0]); + info.bounds[1].y = std::max(info.bounds[1].y, vertex.coordinates[1]); + info.bounds[1].z = std::max(info.bounds[1].z, vertex.coordinates[2]); + } + } + + const Eigen::Vector3f minEigen(info.bounds[0].x, info.bounds[0].y, info.bounds[0].z); + const Eigen::Vector3f maxEigen(info.bounds[1].x, info.bounds[1].y, info.bounds[1].z); + const Eigen::Vector3f boundsCenter = (minEigen + maxEigen) * 0.5f; + info.offset.x = boundsCenter.x(); + info.offset.y = boundsCenter.y(); + info.offset.z = boundsCenter.z(); + info.radiusSquared = Eigen::Vector3f(maxEigen - boundsCenter).squaredNorm(); + } + + bool ApplyCommonBonesToXModel(const JsonXModelLod& jLod, XModel& xmodel, unsigned lodNumber, const XModelCommon& common) const + { + if (common.m_bones.empty()) + return true; + + m_part_classification_state.Load(m_manager); + + const auto boneCount = common.m_bones.size(); + constexpr auto maxBones = std::numeric_limits::max(); + if (boneCount > maxBones) + { + PrintError(xmodel, std::format("Model \"{}\" for lod {} contains too many bones ({} -> max={})", jLod.file, lodNumber, boneCount, maxBones)); + return false; + } + + xmodel.numRootBones = 0u; + xmodel.numBones = 0u; + for (const auto& bone : common.m_bones) + { + if (!bone.parentIndex) + { + // Make sure root bones are at the beginning + assert(xmodel.numRootBones == xmodel.numBones); + xmodel.numRootBones++; + } + + xmodel.numBones++; + } + + xmodel.boneNames = m_memory.Alloc(xmodel.numBones); + xmodel.partClassification = m_memory.Alloc(xmodel.numBones); + xmodel.baseMat = m_memory.Alloc(xmodel.numBones); + xmodel.boneInfo = m_memory.Alloc(xmodel.numBones); + + if (xmodel.numBones > xmodel.numRootBones) + { + xmodel.parentList = m_memory.Alloc(xmodel.numBones - xmodel.numRootBones); + xmodel.trans = m_memory.Alloc(xmodel.numBones - xmodel.numRootBones); + xmodel.quats = m_memory.Alloc(xmodel.numBones - xmodel.numRootBones); + } + else + { + xmodel.parentList = nullptr; + xmodel.trans = nullptr; + xmodel.quats = nullptr; + } + + for (auto boneIndex = 0u; boneIndex < boneCount; boneIndex++) + { + const auto& bone = common.m_bones[boneIndex]; + xmodel.boneNames[boneIndex] = m_script_strings.AddOrGetScriptString(bone.name); + xmodel.partClassification[boneIndex] = static_cast(m_part_classification_state.GetPartClassificationForBoneName(bone.name)); + + ApplyBasePose(xmodel.baseMat[boneIndex], bone); + CalculateBoneBounds(xmodel.boneInfo[boneIndex], boneIndex, common); + + // Other boneInfo data is filled when calculating bone bounds + xmodel.boneInfo[boneIndex].collmap = -1; + + if (xmodel.numRootBones < boneIndex) + { + const auto nonRootIndex = boneIndex - xmodel.numRootBones; + xmodel.parentList[nonRootIndex] = static_cast(bone.parentIndex.value_or(0u)); + + auto& trans = xmodel.trans[nonRootIndex]; + trans.x = bone.localOffset[0]; + trans.y = bone.localOffset[1]; + trans.z = bone.localOffset[2]; + + auto& quats = xmodel.quats[nonRootIndex]; + quats.v[0] = QuatInt16::ToInt16(bone.localRotation.x); + quats.v[1] = QuatInt16::ToInt16(bone.localRotation.y); + quats.v[2] = QuatInt16::ToInt16(bone.localRotation.z); + quats.v[3] = QuatInt16::ToInt16(bone.localRotation.w); + } + } + + return true; + } + + [[nodiscard]] bool VerifyBones(const JsonXModelLod& jLod, const XModel& xmodel, unsigned lodNumber, const XModelCommon& common) const + { + // This method currently only checks names + // This does not necessarily verify correctness entirely. + // It is most likely enough to catch accidental errors, however. + + const auto commonBoneCount = common.m_bones.size(); + if (xmodel.numBones != commonBoneCount) + { + PrintError(xmodel, + std::format(R"(Model "{}" for lod "{}" has different bone count compared to lod 0 ({} != {}))", + jLod.file, + lodNumber, + xmodel.numBones, + commonBoneCount)); + return false; + } + + for (auto boneIndex = 0u; boneIndex < commonBoneCount; boneIndex++) + { + const auto& commonBone = common.m_bones[boneIndex]; + + const auto& boneName = m_script_strings[xmodel.boneNames[boneIndex]]; + if (commonBone.name != boneName) + { + PrintError(xmodel, + std::format(R"(Model "{}" for lod "{}" has different bone names compared to lod 0 (Index {}: {} != {}))", + jLod.file, + lodNumber, + boneIndex, + boneName, + commonBone.name)); + return false; + } + } + + return true; + } + + static void CreateVertex(GfxPackedVertex& vertex, const XModelVertex& commonVertex) + { + constexpr float wrongTangent[]{1, 0, 0}; + + vertex.xyz.x = commonVertex.coordinates[0]; + vertex.xyz.y = commonVertex.coordinates[1]; + vertex.xyz.z = commonVertex.coordinates[2]; + vertex.binormalSign = 1.0f; // TODO: Fill with actual value + vertex.color = Common::Vec4PackGfxColor(commonVertex.color); + vertex.texCoord = Common::Vec2PackTexCoords(commonVertex.uv); + vertex.normal = Common::Vec3PackUnitVec(commonVertex.normal); + vertex.tangent = Common::Vec3PackUnitVec(wrongTangent); // TODO: Fill with actual value + } + + bool CreateXSurface(XSurface& surface, const XModelObject& commonObject, const XModelCommon& common) + { + std::vector verts; + std::unordered_map usedVertices; + + surface.triCount = static_cast(commonObject.m_faces.size()); + surface.triIndices = m_memory.Alloc(commonObject.m_faces.size()); + + const auto faceCount = commonObject.m_faces.size(); + for (auto faceIndex = 0u; faceIndex < faceCount; faceIndex++) + { + const auto& face = commonObject.m_faces[faceIndex]; + auto& tris = surface.triIndices[faceIndex]; + + for (auto faceVertexIndex = 0u; faceVertexIndex < std::extent_v; faceVertexIndex++) + { + const auto vertexIndex = face.vertexIndex[faceVertexIndex]; + const auto existingVertex = usedVertices.find(vertexIndex); + if (existingVertex == usedVertices.end()) + { + const auto newVertexIndex = verts.size(); + tris[faceVertexIndex] = static_cast(newVertexIndex); + + const auto& commonVertex = common.m_vertices[vertexIndex]; + GfxPackedVertex vertex{}; + + CreateVertex(vertex, commonVertex); + + verts.emplace_back(vertex); + usedVertices.emplace(vertexIndex, newVertexIndex); + } + else + tris[faceVertexIndex] = static_cast(existingVertex->second); + } + } + + surface.vertCount = static_cast(verts.size()); + surface.verts0 = m_memory.Alloc(verts.size()); + memcpy(surface.verts0, verts.data(), sizeof(GfxPackedVertex) * verts.size()); + + return true; + } + + bool LoadLod(const JsonXModelLod& jLod, XModel& xmodel, unsigned lodNumber) + { + const auto file = m_manager.GetAssetLoadingContext()->m_raw_search_path->Open(jLod.file); + if (!file.IsOpen()) + { + PrintError(xmodel, std::format("Failed to open file for lod {}: \"{}\"", lodNumber, jLod.file)); + return false; + } + + auto extension = fs::path(jLod.file).extension().string(); + utils::MakeStringLowerCase(extension); + + const auto common = LoadModelByExtension(*file.m_stream, extension); + if (!common) + { + PrintError(xmodel, std::format("Failure while trying to load model for lod {}: \"{}\"", lodNumber, jLod.file)); + return false; + } + + if (lodNumber == 0u) + { + if (!ApplyCommonBonesToXModel(jLod, xmodel, lodNumber, *common)) + return false; + } + else + { + if (!VerifyBones(jLod, xmodel, lodNumber, *common)) + return false; + } + + xmodel.lodInfo[lodNumber].surfIndex = static_cast(m_surfaces.size()); + xmodel.lodInfo[lodNumber].numsurfs = static_cast(common->m_objects.size()); + + return std::ranges::all_of(common->m_objects, + [this, &common](const XModelObject& commonObject) + { + XSurface surface{}; + if (!CreateXSurface(surface, commonObject, *common)) + return false; + + m_surfaces.emplace_back(surface); + return true; + }); + } + + bool CreateXModelFromJson(const JsonXModel& jXModel, XModel& xmodel) + { + auto lodNumber = 0u; + for (const auto& jLod : jXModel.lods) + LoadLod(jLod, xmodel, lodNumber++); + + xmodel.numsurfs = static_cast(m_surfaces.size()); + xmodel.surfs = m_memory.Alloc(xmodel.numsurfs); + memcpy(xmodel.surfs, m_surfaces.data(), sizeof(XSurface) * xmodel.numsurfs); + + if (jXModel.collLod && jXModel.collLod.value() >= 0) + { + if (static_cast(jXModel.collLod.value()) >= jXModel.lods.size()) + { + PrintError(xmodel, "Collision lod is not a valid lod"); + return false; + } + xmodel.collLod = static_cast(jXModel.collLod.value()); + } + else + xmodel.collLod = -1; if (jXModel.physPreset) { @@ -104,9 +539,13 @@ namespace return true; } + std::vector m_surfaces; + std::istream& m_stream; MemoryManager& m_memory; + ZoneScriptStrings& m_script_strings; IAssetLoadingManager& m_manager; + PartClassificationState& m_part_classification_state; std::set& m_dependencies; }; } // namespace @@ -117,7 +556,7 @@ namespace T6 std::istream& stream, XModel& xmodel, MemoryManager* memory, IAssetLoadingManager* manager, std::vector& dependencies) { std::set dependenciesSet; - const JsonLoader loader(stream, *memory, *manager, dependenciesSet); + JsonLoader loader(stream, *memory, *manager, dependenciesSet); dependencies.assign(dependenciesSet.cbegin(), dependenciesSet.cend()); diff --git a/src/ObjLoading/XModel/Gltf/GltfBinInput.cpp b/src/ObjLoading/XModel/Gltf/GltfBinInput.cpp new file mode 100644 index 00000000..1167c6b2 --- /dev/null +++ b/src/ObjLoading/XModel/Gltf/GltfBinInput.cpp @@ -0,0 +1,128 @@ +#include "GltfBinInput.h" + +#include "XModel/Gltf/GltfConstants.h" + +#include +#include +#include + +using namespace gltf; + +BinInput::BinInput() + : m_buffer_size(0u) +{ +} + +bool BinInput::GetEmbeddedBuffer(const void*& buffer, size_t& bufferSize) const +{ + if (!m_buffer || !m_buffer_size) + return false; + + buffer = m_buffer.get(); + bufferSize = m_buffer_size; + + return true; +} + +const nlohmann::json& BinInput::GetJson() const +{ + if (!m_json) + throw std::exception(); + + return *m_json; +} + +bool BinInput::ReadGltfData(std::istream& stream) +{ + uint32_t magic; + if (!Read(stream, &magic, sizeof(magic), "magic")) + return false; + + if (magic != GLTF_MAGIC) + { + std::cerr << "Invalid magic when trying to read GLB\n"; + return false; + } + + uint32_t version; + if (!Read(stream, &version, sizeof(version), "version")) + return false; + + if (version != GLTF_VERSION) + { + std::cerr << std::format("Unsupported version {} when trying to read GLB: Expected version {}\n", version, GLTF_VERSION); + return false; + } + + uint32_t fileLength; + if (!Read(stream, &fileLength, sizeof(fileLength), "file length")) + return false; + + while (true) + { + uint32_t chunkLength; + uint32_t chunkMagic; + if (!Read(stream, &chunkLength, sizeof(chunkLength), "chunk length", false)) + break; + + if (!Read(stream, &chunkMagic, sizeof(chunkMagic), "chunk magic")) + return false; + + if (chunkMagic == CHUNK_MAGIC_JSON) + { + const auto jsonBuffer = std::make_unique(chunkLength + 1); + if (!Read(stream, jsonBuffer.get(), chunkLength, "json")) + return false; + jsonBuffer[chunkLength] = 0u; + + try + { + m_json = std::make_unique(nlohmann::json::parse(jsonBuffer.get(), &jsonBuffer[chunkLength])); + } + catch (const nlohmann::json::exception& e) + { + std::cerr << std::format("Failed trying to parse JSON of GLB: {}\n", e.what()); + return false; + } + } + else if (chunkMagic == CHUNK_MAGIC_BIN) + { + m_buffer = std::make_unique(chunkLength); + m_buffer_size = chunkLength; + + if (!Read(stream, m_buffer.get(), m_buffer_size, "bin buffer")) + return false; + } + else + Skip(stream, chunkLength); + + if (chunkLength % 4u > 0) + Skip(stream, 4u - (chunkLength % 4u)); + } + + if (!m_json) + { + std::cerr << "Failed to load GLB due to missing JSON\n"; + return false; + } + + return true; +} + +bool BinInput::Read(std::istream& stream, void* dest, const size_t dataSize, const char* readTypeName, const bool errorWhenFailed) +{ + stream.read(static_cast(dest), dataSize); + if (stream.gcount() != dataSize) + { + if (errorWhenFailed) + std::cerr << std::format("Unexpected EOF while reading GLB {}\n", readTypeName); + return false; + } + + return true; +} + +void BinInput::Skip(std::istream& stream, const size_t skipLength) +{ + stream.seekg(skipLength, std::ios::cur); +} diff --git a/src/ObjLoading/XModel/Gltf/GltfBinInput.h b/src/ObjLoading/XModel/Gltf/GltfBinInput.h new file mode 100644 index 00000000..39b75549 --- /dev/null +++ b/src/ObjLoading/XModel/Gltf/GltfBinInput.h @@ -0,0 +1,26 @@ +#pragma once + +#include "GltfInput.h" + +#include + +namespace gltf +{ + class BinInput final : public Input + { + public: + BinInput(); + + bool ReadGltfData(std::istream& stream) override; + bool GetEmbeddedBuffer(const void*& buffer, size_t& bufferSize) const override; + [[nodiscard]] const nlohmann::json& GetJson() const override; + + private: + static bool Read(std::istream& stream, void* dest, size_t dataSize, const char* readTypeName, bool errorWhenFailed = true); + static void Skip(std::istream& stream, size_t skipLength); + + std::unique_ptr m_json; + std::unique_ptr m_buffer; + size_t m_buffer_size; + }; +} // namespace gltf diff --git a/src/ObjLoading/XModel/Gltf/GltfInput.h b/src/ObjLoading/XModel/Gltf/GltfInput.h new file mode 100644 index 00000000..6bb5486f --- /dev/null +++ b/src/ObjLoading/XModel/Gltf/GltfInput.h @@ -0,0 +1,24 @@ +#pragma once + +#include +#include + +namespace gltf +{ + class Input + { + protected: + Input() = default; + + public: + virtual ~Input() = default; + Input(const Input& other) = default; + Input(Input&& other) noexcept = default; + Input& operator=(const Input& other) = default; + Input& operator=(Input&& other) noexcept = default; + + virtual bool ReadGltfData(std::istream& stream) = 0; + virtual bool GetEmbeddedBuffer(const void*& buffer, size_t& bufferSize) const = 0; + [[nodiscard]] virtual const nlohmann::json& GetJson() const = 0; + }; +} // namespace gltf diff --git a/src/ObjLoading/XModel/Gltf/GltfLoader.cpp b/src/ObjLoading/XModel/Gltf/GltfLoader.cpp new file mode 100644 index 00000000..3de75c3f --- /dev/null +++ b/src/ObjLoading/XModel/Gltf/GltfLoader.cpp @@ -0,0 +1,688 @@ +#include "GltfLoader.h" + +#include "Internal/GltfAccessor.h" +#include "Internal/GltfBuffer.h" +#include "Internal/GltfBufferView.h" + +#pragma warning(push, 0) +#include +#pragma warning(pop) + +#include +#include +#include +#include +#include +#include +#include + +using namespace gltf; + +namespace +{ + struct AccessorsForVertex + { + unsigned positionAccessor; + std::optional normalAccessor; + std::optional colorAccessor; + std::optional uvAccessor; + std::optional jointsAccessor; + std::optional weightsAccessor; + + friend bool operator==(const AccessorsForVertex& lhs, const AccessorsForVertex& rhs) + { + return lhs.positionAccessor == rhs.positionAccessor && lhs.normalAccessor == rhs.normalAccessor && lhs.colorAccessor == rhs.colorAccessor + && lhs.uvAccessor == rhs.uvAccessor && lhs.jointsAccessor == rhs.jointsAccessor && lhs.weightsAccessor == rhs.weightsAccessor; + } + + friend bool operator!=(const AccessorsForVertex& lhs, const AccessorsForVertex& rhs) + { + return !(lhs == rhs); + } + }; +} // namespace + +template<> struct std::hash +{ + std::size_t operator()(const AccessorsForVertex& v) const noexcept + { + std::size_t seed = 0x7E42C0E6; + seed ^= (seed << 6) + (seed >> 2) + 0x47B15429 + static_cast(v.positionAccessor); + seed ^= (seed << 6) + (seed >> 2) + 0x66847B5C + std::hash>()(v.normalAccessor); + seed ^= (seed << 6) + (seed >> 2) + 0x77399D60 + std::hash>()(v.colorAccessor); + seed ^= (seed << 6) + (seed >> 2) + 0x477AF9AB + std::hash>()(v.uvAccessor); + seed ^= (seed << 6) + (seed >> 2) + 0x4421B4D9 + std::hash>()(v.jointsAccessor); + seed ^= (seed << 6) + (seed >> 2) + 0x13C2EBA1 + std::hash>()(v.weightsAccessor); + return seed; + } +}; + +namespace +{ + class GltfLoadException final : std::exception + { + public: + explicit GltfLoadException(std::string message) + : m_message(std::move(message)) + { + } + + [[nodiscard]] const std::string& Str() const + { + return m_message; + } + + [[nodiscard]] const char* what() const override + { + return m_message.c_str(); + } + + private: + std::string m_message; + }; + + struct ObjectToLoad + { + unsigned meshIndex; + std::optional skinIndex; + + ObjectToLoad(const unsigned meshIndex, const std::optional skinIndex) + : meshIndex(meshIndex), + skinIndex(skinIndex) + { + } + }; + + class GltfLoaderImpl final : public Loader + { + public: + explicit GltfLoaderImpl(const Input* input) + : m_input(input) + { + } + + static std::vector GetRootNodes(const JsonRoot& jRoot) + { + if (!jRoot.nodes || jRoot.nodes->empty()) + return {}; + + const auto nodeCount = jRoot.nodes->size(); + std::vector rootNodes; + std::vector isChild(nodeCount); + + for (const auto& node : jRoot.nodes.value()) + { + if (!node.children) + continue; + + for (const auto childIndex : node.children.value()) + { + if (childIndex >= nodeCount) + throw GltfLoadException("Illegal child index"); + + if (isChild[childIndex]) + throw GltfLoadException("Node hierarchy is not a set of disjoint strict trees"); + + isChild[childIndex] = true; + } + } + + for (auto nodeIndex = 0u; nodeIndex < nodeCount; nodeIndex++) + { + if (!isChild[nodeIndex]) + rootNodes.emplace_back(nodeIndex); + } + + return rootNodes; + } + + void TraverseNodes(const JsonRoot& jRoot) + { + // Make sure there are any nodes to traverse + if (!jRoot.nodes || jRoot.nodes->empty()) + return; + + std::deque nodeQueue; + std::vector rootNodes = GetRootNodes(jRoot); + + for (const auto rootNode : rootNodes) + nodeQueue.emplace_back(rootNode); + + while (!nodeQueue.empty()) + { + const auto& node = jRoot.nodes.value()[nodeQueue.front()]; + nodeQueue.pop_front(); + + if (node.children) + { + for (const auto childIndex : *node.children) + nodeQueue.emplace_back(childIndex); + } + + if (node.mesh) + m_load_objects.emplace_back(*node.mesh, node.skin); + } + } + + std::optional GetAccessorForIndex(const char* attributeName, + const std::optional index, + std::initializer_list allowedAccessorTypes, + std::initializer_list allowedAccessorComponentTypes) const + { + if (!index) + return std::nullopt; + + if (*index > m_accessors.size()) + throw GltfLoadException(std::format("Index for {} accessor out of bounds", attributeName)); + + auto* accessor = m_accessors[*index].get(); + + const auto maybeType = accessor->GetType(); + if (maybeType) + { + if (std::ranges::find(allowedAccessorTypes, *maybeType) == allowedAccessorTypes.end()) + throw GltfLoadException(std::format("Accessor for {} has unsupported type {}", attributeName, static_cast(*maybeType))); + } + + const auto maybeComponentType = accessor->GetComponentType(); + if (maybeComponentType) + { + if (std::ranges::find(allowedAccessorComponentTypes, *maybeComponentType) == allowedAccessorComponentTypes.end()) + throw GltfLoadException( + std::format("Accessor for {} has unsupported component type {}", attributeName, static_cast(*maybeComponentType))); + } + + return accessor; + } + + static void VerifyAccessorVertexCount(const char* accessorType, const Accessor* accessor, const size_t vertexCount) + { + if (accessor->GetCount() != vertexCount) + throw GltfLoadException(std::format("Element count of {} accessor does not match expected vertex count of {}", accessorType, vertexCount)); + } + + unsigned CreateVertices(XModelCommon& common, const AccessorsForVertex& accessorsForVertex) + { + // clang-format off + auto* positionAccessor = GetAccessorForIndex( + "POSITION", + accessorsForVertex.positionAccessor, + {JsonAccessorType::VEC3}, + {JsonAccessorComponentType::FLOAT} + ).value_or(nullptr); + // clang-format on + + const auto vertexCount = positionAccessor->GetCount(); + NullAccessor nullAccessor(vertexCount); + + // clang-format off + auto* normalAccessor = GetAccessorForIndex( + "NORMAL", + accessorsForVertex.normalAccessor, + {JsonAccessorType::VEC3}, + {JsonAccessorComponentType::FLOAT} + ).value_or(&nullAccessor); + VerifyAccessorVertexCount("NORMAL", normalAccessor, vertexCount); + + auto* uvAccessor = GetAccessorForIndex( + "TEXCOORD_0", + accessorsForVertex.uvAccessor, + {JsonAccessorType::VEC2}, + {JsonAccessorComponentType::FLOAT, JsonAccessorComponentType::UNSIGNED_BYTE, JsonAccessorComponentType::UNSIGNED_SHORT} + ).value_or(&nullAccessor); + VerifyAccessorVertexCount("TEXCOORD_0", uvAccessor, vertexCount); + + auto* colorAccessor = GetAccessorForIndex( + "COLOR_0", + accessorsForVertex.colorAccessor, + {JsonAccessorType::VEC3, JsonAccessorType::VEC4}, + {JsonAccessorComponentType::FLOAT, JsonAccessorComponentType::UNSIGNED_BYTE, JsonAccessorComponentType::UNSIGNED_SHORT} + ).value_or(&nullAccessor); + VerifyAccessorVertexCount("COLOR_0", colorAccessor, vertexCount); + + auto* jointsAccessor = GetAccessorForIndex( + "JOINTS_0", + accessorsForVertex.jointsAccessor, + {JsonAccessorType::VEC4}, + {JsonAccessorComponentType::UNSIGNED_BYTE, JsonAccessorComponentType::UNSIGNED_SHORT} + ).value_or(&nullAccessor); + VerifyAccessorVertexCount("JOINTS_0", jointsAccessor, vertexCount); + + auto* weightsAccessor = GetAccessorForIndex( + "WEIGHTS_0", + accessorsForVertex.weightsAccessor, + {JsonAccessorType::VEC4}, + {JsonAccessorComponentType::FLOAT, JsonAccessorComponentType::UNSIGNED_BYTE, JsonAccessorComponentType::UNSIGNED_SHORT} + ).value_or(&nullAccessor); + VerifyAccessorVertexCount("WEIGHTS_0", weightsAccessor, vertexCount); + // clang-format on + + const auto vertexOffset = common.m_vertices.size(); + common.m_vertices.reserve(vertexOffset + vertexCount); + common.m_vertex_bone_weights.reserve(vertexOffset + vertexCount); + for (auto vertexIndex = 0u; vertexIndex < vertexCount; vertexIndex++) + { + XModelVertex vertex; + + unsigned joints[4]; + float weights[4]; + + if (!positionAccessor->GetFloatVec3(vertexIndex, vertex.coordinates) || !normalAccessor->GetFloatVec3(vertexIndex, vertex.normal) + || !colorAccessor->GetFloatVec4(vertexIndex, vertex.color) || !colorAccessor->GetFloatVec2(vertexIndex, vertex.uv) + || !jointsAccessor->GetUnsignedVec4(vertexIndex, joints) || !weightsAccessor->GetFloatVec4(vertexIndex, weights)) + { + return false; + } + + common.m_vertices.emplace_back(vertex); + + XModelVertexBoneWeights vertexWeights{common.m_bone_weight_data.weights.size(), 0u}; + for (auto i = 0u; i < std::extent_v; i++) + { + if (std::abs(weights[i]) < std::numeric_limits::epsilon()) + continue; + + common.m_bone_weight_data.weights.emplace_back(joints[i], weights[i]); + vertexWeights.weightCount++; + } + + common.m_vertex_bone_weights.emplace_back(vertexWeights); + } + + m_vertex_offset_for_accessors.emplace(accessorsForVertex, vertexOffset); + return vertexOffset; + } + + bool ConvertObject(const JsonRoot& jRoot, const JsonMeshPrimitives& primitives, XModelCommon& common, XModelObject& object) + { + if (!primitives.indices) + throw GltfLoadException("Requires primitives indices"); + if (!primitives.material) + throw GltfLoadException("Requires primitives material"); + if (primitives.mode.value_or(JsonMeshPrimitivesMode::TRIANGLES) != JsonMeshPrimitivesMode::TRIANGLES) + throw GltfLoadException("Only triangles are supported"); + if (!primitives.attributes.POSITION) + throw GltfLoadException("Requires primitives attribute POSITION"); + + AccessorsForVertex accessorsForVertex{ + *primitives.attributes.POSITION, + primitives.attributes.NORMAL, + primitives.attributes.COLOR_0, + primitives.attributes.TEXCOORD_0, + primitives.attributes.JOINTS_0, + primitives.attributes.WEIGHTS_0, + }; + + const auto existingVertices = m_vertex_offset_for_accessors.find(accessorsForVertex); + const auto vertexOffset = + existingVertices == m_vertex_offset_for_accessors.end() ? CreateVertices(common, accessorsForVertex) : existingVertices->second; + + // clang-format off + auto* indexAccessor = GetAccessorForIndex( + "INDICES", + primitives.indices, + {JsonAccessorType::SCALAR}, + {JsonAccessorComponentType::UNSIGNED_BYTE, JsonAccessorComponentType::UNSIGNED_SHORT, JsonAccessorComponentType::UNSIGNED_INT} + ).value_or(nullptr); + // clang-format on + + const auto indexCount = indexAccessor->GetCount(); + if (indexCount % 3 != 0) + throw GltfLoadException("Index count must be dividable by 3 for triangles"); + const auto faceCount = indexCount / 3u; + object.m_faces.reserve(faceCount); + + for (auto faceIndex = 0u; faceIndex < faceCount; faceIndex++) + { + unsigned indices[3]; + if (!indexAccessor->GetUnsigned(faceIndex * 3u + 0u, indices[0]) || !indexAccessor->GetUnsigned(faceIndex * 3u + 1u, indices[1]) + || !indexAccessor->GetUnsigned(faceIndex * 3u + 2u, indices[2])) + { + return false; + } + + object.m_faces.emplace_back(XModelFace{ + vertexOffset + indices[0], + vertexOffset + indices[1], + vertexOffset + indices[2], + }); + } + + if (!jRoot.materials || *primitives.material >= jRoot.materials->size()) + throw GltfLoadException("Invalid material index"); + + object.materialIndex = static_cast(*primitives.material); + + return true; + } + + static std::optional GetRootNodeForSkin(const JsonRoot& jRoot, const JsonSkin& skin) + { + if (!jRoot.nodes || skin.joints.empty()) + return std::nullopt; + + const auto jointCount = skin.joints.size(); + auto rootCount = jointCount; + std::vector isRoot(jointCount, true); + + for (const auto joint : skin.joints) + { + if (jRoot.nodes->size() <= joint) + throw GltfLoadException("Invalid joint index"); + + const auto& node = jRoot.nodes.value()[joint]; + if (node.children) + { + for (const auto child : *node.children) + { + const auto foundChildJoint = std::ranges::find(skin.joints, child); + if (foundChildJoint != skin.joints.end()) + { + const auto foundChildJointIndex = std::distance(skin.joints.begin(), foundChildJoint); + if (isRoot[foundChildJointIndex]) + { + isRoot[foundChildJointIndex] = false; + rootCount--; + } + } + } + } + } + + if (rootCount != 1) + throw GltfLoadException("Skins must have exactly one common root node"); + + for (auto index = 0u; index < jointCount; index++) + { + if (isRoot[index]) + return skin.joints[index]; + } + + return std::nullopt; + } + + static bool ConvertJoint(const JsonRoot& jRoot, + XModelCommon& common, + const unsigned nodeIndex, + const std::optional parentIndex, + const float (&parentOffset)[3], + const XModelQuaternion& parentRotation, + const float (&parentScale)[3]) + { + if (!jRoot.nodes || nodeIndex >= jRoot.nodes->size()) + return false; + + const auto& node = jRoot.nodes.value()[nodeIndex]; + + XModelBone bone; + bone.name = node.name.value_or(std::string()); + bone.parentIndex = parentIndex; + + if (node.scale) + { + bone.scale[0] = parentScale[0] * (*node.scale)[0]; + bone.scale[1] = parentScale[1] * (*node.scale)[1]; + bone.scale[2] = parentScale[2] * (*node.scale)[2]; + } + else + { + bone.scale[0] = parentScale[0]; + bone.scale[1] = parentScale[1]; + bone.scale[2] = parentScale[2]; + } + + if (node.translation) + { + bone.localOffset[0] = (*node.translation)[0]; + bone.localOffset[1] = -(*node.translation)[2]; + bone.localOffset[2] = (*node.translation)[1]; + } + else + { + bone.localOffset[0] = 0.0f; + bone.localOffset[1] = 0.0f; + bone.localOffset[2] = 0.0f; + } + + bone.globalOffset[0] = bone.localOffset[0] + parentOffset[0]; + bone.globalOffset[1] = bone.localOffset[1] + parentOffset[1]; + bone.globalOffset[2] = bone.localOffset[2] + parentOffset[2]; + + 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]; + } + else + { + bone.localRotation.x = 0.0f; + bone.localRotation.y = 0.0f; + bone.localRotation.z = 0.0f; + bone.localRotation.w = 1.0f; + } + + 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 = localRotationEigen * parentRotationEigen; + + bone.globalRotation.x = globalRotationEigen.x(); + bone.globalRotation.y = globalRotationEigen.y(); + bone.globalRotation.z = globalRotationEigen.z(); + bone.globalRotation.w = globalRotationEigen.w(); + + const auto commonIndex = common.m_bones.size(); + common.m_bones.emplace_back(std::move(bone)); + + if (node.children) + { + for (const auto childIndex : *node.children) + { + if (!ConvertJoint(jRoot, common, childIndex, commonIndex, bone.globalOffset, bone.globalRotation, bone.scale)) + return false; + } + } + + return true; + } + + static bool ConvertSkin(const JsonRoot& jRoot, const JsonSkin& skin, XModelCommon& common) + { + if (skin.joints.empty()) + return true; + if (!jRoot.nodes) + return false; + + if (!common.m_bones.empty()) + throw GltfLoadException("Only scenes with at most one skin are supported"); + + const auto rootNode = GetRootNodeForSkin(jRoot, skin).value_or(skin.joints[0]); + + constexpr float defaultTranslation[3]{0.0f, 0.0f, 0.0f}; + constexpr XModelQuaternion defaultRotation{0.0f, 0.0f, 0.0f, 1.0f}; + constexpr float defaultScale[3]{1.0f, 1.0f, 1.0f}; + + return ConvertJoint(jRoot, common, rootNode, std::nullopt, defaultTranslation, defaultRotation, defaultScale); + } + + void ConvertObjects(const JsonRoot& jRoot, XModelCommon& common) + { + if (!jRoot.meshes) + return; + + for (const auto& loadObject : m_load_objects) + { + if (loadObject.skinIndex && jRoot.skins) + { + const auto& skin = jRoot.skins.value()[*loadObject.skinIndex]; + if (!ConvertSkin(jRoot, skin, common)) + return; + } + + const auto& mesh = jRoot.meshes.value()[loadObject.meshIndex]; + + common.m_objects.reserve(common.m_objects.size() + mesh.primitives.size()); + for (const auto& primitives : mesh.primitives) + { + XModelObject object; + if (!ConvertObject(jRoot, primitives, common, object)) + return; + + common.m_objects.emplace_back(std::move(object)); + } + } + } + + static void ConvertMaterials(const JsonRoot& jRoot, XModelCommon& common) + { + if (!jRoot.materials) + return; + + common.m_materials.reserve(jRoot.materials->size()); + for (const auto& jMaterial : *jRoot.materials) + { + XModelMaterial material; + material.ApplyDefaults(); + + if (jMaterial.name) + material.name = *jMaterial.name; + else + throw GltfLoadException("Materials must have a name"); + + common.m_materials.emplace_back(std::move(material)); + } + } + + void CreateBuffers(const JsonRoot& jRoot) + { + if (!jRoot.buffers) + return; + + m_buffers.reserve(jRoot.buffers->size()); + for (const auto& jBuffer : *jRoot.buffers) + { + if (!jBuffer.uri) + { + const void* embeddedBufferPtr = nullptr; + size_t embeddedBufferSize = 0u; + if (!m_input->GetEmbeddedBuffer(embeddedBufferPtr, embeddedBufferSize) || embeddedBufferSize == 0u) + throw GltfLoadException("Buffer tried to access embedded data when there is none"); + + m_buffers.emplace_back(std::make_unique(embeddedBufferPtr, embeddedBufferSize)); + } + else if (DataUriBuffer::IsDataUri(*jBuffer.uri)) + { + auto dataUriBuffer = std::make_unique(); + if (!dataUriBuffer->ReadDataFromUri(*jBuffer.uri)) + throw GltfLoadException("Buffer has invalid data uri"); + + m_buffers.emplace_back(std::move(dataUriBuffer)); + } + else + { + throw GltfLoadException("File buffers are not supported"); + } + } + } + + void CreateBufferViews(const JsonRoot& jRoot) + { + if (!jRoot.bufferViews) + return; + + m_buffer_views.reserve(jRoot.bufferViews->size()); + for (const auto& jBufferView : *jRoot.bufferViews) + { + if (jBufferView.buffer >= m_buffers.size()) + throw GltfLoadException("Buffer view references invalid buffer"); + + const auto* buffer = m_buffers[jBufferView.buffer].get(); + const auto offset = jBufferView.byteOffset.value_or(0u); + const auto length = jBufferView.byteLength; + const auto stride = jBufferView.byteStride.value_or(0u); + + if (offset + length > buffer->GetSize()) + throw GltfLoadException("Buffer view is defined larger as underlying buffer"); + + m_buffer_views.emplace_back(std::make_unique(buffer, offset, length, stride)); + } + } + + void CreateAccessors(const JsonRoot& jRoot) + { + if (!jRoot.accessors) + return; + + m_accessors.reserve(jRoot.accessors->size()); + for (const auto& jAccessor : *jRoot.accessors) + { + if (!jAccessor.bufferView) + { + m_accessors.emplace_back(std::make_unique(jAccessor.count)); + continue; + } + + if (*jAccessor.bufferView >= m_buffer_views.size()) + throw GltfLoadException("Accessor references invalid buffer view"); + + const auto* bufferView = m_buffer_views[*jAccessor.bufferView].get(); + if (jAccessor.componentType == JsonAccessorComponentType::FLOAT) + m_accessors.emplace_back(std::make_unique(bufferView, jAccessor.type, jAccessor.count)); + else if (jAccessor.componentType == JsonAccessorComponentType::UNSIGNED_BYTE) + m_accessors.emplace_back(std::make_unique(bufferView, jAccessor.type, jAccessor.count)); + else if (jAccessor.componentType == JsonAccessorComponentType::UNSIGNED_SHORT) + m_accessors.emplace_back(std::make_unique(bufferView, jAccessor.type, jAccessor.count)); + else + throw GltfLoadException(std::format("Accessor has unsupported component type {}", static_cast(jAccessor.componentType))); + } + } + + std::unique_ptr Load() override + { + JsonRoot jRoot; + try + { + jRoot = m_input->GetJson().get(); + } + catch (const nlohmann::json::exception& e) + { + std::cerr << std::format("Failed to parse GLTF JSON: {}\n", e.what()); + return nullptr; + } + + try + { + CreateBuffers(jRoot); + CreateBufferViews(jRoot); + CreateAccessors(jRoot); + + TraverseNodes(jRoot); + + auto common = std::make_unique(); + ConvertObjects(jRoot, *common); + ConvertMaterials(jRoot, *common); + + return common; + } + catch (const GltfLoadException& e) + { + std::cerr << std::format("Failed to load GLTF: {}\n", e.Str()); + return nullptr; + } + } + + private: + const Input* m_input; + std::vector m_load_objects; + std::unordered_map m_vertex_offset_for_accessors; + std::vector> m_accessors; + std::vector> m_buffer_views; + std::vector> m_buffers; + }; +} // namespace + +std::unique_ptr Loader::CreateLoader(const Input* input) +{ + return std::make_unique(input); +} diff --git a/src/ObjLoading/XModel/Gltf/GltfLoader.h b/src/ObjLoading/XModel/Gltf/GltfLoader.h new file mode 100644 index 00000000..b12d8d98 --- /dev/null +++ b/src/ObjLoading/XModel/Gltf/GltfLoader.h @@ -0,0 +1,24 @@ +#pragma once + +#include "GltfInput.h" +#include "XModel/Gltf/JsonGltf.h" +#include "XModel/XModelLoader.h" + +#include +#include + +namespace gltf +{ + class Loader : public XModelLoader + { + public: + Loader() = default; + ~Loader() override = default; + Loader(const Loader& other) = default; + Loader(Loader&& other) noexcept = default; + Loader& operator=(const Loader& other) = default; + Loader& operator=(Loader&& other) noexcept = default; + + static std::unique_ptr CreateLoader(const Input* input); + }; +} // namespace gltf diff --git a/src/ObjLoading/XModel/Gltf/GltfTextInput.cpp b/src/ObjLoading/XModel/Gltf/GltfTextInput.cpp new file mode 100644 index 00000000..ff885002 --- /dev/null +++ b/src/ObjLoading/XModel/Gltf/GltfTextInput.cpp @@ -0,0 +1,48 @@ +#include "GltfTextInput.h" + +#include +#include +#include +#include + +using namespace gltf; + +TextInput::TextInput() + : m_buffer_size(0u) +{ +} + +bool TextInput::GetEmbeddedBuffer(const void*& buffer, size_t& bufferSize) const +{ + if (!m_buffer || !m_buffer_size) + return false; + + buffer = m_buffer.get(); + bufferSize = m_buffer_size; + + return true; +} + +const nlohmann::json& TextInput::GetJson() const +{ + if (!m_json) + throw std::exception(); + + return *m_json; +} + +bool TextInput::ReadGltfData(std::istream& stream) +{ + try + { + m_json = std::make_unique(nlohmann::json::parse(stream)); + + return true; + } + catch (nlohmann::json::exception& e) + { + std::cerr << std::format("Failed to parse json of GLTF: {}", e.what()); + } + + return false; +} diff --git a/src/ObjLoading/XModel/Gltf/GltfTextInput.h b/src/ObjLoading/XModel/Gltf/GltfTextInput.h new file mode 100644 index 00000000..95e15364 --- /dev/null +++ b/src/ObjLoading/XModel/Gltf/GltfTextInput.h @@ -0,0 +1,23 @@ +#pragma once + +#include "GltfInput.h" + +#include + +namespace gltf +{ + class TextInput final : public Input + { + public: + TextInput(); + + bool ReadGltfData(std::istream& stream) override; + bool GetEmbeddedBuffer(const void*& buffer, size_t& bufferSize) const override; + [[nodiscard]] const nlohmann::json& GetJson() const override; + + private: + std::unique_ptr m_json; + std::unique_ptr m_buffer; + size_t m_buffer_size; + }; +} // namespace gltf diff --git a/src/ObjLoading/XModel/Gltf/Internal/GltfAccessor.cpp b/src/ObjLoading/XModel/Gltf/Internal/GltfAccessor.cpp new file mode 100644 index 00000000..f33ff546 --- /dev/null +++ b/src/ObjLoading/XModel/Gltf/Internal/GltfAccessor.cpp @@ -0,0 +1,355 @@ +#include "GltfAccessor.h" + +using namespace gltf; + +NullAccessor::NullAccessor(const size_t count) + : m_count(count) +{ +} + +bool NullAccessor::GetFloatVec2(const size_t index, float (&out)[2]) const +{ + if (index >= m_count) + return false; + + out[0] = 0.0f; + out[1] = 0.0f; + + return true; +} + +bool NullAccessor::GetFloatVec3(const size_t index, float (&out)[3]) const +{ + if (index >= m_count) + return false; + + out[0] = 0.0f; + out[1] = 0.0f; + out[2] = 0.0f; + + return true; +} + +bool NullAccessor::GetFloatVec4(const size_t index, float (&out)[4]) const +{ + if (index >= m_count) + return false; + + out[0] = 0.0f; + out[1] = 0.0f; + out[2] = 0.0f; + out[3] = 0.0f; + + return true; +} + +bool NullAccessor::GetUnsigned(const size_t index, unsigned& out) const +{ + if (index >= m_count) + return false; + + out = 0u; + + return true; +} + +bool NullAccessor::GetUnsignedVec4(const size_t index, unsigned (&out)[4]) const +{ + if (index >= m_count) + return false; + + out[0] = 0u; + out[1] = 0u; + out[2] = 0u; + out[3] = 0u; + + return true; +} + +std::optional NullAccessor::GetComponentType() const +{ + return std::nullopt; +} + +std::optional NullAccessor::GetType() const +{ + return std::nullopt; +} + +size_t NullAccessor::GetCount() const +{ + return m_count; +} + +FloatAccessor::FloatAccessor(const BufferView* bufferView, const JsonAccessorType type, const size_t count) + : m_buffer_view(bufferView), + m_type(type), + m_count(count) +{ +} + +std::optional FloatAccessor::GetType() const +{ + return m_type; +} + +std::optional FloatAccessor::GetComponentType() const +{ + return JsonAccessorComponentType::FLOAT; +} + +size_t FloatAccessor::GetCount() const +{ + return m_count; +} + +bool FloatAccessor::GetFloatVec2(const size_t index, float (&out)[2]) const +{ + assert(m_type == JsonAccessorType::VEC2 || m_type == JsonAccessorType::VEC3 || m_type == JsonAccessorType::VEC4); + if (index >= m_count) + return false; + + return m_buffer_view->ReadElement(&out, index, sizeof(float[2])); +} + +bool FloatAccessor::GetFloatVec3(const size_t index, float (&out)[3]) const +{ + assert(m_type == JsonAccessorType::VEC3 || m_type == JsonAccessorType::VEC4); + if (index >= m_count) + return false; + + return m_buffer_view->ReadElement(&out, index, sizeof(float[3])); +} + +bool FloatAccessor::GetFloatVec4(const size_t index, float (&out)[4]) const +{ + assert(m_type == JsonAccessorType::VEC4); + if (index >= m_count) + return false; + + return m_buffer_view->ReadElement(&out, index, sizeof(float[4])); +} + +bool FloatAccessor::GetUnsigned(size_t index, unsigned& out) const +{ + return false; +} + +bool FloatAccessor::GetUnsignedVec4(size_t index, unsigned (&out)[4]) const +{ + return false; +} + +UnsignedByteAccessor::UnsignedByteAccessor(const BufferView* bufferView, const JsonAccessorType type, const size_t count) + : m_buffer_view(bufferView), + m_type(type), + m_count(count) +{ +} + +std::optional UnsignedByteAccessor::GetType() const +{ + return m_type; +} + +std::optional UnsignedByteAccessor::GetComponentType() const +{ + return JsonAccessorComponentType::UNSIGNED_BYTE; +} + +size_t UnsignedByteAccessor::GetCount() const +{ + return m_count; +} + +bool UnsignedByteAccessor::GetFloatVec2(const size_t index, float (&out)[2]) const +{ + assert(m_type == JsonAccessorType::VEC2 || m_type == JsonAccessorType::VEC3 || m_type == JsonAccessorType::VEC4); + if (index >= m_count) + return false; + + uint8_t temp[2]; + if (!m_buffer_view->ReadElement(temp, index, sizeof(uint8_t[2]))) + return false; + + // Return as normalized value between 0 and 1 + out[0] = static_cast(temp[0]) / static_cast(std::numeric_limits::max()); + out[1] = static_cast(temp[1]) / static_cast(std::numeric_limits::max()); + + return true; +} + +bool UnsignedByteAccessor::GetFloatVec3(const size_t index, float (&out)[3]) const +{ + assert(m_type == JsonAccessorType::VEC3 || m_type == JsonAccessorType::VEC4); + if (index >= m_count) + return false; + + uint8_t temp[3]; + if (!m_buffer_view->ReadElement(temp, index, sizeof(uint8_t[3]))) + return false; + + // Return as normalized value between 0 and 1 + out[0] = static_cast(temp[0]) / static_cast(std::numeric_limits::max()); + out[1] = static_cast(temp[1]) / static_cast(std::numeric_limits::max()); + out[2] = static_cast(temp[2]) / static_cast(std::numeric_limits::max()); + + return true; +} + +bool UnsignedByteAccessor::GetFloatVec4(const size_t index, float (&out)[4]) const +{ + assert(m_type == JsonAccessorType::VEC4); + if (index >= m_count) + return false; + + uint8_t temp[4]; + if (!m_buffer_view->ReadElement(temp, index, sizeof(uint8_t[4]))) + return false; + + // Return as normalized value between 0 and 1 + out[0] = static_cast(temp[0]) / static_cast(std::numeric_limits::max()); + out[1] = static_cast(temp[1]) / static_cast(std::numeric_limits::max()); + out[2] = static_cast(temp[2]) / static_cast(std::numeric_limits::max()); + out[3] = static_cast(temp[3]) / static_cast(std::numeric_limits::max()); + + return true; +} + +bool UnsignedByteAccessor::GetUnsigned(const size_t index, unsigned& out) const +{ + if (index >= m_count) + return false; + + uint8_t temp; + if (!m_buffer_view->ReadElement(&temp, index, sizeof(uint8_t))) + return false; + + out = temp; + return true; +} + +bool UnsignedByteAccessor::GetUnsignedVec4(const size_t index, unsigned (&out)[4]) const +{ + assert(m_type == JsonAccessorType::VEC4); + if (index >= m_count) + return false; + + uint8_t temp[4]; + if (!m_buffer_view->ReadElement(temp, index, sizeof(uint8_t[4]))) + return false; + + out[0] = static_cast(temp[0]); + out[1] = static_cast(temp[1]); + out[2] = static_cast(temp[2]); + out[3] = static_cast(temp[3]); + + return true; +} + +UnsignedShortAccessor::UnsignedShortAccessor(const BufferView* bufferView, const JsonAccessorType type, const size_t count) + : m_buffer_view(bufferView), + m_type(type), + m_count(count) +{ +} + +std::optional UnsignedShortAccessor::GetType() const +{ + return m_type; +} + +std::optional UnsignedShortAccessor::GetComponentType() const +{ + return JsonAccessorComponentType::UNSIGNED_SHORT; +} + +size_t UnsignedShortAccessor::GetCount() const +{ + return m_count; +} + +bool UnsignedShortAccessor::GetFloatVec2(const size_t index, float (&out)[2]) const +{ + assert(m_type == JsonAccessorType::VEC2 || m_type == JsonAccessorType::VEC3 || m_type == JsonAccessorType::VEC4); + if (index >= m_count) + return false; + + uint16_t temp[2]; + if (!m_buffer_view->ReadElement(temp, index, sizeof(uint16_t[2]))) + return false; + + // Return as normalized value between 0 and 1 + out[0] = static_cast(temp[0]) / static_cast(std::numeric_limits::max()); + out[1] = static_cast(temp[1]) / static_cast(std::numeric_limits::max()); + + return true; +} + +bool UnsignedShortAccessor::GetFloatVec3(const size_t index, float (&out)[3]) const +{ + assert(m_type == JsonAccessorType::VEC3 || m_type == JsonAccessorType::VEC4); + if (index >= m_count) + return false; + + uint16_t temp[3]; + if (!m_buffer_view->ReadElement(temp, index, sizeof(uint16_t[3]))) + return false; + + // Return as normalized value between 0 and 1 + out[0] = static_cast(temp[0]) / static_cast(std::numeric_limits::max()); + out[1] = static_cast(temp[1]) / static_cast(std::numeric_limits::max()); + out[2] = static_cast(temp[2]) / static_cast(std::numeric_limits::max()); + + return true; +} + +bool UnsignedShortAccessor::GetFloatVec4(const size_t index, float (&out)[4]) const +{ + assert(m_type == JsonAccessorType::VEC4); + if (index >= m_count) + return false; + + uint16_t temp[4]; + if (!m_buffer_view->ReadElement(temp, index, sizeof(uint16_t[4]))) + return false; + + // Return as normalized value between 0 and 1 + out[0] = static_cast(temp[0]) / static_cast(std::numeric_limits::max()); + out[1] = static_cast(temp[1]) / static_cast(std::numeric_limits::max()); + out[2] = static_cast(temp[2]) / static_cast(std::numeric_limits::max()); + out[3] = static_cast(temp[3]) / static_cast(std::numeric_limits::max()); + + return true; +} + +bool UnsignedShortAccessor::GetUnsigned(const size_t index, unsigned& out) const +{ + if (index >= m_count) + return false; + + uint16_t temp; + if (!m_buffer_view->ReadElement(&temp, index, sizeof(uint16_t))) + return false; + + out = temp; + return true; +} + +bool UnsignedShortAccessor::GetUnsignedVec4(const size_t index, unsigned (&out)[4]) const +{ + assert(m_type == JsonAccessorType::VEC4); + if (index >= m_count) + return false; + + uint16_t temp[4]; + if (!m_buffer_view->ReadElement(temp, index, sizeof(uint8_t[4]))) + return false; + + out[0] = static_cast(temp[0]); + out[1] = static_cast(temp[1]); + out[2] = static_cast(temp[2]); + out[3] = static_cast(temp[3]); + + return true; +} diff --git a/src/ObjLoading/XModel/Gltf/Internal/GltfAccessor.h b/src/ObjLoading/XModel/Gltf/Internal/GltfAccessor.h new file mode 100644 index 00000000..c2aeaaa7 --- /dev/null +++ b/src/ObjLoading/XModel/Gltf/Internal/GltfAccessor.h @@ -0,0 +1,111 @@ +#pragma once +#include "GltfBufferView.h" +#include "XModel/Gltf/JsonGltf.h" + +namespace gltf +{ + class Accessor + { + protected: + Accessor() = default; + + public: + virtual ~Accessor() = default; + Accessor(const Accessor& other) = default; + Accessor(Accessor&& other) noexcept = default; + Accessor& operator=(const Accessor& other) = default; + Accessor& operator=(Accessor&& other) noexcept = default; + + [[nodiscard]] virtual std::optional GetType() const = 0; + [[nodiscard]] virtual std::optional GetComponentType() const = 0; + [[nodiscard]] virtual size_t GetCount() const = 0; + + [[nodiscard]] virtual bool GetFloatVec2(size_t index, float (&out)[2]) const = 0; + [[nodiscard]] virtual bool GetFloatVec3(size_t index, float (&out)[3]) const = 0; + [[nodiscard]] virtual bool GetFloatVec4(size_t index, float (&out)[4]) const = 0; + [[nodiscard]] virtual bool GetUnsigned(size_t index, unsigned& out) const = 0; + [[nodiscard]] virtual bool GetUnsignedVec4(size_t index, unsigned (&out)[4]) const = 0; + }; + + class NullAccessor final : public Accessor + { + public: + explicit NullAccessor(size_t count); + + [[nodiscard]] std::optional GetType() const override; + [[nodiscard]] std::optional GetComponentType() const override; + [[nodiscard]] size_t GetCount() const override; + + [[nodiscard]] bool GetFloatVec2(size_t index, float (&out)[2]) const override; + [[nodiscard]] bool GetFloatVec3(size_t index, float (&out)[3]) const override; + [[nodiscard]] bool GetFloatVec4(size_t index, float (&out)[4]) const override; + [[nodiscard]] bool GetUnsigned(size_t index, unsigned& out) const override; + [[nodiscard]] bool GetUnsignedVec4(size_t index, unsigned (&out)[4]) const override; + + private: + size_t m_count; + }; + + class FloatAccessor final : public Accessor + { + public: + FloatAccessor(const BufferView* bufferView, JsonAccessorType type, size_t count); + + [[nodiscard]] std::optional GetType() const override; + [[nodiscard]] std::optional GetComponentType() const override; + [[nodiscard]] size_t GetCount() const override; + + [[nodiscard]] bool GetFloatVec2(size_t index, float (&out)[2]) const override; + [[nodiscard]] bool GetFloatVec3(size_t index, float (&out)[3]) const override; + [[nodiscard]] bool GetFloatVec4(size_t index, float (&out)[4]) const override; + [[nodiscard]] bool GetUnsigned(size_t index, unsigned& out) const override; + [[nodiscard]] bool GetUnsignedVec4(size_t index, unsigned (&out)[4]) const override; + + private: + const BufferView* m_buffer_view; + JsonAccessorType m_type; + size_t m_count; + }; + + class UnsignedByteAccessor final : public Accessor + { + public: + UnsignedByteAccessor(const BufferView* bufferView, JsonAccessorType type, size_t count); + + [[nodiscard]] std::optional GetType() const override; + [[nodiscard]] std::optional GetComponentType() const override; + [[nodiscard]] size_t GetCount() const override; + + [[nodiscard]] bool GetFloatVec2(size_t index, float (&out)[2]) const override; + [[nodiscard]] bool GetFloatVec3(size_t index, float (&out)[3]) const override; + [[nodiscard]] bool GetFloatVec4(size_t index, float (&out)[4]) const override; + [[nodiscard]] bool GetUnsigned(size_t index, unsigned& out) const override; + [[nodiscard]] bool GetUnsignedVec4(size_t index, unsigned (&out)[4]) const override; + + private: + const BufferView* m_buffer_view; + JsonAccessorType m_type; + size_t m_count; + }; + + class UnsignedShortAccessor final : public Accessor + { + public: + UnsignedShortAccessor(const BufferView* bufferView, JsonAccessorType type, size_t count); + + [[nodiscard]] std::optional GetType() const override; + [[nodiscard]] std::optional GetComponentType() const override; + [[nodiscard]] size_t GetCount() const override; + + [[nodiscard]] bool GetFloatVec2(size_t index, float (&out)[2]) const override; + [[nodiscard]] bool GetFloatVec3(size_t index, float (&out)[3]) const override; + [[nodiscard]] bool GetFloatVec4(size_t index, float (&out)[4]) const override; + [[nodiscard]] bool GetUnsigned(size_t index, unsigned& out) const override; + [[nodiscard]] bool GetUnsignedVec4(size_t index, unsigned (&out)[4]) const override; + + private: + const BufferView* m_buffer_view; + JsonAccessorType m_type; + size_t m_count; + }; +} // namespace gltf diff --git a/src/ObjLoading/XModel/Gltf/Internal/GltfBuffer.cpp b/src/ObjLoading/XModel/Gltf/Internal/GltfBuffer.cpp new file mode 100644 index 00000000..95ec99a1 --- /dev/null +++ b/src/ObjLoading/XModel/Gltf/Internal/GltfBuffer.cpp @@ -0,0 +1,80 @@ +#include "GltfBuffer.h" + +#include "XModel/Gltf/GltfConstants.h" + +#include +#include +#include + +#define LTC_NO_PROTOTYPES +#include + +using namespace gltf; + +EmbeddedBuffer::EmbeddedBuffer(const void* data, const size_t dataSize) + : m_data(static_cast(data)), + m_data_size(dataSize) +{ +} + +bool EmbeddedBuffer::ReadData(void* dest, const size_t offset, const size_t count) const +{ + assert(m_data); + assert(m_data_size > 0u); + + if (offset + count > m_data_size) + return false; + + std::memcpy(dest, &m_data[offset], count); + return true; +} + +size_t EmbeddedBuffer::GetSize() const +{ + return m_data_size; +} + +DataUriBuffer::DataUriBuffer() + : m_data(nullptr), + m_data_size(0u) +{ +} + +bool DataUriBuffer::IsDataUri(const std::string& uri) +{ + return uri.starts_with(GLTF_DATA_URI_PREFIX) && uri.size() > URI_PREFIX_LENGTH; +} + +bool DataUriBuffer::ReadDataFromUri(const std::string& uri) +{ + if (!IsDataUri(uri)) + return false; + + const auto base64DataLength = uri.size() - URI_PREFIX_LENGTH; + + unsigned long outLength = base64DataLength / 4u; + m_data = std::make_unique(outLength); + const auto result = base64_decode(&uri[URI_PREFIX_LENGTH], base64DataLength, m_data.get(), &outLength); + m_data_size = static_cast(outLength); + + assert(result == CRYPT_OK); + + return false; +} + +bool DataUriBuffer::ReadData(void* dest, const size_t offset, const size_t count) const +{ + assert(m_data); + assert(m_data_size > 0u); + + if (offset + count > m_data_size) + return false; + + std::memcpy(dest, &m_data[offset], count); + return true; +} + +size_t DataUriBuffer::GetSize() const +{ + return m_data_size; +} diff --git a/src/ObjLoading/XModel/Gltf/Internal/GltfBuffer.h b/src/ObjLoading/XModel/Gltf/Internal/GltfBuffer.h new file mode 100644 index 00000000..86fa2492 --- /dev/null +++ b/src/ObjLoading/XModel/Gltf/Internal/GltfBuffer.h @@ -0,0 +1,54 @@ +#pragma once + +#include +#include +#include +#include + +namespace gltf +{ + class Buffer + { + protected: + Buffer() = default; + + public: + virtual ~Buffer() = default; + Buffer(const Buffer& other) = default; + Buffer(Buffer&& other) noexcept = default; + Buffer& operator=(const Buffer& other) = default; + Buffer& operator=(Buffer&& other) noexcept = default; + + virtual bool ReadData(void* dest, size_t offset, size_t count) const = 0; + [[nodiscard]] virtual size_t GetSize() const = 0; + }; + + class EmbeddedBuffer final : public Buffer + { + public: + EmbeddedBuffer(const void* data, size_t dataSize); + + bool ReadData(void* dest, size_t offset, size_t count) const override; + [[nodiscard]] size_t GetSize() const override; + + private: + const uint8_t* m_data; + size_t m_data_size; + }; + + class DataUriBuffer final : public Buffer + { + public: + DataUriBuffer(); + + static bool IsDataUri(const std::string& uri); + bool ReadDataFromUri(const std::string& uri); + + bool ReadData(void* dest, size_t offset, size_t count) const override; + [[nodiscard]] size_t GetSize() const override; + + private: + std::unique_ptr m_data; + size_t m_data_size; + }; +} // namespace gltf diff --git a/src/ObjLoading/XModel/Gltf/Internal/GltfBufferView.cpp b/src/ObjLoading/XModel/Gltf/Internal/GltfBufferView.cpp new file mode 100644 index 00000000..cf071987 --- /dev/null +++ b/src/ObjLoading/XModel/Gltf/Internal/GltfBufferView.cpp @@ -0,0 +1,23 @@ +#include "GltfBufferView.h" + +using namespace gltf; + +BufferView::BufferView(const Buffer* buffer, const size_t offset, const size_t length, const size_t stride) + : m_buffer(buffer), + m_offset(offset), + m_length(length), + m_stride(stride) +{ +} + +bool BufferView::ReadElement(void* dest, const size_t elementIndex, const size_t elementSize) const +{ + const auto stride = std::max(elementSize, m_stride); + const auto bufferViewOffset = elementIndex * stride; + if (bufferViewOffset + elementSize > m_length) + return false; + + const auto bufferOffset = m_offset + bufferViewOffset; + + return m_buffer->ReadData(dest, bufferOffset, elementSize); +} diff --git a/src/ObjLoading/XModel/Gltf/Internal/GltfBufferView.h b/src/ObjLoading/XModel/Gltf/Internal/GltfBufferView.h new file mode 100644 index 00000000..fc0e442c --- /dev/null +++ b/src/ObjLoading/XModel/Gltf/Internal/GltfBufferView.h @@ -0,0 +1,19 @@ +#pragma once +#include "GltfBuffer.h" + +namespace gltf +{ + class BufferView + { + public: + BufferView(const Buffer* buffer, size_t offset, size_t length, size_t stride); + + bool ReadElement(void* dest, size_t elementIndex, size_t elementSize) const; + + private: + const Buffer* m_buffer; + size_t m_offset; + size_t m_length; + size_t m_stride; + }; +} // namespace gltf diff --git a/src/ObjLoading/XModel/XModelLoader.h b/src/ObjLoading/XModel/XModelLoader.h new file mode 100644 index 00000000..8aeca27a --- /dev/null +++ b/src/ObjLoading/XModel/XModelLoader.h @@ -0,0 +1,18 @@ +#pragma once + +#include "XModel/XModelCommon.h" + +#include + +class XModelLoader +{ +public: + XModelLoader() = default; + virtual ~XModelLoader() = default; + XModelLoader(const XModelLoader& other) = default; + XModelLoader(XModelLoader&& other) noexcept = default; + XModelLoader& operator=(const XModelLoader& other) = default; + XModelLoader& operator=(XModelLoader&& other) noexcept = default; + + virtual std::unique_ptr Load() = 0; +}; diff --git a/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperWeapon.cpp b/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperWeapon.cpp index 4749d79d..dd1bd9d2 100644 --- a/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperWeapon.cpp +++ b/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperWeapon.cpp @@ -652,7 +652,7 @@ void AssetDumperWeapon::CopyToFullDef(const WeaponCompleteDef* weapon, WeaponFul if (fullDef->weapDef.locationDamageMultipliers) { - static_assert(std::extent_v == HITLOC_NUM); + static_assert(std::extent_v == HITLOC_COUNT); assert(sizeof(WeaponFullDef::locationDamageMultipliers) >= sizeof(float) * std::extent_v); memcpy(fullDef->locationDamageMultipliers, fullDef->weapDef.locationDamageMultipliers, diff --git a/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperXModel.cpp b/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperXModel.cpp index 48d67550..2fc1821c 100644 --- a/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperXModel.cpp +++ b/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperXModel.cpp @@ -139,14 +139,17 @@ namespace } else { - bone.localOffset[0] = model->trans[boneNum - model->numRootBones][0]; - bone.localOffset[1] = model->trans[boneNum - model->numRootBones][1]; - bone.localOffset[2] = model->trans[boneNum - model->numRootBones][2]; + const auto& trans = model->trans[boneNum - model->numRootBones]; + bone.localOffset[0] = trans.x; + bone.localOffset[1] = trans.y; + bone.localOffset[2] = trans.z; + + const auto& quat = model->quats[boneNum - model->numRootBones]; bone.localRotation = { - QuatInt16::ToFloat(model->quats[boneNum - model->numRootBones][0]), - QuatInt16::ToFloat(model->quats[boneNum - model->numRootBones][1]), - QuatInt16::ToFloat(model->quats[boneNum - model->numRootBones][2]), - QuatInt16::ToFloat(model->quats[boneNum - model->numRootBones][3]), + QuatInt16::ToFloat(quat.v[0]), + QuatInt16::ToFloat(quat.v[1]), + QuatInt16::ToFloat(quat.v[2]), + QuatInt16::ToFloat(quat.v[3]), }; } @@ -229,15 +232,15 @@ namespace vertex.coordinates[0] = v.xyz[0]; vertex.coordinates[1] = v.xyz[1]; vertex.coordinates[2] = v.xyz[2]; - vertex.normal[0] = normalVec[0]; - vertex.normal[1] = normalVec[1]; - vertex.normal[2] = normalVec[2]; - vertex.color[0] = color[0]; - vertex.color[1] = color[1]; - vertex.color[2] = color[2]; - vertex.color[3] = color[3]; - vertex.uv[0] = uv[0]; - vertex.uv[1] = uv[1]; + vertex.normal[0] = normalVec.x; + vertex.normal[1] = normalVec.y; + vertex.normal[2] = normalVec.z; + vertex.color[0] = color.r; + vertex.color[1] = color.g; + vertex.color[2] = color.b; + vertex.color[3] = color.a; + vertex.uv[0] = uv.x; + vertex.uv[1] = uv.y; out.m_vertices.emplace_back(vertex); } diff --git a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperWeapon.cpp b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperWeapon.cpp index eb458716..79eb9963 100644 --- a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperWeapon.cpp +++ b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperWeapon.cpp @@ -397,8 +397,8 @@ void AssetDumperWeapon::CopyToFullDef(const WeaponVariantDef* weapon, WeaponFull if (fullDef->weapDef.locationDamageMultipliers) { - assert(sizeof(WeaponFullDef::locationDamageMultipliers) >= sizeof(float) * HITLOC_NUM); - memcpy(fullDef->locationDamageMultipliers, fullDef->weapDef.locationDamageMultipliers, sizeof(float) * HITLOC_NUM); + assert(sizeof(WeaponFullDef::locationDamageMultipliers) >= sizeof(float) * HITLOC_COUNT); + memcpy(fullDef->locationDamageMultipliers, fullDef->weapDef.locationDamageMultipliers, sizeof(float) * HITLOC_COUNT); fullDef->weapDef.locationDamageMultipliers = fullDef->locationDamageMultipliers; } diff --git a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperWeaponAttachmentUnique.cpp b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperWeaponAttachmentUnique.cpp index 33e34f50..a24006ee 100644 --- a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperWeaponAttachmentUnique.cpp +++ b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperWeaponAttachmentUnique.cpp @@ -101,8 +101,8 @@ void AssetDumperWeaponAttachmentUnique::CopyToFullDef(const WeaponAttachmentUniq if (attachment->locationDamageMultipliers) { - assert(sizeof(WeaponAttachmentUniqueFull::locationDamageMultipliers) >= sizeof(float) * HITLOC_NUM); - memcpy(fullDef->locationDamageMultipliers, attachment->locationDamageMultipliers, sizeof(float) * HITLOC_NUM); + assert(sizeof(WeaponAttachmentUniqueFull::locationDamageMultipliers) >= sizeof(float) * HITLOC_COUNT); + memcpy(fullDef->locationDamageMultipliers, attachment->locationDamageMultipliers, sizeof(float) * HITLOC_COUNT); fullDef->attachment.locationDamageMultipliers = fullDef->locationDamageMultipliers; } } diff --git a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperXModel.cpp b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperXModel.cpp index e5b501b3..cb812d11 100644 --- a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperXModel.cpp +++ b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperXModel.cpp @@ -1,6 +1,7 @@ #include "AssetDumperXModel.h" #include "Game/T6/CommonT6.h" +#include "Game/T6/XModel/JsonXModelWriter.h" #include "ObjWriting.h" #include "Utils/DistinctMapper.h" #include "Utils/QuatInt16.h" @@ -132,14 +133,24 @@ namespace bone.scale[1] = 1.0f; bone.scale[2] = 1.0f; - bone.globalOffset[0] = model->baseMat[boneNum].trans.x; - bone.globalOffset[1] = model->baseMat[boneNum].trans.y; - bone.globalOffset[2] = model->baseMat[boneNum].trans.z; + if (model->partClassification[boneNum]) + { + if (boneNum < model->numRootBones + || model->partClassification[model->parentList[boneNum - model->numRootBones]] != model->partClassification[boneNum]) + { + std::cerr << std::format("Part: {:02} = {}\n", model->partClassification[boneNum], bone.name); + } + } + + const auto& baseMat = model->baseMat[boneNum]; + bone.globalOffset[0] = baseMat.trans.x; + bone.globalOffset[1] = baseMat.trans.y; + bone.globalOffset[2] = baseMat.trans.z; bone.globalRotation = { - model->baseMat[boneNum].quat.x, - model->baseMat[boneNum].quat.y, - model->baseMat[boneNum].quat.z, - model->baseMat[boneNum].quat.w, + baseMat.quat.x, + baseMat.quat.y, + baseMat.quat.z, + baseMat.quat.w, }; if (boneNum < model->numRootBones) @@ -151,14 +162,17 @@ namespace } else { - bone.localOffset[0] = model->trans[boneNum - model->numRootBones][0]; - bone.localOffset[1] = model->trans[boneNum - model->numRootBones][1]; - bone.localOffset[2] = model->trans[boneNum - model->numRootBones][2]; + const auto& trans = model->trans[boneNum - model->numRootBones]; + bone.localOffset[0] = trans.x; + bone.localOffset[1] = trans.y; + bone.localOffset[2] = trans.z; + + const auto& quat = model->quats[boneNum - model->numRootBones]; bone.localRotation = { - QuatInt16::ToFloat(model->quats[boneNum - model->numRootBones][0]), - QuatInt16::ToFloat(model->quats[boneNum - model->numRootBones][1]), - QuatInt16::ToFloat(model->quats[boneNum - model->numRootBones][2]), - QuatInt16::ToFloat(model->quats[boneNum - model->numRootBones][3]), + QuatInt16::ToFloat(quat.v[0]), + QuatInt16::ToFloat(quat.v[1]), + QuatInt16::ToFloat(quat.v[2]), + QuatInt16::ToFloat(quat.v[3]), }; } @@ -234,10 +248,12 @@ namespace const auto& v = surface.verts0[vertexIndex]; vec2_t uv{}; vec3_t normalVec{}; + vec3_t tangentVec{}; vec4_t color{}; Common::Vec2UnpackTexCoords(v.texCoord, &uv); Common::Vec3UnpackUnitVec(v.normal, &normalVec); + Common::Vec3UnpackUnitVec(v.tangent, &tangentVec); Common::Vec4UnpackGfxColor(v.color, &color); XModelVertex vertex{}; @@ -546,6 +562,15 @@ namespace } } } + + void DumpXModel(AssetDumpingContext& context, XAssetInfo* asset) + { + const auto assetFile = context.OpenAssetFile(std::format("xmodel/{}.json", asset->m_name)); + if (!assetFile) + return; + + DumpXModelAsJson(*assetFile, asset->Asset(), context); + } } // namespace bool AssetDumperXModel::ShouldDump(XAssetInfo* asset) @@ -556,4 +581,5 @@ bool AssetDumperXModel::ShouldDump(XAssetInfo* asset) void AssetDumperXModel::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) { DumpXModelSurfs(context, asset); + DumpXModel(context, asset); } diff --git a/src/ObjWriting/Game/T6/XModel/JsonXModelWriter.cpp b/src/ObjWriting/Game/T6/XModel/JsonXModelWriter.cpp index 5f05331a..cda24133 100644 --- a/src/ObjWriting/Game/T6/XModel/JsonXModelWriter.cpp +++ b/src/ObjWriting/Game/T6/XModel/JsonXModelWriter.cpp @@ -2,7 +2,10 @@ #include "Game/T6/CommonT6.h" #include "Game/T6/Json/JsonXModel.h" +#include "ObjWriting.h" +#include +#include #include #include @@ -40,9 +43,36 @@ namespace return input; } + static const char* GetExtensionForModelByConfig() + { + switch (ObjWriting::Configuration.ModelOutputFormat) + { + case ObjWriting::Configuration_t::ModelOutputFormat_e::XMODEL_EXPORT: + return ".XMODEL_EXPORT"; + case ObjWriting::Configuration_t::ModelOutputFormat_e::OBJ: + return ".OBJ"; + case ObjWriting::Configuration_t::ModelOutputFormat_e::GLTF: + return ".GLTF"; + case ObjWriting::Configuration_t::ModelOutputFormat_e::GLB: + return ".GLB"; + default: + assert(false); + return ""; + } + } + void CreateJsonXModel(JsonXModel& jXModel, const XModel& xmodel) const { - jXModel.collLod = xmodel.collLod; + if (xmodel.collLod >= 0) + jXModel.collLod = xmodel.collLod; + + for (auto lodNumber = 0u; lodNumber < xmodel.numLods; lodNumber++) + { + JsonXModelLod lod; + lod.file = std::format("model_export/{}_lod{}{}", xmodel.name, lodNumber, GetExtensionForModelByConfig()); + + jXModel.lods.emplace_back(std::move(lod)); + } if (xmodel.physPreset && xmodel.physPreset->name) jXModel.physPreset = AssetName(xmodel.physPreset->name); diff --git a/src/ObjWriting/XModel/Gltf/GltfTextOutput.cpp b/src/ObjWriting/XModel/Gltf/GltfTextOutput.cpp index f04838a3..effb524a 100644 --- a/src/ObjWriting/XModel/Gltf/GltfTextOutput.cpp +++ b/src/ObjWriting/XModel/Gltf/GltfTextOutput.cpp @@ -1,6 +1,5 @@ #include "GltfTextOutput.h" -#include "Utils/Alignment.h" #include "XModel/Gltf/GltfConstants.h" #include @@ -18,7 +17,6 @@ TextOutput::TextOutput(std::ostream& stream) std::optional TextOutput::CreateBufferUri(const void* buffer, const size_t bufferSize) const { - static constexpr auto URI_PREFIX_LENGTH = std::char_traits::length(GLTF_DATA_URI_PREFIX); const auto base64Length = 4u * ((bufferSize + 2u) / 3u); const auto base64BufferSize = URI_PREFIX_LENGTH + base64Length; diff --git a/src/ZoneCode/Game/IW5/XAssets/WeaponCompleteDef.txt b/src/ZoneCode/Game/IW5/XAssets/WeaponCompleteDef.txt index 857cae9d..4a7a9617 100644 --- a/src/ZoneCode/Game/IW5/XAssets/WeaponCompleteDef.txt +++ b/src/ZoneCode/Game/IW5/XAssets/WeaponCompleteDef.txt @@ -101,7 +101,7 @@ set string szUseHintString; set string dropHintString; set string szScript; set reusable locationDamageMultipliers; -set count locationDamageMultipliers HITLOC_NUM; +set count locationDamageMultipliers HITLOC_COUNT; set string fireRumble; set string meleeImpactRumble; set string turretBarrelSpinRumble; diff --git a/src/ZoneCode/Game/T5/XAssets/WeaponVariantDef.txt b/src/ZoneCode/Game/T5/XAssets/WeaponVariantDef.txt index 5789c9e6..50bc2ffa 100644 --- a/src/ZoneCode/Game/T5/XAssets/WeaponVariantDef.txt +++ b/src/ZoneCode/Game/T5/XAssets/WeaponVariantDef.txt @@ -139,7 +139,7 @@ set count originalAiVsPlayerAccuracyGraphKnots aiVsPlayerAccuracyGraphKnotCount; set string szUseHintString; set string dropHintString; set string szScript; -set count locationDamageMultipliers HITLOC_NUM; +set count locationDamageMultipliers HITLOC_COUNT; set reusable locationDamageMultipliers; set string fireRumble; set string meleeImpactRumble; diff --git a/src/ZoneCode/Game/T6/XAssets/WeaponVariantDef.txt b/src/ZoneCode/Game/T6/XAssets/WeaponVariantDef.txt index 49eee990..05b8e41a 100644 --- a/src/ZoneCode/Game/T6/XAssets/WeaponVariantDef.txt +++ b/src/ZoneCode/Game/T6/XAssets/WeaponVariantDef.txt @@ -160,7 +160,7 @@ set count originalAiVsPlayerAccuracyGraphKnots aiVsPlayerAccuracyGraphKnotCount; set string szUseHintString; set string dropHintString; set string szScript; -set count locationDamageMultipliers HITLOC_NUM; +set count locationDamageMultipliers HITLOC_COUNT; set reusable locationDamageMultipliers; set string fireRumble; set string meleeImpactRumble; From f65353071438138737b7608d2fe2a432125482eb Mon Sep 17 00:00:00 2001 From: Jan Date: Sat, 10 Aug 2024 13:54:46 +0200 Subject: [PATCH 04/26] chore: improve api for vector packing for models --- src/Common/Game/IW3/CommonIW3.cpp | 24 +-- src/Common/Game/IW3/CommonIW3.h | 12 +- src/Common/Game/IW4/CommonIW4.cpp | 24 +-- src/Common/Game/IW4/CommonIW4.h | 12 +- src/Common/Game/IW5/CommonIW5.cpp | 24 +-- src/Common/Game/IW5/CommonIW5.h | 12 +- src/Common/Game/T5/CommonT5.cpp | 24 +-- src/Common/Game/T5/CommonT5.h | 12 +- src/Common/Game/T6/CommonT6.cpp | 24 +-- src/Common/Game/T6/CommonT6.h | 12 +- src/Common/Utils/Pack.cpp | 139 +++++++++++------- src/Common/Utils/Pack.h | 24 +-- .../IW3/AssetDumpers/AssetDumperXModel.cpp | 19 +-- .../IW4/AssetDumpers/AssetDumperXModel.cpp | 19 +-- .../IW5/AssetDumpers/AssetDumperXModel.cpp | 19 +-- .../T5/AssetDumpers/AssetDumperXModel.cpp | 19 +-- .../T6/AssetDumpers/AssetDumperXModel.cpp | 21 +-- 17 files changed, 201 insertions(+), 239 deletions(-) diff --git a/src/Common/Game/IW3/CommonIW3.cpp b/src/Common/Game/IW3/CommonIW3.cpp index cea4d616..11775d52 100644 --- a/src/Common/Game/IW3/CommonIW3.cpp +++ b/src/Common/Game/IW3/CommonIW3.cpp @@ -4,32 +4,32 @@ using namespace IW3; -PackedTexCoords Common::Vec2PackTexCoords(const vec2_t* in) +PackedTexCoords Common::Vec2PackTexCoords(const float (&in)[2]) { - return PackedTexCoords{Pack32::Vec2PackTexCoords(reinterpret_cast(in))}; + return PackedTexCoords{pack32::Vec2PackTexCoordsVU(in)}; } -PackedUnitVec Common::Vec3PackUnitVec(const vec3_t* in) +PackedUnitVec Common::Vec3PackUnitVec(const float (&in)[3]) { - return PackedUnitVec{Pack32::Vec3PackUnitVec(reinterpret_cast(in))}; + return PackedUnitVec{pack32::Vec3PackUnitVecScaleBased(in)}; } -GfxColor Common::Vec4PackGfxColor(const vec4_t* in) +GfxColor Common::Vec4PackGfxColor(const float (&in)[4]) { - return GfxColor{Pack32::Vec4PackGfxColor(reinterpret_cast(in))}; + return GfxColor{pack32::Vec4PackGfxColor(in)}; } -void Common::Vec2UnpackTexCoords(const PackedTexCoords& in, vec2_t* out) +void Common::Vec2UnpackTexCoords(const PackedTexCoords& in, float (&out)[2]) { - Pack32::Vec2UnpackTexCoordsVU(in.packed, reinterpret_cast(out)); + pack32::Vec2UnpackTexCoordsVU(in.packed, out); } -void Common::Vec3UnpackUnitVec(const PackedUnitVec& in, vec3_t* out) +void Common::Vec3UnpackUnitVec(const PackedUnitVec& in, float (&out)[3]) { - Pack32::Vec3UnpackUnitVecScaleBased(in.packed, reinterpret_cast(out)); + pack32::Vec3UnpackUnitVecScaleBased(in.packed, out); } -void Common::Vec4UnpackGfxColor(const GfxColor& in, vec4_t* out) +void Common::Vec4UnpackGfxColor(const GfxColor& in, float (&out)[4]) { - Pack32::Vec4UnpackGfxColor(in.packed, reinterpret_cast(out)); + pack32::Vec4UnpackGfxColor(in.packed, out); } diff --git a/src/Common/Game/IW3/CommonIW3.h b/src/Common/Game/IW3/CommonIW3.h index 068c1c6f..a01a6feb 100644 --- a/src/Common/Game/IW3/CommonIW3.h +++ b/src/Common/Game/IW3/CommonIW3.h @@ -21,11 +21,11 @@ namespace IW3 return result; } - static PackedTexCoords Vec2PackTexCoords(const vec2_t* in); - static PackedUnitVec Vec3PackUnitVec(const vec3_t* in); - static GfxColor Vec4PackGfxColor(const vec4_t* in); - static void Vec2UnpackTexCoords(const PackedTexCoords& in, vec2_t* out); - static void Vec3UnpackUnitVec(const PackedUnitVec& in, vec3_t* out); - static void Vec4UnpackGfxColor(const GfxColor& in, vec4_t* out); + static PackedTexCoords Vec2PackTexCoords(const float (&in)[2]); + static PackedUnitVec Vec3PackUnitVec(const float (&in)[3]); + static GfxColor Vec4PackGfxColor(const float (&in)[4]); + static void Vec2UnpackTexCoords(const PackedTexCoords& in, float (&out)[2]); + static void Vec3UnpackUnitVec(const PackedUnitVec& in, float (&out)[3]); + static void Vec4UnpackGfxColor(const GfxColor& in, float (&out)[4]); }; } // namespace IW3 diff --git a/src/Common/Game/IW4/CommonIW4.cpp b/src/Common/Game/IW4/CommonIW4.cpp index 6a512836..de6cc73d 100644 --- a/src/Common/Game/IW4/CommonIW4.cpp +++ b/src/Common/Game/IW4/CommonIW4.cpp @@ -20,32 +20,32 @@ int Common::StringTable_HashString(const char* str) return result; } -PackedTexCoords Common::Vec2PackTexCoords(const vec2_t* in) +PackedTexCoords Common::Vec2PackTexCoords(const float (&in)[2]) { - return PackedTexCoords{Pack32::Vec2PackTexCoords(reinterpret_cast(in))}; + return PackedTexCoords{pack32::Vec2PackTexCoordsVU(in)}; } -PackedUnitVec Common::Vec3PackUnitVec(const vec3_t* in) +PackedUnitVec Common::Vec3PackUnitVec(const float (&in)[3]) { - return PackedUnitVec{Pack32::Vec3PackUnitVec(reinterpret_cast(in))}; + return PackedUnitVec{pack32::Vec3PackUnitVecScaleBased(in)}; } -GfxColor Common::Vec4PackGfxColor(const vec4_t* in) +GfxColor Common::Vec4PackGfxColor(const float (&in)[4]) { - return GfxColor{Pack32::Vec4PackGfxColor(reinterpret_cast(in))}; + return GfxColor{pack32::Vec4PackGfxColor(in)}; } -void Common::Vec2UnpackTexCoords(const PackedTexCoords& in, vec2_t* out) +void Common::Vec2UnpackTexCoords(const PackedTexCoords& in, float (&out)[2]) { - Pack32::Vec2UnpackTexCoordsVU(in.packed, reinterpret_cast(out)); + pack32::Vec2UnpackTexCoordsVU(in.packed, out); } -void Common::Vec3UnpackUnitVec(const PackedUnitVec& in, vec3_t* out) +void Common::Vec3UnpackUnitVec(const PackedUnitVec& in, float (&out)[3]) { - Pack32::Vec3UnpackUnitVecScaleBased(in.packed, reinterpret_cast(out)); + pack32::Vec3UnpackUnitVecScaleBased(in.packed, out); } -void Common::Vec4UnpackGfxColor(const GfxColor& in, vec4_t* out) +void Common::Vec4UnpackGfxColor(const GfxColor& in, float (&out)[4]) { - Pack32::Vec4UnpackGfxColor(in.packed, reinterpret_cast(out)); + pack32::Vec4UnpackGfxColor(in.packed, out); } diff --git a/src/Common/Game/IW4/CommonIW4.h b/src/Common/Game/IW4/CommonIW4.h index 05287502..9aaff02c 100644 --- a/src/Common/Game/IW4/CommonIW4.h +++ b/src/Common/Game/IW4/CommonIW4.h @@ -28,11 +28,11 @@ namespace IW4 static int StringTable_HashString(const char* str); - static PackedTexCoords Vec2PackTexCoords(const vec2_t* in); - static PackedUnitVec Vec3PackUnitVec(const vec3_t* in); - static GfxColor Vec4PackGfxColor(const vec4_t* in); - static void Vec2UnpackTexCoords(const PackedTexCoords& in, vec2_t* out); - static void Vec3UnpackUnitVec(const PackedUnitVec& in, vec3_t* out); - static void Vec4UnpackGfxColor(const GfxColor& in, vec4_t* out); + static PackedTexCoords Vec2PackTexCoords(const float (&in)[2]); + static PackedUnitVec Vec3PackUnitVec(const float (&in)[3]); + static GfxColor Vec4PackGfxColor(const float (&in)[4]); + static void Vec2UnpackTexCoords(const PackedTexCoords& in, float (&out)[2]); + static void Vec3UnpackUnitVec(const PackedUnitVec& in, float (&out)[3]); + static void Vec4UnpackGfxColor(const GfxColor& in, float (&out)[4]); }; } // namespace IW4 diff --git a/src/Common/Game/IW5/CommonIW5.cpp b/src/Common/Game/IW5/CommonIW5.cpp index ee552be2..79379dd4 100644 --- a/src/Common/Game/IW5/CommonIW5.cpp +++ b/src/Common/Game/IW5/CommonIW5.cpp @@ -20,32 +20,32 @@ int Common::StringTable_HashString(const char* str) return result; } -PackedTexCoords Common::Vec2PackTexCoords(const vec2_t* in) +PackedTexCoords Common::Vec2PackTexCoords(const float (&in)[2]) { - return PackedTexCoords{Pack32::Vec2PackTexCoords(reinterpret_cast(in))}; + return PackedTexCoords{pack32::Vec2PackTexCoordsVU(in)}; } -PackedUnitVec Common::Vec3PackUnitVec(const vec3_t* in) +PackedUnitVec Common::Vec3PackUnitVec(const float (&in)[3]) { - return PackedUnitVec{Pack32::Vec3PackUnitVec(reinterpret_cast(in))}; + return PackedUnitVec{pack32::Vec3PackUnitVecScaleBased(in)}; } -GfxColor Common::Vec4PackGfxColor(const vec4_t* in) +GfxColor Common::Vec4PackGfxColor(const float (&in)[4]) { - return GfxColor{Pack32::Vec4PackGfxColor(reinterpret_cast(in))}; + return GfxColor{pack32::Vec4PackGfxColor(in)}; } -void Common::Vec2UnpackTexCoords(const PackedTexCoords& in, vec2_t* out) +void Common::Vec2UnpackTexCoords(const PackedTexCoords& in, float (&out)[2]) { - Pack32::Vec2UnpackTexCoordsVU(in.packed, reinterpret_cast(out)); + pack32::Vec2UnpackTexCoordsVU(in.packed, out); } -void Common::Vec3UnpackUnitVec(const PackedUnitVec& in, vec3_t* out) +void Common::Vec3UnpackUnitVec(const PackedUnitVec& in, float (&out)[3]) { - Pack32::Vec3UnpackUnitVecScaleBased(in.packed, reinterpret_cast(out)); + pack32::Vec3UnpackUnitVecScaleBased(in.packed, out); } -void Common::Vec4UnpackGfxColor(const GfxColor& in, vec4_t* out) +void Common::Vec4UnpackGfxColor(const GfxColor& in, float (&out)[4]) { - Pack32::Vec4UnpackGfxColor(in.packed, reinterpret_cast(out)); + pack32::Vec4UnpackGfxColor(in.packed, out); } diff --git a/src/Common/Game/IW5/CommonIW5.h b/src/Common/Game/IW5/CommonIW5.h index 7e12cc38..8347674c 100644 --- a/src/Common/Game/IW5/CommonIW5.h +++ b/src/Common/Game/IW5/CommonIW5.h @@ -9,11 +9,11 @@ namespace IW5 public: static int StringTable_HashString(const char* str); - static PackedTexCoords Vec2PackTexCoords(const vec2_t* in); - static PackedUnitVec Vec3PackUnitVec(const vec3_t* in); - static GfxColor Vec4PackGfxColor(const vec4_t* in); - static void Vec2UnpackTexCoords(const PackedTexCoords& in, vec2_t* out); - static void Vec3UnpackUnitVec(const PackedUnitVec& in, vec3_t* out); - static void Vec4UnpackGfxColor(const GfxColor& in, vec4_t* out); + static PackedTexCoords Vec2PackTexCoords(const float (&in)[2]); + static PackedUnitVec Vec3PackUnitVec(const float (&in)[3]); + static GfxColor Vec4PackGfxColor(const float (&in)[4]); + static void Vec2UnpackTexCoords(const PackedTexCoords& in, float (&out)[2]); + static void Vec3UnpackUnitVec(const PackedUnitVec& in, float (&out)[3]); + static void Vec4UnpackGfxColor(const GfxColor& in, float (&out)[4]); }; } // namespace IW5 diff --git a/src/Common/Game/T5/CommonT5.cpp b/src/Common/Game/T5/CommonT5.cpp index 313843f2..979f0300 100644 --- a/src/Common/Game/T5/CommonT5.cpp +++ b/src/Common/Game/T5/CommonT5.cpp @@ -58,32 +58,32 @@ int Common::Com_HashString(const char* str, const int len) return result; } -PackedTexCoords Common::Vec2PackTexCoords(const vec2_t* in) +PackedTexCoords Common::Vec2PackTexCoords(const float (&in)[2]) { - return PackedTexCoords{Pack32::Vec2PackTexCoords(reinterpret_cast(in))}; + return PackedTexCoords{pack32::Vec2PackTexCoordsVU(in)}; } -PackedUnitVec Common::Vec3PackUnitVec(const vec3_t* in) +PackedUnitVec Common::Vec3PackUnitVec(const float (&in)[3]) { - return PackedUnitVec{Pack32::Vec3PackUnitVec(reinterpret_cast(in))}; + return PackedUnitVec{pack32::Vec3PackUnitVecScaleBased(in)}; } -GfxColor Common::Vec4PackGfxColor(const vec4_t* in) +GfxColor Common::Vec4PackGfxColor(const float (&in)[4]) { - return GfxColor{Pack32::Vec4PackGfxColor(reinterpret_cast(in))}; + return GfxColor{pack32::Vec4PackGfxColor(in)}; } -void Common::Vec2UnpackTexCoords(const PackedTexCoords& in, vec2_t* out) +void Common::Vec2UnpackTexCoords(const PackedTexCoords& in, float (&out)[2]) { - Pack32::Vec2UnpackTexCoordsVU(in.packed, reinterpret_cast(out)); + pack32::Vec2UnpackTexCoordsVU(in.packed, out); } -void Common::Vec3UnpackUnitVec(const PackedUnitVec& in, vec3_t* out) +void Common::Vec3UnpackUnitVec(const PackedUnitVec& in, float (&out)[3]) { - Pack32::Vec3UnpackUnitVecScaleBased(in.packed, reinterpret_cast(out)); + pack32::Vec3UnpackUnitVecScaleBased(in.packed, out); } -void Common::Vec4UnpackGfxColor(const GfxColor& in, vec4_t* out) +void Common::Vec4UnpackGfxColor(const GfxColor& in, float (&out)[4]) { - Pack32::Vec4UnpackGfxColor(in.packed, reinterpret_cast(out)); + pack32::Vec4UnpackGfxColor(in.packed, out); } diff --git a/src/Common/Game/T5/CommonT5.h b/src/Common/Game/T5/CommonT5.h index 1be8add5..9cb67e29 100644 --- a/src/Common/Game/T5/CommonT5.h +++ b/src/Common/Game/T5/CommonT5.h @@ -11,11 +11,11 @@ namespace T5 static int Com_HashString(const char* str); static int Com_HashString(const char* str, int len); - static PackedTexCoords Vec2PackTexCoords(const vec2_t* in); - static PackedUnitVec Vec3PackUnitVec(const vec3_t* in); - static GfxColor Vec4PackGfxColor(const vec4_t* in); - static void Vec2UnpackTexCoords(const PackedTexCoords& in, vec2_t* out); - static void Vec3UnpackUnitVec(const PackedUnitVec& in, vec3_t* out); - static void Vec4UnpackGfxColor(const GfxColor& in, vec4_t* out); + static PackedTexCoords Vec2PackTexCoords(const float (&in)[2]); + static PackedUnitVec Vec3PackUnitVec(const float (&in)[3]); + static GfxColor Vec4PackGfxColor(const float (&in)[4]); + static void Vec2UnpackTexCoords(const PackedTexCoords& in, float (&out)[2]); + static void Vec3UnpackUnitVec(const PackedUnitVec& in, float (&out)[3]); + static void Vec4UnpackGfxColor(const GfxColor& in, float (&out)[4]); }; } // namespace T5 diff --git a/src/Common/Game/T6/CommonT6.cpp b/src/Common/Game/T6/CommonT6.cpp index 744a3270..4c9287c5 100644 --- a/src/Common/Game/T6/CommonT6.cpp +++ b/src/Common/Game/T6/CommonT6.cpp @@ -58,32 +58,32 @@ int Common::Com_HashString(const char* str, const int len) return result; } -PackedTexCoords Common::Vec2PackTexCoords(const vec2_t* in) +PackedTexCoords Common::Vec2PackTexCoords(const float (&in)[2]) { - return PackedTexCoords{Pack32::Vec2PackTexCoords(in->v)}; + return PackedTexCoords{pack32::Vec2PackTexCoordsUV(in)}; } -PackedUnitVec Common::Vec3PackUnitVec(const vec3_t* in) +PackedUnitVec Common::Vec3PackUnitVec(const float (&in)[3]) { - return PackedUnitVec{Pack32::Vec3PackUnitVec(in->v)}; + return PackedUnitVec{pack32::Vec3PackUnitVecThirdBased(in)}; } -GfxColor Common::Vec4PackGfxColor(const vec4_t* in) +GfxColor Common::Vec4PackGfxColor(const float (&in)[4]) { - return GfxColor{Pack32::Vec4PackGfxColor(in->v)}; + return GfxColor{pack32::Vec4PackGfxColor(in)}; } -void Common::Vec2UnpackTexCoords(const PackedTexCoords& in, vec2_t* out) +void Common::Vec2UnpackTexCoords(const PackedTexCoords& in, float (&out)[2]) { - Pack32::Vec2UnpackTexCoordsUV(in.packed, out->v); + pack32::Vec2UnpackTexCoordsUV(in.packed, out); } -void Common::Vec3UnpackUnitVec(const PackedUnitVec& in, vec3_t* out) +void Common::Vec3UnpackUnitVec(const PackedUnitVec& in, float (&out)[3]) { - Pack32::Vec3UnpackUnitVecThirdBased(in.packed, out->v); + pack32::Vec3UnpackUnitVecThirdBased(in.packed, out); } -void Common::Vec4UnpackGfxColor(const GfxColor& in, vec4_t* out) +void Common::Vec4UnpackGfxColor(const GfxColor& in, float (&out)[4]) { - Pack32::Vec4UnpackGfxColor(in.packed, out->v); + pack32::Vec4UnpackGfxColor(in.packed, out); } diff --git a/src/Common/Game/T6/CommonT6.h b/src/Common/Game/T6/CommonT6.h index 9e939f30..98e0cc6e 100644 --- a/src/Common/Game/T6/CommonT6.h +++ b/src/Common/Game/T6/CommonT6.h @@ -41,11 +41,11 @@ namespace T6 return result; } - static PackedTexCoords Vec2PackTexCoords(const vec2_t* in); - static PackedUnitVec Vec3PackUnitVec(const vec3_t* in); - static GfxColor Vec4PackGfxColor(const vec4_t* in); - static void Vec2UnpackTexCoords(const PackedTexCoords& in, vec2_t* out); - static void Vec3UnpackUnitVec(const PackedUnitVec& in, vec3_t* out); - static void Vec4UnpackGfxColor(const GfxColor& in, vec4_t* out); + static PackedTexCoords Vec2PackTexCoords(const float (&in)[2]); + static PackedUnitVec Vec3PackUnitVec(const float (&in)[3]); + static GfxColor Vec4PackGfxColor(const float (&in)[4]); + static void Vec2UnpackTexCoords(const PackedTexCoords& in, float (&out)[2]); + static void Vec3UnpackUnitVec(const PackedUnitVec& in, float (&out)[3]); + static void Vec4UnpackGfxColor(const GfxColor& in, float (&out)[4]); }; } // namespace T6 diff --git a/src/Common/Utils/Pack.cpp b/src/Common/Utils/Pack.cpp index fbee907e..9008956f 100644 --- a/src/Common/Utils/Pack.cpp +++ b/src/Common/Utils/Pack.cpp @@ -4,6 +4,7 @@ #include #include +#include union PackUtil32 { @@ -14,70 +15,98 @@ union PackUtil32 uint8_t uc[4]; }; -uint32_t Pack32::Vec2PackTexCoords(const float* in) +namespace pack32 { - return static_cast(HalfFloat::ToHalf(in[0])) << 16 | HalfFloat::ToHalf(in[1]); -} + uint32_t Vec2PackTexCoordsUV(const float (&in)[2]) + { + return static_cast(HalfFloat::ToHalf(in[1])) << 16 | HalfFloat::ToHalf(in[0]); + } -uint32_t Pack32::Vec3PackUnitVec(const float* in) -{ - // TODO - return 0; -} + uint32_t Vec2PackTexCoordsVU(const float (&in)[2]) + { + return static_cast(HalfFloat::ToHalf(in[0])) << 16 | HalfFloat::ToHalf(in[1]); + } -uint32_t Pack32::Vec4PackGfxColor(const float* in) -{ - return static_cast(std::clamp(in[0], 0.0f, 1.0f) * 255.0f) | static_cast(std::clamp(in[1], 0.0f, 1.0f) * 255.0f) << 8 - | static_cast(std::clamp(in[2], 0.0f, 1.0f) * 255.0f) << 16 | static_cast(std::clamp(in[3], 0.0f, 1.0f) * 255.0f) << 24; -} + uint32_t Vec3PackUnitVecScaleBased(const float (&in)[3]) + { + // TODO: Implement + assert(false); + return 0; + } -void Pack32::Vec2UnpackTexCoordsUV(const uint32_t in, float* out) -{ - const auto inHiDw = static_cast((in >> 16) & UINT16_MAX); - const auto inLoDw = static_cast(in & UINT16_MAX); + uint32_t Vec3PackUnitVecThirdBased(const float (&in)[3]) + { + // This is based on the game's reversed code, the original code may have made a bit more sense + PackUtil32 x; + x.f = (in[0] - -24624.0939334638f) * 0.0001218318939208984f; + PackUtil32 y; + y.f = (in[1] - -24624.0939334638f) * 0.0001218318939208984f; + PackUtil32 z; + z.f = (in[2] - -24624.0939334638f) * 0.0001218318939208984f; - out[0] = HalfFloat::ToFloat(inLoDw); - out[1] = HalfFloat::ToFloat(inHiDw); -} + return x.u | y.u << 10u | z.u << 20u; + } -void Pack32::Vec2UnpackTexCoordsVU(const uint32_t in, float* out) -{ - const auto inHiDw = static_cast((in >> 16) & UINT16_MAX); - const auto inLoDw = static_cast(in & UINT16_MAX); + uint32_t Vec4PackGfxColor(const float (&in)[4]) + { + // clang-format off + return static_cast(std::clamp(in[0], 0.0f, 1.0f) * 255.0f) + | static_cast(std::clamp(in[1], 0.0f, 1.0f) * 255.0f) << 8 + | static_cast(std::clamp(in[2], 0.0f, 1.0f) * 255.0f) << 16 + | static_cast(std::clamp(in[3], 0.0f, 1.0f) * 255.0f) << 24; + // clang-format on + } - out[0] = HalfFloat::ToFloat(inHiDw); - out[1] = HalfFloat::ToFloat(inLoDw); -} + void Vec2UnpackTexCoordsUV(const uint32_t in, float (&out)[2]) + { + const auto inHiDw = static_cast((in >> 16) & std::numeric_limits::max()); + const auto inLoDw = static_cast(in & std::numeric_limits::max()); -void Pack32::Vec3UnpackUnitVecScaleBased(const uint32_t in, float* out) -{ - assert(out != nullptr); + out[0] = HalfFloat::ToFloat(inLoDw); + out[1] = HalfFloat::ToFloat(inHiDw); + } - PackUtil32 _in{in}; - const float decodeScale = (static_cast(_in.uc[3]) - -192.0f) / 32385.0f; - out[0] = (static_cast(_in.uc[0]) + -127.0f) * decodeScale; - out[1] = (static_cast(_in.uc[1]) + -127.0f) * decodeScale; - out[2] = (static_cast(_in.uc[2]) + -127.0f) * decodeScale; -} + void Vec2UnpackTexCoordsVU(const uint32_t in, float (&out)[2]) + { + const auto inHiDw = static_cast((in >> 16) & std::numeric_limits::max()); + const auto inLoDw = static_cast(in & std::numeric_limits::max()); -void Pack32::Vec3UnpackUnitVecThirdBased(const uint32_t in, float* out) -{ - PackUtil32 v0{(in >> 0) & 0x3FF}; - PackUtil32 v1{(in >> 10) & 0x3FF}; - PackUtil32 v2{(in >> 20) & 0x3FF}; + out[0] = HalfFloat::ToFloat(inHiDw); + out[1] = HalfFloat::ToFloat(inLoDw); + } - v0.u = v0.u - 2 * (v0.u & 0x200) + 0x40400000; - v1.u = v1.u - 2 * (v1.u & 0x200) + 0x40400000; - v2.u = v2.u - 2 * (v2.u & 0x200) + 0x40400000; - out[0] = (v0.f - 3.0f) * 8208.0312f; - out[1] = (v1.f - 3.0f) * 8208.0312f; - out[2] = (v2.f - 3.0f) * 8208.0312f; -} + void Vec3UnpackUnitVecScaleBased(const uint32_t in, float (&out)[3]) + { + assert(out != nullptr); -void Pack32::Vec4UnpackGfxColor(uint32_t in, float* out) -{ - out[0] = static_cast(in & UINT8_MAX) / 255.0f; - out[1] = static_cast((in >> 8) & UINT8_MAX) / 255.0f; - out[2] = static_cast((in >> 16) & UINT8_MAX) / 255.0f; - out[3] = static_cast((in >> 24) & UINT8_MAX) / 255.0f; -} + const PackUtil32 inUtil{in}; + const float decodeScale = (static_cast(inUtil.uc[3]) - -192.0f) / 32385.0f; + out[0] = (static_cast(inUtil.uc[0]) + -127.0f) * decodeScale; + out[1] = (static_cast(inUtil.uc[1]) + -127.0f) * decodeScale; + out[2] = (static_cast(inUtil.uc[2]) + -127.0f) * decodeScale; + } + + void Vec3UnpackUnitVecThirdBased(const uint32_t in, float (&out)[3]) + { + // This is based on the game's reversed code, the original code may have made a bit more sense + PackUtil32 v0{(in >> 0) & 0x3FF}; + PackUtil32 v1{(in >> 10) & 0x3FF}; + PackUtil32 v2{(in >> 20) & 0x3FF}; + + v0.u = v0.u - 2 * (v0.u & 0x200) + 0x40400000; + v1.u = v1.u - 2 * (v1.u & 0x200) + 0x40400000; + v2.u = v2.u - 2 * (v2.u & 0x200) + 0x40400000; + out[0] = (v0.f - 3.0f) * 8208.0312f; + out[1] = (v1.f - 3.0f) * 8208.0312f; + out[2] = (v2.f - 3.0f) * 8208.0312f; + } + + void Vec4UnpackGfxColor(const uint32_t in, float (&out)[4]) + { + out[0] = static_cast(in & std::numeric_limits::max()) / 255.0f; + out[1] = static_cast((in >> 8) & std::numeric_limits::max()) / 255.0f; + out[2] = static_cast((in >> 16) & std::numeric_limits::max()) / 255.0f; + out[3] = static_cast((in >> 24) & std::numeric_limits::max()) / 255.0f; + } + +} // namespace pack32 diff --git a/src/Common/Utils/Pack.h b/src/Common/Utils/Pack.h index 7ef679d7..5dd6743f 100644 --- a/src/Common/Utils/Pack.h +++ b/src/Common/Utils/Pack.h @@ -2,17 +2,17 @@ #include -class Pack32 +namespace pack32 { - Pack32() = default; + uint32_t Vec2PackTexCoordsUV(const float (&in)[2]); + uint32_t Vec2PackTexCoordsVU(const float (&in)[2]); + uint32_t Vec3PackUnitVecScaleBased(const float (&in)[3]); + uint32_t Vec3PackUnitVecThirdBased(const float (&in)[3]); + uint32_t Vec4PackGfxColor(const float (&in)[4]); -public: - static uint32_t Vec2PackTexCoords(const float* in); - static uint32_t Vec3PackUnitVec(const float* in); - static uint32_t Vec4PackGfxColor(const float* in); - static void Vec2UnpackTexCoordsUV(uint32_t in, float* out); - static void Vec2UnpackTexCoordsVU(uint32_t in, float* out); - static void Vec3UnpackUnitVecScaleBased(uint32_t in, float* out); - static void Vec3UnpackUnitVecThirdBased(uint32_t in, float* out); - static void Vec4UnpackGfxColor(uint32_t in, float* out); -}; + void Vec2UnpackTexCoordsUV(uint32_t in, float (&out)[2]); + void Vec2UnpackTexCoordsVU(uint32_t in, float (&out)[2]); + void Vec3UnpackUnitVecScaleBased(uint32_t in, float (&out)[3]); + void Vec3UnpackUnitVecThirdBased(uint32_t in, float (&out)[3]); + void Vec4UnpackGfxColor(uint32_t in, float (&out)[4]); +}; // namespace pack32 diff --git a/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperXModel.cpp b/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperXModel.cpp index c69a0f9e..0775772f 100644 --- a/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperXModel.cpp +++ b/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperXModel.cpp @@ -218,27 +218,14 @@ namespace for (auto vertexIndex = 0u; vertexIndex < surface.vertCount; vertexIndex++) { const auto& v = surface.verts0[vertexIndex]; - vec2_t uv; - vec3_t normalVec; - vec4_t color; - - Common::Vec2UnpackTexCoords(v.texCoord, &uv); - Common::Vec3UnpackUnitVec(v.normal, &normalVec); - Common::Vec4UnpackGfxColor(v.color, &color); XModelVertex vertex{}; vertex.coordinates[0] = v.xyz[0]; vertex.coordinates[1] = v.xyz[1]; vertex.coordinates[2] = v.xyz[2]; - vertex.normal[0] = normalVec[0]; - vertex.normal[1] = normalVec[1]; - vertex.normal[2] = normalVec[2]; - vertex.color[0] = color[0]; - vertex.color[1] = color[1]; - vertex.color[2] = color[2]; - vertex.color[3] = color[3]; - vertex.uv[0] = uv[0]; - vertex.uv[1] = uv[1]; + Common::Vec3UnpackUnitVec(v.normal, vertex.normal); + Common::Vec4UnpackGfxColor(v.color, vertex.color); + Common::Vec2UnpackTexCoords(v.texCoord, vertex.uv); out.m_vertices.emplace_back(vertex); } diff --git a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperXModel.cpp b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperXModel.cpp index 5a36bd6f..cee8cf25 100644 --- a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperXModel.cpp +++ b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperXModel.cpp @@ -207,27 +207,14 @@ namespace for (auto vertexIndex = 0u; vertexIndex < surface.vertCount; vertexIndex++) { const auto& v = surface.verts0[vertexIndex]; - vec2_t uv; - vec3_t normalVec; - vec4_t color; - - Common::Vec2UnpackTexCoords(v.texCoord, &uv); - Common::Vec3UnpackUnitVec(v.normal, &normalVec); - Common::Vec4UnpackGfxColor(v.color, &color); XModelVertex vertex{}; vertex.coordinates[0] = v.xyz[0]; vertex.coordinates[1] = v.xyz[1]; vertex.coordinates[2] = v.xyz[2]; - vertex.normal[0] = normalVec[0]; - vertex.normal[1] = normalVec[1]; - vertex.normal[2] = normalVec[2]; - vertex.color[0] = color[0]; - vertex.color[1] = color[1]; - vertex.color[2] = color[2]; - vertex.color[3] = color[3]; - vertex.uv[0] = uv[0]; - vertex.uv[1] = uv[1]; + Common::Vec3UnpackUnitVec(v.normal, vertex.normal); + Common::Vec4UnpackGfxColor(v.color, vertex.color); + Common::Vec2UnpackTexCoords(v.texCoord, vertex.uv); out.m_vertices.emplace_back(vertex); } diff --git a/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperXModel.cpp b/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperXModel.cpp index 53b4bf5e..6a0af227 100644 --- a/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperXModel.cpp +++ b/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperXModel.cpp @@ -207,27 +207,14 @@ namespace for (auto vertexIndex = 0u; vertexIndex < surface.vertCount; vertexIndex++) { const auto& v = surface.verts0.packedVerts0[vertexIndex]; - vec2_t uv; - vec3_t normalVec; - vec4_t color; - - Common::Vec2UnpackTexCoords(v.texCoord, &uv); - Common::Vec3UnpackUnitVec(v.normal, &normalVec); - Common::Vec4UnpackGfxColor(v.color, &color); XModelVertex vertex{}; vertex.coordinates[0] = v.xyz[0]; vertex.coordinates[1] = v.xyz[1]; vertex.coordinates[2] = v.xyz[2]; - vertex.normal[0] = normalVec[0]; - vertex.normal[1] = normalVec[1]; - vertex.normal[2] = normalVec[2]; - vertex.color[0] = color[0]; - vertex.color[1] = color[1]; - vertex.color[2] = color[2]; - vertex.color[3] = color[3]; - vertex.uv[0] = uv[0]; - vertex.uv[1] = uv[1]; + Common::Vec3UnpackUnitVec(v.normal, vertex.normal); + Common::Vec4UnpackGfxColor(v.color, vertex.color); + Common::Vec2UnpackTexCoords(v.texCoord, vertex.uv); out.m_vertices.emplace_back(vertex); } diff --git a/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperXModel.cpp b/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperXModel.cpp index 2fc1821c..b9102858 100644 --- a/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperXModel.cpp +++ b/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperXModel.cpp @@ -220,27 +220,14 @@ namespace for (auto vertexIndex = 0u; vertexIndex < surface.vertCount; vertexIndex++) { const auto& v = surface.verts0[vertexIndex]; - vec2_t uv; - vec3_t normalVec; - vec4_t color; - - Common::Vec2UnpackTexCoords(v.texCoord, &uv); - Common::Vec3UnpackUnitVec(v.normal, &normalVec); - Common::Vec4UnpackGfxColor(v.color, &color); XModelVertex vertex{}; vertex.coordinates[0] = v.xyz[0]; vertex.coordinates[1] = v.xyz[1]; vertex.coordinates[2] = v.xyz[2]; - vertex.normal[0] = normalVec.x; - vertex.normal[1] = normalVec.y; - vertex.normal[2] = normalVec.z; - vertex.color[0] = color.r; - vertex.color[1] = color.g; - vertex.color[2] = color.b; - vertex.color[3] = color.a; - vertex.uv[0] = uv.x; - vertex.uv[1] = uv.y; + Common::Vec3UnpackUnitVec(v.normal, vertex.normal); + Common::Vec4UnpackGfxColor(v.color, vertex.color); + Common::Vec2UnpackTexCoords(v.texCoord, vertex.uv); out.m_vertices.emplace_back(vertex); } diff --git a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperXModel.cpp b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperXModel.cpp index cb812d11..d160dc77 100644 --- a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperXModel.cpp +++ b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperXModel.cpp @@ -246,29 +246,14 @@ namespace for (auto vertexIndex = 0u; vertexIndex < surface.vertCount; vertexIndex++) { const auto& v = surface.verts0[vertexIndex]; - vec2_t uv{}; - vec3_t normalVec{}; - vec3_t tangentVec{}; - vec4_t color{}; - - Common::Vec2UnpackTexCoords(v.texCoord, &uv); - Common::Vec3UnpackUnitVec(v.normal, &normalVec); - Common::Vec3UnpackUnitVec(v.tangent, &tangentVec); - Common::Vec4UnpackGfxColor(v.color, &color); XModelVertex vertex{}; vertex.coordinates[0] = v.xyz.x; vertex.coordinates[1] = v.xyz.y; vertex.coordinates[2] = v.xyz.z; - vertex.normal[0] = normalVec.x; - vertex.normal[1] = normalVec.y; - vertex.normal[2] = normalVec.z; - vertex.color[0] = color.x; - vertex.color[1] = color.y; - vertex.color[2] = color.z; - vertex.color[3] = color.w; - vertex.uv[0] = uv.x; - vertex.uv[1] = uv.y; + Common::Vec3UnpackUnitVec(v.normal, vertex.normal); + Common::Vec4UnpackGfxColor(v.color, vertex.color); + Common::Vec2UnpackTexCoords(v.texCoord, vertex.uv); out.m_vertices.emplace_back(vertex); } From 75e22b17507282bbcc1b14ba4976d27b06fee98c Mon Sep 17 00:00:00 2001 From: Jan Date: Sat, 10 Aug 2024 20:47:58 +0200 Subject: [PATCH 05/26] fix: parentIndex is relative to current bone index --- src/ObjLoading/Game/T6/XModel/JsonXModelLoader.cpp | 7 +++++-- src/ObjWriting/Game/T6/AssetDumpers/AssetDumperXModel.cpp | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/ObjLoading/Game/T6/XModel/JsonXModelLoader.cpp b/src/ObjLoading/Game/T6/XModel/JsonXModelLoader.cpp index a592a73c..ea60842c 100644 --- a/src/ObjLoading/Game/T6/XModel/JsonXModelLoader.cpp +++ b/src/ObjLoading/Game/T6/XModel/JsonXModelLoader.cpp @@ -314,10 +314,13 @@ namespace // Other boneInfo data is filled when calculating bone bounds xmodel.boneInfo[boneIndex].collmap = -1; - if (xmodel.numRootBones < boneIndex) + if (xmodel.numRootBones <= boneIndex) { const auto nonRootIndex = boneIndex - xmodel.numRootBones; - xmodel.parentList[nonRootIndex] = static_cast(bone.parentIndex.value_or(0u)); + const auto parentBoneIndex = static_cast(bone.parentIndex.value_or(0u)); + assert(parentBoneIndex < boneIndex); + + xmodel.parentList[nonRootIndex] = static_cast(boneIndex - parentBoneIndex); auto& trans = xmodel.trans[nonRootIndex]; trans.x = bone.localOffset[0]; diff --git a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperXModel.cpp b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperXModel.cpp index d160dc77..98a324ab 100644 --- a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperXModel.cpp +++ b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperXModel.cpp @@ -136,7 +136,7 @@ namespace if (model->partClassification[boneNum]) { if (boneNum < model->numRootBones - || model->partClassification[model->parentList[boneNum - model->numRootBones]] != model->partClassification[boneNum]) + || model->partClassification[boneNum - model->parentList[boneNum - model->numRootBones]] != model->partClassification[boneNum]) { std::cerr << std::format("Part: {:02} = {}\n", model->partClassification[boneNum], bone.name); } From 4db13de4710fdf2cdc4d5bb14c1eac25504b2db9 Mon Sep 17 00:00:00 2001 From: Jan Date: Sat, 10 Aug 2024 20:48:29 +0200 Subject: [PATCH 06/26] chore: properly calculate model and bone bounds --- .../Game/T6/XModel/JsonXModelLoader.cpp | 34 +++++++++++++++++++ src/ObjLoading/XModel/Gltf/GltfLoader.cpp | 11 +++++- 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/src/ObjLoading/Game/T6/XModel/JsonXModelLoader.cpp b/src/ObjLoading/Game/T6/XModel/JsonXModelLoader.cpp index ea60842c..a1311d41 100644 --- a/src/ObjLoading/Game/T6/XModel/JsonXModelLoader.cpp +++ b/src/ObjLoading/Game/T6/XModel/JsonXModelLoader.cpp @@ -479,16 +479,50 @@ namespace }); } + static void CalculateModelBounds(XModel& xmodel) + { + if (!xmodel.surfs) + return; + + for (auto surfaceIndex = 0u; surfaceIndex < xmodel.lodInfo[0].numsurfs; surfaceIndex++) + { + const auto& surface = xmodel.surfs[surfaceIndex + xmodel.lodInfo[0].surfIndex]; + + if (!surface.verts0) + continue; + + for (auto vertIndex = 0u; vertIndex < surface.vertCount; vertIndex++) + { + const auto& vertex = surface.verts0[vertIndex]; + + xmodel.mins.x = std::min(xmodel.mins.x, vertex.xyz.v[0]); + xmodel.mins.y = std::min(xmodel.mins.y, vertex.xyz.v[1]); + xmodel.mins.z = std::min(xmodel.mins.z, vertex.xyz.v[2]); + xmodel.maxs.x = std::max(xmodel.maxs.x, vertex.xyz.v[0]); + xmodel.maxs.y = std::max(xmodel.maxs.y, vertex.xyz.v[1]); + xmodel.maxs.z = std::max(xmodel.maxs.z, vertex.xyz.v[2]); + } + } + + const auto maxX = std::max(std::abs(xmodel.mins.x), std::abs(xmodel.maxs.x)); + const auto maxY = std::max(std::abs(xmodel.mins.y), std::abs(xmodel.maxs.y)); + const auto maxZ = std::max(std::abs(xmodel.mins.z), std::abs(xmodel.maxs.z)); + xmodel.radius = Eigen::Vector3f(maxX, maxY, maxZ).norm(); + } + bool CreateXModelFromJson(const JsonXModel& jXModel, XModel& xmodel) { auto lodNumber = 0u; for (const auto& jLod : jXModel.lods) LoadLod(jLod, xmodel, lodNumber++); + xmodel.numLods = static_cast(jXModel.lods.size()); xmodel.numsurfs = static_cast(m_surfaces.size()); xmodel.surfs = m_memory.Alloc(xmodel.numsurfs); memcpy(xmodel.surfs, m_surfaces.data(), sizeof(XSurface) * xmodel.numsurfs); + CalculateModelBounds(xmodel); + if (jXModel.collLod && jXModel.collLod.value() >= 0) { if (static_cast(jXModel.collLod.value()) >= jXModel.lods.size()) diff --git a/src/ObjLoading/XModel/Gltf/GltfLoader.cpp b/src/ObjLoading/XModel/Gltf/GltfLoader.cpp index 3de75c3f..b17011ca 100644 --- a/src/ObjLoading/XModel/Gltf/GltfLoader.cpp +++ b/src/ObjLoading/XModel/Gltf/GltfLoader.cpp @@ -267,13 +267,22 @@ namespace unsigned joints[4]; float weights[4]; - if (!positionAccessor->GetFloatVec3(vertexIndex, vertex.coordinates) || !normalAccessor->GetFloatVec3(vertexIndex, vertex.normal) + float coordinates[3]; + float normal[3]; + if (!positionAccessor->GetFloatVec3(vertexIndex, coordinates) || !normalAccessor->GetFloatVec3(vertexIndex, normal) || !colorAccessor->GetFloatVec4(vertexIndex, vertex.color) || !colorAccessor->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]; + common.m_vertices.emplace_back(vertex); XModelVertexBoneWeights vertexWeights{common.m_bone_weight_data.weights.size(), 0u}; From 0d343dd3faa1ef2a493e5c1dc7e4bdc1405628f5 Mon Sep 17 00:00:00 2001 From: Jan Date: Sat, 10 Aug 2024 22:12:32 +0200 Subject: [PATCH 07/26] chore: dump and load lod dist from xmodel json --- src/ObjCommon/Game/T6/Json/JsonXModel.h | 3 ++- src/ObjLoading/Game/T6/XModel/JsonXModelLoader.cpp | 6 ++++-- src/ObjWriting/Game/T6/XModel/JsonXModelWriter.cpp | 3 ++- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/ObjCommon/Game/T6/Json/JsonXModel.h b/src/ObjCommon/Game/T6/Json/JsonXModel.h index 5e725a32..26e0c2f5 100644 --- a/src/ObjCommon/Game/T6/Json/JsonXModel.h +++ b/src/ObjCommon/Game/T6/Json/JsonXModel.h @@ -16,9 +16,10 @@ namespace T6 { public: std::string file; + float distance; }; - NLOHMANN_DEFINE_TYPE_EXTENSION(JsonXModelLod, file); + NLOHMANN_DEFINE_TYPE_EXTENSION(JsonXModelLod, file, distance); class JsonXModel { diff --git a/src/ObjLoading/Game/T6/XModel/JsonXModelLoader.cpp b/src/ObjLoading/Game/T6/XModel/JsonXModelLoader.cpp index a1311d41..464e5d6c 100644 --- a/src/ObjLoading/Game/T6/XModel/JsonXModelLoader.cpp +++ b/src/ObjLoading/Game/T6/XModel/JsonXModelLoader.cpp @@ -464,8 +464,10 @@ namespace return false; } - xmodel.lodInfo[lodNumber].surfIndex = static_cast(m_surfaces.size()); - xmodel.lodInfo[lodNumber].numsurfs = static_cast(common->m_objects.size()); + auto& lodInfo = xmodel.lodInfo[lodNumber]; + lodInfo.dist = jLod.distance; + lodInfo.surfIndex = static_cast(m_surfaces.size()); + lodInfo.numsurfs = static_cast(common->m_objects.size()); return std::ranges::all_of(common->m_objects, [this, &common](const XModelObject& commonObject) diff --git a/src/ObjWriting/Game/T6/XModel/JsonXModelWriter.cpp b/src/ObjWriting/Game/T6/XModel/JsonXModelWriter.cpp index cda24133..05104da2 100644 --- a/src/ObjWriting/Game/T6/XModel/JsonXModelWriter.cpp +++ b/src/ObjWriting/Game/T6/XModel/JsonXModelWriter.cpp @@ -61,7 +61,7 @@ namespace } } - void CreateJsonXModel(JsonXModel& jXModel, const XModel& xmodel) const + static void CreateJsonXModel(JsonXModel& jXModel, const XModel& xmodel) { if (xmodel.collLod >= 0) jXModel.collLod = xmodel.collLod; @@ -70,6 +70,7 @@ namespace { JsonXModelLod lod; lod.file = std::format("model_export/{}_lod{}{}", xmodel.name, lodNumber, GetExtensionForModelByConfig()); + lod.distance = xmodel.lodInfo[lodNumber].dist; jXModel.lods.emplace_back(std::move(lod)); } From dfd8d1dbe90711d83588d3c7233a9d43441350cb Mon Sep 17 00:00:00 2001 From: Jan Date: Sat, 10 Aug 2024 22:12:51 +0200 Subject: [PATCH 08/26] chore: load materials for xmodel --- .../Game/T6/XModel/JsonXModelLoader.cpp | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/ObjLoading/Game/T6/XModel/JsonXModelLoader.cpp b/src/ObjLoading/Game/T6/XModel/JsonXModelLoader.cpp index 464e5d6c..9a2a7c9b 100644 --- a/src/ObjLoading/Game/T6/XModel/JsonXModelLoader.cpp +++ b/src/ObjLoading/Game/T6/XModel/JsonXModelLoader.cpp @@ -469,14 +469,27 @@ namespace lodInfo.surfIndex = static_cast(m_surfaces.size()); lodInfo.numsurfs = static_cast(common->m_objects.size()); + std::vector materialAssets; + materialAssets.reserve(common->m_materials.size()); + for (const auto& commonMaterial : common->m_materials) + { + auto* assetInfo = m_manager.LoadDependency(commonMaterial.name); + if (!assetInfo) + return false; + + m_dependencies.emplace(assetInfo); + materialAssets.push_back(assetInfo->Asset()); + } + return std::ranges::all_of(common->m_objects, - [this, &common](const XModelObject& commonObject) + [this, &common, &materialAssets](const XModelObject& commonObject) { XSurface surface{}; if (!CreateXSurface(surface, commonObject, *common)) return false; m_surfaces.emplace_back(surface); + m_materials.push_back(materialAssets[commonObject.materialIndex]); return true; }); } @@ -521,7 +534,9 @@ namespace xmodel.numsurfs = static_cast(m_surfaces.size()); xmodel.surfs = m_memory.Alloc(xmodel.numsurfs); + xmodel.materialHandles = m_memory.Alloc(xmodel.numsurfs); memcpy(xmodel.surfs, m_surfaces.data(), sizeof(XSurface) * xmodel.numsurfs); + memcpy(xmodel.materialHandles, m_materials.data(), sizeof(Material*) * xmodel.numsurfs); CalculateModelBounds(xmodel); @@ -579,6 +594,7 @@ namespace } std::vector m_surfaces; + std::vector m_materials; std::istream& m_stream; MemoryManager& m_memory; From 01c284fb37a07e158679f9d4e231080a4d3c8cc3 Mon Sep 17 00:00:00 2001 From: Jan Date: Sat, 10 Aug 2024 23:41:37 +0200 Subject: [PATCH 09/26] chore: update t6 xmodel part classifications --- raw/t6/partclassification.csv | 44 +++--- raw/t6/partclassification_mp.csv | 42 +++--- raw/t6/partclassification_new.csv | 140 ------------------ .../Game/T6/XModel/JsonXModelLoader.cpp | 27 +++- .../T6/AssetDumpers/AssetDumperXModel.cpp | 9 -- 5 files changed, 70 insertions(+), 192 deletions(-) delete mode 100644 raw/t6/partclassification_new.csv diff --git a/raw/t6/partclassification.csv b/raw/t6/partclassification.csv index 66ac687f..e0622b8d 100644 --- a/raw/t6/partclassification.csv +++ b/raw/t6/partclassification.csv @@ -1,21 +1,23 @@ -J_Hip_RI,right_leg_upper -J_Hip_LE,left_leg_upper -J_Knee_RI,right_leg_lower -J_SpineUpper,torso_upper -J_Knee_LE,left_leg_lower -J_Ankle_RI,right_foot -J_Ankle_LE,left_foot -J_Clavicle_RI,torso_upper -J_Clavicle_LE,torso_upper -J_Shoulder_RI,right_arm_upper -J_Shoulder_LE,left_arm_upper -J_Neck,neck -J_Head,head -J_Elbow_RI,right_arm_lower -J_Elbow_LE,left_arm_lower -J_Wrist_RI,right_hand -J_Wrist_LE,left_hand -J_MainRoot,torso_lower -TAG_WEAPON_LEFT,gun -TAG_WEAPON_RIGHT,gun -J_Helmet,helmet +j_helmet,helmet +j_head,head +j_neck,neck +j_clavicle_le,torso_upper +j_clavicle_ri,torso_upper +j_spineupper,torso_upper +j_mainroot,torso_lower +j_shoulder_ri,right_arm_upper +j_shoulder_le,left_arm_upper +j_elbow_ri,right_arm_lower +j_elbow_le,left_arm_lower +j_wrist_ri,right_hand +j_wrist_le,left_hand +j_hip_ri,right_leg_upper +j_hip_le,left_leg_upper +j_knee_ri,right_leg_lower +j_knee_le,left_leg_lower +j_ankle_ri,right_foot +j_ankle_le,left_foot +tag_weapon_left,gun +tag_weapon_right,gun +tag_stowed_back,shield +tag_weapon_left,shield diff --git a/raw/t6/partclassification_mp.csv b/raw/t6/partclassification_mp.csv index 87ee201f..9ee2ad70 100644 --- a/raw/t6/partclassification_mp.csv +++ b/raw/t6/partclassification_mp.csv @@ -1,19 +1,23 @@ -J_Hip_RI,right_leg_upper -J_Hip_LE,left_leg_upper -J_Knee_RI,right_leg_lower -J_SpineUpper,torso_lower -J_SpineLower,torso_lower -J_MainRoot,torso_lower -J_Knee_LE,left_leg_lower -J_Ankle_RI,right_foot -J_Ankle_LE,left_foot -J_Clavicle_RI,torso_upper -J_Clavicle_LE,torso_upper -J_Shoulder_RI,right_arm_upper -J_Shoulder_LE,left_arm_upper -J_Neck,neck -J_Head,head -J_Elbow_RI,right_arm_lower -J_Elbow_LE,left_arm_lower -J_Wrist_RI,right_hand -J_Wrist_LE,left_hand +j_helmet,helmet +j_head,head +j_neck,neck +j_clavicle_le,torso_upper +j_clavicle_ri,torso_upper +j_spineupper,torso_middle +j_mainroot,torso_lower +j_shoulder_ri,right_arm_upper +j_shoulder_le,left_arm_upper +j_elbow_ri,right_arm_lower +j_elbow_le,left_arm_lower +j_wrist_ri,right_hand +j_wrist_le,left_hand +j_hip_ri,right_leg_upper +j_hip_le,left_leg_upper +j_knee_ri,right_leg_lower +j_knee_le,left_leg_lower +j_ankle_ri,right_foot +j_ankle_le,left_foot +tag_weapon_left,gun +tag_weapon_right,gun +tag_stowed_back,shield +tag_weapon_left,shield diff --git a/raw/t6/partclassification_new.csv b/raw/t6/partclassification_new.csv deleted file mode 100644 index f22da27e..00000000 --- a/raw/t6/partclassification_new.csv +++ /dev/null @@ -1,140 +0,0 @@ -j_helmet,helmet -head,02 -j_brow_le,02 -j_brow_ri,02 -j_cheek_le,02 -j_cheek_ri,02 -j_cheekpuff_le,02 -j_cheekpuff_ri,02 -j_eye_lid_bot_le,02 -j_eye_lid_bot_ri,02 -j_eye_lid_top_le,02 -j_eye_lid_top_ri,02 -j_eyeball_le,02 -j_eyeball_ri,02 -j_eyebrow_top_le,02 -j_eyebrow_top_ri,02 -j_eyelid_bottom_le,02 -j_eyelid_bottom_ri,02 -j_eyelid_top_le,02 -j_eyelid_top_ri,02 -j_head,02 -j_head_end,02 -j_helmet,02 -j_jaw,02 -j_levator_le,02 -j_levator_ri,02 -j_lip_bot_le,02 -j_lip_bot_ri,02 -j_lip_top_le,02 -j_lip_top_ri,02 -j_mouth_le,02 -j_mouth_ri,02 -j_nose,02 -le_ear,02 -le_ear_end,02 -ri_ear,02 -ri_ear_end,02 -tag_eye,02 -tag_mouth_fx,02 -j_neck,03 -j_neck_end,03 -neck,03 -j_clavicle_le,04 -j_clavicle_ri,04 -back_mid,05 -j_shoulderraise_le,05 -j_shoulderraise_ri,05 -j_spine4,05 -j_spineupper,05 -tag_inhand,05 -tag_weapon_chest,05 -torso_stabilizer,06 -j_elbow_bulge_ri,07 -j_shoulder_ri,07 -j_shouldertwist_ri,07 -shoulder,07 -j_elbow_bulge_le,08 -j_shoulder_le,08 -j_shouldertwist_le,08 -j_elbow_ri,09 -j_sleave_reshape_top_ri_1,09 -j_sleave_reshape_top_ri_2,09 -j_sleave_reshape_top_ri_3,09 -j_wristtwist_ri,09 -j_elbow_le,10 -j_sleave_reshape_bottom_le_1,10 -j_sleave_reshape_bottom_le_2,10 -j_sleave_reshape_top_le_1,10 -j_sleave_reshape_top_le_2,10 -j_sleave_reshape_top_le_3,10 -j_wristtwist_le,10 -j_index_ri_0,11 -j_index_ri_1,11 -j_index_ri_2,11 -j_index_ri_3,11 -j_mid_ri_0,11 -j_mid_ri_1,11 -j_mid_ri_2,11 -j_mid_ri_3,11 -j_palm_end_ri,11 -j_palm_ri,11 -j_pinky_ri_0,11 -j_pinky_ri_1,11 -j_pinky_ri_2,11 -j_pinky_ri_3,11 -j_pinkypalm_ri,11 -j_ring_ri_0,11 -j_ring_ri_1,11 -j_ring_ri_2,11 -j_ring_ri_3,11 -j_ringpalm_ri,11 -j_thumb_ri_0,11 -j_thumb_ri_1,11 -j_thumb_ri_2,11 -j_thumb_ri_3,11 -j_webbing_ri,11 -j_wrist_ri,11 -tag_weapon_right,11 -j_index_le_0,12 -j_index_le_1,12 -j_index_le_2,12 -j_index_le_3,12 -j_mid_le_0,12 -j_mid_le_1,12 -j_mid_le_2,12 -j_mid_le_3,12 -j_palm_end_le,12 -j_palm_le,12 -j_pinky_le_0,12 -j_pinky_le_1,12 -j_pinky_le_2,12 -j_pinky_le_3,12 -j_pinkypalm_le,12 -j_ring_le_0,12 -j_ring_le_1,12 -j_ring_le_2,12 -j_ring_le_3,12 -j_ringpalm_le,12 -j_thumb_le_0,12 -j_thumb_le_1,12 -j_thumb_le_2,12 -j_thumb_le_3,12 -j_webbing_le,12 -j_wrist_le,12 -tag_knife_attach,12 -j_hip_ri,13 -j_hip_le,14 -j_hiptwist_le,14 -j_knee_bulge_ri,15 -j_knee_ri,15 -j_knee_bulge_le,16 -j_knee_le,16 -j_ankle_ri,17 -j_ball_ri,17 -j_toe_ri,17 -j_ankle_le,18 -j_ball_le,18 -j_toe_le,18 -tag_stowed_back,20 -tag_weapon_left,20 \ No newline at end of file diff --git a/src/ObjLoading/Game/T6/XModel/JsonXModelLoader.cpp b/src/ObjLoading/Game/T6/XModel/JsonXModelLoader.cpp index 9a2a7c9b..677f4973 100644 --- a/src/ObjLoading/Game/T6/XModel/JsonXModelLoader.cpp +++ b/src/ObjLoading/Game/T6/XModel/JsonXModelLoader.cpp @@ -30,14 +30,35 @@ namespace fs = std::filesystem; namespace { const char* HITLOC_NAMES[]{ - "none", "helmet", "head", "neck", "torso_upper", "torso_middle", "torso_lower", "right_arm_upper", - "left_arm_upper", "right_arm_lower", "left_arm_lower", "right_hand", "left_hand", "right_leg_upper", "left_leg_upper", "right_leg_lower", - "left_leg_lower", "right_foot", "left_foot", "gun", "shield", + // clang-format off + "none", + "helmet", + "head", + "neck", + "torso_upper", + "torso_middle", + "torso_lower", + "right_arm_upper", + "left_arm_upper", + "right_arm_lower", + "left_arm_lower", + "right_hand", + "left_hand", + "right_leg_upper", + "left_leg_upper", + "right_leg_lower", + "left_leg_lower", + "right_foot", + "left_foot", + "gun", + "shield", + // clang-format on }; static_assert(std::extent_v == HITLOC_COUNT); class PartClassificationState final : public IZoneAssetLoaderState { + // TODO: Use MP part classifications when building an mp fastfile static constexpr auto PART_CLASSIFICATION_FILE = "partclassification.csv"; public: diff --git a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperXModel.cpp b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperXModel.cpp index 98a324ab..0d77d8a8 100644 --- a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperXModel.cpp +++ b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperXModel.cpp @@ -133,15 +133,6 @@ namespace bone.scale[1] = 1.0f; bone.scale[2] = 1.0f; - if (model->partClassification[boneNum]) - { - if (boneNum < model->numRootBones - || model->partClassification[boneNum - model->parentList[boneNum - model->numRootBones]] != model->partClassification[boneNum]) - { - std::cerr << std::format("Part: {:02} = {}\n", model->partClassification[boneNum], bone.name); - } - } - const auto& baseMat = model->baseMat[boneNum]; bone.globalOffset[0] = baseMat.trans.x; bone.globalOffset[1] = baseMat.trans.y; From f35a2f33ff2039d9cd7440c3834bba61e6c50495 Mon Sep 17 00:00:00 2001 From: Jan Date: Mon, 12 Aug 2024 23:29:11 +0200 Subject: [PATCH 10/26] fix: linux build --- src/ObjLoading/XModel/Gltf/GltfBinInput.cpp | 1 + src/ObjLoading/XModel/Gltf/GltfLoader.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ObjLoading/XModel/Gltf/GltfBinInput.cpp b/src/ObjLoading/XModel/Gltf/GltfBinInput.cpp index 1167c6b2..b954adca 100644 --- a/src/ObjLoading/XModel/Gltf/GltfBinInput.cpp +++ b/src/ObjLoading/XModel/Gltf/GltfBinInput.cpp @@ -3,6 +3,7 @@ #include "XModel/Gltf/GltfConstants.h" #include +#include #include #include diff --git a/src/ObjLoading/XModel/Gltf/GltfLoader.cpp b/src/ObjLoading/XModel/Gltf/GltfLoader.cpp index b17011ca..d7eb4d4c 100644 --- a/src/ObjLoading/XModel/Gltf/GltfLoader.cpp +++ b/src/ObjLoading/XModel/Gltf/GltfLoader.cpp @@ -72,7 +72,7 @@ namespace return m_message; } - [[nodiscard]] const char* what() const override + [[nodiscard]] const char* what() const noexcept override { return m_message.c_str(); } From f4092972e8f093ca1ec6fdab297aac98f87a7213 Mon Sep 17 00:00:00 2001 From: Jan Date: Sun, 11 Aug 2024 01:14:38 +0200 Subject: [PATCH 11/26] chore: implement vertex weights --- src/Common/Game/T6/T6_Assets.h | 11 +- .../Game/T6/XModel/JsonXModelLoader.cpp | 203 ++++++++++++++++-- .../T6/AssetDumpers/AssetDumperXModel.cpp | 6 +- 3 files changed, 200 insertions(+), 20 deletions(-) diff --git a/src/Common/Game/T6/T6_Assets.h b/src/Common/Game/T6/T6_Assets.h index 0ac7f846..edb7c063 100644 --- a/src/Common/Game/T6/T6_Assets.h +++ b/src/Common/Game/T6/T6_Assets.h @@ -2749,7 +2749,12 @@ namespace T6 float* tensionData; }; - typedef tdef_align(16) unsigned short r_index16_t; + struct XSurfaceTri + { + uint16_t i[3]; + }; + + typedef tdef_align(16) XSurfaceTri XSurfaceTri16; struct type_align(16) XSurface { @@ -2759,13 +2764,13 @@ namespace T6 uint16_t vertCount; uint16_t triCount; uint16_t baseVertIndex; - r_index16_t(*triIndices)[3]; + XSurfaceTri16* triIndices; XSurfaceVertexInfo vertInfo; GfxPackedVertex* verts0; void /*ID3D11Buffer*/* vb0; XRigidVertList* vertList; void /*ID3D11Buffer*/* indexBuffer; - int partBits[5]; + unsigned int partBits[5]; }; struct XModelCollSurf_s diff --git a/src/ObjLoading/Game/T6/XModel/JsonXModelLoader.cpp b/src/ObjLoading/Game/T6/XModel/JsonXModelLoader.cpp index 677f4973..01538b9f 100644 --- a/src/ObjLoading/Game/T6/XModel/JsonXModelLoader.cpp +++ b/src/ObjLoading/Game/T6/XModel/JsonXModelLoader.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include using namespace nlohmann; @@ -412,13 +413,166 @@ namespace vertex.tangent = Common::Vec3PackUnitVec(wrongTangent); // TODO: Fill with actual value } + static size_t GetRigidBoneForVertex(const size_t vertexIndex, const XModelCommon& common) + { + return common.m_bone_weight_data.weights[common.m_vertex_bone_weights[vertexIndex].weightOffset].boneIndex; + } + + static std::vector> + GetRigidBoneIndicesForTris(const std::vector& vertexIndices, XSurface& surface, const XModelCommon& common) + { + std::vector> rigidBoneIndexForTri; + rigidBoneIndexForTri.reserve(surface.triCount); + for (auto triIndex = 0u; triIndex < surface.triCount; triIndex++) + { + const auto& tri = surface.triIndices[triIndex]; + const auto vert0Bone = GetRigidBoneForVertex(vertexIndices[tri.i[0]], common); + const auto vert1Bone = GetRigidBoneForVertex(vertexIndices[tri.i[1]], common); + const auto vert2Bone = GetRigidBoneForVertex(vertexIndices[tri.i[2]], common); + + const auto hasSameBone = vert0Bone == vert1Bone && vert1Bone == vert2Bone; + if (hasSameBone) + rigidBoneIndexForTri.emplace_back(vert0Bone); + else + rigidBoneIndexForTri.emplace_back(std::nullopt); + } + + return rigidBoneIndexForTri; + } + + static void ReorderRigidTrisByBoneIndex(const std::vector& vertexIndices, XSurface& surface, const XModelCommon& common) + { + const auto rigidBoneIndexForTri = GetRigidBoneIndicesForTris(vertexIndices, surface, common); + + std::vector triSortList(surface.triCount); + std::ranges::iota(triSortList, 0); + + std::ranges::sort(triSortList, + [&rigidBoneIndexForTri](const size_t triIndex0, const size_t triIndex1) + { + const auto rigidBone0 = rigidBoneIndexForTri[triIndex0]; + const auto rigidBone1 = rigidBoneIndexForTri[triIndex1]; + + if (rigidBone0.has_value() != rigidBone1.has_value()) + return rigidBone0.has_value(); + if (!rigidBone0.has_value()) + return true; + + return *rigidBone0 < *rigidBone1; + }); + + std::vector> sortedTris(surface.triCount); + for (auto i = 0u; i < surface.triCount; i++) + memcpy(&sortedTris[i], &surface.triIndices[triSortList[i]], sizeof(std::remove_pointer_t)); + memcpy(surface.triIndices, sortedTris.data(), sizeof(std::remove_pointer_t) * surface.triCount); + } + + void CreateVertListData(XSurface& surface, const std::vector& vertexIndices, const XModelCommon& common) + { + ReorderRigidTrisByBoneIndex(vertexIndices, surface, common); + const auto rigidBoneIndexForTri = GetRigidBoneIndicesForTris(vertexIndices, surface, common); + + std::vector vertLists; + + auto currentVertexTail = 0u; + auto currentTriTail = 0u; + + const auto vertexCount = vertexIndices.size(); + const auto triCount = static_cast(surface.triCount); + const auto boneCount = common.m_bones.size(); + for (auto boneIndex = 0u; boneIndex < boneCount; boneIndex++) + { + XRigidVertList boneVertList{}; + boneVertList.boneOffset = static_cast(boneIndex * sizeof(DObjSkelMat)); + + auto currentVertexHead = currentVertexTail; + while (currentVertexHead < vertexCount && GetRigidBoneForVertex(currentVertexHead, common) == boneIndex) + currentVertexHead++; + + auto currentTriHead = currentTriTail; + while (currentTriHead < triCount && rigidBoneIndexForTri[currentTriHead] && *rigidBoneIndexForTri[currentTriHead] == boneIndex) + currentTriHead++; + + boneVertList.vertCount = static_cast(currentVertexHead - currentVertexTail); + boneVertList.triOffset = static_cast(currentTriTail); + boneVertList.triCount = static_cast(currentTriHead - currentTriTail); + + if (boneVertList.triCount > 0 || boneVertList.vertCount > 0) + { + boneVertList.collisionTree = nullptr; // TODO + vertLists.emplace_back(boneVertList); + + currentVertexTail = currentVertexHead; + currentTriTail = currentTriHead; + } + } + + if (!vertLists.empty()) + { + surface.vertListCount = static_cast(vertLists.size()); + surface.vertList = m_memory.Alloc(surface.vertListCount); + + memcpy(surface.vertList, vertLists.data(), sizeof(XRigidVertList) * surface.vertListCount); + } + } + + void CreateVertsBlendData(XSurface& surface, const std::vector& vertexIndices, const XModelCommon& common) + { + // TODO + assert(false); + } + + static void ReorderVerticesByWeightCount(std::vector& vertexIndices, XSurface& surface, const XModelCommon& common) + { + if (common.m_vertex_bone_weights.empty()) + return; + + const auto vertexCount = vertexIndices.size(); + std::vector reorderLookup(vertexCount); + std::ranges::iota(reorderLookup, 0); + + std::ranges::sort(reorderLookup, + [&common, &vertexIndices](const size_t& i0, const size_t& i1) + { + const auto& weights0 = common.m_vertex_bone_weights[vertexIndices[i0]]; + const auto& weights1 = common.m_vertex_bone_weights[vertexIndices[i1]]; + + if (weights0.weightCount < weights1.weightCount) + return true; + + // If there is only one weight, make sure all vertices of the same bone follow another + if (weights0.weightCount == 1) + { + const auto bone0 = common.m_bone_weight_data.weights[weights0.weightOffset].boneIndex; + const auto bone1 = common.m_bone_weight_data.weights[weights1.weightOffset].boneIndex; + return bone0 < bone1; + } + + return false; + }); + + for (auto triIndex = 0u; triIndex < surface.triCount; triIndex++) + { + auto& triIndices = surface.triIndices[triIndex]; + + triIndices.i[0] = static_cast(reorderLookup[triIndices.i[0]]); + triIndices.i[1] = static_cast(reorderLookup[triIndices.i[1]]); + triIndices.i[2] = static_cast(reorderLookup[triIndices.i[2]]); + } + + for (auto& entry : reorderLookup) + entry = vertexIndices[entry]; + + vertexIndices = std::move(reorderLookup); + } + bool CreateXSurface(XSurface& surface, const XModelObject& commonObject, const XModelCommon& common) { - std::vector verts; + std::vector vertexIndices; std::unordered_map usedVertices; surface.triCount = static_cast(commonObject.m_faces.size()); - surface.triIndices = m_memory.Alloc(commonObject.m_faces.size()); + surface.triIndices = m_memory.Alloc(commonObject.m_faces.size()); const auto faceCount = commonObject.m_faces.size(); for (auto faceIndex = 0u; faceIndex < faceCount; faceIndex++) @@ -432,25 +586,46 @@ namespace const auto existingVertex = usedVertices.find(vertexIndex); if (existingVertex == usedVertices.end()) { - const auto newVertexIndex = verts.size(); - tris[faceVertexIndex] = static_cast(newVertexIndex); + const auto newVertexIndex = vertexIndices.size(); + tris.i[faceVertexIndex] = static_cast(newVertexIndex); - const auto& commonVertex = common.m_vertices[vertexIndex]; - GfxPackedVertex vertex{}; - - CreateVertex(vertex, commonVertex); - - verts.emplace_back(vertex); + vertexIndices.emplace_back(vertexIndex); usedVertices.emplace(vertexIndex, newVertexIndex); } else - tris[faceVertexIndex] = static_cast(existingVertex->second); + tris.i[faceVertexIndex] = static_cast(existingVertex->second); } } - surface.vertCount = static_cast(verts.size()); - surface.verts0 = m_memory.Alloc(verts.size()); - memcpy(surface.verts0, verts.data(), sizeof(GfxPackedVertex) * verts.size()); + ReorderVerticesByWeightCount(vertexIndices, surface, common); + + const auto vertexCount = vertexIndices.size(); + surface.vertCount = static_cast(vertexCount); + surface.verts0 = m_memory.Alloc(vertexCount); + + for (auto vertexIndex = 0u; vertexIndex < vertexCount; vertexIndex++) + { + const auto& commonVertex = common.m_vertices[vertexIndex]; + CreateVertex(surface.verts0[vertexIndex], commonVertex); + } + + if (!common.m_vertex_bone_weights.empty()) + { + // Since bone weights are sorted by weight count, the last must have the highest weight count + const auto hasVertsBlend = common.m_vertex_bone_weights[vertexIndices[vertexIndices.size() - 1]].weightCount > 1; + if (!hasVertsBlend) + CreateVertListData(surface, vertexIndices, common); + else + CreateVertsBlendData(surface, vertexIndices, common); + } + + const auto boneCount = common.m_bones.size(); + for (auto boneIndex = 0u; boneIndex < boneCount; boneIndex++) + { + const auto partBitsIndex = boneIndex / 32u; + const auto shiftValue = 31u - (boneIndex % 32u); + surface.partBits[partBitsIndex] = 1 << (31u - shiftValue); + } return true; } diff --git a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperXModel.cpp b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperXModel.cpp index 0d77d8a8..7c0f0df1 100644 --- a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperXModel.cpp +++ b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperXModel.cpp @@ -424,9 +424,9 @@ namespace const auto& tri = surface.triIndices[triIndex]; XModelFace face{}; - face.vertexIndex[0] = tri[0] + surface.baseVertIndex; - face.vertexIndex[1] = tri[1] + surface.baseVertIndex; - face.vertexIndex[2] = tri[2] + surface.baseVertIndex; + face.vertexIndex[0] = tri.i[0] + surface.baseVertIndex; + face.vertexIndex[1] = tri.i[1] + surface.baseVertIndex; + face.vertexIndex[2] = tri.i[2] + surface.baseVertIndex; object.m_faces.emplace_back(face); } } From 30d9ffd8c921b79a3d8e6b6fd8671da2c85f48c0 Mon Sep 17 00:00:00 2001 From: Jan Date: Tue, 3 Sep 2024 19:02:25 +0200 Subject: [PATCH 12/26] chore: add byteoffset to gltf loading accessors --- src/ObjLoading/XModel/Gltf/GltfLoader.cpp | 7 ++-- .../XModel/Gltf/Internal/GltfAccessor.cpp | 35 ++++++++++--------- .../XModel/Gltf/Internal/GltfAccessor.h | 9 +++-- .../XModel/Gltf/Internal/GltfBufferView.cpp | 6 ++-- .../XModel/Gltf/Internal/GltfBufferView.h | 2 +- 5 files changed, 33 insertions(+), 26 deletions(-) diff --git a/src/ObjLoading/XModel/Gltf/GltfLoader.cpp b/src/ObjLoading/XModel/Gltf/GltfLoader.cpp index d7eb4d4c..b7610a46 100644 --- a/src/ObjLoading/XModel/Gltf/GltfLoader.cpp +++ b/src/ObjLoading/XModel/Gltf/GltfLoader.cpp @@ -636,12 +636,13 @@ namespace throw GltfLoadException("Accessor references invalid buffer view"); const auto* bufferView = m_buffer_views[*jAccessor.bufferView].get(); + const auto byteOffset = jAccessor.byteOffset.value_or(0u); if (jAccessor.componentType == JsonAccessorComponentType::FLOAT) - m_accessors.emplace_back(std::make_unique(bufferView, jAccessor.type, jAccessor.count)); + m_accessors.emplace_back(std::make_unique(bufferView, jAccessor.type, byteOffset, jAccessor.count)); else if (jAccessor.componentType == JsonAccessorComponentType::UNSIGNED_BYTE) - m_accessors.emplace_back(std::make_unique(bufferView, jAccessor.type, jAccessor.count)); + m_accessors.emplace_back(std::make_unique(bufferView, jAccessor.type, byteOffset, jAccessor.count)); else if (jAccessor.componentType == JsonAccessorComponentType::UNSIGNED_SHORT) - m_accessors.emplace_back(std::make_unique(bufferView, jAccessor.type, jAccessor.count)); + m_accessors.emplace_back(std::make_unique(bufferView, jAccessor.type, byteOffset, jAccessor.count)); else throw GltfLoadException(std::format("Accessor has unsupported component type {}", static_cast(jAccessor.componentType))); } diff --git a/src/ObjLoading/XModel/Gltf/Internal/GltfAccessor.cpp b/src/ObjLoading/XModel/Gltf/Internal/GltfAccessor.cpp index f33ff546..3ded1bb8 100644 --- a/src/ObjLoading/XModel/Gltf/Internal/GltfAccessor.cpp +++ b/src/ObjLoading/XModel/Gltf/Internal/GltfAccessor.cpp @@ -81,9 +81,10 @@ size_t NullAccessor::GetCount() const return m_count; } -FloatAccessor::FloatAccessor(const BufferView* bufferView, const JsonAccessorType type, const size_t count) +FloatAccessor::FloatAccessor(const BufferView* bufferView, const JsonAccessorType type, size_t byteOffset, const size_t count) : m_buffer_view(bufferView), m_type(type), + m_byte_offset(byteOffset), m_count(count) { } @@ -109,7 +110,7 @@ bool FloatAccessor::GetFloatVec2(const size_t index, float (&out)[2]) const if (index >= m_count) return false; - return m_buffer_view->ReadElement(&out, index, sizeof(float[2])); + return m_buffer_view->ReadElement(&out, index, sizeof(float[2]), m_byte_offset); } bool FloatAccessor::GetFloatVec3(const size_t index, float (&out)[3]) const @@ -118,7 +119,7 @@ bool FloatAccessor::GetFloatVec3(const size_t index, float (&out)[3]) const if (index >= m_count) return false; - return m_buffer_view->ReadElement(&out, index, sizeof(float[3])); + return m_buffer_view->ReadElement(&out, index, sizeof(float[3]), m_byte_offset); } bool FloatAccessor::GetFloatVec4(const size_t index, float (&out)[4]) const @@ -127,7 +128,7 @@ bool FloatAccessor::GetFloatVec4(const size_t index, float (&out)[4]) const if (index >= m_count) return false; - return m_buffer_view->ReadElement(&out, index, sizeof(float[4])); + return m_buffer_view->ReadElement(&out, index, sizeof(float[4]), m_byte_offset); } bool FloatAccessor::GetUnsigned(size_t index, unsigned& out) const @@ -140,9 +141,10 @@ bool FloatAccessor::GetUnsignedVec4(size_t index, unsigned (&out)[4]) const return false; } -UnsignedByteAccessor::UnsignedByteAccessor(const BufferView* bufferView, const JsonAccessorType type, const size_t count) +UnsignedByteAccessor::UnsignedByteAccessor(const BufferView* bufferView, const JsonAccessorType type, size_t byteOffset, const size_t count) : m_buffer_view(bufferView), m_type(type), + m_byte_offset(byteOffset), m_count(count) { } @@ -169,7 +171,7 @@ bool UnsignedByteAccessor::GetFloatVec2(const size_t index, float (&out)[2]) con return false; uint8_t temp[2]; - if (!m_buffer_view->ReadElement(temp, index, sizeof(uint8_t[2]))) + if (!m_buffer_view->ReadElement(temp, index, sizeof(uint8_t[2]), m_byte_offset)) return false; // Return as normalized value between 0 and 1 @@ -186,7 +188,7 @@ bool UnsignedByteAccessor::GetFloatVec3(const size_t index, float (&out)[3]) con return false; uint8_t temp[3]; - if (!m_buffer_view->ReadElement(temp, index, sizeof(uint8_t[3]))) + if (!m_buffer_view->ReadElement(temp, index, sizeof(uint8_t[3]), m_byte_offset)) return false; // Return as normalized value between 0 and 1 @@ -204,7 +206,7 @@ bool UnsignedByteAccessor::GetFloatVec4(const size_t index, float (&out)[4]) con return false; uint8_t temp[4]; - if (!m_buffer_view->ReadElement(temp, index, sizeof(uint8_t[4]))) + if (!m_buffer_view->ReadElement(temp, index, sizeof(uint8_t[4]), m_byte_offset)) return false; // Return as normalized value between 0 and 1 @@ -222,7 +224,7 @@ bool UnsignedByteAccessor::GetUnsigned(const size_t index, unsigned& out) const return false; uint8_t temp; - if (!m_buffer_view->ReadElement(&temp, index, sizeof(uint8_t))) + if (!m_buffer_view->ReadElement(&temp, index, sizeof(uint8_t), m_byte_offset)) return false; out = temp; @@ -236,7 +238,7 @@ bool UnsignedByteAccessor::GetUnsignedVec4(const size_t index, unsigned (&out)[4 return false; uint8_t temp[4]; - if (!m_buffer_view->ReadElement(temp, index, sizeof(uint8_t[4]))) + if (!m_buffer_view->ReadElement(temp, index, sizeof(uint8_t[4]), m_byte_offset)) return false; out[0] = static_cast(temp[0]); @@ -247,9 +249,10 @@ bool UnsignedByteAccessor::GetUnsignedVec4(const size_t index, unsigned (&out)[4 return true; } -UnsignedShortAccessor::UnsignedShortAccessor(const BufferView* bufferView, const JsonAccessorType type, const size_t count) +UnsignedShortAccessor::UnsignedShortAccessor(const BufferView* bufferView, const JsonAccessorType type, size_t byteOffset, const size_t count) : m_buffer_view(bufferView), m_type(type), + m_byte_offset(byteOffset), m_count(count) { } @@ -276,7 +279,7 @@ bool UnsignedShortAccessor::GetFloatVec2(const size_t index, float (&out)[2]) co return false; uint16_t temp[2]; - if (!m_buffer_view->ReadElement(temp, index, sizeof(uint16_t[2]))) + if (!m_buffer_view->ReadElement(temp, index, sizeof(uint16_t[2]), m_byte_offset)) return false; // Return as normalized value between 0 and 1 @@ -293,7 +296,7 @@ bool UnsignedShortAccessor::GetFloatVec3(const size_t index, float (&out)[3]) co return false; uint16_t temp[3]; - if (!m_buffer_view->ReadElement(temp, index, sizeof(uint16_t[3]))) + if (!m_buffer_view->ReadElement(temp, index, sizeof(uint16_t[3]), m_byte_offset)) return false; // Return as normalized value between 0 and 1 @@ -311,7 +314,7 @@ bool UnsignedShortAccessor::GetFloatVec4(const size_t index, float (&out)[4]) co return false; uint16_t temp[4]; - if (!m_buffer_view->ReadElement(temp, index, sizeof(uint16_t[4]))) + if (!m_buffer_view->ReadElement(temp, index, sizeof(uint16_t[4]), m_byte_offset)) return false; // Return as normalized value between 0 and 1 @@ -329,7 +332,7 @@ bool UnsignedShortAccessor::GetUnsigned(const size_t index, unsigned& out) const return false; uint16_t temp; - if (!m_buffer_view->ReadElement(&temp, index, sizeof(uint16_t))) + if (!m_buffer_view->ReadElement(&temp, index, sizeof(uint16_t), m_byte_offset)) return false; out = temp; @@ -343,7 +346,7 @@ bool UnsignedShortAccessor::GetUnsignedVec4(const size_t index, unsigned (&out)[ return false; uint16_t temp[4]; - if (!m_buffer_view->ReadElement(temp, index, sizeof(uint8_t[4]))) + if (!m_buffer_view->ReadElement(temp, index, sizeof(uint8_t[4]), m_byte_offset)) return false; out[0] = static_cast(temp[0]); diff --git a/src/ObjLoading/XModel/Gltf/Internal/GltfAccessor.h b/src/ObjLoading/XModel/Gltf/Internal/GltfAccessor.h index c2aeaaa7..12b442c3 100644 --- a/src/ObjLoading/XModel/Gltf/Internal/GltfAccessor.h +++ b/src/ObjLoading/XModel/Gltf/Internal/GltfAccessor.h @@ -49,7 +49,7 @@ namespace gltf class FloatAccessor final : public Accessor { public: - FloatAccessor(const BufferView* bufferView, JsonAccessorType type, size_t count); + FloatAccessor(const BufferView* bufferView, JsonAccessorType type, size_t byteOffset, size_t count); [[nodiscard]] std::optional GetType() const override; [[nodiscard]] std::optional GetComponentType() const override; @@ -64,13 +64,14 @@ namespace gltf private: const BufferView* m_buffer_view; JsonAccessorType m_type; + size_t m_byte_offset; size_t m_count; }; class UnsignedByteAccessor final : public Accessor { public: - UnsignedByteAccessor(const BufferView* bufferView, JsonAccessorType type, size_t count); + UnsignedByteAccessor(const BufferView* bufferView, JsonAccessorType type, size_t byteOffset, size_t count); [[nodiscard]] std::optional GetType() const override; [[nodiscard]] std::optional GetComponentType() const override; @@ -85,13 +86,14 @@ namespace gltf private: const BufferView* m_buffer_view; JsonAccessorType m_type; + size_t m_byte_offset; size_t m_count; }; class UnsignedShortAccessor final : public Accessor { public: - UnsignedShortAccessor(const BufferView* bufferView, JsonAccessorType type, size_t count); + UnsignedShortAccessor(const BufferView* bufferView, JsonAccessorType type, size_t byteOffset, size_t count); [[nodiscard]] std::optional GetType() const override; [[nodiscard]] std::optional GetComponentType() const override; @@ -106,6 +108,7 @@ namespace gltf private: const BufferView* m_buffer_view; JsonAccessorType m_type; + size_t m_byte_offset; size_t m_count; }; } // namespace gltf diff --git a/src/ObjLoading/XModel/Gltf/Internal/GltfBufferView.cpp b/src/ObjLoading/XModel/Gltf/Internal/GltfBufferView.cpp index cf071987..0b87fecb 100644 --- a/src/ObjLoading/XModel/Gltf/Internal/GltfBufferView.cpp +++ b/src/ObjLoading/XModel/Gltf/Internal/GltfBufferView.cpp @@ -10,10 +10,10 @@ BufferView::BufferView(const Buffer* buffer, const size_t offset, const size_t l { } -bool BufferView::ReadElement(void* dest, const size_t elementIndex, const size_t elementSize) const +bool BufferView::ReadElement(void* dest, const size_t elementIndex, const size_t elementSize, const size_t elementOffset) const { - const auto stride = std::max(elementSize, m_stride); - const auto bufferViewOffset = elementIndex * stride; + const auto stride = std::max(elementOffset + elementSize, m_stride); + const auto bufferViewOffset = elementOffset + elementIndex * stride; if (bufferViewOffset + elementSize > m_length) return false; diff --git a/src/ObjLoading/XModel/Gltf/Internal/GltfBufferView.h b/src/ObjLoading/XModel/Gltf/Internal/GltfBufferView.h index fc0e442c..2a69d77d 100644 --- a/src/ObjLoading/XModel/Gltf/Internal/GltfBufferView.h +++ b/src/ObjLoading/XModel/Gltf/Internal/GltfBufferView.h @@ -8,7 +8,7 @@ namespace gltf public: BufferView(const Buffer* buffer, size_t offset, size_t length, size_t stride); - bool ReadElement(void* dest, size_t elementIndex, size_t elementSize) const; + bool ReadElement(void* dest, size_t elementIndex, size_t elementSize, size_t elementOffset) const; private: const Buffer* m_buffer; From 14784fb1c4c79833c5627e619e9f845b0fb5155a Mon Sep 17 00:00:00 2001 From: Jan Date: Tue, 3 Sep 2024 19:55:23 +0200 Subject: [PATCH 13/26] chore: correct bone ordering when loading gltf --- src/ObjLoading/XModel/Gltf/GltfLoader.cpp | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/ObjLoading/XModel/Gltf/GltfLoader.cpp b/src/ObjLoading/XModel/Gltf/GltfLoader.cpp index b7610a46..cc7943fc 100644 --- a/src/ObjLoading/XModel/Gltf/GltfLoader.cpp +++ b/src/ObjLoading/XModel/Gltf/GltfLoader.cpp @@ -411,7 +411,9 @@ namespace } static bool ConvertJoint(const JsonRoot& jRoot, + const JsonSkin& skin, XModelCommon& common, + const unsigned skinBoneOffset, const unsigned nodeIndex, const std::optional parentIndex, const float (&parentOffset)[3], @@ -422,8 +424,13 @@ namespace return false; const auto& node = jRoot.nodes.value()[nodeIndex]; + const auto boneInSkin = std::ranges::find(skin.joints, nodeIndex); + if (boneInSkin == skin.joints.end()) + throw GltfLoadException("Bone node is not part of skin"); + const auto boneIndexInSkin = std::distance(skin.joints.begin(), boneInSkin); - XModelBone bone; + const auto commonBoneOffset = skinBoneOffset + boneIndexInSkin; + auto& bone = common.m_bones[commonBoneOffset]; bone.name = node.name.value_or(std::string()); bone.parentIndex = parentIndex; @@ -481,14 +488,11 @@ namespace bone.globalRotation.z = globalRotationEigen.z(); bone.globalRotation.w = globalRotationEigen.w(); - const auto commonIndex = common.m_bones.size(); - common.m_bones.emplace_back(std::move(bone)); - if (node.children) { for (const auto childIndex : *node.children) { - if (!ConvertJoint(jRoot, common, childIndex, commonIndex, bone.globalOffset, bone.globalRotation, bone.scale)) + if (!ConvertJoint(jRoot, skin, common, skinBoneOffset, childIndex, commonBoneOffset, bone.globalOffset, bone.globalRotation, bone.scale)) return false; } } @@ -507,12 +511,14 @@ namespace throw GltfLoadException("Only scenes with at most one skin are supported"); const auto rootNode = GetRootNodeForSkin(jRoot, skin).value_or(skin.joints[0]); + const auto skinBoneOffset = 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{0.0f, 0.0f, 0.0f, 1.0f}; constexpr float defaultScale[3]{1.0f, 1.0f, 1.0f}; - return ConvertJoint(jRoot, common, rootNode, std::nullopt, defaultTranslation, defaultRotation, defaultScale); + return ConvertJoint(jRoot, skin, common, skinBoneOffset, rootNode, std::nullopt, defaultTranslation, defaultRotation, defaultScale); } void ConvertObjects(const JsonRoot& jRoot, XModelCommon& common) From 0bd581ef75299a9761a5b812f807b329e1cd3fb1 Mon Sep 17 00:00:00 2001 From: Jan Date: Tue, 3 Sep 2024 19:55:38 +0200 Subject: [PATCH 14/26] chore: correct tri ordering when loading gltf --- src/ObjLoading/XModel/Gltf/GltfLoader.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ObjLoading/XModel/Gltf/GltfLoader.cpp b/src/ObjLoading/XModel/Gltf/GltfLoader.cpp index cc7943fc..5010974e 100644 --- a/src/ObjLoading/XModel/Gltf/GltfLoader.cpp +++ b/src/ObjLoading/XModel/Gltf/GltfLoader.cpp @@ -351,9 +351,9 @@ namespace } object.m_faces.emplace_back(XModelFace{ - vertexOffset + indices[0], - vertexOffset + indices[1], vertexOffset + indices[2], + vertexOffset + indices[1], + vertexOffset + indices[0], }); } From 36bc3cf7a3ea1e28c6be24b2a00a6d0544c038ce Mon Sep 17 00:00:00 2001 From: Jan Date: Tue, 3 Sep 2024 22:04:14 +0200 Subject: [PATCH 15/26] chore: fix invalid sign when loading gltf --- src/ObjLoading/XModel/Gltf/GltfLoader.cpp | 4 ++-- src/ObjWriting/XModel/Gltf/GltfWriter.cpp | 24 +++++++++++------------ 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/ObjLoading/XModel/Gltf/GltfLoader.cpp b/src/ObjLoading/XModel/Gltf/GltfLoader.cpp index 5010974e..5ef4508e 100644 --- a/src/ObjLoading/XModel/Gltf/GltfLoader.cpp +++ b/src/ObjLoading/XModel/Gltf/GltfLoader.cpp @@ -467,8 +467,8 @@ namespace if (node.rotation) { bone.localRotation.x = (*node.rotation)[0]; - bone.localRotation.y = (*node.rotation)[2]; - bone.localRotation.z = -(*node.rotation)[1]; + bone.localRotation.y = -(*node.rotation)[2]; + bone.localRotation.z = (*node.rotation)[1]; bone.localRotation.w = (*node.rotation)[3]; } else diff --git a/src/ObjWriting/XModel/Gltf/GltfWriter.cpp b/src/ObjWriting/XModel/Gltf/GltfWriter.cpp index 39e9f0ff..02e96c08 100644 --- a/src/ObjWriting/XModel/Gltf/GltfWriter.cpp +++ b/src/ObjWriting/XModel/Gltf/GltfWriter.cpp @@ -210,46 +210,46 @@ namespace return textureIndex; } - void CreateSkeletonNodes(JsonRoot& gltf, const XModelCommon& xmodel) + void CreateSkeletonNodes(JsonRoot& gltf, const XModelCommon& common) { - if (xmodel.m_bones.empty()) + if (common.m_bones.empty()) return; if (!gltf.nodes.has_value()) gltf.nodes.emplace(); - const auto boneCount = xmodel.m_bones.size(); + const auto boneCount = common.m_bones.size(); m_first_bone_node = gltf.nodes->size(); for (auto boneIndex = 0u; boneIndex < boneCount; boneIndex++) { JsonNode boneNode; - const auto& bone = xmodel.m_bones[boneIndex]; + const auto& bone = common.m_bones[boneIndex]; - Eigen::Vector3f translation(bone.globalOffset[0], bone.globalOffset[2], -bone.globalOffset[1]); - Eigen::Quaternionf rotation(bone.globalRotation.w, bone.globalRotation.x, bone.globalRotation.z, -bone.globalRotation.y); + 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) { - const auto& parentBone = xmodel.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.z, -parentBone.globalRotation.y) + 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[2], -parentBone.globalOffset[1]); + translation -= Eigen::Vector3f(parentBone.globalOffset[0], parentBone.globalOffset[1], parentBone.globalOffset[2]); translation = inverseParentRotation * translation; rotation = inverseParentRotation * rotation; } rotation.normalize(); boneNode.name = bone.name; - boneNode.translation = std::to_array({translation.x(), translation.y(), translation.z()}); - boneNode.rotation = std::to_array({rotation.x(), rotation.y(), rotation.z(), rotation.w()}); + boneNode.translation = std::to_array({translation.x(), translation.z(), -translation.y()}); + boneNode.rotation = std::to_array({rotation.x(), rotation.z(), -rotation.y(), rotation.w()}); std::vector children; for (auto maybeChildIndex = 0u; maybeChildIndex < boneCount; maybeChildIndex++) { - if (xmodel.m_bones[maybeChildIndex].parentIndex && *xmodel.m_bones[maybeChildIndex].parentIndex == boneIndex) + if (common.m_bones[maybeChildIndex].parentIndex && *common.m_bones[maybeChildIndex].parentIndex == boneIndex) children.emplace_back(maybeChildIndex + m_first_bone_node); } if (!children.empty()) From 2699de86b62722a37e28a4fcc38b1ea53105dd88 Mon Sep 17 00:00:00 2001 From: Jan Date: Tue, 3 Sep 2024 22:28:04 +0200 Subject: [PATCH 16/26] chore: use remapped vertex indices when building vertices in gltf loading --- src/ObjLoading/Game/T6/XModel/JsonXModelLoader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ObjLoading/Game/T6/XModel/JsonXModelLoader.cpp b/src/ObjLoading/Game/T6/XModel/JsonXModelLoader.cpp index 01538b9f..87e06983 100644 --- a/src/ObjLoading/Game/T6/XModel/JsonXModelLoader.cpp +++ b/src/ObjLoading/Game/T6/XModel/JsonXModelLoader.cpp @@ -605,7 +605,7 @@ namespace for (auto vertexIndex = 0u; vertexIndex < vertexCount; vertexIndex++) { - const auto& commonVertex = common.m_vertices[vertexIndex]; + const auto& commonVertex = common.m_vertices[vertexIndices[vertexIndex]]; CreateVertex(surface.verts0[vertexIndex], commonVertex); } From 8d2f5541d3adf31cd26fbf343b451d4f751dc82c Mon Sep 17 00:00:00 2001 From: Jan Date: Tue, 3 Sep 2024 22:58:12 +0200 Subject: [PATCH 17/26] chore: make gltf dumping and loading work for models without bone weights --- src/ObjLoading/Game/T6/XModel/JsonXModelLoader.cpp | 6 +++--- src/ObjWriting/XModel/Gltf/GltfWriter.cpp | 14 ++++++++------ 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/ObjLoading/Game/T6/XModel/JsonXModelLoader.cpp b/src/ObjLoading/Game/T6/XModel/JsonXModelLoader.cpp index 87e06983..8fe97020 100644 --- a/src/ObjLoading/Game/T6/XModel/JsonXModelLoader.cpp +++ b/src/ObjLoading/Game/T6/XModel/JsonXModelLoader.cpp @@ -233,7 +233,7 @@ namespace static void CalculateBoneBounds(XBoneInfo& info, const unsigned boneIndex, const XModelCommon& common) { - if (common.m_vertex_bone_weights.empty()) + if (common.m_bone_weight_data.weights.empty()) return; info.bounds[0].x = 0.0f; @@ -524,7 +524,7 @@ namespace static void ReorderVerticesByWeightCount(std::vector& vertexIndices, XSurface& surface, const XModelCommon& common) { - if (common.m_vertex_bone_weights.empty()) + if (common.m_bone_weight_data.weights.empty()) return; const auto vertexCount = vertexIndices.size(); @@ -609,7 +609,7 @@ namespace CreateVertex(surface.verts0[vertexIndex], commonVertex); } - if (!common.m_vertex_bone_weights.empty()) + if (!common.m_bone_weight_data.weights.empty()) { // Since bone weights are sorted by weight count, the last must have the highest weight count const auto hasVertsBlend = common.m_vertex_bone_weights[vertexIndices[vertexIndices.size() - 1]].weightCount > 1; diff --git a/src/ObjWriting/XModel/Gltf/GltfWriter.cpp b/src/ObjWriting/XModel/Gltf/GltfWriter.cpp index 02e96c08..05512e73 100644 --- a/src/ObjWriting/XModel/Gltf/GltfWriter.cpp +++ b/src/ObjWriting/XModel/Gltf/GltfWriter.cpp @@ -119,7 +119,7 @@ namespace JsonMesh mesh; - const auto hasBoneWeightData = !xmodel.m_vertex_bone_weights.empty(); + const auto hasBoneWeightData = !xmodel.m_bone_weight_data.weights.empty(); auto objectIndex = 0u; for (const auto& object : xmodel.m_objects) @@ -275,7 +275,9 @@ namespace for (auto boneIndex = 0u; boneIndex < boneCount; boneIndex++) skin.joints.emplace_back(boneIndex + m_first_bone_node); - skin.inverseBindMatrices = m_inverse_bind_matrices_accessor; + if (!xmodel.m_bone_weight_data.weights.empty()) + skin.inverseBindMatrices = m_inverse_bind_matrices_accessor; + skin.skeleton = m_first_bone_node; gltf.skins->emplace_back(std::move(skin)); @@ -313,7 +315,7 @@ namespace m_vertex_buffer_view = gltf.bufferViews->size(); gltf.bufferViews->emplace_back(vertexBufferView); - if (!xmodel.m_vertex_bone_weights.empty()) + if (!xmodel.m_bone_weight_data.weights.empty()) { JsonBufferView jointsBufferView; jointsBufferView.buffer = 0u; @@ -391,7 +393,7 @@ namespace m_uv_accessor = gltf.accessors->size(); gltf.accessors->emplace_back(uvAccessor); - if (!xmodel.m_vertex_bone_weights.empty()) + if (!xmodel.m_bone_weight_data.weights.empty()) { JsonAccessor jointsAccessor; jointsAccessor.bufferView = m_joints_buffer_view; @@ -488,7 +490,7 @@ namespace gltf.accessors.value()[m_position_accessor].max = std::vector({maxPosition[0], maxPosition[1], maxPosition[2]}); } - if (!xmodel.m_vertex_bone_weights.empty()) + if (!xmodel.m_bone_weight_data.weights.empty()) { assert(xmodel.m_vertex_bone_weights.size() == xmodel.m_vertices.size()); @@ -572,7 +574,7 @@ namespace result += xmodel.m_vertices.size() * sizeof(GltfVertex); - if (!xmodel.m_vertex_bone_weights.empty()) + if (!xmodel.m_bone_weight_data.weights.empty()) { // Joints and weights result += xmodel.m_vertices.size() * 4u * (sizeof(uint8_t) + sizeof(float)); From 1be630f71eefd689bc2c049ca20a3b5f9e07b9de Mon Sep 17 00:00:00 2001 From: Jan Date: Wed, 4 Sep 2024 00:05:57 +0200 Subject: [PATCH 18/26] chore: set vertex base index for all surfaces --- src/Common/Game/T6/T6_Assets.h | 2 +- .../Game/T6/XModel/JsonXModelLoader.cpp | 88 ++++++++++--------- 2 files changed, 49 insertions(+), 41 deletions(-) diff --git a/src/Common/Game/T6/T6_Assets.h b/src/Common/Game/T6/T6_Assets.h index edb7c063..624217dc 100644 --- a/src/Common/Game/T6/T6_Assets.h +++ b/src/Common/Game/T6/T6_Assets.h @@ -590,7 +590,7 @@ namespace T6 float dist; uint16_t numsurfs; uint16_t surfIndex; - int partBits[5]; + unsigned int partBits[5]; }; enum XModelLodRampType : unsigned char diff --git a/src/ObjLoading/Game/T6/XModel/JsonXModelLoader.cpp b/src/ObjLoading/Game/T6/XModel/JsonXModelLoader.cpp index 8fe97020..fc0813ea 100644 --- a/src/ObjLoading/Game/T6/XModel/JsonXModelLoader.cpp +++ b/src/ObjLoading/Game/T6/XModel/JsonXModelLoader.cpp @@ -566,65 +566,58 @@ namespace vertexIndices = std::move(reorderLookup); } - bool CreateXSurface(XSurface& surface, const XModelObject& commonObject, const XModelCommon& common) + bool CreateXSurface(XSurface& surface, const XModelObject& commonObject, const XModelCommon& common, unsigned& vertexOffset) { - std::vector vertexIndices; + std::vector xmodelToCommonVertexIndexLookup; std::unordered_map usedVertices; surface.triCount = static_cast(commonObject.m_faces.size()); - surface.triIndices = m_memory.Alloc(commonObject.m_faces.size()); + surface.triIndices = m_memory.Alloc(surface.triCount); - const auto faceCount = commonObject.m_faces.size(); - for (auto faceIndex = 0u; faceIndex < faceCount; faceIndex++) + for (auto faceIndex = 0u; faceIndex < surface.triCount; faceIndex++) { const auto& face = commonObject.m_faces[faceIndex]; auto& tris = surface.triIndices[faceIndex]; - for (auto faceVertexIndex = 0u; faceVertexIndex < std::extent_v; faceVertexIndex++) + for (auto triVertIndex = 0u; triVertIndex < std::extent_v; triVertIndex++) { - const auto vertexIndex = face.vertexIndex[faceVertexIndex]; - const auto existingVertex = usedVertices.find(vertexIndex); + const auto commonVertexIndex = face.vertexIndex[triVertIndex]; + const auto existingVertex = usedVertices.find(commonVertexIndex); if (existingVertex == usedVertices.end()) { - const auto newVertexIndex = vertexIndices.size(); - tris.i[faceVertexIndex] = static_cast(newVertexIndex); + const auto xmodelVertexIndex = xmodelToCommonVertexIndexLookup.size(); + tris.i[triVertIndex] = static_cast(xmodelVertexIndex); - vertexIndices.emplace_back(vertexIndex); - usedVertices.emplace(vertexIndex, newVertexIndex); + xmodelToCommonVertexIndexLookup.emplace_back(commonVertexIndex); + usedVertices.emplace(commonVertexIndex, xmodelVertexIndex); } else - tris.i[faceVertexIndex] = static_cast(existingVertex->second); + tris.i[triVertIndex] = static_cast(existingVertex->second); } } - ReorderVerticesByWeightCount(vertexIndices, surface, common); + ReorderVerticesByWeightCount(xmodelToCommonVertexIndexLookup, surface, common); - const auto vertexCount = vertexIndices.size(); - surface.vertCount = static_cast(vertexCount); - surface.verts0 = m_memory.Alloc(vertexCount); + surface.baseVertIndex = static_cast(vertexOffset); + surface.vertCount = static_cast(xmodelToCommonVertexIndexLookup.size()); + surface.verts0 = m_memory.Alloc(surface.vertCount); + vertexOffset += surface.vertCount; - for (auto vertexIndex = 0u; vertexIndex < vertexCount; vertexIndex++) + for (auto vertexIndex = 0u; vertexIndex < surface.vertCount; vertexIndex++) { - const auto& commonVertex = common.m_vertices[vertexIndices[vertexIndex]]; + const auto& commonVertex = common.m_vertices[xmodelToCommonVertexIndexLookup[vertexIndex]]; CreateVertex(surface.verts0[vertexIndex], commonVertex); } if (!common.m_bone_weight_data.weights.empty()) { // Since bone weights are sorted by weight count, the last must have the highest weight count - const auto hasVertsBlend = common.m_vertex_bone_weights[vertexIndices[vertexIndices.size() - 1]].weightCount > 1; + const auto hasVertsBlend = + common.m_vertex_bone_weights[xmodelToCommonVertexIndexLookup[xmodelToCommonVertexIndexLookup.size() - 1]].weightCount > 1; if (!hasVertsBlend) - CreateVertListData(surface, vertexIndices, common); + CreateVertListData(surface, xmodelToCommonVertexIndexLookup, common); else - CreateVertsBlendData(surface, vertexIndices, common); - } - - const auto boneCount = common.m_bones.size(); - for (auto boneIndex = 0u; boneIndex < boneCount; boneIndex++) - { - const auto partBitsIndex = boneIndex / 32u; - const auto shiftValue = 31u - (boneIndex % 32u); - surface.partBits[partBitsIndex] = 1 << (31u - shiftValue); + CreateVertsBlendData(surface, xmodelToCommonVertexIndexLookup, common); } return true; @@ -677,17 +670,32 @@ namespace materialAssets.push_back(assetInfo->Asset()); } - return std::ranges::all_of(common->m_objects, - [this, &common, &materialAssets](const XModelObject& commonObject) - { - XSurface surface{}; - if (!CreateXSurface(surface, commonObject, *common)) - return false; + auto vertexOffset = 0u; + const auto surfaceCreationSuccessful = std::ranges::all_of(common->m_objects, + [this, &common, &materialAssets, &vertexOffset](const XModelObject& commonObject) + { + XSurface surface{}; + if (!CreateXSurface(surface, commonObject, *common, vertexOffset)) + return false; - m_surfaces.emplace_back(surface); - m_materials.push_back(materialAssets[commonObject.materialIndex]); - return true; - }); + m_surfaces.emplace_back(surface); + m_materials.push_back(materialAssets[commonObject.materialIndex]); + return true; + }); + + if (!surfaceCreationSuccessful) + return false; + + // Lod part bits are the sum of part bits of all of its surfaces + static_assert(std::extent_v == std::extent_v); + for (auto surfaceOffset = 0u; surfaceOffset < lodInfo.numsurfs; surfaceOffset++) + { + const auto& surface = m_surfaces[lodInfo.surfIndex + surfaceOffset]; + for (auto i = 0u; i < std::extent_v; i++) + lodInfo.partBits[i] |= surface.partBits[i]; + } + + return true; } static void CalculateModelBounds(XModel& xmodel) From 2687fbf9fdbdf1f3bc480052be625f46070d9963 Mon Sep 17 00:00:00 2001 From: Jan Date: Wed, 4 Sep 2024 00:43:21 +0200 Subject: [PATCH 19/26] chore: properly calculate partbits for loaded gltf models --- src/ObjLoading/Game/T6/XModel/JsonXModelLoader.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/ObjLoading/Game/T6/XModel/JsonXModelLoader.cpp b/src/ObjLoading/Game/T6/XModel/JsonXModelLoader.cpp index fc0813ea..503e5621 100644 --- a/src/ObjLoading/Game/T6/XModel/JsonXModelLoader.cpp +++ b/src/ObjLoading/Game/T6/XModel/JsonXModelLoader.cpp @@ -467,6 +467,13 @@ namespace memcpy(surface.triIndices, sortedTris.data(), sizeof(std::remove_pointer_t) * surface.triCount); } + static void AddBoneToXSurfacePartBits(XSurface& surface, const size_t boneIndex) + { + const auto partBitsIndex = boneIndex / 32u; + const auto shiftValue = 31u - (boneIndex % 32u); + surface.partBits[partBitsIndex] |= 1 << shiftValue; + } + void CreateVertListData(XSurface& surface, const std::vector& vertexIndices, const XModelCommon& common) { ReorderRigidTrisByBoneIndex(vertexIndices, surface, common); @@ -504,6 +511,8 @@ namespace currentVertexTail = currentVertexHead; currentTriTail = currentTriHead; + + AddBoneToXSurfacePartBits(surface, boneIndex); } } From c261aef1ef73529e2380cbbe30c8f1dc669a8b6c Mon Sep 17 00:00:00 2001 From: Jan Date: Wed, 4 Sep 2024 22:07:57 +0200 Subject: [PATCH 20/26] chore: fix xmodel root struct bone trans and quats --- src/Common/Game/T5/T5_Assets.h | 2 +- src/Common/Game/T6/T6_Assets.h | 2 +- src/ObjLoading/Game/T6/XModel/JsonXModelLoader.cpp | 12 +++++++----- .../Game/T5/AssetDumpers/AssetDumperXModel.cpp | 8 ++++---- .../Game/T6/AssetDumpers/AssetDumperXModel.cpp | 8 ++++---- src/ZoneCode/Game/T5/XAssets/XModel.txt | 3 ++- src/ZoneCode/Game/T6/XAssets/XModel.txt | 3 ++- 7 files changed, 21 insertions(+), 17 deletions(-) diff --git a/src/Common/Game/T5/T5_Assets.h b/src/Common/Game/T5/T5_Assets.h index e2ef4741..4c6de811 100644 --- a/src/Common/Game/T5/T5_Assets.h +++ b/src/Common/Game/T5/T5_Assets.h @@ -672,7 +672,7 @@ namespace T5 uint16_t* boneNames; char* parentList; XModelQuat* quats; - vec4_t* trans; + float* trans; char* partClassification; DObjAnimMat* baseMat; XSurface* surfs; diff --git a/src/Common/Game/T6/T6_Assets.h b/src/Common/Game/T6/T6_Assets.h index 624217dc..99cea761 100644 --- a/src/Common/Game/T6/T6_Assets.h +++ b/src/Common/Game/T6/T6_Assets.h @@ -616,7 +616,7 @@ namespace T6 ScriptString* boneNames; unsigned char* parentList; XModelQuat* quats; - vec4_t* trans; + float* trans; unsigned char* partClassification; DObjAnimMat* baseMat; XSurface* surfs; diff --git a/src/ObjLoading/Game/T6/XModel/JsonXModelLoader.cpp b/src/ObjLoading/Game/T6/XModel/JsonXModelLoader.cpp index 503e5621..9415787f 100644 --- a/src/ObjLoading/Game/T6/XModel/JsonXModelLoader.cpp +++ b/src/ObjLoading/Game/T6/XModel/JsonXModelLoader.cpp @@ -314,7 +314,9 @@ namespace if (xmodel.numBones > xmodel.numRootBones) { xmodel.parentList = m_memory.Alloc(xmodel.numBones - xmodel.numRootBones); - xmodel.trans = m_memory.Alloc(xmodel.numBones - xmodel.numRootBones); + + // For some reason Treyarch games allocate for a vec4 here. it is treated as a vec3 though? + xmodel.trans = m_memory.Alloc((xmodel.numBones - xmodel.numRootBones) * 4u); xmodel.quats = m_memory.Alloc(xmodel.numBones - xmodel.numRootBones); } else @@ -344,10 +346,10 @@ namespace xmodel.parentList[nonRootIndex] = static_cast(boneIndex - parentBoneIndex); - auto& trans = xmodel.trans[nonRootIndex]; - trans.x = bone.localOffset[0]; - trans.y = bone.localOffset[1]; - trans.z = bone.localOffset[2]; + auto* trans = &xmodel.trans[nonRootIndex * 3]; + trans[0] = bone.localOffset[0]; + trans[1] = bone.localOffset[1]; + trans[2] = bone.localOffset[2]; auto& quats = xmodel.quats[nonRootIndex]; quats.v[0] = QuatInt16::ToInt16(bone.localRotation.x); diff --git a/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperXModel.cpp b/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperXModel.cpp index b9102858..b7d80214 100644 --- a/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperXModel.cpp +++ b/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperXModel.cpp @@ -139,10 +139,10 @@ namespace } else { - const auto& trans = model->trans[boneNum - model->numRootBones]; - bone.localOffset[0] = trans.x; - bone.localOffset[1] = trans.y; - bone.localOffset[2] = trans.z; + const auto* trans = &model->trans[(boneNum - model->numRootBones) * 3]; + bone.localOffset[0] = trans[0]; + bone.localOffset[1] = trans[1]; + bone.localOffset[2] = trans[2]; const auto& quat = model->quats[boneNum - model->numRootBones]; bone.localRotation = { diff --git a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperXModel.cpp b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperXModel.cpp index 7c0f0df1..9a030b9b 100644 --- a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperXModel.cpp +++ b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperXModel.cpp @@ -153,10 +153,10 @@ namespace } else { - const auto& trans = model->trans[boneNum - model->numRootBones]; - bone.localOffset[0] = trans.x; - bone.localOffset[1] = trans.y; - bone.localOffset[2] = trans.z; + const auto* trans = &model->trans[(boneNum - model->numRootBones) * 3]; + bone.localOffset[0] = trans[0]; + bone.localOffset[1] = trans[1]; + bone.localOffset[2] = trans[2]; const auto& quat = model->quats[boneNum - model->numRootBones]; bone.localRotation = { diff --git a/src/ZoneCode/Game/T5/XAssets/XModel.txt b/src/ZoneCode/Game/T5/XAssets/XModel.txt index 4541f1f7..b555fad6 100644 --- a/src/ZoneCode/Game/T5/XAssets/XModel.txt +++ b/src/ZoneCode/Game/T5/XAssets/XModel.txt @@ -13,7 +13,8 @@ set count parentList numBones - numRootBones; set reusable quats; set count quats numBones - numRootBones; set reusable trans; -set count trans numBones - numRootBones; +// This is actually the count but it looks like a bug? It is used like a vec3, but it takes as much memory as vec4 +set count trans (numBones - numRootBones) * 4; set reusable partClassification; set count partClassification numBones; set reusable baseMat; diff --git a/src/ZoneCode/Game/T6/XAssets/XModel.txt b/src/ZoneCode/Game/T6/XAssets/XModel.txt index 13bf0712..62cd446a 100644 --- a/src/ZoneCode/Game/T6/XAssets/XModel.txt +++ b/src/ZoneCode/Game/T6/XAssets/XModel.txt @@ -13,7 +13,8 @@ set count parentList numBones - numRootBones; set reusable quats; set count quats numBones - numRootBones; set reusable trans; -set count trans numBones - numRootBones; +// This is actually the count but it looks like a bug? It is used like a vec3, but it takes as much memory as vec4 +set count trans (numBones - numRootBones) * 4; set reusable partClassification; set count partClassification numBones; set reusable baseMat; From 26f46401945ca83705f88c022963e9e4cb7dc325 Mon Sep 17 00:00:00 2001 From: Jan Date: Thu, 5 Sep 2024 19:09:23 +0200 Subject: [PATCH 21/26] chore: properly read uv data from gltf --- src/Common/Utils/HalfFloat.cpp | 25 +++++++++++++++++++++-- src/ObjLoading/XModel/Gltf/GltfLoader.cpp | 2 +- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/src/Common/Utils/HalfFloat.cpp b/src/Common/Utils/HalfFloat.cpp index 663fefa1..58a4bfe2 100644 --- a/src/Common/Utils/HalfFloat.cpp +++ b/src/Common/Utils/HalfFloat.cpp @@ -17,7 +17,28 @@ float HalfFloat::ToFloat(const half_float_t half) return 0.0f; } -half_float_t HalfFloat::ToHalf(float f) +half_float_t HalfFloat::ToHalf(const float f) { - return 0; + half_float_t v3; + int v6; + + union + { + uint32_t u; + float f; + } result{}; + + result.f = f; + + if (static_cast((2 * result.u) ^ 0x80000000) >> 14 < 0x3FFF) + v6 = static_cast((2 * result.u) ^ 0x80000000) >> 14; + else + v6 = 0x3FFF; + + if (v6 > -16384) + v3 = static_cast(v6); + else + v3 = 0xC000; + + return (v3 & 0x3FFFu) | ((result.u >> 16) & 0xC000u); } diff --git a/src/ObjLoading/XModel/Gltf/GltfLoader.cpp b/src/ObjLoading/XModel/Gltf/GltfLoader.cpp index 5ef4508e..ef5face7 100644 --- a/src/ObjLoading/XModel/Gltf/GltfLoader.cpp +++ b/src/ObjLoading/XModel/Gltf/GltfLoader.cpp @@ -270,7 +270,7 @@ namespace float coordinates[3]; float normal[3]; if (!positionAccessor->GetFloatVec3(vertexIndex, coordinates) || !normalAccessor->GetFloatVec3(vertexIndex, normal) - || !colorAccessor->GetFloatVec4(vertexIndex, vertex.color) || !colorAccessor->GetFloatVec2(vertexIndex, vertex.uv) + || !colorAccessor->GetFloatVec4(vertexIndex, vertex.color) || !uvAccessor->GetFloatVec2(vertexIndex, vertex.uv) || !jointsAccessor->GetUnsignedVec4(vertexIndex, joints) || !weightsAccessor->GetFloatVec4(vertexIndex, weights)) { return false; From db5e53e60d880e511b592b392b6954947a16d37b Mon Sep 17 00:00:00 2001 From: Jan Date: Thu, 5 Sep 2024 19:11:08 +0200 Subject: [PATCH 22/26] chore: calculate tangents for xmodel vertices --- .../Game/T6/XModel/JsonXModelLoader.cpp | 93 +++++-- src/ObjLoading/XModel/Tangentspace.cpp | 231 ++++++++++++++++++ src/ObjLoading/XModel/Tangentspace.h | 22 ++ 3 files changed, 329 insertions(+), 17 deletions(-) create mode 100644 src/ObjLoading/XModel/Tangentspace.cpp create mode 100644 src/ObjLoading/XModel/Tangentspace.h diff --git a/src/ObjLoading/Game/T6/XModel/JsonXModelLoader.cpp b/src/ObjLoading/Game/T6/XModel/JsonXModelLoader.cpp index 9415787f..e015b5f6 100644 --- a/src/ObjLoading/Game/T6/XModel/JsonXModelLoader.cpp +++ b/src/ObjLoading/Game/T6/XModel/JsonXModelLoader.cpp @@ -15,6 +15,8 @@ #include #pragma warning(pop) +#include "XModel/Tangentspace.h" + #include #include #include @@ -136,6 +138,57 @@ namespace std::unordered_map m_part_classifications; }; + class TangentData + { + public: + void CreateTangentData(const XModelCommon& common) + { + if (common.m_vertices.empty()) + return; + + const auto vertexCount = common.m_vertices.size(); + m_tangents.resize(vertexCount); + m_binormals.resize(vertexCount); + + auto triCount = 0u; + for (const auto& object : common.m_objects) + triCount += object.m_faces.size(); + + std::vector indices(triCount * 3u); + auto triOffset = 0u; + for (const auto& object : common.m_objects) + { + for (const auto& face : object.m_faces) + { + indices[triOffset++] = static_cast(face.vertexIndex[0]); + indices[triOffset++] = static_cast(face.vertexIndex[1]); + indices[triOffset++] = static_cast(face.vertexIndex[2]); + } + } + + const auto& firstVertex = common.m_vertices[0]; + + tangent_space::VertexData vertexData{ + firstVertex.coordinates, + sizeof(XModelVertex), + firstVertex.normal, + sizeof(XModelVertex), + firstVertex.uv, + sizeof(XModelVertex), + m_tangents.data(), + sizeof(float) * 3, + m_binormals.data(), + sizeof(float) * 3, + indices.data(), + }; + + tangent_space::CalculateTangentSpace(vertexData, triCount, vertexCount); + } + + std::vector> m_tangents; + std::vector> m_binormals; + }; + class JsonLoader { public: @@ -401,18 +454,19 @@ namespace return true; } - static void CreateVertex(GfxPackedVertex& vertex, const XModelVertex& commonVertex) + static void + CreateVertex(GfxPackedVertex& vertex, const XModelVertex& commonVertex, const std::array& tangent, const std::array& binormal) { - constexpr float wrongTangent[]{1, 0, 0}; + float tangent_[]{tangent[0], tangent[1], tangent[2]}; vertex.xyz.x = commonVertex.coordinates[0]; vertex.xyz.y = commonVertex.coordinates[1]; vertex.xyz.z = commonVertex.coordinates[2]; - vertex.binormalSign = 1.0f; // TODO: Fill with actual value + vertex.binormalSign = binormal[0] > 0.0f ? 1.0f : -1.0f; vertex.color = Common::Vec4PackGfxColor(commonVertex.color); vertex.texCoord = Common::Vec2PackTexCoords(commonVertex.uv); vertex.normal = Common::Vec3PackUnitVec(commonVertex.normal); - vertex.tangent = Common::Vec3PackUnitVec(wrongTangent); // TODO: Fill with actual value + vertex.tangent = Common::Vec3PackUnitVec(tangent_); } static size_t GetRigidBoneForVertex(const size_t vertexIndex, const XModelCommon& common) @@ -577,7 +631,8 @@ namespace vertexIndices = std::move(reorderLookup); } - bool CreateXSurface(XSurface& surface, const XModelObject& commonObject, const XModelCommon& common, unsigned& vertexOffset) + bool CreateXSurface( + XSurface& surface, const XModelObject& commonObject, const XModelCommon& common, const TangentData& tangentData, unsigned& vertexOffset) { std::vector xmodelToCommonVertexIndexLookup; std::unordered_map usedVertices; @@ -616,8 +671,9 @@ namespace for (auto vertexIndex = 0u; vertexIndex < surface.vertCount; vertexIndex++) { - const auto& commonVertex = common.m_vertices[xmodelToCommonVertexIndexLookup[vertexIndex]]; - CreateVertex(surface.verts0[vertexIndex], commonVertex); + const auto commonVertexIndex = xmodelToCommonVertexIndexLookup[vertexIndex]; + const auto& commonVertex = common.m_vertices[commonVertexIndex]; + CreateVertex(surface.verts0[vertexIndex], commonVertex, tangentData.m_binormals[commonVertexIndex], tangentData.m_binormals[commonVertexIndex]); } if (!common.m_bone_weight_data.weights.empty()) @@ -682,17 +738,20 @@ namespace } auto vertexOffset = 0u; - const auto surfaceCreationSuccessful = std::ranges::all_of(common->m_objects, - [this, &common, &materialAssets, &vertexOffset](const XModelObject& commonObject) - { - XSurface surface{}; - if (!CreateXSurface(surface, commonObject, *common, vertexOffset)) - return false; + TangentData tangentData; + tangentData.CreateTangentData(*common); + const auto surfaceCreationSuccessful = + std::ranges::all_of(common->m_objects, + [this, &common, &materialAssets, &tangentData, &vertexOffset](const XModelObject& commonObject) + { + XSurface surface{}; + if (!CreateXSurface(surface, commonObject, *common, tangentData, vertexOffset)) + return false; - m_surfaces.emplace_back(surface); - m_materials.push_back(materialAssets[commonObject.materialIndex]); - return true; - }); + m_surfaces.emplace_back(surface); + m_materials.push_back(materialAssets[commonObject.materialIndex]); + return true; + }); if (!surfaceCreationSuccessful) return false; diff --git a/src/ObjLoading/XModel/Tangentspace.cpp b/src/ObjLoading/XModel/Tangentspace.cpp new file mode 100644 index 00000000..32662f64 --- /dev/null +++ b/src/ObjLoading/XModel/Tangentspace.cpp @@ -0,0 +1,231 @@ +#include "Tangentspace.h" + +#include +#include +#include + +namespace tangent_space +{ + constexpr float NULL_VEC3[3]{0, 0, 0}; + + typedef float tvec2[2]; + typedef float tvec3[3]; + + const tvec2& GetVec2(const void* dest, const size_t index, const size_t stride) + { + return *reinterpret_cast(static_cast(dest) + stride * index); + } + + const tvec3& GetVec3(const void* dest, const size_t index, const size_t stride) + { + return *reinterpret_cast(static_cast(dest) + stride * index); + } + + tvec3& GetVec3(void* dest, const size_t index, const size_t stride) + { + return *reinterpret_cast(static_cast(dest) + stride * index); + } + + void SetVec3(void* dest, const size_t index, const size_t stride, const tvec3& data) + { + auto* out = reinterpret_cast(static_cast(dest) + stride * index); + (*out)[0] = data[0]; + (*out)[1] = data[1]; + (*out)[2] = data[2]; + } + + bool Vec3_IsNormalized(const tvec3& a1) + { + const auto len = a1[0] * a1[0] + a1[1] * a1[1] + a1[2] * a1[2]; + const auto error = fabs(len - 1.0f); + return error < 0.0020000001; + } + + float Vec3_Normalize(tvec3& vector) + { + float length = std::sqrt(vector[0] * vector[0] + vector[1] * vector[1] + vector[2] * vector[2]); + if (-length >= 0.0f) + length = 1.0f; + const auto lengthInv = 1.0f / length; + vector[0] = lengthInv * vector[0]; + vector[1] = lengthInv * vector[1]; + vector[2] = lengthInv * vector[2]; + return length; + } + + void Vec3_Cross(const tvec3& v0, const tvec3& v1, tvec3& cross) + { + cross[0] = v0[1] * v1[2] - v0[2] * v1[1]; + cross[1] = v0[2] * v1[0] - v0[0] * v1[2]; + cross[2] = v0[0] * v1[1] - v1[0] * v0[1]; + } + + float AngleBetweenOriginVectors(const tvec3& a1, const tvec3& a2) + { + const auto v4 = a1[0] * a2[0] + a1[1] * a2[1] + a1[2] * a2[2]; + if (v4 <= -1.0) + return -std::numbers::pi_v; + if (v4 >= 1.0) + return std::numbers::pi_v; + + return acos(v4); + } + + void sub_10022E80(const VertexData& vertexData, const uint16_t i0, const uint16_t i1, const uint16_t i2, tvec3& outVector, tvec3& outCross) + { + const auto& i0_uv = GetVec2(vertexData.uvData, i0, vertexData.uvDataStride); + const auto& i1_uv = GetVec2(vertexData.uvData, i1, vertexData.uvDataStride); + const auto& i2_uv = GetVec2(vertexData.uvData, i2, vertexData.uvDataStride); + const auto& i0_position = GetVec3(vertexData.positionData, i0, vertexData.positionDataStride); + const auto& i1_position = GetVec3(vertexData.positionData, i1, vertexData.positionDataStride); + const auto& i2_position = GetVec3(vertexData.positionData, i2, vertexData.positionDataStride); + + const auto uv0_1m0 = i1_uv[0] - i0_uv[0]; + const auto uv0_2m0 = i2_uv[0] - i0_uv[0]; + const auto uv1_1m0 = i1_uv[1] - i0_uv[1]; + const auto uv1_2m0 = i2_uv[1] - i0_uv[1]; + + const auto p0_1m0 = i1_position[0] - i0_position[0]; + const auto p0_2m0 = i2_position[0] - i0_position[0]; + const auto p1_1m0 = i1_position[1] - i0_position[1]; + const auto p1_2m0 = i2_position[1] - i0_position[1]; + const auto p2_1m0 = i1_position[2] - i0_position[2]; + const auto p2_2m0 = i2_position[2] - i0_position[2]; + + if (uv1_2m0 * uv0_1m0 >= uv1_1m0 * uv0_2m0) + { + outVector[0] = p0_1m0 * uv1_2m0 - p0_2m0 * uv1_1m0; + outCross[0] = p0_2m0 * uv0_1m0 - p0_1m0 * uv0_2m0; + outVector[1] = p1_1m0 * uv1_2m0 - p1_2m0 * uv1_1m0; + outCross[1] = p1_2m0 * uv0_1m0 - p1_1m0 * uv0_2m0; + outVector[2] = uv1_2m0 * p2_1m0 - uv1_1m0 * p2_2m0; + outCross[2] = uv0_1m0 * p2_2m0 - uv0_2m0 * p2_1m0; + } + else + { + outVector[0] = p0_2m0 * uv1_1m0 - p0_1m0 * uv1_2m0; + outCross[0] = p0_1m0 * uv0_2m0 - p0_2m0 * uv0_1m0; + outVector[1] = p1_2m0 * uv1_1m0 - p1_1m0 * uv1_2m0; + outCross[1] = p1_1m0 * uv0_2m0 - p1_2m0 * uv0_1m0; + outVector[2] = uv1_1m0 * p2_2m0 - uv1_2m0 * p2_1m0; + outCross[2] = uv0_2m0 * p2_1m0 - p2_2m0 * uv0_1m0; + } + + Vec3_Normalize(outVector); + Vec3_Normalize(outCross); + } + + void GetExteriorAnglesOfTri(const VertexData& vertexData, tvec3& outExteriorAngles, const uint16_t i0, const uint16_t i1, const uint16_t i2) + { + tvec3 L21; + tvec3 L02; + tvec3 L10; + + const auto* index0Position = GetVec3(vertexData.positionData, i0, vertexData.positionDataStride); + const auto* index1Position = GetVec3(vertexData.positionData, i1, vertexData.positionDataStride); + const auto* index2Position = GetVec3(vertexData.positionData, i2, vertexData.positionDataStride); + L10[0] = index0Position[0] - index1Position[0]; + L10[1] = index0Position[1] - index1Position[1]; + L10[2] = index0Position[2] - index1Position[2]; + L21[0] = index1Position[0] - index2Position[0]; + L21[1] = index1Position[1] - index2Position[1]; + L21[2] = index1Position[2] - index2Position[2]; + L02[0] = index2Position[0] - index0Position[0]; + L02[1] = index2Position[1] - index0Position[1]; + L02[2] = index2Position[2] - index0Position[2]; + Vec3_Normalize(L10); + Vec3_Normalize(L21); + Vec3_Normalize(L02); + outExteriorAngles[0] = AngleBetweenOriginVectors(L10, L02); + outExteriorAngles[1] = AngleBetweenOriginVectors(L21, L10); + outExteriorAngles[2] = AngleBetweenOriginVectors(L02, L21); + } + + void sub_10022CA0(const uint16_t index, void* sourcesDest, const tvec3& ecx0, const float exteriorAngle, const size_t stride) + { + auto& vec = GetVec3(sourcesDest, index, stride); + vec[0] = ecx0[0] * exteriorAngle + vec[0]; + vec[1] = ecx0[1] * exteriorAngle + vec[1]; + vec[2] = ecx0[2] * exteriorAngle + vec[2]; + } + + void sub_10014EE0(const tvec3& src, tvec3& a2) + { + assert(Vec3_IsNormalized(src)); + + const auto v4 = src[0] * src[0]; + float v5 = src[1] * src[1]; + float v6 = src[2] * src[2]; + int v3 = v4 > v5; + if (*(&v4 + v3) > v6) + v3 = 2; + const auto srcc = -src[v3]; + a2[0] = src[0] * srcc; + a2[1] = src[1] * srcc; + a2[2] = src[2] * srcc; + a2[v3] = a2[v3] + 1.0f; + Vec3_Normalize(a2); + } + + void CalculateTangentSpace(const VertexData& vertexData, const size_t triCount, const size_t vertexCount) + { + // Set tangents and binormals to 0 + for (auto vertexIndex = 0u; vertexIndex < vertexCount; vertexIndex++) + { + SetVec3(vertexData.tangentData, vertexIndex, vertexData.tangentDataStride, NULL_VEC3); + SetVec3(vertexData.binormalData, vertexIndex, vertexData.binormalDataStride, NULL_VEC3); + } + + for (auto triIndex = 0u; triIndex < triCount; triIndex++) + { + const auto i0 = vertexData.triData[triIndex * 3u + 0u]; + const auto i1 = vertexData.triData[triIndex * 3u + 1u]; + const auto i2 = vertexData.triData[triIndex * 3u + 2u]; + + tvec3 vector, cross, exteriorAngles; + sub_10022E80(vertexData, i0, i1, i2, vector, cross); + GetExteriorAnglesOfTri(vertexData, exteriorAngles, i0, i1, i2); + sub_10022CA0(i0, vertexData.tangentData, vector, exteriorAngles[0], vertexData.tangentDataStride); + sub_10022CA0(i1, vertexData.tangentData, vector, exteriorAngles[1], vertexData.tangentDataStride); + sub_10022CA0(i2, vertexData.tangentData, vector, exteriorAngles[2], vertexData.tangentDataStride); + sub_10022CA0(i0, vertexData.binormalData, cross, exteriorAngles[0], vertexData.binormalDataStride); + sub_10022CA0(i1, vertexData.binormalData, cross, exteriorAngles[1], vertexData.binormalDataStride); + sub_10022CA0(i2, vertexData.binormalData, cross, exteriorAngles[2], vertexData.binormalDataStride); + } + + for (auto vertexIndex = 0u; vertexIndex < vertexCount; vertexIndex++) + { + const auto& normal = GetVec3(vertexData.normalData, vertexIndex, vertexData.normalDataStride); + auto& tangent = GetVec3(vertexData.tangentData, vertexIndex, vertexData.tangentDataStride); + auto& binormal = GetVec3(vertexData.binormalData, vertexIndex, vertexData.binormalDataStride); + + const auto dot_normal_tangent = normal[0] * tangent[0] + normal[1] * tangent[1] + normal[2] * tangent[2]; + + tangent[0] = normal[0] * -dot_normal_tangent + tangent[0]; + tangent[1] = normal[1] * -dot_normal_tangent + tangent[1]; + tangent[2] = normal[2] * -dot_normal_tangent + tangent[2]; + if (Vec3_Normalize(tangent) < 0.001f) + { + Vec3_Cross(binormal, normal, tangent); + if (Vec3_Normalize(tangent) < 0.001) + sub_10014EE0(normal, tangent); + } + + tvec3 cross; + Vec3_Cross(normal, tangent, cross); + const auto sourcesc = binormal[0] * cross[0] + cross[1] * binormal[1] + cross[2] * binormal[2]; + if (sourcesc >= 0.0) + { + binormal[0] = cross[0]; + binormal[1] = cross[1]; + binormal[2] = cross[2]; + } + else + { + binormal[0] = -cross[0]; + binormal[1] = -cross[1]; + binormal[2] = -cross[2]; + } + } + } +} // namespace tangent_space diff --git a/src/ObjLoading/XModel/Tangentspace.h b/src/ObjLoading/XModel/Tangentspace.h new file mode 100644 index 00000000..b4645c00 --- /dev/null +++ b/src/ObjLoading/XModel/Tangentspace.h @@ -0,0 +1,22 @@ +#pragma once +#include + +namespace tangent_space +{ + struct VertexData + { + const void* positionData; + size_t positionDataStride; + const void* normalData; + size_t normalDataStride; + const void* uvData; + size_t uvDataStride; + void* tangentData; + size_t tangentDataStride; + void* binormalData; + size_t binormalDataStride; + const uint16_t* triData; + }; + + void CalculateTangentSpace(const VertexData& vertexData, size_t triCount, size_t vertexCount); +} // namespace tangent_space From 71819e46a4588fb1af9be993df0b8ac5bc7eafe7 Mon Sep 17 00:00:00 2001 From: Jan Date: Thu, 5 Sep 2024 19:18:49 +0200 Subject: [PATCH 23/26] chore: set vertex color to 1.0 when no value read from gltf --- src/ObjLoading/XModel/Gltf/GltfLoader.cpp | 3 +- .../XModel/Gltf/Internal/GltfAccessor.cpp | 79 +++++++++++++++++++ .../XModel/Gltf/Internal/GltfAccessor.h | 19 +++++ 3 files changed, 100 insertions(+), 1 deletion(-) diff --git a/src/ObjLoading/XModel/Gltf/GltfLoader.cpp b/src/ObjLoading/XModel/Gltf/GltfLoader.cpp index ef5face7..42644a5a 100644 --- a/src/ObjLoading/XModel/Gltf/GltfLoader.cpp +++ b/src/ObjLoading/XModel/Gltf/GltfLoader.cpp @@ -214,6 +214,7 @@ namespace const auto vertexCount = positionAccessor->GetCount(); NullAccessor nullAccessor(vertexCount); + OnesAccessor onesAccessor(vertexCount); // clang-format off auto* normalAccessor = GetAccessorForIndex( @@ -237,7 +238,7 @@ namespace accessorsForVertex.colorAccessor, {JsonAccessorType::VEC3, JsonAccessorType::VEC4}, {JsonAccessorComponentType::FLOAT, JsonAccessorComponentType::UNSIGNED_BYTE, JsonAccessorComponentType::UNSIGNED_SHORT} - ).value_or(&nullAccessor); + ).value_or(&onesAccessor); VerifyAccessorVertexCount("COLOR_0", colorAccessor, vertexCount); auto* jointsAccessor = GetAccessorForIndex( diff --git a/src/ObjLoading/XModel/Gltf/Internal/GltfAccessor.cpp b/src/ObjLoading/XModel/Gltf/Internal/GltfAccessor.cpp index 3ded1bb8..8bf26355 100644 --- a/src/ObjLoading/XModel/Gltf/Internal/GltfAccessor.cpp +++ b/src/ObjLoading/XModel/Gltf/Internal/GltfAccessor.cpp @@ -81,6 +81,85 @@ size_t NullAccessor::GetCount() const return m_count; } +OnesAccessor::OnesAccessor(const size_t count) + : m_count(count) +{ +} + +bool OnesAccessor::GetFloatVec2(const size_t index, float (&out)[2]) const +{ + if (index >= m_count) + return false; + + out[0] = 1.0f; + out[1] = 1.0f; + + return true; +} + +bool OnesAccessor::GetFloatVec3(const size_t index, float (&out)[3]) const +{ + if (index >= m_count) + return false; + + out[0] = 1.0f; + out[1] = 1.0f; + out[2] = 1.0f; + + return true; +} + +bool OnesAccessor::GetFloatVec4(const size_t index, float (&out)[4]) const +{ + if (index >= m_count) + return false; + + out[0] = 1.0f; + out[1] = 1.0f; + out[2] = 1.0f; + out[3] = 1.0f; + + return true; +} + +bool OnesAccessor::GetUnsigned(const size_t index, unsigned& out) const +{ + if (index >= m_count) + return false; + + out = 0xFFFFFFFFu; + + return true; +} + +bool OnesAccessor::GetUnsignedVec4(const size_t index, unsigned (&out)[4]) const +{ + if (index >= m_count) + return false; + + out[0] = 0xFFFFFFFFu; + out[1] = 0xFFFFFFFFu; + out[2] = 0xFFFFFFFFu; + out[3] = 0xFFFFFFFFu; + + return true; +} + +std::optional OnesAccessor::GetComponentType() const +{ + return std::nullopt; +} + +std::optional OnesAccessor::GetType() const +{ + return std::nullopt; +} + +size_t OnesAccessor::GetCount() const +{ + return m_count; +} + FloatAccessor::FloatAccessor(const BufferView* bufferView, const JsonAccessorType type, size_t byteOffset, const size_t count) : m_buffer_view(bufferView), m_type(type), diff --git a/src/ObjLoading/XModel/Gltf/Internal/GltfAccessor.h b/src/ObjLoading/XModel/Gltf/Internal/GltfAccessor.h index 12b442c3..8395f037 100644 --- a/src/ObjLoading/XModel/Gltf/Internal/GltfAccessor.h +++ b/src/ObjLoading/XModel/Gltf/Internal/GltfAccessor.h @@ -46,6 +46,25 @@ namespace gltf size_t m_count; }; + class OnesAccessor final : public Accessor + { + public: + explicit OnesAccessor(size_t count); + + [[nodiscard]] std::optional GetType() const override; + [[nodiscard]] std::optional GetComponentType() const override; + [[nodiscard]] size_t GetCount() const override; + + [[nodiscard]] bool GetFloatVec2(size_t index, float (&out)[2]) const override; + [[nodiscard]] bool GetFloatVec3(size_t index, float (&out)[3]) const override; + [[nodiscard]] bool GetFloatVec4(size_t index, float (&out)[4]) const override; + [[nodiscard]] bool GetUnsigned(size_t index, unsigned& out) const override; + [[nodiscard]] bool GetUnsignedVec4(size_t index, unsigned (&out)[4]) const override; + + private: + size_t m_count; + }; + class FloatAccessor final : public Accessor { public: From 27d047198fa4a2472aa36b98799068390c678b24 Mon Sep 17 00:00:00 2001 From: Jan Date: Thu, 5 Sep 2024 21:13:49 +0200 Subject: [PATCH 24/26] chore: fix not terminating when not being able to load lod --- src/ObjLoading/Game/T6/XModel/JsonXModelLoader.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/ObjLoading/Game/T6/XModel/JsonXModelLoader.cpp b/src/ObjLoading/Game/T6/XModel/JsonXModelLoader.cpp index e015b5f6..5616b6cc 100644 --- a/src/ObjLoading/Game/T6/XModel/JsonXModelLoader.cpp +++ b/src/ObjLoading/Game/T6/XModel/JsonXModelLoader.cpp @@ -803,7 +803,10 @@ namespace { auto lodNumber = 0u; for (const auto& jLod : jXModel.lods) - LoadLod(jLod, xmodel, lodNumber++); + { + if (!LoadLod(jLod, xmodel, lodNumber++)) + return false; + } xmodel.numLods = static_cast(jXModel.lods.size()); xmodel.numsurfs = static_cast(m_surfaces.size()); From 46111ae106bb5e092bbf38a8db4a81b77c9c9ba3 Mon Sep 17 00:00:00 2001 From: Jan Date: Thu, 5 Sep 2024 21:16:55 +0200 Subject: [PATCH 25/26] chore: add note about only rigid models being supported right now --- src/ObjLoading/Game/T6/XModel/JsonXModelLoader.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/ObjLoading/Game/T6/XModel/JsonXModelLoader.cpp b/src/ObjLoading/Game/T6/XModel/JsonXModelLoader.cpp index 5616b6cc..aad41974 100644 --- a/src/ObjLoading/Game/T6/XModel/JsonXModelLoader.cpp +++ b/src/ObjLoading/Game/T6/XModel/JsonXModelLoader.cpp @@ -584,7 +584,6 @@ namespace void CreateVertsBlendData(XSurface& surface, const std::vector& vertexIndices, const XModelCommon& common) { // TODO - assert(false); } static void ReorderVerticesByWeightCount(std::vector& vertexIndices, XSurface& surface, const XModelCommon& common) @@ -684,7 +683,12 @@ namespace if (!hasVertsBlend) CreateVertListData(surface, xmodelToCommonVertexIndexLookup, common); else + { CreateVertsBlendData(surface, xmodelToCommonVertexIndexLookup, common); + + std::cerr << "Only rigid models are supported at the moment\n"; + return false; + } } return true; From 36ddb998a4c6043dc41afdbaccd3b1c082de2f92 Mon Sep 17 00:00:00 2001 From: Jan Date: Thu, 5 Sep 2024 21:33:06 +0200 Subject: [PATCH 26/26] fix: linux build --- src/ObjLoading/Game/T6/XModel/JsonXModelLoader.cpp | 4 ++-- src/ObjLoading/XModel/Tangentspace.h | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/ObjLoading/Game/T6/XModel/JsonXModelLoader.cpp b/src/ObjLoading/Game/T6/XModel/JsonXModelLoader.cpp index aad41974..b399fcb8 100644 --- a/src/ObjLoading/Game/T6/XModel/JsonXModelLoader.cpp +++ b/src/ObjLoading/Game/T6/XModel/JsonXModelLoader.cpp @@ -501,7 +501,7 @@ namespace const auto rigidBoneIndexForTri = GetRigidBoneIndicesForTris(vertexIndices, surface, common); std::vector triSortList(surface.triCount); - std::ranges::iota(triSortList, 0); + std::iota(triSortList.begin(), triSortList.end(), 0); std::ranges::sort(triSortList, [&rigidBoneIndexForTri](const size_t triIndex0, const size_t triIndex1) @@ -593,7 +593,7 @@ namespace const auto vertexCount = vertexIndices.size(); std::vector reorderLookup(vertexCount); - std::ranges::iota(reorderLookup, 0); + std::iota(reorderLookup.begin(), reorderLookup.end(), 0); std::ranges::sort(reorderLookup, [&common, &vertexIndices](const size_t& i0, const size_t& i1) diff --git a/src/ObjLoading/XModel/Tangentspace.h b/src/ObjLoading/XModel/Tangentspace.h index b4645c00..f0158bb4 100644 --- a/src/ObjLoading/XModel/Tangentspace.h +++ b/src/ObjLoading/XModel/Tangentspace.h @@ -1,5 +1,6 @@ #pragma once #include +#include namespace tangent_space {