Revert "feat(dashboard): one table of the game's shader techniques for the 3D views"

This reverts commit ee9135437f.
This commit is contained in:
Aaron Kimbrell
2026-09-28 13:53:44 -05:00
parent c3b52545b5
commit d6dd573741
7 changed files with 123 additions and 515 deletions

View File

@@ -719,182 +719,79 @@ namespace NifFile {
return tagShader && *tagShader >= 3 && *tagShader <= 0x6C ? *tagShader : LEGO_SHADER;
}
namespace {
using F = eShaderFamily;
constexpr auto OPACITY = eTextureAlpha::OPACITY, DECAL = eTextureAlpha::DECAL, IGNORED = eTextureAlpha::IGNORED;
struct TechniqueRow {
int32_t shader;
ShaderTechnique technique;
};
/**
* Every mapShaders gameValue, by the technique its shader class sets up (ShaderManager's factory table at
* 0x01889608, indexed by gameValue; the class's technique setup names it) and the client's res/shaders/*.fx.
* Checked in the client: 33 and 82 Technique_Basic_NoLighting_VertColor_NoTexture, 35 and 84
* Technique_Basic_NoLighting_VertColor, 37 Technique_Basic_Lighting_VertColor_NoTexture, 38 and 94
* Technique_Basic_Lighting_VertColor (94 "Basic": the vtable slot at +0x90 of the class made at 0x0045f240 names
* it), 70 Technique_AlphaAsAlpha_UVScrolling_SimpleV_NoLighting_AlphaAnim, 105
* Technique_TwoLayersBlended_NoLighting_VertColor_UVScrolling. The rest follow their mapShaders labels ("NL" no
* lighting, "NT" no texture, "VC" vertex colors, "AnimUV"/"ScrollingUV" the texture transform, "OneSidedAlpha"
* AlphaAsAlpha culled) and the technique of that name in res/shaders.
*/
constexpr TechniqueRow TECHNIQUES[] = {
{ -1, { F::FIXED_FUNCTION, 0, OPACITY, 0 } },
// TerrainDiffuse.fx's mesh techniques: the texture times the vertex colors and the light, alpha only the fade
{ 2, { F::TERRAIN, 0, OPACITY, NO_BLEND } }, // Terrain Mesh
{ 3, { F::TERRAIN, 0, IGNORED, RIM_LIGHT | NO_BLEND } }, // Terrain Mesh Rim Light
{ 97, { F::TERRAIN, 0, OPACITY, DIFFUSE_ONLY | NO_BLEND } }, // Terrain Diffuse Map Only
eTextureAlpha TextureAlphaFor(int32_t shader) {
switch (shader) {
// LEGOPPLighting: textured alone the texture's alpha is forced to 1; with vertex colors it only lays the
// texture over them (lerp by its alpha) and the vertex alpha is what shows through
{ 4, { F::LEGO, 0, DECAL, 0 } }, // LEGO (No LOD)
{ 5, { F::LEGO, 0, DECAL, 0 } }, // LEGO
{ 12, { F::LEGO, 0, DECAL, 0 } }, // LEGO-Reveal (the reveal mask left out)
{ 14, { F::LEGO, 0, OPACITY, NON_DECAL } }, // LEGO Masked NonDecal (the specular mask left out)
{ 19, { F::LEGO, 0, DECAL, 0 } }, // Powerups (their own effect, drawn as LEGO)
{ 20, { F::LEGO, 0, DECAL, 0 } }, // Orb (Powerups.fx, drawn as LEGO)
{ 22, { F::LEGO, EMISSIVE, OPACITY, SUPER_EMISSIVE } }, // LEGO-SuperEmissive
{ 25, { F::LEGO, 0, DECAL, 0 } }, // LEGO_FrontEnd
{ 26, { F::LEGO, 0, OPACITY, NON_DECAL } }, // LEGO_FaceCreate
{ 27, { F::LEGO, 0, DECAL, GLOW } }, // LEGO-Glow
{ 28, { F::LEGO, 0, DECAL, GRAYSCALE } }, // LEGO-Grayscale
{ 29, { F::LEGO, 0, DECAL, GLOW | IGNORE_VERTEX_ALPHA } }, // LEGO-Glow-IgnoreVertAlpha
{ 30, { F::LEGO, 0, DECAL, UV_ANIM } }, // LEGO-AnimUV
case 4: case 5: case 12: case 25: case 27: case 28: case 29: case 30: case 50: case 72: case 88:
// Darkling: the same lay-over; alpha from lighting or the fade
case 75: case 76: case 77: case 102: case 103: case 104:
return eTextureAlpha::DECAL;
// LEGOPPLighting_Item: texture alpha forced to 1, multiplied by the vertex colors
{ 31, { F::LEGO, 0, IGNORED, NON_DECAL } }, // LEGO-Item
{ 48, { F::LEGO, 0, IGNORED, NON_DECAL | GLOW } }, // LEGO-ItemGlow
{ 50, { F::LEGO, 0, DECAL, 0 } }, // LEGO-FadeUp (as when faded in)
{ 53, { F::LEGO, EMISSIVE, OPACITY, 0 } }, // LEGO-Emissive
{ 72, { F::LEGO, 0, DECAL, SHINY_GLINT } }, // ShinyGlint
{ 88, { F::LEGO, 0, DECAL, NO_AMBIENT } }, // LEGO NoAmbient
{ 92, { F::LEGO, 0, DECAL, 0 } }, // Pet Taming LEGO In Cloud
// LEGOPPLighting's NL pixel shaders: the vertex color or texture as it is
{ 52, { F::BASIC, UNLIT, OPACITY, ANIM_ALPHA } }, // LEGO-No Light
// Darkling: the same lay-over; the dark texture on the second UV set through a window of vertex alphas
{ 75, { F::DARKLING, 0, DECAL, 0 } }, // Darkling
{ 76, { F::DARKLING, 0, DECAL, SPECULAR } }, // Darkling /w Specular
{ 77, { F::DARKLING, 0, DECAL, NON_DECAL } }, // Darkling Structure
{ 102, { F::DARKLING, 0, DECAL, SHINY_GLINT } }, // Darking Shiny Glint
{ 103, { F::DARKLING, 0, DECAL, SPECULAR | SHINY_GLINT } }, // Darkling /w Specular Shiny Glint
{ 104, { F::DARKLING, 0, DECAL, NON_DECAL | SHINY_GLINT } }, // Darkling Structure Shiny Glint
// AlphaAsAlpha: texture times the (lit) vertex color, both sides
{ 7, { F::BASIC, 0, OPACITY, DOUBLE_SIDED } }, // VertColor_Alpha
{ 8, { F::BASIC, UNLIT, OPACITY, DOUBLE_SIDED | ANIM_ALPHA } }, // VertColor_NoLighting_Alpha
{ 9, { F::BASIC, 0, OPACITY, DOUBLE_SIDED } }, // VertColor_Alpha_Fade
{ 10, { F::BASIC, UNLIT, OPACITY, DOUBLE_SIDED | ANIM_ALPHA | BLEND } }, // VertColorTex_NoLight_AlphaBlend
{ 54, { F::BASIC, UNLIT, OPACITY, DOUBLE_SIDED | ANIM_ALPHA | ALPHA_TEST } }, // VertColorTex_NoLight_AlphaTest
{ 13, { F::BASIC, 0, OPACITY, UV_ANIM } }, // ScrollingUV
{ 70, { F::BASIC, UNLIT, OPACITY, UV_ANIM | ANIM_ALPHA } }, // ScrollingUV_NoLight_AnimAlpha
{ 73, { F::BASIC, UNLIT, OPACITY, UV_ANIM | ANIM_ALPHA } }, // ScrollingUV_NoLight_AimAlpha_Post
{ 81, { F::BASIC, UNLIT, OPACITY, UV_ANIM | ANIM_ALPHA | NO_FOG } }, // ScrollingUV NL AnimAlpha NoFog
// OneSidedAlpha: the same, culled
{ 55, { F::BASIC, 0, OPACITY, 0 } }, // OneSidedAlpha VC
{ 56, { F::BASIC, UNLIT | NO_VERTEX_COLORS, OPACITY, 0 } }, // OneSidedAlpha NL
{ 57, { F::BASIC, UNLIT, OPACITY, ANIM_ALPHA } }, // OneSidedAlpha NL VC
{ 58, { F::BASIC, UNLIT | NO_TEXTURE, OPACITY, ANIM_ALPHA } }, // OneSidedAlpha NL VC NT
{ 59, { F::BASIC, 0, OPACITY, UV_ANIM } }, // OneSidedAlpha AnimUV V Skinned
{ 60, { F::BASIC, 0, OPACITY, 0 } }, // OneSidedAlpha VC Skinned
{ 61, { F::BASIC, UNLIT | NO_VERTEX_COLORS, OPACITY, 0 } }, // OneSidedAlpha NL Skinned
{ 62, { F::BASIC, UNLIT, OPACITY, ANIM_ALPHA } }, // OneSidedAlpha NL VC Skinned
{ 63, { F::BASIC, UNLIT | NO_TEXTURE, OPACITY, ANIM_ALPHA } }, // OneSidedAlpha NL VC NT Skinned
{ 64, { F::BASIC, 0, OPACITY, UV_ANIM } }, // OneSidedAlpha AnimUV V
{ 68, { F::BASIC, UNLIT, OPACITY, ANIM_ALPHA } }, // OneSidedAlpha NL AnimAlpha
// BasicShaders
{ 11, { F::BASIC, UNLIT | NO_TEXTURE, OPACITY, ANIM_ALPHA } }, // VertColor_NoLight_NoTex_AnimAlpha
{ 15, { F::BASIC, UNLIT, OPACITY, 0 } }, // VC_NoLighting_2D
{ 16, { F::BASIC, UNLIT | NO_TEXTURE, OPACITY, 0 } }, // VC_NL_NoTex_2D
{ 17, { F::BASIC, UNLIT, OPACITY, 0 } }, // TV Screen (its static and flicker left out)
{ 18, { F::BASIC, UNLIT, OPACITY, 0 } }, // Head Icon
{ 23, { F::BASIC, UNLIT, OPACITY, 0 } }, // Over Everything (Unlit)
{ 24, { F::BASIC, UNLIT, OPACITY, NO_FOG | BLEND } }, // Fogless GrayBubble
{ 32, { F::BASIC, UNLIT | NO_VERTEX_COLORS | MATERIAL_COLOR, OPACITY, 0 } }, // Basic NL Material
{ 33, { F::BASIC, UNLIT | NO_TEXTURE, OPACITY, ANIM_ALPHA } }, // Basic NL VC NT
{ 34, { F::BASIC, UNLIT | NO_VERTEX_COLORS, OPACITY, 0 } }, // Basic NL
{ 35, { F::BASIC, UNLIT, OPACITY, 0 } }, // Basic NL VC
{ 36, { F::BASIC, UNLIT | NO_VERTEX_COLORS, OPACITY, UV_ANIM } }, // Basic NL UVAnim
{ 37, { F::BASIC, NO_TEXTURE, OPACITY, 0 } }, // Basic VC NT
{ 38, { F::BASIC, 0, OPACITY, 0 } }, // Basic VC
{ 39, { F::BASIC, 0, OPACITY, UV_ANIM } }, // Basic VC UVAnim
{ 49, { F::BASIC, 0, OPACITY, 0 } }, // Experimental Stub
{ 65, { F::BASIC, 0, OPACITY, BASIC_EMISSIVE } }, // VC_Texture_Emissive
{ 80, { F::BASIC, UNLIT | NO_TEXTURE, OPACITY, 0 } }, // Basic NL NT
{ 82, { F::BASIC, UNLIT | NO_TEXTURE, OPACITY, NO_BLEND | NO_FOG } }, // Opaque NL VC NT NoFog
{ 83, { F::BASIC, UNLIT | NO_VERTEX_COLORS, OPACITY, NO_BLEND | NO_FOG } }, // Opaque NL NoFog
{ 84, { F::BASIC, UNLIT, OPACITY, NO_BLEND | NO_FOG } }, // Opaque NL VC NoFog
{ 85, { F::BASIC, NO_TEXTURE, OPACITY, NO_BLEND | NO_FOG } }, // Opaque VC NT NoFog
{ 86, { F::BASIC, 0, OPACITY, NO_BLEND | NO_FOG } }, // Opaque VC NoFog
{ 87, { F::BASIC, UNLIT, OPACITY, ADDITIVE } }, // Additive NoLight VertColor
{ 91, { F::BASIC, UNLIT, OPACITY, BLEND } }, // Pet Taming Imagination Cloud
{ 94, { F::BASIC, 0, OPACITY, 0 } }, // Basic
{ 108, { F::BASIC, UNLIT | NO_VERTEX_COLORS | MATERIAL_COLOR, OPACITY, 0 } }, // Over Everything Material Unlit
// Two layers (TwoLayersAdded_PS in BasicShaders.fx; the client ships no Technique_TwoLayersBlended_* shader,
// so the blended ones follow the meshes' data: the dark texture under the base one by the vertex alpha, as
// Avant Gardens' snow caps and grass fade into rock by it)
{ 93, { F::BASIC, UNLIT | TWO_LAYERS_ADDED, OPACITY, UV_ANIM } }, // Two Textures Added NL VC AnimUV
{ 105, { F::BASIC, UNLIT | TWO_LAYERS_BLENDED, OPACITY, UV_ANIM } }, // Two Layers Blended NL VC AnimUV
{ 106, { F::BASIC, TWO_LAYERS_BLENDED, OPACITY, UV_ANIM } }, // Two Layers Blended VC AnimUV
{ 107, { F::BASIC, TWO_LAYERS_ADDED, OPACITY, UV_ANIM } }, // Two Layers Added VC AnimUV
// Metallic.fx: both load their reflection cubes themselves (textures/metal)
{ 98, { F::METAL, REFLECTIVE, OPACITY, 0 } }, // Polished Metal
{ 99, { F::METAL, REFLECTIVE | BRUSHED, OPACITY, 0 } }, // Brushed Steel
{ 100, { F::METAL, REFLECTIVE | BRUSHED, OPACITY, 0 } }, // Brushed Steel Item
{ 6, { F::CLEAR_PLASTIC, 0, OPACITY, BLEND } }, // Clear Plastic
{ 51, { F::BRICK_WATER, 0, OPACITY, 0 } }, // BrickWater
// Ocean.fx
{ 69, { F::OCEAN, 0, OPACITY, UV_ANIM } }, // Distortion (Ocean)
{ 89, { F::OCEAN, 0, OPACITY, UV_ANIM } }, // Distortion Directional (Ocean)
{ 90, { F::OCEAN, UNLIT, OPACITY, UV_ANIM | OCEAN_FX } }, // Distortion FX (Ocean)
{ 95, { F::OCEAN, 0, OPACITY, UV_ANIM | BLEND } }, // Distortion NoDepth (Ocean) (Alpha)
{ 101, { F::OCEAN, UNLIT, OPACITY, UV_ANIM } }, // Distortion (Ocean) Unlit
{ 78, { F::FLAT_SURF, 0, OPACITY, UV_ANIM } }, // Flat Surf
// Drawn by other passes, not in the world: footprints, post-processing, drop shadows, Technique_Undefined
{ 21, { F::BASIC, 0, OPACITY, NOT_DRAWN } }, // Model Footprint
{ 71, { F::BASIC, 0, OPACITY, NOT_DRAWN } }, // PostProcess Gray Bubble
{ 74, { F::BASIC, 0, OPACITY, NOT_DRAWN } }, // Drop Shadow
{ 79, { F::BASIC, 0, OPACITY, NOT_DRAWN } }, // Post Process Gray Bubble Interior Ghost
{ 96, { F::BASIC, 0, OPACITY, NOT_DRAWN } }, // Undefined
};
}
const char* FamilyName(eShaderFamily family) {
switch (family) {
case eShaderFamily::FIXED_FUNCTION: return "fixed";
case eShaderFamily::LEGO: return "lego";
case eShaderFamily::BASIC: return "basic";
case eShaderFamily::METAL: return "metal";
case eShaderFamily::CLEAR_PLASTIC: return "clearPlastic";
case eShaderFamily::OCEAN: return "ocean";
case eShaderFamily::FLAT_SURF: return "flatSurf";
case eShaderFamily::BRICK_WATER: return "brickWater";
case eShaderFamily::DARKLING: return "darkling";
case eShaderFamily::TERRAIN: return "terrain";
case 31: case 48:
// TerrainMeshLighting_Rim: texture times vertex colors, alpha only the fade
case 3:
return eTextureAlpha::IGNORED;
default:
return eTextureAlpha::OPACITY;
}
return "lego";
}
ShaderTechnique TechniqueFor(int32_t shader) {
for (const auto& row : TECHNIQUES) {
if (row.shader == shader) return row.technique;
}
return ShaderTechnique{}; // the LEGO shader, as the client falls back to
}
std::string TechniquesJson(const std::vector<int32_t>& shaders) {
nlohmann::json out = nlohmann::json::object();
for (const auto shader : shaders) {
const auto technique = TechniqueFor(shader);
const char* alpha = technique.textureAlpha == eTextureAlpha::DECAL ? "decal" : technique.textureAlpha == eTextureAlpha::IGNORED ? "ignored" : "opacity";
out[std::to_string(shader)] = { {"family", FamilyName(technique.family)}, {"look", technique.look}, {"alpha", alpha}, {"flags", technique.flags} };
}
return out.dump();
}
eTextureAlpha TextureAlphaFor(int32_t shader) {
return TechniqueFor(shader).textureAlpha;
}
uint16_t ShaderLookFor(int32_t shader) {
return TechniqueFor(shader).look;
// By the technique each shader class sets up (ShaderManager's factory table at 0x01889608, indexed by gameValue;
// the class's technique setup names it). Checked in the client: 33 and 82 Technique_Basic_NoLighting_VertColor_
// NoTexture, 35 and 84 Technique_Basic_NoLighting_VertColor, 37 Technique_Basic_Lighting_VertColor_NoTexture,
// 38 and 94 Technique_Basic_Lighting_VertColor, 70 Technique_AlphaAsAlpha_UVScrolling_SimpleV_NoLighting_
// AlphaAnim, 105 Technique_TwoLayersBlended_NoLighting_VertColor_UVScrolling. The rest follow their mapShaders
// labels ("NL" no lighting, "NT" no texture, "VC" vertex colors)
switch (shader) {
// Basic NL Material, Over Everything Material Unlit
case 32: case 108:
return UNLIT | NO_VERTEX_COLORS | MATERIAL_COLOR;
// Basic NL, Basic NL UVAnim, OneSidedAlpha NL (and skinned), Opaque NL NoFog
case 34: case 36: case 56: case 61: case 83:
return UNLIT | NO_VERTEX_COLORS;
// (94 "Basic" is Technique_Basic_Lighting_VertColor like "Basic VC": its shader's technique setup, the vtable
// slot at +0x90 of the class made at 0x0045f240, names that technique, so it's the usual look)
// VertColor_NoLight_NoTex_AnimAlpha, VC_NL_NoTex_2D, Basic NL VC NT, OneSidedAlpha NL VC NT (and skinned),
// Basic NL NT, Opaque NL VC NT NoFog
case 11: case 16: case 33: case 58: case 63: case 80: case 82:
return UNLIT | NO_TEXTURE;
// Basic VC NT, Opaque VC NT NoFog
case 37: case 85:
return NO_TEXTURE;
// Two Textures Added NL VC AnimUV (TwoLayersAdded_PS in BasicShaders.fx), Two Layers Added VC AnimUV
case 93:
return UNLIT | TWO_LAYERS_ADDED;
case 107:
return TWO_LAYERS_ADDED;
// Two Layers Blended NL VC AnimUV and Two Layers Blended VC AnimUV. The client names techniques for them
// (Technique_TwoLayersBlended_*) that no shader it ships has, so how the game draws them is a guess: the dark
// texture under the base one by the vertex alpha, as the meshes' data suggests (Avant Gardens' snow caps
// and grass fade into rock by it)
case 105:
return UNLIT | TWO_LAYERS_BLENDED;
case 106:
return TWO_LAYERS_BLENDED;
// VertColor_NoLighting_Alpha, VertColorTex_NoLight_AlphaBlend and _AlphaTest, VC_NoLighting_2D, Over
// Everything (Unlit), Basic NL VC, LEGO-No Light, OneSidedAlpha NL VC (and skinned), OneSidedAlpha NL
// AnimAlpha, the NoLight scrolling UVs, Opaque NL VC NoFog, Additive NoLight VertColor, Distortion (Ocean)
// Unlit
case 8: case 10: case 54: case 15: case 23: case 35: case 52: case 57: case 62: case 68: case 70: case 73: case 81:
case 84: case 87: case 101:
return UNLIT;
// Polished Metal (Technique_Lighting_PolishedMetal_VertColor in Metallic.fx) and Brushed Steel (its noise
// in object space); both load their reflection textures themselves
case 98:
return REFLECTIVE;
case 99:
return REFLECTIVE | BRUSHED;
// LEGO-Emissive
case 53:
return EMISSIVE;
default:
return 0;
}
}
std::optional<Model> Parse(std::string_view data, uint32_t lod, std::string& error) {

View File

@@ -78,7 +78,7 @@ namespace NifFile {
// The shader (gameValue) a multishader part is drawn with, from its tag's gameValue: outside 3..108 the LEGO shader
int32_t MultishaderPart(std::optional<int32_t> tagShader);
// What a texture's alpha does under a shader (mapShaders.gameValue, TechniqueFor); -1 is fixed function (opacity)
// What a texture's alpha does under a shader (mapShaders.gameValue); -1 is fixed function (opacity)
eTextureAlpha TextureAlphaFor(int32_t shader);
/**
@@ -112,66 +112,6 @@ namespace NifFile {
// eShaderLook bits of a shader (mapShaders.gameValue); 0 for the usual lit look and for fixed function
uint16_t ShaderLookFor(int32_t shader);
/**
* The client's shader families (res/shaders/*.fx), each drawn by one program in the dashboard's 3D views
* (static/js/game-shaders.js): FIXED_FUNCTION Gamebryo's own lighting with the material; LEGO LEGOPPLighting (hemisphere
* lit sun, fresnel rim, specular, a faint environment reflection); BASIC BasicShaders and AlphaAsAlpha (sun * N.L +
* ambient per vertex, or unlit, times vertex color and texture); METAL Metallic.fx; CLEAR_PLASTIC ClearPlastic.fx;
* OCEAN Ocean.fx's Distortion techniques (warped texture layers); FLAT_SURF Ocean.fx's Flat Surf; BRICK_WATER
* BrickWater.fx; DARKLING LEGOPPLighting's Darkling techniques (a second, dark texture on its own UV set); TERRAIN
* TerrainDiffuse.fx's mesh techniques.
*/
enum class eShaderFamily : uint8_t { FIXED_FUNCTION, LEGO, BASIC, METAL, CLEAR_PLASTIC, OCEAN, FLAT_SURF, BRICK_WATER, DARKLING, TERRAIN };
// Its name in the scenery manifest ("fixed", "lego", "basic", "metal", "clearPlastic", "ocean", "flatSurf", "brickWater", "darkling", "terrain")
const char* FamilyName(eShaderFamily family);
// What a technique does besides its family and eShaderLook bits (static/js/scenery-core.js TECHNIQUE has the same bits)
enum eTechniqueFlag : uint32_t {
UV_ANIM = 1, // the base texture moves by the .nif's texture transform (g_textureMotion)
DOUBLE_SIDED = 2, // Cullmode none (AlphaAsAlpha)
BLEND = 4, // alpha blended without depth writes
ALPHA_TEST = 8, // cut out by the alpha
ADDITIVE = 16, // added onto what's behind
NO_AMBIENT = 32, // lit as if the ambient light were white
GLOW = 64, // LEGOPP_ApplyGlow: the glow color by the alpha
IGNORE_VERTEX_ALPHA = 128, // ... with the vertex alpha taken as 1
SUPER_EMISSIVE = 256, // LEGO-Emissive going to ten times the color
GRAYSCALE = 512, // LEGOPP_ApplyGrayscale
SHINY_GLINT = 1024, // a highlight band moving up the object
SPECULAR = 2048, // Darkling with LEGO lighting
NON_DECAL = 4096, // the texture multiplies the vertex colors instead of being laid over them
OCEAN_FX = 8192, // Distortion FX: unlit, the alpha a window set by the material's emissive
RIM_LIGHT = 16384, // Terrain Mesh Rim Light
DIFFUSE_ONLY = 32768, // Terrain Diffuse Map Only: the texture doubled, lit
ANIM_ALPHA = 65536, // NiMaterialProperty's alpha (animated) multiplies the output alpha
BASIC_EMISSIVE = 131072, // Basic_Emissive_PS
NO_FOG = 262144, // the "NoFog" techniques
NOT_DRAWN = 524288, // nothing of it is drawn in the world (post-processing, footprints, drop shadows)
NO_BLEND = 1048576 // no alpha blending ("Opaque" techniques, terrain meshes)
};
struct ShaderTechnique {
eShaderFamily family{ eShaderFamily::LEGO };
uint16_t look{}; // eShaderLook bits
eTextureAlpha textureAlpha{ eTextureAlpha::DECAL };
uint32_t flags{}; // eTechniqueFlag bits
};
/**
* How the client draws with a shader (mapShaders.gameValue): the one table of the game's shaders for the views.
* Every mapShaders gameValue has a row; -1 is fixed function; any other value is the LEGO shader, which the client
* falls back to.
*/
ShaderTechnique TechniqueFor(int32_t shader);
/**
* The techniques of these shaders (gameValues) as the scenery manifest's "techniques": a JSON object of gameValue ->
* {"family": FamilyName, "look": eShaderLook bits, "alpha": "opacity"|"decal"|"ignored", "flags": eTechniqueFlag
* bits}.
*/
std::string TechniquesJson(const std::vector<int32_t>& shaders);
// Where a node is in the model's space: p' = rotation * p + translation (row-major, scale folded in)
struct NodeTransform {
std::array<float, 9> rotation{ 1, 0, 0, 0, 1, 0, 0, 0, 1 };