diff --git a/src/ObjLoading/Game/T6/ObjLoaderT6.cpp b/src/ObjLoading/Game/T6/ObjLoaderT6.cpp index f7b68262..ba20516c 100644 --- a/src/ObjLoading/Game/T6/ObjLoaderT6.cpp +++ b/src/ObjLoading/Game/T6/ObjLoaderT6.cpp @@ -32,6 +32,7 @@ #include "Slug/LoaderSlugT6.h" #include "Sound/LoaderSoundBankT6.h" #include "StringTable/LoaderStringTableT6.h" +#include "TechniqueSet/LoaderTechniqueSetT6.h" #include "Tracer/GdtLoaderTracerT6.h" #include "Tracer/RawLoaderTracerT6.h" #include "Utils/Logging/Log.h" @@ -391,7 +392,7 @@ namespace T6 // collection.AddAssetCreator(std::make_unique(memory)); collection.AddAssetCreator(xmodel::CreateLoaderT6(memory, searchPath, zone)); collection.AddAssetCreator(material::CreateLoaderT6(memory, searchPath)); - // collection.AddAssetCreator(std::make_unique(memory)); + collection.AddAssetCreator(CreateTechniqueSetLoader(memory, searchPath)); collection.AddAssetCreator(image::CreateLoaderEmbeddedT6(memory, searchPath)); collection.AddAssetCreator(image::CreateLoaderExternalT6(memory, searchPath)); collection.AddAssetCreator(sound::CreateSoundBankLoaderT6(memory, searchPath)); diff --git a/src/ObjLoading/Game/T6/TechniqueSet/LoaderTechniqueSetT6.cpp b/src/ObjLoading/Game/T6/TechniqueSet/LoaderTechniqueSetT6.cpp new file mode 100644 index 00000000..e51538a1 --- /dev/null +++ b/src/ObjLoading/Game/T6/TechniqueSet/LoaderTechniqueSetT6.cpp @@ -0,0 +1,184 @@ +#include "LoaderTechniqueSetT6.h" + +#include "Game/T6/T6.h" + +#include +#include +#include + +using namespace T6; + +namespace +{ + class TechniqueSetLoader final : public AssetCreator + { + public: + TechniqueSetLoader(MemoryManager& memory, ISearchPath& searchPath) + : m_memory(memory), + m_search_path(searchPath) + { + } + + AssetCreationResult CreateAsset(const std::string& assetName, AssetCreationContext& context) override + { + const auto fileName = std::format("techniquesets/{}.json", assetName); + const auto file = m_search_path.Open(assetName); + if (!file.IsOpen()) + return AssetCreationResult::NoAction(); + + MaterialTechniqueSet* techniqueSet = new MaterialTechniqueSet; + memset(techniqueSet, 0, sizeof(MaterialTechniqueSet)); + + const auto js = nlohmann::json::parse(*file.m_stream.get()); + + std::string techSetName = js["name"]; + techniqueSet->name = _strdup(techSetName.c_str()); + techniqueSet->worldVertFormat = (MaterialWorldVertexFormat)js["worldVertFormat"]; + + for (int i = 0; i < 36; i++) + { + nlohmann::json techniqueJs = js["techniques"][i]; + + if (techniqueJs.size() == 0) + { + techniqueSet->techniques[i] = NULL; + } + else + { + MaterialTechnique* technique = new MaterialTechnique; + techniqueSet->techniques[i] = technique; + + std::string techName = techniqueJs["name"]; + technique->name = _strdup(techName.c_str()); + technique->flags = techniqueJs["flags"]; + technique->passCount = techniqueJs["passCount"]; + _ASSERT(technique->passCount == 1); + + for (auto passIndex = 0u; passIndex < technique->passCount; passIndex++) + { + MaterialPass* currPass = &technique->passArray[passIndex]; + nlohmann::json passJs = techniqueJs["passArray"][passIndex]; + + currPass->perPrimArgCount = (unsigned char)passJs["perPrimArgCount"]; + currPass->perObjArgCount = (unsigned char)passJs["perObjArgCount"]; + currPass->stableArgCount = (unsigned char)passJs["stableArgCount"]; + currPass->customSamplerFlags = (unsigned char)passJs["customSamplerFlags"]; + currPass->precompiledIndex = (VertexShaderPrecompiledIndex)passJs["precompiledIndex"]; + currPass->materialType = (MaterialType)passJs["materialType"]; + + int argCount = currPass->perPrimArgCount + currPass->perObjArgCount + currPass->stableArgCount; + if (argCount == 0) + { + currPass->args = NULL; + } + else + { + currPass->args = new MaterialShaderArgument[argCount]; + for (int i = 0; i < argCount; i++) + { + MaterialShaderArgument* currArg = &currPass->args[i]; + auto currArgJs = passJs["args"][i]; + + currArg->type = currArgJs["type"]; + currArg->location.offset = currArgJs["location"]; + currArg->size = currArgJs["size"]; + currArg->buffer = currArgJs["buffer"]; + if (currArg->type == MTL_ARG_LITERAL_VERTEX_CONST || currArg->type == MTL_ARG_LITERAL_PIXEL_CONST) + { + float* consts = new float[4]; + consts[0] = currArgJs["u"]["const0"]; + consts[1] = currArgJs["u"]["const1"]; + consts[2] = currArgJs["u"]["const2"]; + consts[3] = currArgJs["u"]["const3"]; + currArg->u.literalConst = (float (*)[4])consts; + } + else + { + currArg->u.nameHash = currArgJs["u"]["value"]; + } + } + } + + if (passJs["vertexDecl"].size() == 0) + { + currPass->vertexDecl = NULL; + } + else + { + currPass->vertexDecl = new MaterialVertexDeclaration; + currPass->vertexDecl->streamCount = (unsigned char)passJs["vertexDecl"]["streamCount"]; + currPass->vertexDecl->hasOptionalSource = passJs["vertexDecl"]["hasOptionalSource"]; + currPass->vertexDecl->isLoaded = passJs["vertexDecl"]["isLoaded"]; + for (int i = 0; i < 16; i++) + { + currPass->vertexDecl->routing.data[i].source = (unsigned char)passJs["vertexDecl"]["routing"][i]["source"]; + currPass->vertexDecl->routing.data[i].dest = (unsigned char)passJs["vertexDecl"]["routing"][i]["dest"]; + } + } + + if (passJs["pixelShader"].size() == 0) + { + currPass->pixelShader = NULL; + } + else + { + currPass->pixelShader = new MaterialPixelShader; + + std::string pixelName = passJs["pixelShader"]["name"]; + currPass->pixelShader->name = _strdup(pixelName.c_str()); + currPass->pixelShader->prog.ps = NULL; + + const auto psFileName = std::format("techniques/shader_bin/ps_{}", pixelName); + const auto psFile = m_search_path.Open(psFileName); + if (!psFile.IsOpen()) + { + printf("ERROR: Cant find pixel shader %s\n", psFileName.c_str()); + return AssetCreationResult::Failure(); + } + currPass->pixelShader->prog.loadDef.programSize = psFile.m_length; + currPass->pixelShader->prog.loadDef.program = new char[psFile.m_length]; + psFile.m_stream->read(currPass->pixelShader->prog.loadDef.program, psFile.m_length); + } + + if (passJs["vertexShader"].size() == 0) + { + currPass->vertexShader = NULL; + } + else + { + currPass->vertexShader = new MaterialVertexShader; + + std::string vertexName = passJs["vertexShader"]["name"]; + currPass->vertexShader->name = _strdup(vertexName.c_str()); + currPass->vertexShader->prog.vs = NULL; + + const auto vsFileName = std::format("techniques/shader_bin/vs_{}", vertexName); + const auto vsFile = m_search_path.Open(vsFileName); + if (!vsFile.IsOpen()) + { + printf("ERROR: Cant find vertex shader %s\n", vsFileName.c_str()); + return AssetCreationResult::Failure(); + } + currPass->vertexShader->prog.loadDef.programSize = vsFile.m_length; + currPass->vertexShader->prog.loadDef.program = new char[vsFile.m_length]; + vsFile.m_stream->read(currPass->vertexShader->prog.loadDef.program, vsFile.m_length); + } + } + } + } + return AssetCreationResult::Success(context.AddAsset(assetName, techniqueSet)); + } + + private: + MemoryManager& m_memory; + ISearchPath& m_search_path; + }; +} // namespace + +namespace T6 +{ + std::unique_ptr> CreateTechniqueSetLoader(MemoryManager& memory, ISearchPath& searchPath) + { + return std::make_unique(memory, searchPath); + } +} // namespace T6 diff --git a/src/ObjLoading/Game/T6/TechniqueSet/LoaderTechniqueSetT6.h b/src/ObjLoading/Game/T6/TechniqueSet/LoaderTechniqueSetT6.h new file mode 100644 index 00000000..78081a86 --- /dev/null +++ b/src/ObjLoading/Game/T6/TechniqueSet/LoaderTechniqueSetT6.h @@ -0,0 +1,13 @@ +#pragma once + +#include "Asset/IAssetCreator.h" +#include "Game/T6/T6.h" +#include "SearchPath/ISearchPath.h" +#include "Utils/MemoryManager.h" + +#include + +namespace T6 +{ + std::unique_ptr> CreateTechniqueSetLoader(MemoryManager& memory, ISearchPath& searchPath); +} // namespace T6 diff --git a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperTechniqueSet.cpp b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperTechniqueSet.cpp new file mode 100644 index 00000000..a1bdaf04 --- /dev/null +++ b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperTechniqueSet.cpp @@ -0,0 +1,115 @@ +#include "AssetDumperTechniqueSet.h" + +#include +#include +#include + +using namespace T6; + +void AssetDumperTechniqueSet::DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) +{ + const auto* techniqueSet = asset.Asset(); + + const auto assetFile = context.OpenAssetFile(std::format("techniquesets/{}.json", techniqueSet->name)); + if (!assetFile) + return; + + nlohmann::json js; + + js["name"] = techniqueSet->name; + js["worldVertFormat"] = techniqueSet->worldVertFormat; + + js["techniques"] = nlohmann::json::array(); + for (const auto* technique : techniqueSet->techniques) + { + nlohmann::json techniqueJs = nlohmann::json::object(); + + if (technique != NULL) + { + techniqueJs["name"] = technique->name; + techniqueJs["flags"] = technique->flags; + techniqueJs["passCount"] = technique->passCount; + + _ASSERT(technique->passCount == 1); + + techniqueJs["passArray"] = nlohmann::json::array(); + for (auto passIndex = 0u; passIndex < technique->passCount; passIndex++) + { + const MaterialPass* currPass = &technique->passArray[passIndex]; + nlohmann::json passJs = nlohmann::json::object(); + + passJs["perPrimArgCount"] = currPass->perPrimArgCount; + passJs["perObjArgCount"] = currPass->perObjArgCount; + passJs["stableArgCount"] = currPass->stableArgCount; + passJs["customSamplerFlags"] = currPass->customSamplerFlags; + passJs["precompiledIndex"] = currPass->precompiledIndex; + passJs["materialType"] = currPass->materialType; + + nlohmann::json vertDeclJs = nlohmann::json::object(); + if (currPass->vertexDecl != NULL) + { + vertDeclJs["streamCount"] = currPass->vertexDecl->streamCount; + vertDeclJs["hasOptionalSource"] = currPass->vertexDecl->hasOptionalSource; + vertDeclJs["isLoaded"] = currPass->vertexDecl->isLoaded; + for (int i = 0; i < 16; i++) + { + vertDeclJs["routing"][i]["source"] = currPass->vertexDecl->routing.data[i].source; + vertDeclJs["routing"][i]["dest"] = currPass->vertexDecl->routing.data[i].dest; + + _ASSERT(currPass->vertexDecl->routing.decl[i] == NULL); + } + } + passJs["vertexDecl"] = vertDeclJs; + + passJs["args"] = nlohmann::json::array(); + if (currPass->args != NULL) + { + for (int i = 0; i < currPass->perPrimArgCount + currPass->perObjArgCount + currPass->stableArgCount; i++) + { + nlohmann::json argsJs = nlohmann::json::object(); + MaterialShaderArgument* currArg = &currPass->args[i]; + + argsJs["type"] = currArg->type; + argsJs["location"] = currArg->location.offset; + argsJs["size"] = currArg->size; + argsJs["buffer"] = currArg->buffer; + if (currArg->type == MTL_ARG_LITERAL_VERTEX_CONST || currArg->type == MTL_ARG_LITERAL_PIXEL_CONST) + { + argsJs["u"]["const0"] = currArg->u.literalConst[0]; + argsJs["u"]["const1"] = currArg->u.literalConst[1]; + argsJs["u"]["const2"] = currArg->u.literalConst[2]; + argsJs["u"]["const3"] = currArg->u.literalConst[3]; + } + else + { + argsJs["u"]["value"] = currArg->u.nameHash; + } + + passJs["args"].push_back(argsJs); + } + } + + nlohmann::json pixelJs = nlohmann::json::object(); + if (currPass->pixelShader != NULL) + { + pixelJs["name"] = currPass->pixelShader->name; + } + passJs["pixelShader"] = pixelJs; + + nlohmann::json vertexJs = nlohmann::json::object(); + if (currPass->vertexShader != NULL) + { + vertexJs["name"] = currPass->vertexShader->name; + } + passJs["vertexShader"] = vertexJs; + + techniqueJs["passArray"].push_back(passJs); + } + } + + js["techniques"].push_back(techniqueJs); + } + + std::string jsonString = js.dump(4); + assetFile->write(jsonString.c_str(), jsonString.size()); +} diff --git a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperTechniqueSet.h b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperTechniqueSet.h new file mode 100644 index 00000000..d31e93df --- /dev/null +++ b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperTechniqueSet.h @@ -0,0 +1,10 @@ +#pragma once + +#include "Dumping/AbstractAssetDumper.h" +#include "Game/T6/T6.h" + +class AssetDumperTechniqueSet final : public AbstractAssetDumper +{ +protected: + void DumpAsset(AssetDumpingContext& context, const XAssetInfo& asset) override; +}; diff --git a/src/ObjWriting/Game/T6/ObjWriterT6.cpp b/src/ObjWriting/Game/T6/ObjWriterT6.cpp index f4d09da2..e98f8c26 100644 --- a/src/ObjWriting/Game/T6/ObjWriterT6.cpp +++ b/src/ObjWriting/Game/T6/ObjWriterT6.cpp @@ -1,5 +1,6 @@ #include "ObjWriterT6.h" +#include "AssetDumpers/AssetDumperTechniqueSet.h" #include "FontIcon/FontIconDumperT6.h" #include "Game/T6/Material/MaterialJsonDumperT6.h" #include "Game/T6/Techset/TechsetDumperT6.h" @@ -35,6 +36,7 @@ void ObjWriter::RegisterAssetDumpers(AssetDumpingContext& context) // REGISTER_DUMPER(AssetDumperXAnimParts, m_xanim_parts) RegisterAssetDumper(std::make_unique()); RegisterAssetDumper(std::make_unique()); + RegisterAssetDumper(std::make_unique()); RegisterAssetDumper(std::make_unique( #ifdef TECHSET_DEBUG true