import { describe, expect, it } from 'vitest'; import { SceneResourceBag } from '@/scene/resources'; import { renderQualityFor } from '@/scene/render-quality'; import { createExpressway, PYLON_CORE_GAIN, PYLON_HALO_GAIN } from '@/scene/world/expressway'; interface Node { name: string; count?: number; material?: { blending: number; fragmentShader?: string }; } const children = (group: unknown) => (group as { children: Node[] }).children; const luminance = ([r, g, b]: readonly number[]) => 0.2126 * r! + 0.7152 * g! + 0.0722 * b!; const CYAN = [0.02, 0.78, 0.93] as const; const MAGENTA = [1.0, 0.025, 0.34] as const; describe('expressway pylon lamps', () => { it('draws a two-layer lamp: a core and a soft additive halo for every pylon strip', () => { const world = createExpressway(new SceneResourceBag(), renderQualityFor('high', 1, false)); const core = children(world.group).find((child) => child.name === 'recessed-pylon-lights')!; const halo = children(world.group).find((child) => child.name === 'pylon-light-halos')!; expect(halo.count).toBe(core.count); expect(halo.material!.blending).toBe(core.material!.blending); expect(halo.material!.fragmentShader).toContain('across'); }); it('keeps the core under the bloom threshold at full drive energy, so the hue survives the tone curve', () => { // Core: hue * (1.3 + energy 0.5) * gain; core + halo peak stays under 0.85 luminance too. for (const hue of [CYAN, MAGENTA]) { const peak = 1.8 * PYLON_CORE_GAIN; expect(luminance(hue.map((v) => v * peak))).toBeLessThan(0.85); expect(luminance(hue.map((v) => v * 1.8 * (PYLON_CORE_GAIN + PYLON_HALO_GAIN)))).toBeLessThan(1.2); } expect(PYLON_HALO_GAIN).toBeLessThan(PYLON_CORE_GAIN); }); });