mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-10-03 19:33:43 +00:00
feat(ugc): color_brightness and transparent_colors
color_brightness (percent, default 100: unchanged) scales the models' vertex colors, not the icons', for servers where the made models look brighter than they should. transparent_colors names color ids drawn transparent whatever Materials.xml says: 129 (Tr. Bright Bluish Violet with Glitter) has alpha 255 there, so it came out opaque; a named color gets transparent_opacity. Both default to off, so models come out byte for byte as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -442,6 +442,8 @@ namespace {
|
||||
c.Add(Labels(Choice(UGC, "color_palette", "Colors", "LU Toolbox's LU palette (unknown colors black), or the client's Materials.xml.", "lu_toolbox", { "lu_toolbox", "brickdb" }), { "LU Toolbox", "Brick database" }));
|
||||
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, "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));
|
||||
|
||||
@@ -247,7 +247,8 @@ namespace UgcModel {
|
||||
for (size_t index = 0; index < design->size(); index++) {
|
||||
const auto id = index < part.materials.size() ? part.materials[index] : (part.materials.empty() ? 0 : part.materials[0]);
|
||||
const auto toolboxId = materialOf(index);
|
||||
transparent = transparent && (luToolbox && inToolbox(toolboxId) ? UgcPalette::IsTransparent(toolboxId) : library.GetMaterial(luToolbox ? toolboxId : id).Transparent());
|
||||
const auto named = options.transparentColors.contains(luToolbox ? toolboxId : id);
|
||||
transparent = transparent && (named || (luToolbox && inToolbox(toolboxId) ? UgcPalette::IsTransparent(toolboxId) : library.GetMaterial(luToolbox ? toolboxId : id).Transparent()));
|
||||
}
|
||||
auto& mesh = transparent ? model.transparent : model.opaque;
|
||||
if (transparent) model.transparentBricks.push_back(mesh.indices.size());
|
||||
@@ -263,7 +264,7 @@ namespace UgcModel {
|
||||
colorId = materialOf(index);
|
||||
const auto material = library.GetMaterial(colorId);
|
||||
linear = UgcPalette::SrgbToLinear(glm::vec3(material.r, material.g, material.b) / 255.0f);
|
||||
if (transparent) alpha = material.a / 255.0f;
|
||||
if (transparent) alpha = material.a < 255 ? material.a / 255.0f : std::clamp(options.transparentOpacity / 100.0f, 0.0f, 1.0f);
|
||||
} else if (luToolbox) {
|
||||
colorId = materialOf(index);
|
||||
linear = *UgcPalette::Linear(colorId, options.icon);
|
||||
@@ -275,12 +276,13 @@ namespace UgcModel {
|
||||
colorId = index < part.materials.size() ? part.materials[index] : (part.materials.empty() ? 0 : part.materials[0]);
|
||||
const auto material = library.GetMaterial(colorId);
|
||||
linear = UgcPalette::SrgbToLinear(glm::vec3(material.r, material.g, material.b) / 255.0f);
|
||||
if (transparent) alpha = material.a / 255.0f;
|
||||
if (transparent) alpha = material.a < 255 ? material.a / 255.0f : std::clamp(options.transparentOpacity / 100.0f, 0.0f, 1.0f);
|
||||
}
|
||||
if (options.colorVariation > 0.0f) {
|
||||
const float variation = options.colorVariation * (luToolbox ? UgcPalette::VariationScale(colorId) : 1.0f);
|
||||
linear = UgcPalette::ApplyVariation(linear, variation, UgcPalette::BrickRandom(options.seed, brick, colorId));
|
||||
}
|
||||
if (!options.icon && options.brightness != 100.0f) linear *= std::max(options.brightness, 0.0f) / 100.0f;
|
||||
const glm::vec4 color(UgcPalette::LinearToSrgb(linear), alpha);
|
||||
anyGlow = anyGlow || glow != glm::vec3(0.0f);
|
||||
const auto look = transparent ? eLook::PLASTIC : LookOf(colorId, library.GetMaterial(colorId), options.looks);
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
@@ -93,6 +94,8 @@ namespace UgcModel {
|
||||
float colorVariation{ 5.0f }; // percent, 0: none (LU Toolbox: Apply Color Variation, 5%)
|
||||
uint64_t seed{}; // of the variation's random numbers
|
||||
float transparentOpacity{ 58.82f }; // percent, transparent bricks' vertex alpha (LU Toolbox palette only)
|
||||
float brightness{ 100.0f }; // percent, the models' vertex colors (not icons'); 100: as the palette has them
|
||||
std::set<uint32_t> transparentColors; // color ids drawn transparent whatever Materials.xml says (129: its alpha is 255)
|
||||
bool icon{}; // the icon renderer's color corrections
|
||||
uint32_t lod{}; // brickprimitives level
|
||||
LookRules looks; // which colors are metal and glow (Mesh::looks)
|
||||
|
||||
@@ -79,6 +79,15 @@ namespace {
|
||||
settings.build.palette = Game::config->GetValue("color_palette") == "brickdb" ? UgcModel::ePalette::BRICKDB : UgcModel::ePalette::LU_TOOLBOX;
|
||||
settings.build.colorVariation = std::clamp(Setting<float>("color_variation", 5.0f), 0.0f, 100.0f);
|
||||
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"));
|
||||
std::string id;
|
||||
while (std::getline(stream, id, ',')) {
|
||||
std::erase_if(id, [](unsigned char c) { return std::isspace(c); });
|
||||
if (const auto color = GeneralUtils::TryParse<uint32_t>(id)) settings.build.transparentColors.insert(*color);
|
||||
}
|
||||
}
|
||||
const auto lods = ParseLods(Game::config->GetValue("lods").empty() ? "0,2" : Game::config->GetValue("lods"));
|
||||
if (!lods.empty()) settings.lods = lods;
|
||||
settings.lodDistances.lod0 = Setting<float>("lod_distance_0", 0.0f);
|
||||
|
||||
@@ -117,6 +117,10 @@ 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`.
|
||||
`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":
|
||||
the color's HSV value is taken to a 1/2.224 gamma, moved by a random amount of up to `color_variation`/200 (5%:
|
||||
0.025) either way, times the color's own amount (black 0.4, orange 1.5, ...), clamped and taken back; hue and
|
||||
|
||||
@@ -46,9 +46,14 @@ max_attempts=3
|
||||
# color_palette: lu_toolbox (its LU palette, unknown colors black) or brickdb (the client's Materials.xml).
|
||||
# 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.
|
||||
color_palette=lu_toolbox
|
||||
color_variation=5
|
||||
transparent_opacity=58.82
|
||||
color_brightness=100
|
||||
transparent_colors=
|
||||
|
||||
# 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).
|
||||
|
||||
@@ -1272,3 +1272,35 @@ TEST(UgcShaders, IconsDrawGlowUnlitAndMetalShiny) {
|
||||
EXPECT_EQ(UgcModel::FromNif(*read, UgcJobs::Shaders{}.TagLooks()).opaque.looks, std::vector<UgcModel::eLook>(4, UgcModel::eLook::GLOW));
|
||||
EXPECT_TRUE(UgcModel::FromNif(*read).opaque.looks.empty());
|
||||
}
|
||||
|
||||
// color_brightness scales the models' vertex colors (not icons'); transparent_colors makes a color Materials.xml has
|
||||
// opaque transparent, at transparent_opacity
|
||||
TEST(UgcModel, BrightnessAndTransparentColors) {
|
||||
UgcBricks::BrickLibrary library(MakeRes(), 0);
|
||||
constexpr uint32_t ADDED = 50001;
|
||||
library.SetMaterials({ { ADDED, { 0, 200, 100, 255 } } });
|
||||
std::string error;
|
||||
const auto brick = UgcModel::ParseLxfml("<LXFML versionMajor=\"5\"><Bricks><Brick><Part designID=\"3001\" materials=\"" + std::to_string(ADDED) +
|
||||
"\"><Bone transformation=\"1,0,0,0,1,0,0,0,1,0,0,0\"/></Part></Brick></Bricks></LXFML>", error);
|
||||
UgcModel::BuildOptions options;
|
||||
options.colorVariation = 0.0f;
|
||||
const auto normal = UgcModel::Build(brick, library, options);
|
||||
ASSERT_FALSE(normal.opaque.colors.empty());
|
||||
EXPECT_TRUE(normal.transparent.colors.empty());
|
||||
|
||||
options.brightness = 50.0f;
|
||||
const auto darker = UgcModel::Build(brick, library, options);
|
||||
ASSERT_FALSE(darker.opaque.colors.empty());
|
||||
const auto linear = UgcPalette::SrgbToLinear(glm::vec3(normal.opaque.colors[0])) * 0.5f;
|
||||
EXPECT_NEAR(darker.opaque.colors[0].g, UgcPalette::LinearToSrgb(linear).g, 1e-4f);
|
||||
options.icon = true;
|
||||
EXPECT_EQ(UgcModel::Build(brick, library, options).opaque.colors[0], UgcModel::Build(brick, library, [&] { auto o = options; o.brightness = 100.0f; return o; }()).opaque.colors[0]);
|
||||
|
||||
options = {};
|
||||
options.colorVariation = 0.0f;
|
||||
options.transparentColors.insert(ADDED);
|
||||
const auto seeThrough = UgcModel::Build(brick, library, options);
|
||||
EXPECT_TRUE(seeThrough.opaque.colors.empty());
|
||||
ASSERT_FALSE(seeThrough.transparent.colors.empty());
|
||||
EXPECT_NEAR(seeThrough.transparent.colors[0].a, 0.5882f, 1e-4f);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user