DEV Community

Cover image for Part 9: Asset Handling & Animation (WebXR with Babylon.js)
Bryan for Taikonauten

Posted on • Edited on • Originally published at Medium

Part 9: Asset Handling & Animation (WebXR with Babylon.js)

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


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

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) => {
            ...
            });
        });
    });
}
Enter fullscreen mode Exit fullscreen mode

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.

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;
  }
}
Enter fullscreen mode Exit fullscreen mode

Closing the door

closeDoor() {
  if (this._door !== null) {
      this.animateDoor(30);
      this._doorIsOpen = false;
  }
}
Enter fullscreen mode Exit fullscreen mode

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

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);
}
Enter fullscreen mode Exit fullscreen mode

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


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.

Top comments (0)