feat: T4 add xmodel loader (#989)

* feat: T4 add xmodel loader

* chore: remove unused xmodel related struct from T4

* fix: allocating too little trans for t4

---------

Co-authored-by: Jan Laupetin <[email protected]>
This commit is contained in:
mo
2026-09-04 19:37:24 +02:00
committed by GitHub
co-authored by Jan Laupetin
parent 2569303de4
commit 61fd267b2b
8 changed files with 66 additions and 21 deletions
+1 -1
View File
@@ -167,7 +167,7 @@ using `Linker`):
| PhysConstraints | ✅ | ❌ | | | PhysConstraints | ✅ | ❌ | |
| DestructibleDef | ❌ | ❌ | | | DestructibleDef | ❌ | ❌ | |
| XAnimParts | ✅ | ✅ | | | XAnimParts | ✅ | ✅ | |
| XModel | ✅ | | Model data can be exported to `XMODEL_EXPORT/XMODEL_BIN`, `OBJ`, `GLB/GLTF`. | | XModel | ✅ | | Model data can be exported to `XMODEL_EXPORT/XMODEL_BIN`, `OBJ`, `GLB/GLTF`. |
| Material | ✅ | ✅ | | | Material | ✅ | ✅ | |
| MaterialTechniqueSet | ❌ | ❌ | | | MaterialTechniqueSet | ❌ | ❌ | |
| GfxImage | ✅ | ✅ | A few special image encodings are not yet supported. | | GfxImage | ✅ | ✅ | A few special image encodings are not yet supported. |
+21
View File
@@ -0,0 +1,21 @@
J_Hip_RI,right_leg_upper
J_Hip_LE,left_leg_upper
J_Knee_RI,right_leg_lower
J_SpineUpper,torso_upper
J_Knee_LE,left_leg_lower
J_Ankle_RI,right_foot
J_Ankle_LE,left_foot
J_Clavicle_RI,torso_upper
J_Clavicle_LE,torso_upper
J_Shoulder_RI,right_arm_upper
J_Shoulder_LE,left_arm_upper
J_Neck,neck
J_Head,head
J_Elbow_RI,right_arm_lower
J_Elbow_LE,left_arm_lower
J_Wrist_RI,right_hand
J_Wrist_LE,left_hand
J_MainRoot,torso_lower
TAG_WEAPON_LEFT,gun
TAG_WEAPON_RIGHT,gun
J_Helmet,helmet
1 J_Hip_RI right_leg_upper
2 J_Hip_LE left_leg_upper
3 J_Knee_RI right_leg_lower
4 J_SpineUpper torso_upper
5 J_Knee_LE left_leg_lower
6 J_Ankle_RI right_foot
7 J_Ankle_LE left_foot
8 J_Clavicle_RI torso_upper
9 J_Clavicle_LE torso_upper
10 J_Shoulder_RI right_arm_upper
11 J_Shoulder_LE left_arm_upper
12 J_Neck neck
13 J_Head head
14 J_Elbow_RI right_arm_lower
15 J_Elbow_LE left_arm_lower
16 J_Wrist_RI right_hand
17 J_Wrist_LE left_hand
18 J_MainRoot torso_lower
19 TAG_WEAPON_LEFT gun
20 TAG_WEAPON_RIGHT gun
21 J_Helmet helmet
+19
View File
@@ -0,0 +1,19 @@
J_Hip_RI,right_leg_upper
J_Hip_LE,left_leg_upper
J_Knee_RI,right_leg_lower
J_SpineUpper,torso_lower
J_SpineLower,torso_lower
J_MainRoot,torso_lower
J_Knee_LE,left_leg_lower
J_Ankle_RI,right_foot
J_Ankle_LE,left_foot
J_Clavicle_RI,torso_upper
J_Clavicle_LE,torso_upper
J_Shoulder_RI,right_arm_upper
J_Shoulder_LE,left_arm_upper
J_Neck,neck
J_Head,head
J_Elbow_RI,right_arm_lower
J_Elbow_LE,left_arm_lower
J_Wrist_RI,right_hand
J_Wrist_LE,left_hand
1 J_Hip_RI right_leg_upper
2 J_Hip_LE left_leg_upper
3 J_Knee_RI right_leg_lower
4 J_SpineUpper torso_lower
5 J_SpineLower torso_lower
6 J_MainRoot torso_lower
7 J_Knee_LE left_leg_lower
8 J_Ankle_RI right_foot
9 J_Ankle_LE left_foot
10 J_Clavicle_RI torso_upper
11 J_Clavicle_LE torso_upper
12 J_Shoulder_RI right_arm_upper
13 J_Shoulder_LE left_arm_upper
14 J_Neck neck
15 J_Head head
16 J_Elbow_RI right_arm_lower
17 J_Elbow_LE left_arm_lower
18 J_Wrist_RI right_hand
19 J_Wrist_LE left_hand
+2 -8
View File
@@ -376,8 +376,8 @@ namespace T4
struct XSurfaceCollisionTree struct XSurfaceCollisionTree
{ {
float trans[3]; vec3_t trans;
float scale[3]; vec3_t scale;
unsigned int nodeCount; unsigned int nodeCount;
XSurfaceCollisionNode* nodes; XSurfaceCollisionNode* nodes;
unsigned int leafCount; unsigned int leafCount;
@@ -591,12 +591,6 @@ namespace T4
XMODEL_LOD_RAMP_COUNT XMODEL_LOD_RAMP_COUNT
}; };
struct XModelHighMipBounds
{
float mins[3];
float maxs[3];
};
struct XModelStreamInfo struct XModelStreamInfo
{ {
char pad; char pad;
@@ -1,4 +1,4 @@
#options GAME (IW3, IW4, IW5, T5, T6) #options GAME (IW3, IW4, IW5, T4, T5, T6)
#filename "Game/" + GAME + "/XModel/XModelConstants" + GAME + ".h" #filename "Game/" + GAME + "/XModel/XModelConstants" + GAME + ".h"
@@ -10,6 +10,8 @@
#define FEATURE_IW4 #define FEATURE_IW4
#elif GAME == "IW5" #elif GAME == "IW5"
#define FEATURE_IW5 #define FEATURE_IW5
#elif GAME == "T4"
#define FEATURE_T4
#elif GAME == "T5" #elif GAME == "T5"
#define FEATURE_T5 #define FEATURE_T5
#elif GAME == "T6" #elif GAME == "T6"
+2
View File
@@ -9,6 +9,7 @@
#include "Game/T4/T4.h" #include "Game/T4/T4.h"
#include "Game/T4/Weapon/AccuracyGraphLoaderT4.h" #include "Game/T4/Weapon/AccuracyGraphLoaderT4.h"
#include "Game/T4/XAnim/XAnimLoaderT4.h" #include "Game/T4/XAnim/XAnimLoaderT4.h"
#include "Game/T4/XModel/LoaderXModelT4.h"
#include "Localize/AssetLoaderLocalizeT4.h" #include "Localize/AssetLoaderLocalizeT4.h"
#include "Maps/MapEntsLoaderT4.h" #include "Maps/MapEntsLoaderT4.h"
#include "Material/LoaderMaterialT4.h" #include "Material/LoaderMaterialT4.h"
@@ -101,6 +102,7 @@ namespace
auto& memory = zone.Memory(); auto& memory = zone.Memory();
collection.AddAssetCreator(xanim::CreateLoaderT4(memory, searchPath, zone)); collection.AddAssetCreator(xanim::CreateLoaderT4(memory, searchPath, zone));
collection.AddAssetCreator(xmodel::CreateLoaderT4(memory, searchPath, zone));
collection.AddAssetCreator(image::CreateLoaderEmbeddedT4(memory, searchPath)); collection.AddAssetCreator(image::CreateLoaderEmbeddedT4(memory, searchPath));
collection.AddAssetCreator(image::CreateLoaderExternalT4(memory, searchPath)); collection.AddAssetCreator(image::CreateLoaderExternalT4(memory, searchPath));
collection.AddAssetCreator(material::CreateLoaderT4(memory, searchPath)); collection.AddAssetCreator(material::CreateLoaderT4(memory, searchPath));
+17 -10
View File
@@ -1,4 +1,4 @@
#options GAME(IW3, IW4, IW5, T5, T6) #options GAME(IW3, IW4, IW5, T4, T5, T6)
#filename "Game/" + GAME + "/XModel/LoaderXModel" + GAME + ".cpp" #filename "Game/" + GAME + "/XModel/LoaderXModel" + GAME + ".cpp"
@@ -16,6 +16,9 @@
#elif GAME == "IW5" #elif GAME == "IW5"
#define FEATURE_IW5 #define FEATURE_IW5
#define EVEN_TRI_COUNT #define EVEN_TRI_COUNT
#elif GAME == "T4"
#define FEATURE_T4
#define EVEN_TRI_COUNT
#elif GAME == "T5" #elif GAME == "T5"
#define FEATURE_T5 #define FEATURE_T5
#define EVEN_TRI_COUNT #define EVEN_TRI_COUNT
@@ -375,7 +378,7 @@ namespace
{ {
xmodel.parentList = m_memory.Alloc<unsigned char>(xmodel.numBones - xmodel.numRootBones); xmodel.parentList = m_memory.Alloc<unsigned char>(xmodel.numBones - xmodel.numRootBones);
#if defined(FEATURE_IW3) || defined(FEATURE_T5) || defined(FEATURE_T6) #if defined(FEATURE_IW3) || defined(FEATURE_T4) || defined(FEATURE_T5) || defined(FEATURE_T6)
// For some reason some games allocate for a vec4 here. it is treated as a vec3 though? // For some reason some games allocate for a vec4 here. it is treated as a vec3 though?
xmodel.trans = m_memory.Alloc<float>((xmodel.numBones - xmodel.numRootBones) * 4u); xmodel.trans = m_memory.Alloc<float>((xmodel.numBones - xmodel.numRootBones) * 4u);
#else #else
@@ -429,7 +432,7 @@ namespace
// Viewhands seem to have nulled trans for some reason? // Viewhands seem to have nulled trans for some reason?
if (jXModel.type.value_or(JsonXModelType::RIGID) == JsonXModelType::VIEWHANDS) if (jXModel.type.value_or(JsonXModelType::RIGID) == JsonXModelType::VIEWHANDS)
{ {
#if defined(FEATURE_IW3) || defined(FEATURE_T5) || defined(FEATURE_T6) #if defined(FEATURE_IW3) || defined(FEATURE_T4) || defined(FEATURE_T5) || defined(FEATURE_T6)
memset(xmodel.trans, 0, sizeof(float) * 4 * (xmodel.numBones - xmodel.numRootBones)); memset(xmodel.trans, 0, sizeof(float) * 4 * (xmodel.numBones - xmodel.numRootBones));
#else #else
memset(xmodel.trans, 0, sizeof(float) * 3 * (xmodel.numBones - xmodel.numRootBones)); memset(xmodel.trans, 0, sizeof(float) * 3 * (xmodel.numBones - xmodel.numRootBones));
@@ -859,7 +862,7 @@ namespace
const auto maxWeightCount = common.m_vertex_bone_weights[xmodelToCommonVertexIndexLookup[xmodelToCommonVertexIndexLookup.size() - 1]].weightCount; const auto maxWeightCount = common.m_vertex_bone_weights[xmodelToCommonVertexIndexLookup[xmodelToCommonVertexIndexLookup.size() - 1]].weightCount;
const auto surfaceIsRigid = maxWeightCount <= 1; const auto surfaceIsRigid = maxWeightCount <= 1;
#if defined(FEATURE_IW3) || defined(FEATURE_IW4) #if defined(FEATURE_IW3) || defined(FEATURE_IW4) || defined(FEATURE_T4)
surface.deformed = !surfaceIsRigid; surface.deformed = !surfaceIsRigid;
#else #else
if (!surfaceIsRigid) if (!surfaceIsRigid)
@@ -1114,16 +1117,20 @@ namespace
#endif #endif
} }
#if defined(FEATURE_T5) || defined(FEATURE_T6) #if defined(FEATURE_T4) || defined(FEATURE_T5) || defined(FEATURE_T6)
static bool HasAnySkinnedSurfs(const XModel& xmodel) static bool HasAnySkinnedSurfs(const XModel& xmodel)
{ {
for (auto lodIndex = 0u; lodIndex < xmodel.numLods; lodIndex++) for (auto lodIndex = 0u; lodIndex < xmodel.numLods; lodIndex++)
{ {
const auto& lod = xmodel.lodInfo[lodIndex]; const auto& lod = xmodel.lodInfo[lodIndex];
for (auto surfIndex = lod.surfIndex; surfIndex < lod.numsurfs; surfIndex++) for (auto surfaceOffset = 0u; surfaceOffset < lod.numsurfs; surfaceOffset++)
{ {
const auto& surf = xmodel.surfs[lod.surfIndex + surfIndex]; const auto& surf = xmodel.surfs[lod.surfIndex + surfaceOffset];
#ifdef FEATURE_T4
if (surf.deformed)
#else
if (surf.flags & XSURFACE_FLAG_DEFORMED) if (surf.flags & XSURFACE_FLAG_DEFORMED)
#endif
return true; return true;
} }
} }
@@ -1158,7 +1165,7 @@ namespace
xmodel.numsurfs = static_cast<decltype(XModel::numsurfs)>(m_surfaces.size()); xmodel.numsurfs = static_cast<decltype(XModel::numsurfs)>(m_surfaces.size());
#if defined(FEATURE_IW3) || defined(FEATURE_T5) || defined(FEATURE_T6) #if defined(FEATURE_IW3) || defined(FEATURE_T4) || defined(FEATURE_T5) || defined(FEATURE_T6)
xmodel.surfs = m_memory.Alloc<XSurface>(xmodel.numsurfs); xmodel.surfs = m_memory.Alloc<XSurface>(xmodel.numsurfs);
memcpy(xmodel.surfs, m_surfaces.data(), sizeof(XSurface) * xmodel.numsurfs); memcpy(xmodel.surfs, m_surfaces.data(), sizeof(XSurface) * xmodel.numsurfs);
#endif #endif
@@ -1180,7 +1187,7 @@ namespace
else else
xmodel.collLod = -1; xmodel.collLod = -1;
#if defined(FEATURE_T5) || defined(FEATURE_T6) #if defined(FEATURE_T4) || defined(FEATURE_T5) || defined(FEATURE_T6)
xmodel.lodRampType = HasAnySkinnedSurfs(xmodel) ? XMODEL_LOD_RAMP_SKINNED : XMODEL_LOD_RAMP_RIGID; xmodel.lodRampType = HasAnySkinnedSurfs(xmodel) ? XMODEL_LOD_RAMP_SKINNED : XMODEL_LOD_RAMP_RIGID;
#endif #endif
@@ -1218,7 +1225,7 @@ namespace
} }
#endif #endif
#if defined(FEATURE_T5) || defined(FEATURE_T6) #if defined(FEATURE_T4) || defined(FEATURE_T5) || defined(FEATURE_T6)
if (jXModel.physConstraints) if (jXModel.physConstraints)
{ {
auto* physConstraints = context.LoadDependency<AssetPhysConstraints>(jXModel.physConstraints.value()); auto* physConstraints = context.LoadDependency<AssetPhysConstraints>(jXModel.physConstraints.value());
@@ -1,4 +1,4 @@
#options GAME (IW3, IW4, IW5, T5, T6) #options GAME (IW3, IW4, IW5, T4, T5, T6)
#filename "Game/" + GAME + "/XModel/LoaderXModel" + GAME + ".h" #filename "Game/" + GAME + "/XModel/LoaderXModel" + GAME + ".h"