diff --git a/src/Common/Game/IW4/IW4_Assets.h b/src/Common/Game/IW4/IW4_Assets.h index da7d35c7..a54ee34e 100644 --- a/src/Common/Game/IW4/IW4_Assets.h +++ b/src/Common/Game/IW4/IW4_Assets.h @@ -384,6 +384,12 @@ namespace IW4 XAnimDeltaPart* deltaPart; }; + struct DObjSkelMat + { + float axis[3][4]; + float origin[4]; + }; + struct XSurfaceVertexInfo { int16_t vertCount[4]; diff --git a/src/ObjCommon/Model/VertexMerger.cpp b/src/ObjCommon/Model/VertexMerger.cpp deleted file mode 100644 index 71f8acc0..00000000 --- a/src/ObjCommon/Model/VertexMerger.cpp +++ /dev/null @@ -1,63 +0,0 @@ -#include "VertexMerger.h" - -#include -#include - -bool operator==(const VertexMergerPos& lhs, const VertexMergerPos& rhs) -{ - return std::fabs(lhs.x - rhs.x) < std::numeric_limits::epsilon() - && std::fabs(lhs.y - rhs.y) < std::numeric_limits::epsilon() - && std::fabs(lhs.z - rhs.z) < std::numeric_limits::epsilon(); -} - -bool operator!=(const VertexMergerPos& lhs, VertexMergerPos& rhs) -{ - return !(lhs == rhs); -} - -bool operator<(const VertexMergerPos& lhs, const VertexMergerPos& rhs) -{ - return std::tie(lhs.x, lhs.y, lhs.z) < std::tie(rhs.x, rhs.y, rhs.z); -} -/* -VertexMerger::VertexMerger() - : m_current_vertex_index(0) -{ -} - -VertexMerger::VertexMerger(const size_t totalVertexCount) - : m_current_vertex_index(0) -{ - m_position_lookup.reserve(totalVertexCount); -} - -void VertexMerger::ProcessVertex(VertexPos pos) -{ - const auto mapEntry = m_vertex_index_map.find(pos); - if (mapEntry == m_vertex_index_map.end()) - { - m_position_lookup.push_back(m_current_vertex_index); - m_unique_vertex_position_indices.push_back(m_current_vertex_index); - m_vertex_index_map.emplace(std::make_pair(pos, m_current_vertex_index)); - } - else - { - m_position_lookup.push_back(mapEntry->second); - } - - m_current_vertex_index++; -} - -size_t VertexMerger::GetUniqueVertexIndexForVertexIndex(const size_t vertexIndex) -{ - if (vertexIndex >= m_position_lookup.size()) - return 0; - - return m_position_lookup[vertexIndex]; -} - -const std::vector& VertexMerger::GetUniqueVertexIndices() const -{ - return m_unique_vertex_position_indices; -} -*/ \ No newline at end of file diff --git a/src/ObjCommon/Model/VertexMerger.h b/src/ObjCommon/Model/VertexMerger.h deleted file mode 100644 index ad877389..00000000 --- a/src/ObjCommon/Model/VertexMerger.h +++ /dev/null @@ -1,51 +0,0 @@ -#pragma once - -#include "Utils/DistinctMapper.h" - -struct VertexMergerPos -{ - float x; - float y; - float z; - - friend bool operator==(const VertexMergerPos& lhs, const VertexMergerPos& rhs); - friend bool operator!=(const VertexMergerPos& lhs, const VertexMergerPos& rhs); - friend bool operator<(const VertexMergerPos& lhs, const VertexMergerPos& rhs); -}; - -typedef DistinctMapper VertexMerger; - -/* -#include -#include - -#include "Utils/ClassUtils.h" - -class VertexMerger -{ -public: - struct VertexPos - { - float x; - float y; - float z; - - friend bool operator==(const VertexPos& lhs, const VertexPos& rhs); - friend bool operator!=(const VertexPos& lhs, const VertexPos& rhs); - friend bool operator<(const VertexPos& lhs, const VertexPos& rhs); - }; - - VertexMerger(); - explicit VertexMerger(size_t totalVertexCount); - - void ProcessVertex(VertexPos pos); - size_t GetUniqueVertexIndexForVertexIndex(size_t vertexIndex); - _NODISCARD const std::vector& GetUniqueVertexIndices() const; - -private: - size_t m_current_vertex_index; - std::vector m_unique_vertex_position_indices; - std::vector m_position_lookup; - std::map m_vertex_index_map; -}; -*/ \ No newline at end of file diff --git a/src/ObjCommon/Model/XModel/XModelCommon.cpp b/src/ObjCommon/Model/XModel/XModelCommon.cpp index e1364fb5..76442575 100644 --- a/src/ObjCommon/Model/XModel/XModelCommon.cpp +++ b/src/ObjCommon/Model/XModel/XModelCommon.cpp @@ -1,5 +1,8 @@ #include "XModelCommon.h" +#include +#include + void XModelMaterial::ApplyDefaults() { // Phong = Color, Bump, Spec, CosinePower @@ -42,3 +45,56 @@ void XModelMaterial::ApplyDefaults() blinn[1] = -1; phong = -1; } + +bool operator==(const VertexMergerPos& lhs, const VertexMergerPos& rhs) +{ + const auto coordinatesMatch = std::fabs(lhs.x - rhs.x) < std::numeric_limits::epsilon() + && std::fabs(lhs.y - rhs.y) < std::numeric_limits::epsilon() + && std::fabs(lhs.z - rhs.z) < std::numeric_limits::epsilon(); + + if (!coordinatesMatch || lhs.weightCount != rhs.weightCount) + return false; + + for (auto weightIndex = 0u; weightIndex < lhs.weightCount; weightIndex++) + { + if (lhs.weights[weightIndex].boneIndex != rhs.weights[weightIndex].boneIndex + || std::fabs(lhs.weights[weightIndex].weight - rhs.weights[weightIndex].weight) >= std::numeric_limits::epsilon()) + { + return false; + } + } + + return true; +} + +bool operator!=(const VertexMergerPos& lhs, VertexMergerPos& rhs) +{ + return !(lhs == rhs); +} + +bool operator<(const VertexMergerPos& lhs, const VertexMergerPos& rhs) +{ + const auto t0 = std::tie(lhs.x, lhs.y, lhs.z, rhs.weightCount); + const auto t1 = std::tie(rhs.x, rhs.y, rhs.z, rhs.weightCount); + if (t0 < t1) + return true; + + if (!(t0 == t1)) + return false; + + for (auto weightIndex = 0u; weightIndex < lhs.weightCount; weightIndex++) + { + const auto& lhsWeight = lhs.weights[weightIndex]; + const auto& rhsWeight = rhs.weights[weightIndex]; + + const auto t2 = std::tie(lhsWeight.boneIndex, lhsWeight.weight); + const auto t3 = std::tie(rhsWeight.boneIndex, rhsWeight.weight); + if (t2 < t3) + return true; + + if (!(t2 == t3)) + return false; + } + + return false; +} diff --git a/src/ObjCommon/Model/XModel/XModelCommon.h b/src/ObjCommon/Model/XModel/XModelCommon.h index cfcdab3b..158a69db 100644 --- a/src/ObjCommon/Model/XModel/XModelCommon.h +++ b/src/ObjCommon/Model/XModel/XModelCommon.h @@ -1,7 +1,9 @@ #pragma once #include +#include +#include "Utils/DistinctMapper.h" #include "Math/Quaternion.h" struct XModelObject @@ -14,8 +16,28 @@ struct XModelBone std::string name; int parentIndex; float scale[3]; - float offset[3]; - Quaternion32 rotation; + float globalOffset[3]; + float localOffset[3]; + Quaternion32 globalRotation; + Quaternion32 localRotation; +}; + +struct XModelBoneWeight +{ + int boneIndex; + float weight; +}; + +struct XModelVertexBoneWeightCollection +{ + std::unique_ptr weights; + size_t totalWeightCount; +}; + +struct XModelVertexBoneWeights +{ + const XModelBoneWeight* weights; + size_t weightCount; }; struct XModelVertex @@ -63,4 +85,19 @@ struct XModelMaterial std::string colorMapName; void ApplyDefaults(); -}; \ No newline at end of file +}; + +struct VertexMergerPos +{ + float x; + float y; + float z; + const XModelBoneWeight* weights; + size_t weightCount; + + friend bool operator==(const VertexMergerPos& lhs, const VertexMergerPos& rhs); + friend bool operator!=(const VertexMergerPos& lhs, const VertexMergerPos& rhs); + friend bool operator<(const VertexMergerPos& lhs, const VertexMergerPos& rhs); +}; + +typedef DistinctMapper VertexMerger; \ No newline at end of file diff --git a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperXModel.cpp b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperXModel.cpp index 72b5ae26..d8df20b7 100644 --- a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperXModel.cpp +++ b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperXModel.cpp @@ -195,19 +195,24 @@ void AssetDumperXModel::AddBonesToWriter(const AssetDumpingContext& context, Abs bone.scale[1] = 1.0f; bone.scale[2] = 1.0f; + bone.globalOffset[0] = model->baseMat[boneNum].trans[0]; + bone.globalOffset[1] = model->baseMat[boneNum].trans[1]; + bone.globalOffset[2] = model->baseMat[boneNum].trans[2]; + bone.globalRotation = Quaternion32(model->baseMat[boneNum].quat[0], model->baseMat[boneNum].quat[1], model->baseMat[boneNum].quat[2], model->baseMat[boneNum].quat[3]); + if (boneNum < model->numRootBones) { - bone.offset[0] = 0; - bone.offset[1] = 0; - bone.offset[2] = 0; - bone.rotation = Quaternion32(0, 0, 0, 1); + bone.localOffset[0] = 0; + bone.localOffset[1] = 0; + bone.localOffset[2] = 0; + bone.localRotation = Quaternion32(0, 0, 0, 1); } else { - bone.offset[0] = model->trans[boneNum - model->numRootBones][0]; - bone.offset[1] = model->trans[boneNum - model->numRootBones][1]; - bone.offset[2] = model->trans[boneNum - model->numRootBones][2]; - bone.rotation = Quaternion32( + 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]; + bone.localRotation = Quaternion32( QuatInt16::ToFloat(model->quats[boneNum - model->numRootBones][0]), QuatInt16::ToFloat(model->quats[boneNum - model->numRootBones][1]), QuatInt16::ToFloat(model->quats[boneNum - model->numRootBones][2]), @@ -262,6 +267,7 @@ void AssetDumperXModel::AddVerticesToWriter(AbstractXModelWriter& writer, const for (auto surfIndex = 0u; surfIndex < modelSurfs->numsurfs; surfIndex++) { const auto& surface = modelSurfs->surfs[surfIndex]; + for (auto vertexIndex = 0u; vertexIndex < surface.vertCount; vertexIndex++) { const auto& v = surface.verts0[vertexIndex]; @@ -286,11 +292,198 @@ void AssetDumperXModel::AddVerticesToWriter(AbstractXModelWriter& writer, const vertex.color[3] = color[3]; vertex.uv[0] = uv[0]; vertex.uv[1] = uv[1]; + writer.AddVertex(vertex); } } } +void AssetDumperXModel::AllocateBoneWeights(const XModelSurfs* modelSurfs, XModelVertexBoneWeightCollection& weightCollection) +{ + weightCollection.totalWeightCount = 0u; + for (auto surfIndex = 0u; surfIndex < modelSurfs->numsurfs; surfIndex++) + { + const auto& surface = modelSurfs->surfs[surfIndex]; + + if (surface.vertList) + { + weightCollection.totalWeightCount += surface.vertListCount; + } + + if (surface.vertInfo.vertsBlend) + { + weightCollection.totalWeightCount += surface.vertInfo.vertCount[0] * 1; + weightCollection.totalWeightCount += surface.vertInfo.vertCount[1] * 2; + weightCollection.totalWeightCount += surface.vertInfo.vertCount[2] * 3; + weightCollection.totalWeightCount += surface.vertInfo.vertCount[3] * 4; + } + } + + weightCollection.weights = std::make_unique(weightCollection.totalWeightCount); +} + +void AssetDumperXModel::AddVertexBoneWeights(AbstractXModelWriter& writer, const XModelSurfs* modelSurfs, XModelVertexBoneWeightCollection& weightCollection) +{ + size_t weightOffset = 0u; + + for (auto surfIndex = 0u; surfIndex < modelSurfs->numsurfs; surfIndex++) + { + const auto& surface = modelSurfs->surfs[surfIndex]; + auto handledVertices = 0u; + + if (surface.vertList) + { + for (auto vertListIndex = 0u; vertListIndex < surface.vertListCount; vertListIndex++) + { + const auto& vertList = surface.vertList[vertListIndex]; + const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; + + weightCollection.weights[weightOffset++] = XModelBoneWeight{ + static_cast(vertList.boneOffset / sizeof(DObjSkelMat)), + 1.0f + }; + + for (auto vertListVertexOffset = 0u; vertListVertexOffset < vertList.vertCount; vertListVertexOffset++) + { + writer.AddVertexBoneWeights(XModelVertexBoneWeights{ + boneWeightOffset, + 1 + }); + } + handledVertices += vertList.vertCount; + } + } + + auto vertsBlendOffset = 0u; + if (surface.vertInfo.vertsBlend) + { + // 1 bone weight + 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)); + weightCollection.weights[weightOffset++] = XModelBoneWeight{ + boneIndex0, + 1.0f + }; + + vertsBlendOffset += 1; + + writer.AddVertexBoneWeights(XModelVertexBoneWeights{ + boneWeightOffset, + 1 + }); + } + + // 2 bone weights + 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 boneWeight1 = HalfFloat::ToFloat(surface.vertInfo.vertsBlend[vertsBlendOffset + 2]); + const auto boneWeight0 = 1.0f - boneWeight1; + + weightCollection.weights[weightOffset++] = XModelBoneWeight{ + boneIndex0, + boneWeight0 + }; + weightCollection.weights[weightOffset++] = XModelBoneWeight{ + boneIndex1, + boneWeight1 + }; + + vertsBlendOffset += 3; + + writer.AddVertexBoneWeights(XModelVertexBoneWeights{ + boneWeightOffset, + 2 + }); + } + + // 3 bone weights + 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 boneWeight1 = HalfFloat::ToFloat(surface.vertInfo.vertsBlend[vertsBlendOffset + 2]); + const auto boneIndex2 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 3] / sizeof(DObjSkelMat)); + const auto boneWeight2 = HalfFloat::ToFloat(surface.vertInfo.vertsBlend[vertsBlendOffset + 4]); + const auto boneWeight0 = 1.0f - boneWeight1 - boneWeight2; + + weightCollection.weights[weightOffset++] = XModelBoneWeight{ + boneIndex0, + boneWeight0 + }; + weightCollection.weights[weightOffset++] = XModelBoneWeight{ + boneIndex1, + boneWeight1 + }; + weightCollection.weights[weightOffset++] = XModelBoneWeight{ + boneIndex2, + boneWeight2 + }; + + vertsBlendOffset += 5; + + writer.AddVertexBoneWeights(XModelVertexBoneWeights{ + boneWeightOffset, + 3 + }); + } + + // 4 bone weights + 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 boneWeight1 = HalfFloat::ToFloat(surface.vertInfo.vertsBlend[vertsBlendOffset + 2]); + const auto boneIndex2 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 3] / sizeof(DObjSkelMat)); + const auto boneWeight2 = HalfFloat::ToFloat(surface.vertInfo.vertsBlend[vertsBlendOffset + 4]); + const auto boneIndex3 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 5] / sizeof(DObjSkelMat)); + const auto boneWeight3 = HalfFloat::ToFloat(surface.vertInfo.vertsBlend[vertsBlendOffset + 6]); + const auto boneWeight0 = 1.0f - boneWeight1 - boneWeight2 - boneWeight3; + + weightCollection.weights[weightOffset++] = XModelBoneWeight{ + boneIndex0, + boneWeight0 + }; + weightCollection.weights[weightOffset++] = XModelBoneWeight{ + boneIndex1, + boneWeight1 + }; + weightCollection.weights[weightOffset++] = XModelBoneWeight{ + boneIndex2, + boneWeight2 + }; + weightCollection.weights[weightOffset++] = XModelBoneWeight{ + boneIndex3, + boneWeight3 + }; + + vertsBlendOffset += 7; + + writer.AddVertexBoneWeights(XModelVertexBoneWeights{ + boneWeightOffset, + 4 + }); + } + + handledVertices += surface.vertInfo.vertCount[0] + surface.vertInfo.vertCount[1] + surface.vertInfo.vertCount[2] + surface.vertInfo.vertCount[3]; + } + + for (; handledVertices < surface.vertCount; handledVertices++) + { + writer.AddVertexBoneWeights(XModelVertexBoneWeights{ + nullptr, + 0 + }); + } + } +} + void AssetDumperXModel::AddFacesToWriter(AbstractXModelWriter& writer, const DistinctMapper& materialMapper, const XModelSurfs* modelSurfs, const int baseSurfaceIndex) { @@ -327,11 +520,14 @@ void AssetDumperXModel::DumpXModelExportLod(const AssetDumpingContext& context, const auto writer = XModelExportWriter::CreateWriterForVersion6(context.m_zone->m_game->GetShortName(), context.m_zone->m_name); DistinctMapper materialMapper(model->numsurfs); + XModelVertexBoneWeightCollection boneWeightCollection; + AllocateBoneWeights(modelSurfs, boneWeightCollection); AddBonesToWriter(context, *writer, model); AddMaterialsToWriter(*writer, materialMapper, model); AddObjectsToWriter(*writer, modelSurfs); AddVerticesToWriter(*writer, modelSurfs); + AddVertexBoneWeights(*writer, modelSurfs, boneWeightCollection); AddFacesToWriter(*writer, materialMapper, modelSurfs, model->lodInfo[lod].surfIndex); writer->Write(*assetFile); diff --git a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperXModel.h b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperXModel.h index 93f09ce3..9aa6dc0f 100644 --- a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperXModel.h +++ b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperXModel.h @@ -20,6 +20,8 @@ namespace IW4 static void AddMaterialsToWriter(AbstractXModelWriter& writer, DistinctMapper& materialMapper, const XModel* model); static void AddObjectsToWriter(AbstractXModelWriter& writer, const XModelSurfs* modelSurfs); static void AddVerticesToWriter(AbstractXModelWriter& writer, const XModelSurfs* modelSurfs); + static void AllocateBoneWeights(const XModelSurfs* modelSurfs, XModelVertexBoneWeightCollection& weightCollection); + static void AddVertexBoneWeights(AbstractXModelWriter& writer, const XModelSurfs* modelSurfs, XModelVertexBoneWeightCollection& weightCollection); static void AddFacesToWriter(AbstractXModelWriter& writer, const DistinctMapper& materialMapper, const XModelSurfs* modelSurfs, int baseSurfaceIndex); static void DumpXModelExportLod(const AssetDumpingContext& context, XAssetInfo* asset, const unsigned lod); static void DumpXModelExport(const AssetDumpingContext& context, XAssetInfo* asset); diff --git a/src/ObjWriting/Model/XModel/AbstractXModelWriter.cpp b/src/ObjWriting/Model/XModel/AbstractXModelWriter.cpp index def57b9a..1575b471 100644 --- a/src/ObjWriting/Model/XModel/AbstractXModelWriter.cpp +++ b/src/ObjWriting/Model/XModel/AbstractXModelWriter.cpp @@ -23,6 +23,11 @@ void AbstractXModelWriter::AddVertex(XModelVertex vertex) m_vertices.emplace_back(vertex); } +void AbstractXModelWriter::AddVertexBoneWeights(XModelVertexBoneWeights vertexBoneWeights) +{ + m_vertex_bone_weights.emplace_back(vertexBoneWeights); +} + void AbstractXModelWriter::AddFace(XModelFace face) { m_faces.emplace_back(face); diff --git a/src/ObjWriting/Model/XModel/AbstractXModelWriter.h b/src/ObjWriting/Model/XModel/AbstractXModelWriter.h index 2a332531..ab2c30e3 100644 --- a/src/ObjWriting/Model/XModel/AbstractXModelWriter.h +++ b/src/ObjWriting/Model/XModel/AbstractXModelWriter.h @@ -11,6 +11,7 @@ protected: std::vector m_bones; std::vector m_materials; std::vector m_vertices; + std::vector m_vertex_bone_weights; std::vector m_faces; public: @@ -20,5 +21,6 @@ public: void AddBone(XModelBone bone); void AddMaterial(XModelMaterial material); void AddVertex(XModelVertex vertex); + void AddVertexBoneWeights(XModelVertexBoneWeights vertexBoneWeights); void AddFace(XModelFace face); }; diff --git a/src/ObjWriting/Model/XModel/XModelExportWriter.cpp b/src/ObjWriting/Model/XModel/XModelExportWriter.cpp index d470869c..b85d4685 100644 --- a/src/ObjWriting/Model/XModel/XModelExportWriter.cpp +++ b/src/ObjWriting/Model/XModel/XModelExportWriter.cpp @@ -1,8 +1,8 @@ #include "XModelExportWriter.h" #include +#include -#include "Model/VertexMerger.h" #include "Math/Quaternion.h" class XModelExportWriterBase : public XModelExportWriter @@ -16,13 +16,27 @@ protected: void PrepareVertexMerger() { m_vertex_merger = VertexMerger(m_vertices.size()); + + auto vertexOffset = 0u; for (const auto& vertex : m_vertices) { + XModelVertexBoneWeights weights{ + nullptr, + 0 + }; + + if(vertexOffset < m_vertex_bone_weights.size()) + weights = m_vertex_bone_weights[vertexOffset]; + m_vertex_merger.Add(VertexMergerPos{ vertex.coordinates[0], vertex.coordinates[1], - vertex.coordinates[2] + vertex.coordinates[2], + weights.weights, + weights.weightCount }); + + vertexOffset++; } } @@ -58,16 +72,16 @@ protected: { stream << "BONE " << boneNum << "\n"; stream << "OFFSET "; - stream << std::setprecision(6) << std::fixed << bone.offset[0] - << ", " << std::setprecision(6) << std::fixed << bone.offset[1] - << ", " << std::setprecision(6) << std::fixed << bone.offset[2] << "\n"; + stream << std::setprecision(6) << std::fixed << bone.globalOffset[0] + << ", " << std::setprecision(6) << std::fixed << bone.globalOffset[1] + << ", " << std::setprecision(6) << std::fixed << bone.globalOffset[2] << "\n"; stream << "SCALE "; stream << std::setprecision(6) << std::fixed << bone.scale[0] << ", " << std::setprecision(6) << std::fixed << bone.scale[1] << ", " << std::setprecision(6) << std::fixed << bone.scale[2] << "\n"; - const Matrix32 mat = bone.rotation.ToMatrix(); + const Matrix32 mat = bone.globalRotation.ToMatrix(); stream << "X " << std::setprecision(6) << std::fixed << mat.m_data[0][0] << ", " << std::setprecision(6) << std::fixed << mat.m_data[1][0] << ", " << std::setprecision(6) << std::fixed << mat.m_data[2][0] << "\n"; @@ -103,8 +117,13 @@ class XModelExportWriter6 final : public XModelExportWriterBase stream << std::setprecision(6) << std::fixed << vertexPos.x << ", " << std::setprecision(6) << std::fixed << vertexPos.y << ", " << std::setprecision(6) << std::fixed << vertexPos.z << "\n"; - stream << "BONES 1\n"; // TODO: FIXME with bone weights - stream << "BONE 0 1.000000\n"; // TODO: FIXME with bone weights + stream << "BONES " << vertexPos.weightCount << "\n"; + + for (auto weightIndex = 0u; weightIndex < vertexPos.weightCount; weightIndex++) + { + stream << "BONE " << vertexPos.weights[weightIndex].boneIndex + << " " << std::setprecision(6) << std::fixed << vertexPos.weights[weightIndex].weight << "\n"; + } stream << "\n"; vertexNum++; } @@ -166,7 +185,8 @@ class XModelExportWriter6 final : public XModelExportWriterBase size_t materialNum = 0u; for (const auto& material : m_materials) { - stream << "MATERIAL " << materialNum << " \"" << material.name << "\" \"" << material.materialTypeName << "\" \"" << material.colorMapName << "\"\n"; + const auto colorMapPath = "../images/" + material.colorMapName + ".dds"; + stream << "MATERIAL " << materialNum << " \"" << material.name << "\" \"" << material.materialTypeName << "\" \"" << colorMapPath << "\"\n"; stream << "COLOR " << std::setprecision(6) << std::fixed << material.color[0] << " " << std::setprecision(6) << std::fixed << material.color[1] << " " << std::setprecision(6) << std::fixed << material.color[2]