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!

4 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.

4 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

Loading a cube from gltf 2.0. WebGL, JavaScript

image

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>Loading a cube from gltf 2.0. WebGL, JavaScript</title>
    <script src="https://cdn.jsdelivr.net/npm/gl-matrix@3.4.3/gl-matrix-min.js"></script>
</head>

<body>
    <canvas id="renderCanvas" width="400" height="400"></canvas>
    <script>
        loadFile("assets/BoxBlender3.gltf", (content) =>
        {
            const gltf = JSON.parse(content);

            loadBin("assets/BoxBlender3.bin", (binData) =>
            {
                const canvas = document.getElementById("renderCanvas");
                const gl = canvas.getContext("webgl");

                gl.enable(gl.DEPTH_TEST);

                const vertShaderSource =
                    `attribute vec4 aPosition;
                    attribute vec4 aNormal;
                    uniform mat4 uMvpMatrix;
                    uniform mat4 uModelMatrix;
                    uniform mat4 uNormalMatrix;
                    varying vec3 vPosition;
                    varying vec3 vNormal;
                    void main()
                    {
                        gl_Position = uMvpMatrix * aPosition;
                        vPosition = vec3(uModelMatrix * aPosition);
                        vNormal = normalize(vec3(uNormalMatrix * aNormal));
                    }`;

                const fragShaderSource =
                    `precision mediump float;
                    const vec3 lightColor = vec3(1.0, 1.0, 1.0);
                    const vec3 ambientLight = vec3(0.2, 0.2, 0.2);
                    uniform vec3 uLightPosition;
                    varying vec3 vPosition;
                    varying vec3 vNormal;
                    void main()
                    {
                        vec4 color = vec4(0.5, 1.0, 0.5, 1.0);
                        vec3 normal = normalize(vNormal);
                        vec3 lightDirection = normalize(uLightPosition - vPosition);
                        float nDotL = max(dot(lightDirection, normal), 0.0);
                        vec3 diffuse = lightColor * color.rgb * nDotL;
                        vec3 ambient = ambientLight * color.rgb;
                        gl_FragColor = vec4(diffuse + ambient, color.a);
                    }`;

                const vShader = gl.createShader(gl.VERTEX_SHADER);
                gl.shaderSource(vShader, vertShaderSource);
                gl.compileShader(vShader);
                let ok = gl.getShaderParameter(vShader, gl.COMPILE_STATUS);
                if (!ok) { console.log("vert: " + gl.getShaderInfoLog(vShader)); };

                const fShader = gl.createShader(gl.FRAGMENT_SHADER);
                gl.shaderSource(fShader, fragShaderSource);
                gl.compileShader(fShader);
                ok = gl.getShaderParameter(vShader, gl.COMPILE_STATUS);
                if (!ok) { console.log("frag: " + gl.getShaderInfoLog(fShader)); };

                const program = gl.createProgram();
                gl.attachShader(program, vShader);
                gl.attachShader(program, fShader);
                gl.linkProgram(program);
                ok = gl.getProgramParameter(program, gl.LINK_STATUS);
                if (!ok) { console.log("link: " + gl.getProgramInfoLog(program)); };
                gl.useProgram(program);

                // Create a cube
                //    v6----- v5
                //   /|      /|
                //  v1------v0|
                //  | |     | |
                //  | |v7---|-|v4
                //  |/      |/
                //  v2------v3

                // const vertPositions = [
                //     0.5, 0.5, 0.5, -0.5, 0.5, 0.5, -0.5, -0.5, 0.5, 0.5, -0.5, 0.5, // v0-v1-v2-v3 front
                //     0.5, 0.5, 0.5, 0.5, -0.5, 0.5, 0.5, -0.5, -0.5, 0.5, 0.5, -0.5, // v0-v3-v4-v5 right
                //     0.5, 0.5, 0.5, 0.5, 0.5, -0.5, -0.5, 0.5, -0.5, -0.5, 0.5, 0.5, // v0-v5-v6-v1 up
                //     -0.5, 0.5, 0.5, -0.5, 0.5, -0.5, -0.5, -0.5, -0.5, -0.5, -0.5, 0.5, // v1-v6-v7-v2 left
                //     -0.5, -0.5, -0.5, 0.5, -0.5, -0.5, 0.5, -0.5, 0.5, -0.5, -0.5, 0.5, // v7-v4-v3-v2 down
                //     0.5, -0.5, -0.5, -0.5, -0.5, -0.5, -0.5, 0.5, -0.5, 0.5, 0.5, -0.5 // v4-v7-v6-v5 back
                // ];
                const vertPosBuffer = gl.createBuffer();
                gl.bindBuffer(gl.ARRAY_BUFFER, vertPosBuffer);
                // gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertPositions), gl.STATIC_DRAW);
                // const vertPosData = new Uint8Array(binData, 0, 288);
                const vertPosData = new Float32Array(binData, 0, 288 / Float32Array.BYTES_PER_ELEMENT);
                gl.bufferData(gl.ARRAY_BUFFER, vertPosData, gl.STATIC_DRAW);
                gl.bindAttribLocation(program, 0, "aPosition");
                gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 0, 0);
                gl.enableVertexAttribArray(0);

                // const normals = [
                //     0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, // v0-v1-v2-v3 front
                //     1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, // v0-v3-v4-v5 right
                //     0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, // v0-v5-v6-v1 up
                //     -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, // v1-v6-v7-v2 left
                //     0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, // v7-v4-v3-v2 down
                //     0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0 // v4-v7-v6-v5 back  
                // ];
                const normalBuffer = gl.createBuffer();
                gl.bindBuffer(gl.ARRAY_BUFFER, normalBuffer);
                // gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(normals), gl.STATIC_DRAW);
                // const normalData = new Uint8Array(binData, 288, 288);
                const normalData = new Float32Array(binData, 288, 288 / Float32Array.BYTES_PER_ELEMENT);
                gl.bufferData(gl.ARRAY_BUFFER, normalData, gl.STATIC_DRAW);
                gl.bindAttribLocation(program, 1, "aNormal");
                gl.vertexAttribPointer(1, 3, gl.FLOAT, false, 0, 0);
                gl.enableVertexAttribArray(1);

                // const indices = [
                //     0, 1, 2, 0, 2, 3,       // front
                //     4, 5, 6, 4, 6, 7,       // right
                //     8, 9, 10, 8, 10, 11,    // up
                //     12, 13, 14, 12, 14, 15, // left
                //     16, 17, 18, 16, 18, 19, // down
                //     20, 21, 22, 20, 22, 23  // back 
                // ];
                const indexBuffer = gl.createBuffer();
                gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
                // const indexData = new Uint8Array(binData, 576, 72);
                const indexData = new Uint16Array(binData, 576, 36);
                gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indexData, gl.STATIC_DRAW);

                const projMatrix = glMatrix.mat4.create();
                glMatrix.mat4.perspective(projMatrix, 55 * Math.PI / 180, 1, 0.1, 500);
                const viewMatrix = glMatrix.mat4.create();
                glMatrix.mat4.lookAt(viewMatrix, [10, 15, 20], [0, 0, 0], [0, 1, 0]);
                const projViewMatrix = glMatrix.mat4.create();
                glMatrix.mat4.mul(projViewMatrix, projMatrix, viewMatrix);

                const modelMatrix = glMatrix.mat4.create();
                glMatrix.mat4.fromTranslation(modelMatrix, [0, 0, 0]);
                glMatrix.mat4.rotate(modelMatrix, modelMatrix, 0 * Math.PI / 180, [1, 0, 0]);
                glMatrix.mat4.scale(modelMatrix, modelMatrix, [5, 5, 5]);
                const mvpMatrix = glMatrix.mat4.create();
                glMatrix.mat4.mul(mvpMatrix, projViewMatrix, modelMatrix);
                const uMvpMatrixLocation = gl.getUniformLocation(program, "uMvpMatrix");
                gl.uniformMatrix4fv(uMvpMatrixLocation, false, mvpMatrix);
                const uModelMatrixLocation = gl.getUniformLocation(program, "uModelMatrix");
                gl.uniformMatrix4fv(uModelMatrixLocation, false, modelMatrix);

                const normalMatrix = glMatrix.mat4.create();
                glMatrix.mat4.invert(normalMatrix, modelMatrix);
                glMatrix.mat4.transpose(normalMatrix, normalMatrix);
                const uNormalMatrixLocation = gl.getUniformLocation(program, "uNormalMatrix");
                gl.uniformMatrix4fv(uNormalMatrixLocation, false, normalMatrix);

                const lightPosition = glMatrix.vec3.fromValues(7, 8, 9);
                const uLightPositionLocation = gl.getUniformLocation(program, "uLightPosition");
                gl.uniform3fv(uLightPositionLocation, lightPosition);

                gl.clearColor(0.2, 0.2, 0.2, 1);
                gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
                gl.drawElements(gl.TRIANGLES, 36, gl.UNSIGNED_SHORT, 0);
            });
        });

        function loadFile(path, callback)
        {
            const xhr = new XMLHttpRequest();
            xhr.onreadystatechange = () =>
            {
                if (xhr.readyState === 4 && xhr.status != 404)
                {
                    callback(xhr.responseText);
                }
            };
            xhr.open("GET", path, true);
            xhr.send();
        }

        function loadBin(path, callback)
        {
            const xhr = new XMLHttpRequest();

            xhr.onreadystatechange = () =>
            {
                if (xhr.readyState === 4 && xhr.status != 404)
                {
                    callback(xhr.response);
                }
            };

            xhr.open("GET", path, true);
            xhr.responseType = "arraybuffer";
            xhr.send();
        }
    </script>
</body>

</html>

BoxBlender3.gltf

{
    "asset" : {
        "generator" : "Khronos glTF Blender I/O v1.7.33",
        "version" : "2.0"
    },
    "scene" : 0,
    "scenes" : [
        {
            "name" : "Scene",
            "nodes" : [
                0
            ]
        }
    ],
    "nodes" : [
        {
            "mesh" : 0,
            "name" : "Cube"
        }
    ],
    "materials" : [
        {
            "doubleSided" : true,
            "name" : "Material",
            "pbrMetallicRoughness" : {
                "baseColorFactor" : [
                    0.800000011920929,
                    0.800000011920929,
                    0.800000011920929,
                    1
                ],
                "metallicFactor" : 0,
                "roughnessFactor" : 0.4000000059604645
            }
        }
    ],
    "meshes" : [
        {
            "name" : "Cube",
            "primitives" : [
                {
                    "attributes" : {
                        "POSITION" : 0,
                        "NORMAL" : 1
                    },
                    "indices" : 2,
                    "material" : 0
                }
            ]
        }
    ],
    "accessors" : [
        {
            "bufferView" : 0,
            "componentType" : 5126,
            "count" : 24,
            "max" : [
                1,
                1,
                1
            ],
            "min" : [
                -1,
                -1,
                -1
            ],
            "type" : "VEC3"
        },
        {
            "bufferView" : 1,
            "componentType" : 5126,
            "count" : 24,
            "type" : "VEC3"
        },
        {
            "bufferView" : 2,
            "componentType" : 5123,
            "count" : 36,
            "type" : "SCALAR"
        }
    ],
    "bufferViews" : [
        {
            "buffer" : 0,
            "byteLength" : 288,
            "byteOffset" : 0
        },
        {
            "buffer" : 0,
            "byteLength" : 288,
            "byteOffset" : 288
        },
        {
            "buffer" : 0,
            "byteLength" : 72,
            "byteOffset" : 576
        }
    ],
    "buffers" : [
        {
            "byteLength" : 648,
            "uri" : "BoxBlender3.bin"
        }
    ]
}
1 Like

How it is going? I feel like I miss your regular examples :slight_smile:
С Наступающим!

1 Like

For a while I will use and practice only with pure WebGL 1.0 + glMatrix + Ammo.js (Planck.js and Matter.js for 2D). I need simple graphics (without PBR) and low-poly models with full control and understanding how it works and how can I optimize my apps for laptops. But time to time I will practice with Babylon.js too later.

I made this demo in pure WebGL yesterday: FPS Camera. Ammmo.js, WebGL, JavaScript You can switch FPS and Third person camera by ‘f’ key.

fps-capsule-steps

2 Likes

I made better FPS-controller than before: Christmas Tree. Ammo.js, WebGL, JavaScript

  • w, s, d, a - for moving
  • arrows keys - for camera rotation

image

1 Like