diff --git a/src/Common/Game/IW4/IW4_Assets.h b/src/Common/Game/IW4/IW4_Assets.h index 9636bdf2..7b497c54 100644 --- a/src/Common/Game/IW4/IW4_Assets.h +++ b/src/Common/Game/IW4/IW4_Assets.h @@ -674,7 +674,7 @@ namespace IW4 water_t* water; }; - enum MaterialTextureFilter + enum TextureFilter { TEXTURE_FILTER_DISABLED = 0x0, TEXTURE_FILTER_NEAREST = 0x1, @@ -685,6 +685,15 @@ namespace IW4 TEXTURE_FILTER_COUNT }; + enum SamplerStateBitsMipMap_e + { + SAMPLER_MIPMAP_ENUM_DISABLED, + SAMPLER_MIPMAP_ENUM_NEAREST, + SAMPLER_MIPMAP_ENUM_LINEAR, + + SAMPLER_MIPMAP_ENUM_COUNT + }; + enum SamplerStateBits_e { SAMPLER_FILTER_SHIFT = 0x0, @@ -710,13 +719,26 @@ namespace IW4 SAMPLER_CLAMP_MASK = 0xE0, }; + struct MaterialTextureDefSamplerState + { + unsigned char filter : 3; + unsigned char mipMap : 2; + unsigned char clampU : 1; + unsigned char clampV : 1; + unsigned char clampW : 1; + }; + +#ifndef __zonecodegenerator + static_assert(sizeof(MaterialTextureDefSamplerState) == 1u); +#endif + struct MaterialTextureDef { unsigned int nameHash; char nameStart; char nameEnd; - unsigned char samplerState; // SamplerStateBits_e - unsigned char semantic; // TextureSemantic + MaterialTextureDefSamplerState samplerState; // SamplerStateBits_e + unsigned char semantic; // TextureSemantic MaterialTextureDefInfo u; }; @@ -724,7 +746,7 @@ namespace IW4 { unsigned int nameHash; char name[12]; - float literal[4]; + vec4_t literal; }; enum GfxBlend : unsigned int @@ -754,19 +776,36 @@ namespace IW4 GFXS_BLENDOP_MASK = 0x7, }; - enum GfxStencilFunc : unsigned int + enum GfxAlphaTest_e { - GFXS_STENCILFUNC_NEVER = 0x0, - GFXS_STENCILFUNC_LESS = 0x1, - GFXS_STENCILFUNC_EQUAL = 0x2, - GFXS_STENCILFUNC_LESSEQUAL = 0x3, - GFXS_STENCILFUNC_GREATER = 0x4, - GFXS_STENCILFUNC_NOTEQUAL = 0x5, - GFXS_STENCILFUNC_GREATEREQUAL = 0x6, - GFXS_STENCILFUNC_ALWAYS = 0x7, + GFXS_ALPHA_TEST_GT_0 = 1, + GFXS_ALPHA_TEST_LT_128 = 2, + GFXS_ALPHA_TEST_GE_128 = 3, - GFXS_STENCILFUNC_COUNT, - GFXS_STENCILFUNC_MASK = 0x7 + GFXS_ALPHA_TEST_COUNT + }; + + enum GfxCullFace_e + { + GFXS_CULL_NONE = 1, + GFXS_CULL_BACK = 2, + GFXS_CULL_FRONT = 3, + }; + + enum GfxDepthTest_e + { + GFXS_DEPTHTEST_ALWAYS = 0, + GFXS_DEPTHTEST_LESS = 1, + GFXS_DEPTHTEST_EQUAL = 2, + GFXS_DEPTHTEST_LESSEQUAL = 3 + }; + + enum GfxPolygonOffset_e + { + GFXS_POLYGON_OFFSET_0 = 0, + GFXS_POLYGON_OFFSET_1 = 1, + GFXS_POLYGON_OFFSET_2 = 2, + GFXS_POLYGON_OFFSET_SHADOWMAP = 3 }; enum GfxStencilOp : unsigned int @@ -784,6 +823,21 @@ namespace IW4 GFXS_STENCILOP_MASK = 0x7 }; + enum GfxStencilFunc : unsigned int + { + GFXS_STENCILFUNC_NEVER = 0x0, + GFXS_STENCILFUNC_LESS = 0x1, + GFXS_STENCILFUNC_EQUAL = 0x2, + GFXS_STENCILFUNC_LESSEQUAL = 0x3, + GFXS_STENCILFUNC_GREATER = 0x4, + GFXS_STENCILFUNC_NOTEQUAL = 0x5, + GFXS_STENCILFUNC_GREATEREQUAL = 0x6, + GFXS_STENCILFUNC_ALWAYS = 0x7, + + GFXS_STENCILFUNC_COUNT, + GFXS_STENCILFUNC_MASK = 0x7 + }; + enum GfxStateBitsEnum : unsigned int { GFXS0_SRCBLEND_RGB_SHIFT = 0x0, @@ -876,9 +930,55 @@ namespace IW4 GFXS1_STENCILOP_FRONTBACK_MASK = 0x1FF1FF00, }; + struct GfxStateBitsLoadBitsStructured + { + // Byte 0 + unsigned int srcBlendRgb : 4; // 0-3 + unsigned int dstBlendRgb : 4; // 4-7 + unsigned int blendOpRgb : 3; // 8-10 + unsigned int alphaTestDisabled : 1; // 11 + unsigned int alphaTest : 2; // 12-13 + unsigned int cullFace : 2; // 14-15 + unsigned int srcBlendAlpha : 4; // 16-19 + unsigned int dstBlendAlpha : 4; // 20-23 + unsigned int blendOpAlpha : 3; // 24-26 + unsigned int colorWriteRgb : 1; // 27 + unsigned int colorWriteAlpha : 1; // 28 + unsigned int unused0 : 1; // 29 + unsigned int gammaWrite : 1; // 30 + unsigned int polymodeLine : 1; // 31 + + // Byte 1 + unsigned int depthWrite : 1; // 0 + unsigned int depthTestDisabled : 1; // 1 + unsigned int depthTest : 2; // 2-3 + unsigned int polygonOffset : 2; // 4-5 + unsigned int stencilFrontEnabled : 1; // 6 + unsigned int stencilBackEnabled : 1; // 7 + unsigned int stencilFrontPass : 3; // 8-10 + unsigned int stencilFrontFail : 3; // 11-13 + unsigned int stencilFrontZFail : 3; // 14-16 + unsigned int stencilFrontFunc : 3; // 17-19 + unsigned int stencilBackPass : 3; // 20-22 + unsigned int stencilBackFail : 3; // 23-25 + unsigned int stencilBackZFail : 3; // 26-28 + unsigned int stencilBackFunc : 3; // 29-31 + }; + + union GfxStateBitsLoadBits + { + unsigned int raw[2]; + GfxStateBitsLoadBitsStructured structured; + }; + +#ifndef __zonecodegenerator + static_assert(sizeof(GfxStateBitsLoadBits) == 8); + static_assert(sizeof(GfxStateBitsLoadBitsStructured) == 8); +#endif + struct GfxStateBits { - unsigned int loadBits[2]; + GfxStateBitsLoadBits loadBits; }; struct infoParm_t diff --git a/src/Common/Game/IW5/IW5_Assets.h b/src/Common/Game/IW5/IW5_Assets.h index e4753a01..bd050bfe 100644 --- a/src/Common/Game/IW5/IW5_Assets.h +++ b/src/Common/Game/IW5/IW5_Assets.h @@ -991,7 +991,7 @@ namespace IW5 unsigned char constantCount; unsigned char stateBitsCount; unsigned char stateFlags; - unsigned char cameraRegion; + unsigned char cameraRegion; // GfxCameraRegionType MaterialTechniqueSet* techniqueSet; MaterialTextureDef* textureTable; MaterialConstantDef* constantTable; diff --git a/src/ObjCommon/Material/JsonMaterial.h.template b/src/ObjCommon/Material/JsonMaterial.h.template index 168b29fd..551161b7 100644 --- a/src/ObjCommon/Material/JsonMaterial.h.template +++ b/src/ObjCommon/Material/JsonMaterial.h.template @@ -1,8 +1,11 @@ -#options GAME (IW5, T6) +#options GAME (IW4, IW5, T6) #filename "Game/" + GAME + "/Material/JsonMaterial" + GAME + ".h" -#if GAME == "IW5" +#if GAME == "IW4" +#define FEATURE_IW4 +#define HAS_WATER +#elif GAME == "IW5" #define FEATURE_IW5 #define HAS_WATER #elif GAME == "T6" @@ -19,6 +22,7 @@ #include GAME_HEADER #include "Json/JsonExtension.h" + #include #include #include @@ -71,7 +75,7 @@ namespace GAME INVALID, DISABLED, GT0, -#ifdef FEATURE_IW5 +#if defined(FEATURE_IW4) || defined(FEATURE_IW5) LT128, #endif GE128 @@ -81,7 +85,7 @@ namespace GAME {JsonAlphaTest::INVALID, nullptr }, {JsonAlphaTest::DISABLED, "disabled"}, {JsonAlphaTest::GT0, "gt0" }, -#ifdef FEATURE_IW5 +#if defined(FEATURE_IW4) || defined(FEATURE_IW5) {JsonAlphaTest::LT128, "lt128" }, #endif {JsonAlphaTest::GE128, "ge128" } @@ -164,7 +168,7 @@ namespace GAME GfxBlendOp blendOpAlpha; bool colorWriteRgb; bool colorWriteAlpha; -#ifdef FEATURE_IW5 +#if defined(FEATURE_IW4) || defined(FEATURE_IW5) bool gammaWrite; #endif bool polymodeLine; @@ -308,7 +312,7 @@ namespace GAME #endif NLOHMANN_JSON_SERIALIZE_ENUM(TextureSemantic, { -#ifdef FEATURE_IW5 +#if defined(FEATURE_IW4) || defined(FEATURE_IW5) {TS_2D, "2D" }, {TS_FUNCTION, "function" }, {TS_COLOR_MAP, "colorMap" }, @@ -321,7 +325,9 @@ namespace GAME {TS_UNUSED_5, "unused5" }, {TS_UNUSED_6, "unused6" }, {TS_WATER_MAP, "waterMap" }, +#ifdef FEATURE_IW5 {TS_DISPLACEMENT_MAP, "displacementMap"}, +#endif #elif defined(FEATURE_T6) {TS_2D, "2D" }, {TS_FUNCTION, "function" }, @@ -421,10 +427,14 @@ namespace GAME uint8_t columns; }; - NLOHMANN_DEFINE_TYPE_EXTENSION(JsonTextureAtlas, rows, columns); + NLOHMANN_DEFINE_TYPE_EXTENSION( + JsonTextureAtlas, + rows, + columns + ); NLOHMANN_JSON_SERIALIZE_ENUM(MaterialGameFlags, { -#ifdef FEATURE_IW5 +#if defined(FEATURE_IW4) || defined(FEATURE_IW5) {MTL_GAMEFLAG_1, "1" }, {MTL_GAMEFLAG_2, "2" }, {MTL_GAMEFLAG_4, "4" }, @@ -433,11 +443,13 @@ namespace GAME {MTL_GAMEFLAG_20, "20" }, {MTL_GAMEFLAG_40, "40" }, {MTL_GAMEFLAG_80, "80" }, +#ifdef FEATURE_IW5 {MTL_GAMEFLAG_100, "100" }, {MTL_GAMEFLAG_200, "200" }, {MTL_GAMEFLAG_400, "400" }, {MTL_GAMEFLAG_800, "800" }, {MTL_GAMEFLAG_1000, "1000"}, +#endif #elif defined(FEATURE_T6) {MTL_GAMEFLAG_1, "1" }, {MTL_GAMEFLAG_2, "2" }, @@ -458,12 +470,14 @@ namespace GAME }); NLOHMANN_JSON_SERIALIZE_ENUM(GfxCameraRegionType, { -#ifdef FEATURE_IW5 +#if defined(FEATURE_IW4) || defined(FEATURE_IW5) {CAMERA_REGION_LIT_OPAQUE, "litOpaque" }, {CAMERA_REGION_LIT_TRANS, "litTrans" }, {CAMERA_REGION_EMISSIVE, "emissive" }, {CAMERA_REGION_DEPTH_HACK, "depthHack" }, +#ifdef FEATURE_IW5 {CAMERA_REGION_LIGHT_MAP_OPAQUE, "lightMapOpaque"}, +#endif {CAMERA_REGION_NONE, "none" }, #elif defined(FEATURE_T6) {CAMERA_REGION_LIT_OPAQUE, "litOpaque" }, diff --git a/src/ObjLoading/Game/IW4/Material/CompilingMaterialLoad.cpp b/src/ObjLoading/Game/IW4/Material/CompilingMaterialLoad.cpp new file mode 100644 index 00000000..17d8dae1 --- /dev/null +++ b/src/ObjLoading/Game/IW4/Material/CompilingMaterialLoad.cpp @@ -0,0 +1,1383 @@ +#include "CompilingMaterialLoad.h" + +#include "Game/IW4/CommonIW4.h" +#include "Game/IW4/IW4.h" +#include "Game/IW4/MaterialConstantsIW4.h" +#include "Game/IW4/ObjConstantsIW4.h" +#include "Game/IW4/Techset/LoaderTechsetIW4.h" +#include "Game/IW4/TechsetConstantsIW4.h" +#include "Gdt/AbstractGdtEntryReader.h" +#include "Gdt/IGdtQueryable.h" +#include "ObjLoading.h" +#include "Pool/GlobalAssetPool.h" +#include "StateMap/StateMapFromTechniqueExtractor.h" +#include "StateMap/StateMapHandler.h" +#include "Techset/TechniqueFileReader.h" +#include "Techset/TechniqueStateMapCache.h" +#include "Techset/TechsetDefinitionCache.h" + +#include +#include +#include +#include +#include + +using namespace IW4; + +namespace +{ + class SkipMaterialException final : public std::exception + { + }; + + class MaterialGdtLoader : AbstractGdtEntryReader + { + public: + MaterialGdtLoader(const GdtEntry& entry, + Material& material, + MemoryManager& memory, + ISearchPath& searchPath, + AssetCreationContext& context, + AssetRegistration& registration) + : AbstractGdtEntryReader(entry), + m_material(material), + m_memory(memory), + m_search_path(searchPath), + m_context(context), + m_registration(registration), + m_state_map_cache(context.GetZoneAssetCreationState()), + m_base_state_bits{}, + m_techset_creator(CreateTechsetLoader(memory, searchPath)) + { + } + + bool Load() + { + material_template(); + + FinalizeMaterial(); + + return true; + } + + private: + void material_template() + { + const auto materialType = ReadStringProperty("materialType"); + + if (materialType == GDT_MATERIAL_TYPE_MODEL_PHONG || materialType == GDT_MATERIAL_TYPE_WORLD_PHONG || materialType == GDT_MATERIAL_TYPE_IMPACT_MARK) + { + mtl_phong_template(); + } + else if (materialType == GDT_MATERIAL_TYPE_MODEL_AMBIENT) + { + mtl_ambient_template(); + } + else if (materialType == GDT_MATERIAL_TYPE_2D) + { + mtl_2d_template(); + } + else if (materialType == GDT_MATERIAL_TYPE_MODEL_UNLIT || materialType == GDT_MATERIAL_TYPE_WORLD_UNLIT) + { + mtl_unlit_template(); + } + else if (materialType == GDT_MATERIAL_TYPE_UNLIT) + { + mtl_unlit_deprecated_template(); + } + else if (materialType == GDT_MATERIAL_TYPE_EFFECT) + { + mtl_effect_template(); + } + else if (materialType == GDT_MATERIAL_TYPE_DISTORTION) + { + mtl_distortion_template(); + } + else if (materialType == GDT_MATERIAL_TYPE_PARTICLE_CLOUD) + { + mtl_particlecloud_template(); + } + else if (materialType == GDT_MATERIAL_TYPE_TOOLS) + { + mtl_tools_template(); + } + else if (materialType == GDT_MATERIAL_TYPE_SKY) + { + mtl_sky_template(); + } + else if (materialType == GDT_MATERIAL_TYPE_WATER) + { + mtl_water_template(); + } + else if (materialType == GDT_MATERIAL_TYPE_OBJECTIVE) + { + mtl_objective_template(); + } + else if (materialType == GDT_MATERIAL_TYPE_CUSTOM) + { + custom_template(); + } + else + throw GdtReadingException(std::format("Unknown material type: \"{}\"", materialType)); + } + + void mtl_phong_template() + { + // TODO + throw SkipMaterialException(); + } + + void mtl_ambient_template() + { + // TODO + throw SkipMaterialException(); + } + + void mtl_2d_template() + { + commonsetup_template(); + + SetTechniqueSet("2d"); + + const auto colorMapName = ReadStringProperty("colorMap"); + const auto tileColor = ReadEnumProperty("tileColor", GdtTileModeNames, std::extent_v); + const auto filterColor = ReadEnumProperty("filterColor", GdtSamplerFilterNames, std::extent_v); + + if (!colorMapName.empty()) + AddMapTexture("colorMap", tileColor, filterColor, TS_2D, colorMapName); + else + throw GdtReadingException("ColorMap may not be blank in 2d materials"); + } + + void mtl_unlit_template() + { + // TODO + throw SkipMaterialException(); + } + + void mtl_unlit_deprecated_template() + { + // TODO + throw SkipMaterialException(); + } + + void mtl_effect_template() + { + // TODO + throw SkipMaterialException(); + + commonsetup_template(); + unitlitcommon_template(); + } + + void mtl_distortion_template() + { + commonsetup_template(); + + const auto uvAnim = ReadBoolProperty("uvAnim"); + if (uvAnim) + SetTechniqueSet("distortion_scale_ua_zfeather"); + else + SetTechniqueSet("distortion_scale_zfeather"); + + const auto colorMapName = ReadStringProperty("colorMap"); + const auto tileColor = ReadEnumProperty("tileColor", GdtTileModeNames, std::extent_v); + const auto filterColor = ReadEnumProperty("filterColor", GdtSamplerFilterNames, std::extent_v); + + if (!colorMapName.empty()) + AddMapTexture("colorMap", tileColor, filterColor, TS_COLOR_MAP, colorMapName); + else + throw GdtReadingException("ColorMap may not be blank in tools materials"); + + const auto distortionScaleX = ReadFloatProperty("distortionScaleX"); + const auto distortionScaleY = ReadFloatProperty("distortionScaleY"); + AddConstant("distortionScale", {distortionScaleX, distortionScaleY, 0, 0}); + + if (uvAnim) + { + const auto uvScrollX = ReadFloatProperty("uvScrollX"); + const auto uvScrollY = ReadFloatProperty("uvScrollY"); + const auto uvScrollRotate = ReadFloatProperty("uvScrollRotate"); + AddConstant("uvAnimParms", {uvScrollX, uvScrollY, uvScrollRotate, 0}); + } + } + + void mtl_particlecloud_template() + { + refblend_template(); + sort_template(); + clamp_template(); + + SetTextureAtlas(1, 1); + // tessSize(0) + + // hasEditorMaterial + // allocLightmap + + statebits_template(); + + std::string outdoorSuffix; + const auto outdoorOnly = ReadBoolProperty("outdoorOnly"); + if (outdoorOnly) + outdoorSuffix = "_outdoor"; + + std::string addSuffix; + const auto blendFunc = ReadStringProperty("blendFunc"); + if (blendFunc == GDT_BLEND_FUNC_ADD || blendFunc == GDT_BLEND_FUNC_SCREEN_ADD) + addSuffix = "_add"; + + std::string spotSuffix; + const auto useSpotLight = ReadBoolProperty("useSpotLight"); + if (useSpotLight) + spotSuffix = "_spot"; + + if (outdoorOnly && useSpotLight) + throw GdtReadingException("Outdoor and spot aren't supported on particle cloud materials"); + + std::ostringstream ss; + ss << "particle_cloud" << outdoorSuffix << addSuffix << spotSuffix; + SetTechniqueSet(ss.str()); + + const auto colorMapName = ReadStringProperty("colorMap"); + const auto tileColor = ReadEnumProperty("tileColor", GdtTileModeNames, std::extent_v); + const auto filterColor = ReadEnumProperty("filterColor", GdtSamplerFilterNames, std::extent_v); + + if (!colorMapName.empty()) + AddMapTexture("colorMap", tileColor, filterColor, TS_COLOR_MAP, colorMapName); + else + throw GdtReadingException("ColorMap may not be blank in particle cloud materials"); + + std::cout << std::format("Using particlecloud for \"{}\"\n", m_material.info.name); + } + + void mtl_tools_template() + { + commonsetup_template(); + + SetTechniqueSet("tools"); + + AddMapTexture("normalMap", TileMode_e::NO_TILE, GdtFilter_e::NOMIP_NEAREST, TS_NORMAL_MAP, "$identitynormalmap"); + + const auto colorMapName = ReadStringProperty("colorMap"); + const auto tileColor = ReadEnumProperty("tileColor", GdtTileModeNames, std::extent_v); + const auto filterColor = ReadEnumProperty("filterColor", GdtSamplerFilterNames, std::extent_v); + + if (!colorMapName.empty()) + AddMapTexture("colorMap", tileColor, filterColor, TS_COLOR_MAP, colorMapName); + else + throw GdtReadingException("ColorMap may not be blank in tools materials"); + + const auto colorTint = ReadVec4Property("colorTint", {1.0f, 1.0f, 1.0f, 1.0f}); + AddConstant("colorTint", colorTint); + } + + void mtl_sky_template() + { + // TODO + throw SkipMaterialException(); + } + + void mtl_water_template() + { + // TODO + throw SkipMaterialException(); + } + + void mtl_objective_template() + { + // TODO + throw SkipMaterialException(); + } + + void custom_template() + { + const auto customTemplate = ReadStringProperty("customTemplate"); + + if (customTemplate == GDT_CUSTOM_MATERIAL_TYPE_CUSTOM) + { + mtl_custom_template(); + } + else if (customTemplate == GDT_CUSTOM_MATERIAL_TYPE_PHONG_FLAG) + { + mtl_phong_flag_template(); + } + else if (customTemplate == GDT_CUSTOM_MATERIAL_TYPE_GRAIN_OVERLAY) + { + mtl_grain_overlay_template(); + } + else if (customTemplate == GDT_CUSTOM_MATERIAL_TYPE_EFFECT_EYE_OFFSET) + { + mtl_effect_eyeoffset_template(); + } + else if (customTemplate == GDT_CUSTOM_MATERIAL_TYPE_REFLEXSIGHT) + { + mtl_reflexsight_template(); + } + else if (customTemplate == GDT_CUSTOM_MATERIAL_TYPE_SHADOWCLEAR) + { + mtl_shadowclear_template(); + } + else if (customTemplate == GDT_CUSTOM_MATERIAL_TYPE_SHADOWOVERLAY) + { + mtl_shadowoverlay_template(); + } + else if (customTemplate == GDT_CUSTOM_MATERIAL_TYPE_SPLATTER) + { + mtl_splatter_template(); + } + else + throw GdtReadingException(std::format("Unknown custom template: \"{}\"", customTemplate)); + } + + void mtl_custom_template() + { + // TODO + throw SkipMaterialException(); + } + + void mtl_phong_flag_template() + { + // TODO + throw SkipMaterialException(); + } + + void mtl_grain_overlay_template() + { + // TODO + throw SkipMaterialException(); + } + + void mtl_effect_eyeoffset_template() + { + // TODO + throw SkipMaterialException(); + } + + void mtl_reflexsight_template() + { + // TODO + throw SkipMaterialException(); + } + + void mtl_shadowclear_template() + { + // TODO + throw SkipMaterialException(); + } + + void mtl_shadowoverlay_template() + { + // TODO + throw SkipMaterialException(); + } + + void mtl_splatter_template() + { + // TODO + throw SkipMaterialException(); + } + + void unitlitcommon_template() + { + const auto outdoorOnly = ReadBoolProperty("outdoorOnly"); + const auto blendFunc = ReadStringProperty("blendFunc"); + + std::string distFalloffSuffix; + const auto distFalloff = ReadBoolProperty("distFalloff"); + if (distFalloff) + { + const auto hdrPortal = ReadBoolProperty("hdrPortal"); + if (!hdrPortal) + throw GdtReadingException("Cannot have distance falloff active without hdrPortal."); + + if (blendFunc == GDT_BLEND_FUNC_MULTIPLY) + throw GdtReadingException("Distance falloff does not currently support Multiply."); + + if (outdoorOnly) + throw GdtReadingException("Distance falloff does not currently support outdoor-only types."); + + distFalloffSuffix = "_falloff"; + } + + std::string godFalloffSuffix; + const auto falloff = ReadBoolProperty("falloff"); + if (falloff) + { + if (blendFunc == GDT_BLEND_FUNC_MULTIPLY) + throw GdtReadingException("Falloff does not currently support Multiply."); + + if (outdoorOnly) + throw GdtReadingException("Falloff does not currently support outdoor-only types."); + + godFalloffSuffix = "_falloff"; + } + + std::string noFogSuffix; + const auto noFog = ReadBoolProperty("noFog"); + if (noFog) + noFogSuffix = "_nofog"; + + std::string spotSuffix; + const auto useSpotLight = ReadBoolProperty("useSpotLight"); + if (useSpotLight) + spotSuffix = "_spot"; + + std::string eyeOffsetSuffix; + const auto eyeOffsetDepth = ReadFloatProperty("eyeOffsetDepth"); + if (std::fpclassify(eyeOffsetDepth) != FP_ZERO) + eyeOffsetSuffix = "_eyeoffset"; + + const auto materialType = ReadStringProperty("materialType"); + const auto zFeather = ReadBoolProperty("zFeather"); + + if (materialType == GDT_MATERIAL_TYPE_EFFECT && zFeather) + { + if (blendFunc == GDT_BLEND_FUNC_MULTIPLY) + throw GdtReadingException("zFeather does not support multiply."); + + std::string addSuffix; + if (blendFunc == GDT_BLEND_FUNC_ADD || blendFunc == GDT_BLEND_FUNC_SCREEN_ADD) + addSuffix = "_add"; + + if (outdoorOnly) + { + std::ostringstream ss; + ss << "effect_zfeather_outdoor" << addSuffix << noFogSuffix << spotSuffix << eyeOffsetSuffix; + SetTechniqueSet(ss.str()); + } + else + { + std::ostringstream ss; + ss << "effect_zfeather" << distFalloffSuffix << godFalloffSuffix << addSuffix << noFogSuffix << spotSuffix << eyeOffsetSuffix; + SetTechniqueSet(ss.str()); + } + } + else + { + std::string baseTechName = materialType == GDT_MATERIAL_TYPE_EFFECT ? "effect" : "unlit"; + + if (blendFunc == GDT_BLEND_FUNC_MULTIPLY) + { + std::ostringstream ss; + ss << baseTechName << "_multiply" << noFogSuffix << spotSuffix << eyeOffsetSuffix; + SetTechniqueSet(ss.str()); + } + else + { + std::string addSuffix; + if (blendFunc == GDT_BLEND_FUNC_ADD || blendFunc == GDT_BLEND_FUNC_SCREEN_ADD) + addSuffix = "_add"; + + std::ostringstream ss; + ss << baseTechName << distFalloffSuffix << godFalloffSuffix << addSuffix << noFogSuffix << spotSuffix << eyeOffsetSuffix; + SetTechniqueSet(ss.str()); + } + } + + const auto colorMapName = ReadStringProperty("colorMap"); + const auto tileColor = ReadEnumProperty("tileColor", GdtTileModeNames, std::extent_v); + const auto filterColor = ReadEnumProperty("filterColor", GdtSamplerFilterNames, std::extent_v); + + if (!colorMapName.empty()) + AddMapTexture("colorMap", tileColor, filterColor, TS_COLOR_MAP, colorMapName); + else + throw GdtReadingException("ColorMap may not be blank in effect/unlit materials"); + + if (falloff || distFalloff) + { + // TODO + } + + if (zFeather) + { + const auto zFeatherDepth = ReadFloatProperty("zFeatherDepth"); + if (std::fpclassify(zFeatherDepth) == FP_ZERO) + throw GdtReadingException("zFeatherDepth may not be zero"); + AddConstant("featherParms", {1.0f / zFeatherDepth, zFeatherDepth, 0, 0}); + } + + if (std::fpclassify(eyeOffsetDepth) != FP_ZERO) + AddConstant("eyeOffsetParms", {eyeOffsetDepth, 0, 0, 0}); + + const auto colorTint = ReadVec4Property("colorTint", {1.0f, 1.0f, 1.0f, 1.0f}); + AddConstant("colorTint", colorTint); + } + + void commonsetup_template() + { + refblend_template(); + sort_template(); + clamp_template(); + + // tessSize + + textureAtlas_template(); + + // hasEditorMaterial + + // allocLightmap + + statebits_template(); + } + + void refblend_template() + { + const auto blendFunc = ReadStringProperty("blendFunc"); + } + + void sort_template() + { + const auto sort = ReadStringProperty("sort"); + const auto materialType = ReadStringProperty("materialType"); + const auto polygonOffset = ReadStringProperty("polygonOffset"); + const auto blendFunc = ReadStringProperty("blendFunc"); + + std::string sortKey; + if (sort.empty() || sort == GDT_SORTKEY_DEFAULT) + { + if (materialType == GDT_MATERIAL_TYPE_DISTORTION) + sortKey = GDT_SORTKEY_DISTORTION; + else if (polygonOffset == "Static Decal") + sortKey = GDT_SORTKEY_DECAL_STATIC; + else if (polygonOffset == "Weapon Impact") + sortKey = GDT_SORTKEY_DECAL_WEAPON_IMPACT; + else if (materialType == GDT_MATERIAL_TYPE_EFFECT) + sortKey = GDT_SORTKEY_EFFECT_AUTO_SORT; + else if (materialType == GDT_MATERIAL_TYPE_OBJECTIVE || blendFunc == "Blend" || blendFunc == "Add" || blendFunc == "Screen Add") + sortKey = GDT_SORTKEY_BLEND_ADDITIVE; + // else if (blendFunc == "Multiply") // TODO + // sortKey = GDT_SORTKEY_MULTIPLICATIVE; + else if (materialType == GDT_MATERIAL_TYPE_SKY) + sortKey = GDT_SORTKEY_SKY; + else if (materialType == GDT_MATERIAL_TYPE_MODEL_AMBIENT) + sortKey = GDT_SORTKEY_OPAQUE_AMBIENT; + else + sortKey = GDT_SORTKEY_OPAQUE; + } + else + sortKey = sort; + + bool foundSortKey = false; + for (auto sortKeyIndex = 0u; sortKeyIndex < SORTKEY_MAX; sortKeyIndex++) + { + if (SortKeyNames[sortKeyIndex] && sortKey == SortKeyNames[sortKeyIndex]) + { + SetSort(static_cast(sortKeyIndex)); + foundSortKey = true; + break; + } + } + + if (!foundSortKey) + { + char* endPtr; + const auto sortKeyNum = strtoul(sortKey.c_str(), &endPtr, 10); + + if (endPtr != &sortKey[sortKey.size()]) + throw GdtReadingException(std::format("Invalid sort value: \"{}\"", sortKey)); + + SetSort(static_cast(sortKeyNum)); + } + } + + void clamp_template() {} + + void textureAtlas_template() + { + const auto rowCount = ReadIntegerProperty("textureAtlasRowCount", 1); + const auto columnCount = ReadIntegerProperty("textureAtlasColumnCount", 1); + + SetTextureAtlas(static_cast(rowCount), static_cast(columnCount)); + } + + void statebits_template() + { + alphatest_template(); + blendfunc_template(); + colorwrite_template(); + cullface_template(); + depthtest_template(); + depthwrite_template(); + gammawrite_template(); + polygonoffset_template(); + stencil_template(); + } + + void alphatest_template() + { + const auto alphaTest = ReadStringProperty("alphaTest"); + + if (alphaTest == GDT_ALPHA_TEST_ALWAYS) + SetAlphaTest(AlphaTest_e::ALWAYS); + else if (alphaTest == GDT_ALPHA_TEST_GE128) + SetAlphaTest(AlphaTest_e::GE128); + else if (alphaTest == GDT_ALPHA_TEST_GT0) // TODO: This is not available for IW3 + SetAlphaTest(AlphaTest_e::GT0); + else + throw GdtReadingException(std::format("Invalid alphatest value: \"{}\"", alphaTest)); + } + + void blendfunc_template() + { + const auto blendFunc = ReadStringProperty("blendFunc"); + + if (blendFunc == GDT_BLEND_FUNC_ADD) + { + SetBlendFunc(BlendOp_e::ADD, CustomBlendFunc_e::ONE, CustomBlendFunc_e::ONE); + SetSeparateAlphaBlendFunc(BlendOp_e::DISABLE, CustomBlendFunc_e::ONE, CustomBlendFunc_e::ZERO); + } + else if (blendFunc == GDT_BLEND_FUNC_BLEND) + { + SetBlendFunc(BlendOp_e::ADD, CustomBlendFunc_e::SRC_ALPHA, CustomBlendFunc_e::INV_SRC_ALPHA); + SetSeparateAlphaBlendFunc(BlendOp_e::DISABLE, CustomBlendFunc_e::ONE, CustomBlendFunc_e::ZERO); + } + else if (blendFunc == GDT_BLEND_FUNC_MULTIPLY) + { + SetBlendFunc(BlendOp_e::ADD, CustomBlendFunc_e::ZERO, CustomBlendFunc_e::SRC_COLOR); + SetSeparateAlphaBlendFunc(BlendOp_e::DISABLE, CustomBlendFunc_e::ONE, CustomBlendFunc_e::ZERO); + } + else if (blendFunc == GDT_BLEND_FUNC_REPLACE) + { + SetBlendFunc(BlendOp_e::DISABLE, CustomBlendFunc_e::ONE, CustomBlendFunc_e::ZERO); + SetSeparateAlphaBlendFunc(BlendOp_e::DISABLE, CustomBlendFunc_e::ONE, CustomBlendFunc_e::ZERO); + } + else if (blendFunc == GDT_BLEND_FUNC_SCREEN_ADD) + { + SetBlendFunc(BlendOp_e::ADD, CustomBlendFunc_e::INV_DST_COLOR, CustomBlendFunc_e::ONE); + SetSeparateAlphaBlendFunc(BlendOp_e::DISABLE, CustomBlendFunc_e::ONE, CustomBlendFunc_e::ZERO); + } + else if (blendFunc == GDT_BLEND_FUNC_CUSTOM) + { + const auto customBlendOpRgb = ReadEnumProperty("customBlendOpRgb", GdtBlendOpNames, std::extent_v); + const auto srcCustomBlendFunc = + ReadEnumProperty("srcCustomBlendFunc", GdtCustomBlendFuncNames, std::extent_v); + const auto destCustomBlendFunc = + ReadEnumProperty("destCustomBlendFunc", GdtCustomBlendFuncNames, std::extent_v); + const auto customBlendOpAlpha = ReadEnumProperty("customBlendOpAlpha", GdtBlendOpNames, std::extent_v); + const auto srcCustomBlendFuncAlpha = + ReadEnumProperty("srcCustomBlendFuncAlpha", GdtCustomBlendFuncNames, std::extent_v); + const auto destCustomBlendFuncAlpha = + ReadEnumProperty("destCustomBlendFuncAlpha", GdtCustomBlendFuncNames, std::extent_v); + + SetBlendFunc(customBlendOpRgb, srcCustomBlendFunc, destCustomBlendFunc); + SetSeparateAlphaBlendFunc(customBlendOpAlpha, srcCustomBlendFuncAlpha, destCustomBlendFuncAlpha); + } + else + { + throw GdtReadingException(std::format("Invalid blendfunc value: \"{}\"", blendFunc)); + } + } + + void colorwrite_template() + { + const auto colorWriteRed = ReadEnumProperty( + "colorWriteRed", GdtStateBitsEnabledStatusNames, std::extent_v); + const auto colorWriteGreen = ReadEnumProperty( + "colorWriteGreen", GdtStateBitsEnabledStatusNames, std::extent_v); + const auto colorWriteBlue = ReadEnumProperty( + "colorWriteBlue", GdtStateBitsEnabledStatusNames, std::extent_v); + const auto colorWriteAlpha = ReadEnumProperty( + "colorWriteAlpha", GdtStateBitsEnabledStatusNames, std::extent_v); + + SetColorWrite(colorWriteRed, colorWriteGreen, colorWriteBlue, colorWriteAlpha); + } + + void cullface_template() + { + const auto cullFace = ReadEnumProperty("cullFace", GdtCullFaceNames, std::extent_v); + + SetCullFace(cullFace); + } + + void depthtest_template() + { + const auto depthTest = ReadEnumProperty("depthTest", GdtDepthTestNames, std::extent_v); + + SetDepthTest(depthTest); + } + + void depthwrite_template() + { + const auto depthWrite = + ReadEnumProperty("depthWrite", GdtStateBitsOnOffStatusNames, std::extent_v); + const auto blendFunc = ReadStringProperty("blendFunc"); + + if (depthWrite == StateBitsEnabledStatus_e::ENABLED) + SetDepthWrite(true); + else if (depthWrite == StateBitsEnabledStatus_e::DISABLED) + SetDepthWrite(false); + else if (blendFunc == GDT_BLEND_FUNC_ADD) + SetDepthWrite(false); + else if (blendFunc == GDT_BLEND_FUNC_BLEND) + SetDepthWrite(false); + else if (blendFunc == GDT_BLEND_FUNC_MULTIPLY) + SetDepthWrite(false); + else if (blendFunc == GDT_BLEND_FUNC_REPLACE) + SetDepthWrite(true); + else if (blendFunc == GDT_BLEND_FUNC_SCREEN_ADD) + SetDepthWrite(false); + else if (blendFunc == GDT_BLEND_FUNC_CUSTOM) + SetDepthWrite(false); + else + { + throw GdtReadingException(std::format("Invalid depthWrite blendFunc value: \"{}\"", blendFunc)); + } + } + + void gammawrite_template() + { + const auto gammaWrite = + ReadEnumProperty("gammaWrite", GdtStateBitsOnOffStatusNames, std::extent_v); + + if (gammaWrite == StateBitsEnabledStatus_e::UNKNOWN) + { + std::ostringstream ss; + ss << "Invalid gammaWrite blendFunc value: \"\""; + throw GdtReadingException(ss.str()); + } + + SetGammaWrite(gammaWrite == StateBitsEnabledStatus_e::ENABLED); + } + + void polygonoffset_template() + { + const auto polygonOffset = + ReadEnumProperty("polygonOffset", GdtPolygonOffsetNames, std::extent_v); + + SetPolygonOffset(polygonOffset); + } + + void stencil_template() + { + const auto stencilMode = ReadEnumProperty("stencil", GdtStencilModeNames, std::extent_v); + + if (stencilMode == StencilMode_e::DISABLED) + { + DisableStencil(StencilIndex::FRONT); + DisableStencil(StencilIndex::BACK); + } + else + { + if (stencilMode == StencilMode_e::TWO_SIDED) + { + const auto stencilBackFunc = + ReadEnumProperty("stencilFunc2", GdtStencilFuncNames, std::extent_v); + const auto stencilBackOpFail = + ReadEnumProperty("stencilOpFail2", GdtStencilOpNames, std::extent_v); + const auto stencilBackOpZFail = + ReadEnumProperty("stencilOpZFail2", GdtStencilOpNames, std::extent_v); + const auto stencilBackOpPass = + ReadEnumProperty("stencilOpPass2", GdtStencilOpNames, std::extent_v); + + EnableStencil(StencilIndex::BACK, stencilBackFunc, stencilBackOpFail, stencilBackOpZFail, stencilBackOpPass); + } + + const auto stencilFrontFunc = + ReadEnumProperty("stencilFunc1", GdtStencilFuncNames, std::extent_v); + const auto stencilFrontOpFail = ReadEnumProperty("stencilOpFail1", GdtStencilOpNames, std::extent_v); + const auto stencilFrontOpZFail = + ReadEnumProperty("stencilOpZFail1", GdtStencilOpNames, std::extent_v); + const auto stencilFrontOpPass = ReadEnumProperty("stencilOpPass1", GdtStencilOpNames, std::extent_v); + + EnableStencil(StencilIndex::FRONT, stencilFrontFunc, stencilFrontOpFail, stencilFrontOpZFail, stencilFrontOpPass); + } + } + + void SetTechniqueSet(const std::string& techsetName) + { + auto* techset = m_context.LoadDependency(techsetName); + + if (techset == nullptr) + throw GdtReadingException(std::format("Could not load techset: \"{}\"", techsetName)); + + m_registration.AddDependency(techset); + m_material.techniqueSet = techset->Asset(); + + auto& definitionCache = m_context.GetZoneAssetCreationState(); + + bool failure = false; + const auto* techsetDefinition = m_techset_creator->LoadTechsetDefinition(techsetName, m_context, failure); + if (techsetDefinition == nullptr) + { + throw GdtReadingException(std::format("Could not find techset definition for: \"{}\"", techsetName)); + } + + SetTechniqueSetStateBits(techsetDefinition); + SetTechniqueSetCameraRegion(techsetDefinition); + } + + void SetTechniqueSetStateBits(const techset::TechsetDefinition* techsetDefinition) + { + for (auto i = 0; i < TECHNIQUE_COUNT; i++) + { + std::string techniqueName; + if (techsetDefinition->GetTechniqueByIndex(i, techniqueName)) + { + const auto stateBitsForTechnique = GetStateBitsForTechnique(techniqueName); + const auto foundStateBits = std::ranges::find_if(m_state_bits, + [stateBitsForTechnique](const GfxStateBits& s1) + { + return s1.loadBits.raw[0] == stateBitsForTechnique.loadBits.raw[0] + && s1.loadBits.raw[1] == stateBitsForTechnique.loadBits.raw[1]; + }); + + if (foundStateBits != m_state_bits.end()) + { + m_material.stateBitsEntry[i] = static_cast(foundStateBits - m_state_bits.begin()); + } + else + { + m_material.stateBitsEntry[i] = static_cast(m_state_bits.size()); + m_state_bits.push_back(stateBitsForTechnique); + } + } + else + { + m_material.stateBitsEntry[i] = std::numeric_limits::max(); + } + } + } + + GfxStateBits GetStateBitsForTechnique(const std::string& techniqueName) + { + const auto* stateMap = GetStateMapForTechnique(techniqueName); + if (!stateMap) + return m_base_state_bits; + + const auto preCalculatedStateBits = m_state_bits_per_state_map.find(stateMap); + if (preCalculatedStateBits != m_state_bits_per_state_map.end()) + return preCalculatedStateBits->second; + + const auto stateBits = CalculateStateBitsWithStateMap(stateMap); + m_state_bits_per_state_map.emplace(stateMap, stateBits); + + return stateBits; + } + + _NODISCARD const state_map::StateMapDefinition* GetStateMapForTechnique(const std::string& techniqueName) const + { + const auto* preloadedStateMap = m_state_map_cache.GetStateMapForTechnique(techniqueName); + if (preloadedStateMap) + return preloadedStateMap; + + const auto techniqueFileName = GetTechniqueFileName(techniqueName); + const auto file = m_search_path.Open(techniqueFileName); + if (!file.IsOpen()) + return nullptr; + + state_map::StateMapFromTechniqueExtractor extractor; + const techset::TechniqueFileReader reader(*file.m_stream, techniqueFileName, &extractor); + if (!reader.ReadTechniqueDefinition()) + { + m_state_map_cache.SetTechniqueUsesStateMap(techniqueName, nullptr); + return nullptr; + } + + const auto stateMapName = extractor.RetrieveStateMap(); + const auto* loadedStateMap = m_techset_creator->LoadStateMapDefinition(stateMapName, m_context); + m_state_map_cache.SetTechniqueUsesStateMap(techniqueName, loadedStateMap); + + return loadedStateMap; + } + + GfxStateBits CalculateStateBitsWithStateMap(const state_map::StateMapDefinition* stateMap) const + { + const state_map::StateMapHandler stateMapHandler(stateMapLayout, *stateMap); + + GfxStateBits outBits{}; + stateMapHandler.ApplyStateMap(m_base_state_bits.loadBits.raw, outBits.loadBits.raw); + + return outBits; + } + + void SetTechniqueSetCameraRegion(const techset::TechsetDefinition* techsetDefinition) const + { + std::string tempName; + if (techsetDefinition->GetTechniqueByIndex(TECHNIQUE_LIT, tempName)) + { + if (m_material.info.sortKey >= SORTKEY_TRANS_START) + m_material.cameraRegion = CAMERA_REGION_LIT_TRANS; + else + m_material.cameraRegion = CAMERA_REGION_LIT_OPAQUE; + } + else if (techsetDefinition->GetTechniqueByIndex(TECHNIQUE_EMISSIVE, tempName)) + { + m_material.cameraRegion = CAMERA_REGION_EMISSIVE; + } + else + { + m_material.cameraRegion = CAMERA_REGION_NONE; + } + } + + void AddMapTexture(const std::string& typeName, + const TileMode_e tileMode, + const GdtFilter_e filterMode, + const TextureSemantic semantic, + const std::string& textureName) + { + MaterialTextureDef textureDef{}; + textureDef.nameHash = Common::R_HashString(typeName.c_str()); + textureDef.nameStart = typeName[0]; + textureDef.nameEnd = typeName[typeName.size() - 1]; + textureDef.samplerState.mipMap = 0; + textureDef.semantic = static_cast(semantic); + + switch (tileMode) + { + case TileMode_e::TILE_BOTH: + textureDef.samplerState.clampU = 1; + textureDef.samplerState.clampV = 1; + textureDef.samplerState.clampW = 1; + break; + case TileMode_e::TILE_HORIZONTAL: + textureDef.samplerState.clampU = 0; + textureDef.samplerState.clampV = 1; + textureDef.samplerState.clampW = 0; + break; + case TileMode_e::TILE_VERTICAL: + textureDef.samplerState.clampU = 1; + textureDef.samplerState.clampV = 0; + textureDef.samplerState.clampW = 0; + break; + case TileMode_e::UNKNOWN: + case TileMode_e::NO_TILE: + textureDef.samplerState.clampU = 0; + textureDef.samplerState.clampV = 0; + textureDef.samplerState.clampW = 0; + break; + default: + assert(false); + break; + } + + switch (filterMode) + { + case GdtFilter_e::MIP_2X_BILINEAR: + textureDef.samplerState.filter = TEXTURE_FILTER_ANISO2X; + textureDef.samplerState.mipMap = SAMPLER_MIPMAP_ENUM_NEAREST; + break; + case GdtFilter_e::MIP_2X_TRILINEAR: + textureDef.samplerState.filter = TEXTURE_FILTER_ANISO2X; + textureDef.samplerState.mipMap = SAMPLER_MIPMAP_ENUM_LINEAR; + break; + case GdtFilter_e::MIP_4X_BILINEAR: + textureDef.samplerState.filter = TEXTURE_FILTER_ANISO4X; + textureDef.samplerState.mipMap = SAMPLER_MIPMAP_ENUM_NEAREST; + break; + case GdtFilter_e::MIP_4X_TRILINEAR: + textureDef.samplerState.filter = TEXTURE_FILTER_ANISO4X; + textureDef.samplerState.mipMap = SAMPLER_MIPMAP_ENUM_LINEAR; + break; + case GdtFilter_e::NOMIP_NEAREST: + textureDef.samplerState.filter = TEXTURE_FILTER_NEAREST; + textureDef.samplerState.mipMap = SAMPLER_MIPMAP_ENUM_DISABLED; + break; + case GdtFilter_e::NOMIP_BILINEAR: + textureDef.samplerState.filter = TEXTURE_FILTER_LINEAR; + textureDef.samplerState.mipMap = SAMPLER_MIPMAP_ENUM_DISABLED; + break; + default: + assert(false); + break; + } + + auto* image = m_context.LoadDependency(textureName); + + if (image == nullptr) + throw GdtReadingException(std::format("Could not load image: \"{}\"", textureName)); + + m_registration.AddDependency(image); + textureDef.u.image = image->Asset(); + + m_textures.push_back(textureDef); + } + + void AddConstant(const std::string& constantName, const GdtVec4& literalData) + { + MaterialConstantDef constantDef{}; + constantDef.literal.x = literalData.x; + constantDef.literal.y = literalData.y; + constantDef.literal.z = literalData.z; + constantDef.literal.w = literalData.w; + strncpy(constantDef.name, constantName.c_str(), std::extent_v); + constantDef.nameHash = Common::R_HashString(constantName.c_str()); + + m_constants.push_back(constantDef); + } + + void SetSort(const unsigned char sort) const + { + m_material.info.sortKey = sort; + } + + void SetTextureAtlas(const unsigned char rowCount, const unsigned char columnCount) const + { + m_material.info.textureAtlasRowCount = rowCount; + m_material.info.textureAtlasColumnCount = columnCount; + } + + void SetAlphaTest(const AlphaTest_e alphaTest) + { + switch (alphaTest) + { + case AlphaTest_e::ALWAYS: + m_base_state_bits.loadBits.structured.alphaTestDisabled = 1; + break; + + case AlphaTest_e::GT0: + m_base_state_bits.loadBits.structured.alphaTest = GFXS_ALPHA_TEST_GT_0; + break; + + case AlphaTest_e::LT128: + m_base_state_bits.loadBits.structured.alphaTest = GFXS_ALPHA_TEST_LT_128; + break; + + case AlphaTest_e::GE128: + m_base_state_bits.loadBits.structured.alphaTest = GFXS_ALPHA_TEST_GE_128; + break; + + case AlphaTest_e::UNKNOWN: + default: + throw GdtReadingException(std::format("Unknown alphatest value: \"{}\"", static_cast(alphaTest))); + } + } + + void SetBlendFunc(BlendOp_e blendOp, CustomBlendFunc_e srcFunc, CustomBlendFunc_e destFunc) + { + if (blendOp == BlendOp_e::UNKNOWN || srcFunc == CustomBlendFunc_e::UNKNOWN || destFunc == CustomBlendFunc_e::UNKNOWN) + { + std::ostringstream ss; + ss << "Unknown SeparateAlphaBlendFunc values: \"\""; + throw GdtReadingException(ss.str()); + } + + m_base_state_bits.loadBits.structured.blendOpRgb = static_cast(blendOp) - 1; + m_base_state_bits.loadBits.structured.srcBlendRgb = static_cast(srcFunc) - 1; + m_base_state_bits.loadBits.structured.dstBlendRgb = static_cast(destFunc) - 1; + } + + void SetSeparateAlphaBlendFunc(BlendOp_e blendOp, CustomBlendFunc_e srcFunc, CustomBlendFunc_e destFunc) + { + if (blendOp == BlendOp_e::UNKNOWN || srcFunc == CustomBlendFunc_e::UNKNOWN || destFunc == CustomBlendFunc_e::UNKNOWN) + { + std::ostringstream ss; + ss << "Unknown SeparateAlphaBlendFunc values: \"\""; + throw GdtReadingException(ss.str()); + } + + m_base_state_bits.loadBits.structured.blendOpAlpha = static_cast(blendOp) - 1; + m_base_state_bits.loadBits.structured.srcBlendAlpha = static_cast(srcFunc) - 1; + m_base_state_bits.loadBits.structured.dstBlendAlpha = static_cast(destFunc) - 1; + } + + void SetColorWrite(const StateBitsEnabledStatus_e colorWriteRed, + const StateBitsEnabledStatus_e colorWriteGreen, + const StateBitsEnabledStatus_e colorWriteBlue, + const StateBitsEnabledStatus_e colorWriteAlpha) + { + if (colorWriteRed == StateBitsEnabledStatus_e::UNKNOWN || colorWriteGreen == StateBitsEnabledStatus_e::UNKNOWN + || colorWriteBlue == StateBitsEnabledStatus_e::UNKNOWN || colorWriteAlpha == StateBitsEnabledStatus_e::UNKNOWN) + { + std::ostringstream ss; + ss << "Unknown ColorWrite values: \"\""; + throw GdtReadingException(ss.str()); + } + + if (colorWriteRed != colorWriteGreen || colorWriteRed != colorWriteBlue) + { + throw GdtReadingException("Invalid ColorWrite values: values for rgb must match"); + } + + m_base_state_bits.loadBits.structured.colorWriteRgb = 0; + m_base_state_bits.loadBits.structured.colorWriteAlpha = 0; + if (colorWriteRed == StateBitsEnabledStatus_e::ENABLED) + m_base_state_bits.loadBits.structured.colorWriteRgb = 1; + if (colorWriteAlpha == StateBitsEnabledStatus_e::ENABLED) + m_base_state_bits.loadBits.structured.colorWriteAlpha = 1; + } + + void SetCullFace(const CullFace_e cullFace) + { + if (cullFace == CullFace_e::UNKNOWN) + { + std::ostringstream ss; + ss << "Unknown cullFace values: \"\""; + throw GdtReadingException(ss.str()); + } + + if (cullFace == CullFace_e::FRONT) + { + m_base_state_bits.loadBits.structured.cullFace = GFXS_CULL_FRONT; + } + else if (cullFace == CullFace_e::BACK) + { + m_base_state_bits.loadBits.structured.cullFace = GFXS_CULL_BACK; + } + else + { + assert(cullFace == CullFace_e::NONE); + m_base_state_bits.loadBits.structured.cullFace = GFXS_CULL_NONE; + } + } + + void SetDepthTest(const DepthTest_e depthTest) + { + switch (depthTest) + { + case DepthTest_e::LESS_EQUAL: + m_base_state_bits.loadBits.structured.depthTestDisabled = 0; + m_base_state_bits.loadBits.structured.depthTest = GFXS_DEPTHTEST_LESSEQUAL; + break; + + case DepthTest_e::LESS: + m_base_state_bits.loadBits.structured.depthTestDisabled = 0; + m_base_state_bits.loadBits.structured.depthTest = GFXS_DEPTHTEST_LESS; + break; + + case DepthTest_e::EQUAL: + m_base_state_bits.loadBits.structured.depthTestDisabled = 0; + m_base_state_bits.loadBits.structured.depthTest = GFXS_DEPTHTEST_EQUAL; + break; + + case DepthTest_e::ALWAYS: + m_base_state_bits.loadBits.structured.depthTestDisabled = 0; + m_base_state_bits.loadBits.structured.depthTest = GFXS_DEPTHTEST_ALWAYS; + break; + + case DepthTest_e::DISABLE: + m_base_state_bits.loadBits.structured.depthTestDisabled = 1; + m_base_state_bits.loadBits.structured.depthTest = 0; + break; + + case DepthTest_e::UNKNOWN: + default: + std::ostringstream ss; + ss << "Unknown depthTest values: \"\""; + throw GdtReadingException(ss.str()); + } + } + + void SetDepthWrite(const bool depthWrite) + { + if (depthWrite) + m_base_state_bits.loadBits.structured.depthWrite = 1; + else + m_base_state_bits.loadBits.structured.depthWrite = 0; + } + + void SetGammaWrite(const bool gammaWrite) + { + if (gammaWrite) + m_base_state_bits.loadBits.structured.gammaWrite = 1; + else + m_base_state_bits.loadBits.structured.gammaWrite = 0; + } + + void SetPolygonOffset(const PolygonOffset_e polygonOffset) + { + if (polygonOffset == PolygonOffset_e::UNKNOWN) + { + std::ostringstream ss; + ss << "Unknown polygonOffset values: \"\""; + throw GdtReadingException(ss.str()); + } + + m_base_state_bits.loadBits.structured.polygonOffset = static_cast(polygonOffset) - 1; + } + + static void GetStencilMasksForIndex(const StencilIndex stencil, + unsigned& enabledMask, + unsigned& funcShift, + unsigned& funcMask, + unsigned& opFailShift, + unsigned& opFailMask, + unsigned& opZFailShift, + unsigned& opZFailMask, + unsigned& opPassShift, + unsigned& opPassMask) + { + if (stencil == StencilIndex::FRONT) + { + enabledMask = GFXS1_STENCIL_FRONT_ENABLE; + funcShift = GFXS1_STENCIL_FRONT_FUNC_SHIFT; + funcMask = GFXS1_STENCIL_FRONT_FUNC_MASK; + opFailShift = GFXS1_STENCIL_FRONT_FAIL_SHIFT; + opFailMask = GFXS1_STENCIL_FRONT_FAIL_MASK; + opZFailShift = GFXS1_STENCIL_FRONT_ZFAIL_SHIFT; + opZFailMask = GFXS1_STENCIL_FRONT_ZFAIL_MASK; + opPassShift = GFXS1_STENCIL_FRONT_PASS_SHIFT; + opPassMask = GFXS1_STENCIL_FRONT_PASS_MASK; + } + else + { + assert(stencil == StencilIndex::BACK); + + enabledMask = GFXS1_STENCIL_BACK_ENABLE; + funcShift = GFXS1_STENCIL_BACK_FUNC_SHIFT; + funcMask = GFXS1_STENCIL_BACK_FUNC_MASK; + opFailShift = GFXS1_STENCIL_BACK_FAIL_SHIFT; + opFailMask = GFXS1_STENCIL_BACK_FAIL_MASK; + opZFailShift = GFXS1_STENCIL_BACK_ZFAIL_SHIFT; + opZFailMask = GFXS1_STENCIL_BACK_ZFAIL_MASK; + opPassShift = GFXS1_STENCIL_BACK_PASS_SHIFT; + opPassMask = GFXS1_STENCIL_BACK_PASS_MASK; + } + } + + void DisableStencil(const StencilIndex stencil) + { + unsigned enabledMask, funcShift, funcMask, opFailShift, opFailMask, opZFailShift, opZFailMask, opPassShift, opPassMask; + GetStencilMasksForIndex(stencil, enabledMask, funcShift, funcMask, opFailShift, opFailMask, opZFailShift, opZFailMask, opPassShift, opPassMask); + + m_base_state_bits.loadBits.raw[1] &= ~(enabledMask | funcMask | opFailMask | opZFailMask | opPassMask); + } + + void EnableStencil( + const StencilIndex stencil, StencilFunc_e stencilFunc, StencilOp_e stencilOpFail, StencilOp_e stencilOpZFail, StencilOp_e stencilOpPass) + { + unsigned enabledMask, funcShift, funcMask, opFailShift, opFailMask, opZFailShift, opZFailMask, opPassShift, opPassMask; + GetStencilMasksForIndex(stencil, enabledMask, funcShift, funcMask, opFailShift, opFailMask, opZFailShift, opZFailMask, opPassShift, opPassMask); + + m_base_state_bits.loadBits.raw[1] |= enabledMask; + + m_base_state_bits.loadBits.raw[1] &= ~funcMask; + m_base_state_bits.loadBits.raw[1] |= ((static_cast(stencilFunc) - 1) << funcShift) & funcMask; + + m_base_state_bits.loadBits.raw[1] &= ~opFailMask; + m_base_state_bits.loadBits.raw[1] |= ((static_cast(stencilOpFail) - 1) << opFailShift) & opFailMask; + + m_base_state_bits.loadBits.raw[1] &= ~opZFailMask; + m_base_state_bits.loadBits.raw[1] |= ((static_cast(stencilOpZFail) - 1) << opZFailShift) & opZFailMask; + + m_base_state_bits.loadBits.raw[1] &= ~opPassMask; + m_base_state_bits.loadBits.raw[1] |= ((static_cast(stencilOpPass) - 1) << opPassShift) & opPassMask; + } + + void FinalizeMaterial() const + { + if (!m_textures.empty()) + { + m_material.textureTable = m_memory.Alloc(m_textures.size()); + m_material.textureCount = static_cast(m_textures.size()); + std::memcpy(m_material.textureTable, m_textures.data(), sizeof(MaterialTextureDef) * m_textures.size()); + } + else + { + m_material.textureTable = nullptr; + m_material.textureCount = 0u; + } + + if (!m_constants.empty()) + { + m_material.constantTable = m_memory.Alloc(m_constants.size()); + m_material.constantCount = static_cast(m_constants.size()); + std::memcpy(m_material.constantTable, m_constants.data(), sizeof(MaterialConstantDef) * m_constants.size()); + } + else + { + m_material.constantTable = nullptr; + m_material.constantCount = 0u; + } + + if (!m_state_bits.empty()) + { + m_material.stateBitsTable = m_memory.Alloc(m_state_bits.size()); + m_material.stateBitsCount = static_cast(m_state_bits.size()); + std::memcpy(m_material.stateBitsTable, m_state_bits.data(), sizeof(GfxStateBits) * m_state_bits.size()); + } + else + { + m_material.stateBitsTable = nullptr; + m_material.stateBitsCount = 0u; + } + } + + static size_t + GetIndexForString(const std::string& propertyName, const std::string& value, const char** validValuesArray, const size_t validValuesArraySize) + { + for (auto i = 0u; i < validValuesArraySize; i++) + { + if (validValuesArray[i] == value) + return i; + } + + throw GdtReadingException(std::format("Unknown {} value: \"{}\"", propertyName, value)); + } + + template T ReadEnumProperty(const std::string& propertyName, const char** validValuesArray, const size_t validValuesArraySize) const + { + return static_cast(GetIndexForString(propertyName, ReadStringProperty(propertyName), validValuesArray, validValuesArraySize)); + } + + Material& m_material; + MemoryManager& m_memory; + ISearchPath& m_search_path; + AssetCreationContext& m_context; + AssetRegistration& m_registration; + + techset::TechniqueStateMapCache& m_state_map_cache; + std::unordered_map m_state_bits_per_state_map; + + GfxStateBits m_base_state_bits; + std::vector m_state_bits; + std::vector m_textures; + std::vector m_constants; + + std::unique_ptr m_techset_creator; + }; + + class MaterialLoader final : public AssetCreator + { + public: + MaterialLoader(MemoryManager& memory, ISearchPath& searchPath, IGdtQueryable& gdt) + : m_memory(memory), + m_search_path(m_search_path), + m_gdt(gdt) + { + } + + AssetCreationResult CreateAsset(const std::string& assetName, AssetCreationContext& context) override + { + const auto* entry = m_gdt.GetGdtEntryByGdfAndName(ObjConstants::GDF_FILENAME_MATERIAL, assetName); + if (!entry) + return AssetCreationResult::NoAction(); + + auto* material = m_memory.Alloc(); + material->info.name = m_memory.Dup(assetName.c_str()); + + AssetRegistration registration(assetName, material); + + MaterialGdtLoader loader(*entry, *material, m_memory, m_search_path, context, registration); + + try + { + if (loader.Load()) + return AssetCreationResult::Success(context.AddAsset(std::move(registration))); + } + catch (const SkipMaterialException&) + { + return AssetCreationResult::NoAction(); + } + catch (const GdtReadingException& e) + { + std::cerr << std::format("Error while trying to load material from gdt: {} @ GdtEntry \"{}\"\n", e.what(), entry->m_name); + } + + return AssetCreationResult::Failure(); + } + + private: + MemoryManager& m_memory; + ISearchPath& m_search_path; + IGdtQueryable& m_gdt; + }; +} // namespace + +namespace IW4 +{ + std::unique_ptr> CreateCompilingMaterialLoader(MemoryManager& memory, ISearchPath& searchPath, IGdtQueryable& gdt) + { + return std::make_unique(memory, searchPath, gdt); + } +} // namespace IW4 diff --git a/src/ObjLoading/Game/IW4/Material/CompilingMaterialLoad.h b/src/ObjLoading/Game/IW4/Material/CompilingMaterialLoad.h new file mode 100644 index 00000000..938ac9ce --- /dev/null +++ b/src/ObjLoading/Game/IW4/Material/CompilingMaterialLoad.h @@ -0,0 +1,12 @@ +#pragma once + +#include "Asset/IAssetCreator.h" +#include "Game/IW4/IW4.h" +#include "Gdt/IGdtQueryable.h" +#include "SearchPath/ISearchPath.h" +#include "Utils/MemoryManager.h" + +namespace IW4 +{ + std::unique_ptr> CreateCompilingMaterialLoader(MemoryManager& memory, ISearchPath& searchPath, IGdtQueryable& gdt); +} // namespace IW4 diff --git a/src/ObjLoading/Game/IW4/Material/LoaderMaterialIW4.cpp b/src/ObjLoading/Game/IW4/Material/LoaderMaterialIW4.cpp index 51363e80..be3bf8b0 100644 --- a/src/ObjLoading/Game/IW4/Material/LoaderMaterialIW4.cpp +++ b/src/ObjLoading/Game/IW4/Material/LoaderMaterialIW4.cpp @@ -1,1373 +1,54 @@ #include "LoaderMaterialIW4.h" -#include "Game/IW4/CommonIW4.h" #include "Game/IW4/IW4.h" -#include "Game/IW4/MaterialConstantsIW4.h" -#include "Game/IW4/ObjConstantsIW4.h" -#include "Game/IW4/Techset/LoaderTechsetIW4.h" -#include "Game/IW4/TechsetConstantsIW4.h" -#include "Gdt/AbstractGdtEntryReader.h" -#include "ObjLoading.h" -#include "Pool/GlobalAssetPool.h" -#include "StateMap/StateMapFromTechniqueExtractor.h" -#include "StateMap/StateMapHandler.h" -#include "Techset/TechniqueFileReader.h" -#include "Techset/TechniqueStateMapCache.h" -#include "Techset/TechsetDefinitionCache.h" +#include "Game/IW4/Material/JsonMaterialLoaderIW4.h" +#include "Material/MaterialCommon.h" -#include -#include #include #include -#include using namespace IW4; namespace { - class SkipMaterialException final : public std::exception - { - }; - - class MaterialGdtLoader : AbstractGdtEntryReader - { - public: - MaterialGdtLoader(const GdtEntry& entry, - Material& material, - MemoryManager& memory, - ISearchPath& searchPath, - AssetCreationContext& context, - AssetRegistration& registration) - : AbstractGdtEntryReader(entry), - m_material(material), - m_memory(memory), - m_search_path(searchPath), - m_context(context), - m_registration(registration), - m_state_map_cache(context.GetZoneAssetCreationState()), - m_base_state_bits{}, - m_techset_creator(CreateTechsetLoader(memory, searchPath)) - { - } - - bool Load() - { - material_template(); - - FinalizeMaterial(); - - return true; - } - - private: - void material_template() - { - const auto materialType = ReadStringProperty("materialType"); - - if (materialType == GDT_MATERIAL_TYPE_MODEL_PHONG || materialType == GDT_MATERIAL_TYPE_WORLD_PHONG || materialType == GDT_MATERIAL_TYPE_IMPACT_MARK) - { - mtl_phong_template(); - } - else if (materialType == GDT_MATERIAL_TYPE_MODEL_AMBIENT) - { - mtl_ambient_template(); - } - else if (materialType == GDT_MATERIAL_TYPE_2D) - { - mtl_2d_template(); - } - else if (materialType == GDT_MATERIAL_TYPE_MODEL_UNLIT || materialType == GDT_MATERIAL_TYPE_WORLD_UNLIT) - { - mtl_unlit_template(); - } - else if (materialType == GDT_MATERIAL_TYPE_UNLIT) - { - mtl_unlit_deprecated_template(); - } - else if (materialType == GDT_MATERIAL_TYPE_EFFECT) - { - mtl_effect_template(); - } - else if (materialType == GDT_MATERIAL_TYPE_DISTORTION) - { - mtl_distortion_template(); - } - else if (materialType == GDT_MATERIAL_TYPE_PARTICLE_CLOUD) - { - mtl_particlecloud_template(); - } - else if (materialType == GDT_MATERIAL_TYPE_TOOLS) - { - mtl_tools_template(); - } - else if (materialType == GDT_MATERIAL_TYPE_SKY) - { - mtl_sky_template(); - } - else if (materialType == GDT_MATERIAL_TYPE_WATER) - { - mtl_water_template(); - } - else if (materialType == GDT_MATERIAL_TYPE_OBJECTIVE) - { - mtl_objective_template(); - } - else if (materialType == GDT_MATERIAL_TYPE_CUSTOM) - { - custom_template(); - } - else - throw GdtReadingException(std::format("Unknown material type: \"{}\"", materialType)); - } - - void mtl_phong_template() - { - // TODO - throw SkipMaterialException(); - } - - void mtl_ambient_template() - { - // TODO - throw SkipMaterialException(); - } - - void mtl_2d_template() - { - commonsetup_template(); - - SetTechniqueSet("2d"); - - const auto colorMapName = ReadStringProperty("colorMap"); - const auto tileColor = ReadEnumProperty("tileColor", GdtTileModeNames, std::extent_v); - const auto filterColor = ReadEnumProperty("filterColor", GdtSamplerFilterNames, std::extent_v); - - if (!colorMapName.empty()) - AddMapTexture("colorMap", tileColor, filterColor, TS_2D, colorMapName); - else - throw GdtReadingException("ColorMap may not be blank in 2d materials"); - } - - void mtl_unlit_template() - { - // TODO - throw SkipMaterialException(); - } - - void mtl_unlit_deprecated_template() - { - // TODO - throw SkipMaterialException(); - } - - void mtl_effect_template() - { - // TODO - throw SkipMaterialException(); - - commonsetup_template(); - unitlitcommon_template(); - } - - void mtl_distortion_template() - { - commonsetup_template(); - - const auto uvAnim = ReadBoolProperty("uvAnim"); - if (uvAnim) - SetTechniqueSet("distortion_scale_ua_zfeather"); - else - SetTechniqueSet("distortion_scale_zfeather"); - - const auto colorMapName = ReadStringProperty("colorMap"); - const auto tileColor = ReadEnumProperty("tileColor", GdtTileModeNames, std::extent_v); - const auto filterColor = ReadEnumProperty("filterColor", GdtSamplerFilterNames, std::extent_v); - - if (!colorMapName.empty()) - AddMapTexture("colorMap", tileColor, filterColor, TS_COLOR_MAP, colorMapName); - else - throw GdtReadingException("ColorMap may not be blank in tools materials"); - - const auto distortionScaleX = ReadFloatProperty("distortionScaleX"); - const auto distortionScaleY = ReadFloatProperty("distortionScaleY"); - AddConstant("distortionScale", {distortionScaleX, distortionScaleY, 0, 0}); - - if (uvAnim) - { - const auto uvScrollX = ReadFloatProperty("uvScrollX"); - const auto uvScrollY = ReadFloatProperty("uvScrollY"); - const auto uvScrollRotate = ReadFloatProperty("uvScrollRotate"); - AddConstant("uvAnimParms", {uvScrollX, uvScrollY, uvScrollRotate, 0}); - } - } - - void mtl_particlecloud_template() - { - refblend_template(); - sort_template(); - clamp_template(); - - SetTextureAtlas(1, 1); - // tessSize(0) - - // hasEditorMaterial - // allocLightmap - - statebits_template(); - - std::string outdoorSuffix; - const auto outdoorOnly = ReadBoolProperty("outdoorOnly"); - if (outdoorOnly) - outdoorSuffix = "_outdoor"; - - std::string addSuffix; - const auto blendFunc = ReadStringProperty("blendFunc"); - if (blendFunc == GDT_BLEND_FUNC_ADD || blendFunc == GDT_BLEND_FUNC_SCREEN_ADD) - addSuffix = "_add"; - - std::string spotSuffix; - const auto useSpotLight = ReadBoolProperty("useSpotLight"); - if (useSpotLight) - spotSuffix = "_spot"; - - if (outdoorOnly && useSpotLight) - throw GdtReadingException("Outdoor and spot aren't supported on particle cloud materials"); - - std::ostringstream ss; - ss << "particle_cloud" << outdoorSuffix << addSuffix << spotSuffix; - SetTechniqueSet(ss.str()); - - const auto colorMapName = ReadStringProperty("colorMap"); - const auto tileColor = ReadEnumProperty("tileColor", GdtTileModeNames, std::extent_v); - const auto filterColor = ReadEnumProperty("filterColor", GdtSamplerFilterNames, std::extent_v); - - if (!colorMapName.empty()) - AddMapTexture("colorMap", tileColor, filterColor, TS_COLOR_MAP, colorMapName); - else - throw GdtReadingException("ColorMap may not be blank in particle cloud materials"); - - std::cout << std::format("Using particlecloud for \"{}\"\n", m_material.info.name); - } - - void mtl_tools_template() - { - commonsetup_template(); - - SetTechniqueSet("tools"); - - AddMapTexture("normalMap", TileMode_e::NO_TILE, GdtFilter_e::NOMIP_NEAREST, TS_NORMAL_MAP, "$identitynormalmap"); - - const auto colorMapName = ReadStringProperty("colorMap"); - const auto tileColor = ReadEnumProperty("tileColor", GdtTileModeNames, std::extent_v); - const auto filterColor = ReadEnumProperty("filterColor", GdtSamplerFilterNames, std::extent_v); - - if (!colorMapName.empty()) - AddMapTexture("colorMap", tileColor, filterColor, TS_COLOR_MAP, colorMapName); - else - throw GdtReadingException("ColorMap may not be blank in tools materials"); - - const auto colorTint = ReadVec4Property("colorTint", {1.0f, 1.0f, 1.0f, 1.0f}); - AddConstant("colorTint", colorTint); - } - - void mtl_sky_template() - { - // TODO - throw SkipMaterialException(); - } - - void mtl_water_template() - { - // TODO - throw SkipMaterialException(); - } - - void mtl_objective_template() - { - // TODO - throw SkipMaterialException(); - } - - void custom_template() - { - const auto customTemplate = ReadStringProperty("customTemplate"); - - if (customTemplate == GDT_CUSTOM_MATERIAL_TYPE_CUSTOM) - { - mtl_custom_template(); - } - else if (customTemplate == GDT_CUSTOM_MATERIAL_TYPE_PHONG_FLAG) - { - mtl_phong_flag_template(); - } - else if (customTemplate == GDT_CUSTOM_MATERIAL_TYPE_GRAIN_OVERLAY) - { - mtl_grain_overlay_template(); - } - else if (customTemplate == GDT_CUSTOM_MATERIAL_TYPE_EFFECT_EYE_OFFSET) - { - mtl_effect_eyeoffset_template(); - } - else if (customTemplate == GDT_CUSTOM_MATERIAL_TYPE_REFLEXSIGHT) - { - mtl_reflexsight_template(); - } - else if (customTemplate == GDT_CUSTOM_MATERIAL_TYPE_SHADOWCLEAR) - { - mtl_shadowclear_template(); - } - else if (customTemplate == GDT_CUSTOM_MATERIAL_TYPE_SHADOWOVERLAY) - { - mtl_shadowoverlay_template(); - } - else if (customTemplate == GDT_CUSTOM_MATERIAL_TYPE_SPLATTER) - { - mtl_splatter_template(); - } - else - throw GdtReadingException(std::format("Unknown custom template: \"{}\"", customTemplate)); - } - - void mtl_custom_template() - { - // TODO - throw SkipMaterialException(); - } - - void mtl_phong_flag_template() - { - // TODO - throw SkipMaterialException(); - } - - void mtl_grain_overlay_template() - { - // TODO - throw SkipMaterialException(); - } - - void mtl_effect_eyeoffset_template() - { - // TODO - throw SkipMaterialException(); - } - - void mtl_reflexsight_template() - { - // TODO - throw SkipMaterialException(); - } - - void mtl_shadowclear_template() - { - // TODO - throw SkipMaterialException(); - } - - void mtl_shadowoverlay_template() - { - // TODO - throw SkipMaterialException(); - } - - void mtl_splatter_template() - { - // TODO - throw SkipMaterialException(); - } - - void unitlitcommon_template() - { - const auto outdoorOnly = ReadBoolProperty("outdoorOnly"); - const auto blendFunc = ReadStringProperty("blendFunc"); - - std::string distFalloffSuffix; - const auto distFalloff = ReadBoolProperty("distFalloff"); - if (distFalloff) - { - const auto hdrPortal = ReadBoolProperty("hdrPortal"); - if (!hdrPortal) - throw GdtReadingException("Cannot have distance falloff active without hdrPortal."); - - if (blendFunc == GDT_BLEND_FUNC_MULTIPLY) - throw GdtReadingException("Distance falloff does not currently support Multiply."); - - if (outdoorOnly) - throw GdtReadingException("Distance falloff does not currently support outdoor-only types."); - - distFalloffSuffix = "_falloff"; - } - - std::string godFalloffSuffix; - const auto falloff = ReadBoolProperty("falloff"); - if (falloff) - { - if (blendFunc == GDT_BLEND_FUNC_MULTIPLY) - throw GdtReadingException("Falloff does not currently support Multiply."); - - if (outdoorOnly) - throw GdtReadingException("Falloff does not currently support outdoor-only types."); - - godFalloffSuffix = "_falloff"; - } - - std::string noFogSuffix; - const auto noFog = ReadBoolProperty("noFog"); - if (noFog) - noFogSuffix = "_nofog"; - - std::string spotSuffix; - const auto useSpotLight = ReadBoolProperty("useSpotLight"); - if (useSpotLight) - spotSuffix = "_spot"; - - std::string eyeOffsetSuffix; - const auto eyeOffsetDepth = ReadFloatProperty("eyeOffsetDepth"); - if (std::fpclassify(eyeOffsetDepth) != FP_ZERO) - eyeOffsetSuffix = "_eyeoffset"; - - const auto materialType = ReadStringProperty("materialType"); - const auto zFeather = ReadBoolProperty("zFeather"); - - if (materialType == GDT_MATERIAL_TYPE_EFFECT && zFeather) - { - if (blendFunc == GDT_BLEND_FUNC_MULTIPLY) - throw GdtReadingException("zFeather does not support multiply."); - - std::string addSuffix; - if (blendFunc == GDT_BLEND_FUNC_ADD || blendFunc == GDT_BLEND_FUNC_SCREEN_ADD) - addSuffix = "_add"; - - if (outdoorOnly) - { - std::ostringstream ss; - ss << "effect_zfeather_outdoor" << addSuffix << noFogSuffix << spotSuffix << eyeOffsetSuffix; - SetTechniqueSet(ss.str()); - } - else - { - std::ostringstream ss; - ss << "effect_zfeather" << distFalloffSuffix << godFalloffSuffix << addSuffix << noFogSuffix << spotSuffix << eyeOffsetSuffix; - SetTechniqueSet(ss.str()); - } - } - else - { - std::string baseTechName = materialType == GDT_MATERIAL_TYPE_EFFECT ? "effect" : "unlit"; - - if (blendFunc == GDT_BLEND_FUNC_MULTIPLY) - { - std::ostringstream ss; - ss << baseTechName << "_multiply" << noFogSuffix << spotSuffix << eyeOffsetSuffix; - SetTechniqueSet(ss.str()); - } - else - { - std::string addSuffix; - if (blendFunc == GDT_BLEND_FUNC_ADD || blendFunc == GDT_BLEND_FUNC_SCREEN_ADD) - addSuffix = "_add"; - - std::ostringstream ss; - ss << baseTechName << distFalloffSuffix << godFalloffSuffix << addSuffix << noFogSuffix << spotSuffix << eyeOffsetSuffix; - SetTechniqueSet(ss.str()); - } - } - - const auto colorMapName = ReadStringProperty("colorMap"); - const auto tileColor = ReadEnumProperty("tileColor", GdtTileModeNames, std::extent_v); - const auto filterColor = ReadEnumProperty("filterColor", GdtSamplerFilterNames, std::extent_v); - - if (!colorMapName.empty()) - AddMapTexture("colorMap", tileColor, filterColor, TS_COLOR_MAP, colorMapName); - else - throw GdtReadingException("ColorMap may not be blank in effect/unlit materials"); - - if (falloff || distFalloff) - { - // TODO - } - - if (zFeather) - { - const auto zFeatherDepth = ReadFloatProperty("zFeatherDepth"); - if (std::fpclassify(zFeatherDepth) == FP_ZERO) - throw GdtReadingException("zFeatherDepth may not be zero"); - AddConstant("featherParms", {1.0f / zFeatherDepth, zFeatherDepth, 0, 0}); - } - - if (std::fpclassify(eyeOffsetDepth) != FP_ZERO) - AddConstant("eyeOffsetParms", {eyeOffsetDepth, 0, 0, 0}); - - const auto colorTint = ReadVec4Property("colorTint", {1.0f, 1.0f, 1.0f, 1.0f}); - AddConstant("colorTint", colorTint); - } - - void commonsetup_template() - { - refblend_template(); - sort_template(); - clamp_template(); - - // tessSize - - textureAtlas_template(); - - // hasEditorMaterial - - // allocLightmap - - statebits_template(); - } - - void refblend_template() - { - const auto blendFunc = ReadStringProperty("blendFunc"); - } - - void sort_template() - { - const auto sort = ReadStringProperty("sort"); - const auto materialType = ReadStringProperty("materialType"); - const auto polygonOffset = ReadStringProperty("polygonOffset"); - const auto blendFunc = ReadStringProperty("blendFunc"); - - std::string sortKey; - if (sort.empty() || sort == GDT_SORTKEY_DEFAULT) - { - if (materialType == GDT_MATERIAL_TYPE_DISTORTION) - sortKey = GDT_SORTKEY_DISTORTION; - else if (polygonOffset == "Static Decal") - sortKey = GDT_SORTKEY_DECAL_STATIC; - else if (polygonOffset == "Weapon Impact") - sortKey = GDT_SORTKEY_DECAL_WEAPON_IMPACT; - else if (materialType == GDT_MATERIAL_TYPE_EFFECT) - sortKey = GDT_SORTKEY_EFFECT_AUTO_SORT; - else if (materialType == GDT_MATERIAL_TYPE_OBJECTIVE || blendFunc == "Blend" || blendFunc == "Add" || blendFunc == "Screen Add") - sortKey = GDT_SORTKEY_BLEND_ADDITIVE; - // else if (blendFunc == "Multiply") // TODO - // sortKey = GDT_SORTKEY_MULTIPLICATIVE; - else if (materialType == GDT_MATERIAL_TYPE_SKY) - sortKey = GDT_SORTKEY_SKY; - else if (materialType == GDT_MATERIAL_TYPE_MODEL_AMBIENT) - sortKey = GDT_SORTKEY_OPAQUE_AMBIENT; - else - sortKey = GDT_SORTKEY_OPAQUE; - } - else - sortKey = sort; - - bool foundSortKey = false; - for (auto sortKeyIndex = 0u; sortKeyIndex < SORTKEY_MAX; sortKeyIndex++) - { - if (SortKeyNames[sortKeyIndex] && sortKey == SortKeyNames[sortKeyIndex]) - { - SetSort(static_cast(sortKeyIndex)); - foundSortKey = true; - break; - } - } - - if (!foundSortKey) - { - char* endPtr; - const auto sortKeyNum = strtoul(sortKey.c_str(), &endPtr, 10); - - if (endPtr != &sortKey[sortKey.size()]) - throw GdtReadingException(std::format("Invalid sort value: \"{}\"", sortKey)); - - SetSort(static_cast(sortKeyNum)); - } - } - - void clamp_template() {} - - void textureAtlas_template() - { - const auto rowCount = ReadIntegerProperty("textureAtlasRowCount", 1); - const auto columnCount = ReadIntegerProperty("textureAtlasColumnCount", 1); - - SetTextureAtlas(static_cast(rowCount), static_cast(columnCount)); - } - - void statebits_template() - { - alphatest_template(); - blendfunc_template(); - colorwrite_template(); - cullface_template(); - depthtest_template(); - depthwrite_template(); - gammawrite_template(); - polygonoffset_template(); - stencil_template(); - } - - void alphatest_template() - { - const auto alphaTest = ReadStringProperty("alphaTest"); - - if (alphaTest == GDT_ALPHA_TEST_ALWAYS) - SetAlphaTest(AlphaTest_e::ALWAYS); - else if (alphaTest == GDT_ALPHA_TEST_GE128) - SetAlphaTest(AlphaTest_e::GE128); - else if (alphaTest == GDT_ALPHA_TEST_GT0) // TODO: This is not available for IW3 - SetAlphaTest(AlphaTest_e::GT0); - else - throw GdtReadingException(std::format("Invalid alphatest value: \"{}\"", alphaTest)); - } - - void blendfunc_template() - { - const auto blendFunc = ReadStringProperty("blendFunc"); - - if (blendFunc == GDT_BLEND_FUNC_ADD) - { - SetBlendFunc(BlendOp_e::ADD, CustomBlendFunc_e::ONE, CustomBlendFunc_e::ONE); - SetSeparateAlphaBlendFunc(BlendOp_e::DISABLE, CustomBlendFunc_e::ONE, CustomBlendFunc_e::ZERO); - } - else if (blendFunc == GDT_BLEND_FUNC_BLEND) - { - SetBlendFunc(BlendOp_e::ADD, CustomBlendFunc_e::SRC_ALPHA, CustomBlendFunc_e::INV_SRC_ALPHA); - SetSeparateAlphaBlendFunc(BlendOp_e::DISABLE, CustomBlendFunc_e::ONE, CustomBlendFunc_e::ZERO); - } - else if (blendFunc == GDT_BLEND_FUNC_MULTIPLY) - { - SetBlendFunc(BlendOp_e::ADD, CustomBlendFunc_e::ZERO, CustomBlendFunc_e::SRC_COLOR); - SetSeparateAlphaBlendFunc(BlendOp_e::DISABLE, CustomBlendFunc_e::ONE, CustomBlendFunc_e::ZERO); - } - else if (blendFunc == GDT_BLEND_FUNC_REPLACE) - { - SetBlendFunc(BlendOp_e::DISABLE, CustomBlendFunc_e::ONE, CustomBlendFunc_e::ZERO); - SetSeparateAlphaBlendFunc(BlendOp_e::DISABLE, CustomBlendFunc_e::ONE, CustomBlendFunc_e::ZERO); - } - else if (blendFunc == GDT_BLEND_FUNC_SCREEN_ADD) - { - SetBlendFunc(BlendOp_e::ADD, CustomBlendFunc_e::INV_DST_COLOR, CustomBlendFunc_e::ONE); - SetSeparateAlphaBlendFunc(BlendOp_e::DISABLE, CustomBlendFunc_e::ONE, CustomBlendFunc_e::ZERO); - } - else if (blendFunc == GDT_BLEND_FUNC_CUSTOM) - { - const auto customBlendOpRgb = ReadEnumProperty("customBlendOpRgb", GdtBlendOpNames, std::extent_v); - const auto srcCustomBlendFunc = - ReadEnumProperty("srcCustomBlendFunc", GdtCustomBlendFuncNames, std::extent_v); - const auto destCustomBlendFunc = - ReadEnumProperty("destCustomBlendFunc", GdtCustomBlendFuncNames, std::extent_v); - const auto customBlendOpAlpha = ReadEnumProperty("customBlendOpAlpha", GdtBlendOpNames, std::extent_v); - const auto srcCustomBlendFuncAlpha = - ReadEnumProperty("srcCustomBlendFuncAlpha", GdtCustomBlendFuncNames, std::extent_v); - const auto destCustomBlendFuncAlpha = - ReadEnumProperty("destCustomBlendFuncAlpha", GdtCustomBlendFuncNames, std::extent_v); - - SetBlendFunc(customBlendOpRgb, srcCustomBlendFunc, destCustomBlendFunc); - SetSeparateAlphaBlendFunc(customBlendOpAlpha, srcCustomBlendFuncAlpha, destCustomBlendFuncAlpha); - } - else - { - throw GdtReadingException(std::format("Invalid blendfunc value: \"{}\"", blendFunc)); - } - } - - void colorwrite_template() - { - const auto colorWriteRed = ReadEnumProperty( - "colorWriteRed", GdtStateBitsEnabledStatusNames, std::extent_v); - const auto colorWriteGreen = ReadEnumProperty( - "colorWriteGreen", GdtStateBitsEnabledStatusNames, std::extent_v); - const auto colorWriteBlue = ReadEnumProperty( - "colorWriteBlue", GdtStateBitsEnabledStatusNames, std::extent_v); - const auto colorWriteAlpha = ReadEnumProperty( - "colorWriteAlpha", GdtStateBitsEnabledStatusNames, std::extent_v); - - SetColorWrite(colorWriteRed, colorWriteGreen, colorWriteBlue, colorWriteAlpha); - } - - void cullface_template() - { - const auto cullFace = ReadEnumProperty("cullFace", GdtCullFaceNames, std::extent_v); - - SetCullFace(cullFace); - } - - void depthtest_template() - { - const auto depthTest = ReadEnumProperty("depthTest", GdtDepthTestNames, std::extent_v); - - SetDepthTest(depthTest); - } - - void depthwrite_template() - { - const auto depthWrite = - ReadEnumProperty("depthWrite", GdtStateBitsOnOffStatusNames, std::extent_v); - const auto blendFunc = ReadStringProperty("blendFunc"); - - if (depthWrite == StateBitsEnabledStatus_e::ENABLED) - SetDepthWrite(true); - else if (depthWrite == StateBitsEnabledStatus_e::DISABLED) - SetDepthWrite(false); - else if (blendFunc == GDT_BLEND_FUNC_ADD) - SetDepthWrite(false); - else if (blendFunc == GDT_BLEND_FUNC_BLEND) - SetDepthWrite(false); - else if (blendFunc == GDT_BLEND_FUNC_MULTIPLY) - SetDepthWrite(false); - else if (blendFunc == GDT_BLEND_FUNC_REPLACE) - SetDepthWrite(true); - else if (blendFunc == GDT_BLEND_FUNC_SCREEN_ADD) - SetDepthWrite(false); - else if (blendFunc == GDT_BLEND_FUNC_CUSTOM) - SetDepthWrite(false); - else - { - throw GdtReadingException(std::format("Invalid depthWrite blendFunc value: \"{}\"", blendFunc)); - } - } - - void gammawrite_template() - { - const auto gammaWrite = - ReadEnumProperty("gammaWrite", GdtStateBitsOnOffStatusNames, std::extent_v); - - if (gammaWrite == StateBitsEnabledStatus_e::UNKNOWN) - { - std::ostringstream ss; - ss << "Invalid gammaWrite blendFunc value: \"\""; - throw GdtReadingException(ss.str()); - } - - SetGammaWrite(gammaWrite == StateBitsEnabledStatus_e::ENABLED); - } - - void polygonoffset_template() - { - const auto polygonOffset = - ReadEnumProperty("polygonOffset", GdtPolygonOffsetNames, std::extent_v); - - SetPolygonOffset(polygonOffset); - } - - void stencil_template() - { - const auto stencilMode = ReadEnumProperty("stencil", GdtStencilModeNames, std::extent_v); - - if (stencilMode == StencilMode_e::DISABLED) - { - DisableStencil(StencilIndex::FRONT); - DisableStencil(StencilIndex::BACK); - } - else - { - if (stencilMode == StencilMode_e::TWO_SIDED) - { - const auto stencilBackFunc = - ReadEnumProperty("stencilFunc2", GdtStencilFuncNames, std::extent_v); - const auto stencilBackOpFail = - ReadEnumProperty("stencilOpFail2", GdtStencilOpNames, std::extent_v); - const auto stencilBackOpZFail = - ReadEnumProperty("stencilOpZFail2", GdtStencilOpNames, std::extent_v); - const auto stencilBackOpPass = - ReadEnumProperty("stencilOpPass2", GdtStencilOpNames, std::extent_v); - - EnableStencil(StencilIndex::BACK, stencilBackFunc, stencilBackOpFail, stencilBackOpZFail, stencilBackOpPass); - } - - const auto stencilFrontFunc = - ReadEnumProperty("stencilFunc1", GdtStencilFuncNames, std::extent_v); - const auto stencilFrontOpFail = ReadEnumProperty("stencilOpFail1", GdtStencilOpNames, std::extent_v); - const auto stencilFrontOpZFail = - ReadEnumProperty("stencilOpZFail1", GdtStencilOpNames, std::extent_v); - const auto stencilFrontOpPass = ReadEnumProperty("stencilOpPass1", GdtStencilOpNames, std::extent_v); - - EnableStencil(StencilIndex::FRONT, stencilFrontFunc, stencilFrontOpFail, stencilFrontOpZFail, stencilFrontOpPass); - } - } - - void SetTechniqueSet(const std::string& techsetName) - { - auto* techset = m_context.LoadDependency(techsetName); - - if (techset == nullptr) - throw GdtReadingException(std::format("Could not load techset: \"{}\"", techsetName)); - - m_registration.AddDependency(techset); - m_material.techniqueSet = techset->Asset(); - - auto& definitionCache = m_context.GetZoneAssetCreationState(); - - bool failure = false; - const auto* techsetDefinition = m_techset_creator->LoadTechsetDefinition(techsetName, m_context, failure); - if (techsetDefinition == nullptr) - { - throw GdtReadingException(std::format("Could not find techset definition for: \"{}\"", techsetName)); - } - - SetTechniqueSetStateBits(techsetDefinition); - SetTechniqueSetCameraRegion(techsetDefinition); - } - - void SetTechniqueSetStateBits(const techset::TechsetDefinition* techsetDefinition) - { - for (auto i = 0; i < TECHNIQUE_COUNT; i++) - { - std::string techniqueName; - if (techsetDefinition->GetTechniqueByIndex(i, techniqueName)) - { - const auto stateBitsForTechnique = GetStateBitsForTechnique(techniqueName); - const auto foundStateBits = std::ranges::find_if(m_state_bits, - [stateBitsForTechnique](const GfxStateBits& s1) - { - return s1.loadBits[0] == stateBitsForTechnique.loadBits[0] - && s1.loadBits[1] == stateBitsForTechnique.loadBits[1]; - }); - - if (foundStateBits != m_state_bits.end()) - { - m_material.stateBitsEntry[i] = static_cast(foundStateBits - m_state_bits.begin()); - } - else - { - m_material.stateBitsEntry[i] = static_cast(m_state_bits.size()); - m_state_bits.push_back(stateBitsForTechnique); - } - } - else - { - m_material.stateBitsEntry[i] = std::numeric_limits::max(); - } - } - } - - GfxStateBits GetStateBitsForTechnique(const std::string& techniqueName) - { - const auto* stateMap = GetStateMapForTechnique(techniqueName); - if (!stateMap) - return m_base_state_bits; - - const auto preCalculatedStateBits = m_state_bits_per_state_map.find(stateMap); - if (preCalculatedStateBits != m_state_bits_per_state_map.end()) - return preCalculatedStateBits->second; - - const auto stateBits = CalculateStateBitsWithStateMap(stateMap); - m_state_bits_per_state_map.emplace(stateMap, stateBits); - - return stateBits; - } - - _NODISCARD const state_map::StateMapDefinition* GetStateMapForTechnique(const std::string& techniqueName) const - { - const auto* preloadedStateMap = m_state_map_cache.GetStateMapForTechnique(techniqueName); - if (preloadedStateMap) - return preloadedStateMap; - - const auto techniqueFileName = GetTechniqueFileName(techniqueName); - const auto file = m_search_path.Open(techniqueFileName); - if (!file.IsOpen()) - return nullptr; - - state_map::StateMapFromTechniqueExtractor extractor; - const techset::TechniqueFileReader reader(*file.m_stream, techniqueFileName, &extractor); - if (!reader.ReadTechniqueDefinition()) - { - m_state_map_cache.SetTechniqueUsesStateMap(techniqueName, nullptr); - return nullptr; - } - - const auto stateMapName = extractor.RetrieveStateMap(); - const auto* loadedStateMap = m_techset_creator->LoadStateMapDefinition(stateMapName, m_context); - m_state_map_cache.SetTechniqueUsesStateMap(techniqueName, loadedStateMap); - - return loadedStateMap; - } - - GfxStateBits CalculateStateBitsWithStateMap(const state_map::StateMapDefinition* stateMap) const - { - const state_map::StateMapHandler stateMapHandler(stateMapLayout, *stateMap); - - GfxStateBits outBits{}; - stateMapHandler.ApplyStateMap(m_base_state_bits.loadBits, outBits.loadBits); - - return outBits; - } - - void SetTechniqueSetCameraRegion(const techset::TechsetDefinition* techsetDefinition) const - { - std::string tempName; - if (techsetDefinition->GetTechniqueByIndex(TECHNIQUE_LIT, tempName)) - { - if (m_material.info.sortKey >= SORTKEY_TRANS_START) - m_material.cameraRegion = CAMERA_REGION_LIT_TRANS; - else - m_material.cameraRegion = CAMERA_REGION_LIT_OPAQUE; - } - else if (techsetDefinition->GetTechniqueByIndex(TECHNIQUE_EMISSIVE, tempName)) - { - m_material.cameraRegion = CAMERA_REGION_EMISSIVE; - } - else - { - m_material.cameraRegion = CAMERA_REGION_NONE; - } - } - - void AddMapTexture( - const std::string& typeName, const TileMode_e tileMode, GdtFilter_e filterMode, const TextureSemantic semantic, const std::string& textureName) - { - MaterialTextureDef textureDef{}; - textureDef.nameHash = Common::R_HashString(typeName.c_str()); - textureDef.nameStart = typeName[0]; - textureDef.nameEnd = typeName[typeName.size() - 1]; - textureDef.samplerState = 0; - textureDef.semantic = static_cast(semantic); - - switch (tileMode) - { - case TileMode_e::TILE_BOTH: - textureDef.samplerState |= SAMPLER_CLAMP_U | SAMPLER_CLAMP_V | SAMPLER_CLAMP_W; - break; - case TileMode_e::TILE_HORIZONTAL: - textureDef.samplerState |= SAMPLER_CLAMP_V; - break; - case TileMode_e::TILE_VERTICAL: - textureDef.samplerState |= SAMPLER_CLAMP_U; - break; - case TileMode_e::UNKNOWN: - case TileMode_e::NO_TILE: - break; - default: - assert(false); - break; - } - - switch (filterMode) - { - case GdtFilter_e::MIP_2X_BILINEAR: - textureDef.samplerState |= SAMPLER_FILTER_ANISO2X | SAMPLER_MIPMAP_NEAREST; - break; - case GdtFilter_e::MIP_2X_TRILINEAR: - textureDef.samplerState |= SAMPLER_FILTER_ANISO2X | SAMPLER_MIPMAP_LINEAR; - break; - case GdtFilter_e::MIP_4X_BILINEAR: - textureDef.samplerState |= SAMPLER_FILTER_ANISO4X | SAMPLER_MIPMAP_NEAREST; - break; - case GdtFilter_e::MIP_4X_TRILINEAR: - textureDef.samplerState |= SAMPLER_FILTER_ANISO4X | SAMPLER_MIPMAP_LINEAR; - break; - case GdtFilter_e::NOMIP_NEAREST: - textureDef.samplerState |= SAMPLER_FILTER_NEAREST | SAMPLER_MIPMAP_DISABLED; - break; - case GdtFilter_e::NOMIP_BILINEAR: - textureDef.samplerState |= SAMPLER_FILTER_LINEAR | SAMPLER_MIPMAP_DISABLED; - break; - default: - assert(false); - break; - } - - auto* image = m_context.LoadDependency(textureName); - - if (image == nullptr) - throw GdtReadingException(std::format("Could not load image: \"{}\"", textureName)); - - m_registration.AddDependency(image); - textureDef.u.image = image->Asset(); - - m_textures.push_back(textureDef); - } - - void AddConstant(const std::string& constantName, const GdtVec4& literalData) - { - MaterialConstantDef constantDef{}; - constantDef.literal[0] = literalData.x; - constantDef.literal[1] = literalData.y; - constantDef.literal[2] = literalData.z; - constantDef.literal[3] = literalData.w; - strncpy(constantDef.name, constantName.c_str(), std::extent_v); - constantDef.nameHash = Common::R_HashString(constantName.c_str()); - - m_constants.push_back(constantDef); - } - - void SetSort(const unsigned char sort) const - { - m_material.info.sortKey = sort; - } - - void SetTextureAtlas(const unsigned char rowCount, const unsigned char columnCount) const - { - m_material.info.textureAtlasRowCount = rowCount; - m_material.info.textureAtlasColumnCount = columnCount; - } - - void SetAlphaTest(const AlphaTest_e alphaTest) - { - switch (alphaTest) - { - case AlphaTest_e::ALWAYS: - m_base_state_bits.loadBits[0] |= GFXS0_ATEST_DISABLE; - break; - - case AlphaTest_e::GT0: - m_base_state_bits.loadBits[0] |= GFXS0_ATEST_GT_0; - break; - - case AlphaTest_e::LT128: - m_base_state_bits.loadBits[0] |= GFXS0_ATEST_LT_128; - break; - - case AlphaTest_e::GE128: - m_base_state_bits.loadBits[0] |= GFXS0_ATEST_GE_128; - break; - - case AlphaTest_e::UNKNOWN: - default: - throw GdtReadingException(std::format("Unknown alphatest value: \"{}\"", static_cast(alphaTest))); - } - } - - void SetBlendFunc(BlendOp_e blendOp, CustomBlendFunc_e srcFunc, CustomBlendFunc_e destFunc) - { - if (blendOp == BlendOp_e::UNKNOWN || srcFunc == CustomBlendFunc_e::UNKNOWN || destFunc == CustomBlendFunc_e::UNKNOWN) - { - std::ostringstream ss; - ss << "Unknown SeparateAlphaBlendFunc values: \"\""; - throw GdtReadingException(ss.str()); - } - - m_base_state_bits.loadBits[0] &= ~GFXS0_BLENDOP_RGB_MASK; - m_base_state_bits.loadBits[0] |= ((static_cast(blendOp) - 1) << GFXS0_BLENDOP_RGB_SHIFT) & GFXS0_BLENDOP_RGB_MASK; - - m_base_state_bits.loadBits[0] &= ~GFXS0_SRCBLEND_RGB_MASK; - m_base_state_bits.loadBits[0] |= ((static_cast(srcFunc) - 1) << GFXS0_SRCBLEND_RGB_SHIFT) & GFXS0_SRCBLEND_RGB_MASK; - - m_base_state_bits.loadBits[0] &= ~GFXS0_DSTBLEND_RGB_MASK; - m_base_state_bits.loadBits[0] |= ((static_cast(destFunc) - 1) << GFXS0_DSTBLEND_RGB_SHIFT) & GFXS0_DSTBLEND_RGB_MASK; - } - - void SetSeparateAlphaBlendFunc(BlendOp_e blendOp, CustomBlendFunc_e srcFunc, CustomBlendFunc_e destFunc) - { - if (blendOp == BlendOp_e::UNKNOWN || srcFunc == CustomBlendFunc_e::UNKNOWN || destFunc == CustomBlendFunc_e::UNKNOWN) - { - std::ostringstream ss; - ss << "Unknown SeparateAlphaBlendFunc values: \"\""; - throw GdtReadingException(ss.str()); - } - - m_base_state_bits.loadBits[0] &= ~GFXS0_BLENDOP_ALPHA_MASK; - m_base_state_bits.loadBits[0] |= ((static_cast(blendOp) - 1) << GFXS0_BLENDOP_ALPHA_SHIFT) & GFXS0_BLENDOP_ALPHA_MASK; - - m_base_state_bits.loadBits[0] &= ~GFXS0_SRCBLEND_ALPHA_MASK; - m_base_state_bits.loadBits[0] |= ((static_cast(srcFunc) - 1) << GFXS0_SRCBLEND_ALPHA_SHIFT) & GFXS0_SRCBLEND_ALPHA_MASK; - - m_base_state_bits.loadBits[0] &= ~GFXS0_DSTBLEND_ALPHA_MASK; - m_base_state_bits.loadBits[0] |= ((static_cast(destFunc) - 1) << GFXS0_DSTBLEND_ALPHA_SHIFT) & GFXS0_DSTBLEND_ALPHA_MASK; - } - - void SetColorWrite(const StateBitsEnabledStatus_e colorWriteRed, - const StateBitsEnabledStatus_e colorWriteGreen, - const StateBitsEnabledStatus_e colorWriteBlue, - const StateBitsEnabledStatus_e colorWriteAlpha) - { - if (colorWriteRed == StateBitsEnabledStatus_e::UNKNOWN || colorWriteGreen == StateBitsEnabledStatus_e::UNKNOWN - || colorWriteBlue == StateBitsEnabledStatus_e::UNKNOWN || colorWriteAlpha == StateBitsEnabledStatus_e::UNKNOWN) - { - std::ostringstream ss; - ss << "Unknown ColorWrite values: \"\""; - throw GdtReadingException(ss.str()); - } - - if (colorWriteRed != colorWriteGreen || colorWriteRed != colorWriteBlue) - { - throw GdtReadingException("Invalid ColorWrite values: values for rgb must match"); - } - - m_base_state_bits.loadBits[0] &= ~GFXS0_COLORWRITE_MASK; - if (colorWriteRed == StateBitsEnabledStatus_e::ENABLED) - m_base_state_bits.loadBits[0] |= GFXS0_COLORWRITE_RGB; - if (colorWriteAlpha == StateBitsEnabledStatus_e::ENABLED) - m_base_state_bits.loadBits[0] |= GFXS0_COLORWRITE_ALPHA; - } - - void SetCullFace(const CullFace_e cullFace) - { - if (cullFace == CullFace_e::UNKNOWN) - { - std::ostringstream ss; - ss << "Unknown cullFace values: \"\""; - throw GdtReadingException(ss.str()); - } - - m_base_state_bits.loadBits[0] &= ~GFXS0_CULL_MASK; - - if (cullFace == CullFace_e::FRONT) - { - m_base_state_bits.loadBits[0] |= GFXS0_CULL_FRONT; - } - else if (cullFace == CullFace_e::BACK) - { - m_base_state_bits.loadBits[0] |= GFXS0_CULL_BACK; - } - else - { - assert(cullFace == CullFace_e::NONE); - m_base_state_bits.loadBits[0] |= GFXS0_CULL_NONE; - } - } - - void SetDepthTest(const DepthTest_e depthTest) - { - m_base_state_bits.loadBits[1] &= GFXS1_DEPTHTEST_MASK; - - switch (depthTest) - { - case DepthTest_e::LESS_EQUAL: - m_base_state_bits.loadBits[1] |= GFXS1_DEPTHTEST_LESSEQUAL; - break; - - case DepthTest_e::LESS: - m_base_state_bits.loadBits[1] |= GFXS1_DEPTHTEST_LESS; - break; - - case DepthTest_e::EQUAL: - m_base_state_bits.loadBits[1] |= GFXS1_DEPTHTEST_EQUAL; - break; - - case DepthTest_e::ALWAYS: - m_base_state_bits.loadBits[1] |= GFXS1_DEPTHTEST_ALWAYS; - break; - - case DepthTest_e::DISABLE: - m_base_state_bits.loadBits[1] |= GFXS1_DEPTHTEST_DISABLE; - break; - - case DepthTest_e::UNKNOWN: - default: - std::ostringstream ss; - ss << "Unknown depthTest values: \"\""; - throw GdtReadingException(ss.str()); - } - } - - void SetDepthWrite(const bool depthWrite) - { - m_base_state_bits.loadBits[1] &= ~GFXS1_DEPTHWRITE; - - if (depthWrite) - m_base_state_bits.loadBits[1] |= GFXS1_DEPTHWRITE; - } - - void SetGammaWrite(const bool gammaWrite) - { - m_base_state_bits.loadBits[0] &= ~GFXS0_GAMMAWRITE; - - if (gammaWrite) - m_base_state_bits.loadBits[0] |= GFXS0_GAMMAWRITE; - } - - void SetPolygonOffset(const PolygonOffset_e polygonOffset) - { - if (polygonOffset == PolygonOffset_e::UNKNOWN) - { - std::ostringstream ss; - ss << "Unknown polygonOffset values: \"\""; - throw GdtReadingException(ss.str()); - } - - m_base_state_bits.loadBits[1] &= ~GFXS1_POLYGON_OFFSET_MASK; - m_base_state_bits.loadBits[1] |= ((static_cast(polygonOffset) - 1) << GFXS1_POLYGON_OFFSET_SHIFT) & GFXS1_POLYGON_OFFSET_MASK; - } - - static void GetStencilMasksForIndex(const StencilIndex stencil, - unsigned& enabledMask, - unsigned& funcShift, - unsigned& funcMask, - unsigned& opFailShift, - unsigned& opFailMask, - unsigned& opZFailShift, - unsigned& opZFailMask, - unsigned& opPassShift, - unsigned& opPassMask) - { - if (stencil == StencilIndex::FRONT) - { - enabledMask = GFXS1_STENCIL_FRONT_ENABLE; - funcShift = GFXS1_STENCIL_FRONT_FUNC_SHIFT; - funcMask = GFXS1_STENCIL_FRONT_FUNC_MASK; - opFailShift = GFXS1_STENCIL_FRONT_FAIL_SHIFT; - opFailMask = GFXS1_STENCIL_FRONT_FAIL_MASK; - opZFailShift = GFXS1_STENCIL_FRONT_ZFAIL_SHIFT; - opZFailMask = GFXS1_STENCIL_FRONT_ZFAIL_MASK; - opPassShift = GFXS1_STENCIL_FRONT_PASS_SHIFT; - opPassMask = GFXS1_STENCIL_FRONT_PASS_MASK; - } - else - { - assert(stencil == StencilIndex::BACK); - - enabledMask = GFXS1_STENCIL_BACK_ENABLE; - funcShift = GFXS1_STENCIL_BACK_FUNC_SHIFT; - funcMask = GFXS1_STENCIL_BACK_FUNC_MASK; - opFailShift = GFXS1_STENCIL_BACK_FAIL_SHIFT; - opFailMask = GFXS1_STENCIL_BACK_FAIL_MASK; - opZFailShift = GFXS1_STENCIL_BACK_ZFAIL_SHIFT; - opZFailMask = GFXS1_STENCIL_BACK_ZFAIL_MASK; - opPassShift = GFXS1_STENCIL_BACK_PASS_SHIFT; - opPassMask = GFXS1_STENCIL_BACK_PASS_MASK; - } - } - - void DisableStencil(const StencilIndex stencil) - { - unsigned enabledMask, funcShift, funcMask, opFailShift, opFailMask, opZFailShift, opZFailMask, opPassShift, opPassMask; - GetStencilMasksForIndex(stencil, enabledMask, funcShift, funcMask, opFailShift, opFailMask, opZFailShift, opZFailMask, opPassShift, opPassMask); - - m_base_state_bits.loadBits[1] &= ~(enabledMask | funcMask | opFailMask | opZFailMask | opPassMask); - } - - void EnableStencil( - const StencilIndex stencil, StencilFunc_e stencilFunc, StencilOp_e stencilOpFail, StencilOp_e stencilOpZFail, StencilOp_e stencilOpPass) - { - unsigned enabledMask, funcShift, funcMask, opFailShift, opFailMask, opZFailShift, opZFailMask, opPassShift, opPassMask; - GetStencilMasksForIndex(stencil, enabledMask, funcShift, funcMask, opFailShift, opFailMask, opZFailShift, opZFailMask, opPassShift, opPassMask); - - m_base_state_bits.loadBits[1] |= enabledMask; - - m_base_state_bits.loadBits[1] &= ~funcMask; - m_base_state_bits.loadBits[1] |= ((static_cast(stencilFunc) - 1) << funcShift) & funcMask; - - m_base_state_bits.loadBits[1] &= ~opFailMask; - m_base_state_bits.loadBits[1] |= ((static_cast(stencilOpFail) - 1) << opFailShift) & opFailMask; - - m_base_state_bits.loadBits[1] &= ~opZFailMask; - m_base_state_bits.loadBits[1] |= ((static_cast(stencilOpZFail) - 1) << opZFailShift) & opZFailMask; - - m_base_state_bits.loadBits[1] &= ~opPassMask; - m_base_state_bits.loadBits[1] |= ((static_cast(stencilOpPass) - 1) << opPassShift) & opPassMask; - } - - void FinalizeMaterial() const - { - if (!m_textures.empty()) - { - m_material.textureTable = m_memory.Alloc(m_textures.size()); - m_material.textureCount = static_cast(m_textures.size()); - std::memcpy(m_material.textureTable, m_textures.data(), sizeof(MaterialTextureDef) * m_textures.size()); - } - else - { - m_material.textureTable = nullptr; - m_material.textureCount = 0u; - } - - if (!m_constants.empty()) - { - m_material.constantTable = m_memory.Alloc(m_constants.size()); - m_material.constantCount = static_cast(m_constants.size()); - std::memcpy(m_material.constantTable, m_constants.data(), sizeof(MaterialConstantDef) * m_constants.size()); - } - else - { - m_material.constantTable = nullptr; - m_material.constantCount = 0u; - } - - if (!m_state_bits.empty()) - { - m_material.stateBitsTable = m_memory.Alloc(m_state_bits.size()); - m_material.stateBitsCount = static_cast(m_state_bits.size()); - std::memcpy(m_material.stateBitsTable, m_state_bits.data(), sizeof(GfxStateBits) * m_state_bits.size()); - } - else - { - m_material.stateBitsTable = nullptr; - m_material.stateBitsCount = 0u; - } - } - - static size_t - GetIndexForString(const std::string& propertyName, const std::string& value, const char** validValuesArray, const size_t validValuesArraySize) - { - for (auto i = 0u; i < validValuesArraySize; i++) - { - if (validValuesArray[i] == value) - return i; - } - - throw GdtReadingException(std::format("Unknown {} value: \"{}\"", propertyName, value)); - } - - template T ReadEnumProperty(const std::string& propertyName, const char** validValuesArray, const size_t validValuesArraySize) const - { - return static_cast(GetIndexForString(propertyName, ReadStringProperty(propertyName), validValuesArray, validValuesArraySize)); - } - - Material& m_material; - MemoryManager& m_memory; - ISearchPath& m_search_path; - AssetCreationContext& m_context; - AssetRegistration& m_registration; - - techset::TechniqueStateMapCache& m_state_map_cache; - std::unordered_map m_state_bits_per_state_map; - - GfxStateBits m_base_state_bits; - std::vector m_state_bits; - std::vector m_textures; - std::vector m_constants; - - std::unique_ptr m_techset_creator; - }; - class MaterialLoader final : public AssetCreator { public: - MaterialLoader(MemoryManager& memory, ISearchPath& searchPath, IGdtQueryable& gdt) + MaterialLoader(MemoryManager& memory, ISearchPath& searchPath) : m_memory(memory), - m_search_path(m_search_path), - m_gdt(gdt) + m_search_path(searchPath) { } AssetCreationResult CreateAsset(const std::string& assetName, AssetCreationContext& context) override { - const auto* entry = m_gdt.GetGdtEntryByGdfAndName(ObjConstants::GDF_FILENAME_MATERIAL, assetName); - if (!entry) + const auto file = m_search_path.Open(material::GetFileNameForAssetName(assetName)); + if (!file.IsOpen()) return AssetCreationResult::NoAction(); auto* material = m_memory.Alloc(); material->info.name = m_memory.Dup(assetName.c_str()); AssetRegistration registration(assetName, material); - - MaterialGdtLoader loader(*entry, *material, m_memory, m_search_path, context, registration); - - try + if (!LoadMaterialAsJson(*file.m_stream, *material, m_memory, context, registration)) { - if (loader.Load()) - return AssetCreationResult::Success(context.AddAsset(std::move(registration))); - } - catch (const SkipMaterialException&) - { - return AssetCreationResult::NoAction(); - } - catch (const GdtReadingException& e) - { - std::cerr << std::format("Error while trying to load material from gdt: {} @ GdtEntry \"{}\"\n", e.what(), entry->m_name); + std::cerr << std::format("Failed to load material \"{}\"\n", assetName); + return AssetCreationResult::Failure(); } - return AssetCreationResult::Failure(); + return AssetCreationResult::Success(context.AddAsset(std::move(registration))); } private: MemoryManager& m_memory; ISearchPath& m_search_path; - IGdtQueryable& m_gdt; }; } // namespace namespace IW4 { - std::unique_ptr> CreateMaterialLoader(MemoryManager& memory, ISearchPath& searchPath, IGdtQueryable& gdt) + std::unique_ptr> CreateMaterialLoader(MemoryManager& memory, ISearchPath& searchPath) { - return std::make_unique(memory, searchPath, gdt); + return std::make_unique(memory, searchPath); } } // namespace IW4 diff --git a/src/ObjLoading/Game/IW4/Material/LoaderMaterialIW4.h b/src/ObjLoading/Game/IW4/Material/LoaderMaterialIW4.h index 04b975e0..efd0a610 100644 --- a/src/ObjLoading/Game/IW4/Material/LoaderMaterialIW4.h +++ b/src/ObjLoading/Game/IW4/Material/LoaderMaterialIW4.h @@ -8,5 +8,5 @@ namespace IW4 { - std::unique_ptr> CreateMaterialLoader(MemoryManager& memory, ISearchPath& searchPath, IGdtQueryable& gdt); + std::unique_ptr> CreateMaterialLoader(MemoryManager& memory, ISearchPath& searchPath); } // namespace IW4 diff --git a/src/ObjLoading/Game/IW4/ObjLoaderIW4.cpp b/src/ObjLoading/Game/IW4/ObjLoaderIW4.cpp index c124a7f9..ca33ed36 100644 --- a/src/ObjLoading/Game/IW4/ObjLoaderIW4.cpp +++ b/src/ObjLoading/Game/IW4/ObjLoaderIW4.cpp @@ -6,6 +6,7 @@ #include "Leaderboard/LoaderLeaderboardIW4.h" #include "LightDef/LoaderLightDefIW4.h" #include "Localize/LoaderLocalizeIW4.h" +#include "Material/CompilingMaterialLoad.h" #include "Material/LoaderMaterialIW4.h" #include "Menu/LoaderMenuListIW4.h" #include "ObjLoading.h" @@ -126,7 +127,8 @@ namespace // collection.AddAssetCreator(std::make_unique(memory)); // collection.AddAssetCreator(std::make_unique(memory)); // collection.AddAssetCreator(std::make_unique(memory)); - collection.AddAssetCreator(CreateMaterialLoader(memory, searchPath, gdt)); + collection.AddAssetCreator(CreateMaterialLoader(memory, searchPath)); + collection.AddAssetCreator(CreateCompilingMaterialLoader(memory, searchPath, gdt)); collection.AddAssetCreator(CreatePixelShaderLoader(memory, searchPath)); collection.AddAssetCreator(CreateVertexShaderLoader(memory, searchPath)); collection.AddAssetCreator(CreateVertexDeclLoader(memory)); diff --git a/src/ObjLoading/Material/JsonMaterialLoader.cpp.template b/src/ObjLoading/Material/JsonMaterialLoader.cpp.template index f5e21ab2..65687955 100644 --- a/src/ObjLoading/Material/JsonMaterialLoader.cpp.template +++ b/src/ObjLoading/Material/JsonMaterialLoader.cpp.template @@ -1,8 +1,12 @@ -#options GAME (IW5, T6) +#options GAME (IW4, IW5, T6) #filename "Game/" + GAME + "/Material/JsonMaterialLoader" + GAME + ".cpp" -#if GAME == "IW5" +#if GAME == "IW4" +#define FEATURE_IW4 +#define HAS_WATER +#define GAME_LOWER "iw4" +#elif GAME == "IW5" #define FEATURE_IW5 #define HAS_WATER #define GAME_LOWER "iw5" @@ -90,7 +94,7 @@ namespace std::cerr << std::format("Cannot load material \"{}\": {}\n", material.info.name, message); } -#ifdef FEATURE_IW5 +#if defined(FEATURE_IW4) || defined(FEATURE_IW5) static bool CreateGameFlagsFromJson(const JsonMaterial& jMaterial, unsigned char& gameFlags) #elif defined(FEATURE_T6) static bool CreateGameFlagsFromJson(const JsonMaterial& jMaterial, unsigned& gameFlags) @@ -300,7 +304,7 @@ namespace structured.alphaTestDisabled = 0; structured.alphaTest = GFXS_ALPHA_TEST_GT_0; } -#ifdef FEATURE_IW5 +#if defined(FEATURE_IW4) || defined(FEATURE_IW5) else if (jStateBitsTableEntry.alphaTest == JsonAlphaTest::LT128) { structured.alphaTestDisabled = 0; @@ -335,7 +339,7 @@ namespace structured.blendOpAlpha = jStateBitsTableEntry.blendOpAlpha; structured.colorWriteRgb = jStateBitsTableEntry.colorWriteRgb; structured.colorWriteAlpha = jStateBitsTableEntry.colorWriteAlpha; -#ifdef FEATURE_IW5 +#if defined(FEATURE_IW4) || defined(FEATURE_IW5) structured.gammaWrite = jStateBitsTableEntry.gammaWrite; #endif structured.polymodeLine = jStateBitsTableEntry.polymodeLine; diff --git a/src/ObjLoading/Material/JsonMaterialLoader.h.template b/src/ObjLoading/Material/JsonMaterialLoader.h.template index f2b8bc90..ae57b3b6 100644 --- a/src/ObjLoading/Material/JsonMaterialLoader.h.template +++ b/src/ObjLoading/Material/JsonMaterialLoader.h.template @@ -1,4 +1,4 @@ -#options GAME (IW5, T6) +#options GAME (IW4, IW5, T6) #filename "Game/" + GAME + "/Material/JsonMaterialLoader" + GAME + ".h" diff --git a/src/ObjWriting/Game/IW4/Material/AssetDumperMaterial.cpp b/src/ObjWriting/Game/IW4/Material/AssetDumperMaterial.cpp index 19dbc81c..f1a88599 100644 --- a/src/ObjWriting/Game/IW4/Material/AssetDumperMaterial.cpp +++ b/src/ObjWriting/Game/IW4/Material/AssetDumperMaterial.cpp @@ -1,9 +1,22 @@ #include "AssetDumperMaterial.h" #include "DecompilingMaterialDumperIW4.h" +#include "Game/IW4/Material/JsonMaterialWriterIW4.h" +#include "Game/IW4/Material/MaterialConstantZoneState.h" +#include "Material/MaterialCommon.h" + +// #define EXPERIMENTAL_DECOMPILE_MATERIAL 1 using namespace IW4; +void AssetDumperMaterial::DumpPool(AssetDumpingContext& context, AssetPool* pool) +{ + auto* materialConstantState = context.GetZoneAssetDumperState(); + materialConstantState->ExtractNamesFromZone(); + + AbstractAssetDumper::DumpPool(context, pool); +} + bool AssetDumperMaterial::ShouldDump(XAssetInfo* asset) { return true; @@ -11,10 +24,17 @@ bool AssetDumperMaterial::ShouldDump(XAssetInfo* asset) void AssetDumperMaterial::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) { - const auto* material = asset->Asset(); - +#ifdef EXPERIMENTAL_DECOMPILE_MATERIAL if (context.m_gdt) { - DecompileMaterialToGdt(*context.m_gdt, *material, context); + DecompileMaterialToGdt(*context.m_gdt, *asset->Asset(), context); } +#else + const auto assetFile = context.OpenAssetFile(material::GetFileNameForAssetName(asset->m_name)); + + if (!assetFile) + return; + + DumpMaterialAsJson(*assetFile, *asset->Asset(), context); +#endif } diff --git a/src/ObjWriting/Game/IW4/Material/AssetDumperMaterial.h b/src/ObjWriting/Game/IW4/Material/AssetDumperMaterial.h index d55f439e..1d3447f3 100644 --- a/src/ObjWriting/Game/IW4/Material/AssetDumperMaterial.h +++ b/src/ObjWriting/Game/IW4/Material/AssetDumperMaterial.h @@ -7,6 +7,9 @@ namespace IW4 { class AssetDumperMaterial final : public AbstractAssetDumper { + public: + void DumpPool(AssetDumpingContext& context, AssetPool* pool) override; + protected: bool ShouldDump(XAssetInfo* asset) override; void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; diff --git a/src/ObjWriting/Game/IW4/Material/DecompilingMaterialDumperIW4.cpp b/src/ObjWriting/Game/IW4/Material/DecompilingMaterialDumperIW4.cpp index cd05ab8d..ac5d1514 100644 --- a/src/ObjWriting/Game/IW4/Material/DecompilingMaterialDumperIW4.cpp +++ b/src/ObjWriting/Game/IW4/Material/DecompilingMaterialDumperIW4.cpp @@ -564,7 +564,7 @@ namespace }; static inline BlendFuncParameters knownBlendFuncs[]{ - // Only considering passthrough statemap + // Only considering passthrough statemap {BlendFunc_e::ADD, BlendOp_e::ADD, CustomBlendFunc_e::ONE, @@ -601,8 +601,8 @@ namespace CustomBlendFunc_e::UNKNOWN, CustomBlendFunc_e::UNKNOWN}, - // TODO: Enable when using statemaps - // Considering default statemap + // TODO: Enable when using statemaps + // Considering default statemap {BlendFunc_e::ADD, BlendOp_e::ADD, CustomBlendFunc_e::ONE, @@ -624,7 +624,7 @@ namespace BlendOp_e::ADD, CustomBlendFunc_e::INV_DST_ALPHA, CustomBlendFunc_e::ONE }, - // REPLACE matches passthrough statemap + // REPLACE matches passthrough statemap {BlendFunc_e::SCREEN_ADD, BlendOp_e::ADD, CustomBlendFunc_e::INV_DST_COLOR, @@ -666,12 +666,6 @@ namespace m_state_bits_info.m_blend_func = BlendFunc_e::CUSTOM; } - template T StateBitsToEnum(const unsigned input, const size_t mask, const size_t shift) - { - const unsigned value = (input & mask) >> shift; - return value >= (static_cast(T::COUNT) - 1) ? T::UNKNOWN : static_cast(value + 1); - } - void ExamineStateBitsInfo() { if (!m_material.stateBitsTable || m_material.stateBitsCount == 0) @@ -695,37 +689,32 @@ namespace } if (m_state_bits_info.m_custom_blend_op_rgb == BlendOp_e::UNKNOWN) - m_state_bits_info.m_custom_blend_op_rgb = StateBitsToEnum(stateBits.loadBits[0], GFXS0_BLENDOP_RGB_MASK, GFXS0_BLENDOP_RGB_SHIFT); + m_state_bits_info.m_custom_blend_op_rgb = static_cast(stateBits.loadBits.structured.blendOpRgb); if (m_state_bits_info.m_custom_blend_op_alpha == BlendOp_e::UNKNOWN) - m_state_bits_info.m_custom_blend_op_alpha = - StateBitsToEnum(stateBits.loadBits[0], GFXS0_BLENDOP_ALPHA_MASK, GFXS0_BLENDOP_ALPHA_SHIFT); + m_state_bits_info.m_custom_blend_op_alpha = static_cast(stateBits.loadBits.structured.blendOpAlpha); if (m_state_bits_info.m_custom_src_blend_func == CustomBlendFunc_e::UNKNOWN) - m_state_bits_info.m_custom_src_blend_func = - StateBitsToEnum(stateBits.loadBits[0], GFXS0_SRCBLEND_RGB_MASK, GFXS0_SRCBLEND_RGB_SHIFT); + m_state_bits_info.m_custom_src_blend_func = static_cast(stateBits.loadBits.structured.srcBlendRgb); if (m_state_bits_info.m_custom_dst_blend_func == CustomBlendFunc_e::UNKNOWN) - m_state_bits_info.m_custom_dst_blend_func = - StateBitsToEnum(stateBits.loadBits[0], GFXS0_DSTBLEND_RGB_MASK, GFXS0_DSTBLEND_RGB_SHIFT); + m_state_bits_info.m_custom_dst_blend_func = static_cast(stateBits.loadBits.structured.dstBlendRgb); if (m_state_bits_info.m_custom_src_blend_func_alpha == CustomBlendFunc_e::UNKNOWN) - m_state_bits_info.m_custom_src_blend_func_alpha = - StateBitsToEnum(stateBits.loadBits[0], GFXS0_SRCBLEND_ALPHA_MASK, GFXS0_SRCBLEND_ALPHA_SHIFT); + m_state_bits_info.m_custom_src_blend_func_alpha = static_cast(stateBits.loadBits.structured.srcBlendAlpha); if (m_state_bits_info.m_custom_dst_blend_func_alpha == CustomBlendFunc_e::UNKNOWN) - m_state_bits_info.m_custom_dst_blend_func_alpha = - StateBitsToEnum(stateBits.loadBits[0], GFXS0_DSTBLEND_ALPHA_MASK, GFXS0_DSTBLEND_ALPHA_SHIFT); + m_state_bits_info.m_custom_dst_blend_func_alpha = static_cast(stateBits.loadBits.structured.dstBlendAlpha); if (m_state_bits_info.m_alpha_test == AlphaTest_e::UNKNOWN) { - if (stateBits.loadBits[0] & GFXS0_ATEST_DISABLE) + if (stateBits.loadBits.structured.alphaTestDisabled) m_state_bits_info.m_alpha_test = AlphaTest_e::ALWAYS; - else if ((stateBits.loadBits[0] & GFXS0_ATEST_MASK) == GFXS0_ATEST_GE_128) + else if (stateBits.loadBits.structured.alphaTest == GFXS_ALPHA_TEST_GE_128) m_state_bits_info.m_alpha_test = AlphaTest_e::GE128; - else if ((stateBits.loadBits[0] & GFXS0_ATEST_MASK) == GFXS0_ATEST_GT_0) + else if (stateBits.loadBits.structured.alphaTest == GFXS_ALPHA_TEST_GT_0) m_state_bits_info.m_alpha_test = AlphaTest_e::GT0; - else if ((stateBits.loadBits[0] & GFXS0_ATEST_MASK) == GFXS0_ATEST_LT_128) + else if (stateBits.loadBits.structured.alphaTest == GFXS_ALPHA_TEST_LT_128) m_state_bits_info.m_alpha_test = AlphaTest_e::LT128; else assert(false); @@ -733,13 +722,13 @@ namespace if (m_state_bits_info.m_depth_test == DepthTest_e::UNKNOWN) { - if (stateBits.loadBits[1] & GFXS1_DEPTHTEST_DISABLE) + if (stateBits.loadBits.structured.depthTestDisabled) m_state_bits_info.m_depth_test = DepthTest_e::DISABLE; - else if (stateBits.loadBits[1] & GFXS1_DEPTHTEST_LESSEQUAL) + else if (stateBits.loadBits.structured.depthTest == GFXS_DEPTHTEST_LESSEQUAL) m_state_bits_info.m_depth_test = DepthTest_e::LESS_EQUAL; - else if (stateBits.loadBits[1] & GFXS1_DEPTHTEST_LESS) + else if (stateBits.loadBits.structured.depthTest == GFXS_DEPTHTEST_LESS) m_state_bits_info.m_depth_test = DepthTest_e::LESS; - else if (stateBits.loadBits[1] & GFXS1_DEPTHTEST_EQUAL) + else if (stateBits.loadBits.structured.depthTest == GFXS_DEPTHTEST_EQUAL) m_state_bits_info.m_depth_test = DepthTest_e::EQUAL; else m_state_bits_info.m_depth_test = DepthTest_e::ALWAYS; @@ -747,85 +736,82 @@ namespace if (m_state_bits_info.m_depth_write == StateBitsEnabledStatus_e::UNKNOWN) m_state_bits_info.m_depth_write = - (stateBits.loadBits[1] & GFXS1_DEPTHWRITE) ? StateBitsEnabledStatus_e::ENABLED : StateBitsEnabledStatus_e::DISABLED; + stateBits.loadBits.structured.depthWrite ? StateBitsEnabledStatus_e::ENABLED : StateBitsEnabledStatus_e::DISABLED; if (m_state_bits_info.m_cull_face == CullFace_e::UNKNOWN) { - if (stateBits.loadBits[0] & GFXS0_CULL_NONE) + if (stateBits.loadBits.structured.cullFace == GFXS_CULL_NONE) m_state_bits_info.m_cull_face = CullFace_e::NONE; - else if (stateBits.loadBits[0] & GFXS0_CULL_BACK) + else if (stateBits.loadBits.structured.cullFace == GFXS_CULL_BACK) m_state_bits_info.m_cull_face = CullFace_e::BACK; - else if (stateBits.loadBits[0] & GFXS0_CULL_FRONT) + else if (stateBits.loadBits.structured.cullFace == GFXS_CULL_FRONT) m_state_bits_info.m_cull_face = CullFace_e::FRONT; else assert(false); } if (m_state_bits_info.m_polygon_offset == PolygonOffset_e::UNKNOWN) - m_state_bits_info.m_polygon_offset = - StateBitsToEnum(stateBits.loadBits[1], GFXS1_POLYGON_OFFSET_MASK, GFXS1_POLYGON_OFFSET_SHIFT); + m_state_bits_info.m_polygon_offset = static_cast(stateBits.loadBits.structured.polygonOffset); if (m_state_bits_info.m_color_write_rgb == StateBitsEnabledStatus_e::UNKNOWN) + { m_state_bits_info.m_color_write_rgb = - (stateBits.loadBits[0] & GFXS0_COLORWRITE_RGB) ? StateBitsEnabledStatus_e::ENABLED : StateBitsEnabledStatus_e::DISABLED; + stateBits.loadBits.structured.colorWriteRgb ? StateBitsEnabledStatus_e::ENABLED : StateBitsEnabledStatus_e::DISABLED; + } if (m_state_bits_info.m_color_write_alpha == StateBitsEnabledStatus_e::UNKNOWN) + { m_state_bits_info.m_color_write_alpha = - (stateBits.loadBits[0] & GFXS0_COLORWRITE_ALPHA) ? StateBitsEnabledStatus_e::ENABLED : StateBitsEnabledStatus_e::DISABLED; + stateBits.loadBits.structured.colorWriteAlpha ? StateBitsEnabledStatus_e::ENABLED : StateBitsEnabledStatus_e::DISABLED; + } if (m_state_bits_info.m_gamma_write == StateBitsEnabledStatus_e::UNKNOWN) + { m_state_bits_info.m_gamma_write = - (stateBits.loadBits[0] & GFXS0_GAMMAWRITE) ? StateBitsEnabledStatus_e::ENABLED : StateBitsEnabledStatus_e::DISABLED; + stateBits.loadBits.structured.gammaWrite ? StateBitsEnabledStatus_e::ENABLED : StateBitsEnabledStatus_e::DISABLED; + } if (m_state_bits_info.m_stencil_mode == StencilMode_e::UNKNOWN) { - if ((stateBits.loadBits[1] & GFXS1_STENCIL_BACK_ENABLE) == 0 && (stateBits.loadBits[1] & GFXS1_STENCIL_FRONT_ENABLE) == 0) + if (stateBits.loadBits.structured.stencilBackEnabled == 0 && stateBits.loadBits.structured.stencilFrontEnabled == 0) { m_state_bits_info.m_stencil_mode = StencilMode_e::DISABLED; } - else if (stateBits.loadBits[1] & GFXS1_STENCIL_BACK_ENABLE) + else if (stateBits.loadBits.structured.stencilBackEnabled) { - assert(stateBits.loadBits[1] & GFXS1_STENCIL_FRONT_ENABLE); + assert(stateBits.loadBits.structured.stencilFrontEnabled); m_state_bits_info.m_stencil_mode = StencilMode_e::TWO_SIDED; } else { - assert(stateBits.loadBits[1] & GFXS1_STENCIL_FRONT_ENABLE); + assert(stateBits.loadBits.structured.stencilFrontEnabled); m_state_bits_info.m_stencil_mode = StencilMode_e::ONE_SIDED; } } if (m_state_bits_info.m_stencil_front_func == StencilFunc_e::UNKNOWN) - m_state_bits_info.m_stencil_front_func = - StateBitsToEnum(stateBits.loadBits[1], GFXS1_STENCIL_FRONT_FUNC_MASK, GFXS1_STENCIL_FRONT_FUNC_SHIFT); + m_state_bits_info.m_stencil_front_func = static_cast(stateBits.loadBits.structured.stencilFrontFunc); if (m_state_bits_info.m_stencil_front_pass == StencilOp_e::UNKNOWN) - m_state_bits_info.m_stencil_front_pass = - StateBitsToEnum(stateBits.loadBits[1], GFXS1_STENCIL_FRONT_PASS_MASK, GFXS1_STENCIL_FRONT_PASS_SHIFT); + m_state_bits_info.m_stencil_front_pass = static_cast(stateBits.loadBits.structured.stencilFrontPass); if (m_state_bits_info.m_stencil_front_fail == StencilOp_e::UNKNOWN) - m_state_bits_info.m_stencil_front_fail = - StateBitsToEnum(stateBits.loadBits[1], GFXS1_STENCIL_FRONT_FAIL_MASK, GFXS1_STENCIL_FRONT_FAIL_SHIFT); + m_state_bits_info.m_stencil_front_fail = static_cast(stateBits.loadBits.structured.stencilFrontFail); if (m_state_bits_info.m_stencil_front_zfail == StencilOp_e::UNKNOWN) - m_state_bits_info.m_stencil_front_zfail = - StateBitsToEnum(stateBits.loadBits[1], GFXS1_STENCIL_FRONT_ZFAIL_MASK, GFXS1_STENCIL_FRONT_ZFAIL_SHIFT); + m_state_bits_info.m_stencil_front_zfail = static_cast(stateBits.loadBits.structured.stencilFrontZFail); if (m_state_bits_info.m_stencil_back_func == StencilFunc_e::UNKNOWN) - m_state_bits_info.m_stencil_back_func = - StateBitsToEnum(stateBits.loadBits[1], GFXS1_STENCIL_BACK_FUNC_MASK, GFXS1_STENCIL_BACK_FUNC_SHIFT); + m_state_bits_info.m_stencil_back_func = static_cast(stateBits.loadBits.structured.stencilBackFunc); if (m_state_bits_info.m_stencil_back_pass == StencilOp_e::UNKNOWN) - m_state_bits_info.m_stencil_back_pass = - StateBitsToEnum(stateBits.loadBits[1], GFXS1_STENCIL_BACK_PASS_MASK, GFXS1_STENCIL_BACK_PASS_SHIFT); + m_state_bits_info.m_stencil_back_pass = static_cast(stateBits.loadBits.structured.stencilBackPass); if (m_state_bits_info.m_stencil_back_fail == StencilOp_e::UNKNOWN) - m_state_bits_info.m_stencil_back_fail = - StateBitsToEnum(stateBits.loadBits[1], GFXS1_STENCIL_BACK_FAIL_MASK, GFXS1_STENCIL_BACK_FAIL_SHIFT); + m_state_bits_info.m_stencil_back_fail = static_cast(stateBits.loadBits.structured.stencilBackFail); if (m_state_bits_info.m_stencil_back_zfail == StencilOp_e::UNKNOWN) - m_state_bits_info.m_stencil_back_zfail = - StateBitsToEnum(stateBits.loadBits[1], GFXS1_STENCIL_BACK_ZFAIL_MASK, GFXS1_STENCIL_BACK_ZFAIL_SHIFT); + m_state_bits_info.m_stencil_back_zfail = static_cast(stateBits.loadBits.structured.stencilBackZFail); ExamineBlendFunc(); } @@ -857,7 +843,7 @@ namespace if (constant.nameHash == Common::R_HashString("colorTint")) { - m_constants_info.m_color_tint = Eigen::Vector4f(constant.literal); + m_constants_info.m_color_tint = Eigen::Vector4f(constant.literal.v); } else if (constant.nameHash == Common::R_HashString("envMapParms")) { @@ -868,19 +854,19 @@ namespace } else if (constant.nameHash == Common::R_HashString("featherParms")) { - m_constants_info.m_zfeather_depth = constant.literal[1]; + m_constants_info.m_zfeather_depth = constant.literal.y; } else if (constant.nameHash == Common::R_HashString("falloffBeginColor")) { - m_constants_info.m_falloff_begin_color = Eigen::Vector4f(constant.literal); + m_constants_info.m_falloff_begin_color = Eigen::Vector4f(constant.literal.v); } else if (constant.nameHash == Common::R_HashString("falloffEndColor")) { - m_constants_info.m_falloff_end_color = Eigen::Vector4f(constant.literal); + m_constants_info.m_falloff_end_color = Eigen::Vector4f(constant.literal.v); } else if (constant.nameHash == Common::R_HashString("eyeOffsetParms")) { - m_constants_info.m_eye_offset_depth = constant.literal[0]; + m_constants_info.m_eye_offset_depth = constant.literal.x; } else if (constant.nameHash == Common::R_HashString("detailScale")) { @@ -901,21 +887,20 @@ namespace { const auto detailScaleFactorX = static_cast(colorMapTexture->width) / static_cast(detailMapTexture->width); const auto detailScaleFactorY = static_cast(colorMapTexture->height) / static_cast(detailMapTexture->height); - m_constants_info.m_detail_scale = - Eigen::Vector2f(constant.literal[0] / detailScaleFactorX, constant.literal[1] / detailScaleFactorY); + m_constants_info.m_detail_scale = Eigen::Vector2f(constant.literal.x / detailScaleFactorX, constant.literal.y / detailScaleFactorY); } else - m_constants_info.m_detail_scale = Eigen::Vector2f(constant.literal[0], constant.literal[1]); + m_constants_info.m_detail_scale = Eigen::Vector2f(constant.literal.x, constant.literal.y); } else { - m_constants_info.m_detail_scale = Eigen::Vector2f(constant.literal[0], constant.literal[1]); + m_constants_info.m_detail_scale = Eigen::Vector2f(constant.literal.x, constant.literal.y); } } else if (constant.nameHash == Common::R_HashString("flagParms")) { - m_constants_info.m_flag_speed = constant.literal[0]; - m_constants_info.m_flag_phase = constant.literal[1]; + m_constants_info.m_flag_speed = constant.literal.x; + m_constants_info.m_flag_phase = constant.literal.y; } else if (constant.nameHash == Common::R_HashString("falloffParms")) { @@ -927,25 +912,25 @@ namespace } else if (constant.nameHash == Common::R_HashString("distortionScale")) { - m_constants_info.m_distortion_scale = Eigen::Vector2f(constant.literal[0], constant.literal[1]); + m_constants_info.m_distortion_scale = Eigen::Vector2f(constant.literal.x, constant.literal.y); } else if (constant.nameHash == Common::R_HashString("uvAnimParms")) { - m_constants_info.m_uv_scroll_x = constant.literal[0]; - m_constants_info.m_uv_scroll_y = constant.literal[1]; - m_constants_info.m_uv_rotate = constant.literal[2]; + m_constants_info.m_uv_scroll_x = constant.literal.x; + m_constants_info.m_uv_scroll_y = constant.literal.y; + m_constants_info.m_uv_rotate = constant.literal.z; } else if (constant.nameHash == Common::R_HashString("colorObjMin")) { - m_constants_info.m_color_obj_min = Eigen::Vector4f(constant.literal); + m_constants_info.m_color_obj_min = Eigen::Vector4f(constant.literal.v); } else if (constant.nameHash == Common::R_HashString("colorObjMax")) { - m_constants_info.m_color_obj_max = Eigen::Vector4f(constant.literal); + m_constants_info.m_color_obj_max = Eigen::Vector4f(constant.literal.v); } else if (constant.nameHash == Common::R_HashString("waterColor")) { - m_constants_info.m_water_color = Eigen::Vector4f(constant.literal); + m_constants_info.m_water_color = Eigen::Vector4f(constant.literal.v); } else { @@ -1064,45 +1049,45 @@ namespace } TileMode_e tileMode; - if (entry.samplerState & SAMPLER_CLAMP_U && entry.samplerState & SAMPLER_CLAMP_V && entry.samplerState & SAMPLER_CLAMP_W) + if (entry.samplerState.clampU && entry.samplerState.clampV && entry.samplerState.clampW) tileMode = TileMode_e::TILE_BOTH; - else if (entry.samplerState & SAMPLER_CLAMP_U) + else if (entry.samplerState.clampU) tileMode = TileMode_e::TILE_VERTICAL; - else if (entry.samplerState & SAMPLER_CLAMP_V) + else if (entry.samplerState.clampV) tileMode = TileMode_e::TILE_HORIZONTAL; else tileMode = TileMode_e::NO_TILE; auto filter = GdtFilter_e::UNKNOWN; - if ((entry.samplerState & SAMPLER_FILTER_MASK) == SAMPLER_FILTER_ANISO2X) + if (entry.samplerState.filter == TEXTURE_FILTER_ANISO2X) { - if (entry.samplerState & SAMPLER_MIPMAP_NEAREST) + if (entry.samplerState.mipMap == SAMPLER_MIPMAP_ENUM_NEAREST) filter = GdtFilter_e::MIP_2X_BILINEAR; - else if (entry.samplerState & SAMPLER_MIPMAP_LINEAR) + else if (entry.samplerState.mipMap == SAMPLER_MIPMAP_ENUM_LINEAR) filter = GdtFilter_e::MIP_2X_TRILINEAR; } - else if ((entry.samplerState & SAMPLER_FILTER_MASK) == SAMPLER_FILTER_ANISO4X) + else if (entry.samplerState.filter == TEXTURE_FILTER_ANISO4X) { - if (entry.samplerState & SAMPLER_MIPMAP_NEAREST) + if (entry.samplerState.mipMap == SAMPLER_MIPMAP_ENUM_NEAREST) filter = GdtFilter_e::MIP_4X_BILINEAR; - else if (entry.samplerState & SAMPLER_MIPMAP_LINEAR) + else if (entry.samplerState.mipMap == SAMPLER_MIPMAP_ENUM_LINEAR) filter = GdtFilter_e::MIP_4X_TRILINEAR; } - else if ((entry.samplerState & SAMPLER_FILTER_MASK) == SAMPLER_FILTER_NEAREST) + else if (entry.samplerState.filter == TEXTURE_FILTER_NEAREST) { - assert((entry.samplerState & SAMPLER_MIPMAP_MASK) == SAMPLER_MIPMAP_DISABLED); + assert(entry.samplerState.mipMap == SAMPLER_MIPMAP_ENUM_DISABLED); filter = GdtFilter_e::NOMIP_NEAREST; } - else if ((entry.samplerState & SAMPLER_FILTER_MASK) == SAMPLER_FILTER_LINEAR) + else if (entry.samplerState.filter == TEXTURE_FILTER_LINEAR) { - assert((entry.samplerState & SAMPLER_MIPMAP_MASK) == SAMPLER_MIPMAP_DISABLED); + assert(entry.samplerState.mipMap == SAMPLER_MIPMAP_ENUM_DISABLED); filter = GdtFilter_e::NOMIP_BILINEAR; } assert(filter != GdtFilter_e::UNKNOWN); if (filter == GdtFilter_e::UNKNOWN) { - std::cout << "Unknown filter/mipmap combination: " << entry.samplerState << "\n"; + std::cout << std::format("Unknown filter/mipmap combination: {} {}\n", entry.samplerState.filter, entry.samplerState.mipMap); continue; } diff --git a/src/ObjWriting/Game/IW4/Material/MaterialConstantZoneState.cpp b/src/ObjWriting/Game/IW4/Material/MaterialConstantZoneState.cpp new file mode 100644 index 00000000..e06f9697 --- /dev/null +++ b/src/ObjWriting/Game/IW4/Material/MaterialConstantZoneState.cpp @@ -0,0 +1,236 @@ +#include "MaterialConstantZoneState.h" + +#include "Game/IW4/CommonIW4.h" +#include "Game/IW4/GameAssetPoolIW4.h" +#include "Game/IW4/GameIW4.h" +#include "ObjWriting.h" + +namespace IW4 +{ + const char* KNOWN_CONSTANT_NAMES[]{ + "worldViewProjectionMatrix", + "worldViewMatrix2", + "worldViewMatrix1", + "worldViewMatrix", + "worldOutdoorLookupMatrix", + "worldMatrix", + "waterColor", + "viewportDimensions", + "viewProjectionMatrix", + "uvScale", + "uvAnimParms", + "thermalColorOffset", + "sunShadowmapPixelAdjust", + "ssaoParms", + "spotShadowmapPixelAdjust", + "shadowmapSwitchPartition", + "shadowmapScale", + "shadowmapPolygonOffset", + "shadowLookupMatrix", + "renderTargetSize", + "renderSourceSize", + "projectionMatrix", + "playlistPopulationParams", + "pixelCostFracs", + "pixelCostDecode", + "particleCloudSparkColor2", + "particleCloudSparkColor1", + "particleCloudSparkColor0", + "particleCloudMatrix2", + "particleCloudMatrix1", + "particleCloudMatrix", + "particleCloudColor", + "outdoorFeatherParms", + "oceanUVAnimParmPaintedFoam", + "oceanUVAnimParmOctave2", + "oceanUVAnimParmOctave1", + "oceanUVAnimParmOctave0", + "oceanUVAnimParmFoam", + "oceanUVAnimParmDetail1", + "oceanUVAnimParmDetail0", + "oceanScrollParms", + "oceanMiscParms", + "oceanFoamParms", + "oceanAmplitude", + "materialColor", + "lightprobeAmbient", + "lightingLookupScale", + "lightSpotFactors", + "lightSpotDir", + "lightSpecular", + "lightPosition", + "lightFalloffPlacement", + "lightDiffuse", + "inverseWorldViewMatrix", + "inverseViewProjectionMatrix", + "inverseTransposeWorldViewMatrix", + "heatMapDetail", + "glowSetup", + "glowApply", + "gameTime", + "fullscreenDistortion", + "fogSunDir", + "fogSunConsts", + "fogSunColorLinear", + "fogSunColorGamma", + "fogConsts", + "fogColorLinear", + "fogColorGamma", + "flagParms", + "filterTap", + "featherParms", + "falloffParms", + "falloffEndColor", + "falloffBeginColor", + "fadeEffect", + "eyeOffsetParms", + "eyeOffset", + "envMapParms", + "dustTint", + "dustParms", + "dustEyeParms", + "dofRowDelta", + "dofLerpScale", + "dofLerpBias", + "dofEquationViewModelAndFarBlur", + "dofEquationScene", + "distortionScale", + "detailScale", + "depthFromClip", + "debugBumpmap", + "colorTintQuadraticDelta", + "colorTintDelta", + "colorTintBase", + "colorSaturationR", + "colorSaturationG", + "colorSaturationB", + "colorObjMin", + "colorObjMax", + "colorMatrixR", + "colorMatrixG", + "colorMatrixB", + "colorBias", + "codeMeshArg", + "clipSpaceLookupScale", + "clipSpaceLookupOffset", + "baseLightingCoords", + }; + + const char* KNOWN_TEXTURE_DEF_NAMES[]{ + "attenuation", + "attenuationSampler", + "cinematicA", + "cinematicASampler", + "cinematicCb", + "cinematicCbSampler", + "cinematicCr", + "cinematicCrSampler", + "cinematicY", + "cinematicYSampler", + "colorMap", + "colorMap1", + "colorMap2", + "colorMapPostSun", + "colorMapPostSunSampler", + "colorMapSampler", + "colorMapSampler1", + "colorMapSampler2", + "cucoloris", + "cucolorisSampler", + "detailMap", + "detailMapSampler", + "dust", + "dustSampler", + "fadeMap", + "fadeMapSampler", + "floatZ", + "floatZSampler", + "grainMap", + "grainMapSampler", + "halfParticleColor", + "halfParticleColorSampler", + "halfParticleDepth", + "halfParticleDepthSampler", + "heatmap", + "heatmapSampler", + "lightmapPrimary", + "lightmapSamplerPrimary", + "lightmapSamplerSecondary", + "lightmapSecondary", + "lookupMap", + "lookupMapSampler", + "modelLighting", + "modelLightingSampler", + "normalMap", + "normalMapSampler", + "oceanColorRamp", + "oceanColorRampSampler", + "oceanDetailNormal", + "oceanDetailNormalSampler", + "oceanDisplacement", + "oceanDisplacementSampler", + "oceanEnv", + "oceanEnvSampler", + "oceanFoam", + "oceanFoamSampler", + "oceanHeightNormal", + "oceanHeightNormalSampler", + "oceanPaintedFoam", + "oceanPaintedFoamSampler", + "outdoorMap", + "outdoorMapSampler", + "population", + "populationSampler", + "reflectionProbe", + "reflectionProbeSampler", + "shadowmapSamplerSpot", + "shadowmapSamplerSun", + "shadowmapSpot", + "shadowmapSun", + "skyMap", + "skyMapSampler", + "specularMap", + "specularMapSampler", + "ssao", + "ssaoSampler", + "worldMap", + "worldMapSampler", + }; + + void MaterialConstantZoneState::ExtractNamesFromZoneInternal() + { + for (const auto* zone : IGame::GetGameById(GameId::IW5)->GetZones()) + { + const auto* iw5AssetPools = dynamic_cast(zone->m_pools.get()); + if (!iw5AssetPools) + return; + + for (const auto* vertexShaderAsset : *iw5AssetPools->m_material_vertex_shader) + { + const auto* vertexShader = vertexShaderAsset->Asset(); + if (ShouldDumpFromStruct(vertexShader)) + ExtractNamesFromShader(vertexShader->prog.loadDef.program, static_cast(vertexShader->prog.loadDef.programSize) * sizeof(uint32_t)); + } + + for (const auto* pixelShaderAsset : *iw5AssetPools->m_material_pixel_shader) + { + const auto* pixelShader = pixelShaderAsset->Asset(); + if (ShouldDumpFromStruct(pixelShader)) + ExtractNamesFromShader(pixelShader->prog.loadDef.program, static_cast(pixelShader->prog.loadDef.programSize) * sizeof(uint32_t)); + } + } + } + + void MaterialConstantZoneState::AddStaticKnownNames() + { + for (const auto* knownConstantName : KNOWN_CONSTANT_NAMES) + AddConstantName(knownConstantName); + for (const auto* knownTextureDefName : KNOWN_TEXTURE_DEF_NAMES) + AddTextureDefName(knownTextureDefName); + } + + unsigned MaterialConstantZoneState::HashString(const std::string& str) + { + return Common::R_HashString(str.c_str()); + } +} // namespace IW4 diff --git a/src/ObjWriting/Game/IW4/Material/MaterialConstantZoneState.h b/src/ObjWriting/Game/IW4/Material/MaterialConstantZoneState.h new file mode 100644 index 00000000..edde10f8 --- /dev/null +++ b/src/ObjWriting/Game/IW4/Material/MaterialConstantZoneState.h @@ -0,0 +1,16 @@ +#pragma once + +#include "Material/AbstractMaterialConstantZoneState.h" + +#include + +namespace IW4 +{ + class MaterialConstantZoneState final : public AbstractMaterialConstantZoneStateDx9 + { + protected: + void ExtractNamesFromZoneInternal() override; + void AddStaticKnownNames() override; + unsigned HashString(const std::string& str) override; + }; +} // namespace IW4 diff --git a/src/ObjWriting/Material/JsonMaterialWriter.cpp.template b/src/ObjWriting/Material/JsonMaterialWriter.cpp.template index 2bc4047d..d0b83455 100644 --- a/src/ObjWriting/Material/JsonMaterialWriter.cpp.template +++ b/src/ObjWriting/Material/JsonMaterialWriter.cpp.template @@ -1,8 +1,12 @@ -#options GAME (IW5, T6) +#options GAME (IW4, IW5, T6) #filename "Game/" + GAME + "/Material/JsonMaterialWriter" + GAME + ".cpp" -#if GAME == "IW5" +#if GAME == "IW4" +#define FEATURE_IW4 +#define HAS_WATER +#define GAME_LOWER "iw4" +#elif GAME == "IW5" #define FEATURE_IW5 #define HAS_WATER #define GAME_LOWER "iw5" @@ -214,7 +218,7 @@ namespace assert(structured.alphaTestDisabled || structured.alphaTest == GFXS_ALPHA_TEST_GT_0 -#ifdef FEATURE_IW5 +#if defined(FEATURE_IW4) || defined(FEATURE_IW5) || structured.alphaTest == GFXS_ALPHA_TEST_LT_128 #endif || structured.alphaTest == GFXS_ALPHA_TEST_GE_128); @@ -222,7 +226,7 @@ namespace jStateBitsTableEntry.alphaTest = JsonAlphaTest::DISABLED; else if (structured.alphaTest == GFXS_ALPHA_TEST_GT_0) jStateBitsTableEntry.alphaTest = JsonAlphaTest::GT0; -#ifdef FEATURE_IW5 +#if defined(FEATURE_IW4) || defined(FEATURE_IW5) else if (structured.alphaTest == GFXS_ALPHA_TEST_LT_128) jStateBitsTableEntry.alphaTest = JsonAlphaTest::LT128; #endif @@ -246,7 +250,7 @@ namespace jStateBitsTableEntry.blendOpAlpha = static_cast(structured.blendOpAlpha); jStateBitsTableEntry.colorWriteRgb = structured.colorWriteRgb; jStateBitsTableEntry.colorWriteAlpha = structured.colorWriteAlpha; -#ifdef FEATURE_IW5 +#if defined(FEATURE_IW4) || defined(FEATURE_IW5) jStateBitsTableEntry.gammaWrite = structured.gammaWrite; #endif jStateBitsTableEntry.polymodeLine = structured.polymodeLine; diff --git a/src/ObjWriting/Material/JsonMaterialWriter.h.template b/src/ObjWriting/Material/JsonMaterialWriter.h.template index cbc6d0a4..d44748db 100644 --- a/src/ObjWriting/Material/JsonMaterialWriter.h.template +++ b/src/ObjWriting/Material/JsonMaterialWriter.h.template @@ -1,4 +1,4 @@ -#options GAME (IW5, T6) +#options GAME (IW4, IW5, T6) #filename "Game/" + GAME + "/Material/JsonMaterialWriter" + GAME + ".h"