iw4x-client/src/Components/Modules/AssetInterfaces/IMaterialTechniqueSet.cpp

358 lines
12 KiB
C++
Raw Normal View History

2022-02-27 07:53:44 -05:00
#include <STDInclude.hpp>
#include "IMaterialTechniqueSet.hpp"
2016-01-22 05:59:43 -05:00
2022-12-14 03:40:15 -05:00
#define IW4X_TECHSET_VERSION 1
2016-01-22 05:59:43 -05:00
namespace Assets
{
void IMaterialTechniqueSet::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
{
2022-12-14 03:40:15 -05:00
if (!header->data) this->loadFromDisk(header, name, builder); // Check if we need to import a new one into the game
if (!header->data) this->loadNative(header, name, builder); // Check if there is a native one
}
void IMaterialTechniqueSet::loadNative(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* /*builder*/)
{
header->techniqueSet = Components::AssetHandler::FindOriginalAsset(this->getType(), name.data()).techniqueSet;
}
2022-12-14 03:40:15 -05:00
void IMaterialTechniqueSet::loadTechniqueFromDisk(Game::MaterialTechnique** tech, const std::string& name, Components::ZoneBuilder::Zone* builder)
{
AssertSize(Game::MaterialPass, 20);
2022-12-14 03:40:15 -05:00
Components::FileSystem::File techFile(std::format("techniques/{}.iw4x.json", name));
if (!techFile.exists())
{
*tech = nullptr;
2022-12-14 03:40:15 -05:00
Components::Logger::Warning(Game::CON_CHANNEL_DONT_FILTER, "Missing technique '{}'\n", name);
return;
}
2022-12-14 03:40:15 -05:00
nlohmann::json technique;
2022-12-14 03:40:15 -05:00
try
{
technique = nlohmann::json::parse(techFile.getBuffer());
}
catch (std::exception& e)
{
2022-12-14 03:40:15 -05:00
Components::Logger::Error(Game::ERR_FATAL, "Reading techset '{}' failed, file is messed up! {}", name, e.what());
}
2022-12-14 03:40:15 -05:00
int version = technique["version"].get<int>();
if (version != IW4X_TECHSET_VERSION)
{
Components::Logger::Error(Game::ERR_FATAL,
2022-12-14 03:40:15 -05:00
"Reading technique '{}' failed, expected version is {}, but it was {}!", name, IW4X_TECHSET_VERSION, version);
}
2022-12-14 03:40:15 -05:00
unsigned short flags = static_cast<unsigned short>(Utils::Json::ReadFlags(technique["flags"].get<std::string>(), sizeof(short)));
2022-12-14 03:40:15 -05:00
if (technique["passArray"].is_array())
{
nlohmann::json::array_t passArray = technique["passArray"];
2022-12-14 03:40:15 -05:00
Game::MaterialTechnique* asset = (Game::MaterialTechnique*)builder->getAllocator()->allocateArray<unsigned char>(sizeof(Game::MaterialTechnique) + (sizeof(Game::MaterialPass) * (passArray.size() - 1)));
2022-12-14 03:40:15 -05:00
asset->name = builder->getAllocator()->duplicateString(name);
asset->flags = flags;
asset->passCount = static_cast<unsigned short>(passArray.size());
2022-12-14 03:40:15 -05:00
Game::MaterialPass* passes = builder->getAllocator()->allocateArray<Game::MaterialPass>(asset->passCount);
std::memcpy(asset->passArray, passes, sizeof(Game::MaterialPass) * asset->passCount);
2022-12-14 03:40:15 -05:00
for (unsigned short i = 0; i < asset->passCount; i++)
{
2022-12-14 03:40:15 -05:00
Game::MaterialPass* pass = &asset->passArray[i];
auto jsonPass = passArray[i];
2022-12-14 03:40:15 -05:00
if (jsonPass["vertexDeclaration"].is_string())
{
auto declName = jsonPass["vertexDeclaration"].get<std::string>();
pass->vertexDecl = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_VERTEXDECL, declName, builder).vertexDecl;
}
2022-12-14 03:40:15 -05:00
if (jsonPass["vertexShader"].is_string())
{
auto vsName = jsonPass["vertexShader"].get<std::string>();
pass->vertexShader = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_VERTEXSHADER, vsName, builder).vertexShader;
}
2022-12-14 03:40:15 -05:00
if (jsonPass["pixelShader"].is_string())
{
2022-12-14 03:40:15 -05:00
auto psName = jsonPass["pixelShader"].get<std::string>();
pass->pixelShader = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_PIXELSHADER, psName, builder).pixelShader;
}
2022-12-14 03:40:15 -05:00
pass->perPrimArgCount = jsonPass["perPrimArgCount"].get<char>();
pass->perObjArgCount = jsonPass["perObjArgCount"].get<char>();
pass->stableArgCount = jsonPass["stableArgCount"].get<char>();
pass->customSamplerFlags = jsonPass["customSamplerFlags"].get<char>();
if (jsonPass["arguments"].is_array())
{
2022-12-14 03:40:15 -05:00
nlohmann::json::array_t jsonAguments = jsonPass["arguments"];
pass->args = builder->getAllocator()->allocateArray<Game::MaterialShaderArgument>(jsonAguments.size());
for (size_t j = 0; j < jsonAguments.size(); j++)
{
auto jsonArgument = jsonAguments[j];
Game::MaterialShaderArgument* argument = &pass->args[j];
argument->type = jsonArgument["type"].get<Game::MaterialShaderArgumentType>();
argument->dest = jsonArgument["dest"].get<unsigned short>();
if (argument->type == Game::MaterialShaderArgumentType::MTL_ARG_LITERAL_VERTEX_CONST ||
argument->type == Game::MaterialShaderArgumentType::MTL_ARG_LITERAL_PIXEL_CONST)
{
argument->u.literalConst = builder->getAllocator()->allocateArray<float>(4);
auto literals = jsonArgument["literals"].get<std::vector<float>>();
std::copy(literals.begin(), literals.end(), argument->u.literalConst);
}
else if (argument->type == Game::MaterialShaderArgumentType::MTL_ARG_CODE_VERTEX_CONST ||
argument->type == Game::MaterialShaderArgumentType::MTL_ARG_CODE_PIXEL_CONST)
{
if (jsonArgument["codeConst"].is_object())
{
auto codeConst = jsonArgument["codeConst"];
argument->u.codeConst.index = codeConst["index"].get<unsigned short>();
argument->u.codeConst.firstRow = codeConst["firstRow"].get<unsigned char>();
argument->u.codeConst.rowCount = codeConst["rowCount"].get<unsigned char>();
}
}
else if (argument->type == Game::MaterialShaderArgumentType::MTL_ARG_MATERIAL_PIXEL_SAMPLER ||
argument->type == Game::MaterialShaderArgumentType::MTL_ARG_MATERIAL_VERTEX_CONST ||
argument->type == Game::MaterialShaderArgumentType::MTL_ARG_MATERIAL_PIXEL_CONST)
{
argument->u.nameHash = jsonArgument["nameHash"].get<unsigned int>();
}
else if (argument->type == Game::MaterialShaderArgumentType::MTL_ARG_CODE_PIXEL_SAMPLER)
{
argument->u.codeSampler = jsonArgument["codeSampler"].get<unsigned int>();
}
}
}
}
2022-12-14 03:40:15 -05:00
*tech = asset;
}
}
2022-12-14 03:40:15 -05:00
void IMaterialTechniqueSet::loadFromDisk(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
{
2022-12-14 03:40:15 -05:00
Components::FileSystem::File tsFile(std::format("techsets/{}.iw4x.json", name));
if (!tsFile.exists()) return;
2022-12-14 03:40:15 -05:00
nlohmann::json techset;
2022-12-14 03:40:15 -05:00
try
{
techset = nlohmann::json::parse(tsFile.getBuffer());
}
catch (std::exception& e)
{
2022-12-14 03:40:15 -05:00
Components::Logger::Error(Game::ERR_FATAL, "Reading techset '{}' failed, file is messed up! {}", name, e.what());
}
2022-12-14 03:40:15 -05:00
auto version = techset["version"].get<int>();
if (version != IW4X_TECHSET_VERSION)
{
Components::Logger::Error(Game::ERR_FATAL, "Reading techset '{}' failed, expected version is {}, but it was {}!",
2022-06-12 17:07:53 -04:00
name, IW4X_TECHSET_VERSION, version);
}
2022-12-14 03:40:15 -05:00
Game::MaterialTechniqueSet* asset = builder->getAllocator()->allocate<Game::MaterialTechniqueSet>();
if (asset == nullptr)
{
Components::Logger::Error(Game::ERR_FATAL, "Reading techset '{}' failed, allocation failed!", name);
return;
}
2022-12-14 03:40:15 -05:00
if (techset["name"].is_string())
{
2022-12-14 03:40:15 -05:00
asset->name = builder->getAllocator()->duplicateString(techset["name"].get<std::string>());
}
2022-12-14 03:40:15 -05:00
asset->hasBeenUploaded = techset["hasBeenUploaded"].get<bool>();
asset->worldVertFormat = techset["worldVertFormat"].get<char>();
if (techset["remappedTechniqueSet"].is_string())
{
2022-12-14 03:40:15 -05:00
auto remapped = techset["remappedTechniqueSet"].get<std::string>();
if (remapped != asset->name)
{
2022-12-14 03:40:15 -05:00
builder->loadAssetByName(Game::XAssetType::ASSET_TYPE_TECHNIQUE_SET, remapped, false);
}
}
2022-12-14 03:40:15 -05:00
if (techset["techniques"].is_object())
{
for (int i = 0; i < Game::TECHNIQUE_COUNT; i++)
{
auto technique = techset["techniques"].at(std::to_string(i));
if (technique.is_string())
{
this->loadTechniqueFromDisk(&asset->techniques[i], technique.get<std::string>(), builder);
}
}
}
header->techniqueSet = asset;
}
void IMaterialTechniqueSet::mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder)
2022-12-14 03:40:15 -05:00
{
Game::MaterialTechniqueSet* asset = header.techniqueSet;
2016-01-22 05:59:43 -05:00
2016-09-16 18:14:59 -04:00
for (int i = 0; i < ARRAYSIZE(Game::MaterialTechniqueSet::techniques); ++i)
2016-01-22 05:59:43 -05:00
{
Game::MaterialTechnique* technique = asset->techniques[i];
if (!technique) continue;
2017-04-22 15:47:04 -04:00
for (short j = 0; j < technique->passCount; ++j)
2016-01-22 05:59:43 -05:00
{
2017-04-22 15:47:04 -04:00
Game::MaterialPass* pass = &technique->passArray[j];
2016-01-22 05:59:43 -05:00
if (pass->vertexDecl)
{
builder->loadAsset(Game::XAssetType::ASSET_TYPE_VERTEXDECL, pass->vertexDecl);
2016-01-22 05:59:43 -05:00
}
if (pass->vertexShader)
{
builder->loadAsset(Game::XAssetType::ASSET_TYPE_VERTEXSHADER, pass->vertexShader);
2016-01-22 05:59:43 -05:00
}
if (pass->pixelShader)
{
builder->loadAsset(Game::XAssetType::ASSET_TYPE_PIXELSHADER, pass->pixelShader);
2016-01-22 05:59:43 -05:00
}
}
}
}
void IMaterialTechniqueSet::save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder)
2016-01-22 05:59:43 -05:00
{
AssertSize(Game::MaterialTechniqueSet, 204);
2016-01-22 05:59:43 -05:00
Utils::Stream* buffer = builder->getBuffer();
2022-12-14 03:40:15 -05:00
Game::MaterialTechniqueSet* asset = header.techniqueSet;
Game::MaterialTechniqueSet* dest = buffer->dest<Game::MaterialTechniqueSet>();
2022-12-14 03:40:15 -05:00
buffer->save(asset);
2016-01-22 05:59:43 -05:00
buffer->pushBlock(Game::XFILE_BLOCK_VIRTUAL);
2016-01-22 05:59:43 -05:00
if (asset->name)
{
buffer->saveString(builder->getAssetName(this->getType(), asset->name));
Utils::Stream::ClearPointer(&dest->name);
2016-01-22 05:59:43 -05:00
}
// Save_MaterialTechniquePtrArray
2016-09-16 18:14:59 -04:00
static_assert(ARRAYSIZE(Game::MaterialTechniqueSet::techniques) == 48, "Techniques array invalid!");
2016-01-22 05:59:43 -05:00
2016-09-16 18:14:59 -04:00
for (int i = 0; i < ARRAYSIZE(Game::MaterialTechniqueSet::techniques); ++i)
2016-01-22 05:59:43 -05:00
{
Game::MaterialTechnique* technique = asset->techniques[i];
if (technique)
{
if (builder->hasPointer(technique))
2016-01-22 07:18:26 -05:00
{
dest->techniques[i] = builder->getPointer(technique);
2016-01-22 07:18:26 -05:00
}
else
2016-01-22 05:59:43 -05:00
{
2016-01-22 07:18:26 -05:00
// Size-check is obsolete, as the structure is dynamic
buffer->align(Utils::Stream::ALIGN_4);
builder->storePointer(technique);
2016-01-22 05:59:43 -05:00
Game::MaterialTechnique* destTechnique = buffer->dest<Game::MaterialTechnique>();
buffer->save(technique, 8);
2016-01-22 05:59:43 -05:00
2016-01-22 07:18:26 -05:00
// Save_MaterialPassArray
Game::MaterialPass* destPasses = buffer->dest<Game::MaterialPass>();
2017-04-22 15:47:04 -04:00
buffer->saveArray(technique->passArray, technique->passCount);
2017-04-22 15:47:04 -04:00
for (short j = 0; j < technique->passCount; ++j)
2016-01-22 05:59:43 -05:00
{
AssertSize(Game::MaterialPass, 20);
2016-01-22 07:18:26 -05:00
Game::MaterialPass* destPass = &destPasses[j];
2017-04-22 15:47:04 -04:00
Game::MaterialPass* pass = &technique->passArray[j];
2016-01-22 07:18:26 -05:00
if (pass->vertexDecl)
{
destPass->vertexDecl = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_VERTEXDECL, pass->vertexDecl).vertexDecl;
2016-01-22 07:18:26 -05:00
}
if (pass->vertexShader)
{
destPass->vertexShader = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_VERTEXSHADER, pass->vertexShader).vertexShader;
2016-01-22 07:18:26 -05:00
}
if (pass->pixelShader)
{
destPass->pixelShader = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_PIXELSHADER, pass->pixelShader).pixelShader;
2016-01-22 07:18:26 -05:00
}
2017-04-22 15:47:04 -04:00
if (pass->args)
2016-01-22 07:18:26 -05:00
{
buffer->align(Utils::Stream::ALIGN_4);
Game::MaterialShaderArgument* destArgs = buffer->dest<Game::MaterialShaderArgument>();
2017-04-22 15:47:04 -04:00
buffer->saveArray(pass->args, pass->perPrimArgCount + pass->perObjArgCount + pass->stableArgCount);
for (int k = 0; k < pass->perPrimArgCount + pass->perObjArgCount + pass->stableArgCount; ++k)
{
Game::MaterialShaderArgument* arg = &pass->args[k];
Game::MaterialShaderArgument* destArg = &destArgs[k];
if (arg->type == 1 || arg->type == 7)
{
if (builder->hasPointer(arg->u.literalConst))
{
destArg->u.literalConst = builder->getPointer(arg->u.literalConst);
}
else
{
buffer->align(Utils::Stream::ALIGN_4);
builder->storePointer(arg->u.literalConst);
buffer->saveArray(arg->u.literalConst, 4);
Utils::Stream::ClearPointer(&destArg->u.literalConst);
}
}
}
2017-04-22 15:47:04 -04:00
Utils::Stream::ClearPointer(&destPass->args);
2016-01-22 07:18:26 -05:00
}
2016-01-22 05:59:43 -05:00
}
2016-01-22 07:18:26 -05:00
if (technique->name)
2016-01-22 05:59:43 -05:00
{
buffer->saveString(technique->name);
Utils::Stream::ClearPointer(&destTechnique->name);
2016-01-22 05:59:43 -05:00
}
Utils::Stream::ClearPointer(&dest->techniques[i]);
2016-01-22 05:59:43 -05:00
}
}
}
buffer->popBlock();
2016-01-22 05:59:43 -05:00
}
}