mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-10-02 02:43:44 +00:00
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 <noreply@anthropic.com>
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,36 +17,38 @@ namespace UgcGlitter {
|
||||
int Side(const std::vector<uint8_t>& alpha) { return static_cast<int>(std::lround(std::sqrt(static_cast<double>(alpha.size())))); }
|
||||
}
|
||||
|
||||
std::vector<uint8_t> FleckAlpha(uint32_t flecks) {
|
||||
constexpr int N = TEXTURE_SIZE;
|
||||
std::vector<float> alpha(static_cast<size_t>(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<float>(side) < wanted) side *= 2;
|
||||
return side;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> FleckAlpha(const Params& params) {
|
||||
const int N = params.TextureSize();
|
||||
std::vector<uint8_t> alpha(static_cast<size_t>(N) * N, 0);
|
||||
const float size = params.fleckSize / std::max(params.tile, 0.001f) * static_cast<float>(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<float>((z ^ (z >> 31)) >> 40) / static_cast<float>(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<int>(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<int>(std::ceil(radius + 0.5f));
|
||||
for (int dy = -reach; dy <= reach; dy++) {
|
||||
for (int dx = -reach; dx <= reach; dx++) {
|
||||
const int x = static_cast<int>(std::floor(cx)) + dx, y = static_cast<int>(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<size_t>(((y % N) + N) % N) * N + ((x % N) + N) % N];
|
||||
value = std::max(value, peak * (1.0f - d * d));
|
||||
value = std::max(value, static_cast<uint8_t>(std::lround(cover * peak * 255.0f)));
|
||||
}
|
||||
}
|
||||
}
|
||||
std::vector<uint8_t> out(alpha.size());
|
||||
for (size_t i = 0; i < alpha.size(); i++) out[i] = static_cast<uint8_t>(std::lround(std::clamp(alpha[i], 0.0f, 1.0f) * 255.0f));
|
||||
return out;
|
||||
return alpha;
|
||||
}
|
||||
|
||||
float Params::SparkleTile() const {
|
||||
|
||||
@@ -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<uint8_t> 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<uint8_t> 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.
|
||||
|
||||
@@ -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<uint8_t>{};
|
||||
const auto glitterAlpha = anyGlitter ? UgcGlitter::FleckAlpha(options.glitter) : std::vector<uint8_t>{};
|
||||
|
||||
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);
|
||||
|
||||
@@ -122,7 +122,9 @@ namespace {
|
||||
settings.shaders.glitter = std::min(Setting<uint32_t>("shader_glitter", 21), 9999u);
|
||||
settings.shaders.sparkle = std::min(Setting<uint32_t>("shader_glitter_sparkle", 79), 9999u);
|
||||
settings.shaders.glitterParams.tile = std::clamp(Setting<float>("glitter_size", 1.6f), 0.1f, 100.0f);
|
||||
settings.shaders.glitterParams.flecks = std::min(Setting<uint32_t>("glitter_density", 50), 2000u);
|
||||
settings.shaders.glitterParams.flecks = std::min(Setting<uint32_t>("glitter_density", 80), 5000u);
|
||||
settings.shaders.glitterParams.fleckSize = std::clamp(Setting<float>("glitter_fleck_size", 0.05f), 0.005f, 1.0f);
|
||||
settings.shaders.glitterParams.fleckOpacity = std::clamp(Setting<float>("glitter_fleck_opacity", 80.0f), 0.0f, 100.0f);
|
||||
settings.shaders.glitterParams.sparkleSize = std::clamp(Setting<float>("glitter_sparkle_size", 0.1f), 0.01f, 1.0f);
|
||||
settings.shaders.glitterParams.sparkleAmount = std::clamp(Setting<float>("glitter_sparkle_amount", 5.0f), 0.0f, 50.0f);
|
||||
settings.shaders.glitterParams.speed = std::clamp(Setting<float>("glitter_speed", 1.0f), 0.1f, 4.0f);
|
||||
|
||||
Reference in New Issue
Block a user