From 1e1485cedc755319f877d90272404f8c7e819588 Mon Sep 17 00:00:00 2001 From: Jan Date: Sat, 14 Aug 2021 11:35:41 +0200 Subject: [PATCH] Add IW3 xmodel dumping --- src/Common/Game/IW3/CommonIW3.cpp | 35 + src/Common/Game/IW3/CommonIW3.h | 12 + src/Common/Game/IW3/IW3_Assets.h | 19 +- .../IW3/AssetDumpers/AssetDumperXModel.cpp | 596 ++++++++++++++++++ .../Game/IW3/AssetDumpers/AssetDumperXModel.h | 35 + src/ObjWriting/Game/IW3/ZoneDumperIW3.cpp | 3 +- 6 files changed, 693 insertions(+), 7 deletions(-) create mode 100644 src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperXModel.cpp create mode 100644 src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperXModel.h diff --git a/src/Common/Game/IW3/CommonIW3.cpp b/src/Common/Game/IW3/CommonIW3.cpp index e69de29b..9df6bd5a 100644 --- a/src/Common/Game/IW3/CommonIW3.cpp +++ b/src/Common/Game/IW3/CommonIW3.cpp @@ -0,0 +1,35 @@ +#include "CommonIW3.h" + +#include "Utils/Pack.h" + +using namespace IW3; + +PackedTexCoords Common::Vec2PackTexCoords(const vec2_t* in) +{ + return PackedTexCoords{Pack32::Vec2PackTexCoords(reinterpret_cast(in))}; +} + +PackedUnitVec Common::Vec3PackUnitVec(const vec3_t* in) +{ + return PackedUnitVec{Pack32::Vec3PackUnitVec(reinterpret_cast(in))}; +} + +GfxColor Common::Vec4PackGfxColor(const vec4_t* in) +{ + return GfxColor{Pack32::Vec4PackGfxColor(reinterpret_cast(in))}; +} + +void Common::Vec2UnpackTexCoords(const PackedTexCoords& in, vec2_t* out) +{ + Pack32::Vec2UnpackTexCoords(in.packed, reinterpret_cast(out)); +} + +void Common::Vec3UnpackUnitVec(const PackedUnitVec& in, vec3_t* out) +{ + Pack32::Vec3UnpackUnitVec(in.packed, reinterpret_cast(out)); +} + +void Common::Vec4UnpackGfxColor(const GfxColor& in, vec4_t* out) +{ + Pack32::Vec4UnpackGfxColor(in.packed, reinterpret_cast(out)); +} diff --git a/src/Common/Game/IW3/CommonIW3.h b/src/Common/Game/IW3/CommonIW3.h index e7e310b5..7bfef8c6 100644 --- a/src/Common/Game/IW3/CommonIW3.h +++ b/src/Common/Game/IW3/CommonIW3.h @@ -1,5 +1,17 @@ #pragma once +#include "IW3.h" + namespace IW3 { + class Common + { + public: + 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); + }; } \ No newline at end of file diff --git a/src/Common/Game/IW3/IW3_Assets.h b/src/Common/Game/IW3/IW3_Assets.h index 058d37f5..9b8c8e18 100644 --- a/src/Common/Game/IW3/IW3_Assets.h +++ b/src/Common/Game/IW3/IW3_Assets.h @@ -134,6 +134,7 @@ namespace IW3 typedef char cbrushedge_t; typedef float vec2_t[2]; typedef float vec3_t[3]; + typedef float vec4_t[4]; typedef tdef_align(128) unsigned int raw_uint128; struct XModelPiece @@ -276,6 +277,12 @@ namespace IW3 XAnimDeltaPart* deltaPart; }; + struct DObjSkelMat + { + float axis[3][4]; + float origin[4]; + }; + struct DObjAnimMat { float quat[4]; @@ -492,8 +499,8 @@ namespace IW3 float radius; float mins[3]; float maxs[3]; - int16_t numLods; - int16_t collLod; + uint16_t numLods; + uint16_t collLod; XModelStreamInfo streamInfo; int memUsage; char flags; @@ -593,10 +600,10 @@ namespace IW3 { MaterialInfo info; char stateBitsEntry[34]; - char textureCount; - char constantCount; - char stateBitsCount; - char stateFlags; + unsigned char textureCount; + unsigned char constantCount; + unsigned char stateBitsCount; + unsigned char stateFlags; char cameraRegion; MaterialTechniqueSet* techniqueSet; MaterialTextureDef* textureTable; diff --git a/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperXModel.cpp b/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperXModel.cpp new file mode 100644 index 00000000..c1ccc797 --- /dev/null +++ b/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperXModel.cpp @@ -0,0 +1,596 @@ +#include "AssetDumperXModel.h" + +#include +#include + +#include "ObjWriting.h" +#include "Game/IW3/CommonIW3.h" +#include "Math/Quaternion.h" +#include "Model/XModel/XModelExportWriter.h" +#include "Utils/HalfFloat.h" +#include "Utils/QuatInt16.h" + +using namespace IW3; + +bool AssetDumperXModel::ShouldDump(XAssetInfo* asset) +{ + return !asset->m_name.empty() && asset->m_name[0] != ','; +} + +void AssetDumperXModel::AddObjMaterials(ObjWriter& writer, DistinctMapper& materialMapper, const XModel* model) +{ + if (!model->materialHandles) + return; + + for (auto surfIndex = 0u; surfIndex < model->numsurfs; surfIndex++) + { + Material* material = model->materialHandles[surfIndex]; + if (!materialMapper.Add(material)) + continue; + + MtlMaterial mtl; + mtl.materialName = std::string(material->info.name); + + GfxImage* colorMap = nullptr; + GfxImage* normalMap = nullptr; + GfxImage* specularMap = nullptr; + + for (auto i = 0u; i < material->textureCount; i++) + { + const auto& texture = material->textureTable[i]; + + switch (texture.semantic) + { + case TS_COLOR_MAP: + if (texture.nameStart == 'c' && texture.nameEnd == 'p') + colorMap = texture.u.image; + break; + + // Disabled due to looking weird in Blender + // case TS_NORMAL_MAP: + // normalMap = texture.u.image; + // break; + + case TS_SPECULAR_MAP: + specularMap = texture.u.image; + break; + + default: + break; + } + } + + if (colorMap != nullptr) + mtl.colorMapName = colorMap->name; + if (normalMap != nullptr) + mtl.normalMapName = normalMap->name; + if (specularMap != nullptr) + mtl.specularMapName = specularMap->name; + + writer.AddMaterial(std::move(mtl)); + } +} + +void AssetDumperXModel::AddObjObjects(ObjWriter& writer, const DistinctMapper& materialMapper, const XModel* model, const unsigned lod) +{ + const auto surfCount = model->lodInfo[lod].numsurfs; + const auto baseSurfIndex = model->lodInfo[lod].surfIndex; + + for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++) + { + ObjObject object; + object.name = "surf" + std::to_string(surfIndex); + object.materialIndex = static_cast(materialMapper.GetDistinctPositionByInputPosition(surfIndex + baseSurfIndex)); + + writer.AddObject(std::move(object)); + } +} + +void AssetDumperXModel::AddObjVertices(ObjWriter& writer, const XModel* model, const unsigned lod) +{ + const auto* surfs = &model->surfs[model->lodInfo[lod].surfIndex]; + const auto surfCount = model->lodInfo[lod].numsurfs; + + for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++) + { + const auto& surface = surfs[surfIndex]; + + for (auto vertexIndex = 0u; vertexIndex < surface.vertCount; vertexIndex++) + { + const auto& v = surface.verts0[vertexIndex]; + vec2_t uv; + vec3_t normalVec; + + Common::Vec2UnpackTexCoords(v.texCoord, &uv); + Common::Vec3UnpackUnitVec(v.normal, &normalVec); + + ObjVertex objVertex{}; + ObjNormal objNormal{}; + ObjUv objUv{}; + objVertex.coordinates[0] = v.xyz[0]; + objVertex.coordinates[1] = v.xyz[2]; + objVertex.coordinates[2] = -v.xyz[1]; + objNormal.normal[0] = normalVec[0]; + objNormal.normal[1] = normalVec[2]; + objNormal.normal[2] = -normalVec[1]; + objUv.uv[0] = uv[0]; + objUv.uv[1] = 1.0f - uv[1]; + + writer.AddVertex(static_cast(surfIndex), objVertex); + writer.AddNormal(static_cast(surfIndex), objNormal); + writer.AddUv(static_cast(surfIndex), objUv); + } + } +} + +void AssetDumperXModel::AddObjFaces(ObjWriter& writer, const XModel* model, const unsigned lod) +{ + const auto* surfs = &model->surfs[model->lodInfo[lod].surfIndex]; + const auto surfCount = model->lodInfo[lod].numsurfs; + + for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++) + { + const auto& surface = surfs[surfIndex]; + for (auto triIndex = 0u; triIndex < surface.triCount; triIndex++) + { + const auto& tri = surface.triIndices[triIndex]; + + ObjFace face{}; + face.vertexIndex[0] = tri[2] + surface.baseVertIndex; + face.vertexIndex[1] = tri[1] + surface.baseVertIndex; + face.vertexIndex[2] = tri[0] + surface.baseVertIndex; + face.normalIndex[0] = face.vertexIndex[0]; + face.normalIndex[1] = face.vertexIndex[1]; + face.normalIndex[2] = face.vertexIndex[2]; + face.uvIndex[0] = face.vertexIndex[0]; + face.uvIndex[1] = face.vertexIndex[1]; + face.uvIndex[2] = face.vertexIndex[2]; + writer.AddFace(static_cast(surfIndex), face); + } + } +} + +void AssetDumperXModel::DumpObjMat(const AssetDumpingContext& context, XAssetInfo* asset) +{ + const auto* model = asset->Asset(); + const auto matFile = context.OpenAssetFile("model_export/" + std::string(model->name) + ".mtl"); + + if (!matFile) + return; + + ObjWriter writer(context.m_zone->m_game->GetShortName(), context.m_zone->m_name); + DistinctMapper materialMapper(model->numsurfs); + + AddObjMaterials(writer, materialMapper, model); + writer.WriteMtl(*matFile); +} + +void AssetDumperXModel::DumpObjLod(AssetDumpingContext& context, XAssetInfo* asset, const unsigned lod) +{ + const auto* model = asset->Asset(); + std::ostringstream ss; + ss << "model_export/" << model->name << "_lod" << lod << ".OBJ"; + + const auto assetFile = context.OpenAssetFile(ss.str()); + + if (!assetFile) + return; + + ObjWriter writer(context.m_zone->m_game->GetShortName(), context.m_zone->m_name); + DistinctMapper materialMapper(model->numsurfs); + + AddObjMaterials(writer, materialMapper, model); + AddObjObjects(writer, materialMapper, model, lod); + AddObjVertices(writer, model, lod); + AddObjFaces(writer, model, lod); + + writer.WriteObj(*assetFile, std::string(model->name) + ".mtl"); +} + +void AssetDumperXModel::DumpObj(AssetDumpingContext& context, XAssetInfo* asset) +{ + const auto* model = asset->Asset(); + + DumpObjMat(context, asset); + for (auto currentLod = 0u; currentLod < model->numLods; currentLod++) + { + DumpObjLod(context, asset, currentLod); + } +} + +void AssetDumperXModel::AddXModelBones(const AssetDumpingContext& context, AbstractXModelWriter& writer, const XModel* model) +{ + for (auto boneNum = 0u; boneNum < model->numBones; boneNum++) + { + XModelBone bone; + if (model->boneNames[boneNum] < context.m_zone->m_script_strings.Count()) + bone.name = context.m_zone->m_script_strings[model->boneNames[boneNum]]; + else + bone.name = "INVALID_BONE_NAME"; + + if (boneNum < model->numRootBones) + bone.parentIndex = -1; + else + bone.parentIndex = static_cast(boneNum - static_cast(model->parentList[boneNum - model->numRootBones])); + + bone.scale[0] = 1.0f; + 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.localOffset[0] = 0; + bone.localOffset[1] = 0; + bone.localOffset[2] = 0; + bone.localRotation = Quaternion32(0, 0, 0, 1); + } + 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]; + 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]), + QuatInt16::ToFloat(model->quats[boneNum - model->numRootBones][3]) + ); + } + + writer.AddBone(std::move(bone)); + } +} + +void AssetDumperXModel::AddXModelMaterials(AbstractXModelWriter& writer, DistinctMapper& materialMapper, const XModel* model) +{ + for (auto surfaceMaterialNum = 0; surfaceMaterialNum < model->numsurfs; surfaceMaterialNum++) + { + Material* material = model->materialHandles[surfaceMaterialNum]; + if (materialMapper.Add(material)) + { + XModelMaterial xMaterial; + xMaterial.ApplyDefaults(); + + xMaterial.name = material->info.name; + + for (auto textureIndex = 0; textureIndex < material->textureCount; textureIndex++) + { + const auto* textureTableEntry = &material->textureTable[textureIndex]; + if (textureTableEntry->semantic == TS_COLOR_MAP && textureTableEntry->nameStart == 'c' && textureTableEntry->nameEnd == 'p' && textureTableEntry->u.image) + { + xMaterial.colorMapName = textureTableEntry->u.image->name; + break; + } + } + + writer.AddMaterial(std::move(xMaterial)); + } + } +} + +void AssetDumperXModel::AddXModelObjects(AbstractXModelWriter& writer, const XModel* model, const unsigned lod) +{ + const auto surfCount = model->lodInfo[lod].numsurfs; + + for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++) + { + XModelObject object; + object.name = "surf" + std::to_string(surfIndex); + + writer.AddObject(std::move(object)); + } +} + +void AssetDumperXModel::AddXModelVertices(AbstractXModelWriter& writer, const XModel* model, const unsigned lod) +{ + const auto* surfs = &model->surfs[model->lodInfo[lod].surfIndex]; + const auto surfCount = model->lodInfo[lod].numsurfs; + + for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++) + { + const auto& surface = surfs[surfIndex]; + + 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]; + + writer.AddVertex(vertex); + } + } +} + +void AssetDumperXModel::AllocateXModelBoneWeights(const XModel* model, const unsigned lod, XModelVertexBoneWeightCollection& weightCollection) +{ + const auto* surfs = &model->surfs[model->lodInfo[lod].surfIndex]; + const auto surfCount = model->lodInfo[lod].numsurfs; + + weightCollection.totalWeightCount = 0u; + for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++) + { + const auto& surface = 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::AddXModelVertexBoneWeights(AbstractXModelWriter& writer, const XModel* model, const unsigned lod, XModelVertexBoneWeightCollection& weightCollection) +{ + const auto* surfs = &model->surfs[model->lodInfo[lod].surfIndex]; + const auto surfCount = model->lodInfo[lod].numsurfs; + + size_t weightOffset = 0u; + + for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++) + { + const auto& surface = 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::AddXModelFaces(AbstractXModelWriter& writer, const DistinctMapper& materialMapper, const XModel* model, const unsigned lod) +{ + const auto* surfs = &model->surfs[model->lodInfo[lod].surfIndex]; + const auto surfCount = model->lodInfo[lod].numsurfs; + const auto baseSurfIndex = model->lodInfo[lod].surfIndex; + + for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++) + { + const auto& surface = surfs[surfIndex]; + for (auto triIndex = 0u; triIndex < surface.triCount; triIndex++) + { + 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.objectIndex = static_cast(surfIndex); + face.materialIndex = static_cast(materialMapper.GetDistinctPositionByInputPosition(surfIndex + baseSurfIndex)); + writer.AddFace(face); + } + } +} + +void AssetDumperXModel::DumpXModelExportLod(const AssetDumpingContext& context, XAssetInfo* asset, const unsigned lod) +{ + const auto* model = asset->Asset(); + + std::ostringstream ss; + ss << "model_export/" << model->name << "_lod" << lod << ".XMODEL_EXPORT"; + + const auto assetFile = context.OpenAssetFile(ss.str()); + + if (!assetFile) + return; + + const auto writer = XModelExportWriter::CreateWriterForVersion6(context.m_zone->m_game->GetShortName(), context.m_zone->m_name); + DistinctMapper materialMapper(model->numsurfs); + XModelVertexBoneWeightCollection boneWeightCollection; + AllocateXModelBoneWeights(model, lod, boneWeightCollection); + + AddXModelBones(context, *writer, model); + AddXModelMaterials(*writer, materialMapper, model); + AddXModelObjects(*writer, model, lod); + AddXModelVertices(*writer, model, lod); + AddXModelVertexBoneWeights(*writer, model, lod, boneWeightCollection); + AddXModelFaces(*writer, materialMapper, model, lod); + + writer->Write(*assetFile); +} + +void AssetDumperXModel::DumpXModelExport(const AssetDumpingContext& context, XAssetInfo* asset) +{ + const auto* model = asset->Asset(); + for (auto currentLod = 0u; currentLod < model->numLods; currentLod++) + { + DumpXModelExportLod(context, asset, currentLod); + } +} + +void AssetDumperXModel::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) +{ + switch (ObjWriting::Configuration.ModelOutputFormat) + { + case ObjWriting::Configuration_t::ModelOutputFormat_e::OBJ: + DumpObj(context, asset); + break; + + case ObjWriting::Configuration_t::ModelOutputFormat_e::XMODEL_EXPORT: + DumpXModelExport(context, asset); + break; + + default: + assert(false); + break; + } +} diff --git a/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperXModel.h b/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperXModel.h new file mode 100644 index 00000000..c57920b0 --- /dev/null +++ b/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperXModel.h @@ -0,0 +1,35 @@ +#pragma once + +#include "Dumping/AbstractAssetDumper.h" +#include "Game/IW3/IW3.h" +#include "Utils/DistinctMapper.h" +#include "Model/XModel/AbstractXModelWriter.h" +#include "Model/Obj/ObjWriter.h" + +namespace IW3 +{ + class AssetDumperXModel final : public AbstractAssetDumper + { + static void AddObjMaterials(ObjWriter& writer, DistinctMapper& materialMapper, const XModel* model); + static void AddObjObjects(ObjWriter& writer, const DistinctMapper& materialMapper, const XModel* model, unsigned lod); + static void AddObjVertices(ObjWriter& writer, const XModel* model, unsigned lod); + static void AddObjFaces(ObjWriter& writer, const XModel* model, unsigned lod); + static void DumpObjLod(AssetDumpingContext& context, XAssetInfo* asset, unsigned lod); + static void DumpObjMat(const AssetDumpingContext& context, XAssetInfo* asset); + static void DumpObj(AssetDumpingContext& context, XAssetInfo* asset); + + static void AddXModelBones(const AssetDumpingContext& context, AbstractXModelWriter& writer, const XModel* model); + static void AddXModelMaterials(AbstractXModelWriter& writer, DistinctMapper& materialMapper, const XModel* model); + static void AddXModelObjects(AbstractXModelWriter& writer, const XModel* model, unsigned lod); + static void AddXModelVertices(AbstractXModelWriter& writer, const XModel* model, unsigned lod); + static void AllocateXModelBoneWeights(const XModel* model, unsigned lod, XModelVertexBoneWeightCollection& weightCollection); + static void AddXModelVertexBoneWeights(AbstractXModelWriter& writer, const XModel* model, unsigned lod, XModelVertexBoneWeightCollection& weightCollection); + static void AddXModelFaces(AbstractXModelWriter& writer, const DistinctMapper& materialMapper, const XModel* model, unsigned lod); + static void DumpXModelExportLod(const AssetDumpingContext& context, XAssetInfo* asset, unsigned lod); + static void DumpXModelExport(const AssetDumpingContext& context, XAssetInfo* asset); + + protected: + bool ShouldDump(XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + }; +} diff --git a/src/ObjWriting/Game/IW3/ZoneDumperIW3.cpp b/src/ObjWriting/Game/IW3/ZoneDumperIW3.cpp index 96de5b90..d5356d44 100644 --- a/src/ObjWriting/Game/IW3/ZoneDumperIW3.cpp +++ b/src/ObjWriting/Game/IW3/ZoneDumperIW3.cpp @@ -10,6 +10,7 @@ #include "AssetDumpers/AssetDumperRawFile.h" #include "AssetDumpers/AssetDumperStringTable.h" #include "AssetDumpers/AssetDumperWeapon.h" +#include "AssetDumpers/AssetDumperXModel.h" using namespace IW3; @@ -31,7 +32,7 @@ bool ZoneDumper::DumpZone(AssetDumpingContext& context) const // DUMP_ASSET_POOL(AssetDumperPhysPreset, m_phys_preset) // DUMP_ASSET_POOL(AssetDumperXAnimParts, m_xanim_parts) - // DUMP_ASSET_POOL(AssetDumperXModel, m_xmodel) + DUMP_ASSET_POOL(AssetDumperXModel, m_xmodel) // DUMP_ASSET_POOL(AssetDumperMaterial, m_material) // DUMP_ASSET_POOL(AssetDumperMaterialTechniqueSet, m_technique_set) DUMP_ASSET_POOL(AssetDumperGfxImage, m_image)