mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-14 23:32:20 +00:00
Draw aircraft models in their own mesh colours
Every aircraft model (Flights, Military Flights, Local ADS-B) mixed 94% of a flat class colour over its mesh, so airliners drew as white silhouettes and military ones as amber. Models now use HIGHLIGHT: white leaves the mesh's own colours as built, and the tracked and emergency colours tint the mesh without hiding its paint. The IR boost still draws them flat white.
This commit is contained in:
1 parent
3934be9ff9
commit
8cb096fc21
11 files changed
+54
-57
No files matched your search
@@ -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);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -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 };
|
||||
}
|
||||
@@ -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`);
|
||||
}
|
||||
});
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -700,7 +700,6 @@ export function createLocalAdsbLayer({
|
||||
if (viewer.scene?.primitives) {
|
||||
models = createLocalAdsbModels({
|
||||
viewer,
|
||||
color,
|
||||
resolveAsset,
|
||||
groundSnap: services.groundSnap?.createGroundSnap?.() || null,
|
||||
...(loadModel ? { loadModel } : {}),
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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). */
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in new issue
Block a user