mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-05 10:53:59 +00:00
Release God's Eye View as open source
This commit is contained in:
1 parent
a5aaea7e16
commit
880a672b5e
433 files changed
+219893
-23
No files matched your search
@@ -0,0 +1,238 @@
|
||||
/**
|
||||
* Limb-relative recession for distant aircraft billboards.
|
||||
*
|
||||
* This multiplies the billboard's ordinary scale, which Cesium then composes
|
||||
* with the locked NearFarScalar. It never edits that scalar, culls a contact,
|
||||
* or allows alpha to reach zero.
|
||||
*/
|
||||
|
||||
export const DEFAULT_AIRCRAFT_RECESSION_PARAMS = Object.freeze({
|
||||
startLimbRatio: 0.5,
|
||||
scaleFloor: 0.45,
|
||||
alphaFloor: 0.35,
|
||||
combinedAlphaFloor: 0.20,
|
||||
globeViewBlendStartM: 3_500_000,
|
||||
globeViewBlendEndM: 4_500_000,
|
||||
earthRadiusM: 6_378_137,
|
||||
writeEpsilon: 0.005,
|
||||
});
|
||||
|
||||
let _params = { ...DEFAULT_AIRCRAFT_RECESSION_PARAMS };
|
||||
const _scratchFactors = { scale: 1, alpha: 1, limbRatio: null };
|
||||
const _treatmentResult = {
|
||||
scale: 1,
|
||||
alpha: 1,
|
||||
scaleWrites: 0,
|
||||
alphaWrites: 0,
|
||||
factors: _scratchFactors,
|
||||
};
|
||||
|
||||
function clamp(value, min, max) {
|
||||
return Math.min(max, Math.max(min, value));
|
||||
}
|
||||
|
||||
function resolvedParams(overrides) {
|
||||
if (!overrides || overrides === _params) return _params;
|
||||
return { ..._params, ...overrides };
|
||||
}
|
||||
|
||||
function aircraftRecessionFactorsResolved(cameraDistanceM, cameraHeightM, tuning, result) {
|
||||
if (!Number.isFinite(cameraDistanceM) || cameraDistanceM < 0
|
||||
|| !Number.isFinite(cameraHeightM) || cameraHeightM <= 0
|
||||
|| cameraHeightM >= tuning.globeViewBlendEndM) {
|
||||
result.scale = 1;
|
||||
result.alpha = 1;
|
||||
result.limbRatio = null;
|
||||
return result;
|
||||
}
|
||||
const limbDistance = cameraLimbDistanceM(cameraHeightM, tuning.earthRadiusM);
|
||||
if (!Number.isFinite(limbDistance) || limbDistance <= 0) {
|
||||
result.scale = 1;
|
||||
result.alpha = 1;
|
||||
result.limbRatio = null;
|
||||
return result;
|
||||
}
|
||||
const limbRatio = cameraDistanceM / limbDistance;
|
||||
if (limbRatio <= tuning.startLimbRatio) {
|
||||
result.scale = 1;
|
||||
result.alpha = 1;
|
||||
result.limbRatio = limbRatio;
|
||||
return result;
|
||||
}
|
||||
|
||||
const rawT = (limbRatio - tuning.startLimbRatio) / (1 - tuning.startLimbRatio);
|
||||
const t = clamp(rawT, 0, 1);
|
||||
const limbEase = t * t * (3 - 2 * t); // smoothstep: zero slope at both band edges
|
||||
const globeRawT = (cameraHeightM - tuning.globeViewBlendStartM)
|
||||
/ (tuning.globeViewBlendEndM - tuning.globeViewBlendStartM);
|
||||
const globeT = clamp(globeRawT, 0, 1);
|
||||
const globeEase = globeT * globeT * (3 - 2 * globeT);
|
||||
const strength = 1 - globeEase;
|
||||
result.scale = 1 + (tuning.scaleFloor - 1) * limbEase * strength;
|
||||
result.alpha = 1 + (tuning.alphaFloor - 1) * limbEase * strength;
|
||||
result.limbRatio = limbRatio;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Patch runtime taper tuning for evidence capture/A-B work.
|
||||
* @param {Partial<typeof DEFAULT_AIRCRAFT_RECESSION_PARAMS>} [patch]
|
||||
* @returns {typeof DEFAULT_AIRCRAFT_RECESSION_PARAMS}
|
||||
*/
|
||||
export function setAircraftRecessionParams(patch = {}) {
|
||||
const next = { ..._params };
|
||||
if (Number.isFinite(patch.startLimbRatio)) {
|
||||
next.startLimbRatio = clamp(patch.startLimbRatio, 0, 0.99);
|
||||
}
|
||||
if (Number.isFinite(patch.scaleFloor)) next.scaleFloor = clamp(patch.scaleFloor, 0.05, 1);
|
||||
if (Number.isFinite(patch.alphaFloor)) next.alphaFloor = clamp(patch.alphaFloor, 0.05, 1);
|
||||
if (Number.isFinite(patch.combinedAlphaFloor)) {
|
||||
next.combinedAlphaFloor = clamp(patch.combinedAlphaFloor, 0.05, 1);
|
||||
}
|
||||
if (Number.isFinite(patch.globeViewBlendStartM)) {
|
||||
next.globeViewBlendStartM = Math.max(1, patch.globeViewBlendStartM);
|
||||
}
|
||||
if (Number.isFinite(patch.globeViewBlendEndM)) {
|
||||
next.globeViewBlendEndM = Math.max(
|
||||
next.globeViewBlendStartM + 1,
|
||||
patch.globeViewBlendEndM,
|
||||
);
|
||||
}
|
||||
// Backward-compatible evidence override: the former hard threshold now
|
||||
// names the identity end of the blend band.
|
||||
if (Number.isFinite(patch.globeViewHeightM)) {
|
||||
next.globeViewBlendEndM = Math.max(
|
||||
next.globeViewBlendStartM + 1,
|
||||
patch.globeViewHeightM,
|
||||
);
|
||||
}
|
||||
if (next.globeViewBlendStartM >= next.globeViewBlendEndM) {
|
||||
next.globeViewBlendStartM = Math.max(1, next.globeViewBlendEndM - 1);
|
||||
}
|
||||
if (Number.isFinite(patch.earthRadiusM)) next.earthRadiusM = Math.max(1, patch.earthRadiusM);
|
||||
if (Number.isFinite(patch.writeEpsilon)) next.writeEpsilon = Math.max(0, patch.writeEpsilon);
|
||||
_params = next;
|
||||
return { ..._params };
|
||||
}
|
||||
|
||||
/** @returns {typeof DEFAULT_AIRCRAFT_RECESSION_PARAMS} Current tuning. */
|
||||
export function getAircraftRecessionParams() {
|
||||
return _params;
|
||||
}
|
||||
|
||||
/** Restore production defaults for deterministic QA. */
|
||||
export function resetAircraftRecessionParams() {
|
||||
_params = { ...DEFAULT_AIRCRAFT_RECESSION_PARAMS };
|
||||
}
|
||||
|
||||
/**
|
||||
* Straight-line tangent distance from a camera above a spherical Earth to its
|
||||
* geometric limb. The WGS84 semi-major radius is sufficient for a visual
|
||||
* taper; the occluder remains the authoritative far-side visibility test.
|
||||
* @param {number} cameraHeightM
|
||||
* @param {number} [earthRadiusM]
|
||||
* @returns {number}
|
||||
*/
|
||||
export function cameraLimbDistanceM(
|
||||
cameraHeightM,
|
||||
earthRadiusM = DEFAULT_AIRCRAFT_RECESSION_PARAMS.earthRadiusM,
|
||||
) {
|
||||
if (!Number.isFinite(cameraHeightM) || cameraHeightM <= 0) return Number.NaN;
|
||||
return Math.sqrt(cameraHeightM * (2 * earthRadiusM + cameraHeightM));
|
||||
}
|
||||
|
||||
/**
|
||||
* Pure recession factors for one aircraft.
|
||||
* @param {object} input
|
||||
* @param {number} input.cameraDistanceM Sprite-to-camera distance.
|
||||
* @param {number} input.cameraHeightM Camera height above the ellipsoid.
|
||||
* @param {Partial<typeof DEFAULT_AIRCRAFT_RECESSION_PARAMS>} [params]
|
||||
* @returns {{scale:number,alpha:number,limbRatio:number|null}}
|
||||
*/
|
||||
export function aircraftRecessionFactors({ cameraDistanceM, cameraHeightM }, params) {
|
||||
const tuning = resolvedParams(params);
|
||||
return aircraftRecessionFactorsResolved(
|
||||
cameraDistanceM,
|
||||
cameraHeightM,
|
||||
tuning,
|
||||
{ scale: 1, alpha: 1, limbRatio: null },
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Production wire helper: compose base scale/alpha, focus emphasis, and limb
|
||||
* recession into one deadband-gated billboard write site.
|
||||
* @param {object} input
|
||||
* @param {object} input.billboard
|
||||
* @param {number} input.baseScale
|
||||
* @param {number} input.baseAlpha
|
||||
* @param {{withAlpha:(alpha:number)=>object}} input.baseColor
|
||||
* @param {number} input.focusFactor
|
||||
* @param {number} input.cameraDistanceM
|
||||
* @param {number} input.cameraHeightM
|
||||
* @param {Partial<typeof DEFAULT_AIRCRAFT_RECESSION_PARAMS>} [input.params]
|
||||
*
|
||||
* IMPORTANT: the returned object (including `factors`) is a module-owned
|
||||
* mutable singleton. Consume every needed property before the next call;
|
||||
* never retain it as a snapshot.
|
||||
* @returns {{scale:number,alpha:number,scaleWrites:number,alphaWrites:number,factors:object}}
|
||||
*/
|
||||
export function applyAircraftBillboardTreatment({
|
||||
billboard,
|
||||
baseScale,
|
||||
baseAlpha,
|
||||
baseColor,
|
||||
focusFactor,
|
||||
cameraDistanceM,
|
||||
cameraHeightM,
|
||||
params,
|
||||
}) {
|
||||
const tuning = resolvedParams(params);
|
||||
const factors = aircraftRecessionFactorsResolved(
|
||||
cameraDistanceM,
|
||||
cameraHeightM,
|
||||
tuning,
|
||||
_scratchFactors,
|
||||
);
|
||||
const scale = baseScale * factors.scale;
|
||||
const composedTreatment = Math.max(
|
||||
tuning.combinedAlphaFloor,
|
||||
clamp(focusFactor, 0, 1) * factors.alpha,
|
||||
);
|
||||
const alpha = baseAlpha * composedTreatment;
|
||||
let scaleWrites = 0;
|
||||
let alphaWrites = 0;
|
||||
if (!Number.isFinite(billboard.scale) || Math.abs(billboard.scale - scale) > tuning.writeEpsilon) {
|
||||
billboard.scale = scale;
|
||||
scaleWrites = 1;
|
||||
}
|
||||
if (!Number.isFinite(billboard.color?.alpha)
|
||||
|| Math.abs(billboard.color.alpha - alpha) > tuning.writeEpsilon) {
|
||||
billboard.color = baseColor.withAlpha(alpha);
|
||||
alphaWrites = 1;
|
||||
}
|
||||
_treatmentResult.scale = scale;
|
||||
_treatmentResult.alpha = alpha;
|
||||
_treatmentResult.scaleWrites = scaleWrites;
|
||||
_treatmentResult.alphaWrites = alphaWrites;
|
||||
return _treatmentResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the already-composed aircraft alpha to an ambient glTF model without
|
||||
* disturbing Cesium's existing MIX/colorBlendAmount presentation semantics.
|
||||
* @param {object} input
|
||||
* @param {object} input.model
|
||||
* @param {{withAlpha:(alpha:number)=>object}} input.baseColor
|
||||
* @param {number} input.alpha
|
||||
* @param {Partial<typeof DEFAULT_AIRCRAFT_RECESSION_PARAMS>} [input.params]
|
||||
* @returns {number} One when a color write occurred, otherwise zero.
|
||||
*/
|
||||
export function applyAircraftModelTreatment({ model, baseColor, alpha, params }) {
|
||||
const tuning = resolvedParams(params);
|
||||
if (!model || !baseColor || !Number.isFinite(alpha)) return 0;
|
||||
if (Number.isFinite(model.color?.alpha)
|
||||
&& Math.abs(model.color.alpha - alpha) <= tuning.writeEpsilon) return 0;
|
||||
model.color = baseColor.withAlpha(alpha);
|
||||
return 1;
|
||||
}
|
||||
Reference in new issue
Block a user