Babylon JS Day 16

This is the last full week of the A Month of Babylon JS project. Most of the projects I want to build with Babylon JS are related to VR, so this week I’m going to learn everything I can about the WebXR features that Babylon JS has to offer.

I started today with the Diving Deeper documentation on WebXR. This page has some information about setting up a scene with WebXR, as well as some comparisons of WebVR vs WebXR.

The most basic setup involves adding this line to a scene

const xr = scene.createDefaultXRExperienceAsync();

As I mentioned last week, this helper is asynchronous, so the default template is not suitable. Here is a basic template that I use to make a new XR scenes.

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

<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>Babylon JS WebXR Template - 2021.03.22</title>
    <style>
        html,
        body {
            overflow: hidden;
            width: 100%;
            height: 100%;
            margin: 0;
            padding: 0;
        }

        #renderCanvas {
            width: 100%;
            height: 100%;
            touch-action: none;
        }
    </style>
    <script src="https://cdn.babylonjs.com/babylon.js"></script>
    <script src="https://preview.babylonjs.com/gui/babylon.gui.min.js"></script>
    <script src="https://cdn.babylonjs.com/loaders/babylonjs.loaders.min.js"></script>
    <script src="https://code.jquery.com/pep/0.4.3/pep.js"></script>
    <script src=https://preview.babylonjs.com/inspector/babylon.inspector.bundle.js></script>
</head>

<body>
    <canvas id="renderCanvas" touch-action="none"></canvas> <!-- touch-action="none" for best results from PEP -->

    <script>
        const canvas = document.getElementById("renderCanvas"); // Get the canvas element
        const engine = new BABYLON.Engine(canvas, true); // Generate the BABYLON 3D engine


        window.addEventListener("DOMContentLoaded", async function() {
            // get the canvas DOM element
            var canvas = document.getElementById("renderCanvas");
            // load the 3D engine
            var engine = new BABYLON.Engine(canvas, true);
            // createScene function that creates and return the scene
            var createScene = async function() {
                // create a basic BJS Scene object
                var scene = new BABYLON.Scene(engine);
                scene.debugLayer.show();

                const camera = new BABYLON.ArcRotateCamera("camera", -Math.PI / 2, Math.PI / 2.5, 3,
                    new BABYLON.Vector3(0, 0, 0));
                camera.upperBetaLimit = Math.PI / 2.2;
                camera.lowerRadiusLimit = 2;
                camera.upperRadiusLimit = 50;
                camera.setPosition(new BABYLON.Vector3(0, 1.5, -2));
                camera.setTarget(new BABYLON.Vector3(0, 1.5, 0));
                camera.attachControl(canvas, true);

                // create a basic light, aiming 0,1,0 - meaning, to the sky
                var light = new BABYLON.HemisphericLight("light1", new BABYLON.Vector3(0, 1, 0),
                    scene);

                // Our built-in 'ground' shape. Params: name, width, depth, subdivs, scene
                var ground = BABYLON.Mesh.CreateGround("ground1", 20, 20, 2, scene);
                var material = new BABYLON.StandardMaterial(scene);
                material.alpha = 1;
                material.diffuseColor = new BABYLON.Color3.FromHexString("#9ba8b8");
                ground.material = material;


                // WebXRDefaultExperience
                const xrDefault = await scene.createDefaultXRExperienceAsync({
                    floorMeshes: [ground]
                });
                const xrHelper = xrDefault.baseExperience;


                // return the created scene
                return scene;
            };

            // call the createScene function
            var scene = await createScene();

            // run the render loop
            engine.runRenderLoop(function() {
                scene.render();
            });

            // the canvas/window resize event handler
            window.addEventListener("resize", function() {
                engine.resize();
            });
        });
    </script>
</body>

</html>

The template above uses the default experience helper, which includes several built-in features such as pointers to interact with UI, teleportation, and the basic process of entering and exiting XR modes. There is also a basic experience helper available, which has fewer features by default but may be useful when more control is needed. For now, I’m going to use the default version.

Last week I was focused on interacting with the 3D cards. I was using the pointer event system that was added when I setup the default experience helper. Those features allowed me to interact with GUI controls that were rendered using the AdvancedDymaincTexture. Toward the end of the day on Friday I added some code that allowed me to grab and move the cards around in 3D space. This was also using the pointer system. While moving objects with the pointers is an interesting (and powerful) feature, I also want to be able to use the controllers/hands to interact with objects. Throughout the week I’ll work on this and some other ideas.