Babylon.js - Instances demo source

Back to demo

main.ts

import { runDemo } from "../shared/demoRunner";
import { createInstancesScene } from "./scene";

runDemo({ createScene: createInstancesScene });

scene.ts

import type { Engine } from "@babylonjs/core/Engines/engine";
import { Scene } from "@babylonjs/core/scene";
import { FreeCamera } from "@babylonjs/core/Cameras/freeCamera";
import { DirectionalLight } from "@babylonjs/core/Lights/directionalLight";
import { ShadowGenerator } from "@babylonjs/core/Lights/Shadows/shadowGenerator";
import { StandardMaterial } from "@babylonjs/core/Materials/standardMaterial";
import { Texture } from "@babylonjs/core/Materials/Textures/texture";
import { CubeTexture } from "@babylonjs/core/Materials/Textures/cubeTexture";
import { Color3 } from "@babylonjs/core/Maths/math.color";
import { Vector3 } from "@babylonjs/core/Maths/math.vector";
import { Axis, Space } from "@babylonjs/core/Maths/math.axis";
import { CreateBox } from "@babylonjs/core/Meshes/Builders/boxBuilder";
import { CreateGroundFromHeightMap } from "@babylonjs/core/Meshes/Builders/groundBuilder";
import type { GroundMesh } from "@babylonjs/core/Meshes/groundMesh";
import type { Mesh } from "@babylonjs/core/Meshes/mesh";
import { ImportMeshAsync } from "@babylonjs/core/Loading/sceneLoader";
import "@babylonjs/core/Loading/Plugins/babylonFileLoader";
import "@babylonjs/core/Meshes/instancedMesh";
import "@babylonjs/core/Lights/Shadows/shadowGeneratorSceneComponent";
import "@babylonjs/core/Engines/Extensions/engine.cubeTexture";

export function createInstancesScene(engine: Engine, canvas: HTMLCanvasElement): Scene {
    const scene = new Scene(engine);
    const light = new DirectionalLight("dir01", new Vector3(0, -1, -0.3), scene);
    const camera = new FreeCamera("Camera", new Vector3(0, 10, -20), scene);
    camera.speed = 0.4;

    light.position = new Vector3(20, 60, 30);

    scene.ambientColor = Color3.FromInts(10, 30, 10);
    scene.clearColor = Color3.FromInts(127, 165, 13).toColor4(1);
    scene.gravity = new Vector3(0, -0.5, 0);

    scene.fogMode = Scene.FOGMODE_EXP;
    scene.fogDensity = 0.02;
    scene.fogColor = new Color3(scene.clearColor.r, scene.clearColor.g, scene.clearColor.b);

    const skybox = CreateBox("skyBox", { size: 150.0 }, scene);
    const skyboxMaterial = new StandardMaterial("skyBox", scene);
    skyboxMaterial.backFaceCulling = false;
    skyboxMaterial.reflectionTexture = new CubeTexture("/Scenes/Customs/skybox/TropicalSunnyDay", scene);
    skyboxMaterial.reflectionTexture.coordinatesMode = Texture.SKYBOX_MODE;
    skyboxMaterial.diffuseColor = new Color3(0, 0, 0);
    skyboxMaterial.specularColor = new Color3(0, 0, 0);
    skyboxMaterial.disableLighting = true;
    skybox.material = skyboxMaterial;
    skybox.infiniteDistance = true;

    const makeBorder = (name: string, scaling: Vector3, position: Partial<{ x: number; z: number }>): void => {
        const border = CreateBox(name, { size: 1 }, scene);
        border.scaling = scaling;
        if (position.x !== undefined) {
            border.position.x = position.x;
        }
        if (position.z !== undefined) {
            border.position.z = position.z;
        }
        border.checkCollisions = true;
        border.isVisible = false;
    };
    makeBorder("border0", new Vector3(1, 100, 100), { x: -50.0 });
    makeBorder("border1", new Vector3(1, 100, 100), { x: 50.0 });
    makeBorder("border2", new Vector3(100, 100, 1), { z: 50.0 });
    makeBorder("border3", new Vector3(100, 100, 1), { z: -50.0 });

    const ground = CreateGroundFromHeightMap(
        "ground",
        "/Scenes/Customs/heightMap.png",
        {
            width: 100,
            height: 100,
            subdivisions: 100,
            minHeight: 0,
            maxHeight: 5,
            updatable: false,
            onReady: (groundMesh: GroundMesh) => {
                groundMesh.optimize(100);

                const shadowGenerator = new ShadowGenerator(1024, light);

                void ImportMeshAsync("/Scenes/Tree/tree.babylon", scene).then((result) => {
                    const tree = result.meshes[0] as Mesh;
                    const treeMaterial = tree.material as StandardMaterial | null;
                    if (treeMaterial) {
                        treeMaterial.opacityTexture = null;
                        treeMaterial.backFaceCulling = false;
                    }
                    tree.isVisible = false;
                    tree.position.y = groundMesh.getHeightAtCoordinates(0, 0);

                    shadowGenerator.getShadowMap()!.renderList!.push(tree);
                    const range = 60;
                    const count = 100;
                    for (let index = 0; index < count; index++) {
                        const newInstance = tree.createInstance("i" + index);
                        const x = range / 2 - Math.random() * range;
                        const z = range / 2 - Math.random() * range;
                        const y = groundMesh.getHeightAtCoordinates(x, z);
                        newInstance.position = new Vector3(x, y, z);
                        newInstance.rotate(Axis.Y, Math.random() * Math.PI * 2, Space.WORLD);
                        const scale = 0.5 + Math.random() * 2;
                        newInstance.scaling.addInPlace(new Vector3(scale, scale, scale));
                        shadowGenerator.getShadowMap()!.renderList!.push(newInstance);
                    }
                    shadowGenerator.getShadowMap()!.refreshRate = 0;
                    shadowGenerator.usePoissonSampling = true;

                    camera.checkCollisions = true;
                    camera.applyGravity = true;
                });
            },
        },
        scene
    );

    const groundMaterial = new StandardMaterial("ground", scene);
    const groundTexture = new Texture("/Scenes/Customs/Ground.jpg", scene);
    groundMaterial.diffuseTexture = groundTexture;
    groundTexture.uScale = 6;
    groundTexture.vScale = 6;
    groundMaterial.specularColor = new Color3(0, 0, 0);
    groundMaterial.emissiveColor = new Color3(0.3, 0.3, 0.3);
    ground.material = groundMaterial;
    ground.receiveShadows = true;
    ground.checkCollisions = true;

    camera.attachControl(canvas, true);

    return scene;
}