How can I add hotspot buttons on a glb model

I have loaded a GLB model and now want to add few hotspots on the model. I want to show a popup with more info when someone clicks one of the hotspots. The hotspots should also move with the model and disappear when user rotates to the other side. Here is my code example:

import { Engine, Scene, SceneLoader, Vector3, HemisphericLight, ArcRotateCamera } from 'babylonjs';
import 'babylonjs-loaders';
import * as GUI from 'babylonjs-gui';

window.addEventListener('DOMContentLoaded', function () {
    const glbFile = "the-vessel.glb";

    const canvas = document.getElementById("vessel");
    canvas.style.width = "100%";
    canvas.style.height = "100%";
    const engine = new Engine(canvas);
    let scene = new Scene(engine);

    // set the scene and camera
    setCamera(scene, canvas);
    createSkybox(scene);

    // Info Button
    // Our built-in 'sphere' shape. Params: name, subdivs, size, scene
    let guiCanvas = BABYLON.GUI.AdvancedDynamicTexture.CreateFullscreenUI("UI", true, scene);

    let button1 = BABYLON.GUI.Button.CreateSimpleButton("but1", "Click Me");
    button1.width = "200px";
    button1.height = "50px";
    button1.color = "white";
    button1.fontSize = 30;
    button1.background = "green";
    button1.onPointerUpObservable.add(function() {
        // I will trigger the popup from here
        toggleModal2();
    });
    
    //advancedTexture.addControl(button1);
    guiCanvas.addControl(button1);

    // Append the glb/gltf file to the scene
    SceneLoader.Append("file/location", glbFile, scene, (scene) => {
        console.log(scene.meshes);
    });

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

    window.addEventListener('resize', function () {
        engine.resize();
    });
});

function createSkybox(scene) {
    const texture = new BABYLON.CubeTexture('file/location/sky.env', scene);
    // Create a skybox mesh using this texture
    const skybox = scene.createDefaultSkybox(texture, true, 10000, 0.1);
    scene.environmentIntensity = 0.7;
}

function setCamera(scene, canvas) {
    let camera = new ArcRotateCamera("camera1", 0, 0, 10, new Vector3(0, 0, 0), scene);

    // This targets the camera to scene origin
    camera.setPosition(new Vector3(-2, 1, 6));

    // This attaches the camera to the canvas
    camera.attachControl(canvas, true);

    camera.speed = 5;
    camera.maxCameraSpeed = 10;
    camera.lowerRadiusLimit = 4.5;
    camera.upperRadiusLimit = 10;
    camera.wheelPrecision = 0.5;
}

function toggleModal2() {
    //let modal = document.getElementById("modal2");
    //modal.classList.toggle("show-modal");
    alert("you did it!");
}

The most simple way I can think of would be to create GUI.AdvancedDynamicTexture for a plane mesh, then add gui button to it. And utilize backFaceCulling by set it to true, so the plane and button is hidden if camera view is behind the model.

While plane needs to be parented by your model (in my case a simple box mesh), then you could position the plane (with button) to your likings. This way the button “moves” with your model and camera view.

Example PG:

Optionally you could parent the plane to a submesh of your model, if it is animated or so.

There might be a another way using linkWithMesh, but I have not got into manually coding backface logic to link with 2D GUI, @carolhmj or @sebavan may know?

3 Likes

linkWithMesh runs for 2D GUI, which is drawn on a separate layer on top of the existing scene. The way you did it, with a plane GUI is the adequate one to have backface culling :slight_smile: