From f8286fd35802a7928beebe5caa4e32fe74a721d3 Mon Sep 17 00:00:00 2001 From: Jan Date: Sun, 29 Jun 2025 19:12:08 +0100 Subject: [PATCH 01/13] fix: iw4 material constant zone state using iw5 game --- .../Game/IW4/Material/MaterialConstantZoneStateIW4.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/ObjWriting/Game/IW4/Material/MaterialConstantZoneStateIW4.cpp b/src/ObjWriting/Game/IW4/Material/MaterialConstantZoneStateIW4.cpp index 70593610..a46c30a3 100644 --- a/src/ObjWriting/Game/IW4/Material/MaterialConstantZoneStateIW4.cpp +++ b/src/ObjWriting/Game/IW4/Material/MaterialConstantZoneStateIW4.cpp @@ -199,20 +199,20 @@ namespace IW4 void MaterialConstantZoneState::ExtractNamesFromZoneInternal() { - for (const auto* zone : IGame::GetGameById(GameId::IW5)->GetZones()) + for (const auto* zone : IGame::GetGameById(GameId::IW4)->GetZones()) { - const auto* iw5AssetPools = dynamic_cast(zone->m_pools.get()); - if (!iw5AssetPools) + const auto* assetPools = dynamic_cast(zone->m_pools.get()); + if (!assetPools) return; - for (const auto* vertexShaderAsset : *iw5AssetPools->m_material_vertex_shader) + for (const auto* vertexShaderAsset : *assetPools->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) + for (const auto* pixelShaderAsset : *assetPools->m_material_pixel_shader) { const auto* pixelShader = pixelShaderAsset->Asset(); if (ShouldDumpFromStruct(pixelShader)) From 40427cf34f97cb43ff7d0235d42f68eac05e2546 Mon Sep 17 00:00:00 2001 From: Jan Date: Sun, 29 Jun 2025 23:05:19 +0100 Subject: [PATCH 02/13] fix: using wrong enum for mipmaps in iw4,iw5 material tests --- .../Game/IW4/Material/DumperMaterialIW4Test.cpp | 6 +++--- .../Game/IW5/Material/DumperMaterialIW5Test.cpp | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/test/ObjWritingTests/Game/IW4/Material/DumperMaterialIW4Test.cpp b/test/ObjWritingTests/Game/IW4/Material/DumperMaterialIW4Test.cpp index 2d33bd2d..7f44b08e 100644 --- a/test/ObjWritingTests/Game/IW4/Material/DumperMaterialIW4Test.cpp +++ b/test/ObjWritingTests/Game/IW4/Material/DumperMaterialIW4Test.cpp @@ -62,7 +62,7 @@ namespace textureDef0.nameStart = 's'; textureDef0.nameEnd = 'p'; textureDef0.samplerState.filter = TEXTURE_FILTER_ANISO2X; - textureDef0.samplerState.mipMap = TEXTURE_FILTER_NEAREST; + textureDef0.samplerState.mipMap = SAMPLER_MIPMAP_ENUM_NEAREST; textureDef0.samplerState.clampU = 0; textureDef0.samplerState.clampV = 0; textureDef0.samplerState.clampW = 0; @@ -74,7 +74,7 @@ namespace textureDef1.nameStart = 'n'; textureDef1.nameEnd = 'p'; textureDef1.samplerState.filter = TEXTURE_FILTER_ANISO2X; - textureDef1.samplerState.mipMap = TEXTURE_FILTER_NEAREST; + textureDef1.samplerState.mipMap = SAMPLER_MIPMAP_ENUM_NEAREST; textureDef1.samplerState.clampU = 0; textureDef1.samplerState.clampV = 0; textureDef1.samplerState.clampW = 0; @@ -86,7 +86,7 @@ namespace textureDef2.nameStart = 'c'; textureDef2.nameEnd = 'p'; textureDef2.samplerState.filter = TEXTURE_FILTER_ANISO2X; - textureDef2.samplerState.mipMap = TEXTURE_FILTER_NEAREST; + textureDef2.samplerState.mipMap = SAMPLER_MIPMAP_ENUM_NEAREST; textureDef2.samplerState.clampU = 0; textureDef2.samplerState.clampV = 0; textureDef2.samplerState.clampW = 0; diff --git a/test/ObjWritingTests/Game/IW5/Material/DumperMaterialIW5Test.cpp b/test/ObjWritingTests/Game/IW5/Material/DumperMaterialIW5Test.cpp index cf545055..71b289d3 100644 --- a/test/ObjWritingTests/Game/IW5/Material/DumperMaterialIW5Test.cpp +++ b/test/ObjWritingTests/Game/IW5/Material/DumperMaterialIW5Test.cpp @@ -62,7 +62,7 @@ namespace textureDef0.nameStart = 's'; textureDef0.nameEnd = 'p'; textureDef0.samplerState.filter = TEXTURE_FILTER_ANISO2X; - textureDef0.samplerState.mipMap = TEXTURE_FILTER_NEAREST; + textureDef0.samplerState.mipMap = SAMPLER_MIPMAP_ENUM_NEAREST; textureDef0.samplerState.clampU = 0; textureDef0.samplerState.clampV = 0; textureDef0.samplerState.clampW = 0; @@ -74,7 +74,7 @@ namespace textureDef1.nameStart = 'n'; textureDef1.nameEnd = 'p'; textureDef1.samplerState.filter = TEXTURE_FILTER_ANISO2X; - textureDef1.samplerState.mipMap = TEXTURE_FILTER_LINEAR; + textureDef1.samplerState.mipMap = SAMPLER_MIPMAP_ENUM_LINEAR; textureDef1.samplerState.clampU = 0; textureDef1.samplerState.clampV = 0; textureDef1.samplerState.clampW = 0; @@ -86,7 +86,7 @@ namespace textureDef2.nameStart = 'c'; textureDef2.nameEnd = 'p'; textureDef2.samplerState.filter = TEXTURE_FILTER_ANISO2X; - textureDef2.samplerState.mipMap = TEXTURE_FILTER_LINEAR; + textureDef2.samplerState.mipMap = SAMPLER_MIPMAP_ENUM_LINEAR; textureDef2.samplerState.clampU = 0; textureDef2.samplerState.clampV = 0; textureDef2.samplerState.clampW = 0; From 65c8cd619868013d5b50fb16774cc6e16c49bcd9 Mon Sep 17 00:00:00 2001 From: Jan Date: Sun, 29 Jun 2025 23:05:36 +0100 Subject: [PATCH 03/13] feat: add json material loading/writing for iw3 --- src/Common/Game/IW3/CommonIW3.h | 5 + src/Common/Game/IW3/IW3_Assets.h | 150 ++++- .../Material/JsonMaterial.h.template | 99 ++- .../Game/IW3/Material/LoaderMaterialIW3.cpp | 54 ++ .../Game/IW3/Material/LoaderMaterialIW3.h | 12 + src/ObjLoading/Game/IW3/ObjLoaderIW3.cpp | 3 +- .../Material/JsonMaterialLoader.cpp.template | 12 +- .../Material/JsonMaterialLoader.h.template | 2 +- .../IW3/AssetDumpers/AssetDumperMaterial.cpp | 467 -------------- .../Game/IW3/Material/DumperMaterialIW3.cpp | 30 + .../DumperMaterialIW3.h} | 3 + .../Material/MaterialConstantZoneStateIW3.cpp | 250 ++++++++ .../Material/MaterialConstantZoneStateIW3.h | 18 + src/ObjWriting/Game/IW3/ObjWriterIW3.cpp | 2 +- src/ObjWriting/Game/IW3/ObjWriterIW3.h | 1 + .../Material/JsonMaterialWriter.cpp.template | 12 +- .../Material/JsonMaterialWriter.h.template | 2 +- src/ZoneCommon/Game/IW3/GameAssetPoolIW3.h | 3 - .../IW3/Material/LoaderMaterialIW3Test.cpp | 577 ++++++++++++++++++ .../IW3/Material/DumperMaterialIW3Test.cpp | 571 +++++++++++++++++ 20 files changed, 1727 insertions(+), 546 deletions(-) create mode 100644 src/ObjLoading/Game/IW3/Material/LoaderMaterialIW3.cpp create mode 100644 src/ObjLoading/Game/IW3/Material/LoaderMaterialIW3.h delete mode 100644 src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperMaterial.cpp create mode 100644 src/ObjWriting/Game/IW3/Material/DumperMaterialIW3.cpp rename src/ObjWriting/Game/IW3/{AssetDumpers/AssetDumperMaterial.h => Material/DumperMaterialIW3.h} (78%) create mode 100644 src/ObjWriting/Game/IW3/Material/MaterialConstantZoneStateIW3.cpp create mode 100644 src/ObjWriting/Game/IW3/Material/MaterialConstantZoneStateIW3.h create mode 100644 test/ObjLoadingTests/Game/IW3/Material/LoaderMaterialIW3Test.cpp create mode 100644 test/ObjWritingTests/Game/IW3/Material/DumperMaterialIW3Test.cpp diff --git a/src/Common/Game/IW3/CommonIW3.h b/src/Common/Game/IW3/CommonIW3.h index a01a6feb..43664b62 100644 --- a/src/Common/Game/IW3/CommonIW3.h +++ b/src/Common/Game/IW3/CommonIW3.h @@ -21,6 +21,11 @@ namespace IW3 return result; } + static constexpr uint32_t R_HashString(const char* string) + { + return R_HashString(string, 0u); + } + static PackedTexCoords Vec2PackTexCoords(const float (&in)[2]); static PackedUnitVec Vec3PackUnitVec(const float (&in)[3]); static GfxColor Vec4PackGfxColor(const float (&in)[4]); diff --git a/src/Common/Game/IW3/IW3_Assets.h b/src/Common/Game/IW3/IW3_Assets.h index 4e27aa9a..4a645889 100644 --- a/src/Common/Game/IW3/IW3_Assets.h +++ b/src/Common/Game/IW3/IW3_Assets.h @@ -566,7 +566,8 @@ namespace IW3 GFXS_BLEND_INVDESTALPHA = 0x8, GFXS_BLEND_DESTCOLOR = 0x9, GFXS_BLEND_INVDESTCOLOR = 0xA, - GFXS_BLEND_MASK = 0xF, + + GFXS_BLEND_COUNT }; enum GfxBlendOp @@ -577,7 +578,40 @@ namespace IW3 GFXS_BLENDOP_REVSUBTRACT = 0x3, GFXS_BLENDOP_MIN = 0x4, GFXS_BLENDOP_MAX = 0x5, - GFXS_BLENDOP_MASK = 0x7, + + GFXS_BLENDOP_COUNT + }; + + enum GfxAlphaTest_e + { + GFXS_ALPHA_TEST_GT_0 = 1, + GFXS_ALPHA_TEST_LT_128 = 2, + GFXS_ALPHA_TEST_GE_128 = 3, + + 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 @@ -589,10 +623,19 @@ namespace IW3 GFXS_STENCILOP_DECRSAT = 0x4, GFXS_STENCILOP_INVERT = 0x5, GFXS_STENCILOP_INCR = 0x6, - GFXS_STENCILOP_DECR = 0x7, + GFXS_STENCILOP_DECR = 0x7 + }; - GFXS_STENCILOP_COUNT, - GFXS_STENCILOP_MASK = 0x7 + enum GfxStencilFunc + { + 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 }; enum GfxStateBitsEnum : unsigned int @@ -613,10 +656,10 @@ namespace IW3 GFXS0_ATEST_GE_128 = 0x3000, GFXS0_ATEST_MASK = 0x3000, - GFXS0_CULL_SHIFT = 0xE, GFXS0_CULL_NONE = 0x4000, GFXS0_CULL_BACK = 0x8000, GFXS0_CULL_FRONT = 0xC000, + GFXS0_CULL_SHIFT = 0xE, GFXS0_CULL_MASK = 0xC000, GFXS0_SRCBLEND_ALPHA_SHIFT = 0x10, @@ -638,18 +681,18 @@ namespace IW3 GFXS1_DEPTHWRITE = 0x1, GFXS1_DEPTHTEST_DISABLE = 0x2, - GFXS1_DEPTHTEST_SHIFT = 0x2, GFXS1_DEPTHTEST_ALWAYS = 0x0, GFXS1_DEPTHTEST_LESS = 0x4, GFXS1_DEPTHTEST_EQUAL = 0x8, GFXS1_DEPTHTEST_LESSEQUAL = 0xC, + GFXS1_DEPTHTEST_SHIFT = 0x2, GFXS1_DEPTHTEST_MASK = 0xC, - GFXS1_POLYGON_OFFSET_SHIFT = 0x4, GFXS1_POLYGON_OFFSET_0 = 0x0, GFXS1_POLYGON_OFFSET_1 = 0x10, GFXS1_POLYGON_OFFSET_2 = 0x20, GFXS1_POLYGON_OFFSET_SHADOWMAP = 0x30, + GFXS1_POLYGON_OFFSET_SHIFT = 0x4, GFXS1_POLYGON_OFFSET_MASK = 0x30, GFXS1_STENCIL_FRONT_ENABLE = 0x40, @@ -672,16 +715,61 @@ namespace IW3 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 unused1 : 2; // 29-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 type_align(16) MaterialConstantDef { unsigned int nameHash; char name[12]; - float literal[4]; + vec4_t literal; }; struct complex_s @@ -718,6 +806,26 @@ namespace IW3 water_t* water; }; + enum TextureFilter + { + TEXTURE_FILTER_DISABLED = 0x0, + TEXTURE_FILTER_NEAREST = 0x1, + TEXTURE_FILTER_LINEAR = 0x2, + TEXTURE_FILTER_ANISO2X = 0x3, + TEXTURE_FILTER_ANISO4X = 0x4, + + 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, @@ -743,13 +851,26 @@ namespace IW3 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; }; @@ -842,8 +963,9 @@ namespace IW3 CAMERA_REGION_LIT = 0x0, CAMERA_REGION_DECAL = 0x1, CAMERA_REGION_EMISSIVE = 0x2, - CAMERA_REGION_COUNT = 0x3, - CAMERA_REGION_NONE = 0x3, + + CAMERA_REGION_COUNT, + CAMERA_REGION_NONE = CAMERA_REGION_COUNT, }; enum MaterialStateFlags diff --git a/src/ObjCommon/Material/JsonMaterial.h.template b/src/ObjCommon/Material/JsonMaterial.h.template index 7c7f0663..426bf449 100644 --- a/src/ObjCommon/Material/JsonMaterial.h.template +++ b/src/ObjCommon/Material/JsonMaterial.h.template @@ -1,8 +1,11 @@ -#options GAME (IW4, IW5, T6) +#options GAME (IW3, IW4, IW5, T6) #filename "Game/" + GAME + "/Material/JsonMaterial" + GAME + ".h" -#if GAME == "IW4" +#if GAME == "IW3" +#define FEATURE_IW3 +#define HAS_WATER +#elif GAME == "IW4" #define FEATURE_IW4 #define HAS_WATER #elif GAME == "IW5" @@ -75,7 +78,7 @@ namespace GAME INVALID, DISABLED, GT0, -#if defined(FEATURE_IW4) || defined(FEATURE_IW5) +#if defined(FEATURE_IW3) || defined(FEATURE_IW4) || defined(FEATURE_IW5) LT128, #endif GE128 @@ -85,7 +88,7 @@ namespace GAME {JsonAlphaTest::INVALID, nullptr }, {JsonAlphaTest::DISABLED, "disabled"}, {JsonAlphaTest::GT0, "gt0" }, -#if defined(FEATURE_IW4) || defined(FEATURE_IW5) +#if defined(FEATURE_IW3) || defined(FEATURE_IW4) || defined(FEATURE_IW5) {JsonAlphaTest::LT128, "lt128" }, #endif {JsonAlphaTest::GE128, "ge128" } @@ -315,52 +318,46 @@ namespace GAME #endif NLOHMANN_JSON_SERIALIZE_ENUM(TextureSemantic, { -#if defined(FEATURE_IW4) || defined(FEATURE_IW5) - {TS_2D, "2D" }, - {TS_FUNCTION, "function" }, - {TS_COLOR_MAP, "colorMap" }, + {TS_2D, "2D" }, + {TS_FUNCTION, "function" }, + {TS_COLOR_MAP, "colorMap" }, +#if defined(FEATURE_IW3) || defined(FEATURE_T6) + {TS_UNUSED_1, "unused1" }, +#else {TS_DETAIL_MAP, "detailMap" }, +#endif {TS_UNUSED_2, "unused2" }, {TS_NORMAL_MAP, "normalMap" }, {TS_UNUSED_3, "unused3" }, {TS_UNUSED_4, "unused4" }, {TS_SPECULAR_MAP, "specularMap" }, {TS_UNUSED_5, "unused5" }, +#if defined(FEATURE_IW3) || defined(FEATURE_IW4) || defined(FEATURE_IW5) {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" }, + {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 }); @@ -437,26 +434,25 @@ namespace GAME ); 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" }, +#ifdef FEATURE_T6 {MTL_GAMEFLAG_NO_MARKS, "NO_MARKS" }, {MTL_GAMEFLAG_NO_MARKS, "4" }, +#else + {MTL_GAMEFLAG_4, "4" }, +#endif {MTL_GAMEFLAG_8, "8" }, {MTL_GAMEFLAG_10, "10" }, {MTL_GAMEFLAG_20, "20" }, +#if defined(FEATURE_IW3) || defined(FEATURE_T6) {MTL_GAMEFLAG_CASTS_SHADOW, "CASTS_SHADOW"}, {MTL_GAMEFLAG_CASTS_SHADOW, "40" }, +#else + {MTL_GAMEFLAG_40, "40" }, +#endif {MTL_GAMEFLAG_80, "80" }, +#ifdef FEATURE_T6 {MTL_GAMEFLAG_100, "100" }, {MTL_GAMEFLAG_200, "200" }, {MTL_GAMEFLAG_400, "400" }, @@ -466,7 +462,11 @@ namespace GAME }); NLOHMANN_JSON_SERIALIZE_ENUM(GfxCameraRegionType, { -#if defined(FEATURE_IW4) || defined(FEATURE_IW5) +#if defined(FEATURE_IW3) + {CAMERA_REGION_LIT, "lit" }, + {CAMERA_REGION_DECAL, "decal" }, + {CAMERA_REGION_EMISSIVE, "emissive" }, +#elif defined(FEATURE_IW4) || defined(FEATURE_IW5) {CAMERA_REGION_LIT_OPAQUE, "litOpaque" }, {CAMERA_REGION_LIT_TRANS, "litTrans" }, {CAMERA_REGION_EMISSIVE, "emissive" }, @@ -474,7 +474,6 @@ namespace GAME #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" }, @@ -486,8 +485,8 @@ namespace GAME {CAMERA_REGION_DEPTH_HACK, "depthHack" }, {CAMERA_REGION_UNUSED, "unused" }, {CAMERA_REGION_SONAR, "sonar" }, - {CAMERA_REGION_NONE, "none" }, #endif + {CAMERA_REGION_NONE, "none" }, }); class JsonMaterial diff --git a/src/ObjLoading/Game/IW3/Material/LoaderMaterialIW3.cpp b/src/ObjLoading/Game/IW3/Material/LoaderMaterialIW3.cpp new file mode 100644 index 00000000..aec04c81 --- /dev/null +++ b/src/ObjLoading/Game/IW3/Material/LoaderMaterialIW3.cpp @@ -0,0 +1,54 @@ +#include "LoaderMaterialIW3.h" + +#include "Game/IW3/IW3.h" +#include "Game/IW3/Material/JsonMaterialLoaderIW3.h" +#include "Material/MaterialCommon.h" + +#include +#include + +using namespace IW3; + +namespace +{ + class MaterialLoader final : public AssetCreator + { + public: + MaterialLoader(MemoryManager& memory, ISearchPath& searchPath) + : m_memory(memory), + m_search_path(searchPath) + { + } + + AssetCreationResult CreateAsset(const std::string& assetName, AssetCreationContext& context) override + { + 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); + if (!LoadMaterialAsJson(*file.m_stream, *material, m_memory, context, registration)) + { + std::cerr << std::format("Failed to load material \"{}\"\n", assetName); + return AssetCreationResult::Failure(); + } + + return AssetCreationResult::Success(context.AddAsset(std::move(registration))); + } + + private: + MemoryManager& m_memory; + ISearchPath& m_search_path; + }; +} // namespace + +namespace IW3 +{ + std::unique_ptr> CreateMaterialLoader(MemoryManager& memory, ISearchPath& searchPath) + { + return std::make_unique(memory, searchPath); + } +} // namespace IW3 diff --git a/src/ObjLoading/Game/IW3/Material/LoaderMaterialIW3.h b/src/ObjLoading/Game/IW3/Material/LoaderMaterialIW3.h new file mode 100644 index 00000000..8d2dd024 --- /dev/null +++ b/src/ObjLoading/Game/IW3/Material/LoaderMaterialIW3.h @@ -0,0 +1,12 @@ +#pragma once + +#include "Asset/IAssetCreator.h" +#include "Game/IW3/IW3.h" +#include "Gdt/IGdtQueryable.h" +#include "SearchPath/ISearchPath.h" +#include "Utils/MemoryManager.h" + +namespace IW3 +{ + std::unique_ptr> CreateMaterialLoader(MemoryManager& memory, ISearchPath& searchPath); +} // namespace IW3 diff --git a/src/ObjLoading/Game/IW3/ObjLoaderIW3.cpp b/src/ObjLoading/Game/IW3/ObjLoaderIW3.cpp index eeb2d3a8..9c2ef22f 100644 --- a/src/ObjLoading/Game/IW3/ObjLoaderIW3.cpp +++ b/src/ObjLoading/Game/IW3/ObjLoaderIW3.cpp @@ -5,6 +5,7 @@ #include "Game/IW3/IW3.h" #include "Image/AssetLoaderImageIW3.h" #include "Localize/AssetLoaderLocalizeIW3.h" +#include "Material/LoaderMaterialIW3.h" #include "ObjLoading.h" #include "RawFile/AssetLoaderRawFileIW3.h" #include "StringTable/AssetLoaderStringTableIW3.h" @@ -90,7 +91,7 @@ namespace // collection.AddAssetCreator(std::make_unique(memory)); // collection.AddAssetCreator(std::make_unique(memory)); // collection.AddAssetCreator(std::make_unique(memory)); - // collection.AddAssetCreator(std::make_unique(memory)); + collection.AddAssetCreator(CreateMaterialLoader(memory, searchPath)); // collection.AddAssetCreator(std::make_unique(memory)); collection.AddAssetCreator(CreateImageLoader(memory, searchPath)); // collection.AddAssetCreator(std::make_unique(memory)); diff --git a/src/ObjLoading/Material/JsonMaterialLoader.cpp.template b/src/ObjLoading/Material/JsonMaterialLoader.cpp.template index 65687955..8db6cd37 100644 --- a/src/ObjLoading/Material/JsonMaterialLoader.cpp.template +++ b/src/ObjLoading/Material/JsonMaterialLoader.cpp.template @@ -1,8 +1,12 @@ -#options GAME (IW4, IW5, T6) +#options GAME (IW3, IW4, IW5, T6) #filename "Game/" + GAME + "/Material/JsonMaterialLoader" + GAME + ".cpp" -#if GAME == "IW4" +#if GAME == "IW3" +#define FEATURE_IW3 +#define HAS_WATER +#define GAME_LOWER "iw3" +#elif GAME == "IW4" #define FEATURE_IW4 #define HAS_WATER #define GAME_LOWER "iw4" @@ -94,7 +98,7 @@ namespace std::cerr << std::format("Cannot load material \"{}\": {}\n", material.info.name, message); } -#if defined(FEATURE_IW4) || defined(FEATURE_IW5) +#if defined(FEATURE_IW3) || 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) @@ -304,7 +308,7 @@ namespace structured.alphaTestDisabled = 0; structured.alphaTest = GFXS_ALPHA_TEST_GT_0; } -#if defined(FEATURE_IW4) || defined(FEATURE_IW5) +#if defined(FEATURE_IW3) || defined(FEATURE_IW4) || defined(FEATURE_IW5) else if (jStateBitsTableEntry.alphaTest == JsonAlphaTest::LT128) { structured.alphaTestDisabled = 0; diff --git a/src/ObjLoading/Material/JsonMaterialLoader.h.template b/src/ObjLoading/Material/JsonMaterialLoader.h.template index ae57b3b6..49b8057d 100644 --- a/src/ObjLoading/Material/JsonMaterialLoader.h.template +++ b/src/ObjLoading/Material/JsonMaterialLoader.h.template @@ -1,4 +1,4 @@ -#options GAME (IW4, IW5, T6) +#options GAME (IW3, IW4, IW5, T6) #filename "Game/" + GAME + "/Material/JsonMaterialLoader" + GAME + ".h" diff --git a/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperMaterial.cpp b/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperMaterial.cpp deleted file mode 100644 index 0f90a158..00000000 --- a/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperMaterial.cpp +++ /dev/null @@ -1,467 +0,0 @@ -#include "AssetDumperMaterial.h" - -#include "Game/IW3/MaterialConstantsIW3.h" -#include "Game/IW3/TechsetConstantsIW3.h" - -#include -#include -#include - -// #define FLAGS_DEBUG 1 - -using namespace IW3; -using json = nlohmann::json; - -namespace IW3 -{ - const char* AssetName(const char* name) - { - if (name && name[0] == ',') - return &name[1]; - 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(const MaterialTextureDef* textureTable, const size_t count) - { - static const char* semanticNames[]{ - "2d", - "function", - "colorMap", - "unused1", - "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 = knownMaterialSourceNames.find(entry.nameHash); - if (knownMaterialSourceName != knownMaterialSourceNames.end()) - { - jEntry["name"] = knownMaterialSourceName->second; - } - 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 = knownMaterialSourceNames.find(entry.nameHash); - if (knownMaterialSourceName != knownMaterialSourceNames.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", - }; - - 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_GT_0) - alphaTest = "gt0"; - else if (entry.loadBits[0] & GFXS0_ATEST_LT_128) - alphaTest = "lt128"; - else if (entry.loadBits[0] & 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}, - {"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(stencilOpNames, (entry.loadBits[1] >> GFXS1_STENCIL_FRONT_FUNC_SHIFT) & GFXS_STENCILOP_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(stencilOpNames, (entry.loadBits[1] >> GFXS1_STENCIL_BACK_FUNC_SHIFT) & GFXS_STENCILOP_MASK)}, - }); - } - } - - return jArray; - } - - json BuildSurfaceTypeBitsJson(const unsigned surfaceTypeBits) - { - if (!surfaceTypeBits) - return json(surfaceTypeNames[SURF_TYPE_DEFAULT]); - - static constexpr auto NON_SURFACE_TYPE_BITS = - ~(std::numeric_limits::max() >> ((sizeof(unsigned) * 8) - (static_cast(SURF_TYPE_NUM) - 1))); - assert((surfaceTypeBits & NON_SURFACE_TYPE_BITS) == 0); - - std::ostringstream ss; - auto firstSurfaceType = true; - for (auto surfaceTypeIndex = static_cast(SURF_TYPE_BARK); surfaceTypeIndex < SURF_TYPE_NUM; surfaceTypeIndex++) - { - if ((surfaceTypeBits & (1 << (surfaceTypeIndex - 1))) == 0) - continue; - - if (firstSurfaceType) - firstSurfaceType = false; - else - ss << ","; - ss << surfaceTypeNames[surfaceTypeIndex]; - } - - if (firstSurfaceType) - return json(surfaceTypeNames[SURF_TYPE_DEFAULT]); - - return json(ss.str()); - } - - 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); - } -} // namespace IW3 - -bool AssetDumperMaterial::ShouldDump(XAssetInfo* asset) -{ - return true; -} - -void AssetDumperMaterial::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) -{ - auto* material = asset->Asset(); - - std::ostringstream ss; - ss << "materials/" << asset->m_name << ".json"; - const auto assetFile = context.OpenAssetFile(ss.str()); - - if (!assetFile) - return; - - auto& stream = *assetFile; - - static const char* cameraRegionNames[]{"lit", "decal", "emissive", "none"}; - static std::unordered_map sortKeyNames{ - {0, "distortion" }, - {1, "opaque water" }, - {2, "boat hull" }, - {3, "opaque ambient" }, - {4, "opaque" }, - {5, "sky" }, - {6, "skybox - sun / moon" }, - {7, "skybox - clouds" }, - {8, "skybox - horizon" }, - {9, "decal - bottom 1" }, - {10, "decal - bottom 2" }, - {11, "decal - bottom 3" }, - {12, "decal - static decal" }, - {13, "decal - middle 1" }, - {14, "decal - middle 2" }, - {15, "decal - middle 3" }, - {24, "decal - weapon impact" }, - {29, "decal - top 1" }, - {30, "decal - top 2" }, - {31, "decal - top 3" }, - {32, "multiplicative" }, - {33, "banner / curtain" }, - {34, "hair" }, - {35, "underwater" }, - {36, "transparent water" }, - {37, "corona" }, - {38, "window inside" }, - {39, "window outside" }, - {40, "before effects - bottom"}, - {41, "before effects - middle"}, - {42, "before effects - top" }, - {43, "blend / additive" }, - {48, "effect - auto sort" }, - {56, "after effects - bottom" }, - {57, "after effects - middle" }, - {58, "after effects - top" }, - {59, "viewmodel effect" }, - }; - - const auto foundSortKeyName = sortKeyNames.find(material->info.sortKey); - assert(foundSortKeyName != sortKeyNames.end()); - - 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", foundSortKeyName != sortKeyNames.end() ? foundSortKeyName->second : std::to_string(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)}, - {"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.primaryLightIndex)}, - {"surfType", static_cast(material->info.drawSurf.fields.surfType)}, - {"primarySortKey", static_cast(material->info.drawSurf.fields.primarySortKey)}}}, - {"surfaceTypeBits", BuildSurfaceTypeBitsJson(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) : nullptr}, - {"textureTable", BuildTextureTableJson(material->textureTable, material->textureCount)}, - {"constantTable", BuildConstantTableJson(material->constantTable, material->constantCount)}, - {"stateBitsTable", BuildStateBitsTableJson(material->stateBitsTable, material->stateBitsCount)}, - }; - - stream << std::setw(4) << j; -} diff --git a/src/ObjWriting/Game/IW3/Material/DumperMaterialIW3.cpp b/src/ObjWriting/Game/IW3/Material/DumperMaterialIW3.cpp new file mode 100644 index 00000000..bb3a87a8 --- /dev/null +++ b/src/ObjWriting/Game/IW3/Material/DumperMaterialIW3.cpp @@ -0,0 +1,30 @@ +#include "DumperMaterialIW3.h" + +#include "Game/IW3/Material/JsonMaterialWriterIW3.h" +#include "Game/IW3/Material/MaterialConstantZoneStateIW3.h" +#include "Material/MaterialCommon.h" + +using namespace IW3; + +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/IW3/AssetDumpers/AssetDumperMaterial.h b/src/ObjWriting/Game/IW3/Material/DumperMaterialIW3.h similarity index 78% rename from src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperMaterial.h rename to src/ObjWriting/Game/IW3/Material/DumperMaterialIW3.h index 09933cf4..8438f6af 100644 --- a/src/ObjWriting/Game/IW3/AssetDumpers/AssetDumperMaterial.h +++ b/src/ObjWriting/Game/IW3/Material/DumperMaterialIW3.h @@ -7,6 +7,9 @@ namespace IW3 { 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/IW3/Material/MaterialConstantZoneStateIW3.cpp b/src/ObjWriting/Game/IW3/Material/MaterialConstantZoneStateIW3.cpp new file mode 100644 index 00000000..9cd1099c --- /dev/null +++ b/src/ObjWriting/Game/IW3/Material/MaterialConstantZoneStateIW3.cpp @@ -0,0 +1,250 @@ +#include "MaterialConstantZoneStateIW3.h" + +#include "Game/IW3/CommonIW3.h" +#include "Game/IW3/GameAssetPoolIW3.h" +#include "Game/IW3/GameIW3.h" +#include "ObjWriting.h" + +namespace IW3 +{ + 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::IW3)->GetZones()) + { + const auto* assetPools = dynamic_cast(zone->m_pools.get()); + if (!assetPools) + return; + + for (const auto* techniqueSetInfo : *assetPools->m_technique_set) + { + const auto* techniqueSet = techniqueSetInfo->Asset(); + + for (const auto* technique : techniqueSet->techniques) + { + if (technique) + ExtractNamesFromTechnique(technique); + } + } + } + } + + unsigned MaterialConstantZoneState::HashString(const std::string& str) + { + return Common::R_HashString(str.c_str()); + } + + void MaterialConstantZoneState::ExtractNamesFromTechnique(const MaterialTechnique* technique) + { + if (!ShouldDumpFromStruct(technique)) + return; + + for (auto passIndex = 0u; passIndex < technique->passCount; passIndex++) + { + const auto& pass = technique->passArray[passIndex]; + + if (pass.vertexShader && pass.vertexShader->prog.loadDef.program) + ExtractNamesFromShader(pass.vertexShader->prog.loadDef.program, pass.vertexShader->prog.loadDef.programSize); + + if (pass.pixelShader && pass.pixelShader->prog.loadDef.program) + ExtractNamesFromShader(pass.pixelShader->prog.loadDef.program, pass.pixelShader->prog.loadDef.programSize); + } + } + + void MaterialConstantZoneState::AddStaticKnownNames() + { + for (const auto* knownConstantName : KNOWN_CONSTANT_NAMES) + AddConstantName(knownConstantName); + for (const auto* knownTextureDefName : KNOWN_TEXTURE_DEF_NAMES) + AddTextureDefName(knownTextureDefName); + } +} // namespace IW3 diff --git a/src/ObjWriting/Game/IW3/Material/MaterialConstantZoneStateIW3.h b/src/ObjWriting/Game/IW3/Material/MaterialConstantZoneStateIW3.h new file mode 100644 index 00000000..f46d514b --- /dev/null +++ b/src/ObjWriting/Game/IW3/Material/MaterialConstantZoneStateIW3.h @@ -0,0 +1,18 @@ +#pragma once + +#include "Game/IW3/IW3.h" +#include "Material/AbstractMaterialConstantZoneState.h" + +#include + +namespace IW3 +{ + class MaterialConstantZoneState final : public AbstractMaterialConstantZoneStateDx9 + { + protected: + void ExtractNamesFromZoneInternal() override; + void ExtractNamesFromTechnique(const MaterialTechnique* technique); + void AddStaticKnownNames() override; + unsigned HashString(const std::string& str) override; + }; +} // namespace IW3 diff --git a/src/ObjWriting/Game/IW3/ObjWriterIW3.cpp b/src/ObjWriting/Game/IW3/ObjWriterIW3.cpp index 2dc30adf..55da3a17 100644 --- a/src/ObjWriting/Game/IW3/ObjWriterIW3.cpp +++ b/src/ObjWriting/Game/IW3/ObjWriterIW3.cpp @@ -4,12 +4,12 @@ #include "AssetDumpers/AssetDumperLoadedSound.h" #include "AssetDumpers/AssetDumperLocalizeEntry.h" #include "AssetDumpers/AssetDumperMapEnts.h" -#include "AssetDumpers/AssetDumperMaterial.h" #include "AssetDumpers/AssetDumperRawFile.h" #include "AssetDumpers/AssetDumperStringTable.h" #include "AssetDumpers/AssetDumperWeapon.h" #include "AssetDumpers/AssetDumperXModel.h" #include "Game/IW3/GameAssetPoolIW3.h" +#include "Material/DumperMaterialIW3.h" #include "ObjWriting.h" using namespace IW3; diff --git a/src/ObjWriting/Game/IW3/ObjWriterIW3.h b/src/ObjWriting/Game/IW3/ObjWriterIW3.h index 6e6bdbe4..e28527d4 100644 --- a/src/ObjWriting/Game/IW3/ObjWriterIW3.h +++ b/src/ObjWriting/Game/IW3/ObjWriterIW3.h @@ -1,4 +1,5 @@ #pragma once + #include "IObjWriter.h" namespace IW3 diff --git a/src/ObjWriting/Material/JsonMaterialWriter.cpp.template b/src/ObjWriting/Material/JsonMaterialWriter.cpp.template index 2c0ef0f0..f87ca242 100644 --- a/src/ObjWriting/Material/JsonMaterialWriter.cpp.template +++ b/src/ObjWriting/Material/JsonMaterialWriter.cpp.template @@ -1,8 +1,12 @@ -#options GAME (IW4, IW5, T6) +#options GAME (IW3, IW4, IW5, T6) #filename "Game/" + GAME + "/Material/JsonMaterialWriter" + GAME + ".cpp" -#if GAME == "IW4" +#if GAME == "IW3" +#define FEATURE_IW3 +#define HAS_WATER +#define GAME_LOWER "iw3" +#elif GAME == "IW4" #define FEATURE_IW4 #define HAS_WATER #define GAME_LOWER "iw4" @@ -218,7 +222,7 @@ namespace assert(structured.alphaTestDisabled || structured.alphaTest == GFXS_ALPHA_TEST_GT_0 -#if defined(FEATURE_IW4) || defined(FEATURE_IW5) +#if defined(FEATURE_IW3) || defined(FEATURE_IW4) || defined(FEATURE_IW5) || structured.alphaTest == GFXS_ALPHA_TEST_LT_128 #endif || structured.alphaTest == GFXS_ALPHA_TEST_GE_128); @@ -226,7 +230,7 @@ namespace jStateBitsTableEntry.alphaTest = JsonAlphaTest::DISABLED; else if (structured.alphaTest == GFXS_ALPHA_TEST_GT_0) jStateBitsTableEntry.alphaTest = JsonAlphaTest::GT0; -#if defined(FEATURE_IW4) || defined(FEATURE_IW5) +#if defined(FEATURE_IW3) || defined(FEATURE_IW4) || defined(FEATURE_IW5) else if (structured.alphaTest == GFXS_ALPHA_TEST_LT_128) jStateBitsTableEntry.alphaTest = JsonAlphaTest::LT128; #endif diff --git a/src/ObjWriting/Material/JsonMaterialWriter.h.template b/src/ObjWriting/Material/JsonMaterialWriter.h.template index d44748db..82362d35 100644 --- a/src/ObjWriting/Material/JsonMaterialWriter.h.template +++ b/src/ObjWriting/Material/JsonMaterialWriter.h.template @@ -1,4 +1,4 @@ -#options GAME (IW4, IW5, T6) +#options GAME (IW3, IW4, IW5, T6) #filename "Game/" + GAME + "/Material/JsonMaterialWriter" + GAME + ".h" diff --git a/src/ZoneCommon/Game/IW3/GameAssetPoolIW3.h b/src/ZoneCommon/Game/IW3/GameAssetPoolIW3.h index 8f6b3bf9..9d675501 100644 --- a/src/ZoneCommon/Game/IW3/GameAssetPoolIW3.h +++ b/src/ZoneCommon/Game/IW3/GameAssetPoolIW3.h @@ -14,9 +14,6 @@ public: std::unique_ptr> m_xanim_parts; std::unique_ptr> m_xmodel; std::unique_ptr> m_material; - std::unique_ptr> m_material_pixel_shader; - std::unique_ptr> m_material_vertex_shader; - std::unique_ptr> m_material_vertex_decl; std::unique_ptr> m_technique_set; std::unique_ptr> m_image; std::unique_ptr> m_sound; diff --git a/test/ObjLoadingTests/Game/IW3/Material/LoaderMaterialIW3Test.cpp b/test/ObjLoadingTests/Game/IW3/Material/LoaderMaterialIW3Test.cpp new file mode 100644 index 00000000..1a469c62 --- /dev/null +++ b/test/ObjLoadingTests/Game/IW3/Material/LoaderMaterialIW3Test.cpp @@ -0,0 +1,577 @@ +#include "Game/IW3/Material/LoaderMaterialIW3.h" + +#include "Game/IW3/CommonIW3.h" +#include "Game/IW3/GameIW3.h" +#include "SearchPath/MockSearchPath.h" +#include "Utils/MemoryManager.h" + +#include +#include +#include + +using namespace IW3; +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(IW3): Can parse material", "[iw3][material][assetloader]") + { + MockSearchPath searchPath; + searchPath.AddFileData("materials/wc/ch_plasterwall_long.json", + R"MATERIAL( +{ + "_game": "iw3", + "_type": "material", + "_version": 1, + "cameraRegion": "lit", + "constants": [ + { + "literal": [ + 0.800000011920929, + 2.0, + 1.0, + 0.625 + ], + "name": "envMapParms" + }, + { + "literal": [ + 1.0, + 1.0, + 1.0, + 1.0 + ], + "name": "colorTint" + } + ], + "gameFlags": [ + "2", + "10", + "CASTS_SHADOW" + ], + "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" + }, + { + "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": false, + "cullFace": "none", + "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": "none", + "depthTest": "less_equal", + "depthWrite": true, + "dstBlendAlpha": "zero", + "dstBlendRgb": "zero", + "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", + "polygonOffset": "offset0", + "polymodeLine": false, + "srcBlendAlpha": "one", + "srcBlendRgb": "invdestalpha", + "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", + "polygonOffset": "offset2", + "polymodeLine": true, + "srcBlendAlpha": "one", + "srcBlendRgb": "one" + }, + { + "alphaTest": "disabled", + "blendOpAlpha": "disabled", + "blendOpRgb": "add", + "colorWriteAlpha": true, + "colorWriteRgb": true, + "cullFace": "back", + "depthTest": "less_equal", + "depthWrite": false, + "dstBlendAlpha": "zero", + "dstBlendRgb": "invsrccolor", + "polygonOffset": "offset2", + "polymodeLine": false, + "srcBlendAlpha": "one", + "srcBlendRgb": "zero" + } + ], + "stateBitsEntry": [ + 0, + 1, + 2, + 3, + 1, + -1, + -1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + 4, + 4, + 4, + -1, + -1, + -1, + -1, + 5, + -1, + 1, + 6, + 1, + -1 + ], + "stateFlags": 57, + "surfaceTypeBits": 32768, + "techniqueSet": "wc_l_sm_r0c0n0s0", + "textureAtlas": { + "columns": 1, + "rows": 1 + }, + "textures": [ + { + "image": "~ch_plasterwall_long_spc-r-49~3c124bfe", + "name": "specularMap", + "samplerState": { + "clampU": false, + "clampV": false, + "clampW": false, + "filter": "aniso2x", + "mipMap": "nearest" + }, + "semantic": "specularMap" + }, + { + "image": "ch_plasterwall_long_nml", + "name": "normalMap", + "samplerState": { + "clampU": false, + "clampV": false, + "clampW": false, + "filter": "aniso2x", + "mipMap": "linear" + }, + "semantic": "normalMap" + }, + { + "image": "ch_plasterwall_long_col", + "name": "colorMap", + "samplerState": { + "clampU": false, + "clampV": false, + "clampW": false, + "filter": "aniso2x", + "mipMap": "linear" + }, + "semantic": "colorMap" + } + ] +})MATERIAL"); + + Zone zone("MockZone", 0, IGame::GetGameById(GameId::IW3)); + + MemoryManager memory; + AssetCreatorCollection creatorCollection(zone); + IgnoredAssetLookup ignoredAssetLookup; + AssetCreationContext context(zone, &creatorCollection, &ignoredAssetLookup); + + GivenImage("~ch_plasterwall_long_spc-r-49~3c124bfe", context, memory); + GivenImage("ch_plasterwall_long_nml", context, memory); + GivenImage("ch_plasterwall_long_col", context, memory); + GivenTechset("wc_l_sm_r0c0n0s0", context, memory); + + auto loader = CreateMaterialLoader(memory, searchPath); + auto result = loader->CreateAsset("wc/ch_plasterwall_long", context); + REQUIRE(result.HasBeenSuccessful()); + + const auto* assetInfo = reinterpret_cast*>(result.GetAssetInfo()); + const auto* material = assetInfo->Asset(); + + REQUIRE(material->info.name == "wc/ch_plasterwall_long"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 == 0x8000); + + constexpr int8_t expectedStateBitsEntry[]{0, 1, 2, 3, 1, -1, -1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, + -1, -1, -1, -1, 4, 4, 4, -1, -1, -1, -1, 5, -1, 1, 6, 1, -1}; + REQUIRE(std::memcmp(material->stateBitsEntry, expectedStateBitsEntry, sizeof(expectedStateBitsEntry)) == 0); + + REQUIRE(material->stateFlags == 57); + REQUIRE(material->cameraRegion == CAMERA_REGION_LIT); + + 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 == "~ch_plasterwall_long_spc-r-49~3c124bfe"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 == "ch_plasterwall_long_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 == "ch_plasterwall_long_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.8f)); + REQUIRE(constantDef0.literal.y == Approx(2.0f)); + REQUIRE(constantDef0.literal.z == Approx(1.0f)); + REQUIRE(constantDef0.literal.w == Approx(0.625f)); + + 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.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.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.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.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_INVDESTALPHA); + 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 == 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.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); + + const auto& stateBits6 = material->stateBitsTable[6]; + REQUIRE(stateBits6.loadBits.structured.srcBlendRgb == GFXS_BLEND_ZERO); + REQUIRE(stateBits6.loadBits.structured.dstBlendRgb == GFXS_BLEND_INVSRCCOLOR); + REQUIRE(stateBits6.loadBits.structured.blendOpRgb == GFXS_BLENDOP_ADD); + 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 == 1); + REQUIRE(stateBits6.loadBits.structured.polymodeLine == 0); + 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/ObjWritingTests/Game/IW3/Material/DumperMaterialIW3Test.cpp b/test/ObjWritingTests/Game/IW3/Material/DumperMaterialIW3Test.cpp new file mode 100644 index 00000000..42871ad2 --- /dev/null +++ b/test/ObjWritingTests/Game/IW3/Material/DumperMaterialIW3Test.cpp @@ -0,0 +1,571 @@ +#include "Game/IW3/Material/DumperMaterialIW3.h" + +#include "Asset/AssetRegistration.h" +#include "Game/IW3/CommonIW3.h" +#include "Game/IW3/GameIW3.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 IW3; +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 = 0x8000; + + constexpr int8_t stateBitsEntry[] = {0, 1, 2, 3, 1, -1, -1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, + -1, -1, -1, -1, 4, 4, 4, -1, -1, -1, -1, 5, -1, 1, 6, 1, -1}; + std::memcpy(material->stateBitsEntry, stateBitsEntry, sizeof(material->stateBitsEntry)); + + material->cameraRegion = CAMERA_REGION_LIT; + material->stateFlags = 57; + 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("~ch_plasterwall_long_spc-r-49~3c124bfe", memory); + textureDef0.nameHash = 0x34ecccb3; + textureDef0.nameStart = 's'; + textureDef0.nameEnd = 'p'; + textureDef0.samplerState.filter = TEXTURE_FILTER_ANISO2X; + textureDef0.samplerState.mipMap = SAMPLER_MIPMAP_ENUM_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_plasterwall_long_nml", memory); + textureDef1.nameHash = 0x59d30d0f; + textureDef1.nameStart = 'n'; + textureDef1.nameEnd = 'p'; + textureDef1.samplerState.filter = TEXTURE_FILTER_ANISO2X; + textureDef1.samplerState.mipMap = SAMPLER_MIPMAP_ENUM_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("ch_plasterwall_long_col", memory); + textureDef2.nameHash = 0xa0ab1041; + textureDef2.nameStart = 'c'; + textureDef2.nameEnd = 'p'; + textureDef2.samplerState.filter = TEXTURE_FILTER_ANISO2X; + textureDef2.samplerState.mipMap = SAMPLER_MIPMAP_ENUM_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.8f; + constantDef0.literal.y = 2.0f; + constantDef0.literal.z = 1.0f; + constantDef0.literal.w = 0.625f; + + 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.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.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.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.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_INVDESTALPHA; + 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 = 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.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; + + auto& stateBits6 = material->stateBitsTable[6]; + stateBits6.loadBits.structured.srcBlendRgb = GFXS_BLEND_ZERO; + stateBits6.loadBits.structured.dstBlendRgb = GFXS_BLEND_INVSRCCOLOR; + stateBits6.loadBits.structured.blendOpRgb = GFXS_BLENDOP_ADD; + 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 = 1; + stateBits6.loadBits.structured.polymodeLine = 0; + 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(IW3): Can dump material", "[iw3][material][assetdumper]") + { + std::string expected(R"MATERIAL( +{ + "_game": "iw3", + "_type": "material", + "_version": 1, + "cameraRegion": "lit", + "constants": [ + { + "literal": [ + 0.800000011920929, + 2.0, + 1.0, + 0.625 + ], + "name": "envMapParms" + }, + { + "literal": [ + 1.0, + 1.0, + 1.0, + 1.0 + ], + "name": "colorTint" + } + ], + "gameFlags": [ + "2", + "10", + "CASTS_SHADOW" + ], + "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" + }, + { + "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": false, + "cullFace": "none", + "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": "none", + "depthTest": "less_equal", + "depthWrite": true, + "dstBlendAlpha": "zero", + "dstBlendRgb": "zero", + "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", + "polygonOffset": "offset0", + "polymodeLine": false, + "srcBlendAlpha": "one", + "srcBlendRgb": "invdestalpha", + "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", + "polygonOffset": "offset2", + "polymodeLine": true, + "srcBlendAlpha": "one", + "srcBlendRgb": "one" + }, + { + "alphaTest": "disabled", + "blendOpAlpha": "disabled", + "blendOpRgb": "add", + "colorWriteAlpha": true, + "colorWriteRgb": true, + "cullFace": "back", + "depthTest": "less_equal", + "depthWrite": false, + "dstBlendAlpha": "zero", + "dstBlendRgb": "invsrccolor", + "polygonOffset": "offset2", + "polymodeLine": false, + "srcBlendAlpha": "one", + "srcBlendRgb": "zero" + } + ], + "stateBitsEntry": [ + 0, + 1, + 2, + 3, + 1, + -1, + -1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + 4, + 4, + 4, + -1, + -1, + -1, + -1, + 5, + -1, + 1, + 6, + 1, + -1 + ], + "stateFlags": 57, + "surfaceTypeBits": 32768, + "techniqueSet": "wc_l_sm_r0c0n0s0", + "textureAtlas": { + "columns": 1, + "rows": 1 + }, + "textures": [ + { + "image": "~ch_plasterwall_long_spc-r-49~3c124bfe", + "name": "specularMap", + "samplerState": { + "clampU": false, + "clampV": false, + "clampW": false, + "filter": "aniso2x", + "mipMap": "nearest" + }, + "semantic": "specularMap" + }, + { + "image": "ch_plasterwall_long_nml", + "name": "normalMap", + "samplerState": { + "clampU": false, + "clampV": false, + "clampW": false, + "filter": "aniso2x", + "mipMap": "linear" + }, + "semantic": "normalMap" + }, + { + "image": "ch_plasterwall_long_col", + "name": "colorMap", + "samplerState": { + "clampU": false, + "clampV": false, + "clampW": false, + "filter": "aniso2x", + "mipMap": "linear" + }, + "semantic": "colorMap" + } + ] +})MATERIAL"); + + Zone zone("MockZone", 0, IGame::GetGameById(GameId::IW3)); + + MemoryManager memory; + MockSearchPath mockObjPath; + MockOutputPath mockOutput; + AssetDumpingContext context(zone, "", mockOutput, mockObjPath); + + AssetPoolDynamic materialPool(0); + + GivenMaterial("wc/ch_plasterwall_long", materialPool, memory); + + AssetDumperMaterial dumper; + dumper.DumpPool(context, &materialPool); + + const auto* file = mockOutput.GetMockedFile("materials/wc/ch_plasterwall_long.json"); + REQUIRE(file); + REQUIRE(JsonNormalized(file->AsString()) == JsonNormalized(expected)); + } +} // namespace From babfe7c88451bfd585ef36e544c5ef1100d76986 Mon Sep 17 00:00:00 2001 From: Jan Date: Tue, 1 Jul 2025 21:09:02 +0100 Subject: [PATCH 04/13] feat: dump and load T5 materials --- src/Common/Game/T5/CommonT5.h | 15 + src/Common/Game/T5/T5_Assets.h | 209 ++++++- src/Common/Game/T6/T6_Assets.h | 2 + .../Material/JsonMaterial.h.template | 45 +- .../Game/T5/Material/LoaderMaterialT5.cpp | 54 ++ .../Game/T5/Material/LoaderMaterialT5.h | 12 + src/ObjLoading/Game/T5/ObjLoaderT5.cpp | 3 +- .../Material/JsonMaterialLoader.cpp.template | 21 +- .../Material/JsonMaterialLoader.h.template | 2 +- .../Game/IW5/Material/DumperMaterialIW5.cpp | 3 +- .../Game/T5/Material/DumperMaterialT5.cpp | 34 ++ .../Game/T5/Material/DumperMaterialT5.h | 17 + .../Material/MaterialConstantZoneStateT5.cpp | 524 ++++++++++++++++++ .../T5/Material/MaterialConstantZoneStateT5.h | 18 + src/ObjWriting/Game/T5/ObjWriterT5.cpp | 3 +- .../Game/T6/Material/DumperMaterialT6.cpp | 6 +- .../Material/JsonMaterialWriter.cpp.template | 17 +- .../Material/JsonMaterialWriter.h.template | 2 +- 18 files changed, 952 insertions(+), 35 deletions(-) create mode 100644 src/ObjLoading/Game/T5/Material/LoaderMaterialT5.cpp create mode 100644 src/ObjLoading/Game/T5/Material/LoaderMaterialT5.h create mode 100644 src/ObjWriting/Game/T5/Material/DumperMaterialT5.cpp create mode 100644 src/ObjWriting/Game/T5/Material/DumperMaterialT5.h create mode 100644 src/ObjWriting/Game/T5/Material/MaterialConstantZoneStateT5.cpp create mode 100644 src/ObjWriting/Game/T5/Material/MaterialConstantZoneStateT5.h diff --git a/src/Common/Game/T5/CommonT5.h b/src/Common/Game/T5/CommonT5.h index 9cb67e29..c5e4d762 100644 --- a/src/Common/Game/T5/CommonT5.h +++ b/src/Common/Game/T5/CommonT5.h @@ -11,6 +11,21 @@ namespace T5 static int Com_HashString(const char* str); static int Com_HashString(const char* str, int len); + static constexpr uint32_t R_HashString(const char* str, uint32_t hash) + { + for (const auto* pos = str; *pos; pos++) + { + hash = 33 * hash ^ (*pos | 0x20); + } + + return hash; + } + + static constexpr uint32_t R_HashString(const char* string) + { + return R_HashString(string, 0u); + } + static PackedTexCoords Vec2PackTexCoords(const float (&in)[2]); static PackedUnitVec Vec3PackUnitVec(const float (&in)[3]); static GfxColor Vec4PackGfxColor(const float (&in)[4]); diff --git a/src/Common/Game/T5/T5_Assets.h b/src/Common/Game/T5/T5_Assets.h index c1145466..865e2100 100644 --- a/src/Common/Game/T5/T5_Assets.h +++ b/src/Common/Game/T5/T5_Assets.h @@ -739,14 +739,30 @@ namespace T5 gcc_align32(8) uint64_t packed; }; + enum MaterialGameFlags + { + MTL_GAMEFLAG_1 = 0x1, + MTL_GAMEFLAG_2 = 0x2, + MTL_GAMEFLAG_4 = 0x4, + MTL_GAMEFLAG_8 = 0x8, + MTL_GAMEFLAG_10 = 0x10, + MTL_GAMEFLAG_20 = 0x20, + + // Probably, seems to be this in T5 + MTL_GAMEFLAG_CASTS_SHADOW = 0x40, + MTL_GAMEFLAG_80 = 0x80, + MTL_GAMEFLAG_100 = 0x100, + MTL_GAMEFLAG_200 = 0x200, + }; + struct MaterialInfo { const char* name; unsigned int gameFlags; char pad; - char sortKey; - char textureAtlasRowCount; - char textureAtlasColumnCount; + unsigned char sortKey; + unsigned char textureAtlasRowCount; + unsigned char textureAtlasColumnCount; GfxDrawSurf drawSurf; unsigned int surfaceTypeBits; unsigned int layeredSurfaceTypes; @@ -787,12 +803,45 @@ namespace T5 water_t* water; }; + enum TextureFilter + { + TEXTURE_FILTER_DISABLED = 0x0, + TEXTURE_FILTER_NEAREST = 0x1, + TEXTURE_FILTER_LINEAR = 0x2, + TEXTURE_FILTER_ANISO2X = 0x3, + TEXTURE_FILTER_ANISO4X = 0x4, + + TEXTURE_FILTER_COUNT + }; + + enum SamplerStateBitsMipMap_e + { + SAMPLER_MIPMAP_ENUM_DISABLED, + SAMPLER_MIPMAP_ENUM_NEAREST, + SAMPLER_MIPMAP_ENUM_LINEAR, + + SAMPLER_MIPMAP_ENUM_COUNT + }; + + 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; - char samplerState; + MaterialTextureDefSamplerState samplerState; unsigned char semantic; // TextureSemantic char isMatureContent; char pad[3]; @@ -803,7 +852,96 @@ namespace T5 { unsigned int nameHash; char name[12]; - float literal[4]; + vec4_t literal; + }; + + enum GfxBlend : unsigned int + { + GFXS_BLEND_DISABLED = 0x0, + GFXS_BLEND_ZERO = 0x1, + GFXS_BLEND_ONE = 0x2, + GFXS_BLEND_SRCCOLOR = 0x3, + GFXS_BLEND_INVSRCCOLOR = 0x4, + GFXS_BLEND_SRCALPHA = 0x5, + GFXS_BLEND_INVSRCALPHA = 0x6, + GFXS_BLEND_DESTALPHA = 0x7, + GFXS_BLEND_INVDESTALPHA = 0x8, + GFXS_BLEND_DESTCOLOR = 0x9, + GFXS_BLEND_INVDESTCOLOR = 0xA, + GFXS_BLEND_MASK = 0xF, + }; + + enum GfxBlendOp : unsigned int + { + GFXS_BLENDOP_DISABLED = 0x0, + GFXS_BLENDOP_ADD = 0x1, + GFXS_BLENDOP_SUBTRACT = 0x2, + GFXS_BLENDOP_REVSUBTRACT = 0x3, + GFXS_BLENDOP_MIN = 0x4, + GFXS_BLENDOP_MAX = 0x5, + GFXS_BLENDOP_MASK = 0x7, + }; + + enum GfxAlphaTest_e + { + GFXS_ALPHA_TEST_GT_0 = 1, + GFXS_ALPHA_TEST_GE_255 = 2, + GFXS_ALPHA_TEST_GE_128 = 3, + + 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 + { + GFXS_STENCILOP_KEEP = 0x0, + GFXS_STENCILOP_ZERO = 0x1, + GFXS_STENCILOP_REPLACE = 0x2, + GFXS_STENCILOP_INCRSAT = 0x3, + GFXS_STENCILOP_DECRSAT = 0x4, + GFXS_STENCILOP_INVERT = 0x5, + GFXS_STENCILOP_INCR = 0x6, + GFXS_STENCILOP_DECR = 0x7, + + GFXS_STENCILOP_COUNT, + 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 @@ -868,9 +1006,64 @@ namespace T5 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 : 2; // 29-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; + }; + + enum GfxCameraRegionType + { + CAMERA_REGION_LIT = 0x0, + CAMERA_REGION_DECAL = 0x1, + CAMERA_REGION_EMISSIVE = 0x2, + + CAMERA_REGION_COUNT, + CAMERA_REGION_NONE = CAMERA_REGION_COUNT, }; struct Material @@ -880,8 +1073,8 @@ namespace T5 unsigned char textureCount; unsigned char constantCount; unsigned char stateBitsCount; - char stateFlags; - char cameraRegion; + unsigned char stateFlags; + unsigned char cameraRegion; unsigned char maxStreamedMips; MaterialTechniqueSet* techniqueSet; MaterialTextureDef* textureTable; diff --git a/src/Common/Game/T6/T6_Assets.h b/src/Common/Game/T6/T6_Assets.h index 62f3f300..28de10dc 100644 --- a/src/Common/Game/T6/T6_Assets.h +++ b/src/Common/Game/T6/T6_Assets.h @@ -700,6 +700,8 @@ namespace T6 MTL_GAMEFLAG_400 = 0x400, MTL_GAMEFLAG_800 = 0x800, MTL_GAMEFLAG_1000 = 0x1000, + MTL_GAMEFLAG_2000 = 0x2000, + MTL_GAMEFLAG_4000 = 0x4000, }; struct type_align32(8) MaterialInfo diff --git a/src/ObjCommon/Material/JsonMaterial.h.template b/src/ObjCommon/Material/JsonMaterial.h.template index 426bf449..f3fbb66f 100644 --- a/src/ObjCommon/Material/JsonMaterial.h.template +++ b/src/ObjCommon/Material/JsonMaterial.h.template @@ -1,4 +1,4 @@ -#options GAME (IW3, IW4, IW5, T6) +#options GAME (IW3, IW4, IW5, T5, T6) #filename "Game/" + GAME + "/Material/JsonMaterial" + GAME + ".h" @@ -11,6 +11,9 @@ #elif GAME == "IW5" #define FEATURE_IW5 #define HAS_WATER +#elif GAME == "T5" +#define FEATURE_T5 +#define HAS_WATER #elif GAME == "T6" #define FEATURE_T6 #endif @@ -80,6 +83,8 @@ namespace GAME GT0, #if defined(FEATURE_IW3) || defined(FEATURE_IW4) || defined(FEATURE_IW5) LT128, +#elif defined(FEATURE_T5) + GE255, #endif GE128 }; @@ -90,6 +95,8 @@ namespace GAME {JsonAlphaTest::GT0, "gt0" }, #if defined(FEATURE_IW3) || defined(FEATURE_IW4) || defined(FEATURE_IW5) {JsonAlphaTest::LT128, "lt128" }, +#elif defined(FEATURE_T5) + {JsonAlphaTest::GE255, "ge255" }, #endif {JsonAlphaTest::GE128, "ge128" } }); @@ -321,7 +328,7 @@ namespace GAME {TS_2D, "2D" }, {TS_FUNCTION, "function" }, {TS_COLOR_MAP, "colorMap" }, -#if defined(FEATURE_IW3) || defined(FEATURE_T6) +#if defined(FEATURE_IW3) || defined(FEATURE_T5) || defined(FEATURE_T6) {TS_UNUSED_1, "unused1" }, #else {TS_DETAIL_MAP, "detailMap" }, @@ -332,15 +339,17 @@ namespace GAME {TS_UNUSED_4, "unused4" }, {TS_SPECULAR_MAP, "specularMap" }, {TS_UNUSED_5, "unused5" }, -#if defined(FEATURE_IW3) || defined(FEATURE_IW4) || defined(FEATURE_IW5) +#ifdef FEATURE_T6 + {TS_OCCLUSION_MAP, "occlusionMap" }, +#endif {TS_UNUSED_6, "unused6" }, +#if defined(FEATURE_IW3) || defined(FEATURE_IW4) || defined(FEATURE_IW5) || defined(FEATURE_T5) {TS_WATER_MAP, "waterMap" }, +#endif #ifdef FEATURE_IW5 {TS_DISPLACEMENT_MAP, "displacementMap"}, #endif -#elif defined(FEATURE_T6) - {TS_OCCLUSION_MAP, "occlusionMap" }, - {TS_UNUSED_6, "unused6" }, +#if defined(FEATURE_T5) || defined(FEATURE_T6) {TS_COLOR0_MAP, "color0Map" }, {TS_COLOR1_MAP, "color1Map" }, {TS_COLOR2_MAP, "color2Map" }, @@ -369,7 +378,7 @@ namespace GAME std::optional nameStart; std::optional nameEnd; TextureSemantic semantic; -#ifdef FEATURE_T6 +#if defined(FEATURE_T5) || defined(FEATURE_T6) bool isMatureContent; #endif JsonSamplerState samplerState; @@ -393,7 +402,7 @@ namespace GAME } out["semantic"] = in.semantic; -#ifdef FEATURE_T6 +#if defined(FEATURE_T5) || defined(FEATURE_T6) out["isMatureContent"] = in.isMatureContent; #endif out["samplerState"] = in.samplerState; @@ -410,7 +419,7 @@ namespace GAME optional_from_json(in, "nameStart", out.nameStart); optional_from_json(in, "nameEnd", out.nameEnd); in.at("semantic").get_to(out.semantic); -#ifdef FEATURE_T6 +#if defined(FEATURE_T5) || defined(FEATURE_T6) in.at("isMatureContent").get_to(out.isMatureContent); #endif in.at("samplerState").get_to(out.samplerState); @@ -445,24 +454,28 @@ namespace GAME {MTL_GAMEFLAG_8, "8" }, {MTL_GAMEFLAG_10, "10" }, {MTL_GAMEFLAG_20, "20" }, -#if defined(FEATURE_IW3) || defined(FEATURE_T6) +#if defined(FEATURE_IW3) || defined(FEATURE_T5) || defined(FEATURE_T6) {MTL_GAMEFLAG_CASTS_SHADOW, "CASTS_SHADOW"}, {MTL_GAMEFLAG_CASTS_SHADOW, "40" }, #else {MTL_GAMEFLAG_40, "40" }, #endif {MTL_GAMEFLAG_80, "80" }, -#ifdef FEATURE_T6 +#if defined(FEATURE_T5) || defined(FEATURE_T6) {MTL_GAMEFLAG_100, "100" }, {MTL_GAMEFLAG_200, "200" }, +#if defined(FEATURE_T6) {MTL_GAMEFLAG_400, "400" }, {MTL_GAMEFLAG_800, "800" }, {MTL_GAMEFLAG_1000, "1000" }, + {MTL_GAMEFLAG_2000, "2000" }, + {MTL_GAMEFLAG_4000, "4000" }, +#endif #endif }); NLOHMANN_JSON_SERIALIZE_ENUM(GfxCameraRegionType, { -#if defined(FEATURE_IW3) +#if defined(FEATURE_IW3) || defined(FEATURE_T5) {CAMERA_REGION_LIT, "lit" }, {CAMERA_REGION_DECAL, "decal" }, {CAMERA_REGION_EMISSIVE, "emissive" }, @@ -492,7 +505,9 @@ namespace GAME class JsonMaterial { public: -#ifdef FEATURE_T6 +#ifdef FEATURE_T5 + uint8_t maxStreamedMips; +#elif defined(FEATURE_T6) unsigned layeredSurfaceTypes; unsigned hashIndex; unsigned surfaceFlags; @@ -515,7 +530,9 @@ namespace GAME NLOHMANN_DEFINE_TYPE_EXTENSION( JsonMaterial, -#ifdef FEATURE_T6 +#ifdef FEATURE_T5 + maxStreamedMips, +#elif defined(FEATURE_T6) layeredSurfaceTypes, hashIndex, surfaceFlags, diff --git a/src/ObjLoading/Game/T5/Material/LoaderMaterialT5.cpp b/src/ObjLoading/Game/T5/Material/LoaderMaterialT5.cpp new file mode 100644 index 00000000..a362e728 --- /dev/null +++ b/src/ObjLoading/Game/T5/Material/LoaderMaterialT5.cpp @@ -0,0 +1,54 @@ +#include "LoaderMaterialT5.h" + +#include "Game/T5/Material/JsonMaterialLoaderT5.h" +#include "Game/T5/T5.h" +#include "Material/MaterialCommon.h" + +#include +#include + +using namespace T5; + +namespace +{ + class MaterialLoader final : public AssetCreator + { + public: + MaterialLoader(MemoryManager& memory, ISearchPath& searchPath) + : m_memory(memory), + m_search_path(searchPath) + { + } + + AssetCreationResult CreateAsset(const std::string& assetName, AssetCreationContext& context) override + { + 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); + if (!LoadMaterialAsJson(*file.m_stream, *material, m_memory, context, registration)) + { + std::cerr << std::format("Failed to load material \"{}\"\n", assetName); + return AssetCreationResult::Failure(); + } + + return AssetCreationResult::Success(context.AddAsset(std::move(registration))); + } + + private: + MemoryManager& m_memory; + ISearchPath& m_search_path; + }; +} // namespace + +namespace T5 +{ + std::unique_ptr> CreateMaterialLoader(MemoryManager& memory, ISearchPath& searchPath) + { + return std::make_unique(memory, searchPath); + } +} // namespace T5 diff --git a/src/ObjLoading/Game/T5/Material/LoaderMaterialT5.h b/src/ObjLoading/Game/T5/Material/LoaderMaterialT5.h new file mode 100644 index 00000000..22c337ff --- /dev/null +++ b/src/ObjLoading/Game/T5/Material/LoaderMaterialT5.h @@ -0,0 +1,12 @@ +#pragma once + +#include "Asset/IAssetCreator.h" +#include "Game/T5/T5.h" +#include "Gdt/IGdtQueryable.h" +#include "SearchPath/ISearchPath.h" +#include "Utils/MemoryManager.h" + +namespace T5 +{ + std::unique_ptr> CreateMaterialLoader(MemoryManager& memory, ISearchPath& searchPath); +} // namespace T5 diff --git a/src/ObjLoading/Game/T5/ObjLoaderT5.cpp b/src/ObjLoading/Game/T5/ObjLoaderT5.cpp index 1f4d8132..f877d17c 100644 --- a/src/ObjLoading/Game/T5/ObjLoaderT5.cpp +++ b/src/ObjLoading/Game/T5/ObjLoaderT5.cpp @@ -5,6 +5,7 @@ #include "Game/T5/T5.h" #include "Game/T5/XModel/LoaderXModelT5.h" #include "Localize/LoaderLocalizeT5.h" +#include "Material/LoaderMaterialT5.h" #include "ObjLoading.h" #include "RawFile/LoaderRawFileT5.h" #include "StringTable/LoaderStringTableT5.h" @@ -104,7 +105,7 @@ namespace // collection.AddAssetCreator(std::make_unique(memory)); // collection.AddAssetCreator(std::make_unique(memory)); collection.AddAssetCreator(CreateXModelLoader(memory, searchPath, zone)); - // collection.AddAssetCreator(std::make_unique(memory)); + collection.AddAssetCreator(CreateMaterialLoader(memory, searchPath)); // collection.AddAssetCreator(std::make_unique(memory)); // collection.AddAssetCreator(std::make_unique(memory)); // collection.AddAssetCreator(std::make_unique(memory)); diff --git a/src/ObjLoading/Material/JsonMaterialLoader.cpp.template b/src/ObjLoading/Material/JsonMaterialLoader.cpp.template index 8db6cd37..21c32982 100644 --- a/src/ObjLoading/Material/JsonMaterialLoader.cpp.template +++ b/src/ObjLoading/Material/JsonMaterialLoader.cpp.template @@ -1,4 +1,4 @@ -#options GAME (IW3, IW4, IW5, T6) +#options GAME (IW3, IW4, IW5, T5, T6) #filename "Game/" + GAME + "/Material/JsonMaterialLoader" + GAME + ".cpp" @@ -14,6 +14,10 @@ #define FEATURE_IW5 #define HAS_WATER #define GAME_LOWER "iw5" +#elif GAME == "T5" +#define FEATURE_T5 +#define HAS_WATER +#define GAME_LOWER "t5" #elif GAME == "T6" #define FEATURE_T6 #define GAME_LOWER "t6" @@ -29,6 +33,7 @@ #ifdef HAS_WATER #include "Base64.h" #endif + #set COMMON_HEADER "\"Game/" + GAME + "/Common" + GAME + ".h\"" #include COMMON_HEADER #set JSON_HEADER "\"Game/" + GAME + "/Material/JsonMaterial" + GAME + ".h\"" @@ -100,7 +105,7 @@ namespace #if defined(FEATURE_IW3) || defined(FEATURE_IW4) || defined(FEATURE_IW5) static bool CreateGameFlagsFromJson(const JsonMaterial& jMaterial, unsigned char& gameFlags) -#elif defined(FEATURE_T6) +#elif defined(FEATURE_T5) || defined(FEATURE_T6) static bool CreateGameFlagsFromJson(const JsonMaterial& jMaterial, unsigned& gameFlags) #endif { @@ -201,7 +206,7 @@ namespace CreateSamplerStateFromJson(jTexture.samplerState, textureDef.samplerState); textureDef.semantic = jTexture.semantic; -#ifdef FEATURE_T6 +#if defined(FEATURE_T5) || defined(FEATURE_T6) textureDef.isMatureContent = jTexture.isMatureContent; #endif @@ -314,6 +319,12 @@ namespace structured.alphaTestDisabled = 0; structured.alphaTest = GFXS_ALPHA_TEST_LT_128; } +#elif defined(FEATURE_T5) + else if (jStateBitsTableEntry.alphaTest == JsonAlphaTest::GE255) + { + structured.alphaTestDisabled = 0; + structured.alphaTest = GFXS_ALPHA_TEST_GE_255; + } #endif else if (jStateBitsTableEntry.alphaTest == JsonAlphaTest::GE128) { @@ -424,7 +435,9 @@ namespace material.stateFlags = static_cast(jMaterial.stateFlags); material.cameraRegion = jMaterial.cameraRegion; -#ifdef FEATURE_T6 +#if defined(FEATURE_T5) + material.maxStreamedMips = jMaterial.maxStreamedMips; +#elif defined(FEATURE_T6) material.probeMipBits = jMaterial.probeMipBits; #endif diff --git a/src/ObjLoading/Material/JsonMaterialLoader.h.template b/src/ObjLoading/Material/JsonMaterialLoader.h.template index 49b8057d..04b768df 100644 --- a/src/ObjLoading/Material/JsonMaterialLoader.h.template +++ b/src/ObjLoading/Material/JsonMaterialLoader.h.template @@ -1,4 +1,4 @@ -#options GAME (IW3, IW4, IW5, T6) +#options GAME (IW3, IW4, IW5, T5, T6) #filename "Game/" + GAME + "/Material/JsonMaterialLoader" + GAME + ".h" diff --git a/src/ObjWriting/Game/IW5/Material/DumperMaterialIW5.cpp b/src/ObjWriting/Game/IW5/Material/DumperMaterialIW5.cpp index 6b9f6e2c..b8e705cb 100644 --- a/src/ObjWriting/Game/IW5/Material/DumperMaterialIW5.cpp +++ b/src/ObjWriting/Game/IW5/Material/DumperMaterialIW5.cpp @@ -26,5 +26,6 @@ void AssetDumperMaterial::DumpAsset(AssetDumpingContext& context, XAssetInfoAsset(), context); + const auto* material = asset->Asset(); + DumpMaterialAsJson(*assetFile, *material, context); } diff --git a/src/ObjWriting/Game/T5/Material/DumperMaterialT5.cpp b/src/ObjWriting/Game/T5/Material/DumperMaterialT5.cpp new file mode 100644 index 00000000..2d687c0e --- /dev/null +++ b/src/ObjWriting/Game/T5/Material/DumperMaterialT5.cpp @@ -0,0 +1,34 @@ +#include "DumperMaterialT5.h" + +#include "Game/T5/Material/JsonMaterialWriterT5.h" +#include "Game/T5/Material/MaterialConstantZoneStateT5.h" +#include "Material/MaterialCommon.h" + +#include + +using namespace T5; + +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; + + const auto* material = asset->Asset(); + assert(material->info.gameFlags < 0x400); + DumpMaterialAsJson(*assetFile, *material, context); +} diff --git a/src/ObjWriting/Game/T5/Material/DumperMaterialT5.h b/src/ObjWriting/Game/T5/Material/DumperMaterialT5.h new file mode 100644 index 00000000..bb13565c --- /dev/null +++ b/src/ObjWriting/Game/T5/Material/DumperMaterialT5.h @@ -0,0 +1,17 @@ +#pragma once + +#include "Dumping/AbstractAssetDumper.h" +#include "Game/T5/T5.h" + +namespace T5 +{ + 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; + }; +} // namespace T5 diff --git a/src/ObjWriting/Game/T5/Material/MaterialConstantZoneStateT5.cpp b/src/ObjWriting/Game/T5/Material/MaterialConstantZoneStateT5.cpp new file mode 100644 index 00000000..003d74cb --- /dev/null +++ b/src/ObjWriting/Game/T5/Material/MaterialConstantZoneStateT5.cpp @@ -0,0 +1,524 @@ +#include "MaterialConstantZoneStateT5.h" + +#include "Game/T5/CommonT5.h" +#include "Game/T5/GameAssetPoolT5.h" +#include "Game/T5/GameT5.h" +#include "ObjWriting.h" + +namespace T5 +{ + const char* KNOWN_CONSTANT_NAMES[]{ + "AngularVelocityScale", + "AnimSpeed", + "Background", + "BackgroundColor", + "BackgroundNoise", + "BakedLightingIntensity", + "BloodBrightness", + "BloodIntensity", + "BlurAmount", + "CapWidth", + "Char_Size", + "Char_Width", + "Coarseness", + "Color", + "ColorAmount", + "ColorBias", + "Color_Map_Noise", + "Color_Map_Scale", + "Color_Map_Size_Scale", + "DDXScale", + "DDYScale", + "DarkenAmount", + "DarkenPower", + "Detail_Amount", + "Detail_Normal_Tile", + "Diffuse_Normal_Height_Facing", + "Dimensions", + "DispersionAmount", + "Dolly", + "EdgeColor", + "EdgeHarshness", + "EdgeIntensity", + "EdgeMaxDist", + "EdgeMinDist", + "EdgeSize", + "Edge_Color_Multiplier", + "Emissive_Amount", + "EnemiesColor", + "Exposure", + "FPS", + "Fade_Distance", + "Fill_Direction", + "Fill_Direction2", + "FirstFrame", + "FlareIntensity", + "FlareScale", + "FlattenEdges", + "Flicker_Max", + "Flicker_Min", + "Flicker_Seed", + "Flicker_Speed", + "Font_Color", + "Gamma", + "GlossAmount", + "Gloss_Amount", + "Glow_Alt_Color", + "Glow_Color", + "Glow_Falloff", + "GradientColor", + "GradientMax", + "GradientMin", + "Grain_Amount", + "Grain_Color", + "Grid", + "Hardness", + "Heart_Rate_Offset", + "Heart_Rate_Scale", + "Highlight_1_Brightness", + "Highlight_1_Sharpness", + "Highlight_2_Brightness", + "Highlight_2_Sharpness", + "Highlight_2_Size", + "Hightlight_1_Size", + "Holo_Scale", + "LastFrame", + "Layer1Alpha", + "Layer1Depth", + "Layer1Offset", + "Layer1OffsetBobbleDelay", + "Layer1OffsetBobbleSpeedAndSize", + "Layer1Origin", + "Layer1Rotation", + "Layer1Scale", + "Layer1ScaleBobbleDelay", + "Layer1ScaleBobbleSpeedAndSize", + "Layer1Scroll", + "Layer2Alpha", + "Layer2Depth", + "Layer2Offset", + "Layer2OffsetBobbleDelay", + "Layer2OffsetBobbleSpeedAndSize", + "Layer2Origin", + "Layer2Rotation", + "Layer2Scale", + "Layer2ScaleBobbleDelay", + "Layer2ScaleBobbleSpeedAndSize", + "Layer2Scroll", + "Layer3Alpha", + "Layer3Depth", + "Layer3Offset", + "Layer3Origin", + "Layer3Rotation", + "Layer3Scale", + "Layer3Scroll", + "Layer4Alpha", + "Layer4Depth", + "Layer4Offset", + "Layer4Origin", + "Layer4Rotation", + "Layer4Scale", + "Layer4Scroll", + "LineColor", + "LineNoise", + "LineWidth", + "MaxDepth", + "MaxFlickerColor", + "MaxPulseDepth", + "MaxResolution", + "Max_Color", + "Maximum_Distance", + "Midlayer_Depth", + "MinDepth", + "MinFlickerColor", + "MinResolution", + "MinStatic", + "MinVelocityFraction", + "Min_Color", + "Min_Player_Intensity", + "MomentumColor", + "NegativeColor", + "NoisePower", + "Noise_Scale", + "NormalHeightMultiplier", + "Normal_Detail_Height", + "Normal_Detail_Scale", + "Normal_Map_Size_Scale", + "Normal_Variance_Scale", + "NumFrames", + "Outline_Lookup_Scale", + "OverallAmount", + "OverallBrightness", + "Overlay_Color", + "P1", + "P2", + "Padding", + "Player_Color_Multiplier", + "Player_Lookup_Scale", + "PositiveColor", + "Power", + "PulseColor", + "PulseInterval", + "PulseTime", + "Pulse_Color_Multiplier", + "Pulse_Lookup_Scale", + "Radius", + "ReflectionAmount", + "Reflection_Amount", + "Reflection_Blur", + "Reticle_Alt_Color", + "Reticle_Color", + "Row_Chars_", + "Scale", + "ScanlineColor", + "ScanlineIntensity", + "ScanlineOffset", + "ScanlinePower", + "ScanlineSpeed", + "ScatterAmount", + "ScatterSize", + "SceneNoise", + "SparkleBrightness", + "SparkleDensity", + "SparklePower", + "SparkleProbeAmount", + "SparkleScale", + "SparkleSpecAmount", + "SparkleWash", + "SpecGloss_Map_Size_Scale", + "SpecularAmount", + "SpecularColor", + "Specular_Amount", + "Specular_Decay_Threshold", + "Speed", + "StaticAmount", + "StaticLookupSpeed", + "StaticLookupX", + "StaticScale", + "Static_Size", + "Static_amount", + "TearLookupMaxX", + "TearLookupMinX", + "TearLookupSpeed", + "TearMultiplier", + "TearPower", + "Thickness", + "TickMarkColorAndHarshness", + "Tint", + "VelocityScale", + "VignetteMultiplier", + "VignettePower", + "WarpAmount", + "WarpHeight", + "WarpScale", + "WarpSpeed", + "WashOut", + "WashoutMultiply", + "WaterDirection", + "WaterHeight", + "WaterRefraction", + "WaterScale1", + "WaterScale2", + "WaterSpeed1", + "WaterSpeed2", + "Zoom", + "alphaDissolveParms", + "alphaRevealParms", + "alphaRevealParms1", + "alphaRevealParms2", + "alphaRevealParms3", + "alphaRevealParms4", + "clipSpaceLookupOffset", + "clipSpaceLookupScale", + "cloudsFeather", + "cloudsHeights", + "cloudsUVMad1", + "cloudsUVMad2", + "cloudsUVMul1", + "cloudsUVMul2", + "codeMeshArg", + "colorDetailScale", + "colorObjMax", + "colorObjMaxBaseBlend", + "colorObjMin", + "colorObjMinBaseBlend", + "colorTint", + "debugBumpmap", + "debugPerformance", + "detailScale", + "detailScale1", + "detailScale2", + "detailScale3", + "detailScale4", + "distortionScale", + "dofEquationScene", + "dofEquationViewModelAndFarBlur", + "dofLerpBias", + "dofLerpDownBias", + "dofLerpDownScale", + "dofLerpScale", + "dofLerpUpBias", + "dofLerpUpScale", + "dofRowDelta", + "eyeOffsetParms", + "falloffBeginColor", + "falloffEndColor", + "falloffParms", + "featherParms", + "flagParams", + "framebufferRead", + "gameTime", + "hdrAmount", + "inverseTransposeWorldMatrix", + "inverseTransposeWorldViewMatrix", + "inverseWorldMatrix", + "inverseWorldViewMatrix", + "motionblurDirectionAndMagnitude", + "occlusionAmount", + "occlusionAmount1", + "occlusionAmount2", + "occlusionAmount3", + "occlusionAmount4", + "particleCloudColor", + "particleCloudMatrix", + "particleCloudVelWorld", + "resizeParams1", + "resizeParams2", + "scaleRGB", + "scriptVector0", + "scriptVector1", + "scriptVector2", + "scriptVector3", + "scriptVector4", + "scriptVector5", + "scriptVector6", + "scriptVector7", + "skyBoxCloudWeights", + "skyBoxRotationSize", + "skyColorParms", + "spotLightWeight", + "treeCanopyLightingParms", + "treeCanopyScatterColor", + "treeCanopySwayParms", + "ui3dUVSetup0", + "ui3dUVSetup1", + "ui3dUVSetup2", + "ui3dUVSetup3", + "ui3dUVSetup4", + "ui3dUVSetup5", + "uvAnimParms", + "uvScroll", + "viewMatrix", + "weaponParam0", + "weaponParam1", + "weaponParam2", + "weaponParam3", + "weaponParam4", + "weaponParam5", + "weaponParam6", + "weaponParam7", + "weaponParam8", + "weaponParam9", + "worldViewMatrix", + "worldViewProjectionMatrix", + }; + + const char* KNOWN_TEXTURE_DEF_NAMES[]{ + "AddMap", + "Blip_Mask", + "BlockNoise", + "CS_Z_buffer", + "Camo_Detail_Map", + "Color_Map", + "CompassMap", + "Detail_Map", + "Diffuse", + "Diffuse_Map", + "DpadTexture", + "FontTextutre", + "Grain_Map", + "GridTexture", + "GrimeMap", + "Heart_Rate_Image", + "Hologram_Diffuse", + "Image", + "Layer1Map", + "Layer2Map", + "Layer3Map", + "Layer4Map", + "Lookup", + "Lookup2", + "LookupMap", + "Mask", + "Noise", + "Noise_Texture", + "NormalDetailMap", + "Normal_Detail_Map", + "Normal_Map", + "Overlay_Map", + "Reflection_Mask", + "Reveal_Map", + "Rim_Color_Mask", + "Rim_Specular_Mask", + "Rim_Occlusion_Mask", + "Scanline", + "SparkleMap", + "SpecularAndGloss", + "SpecularAndGloss2", + "Specular_Color_Map", + "Specular_Gloss_Map", + "Specular_Map", + "SpotShadowSamplerState", + "SpotShadowState", + "SpriteMap", + "Static", + "StaticMap", + "Static_Noise_Map", + "SunShadowSamplerState", + "SunShadowState", + "Surface_Normal_Map", + "ThermalMapMask", + "Thermal_Gradient", + "Thermal_Map", + "TickMarkMaterial", + "Tile", + "WarpMap", + "WaterNormalMap", + "Weapon_Normal_Map", + "Weapon_Specular_Map", + "Wireframe", + "ZBuffer_Map", + "attenuation", + "attenuationSampler", + "baseLut2D", + "baseLut2DSampler", + "cinematicA", + "cinematicASampler", + "cinematicCb", + "cinematicCbSampler", + "cinematicCr", + "cinematicCrSampler", + "cinematicY", + "cinematicYSampler", + "codeTexture0", + "codeTexture1", + "codeTexture2", + "color", + "colorDetailMap", + "colorDetailMapSampler", + "colorMap", + "colorMap1", + "colorMap2", + "colorMap2D", + "colorMapPostSun", + "colorMapPostSunSampler", + "colorMapSampler", + "colorMapSampler1", + "colorMapSampler2", + "colorSampler", + "detailMap", + "detailMapSampler", + "dlightAttenuation", + "dlightAttenuationSampler", + "floatZ", + "floatZSampler", + "imageSampler", + "lightmapSamplerSecondary", + "lightmapSecondary", + "lut2D", + "lut2DSampler", + "lut3D", + "lut3DSampler", + "missileCam", + "missileCamSampler", + "modelLighting", + "modelLightingSampler", + "normalMap", + "normalMap1", + "normalMap2", + "normalMapSampler", + "normalMapSampler1", + "normalMapSampler2", + "occlusionMap", + "occlusionMapSampler", + "occMap", + "occMapSampler", + "outdoorMap", + "outdoorMapSampler", + "radiantDiffuseMap", + "rawFloatZ", + "rawFloatZSampler", + "reflectionProbe", + "reflectionProbeSampler", + "shadowmapSamplerSpot", + "shadowmapSamplerSun", + "shadowmapSpot", + "shadowmapSun", + "sonarColor", + "sonarColorSampler", + "sonarDepth", + "sonarDepthSampler", + "source", + "specularMap", + "specularMap1", + "specularMap2", + "specularMapSampler", + "specularMapSampler1", + "specularMapSampler2", + "stencil", + "stencilSampler", + "ui3d", + "ui3dSampler", + }; + + void MaterialConstantZoneState::ExtractNamesFromZoneInternal() + { + for (const auto* zone : IGame::GetGameById(GameId::T6)->GetZones()) + { + const auto* assetPools = dynamic_cast(zone->m_pools.get()); + if (!assetPools) + return; + + for (const auto* techniqueSetInfo : *assetPools->m_technique_set) + { + const auto* techniqueSet = techniqueSetInfo->Asset(); + + for (const auto* technique : techniqueSet->techniques) + { + if (technique) + ExtractNamesFromTechnique(technique); + } + } + } + } + + unsigned MaterialConstantZoneState::HashString(const std::string& str) + { + return Common::R_HashString(str.c_str()); + } + + void MaterialConstantZoneState::ExtractNamesFromTechnique(const MaterialTechnique* technique) + { + if (!ShouldDumpFromStruct(technique)) + return; + + for (auto passIndex = 0u; passIndex < technique->passCount; passIndex++) + { + const auto& pass = technique->passArray[passIndex]; + + if (pass.vertexShader && pass.vertexShader->prog.loadDef.program) + ExtractNamesFromShader(pass.vertexShader->prog.loadDef.program, pass.vertexShader->prog.loadDef.programSize); + + if (pass.pixelShader && pass.pixelShader->prog.loadDef.program) + ExtractNamesFromShader(pass.pixelShader->prog.loadDef.program, pass.pixelShader->prog.loadDef.programSize); + } + } + + void MaterialConstantZoneState::AddStaticKnownNames() + { + for (const auto* knownConstantName : KNOWN_CONSTANT_NAMES) + AddConstantName(knownConstantName); + for (const auto* knownTextureDefName : KNOWN_TEXTURE_DEF_NAMES) + AddTextureDefName(knownTextureDefName); + } +} // namespace T5 diff --git a/src/ObjWriting/Game/T5/Material/MaterialConstantZoneStateT5.h b/src/ObjWriting/Game/T5/Material/MaterialConstantZoneStateT5.h new file mode 100644 index 00000000..f9c83922 --- /dev/null +++ b/src/ObjWriting/Game/T5/Material/MaterialConstantZoneStateT5.h @@ -0,0 +1,18 @@ +#pragma once + +#include "Game/T5/T5.h" +#include "Material/AbstractMaterialConstantZoneState.h" + +#include + +namespace T5 +{ + class MaterialConstantZoneState final : public AbstractMaterialConstantZoneStateDx11 + { + protected: + void ExtractNamesFromZoneInternal() override; + void ExtractNamesFromTechnique(const MaterialTechnique* technique); + void AddStaticKnownNames() override; + unsigned HashString(const std::string& str) override; + }; +} // namespace T5 diff --git a/src/ObjWriting/Game/T5/ObjWriterT5.cpp b/src/ObjWriting/Game/T5/ObjWriterT5.cpp index b1c06e2e..ce6dab53 100644 --- a/src/ObjWriting/Game/T5/ObjWriterT5.cpp +++ b/src/ObjWriting/Game/T5/ObjWriterT5.cpp @@ -10,6 +10,7 @@ #include "AssetDumpers/AssetDumperWeapon.h" #include "AssetDumpers/AssetDumperXModel.h" #include "Game/T5/GameAssetPoolT5.h" +#include "Material/DumperMaterialT5.h" #include "ObjWriting.h" using namespace T5; @@ -30,7 +31,7 @@ bool ObjWriter::DumpZone(AssetDumpingContext& context) const // DUMP_ASSET_POOL(AssetDumperDestructibleDef, m_destructible_def, ASSET_TYPE_DESTRUCTIBLEDEF) // DUMP_ASSET_POOL(AssetDumperXAnimParts, m_xanim_parts, ASSET_TYPE_XANIMPARTS) DUMP_ASSET_POOL(AssetDumperXModel, m_xmodel, ASSET_TYPE_XMODEL) - // DUMP_ASSET_POOL(AssetDumperMaterial, m_material, ASSET_TYPE_MATERIAL) + DUMP_ASSET_POOL(AssetDumperMaterial, m_material, ASSET_TYPE_MATERIAL) // DUMP_ASSET_POOL(AssetDumperTechniqueSet, m_technique_set, ASSET_TYPE_TECHNIQUE_SET) DUMP_ASSET_POOL(AssetDumperGfxImage, m_image, ASSET_TYPE_IMAGE) // DUMP_ASSET_POOL(AssetDumperSndBank, m_sound_bank, ASSET_TYPE_SOUND) diff --git a/src/ObjWriting/Game/T6/Material/DumperMaterialT6.cpp b/src/ObjWriting/Game/T6/Material/DumperMaterialT6.cpp index a245cc18..c90c2eb7 100644 --- a/src/ObjWriting/Game/T6/Material/DumperMaterialT6.cpp +++ b/src/ObjWriting/Game/T6/Material/DumperMaterialT6.cpp @@ -4,6 +4,8 @@ #include "Game/T6/Material/MaterialConstantZoneStateT6.h" #include "Material/MaterialCommon.h" +#include + using namespace T6; void AssetDumperMaterial::DumpPool(AssetDumpingContext& context, AssetPool* pool) @@ -26,5 +28,7 @@ void AssetDumperMaterial::DumpAsset(AssetDumpingContext& context, XAssetInfoAsset(), context); + const auto* material = asset->Asset(); + assert(material->info.gameFlags < 0x8000); + DumpMaterialAsJson(*assetFile, *material, context); } diff --git a/src/ObjWriting/Material/JsonMaterialWriter.cpp.template b/src/ObjWriting/Material/JsonMaterialWriter.cpp.template index f87ca242..00cc2b43 100644 --- a/src/ObjWriting/Material/JsonMaterialWriter.cpp.template +++ b/src/ObjWriting/Material/JsonMaterialWriter.cpp.template @@ -1,4 +1,4 @@ -#options GAME (IW3, IW4, IW5, T6) +#options GAME (IW3, IW4, IW5, T5, T6) #filename "Game/" + GAME + "/Material/JsonMaterialWriter" + GAME + ".cpp" @@ -14,6 +14,10 @@ #define FEATURE_IW5 #define HAS_WATER #define GAME_LOWER "iw5" +#elif GAME == "T5" +#define FEATURE_T5 +#define HAS_WATER +#define GAME_LOWER "t5" #elif GAME == "T6" #define FEATURE_T6 #define GAME_LOWER "t6" @@ -144,7 +148,7 @@ namespace } jTextureDef.semantic = static_cast(textureDef.semantic); -#if defined(FEATURE_T6) +#if defined(FEATURE_T5) || defined(FEATURE_T6) jTextureDef.isMatureContent = textureDef.isMatureContent; #endif @@ -224,6 +228,8 @@ namespace || structured.alphaTest == GFXS_ALPHA_TEST_GT_0 #if defined(FEATURE_IW3) || defined(FEATURE_IW4) || defined(FEATURE_IW5) || structured.alphaTest == GFXS_ALPHA_TEST_LT_128 +#elif defined(FEATURE_T5) + || structured.alphaTest == GFXS_ALPHA_TEST_GE_255 #endif || structured.alphaTest == GFXS_ALPHA_TEST_GE_128); if (structured.alphaTestDisabled) @@ -233,6 +239,9 @@ namespace #if defined(FEATURE_IW3) || defined(FEATURE_IW4) || defined(FEATURE_IW5) else if (structured.alphaTest == GFXS_ALPHA_TEST_LT_128) jStateBitsTableEntry.alphaTest = JsonAlphaTest::LT128; +#elif defined(FEATURE_T5) + else if (structured.alphaTest == GFXS_ALPHA_TEST_GE_255) + jStateBitsTableEntry.alphaTest = JsonAlphaTest::GE255; #endif else if (structured.alphaTest == GFXS_ALPHA_TEST_GE_128) jStateBitsTableEntry.alphaTest = JsonAlphaTest::GE128; @@ -317,7 +326,9 @@ namespace jMaterial.stateFlags = material.stateFlags; jMaterial.cameraRegion = static_cast(material.cameraRegion); -#ifdef FEATURE_T6 +#if defined(FEATURE_T5) + jMaterial.maxStreamedMips = material.maxStreamedMips; +#elif defined(FEATURE_T6) jMaterial.probeMipBits = material.probeMipBits; #endif diff --git a/src/ObjWriting/Material/JsonMaterialWriter.h.template b/src/ObjWriting/Material/JsonMaterialWriter.h.template index 82362d35..277c5420 100644 --- a/src/ObjWriting/Material/JsonMaterialWriter.h.template +++ b/src/ObjWriting/Material/JsonMaterialWriter.h.template @@ -1,4 +1,4 @@ -#options GAME (IW3, IW4, IW5, T6) +#options GAME (IW3, IW4, IW5, T5, T6) #filename "Game/" + GAME + "/Material/JsonMaterialWriter" + GAME + ".h" From 31d7ac26febfd73ce05dad0f513d7a5dfd437a28 Mon Sep 17 00:00:00 2001 From: Jan Date: Tue, 1 Jul 2025 21:31:46 +0100 Subject: [PATCH 05/13] chore: remove hashIndex from t6 json material as it is always 0 --- src/ObjCommon/Material/JsonMaterial.h.template | 2 -- src/ObjLoading/Material/JsonMaterialLoader.cpp.template | 1 - src/ObjWriting/Game/T6/Material/DumperMaterialT6.cpp | 1 + src/ObjWriting/Material/JsonMaterialWriter.cpp.template | 1 - test/ObjLoadingTests/Game/T6/Material/LoaderMaterialT6Test.cpp | 1 - test/ObjWritingTests/Game/T6/Material/DumperMaterialT6Test.cpp | 1 - 6 files changed, 1 insertion(+), 6 deletions(-) diff --git a/src/ObjCommon/Material/JsonMaterial.h.template b/src/ObjCommon/Material/JsonMaterial.h.template index f3fbb66f..6afb6b0a 100644 --- a/src/ObjCommon/Material/JsonMaterial.h.template +++ b/src/ObjCommon/Material/JsonMaterial.h.template @@ -509,7 +509,6 @@ namespace GAME uint8_t maxStreamedMips; #elif defined(FEATURE_T6) unsigned layeredSurfaceTypes; - unsigned hashIndex; unsigned surfaceFlags; unsigned contents; uint8_t probeMipBits; @@ -534,7 +533,6 @@ namespace GAME maxStreamedMips, #elif defined(FEATURE_T6) layeredSurfaceTypes, - hashIndex, surfaceFlags, contents, probeMipBits, diff --git a/src/ObjLoading/Material/JsonMaterialLoader.cpp.template b/src/ObjLoading/Material/JsonMaterialLoader.cpp.template index 21c32982..69ceb3ef 100644 --- a/src/ObjLoading/Material/JsonMaterialLoader.cpp.template +++ b/src/ObjLoading/Material/JsonMaterialLoader.cpp.template @@ -420,7 +420,6 @@ 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 diff --git a/src/ObjWriting/Game/T6/Material/DumperMaterialT6.cpp b/src/ObjWriting/Game/T6/Material/DumperMaterialT6.cpp index c90c2eb7..9f193037 100644 --- a/src/ObjWriting/Game/T6/Material/DumperMaterialT6.cpp +++ b/src/ObjWriting/Game/T6/Material/DumperMaterialT6.cpp @@ -30,5 +30,6 @@ void AssetDumperMaterial::DumpAsset(AssetDumpingContext& context, XAssetInfoAsset(); assert(material->info.gameFlags < 0x8000); + assert(material->info.hashIndex == 0); DumpMaterialAsJson(*assetFile, *material, context); } diff --git a/src/ObjWriting/Material/JsonMaterialWriter.cpp.template b/src/ObjWriting/Material/JsonMaterialWriter.cpp.template index 00cc2b43..fdbec537 100644 --- a/src/ObjWriting/Material/JsonMaterialWriter.cpp.template +++ b/src/ObjWriting/Material/JsonMaterialWriter.cpp.template @@ -315,7 +315,6 @@ namespace 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 diff --git a/test/ObjLoadingTests/Game/T6/Material/LoaderMaterialT6Test.cpp b/test/ObjLoadingTests/Game/T6/Material/LoaderMaterialT6Test.cpp index 2596dc78..2d797315 100644 --- a/test/ObjLoadingTests/Game/T6/Material/LoaderMaterialT6Test.cpp +++ b/test/ObjLoadingTests/Game/T6/Material/LoaderMaterialT6Test.cpp @@ -62,7 +62,6 @@ namespace "10", "CASTS_SHADOW" ], - "hashIndex": 0, "layeredSurfaceTypes": 536870925, "probeMipBits": 0, "sortKey": 4, diff --git a/test/ObjWritingTests/Game/T6/Material/DumperMaterialT6Test.cpp b/test/ObjWritingTests/Game/T6/Material/DumperMaterialT6Test.cpp index 4c6e5f50..a8704d6d 100644 --- a/test/ObjWritingTests/Game/T6/Material/DumperMaterialT6Test.cpp +++ b/test/ObjWritingTests/Game/T6/Material/DumperMaterialT6Test.cpp @@ -282,7 +282,6 @@ namespace "10", "CASTS_SHADOW" ], - "hashIndex": 0, "layeredSurfaceTypes": 536870925, "probeMipBits": 0, "sortKey": 4, From 9c2bbc414cf5c7f0c5cbeb290fa9b1cf9d73bb34 Mon Sep 17 00:00:00 2001 From: Jan Date: Tue, 1 Jul 2025 22:06:30 +0100 Subject: [PATCH 06/13] chore: add layeredSurfaceTypes to t5 materials --- src/ObjCommon/Material/JsonMaterial.h.template | 2 ++ src/ObjLoading/Material/JsonMaterialLoader.cpp.template | 4 +++- src/ObjWriting/Material/JsonMaterialWriter.cpp.template | 4 +++- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/ObjCommon/Material/JsonMaterial.h.template b/src/ObjCommon/Material/JsonMaterial.h.template index 6afb6b0a..1ff77dcb 100644 --- a/src/ObjCommon/Material/JsonMaterial.h.template +++ b/src/ObjCommon/Material/JsonMaterial.h.template @@ -507,6 +507,7 @@ namespace GAME public: #ifdef FEATURE_T5 uint8_t maxStreamedMips; + unsigned layeredSurfaceTypes; #elif defined(FEATURE_T6) unsigned layeredSurfaceTypes; unsigned surfaceFlags; @@ -531,6 +532,7 @@ namespace GAME JsonMaterial, #ifdef FEATURE_T5 maxStreamedMips, + layeredSurfaceTypes, #elif defined(FEATURE_T6) layeredSurfaceTypes, surfaceFlags, diff --git a/src/ObjLoading/Material/JsonMaterialLoader.cpp.template b/src/ObjLoading/Material/JsonMaterialLoader.cpp.template index 69ceb3ef..086c7904 100644 --- a/src/ObjLoading/Material/JsonMaterialLoader.cpp.template +++ b/src/ObjLoading/Material/JsonMaterialLoader.cpp.template @@ -418,8 +418,10 @@ namespace } material.info.surfaceTypeBits = jMaterial.surfaceTypeBits; -#ifdef FEATURE_T6 +#if defined(FEATURE_T5) || defined(FEATURE_T6) material.info.layeredSurfaceTypes = jMaterial.layeredSurfaceTypes; +#endif +#if defined(FEATURE_T6) material.info.surfaceFlags = jMaterial.surfaceFlags; material.info.contents = jMaterial.contents; #endif diff --git a/src/ObjWriting/Material/JsonMaterialWriter.cpp.template b/src/ObjWriting/Material/JsonMaterialWriter.cpp.template index fdbec537..6a0a2639 100644 --- a/src/ObjWriting/Material/JsonMaterialWriter.cpp.template +++ b/src/ObjWriting/Material/JsonMaterialWriter.cpp.template @@ -313,8 +313,10 @@ namespace jMaterial.textureAtlas->columns = material.info.textureAtlasColumnCount; jMaterial.surfaceTypeBits = material.info.surfaceTypeBits; -#ifdef FEATURE_T6 +#if defined(FEATURE_T5) || defined(FEATURE_T6) jMaterial.layeredSurfaceTypes = material.info.layeredSurfaceTypes; +#endif +#if defined(FEATURE_T6) jMaterial.surfaceFlags = material.info.surfaceFlags; jMaterial.contents = material.info.contents; #endif From 238ac293ab006a2ac85f8c352f894f6b1f5e6302 Mon Sep 17 00:00:00 2001 From: Jan Laupetin Date: Sun, 6 Jul 2025 15:56:12 +0200 Subject: [PATCH 07/13] chore: add tests for t5 material dumping and loading --- .../Game/T5/Material/LoaderMaterialT5Test.cpp | 642 ++++++++++++++++++ .../Game/T5/Material/DumperMaterialT5Test.cpp | 636 +++++++++++++++++ 2 files changed, 1278 insertions(+) create mode 100644 test/ObjLoadingTests/Game/T5/Material/LoaderMaterialT5Test.cpp create mode 100644 test/ObjWritingTests/Game/T5/Material/DumperMaterialT5Test.cpp diff --git a/test/ObjLoadingTests/Game/T5/Material/LoaderMaterialT5Test.cpp b/test/ObjLoadingTests/Game/T5/Material/LoaderMaterialT5Test.cpp new file mode 100644 index 00000000..6979a94f --- /dev/null +++ b/test/ObjLoadingTests/Game/T5/Material/LoaderMaterialT5Test.cpp @@ -0,0 +1,642 @@ +#include "Game/T5/Material/LoaderMaterialT5.h" + +#include "Game/T5/CommonT5.h" +#include "Game/T5/GameT5.h" +#include "SearchPath/MockSearchPath.h" +#include "Utils/MemoryManager.h" + +#include +#include +#include + +using namespace T5; +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(T5): Can parse material", "[t5][material][assetloader]") + { + MockSearchPath searchPath; + searchPath.AddFileData("materials/mc/jun_art_brick_foundation.json", + R"MATERIAL( +{ + "_game": "t5", + "_type": "material", + "_version": 1, + "cameraRegion": "lit", + "constants": [ + { + "literal": [ + 1.0, + 1.0, + 1.0, + 0.5 + ], + "name": "envMapParms" + }, + { + "literal": [ + 0.0, + 1.0, + 0.0, + 1.0 + ], + "nameFragment": "dynamicFolia", + "nameHash": 3034467714 + } + ], + "gameFlags": [ + "10", + "CASTS_SHADOW" + ], + "layeredSurfaceTypes": 536870914, + "maxStreamedMips": 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": 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": 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": "none", + "depthTest": "less_equal", + "depthWrite": true, + "dstBlendAlpha": "zero", + "dstBlendRgb": "zero", + "polygonOffset": "offset0", + "polymodeLine": false, + "srcBlendAlpha": "one", + "srcBlendRgb": "one" + }, + { + "alphaTest": "disabled", + "blendOpAlpha": "disabled", + "blendOpRgb": "add", + "colorWriteAlpha": true, + "colorWriteRgb": true, + "cullFace": "back", + "depthTest": "less_equal", + "depthWrite": false, + "dstBlendAlpha": "zero", + "dstBlendRgb": "one", + "polygonOffset": "offset0", + "polymodeLine": false, + "srcBlendAlpha": "one", + "srcBlendRgb": "invdestalpha" + }, + { + "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" + } + ], + "stateBitsEntry": [ + 0, + 1, + 2, + 3, + 1, + -1, + -1, + -1, + -1, + -1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + 1, + -1, + 1, + -1, + 1, + -1, + 1, + -1, + 1, + -1, + 1, + -1, + 1, + -1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + 4, + 4, + 4, + 4, + 4, + 4, + -1, + -1, + -1, + -1, + 5, + -1, + 1, + 1, + -1 + ], + "stateFlags": 121, + "surfaceTypeBits": 2, + "techniqueSet": "mc_l_sm_r0c0n0s0", + "textureAtlas": { + "columns": 1, + "rows": 1 + }, + "textures": [ + { + "image": "~$white-rgb&~-rjun_art_brick_~a05e7627", + "isMatureContent": false, + "name": "specularMap", + "samplerState": { + "clampU": false, + "clampV": false, + "clampW": false, + "filter": "aniso2x", + "mipMap": "nearest" + }, + "semantic": "specularMap" + }, + { + "image": "jun_art_brick_foundation_n", + "isMatureContent": false, + "name": "normalMap", + "samplerState": { + "clampU": false, + "clampV": false, + "clampW": false, + "filter": "aniso2x", + "mipMap": "nearest" + }, + "semantic": "normalMap" + }, + { + "image": "~-gjun_art_brick_foundation_c", + "isMatureContent": false, + "name": "colorMap", + "samplerState": { + "clampU": false, + "clampV": false, + "clampW": false, + "filter": "aniso2x", + "mipMap": "nearest" + }, + "semantic": "colorMap" + } + ] +})MATERIAL"); + + Zone zone("MockZone", 0, IGame::GetGameById(GameId::T5)); + + MemoryManager memory; + AssetCreatorCollection creatorCollection(zone); + IgnoredAssetLookup ignoredAssetLookup; + AssetCreationContext context(zone, &creatorCollection, &ignoredAssetLookup); + + GivenImage("~$white-rgb&~-rjun_art_brick_~a05e7627", context, memory); + GivenImage("jun_art_brick_foundation_n", context, memory); + GivenImage("~-gjun_art_brick_foundation_c", context, memory); + GivenTechset("mc_l_sm_r0c0n0s0", context, memory); + + auto loader = CreateMaterialLoader(memory, searchPath); + auto result = loader->CreateAsset("mc/jun_art_brick_foundation", context); + REQUIRE(result.HasBeenSuccessful()); + + const auto* assetInfo = reinterpret_cast*>(result.GetAssetInfo()); + const auto* material = assetInfo->Asset(); + + REQUIRE(material->info.name == "mc/jun_art_brick_foundation"s); + REQUIRE(material->info.gameFlags == 0x50); + REQUIRE(material->info.sortKey == 4); + REQUIRE(material->info.textureAtlasRowCount == 1); + REQUIRE(material->info.textureAtlasColumnCount == 1); + REQUIRE(material->info.surfaceTypeBits == 0x2); + REQUIRE(material->info.layeredSurfaceTypes == 0x20000002); + + constexpr int8_t expectedStateBitsEntry[]{0, 1, 2, 3, 1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, 1, 1, 1, + 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 4, 4, 4, 4, 4, 4, -1, -1, -1, -1, 5, -1, 1, 1, -1}; + REQUIRE(std::memcmp(material->stateBitsEntry, expectedStateBitsEntry, sizeof(expectedStateBitsEntry)) == 0); + + REQUIRE(material->stateFlags == 121); + REQUIRE(material->cameraRegion == CAMERA_REGION_LIT); + REQUIRE(material->maxStreamedMips == 0); + + 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.isMatureContent == false); + REQUIRE(textureDef0.u.image); + REQUIRE(textureDef0.u.image->name); + REQUIRE(textureDef0.u.image->name == "~$white-rgb&~-rjun_art_brick_~a05e7627"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.isMatureContent == false); + REQUIRE(textureDef1.u.image); + REQUIRE(textureDef1.u.image->name); + REQUIRE(textureDef1.u.image->name == "jun_art_brick_foundation_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_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.isMatureContent == false); + REQUIRE(textureDef2.u.image); + REQUIRE(textureDef2.u.image->name); + REQUIRE(textureDef2.u.image->name == "~-gjun_art_brick_foundation_c"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(1.0f)); + REQUIRE(constantDef0.literal.y == Approx(1.0f)); + REQUIRE(constantDef0.literal.z == Approx(1.0f)); + REQUIRE(constantDef0.literal.w == Approx(0.5f)); + + const auto& constantDef1 = material->constantTable[1]; + REQUIRE(constantDef1.nameHash == 0xb4de4d82); + REQUIRE(strncmp(constantDef1.name, "dynamicFolia", std::extent_v) == 0); + REQUIRE(constantDef1.literal.x == Approx(0.0f)); + REQUIRE(constantDef1.literal.y == Approx(1.0f)); + REQUIRE(constantDef1.literal.z == Approx(0.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.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 == 1); + REQUIRE(stateBits1.loadBits.structured.colorWriteAlpha == 1); + 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.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.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_INVDESTALPHA); + 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 == 0); + 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_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.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/ObjWritingTests/Game/T5/Material/DumperMaterialT5Test.cpp b/test/ObjWritingTests/Game/T5/Material/DumperMaterialT5Test.cpp new file mode 100644 index 00000000..2e6d1999 --- /dev/null +++ b/test/ObjWritingTests/Game/T5/Material/DumperMaterialT5Test.cpp @@ -0,0 +1,636 @@ +#include "Game/T5/Material/DumperMaterialT5.h" + +#include "Asset/AssetRegistration.h" +#include "Game/T5/CommonT5.h" +#include "Game/T5/GameT5.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 T5; +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 = 4; + material->info.textureAtlasRowCount = 1; + material->info.textureAtlasColumnCount = 1; + material->info.surfaceTypeBits = 0x2; + material->info.layeredSurfaceTypes = 0x20000002; + + constexpr int8_t stateBitsEntry[] = {0, 1, 2, 3, 1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, 1, 1, 1, + 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 4, 4, 4, 4, 4, 4, -1, -1, -1, -1, 5, -1, 1, 1, -1}; + std::memcpy(material->stateBitsEntry, stateBitsEntry, sizeof(material->stateBitsEntry)); + + material->cameraRegion = CAMERA_REGION_LIT; + material->stateFlags = 121; + material->maxStreamedMips = 0; + 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("~$white-rgb&~-rjun_art_brick_~a05e7627", memory); + textureDef0.nameHash = 0x34ecccb3; + textureDef0.nameStart = 's'; + textureDef0.nameEnd = 'p'; + textureDef0.samplerState.filter = TEXTURE_FILTER_ANISO2X; + textureDef0.samplerState.mipMap = SAMPLER_MIPMAP_ENUM_NEAREST; + 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.u.image = GivenImage("jun_art_brick_foundation_n", memory); + textureDef1.nameHash = 0x59d30d0f; + textureDef1.nameStart = 'n'; + textureDef1.nameEnd = 'p'; + textureDef1.samplerState.filter = TEXTURE_FILTER_ANISO2X; + textureDef1.samplerState.mipMap = SAMPLER_MIPMAP_ENUM_NEAREST; + 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.u.image = GivenImage("~-gjun_art_brick_foundation_c", memory); + textureDef2.nameHash = 0xa0ab1041; + textureDef2.nameStart = 'c'; + textureDef2.nameEnd = 'p'; + textureDef2.samplerState.filter = TEXTURE_FILTER_ANISO2X; + textureDef2.samplerState.mipMap = SAMPLER_MIPMAP_ENUM_NEAREST; + textureDef2.samplerState.clampU = 0; + textureDef2.samplerState.clampV = 0; + textureDef2.samplerState.clampW = 0; + textureDef2.semantic = TS_COLOR_MAP; + textureDef2.isMatureContent = false; + + 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 = 1.0f; + constantDef0.literal.y = 1.0f; + constantDef0.literal.z = 1.0f; + constantDef0.literal.w = 0.5f; + + auto& constantDef1 = material->constantTable[1]; + constantDef1.nameHash = 0xb4de4d82; + strncpy(constantDef1.name, "dynamicFolia", std::extent_v); + constantDef1.literal.x = 0.0f; + constantDef1.literal.y = 1.0f; + constantDef1.literal.z = 0.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.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 = 1; + stateBits1.loadBits.structured.colorWriteAlpha = 1; + 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.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.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_INVDESTALPHA; + 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 = 0; + 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_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.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(T5): Can dump material", "[t5][material][assetdumper]") + { + std::string expected(R"MATERIAL( +{ + "_game": "t5", + "_type": "material", + "_version": 1, + "cameraRegion": "lit", + "constants": [ + { + "literal": [ + 1.0, + 1.0, + 1.0, + 0.5 + ], + "name": "envMapParms" + }, + { + "literal": [ + 0.0, + 1.0, + 0.0, + 1.0 + ], + "nameFragment": "dynamicFolia", + "nameHash": 3034467714 + } + ], + "gameFlags": [ + "10", + "CASTS_SHADOW" + ], + "layeredSurfaceTypes": 536870914, + "maxStreamedMips": 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": 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": 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": "none", + "depthTest": "less_equal", + "depthWrite": true, + "dstBlendAlpha": "zero", + "dstBlendRgb": "zero", + "polygonOffset": "offset0", + "polymodeLine": false, + "srcBlendAlpha": "one", + "srcBlendRgb": "one" + }, + { + "alphaTest": "disabled", + "blendOpAlpha": "disabled", + "blendOpRgb": "add", + "colorWriteAlpha": true, + "colorWriteRgb": true, + "cullFace": "back", + "depthTest": "less_equal", + "depthWrite": false, + "dstBlendAlpha": "zero", + "dstBlendRgb": "one", + "polygonOffset": "offset0", + "polymodeLine": false, + "srcBlendAlpha": "one", + "srcBlendRgb": "invdestalpha" + }, + { + "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" + } + ], + "stateBitsEntry": [ + 0, + 1, + 2, + 3, + 1, + -1, + -1, + -1, + -1, + -1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + 1, + -1, + 1, + -1, + 1, + -1, + 1, + -1, + 1, + -1, + 1, + -1, + 1, + -1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + 4, + 4, + 4, + 4, + 4, + 4, + -1, + -1, + -1, + -1, + 5, + -1, + 1, + 1, + -1 + ], + "stateFlags": 121, + "surfaceTypeBits": 2, + "techniqueSet": "mc_l_sm_r0c0n0s0", + "textureAtlas": { + "columns": 1, + "rows": 1 + }, + "textures": [ + { + "image": "~$white-rgb&~-rjun_art_brick_~a05e7627", + "isMatureContent": false, + "name": "specularMap", + "samplerState": { + "clampU": false, + "clampV": false, + "clampW": false, + "filter": "aniso2x", + "mipMap": "nearest" + }, + "semantic": "specularMap" + }, + { + "image": "jun_art_brick_foundation_n", + "isMatureContent": false, + "name": "normalMap", + "samplerState": { + "clampU": false, + "clampV": false, + "clampW": false, + "filter": "aniso2x", + "mipMap": "nearest" + }, + "semantic": "normalMap" + }, + { + "image": "~-gjun_art_brick_foundation_c", + "isMatureContent": false, + "name": "colorMap", + "samplerState": { + "clampU": false, + "clampV": false, + "clampW": false, + "filter": "aniso2x", + "mipMap": "nearest" + }, + "semantic": "colorMap" + } + ] +})MATERIAL"); + + Zone zone("MockZone", 0, IGame::GetGameById(GameId::T5)); + + 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 From c53b17a3dbb3f08164b1e889917eb1ef2080dec7 Mon Sep 17 00:00:00 2001 From: Jan Laupetin Date: Sun, 6 Jul 2025 16:52:37 +0200 Subject: [PATCH 08/13] feat: add json schema url to dumped materials --- src/ObjWriting/Material/JsonMaterialWriter.cpp.template | 1 + test/ObjWritingTests/Game/IW3/Material/DumperMaterialIW3Test.cpp | 1 + test/ObjWritingTests/Game/IW4/Material/DumperMaterialIW4Test.cpp | 1 + test/ObjWritingTests/Game/IW5/Material/DumperMaterialIW5Test.cpp | 1 + test/ObjWritingTests/Game/T5/Material/DumperMaterialT5Test.cpp | 1 + test/ObjWritingTests/Game/T6/Material/DumperMaterialT6Test.cpp | 1 + 6 files changed, 6 insertions(+) diff --git a/src/ObjWriting/Material/JsonMaterialWriter.cpp.template b/src/ObjWriting/Material/JsonMaterialWriter.cpp.template index 6a0a2639..56434fe3 100644 --- a/src/ObjWriting/Material/JsonMaterialWriter.cpp.template +++ b/src/ObjWriting/Material/JsonMaterialWriter.cpp.template @@ -64,6 +64,7 @@ namespace CreateJsonMaterial(jsonMaterial, material); json jRoot = jsonMaterial; + jRoot["$schema"] = "https://openassettools.dev/schema/material.v1.json"; jRoot["_type"] = "material"; jRoot["_version"] = 1; jRoot["_game"] = GAME_LOWER; diff --git a/test/ObjWritingTests/Game/IW3/Material/DumperMaterialIW3Test.cpp b/test/ObjWritingTests/Game/IW3/Material/DumperMaterialIW3Test.cpp index 42871ad2..cc9d34c1 100644 --- a/test/ObjWritingTests/Game/IW3/Material/DumperMaterialIW3Test.cpp +++ b/test/ObjWritingTests/Game/IW3/Material/DumperMaterialIW3Test.cpp @@ -317,6 +317,7 @@ namespace { std::string expected(R"MATERIAL( { + "$schema": "https://openassettools.dev/schema/material.v1.json", "_game": "iw3", "_type": "material", "_version": 1, diff --git a/test/ObjWritingTests/Game/IW4/Material/DumperMaterialIW4Test.cpp b/test/ObjWritingTests/Game/IW4/Material/DumperMaterialIW4Test.cpp index 7f44b08e..ca7e7881 100644 --- a/test/ObjWritingTests/Game/IW4/Material/DumperMaterialIW4Test.cpp +++ b/test/ObjWritingTests/Game/IW4/Material/DumperMaterialIW4Test.cpp @@ -295,6 +295,7 @@ namespace { std::string expected(R"MATERIAL( { + "$schema": "https://openassettools.dev/schema/material.v1.json", "_game": "iw4", "_type": "material", "_version": 1, diff --git a/test/ObjWritingTests/Game/IW5/Material/DumperMaterialIW5Test.cpp b/test/ObjWritingTests/Game/IW5/Material/DumperMaterialIW5Test.cpp index 71b289d3..e2f0ec73 100644 --- a/test/ObjWritingTests/Game/IW5/Material/DumperMaterialIW5Test.cpp +++ b/test/ObjWritingTests/Game/IW5/Material/DumperMaterialIW5Test.cpp @@ -324,6 +324,7 @@ namespace { std::string expected(R"MATERIAL( { + "$schema": "https://openassettools.dev/schema/material.v1.json", "_game": "iw5", "_type": "material", "_version": 1, diff --git a/test/ObjWritingTests/Game/T5/Material/DumperMaterialT5Test.cpp b/test/ObjWritingTests/Game/T5/Material/DumperMaterialT5Test.cpp index 2e6d1999..e69b73bc 100644 --- a/test/ObjWritingTests/Game/T5/Material/DumperMaterialT5Test.cpp +++ b/test/ObjWritingTests/Game/T5/Material/DumperMaterialT5Test.cpp @@ -297,6 +297,7 @@ namespace { std::string expected(R"MATERIAL( { + "$schema": "https://openassettools.dev/schema/material.v1.json", "_game": "t5", "_type": "material", "_version": 1, diff --git a/test/ObjWritingTests/Game/T6/Material/DumperMaterialT6Test.cpp b/test/ObjWritingTests/Game/T6/Material/DumperMaterialT6Test.cpp index a8704d6d..7ad4f077 100644 --- a/test/ObjWritingTests/Game/T6/Material/DumperMaterialT6Test.cpp +++ b/test/ObjWritingTests/Game/T6/Material/DumperMaterialT6Test.cpp @@ -261,6 +261,7 @@ namespace { std::string expected(R"MATERIAL( { + "$schema": "https://openassettools.dev/schema/material.v1.json", "_game": "t6", "_type": "material", "_version": 1, From 30c6735ff5708fe8c6a9cd0d642560e27b2f3e19 Mon Sep 17 00:00:00 2001 From: Jan Laupetin Date: Mon, 7 Jul 2025 18:13:47 +0200 Subject: [PATCH 09/13] chore: use std array for json constants --- src/ObjCommon/Material/JsonMaterial.h.template | 2 +- src/ObjWriting/Material/JsonMaterialWriter.cpp.template | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ObjCommon/Material/JsonMaterial.h.template b/src/ObjCommon/Material/JsonMaterial.h.template index 1ff77dcb..de0559c7 100644 --- a/src/ObjCommon/Material/JsonMaterial.h.template +++ b/src/ObjCommon/Material/JsonMaterial.h.template @@ -218,7 +218,7 @@ namespace GAME std::optional name; std::optional nameFragment; std::optional nameHash; - std::vector literal; + std::array literal; }; inline void to_json(nlohmann::json& out, const JsonConstant& in) diff --git a/src/ObjWriting/Material/JsonMaterialWriter.cpp.template b/src/ObjWriting/Material/JsonMaterialWriter.cpp.template index 56434fe3..3692970b 100644 --- a/src/ObjWriting/Material/JsonMaterialWriter.cpp.template +++ b/src/ObjWriting/Material/JsonMaterialWriter.cpp.template @@ -201,12 +201,12 @@ namespace jConstantDef.nameFragment = nameFragment; } - jConstantDef.literal = std::vector({ + jConstantDef.literal = { 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) From 90cc7dacbb6ed97440ce6783907ba19035879ec9 Mon Sep 17 00:00:00 2001 From: Jan Laupetin Date: Mon, 7 Jul 2025 20:16:59 +0200 Subject: [PATCH 10/13] chore: use http schema url --- src/ObjCommon/Material/JsonMaterial.h.template | 6 +++--- src/ObjWriting/Material/JsonMaterialWriter.cpp.template | 2 +- .../Game/IW3/Material/DumperMaterialIW3Test.cpp | 2 +- .../Game/IW4/Material/DumperMaterialIW4Test.cpp | 2 +- .../Game/IW5/Material/DumperMaterialIW5Test.cpp | 2 +- .../Game/T5/Material/DumperMaterialT5Test.cpp | 2 +- .../Game/T6/Material/DumperMaterialT6Test.cpp | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/ObjCommon/Material/JsonMaterial.h.template b/src/ObjCommon/Material/JsonMaterial.h.template index de0559c7..091fec76 100644 --- a/src/ObjCommon/Material/JsonMaterial.h.template +++ b/src/ObjCommon/Material/JsonMaterial.h.template @@ -325,9 +325,9 @@ namespace GAME #endif NLOHMANN_JSON_SERIALIZE_ENUM(TextureSemantic, { - {TS_2D, "2D" }, - {TS_FUNCTION, "function" }, - {TS_COLOR_MAP, "colorMap" }, + {TS_2D, "2D" }, + {TS_FUNCTION, "function" }, + {TS_COLOR_MAP, "colorMap" }, #if defined(FEATURE_IW3) || defined(FEATURE_T5) || defined(FEATURE_T6) {TS_UNUSED_1, "unused1" }, #else diff --git a/src/ObjWriting/Material/JsonMaterialWriter.cpp.template b/src/ObjWriting/Material/JsonMaterialWriter.cpp.template index 3692970b..918402fd 100644 --- a/src/ObjWriting/Material/JsonMaterialWriter.cpp.template +++ b/src/ObjWriting/Material/JsonMaterialWriter.cpp.template @@ -64,7 +64,7 @@ namespace CreateJsonMaterial(jsonMaterial, material); json jRoot = jsonMaterial; - jRoot["$schema"] = "https://openassettools.dev/schema/material.v1.json"; + jRoot["$schema"] = "http://openassettools.dev/schema/material.v1.json"; jRoot["_type"] = "material"; jRoot["_version"] = 1; jRoot["_game"] = GAME_LOWER; diff --git a/test/ObjWritingTests/Game/IW3/Material/DumperMaterialIW3Test.cpp b/test/ObjWritingTests/Game/IW3/Material/DumperMaterialIW3Test.cpp index cc9d34c1..077cbe13 100644 --- a/test/ObjWritingTests/Game/IW3/Material/DumperMaterialIW3Test.cpp +++ b/test/ObjWritingTests/Game/IW3/Material/DumperMaterialIW3Test.cpp @@ -317,7 +317,7 @@ namespace { std::string expected(R"MATERIAL( { - "$schema": "https://openassettools.dev/schema/material.v1.json", + "$schema": "http://openassettools.dev/schema/material.v1.json", "_game": "iw3", "_type": "material", "_version": 1, diff --git a/test/ObjWritingTests/Game/IW4/Material/DumperMaterialIW4Test.cpp b/test/ObjWritingTests/Game/IW4/Material/DumperMaterialIW4Test.cpp index ca7e7881..39b264a7 100644 --- a/test/ObjWritingTests/Game/IW4/Material/DumperMaterialIW4Test.cpp +++ b/test/ObjWritingTests/Game/IW4/Material/DumperMaterialIW4Test.cpp @@ -295,7 +295,7 @@ namespace { std::string expected(R"MATERIAL( { - "$schema": "https://openassettools.dev/schema/material.v1.json", + "$schema": "http://openassettools.dev/schema/material.v1.json", "_game": "iw4", "_type": "material", "_version": 1, diff --git a/test/ObjWritingTests/Game/IW5/Material/DumperMaterialIW5Test.cpp b/test/ObjWritingTests/Game/IW5/Material/DumperMaterialIW5Test.cpp index e2f0ec73..f8c15582 100644 --- a/test/ObjWritingTests/Game/IW5/Material/DumperMaterialIW5Test.cpp +++ b/test/ObjWritingTests/Game/IW5/Material/DumperMaterialIW5Test.cpp @@ -324,7 +324,7 @@ namespace { std::string expected(R"MATERIAL( { - "$schema": "https://openassettools.dev/schema/material.v1.json", + "$schema": "http://openassettools.dev/schema/material.v1.json", "_game": "iw5", "_type": "material", "_version": 1, diff --git a/test/ObjWritingTests/Game/T5/Material/DumperMaterialT5Test.cpp b/test/ObjWritingTests/Game/T5/Material/DumperMaterialT5Test.cpp index e69b73bc..b6136ebc 100644 --- a/test/ObjWritingTests/Game/T5/Material/DumperMaterialT5Test.cpp +++ b/test/ObjWritingTests/Game/T5/Material/DumperMaterialT5Test.cpp @@ -297,7 +297,7 @@ namespace { std::string expected(R"MATERIAL( { - "$schema": "https://openassettools.dev/schema/material.v1.json", + "$schema": "http://openassettools.dev/schema/material.v1.json", "_game": "t5", "_type": "material", "_version": 1, diff --git a/test/ObjWritingTests/Game/T6/Material/DumperMaterialT6Test.cpp b/test/ObjWritingTests/Game/T6/Material/DumperMaterialT6Test.cpp index 7ad4f077..c04e3458 100644 --- a/test/ObjWritingTests/Game/T6/Material/DumperMaterialT6Test.cpp +++ b/test/ObjWritingTests/Game/T6/Material/DumperMaterialT6Test.cpp @@ -261,7 +261,7 @@ namespace { std::string expected(R"MATERIAL( { - "$schema": "https://openassettools.dev/schema/material.v1.json", + "$schema": "http://openassettools.dev/schema/material.v1.json", "_game": "t6", "_type": "material", "_version": 1, From 790729f5e01f7769858df0d66b9026d84512d38c Mon Sep 17 00:00:00 2001 From: Jan Laupetin Date: Tue, 8 Jul 2025 19:08:46 +0200 Subject: [PATCH 11/13] chore: remove unused t5 material property maxStreamedMips --- src/ObjCommon/Material/JsonMaterial.h.template | 14 ++++++-------- .../Material/JsonMaterialLoader.cpp.template | 4 +--- .../Game/T5/Material/DumperMaterialT5.cpp | 1 + .../Material/JsonMaterialWriter.cpp.template | 4 +--- .../Game/T5/Material/LoaderMaterialT5Test.cpp | 2 -- .../Game/T5/Material/DumperMaterialT5Test.cpp | 2 -- 6 files changed, 9 insertions(+), 18 deletions(-) diff --git a/src/ObjCommon/Material/JsonMaterial.h.template b/src/ObjCommon/Material/JsonMaterial.h.template index 091fec76..f0eecbd0 100644 --- a/src/ObjCommon/Material/JsonMaterial.h.template +++ b/src/ObjCommon/Material/JsonMaterial.h.template @@ -505,15 +505,14 @@ namespace GAME class JsonMaterial { public: -#ifdef FEATURE_T5 - uint8_t maxStreamedMips; - unsigned layeredSurfaceTypes; -#elif defined(FEATURE_T6) +#if defined(FEATURE_T5) || defined(FEATURE_T6) unsigned layeredSurfaceTypes; +#if defined(FEATURE_T6) unsigned surfaceFlags; unsigned contents; uint8_t probeMipBits; std::optional thermalMaterial; +#endif #endif std::vector gameFlags; unsigned sortKey; @@ -530,15 +529,14 @@ namespace GAME NLOHMANN_DEFINE_TYPE_EXTENSION( JsonMaterial, -#ifdef FEATURE_T5 - maxStreamedMips, - layeredSurfaceTypes, -#elif defined(FEATURE_T6) +#if defined(FEATURE_T5) || defined(FEATURE_T6) layeredSurfaceTypes, +#if defined(FEATURE_T6) surfaceFlags, contents, probeMipBits, thermalMaterial, +#endif #endif gameFlags, sortKey, diff --git a/src/ObjLoading/Material/JsonMaterialLoader.cpp.template b/src/ObjLoading/Material/JsonMaterialLoader.cpp.template index 086c7904..d1887d1f 100644 --- a/src/ObjLoading/Material/JsonMaterialLoader.cpp.template +++ b/src/ObjLoading/Material/JsonMaterialLoader.cpp.template @@ -436,9 +436,7 @@ namespace material.stateFlags = static_cast(jMaterial.stateFlags); material.cameraRegion = jMaterial.cameraRegion; -#if defined(FEATURE_T5) - material.maxStreamedMips = jMaterial.maxStreamedMips; -#elif defined(FEATURE_T6) +#if defined(FEATURE_T6) material.probeMipBits = jMaterial.probeMipBits; #endif diff --git a/src/ObjWriting/Game/T5/Material/DumperMaterialT5.cpp b/src/ObjWriting/Game/T5/Material/DumperMaterialT5.cpp index 2d687c0e..dcc6feb8 100644 --- a/src/ObjWriting/Game/T5/Material/DumperMaterialT5.cpp +++ b/src/ObjWriting/Game/T5/Material/DumperMaterialT5.cpp @@ -30,5 +30,6 @@ void AssetDumperMaterial::DumpAsset(AssetDumpingContext& context, XAssetInfoAsset(); assert(material->info.gameFlags < 0x400); + assert(material->maxStreamedMips == 0); DumpMaterialAsJson(*assetFile, *material, context); } diff --git a/src/ObjWriting/Material/JsonMaterialWriter.cpp.template b/src/ObjWriting/Material/JsonMaterialWriter.cpp.template index 918402fd..a29a1cf4 100644 --- a/src/ObjWriting/Material/JsonMaterialWriter.cpp.template +++ b/src/ObjWriting/Material/JsonMaterialWriter.cpp.template @@ -328,9 +328,7 @@ namespace jMaterial.stateFlags = material.stateFlags; jMaterial.cameraRegion = static_cast(material.cameraRegion); -#if defined(FEATURE_T5) - jMaterial.maxStreamedMips = material.maxStreamedMips; -#elif defined(FEATURE_T6) +#if defined(FEATURE_T6) jMaterial.probeMipBits = material.probeMipBits; #endif diff --git a/test/ObjLoadingTests/Game/T5/Material/LoaderMaterialT5Test.cpp b/test/ObjLoadingTests/Game/T5/Material/LoaderMaterialT5Test.cpp index 6979a94f..90f8283d 100644 --- a/test/ObjLoadingTests/Game/T5/Material/LoaderMaterialT5Test.cpp +++ b/test/ObjLoadingTests/Game/T5/Material/LoaderMaterialT5Test.cpp @@ -71,7 +71,6 @@ namespace "CASTS_SHADOW" ], "layeredSurfaceTypes": 536870914, - "maxStreamedMips": 0, "sortKey": 4, "stateBits": [ { @@ -395,7 +394,6 @@ namespace REQUIRE(material->stateFlags == 121); REQUIRE(material->cameraRegion == CAMERA_REGION_LIT); - REQUIRE(material->maxStreamedMips == 0); REQUIRE(material->techniqueSet != nullptr); REQUIRE(material->techniqueSet->name != nullptr); diff --git a/test/ObjWritingTests/Game/T5/Material/DumperMaterialT5Test.cpp b/test/ObjWritingTests/Game/T5/Material/DumperMaterialT5Test.cpp index b6136ebc..4cc56013 100644 --- a/test/ObjWritingTests/Game/T5/Material/DumperMaterialT5Test.cpp +++ b/test/ObjWritingTests/Game/T5/Material/DumperMaterialT5Test.cpp @@ -55,7 +55,6 @@ namespace material->cameraRegion = CAMERA_REGION_LIT; material->stateFlags = 121; - material->maxStreamedMips = 0; material->techniqueSet = GivenTechset("mc_l_sm_r0c0n0s0", memory); material->textureCount = 3; @@ -328,7 +327,6 @@ namespace "CASTS_SHADOW" ], "layeredSurfaceTypes": 536870914, - "maxStreamedMips": 0, "sortKey": 4, "stateBits": [ { From 75c7949d3fffb0c4d0004cffc101579c9d5a2beb Mon Sep 17 00:00:00 2001 From: Jan Laupetin Date: Tue, 8 Jul 2025 19:12:26 +0200 Subject: [PATCH 12/13] chore: remove unused t6 material property probeMipBits --- src/ObjCommon/Material/JsonMaterial.h.template | 2 -- src/ObjLoading/Material/JsonMaterialLoader.cpp.template | 3 --- src/ObjWriting/Game/T6/Material/DumperMaterialT6.cpp | 1 + src/ObjWriting/Material/JsonMaterialWriter.cpp.template | 3 --- test/ObjLoadingTests/Game/T6/Material/LoaderMaterialT6Test.cpp | 1 - test/ObjWritingTests/Game/T6/Material/DumperMaterialT6Test.cpp | 1 - 6 files changed, 1 insertion(+), 10 deletions(-) diff --git a/src/ObjCommon/Material/JsonMaterial.h.template b/src/ObjCommon/Material/JsonMaterial.h.template index f0eecbd0..b14bf10f 100644 --- a/src/ObjCommon/Material/JsonMaterial.h.template +++ b/src/ObjCommon/Material/JsonMaterial.h.template @@ -510,7 +510,6 @@ namespace GAME #if defined(FEATURE_T6) unsigned surfaceFlags; unsigned contents; - uint8_t probeMipBits; std::optional thermalMaterial; #endif #endif @@ -534,7 +533,6 @@ namespace GAME #if defined(FEATURE_T6) surfaceFlags, contents, - probeMipBits, thermalMaterial, #endif #endif diff --git a/src/ObjLoading/Material/JsonMaterialLoader.cpp.template b/src/ObjLoading/Material/JsonMaterialLoader.cpp.template index d1887d1f..49250488 100644 --- a/src/ObjLoading/Material/JsonMaterialLoader.cpp.template +++ b/src/ObjLoading/Material/JsonMaterialLoader.cpp.template @@ -436,9 +436,6 @@ namespace material.stateFlags = static_cast(jMaterial.stateFlags); material.cameraRegion = jMaterial.cameraRegion; -#if defined(FEATURE_T6) - material.probeMipBits = jMaterial.probeMipBits; -#endif auto* techniqueSet = m_context.LoadDependency(jMaterial.techniqueSet); if (!techniqueSet) diff --git a/src/ObjWriting/Game/T6/Material/DumperMaterialT6.cpp b/src/ObjWriting/Game/T6/Material/DumperMaterialT6.cpp index 9f193037..56e11e97 100644 --- a/src/ObjWriting/Game/T6/Material/DumperMaterialT6.cpp +++ b/src/ObjWriting/Game/T6/Material/DumperMaterialT6.cpp @@ -31,5 +31,6 @@ void AssetDumperMaterial::DumpAsset(AssetDumpingContext& context, XAssetInfoAsset(); assert(material->info.gameFlags < 0x8000); assert(material->info.hashIndex == 0); + assert(material->probeMipBits == 0); DumpMaterialAsJson(*assetFile, *material, context); } diff --git a/src/ObjWriting/Material/JsonMaterialWriter.cpp.template b/src/ObjWriting/Material/JsonMaterialWriter.cpp.template index a29a1cf4..4cce4829 100644 --- a/src/ObjWriting/Material/JsonMaterialWriter.cpp.template +++ b/src/ObjWriting/Material/JsonMaterialWriter.cpp.template @@ -328,9 +328,6 @@ namespace jMaterial.stateFlags = material.stateFlags; jMaterial.cameraRegion = static_cast(material.cameraRegion); -#if defined(FEATURE_T6) - jMaterial.probeMipBits = material.probeMipBits; -#endif if (material.techniqueSet && material.techniqueSet->name) jMaterial.techniqueSet = AssetName(material.techniqueSet->name); diff --git a/test/ObjLoadingTests/Game/T6/Material/LoaderMaterialT6Test.cpp b/test/ObjLoadingTests/Game/T6/Material/LoaderMaterialT6Test.cpp index 2d797315..454054a6 100644 --- a/test/ObjLoadingTests/Game/T6/Material/LoaderMaterialT6Test.cpp +++ b/test/ObjLoadingTests/Game/T6/Material/LoaderMaterialT6Test.cpp @@ -63,7 +63,6 @@ namespace "CASTS_SHADOW" ], "layeredSurfaceTypes": 536870925, - "probeMipBits": 0, "sortKey": 4, "stateBits": [ { diff --git a/test/ObjWritingTests/Game/T6/Material/DumperMaterialT6Test.cpp b/test/ObjWritingTests/Game/T6/Material/DumperMaterialT6Test.cpp index c04e3458..30c83ec1 100644 --- a/test/ObjWritingTests/Game/T6/Material/DumperMaterialT6Test.cpp +++ b/test/ObjWritingTests/Game/T6/Material/DumperMaterialT6Test.cpp @@ -284,7 +284,6 @@ namespace "CASTS_SHADOW" ], "layeredSurfaceTypes": 536870925, - "probeMipBits": 0, "sortKey": 4, "stateBits": [ { From 2082805557f19b89267955722bfb3c460261b4ec Mon Sep 17 00:00:00 2001 From: Jan Laupetin Date: Tue, 8 Jul 2025 23:07:28 +0200 Subject: [PATCH 13/13] fix: make t5 isMatureContent property bool --- src/Common/Game/T5/T5_Assets.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Common/Game/T5/T5_Assets.h b/src/Common/Game/T5/T5_Assets.h index 865e2100..81fdfd7b 100644 --- a/src/Common/Game/T5/T5_Assets.h +++ b/src/Common/Game/T5/T5_Assets.h @@ -843,7 +843,7 @@ namespace T5 char nameEnd; MaterialTextureDefSamplerState samplerState; unsigned char semantic; // TextureSemantic - char isMatureContent; + bool isMatureContent; char pad[3]; MaterialTextureDefInfo u; };