Dump iw3 materials

This commit is contained in:
Jan 2022-04-21 19:58:31 +02:00
parent ebb13e18da
commit c91d71e809
7 changed files with 926 additions and 10 deletions

View File

@ -7,6 +7,20 @@ namespace IW3
class Common class Common
{ {
public: public:
static constexpr uint32_t R_HashString(const char* string, const uint32_t hash)
{
const char* v2 = string; // edx@1
char v3 = *string; // cl@1
uint32_t result = hash;
for (; *v2; v3 = *v2)
{
++v2;
result = 33 * result ^ (v3 | 0x20);
}
return result;
}
static PackedTexCoords Vec2PackTexCoords(const vec2_t* in); static PackedTexCoords Vec2PackTexCoords(const vec2_t* in);
static PackedUnitVec Vec3PackUnitVec(const vec3_t* in); static PackedUnitVec Vec3PackUnitVec(const vec3_t* in);
static GfxColor Vec4PackGfxColor(const vec4_t* in); static GfxColor Vec4PackGfxColor(const vec4_t* in);

View File

@ -509,6 +509,109 @@ namespace IW3
PhysGeomList* physGeoms; PhysGeomList* physGeoms;
}; };
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_MASK = 0xF,
};
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_MASK = 0x7,
};
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,
GFXS_STENCILOP_COUNT,
GFXS_STENCILOP_MASK = 0x7
};
enum GfxStateBitsEnum : unsigned int
{
GFXS0_SRCBLEND_RGB_SHIFT = 0x0,
GFXS0_SRCBLEND_RGB_MASK = 0xF,
GFXS0_DSTBLEND_RGB_SHIFT = 0x4,
GFXS0_DSTBLEND_RGB_MASK = 0xF0,
GFXS0_BLENDOP_RGB_SHIFT = 0x8,
GFXS0_BLENDOP_RGB_MASK = 0x700,
GFXS0_BLEND_RGB_MASK = 0x7FF,
GFXS0_ATEST_DISABLE = 0x800,
GFXS0_ATEST_GT_0 = 0x1000,
GFXS0_ATEST_LT_128 = 0x2000,
GFXS0_ATEST_GE_128 = 0x3000,
GFXS0_ATEST_MASK = 0x3000,
GFXS0_CULL_SHIFT = 0xE,
GFXS0_CULL_NONE = 0x4000,
GFXS0_CULL_BACK = 0x8000,
GFXS0_CULL_FRONT = 0xC000,
GFXS0_CULL_MASK = 0xC000,
GFXS0_SRCBLEND_ALPHA_SHIFT = 0x10,
GFXS0_SRCBLEND_ALPHA_MASK = 0xF0000,
GFXS0_DSTBLEND_ALPHA_SHIFT = 0x14,
GFXS0_DSTBLEND_ALPHA_MASK = 0xF00000,
GFXS0_BLENDOP_ALPHA_SHIFT = 0x18,
GFXS0_BLENDOP_ALPHA_MASK = 0x7000000,
GFXS0_BLEND_ALPHA_MASK = 0x7FF0000,
GFXS0_COLORWRITE_RGB = 0x8000000,
GFXS0_COLORWRITE_ALPHA = 0x10000000,
GFXS0_COLORWRITE_MASK = 0x18000000,
GFXS0_POLYMODE_LINE = 0x80000000,
GFXS1_DEPTHWRITE = 0x1,
GFXS1_DEPTHTEST_DISABLE = 0x2,
GFXS1_DEPTHTEST_SHIFT = 0x2,
GFXS1_DEPTHTEST_ALWAYS = 0x0,
GFXS1_DEPTHTEST_LESS = 0x4,
GFXS1_DEPTHTEST_EQUAL = 0x8,
GFXS1_DEPTHTEST_LESSEQUAL = 0xC,
GFXS1_DEPTHTEST_MASK = 0xC,
GFXS1_POLYGON_OFFSET_SHIFT = 0x4,
GFXS1_POLYGON_OFFSET_0 = 0x0,
GFXS1_POLYGON_OFFSET_1 = 0x10,
GFXS1_POLYGON_OFFSET_2 = 0x20,
GFXS1_POLYGON_OFFSET_SHADOWMAP = 0x30,
GFXS1_POLYGON_OFFSET_MASK = 0x30,
GFXS1_STENCIL_FRONT_ENABLE = 0x40,
GFXS1_STENCIL_BACK_ENABLE = 0x80,
GFXS1_STENCIL_MASK = 0xC0,
GFXS1_STENCIL_FRONT_PASS_SHIFT = 0x8,
GFXS1_STENCIL_FRONT_FAIL_SHIFT = 0xB,
GFXS1_STENCIL_FRONT_ZFAIL_SHIFT = 0xE,
GFXS1_STENCIL_FRONT_FUNC_SHIFT = 0x11,
GFXS1_STENCIL_FRONT_MASK = 0xFFF00,
GFXS1_STENCIL_BACK_PASS_SHIFT = 0x14,
GFXS1_STENCIL_BACK_FAIL_SHIFT = 0x17,
GFXS1_STENCIL_BACK_ZFAIL_SHIFT = 0x1A,
GFXS1_STENCIL_BACK_FUNC_SHIFT = 0x1D,
GFXS1_STENCIL_BACK_MASK = 0xFFF00000,
GFXS1_STENCILFUNC_FRONTBACK_MASK = 0xE00E0000,
GFXS1_STENCILOP_FRONTBACK_MASK = 0x1FF1FF00,
};
struct GfxStateBits struct GfxStateBits
{ {
unsigned int loadBits[2]; unsigned int loadBits[2];
@ -555,13 +658,38 @@ namespace IW3
water_t* water; water_t* water;
}; };
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 MaterialTextureDef struct MaterialTextureDef
{ {
unsigned int nameHash; unsigned int nameHash;
char nameStart; char nameStart;
char nameEnd; char nameEnd;
char samplerState; unsigned char samplerState; // SamplerStateBits_e
char semantic; unsigned char semantic; // TextureSemantic
MaterialTextureDefInfo u; MaterialTextureDefInfo u;
}; };
@ -584,13 +712,48 @@ namespace IW3
gcc_align(8) uint64_t packed; gcc_align(8) uint64_t packed;
}; };
enum materialSurfType_t
{
SURF_TYPE_DEFAULT,
SURF_TYPE_BARK,
SURF_TYPE_BRICK,
SURF_TYPE_CARPET,
SURF_TYPE_CLOTH,
SURF_TYPE_CONCRETE,
SURF_TYPE_DIRT,
SURF_TYPE_FLESH,
SURF_TYPE_FOLIAGE,
SURF_TYPE_GLASS,
SURF_TYPE_GRASS,
SURF_TYPE_GRAVEL,
SURF_TYPE_ICE,
SURF_TYPE_METAL,
SURF_TYPE_MUD,
SURF_TYPE_PAPER,
SURF_TYPE_PLASTER,
SURF_TYPE_ROCK,
SURF_TYPE_SAND,
SURF_TYPE_SNOW,
SURF_TYPE_WATER,
SURF_TYPE_WOOD,
SURF_TYPE_ASPHALT,
SURF_TYPE_CERAMIC,
SURF_TYPE_PLASTIC,
SURF_TYPE_RUBBER,
SURF_TYPE_CUSHION,
SURF_TYPE_FRUIT,
SURF_TYPE_PAINTED_METAL,
SURF_TYPE_NUM
};
struct MaterialInfo struct MaterialInfo
{ {
const char* name; const char* name;
char gameFlags; unsigned char gameFlags;
char sortKey; unsigned char sortKey;
char textureAtlasRowCount; unsigned char textureAtlasRowCount;
char textureAtlasColumnCount; unsigned char textureAtlasColumnCount;
GfxDrawSurf drawSurf; GfxDrawSurf drawSurf;
unsigned int surfaceTypeBits; unsigned int surfaceTypeBits;
uint16_t hashIndex; uint16_t hashIndex;
@ -604,7 +767,7 @@ namespace IW3
unsigned char constantCount; unsigned char constantCount;
unsigned char stateBitsCount; unsigned char stateBitsCount;
unsigned char stateFlags; unsigned char stateFlags;
char cameraRegion; unsigned char cameraRegion;
MaterialTechniqueSet* techniqueSet; MaterialTechniqueSet* techniqueSet;
MaterialTextureDef* textureTable; MaterialTextureDef* textureTable;
MaterialConstantDef* constantTable; MaterialConstantDef* constantTable;
@ -649,10 +812,46 @@ namespace IW3
MaterialArgumentDef u; MaterialArgumentDef u;
}; };
enum MaterialStreamStreamSource_e
{
STREAM_SRC_POSITION = 0x0,
STREAM_SRC_COLOR = 0x1,
STREAM_SRC_TEXCOORD_0 = 0x2,
STREAM_SRC_NORMAL = 0x3,
STREAM_SRC_TANGENT = 0x4,
STREAM_SRC_OPTIONAL_BEGIN = 0x5,
STREAM_SRC_PRE_OPTIONAL_BEGIN = 0x4,
STREAM_SRC_TEXCOORD_1 = 0x5,
STREAM_SRC_TEXCOORD_2 = 0x6,
STREAM_SRC_NORMAL_TRANSFORM_0 = 0x7,
STREAM_SRC_NORMAL_TRANSFORM_1 = 0x8,
STREAM_SRC_COUNT
};
enum MaterialStreamDestination_e
{
STREAM_DST_POSITION = 0x0,
STREAM_DST_NORMAL = 0x1,
STREAM_DST_COLOR_0 = 0x2,
STREAM_DST_COLOR_1 = 0x3,
STREAM_DST_TEXCOORD_0 = 0x4,
STREAM_DST_TEXCOORD_1 = 0x5,
STREAM_DST_TEXCOORD_2 = 0x6,
STREAM_DST_TEXCOORD_3 = 0x7,
STREAM_DST_TEXCOORD_4 = 0x8,
STREAM_DST_TEXCOORD_5 = 0x9,
STREAM_DST_TEXCOORD_6 = 0xA,
STREAM_DST_TEXCOORD_7 = 0xB,
STREAM_DST_COUNT
};
struct MaterialStreamRouting struct MaterialStreamRouting
{ {
char source; unsigned char source;
char dest; unsigned char dest;
}; };
struct MaterialVertexStreamRouting struct MaterialVertexStreamRouting
@ -727,6 +926,53 @@ namespace IW3
MaterialPass passArray[1]; MaterialPass passArray[1];
}; };
enum MaterialTechniqueType
{
TECHNIQUE_DEPTH_PREPASS = 0x0,
TECHNIQUE_BUILD_FLOAT_Z = 0x1,
TECHNIQUE_BUILD_SHADOWMAP_DEPTH = 0x2,
TECHNIQUE_BUILD_SHADOWMAP_COLOR = 0x3,
TECHNIQUE_UNLIT = 0x4,
TECHNIQUE_EMISSIVE = 0x5,
TECHNIQUE_EMISSIVE_SHADOW = 0x6,
TECHNIQUE_LIT_BEGIN = 0x7,
TECHNIQUE_LIT = 0x7,
TECHNIQUE_LIT_SUN = 0x8,
TECHNIQUE_LIT_SUN_SHADOW = 0x9,
TECHNIQUE_LIT_SPOT = 0xA,
TECHNIQUE_LIT_SPOT_SHADOW = 0xB,
TECHNIQUE_LIT_OMNI = 0xC,
TECHNIQUE_LIT_OMNI_SHADOW = 0xD,
TECHNIQUE_LIT_INSTANCED = 0xE,
TECHNIQUE_LIT_INSTANCED_SUN = 0xF,
TECHNIQUE_LIT_INSTANCED_SUN_SHADOW = 0x10,
TECHNIQUE_LIT_INSTANCED_SPOT = 0x11,
TECHNIQUE_LIT_INSTANCED_SPOT_SHADOW = 0x12,
TECHNIQUE_LIT_INSTANCED_OMNI = 0x13,
TECHNIQUE_LIT_INSTANCED_OMNI_SHADOW = 0x14,
TECHNIQUE_LIT_END = 0x15,
TECHNIQUE_LIGHT_SPOT = 0x15,
TECHNIQUE_LIGHT_OMNI = 0x16,
TECHNIQUE_LIGHT_SPOT_SHADOW = 0x17,
TECHNIQUE_FAKELIGHT_NORMAL = 0x18,
TECHNIQUE_FAKELIGHT_VIEW = 0x19,
TECHNIQUE_SUNLIGHT_PREVIEW = 0x1A,
TECHNIQUE_CASE_TEXTURE = 0x1B,
TECHNIQUE_WIREFRAME_SOLID = 0x1C,
TECHNIQUE_WIREFRAME_SHADED = 0x1D,
TECHNIQUE_SHADOWCOOKIE_CASTER = 0x1E,
TECHNIQUE_SHADOWCOOKIE_RECEIVER = 0x1F,
TECHNIQUE_DEBUG_BUMPMAP = 0x20,
TECHNIQUE_DEBUG_BUMPMAP_INSTANCED = 0x21,
TECHNIQUE_COUNT = 0x22,
TECHNIQUE_TOTAL_COUNT = 0x23,
TECHNIQUE_NONE = 0x24,
};
struct MaterialTechniqueSet struct MaterialTechniqueSet
{ {
const char* name; const char* name;

View File

@ -0,0 +1,43 @@
#pragma once
#include <unordered_map>
#include <type_traits>
#include "Game/IW3/IW3.h"
namespace IW3
{
inline const char* surfaceTypeNames[]
{
"default",
"bark",
"brick",
"carpet",
"cloth",
"concrete",
"dirt",
"flesh",
"foliage",
"glass",
"grass",
"gravel",
"ice",
"metal",
"mud",
"paper",
"plaster",
"rock",
"sand",
"snow",
"water",
"wood",
"asphalt",
"ceramic",
"plastic",
"rubber",
"cushion",
"fruit",
"paintedmetal"
};
static_assert(std::extent_v<decltype(surfaceTypeNames)> == SURF_TYPE_NUM);
}

View File

@ -0,0 +1,138 @@
#pragma once
#include <unordered_map>
#include <type_traits>
#include "Game/IW3/CommonIW3.h"
#include "Game/IW3/IW3.h"
namespace IW3
{
inline const char* techniqueTypeNames[]
{
"depth prepass",
"build floatz",
"build shadowmap depth",
"build shadowmap color",
"unlit",
"emissive",
"emissive shadow",
"lit",
"lit sun",
"lit sun shadow",
"lit spot",
"lit spot shadow",
"lit omni",
"lit omni shadow",
"lit instanced",
"lit instanced sun",
"lit instanced sun shadow",
"lit instanced spot",
"lit instanced spot shadow",
"lit instanced omni",
"lit instanced omni shadow",
"light spot",
"light omni",
"light spot shadow",
"fakelight normal",
"fakelight view",
"sunlight preview",
"case texture",
"solid wireframe",
"shaded wireframe",
"shadowcookie caster",
"shadowcookie receiver",
"debug bumpmap",
"debug bumpmap instanced",
};
static_assert(std::extent_v<decltype(techniqueTypeNames)> == TECHNIQUE_COUNT);
static const char* materialStreamDestinationNames[]
{
"position",
"normal",
"color[0]",
"color[1]",
"texcoord[0]",
"texcoord[1]",
"texcoord[2]",
"texcoord[3]",
"texcoord[4]",
"texcoord[5]",
"texcoord[6]",
"texcoord[7]",
};
static_assert(std::extent_v<decltype(materialStreamDestinationNames)> == STREAM_DST_COUNT);
static const char* materialStreamSourceNames[]
{
"position",
"color",
"texcoord[0]",
"normal",
"tangent",
"texcoord[1]",
"texcoord[2]",
"normalTransform[0]",
"normalTransform[1]"
};
static_assert(std::extent_v<decltype(materialStreamSourceNames)> == STREAM_SRC_COUNT);
static constexpr std::pair<uint32_t, const char*> KnownMaterialSource(const char* name)
{
return std::make_pair(Common::R_HashString(name, 0u), name);
}
inline std::unordered_map knownMaterialSourceNames
{
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"),
};
}

View File

@ -0,0 +1,460 @@
#include "AssetDumperMaterial.h"
#include <iomanip>
#include <sstream>
#include <nlohmann/json.hpp>
#include "Game/IW3/MaterialConstantsIW3.h"
#include "Game/IW3/TechsetConstantsIW3.h"
using namespace IW3;
using json = nlohmann::json;
namespace IW3
{
const char* AssetName(const char* name)
{
if (name && name[0] == ',')
return &name[1];
return name;
}
template <size_t S>
json ArrayEntry(const char* (&a)[S], const size_t index)
{
assert(index < S);
if (index < S)
return a[index];
return json{};
}
json BuildComplexTableJson(const 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 ? json{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 BuildSamplerStateJson(unsigned char samplerState)
{
static const char* samplerFilterNames[]
{
"none",
"nearest",
"linear",
"aniso2x",
"aniso4x"
};
static const char* samplerMipmapNames[]
{
"disabled",
"nearest",
"linear"
};
return json{
{"filter", ArrayEntry(samplerFilterNames, (samplerState & SAMPLER_FILTER_MASK) >> SAMPLER_FILTER_SHIFT)},
{"mipmap", ArrayEntry(samplerMipmapNames, (samplerState & SAMPLER_MIPMAP_MASK) >> SAMPLER_MIPMAP_SHIFT)},
{"clampU", (samplerState & SAMPLER_CLAMP_U) ? true : false},
{"clampV", (samplerState & SAMPLER_CLAMP_V) ? true : false},
{"clampW", (samplerState & SAMPLER_CLAMP_W) ? true : false},
};
}
json BuildTextureTableJson(const MaterialTextureDef* textureTable, const size_t count)
{
static const char* semanticNames[]
{
"2d",
"function",
"colorMap",
"unused1",
"unused2",
"normalMap",
"unused3",
"unused4",
"specularMap",
"unused5",
"unused6",
"waterMap"
};
auto jArray = json::array();
if (textureTable)
{
for (auto index = 0u; index < count; index++)
{
const auto& entry = textureTable[index];
json jEntry = {
{"samplerState", BuildSamplerStateJson(entry.samplerState)},
{"semantic", ArrayEntry(semanticNames, 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(const 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<decltype(MaterialConstantDef::name)>);
if (nameLen == std::extent_v<decltype(MaterialConstantDef::name)>)
{
std::string fullLengthName(entry.name, std::extent_v<decltype(MaterialConstantDef::name)>);
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(const GfxStateBits* stateBitsTable, const size_t count)
{
static const char* blendNames[]
{
"disabled",
"zero",
"one",
"srcColor",
"invSrcColor",
"srcAlpha",
"invSrcAlpha",
"destAlpha",
"invDestAlpha",
"destColor",
"invDestColor",
};
static const char* blendOpNames[]
{
"disabled",
"add",
"subtract",
"revSubtract",
"min",
"max"
};
static const char* depthTestNames[]
{
"always",
"less",
"equal",
"lessEqual",
};
static const char* polygonOffsetNames[]
{
"0",
"1",
"2",
"shadowMap",
};
static const char* stencilOpNames[]
{
"keep",
"zero",
"replace",
"incrSat",
"decrSat",
"invert",
"incr",
"decr"
};
auto jArray = json::array();
if (stateBitsTable)
{
for (auto index = 0u; index < count; index++)
{
const auto& entry = stateBitsTable[index];
const auto srcBlendRgb = (entry.loadBits[0] & GFXS0_SRCBLEND_RGB_MASK) >> GFXS0_SRCBLEND_RGB_SHIFT;
const auto dstBlendRgb = (entry.loadBits[0] & GFXS0_DSTBLEND_RGB_MASK) >> GFXS0_DSTBLEND_RGB_SHIFT;
const auto blendOpRgb = (entry.loadBits[0] & GFXS0_BLENDOP_RGB_MASK) >> GFXS0_BLENDOP_RGB_SHIFT;
const auto srcBlendAlpha = (entry.loadBits[0] & GFXS0_SRCBLEND_ALPHA_MASK) >> GFXS0_SRCBLEND_ALPHA_SHIFT;
const auto dstBlendAlpha = (entry.loadBits[0] & GFXS0_DSTBLEND_ALPHA_MASK) >> GFXS0_DSTBLEND_ALPHA_SHIFT;
const auto blendOpAlpha = (entry.loadBits[0] & GFXS0_BLENDOP_ALPHA_MASK) >> GFXS0_BLENDOP_ALPHA_SHIFT;
const auto depthTest = (entry.loadBits[1] & GFXS1_DEPTHTEST_MASK) >> GFXS1_DEPTHTEST_SHIFT;
const auto polygonOffset = (entry.loadBits[1] & GFXS1_POLYGON_OFFSET_MASK) >> GFXS1_POLYGON_OFFSET_SHIFT;
const auto* alphaTest = "disable";
if (entry.loadBits[0] & GFXS0_ATEST_GT_0)
alphaTest = "gt0";
else if (entry.loadBits[0] & GFXS0_ATEST_LT_128)
alphaTest = "lt128";
else if (entry.loadBits[0] & GFXS0_ATEST_GE_128)
alphaTest = "ge128";
else
assert(entry.loadBits[0] & GFXS0_ATEST_DISABLE);
const auto* cullFace = "none";
if ((entry.loadBits[0] & GFXS0_CULL_MASK) == GFXS0_CULL_BACK)
cullFace = "back";
else if ((entry.loadBits[0] & GFXS0_CULL_MASK) == GFXS0_CULL_FRONT)
cullFace = "front";
else
assert((entry.loadBits[0] & GFXS0_CULL_MASK) == GFXS0_CULL_NONE);
jArray.emplace_back(json{
{"srcBlendRgb", ArrayEntry(blendNames, srcBlendRgb)},
{"dstBlendRgb", ArrayEntry(blendNames, dstBlendRgb)},
{"blendOpRgb", ArrayEntry(blendOpNames, blendOpRgb)},
{"alphaTest", alphaTest},
{"cullFace", cullFace},
{"srcBlendAlpha", ArrayEntry(blendNames, srcBlendAlpha)},
{"dstBlendAlpha", ArrayEntry(blendNames, dstBlendAlpha)},
{"blendOpAlpha", ArrayEntry(blendOpNames, blendOpAlpha)},
{"colorWriteRgb", (entry.loadBits[0] & GFXS0_COLORWRITE_RGB) ? true : false},
{"colorWriteAlpha", (entry.loadBits[0] & GFXS0_COLORWRITE_ALPHA) ? true : false},
{"polymodeLine", (entry.loadBits[0] & GFXS0_POLYMODE_LINE) ? true : false},
{"depthWrite", (entry.loadBits[1] & GFXS1_DEPTHWRITE) ? true : false},
{"depthTest", (entry.loadBits[1] & GFXS1_DEPTHTEST_DISABLE) ? json("disable") : ArrayEntry(depthTestNames, depthTest)},
{"polygonOffset", ArrayEntry(polygonOffsetNames, polygonOffset)},
{"stencilFrontEnabled", (entry.loadBits[1] & GFXS1_STENCIL_FRONT_ENABLE) ? true : false},
{"stencilBackEnabled", (entry.loadBits[1] & GFXS1_STENCIL_BACK_ENABLE) ? true : false},
{"stencilFrontPass", ArrayEntry(stencilOpNames, (entry.loadBits[1] >> GFXS1_STENCIL_FRONT_PASS_SHIFT) & GFXS_STENCILOP_MASK)},
{"stencilFrontFail", ArrayEntry(stencilOpNames, (entry.loadBits[1] >> GFXS1_STENCIL_FRONT_FAIL_SHIFT) & GFXS_STENCILOP_MASK)},
{"stencilFrontZFail", ArrayEntry(stencilOpNames, (entry.loadBits[1] >> GFXS1_STENCIL_FRONT_ZFAIL_SHIFT) & GFXS_STENCILOP_MASK)},
{"stencilFrontFunc", ArrayEntry(stencilOpNames, (entry.loadBits[1] >> GFXS1_STENCIL_FRONT_FUNC_SHIFT) & GFXS_STENCILOP_MASK)},
{"stencilBackPass", ArrayEntry(stencilOpNames, (entry.loadBits[1] >> GFXS1_STENCIL_BACK_PASS_SHIFT) & GFXS_STENCILOP_MASK)},
{"stencilBackFail", ArrayEntry(stencilOpNames, (entry.loadBits[1] >> GFXS1_STENCIL_BACK_FAIL_SHIFT) & GFXS_STENCILOP_MASK)},
{"stencilBackZFail", ArrayEntry(stencilOpNames, (entry.loadBits[1] >> GFXS1_STENCIL_BACK_ZFAIL_SHIFT) & GFXS_STENCILOP_MASK)},
{"stencilBackFunc", ArrayEntry(stencilOpNames, (entry.loadBits[1] >> GFXS1_STENCIL_BACK_FUNC_SHIFT) & GFXS_STENCILOP_MASK)},
});
}
}
return jArray;
}
json BuildSurfaceTypeBitsJson(const unsigned surfaceTypeBits)
{
if (!surfaceTypeBits)
return json(surfaceTypeNames[SURF_TYPE_DEFAULT]);
static constexpr auto NON_SURFACE_TYPE_BITS = ~(std::numeric_limits<unsigned>::max() >> ((sizeof(unsigned) * 8) - (static_cast<unsigned>(SURF_TYPE_NUM) - 1)));
assert((surfaceTypeBits & NON_SURFACE_TYPE_BITS) == 0);
std::ostringstream ss;
auto firstSurfaceType = true;
for (auto surfaceTypeIndex = static_cast<unsigned>(SURF_TYPE_BARK); surfaceTypeIndex < SURF_TYPE_NUM; surfaceTypeIndex++)
{
if ((surfaceTypeBits & (1 << (surfaceTypeIndex - 1))) == 0)
continue;
if (firstSurfaceType)
firstSurfaceType = false;
else
ss << ",";
ss << surfaceTypeNames[surfaceTypeIndex];
}
if (firstSurfaceType)
return json(surfaceTypeNames[SURF_TYPE_DEFAULT]);
return json(ss.str());
}
}
bool AssetDumperMaterial::ShouldDump(XAssetInfo<Material>* asset)
{
return true;
}
void AssetDumperMaterial::DumpAsset(AssetDumpingContext& context, XAssetInfo<Material>* 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;
static const char* cameraRegionNames[]
{
"lit",
"decal",
"emissive",
"none"
};
static std::unordered_map<size_t, std::string> sortKeyNames
{
{0, "distortion"},
{1, "opaque water"},
{2, "boat hull"},
{3, "opaque ambient"},
{4, "opaque"},
{5, "sky"},
{6, "skybox - sun / moon"},
{7, "skybox - clouds"},
{8, "skybox - horizon"},
{9, "decal - bottom 1"},
{10, "decal - bottom 2"},
{11, "decal - bottom 3"},
{12, "decal - static decal"},
{13, "decal - middle 1"},
{14, "decal - middle 2"},
{15, "decal - middle 3"},
{24, "decal - weapon impact"},
{29, "decal - top 1"},
{30, "decal - top 2"},
{31, "decal - top 3"},
{32, "multiplicative"},
{33, "banner / curtain"},
{34, "hair"},
{35, "underwater"},
{36, "transparent water"},
{37, "corona"},
{38, "window inside"},
{39, "window outside"},
{40, "before effects - bottom"},
{41, "before effects - middle"},
{42, "before effects - top"},
{43, "blend / additive"},
{48, "effect - auto sort"},
{56, "after effects - bottom"},
{57, "after effects - middle"},
{58, "after effects - top"},
{59, "viewmodel effect"},
};
const auto foundSortKeyName = sortKeyNames.find(material->info.sortKey);
assert(foundSortKeyName != sortKeyNames.end());
const json j = {
{
"info", {
{"gameFlags", material->info.gameFlags}, // TODO: Find out what gameflags mean
{"sortKey", foundSortKeyName != sortKeyNames.end() ? foundSortKeyName->second : std::to_string(material->info.sortKey)},
{"textureAtlasRowCount", material->info.textureAtlasRowCount},
{"textureAtlasColumnCount", material->info.textureAtlasColumnCount},
{
"drawSurf", {
{"objectId", static_cast<unsigned>(material->info.drawSurf.fields.objectId)},
{"reflectionProbeIndex", static_cast<unsigned>(material->info.drawSurf.fields.reflectionProbeIndex)},
{"customIndex", static_cast<unsigned>(material->info.drawSurf.fields.customIndex)},
{"materialSortedIndex", static_cast<unsigned>(material->info.drawSurf.fields.materialSortedIndex)},
{"prepass", static_cast<unsigned>(material->info.drawSurf.fields.prepass)},
{"useHeroLighting", static_cast<unsigned>(material->info.drawSurf.fields.primaryLightIndex)},
{"surfType", static_cast<unsigned>(material->info.drawSurf.fields.surfType)},
{"primarySortKey", static_cast<unsigned>(material->info.drawSurf.fields.primarySortKey)}
}
},
{"surfaceTypeBits", BuildSurfaceTypeBitsJson(material->info.surfaceTypeBits)},
{"hashIndex", material->info.hashIndex}
}
},
{"stateBitsEntry", std::vector(std::begin(material->stateBitsEntry), std::end(material->stateBitsEntry))},
{"stateFlags", material->stateFlags},
{"cameraRegion", ArrayEntry(cameraRegionNames, 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;
}

View File

@ -0,0 +1,14 @@
#pragma once
#include "Dumping/AbstractAssetDumper.h"
#include "Game/IW3/IW3.h"
namespace IW3
{
class AssetDumperMaterial final : public AbstractAssetDumper<Material>
{
protected:
bool ShouldDump(XAssetInfo<Material>* asset) override;
void DumpAsset(AssetDumpingContext& context, XAssetInfo<Material>* asset) override;
};
}

View File

@ -8,6 +8,7 @@
#include "AssetDumpers/AssetDumperLoadedSound.h" #include "AssetDumpers/AssetDumperLoadedSound.h"
#include "AssetDumpers/AssetDumperLocalizeEntry.h" #include "AssetDumpers/AssetDumperLocalizeEntry.h"
#include "AssetDumpers/AssetDumperMapEnts.h" #include "AssetDumpers/AssetDumperMapEnts.h"
#include "AssetDumpers/AssetDumperMaterial.h"
#include "AssetDumpers/AssetDumperRawFile.h" #include "AssetDumpers/AssetDumperRawFile.h"
#include "AssetDumpers/AssetDumperStringTable.h" #include "AssetDumpers/AssetDumperStringTable.h"
#include "AssetDumpers/AssetDumperWeapon.h" #include "AssetDumpers/AssetDumperWeapon.h"
@ -34,7 +35,7 @@ bool ZoneDumper::DumpZone(AssetDumpingContext& context) const
// DUMP_ASSET_POOL(AssetDumperPhysPreset, m_phys_preset, ASSET_TYPE_PHYSPRESET) // DUMP_ASSET_POOL(AssetDumperPhysPreset, m_phys_preset, ASSET_TYPE_PHYSPRESET)
// DUMP_ASSET_POOL(AssetDumperXAnimParts, m_xanim_parts, ASSET_TYPE_XANIMPARTS) // DUMP_ASSET_POOL(AssetDumperXAnimParts, m_xanim_parts, ASSET_TYPE_XANIMPARTS)
DUMP_ASSET_POOL(AssetDumperXModel, m_xmodel, ASSET_TYPE_XMODEL) 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(AssetDumperMaterialTechniqueSet, m_technique_set, ASSET_TYPE_TECHNIQUE_SET) // DUMP_ASSET_POOL(AssetDumperMaterialTechniqueSet, m_technique_set, ASSET_TYPE_TECHNIQUE_SET)
DUMP_ASSET_POOL(AssetDumperGfxImage, m_image, ASSET_TYPE_IMAGE) DUMP_ASSET_POOL(AssetDumperGfxImage, m_image, ASSET_TYPE_IMAGE)
// DUMP_ASSET_POOL(AssetDumpersnd_alias_list_t, m_sound, ASSET_TYPE_SOUND) // DUMP_ASSET_POOL(AssetDumpersnd_alias_list_t, m_sound, ASSET_TYPE_SOUND)