Part 9: Asset Handling & Animation (WebXR with Babylon.js)
Tags: webxr, beginners, babylonjs, tutorial
Published: 2024-01-11 08:51:41

👀 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 9th and final part of this series about asset handling and animation. In the last article we added a model in form a door to the scene. In the following sections we’re going to implement opening and closing the door on the press of a button.

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

`npm start --part=9`

---


![animating the door](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ekkun4x5h3mvrr2an1a9.jpeg)
<figcaption>animating the door</figcaption>

---

## Interacting with the door

```typescript
handleControllerSelection() {
  ...
    this._xr.input.onControllerAddedObservable.add((motionControllerAdded) => {
        motionControllerAdded.onMotionControllerInitObservable.add((motionControllerInit) => {
        
            ...
            const buttonComponent = motionControllerInit.getComponent(motionControllerComponentIds[3]);

            if (buttonComponent) {
                buttonComponent.onButtonStateChangedObservable.add((component) => {
                    if (component.pressed) {
                        (this._doorIsOpen) ? this.closeDoor() : this.openDoor();
                    }
                });
            }

            triggerComponent.onButtonStateChangedObservable.add((component) => {
            ...
            });
        });
    });
}
```

First we’re going to add a new interaction to the `handleControllerSelectionfunction` by observing button presses to the controllers `A-Button`. The `motionControllerComponentIds[3]` is the component id related to this specific button. 

📚 More about component ids can be found [here](https://doc.babylonjs.com/features/featuresDeepDive/webXR/webXRInputControllerSupport#motion-controllers).

If the button is pressed the door is either opened by the `this.openDoor()` or closed `this.closeDoor()` function, depending on the state of `this._doorIsOpen`.

---

## Opening the door

```typescript
openDoor() {
  if (this._door !== null) {
      this.animateDoor(30);
      this._doorIsOpen = true;
  }
}
```

---

## Closing the door

```typescript
closeDoor() {
  if (this._door !== null) {
      this.animateDoor(30);
      this._doorIsOpen = false;
  }
}
```

By adjusting the `duration` we can create an effect where the door opens normally but closes like it is shut. The lower the `duration`, the quicker the animation is.

---

## Animating the door interaction

```typescript
animateDoor(duration: number) {

    const animationName = this._doorIsOpen ? "doorOpenQuat" : "doorCloseQuat";
    const doorAnimation = new Animation(animationName, "rotationQuaternion", 30, Animation.ANIMATIONTYPE_QUATERNION, Animation.ANIMATIONLOOPMODE_CONSTANT);

    const startRotation = this._door!.rotationQuaternion!.clone();

    const axis = new Vector3(0, 1, 0); 
    const angle = this._doorIsOpen ? -Math.PI / 1.5 : Math.PI / 1.5; 
    const endRotation = Quaternion.RotationAxis(axis, angle).multiply(startRotation);

    const keyFrames : {frame: number, value: Quaternion}[] = [];

    keyFrames.push({
        frame: 0,
        value: startRotation
    });

    keyFrames.push({
        frame: duration,
        value: endRotation
    });

    doorAnimation.setKeys(keyFrames);

    if (!this._door!.rotationQuaternion) {
        this._door!.rotationQuaternion = new Quaternion();
    }

    this._door!.animations = [doorAnimation];

    this._scene.beginAnimation(this._door, 0, duration, false);
}
```

To enhance the immersion we implement a smooth animation for the opening and closing the door.

1. **Animation Definition**:
  * `animationName`: Determines the name of the animation based on the current state of the door (open or closed).
  * `doorAnimation`: Creates a new `Animation` object for quaternion rotation. This type of rotation is smooth and avoids issues like gimbal lock. The animation runs at 30 frames per second and doesn't loop.

2. Initial Rotation:
  * Retrieves the door's current rotation in quaternion form ([a complex number system that represents rotations](https://doc.babylonjs.com/features/featuresDeepDive/mesh/transforms/center_origin/rotation_quaternions)).

3. End Rotation Calculation:
  * Defines the rotation axis (y-axis) and the angle (90 degrees for opening, -90 degrees for closing).
  * Calculates the end rotation quaternion by multiplying the start rotation by the rotation created from the specified axis and angle.

4. Keyframe Definition:
  * Initializes an array for keyframes, which are crucial points in the animation.
  * Adds the start (frame 0) and end (frame at `duration`) rotations to the keyframes. This defines the animation path from the current rotation to the target rotation.

5. Animation Assignment:
  * Ensures the door is set to use quaternion rotation.
  * Assigns the created animation to the door mesh, preparing it to be animated.

6. Begin Animation:
  * Starts the animation using the Babylon.js `beginAnimation` method on the door mesh from frame 0 to the specified `duration`. 
  * The animation is not set to loop.

---

{% vimeo 901446662 %}

---

## Conclusion

In this final part of our series, we delved into the nuances of animating a door in a 3D environment, highlighting the seamless integration of user interactions and realistic motion. Utilizing quaternion rotations and Babylon.js, we demonstrated how to create a smooth and responsive door animation that enhances user experience.