Script: Solar System Simulation STEM

Solar System Simulation STEM picture
Type
Typescript logo indicatortypescript
Date Created
Mar 9, 2025, 11:21:44 AM
Last Edit Date
Mar 18, 2025, 2:09:17 PM

Project Information

We've made a solar system simulation in collaboration with Artificial Intelligence

View Full Project

Script Code

async function start() {
    const scene = bitbybit.babylon.scene.getScene();
    const engine = scene.getEngine();

    // Setup point light with shadow generator
    const ptLight = new Bit.Inputs.BabylonScene.PointLightDto();
    ptLight.intensity = 50000;
    ptLight.position = [0, 0, 0];
    ptLight.shadowDarkness = 0;
    ptLight.radius = 0;
    ptLight.shadowMaxZ = 3000;
    ptLight.shadowMinZ = 0;
    const light = bitbybit.babylon.scene.drawPointLight(ptLight);

    // GUI Setup
    const advancedTexture = BABYLON.GUI.AdvancedDynamicTexture.CreateFullscreenUI("UI");
    const hoverText = new BABYLON.GUI.TextBlock();
    hoverText.text = "";
    hoverText.color = "white";
    hoverText.fontSize = 36;
    hoverText.fontFamily = "Arial";
    hoverText.paddingTop = "15px";
    hoverText.paddingBottom = "15px";
    hoverText.paddingLeft = "20px";
    hoverText.paddingRight = "20px";
    hoverText.isVisible = false;
    advancedTexture.addControl(hoverText);

    const speedSlider = new BABYLON.GUI.Slider();
    speedSlider.minimum = 0.05;
    speedSlider.maximum = 1.0;
    speedSlider.value = 0.05;
    speedSlider.step = 0.01;
    speedSlider.height = "30px";
    speedSlider.width = "300px";
    speedSlider.left = "30px";
    speedSlider.top = "30px";
    speedSlider.color = "#f0cebb";
    speedSlider.background = "rgba(255, 255, 255, 0.2)";
    speedSlider.horizontalAlignment = BABYLON.GUI.Control.HORIZONTAL_ALIGNMENT_LEFT;
    speedSlider.verticalAlignment = BABYLON.GUI.Control.VERTICAL_ALIGNMENT_TOP;
    advancedTexture.addControl(speedSlider);

    const sliderLabel = new BABYLON.GUI.TextBlock();
    sliderLabel.text = "Animation Speed: " + speedSlider.value.toFixed(2) + "x";
    sliderLabel.color = "white";
    sliderLabel.fontSize = 24;
    sliderLabel.left = "30px";
    sliderLabel.top = "70px";
    sliderLabel.textHorizontalAlignment = BABYLON.GUI.Control.HORIZONTAL_ALIGNMENT_LEFT;
    sliderLabel.textVerticalAlignment = BABYLON.GUI.Control.VERTICAL_ALIGNMENT_TOP;
    advancedTexture.addControl(sliderLabel);


    // Language selection buttons
    const languagePanel = new BABYLON.GUI.StackPanel();
    languagePanel.isVertical = true;
    languagePanel.width = "150px";
    languagePanel.left = "30px";
    languagePanel.top = "110px";
    languagePanel.horizontalAlignment = BABYLON.GUI.Control.HORIZONTAL_ALIGNMENT_LEFT;
    languagePanel.verticalAlignment = BABYLON.GUI.Control.VERTICAL_ALIGNMENT_TOP;
    advancedTexture.addControl(languagePanel);

    let currentLanguage = "en";
    const languages = [
        { name: "English", code: "en" },
        { name: "Lietuvių", code: "lt" },
        { name: "Polski", code: "pl" },
        { name: "Deutsch", code: "de" },
        { name: "Español", code: "es" },
        { name: "中文", code: "zh" }
    ];

    const languageButtons = languages.map(lang => {
        const button = BABYLON.GUI.Button.CreateSimpleButton(`lang_${lang.code}`, lang.name);
        button.width = "150px";
        button.height = "30px";
        button.color = "white";
        button.background = "rgba(255, 255, 255, 0.2)";
        button.paddingBottom = "5px";
        button.onPointerUpObservable.add(() => {
            currentLanguage = lang.code;
            // Update button visuals to show selection
            languageButtons.forEach(btn => {
                btn.background = "rgba(255, 255, 255, 0.2)";
                btn.color = "#ffffff";
            });
            button.background = "#f0cebb"; // Highlight selected language
            button.color = "#000000"; // Highlight selected language
        });
        if (lang.code === "en") button.background = "#f0cebb55"; // Default to English
        languagePanel.addControl(button);
        return button;
    });

    createLogo();
    
    speedSlider.onValueChangedObservable.add((value) => {
        sliderLabel.text = "Animation Speed: " + value.toFixed(2) + "x";
    });

    // Language translations
    const translations = {
        "en": {
            "Sun": "Sun", "Mercury": "Mercury", "Venus": "Venus", "Earth": "Earth",
            "Moon": "Moon", "Mars": "Mars", "Phobos": "Phobos", "Deimos": "Deimos",
            "Jupiter": "Jupiter", "Io": "Io", "Europa": "Europa", "Ganymede": "Ganymede",
            "Callisto": "Callisto", "Saturn": "Saturn", "Mimas": "Mimas", "Enceladus": "Enceladus",
            "Tethys": "Tethys", "Dione": "Dione", "Rhea": "Rhea", "Titan": "Titan",
            "Iapetus": "Iapetus", "Uranus": "Uranus", "Miranda": "Miranda", "Ariel": "Ariel",
            "Umbriel": "Umbriel", "Titania": "Titania", "Oberon": "Oberon", "Neptune": "Neptune",
            "Triton": "Triton", "Nereid": "Nereid"
        },
        "lt": {
            "Sun": "Saulė", "Mercury": "Merkurijus", "Venus": "Venera", "Earth": "Žemė",
            "Moon": "Mėnulis", "Mars": "Marsas", "Phobos": "Fobas", "Deimos": "Deimas",
            "Jupiter": "Jupiteris", "Io": "Io", "Europa": "Europa", "Ganymede": "Ganimedas",
            "Callisto": "Kalista", "Saturn": "Saturnas", "Mimas": "Mimas", "Enceladus": "Enceladas",
            "Tethys": "Tetija", "Dione": "Dionė", "Rhea": "Rėja", "Titan": "Titanas",
            "Iapetus": "Iapetas", "Uranus": "Uranas", "Miranda": "Miranda", "Ariel": "Arielė",
            "Umbriel": "Umbrielis", "Titania": "Titanija", "Oberon": "Oberonas", "Neptune": "Neptūnas",
            "Triton": "Tritonas", "Nereid": "Nereidė"
        },
        "pl": {
            "Sun": "Słońce", "Mercury": "Merkury", "Venus": "Wenus", "Earth": "Ziemia",
            "Moon": "Księżyc", "Mars": "Mars", "Phobos": "Fobos", "Deimos": "Deimos",
            "Jupiter": "Jowisz", "Io": "Io", "Europa": "Europa", "Ganymede": "Ganimedes",
            "Callisto": "Kallisto", "Saturn": "Saturn", "Mimas": "Mimas", "Enceladus": "Enceladus",
            "Tethys": "Tetyda", "Dione": "Dione", "Rhea": "Rea", "Titan": "Tytan",
            "Iapetus": "Japetus", "Uranus": "Uran", "Miranda": "Miranda", "Ariel": "Ariel",
            "Umbriel": "Umbriel", "Titania": "Tytania", "Oberon": "Oberon", "Neptune": "Neptun",
            "Triton": "Tryton", "Nereid": "Nereida"
        },
        "de": {
            "Sun": "Sonne", "Mercury": "Merkur", "Venus": "Venus", "Earth": "Erde",
            "Moon": "Mond", "Mars": "Mars", "Phobos": "Phobos", "Deimos": "Deimos",
            "Jupiter": "Jupiter", "Io": "Io", "Europa": "Europa", "Ganymede": "Ganymed",
            "Callisto": "Kallisto", "Saturn": "Saturn", "Mimas": "Mimas", "Enceladus": "Enceladus",
            "Tethys": "Tethys", "Dione": "Dione", "Rhea": "Rhea", "Titan": "Titan",
            "Iapetus": "Iapetus", "Uranus": "Uranus", "Miranda": "Miranda", "Ariel": "Ariel",
            "Umbriel": "Umbriel", "Titania": "Titania", "Oberon": "Oberon", "Neptune": "Neptun",
            "Triton": "Triton", "Nereid": "Nereid"
        },
        "es": {
            "Sun": "Sol", "Mercury": "Mercurio", "Venus": "Venus", "Earth": "Tierra",
            "Moon": "Luna", "Mars": "Marte", "Phobos": "Fobos", "Deimos": "Deimos",
            "Jupiter": "Júpiter", "Io": "Ío", "Europa": "Europa", "Ganymede": "Ganímedes",
            "Callisto": "Calisto", "Saturn": "Saturno", "Mimas": "Mimas", "Enceladus": "Encélado",
            "Tethys": "Tetis", "Dione": "Dione", "Rhea": "Rea", "Titan": "Titán",
            "Iapetus": "Jápeto", "Uranus": "Urano", "Miranda": "Miranda", "Ariel": "Ariel",
            "Umbriel": "Umbriel", "Titania": "Titania", "Oberon": "Oberón", "Neptune": "Neptuno",
            "Triton": "Tritón", "Nereid": "Nereida"
        },
        "zh": {
            "Sun": "太阳", "Mercury": "水星", "Venus": "金星", "Earth": "地球",
            "Moon": "月球", "Mars": "火星", "Phobos": "火卫一", "Deimos": "火卫二",
            "Jupiter": "木星", "Io": "木卫一", "Europa": "木卫二", "Ganymede": "木卫三",
            "Callisto": "木卫四", "Saturn": "土星", "Mimas": "土卫一", "Enceladus": "土卫二",
            "Tethys": "土卫三", "Dione": "土卫四", "Rhea": "土卫五", "Titan": "土卫六",
            "Iapetus": "土卫八", "Uranus": "天王星", "Miranda": "天卫五", "Ariel": "天卫一",
            "Umbriel": "天卫二", "Titania": "天卫三", "Oberon": "天卫四", "Neptune": "海王星",
            "Triton": "海卫一", "Nereid": "海卫二"
        }
    };
    // Sun Vertex Shader
    BABYLON.Effect.ShadersStore["sunVertexShader"] = `
    precision highp float;
    attribute vec3 position;
    attribute vec3 normal;
    uniform mat4 world;
    uniform mat4 worldViewProjection;
    varying vec3 vPosition;
    varying vec3 vNormal;
    varying vec3 vWorldPosition;
    void main() {
        gl_Position = worldViewProjection * vec4(position, 1.0);
        vPosition = position;
        vWorldPosition = (world * vec4(position, 1.0)).xyz;
        vNormal = normalize((world * vec4(normal, 0.0)).xyz);
    }
    `;

    // Sun Fragment Shader
    BABYLON.Effect.ShadersStore["sunFragmentShader"] = `
    precision highp float;
    varying vec3 vPosition;
    varying vec3 vNormal;
    varying vec3 vWorldPosition;
    uniform float time;
    uniform vec3 cameraPosition;
    float hash(vec3 p) { return fract(sin(dot(p, vec3(127.1, 311.7, 74.7))) * 43758.5453); }
    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(hash(i + vec3(0.0,0.0,0.0)), hash(i + vec3(1.0,0.0,0.0)), u.x),
                      mix(hash(i + vec3(0.0,1.0,0.0)), hash(i + vec3(1.0,1.0,0.0)), u.x), u.y),
                  mix(mix(hash(i + vec3(0.0,0.0,1.0)), hash(i + vec3(1.0,0.0,1.0)), u.x),
                      mix(hash(i + vec3(0.0,1.0,1.0)), hash(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 < 4; ++i) {
            v += a * noise(p);
            p = p * 2.0 + shift;
            a *= 0.5;
        }
        return v;
    }
    void main() {
        vec3 pos = normalize(vPosition);
        vec2 sphericalUV = vec2(atan(pos.z, pos.x), asin(pos.y));
        sphericalUV.x = sphericalUV.x * 0.159154943 + 0.5;
        sphericalUV.y = sphericalUV.y * 0.318309886 + 0.5;
        
        vec3 baseColor = mix(vec3(1.0, 0.9, 0.4), vec3(1.0, 0.5, 0.1), length(sphericalUV - 0.5));
        float spots = fbm(pos * 8.0 + time * 0.1);
        spots = pow(spots, 3.0);
        vec3 spotColor = vec3(0.8, 0.4, 0.1) * spots;
        float detail = fbm(pos * 20.0 + time * 0.05);
        vec3 detailColor = vec3(0.2, 0.1, 0.0) * detail;
        vec3 viewDir = normalize(cameraPosition - vWorldPosition);
        float rim = 1.0 - max(dot(vNormal, viewDir), 0.0);
        
        vec3 finalColor = baseColor;
        finalColor = mix(finalColor, spotColor, spots * 0.8);
        finalColor += detailColor;
        finalColor += vec3(1.0, 0.8, 0.2) * pow(rim, 1.5) * 0.4;
        float pulse = sin(time * 1.5) * 0.03 + 0.97;
        finalColor *= pulse;
        
        gl_FragColor = vec4(finalColor, 1.0);
    }
    `;

    // Space Vertex Shader
    BABYLON.Effect.ShadersStore["spaceVertexShader"] = `
    precision highp float;
    attribute vec3 position;
    attribute vec3 normal;
    uniform mat4 world;
    uniform mat4 view;
    uniform mat4 projection;
    varying vec3 vPosition;
    varying vec3 vNormal;
    void main() {
        vPosition = position;
        vNormal = normal;
        gl_Position = projection * view * world * vec4(position, 1.0);
    }
    `;

    // Space Fragment Shader
    BABYLON.Effect.ShadersStore["spaceFragmentShader"] = `
    precision highp float;
    varying vec3 vPosition;
    uniform float time;
    
    float hash(vec3 p) {
        p = fract(p * 0.3183099 + 0.1);
        return fract(sin(dot(p, vec3(127.1, 311.7, 74.7))) * 43758.5453);
    }
    
    float noise(vec3 x) {
        vec3 i = floor(x);
        vec3 f = fract(x);
        f = f * f * (3.0 - 2.0 * f);
        return mix(mix(mix(hash(i + vec3(0.0, 0.0, 0.0)), hash(i + vec3(1.0, 0.0, 0.0)), f.x),
                    mix(hash(i + vec3(0.0, 1.0, 0.0)), hash(i + vec3(1.0, 1.0, 0.0)), f.x), f.y),
                mix(mix(hash(i + vec3(0.0, 0.0, 1.0)), hash(i + vec3(1.0, 0.0, 1.0)), f.x),
                    mix(hash(i + vec3(0.0, 1.0, 1.0)), hash(i + vec3(1.0, 1.0, 1.0)), f.x), f.y), f.z);
    }
    
    float fbm(vec3 p) {
        float v = 0.0;
        float a = 0.5;
        vec3 shift = vec3(100.0);
        for (int i = 0; i < 2; ++i) {
            v += a * noise(p);
            p = p * 2.0 + shift;
            a *= 0.5;
        }
        return v;
    }
    
    void main() {
        vec3 pos = normalize(vPosition) * 10.0 + vec3(time * 0.01);
        float stars = noise(pos * 100.0);
        stars = pow(stars, 40.0);
        float nebula = fbm(pos * 0.5);
        vec3 nebulaColor = vec3(0.3, 0.2, 0.7) * nebula * 0.8;
        vec3 color = vec3(0.0);
        color += vec3(0.8, 0.8, 0.9) * stars;
        color += nebulaColor;
        color = mix(vec3(0.0, 0.0, 0.05), color, clamp(color.r + color.g + color.b, 0.0, 1.0));
        gl_FragColor = vec4(color, 1.0);
    }
    `;

    // Space background
    const shaderMaterialSpace = new BABYLON.ShaderMaterial("spaceShader", scene, {
        vertex: "space",
        fragment: "space",
    }, {
        attributes: ["position", "normal"],
        uniforms: ["world", "view", "projection", "time"]
    });
    const box = BABYLON.MeshBuilder.CreateBox("box", { size: 10000 }, scene);
    box.material = shaderMaterialSpace;
    box.flipFaces();

    // Scaling factors
    const AU = 30;
    const planetScale = 1;
    const moonScale = 1;

    // Sun setup
    const sun = BABYLON.MeshBuilder.CreateSphere("sun", { diameter: 5 * planetScale, segments: 128 }, scene);
    const sunMaterial = new BABYLON.ShaderMaterial("sunShader", scene, {
        vertex: "sun",
        fragment: "sun",
    }, {
        attributes: ["position", "normal"],
        uniforms: ["world", "worldViewProjection", "time", "cameraPosition"]
    });
    sunMaterial.setVector3("cameraPosition", scene.activeCamera.position);
    sun.material = sunMaterial;

    const sgs = scene.metadata.shadowGenerators;
    // sgs.forEach(shadowGenerator => {
    //     shadowGenerator.addShadowCaster(sun);
    // });

    // Celestial body definitions
    const celestialData = [
        {
            name: "Mercury", diameter: 0.8, orbit: 0.4, speed: 2.0, color: [0.7, 0.7, 0.7],
            metallic: 0.1, roughness: 0.8, rotationSpeed: 0.06, moons: []
        },
        {
            name: "Venus", diameter: 1.8, orbit: 0.7, speed: 0.8, color: [0.9, 0.6, 0.3],
            metallic: 0.05, roughness: 0.6, rotationSpeed: 0.04, moons: []
        },
        {
            name: "Earth", diameter: 1.8, orbit: 1, speed: 0.5, color: [0.0, 0.6, 0.8],
            metallic: 0.0, roughness: 0.5, rotationSpeed: 0.05,
            moons: [{ name: "Moon", diameter: 0.5, orbit: 2, speed: 4, color: [0.8, 0.8, 0.8], metallic: 0.1, roughness: 0.9, rotationSpeed: 0.02 }]
        },
        {
            name: "Mars", diameter: 1.5, orbit: 1.5, speed: 0.4, color: [0.8, 0.4, 0.2],
            metallic: 0.1, roughness: 0.7, rotationSpeed: 0.03,
            moons: [
                { name: "Phobos", diameter: 0.3, orbit: 1, speed: 3, color: [0.6, 0.5, 0.5], metallic: 0.1, roughness: 0.9, rotationSpeed: 0.04 },
                { name: "Deimos", diameter: 0.2, orbit: 1.5, speed: 2.5, color: [0.7, 0.6, 0.6], metallic: 0.1, roughness: 0.9, rotationSpeed: 0.03 }
            ]
        },
        {
            name: "Jupiter", diameter: 4, orbit: 5, speed: 0.2, color: [0.9, 0.7, 0.5],
            metallic: 0.0, roughness: 0.4, rotationSpeed: 0.02,
            moons: [
                { name: "Io", diameter: 0.7, orbit: 2, speed: 2.5, color: [1.0, 0.8, 0.3], metallic: 0.1, roughness: 0.7, rotationSpeed: 0.03 },
                { name: "Europa", diameter: 0.6, orbit: 3, speed: 2, color: [0.9, 0.9, 0.9], metallic: 0.0, roughness: 0.3, rotationSpeed: 0.025 },
                { name: "Ganymede", diameter: 1.0, orbit: 4, speed: 1.5, color: [0.7, 0.7, 0.7], metallic: 0.1, roughness: 0.8, rotationSpeed: 0.02 },
                { name: "Callisto", diameter: 0.9, orbit: 5, speed: 1, color: [0.6, 0.6, 0.6], metallic: 0.1, roughness: 0.9, rotationSpeed: 0.015 }
            ]
        },
        {
            name: "Saturn", diameter: 3.5, orbit: 9.5, speed: 0.15, color: [0.8, 0.7, 0.4],
            metallic: 0.0, roughness: 0.4, rotationSpeed: 0.015,
            rings: { radius: 5, color: [0.7, 0.6, 0.5], metallic: 0.0, roughness: 0.6 },
            moons: [
                { name: "Mimas", diameter: 0.3, orbit: 1, speed: 3, color: [0.8, 0.8, 0.8], metallic: 0.1, roughness: 0.9, rotationSpeed: 0.04 },
                { name: "Enceladus", diameter: 0.4, orbit: 1.5, speed: 2.5, color: [0.9, 0.9, 0.9], metallic: 0.0, roughness: 0.3, rotationSpeed: 0.035 },
                { name: "Tethys", diameter: 0.5, orbit: 2, speed: 2, color: [0.8, 0.8, 0.8], metallic: 0.1, roughness: 0.8, rotationSpeed: 0.03 },
                { name: "Dione", diameter: 0.6, orbit: 2.5, speed: 1.8, color: [0.7, 0.7, 0.7], metallic: 0.1, roughness: 0.8, rotationSpeed: 0.025 },
                { name: "Rhea", diameter: 0.7, orbit: 3, speed: 1.5, color: [0.8, 0.8, 0.8], metallic: 0.1, roughness: 0.8, rotationSpeed: 0.02 },
                { name: "Titan", diameter: 1.0, orbit: 4, speed: 1, color: [0.9, 0.6, 0.3], metallic: 0.0, roughness: 0.5, rotationSpeed: 0.015 },
                { name: "Iapetus", diameter: 0.8, orbit: 5, speed: 0.8, color: [0.5, 0.5, 0.5], metallic: 0.1, roughness: 0.9, rotationSpeed: 0.01 }
            ]
        },
        {
            name: "Uranus", diameter: 3, orbit: 19, speed: 0.1, color: [0.5, 0.8, 0.8],
            metallic: 0.0, roughness: 0.4, rotationSpeed: 0.012,
            moons: [
                { name: "Miranda", diameter: 0.3, orbit: 1, speed: 3, color: [0.7, 0.7, 0.7], metallic: 0.1, roughness: 0.9, rotationSpeed: 0.04 },
                { name: "Ariel", diameter: 0.5, orbit: 1.5, speed: 2.5, color: [0.8, 0.8, 0.8], metallic: 0.1, roughness: 0.8, rotationSpeed: 0.035 },
                { name: "Umbriel", diameter: 0.6, orbit: 2, speed: 2, color: [0.6, 0.6, 0.6], metallic: 0.1, roughness: 0.9, rotationSpeed: 0.03 },
                { name: "Titania", diameter: 0.8, orbit: 3, speed: 1.5, color: [0.7, 0.7, 0.7], metallic: 0.1, roughness: 0.8, rotationSpeed: 0.02 },
                { name: "Oberon", diameter: 0.7, orbit: 4, speed: 1, color: [0.6, 0.6, 0.6], metallic: 0.1, roughness: 0.9, rotationSpeed: 0.015 }
            ]
        },
        {
            name: "Neptune", diameter: 3, orbit: 30, speed: 0.08, color: [0.2, 0.4, 0.8],
            metallic: 0.0, roughness: 0.4, rotationSpeed: 0.01,
            moons: [
                { name: "Triton", diameter: 0.9, orbit: 3, speed: 1.5, color: [0.7, 0.7, 0.8], metallic: 0.1, roughness: 0.8, rotationSpeed: 0.02 },
                { name: "Nereid", diameter: 0.3, orbit: 5, speed: 0.5, color: [0.6, 0.6, 0.7], metallic: 0.1, roughness: 0.9, rotationSpeed: 0.01 }
            ]
        }
    ];

    // Utility functions
    const createOrbitPath = (radius, parent = null, segments = 128) => {
        const path = [];
  
        for (let i = 0; i <= segments; i++) {
            const angle = (i / segments) * Math.PI * 2;
            path.push(new BABYLON.Vector3(Math.cos(angle) * radius, 0, Math.sin(angle) * radius));
        }
        const orbit = BABYLON.MeshBuilder.CreateLines(`orbit_${radius}`, { points: path }, scene);
        orbit.color = new BABYLON.Color3(0.5, 0.5, 0.5);
        orbit.alpha = 0.5;
        orbit.visibility = 0.1;
    
        if (parent) orbit.parent = parent;
        return orbit;
    };

    const createBody = (name, diameter, color, metallic, roughness, isMoon = false) => {
        const mesh = BABYLON.MeshBuilder.CreateSphere(name, {
            diameter: (isMoon ? moonScale : planetScale) * diameter,
            segments: 32
        }, scene);
        const mat = new BABYLON.PBRMetallicRoughnessMaterial(`${name}Mat`, scene);
        mat.baseColor = new BABYLON.Color3(...color);
        mat.metallic = metallic;
        mat.roughness = roughness;
        mat.backFaceCulling = false;
        mesh.material = mat;
        mesh.receiveShadows = true;
        // sgs.forEach(shadowGenerator => {
        //     shadowGenerator.addShadowCaster(mesh);
        // });
        return mesh;
    };

    // Create celestial bodies
    const celestialBodies = [{ mesh: sun, name: "Sun" }];
    const planets = celestialData.map(planet => {
        const mesh = createBody(planet.name, planet.diameter, planet.color, planet.metallic, planet.roughness);
        mesh.position.x = AU * planet.orbit;
        createOrbitPath(AU * planet.orbit);

        if (planet.rings) {
            const ringMat = new BABYLON.PBRMetallicRoughnessMaterial("ringMat", scene);
            ringMat.baseColor = new BABYLON.Color3(...planet.rings.color);
            ringMat.metallic = planet.rings.metallic;
            ringMat.roughness = planet.rings.roughness;
            ringMat.backFaceCulling = true;
            const ring = BABYLON.MeshBuilder.CreateDisc("ring", { radius: planet.rings.radius * planetScale, tessellation: 64 }, scene);
            ring.rotation.x = Math.PI / 2;
            ring.parent = mesh;
            ring.material = ringMat;
            ring.receiveShadows = true;
        }

        const moons = planet.moons.map(moon => {
            const moonMesh = createBody(moon.name, moon.diameter, moon.color, moon.metallic, moon.roughness, true);
            moonMesh.position.x = AU * planet.orbit + moon.orbit;
            createOrbitPath(moon.orbit, mesh);
            return { mesh: moonMesh, data: moon };
        });

        celestialBodies.push({ mesh, name: planet.name });
        moons.forEach(moon => celestialBodies.push({ mesh: moon.mesh, name: moon.data.name }));

        return { mesh, data: planet, moons };
    });

    // Setup pointer events with language support
    const updateHoverText = (body, language) => {
        hoverText.text = translations[language][body.name] || body.name;
    };
    // Setup pointer events
    celestialBodies.forEach(body => {
        body.mesh.actionManager = new BABYLON.ActionManager(scene);
        body.mesh.actionManager.registerAction(
            new BABYLON.ExecuteCodeAction(BABYLON.ActionManager.OnPointerOverTrigger, () => {
                updateHoverText(body, currentLanguage);
                hoverText.isVisible = true;
            })
        );
        body.mesh.actionManager.registerAction(
            new BABYLON.ExecuteCodeAction(BABYLON.ActionManager.OnPointerOutTrigger, () => {
                hoverText.isVisible = false;
            })
        );
    });

    const canvas = engine.getRenderingCanvas();
    const scalingFactor = 1 / engine.getHardwareScalingLevel();
    scene.onPointerObservable.add((pointerInfo) => {
        if (hoverText.isVisible) {
            const rect = canvas.getBoundingClientRect();
            const x = (pointerInfo.event.x - rect.width / 2) * scalingFactor;
            const y = (pointerInfo.event.y - rect.height / 2) * scalingFactor;
            hoverText.left = x + 60 + "px";
            hoverText.top = y - 60 + "px";
        }
    }, BABYLON.PointerEventTypes.POINTERMOVE);

    // Animation
    let time = 0;
    let twinkleTime = 0;
    bitbybit.time.registerRenderFunction(() => {
        const speedFactor = speedSlider.value;
        time += engine.getDeltaTime() * 0.001 * speedFactor;
        twinkleTime += 0.001;
        sunMaterial.setFloat("time", twinkleTime * 40);
        sunMaterial.setVector3("cameraPosition", scene.activeCamera.position);
        shaderMaterialSpace.setFloat("time", twinkleTime);
        sun.rotation.y += 0.01 * speedFactor;

        planets.forEach(planet => {
            const p = planet.data;
            planet.mesh.position.x = Math.cos(time * p.speed) * AU * p.orbit;
            planet.mesh.position.z = Math.sin(time * p.speed) * AU * p.orbit;
            planet.mesh.rotation.y += p.rotationSpeed * speedFactor;

            planet.moons.forEach(moon => {
                const m = moon.data;
                moon.mesh.position.x = planet.mesh.position.x + Math.cos(time * m.speed) * m.orbit;
                moon.mesh.position.z = planet.mesh.position.z + Math.sin(time * m.speed) * m.orbit;
                moon.mesh.rotation.y += m.rotationSpeed * speedFactor;
            });
        });
    });

    // Add HDR Glow Effect
    const glowLayer = new BABYLON.GlowLayer("glow", scene, {
        // mainTextureFixedSize: 1024,
        mainTextureRatio: 1, // Adjust for performance vs quality
        blurKernelSize: 32,    // Larger kernel for softer glow
    });
    glowLayer.intensity = 0.22;
    glowLayer.addExcludedMesh(box); // Exclude the background box from glowing
    glowLayer.referenceMeshToUseItsOwnMaterial(sun);

}


function createLogo() {
    const gui = bitbybit.babylon.gui;
    const textOpt = new Bit.Inputs.BabylonGui.CreateFullScreenUIDto();
    const fullScreenUI = gui.advancedDynamicTexture.createFullScreenUI(textOpt);
    const stackPanelOpt = new Bit.Inputs.BabylonGui.CreateStackPanelDto();
    stackPanelOpt.background = "#00000000";
    const stackPanel = gui.stackPanel.createStackPanel(stackPanelOpt);
    const stackPanelOpt2 = new Bit.Inputs.BabylonGui.CreateStackPanelDto();
    stackPanelOpt2.background = "#00000000";
    const stackPanel2 = gui.stackPanel.createStackPanel(stackPanelOpt2);
    stackPanel2.verticalAlignment = BABYLON.GUI.Control.VERTICAL_ALIGNMENT_BOTTOM;
    fullScreenUI.addControl(stackPanel2);
    stackPanel.paddingTopInPixels = 10;
    stackPanel2.addControl(stackPanel);

    const imageOpt = new Bit.Inputs.BabylonGui.CreateImageDto();
    imageOpt.url = "assets/logo-gold-small.png";
    const image = gui.image.createImage(imageOpt);
    const padding = 30;
    image.paddingTopInPixels = padding;
    image.paddingBottomInPixels = padding;
    image.paddingLeftInPixels = padding;
    image.paddingRightInPixels = padding;

    image.onPointerClickObservable.add(() => {
        window.open("https://bitbybit.dev", '_blank').focus();
    });
    const txtBlockOptions = new Bit.Inputs.BabylonGui.CreateTextBlockDto();
    txtBlockOptions.text = "BITBYBIT.DEV";
    const txtBlock = gui.textBlock.createTextBlock(txtBlockOptions);
    txtBlock.height = "60px";
    txtBlock.paddingBottomInPixels = 30;

    stackPanel2.addControl(image);
    stackPanel2.addControl(txtBlock);
}

start().catch(console.error);