- 添加tilt、chinaLat、chinaLon、latCoefficient等常量 - earth.js和controls.js使用常量替代硬编码 - 离开地球时隐藏tooltip
241 lines
6.5 KiB
JavaScript
241 lines
6.5 KiB
JavaScript
// earth.js - 3D Earth creation module
|
|
|
|
import * as THREE from 'three';
|
|
import { CONFIG, EARTH_CONFIG } from './constants.js';
|
|
import { latLonToVector3 } from './utils.js';
|
|
|
|
export let earth = null;
|
|
export let clouds = null;
|
|
export let terrain = null;
|
|
|
|
const textureLoader = new THREE.TextureLoader();
|
|
|
|
export function createEarth(scene) {
|
|
const geometry = new THREE.SphereGeometry(CONFIG.earthRadius, 128, 128);
|
|
|
|
const material = new THREE.MeshPhongMaterial({
|
|
color: 0xffffff,
|
|
specular: 0x111111,
|
|
shininess: 10,
|
|
emissive: 0x000000,
|
|
transparent: true,
|
|
opacity: 0.8,
|
|
side: THREE.DoubleSide
|
|
});
|
|
|
|
earth = new THREE.Mesh(geometry, material);
|
|
earth.rotation.x = EARTH_CONFIG.tiltRad;
|
|
scene.add(earth);
|
|
|
|
const textureUrls = [
|
|
'./assets/8k_earth_daymap.jpg',
|
|
'https://raw.githubusercontent.com/mrdoob/three.js/dev/examples/textures/planets/earth_atmos_2048.jpg',
|
|
'https://threejs.org/examples/textures/planets/earth_atmos_2048.jpg',
|
|
'https://assets.codepen.io/982762/earth_texture_2048.jpg'
|
|
];
|
|
|
|
let textureLoaded = false;
|
|
|
|
textureLoader.load(
|
|
textureUrls[0],
|
|
function(texture) {
|
|
console.log('高分辨率地球纹理加载成功');
|
|
textureLoaded = true;
|
|
|
|
texture.wrapS = THREE.RepeatWrapping;
|
|
texture.wrapT = THREE.ClampToEdgeWrapping;
|
|
texture.anisotropy = 16;
|
|
texture.minFilter = THREE.LinearMipmapLinearFilter;
|
|
texture.magFilter = THREE.LinearFilter;
|
|
|
|
material.map = texture;
|
|
material.needsUpdate = true;
|
|
|
|
document.getElementById('loading').style.display = 'none';
|
|
},
|
|
function(xhr) {
|
|
console.log('纹理加载中: ' + (xhr.loaded / xhr.total * 100) + '%');
|
|
},
|
|
function(err) {
|
|
console.log('第一个纹理加载失败,尝试第二个...');
|
|
|
|
textureLoader.load(
|
|
textureUrls[1],
|
|
function(texture) {
|
|
console.log('第二个纹理加载成功');
|
|
textureLoaded = true;
|
|
|
|
texture.wrapS = THREE.RepeatWrapping;
|
|
texture.wrapT = THREE.ClampToEdgeWrapping;
|
|
texture.anisotropy = 16;
|
|
texture.minFilter = THREE.LinearMipmapLinearFilter;
|
|
texture.magFilter = THREE.LinearFilter;
|
|
|
|
material.map = texture;
|
|
material.needsUpdate = true;
|
|
|
|
document.getElementById('loading').style.display = 'none';
|
|
},
|
|
null,
|
|
function(err) {
|
|
console.log('所有纹理加载失败');
|
|
document.getElementById('loading').style.display = 'none';
|
|
}
|
|
);
|
|
}
|
|
);
|
|
|
|
return earth;
|
|
}
|
|
|
|
export function createClouds(scene, earthObj) {
|
|
const geometry = new THREE.SphereGeometry(CONFIG.earthRadius + 3, 64, 64);
|
|
const material = new THREE.MeshPhongMaterial({
|
|
transparent: true,
|
|
linewidth: 2,
|
|
opacity: 0.15,
|
|
depthTest: true,
|
|
depthWrite: false,
|
|
blending: THREE.AdditiveBlending,
|
|
side: THREE.DoubleSide
|
|
});
|
|
|
|
clouds = new THREE.Mesh(geometry, material);
|
|
earthObj.add(clouds);
|
|
|
|
textureLoader.load(
|
|
'https://threejs.org/examples/textures/planets/earth_clouds_1024.png',
|
|
function(texture) {
|
|
material.map = texture;
|
|
material.needsUpdate = true;
|
|
},
|
|
undefined,
|
|
function(err) {
|
|
console.log('云层纹理加载失败');
|
|
}
|
|
);
|
|
|
|
return clouds;
|
|
}
|
|
|
|
export function createTerrain(scene, earthObj, simplex) {
|
|
const geometry = new THREE.SphereGeometry(CONFIG.earthRadius, 128, 128);
|
|
const positionAttribute = geometry.getAttribute('position');
|
|
|
|
for (let i = 0; i < positionAttribute.count; i++) {
|
|
const x = positionAttribute.getX(i);
|
|
const y = positionAttribute.getY(i);
|
|
const z = positionAttribute.getZ(i);
|
|
|
|
const noise = simplex(x / 20, y / 20, z / 20);
|
|
const height = 1 + noise * 0.02;
|
|
|
|
positionAttribute.setXYZ(i, x * height, y * height, z * height);
|
|
}
|
|
|
|
geometry.computeVertexNormals();
|
|
|
|
const material = new THREE.MeshPhongMaterial({
|
|
color: 0x00aa00,
|
|
flatShading: true,
|
|
transparent: true,
|
|
opacity: 0.7
|
|
});
|
|
|
|
terrain = new THREE.Mesh(geometry, material);
|
|
terrain.visible = false;
|
|
earthObj.add(terrain);
|
|
|
|
return terrain;
|
|
}
|
|
|
|
export function toggleTerrain(visible) {
|
|
if (terrain) {
|
|
terrain.visible = visible;
|
|
}
|
|
}
|
|
|
|
export function createStars(scene) {
|
|
const starGeometry = new THREE.BufferGeometry();
|
|
const starCount = 8000;
|
|
const starPositions = new Float32Array(starCount * 3);
|
|
|
|
for (let i = 0; i < starCount * 3; i += 3) {
|
|
const r = 800 + Math.random() * 200;
|
|
const theta = Math.random() * Math.PI * 2;
|
|
const phi = Math.acos(2 * Math.random() - 1);
|
|
|
|
starPositions[i] = r * Math.sin(phi) * Math.cos(theta);
|
|
starPositions[i + 1] = r * Math.sin(phi) * Math.sin(theta);
|
|
starPositions[i + 2] = r * Math.cos(phi);
|
|
}
|
|
|
|
starGeometry.setAttribute('position', new THREE.BufferAttribute(starPositions, 3));
|
|
|
|
const starMaterial = new THREE.PointsMaterial({
|
|
color: 0xffffff,
|
|
size: 0.5,
|
|
transparent: true,
|
|
blending: THREE.AdditiveBlending
|
|
});
|
|
|
|
const stars = new THREE.Points(starGeometry, starMaterial);
|
|
scene.add(stars);
|
|
|
|
return stars;
|
|
}
|
|
|
|
let latitudeLines = [];
|
|
let longitudeLines = [];
|
|
|
|
export function createGridLines(scene, earthObj) {
|
|
latitudeLines.forEach(line => scene.remove(line));
|
|
longitudeLines.forEach(line => scene.remove(line));
|
|
latitudeLines = [];
|
|
longitudeLines = [];
|
|
|
|
const earthRadius = 100.1;
|
|
const gridMaterial = new THREE.LineBasicMaterial({
|
|
color: 0x44aaff,
|
|
transparent: true,
|
|
opacity: 0.2,
|
|
linewidth: 1
|
|
});
|
|
|
|
for (let lat = -75; lat <= 75; lat += 15) {
|
|
const points = [];
|
|
for (let lon = -180; lon <= 180; lon += 5) {
|
|
const point = latLonToVector3(lat, lon, earthRadius);
|
|
points.push(point);
|
|
}
|
|
|
|
const geometry = new THREE.BufferGeometry().setFromPoints(points);
|
|
const line = new THREE.Line(geometry, gridMaterial);
|
|
line.userData = { type: 'latitude', value: lat };
|
|
earthObj.add(line);
|
|
latitudeLines.push(line);
|
|
}
|
|
|
|
for (let lon = -180; lon <= 180; lon += 30) {
|
|
const points = [];
|
|
for (let lat = -90; lat <= 90; lat += 5) {
|
|
const point = latLonToVector3(lat, lon, earthRadius);
|
|
points.push(point);
|
|
}
|
|
|
|
const geometry = new THREE.BufferGeometry().setFromPoints(points);
|
|
const line = new THREE.Line(geometry, gridMaterial);
|
|
line.userData = { type: 'longitude', value: lon };
|
|
earthObj.add(line);
|
|
longitudeLines.push(line);
|
|
}
|
|
}
|
|
|
|
export function getEarth() {
|
|
return earth;
|
|
}
|
|
|
|
export function getClouds() {
|
|
return clouds;
|
|
}
|