Creating simple examples in Babylon.js for regular practice

Hello,

Sometimes I forget to practice with some aspects of Babylon.js. Maybe this topic will motivate me to make regular practice with Babylon.js. If I make some logical mistakes in my examples or I use something in wrong way or you think it can be made better, please, write comments. Publish here you own simple examples.

Happy coding!

3 Likes

Skybox in Babylon.js and JavaScript

skybox-babylonjs-js

Playground
Files: skybox-babylonjs-js.zip (88.9 KB)

<!DOCTYPE html>
<html lang="en">
 
<head>
    <meta charset="UTF-8">
    <title>Skybox. BabylonJS, JavaScript</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/babylonjs/4.2.0/babylon.js"></script>
 
    <style>
        html,
        body {
            overflow: hidden;
            width: 100%;
            height: 100%;
            margin: 0;
            padding: 0;
        }
 
        #renderCanvas {
            width: 100%;
            height: 100%;
            touch-action: none;
        }
    </style>
</head>
 
<body>
    <canvas id="renderCanvas"></canvas>
 
    <script>
        const canvas = document.getElementById("renderCanvas");
        const engine = new BABYLON.Engine(canvas, true);
        const scene = new BABYLON.Scene(engine);
        const camera = new BABYLON.ArcRotateCamera("camera", 90 * Math.PI / 180, 70 * Math.PI / 180, 3,
            new BABYLON.Vector3(0, 0.5, 0), scene);
        camera.attachControl(canvas, true);
        camera.wheelPrecision = 100;
        const light = new BABYLON.HemisphericLight("hemisphericLight", new BABYLON.Vector3(0.1, 1, 0.2), scene);
 
        const ground = BABYLON.MeshBuilder.CreateGround("ground", { width: 2, height: 2 }, scene);
        const box = BABYLON.MeshBuilder.CreateBox("box", { width: 0.5, height: 0.5, depth: 0.5 }, scene);
        box.position.y = 0.25;
        const sphere = BABYLON.MeshBuilder.CreateSphere("sphere", { diameter: 0.5 }, scene);
        sphere.position.y = 0.75;
 
        const skybox = BABYLON.MeshBuilder.CreateBox("skybox", { size: 1000 }, this.scene);
        skybox.infiniteDistance = true;
        const skyboxMaterial = new BABYLON.StandardMaterial("skyboxMat", this.scene);
        skyboxMaterial.backFaceCulling = false;
        const files = [
            "images/skybox_px.jpg",
            "images/skybox_py.jpg",
            "images/skybox_pz.jpg",
            "images/skybox_nx.jpg",
            "images/skybox_ny.jpg",
            "images/skybox_nz.jpg",
        ];
        skyboxMaterial.reflectionTexture = BABYLON.CubeTexture.CreateFromImages(files, this.scene);
        skyboxMaterial.reflectionTexture.coordinatesMode = BABYLON.Texture.SKYBOX_MODE;
        skyboxMaterial.diffuseColor = new BABYLON.Color3(0, 0, 0);
        skyboxMaterial.specularColor = new BABYLON.Color3(0, 0, 0);
        skybox.material = skyboxMaterial;
 
        engine.runRenderLoop(() => { scene.render(); });
        window.onresize = () => engine.resize();
    </script>
</body>
 
</html>
2 Likes

Practicing is a great thing to do! We also have this topic Examples from the playground - Demos and projects - Babylon.js (babylonjs.com) where people share their examples.

3 Likes

Skybox in Babylon.js and TypeScript

Playground
Project Files: skybox-babylonjs-ts.zip (95.9 KB)

  • Install globally these packages: npm i -g typescript uglify-js browserify http-server
  • Install local packages: npm i

Debug build:

  • public/index.html
    <!-- Debug -->
    <script data-main="js/requireConfig"
        src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.6/require.min.js"></script>
    <!-- Release -->
    <!-- <script src="js/bundle.min.js"></script> -->
  • src/client/main.ts
// Debug
main();

// Release
// window.onload = () => main();
  • npm run debug
  • Run http-server and go to browser: http://localhost:8080/index.html

Release build:

    <!-- Debug -->
    <!-- <script data-main="js/requireConfig"
        src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.6/require.min.js"></script> -->
    <!-- Release -->
    <script src="js/bundle.min.js"></script>
  • src/client/main.ts
// Debug
// main();

// Release
window.onload = () => main();
  • npm run release
  • Run http-server and go to browser: http://localhost:8080/index.html

Source code:

package.json

{
  "name": "skybox-babylonjs-ts",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "debug": "tsc -p tsconfigs/tsconfig.debug.json",
    "compile": "tsc -p tsconfigs/tsconfig.release.json",
    "bundle": "browserify public/js/main.js -o public/js/bundle.js",
    "minify": "uglifyjs public/js/bundle.js -o public/js/bundle.min.js",
    "release": "npm run compile && npm run bundle && npm run minify"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "babylonjs": "^4.2.0",
    "requirejs": "^2.3.6"
  },
  "devDependencies": {
    "@types/requirejs": "^2.1.32"
  }
}

tsconfigs/tsconfig.debug.json

{
    "extends": "./tsconfig.json",
    "compilerOptions": {
        "module": "AMD",
        "sourceMap": true,
        "types": [
            "babylonjs",
            "requirejs"
        ],
        "lib": [
            "DOM",
            "ES2015"
        ]
    }
}

tsconfigs/tsconfig.json

{
    "compilerOptions": {
        "target": "ES5",
        "outDir": "../public/js"
    },
    "include": [
        "../src/client/**/*.ts"
    ]
}

tsconfigs/tsconfig.release.json

{
    "extends": "./tsconfig.json",
    "compilerOptions": {
        "module": "CommonJS",
        "sourceMap": false,
        "types": [
            "node"
        ]
    },
    "exclude": [
        "../src/client/requireConfig.ts"
    ]
}

public/index.html

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <title>Skybox. Babylon.js, TypeScript</title>

    <style>
        html,
        body {
            overflow: hidden;
            width: 100%;
            height: 100%;
            margin: 0;
            padding: 0;
        }

        #renderCanvas {
            touch-action: none;
            width: 100%;
            height: 100%;
        }
    </style>

    <!-- Debug -->
    <script data-main="js/requireConfig"
        src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.6/require.min.js"></script>
    <!-- Release -->
    <!-- <script src="js/bundle.min.js"></script> -->
</head>

<body>
    <canvas id="renderCanvas"></canvas>
</body>

</html>

src/client/main.ts

import Scene3D from "./Scene3D";

function main()
{
    new Scene3D("renderCanvas");
}

// Debug
main();

// Release
// window.onload = () => main();

src/client/requireConfig.ts

requirejs.config({
    baseUrl: "js",
    paths: {
        "babylonjs": "https://cdnjs.cloudflare.com/ajax/libs/babylonjs/4.2.0/babylon"
    }
});

requirejs(["main"], () => { });

src/client/Scene3D.ts

import * as BABYLON from "babylonjs";

export default class Scene3D
{
    private _engine: BABYLON.Engine;
    private _scene: BABYLON.Scene;

    public constructor(canvasName: string)
    {
        const canvas = document.getElementById(canvasName) as HTMLCanvasElement;
        this._engine = new BABYLON.Engine(canvas, true);
        this._scene = this.createScene(canvas);

        window.onresize = () => this._engine.resize();
        this.doRender();
    }

    private createScene(canvas: HTMLCanvasElement): BABYLON.Scene
    {
        const scene = new BABYLON.Scene(this._engine);

        const camera = new BABYLON.ArcRotateCamera("arcCamera",
            BABYLON.Tools.ToRadians(90), BABYLON.Tools.ToRadians(70),
            3, new BABYLON.Vector3(0, 0.5, 0), scene);
        camera.attachControl(canvas, true);
        camera.wheelPrecision = 100;

        const light = new BABYLON.HemisphericLight("hemisphericLight", new BABYLON.Vector3(0.1, 1, 0.2), scene);

        const ground = BABYLON.MeshBuilder.CreateGround("ground", { width: 2, height: 2 }, scene);
        const box = BABYLON.MeshBuilder.CreateBox("box", { width: 0.5, height: 0.5, depth: 0.5 }, scene);
        box.position.y = 0.25;
        const sphere = BABYLON.MeshBuilder.CreateSphere("sphere", { diameter: 0.5 }, scene);
        sphere.position.y = 0.75;

        const skybox = BABYLON.MeshBuilder.CreateBox("skybox", { size: 1000 }, scene);
        skybox.infiniteDistance = true;
        const skyboxMaterial = new BABYLON.StandardMaterial("skyboxMat", scene);
        skyboxMaterial.backFaceCulling = false;
        const files = [
            "images/skybox_px.jpg",
            "images/skybox_py.jpg",
            "images/skybox_pz.jpg",
            "images/skybox_nx.jpg",
            "images/skybox_ny.jpg",
            "images/skybox_nz.jpg",
        ];
        skyboxMaterial.reflectionTexture = BABYLON.CubeTexture.CreateFromImages(files, scene);
        skyboxMaterial.reflectionTexture.coordinatesMode = BABYLON.Texture.SKYBOX_MODE;
        skyboxMaterial.diffuseColor = new BABYLON.Color3(0, 0, 0);
        skyboxMaterial.specularColor = new BABYLON.Color3(0, 0, 0);
        skybox.material = skyboxMaterial;

        return scene;
    }

    private doRender(): void
    {
        this._engine.runRenderLoop(() => this._scene.render());
    }
}
1 Like

Skybox. Babylon, Browserify, JavaScript

Playground
Source: skybox-babylonjs-browserify-js.zip (91.5 KB)

  • Install globally these packages: npm i -g uglify-js browserify http-server
  • Install local packages: npm i

Debug build:

  • public/index.html
    <!-- Debug -->
    <script src="js/bundle.js"></script>
    <!-- Release -->
    <!-- <script src="js/bundle.min.js"></script> -->
  • npm run debug
  • Run http-server and go to browser: http://localhost:8080/index.html

Release build:

  • public/index.html
    <!-- Debug -->
    <!-- <script src="js/bundle.js"></script> -->
    <!-- Release -->
    <script src="js/bundle.min.js"></script>
  • npm run release
  • Run http-server and go to browser: http://localhost:8080/index.html

Source code:

package.json

{
  "name": "skybox-babylonjs-browserify-js",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "clear": "del /f /q /s .\\public\\js\\*.*",
    "del-bundle": "del /f /q /s .\\src\\client\\bundle.js",
    "bundle-debug": "browserify --debug src/client/main.js -o public/js/bundle.js",
    "bundle-release": "browserify src/client/main.js -o src/client/bundle.js",
    "uglify": "uglifyjs src/client/bundle.js -o public/js/bundle.min.js",
    "debug": "npm run bundle-debug",
    "release": "npm run clear && npm run bundle-release && npm run uglify && npm run del-bundle"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "babylonjs": "^4.2.0"
  }
}

.gitignore

node_modules/
public/js/

public/index.html

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <title>Skybox. Babylon.js, Browserify, JavaScript</title>

    <style>
        html,
        body {
            overflow: hidden;
            width: 100%;
            height: 100%;
            margin: 0;
            padding: 0;
        }

        #renderCanvas {
            width: 100%;
            height: 100%;
            touch-action: none;
        }
    </style>

    <!-- Debug -->
    <script src="js/bundle.js"></script>
    <!-- Release -->
    <!-- <script src="js/bundle.min.js"></script> -->
</head>

<body>
    <canvas id="renderCanvas"></canvas>
</body>

</html>

src/client/main.js

const Scene3D = require("./Scene3D");

function main()
{
    new Scene3D("renderCanvas");
}

window.onload = () => main();

src/client/Scene3D.js

const BABYLON = require("babylonjs");

class Scene3D
{
    constructor(canvasName)
    {
        const canvas = document.getElementById(canvasName);
        this._engine = new BABYLON.Engine(canvas, true);
        this._scene = this.createScene(canvas);

        window.onresize = () => this._engine.resize();
        this.doRender();
    }

    createScene(canvas)
    {
        const scene = new BABYLON.Scene(this._engine);

        const camera = new BABYLON.ArcRotateCamera("arcCamera",
            BABYLON.Tools.ToRadians(90), BABYLON.Tools.ToRadians(70),
            3, new BABYLON.Vector3(0, 0.5, 0), scene);
        camera.attachControl(canvas, true);
        camera.wheelPrecision = 100;

        const light = new BABYLON.HemisphericLight("hemisphericLight", new BABYLON.Vector3(0.1, 1, 0.2), scene);

        const ground = BABYLON.MeshBuilder.CreateGround("ground", { width: 2, height: 2 }, scene);
        const box = BABYLON.MeshBuilder.CreateBox("box", { width: 0.5, height: 0.5, depth: 0.5 }, scene);
        box.position.y = 0.25;
        const sphere = BABYLON.MeshBuilder.CreateSphere("sphere", { diameter: 0.5 }, scene);
        sphere.position.y = 0.75;

        const skybox = BABYLON.MeshBuilder.CreateBox("skybox", { size: 1000 }, scene);
        skybox.infiniteDistance = true;
        const skyboxMaterial = new BABYLON.StandardMaterial("skyboxMat", scene);
        skyboxMaterial.backFaceCulling = false;
        const files = [
            "images/skybox_px.jpg",
            "images/skybox_py.jpg",
            "images/skybox_pz.jpg",
            "images/skybox_nx.jpg",
            "images/skybox_ny.jpg",
            "images/skybox_nz.jpg",
        ];
        skyboxMaterial.reflectionTexture = BABYLON.CubeTexture.CreateFromImages(files, scene);
        skyboxMaterial.reflectionTexture.coordinatesMode = BABYLON.Texture.SKYBOX_MODE;
        skyboxMaterial.diffuseColor = new BABYLON.Color3(0, 0, 0);
        skyboxMaterial.specularColor = new BABYLON.Color3(0, 0, 0);
        skybox.material = skyboxMaterial;

        return scene;
    }

    doRender()
    {
        this._engine.runRenderLoop(() => this._scene.render());
    }
}

module.exports = Scene3D;

Falling Boxes. MatterJS, BabylonJS, JavaScript

Boxes falls on static objects.

falling-boxes-matterjs-bjs-ts

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">

    <title>Falling Boxes. MatterJS, BabylonJS, JavaScript</title>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/babylonjs/4.2.0/babylon.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/matter-js@0.14.2/build/matter.min.js"></script>

    <style>
        html,
        body {
            overflow: hidden;
            width: 100%;
            height: 100%;
            margin: 0;
            padding: 0;
        }

        #renderCanvas {
            width: 100%;
            height: 100%;
            touch-action: none;
        }
    </style>
</head>

<body>
    <canvas id="renderCanvas"></canvas>

    <script>
        let boxBody1, boxBody2;
        let matterEngine;

        const canvas = document.getElementById("renderCanvas");
        const engine = new BABYLON.Engine(canvas, true);
        const scene = new BABYLON.Scene(engine);
        const camera = new BABYLON.ArcRotateCamera("arcCamera",
            BABYLON.Tools.ToRadians(-90), BABYLON.Tools.ToRadians(90),
            15, new BABYLON.Vector3(0, 3, 0), scene);
        camera.attachControl(canvas, true);
        camera.wheelPrecision = 100;
        const light = new BABYLON.HemisphericLight("hemisphericLight", new BABYLON.Vector3(0.1, 1, -0.3), scene);

        const ground = BABYLON.MeshBuilder.CreateGround("ground", { width: 10, height: 10 }, scene);

        const box1 = BABYLON.MeshBuilder.CreateBox("box1", { width: 1, height: 1, depth: 1 }, scene);
        box1.position.x = 2;
        box1.position.y = 4;

        const box2 = BABYLON.MeshBuilder.CreateBox("box2", { width: 1, height: 1, depth: 1 }, scene);
        box2.position.x = -1.5;
        box2.position.y = 4;

        const staticSphere = BABYLON.MeshBuilder.CreateSphere("staticSphere", { diameter: 1 }, scene);
        staticSphere.position.x = 2;
        staticSphere.position.y = 0.5;

        const staticBox = BABYLON.MeshBuilder.CreateBox("staticBox", { width: 1, height: 1, depth: 1 }, scene);
        staticBox.position.x = -2;
        staticBox.position.y = 0.5;

        const skybox = BABYLON.MeshBuilder.CreateBox("skybox", { size: 1000 }, scene);
        skybox.infiniteDistance = true;
        const skyboxMaterial = new BABYLON.StandardMaterial("skyboxMat", scene);
        skyboxMaterial.backFaceCulling = false;
        const files = [
            "images/skybox_px.jpg",
            "images/skybox_py.jpg",
            "images/skybox_pz.jpg",
            "images/skybox_nx.jpg",
            "images/skybox_ny.jpg",
            "images/skybox_nz.jpg",
        ];
        skyboxMaterial.reflectionTexture = BABYLON.CubeTexture.CreateFromImages(files, scene);
        skyboxMaterial.reflectionTexture.coordinatesMode = BABYLON.Texture.SKYBOX_MODE;
        skyboxMaterial.diffuseColor = new BABYLON.Color3(0, 0, 0);
        skyboxMaterial.specularColor = new BABYLON.Color3(0, 0, 0);
        skybox.material = skyboxMaterial;

        engine.runRenderLoop(() => { scene.render(); });

        matterEngine = Matter.Engine.create();
        matterEngine.world.gravity.y = -1;
        this.createWallsAndFloor();
        this.createPhysicsSimulation();

        window.onresize = () => engine.resize();

        function createWallsAndFloor()
        {
            boxBody1 = Matter.Bodies.rectangle(200, 400, 100, 100);
            boxBody2 = Matter.Bodies.rectangle(-150, 400, 100, 100);
            const staticSphereBody = Matter.Bodies.circle(200, 50, 50, { isStatic: true });
            const staticBoxBody = Matter.Bodies.rectangle(-200, 50, 100, 100, { isStatic: true });
            const ground = Matter.Bodies.rectangle(0, -50, 500, 100, { isStatic: true });
            Matter.World.add(matterEngine.world, [ground, boxBody1, boxBody2,
                staticSphereBody, staticBoxBody]);
        }

        function createPhysicsSimulation()
        {
            setInterval(
                () =>
                {
                    this.updatePhysics();
                }, 15);
        }

        function updatePhysics()
        {
            Matter.Engine.update(matterEngine);

            box1.position.x = boxBody1.position.x / 100;
            box1.position.y = boxBody1.position.y / 100;
            box1.rotation.z = boxBody1.angle;

            box2.position.x = boxBody2.position.x / 100;
            box2.position.y = boxBody2.position.y / 100;
            box2.rotation.z = boxBody2.angle;
        }
    </script>
</body>

</html>
1 Like

Example of class to combine Babylon.js and Matter.js in TypeScript

Box.ts

import * as BABYLON from "babylonjs";
import * as Matter from "matter-js";

export default class Box
{
    private _box: BABYLON.Mesh;
    private _body: Matter.Body;
    private RATIO = 100;

    public constructor(x: number, y: number, width: number, height: number,
        depth: number, isStatic: boolean, matterEngine: Matter.Engine, scene: BABYLON.Scene)
    {
        this._box = BABYLON.MeshBuilder.CreateBox("box",
            { width: width, height: height, depth: depth }, scene);
        this._box.position.x = x;
        this._box.position.y = y;

        this._body = Matter.Bodies.rectangle(x * this.RATIO, y * this.RATIO,
            width * this.RATIO, height * this.RATIO, { isStatic: isStatic });
        Matter.World.add(matterEngine.world, this._body);
    }

    public update(): void
    {
        this._box.position.x = this._body.position.x / this.RATIO;
        this._box.position.y = this._body.position.y / this.RATIO;
        this._box.rotation.z = this._body.angle;
    }
}
4 Likes

Falling Boxes. Babylon.js, Matter.js, TypeScript

falling-boxes-matterjs-babylonjs-ts

  • Install globally these packages: npm i -g typescript uglify-js browserify http-server
  • Go to the project folder
  • Install local packages: npm i

Debug build:

  • public/index.html
    <!-- Debug -->
    <script data-main="js/requireConfig"
        src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.6/require.min.js"></script>
    <!-- Release -->
    <!-- <script src="js/bundle.min.js"></script> -->
  • src/client/main.ts
// Debug
main();

// Release
// window.onload = () => main();
  • npm run debug
  • Run http-server and go to browser: http://localhost:8080/index.html

Release build:

    <!-- Debug -->
    <!-- <script data-main="js/requireConfig"
        src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.6/require.min.js"></script> -->
    <!-- Release -->
    <script src="js/bundle.min.js"></script>
  • src/client/main.ts
// Debug
// main();

// Release
window.onload = () => main();
  • npm run release
  • Run http-server and go to browser: http://localhost:8080/index.html

Source code:

package.json

{
  "name": "falling-boxes-matterjs-babylonjs-ts",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "debug": "tsc -p tsconfigs/tsconfig.debug.json",
    "compile": "tsc -p tsconfigs/tsconfig.release.json",
    "bundle": "browserify public/js/main.js -o public/js/bundle.js",
    "minify": "uglifyjs public/js/bundle.js -o public/js/bundle.min.js",
    "release": "npm run compile && npm run bundle && npm run minify"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "babylonjs": "^4.2.0",
    "matter-js": "^0.16.1",
    "requirejs": "^2.3.6"
  },
  "devDependencies": {
    "@types/matter-js": "^0.14.10",
    "@types/requirejs": "^2.1.32"
  }
}

tsconfigs/tsconfig.debug.json

{
    "extends": "./tsconfig.json",
    "compilerOptions": {
        "module": "AMD",
        "sourceMap": true,
        "types": [
            "babylonjs",
            "matter-js",
            "requirejs"
        ],
        "lib": [
            "DOM",
            "ES2015"
        ]
    }
}

tsconfigs/tsconfig.json

{
    "compilerOptions": {
        "target": "ES5",
        "outDir": "../public/js"
    },
    "include": [
        "../src/client/**/*.ts"
    ]
}

tsconfigs/tsconfig.release.json

{
    "extends": "./tsconfig.json",
    "compilerOptions": {
        "module": "CommonJS",
        "sourceMap": false,
        "types": [
            "node"
        ]
    },
    "exclude": [
        "../src/client/requireConfig.ts"
    ]
}

public/index.html

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <title>Falling Boxes. Babylon.js, Matter.js, TypeScript</title>

    <style>
        html,
        body {
            overflow: hidden;
            width: 100%;
            height: 100%;
            margin: 0;
            padding: 0;
        }

        #renderCanvas {
            touch-action: none;
            width: 100%;
            height: 100%;
        }
    </style>

    <!-- Debug -->
    <script data-main="js/requireConfig"
        src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.6/require.min.js"></script>
    <!-- Release -->
    <!-- <script src="js/bundle.min.js"></script> -->
</head>

<body>
    <canvas id="renderCanvas"></canvas>
</body>

</html>

src/client/Box.ts

import * as BABYLON from "babylonjs";
import * as Matter from "matter-js";

export default class Box
{
    protected _box: BABYLON.Mesh;
    private _body: Matter.Body;
    private _ratio: number;

    public constructor(x: number, y: number, width: number, height: number,
        depth: number, isStatic: boolean, ratio: number, matterEngine: Matter.Engine, scene: BABYLON.Scene)
    {
        this._box = BABYLON.MeshBuilder.CreateBox("box",
            { width: width, height: height, depth: depth }, scene);
        this._box.position.x = x;
        this._box.position.y = y;

        this._ratio = ratio;

        this._body = Matter.Bodies.rectangle(x * this._ratio, y * this._ratio,
            width * this._ratio, height * this._ratio, { isStatic: isStatic });
        Matter.World.add(matterEngine.world, this._body);
    }

    public update(): void
    {
        this._box.position.x = this._body.position.x / this._ratio;
        this._box.position.y = this._body.position.y / this._ratio;
        this._box.rotation.z = this._body.angle;
    }
}

src/client/main.ts

import Scene3D from "./Scene3D";

function main()
{
    new Scene3D("renderCanvas");
}

// Debug
main();

// Release
// window.onload = () => main();

src/client/requireConfig.ts

requirejs.config({
    baseUrl: "js",
    paths: {
        "babylonjs": "https://cdnjs.cloudflare.com/ajax/libs/babylonjs/4.2.0/babylon",
        "matter-js": "https://cdn.jsdelivr.net/npm/matter-js@0.14.2/build/matter.min"
    }
});

requirejs(["main"], () => { });

src/client/Scene3D.ts

import * as BABYLON from "babylonjs";
import * as Matter from "matter-js";
import Box from "./Box";
import Sphere from "./Sphere";

export default class Scene3D
{
    private _engine: BABYLON.Engine;
    private _scene: BABYLON.Scene;
    private _matterEngine: Matter.Engine;
    private _box1: Box;
    private _box2: Box;
    private RATIO = 100;

    public constructor(canvasName: string)
    {
        this._matterEngine = Matter.Engine.create();
        this._matterEngine.world.gravity.y = -1;

        const canvas = document.getElementById(canvasName) as HTMLCanvasElement;
        this._engine = new BABYLON.Engine(canvas, true);
        this._scene = this.createScene(canvas);

        window.onresize = () => this._engine.resize();
        this.createPhysicsSimulation();
        this.doRender();
    }

    private createScene(canvas: HTMLCanvasElement): BABYLON.Scene
    {
        const scene = new BABYLON.Scene(this._engine);

        const camera = new BABYLON.ArcRotateCamera("arcCamera",
            BABYLON.Tools.ToRadians(-90), BABYLON.Tools.ToRadians(90),
            15, new BABYLON.Vector3(0, 3, 0), scene);
        camera.attachControl(canvas, true);
        camera.wheelPrecision = 100;

        const light = new BABYLON.HemisphericLight("hemisphericLight", new BABYLON.Vector3(0.1, 1, -0.3), scene);

        this._box1 = new Box(2, 4, 1, 1, 1, false, this.RATIO, this._matterEngine, scene);
        this._box2 = new Box(-1.5, 4, 1, 1, 1, false, this.RATIO, this._matterEngine, scene);

        const staticSphere = new Sphere(2, 0.5, 1, true, this.RATIO, this._matterEngine, scene);
        const staticBox = new Box(-2, 0.5, 1, 1, 1, true, this.RATIO, this._matterEngine, scene);
        const ground = new Box(0, -0.5, 10, 1, 2, true, this.RATIO, this._matterEngine, scene);

        const skybox = BABYLON.MeshBuilder.CreateBox("skybox", { size: 1000 }, scene);
        skybox.infiniteDistance = true;
        const skyboxMaterial = new BABYLON.StandardMaterial("skyboxMat", scene);
        skyboxMaterial.backFaceCulling = false;
        const files = [
            "images/skybox_px.jpg",
            "images/skybox_py.jpg",
            "images/skybox_pz.jpg",
            "images/skybox_nx.jpg",
            "images/skybox_ny.jpg",
            "images/skybox_nz.jpg",
        ];
        skyboxMaterial.reflectionTexture = BABYLON.CubeTexture.CreateFromImages(files, scene);
        skyboxMaterial.reflectionTexture.coordinatesMode = BABYLON.Texture.SKYBOX_MODE;
        skyboxMaterial.diffuseColor = new BABYLON.Color3(0, 0, 0);
        skyboxMaterial.specularColor = new BABYLON.Color3(0, 0, 0);
        skybox.material = skyboxMaterial;

        return scene;
    }

    private doRender(): void
    {
        this._engine.runRenderLoop(() => this._scene.render());
    }

    private createPhysicsSimulation(): void
    {
        setInterval(
            () =>
            {
                this.updatePhysics();
            }, 15);
    }

    private updatePhysics(): void
    {
        Matter.Engine.update(this._matterEngine);
        this._box1.update();
        this._box2.update();
    }
}

src/client/Sphere.ts

import * as BABYLON from "babylonjs";
import * as Matter from "matter-js";

export default class Sphere
{
    protected _sphere: BABYLON.Mesh;
    private _body: Matter.Body;
    private _ratio: number;

    public constructor(x: number, y: number, diameter: number,
        isStatic: boolean, ratio: number, matterEngine: Matter.Engine, scene: BABYLON.Scene)
    {
        this._sphere = BABYLON.MeshBuilder.CreateSphere("sphere",
            { diameter: diameter }, scene);
        this._sphere.position.x = x;
        this._sphere.position.y = y;

        this._ratio = ratio;

        this._body = Matter.Bodies.circle(x * this._ratio, y * this._ratio,
            diameter / 2 * this._ratio, { isStatic: isStatic });
        Matter.World.add(matterEngine.world, this._body);
    }

    public update(): void
    {
        this._sphere.position.x = this._body.position.x / this._ratio;
        this._sphere.position.y = this._body.position.y / this._ratio;
        this._sphere.rotation.z = this._body.angle;
    }
}

@8Observer8 : I still have a directory on one of my hard drives named after you - files going back to 2016 :smiley:

Just a suggestion, why not put this thread in the “Tutorials and Tips” category? Not as many topics posted as Demos category and where people might look for the kind of stuff you are posting.

Stay Safe, gryff :slight_smile:

2 Likes

Okay, thank you!

1 Like

@8Observer8 : NP :smiley: I see you moved categories!

Stay Safe, gryff :slight_smile:

1 Like

It would be nice to put all this to Github repository so people who follow would have much more convenient way to do it :slight_smile:

3 Likes

I am sure that it is helpful to practice with pure WebGL too. This gives an insight into how certain things work under the hood. If you want I can create a new topic in the “Off topic” section. I want to spend some time to practice with pure WebGL. I study this book: WebGL Programming Guide: Interactive 3D Graphics Programming with WebGL (OpenGL) I am going to write some simple games in WebGL. Could I publish WebGL examples in this topic?

Falling boxes. Matter.js, WebGL, TypeScript

falling-boxes-2d-matterjs-webgl-ts

  • Install globally these packages: npm i -g typescript uglify-js browserify http-server
  • Go to the project folder
  • Install local packages: npm i

Debug build:

  • public/index.html
    <!-- Debug -->
    <script data-main="js/requireConfig"
        src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.6/require.min.js"></script>
    <!-- Release -->
    <!-- <script src="js/bundle.min.js"></script> -->
  • src/client/main.ts
// Debug
main();

// Release
// window.onload = () => main();
  • npm run debug
  • Run http-server and go to browser: http://localhost:8080/index.html

Release build:

    <!-- Debug -->
    <!-- <script data-main="js/requireConfig"
        src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.6/require.min.js"></script> -->
    <!-- Release -->
    <script src="js/bundle.min.js"></script>
  • src/client/main.ts
// Debug
// main();

// Release
window.onload = () => main();
  • npm run release
  • Run http-server and go to browser: http://localhost:8080/index.html

Source code:

src/client/Game.ts

import { mat4 } from "gl-matrix";
import * as Matter from "matter-js";
import Box from "./Box";
import { gl, WebGLContext } from "./WebGLContext";

export default class Game
{
    private _matterEngine: Matter.Engine;
    private _bigBox: Box;
    private _smallBox: Box;
    private _ground: Box;
    private _projViewMatrix: mat4;

    public constructor()
    {
        if (!WebGLContext.Init("renderCanvas")) return;

        var vertexShaderSource =
            `attribute vec2 aPosition;
            uniform mat4 uMvpMatrix;
            
            void main()
            {
                gl_Position = uMvpMatrix * vec4(aPosition, 0.0, 1.0);
            }`;

        var fragmentShaderSource =
            `precision mediump float;
        
            uniform vec3 uColor;

            void main()
            {
                gl_FragColor = vec4(uColor, 1.0);
            }`;

        var vShader = gl.createShader(gl.VERTEX_SHADER);
        gl.shaderSource(vShader, vertexShaderSource);
        gl.compileShader(vShader);

        var fShader = gl.createShader(gl.FRAGMENT_SHADER);
        gl.shaderSource(fShader, fragmentShaderSource);
        gl.compileShader(fShader);

        var program = gl.createProgram();
        gl.attachShader(program, vShader);
        gl.attachShader(program, fShader);
        gl.linkProgram(program);
        gl.useProgram(program);

        gl.clearColor(0.8, 0.937, 0.937, 1.0);

        const projMatrix = mat4.create();
        const viewMatrix = mat4.create();
        this._projViewMatrix = mat4.create();

        mat4.ortho(projMatrix, 0, 500, 500, 0, 100, -100);
        mat4.lookAt(viewMatrix, [0, 0, 50], [0, 0, 0], [0, 1, 0]);

        this._matterEngine = Matter.Engine.create();

        this._smallBox = new Box(225, 100, 50, 50, 0, [0.447, 0.631, 0.149], program, false, this._matterEngine);
        this._bigBox = new Box(250, 200, 50, 100, 0, [0.631, 0.368, 0.149], program, false, this._matterEngine);
        this._ground = new Box(250, 475, 500, 50, 0, [0.149, 0.631, 0.352], program, true, this._matterEngine);
        mat4.mul(this._projViewMatrix, projMatrix, viewMatrix);

        this.createPhysicsSimulation();
        this.draw();
    }

    private draw(): void
    {
        gl.clear(gl.COLOR_BUFFER_BIT);
        this._smallBox.draw(this._projViewMatrix);
        this._bigBox.draw(this._projViewMatrix);
        this._ground.draw(this._projViewMatrix);
        requestAnimationFrame(() => this.draw());
    }

    private createPhysicsSimulation(): void
    {
        setInterval(
            () =>
            {
                this.updatePhysics();
            }, 15);
    }

    private updatePhysics(): void
    {
        Matter.Engine.update(this._matterEngine);
        this._smallBox.update();
        this._bigBox.update();
    }
}

src/client/Box.ts

import { mat4 } from "gl-matrix";
import { gl } from "./WebGLContext";
import * as Matter from "matter-js";

export default class Box
{
    private _program: WebGLProgram;
    private _x: number;
    private _y: number;
    private _w: number;
    private _h: number;
    private _color: number[];
    private _body: Matter.Body;
    private _aPositionLocation: number;
    private _vbo: WebGLBuffer;
    private _uColorLocation: WebGLUniformLocation;
    private _uMvpMatrixLocation: WebGLUniformLocation;
    private _modelMatrix: mat4;
    private _mvpMatrix: mat4;
    private _radians: number;

    public constructor(x: number, y: number, w: number, h: number, radians: number, color: number[],
        program: WebGLProgram, isStatic: boolean, matterEngine: Matter.Engine)
    {
        this._x = x;
        this._y = y;
        this._w = w;
        this._h = h;
        this._radians = radians;
        this._color = color;
        this._program = program;        

        const vertices = new Float32Array([
            -0.5, 0.5,
            0.5, 0.5,
            -0.5, -0.5,
            0.5, -0.5
        ]);

        this._vbo = gl.createBuffer();
        gl.bindBuffer(gl.ARRAY_BUFFER, this._vbo);
        gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);

        this._modelMatrix = mat4.create();
        this._mvpMatrix = mat4.create();

        this._aPositionLocation = gl.getAttribLocation(program, "aPosition");
        this._uColorLocation = gl.getUniformLocation(program, "uColor");
        this._uMvpMatrixLocation = gl.getUniformLocation(this._program, "uMvpMatrix");

        this._body = Matter.Bodies.rectangle(x, y, w, h, { isStatic: isStatic });
        Matter.World.add(matterEngine.world, this._body);
    }

    public draw(projViewMatrix: mat4): void
    {
        gl.bindBuffer(gl.ARRAY_BUFFER, this._vbo);
        gl.vertexAttribPointer(this._aPositionLocation, 2, gl.FLOAT, false, 0, 0);
        gl.enableVertexAttribArray(this._aPositionLocation);

        mat4.fromTranslation(this._modelMatrix, [this._x, this._y, 0]);
        mat4.rotateZ(this._modelMatrix, this._modelMatrix, this._radians);
        mat4.scale(this._modelMatrix, this._modelMatrix, [this._w, this._h, 1]);
        mat4.mul(this._mvpMatrix, projViewMatrix, this._modelMatrix);

        gl.uniformMatrix4fv(this._uMvpMatrixLocation, false, this._mvpMatrix);
        gl.uniform3fv(this._uColorLocation, this._color);
        gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
    }

    public update(): void
    {
        this._x = this._body.position.x;
        this._y = this._body.position.y;
        this._radians = this._body.angle;
    }
}

src/client/main.ts

import Game from "./Game";

function main()
{
    new Game();
}

// Debug
main();

// Release
// window.onload = () => main();

src/client/requireConfig.ts

requirejs.config({
    baseUrl: "js",
    paths: {
        "gl-matrix": "https://cdn.jsdelivr.net/npm/gl-matrix@3.3.0/gl-matrix-min",
        "matter-js": "https://cdn.jsdelivr.net/npm/matter-js@0.14.2/build/matter.min"
    }
});

requirejs(["main"], () => { });

src/client/WebGLContext.ts

export let gl: WebGLRenderingContext;

export class WebGLContext
{
    public static Init(canvasName: string): boolean
    {
        const canvas = document.getElementById(canvasName) as HTMLCanvasElement;
        if (!canvas)
        {
            console.log("Failed to get the HTML5 canvas element");
            return false;
        }

        gl = canvas.getContext("webgl");
        if (!gl)
        {
            console.log("Failed to get WebGL 1.0 rendering context");
            return false;
        }

        return true;
    }
}

public/index.html

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <title>Falling Boxes 2D. Matter.js, WebGL, TypeScript</title>

    <!-- Debug -->
    <script data-main="js/requireConfig"
        src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.6/require.min.js"></script>
    <!-- Release -->
    <!-- <script src="js/bundle.min.js"></script> -->
</head>

<body>
    <canvas id="renderCanvas" width="500" height="500"></canvas>
</body>

</html>

package.json

{
  "name": "angry-birds-2d-matterjs-webgl-ts",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "debug": "tsc -p tsconfigs/tsconfig.debug.json",
    "compile": "tsc -p tsconfigs/tsconfig.release.json",
    "bundle": "browserify public/js/main.js -o public/js/bundle.js",
    "minify": "uglifyjs public/js/bundle.js -o public/js/bundle.min.js",
    "release": "npm run compile && npm run bundle && npm run minify"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "gl-matrix": "^3.3.0",
    "matter-js": "^0.14.2",
    "requirejs": "^2.3.6"
  },
  "devDependencies": {
    "@types/matter-js": "^0.14.2",
    "@types/requirejs": "^2.1.32"
  }
}

tsconfigs/tsconfig.debug.json

{
    "extends": "./tsconfig.json",
    "compilerOptions": {
        "module": "AMD",
        "sourceMap": true,
        "types": [
            "gl-matrix",
            "matter-js",
            "requirejs"
        ],
        "moduleResolution": "node"
    }
}

tsconfigs/tsconfig.json

{
    "compilerOptions": {
        "target": "ES5",
        "outDir": "../public/js"
    },
    "include": [
        "../src/client/**/*.ts"
    ]
}

tsconfigs/tsconfig.release.json

{
    "extends": "./tsconfig.json",
    "compilerOptions": {
        "module": "CommonJS",
        "sourceMap": false,
        "types": [
            "node"
        ]
    },
    "exclude": [
        "../src/client/requireConfig.ts"
    ]
}

Box of Matches created in Blender

I found a texture in the Internet.

Run demo in sandbox

load-matches-babylonjs-js

Demo sources:

index.html

<html lang="en">
 
<head>
    <meta charset="UTF-8">
    <title>Skybox. BabylonJS, JavaScript</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/babylonjs/4.2.0/babylon.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/babylonjs-loaders@4.2.0/babylonjs.loaders.min.js"></script>
 
    <style>
        html,
        body {
            overflow: hidden;
            width: 100%;
            height: 100%;
            margin: 0;
            padding: 0;
        }
 
        #renderCanvas {
            width: 100%;
            height: 100%;
            touch-action: none;
        }
    </style>
</head>
 
<body>
    <canvas id="renderCanvas"></canvas>
 
    <script>
        const canvas = document.getElementById("renderCanvas");
        const engine = new BABYLON.Engine(canvas, true);
        const scene = new BABYLON.Scene(engine);
        const camera = new BABYLON.ArcRotateCamera("camera", 90 * Math.PI / 180, 60 * Math.PI / 180, 10,
            new BABYLON.Vector3(0, 0.5, 0), scene);
        camera.attachControl(canvas, true);
        camera.wheelPrecision = 100;
        const light = new BABYLON.HemisphericLight("hemisphericLight", new BABYLON.Vector3(0.1, 1, 0.2), scene);
 
        BABYLON.SceneLoader.Append("models/", "MatchesBox.glb", this.scene,
            (scene) => { });
 
        const skybox = BABYLON.MeshBuilder.CreateBox("skybox", { size: 1000 }, this.scene);
        skybox.infiniteDistance = true;
        const skyboxMaterial = new BABYLON.StandardMaterial("skyboxMat", this.scene);
        skyboxMaterial.backFaceCulling = false;
        const files = [
            "https://www.cyberforum.ru/images/skybox_px.jpg",
            "https://www.cyberforum.ru/images/skybox_py.jpg",
            "https://www.cyberforum.ru/images/skybox_pz.jpg",
            "https://www.cyberforum.ru/images/skybox_nx.jpg",
            "https://www.cyberforum.ru/images/skybox_ny.jpg",
            "https://www.cyberforum.ru/images/skybox_nz.jpg",
        ];
        skyboxMaterial.reflectionTexture = BABYLON.CubeTexture.CreateFromImages(files, this.scene);
        skyboxMaterial.reflectionTexture.coordinatesMode = BABYLON.Texture.SKYBOX_MODE;
        skyboxMaterial.diffuseColor = new BABYLON.Color3(0, 0, 0);
        skyboxMaterial.specularColor = new BABYLON.Color3(0, 0, 0);
        skybox.material = skyboxMaterial;
 
        engine.runRenderLoop(() => { scene.render(); });
        window.onresize = () => engine.resize();
    </script>
</body>
 
</html>

I made a demo with simple skeletal animation imported from Blender. The demo is written in TypeScript using pure WebGL 1.0. I import 3D models and animations from .dae (COLLADA) format from Blender 3D editor. No code yet, just a demo.

Run demo in browser

anim-with-correction

  • Made an object selection with a mouse click using the color ID in the shader
  • Connected the physics engine Ammo.js (Ammo.js is a port of the physical C++ - Bullet engine)
  • Made the camera rotate while holding the mouse wheel
  • Made the camera zoom in and out by rotating the mouse wheel
  • Made Skybox (sky environment)
  • Display text that is not pixilated when zooming in. To do this, I use Distance Field from the Hiero program, as shown in the tutorial from ThinMatrix:

I used this tutorial for skeleton animation:

1 Like