OpenAssetTools/src/ObjLoading/Game/IW4/AssetLoaders/AssetLoaderMaterial.cpp
2022-08-07 19:23:30 +02:00

1315 lines
52 KiB
C++

#include "AssetLoaderMaterial.h"
#include <cstring>
#include <cmath>
#include <iostream>
#include <sstream>
#include "AssetLoaderTechniqueSet.h"
#include "ObjLoading.h"
#include "AssetLoading/AbstractGdtEntryReader.h"
#include "Game/IW4/CommonIW4.h"
#include "Game/IW4/IW4.h"
#include "Game/IW4/MaterialConstantsIW4.h"
#include "Game/IW4/ObjConstantsIW4.h"
#include "Math/Vector.h"
#include "Pool/GlobalAssetPool.h"
using namespace IW4;
namespace IW4
{
class SkipMaterialException final : public std::exception
{
};
class MaterialGdtLoader : AbstractGdtEntryReader
{
public:
MaterialGdtLoader(const GdtEntry& entry, MemoryManager* memory, IAssetLoadingManager* manager)
: AbstractGdtEntryReader(entry),
m_memory(memory),
m_manager(manager),
m_material(nullptr),
m_base_state_bits{}
{
}
bool Load()
{
m_material = m_memory->Create<Material>();
memset(m_material, 0, sizeof(Material));
m_material->info.name = m_memory->Dup(m_entry.m_name.c_str());
material_template();
FinalizeMaterial();
return true;
}
_NODISCARD Material* GetMaterial() const
{
return m_material;
}
_NODISCARD std::vector<XAssetInfoGeneric*> GetDependencies()
{
return std::move(m_dependencies);
}
private:
void material_template()
{
const auto materialType = ReadStringProperty("materialType");
if (materialType == GDT_MATERIAL_TYPE_MODEL_PHONG
|| materialType == GDT_MATERIAL_TYPE_WORLD_PHONG
|| materialType == GDT_MATERIAL_TYPE_IMPACT_MARK)
{
mtl_phong_template();
}
else if (materialType == GDT_MATERIAL_TYPE_MODEL_AMBIENT)
{
mtl_ambient_template();
}
else if (materialType == GDT_MATERIAL_TYPE_2D)
{
mtl_2d_template();
}
else if (materialType == GDT_MATERIAL_TYPE_MODEL_UNLIT
|| materialType == GDT_MATERIAL_TYPE_WORLD_UNLIT)
{
mtl_unlit_template();
}
else if (materialType == GDT_MATERIAL_TYPE_UNLIT)
{
mtl_unlit_deprecated_template();
}
else if (materialType == GDT_MATERIAL_TYPE_EFFECT)
{
mtl_effect_template();
}
else if (materialType == GDT_MATERIAL_TYPE_DISTORTION)
{
mtl_distortion_template();
}
else if (materialType == GDT_MATERIAL_TYPE_PARTICLE_CLOUD)
{
mtl_particlecloud_template();
}
else if (materialType == GDT_MATERIAL_TYPE_TOOLS)
{
mtl_tools_template();
}
else if (materialType == GDT_MATERIAL_TYPE_SKY)
{
mtl_sky_template();
}
else if (materialType == GDT_MATERIAL_TYPE_WATER)
{
mtl_water_template();
}
else if (materialType == GDT_MATERIAL_TYPE_OBJECTIVE)
{
mtl_objective_template();
}
else if (materialType == GDT_MATERIAL_TYPE_CUSTOM)
{
custom_template();
}
else
{
std::ostringstream ss;
ss << "Unknown material type: \"" << materialType << "\"";
throw GdtReadingException(ss.str());
}
}
void mtl_phong_template()
{
// TODO
throw SkipMaterialException();
}
void mtl_ambient_template()
{
// TODO
throw SkipMaterialException();
}
void mtl_2d_template()
{
commonsetup_template();
SetTechniqueSet("2d");
const auto colorMapName = ReadStringProperty("colorMap");
const auto tileColor = ReadEnumProperty<TileMode_e>("tileColor", GdtTileModeNames, std::extent_v<decltype(GdtTileModeNames)>);
const auto filterColor = ReadEnumProperty<GdtFilter_e>("filterColor", GdtSamplerFilterNames, std::extent_v<decltype(GdtSamplerFilterNames)>);
if (!colorMapName.empty())
AddMapTexture("colorMap", tileColor, filterColor, TS_2D, colorMapName);
else
throw GdtReadingException("ColorMap may not be blank in 2d materials");
}
void mtl_unlit_template()
{
// TODO
throw SkipMaterialException();
}
void mtl_unlit_deprecated_template()
{
// TODO
throw SkipMaterialException();
}
void mtl_effect_template()
{
commonsetup_template();
unitlitcommon_template();
}
void mtl_distortion_template()
{
commonsetup_template();
const auto uvAnim = ReadBoolProperty("uvAnim");
if (uvAnim)
SetTechniqueSet("distortion_scale_ua_zfeather");
else
SetTechniqueSet("distortion_scale_zfeather");
const auto colorMapName = ReadStringProperty("colorMap");
const auto tileColor = ReadEnumProperty<TileMode_e>("tileColor", GdtTileModeNames, std::extent_v<decltype(GdtTileModeNames)>);
const auto filterColor = ReadEnumProperty<GdtFilter_e>("filterColor", GdtSamplerFilterNames, std::extent_v<decltype(GdtSamplerFilterNames)>);
if (!colorMapName.empty())
AddMapTexture("colorMap", tileColor, filterColor, TS_COLOR_MAP, colorMapName);
else
throw GdtReadingException("ColorMap may not be blank in tools materials");
const auto distortionScaleX = ReadFloatProperty("distortionScaleX");
const auto distortionScaleY = ReadFloatProperty("distortionScaleY");
AddConstant("distortionScale", Vector4f(distortionScaleX, distortionScaleY, 0, 0));
if (uvAnim)
{
const auto uvScrollX = ReadFloatProperty("uvScrollX");
const auto uvScrollY = ReadFloatProperty("uvScrollY");
const auto uvScrollRotate = ReadFloatProperty("uvScrollRotate");
AddConstant("uvAnimParms", Vector4f(uvScrollX, uvScrollY, uvScrollRotate, 0));
}
}
void mtl_particlecloud_template()
{
refblend_template();
sort_template();
clamp_template();
SetTextureAtlas(1, 1);
// tessSize(0)
// hasEditorMaterial
// allocLightmap
statebits_template();
std::string outdoorSuffix;
const auto outdoorOnly = ReadBoolProperty("outdoorOnly");
if (outdoorOnly)
outdoorSuffix = "_outdoor";
std::string addSuffix;
const auto blendFunc = ReadStringProperty("blendFunc");
if (blendFunc == GDT_BLEND_FUNC_ADD || blendFunc == GDT_BLEND_FUNC_SCREEN_ADD)
addSuffix = "_add";
std::string spotSuffix;
const auto useSpotLight = ReadBoolProperty("useSpotLight");
if (useSpotLight)
spotSuffix = "_spot";
if (outdoorOnly && useSpotLight)
throw GdtReadingException("Outdoor and spot aren't supported on particle cloud materials");
std::ostringstream ss;
ss << "particle_cloud" << outdoorSuffix << addSuffix << spotSuffix;
SetTechniqueSet(ss.str());
const auto colorMapName = ReadStringProperty("colorMap");
const auto tileColor = ReadEnumProperty<TileMode_e>("tileColor", GdtTileModeNames, std::extent_v<decltype(GdtTileModeNames)>);
const auto filterColor = ReadEnumProperty<GdtFilter_e>("filterColor", GdtSamplerFilterNames, std::extent_v<decltype(GdtSamplerFilterNames)>);
if (!colorMapName.empty())
AddMapTexture("colorMap", tileColor, filterColor, TS_COLOR_MAP, colorMapName);
else
throw GdtReadingException("ColorMap may not be blank in particle cloud materials");
std::cout << "Using particlecloud for \"" << m_material->info.name << "\"\n";
}
void mtl_tools_template()
{
commonsetup_template();
SetTechniqueSet("tools");
AddMapTexture("normalMap", TileMode_e::NO_TILE, GdtFilter_e::NOMIP_NEAREST, TS_NORMAL_MAP, "$identitynormalmap");
const auto colorMapName = ReadStringProperty("colorMap");
const auto tileColor = ReadEnumProperty<TileMode_e>("tileColor", GdtTileModeNames, std::extent_v<decltype(GdtTileModeNames)>);
const auto filterColor = ReadEnumProperty<GdtFilter_e>("filterColor", GdtSamplerFilterNames, std::extent_v<decltype(GdtSamplerFilterNames)>);
if (!colorMapName.empty())
AddMapTexture("colorMap", tileColor, filterColor, TS_COLOR_MAP, colorMapName);
else
throw GdtReadingException("ColorMap may not be blank in tools materials");
const auto colorTint = ReadVec4Property("colorTint", {1.0f, 1.0f, 1.0f, 1.0f});
AddConstant("colorTint", colorTint);
}
void mtl_sky_template()
{
// TODO
throw SkipMaterialException();
}
void mtl_water_template()
{
// TODO
throw SkipMaterialException();
}
void mtl_objective_template()
{
// TODO
throw SkipMaterialException();
}
void custom_template()
{
const auto customTemplate = ReadStringProperty("customTemplate");
if (customTemplate == GDT_CUSTOM_MATERIAL_TYPE_CUSTOM)
{
mtl_custom_template();
}
else if (customTemplate == GDT_CUSTOM_MATERIAL_TYPE_PHONG_FLAG)
{
mtl_phong_flag_template();
}
else if (customTemplate == GDT_CUSTOM_MATERIAL_TYPE_GRAIN_OVERLAY)
{
mtl_grain_overlay_template();
}
else if (customTemplate == GDT_CUSTOM_MATERIAL_TYPE_EFFECT_EYE_OFFSET)
{
mtl_effect_eyeoffset_template();
}
else if (customTemplate == GDT_CUSTOM_MATERIAL_TYPE_REFLEXSIGHT)
{
mtl_reflexsight_template();
}
else if (customTemplate == GDT_CUSTOM_MATERIAL_TYPE_SHADOWCLEAR)
{
mtl_shadowclear_template();
}
else if (customTemplate == GDT_CUSTOM_MATERIAL_TYPE_SHADOWOVERLAY)
{
mtl_shadowoverlay_template();
}
else if (customTemplate == GDT_CUSTOM_MATERIAL_TYPE_SPLATTER)
{
mtl_splatter_template();
}
else
{
std::ostringstream ss;
ss << "Unknown custom template: \"" << customTemplate << "\"";
throw GdtReadingException(ss.str());
}
}
void mtl_custom_template()
{
// TODO
throw SkipMaterialException();
}
void mtl_phong_flag_template()
{
// TODO
throw SkipMaterialException();
}
void mtl_grain_overlay_template()
{
// TODO
throw SkipMaterialException();
}
void mtl_effect_eyeoffset_template()
{
// TODO
throw SkipMaterialException();
}
void mtl_reflexsight_template()
{
// TODO
throw SkipMaterialException();
}
void mtl_shadowclear_template()
{
// TODO
throw SkipMaterialException();
}
void mtl_shadowoverlay_template()
{
// TODO
throw SkipMaterialException();
}
void mtl_splatter_template()
{
// TODO
throw SkipMaterialException();
}
void unitlitcommon_template()
{
const auto outdoorOnly = ReadBoolProperty("outdoorOnly");
const auto blendFunc = ReadStringProperty("blendFunc");
std::string distFalloffSuffix;
const auto distFalloff = ReadBoolProperty("distFalloff");
if (distFalloff)
{
const auto hdrPortal = ReadBoolProperty("hdrPortal");
if (!hdrPortal)
throw GdtReadingException("Cannot have distance falloff active without hdrPortal.");
if (blendFunc == GDT_BLEND_FUNC_MULTIPLY)
throw GdtReadingException("Distance falloff does not currently support Multiply.");
if (outdoorOnly)
throw GdtReadingException("Distance falloff does not currently support outdoor-only types.");
distFalloffSuffix = "_falloff";
}
std::string godFalloffSuffix;
const auto falloff = ReadBoolProperty("falloff");
if (falloff)
{
if (blendFunc == GDT_BLEND_FUNC_MULTIPLY)
throw GdtReadingException("Falloff does not currently support Multiply.");
if (outdoorOnly)
throw GdtReadingException("Falloff does not currently support outdoor-only types.");
godFalloffSuffix = "_falloff";
}
std::string noFogSuffix;
const auto noFog = ReadBoolProperty("noFog");
if (noFog)
noFogSuffix = "_nofog";
std::string spotSuffix;
const auto useSpotLight = ReadBoolProperty("useSpotLight");
if (useSpotLight)
spotSuffix = "_spot";
std::string eyeOffsetSuffix;
const auto eyeOffsetDepth = ReadFloatProperty("eyeOffsetDepth");
if (std::fpclassify(eyeOffsetDepth) != FP_ZERO)
eyeOffsetSuffix = "_eyeoffset";
const auto materialType = ReadStringProperty("materialType");
const auto zFeather = ReadBoolProperty("zFeather");
if (materialType == GDT_MATERIAL_TYPE_EFFECT && zFeather)
{
if (blendFunc == GDT_BLEND_FUNC_MULTIPLY)
throw GdtReadingException("zFeather does not support multiply.");
std::string addSuffix;
if (blendFunc == GDT_BLEND_FUNC_ADD || blendFunc == GDT_BLEND_FUNC_SCREEN_ADD)
addSuffix = "_add";
if (outdoorOnly)
{
std::ostringstream ss;
ss << "effect_zfeather_outdoor" << addSuffix << noFogSuffix << spotSuffix << eyeOffsetSuffix;
SetTechniqueSet(ss.str());
}
else
{
std::ostringstream ss;
ss << "effect_zfeather" << distFalloffSuffix << godFalloffSuffix << addSuffix << noFogSuffix << spotSuffix << eyeOffsetSuffix;
SetTechniqueSet(ss.str());
}
}
else
{
std::string baseTechName = materialType == GDT_MATERIAL_TYPE_EFFECT ? "effect" : "unlit";
if (blendFunc == GDT_BLEND_FUNC_MULTIPLY)
{
std::ostringstream ss;
ss << baseTechName << "_multiply" << noFogSuffix << spotSuffix << eyeOffsetSuffix;
SetTechniqueSet(ss.str());
}
else
{
std::string addSuffix;
if (blendFunc == GDT_BLEND_FUNC_ADD || blendFunc == GDT_BLEND_FUNC_SCREEN_ADD)
addSuffix = "_add";
std::ostringstream ss;
ss << baseTechName << distFalloffSuffix << godFalloffSuffix << addSuffix << noFogSuffix << spotSuffix << eyeOffsetSuffix;
SetTechniqueSet(ss.str());
}
}
const auto colorMapName = ReadStringProperty("colorMap");
const auto tileColor = ReadEnumProperty<TileMode_e>("tileColor", GdtTileModeNames, std::extent_v<decltype(GdtTileModeNames)>);
const auto filterColor = ReadEnumProperty<GdtFilter_e>("filterColor", GdtSamplerFilterNames, std::extent_v<decltype(GdtSamplerFilterNames)>);
if (!colorMapName.empty())
AddMapTexture("colorMap", tileColor, filterColor, TS_COLOR_MAP, colorMapName);
else
throw GdtReadingException("ColorMap may not be blank in effect/unlit materials");
if (falloff || distFalloff)
{
// TODO
}
if (zFeather)
{
const auto zFeatherDepth = ReadFloatProperty("zFeatherDepth");
if (std::fpclassify(zFeatherDepth) == FP_ZERO)
throw GdtReadingException("zFeatherDepth may not be zero");
AddConstant("featherParms", Vector4f(1.0f / zFeatherDepth, zFeatherDepth, 0, 0));
}
if (std::fpclassify(eyeOffsetDepth) != FP_ZERO)
AddConstant("eyeOffsetParms", Vector4f(eyeOffsetDepth, 0, 0, 0));
const auto colorTint = ReadVec4Property("colorTint", {1.0f, 1.0f, 1.0f, 1.0f});
AddConstant("colorTint", colorTint);
}
void commonsetup_template()
{
refblend_template();
sort_template();
clamp_template();
// tessSize
textureAtlas_template();
// hasEditorMaterial
// allocLightmap
statebits_template();
}
void refblend_template()
{
const auto blendFunc = ReadStringProperty("blendFunc");
}
void sort_template()
{
const auto sort = ReadStringProperty("sort");
const auto materialType = ReadStringProperty("materialType");
const auto polygonOffset = ReadStringProperty("polygonOffset");
const auto blendFunc = ReadStringProperty("blendFunc");
std::string sortKey;
if (sort.empty() || sort == GDT_SORTKEY_DEFAULT)
{
if (materialType == GDT_MATERIAL_TYPE_DISTORTION)
sortKey = GDT_SORTKEY_DISTORTION;
else if (polygonOffset == "Static Decal")
sortKey = GDT_SORTKEY_DECAL_STATIC;
else if (polygonOffset == "Weapon Impact")
sortKey = GDT_SORTKEY_DECAL_WEAPON_IMPACT;
else if (materialType == GDT_MATERIAL_TYPE_EFFECT)
sortKey = GDT_SORTKEY_EFFECT_AUTO_SORT;
else if (materialType == GDT_MATERIAL_TYPE_OBJECTIVE
|| blendFunc == "Blend" || blendFunc == "Add" || blendFunc == "Screen Add")
sortKey = GDT_SORTKEY_BLEND_ADDITIVE;
// else if (blendFunc == "Multiply") // TODO
// sortKey = GDT_SORTKEY_MULTIPLICATIVE;
else if (materialType == GDT_MATERIAL_TYPE_SKY)
sortKey = GDT_SORTKEY_SKY;
else if (materialType == GDT_MATERIAL_TYPE_MODEL_AMBIENT)
sortKey = GDT_SORTKEY_OPAQUE_AMBIENT;
else
sortKey = GDT_SORTKEY_OPAQUE;
}
else
sortKey = sort;
bool foundSortKey = false;
for (auto sortKeyIndex = 0u; sortKeyIndex < SORTKEY_MAX; sortKeyIndex++)
{
if (SortKeyNames[sortKeyIndex] && sortKey == SortKeyNames[sortKeyIndex])
{
SetSort(static_cast<unsigned char>(sortKeyIndex));
foundSortKey = true;
break;
}
}
if (!foundSortKey)
{
char* endPtr;
const auto sortKeyNum = strtoul(sortKey.c_str(), &endPtr, 10);
if (endPtr != &sortKey[sortKey.size()])
{
std::ostringstream ss;
ss << "Invalid sort value: \"" << sortKey << "\"";
throw GdtReadingException(ss.str());
}
SetSort(static_cast<unsigned char>(sortKeyNum));
}
}
void clamp_template()
{
}
void textureAtlas_template()
{
const auto rowCount = ReadIntegerProperty("textureAtlasRowCount", 1);
const auto columnCount = ReadIntegerProperty("textureAtlasColumnCount", 1);
SetTextureAtlas(static_cast<unsigned char>(rowCount), static_cast<unsigned char>(columnCount));
}
void statebits_template()
{
alphatest_template();
blendfunc_template();
colorwrite_template();
cullface_template();
depthtest_template();
depthwrite_template();
gammawrite_template();
polygonoffset_template();
stencil_template();
}
void alphatest_template()
{
const auto alphaTest = ReadStringProperty("alphaTest");
if (alphaTest == GDT_ALPHA_TEST_ALWAYS)
SetAlphaTest(AlphaTest_e::ALWAYS);
else if (alphaTest == GDT_ALPHA_TEST_GE128)
SetAlphaTest(AlphaTest_e::GE128);
else if (alphaTest == GDT_ALPHA_TEST_GT0) // TODO: This is not available for IW3
SetAlphaTest(AlphaTest_e::GT0);
else
{
std::ostringstream ss;
ss << "Invalid alphatest value: \"" << alphaTest << "\"";
throw GdtReadingException(ss.str());
}
}
void blendfunc_template()
{
const auto blendFunc = ReadStringProperty("blendFunc");
if (blendFunc == GDT_BLEND_FUNC_ADD)
{
SetBlendFunc(BlendOp_e::ADD, CustomBlendFunc_e::ONE, CustomBlendFunc_e::ONE);
SetSeparateAlphaBlendFunc(BlendOp_e::DISABLE, CustomBlendFunc_e::ONE, CustomBlendFunc_e::ZERO);
}
else if (blendFunc == GDT_BLEND_FUNC_BLEND)
{
SetBlendFunc(BlendOp_e::ADD, CustomBlendFunc_e::SRC_ALPHA, CustomBlendFunc_e::INV_SRC_ALPHA);
SetSeparateAlphaBlendFunc(BlendOp_e::DISABLE, CustomBlendFunc_e::ONE, CustomBlendFunc_e::ZERO);
}
else if (blendFunc == GDT_BLEND_FUNC_MULTIPLY)
{
SetBlendFunc(BlendOp_e::ADD, CustomBlendFunc_e::ZERO, CustomBlendFunc_e::SRC_COLOR);
SetSeparateAlphaBlendFunc(BlendOp_e::DISABLE, CustomBlendFunc_e::ONE, CustomBlendFunc_e::ZERO);
}
else if (blendFunc == GDT_BLEND_FUNC_REPLACE)
{
SetBlendFunc(BlendOp_e::DISABLE, CustomBlendFunc_e::ONE, CustomBlendFunc_e::ZERO);
SetSeparateAlphaBlendFunc(BlendOp_e::DISABLE, CustomBlendFunc_e::ONE, CustomBlendFunc_e::ZERO);
}
else if (blendFunc == GDT_BLEND_FUNC_SCREEN_ADD)
{
SetBlendFunc(BlendOp_e::ADD, CustomBlendFunc_e::INV_DST_ALPHA, CustomBlendFunc_e::ONE);
SetSeparateAlphaBlendFunc(BlendOp_e::DISABLE, CustomBlendFunc_e::ONE, CustomBlendFunc_e::ZERO);
}
else if (blendFunc == GDT_BLEND_FUNC_CUSTOM)
{
const auto customBlendOpRgb = ReadEnumProperty<BlendOp_e>("customBlendOpRgb", GdtBlendOpNames, std::extent_v<decltype(GdtBlendOpNames)>);
const auto srcCustomBlendFunc = ReadEnumProperty<CustomBlendFunc_e>("srcCustomBlendFunc", GdtCustomBlendFuncNames, std::extent_v<decltype(GdtCustomBlendFuncNames)>);
const auto destCustomBlendFunc = ReadEnumProperty<CustomBlendFunc_e>("destCustomBlendFunc", GdtCustomBlendFuncNames, std::extent_v<decltype(GdtCustomBlendFuncNames)>);
const auto customBlendOpAlpha = ReadEnumProperty<BlendOp_e>("customBlendOpAlpha", GdtBlendOpNames, std::extent_v<decltype(GdtBlendOpNames)>);
const auto srcCustomBlendFuncAlpha = ReadEnumProperty<CustomBlendFunc_e>("srcCustomBlendFuncAlpha", GdtCustomBlendFuncNames, std::extent_v<decltype(GdtCustomBlendFuncNames)>);
const auto destCustomBlendFuncAlpha = ReadEnumProperty<CustomBlendFunc_e>("destCustomBlendFuncAlpha", GdtCustomBlendFuncNames, std::extent_v<decltype(GdtCustomBlendFuncNames)>);
SetBlendFunc(customBlendOpRgb, srcCustomBlendFunc, destCustomBlendFunc);
SetSeparateAlphaBlendFunc(customBlendOpAlpha, srcCustomBlendFuncAlpha, destCustomBlendFuncAlpha);
}
else
{
std::ostringstream ss;
ss << "Invalid blendfunc value: \"" << blendFunc << "\"";
throw GdtReadingException(ss.str());
}
}
void colorwrite_template()
{
const auto colorWriteRed = ReadEnumProperty<StateBitsEnabledStatus_e>("colorWriteRed", GdtStateBitsEnabledStatusNames, std::extent_v<decltype(GdtStateBitsEnabledStatusNames)>);
const auto colorWriteGreen = ReadEnumProperty<StateBitsEnabledStatus_e>("colorWriteGreen", GdtStateBitsEnabledStatusNames, std::extent_v<decltype(GdtStateBitsEnabledStatusNames)>);
const auto colorWriteBlue = ReadEnumProperty<StateBitsEnabledStatus_e>("colorWriteBlue", GdtStateBitsEnabledStatusNames, std::extent_v<decltype(GdtStateBitsEnabledStatusNames)>);
const auto colorWriteAlpha = ReadEnumProperty<StateBitsEnabledStatus_e>("colorWriteAlpha", GdtStateBitsEnabledStatusNames, std::extent_v<decltype(GdtStateBitsEnabledStatusNames)>);
SetColorWrite(colorWriteRed, colorWriteGreen, colorWriteBlue, colorWriteAlpha);
}
void cullface_template()
{
const auto cullFace = ReadEnumProperty<CullFace_e>("cullFace", GdtCullFaceNames, std::extent_v<decltype(GdtCullFaceNames)>);
SetCullFace(cullFace);
}
void depthtest_template()
{
const auto depthTest = ReadEnumProperty<DepthTest_e>("depthTest", GdtDepthTestNames, std::extent_v<decltype(GdtDepthTestNames)>);
SetDepthTest(depthTest);
}
void depthwrite_template()
{
const auto depthWrite = ReadEnumProperty<StateBitsEnabledStatus_e>("depthWrite", GdtStateBitsOnOffStatusNames, std::extent_v<decltype(GdtStateBitsOnOffStatusNames)>);
const auto blendFunc = ReadStringProperty("blendFunc");
if (depthWrite == StateBitsEnabledStatus_e::ENABLED)
SetDepthWrite(true);
else if (depthWrite == StateBitsEnabledStatus_e::DISABLED)
SetDepthWrite(false);
else if (blendFunc == GDT_BLEND_FUNC_ADD)
SetDepthWrite(false);
else if (blendFunc == GDT_BLEND_FUNC_BLEND)
SetDepthWrite(false);
else if (blendFunc == GDT_BLEND_FUNC_MULTIPLY)
SetDepthWrite(false);
else if (blendFunc == GDT_BLEND_FUNC_REPLACE)
SetDepthWrite(true);
else if (blendFunc == GDT_BLEND_FUNC_SCREEN_ADD)
SetDepthWrite(false);
else if (blendFunc == GDT_BLEND_FUNC_CUSTOM)
SetDepthWrite(false);
else
{
std::ostringstream ss;
ss << "Invalid depthWrite blendFunc value: \"" << blendFunc << "\"";
throw GdtReadingException(ss.str());
}
}
void gammawrite_template()
{
const auto gammaWrite = ReadEnumProperty<StateBitsEnabledStatus_e>("gammaWrite", GdtStateBitsOnOffStatusNames, std::extent_v<decltype(GdtStateBitsOnOffStatusNames)>);
if (gammaWrite == StateBitsEnabledStatus_e::UNKNOWN)
{
std::ostringstream ss;
ss << "Invalid gammaWrite blendFunc value: \"\"";
throw GdtReadingException(ss.str());
}
SetGammaWrite(gammaWrite == StateBitsEnabledStatus_e::ENABLED);
}
void polygonoffset_template()
{
const auto polygonOffset = ReadEnumProperty<PolygonOffset_e>("polygonOffset", GdtPolygonOffsetNames, std::extent_v<decltype(GdtPolygonOffsetNames)>);
SetPolygonOffset(polygonOffset);
}
void stencil_template()
{
const auto stencilMode = ReadEnumProperty<StencilMode_e>("stencil", GdtStencilModeNames, std::extent_v<decltype(GdtStencilModeNames)>);
if (stencilMode == StencilMode_e::DISABLED)
{
DisableStencil(StencilIndex::FRONT);
DisableStencil(StencilIndex::BACK);
}
else
{
if (stencilMode == StencilMode_e::TWO_SIDED)
{
const auto stencilBackFunc = ReadEnumProperty<StencilFunc_e>("stencilFunc2", GdtStencilFuncNames, std::extent_v<decltype(GdtStencilFuncNames)>);
const auto stencilBackOpFail = ReadEnumProperty<StencilOp_e>("stencilOpFail2", GdtStencilOpNames, std::extent_v<decltype(GdtStencilOpNames)>);
const auto stencilBackOpZFail = ReadEnumProperty<StencilOp_e>("stencilOpZFail2", GdtStencilOpNames, std::extent_v<decltype(GdtStencilOpNames)>);
const auto stencilBackOpPass = ReadEnumProperty<StencilOp_e>("stencilOpPass2", GdtStencilOpNames, std::extent_v<decltype(GdtStencilOpNames)>);
EnableStencil(StencilIndex::BACK, stencilBackFunc, stencilBackOpFail, stencilBackOpZFail, stencilBackOpPass);
}
const auto stencilFrontFunc = ReadEnumProperty<StencilFunc_e>("stencilFunc1", GdtStencilFuncNames, std::extent_v<decltype(GdtStencilFuncNames)>);
const auto stencilFrontOpFail = ReadEnumProperty<StencilOp_e>("stencilOpFail1", GdtStencilOpNames, std::extent_v<decltype(GdtStencilOpNames)>);
const auto stencilFrontOpZFail = ReadEnumProperty<StencilOp_e>("stencilOpZFail1", GdtStencilOpNames, std::extent_v<decltype(GdtStencilOpNames)>);
const auto stencilFrontOpPass = ReadEnumProperty<StencilOp_e>("stencilOpPass1", GdtStencilOpNames, std::extent_v<decltype(GdtStencilOpNames)>);
EnableStencil(StencilIndex::FRONT, stencilFrontFunc, stencilFrontOpFail, stencilFrontOpZFail, stencilFrontOpPass);
}
}
void SetTechniqueSet(const std::string& techsetName)
{
auto* techset = reinterpret_cast<XAssetInfo<MaterialTechniqueSet>*>(m_manager->LoadDependency(ASSET_TYPE_TECHNIQUE_SET, techsetName));
if (techset == nullptr)
{
std::ostringstream ss;
ss << "Could not load techset: \"" << techsetName << "\"";
throw GdtReadingException(ss.str());
}
m_dependencies.push_back(techset);
m_material->techniqueSet = techset->Asset();
auto* loadingContext = m_manager->GetAssetLoadingContext();
auto* searchPath = loadingContext->m_raw_search_path;
auto* definitionCache = loadingContext->GetZoneAssetLoaderState<techset::TechsetDefinitionCache>();
const auto* techsetDefinition = AssetLoaderTechniqueSet::LoadTechsetDefinition(techsetName, searchPath, definitionCache);
if (techsetDefinition == nullptr)
{
std::ostringstream ss;
ss << "Could not find techset definition for: \"" << techsetName << "\"";
throw GdtReadingException(ss.str());
}
SetTechniqueSetStateBits(techsetDefinition);
SetTechniqueSetCameraRegion(techsetDefinition);
}
void SetTechniqueSetStateBits(const techset::TechsetDefinition* techsetDefinition)
{
for (auto i = 0; i < TECHNIQUE_COUNT; i++)
{
std::string techniqueName;
if (techsetDefinition->GetTechniqueByIndex(i, techniqueName))
{
const auto stateBitsForTechnique = GetStateBitsForTechnique(techniqueName);
const auto foundStateBits = std::find_if(m_state_bits.begin(), m_state_bits.end(),
[stateBitsForTechnique](const GfxStateBits& s1)
{
return s1.loadBits[0] == stateBitsForTechnique.loadBits[0] && s1.loadBits[1] == stateBitsForTechnique.loadBits[1];
});
if (foundStateBits != m_state_bits.end())
{
m_material->stateBitsEntry[i] = static_cast<unsigned char>(foundStateBits - m_state_bits.begin());
}
else
{
m_material->stateBitsEntry[i] = static_cast<unsigned char>(m_state_bits.size());
m_state_bits.push_back(stateBitsForTechnique);
}
}
else
{
m_material->stateBitsEntry[i] = std::numeric_limits<unsigned char>::max();
}
}
}
void SetTechniqueSetCameraRegion(const techset::TechsetDefinition* techsetDefinition) const
{
std::string tempName;
if (techsetDefinition->GetTechniqueByIndex(TECHNIQUE_LIT, tempName))
{
if (m_material->info.sortKey >= SORTKEY_TRANS_START)
m_material->cameraRegion = CAMERA_REGION_LIT_TRANS;
else
m_material->cameraRegion = CAMERA_REGION_LIT_OPAQUE;
}
else if (techsetDefinition->GetTechniqueByIndex(TECHNIQUE_EMISSIVE, tempName))
{
m_material->cameraRegion = CAMERA_REGION_EMISSIVE;
}
else
{
m_material->cameraRegion = CAMERA_REGION_NONE;
}
}
GfxStateBits GetStateBitsForTechnique(const std::string& techniqueName)
{
// TODO: Use technique statemap to evaluate actual statebits
return m_base_state_bits;
}
void AddMapTexture(const std::string& typeName, const TileMode_e tileMode, GdtFilter_e filterMode, const TextureSemantic semantic, const std::string& textureName)
{
MaterialTextureDef textureDef{};
textureDef.nameHash = Common::R_HashString(typeName.c_str());
textureDef.nameStart = typeName[0];
textureDef.nameEnd = typeName[typeName.size() - 1];
textureDef.samplerState = 0;
textureDef.semantic = static_cast<unsigned char>(semantic);
switch (tileMode)
{
case TileMode_e::TILE_BOTH:
textureDef.samplerState |= SAMPLER_CLAMP_U | SAMPLER_CLAMP_V | SAMPLER_CLAMP_W;
break;
case TileMode_e::TILE_HORIZONTAL:
textureDef.samplerState |= SAMPLER_CLAMP_V;
break;
case TileMode_e::TILE_VERTICAL:
textureDef.samplerState |= SAMPLER_CLAMP_U;
break;
case TileMode_e::UNKNOWN:
case TileMode_e::NO_TILE:
break;
default:
assert(false);
break;
}
switch (filterMode)
{
case GdtFilter_e::MIP_2X_BILINEAR:
textureDef.samplerState |= SAMPLER_FILTER_ANISO2X | SAMPLER_MIPMAP_NEAREST;
break;
case GdtFilter_e::MIP_2X_TRILINEAR:
textureDef.samplerState |= SAMPLER_FILTER_ANISO2X | SAMPLER_MIPMAP_LINEAR;
break;
case GdtFilter_e::MIP_4X_BILINEAR:
textureDef.samplerState |= SAMPLER_FILTER_ANISO4X | SAMPLER_MIPMAP_NEAREST;
break;
case GdtFilter_e::MIP_4X_TRILINEAR:
textureDef.samplerState |= SAMPLER_FILTER_ANISO4X | SAMPLER_MIPMAP_LINEAR;
break;
case GdtFilter_e::NOMIP_NEAREST:
textureDef.samplerState |= SAMPLER_FILTER_NEAREST | SAMPLER_MIPMAP_DISABLED;
break;
case GdtFilter_e::NOMIP_BILINEAR:
textureDef.samplerState |= SAMPLER_FILTER_LINEAR | SAMPLER_MIPMAP_DISABLED;
break;
default:
assert(false);
break;
}
auto* image = reinterpret_cast<XAssetInfo<GfxImage>*>(m_manager->LoadDependency(ASSET_TYPE_IMAGE, textureName));
if (image == nullptr)
{
std::ostringstream ss;
ss << "Could not load image: \"" << textureName << "\"";
throw GdtReadingException(ss.str());
}
m_dependencies.push_back(image);
textureDef.u.image = image->Asset();
m_textures.push_back(textureDef);
}
void AddConstant(const std::string& constantName, Vector4f literalData)
{
MaterialConstantDef constantDef{};
constantDef.literal[0] = literalData(0);
constantDef.literal[1] = literalData(1);
constantDef.literal[2] = literalData(2);
constantDef.literal[3] = literalData(3);
strncpy(constantDef.name, constantName.c_str(), std::extent_v<decltype(MaterialConstantDef::name)>);
constantDef.nameHash = Common::R_HashString(constantName.c_str());
m_constants.push_back(constantDef);
}
void SetSort(const unsigned char sort) const
{
m_material->info.sortKey = sort;
}
void SetTextureAtlas(const unsigned char rowCount, const unsigned char columnCount) const
{
m_material->info.textureAtlasRowCount = rowCount;
m_material->info.textureAtlasColumnCount = columnCount;
}
void SetAlphaTest(const AlphaTest_e alphaTest)
{
switch (alphaTest)
{
case AlphaTest_e::ALWAYS:
m_base_state_bits.loadBits[0] |= GFXS0_ATEST_DISABLE;
break;
case AlphaTest_e::GT0:
m_base_state_bits.loadBits[0] |= GFXS0_ATEST_GT_0;
break;
case AlphaTest_e::LT128:
m_base_state_bits.loadBits[0] |= GFXS0_ATEST_LT_128;
break;
case AlphaTest_e::GE128:
m_base_state_bits.loadBits[0] |= GFXS0_ATEST_GE_128;
break;
case AlphaTest_e::UNKNOWN:
default:
std::ostringstream ss;
ss << "Unknown alphatest value: \"" << static_cast<int>(alphaTest) << "\"";
throw GdtReadingException(ss.str());
}
}
void SetBlendFunc(BlendOp_e blendOp, CustomBlendFunc_e srcFunc, CustomBlendFunc_e destFunc)
{
if (blendOp == BlendOp_e::UNKNOWN || srcFunc == CustomBlendFunc_e::UNKNOWN || destFunc == CustomBlendFunc_e::UNKNOWN)
{
std::ostringstream ss;
ss << "Unknown SeparateAlphaBlendFunc values: \"\"";
throw GdtReadingException(ss.str());
}
m_base_state_bits.loadBits[0] &= ~GFXS0_BLENDOP_RGB_MASK;
m_base_state_bits.loadBits[0] |= ((static_cast<unsigned>(blendOp) - 1) << GFXS0_BLENDOP_RGB_SHIFT) & GFXS0_BLENDOP_RGB_MASK;
m_base_state_bits.loadBits[0] &= ~GFXS0_SRCBLEND_RGB_MASK;
m_base_state_bits.loadBits[0] |= ((static_cast<unsigned>(srcFunc) - 1) << GFXS0_SRCBLEND_RGB_SHIFT) & GFXS0_SRCBLEND_RGB_MASK;
m_base_state_bits.loadBits[0] &= ~GFXS0_DSTBLEND_RGB_MASK;
m_base_state_bits.loadBits[0] |= ((static_cast<unsigned>(destFunc) - 1) << GFXS0_DSTBLEND_RGB_SHIFT) & GFXS0_DSTBLEND_RGB_MASK;
}
void SetSeparateAlphaBlendFunc(BlendOp_e blendOp, CustomBlendFunc_e srcFunc, CustomBlendFunc_e destFunc)
{
if (blendOp == BlendOp_e::UNKNOWN || srcFunc == CustomBlendFunc_e::UNKNOWN || destFunc == CustomBlendFunc_e::UNKNOWN)
{
std::ostringstream ss;
ss << "Unknown SeparateAlphaBlendFunc values: \"\"";
throw GdtReadingException(ss.str());
}
m_base_state_bits.loadBits[0] &= ~GFXS0_BLENDOP_ALPHA_MASK;
m_base_state_bits.loadBits[0] |= ((static_cast<unsigned>(blendOp) - 1) << GFXS0_BLENDOP_ALPHA_SHIFT) & GFXS0_BLENDOP_ALPHA_MASK;
m_base_state_bits.loadBits[0] &= ~GFXS0_SRCBLEND_ALPHA_MASK;
m_base_state_bits.loadBits[0] |= ((static_cast<unsigned>(srcFunc) - 1) << GFXS0_SRCBLEND_ALPHA_SHIFT) & GFXS0_SRCBLEND_ALPHA_MASK;
m_base_state_bits.loadBits[0] &= ~GFXS0_DSTBLEND_ALPHA_MASK;
m_base_state_bits.loadBits[0] |= ((static_cast<unsigned>(destFunc) - 1) << GFXS0_DSTBLEND_ALPHA_SHIFT) & GFXS0_DSTBLEND_ALPHA_MASK;
}
void SetColorWrite(const StateBitsEnabledStatus_e colorWriteRed, const StateBitsEnabledStatus_e colorWriteGreen, const StateBitsEnabledStatus_e colorWriteBlue,
const StateBitsEnabledStatus_e colorWriteAlpha)
{
if (colorWriteRed == StateBitsEnabledStatus_e::UNKNOWN || colorWriteGreen == StateBitsEnabledStatus_e::UNKNOWN
|| colorWriteBlue == StateBitsEnabledStatus_e::UNKNOWN || colorWriteAlpha == StateBitsEnabledStatus_e::UNKNOWN)
{
std::ostringstream ss;
ss << "Unknown ColorWrite values: \"\"";
throw GdtReadingException(ss.str());
}
if (colorWriteRed != colorWriteGreen || colorWriteRed != colorWriteBlue)
{
std::ostringstream ss;
ss << "Invalid ColorWrite values: values for rgb must match";
throw GdtReadingException(ss.str());
}
m_base_state_bits.loadBits[0] &= ~GFXS0_COLORWRITE_MASK;
if (colorWriteRed == StateBitsEnabledStatus_e::ENABLED)
m_base_state_bits.loadBits[0] |= GFXS0_COLORWRITE_RGB;
if (colorWriteAlpha == StateBitsEnabledStatus_e::ENABLED)
m_base_state_bits.loadBits[0] |= GFXS0_COLORWRITE_ALPHA;
}
void SetCullFace(const CullFace_e cullFace)
{
if (cullFace == CullFace_e::UNKNOWN)
{
std::ostringstream ss;
ss << "Unknown cullFace values: \"\"";
throw GdtReadingException(ss.str());
}
m_base_state_bits.loadBits[0] &= ~GFXS0_CULL_MASK;
if (cullFace == CullFace_e::FRONT)
{
m_base_state_bits.loadBits[0] |= GFXS0_CULL_FRONT;
}
else if (cullFace == CullFace_e::BACK)
{
m_base_state_bits.loadBits[0] |= GFXS0_CULL_BACK;
}
else
{
assert(cullFace == CullFace_e::NONE);
m_base_state_bits.loadBits[0] |= GFXS0_CULL_NONE;
}
}
void SetDepthTest(const DepthTest_e depthTest)
{
m_base_state_bits.loadBits[1] &= GFXS1_DEPTHTEST_MASK;
switch (depthTest)
{
case DepthTest_e::LESS_EQUAL:
m_base_state_bits.loadBits[1] |= GFXS1_DEPTHTEST_LESSEQUAL;
break;
case DepthTest_e::LESS:
m_base_state_bits.loadBits[1] |= GFXS1_DEPTHTEST_LESS;
break;
case DepthTest_e::EQUAL:
m_base_state_bits.loadBits[1] |= GFXS1_DEPTHTEST_EQUAL;
break;
case DepthTest_e::ALWAYS:
m_base_state_bits.loadBits[1] |= GFXS1_DEPTHTEST_ALWAYS;
break;
case DepthTest_e::DISABLE:
m_base_state_bits.loadBits[1] |= GFXS1_DEPTHTEST_DISABLE;
break;
case DepthTest_e::UNKNOWN:
default:
std::ostringstream ss;
ss << "Unknown depthTest values: \"\"";
throw GdtReadingException(ss.str());
}
}
void SetDepthWrite(const bool depthWrite)
{
m_base_state_bits.loadBits[1] &= ~GFXS1_DEPTHWRITE;
if (depthWrite)
m_base_state_bits.loadBits[1] |= GFXS1_DEPTHWRITE;
}
void SetGammaWrite(const bool gammaWrite)
{
m_base_state_bits.loadBits[0] &= ~GFXS0_GAMMAWRITE;
if (gammaWrite)
m_base_state_bits.loadBits[0] |= GFXS0_GAMMAWRITE;
}
void SetPolygonOffset(const PolygonOffset_e polygonOffset)
{
if (polygonOffset == PolygonOffset_e::UNKNOWN)
{
std::ostringstream ss;
ss << "Unknown polygonOffset values: \"\"";
throw GdtReadingException(ss.str());
}
m_base_state_bits.loadBits[1] &= ~GFXS1_POLYGON_OFFSET_MASK;
m_base_state_bits.loadBits[1] |= ((static_cast<unsigned>(polygonOffset) - 1) << GFXS1_POLYGON_OFFSET_SHIFT) & GFXS1_POLYGON_OFFSET_MASK;
}
static void GetStencilMasksForIndex(const StencilIndex stencil, unsigned& enabledMask, unsigned& funcShift, unsigned& funcMask, unsigned& opFailShift, unsigned& opFailMask,
unsigned& opZFailShift, unsigned& opZFailMask, unsigned& opPassShift, unsigned& opPassMask)
{
if (stencil == StencilIndex::FRONT)
{
enabledMask = GFXS1_STENCIL_FRONT_ENABLE;
funcShift = GFXS1_STENCIL_FRONT_FUNC_SHIFT;
funcMask = GFXS1_STENCIL_FRONT_FUNC_MASK;
opFailShift = GFXS1_STENCIL_FRONT_FAIL_SHIFT;
opFailMask = GFXS1_STENCIL_FRONT_FAIL_MASK;
opZFailShift = GFXS1_STENCIL_FRONT_ZFAIL_SHIFT;
opZFailMask = GFXS1_STENCIL_FRONT_ZFAIL_MASK;
opPassShift = GFXS1_STENCIL_FRONT_PASS_SHIFT;
opPassMask = GFXS1_STENCIL_FRONT_PASS_MASK;
}
else
{
assert(stencil == StencilIndex::BACK);
enabledMask = GFXS1_STENCIL_BACK_ENABLE;
funcShift = GFXS1_STENCIL_BACK_FUNC_SHIFT;
funcMask = GFXS1_STENCIL_BACK_FUNC_MASK;
opFailShift = GFXS1_STENCIL_BACK_FAIL_SHIFT;
opFailMask = GFXS1_STENCIL_BACK_FAIL_MASK;
opZFailShift = GFXS1_STENCIL_BACK_ZFAIL_SHIFT;
opZFailMask = GFXS1_STENCIL_BACK_ZFAIL_MASK;
opPassShift = GFXS1_STENCIL_BACK_PASS_SHIFT;
opPassMask = GFXS1_STENCIL_BACK_PASS_MASK;
}
}
void DisableStencil(const StencilIndex stencil)
{
unsigned enabledMask, funcShift, funcMask, opFailShift, opFailMask, opZFailShift, opZFailMask, opPassShift, opPassMask;
GetStencilMasksForIndex(stencil, enabledMask, funcShift, funcMask, opFailShift, opFailMask, opZFailShift, opZFailMask, opPassShift, opPassMask);
m_base_state_bits.loadBits[1] &= ~(enabledMask | funcMask | opFailMask | opZFailMask | opPassMask);
}
void EnableStencil(const StencilIndex stencil, StencilFunc_e stencilFunc, StencilOp_e stencilOpFail, StencilOp_e stencilOpZFail, StencilOp_e stencilOpPass)
{
unsigned enabledMask, funcShift, funcMask, opFailShift, opFailMask, opZFailShift, opZFailMask, opPassShift, opPassMask;
GetStencilMasksForIndex(stencil, enabledMask, funcShift, funcMask, opFailShift, opFailMask, opZFailShift, opZFailMask, opPassShift, opPassMask);
m_base_state_bits.loadBits[1] |= enabledMask;
m_base_state_bits.loadBits[1] &= ~funcMask;
m_base_state_bits.loadBits[1] |= ((static_cast<unsigned>(stencilFunc) - 1) << funcShift) & funcMask;
m_base_state_bits.loadBits[1] &= ~opFailMask;
m_base_state_bits.loadBits[1] |= ((static_cast<unsigned>(stencilOpFail) - 1) << opFailShift) & opFailMask;
m_base_state_bits.loadBits[1] &= ~opZFailMask;
m_base_state_bits.loadBits[1] |= ((static_cast<unsigned>(stencilOpZFail) - 1) << opZFailShift) & opZFailMask;
m_base_state_bits.loadBits[1] &= ~opPassMask;
m_base_state_bits.loadBits[1] |= ((static_cast<unsigned>(stencilOpPass) - 1) << opPassShift) & opPassMask;
}
void FinalizeMaterial() const
{
if (!m_textures.empty())
{
m_material->textureTable = static_cast<MaterialTextureDef*>(m_memory->Alloc(sizeof(MaterialTextureDef) * m_textures.size()));
m_material->textureCount = static_cast<unsigned char>(m_textures.size());
memcpy(m_material->textureTable, m_textures.data(), sizeof(MaterialTextureDef) * m_textures.size());
}
else
{
m_material->textureTable = nullptr;
m_material->textureCount = 0u;
}
if (!m_constants.empty())
{
m_material->constantTable = static_cast<MaterialConstantDef*>(m_memory->Alloc(sizeof(MaterialConstantDef) * m_constants.size()));
m_material->constantCount = static_cast<unsigned char>(m_constants.size());
memcpy(m_material->constantTable, m_constants.data(), sizeof(MaterialConstantDef) * m_constants.size());
}
else
{
m_material->constantTable = nullptr;
m_material->constantCount = 0u;
}
if (!m_state_bits.empty())
{
m_material->stateBitsTable = static_cast<GfxStateBits*>(m_memory->Alloc(sizeof(GfxStateBits) * m_state_bits.size()));
m_material->stateBitsCount = static_cast<unsigned char>(m_state_bits.size());
memcpy(m_material->stateBitsTable, m_state_bits.data(), sizeof(GfxStateBits) * m_state_bits.size());
}
else
{
m_material->stateBitsTable = nullptr;
m_material->stateBitsCount = 0u;
}
}
static size_t GetIndexForString(const std::string& propertyName, const std::string& value, const char** validValuesArray, const size_t validValuesArraySize)
{
for (auto i = 0u; i < validValuesArraySize; i++)
{
if (validValuesArray[i] == value)
return i;
}
std::ostringstream ss;
ss << "Unknown " << propertyName << " value: \"" << value << "\"";
throw GdtReadingException(ss.str());
}
template <typename T>
T ReadEnumProperty(const std::string& propertyName, const char** validValuesArray, const size_t validValuesArraySize) const
{
return static_cast<T>(GetIndexForString(propertyName, ReadStringProperty(propertyName), validValuesArray, validValuesArraySize));
}
MemoryManager* m_memory;
IAssetLoadingManager* m_manager;
std::vector<XAssetInfoGeneric*> m_dependencies;
Material* m_material;
GfxStateBits m_base_state_bits;
std::vector<GfxStateBits> m_state_bits;
std::vector<MaterialTextureDef> m_textures;
std::vector<MaterialConstantDef> m_constants;
};
}
void* AssetLoaderMaterial::CreateEmptyAsset(const std::string& assetName, MemoryManager* memory)
{
auto* material = memory->Create<Material>();
memset(material, 0, sizeof(Material));
material->info.name = memory->Dup(assetName.c_str());
return material;
}
bool AssetLoaderMaterial::CanLoadFromGdt() const
{
return true;
}
bool AssetLoaderMaterial::LoadFromGdt(const std::string& assetName, IGdtQueryable* gdtQueryable, MemoryManager* memory, IAssetLoadingManager* manager, Zone* zone) const
{
const auto* entry = gdtQueryable->GetGdtEntryByGdfAndName(ObjConstants::GDF_FILENAME_MATERIAL, assetName);
if (!entry)
return false;
MaterialGdtLoader loader(*entry, memory, manager);
try
{
if (loader.Load())
manager->AddAsset(ASSET_TYPE_MATERIAL, assetName, loader.GetMaterial(), loader.GetDependencies(), std::vector<scr_string_t>());
}
catch (const SkipMaterialException&)
{
return false;
}
catch (const GdtReadingException& e)
{
std::cerr << "Error while trying to load material from gdt: " << e.what() << " @ GdtEntry \"" << entry->m_name << "\"\n";
return false;
}
return true;
}