Enhance Earth interaction and bump version to 0.21.0
This commit is contained in:
@@ -19,9 +19,10 @@ let earthObjRef = null;
|
||||
let sceneRef = null;
|
||||
let cameraRef = null;
|
||||
let lockedSatelliteIndex = null;
|
||||
let hoveredSatelliteIndex = null;
|
||||
let positionUpdateAccumulator = 0;
|
||||
let satelliteCapacity = 0;
|
||||
|
||||
const MAX_SATELLITES = SATELLITE_CONFIG.maxCount;
|
||||
const TRAIL_LENGTH = SATELLITE_CONFIG.trailLength;
|
||||
const DOT_TEXTURE_SIZE = 32;
|
||||
const POSITION_UPDATE_INTERVAL_MS = 250;
|
||||
@@ -114,17 +115,9 @@ function createRingTexture(innerRadius, outerRadius, color = "#ffffff") {
|
||||
|
||||
export function createSatellites(scene, earthObj) {
|
||||
initSatelliteScene(scene, earthObj);
|
||||
|
||||
const positions = new Float32Array(MAX_SATELLITES * 3);
|
||||
const colors = new Float32Array(MAX_SATELLITES * 3);
|
||||
const dotTexture = createDotTexture();
|
||||
|
||||
const pointsGeometry = new THREE.BufferGeometry();
|
||||
pointsGeometry.setAttribute(
|
||||
"position",
|
||||
new THREE.BufferAttribute(positions, 3),
|
||||
);
|
||||
pointsGeometry.setAttribute("color", new THREE.BufferAttribute(colors, 3));
|
||||
|
||||
const pointsMaterial = new THREE.PointsMaterial({
|
||||
size: SATELLITE_CONFIG.dotSize,
|
||||
@@ -159,18 +152,7 @@ export function createSatellites(scene, earthObj) {
|
||||
|
||||
earthObj.add(satellitePoints);
|
||||
|
||||
const trailPositions = new Float32Array(MAX_SATELLITES * TRAIL_LENGTH * 3);
|
||||
const trailColors = new Float32Array(MAX_SATELLITES * TRAIL_LENGTH * 3);
|
||||
|
||||
const trailGeometry = new THREE.BufferGeometry();
|
||||
trailGeometry.setAttribute(
|
||||
"position",
|
||||
new THREE.BufferAttribute(trailPositions, 3),
|
||||
);
|
||||
trailGeometry.setAttribute(
|
||||
"color",
|
||||
new THREE.BufferAttribute(trailColors, 3),
|
||||
);
|
||||
|
||||
const trailMaterial = new THREE.LineBasicMaterial({
|
||||
vertexColors: true,
|
||||
@@ -183,16 +165,59 @@ export function createSatellites(scene, earthObj) {
|
||||
satelliteTrails.visible = false;
|
||||
satelliteTrails.userData = { type: "satelliteTrails" };
|
||||
earthObj.add(satelliteTrails);
|
||||
ensureSatelliteCapacity(0);
|
||||
|
||||
satellitePositions = Array.from({ length: MAX_SATELLITES }, () => ({
|
||||
positionUpdateAccumulator = POSITION_UPDATE_INTERVAL_MS;
|
||||
return satellitePoints;
|
||||
}
|
||||
|
||||
function getRequestedSatelliteLimit() {
|
||||
return SATELLITE_CONFIG.maxCount < 0 ? null : SATELLITE_CONFIG.maxCount;
|
||||
}
|
||||
|
||||
function createSatellitePositionState() {
|
||||
return {
|
||||
current: new THREE.Vector3(),
|
||||
trail: [],
|
||||
trailIndex: 0,
|
||||
trailCount: 0,
|
||||
}));
|
||||
};
|
||||
}
|
||||
|
||||
positionUpdateAccumulator = POSITION_UPDATE_INTERVAL_MS;
|
||||
return satellitePoints;
|
||||
function ensureSatelliteCapacity(count) {
|
||||
if (!satellitePoints || !satelliteTrails) return;
|
||||
|
||||
const nextCapacity = Math.max(count, 0);
|
||||
if (nextCapacity === satelliteCapacity) return;
|
||||
|
||||
const positions = new Float32Array(nextCapacity * 3);
|
||||
const colors = new Float32Array(nextCapacity * 3);
|
||||
satellitePoints.geometry.setAttribute(
|
||||
"position",
|
||||
new THREE.BufferAttribute(positions, 3),
|
||||
);
|
||||
satellitePoints.geometry.setAttribute(
|
||||
"color",
|
||||
new THREE.BufferAttribute(colors, 3),
|
||||
);
|
||||
satellitePoints.geometry.setDrawRange(0, 0);
|
||||
|
||||
const trailPositions = new Float32Array(nextCapacity * TRAIL_LENGTH * 3);
|
||||
const trailColors = new Float32Array(nextCapacity * TRAIL_LENGTH * 3);
|
||||
satelliteTrails.geometry.setAttribute(
|
||||
"position",
|
||||
new THREE.BufferAttribute(trailPositions, 3),
|
||||
);
|
||||
satelliteTrails.geometry.setAttribute(
|
||||
"color",
|
||||
new THREE.BufferAttribute(trailColors, 3),
|
||||
);
|
||||
|
||||
satellitePositions = Array.from(
|
||||
{ length: nextCapacity },
|
||||
createSatellitePositionState,
|
||||
);
|
||||
satelliteCapacity = nextCapacity;
|
||||
}
|
||||
|
||||
function computeSatellitePosition(satellite, time) {
|
||||
@@ -357,15 +382,20 @@ function generateFallbackPosition(satellite, index, total) {
|
||||
}
|
||||
|
||||
export async function loadSatellites() {
|
||||
const response = await fetch(
|
||||
`${SATELLITE_CONFIG.apiPath}?limit=${SATELLITE_CONFIG.maxCount}`,
|
||||
);
|
||||
const limit = getRequestedSatelliteLimit();
|
||||
const url = new URL(SATELLITE_CONFIG.apiPath, window.location.origin);
|
||||
if (limit !== null) {
|
||||
url.searchParams.set("limit", String(limit));
|
||||
}
|
||||
|
||||
const response = await fetch(url.toString());
|
||||
if (!response.ok) {
|
||||
throw new Error(`卫星接口返回 HTTP ${response.status}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
satelliteData = data.features || [];
|
||||
ensureSatelliteCapacity(satelliteData.length);
|
||||
positionUpdateAccumulator = POSITION_UPDATE_INTERVAL_MS;
|
||||
return satelliteData.length;
|
||||
}
|
||||
@@ -392,7 +422,7 @@ export function updateSatellitePositions(deltaTime = 0, force = false) {
|
||||
const trailPositions = satelliteTrails.geometry.attributes.position.array;
|
||||
const trailColors = satelliteTrails.geometry.attributes.color.array;
|
||||
const baseTime = new Date(Date.now() + elapsedMs);
|
||||
const count = Math.min(satelliteData.length, MAX_SATELLITES);
|
||||
const count = Math.min(satelliteData.length, satelliteCapacity);
|
||||
|
||||
for (let i = 0; i < count; i++) {
|
||||
const satellite = satelliteData[i];
|
||||
@@ -485,7 +515,7 @@ export function updateSatellitePositions(deltaTime = 0, force = false) {
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = count; i < MAX_SATELLITES; i++) {
|
||||
for (let i = count; i < satelliteCapacity; i++) {
|
||||
positions[i * 3] = 0;
|
||||
positions[i * 3 + 1] = 0;
|
||||
positions[i * 3 + 2] = 0;
|
||||
@@ -504,6 +534,17 @@ export function updateSatellitePositions(deltaTime = 0, force = false) {
|
||||
|
||||
satelliteTrails.geometry.attributes.position.needsUpdate = true;
|
||||
satelliteTrails.geometry.attributes.color.needsUpdate = true;
|
||||
|
||||
// Keep the hover ring synced with the propagated satellite position even
|
||||
// when the pointer stays still and no new hover event is emitted.
|
||||
if (
|
||||
hoveredSatelliteIndex !== null &&
|
||||
hoveredSatelliteIndex >= 0 &&
|
||||
hoveredSatelliteIndex < count &&
|
||||
hoveredSatelliteIndex !== lockedSatelliteIndex
|
||||
) {
|
||||
updateHoverRingPosition(satellitePositions[hoveredSatelliteIndex].current);
|
||||
}
|
||||
}
|
||||
|
||||
export function toggleSatellites(visible) {
|
||||
@@ -563,6 +604,10 @@ export function setLockedSatelliteIndex(index) {
|
||||
lockedSatelliteIndex = index;
|
||||
}
|
||||
|
||||
export function setHoveredSatelliteIndex(index) {
|
||||
hoveredSatelliteIndex = index;
|
||||
}
|
||||
|
||||
export function isSatelliteFrontFacing(index, camera = cameraRef) {
|
||||
if (!earthObjRef || !camera) return true;
|
||||
if (!satellitePositions || !satellitePositions[index]) return true;
|
||||
@@ -718,14 +763,17 @@ export function updateHoverRingPosition(position) {
|
||||
export function setSatelliteRingState(index, state, position) {
|
||||
switch (state) {
|
||||
case "hover":
|
||||
hoveredSatelliteIndex = index;
|
||||
hideHoverRings();
|
||||
showHoverRing(position, false);
|
||||
break;
|
||||
case "locked":
|
||||
hoveredSatelliteIndex = null;
|
||||
hideHoverRings();
|
||||
showHoverRing(position, true);
|
||||
break;
|
||||
case "none":
|
||||
hoveredSatelliteIndex = null;
|
||||
hideHoverRings();
|
||||
hideLockedRing();
|
||||
break;
|
||||
@@ -826,6 +874,7 @@ export function clearSatelliteData() {
|
||||
satelliteData = [];
|
||||
selectedSatellite = null;
|
||||
lockedSatelliteIndex = null;
|
||||
hoveredSatelliteIndex = null;
|
||||
positionUpdateAccumulator = 0;
|
||||
|
||||
satellitePositions.forEach((position) => {
|
||||
@@ -836,22 +885,30 @@ export function clearSatelliteData() {
|
||||
});
|
||||
|
||||
if (satellitePoints) {
|
||||
const positions = satellitePoints.geometry.attributes.position.array;
|
||||
const colors = satellitePoints.geometry.attributes.color.array;
|
||||
positions.fill(0);
|
||||
colors.fill(0);
|
||||
satellitePoints.geometry.attributes.position.needsUpdate = true;
|
||||
satellitePoints.geometry.attributes.color.needsUpdate = true;
|
||||
const positionAttr = satellitePoints.geometry.attributes.position;
|
||||
const colorAttr = satellitePoints.geometry.attributes.color;
|
||||
if (positionAttr?.array) {
|
||||
positionAttr.array.fill(0);
|
||||
positionAttr.needsUpdate = true;
|
||||
}
|
||||
if (colorAttr?.array) {
|
||||
colorAttr.array.fill(0);
|
||||
colorAttr.needsUpdate = true;
|
||||
}
|
||||
satellitePoints.geometry.setDrawRange(0, 0);
|
||||
}
|
||||
|
||||
if (satelliteTrails) {
|
||||
const trailPositions = satelliteTrails.geometry.attributes.position.array;
|
||||
const trailColors = satelliteTrails.geometry.attributes.color.array;
|
||||
trailPositions.fill(0);
|
||||
trailColors.fill(0);
|
||||
satelliteTrails.geometry.attributes.position.needsUpdate = true;
|
||||
satelliteTrails.geometry.attributes.color.needsUpdate = true;
|
||||
const trailPositionAttr = satelliteTrails.geometry.attributes.position;
|
||||
const trailColorAttr = satelliteTrails.geometry.attributes.color;
|
||||
if (trailPositionAttr?.array) {
|
||||
trailPositionAttr.array.fill(0);
|
||||
trailPositionAttr.needsUpdate = true;
|
||||
}
|
||||
if (trailColorAttr?.array) {
|
||||
trailColorAttr.array.fill(0);
|
||||
trailColorAttr.needsUpdate = true;
|
||||
}
|
||||
}
|
||||
|
||||
hideHoverRings();
|
||||
@@ -873,6 +930,7 @@ export function resetSatelliteState() {
|
||||
}
|
||||
|
||||
satellitePositions = [];
|
||||
satelliteCapacity = 0;
|
||||
showSatellites = false;
|
||||
showTrails = true;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user