diff --git a/src/Common/Game/IW4/IW4_Assets.h b/src/Common/Game/IW4/IW4_Assets.h index 392b13f5..bd82c731 100644 --- a/src/Common/Game/IW4/IW4_Assets.h +++ b/src/Common/Game/IW4/IW4_Assets.h @@ -627,6 +627,17 @@ namespace IW4 water_t* water; }; + enum MaterialTextureFilter + { + TEXTURE_FILTER_DISABLED = 0x0, + TEXTURE_FILTER_NEAREST = 0x1, + TEXTURE_FILTER_LINEAR = 0x2, + TEXTURE_FILTER_ANISO2X = 0x3, + TEXTURE_FILTER_ANISO4X = 0x4, + + TEXTURE_FILTER_COUNT + }; + enum SamplerStateBits_e { SAMPLER_FILTER_SHIFT = 0x0, diff --git a/src/ObjCommon/Game/IW4/MaterialConstantsIW4.h b/src/ObjCommon/Game/IW4/MaterialConstantsIW4.h index 66f81797..3f198b2c 100644 --- a/src/ObjCommon/Game/IW4/MaterialConstantsIW4.h +++ b/src/ObjCommon/Game/IW4/MaterialConstantsIW4.h @@ -409,6 +409,52 @@ namespace IW4 }; static_assert(std::extent_v == static_cast(StencilOp_e::COUNT)); + enum class TileMode_e + { + UNKNOWN, + TILE_BOTH, + TILE_HORIZONTAL, + TILE_VERTICAL, + NO_TILE, + + COUNT + }; + + inline const char* GdtTileModeNames[] + { + "", + "tile both*", + "tile horizontal", + "tile vertical", + "no tile" + }; + static_assert(std::extent_v == static_cast(TileMode_e::COUNT)); + + enum class GdtFilter_e + { + UNKNOWN, + MIP_2X_BILINEAR, + MIP_4X_BILINEAR, + MIP_2X_TRILINEAR, + MIP_4X_TRILINEAR, + NOMIP_NEAREST, + NOMIP_BILINEAR, + + COUNT + }; + + static const char* GdtSamplerFilterNames[] + { + "", + "mip standard (2x bilinear)*", + "mip expensive (4x bilinear)", + "mip more expensive (2x trilinear)", + "mip most expensive (4x trilinear)", + "nomip nearest", + "nomip bilinear" + }; + static_assert(std::extent_v == static_cast(GdtFilter_e::COUNT)); + enum GdtMaterialType { MATERIAL_TYPE_UNKNOWN, diff --git a/src/ObjCommon/Game/IW4/TechsetConstantsIW4.h b/src/ObjCommon/Game/IW4/TechsetConstantsIW4.h index 11f1abab..e3f0bc17 100644 --- a/src/ObjCommon/Game/IW4/TechsetConstantsIW4.h +++ b/src/ObjCommon/Game/IW4/TechsetConstantsIW4.h @@ -526,61 +526,38 @@ namespace IW4 }; static_assert(std::extent_v == MTL_TYPE_COUNT); - static constexpr std::pair KnownMaterialSource(const char* name) + struct KnownMaterialTextureMap + { + const char* m_name; + const char* m_additional_property_suffix; + }; + + static constexpr std::pair MakeKnownTextureMap(const char* name, const char* additionalPropertySuffix) + { + return std::make_pair(Common::R_HashString(name, 0u), KnownMaterialTextureMap{name, additionalPropertySuffix}); + } + + inline std::unordered_map knownTextureMaps + { + MakeKnownTextureMap("colorMap", "Color"), + MakeKnownTextureMap("colorMap0", "Color00"), + MakeKnownTextureMap("colorMap1", "Color01"), + MakeKnownTextureMap("colorMap2", "Color02"), + MakeKnownTextureMap("normalMap", "Normal"), + MakeKnownTextureMap("specularMap", "Specular"), + MakeKnownTextureMap("detailMap", "Detail"), + }; + + static constexpr std::pair MakeKnownConstantName(const char* name) { return std::make_pair(Common::R_HashString(name, 0u), name); } - inline std::unordered_map knownMaterialSourceNames + inline std::unordered_map knownConstantNames { - KnownMaterialSource("colorMap"), - KnownMaterialSource("colorMap0"), - KnownMaterialSource("colorMap1"), - KnownMaterialSource("colorMap2"), - KnownMaterialSource("colorMap3"), - KnownMaterialSource("colorMap4"), - KnownMaterialSource("colorMap5"), - KnownMaterialSource("colorMap6"), - KnownMaterialSource("colorMap7"), - KnownMaterialSource("normalMap"), - KnownMaterialSource("normalMap0"), - KnownMaterialSource("normalMap1"), - KnownMaterialSource("normalMap2"), - KnownMaterialSource("normalMap3"), - KnownMaterialSource("normalMap4"), - KnownMaterialSource("normalMap5"), - KnownMaterialSource("normalMap6"), - KnownMaterialSource("normalMap7"), - KnownMaterialSource("specularMap"), - KnownMaterialSource("specularMap0"), - KnownMaterialSource("specularMap1"), - KnownMaterialSource("specularMap2"), - KnownMaterialSource("specularMap3"), - KnownMaterialSource("specularMap4"), - KnownMaterialSource("specularMap5"), - KnownMaterialSource("specularMap6"), - KnownMaterialSource("specularMap7"), - KnownMaterialSource("detailMap"), - KnownMaterialSource("detailMap0"), - KnownMaterialSource("detailMap1"), - KnownMaterialSource("detailMap2"), - KnownMaterialSource("detailMap3"), - KnownMaterialSource("detailMap4"), - KnownMaterialSource("detailMap5"), - KnownMaterialSource("detailMap6"), - KnownMaterialSource("detailMap7"), - KnownMaterialSource("attenuationMap"), - KnownMaterialSource("attenuationMap0"), - KnownMaterialSource("attenuationMap1"), - KnownMaterialSource("attenuationMap2"), - KnownMaterialSource("attenuationMap3"), - KnownMaterialSource("attenuationMap4"), - KnownMaterialSource("attenuationMap5"), - KnownMaterialSource("attenuationMap6"), - KnownMaterialSource("attenuationMap7"), - KnownMaterialSource("distortionScale"), - KnownMaterialSource("eyeOffsetParms"), - KnownMaterialSource("falloffBeginColor"), - KnownMaterialSource("falloffEndColor"), + MakeKnownConstantName("distortionScale"), + MakeKnownConstantName("eyeOffsetParms"), + MakeKnownConstantName("falloffBeginColor"), + MakeKnownConstantName("falloffEndColor"), }; } diff --git a/src/ObjLoading/Game/IW4/AssetLoaders/AssetLoaderMaterial.cpp b/src/ObjLoading/Game/IW4/AssetLoaders/AssetLoaderMaterial.cpp index 97eb90e5..a2d9de3e 100644 --- a/src/ObjLoading/Game/IW4/AssetLoaders/AssetLoaderMaterial.cpp +++ b/src/ObjLoading/Game/IW4/AssetLoaders/AssetLoaderMaterial.cpp @@ -142,9 +142,11 @@ namespace IW4 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", TS_2D, colorMapName); + AddMapTexture("colorMap", tileColor, filterColor, TS_2D, colorMapName); else throw GdtReadingException("ColorMap may not be blank in 2d materials"); } @@ -658,15 +660,59 @@ namespace IW4 return m_base_statebits; } - void AddMapTexture(const std::string& typeName, const TextureSemantic semantic, const std::string& textureName) + 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; // TODO + 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 = reinterpret_cast*>(m_manager->LoadDependency(ASSET_TYPE_IMAGE, textureName)); if (image == nullptr) @@ -1015,7 +1061,7 @@ bool AssetLoaderMaterial::LoadFromGdt(const std::string& assetName, IGdtQueryabl if (loader.Load()) manager->AddAsset(ASSET_TYPE_MATERIAL, assetName, loader.GetMaterial(), loader.GetDependencies(), std::vector()); } - catch(const SkipMaterialException&) + catch (const SkipMaterialException&) { return false; } diff --git a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperMaterial.cpp b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperMaterial.cpp index 51e3d214..9ec38c23 100644 --- a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperMaterial.cpp +++ b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperMaterial.cpp @@ -19,6 +19,7 @@ using namespace IW4; using json = nlohmann::json; +using namespace std::string_literals; namespace IW4 { @@ -136,10 +137,10 @@ namespace IW4 {"semantic", ArrayEntry(semanticNames, entry.semantic)} }; - const auto knownMaterialSourceName = knownMaterialSourceNames.find(entry.nameHash); - if (knownMaterialSourceName != knownMaterialSourceNames.end()) + const auto knownMaterialSourceName = knownTextureMaps.find(entry.nameHash); + if (knownMaterialSourceName != knownTextureMaps.end()) { - jEntry["name"] = knownMaterialSourceName->second; + jEntry["name"] = knownMaterialSourceName->second.m_name; } else { @@ -191,8 +192,8 @@ namespace IW4 } else { - const auto knownMaterialSourceName = knownMaterialSourceNames.find(entry.nameHash); - if (knownMaterialSourceName != knownMaterialSourceNames.end()) + const auto knownMaterialSourceName = knownConstantNames.find(entry.nameHash); + if (knownMaterialSourceName != knownConstantNames.end()) { jEntry["name"] = knownMaterialSourceName->second; } @@ -1351,8 +1352,8 @@ namespace IW4 for (auto i = 0u; i < m_material->textureCount; i++) { const auto& entry = m_material->textureTable[i]; - const auto knownMaterialSourceName = knownMaterialSourceNames.find(entry.nameHash); - if (knownMaterialSourceName == knownMaterialSourceNames.end()) + const auto knownMaterialSourceName = knownTextureMaps.find(entry.nameHash); + if (knownMaterialSourceName == knownTextureMaps.end()) { assert(false); std::cout << "Unknown material texture source name hash: 0x" << std::hex << entry.nameHash << " (" << entry.nameStart << "..." << entry.nameEnd << ")\n"; @@ -1373,7 +1374,52 @@ namespace IW4 imageName = AssetName(entry.u.water->image->name); } - SetValue(knownMaterialSourceName->second, imageName); + TileMode_e tileMode; + if (entry.samplerState & SAMPLER_CLAMP_U && entry.samplerState & SAMPLER_CLAMP_V && entry.samplerState & SAMPLER_CLAMP_W) + tileMode = TileMode_e::TILE_BOTH; + else if (entry.samplerState & SAMPLER_CLAMP_U) + tileMode = TileMode_e::TILE_VERTICAL; + else if (entry.samplerState & SAMPLER_CLAMP_V) + 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 & SAMPLER_MIPMAP_NEAREST) + filter = GdtFilter_e::MIP_2X_BILINEAR; + else if (entry.samplerState & SAMPLER_MIPMAP_LINEAR) + filter = GdtFilter_e::MIP_2X_TRILINEAR; + } + else if ((entry.samplerState & SAMPLER_FILTER_MASK) == SAMPLER_FILTER_ANISO4X) + { + if (entry.samplerState & SAMPLER_MIPMAP_NEAREST) + filter = GdtFilter_e::MIP_4X_BILINEAR; + else if (entry.samplerState & SAMPLER_MIPMAP_LINEAR) + filter = GdtFilter_e::MIP_4X_TRILINEAR; + } + else if ((entry.samplerState & SAMPLER_FILTER_MASK) == SAMPLER_FILTER_NEAREST) + { + assert((entry.samplerState & SAMPLER_MIPMAP_MASK) == SAMPLER_MIPMAP_DISABLED); + filter = GdtFilter_e::NOMIP_NEAREST; + } + else if ((entry.samplerState & SAMPLER_FILTER_MASK) == SAMPLER_FILTER_LINEAR) + { + assert((entry.samplerState & SAMPLER_MIPMAP_MASK) == SAMPLER_MIPMAP_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"; + continue; + } + + SetValue(knownMaterialSourceName->second.m_name, imageName); + SetValue("tile"s + knownMaterialSourceName->second.m_additional_property_suffix, GdtTileModeNames[static_cast(tileMode)]); + SetValue("filter"s + knownMaterialSourceName->second.m_additional_property_suffix, GdtSamplerFilterNames[static_cast(filter)]); } } diff --git a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperTechniqueSet.cpp b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperTechniqueSet.cpp index 7f5d8ef9..7c73ea33 100644 --- a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperTechniqueSet.cpp +++ b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperTechniqueSet.cpp @@ -212,18 +212,27 @@ namespace IW4 Indent(); m_stream << codeDestAccessor << " = material."; - const auto knownMaterialSource = knownMaterialSourceNames.find(arg.u.nameHash); - if (knownMaterialSource != knownMaterialSourceNames.end()) + const auto knownConstantName = knownConstantNames.find(arg.u.nameHash); + if (knownConstantName != knownConstantNames.end()) { - m_stream << knownMaterialSource->second; + m_stream << knownConstantName->second; } else { - const auto shaderArgNameHash = Common::R_HashString(targetShaderArg->m_name.c_str(), 0u); - if (shaderArgNameHash == arg.u.nameHash) - m_stream << targetShaderArg->m_name; + const auto knownMaterialTextureName = knownTextureMaps.find(arg.u.nameHash); + + if(knownMaterialTextureName != knownTextureMaps.end()) + { + m_stream << knownConstantName->second; + } else - m_stream << "#0x" << std::hex << arg.u.nameHash; + { + const auto shaderArgNameHash = Common::R_HashString(targetShaderArg->m_name.c_str(), 0u); + if (shaderArgNameHash == arg.u.nameHash) + m_stream << targetShaderArg->m_name; + else + m_stream << "#0x" << std::hex << arg.u.nameHash; + } } m_stream << ";\n";