Merge branch 'main' into fix/adsblol-true-heading

This commit is contained in:
Sameh Khamis authored and GitHub committed 2026-11-14 09:00:30 -08:00
commit 58776a6976
14 files changed
+61 -61

No files matched your search

@@ -0,0 +1,3 @@
- Draw aircraft models in their own mesh colours instead of a 94% flat
white (amber for military) over the model; the tracked and emergency
colours now tint the mesh without hiding its paint (samehkhamis, #1731).
+2 -2
View File
@@ -7429,9 +7429,9 @@ pins and share links already supply complete boxes.
normal four-samples-per-250-ms budget. Retry timestamps more than the maximum
30-second backoff ahead of the current clock are ignored. A smaller rollback
can retain a bounded retry delay of up to 30 seconds.
- Commercial and military aircraft use the same high-level FLEET model regime: 2D billboards when zoomed out, optional glTF models when closer, controlled by the DISPLAY rail `3D` toggle and `Proximity` / `All` modes. Every model is one of the project's procedural fleet models (`public/models/fleet/`, MIT, 122 GLBs, about 2 MB in all; `public/models/README.md`), chosen per aircraft by `aircraftModelSpec` (`src/data/fleetModels.js`): the first of its ICAO type's callsign rules in `types.json` (`aircraft_callsign`) its callsign matches, else its type's entry, else the model of its `classifyAircraft` class for a type the map lacks, else its ADS-B emitter category's, else the default airliner. It is drawn LIFE SIZE: the type's published length over the model's built length (`src/layers/flights/modelSpec.js`, shared by Flights, Military Flights and Local ADS-B), and a grounded aircraft stands the model's scaled clearance above the ground. An airport ground vehicle (feed type `ZVEH`) is a fire truck (`FIRE…`), pushback tug (`PUSH…`, `TUG…`), van (`DEICER…`, `SQUID…`, `SQB…`) or else a car by its callsign, and a ground station (`GRND`) is an 11.8 m radio mast; these models (catalog category `other`) are drawn at their built size, and such a contact with no altitude stands on the ground instead of at the airborne default (`src/layers/flights/records.js`). `classifyAircraft` still picks the 2D icons. The whole set loads once, up front, when a layer that draws it starts (`loadFleetModelSet`, which logs `[Fleet] N/122 models (X MB) loaded in N ms`); models are built from those bytes, never fetched mid-session, and an aircraft stays its billboard until the set has loaded. Textured civilians carry a HEAVY tint, not a light one: `MODEL_COLOR_BLEND_AMOUNT` is `0.94` in BOTH layers under Cesium's `ColorBlendMode.MIX`, so the class colour supplies 94% of the surface and the model's own vertex colour ~6%; military models are flat amber, or emergency red. IR boost raises the blend to a full `1.0`. Under NVG/FLIR (map preset or Cockpit vision) models render unlit flat-white at full alpha and scene fog is disabled (fog otherwise blacks out distant models with the globe hidden); state restores on exit.
- Commercial and military aircraft use the same high-level FLEET model regime: 2D billboards when zoomed out, optional glTF models when closer, controlled by the DISPLAY rail `3D` toggle and `Proximity` / `All` modes. Every model is one of the project's procedural fleet models (`public/models/fleet/`, MIT, 122 GLBs, about 2 MB in all; `public/models/README.md`), chosen per aircraft by `aircraftModelSpec` (`src/data/fleetModels.js`): the first of its ICAO type's callsign rules in `types.json` (`aircraft_callsign`) its callsign matches, else its type's entry, else the model of its `classifyAircraft` class for a type the map lacks, else its ADS-B emitter category's, else the default airliner. It is drawn LIFE SIZE: the type's published length over the model's built length (`src/layers/flights/modelSpec.js`, shared by Flights, Military Flights and Local ADS-B), and a grounded aircraft stands the model's scaled clearance above the ground. An airport ground vehicle (feed type `ZVEH`) is a fire truck (`FIRE…`), pushback tug (`PUSH…`, `TUG…`), van (`DEICER…`, `SQUID…`, `SQB…`) or else a car by its callsign, and a ground station (`GRND`) is an 11.8 m radio mast; these models (catalog category `other`) are drawn at their built size, and such a contact with no altitude stands on the ground instead of at the airborne default (`src/layers/flights/records.js`). `classifyAircraft` still picks the 2D icons. The whole set loads once, up front, when a layer that draws it starts (`loadFleetModelSet`, which logs `[Fleet] N/122 models (X MB) loaded in N ms`); models are built from those bytes, never fetched mid-session, and an aircraft stays its billboard until the set has loaded. Every aircraft model draws its mesh's own colours (`aircraftModelBlend`, `src/data/aircraftModelLighting.js`): Cesium's `ColorBlendMode.HIGHLIGHT` with white, in Flights, Military Flights and Local ADS-B alike; the tracked colour (cyan, or amber in Military Flights) and the emergency red multiply into the mesh, so its paint still shows. IR boost draws them flat white (`MIX` at `1.0`). Under NVG/FLIR (map preset or Cockpit vision) models render unlit flat-white at full alpha and scene fog is disabled (fog otherwise blacks out distant models with the globe hidden); state restores on exit.
- Vessels, Transit and Rail draw fleet models under the same DISPLAY rail `3D` toggle, at the same altitude ceiling, caps and add/keep radii as the aircraft (`FLEET_MODEL_POLICY`, `src/layers/flights/policy.js`; `createFleetModelLayer`, `src/data/fleetModelInstances.js`). A vessel's model follows its AIS ship type and is drawn at its reported length and beam (AIS static dimensions A+B and C+D, now carried on vessel rows as `length` and `beam`), standing at the sea surface, heading by true heading then course. A transit vehicle's model follows its layer mode (bus, tram, subway, rail or ferry; a vehicle of `unknown` mode keeps its icon), a train's its rail category and mode (rail, metro or tram). Neither the transit nor the rail sources carry a GTFS `route_type` or a finer mode (monorail, gondola, funicular, cable car), so the set's models for those and their `transit.elevation` lifts are not reached by any live source today. Both stand on the road or rail top they are drawn on, without the 1.5 m `GROUND_FLOOR_LIFT_M` their icons carry, and are painted in their mode or category colour where the model's `_TINT` attribute marks it. A vessel model is clipped at its waterline (one clipping plane per model, at the model origin's horizontal plane), so its hull below the water never shows through a plain globe, which Cesium does not depth-test models against. Under the NVG/FLIR styles (the Flights `irBoost` parameter) these models render unlit flat white, as aircraft models do. Each vehicle's icon stays the visual until its model is loaded, ready and placed; a model is picked as its icon is, and a vehicle drawn as its model keeps its selection card and its place in the detection overlay.
- **Aircraft models skip Cesium's dynamic environment map (2026-10-24).** Flights, Military Flights and Local ADS-B pass `environmentMapOptions: AIRCRAFT_ENVIRONMENT_MAP_OPTIONS` (`{ enabled: false }`, `src/data/aircraftModelLighting.js`) to every drawn model. Otherwise each `Model` renders the sky around it and reads it back from the GPU (`readPixels`) when it is first placed, and again whenever it moves more than 1 km, the scene's atmosphere settings change, or (under sunlight dynamic lighting) an hour of scene time passes (`DynamicEnvironmentMapManager`, Cesium 1.138). With the class colour supplying 94% of the surface, the models are lit from Cesium's default spherical harmonics instead, with no specular environment map, so the sky's model-lighting cube map no longer reaches aircraft. `src/data/aircraftModelLighting.test.mjs` pins the option at every drawn model call; every model is built from the fleet set loaded up front, so there is no separate preload.
- **Aircraft models skip Cesium's dynamic environment map (2026-10-24).** Flights, Military Flights and Local ADS-B pass `environmentMapOptions: AIRCRAFT_ENVIRONMENT_MAP_OPTIONS` (`{ enabled: false }`, `src/data/aircraftModelLighting.js`) to every drawn model. Otherwise each `Model` renders the sky around it and reads it back from the GPU (`readPixels`) when it is first placed, and again whenever it moves more than 1 km, the scene's atmosphere settings change, or (under sunlight dynamic lighting) an hour of scene time passes (`DynamicEnvironmentMapManager`, Cesium 1.138). The models are lit from Cesium's default spherical harmonics instead, with no specular environment map, so the sky's model-lighting cube map no longer reaches aircraft. `src/data/aircraftModelLighting.test.mjs` pins the option at every drawn model call; every model is built from the fleet set loaded up front, so there is no separate preload.
- The DISPLAY-rail `3D` toggle is the user-facing activation path for both aircraft layers. Their small approved GLBs build their render resources without Cesium's frame-spread job queue, preventing continuous Photorealistic 3D Tiles streaming from starving model readiness; model caps, tracking, camera, and fallback billboards are unchanged.
- **The `3D` toggle DEFAULTS ON in `proximity` on a first run.** Proximity is itself the budget — models appear only below `MODEL_ALT_CEIL_M` and only for the nearest `MODEL_MAX` in view — so the default costs nothing at globe scale, and `all` remains a deliberate opt-in. A fresh boot runs NO layer-state restoration (`LayerStateCoordinator.start()` returns early with neither a share payload nor stored state), so four independent initializers decide what a first-run operator sees and must agree: `booleanOption('models3d', 'e', true)` in `src/data/layerState.js`, `_models3dEnabled = true` in BOTH flight layers, `this._models3dEnabled = true` in `src/ui/aircraftDisplay.js`, and the `active` / `visible` classes on `#models3d-toggle` / `#models3d-mode-row` in `index.html`. All four are pinned together in `src/data/layerState.test.mjs`. Explicit state still wins: because the codec omits default-valued options, `models3d: false` is now what travels in a link (`lo=…f.e.0`) and restores OFF at both aircraft layers. **Consequence for returning users:** a stored `gev:layer-state:v2` blob is a FULL options snapshot, so a session that wrote one before this change carries `models3d:false` and keeps 3D off until the operator flips it (or clears the key) — the durable snapshot is treated as the recipient's own state, by design.
- **Consequence of the flip on the recorded tracked/fleet inversion:** the 150–800 km band where surrounding contacts draw as models while the SELECTED one is still a glyph is now what an operator sees WITHOUT arming anything. The inversion itself is unchanged and still deliberate (see `src/data/trackedModelRegime.js`); only its reachability changed.
+2 -1
View File
@@ -458,7 +458,8 @@ test('tint priorities cover normal, priority, emergency, tracking and Cockpit do
);
flightState._trackedIcao = null;
flightState.records.data.set('civil', { emergency: null });
assert.equal(rendering._modelColor('civil'), normal);
// An ordinary model draws its mesh's own colours, whatever the billboard's.
assert.equal(rendering._modelColor('civil'), Cesium.Color.WHITE);
}
});
+19 -5
View File
@@ -1,3 +1,5 @@
import * as Cesium from 'cesium';
/**
* Lighting options shared by every aircraft 3D model (Flights, Military
* Flights, Local ADS-B).
@@ -6,12 +8,24 @@
* the model and reads the result back from the GPU (`readPixels`) to light it,
* when the model is first placed and again when it moves more than 1 km, when
* the scene's atmosphere settings change, or every scene hour under sunlight
* lighting. Aircraft models take 94% of their surface from the class colour
* (`colorBlendAmount`), so that lighting contributes little; without it they
* are lit from Cesium's default spherical harmonics. Measured 2026-10-24
* (Intel Arc, Google 3D, ~150 models in the scene): GPU per frame at rest
* 14.3-14.8 -> 12.1-12.5 ms.
* lighting. Without it they are lit from Cesium's default spherical
* harmonics. Measured 2026-10-24 (Intel Arc, Google 3D, ~150 models in the
* scene): GPU per frame at rest 14.3-14.8 -> 12.1-12.5 ms.
*/
export const AIRCRAFT_ENVIRONMENT_MAP_OPTIONS = Object.freeze({
enabled: false,
});
/**
* How an aircraft model's colour meets its mesh. HIGHLIGHT multiplies, so
* white draws the mesh's own colours as built, and the tracked or emergency
* colour tints the mesh without hiding its paint. The IR boost draws the
* model flat white instead (MIX at 1, with its unlit shader).
* @param {boolean} irBoost
* @returns {{colorBlendMode: Cesium.ColorBlendMode, colorBlendAmount: number}}
*/
export function aircraftModelBlend(irBoost) {
return irBoost
? { colorBlendMode: Cesium.ColorBlendMode.MIX, colorBlendAmount: 1 }
: { colorBlendMode: Cesium.ColorBlendMode.HIGHLIGHT, colorBlendAmount: 0 };
}
+1 -1
View File
@@ -243,7 +243,7 @@ export function applyAircraftBillboardTreatment({
/**
* Apply layer-owned hue and composed aircraft alpha to an ambient glTF model without
* disturbing Cesium's existing MIX/colorBlendAmount presentation semantics.
* changing the model's colour blend mode or amount.
* @param {object} input
* @param {object} input.model
* @param {{withAlpha:(alpha:number)=>object}} input.baseColor
+1 -3
View File
@@ -291,7 +291,7 @@ test('tracking layers write gevLabelModel and expose only their cached display p
assert.equal(sources['satellites.js'].includes('_trackedEntity.label.text'), false);
});
test('civilian and military trail heads use the lower-centre model anchor and weak-texture tint', async () => {
test('civilian and military trail heads use the lower-centre model anchor', async () => {
const files = await Promise.all(['flights.js', 'militaryFlights.js'].map(async (name) => (
[name, readLayerSource(new URL(`./${name}`, import.meta.url))]
)));
@@ -300,7 +300,5 @@ test('civilian and military trail heads use the lower-centre model anchor and we
(/const\s*head\s*=\s*(?:parts\.motion\.)?_trackedTrailCached\(\s*,?\s*\)\s*\|\|\s*(?:parts\.motion\.)?_trackedDisplayPosition\(\s*(?:flightState\.)?_trackedIcao,?\s*\);/).test(source),
`${name} trail head uses the dedicated lower-centre model anchor`,
);
assert.ok(source.includes('const MODEL_COLOR_BLEND_AMOUNT = 0.94;'),
`${name} keeps diffuse texture contribution weak through code-side MIX`);
}
});
-3
View File
@@ -4,7 +4,6 @@ import {
aircraftModelSpec,
fleetModelSet,
} from '../../data/fleetModels.js';
import { MODEL_COLOR_BLEND_AMOUNT } from './policy.js';
/**
* What an aircraft draws with before the fleet model set has loaded: no
@@ -22,7 +21,6 @@ export const UNLOADED_AIRCRAFT_MODEL_SPEC = Object.freeze({
bellyM: 0,
elevationM: 0,
tinted: false,
blendAmount: MODEL_COLOR_BLEND_AMOUNT,
visualCenterNative: Object.freeze([0, 0, 0]),
trailAnchorNative: Object.freeze([0, 0, 0]),
key: 'unloaded',
@@ -58,7 +56,6 @@ export function aircraftLayerModelSpec(aircraft, set = fleetModelSet()) {
layerSpec = Object.freeze({
...spec,
bellyM: spec.clearanceM,
blendAmount: MODEL_COLOR_BLEND_AMOUNT,
});
cache.set(key, layerSpec);
}
-4
View File
@@ -85,10 +85,6 @@ export const MODEL_ALL_KEEP_M = 450000;
export const MODEL_HEADING_OFFSET_DEG = 180;
// every fleet model is nose -X: opposite Cesium heading 0
// Owner launch-polish direction: models should read as clean light silhouettes,
// with only a weak diffuse contribution from the models' own colours.
export const MODEL_COLOR_BLEND_AMOUNT = 0.94;
export const CYAN_TRANSPARENT = Cesium.Color.CYAN.withAlpha(0);
+9 -11
View File
@@ -45,7 +45,10 @@ import {
STALE_ALPHA,
displayDelayMs,
} from './policy.js';
import { AIRCRAFT_ENVIRONMENT_MAP_OPTIONS } from '../../data/aircraftModelLighting.js';
import {
AIRCRAFT_ENVIRONMENT_MAP_OPTIONS,
aircraftModelBlend,
} from '../../data/aircraftModelLighting.js';
export function createRendering({
flightState,
@@ -192,11 +195,12 @@ export function createRendering({
* @returns {void}
*/
/** Model tint, mirroring the billboard color rules. */
/** Model colour: the mesh's own (white), tinted cyan while tracked and red
* while broadcasting an emergency. */
function _modelColor(icao24) {
if (icao24 === flightState._trackedIcao) return Cesium.Color.CYAN;
return _fleetBillboardColor(icao24);
return _isEmergency(icao24) ? EMERGENCY_TINT : Cesium.Color.WHITE;
}
/** The FLEET's 3D-model regime: models3d enabled AND the camera zoomed in past the altitude
@@ -526,10 +530,7 @@ export function createRendering({
minimumPixelSize: MODEL_MIN_PX,
scale: spec.scale,
color: flightState._irBoost ? Cesium.Color.WHITE : _modelColor(icao24),
colorBlendMode: Cesium.ColorBlendMode.MIX,
// Launch presentation keeps the code-side tint dominant for every approved
// model; IR boost removes the remaining diffuse hint with flat UNLIT white.
colorBlendAmount: flightState._irBoost ? 1.0 : spec.blendAmount,
...aircraftModelBlend(flightState._irBoost),
customShader: flightState._irBoost
? flightState._IR_UNLIT_SHADER
: undefined,
@@ -748,10 +749,7 @@ export function createRendering({
minimumPixelSize: TRACKED_MODEL_MIN_PX,
scale: trackedSpec.scale,
color: flightState._irBoost ? Cesium.Color.WHITE : Cesium.Color.CYAN,
colorBlendMode: Cesium.ColorBlendMode.MIX,
// The tracked aircraft uses the same dominant light tint as the fleet;
// IR boost removes the remaining diffuse hint with flat UNLIT white.
colorBlendAmount: flightState._irBoost ? 1.0 : trackedSpec.blendAmount,
...aircraftModelBlend(flightState._irBoost),
customShader: flightState._irBoost
? flightState._IR_UNLIT_SHADER
: undefined,
-1
View File
@@ -700,7 +700,6 @@ export function createLocalAdsbLayer({
if (viewer.scene?.primitives) {
models = createLocalAdsbModels({
viewer,
color,
resolveAsset,
groundSnap: services.groundSnap?.createGroundSnap?.() || null,
...(loadModel ? { loadModel } : {}),
+4 -7
View File
@@ -503,7 +503,7 @@ test('adsbdb type, operator and route reach the card for the selected aircraft o
assert.equal(queries.length, 2, 'each key is asked for once');
});
test('the DISPLAY 3D toggle gives local aircraft magenta class models', async (t) => {
test('the DISPLAY 3D toggle gives local aircraft their fleet models in their mesh colours', async (t) => {
const receiver = fakeReceiver({ mode: 'adsb', connected: true });
const clock = { now: 100_000 };
const loads = [];
@@ -548,13 +548,10 @@ test('the DISPLAY 3D toggle gives local aircraft magenta class models', async (t
assert.equal(model.scale, spec.scale, 'drawn life size');
assert.equal(model.id, 'local-adsb:a0b702');
assert.ok(
Cesium.Color.equals(
model.color,
Cesium.Color.fromCssColorString('#ff4fd8'),
),
Cesium.Color.equals(model.color, Cesium.Color.WHITE),
'the mesh draws its own colours',
);
assert.equal(model.colorBlendMode, Cesium.ColorBlendMode.MIX);
assert.equal(model.colorBlendAmount, 0.94);
assert.equal(model.colorBlendMode, Cesium.ColorBlendMode.HIGHLIGHT);
assert.equal(model.show, true);
const heli = sources[0].entities.getById('local-adsb:a0b702');
assert.equal(
+8 -8
View File
@@ -16,7 +16,10 @@ import {
MODEL_PROX_ADD_M,
MODEL_PROX_KEEP_M,
} from '../flights/policy.js';
import { AIRCRAFT_ENVIRONMENT_MAP_OPTIONS } from '../../data/aircraftModelLighting.js';
import {
AIRCRAFT_ENVIRONMENT_MAP_OPTIONS,
aircraftModelBlend,
} from '../../data/aircraftModelLighting.js';
const specOf = (marker) =>
aircraftLayerModelSpec({
@@ -31,15 +34,14 @@ const specOf = (marker) =>
* Mirrors the public Flights fleet: models only below the altitude ceiling,
* the same proximity/all caps and add/keep radii, the same visible-first
* eligibility (`selectModelEligible`), the same fleet model per type, life
* size (`aircraftLayerModelSpec`) and the same tint path (a MIX colour blend, here
* magenta, as Flights tints military amber). Each model owns its matrix. A
* size (`aircraftLayerModelSpec`) and the same colours: the mesh's own
* (`aircraftModelBlend`). Each model owns its matrix. A
* billboard stays the visual until its model is loaded AND placed; a grounded
* aircraft's model rides the one-shot ground snap plus the class belly
* offset, and without a resolved ground it stays a billboard. Every fleet
* model loads once, up front, when the layer starts (`loadFleetModelSet`).
* @param {object} options
* @param {object} options.viewer Cesium viewer.
* @param {Cesium.Color} options.color Model tint.
* @param {(url: string) => string} options.resolveAsset
* @param {object|null} [options.groundSnap] `createGroundSnap()` instance.
* @param {(options: object) => Promise<object>} [options.loadModel]
@@ -47,7 +49,6 @@ const specOf = (marker) =>
*/
export function createLocalAdsbModels({
viewer,
color,
resolveAsset,
groundSnap = null,
loadModel = (options) => Cesium.Model.fromGltfAsync(options),
@@ -104,9 +105,8 @@ export function createLocalAdsbModels({
environmentMapOptions: AIRCRAFT_ENVIRONMENT_MAP_OPTIONS,
minimumPixelSize: MODEL_MIN_PX,
scale: spec.scale,
color,
colorBlendMode: Cesium.ColorBlendMode.MIX,
colorBlendAmount: spec.blendAmount,
color: Cesium.Color.WHITE,
...aircraftModelBlend(false),
id,
},
{ resolveAsset, load: loadModel },
-4
View File
@@ -107,10 +107,6 @@ export const MODEL_ALL_KEEP_M = 450000;
export const MODEL_HEADING_OFFSET_DEG = 180;
// every fleet model is nose -X: opposite Cesium heading 0
// Preserve the layer's amber identity while reducing the models' own colours
// to a weak diffuse hint, matching civilian launch presentation.
export const MODEL_COLOR_BLEND_AMOUNT = 0.94;
/** Amber fade target for the tracked billboard once the model takes over (mirrors flights.js CYAN_TRANSPARENT). */
+12 -11
View File
@@ -34,7 +34,6 @@ import {
MODEL_PROX_KEEP_M,
IR_RELOAD_BATCH,
MODEL_MIN_PX,
MODEL_COLOR_BLEND_AMOUNT,
TRACKED_MODEL_MIN_PX,
TRACKED_MODEL_MAX_PX,
FLEET_DR_INTERVAL_MS,
@@ -43,7 +42,10 @@ import {
COURSE_MAX_DPS,
STALE_ALPHA,
} from './policy.js';
import { AIRCRAFT_ENVIRONMENT_MAP_OPTIONS } from '../../data/aircraftModelLighting.js';
import {
AIRCRAFT_ENVIRONMENT_MAP_OPTIONS,
aircraftModelBlend,
} from '../../data/aircraftModelLighting.js';
export function createRendering({
flightState,
@@ -81,7 +83,6 @@ export function createRendering({
const base = aircraftLayerModelSpec(meta);
spec = Object.freeze({
...base,
blendAmount: MODEL_COLOR_BLEND_AMOUNT,
headingOffsetDeg: MODEL_HEADING_OFFSET_DEG,
});
// Not cached before the model set loads: the answer changes once it has.
@@ -152,11 +153,15 @@ export function createRendering({
const _iconKind = (icao24, klass) =>
tr3bIconKind(icao24, klass, { hot: flightState._irBoost });
/** Model tint, mirroring the billboard color rules (amber instead of cyan/white). */
/** Model colour: the mesh's own (white), tinted amber while tracked and red
* while broadcasting an emergency. */
function _modelColor(icao24) {
if (icao24 === flightState._trackedIcao) return TRACKED_ICON_COLOR;
return _fleetBillboardColor(icao24);
const info = flightState.records.data.get(icao24);
return isEmergency(info?.emergency)
? EMERGENCY_ICON_COLOR
: Cesium.Color.WHITE;
}
/** Fleet (untracked) tint: red while the latest poll broadcast an emergency,
@@ -443,9 +448,7 @@ export function createRendering({
minimumPixelSize: MODEL_MIN_PX,
scale: spec.scale,
color: flightState._irBoost ? Cesium.Color.WHITE : _modelColor(icao24),
colorBlendMode: Cesium.ColorBlendMode.MIX,
// near self-illuminated tint so planes read uniform near AND far; IR boost → flat UNLIT white (hot)
colorBlendAmount: flightState._irBoost ? 1.0 : MODEL_COLOR_BLEND_AMOUNT,
...aircraftModelBlend(flightState._irBoost),
customShader: flightState._irBoost
? flightState._IR_UNLIT_SHADER
: undefined,
@@ -662,9 +665,7 @@ export function createRendering({
minimumPixelSize: TRACKED_MODEL_MIN_PX,
scale: trackedSpec.scale,
color: flightState._irBoost ? Cesium.Color.WHITE : TRACKED_ICON_COLOR,
colorBlendMode: Cesium.ColorBlendMode.MIX,
// near self-illuminated tint so planes read uniform near AND far; IR boost → flat UNLIT white (hot)
colorBlendAmount: flightState._irBoost ? 1.0 : MODEL_COLOR_BLEND_AMOUNT,
...aircraftModelBlend(flightState._irBoost),
customShader: flightState._irBoost
? flightState._IR_UNLIT_SHADER
: undefined,