feat(ugc): metal, brushed steel and glow shaders on by default

Tested in game: shader_metal 88, shader_brushed 89 and shader_glow 46 are
now the defaults, with brushed_colors defaulting to the drum lacquered
298,300,1002,1004 and transparent_colors to 129. Empty list settings take
the default; none turns a list off. The three shader ids set to 0 still
write live's all-plastic models byte for byte.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Aaron Kimbrell
2026-09-28 12:07:58 -05:00
parent 5396594690
commit 462a152a66
4 changed files with 36 additions and 30 deletions

View File

@@ -444,7 +444,7 @@ namespace {
c.Add(Unit(Float(UGC, "color_variation", "Color variation", "Each brick's brightness is shifted by up to this much (the same every time the model is made). 0: none.", "5", 0, 100), "%"));
c.Add(Unit(Float(UGC, "transparent_opacity", "Transparent opacity", "", "58.82", 0, 100), "%"));
c.Add(Unit(Float(UGC, "color_brightness", "Model color brightness", "The models' vertex colors (not the icons'). 100: as the palette has them; lower is darker. Models already made keep theirs until they are made again.", "100", 0, 200), "%"));
c.Add(Text(UGC, "transparent_colors", "Always transparent colors", "LEGO color ids drawn transparent whatever Materials.xml says, comma separated (e.g. 129, the transparent glitter violet, which Materials.xml has opaque). Their opacity is Transparent opacity.", ""));
c.Add(Text(UGC, "transparent_colors", "Always transparent colors", "LEGO color ids drawn transparent whatever Materials.xml says, comma separated: by default 129, the transparent glitter violet, which Materials.xml has opaque (none: no colors). Their opacity is Transparent opacity.", "129"));
c.Add(Text(UGC, "lods", "Levels of detail", "brickprimitives levels made, 0 (most detailed) to 2, e.g. 0,2.", "0,2"));
c.Add(Float(UGC, "lod_distance_0", "LOD 0 distance", "", "0", 0, 100000));
c.Add(Float(UGC, "lod_distance_1", "LOD 1 distance", "", "50", 0, 100000));
@@ -455,13 +455,13 @@ namespace {
c.Add(Bool(UGC, "combine_transparent", "One shape for all transparent bricks", "Off: each transparent brick is its own shape, so the client can sort them.", false));
// Metal and glow groups (UgcJobs::Shaders): off keeps the files exactly as before, as live made them
const std::string notLive = " Not how live looked: live's models were all LEGO plastic (S01). Models already made keep their look until they are made again (Make everything again, or Reprocess).";
c.Add(Int(UGC, "shader_metal", "Metal shader", "mapShaders id for metal colors (Materials.xml shinySteel and LU Toolbox's metallic ones), in a group S<id>_Metal_Model: 88 is Polished Metal. 0: off, they stay LEGO plastic." + notLive, "0", 0, 9999));
c.Add(Int(UGC, "shader_brushed", "Brushed steel shader", "mapShaders id for brushed steel colors (Materials.xml brushedSteel and matteSteel; the client's has none) in S<id>_Brushed_Model: 89 is Brushed Steel. 0: off." + notLive, "0", 0, 9999));
c.Add(Int(UGC, "shader_glow", "Glow shader", "mapShaders id for opaque glowing colors (LU Toolbox's glow colors) in S<id>_Glow_Model, with their plain color and an emissive material: 46 is LEGO-Emissive. Transparent glow stays with the transparent bricks. 0: off." + notLive, "0", 0, 9999));
c.Add(Int(UGC, "shader_metal", "Metal shader", "mapShaders id for metal colors (Materials.xml shinySteel and LU Toolbox's metallic ones), in a group S<id>_Metal_Model: 88 is Polished Metal. 0: off, they stay LEGO plastic." + notLive, "88", 0, 9999));
c.Add(Int(UGC, "shader_brushed", "Brushed steel shader", "mapShaders id for brushed steel colors (Materials.xml brushedSteel and matteSteel; the client's has none) in S<id>_Brushed_Model: 89 is Brushed Steel. 0: off." + notLive, "89", 0, 9999));
c.Add(Int(UGC, "shader_glow", "Glow shader", "mapShaders id for opaque glowing colors (LU Toolbox's glow colors) in S<id>_Glow_Model, with their plain color and an emissive material: 46 is LEGO-Emissive. Transparent glow stays with the transparent bricks. 0: off." + notLive, "46", 0, 9999));
c.Add(Float(UGC, "glow_emissive", "Glow emissive strength", "With the glow shader on: the glow shapes' material emissive, how far the emissive shader goes from lit to the plain color (1: fully).", "1", 0, 10));
c.Add(Text(UGC, "metal_material_types", "Metal material types", "Materials.xml MaterialTypes drawn as metal, comma separated (none: only LU Toolbox's metallic colors).", "shinySteel"));
c.Add(Text(UGC, "brushed_material_types", "Brushed steel material types", "Materials.xml MaterialTypes drawn as brushed steel, comma separated (none: no such colors).", "brushedSteel,matteSteel"));
c.Add(Text(UGC, "brushed_colors", "Brushed steel colors", "LEGO color ids drawn as brushed steel whatever their Materials.xml type, comma separated (e.g. the drum lacquered 298,300,1002,1004). Empty: none.", ""));
c.Add(Text(UGC, "brushed_colors", "Brushed steel colors", "LEGO color ids drawn as brushed steel whatever their Materials.xml type, comma separated: by default the drum lacquered 298,300,1002,1004 (none: no colors).", "298,300,1002,1004"));
c.Add(Bool(UGC, "remove_hidden_faces", "Remove faces nobody can see", "", true));
c.Add(Bool(UGC, "hsr_ground_plane", "Nothing seen from below", "Also removes what can only be seen from under the model.", false));
c.Add(Unit(Int(UGC, "optimize_resolution", "Detail of the visibility renders", "", "1024", 64, 4096), "pixels"));

View File

@@ -81,7 +81,9 @@ namespace {
settings.build.transparentOpacity = std::clamp(Setting<float>("transparent_opacity", 58.82f), 0.0f, 100.0f);
settings.build.brightness = std::clamp(Setting<float>("color_brightness", 100.0f), 0.0f, 200.0f);
{
std::stringstream stream(Game::config->GetValue("transparent_colors"));
// Empty: the default (129); none: no colors
const auto value = Game::config->GetValue("transparent_colors");
std::stringstream stream(value.empty() ? "129" : value);
std::string id;
while (std::getline(stream, id, ',')) {
std::erase_if(id, [](unsigned char c) { return std::isspace(c); });
@@ -98,9 +100,9 @@ namespace {
if (!Game::config->GetValue("shader_opaque").empty()) settings.shaderOpaque = Game::config->GetValue("shader_opaque");
settings.combineTransparent = Setting<int32_t>("combine_transparent", 0) != 0;
// Metal and glow colors in NiLODNodes of their own, drawn with those shaders (off by default: not how live looked)
settings.shaders.metal = std::min(Setting<uint32_t>("shader_metal", 0), 9999u);
settings.shaders.brushed = std::min(Setting<uint32_t>("shader_brushed", 0), 9999u);
settings.shaders.glow = std::min(Setting<uint32_t>("shader_glow", 0), 9999u);
settings.shaders.metal = std::min(Setting<uint32_t>("shader_metal", 88), 9999u);
settings.shaders.brushed = std::min(Setting<uint32_t>("shader_brushed", 89), 9999u);
settings.shaders.glow = std::min(Setting<uint32_t>("shader_glow", 46), 9999u);
settings.shaders.glowEmissive = std::clamp(Setting<float>("glow_emissive", 1.0f), 0.0f, 10.0f);
settings.icon.glowEmissive = settings.shaders.glowEmissive;
// Which Materials.xml MaterialTypes are metal and brushed steel
@@ -117,7 +119,9 @@ namespace {
}
// LEGO color ids drawn as brushed steel whatever their Materials.xml type
{
std::stringstream stream(Game::config->GetValue("brushed_colors"));
// Empty: the default (the drum lacquered colors); none: no colors
const auto value = Game::config->GetValue("brushed_colors");
std::stringstream stream(value.empty() ? "298,300,1002,1004" : value);
std::string id;
while (std::getline(stream, id, ',')) {
std::erase_if(id, [](unsigned char c) { return std::isspace(c); });

View File

@@ -117,8 +117,9 @@ defaults. The table below goes through it step by step.
`Materials.xml`): LU's colors, the LDD colors LU doesn't have mapped onto the nearest LU one, colors LU Toolbox doesn't know but the
client's `Materials.xml` has (colors added to the brick database) from `Materials.xml`, unknown ones black. A
brick is transparent only when all of its materials are; transparent bricks get `transparent_opacity` (58.82%).
`transparent_colors` names colors that are transparent whatever `Materials.xml` says (129, "Tr. Bright Bluish
Violet with Glitter", has alpha 255 there); an opaque-listed color named there gets `transparent_opacity`.
`transparent_colors` (default 129; `none`: no colors) names colors that are transparent whatever `Materials.xml`
says (129, "Tr. Bright Bluish Violet with Glitter", has alpha 255 there); a color named there gets
`transparent_opacity`.
`color_brightness` (percent, default 100: unchanged) scales the models' colors after the variation below, not
the icons'.
3. Color variation: each material of each brick has its brightness shifted like LU Toolbox's "Apply Color Variation":
@@ -206,13 +207,13 @@ defaults. The table below goes through it step by step.
| Icon scene BrickBuild / Car: 50 mm lens, camera 53.4 / 19.5 degrees, sun at 21 / 50.3, 128 px, framing 1.03, transparent film | Same framing (`icon_*`); the light is brighter, to match the game's icons |
| Icon scene Rocket: 35 mm lens, other angles, two suns | Not built in; a preset for the rocket build type can be set in the icon editor |
### Metal and glow (opt in, not how live looked)
### Metal and glow (on by default, not how live looked)
Live's models, LU Toolbox's exports and the client's own builder (`LUNifBuilder_BK`, which writes only `S01_Opaque`
and `S01_Alpha`) all draw every brick with the LEGO shader, so metal colors look like grey plastic and glowing colors
like bright plastic. The UGC server can instead give them the client's metal and emissive shaders. Everything is off
by default, and off writes exactly the files it wrote before these settings existed (the same bytes, tested), so
nothing is made again needlessly.
like bright plastic. The UGC server gives them the client's metal and emissive shaders by default. Set the three
shader ids to 0 for live's look: off writes exactly the files it wrote before these settings existed (the same bytes,
tested).
How the client picks the shader (checked in the 1.10.64 client; Ghidra bookmarks under "UGCShaders"): player models
(LOT 14, and 6662) have RenderComponent shader 100, mapShaders "Multishader" (gameValue 9999). For a downloaded model
@@ -224,13 +225,13 @@ all of its levels, so each look needs a group of its own.
| Setting (`ugcconfig.ini`, dashboard: UGC models) | Default | What it writes |
| --- | --- | --- |
| `shader_metal` | 0 (off) | `S<id>_Metal_Model` for metal colors: 88 is Polished Metal (gameValue 98). The client loads `textures/metal/metal_reflection_polished.dds` itself and tints it by the vertex color (`Metallic.fx`, `Technique_Lighting_PolishedMetal_VertColor`). |
| `shader_brushed` | 0 (off) | `S<id>_Brushed_Model` for brushed steel colors: 89 is Brushed Steel (gameValue 99; it loads `metal_reflection_brushed.dds` and `_noise.dds`, the noise in object space). The textures are registered by the client (`RegisterBrushedSteelTextures`, 0x00467090) as global shader textures 6 and 7, so the .nif needs none. The client's Materials.xml has no brushed types, so this needs `brushed_colors` or a Materials.xml that names them. |
| `shader_glow` | 0 (off) | `S<id>_Glow_Model` for opaque glowing colors: 46 is LEGO-Emissive (gameValue 53), which draws `lerp(lit, vertex color, vertex alpha * material emissive red)`, opaque. |
| `shader_metal` | 88 | `S<id>_Metal_Model` for metal colors: 88 is Polished Metal (gameValue 98). The client loads `textures/metal/metal_reflection_polished.dds` itself and tints it by the vertex color (`Metallic.fx`, `Technique_Lighting_PolishedMetal_VertColor`). |
| `shader_brushed` | 89 | `S<id>_Brushed_Model` for brushed steel colors: 89 is Brushed Steel (gameValue 99; it loads `metal_reflection_brushed.dds` and `_noise.dds`, the noise in object space). The textures are registered by the client (`RegisterBrushedSteelTextures`, 0x00467090) as global shader textures 6 and 7, so the .nif needs none. The client's Materials.xml has no brushed types, so this needs `brushed_colors` or a Materials.xml that names them. |
| `shader_glow` | 46 | `S<id>_Glow_Model` for opaque glowing colors: 46 is LEGO-Emissive (gameValue 53), which draws `lerp(lit, vertex color, vertex alpha * material emissive red)`, opaque. |
| `glow_emissive` | 1 | The glow shapes' `NiMaterialProperty` emissive (grey): how far the shader goes from lit to the plain color. |
| `metal_material_types` | `shinySteel` | Materials.xml `MaterialType`s that are metal (empty: the default; `none`: none). |
| `brushed_material_types` | `brushedSteel,matteSteel` | Materials.xml `MaterialType`s that are brushed steel. |
| `brushed_colors` | empty | LEGO color ids that are brushed steel whatever their type (e.g. the drum lacquered 298, 300, 1002, 1004); they win over the metal and glow colors. |
| `brushed_colors` | 298,300,1002,1004 (the drum lacquered colors) | LEGO color ids that are brushed steel whatever their type; they win over the metal and glow colors. Empty: the default; `none`: no colors. |
Which color has which look is data, not a list in the code (`UgcModel::LookOf`): glow is LU Toolbox's glow table
(`UgcPalette::Glow`: 50, 294, 329, 9000-9027), metal is LU Toolbox's metallic table (`UgcPalette::IsMetallic`) plus

View File

@@ -47,13 +47,13 @@ max_attempts=3
# color_variation: each brick's brightness is shifted by up to this percent (the same every time; 0: none).
# transparent_opacity: transparent bricks' opacity in percent.
# color_brightness: the models' colors in percent (not icons'; 100: as the palette has them, lower is darker).
# transparent_colors: color ids drawn transparent whatever Materials.xml says, comma separated (e.g. 129, the
# transparent glitter violet, which Materials.xml has opaque). Empty: none.
# transparent_colors: color ids drawn transparent whatever Materials.xml says, comma separated (empty: the default, 129,
# the transparent glitter violet, which Materials.xml has opaque; none: no colors).
color_palette=lu_toolbox
color_variation=5
transparent_opacity=58.82
color_brightness=100
transparent_colors=
transparent_colors=129
# Levels of detail made, from the client's brickprimitives (0 most detailed, 2 least), and the distances they're
# drawn at (LU Toolbox picks each level's range from these by which levels are made; lod_cull: drawn up to).
@@ -69,8 +69,8 @@ lod_cull=10000
shader_opaque=01
combine_transparent=0
# Metal and glow colors in NiLODNodes of their own, drawn with the client's metal and emissive shaders. Not how live
# looked: live's models were all LEGO plastic (S01), which is what 0 (off, the default) keeps, byte for byte.
# Metal and glow colors in NiLODNodes of their own, drawn with the client's metal and emissive shaders (the default).
# Not how live looked: live's models were all LEGO plastic (S01), which 0 (off) keeps, byte for byte.
# shader_metal: mapShaders id for metal colors (Materials.xml types in metal_material_types and LU Toolbox's metallic
# colors), S<id>_Metal_Model: 88 is Polished Metal.
# shader_brushed: for brushed steel colors (brushed_material_types; the client's Materials.xml has none): 89 is
@@ -80,14 +80,15 @@ combine_transparent=0
# glow_emissive: the glow shapes' material emissive (how far the shader goes from lit to the plain color, 1 fully).
# *_material_types: Materials.xml MaterialTypes, comma separated (empty: the default, none: no types).
# Models already made keep their look until they're made again (the dashboard's Make everything again, or Reprocess).
shader_metal=0
shader_brushed=0
shader_glow=0
shader_metal=88
shader_brushed=89
shader_glow=46
glow_emissive=1
metal_material_types=shinySteel
brushed_material_types=brushedSteel,matteSteel
# brushed_colors: LEGO color ids drawn as brushed steel whatever their type (e.g. the drum lacquered 298,300,1002,1004)
brushed_colors=
# brushed_colors: LEGO color ids drawn as brushed steel whatever their type, comma separated (empty: the default, the
# drum lacquered 298,300,1002,1004; none: no colors)
brushed_colors=298,300,1002,1004
# Remove faces that can't be seen from anywhere (optimize_resolution: detail of the renders that decide it;
# hsr_ground_plane: 1 also removes what can only be seen from below the model)