From 6b0681f27f9104d3907301559e17c0a8d6d9a024 Mon Sep 17 00:00:00 2001 From: Jan Laupetin Date: Fri, 5 Jun 2026 18:44:11 +0200 Subject: [PATCH] feat: dump xanims for T5 --- src/ObjCommon/XAnim/BinaryXAnimCommon.h | 28 +++++++++++++ src/ObjCommon/XAnim/XAnimCommon.cpp | 3 ++ src/ObjCommon/XAnim/XAnimCommon.h | 9 ++--- src/ObjLoading/XAnim/CompiledXAnimLoader.cpp | 1 + src/ObjWriting/Game/T5/ObjWriterT5.cpp | 3 +- src/ObjWriting/XAnim/CompiledXAnimWriter.cpp | 39 ++++++++++++++++--- src/ObjWriting/XAnim/CompiledXAnimWriter.h | 3 +- src/ObjWriting/XAnim/XAnimDumper.cpp.template | 21 +++++++++- src/ObjWriting/XAnim/XAnimDumper.h.template | 2 +- 9 files changed, 92 insertions(+), 17 deletions(-) create mode 100644 src/ObjCommon/XAnim/BinaryXAnimCommon.h diff --git a/src/ObjCommon/XAnim/BinaryXAnimCommon.h b/src/ObjCommon/XAnim/BinaryXAnimCommon.h new file mode 100644 index 00000000..810cfe29 --- /dev/null +++ b/src/ObjCommon/XAnim/BinaryXAnimCommon.h @@ -0,0 +1,28 @@ +#pragma once + +#include + +namespace xanim +{ + enum class CompiledXAnimVersion : uint8_t + { + // IW3, T4 + VERSION_17 = 17, + // T5, T6 + VERSION_19 = 19 + }; + + namespace binary17 + { + constexpr uint8_t FLAG_LOOPED = 1u; + constexpr uint8_t FLAG_DELTA = 2u; + } // namespace binary17 + + namespace binary19 + { + constexpr uint8_t FLAG_LOOPED = 1u; + constexpr uint8_t FLAG_DELTA = 2u; + constexpr uint8_t FLAG_LEFT_HAND_GRIP_IK = 4u; + constexpr uint8_t FLAG_STREAMABLE = 8u; + } // namespace binary19 +} // namespace xanim diff --git a/src/ObjCommon/XAnim/XAnimCommon.cpp b/src/ObjCommon/XAnim/XAnimCommon.cpp index a95e0877..03a3bf80 100644 --- a/src/ObjCommon/XAnim/XAnimCommon.cpp +++ b/src/ObjCommon/XAnim/XAnimCommon.cpp @@ -83,7 +83,10 @@ namespace xanim CommonXAnimParts::CommonXAnimParts() : m_num_frames(0), m_looped(false), + m_left_hand_grip_ik(false), + m_streamable(false), m_frame_rate(0), + m_primed_length(0), m_asset_type(0) { } diff --git a/src/ObjCommon/XAnim/XAnimCommon.h b/src/ObjCommon/XAnim/XAnimCommon.h index c01960fe..9e00b459 100644 --- a/src/ObjCommon/XAnim/XAnimCommon.h +++ b/src/ObjCommon/XAnim/XAnimCommon.h @@ -9,12 +9,6 @@ namespace xanim { - enum class CompiledXAnimVersion : uint8_t - { - // IW3 - VERSION_17 = 17 - }; - enum class QuatType : uint8_t { NO_QUAT = 0, @@ -152,7 +146,10 @@ namespace xanim size_t m_num_frames; bool m_looped; + bool m_left_hand_grip_ik; + bool m_streamable; float m_frame_rate; + float m_primed_length; uint8_t m_asset_type; std::vector m_bone_tracks; std::vector m_notifies; diff --git a/src/ObjLoading/XAnim/CompiledXAnimLoader.cpp b/src/ObjLoading/XAnim/CompiledXAnimLoader.cpp index 81a4986c..822adb79 100644 --- a/src/ObjLoading/XAnim/CompiledXAnimLoader.cpp +++ b/src/ObjLoading/XAnim/CompiledXAnimLoader.cpp @@ -2,6 +2,7 @@ #include "Utils/Alignment.h" #include "Utils/StreamUtils.h" +#include "XAnim/BinaryXAnimCommon.h" #include #include diff --git a/src/ObjWriting/Game/T5/ObjWriterT5.cpp b/src/ObjWriting/Game/T5/ObjWriterT5.cpp index b0d348dc..4a3be588 100644 --- a/src/ObjWriting/Game/T5/ObjWriterT5.cpp +++ b/src/ObjWriting/Game/T5/ObjWriterT5.cpp @@ -3,6 +3,7 @@ #include "Game/T5/Image/ImageDumperT5.h" #include "Game/T5/Material/MaterialJsonDumperT5.h" #include "Game/T5/Techset/TechsetDumperT5.h" +#include "Game/T5/XAnim/XAnimDumperT5.h" #include "Game/T5/XModel/XModelDumperT5.h" #include "LightDef/LightDefDumperT5.h" #include "Localize/LocalizeDumperT5.h" @@ -17,7 +18,7 @@ void ObjWriter::RegisterAssetDumpers(AssetDumpingContext& context) RegisterAssetDumper(std::make_unique()); // REGISTER_DUMPER(AssetDumperPhysConstraints, m_phys_constraints) // REGISTER_DUMPER(AssetDumperDestructibleDef, m_destructible_def) - // REGISTER_DUMPER(AssetDumperXAnimParts, m_xanim_parts) + RegisterAssetDumper(std::make_unique()); RegisterAssetDumper(std::make_unique()); RegisterAssetDumper(std::make_unique()); RegisterAssetDumper(std::make_unique( diff --git a/src/ObjWriting/XAnim/CompiledXAnimWriter.cpp b/src/ObjWriting/XAnim/CompiledXAnimWriter.cpp index 151f6f25..efef3a90 100644 --- a/src/ObjWriting/XAnim/CompiledXAnimWriter.cpp +++ b/src/ObjWriting/XAnim/CompiledXAnimWriter.cpp @@ -11,9 +11,6 @@ using namespace xanim; namespace { - constexpr uint8_t FLAG_LOOPED = 1u; - constexpr uint8_t FLAG_DELTA = 2u; - // The linker decodes raw trans size[] with these exact float literals. // They correspond to 1.0f / 255.0f and 1.0f / 65535.0f, but we keep the // decompiled values to preserve binary-stable round trips. @@ -27,6 +24,34 @@ namespace std::vector m_stored_values; }; + uint8_t GetFlagsForVersion(const CompiledXAnimVersion version, const CommonXAnimParts& parts) + { + uint8_t flags = 0; + + switch (version) + { + case CompiledXAnimVersion::VERSION_17: + if (parts.m_looped) + flags |= binary17::FLAG_LOOPED; + if (parts.m_delta_track) + flags |= binary17::FLAG_DELTA; + break; + + case CompiledXAnimVersion::VERSION_19: + if (parts.m_looped) + flags |= binary19::FLAG_LOOPED; + if (parts.m_delta_track) + flags |= binary19::FLAG_DELTA; + if (parts.m_left_hand_grip_ik) + flags |= binary19::FLAG_LEFT_HAND_GRIP_IK; + if (parts.m_streamable) + flags |= binary19::FLAG_STREAMABLE; + break; + } + + return flags; + } + [[nodiscard]] uint16_t GetNumLoopFrames(const CommonXAnimParts& parts) { assert(parts.m_num_frames < std::numeric_limits::max()); @@ -425,7 +450,7 @@ namespace namespace xanim { - void WriteCompiledXAnim(std::ostream& stream, const CommonXAnimParts& parts) + void WriteCompiledXAnim(std::ostream& stream, const CommonXAnimParts& parts, CompiledXAnimVersion version) { const auto numLoopFrames = GetNumLoopFrames(parts); const auto useByteIndices = parts.m_num_frames < 256; @@ -435,18 +460,20 @@ namespace xanim for (const auto& bone : parts.m_bone_tracks) encodedBoneQuats.emplace_back(EncodeQuatTrack(bone.m_quat)); - const auto flags = static_cast((parts.m_looped ? FLAG_LOOPED : 0u) | (parts.m_delta_track ? FLAG_DELTA : 0u)); + const auto flags = GetFlagsForVersion(version, parts); const auto boneCount = static_cast(parts.m_bone_tracks.size()); const auto assetType = static_cast(parts.m_asset_type); const auto framerate = static_cast(std::lround(parts.m_frame_rate)); - stream::WriteValue(stream, static_cast(CompiledXAnimVersion::VERSION_17)); + stream::WriteValue(stream, static_cast(version)); // Looped raws store numframes directly; non-looped raws store numframes + 1. stream::WriteValue(stream, static_cast(parts.m_looped ? parts.m_num_frames : numLoopFrames)); stream::WriteValue(stream, boneCount); stream::WriteValue(stream, flags); stream::WriteValue(stream, assetType); stream::WriteValue(stream, framerate); + if (version == CompiledXAnimVersion::VERSION_19 && parts.m_streamable) + stream::WriteValue(stream, parts.m_primed_length); if (parts.m_delta_track) WriteDeltaTrack(stream, *parts.m_delta_track, numLoopFrames, useByteIndices); diff --git a/src/ObjWriting/XAnim/CompiledXAnimWriter.h b/src/ObjWriting/XAnim/CompiledXAnimWriter.h index 61c8a268..7b985823 100644 --- a/src/ObjWriting/XAnim/CompiledXAnimWriter.h +++ b/src/ObjWriting/XAnim/CompiledXAnimWriter.h @@ -1,10 +1,11 @@ #pragma once +#include "XAnim/BinaryXAnimCommon.h" #include "XAnim/XAnimCommon.h" #include namespace xanim { - void WriteCompiledXAnim(std::ostream& stream, const CommonXAnimParts& parts); + void WriteCompiledXAnim(std::ostream& stream, const CommonXAnimParts& parts, CompiledXAnimVersion version); } diff --git a/src/ObjWriting/XAnim/XAnimDumper.cpp.template b/src/ObjWriting/XAnim/XAnimDumper.cpp.template index 6f86e8e9..9d7ce644 100644 --- a/src/ObjWriting/XAnim/XAnimDumper.cpp.template +++ b/src/ObjWriting/XAnim/XAnimDumper.cpp.template @@ -1,4 +1,4 @@ -#options GAME(IW3) +#options GAME(IW3, T5) #filename "Game/" + GAME + "/XAnim/XAnimDumper" + GAME + ".cpp" @@ -6,12 +6,14 @@ #if GAME == "IW3" #define FEATURE_IW3 +#define ANIM_17 #elif GAME == "IW4" #define FEATURE_IW4 #elif GAME == "IW5" #define FEATURE_IW5 #elif GAME == "T5" #define FEATURE_T5 +#define ANIM_19 #elif GAME == "T6" #define FEATURE_T6 #endif @@ -252,12 +254,27 @@ namespace xanim CommonXAnimParts commonParts; commonParts.m_num_frames = parts->numframes; commonParts.m_looped = parts->bLoop; +#if defined(FEATURE_T5) + commonParts.m_left_hand_grip_ik = parts->bLeftHandGripIK; + commonParts.m_streamable = parts->bStreamable; +#endif commonParts.m_frame_rate = parts->framerate; +#ifdef defined(FEATURE_T5) + commonParts.m_primed_length = parts->primedLength; +#endif commonParts.m_asset_type = parts->assetType; commonParts.m_bone_tracks = std::move(maybeBoneTracks).value(); commonParts.m_notifies = ConvertNotifies(*parts, asset); commonParts.m_delta_track = ConvertDeltaTrack(*parts, useByteIndices, numLoopFrames); - WriteCompiledXAnim(*assetFile, commonParts); + WriteCompiledXAnim( + *assetFile, + commonParts, +#ifdef ANIM_17 + CompiledXAnimVersion::VERSION_17 +#elif defined(ANIM_19) + CompiledXAnimVersion::VERSION_19 +#endif + ); } } // namespace xanim diff --git a/src/ObjWriting/XAnim/XAnimDumper.h.template b/src/ObjWriting/XAnim/XAnimDumper.h.template index a178dfda..1e5db601 100644 --- a/src/ObjWriting/XAnim/XAnimDumper.h.template +++ b/src/ObjWriting/XAnim/XAnimDumper.h.template @@ -1,4 +1,4 @@ -#options GAME(IW3) +#options GAME(IW3, T5) #filename "Game/" + GAME + "/XAnim/XAnimDumper" + GAME + ".h"