UNPKG

@woosh/meep-engine

Version:

Pure JavaScript game engine. Fully featured and production ready.

242 lines (184 loc) • 6.71 kB
import Signal from "../../../core/events/signal/Signal.js"; import { min2 } from "../../../core/math/min2.js"; class AnimationTrackPlayback { /** * * @param {AnimationTrack} track * @param updateCallback * @param {Object} [updateTarget] * @constructor * @property {AnimationTrack} track * @property {number} position Current playback time */ constructor(track, updateCallback, updateTarget) { this.track = track; this.position = track.timeStart; this.updateCallback = updateCallback; this.updateTraget = updateTarget; this.__lastKeyIndex = 0; this.__lastTransitionIndex = 0; this.__nextKeyIndex = 0; this.__nextKeyTime = 0; this.__loop = false; this.__valueBuffer = new Float64Array(track.propertyCount); this.on = { ended: new Signal() }; this.setPosition(0); } /** * * @returns {boolean} */ get loop() { return this.__loop; } /** * * @returns {Promise<void>} */ promiseEnded() { if (this.position >= this.track.timeEnd) { return Promise.resolve(); } return new Promise((resolve, reject) => { this.on.ended.add(resolve); }); } /** * * @param {boolean} v */ setLoop(v) { this.__loop = v; } reset() { this.setPosition(this.track.timeStart); } /** * * @param {number} time */ setPosition(time) { this.position = time; const track = this.track; const transitionIndex = track.transitionIndexAt(time); this.__lastTransitionIndex = transitionIndex; if (transitionIndex !== -1) { //found a transition, read out start key index this.__lastKeyIndex = track.transitionKeys[transitionIndex]; } else { //no transition found, lets find last key at least this.__lastKeyIndex = track.keyLowerBoundIndexAt(time); } //figure out next key time const trackKeyCount = track.keyTimes.length; if (trackKeyCount > 1) { this.__nextKeyIndex = min2(this.__lastKeyIndex + 1, trackKeyCount - 1); } else { this.__nextKeyIndex = this.__lastKeyIndex; } this.__nextKeyTime = track.keyTimes[this.__nextKeyIndex]; } /** * * @returns {boolean} Indicated whenever or not a key was advanced, no advancement is possible past end of the sequence */ advanceKey() { const track = this.track; const keyCount = track.keyTimes.length; if (this.__lastKeyIndex === keyCount - 1) { //we were on the last key if (this.__loop) { //loop back this.__lastKeyIndex = -1; } else { //we're at the end of the track, no sense in using transition as we are directly on a key this.__lastTransitionIndex = -1; this.on.ended.dispatch(); //can't advance further return false; } } this.__lastKeyIndex = this.__lastKeyIndex + 1; this.__nextKeyTime = track.keyTimes[Math.min(this.__lastKeyIndex + 1, keyCount - 1)]; //fetch transition if one exists const transitionKeys = track.transitionKeys; const transitionCount = transitionKeys.length; for (let i = this.__lastTransitionIndex + 1; i < transitionCount; i++) { const startKeyIndex = transitionKeys[i]; if (startKeyIndex === this.__lastKeyIndex) { //we found a match this.__lastTransitionIndex = i; break; } else if (startKeyIndex > this.__lastKeyIndex) { //we went too far, it appears there is no transition for current key this.__lastTransitionIndex = -1; break; } } return true; } /** * Advance animation by given time delta * @param {number} timeDelta */ advance(timeDelta) { let newPosition = this.position + timeDelta; if (timeDelta < 0) { //negative delta, going back in time this.setPosition(newPosition); } else if (newPosition > this.__nextKeyTime) { if (newPosition >= this.track.timeEnd) { if (this.__loop) { //loop back newPosition %= this.track.timeEnd; } else { this.on.ended.dispatch(); } } this.setPosition(newPosition); } else { this.position = newPosition; } this.update(); } update() { this.readCurrentValues(this.__valueBuffer); this.updateCallback.apply(this.updateTraget, this.__valueBuffer); // console.log("values: ", this.__valueBuffer); } /** * * @param {number[]} result */ readCurrentValues(result) { const track = this.track; const startKeyIndex = this.__lastKeyIndex; if (this.__lastTransitionIndex !== -1) { //we're in a middle of a transition const time1 = this.__nextKeyTime; const time0 = track.keyTimes[startKeyIndex]; const timeDelta = time1 - time0; const relativeTime = (this.position - time0); const normalizedTime = relativeTime / timeDelta; const transitionFunction = track.transitionFunctions[this.__lastTransitionIndex]; const transitionValue = transitionFunction(normalizedTime); const propCount = track.propertyCount; const keyValues = track.keyValues; const offset0 = startKeyIndex * propCount; const offset1 = offset0 + propCount; for (let i = 0; i < propCount; i++) { const v0 = keyValues[i + offset0]; const v1 = keyValues[i + offset1]; const valueDelta = v1 - v0; const value = transitionValue * valueDelta + v0; result[i] = value; } } else { //we're not in a transition, can re-use old key track.readKeyValues(startKeyIndex, result); } } } export default AnimationTrackPlayback;