Learn More About Asset Handling & Animation in WebXR with Babylon.js

Taikonauten
Taikonauten  Magazine
3 min readFeb 13, 2024

👀 Stumbled here on accident? Start with the introduction!

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

Interacting with the door

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

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

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

Closing the door

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 with Babylon.js

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:

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.
  • Assign 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.

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.

Have you acquired a taste for creating MR applications? Well, we feel the same way. Expand your knowledge together with us and take your first steps into Mixed Reality development using Unity.

--

--

Taikonauten
Taikonauten  Magazine

We Create Digital Products & Services Users Love. Strategy, Concept, Design & Engineering