From 2114b761b058d061ac0f865016786253152ec9af Mon Sep 17 00:00:00 2001 From: Jan Date: Mon, 18 Apr 2022 14:46:13 +0200 Subject: [PATCH] Dump iw4 materials as json for data investigation purposes --- src/Common/Game/IW4/IW4_Assets.h | 18 +- src/ObjCommon/Game/IW4/TechsetConstantsIW4.h | 4 + .../IW4/AssetDumpers/AssetDumperMaterial.cpp | 229 ++++++++++++++++++ .../IW4/AssetDumpers/AssetDumperMaterial.h | 14 ++ src/ObjWriting/Game/IW4/ZoneDumperIW4.cpp | 3 +- 5 files changed, 258 insertions(+), 10 deletions(-) create mode 100644 src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperMaterial.cpp create mode 100644 src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperMaterial.h diff --git a/src/Common/Game/IW4/IW4_Assets.h b/src/Common/Game/IW4/IW4_Assets.h index 74b220a3..f8c03f56 100644 --- a/src/Common/Game/IW4/IW4_Assets.h +++ b/src/Common/Game/IW4/IW4_Assets.h @@ -632,8 +632,8 @@ namespace IW4 unsigned int nameHash; char nameStart; char nameEnd; - char samplerState; - char semantic; + unsigned char samplerState; + unsigned char semantic; // TextureSemantic MaterialTextureDefInfo u; }; @@ -735,10 +735,10 @@ namespace IW4 struct MaterialInfo { const char* name; - char gameFlags; - char sortKey; - char textureAtlasRowCount; - char textureAtlasColumnCount; + unsigned char gameFlags; + unsigned char sortKey; + unsigned char textureAtlasRowCount; + unsigned char textureAtlasColumnCount; GfxDrawSurf drawSurf; unsigned int surfaceTypeBits; uint16_t hashIndex; @@ -747,12 +747,12 @@ namespace IW4 struct Material { MaterialInfo info; - char stateBitsEntry[48]; + unsigned char stateBitsEntry[48]; unsigned char textureCount; unsigned char constantCount; unsigned char stateBitsCount; - char stateFlags; - char cameraRegion; + unsigned char stateFlags; + unsigned char cameraRegion; MaterialTechniqueSet* techniqueSet; MaterialTextureDef* textureTable; MaterialConstantDef* constantTable; diff --git a/src/ObjCommon/Game/IW4/TechsetConstantsIW4.h b/src/ObjCommon/Game/IW4/TechsetConstantsIW4.h index c8f5069e..d4fd8129 100644 --- a/src/ObjCommon/Game/IW4/TechsetConstantsIW4.h +++ b/src/ObjCommon/Game/IW4/TechsetConstantsIW4.h @@ -567,5 +567,9 @@ namespace IW4 KnownMaterialSource("attenuationMap5"), KnownMaterialSource("attenuationMap6"), KnownMaterialSource("attenuationMap7"), + KnownMaterialSource("distortionScale"), + KnownMaterialSource("eyeOffsetParms"), + KnownMaterialSource("falloffBeginColor"), + KnownMaterialSource("falloffEndColor"), }; } diff --git a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperMaterial.cpp b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperMaterial.cpp new file mode 100644 index 00000000..a2ccd43a --- /dev/null +++ b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperMaterial.cpp @@ -0,0 +1,229 @@ +#include "AssetDumperMaterial.h" + +#include +#include +#include + +#include "Game/IW4/TechsetConstantsIW4.h" + +using namespace IW4; +using json = nlohmann::json; + +namespace IW4 +{ + const char* AssetName(const char* name) + { + if (name && name[0] == ',') + return &name[1]; + return name; + } + + json BuildComplexTableJson(complex_s* complexTable, const size_t count) + { + auto jArray = json::array(); + + if (complexTable) + { + for (auto index = 0u; index < count; index++) + { + const auto& entry = complexTable[index]; + jArray.emplace_back(json{ + {"real", entry.real}, + {"imag", entry.imag} + }); + } + } + + return jArray; + } + + json BuildWaterJson(water_t* water) + { + if (!water) + return json{}; + + return json{ + {"floatTime", water->writable.floatTime}, + {"H0", BuildComplexTableJson(water->H0, water->M * water->N)}, + {"wTerm", water->wTerm ? std::vector(water->wTerm, water->wTerm + (water->M * water->N)) : json::array()}, + {"M", water->M}, + {"N", water->N}, + {"Lx", water->Lx}, + {"Lz", water->Lz}, + {"windvel", water->windvel}, + {"winddir", std::vector(std::begin(water->winddir), std::end(water->winddir))}, + {"amplitude", water->amplitude}, + {"codeConstant", std::vector(std::begin(water->codeConstant), std::end(water->codeConstant))}, + {"image", water->image && water->image->name ? AssetName(water->image->name) : nullptr} + }; + } + + json BuildTextureTableJson(MaterialTextureDef* textureTable, const size_t count) + { + auto jArray = json::array(); + + if (textureTable) + { + for (auto index = 0u; index < count; index++) + { + const auto& entry = textureTable[index]; + json jEntry = { + {"samplerState", entry.samplerState}, + {"semantic", entry.semantic} + }; + + const auto knownMaterialSourceName = knownMaterialSourceNames.find(entry.nameHash); + if (knownMaterialSourceName != knownMaterialSourceNames.end()) + { + jEntry["name"] = knownMaterialSourceName->second; + } + else + { + jEntry.merge_patch({ + {"nameHash", entry.nameHash}, + {"nameStart", entry.nameStart}, + {"nameEnd", entry.nameEnd}, + }); + } + + if (entry.semantic == TS_WATER_MAP) + { + jEntry["water"] = BuildWaterJson(entry.u.water); + } + else + { + jEntry["image"] = entry.u.image && entry.u.image->name ? AssetName(entry.u.image->name) : nullptr; + } + + jArray.emplace_back(std::move(jEntry)); + } + } + + return jArray; + } + + json BuildConstantTableJson(MaterialConstantDef* constantTable, const size_t count) + { + auto jArray = json::array(); + + if (constantTable) + { + for (auto index = 0u; index < count; index++) + { + const auto& entry = constantTable[index]; + json jEntry = { + {"literal", std::vector(std::begin(entry.literal), std::end(entry.literal))} + }; + + const auto nameLen = strnlen(entry.name, std::extent_v); + if (nameLen == std::extent_v) + { + std::string fullLengthName(entry.name, std::extent_v); + const auto fullLengthHash = Common::R_HashString(fullLengthName.c_str(), 0); + + if (fullLengthHash == entry.nameHash) + { + jEntry["name"] = fullLengthName; + } + else + { + const auto knownMaterialSourceName = knownMaterialSourceNames.find(entry.nameHash); + if (knownMaterialSourceName != knownMaterialSourceNames.end()) + { + jEntry["name"] = knownMaterialSourceName->second; + } + else + { + jEntry.merge_patch({ + {"nameHash", entry.nameHash}, + {"namePart", fullLengthName} + }); + } + } + } + else + { + jEntry["name"] = std::string(entry.name, nameLen); + } + + jArray.emplace_back(std::move(jEntry)); + } + } + + return jArray; + } + + json BuildStateBitsTableJson(GfxStateBits* stateBitsTable, const size_t count) + { + auto jArray = json::array(); + + if (stateBitsTable) + { + for (auto index = 0u; index < count; index++) + { + const auto& entry = stateBitsTable[count]; + jArray.emplace_back(json::array({ + entry.loadBits[0], + entry.loadBits[1] + })); + } + } + + return jArray; + } +} + +bool AssetDumperMaterial::ShouldDump(XAssetInfo* asset) +{ + return true; +} + +void AssetDumperMaterial::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) +{ + auto* material = asset->Asset(); + + std::ostringstream ss; + ss << "materials/" << asset->m_name << ".json"; + const auto assetFile = context.OpenAssetFile(ss.str()); + + if (!assetFile) + return; + + auto& stream = *assetFile; + + const json j = { + { + "info", { + {"gameFlags", material->info.gameFlags}, + {"sortKey", material->info.sortKey}, + {"textureAtlasRowCount", material->info.textureAtlasRowCount}, + {"textureAtlasColumnCount", material->info.textureAtlasColumnCount}, + { + "drawSurf", { + {"objectId", static_cast(material->info.drawSurf.fields.objectId)}, + {"reflectionProbeIndex", static_cast(material->info.drawSurf.fields.reflectionProbeIndex)}, + {"hasGfxEntIndex", static_cast(material->info.drawSurf.fields.hasGfxEntIndex)}, + {"customIndex", static_cast(material->info.drawSurf.fields.customIndex)}, + {"materialSortedIndex", static_cast(material->info.drawSurf.fields.materialSortedIndex)}, + {"prepass", static_cast(material->info.drawSurf.fields.prepass)}, + {"useHeroLighting", static_cast(material->info.drawSurf.fields.useHeroLighting)}, + {"sceneLightIndex", static_cast(material->info.drawSurf.fields.sceneLightIndex)}, + {"surfType", static_cast(material->info.drawSurf.fields.surfType)}, + {"primarySortKey", static_cast(material->info.drawSurf.fields.primarySortKey)} + } + }, + {"surfaceTypeBits", material->info.surfaceTypeBits}, + {"hashIndex", material->info.hashIndex} + } + }, + {"stateBitsEntry", std::vector(std::begin(material->stateBitsEntry), std::end(material->stateBitsEntry))}, + {"stateFlags", material->stateFlags}, + {"cameraRegion", material->cameraRegion}, + {"techniqueSet", material->techniqueSet && material->techniqueSet->name ? AssetName(material->techniqueSet->name) : nullptr}, + {"textureTable", BuildTextureTableJson(material->textureTable, material->textureCount)}, + {"constantTable", BuildConstantTableJson(material->constantTable, material->constantCount)}, + {"stateBitsTable", BuildStateBitsTableJson(material->stateBitsTable, material->stateBitsCount)} + }; + + stream << std::setw(4) << j; +} diff --git a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperMaterial.h b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperMaterial.h new file mode 100644 index 00000000..330d33cd --- /dev/null +++ b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperMaterial.h @@ -0,0 +1,14 @@ +#pragma once + +#include "Dumping/AbstractAssetDumper.h" +#include "Game/IW4/IW4.h" + +namespace IW4 +{ + class AssetDumperMaterial final : public AbstractAssetDumper + { + protected: + bool ShouldDump(XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + }; +} diff --git a/src/ObjWriting/Game/IW4/ZoneDumperIW4.cpp b/src/ObjWriting/Game/IW4/ZoneDumperIW4.cpp index 606aea58..4ed897dc 100644 --- a/src/ObjWriting/Game/IW4/ZoneDumperIW4.cpp +++ b/src/ObjWriting/Game/IW4/ZoneDumperIW4.cpp @@ -9,6 +9,7 @@ #include "AssetDumpers/AssetDumperGfxLightDef.h" #include "AssetDumpers/AssetDumperLoadedSound.h" #include "AssetDumpers/AssetDumperLocalizeEntry.h" +#include "AssetDumpers/AssetDumperMaterial.h" #include "AssetDumpers/AssetDumperMenuDef.h" #include "AssetDumpers/AssetDumperMenuList.h" #include "AssetDumpers/AssetDumperPhysCollmap.h" @@ -47,7 +48,7 @@ bool ZoneDumper::DumpZone(AssetDumpingContext& context) const DUMP_ASSET_POOL(AssetDumperPhysCollmap, m_phys_collmap, ASSET_TYPE_PHYSCOLLMAP) // DUMP_ASSET_POOL(AssetDumperXAnimParts, m_xanim_parts, ASSET_TYPE_XANIMPARTS) DUMP_ASSET_POOL(AssetDumperXModel, m_xmodel, ASSET_TYPE_XMODEL) - // DUMP_ASSET_POOL(AssetDumperMaterial, m_material, ASSET_TYPE_MATERIAL) + DUMP_ASSET_POOL(AssetDumperMaterial, m_material, ASSET_TYPE_MATERIAL) DUMP_ASSET_POOL(AssetDumperPixelShader, m_material_pixel_shader, ASSET_TYPE_PIXELSHADER) DUMP_ASSET_POOL(AssetDumperVertexShader, m_material_vertex_shader, ASSET_TYPE_VERTEXSHADER) DUMP_ASSET_POOL(AssetDumperTechniqueSet, m_technique_set, ASSET_TYPE_TECHNIQUE_SET)