|
|
|
|
@@ -28,6 +28,7 @@ let lockedRingSprite = null;
|
|
|
|
|
let lockedDotSprite = null;
|
|
|
|
|
let lockedHaloMesh = null;
|
|
|
|
|
let lockedGroundFootprintMesh = null;
|
|
|
|
|
let lockedGroundFootprintFillMesh = null;
|
|
|
|
|
let lockedIridiumFootprintMesh = null;
|
|
|
|
|
let predictedOrbitLine = null;
|
|
|
|
|
let relatedSatelliteSprites = [];
|
|
|
|
|
@@ -159,6 +160,9 @@ const GROUND_FOOTPRINT_GAP_CENTER_MAX_RATIO = 0.82;
|
|
|
|
|
const GROUND_FOOTPRINT_GAP_WIDTH_CENTER_KM = 60;
|
|
|
|
|
const GROUND_FOOTPRINT_GAP_WIDTH_EDGE_KM = 120;
|
|
|
|
|
const GROUND_FOOTPRINT_GAP_LENGTH_RATIO = 1.08;
|
|
|
|
|
const GROUND_FOOTPRINT_REBUILD_DISTANCE = 0.001;
|
|
|
|
|
const GROUND_FOOTPRINT_REBUILD_DISTANCE_SQ =
|
|
|
|
|
GROUND_FOOTPRINT_REBUILD_DISTANCE * GROUND_FOOTPRINT_REBUILD_DISTANCE;
|
|
|
|
|
|
|
|
|
|
const scratchWorldSatellitePosition = new THREE.Vector3();
|
|
|
|
|
const scratchToCamera = new THREE.Vector3();
|
|
|
|
|
@@ -168,7 +172,9 @@ const scratchFootprintLateral = new THREE.Vector3();
|
|
|
|
|
const scratchFootprintReference = new THREE.Vector3();
|
|
|
|
|
const scratchFootprintVelocity = new THREE.Vector3();
|
|
|
|
|
const scratchFootprintTangent = new THREE.Vector3();
|
|
|
|
|
const scratchLastGroundFootprintPosition = new THREE.Vector3();
|
|
|
|
|
const satelliteSunDirection = new THREE.Vector3(1, 0.2, 0.4).normalize();
|
|
|
|
|
let hasGroundFootprintGeometry = false;
|
|
|
|
|
|
|
|
|
|
export let breathingPhase = 0;
|
|
|
|
|
|
|
|
|
|
@@ -1335,13 +1341,10 @@ export function setSatelliteCamera(camera) {
|
|
|
|
|
export function setSatelliteSunDirection(direction) {
|
|
|
|
|
if (!direction) return;
|
|
|
|
|
satelliteSunDirection.copy(direction).normalize();
|
|
|
|
|
if (lockedGroundFootprintMesh) {
|
|
|
|
|
const fillMesh = lockedGroundFootprintMesh.getObjectByName("footprint-fill");
|
|
|
|
|
if (fillMesh?.material?.uniforms?.uSunDirectionWorld) {
|
|
|
|
|
fillMesh.material.uniforms.uSunDirectionWorld.value.copy(
|
|
|
|
|
satelliteSunDirection,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
if (lockedGroundFootprintFillMesh?.material?.uniforms?.uSunDirectionWorld) {
|
|
|
|
|
lockedGroundFootprintFillMesh.material.uniforms.uSunDirectionWorld.value.copy(
|
|
|
|
|
satelliteSunDirection,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -1586,14 +1589,12 @@ function createGroundFootprintMaterial() {
|
|
|
|
|
},
|
|
|
|
|
vertexShader: `
|
|
|
|
|
varying vec3 vWorldPosition;
|
|
|
|
|
varying vec3 vWorldNormal;
|
|
|
|
|
varying vec2 vUv;
|
|
|
|
|
|
|
|
|
|
void main() {
|
|
|
|
|
vUv = uv;
|
|
|
|
|
vec4 worldPosition = modelMatrix * vec4(position, 1.0);
|
|
|
|
|
vWorldPosition = worldPosition.xyz;
|
|
|
|
|
vWorldNormal = normalize(mat3(modelMatrix) * normal);
|
|
|
|
|
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
|
|
|
|
|
}
|
|
|
|
|
`,
|
|
|
|
|
@@ -1617,7 +1618,6 @@ function createGroundFootprintMaterial() {
|
|
|
|
|
uniform vec3 uSunDirectionWorld;
|
|
|
|
|
uniform float uDayVisibilityBoost;
|
|
|
|
|
varying vec3 vWorldPosition;
|
|
|
|
|
varying vec3 vWorldNormal;
|
|
|
|
|
varying vec2 vUv;
|
|
|
|
|
|
|
|
|
|
float bowtieHalfWidth(float xEast) {
|
|
|
|
|
@@ -1723,6 +1723,8 @@ function clearLockedSatelliteStyleVisuals() {
|
|
|
|
|
if (lockedGroundFootprintMesh) {
|
|
|
|
|
disposeObjectTree(lockedGroundFootprintMesh);
|
|
|
|
|
lockedGroundFootprintMesh = null;
|
|
|
|
|
lockedGroundFootprintFillMesh = null;
|
|
|
|
|
hasGroundFootprintGeometry = false;
|
|
|
|
|
}
|
|
|
|
|
if (lockedIridiumFootprintMesh) {
|
|
|
|
|
disposeIridiumFootprintAdapter(lockedIridiumFootprintMesh, earthObjRef);
|
|
|
|
|
@@ -1933,28 +1935,6 @@ function buildGroundFootprintGeometry(position) {
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function toEastNorth(alongKm, crossKm) {
|
|
|
|
|
const offset = alongTrack
|
|
|
|
|
.clone()
|
|
|
|
|
.multiplyScalar(alongKm)
|
|
|
|
|
.addScaledVector(crossTrack, crossKm);
|
|
|
|
|
return {
|
|
|
|
|
xEast: offset.dot(east),
|
|
|
|
|
yNorth: offset.dot(north),
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function fromEastNorth(xEast, yNorth) {
|
|
|
|
|
const offset = east
|
|
|
|
|
.clone()
|
|
|
|
|
.multiplyScalar(xEast)
|
|
|
|
|
.addScaledVector(north, yNorth);
|
|
|
|
|
return {
|
|
|
|
|
alongKm: offset.dot(alongTrack),
|
|
|
|
|
crossKm: offset.dot(crossTrack),
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function bowtieHalfWidth(xEast) {
|
|
|
|
|
const t = THREE.MathUtils.clamp(
|
|
|
|
|
Math.abs(xEast) / Math.max(exclusionLengthKm, 1),
|
|
|
|
|
@@ -1969,6 +1949,7 @@ function buildGroundFootprintGeometry(position) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const vertices = [];
|
|
|
|
|
const uvs = [];
|
|
|
|
|
const indices = [];
|
|
|
|
|
const indexMap = [];
|
|
|
|
|
|
|
|
|
|
@@ -2000,6 +1981,10 @@ function buildGroundFootprintGeometry(position) {
|
|
|
|
|
);
|
|
|
|
|
row.push(vertices.length / 3);
|
|
|
|
|
vertices.push(point.x, point.y, point.z);
|
|
|
|
|
uvs.push(
|
|
|
|
|
THREE.MathUtils.mapLinear(alongKm, -majorKm, majorKm, 0, 1),
|
|
|
|
|
THREE.MathUtils.mapLinear(crossKm, -minorKm, minorKm, 0, 1),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
indexMap.push(row);
|
|
|
|
|
}
|
|
|
|
|
@@ -2021,29 +2006,8 @@ function buildGroundFootprintGeometry(position) {
|
|
|
|
|
"position",
|
|
|
|
|
new THREE.Float32BufferAttribute(vertices, 3),
|
|
|
|
|
);
|
|
|
|
|
const uvs = [];
|
|
|
|
|
for (let iy = 0; iy <= GROUND_FOOTPRINT_GRID_Y; iy += 1) {
|
|
|
|
|
const crossKm = THREE.MathUtils.lerp(
|
|
|
|
|
-minorKm,
|
|
|
|
|
minorKm,
|
|
|
|
|
iy / GROUND_FOOTPRINT_GRID_Y,
|
|
|
|
|
);
|
|
|
|
|
for (let ix = 0; ix <= GROUND_FOOTPRINT_GRID_X; ix += 1) {
|
|
|
|
|
const alongKm = THREE.MathUtils.lerp(
|
|
|
|
|
-majorKm,
|
|
|
|
|
majorKm,
|
|
|
|
|
ix / GROUND_FOOTPRINT_GRID_X,
|
|
|
|
|
);
|
|
|
|
|
if (!isInsideEllipse(alongKm, crossKm)) continue;
|
|
|
|
|
uvs.push(
|
|
|
|
|
THREE.MathUtils.mapLinear(alongKm, -majorKm, majorKm, 0, 1),
|
|
|
|
|
THREE.MathUtils.mapLinear(crossKm, -minorKm, minorKm, 0, 1),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
fillGeometry.setAttribute("uv", new THREE.Float32BufferAttribute(uvs, 2));
|
|
|
|
|
fillGeometry.setIndex(indices);
|
|
|
|
|
fillGeometry.computeVertexNormals();
|
|
|
|
|
|
|
|
|
|
const basisToEastNorth = {
|
|
|
|
|
eastAlongDot: alongTrack.dot(east),
|
|
|
|
|
@@ -2064,10 +2028,18 @@ function buildGroundFootprintGeometry(position) {
|
|
|
|
|
|
|
|
|
|
function updateGroundFootprintTransform(position) {
|
|
|
|
|
if (!lockedGroundFootprintMesh || !position || !earthObjRef) return;
|
|
|
|
|
if (
|
|
|
|
|
hasGroundFootprintGeometry &&
|
|
|
|
|
scratchLastGroundFootprintPosition.distanceToSquared(position) <=
|
|
|
|
|
GROUND_FOOTPRINT_REBUILD_DISTANCE_SQ
|
|
|
|
|
) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const geometrySet = buildGroundFootprintGeometry(position);
|
|
|
|
|
if (!geometrySet) return;
|
|
|
|
|
|
|
|
|
|
const fillMesh = lockedGroundFootprintMesh.getObjectByName("footprint-fill");
|
|
|
|
|
const fillMesh = lockedGroundFootprintFillMesh;
|
|
|
|
|
|
|
|
|
|
if (fillMesh?.geometry) fillMesh.geometry.dispose();
|
|
|
|
|
|
|
|
|
|
@@ -2089,6 +2061,8 @@ function updateGroundFootprintTransform(position) {
|
|
|
|
|
fillMesh.material.uniforms.uNorthCrossDot.value =
|
|
|
|
|
geometrySet.basisToEastNorth.northCrossDot;
|
|
|
|
|
}
|
|
|
|
|
scratchLastGroundFootprintPosition.copy(position);
|
|
|
|
|
hasGroundFootprintGeometry = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -2130,6 +2104,8 @@ function showGroundFootprintStyle(position) {
|
|
|
|
|
fill.name = "footprint-fill";
|
|
|
|
|
fill.renderOrder = GROUND_FOOTPRINT_RENDER_ORDER;
|
|
|
|
|
lockedGroundFootprintMesh.add(fill);
|
|
|
|
|
lockedGroundFootprintFillMesh = fill;
|
|
|
|
|
hasGroundFootprintGeometry = false;
|
|
|
|
|
earthObjRef.add(lockedGroundFootprintMesh);
|
|
|
|
|
updateGroundFootprintTransform(position);
|
|
|
|
|
}
|
|
|
|
|
|