diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..21309088 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,3 @@ +# All .sh files should be lf line endings +*.sh text eol=cl + diff --git a/.github/workflows/check-formatting.yaml b/.github/workflows/check-formatting.yaml index fcc6f20e..30431ea4 100644 --- a/.github/workflows/check-formatting.yaml +++ b/.github/workflows/check-formatting.yaml @@ -15,7 +15,7 @@ jobs: - name: Install LLVM and Clang uses: KyleMayes/install-llvm-action@v2 with: - version: "17.0" + version: "20.1" - name: Test formatting for all files working-directory: ${{ github.workspace }} diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 047661c3..1a4412a0 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -46,6 +46,7 @@ jobs: ./ObjCommonTests ./ObjCompilingTests ./ObjLoadingTests + ./ObjWritingTests ./ParserTests ./ZoneCodeGeneratorLibTests ./ZoneCommonTests @@ -89,6 +90,8 @@ jobs: $combinedExitCode = [System.Math]::max($combinedExitCode, $LASTEXITCODE) ./ObjLoadingTests $combinedExitCode = [System.Math]::max($combinedExitCode, $LASTEXITCODE) + ./ObjWritingTests + $combinedExitCode = [System.Math]::max($combinedExitCode, $LASTEXITCODE) ./ParserTests $combinedExitCode = [System.Math]::max($combinedExitCode, $LASTEXITCODE) ./ZoneCodeGeneratorLibTests diff --git a/premake5.lua b/premake5.lua index 53f7714d..4a0f4d8a 100644 --- a/premake5.lua +++ b/premake5.lua @@ -84,6 +84,9 @@ workspace "OpenAssetTools" filter "options:debug-techset" defines { "TECHSET_DEBUG" } filter {} + filter "options:experimental-material-compilation" + defines { "EXPERIMENTAL_MATERIAL_COMPILATION" } + filter {} -- ======================== -- ThirdParty @@ -181,6 +184,7 @@ include "test/ObjCommonTestUtils.lua" include "test/ObjCommonTests.lua" include "test/ObjCompilingTests.lua" include "test/ObjLoadingTests.lua" +include "test/ObjWritingTests.lua" include "test/ParserTestUtils.lua" include "test/ParserTests.lua" include "test/ZoneCodeGeneratorLibTests.lua" @@ -193,6 +197,7 @@ group "Tests" ObjCommonTests:project() ObjCompilingTests:project() ObjLoadingTests:project() + ObjWritingTests:project() ParserTestUtils:project() ParserTests:project() ZoneCodeGeneratorLibTests:project() diff --git a/src/Common/Game/IW4/IW4_Assets.h b/src/Common/Game/IW4/IW4_Assets.h index 9636bdf2..09431961 100644 --- a/src/Common/Game/IW4/IW4_Assets.h +++ b/src/Common/Game/IW4/IW4_Assets.h @@ -674,7 +674,7 @@ namespace IW4 water_t* water; }; - enum MaterialTextureFilter + enum TextureFilter { TEXTURE_FILTER_DISABLED = 0x0, TEXTURE_FILTER_NEAREST = 0x1, @@ -685,6 +685,15 @@ namespace IW4 TEXTURE_FILTER_COUNT }; + enum SamplerStateBitsMipMap_e + { + SAMPLER_MIPMAP_ENUM_DISABLED, + SAMPLER_MIPMAP_ENUM_NEAREST, + SAMPLER_MIPMAP_ENUM_LINEAR, + + SAMPLER_MIPMAP_ENUM_COUNT + }; + enum SamplerStateBits_e { SAMPLER_FILTER_SHIFT = 0x0, @@ -710,13 +719,26 @@ namespace IW4 SAMPLER_CLAMP_MASK = 0xE0, }; + struct MaterialTextureDefSamplerState + { + unsigned char filter : 3; + unsigned char mipMap : 2; + unsigned char clampU : 1; + unsigned char clampV : 1; + unsigned char clampW : 1; + }; + +#ifndef __zonecodegenerator + static_assert(sizeof(MaterialTextureDefSamplerState) == 1u); +#endif + struct MaterialTextureDef { unsigned int nameHash; char nameStart; char nameEnd; - unsigned char samplerState; // SamplerStateBits_e - unsigned char semantic; // TextureSemantic + MaterialTextureDefSamplerState samplerState; // SamplerStateBits_e + unsigned char semantic; // TextureSemantic MaterialTextureDefInfo u; }; @@ -724,7 +746,7 @@ namespace IW4 { unsigned int nameHash; char name[12]; - float literal[4]; + vec4_t literal; }; enum GfxBlend : unsigned int @@ -754,19 +776,36 @@ namespace IW4 GFXS_BLENDOP_MASK = 0x7, }; - enum GfxStencilFunc : unsigned int + enum GfxAlphaTest_e { - GFXS_STENCILFUNC_NEVER = 0x0, - GFXS_STENCILFUNC_LESS = 0x1, - GFXS_STENCILFUNC_EQUAL = 0x2, - GFXS_STENCILFUNC_LESSEQUAL = 0x3, - GFXS_STENCILFUNC_GREATER = 0x4, - GFXS_STENCILFUNC_NOTEQUAL = 0x5, - GFXS_STENCILFUNC_GREATEREQUAL = 0x6, - GFXS_STENCILFUNC_ALWAYS = 0x7, + GFXS_ALPHA_TEST_GT_0 = 1, + GFXS_ALPHA_TEST_LT_128 = 2, + GFXS_ALPHA_TEST_GE_128 = 3, - GFXS_STENCILFUNC_COUNT, - GFXS_STENCILFUNC_MASK = 0x7 + GFXS_ALPHA_TEST_COUNT + }; + + enum GfxCullFace_e + { + GFXS_CULL_NONE = 1, + GFXS_CULL_BACK = 2, + GFXS_CULL_FRONT = 3, + }; + + enum GfxDepthTest_e + { + GFXS_DEPTHTEST_ALWAYS = 0, + GFXS_DEPTHTEST_LESS = 1, + GFXS_DEPTHTEST_EQUAL = 2, + GFXS_DEPTHTEST_LESSEQUAL = 3 + }; + + enum GfxPolygonOffset_e + { + GFXS_POLYGON_OFFSET_0 = 0, + GFXS_POLYGON_OFFSET_1 = 1, + GFXS_POLYGON_OFFSET_2 = 2, + GFXS_POLYGON_OFFSET_SHADOWMAP = 3 }; enum GfxStencilOp : unsigned int @@ -784,6 +823,21 @@ namespace IW4 GFXS_STENCILOP_MASK = 0x7 }; + enum GfxStencilFunc : unsigned int + { + GFXS_STENCILFUNC_NEVER = 0x0, + GFXS_STENCILFUNC_LESS = 0x1, + GFXS_STENCILFUNC_EQUAL = 0x2, + GFXS_STENCILFUNC_LESSEQUAL = 0x3, + GFXS_STENCILFUNC_GREATER = 0x4, + GFXS_STENCILFUNC_NOTEQUAL = 0x5, + GFXS_STENCILFUNC_GREATEREQUAL = 0x6, + GFXS_STENCILFUNC_ALWAYS = 0x7, + + GFXS_STENCILFUNC_COUNT, + GFXS_STENCILFUNC_MASK = 0x7 + }; + enum GfxStateBitsEnum : unsigned int { GFXS0_SRCBLEND_RGB_SHIFT = 0x0, @@ -876,9 +930,55 @@ namespace IW4 GFXS1_STENCILOP_FRONTBACK_MASK = 0x1FF1FF00, }; + struct GfxStateBitsLoadBitsStructured + { + // Byte 0 + unsigned int srcBlendRgb : 4; // 0-3 + unsigned int dstBlendRgb : 4; // 4-7 + unsigned int blendOpRgb : 3; // 8-10 + unsigned int alphaTestDisabled : 1; // 11 + unsigned int alphaTest : 2; // 12-13 + unsigned int cullFace : 2; // 14-15 + unsigned int srcBlendAlpha : 4; // 16-19 + unsigned int dstBlendAlpha : 4; // 20-23 + unsigned int blendOpAlpha : 3; // 24-26 + unsigned int colorWriteRgb : 1; // 27 + unsigned int colorWriteAlpha : 1; // 28 + unsigned int unused0 : 1; // 29 + unsigned int gammaWrite : 1; // 30 + unsigned int polymodeLine : 1; // 31 + + // Byte 1 + unsigned int depthWrite : 1; // 0 + unsigned int depthTestDisabled : 1; // 1 + unsigned int depthTest : 2; // 2-3 + unsigned int polygonOffset : 2; // 4-5 + unsigned int stencilFrontEnabled : 1; // 6 + unsigned int stencilBackEnabled : 1; // 7 + unsigned int stencilFrontPass : 3; // 8-10 + unsigned int stencilFrontFail : 3; // 11-13 + unsigned int stencilFrontZFail : 3; // 14-16 + unsigned int stencilFrontFunc : 3; // 17-19 + unsigned int stencilBackPass : 3; // 20-22 + unsigned int stencilBackFail : 3; // 23-25 + unsigned int stencilBackZFail : 3; // 26-28 + unsigned int stencilBackFunc : 3; // 29-31 + }; + + union GfxStateBitsLoadBits + { + unsigned int raw[2]; + GfxStateBitsLoadBitsStructured structured; + }; + +#ifndef __zonecodegenerator + static_assert(sizeof(GfxStateBitsLoadBits) == 8); + static_assert(sizeof(GfxStateBitsLoadBitsStructured) == 8); +#endif + struct GfxStateBits { - unsigned int loadBits[2]; + GfxStateBitsLoadBits loadBits; }; struct infoParm_t @@ -1028,7 +1128,7 @@ namespace IW4 struct Material { MaterialInfo info; - unsigned char stateBitsEntry[48]; + char stateBitsEntry[48]; unsigned char textureCount; unsigned char constantCount; unsigned char stateBitsCount; diff --git a/src/Common/Game/IW5/IW5_Assets.h b/src/Common/Game/IW5/IW5_Assets.h index e4753a01..cab9ab4f 100644 --- a/src/Common/Game/IW5/IW5_Assets.h +++ b/src/Common/Game/IW5/IW5_Assets.h @@ -691,11 +691,6 @@ namespace IW5 MTL_GAMEFLAG_20 = 0x20, MTL_GAMEFLAG_40 = 0x40, MTL_GAMEFLAG_80 = 0x80, - MTL_GAMEFLAG_100 = 0x100, - MTL_GAMEFLAG_200 = 0x200, - MTL_GAMEFLAG_400 = 0x400, - MTL_GAMEFLAG_800 = 0x800, - MTL_GAMEFLAG_1000 = 0x1000, }; struct MaterialInfo @@ -991,7 +986,7 @@ namespace IW5 unsigned char constantCount; unsigned char stateBitsCount; unsigned char stateFlags; - unsigned char cameraRegion; + unsigned char cameraRegion; // GfxCameraRegionType MaterialTechniqueSet* techniqueSet; MaterialTextureDef* textureTable; MaterialConstantDef* constantTable; diff --git a/src/Common/Game/T6/T6_Assets.h b/src/Common/Game/T6/T6_Assets.h index 4eec23ae..62f3f300 100644 --- a/src/Common/Game/T6/T6_Assets.h +++ b/src/Common/Game/T6/T6_Assets.h @@ -2929,9 +2929,9 @@ namespace T6 enum GfxCullFace_e { - GFXS0_CULL_NONE = 1, - GFXS0_CULL_BACK = 2, - GFXS0_CULL_FRONT = 3, + GFXS_CULL_NONE = 1, + GFXS_CULL_BACK = 2, + GFXS_CULL_FRONT = 3, }; enum GfxDepthTest_e diff --git a/src/ObjCommon.lua b/src/ObjCommon.lua index bcbed0e1..d1e28826 100644 --- a/src/ObjCommon.lua +++ b/src/ObjCommon.lua @@ -7,7 +7,8 @@ function ObjCommon:include(includes) minizip:include(includes) Parser:include(includes) includedirs { - path.join(ProjectFolder(), "ObjCommon") + path.join(ProjectFolder(), "ObjCommon"), + "%{wks.location}/src/ObjCommon" } end end @@ -21,7 +22,7 @@ function ObjCommon:link(links) end function ObjCommon:use() - + dependson(self:name()) end function ObjCommon:name() @@ -48,6 +49,8 @@ function ObjCommon:project() path.join(folder, "ObjCommon") } } + + useSourceTemplating("ObjCommon") self:include(includes) Utils:include(includes) diff --git a/src/ObjCommon/Game/IW4/TechsetConstantsIW4.h b/src/ObjCommon/Game/IW4/TechsetConstantsIW4.h index 7453aded..9b0a08d8 100644 --- a/src/ObjCommon/Game/IW4/TechsetConstantsIW4.h +++ b/src/ObjCommon/Game/IW4/TechsetConstantsIW4.h @@ -818,5 +818,5 @@ namespace IW4 }}, }); - inline state_map::StateMapLayout stateMapLayout(std::extent_v, stateMapEntryLayout, stateMapVarLayout); + inline state_map::StateMapLayout stateMapLayout(std::extent_v, stateMapEntryLayout, stateMapVarLayout); } // namespace IW4 diff --git a/src/ObjCommon/Game/IW5/Material/JsonMaterial.h b/src/ObjCommon/Game/IW5/Material/JsonMaterial.h deleted file mode 100644 index b6dd0d25..00000000 --- a/src/ObjCommon/Game/IW5/Material/JsonMaterial.h +++ /dev/null @@ -1,391 +0,0 @@ -#pragma once - -#include "Game/IW5/IW5.h" - -#include "Json/JsonExtension.h" -#include -#include -#include -#include -#include - -namespace IW5 -{ - NLOHMANN_JSON_SERIALIZE_ENUM(GfxStencilOp, - { - {GFXS_STENCILOP_KEEP, "keep" }, - {GFXS_STENCILOP_ZERO, "zero" }, - {GFXS_STENCILOP_REPLACE, "replace"}, - {GFXS_STENCILOP_INCRSAT, "incrsat"}, - {GFXS_STENCILOP_DECRSAT, "decrsat"}, - {GFXS_STENCILOP_INVERT, "invert" }, - {GFXS_STENCILOP_INCR, "incr" }, - {GFXS_STENCILOP_DECR, "decr" }, - }); - - NLOHMANN_JSON_SERIALIZE_ENUM(GfxStencilFunc, - { - {GFXS_STENCILFUNC_NEVER, "never" }, - {GFXS_STENCILFUNC_LESS, "less" }, - {GFXS_STENCILFUNC_EQUAL, "equal" }, - {GFXS_STENCILFUNC_LESSEQUAL, "lessequal" }, - {GFXS_STENCILFUNC_GREATER, "greater" }, - {GFXS_STENCILFUNC_NOTEQUAL, "notequal" }, - {GFXS_STENCILFUNC_GREATEREQUAL, "greaterequal"}, - {GFXS_STENCILFUNC_ALWAYS, "always" }, - }); - - class JsonStencil - { - public: - GfxStencilOp pass; - GfxStencilOp fail; - GfxStencilOp zfail; - GfxStencilFunc func; - }; - - NLOHMANN_DEFINE_TYPE_EXTENSION(JsonStencil, pass, fail, zfail, func); - - enum class JsonAlphaTest - { - INVALID, - DISABLED, - GT0, - LT128, - GE128 - }; - - NLOHMANN_JSON_SERIALIZE_ENUM(JsonAlphaTest, - { - {JsonAlphaTest::INVALID, nullptr }, - {JsonAlphaTest::DISABLED, "disabled"}, - {JsonAlphaTest::GT0, "gt0" }, - {JsonAlphaTest::LT128, "lt128" }, - {JsonAlphaTest::GE128, "ge128" } - }); - - enum class JsonCullFace - { - INVALID, - NONE, - BACK, - FRONT - }; - - NLOHMANN_JSON_SERIALIZE_ENUM( - JsonCullFace, - { - {JsonCullFace::INVALID, nullptr}, - {JsonCullFace::NONE, "none" }, - {JsonCullFace::BACK, "back" }, - {JsonCullFace::FRONT, "front"} - }); - - enum class JsonDepthTest - { - INVALID, - DISABLED, - ALWAYS, - LESS, - EQUAL, - LESS_EQUAL - }; - - NLOHMANN_JSON_SERIALIZE_ENUM(JsonDepthTest, - { - {JsonDepthTest::INVALID, nullptr }, - {JsonDepthTest::DISABLED, "disabled" }, - {JsonDepthTest::ALWAYS, "always" }, - {JsonDepthTest::LESS, "less" }, - {JsonDepthTest::EQUAL, "equal" }, - {JsonDepthTest::LESS_EQUAL, "less_equal"} - }); - - NLOHMANN_JSON_SERIALIZE_ENUM(GfxBlend, - { - {GFXS_BLEND_DISABLED, "disabled" }, - {GFXS_BLEND_ZERO, "zero" }, - {GFXS_BLEND_ONE, "one" }, - {GFXS_BLEND_SRCCOLOR, "srccolor" }, - {GFXS_BLEND_INVSRCCOLOR, "invsrccolor" }, - {GFXS_BLEND_SRCALPHA, "srcalpha" }, - {GFXS_BLEND_INVSRCALPHA, "invsrcalpha" }, - {GFXS_BLEND_DESTALPHA, "destalpha" }, - {GFXS_BLEND_INVDESTALPHA, "invdestalpha"}, - {GFXS_BLEND_DESTCOLOR, "destcolor" }, - {GFXS_BLEND_INVDESTCOLOR, "invdestcolor"}, - }); - - NLOHMANN_JSON_SERIALIZE_ENUM(GfxBlendOp, - { - {GFXS_BLENDOP_DISABLED, "disabled" }, - {GFXS_BLENDOP_ADD, "add" }, - {GFXS_BLENDOP_SUBTRACT, "subtract" }, - {GFXS_BLENDOP_REVSUBTRACT, "revsubtract"}, - {GFXS_BLENDOP_MIN, "min" }, - {GFXS_BLENDOP_MAX, "max" }, - }); - - NLOHMANN_JSON_SERIALIZE_ENUM(GfxPolygonOffset_e, - { - {GFXS_POLYGON_OFFSET_0, "offset0" }, - {GFXS_POLYGON_OFFSET_1, "offset1" }, - {GFXS_POLYGON_OFFSET_2, "offset2" }, - {GFXS_POLYGON_OFFSET_SHADOWMAP, "offsetShadowmap"}, - }); - - class JsonStateBitsTableEntry - { - public: - GfxBlend srcBlendRgb; - GfxBlend dstBlendRgb; - GfxBlendOp blendOpRgb; - JsonAlphaTest alphaTest; - JsonCullFace cullFace; - GfxBlend srcBlendAlpha; - GfxBlend dstBlendAlpha; - GfxBlendOp blendOpAlpha; - bool colorWriteRgb; - bool colorWriteAlpha; - bool gammaWrite; - bool polymodeLine; - bool depthWrite; - JsonDepthTest depthTest; - GfxPolygonOffset_e polygonOffset; - std::optional stencilFront; - std::optional stencilBack; - }; - - NLOHMANN_DEFINE_TYPE_EXTENSION(JsonStateBitsTableEntry, - srcBlendRgb, - dstBlendRgb, - blendOpRgb, - alphaTest, - cullFace, - srcBlendAlpha, - dstBlendAlpha, - blendOpAlpha, - colorWriteRgb, - colorWriteAlpha, - polymodeLine, - depthWrite, - depthWrite, - depthTest, - polygonOffset, - stencilFront, - stencilBack); - - class JsonConstant - { - public: - std::optional name; - std::optional nameFragment; - std::optional nameHash; - std::vector literal; - }; - - inline void to_json(nlohmann::json& out, const JsonConstant& in) - { - if (in.name.has_value()) - { - optional_to_json(out, "name", in.name); - } - else - { - optional_to_json(out, "nameFragment", in.nameFragment); - optional_to_json(out, "nameHash", in.nameHash); - } - - out["literal"] = in.literal; - } - - inline void from_json(const nlohmann::json& in, JsonConstant& out) - { - optional_from_json(in, "name", out.name); - optional_from_json(in, "nameFragment", out.nameFragment); - optional_from_json(in, "nameHash", out.nameHash); - in.at("literal").get_to(out.literal); - }; - - NLOHMANN_JSON_SERIALIZE_ENUM(TextureFilter, - { - {TEXTURE_FILTER_DISABLED, "disabled"}, - {TEXTURE_FILTER_NEAREST, "nearest" }, - {TEXTURE_FILTER_LINEAR, "linear" }, - {TEXTURE_FILTER_ANISO2X, "aniso2x" }, - {TEXTURE_FILTER_ANISO4X, "aniso4x" }, - }); - - NLOHMANN_JSON_SERIALIZE_ENUM(SamplerStateBitsMipMap_e, - { - {SAMPLER_MIPMAP_ENUM_DISABLED, "disabled"}, - {SAMPLER_MIPMAP_ENUM_NEAREST, "nearest" }, - {SAMPLER_MIPMAP_ENUM_LINEAR, "linear" }, - }); - - class JsonSamplerState - { - public: - TextureFilter filter; - SamplerStateBitsMipMap_e mipMap; - bool clampU; - bool clampV; - bool clampW; - }; - - NLOHMANN_DEFINE_TYPE_EXTENSION(JsonSamplerState, filter, mipMap, clampU, clampV, clampW); - - class JsonComplex - { - public: - float real; - float imag; - }; - - NLOHMANN_DEFINE_TYPE_EXTENSION(JsonComplex, real, imag); - - class JsonWater - { - public: - float floatTime; - int m; - int n; - std::string h0; - std::string wTerm; - float lx; - float lz; - float gravity; - float windvel; - std::array winddir; - float amplitude; - std::array codeConstant; - }; - - NLOHMANN_DEFINE_TYPE_EXTENSION(JsonWater, floatTime, m, n, h0, wTerm, lx, lz, gravity, windvel, winddir, amplitude, codeConstant); - - NLOHMANN_JSON_SERIALIZE_ENUM(TextureSemantic, - { - {TS_2D, "2D" }, - {TS_FUNCTION, "function" }, - {TS_COLOR_MAP, "colorMap" }, - {TS_DETAIL_MAP, "detailMap" }, - {TS_UNUSED_2, "unused2" }, - {TS_NORMAL_MAP, "normalMap" }, - {TS_UNUSED_3, "unused3" }, - {TS_UNUSED_4, "unused4" }, - {TS_SPECULAR_MAP, "specularMap" }, - {TS_UNUSED_5, "unused5" }, - {TS_UNUSED_6, "unused6" }, - {TS_WATER_MAP, "waterMap" }, - {TS_DISPLACEMENT_MAP, "displacementMap"}, - }); - - class JsonTexture - { - public: - std::optional name; - std::optional nameHash; - std::optional nameStart; - std::optional nameEnd; - TextureSemantic semantic; - JsonSamplerState samplerState; - std::string image; - std::optional water; - }; - - inline void to_json(nlohmann::json& out, const JsonTexture& in) - { - if (in.name.has_value()) - { - optional_to_json(out, "name", in.name); - } - else - { - optional_to_json(out, "nameHash", in.nameHash); - optional_to_json(out, "nameStart", in.nameStart); - optional_to_json(out, "nameEnd", in.nameEnd); - } - - out["semantic"] = in.semantic; - out["samplerState"] = in.samplerState; - out["image"] = in.image; - optional_to_json(out, "water", in.water); - } - - inline void from_json(const nlohmann::json& in, JsonTexture& out) - { - optional_from_json(in, "name", out.name); - optional_from_json(in, "nameHash", out.nameHash); - optional_from_json(in, "nameStart", out.nameStart); - optional_from_json(in, "nameEnd", out.nameEnd); - in.at("semantic").get_to(out.semantic); - in.at("samplerState").get_to(out.samplerState); - in.at("image").get_to(out.image); - optional_from_json(in, "water", out.water); - }; - - class JsonTextureAtlas - { - public: - uint8_t rows; - uint8_t columns; - }; - - NLOHMANN_DEFINE_TYPE_EXTENSION(JsonTextureAtlas, rows, columns); - - NLOHMANN_JSON_SERIALIZE_ENUM(MaterialGameFlags, - { - {MTL_GAMEFLAG_1, "1" }, - {MTL_GAMEFLAG_2, "2" }, - {MTL_GAMEFLAG_4, "4" }, - {MTL_GAMEFLAG_8, "8" }, - {MTL_GAMEFLAG_10, "10" }, - {MTL_GAMEFLAG_20, "20" }, - {MTL_GAMEFLAG_40, "40" }, - {MTL_GAMEFLAG_80, "80" }, - {MTL_GAMEFLAG_100, "100" }, - {MTL_GAMEFLAG_200, "200" }, - {MTL_GAMEFLAG_400, "400" }, - {MTL_GAMEFLAG_800, "800" }, - {MTL_GAMEFLAG_1000, "1000"}, - }); - - NLOHMANN_JSON_SERIALIZE_ENUM(GfxCameraRegionType, - { - {CAMERA_REGION_LIT_OPAQUE, "litOpaque" }, - {CAMERA_REGION_LIT_TRANS, "litTrans" }, - {CAMERA_REGION_EMISSIVE, "emissive" }, - {CAMERA_REGION_DEPTH_HACK, "depthHack" }, - {CAMERA_REGION_LIGHT_MAP_OPAQUE, "lightMapOpaque"}, - {CAMERA_REGION_NONE, "none" }, - }); - - class JsonMaterial - { - public: - std::vector gameFlags; - unsigned sortKey; - std::optional textureAtlas; - unsigned surfaceTypeBits; - std::vector stateBitsEntry; - unsigned stateFlags; - GfxCameraRegionType cameraRegion; - std::string techniqueSet; - std::vector textures; - std::vector constants; - std::vector stateBits; - }; - - NLOHMANN_DEFINE_TYPE_EXTENSION(JsonMaterial, - gameFlags, - sortKey, - textureAtlas, - surfaceTypeBits, - stateBitsEntry, - stateFlags, - cameraRegion, - techniqueSet, - textures, - constants, - stateBits); -} // namespace IW5 diff --git a/src/ObjCommon/Game/T6/Json/JsonMaterial.h b/src/ObjCommon/Game/T6/Json/JsonMaterial.h deleted file mode 100644 index e210676d..00000000 --- a/src/ObjCommon/Game/T6/Json/JsonMaterial.h +++ /dev/null @@ -1,397 +0,0 @@ -#pragma once - -#include "Game/T6/T6.h" - -#include "Json/JsonExtension.h" -#include -#include -#include -#include -#include - -namespace T6 -{ - NLOHMANN_JSON_SERIALIZE_ENUM(GfxStencilOp, - { - {GFXS_STENCILOP_KEEP, "keep" }, - {GFXS_STENCILOP_ZERO, "zero" }, - {GFXS_STENCILOP_REPLACE, "replace"}, - {GFXS_STENCILOP_INCRSAT, "incrsat"}, - {GFXS_STENCILOP_DECRSAT, "decrsat"}, - {GFXS_STENCILOP_INVERT, "invert" }, - {GFXS_STENCILOP_INCR, "incr" }, - {GFXS_STENCILOP_DECR, "decr" }, - }); - - NLOHMANN_JSON_SERIALIZE_ENUM(GfxStencilFunc, - { - {GFXS_STENCILFUNC_NEVER, "never" }, - {GFXS_STENCILFUNC_LESS, "less" }, - {GFXS_STENCILFUNC_EQUAL, "equal" }, - {GFXS_STENCILFUNC_LESSEQUAL, "lessequal" }, - {GFXS_STENCILFUNC_GREATER, "greater" }, - {GFXS_STENCILFUNC_NOTEQUAL, "notequal" }, - {GFXS_STENCILFUNC_GREATEREQUAL, "greaterequal"}, - {GFXS_STENCILFUNC_ALWAYS, "always" }, - }); - - class JsonStencil - { - public: - GfxStencilOp pass; - GfxStencilOp fail; - GfxStencilOp zfail; - GfxStencilFunc func; - }; - - NLOHMANN_DEFINE_TYPE_EXTENSION(JsonStencil, pass, fail, zfail, func); - - enum class JsonAlphaTest - { - INVALID, - DISABLED, - GT0, - GE128 - }; - - NLOHMANN_JSON_SERIALIZE_ENUM( - JsonAlphaTest, - { - {JsonAlphaTest::INVALID, nullptr }, - {JsonAlphaTest::DISABLED, "disabled"}, - {JsonAlphaTest::GT0, "gt0" }, - {JsonAlphaTest::GE128, "ge128" } - }); - - enum class JsonCullFace - { - INVALID, - NONE, - BACK, - FRONT - }; - - NLOHMANN_JSON_SERIALIZE_ENUM( - JsonCullFace, - { - {JsonCullFace::INVALID, nullptr}, - {JsonCullFace::NONE, "none" }, - {JsonCullFace::BACK, "back" }, - {JsonCullFace::FRONT, "front"} - }); - - enum class JsonDepthTest - { - INVALID, - DISABLED, - ALWAYS, - LESS, - EQUAL, - LESS_EQUAL - }; - - NLOHMANN_JSON_SERIALIZE_ENUM(JsonDepthTest, - { - {JsonDepthTest::INVALID, nullptr }, - {JsonDepthTest::DISABLED, "disabled" }, - {JsonDepthTest::ALWAYS, "always" }, - {JsonDepthTest::LESS, "less" }, - {JsonDepthTest::EQUAL, "equal" }, - {JsonDepthTest::LESS_EQUAL, "less_equal"} - }); - - NLOHMANN_JSON_SERIALIZE_ENUM(GfxBlend, - { - {GFXS_BLEND_DISABLED, "disabled" }, - {GFXS_BLEND_ZERO, "zero" }, - {GFXS_BLEND_ONE, "one" }, - {GFXS_BLEND_SRCCOLOR, "srccolor" }, - {GFXS_BLEND_INVSRCCOLOR, "invsrccolor" }, - {GFXS_BLEND_SRCALPHA, "srcalpha" }, - {GFXS_BLEND_INVSRCALPHA, "invsrcalpha" }, - {GFXS_BLEND_DESTALPHA, "destalpha" }, - {GFXS_BLEND_INVDESTALPHA, "invdestalpha"}, - {GFXS_BLEND_DESTCOLOR, "destcolor" }, - {GFXS_BLEND_INVDESTCOLOR, "invdestcolor"}, - }); - - NLOHMANN_JSON_SERIALIZE_ENUM(GfxBlendOp, - { - {GFXS_BLENDOP_DISABLED, "disabled" }, - {GFXS_BLENDOP_ADD, "add" }, - {GFXS_BLENDOP_SUBTRACT, "subtract" }, - {GFXS_BLENDOP_REVSUBTRACT, "revsubtract"}, - {GFXS_BLENDOP_MIN, "min" }, - {GFXS_BLENDOP_MAX, "max" }, - }); - - NLOHMANN_JSON_SERIALIZE_ENUM(GfxPolygonOffset_e, - { - {GFXS_POLYGON_OFFSET_0, "offset0" }, - {GFXS_POLYGON_OFFSET_1, "offset1" }, - {GFXS_POLYGON_OFFSET_2, "offset2" }, - {GFXS_POLYGON_OFFSET_SHADOWMAP, "offsetShadowmap"}, - }); - - class JsonStateBitsTableEntry - { - public: - GfxBlend srcBlendRgb; - GfxBlend dstBlendRgb; - GfxBlendOp blendOpRgb; - JsonAlphaTest alphaTest; - JsonCullFace cullFace; - GfxBlend srcBlendAlpha; - GfxBlend dstBlendAlpha; - GfxBlendOp blendOpAlpha; - bool colorWriteRgb; - bool colorWriteAlpha; - bool polymodeLine; - bool depthWrite; - JsonDepthTest depthTest; - GfxPolygonOffset_e polygonOffset; - std::optional stencilFront; - std::optional stencilBack; - }; - - NLOHMANN_DEFINE_TYPE_EXTENSION(JsonStateBitsTableEntry, - srcBlendRgb, - dstBlendRgb, - blendOpRgb, - alphaTest, - cullFace, - srcBlendAlpha, - dstBlendAlpha, - blendOpAlpha, - colorWriteRgb, - colorWriteAlpha, - polymodeLine, - depthWrite, - depthWrite, - depthTest, - polygonOffset, - stencilFront, - stencilBack); - - class JsonConstant - { - public: - std::optional name; - std::optional nameFragment; - std::optional nameHash; - std::vector literal; - }; - - inline void to_json(nlohmann::json& out, const JsonConstant& in) - { - if (in.name.has_value()) - { - optional_to_json(out, "name", in.name); - } - else - { - optional_to_json(out, "nameFragment", in.nameFragment); - optional_to_json(out, "nameHash", in.nameHash); - } - - out["literal"] = in.literal; - } - - inline void from_json(const nlohmann::json& in, JsonConstant& out) - { - optional_from_json(in, "name", out.name); - optional_from_json(in, "nameFragment", out.nameFragment); - optional_from_json(in, "nameHash", out.nameHash); - in.at("literal").get_to(out.literal); - }; - - NLOHMANN_JSON_SERIALIZE_ENUM(TextureFilter, - { - {TEXTURE_FILTER_DISABLED, "disabled"}, - {TEXTURE_FILTER_NEAREST, "nearest" }, - {TEXTURE_FILTER_LINEAR, "linear" }, - {TEXTURE_FILTER_ANISO2X, "aniso2x" }, - {TEXTURE_FILTER_ANISO4X, "aniso4x" }, - {TEXTURE_FILTER_COMPARE, "compare" }, - }); - - NLOHMANN_JSON_SERIALIZE_ENUM(SamplerStateBitsMipMap_e, - { - {SAMPLER_MIPMAP_ENUM_DISABLED, "disabled"}, - {SAMPLER_MIPMAP_ENUM_NEAREST, "nearest" }, - {SAMPLER_MIPMAP_ENUM_LINEAR, "linear" }, - }); - - class JsonSamplerState - { - public: - TextureFilter filter; - SamplerStateBitsMipMap_e mipMap; - bool clampU; - bool clampV; - bool clampW; - }; - - NLOHMANN_DEFINE_TYPE_EXTENSION(JsonSamplerState, filter, mipMap, clampU, clampV, clampW); - - NLOHMANN_JSON_SERIALIZE_ENUM(TextureSemantic, - { - {TS_2D, "2D" }, - {TS_FUNCTION, "function" }, - {TS_COLOR_MAP, "colorMap" }, - {TS_UNUSED_1, "unused1" }, - {TS_UNUSED_2, "unused2" }, - {TS_NORMAL_MAP, "normalMap" }, - {TS_UNUSED_3, "unused3" }, - {TS_UNUSED_4, "unused4" }, - {TS_SPECULAR_MAP, "specularMap" }, - {TS_UNUSED_5, "unused5" }, - {TS_OCCLUSION_MAP, "occlusionMap"}, - {TS_UNUSED_6, "unused6" }, - {TS_COLOR0_MAP, "color0Map" }, - {TS_COLOR1_MAP, "color1Map" }, - {TS_COLOR2_MAP, "color2Map" }, - {TS_COLOR3_MAP, "color3Map" }, - {TS_COLOR4_MAP, "color4Map" }, - {TS_COLOR5_MAP, "color5Map" }, - {TS_COLOR6_MAP, "color6Map" }, - {TS_COLOR7_MAP, "color7Map" }, - {TS_COLOR8_MAP, "color8Map" }, - {TS_COLOR9_MAP, "color9Map" }, - {TS_COLOR10_MAP, "color10Map" }, - {TS_COLOR11_MAP, "color11Map" }, - {TS_COLOR12_MAP, "color12Map" }, - {TS_COLOR13_MAP, "color13Map" }, - {TS_COLOR14_MAP, "color14Map" }, - {TS_COLOR15_MAP, "color15Map" }, - {TS_THROW_MAP, "throwMap" }, - }); - - class JsonTexture - { - public: - std::optional name; - std::optional nameHash; - std::optional nameStart; - std::optional nameEnd; - TextureSemantic semantic; - bool isMatureContent; - JsonSamplerState samplerState; - std::string image; - }; - - inline void to_json(nlohmann::json& out, const JsonTexture& in) - { - if (in.name.has_value()) - { - optional_to_json(out, "name", in.name); - } - else - { - optional_to_json(out, "nameHash", in.nameHash); - optional_to_json(out, "nameStart", in.nameStart); - optional_to_json(out, "nameEnd", in.nameEnd); - } - - out["semantic"] = in.semantic; - out["isMatureContent"] = in.isMatureContent; - out["samplerState"] = in.samplerState; - out["image"] = in.image; - } - - inline void from_json(const nlohmann::json& in, JsonTexture& out) - { - optional_from_json(in, "name", out.name); - optional_from_json(in, "nameHash", out.nameHash); - optional_from_json(in, "nameStart", out.nameStart); - optional_from_json(in, "nameEnd", out.nameEnd); - in.at("semantic").get_to(out.semantic); - in.at("isMatureContent").get_to(out.isMatureContent); - in.at("samplerState").get_to(out.samplerState); - in.at("image").get_to(out.image); - }; - - class JsonTextureAtlas - { - public: - uint8_t rows; - uint8_t columns; - }; - - NLOHMANN_DEFINE_TYPE_EXTENSION(JsonTextureAtlas, rows, columns); - - NLOHMANN_JSON_SERIALIZE_ENUM(MaterialGameFlags, - { - {MTL_GAMEFLAG_1, "1" }, - {MTL_GAMEFLAG_2, "2" }, - {MTL_GAMEFLAG_NO_MARKS, "NO_MARKS" }, - {MTL_GAMEFLAG_NO_MARKS, "4" }, - {MTL_GAMEFLAG_8, "8" }, - {MTL_GAMEFLAG_10, "10" }, - {MTL_GAMEFLAG_20, "20" }, - {MTL_GAMEFLAG_CASTS_SHADOW, "CASTS_SHADOW"}, - {MTL_GAMEFLAG_CASTS_SHADOW, "40" }, - {MTL_GAMEFLAG_80, "80" }, - {MTL_GAMEFLAG_100, "100" }, - {MTL_GAMEFLAG_200, "200" }, - {MTL_GAMEFLAG_400, "400" }, - {MTL_GAMEFLAG_800, "800" }, - {MTL_GAMEFLAG_1000, "1000" }, - }); - - NLOHMANN_JSON_SERIALIZE_ENUM(GfxCameraRegionType, - { - {CAMERA_REGION_LIT_OPAQUE, "litOpaque" }, - {CAMERA_REGION_LIT_TRANS, "litTrans" }, - {CAMERA_REGION_LIT_QUASI_OPAQUE, "litQuasiOpaque"}, - {CAMERA_REGION_EMISSIVE_OPAQUE, "emissiveOpaque"}, - {CAMERA_REGION_EMISSIVE_TRANS, "emissiveTrans" }, - {CAMERA_REGION_EMISSIVE_FX, "emissiveFx" }, - {CAMERA_REGION_LIGHT_MAP_OPAQUE, "lightMapOpaque"}, - {CAMERA_REGION_DEPTH_HACK, "depthHack" }, - {CAMERA_REGION_UNUSED, "unused" }, - {CAMERA_REGION_SONAR, "sonar" }, - {CAMERA_REGION_NONE, "none" }, - }); - - class JsonMaterial - { - public: - std::vector gameFlags; - unsigned sortKey; - std::optional textureAtlas; - unsigned surfaceTypeBits; - unsigned layeredSurfaceTypes; - unsigned hashIndex; - unsigned surfaceFlags; - unsigned contents; - std::vector stateBitsEntry; - unsigned stateFlags; - GfxCameraRegionType cameraRegion; - uint8_t probeMipBits; - std::string techniqueSet; - std::vector textures; - std::vector constants; - std::vector stateBits; - std::optional thermalMaterial; - }; - - NLOHMANN_DEFINE_TYPE_EXTENSION(JsonMaterial, - gameFlags, - sortKey, - textureAtlas, - surfaceTypeBits, - layeredSurfaceTypes, - hashIndex, - surfaceFlags, - contents, - stateBitsEntry, - stateFlags, - cameraRegion, - probeMipBits, - techniqueSet, - textures, - constants, - stateBits, - thermalMaterial); -} // namespace T6 diff --git a/src/ObjCommon/Material/JsonMaterial.h.template b/src/ObjCommon/Material/JsonMaterial.h.template new file mode 100644 index 00000000..7c7f0663 --- /dev/null +++ b/src/ObjCommon/Material/JsonMaterial.h.template @@ -0,0 +1,539 @@ +#options GAME (IW4, IW5, T6) + +#filename "Game/" + GAME + "/Material/JsonMaterial" + GAME + ".h" + +#if GAME == "IW4" +#define FEATURE_IW4 +#define HAS_WATER +#elif GAME == "IW5" +#define FEATURE_IW5 +#define HAS_WATER +#elif GAME == "T6" +#define FEATURE_T6 +#endif + +// This file was templated. +// See JsonMaterialWriter.h.template. +// Do not modify, changes will be lost. + +#pragma once + +#set GAME_HEADER "\"Game/" + GAME + "/" + GAME + ".h\"" +#include GAME_HEADER + +#include "Json/JsonExtension.h" + +#include +#include +#include +#include +#include + +namespace GAME +{ + NLOHMANN_JSON_SERIALIZE_ENUM(GfxStencilOp, { + {GFXS_STENCILOP_KEEP, "keep" }, + {GFXS_STENCILOP_ZERO, "zero" }, + {GFXS_STENCILOP_REPLACE, "replace"}, + {GFXS_STENCILOP_INCRSAT, "incrsat"}, + {GFXS_STENCILOP_DECRSAT, "decrsat"}, + {GFXS_STENCILOP_INVERT, "invert" }, + {GFXS_STENCILOP_INCR, "incr" }, + {GFXS_STENCILOP_DECR, "decr" }, + }); + + NLOHMANN_JSON_SERIALIZE_ENUM(GfxStencilFunc, { + {GFXS_STENCILFUNC_NEVER, "never" }, + {GFXS_STENCILFUNC_LESS, "less" }, + {GFXS_STENCILFUNC_EQUAL, "equal" }, + {GFXS_STENCILFUNC_LESSEQUAL, "lessequal" }, + {GFXS_STENCILFUNC_GREATER, "greater" }, + {GFXS_STENCILFUNC_NOTEQUAL, "notequal" }, + {GFXS_STENCILFUNC_GREATEREQUAL, "greaterequal"}, + {GFXS_STENCILFUNC_ALWAYS, "always" }, + }); + + class JsonStencil + { + public: + GfxStencilOp pass; + GfxStencilOp fail; + GfxStencilOp zfail; + GfxStencilFunc func; + }; + + NLOHMANN_DEFINE_TYPE_EXTENSION( + JsonStencil, + pass, + fail, + zfail, + func + ); + + enum class JsonAlphaTest + { + INVALID, + DISABLED, + GT0, +#if defined(FEATURE_IW4) || defined(FEATURE_IW5) + LT128, +#endif + GE128 + }; + + NLOHMANN_JSON_SERIALIZE_ENUM(JsonAlphaTest, { + {JsonAlphaTest::INVALID, nullptr }, + {JsonAlphaTest::DISABLED, "disabled"}, + {JsonAlphaTest::GT0, "gt0" }, +#if defined(FEATURE_IW4) || defined(FEATURE_IW5) + {JsonAlphaTest::LT128, "lt128" }, +#endif + {JsonAlphaTest::GE128, "ge128" } + }); + + enum class JsonCullFace + { + INVALID, + NONE, + BACK, + FRONT + }; + + NLOHMANN_JSON_SERIALIZE_ENUM(JsonCullFace, { + {JsonCullFace::INVALID, nullptr}, + {JsonCullFace::NONE, "none" }, + {JsonCullFace::BACK, "back" }, + {JsonCullFace::FRONT, "front"} + }); + + enum class JsonDepthTest + { + INVALID, + DISABLED, + ALWAYS, + LESS, + EQUAL, + LESS_EQUAL + }; + + NLOHMANN_JSON_SERIALIZE_ENUM(JsonDepthTest, { + {JsonDepthTest::INVALID, nullptr }, + {JsonDepthTest::DISABLED, "disabled" }, + {JsonDepthTest::ALWAYS, "always" }, + {JsonDepthTest::LESS, "less" }, + {JsonDepthTest::EQUAL, "equal" }, + {JsonDepthTest::LESS_EQUAL, "less_equal"} + }); + + NLOHMANN_JSON_SERIALIZE_ENUM(GfxBlend, { + {GFXS_BLEND_DISABLED, "disabled" }, + {GFXS_BLEND_ZERO, "zero" }, + {GFXS_BLEND_ONE, "one" }, + {GFXS_BLEND_SRCCOLOR, "srccolor" }, + {GFXS_BLEND_INVSRCCOLOR, "invsrccolor" }, + {GFXS_BLEND_SRCALPHA, "srcalpha" }, + {GFXS_BLEND_INVSRCALPHA, "invsrcalpha" }, + {GFXS_BLEND_DESTALPHA, "destalpha" }, + {GFXS_BLEND_INVDESTALPHA, "invdestalpha"}, + {GFXS_BLEND_DESTCOLOR, "destcolor" }, + {GFXS_BLEND_INVDESTCOLOR, "invdestcolor"}, + }); + + NLOHMANN_JSON_SERIALIZE_ENUM(GfxBlendOp, { + {GFXS_BLENDOP_DISABLED, "disabled" }, + {GFXS_BLENDOP_ADD, "add" }, + {GFXS_BLENDOP_SUBTRACT, "subtract" }, + {GFXS_BLENDOP_REVSUBTRACT, "revsubtract"}, + {GFXS_BLENDOP_MIN, "min" }, + {GFXS_BLENDOP_MAX, "max" }, + }); + + NLOHMANN_JSON_SERIALIZE_ENUM(GfxPolygonOffset_e, { + {GFXS_POLYGON_OFFSET_0, "offset0" }, + {GFXS_POLYGON_OFFSET_1, "offset1" }, + {GFXS_POLYGON_OFFSET_2, "offset2" }, + {GFXS_POLYGON_OFFSET_SHADOWMAP, "offsetShadowmap"}, + }); + + class JsonStateBitsTableEntry + { + public: + GfxBlend srcBlendRgb; + GfxBlend dstBlendRgb; + GfxBlendOp blendOpRgb; + JsonAlphaTest alphaTest; + JsonCullFace cullFace; + GfxBlend srcBlendAlpha; + GfxBlend dstBlendAlpha; + GfxBlendOp blendOpAlpha; + bool colorWriteRgb; + bool colorWriteAlpha; +#if defined(FEATURE_IW4) || defined(FEATURE_IW5) + bool gammaWrite; +#endif + bool polymodeLine; + bool depthWrite; + JsonDepthTest depthTest; + GfxPolygonOffset_e polygonOffset; + std::optional stencilFront; + std::optional stencilBack; + }; + + NLOHMANN_DEFINE_TYPE_EXTENSION(JsonStateBitsTableEntry, + srcBlendRgb, + dstBlendRgb, + blendOpRgb, + alphaTest, + cullFace, + srcBlendAlpha, + dstBlendAlpha, + blendOpAlpha, + colorWriteRgb, + colorWriteAlpha, +#if defined(FEATURE_IW4) || defined(FEATURE_IW5) + gammaWrite, +#endif + polymodeLine, + depthWrite, + depthWrite, + depthTest, + polygonOffset, + stencilFront, + stencilBack + ); + + class JsonConstant + { + public: + std::optional name; + std::optional nameFragment; + std::optional nameHash; + std::vector literal; + }; + + inline void to_json(nlohmann::json& out, const JsonConstant& in) + { + if (in.name.has_value()) + { + optional_to_json(out, "name", in.name); + } + else + { + optional_to_json(out, "nameFragment", in.nameFragment); + optional_to_json(out, "nameHash", in.nameHash); + } + + out["literal"] = in.literal; + } + + inline void from_json(const nlohmann::json& in, JsonConstant& out) + { + optional_from_json(in, "name", out.name); + optional_from_json(in, "nameFragment", out.nameFragment); + optional_from_json(in, "nameHash", out.nameHash); + in.at("literal").get_to(out.literal); + }; + + NLOHMANN_JSON_SERIALIZE_ENUM(TextureFilter, { + {TEXTURE_FILTER_DISABLED, "disabled"}, + {TEXTURE_FILTER_NEAREST, "nearest" }, + {TEXTURE_FILTER_LINEAR, "linear" }, + {TEXTURE_FILTER_ANISO2X, "aniso2x" }, + {TEXTURE_FILTER_ANISO4X, "aniso4x" }, +#ifdef FEATURE_T6 + {TEXTURE_FILTER_COMPARE, "compare" }, +#endif + }); + + NLOHMANN_JSON_SERIALIZE_ENUM(SamplerStateBitsMipMap_e, { + {SAMPLER_MIPMAP_ENUM_DISABLED, "disabled"}, + {SAMPLER_MIPMAP_ENUM_NEAREST, "nearest" }, + {SAMPLER_MIPMAP_ENUM_LINEAR, "linear" }, + }); + + class JsonSamplerState + { + public: + TextureFilter filter; + SamplerStateBitsMipMap_e mipMap; + bool clampU; + bool clampV; + bool clampW; + }; + + NLOHMANN_DEFINE_TYPE_EXTENSION( + JsonSamplerState, + filter, + mipMap, + clampU, + clampV, + clampW + ); + +#ifdef HAS_WATER + class JsonComplex + { + public: + float real; + float imag; + }; + + NLOHMANN_DEFINE_TYPE_EXTENSION(JsonComplex, real, imag); + + class JsonWater + { + public: + float floatTime; + int m; + int n; + std::string h0; + std::string wTerm; + float lx; + float lz; + float gravity; + float windvel; + std::array winddir; + float amplitude; + std::array codeConstant; + }; + + NLOHMANN_DEFINE_TYPE_EXTENSION( + JsonWater, + floatTime, + m, + n, + h0, + wTerm, + lx, + lz, + gravity, + windvel, + winddir, + amplitude, + codeConstant + ); +#endif + + NLOHMANN_JSON_SERIALIZE_ENUM(TextureSemantic, { +#if defined(FEATURE_IW4) || defined(FEATURE_IW5) + {TS_2D, "2D" }, + {TS_FUNCTION, "function" }, + {TS_COLOR_MAP, "colorMap" }, + {TS_DETAIL_MAP, "detailMap" }, + {TS_UNUSED_2, "unused2" }, + {TS_NORMAL_MAP, "normalMap" }, + {TS_UNUSED_3, "unused3" }, + {TS_UNUSED_4, "unused4" }, + {TS_SPECULAR_MAP, "specularMap" }, + {TS_UNUSED_5, "unused5" }, + {TS_UNUSED_6, "unused6" }, + {TS_WATER_MAP, "waterMap" }, +#ifdef FEATURE_IW5 + {TS_DISPLACEMENT_MAP, "displacementMap"}, +#endif +#elif defined(FEATURE_T6) + {TS_2D, "2D" }, + {TS_FUNCTION, "function" }, + {TS_COLOR_MAP, "colorMap" }, + {TS_UNUSED_1, "unused1" }, + {TS_UNUSED_2, "unused2" }, + {TS_NORMAL_MAP, "normalMap" }, + {TS_UNUSED_3, "unused3" }, + {TS_UNUSED_4, "unused4" }, + {TS_SPECULAR_MAP, "specularMap" }, + {TS_UNUSED_5, "unused5" }, + {TS_OCCLUSION_MAP, "occlusionMap"}, + {TS_UNUSED_6, "unused6" }, + {TS_COLOR0_MAP, "color0Map" }, + {TS_COLOR1_MAP, "color1Map" }, + {TS_COLOR2_MAP, "color2Map" }, + {TS_COLOR3_MAP, "color3Map" }, + {TS_COLOR4_MAP, "color4Map" }, + {TS_COLOR5_MAP, "color5Map" }, + {TS_COLOR6_MAP, "color6Map" }, + {TS_COLOR7_MAP, "color7Map" }, + {TS_COLOR8_MAP, "color8Map" }, + {TS_COLOR9_MAP, "color9Map" }, + {TS_COLOR10_MAP, "color10Map" }, + {TS_COLOR11_MAP, "color11Map" }, + {TS_COLOR12_MAP, "color12Map" }, + {TS_COLOR13_MAP, "color13Map" }, + {TS_COLOR14_MAP, "color14Map" }, + {TS_COLOR15_MAP, "color15Map" }, + {TS_THROW_MAP, "throwMap" }, +#endif + }); + + class JsonTexture + { + public: + std::optional name; + std::optional nameHash; + std::optional nameStart; + std::optional nameEnd; + TextureSemantic semantic; +#ifdef FEATURE_T6 + bool isMatureContent; +#endif + JsonSamplerState samplerState; + std::string image; +#ifdef HAS_WATER + std::optional water; +#endif + }; + + inline void to_json(nlohmann::json& out, const JsonTexture& in) + { + if (in.name.has_value()) + { + optional_to_json(out, "name", in.name); + } + else + { + optional_to_json(out, "nameHash", in.nameHash); + optional_to_json(out, "nameStart", in.nameStart); + optional_to_json(out, "nameEnd", in.nameEnd); + } + + out["semantic"] = in.semantic; +#ifdef FEATURE_T6 + out["isMatureContent"] = in.isMatureContent; +#endif + out["samplerState"] = in.samplerState; + out["image"] = in.image; +#ifdef HAS_WATER + optional_to_json(out, "water", in.water); +#endif + } + + inline void from_json(const nlohmann::json& in, JsonTexture& out) + { + optional_from_json(in, "name", out.name); + optional_from_json(in, "nameHash", out.nameHash); + optional_from_json(in, "nameStart", out.nameStart); + optional_from_json(in, "nameEnd", out.nameEnd); + in.at("semantic").get_to(out.semantic); +#ifdef FEATURE_T6 + in.at("isMatureContent").get_to(out.isMatureContent); +#endif + in.at("samplerState").get_to(out.samplerState); + in.at("image").get_to(out.image); +#ifdef HAS_WATER + optional_from_json(in, "water", out.water); +#endif + }; + + class JsonTextureAtlas + { + public: + uint8_t rows; + uint8_t columns; + }; + + NLOHMANN_DEFINE_TYPE_EXTENSION( + JsonTextureAtlas, + rows, + columns + ); + + NLOHMANN_JSON_SERIALIZE_ENUM(MaterialGameFlags, { +#if defined(FEATURE_IW4) || defined(FEATURE_IW5) + {MTL_GAMEFLAG_1, "1" }, + {MTL_GAMEFLAG_2, "2" }, + {MTL_GAMEFLAG_4, "4" }, + {MTL_GAMEFLAG_8, "8" }, + {MTL_GAMEFLAG_10, "10" }, + {MTL_GAMEFLAG_20, "20" }, + {MTL_GAMEFLAG_40, "40" }, + {MTL_GAMEFLAG_80, "80" }, +#elif defined(FEATURE_T6) + {MTL_GAMEFLAG_1, "1" }, + {MTL_GAMEFLAG_2, "2" }, + {MTL_GAMEFLAG_NO_MARKS, "NO_MARKS" }, + {MTL_GAMEFLAG_NO_MARKS, "4" }, + {MTL_GAMEFLAG_8, "8" }, + {MTL_GAMEFLAG_10, "10" }, + {MTL_GAMEFLAG_20, "20" }, + {MTL_GAMEFLAG_CASTS_SHADOW, "CASTS_SHADOW"}, + {MTL_GAMEFLAG_CASTS_SHADOW, "40" }, + {MTL_GAMEFLAG_80, "80" }, + {MTL_GAMEFLAG_100, "100" }, + {MTL_GAMEFLAG_200, "200" }, + {MTL_GAMEFLAG_400, "400" }, + {MTL_GAMEFLAG_800, "800" }, + {MTL_GAMEFLAG_1000, "1000" }, +#endif + }); + + NLOHMANN_JSON_SERIALIZE_ENUM(GfxCameraRegionType, { +#if defined(FEATURE_IW4) || defined(FEATURE_IW5) + {CAMERA_REGION_LIT_OPAQUE, "litOpaque" }, + {CAMERA_REGION_LIT_TRANS, "litTrans" }, + {CAMERA_REGION_EMISSIVE, "emissive" }, + {CAMERA_REGION_DEPTH_HACK, "depthHack" }, +#ifdef FEATURE_IW5 + {CAMERA_REGION_LIGHT_MAP_OPAQUE, "lightMapOpaque"}, +#endif + {CAMERA_REGION_NONE, "none" }, +#elif defined(FEATURE_T6) + {CAMERA_REGION_LIT_OPAQUE, "litOpaque" }, + {CAMERA_REGION_LIT_TRANS, "litTrans" }, + {CAMERA_REGION_LIT_QUASI_OPAQUE, "litQuasiOpaque"}, + {CAMERA_REGION_EMISSIVE_OPAQUE, "emissiveOpaque"}, + {CAMERA_REGION_EMISSIVE_TRANS, "emissiveTrans" }, + {CAMERA_REGION_EMISSIVE_FX, "emissiveFx" }, + {CAMERA_REGION_LIGHT_MAP_OPAQUE, "lightMapOpaque"}, + {CAMERA_REGION_DEPTH_HACK, "depthHack" }, + {CAMERA_REGION_UNUSED, "unused" }, + {CAMERA_REGION_SONAR, "sonar" }, + {CAMERA_REGION_NONE, "none" }, +#endif + }); + + class JsonMaterial + { + public: +#ifdef FEATURE_T6 + unsigned layeredSurfaceTypes; + unsigned hashIndex; + unsigned surfaceFlags; + unsigned contents; + uint8_t probeMipBits; + std::optional thermalMaterial; +#endif + std::vector gameFlags; + unsigned sortKey; + std::optional textureAtlas; + unsigned surfaceTypeBits; + std::vector stateBitsEntry; + unsigned stateFlags; + GfxCameraRegionType cameraRegion; + std::string techniqueSet; + std::vector textures; + std::vector constants; + std::vector stateBits; + }; + + NLOHMANN_DEFINE_TYPE_EXTENSION( + JsonMaterial, +#ifdef FEATURE_T6 + layeredSurfaceTypes, + hashIndex, + surfaceFlags, + contents, + probeMipBits, + thermalMaterial, +#endif + gameFlags, + sortKey, + textureAtlas, + surfaceTypeBits, + stateBitsEntry, + stateFlags, + cameraRegion, + techniqueSet, + textures, + constants, + stateBits + ); +} // namespace GAME diff --git a/src/ObjCommon/Material/MaterialCommon.cpp b/src/ObjCommon/Material/MaterialCommon.cpp new file mode 100644 index 00000000..895ef526 --- /dev/null +++ b/src/ObjCommon/Material/MaterialCommon.cpp @@ -0,0 +1,22 @@ +#include "MaterialCommon.h" + +#include +#include + +namespace material +{ + std::string GetFileNameForAssetName(const std::string& assetName) + { + std::string sanitizedFileName(assetName); + if (sanitizedFileName[0] == '*') + { + std::ranges::replace(sanitizedFileName, '*', '_'); + const auto parenthesisPos = sanitizedFileName.find('('); + if (parenthesisPos != std::string::npos) + sanitizedFileName.erase(parenthesisPos); + sanitizedFileName = std::format("generated/{}", sanitizedFileName); + } + + return std::format("materials/{}.json", sanitizedFileName); + } +} // namespace material diff --git a/src/ObjCommon/Material/MaterialCommon.h b/src/ObjCommon/Material/MaterialCommon.h new file mode 100644 index 00000000..f087b484 --- /dev/null +++ b/src/ObjCommon/Material/MaterialCommon.h @@ -0,0 +1,8 @@ +#pragma once + +#include + +namespace material +{ + std::string GetFileNameForAssetName(const std::string& assetName); +} diff --git a/src/ObjCompiling/Game/IW4/Material/CompilerMaterialIW4.cpp b/src/ObjCompiling/Game/IW4/Material/CompilerMaterialIW4.cpp new file mode 100644 index 00000000..7b54ceaa --- /dev/null +++ b/src/ObjCompiling/Game/IW4/Material/CompilerMaterialIW4.cpp @@ -0,0 +1,1383 @@ +#include "CompilerMaterialIW4.h" + +#include "Game/IW4/CommonIW4.h" +#include "Game/IW4/IW4.h" +#include "Game/IW4/MaterialConstantsIW4.h" +#include "Game/IW4/ObjConstantsIW4.h" +#include "Game/IW4/Techset/CompilerTechsetIW4.h" +#include "Game/IW4/TechsetConstantsIW4.h" +#include "Gdt/AbstractGdtEntryReader.h" +#include "Gdt/IGdtQueryable.h" +#include "ObjLoading.h" +#include "Pool/GlobalAssetPool.h" +#include "StateMap/StateMapFromTechniqueExtractor.h" +#include "StateMap/StateMapHandler.h" +#include "Techset/TechniqueFileReader.h" +#include "Techset/TechniqueStateMapCache.h" +#include "Techset/TechsetDefinitionCache.h" + +#include +#include +#include +#include +#include + +using namespace IW4; + +namespace +{ + class SkipMaterialException final : public std::exception + { + }; + + class MaterialGdtLoader : AbstractGdtEntryReader + { + public: + MaterialGdtLoader(const GdtEntry& entry, + Material& material, + MemoryManager& memory, + ISearchPath& searchPath, + AssetCreationContext& context, + AssetRegistration& registration) + : AbstractGdtEntryReader(entry), + m_material(material), + m_memory(memory), + m_search_path(searchPath), + m_context(context), + m_registration(registration), + m_state_map_cache(context.GetZoneAssetCreationState()), + m_base_state_bits{}, + m_techset_creator(CreateTechsetLoader(memory, searchPath)) + { + } + + bool Load() + { + material_template(); + + FinalizeMaterial(); + + return true; + } + + 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 + throw GdtReadingException(std::format("Unknown material type: \"{}\"", materialType)); + } + + 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("tileColor", GdtTileModeNames, std::extent_v); + const auto filterColor = ReadEnumProperty("filterColor", GdtSamplerFilterNames, std::extent_v); + + 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() + { + // TODO + throw SkipMaterialException(); + + 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("tileColor", GdtTileModeNames, std::extent_v); + const auto filterColor = ReadEnumProperty("filterColor", GdtSamplerFilterNames, std::extent_v); + + 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", {distortionScaleX, distortionScaleY, 0, 0}); + + if (uvAnim) + { + const auto uvScrollX = ReadFloatProperty("uvScrollX"); + const auto uvScrollY = ReadFloatProperty("uvScrollY"); + const auto uvScrollRotate = ReadFloatProperty("uvScrollRotate"); + AddConstant("uvAnimParms", {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("tileColor", GdtTileModeNames, std::extent_v); + const auto filterColor = ReadEnumProperty("filterColor", GdtSamplerFilterNames, std::extent_v); + + 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 << std::format("Using particlecloud for \"{}\"\n", m_material.info.name); + } + + 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("tileColor", GdtTileModeNames, std::extent_v); + const auto filterColor = ReadEnumProperty("filterColor", GdtSamplerFilterNames, std::extent_v); + + 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 + throw GdtReadingException(std::format("Unknown custom template: \"{}\"", customTemplate)); + } + + 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("tileColor", GdtTileModeNames, std::extent_v); + const auto filterColor = ReadEnumProperty("filterColor", GdtSamplerFilterNames, std::extent_v); + + 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", {1.0f / zFeatherDepth, zFeatherDepth, 0, 0}); + } + + if (std::fpclassify(eyeOffsetDepth) != FP_ZERO) + AddConstant("eyeOffsetParms", {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(sortKeyIndex)); + foundSortKey = true; + break; + } + } + + if (!foundSortKey) + { + char* endPtr; + const auto sortKeyNum = strtoul(sortKey.c_str(), &endPtr, 10); + + if (endPtr != &sortKey[sortKey.size()]) + throw GdtReadingException(std::format("Invalid sort value: \"{}\"", sortKey)); + + SetSort(static_cast(sortKeyNum)); + } + } + + void clamp_template() {} + + void textureAtlas_template() + { + const auto rowCount = ReadIntegerProperty("textureAtlasRowCount", 1); + const auto columnCount = ReadIntegerProperty("textureAtlasColumnCount", 1); + + SetTextureAtlas(static_cast(rowCount), static_cast(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 + throw GdtReadingException(std::format("Invalid alphatest value: \"{}\"", alphaTest)); + } + + 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_COLOR, CustomBlendFunc_e::ONE); + SetSeparateAlphaBlendFunc(BlendOp_e::DISABLE, CustomBlendFunc_e::ONE, CustomBlendFunc_e::ZERO); + } + else if (blendFunc == GDT_BLEND_FUNC_CUSTOM) + { + const auto customBlendOpRgb = ReadEnumProperty("customBlendOpRgb", GdtBlendOpNames, std::extent_v); + const auto srcCustomBlendFunc = + ReadEnumProperty("srcCustomBlendFunc", GdtCustomBlendFuncNames, std::extent_v); + const auto destCustomBlendFunc = + ReadEnumProperty("destCustomBlendFunc", GdtCustomBlendFuncNames, std::extent_v); + const auto customBlendOpAlpha = ReadEnumProperty("customBlendOpAlpha", GdtBlendOpNames, std::extent_v); + const auto srcCustomBlendFuncAlpha = + ReadEnumProperty("srcCustomBlendFuncAlpha", GdtCustomBlendFuncNames, std::extent_v); + const auto destCustomBlendFuncAlpha = + ReadEnumProperty("destCustomBlendFuncAlpha", GdtCustomBlendFuncNames, std::extent_v); + + SetBlendFunc(customBlendOpRgb, srcCustomBlendFunc, destCustomBlendFunc); + SetSeparateAlphaBlendFunc(customBlendOpAlpha, srcCustomBlendFuncAlpha, destCustomBlendFuncAlpha); + } + else + { + throw GdtReadingException(std::format("Invalid blendfunc value: \"{}\"", blendFunc)); + } + } + + void colorwrite_template() + { + const auto colorWriteRed = ReadEnumProperty( + "colorWriteRed", GdtStateBitsEnabledStatusNames, std::extent_v); + const auto colorWriteGreen = ReadEnumProperty( + "colorWriteGreen", GdtStateBitsEnabledStatusNames, std::extent_v); + const auto colorWriteBlue = ReadEnumProperty( + "colorWriteBlue", GdtStateBitsEnabledStatusNames, std::extent_v); + const auto colorWriteAlpha = ReadEnumProperty( + "colorWriteAlpha", GdtStateBitsEnabledStatusNames, std::extent_v); + + SetColorWrite(colorWriteRed, colorWriteGreen, colorWriteBlue, colorWriteAlpha); + } + + void cullface_template() + { + const auto cullFace = ReadEnumProperty("cullFace", GdtCullFaceNames, std::extent_v); + + SetCullFace(cullFace); + } + + void depthtest_template() + { + const auto depthTest = ReadEnumProperty("depthTest", GdtDepthTestNames, std::extent_v); + + SetDepthTest(depthTest); + } + + void depthwrite_template() + { + const auto depthWrite = + ReadEnumProperty("depthWrite", GdtStateBitsOnOffStatusNames, std::extent_v); + 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 + { + throw GdtReadingException(std::format("Invalid depthWrite blendFunc value: \"{}\"", blendFunc)); + } + } + + void gammawrite_template() + { + const auto gammaWrite = + ReadEnumProperty("gammaWrite", GdtStateBitsOnOffStatusNames, std::extent_v); + + 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", GdtPolygonOffsetNames, std::extent_v); + + SetPolygonOffset(polygonOffset); + } + + void stencil_template() + { + const auto stencilMode = ReadEnumProperty("stencil", GdtStencilModeNames, std::extent_v); + + if (stencilMode == StencilMode_e::DISABLED) + { + DisableStencil(StencilIndex::FRONT); + DisableStencil(StencilIndex::BACK); + } + else + { + if (stencilMode == StencilMode_e::TWO_SIDED) + { + const auto stencilBackFunc = + ReadEnumProperty("stencilFunc2", GdtStencilFuncNames, std::extent_v); + const auto stencilBackOpFail = + ReadEnumProperty("stencilOpFail2", GdtStencilOpNames, std::extent_v); + const auto stencilBackOpZFail = + ReadEnumProperty("stencilOpZFail2", GdtStencilOpNames, std::extent_v); + const auto stencilBackOpPass = + ReadEnumProperty("stencilOpPass2", GdtStencilOpNames, std::extent_v); + + EnableStencil(StencilIndex::BACK, stencilBackFunc, stencilBackOpFail, stencilBackOpZFail, stencilBackOpPass); + } + + const auto stencilFrontFunc = + ReadEnumProperty("stencilFunc1", GdtStencilFuncNames, std::extent_v); + const auto stencilFrontOpFail = ReadEnumProperty("stencilOpFail1", GdtStencilOpNames, std::extent_v); + const auto stencilFrontOpZFail = + ReadEnumProperty("stencilOpZFail1", GdtStencilOpNames, std::extent_v); + const auto stencilFrontOpPass = ReadEnumProperty("stencilOpPass1", GdtStencilOpNames, std::extent_v); + + EnableStencil(StencilIndex::FRONT, stencilFrontFunc, stencilFrontOpFail, stencilFrontOpZFail, stencilFrontOpPass); + } + } + + void SetTechniqueSet(const std::string& techsetName) + { + auto* techset = m_context.LoadDependency(techsetName); + + if (techset == nullptr) + throw GdtReadingException(std::format("Could not load techset: \"{}\"", techsetName)); + + m_registration.AddDependency(techset); + m_material.techniqueSet = techset->Asset(); + + auto& definitionCache = m_context.GetZoneAssetCreationState(); + + bool failure = false; + const auto* techsetDefinition = m_techset_creator->LoadTechsetDefinition(techsetName, m_context, failure); + if (techsetDefinition == nullptr) + { + throw GdtReadingException(std::format("Could not find techset definition for: \"{}\"", techsetName)); + } + + 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::ranges::find_if(m_state_bits, + [stateBitsForTechnique](const GfxStateBits& s1) + { + return s1.loadBits.raw[0] == stateBitsForTechnique.loadBits.raw[0] + && s1.loadBits.raw[1] == stateBitsForTechnique.loadBits.raw[1]; + }); + + if (foundStateBits != m_state_bits.end()) + { + m_material.stateBitsEntry[i] = static_cast(foundStateBits - m_state_bits.begin()); + } + else + { + m_material.stateBitsEntry[i] = static_cast(m_state_bits.size()); + m_state_bits.push_back(stateBitsForTechnique); + } + } + else + { + m_material.stateBitsEntry[i] = std::numeric_limits::max(); + } + } + } + + GfxStateBits GetStateBitsForTechnique(const std::string& techniqueName) + { + const auto* stateMap = GetStateMapForTechnique(techniqueName); + if (!stateMap) + return m_base_state_bits; + + const auto preCalculatedStateBits = m_state_bits_per_state_map.find(stateMap); + if (preCalculatedStateBits != m_state_bits_per_state_map.end()) + return preCalculatedStateBits->second; + + const auto stateBits = CalculateStateBitsWithStateMap(stateMap); + m_state_bits_per_state_map.emplace(stateMap, stateBits); + + return stateBits; + } + + _NODISCARD const state_map::StateMapDefinition* GetStateMapForTechnique(const std::string& techniqueName) const + { + const auto* preloadedStateMap = m_state_map_cache.GetStateMapForTechnique(techniqueName); + if (preloadedStateMap) + return preloadedStateMap; + + const auto techniqueFileName = GetTechniqueFileName(techniqueName); + const auto file = m_search_path.Open(techniqueFileName); + if (!file.IsOpen()) + return nullptr; + + state_map::StateMapFromTechniqueExtractor extractor; + const techset::TechniqueFileReader reader(*file.m_stream, techniqueFileName, &extractor); + if (!reader.ReadTechniqueDefinition()) + { + m_state_map_cache.SetTechniqueUsesStateMap(techniqueName, nullptr); + return nullptr; + } + + const auto stateMapName = extractor.RetrieveStateMap(); + const auto* loadedStateMap = m_techset_creator->LoadStateMapDefinition(stateMapName, m_context); + m_state_map_cache.SetTechniqueUsesStateMap(techniqueName, loadedStateMap); + + return loadedStateMap; + } + + GfxStateBits CalculateStateBitsWithStateMap(const state_map::StateMapDefinition* stateMap) const + { + const state_map::StateMapHandler stateMapHandler(stateMapLayout, *stateMap); + + GfxStateBits outBits{}; + stateMapHandler.ApplyStateMap(m_base_state_bits.loadBits.raw, outBits.loadBits.raw); + + return outBits; + } + + 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; + } + } + + void AddMapTexture(const std::string& typeName, + const TileMode_e tileMode, + const 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.mipMap = 0; + textureDef.semantic = static_cast(semantic); + + switch (tileMode) + { + case TileMode_e::TILE_BOTH: + textureDef.samplerState.clampU = 1; + textureDef.samplerState.clampV = 1; + textureDef.samplerState.clampW = 1; + break; + case TileMode_e::TILE_HORIZONTAL: + textureDef.samplerState.clampU = 0; + textureDef.samplerState.clampV = 1; + textureDef.samplerState.clampW = 0; + break; + case TileMode_e::TILE_VERTICAL: + textureDef.samplerState.clampU = 1; + textureDef.samplerState.clampV = 0; + textureDef.samplerState.clampW = 0; + break; + case TileMode_e::UNKNOWN: + case TileMode_e::NO_TILE: + textureDef.samplerState.clampU = 0; + textureDef.samplerState.clampV = 0; + textureDef.samplerState.clampW = 0; + break; + default: + assert(false); + break; + } + + switch (filterMode) + { + case GdtFilter_e::MIP_2X_BILINEAR: + textureDef.samplerState.filter = TEXTURE_FILTER_ANISO2X; + textureDef.samplerState.mipMap = SAMPLER_MIPMAP_ENUM_NEAREST; + break; + case GdtFilter_e::MIP_2X_TRILINEAR: + textureDef.samplerState.filter = TEXTURE_FILTER_ANISO2X; + textureDef.samplerState.mipMap = SAMPLER_MIPMAP_ENUM_LINEAR; + break; + case GdtFilter_e::MIP_4X_BILINEAR: + textureDef.samplerState.filter = TEXTURE_FILTER_ANISO4X; + textureDef.samplerState.mipMap = SAMPLER_MIPMAP_ENUM_NEAREST; + break; + case GdtFilter_e::MIP_4X_TRILINEAR: + textureDef.samplerState.filter = TEXTURE_FILTER_ANISO4X; + textureDef.samplerState.mipMap = SAMPLER_MIPMAP_ENUM_LINEAR; + break; + case GdtFilter_e::NOMIP_NEAREST: + textureDef.samplerState.filter = TEXTURE_FILTER_NEAREST; + textureDef.samplerState.mipMap = SAMPLER_MIPMAP_ENUM_DISABLED; + break; + case GdtFilter_e::NOMIP_BILINEAR: + textureDef.samplerState.filter = TEXTURE_FILTER_LINEAR; + textureDef.samplerState.mipMap = SAMPLER_MIPMAP_ENUM_DISABLED; + break; + default: + assert(false); + break; + } + + auto* image = m_context.LoadDependency(textureName); + + if (image == nullptr) + throw GdtReadingException(std::format("Could not load image: \"{}\"", textureName)); + + m_registration.AddDependency(image); + textureDef.u.image = image->Asset(); + + m_textures.push_back(textureDef); + } + + void AddConstant(const std::string& constantName, const GdtVec4& literalData) + { + MaterialConstantDef constantDef{}; + constantDef.literal.x = literalData.x; + constantDef.literal.y = literalData.y; + constantDef.literal.z = literalData.z; + constantDef.literal.w = literalData.w; + strncpy(constantDef.name, constantName.c_str(), std::extent_v); + 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.structured.alphaTestDisabled = 1; + break; + + case AlphaTest_e::GT0: + m_base_state_bits.loadBits.structured.alphaTest = GFXS_ALPHA_TEST_GT_0; + break; + + case AlphaTest_e::LT128: + m_base_state_bits.loadBits.structured.alphaTest = GFXS_ALPHA_TEST_LT_128; + break; + + case AlphaTest_e::GE128: + m_base_state_bits.loadBits.structured.alphaTest = GFXS_ALPHA_TEST_GE_128; + break; + + case AlphaTest_e::UNKNOWN: + default: + throw GdtReadingException(std::format("Unknown alphatest value: \"{}\"", static_cast(alphaTest))); + } + } + + 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.structured.blendOpRgb = static_cast(blendOp) - 1; + m_base_state_bits.loadBits.structured.srcBlendRgb = static_cast(srcFunc) - 1; + m_base_state_bits.loadBits.structured.dstBlendRgb = static_cast(destFunc) - 1; + } + + 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.structured.blendOpAlpha = static_cast(blendOp) - 1; + m_base_state_bits.loadBits.structured.srcBlendAlpha = static_cast(srcFunc) - 1; + m_base_state_bits.loadBits.structured.dstBlendAlpha = static_cast(destFunc) - 1; + } + + 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) + { + throw GdtReadingException("Invalid ColorWrite values: values for rgb must match"); + } + + m_base_state_bits.loadBits.structured.colorWriteRgb = 0; + m_base_state_bits.loadBits.structured.colorWriteAlpha = 0; + if (colorWriteRed == StateBitsEnabledStatus_e::ENABLED) + m_base_state_bits.loadBits.structured.colorWriteRgb = 1; + if (colorWriteAlpha == StateBitsEnabledStatus_e::ENABLED) + m_base_state_bits.loadBits.structured.colorWriteAlpha = 1; + } + + void SetCullFace(const CullFace_e cullFace) + { + if (cullFace == CullFace_e::UNKNOWN) + { + std::ostringstream ss; + ss << "Unknown cullFace values: \"\""; + throw GdtReadingException(ss.str()); + } + + if (cullFace == CullFace_e::FRONT) + { + m_base_state_bits.loadBits.structured.cullFace = GFXS_CULL_FRONT; + } + else if (cullFace == CullFace_e::BACK) + { + m_base_state_bits.loadBits.structured.cullFace = GFXS_CULL_BACK; + } + else + { + assert(cullFace == CullFace_e::NONE); + m_base_state_bits.loadBits.structured.cullFace = GFXS_CULL_NONE; + } + } + + void SetDepthTest(const DepthTest_e depthTest) + { + switch (depthTest) + { + case DepthTest_e::LESS_EQUAL: + m_base_state_bits.loadBits.structured.depthTestDisabled = 0; + m_base_state_bits.loadBits.structured.depthTest = GFXS_DEPTHTEST_LESSEQUAL; + break; + + case DepthTest_e::LESS: + m_base_state_bits.loadBits.structured.depthTestDisabled = 0; + m_base_state_bits.loadBits.structured.depthTest = GFXS_DEPTHTEST_LESS; + break; + + case DepthTest_e::EQUAL: + m_base_state_bits.loadBits.structured.depthTestDisabled = 0; + m_base_state_bits.loadBits.structured.depthTest = GFXS_DEPTHTEST_EQUAL; + break; + + case DepthTest_e::ALWAYS: + m_base_state_bits.loadBits.structured.depthTestDisabled = 0; + m_base_state_bits.loadBits.structured.depthTest = GFXS_DEPTHTEST_ALWAYS; + break; + + case DepthTest_e::DISABLE: + m_base_state_bits.loadBits.structured.depthTestDisabled = 1; + m_base_state_bits.loadBits.structured.depthTest = 0; + break; + + case DepthTest_e::UNKNOWN: + default: + std::ostringstream ss; + ss << "Unknown depthTest values: \"\""; + throw GdtReadingException(ss.str()); + } + } + + void SetDepthWrite(const bool depthWrite) + { + if (depthWrite) + m_base_state_bits.loadBits.structured.depthWrite = 1; + else + m_base_state_bits.loadBits.structured.depthWrite = 0; + } + + void SetGammaWrite(const bool gammaWrite) + { + if (gammaWrite) + m_base_state_bits.loadBits.structured.gammaWrite = 1; + else + m_base_state_bits.loadBits.structured.gammaWrite = 0; + } + + 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.structured.polygonOffset = static_cast(polygonOffset) - 1; + } + + 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.raw[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.raw[1] |= enabledMask; + + m_base_state_bits.loadBits.raw[1] &= ~funcMask; + m_base_state_bits.loadBits.raw[1] |= ((static_cast(stencilFunc) - 1) << funcShift) & funcMask; + + m_base_state_bits.loadBits.raw[1] &= ~opFailMask; + m_base_state_bits.loadBits.raw[1] |= ((static_cast(stencilOpFail) - 1) << opFailShift) & opFailMask; + + m_base_state_bits.loadBits.raw[1] &= ~opZFailMask; + m_base_state_bits.loadBits.raw[1] |= ((static_cast(stencilOpZFail) - 1) << opZFailShift) & opZFailMask; + + m_base_state_bits.loadBits.raw[1] &= ~opPassMask; + m_base_state_bits.loadBits.raw[1] |= ((static_cast(stencilOpPass) - 1) << opPassShift) & opPassMask; + } + + void FinalizeMaterial() const + { + if (!m_textures.empty()) + { + m_material.textureTable = m_memory.Alloc(m_textures.size()); + m_material.textureCount = static_cast(m_textures.size()); + std::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 = m_memory.Alloc(m_constants.size()); + m_material.constantCount = static_cast(m_constants.size()); + std::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 = m_memory.Alloc(m_state_bits.size()); + m_material.stateBitsCount = static_cast(m_state_bits.size()); + std::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; + } + + throw GdtReadingException(std::format("Unknown {} value: \"{}\"", propertyName, value)); + } + + template T ReadEnumProperty(const std::string& propertyName, const char** validValuesArray, const size_t validValuesArraySize) const + { + return static_cast(GetIndexForString(propertyName, ReadStringProperty(propertyName), validValuesArray, validValuesArraySize)); + } + + Material& m_material; + MemoryManager& m_memory; + ISearchPath& m_search_path; + AssetCreationContext& m_context; + AssetRegistration& m_registration; + + techset::TechniqueStateMapCache& m_state_map_cache; + std::unordered_map m_state_bits_per_state_map; + + GfxStateBits m_base_state_bits; + std::vector m_state_bits; + std::vector m_textures; + std::vector m_constants; + + std::unique_ptr m_techset_creator; + }; + + class MaterialLoader final : public AssetCreator + { + public: + MaterialLoader(MemoryManager& memory, ISearchPath& searchPath, IGdtQueryable& gdt) + : m_memory(memory), + m_search_path(m_search_path), + m_gdt(gdt) + { + } + + AssetCreationResult CreateAsset(const std::string& assetName, AssetCreationContext& context) override + { + const auto* entry = m_gdt.GetGdtEntryByGdfAndName(ObjConstants::GDF_FILENAME_MATERIAL, assetName); + if (!entry) + return AssetCreationResult::NoAction(); + + auto* material = m_memory.Alloc(); + material->info.name = m_memory.Dup(assetName.c_str()); + + AssetRegistration registration(assetName, material); + + MaterialGdtLoader loader(*entry, *material, m_memory, m_search_path, context, registration); + + try + { + if (loader.Load()) + return AssetCreationResult::Success(context.AddAsset(std::move(registration))); + } + catch (const SkipMaterialException&) + { + return AssetCreationResult::NoAction(); + } + catch (const GdtReadingException& e) + { + std::cerr << std::format("Error while trying to load material from gdt: {} @ GdtEntry \"{}\"\n", e.what(), entry->m_name); + } + + return AssetCreationResult::Failure(); + } + + private: + MemoryManager& m_memory; + ISearchPath& m_search_path; + IGdtQueryable& m_gdt; + }; +} // namespace + +namespace IW4 +{ + std::unique_ptr> CreateMaterialCompiler(MemoryManager& memory, ISearchPath& searchPath, IGdtQueryable& gdt) + { + return std::make_unique(memory, searchPath, gdt); + } +} // namespace IW4 diff --git a/src/ObjCompiling/Game/IW4/Material/CompilerMaterialIW4.h b/src/ObjCompiling/Game/IW4/Material/CompilerMaterialIW4.h new file mode 100644 index 00000000..52c7d3dd --- /dev/null +++ b/src/ObjCompiling/Game/IW4/Material/CompilerMaterialIW4.h @@ -0,0 +1,12 @@ +#pragma once + +#include "Asset/IAssetCreator.h" +#include "Game/IW4/IW4.h" +#include "Gdt/IGdtQueryable.h" +#include "SearchPath/ISearchPath.h" +#include "Utils/MemoryManager.h" + +namespace IW4 +{ + std::unique_ptr> CreateMaterialCompiler(MemoryManager& memory, ISearchPath& searchPath, IGdtQueryable& gdt); +} // namespace IW4 diff --git a/src/ObjCompiling/Game/IW4/ObjCompilerIW4.cpp b/src/ObjCompiling/Game/IW4/ObjCompilerIW4.cpp index 9990d794..f33dd566 100644 --- a/src/ObjCompiling/Game/IW4/ObjCompilerIW4.cpp +++ b/src/ObjCompiling/Game/IW4/ObjCompilerIW4.cpp @@ -2,6 +2,9 @@ #include "Game/IW4/IW4.h" #include "Image/ImageIwdPostProcessor.h" +#include "Material/CompilerMaterialIW4.h" +#include "Techset/CompilerTechsetIW4.h" +#include "Techset/CompilerVertexDeclIW4.h" #include @@ -9,11 +12,15 @@ using namespace IW4; namespace { - void ConfigureCompilers(AssetCreatorCollection& collection, Zone& zone, ISearchPath& searchPath) + void ConfigureCompilers(AssetCreatorCollection& collection, Zone& zone, ISearchPath& searchPath, IGdtQueryable& gdt) { auto& memory = zone.Memory(); - // No compilers yet +#ifdef EXPERIMENTAL_MATERIAL_COMPILATION + collection.AddAssetCreator(CreateMaterialCompiler(memory, searchPath, gdt)); + collection.AddAssetCreator(CreateTechsetLoader(memory, searchPath)); +#endif + collection.AddAssetCreator(CreateVertexDeclLoader(memory)); } void ConfigurePostProcessors(AssetCreatorCollection& collection, @@ -39,5 +46,6 @@ void ObjCompiler::ConfigureCreatorCollection(AssetCreatorCollection& collection, IOutputPath& outDir, IOutputPath& cacheDir) const { + ConfigureCompilers(collection, zone, searchPath, gdt); ConfigurePostProcessors(collection, zone, zoneDefinition, searchPath, zoneStates, outDir); } diff --git a/src/ObjLoading/Game/IW4/Techset/LoaderTechsetIW4.cpp b/src/ObjCompiling/Game/IW4/Techset/CompilerTechsetIW4.cpp similarity index 99% rename from src/ObjLoading/Game/IW4/Techset/LoaderTechsetIW4.cpp rename to src/ObjCompiling/Game/IW4/Techset/CompilerTechsetIW4.cpp index 97c1f76a..bb53a3d5 100644 --- a/src/ObjLoading/Game/IW4/Techset/LoaderTechsetIW4.cpp +++ b/src/ObjCompiling/Game/IW4/Techset/CompilerTechsetIW4.cpp @@ -1,4 +1,4 @@ -#include "LoaderTechsetIW4.h" +#include "CompilerTechsetIW4.h" #include "Game/IW4/IW4.h" #include "Game/IW4/Shader/LoaderPixelShaderIW4.h" diff --git a/src/ObjLoading/Game/IW4/Techset/LoaderTechsetIW4.h b/src/ObjCompiling/Game/IW4/Techset/CompilerTechsetIW4.h similarity index 100% rename from src/ObjLoading/Game/IW4/Techset/LoaderTechsetIW4.h rename to src/ObjCompiling/Game/IW4/Techset/CompilerTechsetIW4.h diff --git a/src/ObjLoading/Game/IW4/Techset/LoaderVertexDeclIW4.cpp b/src/ObjCompiling/Game/IW4/Techset/CompilerVertexDeclIW4.cpp similarity index 99% rename from src/ObjLoading/Game/IW4/Techset/LoaderVertexDeclIW4.cpp rename to src/ObjCompiling/Game/IW4/Techset/CompilerVertexDeclIW4.cpp index 64071166..391975ee 100644 --- a/src/ObjLoading/Game/IW4/Techset/LoaderVertexDeclIW4.cpp +++ b/src/ObjCompiling/Game/IW4/Techset/CompilerVertexDeclIW4.cpp @@ -1,4 +1,4 @@ -#include "LoaderVertexDeclIW4.h" +#include "CompilerVertexDeclIW4.h" #include "Game/IW4/IW4.h" #include "Game/IW4/TechsetConstantsIW4.h" diff --git a/src/ObjLoading/Game/IW4/Techset/LoaderVertexDeclIW4.h b/src/ObjCompiling/Game/IW4/Techset/CompilerVertexDeclIW4.h similarity index 100% rename from src/ObjLoading/Game/IW4/Techset/LoaderVertexDeclIW4.h rename to src/ObjCompiling/Game/IW4/Techset/CompilerVertexDeclIW4.h diff --git a/src/ObjLoading.lua b/src/ObjLoading.lua index 70e945df..9325ca88 100644 --- a/src/ObjLoading.lua +++ b/src/ObjLoading.lua @@ -52,6 +52,7 @@ function ObjLoading:project() } } + ObjCommon:use() useSourceTemplating("ObjLoading") self:include(includes) diff --git a/src/ObjLoading/Asset/IAssetCreator.h b/src/ObjLoading/Asset/IAssetCreator.h index 8d0bc25d..33153df6 100644 --- a/src/ObjLoading/Asset/IAssetCreator.h +++ b/src/ObjLoading/Asset/IAssetCreator.h @@ -25,7 +25,7 @@ public: [[nodiscard]] virtual std::optional GetHandlingAssetType() const = 0; virtual AssetCreationResult CreateAsset(const std::string& assetName, AssetCreationContext& context) = 0; - virtual void FinalizeZone(AssetCreationContext& context){}; + virtual void FinalizeZone(AssetCreationContext& context) {}; }; template class AssetCreator : public IAssetCreator diff --git a/src/ObjLoading/Asset/IAssetPostProcessor.h b/src/ObjLoading/Asset/IAssetPostProcessor.h index afc64527..b5bef0de 100644 --- a/src/ObjLoading/Asset/IAssetPostProcessor.h +++ b/src/ObjLoading/Asset/IAssetPostProcessor.h @@ -25,5 +25,5 @@ public: [[nodiscard]] virtual asset_type_t GetHandlingAssetType() const = 0; virtual void PostProcessAsset(XAssetInfoGeneric& assetInfo, AssetCreationContext& context) = 0; - virtual void FinalizeZone(AssetCreationContext& context){}; + virtual void FinalizeZone(AssetCreationContext& context) {}; }; diff --git a/src/ObjLoading/Game/IW4/Material/LoaderMaterialIW4.cpp b/src/ObjLoading/Game/IW4/Material/LoaderMaterialIW4.cpp index 51363e80..be3bf8b0 100644 --- a/src/ObjLoading/Game/IW4/Material/LoaderMaterialIW4.cpp +++ b/src/ObjLoading/Game/IW4/Material/LoaderMaterialIW4.cpp @@ -1,1373 +1,54 @@ #include "LoaderMaterialIW4.h" -#include "Game/IW4/CommonIW4.h" #include "Game/IW4/IW4.h" -#include "Game/IW4/MaterialConstantsIW4.h" -#include "Game/IW4/ObjConstantsIW4.h" -#include "Game/IW4/Techset/LoaderTechsetIW4.h" -#include "Game/IW4/TechsetConstantsIW4.h" -#include "Gdt/AbstractGdtEntryReader.h" -#include "ObjLoading.h" -#include "Pool/GlobalAssetPool.h" -#include "StateMap/StateMapFromTechniqueExtractor.h" -#include "StateMap/StateMapHandler.h" -#include "Techset/TechniqueFileReader.h" -#include "Techset/TechniqueStateMapCache.h" -#include "Techset/TechsetDefinitionCache.h" +#include "Game/IW4/Material/JsonMaterialLoaderIW4.h" +#include "Material/MaterialCommon.h" -#include -#include #include #include -#include using namespace IW4; namespace { - class SkipMaterialException final : public std::exception - { - }; - - class MaterialGdtLoader : AbstractGdtEntryReader - { - public: - MaterialGdtLoader(const GdtEntry& entry, - Material& material, - MemoryManager& memory, - ISearchPath& searchPath, - AssetCreationContext& context, - AssetRegistration& registration) - : AbstractGdtEntryReader(entry), - m_material(material), - m_memory(memory), - m_search_path(searchPath), - m_context(context), - m_registration(registration), - m_state_map_cache(context.GetZoneAssetCreationState()), - m_base_state_bits{}, - m_techset_creator(CreateTechsetLoader(memory, searchPath)) - { - } - - bool Load() - { - material_template(); - - FinalizeMaterial(); - - return true; - } - - 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 - throw GdtReadingException(std::format("Unknown material type: \"{}\"", materialType)); - } - - 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("tileColor", GdtTileModeNames, std::extent_v); - const auto filterColor = ReadEnumProperty("filterColor", GdtSamplerFilterNames, std::extent_v); - - 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() - { - // TODO - throw SkipMaterialException(); - - 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("tileColor", GdtTileModeNames, std::extent_v); - const auto filterColor = ReadEnumProperty("filterColor", GdtSamplerFilterNames, std::extent_v); - - 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", {distortionScaleX, distortionScaleY, 0, 0}); - - if (uvAnim) - { - const auto uvScrollX = ReadFloatProperty("uvScrollX"); - const auto uvScrollY = ReadFloatProperty("uvScrollY"); - const auto uvScrollRotate = ReadFloatProperty("uvScrollRotate"); - AddConstant("uvAnimParms", {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("tileColor", GdtTileModeNames, std::extent_v); - const auto filterColor = ReadEnumProperty("filterColor", GdtSamplerFilterNames, std::extent_v); - - 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 << std::format("Using particlecloud for \"{}\"\n", m_material.info.name); - } - - 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("tileColor", GdtTileModeNames, std::extent_v); - const auto filterColor = ReadEnumProperty("filterColor", GdtSamplerFilterNames, std::extent_v); - - 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 - throw GdtReadingException(std::format("Unknown custom template: \"{}\"", customTemplate)); - } - - 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("tileColor", GdtTileModeNames, std::extent_v); - const auto filterColor = ReadEnumProperty("filterColor", GdtSamplerFilterNames, std::extent_v); - - 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", {1.0f / zFeatherDepth, zFeatherDepth, 0, 0}); - } - - if (std::fpclassify(eyeOffsetDepth) != FP_ZERO) - AddConstant("eyeOffsetParms", {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(sortKeyIndex)); - foundSortKey = true; - break; - } - } - - if (!foundSortKey) - { - char* endPtr; - const auto sortKeyNum = strtoul(sortKey.c_str(), &endPtr, 10); - - if (endPtr != &sortKey[sortKey.size()]) - throw GdtReadingException(std::format("Invalid sort value: \"{}\"", sortKey)); - - SetSort(static_cast(sortKeyNum)); - } - } - - void clamp_template() {} - - void textureAtlas_template() - { - const auto rowCount = ReadIntegerProperty("textureAtlasRowCount", 1); - const auto columnCount = ReadIntegerProperty("textureAtlasColumnCount", 1); - - SetTextureAtlas(static_cast(rowCount), static_cast(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 - throw GdtReadingException(std::format("Invalid alphatest value: \"{}\"", alphaTest)); - } - - 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_COLOR, CustomBlendFunc_e::ONE); - SetSeparateAlphaBlendFunc(BlendOp_e::DISABLE, CustomBlendFunc_e::ONE, CustomBlendFunc_e::ZERO); - } - else if (blendFunc == GDT_BLEND_FUNC_CUSTOM) - { - const auto customBlendOpRgb = ReadEnumProperty("customBlendOpRgb", GdtBlendOpNames, std::extent_v); - const auto srcCustomBlendFunc = - ReadEnumProperty("srcCustomBlendFunc", GdtCustomBlendFuncNames, std::extent_v); - const auto destCustomBlendFunc = - ReadEnumProperty("destCustomBlendFunc", GdtCustomBlendFuncNames, std::extent_v); - const auto customBlendOpAlpha = ReadEnumProperty("customBlendOpAlpha", GdtBlendOpNames, std::extent_v); - const auto srcCustomBlendFuncAlpha = - ReadEnumProperty("srcCustomBlendFuncAlpha", GdtCustomBlendFuncNames, std::extent_v); - const auto destCustomBlendFuncAlpha = - ReadEnumProperty("destCustomBlendFuncAlpha", GdtCustomBlendFuncNames, std::extent_v); - - SetBlendFunc(customBlendOpRgb, srcCustomBlendFunc, destCustomBlendFunc); - SetSeparateAlphaBlendFunc(customBlendOpAlpha, srcCustomBlendFuncAlpha, destCustomBlendFuncAlpha); - } - else - { - throw GdtReadingException(std::format("Invalid blendfunc value: \"{}\"", blendFunc)); - } - } - - void colorwrite_template() - { - const auto colorWriteRed = ReadEnumProperty( - "colorWriteRed", GdtStateBitsEnabledStatusNames, std::extent_v); - const auto colorWriteGreen = ReadEnumProperty( - "colorWriteGreen", GdtStateBitsEnabledStatusNames, std::extent_v); - const auto colorWriteBlue = ReadEnumProperty( - "colorWriteBlue", GdtStateBitsEnabledStatusNames, std::extent_v); - const auto colorWriteAlpha = ReadEnumProperty( - "colorWriteAlpha", GdtStateBitsEnabledStatusNames, std::extent_v); - - SetColorWrite(colorWriteRed, colorWriteGreen, colorWriteBlue, colorWriteAlpha); - } - - void cullface_template() - { - const auto cullFace = ReadEnumProperty("cullFace", GdtCullFaceNames, std::extent_v); - - SetCullFace(cullFace); - } - - void depthtest_template() - { - const auto depthTest = ReadEnumProperty("depthTest", GdtDepthTestNames, std::extent_v); - - SetDepthTest(depthTest); - } - - void depthwrite_template() - { - const auto depthWrite = - ReadEnumProperty("depthWrite", GdtStateBitsOnOffStatusNames, std::extent_v); - 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 - { - throw GdtReadingException(std::format("Invalid depthWrite blendFunc value: \"{}\"", blendFunc)); - } - } - - void gammawrite_template() - { - const auto gammaWrite = - ReadEnumProperty("gammaWrite", GdtStateBitsOnOffStatusNames, std::extent_v); - - 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", GdtPolygonOffsetNames, std::extent_v); - - SetPolygonOffset(polygonOffset); - } - - void stencil_template() - { - const auto stencilMode = ReadEnumProperty("stencil", GdtStencilModeNames, std::extent_v); - - if (stencilMode == StencilMode_e::DISABLED) - { - DisableStencil(StencilIndex::FRONT); - DisableStencil(StencilIndex::BACK); - } - else - { - if (stencilMode == StencilMode_e::TWO_SIDED) - { - const auto stencilBackFunc = - ReadEnumProperty("stencilFunc2", GdtStencilFuncNames, std::extent_v); - const auto stencilBackOpFail = - ReadEnumProperty("stencilOpFail2", GdtStencilOpNames, std::extent_v); - const auto stencilBackOpZFail = - ReadEnumProperty("stencilOpZFail2", GdtStencilOpNames, std::extent_v); - const auto stencilBackOpPass = - ReadEnumProperty("stencilOpPass2", GdtStencilOpNames, std::extent_v); - - EnableStencil(StencilIndex::BACK, stencilBackFunc, stencilBackOpFail, stencilBackOpZFail, stencilBackOpPass); - } - - const auto stencilFrontFunc = - ReadEnumProperty("stencilFunc1", GdtStencilFuncNames, std::extent_v); - const auto stencilFrontOpFail = ReadEnumProperty("stencilOpFail1", GdtStencilOpNames, std::extent_v); - const auto stencilFrontOpZFail = - ReadEnumProperty("stencilOpZFail1", GdtStencilOpNames, std::extent_v); - const auto stencilFrontOpPass = ReadEnumProperty("stencilOpPass1", GdtStencilOpNames, std::extent_v); - - EnableStencil(StencilIndex::FRONT, stencilFrontFunc, stencilFrontOpFail, stencilFrontOpZFail, stencilFrontOpPass); - } - } - - void SetTechniqueSet(const std::string& techsetName) - { - auto* techset = m_context.LoadDependency(techsetName); - - if (techset == nullptr) - throw GdtReadingException(std::format("Could not load techset: \"{}\"", techsetName)); - - m_registration.AddDependency(techset); - m_material.techniqueSet = techset->Asset(); - - auto& definitionCache = m_context.GetZoneAssetCreationState(); - - bool failure = false; - const auto* techsetDefinition = m_techset_creator->LoadTechsetDefinition(techsetName, m_context, failure); - if (techsetDefinition == nullptr) - { - throw GdtReadingException(std::format("Could not find techset definition for: \"{}\"", techsetName)); - } - - 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::ranges::find_if(m_state_bits, - [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(foundStateBits - m_state_bits.begin()); - } - else - { - m_material.stateBitsEntry[i] = static_cast(m_state_bits.size()); - m_state_bits.push_back(stateBitsForTechnique); - } - } - else - { - m_material.stateBitsEntry[i] = std::numeric_limits::max(); - } - } - } - - GfxStateBits GetStateBitsForTechnique(const std::string& techniqueName) - { - const auto* stateMap = GetStateMapForTechnique(techniqueName); - if (!stateMap) - return m_base_state_bits; - - const auto preCalculatedStateBits = m_state_bits_per_state_map.find(stateMap); - if (preCalculatedStateBits != m_state_bits_per_state_map.end()) - return preCalculatedStateBits->second; - - const auto stateBits = CalculateStateBitsWithStateMap(stateMap); - m_state_bits_per_state_map.emplace(stateMap, stateBits); - - return stateBits; - } - - _NODISCARD const state_map::StateMapDefinition* GetStateMapForTechnique(const std::string& techniqueName) const - { - const auto* preloadedStateMap = m_state_map_cache.GetStateMapForTechnique(techniqueName); - if (preloadedStateMap) - return preloadedStateMap; - - const auto techniqueFileName = GetTechniqueFileName(techniqueName); - const auto file = m_search_path.Open(techniqueFileName); - if (!file.IsOpen()) - return nullptr; - - state_map::StateMapFromTechniqueExtractor extractor; - const techset::TechniqueFileReader reader(*file.m_stream, techniqueFileName, &extractor); - if (!reader.ReadTechniqueDefinition()) - { - m_state_map_cache.SetTechniqueUsesStateMap(techniqueName, nullptr); - return nullptr; - } - - const auto stateMapName = extractor.RetrieveStateMap(); - const auto* loadedStateMap = m_techset_creator->LoadStateMapDefinition(stateMapName, m_context); - m_state_map_cache.SetTechniqueUsesStateMap(techniqueName, loadedStateMap); - - return loadedStateMap; - } - - GfxStateBits CalculateStateBitsWithStateMap(const state_map::StateMapDefinition* stateMap) const - { - const state_map::StateMapHandler stateMapHandler(stateMapLayout, *stateMap); - - GfxStateBits outBits{}; - stateMapHandler.ApplyStateMap(m_base_state_bits.loadBits, outBits.loadBits); - - return outBits; - } - - 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; - } - } - - 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(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 = m_context.LoadDependency(textureName); - - if (image == nullptr) - throw GdtReadingException(std::format("Could not load image: \"{}\"", textureName)); - - m_registration.AddDependency(image); - textureDef.u.image = image->Asset(); - - m_textures.push_back(textureDef); - } - - void AddConstant(const std::string& constantName, const GdtVec4& literalData) - { - MaterialConstantDef constantDef{}; - constantDef.literal[0] = literalData.x; - constantDef.literal[1] = literalData.y; - constantDef.literal[2] = literalData.z; - constantDef.literal[3] = literalData.w; - strncpy(constantDef.name, constantName.c_str(), std::extent_v); - 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: - throw GdtReadingException(std::format("Unknown alphatest value: \"{}\"", static_cast(alphaTest))); - } - } - - 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(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(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(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(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(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(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) - { - throw GdtReadingException("Invalid ColorWrite values: values for rgb must match"); - } - - 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(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(stencilFunc) - 1) << funcShift) & funcMask; - - m_base_state_bits.loadBits[1] &= ~opFailMask; - m_base_state_bits.loadBits[1] |= ((static_cast(stencilOpFail) - 1) << opFailShift) & opFailMask; - - m_base_state_bits.loadBits[1] &= ~opZFailMask; - m_base_state_bits.loadBits[1] |= ((static_cast(stencilOpZFail) - 1) << opZFailShift) & opZFailMask; - - m_base_state_bits.loadBits[1] &= ~opPassMask; - m_base_state_bits.loadBits[1] |= ((static_cast(stencilOpPass) - 1) << opPassShift) & opPassMask; - } - - void FinalizeMaterial() const - { - if (!m_textures.empty()) - { - m_material.textureTable = m_memory.Alloc(m_textures.size()); - m_material.textureCount = static_cast(m_textures.size()); - std::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 = m_memory.Alloc(m_constants.size()); - m_material.constantCount = static_cast(m_constants.size()); - std::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 = m_memory.Alloc(m_state_bits.size()); - m_material.stateBitsCount = static_cast(m_state_bits.size()); - std::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; - } - - throw GdtReadingException(std::format("Unknown {} value: \"{}\"", propertyName, value)); - } - - template T ReadEnumProperty(const std::string& propertyName, const char** validValuesArray, const size_t validValuesArraySize) const - { - return static_cast(GetIndexForString(propertyName, ReadStringProperty(propertyName), validValuesArray, validValuesArraySize)); - } - - Material& m_material; - MemoryManager& m_memory; - ISearchPath& m_search_path; - AssetCreationContext& m_context; - AssetRegistration& m_registration; - - techset::TechniqueStateMapCache& m_state_map_cache; - std::unordered_map m_state_bits_per_state_map; - - GfxStateBits m_base_state_bits; - std::vector m_state_bits; - std::vector m_textures; - std::vector m_constants; - - std::unique_ptr m_techset_creator; - }; - class MaterialLoader final : public AssetCreator { public: - MaterialLoader(MemoryManager& memory, ISearchPath& searchPath, IGdtQueryable& gdt) + MaterialLoader(MemoryManager& memory, ISearchPath& searchPath) : m_memory(memory), - m_search_path(m_search_path), - m_gdt(gdt) + m_search_path(searchPath) { } AssetCreationResult CreateAsset(const std::string& assetName, AssetCreationContext& context) override { - const auto* entry = m_gdt.GetGdtEntryByGdfAndName(ObjConstants::GDF_FILENAME_MATERIAL, assetName); - if (!entry) + const auto file = m_search_path.Open(material::GetFileNameForAssetName(assetName)); + if (!file.IsOpen()) return AssetCreationResult::NoAction(); auto* material = m_memory.Alloc(); material->info.name = m_memory.Dup(assetName.c_str()); AssetRegistration registration(assetName, material); - - MaterialGdtLoader loader(*entry, *material, m_memory, m_search_path, context, registration); - - try + if (!LoadMaterialAsJson(*file.m_stream, *material, m_memory, context, registration)) { - if (loader.Load()) - return AssetCreationResult::Success(context.AddAsset(std::move(registration))); - } - catch (const SkipMaterialException&) - { - return AssetCreationResult::NoAction(); - } - catch (const GdtReadingException& e) - { - std::cerr << std::format("Error while trying to load material from gdt: {} @ GdtEntry \"{}\"\n", e.what(), entry->m_name); + std::cerr << std::format("Failed to load material \"{}\"\n", assetName); + return AssetCreationResult::Failure(); } - return AssetCreationResult::Failure(); + return AssetCreationResult::Success(context.AddAsset(std::move(registration))); } private: MemoryManager& m_memory; ISearchPath& m_search_path; - IGdtQueryable& m_gdt; }; } // namespace namespace IW4 { - std::unique_ptr> CreateMaterialLoader(MemoryManager& memory, ISearchPath& searchPath, IGdtQueryable& gdt) + std::unique_ptr> CreateMaterialLoader(MemoryManager& memory, ISearchPath& searchPath) { - return std::make_unique(memory, searchPath, gdt); + return std::make_unique(memory, searchPath); } } // namespace IW4 diff --git a/src/ObjLoading/Game/IW4/Material/LoaderMaterialIW4.h b/src/ObjLoading/Game/IW4/Material/LoaderMaterialIW4.h index 04b975e0..efd0a610 100644 --- a/src/ObjLoading/Game/IW4/Material/LoaderMaterialIW4.h +++ b/src/ObjLoading/Game/IW4/Material/LoaderMaterialIW4.h @@ -8,5 +8,5 @@ namespace IW4 { - std::unique_ptr> CreateMaterialLoader(MemoryManager& memory, ISearchPath& searchPath, IGdtQueryable& gdt); + std::unique_ptr> CreateMaterialLoader(MemoryManager& memory, ISearchPath& searchPath); } // namespace IW4 diff --git a/src/ObjLoading/Game/IW4/ObjLoaderIW4.cpp b/src/ObjLoading/Game/IW4/ObjLoaderIW4.cpp index c124a7f9..021cc4b3 100644 --- a/src/ObjLoading/Game/IW4/ObjLoaderIW4.cpp +++ b/src/ObjLoading/Game/IW4/ObjLoaderIW4.cpp @@ -17,8 +17,6 @@ #include "Sound/LoaderSoundCurveIW4.h" #include "StringTable/LoaderStringTableIW4.h" #include "StructuredDataDef/LoaderStructuredDataDefIW4.h" -#include "Techset/LoaderTechsetIW4.h" -#include "Techset/LoaderVertexDeclIW4.h" #include "Weapon/GdtLoaderWeaponIW4.h" #include "Weapon/RawLoaderWeaponIW4.h" @@ -126,11 +124,10 @@ namespace // collection.AddAssetCreator(std::make_unique(memory)); // collection.AddAssetCreator(std::make_unique(memory)); // collection.AddAssetCreator(std::make_unique(memory)); - collection.AddAssetCreator(CreateMaterialLoader(memory, searchPath, gdt)); + collection.AddAssetCreator(CreateMaterialLoader(memory, searchPath)); collection.AddAssetCreator(CreatePixelShaderLoader(memory, searchPath)); collection.AddAssetCreator(CreateVertexShaderLoader(memory, searchPath)); - collection.AddAssetCreator(CreateVertexDeclLoader(memory)); - collection.AddAssetCreator(CreateTechsetLoader(memory, searchPath)); + // collection.AddAssetCreator(std::make_unique(memory)); // collection.AddAssetCreator(std::make_unique(memory)); // collection.AddAssetCreator(std::make_unique(memory)); collection.AddAssetCreator(CreateSoundCurveLoader(memory, searchPath)); diff --git a/src/ObjLoading/Game/IW5/Material/LoaderMaterialIW5.cpp b/src/ObjLoading/Game/IW5/Material/LoaderMaterialIW5.cpp index 96f5af0e..189e368f 100644 --- a/src/ObjLoading/Game/IW5/Material/LoaderMaterialIW5.cpp +++ b/src/ObjLoading/Game/IW5/Material/LoaderMaterialIW5.cpp @@ -1,10 +1,9 @@ #include "LoaderMaterialIW5.h" #include "Game/IW5/IW5.h" -#include "JsonMaterialLoader.h" +#include "Game/IW5/Material/JsonMaterialLoaderIW5.h" +#include "Material/MaterialCommon.h" -#include -#include #include #include @@ -23,7 +22,7 @@ namespace AssetCreationResult CreateAsset(const std::string& assetName, AssetCreationContext& context) override { - const auto file = m_search_path.Open(GetFileNameForAsset(assetName)); + const auto file = m_search_path.Open(material::GetFileNameForAssetName(assetName)); if (!file.IsOpen()) return AssetCreationResult::NoAction(); @@ -41,21 +40,6 @@ namespace } private: - std::string GetFileNameForAsset(const std::string& assetName) - { - std::string sanitizedFileName(assetName); - if (sanitizedFileName[0] == '*') - { - std::ranges::replace(sanitizedFileName, '*', '_'); - const auto parenthesisPos = sanitizedFileName.find('('); - if (parenthesisPos != std::string::npos) - sanitizedFileName.erase(parenthesisPos); - sanitizedFileName = std::format("generated/{}", sanitizedFileName); - } - - return std::format("materials/{}.json", sanitizedFileName); - } - MemoryManager& m_memory; ISearchPath& m_search_path; }; diff --git a/src/ObjLoading/Game/T6/Material/JsonMaterialLoader.cpp b/src/ObjLoading/Game/T6/Material/JsonMaterialLoader.cpp deleted file mode 100644 index aa3be869..00000000 --- a/src/ObjLoading/Game/T6/Material/JsonMaterialLoader.cpp +++ /dev/null @@ -1,382 +0,0 @@ -#include "JsonMaterialLoader.h" - -#include "Game/T6/CommonT6.h" -#include "Game/T6/Json/JsonMaterial.h" - -#include -#include -#include - -using namespace nlohmann; -using namespace T6; - -namespace -{ - class JsonLoader - { - public: - JsonLoader(std::istream& stream, MemoryManager& memory, AssetCreationContext& context, AssetRegistration& registration) - : m_stream(stream), - m_memory(memory), - m_context(context), - m_registration(registration) - - { - } - - bool Load(Material& material) 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 != "material" || version != 1u) - { - std::cerr << std::format("Tried to load material \"{}\" but did not find expected type material of version 1\n", material.info.name); - return false; - } - - try - { - const auto jMaterial = jRoot.get(); - return CreateMaterialFromJson(jMaterial, material); - } - catch (const json::exception& e) - { - std::cerr << std::format("Failed to parse json of material: {}\n", e.what()); - } - - return false; - } - - private: - static void PrintError(const Material& material, const std::string& message) - { - std::cerr << std::format("Cannot load material \"{}\": {}\n", material.info.name, message); - } - - static bool CreateGameFlagsFromJson(const JsonMaterial& jMaterial, unsigned& gameFlags) - { - for (const auto gameFlag : jMaterial.gameFlags) - gameFlags |= gameFlag; - - return true; - } - - bool CreateTextureDefFromJson(const JsonTexture& jTexture, MaterialTextureDef& textureDef, const Material& material) const - { - if (jTexture.name) - { - if (jTexture.name->empty()) - { - PrintError(material, "textureDef name cannot be empty"); - return false; - } - - textureDef.nameStart = jTexture.name.value()[0]; - textureDef.nameEnd = jTexture.name.value()[jTexture.name->size() - 1]; - textureDef.nameHash = Common::R_HashString(jTexture.name.value().c_str(), 0); - } - else - { - if (!jTexture.nameStart || !jTexture.nameEnd || !jTexture.nameHash) - { - PrintError(material, "textureDefs without name must have nameStart, nameEnd and nameHash"); - return false; - } - - if (jTexture.nameStart->size() != 1 || jTexture.nameEnd->size() != 1) - { - PrintError(material, "nameStart and nameEnd must be a string of exactly one character"); - return false; - } - - textureDef.nameStart = jTexture.nameStart.value()[0]; - textureDef.nameEnd = jTexture.nameEnd.value()[0]; - textureDef.nameHash = jTexture.nameHash.value(); - } - - textureDef.samplerState.filter = jTexture.samplerState.filter; - textureDef.samplerState.mipMap = jTexture.samplerState.mipMap; - textureDef.samplerState.clampU = jTexture.samplerState.clampU; - textureDef.samplerState.clampV = jTexture.samplerState.clampV; - textureDef.samplerState.clampW = jTexture.samplerState.clampW; - - textureDef.semantic = jTexture.semantic; - textureDef.isMatureContent = jTexture.isMatureContent; - - auto* image = m_context.LoadDependency(jTexture.image); - if (!image) - { - PrintError(material, std::format("Could not find textureDef image: {}", jTexture.image)); - return false; - } - m_registration.AddDependency(image); - textureDef.image = image->Asset(); - - return true; - } - - static bool CreateConstantDefFromJson(const JsonConstant& jConstant, MaterialConstantDef& constantDef, const Material& material) - { - if (jConstant.name) - { - const auto copyCount = std::min(jConstant.name->size() + 1, std::extent_v); - strncpy(constantDef.name, jConstant.name->c_str(), copyCount); - if (copyCount < std::extent_v) - memset(&constantDef.name[copyCount], 0, std::extent_v - copyCount); - constantDef.nameHash = Common::R_HashString(jConstant.name->c_str(), 0); - } - else - { - if (!jConstant.nameFragment || !jConstant.nameHash) - { - PrintError(material, "constantDefs without name must have nameFragment and nameHash"); - return false; - } - - const auto copyCount = std::min(jConstant.nameFragment->size() + 1, std::extent_v); - strncpy(constantDef.name, jConstant.nameFragment->c_str(), copyCount); - if (copyCount < std::extent_v) - memset(&constantDef.name[copyCount], 0, std::extent_v - copyCount); - constantDef.nameHash = jConstant.nameHash.value(); - } - - if (jConstant.literal.size() != 4) - { - PrintError(material, "constantDef literal must be array of size 4"); - return false; - } - - constantDef.literal.x = jConstant.literal[0]; - constantDef.literal.y = jConstant.literal[1]; - constantDef.literal.z = jConstant.literal[2]; - constantDef.literal.w = jConstant.literal[3]; - - return true; - } - - static bool - CreateStateBitsTableEntryFromJson(const JsonStateBitsTableEntry& jStateBitsTableEntry, GfxStateBits& stateBitsTableEntry, const Material& material) - { - auto& structured = stateBitsTableEntry.loadBits.structured; - - structured.srcBlendRgb = jStateBitsTableEntry.srcBlendRgb; - structured.dstBlendRgb = jStateBitsTableEntry.dstBlendRgb; - structured.blendOpRgb = jStateBitsTableEntry.blendOpRgb; - - if (jStateBitsTableEntry.alphaTest == JsonAlphaTest::DISABLED) - { - structured.alphaTestDisabled = 1; - structured.alphaTest = 0; - } - else if (jStateBitsTableEntry.alphaTest == JsonAlphaTest::GT0) - { - structured.alphaTestDisabled = 0; - structured.alphaTest = GFXS_ALPHA_TEST_GT_0; - } - else if (jStateBitsTableEntry.alphaTest == JsonAlphaTest::GE128) - { - structured.alphaTestDisabled = 0; - structured.alphaTest = GFXS_ALPHA_TEST_GE_128; - } - else - { - PrintError(material, "Invalid value for alphaTest"); - return false; - } - - if (jStateBitsTableEntry.cullFace == JsonCullFace::NONE) - structured.cullFace = GFXS0_CULL_NONE; - else if (jStateBitsTableEntry.cullFace == JsonCullFace::BACK) - structured.cullFace = GFXS0_CULL_BACK; - else if (jStateBitsTableEntry.cullFace == JsonCullFace::FRONT) - structured.cullFace = GFXS0_CULL_FRONT; - else - { - PrintError(material, "Invalid value for cull face"); - return false; - } - - structured.srcBlendAlpha = jStateBitsTableEntry.srcBlendAlpha; - structured.dstBlendAlpha = jStateBitsTableEntry.dstBlendAlpha; - structured.blendOpAlpha = jStateBitsTableEntry.blendOpAlpha; - structured.colorWriteRgb = jStateBitsTableEntry.colorWriteRgb; - structured.colorWriteAlpha = jStateBitsTableEntry.colorWriteAlpha; - structured.polymodeLine = jStateBitsTableEntry.polymodeLine; - structured.depthWrite = jStateBitsTableEntry.depthWrite; - - if (jStateBitsTableEntry.depthTest == JsonDepthTest::DISABLED) - structured.depthTestDisabled = 1; - else if (jStateBitsTableEntry.depthTest == JsonDepthTest::ALWAYS) - structured.depthTest = GFXS_DEPTHTEST_ALWAYS; - else if (jStateBitsTableEntry.depthTest == JsonDepthTest::LESS) - structured.depthTest = GFXS_DEPTHTEST_LESS; - else if (jStateBitsTableEntry.depthTest == JsonDepthTest::EQUAL) - structured.depthTest = GFXS_DEPTHTEST_EQUAL; - else if (jStateBitsTableEntry.depthTest == JsonDepthTest::LESS_EQUAL) - structured.depthTest = GFXS_DEPTHTEST_LESSEQUAL; - else - { - PrintError(material, "Invalid value for depth test"); - return false; - } - - structured.polygonOffset = jStateBitsTableEntry.polygonOffset; - - if (jStateBitsTableEntry.stencilFront) - { - structured.stencilFrontEnabled = 1; - structured.stencilFrontPass = jStateBitsTableEntry.stencilFront->pass; - structured.stencilFrontFail = jStateBitsTableEntry.stencilFront->fail; - structured.stencilFrontZFail = jStateBitsTableEntry.stencilFront->zfail; - structured.stencilFrontFunc = jStateBitsTableEntry.stencilFront->func; - } - - if (jStateBitsTableEntry.stencilBack) - { - structured.stencilBackEnabled = 1; - structured.stencilBackPass = jStateBitsTableEntry.stencilBack->pass; - structured.stencilBackFail = jStateBitsTableEntry.stencilBack->fail; - structured.stencilBackZFail = jStateBitsTableEntry.stencilBack->zfail; - structured.stencilBackFunc = jStateBitsTableEntry.stencilBack->func; - } - - return true; - } - - bool CreateMaterialFromJson(const JsonMaterial& jMaterial, Material& material) const - { - if (!CreateGameFlagsFromJson(jMaterial, material.info.gameFlags)) - return false; - - material.info.sortKey = static_cast(jMaterial.sortKey); - - if (jMaterial.textureAtlas) - { - material.info.textureAtlasRowCount = jMaterial.textureAtlas->rows; - material.info.textureAtlasColumnCount = jMaterial.textureAtlas->columns; - } - else - { - material.info.textureAtlasRowCount = 0; - material.info.textureAtlasColumnCount = 0; - } - - material.info.surfaceTypeBits = jMaterial.surfaceTypeBits; - material.info.layeredSurfaceTypes = jMaterial.layeredSurfaceTypes; - material.info.hashIndex = static_cast(jMaterial.hashIndex); - material.info.surfaceFlags = jMaterial.surfaceFlags; - material.info.contents = jMaterial.contents; - - if (jMaterial.stateBitsEntry.size() != std::extent_v) - { - PrintError(material, std::format("StateBitsEntry size is not {}", jMaterial.stateBitsEntry.size())); - return false; - } - for (auto i = 0u; i < std::extent_v; i++) - material.stateBitsEntry[i] = jMaterial.stateBitsEntry[i]; - - material.stateFlags = static_cast(jMaterial.stateFlags); - material.cameraRegion = jMaterial.cameraRegion; - material.probeMipBits = jMaterial.probeMipBits; - - auto* techniqueSet = m_context.LoadDependency(jMaterial.techniqueSet); - if (!techniqueSet) - { - PrintError(material, "Could not find technique set"); - return false; - } - m_registration.AddDependency(techniqueSet); - material.techniqueSet = techniqueSet->Asset(); - - if (!jMaterial.textures.empty()) - { - material.textureCount = static_cast(jMaterial.textures.size()); - material.textureTable = m_memory.Alloc(material.textureCount); - - for (auto i = 0u; i < material.textureCount; i++) - { - if (!CreateTextureDefFromJson(jMaterial.textures[i], material.textureTable[i], material)) - return false; - } - } - else - { - material.textureCount = 0; - material.textureTable = nullptr; - } - - if (!jMaterial.constants.empty()) - { - material.constantCount = static_cast(jMaterial.constants.size()); - material.constantTable = m_memory.Alloc(material.constantCount); - - for (auto i = 0u; i < material.constantCount; i++) - { - if (!CreateConstantDefFromJson(jMaterial.constants[i], material.constantTable[i], material)) - return false; - } - } - else - { - material.constantCount = 0; - material.constantTable = nullptr; - } - - if (!jMaterial.stateBits.empty()) - { - material.stateBitsCount = static_cast(jMaterial.stateBits.size()); - material.stateBitsTable = m_memory.Alloc(material.stateBitsCount); - - for (auto i = 0u; i < material.stateBitsCount; i++) - { - if (!CreateStateBitsTableEntryFromJson(jMaterial.stateBits[i], material.stateBitsTable[i], material)) - return false; - } - } - else - { - material.stateBitsCount = 0; - material.stateBitsTable = nullptr; - } - - if (jMaterial.thermalMaterial) - { - auto* thermalMaterial = m_context.LoadDependency(jMaterial.thermalMaterial.value()); - if (!thermalMaterial) - { - PrintError(material, "Could not find thermal material"); - return false; - } - m_registration.AddDependency(thermalMaterial); - material.thermalMaterial = thermalMaterial->Asset(); - } - else - { - material.thermalMaterial = nullptr; - } - - return true; - } - - std::istream& m_stream; - MemoryManager& m_memory; - AssetCreationContext& m_context; - AssetRegistration& m_registration; - }; -} // namespace - -namespace T6 -{ - bool LoadMaterialAsJson( - std::istream& stream, Material& material, MemoryManager& memory, AssetCreationContext& context, AssetRegistration& registration) - { - const JsonLoader loader(stream, memory, context, registration); - - return loader.Load(material); - } -} // namespace T6 diff --git a/src/ObjLoading/Game/T6/Material/JsonMaterialLoader.h b/src/ObjLoading/Game/T6/Material/JsonMaterialLoader.h deleted file mode 100644 index 0356ed0e..00000000 --- a/src/ObjLoading/Game/T6/Material/JsonMaterialLoader.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include "Asset/AssetCreationContext.h" -#include "Asset/AssetRegistration.h" -#include "Game/T6/T6.h" -#include "Utils/MemoryManager.h" - -#include - -namespace T6 -{ - bool LoadMaterialAsJson( - std::istream& stream, Material& material, MemoryManager& memory, AssetCreationContext& context, AssetRegistration& registration); -} // namespace T6 diff --git a/src/ObjLoading/Game/T6/Material/LoaderMaterialT6.cpp b/src/ObjLoading/Game/T6/Material/LoaderMaterialT6.cpp index 08add739..5458b96e 100644 --- a/src/ObjLoading/Game/T6/Material/LoaderMaterialT6.cpp +++ b/src/ObjLoading/Game/T6/Material/LoaderMaterialT6.cpp @@ -1,10 +1,9 @@ #include "LoaderMaterialT6.h" -#include "Game/T6/Material/JsonMaterialLoader.h" +#include "Game/T6/Material/JsonMaterialLoaderT6.h" #include "Game/T6/T6.h" +#include "Material/MaterialCommon.h" -#include -#include #include #include @@ -23,7 +22,7 @@ namespace AssetCreationResult CreateAsset(const std::string& assetName, AssetCreationContext& context) override { - const auto file = m_search_path.Open(GetFileNameForAsset(assetName)); + const auto file = m_search_path.Open(material::GetFileNameForAssetName(assetName)); if (!file.IsOpen()) return AssetCreationResult::NoAction(); @@ -41,21 +40,6 @@ namespace } private: - static std::string GetFileNameForAsset(const std::string& assetName) - { - std::string sanitizedFileName(assetName); - if (sanitizedFileName[0] == '*') - { - std::ranges::replace(sanitizedFileName, '*', '_'); - const auto parenthesisPos = sanitizedFileName.find('('); - if (parenthesisPos != std::string::npos) - sanitizedFileName.erase(parenthesisPos); - sanitizedFileName = "generated/" + sanitizedFileName; - } - - return std::format("materials/{}.json", sanitizedFileName); - } - MemoryManager& m_memory; ISearchPath& m_search_path; }; diff --git a/src/ObjLoading/Game/IW5/Material/JsonMaterialLoader.cpp b/src/ObjLoading/Material/JsonMaterialLoader.cpp.template similarity index 86% rename from src/ObjLoading/Game/IW5/Material/JsonMaterialLoader.cpp rename to src/ObjLoading/Material/JsonMaterialLoader.cpp.template index 55380c30..65687955 100644 --- a/src/ObjLoading/Game/IW5/Material/JsonMaterialLoader.cpp +++ b/src/ObjLoading/Material/JsonMaterialLoader.cpp.template @@ -1,15 +1,41 @@ -#include "JsonMaterialLoader.h" +#options GAME (IW4, IW5, T6) +#filename "Game/" + GAME + "/Material/JsonMaterialLoader" + GAME + ".cpp" + +#if GAME == "IW4" +#define FEATURE_IW4 +#define HAS_WATER +#define GAME_LOWER "iw4" +#elif GAME == "IW5" +#define FEATURE_IW5 +#define HAS_WATER +#define GAME_LOWER "iw5" +#elif GAME == "T6" +#define FEATURE_T6 +#define GAME_LOWER "t6" +#endif + +// This file was templated. +// See JsonMaterialLoader.cpp.template. +// Do not modify, changes will be lost. + +#set LOADER_HEADER "\"JsonMaterialLoader" + GAME + ".h\"" +#include LOADER_HEADER + +#ifdef HAS_WATER #include "Base64.h" -#include "Game/IW5/CommonIW5.h" -#include "Game/IW5/Material/JsonMaterial.h" +#endif +#set COMMON_HEADER "\"Game/" + GAME + "/Common" + GAME + ".h\"" +#include COMMON_HEADER +#set JSON_HEADER "\"Game/" + GAME + "/Material/JsonMaterial" + GAME + ".h\"" +#include JSON_HEADER #include #include #include using namespace nlohmann; -using namespace IW5; +using namespace GAME; namespace { @@ -27,19 +53,27 @@ namespace bool Load(Material& material) const { const auto jRoot = json::parse(m_stream); - std::string game; std::string type; unsigned version; jRoot.at("_type").get_to(type); - jRoot.at("_game").get_to(game); jRoot.at("_version").get_to(version); - if (type != "material" || version != 1u || game != "iw5") + if (type != "material" || version != 1u) { std::cerr << std::format("Tried to load material \"{}\" but did not find expected type material of version 1\n", material.info.name); return false; } + +#ifndef FEATURE_T6 // T6 did not have this check in version 1, so to stay backwards compatible, let it stay that way + std::string game; + jRoot.at("_game").get_to(game); + if (game != GAME_LOWER) + { + std::cerr << std::format("Tried to load material \"{}\" but \"_game\" did not find expected type value {}\n", material.info.name, GAME_LOWER); + return false; + } +#endif try { @@ -60,7 +94,11 @@ namespace std::cerr << std::format("Cannot load material \"{}\": {}\n", material.info.name, message); } +#if defined(FEATURE_IW4) || defined(FEATURE_IW5) static bool CreateGameFlagsFromJson(const JsonMaterial& jMaterial, unsigned char& gameFlags) +#elif defined(FEATURE_T6) + static bool CreateGameFlagsFromJson(const JsonMaterial& jMaterial, unsigned& gameFlags) +#endif { for (const auto gameFlag : jMaterial.gameFlags) gameFlags |= gameFlag; @@ -77,6 +115,7 @@ namespace samplerState.clampW = jSamplerState.clampW; } +#ifdef HAS_WATER bool CreateWaterFromJson(const JsonWater& jWater, water_t& water, const Material& material) const { water.writable.floatTime = jWater.floatTime; @@ -120,6 +159,7 @@ namespace return true; } +#endif bool CreateTextureDefFromJson(const JsonTexture& jTexture, MaterialTextureDef& textureDef, const Material& material) const { @@ -157,6 +197,9 @@ namespace CreateSamplerStateFromJson(jTexture.samplerState, textureDef.samplerState); textureDef.semantic = jTexture.semantic; +#ifdef FEATURE_T6 + textureDef.isMatureContent = jTexture.isMatureContent; +#endif auto* imageAsset = m_context.LoadDependency(jTexture.image); if (!imageAsset) @@ -166,6 +209,7 @@ namespace } m_registration.AddDependency(imageAsset); +#ifdef HAS_WATER if (jTexture.water) { if (jTexture.semantic != TS_WATER_MAP) @@ -195,6 +239,9 @@ namespace } else textureDef.u.image = imageAsset->Asset(); +#else + textureDef.image = imageAsset->Asset(); +#endif return true; } @@ -257,11 +304,13 @@ namespace structured.alphaTestDisabled = 0; structured.alphaTest = GFXS_ALPHA_TEST_GT_0; } +#if defined(FEATURE_IW4) || defined(FEATURE_IW5) else if (jStateBitsTableEntry.alphaTest == JsonAlphaTest::LT128) { structured.alphaTestDisabled = 0; structured.alphaTest = GFXS_ALPHA_TEST_LT_128; } +#endif else if (jStateBitsTableEntry.alphaTest == JsonAlphaTest::GE128) { structured.alphaTestDisabled = 0; @@ -290,7 +339,9 @@ namespace structured.blendOpAlpha = jStateBitsTableEntry.blendOpAlpha; structured.colorWriteRgb = jStateBitsTableEntry.colorWriteRgb; structured.colorWriteAlpha = jStateBitsTableEntry.colorWriteAlpha; +#if defined(FEATURE_IW4) || defined(FEATURE_IW5) structured.gammaWrite = jStateBitsTableEntry.gammaWrite; +#endif structured.polymodeLine = jStateBitsTableEntry.polymodeLine; structured.depthWrite = jStateBitsTableEntry.depthWrite; @@ -352,6 +403,12 @@ namespace } material.info.surfaceTypeBits = jMaterial.surfaceTypeBits; +#ifdef FEATURE_T6 + material.info.layeredSurfaceTypes = jMaterial.layeredSurfaceTypes; + material.info.hashIndex = static_cast(jMaterial.hashIndex); + material.info.surfaceFlags = jMaterial.surfaceFlags; + material.info.contents = jMaterial.contents; +#endif if (jMaterial.stateBitsEntry.size() != std::extent_v) { @@ -363,6 +420,9 @@ namespace material.stateFlags = static_cast(jMaterial.stateFlags); material.cameraRegion = jMaterial.cameraRegion; +#ifdef FEATURE_T6 + material.probeMipBits = jMaterial.probeMipBits; +#endif auto* techniqueSet = m_context.LoadDependency(jMaterial.techniqueSet); if (!techniqueSet) @@ -424,6 +484,24 @@ namespace material.stateBitsTable = nullptr; } +#ifdef FEATURE_T6 + if (jMaterial.thermalMaterial) + { + auto* thermalMaterial = m_context.LoadDependency(jMaterial.thermalMaterial.value()); + if (!thermalMaterial) + { + PrintError(material, "Could not find thermal material"); + return false; + } + m_registration.AddDependency(thermalMaterial); + material.thermalMaterial = thermalMaterial->Asset(); + } + else + { + material.thermalMaterial = nullptr; + } +#endif + return true; } @@ -434,7 +512,7 @@ namespace }; } // namespace -namespace IW5 +namespace GAME { bool LoadMaterialAsJson( std::istream& stream, Material& material, MemoryManager& memory, AssetCreationContext& context, AssetRegistration& registration) @@ -443,4 +521,4 @@ namespace IW5 return loader.Load(material); } -} // namespace IW5 +} // namespace GAME diff --git a/src/ObjLoading/Game/IW5/Material/JsonMaterialLoader.h b/src/ObjLoading/Material/JsonMaterialLoader.h.template similarity index 50% rename from src/ObjLoading/Game/IW5/Material/JsonMaterialLoader.h rename to src/ObjLoading/Material/JsonMaterialLoader.h.template index 6b2b5b74..ae57b3b6 100644 --- a/src/ObjLoading/Game/IW5/Material/JsonMaterialLoader.h +++ b/src/ObjLoading/Material/JsonMaterialLoader.h.template @@ -1,14 +1,23 @@ +#options GAME (IW4, IW5, T6) + +#filename "Game/" + GAME + "/Material/JsonMaterialLoader" + GAME + ".h" + +// This file was templated. +// See JsonMaterialLoader.h.template. +// Do not modify, changes will be lost. + #pragma once #include "Asset/AssetCreationContext.h" #include "Asset/AssetRegistration.h" -#include "Game/IW5/IW5.h" +#set GAME_HEADER "\"Game/" + GAME + "/" + GAME + ".h\"" +#include GAME_HEADER #include "Utils/MemoryManager.h" #include -namespace IW5 +namespace GAME { bool LoadMaterialAsJson( std::istream& stream, Material& material, MemoryManager& memory, AssetCreationContext& context, AssetRegistration& registration); -} // namespace IW5 +} // namespace GAME diff --git a/src/ObjLoading/XModel/Tangentspace.cpp b/src/ObjLoading/XModel/Tangentspace.cpp index 32662f64..850c806f 100644 --- a/src/ObjLoading/XModel/Tangentspace.cpp +++ b/src/ObjLoading/XModel/Tangentspace.cpp @@ -28,7 +28,7 @@ namespace tangent_space void SetVec3(void* dest, const size_t index, const size_t stride, const tvec3& data) { - auto* out = reinterpret_cast(static_cast(dest) + stride * index); + auto* out = reinterpret_cast(static_cast(dest) + stride * index); (*out)[0] = data[0]; (*out)[1] = data[1]; (*out)[2] = data[2]; diff --git a/src/ObjWriting.lua b/src/ObjWriting.lua index 1f715ade..6fd8361a 100644 --- a/src/ObjWriting.lua +++ b/src/ObjWriting.lua @@ -53,6 +53,7 @@ function ObjWriting:project() } } + ObjCommon:use() useSourceTemplating("ObjWriting") self:include(includes) diff --git a/src/ObjWriting/Game/IW4/Leaderboard/JsonLeaderboardDefWriter.cpp b/src/ObjWriting/Game/IW4/Leaderboard/JsonLeaderboardDefWriter.cpp index 3523c0d6..fe6628bf 100644 --- a/src/ObjWriting/Game/IW4/Leaderboard/JsonLeaderboardDefWriter.cpp +++ b/src/ObjWriting/Game/IW4/Leaderboard/JsonLeaderboardDefWriter.cpp @@ -28,6 +28,7 @@ namespace jRoot["_type"] = "leaderboard"; jRoot["_version"] = 1; + jRoot["_game"] = "iw4"; m_stream << std::setw(4) << jRoot << "\n"; } diff --git a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperMaterial.cpp b/src/ObjWriting/Game/IW4/Material/DecompilingMaterialDumperIW4.cpp similarity index 62% rename from src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperMaterial.cpp rename to src/ObjWriting/Game/IW4/Material/DecompilingMaterialDumperIW4.cpp index d4373617..e6a5443d 100644 --- a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperMaterial.cpp +++ b/src/ObjWriting/Game/IW4/Material/DecompilingMaterialDumperIW4.cpp @@ -1,4 +1,4 @@ -#include "AssetDumperMaterial.h" +#include "DecompilingMaterialDumperIW4.h" #include "Game/IW4/MaterialConstantsIW4.h" #include "Game/IW4/ObjConstantsIW4.h" @@ -9,22 +9,21 @@ #include #pragma warning(pop) +#include +#include #include -#include #include #include #include #include -#define DUMP_AS_JSON 1 // #define DUMP_AS_GDT 1 // #define FLAGS_DEBUG 1 using namespace IW4; -using json = nlohmann::json; using namespace std::string_literals; -namespace IW4 +namespace { const char* AssetName(const char* name) { @@ -33,285 +32,6 @@ namespace IW4 return name; } - template json ArrayEntry(const char* (&a)[S], const size_t index) - { - assert(index < S); - if (index < S) - return a[index]; - - return json{}; - } - - json BuildComplexTableJson(const complex_s* complexTable, const size_t count) - { - auto jArray = json::array(); - - if (complexTable) - { - for (auto index = 0u; index < count; index++) - { - const auto& entry = complexTable[index]; - jArray.emplace_back(json{ - {"real", entry.real}, - {"imag", entry.imag} - }); - } - } - - return jArray; - } - - json BuildWaterJson(water_t* water) - { - if (!water) - return json{}; - - return json{ - {"floatTime", water->writable.floatTime}, - {"H0", BuildComplexTableJson(water->H0, water->M * water->N)}, - {"wTerm", water->wTerm ? json{std::vector(water->wTerm, water->wTerm + (water->M * water->N))} : json::array()}, - {"M", water->M}, - {"N", water->N}, - {"Lx", water->Lx}, - {"Lz", water->Lz}, - {"windvel", water->windvel}, - {"winddir", std::vector(std::begin(water->winddir), std::end(water->winddir))}, - {"amplitude", water->amplitude}, - {"codeConstant", std::vector(std::begin(water->codeConstant), std::end(water->codeConstant))}, - {"image", water->image && water->image->name ? AssetName(water->image->name) : nullptr} - }; - } - - json BuildSamplerStateJson(unsigned char samplerState) - { - static const char* samplerFilterNames[]{"none", "nearest", "linear", "aniso2x", "aniso4x"}; - static const char* samplerMipmapNames[]{"disabled", "nearest", "linear"}; - - return json{ - {"filter", ArrayEntry(samplerFilterNames, (samplerState & SAMPLER_FILTER_MASK) >> SAMPLER_FILTER_SHIFT)}, - {"mipmap", ArrayEntry(samplerMipmapNames, (samplerState & SAMPLER_MIPMAP_MASK) >> SAMPLER_MIPMAP_SHIFT)}, - {"clampU", (samplerState & SAMPLER_CLAMP_U) ? true : false}, - {"clampV", (samplerState & SAMPLER_CLAMP_V) ? true : false}, - {"clampW", (samplerState & SAMPLER_CLAMP_W) ? true : false}, - }; - } - - json BuildTextureTableJson(MaterialTextureDef* textureTable, const size_t count) - { - static const char* semanticNames[]{ - "2d", "function", "colorMap", "detailMap", "unused2", "normalMap", "unused3", "unused4", "specularMap", "unused5", "unused6", "waterMap"}; - - auto jArray = json::array(); - - if (textureTable) - { - for (auto index = 0u; index < count; index++) - { - const auto& entry = textureTable[index]; - - json jEntry = { - {"samplerState", BuildSamplerStateJson(entry.samplerState)}, - {"semantic", ArrayEntry(semanticNames, entry.semantic)} - }; - - const auto knownMaterialSourceName = knownTextureMaps.find(entry.nameHash); - if (knownMaterialSourceName != knownTextureMaps.end()) - { - jEntry["name"] = knownMaterialSourceName->second.m_name; - } - else - { - jEntry.merge_patch({ - {"nameHash", entry.nameHash }, - {"nameStart", entry.nameStart}, - {"nameEnd", entry.nameEnd }, - }); - } - - if (entry.semantic == TS_WATER_MAP) - { - jEntry["water"] = BuildWaterJson(entry.u.water); - } - else - { - jEntry["image"] = entry.u.image && entry.u.image->name ? AssetName(entry.u.image->name) : nullptr; - } - - jArray.emplace_back(std::move(jEntry)); - } - } - - return jArray; - } - - json BuildConstantTableJson(const MaterialConstantDef* constantTable, const size_t count) - { - auto jArray = json::array(); - - if (constantTable) - { - for (auto index = 0u; index < count; index++) - { - const auto& entry = constantTable[index]; - json jEntry = { - {"literal", std::vector(std::begin(entry.literal), std::end(entry.literal))} - }; - - const auto nameLen = strnlen(entry.name, std::extent_v); - if (nameLen == std::extent_v) - { - std::string fullLengthName(entry.name, std::extent_v); - const auto fullLengthHash = Common::R_HashString(fullLengthName.c_str(), 0); - - if (fullLengthHash == entry.nameHash) - { - jEntry["name"] = fullLengthName; - } - else - { - const auto knownMaterialSourceName = knownConstantNames.find(entry.nameHash); - if (knownMaterialSourceName != knownConstantNames.end()) - { - jEntry["name"] = knownMaterialSourceName->second; - } - else - { - jEntry.merge_patch({ - {"nameHash", entry.nameHash}, - {"namePart", fullLengthName} - }); - } - } - } - else - { - jEntry["name"] = std::string(entry.name, nameLen); - } - - jArray.emplace_back(std::move(jEntry)); - } - } - - return jArray; - } - - json BuildStateBitsTableJson(const GfxStateBits* stateBitsTable, const size_t count) - { - static const char* blendNames[]{ - "disabled", - "zero", - "one", - "srcColor", - "invSrcColor", - "srcAlpha", - "invSrcAlpha", - "destAlpha", - "invDestAlpha", - "destColor", - "invDestColor", - }; - static const char* blendOpNames[]{"disabled", "add", "subtract", "revSubtract", "min", "max"}; - static const char* depthTestNames[]{ - "always", - "less", - "equal", - "lessEqual", - }; - static const char* polygonOffsetNames[]{ - "0", - "1", - "2", - "shadowMap", - }; - static const char* stencilOpNames[]{"keep", "zero", "replace", "incrSat", "decrSat", "invert", "incr", "decr"}; - static const char* stencilFuncNames[]{"never", "less", "equal", "lessEqual", "greater", "notEqual", "greaterEqual", "always"}; - - auto jArray = json::array(); - - if (stateBitsTable) - { - for (auto index = 0u; index < count; index++) - { - const auto& entry = stateBitsTable[index]; - - const auto srcBlendRgb = (entry.loadBits[0] & GFXS0_SRCBLEND_RGB_MASK) >> GFXS0_SRCBLEND_RGB_SHIFT; - const auto dstBlendRgb = (entry.loadBits[0] & GFXS0_DSTBLEND_RGB_MASK) >> GFXS0_DSTBLEND_RGB_SHIFT; - const auto blendOpRgb = (entry.loadBits[0] & GFXS0_BLENDOP_RGB_MASK) >> GFXS0_BLENDOP_RGB_SHIFT; - const auto srcBlendAlpha = (entry.loadBits[0] & GFXS0_SRCBLEND_ALPHA_MASK) >> GFXS0_SRCBLEND_ALPHA_SHIFT; - const auto dstBlendAlpha = (entry.loadBits[0] & GFXS0_DSTBLEND_ALPHA_MASK) >> GFXS0_DSTBLEND_ALPHA_SHIFT; - const auto blendOpAlpha = (entry.loadBits[0] & GFXS0_BLENDOP_ALPHA_MASK) >> GFXS0_BLENDOP_ALPHA_SHIFT; - const auto depthTest = (entry.loadBits[1] & GFXS1_DEPTHTEST_MASK) >> GFXS1_DEPTHTEST_SHIFT; - const auto polygonOffset = (entry.loadBits[1] & GFXS1_POLYGON_OFFSET_MASK) >> GFXS1_POLYGON_OFFSET_SHIFT; - - const auto* alphaTest = "disable"; - if ((entry.loadBits[0] & GFXS0_ATEST_MASK) == GFXS0_ATEST_GT_0) - alphaTest = "gt0"; - else if ((entry.loadBits[0] & GFXS0_ATEST_MASK) == GFXS0_ATEST_LT_128) - alphaTest = "lt128"; - else if ((entry.loadBits[0] & GFXS0_ATEST_MASK) == GFXS0_ATEST_GE_128) - alphaTest = "ge128"; - else - assert(entry.loadBits[0] & GFXS0_ATEST_DISABLE); - - const auto* cullFace = "none"; - if ((entry.loadBits[0] & GFXS0_CULL_MASK) == GFXS0_CULL_BACK) - cullFace = "back"; - else if ((entry.loadBits[0] & GFXS0_CULL_MASK) == GFXS0_CULL_FRONT) - cullFace = "front"; - else - assert((entry.loadBits[0] & GFXS0_CULL_MASK) == GFXS0_CULL_NONE); - - jArray.emplace_back(json{ - {"srcBlendRgb", ArrayEntry(blendNames, srcBlendRgb)}, - {"dstBlendRgb", ArrayEntry(blendNames, dstBlendRgb)}, - {"blendOpRgb", ArrayEntry(blendOpNames, blendOpRgb)}, - {"alphaTest", alphaTest}, - {"cullFace", cullFace}, - {"srcBlendAlpha", ArrayEntry(blendNames, srcBlendAlpha)}, - {"dstBlendAlpha", ArrayEntry(blendNames, dstBlendAlpha)}, - {"blendOpAlpha", ArrayEntry(blendOpNames, blendOpAlpha)}, - {"colorWriteRgb", (entry.loadBits[0] & GFXS0_COLORWRITE_RGB) ? true : false}, - {"colorWriteAlpha", (entry.loadBits[0] & GFXS0_COLORWRITE_ALPHA) ? true : false}, - {"gammaWrite", (entry.loadBits[0] & GFXS0_GAMMAWRITE) ? true : false}, - {"polymodeLine", (entry.loadBits[0] & GFXS0_POLYMODE_LINE) ? true : false}, - - {"depthWrite", (entry.loadBits[1] & GFXS1_DEPTHWRITE) ? true : false}, - {"depthTest", (entry.loadBits[1] & GFXS1_DEPTHTEST_DISABLE) ? json("disable") : ArrayEntry(depthTestNames, depthTest)}, - {"polygonOffset", ArrayEntry(polygonOffsetNames, polygonOffset)}, - {"stencilFrontEnabled", (entry.loadBits[1] & GFXS1_STENCIL_FRONT_ENABLE) ? true : false}, - {"stencilBackEnabled", (entry.loadBits[1] & GFXS1_STENCIL_BACK_ENABLE) ? true : false}, - {"stencilFrontPass", ArrayEntry(stencilOpNames, (entry.loadBits[1] >> GFXS1_STENCIL_FRONT_PASS_SHIFT) & GFXS_STENCILOP_MASK)}, - {"stencilFrontFail", ArrayEntry(stencilOpNames, (entry.loadBits[1] >> GFXS1_STENCIL_FRONT_FAIL_SHIFT) & GFXS_STENCILOP_MASK)}, - {"stencilFrontZFail", ArrayEntry(stencilOpNames, (entry.loadBits[1] >> GFXS1_STENCIL_FRONT_ZFAIL_SHIFT) & GFXS_STENCILOP_MASK)}, - {"stencilFrontFunc", ArrayEntry(stencilFuncNames, (entry.loadBits[1] >> GFXS1_STENCIL_FRONT_FUNC_SHIFT) & GFXS_STENCILFUNC_MASK)}, - {"stencilBackPass", ArrayEntry(stencilOpNames, (entry.loadBits[1] >> GFXS1_STENCIL_BACK_PASS_SHIFT) & GFXS_STENCILOP_MASK)}, - {"stencilBackFail", ArrayEntry(stencilOpNames, (entry.loadBits[1] >> GFXS1_STENCIL_BACK_FAIL_SHIFT) & GFXS_STENCILOP_MASK)}, - {"stencilBackZFail", ArrayEntry(stencilOpNames, (entry.loadBits[1] >> GFXS1_STENCIL_BACK_ZFAIL_SHIFT) & GFXS_STENCILOP_MASK)}, - {"stencilBackFunc", ArrayEntry(stencilFuncNames, (entry.loadBits[1] >> GFXS1_STENCIL_BACK_FUNC_SHIFT) & GFXS_STENCILFUNC_MASK)}, - }); - } - } - - return jArray; - } - - json BuildCharFlagsJson(const std::string& prefix, const unsigned char gameFlags) - { - std::vector values; - - for (auto i = 0u; i < (sizeof(gameFlags) * 8u); i++) - { - if (gameFlags & (1 << i)) - { - std::ostringstream ss; - ss << prefix << " 0x" << std::hex << (1 << i); - values.emplace_back(ss.str()); - } - } - - return json(values); - } - std::string CreateSurfaceTypeString(const unsigned surfaceTypeBits) { if (!surfaceTypeBits) @@ -341,50 +61,6 @@ namespace IW4 return ss.str(); } - void DumpMaterialAsJson(Material* material, std::ostream& stream) - { - static const char* cameraRegionNames[]{"litOpaque", "litTrans", "emissive", "depthHack", "none"}; - - const json j = { - {"info", - { -#if defined(FLAGS_DEBUG) && FLAGS_DEBUG == 1 - {"gameFlags", BuildCharFlagsJson("gameFlag", material->info.gameFlags)}, // TODO: Find out what gameflags mean -#else - {"gameFlags", material->info.gameFlags}, // TODO: Find out what gameflags mean -#endif - {"sortKey", material->info.sortKey}, - {"textureAtlasRowCount", material->info.textureAtlasRowCount}, - {"textureAtlasColumnCount", material->info.textureAtlasColumnCount}, - {"drawSurf", - {{"objectId", static_cast(material->info.drawSurf.fields.objectId)}, - {"reflectionProbeIndex", static_cast(material->info.drawSurf.fields.reflectionProbeIndex)}, - {"hasGfxEntIndex", static_cast(material->info.drawSurf.fields.hasGfxEntIndex)}, - {"customIndex", static_cast(material->info.drawSurf.fields.customIndex)}, - {"materialSortedIndex", static_cast(material->info.drawSurf.fields.materialSortedIndex)}, - {"prepass", static_cast(material->info.drawSurf.fields.prepass)}, - {"useHeroLighting", static_cast(material->info.drawSurf.fields.useHeroLighting)}, - {"sceneLightIndex", static_cast(material->info.drawSurf.fields.sceneLightIndex)}, - {"surfType", static_cast(material->info.drawSurf.fields.surfType)}, - {"primarySortKey", static_cast(material->info.drawSurf.fields.primarySortKey)}}}, - {"surfaceTypeBits", CreateSurfaceTypeString(material->info.surfaceTypeBits)}, - {"hashIndex", material->info.hashIndex}}}, - {"stateBitsEntry", std::vector(std::begin(material->stateBitsEntry), std::end(material->stateBitsEntry))}, -#if defined(FLAGS_DEBUG) && FLAGS_DEBUG == 1 - {"stateFlags", BuildCharFlagsJson("stateFlag", material->stateFlags)}, -#else - {"stateFlags", material->stateFlags}, -#endif - {"cameraRegion", ArrayEntry(cameraRegionNames, material->cameraRegion)}, - {"techniqueSet", material->techniqueSet && material->techniqueSet->name ? AssetName(material->techniqueSet->name) : json{}}, - {"textureTable", BuildTextureTableJson(material->textureTable, material->textureCount)}, - {"constantTable", BuildConstantTableJson(material->constantTable, material->constantCount)}, - {"stateBitsTable", BuildStateBitsTableJson(material->stateBitsTable, material->stateBitsCount)} - }; - - stream << std::setw(4) << j; - } - class TechsetInfo { public: @@ -483,13 +159,26 @@ namespace IW4 class MaterialGdtDumper { - TechsetInfo m_techset_info; - StateBitsInfo m_state_bits_info; - ConstantsInfo m_constants_info; + public: + explicit MaterialGdtDumper(const Material& material) + : m_material(material) + { + } - const Material* m_material; - GdtEntry m_entry; + GdtEntry& CreateGdtEntry() + { + m_entry = GdtEntry(); + m_entry.m_gdf_name = ObjConstants::GDF_FILENAME_MATERIAL; + m_entry.m_name = m_material.info.name; + SetCommonValues(); + SetMaterialTypeValues(); + SetTextureTableValues(); + + return m_entry; + } + + private: void SetValue(const std::string& key, const char* value) { m_entry.m_properties.emplace(std::make_pair(key, value)); @@ -519,24 +208,24 @@ namespace IW4 void SetCommonValues() { - SetValue("textureAtlasRowCount", m_material->info.textureAtlasRowCount); - SetValue("textureAtlasColumnCount", m_material->info.textureAtlasColumnCount); - SetValue("surfaceType", CreateSurfaceTypeString(m_material->info.surfaceTypeBits)); + SetValue("textureAtlasRowCount", m_material.info.textureAtlasRowCount); + SetValue("textureAtlasColumnCount", m_material.info.textureAtlasColumnCount); + SetValue("surfaceType", CreateSurfaceTypeString(m_material.info.surfaceTypeBits)); } - _NODISCARD bool MaterialCouldPossiblyUseCustomTemplate() const + [[nodiscard]] bool MaterialCouldPossiblyUseCustomTemplate() const { - if (m_material->constantCount > 0) + if (m_material.constantCount > 0) return false; - if (m_material->textureTable) + if (m_material.textureTable) { static constexpr auto COLOR_MAP_HASH = Common::R_HashString("colorMap", 0u); static constexpr auto DETAIL_MAP_HASH = Common::R_HashString("detailMap", 0u); - for (auto i = 0u; i < m_material->textureCount; i++) + for (auto i = 0u; i < m_material.textureCount; i++) { - const auto nameHash = m_material->textureTable[i].nameHash; + const auto nameHash = m_material.textureTable[i].nameHash; if (nameHash != COLOR_MAP_HASH && nameHash != DETAIL_MAP_HASH) return false; } @@ -751,10 +440,10 @@ namespace IW4 void ExamineTechsetInfo() { - if (!m_material->techniqueSet || !m_material->techniqueSet->name) + if (!m_material.techniqueSet || !m_material.techniqueSet->name) return; - m_techset_info.m_techset_name = AssetName(m_material->techniqueSet->name); + m_techset_info.m_techset_name = AssetName(m_material.techniqueSet->name); m_techset_info.m_techset_base_name = m_techset_info.m_techset_name; for (auto materialType = MTL_TYPE_DEFAULT + 1; materialType < MTL_TYPE_COUNT; materialType++) @@ -768,13 +457,13 @@ namespace IW4 } } - if (m_material->info.sortKey < SORTKEY_MAX && SortKeyNames[m_material->info.sortKey]) + if (m_material.info.sortKey < SORTKEY_MAX && SortKeyNames[m_material.info.sortKey]) { - m_techset_info.m_sort_key_name = SortKeyNames[m_material->info.sortKey]; + m_techset_info.m_sort_key_name = SortKeyNames[m_material.info.sortKey]; } else { - m_techset_info.m_sort_key_name = std::to_string(m_material->info.sortKey); + m_techset_info.m_sort_key_name = std::to_string(m_material.info.sortKey); } if (m_techset_info.m_techset_base_name == "2d") @@ -861,7 +550,7 @@ namespace IW4 } else { - std::cout << "Could not determine material type for material \"" << m_material->info.name << "\"\n"; + std::cout << "Could not determine material type for material \"" << m_material.info.name << "\"\n"; } } @@ -877,7 +566,7 @@ namespace IW4 }; static inline BlendFuncParameters knownBlendFuncs[]{ - // Only considering passthrough statemap + // Only considering passthrough statemap {BlendFunc_e::ADD, BlendOp_e::ADD, CustomBlendFunc_e::ONE, @@ -914,8 +603,8 @@ namespace IW4 CustomBlendFunc_e::UNKNOWN, CustomBlendFunc_e::UNKNOWN}, - // TODO: Enable when using statemaps - // Considering default statemap + // TODO: Enable when using statemaps + // Considering default statemap {BlendFunc_e::ADD, BlendOp_e::ADD, CustomBlendFunc_e::ONE, @@ -937,7 +626,7 @@ namespace IW4 BlendOp_e::ADD, CustomBlendFunc_e::INV_DST_ALPHA, CustomBlendFunc_e::ONE }, - // REPLACE matches passthrough statemap + // REPLACE matches passthrough statemap {BlendFunc_e::SCREEN_ADD, BlendOp_e::ADD, CustomBlendFunc_e::INV_DST_COLOR, @@ -979,28 +668,22 @@ namespace IW4 m_state_bits_info.m_blend_func = BlendFunc_e::CUSTOM; } - template T StateBitsToEnum(const unsigned input, const size_t mask, const size_t shift) - { - const unsigned value = (input & mask) >> shift; - return value >= (static_cast(T::COUNT) - 1) ? T::UNKNOWN : static_cast(value + 1); - } - void ExamineStateBitsInfo() { - if (!m_material->stateBitsTable || m_material->stateBitsCount == 0) + if (!m_material.stateBitsTable || m_material.stateBitsCount == 0) return; // This assumes the statemap of these techniques is passthrough which it is most likely not // This should still not produce any wrong values GfxStateBits stateBits{}; - if (m_material->stateBitsEntry[TECHNIQUE_LIT] < m_material->stateBitsCount) - stateBits = m_material->stateBitsTable[m_material->stateBitsEntry[TECHNIQUE_LIT]]; - else if (m_material->stateBitsEntry[TECHNIQUE_EMISSIVE] < m_material->stateBitsCount) - stateBits = m_material->stateBitsTable[m_material->stateBitsEntry[TECHNIQUE_EMISSIVE]]; - else if (m_material->stateBitsEntry[TECHNIQUE_UNLIT] < m_material->stateBitsCount) - stateBits = m_material->stateBitsTable[m_material->stateBitsEntry[TECHNIQUE_UNLIT]]; - else if (m_material->stateBitsEntry[TECHNIQUE_DEPTH_PREPASS] < m_material->stateBitsCount) - stateBits = m_material->stateBitsTable[m_material->stateBitsEntry[TECHNIQUE_DEPTH_PREPASS]]; + if (m_material.stateBitsEntry[TECHNIQUE_LIT] < m_material.stateBitsCount) + stateBits = m_material.stateBitsTable[m_material.stateBitsEntry[TECHNIQUE_LIT]]; + else if (m_material.stateBitsEntry[TECHNIQUE_EMISSIVE] < m_material.stateBitsCount) + stateBits = m_material.stateBitsTable[m_material.stateBitsEntry[TECHNIQUE_EMISSIVE]]; + else if (m_material.stateBitsEntry[TECHNIQUE_UNLIT] < m_material.stateBitsCount) + stateBits = m_material.stateBitsTable[m_material.stateBitsEntry[TECHNIQUE_UNLIT]]; + else if (m_material.stateBitsEntry[TECHNIQUE_DEPTH_PREPASS] < m_material.stateBitsCount) + stateBits = m_material.stateBitsTable[m_material.stateBitsEntry[TECHNIQUE_DEPTH_PREPASS]]; else { assert(false); @@ -1008,37 +691,32 @@ namespace IW4 } if (m_state_bits_info.m_custom_blend_op_rgb == BlendOp_e::UNKNOWN) - m_state_bits_info.m_custom_blend_op_rgb = StateBitsToEnum(stateBits.loadBits[0], GFXS0_BLENDOP_RGB_MASK, GFXS0_BLENDOP_RGB_SHIFT); + m_state_bits_info.m_custom_blend_op_rgb = static_cast(stateBits.loadBits.structured.blendOpRgb); if (m_state_bits_info.m_custom_blend_op_alpha == BlendOp_e::UNKNOWN) - m_state_bits_info.m_custom_blend_op_alpha = - StateBitsToEnum(stateBits.loadBits[0], GFXS0_BLENDOP_ALPHA_MASK, GFXS0_BLENDOP_ALPHA_SHIFT); + m_state_bits_info.m_custom_blend_op_alpha = static_cast(stateBits.loadBits.structured.blendOpAlpha); if (m_state_bits_info.m_custom_src_blend_func == CustomBlendFunc_e::UNKNOWN) - m_state_bits_info.m_custom_src_blend_func = - StateBitsToEnum(stateBits.loadBits[0], GFXS0_SRCBLEND_RGB_MASK, GFXS0_SRCBLEND_RGB_SHIFT); + m_state_bits_info.m_custom_src_blend_func = static_cast(stateBits.loadBits.structured.srcBlendRgb); if (m_state_bits_info.m_custom_dst_blend_func == CustomBlendFunc_e::UNKNOWN) - m_state_bits_info.m_custom_dst_blend_func = - StateBitsToEnum(stateBits.loadBits[0], GFXS0_DSTBLEND_RGB_MASK, GFXS0_DSTBLEND_RGB_SHIFT); + m_state_bits_info.m_custom_dst_blend_func = static_cast(stateBits.loadBits.structured.dstBlendRgb); if (m_state_bits_info.m_custom_src_blend_func_alpha == CustomBlendFunc_e::UNKNOWN) - m_state_bits_info.m_custom_src_blend_func_alpha = - StateBitsToEnum(stateBits.loadBits[0], GFXS0_SRCBLEND_ALPHA_MASK, GFXS0_SRCBLEND_ALPHA_SHIFT); + m_state_bits_info.m_custom_src_blend_func_alpha = static_cast(stateBits.loadBits.structured.srcBlendAlpha); if (m_state_bits_info.m_custom_dst_blend_func_alpha == CustomBlendFunc_e::UNKNOWN) - m_state_bits_info.m_custom_dst_blend_func_alpha = - StateBitsToEnum(stateBits.loadBits[0], GFXS0_DSTBLEND_ALPHA_MASK, GFXS0_DSTBLEND_ALPHA_SHIFT); + m_state_bits_info.m_custom_dst_blend_func_alpha = static_cast(stateBits.loadBits.structured.dstBlendAlpha); if (m_state_bits_info.m_alpha_test == AlphaTest_e::UNKNOWN) { - if (stateBits.loadBits[0] & GFXS0_ATEST_DISABLE) + if (stateBits.loadBits.structured.alphaTestDisabled) m_state_bits_info.m_alpha_test = AlphaTest_e::ALWAYS; - else if ((stateBits.loadBits[0] & GFXS0_ATEST_MASK) == GFXS0_ATEST_GE_128) + else if (stateBits.loadBits.structured.alphaTest == GFXS_ALPHA_TEST_GE_128) m_state_bits_info.m_alpha_test = AlphaTest_e::GE128; - else if ((stateBits.loadBits[0] & GFXS0_ATEST_MASK) == GFXS0_ATEST_GT_0) + else if (stateBits.loadBits.structured.alphaTest == GFXS_ALPHA_TEST_GT_0) m_state_bits_info.m_alpha_test = AlphaTest_e::GT0; - else if ((stateBits.loadBits[0] & GFXS0_ATEST_MASK) == GFXS0_ATEST_LT_128) + else if (stateBits.loadBits.structured.alphaTest == GFXS_ALPHA_TEST_LT_128) m_state_bits_info.m_alpha_test = AlphaTest_e::LT128; else assert(false); @@ -1046,13 +724,13 @@ namespace IW4 if (m_state_bits_info.m_depth_test == DepthTest_e::UNKNOWN) { - if (stateBits.loadBits[1] & GFXS1_DEPTHTEST_DISABLE) + if (stateBits.loadBits.structured.depthTestDisabled) m_state_bits_info.m_depth_test = DepthTest_e::DISABLE; - else if (stateBits.loadBits[1] & GFXS1_DEPTHTEST_LESSEQUAL) + else if (stateBits.loadBits.structured.depthTest == GFXS_DEPTHTEST_LESSEQUAL) m_state_bits_info.m_depth_test = DepthTest_e::LESS_EQUAL; - else if (stateBits.loadBits[1] & GFXS1_DEPTHTEST_LESS) + else if (stateBits.loadBits.structured.depthTest == GFXS_DEPTHTEST_LESS) m_state_bits_info.m_depth_test = DepthTest_e::LESS; - else if (stateBits.loadBits[1] & GFXS1_DEPTHTEST_EQUAL) + else if (stateBits.loadBits.structured.depthTest == GFXS_DEPTHTEST_EQUAL) m_state_bits_info.m_depth_test = DepthTest_e::EQUAL; else m_state_bits_info.m_depth_test = DepthTest_e::ALWAYS; @@ -1060,114 +738,95 @@ namespace IW4 if (m_state_bits_info.m_depth_write == StateBitsEnabledStatus_e::UNKNOWN) m_state_bits_info.m_depth_write = - (stateBits.loadBits[1] & GFXS1_DEPTHWRITE) ? StateBitsEnabledStatus_e::ENABLED : StateBitsEnabledStatus_e::DISABLED; + stateBits.loadBits.structured.depthWrite ? StateBitsEnabledStatus_e::ENABLED : StateBitsEnabledStatus_e::DISABLED; if (m_state_bits_info.m_cull_face == CullFace_e::UNKNOWN) { - if (stateBits.loadBits[0] & GFXS0_CULL_NONE) + if (stateBits.loadBits.structured.cullFace == GFXS_CULL_NONE) m_state_bits_info.m_cull_face = CullFace_e::NONE; - else if (stateBits.loadBits[0] & GFXS0_CULL_BACK) + else if (stateBits.loadBits.structured.cullFace == GFXS_CULL_BACK) m_state_bits_info.m_cull_face = CullFace_e::BACK; - else if (stateBits.loadBits[0] & GFXS0_CULL_FRONT) + else if (stateBits.loadBits.structured.cullFace == GFXS_CULL_FRONT) m_state_bits_info.m_cull_face = CullFace_e::FRONT; else assert(false); } if (m_state_bits_info.m_polygon_offset == PolygonOffset_e::UNKNOWN) - m_state_bits_info.m_polygon_offset = - StateBitsToEnum(stateBits.loadBits[1], GFXS1_POLYGON_OFFSET_MASK, GFXS1_POLYGON_OFFSET_SHIFT); + m_state_bits_info.m_polygon_offset = static_cast(stateBits.loadBits.structured.polygonOffset); if (m_state_bits_info.m_color_write_rgb == StateBitsEnabledStatus_e::UNKNOWN) + { m_state_bits_info.m_color_write_rgb = - (stateBits.loadBits[0] & GFXS0_COLORWRITE_RGB) ? StateBitsEnabledStatus_e::ENABLED : StateBitsEnabledStatus_e::DISABLED; + stateBits.loadBits.structured.colorWriteRgb ? StateBitsEnabledStatus_e::ENABLED : StateBitsEnabledStatus_e::DISABLED; + } if (m_state_bits_info.m_color_write_alpha == StateBitsEnabledStatus_e::UNKNOWN) + { m_state_bits_info.m_color_write_alpha = - (stateBits.loadBits[0] & GFXS0_COLORWRITE_ALPHA) ? StateBitsEnabledStatus_e::ENABLED : StateBitsEnabledStatus_e::DISABLED; + stateBits.loadBits.structured.colorWriteAlpha ? StateBitsEnabledStatus_e::ENABLED : StateBitsEnabledStatus_e::DISABLED; + } if (m_state_bits_info.m_gamma_write == StateBitsEnabledStatus_e::UNKNOWN) + { m_state_bits_info.m_gamma_write = - (stateBits.loadBits[0] & GFXS0_GAMMAWRITE) ? StateBitsEnabledStatus_e::ENABLED : StateBitsEnabledStatus_e::DISABLED; + stateBits.loadBits.structured.gammaWrite ? StateBitsEnabledStatus_e::ENABLED : StateBitsEnabledStatus_e::DISABLED; + } if (m_state_bits_info.m_stencil_mode == StencilMode_e::UNKNOWN) { - if ((stateBits.loadBits[1] & GFXS1_STENCIL_BACK_ENABLE) == 0 && (stateBits.loadBits[1] & GFXS1_STENCIL_FRONT_ENABLE) == 0) + if (stateBits.loadBits.structured.stencilBackEnabled == 0 && stateBits.loadBits.structured.stencilFrontEnabled == 0) { m_state_bits_info.m_stencil_mode = StencilMode_e::DISABLED; } - else if (stateBits.loadBits[1] & GFXS1_STENCIL_BACK_ENABLE) + else if (stateBits.loadBits.structured.stencilBackEnabled) { - assert(stateBits.loadBits[1] & GFXS1_STENCIL_FRONT_ENABLE); + assert(stateBits.loadBits.structured.stencilFrontEnabled); m_state_bits_info.m_stencil_mode = StencilMode_e::TWO_SIDED; } else { - assert(stateBits.loadBits[1] & GFXS1_STENCIL_FRONT_ENABLE); + assert(stateBits.loadBits.structured.stencilFrontEnabled); m_state_bits_info.m_stencil_mode = StencilMode_e::ONE_SIDED; } } if (m_state_bits_info.m_stencil_front_func == StencilFunc_e::UNKNOWN) - m_state_bits_info.m_stencil_front_func = - StateBitsToEnum(stateBits.loadBits[1], GFXS1_STENCIL_FRONT_FUNC_MASK, GFXS1_STENCIL_FRONT_FUNC_SHIFT); + m_state_bits_info.m_stencil_front_func = static_cast(stateBits.loadBits.structured.stencilFrontFunc); if (m_state_bits_info.m_stencil_front_pass == StencilOp_e::UNKNOWN) - m_state_bits_info.m_stencil_front_pass = - StateBitsToEnum(stateBits.loadBits[1], GFXS1_STENCIL_FRONT_PASS_MASK, GFXS1_STENCIL_FRONT_PASS_SHIFT); + m_state_bits_info.m_stencil_front_pass = static_cast(stateBits.loadBits.structured.stencilFrontPass); if (m_state_bits_info.m_stencil_front_fail == StencilOp_e::UNKNOWN) - m_state_bits_info.m_stencil_front_fail = - StateBitsToEnum(stateBits.loadBits[1], GFXS1_STENCIL_FRONT_FAIL_MASK, GFXS1_STENCIL_FRONT_FAIL_SHIFT); + m_state_bits_info.m_stencil_front_fail = static_cast(stateBits.loadBits.structured.stencilFrontFail); if (m_state_bits_info.m_stencil_front_zfail == StencilOp_e::UNKNOWN) - m_state_bits_info.m_stencil_front_zfail = - StateBitsToEnum(stateBits.loadBits[1], GFXS1_STENCIL_FRONT_ZFAIL_MASK, GFXS1_STENCIL_FRONT_ZFAIL_SHIFT); + m_state_bits_info.m_stencil_front_zfail = static_cast(stateBits.loadBits.structured.stencilFrontZFail); if (m_state_bits_info.m_stencil_back_func == StencilFunc_e::UNKNOWN) - m_state_bits_info.m_stencil_back_func = - StateBitsToEnum(stateBits.loadBits[1], GFXS1_STENCIL_BACK_FUNC_MASK, GFXS1_STENCIL_BACK_FUNC_SHIFT); + m_state_bits_info.m_stencil_back_func = static_cast(stateBits.loadBits.structured.stencilBackFunc); if (m_state_bits_info.m_stencil_back_pass == StencilOp_e::UNKNOWN) - m_state_bits_info.m_stencil_back_pass = - StateBitsToEnum(stateBits.loadBits[1], GFXS1_STENCIL_BACK_PASS_MASK, GFXS1_STENCIL_BACK_PASS_SHIFT); + m_state_bits_info.m_stencil_back_pass = static_cast(stateBits.loadBits.structured.stencilBackPass); if (m_state_bits_info.m_stencil_back_fail == StencilOp_e::UNKNOWN) - m_state_bits_info.m_stencil_back_fail = - StateBitsToEnum(stateBits.loadBits[1], GFXS1_STENCIL_BACK_FAIL_MASK, GFXS1_STENCIL_BACK_FAIL_SHIFT); + m_state_bits_info.m_stencil_back_fail = static_cast(stateBits.loadBits.structured.stencilBackFail); if (m_state_bits_info.m_stencil_back_zfail == StencilOp_e::UNKNOWN) - m_state_bits_info.m_stencil_back_zfail = - StateBitsToEnum(stateBits.loadBits[1], GFXS1_STENCIL_BACK_ZFAIL_MASK, GFXS1_STENCIL_BACK_ZFAIL_SHIFT); + m_state_bits_info.m_stencil_back_zfail = static_cast(stateBits.loadBits.structured.stencilBackZFail); ExamineBlendFunc(); } - _NODISCARD int FindConstant(const std::string& constantName) const - { - const auto constantHash = Common::R_HashString(constantName.c_str(), 0u); - - if (m_material->constantTable) - { - for (auto i = 0; i < m_material->constantCount; i++) - { - if (m_material->constantTable[i].nameHash == constantHash) - return i; - } - } - - return -1; - } - - _NODISCARD int FindTexture(const std::string& textureTypeName) const + [[nodiscard]] int FindTexture(const std::string& textureTypeName) const { const auto textureTypeHash = Common::R_HashString(textureTypeName.c_str(), 0u); - if (m_material->textureTable) + if (m_material.textureTable) { - for (auto i = 0; i < m_material->textureCount; i++) + for (auto i = 0; i < m_material.textureCount; i++) { - if (m_material->textureTable[i].nameHash == textureTypeHash) + if (m_material.textureTable[i].nameHash == textureTypeHash) return i; } } @@ -1177,16 +836,16 @@ namespace IW4 void ExamineConstants() { - if (!m_material->constantTable) + if (!m_material.constantTable) return; - for (auto i = 0u; i < m_material->constantCount; i++) + for (auto i = 0u; i < m_material.constantCount; i++) { - const auto& constant = m_material->constantTable[i]; + const auto& constant = m_material.constantTable[i]; if (constant.nameHash == Common::R_HashString("colorTint")) { - m_constants_info.m_color_tint = Eigen::Vector4f(constant.literal); + m_constants_info.m_color_tint = Eigen::Vector4f(constant.literal.v); } else if (constant.nameHash == Common::R_HashString("envMapParms")) { @@ -1197,54 +856,53 @@ namespace IW4 } else if (constant.nameHash == Common::R_HashString("featherParms")) { - m_constants_info.m_zfeather_depth = constant.literal[1]; + m_constants_info.m_zfeather_depth = constant.literal.y; } else if (constant.nameHash == Common::R_HashString("falloffBeginColor")) { - m_constants_info.m_falloff_begin_color = Eigen::Vector4f(constant.literal); + m_constants_info.m_falloff_begin_color = Eigen::Vector4f(constant.literal.v); } else if (constant.nameHash == Common::R_HashString("falloffEndColor")) { - m_constants_info.m_falloff_end_color = Eigen::Vector4f(constant.literal); + m_constants_info.m_falloff_end_color = Eigen::Vector4f(constant.literal.v); } else if (constant.nameHash == Common::R_HashString("eyeOffsetParms")) { - m_constants_info.m_eye_offset_depth = constant.literal[0]; + m_constants_info.m_eye_offset_depth = constant.literal.x; } else if (constant.nameHash == Common::R_HashString("detailScale")) { const auto materialType = m_techset_info.m_gdt_material_type; const auto colorMapIndex = FindTexture("colorMap"); const auto detailMapIndex = FindTexture("detailMap"); - const auto hasColorMap = colorMapIndex >= 0 && m_material->textureTable[colorMapIndex].semantic != TS_WATER_MAP - && m_material->textureTable[colorMapIndex].u.image; - const auto hasDetailMap = detailMapIndex >= 0 && m_material->textureTable[detailMapIndex].semantic != TS_WATER_MAP - && m_material->textureTable[detailMapIndex].u.image; + const auto hasColorMap = + colorMapIndex >= 0 && m_material.textureTable[colorMapIndex].semantic != TS_WATER_MAP && m_material.textureTable[colorMapIndex].u.image; + const auto hasDetailMap = detailMapIndex >= 0 && m_material.textureTable[detailMapIndex].semantic != TS_WATER_MAP + && m_material.textureTable[detailMapIndex].u.image; if ((materialType == MATERIAL_TYPE_MODEL_PHONG || materialType == MATERIAL_TYPE_WORLD_PHONG) && hasColorMap && hasDetailMap) { - const auto colorMapTexture = m_material->textureTable[colorMapIndex].u.image; - const auto detailMapTexture = m_material->textureTable[detailMapIndex].u.image; + const auto colorMapTexture = m_material.textureTable[colorMapIndex].u.image; + const auto detailMapTexture = m_material.textureTable[detailMapIndex].u.image; if (colorMapTexture->width != 0 && colorMapTexture->height != 0 && detailMapTexture->width != 0 && detailMapTexture->height != 0) { const auto detailScaleFactorX = static_cast(colorMapTexture->width) / static_cast(detailMapTexture->width); const auto detailScaleFactorY = static_cast(colorMapTexture->height) / static_cast(detailMapTexture->height); - m_constants_info.m_detail_scale = - Eigen::Vector2f(constant.literal[0] / detailScaleFactorX, constant.literal[1] / detailScaleFactorY); + m_constants_info.m_detail_scale = Eigen::Vector2f(constant.literal.x / detailScaleFactorX, constant.literal.y / detailScaleFactorY); } else - m_constants_info.m_detail_scale = Eigen::Vector2f(constant.literal[0], constant.literal[1]); + m_constants_info.m_detail_scale = Eigen::Vector2f(constant.literal.x, constant.literal.y); } else { - m_constants_info.m_detail_scale = Eigen::Vector2f(constant.literal[0], constant.literal[1]); + m_constants_info.m_detail_scale = Eigen::Vector2f(constant.literal.x, constant.literal.y); } } else if (constant.nameHash == Common::R_HashString("flagParms")) { - m_constants_info.m_flag_speed = constant.literal[0]; - m_constants_info.m_flag_phase = constant.literal[1]; + m_constants_info.m_flag_speed = constant.literal.x; + m_constants_info.m_flag_phase = constant.literal.y; } else if (constant.nameHash == Common::R_HashString("falloffParms")) { @@ -1256,25 +914,25 @@ namespace IW4 } else if (constant.nameHash == Common::R_HashString("distortionScale")) { - m_constants_info.m_distortion_scale = Eigen::Vector2f(constant.literal[0], constant.literal[1]); + m_constants_info.m_distortion_scale = Eigen::Vector2f(constant.literal.x, constant.literal.y); } else if (constant.nameHash == Common::R_HashString("uvAnimParms")) { - m_constants_info.m_uv_scroll_x = constant.literal[0]; - m_constants_info.m_uv_scroll_y = constant.literal[1]; - m_constants_info.m_uv_rotate = constant.literal[2]; + m_constants_info.m_uv_scroll_x = constant.literal.x; + m_constants_info.m_uv_scroll_y = constant.literal.y; + m_constants_info.m_uv_rotate = constant.literal.z; } else if (constant.nameHash == Common::R_HashString("colorObjMin")) { - m_constants_info.m_color_obj_min = Eigen::Vector4f(constant.literal); + m_constants_info.m_color_obj_min = Eigen::Vector4f(constant.literal.v); } else if (constant.nameHash == Common::R_HashString("colorObjMax")) { - m_constants_info.m_color_obj_max = Eigen::Vector4f(constant.literal); + m_constants_info.m_color_obj_max = Eigen::Vector4f(constant.literal.v); } else if (constant.nameHash == Common::R_HashString("waterColor")) { - m_constants_info.m_water_color = Eigen::Vector4f(constant.literal); + m_constants_info.m_water_color = Eigen::Vector4f(constant.literal.v); } else { @@ -1363,12 +1021,12 @@ namespace IW4 void SetTextureTableValues() { - if (m_material->textureTable == nullptr || m_material->textureCount <= 0) + if (m_material.textureTable == nullptr || m_material.textureCount <= 0) return; - for (auto i = 0u; i < m_material->textureCount; i++) + for (auto i = 0u; i < m_material.textureCount; i++) { - const auto& entry = m_material->textureTable[i]; + const auto& entry = m_material.textureTable[i]; const auto knownMaterialSourceName = knownTextureMaps.find(entry.nameHash); if (knownMaterialSourceName == knownTextureMaps.end()) { @@ -1393,45 +1051,45 @@ namespace IW4 } TileMode_e tileMode; - if (entry.samplerState & SAMPLER_CLAMP_U && entry.samplerState & SAMPLER_CLAMP_V && entry.samplerState & SAMPLER_CLAMP_W) + if (entry.samplerState.clampU && entry.samplerState.clampV && entry.samplerState.clampW) tileMode = TileMode_e::TILE_BOTH; - else if (entry.samplerState & SAMPLER_CLAMP_U) + else if (entry.samplerState.clampU) tileMode = TileMode_e::TILE_VERTICAL; - else if (entry.samplerState & SAMPLER_CLAMP_V) + else if (entry.samplerState.clampV) tileMode = TileMode_e::TILE_HORIZONTAL; else tileMode = TileMode_e::NO_TILE; auto filter = GdtFilter_e::UNKNOWN; - if ((entry.samplerState & SAMPLER_FILTER_MASK) == SAMPLER_FILTER_ANISO2X) + if (entry.samplerState.filter == TEXTURE_FILTER_ANISO2X) { - if (entry.samplerState & SAMPLER_MIPMAP_NEAREST) + if (entry.samplerState.mipMap == SAMPLER_MIPMAP_ENUM_NEAREST) filter = GdtFilter_e::MIP_2X_BILINEAR; - else if (entry.samplerState & SAMPLER_MIPMAP_LINEAR) + else if (entry.samplerState.mipMap == SAMPLER_MIPMAP_ENUM_LINEAR) filter = GdtFilter_e::MIP_2X_TRILINEAR; } - else if ((entry.samplerState & SAMPLER_FILTER_MASK) == SAMPLER_FILTER_ANISO4X) + else if (entry.samplerState.filter == TEXTURE_FILTER_ANISO4X) { - if (entry.samplerState & SAMPLER_MIPMAP_NEAREST) + if (entry.samplerState.mipMap == SAMPLER_MIPMAP_ENUM_NEAREST) filter = GdtFilter_e::MIP_4X_BILINEAR; - else if (entry.samplerState & SAMPLER_MIPMAP_LINEAR) + else if (entry.samplerState.mipMap == SAMPLER_MIPMAP_ENUM_LINEAR) filter = GdtFilter_e::MIP_4X_TRILINEAR; } - else if ((entry.samplerState & SAMPLER_FILTER_MASK) == SAMPLER_FILTER_NEAREST) + else if (entry.samplerState.filter == TEXTURE_FILTER_NEAREST) { - assert((entry.samplerState & SAMPLER_MIPMAP_MASK) == SAMPLER_MIPMAP_DISABLED); + assert(entry.samplerState.mipMap == SAMPLER_MIPMAP_ENUM_DISABLED); filter = GdtFilter_e::NOMIP_NEAREST; } - else if ((entry.samplerState & SAMPLER_FILTER_MASK) == SAMPLER_FILTER_LINEAR) + else if (entry.samplerState.filter == TEXTURE_FILTER_LINEAR) { - assert((entry.samplerState & SAMPLER_MIPMAP_MASK) == SAMPLER_MIPMAP_DISABLED); + assert(entry.samplerState.mipMap == SAMPLER_MIPMAP_ENUM_DISABLED); filter = GdtFilter_e::NOMIP_BILINEAR; } assert(filter != GdtFilter_e::UNKNOWN); if (filter == GdtFilter_e::UNKNOWN) { - std::cout << "Unknown filter/mipmap combination: " << entry.samplerState << "\n"; + std::cout << std::format("Unknown filter/mipmap combination: {} {}\n", entry.samplerState.filter, entry.samplerState.mipMap); continue; } @@ -1441,66 +1099,20 @@ namespace IW4 } } - public: - explicit MaterialGdtDumper(const Material* material) - : m_material(material) - { - } + TechsetInfo m_techset_info; + StateBitsInfo m_state_bits_info; + ConstantsInfo m_constants_info; - GdtEntry& CreateGdtEntry() - { - m_entry = GdtEntry(); - m_entry.m_gdf_name = ObjConstants::GDF_FILENAME_MATERIAL; - m_entry.m_name = m_material->info.name; - - SetCommonValues(); - SetMaterialTypeValues(); - SetTextureTableValues(); - - return m_entry; - } + const Material& m_material; + GdtEntry m_entry; }; +} // namespace + +namespace IW4 +{ + void DecompileMaterialToGdt(GdtOutputStream& out, const Material& material, AssetDumpingContext& context) + { + MaterialGdtDumper dumper(material); + out.WriteEntry(dumper.CreateGdtEntry()); + } } // namespace IW4 - -bool AssetDumperMaterial::ShouldDump(XAssetInfo* asset) -{ - return true; -} - -void AssetDumperMaterial::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) -{ - auto* material = asset->Asset(); - -#if defined(DUMP_AS_JSON) && DUMP_AS_JSON == 1 - { - std::ostringstream ss; - ss << "materials/" << asset->m_name << ".json"; - const auto assetFile = context.OpenAssetFile(ss.str()); - if (!assetFile) - return; - auto& stream = *assetFile; - DumpMaterialAsJson(material, stream); - } -#endif - -#if defined(DUMP_AS_GDT) && DUMP_AS_GDT == 1 - { - std::ostringstream ss; - ss << "materials/" << asset->m_name << ".gdt"; - const auto assetFile = context.OpenAssetFile(ss.str()); - if (!assetFile) - return; - auto& stream = *assetFile; - MaterialGdtDumper dumper(material); - Gdt gdt(GdtVersion("IW4", 1)); - gdt.m_entries.emplace_back(std::make_unique(std::move(dumper.CreateGdtEntry()))); - GdtOutputStream::WriteGdt(gdt, stream); - } -#endif - - if (context.m_gdt) - { - MaterialGdtDumper dumper(material); - context.m_gdt->WriteEntry(dumper.CreateGdtEntry()); - } -} diff --git a/src/ObjWriting/Game/IW4/Material/DecompilingMaterialDumperIW4.h b/src/ObjWriting/Game/IW4/Material/DecompilingMaterialDumperIW4.h new file mode 100644 index 00000000..c6792591 --- /dev/null +++ b/src/ObjWriting/Game/IW4/Material/DecompilingMaterialDumperIW4.h @@ -0,0 +1,10 @@ +#pragma once + +#include "Dumping/AssetDumpingContext.h" +#include "Game/IW4/IW4.h" +#include "Obj/Gdt/GdtStream.h" + +namespace IW4 +{ + void DecompileMaterialToGdt(GdtOutputStream& out, const Material& material, AssetDumpingContext& context); +} // namespace IW4 diff --git a/src/ObjWriting/Game/IW4/Material/DumperMaterialIW4.cpp b/src/ObjWriting/Game/IW4/Material/DumperMaterialIW4.cpp new file mode 100644 index 00000000..9d10c301 --- /dev/null +++ b/src/ObjWriting/Game/IW4/Material/DumperMaterialIW4.cpp @@ -0,0 +1,38 @@ +#include "DumperMaterialIW4.h" + +#include "DecompilingMaterialDumperIW4.h" +#include "Game/IW4/Material/JsonMaterialWriterIW4.h" +#include "Game/IW4/Material/MaterialConstantZoneStateIW4.h" +#include "Material/MaterialCommon.h" + +using namespace IW4; + +void AssetDumperMaterial::DumpPool(AssetDumpingContext& context, AssetPool* pool) +{ + auto* materialConstantState = context.GetZoneAssetDumperState(); + materialConstantState->ExtractNamesFromZone(); + + AbstractAssetDumper::DumpPool(context, pool); +} + +bool AssetDumperMaterial::ShouldDump(XAssetInfo* asset) +{ + return true; +} + +void AssetDumperMaterial::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) +{ +#ifdef EXPERIMENTAL_MATERIAL_COMPILATION + if (context.m_gdt) + { + DecompileMaterialToGdt(*context.m_gdt, *asset->Asset(), context); + } +#else + const auto assetFile = context.OpenAssetFile(material::GetFileNameForAssetName(asset->m_name)); + + if (!assetFile) + return; + + DumpMaterialAsJson(*assetFile, *asset->Asset(), context); +#endif +} diff --git a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperMaterial.h b/src/ObjWriting/Game/IW4/Material/DumperMaterialIW4.h similarity index 78% rename from src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperMaterial.h rename to src/ObjWriting/Game/IW4/Material/DumperMaterialIW4.h index d55f439e..1d3447f3 100644 --- a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperMaterial.h +++ b/src/ObjWriting/Game/IW4/Material/DumperMaterialIW4.h @@ -7,6 +7,9 @@ namespace IW4 { class AssetDumperMaterial final : public AbstractAssetDumper { + public: + void DumpPool(AssetDumpingContext& context, AssetPool* pool) override; + protected: bool ShouldDump(XAssetInfo* asset) override; void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; diff --git a/src/ObjWriting/Game/IW4/Material/MaterialConstantZoneStateIW4.cpp b/src/ObjWriting/Game/IW4/Material/MaterialConstantZoneStateIW4.cpp new file mode 100644 index 00000000..70593610 --- /dev/null +++ b/src/ObjWriting/Game/IW4/Material/MaterialConstantZoneStateIW4.cpp @@ -0,0 +1,236 @@ +#include "MaterialConstantZoneStateIW4.h" + +#include "Game/IW4/CommonIW4.h" +#include "Game/IW4/GameAssetPoolIW4.h" +#include "Game/IW4/GameIW4.h" +#include "ObjWriting.h" + +namespace IW4 +{ + const char* KNOWN_CONSTANT_NAMES[]{ + "worldViewProjectionMatrix", + "worldViewMatrix2", + "worldViewMatrix1", + "worldViewMatrix", + "worldOutdoorLookupMatrix", + "worldMatrix", + "waterColor", + "viewportDimensions", + "viewProjectionMatrix", + "uvScale", + "uvAnimParms", + "thermalColorOffset", + "sunShadowmapPixelAdjust", + "ssaoParms", + "spotShadowmapPixelAdjust", + "shadowmapSwitchPartition", + "shadowmapScale", + "shadowmapPolygonOffset", + "shadowLookupMatrix", + "renderTargetSize", + "renderSourceSize", + "projectionMatrix", + "playlistPopulationParams", + "pixelCostFracs", + "pixelCostDecode", + "particleCloudSparkColor2", + "particleCloudSparkColor1", + "particleCloudSparkColor0", + "particleCloudMatrix2", + "particleCloudMatrix1", + "particleCloudMatrix", + "particleCloudColor", + "outdoorFeatherParms", + "oceanUVAnimParmPaintedFoam", + "oceanUVAnimParmOctave2", + "oceanUVAnimParmOctave1", + "oceanUVAnimParmOctave0", + "oceanUVAnimParmFoam", + "oceanUVAnimParmDetail1", + "oceanUVAnimParmDetail0", + "oceanScrollParms", + "oceanMiscParms", + "oceanFoamParms", + "oceanAmplitude", + "materialColor", + "lightprobeAmbient", + "lightingLookupScale", + "lightSpotFactors", + "lightSpotDir", + "lightSpecular", + "lightPosition", + "lightFalloffPlacement", + "lightDiffuse", + "inverseWorldViewMatrix", + "inverseViewProjectionMatrix", + "inverseTransposeWorldViewMatrix", + "heatMapDetail", + "glowSetup", + "glowApply", + "gameTime", + "fullscreenDistortion", + "fogSunDir", + "fogSunConsts", + "fogSunColorLinear", + "fogSunColorGamma", + "fogConsts", + "fogColorLinear", + "fogColorGamma", + "flagParms", + "filterTap", + "featherParms", + "falloffParms", + "falloffEndColor", + "falloffBeginColor", + "fadeEffect", + "eyeOffsetParms", + "eyeOffset", + "envMapParms", + "dustTint", + "dustParms", + "dustEyeParms", + "dofRowDelta", + "dofLerpScale", + "dofLerpBias", + "dofEquationViewModelAndFarBlur", + "dofEquationScene", + "distortionScale", + "detailScale", + "depthFromClip", + "debugBumpmap", + "colorTintQuadraticDelta", + "colorTintDelta", + "colorTintBase", + "colorSaturationR", + "colorSaturationG", + "colorSaturationB", + "colorObjMin", + "colorObjMax", + "colorMatrixR", + "colorMatrixG", + "colorMatrixB", + "colorBias", + "codeMeshArg", + "clipSpaceLookupScale", + "clipSpaceLookupOffset", + "baseLightingCoords", + }; + + const char* KNOWN_TEXTURE_DEF_NAMES[]{ + "attenuation", + "attenuationSampler", + "cinematicA", + "cinematicASampler", + "cinematicCb", + "cinematicCbSampler", + "cinematicCr", + "cinematicCrSampler", + "cinematicY", + "cinematicYSampler", + "colorMap", + "colorMap1", + "colorMap2", + "colorMapPostSun", + "colorMapPostSunSampler", + "colorMapSampler", + "colorMapSampler1", + "colorMapSampler2", + "cucoloris", + "cucolorisSampler", + "detailMap", + "detailMapSampler", + "dust", + "dustSampler", + "fadeMap", + "fadeMapSampler", + "floatZ", + "floatZSampler", + "grainMap", + "grainMapSampler", + "halfParticleColor", + "halfParticleColorSampler", + "halfParticleDepth", + "halfParticleDepthSampler", + "heatmap", + "heatmapSampler", + "lightmapPrimary", + "lightmapSamplerPrimary", + "lightmapSamplerSecondary", + "lightmapSecondary", + "lookupMap", + "lookupMapSampler", + "modelLighting", + "modelLightingSampler", + "normalMap", + "normalMapSampler", + "oceanColorRamp", + "oceanColorRampSampler", + "oceanDetailNormal", + "oceanDetailNormalSampler", + "oceanDisplacement", + "oceanDisplacementSampler", + "oceanEnv", + "oceanEnvSampler", + "oceanFoam", + "oceanFoamSampler", + "oceanHeightNormal", + "oceanHeightNormalSampler", + "oceanPaintedFoam", + "oceanPaintedFoamSampler", + "outdoorMap", + "outdoorMapSampler", + "population", + "populationSampler", + "reflectionProbe", + "reflectionProbeSampler", + "shadowmapSamplerSpot", + "shadowmapSamplerSun", + "shadowmapSpot", + "shadowmapSun", + "skyMap", + "skyMapSampler", + "specularMap", + "specularMapSampler", + "ssao", + "ssaoSampler", + "worldMap", + "worldMapSampler", + }; + + void MaterialConstantZoneState::ExtractNamesFromZoneInternal() + { + for (const auto* zone : IGame::GetGameById(GameId::IW5)->GetZones()) + { + const auto* iw5AssetPools = dynamic_cast(zone->m_pools.get()); + if (!iw5AssetPools) + return; + + for (const auto* vertexShaderAsset : *iw5AssetPools->m_material_vertex_shader) + { + const auto* vertexShader = vertexShaderAsset->Asset(); + if (ShouldDumpFromStruct(vertexShader)) + ExtractNamesFromShader(vertexShader->prog.loadDef.program, static_cast(vertexShader->prog.loadDef.programSize) * sizeof(uint32_t)); + } + + for (const auto* pixelShaderAsset : *iw5AssetPools->m_material_pixel_shader) + { + const auto* pixelShader = pixelShaderAsset->Asset(); + if (ShouldDumpFromStruct(pixelShader)) + ExtractNamesFromShader(pixelShader->prog.loadDef.program, static_cast(pixelShader->prog.loadDef.programSize) * sizeof(uint32_t)); + } + } + } + + void MaterialConstantZoneState::AddStaticKnownNames() + { + for (const auto* knownConstantName : KNOWN_CONSTANT_NAMES) + AddConstantName(knownConstantName); + for (const auto* knownTextureDefName : KNOWN_TEXTURE_DEF_NAMES) + AddTextureDefName(knownTextureDefName); + } + + unsigned MaterialConstantZoneState::HashString(const std::string& str) + { + return Common::R_HashString(str.c_str()); + } +} // namespace IW4 diff --git a/src/ObjWriting/Game/IW4/Material/MaterialConstantZoneStateIW4.h b/src/ObjWriting/Game/IW4/Material/MaterialConstantZoneStateIW4.h new file mode 100644 index 00000000..edde10f8 --- /dev/null +++ b/src/ObjWriting/Game/IW4/Material/MaterialConstantZoneStateIW4.h @@ -0,0 +1,16 @@ +#pragma once + +#include "Material/AbstractMaterialConstantZoneState.h" + +#include + +namespace IW4 +{ + class MaterialConstantZoneState final : public AbstractMaterialConstantZoneStateDx9 + { + protected: + void ExtractNamesFromZoneInternal() override; + void AddStaticKnownNames() override; + unsigned HashString(const std::string& str) override; + }; +} // namespace IW4 diff --git a/src/ObjWriting/Game/IW4/ObjWriterIW4.cpp b/src/ObjWriting/Game/IW4/ObjWriterIW4.cpp index 4aabfd87..0516a4af 100644 --- a/src/ObjWriting/Game/IW4/ObjWriterIW4.cpp +++ b/src/ObjWriting/Game/IW4/ObjWriterIW4.cpp @@ -6,7 +6,6 @@ #include "AssetDumpers/AssetDumperLeaderboardDef.h" #include "AssetDumpers/AssetDumperLoadedSound.h" #include "AssetDumpers/AssetDumperLocalizeEntry.h" -#include "AssetDumpers/AssetDumperMaterial.h" #include "AssetDumpers/AssetDumperMenuDef.h" #include "AssetDumpers/AssetDumperMenuList.h" #include "AssetDumpers/AssetDumperPhysCollmap.h" @@ -23,6 +22,7 @@ #include "AssetDumpers/AssetDumperWeapon.h" #include "AssetDumpers/AssetDumperXModel.h" #include "Game/IW4/GameAssetPoolIW4.h" +#include "Material/DumperMaterialIW4.h" #include "ObjWriting.h" using namespace IW4; diff --git a/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperMaterial.cpp b/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperMaterial.cpp deleted file mode 100644 index c4efb8f9..00000000 --- a/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperMaterial.cpp +++ /dev/null @@ -1,48 +0,0 @@ -#include "AssetDumperMaterial.h" - -#include "Game/IW5/Material/JsonMaterialWriter.h" -#include "Game/IW5/Material/MaterialConstantZoneState.h" - -#include -#include -#include - -using namespace IW5; - -std::string AssetDumperMaterial::GetFileNameForAsset(const std::string& assetName) -{ - std::string sanitizedFileName(assetName); - if (sanitizedFileName[0] == '*') - { - std::ranges::replace(sanitizedFileName, '*', '_'); - const auto parenthesisPos = sanitizedFileName.find('('); - if (parenthesisPos != std::string::npos) - sanitizedFileName.erase(parenthesisPos); - sanitizedFileName = "generated/" + sanitizedFileName; - } - - return std::format("materials/{}.json", sanitizedFileName); -} - -bool AssetDumperMaterial::ShouldDump(XAssetInfo* asset) -{ - return true; -} - -void AssetDumperMaterial::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) -{ - const auto assetFile = context.OpenAssetFile(GetFileNameForAsset(asset->m_name)); - - if (!assetFile) - return; - - DumpMaterialAsJson(*assetFile, asset->Asset(), context); -} - -void AssetDumperMaterial::DumpPool(AssetDumpingContext& context, AssetPool* pool) -{ - auto* materialConstantState = context.GetZoneAssetDumperState(); - materialConstantState->ExtractNamesFromZone(); - - AbstractAssetDumper::DumpPool(context, pool); -} diff --git a/src/ObjWriting/Game/IW5/Leaderboard/JsonLeaderboardDefWriter.cpp b/src/ObjWriting/Game/IW5/Leaderboard/JsonLeaderboardDefWriter.cpp index 88dcfa42..ef365d79 100644 --- a/src/ObjWriting/Game/IW5/Leaderboard/JsonLeaderboardDefWriter.cpp +++ b/src/ObjWriting/Game/IW5/Leaderboard/JsonLeaderboardDefWriter.cpp @@ -28,6 +28,7 @@ namespace jRoot["_type"] = "leaderboard"; jRoot["_version"] = 1; + jRoot["_game"] = "iw5"; m_stream << std::setw(4) << jRoot << "\n"; } diff --git a/src/ObjWriting/Game/IW5/Material/DumperMaterialIW5.cpp b/src/ObjWriting/Game/IW5/Material/DumperMaterialIW5.cpp new file mode 100644 index 00000000..6b9f6e2c --- /dev/null +++ b/src/ObjWriting/Game/IW5/Material/DumperMaterialIW5.cpp @@ -0,0 +1,30 @@ +#include "DumperMaterialIW5.h" + +#include "Game/IW5/Material/JsonMaterialWriterIW5.h" +#include "Game/IW5/Material/MaterialConstantZoneStateIW5.h" +#include "Material/MaterialCommon.h" + +using namespace IW5; + +void AssetDumperMaterial::DumpPool(AssetDumpingContext& context, AssetPool* pool) +{ + auto* materialConstantState = context.GetZoneAssetDumperState(); + materialConstantState->ExtractNamesFromZone(); + + AbstractAssetDumper::DumpPool(context, pool); +} + +bool AssetDumperMaterial::ShouldDump(XAssetInfo* asset) +{ + return true; +} + +void AssetDumperMaterial::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) +{ + const auto assetFile = context.OpenAssetFile(material::GetFileNameForAssetName(asset->m_name)); + + if (!assetFile) + return; + + DumpMaterialAsJson(*assetFile, *asset->Asset(), context); +} diff --git a/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperMaterial.h b/src/ObjWriting/Game/IW5/Material/DumperMaterialIW5.h similarity index 82% rename from src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperMaterial.h rename to src/ObjWriting/Game/IW5/Material/DumperMaterialIW5.h index 04c8e914..12e57790 100644 --- a/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperMaterial.h +++ b/src/ObjWriting/Game/IW5/Material/DumperMaterialIW5.h @@ -3,19 +3,15 @@ #include "Dumping/AbstractAssetDumper.h" #include "Game/IW5/IW5.h" -#include - namespace IW5 { class AssetDumperMaterial final : public AbstractAssetDumper { - static std::string GetFileNameForAsset(const std::string& assetName); + public: + void DumpPool(AssetDumpingContext& context, AssetPool* pool) override; protected: bool ShouldDump(XAssetInfo* asset) override; void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; - - public: - void DumpPool(AssetDumpingContext& context, AssetPool* pool) override; }; } // namespace IW5 diff --git a/src/ObjWriting/Game/IW5/Material/JsonMaterialWriter.h b/src/ObjWriting/Game/IW5/Material/JsonMaterialWriter.h deleted file mode 100644 index c7869286..00000000 --- a/src/ObjWriting/Game/IW5/Material/JsonMaterialWriter.h +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -#include "Dumping/AssetDumpingContext.h" -#include "Game/IW5/IW5.h" - -#include - -namespace IW5 -{ - void DumpMaterialAsJson(std::ostream& stream, const Material* material, AssetDumpingContext& context); -} // namespace IW5 diff --git a/src/ObjWriting/Game/IW5/Material/MaterialConstantZoneState.cpp b/src/ObjWriting/Game/IW5/Material/MaterialConstantZoneStateIW5.cpp similarity index 99% rename from src/ObjWriting/Game/IW5/Material/MaterialConstantZoneState.cpp rename to src/ObjWriting/Game/IW5/Material/MaterialConstantZoneStateIW5.cpp index fe6d870d..7039a12c 100644 --- a/src/ObjWriting/Game/IW5/Material/MaterialConstantZoneState.cpp +++ b/src/ObjWriting/Game/IW5/Material/MaterialConstantZoneStateIW5.cpp @@ -1,4 +1,4 @@ -#include "MaterialConstantZoneState.h" +#include "MaterialConstantZoneStateIW5.h" #include "Game/IW5/CommonIW5.h" #include "Game/IW5/GameAssetPoolIW5.h" diff --git a/src/ObjWriting/Game/IW5/Material/MaterialConstantZoneState.h b/src/ObjWriting/Game/IW5/Material/MaterialConstantZoneStateIW5.h similarity index 100% rename from src/ObjWriting/Game/IW5/Material/MaterialConstantZoneState.h rename to src/ObjWriting/Game/IW5/Material/MaterialConstantZoneStateIW5.h diff --git a/src/ObjWriting/Game/IW5/ObjWriterIW5.cpp b/src/ObjWriting/Game/IW5/ObjWriterIW5.cpp index bbbca5ce..09a16378 100644 --- a/src/ObjWriting/Game/IW5/ObjWriterIW5.cpp +++ b/src/ObjWriting/Game/IW5/ObjWriterIW5.cpp @@ -5,7 +5,6 @@ #include "AssetDumpers/AssetDumperLeaderboardDef.h" #include "AssetDumpers/AssetDumperLoadedSound.h" #include "AssetDumpers/AssetDumperLocalizeEntry.h" -#include "AssetDumpers/AssetDumperMaterial.h" #include "AssetDumpers/AssetDumperMenuDef.h" #include "AssetDumpers/AssetDumperMenuList.h" #include "AssetDumpers/AssetDumperRawFile.h" @@ -15,6 +14,7 @@ #include "AssetDumpers/AssetDumperWeaponAttachment.h" #include "AssetDumpers/AssetDumperXModel.h" #include "Game/IW5/GameAssetPoolIW5.h" +#include "Material/DumperMaterialIW5.h" #include "ObjWriting.h" using namespace IW5; diff --git a/src/ObjWriting/Game/IW5/Weapon/JsonWeaponAttachmentWriter.cpp b/src/ObjWriting/Game/IW5/Weapon/JsonWeaponAttachmentWriter.cpp index 2c6fadcf..69f50071 100644 --- a/src/ObjWriting/Game/IW5/Weapon/JsonWeaponAttachmentWriter.cpp +++ b/src/ObjWriting/Game/IW5/Weapon/JsonWeaponAttachmentWriter.cpp @@ -27,6 +27,7 @@ namespace jRoot["_type"] = "attachment"; jRoot["_version"] = 1; + jRoot["_game"] = "iw5"; m_stream << std::setw(4) << jRoot << "\n"; } diff --git a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperMaterial.cpp b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperMaterial.cpp deleted file mode 100644 index b967a41e..00000000 --- a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperMaterial.cpp +++ /dev/null @@ -1,48 +0,0 @@ -#include "AssetDumperMaterial.h" - -#include "Game/T6/Material/JsonMaterialWriter.h" -#include "Game/T6/Material/MaterialConstantZoneState.h" - -#include -#include -#include - -using namespace T6; - -std::string AssetDumperMaterial::GetFileNameForAsset(const std::string& assetName) -{ - std::string sanitizedFileName(assetName); - if (sanitizedFileName[0] == '*') - { - std::ranges::replace(sanitizedFileName, '*', '_'); - const auto parenthesisPos = sanitizedFileName.find('('); - if (parenthesisPos != std::string::npos) - sanitizedFileName.erase(parenthesisPos); - sanitizedFileName = "generated/" + sanitizedFileName; - } - - return std::format("materials/{}.json", sanitizedFileName); -} - -bool AssetDumperMaterial::ShouldDump(XAssetInfo* asset) -{ - return true; -} - -void AssetDumperMaterial::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) -{ - const auto assetFile = context.OpenAssetFile(GetFileNameForAsset(asset->m_name)); - - if (!assetFile) - return; - - DumpMaterialAsJson(*assetFile, asset->Asset(), context); -} - -void AssetDumperMaterial::DumpPool(AssetDumpingContext& context, AssetPool* pool) -{ - auto* materialConstantState = context.GetZoneAssetDumperState(); - materialConstantState->ExtractNamesFromZone(); - - AbstractAssetDumper::DumpPool(context, pool); -} diff --git a/src/ObjWriting/Game/T6/Leaderboard/JsonLeaderboardDefWriter.cpp b/src/ObjWriting/Game/T6/Leaderboard/JsonLeaderboardDefWriter.cpp index 609c03d9..a559881f 100644 --- a/src/ObjWriting/Game/T6/Leaderboard/JsonLeaderboardDefWriter.cpp +++ b/src/ObjWriting/Game/T6/Leaderboard/JsonLeaderboardDefWriter.cpp @@ -28,6 +28,7 @@ namespace jRoot["_type"] = "leaderboard"; jRoot["_version"] = 1; + jRoot["_game"] = "t6"; m_stream << std::setw(4) << jRoot << "\n"; } diff --git a/src/ObjWriting/Game/T6/Material/DumperMaterialT6.cpp b/src/ObjWriting/Game/T6/Material/DumperMaterialT6.cpp new file mode 100644 index 00000000..a245cc18 --- /dev/null +++ b/src/ObjWriting/Game/T6/Material/DumperMaterialT6.cpp @@ -0,0 +1,30 @@ +#include "DumperMaterialT6.h" + +#include "Game/T6/Material/JsonMaterialWriterT6.h" +#include "Game/T6/Material/MaterialConstantZoneStateT6.h" +#include "Material/MaterialCommon.h" + +using namespace T6; + +void AssetDumperMaterial::DumpPool(AssetDumpingContext& context, AssetPool* pool) +{ + auto* materialConstantState = context.GetZoneAssetDumperState(); + materialConstantState->ExtractNamesFromZone(); + + AbstractAssetDumper::DumpPool(context, pool); +} + +bool AssetDumperMaterial::ShouldDump(XAssetInfo* asset) +{ + return true; +} + +void AssetDumperMaterial::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) +{ + const auto assetFile = context.OpenAssetFile(material::GetFileNameForAssetName(asset->m_name)); + + if (!assetFile) + return; + + DumpMaterialAsJson(*assetFile, *asset->Asset(), context); +} diff --git a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperMaterial.h b/src/ObjWriting/Game/T6/Material/DumperMaterialT6.h similarity index 82% rename from src/ObjWriting/Game/T6/AssetDumpers/AssetDumperMaterial.h rename to src/ObjWriting/Game/T6/Material/DumperMaterialT6.h index 9f8c8703..99de31e7 100644 --- a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperMaterial.h +++ b/src/ObjWriting/Game/T6/Material/DumperMaterialT6.h @@ -3,19 +3,15 @@ #include "Dumping/AbstractAssetDumper.h" #include "Game/T6/T6.h" -#include - namespace T6 { class AssetDumperMaterial final : public AbstractAssetDumper { - static std::string GetFileNameForAsset(const std::string& assetName); + public: + void DumpPool(AssetDumpingContext& context, AssetPool* pool) override; protected: bool ShouldDump(XAssetInfo* asset) override; void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; - - public: - void DumpPool(AssetDumpingContext& context, AssetPool* pool) override; }; } // namespace T6 diff --git a/src/ObjWriting/Game/T6/Material/JsonMaterialWriter.cpp b/src/ObjWriting/Game/T6/Material/JsonMaterialWriter.cpp deleted file mode 100644 index 75075265..00000000 --- a/src/ObjWriting/Game/T6/Material/JsonMaterialWriter.cpp +++ /dev/null @@ -1,249 +0,0 @@ -#include "JsonMaterialWriter.h" - -#include "Game/T6/CommonT6.h" -#include "Game/T6/Json/JsonMaterial.h" -#include "MaterialConstantZoneState.h" - -#include -#include - -using namespace nlohmann; -using namespace T6; - -namespace -{ - class JsonDumper - { - public: - JsonDumper(AssetDumpingContext& context, std::ostream& stream) - : m_stream(stream), - m_material_constants(*context.GetZoneAssetDumperState()) - { - } - - void Dump(const Material* material) const - { - JsonMaterial jsonMaterial; - CreateJsonMaterial(jsonMaterial, *material); - json jRoot = jsonMaterial; - - jRoot["_type"] = "material"; - 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; - } - - static void CreateJsonGameFlags(JsonMaterial& jMaterial, const unsigned gameFlags) - { - jMaterial.gameFlags.clear(); - for (auto i = 0u; i < sizeof(gameFlags) * 8u; i++) - { - const auto flag = static_cast(1 << i); - - if (gameFlags & flag) - jMaterial.gameFlags.emplace_back(flag); - } - } - - static void CreateJsonSamplerState(JsonSamplerState& jSamplerState, const MaterialTextureDefSamplerState& samplerState) - { - jSamplerState.filter = static_cast(samplerState.filter); - jSamplerState.mipMap = static_cast(samplerState.mipMap); - jSamplerState.clampU = samplerState.clampU; - jSamplerState.clampV = samplerState.clampV; - jSamplerState.clampW = samplerState.clampW; - } - - void CreateJsonTexture(JsonTexture& jTextureDef, const MaterialTextureDef& textureDef) const - { - std::string textureDefName; - if (m_material_constants.GetTextureDefName(textureDef.nameHash, textureDefName)) - { - jTextureDef.name = textureDefName; - } - else - { - jTextureDef.nameHash = textureDef.nameHash; - jTextureDef.nameStart = std::string(1u, textureDef.nameStart); - jTextureDef.nameEnd = std::string(1u, textureDef.nameEnd); - } - - jTextureDef.semantic = static_cast(textureDef.semantic); - jTextureDef.isMatureContent = textureDef.isMatureContent; - - CreateJsonSamplerState(jTextureDef.samplerState, textureDef.samplerState); - - if (textureDef.image && textureDef.image->name) - jTextureDef.image = AssetName(textureDef.image->name); - } - - void CreateJsonConstant(JsonConstant& jConstantDef, const MaterialConstantDef& constantDef) const - { - const auto fragmentLength = strnlen(constantDef.name, std::extent_v); - const std::string nameFragment(constantDef.name, fragmentLength); - std::string knownConstantName; - - if (fragmentLength < std::extent_v || Common::R_HashString(nameFragment.c_str(), 0) == constantDef.nameHash) - { - jConstantDef.name = nameFragment; - } - else if (m_material_constants.GetConstantName(constantDef.nameHash, knownConstantName)) - { - jConstantDef.name = knownConstantName; - } - else - { - jConstantDef.nameHash = constantDef.nameHash; - jConstantDef.nameFragment = nameFragment; - } - - jConstantDef.literal = std::vector({ - constantDef.literal.x, - constantDef.literal.y, - constantDef.literal.z, - constantDef.literal.w, - }); - } - - static void CreateJsonStencil(JsonStencil& jStencil, const unsigned pass, const unsigned fail, const unsigned zFail, const unsigned func) - { - jStencil.pass = static_cast(pass); - jStencil.fail = static_cast(fail); - jStencil.zfail = static_cast(zFail); - jStencil.func = static_cast(func); - } - - static void CreateJsonStateBitsTableEntry(JsonStateBitsTableEntry& jStateBitsTableEntry, const GfxStateBitsTable& stateBitsTableEntry) - { - const auto& structured = stateBitsTableEntry.loadBits.structured; - - jStateBitsTableEntry.srcBlendRgb = static_cast(structured.srcBlendRgb); - jStateBitsTableEntry.dstBlendRgb = static_cast(structured.dstBlendRgb); - jStateBitsTableEntry.blendOpRgb = static_cast(structured.blendOpRgb); - - assert(structured.alphaTestDisabled || structured.alphaTest == GFXS_ALPHA_TEST_GT_0 || structured.alphaTest == GFXS_ALPHA_TEST_GE_128); - if (structured.alphaTestDisabled) - jStateBitsTableEntry.alphaTest = JsonAlphaTest::DISABLED; - else if (structured.alphaTest == GFXS_ALPHA_TEST_GT_0) - jStateBitsTableEntry.alphaTest = JsonAlphaTest::GT0; - else if (structured.alphaTest == GFXS_ALPHA_TEST_GE_128) - jStateBitsTableEntry.alphaTest = JsonAlphaTest::GE128; - else - jStateBitsTableEntry.alphaTest = JsonAlphaTest::INVALID; - - assert(structured.cullFace == GFXS0_CULL_NONE || structured.cullFace == GFXS0_CULL_BACK || structured.cullFace == GFXS0_CULL_FRONT); - if (structured.cullFace == GFXS0_CULL_NONE) - jStateBitsTableEntry.cullFace = JsonCullFace::NONE; - else if (structured.cullFace == GFXS0_CULL_BACK) - jStateBitsTableEntry.cullFace = JsonCullFace::BACK; - else if (structured.cullFace == GFXS0_CULL_FRONT) - jStateBitsTableEntry.cullFace = JsonCullFace::FRONT; - else - jStateBitsTableEntry.cullFace = JsonCullFace::INVALID; - - jStateBitsTableEntry.srcBlendAlpha = static_cast(structured.srcBlendAlpha); - jStateBitsTableEntry.dstBlendAlpha = static_cast(structured.dstBlendAlpha); - jStateBitsTableEntry.blendOpAlpha = static_cast(structured.blendOpAlpha); - jStateBitsTableEntry.colorWriteRgb = structured.colorWriteRgb; - jStateBitsTableEntry.colorWriteAlpha = structured.colorWriteAlpha; - jStateBitsTableEntry.polymodeLine = structured.polymodeLine; - jStateBitsTableEntry.depthWrite = structured.depthWrite; - - assert(structured.depthTestDisabled || structured.depthTest == GFXS_DEPTHTEST_ALWAYS || structured.depthTest == GFXS_DEPTHTEST_LESS - || structured.depthTest == GFXS_DEPTHTEST_EQUAL || structured.depthTest == GFXS_DEPTHTEST_LESSEQUAL); - if (structured.depthTestDisabled) - jStateBitsTableEntry.depthTest = JsonDepthTest::DISABLED; - else if (structured.depthTest == GFXS_DEPTHTEST_ALWAYS) - jStateBitsTableEntry.depthTest = JsonDepthTest::ALWAYS; - else if (structured.depthTest == GFXS_DEPTHTEST_LESS) - jStateBitsTableEntry.depthTest = JsonDepthTest::LESS; - else if (structured.depthTest == GFXS_DEPTHTEST_EQUAL) - jStateBitsTableEntry.depthTest = JsonDepthTest::EQUAL; - else if (structured.depthTest == GFXS_DEPTHTEST_LESSEQUAL) - jStateBitsTableEntry.depthTest = JsonDepthTest::LESS_EQUAL; - else - jStateBitsTableEntry.depthTest = JsonDepthTest::INVALID; - - jStateBitsTableEntry.polygonOffset = static_cast(structured.polygonOffset); - - if (structured.stencilFrontEnabled) - { - JsonStencil jStencilFront; - CreateJsonStencil( - jStencilFront, structured.stencilFrontPass, structured.stencilFrontFail, structured.stencilFrontZFail, structured.stencilFrontFunc); - jStateBitsTableEntry.stencilFront = jStencilFront; - } - - if (structured.stencilBackEnabled) - { - JsonStencil jStencilBack; - CreateJsonStencil( - jStencilBack, structured.stencilBackPass, structured.stencilBackFail, structured.stencilBackZFail, structured.stencilBackFunc); - jStateBitsTableEntry.stencilBack = jStencilBack; - } - } - - void CreateJsonMaterial(JsonMaterial& jMaterial, const Material& material) const - { - CreateJsonGameFlags(jMaterial, material.info.gameFlags); - jMaterial.sortKey = material.info.sortKey; - - jMaterial.textureAtlas = JsonTextureAtlas(); - jMaterial.textureAtlas->rows = material.info.textureAtlasRowCount; - jMaterial.textureAtlas->columns = material.info.textureAtlasColumnCount; - - jMaterial.surfaceTypeBits = material.info.surfaceTypeBits; - jMaterial.layeredSurfaceTypes = material.info.layeredSurfaceTypes; - jMaterial.hashIndex = material.info.hashIndex; - jMaterial.surfaceFlags = material.info.surfaceFlags; - jMaterial.contents = material.info.contents; - - jMaterial.stateBitsEntry.resize(std::extent_v); - for (auto i = 0u; i < std::extent_v; i++) - jMaterial.stateBitsEntry[i] = material.stateBitsEntry[i]; - - jMaterial.stateFlags = material.stateFlags; - jMaterial.cameraRegion = static_cast(material.cameraRegion); - jMaterial.probeMipBits = material.probeMipBits; - - if (material.techniqueSet && material.techniqueSet->name) - jMaterial.techniqueSet = AssetName(material.techniqueSet->name); - - jMaterial.textures.resize(material.textureCount); - for (auto i = 0u; i < material.textureCount; i++) - CreateJsonTexture(jMaterial.textures[i], material.textureTable[i]); - - jMaterial.constants.resize(material.constantCount); - for (auto i = 0u; i < material.constantCount; i++) - CreateJsonConstant(jMaterial.constants[i], material.constantTable[i]); - - jMaterial.stateBits.resize(material.stateBitsCount); - for (auto i = 0u; i < material.stateBitsCount; i++) - CreateJsonStateBitsTableEntry(jMaterial.stateBits[i], material.stateBitsTable[i]); - - if (material.thermalMaterial && material.thermalMaterial->info.name) - jMaterial.thermalMaterial = AssetName(material.thermalMaterial->info.name); - } - - std::ostream& m_stream; - const MaterialConstantZoneState& m_material_constants; - }; -} // namespace - -namespace T6 -{ - void DumpMaterialAsJson(std::ostream& stream, const Material* material, AssetDumpingContext& context) - { - const JsonDumper dumper(context, stream); - dumper.Dump(material); - } -} // namespace T6 diff --git a/src/ObjWriting/Game/T6/Material/JsonMaterialWriter.h b/src/ObjWriting/Game/T6/Material/JsonMaterialWriter.h deleted file mode 100644 index ac012e4f..00000000 --- a/src/ObjWriting/Game/T6/Material/JsonMaterialWriter.h +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -#include "Dumping/AssetDumpingContext.h" -#include "Game/T6/T6.h" - -#include - -namespace T6 -{ - void DumpMaterialAsJson(std::ostream& stream, const Material* material, AssetDumpingContext& context); -} // namespace T6 diff --git a/src/ObjWriting/Game/T6/Material/MaterialConstantZoneState.cpp b/src/ObjWriting/Game/T6/Material/MaterialConstantZoneStateT6.cpp similarity index 99% rename from src/ObjWriting/Game/T6/Material/MaterialConstantZoneState.cpp rename to src/ObjWriting/Game/T6/Material/MaterialConstantZoneStateT6.cpp index 5492c775..352824f8 100644 --- a/src/ObjWriting/Game/T6/Material/MaterialConstantZoneState.cpp +++ b/src/ObjWriting/Game/T6/Material/MaterialConstantZoneStateT6.cpp @@ -1,4 +1,4 @@ -#include "MaterialConstantZoneState.h" +#include "MaterialConstantZoneStateT6.h" #include "Game/T6/CommonT6.h" #include "Game/T6/GameAssetPoolT6.h" diff --git a/src/ObjWriting/Game/T6/Material/MaterialConstantZoneState.h b/src/ObjWriting/Game/T6/Material/MaterialConstantZoneStateT6.h similarity index 100% rename from src/ObjWriting/Game/T6/Material/MaterialConstantZoneState.h rename to src/ObjWriting/Game/T6/Material/MaterialConstantZoneStateT6.h diff --git a/src/ObjWriting/Game/T6/ObjWriterT6.cpp b/src/ObjWriting/Game/T6/ObjWriterT6.cpp index 88df25d8..bb4bcb15 100644 --- a/src/ObjWriting/Game/T6/ObjWriterT6.cpp +++ b/src/ObjWriting/Game/T6/ObjWriterT6.cpp @@ -5,7 +5,6 @@ #include "AssetDumpers/AssetDumperLeaderboardDef.h" #include "AssetDumpers/AssetDumperLocalizeEntry.h" #include "AssetDumpers/AssetDumperMapEnts.h" -#include "AssetDumpers/AssetDumperMaterial.h" #include "AssetDumpers/AssetDumperPhysConstraints.h" #include "AssetDumpers/AssetDumperPhysPreset.h" #include "AssetDumpers/AssetDumperQdb.h" @@ -25,6 +24,7 @@ #include "AssetDumpers/AssetDumperXModel.h" #include "AssetDumpers/AssetDumperZBarrier.h" #include "Game/T6/GameAssetPoolT6.h" +#include "Material/DumperMaterialT6.h" #include "ObjWriting.h" using namespace T6; diff --git a/src/ObjWriting/Game/T6/WeaponCamo/JsonWeaponCamoWriter.cpp b/src/ObjWriting/Game/T6/WeaponCamo/JsonWeaponCamoWriter.cpp index ad40d536..3438dae8 100644 --- a/src/ObjWriting/Game/T6/WeaponCamo/JsonWeaponCamoWriter.cpp +++ b/src/ObjWriting/Game/T6/WeaponCamo/JsonWeaponCamoWriter.cpp @@ -27,6 +27,7 @@ namespace jRoot["_type"] = "weaponCamo"; jRoot["_version"] = 1; + jRoot["_game"] = "t6"; m_stream << std::setw(4) << jRoot << "\n"; } diff --git a/src/ObjWriting/Game/IW5/Material/JsonMaterialWriter.cpp b/src/ObjWriting/Material/JsonMaterialWriter.cpp.template similarity index 83% rename from src/ObjWriting/Game/IW5/Material/JsonMaterialWriter.cpp rename to src/ObjWriting/Material/JsonMaterialWriter.cpp.template index ff36505a..2c0ef0f0 100644 --- a/src/ObjWriting/Game/IW5/Material/JsonMaterialWriter.cpp +++ b/src/ObjWriting/Material/JsonMaterialWriter.cpp.template @@ -1,15 +1,43 @@ -#include "JsonMaterialWriter.h" +#options GAME (IW4, IW5, T6) +#filename "Game/" + GAME + "/Material/JsonMaterialWriter" + GAME + ".cpp" + +#if GAME == "IW4" +#define FEATURE_IW4 +#define HAS_WATER +#define GAME_LOWER "iw4" +#elif GAME == "IW5" +#define FEATURE_IW5 +#define HAS_WATER +#define GAME_LOWER "iw5" +#elif GAME == "T6" +#define FEATURE_T6 +#define GAME_LOWER "t6" +#endif + +// This file was templated. +// See JsonMaterialWriter.cpp.template. +// Do not modify, changes will be lost. + +#set WRITER_HEADER "\"JsonMaterialWriter" + GAME + ".h\"" +#include WRITER_HEADER + +#ifdef HAS_WATER #include "Base64.h" -#include "Game/IW5/CommonIW5.h" -#include "Game/IW5/Material/JsonMaterial.h" -#include "MaterialConstantZoneState.h" +#endif + +#set COMMON_HEADER "\"Game/" + GAME + "/Common" + GAME + ".h\"" +#include COMMON_HEADER +#set JSON_HEADER "\"Game/" + GAME + "/Material/JsonMaterial" + GAME + ".h\"" +#include JSON_HEADER +#set CONSTANTS_HEADER "\"Game/" + GAME + "/Material/MaterialConstantZoneState" + GAME + ".h\"" +#include CONSTANTS_HEADER #include #include using namespace nlohmann; -using namespace IW5; +using namespace GAME; namespace { @@ -22,15 +50,15 @@ namespace { } - void Dump(const Material* material) const + void Dump(const Material& material) const { JsonMaterial jsonMaterial; - CreateJsonMaterial(jsonMaterial, *material); + CreateJsonMaterial(jsonMaterial, material); json jRoot = jsonMaterial; jRoot["_type"] = "material"; - jRoot["_game"] = "iw5"; jRoot["_version"] = 1; + jRoot["_game"] = GAME_LOWER; m_stream << std::setw(4) << jRoot << "\n"; } @@ -65,6 +93,7 @@ namespace jSamplerState.clampW = samplerState.clampW; } +#ifdef HAS_WATER static void CreateJsonWater(JsonWater& jWater, const water_t& water) { jWater.floatTime = water.writable.floatTime; @@ -94,6 +123,7 @@ namespace jWater.wTerm = base64::EncodeBase64(water.wTerm, sizeof(float) * count); } } +#endif void CreateJsonTexture(JsonTexture& jTextureDef, const MaterialTextureDef& textureDef) const { @@ -110,9 +140,13 @@ namespace } jTextureDef.semantic = static_cast(textureDef.semantic); +#if defined(FEATURE_T6) + jTextureDef.isMatureContent = textureDef.isMatureContent; +#endif CreateJsonSamplerState(jTextureDef.samplerState, textureDef.samplerState); +#ifdef HAS_WATER if (textureDef.semantic == TS_WATER_MAP) { if (textureDef.u.water) @@ -132,6 +166,10 @@ namespace if (textureDef.u.image && textureDef.u.image->name) jTextureDef.image = AssetName(textureDef.u.image->name); } +#else + if (textureDef.image && textureDef.image->name) + jTextureDef.image = AssetName(textureDef.image->name); +#endif } void CreateJsonConstant(JsonConstant& jConstantDef, const MaterialConstantDef& constantDef) const @@ -178,14 +216,20 @@ namespace jStateBitsTableEntry.dstBlendRgb = static_cast(structured.dstBlendRgb); jStateBitsTableEntry.blendOpRgb = static_cast(structured.blendOpRgb); - assert(structured.alphaTestDisabled || structured.alphaTest == GFXS_ALPHA_TEST_GT_0 || structured.alphaTest == GFXS_ALPHA_TEST_LT_128 - || structured.alphaTest == GFXS_ALPHA_TEST_GE_128); + assert(structured.alphaTestDisabled + || structured.alphaTest == GFXS_ALPHA_TEST_GT_0 +#if defined(FEATURE_IW4) || defined(FEATURE_IW5) + || structured.alphaTest == GFXS_ALPHA_TEST_LT_128 +#endif + || structured.alphaTest == GFXS_ALPHA_TEST_GE_128); if (structured.alphaTestDisabled) jStateBitsTableEntry.alphaTest = JsonAlphaTest::DISABLED; else if (structured.alphaTest == GFXS_ALPHA_TEST_GT_0) jStateBitsTableEntry.alphaTest = JsonAlphaTest::GT0; +#if defined(FEATURE_IW4) || defined(FEATURE_IW5) else if (structured.alphaTest == GFXS_ALPHA_TEST_LT_128) jStateBitsTableEntry.alphaTest = JsonAlphaTest::LT128; +#endif else if (structured.alphaTest == GFXS_ALPHA_TEST_GE_128) jStateBitsTableEntry.alphaTest = JsonAlphaTest::GE128; else @@ -206,7 +250,9 @@ namespace jStateBitsTableEntry.blendOpAlpha = static_cast(structured.blendOpAlpha); jStateBitsTableEntry.colorWriteRgb = structured.colorWriteRgb; jStateBitsTableEntry.colorWriteAlpha = structured.colorWriteAlpha; +#if defined(FEATURE_IW4) || defined(FEATURE_IW5) jStateBitsTableEntry.gammaWrite = structured.gammaWrite; +#endif jStateBitsTableEntry.polymodeLine = structured.polymodeLine; jStateBitsTableEntry.depthWrite = structured.depthWrite; @@ -254,6 +300,12 @@ namespace jMaterial.textureAtlas->columns = material.info.textureAtlasColumnCount; jMaterial.surfaceTypeBits = material.info.surfaceTypeBits; +#ifdef FEATURE_T6 + jMaterial.layeredSurfaceTypes = material.info.layeredSurfaceTypes; + jMaterial.hashIndex = material.info.hashIndex; + jMaterial.surfaceFlags = material.info.surfaceFlags; + jMaterial.contents = material.info.contents; +#endif jMaterial.stateBitsEntry.resize(std::extent_v); for (auto i = 0u; i < std::extent_v; i++) @@ -261,6 +313,9 @@ namespace jMaterial.stateFlags = material.stateFlags; jMaterial.cameraRegion = static_cast(material.cameraRegion); +#ifdef FEATURE_T6 + jMaterial.probeMipBits = material.probeMipBits; +#endif if (material.techniqueSet && material.techniqueSet->name) jMaterial.techniqueSet = AssetName(material.techniqueSet->name); @@ -276,6 +331,11 @@ namespace jMaterial.stateBits.resize(material.stateBitsCount); for (auto i = 0u; i < material.stateBitsCount; i++) CreateJsonStateBitsTableEntry(jMaterial.stateBits[i], material.stateBitsTable[i]); + +#ifdef FEATURE_T6 + if (material.thermalMaterial && material.thermalMaterial->info.name) + jMaterial.thermalMaterial = AssetName(material.thermalMaterial->info.name); +#endif } std::ostream& m_stream; @@ -283,11 +343,11 @@ namespace }; } // namespace -namespace IW5 +namespace GAME { - void DumpMaterialAsJson(std::ostream& stream, const Material* material, AssetDumpingContext& context) + void DumpMaterialAsJson(std::ostream& stream, const Material& material, AssetDumpingContext& context) { const JsonDumper dumper(context, stream); dumper.Dump(material); } -} // namespace IW5 +} // namespace GAME diff --git a/src/ObjWriting/Material/JsonMaterialWriter.h.template b/src/ObjWriting/Material/JsonMaterialWriter.h.template new file mode 100644 index 00000000..d44748db --- /dev/null +++ b/src/ObjWriting/Material/JsonMaterialWriter.h.template @@ -0,0 +1,20 @@ +#options GAME (IW4, IW5, T6) + +#filename "Game/" + GAME + "/Material/JsonMaterialWriter" + GAME + ".h" + +// This file was templated. +// See JsonMaterialWriter.h.template. +// Do not modify, changes will be lost. + +#pragma once + +#include "Dumping/AssetDumpingContext.h" +#set GAME_HEADER "\"Game/" + GAME + "/" + GAME + ".h\"" +#include GAME_HEADER + +#include + +namespace GAME +{ + void DumpMaterialAsJson(std::ostream& stream, const Material& material, AssetDumpingContext& context); +} // namespace GAME diff --git a/src/ObjWriting/XModel/GenericXModelDumper.inc.h b/src/ObjWriting/XModel/GenericXModelDumper.inc.h deleted file mode 100644 index 07f4fd88..00000000 --- a/src/ObjWriting/XModel/GenericXModelDumper.inc.h +++ /dev/null @@ -1,687 +0,0 @@ -#pragma once - -#ifndef GAME_NAMESPACE -#error Must define GAME_NAMESPACE -#endif - -#include "Game/T6/CommonT6.h" -#include "ObjWriting.h" -#include "Utils/DistinctMapper.h" -#include "Utils/QuatInt16.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 - -namespace GAME_NAMESPACE -{ - inline std::string GetFileNameForLod(const std::string& modelName, const unsigned lod, const std::string& extension) - { - return std::format("model_export/{}_lod{}{}", modelName, lod, extension); - } - - inline GfxImage* GetImageFromTextureDef(const MaterialTextureDef& textureDef) - { -#ifdef FEATURE_T6 - return textureDef.image; -#else - return textureDef.u.image; -#endif - } - - inline 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 GetImageFromTextureDef(*potentialTextureDefs[0]); - - for (const auto* def : potentialTextureDefs) - { - if (tolower(def->nameStart) == 'c' && tolower(def->nameEnd) == 'p') - return GetImageFromTextureDef(*def); - } - - for (const auto* def : potentialTextureDefs) - { - if (tolower(def->nameStart) == 'r' && tolower(def->nameEnd) == 'k') - return GetImageFromTextureDef(*def); - } - - for (const auto* def : potentialTextureDefs) - { - if (tolower(def->nameStart) == 'd' && tolower(def->nameEnd) == 'p') - return GetImageFromTextureDef(*def); - } - - return GetImageFromTextureDef(*potentialTextureDefs[0]); - } - - inline 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 GetImageFromTextureDef(*potentialTextureDefs[0]); - - for (const auto* def : potentialTextureDefs) - { - if (def->nameStart == 'n' && def->nameEnd == 'p') - return GetImageFromTextureDef(*def); - } - - return GetImageFromTextureDef(*potentialTextureDefs[0]); - } - - inline 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 GetImageFromTextureDef(*potentialTextureDefs[0]); - - for (const auto* def : potentialTextureDefs) - { - if (def->nameStart == 's' && def->nameEnd == 'p') - return GetImageFromTextureDef(*def); - } - - return GetImageFromTextureDef(*potentialTextureDefs[0]); - } - - inline bool HasDefaultArmature(const XModel* model, const unsigned lod) - { - if (model->numRootBones != 1 || model->numBones != 1) - return false; - - const auto* surfs = &model->surfs[model->lodInfo[lod].surfIndex]; - const auto surfCount = model->lodInfo[lod].numsurfs; - - if (!surfs) - return true; - - for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++) - { - const auto& surface = surfs[surfIndex]; - - if (surface.vertListCount != 1 || surface.vertInfo.vertsBlend) - return false; - - const auto& vertList = surface.vertList[0]; - if (vertList.boneOffset != 0 || vertList.triOffset != 0 || vertList.triCount != surface.triCount || vertList.vertCount != surface.vertCount) - return false; - } - - return true; - } - - inline void OmitDefaultArmature(XModelCommon& common) - { - common.m_bones.clear(); - common.m_bone_weight_data.weights.clear(); - common.m_vertex_bone_weights.resize(common.m_vertices.size()); - for (auto& vertexWeights : common.m_vertex_bone_weights) - { - vertexWeights.weightOffset = 0u; - vertexWeights.weightCount = 0u; - } - } - - inline void AddXModelBones(XModelCommon& out, const AssetDumpingContext& context, 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 = static_cast(boneNum - static_cast(model->parentList[boneNum - model->numRootBones])); - else - bone.parentIndex = std::nullopt; - - bone.scale[0] = 1.0f; - bone.scale[1] = 1.0f; - bone.scale[2] = 1.0f; - - const auto& baseMat = model->baseMat[boneNum]; - bone.globalOffset[0] = baseMat.trans.x; - bone.globalOffset[1] = baseMat.trans.y; - bone.globalOffset[2] = baseMat.trans.z; - bone.globalRotation = { - baseMat.quat.x, - baseMat.quat.y, - baseMat.quat.z, - baseMat.quat.w, - }; - - if (boneNum < model->numRootBones) - { - bone.localOffset[0] = 0; - bone.localOffset[1] = 0; - bone.localOffset[2] = 0; - bone.localRotation = {0, 0, 0, 1}; - } - else - { - const auto* trans = &model->trans[(boneNum - model->numRootBones) * 3]; - bone.localOffset[0] = trans[0]; - bone.localOffset[1] = trans[1]; - bone.localOffset[2] = trans[2]; - - const auto& quat = model->quats[boneNum - model->numRootBones]; - bone.localRotation = { - QuatInt16::ToFloat(quat.v[0]), - QuatInt16::ToFloat(quat.v[1]), - QuatInt16::ToFloat(quat.v[2]), - QuatInt16::ToFloat(quat.v[3]), - }; - } - - out.m_bones.emplace_back(std::move(bone)); - } - } - - inline const char* AssetName(const char* input) - { - if (input && input[0] == ',') - return &input[1]; - - return input; - } - - inline void AddXModelMaterials(XModelCommon& out, 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 = AssetName(material->info.name); - const auto* colorMap = GetMaterialColorMap(material); - if (colorMap) - xMaterial.colorMapName = AssetName(colorMap->name); - - const auto* normalMap = GetMaterialNormalMap(material); - if (normalMap) - xMaterial.normalMapName = AssetName(normalMap->name); - - const auto* specularMap = GetMaterialSpecularMap(material); - if (specularMap) - xMaterial.specularMapName = AssetName(specularMap->name); - - out.m_materials.emplace_back(std::move(xMaterial)); - } - } - } - - inline 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; - - for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++) - { - XModelObject object; - object.name = std::format("surf{}", surfIndex); - object.materialIndex = static_cast(materialMapper.GetDistinctPositionByInputPosition(surfIndex + baseSurfaceIndex)); - - out.m_objects.emplace_back(std::move(object)); - } - } - - inline 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; - - 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]; - - XModelVertex vertex{}; - vertex.coordinates[0] = v.xyz.x; - vertex.coordinates[1] = v.xyz.y; - vertex.coordinates[2] = v.xyz.z; - Common::Vec3UnpackUnitVec(v.normal, vertex.normal); - Common::Vec4UnpackGfxColor(v.color, vertex.color); - Common::Vec2UnpackTexCoords(v.texCoord, vertex.uv); - - out.m_vertices.emplace_back(vertex); - } - } - } - - inline 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; - - auto totalWeightCount = 0u; - for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++) - { - const auto& surface = surfs[surfIndex]; - - if (surface.vertList) - { - totalWeightCount += surface.vertListCount; - } - - if (surface.vertInfo.vertsBlend) - { - 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.resize(totalWeightCount); - } - - inline float BoneWeight16(const uint16_t value) - { - return static_cast(value) / static_cast(std::numeric_limits::max()); - } - - inline 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; - - auto 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 = weightOffset; - - weightCollection.weights[weightOffset++] = XModelBoneWeight{vertList.boneOffset / sizeof(DObjSkelMat), 1.0f}; - - for (auto vertListVertexOffset = 0u; vertListVertexOffset < vertList.vertCount; vertListVertexOffset++) - { - out.m_vertex_bone_weights.emplace_back(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 = weightOffset; - const auto boneIndex0 = surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat); - weightCollection.weights[weightOffset++] = XModelBoneWeight{boneIndex0, 1.0f}; - - vertsBlendOffset += 1; - - out.m_vertex_bone_weights.emplace_back(boneWeightOffset, 1); - } - - // 2 bone weights - for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[1]; vertIndex++) - { - const auto boneWeightOffset = weightOffset; - const auto boneIndex0 = surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat); - const auto boneIndex1 = surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat); - const auto boneWeight1 = BoneWeight16(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; - - out.m_vertex_bone_weights.emplace_back(boneWeightOffset, 2); - } - - // 3 bone weights - for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[2]; vertIndex++) - { - const auto boneWeightOffset = weightOffset; - const auto boneIndex0 = surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat); - const auto boneIndex1 = surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat); - const auto boneWeight1 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 2]); - const auto boneIndex2 = surface.vertInfo.vertsBlend[vertsBlendOffset + 3] / sizeof(DObjSkelMat); - const auto boneWeight2 = BoneWeight16(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; - - out.m_vertex_bone_weights.emplace_back(boneWeightOffset, 3); - } - - // 4 bone weights - for (auto vertIndex = 0; vertIndex < surface.vertInfo.vertCount[3]; vertIndex++) - { - const auto boneWeightOffset = weightOffset; - const auto boneIndex0 = surface.vertInfo.vertsBlend[vertsBlendOffset + 0] / sizeof(DObjSkelMat); - const auto boneIndex1 = surface.vertInfo.vertsBlend[vertsBlendOffset + 1] / sizeof(DObjSkelMat); - const auto boneWeight1 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 2]); - const auto boneIndex2 = surface.vertInfo.vertsBlend[vertsBlendOffset + 3] / sizeof(DObjSkelMat); - const auto boneWeight2 = BoneWeight16(surface.vertInfo.vertsBlend[vertsBlendOffset + 4]); - const auto boneIndex3 = surface.vertInfo.vertsBlend[vertsBlendOffset + 5] / sizeof(DObjSkelMat); - const auto boneWeight3 = BoneWeight16(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; - - out.m_vertex_bone_weights.emplace_back(boneWeightOffset, 4); - } - - handledVertices += - surface.vertInfo.vertCount[0] + surface.vertInfo.vertCount[1] + surface.vertInfo.vertCount[2] + surface.vertInfo.vertCount[3]; - } - - for (; handledVertices < surface.vertCount; handledVertices++) - { - out.m_vertex_bone_weights.emplace_back(0, 0); - } - } - } - - inline 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; - - if (!surfs) - return; - - 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]; - - XModelFace face{}; - face.vertexIndex[0] = tri.i[0] + surface.baseVertIndex; - face.vertexIndex[1] = tri.i[1] + surface.baseVertIndex; - face.vertexIndex[2] = tri.i[2] + surface.baseVertIndex; - object.m_faces.emplace_back(face); - } - } - } - - inline void PopulateXModelWriter(XModelCommon& out, const AssetDumpingContext& context, const unsigned lod, const XModel* model) - { - DistinctMapper materialMapper(model->numsurfs); - AllocateXModelBoneWeights(model, lod, out.m_bone_weight_data); - - out.m_name = std::format("{}_lod{}", model->name, lod); - AddXModelMaterials(out, materialMapper, model); - AddXModelObjects(out, model, lod, materialMapper); - AddXModelVertices(out, model, lod); - AddXModelFaces(out, model, lod); - - if (!HasDefaultArmature(model, lod)) - { - AddXModelBones(out, context, model); - AddXModelVertexBoneWeights(out, model, lod); - } - else - { - OmitDefaultArmature(out); - } - } - - inline 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); - } - - inline 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); - } - - inline 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")); - - if (!assetFile) - return; - - 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 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)); - - 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); - - writer->Write(common); - } - - inline void DumpXModelSurfs(const AssetDumpingContext& context, const XAssetInfo* asset) - { - const auto* model = asset->Asset(); - - 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: - DumpObjLod(common, context, asset, currentLod); - if (currentLod == 0u) - DumpObjMtl(common, context, asset); - break; - - case ObjWriting::Configuration_t::ModelOutputFormat_e::XMODEL_EXPORT: - DumpXModelExportLod(common, context, asset, currentLod); - break; - - case ObjWriting::Configuration_t::ModelOutputFormat_e::GLTF: - DumpGltfLod(common, context, asset, currentLod, ".gltf"); - break; - - case ObjWriting::Configuration_t::ModelOutputFormat_e::GLB: - DumpGltfLod(common, context, asset, currentLod, ".glb"); - break; - - default: - assert(false); - break; - } - } - } - - class JsonDumper - { - public: - JsonDumper(AssetDumpingContext& context, std::ostream& stream) - : m_stream(stream) - { - } - - void Dump(const XModel* xmodel) const - { - JsonXModel jsonXModel; - CreateJsonXModel(jsonXModel, *xmodel); - nlohmann::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; - } - - static const char* GetExtensionForModelByConfig() - { - switch (ObjWriting::Configuration.ModelOutputFormat) - { - case ObjWriting::Configuration_t::ModelOutputFormat_e::XMODEL_EXPORT: - return ".XMODEL_EXPORT"; - case ObjWriting::Configuration_t::ModelOutputFormat_e::OBJ: - return ".OBJ"; - case ObjWriting::Configuration_t::ModelOutputFormat_e::GLTF: - return ".GLTF"; - case ObjWriting::Configuration_t::ModelOutputFormat_e::GLB: - return ".GLB"; - default: - assert(false); - return ""; - } - } - - static void CreateJsonXModel(JsonXModel& jXModel, const XModel& xmodel) - { - if (xmodel.collLod >= 0) - jXModel.collLod = xmodel.collLod; - - for (auto lodNumber = 0u; lodNumber < xmodel.numLods; lodNumber++) - { - JsonXModelLod lod; - lod.file = std::format("model_export/{}_lod{}{}", xmodel.name, lodNumber, GetExtensionForModelByConfig()); - lod.distance = xmodel.lodInfo[lodNumber].dist; - - jXModel.lods.emplace_back(std::move(lod)); - } - - 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; - -#ifdef FEATURE_T6 - jXModel.lightingOriginOffset.x = xmodel.lightingOriginOffset.x; - jXModel.lightingOriginOffset.y = xmodel.lightingOriginOffset.y; - jXModel.lightingOriginOffset.z = xmodel.lightingOriginOffset.z; - jXModel.lightingOriginRange = xmodel.lightingOriginRange; -#endif - } - - std::ostream& m_stream; - }; - - inline void DumpXModelJson(AssetDumpingContext& context, XAssetInfo* asset) - { - const auto assetFile = context.OpenAssetFile(std::format("xmodel/{}.json", asset->m_name)); - if (!assetFile) - return; - - const JsonDumper dumper(context, *assetFile); - dumper.Dump(asset->Asset()); - } -} // namespace GAME_NAMESPACE diff --git a/src/ObjWriting/XModel/XModelDumper.cpp.template b/src/ObjWriting/XModel/XModelDumper.cpp.template index eebab624..fee67d24 100644 --- a/src/ObjWriting/XModel/XModelDumper.cpp.template +++ b/src/ObjWriting/XModel/XModelDumper.cpp.template @@ -8,12 +8,19 @@ #if GAME == "IW5" #define FEATURE_IW5 +#define GAME_LOWER "iw5" #elif GAME == "T5" #define FEATURE_T5 +#define GAME_LOWER "t5" #elif GAME == "T6" #define FEATURE_T6 +#define GAME_LOWER "t6" #endif +// This file was templated. +// See XModelDumper.cpp.template. +// Do not modify, changes will be lost. + #include DUMPER_HEADER #include COMMON_HEADER @@ -664,6 +671,7 @@ namespace jRoot["_type"] = "xmodel"; jRoot["_version"] = 1; + jRoot["_game"] = GAME_LOWER; m_stream << std::setw(4) << jRoot << "\n"; } diff --git a/src/ObjWriting/XModel/XModelDumper.h.template b/src/ObjWriting/XModel/XModelDumper.h.template index c981ef6b..bcb2d5ab 100644 --- a/src/ObjWriting/XModel/XModelDumper.h.template +++ b/src/ObjWriting/XModel/XModelDumper.h.template @@ -4,6 +4,10 @@ #set GAME_HEADER "\"Game/" + GAME + "/" + GAME + ".h\"" +// This file was templated. +// See XModelDumper.h.template. +// Do not modify, changes will be lost. + #pragma once #include "Dumping/AssetDumpingContext.h" diff --git a/src/Parser/Parsing/Impl/DefinesStreamProxy.cpp b/src/Parser/Parsing/Impl/DefinesStreamProxy.cpp index 631b159f..407fe327 100644 --- a/src/Parser/Parsing/Impl/DefinesStreamProxy.cpp +++ b/src/Parser/Parsing/Impl/DefinesStreamProxy.cpp @@ -49,7 +49,7 @@ DefinesStreamProxy::DefineParameterPosition::DefineParameterPosition(const unsig } DefinesStreamProxy::Define::Define() - : m_contains_token_pasting_operators(false){}; + : m_contains_token_pasting_operators(false) {}; DefinesStreamProxy::Define::Define(std::string name, std::string value) : m_name(std::move(name)), diff --git a/src/ZoneCommon/Pool/XAssetInfo.h b/src/ZoneCommon/Pool/XAssetInfo.h index 321fb996..16a3edbb 100644 --- a/src/ZoneCommon/Pool/XAssetInfo.h +++ b/src/ZoneCommon/Pool/XAssetInfo.h @@ -89,7 +89,7 @@ public: std::vector usedScriptStrings, std::vector indirectAssetReferences) : XAssetInfoGeneric( - type, std::move(name), static_cast(ptr), std::move(dependencies), std::move(usedScriptStrings), std::move(indirectAssetReferences)) + type, std::move(name), static_cast(ptr), std::move(dependencies), std::move(usedScriptStrings), std::move(indirectAssetReferences)) { } @@ -101,7 +101,7 @@ public: std::vector indirectAssetReferences, Zone* zone) : XAssetInfoGeneric( - type, std::move(name), static_cast(ptr), std::move(dependencies), std::move(usedScriptStrings), std::move(indirectAssetReferences), zone) + type, std::move(name), static_cast(ptr), std::move(dependencies), std::move(usedScriptStrings), std::move(indirectAssetReferences), zone) { } diff --git a/test/ObjCommonTestUtils/NormalizedJson.cpp b/test/ObjCommonTestUtils/NormalizedJson.cpp new file mode 100644 index 00000000..c06ffbb2 --- /dev/null +++ b/test/ObjCommonTestUtils/NormalizedJson.cpp @@ -0,0 +1,11 @@ +#include "NormalizedJson.h" + +#include + +using namespace nlohmann; + +std::string JsonNormalized(const std::string& str0) +{ + const json j0 = json::parse(str0); + return j0.dump(4); +} diff --git a/test/ObjCommonTestUtils/NormalizedJson.h b/test/ObjCommonTestUtils/NormalizedJson.h new file mode 100644 index 00000000..ad7a1df3 --- /dev/null +++ b/test/ObjCommonTestUtils/NormalizedJson.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +std::string JsonNormalized(const std::string& str0); diff --git a/test/ObjLoadingTests/Game/IW4/Material/LoaderMaterialIW4Test.cpp b/test/ObjLoadingTests/Game/IW4/Material/LoaderMaterialIW4Test.cpp new file mode 100644 index 00000000..0860550b --- /dev/null +++ b/test/ObjLoadingTests/Game/IW4/Material/LoaderMaterialIW4Test.cpp @@ -0,0 +1,558 @@ +#include "Game/IW4/Material/LoaderMaterialIW4.h" + +#include "Game/IW4/CommonIW4.h" +#include "Game/IW4/GameIW4.h" +#include "SearchPath/MockSearchPath.h" +#include "Utils/MemoryManager.h" + +#include +#include +#include + +using namespace IW4; +using namespace Catch; +using namespace std::literals; + +namespace +{ + void GivenImage(const std::string& name, AssetCreationContext& context, MemoryManager& memory) + { + auto* image = memory.Alloc(); + image->name = memory.Dup(name.c_str()); + + AssetRegistration registration(name); + registration.SetAsset(image); + context.AddAsset(std::move(registration)); + } + + void GivenTechset(const std::string& name, AssetCreationContext& context, MemoryManager& memory) + { + auto* techset = memory.Alloc(); + techset->name = memory.Dup(name.c_str()); + + AssetRegistration registration(name); + registration.SetAsset(techset); + context.AddAsset(std::move(registration)); + } + + TEST_CASE("LoaderMaterial(IW4): Can parse material", "[iw4][material][assetloader]") + { + MockSearchPath searchPath; + searchPath.AddFileData("materials/mc/ch_rubble01.json", + R"MATERIAL( +{ + "_game": "iw4", + "_type": "material", + "_version": 1, + "cameraRegion": "litOpaque", + "constants": [ + { + "literal": [ + 0.07000000029802322, + 0.46000000834465027, + 1.600000023841858, + 2.0 + ], + "name": "envMapParms" + }, + { + "literal": [ + 1.0, + 1.0, + 1.0, + 1.0 + ], + "name": "colorTint" + } + ], + "gameFlags": [ + "10", + "40" + ], + "sortKey": 1, + "stateBits": [ + { + "alphaTest": "disabled", + "blendOpAlpha": "disabled", + "blendOpRgb": "disabled", + "colorWriteAlpha": false, + "colorWriteRgb": false, + "cullFace": "back", + "depthTest": "less_equal", + "depthWrite": true, + "dstBlendAlpha": "zero", + "dstBlendRgb": "zero", + "gammaWrite": false, + "polygonOffset": "offset0", + "polymodeLine": false, + "srcBlendAlpha": "one", + "srcBlendRgb": "one" + }, + { + "alphaTest": "disabled", + "blendOpAlpha": "disabled", + "blendOpRgb": "disabled", + "colorWriteAlpha": true, + "colorWriteRgb": true, + "cullFace": "back", + "depthTest": "less_equal", + "depthWrite": true, + "dstBlendAlpha": "zero", + "dstBlendRgb": "zero", + "gammaWrite": false, + "polygonOffset": "offset0", + "polymodeLine": false, + "srcBlendAlpha": "one", + "srcBlendRgb": "one" + }, + { + "alphaTest": "disabled", + "blendOpAlpha": "disabled", + "blendOpRgb": "disabled", + "colorWriteAlpha": false, + "colorWriteRgb": false, + "cullFace": "back", + "depthTest": "less_equal", + "depthWrite": true, + "dstBlendAlpha": "zero", + "dstBlendRgb": "zero", + "gammaWrite": false, + "polygonOffset": "offsetShadowmap", + "polymodeLine": false, + "srcBlendAlpha": "one", + "srcBlendRgb": "one" + }, + { + "alphaTest": "disabled", + "blendOpAlpha": "disabled", + "blendOpRgb": "disabled", + "colorWriteAlpha": true, + "colorWriteRgb": true, + "cullFace": "back", + "depthTest": "less_equal", + "depthWrite": true, + "dstBlendAlpha": "zero", + "dstBlendRgb": "zero", + "gammaWrite": true, + "polygonOffset": "offset0", + "polymodeLine": false, + "srcBlendAlpha": "one", + "srcBlendRgb": "one" + }, + { + "alphaTest": "disabled", + "blendOpAlpha": "disabled", + "blendOpRgb": "add", + "colorWriteAlpha": true, + "colorWriteRgb": true, + "cullFace": "back", + "depthTest": "equal", + "depthWrite": false, + "dstBlendAlpha": "zero", + "dstBlendRgb": "one", + "gammaWrite": true, + "polygonOffset": "offset0", + "polymodeLine": false, + "srcBlendAlpha": "one", + "srcBlendRgb": "one", + "stencilFront": { + "fail": "keep", + "func": "equal", + "pass": "keep", + "zfail": "keep" + } + }, + { + "alphaTest": "disabled", + "blendOpAlpha": "disabled", + "blendOpRgb": "disabled", + "colorWriteAlpha": false, + "colorWriteRgb": true, + "cullFace": "back", + "depthTest": "less_equal", + "depthWrite": false, + "dstBlendAlpha": "zero", + "dstBlendRgb": "zero", + "gammaWrite": false, + "polygonOffset": "offset2", + "polymodeLine": true, + "srcBlendAlpha": "one", + "srcBlendRgb": "one" + } + ], + "stateBitsEntry": [ + 0, + 1, + 2, + 1, + 3, + -1, + -1, + -1, + -1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + -1, + -1, + -1, + -1, + 5, + -1, + 3, + 3 + ], + "stateFlags": 121, + "surfaceTypeBits": 16, + "techniqueSet": "mc_l_sm_r0c0n0s0", + "textureAtlas": { + "columns": 1, + "rows": 1 + }, + "textures": [ + { + "image": "~ch_rubble02_spc-r-74g-74b-74~1eb1f0d0", + "name": "specularMap", + "samplerState": { + "clampU": false, + "clampV": false, + "clampW": false, + "filter": "aniso2x", + "mipMap": "nearest" + }, + "semantic": "specularMap" + }, + { + "image": "ch_rubble01_nml", + "name": "normalMap", + "samplerState": { + "clampU": false, + "clampV": false, + "clampW": false, + "filter": "aniso2x", + "mipMap": "nearest" + }, + "semantic": "normalMap" + }, + { + "image": "ch_rubble01_col", + "name": "colorMap", + "samplerState": { + "clampU": false, + "clampV": false, + "clampW": false, + "filter": "aniso2x", + "mipMap": "nearest" + }, + "semantic": "colorMap" + } + ] +})MATERIAL"); + + Zone zone("MockZone", 0, IGame::GetGameById(GameId::IW4)); + + MemoryManager memory; + AssetCreatorCollection creatorCollection(zone); + IgnoredAssetLookup ignoredAssetLookup; + AssetCreationContext context(zone, &creatorCollection, &ignoredAssetLookup); + + GivenImage("~ch_rubble02_spc-r-74g-74b-74~1eb1f0d0", context, memory); + GivenImage("ch_rubble01_nml", context, memory); + GivenImage("ch_rubble01_col", context, memory); + GivenTechset("mc_l_sm_r0c0n0s0", context, memory); + + auto loader = CreateMaterialLoader(memory, searchPath); + auto result = loader->CreateAsset("mc/ch_rubble01", context); + REQUIRE(result.HasBeenSuccessful()); + + const auto* assetInfo = reinterpret_cast*>(result.GetAssetInfo()); + const auto* material = assetInfo->Asset(); + + REQUIRE(material->info.name == "mc/ch_rubble01"s); + REQUIRE(material->info.gameFlags == 0x50); + REQUIRE(material->info.sortKey == 1); + REQUIRE(material->info.textureAtlasRowCount == 1); + REQUIRE(material->info.textureAtlasColumnCount == 1); + REQUIRE(material->info.surfaceTypeBits == 16); + + constexpr int8_t expectedStateBitsEntry[]{0, 1, 2, 1, 3, -1, -1, -1, -1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, -1, -1, -1, -1, 5, -1, 3, 3}; + REQUIRE(std::memcmp(material->stateBitsEntry, expectedStateBitsEntry, sizeof(expectedStateBitsEntry)) == 0); + + REQUIRE(material->stateFlags == 121); + REQUIRE(material->cameraRegion == CAMERA_REGION_LIT_OPAQUE); + + REQUIRE(material->techniqueSet != nullptr); + REQUIRE(material->techniqueSet->name != nullptr); + REQUIRE(material->techniqueSet->name == "mc_l_sm_r0c0n0s0"s); + + REQUIRE(material->textureCount == 3); + REQUIRE(material->textureTable); + + const auto& textureDef0 = material->textureTable[0]; + REQUIRE(textureDef0.nameHash == 0x34ecccb3); + REQUIRE(textureDef0.nameStart == 's'); + REQUIRE(textureDef0.nameEnd == 'p'); + REQUIRE(textureDef0.samplerState.filter == TEXTURE_FILTER_ANISO2X); + REQUIRE(textureDef0.samplerState.mipMap == SAMPLER_MIPMAP_ENUM_NEAREST); + REQUIRE(textureDef0.samplerState.clampU == 0); + REQUIRE(textureDef0.samplerState.clampV == 0); + REQUIRE(textureDef0.samplerState.clampW == 0); + REQUIRE(textureDef0.semantic == TS_SPECULAR_MAP); + REQUIRE(textureDef0.u.image); + REQUIRE(textureDef0.u.image->name); + REQUIRE(textureDef0.u.image->name == "~ch_rubble02_spc-r-74g-74b-74~1eb1f0d0"s); + + const auto& textureDef1 = material->textureTable[1]; + REQUIRE(textureDef1.nameHash == 0x59d30d0f); + REQUIRE(textureDef1.nameStart == 'n'); + REQUIRE(textureDef1.nameEnd == 'p'); + REQUIRE(textureDef1.samplerState.filter == TEXTURE_FILTER_ANISO2X); + REQUIRE(textureDef1.samplerState.mipMap == SAMPLER_MIPMAP_ENUM_NEAREST); + REQUIRE(textureDef1.samplerState.clampU == 0); + REQUIRE(textureDef1.samplerState.clampV == 0); + REQUIRE(textureDef1.samplerState.clampW == 0); + REQUIRE(textureDef1.semantic == TS_NORMAL_MAP); + REQUIRE(textureDef1.u.image); + REQUIRE(textureDef1.u.image->name); + REQUIRE(textureDef1.u.image->name == "ch_rubble01_nml"s); + + const auto& textureDef2 = material->textureTable[2]; + REQUIRE(textureDef2.nameHash == 0xa0ab1041); + REQUIRE(textureDef2.nameStart == 'c'); + REQUIRE(textureDef2.nameEnd == 'p'); + REQUIRE(textureDef2.samplerState.filter == TEXTURE_FILTER_ANISO2X); + REQUIRE(textureDef2.samplerState.mipMap == SAMPLER_MIPMAP_ENUM_NEAREST); + REQUIRE(textureDef2.samplerState.clampU == 0); + REQUIRE(textureDef2.samplerState.clampV == 0); + REQUIRE(textureDef2.samplerState.clampW == 0); + REQUIRE(textureDef2.semantic == TS_COLOR_MAP); + REQUIRE(textureDef2.u.image); + REQUIRE(textureDef2.u.image->name); + REQUIRE(textureDef2.u.image->name == "ch_rubble01_col"s); + + REQUIRE(material->constantCount == 2); + REQUIRE(material->constantTable); + + const auto& constantDef0 = material->constantTable[0]; + REQUIRE(constantDef0.nameHash == 0x3d9994dc); + REQUIRE(strncmp(constantDef0.name, "envMapParms", std::extent_v) == 0); + REQUIRE(constantDef0.literal.x == Approx(0.07f)); + REQUIRE(constantDef0.literal.y == Approx(0.46f)); + REQUIRE(constantDef0.literal.z == Approx(1.6f)); + REQUIRE(constantDef0.literal.w == Approx(2.0f)); + + const auto& constantDef1 = material->constantTable[1]; + REQUIRE(constantDef1.nameHash == 0xb60c3b3a); + REQUIRE(strncmp(constantDef1.name, "colorTint", std::extent_v) == 0); + REQUIRE(constantDef1.literal.x == Approx(1.0f)); + REQUIRE(constantDef1.literal.y == Approx(1.0f)); + REQUIRE(constantDef1.literal.z == Approx(1.0f)); + REQUIRE(constantDef1.literal.w == Approx(1.0f)); + + REQUIRE(material->stateBitsCount == 6); + REQUIRE(material->stateBitsTable); + + const auto& stateBits0 = material->stateBitsTable[0]; + REQUIRE(stateBits0.loadBits.structured.srcBlendRgb == GFXS_BLEND_ONE); + REQUIRE(stateBits0.loadBits.structured.dstBlendRgb == GFXS_BLEND_ZERO); + REQUIRE(stateBits0.loadBits.structured.blendOpRgb == GFXS_BLENDOP_DISABLED); + REQUIRE(stateBits0.loadBits.structured.alphaTestDisabled == 1); + REQUIRE(stateBits0.loadBits.structured.alphaTest == 0); + REQUIRE(stateBits0.loadBits.structured.cullFace == GFXS_CULL_BACK); + REQUIRE(stateBits0.loadBits.structured.srcBlendAlpha == GFXS_BLEND_ONE); + REQUIRE(stateBits0.loadBits.structured.dstBlendAlpha == GFXS_BLEND_ZERO); + REQUIRE(stateBits0.loadBits.structured.blendOpAlpha == GFXS_BLENDOP_DISABLED); + REQUIRE(stateBits0.loadBits.structured.colorWriteRgb == 0); + REQUIRE(stateBits0.loadBits.structured.colorWriteAlpha == 0); + REQUIRE(stateBits0.loadBits.structured.gammaWrite == 0); + REQUIRE(stateBits0.loadBits.structured.polymodeLine == 0); + REQUIRE(stateBits0.loadBits.structured.depthWrite == 1); + REQUIRE(stateBits0.loadBits.structured.depthTestDisabled == 0); + REQUIRE(stateBits0.loadBits.structured.depthTest == GFXS_DEPTHTEST_LESSEQUAL); + REQUIRE(stateBits0.loadBits.structured.polygonOffset == GFXS_POLYGON_OFFSET_0); + REQUIRE(stateBits0.loadBits.structured.stencilFrontEnabled == 0); + REQUIRE(stateBits0.loadBits.structured.stencilFrontPass == 0); + REQUIRE(stateBits0.loadBits.structured.stencilFrontFail == 0); + REQUIRE(stateBits0.loadBits.structured.stencilFrontZFail == 0); + REQUIRE(stateBits0.loadBits.structured.stencilFrontFunc == 0); + REQUIRE(stateBits0.loadBits.structured.stencilBackEnabled == 0); + REQUIRE(stateBits0.loadBits.structured.stencilBackPass == 0); + REQUIRE(stateBits0.loadBits.structured.stencilBackFail == 0); + REQUIRE(stateBits0.loadBits.structured.stencilBackZFail == 0); + REQUIRE(stateBits0.loadBits.structured.stencilBackFunc == 0); + + const auto& stateBits1 = material->stateBitsTable[1]; + REQUIRE(stateBits1.loadBits.structured.srcBlendRgb == GFXS_BLEND_ONE); + REQUIRE(stateBits1.loadBits.structured.dstBlendRgb == GFXS_BLEND_ZERO); + REQUIRE(stateBits1.loadBits.structured.blendOpRgb == GFXS_BLENDOP_DISABLED); + REQUIRE(stateBits1.loadBits.structured.alphaTestDisabled == 1); + REQUIRE(stateBits1.loadBits.structured.alphaTest == 0); + REQUIRE(stateBits1.loadBits.structured.cullFace == GFXS_CULL_BACK); + REQUIRE(stateBits1.loadBits.structured.srcBlendAlpha == GFXS_BLEND_ONE); + REQUIRE(stateBits1.loadBits.structured.dstBlendAlpha == GFXS_BLEND_ZERO); + REQUIRE(stateBits1.loadBits.structured.blendOpAlpha == GFXS_BLENDOP_DISABLED); + REQUIRE(stateBits1.loadBits.structured.colorWriteRgb == 1); + REQUIRE(stateBits1.loadBits.structured.colorWriteAlpha == 1); + REQUIRE(stateBits1.loadBits.structured.gammaWrite == 0); + REQUIRE(stateBits1.loadBits.structured.polymodeLine == 0); + REQUIRE(stateBits1.loadBits.structured.depthWrite == 1); + REQUIRE(stateBits1.loadBits.structured.depthTestDisabled == 0); + REQUIRE(stateBits1.loadBits.structured.depthTest == GFXS_DEPTHTEST_LESSEQUAL); + REQUIRE(stateBits1.loadBits.structured.polygonOffset == GFXS_POLYGON_OFFSET_0); + REQUIRE(stateBits1.loadBits.structured.stencilFrontEnabled == 0); + REQUIRE(stateBits1.loadBits.structured.stencilFrontPass == 0); + REQUIRE(stateBits1.loadBits.structured.stencilFrontFail == 0); + REQUIRE(stateBits1.loadBits.structured.stencilFrontZFail == 0); + REQUIRE(stateBits1.loadBits.structured.stencilFrontFunc == 0); + REQUIRE(stateBits1.loadBits.structured.stencilBackEnabled == 0); + REQUIRE(stateBits1.loadBits.structured.stencilBackPass == 0); + REQUIRE(stateBits1.loadBits.structured.stencilBackFail == 0); + REQUIRE(stateBits1.loadBits.structured.stencilBackZFail == 0); + REQUIRE(stateBits1.loadBits.structured.stencilBackFunc == 0); + + const auto& stateBits2 = material->stateBitsTable[2]; + REQUIRE(stateBits2.loadBits.structured.srcBlendRgb == GFXS_BLEND_ONE); + REQUIRE(stateBits2.loadBits.structured.dstBlendRgb == GFXS_BLEND_ZERO); + REQUIRE(stateBits2.loadBits.structured.blendOpRgb == GFXS_BLENDOP_DISABLED); + REQUIRE(stateBits2.loadBits.structured.alphaTestDisabled == 1); + REQUIRE(stateBits2.loadBits.structured.alphaTest == 0); + REQUIRE(stateBits2.loadBits.structured.cullFace == GFXS_CULL_BACK); + REQUIRE(stateBits2.loadBits.structured.srcBlendAlpha == GFXS_BLEND_ONE); + REQUIRE(stateBits2.loadBits.structured.dstBlendAlpha == GFXS_BLEND_ZERO); + REQUIRE(stateBits2.loadBits.structured.blendOpAlpha == GFXS_BLENDOP_DISABLED); + REQUIRE(stateBits2.loadBits.structured.colorWriteRgb == 0); + REQUIRE(stateBits2.loadBits.structured.colorWriteAlpha == 0); + REQUIRE(stateBits2.loadBits.structured.gammaWrite == 0); + REQUIRE(stateBits2.loadBits.structured.polymodeLine == 0); + REQUIRE(stateBits2.loadBits.structured.depthWrite == 1); + REQUIRE(stateBits2.loadBits.structured.depthTestDisabled == 0); + REQUIRE(stateBits2.loadBits.structured.depthTest == GFXS_DEPTHTEST_LESSEQUAL); + REQUIRE(stateBits2.loadBits.structured.polygonOffset == GFXS_POLYGON_OFFSET_SHADOWMAP); + REQUIRE(stateBits2.loadBits.structured.stencilFrontEnabled == 0); + REQUIRE(stateBits2.loadBits.structured.stencilFrontPass == 0); + REQUIRE(stateBits2.loadBits.structured.stencilFrontFail == 0); + REQUIRE(stateBits2.loadBits.structured.stencilFrontZFail == 0); + REQUIRE(stateBits2.loadBits.structured.stencilFrontFunc == 0); + REQUIRE(stateBits2.loadBits.structured.stencilBackEnabled == 0); + REQUIRE(stateBits2.loadBits.structured.stencilBackPass == 0); + REQUIRE(stateBits2.loadBits.structured.stencilBackFail == 0); + REQUIRE(stateBits2.loadBits.structured.stencilBackZFail == 0); + REQUIRE(stateBits2.loadBits.structured.stencilBackFunc == 0); + + const auto& stateBits3 = material->stateBitsTable[3]; + REQUIRE(stateBits3.loadBits.structured.srcBlendRgb == GFXS_BLEND_ONE); + REQUIRE(stateBits3.loadBits.structured.dstBlendRgb == GFXS_BLEND_ZERO); + REQUIRE(stateBits3.loadBits.structured.blendOpRgb == GFXS_BLENDOP_DISABLED); + REQUIRE(stateBits3.loadBits.structured.alphaTestDisabled == 1); + REQUIRE(stateBits3.loadBits.structured.alphaTest == 0); + REQUIRE(stateBits3.loadBits.structured.cullFace == GFXS_CULL_BACK); + REQUIRE(stateBits3.loadBits.structured.srcBlendAlpha == GFXS_BLEND_ONE); + REQUIRE(stateBits3.loadBits.structured.dstBlendAlpha == GFXS_BLEND_ZERO); + REQUIRE(stateBits3.loadBits.structured.blendOpAlpha == GFXS_BLENDOP_DISABLED); + REQUIRE(stateBits3.loadBits.structured.colorWriteRgb == 1); + REQUIRE(stateBits3.loadBits.structured.colorWriteAlpha == 1); + REQUIRE(stateBits3.loadBits.structured.gammaWrite == 1); + REQUIRE(stateBits3.loadBits.structured.polymodeLine == 0); + REQUIRE(stateBits3.loadBits.structured.depthWrite == 1); + REQUIRE(stateBits3.loadBits.structured.depthTestDisabled == 0); + REQUIRE(stateBits3.loadBits.structured.depthTest == GFXS_DEPTHTEST_LESSEQUAL); + REQUIRE(stateBits3.loadBits.structured.polygonOffset == GFXS_POLYGON_OFFSET_0); + REQUIRE(stateBits3.loadBits.structured.stencilFrontEnabled == 0); + REQUIRE(stateBits3.loadBits.structured.stencilFrontPass == 0); + REQUIRE(stateBits3.loadBits.structured.stencilFrontFail == 0); + REQUIRE(stateBits3.loadBits.structured.stencilFrontZFail == 0); + REQUIRE(stateBits3.loadBits.structured.stencilFrontFunc == 0); + REQUIRE(stateBits3.loadBits.structured.stencilBackEnabled == 0); + REQUIRE(stateBits3.loadBits.structured.stencilBackPass == 0); + REQUIRE(stateBits3.loadBits.structured.stencilBackFail == 0); + REQUIRE(stateBits3.loadBits.structured.stencilBackZFail == 0); + REQUIRE(stateBits3.loadBits.structured.stencilBackFunc == 0); + + const auto& stateBits4 = material->stateBitsTable[4]; + REQUIRE(stateBits4.loadBits.structured.srcBlendRgb == GFXS_BLEND_ONE); + REQUIRE(stateBits4.loadBits.structured.dstBlendRgb == GFXS_BLEND_ONE); + REQUIRE(stateBits4.loadBits.structured.blendOpRgb == GFXS_BLENDOP_ADD); + REQUIRE(stateBits4.loadBits.structured.alphaTestDisabled == 1); + REQUIRE(stateBits4.loadBits.structured.alphaTest == 0); + REQUIRE(stateBits4.loadBits.structured.cullFace == GFXS_CULL_BACK); + REQUIRE(stateBits4.loadBits.structured.srcBlendAlpha == GFXS_BLEND_ONE); + REQUIRE(stateBits4.loadBits.structured.dstBlendAlpha == GFXS_BLEND_ZERO); + REQUIRE(stateBits4.loadBits.structured.blendOpAlpha == GFXS_BLENDOP_DISABLED); + REQUIRE(stateBits4.loadBits.structured.colorWriteRgb == 1); + REQUIRE(stateBits4.loadBits.structured.colorWriteAlpha == 1); + REQUIRE(stateBits4.loadBits.structured.gammaWrite == 1); + REQUIRE(stateBits4.loadBits.structured.polymodeLine == 0); + REQUIRE(stateBits4.loadBits.structured.depthWrite == 0); + REQUIRE(stateBits4.loadBits.structured.depthTestDisabled == 0); + REQUIRE(stateBits4.loadBits.structured.depthTest == GFXS_DEPTHTEST_EQUAL); + REQUIRE(stateBits4.loadBits.structured.polygonOffset == GFXS_POLYGON_OFFSET_0); + REQUIRE(stateBits4.loadBits.structured.stencilFrontEnabled == 1); + REQUIRE(stateBits4.loadBits.structured.stencilFrontPass == GFXS_STENCILOP_KEEP); + REQUIRE(stateBits4.loadBits.structured.stencilFrontFail == GFXS_STENCILOP_KEEP); + REQUIRE(stateBits4.loadBits.structured.stencilFrontZFail == GFXS_STENCILOP_KEEP); + REQUIRE(stateBits4.loadBits.structured.stencilFrontFunc == GFXS_STENCILFUNC_EQUAL); + REQUIRE(stateBits4.loadBits.structured.stencilBackEnabled == 0); + REQUIRE(stateBits4.loadBits.structured.stencilBackPass == 0); + REQUIRE(stateBits4.loadBits.structured.stencilBackFail == 0); + REQUIRE(stateBits4.loadBits.structured.stencilBackZFail == 0); + REQUIRE(stateBits4.loadBits.structured.stencilBackFunc == 0); + + const auto& stateBits5 = material->stateBitsTable[5]; + REQUIRE(stateBits5.loadBits.structured.srcBlendRgb == GFXS_BLEND_ONE); + REQUIRE(stateBits5.loadBits.structured.dstBlendRgb == GFXS_BLEND_ZERO); + REQUIRE(stateBits5.loadBits.structured.blendOpRgb == GFXS_BLENDOP_DISABLED); + REQUIRE(stateBits5.loadBits.structured.alphaTestDisabled == 1); + REQUIRE(stateBits5.loadBits.structured.alphaTest == 0); + REQUIRE(stateBits5.loadBits.structured.cullFace == GFXS_CULL_BACK); + REQUIRE(stateBits5.loadBits.structured.srcBlendAlpha == GFXS_BLEND_ONE); + REQUIRE(stateBits5.loadBits.structured.dstBlendAlpha == GFXS_BLEND_ZERO); + REQUIRE(stateBits5.loadBits.structured.blendOpAlpha == GFXS_BLENDOP_DISABLED); + REQUIRE(stateBits5.loadBits.structured.colorWriteRgb == 1); + REQUIRE(stateBits5.loadBits.structured.colorWriteAlpha == 0); + REQUIRE(stateBits5.loadBits.structured.gammaWrite == 0); + REQUIRE(stateBits5.loadBits.structured.polymodeLine == 1); + REQUIRE(stateBits5.loadBits.structured.depthWrite == 0); + REQUIRE(stateBits5.loadBits.structured.depthTestDisabled == 0); + REQUIRE(stateBits5.loadBits.structured.depthTest == GFXS_DEPTHTEST_LESSEQUAL); + REQUIRE(stateBits5.loadBits.structured.polygonOffset == GFXS_POLYGON_OFFSET_2); + REQUIRE(stateBits5.loadBits.structured.stencilFrontEnabled == 0); + REQUIRE(stateBits5.loadBits.structured.stencilFrontPass == 0); + REQUIRE(stateBits5.loadBits.structured.stencilFrontFail == 0); + REQUIRE(stateBits5.loadBits.structured.stencilFrontZFail == 0); + REQUIRE(stateBits5.loadBits.structured.stencilFrontFunc == 0); + REQUIRE(stateBits5.loadBits.structured.stencilBackEnabled == 0); + REQUIRE(stateBits5.loadBits.structured.stencilBackPass == 0); + REQUIRE(stateBits5.loadBits.structured.stencilBackFail == 0); + REQUIRE(stateBits5.loadBits.structured.stencilBackZFail == 0); + REQUIRE(stateBits5.loadBits.structured.stencilBackFunc == 0); + } +} // namespace diff --git a/test/ObjLoadingTests/Game/IW5/Material/LoaderMaterialIW5Test.cpp b/test/ObjLoadingTests/Game/IW5/Material/LoaderMaterialIW5Test.cpp new file mode 100644 index 00000000..5de6912b --- /dev/null +++ b/test/ObjLoadingTests/Game/IW5/Material/LoaderMaterialIW5Test.cpp @@ -0,0 +1,611 @@ +#include "Game/IW5/Material/LoaderMaterialIW5.h" + +#include "Game/IW5/CommonIW5.h" +#include "Game/IW5/GameIW5.h" +#include "SearchPath/MockSearchPath.h" +#include "Utils/MemoryManager.h" + +#include +#include +#include + +using namespace IW5; +using namespace Catch; +using namespace std::literals; + +namespace +{ + void GivenImage(const std::string& name, AssetCreationContext& context, MemoryManager& memory) + { + auto* image = memory.Alloc(); + image->name = memory.Dup(name.c_str()); + + AssetRegistration registration(name); + registration.SetAsset(image); + context.AddAsset(std::move(registration)); + } + + void GivenTechset(const std::string& name, AssetCreationContext& context, MemoryManager& memory) + { + auto* techset = memory.Alloc(); + techset->name = memory.Dup(name.c_str()); + + AssetRegistration registration(name); + registration.SetAsset(techset); + context.AddAsset(std::move(registration)); + } + + TEST_CASE("LoaderMaterial(IW5): Can parse material", "[iw5][material][assetloader]") + { + MockSearchPath searchPath; + searchPath.AddFileData("materials/wc/me_metal_rust_02.json", + R"MATERIAL( +{ + "_game": "iw5", + "_type": "material", + "_version": 1, + "cameraRegion": "litOpaque", + "constants": [ + { + "literal": [ + 0.07000000029802322, + 0.33000001311302185, + 1.399999976158142, + 2.0 + ], + "name": "envMapParms" + }, + { + "literal": [ + 1.0, + 1.0, + 1.0, + 1.0 + ], + "name": "colorTint" + } + ], + "gameFlags": [ + "2", + "10", + "40" + ], + "sortKey": 1, + "stateBits": [ + { + "alphaTest": "disabled", + "blendOpAlpha": "disabled", + "blendOpRgb": "disabled", + "colorWriteAlpha": false, + "colorWriteRgb": false, + "cullFace": "back", + "depthTest": "less_equal", + "depthWrite": true, + "dstBlendAlpha": "zero", + "dstBlendRgb": "zero", + "gammaWrite": false, + "polygonOffset": "offset0", + "polymodeLine": false, + "srcBlendAlpha": "one", + "srcBlendRgb": "one" + }, + { + "alphaTest": "disabled", + "blendOpAlpha": "disabled", + "blendOpRgb": "disabled", + "colorWriteAlpha": true, + "colorWriteRgb": true, + "cullFace": "back", + "depthTest": "less_equal", + "depthWrite": true, + "dstBlendAlpha": "zero", + "dstBlendRgb": "zero", + "gammaWrite": false, + "polygonOffset": "offset0", + "polymodeLine": false, + "srcBlendAlpha": "one", + "srcBlendRgb": "one" + }, + { + "alphaTest": "disabled", + "blendOpAlpha": "disabled", + "blendOpRgb": "disabled", + "colorWriteAlpha": false, + "colorWriteRgb": false, + "cullFace": "none", + "depthTest": "less_equal", + "depthWrite": true, + "dstBlendAlpha": "zero", + "dstBlendRgb": "zero", + "gammaWrite": false, + "polygonOffset": "offsetShadowmap", + "polymodeLine": false, + "srcBlendAlpha": "one", + "srcBlendRgb": "one" + }, + { + "alphaTest": "disabled", + "blendOpAlpha": "disabled", + "blendOpRgb": "disabled", + "colorWriteAlpha": true, + "colorWriteRgb": true, + "cullFace": "none", + "depthTest": "less_equal", + "depthWrite": true, + "dstBlendAlpha": "zero", + "dstBlendRgb": "zero", + "gammaWrite": false, + "polygonOffset": "offset0", + "polymodeLine": false, + "srcBlendAlpha": "one", + "srcBlendRgb": "one" + }, + { + "alphaTest": "disabled", + "blendOpAlpha": "disabled", + "blendOpRgb": "disabled", + "colorWriteAlpha": true, + "colorWriteRgb": true, + "cullFace": "back", + "depthTest": "less_equal", + "depthWrite": true, + "dstBlendAlpha": "zero", + "dstBlendRgb": "zero", + "gammaWrite": true, + "polygonOffset": "offset0", + "polymodeLine": false, + "srcBlendAlpha": "one", + "srcBlendRgb": "one" + }, + { + "alphaTest": "disabled", + "blendOpAlpha": "disabled", + "blendOpRgb": "add", + "colorWriteAlpha": true, + "colorWriteRgb": true, + "cullFace": "back", + "depthTest": "equal", + "depthWrite": false, + "dstBlendAlpha": "zero", + "dstBlendRgb": "one", + "gammaWrite": true, + "polygonOffset": "offset0", + "polymodeLine": false, + "srcBlendAlpha": "one", + "srcBlendRgb": "one", + "stencilFront": { + "fail": "keep", + "func": "equal", + "pass": "keep", + "zfail": "keep" + } + }, + { + "alphaTest": "disabled", + "blendOpAlpha": "disabled", + "blendOpRgb": "disabled", + "colorWriteAlpha": false, + "colorWriteRgb": true, + "cullFace": "back", + "depthTest": "less_equal", + "depthWrite": false, + "dstBlendAlpha": "zero", + "dstBlendRgb": "zero", + "gammaWrite": false, + "polygonOffset": "offset2", + "polymodeLine": true, + "srcBlendAlpha": "one", + "srcBlendRgb": "one" + } + ], + "stateBitsEntry": [ + 0, + 1, + 2, + 3, + 4, + -1, + -1, + -1, + -1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + 5, + 5, + 5, + 5, + -1, + -1, + -1, + -1, + 6, + -1, + -1, + 4, + -1 + ], + "stateFlags": 59, + "surfaceTypeBits": 4096, + "techniqueSet": "wc_l_sm_r0c0n0s0", + "textureAtlas": { + "columns": 1, + "rows": 1 + }, + "textures": [ + { + "image": "~me_metal_rusty02_spc-rgb&me_~927de80f", + "name": "specularMap", + "samplerState": { + "clampU": false, + "clampV": false, + "clampW": false, + "filter": "aniso2x", + "mipMap": "nearest" + }, + "semantic": "specularMap" + }, + { + "image": "me_metal_rusty02_nml", + "name": "normalMap", + "samplerState": { + "clampU": false, + "clampV": false, + "clampW": false, + "filter": "aniso2x", + "mipMap": "linear" + }, + "semantic": "normalMap" + }, + { + "image": "me_metal_rusty02_col", + "name": "colorMap", + "samplerState": { + "clampU": false, + "clampV": false, + "clampW": false, + "filter": "aniso2x", + "mipMap": "linear" + }, + "semantic": "colorMap" + } + ] +})MATERIAL"); + + Zone zone("MockZone", 0, IGame::GetGameById(GameId::IW5)); + + MemoryManager memory; + AssetCreatorCollection creatorCollection(zone); + IgnoredAssetLookup ignoredAssetLookup; + AssetCreationContext context(zone, &creatorCollection, &ignoredAssetLookup); + + GivenImage("~me_metal_rusty02_spc-rgb&me_~927de80f", context, memory); + GivenImage("me_metal_rusty02_nml", context, memory); + GivenImage("me_metal_rusty02_col", context, memory); + GivenTechset("wc_l_sm_r0c0n0s0", context, memory); + + auto loader = CreateMaterialLoader(memory, searchPath); + auto result = loader->CreateAsset("wc/me_metal_rust_02", context); + REQUIRE(result.HasBeenSuccessful()); + + const auto* assetInfo = reinterpret_cast*>(result.GetAssetInfo()); + const auto* material = assetInfo->Asset(); + + REQUIRE(material->info.name == "wc/me_metal_rust_02"s); + REQUIRE(material->info.gameFlags == 0x52); + REQUIRE(material->info.sortKey == 1); + REQUIRE(material->info.textureAtlasRowCount == 1); + REQUIRE(material->info.textureAtlasColumnCount == 1); + REQUIRE(material->info.surfaceTypeBits == 4096); + + constexpr int8_t expectedStateBitsEntry[]{0, 1, 2, 3, 4, -1, -1, -1, -1, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 5, 5, 5, 5, -1, -1, -1, -1, 6, -1, -1, 4, -1}; + REQUIRE(std::memcmp(material->stateBitsEntry, expectedStateBitsEntry, sizeof(expectedStateBitsEntry)) == 0); + + REQUIRE(material->stateFlags == 59); + REQUIRE(material->cameraRegion == CAMERA_REGION_LIT_OPAQUE); + + REQUIRE(material->techniqueSet != nullptr); + REQUIRE(material->techniqueSet->name != nullptr); + REQUIRE(material->techniqueSet->name == "wc_l_sm_r0c0n0s0"s); + + REQUIRE(material->textureCount == 3); + REQUIRE(material->textureTable); + + const auto& textureDef0 = material->textureTable[0]; + REQUIRE(textureDef0.nameHash == 0x34ecccb3); + REQUIRE(textureDef0.nameStart == 's'); + REQUIRE(textureDef0.nameEnd == 'p'); + REQUIRE(textureDef0.samplerState.filter == TEXTURE_FILTER_ANISO2X); + REQUIRE(textureDef0.samplerState.mipMap == SAMPLER_MIPMAP_ENUM_NEAREST); + REQUIRE(textureDef0.samplerState.clampU == 0); + REQUIRE(textureDef0.samplerState.clampV == 0); + REQUIRE(textureDef0.samplerState.clampW == 0); + REQUIRE(textureDef0.semantic == TS_SPECULAR_MAP); + REQUIRE(textureDef0.u.image); + REQUIRE(textureDef0.u.image->name); + REQUIRE(textureDef0.u.image->name == "~me_metal_rusty02_spc-rgb&me_~927de80f"s); + + const auto& textureDef1 = material->textureTable[1]; + REQUIRE(textureDef1.nameHash == 0x59d30d0f); + REQUIRE(textureDef1.nameStart == 'n'); + REQUIRE(textureDef1.nameEnd == 'p'); + REQUIRE(textureDef1.samplerState.filter == TEXTURE_FILTER_ANISO2X); + REQUIRE(textureDef1.samplerState.mipMap == SAMPLER_MIPMAP_ENUM_LINEAR); + REQUIRE(textureDef1.samplerState.clampU == 0); + REQUIRE(textureDef1.samplerState.clampV == 0); + REQUIRE(textureDef1.samplerState.clampW == 0); + REQUIRE(textureDef1.semantic == TS_NORMAL_MAP); + REQUIRE(textureDef1.u.image); + REQUIRE(textureDef1.u.image->name); + REQUIRE(textureDef1.u.image->name == "me_metal_rusty02_nml"s); + + const auto& textureDef2 = material->textureTable[2]; + REQUIRE(textureDef2.nameHash == 0xa0ab1041); + REQUIRE(textureDef2.nameStart == 'c'); + REQUIRE(textureDef2.nameEnd == 'p'); + REQUIRE(textureDef2.samplerState.filter == TEXTURE_FILTER_ANISO2X); + REQUIRE(textureDef2.samplerState.mipMap == SAMPLER_MIPMAP_ENUM_LINEAR); + REQUIRE(textureDef2.samplerState.clampU == 0); + REQUIRE(textureDef2.samplerState.clampV == 0); + REQUIRE(textureDef2.samplerState.clampW == 0); + REQUIRE(textureDef2.semantic == TS_COLOR_MAP); + REQUIRE(textureDef2.u.image); + REQUIRE(textureDef2.u.image->name); + REQUIRE(textureDef2.u.image->name == "me_metal_rusty02_col"s); + + REQUIRE(material->constantCount == 2); + REQUIRE(material->constantTable); + + const auto& constantDef0 = material->constantTable[0]; + REQUIRE(constantDef0.nameHash == 0x3d9994dc); + REQUIRE(strncmp(constantDef0.name, "envMapParms", std::extent_v) == 0); + REQUIRE(constantDef0.literal.x == Approx(0.07f)); + REQUIRE(constantDef0.literal.y == Approx(0.33f)); + REQUIRE(constantDef0.literal.z == Approx(1.4f)); + REQUIRE(constantDef0.literal.w == Approx(2.0f)); + + const auto& constantDef1 = material->constantTable[1]; + REQUIRE(constantDef1.nameHash == 0xb60c3b3a); + REQUIRE(strncmp(constantDef1.name, "colorTint", std::extent_v) == 0); + REQUIRE(constantDef1.literal.x == Approx(1.0f)); + REQUIRE(constantDef1.literal.y == Approx(1.0f)); + REQUIRE(constantDef1.literal.z == Approx(1.0f)); + REQUIRE(constantDef1.literal.w == Approx(1.0f)); + + REQUIRE(material->stateBitsCount == 7); + REQUIRE(material->stateBitsTable); + + const auto& stateBits0 = material->stateBitsTable[0]; + REQUIRE(stateBits0.loadBits.structured.srcBlendRgb == GFXS_BLEND_ONE); + REQUIRE(stateBits0.loadBits.structured.dstBlendRgb == GFXS_BLEND_ZERO); + REQUIRE(stateBits0.loadBits.structured.blendOpRgb == GFXS_BLENDOP_DISABLED); + REQUIRE(stateBits0.loadBits.structured.alphaTestDisabled == 1); + REQUIRE(stateBits0.loadBits.structured.alphaTest == 0); + REQUIRE(stateBits0.loadBits.structured.cullFace == GFXS_CULL_BACK); + REQUIRE(stateBits0.loadBits.structured.srcBlendAlpha == GFXS_BLEND_ONE); + REQUIRE(stateBits0.loadBits.structured.dstBlendAlpha == GFXS_BLEND_ZERO); + REQUIRE(stateBits0.loadBits.structured.blendOpAlpha == GFXS_BLENDOP_DISABLED); + REQUIRE(stateBits0.loadBits.structured.colorWriteRgb == 0); + REQUIRE(stateBits0.loadBits.structured.colorWriteAlpha == 0); + REQUIRE(stateBits0.loadBits.structured.gammaWrite == 0); + REQUIRE(stateBits0.loadBits.structured.polymodeLine == 0); + REQUIRE(stateBits0.loadBits.structured.depthWrite == 1); + REQUIRE(stateBits0.loadBits.structured.depthTestDisabled == 0); + REQUIRE(stateBits0.loadBits.structured.depthTest == GFXS_DEPTHTEST_LESSEQUAL); + REQUIRE(stateBits0.loadBits.structured.polygonOffset == GFXS_POLYGON_OFFSET_0); + REQUIRE(stateBits0.loadBits.structured.stencilFrontEnabled == 0); + REQUIRE(stateBits0.loadBits.structured.stencilFrontPass == 0); + REQUIRE(stateBits0.loadBits.structured.stencilFrontFail == 0); + REQUIRE(stateBits0.loadBits.structured.stencilFrontZFail == 0); + REQUIRE(stateBits0.loadBits.structured.stencilFrontFunc == 0); + REQUIRE(stateBits0.loadBits.structured.stencilBackEnabled == 0); + REQUIRE(stateBits0.loadBits.structured.stencilBackPass == 0); + REQUIRE(stateBits0.loadBits.structured.stencilBackFail == 0); + REQUIRE(stateBits0.loadBits.structured.stencilBackZFail == 0); + REQUIRE(stateBits0.loadBits.structured.stencilBackFunc == 0); + + const auto& stateBits1 = material->stateBitsTable[1]; + REQUIRE(stateBits1.loadBits.structured.srcBlendRgb == GFXS_BLEND_ONE); + REQUIRE(stateBits1.loadBits.structured.dstBlendRgb == GFXS_BLEND_ZERO); + REQUIRE(stateBits1.loadBits.structured.blendOpRgb == GFXS_BLENDOP_DISABLED); + REQUIRE(stateBits1.loadBits.structured.alphaTestDisabled == 1); + REQUIRE(stateBits1.loadBits.structured.alphaTest == 0); + REQUIRE(stateBits1.loadBits.structured.cullFace == GFXS_CULL_BACK); + REQUIRE(stateBits1.loadBits.structured.srcBlendAlpha == GFXS_BLEND_ONE); + REQUIRE(stateBits1.loadBits.structured.dstBlendAlpha == GFXS_BLEND_ZERO); + REQUIRE(stateBits1.loadBits.structured.blendOpAlpha == GFXS_BLENDOP_DISABLED); + REQUIRE(stateBits1.loadBits.structured.colorWriteRgb == 1); + REQUIRE(stateBits1.loadBits.structured.colorWriteAlpha == 1); + REQUIRE(stateBits1.loadBits.structured.gammaWrite == 0); + REQUIRE(stateBits1.loadBits.structured.polymodeLine == 0); + REQUIRE(stateBits1.loadBits.structured.depthWrite == 1); + REQUIRE(stateBits1.loadBits.structured.depthTestDisabled == 0); + REQUIRE(stateBits1.loadBits.structured.depthTest == GFXS_DEPTHTEST_LESSEQUAL); + REQUIRE(stateBits1.loadBits.structured.polygonOffset == GFXS_POLYGON_OFFSET_0); + REQUIRE(stateBits1.loadBits.structured.stencilFrontEnabled == 0); + REQUIRE(stateBits1.loadBits.structured.stencilFrontPass == 0); + REQUIRE(stateBits1.loadBits.structured.stencilFrontFail == 0); + REQUIRE(stateBits1.loadBits.structured.stencilFrontZFail == 0); + REQUIRE(stateBits1.loadBits.structured.stencilFrontFunc == 0); + REQUIRE(stateBits1.loadBits.structured.stencilBackEnabled == 0); + REQUIRE(stateBits1.loadBits.structured.stencilBackPass == 0); + REQUIRE(stateBits1.loadBits.structured.stencilBackFail == 0); + REQUIRE(stateBits1.loadBits.structured.stencilBackZFail == 0); + REQUIRE(stateBits1.loadBits.structured.stencilBackFunc == 0); + + const auto& stateBits2 = material->stateBitsTable[2]; + REQUIRE(stateBits2.loadBits.structured.srcBlendRgb == GFXS_BLEND_ONE); + REQUIRE(stateBits2.loadBits.structured.dstBlendRgb == GFXS_BLEND_ZERO); + REQUIRE(stateBits2.loadBits.structured.blendOpRgb == GFXS_BLENDOP_DISABLED); + REQUIRE(stateBits2.loadBits.structured.alphaTestDisabled == 1); + REQUIRE(stateBits2.loadBits.structured.alphaTest == 0); + REQUIRE(stateBits2.loadBits.structured.cullFace == GFXS_CULL_NONE); + REQUIRE(stateBits2.loadBits.structured.srcBlendAlpha == GFXS_BLEND_ONE); + REQUIRE(stateBits2.loadBits.structured.dstBlendAlpha == GFXS_BLEND_ZERO); + REQUIRE(stateBits2.loadBits.structured.blendOpAlpha == GFXS_BLENDOP_DISABLED); + REQUIRE(stateBits2.loadBits.structured.colorWriteRgb == 0); + REQUIRE(stateBits2.loadBits.structured.colorWriteAlpha == 0); + REQUIRE(stateBits2.loadBits.structured.gammaWrite == 0); + REQUIRE(stateBits2.loadBits.structured.polymodeLine == 0); + REQUIRE(stateBits2.loadBits.structured.depthWrite == 1); + REQUIRE(stateBits2.loadBits.structured.depthTestDisabled == 0); + REQUIRE(stateBits2.loadBits.structured.depthTest == GFXS_DEPTHTEST_LESSEQUAL); + REQUIRE(stateBits2.loadBits.structured.polygonOffset == GFXS_POLYGON_OFFSET_SHADOWMAP); + REQUIRE(stateBits2.loadBits.structured.stencilFrontEnabled == 0); + REQUIRE(stateBits2.loadBits.structured.stencilFrontPass == 0); + REQUIRE(stateBits2.loadBits.structured.stencilFrontFail == 0); + REQUIRE(stateBits2.loadBits.structured.stencilFrontZFail == 0); + REQUIRE(stateBits2.loadBits.structured.stencilFrontFunc == 0); + REQUIRE(stateBits2.loadBits.structured.stencilBackEnabled == 0); + REQUIRE(stateBits2.loadBits.structured.stencilBackPass == 0); + REQUIRE(stateBits2.loadBits.structured.stencilBackFail == 0); + REQUIRE(stateBits2.loadBits.structured.stencilBackZFail == 0); + REQUIRE(stateBits2.loadBits.structured.stencilBackFunc == 0); + + const auto& stateBits3 = material->stateBitsTable[3]; + REQUIRE(stateBits3.loadBits.structured.srcBlendRgb == GFXS_BLEND_ONE); + REQUIRE(stateBits3.loadBits.structured.dstBlendRgb == GFXS_BLEND_ZERO); + REQUIRE(stateBits3.loadBits.structured.blendOpRgb == GFXS_BLENDOP_DISABLED); + REQUIRE(stateBits3.loadBits.structured.alphaTestDisabled == 1); + REQUIRE(stateBits3.loadBits.structured.alphaTest == 0); + REQUIRE(stateBits3.loadBits.structured.cullFace == GFXS_CULL_NONE); + REQUIRE(stateBits3.loadBits.structured.srcBlendAlpha == GFXS_BLEND_ONE); + REQUIRE(stateBits3.loadBits.structured.dstBlendAlpha == GFXS_BLEND_ZERO); + REQUIRE(stateBits3.loadBits.structured.blendOpAlpha == GFXS_BLENDOP_DISABLED); + REQUIRE(stateBits3.loadBits.structured.colorWriteRgb == 1); + REQUIRE(stateBits3.loadBits.structured.colorWriteAlpha == 1); + REQUIRE(stateBits3.loadBits.structured.gammaWrite == 0); + REQUIRE(stateBits3.loadBits.structured.polymodeLine == 0); + REQUIRE(stateBits3.loadBits.structured.depthWrite == 1); + REQUIRE(stateBits3.loadBits.structured.depthTestDisabled == 0); + REQUIRE(stateBits3.loadBits.structured.depthTest == GFXS_DEPTHTEST_LESSEQUAL); + REQUIRE(stateBits3.loadBits.structured.polygonOffset == GFXS_POLYGON_OFFSET_0); + REQUIRE(stateBits3.loadBits.structured.stencilFrontEnabled == 0); + REQUIRE(stateBits3.loadBits.structured.stencilFrontPass == 0); + REQUIRE(stateBits3.loadBits.structured.stencilFrontFail == 0); + REQUIRE(stateBits3.loadBits.structured.stencilFrontZFail == 0); + REQUIRE(stateBits3.loadBits.structured.stencilFrontFunc == 0); + REQUIRE(stateBits3.loadBits.structured.stencilBackEnabled == 0); + REQUIRE(stateBits3.loadBits.structured.stencilBackPass == 0); + REQUIRE(stateBits3.loadBits.structured.stencilBackFail == 0); + REQUIRE(stateBits3.loadBits.structured.stencilBackZFail == 0); + REQUIRE(stateBits3.loadBits.structured.stencilBackFunc == 0); + + const auto& stateBits4 = material->stateBitsTable[4]; + REQUIRE(stateBits4.loadBits.structured.srcBlendRgb == GFXS_BLEND_ONE); + REQUIRE(stateBits4.loadBits.structured.dstBlendRgb == GFXS_BLEND_ZERO); + REQUIRE(stateBits4.loadBits.structured.blendOpRgb == GFXS_BLENDOP_DISABLED); + REQUIRE(stateBits4.loadBits.structured.alphaTestDisabled == 1); + REQUIRE(stateBits4.loadBits.structured.alphaTest == 0); + REQUIRE(stateBits4.loadBits.structured.cullFace == GFXS_CULL_BACK); + REQUIRE(stateBits4.loadBits.structured.srcBlendAlpha == GFXS_BLEND_ONE); + REQUIRE(stateBits4.loadBits.structured.dstBlendAlpha == GFXS_BLEND_ZERO); + REQUIRE(stateBits4.loadBits.structured.blendOpAlpha == GFXS_BLENDOP_DISABLED); + REQUIRE(stateBits4.loadBits.structured.colorWriteRgb == 1); + REQUIRE(stateBits4.loadBits.structured.colorWriteAlpha == 1); + REQUIRE(stateBits4.loadBits.structured.gammaWrite == 1); + REQUIRE(stateBits4.loadBits.structured.polymodeLine == 0); + REQUIRE(stateBits4.loadBits.structured.depthWrite == 1); + REQUIRE(stateBits4.loadBits.structured.depthTestDisabled == 0); + REQUIRE(stateBits4.loadBits.structured.depthTest == GFXS_DEPTHTEST_LESSEQUAL); + REQUIRE(stateBits4.loadBits.structured.polygonOffset == GFXS_POLYGON_OFFSET_0); + REQUIRE(stateBits4.loadBits.structured.stencilFrontEnabled == 0); + REQUIRE(stateBits4.loadBits.structured.stencilFrontPass == 0); + REQUIRE(stateBits4.loadBits.structured.stencilFrontFail == 0); + REQUIRE(stateBits4.loadBits.structured.stencilFrontZFail == 0); + REQUIRE(stateBits4.loadBits.structured.stencilFrontFunc == 0); + REQUIRE(stateBits4.loadBits.structured.stencilBackEnabled == 0); + REQUIRE(stateBits4.loadBits.structured.stencilBackPass == 0); + REQUIRE(stateBits4.loadBits.structured.stencilBackFail == 0); + REQUIRE(stateBits4.loadBits.structured.stencilBackZFail == 0); + REQUIRE(stateBits4.loadBits.structured.stencilBackFunc == 0); + + const auto& stateBits5 = material->stateBitsTable[5]; + REQUIRE(stateBits5.loadBits.structured.srcBlendRgb == GFXS_BLEND_ONE); + REQUIRE(stateBits5.loadBits.structured.dstBlendRgb == GFXS_BLEND_ONE); + REQUIRE(stateBits5.loadBits.structured.blendOpRgb == GFXS_BLENDOP_ADD); + REQUIRE(stateBits5.loadBits.structured.alphaTestDisabled == 1); + REQUIRE(stateBits5.loadBits.structured.alphaTest == 0); + REQUIRE(stateBits5.loadBits.structured.cullFace == GFXS_CULL_BACK); + REQUIRE(stateBits5.loadBits.structured.srcBlendAlpha == GFXS_BLEND_ONE); + REQUIRE(stateBits5.loadBits.structured.dstBlendAlpha == GFXS_BLEND_ZERO); + REQUIRE(stateBits5.loadBits.structured.blendOpAlpha == GFXS_BLENDOP_DISABLED); + REQUIRE(stateBits5.loadBits.structured.colorWriteRgb == 1); + REQUIRE(stateBits5.loadBits.structured.colorWriteAlpha == 1); + REQUIRE(stateBits5.loadBits.structured.gammaWrite == 1); + REQUIRE(stateBits5.loadBits.structured.polymodeLine == 0); + REQUIRE(stateBits5.loadBits.structured.depthWrite == 0); + REQUIRE(stateBits5.loadBits.structured.depthTestDisabled == 0); + REQUIRE(stateBits5.loadBits.structured.depthTest == GFXS_DEPTHTEST_EQUAL); + REQUIRE(stateBits5.loadBits.structured.polygonOffset == GFXS_POLYGON_OFFSET_0); + REQUIRE(stateBits5.loadBits.structured.stencilFrontEnabled == 1); + REQUIRE(stateBits5.loadBits.structured.stencilFrontPass == GFXS_STENCILOP_KEEP); + REQUIRE(stateBits5.loadBits.structured.stencilFrontFail == GFXS_STENCILOP_KEEP); + REQUIRE(stateBits5.loadBits.structured.stencilFrontZFail == GFXS_STENCILOP_KEEP); + REQUIRE(stateBits5.loadBits.structured.stencilFrontFunc == GFXS_STENCILFUNC_EQUAL); + REQUIRE(stateBits5.loadBits.structured.stencilBackEnabled == 0); + REQUIRE(stateBits5.loadBits.structured.stencilBackPass == 0); + REQUIRE(stateBits5.loadBits.structured.stencilBackFail == 0); + REQUIRE(stateBits5.loadBits.structured.stencilBackZFail == 0); + REQUIRE(stateBits5.loadBits.structured.stencilBackFunc == 0); + + const auto& stateBits6 = material->stateBitsTable[6]; + REQUIRE(stateBits6.loadBits.structured.srcBlendRgb == GFXS_BLEND_ONE); + REQUIRE(stateBits6.loadBits.structured.dstBlendRgb == GFXS_BLEND_ZERO); + REQUIRE(stateBits6.loadBits.structured.blendOpRgb == GFXS_BLENDOP_DISABLED); + REQUIRE(stateBits6.loadBits.structured.alphaTestDisabled == 1); + REQUIRE(stateBits6.loadBits.structured.alphaTest == 0); + REQUIRE(stateBits6.loadBits.structured.cullFace == GFXS_CULL_BACK); + REQUIRE(stateBits6.loadBits.structured.srcBlendAlpha == GFXS_BLEND_ONE); + REQUIRE(stateBits6.loadBits.structured.dstBlendAlpha == GFXS_BLEND_ZERO); + REQUIRE(stateBits6.loadBits.structured.blendOpAlpha == GFXS_BLENDOP_DISABLED); + REQUIRE(stateBits6.loadBits.structured.colorWriteRgb == 1); + REQUIRE(stateBits6.loadBits.structured.colorWriteAlpha == 0); + REQUIRE(stateBits6.loadBits.structured.gammaWrite == 0); + REQUIRE(stateBits6.loadBits.structured.polymodeLine == 1); + REQUIRE(stateBits6.loadBits.structured.depthWrite == 0); + REQUIRE(stateBits6.loadBits.structured.depthTestDisabled == 0); + REQUIRE(stateBits6.loadBits.structured.depthTest == GFXS_DEPTHTEST_LESSEQUAL); + REQUIRE(stateBits6.loadBits.structured.polygonOffset == GFXS_POLYGON_OFFSET_2); + REQUIRE(stateBits6.loadBits.structured.stencilFrontEnabled == 0); + REQUIRE(stateBits6.loadBits.structured.stencilFrontPass == 0); + REQUIRE(stateBits6.loadBits.structured.stencilFrontFail == 0); + REQUIRE(stateBits6.loadBits.structured.stencilFrontZFail == 0); + REQUIRE(stateBits6.loadBits.structured.stencilFrontFunc == 0); + REQUIRE(stateBits6.loadBits.structured.stencilBackEnabled == 0); + REQUIRE(stateBits6.loadBits.structured.stencilBackPass == 0); + REQUIRE(stateBits6.loadBits.structured.stencilBackFail == 0); + REQUIRE(stateBits6.loadBits.structured.stencilBackZFail == 0); + REQUIRE(stateBits6.loadBits.structured.stencilBackFunc == 0); + } +} // namespace diff --git a/test/ObjLoadingTests/Game/T6/Material/LoaderMaterialT6Test.cpp b/test/ObjLoadingTests/Game/T6/Material/LoaderMaterialT6Test.cpp new file mode 100644 index 00000000..2596dc78 --- /dev/null +++ b/test/ObjLoadingTests/Game/T6/Material/LoaderMaterialT6Test.cpp @@ -0,0 +1,491 @@ +#include "Game/T6/Material/LoaderMaterialT6.h" + +#include "Game/T6/CommonT6.h" +#include "Game/T6/GameT6.h" +#include "SearchPath/MockSearchPath.h" +#include "Utils/MemoryManager.h" + +#include +#include +#include + +using namespace T6; +using namespace Catch; +using namespace std::literals; + +namespace +{ + void GivenImage(const std::string& name, AssetCreationContext& context, MemoryManager& memory) + { + auto* image = memory.Alloc(); + image->name = memory.Dup(name.c_str()); + + AssetRegistration registration(name); + registration.SetAsset(image); + context.AddAsset(std::move(registration)); + } + + void GivenTechset(const std::string& name, AssetCreationContext& context, MemoryManager& memory) + { + auto* techset = memory.Alloc(); + techset->name = memory.Dup(name.c_str()); + + AssetRegistration registration(name); + registration.SetAsset(techset); + context.AddAsset(std::move(registration)); + } + + TEST_CASE("LoaderMaterial(T6): Can parse material", "[t6][material][assetloader]") + { + MockSearchPath searchPath; + searchPath.AddFileData("materials/wpc/metal_ac_duct.json", + R"MATERIAL( +{ + "_game": "t6", + "_type": "material", + "_version": 1, + "cameraRegion": "litOpaque", + "constants": [ + { + "literal": [ + 1.0, + 1.0, + 1.0, + 1.0 + ], + "name": "occlusionAmount" + } + ], + "contents": 1, + "gameFlags": [ + "2", + "10", + "CASTS_SHADOW" + ], + "hashIndex": 0, + "layeredSurfaceTypes": 536870925, + "probeMipBits": 0, + "sortKey": 4, + "stateBits": [ + { + "alphaTest": "disabled", + "blendOpAlpha": "disabled", + "blendOpRgb": "disabled", + "colorWriteAlpha": false, + "colorWriteRgb": false, + "cullFace": "back", + "depthTest": "less_equal", + "depthWrite": true, + "dstBlendAlpha": "zero", + "dstBlendRgb": "zero", + "polygonOffset": "offset0", + "polymodeLine": false, + "srcBlendAlpha": "one", + "srcBlendRgb": "one", + "stencilFront": { + "fail": "keep", + "func": "equal", + "pass": "keep", + "zfail": "keep" + } + }, + { + "alphaTest": "disabled", + "blendOpAlpha": "disabled", + "blendOpRgb": "disabled", + "colorWriteAlpha": false, + "colorWriteRgb": false, + "cullFace": "back", + "depthTest": "less_equal", + "depthWrite": true, + "dstBlendAlpha": "zero", + "dstBlendRgb": "zero", + "polygonOffset": "offsetShadowmap", + "polymodeLine": false, + "srcBlendAlpha": "one", + "srcBlendRgb": "one" + }, + { + "alphaTest": "disabled", + "blendOpAlpha": "disabled", + "blendOpRgb": "disabled", + "colorWriteAlpha": true, + "colorWriteRgb": true, + "cullFace": "back", + "depthTest": "less_equal", + "depthWrite": true, + "dstBlendAlpha": "zero", + "dstBlendRgb": "zero", + "polygonOffset": "offset0", + "polymodeLine": false, + "srcBlendAlpha": "one", + "srcBlendRgb": "one" + }, + { + "alphaTest": "disabled", + "blendOpAlpha": "disabled", + "blendOpRgb": "disabled", + "colorWriteAlpha": false, + "colorWriteRgb": true, + "cullFace": "back", + "depthTest": "less_equal", + "depthWrite": false, + "dstBlendAlpha": "zero", + "dstBlendRgb": "zero", + "polygonOffset": "offset2", + "polymodeLine": true, + "srcBlendAlpha": "one", + "srcBlendRgb": "one" + }, + { + "alphaTest": "disabled", + "blendOpAlpha": "disabled", + "blendOpRgb": "add", + "colorWriteAlpha": true, + "colorWriteRgb": true, + "cullFace": "back", + "depthTest": "less_equal", + "depthWrite": true, + "dstBlendAlpha": "zero", + "dstBlendRgb": "one", + "polygonOffset": "offset1", + "polymodeLine": false, + "srcBlendAlpha": "one", + "srcBlendRgb": "one" + } + ], + "stateBitsEntry": [ + 0, + 1, + 2, + -1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + -1, + -1, + -1, + -1, + -1, + -1, + 3, + -1, + 2, + 4 + ], + "stateFlags": 121, + "surfaceFlags": 13631488, + "surfaceTypeBits": 4096, + "techniqueSet": "wpc_lit_sm_r0c0n0s0_1zzj1138", + "textureAtlas": { + "columns": 1, + "rows": 1 + }, + "textures": [ + { + "image": "~~-gmetal_ac_duct_s-rgb&~-rme~0a5a2e6f", + "isMatureContent": false, + "name": "specularMap", + "samplerState": { + "clampU": false, + "clampV": false, + "clampW": false, + "filter": "aniso4x", + "mipMap": "linear" + }, + "semantic": "specularMap" + }, + { + "image": "metal_ac_duct_n", + "isMatureContent": false, + "name": "normalMap", + "samplerState": { + "clampU": false, + "clampV": false, + "clampW": false, + "filter": "aniso4x", + "mipMap": "linear" + }, + "semantic": "normalMap" + }, + { + "image": "~-gmetal_ac_duct_c", + "isMatureContent": false, + "name": "colorMap", + "samplerState": { + "clampU": false, + "clampV": false, + "clampW": false, + "filter": "aniso4x", + "mipMap": "linear" + }, + "semantic": "colorMap" + } + ] +})MATERIAL"); + + Zone zone("MockZone", 0, IGame::GetGameById(GameId::T6)); + + MemoryManager memory; + AssetCreatorCollection creatorCollection(zone); + IgnoredAssetLookup ignoredAssetLookup; + AssetCreationContext context(zone, &creatorCollection, &ignoredAssetLookup); + + GivenImage("~~-gmetal_ac_duct_s-rgb&~-rme~0a5a2e6f", context, memory); + GivenImage("metal_ac_duct_n", context, memory); + GivenImage("~-gmetal_ac_duct_c", context, memory); + GivenTechset("wpc_lit_sm_r0c0n0s0_1zzj1138", context, memory); + + auto loader = CreateMaterialLoader(memory, searchPath); + auto result = loader->CreateAsset("wpc/metal_ac_duct", context); + REQUIRE(result.HasBeenSuccessful()); + + const auto* assetInfo = reinterpret_cast*>(result.GetAssetInfo()); + const auto* material = assetInfo->Asset(); + + REQUIRE(material->info.name == "wpc/metal_ac_duct"s); + REQUIRE(material->info.gameFlags == 0x52); + REQUIRE(material->info.sortKey == 4); + REQUIRE(material->info.textureAtlasRowCount == 1); + REQUIRE(material->info.textureAtlasColumnCount == 1); + REQUIRE(material->info.surfaceTypeBits == 4096); + REQUIRE(material->info.layeredSurfaceTypes == 0x2000000Du); + REQUIRE(material->info.surfaceFlags == 0xD00000u); + REQUIRE(material->info.contents == 1); + + constexpr int8_t expectedStateBitsEntry[]{0, 1, 2, -1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, -1, -1, -1, -1, -1, -1, 3, -1, 2, 4}; + REQUIRE(std::memcmp(material->stateBitsEntry, expectedStateBitsEntry, sizeof(expectedStateBitsEntry)) == 0); + + REQUIRE(material->stateFlags == 121); + REQUIRE(material->cameraRegion == CAMERA_REGION_LIT_OPAQUE); + REQUIRE(material->probeMipBits == 0); + + REQUIRE(material->techniqueSet != nullptr); + REQUIRE(material->techniqueSet->name != nullptr); + REQUIRE(material->techniqueSet->name == "wpc_lit_sm_r0c0n0s0_1zzj1138"s); + + REQUIRE(material->textureCount == 3); + REQUIRE(material->textureTable); + + const auto& textureDef0 = material->textureTable[0]; + REQUIRE(textureDef0.nameHash == 0x34ecccb3); + REQUIRE(textureDef0.nameStart == 's'); + REQUIRE(textureDef0.nameEnd == 'p'); + REQUIRE(textureDef0.samplerState.filter == TEXTURE_FILTER_ANISO4X); + REQUIRE(textureDef0.samplerState.mipMap == SAMPLER_MIPMAP_ENUM_LINEAR); + REQUIRE(textureDef0.samplerState.clampU == 0); + REQUIRE(textureDef0.samplerState.clampV == 0); + REQUIRE(textureDef0.samplerState.clampW == 0); + REQUIRE(textureDef0.semantic == TS_SPECULAR_MAP); + REQUIRE(textureDef0.isMatureContent == false); + REQUIRE(textureDef0.image); + REQUIRE(textureDef0.image->name); + REQUIRE(textureDef0.image->name == "~~-gmetal_ac_duct_s-rgb&~-rme~0a5a2e6f"s); + + const auto& textureDef1 = material->textureTable[1]; + REQUIRE(textureDef1.nameHash == 0x59d30d0f); + REQUIRE(textureDef1.nameStart == 'n'); + REQUIRE(textureDef1.nameEnd == 'p'); + REQUIRE(textureDef1.samplerState.filter == TEXTURE_FILTER_ANISO4X); + REQUIRE(textureDef1.samplerState.mipMap == SAMPLER_MIPMAP_ENUM_LINEAR); + REQUIRE(textureDef1.samplerState.clampU == 0); + REQUIRE(textureDef1.samplerState.clampV == 0); + REQUIRE(textureDef1.samplerState.clampW == 0); + REQUIRE(textureDef1.semantic == TS_NORMAL_MAP); + REQUIRE(textureDef1.isMatureContent == false); + REQUIRE(textureDef1.image); + REQUIRE(textureDef1.image->name); + REQUIRE(textureDef1.image->name == "metal_ac_duct_n"s); + + const auto& textureDef2 = material->textureTable[2]; + REQUIRE(textureDef2.nameHash == 0xa0ab1041); + REQUIRE(textureDef2.nameStart == 'c'); + REQUIRE(textureDef2.nameEnd == 'p'); + REQUIRE(textureDef2.samplerState.filter == TEXTURE_FILTER_ANISO4X); + REQUIRE(textureDef2.samplerState.mipMap == SAMPLER_MIPMAP_ENUM_LINEAR); + REQUIRE(textureDef2.samplerState.clampU == 0); + REQUIRE(textureDef2.samplerState.clampV == 0); + REQUIRE(textureDef2.samplerState.clampW == 0); + REQUIRE(textureDef2.semantic == TS_COLOR_MAP); + REQUIRE(textureDef2.isMatureContent == false); + REQUIRE(textureDef2.image); + REQUIRE(textureDef2.image->name); + REQUIRE(textureDef2.image->name == "~-gmetal_ac_duct_c"s); + + REQUIRE(material->constantCount == 1); + REQUIRE(material->constantTable); + + const auto& constantDef0 = material->constantTable[0]; + REQUIRE(constantDef0.nameHash == 0x9027e5c1); + REQUIRE(strncmp(constantDef0.name, "occlusionAmount", std::extent_v) == 0); + REQUIRE(constantDef0.literal.x == Approx(1.0f)); + REQUIRE(constantDef0.literal.y == Approx(1.0f)); + REQUIRE(constantDef0.literal.z == Approx(1.0f)); + REQUIRE(constantDef0.literal.w == Approx(1.0f)); + + REQUIRE(material->stateBitsCount == 5); + REQUIRE(material->stateBitsTable); + + const auto& stateBits0 = material->stateBitsTable[0]; + REQUIRE(stateBits0.loadBits.structured.srcBlendRgb == GFXS_BLEND_ONE); + REQUIRE(stateBits0.loadBits.structured.dstBlendRgb == GFXS_BLEND_ZERO); + REQUIRE(stateBits0.loadBits.structured.blendOpRgb == GFXS_BLENDOP_DISABLED); + REQUIRE(stateBits0.loadBits.structured.alphaTestDisabled == 1); + REQUIRE(stateBits0.loadBits.structured.alphaTest == 0); + REQUIRE(stateBits0.loadBits.structured.cullFace == GFXS_CULL_BACK); + REQUIRE(stateBits0.loadBits.structured.srcBlendAlpha == GFXS_BLEND_ONE); + REQUIRE(stateBits0.loadBits.structured.dstBlendAlpha == GFXS_BLEND_ZERO); + REQUIRE(stateBits0.loadBits.structured.blendOpAlpha == GFXS_BLENDOP_DISABLED); + REQUIRE(stateBits0.loadBits.structured.colorWriteRgb == 0); + REQUIRE(stateBits0.loadBits.structured.colorWriteAlpha == 0); + REQUIRE(stateBits0.loadBits.structured.polymodeLine == 0); + REQUIRE(stateBits0.loadBits.structured.depthWrite == 1); + REQUIRE(stateBits0.loadBits.structured.depthTestDisabled == 0); + REQUIRE(stateBits0.loadBits.structured.depthTest == GFXS_DEPTHTEST_LESSEQUAL); + REQUIRE(stateBits0.loadBits.structured.polygonOffset == GFXS_POLYGON_OFFSET_0); + REQUIRE(stateBits0.loadBits.structured.stencilFrontEnabled == 1); + REQUIRE(stateBits0.loadBits.structured.stencilFrontPass == GFXS_STENCILOP_KEEP); + REQUIRE(stateBits0.loadBits.structured.stencilFrontFail == GFXS_STENCILOP_KEEP); + REQUIRE(stateBits0.loadBits.structured.stencilFrontZFail == GFXS_STENCILOP_KEEP); + REQUIRE(stateBits0.loadBits.structured.stencilFrontFunc == GFXS_STENCILFUNC_EQUAL); + REQUIRE(stateBits0.loadBits.structured.stencilBackEnabled == 0); + REQUIRE(stateBits0.loadBits.structured.stencilBackPass == 0); + REQUIRE(stateBits0.loadBits.structured.stencilBackFail == 0); + REQUIRE(stateBits0.loadBits.structured.stencilBackZFail == 0); + REQUIRE(stateBits0.loadBits.structured.stencilBackFunc == 0); + + const auto& stateBits1 = material->stateBitsTable[1]; + REQUIRE(stateBits1.loadBits.structured.srcBlendRgb == GFXS_BLEND_ONE); + REQUIRE(stateBits1.loadBits.structured.dstBlendRgb == GFXS_BLEND_ZERO); + REQUIRE(stateBits1.loadBits.structured.blendOpRgb == GFXS_BLENDOP_DISABLED); + REQUIRE(stateBits1.loadBits.structured.alphaTestDisabled == 1); + REQUIRE(stateBits1.loadBits.structured.alphaTest == 0); + REQUIRE(stateBits1.loadBits.structured.cullFace == GFXS_CULL_BACK); + REQUIRE(stateBits1.loadBits.structured.srcBlendAlpha == GFXS_BLEND_ONE); + REQUIRE(stateBits1.loadBits.structured.dstBlendAlpha == GFXS_BLEND_ZERO); + REQUIRE(stateBits1.loadBits.structured.blendOpAlpha == GFXS_BLENDOP_DISABLED); + REQUIRE(stateBits1.loadBits.structured.colorWriteRgb == 0); + REQUIRE(stateBits1.loadBits.structured.colorWriteAlpha == 0); + REQUIRE(stateBits1.loadBits.structured.polymodeLine == 0); + REQUIRE(stateBits1.loadBits.structured.depthWrite == 1); + REQUIRE(stateBits1.loadBits.structured.depthTestDisabled == 0); + REQUIRE(stateBits1.loadBits.structured.depthTest == GFXS_DEPTHTEST_LESSEQUAL); + REQUIRE(stateBits1.loadBits.structured.polygonOffset == GFXS_POLYGON_OFFSET_SHADOWMAP); + REQUIRE(stateBits1.loadBits.structured.stencilFrontEnabled == 0); + REQUIRE(stateBits1.loadBits.structured.stencilFrontPass == 0); + REQUIRE(stateBits1.loadBits.structured.stencilFrontFail == 0); + REQUIRE(stateBits1.loadBits.structured.stencilFrontZFail == 0); + REQUIRE(stateBits1.loadBits.structured.stencilFrontFunc == 0); + REQUIRE(stateBits1.loadBits.structured.stencilBackEnabled == 0); + REQUIRE(stateBits1.loadBits.structured.stencilBackPass == 0); + REQUIRE(stateBits1.loadBits.structured.stencilBackFail == 0); + REQUIRE(stateBits1.loadBits.structured.stencilBackZFail == 0); + REQUIRE(stateBits1.loadBits.structured.stencilBackFunc == 0); + + const auto& stateBits2 = material->stateBitsTable[2]; + REQUIRE(stateBits2.loadBits.structured.srcBlendRgb == GFXS_BLEND_ONE); + REQUIRE(stateBits2.loadBits.structured.dstBlendRgb == GFXS_BLEND_ZERO); + REQUIRE(stateBits2.loadBits.structured.blendOpRgb == GFXS_BLENDOP_DISABLED); + REQUIRE(stateBits2.loadBits.structured.alphaTestDisabled == 1); + REQUIRE(stateBits2.loadBits.structured.alphaTest == 0); + REQUIRE(stateBits2.loadBits.structured.cullFace == GFXS_CULL_BACK); + REQUIRE(stateBits2.loadBits.structured.srcBlendAlpha == GFXS_BLEND_ONE); + REQUIRE(stateBits2.loadBits.structured.dstBlendAlpha == GFXS_BLEND_ZERO); + REQUIRE(stateBits2.loadBits.structured.blendOpAlpha == GFXS_BLENDOP_DISABLED); + REQUIRE(stateBits2.loadBits.structured.colorWriteRgb == 1); + REQUIRE(stateBits2.loadBits.structured.colorWriteAlpha == 1); + REQUIRE(stateBits2.loadBits.structured.polymodeLine == 0); + REQUIRE(stateBits2.loadBits.structured.depthWrite == 1); + REQUIRE(stateBits2.loadBits.structured.depthTestDisabled == 0); + REQUIRE(stateBits2.loadBits.structured.depthTest == GFXS_DEPTHTEST_LESSEQUAL); + REQUIRE(stateBits2.loadBits.structured.polygonOffset == GFXS_POLYGON_OFFSET_0); + REQUIRE(stateBits2.loadBits.structured.stencilFrontEnabled == 0); + REQUIRE(stateBits2.loadBits.structured.stencilFrontPass == 0); + REQUIRE(stateBits2.loadBits.structured.stencilFrontFail == 0); + REQUIRE(stateBits2.loadBits.structured.stencilFrontZFail == 0); + REQUIRE(stateBits2.loadBits.structured.stencilFrontFunc == 0); + REQUIRE(stateBits2.loadBits.structured.stencilBackEnabled == 0); + REQUIRE(stateBits2.loadBits.structured.stencilBackPass == 0); + REQUIRE(stateBits2.loadBits.structured.stencilBackFail == 0); + REQUIRE(stateBits2.loadBits.structured.stencilBackZFail == 0); + REQUIRE(stateBits2.loadBits.structured.stencilBackFunc == 0); + + const auto& stateBits3 = material->stateBitsTable[3]; + REQUIRE(stateBits3.loadBits.structured.srcBlendRgb == GFXS_BLEND_ONE); + REQUIRE(stateBits3.loadBits.structured.dstBlendRgb == GFXS_BLEND_ZERO); + REQUIRE(stateBits3.loadBits.structured.blendOpRgb == GFXS_BLENDOP_DISABLED); + REQUIRE(stateBits3.loadBits.structured.alphaTestDisabled == 1); + REQUIRE(stateBits3.loadBits.structured.alphaTest == 0); + REQUIRE(stateBits3.loadBits.structured.cullFace == GFXS_CULL_BACK); + REQUIRE(stateBits3.loadBits.structured.srcBlendAlpha == GFXS_BLEND_ONE); + REQUIRE(stateBits3.loadBits.structured.dstBlendAlpha == GFXS_BLEND_ZERO); + REQUIRE(stateBits3.loadBits.structured.blendOpAlpha == GFXS_BLENDOP_DISABLED); + REQUIRE(stateBits3.loadBits.structured.colorWriteRgb == 1); + REQUIRE(stateBits3.loadBits.structured.colorWriteAlpha == 0); + REQUIRE(stateBits3.loadBits.structured.polymodeLine == 1); + REQUIRE(stateBits3.loadBits.structured.depthWrite == 0); + REQUIRE(stateBits3.loadBits.structured.depthTestDisabled == 0); + REQUIRE(stateBits3.loadBits.structured.depthTest == GFXS_DEPTHTEST_LESSEQUAL); + REQUIRE(stateBits3.loadBits.structured.polygonOffset == GFXS_POLYGON_OFFSET_2); + REQUIRE(stateBits3.loadBits.structured.stencilFrontEnabled == 0); + REQUIRE(stateBits3.loadBits.structured.stencilFrontPass == 0); + REQUIRE(stateBits3.loadBits.structured.stencilFrontFail == 0); + REQUIRE(stateBits3.loadBits.structured.stencilFrontZFail == 0); + REQUIRE(stateBits3.loadBits.structured.stencilFrontFunc == 0); + REQUIRE(stateBits3.loadBits.structured.stencilBackEnabled == 0); + REQUIRE(stateBits3.loadBits.structured.stencilBackPass == 0); + REQUIRE(stateBits3.loadBits.structured.stencilBackFail == 0); + REQUIRE(stateBits3.loadBits.structured.stencilBackZFail == 0); + REQUIRE(stateBits3.loadBits.structured.stencilBackFunc == 0); + + const auto& stateBits4 = material->stateBitsTable[4]; + REQUIRE(stateBits4.loadBits.structured.srcBlendRgb == GFXS_BLEND_ONE); + REQUIRE(stateBits4.loadBits.structured.dstBlendRgb == GFXS_BLEND_ONE); + REQUIRE(stateBits4.loadBits.structured.blendOpRgb == GFXS_BLENDOP_ADD); + REQUIRE(stateBits4.loadBits.structured.alphaTestDisabled == 1); + REQUIRE(stateBits4.loadBits.structured.alphaTest == 0); + REQUIRE(stateBits4.loadBits.structured.cullFace == GFXS_CULL_BACK); + REQUIRE(stateBits4.loadBits.structured.srcBlendAlpha == GFXS_BLEND_ONE); + REQUIRE(stateBits4.loadBits.structured.dstBlendAlpha == GFXS_BLEND_ZERO); + REQUIRE(stateBits4.loadBits.structured.blendOpAlpha == GFXS_BLENDOP_DISABLED); + REQUIRE(stateBits4.loadBits.structured.colorWriteRgb == 1); + REQUIRE(stateBits4.loadBits.structured.colorWriteAlpha == 1); + REQUIRE(stateBits4.loadBits.structured.polymodeLine == 0); + REQUIRE(stateBits4.loadBits.structured.depthWrite == 1); + REQUIRE(stateBits4.loadBits.structured.depthTestDisabled == 0); + REQUIRE(stateBits4.loadBits.structured.depthTest == GFXS_DEPTHTEST_LESSEQUAL); + REQUIRE(stateBits4.loadBits.structured.polygonOffset == GFXS_POLYGON_OFFSET_1); + REQUIRE(stateBits4.loadBits.structured.stencilFrontEnabled == 0); + REQUIRE(stateBits4.loadBits.structured.stencilFrontPass == 0); + REQUIRE(stateBits4.loadBits.structured.stencilFrontFail == 0); + REQUIRE(stateBits4.loadBits.structured.stencilFrontZFail == 0); + REQUIRE(stateBits4.loadBits.structured.stencilFrontFunc == 0); + REQUIRE(stateBits4.loadBits.structured.stencilBackEnabled == 0); + REQUIRE(stateBits4.loadBits.structured.stencilBackPass == 0); + REQUIRE(stateBits4.loadBits.structured.stencilBackFail == 0); + REQUIRE(stateBits4.loadBits.structured.stencilBackZFail == 0); + REQUIRE(stateBits4.loadBits.structured.stencilBackFunc == 0); + + REQUIRE(material->thermalMaterial == nullptr); + } +} // namespace diff --git a/test/ObjWritingTests.lua b/test/ObjWritingTests.lua new file mode 100644 index 00000000..8afcba6c --- /dev/null +++ b/test/ObjWritingTests.lua @@ -0,0 +1,58 @@ +ObjWritingTests = {} + +function ObjWritingTests:include(includes) + if includes:handle(self:name()) then + includedirs { + path.join(TestFolder(), "ObjWritingTests") + } + end +end + +function ObjWritingTests:link(links) + +end + +function ObjWritingTests:use() + +end + +function ObjWritingTests:name() + return "ObjWritingTests" +end + +function ObjWritingTests:project() + local folder = TestFolder() + local includes = Includes:create() + local links = Links:create() + + project(self:name()) + targetdir(TargetDirectoryTest) + location "%{wks.location}/test/%{prj.name}" + kind "ConsoleApp" + language "C++" + + files { + path.join(folder, "ObjWritingTests/**.h"), + path.join(folder, "ObjWritingTests/**.cpp") + } + + vpaths { + ["*"] = { + path.join(folder, "ObjWritingTests") + } + } + + self:include(includes) + Catch2Common:include(includes) + ObjCommonTestUtils:include(includes) + ParserTestUtils:include(includes) + ObjWriting:include(includes) + catch2:include(includes) + + links:linkto(ObjCommonTestUtils) + links:linkto(ParserTestUtils) + links:linkto(ObjWriting) + links:linkto(catch2) + links:linkto(Catch2Common) + links:linkall() +end diff --git a/test/ObjWritingTests/Game/IW4/Material/DumperMaterialIW4Test.cpp b/test/ObjWritingTests/Game/IW4/Material/DumperMaterialIW4Test.cpp new file mode 100644 index 00000000..2d33bd2d --- /dev/null +++ b/test/ObjWritingTests/Game/IW4/Material/DumperMaterialIW4Test.cpp @@ -0,0 +1,552 @@ +#include "Game/IW4/Material/DumperMaterialIW4.h" + +#include "Asset/AssetRegistration.h" +#include "Game/IW4/CommonIW4.h" +#include "Game/IW4/GameIW4.h" +#include "NormalizedJson.h" +#include "Pool/AssetPoolDynamic.h" +#include "SearchPath/MockOutputPath.h" +#include "SearchPath/MockSearchPath.h" +#include "Utils/MemoryManager.h" + +#include +#include +#include + +using namespace IW4; +using namespace Catch; +using namespace std::literals; + +namespace +{ + GfxImage* GivenImage(const std::string& name, MemoryManager& memory) + { + auto* image = memory.Alloc(); + image->name = memory.Dup(name.c_str()); + + return image; + } + + MaterialTechniqueSet* GivenTechset(const std::string& name, MemoryManager& memory) + { + auto* techset = memory.Alloc(); + techset->name = memory.Dup(name.c_str()); + + return techset; + } + + void GivenMaterial(const std::string& name, AssetPool& pool, MemoryManager& memory) + { + auto* material = memory.Alloc(); + material->info.name = memory.Dup(name.c_str()); + material->info.gameFlags = 0x50; + material->info.sortKey = 1; + material->info.textureAtlasRowCount = 1; + material->info.textureAtlasColumnCount = 1; + material->info.surfaceTypeBits = 0x10; + + constexpr int8_t stateBitsEntry[] = {0, 1, 2, 1, 3, -1, -1, -1, -1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, -1, -1, -1, -1, 5, -1, 3, 3}; + std::memcpy(material->stateBitsEntry, stateBitsEntry, sizeof(material->stateBitsEntry)); + + material->cameraRegion = CAMERA_REGION_LIT_OPAQUE; + material->stateFlags = 121; + material->techniqueSet = GivenTechset("mc_l_sm_r0c0n0s0", memory); + + material->textureCount = 3; + material->textureTable = memory.Alloc(3); + + auto& textureDef0 = material->textureTable[0]; + textureDef0.u.image = GivenImage("~ch_rubble02_spc-r-74g-74b-74~1eb1f0d0", memory); + textureDef0.nameHash = 0x34ecccb3; + textureDef0.nameStart = 's'; + textureDef0.nameEnd = 'p'; + textureDef0.samplerState.filter = TEXTURE_FILTER_ANISO2X; + textureDef0.samplerState.mipMap = TEXTURE_FILTER_NEAREST; + textureDef0.samplerState.clampU = 0; + textureDef0.samplerState.clampV = 0; + textureDef0.samplerState.clampW = 0; + textureDef0.semantic = TS_SPECULAR_MAP; + + auto& textureDef1 = material->textureTable[1]; + textureDef1.u.image = GivenImage("ch_rubble01_nml", memory); + textureDef1.nameHash = 0x59d30d0f; + textureDef1.nameStart = 'n'; + textureDef1.nameEnd = 'p'; + textureDef1.samplerState.filter = TEXTURE_FILTER_ANISO2X; + textureDef1.samplerState.mipMap = TEXTURE_FILTER_NEAREST; + textureDef1.samplerState.clampU = 0; + textureDef1.samplerState.clampV = 0; + textureDef1.samplerState.clampW = 0; + textureDef1.semantic = TS_NORMAL_MAP; + + auto& textureDef2 = material->textureTable[2]; + textureDef2.u.image = GivenImage("ch_rubble01_col", memory); + textureDef2.nameHash = 0xa0ab1041; + textureDef2.nameStart = 'c'; + textureDef2.nameEnd = 'p'; + textureDef2.samplerState.filter = TEXTURE_FILTER_ANISO2X; + textureDef2.samplerState.mipMap = TEXTURE_FILTER_NEAREST; + textureDef2.samplerState.clampU = 0; + textureDef2.samplerState.clampV = 0; + textureDef2.samplerState.clampW = 0; + textureDef2.semantic = TS_COLOR_MAP; + + material->constantCount = 2; + material->constantTable = memory.Alloc(2); + + auto& constantDef0 = material->constantTable[0]; + constantDef0.nameHash = 0x3d9994dc; + strncpy(constantDef0.name, "envMapParms", std::extent_v); + constantDef0.literal.x = 0.07f; + constantDef0.literal.y = 0.46f; + constantDef0.literal.z = 1.6f; + constantDef0.literal.w = 2.0f; + + auto& constantDef1 = material->constantTable[1]; + constantDef1.nameHash = 0xb60c3b3a; + strncpy(constantDef1.name, "colorTint", std::extent_v); + constantDef1.literal.x = 1.0f; + constantDef1.literal.y = 1.0f; + constantDef1.literal.z = 1.0f; + constantDef1.literal.w = 1.0f; + + material->stateBitsCount = 6; + material->stateBitsTable = memory.Alloc(6); + + auto& stateBits0 = material->stateBitsTable[0]; + stateBits0.loadBits.structured.srcBlendRgb = GFXS_BLEND_ONE; + stateBits0.loadBits.structured.dstBlendRgb = GFXS_BLEND_ZERO; + stateBits0.loadBits.structured.blendOpRgb = GFXS_BLENDOP_DISABLED; + stateBits0.loadBits.structured.alphaTestDisabled = 1; + stateBits0.loadBits.structured.alphaTest = 0; + stateBits0.loadBits.structured.cullFace = GFXS_CULL_BACK; + stateBits0.loadBits.structured.srcBlendAlpha = GFXS_BLEND_ONE; + stateBits0.loadBits.structured.dstBlendAlpha = GFXS_BLEND_ZERO; + stateBits0.loadBits.structured.blendOpAlpha = GFXS_BLENDOP_DISABLED; + stateBits0.loadBits.structured.colorWriteRgb = 0; + stateBits0.loadBits.structured.colorWriteAlpha = 0; + stateBits0.loadBits.structured.gammaWrite = 0; + stateBits0.loadBits.structured.polymodeLine = 0; + stateBits0.loadBits.structured.depthWrite = 1; + stateBits0.loadBits.structured.depthTestDisabled = 0; + stateBits0.loadBits.structured.depthTest = GFXS_DEPTHTEST_LESSEQUAL; + stateBits0.loadBits.structured.polygonOffset = GFXS_POLYGON_OFFSET_0; + stateBits0.loadBits.structured.stencilFrontEnabled = 0; + stateBits0.loadBits.structured.stencilFrontPass = 0; + stateBits0.loadBits.structured.stencilFrontFail = 0; + stateBits0.loadBits.structured.stencilFrontZFail = 0; + stateBits0.loadBits.structured.stencilFrontFunc = 0; + stateBits0.loadBits.structured.stencilBackEnabled = 0; + stateBits0.loadBits.structured.stencilBackPass = 0; + stateBits0.loadBits.structured.stencilBackFail = 0; + stateBits0.loadBits.structured.stencilBackZFail = 0; + stateBits0.loadBits.structured.stencilBackFunc = 0; + + auto& stateBits1 = material->stateBitsTable[1]; + stateBits1.loadBits.structured.srcBlendRgb = GFXS_BLEND_ONE; + stateBits1.loadBits.structured.dstBlendRgb = GFXS_BLEND_ZERO; + stateBits1.loadBits.structured.blendOpRgb = GFXS_BLENDOP_DISABLED; + stateBits1.loadBits.structured.alphaTestDisabled = 1; + stateBits1.loadBits.structured.alphaTest = 0; + stateBits1.loadBits.structured.cullFace = GFXS_CULL_BACK; + stateBits1.loadBits.structured.srcBlendAlpha = GFXS_BLEND_ONE; + stateBits1.loadBits.structured.dstBlendAlpha = GFXS_BLEND_ZERO; + stateBits1.loadBits.structured.blendOpAlpha = GFXS_BLENDOP_DISABLED; + stateBits1.loadBits.structured.colorWriteRgb = 1; + stateBits1.loadBits.structured.colorWriteAlpha = 1; + stateBits1.loadBits.structured.gammaWrite = 0; + stateBits1.loadBits.structured.polymodeLine = 0; + stateBits1.loadBits.structured.depthWrite = 1; + stateBits1.loadBits.structured.depthTestDisabled = 0; + stateBits1.loadBits.structured.depthTest = GFXS_DEPTHTEST_LESSEQUAL; + stateBits1.loadBits.structured.polygonOffset = GFXS_POLYGON_OFFSET_0; + stateBits1.loadBits.structured.stencilFrontEnabled = 0; + stateBits1.loadBits.structured.stencilFrontPass = 0; + stateBits1.loadBits.structured.stencilFrontFail = 0; + stateBits1.loadBits.structured.stencilFrontZFail = 0; + stateBits1.loadBits.structured.stencilFrontFunc = 0; + stateBits1.loadBits.structured.stencilBackEnabled = 0; + stateBits1.loadBits.structured.stencilBackPass = 0; + stateBits1.loadBits.structured.stencilBackFail = 0; + stateBits1.loadBits.structured.stencilBackZFail = 0; + stateBits1.loadBits.structured.stencilBackFunc = 0; + + auto& stateBits2 = material->stateBitsTable[2]; + stateBits2.loadBits.structured.srcBlendRgb = GFXS_BLEND_ONE; + stateBits2.loadBits.structured.dstBlendRgb = GFXS_BLEND_ZERO; + stateBits2.loadBits.structured.blendOpRgb = GFXS_BLENDOP_DISABLED; + stateBits2.loadBits.structured.alphaTestDisabled = 1; + stateBits2.loadBits.structured.alphaTest = 0; + stateBits2.loadBits.structured.cullFace = GFXS_CULL_BACK; + stateBits2.loadBits.structured.srcBlendAlpha = GFXS_BLEND_ONE; + stateBits2.loadBits.structured.dstBlendAlpha = GFXS_BLEND_ZERO; + stateBits2.loadBits.structured.blendOpAlpha = GFXS_BLENDOP_DISABLED; + stateBits2.loadBits.structured.colorWriteRgb = 0; + stateBits2.loadBits.structured.colorWriteAlpha = 0; + stateBits2.loadBits.structured.gammaWrite = 0; + stateBits2.loadBits.structured.polymodeLine = 0; + stateBits2.loadBits.structured.depthWrite = 1; + stateBits2.loadBits.structured.depthTestDisabled = 0; + stateBits2.loadBits.structured.depthTest = GFXS_DEPTHTEST_LESSEQUAL; + stateBits2.loadBits.structured.polygonOffset = GFXS_POLYGON_OFFSET_SHADOWMAP; + stateBits2.loadBits.structured.stencilFrontEnabled = 0; + stateBits2.loadBits.structured.stencilFrontPass = 0; + stateBits2.loadBits.structured.stencilFrontFail = 0; + stateBits2.loadBits.structured.stencilFrontZFail = 0; + stateBits2.loadBits.structured.stencilFrontFunc = 0; + stateBits2.loadBits.structured.stencilBackEnabled = 0; + stateBits2.loadBits.structured.stencilBackPass = 0; + stateBits2.loadBits.structured.stencilBackFail = 0; + stateBits2.loadBits.structured.stencilBackZFail = 0; + stateBits2.loadBits.structured.stencilBackFunc = 0; + + auto& stateBits3 = material->stateBitsTable[3]; + stateBits3.loadBits.structured.srcBlendRgb = GFXS_BLEND_ONE; + stateBits3.loadBits.structured.dstBlendRgb = GFXS_BLEND_ZERO; + stateBits3.loadBits.structured.blendOpRgb = GFXS_BLENDOP_DISABLED; + stateBits3.loadBits.structured.alphaTestDisabled = 1; + stateBits3.loadBits.structured.alphaTest = 0; + stateBits3.loadBits.structured.cullFace = GFXS_CULL_BACK; + stateBits3.loadBits.structured.srcBlendAlpha = GFXS_BLEND_ONE; + stateBits3.loadBits.structured.dstBlendAlpha = GFXS_BLEND_ZERO; + stateBits3.loadBits.structured.blendOpAlpha = GFXS_BLENDOP_DISABLED; + stateBits3.loadBits.structured.colorWriteRgb = 1; + stateBits3.loadBits.structured.colorWriteAlpha = 1; + stateBits3.loadBits.structured.gammaWrite = 1; + stateBits3.loadBits.structured.polymodeLine = 0; + stateBits3.loadBits.structured.depthWrite = 1; + stateBits3.loadBits.structured.depthTestDisabled = 0; + stateBits3.loadBits.structured.depthTest = GFXS_DEPTHTEST_LESSEQUAL; + stateBits3.loadBits.structured.polygonOffset = GFXS_POLYGON_OFFSET_0; + stateBits3.loadBits.structured.stencilFrontEnabled = 0; + stateBits3.loadBits.structured.stencilFrontPass = 0; + stateBits3.loadBits.structured.stencilFrontFail = 0; + stateBits3.loadBits.structured.stencilFrontZFail = 0; + stateBits3.loadBits.structured.stencilFrontFunc = 0; + stateBits3.loadBits.structured.stencilBackEnabled = 0; + stateBits3.loadBits.structured.stencilBackPass = 0; + stateBits3.loadBits.structured.stencilBackFail = 0; + stateBits3.loadBits.structured.stencilBackZFail = 0; + stateBits3.loadBits.structured.stencilBackFunc = 0; + + auto& stateBits4 = material->stateBitsTable[4]; + stateBits4.loadBits.structured.srcBlendRgb = GFXS_BLEND_ONE; + stateBits4.loadBits.structured.dstBlendRgb = GFXS_BLEND_ONE; + stateBits4.loadBits.structured.blendOpRgb = GFXS_BLENDOP_ADD; + stateBits4.loadBits.structured.alphaTestDisabled = 1; + stateBits4.loadBits.structured.alphaTest = 0; + stateBits4.loadBits.structured.cullFace = GFXS_CULL_BACK; + stateBits4.loadBits.structured.srcBlendAlpha = GFXS_BLEND_ONE; + stateBits4.loadBits.structured.dstBlendAlpha = GFXS_BLEND_ZERO; + stateBits4.loadBits.structured.blendOpAlpha = GFXS_BLENDOP_DISABLED; + stateBits4.loadBits.structured.colorWriteRgb = 1; + stateBits4.loadBits.structured.colorWriteAlpha = 1; + stateBits4.loadBits.structured.gammaWrite = 1; + stateBits4.loadBits.structured.polymodeLine = 0; + stateBits4.loadBits.structured.depthWrite = 0; + stateBits4.loadBits.structured.depthTestDisabled = 0; + stateBits4.loadBits.structured.depthTest = GFXS_DEPTHTEST_EQUAL; + stateBits4.loadBits.structured.polygonOffset = GFXS_POLYGON_OFFSET_0; + stateBits4.loadBits.structured.stencilFrontEnabled = 1; + stateBits4.loadBits.structured.stencilFrontPass = GFXS_STENCILOP_KEEP; + stateBits4.loadBits.structured.stencilFrontFail = GFXS_STENCILOP_KEEP; + stateBits4.loadBits.structured.stencilFrontZFail = GFXS_STENCILOP_KEEP; + stateBits4.loadBits.structured.stencilFrontFunc = GFXS_STENCILFUNC_EQUAL; + stateBits4.loadBits.structured.stencilBackEnabled = 0; + stateBits4.loadBits.structured.stencilBackPass = 0; + stateBits4.loadBits.structured.stencilBackFail = 0; + stateBits4.loadBits.structured.stencilBackZFail = 0; + stateBits4.loadBits.structured.stencilBackFunc = 0; + + auto& stateBits5 = material->stateBitsTable[5]; + stateBits5.loadBits.structured.srcBlendRgb = GFXS_BLEND_ONE; + stateBits5.loadBits.structured.dstBlendRgb = GFXS_BLEND_ZERO; + stateBits5.loadBits.structured.blendOpRgb = GFXS_BLENDOP_DISABLED; + stateBits5.loadBits.structured.alphaTestDisabled = 1; + stateBits5.loadBits.structured.alphaTest = 0; + stateBits5.loadBits.structured.cullFace = GFXS_CULL_BACK; + stateBits5.loadBits.structured.srcBlendAlpha = GFXS_BLEND_ONE; + stateBits5.loadBits.structured.dstBlendAlpha = GFXS_BLEND_ZERO; + stateBits5.loadBits.structured.blendOpAlpha = GFXS_BLENDOP_DISABLED; + stateBits5.loadBits.structured.colorWriteRgb = 1; + stateBits5.loadBits.structured.colorWriteAlpha = 0; + stateBits5.loadBits.structured.gammaWrite = 0; + stateBits5.loadBits.structured.polymodeLine = 1; + stateBits5.loadBits.structured.depthWrite = 0; + stateBits5.loadBits.structured.depthTestDisabled = 0; + stateBits5.loadBits.structured.depthTest = GFXS_DEPTHTEST_LESSEQUAL; + stateBits5.loadBits.structured.polygonOffset = GFXS_POLYGON_OFFSET_2; + stateBits5.loadBits.structured.stencilFrontEnabled = 0; + stateBits5.loadBits.structured.stencilFrontPass = 0; + stateBits5.loadBits.structured.stencilFrontFail = 0; + stateBits5.loadBits.structured.stencilFrontZFail = 0; + stateBits5.loadBits.structured.stencilFrontFunc = 0; + stateBits5.loadBits.structured.stencilBackEnabled = 0; + stateBits5.loadBits.structured.stencilBackPass = 0; + stateBits5.loadBits.structured.stencilBackFail = 0; + stateBits5.loadBits.structured.stencilBackZFail = 0; + stateBits5.loadBits.structured.stencilBackFunc = 0; + + pool.AddAsset(std::make_unique>(ASSET_TYPE_MATERIAL, name, material)); + } + + TEST_CASE("DumperMaterial(IW4): Can dump material", "[iw4][material][assetdumper]") + { + std::string expected(R"MATERIAL( +{ + "_game": "iw4", + "_type": "material", + "_version": 1, + "cameraRegion": "litOpaque", + "constants": [ + { + "literal": [ + 0.07000000029802322, + 0.46000000834465027, + 1.600000023841858, + 2.0 + ], + "name": "envMapParms" + }, + { + "literal": [ + 1.0, + 1.0, + 1.0, + 1.0 + ], + "name": "colorTint" + } + ], + "gameFlags": [ + "10", + "40" + ], + "sortKey": 1, + "stateBits": [ + { + "alphaTest": "disabled", + "blendOpAlpha": "disabled", + "blendOpRgb": "disabled", + "colorWriteAlpha": false, + "colorWriteRgb": false, + "cullFace": "back", + "depthTest": "less_equal", + "depthWrite": true, + "dstBlendAlpha": "zero", + "dstBlendRgb": "zero", + "gammaWrite": false, + "polygonOffset": "offset0", + "polymodeLine": false, + "srcBlendAlpha": "one", + "srcBlendRgb": "one" + }, + { + "alphaTest": "disabled", + "blendOpAlpha": "disabled", + "blendOpRgb": "disabled", + "colorWriteAlpha": true, + "colorWriteRgb": true, + "cullFace": "back", + "depthTest": "less_equal", + "depthWrite": true, + "dstBlendAlpha": "zero", + "dstBlendRgb": "zero", + "gammaWrite": false, + "polygonOffset": "offset0", + "polymodeLine": false, + "srcBlendAlpha": "one", + "srcBlendRgb": "one" + }, + { + "alphaTest": "disabled", + "blendOpAlpha": "disabled", + "blendOpRgb": "disabled", + "colorWriteAlpha": false, + "colorWriteRgb": false, + "cullFace": "back", + "depthTest": "less_equal", + "depthWrite": true, + "dstBlendAlpha": "zero", + "dstBlendRgb": "zero", + "gammaWrite": false, + "polygonOffset": "offsetShadowmap", + "polymodeLine": false, + "srcBlendAlpha": "one", + "srcBlendRgb": "one" + }, + { + "alphaTest": "disabled", + "blendOpAlpha": "disabled", + "blendOpRgb": "disabled", + "colorWriteAlpha": true, + "colorWriteRgb": true, + "cullFace": "back", + "depthTest": "less_equal", + "depthWrite": true, + "dstBlendAlpha": "zero", + "dstBlendRgb": "zero", + "gammaWrite": true, + "polygonOffset": "offset0", + "polymodeLine": false, + "srcBlendAlpha": "one", + "srcBlendRgb": "one" + }, + { + "alphaTest": "disabled", + "blendOpAlpha": "disabled", + "blendOpRgb": "add", + "colorWriteAlpha": true, + "colorWriteRgb": true, + "cullFace": "back", + "depthTest": "equal", + "depthWrite": false, + "dstBlendAlpha": "zero", + "dstBlendRgb": "one", + "gammaWrite": true, + "polygonOffset": "offset0", + "polymodeLine": false, + "srcBlendAlpha": "one", + "srcBlendRgb": "one", + "stencilFront": { + "fail": "keep", + "func": "equal", + "pass": "keep", + "zfail": "keep" + } + }, + { + "alphaTest": "disabled", + "blendOpAlpha": "disabled", + "blendOpRgb": "disabled", + "colorWriteAlpha": false, + "colorWriteRgb": true, + "cullFace": "back", + "depthTest": "less_equal", + "depthWrite": false, + "dstBlendAlpha": "zero", + "dstBlendRgb": "zero", + "gammaWrite": false, + "polygonOffset": "offset2", + "polymodeLine": true, + "srcBlendAlpha": "one", + "srcBlendRgb": "one" + } + ], + "stateBitsEntry": [ + 0, + 1, + 2, + 1, + 3, + -1, + -1, + -1, + -1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + -1, + -1, + -1, + -1, + 5, + -1, + 3, + 3 + ], + "stateFlags": 121, + "surfaceTypeBits": 16, + "techniqueSet": "mc_l_sm_r0c0n0s0", + "textureAtlas": { + "columns": 1, + "rows": 1 + }, + "textures": [ + { + "image": "~ch_rubble02_spc-r-74g-74b-74~1eb1f0d0", + "name": "specularMap", + "samplerState": { + "clampU": false, + "clampV": false, + "clampW": false, + "filter": "aniso2x", + "mipMap": "nearest" + }, + "semantic": "specularMap" + }, + { + "image": "ch_rubble01_nml", + "name": "normalMap", + "samplerState": { + "clampU": false, + "clampV": false, + "clampW": false, + "filter": "aniso2x", + "mipMap": "nearest" + }, + "semantic": "normalMap" + }, + { + "image": "ch_rubble01_col", + "name": "colorMap", + "samplerState": { + "clampU": false, + "clampV": false, + "clampW": false, + "filter": "aniso2x", + "mipMap": "nearest" + }, + "semantic": "colorMap" + } + ] +})MATERIAL"); + + Zone zone("MockZone", 0, IGame::GetGameById(GameId::IW4)); + + MemoryManager memory; + MockSearchPath mockObjPath; + MockOutputPath mockOutput; + AssetDumpingContext context(zone, "", mockOutput, mockObjPath); + + AssetPoolDynamic materialPool(0); + + GivenMaterial("mc/ch_rubble01", materialPool, memory); + + AssetDumperMaterial dumper; + dumper.DumpPool(context, &materialPool); + + const auto* file = mockOutput.GetMockedFile("materials/mc/ch_rubble01.json"); + REQUIRE(file); + REQUIRE(JsonNormalized(file->AsString()) == JsonNormalized(expected)); + } +} // namespace diff --git a/test/ObjWritingTests/Game/IW5/Material/DumperMaterialIW5Test.cpp b/test/ObjWritingTests/Game/IW5/Material/DumperMaterialIW5Test.cpp new file mode 100644 index 00000000..cf545055 --- /dev/null +++ b/test/ObjWritingTests/Game/IW5/Material/DumperMaterialIW5Test.cpp @@ -0,0 +1,605 @@ +#include "Game/IW5/Material/DumperMaterialIW5.h" + +#include "Asset/AssetRegistration.h" +#include "Game/IW5/CommonIW5.h" +#include "Game/IW5/GameIW5.h" +#include "NormalizedJson.h" +#include "Pool/AssetPoolDynamic.h" +#include "SearchPath/MockOutputPath.h" +#include "SearchPath/MockSearchPath.h" +#include "Utils/MemoryManager.h" + +#include +#include +#include + +using namespace IW5; +using namespace Catch; +using namespace std::literals; + +namespace +{ + GfxImage* GivenImage(const std::string& name, MemoryManager& memory) + { + auto* image = memory.Alloc(); + image->name = memory.Dup(name.c_str()); + + return image; + } + + MaterialTechniqueSet* GivenTechset(const std::string& name, MemoryManager& memory) + { + auto* techset = memory.Alloc(); + techset->name = memory.Dup(name.c_str()); + + return techset; + } + + void GivenMaterial(const std::string& name, AssetPool& pool, MemoryManager& memory) + { + auto* material = memory.Alloc(); + material->info.name = memory.Dup(name.c_str()); + material->info.gameFlags = 0x52; + material->info.sortKey = 1; + material->info.textureAtlasRowCount = 1; + material->info.textureAtlasColumnCount = 1; + material->info.surfaceTypeBits = 0x1000; + + constexpr int8_t stateBitsEntry[] = {0, 1, 2, 3, 4, -1, -1, -1, -1, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 5, 5, 5, 5, -1, -1, -1, -1, 6, -1, -1, 4, -1}; + std::memcpy(material->stateBitsEntry, stateBitsEntry, sizeof(material->stateBitsEntry)); + + material->cameraRegion = CAMERA_REGION_LIT_OPAQUE; + material->stateFlags = 59; + material->techniqueSet = GivenTechset("wc_l_sm_r0c0n0s0", memory); + + material->textureCount = 3; + material->textureTable = memory.Alloc(3); + + auto& textureDef0 = material->textureTable[0]; + textureDef0.u.image = GivenImage("~me_metal_rusty02_spc-rgb&me_~927de80f", memory); + textureDef0.nameHash = 0x34ecccb3; + textureDef0.nameStart = 's'; + textureDef0.nameEnd = 'p'; + textureDef0.samplerState.filter = TEXTURE_FILTER_ANISO2X; + textureDef0.samplerState.mipMap = TEXTURE_FILTER_NEAREST; + textureDef0.samplerState.clampU = 0; + textureDef0.samplerState.clampV = 0; + textureDef0.samplerState.clampW = 0; + textureDef0.semantic = TS_SPECULAR_MAP; + + auto& textureDef1 = material->textureTable[1]; + textureDef1.u.image = GivenImage("me_metal_rusty02_nml", memory); + textureDef1.nameHash = 0x59d30d0f; + textureDef1.nameStart = 'n'; + textureDef1.nameEnd = 'p'; + textureDef1.samplerState.filter = TEXTURE_FILTER_ANISO2X; + textureDef1.samplerState.mipMap = TEXTURE_FILTER_LINEAR; + textureDef1.samplerState.clampU = 0; + textureDef1.samplerState.clampV = 0; + textureDef1.samplerState.clampW = 0; + textureDef1.semantic = TS_NORMAL_MAP; + + auto& textureDef2 = material->textureTable[2]; + textureDef2.u.image = GivenImage("me_metal_rusty02_col", memory); + textureDef2.nameHash = 0xa0ab1041; + textureDef2.nameStart = 'c'; + textureDef2.nameEnd = 'p'; + textureDef2.samplerState.filter = TEXTURE_FILTER_ANISO2X; + textureDef2.samplerState.mipMap = TEXTURE_FILTER_LINEAR; + textureDef2.samplerState.clampU = 0; + textureDef2.samplerState.clampV = 0; + textureDef2.samplerState.clampW = 0; + textureDef2.semantic = TS_COLOR_MAP; + + material->constantCount = 2; + material->constantTable = memory.Alloc(2); + + auto& constantDef0 = material->constantTable[0]; + constantDef0.nameHash = 0x3d9994dc; + strncpy(constantDef0.name, "envMapParms", std::extent_v); + constantDef0.literal.x = 0.07f; + constantDef0.literal.y = 0.33f; + constantDef0.literal.z = 1.4f; + constantDef0.literal.w = 2.0f; + + auto& constantDef1 = material->constantTable[1]; + constantDef1.nameHash = 0xb60c3b3a; + strncpy(constantDef1.name, "colorTint", std::extent_v); + constantDef1.literal.x = 1.0f; + constantDef1.literal.y = 1.0f; + constantDef1.literal.z = 1.0f; + constantDef1.literal.w = 1.0f; + + material->stateBitsCount = 7; + material->stateBitsTable = memory.Alloc(7); + + auto& stateBits0 = material->stateBitsTable[0]; + stateBits0.loadBits.structured.srcBlendRgb = GFXS_BLEND_ONE; + stateBits0.loadBits.structured.dstBlendRgb = GFXS_BLEND_ZERO; + stateBits0.loadBits.structured.blendOpRgb = GFXS_BLENDOP_DISABLED; + stateBits0.loadBits.structured.alphaTestDisabled = 1; + stateBits0.loadBits.structured.alphaTest = 0; + stateBits0.loadBits.structured.cullFace = GFXS_CULL_BACK; + stateBits0.loadBits.structured.srcBlendAlpha = GFXS_BLEND_ONE; + stateBits0.loadBits.structured.dstBlendAlpha = GFXS_BLEND_ZERO; + stateBits0.loadBits.structured.blendOpAlpha = GFXS_BLENDOP_DISABLED; + stateBits0.loadBits.structured.colorWriteRgb = 0; + stateBits0.loadBits.structured.colorWriteAlpha = 0; + stateBits0.loadBits.structured.gammaWrite = 0; + stateBits0.loadBits.structured.polymodeLine = 0; + stateBits0.loadBits.structured.depthWrite = 1; + stateBits0.loadBits.structured.depthTestDisabled = 0; + stateBits0.loadBits.structured.depthTest = GFXS_DEPTHTEST_LESSEQUAL; + stateBits0.loadBits.structured.polygonOffset = GFXS_POLYGON_OFFSET_0; + stateBits0.loadBits.structured.stencilFrontEnabled = 0; + stateBits0.loadBits.structured.stencilFrontPass = 0; + stateBits0.loadBits.structured.stencilFrontFail = 0; + stateBits0.loadBits.structured.stencilFrontZFail = 0; + stateBits0.loadBits.structured.stencilFrontFunc = 0; + stateBits0.loadBits.structured.stencilBackEnabled = 0; + stateBits0.loadBits.structured.stencilBackPass = 0; + stateBits0.loadBits.structured.stencilBackFail = 0; + stateBits0.loadBits.structured.stencilBackZFail = 0; + stateBits0.loadBits.structured.stencilBackFunc = 0; + + auto& stateBits1 = material->stateBitsTable[1]; + stateBits1.loadBits.structured.srcBlendRgb = GFXS_BLEND_ONE; + stateBits1.loadBits.structured.dstBlendRgb = GFXS_BLEND_ZERO; + stateBits1.loadBits.structured.blendOpRgb = GFXS_BLENDOP_DISABLED; + stateBits1.loadBits.structured.alphaTestDisabled = 1; + stateBits1.loadBits.structured.alphaTest = 0; + stateBits1.loadBits.structured.cullFace = GFXS_CULL_BACK; + stateBits1.loadBits.structured.srcBlendAlpha = GFXS_BLEND_ONE; + stateBits1.loadBits.structured.dstBlendAlpha = GFXS_BLEND_ZERO; + stateBits1.loadBits.structured.blendOpAlpha = GFXS_BLENDOP_DISABLED; + stateBits1.loadBits.structured.colorWriteRgb = 1; + stateBits1.loadBits.structured.colorWriteAlpha = 1; + stateBits1.loadBits.structured.gammaWrite = 0; + stateBits1.loadBits.structured.polymodeLine = 0; + stateBits1.loadBits.structured.depthWrite = 1; + stateBits1.loadBits.structured.depthTestDisabled = 0; + stateBits1.loadBits.structured.depthTest = GFXS_DEPTHTEST_LESSEQUAL; + stateBits1.loadBits.structured.polygonOffset = GFXS_POLYGON_OFFSET_0; + stateBits1.loadBits.structured.stencilFrontEnabled = 0; + stateBits1.loadBits.structured.stencilFrontPass = 0; + stateBits1.loadBits.structured.stencilFrontFail = 0; + stateBits1.loadBits.structured.stencilFrontZFail = 0; + stateBits1.loadBits.structured.stencilFrontFunc = 0; + stateBits1.loadBits.structured.stencilBackEnabled = 0; + stateBits1.loadBits.structured.stencilBackPass = 0; + stateBits1.loadBits.structured.stencilBackFail = 0; + stateBits1.loadBits.structured.stencilBackZFail = 0; + stateBits1.loadBits.structured.stencilBackFunc = 0; + + auto& stateBits2 = material->stateBitsTable[2]; + stateBits2.loadBits.structured.srcBlendRgb = GFXS_BLEND_ONE; + stateBits2.loadBits.structured.dstBlendRgb = GFXS_BLEND_ZERO; + stateBits2.loadBits.structured.blendOpRgb = GFXS_BLENDOP_DISABLED; + stateBits2.loadBits.structured.alphaTestDisabled = 1; + stateBits2.loadBits.structured.alphaTest = 0; + stateBits2.loadBits.structured.cullFace = GFXS_CULL_NONE; + stateBits2.loadBits.structured.srcBlendAlpha = GFXS_BLEND_ONE; + stateBits2.loadBits.structured.dstBlendAlpha = GFXS_BLEND_ZERO; + stateBits2.loadBits.structured.blendOpAlpha = GFXS_BLENDOP_DISABLED; + stateBits2.loadBits.structured.colorWriteRgb = 0; + stateBits2.loadBits.structured.colorWriteAlpha = 0; + stateBits2.loadBits.structured.gammaWrite = 0; + stateBits2.loadBits.structured.polymodeLine = 0; + stateBits2.loadBits.structured.depthWrite = 1; + stateBits2.loadBits.structured.depthTestDisabled = 0; + stateBits2.loadBits.structured.depthTest = GFXS_DEPTHTEST_LESSEQUAL; + stateBits2.loadBits.structured.polygonOffset = GFXS_POLYGON_OFFSET_SHADOWMAP; + stateBits2.loadBits.structured.stencilFrontEnabled = 0; + stateBits2.loadBits.structured.stencilFrontPass = 0; + stateBits2.loadBits.structured.stencilFrontFail = 0; + stateBits2.loadBits.structured.stencilFrontZFail = 0; + stateBits2.loadBits.structured.stencilFrontFunc = 0; + stateBits2.loadBits.structured.stencilBackEnabled = 0; + stateBits2.loadBits.structured.stencilBackPass = 0; + stateBits2.loadBits.structured.stencilBackFail = 0; + stateBits2.loadBits.structured.stencilBackZFail = 0; + stateBits2.loadBits.structured.stencilBackFunc = 0; + + auto& stateBits3 = material->stateBitsTable[3]; + stateBits3.loadBits.structured.srcBlendRgb = GFXS_BLEND_ONE; + stateBits3.loadBits.structured.dstBlendRgb = GFXS_BLEND_ZERO; + stateBits3.loadBits.structured.blendOpRgb = GFXS_BLENDOP_DISABLED; + stateBits3.loadBits.structured.alphaTestDisabled = 1; + stateBits3.loadBits.structured.alphaTest = 0; + stateBits3.loadBits.structured.cullFace = GFXS_CULL_NONE; + stateBits3.loadBits.structured.srcBlendAlpha = GFXS_BLEND_ONE; + stateBits3.loadBits.structured.dstBlendAlpha = GFXS_BLEND_ZERO; + stateBits3.loadBits.structured.blendOpAlpha = GFXS_BLENDOP_DISABLED; + stateBits3.loadBits.structured.colorWriteRgb = 1; + stateBits3.loadBits.structured.colorWriteAlpha = 1; + stateBits3.loadBits.structured.gammaWrite = 0; + stateBits3.loadBits.structured.polymodeLine = 0; + stateBits3.loadBits.structured.depthWrite = 1; + stateBits3.loadBits.structured.depthTestDisabled = 0; + stateBits3.loadBits.structured.depthTest = GFXS_DEPTHTEST_LESSEQUAL; + stateBits3.loadBits.structured.polygonOffset = GFXS_POLYGON_OFFSET_0; + stateBits3.loadBits.structured.stencilFrontEnabled = 0; + stateBits3.loadBits.structured.stencilFrontPass = 0; + stateBits3.loadBits.structured.stencilFrontFail = 0; + stateBits3.loadBits.structured.stencilFrontZFail = 0; + stateBits3.loadBits.structured.stencilFrontFunc = 0; + stateBits3.loadBits.structured.stencilBackEnabled = 0; + stateBits3.loadBits.structured.stencilBackPass = 0; + stateBits3.loadBits.structured.stencilBackFail = 0; + stateBits3.loadBits.structured.stencilBackZFail = 0; + stateBits3.loadBits.structured.stencilBackFunc = 0; + + auto& stateBits4 = material->stateBitsTable[4]; + stateBits4.loadBits.structured.srcBlendRgb = GFXS_BLEND_ONE; + stateBits4.loadBits.structured.dstBlendRgb = GFXS_BLEND_ZERO; + stateBits4.loadBits.structured.blendOpRgb = GFXS_BLENDOP_DISABLED; + stateBits4.loadBits.structured.alphaTestDisabled = 1; + stateBits4.loadBits.structured.alphaTest = 0; + stateBits4.loadBits.structured.cullFace = GFXS_CULL_BACK; + stateBits4.loadBits.structured.srcBlendAlpha = GFXS_BLEND_ONE; + stateBits4.loadBits.structured.dstBlendAlpha = GFXS_BLEND_ZERO; + stateBits4.loadBits.structured.blendOpAlpha = GFXS_BLENDOP_DISABLED; + stateBits4.loadBits.structured.colorWriteRgb = 1; + stateBits4.loadBits.structured.colorWriteAlpha = 1; + stateBits4.loadBits.structured.gammaWrite = 1; + stateBits4.loadBits.structured.polymodeLine = 0; + stateBits4.loadBits.structured.depthWrite = 1; + stateBits4.loadBits.structured.depthTestDisabled = 0; + stateBits4.loadBits.structured.depthTest = GFXS_DEPTHTEST_LESSEQUAL; + stateBits4.loadBits.structured.polygonOffset = GFXS_POLYGON_OFFSET_0; + stateBits4.loadBits.structured.stencilFrontEnabled = 0; + stateBits4.loadBits.structured.stencilFrontPass = 0; + stateBits4.loadBits.structured.stencilFrontFail = 0; + stateBits4.loadBits.structured.stencilFrontZFail = 0; + stateBits4.loadBits.structured.stencilFrontFunc = 0; + stateBits4.loadBits.structured.stencilBackEnabled = 0; + stateBits4.loadBits.structured.stencilBackPass = 0; + stateBits4.loadBits.structured.stencilBackFail = 0; + stateBits4.loadBits.structured.stencilBackZFail = 0; + stateBits4.loadBits.structured.stencilBackFunc = 0; + + auto& stateBits5 = material->stateBitsTable[5]; + stateBits5.loadBits.structured.srcBlendRgb = GFXS_BLEND_ONE; + stateBits5.loadBits.structured.dstBlendRgb = GFXS_BLEND_ONE; + stateBits5.loadBits.structured.blendOpRgb = GFXS_BLENDOP_ADD; + stateBits5.loadBits.structured.alphaTestDisabled = 1; + stateBits5.loadBits.structured.alphaTest = 0; + stateBits5.loadBits.structured.cullFace = GFXS_CULL_BACK; + stateBits5.loadBits.structured.srcBlendAlpha = GFXS_BLEND_ONE; + stateBits5.loadBits.structured.dstBlendAlpha = GFXS_BLEND_ZERO; + stateBits5.loadBits.structured.blendOpAlpha = GFXS_BLENDOP_DISABLED; + stateBits5.loadBits.structured.colorWriteRgb = 1; + stateBits5.loadBits.structured.colorWriteAlpha = 1; + stateBits5.loadBits.structured.gammaWrite = 1; + stateBits5.loadBits.structured.polymodeLine = 0; + stateBits5.loadBits.structured.depthWrite = 0; + stateBits5.loadBits.structured.depthTestDisabled = 0; + stateBits5.loadBits.structured.depthTest = GFXS_DEPTHTEST_EQUAL; + stateBits5.loadBits.structured.polygonOffset = GFXS_POLYGON_OFFSET_0; + stateBits5.loadBits.structured.stencilFrontEnabled = 1; + stateBits5.loadBits.structured.stencilFrontPass = GFXS_STENCILOP_KEEP; + stateBits5.loadBits.structured.stencilFrontFail = GFXS_STENCILOP_KEEP; + stateBits5.loadBits.structured.stencilFrontZFail = GFXS_STENCILOP_KEEP; + stateBits5.loadBits.structured.stencilFrontFunc = GFXS_STENCILFUNC_EQUAL; + stateBits5.loadBits.structured.stencilBackEnabled = 0; + stateBits5.loadBits.structured.stencilBackPass = 0; + stateBits5.loadBits.structured.stencilBackFail = 0; + stateBits5.loadBits.structured.stencilBackZFail = 0; + stateBits5.loadBits.structured.stencilBackFunc = 0; + + auto& stateBits6 = material->stateBitsTable[6]; + stateBits6.loadBits.structured.srcBlendRgb = GFXS_BLEND_ONE; + stateBits6.loadBits.structured.dstBlendRgb = GFXS_BLEND_ZERO; + stateBits6.loadBits.structured.blendOpRgb = GFXS_BLENDOP_DISABLED; + stateBits6.loadBits.structured.alphaTestDisabled = 1; + stateBits6.loadBits.structured.alphaTest = 0; + stateBits6.loadBits.structured.cullFace = GFXS_CULL_BACK; + stateBits6.loadBits.structured.srcBlendAlpha = GFXS_BLEND_ONE; + stateBits6.loadBits.structured.dstBlendAlpha = GFXS_BLEND_ZERO; + stateBits6.loadBits.structured.blendOpAlpha = GFXS_BLENDOP_DISABLED; + stateBits6.loadBits.structured.colorWriteRgb = 1; + stateBits6.loadBits.structured.colorWriteAlpha = 0; + stateBits6.loadBits.structured.gammaWrite = 0; + stateBits6.loadBits.structured.polymodeLine = 1; + stateBits6.loadBits.structured.depthWrite = 0; + stateBits6.loadBits.structured.depthTestDisabled = 0; + stateBits6.loadBits.structured.depthTest = GFXS_DEPTHTEST_LESSEQUAL; + stateBits6.loadBits.structured.polygonOffset = GFXS_POLYGON_OFFSET_2; + stateBits6.loadBits.structured.stencilFrontEnabled = 0; + stateBits6.loadBits.structured.stencilFrontPass = 0; + stateBits6.loadBits.structured.stencilFrontFail = 0; + stateBits6.loadBits.structured.stencilFrontZFail = 0; + stateBits6.loadBits.structured.stencilFrontFunc = 0; + stateBits6.loadBits.structured.stencilBackEnabled = 0; + stateBits6.loadBits.structured.stencilBackPass = 0; + stateBits6.loadBits.structured.stencilBackFail = 0; + stateBits6.loadBits.structured.stencilBackZFail = 0; + stateBits6.loadBits.structured.stencilBackFunc = 0; + + pool.AddAsset(std::make_unique>(ASSET_TYPE_MATERIAL, name, material)); + } + + TEST_CASE("DumperMaterial(IW5): Can dump material", "[iw5][material][assetdumper]") + { + std::string expected(R"MATERIAL( +{ + "_game": "iw5", + "_type": "material", + "_version": 1, + "cameraRegion": "litOpaque", + "constants": [ + { + "literal": [ + 0.07000000029802322, + 0.33000001311302185, + 1.399999976158142, + 2.0 + ], + "name": "envMapParms" + }, + { + "literal": [ + 1.0, + 1.0, + 1.0, + 1.0 + ], + "name": "colorTint" + } + ], + "gameFlags": [ + "2", + "10", + "40" + ], + "sortKey": 1, + "stateBits": [ + { + "alphaTest": "disabled", + "blendOpAlpha": "disabled", + "blendOpRgb": "disabled", + "colorWriteAlpha": false, + "colorWriteRgb": false, + "cullFace": "back", + "depthTest": "less_equal", + "depthWrite": true, + "dstBlendAlpha": "zero", + "dstBlendRgb": "zero", + "gammaWrite": false, + "polygonOffset": "offset0", + "polymodeLine": false, + "srcBlendAlpha": "one", + "srcBlendRgb": "one" + }, + { + "alphaTest": "disabled", + "blendOpAlpha": "disabled", + "blendOpRgb": "disabled", + "colorWriteAlpha": true, + "colorWriteRgb": true, + "cullFace": "back", + "depthTest": "less_equal", + "depthWrite": true, + "dstBlendAlpha": "zero", + "dstBlendRgb": "zero", + "gammaWrite": false, + "polygonOffset": "offset0", + "polymodeLine": false, + "srcBlendAlpha": "one", + "srcBlendRgb": "one" + }, + { + "alphaTest": "disabled", + "blendOpAlpha": "disabled", + "blendOpRgb": "disabled", + "colorWriteAlpha": false, + "colorWriteRgb": false, + "cullFace": "none", + "depthTest": "less_equal", + "depthWrite": true, + "dstBlendAlpha": "zero", + "dstBlendRgb": "zero", + "gammaWrite": false, + "polygonOffset": "offsetShadowmap", + "polymodeLine": false, + "srcBlendAlpha": "one", + "srcBlendRgb": "one" + }, + { + "alphaTest": "disabled", + "blendOpAlpha": "disabled", + "blendOpRgb": "disabled", + "colorWriteAlpha": true, + "colorWriteRgb": true, + "cullFace": "none", + "depthTest": "less_equal", + "depthWrite": true, + "dstBlendAlpha": "zero", + "dstBlendRgb": "zero", + "gammaWrite": false, + "polygonOffset": "offset0", + "polymodeLine": false, + "srcBlendAlpha": "one", + "srcBlendRgb": "one" + }, + { + "alphaTest": "disabled", + "blendOpAlpha": "disabled", + "blendOpRgb": "disabled", + "colorWriteAlpha": true, + "colorWriteRgb": true, + "cullFace": "back", + "depthTest": "less_equal", + "depthWrite": true, + "dstBlendAlpha": "zero", + "dstBlendRgb": "zero", + "gammaWrite": true, + "polygonOffset": "offset0", + "polymodeLine": false, + "srcBlendAlpha": "one", + "srcBlendRgb": "one" + }, + { + "alphaTest": "disabled", + "blendOpAlpha": "disabled", + "blendOpRgb": "add", + "colorWriteAlpha": true, + "colorWriteRgb": true, + "cullFace": "back", + "depthTest": "equal", + "depthWrite": false, + "dstBlendAlpha": "zero", + "dstBlendRgb": "one", + "gammaWrite": true, + "polygonOffset": "offset0", + "polymodeLine": false, + "srcBlendAlpha": "one", + "srcBlendRgb": "one", + "stencilFront": { + "fail": "keep", + "func": "equal", + "pass": "keep", + "zfail": "keep" + } + }, + { + "alphaTest": "disabled", + "blendOpAlpha": "disabled", + "blendOpRgb": "disabled", + "colorWriteAlpha": false, + "colorWriteRgb": true, + "cullFace": "back", + "depthTest": "less_equal", + "depthWrite": false, + "dstBlendAlpha": "zero", + "dstBlendRgb": "zero", + "gammaWrite": false, + "polygonOffset": "offset2", + "polymodeLine": true, + "srcBlendAlpha": "one", + "srcBlendRgb": "one" + } + ], + "stateBitsEntry": [ + 0, + 1, + 2, + 3, + 4, + -1, + -1, + -1, + -1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + 5, + 5, + 5, + 5, + -1, + -1, + -1, + -1, + 6, + -1, + -1, + 4, + -1 + ], + "stateFlags": 59, + "surfaceTypeBits": 4096, + "techniqueSet": "wc_l_sm_r0c0n0s0", + "textureAtlas": { + "columns": 1, + "rows": 1 + }, + "textures": [ + { + "image": "~me_metal_rusty02_spc-rgb&me_~927de80f", + "name": "specularMap", + "samplerState": { + "clampU": false, + "clampV": false, + "clampW": false, + "filter": "aniso2x", + "mipMap": "nearest" + }, + "semantic": "specularMap" + }, + { + "image": "me_metal_rusty02_nml", + "name": "normalMap", + "samplerState": { + "clampU": false, + "clampV": false, + "clampW": false, + "filter": "aniso2x", + "mipMap": "linear" + }, + "semantic": "normalMap" + }, + { + "image": "me_metal_rusty02_col", + "name": "colorMap", + "samplerState": { + "clampU": false, + "clampV": false, + "clampW": false, + "filter": "aniso2x", + "mipMap": "linear" + }, + "semantic": "colorMap" + } + ] +})MATERIAL"); + + Zone zone("MockZone", 0, IGame::GetGameById(GameId::IW5)); + + MemoryManager memory; + MockSearchPath mockObjPath; + MockOutputPath mockOutput; + AssetDumpingContext context(zone, "", mockOutput, mockObjPath); + + AssetPoolDynamic materialPool(0); + + GivenMaterial("wc/me_metal_rust_02", materialPool, memory); + + AssetDumperMaterial dumper; + dumper.DumpPool(context, &materialPool); + + const auto* file = mockOutput.GetMockedFile("materials/wc/me_metal_rust_02.json"); + REQUIRE(file); + REQUIRE(JsonNormalized(file->AsString()) == JsonNormalized(expected)); + } +} // namespace diff --git a/test/ObjWritingTests/Game/T6/Material/DumperMaterialT6Test.cpp b/test/ObjWritingTests/Game/T6/Material/DumperMaterialT6Test.cpp new file mode 100644 index 00000000..4c6e5f50 --- /dev/null +++ b/test/ObjWritingTests/Game/T6/Material/DumperMaterialT6Test.cpp @@ -0,0 +1,484 @@ +#include "Game/T6/Material/DumperMaterialT6.h" + +#include "Asset/AssetRegistration.h" +#include "Game/T6/CommonT6.h" +#include "Game/T6/GameT6.h" +#include "NormalizedJson.h" +#include "Pool/AssetPoolDynamic.h" +#include "SearchPath/MockOutputPath.h" +#include "SearchPath/MockSearchPath.h" +#include "Utils/MemoryManager.h" + +#include +#include +#include + +using namespace T6; +using namespace Catch; +using namespace std::literals; + +namespace +{ + GfxImage* GivenImage(const std::string& name, MemoryManager& memory) + { + auto* image = memory.Alloc(); + image->name = memory.Dup(name.c_str()); + + return image; + } + + MaterialTechniqueSet* GivenTechset(const std::string& name, MemoryManager& memory) + { + auto* techset = memory.Alloc(); + techset->name = memory.Dup(name.c_str()); + + return techset; + } + + void GivenMaterial(const std::string& name, AssetPool& pool, MemoryManager& memory) + { + auto* material = memory.Alloc(); + material->info.name = memory.Dup(name.c_str()); + material->info.gameFlags = 0x52; + material->info.sortKey = 4; + material->info.textureAtlasRowCount = 1; + material->info.textureAtlasColumnCount = 1; + material->info.surfaceTypeBits = 0x1000; + material->info.layeredSurfaceTypes = 0x2000000Du; + material->info.surfaceFlags = 0xD00000u; + material->info.contents = 1; + + constexpr int8_t stateBitsEntry[] = {0, 1, 2, -1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, -1, -1, -1, -1, -1, -1, 3, -1, 2, 4}; + std::memcpy(material->stateBitsEntry, stateBitsEntry, sizeof(material->stateBitsEntry)); + + material->cameraRegion = CAMERA_REGION_LIT_OPAQUE; + material->stateFlags = 121; + material->techniqueSet = GivenTechset("wpc_lit_sm_r0c0n0s0_1zzj1138", memory); + + material->textureCount = 3; + material->textureTable = memory.Alloc(3); + + auto& textureDef0 = material->textureTable[0]; + textureDef0.image = GivenImage("~~-gmetal_ac_duct_s-rgb&~-rme~0a5a2e6f", memory); + textureDef0.nameHash = 0x34ecccb3; + textureDef0.nameStart = 's'; + textureDef0.nameEnd = 'p'; + textureDef0.samplerState.filter = TEXTURE_FILTER_ANISO4X; + textureDef0.samplerState.mipMap = SAMPLER_MIPMAP_ENUM_LINEAR; + textureDef0.samplerState.clampU = 0; + textureDef0.samplerState.clampV = 0; + textureDef0.samplerState.clampW = 0; + textureDef0.semantic = TS_SPECULAR_MAP; + textureDef0.isMatureContent = false; + + auto& textureDef1 = material->textureTable[1]; + textureDef1.image = GivenImage("metal_ac_duct_n", memory); + textureDef1.nameHash = 0x59d30d0f; + textureDef1.nameStart = 'n'; + textureDef1.nameEnd = 'p'; + textureDef1.samplerState.filter = TEXTURE_FILTER_ANISO4X; + textureDef1.samplerState.mipMap = SAMPLER_MIPMAP_ENUM_LINEAR; + textureDef1.samplerState.clampU = 0; + textureDef1.samplerState.clampV = 0; + textureDef1.samplerState.clampW = 0; + textureDef1.semantic = TS_NORMAL_MAP; + textureDef1.isMatureContent = false; + + auto& textureDef2 = material->textureTable[2]; + textureDef2.image = GivenImage("~-gmetal_ac_duct_c", memory); + textureDef2.nameHash = 0xa0ab1041; + textureDef2.nameStart = 'c'; + textureDef2.nameEnd = 'p'; + textureDef2.samplerState.filter = TEXTURE_FILTER_ANISO4X; + textureDef2.samplerState.mipMap = SAMPLER_MIPMAP_ENUM_LINEAR; + textureDef2.samplerState.clampU = 0; + textureDef2.samplerState.clampV = 0; + textureDef2.samplerState.clampW = 0; + textureDef2.semantic = TS_COLOR_MAP; + textureDef2.isMatureContent = false; + + material->constantCount = 1; + material->constantTable = memory.Alloc(1); + + auto& constantDef0 = material->constantTable[0]; + constantDef0.nameHash = 0x9027e5c1; + strncpy(constantDef0.name, "occlusionAmount", std::extent_v); + constantDef0.literal.x = 1.0f; + constantDef0.literal.y = 1.0f; + constantDef0.literal.z = 1.0f; + constantDef0.literal.w = 1.0f; + + material->stateBitsCount = 5; + material->stateBitsTable = memory.Alloc(5); + + auto& stateBits0 = material->stateBitsTable[0]; + stateBits0.loadBits.structured.srcBlendRgb = GFXS_BLEND_ONE; + stateBits0.loadBits.structured.dstBlendRgb = GFXS_BLEND_ZERO; + stateBits0.loadBits.structured.blendOpRgb = GFXS_BLENDOP_DISABLED; + stateBits0.loadBits.structured.alphaTestDisabled = 1; + stateBits0.loadBits.structured.alphaTest = 0; + stateBits0.loadBits.structured.cullFace = GFXS_CULL_BACK; + stateBits0.loadBits.structured.srcBlendAlpha = GFXS_BLEND_ONE; + stateBits0.loadBits.structured.dstBlendAlpha = GFXS_BLEND_ZERO; + stateBits0.loadBits.structured.blendOpAlpha = GFXS_BLENDOP_DISABLED; + stateBits0.loadBits.structured.colorWriteRgb = 0; + stateBits0.loadBits.structured.colorWriteAlpha = 0; + stateBits0.loadBits.structured.polymodeLine = 0; + stateBits0.loadBits.structured.depthWrite = 1; + stateBits0.loadBits.structured.depthTestDisabled = 0; + stateBits0.loadBits.structured.depthTest = GFXS_DEPTHTEST_LESSEQUAL; + stateBits0.loadBits.structured.polygonOffset = GFXS_POLYGON_OFFSET_0; + stateBits0.loadBits.structured.stencilFrontEnabled = 1; + stateBits0.loadBits.structured.stencilFrontPass = GFXS_STENCILOP_KEEP; + stateBits0.loadBits.structured.stencilFrontFail = GFXS_STENCILOP_KEEP; + stateBits0.loadBits.structured.stencilFrontZFail = GFXS_STENCILOP_KEEP; + stateBits0.loadBits.structured.stencilFrontFunc = GFXS_STENCILFUNC_EQUAL; + stateBits0.loadBits.structured.stencilBackEnabled = 0; + stateBits0.loadBits.structured.stencilBackPass = 0; + stateBits0.loadBits.structured.stencilBackFail = 0; + stateBits0.loadBits.structured.stencilBackZFail = 0; + stateBits0.loadBits.structured.stencilBackFunc = 0; + + auto& stateBits1 = material->stateBitsTable[1]; + stateBits1.loadBits.structured.srcBlendRgb = GFXS_BLEND_ONE; + stateBits1.loadBits.structured.dstBlendRgb = GFXS_BLEND_ZERO; + stateBits1.loadBits.structured.blendOpRgb = GFXS_BLENDOP_DISABLED; + stateBits1.loadBits.structured.alphaTestDisabled = 1; + stateBits1.loadBits.structured.alphaTest = 0; + stateBits1.loadBits.structured.cullFace = GFXS_CULL_BACK; + stateBits1.loadBits.structured.srcBlendAlpha = GFXS_BLEND_ONE; + stateBits1.loadBits.structured.dstBlendAlpha = GFXS_BLEND_ZERO; + stateBits1.loadBits.structured.blendOpAlpha = GFXS_BLENDOP_DISABLED; + stateBits1.loadBits.structured.colorWriteRgb = 0; + stateBits1.loadBits.structured.colorWriteAlpha = 0; + stateBits1.loadBits.structured.polymodeLine = 0; + stateBits1.loadBits.structured.depthWrite = 1; + stateBits1.loadBits.structured.depthTestDisabled = 0; + stateBits1.loadBits.structured.depthTest = GFXS_DEPTHTEST_LESSEQUAL; + stateBits1.loadBits.structured.polygonOffset = GFXS_POLYGON_OFFSET_SHADOWMAP; + stateBits1.loadBits.structured.stencilFrontEnabled = 0; + stateBits1.loadBits.structured.stencilFrontPass = 0; + stateBits1.loadBits.structured.stencilFrontFail = 0; + stateBits1.loadBits.structured.stencilFrontZFail = 0; + stateBits1.loadBits.structured.stencilFrontFunc = 0; + stateBits1.loadBits.structured.stencilBackEnabled = 0; + stateBits1.loadBits.structured.stencilBackPass = 0; + stateBits1.loadBits.structured.stencilBackFail = 0; + stateBits1.loadBits.structured.stencilBackZFail = 0; + stateBits1.loadBits.structured.stencilBackFunc = 0; + + auto& stateBits2 = material->stateBitsTable[2]; + stateBits2.loadBits.structured.srcBlendRgb = GFXS_BLEND_ONE; + stateBits2.loadBits.structured.dstBlendRgb = GFXS_BLEND_ZERO; + stateBits2.loadBits.structured.blendOpRgb = GFXS_BLENDOP_DISABLED; + stateBits2.loadBits.structured.alphaTestDisabled = 1; + stateBits2.loadBits.structured.alphaTest = 0; + stateBits2.loadBits.structured.cullFace = GFXS_CULL_BACK; + stateBits2.loadBits.structured.srcBlendAlpha = GFXS_BLEND_ONE; + stateBits2.loadBits.structured.dstBlendAlpha = GFXS_BLEND_ZERO; + stateBits2.loadBits.structured.blendOpAlpha = GFXS_BLENDOP_DISABLED; + stateBits2.loadBits.structured.colorWriteRgb = 1; + stateBits2.loadBits.structured.colorWriteAlpha = 1; + stateBits2.loadBits.structured.polymodeLine = 0; + stateBits2.loadBits.structured.depthWrite = 1; + stateBits2.loadBits.structured.depthTestDisabled = 0; + stateBits2.loadBits.structured.depthTest = GFXS_DEPTHTEST_LESSEQUAL; + stateBits2.loadBits.structured.polygonOffset = GFXS_POLYGON_OFFSET_0; + stateBits2.loadBits.structured.stencilFrontEnabled = 0; + stateBits2.loadBits.structured.stencilFrontPass = 0; + stateBits2.loadBits.structured.stencilFrontFail = 0; + stateBits2.loadBits.structured.stencilFrontZFail = 0; + stateBits2.loadBits.structured.stencilFrontFunc = 0; + stateBits2.loadBits.structured.stencilBackEnabled = 0; + stateBits2.loadBits.structured.stencilBackPass = 0; + stateBits2.loadBits.structured.stencilBackFail = 0; + stateBits2.loadBits.structured.stencilBackZFail = 0; + stateBits2.loadBits.structured.stencilBackFunc = 0; + + auto& stateBits3 = material->stateBitsTable[3]; + stateBits3.loadBits.structured.srcBlendRgb = GFXS_BLEND_ONE; + stateBits3.loadBits.structured.dstBlendRgb = GFXS_BLEND_ZERO; + stateBits3.loadBits.structured.blendOpRgb = GFXS_BLENDOP_DISABLED; + stateBits3.loadBits.structured.alphaTestDisabled = 1; + stateBits3.loadBits.structured.alphaTest = 0; + stateBits3.loadBits.structured.cullFace = GFXS_CULL_BACK; + stateBits3.loadBits.structured.srcBlendAlpha = GFXS_BLEND_ONE; + stateBits3.loadBits.structured.dstBlendAlpha = GFXS_BLEND_ZERO; + stateBits3.loadBits.structured.blendOpAlpha = GFXS_BLENDOP_DISABLED; + stateBits3.loadBits.structured.colorWriteRgb = 1; + stateBits3.loadBits.structured.colorWriteAlpha = 0; + stateBits3.loadBits.structured.polymodeLine = 1; + stateBits3.loadBits.structured.depthWrite = 0; + stateBits3.loadBits.structured.depthTestDisabled = 0; + stateBits3.loadBits.structured.depthTest = GFXS_DEPTHTEST_LESSEQUAL; + stateBits3.loadBits.structured.polygonOffset = GFXS_POLYGON_OFFSET_2; + stateBits3.loadBits.structured.stencilFrontEnabled = 0; + stateBits3.loadBits.structured.stencilFrontPass = 0; + stateBits3.loadBits.structured.stencilFrontFail = 0; + stateBits3.loadBits.structured.stencilFrontZFail = 0; + stateBits3.loadBits.structured.stencilFrontFunc = 0; + stateBits3.loadBits.structured.stencilBackEnabled = 0; + stateBits3.loadBits.structured.stencilBackPass = 0; + stateBits3.loadBits.structured.stencilBackFail = 0; + stateBits3.loadBits.structured.stencilBackZFail = 0; + stateBits3.loadBits.structured.stencilBackFunc = 0; + + auto& stateBits4 = material->stateBitsTable[4]; + stateBits4.loadBits.structured.srcBlendRgb = GFXS_BLEND_ONE; + stateBits4.loadBits.structured.dstBlendRgb = GFXS_BLEND_ONE; + stateBits4.loadBits.structured.blendOpRgb = GFXS_BLENDOP_ADD; + stateBits4.loadBits.structured.alphaTestDisabled = 1; + stateBits4.loadBits.structured.alphaTest = 0; + stateBits4.loadBits.structured.cullFace = GFXS_CULL_BACK; + stateBits4.loadBits.structured.srcBlendAlpha = GFXS_BLEND_ONE; + stateBits4.loadBits.structured.dstBlendAlpha = GFXS_BLEND_ZERO; + stateBits4.loadBits.structured.blendOpAlpha = GFXS_BLENDOP_DISABLED; + stateBits4.loadBits.structured.colorWriteRgb = 1; + stateBits4.loadBits.structured.colorWriteAlpha = 1; + stateBits4.loadBits.structured.polymodeLine = 0; + stateBits4.loadBits.structured.depthWrite = 1; + stateBits4.loadBits.structured.depthTestDisabled = 0; + stateBits4.loadBits.structured.depthTest = GFXS_DEPTHTEST_LESSEQUAL; + stateBits4.loadBits.structured.polygonOffset = GFXS_POLYGON_OFFSET_1; + stateBits4.loadBits.structured.stencilFrontEnabled = 0; + stateBits4.loadBits.structured.stencilFrontPass = 0; + stateBits4.loadBits.structured.stencilFrontFail = 0; + stateBits4.loadBits.structured.stencilFrontZFail = 0; + stateBits4.loadBits.structured.stencilFrontFunc = 0; + stateBits4.loadBits.structured.stencilBackEnabled = 0; + stateBits4.loadBits.structured.stencilBackPass = 0; + stateBits4.loadBits.structured.stencilBackFail = 0; + stateBits4.loadBits.structured.stencilBackZFail = 0; + stateBits4.loadBits.structured.stencilBackFunc = 0; + + material->thermalMaterial = nullptr; + + pool.AddAsset(std::make_unique>(ASSET_TYPE_MATERIAL, name, material)); + } + + TEST_CASE("DumperMaterial(T6): Can dump material", "[t6][material][assetdumper]") + { + std::string expected(R"MATERIAL( +{ + "_game": "t6", + "_type": "material", + "_version": 1, + "cameraRegion": "litOpaque", + "constants": [ + { + "literal": [ + 1.0, + 1.0, + 1.0, + 1.0 + ], + "name": "occlusionAmount" + } + ], + "contents": 1, + "gameFlags": [ + "2", + "10", + "CASTS_SHADOW" + ], + "hashIndex": 0, + "layeredSurfaceTypes": 536870925, + "probeMipBits": 0, + "sortKey": 4, + "stateBits": [ + { + "alphaTest": "disabled", + "blendOpAlpha": "disabled", + "blendOpRgb": "disabled", + "colorWriteAlpha": false, + "colorWriteRgb": false, + "cullFace": "back", + "depthTest": "less_equal", + "depthWrite": true, + "dstBlendAlpha": "zero", + "dstBlendRgb": "zero", + "polygonOffset": "offset0", + "polymodeLine": false, + "srcBlendAlpha": "one", + "srcBlendRgb": "one", + "stencilFront": { + "fail": "keep", + "func": "equal", + "pass": "keep", + "zfail": "keep" + } + }, + { + "alphaTest": "disabled", + "blendOpAlpha": "disabled", + "blendOpRgb": "disabled", + "colorWriteAlpha": false, + "colorWriteRgb": false, + "cullFace": "back", + "depthTest": "less_equal", + "depthWrite": true, + "dstBlendAlpha": "zero", + "dstBlendRgb": "zero", + "polygonOffset": "offsetShadowmap", + "polymodeLine": false, + "srcBlendAlpha": "one", + "srcBlendRgb": "one" + }, + { + "alphaTest": "disabled", + "blendOpAlpha": "disabled", + "blendOpRgb": "disabled", + "colorWriteAlpha": true, + "colorWriteRgb": true, + "cullFace": "back", + "depthTest": "less_equal", + "depthWrite": true, + "dstBlendAlpha": "zero", + "dstBlendRgb": "zero", + "polygonOffset": "offset0", + "polymodeLine": false, + "srcBlendAlpha": "one", + "srcBlendRgb": "one" + }, + { + "alphaTest": "disabled", + "blendOpAlpha": "disabled", + "blendOpRgb": "disabled", + "colorWriteAlpha": false, + "colorWriteRgb": true, + "cullFace": "back", + "depthTest": "less_equal", + "depthWrite": false, + "dstBlendAlpha": "zero", + "dstBlendRgb": "zero", + "polygonOffset": "offset2", + "polymodeLine": true, + "srcBlendAlpha": "one", + "srcBlendRgb": "one" + }, + { + "alphaTest": "disabled", + "blendOpAlpha": "disabled", + "blendOpRgb": "add", + "colorWriteAlpha": true, + "colorWriteRgb": true, + "cullFace": "back", + "depthTest": "less_equal", + "depthWrite": true, + "dstBlendAlpha": "zero", + "dstBlendRgb": "one", + "polygonOffset": "offset1", + "polymodeLine": false, + "srcBlendAlpha": "one", + "srcBlendRgb": "one" + } + ], + "stateBitsEntry": [ + 0, + 1, + 2, + -1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + -1, + -1, + -1, + -1, + -1, + -1, + 3, + -1, + 2, + 4 + ], + "stateFlags": 121, + "surfaceFlags": 13631488, + "surfaceTypeBits": 4096, + "techniqueSet": "wpc_lit_sm_r0c0n0s0_1zzj1138", + "textureAtlas": { + "columns": 1, + "rows": 1 + }, + "textures": [ + { + "image": "~~-gmetal_ac_duct_s-rgb&~-rme~0a5a2e6f", + "isMatureContent": false, + "name": "specularMap", + "samplerState": { + "clampU": false, + "clampV": false, + "clampW": false, + "filter": "aniso4x", + "mipMap": "linear" + }, + "semantic": "specularMap" + }, + { + "image": "metal_ac_duct_n", + "isMatureContent": false, + "name": "normalMap", + "samplerState": { + "clampU": false, + "clampV": false, + "clampW": false, + "filter": "aniso4x", + "mipMap": "linear" + }, + "semantic": "normalMap" + }, + { + "image": "~-gmetal_ac_duct_c", + "isMatureContent": false, + "name": "colorMap", + "samplerState": { + "clampU": false, + "clampV": false, + "clampW": false, + "filter": "aniso4x", + "mipMap": "linear" + }, + "semantic": "colorMap" + } + ] +})MATERIAL"); + + Zone zone("MockZone", 0, IGame::GetGameById(GameId::T6)); + + MemoryManager memory; + MockSearchPath mockObjPath; + MockOutputPath mockOutput; + AssetDumpingContext context(zone, "", mockOutput, mockObjPath); + + AssetPoolDynamic materialPool(0); + + GivenMaterial("wpc/metal_ac_duct", materialPool, memory); + + AssetDumperMaterial dumper; + dumper.DumpPool(context, &materialPool); + + const auto* file = mockOutput.GetMockedFile("materials/wpc/metal_ac_duct.json"); + REQUIRE(file); + REQUIRE(JsonNormalized(file->AsString()) == JsonNormalized(expected)); + } +} // namespace diff --git a/tools/scripts/options.lua b/tools/scripts/options.lua index a07c7392..28ed3633 100644 --- a/tools/scripts/options.lua +++ b/tools/scripts/options.lua @@ -5,4 +5,8 @@ newoption { newoption { trigger = "debug-techset", description = "Activate additional debugging logic for Techset assets" -} \ No newline at end of file +} +newoption { + trigger = "experimental-material-compilation", + description = "Activate experimental material compilation support" +}