import { ComponentSystem } from "./ECS/component"; import { System } from "./ECS/system"; import { Accessor } from "./mesh/mesh"; import { vec3, quat } from "./math"; import { Entity } from "./ECS/entityMgr"; export class Animation { channels: AnimationChannel[] = []; static totalTime = 0; static attachChannel(anim: Animation, chan: AnimationChannel) { anim.channels.push(chan); this.totalTime = Math.max(this.totalTime, chan.endTime); } } export class AnimationChannel { entity: Entity; isBezier; isKeyframe; isLoop = true; timeline: Float32Array; keyframe: Float32Array[]; channel: Float32Array; pause = false; currentTime = 0; startTime = 0; endTime = 0; totalTime = 0; step = 0; speed = 1; constructor(pTarget, timeline: Accessor, keyframe: Accessor) { this.channel = pTarget; this.timeline = Accessor.newFloat32Array(timeline); this.endTime = this.timeline[this.timeline.length-1]; this.keyframe = Accessor.getFloat32Blocks(keyframe); if(this.timeline.length != 1) { this.startTime = this.timeline[0]; } else { this.currentTime = this.endTime; } } } class AnimationSystem extends ComponentSystem { group = []; depends = [ Animation.name, ]; reset(anim: AnimationChannel) { anim.step = 0; anim.currentTime = 0; } static defaultQuat = quat.create(); static defaultVec3 = vec3.create(); static step(anim: AnimationChannel) { let rigLength = anim.channel.length; let prevKey = anim.step; let nextKey = prevKey+1; let prevTime, prevFrame, nextTime, nextFrame; if (anim.timeline.length == 1) { // Single frame nextKey = 0; prevTime = 0; anim.pause = true; } else { prevTime = anim.timeline[prevKey]; prevFrame = anim.keyframe[prevKey]; } nextTime = anim.timeline[nextKey]; nextFrame = anim.keyframe[nextKey]; let scopeTime = nextTime - prevTime; // Confirm if (anim.currentTime < prevTime || anim.currentTime > nextTime) { console.error('Wrong step!', anim.currentTime, prevTime, nextTime); console.log(anim.timeline); return; } let interpolationValue = scopeTime ? (anim.currentTime - prevTime) / scopeTime : 1; switch (rigLength) { case 4: quat.slerp(anim.channel, prevFrame||this.defaultQuat, nextFrame, interpolationValue); break; case 3: vec3.lerp(anim.channel, prevFrame||this.defaultVec3, nextFrame, interpolationValue); break; default: break; } } playStep(anim: AnimationChannel, dt: number) { if (!anim.pause) { if (anim.currentTime > Animation.totalTime) { this.reset(anim); if (!anim.isLoop) { anim.pause = true; // execute the last frame even already missed? // anim.currentTime = anim.endTime; console.log('stop'); } return; } while(anim.currentTime > anim.timeline[anim.step + 1]) { anim.step++; } if(anim.currentTime > anim.startTime && anim.currentTime <= anim.endTime) { AnimationSystem.step(anim); } anim.currentTime += dt * anim.speed; } } onUpdate(dt) { for (let { components } of this.group) { if(dt > 0.016) dt = 0.016; let anim = components.Animation as Animation; for(let channel of anim.channels) { this.playStep(channel, dt); } } }; } System.registSystem(new AnimationSystem());