From b1b734b6cf41c1a079b43ce3576feb0c6310f5a3 Mon Sep 17 00:00:00 2001 From: Lukas Cone Date: Sat, 9 Dec 2023 17:21:42 +0100 Subject: [PATCH] add mod xE5, xE6 --- include/revil/mod.hpp | 2 + src/mtf_mod/common.cpp | 125 +++++++++++++++++++++++++++- src/mtf_mod/header.hpp | 13 +++ src/mtf_mod/mesh.hpp | 9 +- src/mtf_mod/serialize.cpp | 80 +++++++++++++++++- src/mtf_mod/traits.hpp | 11 +++ toolset/mod_to_gltf/mod_to_gltf.cpp | 93 +++++++++++++++------ 7 files changed, 299 insertions(+), 34 deletions(-) diff --git a/include/revil/mod.hpp b/include/revil/mod.hpp index 9328a54..00467a9 100644 --- a/include/revil/mod.hpp +++ b/include/revil/mod.hpp @@ -36,6 +36,8 @@ enum MODVersion : uint16 { X2CFF = 0x2CFF, // RE6 PS3 XC5 = 0xC5, // LP2 PC XC3 = 0xC3, // LP2 PS3 + XE5 = 0xE6, + XE6 = 0xE6, }; struct alignas(8) MODMaker { diff --git a/src/mtf_mod/common.cpp b/src/mtf_mod/common.cpp index 3f484ce..8cbbfcd 100644 --- a/src/mtf_mod/common.cpp +++ b/src/mtf_mod/common.cpp @@ -1010,6 +1010,109 @@ std::map formats{ TexCoord, VertexTangent), }, + { + 0xF606F017, + BuildVertices( + VertexPosition, + VertexNormalSigned, + V{F::FLOAT, D::R32G32, U::TextureCoordiante}, + V{F::UINT, D::R8G8, U::BoneIndices}, + V{F::UNORM, D::R8G8, U::BoneWeights}, + VertexTangentSigned, + V{F::UINT, D::R8G8, U::BoneIndices}, + V{F::UNORM, D::R8G8, U::BoneWeights} + ), + }, + { + 0x01B36016, + BuildVertices( + VertexPosition, + VertexNormalSigned, + V{F::FLOAT, D::R32G32, U::TextureCoordiante}, + V{F::UINT, D::R8G8, U::BoneIndices}, + V{F::UNORM, D::R8G8, U::BoneWeights}, + VertexTangentSigned, // always zero? + V{F::UINT, D::R8G8, U::BoneIndices}, + V{F::UNORM, D::R8G8, U::BoneWeights} + ), + }, + { + 0xD6784014, + BuildVertices( + VertexPosition, + VertexNormalSigned, + V{F::FLOAT, D::R32G32, U::TextureCoordiante}, + V{F::UINT, D::R8G8, U::BoneIndices}, + V{F::UNORM, D::R8G8, U::BoneWeights} + ), + }, + { + 0x82917009, + BuildVertices( + VertexPosition, + VertexNormalSigned, + V{F::FLOAT, D::R32G32, U::TextureCoordiante} + ), + }, + { + 0x59DC400B, + BuildVertices( + VertexPosition, + VertexNormalSigned, + V{F::FLOAT, D::R32G32, U::TextureCoordiante}, + VertexTangentSigned + ), + }, + { + 0x43FB3015, + BuildVertices( + VertexPosition, + VertexNormalSigned, + V{F::FLOAT, D::R32G32, U::TextureCoordiante}, + V{F::UINT, D::R8G8, U::BoneIndices}, + V{F::UNORM, D::R8G8, U::BoneWeights}, + VertexTangentSigned + ), + }, + { + 0x7BA7401B, + BuildVertices( + VertexPosition, + VertexNormalSigned, + V{F::FLOAT, D::R32G32, U::TextureCoordiante}, + V{F::UINT, D::R8G8, U::BoneIndices}, + V{F::UNORM, D::R8G8, U::BoneWeights}, + VertexColor + ), + }, + { + 0xAE252019, + BuildVertices( + VertexPosition, + VertexNormalSigned, + V{F::FLOAT, D::R32G32, U::TextureCoordiante}, + V{F::UINT, D::R8G8, U::BoneIndices}, + V{F::UNORM, D::R8G8, U::BoneWeights}, + VertexTangentSigned, + V{F::UINT, D::R8G8, U::BoneIndices}, + V{F::UNORM, D::R8G8, U::BoneWeights}, + V{F::UINT, D::R8G8, U::BoneIndices}, + V{F::UNORM, D::R8G8, U::BoneWeights}, + V{F::UINT, D::R8G8, U::BoneIndices}, + V{F::UNORM, D::R8G8, U::BoneWeights} + ), + }, + { + 0x3D62B012, + BuildVertices( + VertexPosition, + VertexNormalSigned, + V{F::FLOAT, D::R32G32, U::TextureCoordiante}, + VertexColor, + VertexTangentSigned, // always zero? + V{F::FLOAT, D::R32G32, U::TextureCoordiante} + ), + }, }; // clang-format on @@ -1072,10 +1175,10 @@ static const auto makeV2 = [](auto &self, revil::MODImpl &main, bool swap, } else { main.vertices.storage.emplace_back(); - /*if (!edgeModels.contains(self.vertexFormat)) { + if (!edgeModels.contains(self.vertexFormat)) { throw std::runtime_error("Unregistered vertex format: " + std::to_string(self.vertexFormat)); - }*/ + } } uint16 *indexBuffer = reinterpret_cast( @@ -1251,6 +1354,24 @@ MODPrimitiveProxy MODMeshX06::ReflectLE(revil::MODImpl &main_) { return retval; } +MODPrimitiveProxy MODMeshXE5::ReflectBE(revil::MODImpl &main_) { + auto &main = static_cast &>(main_); + auto retval = makeV2(*this, main, true, [&](MODVertexDescriptor &d) { + swapBuffers(d, numVertices); + }); + retval.skinIndex = numEnvelopes; + + return retval; +} + +MODPrimitiveProxy MODMeshXE5::ReflectLE(revil::MODImpl &main_) { + auto &main = static_cast &>(main_); + auto retval = makeV2(*this, main, false, [&](MODVertexDescriptor &) {}); + retval.skinIndex = numEnvelopes; + + return retval; +} + std::string MODPrimitiveProxy::Name() const { return name; } size_t MODPrimitiveProxy::SkinIndex() const { return skinIndex; } int64 MODPrimitiveProxy::LODIndex() const { return lodIndex; } diff --git a/src/mtf_mod/header.hpp b/src/mtf_mod/header.hpp index df1df66..fa3116f 100644 --- a/src/mtf_mod/header.hpp +++ b/src/mtf_mod/header.hpp @@ -123,3 +123,16 @@ struct MODHeaderX05 : MODHeaderCommon { struct MODHeaderX06 : MODHeaderX05 { uint64 dataEnd; }; + +struct MODHeaderXE5 : MODHeaderCommon { + uint32 vertexBufferSize; + uint32 numTextures; + uint32 numGroups; + uint32 numSkins; + uint32 bones; + uint32 groups; + uint32 materials; + uint32 meshes; + uint32 vertexBuffer; + uint32 indices; +}; diff --git a/src/mtf_mod/mesh.hpp b/src/mtf_mod/mesh.hpp index 38392a6..d3b9239 100644 --- a/src/mtf_mod/mesh.hpp +++ b/src/mtf_mod/mesh.hpp @@ -93,8 +93,8 @@ struct MODMeshXC5 { using Unk00 = BitMemberDecl<3, 5>; using Unk01 = BitMemberDecl<4, 8>; using VertexBufferStride = BitMemberDecl<5, 8>; - using PrimitiveType = BitMemberDecl<6, 5>; - using Unk02 = BitMemberDecl<7, 3>; + using PrimitiveType = BitMemberDecl<6, 6>; + using Unk02 = BitMemberDecl<7, 2>; using BitField01 = BitFieldType; @@ -165,3 +165,8 @@ struct MODMeshX06 : MODMeshXD2 { MODPrimitiveProxy ReflectBE(revil::MODImpl &) { return {}; } void NoSwap(); }; + +struct MODMeshXE5 : MODMeshXD2 { + MODPrimitiveProxy ReflectLE(revil::MODImpl &); + MODPrimitiveProxy ReflectBE(revil::MODImpl &); +}; diff --git a/src/mtf_mod/serialize.cpp b/src/mtf_mod/serialize.cpp index bc9cd86..07a809f 100644 --- a/src/mtf_mod/serialize.cpp +++ b/src/mtf_mod/serialize.cpp @@ -191,6 +191,10 @@ template <> void FByteswapper(MODMetaDataV2 &self, bool) { FByteswapper(self.numEnvelopes); } +template <> void FByteswapper(MODSkinRemap<24> &self, bool) { + FByteswapper(self.count); +} + template <> void FByteswapper(MODSkinRemap<32> &self, bool) { FByteswapper(self.count); } @@ -266,6 +270,20 @@ template <> void FByteswapper(MODHeaderX70 &self, bool) { FByteswapper(self.indices); } +template <> void FByteswapper(MODHeaderXE5 &self, bool) { + FByteswapper(static_cast(self)); + FByteswapper(self.vertexBufferSize); + FByteswapper(self.numTextures); + FByteswapper(self.numGroups); + FByteswapper(self.numSkins); + FByteswapper(self.bones); + FByteswapper(self.groups); + FByteswapper(self.materials); + FByteswapper(self.meshes); + FByteswapper(self.vertexBuffer); + FByteswapper(self.indices); +} + template <> void FByteswapper(MODHeaderX170 &self, bool) { FByteswapper(static_cast(self)); FByteswapper(self.vertexBufferSize); @@ -431,6 +449,10 @@ template <> void FByteswapper(MODMeshXD2 &self, bool way) { FByteswapper(self.unk); } +template <> void FByteswapper(MODMeshXE5 &self, bool way) { + FByteswapper(self, way); +} + #pragma endregion #pragma region Savers void SaveMODX99(const MODInner &main, BinWritterRef wr) { @@ -795,6 +817,11 @@ template MODImpl::ptr LoadMODXD2x32(BinReaderRef_e rd) { rd.Read(main.bounds); rd.Read(main.metadata); + if (header.bones <= 0x80) { + // UMVC3 PS3 uses unique model + throw std::runtime_error("Unsupported model format"); + } + if (header.numBones) { rd.Seek(header.bones); rd.ReadContainer(main.bones, header.numBones); @@ -837,8 +864,7 @@ template MODImpl::ptr LoadMODXD2x32(BinReaderRef_e rd) { return std::make_unique(std::move(main)); } -template -MODImpl::ptr LoadMODXD3x64(BinReaderRef_e rdn) { +template MODImpl::ptr LoadMODXD3x64(BinReaderRef_e rdn) { MODHeaderXD3X64 header; MODInner main; BinReaderRef rd(rdn); @@ -944,6 +970,49 @@ MODImpl::ptr LoadMODX06(BinReaderRef_e rdn) { return std::make_unique(std::move(main)); } +MODImpl::ptr LoadMODXE5(BinReaderRef_e rd) { + MODHeaderXE5 header; + MODInner main; + rd.Push(); + rd.Read(header); + rd.ApplyPadding(); + rd.Read(main.bounds); + rd.Read(main.metadata); + + if (header.numBones) { + rd.Seek(header.bones); + rd.ReadContainer(main.bones, header.numBones); + rd.ReadContainer(main.refPoses, header.numBones); + rd.ReadContainer(main.transforms, header.numBones); + rd.Read(main.remaps); + rd.ReadContainer(main.skinRemaps, header.numSkins); + } + + if (header.numGroups) { + rd.Seek(header.groups); + rd.ReadContainer(main.groups, header.numGroups); + } + + rd.ReadContainer(main.materials.storage, header.numMaterials); + + rd.Seek(header.meshes); + rd.ReadContainer(main.meshes, header.numMeshes); + rd.ReadContainer(main.envelopes); + + main.vertexBufferSize = header.vertexBufferSize; + main.indexBufferSize = header.numIndices * sizeof(uint16); + + main.buffer.resize(main.vertexBufferSize + main.indexBufferSize); + + rd.Seek(header.vertexBuffer); + rd.ReadBuffer(&main.buffer[0], header.vertexBufferSize); + + rd.Seek(header.indices); + rd.ReadBuffer(&main.buffer[header.vertexBufferSize], main.indexBufferSize); + + return std::make_unique(std::move(main)); +} + #pragma endregion bool MODMaker::operator<(const MODMaker &i0) const { @@ -960,10 +1029,15 @@ static const std::map modLoaders{ {{MODVersion::XC5, false}, LoadMODXC5}, {{MODVersion::XD2, true}, LoadMODXD2x32}, {{MODVersion::XD3, true}, LoadMODXD2x32}, - {{MODVersion::XD3, false, false, Platform::PS4}, LoadMODXD3x64}, + {{MODVersion::XD3, false, false, Platform::PS4}, + LoadMODXD3x64}, {{MODVersion::XD3, false}, LoadMODXD3x64}, {{MODVersion::X05, false}, LoadMODX06}, {{MODVersion::X06, false}, LoadMODX06}, + {{MODVersion::XE5, true}, LoadMODXE5}, + {{MODVersion::XE5, false}, LoadMODXE5}, + {{MODVersion::XE6, true}, LoadMODXE5}, + {{MODVersion::XE6, false}, LoadMODXE5}, }; template MODImpl::ptr makeMod() { diff --git a/src/mtf_mod/traits.hpp b/src/mtf_mod/traits.hpp index ae8556b..f06b570 100644 --- a/src/mtf_mod/traits.hpp +++ b/src/mtf_mod/traits.hpp @@ -102,3 +102,14 @@ struct MODTraitsX06 { using mesh = MODMeshX06; using metadata = MODMetaDataV1; }; + +struct MODTraitsXE5 { + static constexpr size_t numSkinRemaps = 24; + static constexpr size_t numRemaps = 0x100; + static constexpr size_t pathSize = 1; + using primitive = MODPrimitiveProxy; + using bone = MODBoneV1_5; + using material = MODMaterialName; + using mesh = MODMeshXE5; + using metadata = MODMetaDataV1; +}; diff --git a/toolset/mod_to_gltf/mod_to_gltf.cpp b/toolset/mod_to_gltf/mod_to_gltf.cpp index 6d74b87..5f6d27c 100644 --- a/toolset/mod_to_gltf/mod_to_gltf.cpp +++ b/toolset/mod_to_gltf/mod_to_gltf.cpp @@ -148,8 +148,13 @@ gltf::Attributes MODGLTF::SaveVertices(const uni::VertexArray &vtArray) { std::vector weights[2]; std::vector bones[2]; size_t curWeight = 0; + size_t curBone = 0; for (auto d : *descs) { + static constexpr size_t fmtNumElements[]{ + 0, 4, 3, 4, 2, 3, 1, 2, 4, 3, 4, 2, 3, 2, 1, 3, 4, 1, + }; + using u = uni::PrimitiveDescriptor::Usage_e; switch (d->Usage()) { case u::Position: { @@ -202,18 +207,35 @@ gltf::Attributes MODGLTF::SaveVertices(const uni::VertexArray &vtArray) { } case u::BoneIndices: { + size_t numElems = fmtNumElements[uint8(d->Type().compType)]; + if (d->Type().outType == uni::FormatType::FLOAT) { uni::FormatCodec::fvec sampled; d->Codec().Sample(sampled, d->RawBuffer(), vertexCount, d->Stride()); d->Resample(sampled); - auto &curBones = bones[d->Index()]; - if (curBones.empty()) { - curBones.resize(vertexCount); - } + if (numElems == 4) { + curBone += 4; + auto &curBones = bones[d->Index()]; + if (curBones.empty()) { + curBones.resize(vertexCount); + } - for (size_t index = 0; auto &v : sampled) { - curBones[index++] = v.Convert(); + for (size_t index = 0; auto &v : sampled) { + curBones[index++] = v.Convert(); + } + } else { + for (size_t e = 0; e < numElems; e++, curBone++) { + auto &curBones = bones[curBone / 4]; + if (curBones.empty()) { + curBones.resize(vertexCount); + } + + for (size_t index = 0; auto &v : sampled) { + auto cvted = v.Convert(); + curBones[index++][curBone % 4] = cvted._arr[e]; + } + } } } else { uni::FormatCodec::ivec sampled; @@ -228,13 +250,28 @@ gltf::Attributes MODGLTF::SaveVertices(const uni::VertexArray &vtArray) { } } - auto &curBones = bones[d->Index()]; - if (curBones.empty()) { - curBones.resize(vertexCount); - } + if (numElems == 4) { + curBone += 4; + auto &curBones = bones[d->Index()]; + if (curBones.empty()) { + curBones.resize(vertexCount); + } - for (size_t index = 0; auto &v : sampled) { - curBones[index++] = v.Convert(); + for (size_t index = 0; auto &v : sampled) { + curBones[index++] = v.Convert(); + } + } else { + for (size_t e = 0; e < numElems; e++, curBone++) { + auto &curBones = bones[curBone / 4]; + if (curBones.empty()) { + curBones.resize(vertexCount); + } + + for (size_t index = 0; auto &v : sampled) { + auto cvted = v.Convert(); + curBones[index++][curBone % 4] = cvted._arr[e]; + } + } } } @@ -308,7 +345,7 @@ gltf::Attributes MODGLTF::SaveVertices(const uni::VertexArray &vtArray) { const size_t numWeights = weights[0].size(); if (!weights[1].empty()) { - if (curWeight < 8) { + if (curWeight != curBone && curWeight < 8) { for (size_t i = 0; i < numWeights; i++) { auto &wt0 = weights[0].at(i); auto &wt1 = weights[1].at(i); @@ -323,20 +360,22 @@ gltf::Attributes MODGLTF::SaveVertices(const uni::VertexArray &vtArray) { WriteWeights(1); WriteBones(1); } else if (!weights[0].empty()) { - if (curWeight == 3) { - for (size_t i = 0; i < numWeights; i++) { - auto &wt0 = weights[0].at(i); - wt0[3] = std::max(0xff - wt0[2] - wt0[1] - wt0[0], 0); - } - } else if (curWeight == 2) { - for (size_t i = 0; i < numWeights; i++) { - auto &wt0 = weights[0].at(i); - wt0[2] = std::max(0xff - wt0[1] - wt0[0], 0); - } - } else if (curWeight == 1) { - for (size_t i = 0; i < numWeights; i++) { - auto &wt0 = weights[0].at(i); - wt0[1] = std::max(0xff - wt0[0], 0); + if (curWeight != curBone) { + if (curWeight == 3) { + for (size_t i = 0; i < numWeights; i++) { + auto &wt0 = weights[0].at(i); + wt0[3] = std::max(0xff - wt0[2] - wt0[1] - wt0[0], 0); + } + } else if (curWeight == 2) { + for (size_t i = 0; i < numWeights; i++) { + auto &wt0 = weights[0].at(i); + wt0[2] = std::max(0xff - wt0[1] - wt0[0], 0); + } + } else if (curWeight == 1) { + for (size_t i = 0; i < numWeights; i++) { + auto &wt0 = weights[0].at(i); + wt0[1] = std::max(0xff - wt0[0], 0); + } } }