UNPKG

@polygonjs/polygonjs

Version:

node-based WebGL 3D engine https://polygonjs.com

240 lines (239 loc) 7 kB
"use strict"; 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); } } }