DEV Community

Cover image for Part 8: Models & Assets (WebXR with Babylon.js)
Bryan for Taikonauten

Posted on • Updated on • Originally published at Medium

Part 8: Models & Assets (WebXR with Babylon.js)

πŸ‘€ Stumbled here on accident? Start with the first part!


Welcome back to the 8th instalment in this series. This part is about importing an asset in form of a 3D model of a door that gets attached to an anchor.

ℹ️ Remember - you can always run the code associated with this article and follow along using

npm start --part=8


loading and attaching a model to an anchor

loading and attaching a model to an anchor

Prerequisite

To be able to load model files in form of assets into a scene we first need to import the babylonjs model loader for glb/glTF files.

import '@babylonjs/loaders/glTF';
Enter fullscreen mode Exit fullscreen mode

The provided 3D model file consists of 3 layers. A Handle, a Door and a Door_frame.

The loader itself add a parent node called __root__ to the whole model, therefore the 3 layers are stored as sub meshes to the __root__.


enum DoorMeshes {
    Handle = 'Handle',
    Door = 'Door',
    DoorFrame = 'Door_frame',
    Container = '__root__',
}
Enter fullscreen mode Exit fullscreen mode

To make things easier when loading parts on their own, we create an enum for the layer names.


Loading the model

addDoor(): void {
    SceneLoader.Append("/models/", "door.glb", this._scene, ((scene: Scene) => {

        this._handle = scene.getMeshByName(DoorMeshes.Handle);
        this._door = scene.getMeshByName(DoorMeshes.Door);
        this._doorFrame = scene.getMeshByName(DoorMeshes.DoorFrame);
        this._doorContainer = scene.getMeshByName(DoorMeshes.Container);

        const meshes = this._doorContainer!.getChildMeshes();

        meshes.forEach((mesh) => {
            this._shadowGenerator!.addShadowCaster(mesh);
            mesh.receiveShadows = true;
        });

        this._handle!.isVisible = false;
        this._door!.isVisible = false;
        this._doorFrame!.isVisible = false;
    }));
}
Enter fullscreen mode Exit fullscreen mode

Next we’re going to add the addDoor function to load the 3D model.

SceneLoader.Append loads a model from a specific local file path into the the this._scene.
Once the model is loaded a callback function is called. In the callback function each mesh is assigned to a variable making them easily accessible later on.

❗️The most important layer is the node layer which is assigned to this._doorContainer since this is going to be the one that is being attached to an anchor later on.

Then we loop through all the child meshes to:

  • add the mesh as a shadow caster to the this._shadowGenerator, allowing it to cast shadows to the scene

  • setting receiveShadows to true to enable the mesh to receive shadows from other objects in the scene

Finally we set each mesh to be invisible initially to be later explicitly make them visible again when attached to an anchor.


Adding the door to the scene

async createScene(): Promise<Scene> {

  ...
  this.addDoor();

  if (this._xrAnchors && this._xrAnchors.isCompatible()) {
      this.observeAnchors();
      this.handleControllerSelection();
  }

  return this._scene;
}
Enter fullscreen mode Exit fullscreen mode

To be able for the scene to load the model, we add this.addDoor() to the createScene function.


Attaching the model to an anchor

addAnchorAtPosition(raycastHit: PickingInfo) {
    this._xrAnchors!.addAnchorAtPositionAndRotationAsync(raycastHit.pickedPoint!).then((anchor) => {
        const boxTransformNode = new TransformNode('boxTransformNode');

        this._box!.parent = boxTransformNode;
        this._box!.position = new Vector3(0, 1, .5);
        this._box!.isVisible = true;

        this._door!.isVisible = true;
        this._doorFrame!.isVisible = true;
        this._handle!.isVisible = true;

        this._doorContainer!.position = raycastHit.pickedPoint!;
        boxTransformNode.parent = this._doorContainer!;

        anchor.attachedNode = this._doorContainer!;
        anchor.attachedNode.position = raycastHit.pickedPoint!;
    });
}
Enter fullscreen mode Exit fullscreen mode

Since we already know how to attach a mesh to an anchor, we adjust our previous implementation for the this._box to this._doorContainer. Instead of attaching the this._box directly to the anchor, we assign the this._doorContainer as the parent to the box and assign the doorContainer to anchor.attachedNode. Important in this step is to make the model layers visible by setting isVisible to true.



Conclusion

In this 8th installment, we covered the process of importing and integrating a 3D door model with Babylon.js. We detailed loading the model, managing its components, and attaching it to an anchor. The steps taken demonstrate a straightforward approach to incorporating interactive elements in virtual environments, suggesting practical applications in web technology.

In the ninth and final part we’re going to implement some animations for the door.

Top comments (0)