diff --git a/src/ObjCommon/Game/IW4/MaterialConstantsIW4.h b/src/ObjCommon/Game/IW4/MaterialConstantsIW4.h index 57eab259..01021bb8 100644 --- a/src/ObjCommon/Game/IW4/MaterialConstantsIW4.h +++ b/src/ObjCommon/Game/IW4/MaterialConstantsIW4.h @@ -123,15 +123,23 @@ namespace IW4 COUNT }; + constexpr auto GDT_BLEND_FUNC_UNKNOWN = ""; + constexpr auto GDT_BLEND_FUNC_CUSTOM = "Custom"; + constexpr auto GDT_BLEND_FUNC_REPLACE = "Replace*"; + constexpr auto GDT_BLEND_FUNC_BLEND = "Blend"; + constexpr auto GDT_BLEND_FUNC_ADD = "Add"; + constexpr auto GDT_BLEND_FUNC_MULTIPLY = "Multiply"; + constexpr auto GDT_BLEND_FUNC_SCREEN_ADD = "Screen Add"; + inline const char* GdtBlendFuncNames[] { - "", - "Custom", - "Replace*", - "Blend", - "Add", - "Multiply", - "Screen Add" + GDT_BLEND_FUNC_UNKNOWN, + GDT_BLEND_FUNC_CUSTOM, + GDT_BLEND_FUNC_REPLACE, + GDT_BLEND_FUNC_BLEND, + GDT_BLEND_FUNC_ADD, + GDT_BLEND_FUNC_MULTIPLY, + GDT_BLEND_FUNC_SCREEN_ADD }; static_assert(std::extent_v == static_cast(BlendFunc_e::COUNT)); @@ -148,15 +156,23 @@ namespace IW4 COUNT }; + constexpr auto GDT_BLEND_OP_UNKNOWN = ""; + constexpr auto GDT_BLEND_OP_DISABLE = "Disable"; + constexpr auto GDT_BLEND_OP_ADD = "Add*"; + constexpr auto GDT_BLEND_OP_SUBTRACT = "Subtract"; + constexpr auto GDT_BLEND_OP_REV_SUBTRACT = "RevSubtract"; + constexpr auto GDT_BLEND_OP_MIN = "Min"; + constexpr auto GDT_BLEND_OP_MAX = "Max"; + inline const char* GdtBlendOpNames[] { - "", - "Disable", - "Add*", - "Subtract", - "RevSubtract", - "Min", - "Max" + GDT_BLEND_OP_UNKNOWN, + GDT_BLEND_OP_DISABLE, + GDT_BLEND_OP_ADD, + GDT_BLEND_OP_SUBTRACT, + GDT_BLEND_OP_REV_SUBTRACT, + GDT_BLEND_OP_MIN, + GDT_BLEND_OP_MAX }; static_assert(std::extent_v == static_cast(BlendOp_e::COUNT)); @@ -206,13 +222,19 @@ namespace IW4 COUNT }; + constexpr auto GDT_ALPHA_TEST_UNKNOWN = ""; + constexpr auto GDT_ALPHA_TEST_ALWAYS = "Always*"; + constexpr auto GDT_ALPHA_TEST_GT0 = "GT0"; + constexpr auto GDT_ALPHA_TEST_LT128 = "LT128"; + constexpr auto GDT_ALPHA_TEST_GE128 = "GE128"; + inline const char* GdtAlphaTestNames[] { - "", - "Always*", - "GT0", - "LT128", - "GE128" + GDT_ALPHA_TEST_UNKNOWN, + GDT_ALPHA_TEST_ALWAYS, + GDT_ALPHA_TEST_GT0, + GDT_ALPHA_TEST_LT128, + GDT_ALPHA_TEST_GE128 }; static_assert(std::extent_v == static_cast(AlphaTest_e::COUNT)); diff --git a/src/ObjLoading/Game/IW4/AssetLoaders/AssetLoaderMaterial.cpp b/src/ObjLoading/Game/IW4/AssetLoaders/AssetLoaderMaterial.cpp index 723512f8..1addca29 100644 --- a/src/ObjLoading/Game/IW4/AssetLoaders/AssetLoaderMaterial.cpp +++ b/src/ObjLoading/Game/IW4/AssetLoaders/AssetLoaderMaterial.cpp @@ -23,7 +23,8 @@ namespace IW4 : AbstractGdtEntryReader(entry), m_memory(memory), m_manager(manager), - m_material(nullptr) + m_material(nullptr), + m_base_statebits{} { } @@ -325,7 +326,7 @@ namespace IW4 if (endPtr != &sortKey[sortKey.size()]) { std::ostringstream ss; - ss << "Invalid sortkey value: \"" << sortKey << "\""; + ss << "Invalid sort value: \"" << sortKey << "\""; throw GdtReadingException(ss.str()); } @@ -339,9 +340,118 @@ namespace IW4 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(); + 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 + { + std::ostringstream ss; + ss << "Invalid alphatest value: \"" << alphaTest << "\""; + throw GdtReadingException(ss.str()); + } + } + + void blendfunc_template() + { + const auto blendFunc = ReadStringProperty("blendFunc"); + + if (blendFunc == GDT_BLEND_FUNC_ADD) + { + SetBlendFunc(BlendOp_e::ADD, CustomBlendFunc_e::ONE, CustomBlendFunc_e::ONE); + SetSeparateAlphaBlendFunc(BlendOp_e::DISABLE, CustomBlendFunc_e::ONE, CustomBlendFunc_e::ZERO); + } + else if (blendFunc == GDT_BLEND_FUNC_BLEND) + { + SetBlendFunc(BlendOp_e::ADD, CustomBlendFunc_e::SRC_ALPHA, CustomBlendFunc_e::INV_SRC_ALPHA); + SetSeparateAlphaBlendFunc(BlendOp_e::DISABLE, CustomBlendFunc_e::ONE, CustomBlendFunc_e::ZERO); + } + else if (blendFunc == GDT_BLEND_FUNC_MULTIPLY) + { + SetBlendFunc(BlendOp_e::ADD, CustomBlendFunc_e::ZERO, CustomBlendFunc_e::SRC_COLOR); + SetSeparateAlphaBlendFunc(BlendOp_e::DISABLE, CustomBlendFunc_e::ONE, CustomBlendFunc_e::ZERO); + } + else if (blendFunc == GDT_BLEND_FUNC_REPLACE) + { + SetBlendFunc(BlendOp_e::DISABLE, CustomBlendFunc_e::ONE, CustomBlendFunc_e::ZERO); + SetSeparateAlphaBlendFunc(BlendOp_e::DISABLE, CustomBlendFunc_e::ONE, CustomBlendFunc_e::ZERO); + } + else if (blendFunc == GDT_BLEND_FUNC_SCREEN_ADD) + { + SetBlendFunc(BlendOp_e::ADD, CustomBlendFunc_e::INV_DST_ALPHA, CustomBlendFunc_e::ONE); + SetSeparateAlphaBlendFunc(BlendOp_e::DISABLE, CustomBlendFunc_e::ONE, CustomBlendFunc_e::ZERO); + } + else if (blendFunc == GDT_BLEND_FUNC_CUSTOM) + { + const auto customBlendOpRgb = ReadEnumProperty("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(static_cast(customBlendOpRgb), static_cast(srcCustomBlendFunc), static_cast(destCustomBlendFunc)); + SetSeparateAlphaBlendFunc(static_cast(customBlendOpAlpha), static_cast(srcCustomBlendFuncAlpha), + static_cast(destCustomBlendFuncAlpha)); + } + else + { + std::ostringstream ss; + ss << "Invalid blendfunc value: \"" << blendFunc << "\""; + throw GdtReadingException(ss.str()); + } + } + + 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(static_cast(colorWriteRed), static_cast(colorWriteGreen), static_cast(colorWriteBlue), + static_cast(colorWriteAlpha)); + } + + void cullface_template() + { + } + + void depthtest_template() + { + } + + void depthwrite_template() + { + } + + void polygonoffset_template() + { + } + + void stencil_template() { } @@ -389,6 +499,103 @@ namespace IW4 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_statebits.loadBits[0] |= GFXS0_ATEST_DISABLE; + break; + + case AlphaTest_e::GT0: + m_base_statebits.loadBits[0] |= GFXS0_ATEST_GT_0; + break; + + case AlphaTest_e::LT128: + m_base_statebits.loadBits[0] |= GFXS0_ATEST_LT_128; + break; + + case AlphaTest_e::GE128: + m_base_statebits.loadBits[0] |= GFXS0_ATEST_GE_128; + break; + + case AlphaTest_e::UNKNOWN: + default: + std::ostringstream ss; + ss << "Unknown alphatest value: \"" << static_cast(alphaTest) << "\""; + throw GdtReadingException(ss.str()); + } + } + + void SetBlendFunc(BlendOp_e blendOp, CustomBlendFunc_e srcFunc, CustomBlendFunc_e destFunc) + { + if (blendOp == BlendOp_e::UNKNOWN || srcFunc == CustomBlendFunc_e::UNKNOWN || destFunc == CustomBlendFunc_e::UNKNOWN) + { + std::ostringstream ss; + ss << "Unknown SeparateAlphaBlendFunc values: \"\""; + throw GdtReadingException(ss.str()); + } + + m_base_statebits.loadBits[0] &= ~GFXS0_BLENDOP_RGB_MASK; + m_base_statebits.loadBits[0] |= ((static_cast(blendOp) - 1) >> GFXS0_BLENDOP_RGB_SHIFT) & GFXS0_BLENDOP_RGB_MASK; + + m_base_statebits.loadBits[0] &= ~GFXS0_SRCBLEND_RGB_MASK; + m_base_statebits.loadBits[0] |= ((static_cast(srcFunc) - 1) >> GFXS0_SRCBLEND_RGB_SHIFT) & GFXS0_SRCBLEND_RGB_MASK; + + m_base_statebits.loadBits[0] &= ~GFXS0_DSTBLEND_RGB_MASK; + m_base_statebits.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_statebits.loadBits[0] &= ~GFXS0_BLENDOP_ALPHA_MASK; + m_base_statebits.loadBits[0] |= ((static_cast(blendOp) - 1) >> GFXS0_BLENDOP_ALPHA_SHIFT) & GFXS0_BLENDOP_ALPHA_MASK; + + m_base_statebits.loadBits[0] &= ~GFXS0_SRCBLEND_ALPHA_MASK; + m_base_statebits.loadBits[0] |= ((static_cast(srcFunc) - 1) >> GFXS0_SRCBLEND_ALPHA_SHIFT) & GFXS0_SRCBLEND_ALPHA_MASK; + + m_base_statebits.loadBits[0] &= ~GFXS0_DSTBLEND_ALPHA_MASK; + m_base_statebits.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) + { + std::ostringstream ss; + ss << "Invalid ColorWrite values: values for rgb must match"; + throw GdtReadingException(ss.str()); + } + + m_base_statebits.loadBits[0] &= ~GFXS0_COLORWRITE_MASK; + if (colorWriteRed == StateBitsEnabledStatus_e::ENABLED) + m_base_statebits.loadBits[0] |= GFXS0_COLORWRITE_RGB; + if (colorWriteAlpha == StateBitsEnabledStatus_e::ENABLED) + m_base_statebits.loadBits[0] |= GFXS0_COLORWRITE_ALPHA; + } + void FinalizeMaterial() const { if (!m_textures.empty()) @@ -404,11 +611,30 @@ namespace IW4 } } + static size_t GetIndexForString(const std::string& propertyName, const std::string& value, const char** validValuesArray, const size_t validValuesArraySize) + { + for (auto i = 0u; i < validValuesArraySize; i++) + { + if (validValuesArray[i] == value) + return i; + } + + std::ostringstream ss; + ss << "Unknown " << propertyName << " value: \"" << value << "\""; + throw GdtReadingException(ss.str()); + } + + size_t ReadEnumProperty(const std::string& propertyName, const char** validValuesArray, const size_t validValuesArraySize) const + { + return GetIndexForString(propertyName, ReadStringProperty(propertyName), validValuesArray, validValuesArraySize); + } + MemoryManager* m_memory; IAssetLoadingManager* m_manager; std::vector m_dependencies; Material* m_material; + GfxStateBits m_base_statebits; std::vector m_textures; }; }