From 237dc83cfcdc6c2f2bfe95cd8c36015b396edb90 Mon Sep 17 00:00:00 2001 From: Jan Date: Sat, 30 Mar 2024 14:09:29 +0100 Subject: [PATCH 01/25] chore: update xmodel structs --- src/Common/Game/T6/T6_Assets.h | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/Common/Game/T6/T6_Assets.h b/src/Common/Game/T6/T6_Assets.h index 11e55fc0..581745fb 100644 --- a/src/Common/Game/T6/T6_Assets.h +++ b/src/Common/Game/T6/T6_Assets.h @@ -22,6 +22,8 @@ namespace T6 typedef tdef_align(128) float float_align128; + typedef uint16_t ScriptString; + struct dvar_t; struct MenuCell; struct cplane_s; @@ -591,14 +593,22 @@ namespace T6 int partBits[5]; }; + enum XModelLodRampType : unsigned char + { + XMODEL_LOD_RAMP_RIGID = 0x0, + XMODEL_LOD_RAMP_SKINNED = 0x1, + + XMODEL_LOD_RAMP_COUNT + }; + struct XModel { const char* name; unsigned char numBones; unsigned char numRootBones; unsigned char numsurfs; - char lodRampType; - uint16_t* boneNames; + XModelLodRampType lodRampType; + ScriptString* boneNames; unsigned char* parentList; uint16_t (*quats)[4]; float (*trans)[4]; @@ -618,7 +628,7 @@ namespace T6 uint16_t collLod; float* himipInvSqRadii; int memUsage; - int flags; + unsigned int flags; bool bad; PhysPreset* physPreset; unsigned char numCollmaps; From abc3003b5b4d54946d05ac9326fc20f270a44d4e Mon Sep 17 00:00:00 2001 From: Jan Date: Sat, 30 Mar 2024 14:09:58 +0100 Subject: [PATCH 02/25] chore: implement base loading and writing of xmodel json --- src/ObjCommon/Game/T6/Json/JsonXModel.h | 36 ++++++ .../T6/AssetLoaders/AssetLoaderXModel.cpp | 44 +++++++ .../Game/T6/AssetLoaders/AssetLoaderXModel.h | 19 +++ .../Game/T6/XModel/JsonXModelLoader.cpp | 116 ++++++++++++++++++ .../Game/T6/XModel/JsonXModelLoader.h | 13 ++ .../Game/T6/XModel/JsonXModelWriter.cpp | 71 +++++++++++ .../Game/T6/XModel/JsonXModelWriter.h | 11 ++ .../Model/XModel/XModelExportWriter.cpp | 1 + 8 files changed, 311 insertions(+) create mode 100644 src/ObjCommon/Game/T6/Json/JsonXModel.h create mode 100644 src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderXModel.cpp create mode 100644 src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderXModel.h create mode 100644 src/ObjLoading/Game/T6/XModel/JsonXModelLoader.cpp create mode 100644 src/ObjLoading/Game/T6/XModel/JsonXModelLoader.h create mode 100644 src/ObjWriting/Game/T6/XModel/JsonXModelWriter.cpp create mode 100644 src/ObjWriting/Game/T6/XModel/JsonXModelWriter.h diff --git a/src/ObjCommon/Game/T6/Json/JsonXModel.h b/src/ObjCommon/Game/T6/Json/JsonXModel.h new file mode 100644 index 00000000..28ac5563 --- /dev/null +++ b/src/ObjCommon/Game/T6/Json/JsonXModel.h @@ -0,0 +1,36 @@ +#pragma once + +#include "Game/T6/T6.h" + +#include "Json/JsonCommon.h" +#include "Json/JsonExtension.h" +#include +#include +#include +#include +#include + +namespace T6 +{ + class JsonXModelLod + { + public: + std::string file; + }; + + NLOHMANN_DEFINE_TYPE_EXTENSION(JsonXModelLod, file); + + class JsonXModel + { + public: + std::vector lods; + unsigned 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/ObjLoading/Game/T6/AssetLoaders/AssetLoaderXModel.cpp b/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderXModel.cpp new file mode 100644 index 00000000..8cbbd072 --- /dev/null +++ b/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderXModel.cpp @@ -0,0 +1,44 @@ +#include "AssetLoaderXModel.h" + +#include "Game/T6/XModel/JsonXModelLoader.h" +#include "Game/T6/T6.h" +#include "Pool/GlobalAssetPool.h" + +#include +#include +#include + +using namespace T6; + +void* AssetLoaderXModel::CreateEmptyAsset(const std::string& assetName, MemoryManager* memory) +{ + auto* xmodel = memory->Create(); + memset(xmodel, 0, sizeof(XModel)); + xmodel->name = memory->Dup(assetName.c_str()); + + return xmodel; +} + +bool AssetLoaderXModel::CanLoadFromRaw() const +{ + return true; +} + +bool AssetLoaderXModel::LoadFromRaw( + const std::string& assetName, ISearchPath* searchPath, MemoryManager* memory, IAssetLoadingManager* manager, Zone* zone) const +{ + const auto file = searchPath->Open(std::format("xmodel/{}.json", assetName)); + if (!file.IsOpen()) + return false; + + auto* xmodel = memory->Alloc(); + xmodel->name = memory->Dup(assetName.c_str()); + + std::vector dependencies; + if (LoadXModelAsJson(*file.m_stream, *xmodel, memory, manager, dependencies)) + manager->AddAsset(assetName, xmodel, std::move(dependencies)); + else + std::cerr << "Failed to load xmodel \"" << assetName << "\"\n"; + + return true; +} diff --git a/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderXModel.h b/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderXModel.h new file mode 100644 index 00000000..0e0115da --- /dev/null +++ b/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderXModel.h @@ -0,0 +1,19 @@ +#pragma once +#include "AssetLoading/BasicAssetLoader.h" +#include "AssetLoading/IAssetLoadingManager.h" +#include "Game/T6/T6.h" +#include "SearchPath/ISearchPath.h" + +namespace T6 +{ + class AssetLoaderXModel final : public BasicAssetLoader + { + static std::string GetFileNameForAsset(const std::string& assetName); + + public: + _NODISCARD void* CreateEmptyAsset(const std::string& assetName, MemoryManager* memory) override; + _NODISCARD bool CanLoadFromRaw() const override; + bool + LoadFromRaw(const std::string& assetName, ISearchPath* searchPath, MemoryManager* memory, IAssetLoadingManager* manager, Zone* zone) const override; + }; +} // namespace T6 diff --git a/src/ObjLoading/Game/T6/XModel/JsonXModelLoader.cpp b/src/ObjLoading/Game/T6/XModel/JsonXModelLoader.cpp new file mode 100644 index 00000000..6dc2268a --- /dev/null +++ b/src/ObjLoading/Game/T6/XModel/JsonXModelLoader.cpp @@ -0,0 +1,116 @@ +#include "JsonXModelLoader.h" + +#include "Game/T6/CommonT6.h" +#include "Game/T6/Json/JsonXModel.h" + +#include +#include +#include + +using namespace nlohmann; +using namespace T6; + +namespace +{ + class JsonLoader + { + public: + JsonLoader(std::istream& stream, MemoryManager& memory, IAssetLoadingManager& manager, std::set& dependencies) + : m_stream(stream), + m_memory(memory), + m_manager(manager), + m_dependencies(dependencies) + + { + } + + bool Load(XModel& xmodel) const + { + const auto jRoot = json::parse(m_stream); + std::string type; + unsigned version; + + jRoot.at("_type").get_to(type); + jRoot.at("_version").get_to(version); + + if (type != "xmodel" || version != 1u) + { + std::cerr << "Tried to load xmodel \"" << xmodel.name << "\" but did not find expected type material of version 1\n"; + return false; + } + + const auto jXModel = jRoot.get(); + return CreateXModelFromJson(jXModel, xmodel); + } + + private: + static void PrintError(const XModel& xmodel, const std::string& message) + { + std::cerr << "Cannot load xmodel \"" << xmodel.name << "\": " << message << "\n"; + } + + bool CreateXModelFromJson(const JsonXModel& jXModel, XModel& xmodel) const + { + xmodel.collLod = static_cast(jXModel.collLod); + + if (jXModel.physPreset) + { + auto* physPreset = m_manager.LoadDependency(jXModel.physPreset.value()); + if (!physPreset) + { + PrintError(xmodel, "Could not find phys preset"); + return false; + } + m_dependencies.emplace(physPreset); + xmodel.physPreset = physPreset->Asset(); + } + else + { + xmodel.physPreset = nullptr; + } + + if (jXModel.physConstraints) + { + auto* physConstraints = m_manager.LoadDependency(jXModel.physConstraints.value()); + if (!physConstraints) + { + PrintError(xmodel, "Could not find phys constraints"); + return false; + } + m_dependencies.emplace(physConstraints); + xmodel.physConstraints = physConstraints->Asset(); + } + else + { + xmodel.physConstraints = nullptr; + } + + xmodel.flags = jXModel.flags; + xmodel.lightingOriginOffset.x = jXModel.lightingOriginOffset.x; + xmodel.lightingOriginOffset.y = jXModel.lightingOriginOffset.y; + xmodel.lightingOriginOffset.z = jXModel.lightingOriginOffset.z; + xmodel.lightingOriginRange = jXModel.lightingOriginRange; + + return true; + } + + std::istream& m_stream; + MemoryManager& m_memory; + IAssetLoadingManager& m_manager; + std::set& m_dependencies; + }; +} // namespace + +namespace T6 +{ + bool LoadXModelAsJson( + std::istream& stream, XModel& xmodel, MemoryManager* memory, IAssetLoadingManager* manager, std::vector& dependencies) + { + std::set dependenciesSet; + const JsonLoader loader(stream, *memory, *manager, dependenciesSet); + + dependencies.assign_range(dependenciesSet); + + return loader.Load(xmodel); + } +} // namespace T6 diff --git a/src/ObjLoading/Game/T6/XModel/JsonXModelLoader.h b/src/ObjLoading/Game/T6/XModel/JsonXModelLoader.h new file mode 100644 index 00000000..d7747287 --- /dev/null +++ b/src/ObjLoading/Game/T6/XModel/JsonXModelLoader.h @@ -0,0 +1,13 @@ +#pragma once + +#include "AssetLoading/IAssetLoadingManager.h" +#include "Game/T6/T6.h" +#include "Utils/MemoryManager.h" + +#include + +namespace T6 +{ + bool LoadXModelAsJson( + std::istream& stream, XModel& xmodel, MemoryManager* memory, IAssetLoadingManager* manager, std::vector& dependencies); +} // namespace T6 diff --git a/src/ObjWriting/Game/T6/XModel/JsonXModelWriter.cpp b/src/ObjWriting/Game/T6/XModel/JsonXModelWriter.cpp new file mode 100644 index 00000000..5f05331a --- /dev/null +++ b/src/ObjWriting/Game/T6/XModel/JsonXModelWriter.cpp @@ -0,0 +1,71 @@ +#include "JsonXModelWriter.h" + +#include "Game/T6/CommonT6.h" +#include "Game/T6/Json/JsonXModel.h" + +#include +#include + +using namespace nlohmann; +using namespace T6; + +namespace +{ + class JsonDumper + { + public: + JsonDumper(AssetDumpingContext& context, std::ostream& stream) + : m_stream(stream) + { + } + + void Dump(const XModel* xmodel) const + { + JsonXModel jsonXModel; + CreateJsonXModel(jsonXModel, *xmodel); + json jRoot = jsonXModel; + + jRoot["_type"] = "xmodel"; + jRoot["_version"] = 1; + + m_stream << std::setw(4) << jRoot << "\n"; + } + + private: + static const char* AssetName(const char* input) + { + if (input && input[0] == ',') + return &input[1]; + + return input; + } + + void CreateJsonXModel(JsonXModel& jXModel, const XModel& xmodel) const + { + jXModel.collLod = xmodel.collLod; + + if (xmodel.physPreset && xmodel.physPreset->name) + jXModel.physPreset = AssetName(xmodel.physPreset->name); + + if (xmodel.physConstraints && xmodel.physConstraints->name) + jXModel.physConstraints = AssetName(xmodel.physConstraints->name); + + jXModel.flags = xmodel.flags; + jXModel.lightingOriginOffset.x = xmodel.lightingOriginOffset.x; + jXModel.lightingOriginOffset.y = xmodel.lightingOriginOffset.y; + jXModel.lightingOriginOffset.z = xmodel.lightingOriginOffset.z; + jXModel.lightingOriginRange = xmodel.lightingOriginRange; + } + + std::ostream& m_stream; + }; +} // namespace + +namespace T6 +{ + void DumpXModelAsJson(std::ostream& stream, const XModel* xmodel, AssetDumpingContext& context) + { + const JsonDumper dumper(context, stream); + dumper.Dump(xmodel); + } +} // namespace T6 diff --git a/src/ObjWriting/Game/T6/XModel/JsonXModelWriter.h b/src/ObjWriting/Game/T6/XModel/JsonXModelWriter.h new file mode 100644 index 00000000..f40f008d --- /dev/null +++ b/src/ObjWriting/Game/T6/XModel/JsonXModelWriter.h @@ -0,0 +1,11 @@ +#pragma once + +#include "Dumping/AssetDumpingContext.h" +#include "Game/T6/T6.h" + +#include + +namespace T6 +{ + void DumpXModelAsJson(std::ostream& stream, const XModel* xmodel, AssetDumpingContext& context); +} // namespace T6 diff --git a/src/ObjWriting/Model/XModel/XModelExportWriter.cpp b/src/ObjWriting/Model/XModel/XModelExportWriter.cpp index c8b96395..668ee9ea 100644 --- a/src/ObjWriting/Model/XModel/XModelExportWriter.cpp +++ b/src/ObjWriting/Model/XModel/XModelExportWriter.cpp @@ -2,6 +2,7 @@ #include "Math/Quaternion.h" +#include #include #include From d4ef9fa3d944a2b96429a0313e4b23fe7c7d219b Mon Sep 17 00:00:00 2001 From: Jan Date: Sat, 30 Mar 2024 15:19:11 +0100 Subject: [PATCH 03/25] chore: move xmodel packages --- src/ObjCommon/{Model => XModel}/Obj/ObjCommon.cpp | 0 src/ObjCommon/{Model => XModel}/Obj/ObjCommon.h | 0 src/ObjCommon/{Model => }/XModel/XModelCommon.cpp | 0 src/ObjCommon/{Model => }/XModel/XModelCommon.h | 0 src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperXModel.cpp | 2 +- src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperXModel.h | 4 ++-- src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperXModel.cpp | 2 +- src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperXModel.h | 4 ++-- src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperXModel.cpp | 2 +- src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperXModel.h | 4 ++-- src/ObjWriting/Game/T5/AssetDumpers/AssetDumperXModel.cpp | 2 +- src/ObjWriting/Game/T5/AssetDumpers/AssetDumperXModel.h | 4 ++-- src/ObjWriting/Game/T6/AssetDumpers/AssetDumperXModel.cpp | 2 +- src/ObjWriting/Game/T6/AssetDumpers/AssetDumperXModel.h | 4 ++-- src/ObjWriting/{Model => }/XModel/AbstractXModelWriter.cpp | 0 src/ObjWriting/{Model => }/XModel/AbstractXModelWriter.h | 2 +- .../{Model/XModel => XModel/Export}/XModelBinWriter.cpp | 0 .../{Model/XModel => XModel/Export}/XModelBinWriter.h | 0 .../{Model/XModel => XModel/Export}/XModelExportWriter.cpp | 0 .../{Model/XModel => XModel/Export}/XModelExportWriter.h | 2 +- src/ObjWriting/{Model => XModel}/Obj/ObjWriter.cpp | 0 src/ObjWriting/{Model => XModel}/Obj/ObjWriter.h | 2 +- 22 files changed, 18 insertions(+), 18 deletions(-) rename src/ObjCommon/{Model => XModel}/Obj/ObjCommon.cpp (100%) rename src/ObjCommon/{Model => XModel}/Obj/ObjCommon.h (100%) rename src/ObjCommon/{Model => }/XModel/XModelCommon.cpp (100%) rename src/ObjCommon/{Model => }/XModel/XModelCommon.h (100%) rename src/ObjWriting/{Model => }/XModel/AbstractXModelWriter.cpp (100%) rename src/ObjWriting/{Model => }/XModel/AbstractXModelWriter.h (94%) rename src/ObjWriting/{Model/XModel => XModel/Export}/XModelBinWriter.cpp (100%) rename src/ObjWriting/{Model/XModel => XModel/Export}/XModelBinWriter.h (100%) rename src/ObjWriting/{Model/XModel => XModel/Export}/XModelExportWriter.cpp (100%) rename src/ObjWriting/{Model/XModel => XModel/Export}/XModelExportWriter.h (94%) rename src/ObjWriting/{Model => XModel}/Obj/ObjWriter.cpp (100%) rename src/ObjWriting/{Model => XModel}/Obj/ObjWriter.h (97%) diff --git a/src/ObjCommon/Model/Obj/ObjCommon.cpp b/src/ObjCommon/XModel/Obj/ObjCommon.cpp similarity index 100% rename from src/ObjCommon/Model/Obj/ObjCommon.cpp rename to src/ObjCommon/XModel/Obj/ObjCommon.cpp diff --git a/src/ObjCommon/Model/Obj/ObjCommon.h b/src/ObjCommon/XModel/Obj/ObjCommon.h similarity index 100% rename from src/ObjCommon/Model/Obj/ObjCommon.h rename to src/ObjCommon/XModel/Obj/ObjCommon.h diff --git a/src/ObjCommon/Model/XModel/XModelCommon.cpp b/src/ObjCommon/XModel/XModelCommon.cpp similarity index 100% rename from src/ObjCommon/Model/XModel/XModelCommon.cpp rename to src/ObjCommon/XModel/XModelCommon.cpp diff --git a/src/ObjCommon/Model/XModel/XModelCommon.h b/src/ObjCommon/XModel/XModelCommon.h similarity index 100% rename from src/ObjCommon/Model/XModel/XModelCommon.h rename to src/ObjCommon/XModel/XModelCommon.h diff --git a/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperXModel.cpp b/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperXModel.cpp index 45d031fd..776f68bb 100644 --- a/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperXModel.cpp +++ b/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperXModel.cpp @@ -2,10 +2,10 @@ #include "Game/IW3/CommonIW3.h" #include "Math/Quaternion.h" -#include "Model/XModel/XModelExportWriter.h" #include "ObjWriting.h" #include "Utils/HalfFloat.h" #include "Utils/QuatInt16.h" +#include "XModel/Export/XModelExportWriter.h" #include #include diff --git a/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperXModel.h b/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperXModel.h index 66569386..a304d3b9 100644 --- a/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperXModel.h +++ b/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperXModel.h @@ -2,9 +2,9 @@ #include "Dumping/AbstractAssetDumper.h" #include "Game/IW3/IW3.h" -#include "Model/Obj/ObjWriter.h" -#include "Model/XModel/AbstractXModelWriter.h" #include "Utils/DistinctMapper.h" +#include "XModel/AbstractXModelWriter.h" +#include "XModel/Obj/ObjWriter.h" namespace IW3 { diff --git a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperXModel.cpp b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperXModel.cpp index 43f565cf..da956c9b 100644 --- a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperXModel.cpp +++ b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperXModel.cpp @@ -2,10 +2,10 @@ #include "Game/IW4/CommonIW4.h" #include "Math/Quaternion.h" -#include "Model/XModel/XModelExportWriter.h" #include "ObjWriting.h" #include "Utils/HalfFloat.h" #include "Utils/QuatInt16.h" +#include "XModel/Export/XModelExportWriter.h" #include diff --git a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperXModel.h b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperXModel.h index 647ce28c..0d2b058d 100644 --- a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperXModel.h +++ b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperXModel.h @@ -2,9 +2,9 @@ #include "Dumping/AbstractAssetDumper.h" #include "Game/IW4/IW4.h" -#include "Model/Obj/ObjWriter.h" -#include "Model/XModel/AbstractXModelWriter.h" #include "Utils/DistinctMapper.h" +#include "XModel/AbstractXModelWriter.h" +#include "XModel/Obj/ObjWriter.h" namespace IW4 { diff --git a/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperXModel.cpp b/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperXModel.cpp index 865831ca..b5aef51c 100644 --- a/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperXModel.cpp +++ b/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperXModel.cpp @@ -2,10 +2,10 @@ #include "Game/IW5/CommonIW5.h" #include "Math/Quaternion.h" -#include "Model/XModel/XModelExportWriter.h" #include "ObjWriting.h" #include "Utils/HalfFloat.h" #include "Utils/QuatInt16.h" +#include "XModel/Export/XModelExportWriter.h" #include diff --git a/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperXModel.h b/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperXModel.h index 458caba8..6a6196a5 100644 --- a/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperXModel.h +++ b/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperXModel.h @@ -2,9 +2,9 @@ #include "Dumping/AbstractAssetDumper.h" #include "Game/IW5/IW5.h" -#include "Model/Obj/ObjWriter.h" -#include "Model/XModel/AbstractXModelWriter.h" #include "Utils/DistinctMapper.h" +#include "XModel/AbstractXModelWriter.h" +#include "XModel/Obj/ObjWriter.h" namespace IW5 { diff --git a/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperXModel.cpp b/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperXModel.cpp index 5997be6b..ef75590a 100644 --- a/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperXModel.cpp +++ b/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperXModel.cpp @@ -2,10 +2,10 @@ #include "Game/T5/CommonT5.h" #include "Math/Quaternion.h" -#include "Model/XModel/XModelExportWriter.h" #include "ObjWriting.h" #include "Utils/HalfFloat.h" #include "Utils/QuatInt16.h" +#include "XModel/Export/XModelExportWriter.h" #include #include diff --git a/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperXModel.h b/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperXModel.h index bd96b00e..155d79d0 100644 --- a/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperXModel.h +++ b/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperXModel.h @@ -2,9 +2,9 @@ #include "Dumping/AbstractAssetDumper.h" #include "Game/T5/T5.h" -#include "Model/Obj/ObjWriter.h" -#include "Model/XModel/AbstractXModelWriter.h" #include "Utils/DistinctMapper.h" +#include "XModel/AbstractXModelWriter.h" +#include "XModel/Obj/ObjWriter.h" namespace T5 { diff --git a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperXModel.cpp b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperXModel.cpp index 03884831..712cf309 100644 --- a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperXModel.cpp +++ b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperXModel.cpp @@ -2,10 +2,10 @@ #include "Game/T6/CommonT6.h" #include "Math/Quaternion.h" -#include "Model/XModel/XModelExportWriter.h" #include "ObjWriting.h" #include "Utils/HalfFloat.h" #include "Utils/QuatInt16.h" +#include "XModel/Export/XModelExportWriter.h" #include #include diff --git a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperXModel.h b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperXModel.h index a39bdaff..fd56c376 100644 --- a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperXModel.h +++ b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperXModel.h @@ -2,9 +2,9 @@ #include "Dumping/AbstractAssetDumper.h" #include "Game/T6/T6.h" -#include "Model/Obj/ObjWriter.h" -#include "Model/XModel/AbstractXModelWriter.h" #include "Utils/DistinctMapper.h" +#include "XModel/AbstractXModelWriter.h" +#include "XModel/Obj/ObjWriter.h" namespace T6 { diff --git a/src/ObjWriting/Model/XModel/AbstractXModelWriter.cpp b/src/ObjWriting/XModel/AbstractXModelWriter.cpp similarity index 100% rename from src/ObjWriting/Model/XModel/AbstractXModelWriter.cpp rename to src/ObjWriting/XModel/AbstractXModelWriter.cpp diff --git a/src/ObjWriting/Model/XModel/AbstractXModelWriter.h b/src/ObjWriting/XModel/AbstractXModelWriter.h similarity index 94% rename from src/ObjWriting/Model/XModel/AbstractXModelWriter.h rename to src/ObjWriting/XModel/AbstractXModelWriter.h index 277b5282..e825d92a 100644 --- a/src/ObjWriting/Model/XModel/AbstractXModelWriter.h +++ b/src/ObjWriting/XModel/AbstractXModelWriter.h @@ -1,6 +1,6 @@ #pragma once -#include "Model/XModel/XModelCommon.h" +#include "XModel/XModelCommon.h" #include diff --git a/src/ObjWriting/Model/XModel/XModelBinWriter.cpp b/src/ObjWriting/XModel/Export/XModelBinWriter.cpp similarity index 100% rename from src/ObjWriting/Model/XModel/XModelBinWriter.cpp rename to src/ObjWriting/XModel/Export/XModelBinWriter.cpp diff --git a/src/ObjWriting/Model/XModel/XModelBinWriter.h b/src/ObjWriting/XModel/Export/XModelBinWriter.h similarity index 100% rename from src/ObjWriting/Model/XModel/XModelBinWriter.h rename to src/ObjWriting/XModel/Export/XModelBinWriter.h diff --git a/src/ObjWriting/Model/XModel/XModelExportWriter.cpp b/src/ObjWriting/XModel/Export/XModelExportWriter.cpp similarity index 100% rename from src/ObjWriting/Model/XModel/XModelExportWriter.cpp rename to src/ObjWriting/XModel/Export/XModelExportWriter.cpp diff --git a/src/ObjWriting/Model/XModel/XModelExportWriter.h b/src/ObjWriting/XModel/Export/XModelExportWriter.h similarity index 94% rename from src/ObjWriting/Model/XModel/XModelExportWriter.h rename to src/ObjWriting/XModel/Export/XModelExportWriter.h index 5535f579..175bc1ef 100644 --- a/src/ObjWriting/Model/XModel/XModelExportWriter.h +++ b/src/ObjWriting/XModel/Export/XModelExportWriter.h @@ -1,6 +1,6 @@ #pragma once -#include "AbstractXModelWriter.h" +#include "XModel/AbstractXModelWriter.h" #include #include diff --git a/src/ObjWriting/Model/Obj/ObjWriter.cpp b/src/ObjWriting/XModel/Obj/ObjWriter.cpp similarity index 100% rename from src/ObjWriting/Model/Obj/ObjWriter.cpp rename to src/ObjWriting/XModel/Obj/ObjWriter.cpp diff --git a/src/ObjWriting/Model/Obj/ObjWriter.h b/src/ObjWriting/XModel/Obj/ObjWriter.h similarity index 97% rename from src/ObjWriting/Model/Obj/ObjWriter.h rename to src/ObjWriting/XModel/Obj/ObjWriter.h index 157ffc87..8f809282 100644 --- a/src/ObjWriting/Model/Obj/ObjWriter.h +++ b/src/ObjWriting/XModel/Obj/ObjWriter.h @@ -1,7 +1,7 @@ #pragma once -#include "Model/Obj/ObjCommon.h" #include "Utils/DistinctMapper.h" +#include "XModel/Obj/ObjCommon.h" #include #include From 7a0930a208cbcaa350ec60c4b48824ec95f0aed5 Mon Sep 17 00:00:00 2001 From: Jan Date: Sun, 31 Mar 2024 13:07:06 +0200 Subject: [PATCH 04/25] chore: add json types for gltf --- src/ObjCommon/XModel/Gltf/JsonGltf.h | 256 +++++++++++++++++++++++++++ 1 file changed, 256 insertions(+) create mode 100644 src/ObjCommon/XModel/Gltf/JsonGltf.h diff --git a/src/ObjCommon/XModel/Gltf/JsonGltf.h b/src/ObjCommon/XModel/Gltf/JsonGltf.h new file mode 100644 index 00000000..5922df88 --- /dev/null +++ b/src/ObjCommon/XModel/Gltf/JsonGltf.h @@ -0,0 +1,256 @@ +#pragma once + +#include "Json/JsonExtension.h" +#include +#include +#include +#include + +namespace gltf +{ + class JsonAsset + { + public: + std::string version; + std::optional generator; + }; + + NLOHMANN_DEFINE_TYPE_EXTENSION(JsonAsset, version, generator); + + class JsonNode + { + public: + std::string name; + std::vector translation; + std::vector rotation; + std::vector scale; + std::vector children; + }; + + NLOHMANN_DEFINE_TYPE_EXTENSION(JsonNode, name, translation, rotation, scale, children); + + class JsonBuffer + { + public: + unsigned byteLength; + std::optional uri; + }; + + NLOHMANN_DEFINE_TYPE_EXTENSION(JsonBuffer, byteLength, uri); + + enum class JsonAccessorComponentType + { + SIGNED_BYTE = 5120, + UNSIGNED_BYTE = 5121, + SIGNED_SHORT = 5122, + UNSIGNED_SHORT = 5123, + UNSIGNED_INT = 5125, + FLOAT = 5126 + }; + + NLOHMANN_JSON_SERIALIZE_ENUM(JsonAccessorComponentType, + { + {JsonAccessorComponentType::SIGNED_BYTE, static_cast(JsonAccessorComponentType::SIGNED_BYTE) }, + {JsonAccessorComponentType::UNSIGNED_BYTE, static_cast(JsonAccessorComponentType::UNSIGNED_BYTE) }, + {JsonAccessorComponentType::SIGNED_SHORT, static_cast(JsonAccessorComponentType::SIGNED_SHORT) }, + {JsonAccessorComponentType::UNSIGNED_SHORT, static_cast(JsonAccessorComponentType::UNSIGNED_SHORT)}, + {JsonAccessorComponentType::UNSIGNED_INT, static_cast(JsonAccessorComponentType::UNSIGNED_INT) }, + {JsonAccessorComponentType::FLOAT, static_cast(JsonAccessorComponentType::FLOAT) }, + }); + + enum class JsonAccessorType + { + SCALAR, + VEC2, + VEC3, + VEC4, + MAT2, + MAT3, + MAT4 + }; + + NLOHMANN_JSON_SERIALIZE_ENUM(JsonAccessorType, + { + {JsonAccessorType::SCALAR, "SCALAR"}, + {JsonAccessorType::VEC2, "VEC2" }, + {JsonAccessorType::VEC3, "VEC3" }, + {JsonAccessorType::VEC4, "VEC4" }, + {JsonAccessorType::MAT2, "MAT2" }, + {JsonAccessorType::MAT3, "MAT3" }, + {JsonAccessorType::MAT4, "MAT4" }, + }); + + class JsonAccessor + { + public: + std::optional bufferView; + std::optional byteOffset; + JsonAccessorComponentType componentType; + // std::optional normalized + unsigned count; + JsonAccessorType type; + std::optional> max; + std::optional> min; + // std::optional sparse; + // std::optional name; + // extensions + // extras + }; + + NLOHMANN_DEFINE_TYPE_EXTENSION(JsonAccessor, bufferView, byteOffset, componentType, count, type, min, max); + + class JsonBufferView + { + public: + unsigned buffer; + unsigned byteLength; + unsigned byteOffset; + unsigned target; + }; + + NLOHMANN_DEFINE_TYPE_EXTENSION(JsonBufferView, buffer, byteLength, byteOffset, target); + + enum class JsonAnimationChannelTargetPath + { + TRANSLATION, + ROTATION, + SCALE, + WEIGHTS + }; + + NLOHMANN_JSON_SERIALIZE_ENUM(JsonAnimationChannelTargetPath, + { + {JsonAnimationChannelTargetPath::TRANSLATION, "translation"}, + {JsonAnimationChannelTargetPath::ROTATION, "rotation" }, + {JsonAnimationChannelTargetPath::SCALE, "scale" }, + {JsonAnimationChannelTargetPath::WEIGHTS, "weights" }, + }); + + class JsonAnimationChannelTarget + { + public: + unsigned node; + JsonAnimationChannelTargetPath path; + }; + + NLOHMANN_DEFINE_TYPE_EXTENSION(JsonAnimationChannelTarget, node, path); + + class JsonAnimationChannel + { + public: + unsigned sampler; + JsonAnimationChannelTarget target; + }; + + NLOHMANN_DEFINE_TYPE_EXTENSION(JsonAnimationChannel, sampler, target); + + enum class JsonAnimationSamplerInterpolation + { + LINEAR, + STEP, + CUBIC_SPLINE + }; + + NLOHMANN_JSON_SERIALIZE_ENUM(JsonAnimationSamplerInterpolation, + { + {JsonAnimationSamplerInterpolation::LINEAR, "LINEAR" }, + {JsonAnimationSamplerInterpolation::STEP, "STEP" }, + {JsonAnimationSamplerInterpolation::CUBIC_SPLINE, "CUBICSPLINE"}, + }); + + class JsonAnimationSampler + { + public: + unsigned input; + std::optional interpolation; + unsigned output; + }; + + NLOHMANN_DEFINE_TYPE_EXTENSION(JsonAnimationSampler, input); + + class JsonAnimation + { + public: + std::vector channels; + std::vector samplers; + std::optional name; + }; + + NLOHMANN_DEFINE_TYPE_EXTENSION(JsonAnimation, channels, samplers, name); + + class JsonMaterial + { + public: + std::optional name; + }; + + NLOHMANN_DEFINE_TYPE_EXTENSION(JsonMaterial, name); + + enum class JsonMeshPrimitivesMode + { + POINTS = 0, + LINES = 1, + LINE_LOOP = 2, + LINE_STRIP = 3, + TRIANGLES = 4, + TRIANGLES_STRIP = 5, + TRIANGLE_FAN = 6 + }; + + NLOHMANN_JSON_SERIALIZE_ENUM(JsonMeshPrimitivesMode, + { + {JsonMeshPrimitivesMode::POINTS, static_cast(JsonMeshPrimitivesMode::POINTS) }, + {JsonMeshPrimitivesMode::LINES, static_cast(JsonMeshPrimitivesMode::LINES) }, + {JsonMeshPrimitivesMode::LINE_LOOP, static_cast(JsonMeshPrimitivesMode::LINE_LOOP) }, + {JsonMeshPrimitivesMode::LINE_STRIP, static_cast(JsonMeshPrimitivesMode::LINE_STRIP) }, + {JsonMeshPrimitivesMode::TRIANGLES, static_cast(JsonMeshPrimitivesMode::TRIANGLES) }, + {JsonMeshPrimitivesMode::TRIANGLES_STRIP, static_cast(JsonMeshPrimitivesMode::TRIANGLES_STRIP)}, + {JsonMeshPrimitivesMode::TRIANGLE_FAN, static_cast(JsonMeshPrimitivesMode::TRIANGLE_FAN) }, + }); + + class JsonMeshPrimitives + { + public: + std::unordered_map attributes; + std::optional indices; + std::optional material; + std::optional mode; + }; + + NLOHMANN_DEFINE_TYPE_EXTENSION(JsonMeshPrimitives, attributes, indices, material, mode); + + class JsonMesh + { + public: + std::vector primitives; + std::optional> weights; + }; + + NLOHMANN_DEFINE_TYPE_EXTENSION(JsonMesh, primitives, weights); + + class JsonSkin + { + public: + std::optional inverseBindMatrices; + std::optional skeleton; + std::vector joints; + }; + + NLOHMANN_DEFINE_TYPE_EXTENSION(JsonSkin, inverseBindMatrices, skeleton, joints); + + class JsonRoot + { + public: + std::vector accessors; + std::vector animations; + JsonAsset asset; + std::vector buffers; + std::vector bufferViews; + std::vector materials; + std::vector meshes; + std::vector nodes; + std::vector skins; + }; + + NLOHMANN_DEFINE_TYPE_EXTENSION(JsonRoot, asset); +} // namespace gltf From c27f4ed544b423cec0cda4c85a9c018611126a5a Mon Sep 17 00:00:00 2001 From: Jan Date: Sun, 31 Mar 2024 22:46:31 +0200 Subject: [PATCH 05/25] chore: add output implementations for gltf and glb --- src/ObjCommon/XModel/Gltf/GltfConstants.h | 20 +++++ src/ObjWriting.lua | 2 + src/ObjWriting/XModel/Gltf/GltfBinOutput.cpp | 86 +++++++++++++++++++ src/ObjWriting/XModel/Gltf/GltfBinOutput.h | 25 ++++++ src/ObjWriting/XModel/Gltf/GltfOutput.h | 25 ++++++ src/ObjWriting/XModel/Gltf/GltfTextOutput.cpp | 48 +++++++++++ src/ObjWriting/XModel/Gltf/GltfTextOutput.h | 22 +++++ 7 files changed, 228 insertions(+) create mode 100644 src/ObjCommon/XModel/Gltf/GltfConstants.h create mode 100644 src/ObjWriting/XModel/Gltf/GltfBinOutput.cpp create mode 100644 src/ObjWriting/XModel/Gltf/GltfBinOutput.h create mode 100644 src/ObjWriting/XModel/Gltf/GltfOutput.h create mode 100644 src/ObjWriting/XModel/Gltf/GltfTextOutput.cpp create mode 100644 src/ObjWriting/XModel/Gltf/GltfTextOutput.h diff --git a/src/ObjCommon/XModel/Gltf/GltfConstants.h b/src/ObjCommon/XModel/Gltf/GltfConstants.h new file mode 100644 index 00000000..e8d3001a --- /dev/null +++ b/src/ObjCommon/XModel/Gltf/GltfConstants.h @@ -0,0 +1,20 @@ +#pragma once + +#include "Utils/FileUtils.h" + +#include + +namespace gltf +{ + constexpr uint32_t GLTF_MAGIC = FileUtils::MakeMagic32('g', 'l', 'T', 'F'); + constexpr uint32_t GLTF_VERSION = 2u; + + constexpr uint32_t CHUNK_MAGIC_JSON = FileUtils::MakeMagic32('J', 'S', 'O', 'N'); + constexpr uint32_t CHUNK_MAGIC_BIN = FileUtils::MakeMagic32('B', 'I', 'N', '\x00'); + + constexpr auto GLTF_LENGTH_OFFSET = 8u; + constexpr auto GLTF_JSON_CHUNK_LENGTH_OFFSET = 12u; + constexpr auto GLTF_JSON_CHUNK_DATA_OFFSET = 20u; + + constexpr auto GLTF_DATA_URI_PREFIX = "data:application/octet-stream;base64,"; +} // namespace gltf diff --git a/src/ObjWriting.lua b/src/ObjWriting.lua index 8505cd8b..fa796ae8 100644 --- a/src/ObjWriting.lua +++ b/src/ObjWriting.lua @@ -19,6 +19,7 @@ function ObjWriting:link(links) links:linkto(ZoneCommon) links:linkto(minilzo) links:linkto(minizip) + links:linkto(libtomcrypt) end function ObjWriting:use() @@ -55,5 +56,6 @@ function ObjWriting:project() minilzo:include(includes) minizip:include(includes) json:include(includes) + libtomcrypt:include(includes) end diff --git a/src/ObjWriting/XModel/Gltf/GltfBinOutput.cpp b/src/ObjWriting/XModel/Gltf/GltfBinOutput.cpp new file mode 100644 index 00000000..7bce3561 --- /dev/null +++ b/src/ObjWriting/XModel/Gltf/GltfBinOutput.cpp @@ -0,0 +1,86 @@ +#include "GltfBinOutput.h" + +#include "Utils/Alignment.h" +#include "XModel/Gltf/GltfConstants.h" + +#include +#include + +using namespace gltf; + +BinOutput::BinOutput(std::ostream& stream) + : m_stream(stream) +{ +} + +void BinOutput::Write(const void* data, const size_t dataSize) const +{ + m_stream.write(static_cast(data), dataSize); +} + +void BinOutput::AlignToFour(const char value) const +{ + const auto offset = m_stream.tellp(); + if (offset % 4 > 0) + { + const uint32_t alignmentValue = FileUtils::MakeMagic32(value, value, value, value); + Write(&alignmentValue, 4u - (offset % 4u)); + } +} + +std::optional BinOutput::CreateBufferUri(const void* buffer, size_t bufferSize) const +{ + return std::nullopt; +} + +void BinOutput::EmitJson(const nlohmann::json& json) const +{ + static constexpr uint32_t ZERO = 0u; + + Write(&GLTF_MAGIC, sizeof(GLTF_MAGIC)); + Write(&GLTF_VERSION, sizeof(GLTF_VERSION)); + + // File length will be filled later + Write(&ZERO, sizeof(ZERO)); + + // Chunk length will be filled after writing json + Write(&ZERO, sizeof(ZERO)); + Write(&CHUNK_MAGIC_JSON, sizeof(CHUNK_MAGIC_JSON)); + + m_stream << json; + + AlignToFour(' '); + + const auto offsetAfterData = m_stream.tellp(); + const auto jsonDataLength = static_cast(static_cast(offsetAfterData) - GLTF_JSON_CHUNK_DATA_OFFSET); + + // Fill chunk length now + m_stream.seekp(GLTF_JSON_CHUNK_LENGTH_OFFSET, std::ios::beg); + Write(&jsonDataLength, sizeof(jsonDataLength)); + + // Return to previous pos + m_stream.seekp(offsetAfterData, std::ios::beg); +} + +void BinOutput::EmitBuffer(const void* buffer, const size_t bufferSize) const +{ + const auto chunkLength = utils::Align(bufferSize, 4u); + Write(&chunkLength, sizeof(chunkLength)); + Write(&CHUNK_MAGIC_BIN, sizeof(CHUNK_MAGIC_BIN)); + + Write(buffer, bufferSize); + AlignToFour('\0'); +} + +void BinOutput::Finalize() const +{ + const auto fileEndOffset = m_stream.tellp(); + const auto fileSize = static_cast(fileEndOffset); + + // Fill chunk length now + m_stream.seekp(GLTF_LENGTH_OFFSET, std::ios::beg); + Write(&fileSize, sizeof(fileSize)); + + // Return to file end + m_stream.seekp(fileEndOffset, std::ios::beg); +} diff --git a/src/ObjWriting/XModel/Gltf/GltfBinOutput.h b/src/ObjWriting/XModel/Gltf/GltfBinOutput.h new file mode 100644 index 00000000..91eacc75 --- /dev/null +++ b/src/ObjWriting/XModel/Gltf/GltfBinOutput.h @@ -0,0 +1,25 @@ +#pragma once + +#include "GltfOutput.h" + +#include + +namespace gltf +{ + class BinOutput final : public Output + { + public: + explicit BinOutput(std::ostream& stream); + + std::optional CreateBufferUri(const void* buffer, size_t bufferSize) const override; + void EmitJson(const nlohmann::json& json) const override; + void EmitBuffer(const void* buffer, size_t bufferSize) const override; + void Finalize() const override; + + private: + void Write(const void* data, size_t dataSize) const; + void AlignToFour(char value) const; + + std::ostream& m_stream; + }; +} // namespace gltf diff --git a/src/ObjWriting/XModel/Gltf/GltfOutput.h b/src/ObjWriting/XModel/Gltf/GltfOutput.h new file mode 100644 index 00000000..8cc7a3da --- /dev/null +++ b/src/ObjWriting/XModel/Gltf/GltfOutput.h @@ -0,0 +1,25 @@ +#pragma once + +#include +#include +#include + +namespace gltf +{ + class Output + { + protected: + Output() = default; + virtual ~Output() = default; + Output(const Output& other) = default; + Output(Output&& other) noexcept = default; + Output& operator=(const Output& other) = default; + Output& operator=(Output&& other) noexcept = default; + + public: + virtual std::optional CreateBufferUri(const void* buffer, size_t bufferSize) const = 0; + virtual void EmitJson(const nlohmann::json& json) const = 0; + virtual void EmitBuffer(const void* buffer, size_t bufferSize) const = 0; + virtual void Finalize() const = 0; + }; +} // namespace gltf diff --git a/src/ObjWriting/XModel/Gltf/GltfTextOutput.cpp b/src/ObjWriting/XModel/Gltf/GltfTextOutput.cpp new file mode 100644 index 00000000..7547b3d2 --- /dev/null +++ b/src/ObjWriting/XModel/Gltf/GltfTextOutput.cpp @@ -0,0 +1,48 @@ +#include "GltfTextOutput.h" + +#include "Utils/Alignment.h" +#include "XModel/Gltf/GltfConstants.h" + +#include +#include +#include + +using namespace gltf; + +TextOutput::TextOutput(std::ostream& stream) + : m_stream(stream) +{ +} + +std::optional TextOutput::CreateBufferUri(const void* buffer, const size_t bufferSize) const +{ + static constexpr auto URI_PREFIX_LENGTH = std::char_traits::length(GLTF_DATA_URI_PREFIX); + const auto base64Length = utils::Align(4u * (bufferSize / 3u), 4u); + const auto base64BufferSize = URI_PREFIX_LENGTH + base64Length; + + std::string output(base64BufferSize, '\0'); + + std::memcpy(output.data(), &GLTF_DATA_URI_PREFIX, URI_PREFIX_LENGTH); + + unsigned long outLength = base64Length; + base64_encode(static_cast(buffer), bufferSize, &output[URI_PREFIX_LENGTH], &outLength); + + assert(outLength == base64Length); + + return output; +} + +void TextOutput::EmitJson(const nlohmann::json& json) const +{ + m_stream << std::setw(4) << json; +} + +void TextOutput::EmitBuffer(const void* buffer, const size_t bufferSize) const +{ + // Nothing to do +} + +void TextOutput::Finalize() const +{ + // Nothing to do +} diff --git a/src/ObjWriting/XModel/Gltf/GltfTextOutput.h b/src/ObjWriting/XModel/Gltf/GltfTextOutput.h new file mode 100644 index 00000000..d6b9d7b9 --- /dev/null +++ b/src/ObjWriting/XModel/Gltf/GltfTextOutput.h @@ -0,0 +1,22 @@ +#pragma once + +#include "GltfOutput.h" + +#include + +namespace gltf +{ + class TextOutput final : public Output + { + public: + explicit TextOutput(std::ostream& stream); + + std::optional CreateBufferUri(const void* buffer, size_t bufferSize) const override; + void EmitJson(const nlohmann::json& json) const override; + void EmitBuffer(const void* buffer, size_t bufferSize) const override; + void Finalize() const override; + + private: + std::ostream& m_stream; + }; +} // namespace gltf From f2438bea122d5ba5f7a144126b08265a53f90325 Mon Sep 17 00:00:00 2001 From: Jan Date: Sat, 30 Mar 2024 17:12:48 +0100 Subject: [PATCH 06/25] chore: write gltf header with new writers and outputs --- src/ObjCommon/XModel/Gltf/GltfConstants.h | 1 + src/ObjWriting/XModel/Gltf/GltfWriter.cpp | 45 +++++++++++++++++++++++ src/ObjWriting/XModel/Gltf/GltfWriter.h | 26 +++++++++++++ 3 files changed, 72 insertions(+) create mode 100644 src/ObjWriting/XModel/Gltf/GltfWriter.cpp create mode 100644 src/ObjWriting/XModel/Gltf/GltfWriter.h diff --git a/src/ObjCommon/XModel/Gltf/GltfConstants.h b/src/ObjCommon/XModel/Gltf/GltfConstants.h index e8d3001a..1ce687d0 100644 --- a/src/ObjCommon/XModel/Gltf/GltfConstants.h +++ b/src/ObjCommon/XModel/Gltf/GltfConstants.h @@ -8,6 +8,7 @@ namespace gltf { constexpr uint32_t GLTF_MAGIC = FileUtils::MakeMagic32('g', 'l', 'T', 'F'); constexpr uint32_t GLTF_VERSION = 2u; + constexpr auto GLTF_VERSION_STRING = "2.0"; constexpr uint32_t CHUNK_MAGIC_JSON = FileUtils::MakeMagic32('J', 'S', 'O', 'N'); constexpr uint32_t CHUNK_MAGIC_BIN = FileUtils::MakeMagic32('B', 'I', 'N', '\x00'); diff --git a/src/ObjWriting/XModel/Gltf/GltfWriter.cpp b/src/ObjWriting/XModel/Gltf/GltfWriter.cpp new file mode 100644 index 00000000..3aa03f9b --- /dev/null +++ b/src/ObjWriting/XModel/Gltf/GltfWriter.cpp @@ -0,0 +1,45 @@ +#include "GltfWriter.h" + +#include "GitVersion.h" +#include "XModel/Gltf/GltfConstants.h" +#include "XModel/Gltf/JsonGltf.h" + +using namespace gltf; +using namespace nlohmann; + +namespace +{ + constexpr auto GLTF_GENERATOR = "OpenAssetTools " GIT_VERSION; + + class GltfWriterImpl final : public gltf::Writer + { + public: + GltfWriterImpl(const Output* output, std::string gameName, std::string zoneName) + : m_output(output), + m_game_name(std::move(gameName)), + m_zone_name(std::move(zoneName)) + { + } + + void Write(std::ostream& stream) override + { + JsonRoot gltf; + gltf.asset.version = GLTF_VERSION_STRING; + gltf.asset.generator = GLTF_GENERATOR; + + const json jRoot = gltf; + m_output->EmitJson(jRoot); + m_output->Finalize(); + } + + private: + const Output* m_output; + std::string m_game_name; + std::string m_zone_name; + }; +} // namespace + +std::unique_ptr Writer::CreateWriter(const Output* output, std::string gameName, std::string zoneName) +{ + return std::make_unique(output, std::move(gameName), std::move(zoneName)); +} diff --git a/src/ObjWriting/XModel/Gltf/GltfWriter.h b/src/ObjWriting/XModel/Gltf/GltfWriter.h new file mode 100644 index 00000000..3d69d77e --- /dev/null +++ b/src/ObjWriting/XModel/Gltf/GltfWriter.h @@ -0,0 +1,26 @@ +#pragma once + +#include "GltfOutput.h" +#include "XModel/AbstractXModelWriter.h" +#include "XModel/Gltf/JsonGltf.h" + +#include +#include + +namespace gltf +{ + class Writer : public AbstractXModelWriter + { + public: + Writer() = default; + virtual ~Writer() = default; + Writer(const Writer& other) = default; + Writer(Writer&& other) noexcept = default; + Writer& operator=(const Writer& other) = default; + Writer& operator=(Writer&& other) noexcept = default; + + virtual void Write(std::ostream& stream) = 0; + + static std::unique_ptr CreateWriter(const Output* output, std::string gameName, std::string zoneName); + }; +} // namespace gltf From 8a0c93d3d87b7c4a4165f91706dfe2c51910edfd Mon Sep 17 00:00:00 2001 From: Jan Date: Mon, 1 Apr 2024 01:45:29 +0200 Subject: [PATCH 07/25] feat: add gltf and glb as model dumping formats --- .../IW3/AssetDumpers/AssetDumperXModel.cpp | 1170 ++++++++-------- .../Game/IW3/AssetDumpers/AssetDumperXModel.h | 26 - .../IW4/AssetDumpers/AssetDumperXModel.cpp | 1141 +++++++-------- .../Game/IW4/AssetDumpers/AssetDumperXModel.h | 26 - .../IW5/AssetDumpers/AssetDumperXModel.cpp | 1139 +++++++-------- .../Game/IW5/AssetDumpers/AssetDumperXModel.h | 26 - .../T5/AssetDumpers/AssetDumperXModel.cpp | 1170 ++++++++-------- .../Game/T5/AssetDumpers/AssetDumperXModel.h | 26 - .../T6/AssetDumpers/AssetDumperXModel.cpp | 1218 +++++++++-------- .../Game/T6/AssetDumpers/AssetDumperXModel.h | 26 - src/ObjWriting/ObjWriting.h | 6 +- src/Unlinker/UnlinkerArgs.cpp | 24 +- 12 files changed, 2996 insertions(+), 3002 deletions(-) diff --git a/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperXModel.cpp b/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperXModel.cpp index 776f68bb..1902ca10 100644 --- a/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperXModel.cpp +++ b/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperXModel.cpp @@ -3,591 +3,617 @@ #include "Game/IW3/CommonIW3.h" #include "Math/Quaternion.h" #include "ObjWriting.h" +#include "Utils/DistinctMapper.h" #include "Utils/HalfFloat.h" #include "Utils/QuatInt16.h" +#include "XModel/AbstractXModelWriter.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 -#include +#include 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 AddObjMaterials(ObjWriter& writer, DistinctMapper& materialMapper, const XModel* model) + { + if (!model->materialHandles) + return; + + for (auto surfIndex = 0u; surfIndex < model->numsurfs; surfIndex++) + { + Material* material = model->materialHandles[surfIndex]; + if (!materialMapper.Add(material)) + continue; + + MtlMaterial mtl; + mtl.materialName = std::string(material->info.name); + + GfxImage* colorMap = GetMaterialColorMap(material); + GfxImage* normalMap = GetMaterialNormalMap(material); + GfxImage* specularMap = GetMaterialSpecularMap(material); + + if (colorMap != nullptr) + mtl.colorMapName = colorMap->name; + if (normalMap != nullptr) + mtl.normalMapName = normalMap->name; + if (specularMap != nullptr) + mtl.specularMapName = specularMap->name; + + writer.AddMaterial(std::move(mtl)); + } + } + + void AddObjObjects(ObjWriter& writer, const DistinctMapper& materialMapper, const XModel* model, const unsigned lod) + { + const auto surfCount = model->lodInfo[lod].numsurfs; + const auto baseSurfIndex = model->lodInfo[lod].surfIndex; + + for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++) + { + ObjObject object; + object.name = "surf" + std::to_string(surfIndex); + object.materialIndex = static_cast(materialMapper.GetDistinctPositionByInputPosition(surfIndex + baseSurfIndex)); + + writer.AddObject(std::move(object)); + } + } + + void AddObjVertices(ObjWriter& writer, const XModel* model, const unsigned lod) + { + const auto* surfs = &model->surfs[model->lodInfo[lod].surfIndex]; + const auto surfCount = model->lodInfo[lod].numsurfs; + + for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++) + { + const auto& surface = surfs[surfIndex]; + + for (auto vertexIndex = 0u; vertexIndex < surface.vertCount; vertexIndex++) + { + const auto& v = surface.verts0[vertexIndex]; + vec2_t uv; + vec3_t normalVec; + + Common::Vec2UnpackTexCoords(v.texCoord, &uv); + Common::Vec3UnpackUnitVec(v.normal, &normalVec); + + ObjVertex objVertex{}; + ObjNormal objNormal{}; + ObjUv objUv{}; + objVertex.coordinates[0] = v.xyz[0]; + objVertex.coordinates[1] = v.xyz[2]; + objVertex.coordinates[2] = -v.xyz[1]; + objNormal.normal[0] = normalVec[0]; + objNormal.normal[1] = normalVec[2]; + objNormal.normal[2] = -normalVec[1]; + objUv.uv[0] = uv[0]; + objUv.uv[1] = 1.0f - uv[1]; + + writer.AddVertex(static_cast(surfIndex), objVertex); + writer.AddNormal(static_cast(surfIndex), objNormal); + writer.AddUv(static_cast(surfIndex), objUv); + } + } + } + + void AddObjFaces(ObjWriter& writer, const XModel* model, const unsigned lod) + { + const auto* surfs = &model->surfs[model->lodInfo[lod].surfIndex]; + const auto surfCount = model->lodInfo[lod].numsurfs; + + for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++) + { + const auto& surface = surfs[surfIndex]; + for (auto triIndex = 0u; triIndex < surface.triCount; triIndex++) + { + const auto& tri = surface.triIndices[triIndex]; + + ObjFace face{}; + face.vertexIndex[0] = tri[2] + surface.baseVertIndex; + face.vertexIndex[1] = tri[1] + surface.baseVertIndex; + face.vertexIndex[2] = tri[0] + surface.baseVertIndex; + face.normalIndex[0] = face.vertexIndex[0]; + face.normalIndex[1] = face.vertexIndex[1]; + face.normalIndex[2] = face.vertexIndex[2]; + face.uvIndex[0] = face.vertexIndex[0]; + face.uvIndex[1] = face.vertexIndex[1]; + face.uvIndex[2] = face.vertexIndex[2]; + writer.AddFace(static_cast(surfIndex), face); + } + } + } + + void DumpObjMat(const AssetDumpingContext& context, const XAssetInfo* asset) + { + const auto* model = asset->Asset(); + const auto matFile = context.OpenAssetFile(std::format("model_export/{}.mtl", model->name)); + + if (!matFile) + return; + + ObjWriter writer(context.m_zone->m_game->GetShortName(), context.m_zone->m_name); + DistinctMapper materialMapper(model->numsurfs); + + AddObjMaterials(writer, materialMapper, model); + writer.WriteMtl(*matFile); + } + + void DumpObjLod(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; + + ObjWriter writer(context.m_zone->m_game->GetShortName(), context.m_zone->m_name); + DistinctMapper materialMapper(model->numsurfs); + + AddObjMaterials(writer, materialMapper, model); + AddObjObjects(writer, materialMapper, model, lod); + AddObjVertices(writer, model, lod); + AddObjFaces(writer, model, lod); + + writer.WriteObj(*assetFile, std::format("{}.mtl", model->name)); + } + + void AddXModelBones(const AssetDumpingContext& context, AbstractXModelWriter& writer, const XModel* model) + { + for (auto boneNum = 0u; boneNum < model->numBones; boneNum++) + { + XModelBone bone; + if (model->boneNames[boneNum] < context.m_zone->m_script_strings.Count()) + bone.name = context.m_zone->m_script_strings[model->boneNames[boneNum]]; + else + bone.name = "INVALID_BONE_NAME"; + + if (boneNum < model->numRootBones) + bone.parentIndex = -1; + else + bone.parentIndex = static_cast(boneNum - static_cast(model->parentList[boneNum - model->numRootBones])); + + bone.scale[0] = 1.0f; + bone.scale[1] = 1.0f; + bone.scale[2] = 1.0f; + + bone.globalOffset[0] = model->baseMat[boneNum].trans[0]; + bone.globalOffset[1] = model->baseMat[boneNum].trans[1]; + bone.globalOffset[2] = model->baseMat[boneNum].trans[2]; + bone.globalRotation = Quaternion32( + model->baseMat[boneNum].quat[0], model->baseMat[boneNum].quat[1], model->baseMat[boneNum].quat[2], model->baseMat[boneNum].quat[3]); + + if (boneNum < model->numRootBones) + { + bone.localOffset[0] = 0; + bone.localOffset[1] = 0; + bone.localOffset[2] = 0; + bone.localRotation = Quaternion32(0, 0, 0, 1); + } + else + { + bone.localOffset[0] = model->trans[boneNum - model->numRootBones][0]; + bone.localOffset[1] = model->trans[boneNum - model->numRootBones][1]; + bone.localOffset[2] = model->trans[boneNum - model->numRootBones][2]; + bone.localRotation = Quaternion32(QuatInt16::ToFloat(model->quats[boneNum - model->numRootBones][0]), + QuatInt16::ToFloat(model->quats[boneNum - model->numRootBones][1]), + QuatInt16::ToFloat(model->quats[boneNum - model->numRootBones][2]), + QuatInt16::ToFloat(model->quats[boneNum - model->numRootBones][3])); + } + + writer.AddBone(std::move(bone)); + } + } + + void AddXModelMaterials(AbstractXModelWriter& writer, DistinctMapper& materialMapper, const XModel* model) + { + for (auto surfaceMaterialNum = 0; surfaceMaterialNum < model->numsurfs; surfaceMaterialNum++) + { + Material* material = model->materialHandles[surfaceMaterialNum]; + if (materialMapper.Add(material)) + { + XModelMaterial xMaterial; + xMaterial.ApplyDefaults(); + + xMaterial.name = material->info.name; + const auto* colorMap = GetMaterialColorMap(material); + if (colorMap) + xMaterial.colorMapName = std::string(colorMap->name); + + writer.AddMaterial(std::move(xMaterial)); + } + } + } + + void AddXModelObjects(AbstractXModelWriter& writer, const XModel* model, const unsigned lod) + { + const auto surfCount = model->lodInfo[lod].numsurfs; + + for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++) + { + XModelObject object; + object.name = "surf" + std::to_string(surfIndex); + + writer.AddObject(std::move(object)); + } + } + + void AddXModelVertices(AbstractXModelWriter& writer, const XModel* model, const unsigned lod) + { + const auto* surfs = &model->surfs[model->lodInfo[lod].surfIndex]; + const auto surfCount = model->lodInfo[lod].numsurfs; + + for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++) + { + const auto& surface = surfs[surfIndex]; + + for (auto vertexIndex = 0u; vertexIndex < surface.vertCount; vertexIndex++) + { + const auto& v = surface.verts0[vertexIndex]; + vec2_t uv; + vec3_t normalVec; + vec4_t color; + + Common::Vec2UnpackTexCoords(v.texCoord, &uv); + Common::Vec3UnpackUnitVec(v.normal, &normalVec); + Common::Vec4UnpackGfxColor(v.color, &color); + + XModelVertex vertex{}; + vertex.coordinates[0] = v.xyz[0]; + vertex.coordinates[1] = v.xyz[1]; + vertex.coordinates[2] = v.xyz[2]; + vertex.normal[0] = normalVec[0]; + vertex.normal[1] = normalVec[1]; + vertex.normal[2] = normalVec[2]; + vertex.color[0] = color[0]; + vertex.color[1] = color[1]; + vertex.color[2] = color[2]; + vertex.color[3] = color[3]; + vertex.uv[0] = uv[0]; + vertex.uv[1] = uv[1]; + + writer.AddVertex(vertex); + } + } + } + + void AllocateXModelBoneWeights(const XModel* model, const unsigned lod, XModelVertexBoneWeightCollection& weightCollection) + { + const auto* surfs = &model->surfs[model->lodInfo[lod].surfIndex]; + const auto surfCount = model->lodInfo[lod].numsurfs; + + weightCollection.totalWeightCount = 0u; + for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++) + { + const auto& surface = surfs[surfIndex]; + + if (surface.vertList) + { + weightCollection.totalWeightCount += surface.vertListCount; + } + + if (surface.vertInfo.vertsBlend) + { + weightCollection.totalWeightCount += surface.vertInfo.vertCount[0] * 1; + weightCollection.totalWeightCount += surface.vertInfo.vertCount[1] * 2; + weightCollection.totalWeightCount += surface.vertInfo.vertCount[2] * 3; + weightCollection.totalWeightCount += surface.vertInfo.vertCount[3] * 4; + } + } + + weightCollection.weights = std::make_unique(weightCollection.totalWeightCount); + } + + void AddXModelVertexBoneWeights(AbstractXModelWriter& writer, const XModel* model, const unsigned lod, XModelVertexBoneWeightCollection& weightCollection) + { + const auto* surfs = &model->surfs[model->lodInfo[lod].surfIndex]; + const auto surfCount = model->lodInfo[lod].numsurfs; + + size_t weightOffset = 0u; + + for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++) + { + const auto& surface = surfs[surfIndex]; + auto handledVertices = 0u; + + if (surface.vertList) + { + for (auto vertListIndex = 0u; vertListIndex < surface.vertListCount; vertListIndex++) + { + const auto& vertList = surface.vertList[vertListIndex]; + const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; + + weightCollection.weights[weightOffset++] = XModelBoneWeight{static_cast(vertList.boneOffset / sizeof(DObjSkelMat)), 1.0f}; + + for (auto vertListVertexOffset = 0u; vertListVertexOffset < vertList.vertCount; vertListVertexOffset++) + { + writer.AddVertexBoneWeights(XModelVertexBoneWeights{boneWeightOffset, 1}); + } + handledVertices += vertList.vertCount; + } + } + + auto vertsBlendOffset = 0u; + if (surface.vertInfo.vertsBlend) + { + // 1 bone weight + for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[0]; vertIndex++) + { + const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; + const auto boneIndex0 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat)); + weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex0, 1.0f}; + + vertsBlendOffset += 1; + + writer.AddVertexBoneWeights(XModelVertexBoneWeights{boneWeightOffset, 1}); + } + + // 2 bone weights + for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[1]; vertIndex++) + { + const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; + const auto boneIndex0 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat)); + const auto boneIndex1 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat)); + const auto boneWeight1 = HalfFloat::ToFloat(surface.vertInfo.vertsBlend[vertsBlendOffset + 2]); + const auto boneWeight0 = 1.0f - boneWeight1; + + weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex0, boneWeight0}; + weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex1, boneWeight1}; + + vertsBlendOffset += 3; + + writer.AddVertexBoneWeights(XModelVertexBoneWeights{boneWeightOffset, 2}); + } + + // 3 bone weights + for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[2]; vertIndex++) + { + const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; + const auto boneIndex0 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat)); + const auto boneIndex1 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat)); + const auto boneWeight1 = HalfFloat::ToFloat(surface.vertInfo.vertsBlend[vertsBlendOffset + 2]); + const auto boneIndex2 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 3] / sizeof(DObjSkelMat)); + const auto boneWeight2 = HalfFloat::ToFloat(surface.vertInfo.vertsBlend[vertsBlendOffset + 4]); + const auto boneWeight0 = 1.0f - boneWeight1 - boneWeight2; + + weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex0, boneWeight0}; + weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex1, boneWeight1}; + weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex2, boneWeight2}; + + vertsBlendOffset += 5; + + writer.AddVertexBoneWeights(XModelVertexBoneWeights{boneWeightOffset, 3}); + } + + // 4 bone weights + for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[3]; vertIndex++) + { + const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; + const auto boneIndex0 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat)); + const auto boneIndex1 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat)); + const auto boneWeight1 = HalfFloat::ToFloat(surface.vertInfo.vertsBlend[vertsBlendOffset + 2]); + const auto boneIndex2 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 3] / sizeof(DObjSkelMat)); + const auto boneWeight2 = HalfFloat::ToFloat(surface.vertInfo.vertsBlend[vertsBlendOffset + 4]); + const auto boneIndex3 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 5] / sizeof(DObjSkelMat)); + const auto boneWeight3 = HalfFloat::ToFloat(surface.vertInfo.vertsBlend[vertsBlendOffset + 6]); + const auto boneWeight0 = 1.0f - boneWeight1 - boneWeight2 - boneWeight3; + + weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex0, boneWeight0}; + weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex1, boneWeight1}; + weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex2, boneWeight2}; + weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex3, boneWeight3}; + + vertsBlendOffset += 7; + + writer.AddVertexBoneWeights(XModelVertexBoneWeights{boneWeightOffset, 4}); + } + + handledVertices += + surface.vertInfo.vertCount[0] + surface.vertInfo.vertCount[1] + surface.vertInfo.vertCount[2] + surface.vertInfo.vertCount[3]; + } + + for (; handledVertices < surface.vertCount; handledVertices++) + { + writer.AddVertexBoneWeights(XModelVertexBoneWeights{nullptr, 0}); + } + } + } + + void AddXModelFaces(AbstractXModelWriter& writer, const DistinctMapper& materialMapper, const XModel* model, const unsigned lod) + { + const auto* surfs = &model->surfs[model->lodInfo[lod].surfIndex]; + const auto surfCount = model->lodInfo[lod].numsurfs; + const auto baseSurfIndex = model->lodInfo[lod].surfIndex; + + for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++) + { + const auto& surface = surfs[surfIndex]; + for (auto triIndex = 0u; triIndex < surface.triCount; triIndex++) + { + const auto& tri = surface.triIndices[triIndex]; + + XModelFace face{}; + face.vertexIndex[0] = tri[0] + surface.baseVertIndex; + face.vertexIndex[1] = tri[1] + surface.baseVertIndex; + face.vertexIndex[2] = tri[2] + surface.baseVertIndex; + face.objectIndex = static_cast(surfIndex); + face.materialIndex = static_cast(materialMapper.GetDistinctPositionByInputPosition(surfIndex + baseSurfIndex)); + writer.AddFace(face); + } + } + } + + void PopulateXModelWriter(const AssetDumpingContext& context, const unsigned lod, const XModel* model, AbstractXModelWriter& writer) + { + DistinctMapper materialMapper(model->numsurfs); + XModelVertexBoneWeightCollection boneWeightCollection; + AllocateXModelBoneWeights(model, lod, boneWeightCollection); + + AddXModelBones(context, writer, model); + AddXModelMaterials(writer, materialMapper, model); + AddXModelObjects(writer, model, lod); + AddXModelVertices(writer, model, lod); + AddXModelVertexBoneWeights(writer, model, lod, boneWeightCollection); + AddXModelFaces(writer, materialMapper, model, lod); + } + + void DumpXModelExportLod(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 = XModelExportWriter::CreateWriterForVersion6(context.m_zone->m_game->GetShortName(), context.m_zone->m_name); + PopulateXModelWriter(context, lod, model, *writer); + + writer->Write(*assetFile); + } + + template void DumpGltfLod(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); + PopulateXModelWriter(context, lod, model, *writer); + + writer->Write(*assetFile); + } + + void DumpXModelSurfs(const AssetDumpingContext& context, const XAssetInfo* asset) + { + const auto* model = asset->Asset(); + + if (ObjWriting::Configuration.ModelOutputFormat == ObjWriting::Configuration_t::ModelOutputFormat_e::OBJ) + DumpObjMat(context, asset); + + for (auto currentLod = 0u; currentLod < model->numLods; currentLod++) + { + switch (ObjWriting::Configuration.ModelOutputFormat) + { + case ObjWriting::Configuration_t::ModelOutputFormat_e::OBJ: + DumpObjLod(context, asset, currentLod); + break; + + case ObjWriting::Configuration_t::ModelOutputFormat_e::XMODEL_EXPORT: + DumpXModelExportLod(context, asset, currentLod); + break; + + case ObjWriting::Configuration_t::ModelOutputFormat_e::GLTF: + DumpGltfLod(context, asset, currentLod, ".gltf"); + break; + + case ObjWriting::Configuration_t::ModelOutputFormat_e::GLB: + DumpGltfLod(context, asset, currentLod, ".glb"); + break; + + default: + assert(false); + break; + } + } + } +} // namespace + bool AssetDumperXModel::ShouldDump(XAssetInfo* asset) { return !asset->m_name.empty() && asset->m_name[0] != ','; } -GfxImage* AssetDumperXModel::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* AssetDumperXModel::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* AssetDumperXModel::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 AssetDumperXModel::AddObjMaterials(ObjWriter& writer, DistinctMapper& materialMapper, const XModel* model) -{ - if (!model->materialHandles) - return; - - for (auto surfIndex = 0u; surfIndex < model->numsurfs; surfIndex++) - { - Material* material = model->materialHandles[surfIndex]; - if (!materialMapper.Add(material)) - continue; - - MtlMaterial mtl; - mtl.materialName = std::string(material->info.name); - - GfxImage* colorMap = GetMaterialColorMap(material); - GfxImage* normalMap = GetMaterialNormalMap(material); - GfxImage* specularMap = GetMaterialSpecularMap(material); - - if (colorMap != nullptr) - mtl.colorMapName = colorMap->name; - if (normalMap != nullptr) - mtl.normalMapName = normalMap->name; - if (specularMap != nullptr) - mtl.specularMapName = specularMap->name; - - writer.AddMaterial(std::move(mtl)); - } -} - -void AssetDumperXModel::AddObjObjects(ObjWriter& writer, const DistinctMapper& materialMapper, const XModel* model, const unsigned lod) -{ - const auto surfCount = model->lodInfo[lod].numsurfs; - const auto baseSurfIndex = model->lodInfo[lod].surfIndex; - - for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++) - { - ObjObject object; - object.name = "surf" + std::to_string(surfIndex); - object.materialIndex = static_cast(materialMapper.GetDistinctPositionByInputPosition(surfIndex + baseSurfIndex)); - - writer.AddObject(std::move(object)); - } -} - -void AssetDumperXModel::AddObjVertices(ObjWriter& writer, const XModel* model, const unsigned lod) -{ - const auto* surfs = &model->surfs[model->lodInfo[lod].surfIndex]; - const auto surfCount = model->lodInfo[lod].numsurfs; - - for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++) - { - const auto& surface = surfs[surfIndex]; - - for (auto vertexIndex = 0u; vertexIndex < surface.vertCount; vertexIndex++) - { - const auto& v = surface.verts0[vertexIndex]; - vec2_t uv; - vec3_t normalVec; - - Common::Vec2UnpackTexCoords(v.texCoord, &uv); - Common::Vec3UnpackUnitVec(v.normal, &normalVec); - - ObjVertex objVertex{}; - ObjNormal objNormal{}; - ObjUv objUv{}; - objVertex.coordinates[0] = v.xyz[0]; - objVertex.coordinates[1] = v.xyz[2]; - objVertex.coordinates[2] = -v.xyz[1]; - objNormal.normal[0] = normalVec[0]; - objNormal.normal[1] = normalVec[2]; - objNormal.normal[2] = -normalVec[1]; - objUv.uv[0] = uv[0]; - objUv.uv[1] = 1.0f - uv[1]; - - writer.AddVertex(static_cast(surfIndex), objVertex); - writer.AddNormal(static_cast(surfIndex), objNormal); - writer.AddUv(static_cast(surfIndex), objUv); - } - } -} - -void AssetDumperXModel::AddObjFaces(ObjWriter& writer, const XModel* model, const unsigned lod) -{ - const auto* surfs = &model->surfs[model->lodInfo[lod].surfIndex]; - const auto surfCount = model->lodInfo[lod].numsurfs; - - for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++) - { - const auto& surface = surfs[surfIndex]; - for (auto triIndex = 0u; triIndex < surface.triCount; triIndex++) - { - const auto& tri = surface.triIndices[triIndex]; - - ObjFace face{}; - face.vertexIndex[0] = tri[2] + surface.baseVertIndex; - face.vertexIndex[1] = tri[1] + surface.baseVertIndex; - face.vertexIndex[2] = tri[0] + surface.baseVertIndex; - face.normalIndex[0] = face.vertexIndex[0]; - face.normalIndex[1] = face.vertexIndex[1]; - face.normalIndex[2] = face.vertexIndex[2]; - face.uvIndex[0] = face.vertexIndex[0]; - face.uvIndex[1] = face.vertexIndex[1]; - face.uvIndex[2] = face.vertexIndex[2]; - writer.AddFace(static_cast(surfIndex), face); - } - } -} - -void AssetDumperXModel::DumpObjMat(const AssetDumpingContext& context, XAssetInfo* asset) -{ - const auto* model = asset->Asset(); - const auto matFile = context.OpenAssetFile("model_export/" + std::string(model->name) + ".mtl"); - - if (!matFile) - return; - - ObjWriter writer(context.m_zone->m_game->GetShortName(), context.m_zone->m_name); - DistinctMapper materialMapper(model->numsurfs); - - AddObjMaterials(writer, materialMapper, model); - writer.WriteMtl(*matFile); -} - -void AssetDumperXModel::DumpObjLod(AssetDumpingContext& context, XAssetInfo* asset, const unsigned lod) -{ - const auto* model = asset->Asset(); - std::ostringstream ss; - ss << "model_export/" << model->name << "_lod" << lod << ".OBJ"; - - const auto assetFile = context.OpenAssetFile(ss.str()); - - if (!assetFile) - return; - - ObjWriter writer(context.m_zone->m_game->GetShortName(), context.m_zone->m_name); - DistinctMapper materialMapper(model->numsurfs); - - AddObjMaterials(writer, materialMapper, model); - AddObjObjects(writer, materialMapper, model, lod); - AddObjVertices(writer, model, lod); - AddObjFaces(writer, model, lod); - - writer.WriteObj(*assetFile, std::string(model->name) + ".mtl"); -} - -void AssetDumperXModel::DumpObj(AssetDumpingContext& context, XAssetInfo* asset) -{ - const auto* model = asset->Asset(); - - DumpObjMat(context, asset); - for (auto currentLod = 0u; currentLod < model->numLods; currentLod++) - { - DumpObjLod(context, asset, currentLod); - } -} - -void AssetDumperXModel::AddXModelBones(const AssetDumpingContext& context, AbstractXModelWriter& writer, const XModel* model) -{ - for (auto boneNum = 0u; boneNum < model->numBones; boneNum++) - { - XModelBone bone; - if (model->boneNames[boneNum] < context.m_zone->m_script_strings.Count()) - bone.name = context.m_zone->m_script_strings[model->boneNames[boneNum]]; - else - bone.name = "INVALID_BONE_NAME"; - - if (boneNum < model->numRootBones) - bone.parentIndex = -1; - else - bone.parentIndex = static_cast(boneNum - static_cast(model->parentList[boneNum - model->numRootBones])); - - bone.scale[0] = 1.0f; - bone.scale[1] = 1.0f; - bone.scale[2] = 1.0f; - - bone.globalOffset[0] = model->baseMat[boneNum].trans[0]; - bone.globalOffset[1] = model->baseMat[boneNum].trans[1]; - bone.globalOffset[2] = model->baseMat[boneNum].trans[2]; - bone.globalRotation = - Quaternion32(model->baseMat[boneNum].quat[0], model->baseMat[boneNum].quat[1], model->baseMat[boneNum].quat[2], model->baseMat[boneNum].quat[3]); - - if (boneNum < model->numRootBones) - { - bone.localOffset[0] = 0; - bone.localOffset[1] = 0; - bone.localOffset[2] = 0; - bone.localRotation = Quaternion32(0, 0, 0, 1); - } - else - { - bone.localOffset[0] = model->trans[boneNum - model->numRootBones][0]; - bone.localOffset[1] = model->trans[boneNum - model->numRootBones][1]; - bone.localOffset[2] = model->trans[boneNum - model->numRootBones][2]; - bone.localRotation = Quaternion32(QuatInt16::ToFloat(model->quats[boneNum - model->numRootBones][0]), - QuatInt16::ToFloat(model->quats[boneNum - model->numRootBones][1]), - QuatInt16::ToFloat(model->quats[boneNum - model->numRootBones][2]), - QuatInt16::ToFloat(model->quats[boneNum - model->numRootBones][3])); - } - - writer.AddBone(std::move(bone)); - } -} - -void AssetDumperXModel::AddXModelMaterials(AbstractXModelWriter& writer, DistinctMapper& materialMapper, const XModel* model) -{ - for (auto surfaceMaterialNum = 0; surfaceMaterialNum < model->numsurfs; surfaceMaterialNum++) - { - Material* material = model->materialHandles[surfaceMaterialNum]; - if (materialMapper.Add(material)) - { - XModelMaterial xMaterial; - xMaterial.ApplyDefaults(); - - xMaterial.name = material->info.name; - const auto* colorMap = GetMaterialColorMap(material); - if (colorMap) - xMaterial.colorMapName = std::string(colorMap->name); - - writer.AddMaterial(std::move(xMaterial)); - } - } -} - -void AssetDumperXModel::AddXModelObjects(AbstractXModelWriter& writer, const XModel* model, const unsigned lod) -{ - const auto surfCount = model->lodInfo[lod].numsurfs; - - for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++) - { - XModelObject object; - object.name = "surf" + std::to_string(surfIndex); - - writer.AddObject(std::move(object)); - } -} - -void AssetDumperXModel::AddXModelVertices(AbstractXModelWriter& writer, const XModel* model, const unsigned lod) -{ - const auto* surfs = &model->surfs[model->lodInfo[lod].surfIndex]; - const auto surfCount = model->lodInfo[lod].numsurfs; - - for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++) - { - const auto& surface = surfs[surfIndex]; - - for (auto vertexIndex = 0u; vertexIndex < surface.vertCount; vertexIndex++) - { - const auto& v = surface.verts0[vertexIndex]; - vec2_t uv; - vec3_t normalVec; - vec4_t color; - - Common::Vec2UnpackTexCoords(v.texCoord, &uv); - Common::Vec3UnpackUnitVec(v.normal, &normalVec); - Common::Vec4UnpackGfxColor(v.color, &color); - - XModelVertex vertex{}; - vertex.coordinates[0] = v.xyz[0]; - vertex.coordinates[1] = v.xyz[1]; - vertex.coordinates[2] = v.xyz[2]; - vertex.normal[0] = normalVec[0]; - vertex.normal[1] = normalVec[1]; - vertex.normal[2] = normalVec[2]; - vertex.color[0] = color[0]; - vertex.color[1] = color[1]; - vertex.color[2] = color[2]; - vertex.color[3] = color[3]; - vertex.uv[0] = uv[0]; - vertex.uv[1] = uv[1]; - - writer.AddVertex(vertex); - } - } -} - -void AssetDumperXModel::AllocateXModelBoneWeights(const XModel* model, const unsigned lod, XModelVertexBoneWeightCollection& weightCollection) -{ - const auto* surfs = &model->surfs[model->lodInfo[lod].surfIndex]; - const auto surfCount = model->lodInfo[lod].numsurfs; - - weightCollection.totalWeightCount = 0u; - for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++) - { - const auto& surface = surfs[surfIndex]; - - if (surface.vertList) - { - weightCollection.totalWeightCount += surface.vertListCount; - } - - if (surface.vertInfo.vertsBlend) - { - weightCollection.totalWeightCount += surface.vertInfo.vertCount[0] * 1; - weightCollection.totalWeightCount += surface.vertInfo.vertCount[1] * 2; - weightCollection.totalWeightCount += surface.vertInfo.vertCount[2] * 3; - weightCollection.totalWeightCount += surface.vertInfo.vertCount[3] * 4; - } - } - - weightCollection.weights = std::make_unique(weightCollection.totalWeightCount); -} - -void AssetDumperXModel::AddXModelVertexBoneWeights(AbstractXModelWriter& writer, - const XModel* model, - const unsigned lod, - XModelVertexBoneWeightCollection& weightCollection) -{ - const auto* surfs = &model->surfs[model->lodInfo[lod].surfIndex]; - const auto surfCount = model->lodInfo[lod].numsurfs; - - size_t weightOffset = 0u; - - for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++) - { - const auto& surface = surfs[surfIndex]; - auto handledVertices = 0u; - - if (surface.vertList) - { - for (auto vertListIndex = 0u; vertListIndex < surface.vertListCount; vertListIndex++) - { - const auto& vertList = surface.vertList[vertListIndex]; - const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; - - weightCollection.weights[weightOffset++] = XModelBoneWeight{static_cast(vertList.boneOffset / sizeof(DObjSkelMat)), 1.0f}; - - for (auto vertListVertexOffset = 0u; vertListVertexOffset < vertList.vertCount; vertListVertexOffset++) - { - writer.AddVertexBoneWeights(XModelVertexBoneWeights{boneWeightOffset, 1}); - } - handledVertices += vertList.vertCount; - } - } - - auto vertsBlendOffset = 0u; - if (surface.vertInfo.vertsBlend) - { - // 1 bone weight - for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[0]; vertIndex++) - { - const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; - const auto boneIndex0 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat)); - weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex0, 1.0f}; - - vertsBlendOffset += 1; - - writer.AddVertexBoneWeights(XModelVertexBoneWeights{boneWeightOffset, 1}); - } - - // 2 bone weights - for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[1]; vertIndex++) - { - const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; - const auto boneIndex0 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat)); - const auto boneIndex1 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat)); - const auto boneWeight1 = HalfFloat::ToFloat(surface.vertInfo.vertsBlend[vertsBlendOffset + 2]); - const auto boneWeight0 = 1.0f - boneWeight1; - - weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex0, boneWeight0}; - weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex1, boneWeight1}; - - vertsBlendOffset += 3; - - writer.AddVertexBoneWeights(XModelVertexBoneWeights{boneWeightOffset, 2}); - } - - // 3 bone weights - for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[2]; vertIndex++) - { - const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; - const auto boneIndex0 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat)); - const auto boneIndex1 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat)); - const auto boneWeight1 = HalfFloat::ToFloat(surface.vertInfo.vertsBlend[vertsBlendOffset + 2]); - const auto boneIndex2 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 3] / sizeof(DObjSkelMat)); - const auto boneWeight2 = HalfFloat::ToFloat(surface.vertInfo.vertsBlend[vertsBlendOffset + 4]); - const auto boneWeight0 = 1.0f - boneWeight1 - boneWeight2; - - weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex0, boneWeight0}; - weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex1, boneWeight1}; - weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex2, boneWeight2}; - - vertsBlendOffset += 5; - - writer.AddVertexBoneWeights(XModelVertexBoneWeights{boneWeightOffset, 3}); - } - - // 4 bone weights - for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[3]; vertIndex++) - { - const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; - const auto boneIndex0 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat)); - const auto boneIndex1 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat)); - const auto boneWeight1 = HalfFloat::ToFloat(surface.vertInfo.vertsBlend[vertsBlendOffset + 2]); - const auto boneIndex2 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 3] / sizeof(DObjSkelMat)); - const auto boneWeight2 = HalfFloat::ToFloat(surface.vertInfo.vertsBlend[vertsBlendOffset + 4]); - const auto boneIndex3 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 5] / sizeof(DObjSkelMat)); - const auto boneWeight3 = HalfFloat::ToFloat(surface.vertInfo.vertsBlend[vertsBlendOffset + 6]); - const auto boneWeight0 = 1.0f - boneWeight1 - boneWeight2 - boneWeight3; - - weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex0, boneWeight0}; - weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex1, boneWeight1}; - weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex2, boneWeight2}; - weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex3, boneWeight3}; - - vertsBlendOffset += 7; - - writer.AddVertexBoneWeights(XModelVertexBoneWeights{boneWeightOffset, 4}); - } - - handledVertices += surface.vertInfo.vertCount[0] + surface.vertInfo.vertCount[1] + surface.vertInfo.vertCount[2] + surface.vertInfo.vertCount[3]; - } - - for (; handledVertices < surface.vertCount; handledVertices++) - { - writer.AddVertexBoneWeights(XModelVertexBoneWeights{nullptr, 0}); - } - } -} - -void AssetDumperXModel::AddXModelFaces(AbstractXModelWriter& writer, const DistinctMapper& materialMapper, const XModel* model, const unsigned lod) -{ - const auto* surfs = &model->surfs[model->lodInfo[lod].surfIndex]; - const auto surfCount = model->lodInfo[lod].numsurfs; - const auto baseSurfIndex = model->lodInfo[lod].surfIndex; - - for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++) - { - const auto& surface = surfs[surfIndex]; - for (auto triIndex = 0u; triIndex < surface.triCount; triIndex++) - { - const auto& tri = surface.triIndices[triIndex]; - - XModelFace face{}; - face.vertexIndex[0] = tri[0] + surface.baseVertIndex; - face.vertexIndex[1] = tri[1] + surface.baseVertIndex; - face.vertexIndex[2] = tri[2] + surface.baseVertIndex; - face.objectIndex = static_cast(surfIndex); - face.materialIndex = static_cast(materialMapper.GetDistinctPositionByInputPosition(surfIndex + baseSurfIndex)); - writer.AddFace(face); - } - } -} - -void AssetDumperXModel::DumpXModelExportLod(const AssetDumpingContext& context, XAssetInfo* asset, const unsigned lod) -{ - const auto* model = asset->Asset(); - - std::ostringstream ss; - ss << "model_export/" << model->name << "_lod" << lod << ".XMODEL_EXPORT"; - - const auto assetFile = context.OpenAssetFile(ss.str()); - - if (!assetFile) - return; - - const auto writer = XModelExportWriter::CreateWriterForVersion6(context.m_zone->m_game->GetShortName(), context.m_zone->m_name); - DistinctMapper materialMapper(model->numsurfs); - XModelVertexBoneWeightCollection boneWeightCollection; - AllocateXModelBoneWeights(model, lod, boneWeightCollection); - - AddXModelBones(context, *writer, model); - AddXModelMaterials(*writer, materialMapper, model); - AddXModelObjects(*writer, model, lod); - AddXModelVertices(*writer, model, lod); - AddXModelVertexBoneWeights(*writer, model, lod, boneWeightCollection); - AddXModelFaces(*writer, materialMapper, model, lod); - - writer->Write(*assetFile); -} - -void AssetDumperXModel::DumpXModelExport(const AssetDumpingContext& context, XAssetInfo* asset) -{ - const auto* model = asset->Asset(); - for (auto currentLod = 0u; currentLod < model->numLods; currentLod++) - { - DumpXModelExportLod(context, asset, currentLod); - } -} - void AssetDumperXModel::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) { - switch (ObjWriting::Configuration.ModelOutputFormat) - { - case ObjWriting::Configuration_t::ModelOutputFormat_e::OBJ: - DumpObj(context, asset); - break; - - case ObjWriting::Configuration_t::ModelOutputFormat_e::XMODEL_EXPORT: - DumpXModelExport(context, asset); - break; - - default: - assert(false); - break; - } + DumpXModelSurfs(context, asset); } diff --git a/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperXModel.h b/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperXModel.h index a304d3b9..e8aa5df8 100644 --- a/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperXModel.h +++ b/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperXModel.h @@ -2,37 +2,11 @@ #include "Dumping/AbstractAssetDumper.h" #include "Game/IW3/IW3.h" -#include "Utils/DistinctMapper.h" -#include "XModel/AbstractXModelWriter.h" -#include "XModel/Obj/ObjWriter.h" namespace IW3 { class AssetDumperXModel final : public AbstractAssetDumper { - static GfxImage* GetMaterialColorMap(const Material* material); - static GfxImage* GetMaterialNormalMap(const Material* material); - static GfxImage* GetMaterialSpecularMap(const Material* material); - - static void AddObjMaterials(ObjWriter& writer, DistinctMapper& materialMapper, const XModel* model); - static void AddObjObjects(ObjWriter& writer, const DistinctMapper& materialMapper, const XModel* model, unsigned lod); - static void AddObjVertices(ObjWriter& writer, const XModel* model, unsigned lod); - static void AddObjFaces(ObjWriter& writer, const XModel* model, unsigned lod); - static void DumpObjLod(AssetDumpingContext& context, XAssetInfo* asset, unsigned lod); - static void DumpObjMat(const AssetDumpingContext& context, XAssetInfo* asset); - static void DumpObj(AssetDumpingContext& context, XAssetInfo* asset); - - static void AddXModelBones(const AssetDumpingContext& context, AbstractXModelWriter& writer, const XModel* model); - static void AddXModelMaterials(AbstractXModelWriter& writer, DistinctMapper& materialMapper, const XModel* model); - static void AddXModelObjects(AbstractXModelWriter& writer, const XModel* model, unsigned lod); - static void AddXModelVertices(AbstractXModelWriter& writer, const XModel* model, unsigned lod); - static void AllocateXModelBoneWeights(const XModel* model, unsigned lod, XModelVertexBoneWeightCollection& weightCollection); - static void - AddXModelVertexBoneWeights(AbstractXModelWriter& writer, const XModel* model, unsigned lod, XModelVertexBoneWeightCollection& weightCollection); - static void AddXModelFaces(AbstractXModelWriter& writer, const DistinctMapper& materialMapper, const XModel* model, unsigned lod); - static void DumpXModelExportLod(const AssetDumpingContext& context, XAssetInfo* asset, unsigned lod); - static void DumpXModelExport(const AssetDumpingContext& context, XAssetInfo* asset); - protected: bool ShouldDump(XAssetInfo* asset) override; void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; diff --git a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperXModel.cpp b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperXModel.cpp index da956c9b..93141199 100644 --- a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperXModel.cpp +++ b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperXModel.cpp @@ -3,15 +3,22 @@ #include "Game/IW4/CommonIW4.h" #include "Math/Quaternion.h" #include "ObjWriting.h" +#include "Utils/DistinctMapper.h" #include "Utils/HalfFloat.h" #include "Utils/QuatInt16.h" +#include "XModel/AbstractXModelWriter.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 +#include using namespace IW4; -namespace IW4 +namespace { class SurfsDumpingZoneState final : public IZoneAssetDumperState { @@ -20,579 +27,589 @@ namespace IW4 public: bool ShouldDumpTechnique(const XModelSurfs* surfs) { - if (m_dumped_surfs.find(surfs) != m_dumped_surfs.end()) + if (m_dumped_surfs.contains(surfs)) return false; m_dumped_surfs.emplace(surfs); return true; } }; -} // namespace IW4 + + 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 AddObjMaterials(ObjWriter& writer, DistinctMapper& materialMapper, const XModel* model) + { + if (!model->materialHandles) + return; + + for (auto surfIndex = 0u; surfIndex < model->numsurfs; surfIndex++) + { + Material* material = model->materialHandles[surfIndex]; + if (!materialMapper.Add(material)) + continue; + + MtlMaterial mtl; + mtl.materialName = std::string(material->info.name); + + GfxImage* colorMap = GetMaterialColorMap(material); + GfxImage* normalMap = GetMaterialNormalMap(material); + GfxImage* specularMap = GetMaterialSpecularMap(material); + + if (colorMap != nullptr) + mtl.colorMapName = colorMap->name; + if (normalMap != nullptr) + mtl.normalMapName = normalMap->name; + if (specularMap != nullptr) + mtl.specularMapName = specularMap->name; + + writer.AddMaterial(std::move(mtl)); + } + } + + void AddObjObjects(ObjWriter& writer, const DistinctMapper& materialMapper, const XModelSurfs* modelSurfs, int baseSurfaceIndex) + { + for (auto surfIndex = 0u; surfIndex < modelSurfs->numsurfs; surfIndex++) + { + ObjObject object; + object.name = "surf" + std::to_string(surfIndex); + object.materialIndex = static_cast(materialMapper.GetDistinctPositionByInputPosition(surfIndex + baseSurfaceIndex)); + + writer.AddObject(std::move(object)); + } + } + + void AddObjVertices(ObjWriter& writer, 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]; + vec2_t uv; + vec3_t normalVec; + + Common::Vec2UnpackTexCoords(v.texCoord, &uv); + Common::Vec3UnpackUnitVec(v.normal, &normalVec); + + ObjVertex objVertex{}; + ObjNormal objNormal{}; + ObjUv objUv{}; + objVertex.coordinates[0] = v.xyz[0]; + objVertex.coordinates[1] = v.xyz[2]; + objVertex.coordinates[2] = -v.xyz[1]; + objNormal.normal[0] = normalVec[0]; + objNormal.normal[1] = normalVec[2]; + objNormal.normal[2] = -normalVec[1]; + objUv.uv[0] = uv[0]; + objUv.uv[1] = 1.0f - uv[1]; + + writer.AddVertex(static_cast(surfIndex), objVertex); + writer.AddNormal(static_cast(surfIndex), objNormal); + writer.AddUv(static_cast(surfIndex), objUv); + } + } + } + + void AddObjFaces(ObjWriter& writer, const XModelSurfs* modelSurfs) + { + for (auto surfIndex = 0u; surfIndex < modelSurfs->numsurfs; surfIndex++) + { + const auto& surface = modelSurfs->surfs[surfIndex]; + for (auto triIndex = 0u; triIndex < surface.triCount; triIndex++) + { + const auto& tri = surface.triIndices[triIndex]; + + ObjFace face{}; + face.vertexIndex[0] = tri[2] + surface.baseVertIndex; + face.vertexIndex[1] = tri[1] + surface.baseVertIndex; + face.vertexIndex[2] = tri[0] + surface.baseVertIndex; + face.normalIndex[0] = face.vertexIndex[0]; + face.normalIndex[1] = face.vertexIndex[1]; + face.normalIndex[2] = face.vertexIndex[2]; + face.uvIndex[0] = face.vertexIndex[0]; + face.uvIndex[1] = face.vertexIndex[1]; + face.uvIndex[2] = face.vertexIndex[2]; + writer.AddFace(static_cast(surfIndex), face); + } + } + } + + void DumpObjMat(const AssetDumpingContext& context, const XAssetInfo* asset) + { + const auto* model = asset->Asset(); + const auto matFile = context.OpenAssetFile(std::format("model_export/{}.mtl", model->name)); + + if (!matFile) + return; + + ObjWriter writer(context.m_zone->m_game->GetShortName(), context.m_zone->m_name); + DistinctMapper materialMapper(model->numsurfs); + + AddObjMaterials(writer, materialMapper, model); + writer.WriteMtl(*matFile); + } + + void DumpObjLod(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; + + ObjWriter writer(context.m_zone->m_game->GetShortName(), context.m_zone->m_name); + DistinctMapper materialMapper(model->numsurfs); + + AddObjMaterials(writer, materialMapper, model); + AddObjObjects(writer, materialMapper, modelSurfs, model->lodInfo[lod].surfIndex); + AddObjVertices(writer, modelSurfs); + AddObjFaces(writer, modelSurfs); + + writer.WriteObj(*assetFile, std::format("{}.mtl", model->name)); + } + + void AddXModelBones(const AssetDumpingContext& context, AbstractXModelWriter& writer, const XModel* model) + { + for (auto boneNum = 0u; boneNum < model->numBones; boneNum++) + { + XModelBone bone; + if (model->boneNames[boneNum] < context.m_zone->m_script_strings.Count()) + bone.name = context.m_zone->m_script_strings[model->boneNames[boneNum]]; + else + bone.name = "INVALID_BONE_NAME"; + + if (boneNum < model->numRootBones) + bone.parentIndex = -1; + else + bone.parentIndex = static_cast(boneNum - static_cast(model->parentList[boneNum - model->numRootBones])); + + bone.scale[0] = 1.0f; + bone.scale[1] = 1.0f; + bone.scale[2] = 1.0f; + + bone.globalOffset[0] = model->baseMat[boneNum].trans[0]; + bone.globalOffset[1] = model->baseMat[boneNum].trans[1]; + bone.globalOffset[2] = model->baseMat[boneNum].trans[2]; + bone.globalRotation = Quaternion32( + model->baseMat[boneNum].quat[0], model->baseMat[boneNum].quat[1], model->baseMat[boneNum].quat[2], model->baseMat[boneNum].quat[3]); + + if (boneNum < model->numRootBones) + { + bone.localOffset[0] = 0; + bone.localOffset[1] = 0; + bone.localOffset[2] = 0; + bone.localRotation = Quaternion32(0, 0, 0, 1); + } + else + { + bone.localOffset[0] = model->trans[boneNum - model->numRootBones][0]; + bone.localOffset[1] = model->trans[boneNum - model->numRootBones][1]; + bone.localOffset[2] = model->trans[boneNum - model->numRootBones][2]; + bone.localRotation = Quaternion32(QuatInt16::ToFloat(model->quats[boneNum - model->numRootBones][0]), + QuatInt16::ToFloat(model->quats[boneNum - model->numRootBones][1]), + QuatInt16::ToFloat(model->quats[boneNum - model->numRootBones][2]), + QuatInt16::ToFloat(model->quats[boneNum - model->numRootBones][3])); + } + + writer.AddBone(std::move(bone)); + } + } + + void AddXModelMaterials(AbstractXModelWriter& writer, DistinctMapper& materialMapper, const XModel* model) + { + for (auto surfaceMaterialNum = 0; surfaceMaterialNum < model->numsurfs; surfaceMaterialNum++) + { + Material* material = model->materialHandles[surfaceMaterialNum]; + if (materialMapper.Add(material)) + { + XModelMaterial xMaterial; + xMaterial.ApplyDefaults(); + + xMaterial.name = material->info.name; + const auto* colorMap = GetMaterialColorMap(material); + if (colorMap) + xMaterial.colorMapName = std::string(colorMap->name); + + writer.AddMaterial(std::move(xMaterial)); + } + } + } + + void AddXModelObjects(AbstractXModelWriter& writer, const XModelSurfs* modelSurfs) + { + for (auto surfIndex = 0u; surfIndex < modelSurfs->numsurfs; surfIndex++) + { + XModelObject object; + object.name = "surf" + std::to_string(surfIndex); + + writer.AddObject(std::move(object)); + } + } + + void AddXModelVertices(AbstractXModelWriter& writer, 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]; + vec2_t uv; + vec3_t normalVec; + vec4_t color; + + Common::Vec2UnpackTexCoords(v.texCoord, &uv); + Common::Vec3UnpackUnitVec(v.normal, &normalVec); + Common::Vec4UnpackGfxColor(v.color, &color); + + XModelVertex vertex{}; + vertex.coordinates[0] = v.xyz[0]; + vertex.coordinates[1] = v.xyz[1]; + vertex.coordinates[2] = v.xyz[2]; + vertex.normal[0] = normalVec[0]; + vertex.normal[1] = normalVec[1]; + vertex.normal[2] = normalVec[2]; + vertex.color[0] = color[0]; + vertex.color[1] = color[1]; + vertex.color[2] = color[2]; + vertex.color[3] = color[3]; + vertex.uv[0] = uv[0]; + vertex.uv[1] = uv[1]; + + writer.AddVertex(vertex); + } + } + } + + void AllocateXModelBoneWeights(const XModelSurfs* modelSurfs, XModelVertexBoneWeightCollection& weightCollection) + { + weightCollection.totalWeightCount = 0u; + for (auto surfIndex = 0u; surfIndex < modelSurfs->numsurfs; surfIndex++) + { + const auto& surface = modelSurfs->surfs[surfIndex]; + + if (surface.vertList) + { + weightCollection.totalWeightCount += surface.vertListCount; + } + + if (surface.vertInfo.vertsBlend) + { + weightCollection.totalWeightCount += surface.vertInfo.vertCount[0] * 1; + weightCollection.totalWeightCount += surface.vertInfo.vertCount[1] * 2; + weightCollection.totalWeightCount += surface.vertInfo.vertCount[2] * 3; + weightCollection.totalWeightCount += surface.vertInfo.vertCount[3] * 4; + } + } + + weightCollection.weights = std::make_unique(weightCollection.totalWeightCount); + } + + void AddXModelVertexBoneWeights(AbstractXModelWriter& writer, const XModelSurfs* modelSurfs, XModelVertexBoneWeightCollection& weightCollection) + { + size_t 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 = &weightCollection.weights[weightOffset]; + + weightCollection.weights[weightOffset++] = XModelBoneWeight{static_cast(vertList.boneOffset / sizeof(DObjSkelMat)), 1.0f}; + + for (auto vertListVertexOffset = 0u; vertListVertexOffset < vertList.vertCount; vertListVertexOffset++) + { + writer.AddVertexBoneWeights(XModelVertexBoneWeights{boneWeightOffset, 1}); + } + handledVertices += vertList.vertCount; + } + } + + auto vertsBlendOffset = 0u; + if (surface.vertInfo.vertsBlend) + { + // 1 bone weight + for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[0]; vertIndex++) + { + const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; + const auto boneIndex0 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat)); + weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex0, 1.0f}; + + vertsBlendOffset += 1; + + writer.AddVertexBoneWeights(XModelVertexBoneWeights{boneWeightOffset, 1}); + } + + // 2 bone weights + for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[1]; vertIndex++) + { + const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; + const auto boneIndex0 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat)); + const auto boneIndex1 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat)); + const auto boneWeight1 = HalfFloat::ToFloat(surface.vertInfo.vertsBlend[vertsBlendOffset + 2]); + const auto boneWeight0 = 1.0f - boneWeight1; + + weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex0, boneWeight0}; + weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex1, boneWeight1}; + + vertsBlendOffset += 3; + + writer.AddVertexBoneWeights(XModelVertexBoneWeights{boneWeightOffset, 2}); + } + + // 3 bone weights + for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[2]; vertIndex++) + { + const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; + const auto boneIndex0 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat)); + const auto boneIndex1 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat)); + const auto boneWeight1 = HalfFloat::ToFloat(surface.vertInfo.vertsBlend[vertsBlendOffset + 2]); + const auto boneIndex2 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 3] / sizeof(DObjSkelMat)); + const auto boneWeight2 = HalfFloat::ToFloat(surface.vertInfo.vertsBlend[vertsBlendOffset + 4]); + const auto boneWeight0 = 1.0f - boneWeight1 - boneWeight2; + + weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex0, boneWeight0}; + weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex1, boneWeight1}; + weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex2, boneWeight2}; + + vertsBlendOffset += 5; + + writer.AddVertexBoneWeights(XModelVertexBoneWeights{boneWeightOffset, 3}); + } + + // 4 bone weights + for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[3]; vertIndex++) + { + const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; + const auto boneIndex0 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat)); + const auto boneIndex1 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat)); + const auto boneWeight1 = HalfFloat::ToFloat(surface.vertInfo.vertsBlend[vertsBlendOffset + 2]); + const auto boneIndex2 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 3] / sizeof(DObjSkelMat)); + const auto boneWeight2 = HalfFloat::ToFloat(surface.vertInfo.vertsBlend[vertsBlendOffset + 4]); + const auto boneIndex3 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 5] / sizeof(DObjSkelMat)); + const auto boneWeight3 = HalfFloat::ToFloat(surface.vertInfo.vertsBlend[vertsBlendOffset + 6]); + const auto boneWeight0 = 1.0f - boneWeight1 - boneWeight2 - boneWeight3; + + weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex0, boneWeight0}; + weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex1, boneWeight1}; + weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex2, boneWeight2}; + weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex3, boneWeight3}; + + vertsBlendOffset += 7; + + writer.AddVertexBoneWeights(XModelVertexBoneWeights{boneWeightOffset, 4}); + } + + handledVertices += + surface.vertInfo.vertCount[0] + surface.vertInfo.vertCount[1] + surface.vertInfo.vertCount[2] + surface.vertInfo.vertCount[3]; + } + + for (; handledVertices < surface.vertCount; handledVertices++) + { + writer.AddVertexBoneWeights(XModelVertexBoneWeights{nullptr, 0}); + } + } + } + + void + AddXModelFaces(AbstractXModelWriter& writer, const DistinctMapper& materialMapper, const XModelSurfs* modelSurfs, const int baseSurfaceIndex) + { + for (auto surfIndex = 0u; surfIndex < modelSurfs->numsurfs; surfIndex++) + { + const auto& surface = modelSurfs->surfs[surfIndex]; + for (auto triIndex = 0u; triIndex < surface.triCount; triIndex++) + { + const auto& tri = surface.triIndices[triIndex]; + + XModelFace face{}; + face.vertexIndex[0] = tri[0] + surface.baseVertIndex; + face.vertexIndex[1] = tri[1] + surface.baseVertIndex; + face.vertexIndex[2] = tri[2] + surface.baseVertIndex; + face.objectIndex = static_cast(surfIndex); + face.materialIndex = static_cast(materialMapper.GetDistinctPositionByInputPosition(surfIndex + baseSurfaceIndex)); + writer.AddFace(face); + } + } + } + + void PopulateXModelWriter(const AssetDumpingContext& context, const unsigned lod, const XModel* model, AbstractXModelWriter& writer) + { + const auto* modelSurfs = model->lodInfo[lod].modelSurfs; + + DistinctMapper materialMapper(model->numsurfs); + XModelVertexBoneWeightCollection boneWeightCollection; + AllocateXModelBoneWeights(modelSurfs, boneWeightCollection); + + AddXModelBones(context, writer, model); + AddXModelMaterials(writer, materialMapper, model); + AddXModelObjects(writer, modelSurfs); + AddXModelVertices(writer, modelSurfs); + AddXModelVertexBoneWeights(writer, modelSurfs, boneWeightCollection); + AddXModelFaces(writer, materialMapper, modelSurfs, model->lodInfo[lod].surfIndex); + } + + void DumpXModelExportLod(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 = XModelExportWriter::CreateWriterForVersion6(context.m_zone->m_game->GetShortName(), context.m_zone->m_name); + PopulateXModelWriter(context, lod, model, *writer); + + writer->Write(*assetFile); + } + + template void DumpGltfLod(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); + PopulateXModelWriter(context, lod, model, *writer); + + writer->Write(*assetFile); + } + + void DumpXModelSurfs(const AssetDumpingContext& context, const XAssetInfo* asset) + { + const auto* model = asset->Asset(); + + if (ObjWriting::Configuration.ModelOutputFormat == ObjWriting::Configuration_t::ModelOutputFormat_e::OBJ) + DumpObjMat(context, asset); + + for (auto currentLod = 0u; currentLod < model->numLods; currentLod++) + { + switch (ObjWriting::Configuration.ModelOutputFormat) + { + case ObjWriting::Configuration_t::ModelOutputFormat_e::OBJ: + DumpObjLod(context, asset, currentLod); + break; + + case ObjWriting::Configuration_t::ModelOutputFormat_e::XMODEL_EXPORT: + DumpXModelExportLod(context, asset, currentLod); + break; + + case ObjWriting::Configuration_t::ModelOutputFormat_e::GLTF: + DumpGltfLod(context, asset, currentLod, ".gltf"); + break; + + case ObjWriting::Configuration_t::ModelOutputFormat_e::GLB: + DumpGltfLod(context, asset, currentLod, ".glb"); + break; + + default: + assert(false); + break; + } + } + } +} // namespace bool AssetDumperXModel::ShouldDump(XAssetInfo* asset) { return !asset->m_name.empty() && asset->m_name[0] != ','; } -GfxImage* AssetDumperXModel::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* AssetDumperXModel::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* AssetDumperXModel::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 AssetDumperXModel::AddObjMaterials(ObjWriter& writer, DistinctMapper& materialMapper, const XModel* model) -{ - if (!model->materialHandles) - return; - - for (auto surfIndex = 0u; surfIndex < model->numsurfs; surfIndex++) - { - Material* material = model->materialHandles[surfIndex]; - if (!materialMapper.Add(material)) - continue; - - MtlMaterial mtl; - mtl.materialName = std::string(material->info.name); - - GfxImage* colorMap = GetMaterialColorMap(material); - GfxImage* normalMap = GetMaterialNormalMap(material); - GfxImage* specularMap = GetMaterialSpecularMap(material); - - if (colorMap != nullptr) - mtl.colorMapName = colorMap->name; - if (normalMap != nullptr) - mtl.normalMapName = normalMap->name; - if (specularMap != nullptr) - mtl.specularMapName = specularMap->name; - - writer.AddMaterial(std::move(mtl)); - } -} - -void AssetDumperXModel::AddObjObjects(ObjWriter& writer, const DistinctMapper& materialMapper, const XModelSurfs* modelSurfs, int baseSurfaceIndex) -{ - for (auto surfIndex = 0u; surfIndex < modelSurfs->numsurfs; surfIndex++) - { - ObjObject object; - object.name = "surf" + std::to_string(surfIndex); - object.materialIndex = static_cast(materialMapper.GetDistinctPositionByInputPosition(surfIndex + baseSurfaceIndex)); - - writer.AddObject(std::move(object)); - } -} - -void AssetDumperXModel::AddObjVertices(ObjWriter& writer, 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]; - vec2_t uv; - vec3_t normalVec; - - Common::Vec2UnpackTexCoords(v.texCoord, &uv); - Common::Vec3UnpackUnitVec(v.normal, &normalVec); - - ObjVertex objVertex{}; - ObjNormal objNormal{}; - ObjUv objUv{}; - objVertex.coordinates[0] = v.xyz[0]; - objVertex.coordinates[1] = v.xyz[2]; - objVertex.coordinates[2] = -v.xyz[1]; - objNormal.normal[0] = normalVec[0]; - objNormal.normal[1] = normalVec[2]; - objNormal.normal[2] = -normalVec[1]; - objUv.uv[0] = uv[0]; - objUv.uv[1] = 1.0f - uv[1]; - - writer.AddVertex(static_cast(surfIndex), objVertex); - writer.AddNormal(static_cast(surfIndex), objNormal); - writer.AddUv(static_cast(surfIndex), objUv); - } - } -} - -void AssetDumperXModel::AddObjFaces(ObjWriter& writer, const XModelSurfs* modelSurfs) -{ - for (auto surfIndex = 0u; surfIndex < modelSurfs->numsurfs; surfIndex++) - { - const auto& surface = modelSurfs->surfs[surfIndex]; - for (auto triIndex = 0u; triIndex < surface.triCount; triIndex++) - { - const auto& tri = surface.triIndices[triIndex]; - - ObjFace face{}; - face.vertexIndex[0] = tri[2] + surface.baseVertIndex; - face.vertexIndex[1] = tri[1] + surface.baseVertIndex; - face.vertexIndex[2] = tri[0] + surface.baseVertIndex; - face.normalIndex[0] = face.vertexIndex[0]; - face.normalIndex[1] = face.vertexIndex[1]; - face.normalIndex[2] = face.vertexIndex[2]; - face.uvIndex[0] = face.vertexIndex[0]; - face.uvIndex[1] = face.vertexIndex[1]; - face.uvIndex[2] = face.vertexIndex[2]; - writer.AddFace(static_cast(surfIndex), face); - } - } -} - -void AssetDumperXModel::DumpObjMat(const AssetDumpingContext& context, XAssetInfo* asset) -{ - const auto* model = asset->Asset(); - const auto matFile = context.OpenAssetFile("model_export/" + std::string(model->name) + ".mtl"); - - if (!matFile) - return; - - ObjWriter writer(context.m_zone->m_game->GetShortName(), context.m_zone->m_name); - DistinctMapper materialMapper(model->numsurfs); - - AddObjMaterials(writer, materialMapper, model); - writer.WriteMtl(*matFile); -} - -void AssetDumperXModel::DumpObjLod(const AssetDumpingContext& context, 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("model_export/" + std::string(modelSurfs->name) + ".obj"); - - if (!assetFile) - return; - - ObjWriter writer(context.m_zone->m_game->GetShortName(), context.m_zone->m_name); - DistinctMapper materialMapper(model->numsurfs); - - AddObjMaterials(writer, materialMapper, model); - AddObjObjects(writer, materialMapper, modelSurfs, model->lodInfo[lod].surfIndex); - AddObjVertices(writer, modelSurfs); - AddObjFaces(writer, modelSurfs); - - writer.WriteObj(*assetFile, std::string(model->name) + ".mtl"); -} - -void AssetDumperXModel::DumpObj(AssetDumpingContext& context, XAssetInfo* asset) -{ - const auto* model = asset->Asset(); - auto* surfZoneState = context.GetZoneAssetDumperState(); - - DumpObjMat(context, asset); - for (auto currentLod = 0u; currentLod < model->numLods; currentLod++) - { - if (!model->lodInfo[currentLod].modelSurfs || !surfZoneState->ShouldDumpTechnique(model->lodInfo[currentLod].modelSurfs)) - continue; - - DumpObjLod(context, asset, currentLod); - } -} - -void AssetDumperXModel::AddXModelBones(const AssetDumpingContext& context, AbstractXModelWriter& writer, const XModel* model) -{ - for (auto boneNum = 0u; boneNum < model->numBones; boneNum++) - { - XModelBone bone; - if (model->boneNames[boneNum] < context.m_zone->m_script_strings.Count()) - bone.name = context.m_zone->m_script_strings[model->boneNames[boneNum]]; - else - bone.name = "INVALID_BONE_NAME"; - - if (boneNum < model->numRootBones) - bone.parentIndex = -1; - else - bone.parentIndex = static_cast(boneNum - static_cast(model->parentList[boneNum - model->numRootBones])); - - bone.scale[0] = 1.0f; - bone.scale[1] = 1.0f; - bone.scale[2] = 1.0f; - - bone.globalOffset[0] = model->baseMat[boneNum].trans[0]; - bone.globalOffset[1] = model->baseMat[boneNum].trans[1]; - bone.globalOffset[2] = model->baseMat[boneNum].trans[2]; - bone.globalRotation = - Quaternion32(model->baseMat[boneNum].quat[0], model->baseMat[boneNum].quat[1], model->baseMat[boneNum].quat[2], model->baseMat[boneNum].quat[3]); - - if (boneNum < model->numRootBones) - { - bone.localOffset[0] = 0; - bone.localOffset[1] = 0; - bone.localOffset[2] = 0; - bone.localRotation = Quaternion32(0, 0, 0, 1); - } - else - { - bone.localOffset[0] = model->trans[boneNum - model->numRootBones][0]; - bone.localOffset[1] = model->trans[boneNum - model->numRootBones][1]; - bone.localOffset[2] = model->trans[boneNum - model->numRootBones][2]; - bone.localRotation = Quaternion32(QuatInt16::ToFloat(model->quats[boneNum - model->numRootBones][0]), - QuatInt16::ToFloat(model->quats[boneNum - model->numRootBones][1]), - QuatInt16::ToFloat(model->quats[boneNum - model->numRootBones][2]), - QuatInt16::ToFloat(model->quats[boneNum - model->numRootBones][3])); - } - - writer.AddBone(std::move(bone)); - } -} - -void AssetDumperXModel::AddXModelMaterials(AbstractXModelWriter& writer, DistinctMapper& materialMapper, const XModel* model) -{ - for (auto surfaceMaterialNum = 0; surfaceMaterialNum < model->numsurfs; surfaceMaterialNum++) - { - Material* material = model->materialHandles[surfaceMaterialNum]; - if (materialMapper.Add(material)) - { - XModelMaterial xMaterial; - xMaterial.ApplyDefaults(); - - xMaterial.name = material->info.name; - const auto* colorMap = GetMaterialColorMap(material); - if (colorMap) - xMaterial.colorMapName = std::string(colorMap->name); - - writer.AddMaterial(std::move(xMaterial)); - } - } -} - -void AssetDumperXModel::AddXModelObjects(AbstractXModelWriter& writer, const XModelSurfs* modelSurfs) -{ - for (auto surfIndex = 0u; surfIndex < modelSurfs->numsurfs; surfIndex++) - { - XModelObject object; - object.name = "surf" + std::to_string(surfIndex); - - writer.AddObject(std::move(object)); - } -} - -void AssetDumperXModel::AddXModelVertices(AbstractXModelWriter& writer, 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]; - vec2_t uv; - vec3_t normalVec; - vec4_t color; - - Common::Vec2UnpackTexCoords(v.texCoord, &uv); - Common::Vec3UnpackUnitVec(v.normal, &normalVec); - Common::Vec4UnpackGfxColor(v.color, &color); - - XModelVertex vertex{}; - vertex.coordinates[0] = v.xyz[0]; - vertex.coordinates[1] = v.xyz[1]; - vertex.coordinates[2] = v.xyz[2]; - vertex.normal[0] = normalVec[0]; - vertex.normal[1] = normalVec[1]; - vertex.normal[2] = normalVec[2]; - vertex.color[0] = color[0]; - vertex.color[1] = color[1]; - vertex.color[2] = color[2]; - vertex.color[3] = color[3]; - vertex.uv[0] = uv[0]; - vertex.uv[1] = uv[1]; - - writer.AddVertex(vertex); - } - } -} - -void AssetDumperXModel::AllocateXModelBoneWeights(const XModelSurfs* modelSurfs, XModelVertexBoneWeightCollection& weightCollection) -{ - weightCollection.totalWeightCount = 0u; - for (auto surfIndex = 0u; surfIndex < modelSurfs->numsurfs; surfIndex++) - { - const auto& surface = modelSurfs->surfs[surfIndex]; - - if (surface.vertList) - { - weightCollection.totalWeightCount += surface.vertListCount; - } - - if (surface.vertInfo.vertsBlend) - { - weightCollection.totalWeightCount += surface.vertInfo.vertCount[0] * 1; - weightCollection.totalWeightCount += surface.vertInfo.vertCount[1] * 2; - weightCollection.totalWeightCount += surface.vertInfo.vertCount[2] * 3; - weightCollection.totalWeightCount += surface.vertInfo.vertCount[3] * 4; - } - } - - weightCollection.weights = std::make_unique(weightCollection.totalWeightCount); -} - -void AssetDumperXModel::AddXModelVertexBoneWeights(AbstractXModelWriter& writer, - const XModelSurfs* modelSurfs, - XModelVertexBoneWeightCollection& weightCollection) -{ - size_t 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 = &weightCollection.weights[weightOffset]; - - weightCollection.weights[weightOffset++] = XModelBoneWeight{static_cast(vertList.boneOffset / sizeof(DObjSkelMat)), 1.0f}; - - for (auto vertListVertexOffset = 0u; vertListVertexOffset < vertList.vertCount; vertListVertexOffset++) - { - writer.AddVertexBoneWeights(XModelVertexBoneWeights{boneWeightOffset, 1}); - } - handledVertices += vertList.vertCount; - } - } - - auto vertsBlendOffset = 0u; - if (surface.vertInfo.vertsBlend) - { - // 1 bone weight - for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[0]; vertIndex++) - { - const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; - const auto boneIndex0 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat)); - weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex0, 1.0f}; - - vertsBlendOffset += 1; - - writer.AddVertexBoneWeights(XModelVertexBoneWeights{boneWeightOffset, 1}); - } - - // 2 bone weights - for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[1]; vertIndex++) - { - const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; - const auto boneIndex0 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat)); - const auto boneIndex1 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat)); - const auto boneWeight1 = HalfFloat::ToFloat(surface.vertInfo.vertsBlend[vertsBlendOffset + 2]); - const auto boneWeight0 = 1.0f - boneWeight1; - - weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex0, boneWeight0}; - weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex1, boneWeight1}; - - vertsBlendOffset += 3; - - writer.AddVertexBoneWeights(XModelVertexBoneWeights{boneWeightOffset, 2}); - } - - // 3 bone weights - for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[2]; vertIndex++) - { - const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; - const auto boneIndex0 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat)); - const auto boneIndex1 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat)); - const auto boneWeight1 = HalfFloat::ToFloat(surface.vertInfo.vertsBlend[vertsBlendOffset + 2]); - const auto boneIndex2 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 3] / sizeof(DObjSkelMat)); - const auto boneWeight2 = HalfFloat::ToFloat(surface.vertInfo.vertsBlend[vertsBlendOffset + 4]); - const auto boneWeight0 = 1.0f - boneWeight1 - boneWeight2; - - weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex0, boneWeight0}; - weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex1, boneWeight1}; - weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex2, boneWeight2}; - - vertsBlendOffset += 5; - - writer.AddVertexBoneWeights(XModelVertexBoneWeights{boneWeightOffset, 3}); - } - - // 4 bone weights - for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[3]; vertIndex++) - { - const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; - const auto boneIndex0 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat)); - const auto boneIndex1 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat)); - const auto boneWeight1 = HalfFloat::ToFloat(surface.vertInfo.vertsBlend[vertsBlendOffset + 2]); - const auto boneIndex2 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 3] / sizeof(DObjSkelMat)); - const auto boneWeight2 = HalfFloat::ToFloat(surface.vertInfo.vertsBlend[vertsBlendOffset + 4]); - const auto boneIndex3 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 5] / sizeof(DObjSkelMat)); - const auto boneWeight3 = HalfFloat::ToFloat(surface.vertInfo.vertsBlend[vertsBlendOffset + 6]); - const auto boneWeight0 = 1.0f - boneWeight1 - boneWeight2 - boneWeight3; - - weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex0, boneWeight0}; - weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex1, boneWeight1}; - weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex2, boneWeight2}; - weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex3, boneWeight3}; - - vertsBlendOffset += 7; - - writer.AddVertexBoneWeights(XModelVertexBoneWeights{boneWeightOffset, 4}); - } - - handledVertices += surface.vertInfo.vertCount[0] + surface.vertInfo.vertCount[1] + surface.vertInfo.vertCount[2] + surface.vertInfo.vertCount[3]; - } - - for (; handledVertices < surface.vertCount; handledVertices++) - { - writer.AddVertexBoneWeights(XModelVertexBoneWeights{nullptr, 0}); - } - } -} - -void AssetDumperXModel::AddXModelFaces(AbstractXModelWriter& writer, - const DistinctMapper& materialMapper, - const XModelSurfs* modelSurfs, - const int baseSurfaceIndex) -{ - for (auto surfIndex = 0u; surfIndex < modelSurfs->numsurfs; surfIndex++) - { - const auto& surface = modelSurfs->surfs[surfIndex]; - for (auto triIndex = 0u; triIndex < surface.triCount; triIndex++) - { - const auto& tri = surface.triIndices[triIndex]; - - XModelFace face{}; - face.vertexIndex[0] = tri[0] + surface.baseVertIndex; - face.vertexIndex[1] = tri[1] + surface.baseVertIndex; - face.vertexIndex[2] = tri[2] + surface.baseVertIndex; - face.objectIndex = static_cast(surfIndex); - face.materialIndex = static_cast(materialMapper.GetDistinctPositionByInputPosition(surfIndex + baseSurfaceIndex)); - writer.AddFace(face); - } - } -} - -void AssetDumperXModel::DumpXModelExportLod(const AssetDumpingContext& context, 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("model_export/" + std::string(modelSurfs->name) + ".XMODEL_EXPORT"); - - if (!assetFile) - return; - - const auto writer = XModelExportWriter::CreateWriterForVersion6(context.m_zone->m_game->GetShortName(), context.m_zone->m_name); - DistinctMapper materialMapper(model->numsurfs); - XModelVertexBoneWeightCollection boneWeightCollection; - AllocateXModelBoneWeights(modelSurfs, boneWeightCollection); - - AddXModelBones(context, *writer, model); - AddXModelMaterials(*writer, materialMapper, model); - AddXModelObjects(*writer, modelSurfs); - AddXModelVertices(*writer, modelSurfs); - AddXModelVertexBoneWeights(*writer, modelSurfs, boneWeightCollection); - AddXModelFaces(*writer, materialMapper, modelSurfs, model->lodInfo[lod].surfIndex); - - writer->Write(*assetFile); -} - -void AssetDumperXModel::DumpXModelExport(AssetDumpingContext& context, XAssetInfo* asset) -{ - auto* surfZoneState = context.GetZoneAssetDumperState(); - const auto* model = asset->Asset(); - for (auto currentLod = 0u; currentLod < model->numLods; currentLod++) - { - if (!model->lodInfo[currentLod].modelSurfs || !surfZoneState->ShouldDumpTechnique(model->lodInfo[currentLod].modelSurfs)) - continue; - DumpXModelExportLod(context, asset, currentLod); - } -} - void AssetDumperXModel::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) { - switch (ObjWriting::Configuration.ModelOutputFormat) - { - case ObjWriting::Configuration_t::ModelOutputFormat_e::OBJ: - DumpObj(context, asset); - break; - - case ObjWriting::Configuration_t::ModelOutputFormat_e::XMODEL_EXPORT: - DumpXModelExport(context, asset); - break; - - default: - assert(false); - break; - } + DumpXModelSurfs(context, asset); } diff --git a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperXModel.h b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperXModel.h index 0d2b058d..84be5262 100644 --- a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperXModel.h +++ b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperXModel.h @@ -2,37 +2,11 @@ #include "Dumping/AbstractAssetDumper.h" #include "Game/IW4/IW4.h" -#include "Utils/DistinctMapper.h" -#include "XModel/AbstractXModelWriter.h" -#include "XModel/Obj/ObjWriter.h" namespace IW4 { class AssetDumperXModel final : public AbstractAssetDumper { - static GfxImage* GetMaterialColorMap(const Material* material); - static GfxImage* GetMaterialNormalMap(const Material* material); - static GfxImage* GetMaterialSpecularMap(const Material* material); - - static void AddObjMaterials(ObjWriter& writer, DistinctMapper& materialMapper, const XModel* model); - static void AddObjObjects(ObjWriter& writer, const DistinctMapper& materialMapper, const XModelSurfs* modelSurfs, int baseSurfaceIndex); - static void AddObjVertices(ObjWriter& writer, const XModelSurfs* modelSurfs); - static void AddObjFaces(ObjWriter& writer, const XModelSurfs* modelSurfs); - static void DumpObjLod(const AssetDumpingContext& context, XAssetInfo* asset, const unsigned lod); - static void DumpObjMat(const AssetDumpingContext& context, XAssetInfo* asset); - static void DumpObj(AssetDumpingContext& context, XAssetInfo* asset); - - static void AddXModelBones(const AssetDumpingContext& context, AbstractXModelWriter& writer, const XModel* model); - static void AddXModelMaterials(AbstractXModelWriter& writer, DistinctMapper& materialMapper, const XModel* model); - static void AddXModelObjects(AbstractXModelWriter& writer, const XModelSurfs* modelSurfs); - static void AddXModelVertices(AbstractXModelWriter& writer, const XModelSurfs* modelSurfs); - static void AllocateXModelBoneWeights(const XModelSurfs* modelSurfs, XModelVertexBoneWeightCollection& weightCollection); - static void AddXModelVertexBoneWeights(AbstractXModelWriter& writer, const XModelSurfs* modelSurfs, XModelVertexBoneWeightCollection& weightCollection); - static void - AddXModelFaces(AbstractXModelWriter& writer, const DistinctMapper& materialMapper, const XModelSurfs* modelSurfs, int baseSurfaceIndex); - static void DumpXModelExportLod(const AssetDumpingContext& context, XAssetInfo* asset, unsigned lod); - static void DumpXModelExport(AssetDumpingContext& context, XAssetInfo* asset); - protected: bool ShouldDump(XAssetInfo* asset) override; void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; diff --git a/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperXModel.cpp b/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperXModel.cpp index b5aef51c..8d986960 100644 --- a/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperXModel.cpp +++ b/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperXModel.cpp @@ -3,15 +3,22 @@ #include "Game/IW5/CommonIW5.h" #include "Math/Quaternion.h" #include "ObjWriting.h" +#include "Utils/DistinctMapper.h" #include "Utils/HalfFloat.h" #include "Utils/QuatInt16.h" +#include "XModel/AbstractXModelWriter.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 +#include using namespace IW5; -namespace IW5 +namespace { class SurfsDumpingZoneState final : public IZoneAssetDumperState { @@ -20,578 +27,588 @@ namespace IW5 public: bool ShouldDumpTechnique(const XModelSurfs* surfs) { - if (m_dumped_surfs.find(surfs) != m_dumped_surfs.end()) + if (m_dumped_surfs.contains(surfs)) return false; m_dumped_surfs.emplace(surfs); return true; } }; -} // namespace IW5 + + 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 AddObjMaterials(ObjWriter& writer, DistinctMapper& materialMapper, const XModel* model) + { + if (!model->materialHandles) + return; + + for (auto surfIndex = 0u; surfIndex < model->numsurfs; surfIndex++) + { + Material* material = model->materialHandles[surfIndex]; + if (!materialMapper.Add(material)) + continue; + + MtlMaterial mtl; + mtl.materialName = std::string(material->info.name); + GfxImage* colorMap = GetMaterialColorMap(material); + GfxImage* normalMap = GetMaterialNormalMap(material); + GfxImage* specularMap = GetMaterialSpecularMap(material); + + if (colorMap != nullptr) + mtl.colorMapName = colorMap->name; + if (normalMap != nullptr) + mtl.normalMapName = normalMap->name; + if (specularMap != nullptr) + mtl.specularMapName = specularMap->name; + + writer.AddMaterial(std::move(mtl)); + } + } + + void AddObjObjects(ObjWriter& writer, const DistinctMapper& materialMapper, const XModelSurfs* modelSurfs, int baseSurfaceIndex) + { + for (auto surfIndex = 0u; surfIndex < modelSurfs->numsurfs; surfIndex++) + { + ObjObject object; + object.name = "surf" + std::to_string(surfIndex); + object.materialIndex = static_cast(materialMapper.GetDistinctPositionByInputPosition(surfIndex + baseSurfaceIndex)); + + writer.AddObject(std::move(object)); + } + } + + void AddObjVertices(ObjWriter& writer, 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.packedVerts0[vertexIndex]; + vec2_t uv; + vec3_t normalVec; + + Common::Vec2UnpackTexCoords(v.texCoord, &uv); + Common::Vec3UnpackUnitVec(v.normal, &normalVec); + + ObjVertex objVertex{}; + ObjNormal objNormal{}; + ObjUv objUv{}; + objVertex.coordinates[0] = v.xyz[0]; + objVertex.coordinates[1] = v.xyz[2]; + objVertex.coordinates[2] = -v.xyz[1]; + objNormal.normal[0] = normalVec[0]; + objNormal.normal[1] = normalVec[2]; + objNormal.normal[2] = -normalVec[1]; + objUv.uv[0] = uv[0]; + objUv.uv[1] = 1.0f - uv[1]; + + writer.AddVertex(static_cast(surfIndex), objVertex); + writer.AddNormal(static_cast(surfIndex), objNormal); + writer.AddUv(static_cast(surfIndex), objUv); + } + } + } + + void AddObjFaces(ObjWriter& writer, const XModelSurfs* modelSurfs) + { + for (auto surfIndex = 0u; surfIndex < modelSurfs->numsurfs; surfIndex++) + { + const auto& surface = modelSurfs->surfs[surfIndex]; + for (auto triIndex = 0u; triIndex < surface.triCount; triIndex++) + { + const auto& tri = surface.triIndices[triIndex]; + + ObjFace face{}; + face.vertexIndex[0] = tri[2] + surface.baseVertIndex; + face.vertexIndex[1] = tri[1] + surface.baseVertIndex; + face.vertexIndex[2] = tri[0] + surface.baseVertIndex; + face.normalIndex[0] = face.vertexIndex[0]; + face.normalIndex[1] = face.vertexIndex[1]; + face.normalIndex[2] = face.vertexIndex[2]; + face.uvIndex[0] = face.vertexIndex[0]; + face.uvIndex[1] = face.vertexIndex[1]; + face.uvIndex[2] = face.vertexIndex[2]; + writer.AddFace(static_cast(surfIndex), face); + } + } + } + + void DumpObjMat(const AssetDumpingContext& context, const XAssetInfo* asset) + { + const auto* model = asset->Asset(); + const auto matFile = context.OpenAssetFile(std::format("model_export/{}.mtl", model->name)); + + if (!matFile) + return; + + ObjWriter writer(context.m_zone->m_game->GetShortName(), context.m_zone->m_name); + DistinctMapper materialMapper(model->numsurfs); + + AddObjMaterials(writer, materialMapper, model); + writer.WriteMtl(*matFile); + } + + void DumpObjLod(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; + + ObjWriter writer(context.m_zone->m_game->GetShortName(), context.m_zone->m_name); + DistinctMapper materialMapper(model->numsurfs); + + AddObjMaterials(writer, materialMapper, model); + AddObjObjects(writer, materialMapper, modelSurfs, model->lodInfo[lod].surfIndex); + AddObjVertices(writer, modelSurfs); + AddObjFaces(writer, modelSurfs); + + writer.WriteObj(*assetFile, std::format("{}.mtl", model->name)); + } + + void AddXModelBones(const AssetDumpingContext& context, AbstractXModelWriter& writer, const XModel* model) + { + for (auto boneNum = 0u; boneNum < model->numBones; boneNum++) + { + XModelBone bone; + if (model->boneNames[boneNum] < context.m_zone->m_script_strings.Count()) + bone.name = context.m_zone->m_script_strings[model->boneNames[boneNum]]; + else + bone.name = "INVALID_BONE_NAME"; + + if (boneNum < model->numRootBones) + bone.parentIndex = -1; + else + bone.parentIndex = static_cast(boneNum - static_cast(model->parentList[boneNum - model->numRootBones])); + + bone.scale[0] = 1.0f; + bone.scale[1] = 1.0f; + bone.scale[2] = 1.0f; + + bone.globalOffset[0] = model->baseMat[boneNum].trans[0]; + bone.globalOffset[1] = model->baseMat[boneNum].trans[1]; + bone.globalOffset[2] = model->baseMat[boneNum].trans[2]; + bone.globalRotation = Quaternion32( + model->baseMat[boneNum].quat[0], model->baseMat[boneNum].quat[1], model->baseMat[boneNum].quat[2], model->baseMat[boneNum].quat[3]); + + if (boneNum < model->numRootBones) + { + bone.localOffset[0] = 0; + bone.localOffset[1] = 0; + bone.localOffset[2] = 0; + bone.localRotation = Quaternion32(0, 0, 0, 1); + } + else + { + bone.localOffset[0] = model->trans[boneNum - model->numRootBones][0]; + bone.localOffset[1] = model->trans[boneNum - model->numRootBones][1]; + bone.localOffset[2] = model->trans[boneNum - model->numRootBones][2]; + bone.localRotation = Quaternion32(QuatInt16::ToFloat(model->quats[boneNum - model->numRootBones][0]), + QuatInt16::ToFloat(model->quats[boneNum - model->numRootBones][1]), + QuatInt16::ToFloat(model->quats[boneNum - model->numRootBones][2]), + QuatInt16::ToFloat(model->quats[boneNum - model->numRootBones][3])); + } + + writer.AddBone(std::move(bone)); + } + } + + void AddXModelMaterials(AbstractXModelWriter& writer, DistinctMapper& materialMapper, const XModel* model) + { + for (auto surfaceMaterialNum = 0; surfaceMaterialNum < model->numsurfs; surfaceMaterialNum++) + { + Material* material = model->materialHandles[surfaceMaterialNum]; + if (materialMapper.Add(material)) + { + XModelMaterial xMaterial; + xMaterial.ApplyDefaults(); + + xMaterial.name = material->info.name; + const auto* colorMap = GetMaterialColorMap(material); + if (colorMap) + xMaterial.colorMapName = std::string(colorMap->name); + + writer.AddMaterial(std::move(xMaterial)); + } + } + } + + void AddXModelObjects(AbstractXModelWriter& writer, const XModelSurfs* modelSurfs) + { + for (auto surfIndex = 0u; surfIndex < modelSurfs->numsurfs; surfIndex++) + { + XModelObject object; + object.name = "surf" + std::to_string(surfIndex); + + writer.AddObject(std::move(object)); + } + } + + void AddXModelVertices(AbstractXModelWriter& writer, 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.packedVerts0[vertexIndex]; + vec2_t uv; + vec3_t normalVec; + vec4_t color; + + Common::Vec2UnpackTexCoords(v.texCoord, &uv); + Common::Vec3UnpackUnitVec(v.normal, &normalVec); + Common::Vec4UnpackGfxColor(v.color, &color); + + XModelVertex vertex{}; + vertex.coordinates[0] = v.xyz[0]; + vertex.coordinates[1] = v.xyz[1]; + vertex.coordinates[2] = v.xyz[2]; + vertex.normal[0] = normalVec[0]; + vertex.normal[1] = normalVec[1]; + vertex.normal[2] = normalVec[2]; + vertex.color[0] = color[0]; + vertex.color[1] = color[1]; + vertex.color[2] = color[2]; + vertex.color[3] = color[3]; + vertex.uv[0] = uv[0]; + vertex.uv[1] = uv[1]; + + writer.AddVertex(vertex); + } + } + } + + void AllocateXModelBoneWeights(const XModelSurfs* modelSurfs, XModelVertexBoneWeightCollection& weightCollection) + { + weightCollection.totalWeightCount = 0u; + for (auto surfIndex = 0u; surfIndex < modelSurfs->numsurfs; surfIndex++) + { + const auto& surface = modelSurfs->surfs[surfIndex]; + + if (surface.vertList) + { + weightCollection.totalWeightCount += surface.vertListCount; + } + + if (surface.vertInfo.vertsBlend) + { + weightCollection.totalWeightCount += surface.vertInfo.vertCount[0] * 1; + weightCollection.totalWeightCount += surface.vertInfo.vertCount[1] * 2; + weightCollection.totalWeightCount += surface.vertInfo.vertCount[2] * 3; + weightCollection.totalWeightCount += surface.vertInfo.vertCount[3] * 4; + } + } + + weightCollection.weights = std::make_unique(weightCollection.totalWeightCount); + } + + void AddXModelVertexBoneWeights(AbstractXModelWriter& writer, const XModelSurfs* modelSurfs, XModelVertexBoneWeightCollection& weightCollection) + { + size_t 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 = &weightCollection.weights[weightOffset]; + + weightCollection.weights[weightOffset++] = XModelBoneWeight{static_cast(vertList.boneOffset / sizeof(DObjSkelMat)), 1.0f}; + + for (auto vertListVertexOffset = 0u; vertListVertexOffset < vertList.vertCount; vertListVertexOffset++) + { + writer.AddVertexBoneWeights(XModelVertexBoneWeights{boneWeightOffset, 1}); + } + handledVertices += vertList.vertCount; + } + } + + auto vertsBlendOffset = 0u; + if (surface.vertInfo.vertsBlend) + { + // 1 bone weight + for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[0]; vertIndex++) + { + const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; + const auto boneIndex0 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat)); + weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex0, 1.0f}; + + vertsBlendOffset += 1; + + writer.AddVertexBoneWeights(XModelVertexBoneWeights{boneWeightOffset, 1}); + } + + // 2 bone weights + for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[1]; vertIndex++) + { + const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; + const auto boneIndex0 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat)); + const auto boneIndex1 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat)); + const auto boneWeight1 = HalfFloat::ToFloat(surface.vertInfo.vertsBlend[vertsBlendOffset + 2]); + const auto boneWeight0 = 1.0f - boneWeight1; + + weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex0, boneWeight0}; + weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex1, boneWeight1}; + + vertsBlendOffset += 3; + + writer.AddVertexBoneWeights(XModelVertexBoneWeights{boneWeightOffset, 2}); + } + + // 3 bone weights + for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[2]; vertIndex++) + { + const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; + const auto boneIndex0 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat)); + const auto boneIndex1 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat)); + const auto boneWeight1 = HalfFloat::ToFloat(surface.vertInfo.vertsBlend[vertsBlendOffset + 2]); + const auto boneIndex2 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 3] / sizeof(DObjSkelMat)); + const auto boneWeight2 = HalfFloat::ToFloat(surface.vertInfo.vertsBlend[vertsBlendOffset + 4]); + const auto boneWeight0 = 1.0f - boneWeight1 - boneWeight2; + + weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex0, boneWeight0}; + weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex1, boneWeight1}; + weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex2, boneWeight2}; + + vertsBlendOffset += 5; + + writer.AddVertexBoneWeights(XModelVertexBoneWeights{boneWeightOffset, 3}); + } + + // 4 bone weights + for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[3]; vertIndex++) + { + const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; + const auto boneIndex0 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat)); + const auto boneIndex1 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat)); + const auto boneWeight1 = HalfFloat::ToFloat(surface.vertInfo.vertsBlend[vertsBlendOffset + 2]); + const auto boneIndex2 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 3] / sizeof(DObjSkelMat)); + const auto boneWeight2 = HalfFloat::ToFloat(surface.vertInfo.vertsBlend[vertsBlendOffset + 4]); + const auto boneIndex3 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 5] / sizeof(DObjSkelMat)); + const auto boneWeight3 = HalfFloat::ToFloat(surface.vertInfo.vertsBlend[vertsBlendOffset + 6]); + const auto boneWeight0 = 1.0f - boneWeight1 - boneWeight2 - boneWeight3; + + weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex0, boneWeight0}; + weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex1, boneWeight1}; + weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex2, boneWeight2}; + weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex3, boneWeight3}; + + vertsBlendOffset += 7; + + writer.AddVertexBoneWeights(XModelVertexBoneWeights{boneWeightOffset, 4}); + } + + handledVertices += + surface.vertInfo.vertCount[0] + surface.vertInfo.vertCount[1] + surface.vertInfo.vertCount[2] + surface.vertInfo.vertCount[3]; + } + + for (; handledVertices < surface.vertCount; handledVertices++) + { + writer.AddVertexBoneWeights(XModelVertexBoneWeights{nullptr, 0}); + } + } + } + + void + AddXModelFaces(AbstractXModelWriter& writer, const DistinctMapper& materialMapper, const XModelSurfs* modelSurfs, const int baseSurfaceIndex) + { + for (auto surfIndex = 0u; surfIndex < modelSurfs->numsurfs; surfIndex++) + { + const auto& surface = modelSurfs->surfs[surfIndex]; + for (auto triIndex = 0u; triIndex < surface.triCount; triIndex++) + { + const auto& tri = surface.triIndices[triIndex]; + + XModelFace face{}; + face.vertexIndex[0] = tri[0] + surface.baseVertIndex; + face.vertexIndex[1] = tri[1] + surface.baseVertIndex; + face.vertexIndex[2] = tri[2] + surface.baseVertIndex; + face.objectIndex = static_cast(surfIndex); + face.materialIndex = static_cast(materialMapper.GetDistinctPositionByInputPosition(surfIndex + baseSurfaceIndex)); + writer.AddFace(face); + } + } + } + + void PopulateXModelWriter(const AssetDumpingContext& context, const unsigned lod, const XModel* model, AbstractXModelWriter& writer) + { + const auto* modelSurfs = model->lodInfo[lod].modelSurfs; + + DistinctMapper materialMapper(model->numsurfs); + XModelVertexBoneWeightCollection boneWeightCollection; + AllocateXModelBoneWeights(modelSurfs, boneWeightCollection); + + AddXModelBones(context, writer, model); + AddXModelMaterials(writer, materialMapper, model); + AddXModelObjects(writer, modelSurfs); + AddXModelVertices(writer, modelSurfs); + AddXModelVertexBoneWeights(writer, modelSurfs, boneWeightCollection); + AddXModelFaces(writer, materialMapper, modelSurfs, model->lodInfo[lod].surfIndex); + } + + void DumpXModelExportLod(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 = XModelExportWriter::CreateWriterForVersion6(context.m_zone->m_game->GetShortName(), context.m_zone->m_name); + PopulateXModelWriter(context, lod, model, *writer); + + writer->Write(*assetFile); + } + + template void DumpGltfLod(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); + PopulateXModelWriter(context, lod, model, *writer); + + writer->Write(*assetFile); + } + + void DumpXModelSurfs(const AssetDumpingContext& context, const XAssetInfo* asset) + { + const auto* model = asset->Asset(); + + if (ObjWriting::Configuration.ModelOutputFormat == ObjWriting::Configuration_t::ModelOutputFormat_e::OBJ) + DumpObjMat(context, asset); + + for (auto currentLod = 0u; currentLod < model->numLods; currentLod++) + { + switch (ObjWriting::Configuration.ModelOutputFormat) + { + case ObjWriting::Configuration_t::ModelOutputFormat_e::OBJ: + DumpObjLod(context, asset, currentLod); + break; + + case ObjWriting::Configuration_t::ModelOutputFormat_e::XMODEL_EXPORT: + DumpXModelExportLod(context, asset, currentLod); + break; + + case ObjWriting::Configuration_t::ModelOutputFormat_e::GLTF: + DumpGltfLod(context, asset, currentLod, ".gltf"); + break; + + case ObjWriting::Configuration_t::ModelOutputFormat_e::GLB: + DumpGltfLod(context, asset, currentLod, ".glb"); + break; + + default: + assert(false); + break; + } + } + } +} // namespace bool AssetDumperXModel::ShouldDump(XAssetInfo* asset) { return !asset->m_name.empty() && asset->m_name[0] != ','; } -GfxImage* AssetDumperXModel::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* AssetDumperXModel::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* AssetDumperXModel::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 AssetDumperXModel::AddObjMaterials(ObjWriter& writer, DistinctMapper& materialMapper, const XModel* model) -{ - if (!model->materialHandles) - return; - - for (auto surfIndex = 0u; surfIndex < model->numsurfs; surfIndex++) - { - Material* material = model->materialHandles[surfIndex]; - if (!materialMapper.Add(material)) - continue; - - MtlMaterial mtl; - mtl.materialName = std::string(material->info.name); - GfxImage* colorMap = GetMaterialColorMap(material); - GfxImage* normalMap = GetMaterialNormalMap(material); - GfxImage* specularMap = GetMaterialSpecularMap(material); - - if (colorMap != nullptr) - mtl.colorMapName = colorMap->name; - if (normalMap != nullptr) - mtl.normalMapName = normalMap->name; - if (specularMap != nullptr) - mtl.specularMapName = specularMap->name; - - writer.AddMaterial(std::move(mtl)); - } -} - -void AssetDumperXModel::AddObjObjects(ObjWriter& writer, const DistinctMapper& materialMapper, const XModelSurfs* modelSurfs, int baseSurfaceIndex) -{ - for (auto surfIndex = 0u; surfIndex < modelSurfs->numsurfs; surfIndex++) - { - ObjObject object; - object.name = "surf" + std::to_string(surfIndex); - object.materialIndex = static_cast(materialMapper.GetDistinctPositionByInputPosition(surfIndex + baseSurfaceIndex)); - - writer.AddObject(std::move(object)); - } -} - -void AssetDumperXModel::AddObjVertices(ObjWriter& writer, 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.packedVerts0[vertexIndex]; - vec2_t uv; - vec3_t normalVec; - - Common::Vec2UnpackTexCoords(v.texCoord, &uv); - Common::Vec3UnpackUnitVec(v.normal, &normalVec); - - ObjVertex objVertex{}; - ObjNormal objNormal{}; - ObjUv objUv{}; - objVertex.coordinates[0] = v.xyz[0]; - objVertex.coordinates[1] = v.xyz[2]; - objVertex.coordinates[2] = -v.xyz[1]; - objNormal.normal[0] = normalVec[0]; - objNormal.normal[1] = normalVec[2]; - objNormal.normal[2] = -normalVec[1]; - objUv.uv[0] = uv[0]; - objUv.uv[1] = 1.0f - uv[1]; - - writer.AddVertex(static_cast(surfIndex), objVertex); - writer.AddNormal(static_cast(surfIndex), objNormal); - writer.AddUv(static_cast(surfIndex), objUv); - } - } -} - -void AssetDumperXModel::AddObjFaces(ObjWriter& writer, const XModelSurfs* modelSurfs) -{ - for (auto surfIndex = 0u; surfIndex < modelSurfs->numsurfs; surfIndex++) - { - const auto& surface = modelSurfs->surfs[surfIndex]; - for (auto triIndex = 0u; triIndex < surface.triCount; triIndex++) - { - const auto& tri = surface.triIndices[triIndex]; - - ObjFace face{}; - face.vertexIndex[0] = tri[2] + surface.baseVertIndex; - face.vertexIndex[1] = tri[1] + surface.baseVertIndex; - face.vertexIndex[2] = tri[0] + surface.baseVertIndex; - face.normalIndex[0] = face.vertexIndex[0]; - face.normalIndex[1] = face.vertexIndex[1]; - face.normalIndex[2] = face.vertexIndex[2]; - face.uvIndex[0] = face.vertexIndex[0]; - face.uvIndex[1] = face.vertexIndex[1]; - face.uvIndex[2] = face.vertexIndex[2]; - writer.AddFace(static_cast(surfIndex), face); - } - } -} - -void AssetDumperXModel::DumpObjMat(const AssetDumpingContext& context, XAssetInfo* asset) -{ - const auto* model = asset->Asset(); - const auto matFile = context.OpenAssetFile("model_export/" + std::string(model->name) + ".mtl"); - - if (!matFile) - return; - - ObjWriter writer(context.m_zone->m_game->GetShortName(), context.m_zone->m_name); - DistinctMapper materialMapper(model->numsurfs); - - AddObjMaterials(writer, materialMapper, model); - writer.WriteMtl(*matFile); -} - -void AssetDumperXModel::DumpObjLod(const AssetDumpingContext& context, 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("model_export/" + std::string(modelSurfs->name) + ".obj"); - - if (!assetFile) - return; - - ObjWriter writer(context.m_zone->m_game->GetShortName(), context.m_zone->m_name); - DistinctMapper materialMapper(model->numsurfs); - - AddObjMaterials(writer, materialMapper, model); - AddObjObjects(writer, materialMapper, modelSurfs, model->lodInfo[lod].surfIndex); - AddObjVertices(writer, modelSurfs); - AddObjFaces(writer, modelSurfs); - - writer.WriteObj(*assetFile, std::string(model->name) + ".mtl"); -} - -void AssetDumperXModel::DumpObj(AssetDumpingContext& context, XAssetInfo* asset) -{ - const auto* model = asset->Asset(); - auto* surfZoneState = context.GetZoneAssetDumperState(); - - DumpObjMat(context, asset); - for (auto currentLod = 0u; currentLod < model->numLods; currentLod++) - { - if (!model->lodInfo[currentLod].modelSurfs || !surfZoneState->ShouldDumpTechnique(model->lodInfo[currentLod].modelSurfs)) - continue; - - DumpObjLod(context, asset, currentLod); - } -} - -void AssetDumperXModel::AddXModelBones(const AssetDumpingContext& context, AbstractXModelWriter& writer, const XModel* model) -{ - for (auto boneNum = 0u; boneNum < model->numBones; boneNum++) - { - XModelBone bone; - if (model->boneNames[boneNum] < context.m_zone->m_script_strings.Count()) - bone.name = context.m_zone->m_script_strings[model->boneNames[boneNum]]; - else - bone.name = "INVALID_BONE_NAME"; - - if (boneNum < model->numRootBones) - bone.parentIndex = -1; - else - bone.parentIndex = static_cast(boneNum - static_cast(model->parentList[boneNum - model->numRootBones])); - - bone.scale[0] = 1.0f; - bone.scale[1] = 1.0f; - bone.scale[2] = 1.0f; - - bone.globalOffset[0] = model->baseMat[boneNum].trans[0]; - bone.globalOffset[1] = model->baseMat[boneNum].trans[1]; - bone.globalOffset[2] = model->baseMat[boneNum].trans[2]; - bone.globalRotation = - Quaternion32(model->baseMat[boneNum].quat[0], model->baseMat[boneNum].quat[1], model->baseMat[boneNum].quat[2], model->baseMat[boneNum].quat[3]); - - if (boneNum < model->numRootBones) - { - bone.localOffset[0] = 0; - bone.localOffset[1] = 0; - bone.localOffset[2] = 0; - bone.localRotation = Quaternion32(0, 0, 0, 1); - } - else - { - bone.localOffset[0] = model->trans[boneNum - model->numRootBones][0]; - bone.localOffset[1] = model->trans[boneNum - model->numRootBones][1]; - bone.localOffset[2] = model->trans[boneNum - model->numRootBones][2]; - bone.localRotation = Quaternion32(QuatInt16::ToFloat(model->quats[boneNum - model->numRootBones][0]), - QuatInt16::ToFloat(model->quats[boneNum - model->numRootBones][1]), - QuatInt16::ToFloat(model->quats[boneNum - model->numRootBones][2]), - QuatInt16::ToFloat(model->quats[boneNum - model->numRootBones][3])); - } - - writer.AddBone(std::move(bone)); - } -} - -void AssetDumperXModel::AddXModelMaterials(AbstractXModelWriter& writer, DistinctMapper& materialMapper, const XModel* model) -{ - for (auto surfaceMaterialNum = 0; surfaceMaterialNum < model->numsurfs; surfaceMaterialNum++) - { - Material* material = model->materialHandles[surfaceMaterialNum]; - if (materialMapper.Add(material)) - { - XModelMaterial xMaterial; - xMaterial.ApplyDefaults(); - - xMaterial.name = material->info.name; - const auto* colorMap = GetMaterialColorMap(material); - if (colorMap) - xMaterial.colorMapName = std::string(colorMap->name); - - writer.AddMaterial(std::move(xMaterial)); - } - } -} - -void AssetDumperXModel::AddXModelObjects(AbstractXModelWriter& writer, const XModelSurfs* modelSurfs) -{ - for (auto surfIndex = 0u; surfIndex < modelSurfs->numsurfs; surfIndex++) - { - XModelObject object; - object.name = "surf" + std::to_string(surfIndex); - - writer.AddObject(std::move(object)); - } -} - -void AssetDumperXModel::AddXModelVertices(AbstractXModelWriter& writer, 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.packedVerts0[vertexIndex]; - vec2_t uv; - vec3_t normalVec; - vec4_t color; - - Common::Vec2UnpackTexCoords(v.texCoord, &uv); - Common::Vec3UnpackUnitVec(v.normal, &normalVec); - Common::Vec4UnpackGfxColor(v.color, &color); - - XModelVertex vertex{}; - vertex.coordinates[0] = v.xyz[0]; - vertex.coordinates[1] = v.xyz[1]; - vertex.coordinates[2] = v.xyz[2]; - vertex.normal[0] = normalVec[0]; - vertex.normal[1] = normalVec[1]; - vertex.normal[2] = normalVec[2]; - vertex.color[0] = color[0]; - vertex.color[1] = color[1]; - vertex.color[2] = color[2]; - vertex.color[3] = color[3]; - vertex.uv[0] = uv[0]; - vertex.uv[1] = uv[1]; - - writer.AddVertex(vertex); - } - } -} - -void AssetDumperXModel::AllocateXModelBoneWeights(const XModelSurfs* modelSurfs, XModelVertexBoneWeightCollection& weightCollection) -{ - weightCollection.totalWeightCount = 0u; - for (auto surfIndex = 0u; surfIndex < modelSurfs->numsurfs; surfIndex++) - { - const auto& surface = modelSurfs->surfs[surfIndex]; - - if (surface.vertList) - { - weightCollection.totalWeightCount += surface.vertListCount; - } - - if (surface.vertInfo.vertsBlend) - { - weightCollection.totalWeightCount += surface.vertInfo.vertCount[0] * 1; - weightCollection.totalWeightCount += surface.vertInfo.vertCount[1] * 2; - weightCollection.totalWeightCount += surface.vertInfo.vertCount[2] * 3; - weightCollection.totalWeightCount += surface.vertInfo.vertCount[3] * 4; - } - } - - weightCollection.weights = std::make_unique(weightCollection.totalWeightCount); -} - -void AssetDumperXModel::AddXModelVertexBoneWeights(AbstractXModelWriter& writer, - const XModelSurfs* modelSurfs, - XModelVertexBoneWeightCollection& weightCollection) -{ - size_t 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 = &weightCollection.weights[weightOffset]; - - weightCollection.weights[weightOffset++] = XModelBoneWeight{static_cast(vertList.boneOffset / sizeof(DObjSkelMat)), 1.0f}; - - for (auto vertListVertexOffset = 0u; vertListVertexOffset < vertList.vertCount; vertListVertexOffset++) - { - writer.AddVertexBoneWeights(XModelVertexBoneWeights{boneWeightOffset, 1}); - } - handledVertices += vertList.vertCount; - } - } - - auto vertsBlendOffset = 0u; - if (surface.vertInfo.vertsBlend) - { - // 1 bone weight - for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[0]; vertIndex++) - { - const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; - const auto boneIndex0 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat)); - weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex0, 1.0f}; - - vertsBlendOffset += 1; - - writer.AddVertexBoneWeights(XModelVertexBoneWeights{boneWeightOffset, 1}); - } - - // 2 bone weights - for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[1]; vertIndex++) - { - const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; - const auto boneIndex0 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat)); - const auto boneIndex1 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat)); - const auto boneWeight1 = HalfFloat::ToFloat(surface.vertInfo.vertsBlend[vertsBlendOffset + 2]); - const auto boneWeight0 = 1.0f - boneWeight1; - - weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex0, boneWeight0}; - weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex1, boneWeight1}; - - vertsBlendOffset += 3; - - writer.AddVertexBoneWeights(XModelVertexBoneWeights{boneWeightOffset, 2}); - } - - // 3 bone weights - for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[2]; vertIndex++) - { - const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; - const auto boneIndex0 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat)); - const auto boneIndex1 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat)); - const auto boneWeight1 = HalfFloat::ToFloat(surface.vertInfo.vertsBlend[vertsBlendOffset + 2]); - const auto boneIndex2 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 3] / sizeof(DObjSkelMat)); - const auto boneWeight2 = HalfFloat::ToFloat(surface.vertInfo.vertsBlend[vertsBlendOffset + 4]); - const auto boneWeight0 = 1.0f - boneWeight1 - boneWeight2; - - weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex0, boneWeight0}; - weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex1, boneWeight1}; - weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex2, boneWeight2}; - - vertsBlendOffset += 5; - - writer.AddVertexBoneWeights(XModelVertexBoneWeights{boneWeightOffset, 3}); - } - - // 4 bone weights - for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[3]; vertIndex++) - { - const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; - const auto boneIndex0 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat)); - const auto boneIndex1 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat)); - const auto boneWeight1 = HalfFloat::ToFloat(surface.vertInfo.vertsBlend[vertsBlendOffset + 2]); - const auto boneIndex2 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 3] / sizeof(DObjSkelMat)); - const auto boneWeight2 = HalfFloat::ToFloat(surface.vertInfo.vertsBlend[vertsBlendOffset + 4]); - const auto boneIndex3 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 5] / sizeof(DObjSkelMat)); - const auto boneWeight3 = HalfFloat::ToFloat(surface.vertInfo.vertsBlend[vertsBlendOffset + 6]); - const auto boneWeight0 = 1.0f - boneWeight1 - boneWeight2 - boneWeight3; - - weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex0, boneWeight0}; - weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex1, boneWeight1}; - weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex2, boneWeight2}; - weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex3, boneWeight3}; - - vertsBlendOffset += 7; - - writer.AddVertexBoneWeights(XModelVertexBoneWeights{boneWeightOffset, 4}); - } - - handledVertices += surface.vertInfo.vertCount[0] + surface.vertInfo.vertCount[1] + surface.vertInfo.vertCount[2] + surface.vertInfo.vertCount[3]; - } - - for (; handledVertices < surface.vertCount; handledVertices++) - { - writer.AddVertexBoneWeights(XModelVertexBoneWeights{nullptr, 0}); - } - } -} - -void AssetDumperXModel::AddXModelFaces(AbstractXModelWriter& writer, - const DistinctMapper& materialMapper, - const XModelSurfs* modelSurfs, - const int baseSurfaceIndex) -{ - for (auto surfIndex = 0u; surfIndex < modelSurfs->numsurfs; surfIndex++) - { - const auto& surface = modelSurfs->surfs[surfIndex]; - for (auto triIndex = 0u; triIndex < surface.triCount; triIndex++) - { - const auto& tri = surface.triIndices[triIndex]; - - XModelFace face{}; - face.vertexIndex[0] = tri[0] + surface.baseVertIndex; - face.vertexIndex[1] = tri[1] + surface.baseVertIndex; - face.vertexIndex[2] = tri[2] + surface.baseVertIndex; - face.objectIndex = static_cast(surfIndex); - face.materialIndex = static_cast(materialMapper.GetDistinctPositionByInputPosition(surfIndex + baseSurfaceIndex)); - writer.AddFace(face); - } - } -} - -void AssetDumperXModel::DumpXModelExportLod(const AssetDumpingContext& context, 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("model_export/" + std::string(modelSurfs->name) + ".XMODEL_EXPORT"); - - if (!assetFile) - return; - - const auto writer = XModelExportWriter::CreateWriterForVersion6(context.m_zone->m_game->GetShortName(), context.m_zone->m_name); - DistinctMapper materialMapper(model->numsurfs); - XModelVertexBoneWeightCollection boneWeightCollection; - AllocateXModelBoneWeights(modelSurfs, boneWeightCollection); - - AddXModelBones(context, *writer, model); - AddXModelMaterials(*writer, materialMapper, model); - AddXModelObjects(*writer, modelSurfs); - AddXModelVertices(*writer, modelSurfs); - AddXModelVertexBoneWeights(*writer, modelSurfs, boneWeightCollection); - AddXModelFaces(*writer, materialMapper, modelSurfs, model->lodInfo[lod].surfIndex); - - writer->Write(*assetFile); -} - -void AssetDumperXModel::DumpXModelExport(AssetDumpingContext& context, XAssetInfo* asset) -{ - auto* surfZoneState = context.GetZoneAssetDumperState(); - const auto* model = asset->Asset(); - for (auto currentLod = 0u; currentLod < model->numLods; currentLod++) - { - if (!model->lodInfo[currentLod].modelSurfs || !surfZoneState->ShouldDumpTechnique(model->lodInfo[currentLod].modelSurfs)) - continue; - DumpXModelExportLod(context, asset, currentLod); - } -} - void AssetDumperXModel::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) { - switch (ObjWriting::Configuration.ModelOutputFormat) - { - case ObjWriting::Configuration_t::ModelOutputFormat_e::OBJ: - DumpObj(context, asset); - break; - - case ObjWriting::Configuration_t::ModelOutputFormat_e::XMODEL_EXPORT: - DumpXModelExport(context, asset); - break; - - default: - assert(false); - break; - } + DumpXModelSurfs(context, asset); } diff --git a/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperXModel.h b/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperXModel.h index 6a6196a5..28880a59 100644 --- a/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperXModel.h +++ b/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperXModel.h @@ -2,37 +2,11 @@ #include "Dumping/AbstractAssetDumper.h" #include "Game/IW5/IW5.h" -#include "Utils/DistinctMapper.h" -#include "XModel/AbstractXModelWriter.h" -#include "XModel/Obj/ObjWriter.h" namespace IW5 { class AssetDumperXModel final : public AbstractAssetDumper { - static GfxImage* GetMaterialColorMap(const Material* material); - static GfxImage* GetMaterialNormalMap(const Material* material); - static GfxImage* GetMaterialSpecularMap(const Material* material); - - static void AddObjMaterials(ObjWriter& writer, DistinctMapper& materialMapper, const XModel* model); - static void AddObjObjects(ObjWriter& writer, const DistinctMapper& materialMapper, const XModelSurfs* modelSurfs, int baseSurfaceIndex); - static void AddObjVertices(ObjWriter& writer, const XModelSurfs* modelSurfs); - static void AddObjFaces(ObjWriter& writer, const XModelSurfs* modelSurfs); - static void DumpObjLod(const AssetDumpingContext& context, XAssetInfo* asset, const unsigned lod); - static void DumpObjMat(const AssetDumpingContext& context, XAssetInfo* asset); - static void DumpObj(AssetDumpingContext& context, XAssetInfo* asset); - - static void AddXModelBones(const AssetDumpingContext& context, AbstractXModelWriter& writer, const XModel* model); - static void AddXModelMaterials(AbstractXModelWriter& writer, DistinctMapper& materialMapper, const XModel* model); - static void AddXModelObjects(AbstractXModelWriter& writer, const XModelSurfs* modelSurfs); - static void AddXModelVertices(AbstractXModelWriter& writer, const XModelSurfs* modelSurfs); - static void AllocateXModelBoneWeights(const XModelSurfs* modelSurfs, XModelVertexBoneWeightCollection& weightCollection); - static void AddXModelVertexBoneWeights(AbstractXModelWriter& writer, const XModelSurfs* modelSurfs, XModelVertexBoneWeightCollection& weightCollection); - static void - AddXModelFaces(AbstractXModelWriter& writer, const DistinctMapper& materialMapper, const XModelSurfs* modelSurfs, int baseSurfaceIndex); - static void DumpXModelExportLod(const AssetDumpingContext& context, XAssetInfo* asset, unsigned lod); - static void DumpXModelExport(AssetDumpingContext& context, XAssetInfo* asset); - protected: bool ShouldDump(XAssetInfo* asset) override; void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; diff --git a/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperXModel.cpp b/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperXModel.cpp index ef75590a..3cf83c41 100644 --- a/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperXModel.cpp +++ b/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperXModel.cpp @@ -3,591 +3,617 @@ #include "Game/T5/CommonT5.h" #include "Math/Quaternion.h" #include "ObjWriting.h" +#include "Utils/DistinctMapper.h" #include "Utils/HalfFloat.h" #include "Utils/QuatInt16.h" +#include "XModel/AbstractXModelWriter.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 -#include +#include using namespace T5; +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 || def->semantic >= TS_COLOR0_MAP && def->semantic <= TS_COLOR15_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 AddObjMaterials(ObjWriter& writer, DistinctMapper& materialMapper, const XModel* model) + { + if (!model->materialHandles) + return; + + for (auto surfIndex = 0u; surfIndex < model->numsurfs; surfIndex++) + { + Material* material = model->materialHandles[surfIndex]; + if (!materialMapper.Add(material)) + continue; + + MtlMaterial mtl; + mtl.materialName = std::string(material->info.name); + + GfxImage* colorMap = GetMaterialColorMap(material); + GfxImage* normalMap = GetMaterialNormalMap(material); + GfxImage* specularMap = GetMaterialSpecularMap(material); + + if (colorMap != nullptr) + mtl.colorMapName = colorMap->name; + if (normalMap != nullptr) + mtl.normalMapName = normalMap->name; + if (specularMap != nullptr) + mtl.specularMapName = specularMap->name; + + writer.AddMaterial(std::move(mtl)); + } + } + + void AddObjObjects(ObjWriter& writer, const DistinctMapper& materialMapper, const XModel* model, const unsigned lod) + { + const auto surfCount = model->lodInfo[lod].numsurfs; + const auto baseSurfIndex = model->lodInfo[lod].surfIndex; + + for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++) + { + ObjObject object; + object.name = "surf" + std::to_string(surfIndex); + object.materialIndex = static_cast(materialMapper.GetDistinctPositionByInputPosition(surfIndex + baseSurfIndex)); + + writer.AddObject(std::move(object)); + } + } + + void AddObjVertices(ObjWriter& writer, const XModel* model, const unsigned lod) + { + const auto* surfs = &model->surfs[model->lodInfo[lod].surfIndex]; + const auto surfCount = model->lodInfo[lod].numsurfs; + + for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++) + { + const auto& surface = surfs[surfIndex]; + + for (auto vertexIndex = 0u; vertexIndex < surface.vertCount; vertexIndex++) + { + const auto& v = surface.verts0[vertexIndex]; + vec2_t uv; + vec3_t normalVec; + + Common::Vec2UnpackTexCoords(v.texCoord, &uv); + Common::Vec3UnpackUnitVec(v.normal, &normalVec); + + ObjVertex objVertex{}; + ObjNormal objNormal{}; + ObjUv objUv{}; + objVertex.coordinates[0] = v.xyz[0]; + objVertex.coordinates[1] = v.xyz[2]; + objVertex.coordinates[2] = -v.xyz[1]; + objNormal.normal[0] = normalVec[0]; + objNormal.normal[1] = normalVec[2]; + objNormal.normal[2] = -normalVec[1]; + objUv.uv[0] = uv[0]; + objUv.uv[1] = 1.0f - uv[1]; + + writer.AddVertex(static_cast(surfIndex), objVertex); + writer.AddNormal(static_cast(surfIndex), objNormal); + writer.AddUv(static_cast(surfIndex), objUv); + } + } + } + + void AddObjFaces(ObjWriter& writer, const XModel* model, const unsigned lod) + { + const auto* surfs = &model->surfs[model->lodInfo[lod].surfIndex]; + const auto surfCount = model->lodInfo[lod].numsurfs; + + for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++) + { + const auto& surface = surfs[surfIndex]; + for (auto triIndex = 0u; triIndex < surface.triCount; triIndex++) + { + const auto& tri = surface.triIndices[triIndex]; + + ObjFace face{}; + face.vertexIndex[0] = tri[2] + surface.baseVertIndex; + face.vertexIndex[1] = tri[1] + surface.baseVertIndex; + face.vertexIndex[2] = tri[0] + surface.baseVertIndex; + face.normalIndex[0] = face.vertexIndex[0]; + face.normalIndex[1] = face.vertexIndex[1]; + face.normalIndex[2] = face.vertexIndex[2]; + face.uvIndex[0] = face.vertexIndex[0]; + face.uvIndex[1] = face.vertexIndex[1]; + face.uvIndex[2] = face.vertexIndex[2]; + writer.AddFace(static_cast(surfIndex), face); + } + } + } + + void DumpObjMat(const AssetDumpingContext& context, const XAssetInfo* asset) + { + const auto* model = asset->Asset(); + const auto matFile = context.OpenAssetFile(std::format("model_export/{}.mtl", model->name)); + + if (!matFile) + return; + + ObjWriter writer(context.m_zone->m_game->GetShortName(), context.m_zone->m_name); + DistinctMapper materialMapper(model->numsurfs); + + AddObjMaterials(writer, materialMapper, model); + writer.WriteMtl(*matFile); + } + + void DumpObjLod(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; + + ObjWriter writer(context.m_zone->m_game->GetShortName(), context.m_zone->m_name); + DistinctMapper materialMapper(model->numsurfs); + + AddObjMaterials(writer, materialMapper, model); + AddObjObjects(writer, materialMapper, model, lod); + AddObjVertices(writer, model, lod); + AddObjFaces(writer, model, lod); + + writer.WriteObj(*assetFile, std::format("{}.mtl", model->name)); + } + + void AddXModelBones(const AssetDumpingContext& context, AbstractXModelWriter& writer, const XModel* model) + { + for (auto boneNum = 0u; boneNum < model->numBones; boneNum++) + { + XModelBone bone; + if (model->boneNames[boneNum] < context.m_zone->m_script_strings.Count()) + bone.name = context.m_zone->m_script_strings[model->boneNames[boneNum]]; + else + bone.name = "INVALID_BONE_NAME"; + + if (boneNum < model->numRootBones) + bone.parentIndex = -1; + else + bone.parentIndex = static_cast(boneNum - static_cast(model->parentList[boneNum - model->numRootBones])); + + bone.scale[0] = 1.0f; + bone.scale[1] = 1.0f; + bone.scale[2] = 1.0f; + + bone.globalOffset[0] = model->baseMat[boneNum].trans[0]; + bone.globalOffset[1] = model->baseMat[boneNum].trans[1]; + bone.globalOffset[2] = model->baseMat[boneNum].trans[2]; + bone.globalRotation = Quaternion32( + model->baseMat[boneNum].quat[0], model->baseMat[boneNum].quat[1], model->baseMat[boneNum].quat[2], model->baseMat[boneNum].quat[3]); + + if (boneNum < model->numRootBones) + { + bone.localOffset[0] = 0; + bone.localOffset[1] = 0; + bone.localOffset[2] = 0; + bone.localRotation = Quaternion32(0, 0, 0, 1); + } + else + { + bone.localOffset[0] = model->trans[boneNum - model->numRootBones][0]; + bone.localOffset[1] = model->trans[boneNum - model->numRootBones][1]; + bone.localOffset[2] = model->trans[boneNum - model->numRootBones][2]; + bone.localRotation = Quaternion32(QuatInt16::ToFloat(model->quats[boneNum - model->numRootBones][0]), + QuatInt16::ToFloat(model->quats[boneNum - model->numRootBones][1]), + QuatInt16::ToFloat(model->quats[boneNum - model->numRootBones][2]), + QuatInt16::ToFloat(model->quats[boneNum - model->numRootBones][3])); + } + + writer.AddBone(std::move(bone)); + } + } + + void AddXModelMaterials(AbstractXModelWriter& writer, DistinctMapper& materialMapper, const XModel* model) + { + for (auto surfaceMaterialNum = 0; surfaceMaterialNum < model->numsurfs; surfaceMaterialNum++) + { + Material* material = model->materialHandles[surfaceMaterialNum]; + if (materialMapper.Add(material)) + { + XModelMaterial xMaterial; + xMaterial.ApplyDefaults(); + + xMaterial.name = material->info.name; + const auto* colorMap = GetMaterialColorMap(material); + if (colorMap) + xMaterial.colorMapName = std::string(colorMap->name); + + writer.AddMaterial(std::move(xMaterial)); + } + } + } + + void AddXModelObjects(AbstractXModelWriter& writer, const XModel* model, const unsigned lod) + { + const auto surfCount = model->lodInfo[lod].numsurfs; + + for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++) + { + XModelObject object; + object.name = "surf" + std::to_string(surfIndex); + + writer.AddObject(std::move(object)); + } + } + + void AddXModelVertices(AbstractXModelWriter& writer, const XModel* model, const unsigned lod) + { + const auto* surfs = &model->surfs[model->lodInfo[lod].surfIndex]; + const auto surfCount = model->lodInfo[lod].numsurfs; + + for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++) + { + const auto& surface = surfs[surfIndex]; + + for (auto vertexIndex = 0u; vertexIndex < surface.vertCount; vertexIndex++) + { + const auto& v = surface.verts0[vertexIndex]; + vec2_t uv; + vec3_t normalVec; + vec4_t color; + + Common::Vec2UnpackTexCoords(v.texCoord, &uv); + Common::Vec3UnpackUnitVec(v.normal, &normalVec); + Common::Vec4UnpackGfxColor(v.color, &color); + + XModelVertex vertex{}; + vertex.coordinates[0] = v.xyz[0]; + vertex.coordinates[1] = v.xyz[1]; + vertex.coordinates[2] = v.xyz[2]; + vertex.normal[0] = normalVec[0]; + vertex.normal[1] = normalVec[1]; + vertex.normal[2] = normalVec[2]; + vertex.color[0] = color[0]; + vertex.color[1] = color[1]; + vertex.color[2] = color[2]; + vertex.color[3] = color[3]; + vertex.uv[0] = uv[0]; + vertex.uv[1] = uv[1]; + + writer.AddVertex(vertex); + } + } + } + + void AllocateXModelBoneWeights(const XModel* model, const unsigned lod, XModelVertexBoneWeightCollection& weightCollection) + { + const auto* surfs = &model->surfs[model->lodInfo[lod].surfIndex]; + const auto surfCount = model->lodInfo[lod].numsurfs; + + weightCollection.totalWeightCount = 0u; + for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++) + { + const auto& surface = surfs[surfIndex]; + + if (surface.vertList) + { + weightCollection.totalWeightCount += surface.vertListCount; + } + + if (surface.vertInfo.vertsBlend) + { + weightCollection.totalWeightCount += surface.vertInfo.vertCount[0] * 1; + weightCollection.totalWeightCount += surface.vertInfo.vertCount[1] * 2; + weightCollection.totalWeightCount += surface.vertInfo.vertCount[2] * 3; + weightCollection.totalWeightCount += surface.vertInfo.vertCount[3] * 4; + } + } + + weightCollection.weights = std::make_unique(weightCollection.totalWeightCount); + } + + void AddXModelVertexBoneWeights(AbstractXModelWriter& writer, const XModel* model, const unsigned lod, XModelVertexBoneWeightCollection& weightCollection) + { + const auto* surfs = &model->surfs[model->lodInfo[lod].surfIndex]; + const auto surfCount = model->lodInfo[lod].numsurfs; + + size_t weightOffset = 0u; + + for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++) + { + const auto& surface = surfs[surfIndex]; + auto handledVertices = 0u; + + if (surface.vertList) + { + for (auto vertListIndex = 0u; vertListIndex < surface.vertListCount; vertListIndex++) + { + const auto& vertList = surface.vertList[vertListIndex]; + const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; + + weightCollection.weights[weightOffset++] = XModelBoneWeight{static_cast(vertList.boneOffset / sizeof(DObjSkelMat)), 1.0f}; + + for (auto vertListVertexOffset = 0u; vertListVertexOffset < vertList.vertCount; vertListVertexOffset++) + { + writer.AddVertexBoneWeights(XModelVertexBoneWeights{boneWeightOffset, 1}); + } + handledVertices += vertList.vertCount; + } + } + + auto vertsBlendOffset = 0u; + if (surface.vertInfo.vertsBlend) + { + // 1 bone weight + for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[0]; vertIndex++) + { + const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; + const auto boneIndex0 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat)); + weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex0, 1.0f}; + + vertsBlendOffset += 1; + + writer.AddVertexBoneWeights(XModelVertexBoneWeights{boneWeightOffset, 1}); + } + + // 2 bone weights + for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[1]; vertIndex++) + { + const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; + const auto boneIndex0 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat)); + const auto boneIndex1 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat)); + const auto boneWeight1 = HalfFloat::ToFloat(surface.vertInfo.vertsBlend[vertsBlendOffset + 2]); + const auto boneWeight0 = 1.0f - boneWeight1; + + weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex0, boneWeight0}; + weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex1, boneWeight1}; + + vertsBlendOffset += 3; + + writer.AddVertexBoneWeights(XModelVertexBoneWeights{boneWeightOffset, 2}); + } + + // 3 bone weights + for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[2]; vertIndex++) + { + const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; + const auto boneIndex0 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat)); + const auto boneIndex1 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat)); + const auto boneWeight1 = HalfFloat::ToFloat(surface.vertInfo.vertsBlend[vertsBlendOffset + 2]); + const auto boneIndex2 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 3] / sizeof(DObjSkelMat)); + const auto boneWeight2 = HalfFloat::ToFloat(surface.vertInfo.vertsBlend[vertsBlendOffset + 4]); + const auto boneWeight0 = 1.0f - boneWeight1 - boneWeight2; + + weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex0, boneWeight0}; + weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex1, boneWeight1}; + weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex2, boneWeight2}; + + vertsBlendOffset += 5; + + writer.AddVertexBoneWeights(XModelVertexBoneWeights{boneWeightOffset, 3}); + } + + // 4 bone weights + for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[3]; vertIndex++) + { + const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; + const auto boneIndex0 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat)); + const auto boneIndex1 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat)); + const auto boneWeight1 = HalfFloat::ToFloat(surface.vertInfo.vertsBlend[vertsBlendOffset + 2]); + const auto boneIndex2 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 3] / sizeof(DObjSkelMat)); + const auto boneWeight2 = HalfFloat::ToFloat(surface.vertInfo.vertsBlend[vertsBlendOffset + 4]); + const auto boneIndex3 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 5] / sizeof(DObjSkelMat)); + const auto boneWeight3 = HalfFloat::ToFloat(surface.vertInfo.vertsBlend[vertsBlendOffset + 6]); + const auto boneWeight0 = 1.0f - boneWeight1 - boneWeight2 - boneWeight3; + + weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex0, boneWeight0}; + weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex1, boneWeight1}; + weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex2, boneWeight2}; + weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex3, boneWeight3}; + + vertsBlendOffset += 7; + + writer.AddVertexBoneWeights(XModelVertexBoneWeights{boneWeightOffset, 4}); + } + + handledVertices += + surface.vertInfo.vertCount[0] + surface.vertInfo.vertCount[1] + surface.vertInfo.vertCount[2] + surface.vertInfo.vertCount[3]; + } + + for (; handledVertices < surface.vertCount; handledVertices++) + { + writer.AddVertexBoneWeights(XModelVertexBoneWeights{nullptr, 0}); + } + } + } + + void AddXModelFaces(AbstractXModelWriter& writer, const DistinctMapper& materialMapper, const XModel* model, const unsigned lod) + { + const auto* surfs = &model->surfs[model->lodInfo[lod].surfIndex]; + const auto surfCount = model->lodInfo[lod].numsurfs; + const auto baseSurfIndex = model->lodInfo[lod].surfIndex; + + for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++) + { + const auto& surface = surfs[surfIndex]; + for (auto triIndex = 0u; triIndex < surface.triCount; triIndex++) + { + const auto& tri = surface.triIndices[triIndex]; + + XModelFace face{}; + face.vertexIndex[0] = tri[0] + surface.baseVertIndex; + face.vertexIndex[1] = tri[1] + surface.baseVertIndex; + face.vertexIndex[2] = tri[2] + surface.baseVertIndex; + face.objectIndex = static_cast(surfIndex); + face.materialIndex = static_cast(materialMapper.GetDistinctPositionByInputPosition(surfIndex + baseSurfIndex)); + writer.AddFace(face); + } + } + } + + void PopulateXModelWriter(const AssetDumpingContext& context, const unsigned lod, const XModel* model, AbstractXModelWriter& writer) + { + DistinctMapper materialMapper(model->numsurfs); + XModelVertexBoneWeightCollection boneWeightCollection; + AllocateXModelBoneWeights(model, lod, boneWeightCollection); + + AddXModelBones(context, writer, model); + AddXModelMaterials(writer, materialMapper, model); + AddXModelObjects(writer, model, lod); + AddXModelVertices(writer, model, lod); + AddXModelVertexBoneWeights(writer, model, lod, boneWeightCollection); + AddXModelFaces(writer, materialMapper, model, lod); + } + + void DumpXModelExportLod(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 = XModelExportWriter::CreateWriterForVersion6(context.m_zone->m_game->GetShortName(), context.m_zone->m_name); + PopulateXModelWriter(context, lod, model, *writer); + + writer->Write(*assetFile); + } + + template void DumpGltfLod(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); + PopulateXModelWriter(context, lod, model, *writer); + + writer->Write(*assetFile); + } + + void DumpXModelSurfs(const AssetDumpingContext& context, const XAssetInfo* asset) + { + const auto* model = asset->Asset(); + + if (ObjWriting::Configuration.ModelOutputFormat == ObjWriting::Configuration_t::ModelOutputFormat_e::OBJ) + DumpObjMat(context, asset); + + for (auto currentLod = 0u; currentLod < model->numLods; currentLod++) + { + switch (ObjWriting::Configuration.ModelOutputFormat) + { + case ObjWriting::Configuration_t::ModelOutputFormat_e::OBJ: + DumpObjLod(context, asset, currentLod); + break; + + case ObjWriting::Configuration_t::ModelOutputFormat_e::XMODEL_EXPORT: + DumpXModelExportLod(context, asset, currentLod); + break; + + case ObjWriting::Configuration_t::ModelOutputFormat_e::GLTF: + DumpGltfLod(context, asset, currentLod, ".gltf"); + break; + + case ObjWriting::Configuration_t::ModelOutputFormat_e::GLB: + DumpGltfLod(context, asset, currentLod, ".glb"); + break; + + default: + assert(false); + break; + } + } + } +} // namespace + bool AssetDumperXModel::ShouldDump(XAssetInfo* asset) { return !asset->m_name.empty() && asset->m_name[0] != ','; } -GfxImage* AssetDumperXModel::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 || def->semantic >= TS_COLOR0_MAP && def->semantic <= TS_COLOR15_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* AssetDumperXModel::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* AssetDumperXModel::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 AssetDumperXModel::AddObjMaterials(ObjWriter& writer, DistinctMapper& materialMapper, const XModel* model) -{ - if (!model->materialHandles) - return; - - for (auto surfIndex = 0u; surfIndex < model->numsurfs; surfIndex++) - { - Material* material = model->materialHandles[surfIndex]; - if (!materialMapper.Add(material)) - continue; - - MtlMaterial mtl; - mtl.materialName = std::string(material->info.name); - - GfxImage* colorMap = GetMaterialColorMap(material); - GfxImage* normalMap = GetMaterialNormalMap(material); - GfxImage* specularMap = GetMaterialSpecularMap(material); - - if (colorMap != nullptr) - mtl.colorMapName = colorMap->name; - if (normalMap != nullptr) - mtl.normalMapName = normalMap->name; - if (specularMap != nullptr) - mtl.specularMapName = specularMap->name; - - writer.AddMaterial(std::move(mtl)); - } -} - -void AssetDumperXModel::AddObjObjects(ObjWriter& writer, const DistinctMapper& materialMapper, const XModel* model, const unsigned lod) -{ - const auto surfCount = model->lodInfo[lod].numsurfs; - const auto baseSurfIndex = model->lodInfo[lod].surfIndex; - - for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++) - { - ObjObject object; - object.name = "surf" + std::to_string(surfIndex); - object.materialIndex = static_cast(materialMapper.GetDistinctPositionByInputPosition(surfIndex + baseSurfIndex)); - - writer.AddObject(std::move(object)); - } -} - -void AssetDumperXModel::AddObjVertices(ObjWriter& writer, const XModel* model, const unsigned lod) -{ - const auto* surfs = &model->surfs[model->lodInfo[lod].surfIndex]; - const auto surfCount = model->lodInfo[lod].numsurfs; - - for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++) - { - const auto& surface = surfs[surfIndex]; - - for (auto vertexIndex = 0u; vertexIndex < surface.vertCount; vertexIndex++) - { - const auto& v = surface.verts0[vertexIndex]; - vec2_t uv; - vec3_t normalVec; - - Common::Vec2UnpackTexCoords(v.texCoord, &uv); - Common::Vec3UnpackUnitVec(v.normal, &normalVec); - - ObjVertex objVertex{}; - ObjNormal objNormal{}; - ObjUv objUv{}; - objVertex.coordinates[0] = v.xyz[0]; - objVertex.coordinates[1] = v.xyz[2]; - objVertex.coordinates[2] = -v.xyz[1]; - objNormal.normal[0] = normalVec[0]; - objNormal.normal[1] = normalVec[2]; - objNormal.normal[2] = -normalVec[1]; - objUv.uv[0] = uv[0]; - objUv.uv[1] = 1.0f - uv[1]; - - writer.AddVertex(static_cast(surfIndex), objVertex); - writer.AddNormal(static_cast(surfIndex), objNormal); - writer.AddUv(static_cast(surfIndex), objUv); - } - } -} - -void AssetDumperXModel::AddObjFaces(ObjWriter& writer, const XModel* model, const unsigned lod) -{ - const auto* surfs = &model->surfs[model->lodInfo[lod].surfIndex]; - const auto surfCount = model->lodInfo[lod].numsurfs; - - for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++) - { - const auto& surface = surfs[surfIndex]; - for (auto triIndex = 0u; triIndex < surface.triCount; triIndex++) - { - const auto& tri = surface.triIndices[triIndex]; - - ObjFace face{}; - face.vertexIndex[0] = tri[2] + surface.baseVertIndex; - face.vertexIndex[1] = tri[1] + surface.baseVertIndex; - face.vertexIndex[2] = tri[0] + surface.baseVertIndex; - face.normalIndex[0] = face.vertexIndex[0]; - face.normalIndex[1] = face.vertexIndex[1]; - face.normalIndex[2] = face.vertexIndex[2]; - face.uvIndex[0] = face.vertexIndex[0]; - face.uvIndex[1] = face.vertexIndex[1]; - face.uvIndex[2] = face.vertexIndex[2]; - writer.AddFace(static_cast(surfIndex), face); - } - } -} - -void AssetDumperXModel::DumpObjMat(const AssetDumpingContext& context, XAssetInfo* asset) -{ - const auto* model = asset->Asset(); - const auto matFile = context.OpenAssetFile("model_export/" + std::string(model->name) + ".mtl"); - - if (!matFile) - return; - - ObjWriter writer(context.m_zone->m_game->GetShortName(), context.m_zone->m_name); - DistinctMapper materialMapper(model->numsurfs); - - AddObjMaterials(writer, materialMapper, model); - writer.WriteMtl(*matFile); -} - -void AssetDumperXModel::DumpObjLod(AssetDumpingContext& context, XAssetInfo* asset, const unsigned lod) -{ - const auto* model = asset->Asset(); - std::ostringstream ss; - ss << "model_export/" << model->name << "_lod" << lod << ".OBJ"; - - const auto assetFile = context.OpenAssetFile(ss.str()); - - if (!assetFile) - return; - - ObjWriter writer(context.m_zone->m_game->GetShortName(), context.m_zone->m_name); - DistinctMapper materialMapper(model->numsurfs); - - AddObjMaterials(writer, materialMapper, model); - AddObjObjects(writer, materialMapper, model, lod); - AddObjVertices(writer, model, lod); - AddObjFaces(writer, model, lod); - - writer.WriteObj(*assetFile, std::string(model->name) + ".mtl"); -} - -void AssetDumperXModel::DumpObj(AssetDumpingContext& context, XAssetInfo* asset) -{ - const auto* model = asset->Asset(); - - DumpObjMat(context, asset); - for (auto currentLod = 0u; currentLod < model->numLods; currentLod++) - { - DumpObjLod(context, asset, currentLod); - } -} - -void AssetDumperXModel::AddXModelBones(const AssetDumpingContext& context, AbstractXModelWriter& writer, const XModel* model) -{ - for (auto boneNum = 0u; boneNum < model->numBones; boneNum++) - { - XModelBone bone; - if (model->boneNames[boneNum] < context.m_zone->m_script_strings.Count()) - bone.name = context.m_zone->m_script_strings[model->boneNames[boneNum]]; - else - bone.name = "INVALID_BONE_NAME"; - - if (boneNum < model->numRootBones) - bone.parentIndex = -1; - else - bone.parentIndex = static_cast(boneNum - static_cast(model->parentList[boneNum - model->numRootBones])); - - bone.scale[0] = 1.0f; - bone.scale[1] = 1.0f; - bone.scale[2] = 1.0f; - - bone.globalOffset[0] = model->baseMat[boneNum].trans[0]; - bone.globalOffset[1] = model->baseMat[boneNum].trans[1]; - bone.globalOffset[2] = model->baseMat[boneNum].trans[2]; - bone.globalRotation = - Quaternion32(model->baseMat[boneNum].quat[0], model->baseMat[boneNum].quat[1], model->baseMat[boneNum].quat[2], model->baseMat[boneNum].quat[3]); - - if (boneNum < model->numRootBones) - { - bone.localOffset[0] = 0; - bone.localOffset[1] = 0; - bone.localOffset[2] = 0; - bone.localRotation = Quaternion32(0, 0, 0, 1); - } - else - { - bone.localOffset[0] = model->trans[boneNum - model->numRootBones][0]; - bone.localOffset[1] = model->trans[boneNum - model->numRootBones][1]; - bone.localOffset[2] = model->trans[boneNum - model->numRootBones][2]; - bone.localRotation = Quaternion32(QuatInt16::ToFloat(model->quats[boneNum - model->numRootBones][0]), - QuatInt16::ToFloat(model->quats[boneNum - model->numRootBones][1]), - QuatInt16::ToFloat(model->quats[boneNum - model->numRootBones][2]), - QuatInt16::ToFloat(model->quats[boneNum - model->numRootBones][3])); - } - - writer.AddBone(std::move(bone)); - } -} - -void AssetDumperXModel::AddXModelMaterials(AbstractXModelWriter& writer, DistinctMapper& materialMapper, const XModel* model) -{ - for (auto surfaceMaterialNum = 0; surfaceMaterialNum < model->numsurfs; surfaceMaterialNum++) - { - Material* material = model->materialHandles[surfaceMaterialNum]; - if (materialMapper.Add(material)) - { - XModelMaterial xMaterial; - xMaterial.ApplyDefaults(); - - xMaterial.name = material->info.name; - const auto* colorMap = GetMaterialColorMap(material); - if (colorMap) - xMaterial.colorMapName = std::string(colorMap->name); - - writer.AddMaterial(std::move(xMaterial)); - } - } -} - -void AssetDumperXModel::AddXModelObjects(AbstractXModelWriter& writer, const XModel* model, const unsigned lod) -{ - const auto surfCount = model->lodInfo[lod].numsurfs; - - for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++) - { - XModelObject object; - object.name = "surf" + std::to_string(surfIndex); - - writer.AddObject(std::move(object)); - } -} - -void AssetDumperXModel::AddXModelVertices(AbstractXModelWriter& writer, const XModel* model, const unsigned lod) -{ - const auto* surfs = &model->surfs[model->lodInfo[lod].surfIndex]; - const auto surfCount = model->lodInfo[lod].numsurfs; - - for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++) - { - const auto& surface = surfs[surfIndex]; - - for (auto vertexIndex = 0u; vertexIndex < surface.vertCount; vertexIndex++) - { - const auto& v = surface.verts0[vertexIndex]; - vec2_t uv; - vec3_t normalVec; - vec4_t color; - - Common::Vec2UnpackTexCoords(v.texCoord, &uv); - Common::Vec3UnpackUnitVec(v.normal, &normalVec); - Common::Vec4UnpackGfxColor(v.color, &color); - - XModelVertex vertex{}; - vertex.coordinates[0] = v.xyz[0]; - vertex.coordinates[1] = v.xyz[1]; - vertex.coordinates[2] = v.xyz[2]; - vertex.normal[0] = normalVec[0]; - vertex.normal[1] = normalVec[1]; - vertex.normal[2] = normalVec[2]; - vertex.color[0] = color[0]; - vertex.color[1] = color[1]; - vertex.color[2] = color[2]; - vertex.color[3] = color[3]; - vertex.uv[0] = uv[0]; - vertex.uv[1] = uv[1]; - - writer.AddVertex(vertex); - } - } -} - -void AssetDumperXModel::AllocateXModelBoneWeights(const XModel* model, const unsigned lod, XModelVertexBoneWeightCollection& weightCollection) -{ - const auto* surfs = &model->surfs[model->lodInfo[lod].surfIndex]; - const auto surfCount = model->lodInfo[lod].numsurfs; - - weightCollection.totalWeightCount = 0u; - for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++) - { - const auto& surface = surfs[surfIndex]; - - if (surface.vertList) - { - weightCollection.totalWeightCount += surface.vertListCount; - } - - if (surface.vertInfo.vertsBlend) - { - weightCollection.totalWeightCount += surface.vertInfo.vertCount[0] * 1; - weightCollection.totalWeightCount += surface.vertInfo.vertCount[1] * 2; - weightCollection.totalWeightCount += surface.vertInfo.vertCount[2] * 3; - weightCollection.totalWeightCount += surface.vertInfo.vertCount[3] * 4; - } - } - - weightCollection.weights = std::make_unique(weightCollection.totalWeightCount); -} - -void AssetDumperXModel::AddXModelVertexBoneWeights(AbstractXModelWriter& writer, - const XModel* model, - const unsigned lod, - XModelVertexBoneWeightCollection& weightCollection) -{ - const auto* surfs = &model->surfs[model->lodInfo[lod].surfIndex]; - const auto surfCount = model->lodInfo[lod].numsurfs; - - size_t weightOffset = 0u; - - for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++) - { - const auto& surface = surfs[surfIndex]; - auto handledVertices = 0u; - - if (surface.vertList) - { - for (auto vertListIndex = 0u; vertListIndex < surface.vertListCount; vertListIndex++) - { - const auto& vertList = surface.vertList[vertListIndex]; - const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; - - weightCollection.weights[weightOffset++] = XModelBoneWeight{static_cast(vertList.boneOffset / sizeof(DObjSkelMat)), 1.0f}; - - for (auto vertListVertexOffset = 0u; vertListVertexOffset < vertList.vertCount; vertListVertexOffset++) - { - writer.AddVertexBoneWeights(XModelVertexBoneWeights{boneWeightOffset, 1}); - } - handledVertices += vertList.vertCount; - } - } - - auto vertsBlendOffset = 0u; - if (surface.vertInfo.vertsBlend) - { - // 1 bone weight - for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[0]; vertIndex++) - { - const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; - const auto boneIndex0 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat)); - weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex0, 1.0f}; - - vertsBlendOffset += 1; - - writer.AddVertexBoneWeights(XModelVertexBoneWeights{boneWeightOffset, 1}); - } - - // 2 bone weights - for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[1]; vertIndex++) - { - const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; - const auto boneIndex0 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat)); - const auto boneIndex1 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat)); - const auto boneWeight1 = HalfFloat::ToFloat(surface.vertInfo.vertsBlend[vertsBlendOffset + 2]); - const auto boneWeight0 = 1.0f - boneWeight1; - - weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex0, boneWeight0}; - weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex1, boneWeight1}; - - vertsBlendOffset += 3; - - writer.AddVertexBoneWeights(XModelVertexBoneWeights{boneWeightOffset, 2}); - } - - // 3 bone weights - for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[2]; vertIndex++) - { - const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; - const auto boneIndex0 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat)); - const auto boneIndex1 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat)); - const auto boneWeight1 = HalfFloat::ToFloat(surface.vertInfo.vertsBlend[vertsBlendOffset + 2]); - const auto boneIndex2 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 3] / sizeof(DObjSkelMat)); - const auto boneWeight2 = HalfFloat::ToFloat(surface.vertInfo.vertsBlend[vertsBlendOffset + 4]); - const auto boneWeight0 = 1.0f - boneWeight1 - boneWeight2; - - weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex0, boneWeight0}; - weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex1, boneWeight1}; - weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex2, boneWeight2}; - - vertsBlendOffset += 5; - - writer.AddVertexBoneWeights(XModelVertexBoneWeights{boneWeightOffset, 3}); - } - - // 4 bone weights - for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[3]; vertIndex++) - { - const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; - const auto boneIndex0 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat)); - const auto boneIndex1 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat)); - const auto boneWeight1 = HalfFloat::ToFloat(surface.vertInfo.vertsBlend[vertsBlendOffset + 2]); - const auto boneIndex2 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 3] / sizeof(DObjSkelMat)); - const auto boneWeight2 = HalfFloat::ToFloat(surface.vertInfo.vertsBlend[vertsBlendOffset + 4]); - const auto boneIndex3 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 5] / sizeof(DObjSkelMat)); - const auto boneWeight3 = HalfFloat::ToFloat(surface.vertInfo.vertsBlend[vertsBlendOffset + 6]); - const auto boneWeight0 = 1.0f - boneWeight1 - boneWeight2 - boneWeight3; - - weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex0, boneWeight0}; - weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex1, boneWeight1}; - weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex2, boneWeight2}; - weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex3, boneWeight3}; - - vertsBlendOffset += 7; - - writer.AddVertexBoneWeights(XModelVertexBoneWeights{boneWeightOffset, 4}); - } - - handledVertices += surface.vertInfo.vertCount[0] + surface.vertInfo.vertCount[1] + surface.vertInfo.vertCount[2] + surface.vertInfo.vertCount[3]; - } - - for (; handledVertices < surface.vertCount; handledVertices++) - { - writer.AddVertexBoneWeights(XModelVertexBoneWeights{nullptr, 0}); - } - } -} - -void AssetDumperXModel::AddXModelFaces(AbstractXModelWriter& writer, const DistinctMapper& materialMapper, const XModel* model, const unsigned lod) -{ - const auto* surfs = &model->surfs[model->lodInfo[lod].surfIndex]; - const auto surfCount = model->lodInfo[lod].numsurfs; - const auto baseSurfIndex = model->lodInfo[lod].surfIndex; - - for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++) - { - const auto& surface = surfs[surfIndex]; - for (auto triIndex = 0u; triIndex < surface.triCount; triIndex++) - { - const auto& tri = surface.triIndices[triIndex]; - - XModelFace face{}; - face.vertexIndex[0] = tri[0] + surface.baseVertIndex; - face.vertexIndex[1] = tri[1] + surface.baseVertIndex; - face.vertexIndex[2] = tri[2] + surface.baseVertIndex; - face.objectIndex = static_cast(surfIndex); - face.materialIndex = static_cast(materialMapper.GetDistinctPositionByInputPosition(surfIndex + baseSurfIndex)); - writer.AddFace(face); - } - } -} - -void AssetDumperXModel::DumpXModelExportLod(const AssetDumpingContext& context, XAssetInfo* asset, const unsigned lod) -{ - const auto* model = asset->Asset(); - - std::ostringstream ss; - ss << "model_export/" << model->name << "_lod" << lod << ".XMODEL_EXPORT"; - - const auto assetFile = context.OpenAssetFile(ss.str()); - - if (!assetFile) - return; - - const auto writer = XModelExportWriter::CreateWriterForVersion6(context.m_zone->m_game->GetShortName(), context.m_zone->m_name); - DistinctMapper materialMapper(model->numsurfs); - XModelVertexBoneWeightCollection boneWeightCollection; - AllocateXModelBoneWeights(model, lod, boneWeightCollection); - - AddXModelBones(context, *writer, model); - AddXModelMaterials(*writer, materialMapper, model); - AddXModelObjects(*writer, model, lod); - AddXModelVertices(*writer, model, lod); - AddXModelVertexBoneWeights(*writer, model, lod, boneWeightCollection); - AddXModelFaces(*writer, materialMapper, model, lod); - - writer->Write(*assetFile); -} - -void AssetDumperXModel::DumpXModelExport(const AssetDumpingContext& context, XAssetInfo* asset) -{ - const auto* model = asset->Asset(); - for (auto currentLod = 0u; currentLod < model->numLods; currentLod++) - { - DumpXModelExportLod(context, asset, currentLod); - } -} - void AssetDumperXModel::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) { - switch (ObjWriting::Configuration.ModelOutputFormat) - { - case ObjWriting::Configuration_t::ModelOutputFormat_e::OBJ: - DumpObj(context, asset); - break; - - case ObjWriting::Configuration_t::ModelOutputFormat_e::XMODEL_EXPORT: - DumpXModelExport(context, asset); - break; - - default: - assert(false); - break; - } + DumpXModelSurfs(context, asset); } diff --git a/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperXModel.h b/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperXModel.h index 155d79d0..08a1a274 100644 --- a/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperXModel.h +++ b/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperXModel.h @@ -2,37 +2,11 @@ #include "Dumping/AbstractAssetDumper.h" #include "Game/T5/T5.h" -#include "Utils/DistinctMapper.h" -#include "XModel/AbstractXModelWriter.h" -#include "XModel/Obj/ObjWriter.h" namespace T5 { class AssetDumperXModel final : public AbstractAssetDumper { - static GfxImage* GetMaterialColorMap(const Material* material); - static GfxImage* GetMaterialNormalMap(const Material* material); - static GfxImage* GetMaterialSpecularMap(const Material* material); - - static void AddObjMaterials(ObjWriter& writer, DistinctMapper& materialMapper, const XModel* model); - static void AddObjObjects(ObjWriter& writer, const DistinctMapper& materialMapper, const XModel* model, unsigned lod); - static void AddObjVertices(ObjWriter& writer, const XModel* model, unsigned lod); - static void AddObjFaces(ObjWriter& writer, const XModel* model, unsigned lod); - static void DumpObjLod(AssetDumpingContext& context, XAssetInfo* asset, unsigned lod); - static void DumpObjMat(const AssetDumpingContext& context, XAssetInfo* asset); - static void DumpObj(AssetDumpingContext& context, XAssetInfo* asset); - - static void AddXModelBones(const AssetDumpingContext& context, AbstractXModelWriter& writer, const XModel* model); - static void AddXModelMaterials(AbstractXModelWriter& writer, DistinctMapper& materialMapper, const XModel* model); - static void AddXModelObjects(AbstractXModelWriter& writer, const XModel* model, unsigned lod); - static void AddXModelVertices(AbstractXModelWriter& writer, const XModel* model, unsigned lod); - static void AllocateXModelBoneWeights(const XModel* model, unsigned lod, XModelVertexBoneWeightCollection& weightCollection); - static void - AddXModelVertexBoneWeights(AbstractXModelWriter& writer, const XModel* model, unsigned lod, XModelVertexBoneWeightCollection& weightCollection); - static void AddXModelFaces(AbstractXModelWriter& writer, const DistinctMapper& materialMapper, const XModel* model, unsigned lod); - static void DumpXModelExportLod(const AssetDumpingContext& context, XAssetInfo* asset, unsigned lod); - static void DumpXModelExport(const AssetDumpingContext& context, XAssetInfo* asset); - protected: bool ShouldDump(XAssetInfo* asset) override; void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; diff --git a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperXModel.cpp b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperXModel.cpp index 712cf309..50753b03 100644 --- a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperXModel.cpp +++ b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperXModel.cpp @@ -3,615 +3,641 @@ #include "Game/T6/CommonT6.h" #include "Math/Quaternion.h" #include "ObjWriting.h" +#include "Utils/DistinctMapper.h" #include "Utils/HalfFloat.h" #include "Utils/QuatInt16.h" +#include "XModel/AbstractXModelWriter.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 -#include +#include using namespace T6; +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 || def->semantic >= TS_COLOR0_MAP && def->semantic <= TS_COLOR15_MAP) + potentialTextureDefs.push_back(def); + } + + if (potentialTextureDefs.empty()) + return nullptr; + if (potentialTextureDefs.size() == 1) + return potentialTextureDefs[0]->image; + + for (const auto* def : potentialTextureDefs) + { + if (tolower(def->nameStart) == 'c' && tolower(def->nameEnd) == 'p') + return def->image; + } + + for (const auto* def : potentialTextureDefs) + { + if (tolower(def->nameStart) == 'r' && tolower(def->nameEnd) == 'k') + return def->image; + } + + for (const auto* def : potentialTextureDefs) + { + if (tolower(def->nameStart) == 'd' && tolower(def->nameEnd) == 'p') + return def->image; + } + + return potentialTextureDefs[0]->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]->image; + + for (const auto* def : potentialTextureDefs) + { + if (def->nameStart == 'n' && def->nameEnd == 'p') + return def->image; + } + + return potentialTextureDefs[0]->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]->image; + + for (const auto* def : potentialTextureDefs) + { + if (def->nameStart == 's' && def->nameEnd == 'p') + return def->image; + } + + return potentialTextureDefs[0]->image; + } + + void AddObjMaterials(ObjWriter& writer, DistinctMapper& materialMapper, const XModel* model) + { + if (!model->materialHandles) + return; + + for (auto surfIndex = 0u; surfIndex < model->numsurfs; surfIndex++) + { + Material* material = model->materialHandles[surfIndex]; + if (!materialMapper.Add(material)) + continue; + + MtlMaterial mtl; + mtl.materialName = std::string(material->info.name); + + GfxImage* colorMap = GetMaterialColorMap(material); + GfxImage* normalMap = GetMaterialNormalMap(material); + GfxImage* specularMap = GetMaterialSpecularMap(material); + + if (colorMap != nullptr) + mtl.colorMapName = colorMap->name; + if (normalMap != nullptr) + mtl.normalMapName = normalMap->name; + if (specularMap != nullptr) + mtl.specularMapName = specularMap->name; + + writer.AddMaterial(std::move(mtl)); + } + } + + void AddObjObjects(ObjWriter& writer, const DistinctMapper& materialMapper, const XModel* model, const unsigned lod) + { + const auto surfCount = model->lodInfo[lod].numsurfs; + const auto baseSurfIndex = model->lodInfo[lod].surfIndex; + + for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++) + { + ObjObject object; + object.name = "surf" + std::to_string(surfIndex); + object.materialIndex = static_cast(materialMapper.GetDistinctPositionByInputPosition(surfIndex + baseSurfIndex)); + + writer.AddObject(std::move(object)); + } + } + + void AddObjVertices(ObjWriter& writer, const XModel* model, const unsigned lod) + { + const auto* surfs = &model->surfs[model->lodInfo[lod].surfIndex]; + const auto surfCount = model->lodInfo[lod].numsurfs; + + for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++) + { + const auto& surface = surfs[surfIndex]; + + for (auto vertexIndex = 0u; vertexIndex < surface.vertCount; vertexIndex++) + { + const auto& v = surface.verts0[vertexIndex]; + vec2_t uv{}; + vec3_t normalVec{}; + + Common::Vec2UnpackTexCoords(v.texCoord, &uv); + Common::Vec3UnpackUnitVec(v.normal, &normalVec); + + ObjVertex objVertex{}; + ObjNormal objNormal{}; + ObjUv objUv{}; + objVertex.coordinates[0] = v.xyz.x; + objVertex.coordinates[1] = v.xyz.z; + objVertex.coordinates[2] = -v.xyz.y; + objNormal.normal[0] = normalVec.x; + objNormal.normal[1] = normalVec.z; + objNormal.normal[2] = -normalVec.y; + objUv.uv[0] = uv.x; + objUv.uv[1] = 1.0f - uv.y; + + writer.AddVertex(static_cast(surfIndex), objVertex); + writer.AddNormal(static_cast(surfIndex), objNormal); + writer.AddUv(static_cast(surfIndex), objUv); + } + } + } + + void AddObjFaces(ObjWriter& writer, const XModel* model, const unsigned lod) + { + const auto* surfs = &model->surfs[model->lodInfo[lod].surfIndex]; + const auto surfCount = model->lodInfo[lod].numsurfs; + + for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++) + { + const auto& surface = surfs[surfIndex]; + for (auto triIndex = 0u; triIndex < surface.triCount; triIndex++) + { + const auto& tri = surface.triIndices[triIndex]; + + ObjFace face{}; + face.vertexIndex[0] = tri[2] + surface.baseVertIndex; + face.vertexIndex[1] = tri[1] + surface.baseVertIndex; + face.vertexIndex[2] = tri[0] + surface.baseVertIndex; + face.normalIndex[0] = face.vertexIndex[0]; + face.normalIndex[1] = face.vertexIndex[1]; + face.normalIndex[2] = face.vertexIndex[2]; + face.uvIndex[0] = face.vertexIndex[0]; + face.uvIndex[1] = face.vertexIndex[1]; + face.uvIndex[2] = face.vertexIndex[2]; + writer.AddFace(static_cast(surfIndex), face); + } + } + } + + void DumpObjMat(const AssetDumpingContext& context, const XAssetInfo* asset) + { + const auto* model = asset->Asset(); + const auto matFile = context.OpenAssetFile(std::format("model_export/{}.mtl", model->name)); + + if (!matFile) + return; + + ObjWriter writer(context.m_zone->m_game->GetShortName(), context.m_zone->m_name); + DistinctMapper materialMapper(model->numsurfs); + + AddObjMaterials(writer, materialMapper, model); + writer.WriteMtl(*matFile); + } + + void DumpObjLod(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; + + ObjWriter writer(context.m_zone->m_game->GetShortName(), context.m_zone->m_name); + DistinctMapper materialMapper(model->numsurfs); + + AddObjMaterials(writer, materialMapper, model); + AddObjObjects(writer, materialMapper, model, lod); + AddObjVertices(writer, model, lod); + AddObjFaces(writer, model, lod); + + writer.WriteObj(*assetFile, std::format("{}.mtl", model->name)); + } + + void AddXModelBones(const AssetDumpingContext& context, AbstractXModelWriter& writer, const XModel* model) + { + for (auto boneNum = 0u; boneNum < model->numBones; boneNum++) + { + XModelBone bone; + if (model->boneNames[boneNum] < context.m_zone->m_script_strings.Count()) + bone.name = context.m_zone->m_script_strings[model->boneNames[boneNum]]; + else + bone.name = "INVALID_BONE_NAME"; + + if (boneNum < model->numRootBones) + bone.parentIndex = -1; + else + bone.parentIndex = static_cast(boneNum - static_cast(model->parentList[boneNum - model->numRootBones])); + + bone.scale[0] = 1.0f; + bone.scale[1] = 1.0f; + bone.scale[2] = 1.0f; + + bone.globalOffset[0] = model->baseMat[boneNum].trans.x; + bone.globalOffset[1] = model->baseMat[boneNum].trans.y; + bone.globalOffset[2] = model->baseMat[boneNum].trans.z; + bone.globalRotation = + Quaternion32(model->baseMat[boneNum].quat.x, model->baseMat[boneNum].quat.y, model->baseMat[boneNum].quat.z, model->baseMat[boneNum].quat.w); + + if (boneNum < model->numRootBones) + { + bone.localOffset[0] = 0; + bone.localOffset[1] = 0; + bone.localOffset[2] = 0; + bone.localRotation = Quaternion32(0, 0, 0, 1); + } + else + { + bone.localOffset[0] = model->trans[boneNum - model->numRootBones][0]; + bone.localOffset[1] = model->trans[boneNum - model->numRootBones][1]; + bone.localOffset[2] = model->trans[boneNum - model->numRootBones][2]; + bone.localRotation = Quaternion32(QuatInt16::ToFloat(model->quats[boneNum - model->numRootBones][0]), + QuatInt16::ToFloat(model->quats[boneNum - model->numRootBones][1]), + QuatInt16::ToFloat(model->quats[boneNum - model->numRootBones][2]), + QuatInt16::ToFloat(model->quats[boneNum - model->numRootBones][3])); + } + + writer.AddBone(std::move(bone)); + } + } + + void AddXModelMaterials(AbstractXModelWriter& writer, DistinctMapper& materialMapper, const XModel* model) + { + for (auto surfaceMaterialNum = 0; surfaceMaterialNum < model->numsurfs; surfaceMaterialNum++) + { + Material* material = model->materialHandles[surfaceMaterialNum]; + if (materialMapper.Add(material)) + { + XModelMaterial xMaterial; + xMaterial.ApplyDefaults(); + + xMaterial.name = material->info.name; + const auto* colorMap = GetMaterialColorMap(material); + if (colorMap) + xMaterial.colorMapName = std::string(colorMap->name); + + writer.AddMaterial(std::move(xMaterial)); + } + } + } + + void AddXModelObjects(AbstractXModelWriter& writer, const XModel* model, const unsigned lod) + { + const auto surfCount = model->lodInfo[lod].numsurfs; + + for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++) + { + XModelObject object; + object.name = "surf" + std::to_string(surfIndex); + + writer.AddObject(std::move(object)); + } + } + + void AddXModelVertices(AbstractXModelWriter& writer, const XModel* model, const unsigned lod) + { + const auto* surfs = &model->surfs[model->lodInfo[lod].surfIndex]; + const auto surfCount = model->lodInfo[lod].numsurfs; + + if (!surfs) + return; + + for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++) + { + const auto& surface = surfs[surfIndex]; + + for (auto vertexIndex = 0u; vertexIndex < surface.vertCount; vertexIndex++) + { + const auto& v = surface.verts0[vertexIndex]; + vec2_t uv{}; + vec3_t normalVec{}; + vec4_t color{}; + + Common::Vec2UnpackTexCoords(v.texCoord, &uv); + Common::Vec3UnpackUnitVec(v.normal, &normalVec); + Common::Vec4UnpackGfxColor(v.color, &color); + + XModelVertex vertex{}; + vertex.coordinates[0] = v.xyz.x; + vertex.coordinates[1] = v.xyz.y; + vertex.coordinates[2] = v.xyz.z; + vertex.normal[0] = normalVec.x; + vertex.normal[1] = normalVec.y; + vertex.normal[2] = normalVec.z; + vertex.color[0] = color.x; + vertex.color[1] = color.y; + vertex.color[2] = color.z; + vertex.color[3] = color.w; + vertex.uv[0] = uv.x; + vertex.uv[1] = uv.y; + + writer.AddVertex(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; + + if (!surfs) + return; + + weightCollection.totalWeightCount = 0u; + for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++) + { + const auto& surface = surfs[surfIndex]; + + if (surface.vertList) + { + weightCollection.totalWeightCount += surface.vertListCount; + } + + if (surface.vertInfo.vertsBlend) + { + weightCollection.totalWeightCount += surface.vertInfo.vertCount[0] * 1; + weightCollection.totalWeightCount += surface.vertInfo.vertCount[1] * 2; + weightCollection.totalWeightCount += surface.vertInfo.vertCount[2] * 3; + weightCollection.totalWeightCount += surface.vertInfo.vertCount[3] * 4; + } + } + + weightCollection.weights = std::make_unique(weightCollection.totalWeightCount); + } + + void AddXModelVertexBoneWeights(AbstractXModelWriter& writer, const XModel* model, const unsigned lod, XModelVertexBoneWeightCollection& weightCollection) + { + const auto* surfs = &model->surfs[model->lodInfo[lod].surfIndex]; + const auto surfCount = model->lodInfo[lod].numsurfs; + + if (!surfs) + return; + + size_t weightOffset = 0u; + + for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++) + { + const auto& surface = surfs[surfIndex]; + auto handledVertices = 0u; + + if (surface.vertList) + { + for (auto vertListIndex = 0u; vertListIndex < surface.vertListCount; vertListIndex++) + { + const auto& vertList = surface.vertList[vertListIndex]; + const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; + + weightCollection.weights[weightOffset++] = XModelBoneWeight{static_cast(vertList.boneOffset / sizeof(DObjSkelMat)), 1.0f}; + + for (auto vertListVertexOffset = 0u; vertListVertexOffset < vertList.vertCount; vertListVertexOffset++) + { + writer.AddVertexBoneWeights(XModelVertexBoneWeights{boneWeightOffset, 1}); + } + handledVertices += vertList.vertCount; + } + } + + auto vertsBlendOffset = 0u; + if (surface.vertInfo.vertsBlend) + { + // 1 bone weight + for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[0]; vertIndex++) + { + const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; + const auto boneIndex0 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat)); + weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex0, 1.0f}; + + vertsBlendOffset += 1; + + writer.AddVertexBoneWeights(XModelVertexBoneWeights{boneWeightOffset, 1}); + } + + // 2 bone weights + for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[1]; vertIndex++) + { + const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; + const auto boneIndex0 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat)); + const auto boneIndex1 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat)); + const auto boneWeight1 = HalfFloat::ToFloat(surface.vertInfo.vertsBlend[vertsBlendOffset + 2]); + const auto boneWeight0 = 1.0f - boneWeight1; + + weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex0, boneWeight0}; + weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex1, boneWeight1}; + + vertsBlendOffset += 3; + + writer.AddVertexBoneWeights(XModelVertexBoneWeights{boneWeightOffset, 2}); + } + + // 3 bone weights + for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[2]; vertIndex++) + { + const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; + const auto boneIndex0 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat)); + const auto boneIndex1 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat)); + const auto boneWeight1 = HalfFloat::ToFloat(surface.vertInfo.vertsBlend[vertsBlendOffset + 2]); + const auto boneIndex2 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 3] / sizeof(DObjSkelMat)); + const auto boneWeight2 = HalfFloat::ToFloat(surface.vertInfo.vertsBlend[vertsBlendOffset + 4]); + const auto boneWeight0 = 1.0f - boneWeight1 - boneWeight2; + + weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex0, boneWeight0}; + weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex1, boneWeight1}; + weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex2, boneWeight2}; + + vertsBlendOffset += 5; + + writer.AddVertexBoneWeights(XModelVertexBoneWeights{boneWeightOffset, 3}); + } + + // 4 bone weights + for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[3]; vertIndex++) + { + const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; + const auto boneIndex0 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat)); + const auto boneIndex1 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat)); + const auto boneWeight1 = HalfFloat::ToFloat(surface.vertInfo.vertsBlend[vertsBlendOffset + 2]); + const auto boneIndex2 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 3] / sizeof(DObjSkelMat)); + const auto boneWeight2 = HalfFloat::ToFloat(surface.vertInfo.vertsBlend[vertsBlendOffset + 4]); + const auto boneIndex3 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 5] / sizeof(DObjSkelMat)); + const auto boneWeight3 = HalfFloat::ToFloat(surface.vertInfo.vertsBlend[vertsBlendOffset + 6]); + const auto boneWeight0 = 1.0f - boneWeight1 - boneWeight2 - boneWeight3; + + weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex0, boneWeight0}; + weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex1, boneWeight1}; + weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex2, boneWeight2}; + weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex3, boneWeight3}; + + vertsBlendOffset += 7; + + writer.AddVertexBoneWeights(XModelVertexBoneWeights{boneWeightOffset, 4}); + } + + handledVertices += + surface.vertInfo.vertCount[0] + surface.vertInfo.vertCount[1] + surface.vertInfo.vertCount[2] + surface.vertInfo.vertCount[3]; + } + + for (; handledVertices < surface.vertCount; handledVertices++) + { + writer.AddVertexBoneWeights(XModelVertexBoneWeights{nullptr, 0}); + } + } + } + + void AddXModelFaces(AbstractXModelWriter& writer, const DistinctMapper& materialMapper, const XModel* model, const unsigned lod) + { + const auto* surfs = &model->surfs[model->lodInfo[lod].surfIndex]; + const auto surfCount = model->lodInfo[lod].numsurfs; + const auto baseSurfIndex = model->lodInfo[lod].surfIndex; + + if (!surfs) + return; + + for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++) + { + const auto& surface = surfs[surfIndex]; + for (auto triIndex = 0u; triIndex < surface.triCount; triIndex++) + { + const auto& tri = surface.triIndices[triIndex]; + + XModelFace face{}; + face.vertexIndex[0] = tri[0] + surface.baseVertIndex; + face.vertexIndex[1] = tri[1] + surface.baseVertIndex; + face.vertexIndex[2] = tri[2] + surface.baseVertIndex; + face.objectIndex = static_cast(surfIndex); + face.materialIndex = static_cast(materialMapper.GetDistinctPositionByInputPosition(surfIndex + baseSurfIndex)); + writer.AddFace(face); + } + } + } + + void PopulateXModelWriter(const AssetDumpingContext& context, const unsigned lod, const XModel* model, AbstractXModelWriter& writer) + { + DistinctMapper materialMapper(model->numsurfs); + XModelVertexBoneWeightCollection boneWeightCollection; + AllocateXModelBoneWeights(model, lod, boneWeightCollection); + + AddXModelBones(context, writer, model); + AddXModelMaterials(writer, materialMapper, model); + AddXModelObjects(writer, model, lod); + AddXModelVertices(writer, model, lod); + AddXModelVertexBoneWeights(writer, model, lod, boneWeightCollection); + AddXModelFaces(writer, materialMapper, model, lod); + } + + void DumpXModelExportLod(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 = XModelExportWriter::CreateWriterForVersion6(context.m_zone->m_game->GetShortName(), context.m_zone->m_name); + PopulateXModelWriter(context, lod, model, *writer); + + writer->Write(*assetFile); + } + + template void DumpGltfLod(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); + PopulateXModelWriter(context, lod, model, *writer); + + writer->Write(*assetFile); + } + + void DumpXModelSurfs(const AssetDumpingContext& context, const XAssetInfo* asset) + { + const auto* model = asset->Asset(); + + if (ObjWriting::Configuration.ModelOutputFormat == ObjWriting::Configuration_t::ModelOutputFormat_e::OBJ) + DumpObjMat(context, asset); + + for (auto currentLod = 0u; currentLod < model->numLods; currentLod++) + { + switch (ObjWriting::Configuration.ModelOutputFormat) + { + case ObjWriting::Configuration_t::ModelOutputFormat_e::OBJ: + DumpObjLod(context, asset, currentLod); + break; + + case ObjWriting::Configuration_t::ModelOutputFormat_e::XMODEL_EXPORT: + DumpXModelExportLod(context, asset, currentLod); + break; + + case ObjWriting::Configuration_t::ModelOutputFormat_e::GLTF: + DumpGltfLod(context, asset, currentLod, ".gltf"); + break; + + case ObjWriting::Configuration_t::ModelOutputFormat_e::GLB: + DumpGltfLod(context, asset, currentLod, ".glb"); + break; + + default: + assert(false); + break; + } + } + } +} // namespace + bool AssetDumperXModel::ShouldDump(XAssetInfo* asset) { return !asset->m_name.empty() && asset->m_name[0] != ','; } -GfxImage* AssetDumperXModel::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 || def->semantic >= TS_COLOR0_MAP && def->semantic <= TS_COLOR15_MAP) - potentialTextureDefs.push_back(def); - } - - if (potentialTextureDefs.empty()) - return nullptr; - if (potentialTextureDefs.size() == 1) - return potentialTextureDefs[0]->image; - - for (const auto* def : potentialTextureDefs) - { - if (tolower(def->nameStart) == 'c' && tolower(def->nameEnd) == 'p') - return def->image; - } - - for (const auto* def : potentialTextureDefs) - { - if (tolower(def->nameStart) == 'r' && tolower(def->nameEnd) == 'k') - return def->image; - } - - for (const auto* def : potentialTextureDefs) - { - if (tolower(def->nameStart) == 'd' && tolower(def->nameEnd) == 'p') - return def->image; - } - - return potentialTextureDefs[0]->image; -} - -GfxImage* AssetDumperXModel::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]->image; - - for (const auto* def : potentialTextureDefs) - { - if (def->nameStart == 'n' && def->nameEnd == 'p') - return def->image; - } - - return potentialTextureDefs[0]->image; -} - -GfxImage* AssetDumperXModel::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]->image; - - for (const auto* def : potentialTextureDefs) - { - if (def->nameStart == 's' && def->nameEnd == 'p') - return def->image; - } - - return potentialTextureDefs[0]->image; -} - -void AssetDumperXModel::AddObjMaterials(ObjWriter& writer, DistinctMapper& materialMapper, const XModel* model) -{ - if (!model->materialHandles) - return; - - for (auto surfIndex = 0u; surfIndex < model->numsurfs; surfIndex++) - { - Material* material = model->materialHandles[surfIndex]; - if (!materialMapper.Add(material)) - continue; - - MtlMaterial mtl; - mtl.materialName = std::string(material->info.name); - - GfxImage* colorMap = GetMaterialColorMap(material); - GfxImage* normalMap = GetMaterialNormalMap(material); - GfxImage* specularMap = GetMaterialSpecularMap(material); - - if (colorMap != nullptr) - mtl.colorMapName = colorMap->name; - if (normalMap != nullptr) - mtl.normalMapName = normalMap->name; - if (specularMap != nullptr) - mtl.specularMapName = specularMap->name; - - writer.AddMaterial(std::move(mtl)); - } -} - -void AssetDumperXModel::AddObjObjects(ObjWriter& writer, const DistinctMapper& materialMapper, const XModel* model, const unsigned lod) -{ - const auto surfCount = model->lodInfo[lod].numsurfs; - const auto baseSurfIndex = model->lodInfo[lod].surfIndex; - - for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++) - { - ObjObject object; - object.name = "surf" + std::to_string(surfIndex); - object.materialIndex = static_cast(materialMapper.GetDistinctPositionByInputPosition(surfIndex + baseSurfIndex)); - - writer.AddObject(std::move(object)); - } -} - -void AssetDumperXModel::AddObjVertices(ObjWriter& writer, const XModel* model, const unsigned lod) -{ - const auto* surfs = &model->surfs[model->lodInfo[lod].surfIndex]; - const auto surfCount = model->lodInfo[lod].numsurfs; - - for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++) - { - const auto& surface = surfs[surfIndex]; - - for (auto vertexIndex = 0u; vertexIndex < surface.vertCount; vertexIndex++) - { - const auto& v = surface.verts0[vertexIndex]; - vec2_t uv{}; - vec3_t normalVec{}; - - Common::Vec2UnpackTexCoords(v.texCoord, &uv); - Common::Vec3UnpackUnitVec(v.normal, &normalVec); - - ObjVertex objVertex{}; - ObjNormal objNormal{}; - ObjUv objUv{}; - objVertex.coordinates[0] = v.xyz.x; - objVertex.coordinates[1] = v.xyz.z; - objVertex.coordinates[2] = -v.xyz.y; - objNormal.normal[0] = normalVec.x; - objNormal.normal[1] = normalVec.z; - objNormal.normal[2] = -normalVec.y; - objUv.uv[0] = uv.x; - objUv.uv[1] = 1.0f - uv.y; - - writer.AddVertex(static_cast(surfIndex), objVertex); - writer.AddNormal(static_cast(surfIndex), objNormal); - writer.AddUv(static_cast(surfIndex), objUv); - } - } -} - -void AssetDumperXModel::AddObjFaces(ObjWriter& writer, const XModel* model, const unsigned lod) -{ - const auto* surfs = &model->surfs[model->lodInfo[lod].surfIndex]; - const auto surfCount = model->lodInfo[lod].numsurfs; - - for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++) - { - const auto& surface = surfs[surfIndex]; - for (auto triIndex = 0u; triIndex < surface.triCount; triIndex++) - { - const auto& tri = surface.triIndices[triIndex]; - - ObjFace face{}; - face.vertexIndex[0] = tri[2] + surface.baseVertIndex; - face.vertexIndex[1] = tri[1] + surface.baseVertIndex; - face.vertexIndex[2] = tri[0] + surface.baseVertIndex; - face.normalIndex[0] = face.vertexIndex[0]; - face.normalIndex[1] = face.vertexIndex[1]; - face.normalIndex[2] = face.vertexIndex[2]; - face.uvIndex[0] = face.vertexIndex[0]; - face.uvIndex[1] = face.vertexIndex[1]; - face.uvIndex[2] = face.vertexIndex[2]; - writer.AddFace(static_cast(surfIndex), face); - } - } -} - -void AssetDumperXModel::DumpObjMat(const AssetDumpingContext& context, XAssetInfo* asset) -{ - const auto* model = asset->Asset(); - const auto matFile = context.OpenAssetFile("model_export/" + std::string(model->name) + ".mtl"); - - if (!matFile) - return; - - ObjWriter writer(context.m_zone->m_game->GetShortName(), context.m_zone->m_name); - DistinctMapper materialMapper(model->numsurfs); - - AddObjMaterials(writer, materialMapper, model); - writer.WriteMtl(*matFile); -} - -void AssetDumperXModel::DumpObjLod(AssetDumpingContext& context, XAssetInfo* asset, const unsigned lod) -{ - const auto* model = asset->Asset(); - std::ostringstream ss; - ss << "model_export/" << model->name << "_lod" << lod << ".OBJ"; - - const auto assetFile = context.OpenAssetFile(ss.str()); - - if (!assetFile) - return; - - ObjWriter writer(context.m_zone->m_game->GetShortName(), context.m_zone->m_name); - DistinctMapper materialMapper(model->numsurfs); - - AddObjMaterials(writer, materialMapper, model); - AddObjObjects(writer, materialMapper, model, lod); - AddObjVertices(writer, model, lod); - AddObjFaces(writer, model, lod); - - writer.WriteObj(*assetFile, std::string(model->name) + ".mtl"); -} - -void AssetDumperXModel::DumpObj(AssetDumpingContext& context, XAssetInfo* asset) -{ - const auto* model = asset->Asset(); - - DumpObjMat(context, asset); - for (auto currentLod = 0u; currentLod < model->numLods; currentLod++) - { - DumpObjLod(context, asset, currentLod); - } -} - -void AssetDumperXModel::AddXModelBones(const AssetDumpingContext& context, AbstractXModelWriter& writer, const XModel* model) -{ - for (auto boneNum = 0u; boneNum < model->numBones; boneNum++) - { - XModelBone bone; - if (model->boneNames[boneNum] < context.m_zone->m_script_strings.Count()) - bone.name = context.m_zone->m_script_strings[model->boneNames[boneNum]]; - else - bone.name = "INVALID_BONE_NAME"; - - if (boneNum < model->numRootBones) - bone.parentIndex = -1; - else - bone.parentIndex = static_cast(boneNum - static_cast(model->parentList[boneNum - model->numRootBones])); - - bone.scale[0] = 1.0f; - bone.scale[1] = 1.0f; - bone.scale[2] = 1.0f; - - bone.globalOffset[0] = model->baseMat[boneNum].trans.x; - bone.globalOffset[1] = model->baseMat[boneNum].trans.y; - bone.globalOffset[2] = model->baseMat[boneNum].trans.z; - bone.globalRotation = - Quaternion32(model->baseMat[boneNum].quat.x, model->baseMat[boneNum].quat.y, model->baseMat[boneNum].quat.z, model->baseMat[boneNum].quat.w); - - if (boneNum < model->numRootBones) - { - bone.localOffset[0] = 0; - bone.localOffset[1] = 0; - bone.localOffset[2] = 0; - bone.localRotation = Quaternion32(0, 0, 0, 1); - } - else - { - bone.localOffset[0] = model->trans[boneNum - model->numRootBones][0]; - bone.localOffset[1] = model->trans[boneNum - model->numRootBones][1]; - bone.localOffset[2] = model->trans[boneNum - model->numRootBones][2]; - bone.localRotation = Quaternion32(QuatInt16::ToFloat(model->quats[boneNum - model->numRootBones][0]), - QuatInt16::ToFloat(model->quats[boneNum - model->numRootBones][1]), - QuatInt16::ToFloat(model->quats[boneNum - model->numRootBones][2]), - QuatInt16::ToFloat(model->quats[boneNum - model->numRootBones][3])); - } - - writer.AddBone(std::move(bone)); - } -} - -void AssetDumperXModel::AddXModelMaterials(AbstractXModelWriter& writer, DistinctMapper& materialMapper, const XModel* model) -{ - for (auto surfaceMaterialNum = 0; surfaceMaterialNum < model->numsurfs; surfaceMaterialNum++) - { - Material* material = model->materialHandles[surfaceMaterialNum]; - if (materialMapper.Add(material)) - { - XModelMaterial xMaterial; - xMaterial.ApplyDefaults(); - - xMaterial.name = material->info.name; - const auto* colorMap = GetMaterialColorMap(material); - if (colorMap) - xMaterial.colorMapName = std::string(colorMap->name); - - writer.AddMaterial(std::move(xMaterial)); - } - } -} - -void AssetDumperXModel::AddXModelObjects(AbstractXModelWriter& writer, const XModel* model, const unsigned lod) -{ - const auto surfCount = model->lodInfo[lod].numsurfs; - - for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++) - { - XModelObject object; - object.name = "surf" + std::to_string(surfIndex); - - writer.AddObject(std::move(object)); - } -} - -void AssetDumperXModel::AddXModelVertices(AbstractXModelWriter& writer, const XModel* model, const unsigned lod) -{ - const auto* surfs = &model->surfs[model->lodInfo[lod].surfIndex]; - const auto surfCount = model->lodInfo[lod].numsurfs; - - if (!surfs) - return; - - for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++) - { - const auto& surface = surfs[surfIndex]; - - for (auto vertexIndex = 0u; vertexIndex < surface.vertCount; vertexIndex++) - { - const auto& v = surface.verts0[vertexIndex]; - vec2_t uv{}; - vec3_t normalVec{}; - vec4_t color{}; - - Common::Vec2UnpackTexCoords(v.texCoord, &uv); - Common::Vec3UnpackUnitVec(v.normal, &normalVec); - Common::Vec4UnpackGfxColor(v.color, &color); - - XModelVertex vertex{}; - vertex.coordinates[0] = v.xyz.x; - vertex.coordinates[1] = v.xyz.y; - vertex.coordinates[2] = v.xyz.z; - vertex.normal[0] = normalVec.x; - vertex.normal[1] = normalVec.y; - vertex.normal[2] = normalVec.z; - vertex.color[0] = color.x; - vertex.color[1] = color.y; - vertex.color[2] = color.z; - vertex.color[3] = color.w; - vertex.uv[0] = uv.x; - vertex.uv[1] = uv.y; - - writer.AddVertex(vertex); - } - } -} - -void AssetDumperXModel::AllocateXModelBoneWeights(const XModel* model, const unsigned lod, XModelVertexBoneWeightCollection& weightCollection) -{ - const auto* surfs = &model->surfs[model->lodInfo[lod].surfIndex]; - const auto surfCount = model->lodInfo[lod].numsurfs; - - if (!surfs) - return; - - weightCollection.totalWeightCount = 0u; - for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++) - { - const auto& surface = surfs[surfIndex]; - - if (surface.vertList) - { - weightCollection.totalWeightCount += surface.vertListCount; - } - - if (surface.vertInfo.vertsBlend) - { - weightCollection.totalWeightCount += surface.vertInfo.vertCount[0] * 1; - weightCollection.totalWeightCount += surface.vertInfo.vertCount[1] * 2; - weightCollection.totalWeightCount += surface.vertInfo.vertCount[2] * 3; - weightCollection.totalWeightCount += surface.vertInfo.vertCount[3] * 4; - } - } - - weightCollection.weights = std::make_unique(weightCollection.totalWeightCount); -} - -void AssetDumperXModel::AddXModelVertexBoneWeights(AbstractXModelWriter& writer, - const XModel* model, - const unsigned lod, - XModelVertexBoneWeightCollection& weightCollection) -{ - const auto* surfs = &model->surfs[model->lodInfo[lod].surfIndex]; - const auto surfCount = model->lodInfo[lod].numsurfs; - - if (!surfs) - return; - - size_t weightOffset = 0u; - - for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++) - { - const auto& surface = surfs[surfIndex]; - auto handledVertices = 0u; - - if (surface.vertList) - { - for (auto vertListIndex = 0u; vertListIndex < surface.vertListCount; vertListIndex++) - { - const auto& vertList = surface.vertList[vertListIndex]; - const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; - - weightCollection.weights[weightOffset++] = XModelBoneWeight{static_cast(vertList.boneOffset / sizeof(DObjSkelMat)), 1.0f}; - - for (auto vertListVertexOffset = 0u; vertListVertexOffset < vertList.vertCount; vertListVertexOffset++) - { - writer.AddVertexBoneWeights(XModelVertexBoneWeights{boneWeightOffset, 1}); - } - handledVertices += vertList.vertCount; - } - } - - auto vertsBlendOffset = 0u; - if (surface.vertInfo.vertsBlend) - { - // 1 bone weight - for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[0]; vertIndex++) - { - const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; - const auto boneIndex0 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat)); - weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex0, 1.0f}; - - vertsBlendOffset += 1; - - writer.AddVertexBoneWeights(XModelVertexBoneWeights{boneWeightOffset, 1}); - } - - // 2 bone weights - for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[1]; vertIndex++) - { - const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; - const auto boneIndex0 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat)); - const auto boneIndex1 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat)); - const auto boneWeight1 = HalfFloat::ToFloat(surface.vertInfo.vertsBlend[vertsBlendOffset + 2]); - const auto boneWeight0 = 1.0f - boneWeight1; - - weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex0, boneWeight0}; - weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex1, boneWeight1}; - - vertsBlendOffset += 3; - - writer.AddVertexBoneWeights(XModelVertexBoneWeights{boneWeightOffset, 2}); - } - - // 3 bone weights - for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[2]; vertIndex++) - { - const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; - const auto boneIndex0 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat)); - const auto boneIndex1 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat)); - const auto boneWeight1 = HalfFloat::ToFloat(surface.vertInfo.vertsBlend[vertsBlendOffset + 2]); - const auto boneIndex2 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 3] / sizeof(DObjSkelMat)); - const auto boneWeight2 = HalfFloat::ToFloat(surface.vertInfo.vertsBlend[vertsBlendOffset + 4]); - const auto boneWeight0 = 1.0f - boneWeight1 - boneWeight2; - - weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex0, boneWeight0}; - weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex1, boneWeight1}; - weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex2, boneWeight2}; - - vertsBlendOffset += 5; - - writer.AddVertexBoneWeights(XModelVertexBoneWeights{boneWeightOffset, 3}); - } - - // 4 bone weights - for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[3]; vertIndex++) - { - const auto* boneWeightOffset = &weightCollection.weights[weightOffset]; - const auto boneIndex0 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat)); - const auto boneIndex1 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat)); - const auto boneWeight1 = HalfFloat::ToFloat(surface.vertInfo.vertsBlend[vertsBlendOffset + 2]); - const auto boneIndex2 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 3] / sizeof(DObjSkelMat)); - const auto boneWeight2 = HalfFloat::ToFloat(surface.vertInfo.vertsBlend[vertsBlendOffset + 4]); - const auto boneIndex3 = static_cast(surface.vertInfo.vertsBlend[vertsBlendOffset + 5] / sizeof(DObjSkelMat)); - const auto boneWeight3 = HalfFloat::ToFloat(surface.vertInfo.vertsBlend[vertsBlendOffset + 6]); - const auto boneWeight0 = 1.0f - boneWeight1 - boneWeight2 - boneWeight3; - - weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex0, boneWeight0}; - weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex1, boneWeight1}; - weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex2, boneWeight2}; - weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex3, boneWeight3}; - - vertsBlendOffset += 7; - - writer.AddVertexBoneWeights(XModelVertexBoneWeights{boneWeightOffset, 4}); - } - - handledVertices += surface.vertInfo.vertCount[0] + surface.vertInfo.vertCount[1] + surface.vertInfo.vertCount[2] + surface.vertInfo.vertCount[3]; - } - - for (; handledVertices < surface.vertCount; handledVertices++) - { - writer.AddVertexBoneWeights(XModelVertexBoneWeights{nullptr, 0}); - } - } -} - -void AssetDumperXModel::AddXModelFaces(AbstractXModelWriter& writer, const DistinctMapper& materialMapper, const XModel* model, const unsigned lod) -{ - const auto* surfs = &model->surfs[model->lodInfo[lod].surfIndex]; - const auto surfCount = model->lodInfo[lod].numsurfs; - const auto baseSurfIndex = model->lodInfo[lod].surfIndex; - - if (!surfs) - return; - - for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++) - { - const auto& surface = surfs[surfIndex]; - for (auto triIndex = 0u; triIndex < surface.triCount; triIndex++) - { - const auto& tri = surface.triIndices[triIndex]; - - XModelFace face{}; - face.vertexIndex[0] = tri[0] + surface.baseVertIndex; - face.vertexIndex[1] = tri[1] + surface.baseVertIndex; - face.vertexIndex[2] = tri[2] + surface.baseVertIndex; - face.objectIndex = static_cast(surfIndex); - face.materialIndex = static_cast(materialMapper.GetDistinctPositionByInputPosition(surfIndex + baseSurfIndex)); - writer.AddFace(face); - } - } -} - -void AssetDumperXModel::DumpXModelExportLod(const AssetDumpingContext& context, XAssetInfo* asset, const unsigned lod) -{ - const auto* model = asset->Asset(); - - std::ostringstream ss; - ss << "model_export/" << model->name << "_lod" << lod << ".XMODEL_EXPORT"; - - const auto assetFile = context.OpenAssetFile(ss.str()); - - if (!assetFile) - return; - - const auto writer = XModelExportWriter::CreateWriterForVersion6(context.m_zone->m_game->GetShortName(), context.m_zone->m_name); - DistinctMapper materialMapper(model->numsurfs); - XModelVertexBoneWeightCollection boneWeightCollection; - AllocateXModelBoneWeights(model, lod, boneWeightCollection); - - AddXModelBones(context, *writer, model); - AddXModelMaterials(*writer, materialMapper, model); - AddXModelObjects(*writer, model, lod); - AddXModelVertices(*writer, model, lod); - AddXModelVertexBoneWeights(*writer, model, lod, boneWeightCollection); - AddXModelFaces(*writer, materialMapper, model, lod); - - writer->Write(*assetFile); -} - -void AssetDumperXModel::DumpXModelExport(const AssetDumpingContext& context, XAssetInfo* asset) -{ - const auto* model = asset->Asset(); - for (auto currentLod = 0u; currentLod < model->numLods; currentLod++) - { - DumpXModelExportLod(context, asset, currentLod); - } -} - void AssetDumperXModel::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) { - switch (ObjWriting::Configuration.ModelOutputFormat) - { - case ObjWriting::Configuration_t::ModelOutputFormat_e::OBJ: - DumpObj(context, asset); - break; - - case ObjWriting::Configuration_t::ModelOutputFormat_e::XMODEL_EXPORT: - DumpXModelExport(context, asset); - break; - - default: - assert(false); - break; - } + DumpXModelSurfs(context, asset); } diff --git a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperXModel.h b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperXModel.h index fd56c376..75218729 100644 --- a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperXModel.h +++ b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperXModel.h @@ -2,37 +2,11 @@ #include "Dumping/AbstractAssetDumper.h" #include "Game/T6/T6.h" -#include "Utils/DistinctMapper.h" -#include "XModel/AbstractXModelWriter.h" -#include "XModel/Obj/ObjWriter.h" namespace T6 { class AssetDumperXModel final : public AbstractAssetDumper { - static GfxImage* GetMaterialColorMap(const Material* material); - static GfxImage* GetMaterialNormalMap(const Material* material); - static GfxImage* GetMaterialSpecularMap(const Material* material); - - static void AddObjMaterials(ObjWriter& writer, DistinctMapper& materialMapper, const XModel* model); - static void AddObjObjects(ObjWriter& writer, const DistinctMapper& materialMapper, const XModel* model, unsigned lod); - static void AddObjVertices(ObjWriter& writer, const XModel* model, unsigned lod); - static void AddObjFaces(ObjWriter& writer, const XModel* model, unsigned lod); - static void DumpObjLod(AssetDumpingContext& context, XAssetInfo* asset, unsigned lod); - static void DumpObjMat(const AssetDumpingContext& context, XAssetInfo* asset); - static void DumpObj(AssetDumpingContext& context, XAssetInfo* asset); - - static void AddXModelBones(const AssetDumpingContext& context, AbstractXModelWriter& writer, const XModel* model); - static void AddXModelMaterials(AbstractXModelWriter& writer, DistinctMapper& materialMapper, const XModel* model); - static void AddXModelObjects(AbstractXModelWriter& writer, const XModel* model, unsigned lod); - static void AddXModelVertices(AbstractXModelWriter& writer, const XModel* model, unsigned lod); - static void AllocateXModelBoneWeights(const XModel* model, unsigned lod, XModelVertexBoneWeightCollection& weightCollection); - static void - AddXModelVertexBoneWeights(AbstractXModelWriter& writer, const XModel* model, unsigned lod, XModelVertexBoneWeightCollection& weightCollection); - static void AddXModelFaces(AbstractXModelWriter& writer, const DistinctMapper& materialMapper, const XModel* model, unsigned lod); - static void DumpXModelExportLod(const AssetDumpingContext& context, XAssetInfo* asset, unsigned lod); - static void DumpXModelExport(const AssetDumpingContext& context, XAssetInfo* asset); - protected: bool ShouldDump(XAssetInfo* asset) override; void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; diff --git a/src/ObjWriting/ObjWriting.h b/src/ObjWriting/ObjWriting.h index e69903e2..e8796b2b 100644 --- a/src/ObjWriting/ObjWriting.h +++ b/src/ObjWriting/ObjWriting.h @@ -20,14 +20,16 @@ public: enum class ModelOutputFormat_e { XMODEL_EXPORT, - OBJ + OBJ, + GLTF, + GLB }; bool Verbose = false; std::vector AssetTypesToHandleBitfield; ImageOutputFormat_e ImageOutputFormat = ImageOutputFormat_e::DDS; - ModelOutputFormat_e ModelOutputFormat = ModelOutputFormat_e::XMODEL_EXPORT; + ModelOutputFormat_e ModelOutputFormat = ModelOutputFormat_e::GLB; bool MenuLegacyMode = false; } Configuration; diff --git a/src/Unlinker/UnlinkerArgs.cpp b/src/Unlinker/UnlinkerArgs.cpp index 29276c66..43aac8f7 100644 --- a/src/Unlinker/UnlinkerArgs.cpp +++ b/src/Unlinker/UnlinkerArgs.cpp @@ -5,6 +5,7 @@ #include "ObjWriting.h" #include "Utils/Arguments/UsageInformation.h" #include "Utils/FileUtils.h" +#include "Utils/StringUtils.h" #include #include @@ -79,7 +80,7 @@ const CommandLineOption* const OPTION_IMAGE_FORMAT = const CommandLineOption* const OPTION_MODEL_FORMAT = CommandLineOption::Builder::Create() .WithLongName("model-format") - .WithDescription("Specifies the format of dumped model files. Valid values are: XMODEL_EXPORT, OBJ") + .WithDescription("Specifies the format of dumped model files. Valid values are: XMODEL_EXPORT, OBJ, GLTF, GLB") .WithParameter("modelFormatValue") .Build(); @@ -179,8 +180,7 @@ void UnlinkerArgs::SetVerbose(const bool isVerbose) bool UnlinkerArgs::SetImageDumpingMode() { auto specifiedValue = m_argument_parser.GetValueForOption(OPTION_IMAGE_FORMAT); - for (auto& c : specifiedValue) - c = static_cast(tolower(c)); + utils::MakeStringLowerCase(specifiedValue); if (specifiedValue == "dds") { @@ -202,8 +202,7 @@ bool UnlinkerArgs::SetImageDumpingMode() bool UnlinkerArgs::SetModelDumpingMode() { auto specifiedValue = m_argument_parser.GetValueForOption(OPTION_MODEL_FORMAT); - for (auto& c : specifiedValue) - c = static_cast(tolower(c)); + utils::MakeStringLowerCase(specifiedValue); if (specifiedValue == "xmodel_export") { @@ -217,6 +216,18 @@ bool UnlinkerArgs::SetModelDumpingMode() return true; } + if (specifiedValue == "gltf") + { + ObjWriting::Configuration.ModelOutputFormat = ObjWriting::Configuration_t::ModelOutputFormat_e::GLTF; + return true; + } + + if (specifiedValue == "glb") + { + ObjWriting::Configuration.ModelOutputFormat = ObjWriting::Configuration_t::ModelOutputFormat_e::GLB; + return true; + } + const std::string originalValue = m_argument_parser.GetValueForOption(OPTION_MODEL_FORMAT); printf("Illegal value: \"%s\" is not a valid model output format. Use -? to see usage information.\n", originalValue.c_str()); return false; @@ -238,8 +249,7 @@ void UnlinkerArgs::ParseCommaSeparatedAssetTypeString(const std::string& input) size_t endPos; std::string lowerInput(input); - for (auto& c : lowerInput) - c = static_cast(tolower(c)); + utils::MakeStringLowerCase(lowerInput); while (currentPos < lowerInput.size() && (endPos = lowerInput.find_first_of(',', currentPos)) != std::string::npos) { From 0d96213f218c1a9f6146eb3d443899252a884335 Mon Sep 17 00:00:00 2001 From: Jan Date: Mon, 1 Apr 2024 15:20:58 +0200 Subject: [PATCH 08/25] =?UTF-8?q?=EF=BB=BFchore:=20export=20gltf=20basic?= =?UTF-8?q?=20attributes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/ObjCommon/XModel/Gltf/JsonGltf.h | 46 ++++++--- src/ObjWriting/XModel/Gltf/GltfWriter.cpp | 114 +++++++++++++++++++++- 2 files changed, 142 insertions(+), 18 deletions(-) diff --git a/src/ObjCommon/XModel/Gltf/JsonGltf.h b/src/ObjCommon/XModel/Gltf/JsonGltf.h index 5922df88..16eb8104 100644 --- a/src/ObjCommon/XModel/Gltf/JsonGltf.h +++ b/src/ObjCommon/XModel/Gltf/JsonGltf.h @@ -1,6 +1,7 @@ #pragma once #include "Json/JsonExtension.h" +#include #include #include #include @@ -20,14 +21,16 @@ namespace gltf class JsonNode { public: - std::string name; - std::vector translation; - std::vector rotation; - std::vector scale; - std::vector children; + std::optional name; + std::optional> translation; + std::optional> rotation; + std::optional> scale; + std::optional> children; + std::optional skin; + std::optional mesh; }; - NLOHMANN_DEFINE_TYPE_EXTENSION(JsonNode, name, translation, rotation, scale, children); + NLOHMANN_DEFINE_TYPE_EXTENSION(JsonNode, name, translation, rotation, scale, children, skin, mesh); class JsonBuffer { @@ -166,7 +169,7 @@ namespace gltf unsigned output; }; - NLOHMANN_DEFINE_TYPE_EXTENSION(JsonAnimationSampler, input); + NLOHMANN_DEFINE_TYPE_EXTENSION(JsonAnimationSampler, input, interpolation, output); class JsonAnimation { @@ -238,19 +241,30 @@ namespace gltf NLOHMANN_DEFINE_TYPE_EXTENSION(JsonSkin, inverseBindMatrices, skeleton, joints); + class JsonScene + { + public: + std::vector nodes; + std::optional name; + }; + + NLOHMANN_DEFINE_TYPE_EXTENSION(JsonScene, nodes, name); + class JsonRoot { public: - std::vector accessors; - std::vector animations; + std::optional> accessors; + std::optional> animations; JsonAsset asset; - std::vector buffers; - std::vector bufferViews; - std::vector materials; - std::vector meshes; - std::vector nodes; - std::vector skins; + std::optional> buffers; + std::optional> bufferViews; + std::optional> materials; + std::optional> meshes; + std::optional> nodes; + std::optional> skins; + std::optional scene; + std::optional> scenes; }; - NLOHMANN_DEFINE_TYPE_EXTENSION(JsonRoot, asset); + NLOHMANN_DEFINE_TYPE_EXTENSION(JsonRoot, accessors, animations, asset, buffers, bufferViews, materials, meshes, nodes, skins, scene, scenes); } // namespace gltf diff --git a/src/ObjWriting/XModel/Gltf/GltfWriter.cpp b/src/ObjWriting/XModel/Gltf/GltfWriter.cpp index 3aa03f9b..01d37ac1 100644 --- a/src/ObjWriting/XModel/Gltf/GltfWriter.cpp +++ b/src/ObjWriting/XModel/Gltf/GltfWriter.cpp @@ -13,6 +13,9 @@ namespace class GltfWriterImpl final : public gltf::Writer { + static constexpr auto NODE_INDEX_MESH = 0u; + static constexpr auto NODE_FIRST_INDEX_BONES = 1u; + public: GltfWriterImpl(const Output* output, std::string gameName, std::string zoneName) : m_output(output), @@ -24,8 +27,12 @@ namespace void Write(std::ostream& stream) override { JsonRoot gltf; - gltf.asset.version = GLTF_VERSION_STRING; - gltf.asset.generator = GLTF_GENERATOR; + CreateJsonAsset(gltf.asset); + CreateMeshNode(gltf); + CreateMesh(gltf); + CreateSkeletonNodes(gltf); + CreateSkin(gltf); + CreateScene(gltf); const json jRoot = gltf; m_output->EmitJson(jRoot); @@ -33,6 +40,109 @@ namespace } private: + static void CreateJsonAsset(JsonAsset& asset) + { + asset.version = GLTF_VERSION_STRING; + asset.generator = GLTF_GENERATOR; + } + + void CreateMeshNode(JsonRoot& gltf) const + { + JsonNode meshNode; + + // We only have one mesh + meshNode.mesh = 0u; + + // Only add skin if the model has bones + if (!m_bones.empty()) + { + // We only have one skin + meshNode.skin = 0u; + } + + if (!gltf.nodes.has_value()) + gltf.nodes.emplace(); + + gltf.nodes->emplace_back(std::move(meshNode)); + } + + void CreateMesh(JsonRoot& gltf) const + { + if (!gltf.meshes.has_value()) + gltf.meshes.emplace(); + + JsonMesh mesh; + + gltf.meshes->emplace_back(std::move(mesh)); + } + + static unsigned CreateNodeIndexFromBoneIndex(const unsigned boneIndex) + { + return boneIndex + NODE_FIRST_INDEX_BONES; + } + + void CreateSkeletonNodes(JsonRoot& gltf) const + { + if (m_bones.empty()) + return; + + if (!gltf.nodes.has_value()) + gltf.nodes.emplace(); + + for (const auto [boneIndex, bone] : std::views::enumerate(m_bones)) + { + JsonNode boneNode; + + boneNode.name = bone.name; + boneNode.translation = std::to_array(bone.globalOffset); + boneNode.rotation = std::to_array({bone.globalRotation.m_x, bone.globalRotation.m_y, bone.globalRotation.m_z, bone.globalRotation.m_w}); + + const auto isParentOf = [this, boneIndex](const unsigned b) + { + return m_bones[b].parentIndex == boneIndex; + }; + auto children = std::ranges::iota_view(0u, m_bones.size()) | std::views::filter(isParentOf) + | std::views::transform(CreateNodeIndexFromBoneIndex) | std::ranges::to>(); + if (!children.empty()) + boneNode.children = std::move(children); + + gltf.nodes->emplace_back(std::move(boneNode)); + } + } + + void CreateSkin(JsonRoot& gltf) const + { + if (m_bones.empty()) + return; + + JsonSkin skin; + skin.joints = + std::ranges::iota_view(0u, m_bones.size()) | std::views::transform(CreateNodeIndexFromBoneIndex) | std::ranges::to>(); + + if (!gltf.skins.has_value()) + gltf.skins.emplace(); + gltf.skins->emplace_back(std::move(skin)); + } + + void CreateScene(JsonRoot& gltf) const + { + JsonScene scene; + + // Only add skin if the model has bones + if (m_bones.empty()) + scene.nodes.emplace_back(NODE_INDEX_MESH); + else + scene.nodes.emplace_back(m_skeleton_node); + + if (!gltf.scenes.has_value()) + gltf.scenes.emplace(); + + gltf.scenes->emplace_back(std::move(scene)); + gltf.scene = 0u; + } + + unsigned m_skeleton_node = 0u; + const Output* m_output; std::string m_game_name; std::string m_zone_name; From 1bc1c122440a122edd2d588c3468792097d43cd1 Mon Sep 17 00:00:00 2001 From: Jan Date: Mon, 1 Apr 2024 20:48:39 +0200 Subject: [PATCH 09/25] chore: fix linux build --- .../T6/AssetLoaders/AssetLoaderXModel.cpp | 2 +- .../Game/T6/XModel/JsonXModelLoader.cpp | 3 +- src/ObjWriting/XModel/Gltf/GltfTextOutput.cpp | 2 +- src/ObjWriting/XModel/Gltf/GltfWriter.cpp | 32 ++++++++++--------- 4 files changed, 21 insertions(+), 18 deletions(-) diff --git a/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderXModel.cpp b/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderXModel.cpp index 8cbbd072..2ee2dc36 100644 --- a/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderXModel.cpp +++ b/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderXModel.cpp @@ -1,7 +1,7 @@ #include "AssetLoaderXModel.h" -#include "Game/T6/XModel/JsonXModelLoader.h" #include "Game/T6/T6.h" +#include "Game/T6/XModel/JsonXModelLoader.h" #include "Pool/GlobalAssetPool.h" #include diff --git a/src/ObjLoading/Game/T6/XModel/JsonXModelLoader.cpp b/src/ObjLoading/Game/T6/XModel/JsonXModelLoader.cpp index 6dc2268a..692a340f 100644 --- a/src/ObjLoading/Game/T6/XModel/JsonXModelLoader.cpp +++ b/src/ObjLoading/Game/T6/XModel/JsonXModelLoader.cpp @@ -6,6 +6,7 @@ #include #include #include +#include using namespace nlohmann; using namespace T6; @@ -109,7 +110,7 @@ namespace T6 std::set dependenciesSet; const JsonLoader loader(stream, *memory, *manager, dependenciesSet); - dependencies.assign_range(dependenciesSet); + dependencies.assign(dependenciesSet.cbegin(), dependenciesSet.cend()); return loader.Load(xmodel); } diff --git a/src/ObjWriting/XModel/Gltf/GltfTextOutput.cpp b/src/ObjWriting/XModel/Gltf/GltfTextOutput.cpp index 7547b3d2..267e5f68 100644 --- a/src/ObjWriting/XModel/Gltf/GltfTextOutput.cpp +++ b/src/ObjWriting/XModel/Gltf/GltfTextOutput.cpp @@ -22,7 +22,7 @@ std::optional TextOutput::CreateBufferUri(const void* buffer, const std::string output(base64BufferSize, '\0'); - std::memcpy(output.data(), &GLTF_DATA_URI_PREFIX, URI_PREFIX_LENGTH); + std::memcpy(output.data(), GLTF_DATA_URI_PREFIX, URI_PREFIX_LENGTH); unsigned long outLength = base64Length; base64_encode(static_cast(buffer), bufferSize, &output[URI_PREFIX_LENGTH], &outLength); diff --git a/src/ObjWriting/XModel/Gltf/GltfWriter.cpp b/src/ObjWriting/XModel/Gltf/GltfWriter.cpp index 01d37ac1..17cdbf25 100644 --- a/src/ObjWriting/XModel/Gltf/GltfWriter.cpp +++ b/src/ObjWriting/XModel/Gltf/GltfWriter.cpp @@ -76,11 +76,6 @@ namespace gltf.meshes->emplace_back(std::move(mesh)); } - static unsigned CreateNodeIndexFromBoneIndex(const unsigned boneIndex) - { - return boneIndex + NODE_FIRST_INDEX_BONES; - } - void CreateSkeletonNodes(JsonRoot& gltf) const { if (m_bones.empty()) @@ -89,20 +84,22 @@ namespace if (!gltf.nodes.has_value()) gltf.nodes.emplace(); - for (const auto [boneIndex, bone] : std::views::enumerate(m_bones)) + const auto boneCount = m_bones.size(); + for (auto boneIndex = 0u; boneIndex < boneCount; boneIndex++) { JsonNode boneNode; + const auto& bone = m_bones[boneIndex]; boneNode.name = bone.name; boneNode.translation = std::to_array(bone.globalOffset); boneNode.rotation = std::to_array({bone.globalRotation.m_x, bone.globalRotation.m_y, bone.globalRotation.m_z, bone.globalRotation.m_w}); - const auto isParentOf = [this, boneIndex](const unsigned b) + std::vector children; + for (auto maybeChildIndex = 0u; maybeChildIndex < boneCount; maybeChildIndex++) { - return m_bones[b].parentIndex == boneIndex; - }; - auto children = std::ranges::iota_view(0u, m_bones.size()) | std::views::filter(isParentOf) - | std::views::transform(CreateNodeIndexFromBoneIndex) | std::ranges::to>(); + if (m_bones[maybeChildIndex].parentIndex == static_cast(boneIndex)) + children.emplace_back(boneIndex + NODE_FIRST_INDEX_BONES); + } if (!children.empty()) boneNode.children = std::move(children); @@ -115,12 +112,17 @@ namespace if (m_bones.empty()) return; - JsonSkin skin; - skin.joints = - std::ranges::iota_view(0u, m_bones.size()) | std::views::transform(CreateNodeIndexFromBoneIndex) | std::ranges::to>(); - if (!gltf.skins.has_value()) gltf.skins.emplace(); + + JsonSkin skin; + + const auto boneCount = m_bones.size(); + + skin.joints.reserve(boneCount); + for (auto boneIndex = 0u; boneIndex < boneCount; boneIndex++) + skin.joints.emplace_back(boneIndex + NODE_FIRST_INDEX_BONES); + gltf.skins->emplace_back(std::move(skin)); } From 0a65c93aa5a14694556337c261190f6300a762bc Mon Sep 17 00:00:00 2001 From: Jan Date: Tue, 2 Apr 2024 18:47:54 +0200 Subject: [PATCH 10/25] chore: move materialIndex to XModelObject --- src/ObjCommon/XModel/XModelCommon.h | 2 +- .../Game/IW3/AssetDumpers/AssetDumperXModel.cpp | 12 ++++++------ .../Game/IW4/AssetDumpers/AssetDumperXModel.cpp | 14 ++++++++------ .../Game/IW5/AssetDumpers/AssetDumperXModel.cpp | 14 ++++++++------ .../Game/T5/AssetDumpers/AssetDumperXModel.cpp | 12 ++++++------ .../Game/T6/AssetDumpers/AssetDumperXModel.cpp | 12 ++++++------ .../XModel/Export/XModelExportWriter.cpp | 2 +- 7 files changed, 36 insertions(+), 32 deletions(-) diff --git a/src/ObjCommon/XModel/XModelCommon.h b/src/ObjCommon/XModel/XModelCommon.h index 41f50f1b..7469216e 100644 --- a/src/ObjCommon/XModel/XModelCommon.h +++ b/src/ObjCommon/XModel/XModelCommon.h @@ -9,6 +9,7 @@ struct XModelObject { std::string name; + int materialIndex; }; struct XModelBone @@ -52,7 +53,6 @@ struct XModelFace { int vertexIndex[3]; int objectIndex; - int materialIndex; }; struct XModelMaterial diff --git a/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperXModel.cpp b/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperXModel.cpp index 1902ca10..b059653a 100644 --- a/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperXModel.cpp +++ b/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperXModel.cpp @@ -312,14 +312,16 @@ namespace } } - void AddXModelObjects(AbstractXModelWriter& writer, const XModel* model, const unsigned lod) + void AddXModelObjects(AbstractXModelWriter& writer, 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 = "surf" + std::to_string(surfIndex); + object.materialIndex = static_cast(materialMapper.GetDistinctPositionByInputPosition(surfIndex + baseSurfaceIndex)); writer.AddObject(std::move(object)); } @@ -506,11 +508,10 @@ namespace } } - void AddXModelFaces(AbstractXModelWriter& writer, const DistinctMapper& materialMapper, const XModel* model, const unsigned lod) + void AddXModelFaces(AbstractXModelWriter& writer, const XModel* model, const unsigned lod) { const auto* surfs = &model->surfs[model->lodInfo[lod].surfIndex]; const auto surfCount = model->lodInfo[lod].numsurfs; - const auto baseSurfIndex = model->lodInfo[lod].surfIndex; for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++) { @@ -524,7 +525,6 @@ namespace face.vertexIndex[1] = tri[1] + surface.baseVertIndex; face.vertexIndex[2] = tri[2] + surface.baseVertIndex; face.objectIndex = static_cast(surfIndex); - face.materialIndex = static_cast(materialMapper.GetDistinctPositionByInputPosition(surfIndex + baseSurfIndex)); writer.AddFace(face); } } @@ -538,10 +538,10 @@ namespace AddXModelBones(context, writer, model); AddXModelMaterials(writer, materialMapper, model); - AddXModelObjects(writer, model, lod); + AddXModelObjects(writer, model, lod, materialMapper); AddXModelVertices(writer, model, lod); AddXModelVertexBoneWeights(writer, model, lod, boneWeightCollection); - AddXModelFaces(writer, materialMapper, model, lod); + AddXModelFaces(writer, model, lod); } void DumpXModelExportLod(const AssetDumpingContext& context, const XAssetInfo* asset, const unsigned lod) diff --git a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperXModel.cpp b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperXModel.cpp index 93141199..148f0979 100644 --- a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperXModel.cpp +++ b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperXModel.cpp @@ -318,12 +318,16 @@ namespace } } - void AddXModelObjects(AbstractXModelWriter& writer, const XModelSurfs* modelSurfs) + void AddXModelObjects(AbstractXModelWriter& writer, + const XModelSurfs* modelSurfs, + const DistinctMapper& materialMapper, + const int baseSurfaceIndex) { for (auto surfIndex = 0u; surfIndex < modelSurfs->numsurfs; surfIndex++) { XModelObject object; object.name = "surf" + std::to_string(surfIndex); + object.materialIndex = static_cast(materialMapper.GetDistinctPositionByInputPosition(surfIndex + baseSurfaceIndex)); writer.AddObject(std::move(object)); } @@ -501,8 +505,7 @@ namespace } } - void - AddXModelFaces(AbstractXModelWriter& writer, const DistinctMapper& materialMapper, const XModelSurfs* modelSurfs, const int baseSurfaceIndex) + void AddXModelFaces(AbstractXModelWriter& writer, const XModelSurfs* modelSurfs) { for (auto surfIndex = 0u; surfIndex < modelSurfs->numsurfs; surfIndex++) { @@ -516,7 +519,6 @@ namespace face.vertexIndex[1] = tri[1] + surface.baseVertIndex; face.vertexIndex[2] = tri[2] + surface.baseVertIndex; face.objectIndex = static_cast(surfIndex); - face.materialIndex = static_cast(materialMapper.GetDistinctPositionByInputPosition(surfIndex + baseSurfaceIndex)); writer.AddFace(face); } } @@ -532,10 +534,10 @@ namespace AddXModelBones(context, writer, model); AddXModelMaterials(writer, materialMapper, model); - AddXModelObjects(writer, modelSurfs); + AddXModelObjects(writer, modelSurfs, materialMapper, model->lodInfo[lod].surfIndex); AddXModelVertices(writer, modelSurfs); AddXModelVertexBoneWeights(writer, modelSurfs, boneWeightCollection); - AddXModelFaces(writer, materialMapper, modelSurfs, model->lodInfo[lod].surfIndex); + AddXModelFaces(writer, modelSurfs); } void DumpXModelExportLod(const AssetDumpingContext& context, const XAssetInfo* asset, const unsigned lod) diff --git a/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperXModel.cpp b/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperXModel.cpp index 8d986960..6c63fd28 100644 --- a/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperXModel.cpp +++ b/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperXModel.cpp @@ -317,12 +317,16 @@ namespace } } - void AddXModelObjects(AbstractXModelWriter& writer, const XModelSurfs* modelSurfs) + void AddXModelObjects(AbstractXModelWriter& writer, + const XModelSurfs* modelSurfs, + const DistinctMapper& materialMapper, + const int baseSurfaceIndex) { for (auto surfIndex = 0u; surfIndex < modelSurfs->numsurfs; surfIndex++) { XModelObject object; object.name = "surf" + std::to_string(surfIndex); + object.materialIndex = static_cast(materialMapper.GetDistinctPositionByInputPosition(surfIndex + baseSurfaceIndex)); writer.AddObject(std::move(object)); } @@ -500,8 +504,7 @@ namespace } } - void - AddXModelFaces(AbstractXModelWriter& writer, const DistinctMapper& materialMapper, const XModelSurfs* modelSurfs, const int baseSurfaceIndex) + void AddXModelFaces(AbstractXModelWriter& writer, const XModelSurfs* modelSurfs) { for (auto surfIndex = 0u; surfIndex < modelSurfs->numsurfs; surfIndex++) { @@ -515,7 +518,6 @@ namespace face.vertexIndex[1] = tri[1] + surface.baseVertIndex; face.vertexIndex[2] = tri[2] + surface.baseVertIndex; face.objectIndex = static_cast(surfIndex); - face.materialIndex = static_cast(materialMapper.GetDistinctPositionByInputPosition(surfIndex + baseSurfaceIndex)); writer.AddFace(face); } } @@ -531,10 +533,10 @@ namespace AddXModelBones(context, writer, model); AddXModelMaterials(writer, materialMapper, model); - AddXModelObjects(writer, modelSurfs); + AddXModelObjects(writer, modelSurfs, materialMapper, model->lodInfo[lod].surfIndex); AddXModelVertices(writer, modelSurfs); AddXModelVertexBoneWeights(writer, modelSurfs, boneWeightCollection); - AddXModelFaces(writer, materialMapper, modelSurfs, model->lodInfo[lod].surfIndex); + AddXModelFaces(writer, modelSurfs); } void DumpXModelExportLod(const AssetDumpingContext& context, const XAssetInfo* asset, const unsigned lod) diff --git a/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperXModel.cpp b/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperXModel.cpp index 3cf83c41..a0449f72 100644 --- a/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperXModel.cpp +++ b/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperXModel.cpp @@ -312,14 +312,16 @@ namespace } } - void AddXModelObjects(AbstractXModelWriter& writer, const XModel* model, const unsigned lod) + void AddXModelObjects(AbstractXModelWriter& writer, 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 = "surf" + std::to_string(surfIndex); + object.materialIndex = static_cast(materialMapper.GetDistinctPositionByInputPosition(surfIndex + baseSurfaceIndex)); writer.AddObject(std::move(object)); } @@ -506,11 +508,10 @@ namespace } } - void AddXModelFaces(AbstractXModelWriter& writer, const DistinctMapper& materialMapper, const XModel* model, const unsigned lod) + void AddXModelFaces(AbstractXModelWriter& writer, const XModel* model, const unsigned lod) { const auto* surfs = &model->surfs[model->lodInfo[lod].surfIndex]; const auto surfCount = model->lodInfo[lod].numsurfs; - const auto baseSurfIndex = model->lodInfo[lod].surfIndex; for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++) { @@ -524,7 +525,6 @@ namespace face.vertexIndex[1] = tri[1] + surface.baseVertIndex; face.vertexIndex[2] = tri[2] + surface.baseVertIndex; face.objectIndex = static_cast(surfIndex); - face.materialIndex = static_cast(materialMapper.GetDistinctPositionByInputPosition(surfIndex + baseSurfIndex)); writer.AddFace(face); } } @@ -538,10 +538,10 @@ namespace AddXModelBones(context, writer, model); AddXModelMaterials(writer, materialMapper, model); - AddXModelObjects(writer, model, lod); + AddXModelObjects(writer, model, lod, materialMapper); AddXModelVertices(writer, model, lod); AddXModelVertexBoneWeights(writer, model, lod, boneWeightCollection); - AddXModelFaces(writer, materialMapper, model, lod); + AddXModelFaces(writer, model, lod); } void DumpXModelExportLod(const AssetDumpingContext& context, const XAssetInfo* asset, const unsigned lod) diff --git a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperXModel.cpp b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperXModel.cpp index 50753b03..255bff70 100644 --- a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperXModel.cpp +++ b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperXModel.cpp @@ -324,14 +324,16 @@ namespace } } - void AddXModelObjects(AbstractXModelWriter& writer, const XModel* model, const unsigned lod) + void AddXModelObjects(AbstractXModelWriter& writer, 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 = "surf" + std::to_string(surfIndex); + object.materialIndex = static_cast(materialMapper.GetDistinctPositionByInputPosition(surfIndex + baseSurfaceIndex)); writer.AddObject(std::move(object)); } @@ -527,11 +529,10 @@ namespace } } - void AddXModelFaces(AbstractXModelWriter& writer, const DistinctMapper& materialMapper, const XModel* model, const unsigned lod) + void AddXModelFaces(AbstractXModelWriter& writer, const XModel* model, const unsigned lod) { const auto* surfs = &model->surfs[model->lodInfo[lod].surfIndex]; const auto surfCount = model->lodInfo[lod].numsurfs; - const auto baseSurfIndex = model->lodInfo[lod].surfIndex; if (!surfs) return; @@ -548,7 +549,6 @@ namespace face.vertexIndex[1] = tri[1] + surface.baseVertIndex; face.vertexIndex[2] = tri[2] + surface.baseVertIndex; face.objectIndex = static_cast(surfIndex); - face.materialIndex = static_cast(materialMapper.GetDistinctPositionByInputPosition(surfIndex + baseSurfIndex)); writer.AddFace(face); } } @@ -562,10 +562,10 @@ namespace AddXModelBones(context, writer, model); AddXModelMaterials(writer, materialMapper, model); - AddXModelObjects(writer, model, lod); + AddXModelObjects(writer, model, lod, materialMapper); AddXModelVertices(writer, model, lod); AddXModelVertexBoneWeights(writer, model, lod, boneWeightCollection); - AddXModelFaces(writer, materialMapper, model, lod); + AddXModelFaces(writer, model, lod); } void DumpXModelExportLod(const AssetDumpingContext& context, const XAssetInfo* asset, const unsigned lod) diff --git a/src/ObjWriting/XModel/Export/XModelExportWriter.cpp b/src/ObjWriting/XModel/Export/XModelExportWriter.cpp index 668ee9ea..6abbe100 100644 --- a/src/ObjWriting/XModel/Export/XModelExportWriter.cpp +++ b/src/ObjWriting/XModel/Export/XModelExportWriter.cpp @@ -140,7 +140,7 @@ class XModelExportWriter6 final : public XModelExportWriterBase const XModelVertex& v1 = m_vertices[face.vertexIndex[1]]; const XModelVertex& v2 = m_vertices[face.vertexIndex[2]]; - stream << "TRI " << face.objectIndex << " " << face.materialIndex << " 0 0\n"; + stream << "TRI " << face.objectIndex << " " << m_objects[face.objectIndex].materialIndex << " 0 0\n"; WriteFaceVertex(stream, distinctPositions[0], v0); WriteFaceVertex(stream, distinctPositions[1], v1); WriteFaceVertex(stream, distinctPositions[2], v2); From 826e59d627150a646caaedc96274b4cbf464e073 Mon Sep 17 00:00:00 2001 From: Jan Date: Tue, 2 Apr 2024 21:34:51 +0200 Subject: [PATCH 11/25] chore: update xmodel common to be a struct with substructs --- src/ObjCommon/XModel/XModelCommon.h | 16 +- .../IW3/AssetDumpers/AssetDumperXModel.cpp | 92 +++---- .../IW4/AssetDumpers/AssetDumperXModel.cpp | 95 ++++--- .../IW5/AssetDumpers/AssetDumperXModel.cpp | 95 ++++--- .../T5/AssetDumpers/AssetDumperXModel.cpp | 92 +++---- .../T6/AssetDumpers/AssetDumperXModel.cpp | 92 +++---- .../XModel/AbstractXModelWriter.cpp | 33 --- src/ObjWriting/XModel/AbstractXModelWriter.h | 26 -- .../XModel/Export/XModelExportWriter.cpp | 241 +++++++++--------- .../XModel/Export/XModelExportWriter.h | 18 +- src/ObjWriting/XModel/Gltf/GltfWriter.cpp | 38 +-- src/ObjWriting/XModel/Gltf/GltfWriter.h | 8 +- src/ObjWriting/XModel/XModelWriter.h | 18 ++ 13 files changed, 413 insertions(+), 451 deletions(-) delete mode 100644 src/ObjWriting/XModel/AbstractXModelWriter.cpp delete mode 100644 src/ObjWriting/XModel/AbstractXModelWriter.h create mode 100644 src/ObjWriting/XModel/XModelWriter.h diff --git a/src/ObjCommon/XModel/XModelCommon.h b/src/ObjCommon/XModel/XModelCommon.h index 7469216e..a8ee49b2 100644 --- a/src/ObjCommon/XModel/XModelCommon.h +++ b/src/ObjCommon/XModel/XModelCommon.h @@ -3,8 +3,8 @@ #include "Math/Quaternion.h" #include "Utils/DistinctMapper.h" -#include #include +#include struct XModelObject { @@ -31,8 +31,7 @@ struct XModelBoneWeight struct XModelVertexBoneWeightCollection { - std::unique_ptr weights; - size_t totalWeightCount; + std::vector weights; }; struct XModelVertexBoneWeights @@ -93,6 +92,17 @@ struct XModelMaterial void ApplyDefaults(); }; +struct XModelCommon +{ + std::vector m_objects; + std::vector m_bones; + std::vector m_materials; + std::vector m_vertices; + std::vector m_vertex_bone_weights; + std::vector m_faces; + XModelVertexBoneWeightCollection m_bone_weight_data; +}; + struct VertexMergerPos { float x; diff --git a/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperXModel.cpp b/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperXModel.cpp index b059653a..82b1ed39 100644 --- a/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperXModel.cpp +++ b/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperXModel.cpp @@ -6,12 +6,12 @@ #include "Utils/DistinctMapper.h" #include "Utils/HalfFloat.h" #include "Utils/QuatInt16.h" -#include "XModel/AbstractXModelWriter.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 @@ -140,7 +140,7 @@ namespace for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++) { ObjObject object; - object.name = "surf" + std::to_string(surfIndex); + object.name = std::format("surf{}", surfIndex); object.materialIndex = static_cast(materialMapper.GetDistinctPositionByInputPosition(surfIndex + baseSurfIndex)); writer.AddObject(std::move(object)); @@ -245,7 +245,7 @@ namespace writer.WriteObj(*assetFile, std::format("{}.mtl", model->name)); } - void AddXModelBones(const AssetDumpingContext& context, AbstractXModelWriter& writer, const XModel* model) + void AddXModelBones(XModelCommon& out, const AssetDumpingContext& context, const XModel* model) { for (auto boneNum = 0u; boneNum < model->numBones; boneNum++) { @@ -288,11 +288,11 @@ namespace QuatInt16::ToFloat(model->quats[boneNum - model->numRootBones][3])); } - writer.AddBone(std::move(bone)); + out.m_bones.emplace_back(std::move(bone)); } } - void AddXModelMaterials(AbstractXModelWriter& writer, DistinctMapper& materialMapper, const XModel* model) + void AddXModelMaterials(XModelCommon& out, DistinctMapper& materialMapper, const XModel* model) { for (auto surfaceMaterialNum = 0; surfaceMaterialNum < model->numsurfs; surfaceMaterialNum++) { @@ -307,12 +307,12 @@ namespace if (colorMap) xMaterial.colorMapName = std::string(colorMap->name); - writer.AddMaterial(std::move(xMaterial)); + out.m_materials.emplace_back(std::move(xMaterial)); } } } - void AddXModelObjects(AbstractXModelWriter& writer, const XModel* model, const unsigned lod, const DistinctMapper& materialMapper) + 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; @@ -320,14 +320,14 @@ namespace for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++) { XModelObject object; - object.name = "surf" + std::to_string(surfIndex); + object.name = std::format("surf{}", surfIndex); object.materialIndex = static_cast(materialMapper.GetDistinctPositionByInputPosition(surfIndex + baseSurfaceIndex)); - writer.AddObject(std::move(object)); + out.m_objects.emplace_back(std::move(object)); } } - void AddXModelVertices(AbstractXModelWriter& writer, const XModel* model, const unsigned lod) + 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; @@ -361,7 +361,7 @@ namespace vertex.uv[0] = uv[0]; vertex.uv[1] = uv[1]; - writer.AddVertex(vertex); + out.m_vertices.emplace_back(vertex); } } } @@ -371,29 +371,29 @@ namespace const auto* surfs = &model->surfs[model->lodInfo[lod].surfIndex]; const auto surfCount = model->lodInfo[lod].numsurfs; - weightCollection.totalWeightCount = 0u; + auto totalWeightCount = 0u; for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++) { const auto& surface = surfs[surfIndex]; if (surface.vertList) { - weightCollection.totalWeightCount += surface.vertListCount; + totalWeightCount += surface.vertListCount; } if (surface.vertInfo.vertsBlend) { - weightCollection.totalWeightCount += surface.vertInfo.vertCount[0] * 1; - weightCollection.totalWeightCount += surface.vertInfo.vertCount[1] * 2; - weightCollection.totalWeightCount += surface.vertInfo.vertCount[2] * 3; - weightCollection.totalWeightCount += surface.vertInfo.vertCount[3] * 4; + 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 = std::make_unique(weightCollection.totalWeightCount); + weightCollection.weights.resize(totalWeightCount); } - void AddXModelVertexBoneWeights(AbstractXModelWriter& writer, const XModel* model, const unsigned lod, XModelVertexBoneWeightCollection& weightCollection) + void AddXModelVertexBoneWeights(XModelCommon& out, const XModel* model, const unsigned lod, XModelVertexBoneWeightCollection& weightCollection) { const auto* surfs = &model->surfs[model->lodInfo[lod].surfIndex]; const auto surfCount = model->lodInfo[lod].numsurfs; @@ -416,7 +416,7 @@ namespace for (auto vertListVertexOffset = 0u; vertListVertexOffset < vertList.vertCount; vertListVertexOffset++) { - writer.AddVertexBoneWeights(XModelVertexBoneWeights{boneWeightOffset, 1}); + out.m_vertex_bone_weights.emplace_back(boneWeightOffset, 1); } handledVertices += vertList.vertCount; } @@ -434,7 +434,7 @@ namespace vertsBlendOffset += 1; - writer.AddVertexBoneWeights(XModelVertexBoneWeights{boneWeightOffset, 1}); + out.m_vertex_bone_weights.emplace_back(boneWeightOffset, 1); } // 2 bone weights @@ -451,7 +451,7 @@ namespace vertsBlendOffset += 3; - writer.AddVertexBoneWeights(XModelVertexBoneWeights{boneWeightOffset, 2}); + out.m_vertex_bone_weights.emplace_back(boneWeightOffset, 2); } // 3 bone weights @@ -471,7 +471,7 @@ namespace vertsBlendOffset += 5; - writer.AddVertexBoneWeights(XModelVertexBoneWeights{boneWeightOffset, 3}); + out.m_vertex_bone_weights.emplace_back(boneWeightOffset, 3); } // 4 bone weights @@ -494,7 +494,7 @@ namespace vertsBlendOffset += 7; - writer.AddVertexBoneWeights(XModelVertexBoneWeights{boneWeightOffset, 4}); + out.m_vertex_bone_weights.emplace_back(boneWeightOffset, 4); } handledVertices += @@ -503,12 +503,12 @@ namespace for (; handledVertices < surface.vertCount; handledVertices++) { - writer.AddVertexBoneWeights(XModelVertexBoneWeights{nullptr, 0}); + out.m_vertex_bone_weights.emplace_back(nullptr, 0); } } } - void AddXModelFaces(AbstractXModelWriter& writer, const XModel* model, const unsigned lod) + 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; @@ -525,26 +525,26 @@ namespace face.vertexIndex[1] = tri[1] + surface.baseVertIndex; face.vertexIndex[2] = tri[2] + surface.baseVertIndex; face.objectIndex = static_cast(surfIndex); - writer.AddFace(face); + out.m_faces.emplace_back(face); } } } - void PopulateXModelWriter(const AssetDumpingContext& context, const unsigned lod, const XModel* model, AbstractXModelWriter& writer) + void PopulateXModelWriter(XModelCommon& out, const AssetDumpingContext& context, const unsigned lod, const XModel* model) { DistinctMapper materialMapper(model->numsurfs); XModelVertexBoneWeightCollection boneWeightCollection; AllocateXModelBoneWeights(model, lod, boneWeightCollection); - AddXModelBones(context, writer, model); - AddXModelMaterials(writer, materialMapper, model); - AddXModelObjects(writer, model, lod, materialMapper); - AddXModelVertices(writer, model, lod); - AddXModelVertexBoneWeights(writer, model, lod, boneWeightCollection); - AddXModelFaces(writer, model, lod); + AddXModelBones(out, context, model); + AddXModelMaterials(out, materialMapper, model); + AddXModelObjects(out, model, lod, materialMapper); + AddXModelVertices(out, model, lod); + AddXModelVertexBoneWeights(out, model, lod, boneWeightCollection); + AddXModelFaces(out, model, lod); } - void DumpXModelExportLod(const AssetDumpingContext& context, const XAssetInfo* asset, const unsigned lod) + 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")); @@ -552,13 +552,13 @@ namespace if (!assetFile) return; - const auto writer = XModelExportWriter::CreateWriterForVersion6(context.m_zone->m_game->GetShortName(), context.m_zone->m_name); - PopulateXModelWriter(context, lod, model, *writer); - - writer->Write(*assetFile); + const auto writer = xmodel_export::CreateWriterForVersion6(*assetFile, context.m_zone->m_game->GetShortName(), context.m_zone->m_name); + writer->Write(common); } - template void DumpGltfLod(const AssetDumpingContext& context, const XAssetInfo* asset, const unsigned lod, const std::string& extension) + 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)); @@ -568,9 +568,8 @@ namespace 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); - PopulateXModelWriter(context, lod, model, *writer); - writer->Write(*assetFile); + writer->Write(common); } void DumpXModelSurfs(const AssetDumpingContext& context, const XAssetInfo* asset) @@ -582,6 +581,9 @@ namespace 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: @@ -589,15 +591,15 @@ namespace break; case ObjWriting::Configuration_t::ModelOutputFormat_e::XMODEL_EXPORT: - DumpXModelExportLod(context, asset, currentLod); + DumpXModelExportLod(common, context, asset, currentLod); break; case ObjWriting::Configuration_t::ModelOutputFormat_e::GLTF: - DumpGltfLod(context, asset, currentLod, ".gltf"); + DumpGltfLod(common, context, asset, currentLod, ".gltf"); break; case ObjWriting::Configuration_t::ModelOutputFormat_e::GLB: - DumpGltfLod(context, asset, currentLod, ".glb"); + DumpGltfLod(common, context, asset, currentLod, ".glb"); break; default: diff --git a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperXModel.cpp b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperXModel.cpp index 148f0979..419ce4e1 100644 --- a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperXModel.cpp +++ b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperXModel.cpp @@ -6,12 +6,12 @@ #include "Utils/DistinctMapper.h" #include "Utils/HalfFloat.h" #include "Utils/QuatInt16.h" -#include "XModel/AbstractXModelWriter.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 @@ -147,7 +147,7 @@ namespace for (auto surfIndex = 0u; surfIndex < modelSurfs->numsurfs; surfIndex++) { ObjObject object; - object.name = "surf" + std::to_string(surfIndex); + object.name = std::format("surf{}", surfIndex); object.materialIndex = static_cast(materialMapper.GetDistinctPositionByInputPosition(surfIndex + baseSurfaceIndex)); writer.AddObject(std::move(object)); @@ -251,7 +251,7 @@ namespace writer.WriteObj(*assetFile, std::format("{}.mtl", model->name)); } - void AddXModelBones(const AssetDumpingContext& context, AbstractXModelWriter& writer, const XModel* model) + void AddXModelBones(XModelCommon& out, const AssetDumpingContext& context, const XModel* model) { for (auto boneNum = 0u; boneNum < model->numBones; boneNum++) { @@ -294,11 +294,11 @@ namespace QuatInt16::ToFloat(model->quats[boneNum - model->numRootBones][3])); } - writer.AddBone(std::move(bone)); + out.m_bones.emplace_back(std::move(bone)); } } - void AddXModelMaterials(AbstractXModelWriter& writer, DistinctMapper& materialMapper, const XModel* model) + void AddXModelMaterials(XModelCommon& out, DistinctMapper& materialMapper, const XModel* model) { for (auto surfaceMaterialNum = 0; surfaceMaterialNum < model->numsurfs; surfaceMaterialNum++) { @@ -313,27 +313,24 @@ namespace if (colorMap) xMaterial.colorMapName = std::string(colorMap->name); - writer.AddMaterial(std::move(xMaterial)); + out.m_materials.emplace_back(std::move(xMaterial)); } } } - void AddXModelObjects(AbstractXModelWriter& writer, - const XModelSurfs* modelSurfs, - const DistinctMapper& materialMapper, - const int baseSurfaceIndex) + 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 = "surf" + std::to_string(surfIndex); + object.name = std::format("surf{}", surfIndex); object.materialIndex = static_cast(materialMapper.GetDistinctPositionByInputPosition(surfIndex + baseSurfaceIndex)); - writer.AddObject(std::move(object)); + out.m_objects.emplace_back(std::move(object)); } } - void AddXModelVertices(AbstractXModelWriter& writer, const XModelSurfs* modelSurfs) + void AddXModelVertices(XModelCommon& out, const XModelSurfs* modelSurfs) { for (auto surfIndex = 0u; surfIndex < modelSurfs->numsurfs; surfIndex++) { @@ -364,36 +361,36 @@ namespace vertex.uv[0] = uv[0]; vertex.uv[1] = uv[1]; - writer.AddVertex(vertex); + out.m_vertices.emplace_back(vertex); } } } void AllocateXModelBoneWeights(const XModelSurfs* modelSurfs, XModelVertexBoneWeightCollection& weightCollection) { - weightCollection.totalWeightCount = 0u; + auto totalWeightCount = 0u; for (auto surfIndex = 0u; surfIndex < modelSurfs->numsurfs; surfIndex++) { const auto& surface = modelSurfs->surfs[surfIndex]; if (surface.vertList) { - weightCollection.totalWeightCount += surface.vertListCount; + totalWeightCount += surface.vertListCount; } if (surface.vertInfo.vertsBlend) { - weightCollection.totalWeightCount += surface.vertInfo.vertCount[0] * 1; - weightCollection.totalWeightCount += surface.vertInfo.vertCount[1] * 2; - weightCollection.totalWeightCount += surface.vertInfo.vertCount[2] * 3; - weightCollection.totalWeightCount += surface.vertInfo.vertCount[3] * 4; + 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 = std::make_unique(weightCollection.totalWeightCount); + weightCollection.weights.resize(totalWeightCount); } - void AddXModelVertexBoneWeights(AbstractXModelWriter& writer, const XModelSurfs* modelSurfs, XModelVertexBoneWeightCollection& weightCollection) + void AddXModelVertexBoneWeights(XModelCommon& out, const XModelSurfs* modelSurfs, XModelVertexBoneWeightCollection& weightCollection) { size_t weightOffset = 0u; @@ -413,7 +410,7 @@ namespace for (auto vertListVertexOffset = 0u; vertListVertexOffset < vertList.vertCount; vertListVertexOffset++) { - writer.AddVertexBoneWeights(XModelVertexBoneWeights{boneWeightOffset, 1}); + out.m_vertex_bone_weights.emplace_back(boneWeightOffset, 1); } handledVertices += vertList.vertCount; } @@ -431,7 +428,7 @@ namespace vertsBlendOffset += 1; - writer.AddVertexBoneWeights(XModelVertexBoneWeights{boneWeightOffset, 1}); + out.m_vertex_bone_weights.emplace_back(boneWeightOffset, 1); } // 2 bone weights @@ -448,7 +445,7 @@ namespace vertsBlendOffset += 3; - writer.AddVertexBoneWeights(XModelVertexBoneWeights{boneWeightOffset, 2}); + out.m_vertex_bone_weights.emplace_back(boneWeightOffset, 2); } // 3 bone weights @@ -468,7 +465,7 @@ namespace vertsBlendOffset += 5; - writer.AddVertexBoneWeights(XModelVertexBoneWeights{boneWeightOffset, 3}); + out.m_vertex_bone_weights.emplace_back(boneWeightOffset, 3); } // 4 bone weights @@ -491,7 +488,7 @@ namespace vertsBlendOffset += 7; - writer.AddVertexBoneWeights(XModelVertexBoneWeights{boneWeightOffset, 4}); + out.m_vertex_bone_weights.emplace_back(boneWeightOffset, 4); } handledVertices += @@ -500,12 +497,12 @@ namespace for (; handledVertices < surface.vertCount; handledVertices++) { - writer.AddVertexBoneWeights(XModelVertexBoneWeights{nullptr, 0}); + out.m_vertex_bone_weights.emplace_back(nullptr, 0); } } } - void AddXModelFaces(AbstractXModelWriter& writer, const XModelSurfs* modelSurfs) + void AddXModelFaces(XModelCommon& out, const XModelSurfs* modelSurfs) { for (auto surfIndex = 0u; surfIndex < modelSurfs->numsurfs; surfIndex++) { @@ -519,12 +516,12 @@ namespace face.vertexIndex[1] = tri[1] + surface.baseVertIndex; face.vertexIndex[2] = tri[2] + surface.baseVertIndex; face.objectIndex = static_cast(surfIndex); - writer.AddFace(face); + out.m_faces.emplace_back(face); } } } - void PopulateXModelWriter(const AssetDumpingContext& context, const unsigned lod, const XModel* model, AbstractXModelWriter& writer) + void PopulateXModelWriter(XModelCommon& out, const AssetDumpingContext& context, const unsigned lod, const XModel* model) { const auto* modelSurfs = model->lodInfo[lod].modelSurfs; @@ -532,15 +529,15 @@ namespace XModelVertexBoneWeightCollection boneWeightCollection; AllocateXModelBoneWeights(modelSurfs, boneWeightCollection); - AddXModelBones(context, writer, model); - AddXModelMaterials(writer, materialMapper, model); - AddXModelObjects(writer, modelSurfs, materialMapper, model->lodInfo[lod].surfIndex); - AddXModelVertices(writer, modelSurfs); - AddXModelVertexBoneWeights(writer, modelSurfs, boneWeightCollection); - AddXModelFaces(writer, modelSurfs); + AddXModelBones(out, context, model); + AddXModelMaterials(out, materialMapper, model); + AddXModelObjects(out, modelSurfs, materialMapper, model->lodInfo[lod].surfIndex); + AddXModelVertices(out, modelSurfs); + AddXModelVertexBoneWeights(out, modelSurfs, boneWeightCollection); + AddXModelFaces(out, modelSurfs); } - void DumpXModelExportLod(const AssetDumpingContext& context, const XAssetInfo* asset, const unsigned lod) + 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; @@ -549,13 +546,13 @@ namespace if (!assetFile) return; - const auto writer = XModelExportWriter::CreateWriterForVersion6(context.m_zone->m_game->GetShortName(), context.m_zone->m_name); - PopulateXModelWriter(context, lod, model, *writer); - - writer->Write(*assetFile); + const auto writer = xmodel_export::CreateWriterForVersion6(*assetFile, context.m_zone->m_game->GetShortName(), context.m_zone->m_name); + writer->Write(common); } - template void DumpGltfLod(const AssetDumpingContext& context, const XAssetInfo* asset, const unsigned lod, const std::string& extension) + 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; @@ -566,9 +563,8 @@ namespace 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); - PopulateXModelWriter(context, lod, model, *writer); - writer->Write(*assetFile); + writer->Write(common); } void DumpXModelSurfs(const AssetDumpingContext& context, const XAssetInfo* asset) @@ -580,6 +576,9 @@ namespace 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: @@ -587,15 +586,15 @@ namespace break; case ObjWriting::Configuration_t::ModelOutputFormat_e::XMODEL_EXPORT: - DumpXModelExportLod(context, asset, currentLod); + DumpXModelExportLod(common, context, asset, currentLod); break; case ObjWriting::Configuration_t::ModelOutputFormat_e::GLTF: - DumpGltfLod(context, asset, currentLod, ".gltf"); + DumpGltfLod(common, context, asset, currentLod, ".gltf"); break; case ObjWriting::Configuration_t::ModelOutputFormat_e::GLB: - DumpGltfLod(context, asset, currentLod, ".glb"); + DumpGltfLod(common, context, asset, currentLod, ".glb"); break; default: diff --git a/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperXModel.cpp b/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperXModel.cpp index 6c63fd28..1be3b9f2 100644 --- a/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperXModel.cpp +++ b/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperXModel.cpp @@ -6,12 +6,12 @@ #include "Utils/DistinctMapper.h" #include "Utils/HalfFloat.h" #include "Utils/QuatInt16.h" -#include "XModel/AbstractXModelWriter.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 @@ -146,7 +146,7 @@ namespace for (auto surfIndex = 0u; surfIndex < modelSurfs->numsurfs; surfIndex++) { ObjObject object; - object.name = "surf" + std::to_string(surfIndex); + object.name = std::format("surf{}", surfIndex); object.materialIndex = static_cast(materialMapper.GetDistinctPositionByInputPosition(surfIndex + baseSurfaceIndex)); writer.AddObject(std::move(object)); @@ -250,7 +250,7 @@ namespace writer.WriteObj(*assetFile, std::format("{}.mtl", model->name)); } - void AddXModelBones(const AssetDumpingContext& context, AbstractXModelWriter& writer, const XModel* model) + void AddXModelBones(XModelCommon& out, const AssetDumpingContext& context, const XModel* model) { for (auto boneNum = 0u; boneNum < model->numBones; boneNum++) { @@ -293,11 +293,11 @@ namespace QuatInt16::ToFloat(model->quats[boneNum - model->numRootBones][3])); } - writer.AddBone(std::move(bone)); + out.m_bones.emplace_back(std::move(bone)); } } - void AddXModelMaterials(AbstractXModelWriter& writer, DistinctMapper& materialMapper, const XModel* model) + void AddXModelMaterials(XModelCommon& out, DistinctMapper& materialMapper, const XModel* model) { for (auto surfaceMaterialNum = 0; surfaceMaterialNum < model->numsurfs; surfaceMaterialNum++) { @@ -312,27 +312,24 @@ namespace if (colorMap) xMaterial.colorMapName = std::string(colorMap->name); - writer.AddMaterial(std::move(xMaterial)); + out.m_materials.emplace_back(std::move(xMaterial)); } } } - void AddXModelObjects(AbstractXModelWriter& writer, - const XModelSurfs* modelSurfs, - const DistinctMapper& materialMapper, - const int baseSurfaceIndex) + 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 = "surf" + std::to_string(surfIndex); + object.name = std::format("surf{}", surfIndex); object.materialIndex = static_cast(materialMapper.GetDistinctPositionByInputPosition(surfIndex + baseSurfaceIndex)); - writer.AddObject(std::move(object)); + out.m_objects.emplace_back(std::move(object)); } } - void AddXModelVertices(AbstractXModelWriter& writer, const XModelSurfs* modelSurfs) + void AddXModelVertices(XModelCommon& out, const XModelSurfs* modelSurfs) { for (auto surfIndex = 0u; surfIndex < modelSurfs->numsurfs; surfIndex++) { @@ -363,36 +360,36 @@ namespace vertex.uv[0] = uv[0]; vertex.uv[1] = uv[1]; - writer.AddVertex(vertex); + out.m_vertices.emplace_back(vertex); } } } void AllocateXModelBoneWeights(const XModelSurfs* modelSurfs, XModelVertexBoneWeightCollection& weightCollection) { - weightCollection.totalWeightCount = 0u; + auto totalWeightCount = 0u; for (auto surfIndex = 0u; surfIndex < modelSurfs->numsurfs; surfIndex++) { const auto& surface = modelSurfs->surfs[surfIndex]; if (surface.vertList) { - weightCollection.totalWeightCount += surface.vertListCount; + totalWeightCount += surface.vertListCount; } if (surface.vertInfo.vertsBlend) { - weightCollection.totalWeightCount += surface.vertInfo.vertCount[0] * 1; - weightCollection.totalWeightCount += surface.vertInfo.vertCount[1] * 2; - weightCollection.totalWeightCount += surface.vertInfo.vertCount[2] * 3; - weightCollection.totalWeightCount += surface.vertInfo.vertCount[3] * 4; + 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 = std::make_unique(weightCollection.totalWeightCount); + weightCollection.weights.resize(totalWeightCount); } - void AddXModelVertexBoneWeights(AbstractXModelWriter& writer, const XModelSurfs* modelSurfs, XModelVertexBoneWeightCollection& weightCollection) + void AddXModelVertexBoneWeights(XModelCommon& out, const XModelSurfs* modelSurfs, XModelVertexBoneWeightCollection& weightCollection) { size_t weightOffset = 0u; @@ -412,7 +409,7 @@ namespace for (auto vertListVertexOffset = 0u; vertListVertexOffset < vertList.vertCount; vertListVertexOffset++) { - writer.AddVertexBoneWeights(XModelVertexBoneWeights{boneWeightOffset, 1}); + out.m_vertex_bone_weights.emplace_back(boneWeightOffset, 1); } handledVertices += vertList.vertCount; } @@ -430,7 +427,7 @@ namespace vertsBlendOffset += 1; - writer.AddVertexBoneWeights(XModelVertexBoneWeights{boneWeightOffset, 1}); + out.m_vertex_bone_weights.emplace_back(boneWeightOffset, 1); } // 2 bone weights @@ -447,7 +444,7 @@ namespace vertsBlendOffset += 3; - writer.AddVertexBoneWeights(XModelVertexBoneWeights{boneWeightOffset, 2}); + out.m_vertex_bone_weights.emplace_back(boneWeightOffset, 2); } // 3 bone weights @@ -467,7 +464,7 @@ namespace vertsBlendOffset += 5; - writer.AddVertexBoneWeights(XModelVertexBoneWeights{boneWeightOffset, 3}); + out.m_vertex_bone_weights.emplace_back(boneWeightOffset, 3); } // 4 bone weights @@ -490,7 +487,7 @@ namespace vertsBlendOffset += 7; - writer.AddVertexBoneWeights(XModelVertexBoneWeights{boneWeightOffset, 4}); + out.m_vertex_bone_weights.emplace_back(boneWeightOffset, 4); } handledVertices += @@ -499,12 +496,12 @@ namespace for (; handledVertices < surface.vertCount; handledVertices++) { - writer.AddVertexBoneWeights(XModelVertexBoneWeights{nullptr, 0}); + out.m_vertex_bone_weights.emplace_back(nullptr, 0); } } } - void AddXModelFaces(AbstractXModelWriter& writer, const XModelSurfs* modelSurfs) + void AddXModelFaces(XModelCommon& out, const XModelSurfs* modelSurfs) { for (auto surfIndex = 0u; surfIndex < modelSurfs->numsurfs; surfIndex++) { @@ -518,12 +515,12 @@ namespace face.vertexIndex[1] = tri[1] + surface.baseVertIndex; face.vertexIndex[2] = tri[2] + surface.baseVertIndex; face.objectIndex = static_cast(surfIndex); - writer.AddFace(face); + out.m_faces.emplace_back(face); } } } - void PopulateXModelWriter(const AssetDumpingContext& context, const unsigned lod, const XModel* model, AbstractXModelWriter& writer) + void PopulateXModelWriter(XModelCommon& out, const AssetDumpingContext& context, const unsigned lod, const XModel* model) { const auto* modelSurfs = model->lodInfo[lod].modelSurfs; @@ -531,15 +528,15 @@ namespace XModelVertexBoneWeightCollection boneWeightCollection; AllocateXModelBoneWeights(modelSurfs, boneWeightCollection); - AddXModelBones(context, writer, model); - AddXModelMaterials(writer, materialMapper, model); - AddXModelObjects(writer, modelSurfs, materialMapper, model->lodInfo[lod].surfIndex); - AddXModelVertices(writer, modelSurfs); - AddXModelVertexBoneWeights(writer, modelSurfs, boneWeightCollection); - AddXModelFaces(writer, modelSurfs); + AddXModelBones(out, context, model); + AddXModelMaterials(out, materialMapper, model); + AddXModelObjects(out, modelSurfs, materialMapper, model->lodInfo[lod].surfIndex); + AddXModelVertices(out, modelSurfs); + AddXModelVertexBoneWeights(out, modelSurfs, boneWeightCollection); + AddXModelFaces(out, modelSurfs); } - void DumpXModelExportLod(const AssetDumpingContext& context, const XAssetInfo* asset, const unsigned lod) + 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; @@ -548,13 +545,13 @@ namespace if (!assetFile) return; - const auto writer = XModelExportWriter::CreateWriterForVersion6(context.m_zone->m_game->GetShortName(), context.m_zone->m_name); - PopulateXModelWriter(context, lod, model, *writer); - - writer->Write(*assetFile); + const auto writer = xmodel_export::CreateWriterForVersion6(*assetFile, context.m_zone->m_game->GetShortName(), context.m_zone->m_name); + writer->Write(common); } - template void DumpGltfLod(const AssetDumpingContext& context, const XAssetInfo* asset, const unsigned lod, const std::string& extension) + 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; @@ -565,9 +562,8 @@ namespace 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); - PopulateXModelWriter(context, lod, model, *writer); - writer->Write(*assetFile); + writer->Write(common); } void DumpXModelSurfs(const AssetDumpingContext& context, const XAssetInfo* asset) @@ -579,6 +575,9 @@ namespace 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: @@ -586,15 +585,15 @@ namespace break; case ObjWriting::Configuration_t::ModelOutputFormat_e::XMODEL_EXPORT: - DumpXModelExportLod(context, asset, currentLod); + DumpXModelExportLod(common, context, asset, currentLod); break; case ObjWriting::Configuration_t::ModelOutputFormat_e::GLTF: - DumpGltfLod(context, asset, currentLod, ".gltf"); + DumpGltfLod(common, context, asset, currentLod, ".gltf"); break; case ObjWriting::Configuration_t::ModelOutputFormat_e::GLB: - DumpGltfLod(context, asset, currentLod, ".glb"); + DumpGltfLod(common, context, asset, currentLod, ".glb"); break; default: diff --git a/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperXModel.cpp b/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperXModel.cpp index a0449f72..a77887a7 100644 --- a/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperXModel.cpp +++ b/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperXModel.cpp @@ -6,12 +6,12 @@ #include "Utils/DistinctMapper.h" #include "Utils/HalfFloat.h" #include "Utils/QuatInt16.h" -#include "XModel/AbstractXModelWriter.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 @@ -140,7 +140,7 @@ namespace for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++) { ObjObject object; - object.name = "surf" + std::to_string(surfIndex); + object.name = std::format("surf{}", surfIndex); object.materialIndex = static_cast(materialMapper.GetDistinctPositionByInputPosition(surfIndex + baseSurfIndex)); writer.AddObject(std::move(object)); @@ -245,7 +245,7 @@ namespace writer.WriteObj(*assetFile, std::format("{}.mtl", model->name)); } - void AddXModelBones(const AssetDumpingContext& context, AbstractXModelWriter& writer, const XModel* model) + void AddXModelBones(XModelCommon& out, const AssetDumpingContext& context, const XModel* model) { for (auto boneNum = 0u; boneNum < model->numBones; boneNum++) { @@ -288,11 +288,11 @@ namespace QuatInt16::ToFloat(model->quats[boneNum - model->numRootBones][3])); } - writer.AddBone(std::move(bone)); + out.m_bones.emplace_back(std::move(bone)); } } - void AddXModelMaterials(AbstractXModelWriter& writer, DistinctMapper& materialMapper, const XModel* model) + void AddXModelMaterials(XModelCommon& out, DistinctMapper& materialMapper, const XModel* model) { for (auto surfaceMaterialNum = 0; surfaceMaterialNum < model->numsurfs; surfaceMaterialNum++) { @@ -307,12 +307,12 @@ namespace if (colorMap) xMaterial.colorMapName = std::string(colorMap->name); - writer.AddMaterial(std::move(xMaterial)); + out.m_materials.emplace_back(std::move(xMaterial)); } } } - void AddXModelObjects(AbstractXModelWriter& writer, const XModel* model, const unsigned lod, const DistinctMapper& materialMapper) + 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; @@ -320,14 +320,14 @@ namespace for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++) { XModelObject object; - object.name = "surf" + std::to_string(surfIndex); + object.name = std::format("surf{}", surfIndex); object.materialIndex = static_cast(materialMapper.GetDistinctPositionByInputPosition(surfIndex + baseSurfaceIndex)); - writer.AddObject(std::move(object)); + out.m_objects.emplace_back(std::move(object)); } } - void AddXModelVertices(AbstractXModelWriter& writer, const XModel* model, const unsigned lod) + 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; @@ -361,7 +361,7 @@ namespace vertex.uv[0] = uv[0]; vertex.uv[1] = uv[1]; - writer.AddVertex(vertex); + out.m_vertices.emplace_back(vertex); } } } @@ -371,29 +371,29 @@ namespace const auto* surfs = &model->surfs[model->lodInfo[lod].surfIndex]; const auto surfCount = model->lodInfo[lod].numsurfs; - weightCollection.totalWeightCount = 0u; + auto totalWeightCount = 0u; for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++) { const auto& surface = surfs[surfIndex]; if (surface.vertList) { - weightCollection.totalWeightCount += surface.vertListCount; + totalWeightCount += surface.vertListCount; } if (surface.vertInfo.vertsBlend) { - weightCollection.totalWeightCount += surface.vertInfo.vertCount[0] * 1; - weightCollection.totalWeightCount += surface.vertInfo.vertCount[1] * 2; - weightCollection.totalWeightCount += surface.vertInfo.vertCount[2] * 3; - weightCollection.totalWeightCount += surface.vertInfo.vertCount[3] * 4; + 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 = std::make_unique(weightCollection.totalWeightCount); + weightCollection.weights.resize(totalWeightCount); } - void AddXModelVertexBoneWeights(AbstractXModelWriter& writer, const XModel* model, const unsigned lod, XModelVertexBoneWeightCollection& weightCollection) + void AddXModelVertexBoneWeights(XModelCommon& out, const XModel* model, const unsigned lod, XModelVertexBoneWeightCollection& weightCollection) { const auto* surfs = &model->surfs[model->lodInfo[lod].surfIndex]; const auto surfCount = model->lodInfo[lod].numsurfs; @@ -416,7 +416,7 @@ namespace for (auto vertListVertexOffset = 0u; vertListVertexOffset < vertList.vertCount; vertListVertexOffset++) { - writer.AddVertexBoneWeights(XModelVertexBoneWeights{boneWeightOffset, 1}); + out.m_vertex_bone_weights.emplace_back(boneWeightOffset, 1); } handledVertices += vertList.vertCount; } @@ -434,7 +434,7 @@ namespace vertsBlendOffset += 1; - writer.AddVertexBoneWeights(XModelVertexBoneWeights{boneWeightOffset, 1}); + out.m_vertex_bone_weights.emplace_back(boneWeightOffset, 1); } // 2 bone weights @@ -451,7 +451,7 @@ namespace vertsBlendOffset += 3; - writer.AddVertexBoneWeights(XModelVertexBoneWeights{boneWeightOffset, 2}); + out.m_vertex_bone_weights.emplace_back(boneWeightOffset, 2); } // 3 bone weights @@ -471,7 +471,7 @@ namespace vertsBlendOffset += 5; - writer.AddVertexBoneWeights(XModelVertexBoneWeights{boneWeightOffset, 3}); + out.m_vertex_bone_weights.emplace_back(boneWeightOffset, 3); } // 4 bone weights @@ -494,7 +494,7 @@ namespace vertsBlendOffset += 7; - writer.AddVertexBoneWeights(XModelVertexBoneWeights{boneWeightOffset, 4}); + out.m_vertex_bone_weights.emplace_back(boneWeightOffset, 4); } handledVertices += @@ -503,12 +503,12 @@ namespace for (; handledVertices < surface.vertCount; handledVertices++) { - writer.AddVertexBoneWeights(XModelVertexBoneWeights{nullptr, 0}); + out.m_vertex_bone_weights.emplace_back(nullptr, 0); } } } - void AddXModelFaces(AbstractXModelWriter& writer, const XModel* model, const unsigned lod) + 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; @@ -525,26 +525,26 @@ namespace face.vertexIndex[1] = tri[1] + surface.baseVertIndex; face.vertexIndex[2] = tri[2] + surface.baseVertIndex; face.objectIndex = static_cast(surfIndex); - writer.AddFace(face); + out.m_faces.emplace_back(face); } } } - void PopulateXModelWriter(const AssetDumpingContext& context, const unsigned lod, const XModel* model, AbstractXModelWriter& writer) + void PopulateXModelWriter(XModelCommon& out, const AssetDumpingContext& context, const unsigned lod, const XModel* model) { DistinctMapper materialMapper(model->numsurfs); XModelVertexBoneWeightCollection boneWeightCollection; AllocateXModelBoneWeights(model, lod, boneWeightCollection); - AddXModelBones(context, writer, model); - AddXModelMaterials(writer, materialMapper, model); - AddXModelObjects(writer, model, lod, materialMapper); - AddXModelVertices(writer, model, lod); - AddXModelVertexBoneWeights(writer, model, lod, boneWeightCollection); - AddXModelFaces(writer, model, lod); + AddXModelBones(out, context, model); + AddXModelMaterials(out, materialMapper, model); + AddXModelObjects(out, model, lod, materialMapper); + AddXModelVertices(out, model, lod); + AddXModelVertexBoneWeights(out, model, lod, boneWeightCollection); + AddXModelFaces(out, model, lod); } - void DumpXModelExportLod(const AssetDumpingContext& context, const XAssetInfo* asset, const unsigned lod) + 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")); @@ -552,13 +552,13 @@ namespace if (!assetFile) return; - const auto writer = XModelExportWriter::CreateWriterForVersion6(context.m_zone->m_game->GetShortName(), context.m_zone->m_name); - PopulateXModelWriter(context, lod, model, *writer); - - writer->Write(*assetFile); + const auto writer = xmodel_export::CreateWriterForVersion6(*assetFile, context.m_zone->m_game->GetShortName(), context.m_zone->m_name); + writer->Write(common); } - template void DumpGltfLod(const AssetDumpingContext& context, const XAssetInfo* asset, const unsigned lod, const std::string& extension) + 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)); @@ -568,9 +568,8 @@ namespace 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); - PopulateXModelWriter(context, lod, model, *writer); - writer->Write(*assetFile); + writer->Write(common); } void DumpXModelSurfs(const AssetDumpingContext& context, const XAssetInfo* asset) @@ -582,6 +581,9 @@ namespace 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: @@ -589,15 +591,15 @@ namespace break; case ObjWriting::Configuration_t::ModelOutputFormat_e::XMODEL_EXPORT: - DumpXModelExportLod(context, asset, currentLod); + DumpXModelExportLod(common, context, asset, currentLod); break; case ObjWriting::Configuration_t::ModelOutputFormat_e::GLTF: - DumpGltfLod(context, asset, currentLod, ".gltf"); + DumpGltfLod(common, context, asset, currentLod, ".gltf"); break; case ObjWriting::Configuration_t::ModelOutputFormat_e::GLB: - DumpGltfLod(context, asset, currentLod, ".glb"); + DumpGltfLod(common, context, asset, currentLod, ".glb"); break; default: diff --git a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperXModel.cpp b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperXModel.cpp index 255bff70..bfeaa64c 100644 --- a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperXModel.cpp +++ b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperXModel.cpp @@ -6,12 +6,12 @@ #include "Utils/DistinctMapper.h" #include "Utils/HalfFloat.h" #include "Utils/QuatInt16.h" -#include "XModel/AbstractXModelWriter.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 @@ -152,7 +152,7 @@ namespace for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++) { ObjObject object; - object.name = "surf" + std::to_string(surfIndex); + object.name = std::format("surf{}", surfIndex); object.materialIndex = static_cast(materialMapper.GetDistinctPositionByInputPosition(surfIndex + baseSurfIndex)); writer.AddObject(std::move(object)); @@ -257,7 +257,7 @@ namespace writer.WriteObj(*assetFile, std::format("{}.mtl", model->name)); } - void AddXModelBones(const AssetDumpingContext& context, AbstractXModelWriter& writer, const XModel* model) + void AddXModelBones(XModelCommon& out, const AssetDumpingContext& context, const XModel* model) { for (auto boneNum = 0u; boneNum < model->numBones; boneNum++) { @@ -300,11 +300,11 @@ namespace QuatInt16::ToFloat(model->quats[boneNum - model->numRootBones][3])); } - writer.AddBone(std::move(bone)); + out.m_bones.emplace_back(std::move(bone)); } } - void AddXModelMaterials(AbstractXModelWriter& writer, DistinctMapper& materialMapper, const XModel* model) + void AddXModelMaterials(XModelCommon& out, DistinctMapper& materialMapper, const XModel* model) { for (auto surfaceMaterialNum = 0; surfaceMaterialNum < model->numsurfs; surfaceMaterialNum++) { @@ -319,12 +319,12 @@ namespace if (colorMap) xMaterial.colorMapName = std::string(colorMap->name); - writer.AddMaterial(std::move(xMaterial)); + out.m_materials.emplace_back(std::move(xMaterial)); } } } - void AddXModelObjects(AbstractXModelWriter& writer, const XModel* model, const unsigned lod, const DistinctMapper& materialMapper) + 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; @@ -332,14 +332,14 @@ namespace for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++) { XModelObject object; - object.name = "surf" + std::to_string(surfIndex); + object.name = std::format("surf{}", surfIndex); object.materialIndex = static_cast(materialMapper.GetDistinctPositionByInputPosition(surfIndex + baseSurfaceIndex)); - writer.AddObject(std::move(object)); + out.m_objects.emplace_back(std::move(object)); } } - void AddXModelVertices(AbstractXModelWriter& writer, const XModel* model, const unsigned lod) + 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; @@ -376,7 +376,7 @@ namespace vertex.uv[0] = uv.x; vertex.uv[1] = uv.y; - writer.AddVertex(vertex); + out.m_vertices.emplace_back(vertex); } } } @@ -389,29 +389,29 @@ namespace if (!surfs) return; - weightCollection.totalWeightCount = 0u; + auto totalWeightCount = 0u; for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++) { const auto& surface = surfs[surfIndex]; if (surface.vertList) { - weightCollection.totalWeightCount += surface.vertListCount; + totalWeightCount += surface.vertListCount; } if (surface.vertInfo.vertsBlend) { - weightCollection.totalWeightCount += surface.vertInfo.vertCount[0] * 1; - weightCollection.totalWeightCount += surface.vertInfo.vertCount[1] * 2; - weightCollection.totalWeightCount += surface.vertInfo.vertCount[2] * 3; - weightCollection.totalWeightCount += surface.vertInfo.vertCount[3] * 4; + 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 = std::make_unique(weightCollection.totalWeightCount); + weightCollection.weights.resize(totalWeightCount); } - void AddXModelVertexBoneWeights(AbstractXModelWriter& writer, const XModel* model, const unsigned lod, XModelVertexBoneWeightCollection& weightCollection) + void AddXModelVertexBoneWeights(XModelCommon& out, const XModel* model, const unsigned lod, XModelVertexBoneWeightCollection& weightCollection) { const auto* surfs = &model->surfs[model->lodInfo[lod].surfIndex]; const auto surfCount = model->lodInfo[lod].numsurfs; @@ -437,7 +437,7 @@ namespace for (auto vertListVertexOffset = 0u; vertListVertexOffset < vertList.vertCount; vertListVertexOffset++) { - writer.AddVertexBoneWeights(XModelVertexBoneWeights{boneWeightOffset, 1}); + out.m_vertex_bone_weights.emplace_back(boneWeightOffset, 1); } handledVertices += vertList.vertCount; } @@ -455,7 +455,7 @@ namespace vertsBlendOffset += 1; - writer.AddVertexBoneWeights(XModelVertexBoneWeights{boneWeightOffset, 1}); + out.m_vertex_bone_weights.emplace_back(boneWeightOffset, 1); } // 2 bone weights @@ -472,7 +472,7 @@ namespace vertsBlendOffset += 3; - writer.AddVertexBoneWeights(XModelVertexBoneWeights{boneWeightOffset, 2}); + out.m_vertex_bone_weights.emplace_back(boneWeightOffset, 2); } // 3 bone weights @@ -492,7 +492,7 @@ namespace vertsBlendOffset += 5; - writer.AddVertexBoneWeights(XModelVertexBoneWeights{boneWeightOffset, 3}); + out.m_vertex_bone_weights.emplace_back(boneWeightOffset, 3); } // 4 bone weights @@ -515,7 +515,7 @@ namespace vertsBlendOffset += 7; - writer.AddVertexBoneWeights(XModelVertexBoneWeights{boneWeightOffset, 4}); + out.m_vertex_bone_weights.emplace_back(boneWeightOffset, 4); } handledVertices += @@ -524,12 +524,12 @@ namespace for (; handledVertices < surface.vertCount; handledVertices++) { - writer.AddVertexBoneWeights(XModelVertexBoneWeights{nullptr, 0}); + out.m_vertex_bone_weights.emplace_back(nullptr, 0); } } } - void AddXModelFaces(AbstractXModelWriter& writer, const XModel* model, const unsigned lod) + 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; @@ -549,26 +549,26 @@ namespace face.vertexIndex[1] = tri[1] + surface.baseVertIndex; face.vertexIndex[2] = tri[2] + surface.baseVertIndex; face.objectIndex = static_cast(surfIndex); - writer.AddFace(face); + out.m_faces.emplace_back(face); } } } - void PopulateXModelWriter(const AssetDumpingContext& context, const unsigned lod, const XModel* model, AbstractXModelWriter& writer) + void PopulateXModelWriter(XModelCommon& out, const AssetDumpingContext& context, const unsigned lod, const XModel* model) { DistinctMapper materialMapper(model->numsurfs); XModelVertexBoneWeightCollection boneWeightCollection; AllocateXModelBoneWeights(model, lod, boneWeightCollection); - AddXModelBones(context, writer, model); - AddXModelMaterials(writer, materialMapper, model); - AddXModelObjects(writer, model, lod, materialMapper); - AddXModelVertices(writer, model, lod); - AddXModelVertexBoneWeights(writer, model, lod, boneWeightCollection); - AddXModelFaces(writer, model, lod); + AddXModelBones(out, context, model); + AddXModelMaterials(out, materialMapper, model); + AddXModelObjects(out, model, lod, materialMapper); + AddXModelVertices(out, model, lod); + AddXModelVertexBoneWeights(out, model, lod, boneWeightCollection); + AddXModelFaces(out, model, lod); } - void DumpXModelExportLod(const AssetDumpingContext& context, const XAssetInfo* asset, const unsigned lod) + 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")); @@ -576,13 +576,13 @@ namespace if (!assetFile) return; - const auto writer = XModelExportWriter::CreateWriterForVersion6(context.m_zone->m_game->GetShortName(), context.m_zone->m_name); - PopulateXModelWriter(context, lod, model, *writer); - - writer->Write(*assetFile); + const auto writer = xmodel_export::CreateWriterForVersion6(*assetFile, context.m_zone->m_game->GetShortName(), context.m_zone->m_name); + writer->Write(common); } - template void DumpGltfLod(const AssetDumpingContext& context, const XAssetInfo* asset, const unsigned lod, const std::string& extension) + 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)); @@ -592,9 +592,8 @@ namespace 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); - PopulateXModelWriter(context, lod, model, *writer); - writer->Write(*assetFile); + writer->Write(common); } void DumpXModelSurfs(const AssetDumpingContext& context, const XAssetInfo* asset) @@ -606,6 +605,9 @@ namespace 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: @@ -613,15 +615,15 @@ namespace break; case ObjWriting::Configuration_t::ModelOutputFormat_e::XMODEL_EXPORT: - DumpXModelExportLod(context, asset, currentLod); + DumpXModelExportLod(common, context, asset, currentLod); break; case ObjWriting::Configuration_t::ModelOutputFormat_e::GLTF: - DumpGltfLod(context, asset, currentLod, ".gltf"); + DumpGltfLod(common, context, asset, currentLod, ".gltf"); break; case ObjWriting::Configuration_t::ModelOutputFormat_e::GLB: - DumpGltfLod(context, asset, currentLod, ".glb"); + DumpGltfLod(common, context, asset, currentLod, ".glb"); break; default: diff --git a/src/ObjWriting/XModel/AbstractXModelWriter.cpp b/src/ObjWriting/XModel/AbstractXModelWriter.cpp deleted file mode 100644 index 726cde89..00000000 --- a/src/ObjWriting/XModel/AbstractXModelWriter.cpp +++ /dev/null @@ -1,33 +0,0 @@ -#include "AbstractXModelWriter.h" - -AbstractXModelWriter::AbstractXModelWriter() = default; - -void AbstractXModelWriter::AddObject(XModelObject object) -{ - m_objects.emplace_back(std::move(object)); -} - -void AbstractXModelWriter::AddBone(XModelBone bone) -{ - m_bones.emplace_back(std::move(bone)); -} - -void AbstractXModelWriter::AddMaterial(XModelMaterial material) -{ - m_materials.emplace_back(std::move(material)); -} - -void AbstractXModelWriter::AddVertex(XModelVertex vertex) -{ - m_vertices.emplace_back(vertex); -} - -void AbstractXModelWriter::AddVertexBoneWeights(XModelVertexBoneWeights vertexBoneWeights) -{ - m_vertex_bone_weights.emplace_back(vertexBoneWeights); -} - -void AbstractXModelWriter::AddFace(XModelFace face) -{ - m_faces.emplace_back(face); -} diff --git a/src/ObjWriting/XModel/AbstractXModelWriter.h b/src/ObjWriting/XModel/AbstractXModelWriter.h deleted file mode 100644 index e825d92a..00000000 --- a/src/ObjWriting/XModel/AbstractXModelWriter.h +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -#include "XModel/XModelCommon.h" - -#include - -class AbstractXModelWriter -{ -protected: - std::vector m_objects; - std::vector m_bones; - std::vector m_materials; - std::vector m_vertices; - std::vector m_vertex_bone_weights; - std::vector m_faces; - -public: - AbstractXModelWriter(); - - void AddObject(XModelObject object); - void AddBone(XModelBone bone); - void AddMaterial(XModelMaterial material); - void AddVertex(XModelVertex vertex); - void AddVertexBoneWeights(XModelVertexBoneWeights vertexBoneWeights); - void AddFace(XModelFace face); -}; diff --git a/src/ObjWriting/XModel/Export/XModelExportWriter.cpp b/src/ObjWriting/XModel/Export/XModelExportWriter.cpp index 6abbe100..356b0b51 100644 --- a/src/ObjWriting/XModel/Export/XModelExportWriter.cpp +++ b/src/ObjWriting/XModel/Export/XModelExportWriter.cpp @@ -6,25 +6,20 @@ #include #include -class XModelExportWriterBase : public XModelExportWriter +class XModelExportWriterBase : public XModelWriter { protected: - std::string m_game_name; - std::string m_zone_name; - - VertexMerger m_vertex_merger; - - void PrepareVertexMerger() + void PrepareVertexMerger(const XModelCommon& xmodel) { - m_vertex_merger = VertexMerger(m_vertices.size()); + m_vertex_merger = VertexMerger(xmodel.m_vertices.size()); auto vertexOffset = 0u; - for (const auto& vertex : m_vertices) + for (const auto& vertex : xmodel.m_vertices) { XModelVertexBoneWeights weights{nullptr, 0}; - if (vertexOffset < m_vertex_bone_weights.size()) - weights = m_vertex_bone_weights[vertexOffset]; + if (vertexOffset < xmodel.m_vertex_bone_weights.size()) + weights = xmodel.m_vertex_bone_weights[vertexOffset]; m_vertex_merger.Add(VertexMergerPos{vertex.coordinates[0], vertex.coordinates[1], vertex.coordinates[2], weights.weights, weights.weightCount}); @@ -32,103 +27,99 @@ protected: } } - void WriteHeader(std::ostream& stream, const int version) const + void WriteHeader(const int version) const { - stream << "// OpenAssetTools XMODEL_EXPORT File\n"; - stream << "// Game Origin: " << m_game_name << "\n"; - stream << "// Zone Origin: " << m_zone_name << "\n"; - stream << "MODEL\n"; - stream << "VERSION " << version << "\n"; - stream << "\n"; + m_stream << "// OpenAssetTools XMODEL_EXPORT File\n"; + m_stream << "// Game Origin: " << m_game_name << "\n"; + m_stream << "// Zone Origin: " << m_zone_name << "\n"; + m_stream << "MODEL\n"; + m_stream << "VERSION " << version << "\n"; + m_stream << "\n"; } - void WriteBones(std::ostream& stream) const + void WriteBones(const XModelCommon& xmodel) const { - stream << "NUMBONES " << m_bones.size() << "\n"; + m_stream << "NUMBONES " << xmodel.m_bones.size() << "\n"; size_t boneNum = 0u; - for (const auto& bone : m_bones) + for (const auto& bone : xmodel.m_bones) { - stream << "BONE " << boneNum << " "; + m_stream << "BONE " << boneNum << " "; if (bone.parentIndex < 0) - stream << "-1"; + m_stream << "-1"; else - stream << bone.parentIndex; + m_stream << bone.parentIndex; - stream << " \"" << bone.name << "\"\n"; + m_stream << " \"" << bone.name << "\"\n"; boneNum++; } - stream << "\n"; + m_stream << "\n"; boneNum = 0u; - for (const auto& bone : m_bones) + for (const auto& bone : xmodel.m_bones) { - stream << "BONE " << boneNum << "\n"; - stream << "OFFSET "; - stream << std::setprecision(6) << std::fixed << bone.globalOffset[0] << ", " << std::setprecision(6) << std::fixed << bone.globalOffset[1] << ", " - << std::setprecision(6) << std::fixed << bone.globalOffset[2] << "\n"; - - stream << "SCALE "; - stream << std::setprecision(6) << std::fixed << bone.scale[0] << ", " << std::setprecision(6) << std::fixed << bone.scale[1] << ", " - << std::setprecision(6) << std::fixed << bone.scale[2] << "\n"; + m_stream << "BONE " << boneNum << "\n"; + m_stream << std::format("OFFSET {:.6f}, {:.6f}, {:.6f}\n", bone.globalOffset[0], bone.globalOffset[1], bone.globalOffset[2]); + m_stream << std::format("SCALE {:.6f}, {:.6f}, {:.6f}\n", bone.scale[0], bone.scale[1], bone.scale[2]); const Matrix32 mat = bone.globalRotation.ToMatrix(); - stream << "X " << std::setprecision(6) << std::fixed << mat.m_data[0][0] << ", " << std::setprecision(6) << std::fixed << mat.m_data[1][0] << ", " - << std::setprecision(6) << std::fixed << mat.m_data[2][0] << "\n"; - stream << "Y " << std::setprecision(6) << std::fixed << mat.m_data[0][1] << ", " << std::setprecision(6) << std::fixed << mat.m_data[1][1] << ", " - << std::setprecision(6) << std::fixed << mat.m_data[2][1] << "\n"; - stream << "Z " << std::setprecision(6) << std::fixed << mat.m_data[0][2] << ", " << std::setprecision(6) << std::fixed << mat.m_data[1][2] << ", " - << std::setprecision(6) << std::fixed << mat.m_data[2][2] << "\n"; - stream << "\n"; + m_stream << std::format("X {:.6f}, {:.6f}, {:.6f}\n", mat.m_data[0][0], mat.m_data[1][0], mat.m_data[2][0]); + m_stream << std::format("Y {:.6f}, {:.6f}, {:.6f}\n", mat.m_data[0][1], mat.m_data[1][1], mat.m_data[2][1]); + m_stream << std::format("Z {:.6f}, {:.6f}, {:.6f}\n", mat.m_data[0][2], mat.m_data[1][2], mat.m_data[2][2]); + m_stream << '\n'; boneNum++; } } - XModelExportWriterBase(std::string gameName, std::string zoneName) - : m_game_name(std::move(gameName)), + XModelExportWriterBase(std::ostream& stream, std::string gameName, std::string zoneName) + : m_stream(stream), + m_game_name(std::move(gameName)), m_zone_name(std::move(zoneName)) { } + + std::ostream& m_stream; + std::string m_game_name; + std::string m_zone_name; + VertexMerger m_vertex_merger; }; class XModelExportWriter6 final : public XModelExportWriterBase { - void WriteVertices(std::ostream& stream) const + void WriteVertices(const XModelCommon& xmodel) const { const auto& distinctVertexValues = m_vertex_merger.GetDistinctValues(); - stream << "NUMVERTS " << distinctVertexValues.size() << "\n"; + m_stream << "NUMVERTS " << distinctVertexValues.size() << "\n"; size_t vertexNum = 0u; for (const auto& vertexPos : distinctVertexValues) { - stream << "VERT " << vertexNum << "\n"; - stream << "OFFSET "; - stream << std::setprecision(6) << std::fixed << vertexPos.x << ", " << std::setprecision(6) << std::fixed << vertexPos.y << ", " - << std::setprecision(6) << std::fixed << vertexPos.z << "\n"; - stream << "BONES " << vertexPos.weightCount << "\n"; + m_stream << "VERT " << vertexNum << "\n"; + m_stream << std::format("OFFSET {:.6f}, {:.6f}, {:.6f}\n", vertexPos.x, vertexPos.y, vertexPos.z); + m_stream << "BONES " << vertexPos.weightCount << "\n"; for (auto weightIndex = 0u; weightIndex < vertexPos.weightCount; weightIndex++) { - stream << "BONE " << vertexPos.weights[weightIndex].boneIndex << " " << std::setprecision(6) << std::fixed - << vertexPos.weights[weightIndex].weight << "\n"; + const auto& weight = vertexPos.weights[weightIndex]; + m_stream << std::format("BONE {} {:.6f}\n", weight.boneIndex, weight.weight); } - stream << "\n"; + m_stream << "\n"; vertexNum++; } } - static void WriteFaceVertex(std::ostream& stream, const size_t index, const XModelVertex& vertex) + void WriteFaceVertex(const size_t index, const XModelVertex& vertex) const { - stream << "VERT " << index << "\n"; - stream << "NORMAL " << std::setprecision(6) << std::fixed << vertex.normal[0] << " " << std::setprecision(6) << std::fixed << vertex.normal[1] << " " - << std::setprecision(6) << std::fixed << vertex.normal[2] << "\n"; - stream << "COLOR " << std::setprecision(6) << std::fixed << vertex.color[0] << " " << std::setprecision(6) << std::fixed << vertex.color[1] << " " - << std::setprecision(6) << std::fixed << vertex.color[2] << " " << std::setprecision(6) << std::fixed << vertex.color[3] << "\n"; - stream << "UV 1 " << std::setprecision(6) << std::fixed << vertex.uv[0] << " " << std::setprecision(6) << std::fixed << vertex.uv[1] << "\n"; + m_stream << "VERT " << index << "\n"; + m_stream << std::format("NORMAL {:.6f} {:.6f} {:.6f}\n", vertex.normal[0], vertex.normal[1], vertex.normal[2]); + m_stream << "NORMAL " << std::setprecision(6) << std::fixed << vertex.normal[0] << " " << std::setprecision(6) << std::fixed << vertex.normal[1] << " " + << std::setprecision(6) << std::fixed << vertex.normal[2] << "\n"; + m_stream << std::format("COLOR {:.6f} {:.6f} {:.6f} {:.6f}\n", vertex.color[0], vertex.color[1], vertex.color[2], vertex.color[3]); + m_stream << std::format("UV 1 {:.6f} {:.6f}\n", vertex.uv[0], vertex.uv[1]); } - void WriteFaces(std::ostream& stream) const + void WriteFaces(const XModelCommon& xmodel) const { - stream << "NUMFACES " << m_faces.size() << "\n"; - for (const auto& face : m_faces) + m_stream << "NUMFACES " << xmodel.m_faces.size() << "\n"; + for (const auto& face : xmodel.m_faces) { const size_t distinctPositions[3]{ m_vertex_merger.GetDistinctPositionByInputPosition(face.vertexIndex[0]), @@ -136,87 +127,97 @@ class XModelExportWriter6 final : public XModelExportWriterBase m_vertex_merger.GetDistinctPositionByInputPosition(face.vertexIndex[2]), }; - const XModelVertex& v0 = m_vertices[face.vertexIndex[0]]; - const XModelVertex& v1 = m_vertices[face.vertexIndex[1]]; - const XModelVertex& v2 = m_vertices[face.vertexIndex[2]]; + const XModelVertex& v0 = xmodel.m_vertices[face.vertexIndex[0]]; + const XModelVertex& v1 = xmodel.m_vertices[face.vertexIndex[1]]; + const XModelVertex& v2 = xmodel.m_vertices[face.vertexIndex[2]]; - stream << "TRI " << face.objectIndex << " " << m_objects[face.objectIndex].materialIndex << " 0 0\n"; - WriteFaceVertex(stream, distinctPositions[0], v0); - WriteFaceVertex(stream, distinctPositions[1], v1); - WriteFaceVertex(stream, distinctPositions[2], v2); - stream << "\n"; + m_stream << "TRI " << face.objectIndex << " " << xmodel.m_objects[face.objectIndex].materialIndex << " 0 0\n"; + WriteFaceVertex(distinctPositions[0], v0); + WriteFaceVertex(distinctPositions[1], v1); + WriteFaceVertex(distinctPositions[2], v2); + m_stream << "\n"; } } - void WriteObjects(std::ostream& stream) const + void WriteObjects(const XModelCommon& xmodel) const { - stream << "NUMOBJECTS " << m_objects.size() << "\n"; + m_stream << "NUMOBJECTS " << xmodel.m_objects.size() << "\n"; size_t objectNum = 0u; - for (const auto& object : m_objects) + for (const auto& object : xmodel.m_objects) { - stream << "OBJECT " << objectNum << " \"" << object.name << "\"\n"; + m_stream << "OBJECT " << objectNum << " \"" << object.name << "\"\n"; objectNum++; } - stream << "\n"; + m_stream << "\n"; } - void WriteMaterials(std::ostream& stream) const + void WriteMaterials(const XModelCommon& xmodel) const { - stream << "NUMMATERIALS " << m_materials.size() << "\n"; + m_stream << "NUMMATERIALS " << xmodel.m_materials.size() << "\n"; size_t materialNum = 0u; - for (const auto& material : m_materials) + for (const auto& material : xmodel.m_materials) { const auto colorMapPath = "../images/" + material.colorMapName + ".dds"; - stream << "MATERIAL " << materialNum << " \"" << material.name << "\" \"" << material.materialTypeName << "\" \"" << colorMapPath << "\"\n"; - stream << "COLOR " << std::setprecision(6) << std::fixed << material.color[0] << " " << std::setprecision(6) << std::fixed << material.color[1] - << " " << std::setprecision(6) << std::fixed << material.color[2] << " " << std::setprecision(6) << std::fixed << material.color[3] << "\n"; - stream << "TRANSPARENCY " << std::setprecision(6) << std::fixed << material.transparency[0] << " " << std::setprecision(6) << std::fixed - << material.transparency[1] << " " << std::setprecision(6) << std::fixed << material.transparency[2] << " " << std::setprecision(6) - << std::fixed << material.transparency[3] << "\n"; - stream << "AMBIENTCOLOR " << std::setprecision(6) << std::fixed << material.ambientColor[0] << " " << std::setprecision(6) << std::fixed - << material.ambientColor[1] << " " << std::setprecision(6) << std::fixed << material.ambientColor[2] << " " << std::setprecision(6) - << std::fixed << material.ambientColor[3] << "\n"; - stream << "INCANDESCENCE " << std::setprecision(6) << std::fixed << material.incandescence[0] << " " << std::setprecision(6) << std::fixed - << material.incandescence[1] << " " << std::setprecision(6) << std::fixed << material.incandescence[2] << " " << std::setprecision(6) - << std::fixed << material.incandescence[3] << "\n"; - stream << "COEFFS " << std::setprecision(6) << std::fixed << material.coeffs[0] << " " << std::setprecision(6) << std::fixed << material.coeffs[1] - << "\n"; - stream << "GLOW " << std::setprecision(6) << std::fixed << material.glow.x << " " << material.glow.y << "\n"; - stream << "REFRACTIVE " << material.refractive.x << " " << std::setprecision(6) << std::fixed << material.refractive.y << "\n"; - stream << "SPECULARCOLOR " << std::setprecision(6) << std::fixed << material.specularColor[0] << " " << std::setprecision(6) << std::fixed - << material.specularColor[1] << " " << std::setprecision(6) << std::fixed << material.specularColor[2] << " " << std::setprecision(6) - << std::fixed << material.specularColor[3] << "\n"; - stream << "REFLECTIVECOLOR " << std::setprecision(6) << std::fixed << material.reflectiveColor[0] << " " << std::setprecision(6) << std::fixed - << material.reflectiveColor[1] << " " << std::setprecision(6) << std::fixed << material.reflectiveColor[2] << " " << std::setprecision(6) - << std::fixed << material.reflectiveColor[3] << "\n"; - stream << "REFLECTIVE " << material.reflective.x << " " << std::setprecision(6) << std::fixed << material.reflective.y << "\n"; - stream << "BLINN " << std::setprecision(6) << std::fixed << material.blinn[0] << " " << std::setprecision(6) << std::fixed << material.blinn[1] - << "\n"; - stream << "PHONG " << std::setprecision(6) << std::fixed << material.phong << "\n"; - stream << "\n"; + m_stream << "MATERIAL " << materialNum << " \"" << material.name << "\" \"" << material.materialTypeName << "\" \"" << colorMapPath << "\"\n"; + m_stream << std::format("COLOR {:.6f} {:.6f} {:.6f} {:.6f}\n", material.color[0], material.color[1], material.color[2], material.color[3]); + m_stream << std::format("TRANSPARENCY {:.6f} {:.6f} {:.6f} {:.6f}\n", + material.transparency[0], + material.transparency[1], + material.transparency[2], + material.transparency[3]); + m_stream << std::format("AMBIENTCOLOR {:.6f} {:.6f} {:.6f} {:.6f}\n", + material.ambientColor[0], + material.ambientColor[1], + material.ambientColor[2], + material.ambientColor[3]); + m_stream << std::format("INCANDESCENCE {:.6f} {:.6f} {:.6f} {:.6f}\n", + material.incandescence[0], + material.incandescence[1], + material.incandescence[2], + material.incandescence[3]); + m_stream << std::format("COEFFS {:.6f} {:.6f}\n", material.coeffs[0], material.coeffs[1]); + m_stream << std::format("GLOW {:.6f} {}\n", material.glow.x, material.glow.y); + m_stream << std::format("REFRACTIVE {} {:.6f}\n", material.refractive.x, material.refractive.y); + m_stream << std::format("SPECULARCOLOR {:.6f} {:.6f} {:.6f} {:.6f}\n", + material.specularColor[0], + material.specularColor[1], + material.specularColor[2], + material.specularColor[3]); + m_stream << std::format("REFLECTIVECOLOR {:.6f} {:.6f} {:.6f} {:.6f}\n", + material.reflectiveColor[0], + material.reflectiveColor[1], + material.reflectiveColor[2], + material.reflectiveColor[3]); + m_stream << std::format("REFLECTIVE {} {:.6f}\n", material.reflective.x, material.reflective.y); + m_stream << std::format("BLINN {:.6f} {:.6f}\n", material.blinn[0], material.blinn[1]); + m_stream << std::format("PHONG {:.6f}\n", material.phong); + m_stream << '\n'; materialNum++; } } public: - XModelExportWriter6(std::string gameName, std::string zoneName) - : XModelExportWriterBase(std::move(gameName), std::move(zoneName)) + XModelExportWriter6(std::ostream& stream, std::string gameName, std::string zoneName) + : XModelExportWriterBase(stream, std::move(gameName), std::move(zoneName)) { } - void Write(std::ostream& stream) override + void Write(const XModelCommon& xmodel) override { - PrepareVertexMerger(); - WriteHeader(stream, 6); - WriteBones(stream); - WriteVertices(stream); - WriteFaces(stream); - WriteObjects(stream); - WriteMaterials(stream); + PrepareVertexMerger(xmodel); + WriteHeader(6); + WriteBones(xmodel); + WriteVertices(xmodel); + WriteFaces(xmodel); + WriteObjects(xmodel); + WriteMaterials(xmodel); } }; -std::unique_ptr XModelExportWriter::CreateWriterForVersion6(std::string gameName, std::string zoneName) +namespace xmodel_export { - return std::make_unique(std::move(gameName), std::move(zoneName)); -} + std::unique_ptr CreateWriterForVersion6(std::ostream& stream, std::string gameName, std::string zoneName) + { + return std::make_unique(stream, std::move(gameName), std::move(zoneName)); + } +} // namespace xmodel_export diff --git a/src/ObjWriting/XModel/Export/XModelExportWriter.h b/src/ObjWriting/XModel/Export/XModelExportWriter.h index 175bc1ef..b0d808f4 100644 --- a/src/ObjWriting/XModel/Export/XModelExportWriter.h +++ b/src/ObjWriting/XModel/Export/XModelExportWriter.h @@ -1,21 +1,11 @@ #pragma once -#include "XModel/AbstractXModelWriter.h" +#include "XModel/XModelWriter.h" #include #include -class XModelExportWriter : public AbstractXModelWriter +namespace xmodel_export { -public: - XModelExportWriter() = default; - virtual ~XModelExportWriter() = default; - XModelExportWriter(const XModelExportWriter& other) = default; - XModelExportWriter(XModelExportWriter&& other) noexcept = default; - XModelExportWriter& operator=(const XModelExportWriter& other) = default; - XModelExportWriter& operator=(XModelExportWriter&& other) noexcept = default; - - virtual void Write(std::ostream& stream) = 0; - - static std::unique_ptr CreateWriterForVersion6(std::string gameName, std::string zoneName); -}; + std::unique_ptr CreateWriterForVersion6(std::ostream& stream, std::string gameName, std::string zoneName); +} diff --git a/src/ObjWriting/XModel/Gltf/GltfWriter.cpp b/src/ObjWriting/XModel/Gltf/GltfWriter.cpp index 17cdbf25..05b22839 100644 --- a/src/ObjWriting/XModel/Gltf/GltfWriter.cpp +++ b/src/ObjWriting/XModel/Gltf/GltfWriter.cpp @@ -24,15 +24,15 @@ namespace { } - void Write(std::ostream& stream) override + void Write(const XModelCommon& xmodel) override { JsonRoot gltf; CreateJsonAsset(gltf.asset); - CreateMeshNode(gltf); - CreateMesh(gltf); - CreateSkeletonNodes(gltf); - CreateSkin(gltf); - CreateScene(gltf); + CreateMeshNode(gltf, xmodel); + CreateMesh(gltf, xmodel); + CreateSkeletonNodes(gltf, xmodel); + CreateSkin(gltf, xmodel); + CreateScene(gltf, xmodel); const json jRoot = gltf; m_output->EmitJson(jRoot); @@ -46,7 +46,7 @@ namespace asset.generator = GLTF_GENERATOR; } - void CreateMeshNode(JsonRoot& gltf) const + static void CreateMeshNode(JsonRoot& gltf, const XModelCommon& xmodel) { JsonNode meshNode; @@ -54,7 +54,7 @@ namespace meshNode.mesh = 0u; // Only add skin if the model has bones - if (!m_bones.empty()) + if (!xmodel.m_bones.empty()) { // We only have one skin meshNode.skin = 0u; @@ -66,7 +66,7 @@ namespace gltf.nodes->emplace_back(std::move(meshNode)); } - void CreateMesh(JsonRoot& gltf) const + static void CreateMesh(JsonRoot& gltf, const XModelCommon& xmodel) { if (!gltf.meshes.has_value()) gltf.meshes.emplace(); @@ -76,19 +76,19 @@ namespace gltf.meshes->emplace_back(std::move(mesh)); } - void CreateSkeletonNodes(JsonRoot& gltf) const + static void CreateSkeletonNodes(JsonRoot& gltf, const XModelCommon& xmodel) { - if (m_bones.empty()) + if (xmodel.m_bones.empty()) return; if (!gltf.nodes.has_value()) gltf.nodes.emplace(); - const auto boneCount = m_bones.size(); + const auto boneCount = xmodel.m_bones.size(); for (auto boneIndex = 0u; boneIndex < boneCount; boneIndex++) { JsonNode boneNode; - const auto& bone = m_bones[boneIndex]; + const auto& bone = xmodel.m_bones[boneIndex]; boneNode.name = bone.name; boneNode.translation = std::to_array(bone.globalOffset); @@ -97,7 +97,7 @@ namespace std::vector children; for (auto maybeChildIndex = 0u; maybeChildIndex < boneCount; maybeChildIndex++) { - if (m_bones[maybeChildIndex].parentIndex == static_cast(boneIndex)) + if (xmodel.m_bones[maybeChildIndex].parentIndex == static_cast(boneIndex)) children.emplace_back(boneIndex + NODE_FIRST_INDEX_BONES); } if (!children.empty()) @@ -107,9 +107,9 @@ namespace } } - void CreateSkin(JsonRoot& gltf) const + static void CreateSkin(JsonRoot& gltf, const XModelCommon& xmodel) { - if (m_bones.empty()) + if (xmodel.m_bones.empty()) return; if (!gltf.skins.has_value()) @@ -117,7 +117,7 @@ namespace JsonSkin skin; - const auto boneCount = m_bones.size(); + const auto boneCount = xmodel.m_bones.size(); skin.joints.reserve(boneCount); for (auto boneIndex = 0u; boneIndex < boneCount; boneIndex++) @@ -126,12 +126,12 @@ namespace gltf.skins->emplace_back(std::move(skin)); } - void CreateScene(JsonRoot& gltf) const + void CreateScene(JsonRoot& gltf, const XModelCommon& xmodel) const { JsonScene scene; // Only add skin if the model has bones - if (m_bones.empty()) + if (xmodel.m_bones.empty()) scene.nodes.emplace_back(NODE_INDEX_MESH); else scene.nodes.emplace_back(m_skeleton_node); diff --git a/src/ObjWriting/XModel/Gltf/GltfWriter.h b/src/ObjWriting/XModel/Gltf/GltfWriter.h index 3d69d77e..0da26d71 100644 --- a/src/ObjWriting/XModel/Gltf/GltfWriter.h +++ b/src/ObjWriting/XModel/Gltf/GltfWriter.h @@ -1,26 +1,24 @@ #pragma once #include "GltfOutput.h" -#include "XModel/AbstractXModelWriter.h" #include "XModel/Gltf/JsonGltf.h" +#include "XModel/XModelWriter.h" #include #include namespace gltf { - class Writer : public AbstractXModelWriter + class Writer : public XModelWriter { public: Writer() = default; - virtual ~Writer() = default; + ~Writer() override = default; Writer(const Writer& other) = default; Writer(Writer&& other) noexcept = default; Writer& operator=(const Writer& other) = default; Writer& operator=(Writer&& other) noexcept = default; - virtual void Write(std::ostream& stream) = 0; - static std::unique_ptr CreateWriter(const Output* output, std::string gameName, std::string zoneName); }; } // namespace gltf diff --git a/src/ObjWriting/XModel/XModelWriter.h b/src/ObjWriting/XModel/XModelWriter.h new file mode 100644 index 00000000..80324ed9 --- /dev/null +++ b/src/ObjWriting/XModel/XModelWriter.h @@ -0,0 +1,18 @@ +#pragma once + +#include "XModel/XModelCommon.h" + +#include + +class XModelWriter +{ +public: + XModelWriter() = default; + virtual ~XModelWriter() = default; + XModelWriter(const XModelWriter& other) = default; + XModelWriter(XModelWriter&& other) noexcept = default; + XModelWriter& operator=(const XModelWriter& other) = default; + XModelWriter& operator=(XModelWriter&& other) noexcept = default; + + virtual void Write(const XModelCommon& xmodel) = 0; +}; From 2497a9f22851aa8f653e66514a81cbcc8407f97f Mon Sep 17 00:00:00 2001 From: Jan Date: Sun, 28 Apr 2024 14:36:02 +0200 Subject: [PATCH 12/25] fix: use after free when dumping xmodel vertex weights --- .../Game/IW3/AssetDumpers/AssetDumperXModel.cpp | 8 ++++---- .../Game/IW4/AssetDumpers/AssetDumperXModel.cpp | 8 ++++---- .../Game/IW5/AssetDumpers/AssetDumperXModel.cpp | 8 ++++---- src/ObjWriting/Game/T5/AssetDumpers/AssetDumperXModel.cpp | 8 ++++---- src/ObjWriting/Game/T6/AssetDumpers/AssetDumperXModel.cpp | 8 ++++---- 5 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperXModel.cpp b/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperXModel.cpp index 82b1ed39..d720515d 100644 --- a/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperXModel.cpp +++ b/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperXModel.cpp @@ -393,10 +393,11 @@ namespace weightCollection.weights.resize(totalWeightCount); } - void AddXModelVertexBoneWeights(XModelCommon& out, const XModel* model, const unsigned lod, XModelVertexBoneWeightCollection& weightCollection) + 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; size_t weightOffset = 0u; @@ -533,14 +534,13 @@ namespace void PopulateXModelWriter(XModelCommon& out, const AssetDumpingContext& context, const unsigned lod, const XModel* model) { DistinctMapper materialMapper(model->numsurfs); - XModelVertexBoneWeightCollection boneWeightCollection; - AllocateXModelBoneWeights(model, lod, boneWeightCollection); + AllocateXModelBoneWeights(model, lod, out.m_bone_weight_data); AddXModelBones(out, context, model); AddXModelMaterials(out, materialMapper, model); AddXModelObjects(out, model, lod, materialMapper); AddXModelVertices(out, model, lod); - AddXModelVertexBoneWeights(out, model, lod, boneWeightCollection); + AddXModelVertexBoneWeights(out, model, lod); AddXModelFaces(out, model, lod); } diff --git a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperXModel.cpp b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperXModel.cpp index 419ce4e1..a1064a0a 100644 --- a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperXModel.cpp +++ b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperXModel.cpp @@ -390,8 +390,9 @@ namespace weightCollection.weights.resize(totalWeightCount); } - void AddXModelVertexBoneWeights(XModelCommon& out, const XModelSurfs* modelSurfs, XModelVertexBoneWeightCollection& weightCollection) + void AddXModelVertexBoneWeights(XModelCommon& out, const XModelSurfs* modelSurfs) { + auto& weightCollection = out.m_bone_weight_data; size_t weightOffset = 0u; for (auto surfIndex = 0u; surfIndex < modelSurfs->numsurfs; surfIndex++) @@ -526,14 +527,13 @@ namespace const auto* modelSurfs = model->lodInfo[lod].modelSurfs; DistinctMapper materialMapper(model->numsurfs); - XModelVertexBoneWeightCollection boneWeightCollection; - AllocateXModelBoneWeights(modelSurfs, boneWeightCollection); + AllocateXModelBoneWeights(modelSurfs, out.m_bone_weight_data); AddXModelBones(out, context, model); AddXModelMaterials(out, materialMapper, model); AddXModelObjects(out, modelSurfs, materialMapper, model->lodInfo[lod].surfIndex); AddXModelVertices(out, modelSurfs); - AddXModelVertexBoneWeights(out, modelSurfs, boneWeightCollection); + AddXModelVertexBoneWeights(out, modelSurfs); AddXModelFaces(out, modelSurfs); } diff --git a/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperXModel.cpp b/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperXModel.cpp index 1be3b9f2..a11240e8 100644 --- a/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperXModel.cpp +++ b/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperXModel.cpp @@ -389,8 +389,9 @@ namespace weightCollection.weights.resize(totalWeightCount); } - void AddXModelVertexBoneWeights(XModelCommon& out, const XModelSurfs* modelSurfs, XModelVertexBoneWeightCollection& weightCollection) + void AddXModelVertexBoneWeights(XModelCommon& out, const XModelSurfs* modelSurfs) { + auto& weightCollection = out.m_bone_weight_data; size_t weightOffset = 0u; for (auto surfIndex = 0u; surfIndex < modelSurfs->numsurfs; surfIndex++) @@ -525,14 +526,13 @@ namespace const auto* modelSurfs = model->lodInfo[lod].modelSurfs; DistinctMapper materialMapper(model->numsurfs); - XModelVertexBoneWeightCollection boneWeightCollection; - AllocateXModelBoneWeights(modelSurfs, boneWeightCollection); + AllocateXModelBoneWeights(modelSurfs, out.m_bone_weight_data); AddXModelBones(out, context, model); AddXModelMaterials(out, materialMapper, model); AddXModelObjects(out, modelSurfs, materialMapper, model->lodInfo[lod].surfIndex); AddXModelVertices(out, modelSurfs); - AddXModelVertexBoneWeights(out, modelSurfs, boneWeightCollection); + AddXModelVertexBoneWeights(out, modelSurfs); AddXModelFaces(out, modelSurfs); } diff --git a/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperXModel.cpp b/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperXModel.cpp index a77887a7..82d59797 100644 --- a/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperXModel.cpp +++ b/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperXModel.cpp @@ -393,10 +393,11 @@ namespace weightCollection.weights.resize(totalWeightCount); } - void AddXModelVertexBoneWeights(XModelCommon& out, const XModel* model, const unsigned lod, XModelVertexBoneWeightCollection& weightCollection) + 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; size_t weightOffset = 0u; @@ -533,14 +534,13 @@ namespace void PopulateXModelWriter(XModelCommon& out, const AssetDumpingContext& context, const unsigned lod, const XModel* model) { DistinctMapper materialMapper(model->numsurfs); - XModelVertexBoneWeightCollection boneWeightCollection; - AllocateXModelBoneWeights(model, lod, boneWeightCollection); + AllocateXModelBoneWeights(model, lod, out.m_bone_weight_data); AddXModelBones(out, context, model); AddXModelMaterials(out, materialMapper, model); AddXModelObjects(out, model, lod, materialMapper); AddXModelVertices(out, model, lod); - AddXModelVertexBoneWeights(out, model, lod, boneWeightCollection); + AddXModelVertexBoneWeights(out, model, lod); AddXModelFaces(out, model, lod); } diff --git a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperXModel.cpp b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperXModel.cpp index bfeaa64c..526dfa68 100644 --- a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperXModel.cpp +++ b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperXModel.cpp @@ -411,10 +411,11 @@ namespace weightCollection.weights.resize(totalWeightCount); } - void AddXModelVertexBoneWeights(XModelCommon& out, const XModel* model, const unsigned lod, XModelVertexBoneWeightCollection& weightCollection) + 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; if (!surfs) return; @@ -557,14 +558,13 @@ namespace void PopulateXModelWriter(XModelCommon& out, const AssetDumpingContext& context, const unsigned lod, const XModel* model) { DistinctMapper materialMapper(model->numsurfs); - XModelVertexBoneWeightCollection boneWeightCollection; - AllocateXModelBoneWeights(model, lod, boneWeightCollection); + AllocateXModelBoneWeights(model, lod, out.m_bone_weight_data); AddXModelBones(out, context, model); AddXModelMaterials(out, materialMapper, model); AddXModelObjects(out, model, lod, materialMapper); AddXModelVertices(out, model, lod); - AddXModelVertexBoneWeights(out, model, lod, boneWeightCollection); + AddXModelVertexBoneWeights(out, model, lod); AddXModelFaces(out, model, lod); } From 45684ac8281b84049b81d9ec117fe0c1e5e2c71b Mon Sep 17 00:00:00 2001 From: Jan Date: Sun, 28 Apr 2024 14:37:06 +0200 Subject: [PATCH 13/25] fix: accidentally dumping NORMAL twice for xmodel export --- src/ObjWriting/XModel/Export/XModelExportWriter.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/ObjWriting/XModel/Export/XModelExportWriter.cpp b/src/ObjWriting/XModel/Export/XModelExportWriter.cpp index 356b0b51..e872d602 100644 --- a/src/ObjWriting/XModel/Export/XModelExportWriter.cpp +++ b/src/ObjWriting/XModel/Export/XModelExportWriter.cpp @@ -110,8 +110,6 @@ class XModelExportWriter6 final : public XModelExportWriterBase { m_stream << "VERT " << index << "\n"; m_stream << std::format("NORMAL {:.6f} {:.6f} {:.6f}\n", vertex.normal[0], vertex.normal[1], vertex.normal[2]); - m_stream << "NORMAL " << std::setprecision(6) << std::fixed << vertex.normal[0] << " " << std::setprecision(6) << std::fixed << vertex.normal[1] << " " - << std::setprecision(6) << std::fixed << vertex.normal[2] << "\n"; m_stream << std::format("COLOR {:.6f} {:.6f} {:.6f} {:.6f}\n", vertex.color[0], vertex.color[1], vertex.color[2], vertex.color[3]); m_stream << std::format("UV 1 {:.6f} {:.6f}\n", vertex.uv[0], vertex.uv[1]); } From a39e993cc6b7c2835461e3abf8d6b04141c5c241 Mon Sep 17 00:00:00 2001 From: Jan Date: Sun, 28 Apr 2024 14:37:21 +0200 Subject: [PATCH 14/25] chore: use XModelCommon for dumping obj to reduce code duplication --- src/ObjCommon/XModel/XModelCommon.h | 17 +- .../IW3/AssetDumpers/AssetDumperXModel.cpp | 192 +++------- .../IW4/AssetDumpers/AssetDumperXModel.cpp | 208 +++-------- .../IW5/AssetDumpers/AssetDumperXModel.cpp | 207 +++-------- .../T5/AssetDumpers/AssetDumperXModel.cpp | 192 +++------- .../T6/AssetDumpers/AssetDumperXModel.cpp | 192 +++------- .../XModel/Export/XModelExportWriter.cpp | 41 ++- src/ObjWriting/XModel/Obj/ObjWriter.cpp | 346 +++++++++++------- src/ObjWriting/XModel/Obj/ObjWriter.h | 47 +-- 9 files changed, 477 insertions(+), 965 deletions(-) diff --git a/src/ObjCommon/XModel/XModelCommon.h b/src/ObjCommon/XModel/XModelCommon.h index a8ee49b2..7f1f0b44 100644 --- a/src/ObjCommon/XModel/XModelCommon.h +++ b/src/ObjCommon/XModel/XModelCommon.h @@ -6,12 +6,6 @@ #include #include -struct XModelObject -{ - std::string name; - int materialIndex; -}; - struct XModelBone { std::string name; @@ -51,7 +45,6 @@ struct XModelVertex struct XModelFace { int vertexIndex[3]; - int objectIndex; }; struct XModelMaterial @@ -88,10 +81,19 @@ struct XModelMaterial float blinn[2]; float phong; std::string colorMapName; + std::string normalMapName; + std::string specularMapName; void ApplyDefaults(); }; +struct XModelObject +{ + std::string name; + int materialIndex; + std::vector m_faces; +}; + struct XModelCommon { std::vector m_objects; @@ -99,7 +101,6 @@ struct XModelCommon std::vector m_materials; std::vector m_vertices; std::vector m_vertex_bone_weights; - std::vector m_faces; XModelVertexBoneWeightCollection m_bone_weight_data; }; diff --git a/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperXModel.cpp b/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperXModel.cpp index d720515d..ee73a0b6 100644 --- a/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperXModel.cpp +++ b/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperXModel.cpp @@ -103,148 +103,6 @@ namespace return potentialTextureDefs[0]->u.image; } - void AddObjMaterials(ObjWriter& writer, DistinctMapper& materialMapper, const XModel* model) - { - if (!model->materialHandles) - return; - - for (auto surfIndex = 0u; surfIndex < model->numsurfs; surfIndex++) - { - Material* material = model->materialHandles[surfIndex]; - if (!materialMapper.Add(material)) - continue; - - MtlMaterial mtl; - mtl.materialName = std::string(material->info.name); - - GfxImage* colorMap = GetMaterialColorMap(material); - GfxImage* normalMap = GetMaterialNormalMap(material); - GfxImage* specularMap = GetMaterialSpecularMap(material); - - if (colorMap != nullptr) - mtl.colorMapName = colorMap->name; - if (normalMap != nullptr) - mtl.normalMapName = normalMap->name; - if (specularMap != nullptr) - mtl.specularMapName = specularMap->name; - - writer.AddMaterial(std::move(mtl)); - } - } - - void AddObjObjects(ObjWriter& writer, const DistinctMapper& materialMapper, const XModel* model, const unsigned lod) - { - const auto surfCount = model->lodInfo[lod].numsurfs; - const auto baseSurfIndex = model->lodInfo[lod].surfIndex; - - for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++) - { - ObjObject object; - object.name = std::format("surf{}", surfIndex); - object.materialIndex = static_cast(materialMapper.GetDistinctPositionByInputPosition(surfIndex + baseSurfIndex)); - - writer.AddObject(std::move(object)); - } - } - - void AddObjVertices(ObjWriter& writer, const XModel* model, const unsigned lod) - { - const auto* surfs = &model->surfs[model->lodInfo[lod].surfIndex]; - const auto surfCount = model->lodInfo[lod].numsurfs; - - for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++) - { - const auto& surface = surfs[surfIndex]; - - for (auto vertexIndex = 0u; vertexIndex < surface.vertCount; vertexIndex++) - { - const auto& v = surface.verts0[vertexIndex]; - vec2_t uv; - vec3_t normalVec; - - Common::Vec2UnpackTexCoords(v.texCoord, &uv); - Common::Vec3UnpackUnitVec(v.normal, &normalVec); - - ObjVertex objVertex{}; - ObjNormal objNormal{}; - ObjUv objUv{}; - objVertex.coordinates[0] = v.xyz[0]; - objVertex.coordinates[1] = v.xyz[2]; - objVertex.coordinates[2] = -v.xyz[1]; - objNormal.normal[0] = normalVec[0]; - objNormal.normal[1] = normalVec[2]; - objNormal.normal[2] = -normalVec[1]; - objUv.uv[0] = uv[0]; - objUv.uv[1] = 1.0f - uv[1]; - - writer.AddVertex(static_cast(surfIndex), objVertex); - writer.AddNormal(static_cast(surfIndex), objNormal); - writer.AddUv(static_cast(surfIndex), objUv); - } - } - } - - void AddObjFaces(ObjWriter& writer, const XModel* model, const unsigned lod) - { - const auto* surfs = &model->surfs[model->lodInfo[lod].surfIndex]; - const auto surfCount = model->lodInfo[lod].numsurfs; - - for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++) - { - const auto& surface = surfs[surfIndex]; - for (auto triIndex = 0u; triIndex < surface.triCount; triIndex++) - { - const auto& tri = surface.triIndices[triIndex]; - - ObjFace face{}; - face.vertexIndex[0] = tri[2] + surface.baseVertIndex; - face.vertexIndex[1] = tri[1] + surface.baseVertIndex; - face.vertexIndex[2] = tri[0] + surface.baseVertIndex; - face.normalIndex[0] = face.vertexIndex[0]; - face.normalIndex[1] = face.vertexIndex[1]; - face.normalIndex[2] = face.vertexIndex[2]; - face.uvIndex[0] = face.vertexIndex[0]; - face.uvIndex[1] = face.vertexIndex[1]; - face.uvIndex[2] = face.vertexIndex[2]; - writer.AddFace(static_cast(surfIndex), face); - } - } - } - - void DumpObjMat(const AssetDumpingContext& context, const XAssetInfo* asset) - { - const auto* model = asset->Asset(); - const auto matFile = context.OpenAssetFile(std::format("model_export/{}.mtl", model->name)); - - if (!matFile) - return; - - ObjWriter writer(context.m_zone->m_game->GetShortName(), context.m_zone->m_name); - DistinctMapper materialMapper(model->numsurfs); - - AddObjMaterials(writer, materialMapper, model); - writer.WriteMtl(*matFile); - } - - void DumpObjLod(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; - - ObjWriter writer(context.m_zone->m_game->GetShortName(), context.m_zone->m_name); - DistinctMapper materialMapper(model->numsurfs); - - AddObjMaterials(writer, materialMapper, model); - AddObjObjects(writer, materialMapper, model, lod); - AddObjVertices(writer, model, lod); - AddObjFaces(writer, model, lod); - - writer.WriteObj(*assetFile, std::format("{}.mtl", model->name)); - } - void AddXModelBones(XModelCommon& out, const AssetDumpingContext& context, const XModel* model) { for (auto boneNum = 0u; boneNum < model->numBones; boneNum++) @@ -307,6 +165,14 @@ namespace if (colorMap) xMaterial.colorMapName = std::string(colorMap->name); + const auto* normalMap = GetMaterialNormalMap(material); + if (normalMap) + xMaterial.normalMapName = std::string(normalMap->name); + + const auto* specularMap = GetMaterialSpecularMap(material); + if (specularMap) + xMaterial.specularMapName = std::string(specularMap->name); + out.m_materials.emplace_back(std::move(xMaterial)); } } @@ -517,6 +383,9 @@ namespace 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]; @@ -525,8 +394,7 @@ namespace face.vertexIndex[0] = tri[0] + surface.baseVertIndex; face.vertexIndex[1] = tri[1] + surface.baseVertIndex; face.vertexIndex[2] = tri[2] + surface.baseVertIndex; - face.objectIndex = static_cast(surfIndex); - out.m_faces.emplace_back(face); + object.m_faces.emplace_back(face); } } } @@ -544,6 +412,35 @@ namespace 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(); @@ -576,9 +473,6 @@ namespace { const auto* model = asset->Asset(); - if (ObjWriting::Configuration.ModelOutputFormat == ObjWriting::Configuration_t::ModelOutputFormat_e::OBJ) - DumpObjMat(context, asset); - for (auto currentLod = 0u; currentLod < model->numLods; currentLod++) { XModelCommon common; @@ -587,7 +481,9 @@ namespace switch (ObjWriting::Configuration.ModelOutputFormat) { case ObjWriting::Configuration_t::ModelOutputFormat_e::OBJ: - DumpObjLod(context, asset, currentLod); + DumpObjLod(common, context, asset, currentLod); + if (currentLod == 0u) + DumpObjMtl(common, context, asset); break; case ObjWriting::Configuration_t::ModelOutputFormat_e::XMODEL_EXPORT: diff --git a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperXModel.cpp b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperXModel.cpp index a1064a0a..b1019067 100644 --- a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperXModel.cpp +++ b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperXModel.cpp @@ -20,21 +20,6 @@ using namespace IW4; namespace { - class SurfsDumpingZoneState final : public IZoneAssetDumperState - { - std::set m_dumped_surfs; - - public: - bool ShouldDumpTechnique(const XModelSurfs* surfs) - { - if (m_dumped_surfs.contains(surfs)) - return false; - - m_dumped_surfs.emplace(surfs); - return true; - } - }; - GfxImage* GetMaterialColorMap(const Material* material) { std::vector potentialTextureDefs; @@ -113,144 +98,6 @@ namespace return potentialTextureDefs[0]->u.image; } - void AddObjMaterials(ObjWriter& writer, DistinctMapper& materialMapper, const XModel* model) - { - if (!model->materialHandles) - return; - - for (auto surfIndex = 0u; surfIndex < model->numsurfs; surfIndex++) - { - Material* material = model->materialHandles[surfIndex]; - if (!materialMapper.Add(material)) - continue; - - MtlMaterial mtl; - mtl.materialName = std::string(material->info.name); - - GfxImage* colorMap = GetMaterialColorMap(material); - GfxImage* normalMap = GetMaterialNormalMap(material); - GfxImage* specularMap = GetMaterialSpecularMap(material); - - if (colorMap != nullptr) - mtl.colorMapName = colorMap->name; - if (normalMap != nullptr) - mtl.normalMapName = normalMap->name; - if (specularMap != nullptr) - mtl.specularMapName = specularMap->name; - - writer.AddMaterial(std::move(mtl)); - } - } - - void AddObjObjects(ObjWriter& writer, const DistinctMapper& materialMapper, const XModelSurfs* modelSurfs, int baseSurfaceIndex) - { - for (auto surfIndex = 0u; surfIndex < modelSurfs->numsurfs; surfIndex++) - { - ObjObject object; - object.name = std::format("surf{}", surfIndex); - object.materialIndex = static_cast(materialMapper.GetDistinctPositionByInputPosition(surfIndex + baseSurfaceIndex)); - - writer.AddObject(std::move(object)); - } - } - - void AddObjVertices(ObjWriter& writer, 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]; - vec2_t uv; - vec3_t normalVec; - - Common::Vec2UnpackTexCoords(v.texCoord, &uv); - Common::Vec3UnpackUnitVec(v.normal, &normalVec); - - ObjVertex objVertex{}; - ObjNormal objNormal{}; - ObjUv objUv{}; - objVertex.coordinates[0] = v.xyz[0]; - objVertex.coordinates[1] = v.xyz[2]; - objVertex.coordinates[2] = -v.xyz[1]; - objNormal.normal[0] = normalVec[0]; - objNormal.normal[1] = normalVec[2]; - objNormal.normal[2] = -normalVec[1]; - objUv.uv[0] = uv[0]; - objUv.uv[1] = 1.0f - uv[1]; - - writer.AddVertex(static_cast(surfIndex), objVertex); - writer.AddNormal(static_cast(surfIndex), objNormal); - writer.AddUv(static_cast(surfIndex), objUv); - } - } - } - - void AddObjFaces(ObjWriter& writer, const XModelSurfs* modelSurfs) - { - for (auto surfIndex = 0u; surfIndex < modelSurfs->numsurfs; surfIndex++) - { - const auto& surface = modelSurfs->surfs[surfIndex]; - for (auto triIndex = 0u; triIndex < surface.triCount; triIndex++) - { - const auto& tri = surface.triIndices[triIndex]; - - ObjFace face{}; - face.vertexIndex[0] = tri[2] + surface.baseVertIndex; - face.vertexIndex[1] = tri[1] + surface.baseVertIndex; - face.vertexIndex[2] = tri[0] + surface.baseVertIndex; - face.normalIndex[0] = face.vertexIndex[0]; - face.normalIndex[1] = face.vertexIndex[1]; - face.normalIndex[2] = face.vertexIndex[2]; - face.uvIndex[0] = face.vertexIndex[0]; - face.uvIndex[1] = face.vertexIndex[1]; - face.uvIndex[2] = face.vertexIndex[2]; - writer.AddFace(static_cast(surfIndex), face); - } - } - } - - void DumpObjMat(const AssetDumpingContext& context, const XAssetInfo* asset) - { - const auto* model = asset->Asset(); - const auto matFile = context.OpenAssetFile(std::format("model_export/{}.mtl", model->name)); - - if (!matFile) - return; - - ObjWriter writer(context.m_zone->m_game->GetShortName(), context.m_zone->m_name); - DistinctMapper materialMapper(model->numsurfs); - - AddObjMaterials(writer, materialMapper, model); - writer.WriteMtl(*matFile); - } - - void DumpObjLod(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; - - ObjWriter writer(context.m_zone->m_game->GetShortName(), context.m_zone->m_name); - DistinctMapper materialMapper(model->numsurfs); - - AddObjMaterials(writer, materialMapper, model); - AddObjObjects(writer, materialMapper, modelSurfs, model->lodInfo[lod].surfIndex); - AddObjVertices(writer, modelSurfs); - AddObjFaces(writer, modelSurfs); - - writer.WriteObj(*assetFile, std::format("{}.mtl", model->name)); - } - void AddXModelBones(XModelCommon& out, const AssetDumpingContext& context, const XModel* model) { for (auto boneNum = 0u; boneNum < model->numBones; boneNum++) @@ -313,6 +160,14 @@ namespace if (colorMap) xMaterial.colorMapName = std::string(colorMap->name); + const auto* normalMap = GetMaterialNormalMap(material); + if (normalMap) + xMaterial.normalMapName = std::string(normalMap->name); + + const auto* specularMap = GetMaterialSpecularMap(material); + if (specularMap) + xMaterial.specularMapName = std::string(specularMap->name); + out.m_materials.emplace_back(std::move(xMaterial)); } } @@ -508,6 +363,9 @@ namespace 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]; @@ -516,8 +374,7 @@ namespace face.vertexIndex[0] = tri[0] + surface.baseVertIndex; face.vertexIndex[1] = tri[1] + surface.baseVertIndex; face.vertexIndex[2] = tri[2] + surface.baseVertIndex; - face.objectIndex = static_cast(surfIndex); - out.m_faces.emplace_back(face); + object.m_faces.emplace_back(face); } } } @@ -537,6 +394,40 @@ namespace 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(); @@ -571,9 +462,6 @@ namespace { const auto* model = asset->Asset(); - if (ObjWriting::Configuration.ModelOutputFormat == ObjWriting::Configuration_t::ModelOutputFormat_e::OBJ) - DumpObjMat(context, asset); - for (auto currentLod = 0u; currentLod < model->numLods; currentLod++) { XModelCommon common; @@ -582,7 +470,9 @@ namespace switch (ObjWriting::Configuration.ModelOutputFormat) { case ObjWriting::Configuration_t::ModelOutputFormat_e::OBJ: - DumpObjLod(context, asset, currentLod); + DumpObjLod(common, context, asset, currentLod); + if (currentLod == 0u) + DumpObjMtl(common, context, asset); break; case ObjWriting::Configuration_t::ModelOutputFormat_e::XMODEL_EXPORT: diff --git a/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperXModel.cpp b/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperXModel.cpp index a11240e8..b9cb4435 100644 --- a/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperXModel.cpp +++ b/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperXModel.cpp @@ -20,21 +20,6 @@ using namespace IW5; namespace { - class SurfsDumpingZoneState final : public IZoneAssetDumperState - { - std::set m_dumped_surfs; - - public: - bool ShouldDumpTechnique(const XModelSurfs* surfs) - { - if (m_dumped_surfs.contains(surfs)) - return false; - - m_dumped_surfs.emplace(surfs); - return true; - } - }; - GfxImage* GetMaterialColorMap(const Material* material) { std::vector potentialTextureDefs; @@ -113,143 +98,6 @@ namespace return potentialTextureDefs[0]->u.image; } - void AddObjMaterials(ObjWriter& writer, DistinctMapper& materialMapper, const XModel* model) - { - if (!model->materialHandles) - return; - - for (auto surfIndex = 0u; surfIndex < model->numsurfs; surfIndex++) - { - Material* material = model->materialHandles[surfIndex]; - if (!materialMapper.Add(material)) - continue; - - MtlMaterial mtl; - mtl.materialName = std::string(material->info.name); - GfxImage* colorMap = GetMaterialColorMap(material); - GfxImage* normalMap = GetMaterialNormalMap(material); - GfxImage* specularMap = GetMaterialSpecularMap(material); - - if (colorMap != nullptr) - mtl.colorMapName = colorMap->name; - if (normalMap != nullptr) - mtl.normalMapName = normalMap->name; - if (specularMap != nullptr) - mtl.specularMapName = specularMap->name; - - writer.AddMaterial(std::move(mtl)); - } - } - - void AddObjObjects(ObjWriter& writer, const DistinctMapper& materialMapper, const XModelSurfs* modelSurfs, int baseSurfaceIndex) - { - for (auto surfIndex = 0u; surfIndex < modelSurfs->numsurfs; surfIndex++) - { - ObjObject object; - object.name = std::format("surf{}", surfIndex); - object.materialIndex = static_cast(materialMapper.GetDistinctPositionByInputPosition(surfIndex + baseSurfaceIndex)); - - writer.AddObject(std::move(object)); - } - } - - void AddObjVertices(ObjWriter& writer, 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.packedVerts0[vertexIndex]; - vec2_t uv; - vec3_t normalVec; - - Common::Vec2UnpackTexCoords(v.texCoord, &uv); - Common::Vec3UnpackUnitVec(v.normal, &normalVec); - - ObjVertex objVertex{}; - ObjNormal objNormal{}; - ObjUv objUv{}; - objVertex.coordinates[0] = v.xyz[0]; - objVertex.coordinates[1] = v.xyz[2]; - objVertex.coordinates[2] = -v.xyz[1]; - objNormal.normal[0] = normalVec[0]; - objNormal.normal[1] = normalVec[2]; - objNormal.normal[2] = -normalVec[1]; - objUv.uv[0] = uv[0]; - objUv.uv[1] = 1.0f - uv[1]; - - writer.AddVertex(static_cast(surfIndex), objVertex); - writer.AddNormal(static_cast(surfIndex), objNormal); - writer.AddUv(static_cast(surfIndex), objUv); - } - } - } - - void AddObjFaces(ObjWriter& writer, const XModelSurfs* modelSurfs) - { - for (auto surfIndex = 0u; surfIndex < modelSurfs->numsurfs; surfIndex++) - { - const auto& surface = modelSurfs->surfs[surfIndex]; - for (auto triIndex = 0u; triIndex < surface.triCount; triIndex++) - { - const auto& tri = surface.triIndices[triIndex]; - - ObjFace face{}; - face.vertexIndex[0] = tri[2] + surface.baseVertIndex; - face.vertexIndex[1] = tri[1] + surface.baseVertIndex; - face.vertexIndex[2] = tri[0] + surface.baseVertIndex; - face.normalIndex[0] = face.vertexIndex[0]; - face.normalIndex[1] = face.vertexIndex[1]; - face.normalIndex[2] = face.vertexIndex[2]; - face.uvIndex[0] = face.vertexIndex[0]; - face.uvIndex[1] = face.vertexIndex[1]; - face.uvIndex[2] = face.vertexIndex[2]; - writer.AddFace(static_cast(surfIndex), face); - } - } - } - - void DumpObjMat(const AssetDumpingContext& context, const XAssetInfo* asset) - { - const auto* model = asset->Asset(); - const auto matFile = context.OpenAssetFile(std::format("model_export/{}.mtl", model->name)); - - if (!matFile) - return; - - ObjWriter writer(context.m_zone->m_game->GetShortName(), context.m_zone->m_name); - DistinctMapper materialMapper(model->numsurfs); - - AddObjMaterials(writer, materialMapper, model); - writer.WriteMtl(*matFile); - } - - void DumpObjLod(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; - - ObjWriter writer(context.m_zone->m_game->GetShortName(), context.m_zone->m_name); - DistinctMapper materialMapper(model->numsurfs); - - AddObjMaterials(writer, materialMapper, model); - AddObjObjects(writer, materialMapper, modelSurfs, model->lodInfo[lod].surfIndex); - AddObjVertices(writer, modelSurfs); - AddObjFaces(writer, modelSurfs); - - writer.WriteObj(*assetFile, std::format("{}.mtl", model->name)); - } - void AddXModelBones(XModelCommon& out, const AssetDumpingContext& context, const XModel* model) { for (auto boneNum = 0u; boneNum < model->numBones; boneNum++) @@ -312,6 +160,14 @@ namespace if (colorMap) xMaterial.colorMapName = std::string(colorMap->name); + const auto* normalMap = GetMaterialNormalMap(material); + if (normalMap) + xMaterial.normalMapName = std::string(normalMap->name); + + const auto* specularMap = GetMaterialSpecularMap(material); + if (specularMap) + xMaterial.specularMapName = std::string(specularMap->name); + out.m_materials.emplace_back(std::move(xMaterial)); } } @@ -507,6 +363,9 @@ namespace 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]; @@ -515,8 +374,7 @@ namespace face.vertexIndex[0] = tri[0] + surface.baseVertIndex; face.vertexIndex[1] = tri[1] + surface.baseVertIndex; face.vertexIndex[2] = tri[2] + surface.baseVertIndex; - face.objectIndex = static_cast(surfIndex); - out.m_faces.emplace_back(face); + object.m_faces.emplace_back(face); } } } @@ -536,6 +394,40 @@ namespace 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(); @@ -570,9 +462,6 @@ namespace { const auto* model = asset->Asset(); - if (ObjWriting::Configuration.ModelOutputFormat == ObjWriting::Configuration_t::ModelOutputFormat_e::OBJ) - DumpObjMat(context, asset); - for (auto currentLod = 0u; currentLod < model->numLods; currentLod++) { XModelCommon common; @@ -581,7 +470,9 @@ namespace switch (ObjWriting::Configuration.ModelOutputFormat) { case ObjWriting::Configuration_t::ModelOutputFormat_e::OBJ: - DumpObjLod(context, asset, currentLod); + DumpObjLod(common, context, asset, currentLod); + if (currentLod == 0u) + DumpObjMtl(common, context, asset); break; case ObjWriting::Configuration_t::ModelOutputFormat_e::XMODEL_EXPORT: diff --git a/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperXModel.cpp b/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperXModel.cpp index 82d59797..ad9c3657 100644 --- a/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperXModel.cpp +++ b/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperXModel.cpp @@ -103,148 +103,6 @@ namespace return potentialTextureDefs[0]->u.image; } - void AddObjMaterials(ObjWriter& writer, DistinctMapper& materialMapper, const XModel* model) - { - if (!model->materialHandles) - return; - - for (auto surfIndex = 0u; surfIndex < model->numsurfs; surfIndex++) - { - Material* material = model->materialHandles[surfIndex]; - if (!materialMapper.Add(material)) - continue; - - MtlMaterial mtl; - mtl.materialName = std::string(material->info.name); - - GfxImage* colorMap = GetMaterialColorMap(material); - GfxImage* normalMap = GetMaterialNormalMap(material); - GfxImage* specularMap = GetMaterialSpecularMap(material); - - if (colorMap != nullptr) - mtl.colorMapName = colorMap->name; - if (normalMap != nullptr) - mtl.normalMapName = normalMap->name; - if (specularMap != nullptr) - mtl.specularMapName = specularMap->name; - - writer.AddMaterial(std::move(mtl)); - } - } - - void AddObjObjects(ObjWriter& writer, const DistinctMapper& materialMapper, const XModel* model, const unsigned lod) - { - const auto surfCount = model->lodInfo[lod].numsurfs; - const auto baseSurfIndex = model->lodInfo[lod].surfIndex; - - for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++) - { - ObjObject object; - object.name = std::format("surf{}", surfIndex); - object.materialIndex = static_cast(materialMapper.GetDistinctPositionByInputPosition(surfIndex + baseSurfIndex)); - - writer.AddObject(std::move(object)); - } - } - - void AddObjVertices(ObjWriter& writer, const XModel* model, const unsigned lod) - { - const auto* surfs = &model->surfs[model->lodInfo[lod].surfIndex]; - const auto surfCount = model->lodInfo[lod].numsurfs; - - for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++) - { - const auto& surface = surfs[surfIndex]; - - for (auto vertexIndex = 0u; vertexIndex < surface.vertCount; vertexIndex++) - { - const auto& v = surface.verts0[vertexIndex]; - vec2_t uv; - vec3_t normalVec; - - Common::Vec2UnpackTexCoords(v.texCoord, &uv); - Common::Vec3UnpackUnitVec(v.normal, &normalVec); - - ObjVertex objVertex{}; - ObjNormal objNormal{}; - ObjUv objUv{}; - objVertex.coordinates[0] = v.xyz[0]; - objVertex.coordinates[1] = v.xyz[2]; - objVertex.coordinates[2] = -v.xyz[1]; - objNormal.normal[0] = normalVec[0]; - objNormal.normal[1] = normalVec[2]; - objNormal.normal[2] = -normalVec[1]; - objUv.uv[0] = uv[0]; - objUv.uv[1] = 1.0f - uv[1]; - - writer.AddVertex(static_cast(surfIndex), objVertex); - writer.AddNormal(static_cast(surfIndex), objNormal); - writer.AddUv(static_cast(surfIndex), objUv); - } - } - } - - void AddObjFaces(ObjWriter& writer, const XModel* model, const unsigned lod) - { - const auto* surfs = &model->surfs[model->lodInfo[lod].surfIndex]; - const auto surfCount = model->lodInfo[lod].numsurfs; - - for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++) - { - const auto& surface = surfs[surfIndex]; - for (auto triIndex = 0u; triIndex < surface.triCount; triIndex++) - { - const auto& tri = surface.triIndices[triIndex]; - - ObjFace face{}; - face.vertexIndex[0] = tri[2] + surface.baseVertIndex; - face.vertexIndex[1] = tri[1] + surface.baseVertIndex; - face.vertexIndex[2] = tri[0] + surface.baseVertIndex; - face.normalIndex[0] = face.vertexIndex[0]; - face.normalIndex[1] = face.vertexIndex[1]; - face.normalIndex[2] = face.vertexIndex[2]; - face.uvIndex[0] = face.vertexIndex[0]; - face.uvIndex[1] = face.vertexIndex[1]; - face.uvIndex[2] = face.vertexIndex[2]; - writer.AddFace(static_cast(surfIndex), face); - } - } - } - - void DumpObjMat(const AssetDumpingContext& context, const XAssetInfo* asset) - { - const auto* model = asset->Asset(); - const auto matFile = context.OpenAssetFile(std::format("model_export/{}.mtl", model->name)); - - if (!matFile) - return; - - ObjWriter writer(context.m_zone->m_game->GetShortName(), context.m_zone->m_name); - DistinctMapper materialMapper(model->numsurfs); - - AddObjMaterials(writer, materialMapper, model); - writer.WriteMtl(*matFile); - } - - void DumpObjLod(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; - - ObjWriter writer(context.m_zone->m_game->GetShortName(), context.m_zone->m_name); - DistinctMapper materialMapper(model->numsurfs); - - AddObjMaterials(writer, materialMapper, model); - AddObjObjects(writer, materialMapper, model, lod); - AddObjVertices(writer, model, lod); - AddObjFaces(writer, model, lod); - - writer.WriteObj(*assetFile, std::format("{}.mtl", model->name)); - } - void AddXModelBones(XModelCommon& out, const AssetDumpingContext& context, const XModel* model) { for (auto boneNum = 0u; boneNum < model->numBones; boneNum++) @@ -307,6 +165,14 @@ namespace if (colorMap) xMaterial.colorMapName = std::string(colorMap->name); + const auto* normalMap = GetMaterialNormalMap(material); + if (normalMap) + xMaterial.normalMapName = std::string(normalMap->name); + + const auto* specularMap = GetMaterialSpecularMap(material); + if (specularMap) + xMaterial.specularMapName = std::string(specularMap->name); + out.m_materials.emplace_back(std::move(xMaterial)); } } @@ -517,6 +383,9 @@ namespace 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]; @@ -525,8 +394,7 @@ namespace face.vertexIndex[0] = tri[0] + surface.baseVertIndex; face.vertexIndex[1] = tri[1] + surface.baseVertIndex; face.vertexIndex[2] = tri[2] + surface.baseVertIndex; - face.objectIndex = static_cast(surfIndex); - out.m_faces.emplace_back(face); + object.m_faces.emplace_back(face); } } } @@ -544,6 +412,35 @@ namespace 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(); @@ -576,9 +473,6 @@ namespace { const auto* model = asset->Asset(); - if (ObjWriting::Configuration.ModelOutputFormat == ObjWriting::Configuration_t::ModelOutputFormat_e::OBJ) - DumpObjMat(context, asset); - for (auto currentLod = 0u; currentLod < model->numLods; currentLod++) { XModelCommon common; @@ -587,7 +481,9 @@ namespace switch (ObjWriting::Configuration.ModelOutputFormat) { case ObjWriting::Configuration_t::ModelOutputFormat_e::OBJ: - DumpObjLod(context, asset, currentLod); + DumpObjLod(common, context, asset, currentLod); + if (currentLod == 0u) + DumpObjMtl(common, context, asset); break; case ObjWriting::Configuration_t::ModelOutputFormat_e::XMODEL_EXPORT: diff --git a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperXModel.cpp b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperXModel.cpp index 526dfa68..794367ed 100644 --- a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperXModel.cpp +++ b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperXModel.cpp @@ -115,148 +115,6 @@ namespace return potentialTextureDefs[0]->image; } - void AddObjMaterials(ObjWriter& writer, DistinctMapper& materialMapper, const XModel* model) - { - if (!model->materialHandles) - return; - - for (auto surfIndex = 0u; surfIndex < model->numsurfs; surfIndex++) - { - Material* material = model->materialHandles[surfIndex]; - if (!materialMapper.Add(material)) - continue; - - MtlMaterial mtl; - mtl.materialName = std::string(material->info.name); - - GfxImage* colorMap = GetMaterialColorMap(material); - GfxImage* normalMap = GetMaterialNormalMap(material); - GfxImage* specularMap = GetMaterialSpecularMap(material); - - if (colorMap != nullptr) - mtl.colorMapName = colorMap->name; - if (normalMap != nullptr) - mtl.normalMapName = normalMap->name; - if (specularMap != nullptr) - mtl.specularMapName = specularMap->name; - - writer.AddMaterial(std::move(mtl)); - } - } - - void AddObjObjects(ObjWriter& writer, const DistinctMapper& materialMapper, const XModel* model, const unsigned lod) - { - const auto surfCount = model->lodInfo[lod].numsurfs; - const auto baseSurfIndex = model->lodInfo[lod].surfIndex; - - for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++) - { - ObjObject object; - object.name = std::format("surf{}", surfIndex); - object.materialIndex = static_cast(materialMapper.GetDistinctPositionByInputPosition(surfIndex + baseSurfIndex)); - - writer.AddObject(std::move(object)); - } - } - - void AddObjVertices(ObjWriter& writer, const XModel* model, const unsigned lod) - { - const auto* surfs = &model->surfs[model->lodInfo[lod].surfIndex]; - const auto surfCount = model->lodInfo[lod].numsurfs; - - for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++) - { - const auto& surface = surfs[surfIndex]; - - for (auto vertexIndex = 0u; vertexIndex < surface.vertCount; vertexIndex++) - { - const auto& v = surface.verts0[vertexIndex]; - vec2_t uv{}; - vec3_t normalVec{}; - - Common::Vec2UnpackTexCoords(v.texCoord, &uv); - Common::Vec3UnpackUnitVec(v.normal, &normalVec); - - ObjVertex objVertex{}; - ObjNormal objNormal{}; - ObjUv objUv{}; - objVertex.coordinates[0] = v.xyz.x; - objVertex.coordinates[1] = v.xyz.z; - objVertex.coordinates[2] = -v.xyz.y; - objNormal.normal[0] = normalVec.x; - objNormal.normal[1] = normalVec.z; - objNormal.normal[2] = -normalVec.y; - objUv.uv[0] = uv.x; - objUv.uv[1] = 1.0f - uv.y; - - writer.AddVertex(static_cast(surfIndex), objVertex); - writer.AddNormal(static_cast(surfIndex), objNormal); - writer.AddUv(static_cast(surfIndex), objUv); - } - } - } - - void AddObjFaces(ObjWriter& writer, const XModel* model, const unsigned lod) - { - const auto* surfs = &model->surfs[model->lodInfo[lod].surfIndex]; - const auto surfCount = model->lodInfo[lod].numsurfs; - - for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++) - { - const auto& surface = surfs[surfIndex]; - for (auto triIndex = 0u; triIndex < surface.triCount; triIndex++) - { - const auto& tri = surface.triIndices[triIndex]; - - ObjFace face{}; - face.vertexIndex[0] = tri[2] + surface.baseVertIndex; - face.vertexIndex[1] = tri[1] + surface.baseVertIndex; - face.vertexIndex[2] = tri[0] + surface.baseVertIndex; - face.normalIndex[0] = face.vertexIndex[0]; - face.normalIndex[1] = face.vertexIndex[1]; - face.normalIndex[2] = face.vertexIndex[2]; - face.uvIndex[0] = face.vertexIndex[0]; - face.uvIndex[1] = face.vertexIndex[1]; - face.uvIndex[2] = face.vertexIndex[2]; - writer.AddFace(static_cast(surfIndex), face); - } - } - } - - void DumpObjMat(const AssetDumpingContext& context, const XAssetInfo* asset) - { - const auto* model = asset->Asset(); - const auto matFile = context.OpenAssetFile(std::format("model_export/{}.mtl", model->name)); - - if (!matFile) - return; - - ObjWriter writer(context.m_zone->m_game->GetShortName(), context.m_zone->m_name); - DistinctMapper materialMapper(model->numsurfs); - - AddObjMaterials(writer, materialMapper, model); - writer.WriteMtl(*matFile); - } - - void DumpObjLod(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; - - ObjWriter writer(context.m_zone->m_game->GetShortName(), context.m_zone->m_name); - DistinctMapper materialMapper(model->numsurfs); - - AddObjMaterials(writer, materialMapper, model); - AddObjObjects(writer, materialMapper, model, lod); - AddObjVertices(writer, model, lod); - AddObjFaces(writer, model, lod); - - writer.WriteObj(*assetFile, std::format("{}.mtl", model->name)); - } - void AddXModelBones(XModelCommon& out, const AssetDumpingContext& context, const XModel* model) { for (auto boneNum = 0u; boneNum < model->numBones; boneNum++) @@ -319,6 +177,14 @@ namespace if (colorMap) xMaterial.colorMapName = std::string(colorMap->name); + const auto* normalMap = GetMaterialNormalMap(material); + if (normalMap) + xMaterial.normalMapName = std::string(normalMap->name); + + const auto* specularMap = GetMaterialSpecularMap(material); + if (specularMap) + xMaterial.specularMapName = std::string(specularMap->name); + out.m_materials.emplace_back(std::move(xMaterial)); } } @@ -541,6 +407,9 @@ namespace 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]; @@ -549,8 +418,7 @@ namespace face.vertexIndex[0] = tri[0] + surface.baseVertIndex; face.vertexIndex[1] = tri[1] + surface.baseVertIndex; face.vertexIndex[2] = tri[2] + surface.baseVertIndex; - face.objectIndex = static_cast(surfIndex); - out.m_faces.emplace_back(face); + object.m_faces.emplace_back(face); } } } @@ -568,6 +436,35 @@ namespace 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(); @@ -600,9 +497,6 @@ namespace { const auto* model = asset->Asset(); - if (ObjWriting::Configuration.ModelOutputFormat == ObjWriting::Configuration_t::ModelOutputFormat_e::OBJ) - DumpObjMat(context, asset); - for (auto currentLod = 0u; currentLod < model->numLods; currentLod++) { XModelCommon common; @@ -611,7 +505,9 @@ namespace switch (ObjWriting::Configuration.ModelOutputFormat) { case ObjWriting::Configuration_t::ModelOutputFormat_e::OBJ: - DumpObjLod(context, asset, currentLod); + DumpObjLod(common, context, asset, currentLod); + if (currentLod == 0u) + DumpObjMtl(common, context, asset); break; case ObjWriting::Configuration_t::ModelOutputFormat_e::XMODEL_EXPORT: diff --git a/src/ObjWriting/XModel/Export/XModelExportWriter.cpp b/src/ObjWriting/XModel/Export/XModelExportWriter.cpp index e872d602..f65bc559 100644 --- a/src/ObjWriting/XModel/Export/XModelExportWriter.cpp +++ b/src/ObjWriting/XModel/Export/XModelExportWriter.cpp @@ -116,24 +116,35 @@ class XModelExportWriter6 final : public XModelExportWriterBase void WriteFaces(const XModelCommon& xmodel) const { - m_stream << "NUMFACES " << xmodel.m_faces.size() << "\n"; - for (const auto& face : xmodel.m_faces) + auto totalFaceCount = 0u; + for (const auto& object : xmodel.m_objects) + totalFaceCount += object.m_faces.size(); + + m_stream << "NUMFACES " << totalFaceCount << "\n"; + + auto objectIndex = 0u; + for (const auto& object : xmodel.m_objects) { - const size_t distinctPositions[3]{ - m_vertex_merger.GetDistinctPositionByInputPosition(face.vertexIndex[0]), - m_vertex_merger.GetDistinctPositionByInputPosition(face.vertexIndex[1]), - m_vertex_merger.GetDistinctPositionByInputPosition(face.vertexIndex[2]), - }; + for (const auto& face : object.m_faces) + { + const size_t distinctPositions[3]{ + m_vertex_merger.GetDistinctPositionByInputPosition(face.vertexIndex[0]), + m_vertex_merger.GetDistinctPositionByInputPosition(face.vertexIndex[1]), + m_vertex_merger.GetDistinctPositionByInputPosition(face.vertexIndex[2]), + }; - const XModelVertex& v0 = xmodel.m_vertices[face.vertexIndex[0]]; - const XModelVertex& v1 = xmodel.m_vertices[face.vertexIndex[1]]; - const XModelVertex& v2 = xmodel.m_vertices[face.vertexIndex[2]]; + const XModelVertex& v0 = xmodel.m_vertices[face.vertexIndex[0]]; + const XModelVertex& v1 = xmodel.m_vertices[face.vertexIndex[1]]; + const XModelVertex& v2 = xmodel.m_vertices[face.vertexIndex[2]]; - m_stream << "TRI " << face.objectIndex << " " << xmodel.m_objects[face.objectIndex].materialIndex << " 0 0\n"; - WriteFaceVertex(distinctPositions[0], v0); - WriteFaceVertex(distinctPositions[1], v1); - WriteFaceVertex(distinctPositions[2], v2); - m_stream << "\n"; + m_stream << "TRI " << objectIndex << " " << object.materialIndex << " 0 0\n"; + WriteFaceVertex(distinctPositions[0], v0); + WriteFaceVertex(distinctPositions[1], v1); + WriteFaceVertex(distinctPositions[2], v2); + m_stream << "\n"; + } + + objectIndex++; } } diff --git a/src/ObjWriting/XModel/Obj/ObjWriter.cpp b/src/ObjWriting/XModel/Obj/ObjWriter.cpp index 2c8ad908..e0538943 100644 --- a/src/ObjWriting/XModel/Obj/ObjWriter.cpp +++ b/src/ObjWriting/XModel/Obj/ObjWriter.cpp @@ -1,157 +1,225 @@ #include "ObjWriter.h" -#include "Game/IW4/CommonIW4.h" +#include "Utils/DistinctMapper.h" +#include "XModel/Obj/ObjCommon.h" -ObjWriter::ObjWriter(std::string gameName, std::string zoneName) - : m_game_name(std::move(gameName)), - m_zone_name(std::move(zoneName)) +namespace { -} - -void ObjWriter::AddObject(ObjObject object) -{ - m_objects.emplace_back(std::move(object)); - m_object_data.emplace_back(); -} - -void ObjWriter::AddMaterial(MtlMaterial material) -{ - m_materials.emplace_back(std::move(material)); -} - -void ObjWriter::AddVertex(const int objectId, const ObjVertex vertex) -{ - if (objectId < 0 || static_cast(objectId) >= m_object_data.size()) - return; - - m_object_data[objectId].m_vertices.Add(vertex); -} - -void ObjWriter::AddNormal(const int objectId, const ObjNormal normal) -{ - if (objectId < 0 || static_cast(objectId) >= m_object_data.size()) - return; - - m_object_data[objectId].m_normals.Add(normal); -} - -void ObjWriter::AddUv(const int objectId, const ObjUv uv) -{ - if (objectId < 0 || static_cast(objectId) >= m_object_data.size()) - return; - - m_object_data[objectId].m_uvs.Add(uv); -} - -void ObjWriter::AddFace(const int objectId, const ObjFace face) -{ - if (objectId < 0 || static_cast(objectId) >= m_object_data.size()) - return; - - m_object_data[objectId].m_faces.push_back(face); -} - -void ObjWriter::GetObjObjectDataOffsets(std::vector& inputOffsets, std::vector& distinctOffsets) -{ - ObjObjectDataOffsets currentInputOffsets{}; - ObjObjectDataOffsets currentDistinctOffsets{}; - - for (const auto& objectData : m_object_data) + struct ObjObjectData { - inputOffsets.push_back(currentInputOffsets); - distinctOffsets.push_back(currentDistinctOffsets); + DistinctMapper m_vertices; + DistinctMapper m_normals; + DistinctMapper m_uvs; + }; - currentInputOffsets.vertexOffset += objectData.m_vertices.GetInputValueCount(); - currentInputOffsets.normalOffset += objectData.m_normals.GetInputValueCount(); - currentInputOffsets.uvOffset += objectData.m_uvs.GetInputValueCount(); - currentDistinctOffsets.vertexOffset += objectData.m_vertices.GetDistinctValueCount(); - currentDistinctOffsets.normalOffset += objectData.m_normals.GetDistinctValueCount(); - currentDistinctOffsets.uvOffset += objectData.m_uvs.GetDistinctValueCount(); - } -} - -void ObjWriter::WriteObj(std::ostream& stream) -{ - WriteObj(stream, std::string()); -} - -void ObjWriter::WriteObj(std::ostream& stream, const std::string& mtlName) -{ - stream << "# OpenAssetTools OBJ File ( " << m_game_name << ")\n"; - stream << "# Game Origin: " << m_game_name << "\n"; - stream << "# Zone Origin: " << m_zone_name << "\n"; - - if (!mtlName.empty()) - stream << "mtllib " << mtlName << "\n"; - - std::vector inputOffsetsByObject; - std::vector distinctOffsetsByObject; - GetObjObjectDataOffsets(inputOffsetsByObject, distinctOffsetsByObject); - - auto objectIndex = 0; - for (const auto& object : m_objects) + struct ObjObjectDataOffsets { - const auto& objectData = m_object_data[objectIndex]; - stream << "o " << object.name << "\n"; + size_t vertexOffset; + size_t normalOffset; + size_t uvOffset; + }; - for (const auto& v : objectData.m_vertices.GetDistinctValues()) - stream << "v " << v.coordinates[0] << " " << v.coordinates[1] << " " << v.coordinates[2] << "\n"; - for (const auto& uv : objectData.m_uvs.GetDistinctValues()) - stream << "vt " << uv.uv[0] << " " << uv.uv[1] << "\n"; - for (const auto& n : objectData.m_normals.GetDistinctValues()) - stream << "vn " << n.normal[0] << " " << n.normal[1] << " " << n.normal[2] << "\n"; - - if (object.materialIndex >= 0 && static_cast(object.materialIndex) < m_materials.size()) - stream << "usemtl " << m_materials[object.materialIndex].materialName << "\n"; - - for (const auto& f : objectData.m_faces) + class ObjWriter final : public XModelWriter + { + public: + ObjWriter(std::ostream& stream, std::string gameName, std::string zoneName, std::string mtlName) + : m_stream(stream), + m_mtl_name(std::move(mtlName)), + m_game_name(std::move(gameName)), + m_zone_name(std::move(zoneName)) { - const size_t v[3]{objectData.m_vertices.GetDistinctPositionByInputPosition(f.vertexIndex[0] - inputOffsetsByObject[objectIndex].vertexOffset) - + distinctOffsetsByObject[objectIndex].vertexOffset + 1, - objectData.m_vertices.GetDistinctPositionByInputPosition(f.vertexIndex[1] - inputOffsetsByObject[objectIndex].vertexOffset) - + distinctOffsetsByObject[objectIndex].vertexOffset + 1, - objectData.m_vertices.GetDistinctPositionByInputPosition(f.vertexIndex[2] - inputOffsetsByObject[objectIndex].vertexOffset) - + distinctOffsetsByObject[objectIndex].vertexOffset + 1}; - const size_t n[3]{objectData.m_normals.GetDistinctPositionByInputPosition(f.normalIndex[0] - inputOffsetsByObject[objectIndex].normalOffset) - + distinctOffsetsByObject[objectIndex].normalOffset + 1, - objectData.m_normals.GetDistinctPositionByInputPosition(f.normalIndex[1] - inputOffsetsByObject[objectIndex].normalOffset) - + distinctOffsetsByObject[objectIndex].normalOffset + 1, - objectData.m_normals.GetDistinctPositionByInputPosition(f.normalIndex[2] - inputOffsetsByObject[objectIndex].normalOffset) - + distinctOffsetsByObject[objectIndex].normalOffset + 1}; - const size_t uv[3]{objectData.m_uvs.GetDistinctPositionByInputPosition(f.uvIndex[0] - inputOffsetsByObject[objectIndex].uvOffset) - + distinctOffsetsByObject[objectIndex].uvOffset + 1, - objectData.m_uvs.GetDistinctPositionByInputPosition(f.uvIndex[1] - inputOffsetsByObject[objectIndex].uvOffset) - + distinctOffsetsByObject[objectIndex].uvOffset + 1, - objectData.m_uvs.GetDistinctPositionByInputPosition(f.uvIndex[2] - inputOffsetsByObject[objectIndex].uvOffset) - + distinctOffsetsByObject[objectIndex].uvOffset + 1}; - - stream << "f " << v[0] << "/" << uv[0] << "/" << n[0] << " " << v[1] << "/" << uv[1] << "/" << n[1] << " " << v[2] << "/" << uv[2] << "/" << n[2] - << "\n"; } - objectIndex++; - } -} + void Write(const XModelCommon& xmodel) override + { + m_stream << "# OpenAssetTools OBJ File ( " << m_game_name << ")\n"; + m_stream << "# Game Origin: " << m_game_name << "\n"; + m_stream << "# Zone Origin: " << m_zone_name << "\n"; -void ObjWriter::WriteMtl(std::ostream& stream) -{ - stream << "# OpenAssetTools MAT File ( " << m_game_name << ")\n"; - stream << "# Game Origin: " << m_game_name << "\n"; - stream << "# Zone Origin: " << m_zone_name << "\n"; - stream << "# Material count: " << m_materials.size() << "\n"; + if (!m_mtl_name.empty()) + m_stream << "mtllib " << m_mtl_name << "\n"; - for (const auto& material : m_materials) + std::vector inputOffsetsByObject; + std::vector distinctOffsetsByObject; + GetObjObjectDataOffsets(xmodel, inputOffsetsByObject, distinctOffsetsByObject); + + auto objectIndex = 0; + for (const auto& object : xmodel.m_objects) + { + const auto& objectData = m_object_data[objectIndex]; + m_stream << "o " << object.name << "\n"; + + for (const auto& v : objectData.m_vertices.GetDistinctValues()) + m_stream << "v " << v.coordinates[0] << " " << v.coordinates[1] << " " << v.coordinates[2] << "\n"; + for (const auto& uv : objectData.m_uvs.GetDistinctValues()) + m_stream << "vt " << uv.uv[0] << " " << uv.uv[1] << "\n"; + for (const auto& n : objectData.m_normals.GetDistinctValues()) + m_stream << "vn " << n.normal[0] << " " << n.normal[1] << " " << n.normal[2] << "\n"; + + if (object.materialIndex >= 0 && static_cast(object.materialIndex) < xmodel.m_materials.size()) + m_stream << "usemtl " << xmodel.m_materials[object.materialIndex].name << "\n"; + + auto faceIndex = 0u; + for (const auto& f : object.m_faces) + { + const auto faceVertexOffset = 3u * faceIndex; + const size_t v[3]{ + objectData.m_vertices.GetDistinctPositionByInputPosition(faceVertexOffset + 0) + distinctOffsetsByObject[objectIndex].vertexOffset + 1, + objectData.m_vertices.GetDistinctPositionByInputPosition(faceVertexOffset + 1) + distinctOffsetsByObject[objectIndex].vertexOffset + 1, + objectData.m_vertices.GetDistinctPositionByInputPosition(faceVertexOffset + 2) + distinctOffsetsByObject[objectIndex].vertexOffset + 1, + }; + const size_t n[3]{ + objectData.m_normals.GetDistinctPositionByInputPosition(faceVertexOffset + 0) + distinctOffsetsByObject[objectIndex].normalOffset + 1, + objectData.m_normals.GetDistinctPositionByInputPosition(faceVertexOffset + 1) + distinctOffsetsByObject[objectIndex].normalOffset + 1, + objectData.m_normals.GetDistinctPositionByInputPosition(faceVertexOffset + 2) + distinctOffsetsByObject[objectIndex].normalOffset + 1, + }; + const size_t uv[3]{ + objectData.m_uvs.GetDistinctPositionByInputPosition(faceVertexOffset + 0) + distinctOffsetsByObject[objectIndex].uvOffset + 1, + objectData.m_uvs.GetDistinctPositionByInputPosition(faceVertexOffset + 1) + distinctOffsetsByObject[objectIndex].uvOffset + 1, + objectData.m_uvs.GetDistinctPositionByInputPosition(faceVertexOffset + 2) + distinctOffsetsByObject[objectIndex].uvOffset + 1, + }; + + m_stream << std::format("f {}/{}/{} {}/{}/{} {}/{}/{}\n", v[0], uv[0], n[0], v[1], uv[1], n[1], v[2], uv[2], n[2]); + faceIndex++; + } + + objectIndex++; + } + } + + void GetObjObjectDataOffsets(const XModelCommon& xmodel, + std::vector& inputOffsets, + std::vector& distinctOffsets) + { + ObjObjectDataOffsets currentInputOffsets{}; + ObjObjectDataOffsets currentDistinctOffsets{}; + + AddObjFaces(xmodel); + + for (const auto& objectData : m_object_data) + { + inputOffsets.push_back(currentInputOffsets); + distinctOffsets.push_back(currentDistinctOffsets); + + currentInputOffsets.vertexOffset += objectData.m_vertices.GetInputValueCount(); + currentInputOffsets.normalOffset += objectData.m_normals.GetInputValueCount(); + currentInputOffsets.uvOffset += objectData.m_uvs.GetInputValueCount(); + currentDistinctOffsets.vertexOffset += objectData.m_vertices.GetDistinctValueCount(); + currentDistinctOffsets.normalOffset += objectData.m_normals.GetDistinctValueCount(); + currentDistinctOffsets.uvOffset += objectData.m_uvs.GetDistinctValueCount(); + } + } + + void AddObjFaces(const XModelCommon& common) + { + m_object_data.resize(common.m_objects.size()); + + auto objectIndex = 0u; + for (const auto& commonObject : common.m_objects) + { + auto& objectData = m_object_data[objectIndex]; + + for (const auto& commonFace : commonObject.m_faces) + { + ObjFace face{}; + face.vertexIndex[0] = commonFace.vertexIndex[2]; + face.vertexIndex[1] = commonFace.vertexIndex[1]; + face.vertexIndex[2] = commonFace.vertexIndex[0]; + face.normalIndex[0] = face.vertexIndex[0]; + face.normalIndex[1] = face.vertexIndex[1]; + face.normalIndex[2] = face.vertexIndex[2]; + face.uvIndex[0] = face.vertexIndex[0]; + face.uvIndex[1] = face.vertexIndex[1]; + face.uvIndex[2] = face.vertexIndex[2]; + + // Reverse order for OBJ + AddObjVertex(objectData, common, commonFace.vertexIndex[2]); + AddObjVertex(objectData, common, commonFace.vertexIndex[1]); + AddObjVertex(objectData, common, commonFace.vertexIndex[0]); + } + + objectIndex++; + } + } + + static void AddObjVertex(ObjObjectData& objectData, const XModelCommon& common, const int commonVertexIndex) + { + const auto& commonVertex = common.m_vertices[commonVertexIndex]; + + ObjVertex objVertex{}; + objVertex.coordinates[0] = commonVertex.coordinates[0]; + objVertex.coordinates[1] = commonVertex.coordinates[2]; + objVertex.coordinates[2] = -commonVertex.coordinates[1]; + objectData.m_vertices.Add(objVertex); + + ObjNormal objNormal{}; + objNormal.normal[0] = commonVertex.normal[0]; + objNormal.normal[1] = commonVertex.normal[2]; + objNormal.normal[2] = -commonVertex.normal[1]; + objectData.m_normals.Add(objNormal); + + ObjUv objUv{}; + objUv.uv[0] = commonVertex.uv[0]; + objUv.uv[1] = 1.0f - commonVertex.uv[1]; + objectData.m_uvs.Add(objUv); + } + + std::ostream& m_stream; + std::string m_mtl_name; + std::string m_game_name; + std::string m_zone_name; + std::vector m_object_data; + }; + + class MtlWriter final : public XModelWriter { - stream << "\n"; - stream << "newmtl " << material.materialName << "\n"; + public: + MtlWriter(std::ostream& stream, std::string gameName, std::string zoneName) + : m_stream(stream), + m_game_name(std::move(gameName)), + m_zone_name(std::move(zoneName)) + { + } - if (!material.colorMapName.empty()) - stream << "map_Kd ../images/" << material.colorMapName << ".dds\n"; + void Write(const XModelCommon& xmodel) override + { + m_stream << "# OpenAssetTools MAT File ( " << m_game_name << ")\n"; + m_stream << "# Game Origin: " << m_game_name << "\n"; + m_stream << "# Zone Origin: " << m_zone_name << "\n"; + m_stream << "# Material count: " << xmodel.m_materials.size() << "\n"; - if (!material.normalMapName.empty()) - stream << "map_bump ../images/" << material.normalMapName << ".dds\n"; + for (const auto& material : xmodel.m_materials) + { + m_stream << "\n"; + m_stream << "newmtl " << material.name << "\n"; - if (!material.specularMapName.empty()) - stream << "map_Ks ../images/" << material.specularMapName << ".dds\n"; + if (!material.colorMapName.empty()) + m_stream << "map_Kd ../images/" << material.colorMapName << ".dds\n"; + + if (!material.normalMapName.empty()) + m_stream << "map_bump ../images/" << material.normalMapName << ".dds\n"; + + if (!material.specularMapName.empty()) + m_stream << "map_Ks ../images/" << material.specularMapName << ".dds\n"; + } + } + + private: + std::ostream& m_stream; + std::string m_game_name; + std::string m_zone_name; + }; +} // namespace + +namespace obj +{ + std::unique_ptr CreateObjWriter(std::ostream& stream, std::string mtlName, std::string gameName, std::string zoneName) + { + return std::make_unique(stream, std::move(mtlName), std::move(gameName), std::move(zoneName)); } -} + + std::unique_ptr CreateMtlWriter(std::ostream& stream, std::string gameName, std::string zoneName) + { + return std::make_unique(stream, std::move(gameName), std::move(zoneName)); + } +} // namespace obj diff --git a/src/ObjWriting/XModel/Obj/ObjWriter.h b/src/ObjWriting/XModel/Obj/ObjWriter.h index 8f809282..5d1ff9dd 100644 --- a/src/ObjWriting/XModel/Obj/ObjWriter.h +++ b/src/ObjWriting/XModel/Obj/ObjWriter.h @@ -1,48 +1,11 @@ #pragma once -#include "Utils/DistinctMapper.h" -#include "XModel/Obj/ObjCommon.h" +#include "XModel/XModelWriter.h" #include -#include -class ObjWriter +namespace obj { -protected: - struct ObjObjectData - { - DistinctMapper m_vertices; - DistinctMapper m_normals; - DistinctMapper m_uvs; - std::vector m_faces; - }; - - struct ObjObjectDataOffsets - { - size_t vertexOffset; - size_t normalOffset; - size_t uvOffset; - }; - - std::string m_game_name; - std::string m_zone_name; - std::vector m_objects; - std::vector m_object_data; - std::vector m_materials; - - void GetObjObjectDataOffsets(std::vector& inputOffsets, std::vector& distinctOffsets); - -public: - ObjWriter(std::string gameName, std::string zoneName); - - void AddObject(ObjObject object); - void AddMaterial(MtlMaterial material); - void AddVertex(int objectId, ObjVertex vertex); - void AddNormal(int objectId, ObjNormal normal); - void AddUv(int objectId, ObjUv uv); - void AddFace(int objectId, ObjFace face); - - void WriteObj(std::ostream& stream); - void WriteObj(std::ostream& stream, const std::string& mtlName); - void WriteMtl(std::ostream& stream); -}; + std::unique_ptr CreateObjWriter(std::ostream& stream, std::string mtlName, std::string gameName, std::string zoneName); + std::unique_ptr CreateMtlWriter(std::ostream& stream, std::string gameName, std::string zoneName); +} // namespace obj From 07fa12b7f6b80b080737603dd1c05d23674cf7cd Mon Sep 17 00:00:00 2001 From: Jan Date: Sat, 27 Apr 2024 10:35:00 +0200 Subject: [PATCH 15/25] chore: implement mesh gltf export --- src/ObjCommon/XModel/Gltf/GltfConstants.h | 6 + src/ObjCommon/XModel/Gltf/JsonGltf.h | 34 ++- src/ObjWriting/XModel/Gltf/GltfTextOutput.cpp | 9 +- src/ObjWriting/XModel/Gltf/GltfWriter.cpp | 212 +++++++++++++++++- 4 files changed, 251 insertions(+), 10 deletions(-) diff --git a/src/ObjCommon/XModel/Gltf/GltfConstants.h b/src/ObjCommon/XModel/Gltf/GltfConstants.h index 1ce687d0..a83f4e2e 100644 --- a/src/ObjCommon/XModel/Gltf/GltfConstants.h +++ b/src/ObjCommon/XModel/Gltf/GltfConstants.h @@ -18,4 +18,10 @@ namespace gltf constexpr auto GLTF_JSON_CHUNK_DATA_OFFSET = 20u; constexpr auto GLTF_DATA_URI_PREFIX = "data:application/octet-stream;base64,"; + + constexpr auto GLTF_ATTRIBUTE_POSITION = "POSITION"; + constexpr auto GLTF_ATTRIBUTE_NORMAL = "NORMAL"; + constexpr auto GLTF_ATTRIBUTE_TEXCOORD_0 = "TEXCOORD_0"; + constexpr auto GLTF_ATTRIBUTE_JOINTS_0 = "JOINTS_0"; + constexpr auto GLTF_ATTRIBUTE_WEIGHTS_0 = "WEIGHTS_0"; } // namespace gltf diff --git a/src/ObjCommon/XModel/Gltf/JsonGltf.h b/src/ObjCommon/XModel/Gltf/JsonGltf.h index 16eb8104..dccbc794 100644 --- a/src/ObjCommon/XModel/Gltf/JsonGltf.h +++ b/src/ObjCommon/XModel/Gltf/JsonGltf.h @@ -102,16 +102,29 @@ namespace gltf NLOHMANN_DEFINE_TYPE_EXTENSION(JsonAccessor, bufferView, byteOffset, componentType, count, type, min, max); + enum class JsonBufferViewTarget + { + ARRAY_BUFFER = 34962, + ELEMENT_ARRAY_BUFFER = 34963 + }; + + NLOHMANN_JSON_SERIALIZE_ENUM(JsonBufferViewTarget, + { + {JsonBufferViewTarget::ARRAY_BUFFER, static_cast(JsonBufferViewTarget::ARRAY_BUFFER) }, + {JsonBufferViewTarget::ELEMENT_ARRAY_BUFFER, static_cast(JsonBufferViewTarget::ELEMENT_ARRAY_BUFFER)}, + }); + class JsonBufferView { public: unsigned buffer; unsigned byteLength; - unsigned byteOffset; - unsigned target; + std::optional byteOffset; + std::optional byteStride; + std::optional target; }; - NLOHMANN_DEFINE_TYPE_EXTENSION(JsonBufferView, buffer, byteLength, byteOffset, target); + NLOHMANN_DEFINE_TYPE_EXTENSION(JsonBufferView, buffer, byteLength, byteOffset, byteStride, target); enum class JsonAnimationChannelTargetPath { @@ -211,10 +224,23 @@ namespace gltf {JsonMeshPrimitivesMode::TRIANGLE_FAN, static_cast(JsonMeshPrimitivesMode::TRIANGLE_FAN) }, }); + // This should probably be a map, but the supported models do not have arbitrary primitives anyway + class JsonMeshPrimitivesAttributes + { + public: + std::optional POSITION; + std::optional NORMAL; + std::optional TEXCOORD_0; + std::optional JOINTS_0; + std::optional WEIGHTS_0; + }; + + NLOHMANN_DEFINE_TYPE_EXTENSION(JsonMeshPrimitivesAttributes, POSITION, NORMAL, TEXCOORD_0, JOINTS_0, WEIGHTS_0); + class JsonMeshPrimitives { public: - std::unordered_map attributes; + JsonMeshPrimitivesAttributes attributes; std::optional indices; std::optional material; std::optional mode; diff --git a/src/ObjWriting/XModel/Gltf/GltfTextOutput.cpp b/src/ObjWriting/XModel/Gltf/GltfTextOutput.cpp index 267e5f68..f04838a3 100644 --- a/src/ObjWriting/XModel/Gltf/GltfTextOutput.cpp +++ b/src/ObjWriting/XModel/Gltf/GltfTextOutput.cpp @@ -5,6 +5,8 @@ #include #include + +#define LTC_NO_PROTOTYPES #include using namespace gltf; @@ -17,16 +19,17 @@ TextOutput::TextOutput(std::ostream& stream) std::optional TextOutput::CreateBufferUri(const void* buffer, const size_t bufferSize) const { static constexpr auto URI_PREFIX_LENGTH = std::char_traits::length(GLTF_DATA_URI_PREFIX); - const auto base64Length = utils::Align(4u * (bufferSize / 3u), 4u); + const auto base64Length = 4u * ((bufferSize + 2u) / 3u); const auto base64BufferSize = URI_PREFIX_LENGTH + base64Length; std::string output(base64BufferSize, '\0'); std::memcpy(output.data(), GLTF_DATA_URI_PREFIX, URI_PREFIX_LENGTH); - unsigned long outLength = base64Length; - base64_encode(static_cast(buffer), bufferSize, &output[URI_PREFIX_LENGTH], &outLength); + unsigned long outLength = base64Length + 1u; + const auto result = base64_encode(static_cast(buffer), bufferSize, &output[URI_PREFIX_LENGTH], &outLength); + assert(result == CRYPT_OK); assert(outLength == base64Length); return output; diff --git a/src/ObjWriting/XModel/Gltf/GltfWriter.cpp b/src/ObjWriting/XModel/Gltf/GltfWriter.cpp index 05b22839..18fe88dc 100644 --- a/src/ObjWriting/XModel/Gltf/GltfWriter.cpp +++ b/src/ObjWriting/XModel/Gltf/GltfWriter.cpp @@ -11,6 +11,13 @@ namespace { constexpr auto GLTF_GENERATOR = "OpenAssetTools " GIT_VERSION; + struct GltfVertex + { + float coordinates[3]; + float normal[3]; + float uv[2]; + }; + class GltfWriterImpl final : public gltf::Writer { static constexpr auto NODE_INDEX_MESH = 0u; @@ -27,15 +34,26 @@ namespace void Write(const XModelCommon& xmodel) override { JsonRoot gltf; + std::vector bufferData; + CreateJsonAsset(gltf.asset); CreateMeshNode(gltf, xmodel); - CreateMesh(gltf, xmodel); + CreateMaterials(gltf, xmodel); CreateSkeletonNodes(gltf, xmodel); CreateSkin(gltf, xmodel); + CreateBufferViews(gltf, xmodel); + CreateAccessors(gltf, xmodel); + CreateMesh(gltf, xmodel); CreateScene(gltf, xmodel); + FillBufferData(xmodel, bufferData); + CreateBuffer(gltf, xmodel, bufferData); const json jRoot = gltf; m_output->EmitJson(jRoot); + + if (!bufferData.empty()) + m_output->EmitBuffer(bufferData.data(), bufferData.size()); + m_output->Finalize(); } @@ -66,16 +84,50 @@ namespace gltf.nodes->emplace_back(std::move(meshNode)); } - static void CreateMesh(JsonRoot& gltf, const XModelCommon& xmodel) + void CreateMesh(JsonRoot& gltf, const XModelCommon& xmodel) { if (!gltf.meshes.has_value()) gltf.meshes.emplace(); JsonMesh mesh; + auto objectIndex = 0u; + for (const auto& object : xmodel.m_objects) + { + JsonMeshPrimitives primitives; + + if (object.materialIndex >= 0) + primitives.material = static_cast(object.materialIndex); + + primitives.attributes.POSITION = m_position_accessor; + primitives.attributes.NORMAL = m_normal_accessor; + primitives.attributes.TEXCOORD_0 = m_uv_accessor; + + primitives.mode = JsonMeshPrimitivesMode::TRIANGLES; + primitives.indices = m_first_index_accessor + objectIndex; + + mesh.primitives.emplace_back(primitives); + objectIndex++; + } + gltf.meshes->emplace_back(std::move(mesh)); } + static void CreateMaterials(JsonRoot& gltf, const XModelCommon& xmodel) + { + if (!gltf.materials.has_value()) + gltf.materials.emplace(); + + for (const auto& modelMaterial : xmodel.m_materials) + { + JsonMaterial material; + + material.name = modelMaterial.name; + + gltf.materials->emplace_back(material); + } + } + static void CreateSkeletonNodes(JsonRoot& gltf, const XModelCommon& xmodel) { if (xmodel.m_bones.empty()) @@ -98,7 +150,7 @@ namespace for (auto maybeChildIndex = 0u; maybeChildIndex < boneCount; maybeChildIndex++) { if (xmodel.m_bones[maybeChildIndex].parentIndex == static_cast(boneIndex)) - children.emplace_back(boneIndex + NODE_FIRST_INDEX_BONES); + children.emplace_back(maybeChildIndex + NODE_FIRST_INDEX_BONES); } if (!children.empty()) boneNode.children = std::move(children); @@ -143,7 +195,161 @@ namespace gltf.scene = 0u; } + void CreateBufferViews(JsonRoot& gltf, const XModelCommon& xmodel) + { + if (!gltf.bufferViews.has_value()) + gltf.bufferViews.emplace(); + + unsigned bufferOffset = 0u; + + JsonBufferView vertexBufferView; + vertexBufferView.buffer = 0u; + vertexBufferView.byteOffset = bufferOffset; + vertexBufferView.byteStride = sizeof(GltfVertex); + vertexBufferView.byteLength = sizeof(GltfVertex) * xmodel.m_vertices.size(); + vertexBufferView.target = JsonBufferViewTarget::ARRAY_BUFFER; + bufferOffset += vertexBufferView.byteLength; + + m_vertex_buffer_view = gltf.bufferViews->size(); + gltf.bufferViews->emplace_back(vertexBufferView); + + m_first_index_buffer_view = gltf.bufferViews->size(); + for (const auto& object : xmodel.m_objects) + { + JsonBufferView indicesBufferView; + indicesBufferView.buffer = 0u; + indicesBufferView.byteOffset = bufferOffset; + indicesBufferView.byteLength = sizeof(unsigned short) * object.m_faces.size() * 3u; + indicesBufferView.target = JsonBufferViewTarget::ELEMENT_ARRAY_BUFFER; + bufferOffset += indicesBufferView.byteLength; + + gltf.bufferViews->emplace_back(indicesBufferView); + } + } + + void CreateAccessors(JsonRoot& gltf, const XModelCommon& xmodel) + { + if (!gltf.accessors.has_value()) + gltf.accessors.emplace(); + + JsonAccessor positionAccessor; + positionAccessor.bufferView = m_vertex_buffer_view; + positionAccessor.byteOffset = offsetof(GltfVertex, coordinates); + positionAccessor.componentType = JsonAccessorComponentType::FLOAT; + positionAccessor.count = xmodel.m_vertices.size(); + positionAccessor.type = JsonAccessorType::VEC3; + m_position_accessor = gltf.accessors->size(); + gltf.accessors->emplace_back(positionAccessor); + + JsonAccessor normalAccessor; + normalAccessor.bufferView = m_vertex_buffer_view; + normalAccessor.byteOffset = offsetof(GltfVertex, normal); + normalAccessor.componentType = JsonAccessorComponentType::FLOAT; + normalAccessor.count = xmodel.m_vertices.size(); + normalAccessor.type = JsonAccessorType::VEC3; + m_normal_accessor = gltf.accessors->size(); + gltf.accessors->emplace_back(normalAccessor); + + JsonAccessor uvAccessor; + uvAccessor.bufferView = m_vertex_buffer_view; + uvAccessor.byteOffset = offsetof(GltfVertex, uv); + uvAccessor.componentType = JsonAccessorComponentType::FLOAT; + uvAccessor.count = xmodel.m_vertices.size(); + uvAccessor.type = JsonAccessorType::VEC2; + m_uv_accessor = gltf.accessors->size(); + gltf.accessors->emplace_back(uvAccessor); + + m_first_index_accessor = gltf.accessors->size(); + for (auto i = 0u; i < xmodel.m_objects.size(); i++) + { + const auto& object = xmodel.m_objects[i]; + + JsonAccessor indicesAccessor; + indicesAccessor.bufferView = m_first_index_buffer_view + i; + indicesAccessor.componentType = JsonAccessorComponentType::UNSIGNED_SHORT; + indicesAccessor.count = object.m_faces.size() * 3u; + indicesAccessor.type = JsonAccessorType::SCALAR; + + gltf.accessors->emplace_back(indicesAccessor); + } + } + + static void FillBufferData(const XModelCommon& xmodel, std::vector& bufferData) + { + const auto expectedBufferSize = GetExpectedBufferSize(xmodel); + bufferData.resize(expectedBufferSize); + + auto currentBufferOffset = 0u; + + for (const auto& commonVertex : xmodel.m_vertices) + { + auto* vertex = reinterpret_cast(&bufferData[currentBufferOffset]); + + vertex->coordinates[0] = commonVertex.coordinates[0]; + vertex->coordinates[1] = commonVertex.coordinates[2]; + vertex->coordinates[2] = -commonVertex.coordinates[1]; + + vertex->normal[0] = commonVertex.normal[0]; + vertex->normal[1] = commonVertex.normal[2]; + vertex->normal[2] = -commonVertex.normal[1]; + + vertex->uv[0] = commonVertex.uv[0]; + vertex->uv[1] = commonVertex.uv[1]; + + currentBufferOffset += sizeof(GltfVertex); + } + + for (const auto& object : xmodel.m_objects) + { + for (const auto& face : object.m_faces) + { + auto* faceIndices = reinterpret_cast(&bufferData[currentBufferOffset]); + faceIndices[0] = static_cast(face.vertexIndex[2]); + faceIndices[1] = static_cast(face.vertexIndex[1]); + faceIndices[2] = static_cast(face.vertexIndex[0]); + + currentBufferOffset += sizeof(unsigned short) * 3u; + } + } + + assert(expectedBufferSize == currentBufferOffset); + } + + static size_t GetExpectedBufferSize(const XModelCommon& xmodel) + { + auto result = 0u; + + result += xmodel.m_vertices.size() * sizeof(GltfVertex); + + for (const auto& object : xmodel.m_objects) + { + result += object.m_faces.size() * sizeof(unsigned short) * 3u; + } + + return result; + } + + void CreateBuffer(JsonRoot& gltf, const XModelCommon& xmodel, const std::vector& bufferData) const + { + if (!gltf.buffers.has_value()) + gltf.buffers.emplace(); + + JsonBuffer jsonBuffer; + jsonBuffer.byteLength = bufferData.size(); + + if (!bufferData.empty()) + jsonBuffer.uri = m_output->CreateBufferUri(bufferData.data(), bufferData.size()); + + gltf.buffers->emplace_back(std::move(jsonBuffer)); + } + unsigned m_skeleton_node = 0u; + unsigned m_position_accessor = 0u; + unsigned m_normal_accessor = 0u; + unsigned m_uv_accessor = 0u; + unsigned m_vertex_buffer_view = 0u; + unsigned m_first_index_buffer_view = 0u; + unsigned m_first_index_accessor = 0u; const Output* m_output; std::string m_game_name; From c587dae05f1217fc76d38332cff0318a7a4817d9 Mon Sep 17 00:00:00 2001 From: Jan Date: Wed, 1 May 2024 00:19:21 +0200 Subject: [PATCH 16/25] fix: build on linux --- src/ObjWriting/XModel/Obj/ObjWriter.cpp | 2 ++ src/ObjWriting/XModel/Obj/ObjWriter.h | 1 + 2 files changed, 3 insertions(+) diff --git a/src/ObjWriting/XModel/Obj/ObjWriter.cpp b/src/ObjWriting/XModel/Obj/ObjWriter.cpp index e0538943..a883997c 100644 --- a/src/ObjWriting/XModel/Obj/ObjWriter.cpp +++ b/src/ObjWriting/XModel/Obj/ObjWriter.cpp @@ -3,6 +3,8 @@ #include "Utils/DistinctMapper.h" #include "XModel/Obj/ObjCommon.h" +#include + namespace { struct ObjObjectData diff --git a/src/ObjWriting/XModel/Obj/ObjWriter.h b/src/ObjWriting/XModel/Obj/ObjWriter.h index 5d1ff9dd..ef2092b7 100644 --- a/src/ObjWriting/XModel/Obj/ObjWriter.h +++ b/src/ObjWriting/XModel/Obj/ObjWriter.h @@ -2,6 +2,7 @@ #include "XModel/XModelWriter.h" +#include #include namespace obj From ebccd676763b7ae5740114764ac28d818503a2c6 Mon Sep 17 00:00:00 2001 From: Jan Date: Sat, 4 May 2024 20:06:31 +0200 Subject: [PATCH 17/25] chore: fill dumped gltf min max fields for position accessor --- src/ObjWriting/XModel/Gltf/GltfWriter.cpp | 34 +++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/src/ObjWriting/XModel/Gltf/GltfWriter.cpp b/src/ObjWriting/XModel/Gltf/GltfWriter.cpp index 18fe88dc..3db6698b 100644 --- a/src/ObjWriting/XModel/Gltf/GltfWriter.cpp +++ b/src/ObjWriting/XModel/Gltf/GltfWriter.cpp @@ -45,7 +45,7 @@ namespace CreateAccessors(gltf, xmodel); CreateMesh(gltf, xmodel); CreateScene(gltf, xmodel); - FillBufferData(xmodel, bufferData); + FillBufferData(gltf, xmodel, bufferData); CreateBuffer(gltf, xmodel, bufferData); const json jRoot = gltf; @@ -274,13 +274,24 @@ namespace } } - static void FillBufferData(const XModelCommon& xmodel, std::vector& bufferData) + void FillBufferData(JsonRoot& gltf, const XModelCommon& xmodel, std::vector& bufferData) const { const auto expectedBufferSize = GetExpectedBufferSize(xmodel); bufferData.resize(expectedBufferSize); auto currentBufferOffset = 0u; + float minPosition[3]{ + std::numeric_limits::max(), + std::numeric_limits::max(), + std::numeric_limits::max(), + }; + float maxPosition[3]{ + -std::numeric_limits::max(), + -std::numeric_limits::max(), + -std::numeric_limits::max(), + }; + for (const auto& commonVertex : xmodel.m_vertices) { auto* vertex = reinterpret_cast(&bufferData[currentBufferOffset]); @@ -289,6 +300,19 @@ namespace vertex->coordinates[1] = commonVertex.coordinates[2]; vertex->coordinates[2] = -commonVertex.coordinates[1]; + if (minPosition[0] > vertex->coordinates[0]) + minPosition[0] = vertex->coordinates[0]; + if (minPosition[1] > vertex->coordinates[1]) + minPosition[1] = vertex->coordinates[1]; + if (minPosition[2] > vertex->coordinates[2]) + minPosition[2] = vertex->coordinates[2]; + if (maxPosition[0] < vertex->coordinates[0]) + maxPosition[0] = vertex->coordinates[0]; + if (maxPosition[1] < vertex->coordinates[1]) + maxPosition[1] = vertex->coordinates[1]; + if (maxPosition[2] < vertex->coordinates[2]) + maxPosition[2] = vertex->coordinates[2]; + vertex->normal[0] = commonVertex.normal[0]; vertex->normal[1] = commonVertex.normal[2]; vertex->normal[2] = -commonVertex.normal[1]; @@ -299,6 +323,12 @@ namespace currentBufferOffset += sizeof(GltfVertex); } + if (gltf.accessors.has_value()) + { + gltf.accessors.value()[m_position_accessor].min = std::vector({minPosition[0], minPosition[1], minPosition[2]}); + gltf.accessors.value()[m_position_accessor].max = std::vector({maxPosition[0], maxPosition[1], maxPosition[2]}); + } + for (const auto& object : xmodel.m_objects) { for (const auto& face : object.m_faces) From 1a35152098b9dab13c116e76a4d5fcc8a70bf115 Mon Sep 17 00:00:00 2001 From: Jan Date: Sat, 4 May 2024 21:58:18 +0200 Subject: [PATCH 18/25] chore: dump more information about gltf materials --- src/ObjCommon/XModel/Gltf/JsonGltf.h | 50 ++++++++++++++++++++++- src/ObjWriting/XModel/Gltf/GltfWriter.cpp | 42 +++++++++++++++++++ 2 files changed, 90 insertions(+), 2 deletions(-) diff --git a/src/ObjCommon/XModel/Gltf/JsonGltf.h b/src/ObjCommon/XModel/Gltf/JsonGltf.h index dccbc794..f7b18b12 100644 --- a/src/ObjCommon/XModel/Gltf/JsonGltf.h +++ b/src/ObjCommon/XModel/Gltf/JsonGltf.h @@ -194,13 +194,40 @@ namespace gltf NLOHMANN_DEFINE_TYPE_EXTENSION(JsonAnimation, channels, samplers, name); + class JsonTextureInfo + { + public: + unsigned index; + }; + + NLOHMANN_DEFINE_TYPE_EXTENSION(JsonTextureInfo, index); + + class JsonPbrMetallicRoughness + { + public: + std::optional baseColorTexture; + }; + + NLOHMANN_DEFINE_TYPE_EXTENSION(JsonPbrMetallicRoughness, baseColorTexture); + + class JsonNormalTextureInfo + { + public: + unsigned index; + }; + + NLOHMANN_DEFINE_TYPE_EXTENSION(JsonNormalTextureInfo, index); + class JsonMaterial { public: std::optional name; + std::optional pbrMetallicRoughness; + std::optional normalTexture; + std::optional doubleSided; }; - NLOHMANN_DEFINE_TYPE_EXTENSION(JsonMaterial, name); + NLOHMANN_DEFINE_TYPE_EXTENSION(JsonMaterial, name, pbrMetallicRoughness, normalTexture, doubleSided); enum class JsonMeshPrimitivesMode { @@ -276,6 +303,22 @@ namespace gltf NLOHMANN_DEFINE_TYPE_EXTENSION(JsonScene, nodes, name); + class JsonTexture + { + public: + unsigned source; + }; + + NLOHMANN_DEFINE_TYPE_EXTENSION(JsonTexture, source); + + class JsonImage + { + public: + std::optional uri; + }; + + NLOHMANN_DEFINE_TYPE_EXTENSION(JsonImage, uri); + class JsonRoot { public: @@ -284,13 +327,16 @@ namespace gltf JsonAsset asset; std::optional> buffers; std::optional> bufferViews; + std::optional> images; std::optional> materials; std::optional> meshes; std::optional> nodes; std::optional> skins; std::optional scene; std::optional> scenes; + std::optional> textures; }; - NLOHMANN_DEFINE_TYPE_EXTENSION(JsonRoot, accessors, animations, asset, buffers, bufferViews, materials, meshes, nodes, skins, scene, scenes); + NLOHMANN_DEFINE_TYPE_EXTENSION( + JsonRoot, accessors, animations, asset, buffers, bufferViews, images, materials, meshes, nodes, skins, scene, scenes, textures); } // namespace gltf diff --git a/src/ObjWriting/XModel/Gltf/GltfWriter.cpp b/src/ObjWriting/XModel/Gltf/GltfWriter.cpp index 3db6698b..ed640ba9 100644 --- a/src/ObjWriting/XModel/Gltf/GltfWriter.cpp +++ b/src/ObjWriting/XModel/Gltf/GltfWriter.cpp @@ -4,6 +4,8 @@ #include "XModel/Gltf/GltfConstants.h" #include "XModel/Gltf/JsonGltf.h" +#include + using namespace gltf; using namespace nlohmann; @@ -124,10 +126,50 @@ namespace material.name = modelMaterial.name; + if (!modelMaterial.colorMapName.empty()) + material.pbrMetallicRoughness.emplace().baseColorTexture.emplace().index = CreateTexture(gltf, modelMaterial.colorMapName); + + if (!modelMaterial.normalMapName.empty()) + material.normalTexture.emplace().index = CreateTexture(gltf, modelMaterial.colorMapName); + + material.doubleSided = true; gltf.materials->emplace_back(material); } } + static unsigned CreateTexture(JsonRoot& gltf, const std::string& textureName) + { + if (!gltf.textures.has_value()) + gltf.textures.emplace(); + if (!gltf.images.has_value()) + gltf.images.emplace(); + + auto uri = std::format("../images/{}.dds", textureName); + + auto existingTexIndex = 0u; + for (const auto& existingTex : gltf.textures.value()) + { + const auto& existingImage = gltf.images.value()[existingTex.source]; + + if (existingImage.uri == uri) + return existingTexIndex; + + existingTexIndex++; + } + + JsonImage image; + image.uri = std::move(uri); + const auto imageIndex = gltf.images->size(); + gltf.images->emplace_back(std::move(image)); + + JsonTexture texture; + texture.source = imageIndex; + const auto textureIndex = gltf.textures->size(); + gltf.textures->emplace_back(texture); + + return textureIndex; + } + static void CreateSkeletonNodes(JsonRoot& gltf, const XModelCommon& xmodel) { if (xmodel.m_bones.empty()) From a0138c8abc3619077a1f20838fb2b04327bcb40e Mon Sep 17 00:00:00 2001 From: Jan Date: Sat, 4 May 2024 22:10:58 +0200 Subject: [PATCH 19/25] chore: set gltf material default metallicness to 0 --- src/ObjCommon/XModel/Gltf/JsonGltf.h | 3 ++- src/ObjWriting/XModel/Gltf/GltfWriter.cpp | 8 +++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/ObjCommon/XModel/Gltf/JsonGltf.h b/src/ObjCommon/XModel/Gltf/JsonGltf.h index f7b18b12..2f3a15d4 100644 --- a/src/ObjCommon/XModel/Gltf/JsonGltf.h +++ b/src/ObjCommon/XModel/Gltf/JsonGltf.h @@ -206,9 +206,10 @@ namespace gltf { public: std::optional baseColorTexture; + std::optional metallicFactor; }; - NLOHMANN_DEFINE_TYPE_EXTENSION(JsonPbrMetallicRoughness, baseColorTexture); + NLOHMANN_DEFINE_TYPE_EXTENSION(JsonPbrMetallicRoughness, baseColorTexture, metallicFactor); class JsonNormalTextureInfo { diff --git a/src/ObjWriting/XModel/Gltf/GltfWriter.cpp b/src/ObjWriting/XModel/Gltf/GltfWriter.cpp index ed640ba9..3feeada7 100644 --- a/src/ObjWriting/XModel/Gltf/GltfWriter.cpp +++ b/src/ObjWriting/XModel/Gltf/GltfWriter.cpp @@ -127,7 +127,13 @@ namespace material.name = modelMaterial.name; if (!modelMaterial.colorMapName.empty()) - material.pbrMetallicRoughness.emplace().baseColorTexture.emplace().index = CreateTexture(gltf, modelMaterial.colorMapName); + { + material.pbrMetallicRoughness.emplace(); + material.pbrMetallicRoughness->baseColorTexture.emplace(); + + material.pbrMetallicRoughness->baseColorTexture->index = CreateTexture(gltf, modelMaterial.colorMapName); + material.pbrMetallicRoughness->metallicFactor = 0.0f; + } if (!modelMaterial.normalMapName.empty()) material.normalTexture.emplace().index = CreateTexture(gltf, modelMaterial.colorMapName); From 9ab78d53840085d886405416903fcd112e84b713 Mon Sep 17 00:00:00 2001 From: Jan Date: Sat, 4 May 2024 23:00:05 +0200 Subject: [PATCH 20/25] fix: correct asset references in xmodel material dumping --- .../Game/IW3/AssetDumpers/AssetDumperXModel.cpp | 16 ++++++++++++---- .../Game/IW4/AssetDumpers/AssetDumperXModel.cpp | 16 ++++++++++++---- .../Game/IW5/AssetDumpers/AssetDumperXModel.cpp | 16 ++++++++++++---- .../Game/T5/AssetDumpers/AssetDumperXModel.cpp | 16 ++++++++++++---- .../Game/T6/AssetDumpers/AssetDumperXModel.cpp | 16 ++++++++++++---- 5 files changed, 60 insertions(+), 20 deletions(-) diff --git a/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperXModel.cpp b/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperXModel.cpp index ee73a0b6..df8dad55 100644 --- a/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperXModel.cpp +++ b/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperXModel.cpp @@ -150,6 +150,14 @@ namespace } } + 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 = 0; surfaceMaterialNum < model->numsurfs; surfaceMaterialNum++) @@ -160,18 +168,18 @@ namespace XModelMaterial xMaterial; xMaterial.ApplyDefaults(); - xMaterial.name = material->info.name; + xMaterial.name = AssetName(material->info.name); const auto* colorMap = GetMaterialColorMap(material); if (colorMap) - xMaterial.colorMapName = std::string(colorMap->name); + xMaterial.colorMapName = AssetName(colorMap->name); const auto* normalMap = GetMaterialNormalMap(material); if (normalMap) - xMaterial.normalMapName = std::string(normalMap->name); + xMaterial.normalMapName = AssetName(normalMap->name); const auto* specularMap = GetMaterialSpecularMap(material); if (specularMap) - xMaterial.specularMapName = std::string(specularMap->name); + xMaterial.specularMapName = AssetName(specularMap->name); out.m_materials.emplace_back(std::move(xMaterial)); } diff --git a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperXModel.cpp b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperXModel.cpp index b1019067..f88bcc3b 100644 --- a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperXModel.cpp +++ b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperXModel.cpp @@ -145,6 +145,14 @@ namespace } } + 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 = 0; surfaceMaterialNum < model->numsurfs; surfaceMaterialNum++) @@ -155,18 +163,18 @@ namespace XModelMaterial xMaterial; xMaterial.ApplyDefaults(); - xMaterial.name = material->info.name; + xMaterial.name = AssetName(material->info.name); const auto* colorMap = GetMaterialColorMap(material); if (colorMap) - xMaterial.colorMapName = std::string(colorMap->name); + xMaterial.colorMapName = AssetName(colorMap->name); const auto* normalMap = GetMaterialNormalMap(material); if (normalMap) - xMaterial.normalMapName = std::string(normalMap->name); + xMaterial.normalMapName = AssetName(normalMap->name); const auto* specularMap = GetMaterialSpecularMap(material); if (specularMap) - xMaterial.specularMapName = std::string(specularMap->name); + xMaterial.specularMapName = AssetName(specularMap->name); out.m_materials.emplace_back(std::move(xMaterial)); } diff --git a/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperXModel.cpp b/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperXModel.cpp index b9cb4435..cef5b987 100644 --- a/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperXModel.cpp +++ b/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperXModel.cpp @@ -145,6 +145,14 @@ namespace } } + 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 = 0; surfaceMaterialNum < model->numsurfs; surfaceMaterialNum++) @@ -155,18 +163,18 @@ namespace XModelMaterial xMaterial; xMaterial.ApplyDefaults(); - xMaterial.name = material->info.name; + xMaterial.name = AssetName(material->info.name); const auto* colorMap = GetMaterialColorMap(material); if (colorMap) - xMaterial.colorMapName = std::string(colorMap->name); + xMaterial.colorMapName = AssetName(colorMap->name); const auto* normalMap = GetMaterialNormalMap(material); if (normalMap) - xMaterial.normalMapName = std::string(normalMap->name); + xMaterial.normalMapName = AssetName(normalMap->name); const auto* specularMap = GetMaterialSpecularMap(material); if (specularMap) - xMaterial.specularMapName = std::string(specularMap->name); + xMaterial.specularMapName = AssetName(specularMap->name); out.m_materials.emplace_back(std::move(xMaterial)); } diff --git a/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperXModel.cpp b/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperXModel.cpp index ad9c3657..6538f7c7 100644 --- a/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperXModel.cpp +++ b/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperXModel.cpp @@ -150,6 +150,14 @@ namespace } } + 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 = 0; surfaceMaterialNum < model->numsurfs; surfaceMaterialNum++) @@ -160,18 +168,18 @@ namespace XModelMaterial xMaterial; xMaterial.ApplyDefaults(); - xMaterial.name = material->info.name; + xMaterial.name = AssetName(material->info.name); const auto* colorMap = GetMaterialColorMap(material); if (colorMap) - xMaterial.colorMapName = std::string(colorMap->name); + xMaterial.colorMapName = AssetName(colorMap->name); const auto* normalMap = GetMaterialNormalMap(material); if (normalMap) - xMaterial.normalMapName = std::string(normalMap->name); + xMaterial.normalMapName = AssetName(normalMap->name); const auto* specularMap = GetMaterialSpecularMap(material); if (specularMap) - xMaterial.specularMapName = std::string(specularMap->name); + xMaterial.specularMapName = AssetName(specularMap->name); out.m_materials.emplace_back(std::move(xMaterial)); } diff --git a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperXModel.cpp b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperXModel.cpp index 794367ed..f2cf9370 100644 --- a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperXModel.cpp +++ b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperXModel.cpp @@ -162,6 +162,14 @@ namespace } } + 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 = 0; surfaceMaterialNum < model->numsurfs; surfaceMaterialNum++) @@ -172,18 +180,18 @@ namespace XModelMaterial xMaterial; xMaterial.ApplyDefaults(); - xMaterial.name = material->info.name; + xMaterial.name = AssetName(material->info.name); const auto* colorMap = GetMaterialColorMap(material); if (colorMap) - xMaterial.colorMapName = std::string(colorMap->name); + xMaterial.colorMapName = AssetName(colorMap->name); const auto* normalMap = GetMaterialNormalMap(material); if (normalMap) - xMaterial.normalMapName = std::string(normalMap->name); + xMaterial.normalMapName = AssetName(normalMap->name); const auto* specularMap = GetMaterialSpecularMap(material); if (specularMap) - xMaterial.specularMapName = std::string(specularMap->name); + xMaterial.specularMapName = AssetName(specularMap->name); out.m_materials.emplace_back(std::move(xMaterial)); } From c74be5e8aea3c909affecde356e6b2b1d55dfed9 Mon Sep 17 00:00:00 2001 From: Jan Date: Sat, 4 May 2024 23:59:43 +0200 Subject: [PATCH 21/25] chore: dump gltf vertex weights --- src/ObjWriting/XModel/Gltf/GltfWriter.cpp | 133 +++++++++++++++++++--- src/Utils/Math/Quaternion.h | 90 +++++++++++++++ src/Utils/Math/Vector.h | 102 +++++++++++++++++ 3 files changed, 308 insertions(+), 17 deletions(-) diff --git a/src/ObjWriting/XModel/Gltf/GltfWriter.cpp b/src/ObjWriting/XModel/Gltf/GltfWriter.cpp index 3feeada7..2f148aad 100644 --- a/src/ObjWriting/XModel/Gltf/GltfWriter.cpp +++ b/src/ObjWriting/XModel/Gltf/GltfWriter.cpp @@ -1,6 +1,7 @@ #include "GltfWriter.h" #include "GitVersion.h" +#include "Math/Vector.h" #include "XModel/Gltf/GltfConstants.h" #include "XModel/Gltf/JsonGltf.h" @@ -22,9 +23,6 @@ namespace class GltfWriterImpl final : public gltf::Writer { - static constexpr auto NODE_INDEX_MESH = 0u; - static constexpr auto NODE_FIRST_INDEX_BONES = 1u; - public: GltfWriterImpl(const Output* output, std::string gameName, std::string zoneName) : m_output(output), @@ -40,8 +38,8 @@ namespace CreateJsonAsset(gltf.asset); CreateMeshNode(gltf, xmodel); - CreateMaterials(gltf, xmodel); CreateSkeletonNodes(gltf, xmodel); + CreateMaterials(gltf, xmodel); CreateSkin(gltf, xmodel); CreateBufferViews(gltf, xmodel); CreateAccessors(gltf, xmodel); @@ -66,7 +64,7 @@ namespace asset.generator = GLTF_GENERATOR; } - static void CreateMeshNode(JsonRoot& gltf, const XModelCommon& xmodel) + void CreateMeshNode(JsonRoot& gltf, const XModelCommon& xmodel) { JsonNode meshNode; @@ -83,6 +81,7 @@ namespace if (!gltf.nodes.has_value()) gltf.nodes.emplace(); + m_mesh_node = gltf.nodes->size(); gltf.nodes->emplace_back(std::move(meshNode)); } @@ -93,6 +92,8 @@ namespace JsonMesh mesh; + const auto hasBoneWeightData = !xmodel.m_vertex_bone_weights.empty(); + auto objectIndex = 0u; for (const auto& object : xmodel.m_objects) { @@ -105,6 +106,12 @@ namespace primitives.attributes.NORMAL = m_normal_accessor; primitives.attributes.TEXCOORD_0 = m_uv_accessor; + if (hasBoneWeightData) + { + primitives.attributes.JOINTS_0 = m_joints_accessor; + primitives.attributes.WEIGHTS_0 = m_weights_accessor; + } + primitives.mode = JsonMeshPrimitivesMode::TRIANGLES; primitives.indices = m_first_index_accessor + objectIndex; @@ -136,7 +143,7 @@ namespace } if (!modelMaterial.normalMapName.empty()) - material.normalTexture.emplace().index = CreateTexture(gltf, modelMaterial.colorMapName); + material.normalTexture.emplace().index = CreateTexture(gltf, modelMaterial.normalMapName); material.doubleSided = true; gltf.materials->emplace_back(material); @@ -176,7 +183,7 @@ namespace return textureIndex; } - static void CreateSkeletonNodes(JsonRoot& gltf, const XModelCommon& xmodel) + void CreateSkeletonNodes(JsonRoot& gltf, const XModelCommon& xmodel) { if (xmodel.m_bones.empty()) return; @@ -185,20 +192,32 @@ namespace gltf.nodes.emplace(); const auto boneCount = xmodel.m_bones.size(); + m_first_bone_node = gltf.nodes->size(); for (auto boneIndex = 0u; boneIndex < boneCount; boneIndex++) { JsonNode boneNode; const auto& bone = xmodel.m_bones[boneIndex]; + Vector3f translation(bone.globalOffset[0], bone.globalOffset[2], -bone.globalOffset[1]); + Quaternion32 rotation(bone.globalRotation.m_x, bone.globalRotation.m_z, -bone.globalRotation.m_y, bone.globalRotation.m_w); + if (bone.parentIndex >= 0) + { + const auto& parentBone = xmodel.m_bones[bone.parentIndex]; + translation -= Vector3f(parentBone.globalOffset[0], parentBone.globalOffset[2], -parentBone.globalOffset[1]); + rotation -= Quaternion32( + parentBone.globalRotation.m_x, parentBone.globalRotation.m_z, -parentBone.globalRotation.m_y, parentBone.globalRotation.m_w); + } + rotation.Normalize(); + boneNode.name = bone.name; - boneNode.translation = std::to_array(bone.globalOffset); - boneNode.rotation = std::to_array({bone.globalRotation.m_x, bone.globalRotation.m_y, bone.globalRotation.m_z, bone.globalRotation.m_w}); + boneNode.translation = std::to_array({translation.x(), translation.y(), translation.z()}); + boneNode.rotation = std::to_array({rotation.m_x, rotation.m_y, rotation.m_z, rotation.m_w}); std::vector children; for (auto maybeChildIndex = 0u; maybeChildIndex < boneCount; maybeChildIndex++) { if (xmodel.m_bones[maybeChildIndex].parentIndex == static_cast(boneIndex)) - children.emplace_back(maybeChildIndex + NODE_FIRST_INDEX_BONES); + children.emplace_back(maybeChildIndex + m_first_bone_node); } if (!children.empty()) boneNode.children = std::move(children); @@ -207,7 +226,7 @@ namespace } } - static void CreateSkin(JsonRoot& gltf, const XModelCommon& xmodel) + void CreateSkin(JsonRoot& gltf, const XModelCommon& xmodel) const { if (xmodel.m_bones.empty()) return; @@ -221,7 +240,7 @@ namespace skin.joints.reserve(boneCount); for (auto boneIndex = 0u; boneIndex < boneCount; boneIndex++) - skin.joints.emplace_back(boneIndex + NODE_FIRST_INDEX_BONES); + skin.joints.emplace_back(boneIndex + m_first_bone_node); gltf.skins->emplace_back(std::move(skin)); } @@ -231,10 +250,7 @@ namespace JsonScene scene; // Only add skin if the model has bones - if (xmodel.m_bones.empty()) - scene.nodes.emplace_back(NODE_INDEX_MESH); - else - scene.nodes.emplace_back(m_skeleton_node); + scene.nodes.emplace_back(m_mesh_node); if (!gltf.scenes.has_value()) gltf.scenes.emplace(); @@ -261,6 +277,29 @@ namespace m_vertex_buffer_view = gltf.bufferViews->size(); gltf.bufferViews->emplace_back(vertexBufferView); + if (!xmodel.m_vertex_bone_weights.empty()) + { + JsonBufferView jointsBufferView; + jointsBufferView.buffer = 0u; + jointsBufferView.byteOffset = bufferOffset; + jointsBufferView.byteLength = sizeof(uint8_t) * xmodel.m_vertices.size() * 4u; + jointsBufferView.target = JsonBufferViewTarget::ARRAY_BUFFER; + bufferOffset += jointsBufferView.byteLength; + + m_joints_buffer_view = gltf.bufferViews->size(); + gltf.bufferViews->emplace_back(jointsBufferView); + + JsonBufferView weightsBufferView; + weightsBufferView.buffer = 0u; + weightsBufferView.byteOffset = bufferOffset; + weightsBufferView.byteLength = sizeof(float) * xmodel.m_vertices.size() * 4u; + weightsBufferView.target = JsonBufferViewTarget::ARRAY_BUFFER; + bufferOffset += weightsBufferView.byteLength; + + m_weights_buffer_view = gltf.bufferViews->size(); + gltf.bufferViews->emplace_back(weightsBufferView); + } + m_first_index_buffer_view = gltf.bufferViews->size(); for (const auto& object : xmodel.m_objects) { @@ -307,6 +346,25 @@ namespace m_uv_accessor = gltf.accessors->size(); gltf.accessors->emplace_back(uvAccessor); + if (!xmodel.m_vertex_bone_weights.empty()) + { + JsonAccessor jointsAccessor; + jointsAccessor.bufferView = m_joints_buffer_view; + jointsAccessor.componentType = JsonAccessorComponentType::UNSIGNED_BYTE; + jointsAccessor.count = xmodel.m_vertices.size(); + jointsAccessor.type = JsonAccessorType::VEC4; + m_joints_accessor = gltf.accessors->size(); + gltf.accessors->emplace_back(jointsAccessor); + + JsonAccessor weightsAccessor; + weightsAccessor.bufferView = m_weights_buffer_view; + weightsAccessor.componentType = JsonAccessorComponentType::FLOAT; + weightsAccessor.count = xmodel.m_vertices.size(); + weightsAccessor.type = JsonAccessorType::VEC4; + m_weights_accessor = gltf.accessors->size(); + gltf.accessors->emplace_back(weightsAccessor); + } + m_first_index_accessor = gltf.accessors->size(); for (auto i = 0u; i < xmodel.m_objects.size(); i++) { @@ -377,6 +435,37 @@ namespace gltf.accessors.value()[m_position_accessor].max = std::vector({maxPosition[0], maxPosition[1], maxPosition[2]}); } + if (!xmodel.m_vertex_bone_weights.empty()) + { + assert(xmodel.m_vertex_bone_weights.size() == xmodel.m_vertices.size()); + + auto* joints = reinterpret_cast(&bufferData[currentBufferOffset]); + auto* weights = reinterpret_cast(&bufferData[currentBufferOffset + sizeof(uint8_t) * 4u * xmodel.m_vertex_bone_weights.size()]); + for (const auto& commonVertexWeights : xmodel.m_vertex_bone_weights) + { + assert(commonVertexWeights.weights != nullptr); + assert(commonVertexWeights.weightCount <= 4u); + + const auto commonVertexWeightCount = std::min(commonVertexWeights.weightCount, 4u); + for (auto i = 0u; i < commonVertexWeightCount; i++) + { + joints[i] = static_cast(commonVertexWeights.weights[i].boneIndex); + weights[i] = commonVertexWeights.weights[i].weight; + } + + for (auto i = commonVertexWeightCount; i < 4u; i++) + { + joints[i] = 0u; + weights[i] = 0.0f; + } + + joints += 4u; + weights += 4u; + } + + currentBufferOffset += (sizeof(uint8_t) + sizeof(float)) * 4u * xmodel.m_vertex_bone_weights.size(); + } + for (const auto& object : xmodel.m_objects) { for (const auto& face : object.m_faces) @@ -399,6 +488,11 @@ namespace result += xmodel.m_vertices.size() * sizeof(GltfVertex); + if (!xmodel.m_vertex_bone_weights.empty()) + { + result += xmodel.m_vertices.size() * 4u * (sizeof(uint8_t) + sizeof(float)); + } + for (const auto& object : xmodel.m_objects) { result += object.m_faces.size() * sizeof(unsigned short) * 3u; @@ -421,11 +515,16 @@ namespace gltf.buffers->emplace_back(std::move(jsonBuffer)); } - unsigned m_skeleton_node = 0u; + unsigned m_mesh_node = 0u; + unsigned m_first_bone_node = 0u; unsigned m_position_accessor = 0u; unsigned m_normal_accessor = 0u; unsigned m_uv_accessor = 0u; + unsigned m_joints_accessor = 0u; + unsigned m_weights_accessor = 0u; unsigned m_vertex_buffer_view = 0u; + unsigned m_joints_buffer_view = 0u; + unsigned m_weights_buffer_view = 0u; unsigned m_first_index_buffer_view = 0u; unsigned m_first_index_accessor = 0u; diff --git a/src/Utils/Math/Quaternion.h b/src/Utils/Math/Quaternion.h index 1090f96e..f2a88e55 100644 --- a/src/Utils/Math/Quaternion.h +++ b/src/Utils/Math/Quaternion.h @@ -51,6 +51,96 @@ public: return Matrix(m00, m01, m02, 0, m10, m11, m12, 0, m20, m21, m22, 0, 0, 0, 0, T(1.0)); } + + static T dot(const Quaternion& q1, const Quaternion& q2) + { + return static_cast((q1.m_x * q2.m_x) + (q1.m_y * q2.m_y) + (q1.m_z * q2.m_z) + (q1.m_w * q2.m_w)); + } + + T lengthSquared() + { + return Quaternion::dot(*this, *this); + } + + T length() + { + return sqrt(lengthSquared()); + } + + void Normalize() + { + const auto l = length(); + + // return if no magnitude (already as normalized as possible) + if (l < static_cast(0.0001)) + return; + + T inverseLength = static_cast(1.0) / l; + m_x *= inverseLength; + m_y *= inverseLength; + m_z *= inverseLength; + m_w *= inverseLength; + } + + friend Quaternion operator+(const Quaternion& lhs, const Quaternion& rhs) + { + return Quaternion(lhs.m_x + rhs.m_x, lhs.m_y + rhs.m_y, lhs.m_z + rhs.m_z, lhs.m_w + rhs.m_w); + } + + friend Quaternion operator-(const Quaternion& lhs, const Quaternion& rhs) + { + return Quaternion(lhs.m_x - rhs.m_x, lhs.m_y - rhs.m_y, lhs.m_z - rhs.m_z, lhs.m_w - rhs.m_w); + } + + friend Quaternion& operator+=(Quaternion& lhs, const Quaternion& rhs) + { + lhs.m_x += rhs.m_x; + lhs.m_y += rhs.m_y; + lhs.m_z += rhs.m_z; + lhs.m_w += rhs.m_w; + + return lhs; + } + + friend Quaternion& operator-=(Quaternion& lhs, const Quaternion& rhs) + { + lhs.m_x -= rhs.m_x; + lhs.m_y -= rhs.m_y; + lhs.m_z -= rhs.m_z; + lhs.m_w -= rhs.m_w; + + return lhs; + } + + friend Quaternion operator*(const Quaternion& lhs, const Quaternion& rhs) + { + return Quaternion(lhs.m_x + rhs.m_x, lhs.m_y + rhs.m_y, lhs.m_z + rhs.m_z, lhs.m_w + rhs.m_w); + } + + friend Quaternion operator/(const Quaternion& lhs, const Quaternion& rhs) + { + return Quaternion(lhs.m_x - rhs.m_x, lhs.m_y - rhs.m_y, lhs.m_z - rhs.m_z, lhs.m_w - rhs.m_w); + } + + friend Quaternion& operator*=(Quaternion& lhs, const Quaternion& rhs) + { + lhs.m_x += rhs.m_x; + lhs.m_y += rhs.m_y; + lhs.m_z += rhs.m_z; + lhs.m_w += rhs.m_w; + + return lhs; + } + + friend Quaternion& operator/=(Quaternion& lhs, const Quaternion& rhs) + { + lhs.m_x -= rhs.m_x; + lhs.m_y -= rhs.m_y; + lhs.m_z -= rhs.m_z; + lhs.m_w -= rhs.m_w; + + return lhs; + } }; typedef Quaternion Quaternion32; diff --git a/src/Utils/Math/Vector.h b/src/Utils/Math/Vector.h index 2a188a1d..60092681 100644 --- a/src/Utils/Math/Vector.h +++ b/src/Utils/Math/Vector.h @@ -24,6 +24,12 @@ public: { } + ~Vector2() = default; + Vector2(const Vector2& other) = default; + Vector2(Vector2&& other) noexcept = default; + Vector2& operator=(const Vector2& other) = default; + Vector2& operator=(Vector2&& other) noexcept = default; + _NODISCARD T& operator()(const size_t index) { assert(index < 2); @@ -55,6 +61,32 @@ public: { return m_value[1]; } + + friend Vector2 operator+(const Vector2& lhs, const Vector2& rhs) + { + return Vector2(lhs.m_value[0] + rhs.m_value[0], lhs.m_value[1] + rhs.m_value[1]); + } + + friend Vector2 operator-(const Vector2& lhs, const Vector2& rhs) + { + return Vector2(lhs.m_value[0] - rhs.m_value[0], lhs.m_value[1] - rhs.m_value[1]); + } + + friend Vector2& operator+=(Vector2& lhs, const Vector2& rhs) + { + lhs.m_value[0] += rhs.m_value[0]; + lhs.m_value[1] += rhs.m_value[1]; + + return lhs; + } + + friend Vector2& operator-=(Vector2& lhs, const Vector2& rhs) + { + lhs.m_value[0] -= rhs.m_value[0]; + lhs.m_value[1] -= rhs.m_value[1]; + + return lhs; + } }; typedef Vector2 Vector2f; @@ -80,6 +112,12 @@ public: { } + ~Vector3() = default; + Vector3(const Vector3& other) = default; + Vector3(Vector3&& other) noexcept = default; + Vector3& operator=(const Vector3& other) = default; + Vector3& operator=(Vector3&& other) noexcept = default; + _NODISCARD T& operator()(const size_t index) { assert(index < 3); @@ -151,6 +189,34 @@ public: { return m_value[2]; } + + friend Vector3 operator+(const Vector3& lhs, const Vector3& rhs) + { + return Vector3(lhs.m_value[0] + rhs.m_value[0], lhs.m_value[1] + rhs.m_value[1], lhs.m_value[2] + rhs.m_value[2]); + } + + friend Vector3 operator-(const Vector3& lhs, const Vector3& rhs) + { + return Vector3(lhs.m_value[0] - rhs.m_value[0], lhs.m_value[1] - rhs.m_value[1], lhs.m_value[2] - rhs.m_value[2]); + } + + friend Vector3& operator+=(Vector3& lhs, const Vector3& rhs) + { + lhs.m_value[0] += rhs.m_value[0]; + lhs.m_value[1] += rhs.m_value[1]; + lhs.m_value[2] += rhs.m_value[2]; + + return lhs; + } + + friend Vector3& operator-=(Vector3& lhs, const Vector3& rhs) + { + lhs.m_value[0] -= rhs.m_value[0]; + lhs.m_value[1] -= rhs.m_value[1]; + lhs.m_value[2] -= rhs.m_value[2]; + + return lhs; + } }; typedef Vector3 Vector3f; @@ -176,6 +242,12 @@ public: { } + ~Vector4() = default; + Vector4(const Vector4& other) = default; + Vector4(Vector4&& other) noexcept = default; + Vector4& operator=(const Vector4& other) = default; + Vector4& operator=(Vector4&& other) noexcept = default; + _NODISCARD T& operator()(const size_t index) { assert(index < 4); @@ -267,6 +339,36 @@ public: { return m_value[3]; } + + friend Vector4 operator+(const Vector4& lhs, const Vector4& rhs) + { + return Vector4(lhs.m_value[0] + rhs.m_value[0], lhs.m_value[1] + rhs.m_value[1], lhs.m_value[2] + rhs.m_value[2], lhs.m_value[3] + rhs.m_value[3]); + } + + friend Vector4 operator-(const Vector4& lhs, const Vector4& rhs) + { + return Vector4(lhs.m_value[0] - rhs.m_value[0], lhs.m_value[1] - rhs.m_value[1], lhs.m_value[2] - rhs.m_value[2], lhs.m_value[3] - rhs.m_value[3]); + } + + friend Vector4& operator+=(Vector4& lhs, const Vector4& rhs) + { + lhs.m_value[0] += rhs.m_value[0]; + lhs.m_value[1] += rhs.m_value[1]; + lhs.m_value[2] += rhs.m_value[2]; + lhs.m_value[3] += rhs.m_value[3]; + + return lhs; + } + + friend Vector4& operator-=(Vector4& lhs, const Vector4& rhs) + { + lhs.m_value[0] -= rhs.m_value[0]; + lhs.m_value[1] -= rhs.m_value[1]; + lhs.m_value[2] -= rhs.m_value[2]; + lhs.m_value[3] -= rhs.m_value[3]; + + return lhs; + } }; typedef Vector4 Vector4f; From 1c105db5bc0dcd1f750a7381616005c82614b1fc Mon Sep 17 00:00:00 2001 From: Jan Date: Fri, 10 May 2024 22:55:22 +0200 Subject: [PATCH 22/25] fix: quaternion multiplication and division i hope its correct at least, quaternions are not my strength --- src/ObjWriting/XModel/Gltf/GltfWriter.cpp | 2 +- src/Utils/Math/Quaternion.h | 65 ++++++++++++++++++----- 2 files changed, 53 insertions(+), 14 deletions(-) diff --git a/src/ObjWriting/XModel/Gltf/GltfWriter.cpp b/src/ObjWriting/XModel/Gltf/GltfWriter.cpp index 2f148aad..4a63bc43 100644 --- a/src/ObjWriting/XModel/Gltf/GltfWriter.cpp +++ b/src/ObjWriting/XModel/Gltf/GltfWriter.cpp @@ -204,7 +204,7 @@ namespace { const auto& parentBone = xmodel.m_bones[bone.parentIndex]; translation -= Vector3f(parentBone.globalOffset[0], parentBone.globalOffset[2], -parentBone.globalOffset[1]); - rotation -= Quaternion32( + rotation /= Quaternion32( parentBone.globalRotation.m_x, parentBone.globalRotation.m_z, -parentBone.globalRotation.m_y, parentBone.globalRotation.m_w); } rotation.Normalize(); diff --git a/src/Utils/Math/Quaternion.h b/src/Utils/Math/Quaternion.h index f2a88e55..b69cc414 100644 --- a/src/Utils/Math/Quaternion.h +++ b/src/Utils/Math/Quaternion.h @@ -57,12 +57,41 @@ public: return static_cast((q1.m_x * q2.m_x) + (q1.m_y * q2.m_y) + (q1.m_z * q2.m_z) + (q1.m_w * q2.m_w)); } - T lengthSquared() + static Quaternion& conj(Quaternion& result) + { + result.m_x = -result.m_x; + result.m_y = -result.m_y; + result.m_z = -result.m_z; + return result; + } + + static Quaternion& invert(Quaternion& result) + { + // from game programming gems p198 + // do result = conj( q ) / norm( q ) + Quaternion::conj(result); + + // return if norm() is near 0 (divide by 0 would result in NaN) + T l = result.lengthSquared(); + if (l < static_cast(0.0001)) + { + return result; + } + + T l_inv = static_cast(1.0) / l; + result.m_x *= l_inv; + result.m_y *= l_inv; + result.m_z *= l_inv; + result.m_w *= l_inv; + return result; + } + + T lengthSquared() const { return Quaternion::dot(*this, *this); } - T length() + T length() const { return sqrt(lengthSquared()); } @@ -114,31 +143,41 @@ public: friend Quaternion operator*(const Quaternion& lhs, const Quaternion& rhs) { - return Quaternion(lhs.m_x + rhs.m_x, lhs.m_y + rhs.m_y, lhs.m_z + rhs.m_z, lhs.m_w + rhs.m_w); + const T x2 = lhs.m_w * rhs.m_x + lhs.m_x * rhs.m_w + lhs.m_y * rhs.m_z - lhs.m_z * rhs.m_y; + const T y2 = lhs.m_w * rhs.m_y + lhs.m_y * rhs.m_w + lhs.m_z * rhs.m_x - lhs.m_x * rhs.m_z; + const T z2 = lhs.m_w * rhs.m_z + lhs.m_z * rhs.m_w + lhs.m_x * rhs.m_y - lhs.m_y * rhs.m_x; + const T w2 = lhs.m_w * rhs.m_w - lhs.m_x * rhs.m_x - lhs.m_y * rhs.m_y - lhs.m_z * rhs.m_z; + + return Quaternion(x2, y2, z2, w2); } friend Quaternion operator/(const Quaternion& lhs, const Quaternion& rhs) { - return Quaternion(lhs.m_x - rhs.m_x, lhs.m_y - rhs.m_y, lhs.m_z - rhs.m_z, lhs.m_w - rhs.m_w); + Quaternion rhsInv = rhs; + Quaternion::invert(rhsInv); + return lhs * rhsInv; } friend Quaternion& operator*=(Quaternion& lhs, const Quaternion& rhs) { - lhs.m_x += rhs.m_x; - lhs.m_y += rhs.m_y; - lhs.m_z += rhs.m_z; - lhs.m_w += rhs.m_w; + const T x2 = lhs.m_w * rhs.m_x + lhs.m_x * rhs.m_w + lhs.m_y * rhs.m_z - lhs.m_z * rhs.m_y; + const T y2 = lhs.m_w * rhs.m_y + lhs.m_y * rhs.m_w + lhs.m_z * rhs.m_x - lhs.m_x * rhs.m_z; + const T z2 = lhs.m_w * rhs.m_z + lhs.m_z * rhs.m_w + lhs.m_x * rhs.m_y - lhs.m_y * rhs.m_x; + const T w2 = lhs.m_w * rhs.m_w - lhs.m_x * rhs.m_x - lhs.m_y * rhs.m_y - lhs.m_z * rhs.m_z; + + lhs.m_x = x2; + lhs.m_y = y2; + lhs.m_z = z2; + lhs.m_w = w2; return lhs; } friend Quaternion& operator/=(Quaternion& lhs, const Quaternion& rhs) { - lhs.m_x -= rhs.m_x; - lhs.m_y -= rhs.m_y; - lhs.m_z -= rhs.m_z; - lhs.m_w -= rhs.m_w; - + Quaternion rhsInv = rhs; + Quaternion::invert(rhsInv); + lhs *= rhsInv; return lhs; } }; From 6a1962b65ac6d7f0e16bf508d71215298f8cf7c5 Mon Sep 17 00:00:00 2001 From: Jan Date: Fri, 10 May 2024 22:57:22 +0200 Subject: [PATCH 23/25] chore: export separate root node for gltf --- src/ObjWriting/XModel/Gltf/GltfWriter.cpp | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/src/ObjWriting/XModel/Gltf/GltfWriter.cpp b/src/ObjWriting/XModel/Gltf/GltfWriter.cpp index 4a63bc43..8644d852 100644 --- a/src/ObjWriting/XModel/Gltf/GltfWriter.cpp +++ b/src/ObjWriting/XModel/Gltf/GltfWriter.cpp @@ -37,8 +37,9 @@ namespace std::vector bufferData; CreateJsonAsset(gltf.asset); - CreateMeshNode(gltf, xmodel); CreateSkeletonNodes(gltf, xmodel); + CreateMeshNode(gltf, xmodel); + CreateRootNode(gltf, xmodel); CreateMaterials(gltf, xmodel); CreateSkin(gltf, xmodel); CreateBufferViews(gltf, xmodel); @@ -85,6 +86,23 @@ namespace gltf.nodes->emplace_back(std::move(meshNode)); } + void CreateRootNode(JsonRoot& gltf, const XModelCommon& xmodel) + { + JsonNode rootNode; + + if (!gltf.nodes.has_value()) + gltf.nodes.emplace(); + + rootNode.children.emplace(); + rootNode.children->push_back(m_mesh_node); + + if (!xmodel.m_bones.empty()) + rootNode.children->push_back(m_first_bone_node); + + m_root_node = gltf.nodes->size(); + gltf.nodes->emplace_back(std::move(rootNode)); + } + void CreateMesh(JsonRoot& gltf, const XModelCommon& xmodel) { if (!gltf.meshes.has_value()) @@ -250,7 +268,7 @@ namespace JsonScene scene; // Only add skin if the model has bones - scene.nodes.emplace_back(m_mesh_node); + scene.nodes.emplace_back(m_root_node); if (!gltf.scenes.has_value()) gltf.scenes.emplace(); @@ -516,6 +534,7 @@ namespace } unsigned m_mesh_node = 0u; + unsigned m_root_node = 0u; unsigned m_first_bone_node = 0u; unsigned m_position_accessor = 0u; unsigned m_normal_accessor = 0u; From 5c06b6e5e43eaf4980fa5116e41e60ee54839af8 Mon Sep 17 00:00:00 2001 From: Jan Date: Sat, 11 May 2024 00:35:18 +0200 Subject: [PATCH 24/25] chore: dump gltf skin inverseBindMatrices --- src/ObjWriting/XModel/Gltf/GltfWriter.cpp | 58 ++++++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) diff --git a/src/ObjWriting/XModel/Gltf/GltfWriter.cpp b/src/ObjWriting/XModel/Gltf/GltfWriter.cpp index 8644d852..11da0980 100644 --- a/src/ObjWriting/XModel/Gltf/GltfWriter.cpp +++ b/src/ObjWriting/XModel/Gltf/GltfWriter.cpp @@ -41,9 +41,9 @@ namespace CreateMeshNode(gltf, xmodel); CreateRootNode(gltf, xmodel); CreateMaterials(gltf, xmodel); - CreateSkin(gltf, xmodel); CreateBufferViews(gltf, xmodel); CreateAccessors(gltf, xmodel); + CreateSkin(gltf, xmodel); CreateMesh(gltf, xmodel); CreateScene(gltf, xmodel); FillBufferData(gltf, xmodel, bufferData); @@ -260,6 +260,8 @@ namespace for (auto boneIndex = 0u; boneIndex < boneCount; boneIndex++) skin.joints.emplace_back(boneIndex + m_first_bone_node); + skin.inverseBindMatrices = m_inverse_bind_matrices_accessor; + gltf.skins->emplace_back(std::move(skin)); } @@ -316,6 +318,15 @@ namespace m_weights_buffer_view = gltf.bufferViews->size(); gltf.bufferViews->emplace_back(weightsBufferView); + + JsonBufferView inverseBindMatricesBufferView; + inverseBindMatricesBufferView.buffer = 0u; + inverseBindMatricesBufferView.byteOffset = bufferOffset; + inverseBindMatricesBufferView.byteLength = sizeof(float) * xmodel.m_bones.size() * 16u; + bufferOffset += inverseBindMatricesBufferView.byteLength; + + m_inverse_bind_matrices_buffer_view = gltf.bufferViews->size(); + gltf.bufferViews->emplace_back(inverseBindMatricesBufferView); } m_first_index_buffer_view = gltf.bufferViews->size(); @@ -381,6 +392,14 @@ namespace weightsAccessor.type = JsonAccessorType::VEC4; m_weights_accessor = gltf.accessors->size(); gltf.accessors->emplace_back(weightsAccessor); + + JsonAccessor inverseBindMatricesAccessor; + inverseBindMatricesAccessor.bufferView = m_inverse_bind_matrices_buffer_view; + inverseBindMatricesAccessor.componentType = JsonAccessorComponentType::FLOAT; + inverseBindMatricesAccessor.count = xmodel.m_bones.size(); + inverseBindMatricesAccessor.type = JsonAccessorType::MAT4; + m_inverse_bind_matrices_accessor = gltf.accessors->size(); + gltf.accessors->emplace_back(inverseBindMatricesAccessor); } m_first_index_accessor = gltf.accessors->size(); @@ -482,6 +501,37 @@ namespace } currentBufferOffset += (sizeof(uint8_t) + sizeof(float)) * 4u * xmodel.m_vertex_bone_weights.size(); + + auto* inverseBindMatrixData = reinterpret_cast(&bufferData[currentBufferOffset]); + for (const auto& bone : xmodel.m_bones) + { + Matrix32 inverseBindMatrix; + inverseBindMatrix.m_data[0][3] = -bone.globalOffset[0]; + inverseBindMatrix.m_data[1][3] = -bone.globalOffset[2]; + inverseBindMatrix.m_data[2][3] = bone.globalOffset[1]; + + // In-memory = row major + // gltf = column major + inverseBindMatrixData[0] = inverseBindMatrix.m_data[0][0]; + inverseBindMatrixData[1] = inverseBindMatrix.m_data[1][0]; + inverseBindMatrixData[2] = inverseBindMatrix.m_data[2][0]; + inverseBindMatrixData[3] = inverseBindMatrix.m_data[3][0]; + inverseBindMatrixData[4] = inverseBindMatrix.m_data[0][1]; + inverseBindMatrixData[5] = inverseBindMatrix.m_data[1][1]; + inverseBindMatrixData[6] = inverseBindMatrix.m_data[2][1]; + inverseBindMatrixData[7] = inverseBindMatrix.m_data[3][1]; + inverseBindMatrixData[8] = inverseBindMatrix.m_data[0][2]; + inverseBindMatrixData[9] = inverseBindMatrix.m_data[1][2]; + inverseBindMatrixData[10] = inverseBindMatrix.m_data[2][2]; + inverseBindMatrixData[11] = inverseBindMatrix.m_data[3][2]; + inverseBindMatrixData[12] = inverseBindMatrix.m_data[0][3]; + inverseBindMatrixData[13] = inverseBindMatrix.m_data[1][3]; + inverseBindMatrixData[14] = inverseBindMatrix.m_data[2][3]; + inverseBindMatrixData[15] = inverseBindMatrix.m_data[3][3]; + + inverseBindMatrixData += 16u; + } + currentBufferOffset += sizeof(float) * 16u * xmodel.m_bones.size(); } for (const auto& object : xmodel.m_objects) @@ -508,7 +558,11 @@ namespace if (!xmodel.m_vertex_bone_weights.empty()) { + // Joints and weights result += xmodel.m_vertices.size() * 4u * (sizeof(uint8_t) + sizeof(float)); + + // Inverse bind matrices + result += xmodel.m_bones.size() * 16u * sizeof(float); } for (const auto& object : xmodel.m_objects) @@ -541,9 +595,11 @@ namespace unsigned m_uv_accessor = 0u; unsigned m_joints_accessor = 0u; unsigned m_weights_accessor = 0u; + unsigned m_inverse_bind_matrices_accessor = 0u; unsigned m_vertex_buffer_view = 0u; unsigned m_joints_buffer_view = 0u; unsigned m_weights_buffer_view = 0u; + unsigned m_inverse_bind_matrices_buffer_view = 0u; unsigned m_first_index_buffer_view = 0u; unsigned m_first_index_accessor = 0u; From 499b752272b3e6fb7a614e0f6c037f41c4828b75 Mon Sep 17 00:00:00 2001 From: Jan Date: Sat, 11 May 2024 01:51:28 +0200 Subject: [PATCH 25/25] chore: add names to exported gltf models --- src/ObjCommon/XModel/XModelCommon.h | 1 + src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperXModel.cpp | 1 + src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperXModel.cpp | 1 + src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperXModel.cpp | 1 + src/ObjWriting/Game/T5/AssetDumpers/AssetDumperXModel.cpp | 1 + src/ObjWriting/Game/T6/AssetDumpers/AssetDumperXModel.cpp | 1 + src/ObjWriting/XModel/Gltf/GltfWriter.cpp | 6 ++++++ 7 files changed, 12 insertions(+) diff --git a/src/ObjCommon/XModel/XModelCommon.h b/src/ObjCommon/XModel/XModelCommon.h index 7f1f0b44..82f6bbef 100644 --- a/src/ObjCommon/XModel/XModelCommon.h +++ b/src/ObjCommon/XModel/XModelCommon.h @@ -96,6 +96,7 @@ struct XModelObject struct XModelCommon { + std::string m_name; std::vector m_objects; std::vector m_bones; std::vector m_materials; diff --git a/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperXModel.cpp b/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperXModel.cpp index df8dad55..862b0470 100644 --- a/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperXModel.cpp +++ b/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperXModel.cpp @@ -412,6 +412,7 @@ namespace 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); diff --git a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperXModel.cpp b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperXModel.cpp index f88bcc3b..d8310cdd 100644 --- a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperXModel.cpp +++ b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperXModel.cpp @@ -394,6 +394,7 @@ namespace 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); diff --git a/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperXModel.cpp b/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperXModel.cpp index cef5b987..1cf707d5 100644 --- a/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperXModel.cpp +++ b/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperXModel.cpp @@ -394,6 +394,7 @@ namespace 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); diff --git a/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperXModel.cpp b/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperXModel.cpp index 6538f7c7..ed204379 100644 --- a/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperXModel.cpp +++ b/src/ObjWriting/Game/T5/AssetDumpers/AssetDumperXModel.cpp @@ -412,6 +412,7 @@ namespace 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); diff --git a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperXModel.cpp b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperXModel.cpp index f2cf9370..0a136a96 100644 --- a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperXModel.cpp +++ b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperXModel.cpp @@ -436,6 +436,7 @@ namespace 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); diff --git a/src/ObjWriting/XModel/Gltf/GltfWriter.cpp b/src/ObjWriting/XModel/Gltf/GltfWriter.cpp index 11da0980..a48ba3f8 100644 --- a/src/ObjWriting/XModel/Gltf/GltfWriter.cpp +++ b/src/ObjWriting/XModel/Gltf/GltfWriter.cpp @@ -69,6 +69,9 @@ namespace { JsonNode meshNode; + if (!xmodel.m_name.empty()) + meshNode.name = xmodel.m_name; + // We only have one mesh meshNode.mesh = 0u; @@ -90,6 +93,9 @@ namespace { JsonNode rootNode; + if (!xmodel.m_name.empty()) + rootNode.name = std::format("{}_skel", xmodel.m_name); + if (!gltf.nodes.has_value()) gltf.nodes.emplace();