[claude] Holographic planet Earth slowly rotating above the Nexus (#253) #343
197
app.js
197
app.js
@@ -967,6 +967,198 @@ for (let i = 0; i < RUNE_COUNT; i++) {
|
||||
}
|
||||
|
||||
|
||||
// === HOLOGRAPHIC EARTH ===
|
||||
// A procedural holographic planet Earth slowly rotating above the Nexus.
|
||||
// Continents rendered via layered simplex noise on UV-sphere coords.
|
||||
// Holographic effects: scan lines, fresnel rim glow, lat/lon grid overlay.
|
||||
|
||||
const EARTH_RADIUS = 2.8;
|
||||
const EARTH_Y = 20.0;
|
||||
const EARTH_ROTATION_SPEED = 0.035; // radians per second — gentle drift
|
||||
const EARTH_AXIAL_TILT = 23.4 * (Math.PI / 180);
|
||||
|
||||
const earthGroup = new THREE.Group();
|
||||
earthGroup.position.set(0, EARTH_Y, 0);
|
||||
earthGroup.rotation.z = EARTH_AXIAL_TILT;
|
||||
scene.add(earthGroup);
|
||||
|
||||
// Surface shader — continents via noise, holographic scan lines, fresnel rim
|
||||
const earthSurfaceMat = new THREE.ShaderMaterial({
|
||||
uniforms: {
|
||||
uTime: { value: 0.0 },
|
||||
uOceanColor: { value: new THREE.Color(0x003d99) },
|
||||
uLandColor: { value: new THREE.Color(0x1a5c2a) },
|
||||
uGlowColor: { value: new THREE.Color(NEXUS.colors.accent) },
|
||||
},
|
||||
vertexShader: `
|
||||
varying vec3 vNormal;
|
||||
varying vec3 vWorldPos;
|
||||
varying vec2 vUv;
|
||||
void main() {
|
||||
vNormal = normalize(normalMatrix * normal);
|
||||
vWorldPos = (modelMatrix * vec4(position, 1.0)).xyz;
|
||||
vUv = uv;
|
||||
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
|
||||
}
|
||||
`,
|
||||
fragmentShader: `
|
||||
uniform float uTime;
|
||||
uniform vec3 uOceanColor;
|
||||
uniform vec3 uLandColor;
|
||||
uniform vec3 uGlowColor;
|
||||
varying vec3 vNormal;
|
||||
varying vec3 vWorldPos;
|
||||
varying vec2 vUv;
|
||||
|
||||
// Simplex noise (3-D)
|
||||
vec3 _m3(vec3 x){ return x - floor(x*(1./289.))*289.; }
|
||||
vec4 _m4(vec4 x){ return x - floor(x*(1./289.))*289.; }
|
||||
vec4 _p4(vec4 x){ return _m4((x*34.+1.)*x); }
|
||||
float snoise(vec3 v){
|
||||
const vec2 C = vec2(1./6., 1./3.);
|
||||
vec3 i = floor(v + dot(v, C.yyy));
|
||||
vec3 x0 = v - i + dot(i, C.xxx);
|
||||
vec3 g = step(x0.yzx, x0.xyz);
|
||||
vec3 l = 1.0 - g;
|
||||
vec3 i1 = min(g.xyz, l.zxy);
|
||||
vec3 i2 = max(g.xyz, l.zxy);
|
||||
vec3 x1 = x0 - i1 + C.xxx;
|
||||
vec3 x2 = x0 - i2 + C.yyy;
|
||||
vec3 x3 = x0 - 0.5;
|
||||
i = _m3(i);
|
||||
vec4 p = _p4(_p4(_p4(
|
||||
i.z+vec4(0.,i1.z,i2.z,1.))+
|
||||
i.y+vec4(0.,i1.y,i2.y,1.))+
|
||||
i.x+vec4(0.,i1.x,i2.x,1.));
|
||||
float n_ = .142857142857;
|
||||
vec3 ns = n_*vec3(2.,0.,-1.)+vec3(0.,-.5,1.);
|
||||
vec4 j = p - 49.*floor(p*ns.z*ns.z);
|
||||
vec4 x_ = floor(j*ns.z);
|
||||
vec4 y_ = floor(j - 7.*x_);
|
||||
vec4 h = 1. - abs(x_*(2./7.)) - abs(y_*(2./7.));
|
||||
vec4 b0 = vec4(x_.xy,y_.xy)*(2./7.);
|
||||
vec4 b1 = vec4(x_.zw,y_.zw)*(2./7.);
|
||||
vec4 s0 = floor(b0)*2.+1.; vec4 s1 = floor(b1)*2.+1.;
|
||||
vec4 sh = -step(h, vec4(0.));
|
||||
vec4 a0 = b0.xzyw + s0.xzyw*sh.xxyy;
|
||||
vec4 a1 = b1.xzyw + s1.xzyw*sh.zzww;
|
||||
vec3 p0=vec3(a0.xy,h.x); vec3 p1=vec3(a0.zw,h.y);
|
||||
vec3 p2=vec3(a1.xy,h.z); vec3 p3=vec3(a1.zw,h.w);
|
||||
vec4 nm = max(0.6-vec4(dot(x0,x0),dot(x1,x1),dot(x2,x2),dot(x3,x3)),0.);
|
||||
vec4 nr = 1.79284291400159-0.85373472095314*nm;
|
||||
p0*=nr.x; p1*=nr.y; p2*=nr.z; p3*=nr.w;
|
||||
nm = nm*nm;
|
||||
return 42.*dot(nm*nm, vec4(dot(p0,x0),dot(p1,x1),dot(p2,x2),dot(p3,x3)));
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec3 n = normalize(vNormal);
|
||||
vec3 vd = normalize(cameraPosition - vWorldPos);
|
||||
|
||||
// Stable spherical sample coords from UV (fixed to sphere surface)
|
||||
float lat = (vUv.y - 0.5) * 3.14159265;
|
||||
float lon = vUv.x * 6.28318530;
|
||||
vec3 sp = vec3(cos(lat)*cos(lon), sin(lat), cos(lat)*sin(lon));
|
||||
|
||||
// Layered noise for continent shapes
|
||||
float c = snoise(sp*1.8)*0.60
|
||||
+ snoise(sp*3.6)*0.30
|
||||
+ snoise(sp*7.2)*0.10;
|
||||
float land = smoothstep(0.05, 0.30, c);
|
||||
|
||||
// Surface colour: ocean / land, holographic-tinted
|
||||
vec3 surf = mix(uOceanColor, uLandColor, land);
|
||||
surf = mix(surf, uGlowColor * 0.45, 0.38);
|
||||
|
||||
// Scan lines (horizontal bands)
|
||||
float scan = 0.5 + 0.5*sin(vUv.y * 220.0 + uTime * 1.8);
|
||||
scan = smoothstep(0.30, 0.70, scan) * 0.14;
|
||||
|
||||
// Fresnel rim glow
|
||||
float fresnel = pow(1.0 - max(dot(n, vd), 0.0), 4.0);
|
||||
|
||||
vec3 col = surf + scan*uGlowColor*0.9 + fresnel*uGlowColor*1.5;
|
||||
float alpha = 0.48 + fresnel * 0.42;
|
||||
|
||||
gl_FragColor = vec4(col, alpha);
|
||||
}
|
||||
`,
|
||||
transparent: true,
|
||||
depthWrite: false,
|
||||
side: THREE.FrontSide,
|
||||
});
|
||||
|
||||
const earthSphere = new THREE.SphereGeometry(EARTH_RADIUS, 64, 32);
|
||||
const earthMesh = new THREE.Mesh(earthSphere, earthSurfaceMat);
|
||||
earthMesh.userData.zoomLabel = 'Planet Earth';
|
||||
earthGroup.add(earthMesh);
|
||||
|
||||
// Lat/lon grid lines
|
||||
(function buildEarthGrid() {
|
||||
const lineMat = new THREE.LineBasicMaterial({
|
||||
color: 0x2266bb,
|
||||
transparent: true,
|
||||
opacity: 0.30,
|
||||
});
|
||||
const r = EARTH_RADIUS + 0.015;
|
||||
const SEG = 64;
|
||||
|
||||
// Latitude rings every 30°
|
||||
for (let lat = -60; lat <= 60; lat += 30) {
|
||||
const phi = lat * (Math.PI / 180);
|
||||
const pts = [];
|
||||
for (let i = 0; i <= SEG; i++) {
|
||||
const th = (i / SEG) * Math.PI * 2;
|
||||
pts.push(new THREE.Vector3(
|
||||
Math.cos(phi) * Math.cos(th) * r,
|
||||
Math.sin(phi) * r,
|
||||
Math.cos(phi) * Math.sin(th) * r
|
||||
));
|
||||
}
|
||||
earthGroup.add(new THREE.Line(
|
||||
new THREE.BufferGeometry().setFromPoints(pts), lineMat
|
||||
));
|
||||
}
|
||||
|
||||
// Longitude meridians every 30°
|
||||
for (let lon = 0; lon < 360; lon += 30) {
|
||||
const th = lon * (Math.PI / 180);
|
||||
const pts = [];
|
||||
for (let i = 0; i <= SEG; i++) {
|
||||
const phi = (i / SEG) * Math.PI - Math.PI / 2;
|
||||
pts.push(new THREE.Vector3(
|
||||
Math.cos(phi) * Math.cos(th) * r,
|
||||
Math.sin(phi) * r,
|
||||
Math.cos(phi) * Math.sin(th) * r
|
||||
));
|
||||
}
|
||||
earthGroup.add(new THREE.Line(
|
||||
new THREE.BufferGeometry().setFromPoints(pts), lineMat
|
||||
));
|
||||
}
|
||||
})();
|
||||
|
||||
// Atmosphere shell — soft additive glow around the globe
|
||||
const atmMat = new THREE.MeshBasicMaterial({
|
||||
color: 0x1144cc,
|
||||
transparent: true,
|
||||
opacity: 0.07,
|
||||
side: THREE.BackSide,
|
||||
depthWrite: false,
|
||||
blending: THREE.AdditiveBlending,
|
||||
});
|
||||
earthGroup.add(new THREE.Mesh(
|
||||
new THREE.SphereGeometry(EARTH_RADIUS * 1.14, 32, 16), atmMat
|
||||
));
|
||||
|
||||
// Soft blue point light emanating from Earth
|
||||
const earthGlowLight = new THREE.PointLight(NEXUS.colors.accent, 0.4, 25);
|
||||
earthGroup.add(earthGlowLight);
|
||||
|
||||
earthGroup.traverse(obj => {
|
||||
if (obj.isMesh || obj.isLine) obj.userData.zoomLabel = 'Planet Earth';
|
||||
});
|
||||
|
||||
// === WARP TUNNEL EFFECT ===
|
||||
const WarpShader = {
|
||||
uniforms: {
|
||||
@@ -1185,6 +1377,11 @@ function animate() {
|
||||
rune.sprite.material.opacity = 0.65 + Math.sin(elapsed * 1.2 + rune.floatPhase) * 0.2;
|
||||
}
|
||||
|
||||
// Animate holographic Earth — slow axial rotation, glow pulse
|
||||
earthMesh.rotation.y = elapsed * EARTH_ROTATION_SPEED;
|
||||
earthSurfaceMat.uniforms.uTime.value = elapsed;
|
||||
earthGlowLight.intensity = 0.30 + Math.sin(elapsed * 0.7) * 0.12;
|
||||
|
||||
// === WEATHER PARTICLE ANIMATION ===
|
||||
if (rainParticles.visible) {
|
||||
const rpos = rainGeo.attributes.position.array;
|
||||
|
||||
Reference in New Issue
Block a user