Script: Trampoline

Trampoline picture
Type
Typescript logo indicatortypescript
Author
matas
Date Created
Nov 12, 2023, 5:21:38 PM
Last Edit Date
Oct 2, 2025, 7:10:40 PM

Project Information

Physics demo of a trampoline that rolls alloy wheels.

View Full Project

Script Code

const scene = bitbybit.babylon.scene.getScene();
const wheelScale = 0.5;

const camera = scene.activeCamera as BABYLON.ArcRotateCamera;
camera.position = new BABYLON.Vector3(110, 10, 110);
camera.target = new BABYLON.Vector3(0, 30, 0);

const start = async () => {

    bitbybit.babylon.scene.enablePhysics({
        vector: [0, -9.81, 0]
    });

    const alloyWheelAssetFile = await bitbybit.asset.fetchFile({
        url: "https://bitbybit.dev/files/users%2FfYfmPXgG8YSLMSc2YiIpHr0WrG42%2Fprojects%2FVS3CdFFzKfRMr99jsnaq%2Fassets%2Falloy-wheel.glb?alt=media&token=f213e661-1fa5-4985-af52-bb43582e09dc"
    });

    const alloyWheelMesh = await bitbybit.babylon.io.loadAssetIntoScene({
        assetFile: alloyWheelAssetFile,
        hidden: false
    });

    alloyWheelMesh.getChildMeshes().forEach(m => m.isVisible = false);

    const width = 40;
    const pointsHorizontal = [
        [-55, 55, -width / 2],
        [-35, 50, -width / 2],
        [-30, 50, -width / 2],
        [0, 25, -width / 2],
        [5, 20, -width / 2],
        [10, 15, -width / 2],
        [30, 10, -width / 2],
        [50, 20, -width / 2],
    ] as Bit.Inputs.Base.Point3[];

    const wireHorizontal = await bitbybit.occt.shapes.wire.interpolatePoints({
        points: pointsHorizontal,
        periodic: false,
        tolerance: 0.1
    });

    const extrude = await bitbybit.occt.operations.extrude({
        shape: wireHorizontal,
        direction: [0, 0, width]
    });

    const thick = await bitbybit.occt.operations.makeThickSolidSimple({
        shape: extrude,
        offset: -2
    });

    const fillet = await bitbybit.occt.fillets.filletEdges({
        shape: thick,
        radius: 0.6
    })

    const optionsDrawSlide = new Bit.Inputs.Draw.DrawOcctShapeOptions();
    optionsDrawSlide.faceColour = "#000055";
    const filletShapeMesh = await bitbybit.draw.drawAnyAsync({
        entity: fillet,
        options: optionsDrawSlide
    });

    const thichMesh = filletShapeMesh.getChildMeshes().find(m => m.name.includes("surface")) as BABYLON.Mesh;

    const groundMesh = await createBox();
    const groundCollisionMesh = groundMesh.getChildMeshes().find((m) => m.name.includes("surface")) as BABYLON.Mesh;


    for (let z = -width / 2 + 5; z < width / 2; z += 5) {
        const alloyWheelInstanceParent = new BABYLON.Mesh(`${z}`);

        const children = alloyWheelMesh.getChildren();
        const root = children.find(c => c.name === "__root__");
        const rootChildren = root.getChildMeshes(false).filter(s => s.name.includes("surface"));
        console.log(rootChildren);

        rootChildren.forEach((c: BABYLON.Mesh) => {
            console.log(c);
            const inst = alloyWheelMesh.createInstance(c.name + "instance" + z);
            inst.isVisible = true;
            inst.receiveShadows = true;

            inst.parent = alloyWheelInstanceParent;
        })
        alloyWheelInstanceParent.scaling = new BABYLON.Vector3(wheelScale, wheelScale, wheelScale);
        alloyWheelInstanceParent.position = new BABYLON.Vector3(-50, 60, z);
        new BABYLON.PhysicsAggregate(alloyWheelInstanceParent, BABYLON.PhysicsShapeType.CYLINDER, { pointA: new BABYLON.Vector3(0, 0, -4), pointB: new BABYLON.Vector3(0, 0, 0), friction: 1, radius: 7, mass: 20, restitution: 0.75 }, scene);

    }

    let timeoutIncrease = 5000;
    const shadowGenerators = scene.metadata.shadowGenerators as BABYLON.ShadowGenerator[];
    for (let y = 0; y <= 60; y += 20) {
        setTimeout(() => {
            for (let z = -width / 2 + 5; z < width / 2; z += 5) {
                const alloyWheelInstanceParent = new BABYLON.Mesh(`${z}`);
                const children = alloyWheelMesh.getChildren();
                const root = children.find(c => c.name === "__root__");
                const rootChildren = root.getChildMeshes(false).filter(s => s.name.includes("surface"));
                console.log(rootChildren);

                rootChildren.forEach((c: BABYLON.Mesh) => {
                    const inst = c.createInstance(c.name + "instance" + z);
                    inst.isVisible = true;
                    inst.receiveShadows = true;
                    inst.parent = alloyWheelInstanceParent;
                    shadowGenerators.forEach(sg => {
                        sg.addShadowCaster(inst);
                    })
                })
                alloyWheelInstanceParent.scaling = new BABYLON.Vector3(wheelScale, wheelScale, wheelScale);
                alloyWheelInstanceParent.position = new BABYLON.Vector3(-50, 70 + y, z);
                new BABYLON.PhysicsAggregate(alloyWheelInstanceParent, BABYLON.PhysicsShapeType.CYLINDER, { pointA: new BABYLON.Vector3(0, 0, -4), pointB: new BABYLON.Vector3(0, 0, 0), friction: 1, radius: 7, mass: 20, restitution: 0.75 }, scene);

            }
        }, timeoutIncrease);
        timeoutIncrease += 5000;
    }

    new BABYLON.PhysicsAggregate(thichMesh, BABYLON.PhysicsShapeType.MESH, { mesh: thichMesh, mass: 0, restitution: 0.75, friction: 1 }, scene);
    new BABYLON.PhysicsAggregate(groundMesh, BABYLON.PhysicsShapeType.MESH, { mesh: groundCollisionMesh, mass: 0, restitution: 0.75 }, scene);


    const dirLightOptions = new Bit.Inputs.BabylonScene.DirectionalLightDto();
    dirLightOptions.direction = [100, -100, 100];
    dirLightOptions.intensity = 1;
    dirLightOptions.shadowGeneratorMapSize = 2056;
    dirLightOptions.shadowBias = 0.001;
    bitbybit.babylon.scene.drawDirectionalLight(dirLightOptions);


    const skyBox = new Bit.Inputs.BabylonScene.SkyboxDto();
    skyBox.skybox = Bit.Inputs.Base.skyboxEnum.city;
    skyBox.blur = 0.4;
    skyBox.environmentIntensity = 0.3;
    bitbybit.babylon.scene.enableSkybox(skyBox)

}

start();

async function createBox() {

    const box = await bitbybit.occt.shapes.solid.createBox({
        width: 250,
        length: 250,
        height: 70,
        center: [0, 13, 0],
    })

    const boxCutout = await bitbybit.occt.shapes.solid.createBox({
        width: 240,
        length: 240,
        height: 70,
        center: [0, 15, 0],
    })

    const diff = await bitbybit.occt.booleans.difference({
        shape: box,
        shapes: [boxCutout],
        keepEdges: false
    })

    const boxDrawOptions = new Bit.Inputs.Draw.DrawOcctShapeOptions();
    boxDrawOptions.precision = 0.1;
    boxDrawOptions.faceColour = "#000033";
    const boxMesh = await bitbybit.draw.drawAnyAsync({
        entity: diff,
        options: boxDrawOptions
    })

    return boxMesh

}