mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-10-02 02:43:44 +00:00
Why the glitter never moved: player models (LOT 14) are wrapped in weeblewobble.kfm (RenderComponentWrapper 9845), so the client makes them an LWOSkinnedRenderComponent, whose Run (0x00d6d3d0) updates the scene graph (and so any NiTextureTransformController) only while animation is enabled, and LWOModelBehaviorComponent::EnableAnimation (0x00be2740) turns it off for modelType 2, which every placed property model is. The root flags 0x102 added earlier are only read by the base render component. Nothing in a placed model's .nif can move. What does move: shader classes set globals in their own per-frame Run. Distortion Directional (Ocean) (mapShaders 79, Run 0x010b90c0) slides its texture layers by fixed shares of a tile a second, as the game's own pond ripples (S79__pond_ripplesShape). Glitter bricks now get a sparkle group, S79_GlitterSparkle_Model: their triangles lifted 0.005 off the brick, vertex colors white tinted by the brick, UVs placed per brick, alpha tested (ShaderCommon's alpha test phase, GREATEREQUAL 127), with a stored texture of flat sparkles at alpha 230: one layer's sparkle alone averages under the test, two meeting pass, so sparkles flash and go out as the layers cross. The flecks stay (LEGO-AnimUV, now without the controllers and flags that never ran). The icon and the dashboard's 3D view leave the sparkles out. New settings: shader_glitter_sparkle (79, 0 off), glitter_sparkle_size, glitter_sparkle_amount, glitter_sparkle_tint, glitter_sparkle_brightness; glitter_speed is now how fast sparkles flash (the sparkle tile). Only glitter output changes; non-glitter models are byte-identical. Check in game: reprocess a property with glitter models, then look at them from a few angles and distances, on each graphics quality: - sparkles flash on and off all over the glitter bricks, continuously - no flickering fight between the sparkles and the brick surface - transparent glitter bricks still see-through, flecks still visible - nothing drawn where there is no glitter brick; icons unchanged apart from the flecks Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
87 lines
5.2 KiB
C++
87 lines
5.2 KiB
C++
#pragma once
|
|
|
|
#include <cstdint>
|
|
#include <vector>
|
|
|
|
#include <glm/glm.hpp>
|
|
|
|
/**
|
|
* The glitter the UGC server gives glitter colors (docs/UgcServer.md, "Glitter"). Pure.
|
|
*
|
|
* Flecks: a tileable texture of white flecks (its alpha) laid over the brick's color by the client's LEGO-AnimUV shader
|
|
* (lerp(vertex color, texture, texture alpha), then the LEGO lighting). They stay still: a placed player model is never
|
|
* updated after it loads (LWOSkinnedRenderComponent::Run with animation off for modelType 2), so texture controllers in
|
|
* its .nif never run.
|
|
*
|
|
* Sparkles: a second shape over each glitter brick drawn by the client's Distortion Directional (Ocean) shader, whose
|
|
* texture layers the client moves every frame whatever the object does (the shader's Run sets
|
|
* g_vDirectionalMotionLayer1..3). Its texture has flat sparkles at an alpha that one layer's alone keeps under the
|
|
* alpha test, so a sparkle shows only where two moving layers' sparkles meet: points that flash and go out.
|
|
*
|
|
* Both are placed on each brick by UVs projected from the model's own coordinates, turned and moved by a number of the
|
|
* 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
|
|
// 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
|
|
float speed{ 1.0f }; // glitter_speed: how fast sparkles flash and go out (1: about half a second)
|
|
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 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;
|
|
// The sparkle texture's side in pixels: the power of two (128 to 1024) that makes a sparkle 3 pixels wide
|
|
int SparkleTextureSize() const;
|
|
bool operator==(const Params&) const = default;
|
|
};
|
|
|
|
// The sparkles' alpha in their texture. The alpha test of the client's alpha test phase keeps what reaches 127
|
|
// (ShaderCommon__SetupPhaseRenderStates: GREATEREQUAL 0x7f); the Directional shader averages 2 layers or 3 (by the
|
|
// graphics settings), so one sparkle alone is 115 or 77 and two meeting are 230 or 153
|
|
constexpr uint8_t SPARKLE_ALPHA = 230;
|
|
// How far the sparkle shapes stand off their brick, along its normals (model units): in front of its surface, so
|
|
// 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 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.
|
|
std::vector<uint8_t> SparkleAlpha(const Params& params);
|
|
|
|
// A square texture's mipmaps' alpha, from its own size down to 1: each the mean of 2x2 of the one before, or for
|
|
// the first `keepPeaks` the brightest of them (so sparkles keep their alpha at the next few distances)
|
|
std::vector<std::vector<uint8_t>> Mipmaps(const std::vector<uint8_t>& alpha, int keepPeaks = 0);
|
|
|
|
// A brick's number for placing its glitter (never 0), from the model's seed and the brick's index: the same for
|
|
// the brick in every LOD and every time the model is made
|
|
uint32_t BrickSeed(uint64_t modelSeed, uint32_t brick);
|
|
|
|
// Which texture a UV set is for: a brick's sparkles are placed apart from its flecks
|
|
enum class eLayer : uint8_t { FLECKS = 0, SPARKLES };
|
|
|
|
// A vertex's UV: its position on the axis plane its normal faces most, in tiles, turned by an angle and moved by
|
|
// an offset (under a tile) that `seed` (the brick's BrickSeed) picks for each plane and layer; seed 0 leaves it
|
|
// as it is
|
|
glm::vec2 Uv(const glm::vec3& position, const glm::vec3& normal, float tile, uint32_t seed = 0, eLayer layer = eLayer::FLECKS);
|
|
|
|
// A sparkle's vertex color: white taking `sparkleTint` percent of its brick's color (sRGB), at `sparkleBrightness`,
|
|
// alpha 1
|
|
glm::vec4 SparkleColor(const glm::vec4& brickColor, const Params& params);
|
|
|
|
// A square texture's alpha (0..1) at `uv` (wrapping, bilinear)
|
|
float Sample(const std::vector<uint8_t>& alpha, const glm::vec2& uv);
|
|
}
|