Root Motion Animation System

Holy Crap… I think i finally fixed it… @Evgeni_Popov was right about the looping…But i came up with a better way of reseting the last motion buffers. So on _frametime = 0… I simply reset the last motion buffers and it worked :slight_smile:

This is how i update root motion delta position and rotation

if (this.applyRootMotion === true && dirtyMotionMatrix != null) {
    this._rootMotionMatrix.decompose(this._rootMotionScaling, this._rootMotionRotation, this._rootMotionPosition);
    // ..
    // Reset Last Frame Buffers
    // ..
    if (this._frametime === 0) { 
        this._lastMotionPosition.copyFrom(this._rootMotionPosition);
        this._lastMotionRotation.copyFrom(this._rootMotionRotation);
    }
    // ..
    // Update Current Delta Position
    // ..
    this._rootMotionPosition.subtractToRef(this._lastMotionPosition, this._deltaPosition);
    this._lastMotionPosition.copyFrom(this._rootMotionPosition);
    // ..
    // TODO: Optimize - Update Current Delta Rotation
    // ..
    const q1:BABYLON.Quaternion = new BABYLON.Quaternion(0,0,0,0);
    const q2:BABYLON.Quaternion = new BABYLON.Quaternion(0,0,0,0);
    const v1:BABYLON.Vector3 = new BABYLON.Vector3(0,0,0);
    BABYLON.Quaternion.InverseToRef(this._lastMotionRotation, q1);
    q1.multiplyToRef(this._rootMotionRotation, q2);
    q2.toEulerAnglesToRef(v1);
    BABYLON.Quaternion.FromEulerAnglesToRef(0, v1.y, 0, this._deltaRotation);
    this._lastMotionRotation.multiplyToRef(this._deltaRotation, this._lastMotionRotation);
}

The animation state component stores that root motion and can be be access via the:

  • getDeltaSpeed()
  • getDeletaPosition()
  • getDeltaRotation()

Using a Forward Only type movement controller:

const locomotionSpeed:number = (this.animationState != null) ? this.animationState.getDeltaSpeed() : 0;
this.transform.forward.scaleToRef(locomotionSpeed, this.playerMovementFinalVector);

or for strafing type movement:

const deltaPosition:BABYLON.Vector3 = (this.animationState != null) ? this.animationState.getDeltaPosition() : null;
if (deltaPosition != null) this.playerMovementFinalVector.copyFrom(deltaPosition);

Then the playerMovementFinalVector can easily be used with the character controller or even used to directly translate the mesh position

if (this.characterController != null) {
    this.isCharacterGrounded = this.characterController.isGrounded();
    this.isCharacterJumpFrame = false;
    if (this.isCharacterGrounded === true) {
        this.isCharacterJumpFrame = (BABYLON.SceneManager.GetKeyboardInput(this.keyboardJump) || BABYLON.SceneManager.GetGamepadButtonInput(this.buttonJump));
        this.characterController.move(this.playerMovementFinalVector);
        if (this.isCharacterJumpFrame === true) {
            this.characterController.jump(this.jumpingVelocity);
        }
    }
}

Works Beautifully… No More Animation Foot Slide… And now animation that speed up and slow down within the actual animation play perfectly synced.

Thanks for the help in narrowing down the root motion issue :slight_smile:

P.S.

I still need help with the mirror animation key frame issue:slight_smile:

3 Likes