AlexisTercero55 / THREEjs_III

Another abstraction level for computer graphics on the web, build on top of Threejs.
https://alexistercero55.github.io/THREEjs_III/
1 stars 0 forks source link

animations in FBX files. #14

Closed AlexisTercero55 closed 1 year ago

AlexisTercero55 commented 1 year ago

Motivation

From

class BasicCharacterControllerProxy {
    constructor(animations) {
      this._animations = animations;
    }

    get animations() {
      return this._animations;
    }
};

at https://github.com/AlexisTercero55/THREEjs_III/blob/ee016cc5a5228ad26b4f9f19b072f24d43d7984f/js/threejs_iii/III_MODELS/CController.js#L10

AlexisTercero55 commented 1 year ago

animations definition

Expected value for BasicCharacterControllerProxy costructor:

animations = {
   name_1 : {
        clip: AnimationAction,
        action: AnimationClip,
    },
   ...

   name_n : {
        clip: AnimationAction,
        action: AnimationClip,
    }
}
AlexisTercero55 commented 1 year ago

AnimationAction (Article)

From Enter() of the IdleState:

Enter(prevState) {
    const currentAction = this._parent._proxy._animations['idle'].action;
    if (prevState) {
      const prevAction = this._parent._proxy._animations[prevState.Name].action;
      currentAction.time = 0.0;
      currentAction.enabled = true;
      currentAction.setEffectiveTimeScale(1.0);
      currentAction.setEffectiveWeight(1.0);
      currentAction.crossFadeFrom(prevAction, 0.5, true);
      currentAction.play();
    } else {
      currentAction.play();
    }
  }

AnimationActions schedule the performance of the animations which are stored in AnimationClips.

Note: Most of AnimationAction's methods can be chained.

.weight : Number The degree of influence of this action (in the interval [0, 1]). Values between 0 (no impact) and 1 (full impact) can be used to blend between several actions. Default is 1.

.timeScale : Number Scaling factor for the time. A value of 0 causes the animation to pause. Negative values cause the animation to play backwards. Default is 1.

.crossFadeFrom ( fadeOutAction : AnimationAction, durationInSeconds : Number, warpBoolean : Boolean ) : this Causes this action to fade in, fading out another action simultaneously, within the passed time interval. This method can be chained.

If warpBoolean is true, additional warping (gradually changes of the time scales) will be applied.

Note: Like with fadeIn/fadeOut, the fading starts/ends with a weight of 1.

.play () : this Tells the mixer to activate the action. This method can be chained.

AlexisTercero55 commented 1 year ago

AnimationClip

AlexisTercero55 commented 1 year ago

AnimationMixer

The AnimationMixer is a player for animations on a particular object in the scene. When multiple objects in the scene are animated independently, one AnimationMixer may be used for each object.

Constructor

AnimationMixer( rootObject : Object3D )

rootObject - the object whose animations shall be played by this mixer.

Object3D

AlexisTercero55 commented 1 year ago

LoadingManager

Handles and keeps track of loaded and pending data. A default global instance of this class is created and used by loaders if not supplied manually - see DefaultLoadingManager.

In general that should be sufficient, however there are times when it can be useful to have separate loaders - for example if you want to show separate loading bars for objects and textures.

AlexisTercero55 commented 1 year ago

About controller traslation animation

export class BasicCharacterController {

Update(timeInSeconds) {
      if (!this._target) {
        return;
      }

      // update the current state | keep walking and traslating.
      this._stateMachine.Update(timeInSeconds, this._input);

      // Calculate the frame deceleration based on the object's velocity
      const velocity = this._velocity;
      const frameDecceleration = new THREE.Vector3(
          velocity.x * this._decceleration.x,
          velocity.y * this._decceleration.y,
          velocity.z * this._decceleration.z
      );
      frameDecceleration.multiplyScalar(timeInSeconds);
      frameDecceleration.z = Math.sign(frameDecceleration.z) * Math.min(
          Math.abs(frameDecceleration.z), Math.abs(velocity.z));

      // Update the object's velocity based on the frame deceleration
      velocity.add(frameDecceleration);

      // Get the target object and its current orientation
      const controlObject = this._target;
      const _Q = new THREE.Quaternion();
      const _A = new THREE.Vector3();
      const _R = controlObject.quaternion.clone();

      // Calculate the acceleration based on user input
      const acc = this._acceleration.clone()
      // If shift is pressed, double the acceleration;
      if (this._input._keys.shift) {
        acc.multiplyScalar(2.0);
      }
      // If the object is in the "dance" state, set acceleration to zero
      if (this._stateMachine._currentState?.Name == 'dance') {
        acc.multiplyScalar(0.0);
      }
      // Update the object's velocity based on user input
      if (this._input._keys.forward) {
        velocity.z += acc.z * timeInSeconds;
      }
      if (this._input._keys.backward) {
        velocity.z -= acc.z * timeInSeconds;
      }
      // Rotate the object left around its vertical axis
      if (this._input._keys.left) {
        _A.set(0, 1, 0);
        _Q.setFromAxisAngle(_A, 4.0 * Math.PI * timeInSeconds * this._acceleration.y);
        _R.multiply(_Q);
      }
      // Rotate the object right around its vertical axis
      if (this._input._keys.right) {
        _A.set(0, 1, 0);
        _Q.setFromAxisAngle(_A, 4.0 * -Math.PI * timeInSeconds * this._acceleration.y);
        _R.multiply(_Q);
      }
      // Update the object's orientation
      controlObject.quaternion.copy(_R);
      // Calculate the object's new position based on its velocity and orientation
      const oldPosition = new THREE.Vector3();
      oldPosition.copy(controlObject.position);

      const forward = new THREE.Vector3(0, 0, 1);
      forward.applyQuaternion(controlObject.quaternion);
      forward.normalize();

      const sideways = new THREE.Vector3(1, 0, 0);
      sideways.applyQuaternion(controlObject.quaternion);
      sideways.normalize();

      sideways.multiplyScalar(velocity.x * timeInSeconds);
      forward.multiplyScalar(velocity.z * timeInSeconds);
      // Update the object's position
      controlObject.position.add(forward);
      controlObject.position.add(sideways);

      // Update the object's animation mixer (if one exists)
      oldPosition.copy(controlObject.position);
      if (this._mixer) {
        this._mixer.update(timeInSeconds);
      }
    }

}

In computer graphics, velocity is a vector that represents the speed and direction of a moving object. In the code snippet provided, the velocity vector is used to update the position of the controlled object over time.

At each update, the velocity vector is updated based on the current acceleration and the frame deceleration, which is a measure of how much the object's velocity is slowing down due to friction or other forces. The new velocity vector is then used to update the position of the controlled object by moving it along its forward and sideways vectors, which are computed based on its current orientation.

Overall, this process of updating the velocity and position of an object is fundamental to many computer graphics applications, including video games, simulations, and visual effects. By controlling the velocity of an object, we can create realistic motion and interactions between objects in a virtual environment.

AlexisTercero55 commented 1 year ago

About velocity vector update

From BasicCharacterController:_velocity

//----------------------------------------------------------------
      // Calculate the frame deceleration based on the object's velocity
      // This is a measure of how much the object's velocity is slowing down due to friction or other forces.
      const velocity = this._velocity;//:THREE.Vector3
       // The deceleration is computed by multiplying the object's velocity with the deceleration vector.
      const frameDecceleration = new THREE.Vector3(
          velocity.x * this._decceleration.x,// (-0.0005, -0.0001, -5.0)
          velocity.y * this._decceleration.y,
          velocity.z * this._decceleration.z
      );
      // Multiply the frame deceleration by the time since the last frame to get a frame-specific deceleration value
      // {x,y,z}*dt = {x*dt,y*dt,z*dt}
      // Apply a damping effect to the deceleration in the z-axis direction to simulate ground friction
      frameDecceleration.multiplyScalar(timeInSeconds);
      frameDecceleration.z = Math.sign(frameDecceleration.z) * Math.min(
          Math.abs(frameDecceleration.z), Math.abs(velocity.z));

      // Update the object's velocity based on the frame deceleration
      velocity.add(frameDecceleration);
//----------------------------------------------------------------

The damping effect is over the z-axis because in the context of 3D graphics, the z-axis usually represents the depth or height of an object in space. The code applies a damping effect to the deceleration in the z-axis direction to simulate ground friction, which is a common physical force that can slow down or stop the movement of an object in the vertical direction. This effect helps to make the movement of the object more realistic and natural-looking.

AlexisTercero55 commented 1 year ago

About model orientation update (Quaternions operrations)

From

     // Rotate the object left around its vertical axis
      if (this._input._keys.left) {
        _A.set(0, 1, 0);//THREE.Vector3
        _Q.setFromAxisAngle(_A, 4.0 * Math.PI * timeInSeconds * this._acceleration.y);
        _R.multiply(_Q);
      }
      // Rotate the object right around its vertical axis
      if (this._input._keys.right) {
        _A.set(0, 1, 0);
        _Q.setFromAxisAngle(_A, 4.0 * -Math.PI * timeInSeconds * this._acceleration.y);
        _R.multiply(_Q);
      }
      // Update the object's orientation
      controlObject.quaternion.copy(_R);

This code block is responsible for rotating the controlled object left around its vertical axis in response to user input. Here's a breakdown of what the code does:

  1. Check if the "left" or "right" key is pressed in the input object. If it is, proceed to the next step.
  2. Create a new THREE.Vector3 object _A with the x, y, and z values set to 0, 1, and 0 respectively. This represents the vertical axis around which the object will be rotated.
  3. Create a new THREE.Quaternion object _Q and set it to a rotation of 4.0 Math.PI timeInSeconds * this._acceleration.y around the _A axis. This quaternion represents the rotation to be applied to the object.
  4. Multiply the current rotation quaternion _R by the new rotation quaternion _Q. This updates the orientation of the object to include the new rotation. The resulting quaternion _R is used in the subsequent position update step to move the object based on its new orientation.

In summary, this code block is responsible for updating the orientation of the controlled object by rotating it left around its vertical axis when the left key is pressed.

AlexisTercero55 commented 1 year ago

PR for this changes

15