fix(dashboard): flair tints and Basic lighting as the client computes them

- Flair tints are the terrain file's color bytes over 255, times the flair
  model's own vertex colors (FlairAssets::AppendRenderBuffer divides by
  255.0). The manifests said 63, so in the game shaders' sRGB maths tints
  of up to 4 turned the flairs white (Battle Against Frakjaw's leaves).
  Conversion format 6, so flair manifests kept by browsers are fetched
  again.
- BasicShaders, AlphaAsAlpha and Flair.fx multiply the light by the vertex
  color and only then clamp, as the COLOR0 output; the views clamped the
  light first, so a light brighter than 1 no longer lifted darker vertex
  colors.
- Docs: the flair tint, the clamp, and that AlphaAsAlpha cards show their
  backs (Cullmode none) from outside a playable area.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Aaron Kimbrell
2026-09-28 16:31:12 -05:00
parent 4bd34dc087
commit 93f4b051c6
4 changed files with 24 additions and 11 deletions

View File

@@ -278,9 +278,10 @@ namespace {
* which must follow this; SceneryCoreJs checks), so a manifest a browser kept for a day from an older server
* isn't drawn with newer code. 2: meshes carry their multishader tag; conversions without it drew glom parts with
* the LEGO shader. 3: dark textures and the UV set each texture names. 4: the game's shaders draw the models
* (manifest "techniques"), vertex colors go to them as stored. 5: an animated material alpha is its highest key.
* (manifest "techniques"), vertex colors go to them as stored. 5: an animated material alpha is its highest key. 6: flair tints are bytes over
* 255 (they were over 63).
*/
constexpr uint32_t FORMAT_VERSION = 5;
constexpr uint32_t FORMAT_VERSION = 6;
// A zone's lighting (WorldScene::Lighting) for the viewers' shaders
nlohmann::json LightingJson(const WorldScene::Lighting& lighting) {
@@ -442,7 +443,7 @@ namespace {
/**
* The flairs' manifest (as the scenery's, plus a tint per flair), from the zone's terrain file. A flair's color
* tints its model; 63 is full strength (the files use 0 to 63 for most flairs, a little more for brighter ones).
* tints its model as a byte over 255, times the model's own vertex colors (FlairAssets::AppendRenderBuffer).
*/
std::optional<std::string> BuildFlairs(uint32_t zoneId, ZoneScenery& scenery) {
const auto raw = ZoneRawShared(zoneId);
@@ -477,7 +478,7 @@ namespace {
assets = scenery.assets;
}
return nlohmann::json{
{"zone", zoneId}, {"sky", -1}, {"assets", assets}, {"distance", FLAIR_DISTANCE}, {"colorScale", 1.0 / 63.0}, {"lighting", scenery.lighting}, {"format", FORMAT_VERSION},
{"zone", zoneId}, {"sky", -1}, {"assets", assets}, {"distance", FLAIR_DISTANCE}, {"colorScale", 1.0 / 255.0}, {"lighting", scenery.lighting}, {"format", FORMAT_VERSION},
// Flair.fx for all of them: (0.85 * sun + ambient) * the flair's tint, whatever their facing
{"technique", { {"family", "flair"}, {"look", 0}, {"alpha", "opacity"}, {"flags", 0} }},
{"objects", { {"asset", assetOf}, {"pos", positions}, {"rot", rotations}, {"scale", scales}, {"color", colors} }}
@@ -488,7 +489,7 @@ namespace {
/**
* The flairs' manifest (as the scenery's, plus a tint per flair), from the zone's terrain file. A flair's color
* tints its model; 63 is full strength (the files use 0 to 63 for most flairs, a little more for brighter ones).
* tints its model as a byte over 255, times the model's own vertex colors (FlairAssets::AppendRenderBuffer).
* Built when first asked for (any thread).
*/
const std::optional<std::string>& Flairs(uint32_t zoneId, ZoneScenery& scenery) {

View File

@@ -32,6 +32,7 @@ varying vec2 vUvDark;
varying vec2 vUv;
varying vec4 vColor;
varying vec3 vLight; // the lit color the technique's vertex shader outputs (clamped as a COLOR output is)
varying vec3 vLit; // ... times the vertex color, clamped once as the Basic, AlphaAsAlpha and Flair COLOR0 outputs are
varying float vLdn; // N.L, clamped
varying vec3 vNormal; // world space
varying vec3 vWorld;
@@ -92,11 +93,21 @@ void main() {
// g_metalDiffuse * N.L^4 + g_metalAmbient
vLight = clamp( vec3( 0.7 ) * pow( vLdn, 4.0 ) + vec3( 0.3 ), 0.0, 1.0 );
#elif defined( FAMILY_FLAIR )
// Flair_VS: (0.85 * sun + ambient) * the flair's tint, clamped once as the COLOR0 output
vLight = clamp( 0.85 * gameLightColor + gameAmbient, 0.0, 1.0 );
vLit = clamp( ( 0.85 * gameLightColor + gameAmbient ) * vcol.rgb, 0.0, 1.0 );
#elif defined( UNLIT )
vLight = vec3( 1.0 );
vLit = clamp( vcol.rgb, 0.0, 1.0 );
#else
vLight = clamp( max( vec3( 0.0 ), gameLightColor * ldn ) + gameAmbient, 0.0, 1.0 );
// BasicShaders / AlphaAsAlpha *_Lighting_VertColor_VS: (sun * N.L + ambient) * the vertex color, then clamped as
// the COLOR0 output (not the light alone: a bright light still lifts a dark vertex color)
vec3 lit = max( vec3( 0.0 ), gameLightColor * ldn ) + gameAmbient;
vLight = clamp( lit, 0.0, 1.0 );
vLit = clamp( lit * vcol.rgb, 0.0, 1.0 );
#endif
#if !defined( FAMILY_FLAIR ) && !defined( FAMILY_BASIC )
vLit = vLight * vcol.rgb;
#endif
gl_Position = projectionMatrix * viewMatrix * worldPosition;
}
@@ -128,6 +139,7 @@ varying vec2 vUvDark;
#endif
varying vec4 vColor;
varying vec3 vLight;
varying vec3 vLit;
varying float vLdn;
varying vec3 vNormal;
varying vec3 vWorld;
@@ -309,7 +321,7 @@ void main() {
#else
// Basic, AlphaAsAlpha, fixed function, flairs, the sky: texture times the lit (or unlit) vertex color
vec4 light = vec4( vLight * vc.rgb, vc.a );
vec4 light = vec4( vLit, vc.a );
#ifdef LAYERS_BLENDED
result = vec4( mix( tex( darkMap, vUvDark ).rgb, tex( map, vUv ).rgb, vc.a ) * light.rgb, 1.0 );
#elif defined( LAYERS_ADDED )

View File

@@ -111,7 +111,7 @@ export const TECHNIQUE = {
* The conversion format (Scenery.cpp FORMAT_VERSION) these views are written for. Manifest URLs carry it, so a
* browser never draws with a manifest it kept from an older server (they are cached for up to a day).
*/
export const SCENERY_FORMAT = 5;
export const SCENERY_FORMAT = 6;
/** A manifest URL asking for SCENERY_FORMAT's manifest (the server ignores the parameter; browsers cache by it). */
export function manifestUrl(url) {