@polygonjs/polygonjs
Version:
node-based WebGL 3D engine https://polygonjs.com
240 lines (239 loc) • 7 kB
JavaScript
;
import { AnimationMixer } from "three";
import { arraySortBy } from "../../core/ArrayUtils";
import { isBooleanTrue } from "../../core/Type";
import {
NamedFunction1,
NamedFunction2,
NamedFunction3,
NamedFunction5,
ObjectNamedFunction0
} from "./_Base";
const _mixerByObject = /* @__PURE__ */ new Map();
const _actionByNameByMixer = /* @__PURE__ */ new Map();
export function findOrCreateAnimationMixer(object3D) {
let animationMixer = _mixerByObject.get(object3D);
if (!animationMixer) {
animationMixer = new AnimationMixer(object3D);
_mixerByObject.set(object3D, animationMixer);
}
return animationMixer;
}
function findOrCreateAnimationAction(mixer, clipName, autoPlay) {
let mixerMap = _actionByNameByMixer.get(mixer);
if (!mixerMap) {
mixerMap = /* @__PURE__ */ new Map();
_actionByNameByMixer.set(mixer, mixerMap);
}
let action = mixerMap.get(clipName);
if (!action) {
const root = mixer.getRoot();
const animations = root.animations;
if (!animations) {
console.warn("no animations");
return;
}
const animation = animations.find((animation2) => animation2.name == clipName);
if (!animation) {
return;
}
action = mixer.existingAction(animation) || mixer.clipAction(animation);
if (isBooleanTrue(autoPlay)) {
action.play();
}
mixerMap.set(clipName, action);
}
return action;
}
export function animationClipsFromAnimationMixer(animationMixer) {
const root = animationMixer.getRoot();
return root.animations;
}
export function existingAnimationActionsFromAnimationMixer(animationMixer) {
const root = animationMixer.getRoot();
const animations = root.animations;
if (!animations) {
console.warn("no animations found", root);
return [];
}
const animationActions = [];
for (let animation of animations) {
const existingAnimationAction = animationMixer.existingAction(animation);
if (existingAnimationAction) {
animationActions.push(existingAnimationAction);
}
}
return animationActions;
}
export function getMostActiveAnimationActionFromMixer(animationMixer, except) {
const existing = existingAnimationActionsFromAnimationMixer(animationMixer);
const otherActions = existing.filter((action) => action !== except);
const actionsSortedByWeight = arraySortBy(otherActions, (action) => -action.getEffectiveWeight());
const mostActiveAnimationAction = actionsSortedByWeight[0];
return {
otherActions,
mostActiveAnimationAction
};
}
function _setAnimationActionWeight(action, weight) {
action.enabled = true;
action.setEffectiveTimeScale(1);
action.setEffectiveWeight(weight);
}
function _crossFade(from, to, duration, warp) {
_setAnimationActionWeight(to, 1);
to.syncWith(from);
to.play();
from.crossFadeTo(to, duration, warp);
}
function _fadeOutOtherActions(animationActionTo, duration, warp, startOnFromActionEnd) {
const mixer = animationActionTo.getMixer();
const { otherActions, mostActiveAnimationAction } = getMostActiveAnimationActionFromMixer(mixer, animationActionTo);
const animationActionFrom = mostActiveAnimationAction;
const _fadeInCurrentAndFadeOutOtherActions = () => {
_crossFade(animationActionFrom, animationActionTo, duration, warp);
for (let action of otherActions) {
action.fadeOut(duration);
}
};
if (otherActions.length == 0) {
_fadeInSimple(animationActionTo, duration);
} else {
if (startOnFromActionEnd) {
animationActionTo.stop();
_fadeInWhenPreviousLoopCompleted(mixer, animationActionFrom, _fadeInCurrentAndFadeOutOtherActions);
} else {
_fadeInCurrentAndFadeOutOtherActions();
}
}
}
function _fadeInSimple(animationActionTo, duration) {
_setAnimationActionWeight(animationActionTo, 1);
animationActionTo.fadeIn(duration);
}
function _fadeInWhenPreviousLoopCompleted(mixer, animationActionFrom, callback) {
const onLoop = (event) => {
if (event.action === animationActionFrom) {
mixer.removeEventListener("loop", onLoop);
callback();
}
};
mixer.addEventListener("loop", onLoop);
}
function startCrossFade(actionFrom, actionToGet, duration, warp) {
const actionTo = actionToGet();
if (!actionTo) {
return;
}
_crossFade(actionFrom, actionTo, duration, warp);
}
export class getAnimationMixer extends ObjectNamedFunction0 {
static type() {
return "getAnimationMixer";
}
func(object3D) {
const action = findOrCreateAnimationMixer(object3D);
return action;
}
}
export class animationMixerUpdate extends ObjectNamedFunction0 {
static type() {
return "animationMixerUpdate";
}
func(object3D) {
const mixer = _mixerByObject.get(object3D);
if (!mixer) {
return;
}
const delta = this.scene.timeController.delta();
const root = mixer.getRoot();
if (root.traverse) {
root.traverse((child) => {
if (!child.matrixAutoUpdate) {
child.updateMatrix();
}
});
}
mixer.update(delta);
}
}
export class getAnimationAction extends NamedFunction3 {
static type() {
return "getAnimationAction";
}
func(mixer, clipName, autoPlay) {
const action = findOrCreateAnimationAction(mixer, clipName, autoPlay);
return action;
}
}
export class animationActionPlay extends NamedFunction1 {
static type() {
return "animationActionPlay";
}
func(action) {
if (!action) {
return;
}
action.play();
}
}
export class animationActionStop extends NamedFunction1 {
static type() {
return "animationActionStop";
}
func(action) {
if (!action) {
return;
}
action.stop();
}
}
export class animationActionFadeIn extends NamedFunction5 {
static type() {
return "animationActionFadeIn";
}
func(action, duration, fadeOutOtherActions, warp, startOnFromActionEnd) {
if (!action) {
console.warn(`action '${action}' not found`);
return;
}
if (fadeOutOtherActions) {
_fadeOutOtherActions(action, duration, warp, startOnFromActionEnd);
} else {
_fadeInSimple(action, duration);
}
}
}
export class animationActionFadeOut extends NamedFunction2 {
static type() {
return "animationActionFadeOut";
}
func(action, duration) {
if (!action) {
return;
}
action.fadeOut(duration);
}
}
export class animationActionCrossFade extends NamedFunction5 {
static type() {
return "animationActionCrossFade";
}
func(actionFrom, actionToGet, duration, warp, startOnFromActionEnd) {
if (!actionFrom) {
return;
}
if (startOnFromActionEnd) {
const mixer = actionFrom.getMixer();
const onLoop = (event) => {
if (event.action === actionFrom) {
mixer.removeEventListener("loop", onLoop);
startCrossFade(actionFrom, actionToGet, duration, warp);
}
};
mixer.addEventListener("loop", onLoop);
} else {
startCrossFade(actionFrom, actionToGet, duration, warp);
}
}
}