~firefoxsimulationsmg1
5 itemsDownload ./*

..
build
jsm
earth_atmos_2048.jpg
index.html
moon_1024.jpg


mg1index.html
48 KB• 8•  27 minutes ago•  DownloadRawClose
27 minutes ago•  8

{}
<!DOCTYPE html>
<html>
<head>
    <title>Asteroid MG1 Passing Earth - July 12, 2025</title>
    <style>
        body { margin: 0; overflow: hidden; }
        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: 20px; left: 50%;
            transform: translateX(-50%);
            width: 80%; max-width: 600px;
            text-align: center;
            padding: 10px;
            color: white;
            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;
        }
    </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 />
        <button id="play1x">Play 1x</button>
        <button id="play100x">Play 100x</button>
        <button id="play1000x">Play 1000x</button>
        <button id="play10000x">Play 10000x</button>
        <button id="play100000x">Play 100000x</button>
        <button id="stop">Stop</button>
    </div>
    <div id="controls2">
        <span id="mF0">&gt;</span><button id="modeF">Free mode</button><br />
        <span id="lockLabel">Lock mode:</span><br />
        <span id="mF1">&gt;</span><button id="modeL1">Asteroid</button><br />
        <span id="mF2">&gt;</span><button id="modeL2">Moon</button><br />
        <span id="mF3">&gt;</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';

        // 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);

        // Lighting
        const sunLight = new THREE.PointLight(0xffffff, 50000, 1000);
        sunLight.position.set(0, 0, 0);
        scene.add(sunLight);
        const ambientLight = new THREE.AmbientLight(0x404040, 0.1); // Adjusted up from 0.1
        scene.add(ambientLight);

// Define the shaders
const vertexShaderSun = `
    varying vec3 vPosition;

    void main() {
        vPosition = (modelMatrix * vec4(position, 1.0)).xyz;
        gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
    }
`;
const fragmentShaderSun = `
    uniform float time;
    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
        );
    }

    float noise4D(vec3 p, float t) {
        return noise(p + vec3(t));
    }

    void main() {
        vec3 pos = normalize(vPosition);
    
        // 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
        
        // Three main flow layers with increased frequency
        vec3 flow1 = pos * baseScale + vec3(time * 0.1);
        vec3 flow2 = pos * baseScale - vec3(time * 0.15);
        vec3 flow3 = pos * baseScale + vec3(time * 0.05, 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.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;
        
        gl_FragColor = vec4(color, 1.0);
    }
`;

const vertexShaderStars = `
    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 = projectionMatrix * modelViewMatrix * 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 = `
    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 = projectionMatrix * modelViewMatrix * 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 v_vertToLight; 
    varying vec3 vViewDir;
    
    void main() {
        vUv = uv;
        vNormal = normal;
        vViewPosition = (modelViewMatrix * vec4(position, 1.0)).xyz;

        vec3 worldNormal = normalize(nMatrix * normal);
        vNormal = normalize((vMatrix * vec4(worldNormal, 0.0)).xyz);
        vec4 viewSunPos   = vMatrix * vec4(lightPosition, 1.0);
        v_vertToLight = normalize(viewSunPos.xyz - vViewPosition.xyz);
        vViewDir = normalize(-vViewPosition);

        gl_Position = mvpMatrix * vec4(position, 1.0);
    }
`;
const fragmentShaderEarth = `
    uniform sampler2D diffuseMap; // Day/night texture
    uniform vec3 lightPosition;   // Sun at (0, 0, 0)
    varying vec2 vUv;
    varying vec3 vNormal;
    varying vec3 vViewPosition;
    varying vec3 v_vertToLight;
    varying vec3 vViewDir;
    
    void main() {
        // Sample diffuse texture
        vec4 diffuseColor = texture2D(diffuseMap, vUv);
    
        // Water/land detection (simplified: blue = water, others = land)
        float isWater = step(0.5, diffuseColor.b); // High blue = water
        float colorRG = (texture2D(diffuseMap, vUv).r + texture2D(diffuseMap, vUv).g) * 0.5;
        isWater = clamp(isWater - (colorRG * .5), 0.0, 1.0);

        float isLand = 1.0 - isWater;
    
        // Bump mapping (fake height from green channel)
        float height = colorRG * 0.1; // Use green for elevation
        vec2 uvOffset = vec2(dFdx(vUv.x), dFdy(vUv.y)) * height * 10.0; // Approximate normal perturbation
        vec3 bumpNormal = normalize(vNormal + vec3(uvOffset, 0.0));
    
        // Lighting (view space)
        //vec3 lightDir = normalize((viewMatrix * vec4(lightPosition, 1.0)).xyz - vViewPosition);
        vec3 lightDir = normalize(v_vertToLight);
        float diffuse = max(dot(bumpNormal, lightDir * 1.5), 0.02); //ambient
    
        // Specular (Phong model)
        //vec3 vViewDir = normalize(-vViewPosition);
        vec3 reflectDir = reflect(-lightDir, bumpNormal);
        float spec = pow(max(dot(vViewDir, reflectDir), 0.0), 32.0); // Shininess = 32
        float gloss = mix(0.05, 0.8, isWater); // Water glossy, land matte
        float specular = spec * (gloss * 5.0);
    
        // Combine
        vec3 color = diffuseColor.rgb * diffuse + vec3(1.0, 1.0, 1.0) * specular;
        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 vViewPosition;
    varying vec3 v_vertToLight;
    varying vec3 vViewDir;
    
    void main() {
        vUv = uv;
        vNormal = normal;
        vViewPosition = (modelViewMatrix * vec4(position, 1.0)).xyz;

        vec3 worldNormal = normalize(nMatrix * normal);
        vNormal = normalize((vMatrix * vec4(worldNormal, 0.0)).xyz);
        vec4 viewSunPos   = vMatrix * vec4(lightPosition, 1.0);
        v_vertToLight = normalize(viewSunPos.xyz - vViewPosition.xyz);
        vViewDir = normalize(-vViewPosition);

        gl_Position = mvpMatrix * vec4(position, 1.0);
    }
`;
const fragmentShaderMoon = `
    uniform sampler2D diffuseMap; // Moon texture
    uniform vec3 lightPosition;
    varying vec2 vUv;
    varying vec3 vNormal;
    varying vec3 vViewPosition;
    varying vec3 v_vertToLight;
    varying vec3 vViewDir;
    
    void main() {
        vec4 diffuseColor = texture2D(diffuseMap, vUv);
        float height = texture2D(diffuseMap, vUv).r * 0.15; // Use red for crater depth
        vec2 uvOffset = vec2(dFdx(vUv.x), dFdy(vUv.y)) * height * 5.0;
        vec3 bumpNormal = normalize(vNormal + vec3(uvOffset, 0.0));
    
        //vec3 lightDir = normalize((viewMatrix * vec4(lightPosition, 1.0)).xyz - vViewPosition);
        vec3 lightDir = normalize(v_vertToLight);
        //float lightIntensity = max(dot(vNormal, lightDir), 0.0);
        float diffuse = max(dot(bumpNormal, lightDir * 1.5), 0.02);// ambient
    
        //vec3 vViewDir = normalize(-vViewPosition);
        vec3 reflectDir = reflect(-lightDir, bumpNormal);
        float spec = pow(max(dot(vViewDir, reflectDir), 0.0), 16.0); // Lower shininess for moon
        float specular = spec * (0.05 * 5.0);
    
        vec3 color = diffuseColor.rgb * diffuse + vec3(1.0, 1.0, 1.0) * 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;
    
    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);

        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;
    
    // Simple random function
    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));
    }

    //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 scale = 10.0;
        vec3 flow1 = pos * scale + vec3(time * 0.05);
        vec3 flow2 = pos * scale - vec3(time * 0.03);
        vec3 flow3 = pos * scale - vec3(time * 0.0015);
        vec3 flow4 = pos * scale - vec3(time * 0.015);
        float n1 = noise4D(flow1, time * 0.05);
        float n2 = noise4D(flow2, time * 0.03);
        float n3 = noise4D(flow3, time * 0.0015);
        float n4 = noise4D(flow4, time * 0.25);
        float n = (n2 + n3 - n1 + n4) / 3.0;
    
        // Cloud density and fading
        float cloudDensity = smoothstep(0.4, 0.9, n);
        vec3 cloudColor = vec3(1.0, 1.0, 1.0);
        float alpha = cloudDensity * 1.0;

        //vec3 P = getWorldPosition(vPosition.z, TexCoord); 
    
        // Lighting calculation (view space)
        vec3 fragToLight = normalize(v_vertToLight);
        //float lightIntensity = max(dot(v_Normal, fragToLight), 0.0);
        float lightIntensity = dot(v_vertToLight, v_Normal);

        lightIntensity = clamp(lightIntensity * 1.5, 0.05, 1.0); // Subtle night glow

    
        //vec3 testColor = vec3(0,.5,0);
        // Apply lighting to color
        cloudColor *= lightIntensity;
        //testColor *= lightIntensity;
    
        gl_FragColor = vec4(cloudColor, alpha);
        //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() {
        // Lighting calculation (view space)
        vec3 fragToLight = normalize(v_vertToLight);
        float lightIntensity = max(dot(v_Normal, fragToLight), 0.0);
    
        // Fresnel effect for rim glow
        //float fresnel = pow(1.0 - max(dot(v_Normal, vViewDir), 0.0), 3.0);
        float fresnel = pow(1.0 - abs(dot(v_Normal, vViewDir)), 2.0);
        fresnel = clamp(fresnel, 0.1, 1.0);

        // Atmosphere color (blue-cyan)
        vec3 atmColor = vec3(0.2, 0.6, 1.0); // Blue-cyan glow
        vec3 color = atmColor * fresnel * lightIntensity;
    
        // Transparency based on light and fresnel
        float alpha = fresnel * clamp(lightIntensity * 1.5, 0.1, 0.8);
    
        gl_FragColor = vec4(color, alpha);
        //gl_FragColor = vec4(color, 1);
    }
`;

        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,
            //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 }
            },
            //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, 64);
        //const sunMaterial = new THREE.MeshBasicMaterial({ map: sunTexture });
        // Create the shader material
        const sunMaterial = new THREE.ShaderMaterial({
            uniforms: {
                time: { value: 0.0 }
            },
            vertexShader: vertexShaderSun,
            fragmentShader: fragmentShaderSun
        });

        const sun = new THREE.Mesh(sunGeometry, sunMaterial);
        sun.position.set(0, 0, 0);
        scene.add(sun);


        // Earth
        //const earthTexture = textureLoader.load('earth_atmos_2048.jpg');
        const earthGeometry = new THREE.SphereGeometry(1, 64, 64);
        //const earthMaterial = new THREE.MeshPhongMaterial({ map: earthTexture });
        const earthMaterial = new THREE.ShaderMaterial({
            vertexShader: vertexShaderEarth,
            fragmentShader: fragmentShaderEarth,
            uniforms: {
                diffuseMap: { value: new THREE.TextureLoader().load('earth_atmos_2048.jpg') },
                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() }
            }
        });
        earthMaterial.castShadow = true;
        const earth = new THREE.Mesh(earthGeometry, earthMaterial);
        earth.position.set(100, 0, 0);
        scene.add(earth);

        // Atmosphere sphere setup
        const atmGeometry = new THREE.SphereGeometry(1.02, 64, 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,
            side: THREE.BackSide // Render on inner surface for better glow
        });
        const atmosphere = new THREE.Mesh(atmGeometry, atmMaterial);
        scene.add(atmosphere);


        // Create cloud sphere
        const cloudGeometry = new THREE.SphereGeometry(1.01, 64, 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
        });
        cloudMaterial.castShadow = true;
        const clouds = new THREE.Mesh(cloudGeometry, cloudMaterial);
        scene.add(clouds);


        // Moon
        //const moonTexture = textureLoader.load('moon_1024.jpg');
        const moonGeometry = new THREE.SphereGeometry(0.27, 32, 32);
        //const moonMaterial = new THREE.MeshPhongMaterial({ map: moonTexture });
        const moonMaterial = new THREE.ShaderMaterial({
            vertexShader: vertexShaderMoon,
            fragmentShader: fragmentShaderMoon,
            uniforms: {
                diffuseMap: { value: new THREE.TextureLoader().load('moon_1024.jpg') },
                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() }
            }
        });
        moonMaterial.castShadow = true;
        const moon = new THREE.Mesh(moonGeometry, moonMaterial);
        moon.position.set(105, 0, .5);
        scene.add(moon);

        // Asteroid MG1
        const asteroidGeometry = new THREE.SphereGeometry(0.05, 16, 16);
        const asteroidMaterial = new THREE.MeshBasicMaterial({ color: 0xaaaaaa });
        const asteroid = new THREE.Mesh(asteroidGeometry, asteroidMaterial);
        //asteroid.position.set(125, .7, -45);
        asteroid.position.set(115, .7, 51);
        scene.add(asteroid);

        // Orbital paths
        const earthOrbitCurve = new THREE.EllipseCurve(0, 0, 100, 100, 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
        const earthOrbit = new THREE.Line(earthOrbitGeometry, new THREE.LineBasicMaterial({ color: 0x0000ff }));
        scene.add(earthOrbit);

        const moonOrbitCurve = new THREE.EllipseCurve(0, 0, 5, 5, 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(5 / (Math.PI / 2)); // Aligns to XZ plane
        const moonOrbit = new THREE.Line(moonOrbitGeometry, new THREE.LineBasicMaterial({ color: 0x00ff00 }));
        scene.add(moonOrbit);

        // Asteroid path
        const asteroidPath = new THREE.Line(
            new THREE.BufferGeometry().setFromPoints([
                new THREE.Vector3(125, .7, -45),
                new THREE.Vector3(115, .7, 51)
            ]),
            new THREE.LineBasicMaterial({ color: 0xff0000 })
        );
        scene.add(asteroidPath);
        const asteroidMarkGeometry = new THREE.BufferGeometry();
        const asteroidMarkPositions = new Float32Array(6); // 2 vertices * 3 coordinates
        asteroidMarkGeometry.setAttribute('position', new THREE.BufferAttribute(asteroidMarkPositions, 3));
        const asteroidMark = new THREE.Line(
            asteroidMarkGeometry,
            new THREE.LineBasicMaterial({ color: 0x00ff00 })
        );
        asteroidMark.frustumCulled = false;
        scene.add(asteroidMark);

        // Camera setup
        camera.position.set(97, 2, 2);
        //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, 0, 2);

        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 = 192 * 24; // 192 days ~ 6.4 months / 2
        const earthOrbitalPeriodMs = 365.25 * 24 * 3600 * 1000;
        const moonOrbitalPeriodMs = 27.3 * 24 * 3600 * 1000;
        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 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', () => { isPlaying = true; speed = 1; });
        play100x.addEventListener('click', () => { isPlaying = true; speed = 100; });
        play1000x.addEventListener('click', () => { isPlaying = true; speed = 1000; });
        play10000x.addEventListener('click', () => { isPlaying = true; speed = 10000; });
        play100000x.addEventListener('click', () => { isPlaying = true; speed = 100000; });
        stop.addEventListener('click', () => { isPlaying = false; });

        let lockControls = false;
        let tDiff = new THREE.Vector3();
        let tMode = 0; // free look mode
        modeF.addEventListener('click', () => {
            tMode=0;
            for(let i=0;i<4;i++) mFL[i].style.display="none";
            mFL[0].style.display="inline-block";
        });
        modeL1.addEventListener('click', () => {
            tMode=1;
            tDiff.x = asteroid.position.x - camera.position.x;
            tDiff.y = asteroid.position.y - camera.position.y;
            tDiff.z = asteroid.position.z - camera.position.z;
            for(let i=0;i<4;i++) mFL[i].style.display="none";
            mFL[1].style.display="inline-block";
        });
        modeL2.addEventListener('click', () => {
            tMode=2;
            tDiff.x = moon.position.x - camera.position.x;
            tDiff.y = moon.position.y - camera.position.y;
            tDiff.z = moon.position.z - camera.position.z;
            for(let i=0;i<4;i++) mFL[i].style.display="none";
            mFL[2].style.display="inline-block";
        });
        modeL3.addEventListener('click', () => {
            tMode=3;
            tDiff.x = earth.position.x - camera.position.x;
            tDiff.y = earth.position.y - camera.position.y;
            tDiff.z = earth.position.z - camera.position.z;
            for(let i=0;i<4;i++) mFL[i].style.display="none";
            mFL[3].style.display="inline-block";
        });

        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 mvpMatrix = new THREE.Matrix4();
        const atmMvpMatrix = new THREE.Matrix4();
        
        const earthMvpMatrix = new THREE.Matrix4();
        const moonMvpMatrix = new THREE.Matrix4();

        let timeOffset = 0.0;
        let timeFraction = 0.0;

        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 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.set(100 * Math.cos(earthAngle), 0, 100 * Math.sin(earthAngle));

            clouds.position.set(earth.position.x, 0, earth.position.z);
            atmosphere.position.set(earth.position.x, 0, earth.position.z);

            const rad = Math.PI / 180;
            // Moon orbital position
            const moonAngle = (timeOffset / moonOrbitalPeriodMs) * 2 * Math.PI + (45 * rad);
            //const tF = -1 + (moonAngle - ( 90 / ( 180 / Math.PI )) * 2);
            const moonTilt = Math.cos(moonAngle);
            //const tF = -1 + (timeFraction * 2);
            moon.position.set(
                earth.position.x + 5 * Math.cos(moonAngle),
                //Math.abs(tF * .2),
                moonTilt * .2,
                earth.position.z + 5 * Math.sin(moonAngle)
            );

            // Asteroid MG1 position
            const asteroidX = 115 + (timeFraction * 5 * 2); // Moves from 14 to 8 over 24 hours
            const asteroidZ = 51 - (timeFraction * 48 * 2); // Moves from 3 to 6 over 24 hours
            asteroid.position.set(asteroidX, .7, asteroidZ);

            // Update asteroid marker line
            asteroidMarkPositions[0] = asteroidX; // Top vertex X
            asteroidMarkPositions[1] = 3.0;      // Top vertex Y (0.7 + 2)
            asteroidMarkPositions[2] = asteroidZ; // Top vertex Z
            asteroidMarkPositions[3] = asteroidX; // Bottom vertex X
            asteroidMarkPositions[4] = 1.0;      // Bottom vertex Y (asteroid's Y)
            asteroidMarkPositions[5] = asteroidZ; // Bottom vertex Z
            asteroidMarkGeometry.attributes.position.needsUpdate = true;

            // Earth rotation (360 degrees in 24 hours)
            const earthRotationAngle = (timeOffset / (24 * 3600 * 1000)) * 2 * Math.PI;
            earth.rotation.y = earthRotationAngle + 50;
            earth.rotation.x = -25;
            
            clouds.rotation.y = earthRotationAngle + 50;
            clouds.rotation.x = -25;
            clouds.matrixWorldNeedsUpdate = true; // Ensure matrix updates

            atmosphere.rotation.y = earthRotationAngle + 50;
            atmosphere.rotation.x = -25;
            atmosphere.matrixWorldNeedsUpdate = true;
           
            // 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;
            moon.rotation.y = ((Math.PI * 2) - moonRotationAngle) + (180 * rad);

            //console.log(moon.rotation.y);
            moon.rotation.x = -25 * rad;
            moon.rotation.z = -15 * rad;

            // Update Moon orbit position to follow Earth
            moonOrbit.position.set(earth.position.x, 0, earth.position.z);

            // Update cloud animation
            cloudMaterial.uniforms.time.value = timeFraction * (192 * 24 * 2); // Scale to hou
        });

        // 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');

        // 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();
        
                timeline.dispatchEvent(initialEvent);
            }

            //--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(tMode>0) {
                if(tMode==1) {
                    camera.position.set(asteroid.position.x - tDiff.x, asteroid.position.y - tDiff.y, asteroid.position.z - tDiff.z);
                    camera.lookAt(asteroid.position);
                    controls.target.copy(asteroid.position); // Update OrbitControls target
                } else if(tMode==2) {
                    camera.position.set(moon.position.x - tDiff.x, moon.position.y - tDiff.y, moon.position.z - tDiff.z);
                    camera.lookAt(moon.position);
                    controls.target.copy(moon.position); // Update OrbitControls target
                } else if(tMode==3) {
                    camera.position.set(earth.position.x - tDiff.x, earth.position.y - tDiff.y, earth.position.z - tDiff.z);
                    camera.lookAt(earth.position);
                    controls.target.copy(earth.position); // Update OrbitControls target
                }
                if(!lockControls) {
                    controls.enabled=false;
                    lockControls=true;
                    //console.log("controls locked");
                }
            } else {
                if(lockControls) {
                    controls.enabled=true;
                    lockControls=false;
                    //console.log("controls unlocked");
                }
            }
            
            // 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;

            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
            sunMaterial.uniforms.time.value += 0.015; // Adjust speed here
            // Update camera info
            const pos = camera.position;
            const target = controls.target; // OrbitControls look-at point
            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)})`;

            renderer.render(scene, camera);
            requestAnimationFrame(updateScene);
        }
        //updateScene();
        requestAnimationFrame(updateScene);

        // 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
©twily.info 2013 - 2025
twily at twily dot info



2 368 436 visits
... ^ v