Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,19 @@ All notable changes to TowerCab 3D will be documented in this file.
- New **Show Moon** setting under Settings → Graphics lets you hide the moon from the sky.
- vNAS can join your CRC session on a YAAT training server: YAAT1, or YAAT Local when a local YAAT server is running.
- 3D buildings now show rows of windows on their walls, some of them lit at night.
- New **Weather quality** setting (Low, Medium, High) under Settings → Graphics; iPads and phones start lower, and small insets step down.

### Changed
- 3D buildings and aircraft no longer have an olive-green cast on their walls and fuselages; their reflected ground light is now a neutral grey.
- Remote access accepts only devices on your local network, unless you turn on **Allow devices outside the local network** in Settings → Configuration.
- Clouds and fog are now drawn with the scene's depth, so aircraft, buildings and terrain in front of a cloud deck or fog bank hide it correctly. They need the logarithmic depth setting on; with it off, no clouds or weather fog are drawn.
- Fog Intensity sets how thick the depth-drawn fog looks, and terrain in low visibility is no longer fogged twice.
- Weather fog now fades in as visibility drops from 10 to 8 miles, and eases over six seconds when visibility changes, instead of popping.
- Tower mod cabs now darken at night with the rest of the view and cast a shadow on the ground.
- Tower mod `rotation` now turns the cab clockwise, as documented; a cab with a rotation other than 0 may now face a different way.

### Fixed
- Tower mod cabs no longer float about 30 m above their tower when the airport loads before the terrain.
- The remote access server no longer serves or writes files outside its own folders.
- Remote browsers no longer receive your stored keys and sign-ins, such as the RealTraffic key and vNAS login.
- Remote browsers no longer receive the host's folder locations (MSFS Community, model cache, VMR, FSLTL, mods, diagnostics) from its settings, lists or error messages.
Expand All @@ -27,6 +34,7 @@ All notable changes to TowerCab 3D will be documented in this file.
- Tower and aircraft mod models now load on iPads and other browsers connected to the desktop app.
- The MSFS model cache size limit, Unlimited included, now survives a restart instead of resetting and deleting converted models.
- Converting an FSLTL or AIG model can no longer read files from outside that model's own MSFS package.
- An overcast now reaches the horizon and meets the distant haze in one grey, and broken layers beneath it no longer show bright horizon streaks.

### Removed
- The experimental VR mode and its VR button in the controls bar.
Expand Down
22 changes: 17 additions & 5 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,9 @@ In either, `WeatherDebugPanel` (dev builds only) forces rain, snow and cloud cov
- `camera.get()` returns the active viewport's `{ heading, pitch, fov, followMode, followingCallsign }`; `camera.set({ heading, pitch, fov })` sets any of them.
- `lookAt(lat, lon, altitudeFt)` aims the active viewport at a position.
- `project(lat, lon, heightM)` returns the page CSS-pixel point for `browser_click` and friends, or `null` when the point is behind the camera.
- `readPixels(x, y, w, h)` returns the Babylon canvas's RGBA bytes for a page rectangle (bottom-left row order). Rain is drawn additively with alpha 0, so screenshots and canvas `drawImage` miss it; read pixels instead.
- `readPixels(x, y, w, h)` returns the Babylon canvas's RGBA bytes for a page rectangle (bottom-left row order). Rain is drawn additively with alpha 0, so screenshots and canvas `drawImage` miss it; read pixels instead. It cannot see the Cesium weather stage's clouds and fog; use screenshots for those.
- `weatherStage` drives the cloud and fog stage: `set({ camera?, testAircraft? })` (null clears an aid) and `info()` returning `{ stageInScene, stageEnabled, stageNames, numberOfFrustums, logDepth }`.
- `towerModel` returns `{ loaded, modelMatrix, nightFactor }` for the tower cab, read live: whether a cab is in the scene, its column-major model matrix (`number[]`, or null without a cab) and the light factor (1 by day, lower at night). The main view only: insets draw no cab.
- `perf()` returns `{ current, window }`: `current` is the `performanceMonitor.getMetrics()` snapshot, `window` the median and p95 (`{ median, p95 }`, or null) over the last 300 frames of `frameInterval`, `totalFrame`, `cesiumUpdate`, `cesiumDraw` and `babylonRender`, plus `gpuCesiumMs` / `gpuBabylonMs` over the last 300 GPU timer results (null until the timer yields a real result). Rendering spikes sample it over 300 frames and report median and p95.

Driving notes:
Expand All @@ -79,6 +81,7 @@ Driving notes:
- Headless Edge over raw CDP renders at 1 fps until `page.bringToFront()` and needs `--disable-extensions`; do not restart Vite while its page is open (see `docs/DRIVING_CONVENTIONS.md`, rule headless-edge-quirks).
- `pnpm run dev -- <log>` writes the log relative to `src-tauri/`, not the repo root.
- The weather debug panel's Apply replaces all weather, clouds included.
- `viewer.scene.render()` with no argument renders at the wall clock, not the app's fixed clock; pass `viewer.clock.currentTime`.
- CDP `Emulation.*` overrides last only for the `browser_run_code_unsafe` call that sets them.
- An airport switch stalls page timers for about 10 s; wait before reading state.

Expand Down Expand Up @@ -414,10 +417,19 @@ if (version < 7) {
### Modifying Aircraft Rendering

1. Interpolation logic (60 Hz smooth motion): `useAircraftInterpolation.ts`
2. 3D model rendering: `CesiumViewer.tsx` (Cesium entities)
2. 3D model rendering: `CesiumViewer.tsx` (Cesium entities); model heading goes through `utils/modelOrientation.ts` (`aircraftModelHeading`)
3. Datablock labels and leader lines: `useBabylonOverlay.ts` (Babylon.js GUI)
4. Regression suite: `tests/motion/` drives the interpolation hook, the timeline store, the phase detector and the gear controller with synthetic VATSIM (15 s) and vNAS (1 Hz) flights and compares each frame with truth (`pnpm vitest run tests/motion`; `MOTION_REPORT=1` writes `.tmp/motion/report.txt`). A motion fix lands with a red-first assertion there, turning its finding's `it.todo` into an `it`.

### Modifying the Tower Cab

1. The cab is a Cesium model (`useTowerModel.ts`), not Babylon, so it hides the weather stage's clouds and fog behind it and casts a Cesium shadow; its console lines start `[useTowerModel]`
2. Placement: `ModService.getTowerPlacement` (manifest `modelPosition`, then tower-positions); ground height from `resolveGroundHeight` in `utils/towerHeight.ts` (field elevation until terrain loads); live wizard edits from `towerPositioningStore.ts`
3. Axis convention: `utils/modelOrientation.ts` (`compassToModelHeading`, `aircraftModelHeading`) serves aircraft and cab alike; `compassToModelHeading` points the glTF +X axis along a compass bearing; aircraft add 180 so their nose is on -X, and the cab adds 90 (`towerModelMatrix`) so its +X faces east at manifest `rotation` 0 and bearing `rotation + 90`, the convention KOAK's mod is tuned to
4. Night darkening: `cabNightFactor` over the shared curve in `utils/nightBrightness.ts`, the same one `useCesiumNightDarkening.ts` uses for the imagery
5. Inset windows never load mods (only the main window calls `modService.loadMods()`), so insets mount the hook but draw no cab
6. `cab-environment.env` stays on disk but nothing loads it

### Modifying Camera Behavior

1. Camera math: `useCesiumCamera.ts`
Expand All @@ -428,9 +440,9 @@ if (version < 7) {
### Modifying Weather Effects

1. METAR fetching/parsing: `services/MetarService.ts`, `weatherStore.ts`
2. Fog/cloud rendering: `useBabylonOverlay.ts`, `useBabylonWeather.ts`, `useCesiumWeather.ts`
3. Smoothing: `useSmoothedWeather.ts`
4. Settings: `settingsStore.ts` (fog/cloud toggles, intensity)
2. Fog/cloud rendering: the Cesium post-process stage in `useCesiumWeatherStages.ts` (shader in `hooks/weather/cloudStage.glsl.ts`, pure helpers in `utils/weatherStage.ts`, the deck's curved-earth geometry in `utils/cloudDeckGeometry.ts`); the stage stays hidden in a view with no logarithmic depth buffer, which then shows only the globe's distance fog; label weather culling (`isVisibleByWeather`, over `utils/weatherVisibility.ts`) in `useBabylonOverlay.ts`; scene fog in `useCesiumWeather.ts`
3. Smoothing: the fog's fade-in (`stageFogFade`) and its 6 s ease on a visibility step (`nextFogEase`, `easedFogDensity`) are in `utils/weatherStage.ts`, stepped from the stage's `preRender` in `useCesiumWeatherStages.ts`
4. Settings: `settingsStore.ts` (fog/cloud toggles, intensity, `weather.stageQuality`, the Low/Medium/High quality preset). The knobs of each preset are `WEATHER_STAGE_PRESETS` in `constants/weather.ts`; the main view reads the setting, an inset takes `insetStageQuality` from its size against the main window; the device presets (`SETTINGS_PRESETS`, via `deviceSettingsPreset()`) set it to High on desktop, Medium on iPad, Low on a phone

### Modifying Viewport System

Expand Down
2 changes: 1 addition & 1 deletion CREDITS.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

### `cab-environment.env`

Prefiltered environment cubemap used as the Babylon.js scene `environmentTexture` for image-based lighting (IBL) on the modded tower cab interior.
Prefiltered environment cubemap kept for image-based lighting (IBL) of the modded tower cab interior. Nothing loads it today; it stays on disk until the Babylon removal decides whether the Cesium cab needs it.

- **File:** `src/renderer/public/cab-environment.env`
- **Source:** [BabylonJS/Assets — `environments/studio.env`](https://github.com/BabylonJS/Assets/blob/master/environments/studio.env)
Expand Down
8 changes: 4 additions & 4 deletions MODDING.md
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ One mod can match multiple aircraft types by listing them in `aircraftTypes`.

### Model Guidelines

1. **Orientation**: Aircraft should point along the +Y axis (forward)
1. **Orientation**: Nose along -X and top up (in Blender: nose toward -X, Z up; export with +Y Up). A model with its nose along +X sets `rotationOffset.y` to 180
2. **Origin**: Place the origin at the aircraft's center
3. **Scale**: Model should be in meters (1 unit = 1 meter)
4. **File Size**: Keep models under 5MB for best performance
Expand Down Expand Up @@ -241,7 +241,7 @@ Tower mods can specify an explicit model position using absolute lat/lon coordin
| `lat` | number | Yes | Latitude of 3D model position (double precision) |
| `lon` | number | Yes | Longitude of 3D model position (double precision) |
| `height` | number | No | Height offset in meters above terrain (default 0) |
| `rotation` | number | No | Model rotation in degrees (0=north, 90=east, default 0) |
| `rotation` | number | No | Clockwise turn of the model in degrees. At 0 the model's +X axis faces east, and 90 turns it to face south (default 0) |

**Note:** The `modelPosition` field controls where the 3D tower model renders. The `cameraPosition` field (separate) controls where the camera viewpoint is located. These can be different positions.

Expand Down Expand Up @@ -271,7 +271,7 @@ When these are set, they become the default camera position for that airport (us

### Model Guidelines

1. **Orientation**: Tower should be upright with entrance facing +Y
1. **Orientation**: Upright, with the side that should face east at `rotation` 0 along +X (in Blender: that side toward +X, Z up; export with +Y Up)
2. **Origin**: Place at ground level, center of the tower base
3. **Scale**: Model should be in meters
4. **Detail**: Include cab windows and basic structure
Expand Down Expand Up @@ -515,7 +515,7 @@ For best results, convert SketchUp models to GLB:

- **Scale**: SketchUp uses inches by default. Set units to Meters (Window → Model Info → Units)
- **Origin**: Position your model at the origin (0,0,0) for proper placement
- **Orientation**: Tower entrance should face the green axis (+Y)
- **Orientation**: The side that should face east at `rotation` 0 faces the red axis (+X), with blue (+Z) up
- **Textures**: Use simple, low-resolution textures for better performance
- **Components**: Flatten components before export (Edit → Component → Explode)

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ A 3D tower cab view application for VATSIM air traffic controllers. View real-ti
- Dynamic time-of-day lighting (real-time or fixed hour)
- Aircraft rendered as 3D models with realistic type-based sizing (39 aircraft types)
- Aircraft physics emulation: pitch during climbs/descents, banking in turns, landing flare
- METAR-based weather effects: fog domes and cloud layers at real ceiling altitudes
- METAR-based weather effects: visibility fog and cloud layers at real ceiling altitudes

### Multi-Viewport System
- Create multiple inset viewports for simultaneous views
Expand Down
15 changes: 10 additions & 5 deletions USER_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ On first connection from a touch device, you'll be prompted to apply optimized g
- Reduced shadow quality for smoother performance
- Lower MSAA anti-aliasing (2x instead of 4x)
- Smaller tile cache to conserve memory
- Lower weather quality (Medium; a low-end tablet gets the phone preset and Low)
- Optimized for 60 FPS on mobile devices

**Desktop Preset:**
Expand Down Expand Up @@ -711,12 +712,14 @@ TowerCab 3D displays real-time weather based on METAR reports.

**Fog Effects:**
- Visibility-based fog that limits terrain draw distance
- Visual fog dome at the visibility boundary
- Visual fog at the visibility boundary, drawn behind aircraft, buildings and terrain that are closer than the fog (not drawn in top-down view)
- Fog fades in as visibility drops from 10 to 8 statute miles, and eases over 6 seconds when the visibility changes (a new METAR, or a move of the interpolated visibility); the Visibility Scale slider, the fog toggle and the top-down view apply at once
- Adjustable intensity and visibility scale

**Cloud Layers:**
- Cloud planes positioned at METAR-reported ceiling altitudes
- Scattered (SCT), broken (BKN), and overcast (OVC) layers shown
- Clouds positioned at METAR-reported ceiling altitudes, hidden by anything closer to the camera
- With the Logarithmic Depth Buffer graphics setting off, no clouds or METAR fog are drawn, and only the globe's distance fog shows
- Scattered (SCT), broken (BKN), and overcast (OVC) layers shown; a layer under a BKN or OVC layer is drawn grey on top, with no bright streaks along the horizon
- Aircraft behind clouds may have hidden datablocks

### Weather-Based Visibility
Expand All @@ -732,9 +735,10 @@ When weather effects are enabled:
| Setting | Description |
|---------|-------------|
| Enable Fog | Toggle fog effects on/off |
| Fog Intensity | Opacity of the fog dome (0.5x-2x) |
| Fog Intensity | How thick weather fog looks (0.5x-2x) |
| Visibility Scale | Multiplier for fog distance (2x = see twice as far as METAR) |
| Enable Clouds | Toggle cloud layer display on/off |
| Weather Quality | Cloud and fog detail (Low, Medium, High). Lower draws clouds nearer and coarser for a faster frame; smaller inset windows automatically use a lower quality |

### Automatic Weather Updates

Expand Down Expand Up @@ -858,6 +862,7 @@ See the [MSFS Model Support](#msfs-model-support) section for details on using F
| **Fog Intensity** | Fog opacity multiplier | 1x |
| **Visibility Scale** | Fog distance multiplier | 1x |
| **Enable Clouds** | Cloud layers at METAR ceilings | On |
| **Weather Quality** | Cloud and fog detail: Low, Medium or High | High in the desktop app; in a browser, Medium on iPads and tablets, Low on phones and low-end tablets |
| **Enable Precipitation** | Rain/snow particle effects | On |
| **Enable Lightning** | Lightning flashes during storms | On |

Expand Down Expand Up @@ -971,7 +976,7 @@ Add custom aircraft models with airline-specific liveries. Mods match by ICAO ai

### Tower Mods

Add custom control tower models for specific airports. Mods match by ICAO airport code (e.g., KJFK, EGLL).
Add custom control tower models for specific airports. Mods match by ICAO airport code (e.g., KJFK, EGLL). The tower cab appears in the main view only, hides clouds and fog behind it, casts a shadow on the ground, and darkens at night when night darkening and lighting are on. Inset windows do not show it.

### Bundled Tower Positions

Expand Down
4 changes: 3 additions & 1 deletion docs/DRIVING_CONVENTIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,9 @@ Source: CLAUDE.md:41, CLAUDE.md:56; docs/plans/rendering-opportunities-design.md

Rain in the Babylon overlay is drawn additively with alpha 0, so CDP screenshots and canvas `drawImage` both miss it. Verify such effects with the hook's `readPixels(x, y, w, h)` on the Babylon canvas (RGBA, bottom-left row order) or a video, never a still screenshot.

The converse also holds: `readPixels` covers only the Babylon canvas, so Cesium pixels are still judged from page screenshots. The plan's standing verification line says "particles and other additive effects need a video or engine.readPixels, not a screenshot".
The converse also holds: `readPixels` covers only the Babylon canvas, so Cesium pixels, including the clouds and fog of the Cesium weather stage (drawn only in a view with a logarithmic depth buffer), are still judged from page screenshots.

A render check that calls `viewer.scene.render()` to read those pixels passes `viewer.clock.currentTime`: with no argument it renders at the wall clock, not the app's fixed clock, so sun and night lighting differ from the screenshot. The plan's standing verification line says "particles and other additive effects need a video or engine.readPixels, not a screenshot".
Source: CLAUDE.md:49; src/renderer/hooks/useAgentHarness.ts:66-81; docs/plans/rendering-opportunities-design.md:109, 214; commit 8d672dc (MAIN.md "Verification, every wave"). Seen: Wave 1 run.

### CDP emulation overrides last only for the call that sets them
Expand Down
Loading