diff --git a/dDashboardServer/routes/SettingsCatalog.cpp b/dDashboardServer/routes/SettingsCatalog.cpp index 22c0134e8..d077655d4 100644 --- a/dDashboardServer/routes/SettingsCatalog.cpp +++ b/dDashboardServer/routes/SettingsCatalog.cpp @@ -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)); diff --git a/dUgcServer/UgcModel.cpp b/dUgcServer/UgcModel.cpp index 3f2e22b8c..523ffcd43 100644 --- a/dUgcServer/UgcModel.cpp +++ b/dUgcServer/UgcModel.cpp @@ -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); diff --git a/dUgcServer/UgcModel.h b/dUgcServer/UgcModel.h index 301d16100..73ed97263 100644 --- a/dUgcServer/UgcModel.h +++ b/dUgcServer/UgcModel.h @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -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 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) diff --git a/dUgcServer/UgcServer.cpp b/dUgcServer/UgcServer.cpp index c87a5ce03..aceecd8d7 100644 --- a/dUgcServer/UgcServer.cpp +++ b/dUgcServer/UgcServer.cpp @@ -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("color_variation", 5.0f), 0.0f, 100.0f); settings.build.transparentOpacity = std::clamp(Setting("transparent_opacity", 58.82f), 0.0f, 100.0f); + settings.build.brightness = std::clamp(Setting("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(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("lod_distance_0", 0.0f); diff --git a/docs/UgcServer.md b/docs/UgcServer.md index b5dd5f27a..0ff187f47 100644 --- a/docs/UgcServer.md +++ b/docs/UgcServer.md @@ -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 diff --git a/resources/ugcconfig.ini b/resources/ugcconfig.ini index ebcf86489..a396eeb51 100644 --- a/resources/ugcconfig.ini +++ b/resources/ugcconfig.ini @@ -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). diff --git a/tests/dUgcTests/UgcTests.cpp b/tests/dUgcTests/UgcTests.cpp index 104c0626b..28d44fc85 100644 --- a/tests/dUgcTests/UgcTests.cpp +++ b/tests/dUgcTests/UgcTests.cpp @@ -1272,3 +1272,35 @@ TEST(UgcShaders, IconsDrawGlowUnlitAndMetalShiny) { EXPECT_EQ(UgcModel::FromNif(*read, UgcJobs::Shaders{}.TagLooks()).opaque.looks, std::vector(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("", 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); +}