mirror of
https://github.com/Laupetin/OpenAssetTools.git
synced 2026-06-06 00:32:34 +00:00
feat: load binary t5 xanims
This commit is contained in:
@@ -8,6 +8,7 @@
|
|||||||
#include "Game/T5/T5.h"
|
#include "Game/T5/T5.h"
|
||||||
#include "Game/T5/Techset/PixelShaderLoaderT5.h"
|
#include "Game/T5/Techset/PixelShaderLoaderT5.h"
|
||||||
#include "Game/T5/Techset/VertexShaderLoaderT5.h"
|
#include "Game/T5/Techset/VertexShaderLoaderT5.h"
|
||||||
|
#include "Game/T5/XAnim/XAnimLoaderT5.h"
|
||||||
#include "Game/T5/XModel/LoaderXModelT5.h"
|
#include "Game/T5/XModel/LoaderXModelT5.h"
|
||||||
#include "LightDef/LightDefLoaderT5.h"
|
#include "LightDef/LightDefLoaderT5.h"
|
||||||
#include "Localize/LoaderLocalizeT5.h"
|
#include "Localize/LoaderLocalizeT5.h"
|
||||||
@@ -112,7 +113,7 @@ namespace
|
|||||||
collection.AddAssetCreator(phys_preset::CreateGdtLoaderT5(memory, gdt, zone));
|
collection.AddAssetCreator(phys_preset::CreateGdtLoaderT5(memory, gdt, zone));
|
||||||
// collection.AddAssetCreator(std::make_unique<AssetLoaderPhysConstraints>(memory));
|
// collection.AddAssetCreator(std::make_unique<AssetLoaderPhysConstraints>(memory));
|
||||||
// collection.AddAssetCreator(std::make_unique<AssetLoaderDestructibleDef>(memory));
|
// collection.AddAssetCreator(std::make_unique<AssetLoaderDestructibleDef>(memory));
|
||||||
// collection.AddAssetCreator(std::make_unique<AssetLoaderXAnim>(memory));
|
collection.AddAssetCreator(xanim::CreateLoaderT5(memory, searchPath, zone));
|
||||||
collection.AddAssetCreator(xmodel::CreateLoaderT5(memory, searchPath, zone));
|
collection.AddAssetCreator(xmodel::CreateLoaderT5(memory, searchPath, zone));
|
||||||
collection.AddAssetCreator(material::CreateLoaderT5(memory, searchPath));
|
collection.AddAssetCreator(material::CreateLoaderT5(memory, searchPath));
|
||||||
// collection.AddAssetCreator(std::make_unique<AssetLoaderTechniqueSet>(memory));
|
// collection.AddAssetCreator(std::make_unique<AssetLoaderTechniqueSet>(memory));
|
||||||
|
|||||||
@@ -14,9 +14,6 @@ using namespace xanim;
|
|||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
constexpr uint8_t FLAG_LOOPED = 1u;
|
|
||||||
constexpr uint8_t FLAG_DELTA = 2u;
|
|
||||||
|
|
||||||
// The linker decodes raw trans size[] with these exact float literals.
|
// 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
|
// They correspond to 1.0f / 255.0f and 1.0f / 65535.0f, but we keep the
|
||||||
// decompiled values to preserve binary-stable round trips.
|
// decompiled values to preserve binary-stable round trips.
|
||||||
@@ -30,6 +27,8 @@ namespace
|
|||||||
{
|
{
|
||||||
case CompiledXAnimVersion::VERSION_17:
|
case CompiledXAnimVersion::VERSION_17:
|
||||||
return CompiledXAnimVersion::VERSION_17;
|
return CompiledXAnimVersion::VERSION_17;
|
||||||
|
case CompiledXAnimVersion::VERSION_19:
|
||||||
|
return CompiledXAnimVersion::VERSION_19;
|
||||||
default:
|
default:
|
||||||
return std::unexpected(std::format("Version {} is not supported", fileVersion));
|
return std::unexpected(std::format("Version {} is not supported", fileVersion));
|
||||||
}
|
}
|
||||||
@@ -347,6 +346,54 @@ namespace
|
|||||||
// This notify is always automatically added
|
// This notify is always automatically added
|
||||||
parts.m_notifies.emplace_back("end", 1.0f);
|
parts.m_notifies.emplace_back("end", 1.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IsLooped(const uint8_t flags, const CompiledXAnimVersion version)
|
||||||
|
{
|
||||||
|
switch (version)
|
||||||
|
{
|
||||||
|
case CompiledXAnimVersion::VERSION_17:
|
||||||
|
return (flags & binary17::FLAG_LOOPED) > 0;
|
||||||
|
case CompiledXAnimVersion::VERSION_19:
|
||||||
|
return (flags & binary19::FLAG_LOOPED) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HasDelta(const uint8_t flags, const CompiledXAnimVersion version)
|
||||||
|
{
|
||||||
|
switch (version)
|
||||||
|
{
|
||||||
|
case CompiledXAnimVersion::VERSION_17:
|
||||||
|
return (flags & binary17::FLAG_DELTA) > 0;
|
||||||
|
case CompiledXAnimVersion::VERSION_19:
|
||||||
|
return (flags & binary19::FLAG_DELTA) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsLeftHandGripIk(const uint8_t flags, const CompiledXAnimVersion version)
|
||||||
|
{
|
||||||
|
switch (version)
|
||||||
|
{
|
||||||
|
case CompiledXAnimVersion::VERSION_19:
|
||||||
|
return (flags & binary19::FLAG_LEFT_HAND_GRIP_IK) > 0;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsStreamable(const uint8_t flags, const CompiledXAnimVersion version)
|
||||||
|
{
|
||||||
|
switch (version)
|
||||||
|
{
|
||||||
|
case CompiledXAnimVersion::VERSION_19:
|
||||||
|
return (flags & binary19::FLAG_STREAMABLE) > 0;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
namespace xanim
|
namespace xanim
|
||||||
@@ -360,7 +407,6 @@ namespace xanim
|
|||||||
auto parts = std::make_unique<CommonXAnimParts>();
|
auto parts = std::make_unique<CommonXAnimParts>();
|
||||||
|
|
||||||
const auto version = maybeVersion.value();
|
const auto version = maybeVersion.value();
|
||||||
assert(version == CompiledXAnimVersion::VERSION_17);
|
|
||||||
|
|
||||||
const auto numFrames = stream::ReadValue<uint16_t>(stream);
|
const auto numFrames = stream::ReadValue<uint16_t>(stream);
|
||||||
const auto boneCount = stream::ReadValue<uint16_t>(stream);
|
const auto boneCount = stream::ReadValue<uint16_t>(stream);
|
||||||
@@ -370,15 +416,22 @@ namespace xanim
|
|||||||
if (stream.fail())
|
if (stream.fail())
|
||||||
return std::unexpected("Truncated file");
|
return std::unexpected("Truncated file");
|
||||||
|
|
||||||
const bool isLooped = flags & FLAG_LOOPED;
|
const bool isLooped = IsLooped(flags, version);
|
||||||
const bool hasDelta = flags & FLAG_DELTA;
|
const bool hasDelta = HasDelta(flags, version);
|
||||||
|
const bool leftHandGripIk = IsLeftHandGripIk(flags, version);
|
||||||
|
const bool streamable = IsStreamable(flags, version);
|
||||||
const uint16_t numLoopFrames = isLooped ? numFrames + 1u : numFrames;
|
const uint16_t numLoopFrames = isLooped ? numFrames + 1u : numFrames;
|
||||||
|
|
||||||
parts->m_num_frames = numLoopFrames - 1;
|
parts->m_num_frames = numLoopFrames - 1;
|
||||||
parts->m_looped = isLooped;
|
parts->m_looped = isLooped;
|
||||||
|
parts->m_left_hand_grip_ik = leftHandGripIk;
|
||||||
|
parts->m_streamable = streamable;
|
||||||
parts->m_asset_type = assetType;
|
parts->m_asset_type = assetType;
|
||||||
parts->m_frame_rate = static_cast<float>(framerate);
|
parts->m_frame_rate = static_cast<float>(framerate);
|
||||||
|
|
||||||
|
if (version == CompiledXAnimVersion::VERSION_19 && streamable)
|
||||||
|
parts->m_primed_length = stream::ReadValue<float>(stream);
|
||||||
|
|
||||||
const auto useByteIndices = parts->m_num_frames < 256;
|
const auto useByteIndices = parts->m_num_frames < 256;
|
||||||
|
|
||||||
if (hasDelta)
|
if (hasDelta)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#options GAME(IW3)
|
#options GAME(IW3, T5)
|
||||||
|
|
||||||
#filename "Game/" + GAME + "/XAnim/XAnimLoader" + GAME + ".cpp"
|
#filename "Game/" + GAME + "/XAnim/XAnimLoader" + GAME + ".cpp"
|
||||||
|
|
||||||
@@ -70,6 +70,18 @@ namespace
|
|||||||
|
|
||||||
notify.time = commonNotify.m_time;
|
notify.time = commonNotify.m_time;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef FEATURE_T5
|
||||||
|
const auto loopBegin = std::ranges::find_if(commonParts.m_notifies, [](const xanim::CommonXAnimNotifyInfo& notify)
|
||||||
|
{
|
||||||
|
return notify.m_name == "loop_begin";
|
||||||
|
});
|
||||||
|
|
||||||
|
if (loopBegin != commonParts.m_notifies.end())
|
||||||
|
parts.loopEntryTime = loopBegin->m_time;
|
||||||
|
else
|
||||||
|
parts.loopEntryTime = 0;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T> void ConvertIndices(T& indices, const std::vector<uint16_t>& commonIndices, const bool useByteIndices)
|
template<typename T> void ConvertIndices(T& indices, const std::vector<uint16_t>& commonIndices, const bool useByteIndices)
|
||||||
@@ -279,9 +291,16 @@ namespace
|
|||||||
{
|
{
|
||||||
parts.numframes = static_cast<decltype(XAnimParts::numframes)>(commonParts.m_num_frames);
|
parts.numframes = static_cast<decltype(XAnimParts::numframes)>(commonParts.m_num_frames);
|
||||||
parts.bLoop = commonParts.m_looped;
|
parts.bLoop = commonParts.m_looped;
|
||||||
|
#ifdef FEATURE_T5
|
||||||
|
parts.bLeftHandGripIK = commonParts.m_left_hand_grip_ik;
|
||||||
|
parts.bStreamable = commonParts.m_streamable;
|
||||||
|
#endif
|
||||||
parts.assetType = commonParts.m_asset_type;
|
parts.assetType = commonParts.m_asset_type;
|
||||||
parts.framerate = commonParts.m_frame_rate;
|
parts.framerate = commonParts.m_frame_rate;
|
||||||
parts.frequency = parts.numframes > 0 ? parts.framerate / static_cast<float>(parts.numframes) : 0;
|
parts.frequency = parts.numframes > 0 ? parts.framerate / static_cast<float>(parts.numframes) : 0;
|
||||||
|
#ifdef FEATURE_T5
|
||||||
|
parts.primedLength = commonParts.m_primed_length;
|
||||||
|
#endif
|
||||||
|
|
||||||
const auto useByteIndices = parts.numframes < 256;
|
const auto useByteIndices = parts.numframes < 256;
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#options GAME(IW3)
|
#options GAME(IW3, T5)
|
||||||
|
|
||||||
#filename "Game/" + GAME + "/XAnim/XAnimLoader" + GAME + ".h"
|
#filename "Game/" + GAME + "/XAnim/XAnimLoader" + GAME + ".h"
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
@@ -0,0 +1,62 @@
|
|||||||
|
#include "Game/T5/XAnim/XAnimDumperT5.h"
|
||||||
|
#include "Game/T5/XAnim/XAnimLoaderT5.h"
|
||||||
|
#include "OatTestPaths.h"
|
||||||
|
#include "SearchPath/MockOutputPath.h"
|
||||||
|
#include "SearchPath/MockSearchPath.h"
|
||||||
|
#include "ZoneLoading.h"
|
||||||
|
|
||||||
|
#include <catch2/catch_test_macros.hpp>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <fstream>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
using namespace std::literals;
|
||||||
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
TEST_CASE("XAnim Loading/Dumping (T5)", "[t5][system]")
|
||||||
|
{
|
||||||
|
MockSearchPath searchPath;
|
||||||
|
|
||||||
|
const auto filePath = oat::paths::GetTestDirectory() / "SystemTests/Game/T5/XAnim/test_anim";
|
||||||
|
const auto fileSize = static_cast<size_t>(fs::file_size(filePath));
|
||||||
|
|
||||||
|
std::ifstream file(filePath, std::ios::binary);
|
||||||
|
REQUIRE(file.is_open());
|
||||||
|
|
||||||
|
const auto data = std::make_unique<char[]>(fileSize);
|
||||||
|
file.read(data.get(), fileSize);
|
||||||
|
|
||||||
|
searchPath.AddFileData("xanim/test_anim", std::string(data.get(), fileSize));
|
||||||
|
|
||||||
|
Zone zone("MockZone", 0, GameId::IW3, GamePlatform::PC);
|
||||||
|
AssetCreatorCollection creatorCollection(zone);
|
||||||
|
IgnoredAssetLookup ignoredAssetLookup;
|
||||||
|
MemoryManager memoryManager;
|
||||||
|
const auto loader = xanim::CreateLoaderT5(memoryManager, searchPath, zone);
|
||||||
|
AssetCreationContext context(zone, &creatorCollection, &ignoredAssetLookup);
|
||||||
|
|
||||||
|
const auto result = loader->CreateAsset("test_anim", context);
|
||||||
|
|
||||||
|
REQUIRE(result.HasBeenSuccessful());
|
||||||
|
const auto* assetInfo = reinterpret_cast<XAssetInfo<T5::AssetXAnim::Type>*>(result.GetAssetInfo());
|
||||||
|
const auto* parts = assetInfo->Asset();
|
||||||
|
|
||||||
|
REQUIRE(parts->name == "test_anim"s);
|
||||||
|
REQUIRE(parts->numframes > 0);
|
||||||
|
|
||||||
|
MockSearchPath mockObjPath;
|
||||||
|
MockOutputPath mockOutput;
|
||||||
|
xanim::DumperT5 dumper;
|
||||||
|
AssetDumpingContext dumpingContext(zone, "", mockOutput, mockObjPath, std::nullopt);
|
||||||
|
dumper.Dump(dumpingContext);
|
||||||
|
|
||||||
|
const auto* outAnimFile = mockOutput.GetMockedFile("xanim/test_anim");
|
||||||
|
REQUIRE(outAnimFile != nullptr);
|
||||||
|
|
||||||
|
REQUIRE(outAnimFile->m_data.size() == fileSize);
|
||||||
|
REQUIRE(memcmp(outAnimFile->m_data.data(), data.get(), fileSize) == 0);
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
Reference in New Issue
Block a user