Part 8: Models & Assets (WebXR with Babylon.js)
Tags: webxr, beginners, babylonjs, tutorial
Published: 2024-01-11 08:51:36

👀 Stumbled here on accident? Start with the [first part](https://dev.to/taikonauten/welcome-to-the-exciting-world-of-webxr-and-babylonjs-1nll)!

---

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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hly5gqd975deoydatgej.jpeg)
<figcaption>loading and attaching a model to an anchor</figcaption>

---

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

```typescript
import '@babylonjs/loaders/glTF';
```

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__`.

---

```typescript
enum DoorMeshes {
    Handle = 'Handle',
    Door = 'Door',
    DoorFrame = 'Door_frame',
    Container = '__root__',
}
```
To make things easier when loading parts on their own, we create an enum for the layer names.

---

## Loading the model

```typescript
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;
    }));
}
```

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

```typescript
async createScene(): Promise<Scene> {

  ...
  this.addDoor();

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

  return this._scene;
}
```

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

---

## Attaching the model to an anchor

```typescript
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!;
    });
}
```

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

---

{% vimeo 901446644 %}

---

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