Skip to content

Commit

Permalink
make animation runtime as tree-shakeable
Browse files Browse the repository at this point in the history
  • Loading branch information
noname0310 committed Aug 12, 2023
1 parent d78aba6 commit 2d3b7ad
Show file tree
Hide file tree
Showing 9 changed files with 769 additions and 717 deletions.
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@
"**/mmdOutlineRenderer.*",
"**/pmxLoader.*",
"**/bpmxLoader.*",
"**/mmdRuntimeCameraAnimation.*",
"**/mmdRuntimeModelAnimation.*",
"**/index.*"
],
"peerDependencies": {
Expand Down
712 changes: 1 addition & 711 deletions src/Runtime/Animation/mmdRuntimeAnimation.ts

Large diffs are not rendered by default.

196 changes: 196 additions & 0 deletions src/Runtime/Animation/mmdRuntimeCameraAnimation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
import { Vector3 } from "@babylonjs/core/Maths/math.vector";

import { MmdAnimation } from "@/Loader/Animation/mmdAnimation";
import type { MmdCameraAnimationTrack } from "@/Loader/Animation/mmdAnimationTrack";

import type { MmdCamera } from "../mmdCamera";
import { MmdInterpolator, MmdRuntimeAnimation } from "./mmdRuntimeAnimation";

/**
* Mmd runtime camera animation
*
* An object with mmd animation and camera binding information
*/
export class MmdRuntimeCameraAnimation extends MmdRuntimeAnimation {
public readonly animation: MmdCameraAnimationTrack;

private readonly _camera: MmdCamera;

private constructor(
animation: MmdAnimation,
camera: MmdCamera
) {
super();

this.animation = animation.cameraTrack;
this._camera = camera;
}

private static readonly _CameraPositionA = new Vector3();
private static readonly _CameraPositionB = new Vector3();
private static readonly _CameraRotationA = new Vector3();
private static readonly _CameraRotationB = new Vector3();

private static readonly _DegToRad = Math.PI / 180;

/**
* Update animation
* @param frameTime frame time in 30fps
*/
public animate(frameTime: number): void {
const cameraTrack = this.animation;
if (cameraTrack.frameNumbers.length === 0) return;

const camera = this._camera;

const clampedFrameTime = Math.max(cameraTrack.startFrame, Math.min(cameraTrack.endFrame, frameTime));
const upperBoundIndex = this._upperBoundFrameIndex(clampedFrameTime, cameraTrack);
const upperBoundIndexMinusOne = upperBoundIndex - 1;

const frameNumberA = cameraTrack.frameNumbers[upperBoundIndexMinusOne];
const frameNumberB = cameraTrack.frameNumbers[upperBoundIndex];

if (frameNumberB === undefined || frameNumberA + 1 === frameNumberB) {
const positions = cameraTrack.positions;
camera.position.set(
positions[upperBoundIndexMinusOne * 3],
positions[upperBoundIndexMinusOne * 3 + 1],
positions[upperBoundIndexMinusOne * 3 + 2]
);

const rotations = cameraTrack.rotations;
camera.rotation.set(
rotations[upperBoundIndexMinusOne * 3],
rotations[upperBoundIndexMinusOne * 3 + 1],
rotations[upperBoundIndexMinusOne * 3 + 2]
);

camera.distance = cameraTrack.distances[upperBoundIndexMinusOne];
camera.fov = cameraTrack.fovs[upperBoundIndexMinusOne] * MmdRuntimeCameraAnimation._DegToRad;
} else {
const gradient = (clampedFrameTime - frameNumberA) / (frameNumberB - frameNumberA);

const positions = cameraTrack.positions;
const positionInterpolations = cameraTrack.positionInterpolations;

const positionA = MmdRuntimeCameraAnimation._CameraPositionA.set(
positions[upperBoundIndexMinusOne * 3],
positions[upperBoundIndexMinusOne * 3 + 1],
positions[upperBoundIndexMinusOne * 3 + 2]
);
const positionB = MmdRuntimeCameraAnimation._CameraPositionB.set(
positions[upperBoundIndex * 3],
positions[upperBoundIndex * 3 + 1],
positions[upperBoundIndex * 3 + 2]
);

const xWeight = MmdInterpolator.Interpolate(
positionInterpolations[upperBoundIndex * 12] / 127, // x_x1
positionInterpolations[upperBoundIndex * 12 + 1] / 127, // x_x2
positionInterpolations[upperBoundIndex * 12 + 2] / 127, // x_y1
positionInterpolations[upperBoundIndex * 12 + 3] / 127, // x_y2
gradient
);
const yWeight = MmdInterpolator.Interpolate(
positionInterpolations[upperBoundIndex * 12 + 4] / 127, // y_x1
positionInterpolations[upperBoundIndex * 12 + 5] / 127, // y_x2
positionInterpolations[upperBoundIndex * 12 + 6] / 127, // y_y1
positionInterpolations[upperBoundIndex * 12 + 7] / 127, // y_y2
gradient
);
const zWeight = MmdInterpolator.Interpolate(
positionInterpolations[upperBoundIndex * 12 + 8] / 127, // z_x1
positionInterpolations[upperBoundIndex * 12 + 9] / 127, // z_x2
positionInterpolations[upperBoundIndex * 12 + 10] / 127, // z_y1
positionInterpolations[upperBoundIndex * 12 + 11] / 127, // z_y2
gradient
);

camera.position.set(
positionA.x + (positionB.x - positionA.x) * xWeight,
positionA.y + (positionB.y - positionA.y) * yWeight,
positionA.z + (positionB.z - positionA.z) * zWeight
);

const rotations = cameraTrack.rotations;
const rotationInterpolations = cameraTrack.rotationInterpolations;

const rotationA = MmdRuntimeCameraAnimation._CameraRotationA.set(
rotations[upperBoundIndexMinusOne * 3],
rotations[upperBoundIndexMinusOne * 3 + 1],
rotations[upperBoundIndexMinusOne * 3 + 2]
);
const rotationB = MmdRuntimeCameraAnimation._CameraRotationB.set(
rotations[upperBoundIndex * 3],
rotations[upperBoundIndex * 3 + 1],
rotations[upperBoundIndex * 3 + 2]
);

const rotationWeight = MmdInterpolator.Interpolate(
rotationInterpolations[upperBoundIndex * 4] / 127, // x1
rotationInterpolations[upperBoundIndex * 4 + 1] / 127, // x2
rotationInterpolations[upperBoundIndex * 4 + 2] / 127, // y1
rotationInterpolations[upperBoundIndex * 4 + 3] / 127, // y2
gradient
);
const oneMinusRotationWeight = 1 - rotationWeight;

camera.rotation.set(
rotationA.x * oneMinusRotationWeight + rotationB.x * rotationWeight,
rotationA.y * oneMinusRotationWeight + rotationB.y * rotationWeight,
rotationA.z * oneMinusRotationWeight + rotationB.z * rotationWeight
);

const distanceA = cameraTrack.distances[upperBoundIndexMinusOne];
const distanceB = cameraTrack.distances[upperBoundIndex];

const distanceWeight = MmdInterpolator.Interpolate(
cameraTrack.distanceInterpolations[upperBoundIndex * 4] / 127, // x1
cameraTrack.distanceInterpolations[upperBoundIndex * 4 + 1] / 127, // x2
cameraTrack.distanceInterpolations[upperBoundIndex * 4 + 2] / 127, // y1
cameraTrack.distanceInterpolations[upperBoundIndex * 4 + 3] / 127, // y2
gradient
);

camera.distance = distanceA + (distanceB - distanceA) * distanceWeight;

const fovA = cameraTrack.fovs[upperBoundIndexMinusOne];
const fovB = cameraTrack.fovs[upperBoundIndex];

const fovWeight = MmdInterpolator.Interpolate(
cameraTrack.fovInterpolations[upperBoundIndex * 4] / 127, // x1
cameraTrack.fovInterpolations[upperBoundIndex * 4 + 1] / 127, // x2
cameraTrack.fovInterpolations[upperBoundIndex * 4 + 2] / 127, // y1
cameraTrack.fovInterpolations[upperBoundIndex * 4 + 3] / 127, // y2
gradient
);

camera.fov = (fovA + (fovB - fovA) * fovWeight) * MmdRuntimeCameraAnimation._DegToRad;
}
}

/**
* bind animation to camera
* @param animation animation to bind
* @param camera bind target
* @returns MmdRuntimeCameraAnimation instance
*/
public static Create(animation: MmdAnimation, camera: MmdCamera): MmdRuntimeCameraAnimation {
return new MmdRuntimeCameraAnimation(animation, camera);
}
}

declare module "../../Loader/Animation/mmdAnimation" {
export interface MmdAnimation {
/**
* Create runtime camera animation
* @param camera bind target
* @returns MmdRuntimeCameraAnimation instance
*/
createRuntimeCameraAnimation(camera: MmdCamera): MmdRuntimeCameraAnimation;
}
}

MmdAnimation.prototype.createRuntimeCameraAnimation = function(camera: MmdCamera): MmdRuntimeCameraAnimation {
return MmdRuntimeCameraAnimation.Create(this, camera);
};
Loading

0 comments on commit 2d3b7ad

Please sign in to comment.