From 28a4fbd0d697390bd863aad570c20868b62cc94a Mon Sep 17 00:00:00 2001 From: Jan Date: Sun, 22 Sep 2024 15:11:40 +0200 Subject: [PATCH] feat: dump iw5 materials as json --- src/Common/Game/IW5/CommonIW5.h | 15 + src/Common/Game/IW5/IW5_Assets.h | 228 +++++++++- .../Game/IW5/Material/JsonMaterial.h | 391 ++++++++++++++++++ .../IW5/AssetDumpers/AssetDumperMaterial.cpp | 48 +++ .../IW5/AssetDumpers/AssetDumperMaterial.h | 21 + .../Game/IW5/Material/JsonMaterialWriter.cpp | 293 +++++++++++++ .../Game/IW5/Material/JsonMaterialWriter.h | 11 + .../Material/MaterialConstantZoneState.cpp | 236 +++++++++++ .../IW5/Material/MaterialConstantZoneState.h | 16 + src/ObjWriting/Game/IW5/ZoneDumperIW5.cpp | 3 +- 10 files changed, 1256 insertions(+), 6 deletions(-) create mode 100644 src/ObjCommon/Game/IW5/Material/JsonMaterial.h create mode 100644 src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperMaterial.cpp create mode 100644 src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperMaterial.h create mode 100644 src/ObjWriting/Game/IW5/Material/JsonMaterialWriter.cpp create mode 100644 src/ObjWriting/Game/IW5/Material/JsonMaterialWriter.h create mode 100644 src/ObjWriting/Game/IW5/Material/MaterialConstantZoneState.cpp create mode 100644 src/ObjWriting/Game/IW5/Material/MaterialConstantZoneState.h diff --git a/src/Common/Game/IW5/CommonIW5.h b/src/Common/Game/IW5/CommonIW5.h index 8347674c..33fac614 100644 --- a/src/Common/Game/IW5/CommonIW5.h +++ b/src/Common/Game/IW5/CommonIW5.h @@ -9,6 +9,21 @@ namespace IW5 public: static int StringTable_HashString(const char* str); + static constexpr uint32_t R_HashString(const char* str, uint32_t hash) + { + for (const auto* pos = str; *pos; pos++) + { + hash = 33 * hash ^ (*pos | 0x20); + } + + return hash; + } + + static constexpr uint32_t R_HashString(const char* string) + { + return R_HashString(string, 0u); + } + static PackedTexCoords Vec2PackTexCoords(const float (&in)[2]); static PackedUnitVec Vec3PackUnitVec(const float (&in)[3]); static GfxColor Vec4PackGfxColor(const float (&in)[4]); diff --git a/src/Common/Game/IW5/IW5_Assets.h b/src/Common/Game/IW5/IW5_Assets.h index 129c9f2c..2d45909d 100644 --- a/src/Common/Game/IW5/IW5_Assets.h +++ b/src/Common/Game/IW5/IW5_Assets.h @@ -675,6 +675,23 @@ namespace IW5 gcc_align(8) uint64_t packed; }; + enum MaterialGameFlags + { + MTL_GAMEFLAG_1 = 0x1, + MTL_GAMEFLAG_2 = 0x2, + MTL_GAMEFLAG_4 = 0x4, + MTL_GAMEFLAG_8 = 0x8, + MTL_GAMEFLAG_10 = 0x10, + MTL_GAMEFLAG_20 = 0x20, + MTL_GAMEFLAG_40 = 0x40, + MTL_GAMEFLAG_80 = 0x80, + MTL_GAMEFLAG_100 = 0x100, + MTL_GAMEFLAG_200 = 0x200, + MTL_GAMEFLAG_400 = 0x400, + MTL_GAMEFLAG_800 = 0x800, + MTL_GAMEFLAG_1000 = 0x1000, + }; + struct MaterialInfo { const char* name; @@ -737,13 +754,71 @@ namespace IW5 water_t* water; }; + enum TextureFilter + { + TEXTURE_FILTER_DISABLED = 0x0, + TEXTURE_FILTER_NEAREST = 0x1, + TEXTURE_FILTER_LINEAR = 0x2, + TEXTURE_FILTER_ANISO2X = 0x3, + TEXTURE_FILTER_ANISO4X = 0x4, + + TEXTURE_FILTER_COUNT + }; + + enum SamplerStateBitsMipMap_e + { + SAMPLER_MIPMAP_ENUM_DISABLED, + SAMPLER_MIPMAP_ENUM_NEAREST, + SAMPLER_MIPMAP_ENUM_LINEAR, + + SAMPLER_MIPMAP_ENUM_COUNT + }; + + enum SamplerStateBits_e + { + SAMPLER_FILTER_SHIFT = 0x0, + SAMPLER_FILTER_NEAREST = 0x1, + SAMPLER_FILTER_LINEAR = 0x2, + SAMPLER_FILTER_ANISO2X = 0x3, + SAMPLER_FILTER_ANISO4X = 0x4, + SAMPLER_FILTER_MASK = 0x7, + + SAMPLER_MIPMAP_SHIFT = 0x3, + SAMPLER_MIPMAP_DISABLED = 0x0, + SAMPLER_MIPMAP_NEAREST = 0x8, + SAMPLER_MIPMAP_LINEAR = 0x10, + SAMPLER_MIPMAP_COUNT = 0x3, + SAMPLER_MIPMAP_MASK = 0x18, + + SAMPLER_CLAMP_U_SHIFT = 0x5, + SAMPLER_CLAMP_V_SHIFT = 0x6, + SAMPLER_CLAMP_W_SHIFT = 0x7, + SAMPLER_CLAMP_U = 0x20, + SAMPLER_CLAMP_V = 0x40, + SAMPLER_CLAMP_W = 0x80, + 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; - unsigned char semantic; + MaterialTextureDefSamplerState samplerState; + unsigned char semantic; // TextureSemantic MaterialTextureDefInfo u; }; @@ -751,18 +826,161 @@ namespace IW5 { unsigned int nameHash; char name[12]; - float literal[4]; + vec4_t literal; }; + enum GfxBlend + { + GFXS_BLEND_DISABLED = 0x0, + GFXS_BLEND_ZERO = 0x1, + GFXS_BLEND_ONE = 0x2, + GFXS_BLEND_SRCCOLOR = 0x3, + GFXS_BLEND_INVSRCCOLOR = 0x4, + GFXS_BLEND_SRCALPHA = 0x5, + GFXS_BLEND_INVSRCALPHA = 0x6, + GFXS_BLEND_DESTALPHA = 0x7, + GFXS_BLEND_INVDESTALPHA = 0x8, + GFXS_BLEND_DESTCOLOR = 0x9, + GFXS_BLEND_INVDESTCOLOR = 0xA, + + GFXS_BLEND_COUNT + }; + + enum GfxBlendOp + { + GFXS_BLENDOP_DISABLED = 0x0, + GFXS_BLENDOP_ADD = 0x1, + GFXS_BLENDOP_SUBTRACT = 0x2, + GFXS_BLENDOP_REVSUBTRACT = 0x3, + GFXS_BLENDOP_MIN = 0x4, + GFXS_BLENDOP_MAX = 0x5, + + GFXS_BLENDOP_COUNT + }; + + enum GfxAlphaTest_e + { + GFXS_ALPHA_TEST_GT_0 = 1, + GFXS_ALPHA_TEST_LT_128 = 2, + GFXS_ALPHA_TEST_GE_128 = 3, + + GFXS_ALPHA_TEST_COUNT + }; + + enum GfxCullFace_e + { + GFXS_CULL_NONE = 1, + GFXS_CULL_BACK = 2, + GFXS_CULL_FRONT = 3, + }; + + enum GfxDepthTest_e + { + GFXS_DEPTHTEST_ALWAYS = 0, + GFXS_DEPTHTEST_LESS = 1, + GFXS_DEPTHTEST_EQUAL = 2, + GFXS_DEPTHTEST_LESSEQUAL = 3 + }; + + enum GfxPolygonOffset_e + { + GFXS_POLYGON_OFFSET_0 = 0, + GFXS_POLYGON_OFFSET_1 = 1, + GFXS_POLYGON_OFFSET_2 = 2, + GFXS_POLYGON_OFFSET_SHADOWMAP = 3 + }; + + enum GfxStencilOp + { + GFXS_STENCILOP_KEEP = 0x0, + GFXS_STENCILOP_ZERO = 0x1, + GFXS_STENCILOP_REPLACE = 0x2, + GFXS_STENCILOP_INCRSAT = 0x3, + GFXS_STENCILOP_DECRSAT = 0x4, + GFXS_STENCILOP_INVERT = 0x5, + GFXS_STENCILOP_INCR = 0x6, + GFXS_STENCILOP_DECR = 0x7 + }; + + enum GfxStencilFunc + { + GFXS_STENCILFUNC_NEVER = 0x0, + GFXS_STENCILFUNC_LESS = 0x1, + GFXS_STENCILFUNC_EQUAL = 0x2, + GFXS_STENCILFUNC_LESSEQUAL = 0x3, + GFXS_STENCILFUNC_GREATER = 0x4, + GFXS_STENCILFUNC_NOTEQUAL = 0x5, + GFXS_STENCILFUNC_GREATEREQUAL = 0x6, + GFXS_STENCILFUNC_ALWAYS = 0x7 + }; + + 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; + }; + + enum GfxCameraRegionType + { + CAMERA_REGION_LIT_OPAQUE = 0x0, + CAMERA_REGION_LIT_TRANS = 0x1, + CAMERA_REGION_EMISSIVE = 0x2, + CAMERA_REGION_DEPTH_HACK = 0x3, + CAMERA_REGION_LIGHT_MAP_OPAQUE = 0x4, + + CAMERA_REGION_COUNT, + CAMERA_REGION_NONE = CAMERA_REGION_COUNT, }; struct Material { MaterialInfo info; - unsigned char stateBitsEntry[54]; + char stateBitsEntry[54]; unsigned char textureCount; unsigned char constantCount; unsigned char stateBitsCount; diff --git a/src/ObjCommon/Game/IW5/Material/JsonMaterial.h b/src/ObjCommon/Game/IW5/Material/JsonMaterial.h new file mode 100644 index 00000000..b6dd0d25 --- /dev/null +++ b/src/ObjCommon/Game/IW5/Material/JsonMaterial.h @@ -0,0 +1,391 @@ +#pragma once + +#include "Game/IW5/IW5.h" + +#include "Json/JsonExtension.h" +#include +#include +#include +#include +#include + +namespace IW5 +{ + NLOHMANN_JSON_SERIALIZE_ENUM(GfxStencilOp, + { + {GFXS_STENCILOP_KEEP, "keep" }, + {GFXS_STENCILOP_ZERO, "zero" }, + {GFXS_STENCILOP_REPLACE, "replace"}, + {GFXS_STENCILOP_INCRSAT, "incrsat"}, + {GFXS_STENCILOP_DECRSAT, "decrsat"}, + {GFXS_STENCILOP_INVERT, "invert" }, + {GFXS_STENCILOP_INCR, "incr" }, + {GFXS_STENCILOP_DECR, "decr" }, + }); + + NLOHMANN_JSON_SERIALIZE_ENUM(GfxStencilFunc, + { + {GFXS_STENCILFUNC_NEVER, "never" }, + {GFXS_STENCILFUNC_LESS, "less" }, + {GFXS_STENCILFUNC_EQUAL, "equal" }, + {GFXS_STENCILFUNC_LESSEQUAL, "lessequal" }, + {GFXS_STENCILFUNC_GREATER, "greater" }, + {GFXS_STENCILFUNC_NOTEQUAL, "notequal" }, + {GFXS_STENCILFUNC_GREATEREQUAL, "greaterequal"}, + {GFXS_STENCILFUNC_ALWAYS, "always" }, + }); + + class JsonStencil + { + public: + GfxStencilOp pass; + GfxStencilOp fail; + GfxStencilOp zfail; + GfxStencilFunc func; + }; + + NLOHMANN_DEFINE_TYPE_EXTENSION(JsonStencil, pass, fail, zfail, func); + + enum class JsonAlphaTest + { + INVALID, + DISABLED, + GT0, + LT128, + GE128 + }; + + NLOHMANN_JSON_SERIALIZE_ENUM(JsonAlphaTest, + { + {JsonAlphaTest::INVALID, nullptr }, + {JsonAlphaTest::DISABLED, "disabled"}, + {JsonAlphaTest::GT0, "gt0" }, + {JsonAlphaTest::LT128, "lt128" }, + {JsonAlphaTest::GE128, "ge128" } + }); + + enum class JsonCullFace + { + INVALID, + NONE, + BACK, + FRONT + }; + + NLOHMANN_JSON_SERIALIZE_ENUM( + JsonCullFace, + { + {JsonCullFace::INVALID, nullptr}, + {JsonCullFace::NONE, "none" }, + {JsonCullFace::BACK, "back" }, + {JsonCullFace::FRONT, "front"} + }); + + enum class JsonDepthTest + { + INVALID, + DISABLED, + ALWAYS, + LESS, + EQUAL, + LESS_EQUAL + }; + + NLOHMANN_JSON_SERIALIZE_ENUM(JsonDepthTest, + { + {JsonDepthTest::INVALID, nullptr }, + {JsonDepthTest::DISABLED, "disabled" }, + {JsonDepthTest::ALWAYS, "always" }, + {JsonDepthTest::LESS, "less" }, + {JsonDepthTest::EQUAL, "equal" }, + {JsonDepthTest::LESS_EQUAL, "less_equal"} + }); + + NLOHMANN_JSON_SERIALIZE_ENUM(GfxBlend, + { + {GFXS_BLEND_DISABLED, "disabled" }, + {GFXS_BLEND_ZERO, "zero" }, + {GFXS_BLEND_ONE, "one" }, + {GFXS_BLEND_SRCCOLOR, "srccolor" }, + {GFXS_BLEND_INVSRCCOLOR, "invsrccolor" }, + {GFXS_BLEND_SRCALPHA, "srcalpha" }, + {GFXS_BLEND_INVSRCALPHA, "invsrcalpha" }, + {GFXS_BLEND_DESTALPHA, "destalpha" }, + {GFXS_BLEND_INVDESTALPHA, "invdestalpha"}, + {GFXS_BLEND_DESTCOLOR, "destcolor" }, + {GFXS_BLEND_INVDESTCOLOR, "invdestcolor"}, + }); + + NLOHMANN_JSON_SERIALIZE_ENUM(GfxBlendOp, + { + {GFXS_BLENDOP_DISABLED, "disabled" }, + {GFXS_BLENDOP_ADD, "add" }, + {GFXS_BLENDOP_SUBTRACT, "subtract" }, + {GFXS_BLENDOP_REVSUBTRACT, "revsubtract"}, + {GFXS_BLENDOP_MIN, "min" }, + {GFXS_BLENDOP_MAX, "max" }, + }); + + NLOHMANN_JSON_SERIALIZE_ENUM(GfxPolygonOffset_e, + { + {GFXS_POLYGON_OFFSET_0, "offset0" }, + {GFXS_POLYGON_OFFSET_1, "offset1" }, + {GFXS_POLYGON_OFFSET_2, "offset2" }, + {GFXS_POLYGON_OFFSET_SHADOWMAP, "offsetShadowmap"}, + }); + + class JsonStateBitsTableEntry + { + public: + GfxBlend srcBlendRgb; + GfxBlend dstBlendRgb; + GfxBlendOp blendOpRgb; + JsonAlphaTest alphaTest; + JsonCullFace cullFace; + GfxBlend srcBlendAlpha; + GfxBlend dstBlendAlpha; + GfxBlendOp blendOpAlpha; + bool colorWriteRgb; + bool colorWriteAlpha; + bool gammaWrite; + bool polymodeLine; + bool depthWrite; + JsonDepthTest depthTest; + GfxPolygonOffset_e polygonOffset; + std::optional stencilFront; + std::optional stencilBack; + }; + + NLOHMANN_DEFINE_TYPE_EXTENSION(JsonStateBitsTableEntry, + srcBlendRgb, + dstBlendRgb, + blendOpRgb, + alphaTest, + cullFace, + srcBlendAlpha, + dstBlendAlpha, + blendOpAlpha, + colorWriteRgb, + colorWriteAlpha, + polymodeLine, + depthWrite, + depthWrite, + depthTest, + polygonOffset, + stencilFront, + stencilBack); + + class JsonConstant + { + public: + std::optional name; + std::optional nameFragment; + std::optional nameHash; + std::vector literal; + }; + + inline void to_json(nlohmann::json& out, const JsonConstant& in) + { + if (in.name.has_value()) + { + optional_to_json(out, "name", in.name); + } + else + { + optional_to_json(out, "nameFragment", in.nameFragment); + optional_to_json(out, "nameHash", in.nameHash); + } + + out["literal"] = in.literal; + } + + inline void from_json(const nlohmann::json& in, JsonConstant& out) + { + optional_from_json(in, "name", out.name); + optional_from_json(in, "nameFragment", out.nameFragment); + optional_from_json(in, "nameHash", out.nameHash); + in.at("literal").get_to(out.literal); + }; + + NLOHMANN_JSON_SERIALIZE_ENUM(TextureFilter, + { + {TEXTURE_FILTER_DISABLED, "disabled"}, + {TEXTURE_FILTER_NEAREST, "nearest" }, + {TEXTURE_FILTER_LINEAR, "linear" }, + {TEXTURE_FILTER_ANISO2X, "aniso2x" }, + {TEXTURE_FILTER_ANISO4X, "aniso4x" }, + }); + + NLOHMANN_JSON_SERIALIZE_ENUM(SamplerStateBitsMipMap_e, + { + {SAMPLER_MIPMAP_ENUM_DISABLED, "disabled"}, + {SAMPLER_MIPMAP_ENUM_NEAREST, "nearest" }, + {SAMPLER_MIPMAP_ENUM_LINEAR, "linear" }, + }); + + class JsonSamplerState + { + public: + TextureFilter filter; + SamplerStateBitsMipMap_e mipMap; + bool clampU; + bool clampV; + bool clampW; + }; + + NLOHMANN_DEFINE_TYPE_EXTENSION(JsonSamplerState, filter, mipMap, clampU, clampV, clampW); + + class JsonComplex + { + public: + float real; + float imag; + }; + + NLOHMANN_DEFINE_TYPE_EXTENSION(JsonComplex, real, imag); + + class JsonWater + { + public: + float floatTime; + int m; + int n; + std::string h0; + std::string wTerm; + float lx; + float lz; + float gravity; + float windvel; + std::array winddir; + float amplitude; + std::array codeConstant; + }; + + NLOHMANN_DEFINE_TYPE_EXTENSION(JsonWater, floatTime, m, n, h0, wTerm, lx, lz, gravity, windvel, winddir, amplitude, codeConstant); + + NLOHMANN_JSON_SERIALIZE_ENUM(TextureSemantic, + { + {TS_2D, "2D" }, + {TS_FUNCTION, "function" }, + {TS_COLOR_MAP, "colorMap" }, + {TS_DETAIL_MAP, "detailMap" }, + {TS_UNUSED_2, "unused2" }, + {TS_NORMAL_MAP, "normalMap" }, + {TS_UNUSED_3, "unused3" }, + {TS_UNUSED_4, "unused4" }, + {TS_SPECULAR_MAP, "specularMap" }, + {TS_UNUSED_5, "unused5" }, + {TS_UNUSED_6, "unused6" }, + {TS_WATER_MAP, "waterMap" }, + {TS_DISPLACEMENT_MAP, "displacementMap"}, + }); + + class JsonTexture + { + public: + std::optional name; + std::optional nameHash; + std::optional nameStart; + std::optional nameEnd; + TextureSemantic semantic; + JsonSamplerState samplerState; + std::string image; + std::optional water; + }; + + inline void to_json(nlohmann::json& out, const JsonTexture& in) + { + if (in.name.has_value()) + { + optional_to_json(out, "name", in.name); + } + else + { + optional_to_json(out, "nameHash", in.nameHash); + optional_to_json(out, "nameStart", in.nameStart); + optional_to_json(out, "nameEnd", in.nameEnd); + } + + out["semantic"] = in.semantic; + out["samplerState"] = in.samplerState; + out["image"] = in.image; + optional_to_json(out, "water", in.water); + } + + inline void from_json(const nlohmann::json& in, JsonTexture& out) + { + optional_from_json(in, "name", out.name); + optional_from_json(in, "nameHash", out.nameHash); + optional_from_json(in, "nameStart", out.nameStart); + optional_from_json(in, "nameEnd", out.nameEnd); + in.at("semantic").get_to(out.semantic); + in.at("samplerState").get_to(out.samplerState); + in.at("image").get_to(out.image); + optional_from_json(in, "water", out.water); + }; + + class JsonTextureAtlas + { + public: + uint8_t rows; + uint8_t columns; + }; + + NLOHMANN_DEFINE_TYPE_EXTENSION(JsonTextureAtlas, rows, columns); + + NLOHMANN_JSON_SERIALIZE_ENUM(MaterialGameFlags, + { + {MTL_GAMEFLAG_1, "1" }, + {MTL_GAMEFLAG_2, "2" }, + {MTL_GAMEFLAG_4, "4" }, + {MTL_GAMEFLAG_8, "8" }, + {MTL_GAMEFLAG_10, "10" }, + {MTL_GAMEFLAG_20, "20" }, + {MTL_GAMEFLAG_40, "40" }, + {MTL_GAMEFLAG_80, "80" }, + {MTL_GAMEFLAG_100, "100" }, + {MTL_GAMEFLAG_200, "200" }, + {MTL_GAMEFLAG_400, "400" }, + {MTL_GAMEFLAG_800, "800" }, + {MTL_GAMEFLAG_1000, "1000"}, + }); + + NLOHMANN_JSON_SERIALIZE_ENUM(GfxCameraRegionType, + { + {CAMERA_REGION_LIT_OPAQUE, "litOpaque" }, + {CAMERA_REGION_LIT_TRANS, "litTrans" }, + {CAMERA_REGION_EMISSIVE, "emissive" }, + {CAMERA_REGION_DEPTH_HACK, "depthHack" }, + {CAMERA_REGION_LIGHT_MAP_OPAQUE, "lightMapOpaque"}, + {CAMERA_REGION_NONE, "none" }, + }); + + class JsonMaterial + { + public: + std::vector gameFlags; + unsigned sortKey; + std::optional textureAtlas; + unsigned surfaceTypeBits; + std::vector stateBitsEntry; + unsigned stateFlags; + GfxCameraRegionType cameraRegion; + std::string techniqueSet; + std::vector textures; + std::vector constants; + std::vector stateBits; + }; + + NLOHMANN_DEFINE_TYPE_EXTENSION(JsonMaterial, + gameFlags, + sortKey, + textureAtlas, + surfaceTypeBits, + stateBitsEntry, + stateFlags, + cameraRegion, + techniqueSet, + textures, + constants, + stateBits); +} // namespace IW5 diff --git a/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperMaterial.cpp b/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperMaterial.cpp new file mode 100644 index 00000000..c4efb8f9 --- /dev/null +++ b/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperMaterial.cpp @@ -0,0 +1,48 @@ +#include "AssetDumperMaterial.h" + +#include "Game/IW5/Material/JsonMaterialWriter.h" +#include "Game/IW5/Material/MaterialConstantZoneState.h" + +#include +#include +#include + +using namespace IW5; + +std::string AssetDumperMaterial::GetFileNameForAsset(const std::string& assetName) +{ + std::string sanitizedFileName(assetName); + if (sanitizedFileName[0] == '*') + { + std::ranges::replace(sanitizedFileName, '*', '_'); + const auto parenthesisPos = sanitizedFileName.find('('); + if (parenthesisPos != std::string::npos) + sanitizedFileName.erase(parenthesisPos); + sanitizedFileName = "generated/" + sanitizedFileName; + } + + return std::format("materials/{}.json", sanitizedFileName); +} + +bool AssetDumperMaterial::ShouldDump(XAssetInfo* asset) +{ + return true; +} + +void AssetDumperMaterial::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) +{ + const auto assetFile = context.OpenAssetFile(GetFileNameForAsset(asset->m_name)); + + if (!assetFile) + return; + + DumpMaterialAsJson(*assetFile, asset->Asset(), context); +} + +void AssetDumperMaterial::DumpPool(AssetDumpingContext& context, AssetPool* pool) +{ + auto* materialConstantState = context.GetZoneAssetDumperState(); + materialConstantState->ExtractNamesFromZone(); + + AbstractAssetDumper::DumpPool(context, pool); +} diff --git a/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperMaterial.h b/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperMaterial.h new file mode 100644 index 00000000..04c8e914 --- /dev/null +++ b/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperMaterial.h @@ -0,0 +1,21 @@ +#pragma once + +#include "Dumping/AbstractAssetDumper.h" +#include "Game/IW5/IW5.h" + +#include + +namespace IW5 +{ + class AssetDumperMaterial final : public AbstractAssetDumper + { + static std::string GetFileNameForAsset(const std::string& assetName); + + protected: + bool ShouldDump(XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + + public: + void DumpPool(AssetDumpingContext& context, AssetPool* pool) override; + }; +} // namespace IW5 diff --git a/src/ObjWriting/Game/IW5/Material/JsonMaterialWriter.cpp b/src/ObjWriting/Game/IW5/Material/JsonMaterialWriter.cpp new file mode 100644 index 00000000..66ec64b4 --- /dev/null +++ b/src/ObjWriting/Game/IW5/Material/JsonMaterialWriter.cpp @@ -0,0 +1,293 @@ +#include "JsonMaterialWriter.h" + +#include "Game/IW5/CommonIW5.h" +#include "Game/IW5/Material/JsonMaterial.h" +#include "Impl/Base64.h" +#include "MaterialConstantZoneState.h" + +#include +#include + +using namespace nlohmann; +using namespace IW5; + +namespace +{ + class JsonDumper + { + public: + JsonDumper(AssetDumpingContext& context, std::ostream& stream) + : m_stream(stream), + m_material_constants(*context.GetZoneAssetDumperState()) + { + } + + void Dump(const Material* material) const + { + JsonMaterial jsonMaterial; + CreateJsonMaterial(jsonMaterial, *material); + json jRoot = jsonMaterial; + + jRoot["_type"] = "material"; + jRoot["_game"] = "iw5"; + jRoot["_version"] = 1; + + m_stream << std::setw(4) << jRoot << "\n"; + } + + private: + static const char* AssetName(const char* input) + { + if (input && input[0] == ',') + return &input[1]; + + return input; + } + + static void CreateJsonGameFlags(JsonMaterial& jMaterial, const unsigned gameFlags) + { + jMaterial.gameFlags.clear(); + for (auto i = 0u; i < sizeof(gameFlags) * 8u; i++) + { + const auto flag = static_cast(1 << i); + + if (gameFlags & flag) + jMaterial.gameFlags.emplace_back(flag); + } + } + + static void CreateJsonSamplerState(JsonSamplerState& jSamplerState, const MaterialTextureDefSamplerState& samplerState) + { + jSamplerState.filter = static_cast(samplerState.filter); + jSamplerState.mipMap = static_cast(samplerState.mipMap); + jSamplerState.clampU = samplerState.clampU; + jSamplerState.clampV = samplerState.clampV; + jSamplerState.clampW = samplerState.clampW; + } + + static void CreateJsonWater(JsonWater& jWater, const water_t& water) + { + jWater.floatTime = water.writable.floatTime; + jWater.m = water.M; + jWater.n = water.N; + jWater.lx = water.Lx; + jWater.lz = water.Lz; + jWater.gravity = water.gravity; + jWater.windvel = water.windvel; + jWater.winddir[0] = water.winddir[0]; + jWater.winddir[1] = water.winddir[1]; + jWater.amplitude = water.amplitude; + jWater.codeConstant[0] = water.codeConstant[0]; + jWater.codeConstant[1] = water.codeConstant[1]; + jWater.codeConstant[2] = water.codeConstant[2]; + jWater.codeConstant[3] = water.codeConstant[3]; + + if (water.H0) + { + const auto count = water.M * water.N; + jWater.h0 = base64::EncodeBase64(water.H0, sizeof(complex_s) * count); + } + + if (water.wTerm) + { + const auto count = water.M * water.N; + jWater.wTerm = base64::EncodeBase64(water.wTerm, sizeof(float) * count); + } + } + + void CreateJsonTexture(JsonTexture& jTextureDef, const MaterialTextureDef& textureDef) const + { + std::string textureDefName; + if (m_material_constants.GetTextureDefName(textureDef.nameHash, textureDefName)) + { + jTextureDef.name = textureDefName; + } + else + { + jTextureDef.nameHash = textureDef.nameHash; + jTextureDef.nameStart = std::string(1u, textureDef.nameStart); + jTextureDef.nameEnd = std::string(1u, textureDef.nameEnd); + } + + jTextureDef.semantic = static_cast(textureDef.semantic); + + CreateJsonSamplerState(jTextureDef.samplerState, textureDef.samplerState); + + if (textureDef.semantic == TS_WATER_MAP) + { + if (textureDef.u.water) + { + const auto& water = *textureDef.u.water; + if (water.image && water.image->name) + jTextureDef.image = AssetName(water.image->name); + + JsonWater jWater; + CreateJsonWater(jWater, water); + + jTextureDef.water = std::move(jWater); + } + } + else + { + if (textureDef.u.image && textureDef.u.image->name) + jTextureDef.image = AssetName(textureDef.u.image->name); + } + } + + void CreateJsonConstant(JsonConstant& jConstantDef, const MaterialConstantDef& constantDef) const + { + const auto fragmentLength = strnlen(constantDef.name, std::extent_v); + const std::string nameFragment(constantDef.name, fragmentLength); + std::string knownConstantName; + + if (fragmentLength < std::extent_v || Common::R_HashString(nameFragment.c_str(), 0) == constantDef.nameHash) + { + jConstantDef.name = nameFragment; + } + else if (m_material_constants.GetConstantName(constantDef.nameHash, knownConstantName)) + { + jConstantDef.name = knownConstantName; + } + else + { + jConstantDef.nameHash = constantDef.nameHash; + jConstantDef.nameFragment = nameFragment; + } + + jConstantDef.literal = std::vector({ + constantDef.literal.x, + constantDef.literal.y, + constantDef.literal.z, + constantDef.literal.w, + }); + } + + static void CreateJsonStencil(JsonStencil& jStencil, const unsigned pass, const unsigned fail, const unsigned zFail, const unsigned func) + { + jStencil.pass = static_cast(pass); + jStencil.fail = static_cast(fail); + jStencil.zfail = static_cast(zFail); + jStencil.func = static_cast(func); + } + + static void CreateJsonStateBitsTableEntry(JsonStateBitsTableEntry& jStateBitsTableEntry, const GfxStateBits& stateBitsTableEntry) + { + const auto& structured = stateBitsTableEntry.loadBits.structured; + + jStateBitsTableEntry.srcBlendRgb = static_cast(structured.srcBlendRgb); + jStateBitsTableEntry.dstBlendRgb = static_cast(structured.dstBlendRgb); + jStateBitsTableEntry.blendOpRgb = static_cast(structured.blendOpRgb); + + assert(structured.alphaTestDisabled || structured.alphaTest == GFXS_ALPHA_TEST_GT_0 || structured.alphaTest == GFXS_ALPHA_TEST_LT_128 + || structured.alphaTest == GFXS_ALPHA_TEST_GE_128); + if (structured.alphaTestDisabled) + jStateBitsTableEntry.alphaTest = JsonAlphaTest::DISABLED; + else if (structured.alphaTest == GFXS_ALPHA_TEST_GT_0) + jStateBitsTableEntry.alphaTest = JsonAlphaTest::GT0; + else if (structured.alphaTest == GFXS_ALPHA_TEST_LT_128) + jStateBitsTableEntry.alphaTest = JsonAlphaTest::LT128; + else if (structured.alphaTest == GFXS_ALPHA_TEST_GE_128) + jStateBitsTableEntry.alphaTest = JsonAlphaTest::GE128; + else + jStateBitsTableEntry.alphaTest = JsonAlphaTest::INVALID; + + assert(structured.cullFace == GFXS_CULL_NONE || structured.cullFace == GFXS_CULL_BACK || structured.cullFace == GFXS_CULL_FRONT); + if (structured.cullFace == GFXS_CULL_NONE) + jStateBitsTableEntry.cullFace = JsonCullFace::NONE; + else if (structured.cullFace == GFXS_CULL_BACK) + jStateBitsTableEntry.cullFace = JsonCullFace::BACK; + else if (structured.cullFace == GFXS_CULL_FRONT) + jStateBitsTableEntry.cullFace = JsonCullFace::FRONT; + else + jStateBitsTableEntry.cullFace = JsonCullFace::INVALID; + + jStateBitsTableEntry.srcBlendAlpha = static_cast(structured.srcBlendAlpha); + jStateBitsTableEntry.dstBlendAlpha = static_cast(structured.dstBlendAlpha); + jStateBitsTableEntry.blendOpAlpha = static_cast(structured.blendOpAlpha); + jStateBitsTableEntry.colorWriteRgb = structured.colorWriteRgb; + jStateBitsTableEntry.colorWriteAlpha = structured.colorWriteAlpha; + jStateBitsTableEntry.gammaWrite = structured.gammaWrite; + jStateBitsTableEntry.polymodeLine = structured.polymodeLine; + jStateBitsTableEntry.depthWrite = structured.depthWrite; + + assert(structured.depthTestDisabled || structured.depthTest == GFXS_DEPTHTEST_ALWAYS || structured.depthTest == GFXS_DEPTHTEST_LESS + || structured.depthTest == GFXS_DEPTHTEST_EQUAL || structured.depthTest == GFXS_DEPTHTEST_LESSEQUAL); + if (structured.depthTestDisabled) + jStateBitsTableEntry.depthTest = JsonDepthTest::DISABLED; + else if (structured.depthTest == GFXS_DEPTHTEST_ALWAYS) + jStateBitsTableEntry.depthTest = JsonDepthTest::ALWAYS; + else if (structured.depthTest == GFXS_DEPTHTEST_LESS) + jStateBitsTableEntry.depthTest = JsonDepthTest::LESS; + else if (structured.depthTest == GFXS_DEPTHTEST_EQUAL) + jStateBitsTableEntry.depthTest = JsonDepthTest::EQUAL; + else if (structured.depthTest == GFXS_DEPTHTEST_LESSEQUAL) + jStateBitsTableEntry.depthTest = JsonDepthTest::LESS_EQUAL; + else + jStateBitsTableEntry.depthTest = JsonDepthTest::INVALID; + + jStateBitsTableEntry.polygonOffset = static_cast(structured.polygonOffset); + + if (structured.stencilFrontEnabled) + { + JsonStencil jStencilFront; + CreateJsonStencil( + jStencilFront, structured.stencilFrontPass, structured.stencilFrontFail, structured.stencilFrontZFail, structured.stencilFrontFunc); + jStateBitsTableEntry.stencilFront = jStencilFront; + } + + if (structured.stencilBackEnabled) + { + JsonStencil jStencilBack; + CreateJsonStencil( + jStencilBack, structured.stencilBackPass, structured.stencilBackFail, structured.stencilBackZFail, structured.stencilBackFunc); + jStateBitsTableEntry.stencilBack = jStencilBack; + } + } + + void CreateJsonMaterial(JsonMaterial& jMaterial, const Material& material) const + { + CreateJsonGameFlags(jMaterial, material.info.gameFlags); + jMaterial.sortKey = material.info.sortKey; + + jMaterial.textureAtlas = JsonTextureAtlas(); + jMaterial.textureAtlas->rows = material.info.textureAtlasRowCount; + jMaterial.textureAtlas->columns = material.info.textureAtlasColumnCount; + + jMaterial.surfaceTypeBits = material.info.surfaceTypeBits; + + jMaterial.stateBitsEntry.resize(std::extent_v); + for (auto i = 0u; i < std::extent_v; i++) + jMaterial.stateBitsEntry[i] = material.stateBitsEntry[i]; + + jMaterial.stateFlags = material.stateFlags; + jMaterial.cameraRegion = static_cast(material.cameraRegion); + + if (material.techniqueSet && material.techniqueSet->name) + jMaterial.techniqueSet = AssetName(material.techniqueSet->name); + + jMaterial.textures.resize(material.textureCount); + for (auto i = 0u; i < material.textureCount; i++) + CreateJsonTexture(jMaterial.textures[i], material.textureTable[i]); + + jMaterial.constants.resize(material.constantCount); + for (auto i = 0u; i < material.constantCount; i++) + CreateJsonConstant(jMaterial.constants[i], material.constantTable[i]); + + jMaterial.stateBits.resize(material.stateBitsCount); + for (auto i = 0u; i < material.stateBitsCount; i++) + CreateJsonStateBitsTableEntry(jMaterial.stateBits[i], material.stateBitsTable[i]); + } + + std::ostream& m_stream; + const MaterialConstantZoneState& m_material_constants; + }; +} // namespace + +namespace IW5 +{ + void DumpMaterialAsJson(std::ostream& stream, const Material* material, AssetDumpingContext& context) + { + const JsonDumper dumper(context, stream); + dumper.Dump(material); + } +} // namespace IW5 diff --git a/src/ObjWriting/Game/IW5/Material/JsonMaterialWriter.h b/src/ObjWriting/Game/IW5/Material/JsonMaterialWriter.h new file mode 100644 index 00000000..c7869286 --- /dev/null +++ b/src/ObjWriting/Game/IW5/Material/JsonMaterialWriter.h @@ -0,0 +1,11 @@ +#pragma once + +#include "Dumping/AssetDumpingContext.h" +#include "Game/IW5/IW5.h" + +#include + +namespace IW5 +{ + void DumpMaterialAsJson(std::ostream& stream, const Material* material, AssetDumpingContext& context); +} // namespace IW5 diff --git a/src/ObjWriting/Game/IW5/Material/MaterialConstantZoneState.cpp b/src/ObjWriting/Game/IW5/Material/MaterialConstantZoneState.cpp new file mode 100644 index 00000000..033421f8 --- /dev/null +++ b/src/ObjWriting/Game/IW5/Material/MaterialConstantZoneState.cpp @@ -0,0 +1,236 @@ +#include "MaterialConstantZoneState.h" + +#include "Game/IW5/CommonIW5.h" +#include "Game/IW5/GameAssetPoolIW5.h" +#include "Game/IW5/GameIW5.h" +#include "ObjWriting.h" + +namespace IW5 +{ + 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 : g_GameIW5.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 IW5 diff --git a/src/ObjWriting/Game/IW5/Material/MaterialConstantZoneState.h b/src/ObjWriting/Game/IW5/Material/MaterialConstantZoneState.h new file mode 100644 index 00000000..d8a33350 --- /dev/null +++ b/src/ObjWriting/Game/IW5/Material/MaterialConstantZoneState.h @@ -0,0 +1,16 @@ +#pragma once + +#include "Material/AbstractMaterialConstantZoneState.h" + +#include + +namespace IW5 +{ + class MaterialConstantZoneState final : public AbstractMaterialConstantZoneStateDx9 + { + protected: + void ExtractNamesFromZoneInternal() override; + void AddStaticKnownNames() override; + unsigned HashString(const std::string& str) override; + }; +} // namespace IW5 diff --git a/src/ObjWriting/Game/IW5/ZoneDumperIW5.cpp b/src/ObjWriting/Game/IW5/ZoneDumperIW5.cpp index ea18d89c..f102023c 100644 --- a/src/ObjWriting/Game/IW5/ZoneDumperIW5.cpp +++ b/src/ObjWriting/Game/IW5/ZoneDumperIW5.cpp @@ -5,6 +5,7 @@ #include "AssetDumpers/AssetDumperLeaderboardDef.h" #include "AssetDumpers/AssetDumperLoadedSound.h" #include "AssetDumpers/AssetDumperLocalizeEntry.h" +#include "AssetDumpers/AssetDumperMaterial.h" #include "AssetDumpers/AssetDumperMenuDef.h" #include "AssetDumpers/AssetDumperMenuList.h" #include "AssetDumpers/AssetDumperRawFile.h" @@ -39,7 +40,7 @@ bool ZoneDumper::DumpZone(AssetDumpingContext& context) const // DUMP_ASSET_POOL(AssetDumperXAnimParts, m_xanim_parts, ASSET_TYPE_XANIMPARTS) // DUMP_ASSET_POOL(AssetDumperXModelSurfs, m_xmodel_surfs, ASSET_TYPE_XMODEL_SURFS) 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(AssetDumperMaterialPixelShader, m_material_pixel_shader, ASSET_TYPE_PIXELSHADER) // DUMP_ASSET_POOL(AssetDumperMaterialVertexShader, m_material_vertex_shader, ASSET_TYPE_VERTEXSHADER) // DUMP_ASSET_POOL(AssetDumperMaterialVertexDeclaration, m_material_vertex_decl, ASSET_TYPE_VERTEXDECL)