From aab161038e67fcdd3f7fc85d73928e9b13b66592 Mon Sep 17 00:00:00 2001 From: Aaron Kimbrell Date: Tue, 29 Sep 2026 00:35:48 -0500 Subject: [PATCH] fix(ugc): glitter flecks look like LEGO glitter The fleck texture was 50 soft round blobs of one size, bright in the middle and fading out, which read as a smudgy dot pattern. LEGO's glitter bricks have many small flat flakes (about half a millimetre) of which most look faint and a few catch the light. The flecks are now flat flakes of glitter_fleck_size (0.05 model units, i.e. 0.5 mm; 0.7 to 1.3 of it) with a one-pixel edge, each as bright as its facet catches the light (0.3 to 1 of glitter_fleck_opacity, 80%, weighted towards dim), 80 a tile by default. The texture grows (128 to 512) to keep a fleck 3 pixels wide. New settings glitter_fleck_size and glitter_fleck_opacity; glitter_density defaults to 80. Only glitter output changes. Check in game: reprocess a glitter model; close up, the flecks are small crisp flakes of varied brightness, not blurry dots; from a few metres the brick still reads as its color with a fine glitter. Co-Authored-By: Claude Opus 5.5 --- dDashboardServer/routes/ClientAssets.cpp | 4 +- dDashboardServer/routes/SettingsCatalog.cpp | 6 ++- dUgcServer/Formats/UgcFormats.cpp | 2 +- dUgcServer/Model/UgcGlitter.cpp | 42 +++++++++++---------- dUgcServer/Model/UgcGlitter.h | 21 ++++++----- dUgcServer/Render/UgcRender.cpp | 2 +- dUgcServer/UgcServer.cpp | 4 +- docs/UgcServer.md | 16 +++++--- resources/ugcconfig.ini | 6 ++- tests/dUgcTests/UgcTests.cpp | 38 ++++++++++++++----- 10 files changed, 90 insertions(+), 51 deletions(-) diff --git a/dDashboardServer/routes/ClientAssets.cpp b/dDashboardServer/routes/ClientAssets.cpp index 76c26dc07..1bbdb391f 100644 --- a/dDashboardServer/routes/ClientAssets.cpp +++ b/dDashboardServer/routes/ClientAssets.cpp @@ -502,7 +502,9 @@ void RegisterClientAssetRoutes() { } const nlohmann::json settings{ { "colors", glitter }, { "tile", GeneralUtils::TryParse(UgcRoutes::Setting("glitter_size").value_or("")).value_or(1.6f) }, - { "flecks", GeneralUtils::TryParse(UgcRoutes::Setting("glitter_density").value_or("")).value_or(50) }, + { "flecks", GeneralUtils::TryParse(UgcRoutes::Setting("glitter_density").value_or("")).value_or(80) }, + { "fleckSize", GeneralUtils::TryParse(UgcRoutes::Setting("glitter_fleck_size").value_or("")).value_or(0.05f) }, + { "fleckOpacity", GeneralUtils::TryParse(UgcRoutes::Setting("glitter_fleck_opacity").value_or("")).value_or(80.0f) }, { "speed", GeneralUtils::TryParse(UgcRoutes::Setting("glitter_speed").value_or("")).value_or(1.0f) } }; reply.status = eHTTPStatusCode::OK; reply.message = colours.script + "window.LDD_GLITTER = " + settings.dump() + ";\n"; diff --git a/dDashboardServer/routes/SettingsCatalog.cpp b/dDashboardServer/routes/SettingsCatalog.cpp index 68e69b572..eca83644a 100644 --- a/dDashboardServer/routes/SettingsCatalog.cpp +++ b/dDashboardServer/routes/SettingsCatalog.cpp @@ -477,8 +477,10 @@ namespace { c.Add(Int(UGC, "shader_glitter", "Glitter shader", "mapShaders id for glitter colors, in S_Glitter_Model and (transparent ones) S_GlitterAlpha_Model: 21 is LEGO-AnimUV, which lays a white fleck texture stored in the model over the color (still: the client never updates a placed model; see the sparkle shader). 0: off, they stay plastic." + notLive, "21", 0, 9999)); c.Add(Text(UGC, "glitter_material_types", "Glitter material types", "Materials.xml MaterialTypes drawn as glitter, comma separated (none: only the glitter colors below).", "glitter")); c.Add(Text(UGC, "glitter_colors", "Glitter colors", "LEGO color ids drawn as glitter whatever their Materials.xml type, comma separated: by default 114,117, which LEGO's color data calls glitter and the client's Materials.xml plain plastic (none: no colors).", "114,117")); - c.Add(Float(UGC, "glitter_size", "Glitter tile size", "The fleck texture's tile in model units (a stud is 0.8): how far apart the flecks are, the same on every brick.", "1.6", 0.1f, 100)); - c.Add(Int(UGC, "glitter_density", "Glitter flecks", "Flecks in one tile of the glitter texture.", "50", 0, 2000)); + c.Add(Float(UGC, "glitter_size", "Glitter tile size", "The fleck texture's tile in model units (a stud is 0.8): with the flecks in a tile, how far apart they are (each brick places the tile its own way).", "1.6", 0.1f, 100)); + c.Add(Int(UGC, "glitter_density", "Glitter flecks", "Flecks in one tile of the glitter texture.", "80", 0, 5000)); + c.Add(Float(UGC, "glitter_fleck_size", "Glitter fleck size", "A fleck's diameter in model units (they are cm: 0.05 is half a millimetre, as LEGO's glitter).", "0.05", 0.005f, 1)); + c.Add(Float(UGC, "glitter_fleck_opacity", "Glitter fleck brightness", "Percent: how white the brightest flecks are over the brick's color (each catches the light differently: most are dimmer).", "80", 0, 100)); c.Add(Int(UGC, "shader_glitter_sparkle", "Glitter sparkle shader", "mapShaders id for the glitter bricks' sparkles, in S_GlitterSparkle_Model over both glitter groups (only with the glitter shader on): 79 is Distortion Directional (Ocean), whose texture layers the client moves every frame on its own, so a sparkle flashes where two layers' sparkles meet. Nothing else moves on a placed model (the client never updates it). 0: no sparkles.", "79", 0, 9999)); c.Add(Float(UGC, "glitter_sparkle_size", "Glitter sparkle size", "A sparkle's diameter in model units (a stud is 0.8).", "0.1", 0.01f, 1)); c.Add(Float(UGC, "glitter_sparkle_amount", "Glitter sparkle amount", "Percent of each moving sparkle layer covered by sparkles; a sparkle shows where two meet, so about this share squared of a brick sparkles at once.", "5", 0, 50)); diff --git a/dUgcServer/Formats/UgcFormats.cpp b/dUgcServer/Formats/UgcFormats.cpp index d4f9c0058..9ff0bb4b4 100644 --- a/dUgcServer/Formats/UgcFormats.cpp +++ b/dUgcServer/Formats/UgcFormats.cpp @@ -361,7 +361,7 @@ namespace { // function would do with it is what LEGO-AnimUV does: the texture over the vertex color by its alpha, the // vertex alpha kept), with the texture transform LEGO-AnimUV reads int32_t GlitterTexturing(const UgcGlitter::Params& glitter) { - if (m_Glitter < 0) m_Glitter = Texturing(StoredTexture("ugc_glitter.dds", UgcGlitter::Mipmaps(UgcGlitter::FleckAlpha(glitter.flecks))), 1 << 1, true); + if (m_Glitter < 0) m_Glitter = Texturing(StoredTexture("ugc_glitter.dds", UgcGlitter::Mipmaps(UgcGlitter::FleckAlpha(glitter))), 1 << 1, true); return m_Glitter; } diff --git a/dUgcServer/Model/UgcGlitter.cpp b/dUgcServer/Model/UgcGlitter.cpp index 12927860a..7dd10e77f 100644 --- a/dUgcServer/Model/UgcGlitter.cpp +++ b/dUgcServer/Model/UgcGlitter.cpp @@ -17,36 +17,38 @@ namespace UgcGlitter { int Side(const std::vector& alpha) { return static_cast(std::lround(std::sqrt(static_cast(alpha.size())))); } } - std::vector FleckAlpha(uint32_t flecks) { - constexpr int N = TEXTURE_SIZE; - std::vector alpha(static_cast(N) * N, 0.0f); - // SplitMix64 from a fixed seed: the same texture on every platform + int Params::TextureSize() const { + const float wanted = 3.0f * std::max(tile, 0.001f) / std::max(fleckSize, 0.001f); + int side = 128; + while (side < 512 && static_cast(side) < wanted) side *= 2; + return side; + } + + std::vector FleckAlpha(const Params& params) { + const int N = params.TextureSize(); + std::vector alpha(static_cast(N) * N, 0); + const float size = params.fleckSize / std::max(params.tile, 0.001f) * static_cast(N); + const float opacity = std::clamp(params.fleckOpacity, 0.0f, 100.0f) / 100.0f; uint64_t state = 0x6C69747465720000ull; - const auto next = [&state] { - uint64_t z = (state += 0x9E3779B97F4A7C15ull); - z = (z ^ (z >> 30)) * 0xBF58476D1CE4E5B9ull; - z = (z ^ (z >> 27)) * 0x94D049BB133111EBull; - return static_cast((z ^ (z >> 31)) >> 40) / static_cast(1ull << 24); - }; - for (uint32_t i = 0; i < flecks; i++) { + const auto next = [&state] { return Unit(SplitMix(state++)); }; + for (uint32_t i = 0; i < params.flecks; i++) { const float cx = next() * N, cy = next() * N; - const float radius = 1.2f + next() * 1.0f; - const float peak = 0.65f + next() * 0.35f; - const int reach = static_cast(std::ceil(radius)); + const float radius = std::max(size * (0.35f + next() * 0.3f), 0.6f); + const float facet = next(); + const float peak = opacity * (0.3f + 0.7f * facet * facet); + const int reach = static_cast(std::ceil(radius + 0.5f)); for (int dy = -reach; dy <= reach; dy++) { for (int dx = -reach; dx <= reach; dx++) { const int x = static_cast(std::floor(cx)) + dx, y = static_cast(std::floor(cy)) + dy; const float ddx = x + 0.5f - cx, ddy = y + 0.5f - cy; - const float d = std::sqrt(ddx * ddx + ddy * ddy) / radius; - if (d >= 1.0f) continue; + const float cover = std::clamp(radius + 0.5f - std::sqrt(ddx * ddx + ddy * ddy), 0.0f, 1.0f); + if (cover <= 0.0f) continue; auto& value = alpha[static_cast(((y % N) + N) % N) * N + ((x % N) + N) % N]; - value = std::max(value, peak * (1.0f - d * d)); + value = std::max(value, static_cast(std::lround(cover * peak * 255.0f))); } } } - std::vector out(alpha.size()); - for (size_t i = 0; i < alpha.size(); i++) out[i] = static_cast(std::lround(std::clamp(alpha[i], 0.0f, 1.0f) * 255.0f)); - return out; + return alpha; } float Params::SparkleTile() const { diff --git a/dUgcServer/Model/UgcGlitter.h b/dUgcServer/Model/UgcGlitter.h index 185b69301..5eb7ef0b8 100644 --- a/dUgcServer/Model/UgcGlitter.h +++ b/dUgcServer/Model/UgcGlitter.h @@ -22,14 +22,13 @@ * brick's own (BrickSeed), so no two bricks have the same pattern and a model made again has the same one. */ namespace UgcGlitter { - // The fleck texture's side in pixels (a power of two, mipmapped down to 1) - constexpr int TEXTURE_SIZE = 128; - struct Params { // Flecks (LEGO-AnimUV) - float tile{ 1.6f }; // glitter_size: the fleck texture's side in model units (LDD units: a stud is 0.8) - uint32_t flecks{ 50 }; // glitter_density: flecks in one tile - bool random{ true }; // glitter_random: each brick its own pattern (BrickSeed), else the same on every brick + float tile{ 1.6f }; // glitter_size: the fleck texture's side in model units (LDD units: a stud is 0.8) + uint32_t flecks{ 80 }; // glitter_density: flecks in one tile + float fleckSize{ 0.05f }; // glitter_fleck_size: a fleck's diameter in model units (LDD units are cm: 0.5 mm) + float fleckOpacity{ 80.0f }; // glitter_fleck_opacity: percent, the brightest flecks' alpha + bool random{ true }; // glitter_random: each brick its own pattern (BrickSeed), else the same on every brick // Sparkles (Distortion Directional) float sparkleSize{ 0.1f }; // glitter_sparkle_size: a sparkle's diameter in model units float sparkleAmount{ 5.0f }; // glitter_sparkle_amount: percent of each moving layer covered by sparkles @@ -37,6 +36,8 @@ namespace UgcGlitter { float sparkleTint{ 30.0f }; // glitter_sparkle_tint: percent, how far sparkles take their brick's color float sparkleBrightness{ 100.0f }; // glitter_sparkle_brightness: percent, the sparkles' vertex color + // The fleck texture's side in pixels: the power of two (128 to 512) that makes a fleck at least 3 pixels wide + int TextureSize() const; // The sparkle texture's side in model units. The client moves its layers a fixed share of a tile a second (a // tile in 24, 48 and 72 s), so the tile sets how fast they cross: 75 sparkle sizes times the speed. float SparkleTile() const; @@ -53,9 +54,11 @@ namespace UgcGlitter { // the brick (drawn after them when it is transparent) doesn't cover them and they don't fight it for the depth constexpr float SPARKLE_LIFT = 0.005f; - // The fleck texture's alpha (TEXTURE_SIZE squared, rows top to bottom): `flecks` soft dots at the same places every - // time, wrapping around the edges so the texture tiles. Its color is white. - std::vector FleckAlpha(uint32_t flecks); + // The fleck texture's alpha (TextureSize() squared, rows top to bottom): `flecks` flat flakes of about `fleckSize` + // (0.7 to 1.3 of it) with a pixel's worth of edge, each as bright as its facet happens to catch the light (0.3 to 1 + // of `fleckOpacity`, most of them dim), at the same places every time, wrapping around the edges so the texture + // tiles. Its color is white. + std::vector FleckAlpha(const Params& params); // The sparkle texture's alpha (SparkleTextureSize() squared): flat discs of sparkleSize at SPARKLE_ALPHA covering // sparkleAmount percent of it, at the same places every time, tiling. Its color is white. diff --git a/dUgcServer/Render/UgcRender.cpp b/dUgcServer/Render/UgcRender.cpp index 2a1cd4bcd..0aa4cd533 100644 --- a/dUgcServer/Render/UgcRender.cpp +++ b/dUgcServer/Render/UgcRender.cpp @@ -358,7 +358,7 @@ namespace UgcRender { for (const auto* mesh : { &model.opaque, &model.transparent }) { anyGlitter = anyGlitter || std::find(mesh->looks.begin(), mesh->looks.end(), UgcModel::eLook::GLITTER) != mesh->looks.end(); } - const auto glitterAlpha = anyGlitter ? UgcGlitter::FleckAlpha(options.glitter.flecks) : std::vector{}; + const auto glitterAlpha = anyGlitter ? UgcGlitter::FleckAlpha(options.glitter) : std::vector{}; const auto shade = [&](const UgcModel::Mesh& mesh, bool isOpaque, uint32_t i0, uint32_t i1, uint32_t i2, float w0, float w1, float w2) { glm::vec3 normal(0.0f, 1.0f, 0.0f); diff --git a/dUgcServer/UgcServer.cpp b/dUgcServer/UgcServer.cpp index 03d4f94fd..72822b4f9 100644 --- a/dUgcServer/UgcServer.cpp +++ b/dUgcServer/UgcServer.cpp @@ -122,7 +122,9 @@ namespace { settings.shaders.glitter = std::min(Setting("shader_glitter", 21), 9999u); settings.shaders.sparkle = std::min(Setting("shader_glitter_sparkle", 79), 9999u); settings.shaders.glitterParams.tile = std::clamp(Setting("glitter_size", 1.6f), 0.1f, 100.0f); - settings.shaders.glitterParams.flecks = std::min(Setting("glitter_density", 50), 2000u); + settings.shaders.glitterParams.flecks = std::min(Setting("glitter_density", 80), 5000u); + settings.shaders.glitterParams.fleckSize = std::clamp(Setting("glitter_fleck_size", 0.05f), 0.005f, 1.0f); + settings.shaders.glitterParams.fleckOpacity = std::clamp(Setting("glitter_fleck_opacity", 80.0f), 0.0f, 100.0f); settings.shaders.glitterParams.sparkleSize = std::clamp(Setting("glitter_sparkle_size", 0.1f), 0.01f, 1.0f); settings.shaders.glitterParams.sparkleAmount = std::clamp(Setting("glitter_sparkle_amount", 5.0f), 0.0f, 50.0f); settings.shaders.glitterParams.speed = std::clamp(Setting("glitter_speed", 1.0f), 0.1f, 4.0f); diff --git a/docs/UgcServer.md b/docs/UgcServer.md index 1ed72f962..6bf67a9fd 100644 --- a/docs/UgcServer.md +++ b/docs/UgcServer.md @@ -303,8 +303,10 @@ all of its levels, so each look needs a group of its own. | `shader_glitter` | 21 | `S_Glitter_Model` (opaque) and `S_GlitterAlpha_Model` (transparent) for glitter colors: 21 is LEGO-AnimUV (gameValue 30), see Glitter below. | | `glitter_material_types` | `glitter` | Materials.xml `MaterialType`s that are glitter. | | `glitter_colors` | 114,117 | LEGO color ids that are glitter whatever their type (as `brushed_colors`). The default: the two colors LEGO's own color data (Studio's color categories, "Glitter Colors") files as glitter that the client's Materials.xml types `shinyPlastic` (114 Tr. Medium Reddish-Violet w. Glitter, 117 Transparent Glitter). | -| `glitter_size` | 1.6 | The glitter texture's tile, in model units (a stud is 0.8): the flecks' spacing, the same on every brick. | -| `glitter_density` | 50 | Flecks in one tile. | +| `glitter_size` | 1.6 | The glitter texture's tile, in model units (a stud is 0.8): with `glitter_density`, the flecks' spacing. | +| `glitter_density` | 80 | Flecks in one tile. | +| `glitter_fleck_size` | 0.05 | A fleck's diameter in model units (LDD units are centimetres: half a millimetre, about LEGO's glitter). | +| `glitter_fleck_opacity` | 80 | Percent: how white the brightest flecks are over the brick's color; most are dimmer. | | `shader_glitter_sparkle` | 79 | `S_GlitterSparkle_Model`, the glitter bricks' sparkles (only with `shader_glitter`): 79 is Distortion Directional (Ocean) (gameValue 89), whose layers the client moves on its own; 0: no sparkles. See Glitter below. | | `glitter_sparkle_size` | 0.1 | A sparkle's diameter in model units. | | `glitter_sparkle_amount` | 5 | Percent of each moving layer covered by sparkles (about its square's share of a brick sparkles at once). | @@ -392,10 +394,12 @@ material, alpha, specular, vertex colors): (translation 0, scale 1, Maya method, center 0.5). No controllers. - Its source, stored in the file as the client's own stored textures (`res/mesh/env/env_ag_ocean-maelstrom.nif`): `NiSourceTexture` (use external 0, name `ugc_glitter.dds`, pixel layout 6, mipmaps 2, alpha 3, static, persist - render data) and `NiPersistentSrcTextureRendererData`: RGBA 32 bit, channels blue, green, red, alpha, platform DX9, - 128 x 128 with 8 mipmaps. RGB is white; the alpha is `glitter_density` soft dots (radius 1.2 to 2.2 px, peak 0.65 - to 1) at places from a fixed seed, wrapping at the edges (`UgcGlitter::FleckAlpha`), each mipmap the 2x2 mean of - the one above. + render data) and `NiPersistentSrcTextureRendererData`: RGBA 32 bit, channels blue, green, red, alpha, platform DX9, square with + every mipmap down to 1 (128 at the defaults; the power of two up to 512 that keeps a fleck 3 pixels wide, + `Params::TextureSize`). RGB is white; the alpha is `glitter_density` flat flakes (`UgcGlitter::FleckAlpha`): 0.7 to + 1.3 times `glitter_fleck_size` across with a pixel's worth of edge, each as bright as its facet happens to catch the + light (0.3 to 1 of `glitter_fleck_opacity`, weighted towards dim: LEGO's glitter bricks show many faint flecks and + a few bright ones), at places from a fixed seed, wrapping at the edges; each mipmap the 2x2 mean of the one above. Transparent glitter: every UGC shape has the same `NiAlphaProperty` (blend source alpha over one minus source alpha) and transparent bricks are transparent by their vertex alpha; the LEGO-AnimUV techniques declare diff --git a/resources/ugcconfig.ini b/resources/ugcconfig.ini index 83c3a5c22..5a9dede2f 100644 --- a/resources/ugcconfig.ini +++ b/resources/ugcconfig.ini @@ -94,6 +94,8 @@ brushed_colors=298,300,1002,1004 # transparent ones, S_GlitterAlpha_Model: 21 is LEGO-AnimUV, which lays a white fleck texture stored in the .nif # over the color (still: the client never updates a placed model). 0: off, glitter stays plastic. # glitter_size: the fleck texture's tile in model units (a stud is 0.8); glitter_density: flecks in a tile; +# glitter_fleck_size: a fleck's diameter in model units (they are cm: 0.05 is half a millimetre, as LEGO's glitter); +# glitter_fleck_opacity: percent, the brightest flecks (each catches the light differently: most are dimmer). # shader_glitter_sparkle: the glitter bricks' sparkles, S_GlitterSparkle_Model over both glitter groups (only with # shader_glitter). 79 is Distortion Directional (Ocean), whose texture layers the client moves every frame on its # own: a sparkle flashes where two layers' sparkles meet. (Nothing else can move on a placed model: the client never @@ -109,7 +111,9 @@ shader_glitter=21 glitter_material_types=glitter glitter_colors=114,117 glitter_size=1.6 -glitter_density=50 +glitter_density=80 +glitter_fleck_size=0.05 +glitter_fleck_opacity=80 shader_glitter_sparkle=79 glitter_sparkle_size=0.1 glitter_sparkle_amount=5 diff --git a/tests/dUgcTests/UgcTests.cpp b/tests/dUgcTests/UgcTests.cpp index 7359a9fcc..3b25e9338 100644 --- a/tests/dUgcTests/UgcTests.cpp +++ b/tests/dUgcTests/UgcTests.cpp @@ -1304,17 +1304,36 @@ TEST(UgcModel, BrightnessAndTransparentColors) { EXPECT_NEAR(seeThrough.transparent.colors[0].a, 0.5882f, 1e-4f); } -// The glitter texture: the same every time, tiling (flecks wrap around the edges), mipmapped down to 1x1 +// The glitter texture: the same every time, tiling (flecks wrap around the edges), mipmapped down to 1x1; flecks of +// the size asked for (the texture grows to keep them 3 pixels wide), most dimmer than the brightest TEST(UgcGlitter, TextureIsTheSameEveryTimeAndMipmapped) { - const auto alpha = UgcGlitter::FleckAlpha(50); - ASSERT_EQ(alpha.size(), static_cast(UgcGlitter::TEXTURE_SIZE * UgcGlitter::TEXTURE_SIZE)); - EXPECT_EQ(alpha, UgcGlitter::FleckAlpha(50)); + UgcGlitter::Params params; + EXPECT_EQ(params.TextureSize(), 128); + const auto alpha = UgcGlitter::FleckAlpha(params); + ASSERT_EQ(alpha.size(), 128u * 128u); + EXPECT_EQ(alpha, UgcGlitter::FleckAlpha(params)); const auto lit = std::count_if(alpha.begin(), alpha.end(), [](uint8_t a) { return a > 0; }); - EXPECT_GT(lit, 50); + EXPECT_GT(lit, 80 * 4); EXPECT_LT(lit, static_cast(alpha.size() / 10)); // sparse - const auto none = UgcGlitter::FleckAlpha(0), dense = UgcGlitter::FleckAlpha(200); - EXPECT_EQ(std::count_if(none.begin(), none.end(), [](uint8_t a) { return a > 0; }), 0); - EXPECT_GT(std::count_if(dense.begin(), dense.end(), [](uint8_t a) { return a > 0; }), lit); + // Flat flecks up to the opacity (80%: 204), most of them dimmer + EXPECT_LE(*std::max_element(alpha.begin(), alpha.end()), 204); + EXPECT_GE(*std::max_element(alpha.begin(), alpha.end()), 190); + EXPECT_GT(std::count_if(alpha.begin(), alpha.end(), [](uint8_t a) { return a > 0 && a < 120; }), std::count_if(alpha.begin(), alpha.end(), [](uint8_t a) { return a >= 160; })); + // Bigger flecks cover more; small ones get a bigger texture + auto big = params; + big.fleckSize = 0.1f; + const auto bigAlpha = UgcGlitter::FleckAlpha(big); + EXPECT_GT(std::count_if(bigAlpha.begin(), bigAlpha.end(), [](uint8_t a) { return a > 0; }), lit * 2); + auto small = params; + small.fleckSize = 0.02f; + EXPECT_EQ(small.TextureSize(), 256); + EXPECT_EQ(UgcGlitter::FleckAlpha(small).size(), 256u * 256u); + auto none = params, dense = params; + none.flecks = 0; + dense.flecks = 300; + const auto noneAlpha = UgcGlitter::FleckAlpha(none), denseAlpha = UgcGlitter::FleckAlpha(dense); + EXPECT_EQ(std::count_if(noneAlpha.begin(), noneAlpha.end(), [](uint8_t a) { return a > 0; }), 0); + EXPECT_GT(std::count_if(denseAlpha.begin(), denseAlpha.end(), [](uint8_t a) { return a > 0; }), lit); const auto mips = UgcGlitter::Mipmaps(alpha); ASSERT_EQ(mips.size(), 8u); // 128 .. 1 EXPECT_EQ(mips.back().size(), 1u); @@ -1439,7 +1458,7 @@ TEST(UgcFormats, GlitterNifReadsBack) { EXPECT_EQ(header[3], 128u); EXPECT_EQ(header[6], 8u); EXPECT_EQ(header[21], 32u); - const auto alpha = UgcGlitter::FleckAlpha(50); + const auto alpha = UgcGlitter::FleckAlpha(glitter); for (size_t i = 0; i < alpha.size(); i++) { ASSERT_EQ(static_cast((*dds)[128 + i * 4]), 255); ASSERT_EQ(static_cast((*dds)[128 + i * 4 + 3]), alpha[i]) << i; @@ -1712,6 +1731,7 @@ TEST(UgcShaders, IconsDrawGlitterFlecks) { options.shadows = 0.0f; options.glitter.tile = 0.5f; options.glitter.flecks = 60; + options.glitter.fleckSize = 0.0156f; // as big against the tile as the defaults const auto plain = UgcRender::RenderIcon(model, options); model.opaque.looks.assign(4, UgcModel::eLook::GLITTER); const auto glitter = UgcRender::RenderIcon(model, options);