From a9c693d04df73852f4756345558086f311e0b88d Mon Sep 17 00:00:00 2001 From: Jan Date: Wed, 9 Jul 2025 21:57:22 +0100 Subject: [PATCH 1/5] refactor: template remaining xmodel files for iw5,t5,t6 --- src/ObjCommon/Game/IW5/XModel/JsonXModelIW5.h | 32 --------- .../Game/IW5/XModel/XModelConstantsIW5.h | 32 --------- src/ObjCommon/Game/T5/XModel/JsonXModelT5.h | 31 -------- .../Game/T5/XModel/XModelConstantsT5.h | 31 -------- src/ObjCommon/Game/T6/XModel/JsonXModelT6.h | 33 --------- src/ObjCommon/XModel/JsonXModel.h.template | 71 +++++++++++++++++++ .../XModelConstants.h.template} | 30 ++++++-- .../Game/IW5/XModel/LoaderXModelIW5asdf.cpp | 55 -------------- .../Game/IW5/XModel/LoaderXModelIW5asdf.h | 13 ---- .../XModel/XModelDumper.cpp.template | 6 +- 10 files changed, 99 insertions(+), 235 deletions(-) delete mode 100644 src/ObjCommon/Game/IW5/XModel/JsonXModelIW5.h delete mode 100644 src/ObjCommon/Game/IW5/XModel/XModelConstantsIW5.h delete mode 100644 src/ObjCommon/Game/T5/XModel/JsonXModelT5.h delete mode 100644 src/ObjCommon/Game/T5/XModel/XModelConstantsT5.h delete mode 100644 src/ObjCommon/Game/T6/XModel/JsonXModelT6.h create mode 100644 src/ObjCommon/XModel/JsonXModel.h.template rename src/ObjCommon/{Game/T6/XModel/XModelConstantsT6.h => XModel/XModelConstants.h.template} (51%) delete mode 100644 src/ObjLoading/Game/IW5/XModel/LoaderXModelIW5asdf.cpp delete mode 100644 src/ObjLoading/Game/IW5/XModel/LoaderXModelIW5asdf.h diff --git a/src/ObjCommon/Game/IW5/XModel/JsonXModelIW5.h b/src/ObjCommon/Game/IW5/XModel/JsonXModelIW5.h deleted file mode 100644 index ce7424ff..00000000 --- a/src/ObjCommon/Game/IW5/XModel/JsonXModelIW5.h +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once - -#include "Json/JsonCommon.h" -#include -#include -#include -#include -#include - -namespace IW5 -{ - class JsonXModelLod - { - public: - std::string file; - float distance; - }; - - NLOHMANN_DEFINE_TYPE_EXTENSION(JsonXModelLod, file, distance); - - class JsonXModel - { - public: - std::vector lods; - std::optional collLod; - std::optional physPreset; - std::optional physCollmap; - uint8_t flags; - }; - - NLOHMANN_DEFINE_TYPE_EXTENSION(JsonXModel, lods, collLod, physPreset, physCollmap, flags); -} // namespace IW5 diff --git a/src/ObjCommon/Game/IW5/XModel/XModelConstantsIW5.h b/src/ObjCommon/Game/IW5/XModel/XModelConstantsIW5.h deleted file mode 100644 index a0a129a3..00000000 --- a/src/ObjCommon/Game/IW5/XModel/XModelConstantsIW5.h +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once - -#include "Game/IW5/IW5.h" - -namespace IW5 -{ - inline const char* HITLOC_NAMES[]{ - // clang-format off - "none", - "helmet", - "head", - "neck", - "torso_upper", - "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); -} // namespace IW5 diff --git a/src/ObjCommon/Game/T5/XModel/JsonXModelT5.h b/src/ObjCommon/Game/T5/XModel/JsonXModelT5.h deleted file mode 100644 index 5b52ca9a..00000000 --- a/src/ObjCommon/Game/T5/XModel/JsonXModelT5.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -#include "Json/JsonCommon.h" -#include -#include -#include -#include - -namespace T5 -{ - class JsonXModelLod - { - public: - std::string file; - float distance; - }; - - NLOHMANN_DEFINE_TYPE_EXTENSION(JsonXModelLod, file, distance); - - class JsonXModel - { - public: - std::vector lods; - std::optional collLod; - std::optional physPreset; - std::optional physConstraints; - unsigned flags; - }; - - NLOHMANN_DEFINE_TYPE_EXTENSION(JsonXModel, lods, collLod, physPreset, physConstraints, flags); -} // namespace T5 diff --git a/src/ObjCommon/Game/T5/XModel/XModelConstantsT5.h b/src/ObjCommon/Game/T5/XModel/XModelConstantsT5.h deleted file mode 100644 index 0481cd27..00000000 --- a/src/ObjCommon/Game/T5/XModel/XModelConstantsT5.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -#include "Game/T5/T5.h" - -namespace T5 -{ - inline const char* HITLOC_NAMES[]{ - // clang-format off - "none", - "helmet", - "head", - "neck", - "torso_upper", - "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", - // clang-format on - }; - static_assert(std::extent_v == HITLOC_COUNT); -} // namespace T5 diff --git a/src/ObjCommon/Game/T6/XModel/JsonXModelT6.h b/src/ObjCommon/Game/T6/XModel/JsonXModelT6.h deleted file mode 100644 index 79e6eb06..00000000 --- a/src/ObjCommon/Game/T6/XModel/JsonXModelT6.h +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -#include "Json/JsonCommon.h" -#include -#include -#include -#include - -namespace T6 -{ - class JsonXModelLod - { - public: - std::string file; - float distance; - }; - - NLOHMANN_DEFINE_TYPE_EXTENSION(JsonXModelLod, file, distance); - - class JsonXModel - { - public: - std::vector lods; - std::optional collLod; - std::optional physPreset; - std::optional physConstraints; - unsigned flags; - JsonVec3 lightingOriginOffset; - float lightingOriginRange; - }; - - NLOHMANN_DEFINE_TYPE_EXTENSION(JsonXModel, lods, collLod, physPreset, physConstraints, flags, lightingOriginOffset, lightingOriginRange); -} // namespace T6 diff --git a/src/ObjCommon/XModel/JsonXModel.h.template b/src/ObjCommon/XModel/JsonXModel.h.template new file mode 100644 index 00000000..315960e0 --- /dev/null +++ b/src/ObjCommon/XModel/JsonXModel.h.template @@ -0,0 +1,71 @@ +#options GAME (IW5, T5, T6) + +#filename "Game/" + GAME + "/XModel/JsonXModel" + GAME + ".h" + +#set GAME_HEADER "\"Game/" + GAME + "/" + GAME + ".h\"" + +#if GAME == "IW5" +#define FEATURE_IW5 +#elif GAME == "T5" +#define FEATURE_T5 +#elif GAME == "T6" +#define FEATURE_T6 +#endif + +// This file was templated. +// See JsonXModel.h.template. +// Do not modify, changes will be lost. +#pragma once + +#include "Json/JsonCommon.h" +#include +#include +#include +#include + +namespace GAME +{ + class JsonXModelLod + { + public: + std::string file; + float distance; + }; + + NLOHMANN_DEFINE_TYPE_EXTENSION(JsonXModelLod, file, distance); + + class JsonXModel + { + public: + std::vector lods; + std::optional collLod; + std::optional physPreset; +#if defined(FEATURE_IW5) + std::optional physCollmap; +#elif defined(FEATURE_T5) || defined(FEATURE_T6) + std::optional physConstraints; +#endif +#if defined(FEATURE_T6) + JsonVec3 lightingOriginOffset; + float lightingOriginRange; +#endif + unsigned flags; + }; + + NLOHMANN_DEFINE_TYPE_EXTENSION( + JsonXModel, + lods, + collLod, + physPreset, +#if defined(FEATURE_IW5) + physCollmap, +#elif defined(FEATURE_T5) || defined(FEATURE_T6) + physConstraints, +#endif +#if defined(FEATURE_T6) + lightingOriginOffset, + lightingOriginRange, +#endif + flags + ); +} // namespace GAME diff --git a/src/ObjCommon/Game/T6/XModel/XModelConstantsT6.h b/src/ObjCommon/XModel/XModelConstants.h.template similarity index 51% rename from src/ObjCommon/Game/T6/XModel/XModelConstantsT6.h rename to src/ObjCommon/XModel/XModelConstants.h.template index e7ae94cd..6f4b5bfe 100644 --- a/src/ObjCommon/Game/T6/XModel/XModelConstantsT6.h +++ b/src/ObjCommon/XModel/XModelConstants.h.template @@ -1,17 +1,36 @@ +#options GAME (IW5, T5, T6) + +#filename "Game/" + GAME + "/XModel/XModelConstants" + GAME + ".h" + +#set GAME_HEADER "\"Game/" + GAME + "/" + GAME + ".h\"" + +#if GAME == "IW5" +#define FEATURE_IW5 +#elif GAME == "T5" +#define FEATURE_T5 +#elif GAME == "T6" +#define FEATURE_T6 +#endif + +// This file was templated. +// See JsonXModel.h.template. +// Do not modify, changes will be lost. #pragma once -#include "Game/T6/T6.h" +#set GAME_HEADER "\"Game/" + GAME + "/" + GAME + ".h\"" +#include GAME_HEADER -namespace T6 +namespace GAME { inline const char* HITLOC_NAMES[]{ - // clang-format off "none", "helmet", "head", "neck", "torso_upper", +#if defined(FEATURE_T6) "torso_middle", +#endif "torso_lower", "right_arm_upper", "left_arm_upper", @@ -26,8 +45,9 @@ namespace T6 "right_foot", "left_foot", "gun", +#if defined(FEATURE_IW5) || defined(FEATURE_T6) "shield", - // clang-format on +#endif }; static_assert(std::extent_v == HITLOC_COUNT); -} // namespace T6 +} // namespace GAME diff --git a/src/ObjLoading/Game/IW5/XModel/LoaderXModelIW5asdf.cpp b/src/ObjLoading/Game/IW5/XModel/LoaderXModelIW5asdf.cpp deleted file mode 100644 index 973d59f3..00000000 --- a/src/ObjLoading/Game/IW5/XModel/LoaderXModelIW5asdf.cpp +++ /dev/null @@ -1,55 +0,0 @@ -// #include "LoaderXModelIW5.h" - -// #include "Game/IW5/IW5.h" -// #include "Game/IW5/XModel/XModelLoaderIW5.h" -// #include "Pool/GlobalAssetPool.h" - -// #include -// #include -// #include - -// using namespace IW5; - -// namespace -// { -// class XModelLoader final : public AssetCreator -// { -// public: -// XModelLoader(MemoryManager& memory, ISearchPath& searchPath) -// : m_memory(memory), -// m_search_path(searchPath) -// { -// } - -// AssetCreationResult CreateAsset(const std::string& assetName, AssetCreationContext& context) override -// { -// const auto file = m_search_path.Open(std::format("xmodel/{}.json", assetName)); -// if (!file.IsOpen()) -// return AssetCreationResult::NoAction(); - -// auto* xmodel = m_memory.Alloc(); -// xmodel->name = m_memory.Dup(assetName.c_str()); - -// AssetRegistration registration(assetName, xmodel); -// if (!LoadXModel(*file.m_stream, *xmodel, m_memory, context, registration)) -// { -// std::cerr << std::format("Failed to load xmodel \"{}\"\n", assetName); -// return AssetCreationResult::Failure(); -// } - -// return AssetCreationResult::Success(context.AddAsset(std::move(registration))); -// } - -// private: -// MemoryManager& m_memory; -// ISearchPath& m_search_path; -// }; -// } // namespace - -// namespace IW5 -// { -// std::unique_ptr> CreateXModelLoader(MemoryManager& memory, ISearchPath& searchPath) -// { -// return std::make_unique(memory, searchPath); -// } -// } // namespace IW5 diff --git a/src/ObjLoading/Game/IW5/XModel/LoaderXModelIW5asdf.h b/src/ObjLoading/Game/IW5/XModel/LoaderXModelIW5asdf.h deleted file mode 100644 index 3d656f21..00000000 --- a/src/ObjLoading/Game/IW5/XModel/LoaderXModelIW5asdf.h +++ /dev/null @@ -1,13 +0,0 @@ -// #pragma once - -// #include "Asset/IAssetCreator.h" -// #include "Game/IW5/IW5.h" -// #include "SearchPath/ISearchPath.h" -// #include "Utils/MemoryManager.h" - -// #include - -// namespace IW5 -// { -// std::unique_ptr> CreateXModelLoader(MemoryManager& memory, ISearchPath& searchPath); -// } // namespace IW5 diff --git a/src/ObjWriting/XModel/XModelDumper.cpp.template b/src/ObjWriting/XModel/XModelDumper.cpp.template index fee67d24..7759f92a 100644 --- a/src/ObjWriting/XModel/XModelDumper.cpp.template +++ b/src/ObjWriting/XModel/XModelDumper.cpp.template @@ -66,7 +66,7 @@ namespace { MaterialTextureDef* def = &material->textureTable[textureIndex]; -#ifdef FEATURE_IW5 +#if defined(FEATURE_IW5) if (def->semantic == TS_COLOR_MAP) potentialTextureDefs.push_back(def); #else @@ -155,7 +155,7 @@ namespace bool GetSurfaces(const XModel* model, const unsigned lod, XSurface*& surfs, unsigned& surfCount) { -#ifdef FEATURE_IW5 +#if defined(FEATURE_IW5) if (!model->lodInfo[lod].modelSurfs || !model->lodInfo[lod].modelSurfs->surfs) return false; @@ -722,7 +722,7 @@ namespace if (xmodel.physPreset && xmodel.physPreset->name) jXModel.physPreset = AssetName(xmodel.physPreset->name); -#ifdef FEATURE_IW5 +#if defined(FEATURE_IW5) if (xmodel.physCollmap && xmodel.physCollmap->name) jXModel.physCollmap = AssetName(xmodel.physCollmap->name); #endif From 3daa8b04120b707c6acaf62b8a7fa9c715fad077 Mon Sep 17 00:00:00 2001 From: Jan Date: Wed, 9 Jul 2025 23:10:40 +0100 Subject: [PATCH 2/5] refactor: use templated xmodel loading and dumping code for iw4 --- src/Common/Game/IW4/IW4_Assets.h | 36 +- src/ObjCommon/XModel/JsonXModel.h.template | 10 +- .../XModel/XModelConstants.h.template | 8 +- src/ObjLoading/Game/IW4/ObjLoaderIW4.cpp | 3 +- .../XModel/LoaderXModel.cpp.template | 20 +- src/ObjLoading/XModel/LoaderXModel.h.template | 2 +- .../AssetDumpers/AssetDumperPhysCollmap.cpp | 18 +- .../IW4/AssetDumpers/AssetDumperXModel.cpp | 519 +----------------- .../XModel/XModelDumper.cpp.template | 13 +- src/ObjWriting/XModel/XModelDumper.h.template | 2 +- src/ZoneCode/Game/IW4/XAssets/XModel.txt | 2 +- 11 files changed, 70 insertions(+), 563 deletions(-) diff --git a/src/Common/Game/IW4/IW4_Assets.h b/src/Common/Game/IW4/IW4_Assets.h index 09431961..3be9822a 100644 --- a/src/Common/Game/IW4/IW4_Assets.h +++ b/src/Common/Game/IW4/IW4_Assets.h @@ -113,6 +113,8 @@ namespace IW4 struct VehicleDef; struct AddonMapEnts; + typedef unsigned short ScriptString; + union XAssetHeader { PhysPreset* physPreset; @@ -239,8 +241,8 @@ namespace IW4 struct Bounds { - float midPoint[3]; - float halfSize[3]; + vec3_t midPoint; + vec3_t halfSize; }; struct cplane_s @@ -325,7 +327,7 @@ namespace IW4 struct XAnimNotifyInfo { - uint16_t name; + ScriptString name; float time; }; @@ -448,7 +450,7 @@ namespace IW4 unsigned int indexCount; float framerate; float frequency; - uint16_t* names; + ScriptString* names; char* dataByte; int16_t* dataShort; int* dataInt; @@ -491,7 +493,7 @@ namespace IW4 struct type_align(16) GfxPackedVertex { - float xyz[3]; + vec3_t xyz; float binormalSign; GfxColor color; PackedTexCoords texCoord; @@ -536,7 +538,12 @@ namespace IW4 XSurfaceCollisionTree* collisionTree; }; - typedef tdef_align32(16) uint16_t r_index16_t; + struct XSurfaceTri + { + uint16_t i[3]; + }; + + typedef tdef_align32(16) XSurfaceTri XSurfaceTri16; struct XSurface { @@ -547,7 +554,7 @@ namespace IW4 char zoneHandle; uint16_t baseTriIndex; uint16_t baseVertIndex; - r_index16_t (*triIndices)[3]; + XSurfaceTri16* triIndices; XSurfaceVertexInfo vertInfo; GfxPackedVertex* verts0; unsigned int vertListCount; @@ -602,11 +609,16 @@ namespace IW4 struct DObjAnimMat { - float quat[4]; - float trans[3]; + vec4_t quat; + vec3_t trans; float transWeight; }; + struct XModelQuat + { + int16_t v[4]; + }; + struct XModel { const char* name; @@ -616,10 +628,10 @@ namespace IW4 char lodRampType; float scale; unsigned int noScalePartBits[6]; - uint16_t* boneNames; + ScriptString* boneNames; unsigned char* parentList; - int16_t (*quats)[4]; - float (*trans)[3]; + XModelQuat* quats; + float* trans; unsigned char* partClassification; DObjAnimMat* baseMat; Material** materialHandles; diff --git a/src/ObjCommon/XModel/JsonXModel.h.template b/src/ObjCommon/XModel/JsonXModel.h.template index 315960e0..143d4e54 100644 --- a/src/ObjCommon/XModel/JsonXModel.h.template +++ b/src/ObjCommon/XModel/JsonXModel.h.template @@ -1,10 +1,12 @@ -#options GAME (IW5, T5, T6) +#options GAME (IW4, IW5, T5, T6) #filename "Game/" + GAME + "/XModel/JsonXModel" + GAME + ".h" #set GAME_HEADER "\"Game/" + GAME + "/" + GAME + ".h\"" -#if GAME == "IW5" +#if GAME == "IW4" +#define FEATURE_IW4 +#elif GAME == "IW5" #define FEATURE_IW5 #elif GAME == "T5" #define FEATURE_T5 @@ -40,7 +42,7 @@ namespace GAME std::vector lods; std::optional collLod; std::optional physPreset; -#if defined(FEATURE_IW5) +#if defined(FEATURE_IW4) || defined(FEATURE_IW5) std::optional physCollmap; #elif defined(FEATURE_T5) || defined(FEATURE_T6) std::optional physConstraints; @@ -57,7 +59,7 @@ namespace GAME lods, collLod, physPreset, -#if defined(FEATURE_IW5) +#if defined(FEATURE_IW4) || defined(FEATURE_IW5) physCollmap, #elif defined(FEATURE_T5) || defined(FEATURE_T6) physConstraints, diff --git a/src/ObjCommon/XModel/XModelConstants.h.template b/src/ObjCommon/XModel/XModelConstants.h.template index 6f4b5bfe..fdf67629 100644 --- a/src/ObjCommon/XModel/XModelConstants.h.template +++ b/src/ObjCommon/XModel/XModelConstants.h.template @@ -1,10 +1,12 @@ -#options GAME (IW5, T5, T6) +#options GAME (IW4, IW5, T5, T6) #filename "Game/" + GAME + "/XModel/XModelConstants" + GAME + ".h" #set GAME_HEADER "\"Game/" + GAME + "/" + GAME + ".h\"" -#if GAME == "IW5" +#if GAME == "IW4" +#define FEATURE_IW4 +#elif GAME == "IW5" #define FEATURE_IW5 #elif GAME == "T5" #define FEATURE_T5 @@ -45,7 +47,7 @@ namespace GAME "right_foot", "left_foot", "gun", -#if defined(FEATURE_IW5) || defined(FEATURE_T6) +#if defined(FEATURE_IW4) || defined(FEATURE_IW5) || defined(FEATURE_T6) "shield", #endif }; diff --git a/src/ObjLoading/Game/IW4/ObjLoaderIW4.cpp b/src/ObjLoading/Game/IW4/ObjLoaderIW4.cpp index 021cc4b3..f90d1afd 100644 --- a/src/ObjLoading/Game/IW4/ObjLoaderIW4.cpp +++ b/src/ObjLoading/Game/IW4/ObjLoaderIW4.cpp @@ -3,6 +3,7 @@ #include "Asset/GlobalAssetPoolsLoader.h" #include "Game/IW4/GameIW4.h" #include "Game/IW4/IW4.h" +#include "Game/IW4/XModel/LoaderXModelIW4.h" #include "Leaderboard/LoaderLeaderboardIW4.h" #include "LightDef/LoaderLightDefIW4.h" #include "Localize/LoaderLocalizeIW4.h" @@ -123,7 +124,7 @@ namespace // collection.AddAssetCreator(std::make_unique(memory)); // collection.AddAssetCreator(std::make_unique(memory)); // collection.AddAssetCreator(std::make_unique(memory)); - // collection.AddAssetCreator(std::make_unique(memory)); + collection.AddAssetCreator(CreateXModelLoader(memory, searchPath, zone)); collection.AddAssetCreator(CreateMaterialLoader(memory, searchPath)); collection.AddAssetCreator(CreatePixelShaderLoader(memory, searchPath)); collection.AddAssetCreator(CreateVertexShaderLoader(memory, searchPath)); diff --git a/src/ObjLoading/XModel/LoaderXModel.cpp.template b/src/ObjLoading/XModel/LoaderXModel.cpp.template index ff4bf151..3f9bee32 100644 --- a/src/ObjLoading/XModel/LoaderXModel.cpp.template +++ b/src/ObjLoading/XModel/LoaderXModel.cpp.template @@ -1,4 +1,4 @@ -#options GAME(IW5, T5, T6) +#options GAME(IW4, IW5, T5, T6) #filename "Game/" + GAME + "/XModel/LoaderXModel" + GAME + ".cpp" @@ -7,7 +7,9 @@ #set CONSTANTS_HEADER "\"Game/" + GAME + "/XModel/XModelConstants" + GAME + ".h\"" #set JSON_HEADER "\"Game/" + GAME + "/XModel/JsonXModel" + GAME + ".h\"" -#if GAME == "IW5" +#if GAME == "IW4" +#define FEATURE_IW4 +#elif GAME == "IW5" #define FEATURE_IW5 #elif GAME == "T5" #define FEATURE_T5 @@ -191,7 +193,7 @@ namespace if (common.m_bone_weight_data.weights.empty()) return; -#ifdef FEATURE_IW5 +#if defined(FEATURE_IW4) || defined(FEATURE_IW5) vec3_t minCoordinate, maxCoordinate; auto& offset = info.bounds.midPoint; #else @@ -236,7 +238,7 @@ namespace const Eigen::Vector3f maxEigen(maxCoordinate.x, maxCoordinate.y, maxCoordinate.z); const Eigen::Vector3f boundsCenter = (minEigen + maxEigen) * 0.5f; const Eigen::Vector3f halfSizeEigen = maxEigen - boundsCenter; -#ifdef FEATURE_IW5 +#if defined(FEATURE_IW4) || defined(FEATURE_IW5) info.bounds.halfSize.x = halfSizeEigen.x(); info.bounds.halfSize.y = halfSizeEigen.y(); @@ -727,7 +729,7 @@ namespace lodInfo.partBits[i] |= surface.partBits[i]; } -#ifdef FEATURE_IW5 +#if defined(FEATURE_IW4) || defined(FEATURE_IW5) auto* modelSurfs = m_memory.Alloc(); const auto modelSurfsName = std::format("{}_lod{}", xmodel.name, lodNumber); modelSurfs->name = m_memory.Dup(modelSurfsName.c_str()); @@ -752,7 +754,7 @@ namespace static void CalculateModelBounds(XModel& xmodel) { -#ifdef FEATURE_IW5 +#if defined(FEATURE_IW4) || defined(FEATURE_IW5) if (!xmodel.lodInfo[0].modelSurfs || !xmodel.lodInfo[0].modelSurfs->surfs) return; @@ -768,7 +770,7 @@ namespace for (auto surfaceIndex = 0u; surfaceIndex < xmodel.lodInfo[0].numsurfs; surfaceIndex++) { -#ifdef FEATURE_IW5 +#if defined(FEATURE_IW4) || defined(FEATURE_IW5) const auto& surface = surfs[surfaceIndex]; #else const auto& surface = xmodel.surfs[surfaceIndex + xmodel.lodInfo[0].surfIndex]; @@ -790,7 +792,7 @@ namespace } } -#ifdef FEATURE_IW5 +#if defined(FEATURE_IW4) || defined(FEATURE_IW5) const Eigen::Vector3f minEigen(minCoordinate.x, minCoordinate.y, minCoordinate.z); const Eigen::Vector3f maxEigen(maxCoordinate.x, maxCoordinate.y, maxCoordinate.z); const Eigen::Vector3f boundsCenter = (minEigen + maxEigen) * 0.5f; @@ -876,7 +878,7 @@ namespace xmodel.physPreset = nullptr; } -#if defined(FEATURE_IW5) +#if defined(FEATURE_IW4) || defined(FEATURE_IW5) if (jXModel.physCollmap) { auto* physCollmap = context.LoadDependency(jXModel.physCollmap.value()); diff --git a/src/ObjLoading/XModel/LoaderXModel.h.template b/src/ObjLoading/XModel/LoaderXModel.h.template index 16075cb2..cc8dc952 100644 --- a/src/ObjLoading/XModel/LoaderXModel.h.template +++ b/src/ObjLoading/XModel/LoaderXModel.h.template @@ -1,4 +1,4 @@ -#options GAME (IW5, T5, T6) +#options GAME (IW4, IW5, T5, T6) #filename "Game/" + GAME + "/XModel/LoaderXModel" + GAME + ".h" diff --git a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperPhysCollmap.cpp b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperPhysCollmap.cpp index f286a61e..7055e8aa 100644 --- a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperPhysCollmap.cpp +++ b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperPhysCollmap.cpp @@ -52,20 +52,20 @@ void AssetDumperPhysCollmap::DumpAsset(AssetDumpingContext& context, XAssetInfo< break; case PHYS_GEOM_BOX: mapFileDumper.WritePhysicsBox({ - {geom.bounds.midPoint[0], geom.bounds.midPoint[1], geom.bounds.midPoint[2]}, - {geom.bounds.halfSize[0], geom.bounds.halfSize[1], geom.bounds.halfSize[2]}, - {geom.orientation[0][0], geom.orientation[0][1], geom.orientation[0][2] }, - {geom.orientation[1][0], geom.orientation[1][1], geom.orientation[1][2] }, - {geom.orientation[2][0], geom.orientation[2][1], geom.orientation[2][2] } + {geom.bounds.midPoint.v[0], geom.bounds.midPoint.v[1], geom.bounds.midPoint.v[2]}, + {geom.bounds.halfSize.v[0], geom.bounds.halfSize.v[1], geom.bounds.halfSize.v[2]}, + {geom.orientation[0][0], geom.orientation[0][1], geom.orientation[0][2] }, + {geom.orientation[1][0], geom.orientation[1][1], geom.orientation[1][2] }, + {geom.orientation[2][0], geom.orientation[2][1], geom.orientation[2][2] } }); break; case PHYS_GEOM_CYLINDER: mapFileDumper.WritePhysicsCylinder({ - {geom.bounds.midPoint[0], geom.bounds.midPoint[1], geom.bounds.midPoint[2]}, - geom.bounds.halfSize[0], - geom.bounds.halfSize[2] * 2, - {geom.orientation[0][0], geom.orientation[0][1], geom.orientation[0][2] } + {geom.bounds.midPoint.v[0], geom.bounds.midPoint.v[1], geom.bounds.midPoint.v[2]}, + geom.bounds.halfSize.v[0], + geom.bounds.halfSize.v[2] * 2, + {geom.orientation[0][0], geom.orientation[0][1], geom.orientation[0][2] } }); break; diff --git a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperXModel.cpp b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperXModel.cpp index efb8dfd3..a82ddbd8 100644 --- a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperXModel.cpp +++ b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperXModel.cpp @@ -1,524 +1,9 @@ #include "AssetDumperXModel.h" -#include "Game/IW4/CommonIW4.h" -#include "ObjWriting.h" -#include "Utils/DistinctMapper.h" -#include "Utils/HalfFloat.h" -#include "Utils/QuatInt16.h" -#include "XModel/Export/XModelBinWriter.h" -#include "XModel/Export/XModelExportWriter.h" -#include "XModel/Gltf/GltfBinOutput.h" -#include "XModel/Gltf/GltfTextOutput.h" -#include "XModel/Gltf/GltfWriter.h" -#include "XModel/Obj/ObjWriter.h" -#include "XModel/XModelWriter.h" - -#include -#include +#include "Game/IW4/XModel/XModelDumperIW4.h" using namespace IW4; -namespace -{ - GfxImage* GetMaterialColorMap(const Material* material) - { - std::vector potentialTextureDefs; - - for (auto textureIndex = 0u; textureIndex < material->textureCount; textureIndex++) - { - MaterialTextureDef* def = &material->textureTable[textureIndex]; - - if (def->semantic == TS_COLOR_MAP) - potentialTextureDefs.push_back(def); - } - - if (potentialTextureDefs.empty()) - return nullptr; - if (potentialTextureDefs.size() == 1) - return potentialTextureDefs[0]->u.image; - - for (const auto* def : potentialTextureDefs) - { - if (def->nameStart == 'c' && def->nameEnd == 'p') - return def->u.image; - } - - return potentialTextureDefs[0]->u.image; - } - - GfxImage* GetMaterialNormalMap(const Material* material) - { - std::vector potentialTextureDefs; - - for (auto textureIndex = 0u; textureIndex < material->textureCount; textureIndex++) - { - MaterialTextureDef* def = &material->textureTable[textureIndex]; - - if (def->semantic == TS_NORMAL_MAP) - potentialTextureDefs.push_back(def); - } - - if (potentialTextureDefs.empty()) - return nullptr; - if (potentialTextureDefs.size() == 1) - return potentialTextureDefs[0]->u.image; - - for (const auto* def : potentialTextureDefs) - { - if (def->nameStart == 'n' && def->nameEnd == 'p') - return def->u.image; - } - - return potentialTextureDefs[0]->u.image; - } - - GfxImage* GetMaterialSpecularMap(const Material* material) - { - std::vector potentialTextureDefs; - - for (auto textureIndex = 0u; textureIndex < material->textureCount; textureIndex++) - { - MaterialTextureDef* def = &material->textureTable[textureIndex]; - - if (def->semantic == TS_SPECULAR_MAP) - potentialTextureDefs.push_back(def); - } - - if (potentialTextureDefs.empty()) - return nullptr; - if (potentialTextureDefs.size() == 1) - return potentialTextureDefs[0]->u.image; - - for (const auto* def : potentialTextureDefs) - { - if (def->nameStart == 's' && def->nameEnd == 'p') - return def->u.image; - } - - return potentialTextureDefs[0]->u.image; - } - - void AddXModelBones(XModelCommon& out, const AssetDumpingContext& context, 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 = boneNum - static_cast(model->parentList[boneNum - model->numRootBones]); - else - bone.parentIndex = std::nullopt; - - 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 = { - .x = model->baseMat[boneNum].quat[0], - .y = model->baseMat[boneNum].quat[1], - .z = model->baseMat[boneNum].quat[2], - .w = model->baseMat[boneNum].quat[3], - }; - - if (boneNum < model->numRootBones) - { - bone.localOffset[0] = 0; - bone.localOffset[1] = 0; - bone.localOffset[2] = 0; - bone.localRotation = {.x = 0, .y = 0, .z = 0, .w = 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 = { - .x = QuatInt16::ToFloat(model->quats[boneNum - model->numRootBones][0]), - .y = QuatInt16::ToFloat(model->quats[boneNum - model->numRootBones][1]), - .z = QuatInt16::ToFloat(model->quats[boneNum - model->numRootBones][2]), - .w = QuatInt16::ToFloat(model->quats[boneNum - model->numRootBones][3]), - }; - } - - out.m_bones.emplace_back(std::move(bone)); - } - } - - const char* AssetName(const char* input) - { - if (input && input[0] == ',') - return &input[1]; - - return input; - } - - void AddXModelMaterials(XModelCommon& out, DistinctMapper& materialMapper, const XModel* model) - { - for (auto surfaceMaterialNum = 0u; surfaceMaterialNum < model->numsurfs; surfaceMaterialNum++) - { - Material* material = model->materialHandles[surfaceMaterialNum]; - if (materialMapper.Add(material)) - { - XModelMaterial xMaterial; - xMaterial.ApplyDefaults(); - - xMaterial.name = AssetName(material->info.name); - const auto* colorMap = GetMaterialColorMap(material); - if (colorMap) - xMaterial.colorMapName = AssetName(colorMap->name); - - const auto* normalMap = GetMaterialNormalMap(material); - if (normalMap) - xMaterial.normalMapName = AssetName(normalMap->name); - - const auto* specularMap = GetMaterialSpecularMap(material); - if (specularMap) - xMaterial.specularMapName = AssetName(specularMap->name); - - out.m_materials.emplace_back(std::move(xMaterial)); - } - } - } - - void AddXModelObjects(XModelCommon& out, const XModelSurfs* modelSurfs, const DistinctMapper& materialMapper, const int baseSurfaceIndex) - { - for (auto surfIndex = 0u; surfIndex < modelSurfs->numsurfs; surfIndex++) - { - XModelObject object; - object.name = std::format("surf{}", surfIndex); - object.materialIndex = static_cast(materialMapper.GetDistinctPositionByInputPosition(surfIndex + baseSurfaceIndex)); - - out.m_objects.emplace_back(std::move(object)); - } - } - - void AddXModelVertices(XModelCommon& out, const XModelSurfs* modelSurfs) - { - 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]; - - XModelVertex vertex{}; - vertex.coordinates[0] = v.xyz[0]; - vertex.coordinates[1] = v.xyz[1]; - vertex.coordinates[2] = v.xyz[2]; - Common::Vec3UnpackUnitVec(v.normal, vertex.normal); - Common::Vec4UnpackGfxColor(v.color, vertex.color); - Common::Vec2UnpackTexCoords(v.texCoord, vertex.uv); - - out.m_vertices.emplace_back(vertex); - } - } - } - - void AllocateXModelBoneWeights(const XModelSurfs* modelSurfs, XModelVertexBoneWeightCollection& weightCollection) - { - auto totalWeightCount = 0u; - for (auto surfIndex = 0u; surfIndex < modelSurfs->numsurfs; surfIndex++) - { - const auto& surface = modelSurfs->surfs[surfIndex]; - - if (surface.vertList) - { - totalWeightCount += surface.vertListCount; - } - - if (surface.vertInfo.vertsBlend) - { - totalWeightCount += surface.vertInfo.vertCount[0] * 1; - totalWeightCount += surface.vertInfo.vertCount[1] * 2; - totalWeightCount += surface.vertInfo.vertCount[2] * 3; - totalWeightCount += surface.vertInfo.vertCount[3] * 4; - } - } - - weightCollection.weights.resize(totalWeightCount); - } - - float BoneWeight16(const uint16_t value) - { - return static_cast(value) / static_cast(std::numeric_limits::max()); - } - - void AddXModelVertexBoneWeights(XModelCommon& out, const XModelSurfs* modelSurfs) - { - auto& weightCollection = out.m_bone_weight_data; - auto 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 = weightOffset; - - weightCollection.weights[weightOffset++] = - XModelBoneWeight{.boneIndex = static_cast(vertList.boneOffset / sizeof(DObjSkelMat)), .weight = 1.0f}; - - for (auto vertListVertexOffset = 0u; vertListVertexOffset < vertList.vertCount; vertListVertexOffset++) - { - out.m_vertex_bone_weights.emplace_back(boneWeightOffset, 1u); - } - 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 = weightOffset; - const unsigned boneIndex0 = surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat); - weightCollection.weights[weightOffset++] = XModelBoneWeight{.boneIndex = boneIndex0, .weight = 1.0f}; - - vertsBlendOffset += 1; - - out.m_vertex_bone_weights.emplace_back(boneWeightOffset, 1); - } - - // 2 bone weights - for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[1]; vertIndex++) - { - const auto boneWeightOffset = weightOffset; - const unsigned boneIndex0 = surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat); - const unsigned boneIndex1 = surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat); - const auto boneWeight1 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 2]); - const auto boneWeight0 = 1.0f - boneWeight1; - - weightCollection.weights[weightOffset++] = XModelBoneWeight{.boneIndex = boneIndex0, .weight = boneWeight0}; - weightCollection.weights[weightOffset++] = XModelBoneWeight{.boneIndex = boneIndex1, .weight = boneWeight1}; - - vertsBlendOffset += 3; - - out.m_vertex_bone_weights.emplace_back(boneWeightOffset, 2); - } - - // 3 bone weights - for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[2]; vertIndex++) - { - const auto boneWeightOffset = weightOffset; - const unsigned boneIndex0 = surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat); - const unsigned boneIndex1 = surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat); - const auto boneWeight1 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 2]); - const unsigned boneIndex2 = surface.vertInfo.vertsBlend[vertsBlendOffset + 3] / sizeof(DObjSkelMat); - const auto boneWeight2 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 4]); - const auto boneWeight0 = 1.0f - boneWeight1 - boneWeight2; - - weightCollection.weights[weightOffset++] = XModelBoneWeight{.boneIndex = boneIndex0, .weight = boneWeight0}; - weightCollection.weights[weightOffset++] = XModelBoneWeight{.boneIndex = boneIndex1, .weight = boneWeight1}; - weightCollection.weights[weightOffset++] = XModelBoneWeight{.boneIndex = boneIndex2, .weight = boneWeight2}; - - vertsBlendOffset += 5; - - out.m_vertex_bone_weights.emplace_back(boneWeightOffset, 3); - } - - // 4 bone weights - for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[3]; vertIndex++) - { - const auto boneWeightOffset = weightOffset; - const unsigned boneIndex0 = surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat); - const unsigned boneIndex1 = surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat); - const auto boneWeight1 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 2]); - const unsigned boneIndex2 = surface.vertInfo.vertsBlend[vertsBlendOffset + 3] / sizeof(DObjSkelMat); - const auto boneWeight2 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 4]); - const unsigned 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; - - weightCollection.weights[weightOffset++] = XModelBoneWeight{.boneIndex = boneIndex0, .weight = boneWeight0}; - weightCollection.weights[weightOffset++] = XModelBoneWeight{.boneIndex = boneIndex1, .weight = boneWeight1}; - weightCollection.weights[weightOffset++] = XModelBoneWeight{.boneIndex = boneIndex2, .weight = boneWeight2}; - weightCollection.weights[weightOffset++] = XModelBoneWeight{.boneIndex = boneIndex3, .weight = boneWeight3}; - - vertsBlendOffset += 7; - - out.m_vertex_bone_weights.emplace_back(boneWeightOffset, 4); - } - - handledVertices += - surface.vertInfo.vertCount[0] + surface.vertInfo.vertCount[1] + surface.vertInfo.vertCount[2] + surface.vertInfo.vertCount[3]; - } - - for (; handledVertices < surface.vertCount; handledVertices++) - { - out.m_vertex_bone_weights.emplace_back(0, 0); - } - } - } - - void AddXModelFaces(XModelCommon& out, const XModelSurfs* modelSurfs) - { - for (auto surfIndex = 0u; surfIndex < modelSurfs->numsurfs; surfIndex++) - { - const auto& surface = modelSurfs->surfs[surfIndex]; - auto& object = out.m_objects[surfIndex]; - object.m_faces.reserve(surface.triCount); - - 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; - object.m_faces.emplace_back(face); - } - } - } - - void PopulateXModelWriter(XModelCommon& out, const AssetDumpingContext& context, const unsigned lod, const XModel* model) - { - const auto* modelSurfs = model->lodInfo[lod].modelSurfs; - - DistinctMapper materialMapper(model->numsurfs); - AllocateXModelBoneWeights(modelSurfs, out.m_bone_weight_data); - - out.m_name = modelSurfs->name; - AddXModelBones(out, context, model); - AddXModelMaterials(out, materialMapper, model); - AddXModelObjects(out, modelSurfs, materialMapper, model->lodInfo[lod].surfIndex); - AddXModelVertices(out, modelSurfs); - AddXModelVertexBoneWeights(out, modelSurfs); - AddXModelFaces(out, modelSurfs); - } - - void DumpObjMtl(const XModelCommon& common, const AssetDumpingContext& context, const XAssetInfo* asset) - { - const auto* model = asset->Asset(); - const auto mtlFile = context.OpenAssetFile(std::format("model_export/{}.mtl", model->name)); - - if (!mtlFile) - return; - - const auto writer = obj::CreateMtlWriter(*mtlFile, context.m_zone.m_game->GetShortName(), context.m_zone.m_name); - DistinctMapper materialMapper(model->numsurfs); - - writer->Write(common); - } - - void DumpObjLod(const XModelCommon& common, const AssetDumpingContext& context, const XAssetInfo* asset, const unsigned lod) - { - const auto* model = asset->Asset(); - const auto* modelSurfs = model->lodInfo[lod].modelSurfs; - - if (modelSurfs->name[0] == ',' || modelSurfs->surfs == nullptr) - return; - - const auto assetFile = context.OpenAssetFile(std::format("model_export/{}.obj", modelSurfs->name)); - - if (!assetFile) - return; - - const auto writer = obj::CreateObjWriter(*assetFile, std::format("{}.mtl", model->name), context.m_zone.m_game->GetShortName(), context.m_zone.m_name); - DistinctMapper materialMapper(model->numsurfs); - - writer->Write(common); - } - - void DumpXModelExportLod(const XModelCommon& common, const AssetDumpingContext& context, const XAssetInfo* asset, const unsigned lod) - { - const auto* model = asset->Asset(); - const auto* modelSurfs = model->lodInfo[lod].modelSurfs; - const auto assetFile = context.OpenAssetFile(std::format("model_export/{}.XMODEL_EXPORT", modelSurfs->name)); - - if (!assetFile) - return; - - const auto writer = xmodel_export::CreateWriterForVersion6(*assetFile, context.m_zone.m_game->GetShortName(), context.m_zone.m_name); - writer->Write(common); - } - - void DumpXModelBinLod(const XModelCommon& common, const AssetDumpingContext& context, const XAssetInfo* asset, const unsigned lod) - { - const auto* model = asset->Asset(); - const auto* modelSurfs = model->lodInfo[lod].modelSurfs; - const auto assetFile = context.OpenAssetFile(std::format("model_export/{}.xmodel_bin", modelSurfs->name)); - - if (!assetFile) - return; - - const auto writer = xmodel_bin::CreateWriterForVersion7(*assetFile, context.m_zone.m_game->GetShortName(), context.m_zone.m_name); - writer->Write(common); - } - - template - void DumpGltfLod( - const XModelCommon& common, const AssetDumpingContext& context, const XAssetInfo* asset, const unsigned lod, const std::string& extension) - { - const auto* model = asset->Asset(); - const auto* modelSurfs = model->lodInfo[lod].modelSurfs; - const auto assetFile = context.OpenAssetFile(std::format("model_export/{}{}", modelSurfs->name, extension)); - - if (!assetFile) - return; - - const auto output = std::make_unique(*assetFile); - const auto writer = gltf::Writer::CreateWriter(output.get(), context.m_zone.m_game->GetShortName(), context.m_zone.m_name); - - writer->Write(common); - } - - void DumpXModelSurfs(const AssetDumpingContext& context, const XAssetInfo* asset) - { - const auto* model = asset->Asset(); - - for (auto currentLod = 0u; currentLod < model->numLods; currentLod++) - { - XModelCommon common; - PopulateXModelWriter(common, context, currentLod, asset->Asset()); - - switch (ObjWriting::Configuration.ModelOutputFormat) - { - case ObjWriting::Configuration_t::ModelOutputFormat_e::OBJ: - DumpObjLod(common, context, asset, currentLod); - if (currentLod == 0u) - DumpObjMtl(common, context, asset); - break; - - case ObjWriting::Configuration_t::ModelOutputFormat_e::XMODEL_EXPORT: - DumpXModelExportLod(common, context, asset, currentLod); - break; - - case ObjWriting::Configuration_t::ModelOutputFormat_e::XMODEL_BIN: - DumpXModelBinLod(common, context, asset, currentLod); - break; - - case ObjWriting::Configuration_t::ModelOutputFormat_e::GLTF: - DumpGltfLod(common, context, asset, currentLod, ".gltf"); - break; - - case ObjWriting::Configuration_t::ModelOutputFormat_e::GLB: - DumpGltfLod(common, context, asset, currentLod, ".glb"); - break; - - default: - assert(false); - break; - } - } - } -} // namespace - bool AssetDumperXModel::ShouldDump(XAssetInfo* asset) { return !asset->m_name.empty() && asset->m_name[0] != ','; @@ -526,5 +11,5 @@ bool AssetDumperXModel::ShouldDump(XAssetInfo* asset) void AssetDumperXModel::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) { - DumpXModelSurfs(context, asset); + DumpXModel(context, asset); } diff --git a/src/ObjWriting/XModel/XModelDumper.cpp.template b/src/ObjWriting/XModel/XModelDumper.cpp.template index 7759f92a..d3d311af 100644 --- a/src/ObjWriting/XModel/XModelDumper.cpp.template +++ b/src/ObjWriting/XModel/XModelDumper.cpp.template @@ -1,4 +1,4 @@ -#options GAME (IW5, T5, T6) +#options GAME (IW4, IW5, T5, T6) #filename "Game/" + GAME + "/XModel/XModelDumper" + GAME + ".cpp" @@ -6,7 +6,10 @@ #set COMMON_HEADER "\"Game/" + GAME + "/Common" + GAME + ".h\"" #set JSON_HEADER "\"Game/" + GAME + "/XModel/JsonXModel" + GAME + ".h\"" -#if GAME == "IW5" +#if GAME == "IW4" +#define FEATURE_IW4 +#define GAME_LOWER "iw4" +#elif GAME == "IW5" #define FEATURE_IW5 #define GAME_LOWER "iw5" #elif GAME == "T5" @@ -66,7 +69,7 @@ namespace { MaterialTextureDef* def = &material->textureTable[textureIndex]; -#if defined(FEATURE_IW5) +#if defined(FEATURE_IW4) || defined(FEATURE_IW5) if (def->semantic == TS_COLOR_MAP) potentialTextureDefs.push_back(def); #else @@ -155,7 +158,7 @@ namespace bool GetSurfaces(const XModel* model, const unsigned lod, XSurface*& surfs, unsigned& surfCount) { -#if defined(FEATURE_IW5) +#if defined(FEATURE_IW4) || defined(FEATURE_IW5) if (!model->lodInfo[lod].modelSurfs || !model->lodInfo[lod].modelSurfs->surfs) return false; @@ -722,7 +725,7 @@ namespace if (xmodel.physPreset && xmodel.physPreset->name) jXModel.physPreset = AssetName(xmodel.physPreset->name); -#if defined(FEATURE_IW5) +#if defined(FEATURE_IW4) || defined(FEATURE_IW5) if (xmodel.physCollmap && xmodel.physCollmap->name) jXModel.physCollmap = AssetName(xmodel.physCollmap->name); #endif diff --git a/src/ObjWriting/XModel/XModelDumper.h.template b/src/ObjWriting/XModel/XModelDumper.h.template index bcb2d5ab..af07b89e 100644 --- a/src/ObjWriting/XModel/XModelDumper.h.template +++ b/src/ObjWriting/XModel/XModelDumper.h.template @@ -1,4 +1,4 @@ -#options GAME (IW5, T5, T6) +#options GAME (IW4, IW5, T5, T6) #filename "Game/" + GAME + "/XModel/XModelDumper" + GAME + ".h" diff --git a/src/ZoneCode/Game/IW4/XAssets/XModel.txt b/src/ZoneCode/Game/IW4/XAssets/XModel.txt index 6e499781..0870c225 100644 --- a/src/ZoneCode/Game/IW4/XAssets/XModel.txt +++ b/src/ZoneCode/Game/IW4/XAssets/XModel.txt @@ -12,7 +12,7 @@ set count parentList numBones - numRootBones; set reusable quats; set count quats numBones - numRootBones; set reusable trans; -set count trans numBones - numRootBones; +set count trans (numBones - numRootBones) * 3; set reusable partClassification; set count partClassification numBones; set reusable baseMat; From 311d5935b3d459947e3af7b48d9565acab8faaf6 Mon Sep 17 00:00:00 2001 From: Jan Date: Thu, 10 Jul 2025 16:59:23 +0100 Subject: [PATCH 3/5] feat: use templated xmodel loading and dumping code for iw3 --- src/Common/Game/IW3/IW3_Assets.h | 68 ++- src/ObjCommon/XModel/JsonXModel.h.template | 6 +- .../XModel/XModelConstants.h.template | 8 +- .../XModel/LoaderXModel.cpp.template | 6 +- src/ObjLoading/XModel/LoaderXModel.h.template | 2 +- .../IW3/AssetDumpers/AssetDumperXModel.cpp | 529 +----------------- .../XModel/XModelDumper.cpp.template | 9 +- src/ObjWriting/XModel/XModelDumper.h.template | 2 +- src/ZoneCode/Game/IW3/XAssets/XModel.txt | 3 +- 9 files changed, 78 insertions(+), 555 deletions(-) diff --git a/src/Common/Game/IW3/IW3_Assets.h b/src/Common/Game/IW3/IW3_Assets.h index 4a645889..830e3c6f 100644 --- a/src/Common/Game/IW3/IW3_Assets.h +++ b/src/Common/Game/IW3/IW3_Assets.h @@ -97,6 +97,8 @@ namespace IW3 struct RawFile; struct StringTable; + typedef unsigned short ScriptString; + union XAssetHeader { // XModelPieces *xmodelPieces; // NOT AN ASSET @@ -215,7 +217,7 @@ namespace IW3 struct XAnimNotifyInfo { - uint16_t name; + ScriptString name; float time; }; @@ -309,7 +311,7 @@ namespace IW3 unsigned int indexCount; float framerate; float frequency; - uint16_t* names; + ScriptString* names; char* dataByte; int16_t* dataShort; int* dataInt; @@ -329,8 +331,8 @@ namespace IW3 struct DObjAnimMat { - float quat[4]; - float trans[3]; + vec4_t quat; + vec3_t trans; float transWeight; }; @@ -390,7 +392,7 @@ namespace IW3 struct type_align(16) GfxPackedVertex { - float xyz[3]; + vec3_t xyz; float binormalSign; GfxColor color; PackedTexCoords texCoord; @@ -404,7 +406,12 @@ namespace IW3 uint16_t* vertsBlend; }; - typedef tdef_align32(16) uint16_t r_index16_t; + struct XSurfaceTri + { + uint16_t i[3]; + }; + + typedef tdef_align32(16) XSurfaceTri XSurfaceTri16; struct XSurface { @@ -415,7 +422,7 @@ namespace IW3 char zoneHandle; uint16_t baseTriIndex; uint16_t baseVertIndex; - r_index16_t (*triIndices)[3]; + XSurfaceTri16* triIndices; XSurfaceVertexInfo vertInfo; GfxPackedVertex* verts0; unsigned int vertListCount; @@ -455,8 +462,8 @@ namespace IW3 struct XBoneInfo { - float bounds[2][3]; - float offset[3]; + vec3_t bounds[2]; + vec3_t offset; float radiusSquared; }; @@ -520,6 +527,11 @@ namespace IW3 char pad; }; + struct XModelQuat + { + int16_t v[4]; + }; + struct XModel { const char* name; @@ -527,11 +539,11 @@ namespace IW3 unsigned char numRootBones; unsigned char numsurfs; char lodRampType; - uint16_t* boneNames; - char* parentList; - int16_t (*quats)[4]; - float (*trans)[4]; - char* partClassification; + ScriptString* boneNames; + unsigned char* parentList; + XModelQuat* quats; + float* trans; + unsigned char* partClassification; DObjAnimMat* baseMat; XSurface* surfs; Material** materialHandles; @@ -541,8 +553,8 @@ namespace IW3 int contents; XBoneInfo* boneInfo; float radius; - float mins[3]; - float maxs[3]; + vec3_t mins; + vec3_t maxs; uint16_t numLods; uint16_t collLod; XModelStreamInfo streamInfo; @@ -2921,6 +2933,30 @@ namespace IW3 MISSILE_GUIDANCE_COUNT = 0x4, }; + enum hitLocation_t + { + HITLOC_NONE = 0x0, + HITLOC_HELMET = 0x1, + HITLOC_HEAD = 0x2, + HITLOC_NECK = 0x3, + HITLOC_TORSO_UPR = 0x4, + HITLOC_TORSO_LWR = 0x5, + HITLOC_R_ARM_UPR = 0x6, + HITLOC_L_ARM_UPR = 0x7, + HITLOC_R_ARM_LWR = 0x8, + HITLOC_L_ARM_LWR = 0x9, + HITLOC_R_HAND = 0xA, + HITLOC_L_HAND = 0xB, + HITLOC_R_LEG_UPR = 0xC, + HITLOC_L_LEG_UPR = 0xD, + HITLOC_R_LEG_LWR = 0xE, + HITLOC_L_LEG_LWR = 0xF, + HITLOC_R_FOOT = 0x10, + HITLOC_L_FOOT = 0x11, + + HITLOC_COUNT, + }; + struct snd_alias_list_name { const char* soundName; diff --git a/src/ObjCommon/XModel/JsonXModel.h.template b/src/ObjCommon/XModel/JsonXModel.h.template index 143d4e54..224ba42c 100644 --- a/src/ObjCommon/XModel/JsonXModel.h.template +++ b/src/ObjCommon/XModel/JsonXModel.h.template @@ -1,10 +1,12 @@ -#options GAME (IW4, IW5, T5, T6) +#options GAME (IW3, IW4, IW5, T5, T6) #filename "Game/" + GAME + "/XModel/JsonXModel" + GAME + ".h" #set GAME_HEADER "\"Game/" + GAME + "/" + GAME + ".h\"" -#if GAME == "IW4" +#if GAME == "IW3" +#define FEATURE_IW3 +#elif GAME == "IW4" #define FEATURE_IW4 #elif GAME == "IW5" #define FEATURE_IW5 diff --git a/src/ObjCommon/XModel/XModelConstants.h.template b/src/ObjCommon/XModel/XModelConstants.h.template index fdf67629..29807b4f 100644 --- a/src/ObjCommon/XModel/XModelConstants.h.template +++ b/src/ObjCommon/XModel/XModelConstants.h.template @@ -1,10 +1,12 @@ -#options GAME (IW4, IW5, T5, T6) +#options GAME (IW3, IW4, IW5, T5, T6) #filename "Game/" + GAME + "/XModel/XModelConstants" + GAME + ".h" #set GAME_HEADER "\"Game/" + GAME + "/" + GAME + ".h\"" -#if GAME == "IW4" +#if GAME == "IW3" +#define FEATURE_IW3 +#elif GAME == "IW4" #define FEATURE_IW4 #elif GAME == "IW5" #define FEATURE_IW5 @@ -46,9 +48,11 @@ namespace GAME "left_leg_lower", "right_foot", "left_foot", +#if defined(FEATURE_IW4) || defined(FEATURE_IW5) || defined(FEATURE_T5) || defined(FEATURE_T6) "gun", #if defined(FEATURE_IW4) || defined(FEATURE_IW5) || defined(FEATURE_T6) "shield", +#endif #endif }; static_assert(std::extent_v == HITLOC_COUNT); diff --git a/src/ObjLoading/XModel/LoaderXModel.cpp.template b/src/ObjLoading/XModel/LoaderXModel.cpp.template index 3f9bee32..8b9cfe98 100644 --- a/src/ObjLoading/XModel/LoaderXModel.cpp.template +++ b/src/ObjLoading/XModel/LoaderXModel.cpp.template @@ -1,4 +1,4 @@ -#options GAME(IW4, IW5, T5, T6) +#options GAME(IW3, IW4, IW5, T5, T6) #filename "Game/" + GAME + "/XModel/LoaderXModel" + GAME + ".cpp" @@ -7,7 +7,9 @@ #set CONSTANTS_HEADER "\"Game/" + GAME + "/XModel/XModelConstants" + GAME + ".h\"" #set JSON_HEADER "\"Game/" + GAME + "/XModel/JsonXModel" + GAME + ".h\"" -#if GAME == "IW4" +#if GAME == "IW3" +#define FEATURE_IW3 +#elif GAME == "IW4" #define FEATURE_IW4 #elif GAME == "IW5" #define FEATURE_IW5 diff --git a/src/ObjLoading/XModel/LoaderXModel.h.template b/src/ObjLoading/XModel/LoaderXModel.h.template index cc8dc952..4d296a58 100644 --- a/src/ObjLoading/XModel/LoaderXModel.h.template +++ b/src/ObjLoading/XModel/LoaderXModel.h.template @@ -1,4 +1,4 @@ -#options GAME (IW4, IW5, T5, T6) +#options GAME (IW3, IW4, IW5, T5, T6) #filename "Game/" + GAME + "/XModel/LoaderXModel" + GAME + ".h" diff --git a/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperXModel.cpp b/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperXModel.cpp index e95b1d47..1491047c 100644 --- a/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperXModel.cpp +++ b/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperXModel.cpp @@ -1,534 +1,9 @@ #include "AssetDumperXModel.h" -#include "Game/IW3/CommonIW3.h" -#include "ObjWriting.h" -#include "Utils/DistinctMapper.h" -#include "Utils/HalfFloat.h" -#include "Utils/QuatInt16.h" -#include "XModel/Export/XModelBinWriter.h" -#include "XModel/Export/XModelExportWriter.h" -#include "XModel/Gltf/GltfBinOutput.h" -#include "XModel/Gltf/GltfTextOutput.h" -#include "XModel/Gltf/GltfWriter.h" -#include "XModel/Obj/ObjWriter.h" -#include "XModel/XModelWriter.h" - -#include -#include +#include "Game/IW3/XModel/XModelDumperIW3.h" using namespace IW3; -namespace -{ - std::string GetFileNameForLod(const std::string& modelName, const unsigned lod, const std::string& extension) - { - return std::format("model_export/{}_lod{}{}", modelName, lod, extension); - } - - GfxImage* GetMaterialColorMap(const Material* material) - { - std::vector potentialTextureDefs; - - for (auto textureIndex = 0u; textureIndex < material->textureCount; textureIndex++) - { - MaterialTextureDef* def = &material->textureTable[textureIndex]; - - if (def->semantic == TS_COLOR_MAP) - potentialTextureDefs.push_back(def); - } - - if (potentialTextureDefs.empty()) - return nullptr; - if (potentialTextureDefs.size() == 1) - return potentialTextureDefs[0]->u.image; - - for (const auto* def : potentialTextureDefs) - { - if (def->nameStart == 'c' && def->nameEnd == 'p') - return def->u.image; - } - - return potentialTextureDefs[0]->u.image; - } - - GfxImage* GetMaterialNormalMap(const Material* material) - { - std::vector potentialTextureDefs; - - for (auto textureIndex = 0u; textureIndex < material->textureCount; textureIndex++) - { - MaterialTextureDef* def = &material->textureTable[textureIndex]; - - if (def->semantic == TS_NORMAL_MAP) - potentialTextureDefs.push_back(def); - } - - if (potentialTextureDefs.empty()) - return nullptr; - if (potentialTextureDefs.size() == 1) - return potentialTextureDefs[0]->u.image; - - for (const auto* def : potentialTextureDefs) - { - if (def->nameStart == 'n' && def->nameEnd == 'p') - return def->u.image; - } - - return potentialTextureDefs[0]->u.image; - } - - GfxImage* GetMaterialSpecularMap(const Material* material) - { - std::vector potentialTextureDefs; - - for (auto textureIndex = 0u; textureIndex < material->textureCount; textureIndex++) - { - MaterialTextureDef* def = &material->textureTable[textureIndex]; - - if (def->semantic == TS_SPECULAR_MAP) - potentialTextureDefs.push_back(def); - } - - if (potentialTextureDefs.empty()) - return nullptr; - if (potentialTextureDefs.size() == 1) - return potentialTextureDefs[0]->u.image; - - for (const auto* def : potentialTextureDefs) - { - if (def->nameStart == 's' && def->nameEnd == 'p') - return def->u.image; - } - - return potentialTextureDefs[0]->u.image; - } - - void AddXModelBones(XModelCommon& out, const AssetDumpingContext& context, 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 = boneNum - static_cast(model->parentList[boneNum - model->numRootBones]); - else - bone.parentIndex = std::nullopt; - - 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 = { - .x = model->baseMat[boneNum].quat[0], - .y = model->baseMat[boneNum].quat[1], - .z = model->baseMat[boneNum].quat[2], - .w = model->baseMat[boneNum].quat[3], - }; - - if (boneNum < model->numRootBones) - { - bone.localOffset[0] = 0; - bone.localOffset[1] = 0; - bone.localOffset[2] = 0; - bone.localRotation = {.x = 0, .y = 0, .z = 0, .w = 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 = { - .x = QuatInt16::ToFloat(model->quats[boneNum - model->numRootBones][0]), - .y = QuatInt16::ToFloat(model->quats[boneNum - model->numRootBones][1]), - .z = QuatInt16::ToFloat(model->quats[boneNum - model->numRootBones][2]), - .w = QuatInt16::ToFloat(model->quats[boneNum - model->numRootBones][3]), - }; - } - - out.m_bones.emplace_back(std::move(bone)); - } - } - - const char* AssetName(const char* input) - { - if (input && input[0] == ',') - return &input[1]; - - return input; - } - - void AddXModelMaterials(XModelCommon& out, DistinctMapper& materialMapper, const XModel* model) - { - for (auto surfaceMaterialNum = 0u; surfaceMaterialNum < model->numsurfs; surfaceMaterialNum++) - { - Material* material = model->materialHandles[surfaceMaterialNum]; - if (materialMapper.Add(material)) - { - XModelMaterial xMaterial; - xMaterial.ApplyDefaults(); - - xMaterial.name = AssetName(material->info.name); - const auto* colorMap = GetMaterialColorMap(material); - if (colorMap) - xMaterial.colorMapName = AssetName(colorMap->name); - - const auto* normalMap = GetMaterialNormalMap(material); - if (normalMap) - xMaterial.normalMapName = AssetName(normalMap->name); - - const auto* specularMap = GetMaterialSpecularMap(material); - if (specularMap) - xMaterial.specularMapName = AssetName(specularMap->name); - - out.m_materials.emplace_back(std::move(xMaterial)); - } - } - } - - void AddXModelObjects(XModelCommon& out, const XModel* model, const unsigned lod, const DistinctMapper& materialMapper) - { - const auto surfCount = model->lodInfo[lod].numsurfs; - const auto baseSurfaceIndex = model->lodInfo[lod].surfIndex; - - for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++) - { - XModelObject object; - object.name = std::format("surf{}", surfIndex); - object.materialIndex = static_cast(materialMapper.GetDistinctPositionByInputPosition(surfIndex + baseSurfaceIndex)); - - out.m_objects.emplace_back(std::move(object)); - } - } - - void AddXModelVertices(XModelCommon& out, 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]; - - XModelVertex vertex{}; - vertex.coordinates[0] = v.xyz[0]; - vertex.coordinates[1] = v.xyz[1]; - vertex.coordinates[2] = v.xyz[2]; - Common::Vec3UnpackUnitVec(v.normal, vertex.normal); - Common::Vec4UnpackGfxColor(v.color, vertex.color); - Common::Vec2UnpackTexCoords(v.texCoord, vertex.uv); - - out.m_vertices.emplace_back(vertex); - } - } - } - - void 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; - - auto totalWeightCount = 0u; - for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++) - { - const auto& surface = surfs[surfIndex]; - - if (surface.vertList) - { - totalWeightCount += surface.vertListCount; - } - - if (surface.vertInfo.vertsBlend) - { - totalWeightCount += surface.vertInfo.vertCount[0] * 1; - totalWeightCount += surface.vertInfo.vertCount[1] * 2; - totalWeightCount += surface.vertInfo.vertCount[2] * 3; - totalWeightCount += surface.vertInfo.vertCount[3] * 4; - } - } - - weightCollection.weights.resize(totalWeightCount); - } - - float BoneWeight16(const uint16_t value) - { - return static_cast(value) / static_cast(std::numeric_limits::max()); - } - - void AddXModelVertexBoneWeights(XModelCommon& out, const XModel* model, const unsigned lod) - { - const auto* surfs = &model->surfs[model->lodInfo[lod].surfIndex]; - const auto surfCount = model->lodInfo[lod].numsurfs; - auto& weightCollection = out.m_bone_weight_data; - - auto 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 = weightOffset; - - weightCollection.weights[weightOffset++] = - XModelBoneWeight{.boneIndex = static_cast(vertList.boneOffset / sizeof(DObjSkelMat)), .weight = 1.0f}; - - for (auto vertListVertexOffset = 0u; vertListVertexOffset < vertList.vertCount; vertListVertexOffset++) - { - out.m_vertex_bone_weights.emplace_back(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 = weightOffset; - const unsigned boneIndex0 = surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat); - weightCollection.weights[weightOffset++] = XModelBoneWeight{.boneIndex = boneIndex0, .weight = 1.0f}; - - vertsBlendOffset += 1; - - out.m_vertex_bone_weights.emplace_back(boneWeightOffset, 1); - } - - // 2 bone weights - for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[1]; vertIndex++) - { - const auto boneWeightOffset = weightOffset; - const unsigned boneIndex0 = surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat); - const unsigned boneIndex1 = surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat); - const auto boneWeight1 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 2]); - const auto boneWeight0 = 1.0f - boneWeight1; - - weightCollection.weights[weightOffset++] = XModelBoneWeight{.boneIndex = boneIndex0, .weight = boneWeight0}; - weightCollection.weights[weightOffset++] = XModelBoneWeight{.boneIndex = boneIndex1, .weight = boneWeight1}; - - vertsBlendOffset += 3; - - out.m_vertex_bone_weights.emplace_back(boneWeightOffset, 2); - } - - // 3 bone weights - for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[2]; vertIndex++) - { - const auto boneWeightOffset = weightOffset; - const unsigned boneIndex0 = surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat); - const unsigned boneIndex1 = surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat); - const auto boneWeight1 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 2]); - const unsigned boneIndex2 = surface.vertInfo.vertsBlend[vertsBlendOffset + 3] / sizeof(DObjSkelMat); - const auto boneWeight2 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 4]); - const auto boneWeight0 = 1.0f - boneWeight1 - boneWeight2; - - weightCollection.weights[weightOffset++] = XModelBoneWeight{.boneIndex = boneIndex0, .weight = boneWeight0}; - weightCollection.weights[weightOffset++] = XModelBoneWeight{.boneIndex = boneIndex1, .weight = boneWeight1}; - weightCollection.weights[weightOffset++] = XModelBoneWeight{.boneIndex = boneIndex2, .weight = boneWeight2}; - - vertsBlendOffset += 5; - - out.m_vertex_bone_weights.emplace_back(boneWeightOffset, 3); - } - - // 4 bone weights - for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[3]; vertIndex++) - { - const auto boneWeightOffset = weightOffset; - const unsigned boneIndex0 = surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat); - const unsigned boneIndex1 = surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat); - const auto boneWeight1 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 2]); - const unsigned boneIndex2 = surface.vertInfo.vertsBlend[vertsBlendOffset + 3] / sizeof(DObjSkelMat); - const auto boneWeight2 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 4]); - const unsigned 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; - - weightCollection.weights[weightOffset++] = XModelBoneWeight{.boneIndex = boneIndex0, .weight = boneWeight0}; - weightCollection.weights[weightOffset++] = XModelBoneWeight{.boneIndex = boneIndex1, .weight = boneWeight1}; - weightCollection.weights[weightOffset++] = XModelBoneWeight{.boneIndex = boneIndex2, .weight = boneWeight2}; - weightCollection.weights[weightOffset++] = XModelBoneWeight{.boneIndex = boneIndex3, .weight = boneWeight3}; - - vertsBlendOffset += 7; - - out.m_vertex_bone_weights.emplace_back(boneWeightOffset, 4); - } - - handledVertices += - surface.vertInfo.vertCount[0] + surface.vertInfo.vertCount[1] + surface.vertInfo.vertCount[2] + surface.vertInfo.vertCount[3]; - } - - for (; handledVertices < surface.vertCount; handledVertices++) - { - out.m_vertex_bone_weights.emplace_back(0, 0); - } - } - } - - void AddXModelFaces(XModelCommon& out, 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]; - auto& object = out.m_objects[surfIndex]; - object.m_faces.reserve(surface.triCount); - - 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; - object.m_faces.emplace_back(face); - } - } - } - - void PopulateXModelWriter(XModelCommon& out, const AssetDumpingContext& context, const unsigned lod, const XModel* model) - { - DistinctMapper materialMapper(model->numsurfs); - AllocateXModelBoneWeights(model, lod, out.m_bone_weight_data); - - out.m_name = std::format("{}_lod{}", model->name, lod); - AddXModelBones(out, context, model); - AddXModelMaterials(out, materialMapper, model); - AddXModelObjects(out, model, lod, materialMapper); - AddXModelVertices(out, model, lod); - AddXModelVertexBoneWeights(out, model, lod); - AddXModelFaces(out, model, lod); - } - - void DumpObjMtl(const XModelCommon& common, const AssetDumpingContext& context, const XAssetInfo* asset) - { - const auto* model = asset->Asset(); - const auto mtlFile = context.OpenAssetFile(std::format("model_export/{}.mtl", model->name)); - - if (!mtlFile) - return; - - const auto writer = obj::CreateMtlWriter(*mtlFile, context.m_zone.m_game->GetShortName(), context.m_zone.m_name); - DistinctMapper materialMapper(model->numsurfs); - - writer->Write(common); - } - - void DumpObjLod(const XModelCommon& common, const AssetDumpingContext& context, const XAssetInfo* asset, const unsigned lod) - { - const auto* model = asset->Asset(); - const auto assetFile = context.OpenAssetFile(GetFileNameForLod(model->name, lod, ".obj")); - - if (!assetFile) - return; - - const auto writer = obj::CreateObjWriter(*assetFile, std::format("{}.mtl", model->name), context.m_zone.m_game->GetShortName(), context.m_zone.m_name); - DistinctMapper materialMapper(model->numsurfs); - - writer->Write(common); - } - - void DumpXModelExportLod(const XModelCommon& common, const AssetDumpingContext& context, const XAssetInfo* asset, const unsigned lod) - { - const auto* model = asset->Asset(); - const auto assetFile = context.OpenAssetFile(GetFileNameForLod(model->name, lod, ".XMODEL_EXPORT")); - - if (!assetFile) - return; - - const auto writer = xmodel_export::CreateWriterForVersion6(*assetFile, context.m_zone.m_game->GetShortName(), context.m_zone.m_name); - writer->Write(common); - } - - void DumpXModelBinLod(const XModelCommon& common, const AssetDumpingContext& context, const XAssetInfo* asset, const unsigned lod) - { - const auto* model = asset->Asset(); - const auto assetFile = context.OpenAssetFile(GetFileNameForLod(model->name, lod, ".xmodel_bin")); - - if (!assetFile) - return; - - const auto writer = xmodel_bin::CreateWriterForVersion7(*assetFile, context.m_zone.m_game->GetShortName(), context.m_zone.m_name); - writer->Write(common); - } - - template - void DumpGltfLod( - const XModelCommon& common, const AssetDumpingContext& context, const XAssetInfo* asset, const unsigned lod, const std::string& extension) - { - const auto* model = asset->Asset(); - const auto assetFile = context.OpenAssetFile(GetFileNameForLod(model->name, lod, extension)); - - if (!assetFile) - return; - - const auto output = std::make_unique(*assetFile); - const auto writer = gltf::Writer::CreateWriter(output.get(), context.m_zone.m_game->GetShortName(), context.m_zone.m_name); - - writer->Write(common); - } - - void DumpXModelSurfs(const AssetDumpingContext& context, const XAssetInfo* asset) - { - const auto* model = asset->Asset(); - - for (auto currentLod = 0u; currentLod < model->numLods; currentLod++) - { - XModelCommon common; - PopulateXModelWriter(common, context, currentLod, asset->Asset()); - - switch (ObjWriting::Configuration.ModelOutputFormat) - { - case ObjWriting::Configuration_t::ModelOutputFormat_e::OBJ: - DumpObjLod(common, context, asset, currentLod); - if (currentLod == 0u) - DumpObjMtl(common, context, asset); - break; - - case ObjWriting::Configuration_t::ModelOutputFormat_e::XMODEL_EXPORT: - DumpXModelExportLod(common, context, asset, currentLod); - break; - - case ObjWriting::Configuration_t::ModelOutputFormat_e::XMODEL_BIN: - DumpXModelBinLod(common, context, asset, currentLod); - break; - - case ObjWriting::Configuration_t::ModelOutputFormat_e::GLTF: - DumpGltfLod(common, context, asset, currentLod, ".gltf"); - break; - - case ObjWriting::Configuration_t::ModelOutputFormat_e::GLB: - DumpGltfLod(common, context, asset, currentLod, ".glb"); - break; - - default: - assert(false); - break; - } - } - } -} // namespace - bool AssetDumperXModel::ShouldDump(XAssetInfo* asset) { return !asset->m_name.empty() && asset->m_name[0] != ','; @@ -536,5 +11,5 @@ bool AssetDumperXModel::ShouldDump(XAssetInfo* asset) void AssetDumperXModel::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) { - DumpXModelSurfs(context, asset); + DumpXModel(context, asset); } diff --git a/src/ObjWriting/XModel/XModelDumper.cpp.template b/src/ObjWriting/XModel/XModelDumper.cpp.template index d3d311af..11a35d69 100644 --- a/src/ObjWriting/XModel/XModelDumper.cpp.template +++ b/src/ObjWriting/XModel/XModelDumper.cpp.template @@ -1,4 +1,4 @@ -#options GAME (IW4, IW5, T5, T6) +#options GAME (IW3, IW4, IW5, T5, T6) #filename "Game/" + GAME + "/XModel/XModelDumper" + GAME + ".cpp" @@ -6,7 +6,10 @@ #set COMMON_HEADER "\"Game/" + GAME + "/Common" + GAME + ".h\"" #set JSON_HEADER "\"Game/" + GAME + "/XModel/JsonXModel" + GAME + ".h\"" -#if GAME == "IW4" +#if GAME == "IW3" +#define FEATURE_IW3 +#define GAME_LOWER "iw3" +#elif GAME == "IW4" #define FEATURE_IW4 #define GAME_LOWER "iw4" #elif GAME == "IW5" @@ -69,7 +72,7 @@ namespace { MaterialTextureDef* def = &material->textureTable[textureIndex]; -#if defined(FEATURE_IW4) || defined(FEATURE_IW5) +#if defined(FEATURE_IW3) || defined(FEATURE_IW4) || defined(FEATURE_IW5) if (def->semantic == TS_COLOR_MAP) potentialTextureDefs.push_back(def); #else diff --git a/src/ObjWriting/XModel/XModelDumper.h.template b/src/ObjWriting/XModel/XModelDumper.h.template index af07b89e..5c4cecb2 100644 --- a/src/ObjWriting/XModel/XModelDumper.h.template +++ b/src/ObjWriting/XModel/XModelDumper.h.template @@ -1,4 +1,4 @@ -#options GAME (IW4, IW5, T5, T6) +#options GAME (IW3, IW4, IW5, T5, T6) #filename "Game/" + GAME + "/XModel/XModelDumper" + GAME + ".h" diff --git a/src/ZoneCode/Game/IW3/XAssets/XModel.txt b/src/ZoneCode/Game/IW3/XAssets/XModel.txt index 72538b90..f044e586 100644 --- a/src/ZoneCode/Game/IW3/XAssets/XModel.txt +++ b/src/ZoneCode/Game/IW3/XAssets/XModel.txt @@ -12,7 +12,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 08bcd02b24789c6fac90d27bf45ed1a8abc203de Mon Sep 17 00:00:00 2001 From: Jan Laupetin Date: Thu, 10 Jul 2025 17:57:25 +0100 Subject: [PATCH 4/5] feat: link schema in xmodel jsons --- src/ObjWriting/XModel/XModelDumper.cpp.template | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ObjWriting/XModel/XModelDumper.cpp.template b/src/ObjWriting/XModel/XModelDumper.cpp.template index 11a35d69..5426b73a 100644 --- a/src/ObjWriting/XModel/XModelDumper.cpp.template +++ b/src/ObjWriting/XModel/XModelDumper.cpp.template @@ -675,6 +675,7 @@ namespace CreateJsonXModel(jsonXModel, *xmodel); nlohmann::json jRoot = jsonXModel; + jRoot["$schema"] = "http://openassettools.dev/schema/xmodel.v1.json"; jRoot["_type"] = "xmodel"; jRoot["_version"] = 1; jRoot["_game"] = GAME_LOWER; From 4b5cea31803ca3777296db935e3f5faabee26456 Mon Sep 17 00:00:00 2001 From: Jan Laupetin Date: Thu, 10 Jul 2025 17:57:48 +0100 Subject: [PATCH 5/5] fix: iw3/iw4 xmodels having invalid collLod --- src/Common/Game/IW3/IW3_Assets.h | 2 +- src/Common/Game/IW4/IW4_Assets.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Common/Game/IW3/IW3_Assets.h b/src/Common/Game/IW3/IW3_Assets.h index 830e3c6f..eb25545d 100644 --- a/src/Common/Game/IW3/IW3_Assets.h +++ b/src/Common/Game/IW3/IW3_Assets.h @@ -556,7 +556,7 @@ namespace IW3 vec3_t mins; vec3_t maxs; uint16_t numLods; - uint16_t collLod; + int16_t collLod; XModelStreamInfo streamInfo; int memUsage; char flags; diff --git a/src/Common/Game/IW4/IW4_Assets.h b/src/Common/Game/IW4/IW4_Assets.h index 3be9822a..398f0f77 100644 --- a/src/Common/Game/IW4/IW4_Assets.h +++ b/src/Common/Game/IW4/IW4_Assets.h @@ -638,7 +638,7 @@ namespace IW4 XModelLodInfo lodInfo[4]; char maxLoadedLod; unsigned char numLods; - unsigned char collLod; + char collLod; char flags; XModelCollSurf_s* collSurfs; int numCollSurfs;