# Boreas visual import

The current scene imports the actual locally supplied `surf_boreas.bsp`. It replaces the original procedural mountain scene. This is an independently written browser renderer for the map's geometry and packed assets; it is not Valve's renderer and does not claim pixel parity with CSS.

## Provenance and rights

- Original map credit: **Syncronyze**. The supplied map's wider provenance also credits **granis**; retain the map's original author and asset credits.
- Input: BSP version 20, SHA-1 `9bd9daa0a23288c7e6f439fb7a899beade34a80b`.
- Download provenance: [the matching FastDL BSP archive](https://main.fastdl.me/h2/9bd9daa0a23288c7e6f439fb7a899beade34a80b/surf_boreas.bsp.bz2).
- Generated map geometry, textures, models and lighting remain their original authors' work. Public download availability is not a permissive redistribution license. This conversion is for the user's local project; no permission to redistribute or publish those assets is asserted.
- The converter and renderer were written independently from format descriptions and mathematical behavior. No Valve source implementation was copied into them. The [Source SDK license](https://github.com/ValveSoftware/source-sdk-2013/blob/master/LICENSE) must not be represented as a general browser-game asset or code license.

Primary technical references: Valve's [BSP structures](https://github.com/ValveSoftware/source-sdk-2013/blob/master/src/public/bspfile.h), [studio model structures](https://github.com/ValveSoftware/source-sdk-2013/blob/master/src/public/studio.h), [optimized model structures](https://github.com/ValveSoftware/source-sdk-2013/blob/master/src/public/optimize.h), [displacement construction](https://github.com/ValveSoftware/source-sdk-2013/blob/master/src/public/builddisp.cpp), and [client sky rendering](https://github.com/ValveSoftware/source-sdk-2013/blob/master/src/game/client/viewrender.cpp). Shared SDK structures establish format behavior where applicable; they do not establish exact CSS shader behavior.

## Imported resources

| Resource | Imported result |
| --- | --- |
| Visible brush and displacement faces | 1,783, combined into 35 material/lightmap/sky batches |
| Displacements | All 1,125, full resolution, using the collision importer's shared grid and triangulation |
| Original static props | All 1,587, from 26 distinct model files |
| Studio model geometry | MDL v44/v48, VVD LOD 0, DX90 VTX strip groups |
| Original material images | 233 PNG images decoded from packed VTFs, including normal/detail maps and cubemap faces |
| Baked lighting | One lossless 2,048² RGBExp32 world-lightmap atlas and one original VHV vertex-lighting atlas |
| Total referenced images | 235, including the two lighting images |
| Sky | Original packed sky textures, scaled 3D sky geometry and sky props |
| Brush entities | Authored initial origin/angle transforms and render colors, including the purple finish decorations |

`scripts/import_boreas_visuals.py` creates `public/maps/boreas-visuals.json` and resources beneath `public/maps/boreas/`. It reads the local BSP path by default; pass a different BSP path as its first argument to reproduce the conversion elsewhere. Python requires NumPy and Pillow. `scripts/bsp_common.py` provides the common compressed-lump, entity, transform and displacement readers.

`src/view/bsp-renderer.ts` exposes `loadBspVisuals(slug)`, its compatibility alias `loadBoreasVisuals()`, and `createBspRenderer(canvas, data, map?)`. Geometry/image fetching finishes before renderer creation. Asset decoding never happens in a movement tick. Opaque props use conservative per-instance frustum culling and shared model buffers; translucent props retain spatial batches. The optional map argument supplies visible timer-zone markers.

## Camera and material treatment

Positions remain in Source units throughout export. The renderer's only axis conversion is `(x, y, z) → (x, z, -y)`. Source yaw 0 faces +X; positive pitch looks down. The supplied eye position and current mouse angles are used directly. There is no camera easing, banking, mouse filtering or speed-based FOV. A 4:3 horizontal Source FOV becomes vertical FOV through `2 atan(tan(horizontalFov/2) × 3/4)`; viewport aspect then determines widescreen horizontal coverage.

Brush UVs use the original texture vectors and dimensions. Displacement UVs interpolate their ordered base surface; displacement lightmap UVs stretch the authored luxel rectangle over the grid, as in `CCoreDispSurface::CalcLuxelCoords`. Seamless terrain uses world-coordinate triplanar sampling and the authored rock/snow alpha. Sky sampling reverses the sky-camera expansion for texture scale.

RGBExp32 light samples preserve their exponent. The shader decodes four neighboring luxels to linear values before bilinear interpolation; it never linearly filters the exponent byte. Props use their embedded VHV vertex lighting. Alpha-tested foliage clips the original leaf alpha, then writes opaque alpha to avoid browser compositing fringes. Packed cubemaps are used for reflective materials; `env_cubemap` props select the nearest packed probe. Rock detail textures retain their authored scale and modulation factor.

Fog uses the map's original color (232, 255, 254), start 500 and end 43,420. The default planar fog path is used. Source shrinks the sky camera and its fog ranges together; because this renderer expands sky geometry instead, it retains those original fog ranges. Sky rendering precedes the main world with a depth clear between them.

## Validation

Run `python scripts/validate_boreas_visuals.py` for asset integrity. The saved result is `fixtures/boreas-asset-validation.json`:

- 127,829 mesh vertices and 531,810 triangle indices are finite, in range and structurally valid.
- All 84,405 displacement vertices match the common BSP decoder exactly after float32 export and the documented sky expansion: maximum coordinate error **0 Source units**.
- All 1,587 original props are represented and all 235 referenced images exist.

This is an internal geometry/resource integrity check, not independent proof that every BSP format assumption or rendered pixel matches CSS. The movement validation and external CSS trajectory comparisons are separate deliverables.

With the development server running, `node --import tsx scripts/bsp-visual-qa.ts` launches headless Edge, renders four fixed inspection views and measures a short camera traversal. It uses an isolated visual scene and cannot move the gameplay player. Results are in `fixtures/boreas-render-results.json`; screenshots are `docs/screenshots/boreas-start.png`, `boreas-first-ravine.png`, `boreas-overview.png` and `boreas-finish-basin.png`.

The newer full-route renderer comparison at 1,600 × 900 reduced CPU submission mean from **0.497 ms to 0.264 ms**, p95 from **1.0 ms to 0.5 ms**, while retaining the original geometry. See [rendering performance](RENDER-PERFORMANCE.md) for GPU measurements, stable quality settings, before/after pixel comparisons, reproducible scripts, and limitations. These numbers describe this machine and test, not a guaranteed end-user frame rate, input-to-photon latency, or the complete simulation/UI frame cost.

## Remaining visual differences

1. Two stock game textures are referenced but not packed: `nature/dirtfloor005b` and `models/props/cs_office/clouds`. The missing secondary dirt layer falls back to the primary rock material. The missing translucent cloud surface is omitted instead of drawing an opaque white substitute.
2. Original diffuse baked lighting is retained, but Source's directional bumped-lightmap basis, SSBump lighting and exact vertex-light color conversion are not reproduced. HDR exposure/tonemapping and Source-specific lighting overbright behavior are not claimed equivalent.
3. Displacement alpha is retained, while blend-modulation textures and exact Source seamless sampling are approximated. Displacement normals are recomputed; Source's cross-displacement smoothing details can differ.
4. Cubemap reflections and tangent-space normal perturbations are approximations. Exact CSS reflection contrast, saturation, Fresnel, probe orientation and material shader permutations have not received pixel-level comparison.
5. Refractive ice and water use transparent reflected surfaces. Scene-color refraction, water reflection/refraction render targets and animated normal maps are not implemented.
6. Brush entities appear at their authored initial transforms. Rotating symbols, animated material proxies, moving decoration, dust/particles, laser beams and soundscapes are not reproduced. The original finish symbols are present but their animated effects differ.
7. Rendering uses full model LOD 0 and spatial/frustum culling, rather than Source's PVS, model LOD selection and displacement tessellation. This preserves collision-scale geometry but can differ in distant appearance and GPU cost.

No direct CSS screenshot/pixel comparison or CSS renderer telemetry was available for this visual import. Browser inspection establishes that the actual imported scene renders coherently; movement fidelity rests on its separate evidence.
