<!DOCTYPE html>
<!--
Author: twily 2025
Helper in making: grok 3 (ai) x)
Textures: nasa earth observatory terra:blue marble
Description: simluate asteroid in our solar system
Keybinds available:
w : wireframe toggle
l : lines toggle
u : ui toggle
shift: swap mouseR
-->
<html>
<head>
<title>Asteroid MG1 Passing Earth - July 12, 2025</title>
<style>
body { margin: 0; overflow: hidden; user-select: none; }
canvas { display: block; }
#timeline {
width: 100%;
padding: 10px;
background: rgba(255, 255, 255, 0.8);
border-radius: 5px;
}
#timeLabel {
color: white;
font-family: Arial, sans-serif;
cursor: default;
}
#controls {
position: absolute;
bottom: 0px; left: 50%;
transform: translateX(-50%);
width: 80%; max-width: 600px;
text-align: center;
padding: 10px;
color: lime;
font-family: Arial, sans-serif;
}
#controls2 {
position: absolute;
top: 10px; right: 10px;
color: lime; background: rgba(0, 0, 0, 0.5);
text-align: right;
padding: 10px;
font-family: Arial, sans-serif;
}
#controls2 > span {
cursor: default;
}
#cameraInfo {
position: absolute;
top: 10px; left: 10px;
color: lime; background: rgba(0, 0, 0, 0.5);
font-family: Arial, sans-serif; font-size: 14px;
padding: 5px;
border-radius: 3px;
cursor: default;
}
#mF1, #mF2, #mF3 {
display:none;
}
#pF1, #pF2, #pF3, #pF4, #pF5, #pF6 {
display:none;
}
.tbl { display: table; width: 100%; height: 100%; /*table-layout: fixed;*/ }
.tr { display: table-row; }
.td { display: table-cell; vertical-align: top; /*border: 1px solid #606163; box-shadow: inset 0 0 2px 2px #000;*/ }
/*#tbl1 span { height: 0; line-height: 0; }*/
</style>
</style>
</head>
<body>
<div id="cameraInfo">
<div id="infoDynamic"></div><br />
<div id="infoStatic">
Earth speed ~ 107 000 km/h<br />
Moon speed ~ 3 600 km/h<br />
Asteroid speed ~ 25 000 km/h<br />
<!--(Sun ~ 800 000 km/h)<br />
(Milky Way ~ 2.1 million km/h)</br>-->
</div>
</div>
<div id="controls">
<div id="timeLabel">July 12, 2025 12:00 UTC</div>
<input type="range" id="timeline" min="0" max="100" value="50" step=".01">
<br />
<div class="tbl" id="tbl1">
<div class="tr">
<div class="td">
<button id="play1x">Play 1x</button>
</div>
<div class="td">
<button id="play100x">Play 100x</button>
</div>
<div class="td">
<button id="play1000x">Play 1.000x</button>
</div>
<div class="td">
<button id="play10000x">Play 10.000x</button>
</div>
<div class="td">
<button id="play100000x">Play 100.000x</button>
</div>
<div class="td">
<button id="play1000000x">Play 1.000.000x</button>
</div>
<div class="td">
<button id="stop">Stop</button>
</div>
</div>
<div class="tr">
<div class="td"><span id="pF1">^</span></div>
<div class="td"><span id="pF2">^</span></div>
<div class="td"><span id="pF3">^</span></div>
<div class="td"><span id="pF4">^</span></div>
<div class="td"><span id="pF5">^</span></div>
<div class="td"><span id="pF6">^</span></div>
<div class="td"><span id="pF0">^</span></div>
</div>
</div>
</div>
</div>
<div id="controls2">
<span id="mF0">></span><button id="modeF">Free mode</button><br />
<span id="lockLabel">Lock mode:</span><br />
<span id="mF1">></span><button id="modeL1">Asteroid</button><br />
<span id="mF2">></span><button id="modeL2">Moon</button><br />
<span id="mF3">></span><button id="modeL3">Earth</button><br />
</div>
<!--<script src="three.min.js"></script>
<script src="OrbitControls.js"></script>-->
<script type="importmap">
{
"imports": {
"three": "./build/three.module.js",
"three/addons/": "./jsm/"
}
}
</script>
<script type="module">
import * as THREE from 'three';
import Stats from 'three/addons/libs/stats.module.js';
import { OrbitControls } from 'three/addons/controls/OrbitControls2.js';
//import { FBXLoader } from 'three/addons/loaders/FBXLoader.js';
//import { GUI } from 'three/addons/libs/lil-gui.module.min.js';
// Add composer
//--import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js';
//--import { RenderPass } from 'three/addons/postprocessing/RenderPass.js';
//--import { UnrealBloomPass } from 'three/addons/postprocessing/UnrealBloomPass.js';
// Scene setup
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
//THREE.Object3D._threexDomEvent.camera(camera);
scene.add(camera);
const canvas = renderer.domElement;
camera.near=.01;
camera.far=1000;
camera.updateProjectionMatrix()
//--const renderScene = new RenderPass(scene, camera);
//--const bloomPass = new UnrealBloomPass(new THREE.Vector2(window.innerWidth, window.innerHeight), 1.5, 0.4, 0.85);
//--bloomPass.threshold = 0.1; // Glow starts at bright areas
//--bloomPass.strength = 1.0; // Intensity
//--bloomPass.radius = 0.5; // Spread
//--const composer = new EffectComposer(renderer);
//--composer.addPass(renderScene);
//--composer.addPass(bloomPass);
// Lighting
const sunLight = new THREE.PointLight(0xffffff, 50000, 1000);
sunLight.position.set(0, 0, 0);
sunLight.castShadow = true;
sunLight.shadow.mapSize.width = 1024;
sunLight.shadow.mapSize.height = 1024;
sunLight.shadow.camera.near = 0.5;
sunLight.shadow.camera.far = 500;
sunLight.shadow.radius = 1;
scene.add(sunLight);
const ambientLight = new THREE.AmbientLight(0x404040, 0.1); // Adjusted up from 0.1
scene.add(ambientLight);
//
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//
// Define the shaders
const vertexShaderSun = `
uniform mat4 mvpMatrix;
varying vec2 vUv;
varying vec3 vNormal;
varying vec3 vPosition;
varying vec3 lPosition;
void main() {
vUv = uv;
vNormal = normal;
vPosition = position;
lPosition = (modelMatrix * vec4(position, 1.0)).xyz;
gl_Position = mvpMatrix * vec4(position, 1.0);
}
`;
const fragmentShaderSun = `
uniform float time; // surface
uniform float realtime; // rotation
varying vec2 vUv;
varying vec3 vNormal;
varying vec3 vPosition;
varying vec3 lPosition;
const float PI = 3.1415926535897932384626433832795;
float random(vec3 p) {
return fract(sin(dot(p, vec3(12.9898, 78.233, 45.5432))) * 43758.5453123);
}
float noise(vec3 p) {
vec3 i = floor(p);
vec3 f = fract(p);
vec3 u = f * f * (3.0 - 2.0 * f);
float n0 = random(i);
float n1 = random(i + vec3(1.0, 0.0, 0.0));
float n2 = random(i + vec3(0.0, 1.0, 0.0));
float n3 = random(i + vec3(1.0, 1.0, 0.0));
float n4 = random(i + vec3(0.0, 0.0, 1.0));
float n5 = random(i + vec3(1.0, 0.0, 1.0));
float n6 = random(i + vec3(0.0, 1.0, 1.0));
float n7 = random(i + vec3(1.0, 1.0, 1.0));
return mix(
mix(mix(n0, n1, u.x), mix(n2, n3, u.x), u.y),
mix(mix(n4, n5, u.x), mix(n6, n7, u.x), u.y),
u.z
);
}
float noise4D(vec3 p, float t) {
return noise(p + vec3(0.0, 0.0, t)); // Use Z for time dimension
}
void main() {
//float loopedRealtime = mod(realtime, 25.0 * 24.0 * 3600.0); // 25 days in seconds
//float globalAngle = (loopedRealtime / (25.0 * 24.0 * 3600.0)) * 2.0 * PI; // 0 to 2PI
// Latitude-dependent speed factor
float latitude = asin(vNormal.y); // -PI/2 to PI/2
//--float speedFactor = mix(0.1, 0.3, abs(latitude) / (PI / 2.0)); // 1.0 at poles, 1.2 at equator
float speedFactor = mix(realtime, realtime, abs(latitude) / (PI / 2.0)); // 1.0 at poles, 1.2 at equator
//--float localAngle = globalAngle * speedFactor;
float localAngle = speedFactor;
// Base scale increased for higher resolution (finer details)
float baseScale = 10.0; // Doubled from 5.0 for more detail
float detailScale = 20.0; // High-frequency detail layer
vec3 pos = normalize(vPosition);
// 3D rotation around Y-axis (horizontal)
float cosR = cos(-localAngle);
float sinR = sin(-localAngle);
vec3 pos2 = vec3(
cosR * pos.x + sinR * pos.z, // Rotate X around Y
pos.y, // Y unchanged (horizontal axis)
-sinR * pos.x + cosR * pos.z // Rotate Z around Y
);
// Three main flow layers with increased frequency
vec3 flow1 = pos2 * baseScale + vec3(time * 0.1);
vec3 flow2 = pos2 * baseScale - vec3(time * 0.15);
vec3 flow3 = pos2 * baseScale + vec3(time * 0.05, time * 0.05, 0.0);
// Additional high-frequency detail layer
vec3 flowDetail = pos2 * detailScale + vec3(time * 0.2);
// Generate noise layers
float n1 = noise4D(flow1, time * 0.1);
float n2 = noise4D(flow2, time * 0.15);
float n3 = noise4D(flow3, time * 0.05);
float nDetail = noise4D(flowDetail, time * 0.2);
// Combine noise layers with detail
float n = (n1 + n2 + n3) / 3.0 + nDetail * 0.2; // Detail contributes subtly
// Define lava colors
vec3 color1 = vec3(1.0, 1.0, 0.2); // Bright yellow/orange
vec3 color2 = vec3(1.0, 0.25, 0.0); // Darker orange/red
vec3 color3 = vec3(0.25, 0.0, 0.15); // Near-black
// Blend colors based on noise
vec3 color;
if (n < 0.3) {
color = mix(color3, color2, n / 0.3);
} else {
color = mix(color2, color1, (n - 0.3) / 0.7);
}
// Add glow for hot spots
float glow = smoothstep(0.6, 1.0, n);
color += vec3(1.0, 0.5, 0.0) * glow * 0.5;
//color += .2; // boost brightness
//vec2 fuv=fract(uv);
//color *= (color + 0.5);
gl_FragColor = vec4(color, 1.0);
//gl_FragColor = vec4(fuv, 0.0, 1.0);
//gl_FragColor = vec4(realtime, 0.0, 0.0, 1.0);
}
`;
const vertexShaderStars = `
uniform mat4 mvpMatrix;
varying vec2 vUv;
varying float vNoise;
varying vec3 vPosition;
//varying float vDensity;
//varying float vColorNoise;
void main() {
vUv = uv;
vPosition = position;
// Simple 3D noise for clustering
vec3 p = position * 0.01; // Scale for noise frequency
float n = fract(sin(dot(p, vec3(12.9898, 78.233, 45.5432))) * 43758.5453123);
vNoise = n;
//vDensity = smoothstep(0.3, 0.7, (noise(p) + noise(p + vec3(5.0))) * 0.5);
//vColorNoise = noise(p + vec3(10.0));
gl_Position = mvpMatrix * vec4(position, 1.0);
gl_PointSize = 2.0; // Adjust star size
}
`;
const fragmentShaderStars = `
varying vec2 vUv;
varying float vNoise;
varying vec3 vPosition;
//varying float vDensity;
//varying float vColorNoise;
// Simple 3D noise function
float random(vec3 p) {
return fract(sin(dot(p, vec3(12.9898, 78.233, 45.5432))) * 43758.5453123);
}
float noise(vec3 p) {
vec3 i = floor(p);
vec3 f = fract(p);
vec3 u = f * f * (3.0 - 2.0 * f);
float n0 = random(i);
float n1 = random(i + vec3(1.0, 0.0, 0.0));
float n2 = random(i + vec3(0.0, 1.0, 0.0));
float n3 = random(i + vec3(1.0, 1.0, 0.0));
float n4 = random(i + vec3(0.0, 0.0, 1.0));
float n5 = random(i + vec3(1.0, 0.0, 1.0));
float n6 = random(i + vec3(0.0, 1.0, 1.0));
float n7 = random(i + vec3(1.0, 1.0, 1.0));
return mix(
mix(mix(n0, n1, u.x), mix(n2, n3, u.x), u.y),
mix(mix(n4, n5, u.x), mix(n6, n7, u.x), u.y),
u.z
);
}
void main() {
// Cluster density using noise
vec3 pos = vPosition * 0.075; // Adjust noise scale
float n1 = noise(pos);
float n2 = noise(pos + vec3(5.0)); // Offset for variation
float density = smoothstep(0.03, 0.7, (n1 + n2) * 0.5);
// Noise-based color map
vec3 color;
float colorNoise = noise(pos + vec3(10.0));
if (colorNoise < 0.2) {
color = vec3(0.9, 0.0, 0.0); // Reddish clusters
} else if (colorNoise < 0.4) {
color = vec3(0.0, 0.3, 0.6); // Bluish clusters
} else if (colorNoise < 0.6) {
color = vec3(0.6, 0.3, 0.0); // Yellowish clusters
} else {
color = vec3(1.0, 1.0, 1.0); // White for major spaces
}
// Star visibility based on density
float alpha = density * 0.8;
if (density < 0.4) {
discard; // Skip low-density areas for sparse stars
}
gl_FragColor = vec4(color, alpha);
}
`;
const vertexShaderGlow = `
uniform mat4 mvpMatrix;
varying vec2 vUv;
varying float vNoise;
varying vec3 vPosition;
void main() {
vUv = uv;
vPosition = position;
vec3 p = position * 0.01;
vNoise = fract(sin(dot(p, vec3(12.9898, 78.233, 45.5432))) * 43758.5453123);
gl_Position = mvpMatrix * vec4(position, 1.0);
gl_PointSize = 10.0 * vNoise; // Vary size by noise
}
`;
const fragmentShaderGlow = `
uniform sampler2D glowTexture;
varying vec2 vUv;
varying float vNoise;
varying vec3 vPosition;
float random(vec3 p) {
return fract(sin(dot(p, vec3(12.9898, 78.233, 45.5432))) * 43758.5453123);
}
float noise(vec3 p) {
vec3 i = floor(p);
vec3 f = fract(p);
vec3 u = f * f * (3.0 - 2.0 * f);
float n0 = random(i);
float n1 = random(i + vec3(1.0, 0.0, 0.0));
float n2 = random(i + vec3(0.0, 1.0, 0.0));
float n3 = random(i + vec3(1.0, 1.0, 0.0));
float n4 = random(i + vec3(0.0, 0.0, 1.0));
float n5 = random(i + vec3(1.0, 0.0, 1.0));
float n6 = random(i + vec3(0.0, 1.0, 1.0));
float n7 = random(i + vec3(1.0, 1.0, 1.0));
return mix(
mix(mix(n0, n1, u.x), mix(n2, n3, u.x), u.y),
mix(mix(n4, n5, u.x), mix(n6, n7, u.x), u.y),
u.z
);
}
void main() {
vec3 pos = vPosition * 0.075;
float n1 = noise(pos);
float n2 = noise(pos + vec3(5.0));
float density = smoothstep(0.03, 0.7, (n1 + n2) * 0.5);
vec3 color;
float colorNoise = noise(pos + vec3(10.0));
if (colorNoise < 0.2) {
color = vec3(1.0, 0.3, 0.3); // Reddish glow
} else if (colorNoise < 0.4) {
color = vec3(0.3, 0.3, 1.0); // Bluish glow
} else if (colorNoise < 0.6) {
color = vec3(1.0, 1.0, 0.3); // Yellowish glow
} else {
color = vec3(1.0, 1.0, 1.0); // White glow
}
float alpha = density * texture2D(glowTexture, gl_PointCoord).a * 0.5;
if (density < 0.4) {
discard;
}
gl_FragColor = vec4(color, alpha);
}
`;
const vertexShaderEarth = `
uniform mat4 mvpMatrix;
uniform mat3 nMatrix; // CPU-computed normal matrix
uniform mat4 vMatrix; // View matrix (for view-space lighting)
uniform vec3 lightPosition;
varying vec2 vUv;
varying vec3 vNormal;
varying vec3 vViewPosition;
varying vec3 vPosition;
varying vec4 viewSunPos;
varying mat4 pMatrix;
void main() {
vUv = uv;
vNormal = normal;
vViewPosition = (modelViewMatrix * vec4(position, 1.0)).xyz;
vPosition = position;
//vPosition = (modelMatrix * vec4(position, 1.0)).xyz; // World space use vViewPos
pMatrix = projectionMatrix;
vec3 worldNormal = normalize(nMatrix * normal);
vNormal = normalize((vMatrix * vec4(worldNormal, 0.0)).xyz);
viewSunPos = vMatrix * vec4(lightPosition, 1.0);
gl_Position = mvpMatrix * vec4(position, 1.0);
}
`;
const fragmentShaderEarth = `
uniform sampler2D diffuseMapNightEmit;
uniform sampler2D diffuseMapNight;
uniform sampler2D diffuseMapJuly;
uniform sampler2D diffuseMapDecember;
uniform sampler2D diffuseMapAqua;
uniform sampler2D normalMap;
uniform sampler2D earth_night_emit;
uniform mat4 vMatrix; // View matrix (for view-space lighting)
uniform vec3 lightPosition; // Sun at (0, 0, 0)
uniform float isWinter; // 0 = summer (July), 1 = winter (December)
uniform float time; // For animation
varying vec2 vUv;
varying vec3 vNormal;
varying vec3 vViewPosition;
varying vec3 vPosition;
varying vec4 viewSunPos;
varying mat4 pMatrix;
// Shadow map uniforms
//uniform sampler2D shadowMap; // for directional
//uniform mat4 lightSpaceMatrix;
uniform samplerCube shadowMap; // Cube map for PointLight
uniform float shadowBias;
float random(vec3 p) {
return fract(sin(dot(p, vec3(12.9898, 78.233, 45.5432))) * 43758.5453123);
}
// 3D noise function
float noise(vec3 p) {
vec3 i = floor(p);
vec3 f = fract(p);
vec3 u = f * f * (3.0 - 2.0 * f);
float n0 = random(i);
float n1 = random(i + vec3(1.0, 0.0, 0.0));
float n2 = random(i + vec3(0.0, 1.0, 0.0));
float n3 = random(i + vec3(1.0, 1.0, 0.0));
float n4 = random(i + vec3(0.0, 0.0, 1.0));
float n5 = random(i + vec3(1.0, 0.0, 1.0));
float n6 = random(i + vec3(0.0, 1.0, 1.0));
float n7 = random(i + vec3(1.0, 1.0, 1.0));
return mix(
mix(mix(n0, n1, u.x), mix(n2, n3, u.x), u.y),
mix(mix(n4, n5, u.x), mix(n6, n7, u.x), u.y),
u.z
);
}
// 4D noise
float noise4D(vec3 p, float t) {
return noise(p + vec3(t));
}
//----shadowmap
//float sampleShadowMap(vec3 worldPos) {
// vec4 lightSpacePos = lightSpaceMatrix * vec4(worldPos, 1.0);
// vec3 projCoords = lightSpacePos.xyz / lightSpacePos.w;
// projCoords = projCoords * 0.5 + 0.5; // Convert to [0,1]
// if (projCoords.z > 1.0) return 1.0; // Outside shadow map
// float closestDepth = texture2D(shadowMap, projCoords.xy).r;
// float currentDepth = projCoords.z - shadowBias;
// return currentDepth > closestDepth ? 0.5 : 1.0; // Partial shadow
//}
//float sampleShadowMap(vec3 worldPos) { // cube (point lights)
// vec3 lightToFrag = worldPos - lightPosition;
// float currentDepth = length(lightToFrag) - shadowBias;
// float closestDepth = textureCube(shadowMap, lightToFrag).r * 1000.0; // Scale to match scene
// // Define cloud depth range (Earth radius 100 + cloud thickness)
// float cloudMinDepth = 90.0; // Start of cloud layer
// float cloudMaxDepth = 110.0; // End of cloud layer
// if (closestDepth < cloudMinDepth || closestDepth > cloudMaxDepth) return 1.0; // No shadow outside range
// return currentDepth > closestDepth ? 0.5 : 1.0; // Partial shadow within range
//}
//----prepass shadow depth for alpha clip shadows
//float sampleShadowMap(vec3 worldPos) { // custom prepass
// // Project world position to light's view (simplified for camera-aligned depth)
// vec4 lightSpacePos = pMatrix * vMatrix * vec4(worldPos, 1.0);
// vec3 projCoords = lightSpacePos.xyz / lightSpacePos.w;
// projCoords = projCoords * 0.5 + 0.5; // Convert to [0,1]
// if (projCoords.z > 1.0 || projCoords.x < 0.0 || projCoords.x > 1.0 || projCoords.y < 0.0 || projCoords.y > 1.0) return 1.0;
// float closestDepth = texture2D(shadowMap, projCoords.xy).r; // Depth from render target
// float currentDepth = projCoords.z - shadowBias;
// return currentDepth > closestDepth ? 0.5 : 1.0; // Partial shadow
//}
float sampleShadowMap(vec3 worldPos) {
vec3 lightToFrag = worldPos - lightPosition;
float currentDepth = length(lightToFrag) - shadowBias;
float closestDepth = textureCube(shadowMap, lightToFrag).r * 150.0; // Scale to far plane
return currentDepth > closestDepth ? 0.5 : 1.0; // Partial shadow
}
void main() {
// Sample diffuse texture
//vec4 diffuseColor = texture2D(diffuseMap, vUv);
// Sample textures
vec4 nightEmit = texture2D(diffuseMapNightEmit, vUv);
vec4 nightColor = texture2D(diffuseMapNight, vUv);
vec4 dayColor = mix(texture2D(diffuseMapJuly, vUv), texture2D(diffuseMapDecember, vUv), isWinter);
vec4 aquaColor = texture2D(diffuseMapAqua, vUv);
vec3 normalMap = texture2D(normalMap, vUv).xyz * 2.0 - 1.0; // [-1,1]
normalMap.x = -normalMap.x; // Flip X axis
vec4 emitColor = texture2D(earth_night_emit, vUv);
// Bump mapping with normal map
vec3 bumpNormal = normalize(vNormal + normalMap * 0.3); // Adjust bump strength
// Lighting (view space)
vec3 lightDir = normalize(viewSunPos.xyz - vViewPosition.xyz);
float dist = length((viewMatrix * vec4(lightPosition, 1.0)).xyz - vViewPosition);
float attenuation = 1.0 / (1.0 + 0.01 * dist * dist);
float nightFactor = 1.0 - max(dot(bumpNormal, lightDir), 0.0); // 0 = day, 1 = night
nightFactor = smoothstep(0.2, 0.8, nightFactor); // Soften transition
// Day/night blend
vec4 diffuseColor = mix(dayColor, nightColor, nightFactor);
// ---- water begin
// Water/land detection
float isWater = step(0.1, length(aquaColor.rgb)); // Non-black = water
//float waterDepth = 1.0-((aquaColor.r+aquaColor.g+aquaColor.b)/3.0)*.2;
float waterDepth = ((aquaColor.r+aquaColor.g+aquaColor.b)/3.0)*.33;
vec3 pos = normalize(vPosition);
// Base scale increased for higher resolution (finer details)
float baseScale = 20.0; // Doubled from 5.0 for more detail
float detailScale = 240.0; // High-frequency detail layer
// Three main flow layers with increased frequency
vec3 flow1 = pos * baseScale + vec3(time * 0.01);
vec3 flow2 = pos * baseScale + vec3(time * 0.07);
vec3 flow3 = pos * baseScale - vec3(time * 0.15, time * 0.05, 0.0);
// Additional high-frequency detail layer
vec3 flowDetail = pos * detailScale + vec3(time * 0.2);
// Generate noise layers
float n1 = noise4D(flow1, time * 0.01);
float n2 = noise4D(flow2, time * 0.07);
float n3 = noise4D(flow3, time * 0.15);
float nDetail = noise4D(flowDetail, time * 0.2);
// Combine noise layers with detail
float n = (n2 + n3 + n1) * 0.5 + nDetail * 0.2; // Detail contributes subtly
// Procedural water effect
vec2 waterUv = vUv * 10.0 + vec2(time * 0.0015, 0.0); // Slow ripple
float waterNoise = n * 0.2; // Subtle amplitude
//vec3 waterColor = vec3(0.0, 0.8, 1.0) * (0.2 + (waterNoise * .8)) * isWater; // Bright cyan tint
vec3 waterColor = vec3(0.0, 0.8, 1.0) * (waterDepth + (waterNoise * .8)) * isWater; // Bright cyan tint
vec3 waterOffset = vec3(0.0, waterNoise, 0.0) * isWater; // Apply only to water
vec3 waterBumpNormal = normalize(bumpNormal + waterOffset);
// ---- water end
float lightIntensity = 200.0;
// Lighting calculation with water effect
//float diffuse = max(dot(waterBumpNormal, lightDir * (2.0 * (1.0 - isWater))), 0.2); // Minimum light for night glow
float diffuse = max(dot(waterBumpNormal, lightDir), 0.05) * attenuation * lightIntensity; // ambient
//float shadowFactor = sampleShadowMap(vViewPosition);
//diffuse *= shadowFactor; // Apply shadow
diffuse = smoothstep(0.0, 0.6, diffuse); // Softer transition, wider lit area
// Specular (Phong model)
vec3 vViewDir = normalize(-vViewPosition);
float rim = 1.0 - max(dot(bumpNormal, vViewDir), 0.05); // Backside intensity
rim = smoothstep(0.5, 1.0, rim) * 0.3; // Limit and scale rim effect
vec3 lighting = (diffuseColor.rgb + waterColor) * (diffuse + rim);
vec3 reflectDir = reflect(-lightDir, bumpNormal);
// Specular (Phong model)
float spec = pow(max(dot(vViewDir, reflectDir), 0.0), 32.0); // Shininess = 32
float gloss = mix(0.1, 0.5, isWater); // Water glossy, land matte
float specular = spec * gloss * attenuation * lightIntensity * 1.0;
// Emission (only at night)
float test = ((nightFactor * 2.0) - 1.0) * 1.0;
vec3 emit = clamp(emitColor.rgb * test, 0.0, 1.0);
//emit = smoothstep(0.0, 0.6, emit); // Softer transition, wider lit area
// Combine
vec3 color = lighting + vec3(1.0, 1.0, 1.0) * specular + emit;
//vec3 color = (diffuseColor.rgb + waterColor) * diffuse + vec3(1.0, 1.0, 1.0) * specular + emit;
//gl_FragColor = vec4(vec3(shadowFactor / 1000.0), 1.0); // Debug depth
gl_FragColor = vec4(color, 1.0);
}
`;
const vertexShaderMoon = `
uniform mat4 mvpMatrix;
uniform mat3 nMatrix; // CPU-computed normal matrix
uniform mat4 vMatrix; // View matrix (for view-space lighting)
uniform vec3 lightPosition;
varying vec2 vUv;
varying vec3 vNormal;
varying vec3 vPosition;
varying vec3 vViewPosition;
varying vec4 viewSunPos;
void main() {
vUv = uv;
vNormal = normal;
vPosition = normal;
vViewPosition = (modelViewMatrix * vec4(position, 1.0)).xyz;
vec3 worldNormal = normalize(nMatrix * normal);
vNormal = normalize((vMatrix * vec4(worldNormal, 0.0)).xyz);
viewSunPos = vMatrix * vec4(lightPosition, 1.0);
gl_Position = mvpMatrix * vec4(position, 1.0);
}
`;
const fragmentShaderMoon = `
uniform sampler2D diffuseMap;
uniform sampler2D normalMap;
uniform vec3 lightPosition;
//uniform mat4 vMatrix; // View matrix (for view-space lighting)
varying vec2 vUv;
varying vec3 vNormal;
varying vec3 vPosition;
varying vec3 vViewPosition;
varying vec4 viewSunPos;
void main() {
vec4 diffuseColor = texture2D(diffuseMap, vUv);
// Sample normal map and convert to normal
vec3 normalMap = texture2D(normalMap, vUv).xyz * 2.0 - 1.0; // Convert [0,1] to [-1,1]
normalMap.x = -normalMap.x; // Flip X axis
vec3 bumpNormal = normalize(vNormal + normalMap * 0.5); // Adjust bump strength
vec3 lightDir = normalize(viewSunPos.xyz - vViewPosition.xyz);
float diffuse = max(dot(bumpNormal, lightDir * 1.5), 0.1);// ambient
diffuse = smoothstep(0.0, 0.6, diffuse); // Softer transition, wider lit area
// Specular (Phong model)
vec3 vViewDir = normalize(-vViewPosition);
float rim = 1.0 - max(dot(bumpNormal, vViewDir), 0.05); // Backside intensity
rim = smoothstep(0.5, 1.0, rim) * 0.3; // Limit and scale rim effect
//vec3 baseColor = vec3(0.6, 0.6, 0.6); // Adjust base color
vec3 lighting = diffuseColor.rgb * (diffuse + rim);
vec3 reflectDir = reflect(-lightDir, bumpNormal);
float spec = pow(max(dot(vViewDir, reflectDir), 0.0), 16.0); // Lower shininess for moon
float specular = spec * 0.1;
vec3 color = lighting + specular;
gl_FragColor = vec4(color, 1.0);
}
`;
const vertexShaderClouds = `
uniform mat4 mvpMatrix;
varying vec3 vPosition;
varying vec3 vLocalPosition;
uniform mat3 nMatrix; // CPU-computed normal matrix
uniform mat4 vMatrix; // View matrix (for view-space lighting)
varying vec3 vViewPosition; // View-space position
varying vec2 TexCoord;
varying vec3 v_Normal;
uniform vec3 lightPosition; // Sun at (0, 0, 0)
//varying vec3 v_vertToLight;
varying vec4 viewSunPos;
void main() {
vLocalPosition = position;
vPosition = (modelMatrix * vec4(position, 1.0)).xyz;
vViewPosition = (modelViewMatrix * vec4(position, 1.0)).xyz; // View-space position
TexCoord = vec2(vPosition.x, vPosition.z);
vec3 worldNormal = normalize(nMatrix * normal);
v_Normal = normalize((vMatrix * vec4(worldNormal, 0.0)).xyz);
//vec4 viewSunPos = vMatrix * vec4(lightPosition, 1.0);
//v_vertToLight = normalize(viewSunPos.xyz - vViewPosition.xyz);
viewSunPos = vMatrix * vec4(lightPosition, 1.0);
gl_Position = mvpMatrix * vec4(position, 1.0);
}
`;
const fragmentShaderClouds = `
uniform float time;
//uniform mat4 invView; // Inverse view matrix
//uniform mat4 invProjection; // Inverse view matrix
varying vec3 vPosition;
varying vec3 vLocalPosition;
varying vec3 vViewPosition;
varying vec2 TexCoord;
varying vec3 v_Normal;
//varying vec3 v_vertToLight;
varying vec4 viewSunPos;
const float PI = 3.1415926535897932384626433832795;
// Noise functions
float random(vec3 p) {
return fract(sin(dot(p, vec3(12.9898, 78.233, 45.5432))) * 43758.5453123);
}
float noise(vec3 p) {
vec3 i = floor(p);
vec3 f = fract(p);
vec3 u = f * f * (3.0 - 2.0 * f);
return mix(mix(mix(random(i), random(i + vec3(1.0, 0.0, 0.0)), u.x),
mix(random(i + vec3(0.0, 1.0, 0.0)), random(i + vec3(1.0, 1.0, 0.0)), u.x), u.y),
mix(mix(random(i + vec3(0.0, 0.0, 1.0)), random(i + vec3(1.0, 0.0, 1.0)), u.x),
mix(random(i + vec3(0.0, 1.0, 1.0)), random(i + vec3(1.0, 1.0, 1.0)), u.x), u.y), u.z);
}
float fbm(vec3 p) {
float v = 0.0;
float a = 0.5;
vec3 shift = vec3(100.0);
for (int i = 0; i < 6; ++i) {
v += a * noise(p);
p = p * 2.0 + shift;
a *= 0.5;
}
return v;
}
//float linearizeDepth(float depth) {
// float near = 0.1;
// float far = 500.0; // Adjust to match your camera
// float z = depth * 2.0 - 1.0; // NDC
// return 2.0 * near * far / (far + near - z * (far - near));
//}
//
//vec3 getWorldPosition(float depth, vec2 uv) {
// float z = linearizeDepth(depth);
// vec4 clipSpace = vec4(uv * 2.0 - 1.0, z, 1.0);
// vec4 viewSpace = invProjection * clipSpace;
// viewSpace /= viewSpace.w;
// vec4 worldSpace = invView * viewSpace;
// return worldSpace.xyz;
//}
void main() {
// Noise for cloud pattern
vec3 pos = normalize(vLocalPosition);
//float loopedRealtime = mod(time, 365.0 * 24.0 * 3600.0); // 365 days
//float globalAngle = (loopedRealtime / (365.0 * 24.0 * 3600.0)) * 2.0 * PI;
//float localAngle = globalAngle; // Optional latitude factor if desired
//-- float cosR = cos(localAngle);
//-- float sinR = sin(localAngle);
//-- vec3 pos2 = vec3(
//-- cosR * pos.x + sinR * pos.z,
//-- pos.y,
//-- -sinR * pos.x + cosR * pos.z
//-- );
float scale = 10.0;
vec3 flow1 = pos * scale + vec3(time * 0.10);
vec3 flow2 = pos * scale - vec3(time * 0.06);
vec3 flow3 = pos * scale + vec3(time * 0.03);
float n1 = noise(flow1);
float n2 = noise(flow2);
float n3 = noise(flow3);
float baseNoise = (n2 + n3 - n1) * 0.5; // Base detail
float detailNoise = fbm(pos * scale * 10.0 + vec3(time * 0.01)); // Smudged variation
float cloudDensity = smoothstep(0.3, .7, baseNoise + detailNoise * 0.3); // Increased coverage
vec3 cloudColor = vec3(.5, .5, .5);
float alpha = cloudDensity * 0.9;
// Edge tint based on alpha and nightFactor (approximated from light)
vec3 lightDir = normalize(viewSunPos.xyz - vViewPosition.xyz);
float edgeFactor = smoothstep(0.2, 0.8, 1.0 - alpha); // Higher alpha = less edge
float nightFactor = 1.0 - max(dot(v_Normal, lightDir), 0.0); // Approx. day/night
nightFactor = smoothstep(0.2, 0.8, nightFactor);
vec3 edgeTint = mix(
vec3(0.2, 0.2, 0.2), // Grey
mix(
vec3(0.5, 0.6, 0.7),
vec3(0.8, 0.5, 0.5),
nightFactor * 5.0), // Blue to red
0.5 - (nightFactor * .25)); // Darker at edges
cloudColor = mix(cloudColor, edgeTint, edgeFactor) * 2.0; // Apply tint
//vec3 P = getWorldPosition(vPosition.z, TexCoord);
// Bump mapping (fake height from green channel)
float height = ((cloudColor.r+cloudColor.g+cloudColor.b)/3.0) * 1.0;
vec2 uvOffset = vec2(dFdx(TexCoord.x), dFdy(TexCoord.y)) * height * 10.0; // Approximate normal perturbation
vec3 bumpNormal = normalize(v_Normal + vec3(uvOffset, 0.0));
float diffuse = max(dot(bumpNormal, lightDir * 1.5), 0.05);// ambient
diffuse = smoothstep(0.0, 0.3, diffuse); // Softer transition, wider lit area
// Specular (Phong model)
vec3 vViewDir = normalize(-vViewPosition);
float rim = 1.0 - max(dot(bumpNormal, vViewDir), 0.05); // Backside intensity
rim = smoothstep(0.5, 1.0, rim) * 0.1; // Limit and scale rim effect
vec3 lighting = cloudColor.rgb * (diffuse + rim);
vec3 reflectDir = reflect(-lightDir, bumpNormal);
float spec = pow(max(dot(vViewDir, reflectDir), 0.0), 16.0); // Lower shininess for moon
float specular = spec * 0.1;
vec3 color = lighting + specular;
//vec3 testColor = vec3(0,.5,0);
// Apply lighting to color
//testColor *= lightIntensity;
//uniform float alphaThreshold;
float alphaThreshold = 0.3;
//if (alpha < alphaThreshold) discard;
gl_FragColor = vec4(color, alpha);
//gl_FragColor = vec4(cloudColor, alpha);
//gl_FragColor = vec4(cloudColor, 1);
//gl_FragColor = vec4(v_Normal, 1);
//gl_FragColor = vec4(testColor, 1);
//gl_FragColor = vec4(P, alpha);
}
`;
const vertexShaderAtmosphere = `
uniform mat4 mvpMatrix;
varying vec3 vPosition;
varying vec3 vViewPosition; // View-space position
uniform mat3 nMatrix; // CPU-computed normal matrix
uniform mat4 vMatrix; // View matrix (for view-space lighting)
varying vec3 vViewDir;
varying vec2 TexCoord;
varying vec3 v_Normal;
uniform vec3 lightPosition; // Sun at (0, 0, 0)
varying vec3 v_vertToLight;
void main() {
vPosition = (modelMatrix * vec4(position, 1.0)).xyz;
vViewPosition = (modelViewMatrix * vec4(position, 1.0)).xyz; // View-space position
TexCoord = vec2(vPosition.x, vPosition.z);
vec3 worldNormal = normalize(nMatrix * normal);
v_Normal = normalize((vMatrix * vec4(worldNormal, 0.0)).xyz);
//v_Normal = normalize((vMatrix * vec4(mat3(0, 0, -1, 0, 1, 0, 1, 0, 0) * worldNormal, 0.0)).xyz);
vec4 viewSunPos = vMatrix * vec4(lightPosition, 1.0);
v_vertToLight = normalize(viewSunPos.xyz - vViewPosition.xyz);
//vViewDir = normalize(cameraPosition - vPosition);
vViewDir = normalize(-vViewPosition);
gl_Position = mvpMatrix * vec4(position, 1.0);
}
`;
const fragmentShaderAtmosphere = `
uniform vec3 lightPosition; // Sun at (0, 0, 0)
varying vec3 vPosition;
varying vec3 vViewDir; // fresnel
varying vec2 TexCoord;
varying vec3 v_Normal;
varying vec3 v_vertToLight;
void main() {
// Surface distance (from Earth center, radius 100)
float surfaceDist = length(v_vertToLight);
float atmosphereThickness = 10.0; // Thin layer beyond Earth radius
float depthFactor = clamp((surfaceDist - 100.0) / atmosphereThickness, 0.0, 1.0); // 0 at surface, 1 at edge
// Lighting calculation (view space)
vec3 fragToLight = normalize(v_vertToLight);
float lightIntensity = max(dot(v_Normal, fragToLight), 0.0);
// Front-face volumetric effect
float frontFactor = 1.0 - dot(v_Normal, vViewDir); // 1 when facing camera, 0 when edge-on
frontFactor = smoothstep(0.0, 0.3, frontFactor); // Fade out at edges
vec3 volumetricColor = vec3(0.0, 0.5, 0.8) * depthFactor * frontFactor; // Blue tint
// Backside Fresnel effect
float fresnel = pow(1.0 - abs(dot(v_Normal, vViewDir)), 2.0);
fresnel = clamp(fresnel, 0.1, 1.0);
vec3 fresnelColor = vec3(0.2, 0.6, 1.0) * fresnel; // Bright blue outline
// Combine effects
vec3 color = ((volumetricColor + fresnelColor) * 1.) * clamp(lightIntensity * 1.5, 0.1, 1.8);
// Transparency based on light and fresnel
float alpha = ((fresnel + frontFactor) * 1.) * clamp(lightIntensity * 1.5, 0.4, 1.8);
gl_FragColor = vec4(color, alpha);
//gl_FragColor = vec4(fresnelColor, 1.0); // debug
//gl_FragColor = vec4(color, 1);
}
`;
const vertexShaderAsteroid = `
uniform mat4 mvpMatrix;
uniform mat3 nMatrix; // CPU-computed normal matrix
uniform mat4 vMatrix; // View matrix (for view-space lighting)
varying vec3 vNormal;
varying vec3 vPosition;
uniform vec3 lightPosition;
varying vec3 vViewPosition;
varying vec4 viewSunPos;
varying vec2 vUv;
void main() {
//vNormal = normal;
vec3 worldNormal = normalize(nMatrix * normal);
vNormal = normalize((vMatrix * vec4(worldNormal, 0.0)).xyz);
vPosition = position;
vUv = uv;
vViewPosition = (modelViewMatrix * vec4(position, 1.0)).xyz;
viewSunPos = vMatrix * vec4(lightPosition, 1.0);
//lPosition = (modelMatrix * vec4(position, 1.0)).xyz;
gl_Position = mvpMatrix * vec4(position, 1.0);
}
`;
const fragmentShaderAsteroid= `
uniform float time;
varying vec3 vNormal;
varying vec3 vPosition;
varying vec3 vViewPosition;
varying vec4 viewSunPos;
varying vec2 vUv;
const float PI = 3.1415926535897932384626433832795;
float random(vec3 p) {
return fract(sin(dot(p, vec3(12.9898, 78.233, 45.5432))) * 43758.5453123);
}
float noise(vec3 p) {
vec3 i = floor(p);
vec3 f = fract(p);
vec3 u = f * f * (3.0 - 2.0 * f);
float n0 = random(i);
float n1 = random(i + vec3(1.0, 0.0, 0.0));
float n2 = random(i + vec3(0.0, 1.0, 0.0));
float n3 = random(i + vec3(1.0, 1.0, 0.0));
float n4 = random(i + vec3(0.0, 0.0, 1.0));
float n5 = random(i + vec3(1.0, 0.0, 1.0));
float n6 = random(i + vec3(0.0, 1.0, 1.0));
float n7 = random(i + vec3(1.0, 1.0, 1.0));
return mix(
mix(mix(n0, n1, u.x), mix(n2, n3, u.x), u.y),
mix(mix(n4, n5, u.x), mix(n6, n7, u.x), u.y),
u.z
);
}
float noise4D(vec3 p, float t) {
return noise(p + vec3(0.0, 0.0, t)); // Use Z for time dimension
}
void main() {
//vec2 uv = vNormal.xy * 0.5 + 0.5;
vec2 uv = vUv * 0.5 + 0.5;
vec3 pos=normalize(vPosition);
float n = noise4D(pos * 10.0, time); // Lower scale to reduce noise grain
vec3 color = mix(vec3(0.34, 0.29, 0.24), vec3(0.49, 0.47, 0.44), smoothstep(0.0, 1.0, n)); // Smoother transition
// Bump mapping (fake height from green channel)
float height = ((color.r+color.g+color.b)/3.0) * 1.0;
vec2 uvOffset = vec2(dFdx(uv.x), dFdy(uv.y)) * height * 10.0; // Approximate normal perturbation
vec3 bumpNormal = normalize(vNormal + vec3(uvOffset, 0.0));
vec3 lightDir = normalize(viewSunPos.xyz - vViewPosition.xyz);
float diffuse = max(dot(bumpNormal, lightDir * 1.5), 0.1);// ambient
diffuse = smoothstep(0.0, 0.6, diffuse); // Softer transition, wider lit area
// Specular (Phong model)
vec3 vViewDir = normalize(-vViewPosition);
float rim = 1.0 - max(dot(bumpNormal, vViewDir), 0.05); // Backside intensity
rim = smoothstep(0.5, 1.0, rim) * 0.3; // Limit and scale rim effect
//vec3 baseColor = vec3(0.6, 0.6, 0.6); // Adjust base color
vec3 lighting = color.rgb * (diffuse + rim);
vec3 reflectDir = reflect(-lightDir, bumpNormal);
float spec = pow(max(dot(vViewDir, reflectDir), 0.0), 16.0); // Lower shininess for moon
float specular = spec * 0.1;
color = lighting + specular;
//color = color.rgb * diffuse + vec3(1.0, 1.0, 1.0) * specular;
//gl_FragColor = vec4(height, 0.0, 0.0, 1.0);
gl_FragColor = vec4(color, 1.0);
}
`;
//
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//
const rad = Math.PI / 180;
function createGlowTexture() {
const canvas = document.createElement('canvas');
canvas.width = 64;
canvas.height = 64;
const ctx = canvas.getContext('2d');
const gradient = ctx.createRadialGradient(32, 32, 0, 32, 32, 32);
gradient.addColorStop(0, 'rgba(255, 255, 255, 1)');
gradient.addColorStop(1, 'rgba(255, 255, 255, 0)');
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, 64, 64);
return new THREE.CanvasTexture(canvas);
}
const glowTexture = createGlowTexture();
// Create star field sphere
//const starGeometry = new THREE.SphereGeometry(500, 64, 64); // Large enough to enclose scene
const starMaterial = new THREE.ShaderMaterial({
vertexShader: vertexShaderStars,
fragmentShader: fragmentShaderStars,
uniforms: {
mvpMatrix: { value: new THREE.Matrix4() }
},
//side: THREE.BackSide // Render on inner surface
transparent: true
});
const starCount = 10000;
const starGeometry = new THREE.BufferGeometry();
const positions = new Float32Array(starCount * 3);
for (let i = 0; i < starCount; i++) {
const theta = Math.random() * 2 * Math.PI;
const phi = Math.acos(2 * Math.random() - 1);
const r = 500; // Starfield radius
positions[i * 3] = r * Math.sin(phi) * Math.cos(theta);
positions[i * 3 + 1] = r * Math.sin(phi) * Math.sin(theta);
positions[i * 3 + 2] = r * Math.cos(phi);
}
starGeometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
const starField = new THREE.Points(starGeometry, starMaterial);
//const starField = new THREE.Mesh(starGeometry, starMaterial);
scene.add(starField);
const glowMaterial = new THREE.ShaderMaterial({
vertexShader: vertexShaderGlow,
fragmentShader: fragmentShaderGlow,
uniforms: {
glowTexture: { value: glowTexture },
mvpMatrix: { value: new THREE.Matrix4() }
},
//side: THREE.BackSide // Render on inner surface
transparent: true,
blending: THREE.AdditiveBlending // Enhance glow effect
});
const glowCount = 2000; // Fewer points for glow to optimize performance
const glowGeometry = new THREE.BufferGeometry();
const glowPositions = new Float32Array(glowCount * 3);
for (let i = 0; i < glowCount; i++) {
const theta = Math.random() * 2 * Math.PI;
const phi = Math.acos(2 * Math.random() - 1);
const r = 500; // Same radius as starfield
glowPositions[i * 3] = r * Math.sin(phi) * Math.cos(theta);
glowPositions[i * 3 + 1] = r * Math.sin(phi) * Math.sin(theta);
glowPositions[i * 3 + 2] = r * Math.cos(phi);
}
glowGeometry.setAttribute('position', new THREE.BufferAttribute(glowPositions, 3));
const glowField = new THREE.Points(glowGeometry, glowMaterial);
scene.add(glowField);
// Texture loader
const textureLoader = new THREE.TextureLoader();
// Sun
//const sunTexture = textureLoader.load('sun_1024.jpg');
const sunGeometry = new THREE.SphereGeometry(15, 64, 32);
//const sunMaterial = new THREE.MeshBasicMaterial({ map: sunTexture });
// Create the shader material
const sunMaterial = new THREE.ShaderMaterial({
uniforms: {
time: { value: 0.0 },
realtime: { value: 0.0 },
mvpMatrix: { value: new THREE.Matrix4() }
},
vertexShader: vertexShaderSun,
fragmentShader: fragmentShaderSun
});
const sun = new THREE.Mesh(sunGeometry, sunMaterial);
sun.position.set(0, 0, 0);
sun.rotation.z = -23.44 * rad;
sun.receiveShadow = false;
sun.castShadow = false;
scene.add(sun);
// Flame geometry (simple spikes)
function createFlames(count) {
const flameGeometry = new THREE.BufferGeometry();
const positions = [];
const scales = [];
for (let i = 0; i < count; i++) {
const theta = Math.random() * Math.PI * 2;
const phi = Math.acos(2 * Math.random() - 1); // Uniform on sphere
const x = Math.sin(phi) * Math.cos(theta);
const y = Math.sin(phi) * Math.sin(theta);
const z = Math.cos(phi);
// Spike from surface outward
positions.push(0, 0, 0); // Base at surface (will be offset to sun surface)
positions.push(x * 2, y * 2, z * 2); // Tip extends 2 units
scales.push(1.0); // Initial scale
}
flameGeometry.setAttribute('position', new THREE.BufferAttribute(new Float32Array(positions), 3));
flameGeometry.setAttribute('scale', new THREE.BufferAttribute(new Float32Array(scales), 1));
const flameMaterial = new THREE.LineBasicMaterial({ color: 0xff4500, transparent: true }); // Orange-red, transparent for fade
const flames = new THREE.LineSegments(flameGeometry, flameMaterial);
flames.position.copy(sun.position); // Ensure flames start at sun position
flames.frustumCulled = false;
scene.add(flames);
return flames;
}
const flames = createFlames(20); // 20 random flames
let flameTime = 0.0;
//console.dir(flames.geometry.attributes.position.array);
// Earth
//const earthTexture = textureLoader.load('earth_atmos_2048.jpg');
const earthGeometry = new THREE.SphereGeometry(1, 128, 64);
//const earthMaterial = new THREE.MeshPhongMaterial({ map: earthTexture });
const earthMaterial = new THREE.ShaderMaterial({
vertexShader: vertexShaderEarth,
fragmentShader: fragmentShaderEarth,
uniforms: {
diffuseMapNightEmit: { value: new THREE.TextureLoader().load('textures/earth_night_emit.png') },
diffuseMapNight: { value: new THREE.TextureLoader().load('textures/earth_night.jpg') },
//diffuseMapJuly: { value: new THREE.TextureLoader().load('textures/earth_july_aqua.jpg') },
diffuseMapJuly: { value: new THREE.TextureLoader().load('textures/earth_july.jpg') },
//diffuseMapDecember: { value: new THREE.TextureLoader().load('textures/earth_december_aqua.jpg') },
diffuseMapDecember: { value: new THREE.TextureLoader().load('textures/earth_december.jpg') },
diffuseMapAqua: { value: new THREE.TextureLoader().load('textures/earth_aqua.jpg') },
normalMap: { value: new THREE.TextureLoader().load('textures/earth_normal.png') },
lightPosition: { value: new THREE.Vector3(0, 0, 0) },
isWinter: { value: 0.0 },
time: { value: 0.0 },
shadowMap: { value: null }, // Will be set from light
//lightSpaceMatrix: { value: new THREE.Matrix4() },
shadowBias: { value: 0.005 },
nMatrix: { value: new THREE.Matrix3() }, // Initialize empty mat3
vMatrix: { value: new THREE.Matrix4() }, // View matrix
mvpMatrix: { value: new THREE.Matrix4() }
}
});
const earth = new THREE.Mesh(earthGeometry, earthMaterial);
earth.position.set(100, 0, 0);
earth.castShadow = true;
earth.receiveShadow = true;
scene.add(earth);
// Atmosphere sphere setup
const atmGeometry = new THREE.SphereGeometry(1.02, 128, 64); // Slightly larger than clouds
const atmMaterial = new THREE.ShaderMaterial({
vertexShader: vertexShaderAtmosphere, // Atmosphere vertex shader
fragmentShader: fragmentShaderAtmosphere, // Atmosphere fragment shader
uniforms: {
lightPosition: { value: new THREE.Vector3(0, 0, 0) }, // Sun at origin
nMatrix: { value: new THREE.Matrix3() },
vMatrix: { value: new THREE.Matrix4() },
mvpMatrix: { value: new THREE.Matrix4() }
},
transparent: true,
blending: THREE.AdditiveBlending, // Enhance glow effect
side: THREE.DoubleSide, // Render both views
//side: THREE.BackSide, // Render on inner surface for better glow
});
const atmosphere = new THREE.Mesh(atmGeometry, atmMaterial);
atmosphere.castShadow = false;
atmosphere.receiveShadow = false;
scene.add(atmosphere);
// Create cloud sphere
const cloudGeometry = new THREE.SphereGeometry(1.01, 128, 64); // Slightly larger than Earth (radius 100)
const cloudMaterial = new THREE.ShaderMaterial({
vertexShader: vertexShaderClouds,
fragmentShader: fragmentShaderClouds,
uniforms: {
time: { value: 0.0 },
lightPosition: { value: new THREE.Vector3(0, 0, 0) }, // Sun at origin
nMatrix: { value: new THREE.Matrix3() }, // Initialize empty mat3
vMatrix: { value: new THREE.Matrix4() }, // View matrix
//invView: { value: new THREE.Matrix4() } // Inverse view matrix
mvpMatrix: { value: new THREE.Matrix4() }
},
transparent: true, // Disable blending for clipping
alphaTest: 0.3, // Threshold for clipping
side: THREE.DoubleSide
});
const clouds = new THREE.Mesh(cloudGeometry, cloudMaterial);
clouds.castShadow = true;
clouds.receiveShadow = true;
scene.add(clouds);
earth.renderOrder = 0; // Render first
clouds.renderOrder = 1; // Render second
atmosphere.renderOrder = 2; // Render last
// Create a render target and depth material
const shadowMapSize = 1024;
const pars = {
minFilter: THREE.LinearFilter,
magFilter: THREE.LinearFilter,
format: THREE.RGBAFormat
};
const renderTargetDepth = new THREE.WebGLRenderTarget(shadowMapSize, shadowMapSize, pars);
const depthMaterial = new THREE.MeshDepthMaterial({
depthPacking: THREE.RGBADepthPacking,
alphaTest: 0.3 // Match cloud's alphaTest to clip correctly
});
// Store original cloud material for restoration
const originalCloudMaterial = clouds.material;
// Function to set up depth pass objects
function setupDepthPass() {
scene.traverse((object) => {
if (object === clouds) {
object.material = depthMaterial; // Apply depth material to clouds
} else if (object === earth ||
object === atmosphere ||
object === sun) {
object.visible = false; // Hide Earth during depth pass
}
});
}
// Cube camera for PointLight depth
const cubeRenderTarget = new THREE.WebGLCubeRenderTarget(shadowMapSize, { format: THREE.RGBAFormat });
const cubeCamera = new THREE.CubeCamera(0.5, 150.0, cubeRenderTarget); // Near/far match light
cubeCamera.position.copy(sunLight.position);
// Function to restore original materials
function restoreMaterials() {
scene.traverse((object) => {
if (object === clouds) {
object.material = originalCloudMaterial;
} else if (object === earth ||
object === atmosphere ||
object === sun) {
object.visible = true;
}
});
}
// Moon
//const moonTexture = textureLoader.load('moon_1024.jpg');
const moonGeometry = new THREE.SphereGeometry(0.27, 48, 32);
//const moonMaterial = new THREE.MeshPhongMaterial({ map: moonTexture });
const moonMaterial = new THREE.ShaderMaterial({
vertexShader: vertexShaderMoon,
fragmentShader: fragmentShaderMoon,
uniforms: {
diffuseMap: { value: new THREE.TextureLoader().load('textures/moon_diffuse_2k.png') },
normalMap: { value: new THREE.TextureLoader().load('textures/moon_normal_2k.png') },
lightPosition: { value: new THREE.Vector3(0, 0, 0) },
nMatrix: { value: new THREE.Matrix3() }, // Initialize empty mat3
vMatrix: { value: new THREE.Matrix4() }, // View matrix
mvpMatrix: { value: new THREE.Matrix4() }
}
});
const moon = new THREE.Mesh(moonGeometry, moonMaterial);
moon.castShadow = true;
moon.receiveShadow = true;
moon.position.set(105, 0, .5);
scene.add(moon);
// Asteroid MG1
const asteroidGeometry = new THREE.SphereGeometry(0.05, 24, 16);
//const asteroidMaterial = new THREE.MeshBasicMaterial({ color: 0xaaaaaa });
const asteroidMaterial = new THREE.ShaderMaterial({
vertexShader: vertexShaderAsteroid,
fragmentShader: fragmentShaderAsteroid,
uniforms: {
mvpMatrix: { value: new THREE.Matrix4() },
nMatrix: { value: new THREE.Matrix3() }, // Initialize empty mat3
vMatrix: { value: new THREE.Matrix4() }, // View matrix
time: { value: 0.0 }
}
});
const asteroid = new THREE.Mesh(asteroidGeometry, asteroidMaterial);
//asteroid.position.set(135, -40.7, -65);
scene.add(asteroid);
function randomizeAsteroidShape(geometry) {
const positionAttribute = geometry.attributes.position;
const normalAttribute = geometry.attributes.normal;
const vertex = new THREE.Vector3();
const normal = new THREE.Vector3();
const neighborOffset = new THREE.Vector3();
const poleThreshold = 0.98; // Lock vertices near poles
const seamThreshold = 0.001; // Detect seam vertices
const stretchFactor = 0.3; // Outward stretch bias
const smoothFactor = 0.4; // Smoothing with neighbors
const strength = 20.0;
const originalPositions = new Float32Array(positionAttribute.array); // Store original for output
for (let i = 0; i < positionAttribute.count; i++) {
vertex.fromBufferAttribute(positionAttribute, i);
normal.fromBufferAttribute(normalAttribute, i);
const latitudeFactor = Math.abs(normal.z);
if (latitudeFactor > poleThreshold) {
continue; // Lock poles
}
// Base outward offset with reduced deviation
let offsetMagnitude = (Math.random() - 0.3) * (0.002 * strength); // Range: -0.06 to 0.14 (less deviation)
offsetMagnitude = Math.max(0, offsetMagnitude) * stretchFactor; // Ensure outward bias
// Smooth with neighbors
neighborOffset.set(0, 0, 0);
let neighborCount = 0;
for (let j = 0; j < positionAttribute.count; j++) {
if (i !== j) {
const otherVertex = new THREE.Vector3().fromBufferAttribute(positionAttribute, j);
if (vertex.distanceTo(otherVertex) < 0.5) { // Nearby vertices
neighborOffset.add(otherVertex);
neighborCount++;
}
}
}
if (neighborCount > 0) {
neighborOffset.divideScalar(neighborCount);
const smoothOffset = neighborOffset.clone().sub(vertex).multiplyScalar(smoothFactor);
offsetMagnitude += smoothOffset.length() * (Math.random() > 0.5 ? 1 : -1) * 0.1; // Slight random tweak
}
// Apply offset along normal
const elongationDirection = new THREE.Vector3(1.4, 1.1, 2); // Stretch along Z
const offset = normal.clone().multiplyScalar(offsetMagnitude);
offset.projectOnVector(elongationDirection).multiplyScalar(2.0); // Double stretch along X
// Seam fix
let isSeamVertex = false;
for (let j = 0; j < positionAttribute.count; j++) {
if (i !== j) {
const otherVertex = new THREE.Vector3().fromBufferAttribute(positionAttribute, j);
if (vertex.distanceTo(otherVertex) < seamThreshold && Math.abs(normal.z - normalAttribute.getZ(j)) < 0.1) {
isSeamVertex = true;
offset.add(otherVertex).divideScalar(2); // Average with seam neighbor
break;
}
}
}
if (!isSeamVertex) {
vertex.add(offset);
}
positionAttribute.setXYZ(i, vertex.x, vertex.y, vertex.z);
}
geometry.computeVertexNormals();
geometry.computeBoundingSphere();
// Output modified positions as string/array
const modifiedPositions = Array.from(positionAttribute.array);
const outputString = JSON.stringify(modifiedPositions);
//console.log("Modified Positions:", modifiedPositions);
// Optional prompt for copy-paste
prompt("Copy asteroid vertex data:", outputString);
}
let bakedAsteroid=[0,0.05000000074505806,0,0,0.05000000074505806,0,0,0.05000000074505806,0,0,0.05000000074505806,0,0,0.05000000074505806,0,0,0.05000000074505806,0,0,0.05000000074505806,0,0,0.05000000074505806,0,0,0.05000000074505806,0,0,0.05000000074505806,0,0,0.05000000074505806,0,0,0.05000000074505806,0,0,0.05000000074505806,0,0,0.05000000074505806,0,0,0.05000000074505806,0,0,0.05000000074505806,0,0,0.05000000074505806,0,0,0.05000000074505806,0,0,0.05000000074505806,0,0,0.05000000074505806,0,0,0.05000000074505806,0,0,0.05000000074505806,0,0,0.05000000074505806,0,0,0.05000000074505806,0,0,0.05000000074505806,0,-0.009754516184329987,0.049039263278245926,0,-0.00870803277939558,0.04960034787654877,0.0035448060370981693,-0.006100789178162813,0.05088323354721069,0.008229929953813553,-0.006751130800694227,0.04915425553917885,0.0071065607480704784,-0.004821626469492912,0.04908297210931778,0.00852713268250227,-0.0014414956094697118,0.049890317022800446,0.010969509370625019,-0.00000473903901365702,0.049035537987947464,0.009747746400535107,0.003721174318343401,0.04997938498854637,0.011131453327834606,0.004118083044886589,0.048442769795656204,0.0073631233535707,0.011041965335607529,0.05229564011096954,0.0128181716427207,0.008604726754128933,0.04916267469525337,0.005101640243083239,0.008287036791443825,0.04814739525318146,0.0009030794608406723,0.008691245689988136,0.048203837126493454,-0.0015189583646133542,0.010982540436089039,0.050265293568372726,-0.00029551071929745376,0.009328215382993221,0.04973112791776657,-0.003619320224970579,0.010612435638904572,0.05195815488696098,-0.0015904114115983248,0.005565767642110586,0.04958023503422737,-0.0074640740640461445,0.004859807435423136,0.050874024629592896,-0.006086206529289484,-0.0005390964215621352,0.04861568659543991,-0.01052465382963419,-0.00144057662691921,0.04989103972911835,-0.007873455993831158,-0.004003680776804686,0.049725644290447235,-0.007199691608548164,-0.006654378958046436,0.04923027381300926,-0.006550190970301628,-0.008952256292104721,0.04864279553294182,-0.0055981106124818325,-0.007307030260562897,0.05070113390684128,0.0004969298606738448,-0.009754516184329987,0.049039263278245926,-2.3891674767279894e-18,-0.019134171307086945,0.04619397595524788,0,-0.016878526657819748,0.047453995794057846,0.0072432346642017365,-0.015463707968592644,0.047063738107681274,0.011148471385240555,-0.012323503382503986,0.04714186117053032,0.01525332871824503,-0.008174797520041466,0.04728791490197182,0.018559660762548447,-0.00621374137699604,0.0452028326690197,0.016680113971233368,0.005800742655992508,0.0507517009973526,0.027420947328209877,0.004444079007953405,0.04579466953873634,0.017756177112460136,0.008038359694182873,0.0449928343296051,0.014386783353984356,0.015036406926810741,0.047377657145261765,0.015682050958275795,0.016148554161190987,0.045862309634685516,0.0089640524238348,0.01955985836684704,0.04704071581363678,0.006491815205663443,0.01978149078786373,0.04670258238911629,0.000924742198549211,0.018603436648845673,0.046289242804050446,-0.004779078532010317,0.015705376863479614,0.045514095574617386,-0.010803230106830597,0.014202325604856014,0.04672230780124664,-0.012569297105073929,0.009078631177544594,0.045810189098119736,-0.0172684695571661,0.0052615683525800705,0.04643698036670685,-0.018040360882878304,-0.00019642457482405007,0.0460396409034729,-0.01941477693617344,-0.004844247829169035,0.04627886414527893,-0.018327847123146057,-0.009500396437942982,0.046246375888586044,-0.016475407406687737,-0.013352852314710617,0.046333085745573044,-0.013276973739266396,-0.01670241355895996,0.046090468764305115,-0.009755278937518597,-0.018248897045850754,0.0463772751390934,-0.004619014449417591,-0.019134171307086945,0.04619397595524788,-4.68652038700443e-18,-0.027778511866927147,0.041573479771614075,0,-0.026021510362625122,0.042210280895233154,0.008347424678504467,-0.02214227244257927,0.043077826499938965,0.016624432057142258,-0.0168925691395998,0.04373404011130333,0.02357066422700882,-0.014512797817587852,0.04108355566859245,0.023166121914982796,-0.004317003767937422,0.04383052513003349,0.030935702845454216,0.0043922909535467625,0.0450245663523674,0.03405321389436722,0.008904480375349522,0.042920880019664764,0.029281800612807274,0.020536769181489944,0.0467965267598629,0.0335533432662487,0.027202541008591652,0.04751361161470413,0.03044261410832405,0.026235727593302727,0.043285418301820755,0.017001871019601822,0.031534742563962936,0.045268505811691284,0.013907833956182003,0.03037290647625923,0.043611932545900345,0.003706278745085001,0.03234431892633438,0.045904599130153656,0.0006851570215076208,0.024876846000552177,0.04221772775053978,-0.012717898003757,0.019574852660298347,0.04152042791247368,-0.019738832488656044,0.014660574495792389,0.042179517447948456,-0.022955013439059258,0.007228655740618706,0.04160416126251221,-0.026776200160384178,-0.00015396370145026594,0.04145250841975212,-0.027998460456728935,-0.00765716889873147,0.04120611026883125,-0.027499927207827568,-0.014229200780391693,0.041306380182504654,-0.024542532861232758,-0.01931541971862316,0.04183037206530571,-0.019175296649336815,-0.025303054600954056,0.04059435427188873,-0.015669483691453934,-0.026972223073244095,0.04146328940987587,-0.007389951962977648,-0.027778511866927147,0.041573479771614075,-6.803773123666079e-18,-0.0353553406894207,0.0353553406894207,0,-0.033438097685575485,0.03591518849134445,0.010168543085455894,-0.031102055683732033,0.03497549891471863,0.01698704995214939,-0.025831595063209534,0.03470194712281227,0.023812009021639824,-0.015162907540798187,0.0373312272131443,0.034211140125989914,-0.002792389364913106,0.040351103991270065,0.04323384165763855,0.0028823448810726404,0.03762004151940346,0.039472974836826324,0.014449885115027428,0.039519038051366806,0.04172099009156227,0.019635260105133057,0.03689344599843025,0.03341517597436905,0.026948697865009308,0.036886461079120636,0.02778385393321514,0.03141557797789574,0.035981521010398865,0.018816180527210236,0.0376858189702034,0.038132987916469574,0.014200901612639427,0.03810368850827217,0.03751475736498833,0.0039262110367417336,0.03335634246468544,0.03473125398159027,-0.010285339318215847,0.029883602634072304,0.03477782383561134,-0.018727697432041168,0.026095371693372726,0.03621599078178406,-0.023435184732079506,0.01763973757624626,0.035325534641742706,-0.030672810971736908,0.00930714886635542,0.035478316247463226,-0.03392704203724861,-0.0019026065710932016,0.03386043384671211,-0.0380733497440815,-0.00978921540081501,0.03485359996557236,-0.035062890499830246,-0.01843878999352455,0.03475731611251831,-0.031705934554338455,-0.027192801237106323,0.03363242745399475,-0.028132572770118713,-0.03254544734954834,0.03384140506386757,-0.020430278033018112,-0.03462645784020424,0.03498147800564766,-0.009830385446548462,-0.0353553406894207,0.0353553406894207,-8.65956027255431e-18,-0.041573479771614075,0.027778511866927147,0,-0.040174566209316254,0.02776462770998478,0.01073476392775774,-0.035129331052303314,0.02846550941467285,0.02203582599759102,-0.028655095025897026,0.028361350297927856,0.030456596985459328,-0.021920375525951385,0.026887796819210052,0.03438420966267586,-0.0034575399477034807,0.03351616486907005,0.05058899521827698,0.00596990529447794,0.03246915340423584,0.05010191723704338,0.020152440294623375,0.03515828028321266,0.053574658930301666,0.02705717273056507,0.03270528092980385,0.04496145248413086,0.027346104383468628,0.02616718038916588,0.02646719664335251,0.03515484556555748,0.027111563831567764,0.019574107602238655,0.040744196623563766,0.028239961713552475,0.011599007062613964,0.04621380195021629,0.03142447769641876,0.006629031617194414,0.040228936821222305,0.027835113927721977,-0.010657095350325108,0.03873142600059509,0.02992173470556736,-0.016889972612261772,0.02919197827577591,0.02761751040816307,-0.029689619317650795,0.020332062616944313,0.027421263977885246,-0.03665323182940483,0.011307698674499989,0.028208840638399124,-0.03937448188662529,0.0008272718405351043,0.028428511694073677,-0.04039166122674942,-0.00974445790052414,0.02857644483447075,-0.038706108927726746,-0.019686108455061913,0.0286432933062315,-0.03443136066198349,-0.03418312221765518,0.024017898365855217,-0.03623436763882637,-0.0374394915997982,0.026650384068489075,-0.022837882861495018,-0.04088444262742996,0.027206867933273315,-0.011799359694123268,-0.041573479771614075,0.027778511866927147,-1.0182566043567556e-17,-0.04619397595524788,0.019134171307086945,0,-0.044667165726423264,0.01909707672894001,0.011888435110449791,-0.04017753526568413,0.018998730927705765,0.022850733250379562,-0.031840234994888306,0.019781475886702538,0.03384099155664444,-0.02415510080754757,0.01830279640853405,0.0384935699403286,-0.009637651033699512,0.020955637097358704,0.04793171212077141,-0.0017596088582649827,0.017751621082425117,0.04368025064468384,0.013109750114381313,0.020040784031152725,0.046268340200185776,0.0251859650015831,0.020775509998202324,0.04298941045999527,0.03058600425720215,0.017501400783658028,0.029695400968194008,0.04547550901770592,0.023432303220033646,0.030911773443222046,0.04822121933102608,0.021963736042380333,0.017100544646382332,0.0456976592540741,0.0187442097812891,-0.0007090214057825506,0.044707294553518295,0.01920279674232006,-0.01183110848069191,0.040922775864601135,0.019855158403515816,-0.021786103025078773,0.032641030848026276,0.01911606639623642,-0.03269699588418007,0.023102177307009697,0.01913824863731861,-0.03999774530529976,0.012769953347742558,0.019773799926042557,-0.043456993997097015,-0.003395621431991458,0.016466183587908745,-0.05104486271739006,-0.012935880571603775,0.01836417056620121,-0.04601995646953583,-0.025855474174022675,0.016966789960861206,-0.04394585266709328,-0.0340624675154686,0.018035434186458588,-0.034661781042814255,-0.03874992951750755,0.020120423287153244,-0.021303804591298103,-0.048228587955236435,0.016298817470669746,-0.017111068591475487,-0.04619397595524788,0.019134171307086945,-1.1314260790922793e-17,-0.049039263278245926,0.009754516184329987,0,-0.048477280884981155,0.008883168920874596,0.011108027771115303,-0.04244719073176384,0.009771847166121006,0.024551142007112503,-0.033404476940631866,0.010753567330539227,0.03649245202541351,-0.0247452724725008,0.00957722682505846,0.04214690625667572,-0.00871492363512516,0.01287959422916174,0.05305025354027748,-3.002788873478761e-18,0.009754516184329987,0.049039263278245926,0.014613662846386433,0.011264162138104439,0.05011310055851936,0.026924386620521545,0.011643966659903526,0.04590461403131485,0.03670090064406395,0.0113455131649971,0.03756871819496155,0.040584221482276917,0.008273422718048096,0.021826734766364098,0.04574251547455788,0.008477121591567993,0.010369759984314442,0.05455435439944267,0.014087803661823273,0.007878703996539116,0.048079099506139755,0.010313007980585098,-0.011676856316626072,0.04303576797246933,0.01019963901489973,-0.02371031790971756,0.03483652323484421,0.009880644269287586,-0.03444667160511017,0.02515372633934021,0.010252733714878559,-0.04156339913606644,0.009777696803212166,0.007464474067091942,-0.05153200402855873,9.008366620436283e-18,0.009754516184329987,-0.049039263278245926,-0.015484345145523548,0.007560763042420149,-0.05135693401098251,-0.02880512736737728,0.0063873413018882275,-0.048591382801532745,-0.03301011398434639,0.01106342300772667,-0.03229616582393646,-0.04529714211821556,0.007532600313425064,-0.028559477999806404,-0.0486162006855011,0.008774016052484512,-0.014475023373961449,-0.049039263278245926,0.009754516184329987,-1.2011155493915043e-17,-0.05000000074505806,3.0616169246677665e-18,0,-0.05098101496696472,-0.0021094265393912792,0.009105631150305271,-0.04376108571887016,-0.0003612843865994364,0.02434311993420124,-0.034292273223400116,0.0008352676522918046,0.03687400743365288,-0.02120712399482727,0.0029801172204315662,0.04871966317296028,-0.011701573617756367,0.0009737975778989494,0.050066832453012466,-3.0616169246677665e-18,3.0616169246677665e-18,0.05000000074505806,0.0184783935546875,0.004350847098976374,0.056206922978162766,0.023093633353710175,-0.0014978591352701187,0.04057788848876953,0.037249647080898285,0.001488383742980659,0.03806149214506149,0.04376329854130745,0.0003630236315075308,0.025660043582320213,0.046810757368803024,-0.0011672057444229722,0.01081875991076231,0.05111543834209442,0.0008764150552451611,0.001593481982126832,0.050469428300857544,0.0017074650386348367,-0.009836470708251,0.04347086325287819,0.0001332509855274111,-0.02475772611796856,0.034302446991205215,-0.0008272748091258109,-0.03685947507619858,0.02087235637009144,-0.003243148559704423,-0.049197904765605927,0.009246964007616043,-0.0029024197719991207,-0.05357341840863228,9.184851394388759e-18,3.0616169246677665e-18,-0.05000000074505806,-0.01594308204948902,-0.0023588156327605247,-0.052585046738386154,-0.02690093033015728,-0.0014935871586203575,-0.0460168831050396,-0.03873726353049278,-0.002657224191352725,-0.04018665850162506,-0.04724470153450966,-0.003098410554230213,-0.0306334737688303,-0.05077118054032326,-0.0019445557845756412,-0.016476508229970932,-0.05000000074505806,3.0616169246677665e-18,-1.2246467698671066e-17,-0.049039263278245926,-0.009754516184329987,0,-0.050021201372146606,-0.01183894369751215,0.008902426809072495,-0.04373304173350334,-0.010747496038675308,0.02271421253681183,-0.03392739221453667,-0.009166328236460686,0.03574543073773384,-0.02140050195157528,-0.0073037706315517426,0.046925149857997894,-0.007533665746450424,-0.005701306741684675,0.05473776161670685,-3.002788873478761e-18,-0.009754516184329987,0.049039263278245926,0.016628213226795197,-0.006662009283900261,0.05299103260040283,0.02693003974854946,-0.007860624231398106,0.045912690460681915,0.0378524474799633,-0.0072587342001497746,0.03921378403902054,0.045856084674596786,-0.0070934295654296875,0.029357971623539925,0.050895705819129944,-0.006982975639402866,0.01773145981132984,0.05030697211623192,-0.008758459240198135,0.001811013207770884,0.04785057529807091,-0.00937557965517044,-0.012003320269286633,0.04247423633933067,-0.00975059624761343,-0.024512505158782005,0.03405778482556343,-0.010240254923701286,-0.03555915877223015,0.024071719497442245,-0.010106447152793407,-0.04310912266373634,0.0127097824588418,-0.009740777313709259,-0.047343309968709946,9.008366620436283e-18,-0.009754516184329987,-0.049039263278245926,-0.01752157136797905,-0.013548947870731354,-0.05426725745201111,-0.02922087162733078,-0.013448347337543964,-0.04918530583381653,-0.037496186792850494,-0.011970380321145058,-0.03870484232902527,-0.04816902428865433,-0.014232910238206387,-0.03266216814517975,-0.04623059928417206,-0.008860616013407707,-0.011067022569477558,-0.049039263278245926,-0.009754516184329987,-1.2011155493915043e-17,-0.04619397595524788,-0.019134171307086945,0,-0.04379911348223686,-0.018489224836230278,0.013128511607646942,-0.04169834405183792,-0.02046453207731247,0.020678149536252022,-0.03266831114888191,-0.01913749799132347,0.03265802562236786,-0.023508479818701744,-0.019457485526800156,0.039417315274477005,-0.011495649814605713,-0.018772561103105545,0.0452774278819561,0.001414150814525783,-0.01802305318415165,0.04821418970823288,0.012767361477017403,-0.018496578559279442,0.045779213309288025,0.02234853245317936,-0.019722243770956993,0.038935936987400055,0.03124258480966091,-0.02025105617940426,0.03063337504863739,0.038718145340681076,-0.020145395770668983,0.02125839702785015,0.045659586787223816,-0.018317315727472305,0.013441070914268494,0.046787478029727936,-0.01866784878075123,0.000847859715577215,0.045769598335027695,-0.018230879679322243,-0.010313531383872032,0.03978670760989189,-0.019305812194943428,-0.02340906299650669,0.03331249579787254,-0.018624698743224144,-0.031737759709358215,0.02044888213276863,-0.02121482603251934,-0.04378816485404968,0.010471895337104797,-0.020300159230828285,-0.046739935874938965,-0.0017919365782290697,-0.020542120561003685,-0.04875388368964195,-0.020199444144964218,-0.025611257180571556,-0.056396473199129105,-0.03355381265282631,-0.027350248768925667,-0.05494347959756851,-0.03116896189749241,-0.017959438264369965,-0.030528197064995766,-0.04191361740231514,-0.020633675158023834,-0.025823360309004784,-0.04627770185470581,-0.020436687394976616,-0.014324091374874115,-0.04619397595524788,-0.019134171307086945,-1.1314260790922793e-17,-0.041573479771614075,-0.027778511866927147,0,-0.042599912732839584,-0.029698023572564125,0.007269986905157566,-0.03539605066180229,-0.027301082387566566,0.021654795855283737,-0.029596500098705292,-0.027935348451137543,0.02911173179745674,-0.020871644839644432,-0.027845222502946854,0.03588239848613739,-0.009641628712415695,-0.026899784803390503,0.0417545810341835,0.001583561417646706,-0.02653428539633751,0.04383571073412895,0.01601945236325264,-0.023646092042326927,0.0476703867316246,0.023598644882440567,-0.025569159537553787,0.04002069681882858,0.0286326315253973,-0.02837900072336197,0.028305092826485634,0.0369781069457531,-0.027012899518013,0.022178763523697853,0.041716039180755615,-0.026553472504019737,0.012987352907657623,0.04327789694070816,-0.026439327746629715,0.0024348790757358074,0.04032093286514282,-0.02764962799847126,-0.010525673627853394,0.03604860603809357,-0.027743220329284668,-0.02072257176041603,0.0301646888256073,-0.027175240218639374,-0.028300033882260323,0.020514126867055893,-0.02799270674586296,-0.03639313578605652,0.008318666368722916,-0.029696708545088768,-0.04364452883601189,-0.00015723115939181298,-0.02790204994380474,-0.041798096150159836,-0.0161848496645689,-0.03204088658094406,-0.04790667071938515,-0.02549729309976101,-0.03147966042160988,-0.042733050882816315,-0.03617341071367264,-0.03310292214155197,-0.03907763212919235,-0.04183238744735718,-0.03235820308327675,-0.029113449156284332,-0.04341130331158638,-0.030335543677210808,-0.015409158542752266,-0.041573479771614075,-0.027778511866927147,-1.0182566043567556e-17,-0.0353553406894207,-0.0353553406894207,0,-0.03510556370019913,-0.03610564395785332,0.0077864499762654305,-0.03270172327756882,-0.036992065608501434,0.014701809734106064,-0.025370106101036072,-0.03564613685011864,0.024471277371048927,-0.017851024866104126,-0.03549154847860336,0.03037097118794918,-0.008656056597828865,-0.03496674448251724,0.03485717624425888,0.001140155247412622,-0.03445950523018837,0.03698413446545601,0.010991533286869526,-0.03390892222523689,0.03678048774600029,0.01975434273481369,-0.03372367098927498,0.03358529508113861,0.026966385543346405,-0.0338103249669075,0.02780912257730961,0.03012065403163433,-0.03574660047888756,0.016966290771961212,0.03472420573234558,-0.03490467742085457,0.009970021434128284,0.03562521934509277,-0.03514329344034195,0.00038553966442123055,0.034300003200769424,-0.035237979143857956,-0.008937249891459942,0.03111802227795124,-0.03496295586228371,-0.01696423999965191,0.024143552407622337,-0.036028262227773666,-0.026223497465252876,0.015515376813709736,-0.037054285407066345,-0.03370761126279831,0.005295312497764826,-0.03838452324271202,-0.039658237248659134,-0.008259779773652554,-0.0418451689183712,-0.047155026346445084,-0.011804556474089622,-0.03744056448340416,-0.03794195130467415,-0.02215423807501793,-0.03887264430522919,-0.037013716995716095,-0.03403061255812645,-0.04245081916451454,-0.03790087252855301,-0.034350063651800156,-0.03828718885779381,-0.023008305579423904,-0.03577350825071335,-0.036630455404520035,-0.011469028890132904,-0.0353553406894207,-0.0353553406894207,-8.65956027255431e-18,-0.027778511866927147,-0.041573479771614075,0,-0.030256986618041992,-0.04426455497741699,0.0022967455442994833,-0.025696521624922752,-0.042861755937337875,0.011546933092176914,-0.0210898295044899,-0.042710766196250916,0.017574578523635864,-0.014154809527099133,-0.04178212955594063,0.023677535355091095,-0.00729021243751049,-0.04165252670645714,0.026688260957598686,-0.00012213083391543478,-0.041669439524412155,0.027604039758443832,0.00769449258223176,-0.04117678478360176,0.027553247287869453,0.01476742047816515,-0.040883492678403854,0.025311416015028954,0.020060958340764046,-0.041244592517614365,0.02024035155773163,0.02430592104792595,-0.04137781634926796,0.014245004393160343,0.02698930911719799,-0.04144986718893051,0.00741436006501317,0.02786480449140072,-0.04150567948818207,0.00012327598233241588,0.026321230456233025,-0.041974782943725586,-0.0079192528501153,0.02251211181282997,-0.04278723895549774,-0.016096092760562897,0.020552869886159897,-0.04085808992385864,-0.018341664224863052,0.008622209541499615,-0.04571187496185303,-0.031581249088048935,0.005782993044704199,-0.042678676545619965,-0.028841432183980942,0.0014504241989925504,-0.0404338613152504,-0.025706477463245392,-0.00974850170314312,-0.04358403757214546,-0.0304875448346138,-0.01212258543819189,-0.04018538072705269,-0.02153308130800724,-0.022450977936387062,-0.043780241161584854,-0.023654665797948837,-0.02475455403327942,-0.04212163761258125,-0.014885908924043179,-0.034073904156684875,-0.04726355895400047,-0.01753520779311657,-0.027778511866927147,-0.041573479771614075,-6.803773123666079e-18,-0.019134171307086945,-0.04619397595524788,0,-0.020712632685899734,-0.047946467995643616,0.0017659412696957588,-0.019010305404663086,-0.048110827803611755,0.0060819038189947605,-0.015503531321883202,-0.04774468392133713,0.010710432194173336,-0.009082612581551075,-0.045813318341970444,0.017262782901525497,-0.0064285690896213055,-0.04735391214489937,0.01637321710586548,-0.00023581244749948382,-0.04637925699353218,0.018797297030687332,0.004513334948569536,-0.04653886705636978,0.017855113372206688,0.009230910800397396,-0.04645811393857002,0.016090428456664085,0.013443594798445702,-0.04626178741455078,0.013406605459749699,0.01644292287528515,-0.046294353902339935,0.009384578093886375,0.018366739153862,-0.04628468677401543,0.004787357524037361,0.018754389137029648,-0.04649237543344498,-0.0005425457493402064,0.01566522940993309,-0.04840730130672455,-0.008976518176496029,0.017308838665485382,-0.04561399295926094,-0.008512571454048157,0.014459196478128433,-0.045463815331459045,-0.012202340178191662,0.004199590999633074,-0.05041129142045975,-0.024238526821136475,0.0025056879967451096,-0.048116303980350494,-0.02197733335196972,-0.0013954887399449944,-0.04729043319821358,-0.02112772688269615,-0.007627477403730154,-0.04829591140151024,-0.022303888574242592,-0.012583334930241108,-0.0485638864338398,-0.020879605785012245,-0.01202375814318657,-0.04501057788729668,-0.011378267779946327,-0.01512359082698822,-0.04505698010325432,-0.0074998182244598866,-0.017402444034814835,-0.04534560441970825,-0.003409795230254531,-0.019134171307086945,-0.04619397595524788,-4.68652038700443e-18,-0.009754516184329987,-0.049039263278245926,0,-0.010390146635472775,-0.0497998408973217,0.0011417872738093138,-0.007573908660560846,-0.048352744430303574,0.006125473417341709,-0.006120007485151291,-0.04842839017510414,0.008008165284991264,-0.008209330961108208,-0.05165731906890869,0.003687554970383644,-0.0042000203393399715,-0.05035562068223953,0.007028759922832251,0.0005392612656578422,-0.048615556210279465,0.010524889454245567,0.0008983754087239504,-0.050317052751779556,0.007098883390426636,0.004402901511639357,-0.049411971122026443,0.007770007010549307,0.006415745243430138,-0.04941777139902115,0.006209285464137793,0.007532478775829077,-0.04975833371281624,0.0035698572173714638,0.008444665931165218,-0.04980727657675743,0.0011282642371952534,0.008093638345599174,-0.05034423992037773,-0.002372682560235262,0.010142502374947071,-0.04847326502203941,-0.0014955647056922317,0.007632223889231682,-0.0496799610555172,-0.00604216568171978,0.006470590829849243,-0.04937468096613884,-0.007507332134991884,0.005881702993065119,-0.04825005680322647,-0.007012737914919853,-0.0008444988052360713,-0.05168645456433296,-0.01423521526157856,0.0011497254017740488,-0.048135906457901,-0.008112051524221897,-0.0037178974598646164,-0.049976810812950134,-0.011126771569252014,-0.007184107322245836,-0.050851788371801376,-0.011743158102035522,-0.006955909077078104,-0.04908517003059387,-0.006980948615819216,-0.009624874219298363,-0.04996421933174133,-0.006558993831276894,-0.012594676576554775,-0.051531970500946045,-0.00705685093998909,-0.009754516184329987,-0.049039263278245926,-2.3891674767279894e-18,-6.123233849335533e-18,-0.05000000074505806,0,-5.91458994752822e-18,-0.05000000074505806,1.5848096044559123e-18,-5.302876236065149e-18,-0.05000000074505806,3.0616169246677665e-18,-4.329780136277155e-18,-0.05000000074505806,4.329780136277155e-18,-3.0616169246677665e-18,-0.05000000074505806,5.302876236065149e-18,-1.5848096044559123e-18,-0.05000000074505806,5.91458994752822e-18,-3.7493994848884817e-34,-0.05000000074505806,6.123233849335533e-18,1.5848096044559123e-18,-0.05000000074505806,5.91458994752822e-18,3.0616169246677665e-18,-0.05000000074505806,5.302876236065149e-18,4.329780136277155e-18,-0.05000000074505806,4.329780136277155e-18,5.302876236065149e-18,-0.05000000074505806,3.0616169246677665e-18,5.91458994752822e-18,-0.05000000074505806,1.5848096044559123e-18,6.123233849335533e-18,-0.05000000074505806,7.498798969776963e-34,5.91458994752822e-18,-0.05000000074505806,-1.5848096044559123e-18,5.302876236065149e-18,-0.05000000074505806,-3.0616169246677665e-18,4.329780136277155e-18,-0.05000000074505806,-4.329780136277155e-18,3.0616169246677665e-18,-0.05000000074505806,-5.302876236065149e-18,1.5848096044559123e-18,-0.05000000074505806,-5.91458994752822e-18,1.1248197995487964e-33,-0.05000000074505806,-6.123233849335533e-18,-1.5848096044559123e-18,-0.05000000074505806,-5.91458994752822e-18,-3.0616169246677665e-18,-0.05000000074505806,-5.302876236065149e-18,-4.329780136277155e-18,-0.05000000074505806,-4.329780136277155e-18,-5.302876236065149e-18,-0.05000000074505806,-3.0616169246677665e-18,-5.91458994752822e-18,-0.05000000074505806,-1.5848096044559123e-18,-6.123233849335533e-18,-0.05000000074505806,-1.4997597939553927e-33];
// Apply randomization on initialization
//randomizeAsteroidShape(asteroidGeometry);
//geometry.attributes.position.array = new Float32Array(JSON.parse(savedString))
asteroidGeometry.attributes.position.array = new Float32Array(bakedAsteroid);
asteroidGeometry.attributes.position.needsUpdate = true;
// Orbital paths
//--const earthOrbitGeometry = new THREE.RingGeometry(99, 101, 64); // Orbital ring
const earthOrbitCurve = new THREE.EllipseCurve(0, 0, 107.5, 92.0, 0, 2 * Math.PI, false, 0);
const earthOrbitPoints = earthOrbitCurve.getPoints(128);
const earthOrbitGeometry = new THREE.BufferGeometry().setFromPoints(earthOrbitPoints);
earthOrbitGeometry.rotateX((Math.PI / 2)); // Aligns to XZ plane
earthOrbitGeometry.rotateZ(-21.66 * rad); // Tilt orbital plane by 23.44° around X-axis
const earthOrbit = new THREE.Line(earthOrbitGeometry, new THREE.LineBasicMaterial({ color: 0x0000ff }));
//--const earthOrbit = new THREE.LineLoop(earthOrbitGeometry, new THREE.LineBasicMaterial({ color: 0x0000ff }));
scene.add(earthOrbit);
const moonOrbitCurve = new THREE.EllipseCurve(0, 0, 5.4, 4.6, 0, 2 * Math.PI, false, 0);
const moonOrbitPoints = moonOrbitCurve.getPoints(64);
const moonOrbitGeometry = new THREE.BufferGeometry().setFromPoints(moonOrbitPoints);
moonOrbitGeometry.rotateX(Math.PI / 2); // Aligns to XZ plane
moonOrbitGeometry.rotateZ((-21.55) * rad); // Aligns to XZ plane
const moonOrbit = new THREE.Line(moonOrbitGeometry, new THREE.LineBasicMaterial({ color: 0x888888 })); // 00ff00
scene.add(moonOrbit);
const asteroidOrbitCurve = new THREE.EllipseCurve(0, 0, 85.0, 60.0, 0, 2 * Math.PI, false, 0);
//const asteroidOrbitPoints = asteroidOrbitCurve.getPoints(64);
const asteroidOrbitPoints = asteroidOrbitCurve.getPoints(128).map(point => new THREE.Vector3(point.x, point.y, 0)); // Convert to 3D
const asteroidOrbitGeometry = new THREE.BufferGeometry().setFromPoints(asteroidOrbitPoints);
//asteroidOrbitGeometry.rotateX((Math.PI / 2)); // Aligns to XZ plane
//asteroidOrbitGeometry.rotateX(75.55 * rad); // Aligns to XZ plane
asteroidOrbitGeometry.rotateX(-21.55 * rad); // Aligns to XZ plane
asteroidOrbitGeometry.rotateZ(-21.55 * rad); // Aligns to XZ plane
const asteroidOrbit = new THREE.Line(asteroidOrbitGeometry, new THREE.LineBasicMaterial({ color: 0xff0000 }));
scene.add(asteroidOrbit);
const asteroidOrbitOffset = new THREE.Vector3(35.0, -10.0, 0.0);
asteroidOrbit.position.copy(asteroidOrbitOffset);
//--// Asteroid path
//--const asteroidPath = new THREE.Line(
//-- new THREE.BufferGeometry().setFromPoints([
//-- new THREE.Vector3(135, -40.7, -68),
//-- new THREE.Vector3(105, -40.7, 68)
//-- ]),
//-- new THREE.LineBasicMaterial({ color: 0xff0000 })
//--);
//--scene.add(asteroidPath);
const asteroidMarkGeometry = new THREE.BufferGeometry();
const asteroidMarkPositions = new Float32Array([0, 0, 0, 0, 0, 0]); // Initialize to 0
asteroidMarkGeometry.setAttribute('position', new THREE.BufferAttribute(asteroidMarkPositions, 3));
asteroidMarkGeometry.rotateX(-21.55 * rad); // Apply same tilt as orbit
asteroidMarkGeometry.rotateZ(-21.55 * rad); // Apply same tilt as orbit
const asteroidMark = new THREE.Line(
asteroidMarkGeometry,
new THREE.LineBasicMaterial({ color: 0x00ff00 })
);
asteroidMark.frustumCulled = false;
scene.add(asteroidMark);
const direction = new THREE.Vector3(0, 1, 0); // Start with Y-axis (sideways in 2D)
direction.applyAxisAngle(new THREE.Vector3(0, 0, 1), 90.0 * rad);
direction.applyAxisAngle(new THREE.Vector3(0, 1, 0), 90.0 * rad);
direction.applyAxisAngle(new THREE.Vector3(1, 0, 0), -21.55 * rad);
direction.applyAxisAngle(new THREE.Vector3(0, 0, 1), -21.55 * rad);
direction.normalize();
const lineLength = 5; // Fixed length
const lineOffset = .3; // Fixed length
// Camera setup
camera.position.set(97.65, -38.78, 1.70);
//camera.position.set(97.5, -38.0, 0.0);
//camera.position.set(40.57, 7.01, -178.13);
//camera.lookAt(135, 3, 0);
// OrbitControls for free movement
//const controls = new THREE.OrbitControls(camera, renderer.domElement);
const controls = new OrbitControls( camera, renderer.domElement );
controls.enableDamping = true;
controls.dampingFactor = 0.05;
//controls.screenSpacePanning = false;
controls.minDistance = .1;
controls.maxDistance = 100;
controls.target.set(100, -39.78, 0);
//controls.target.set(100, -39.78, 0);
//controls.target.set(27.56, 18.19, -79.61);
controls.update();
const helperGrids = false; // debug
if(helperGrids) {
const helper = new THREE.GridHelper(500, 500);
helper.material.opacity = 0.25;
helper.material.transparent = true;
scene.add(helper);
const axis = new THREE.AxesHelper(1000);
scene.add(axis);
}
// Timeline control
const timeline = document.getElementById('timeline');
const timeLabel = document.getElementById('timeLabel');
//const baseTime = new Date('2025-07-13T00:00:00Z').getTime();
const baseTime = new Date('2025-07-12T00:00:00Z').getTime();
const hoursRange = 365 * 24; // 192 days ~ 6.4 months / 2
const earthOrbitalPeriodMs = 365.25 * 24 * 3600 * 1000;
const moonOrbitalPeriodMs = 27.3 * 24 * 3600 * 1000;
const sunPeriod = 25 * 24 * 3600; // 25 days in seconds (equator base)
const cloudPeriod = 365 * 24 * 3600; // 365 days in seconds
let isPlaying = false;
let speed = 1; // 1x, 100x, 1000x, 10000x
let lastTime = null;
const play1x = document.getElementById('play1x');
const play100x = document.getElementById('play100x');
const play1000x = document.getElementById('play1000x');
const play10000x = document.getElementById('play10000x');
const play100000x = document.getElementById('play100000x');
const play1000000x = document.getElementById('play1000000x');
const stop = document.getElementById('stop');
const modeF = document.getElementById('modeF');
const modeL1 = document.getElementById('modeL1'); // aster
const modeL2 = document.getElementById('modeL2'); // moon
const modeL3 = document.getElementById('modeL3'); // earth
let mFL = [];
mFL[0] = document.getElementById('mF0');
mFL[1] = document.getElementById('mF1');
mFL[2] = document.getElementById('mF2');
mFL[3] = document.getElementById('mF3');
play1x.addEventListener('click', () => { playSpeed(1); });
play100x.addEventListener('click', () => { playSpeed(100); });
play1000x.addEventListener('click', () => { playSpeed(1000); });
play10000x.addEventListener('click', () => { playSpeed(10000); });
play100000x.addEventListener('click', () => { playSpeed(100000); });
play1000000x.addEventListener('click', () => { playSpeed(1000000); });
stop.addEventListener('click', () => { stopPlay(); });
let pFL = [];
pFL[0] = document.getElementById('pF0');
pFL[1] = document.getElementById('pF1');
pFL[2] = document.getElementById('pF2');
pFL[3] = document.getElementById('pF3');
pFL[4] = document.getElementById('pF4');
pFL[5] = document.getElementById('pF5');
pFL[6] = document.getElementById('pF6');
function playSpeed(x) {
isPlaying = true;
speed = x;
let sel=0;
for (let i = 0; i < 7; i++) pFL[i].style.display = "none";
switch(x) {
case 1:
sel = 1;
break;
case 100:
sel = 2;
break;
case 1000:
sel = 3;
break;
case 10000:
sel = 4;
break;
case 100000:
sel = 5;
break;
case 1000000:
sel = 6;
break;
default:
}
pFL[sel].style.display = "inline-block"; // Show speed v
}
function stopPlay() {
isPlaying = false;
speed = 0;
for (let i = 0; i < 7; i++) pFL[i].style.display = "none";
pFL[0].style.display = "inline-block"; // Show speed v
}
const spherical = { radius: 500, theta: 0, phi: Math.PI / 2, velocityTheta: 0, velocityPhi: 0 }; // Default values
function updateSphericalOrbit(obj) {
const x = spherical.radius * Math.cos(spherical.phi) * Math.cos(spherical.theta);
const y = spherical.radius * Math.sin(spherical.phi);
const z = spherical.radius * Math.cos(spherical.phi) * Math.sin(spherical.theta);
camera.position.set(x, y, z).add(obj.position); // Offset from object
camera.lookAt(obj.position);
controls.target.copy(obj.position); // Point OrbitControls at object
controls.update(); // Apply user input and damping
//camera.getWorldPosition();
}
function targetObject(obj1, obj2) {
tDiff.copy(obj2.position).sub(obj1.position); // Update tDiff with current positions
isLocked = true;
controls.enabled = false;
controls.enableDamping = false;
objRef = obj2;
for (let i = 0; i < 4; i++) mFL[i].style.display = "none";
let sel=0;
switch(obj2) {
case earth:
//objRef=earth;
sel=3;
break;
case moon:
//objRef=moon;
sel=2;
break;
case asteroid:
//objRef=asteroid;
sel=1;
break;
default:
};
mFL[sel].style.display = "inline-block"; // Show Earth lock UI
// Initialize spherical orbit with current distance
const dx = obj2.position.x - obj1.position.x;
const dy = obj2.position.y - obj1.position.y;
const dz = obj2.position.z - obj1.position.z;
spherical.radius = Math.sqrt(dx * dx + dy * dy + dz * dz); // Dynamic radius
spherical.theta = Math.atan2(dz, dx); // Azimuth
//spherical.phi = Math.acos(dy / spherical.radius) - (90.0 * rad); // Elevation, adjusted for acos range
//spherical.phi = Math.asin(dy / spherical.radius); // Elevation
spherical.phi = Math.asin(dy / spherical.radius) + (180.0 * rad); // Elevation
//spherical.phi = Math.PI / 2;
//spherical.phi = 180.0 * rad;
//spherical.phi = angle;
spherical.velocityTheta = 0; // Reset velocity
spherical.velocityPhi = 0; // Reset velocity
}
//let lockControls = false;
let isLocked = false;
let tDiff = new THREE.Vector3();
let objRef = null;
modeF.addEventListener('click', () => {
isLocked=false;
objRef=null;
controls.enabled = true;
controls.enableDamping = true;
for(let i=0;i<4;i++) mFL[i].style.display="none";
mFL[0].style.display="inline-block";
});
modeL1.addEventListener('click', () => targetObject(camera, asteroid));
modeL2.addEventListener('click', () => targetObject(camera, moon));
modeL3.addEventListener('click', () => targetObject(camera, earth));
const normalMatrix = new THREE.Matrix3(); // create once and reuse
const atmNormalMatrix = new THREE.Matrix3();
const earthNormalMatrix = new THREE.Matrix3();
const moonNormalMatrix = new THREE.Matrix3();
const astNormalMatrix = new THREE.Matrix3();
const mvpMatrix = new THREE.Matrix4();
const atmMvpMatrix = new THREE.Matrix4();
const earthMvpMatrix = new THREE.Matrix4();
const moonMvpMatrix = new THREE.Matrix4();
const astMvpMatrix = new THREE.Matrix4();
const sunMvpMatrix = new THREE.Matrix4();
const starMvpMatrix = new THREE.Matrix4();
const glowMvpMatrix = new THREE.Matrix4();
let timeOffset = 0.0;
let timeFraction = 0.0;
let yToWinter = 0.0;
let wireframeMode = false;
let linesVisible = true;
let uiVisible = true;
let currentTime = new Date(baseTime + timeOffset);
timeline.addEventListener('input', (event) => {
if(!isPlaying) {
timeFraction = event.target.value / 100; // 0 to 1
timeOffset = (timeFraction - 0.5) * hoursRange * 3600 * 1000; // -24 to +24 hours in ms
//const loopedSunTime = (currentTime * .0000001) % 360.0; // Loop within 25 days
//sunMaterial.uniforms.realtime.value = loopedSunTime * rad;
// Update realtime animation
//sunMaterial.uniforms.realtime.value = timeFraction * cloudPeriod; // Scale to 25-day period
//cloudMaterial.uniforms.time.value = timeFraction * cloudPeriod; // Scale to 365-day period
}
currentTime = new Date(baseTime + timeOffset);
timeLabel.textContent = currentTime.toUTCString();
// Earth orbital position (moves 1/365th of orbit per day)
const earthAngle = -(timeOffset / earthOrbitalPeriodMs) * 2 * Math.PI;
// Earth position with 23.44° tilt
const earthRadius = 100;
const tiltRad = 23.44 * rad; // Axial and orbital tilt
const earthX = earthRadius * Math.cos(earthAngle);
const earthY = earthRadius * Math.sin(-(90*rad)+earthAngle) * Math.sin(tiltRad); // Y-offset based on tilt
const earthZ = earthRadius * Math.sin(earthAngle) * Math.cos(tiltRad); // Adjusted Z for tilt
earth.position.set(earthX, earthY, earthZ);
clouds.position.set(earth.position.x, earth.position.y, earth.position.z);
atmosphere.position.set(earth.position.x, earth.position.y, earth.position.z);
// Update Moon orbit position to follow Earth
moonOrbit.position.set(earth.position.x, earth.position.y, earth.position.z);
// Moon orbital position
const moonAngle = -(timeOffset / moonOrbitalPeriodMs) * 2 * Math.PI + (45 * rad);
// Moon spherical coordinates (radius 5 around Earth, tilted 5°)
const moonRadius = 5; // Distance from Earth
const moonTiltAngle = 5 * rad; // Tilt of Moon’s orbit
const moonX = moonRadius * Math.cos(moonAngle);
const moonY = moonRadius * Math.sin((-90*rad)+moonAngle) * Math.sin(tiltRad); // Y-offset based on tilt
const moonZ = moonRadius * Math.sin(moonAngle) * Math.cos(tiltRad); // Adjusted Z for tilt
moon.position.set(earthX + moonX, earthY + moonY, earthZ + moonZ);
// Asteroid position
const asteroidAngle = (timeOffset / earthOrbitalPeriodMs) * 2 * Math.PI + (178.0 * rad);
const t = (asteroidAngle + Math.PI) / (2 * Math.PI); // 0 to 1 parameter
const pointOnEllipse = new THREE.Vector3().set(
asteroidOrbitCurve.getPoint(t).x,
asteroidOrbitCurve.getPoint(t).y,
0
);
//pointOnEllipse.applyAxisAngle(new THREE.Vector3(0, 0, 1), 90.0 * rad);
//pointOnEllipse.applyAxisAngle(new THREE.Vector3(0, 1, 0), 90.0 * rad);
pointOnEllipse.applyAxisAngle(new THREE.Vector3(1, 0, 0), -21.55 * Math.PI / 180); // Apply X tilt
pointOnEllipse.applyAxisAngle(new THREE.Vector3(0, 0, 1), -21.55 * Math.PI / 180); // Apply Z tilt
let asteroidPos = pointOnEllipse.add(asteroidOrbitOffset);
asteroid.position.copy(asteroidPos);
// Asteroid Mark with fixed direction and offset
const dirClone = direction.clone(); // Prevent cumulative modification
const startPos = asteroidPos.clone().sub(dirClone.multiplyScalar(lineOffset)); // Start at -0.3
const endPos = asteroidPos.clone().sub(dirClone.multiplyScalar(lineLength + lineOffset)); // End at -(5 + 0.3)
// Safe buffer update with NaN check
asteroidMarkPositions[0] = isNaN(startPos.x) ? 0 : startPos.x;
asteroidMarkPositions[1] = isNaN(startPos.y) ? 0 : startPos.y;
asteroidMarkPositions[2] = isNaN(startPos.z) ? 0 : startPos.z;
asteroidMarkPositions[3] = isNaN(endPos.x) ? 0 : endPos.x;
asteroidMarkPositions[4] = isNaN(endPos.y) ? 0 : endPos.y;
asteroidMarkPositions[5] = isNaN(endPos.z) ? 0 : endPos.z;
asteroidMarkGeometry.attributes.position.needsUpdate = true;
asteroidMarkGeometry.computeBoundingSphere(); // Force recompute
// Calculate tilted local Y-axis
const tiltAxis = new THREE.Vector3(1, 0, 0).applyAxisAngle(new THREE.Vector3(0, 1, 0), tiltRad);
const localYAxis = new THREE.Vector3(0, 1, 0).applyAxisAngle(tiltAxis, 0); // Simplify, adjust if needed
// Daily rotation angle
const earthRotationAngle = (timeOffset / (24 * 3600 * 1000)) * 2 * Math.PI;
earth.rotation.y = earthRotationAngle + (350 * rad) - earthAngle; // Align UK to Sun at 12 PM GMT
earth.rotation.x = + (5.0 * rad); // side tilt to even egypt to norway / south america to north america
clouds.rotation.copy(earth.rotation);
atmosphere.rotation.copy(earth.rotation);
//--// Create transformation matrix -- not used for now
//--const positionMatrix = new THREE.Matrix4().makeTranslation(earthX, earthY, earthZ);
//--const tiltMatrix = new THREE.Matrix4().makeRotationZ(-tiltRad);
//--const rotationMatrix = new THREE.Matrix4().makeRotationAxis(localYAxis, earthRotationAngle);
//--const combinedMatrix = new THREE.Matrix4().multiplyMatrices(positionMatrix, tiltMatrix).multiply(rotationMatrix);
//--// Apply to Earth, clouds, and atmosphere
//--earth.matrixAutoUpdate = false;
//--earth.matrix.copy(combinedMatrix);
//--earth.updateMatrixWorld();
//--clouds.matrixAutoUpdate = false;
//--clouds.matrix.copy(combinedMatrix); // Same matrix for clouds
//--clouds.updateMatrixWorld();
//--atmosphere.matrixAutoUpdate = false;
//--atmosphere.matrix.copy(combinedMatrix); // Same matrix for atmosphere
//--atmosphere.updateMatrixWorld();
// Moon rotation (tidally locked to its orbit)
let moonRotationAngle = -(((timeOffset / moonOrbitalPeriodMs) * 2 * Math.PI));
while(moonRotationAngle > (Math.PI * 2)) moonRotationAngle-=Math.PI * 2;
while(moonRotationAngle < 0.0) moonRotationAngle+=Math.PI * 2;
//console.log(moon.rotation.y);
moon.rotation.y = ((Math.PI * 2) - moonRotationAngle) + (120 * rad);
// Sun rotation
//const sunRotationPeriodMs = 25 * 24 * 3600 * 1000; // 25 days in milliseconds
//const sunRotationAngle = (timeOffset / sunRotationPeriodMs) * 2 * Math.PI;
//sun.rotation.y = sunRotationAngle; // Rotate around Y-axis
//console.log(sun.rotation.y);
//console.log(earth.position.y); // ~-40 to +40
yToWinter=(earth.position.y + 20) * .025;
if(yToWinter>1.0) yToWinter=1.0;
else if(yToWinter<0.0) yToWinter=0.0;
earthMaterial.uniforms.isWinter.value = yToWinter;
});
function updateFlames(time) {
const positionAttribute = flames.geometry.attributes.position;
const scaleAttribute = flames.geometry.attributes.scale;
for (let i = 0; i < positionAttribute.count; i += 2) {
const baseIndex = i / 2;
const scale = scaleAttribute.getX(baseIndex);
const newScale = Math.max(0, Math.sin(time + baseIndex) * 0.5 + 0.5); // Oscillate 0-1 with offset
scaleAttribute.setX(baseIndex, newScale);
// Update tip position based on scale
const tipX = positionAttribute.getX(i + 1) * newScale;
const tipY = positionAttribute.getY(i + 1) * newScale;
const tipZ = positionAttribute.getZ(i + 1) * newScale;
positionAttribute.setXYZ(i + 1, tipX, tipY, tipZ);
//if(i==0) {
//console.dir(newScale);
//}
}
scaleAttribute.needsUpdate = true;
positionAttribute.needsUpdate = true;
}
// Trigger initial state
const initialEvent = new Event('input');
timeline.value = 50; // Set to middle of timeline (12:00 UTC)
timeline.dispatchEvent(initialEvent);
const cameraInfo = document.getElementById('infoDynamic');
const controlsUI1 = document.getElementById('controls');
const controlsUI2 = document.getElementById('controls2');
// Animation loop
function updateScene(time) {
if (lastTime === null) lastTime = time;
const deltaTime = (time - lastTime) / 1000; // Seconds
lastTime = time;
if (isPlaying) {
//timeFraction = event.target.value / 100; // 0 to 1
//timeOffset = (timeFraction - 0.5) * hoursRange * 3600 * 1000; // -24 to +24 hours in ms
timeOffset += deltaTime * (speed * .00025) * 3600 * 1000; // Advance by speed * hours
timeFraction = (timeOffset / hoursRange / 3600 / 1000) + .5;
if (timeFraction < 0) timeFraction = 0;
if (timeFraction > 1) timeFraction = 1;
timeline.value = timeFraction * 100;
//currentTime = new Date(baseTime + timeOffset);
//timeLabel.textContent = currentTime.toUTCString();
// Update sun material realtime
//const loopedSunTime = currentTime % sunPeriod; // Loop within 25 days
//const loopedCloudTime = currentTime % cloudPeriod; // Loop within 365 days
//cloudMaterial.uniforms.time.value = loopedCloudTime;
timeline.dispatchEvent(initialEvent);
}
const loopedSunTime = (currentTime * .0000001) % 360.0; // Loop within 25 days
sunMaterial.uniforms.realtime.value = loopedSunTime * rad;
//--debug
//camera.position.set(102, .5, 3); // View from a diagonal angle
//camera.lookAt(earth.position); // Look at Earth
//camera.lookAt(moon.position); // Look at Earth
//controls.target.copy(moon.position); // Update OrbitControls target
//---
controls.update();
camera.updateMatrixWorld();
//camera.matrixWorldInverse.getInverse( camera.matrixWorld );
if (isLocked && objRef) {
//camera.position.set(objRef.position.x - tDiff.x, objRef.position.y - tDiff.y, objRef.position.z - tDiff.z);
//camera.lookAt(objRef.position);
//controls.target.copy(objRef.position); // Update OrbitControls target
//spherical.theta += 0.01; // Rotate around Earth
// Apply velocity with dampening
spherical.velocityTheta += mouseVelocityX + touchVelocityX;
spherical.velocityPhi += mouseVelocityY + touchVelocityY;
spherical.theta += spherical.velocityTheta;
spherical.phi += spherical.velocityPhi;
//spherical.phi = Math.max(0.1, Math.min(Math.PI - 0.1, spherical.phi)); // Clamp
//spherical.phi = Math.max(0.0, Math.min(Math.PI * 2, spherical.phi)); // Clamp
spherical.theta %= Math.PI * 2; // Clamp
spherical.phi = Math.max((Math.PI / 2) + .1, Math.min(((Math.PI / 2) + Math.PI) - .1, spherical.phi)); // Clamp
spherical.velocityTheta *= dampingFactor; // Exponential decay
spherical.velocityPhi *= dampingFactor; // Exponential decay
updateSphericalOrbit(objRef);
if(mouseVelocityY > 0.0) mouseVelocityY *= dampingFactor;
if(touchVelocityY > 0.0) touchVelocityY *= dampingFactor;
//console.log(mouseVelocityX);
}
// Update star field position
starField.position.copy(camera.position);
//starField.position.set(camera.position.x, camera.position.y, camera.position.z);
starField.updateMatrixWorld();
glowField.position.copy(camera.position); // Sync glow layer
glowField.updateMatrixWorld();
// Update normal matrices
earth.updateMatrixWorld();
earthNormalMatrix.getNormalMatrix(earth.matrixWorld);
earthMaterial.uniforms.nMatrix.value = earthNormalMatrix;
earthMaterial.uniforms.vMatrix.value.copy(camera.matrixWorldInverse);
earthMvpMatrix.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse).multiply(earth.matrixWorld);
earthMaterial.uniforms.mvpMatrix.value = earthMvpMatrix;
moon.updateMatrixWorld();
moonNormalMatrix.getNormalMatrix(moon.matrixWorld);
moonMaterial.uniforms.nMatrix.value = moonNormalMatrix;
moonMaterial.uniforms.vMatrix.value.copy(camera.matrixWorldInverse);
moonMvpMatrix.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse).multiply(moon.matrixWorld);
moonMaterial.uniforms.mvpMatrix.value = moonMvpMatrix;
sunMvpMatrix.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse).multiply(sun.matrixWorld);
sunMaterial.uniforms.mvpMatrix.value = sunMvpMatrix;
starMvpMatrix.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse).multiply(starField.matrixWorld);
starMaterial.uniforms.mvpMatrix.value = starMvpMatrix;
glowMvpMatrix.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse).multiply(glowField.matrixWorld);
glowMaterial.uniforms.mvpMatrix.value = glowMvpMatrix;
asteroid.updateMatrixWorld();
astNormalMatrix.getNormalMatrix(asteroid.matrixWorld);
asteroidMaterial.uniforms.nMatrix.value = astNormalMatrix;
asteroidMaterial.uniforms.vMatrix.value.copy(camera.matrixWorldInverse);
astMvpMatrix.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse).multiply(asteroid.matrixWorld);
asteroidMaterial.uniforms.mvpMatrix.value = astMvpMatrix;
clouds.updateMatrixWorld();
normalMatrix.getNormalMatrix(clouds.matrixWorld);
cloudMaterial.uniforms.nMatrix.value = normalMatrix;
cloudMaterial.uniforms.vMatrix.value.copy(camera.matrixWorldInverse);
//cloudMaterial.uniforms.invView.value.copy(camera.matrixWorldInverse).invert();
//cloudMaterial.uniforms.invProjection = { value: new THREE.Matrix4() };
//cloudMaterial.uniforms.invProjection.value.copy(camera.projectionMatrix).invert();
mvpMatrix.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse).multiply(clouds.matrixWorld);
cloudMaterial.uniforms.mvpMatrix.value = mvpMatrix;
atmosphere.updateMatrixWorld();
atmNormalMatrix.getNormalMatrix(atmosphere.matrixWorld);
atmMaterial.uniforms.nMatrix.value = atmNormalMatrix;
atmMaterial.uniforms.vMatrix.value.copy(camera.matrixWorldInverse);
atmMvpMatrix.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse).multiply(atmosphere.matrixWorld);
atmMaterial.uniforms.mvpMatrix.value = atmMvpMatrix;
// Update sun material time
if(sunMaterial.uniforms.time.value==0) {
sunMaterial.uniforms.time.value = 1.0; // defaultset
}
sunMaterial.uniforms.time.value += 0.005 + (speed * .0000001); // Adjust speed here
earthMaterial.uniforms.time.value += 0.015; // Animate water
cloudMaterial.uniforms.time.value += 0.005 + (speed * .000001);
//const loopedFlameTime = currentTime % (1.0 * 24 * 3600); // Loop within 25 days
flameTime += .01 + (speed * .000001);
updateFlames(flameTime);
//if (sunLight.shadow.map) {
// earthMaterial.uniforms.shadowMap.value = sunLight.shadow.map.texture;
// // No lightSpaceMatrix needed for point light; use direct depth
// //earthMaterial.uniforms.lightSpaceMatrix.value.copy(sunLight.shadow.camera.matrixWorldInverse).multiply(earth.matrixWorld);
//}
// Update camera info
const pos = camera.position;
const target = controls.target; // OrbitControls look-at point
if(!isLocked) {
cameraInfo.innerHTML = `Camera Pos: (${pos.x.toFixed(2)}, ${pos.y.toFixed(2)}, ${pos.z.toFixed(2)})<br />\nLookAt: (${target.x.toFixed(2)}, ${target.y.toFixed(2)}, ${target.z.toFixed(2)})`;
} else {
cameraInfo.innerHTML = `CameraPos: (${pos.x.toFixed(2)}, ${pos.y.toFixed(2)}, ${pos.z.toFixed(2)})<br />\nLookAt: (${target.x.toFixed(2)}, ${target.y.toFixed(2)}, ${target.z.toFixed(2)})<br />\ntheta: ${spherical.theta.toFixed(3)} phi: ${spherical.phi.toFixed(3)}<br />\nvelocityTheta: ${spherical.velocityTheta.toFixed(3)}, velocityPhi: ${spherical.velocityPhi.toFixed(3)}`;
}
//// Pre-depth pass for clouds
//setupDepthPass();
//renderer.setRenderTarget(renderTargetDepth);
//renderer.clear();
//renderer.render(scene, camera); // Render depth from camera perspective (adjust if needed)
//renderer.setRenderTarget(null);
//restoreMaterials();
// Pre-depth pass with cube camera
//-- setupDepthPass(); // disabled fow now
//-- cubeCamera.update(renderer, scene); // Render all six faces
//-- restoreMaterials();
//-- // Update shadow map uniform
//-- earthMaterial.uniforms.shadowMap.value = renderTargetDepth.texture;
renderer.render(scene, camera);
//--composer.render(); // Replace renderer.render
requestAnimationFrame(updateScene);
}
//updateScene();
requestAnimationFrame(updateScene);
let isDragging = false;
let isDragValid = false; // Track if drag started on canvas
let isTouchZooming = false; // to zoom while sinning on 2 finger touch
let lastMouseX = 0, lastMouseY = 0, lastTouchX = 0, lastTouchY = 0;
let mouseVelocityX = 0, mouseVelocityY = 0, touchVelocityX = 0, touchVelocityY = 0;
let touchStartTime = 0;
const dragSensitivity = 0.00025; // Adjust for speed
const dampingFactor = 0.95; // Velocity decay
const spinThreshold = 200; // 200ms for spin triggery
// Mouse Listeners
canvas.addEventListener('mousedown', (event) => {
if (isLocked) {
isDragging = true;
isDragValid = true; // Mark drag as valid since it started on canvas
lastMouseX = event.clientX;
lastMouseY = event.clientY;
mouseVelocityX = mouseVelocityY = 0;
touchStartTime=new Date().getTime();
}
});
document.addEventListener('mouseup', () => {
if (isLocked) {
if (isDragValid && Date.now() - touchStartTime > spinThreshold) {
mouseVelocityX = 0;
}
isDragging = false;
isDragValid = false; // Reset drag validity
mouseVelocityY = 0;
}
});
document.addEventListener('mousemove', (event) => {
if (isLocked && isDragging && isDragValid) {
const deltaX = event.clientX - lastMouseX;
const deltaY = event.clientY - lastMouseY;
spherical.theta += deltaX * dragSensitivity; // Rotate theta with mouse
spherical.phi -= deltaY * dragSensitivity; // Rotate theta with mouse
lastMouseX = event.clientX;
lastMouseY = event.clientY;
mouseVelocityX = deltaX * (dragSensitivity * .5); // Update velocity
mouseVelocityY = -(deltaY * (dragSensitivity * .5)); // Update velocity
}
});
document.addEventListener('wheel', (event) => {
if (isLocked) {
let speedzoom = Math.max(1,spherical.radius);
spherical.radius += event.deltaY * speedzoom * 0.0005; // Zoom with mouse wheel
spherical.radius = Math.max(.1, Math.min(100, spherical.radius)); // Clamp radius
//console.log(spherical.radius);
}
});
// Touch Listeners
canvas.addEventListener('touchstart', (event) => {
//event.preventDefault(); // Prevent default touch behavior (e.g., scrolling)
if (isLocked) {
isDragValid = true; // Mark drag as valid
touchStartTime = Date.now();
if (event.touches.length === 1) {
isDragging = true;
const touch = event.touches[0];
lastTouchX = touch.clientX;
lastTouchY = touch.clientY;
// touchVelocityX = touchVelocityY = 0;
} else if (event.touches.length === 2) {
isTouchZooming = true;
// Two touches for pinch zoom
updatePinchZoom(event.touches);
}
}
}, { passive: false }); // Allow preventDefault
document.addEventListener('touchend', (event) => {
if (isLocked && (isDragging || isTouchZooming)) {
if (isDragValid && !isTouchZooming && event.touches.length < 1 && Date.now() - touchStartTime > spinThreshold) {
touchVelocityX = 0;
}
isDragging = false;
isDragValid = false; // Reset drag validity
isTouchZooming = false;
touchVelocityY = 0;
}
});
document.addEventListener('touchmove', (event) => {
if (isLocked && (isDragging || isTouchZooming) && isDragValid) {
event.preventDefault();
if (event.touches.length === 1 && !isTouchZooming) {
// Single touch movement
const deltaX = event.touches[0].clientX - lastTouchX;
const deltaY = event.touches[0].clientY - lastTouchY;
spherical.velocityTheta += deltaX * dragSensitivity * .5;
spherical.velocityPhi -= deltaY * dragSensitivity * .5;
lastTouchX = event.touches[0].clientX;
lastTouchY = event.touches[0].clientY;
touchVelocityX = deltaX * (dragSensitivity * .25);
touchVelocityY = -(deltaY * (dragSensitivity * .25));
} else if (event.touches.length === 2 && isTouchZooming) {
isDragging = false;
// Pinch zoom
updatePinchZoom(event.touches);
}
}
}, { passive: false });
// Pinch Zoom Function
function updatePinchZoom(touches) {
const touch1 = touches[0];
const touch2 = touches[1];
const dx = touch2.clientX - touch1.clientX;
const dy = touch2.clientY - touch1.clientY;
const currentDistance = Math.sqrt(dx * dx + dy * dy);
if (!spherical.lastPinchDistance) spherical.lastPinchDistance = currentDistance;
const deltaDistance = currentDistance - spherical.lastPinchDistance;
let speedzoom = Math.max(1, spherical.radius);
if(deltaDistance>0.0) {
// zoom in
spherical.radius -= 1.0 * speedzoom * 0.02; // Adjust factor as needed
} else {
// zoom oout
spherical.radius += 1.0 * speedzoom * 0.02; // Adjust factor as needed
}
spherical.radius = Math.max(0.1, Math.min(100, spherical.radius)); // Clamp radius
spherical.lastPinchDistance = currentDistance;
}
// Key listeners
// altered orbitcontrols2 event.ctrl key to use custom here
const pressedKeys = new Set;
document.addEventListener('keydown', (e) => {
pressedKeys.add(e.key);
if (e.key === 'Shift') {
controls.screenSpacePanning = false;
}
});
document.addEventListener('keyup', (e) => {
pressedKeys.delete(e.key);
if (event.key === 'w') { // Toggle with 'w' key
wireframeMode = !wireframeMode;
sun.material.wireframe = wireframeMode; // Toggle wireframe
moon.material.wireframe = wireframeMode; // Toggle wireframe
earth.material.wireframe = wireframeMode;
clouds.material.wireframe = wireframeMode;
atmosphere.material.wireframe = wireframeMode;
asteroid.material.wireframe = wireframeMode;
} else if (e.key === 'l') { // Toggle with 'l' key
linesVisible = !linesVisible;
earthOrbit.visible = linesVisible;
moonOrbit.visible = linesVisible;
//--asteroidPath.visible = linesVisible;
//--asteroidMark.visible = linesVisible;
} else if( e.key === 'u' ) {
uiVisible = !uiVisible;
if(uiVisible) {
cameraInfo.parentNode.style.display = "none";
controlsUI1.style.display = "none";
controlsUI2.style.display = "none";
} else {
cameraInfo.parentNode.style.display = "block";
controlsUI1.style.display = "block";
controlsUI2.style.display = "block";
}
} else if (e.key === 'Shift') {
controls.screenSpacePanning = true;
}
pressedKeys.clear();
});
// Handle window resize
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
var anaS=`%c
,/#(,
./%%%%(,
.*#%%%%#,
,/#%%%%%%*
./%%%%%%%%%%*
*%%%%%%%%%%%%/.
,#%%%%%%%%%%%%(.
./%%%%#*/%%%%%%/. /%(,*(
./#%%%%/. ./%%#/,,%%%%%%%%%%%/
*(%%##%%%%%%%%%%%%%%(,./%%%%%%#*.
*#%%#* ../%%%%#/. ./%%%%/.
.*(%%#*. ./%%%%#, *%%%%(,
.*#%%###%%%%%%#/, .*%%%%#, .*%%%%#, *
./#%%/.*(%%(*. *%%%%%%%%/.,#%%%%* *
.(%%%%%%%%%%/, ,*//*, ,
`
function printascii() { setTimeout(console.log.bind(console,anaS,'background: #2a1e27; color: #fbbb57;')); }
printascii();
</script>
</body>
</html>
Top