render: the hover highlight is the scene ambient, and unlit M2 parts take it
The 1.12 highlight is not a fixed 64/255 lift. SetHighlight samples the scene's committed ambient (read at 0x614576-0x6145bd), and every batch adds it to its colour before the final clamp, lit or unlit. 0xff404040 is only the fallback before a map's light loads. The highlight now follows the time of day, zone and weather: roughly (29, 60, 84)/255 at night to (104, 130, 154)/255 at noon, where it was always (64, 64, 64)/255. Unlit M2 batches in blend modes 0 to 4 now draw texel × clamp(tint·M2Color + highlight), the unlit program's c28 + c29. They took no highlight before. The reference holds the ambient sampled when a highlight begins; benilla samples it live, a marked deviation. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -4,13 +4,13 @@
|
||||
//! The reference pushes it on hover/target *change*, never a per-frame compare: the mouseover
|
||||
//! publisher and the target setter call `SetHighlight 0x614550` / `ClearHighlight 0x6144f0` with a
|
||||
//! per-object reason bitmask (bit 0 = target, bit 1 = mouseover — hover + target **stack**, and the
|
||||
//! glow drops only when the last reason clears). `SetHighlight` writes the config RGB — shipped
|
||||
//! default `0xff404040` ⇒ **+64/255 per channel** — into the model, and the animate kernel adds it
|
||||
//! to the material emissive (`glMaterialfv(GL_EMISSION)`): a flat additive lift inside the GL
|
||||
//! lighting sum, pre-texture-modulate, riding every lit material of the model *and* its attachments.
|
||||
//! glow drops only when the last reason clears). `SetHighlight` samples the scene's committed
|
||||
//! ambient (`[0xce9cd8]`, read at `0x614576`-`0x6145bd`) into the model and holds it until the last
|
||||
//! reason clears; each batch adds it to its colour before the final clamp (`c29`), lit or unlit,
|
||||
//! the model's attachments included. `0xff404040` is only the fallback before a map's light loads.
|
||||
//!
|
||||
//! benilla carries the flag in **bit 31 of the per-instance `MeshTag`** (the convention home is
|
||||
//! `benilla_world::mesh_tag`); `wow_model.wgsl` adds the 64/255 lift to its lighting factor when set. This
|
||||
//! `benilla_world::mesh_tag`); `wow_model.wgsl` adds the scene ambient when it is set. This
|
||||
//! system is the bit's only writer: each frame (PostUpdate — after every Update payload writer, so
|
||||
//! their whole-`u32` overwrites can't strand the bit) it ORs the flag onto every part of the
|
||||
//! hovered + selected roots and clears it on roots that left the set. The reason bitmask collapses
|
||||
|
||||
@@ -676,9 +676,9 @@ fn fragment(in: WowVsOut, @builtin(front_facing) is_front: bool) -> WowFragOut {
|
||||
let lit = select(select(lit_exterior, lit_interior, is_interior), lit_m2_interior, is_rig);
|
||||
// Gamma-space albedo: lighting runs on the authored byte values. `m.tint` plus its mat-anim
|
||||
// delta is the animated M2Color tint.
|
||||
let albedo = base.rgb * (m.tint.rgb + wow_light.matanim[u32(m.anim_slots.y)].xyz);
|
||||
// Unlit fullbright replaces the lit path and takes no emission: with lighting off, GL_EMISSION
|
||||
// is dead.
|
||||
let anim_tint = m.tint.rgb + wow_light.matanim[u32(m.anim_slots.y)].xyz;
|
||||
let albedo = base.rgb * anim_tint;
|
||||
// An unlit batch replaces the lit path: fullbright, with only the highlight added (below).
|
||||
let is_emissive = m.model_flags.w > 0.5;
|
||||
// SIDN night glow: the emissive × the night fraction (ramping 20:30→21:30, 06:00→07:00), a
|
||||
// GL_EMISSION term inside the clamped lit sum, on lit lanes only.
|
||||
@@ -693,9 +693,11 @@ fn fragment(in: WowVsOut, @builtin(front_facing) is_front: bool) -> WowFragOut {
|
||||
let sidn_e = m.sidn.rgb * (wow_light.grade.x * sidn_w);
|
||||
let point_diffuse = in.point_lit;
|
||||
|
||||
// The hover/target highlight (tag bit 31): `glMaterialfv(GL_EMISSION)` of 64/255 per channel
|
||||
// (config default 0xff404040), inside the lighting clamp; the unlit path never gets it.
|
||||
let highlight = select(0.0, 0.2509804, highlighted);
|
||||
// The hover/target highlight (tag bit 31): the scene's committed ambient
|
||||
// (`0x614576`-`0x6145bd`), added to the batch colour inside the final clamp, lit or unlit
|
||||
// (`c29`). Deviation: sampled live, where the reference holds the value sampled when the
|
||||
// highlight began; a unit's tag has no slot to hold a colour, and the ambient moves slowly.
|
||||
let highlight = select(vec3<f32>(0.0), wow_light.light_ambient.rgb, highlighted);
|
||||
// The FFP combine: the light sum (lit, point lights, emission) clamps first and the texture
|
||||
// modulates it, `tex × clamp(C·sum + emission)`, with C the GL_COLOR_MATERIAL colour (MOCV on
|
||||
// WMO, the body tint on M2). The WMO branch divides Bevy's MOCV fold back out, guarded; a dim
|
||||
@@ -705,7 +707,7 @@ fn fragment(in: WowVsOut, @builtin(front_facing) is_front: bool) -> WowFragOut {
|
||||
if (is_wmo) {
|
||||
let vc = in.color.rgb;
|
||||
let primary = clamp(
|
||||
vc * (lit + point_diffuse) + sidn_e + vec3<f32>(highlight),
|
||||
vc * (lit + point_diffuse) + sidn_e + highlight,
|
||||
vec3<f32>(0.0),
|
||||
vec3<f32>(1.0),
|
||||
);
|
||||
@@ -724,7 +726,7 @@ fn fragment(in: WowVsOut, @builtin(front_facing) is_front: bool) -> WowFragOut {
|
||||
// An M2: the body tint multiplies the light terms as MOCV does above. A WMO surface has
|
||||
// no tint slot, so the WMO branch leaves it out.
|
||||
let primary = clamp(
|
||||
inst_tint * (lit + point_diffuse) + sidn_e + vec3<f32>(highlight),
|
||||
inst_tint * (lit + point_diffuse) + sidn_e + highlight,
|
||||
vec3<f32>(0.0),
|
||||
vec3<f32>(1.0),
|
||||
);
|
||||
@@ -733,15 +735,29 @@ fn fragment(in: WowVsOut, @builtin(front_facing) is_front: bool) -> WowFragOut {
|
||||
#else
|
||||
// A WMO batch without MOCV lands here too; its tint slot is the identity slot 0.
|
||||
let primary = clamp(
|
||||
inst_tint * (lit + point_diffuse) + sidn_e + vec3<f32>(highlight),
|
||||
inst_tint * (lit + point_diffuse) + sidn_e + highlight,
|
||||
vec3<f32>(0.0),
|
||||
vec3<f32>(1.0),
|
||||
);
|
||||
lit_rgb = albedo * primary;
|
||||
#endif
|
||||
// The unlit path takes the body tint: with GL_LIGHTING off, gx SetState(1) is a plain
|
||||
// `glColor` modulate.
|
||||
var rgb = select(lit_rgb, albedo * inst_tint, is_emissive);
|
||||
// The unlit path: an M2's unlit program outputs `c28 + c29` (`0x70c663`-`0x70c693` fold the
|
||||
// tint·M2Color term into `c29` beside the highlight), so the texel modulates
|
||||
// `clamp(C·tint + highlight)`, C the M2Color. A WMO keeps the plain modulate.
|
||||
var unlit_rgb = albedo * inst_tint;
|
||||
if (!is_wmo) {
|
||||
#ifdef VERTEX_COLORS
|
||||
// The constant M2Color rides the vertex colour, which Bevy folded into `base`.
|
||||
let unlit_c = in.color.rgb * anim_tint;
|
||||
let unlit_tex = base.rgb / max(in.color.rgb, vec3<f32>(1.0 / 255.0));
|
||||
#else
|
||||
let unlit_c = anim_tint;
|
||||
let unlit_tex = base.rgb;
|
||||
#endif
|
||||
let unlit_sum = unlit_c * inst_tint + highlight;
|
||||
unlit_rgb = unlit_tex * clamp(unlit_sum, vec3<f32>(0.0), vec3<f32>(1.0));
|
||||
}
|
||||
var rgb = select(lit_rgb, unlit_rgb, is_emissive);
|
||||
// An M2 Mod or Mod2x batch draws the bare texel: the reference zeroes its tint·M2Color term and
|
||||
// forces the primary colour to the blend identity (`0x70c507`/`0x70c5b8`), so neither the
|
||||
// animated M2Color nor the body tint reaches it. Its alpha still does, through the lerp below.
|
||||
|
||||
@@ -17,9 +17,9 @@
|
||||
//! the device-init `GL_COLOR_MATERIAL` / `GL_AMBIENT_AND_DIFFUSE` current-colour path. So the tint
|
||||
//! is the **material ambient+diffuse colour**: it multiplies the light sum *inside* the [0,1] clamp
|
||||
//! and NOT the emission terms — structurally identical to what a WMO batch's MOCV already does in
|
||||
//! `wow_model.wgsl`, which is where the shader multiplies it. With lighting off (an UNLIT batch) the
|
||||
//! same state is a plain `glColor` modulate on the texture, so the fullbright path takes it too —
|
||||
//! unlike the highlight, whose GL_EMISSION is dead there.
|
||||
//! `wow_model.wgsl`, which is where the shader multiplies it. An UNLIT batch's program adds the
|
||||
//! tint·M2Color term and the highlight inside one clamp (`c28 + c29`), so the fullbright path takes
|
||||
//! the tint and the highlight both.
|
||||
//!
|
||||
//! (wow-re's `ghost-death-visuals.md` §4 still flags this combine as its one INFERRED hop and asks
|
||||
//! for a models-node follow-up; `models.md` is that follow-up, and it is VERIFIED — two independent
|
||||
|
||||
Reference in New Issue
Block a user