diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c25ca8f16213ce2cc6001ff4317ecfdf9fa5eb1..1d4d54c0eee5155a7f68126357272e7234fa8dbf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ Note that since we don't clearly distinguish between a public and private interf ## [Unreleased] - Fix ``getOperatorsForIndex`` +- Pass animation info (current frame & count) to state animations + - Fix camera stutter for "camera spin" animation ## [v3.0.0-dev.9] - 2022-01-09 diff --git a/src/extensions/mp4-export/encoder.ts b/src/extensions/mp4-export/encoder.ts index 85191c2aad04cd6a434e823c7d0863edbbca351a..5dc12af421808abee78e69e341f7b4d7ae263e4f 100644 --- a/src/extensions/mp4-export/encoder.ts +++ b/src/extensions/mp4-export/encoder.ts @@ -73,7 +73,7 @@ export async function encodeMp4Animation<A extends PluginStateAnimation>(plugin: await plugin.managers.animation.play(params.animation.definition, params.animation.params); stoppedAnimation = false; for (let i = 0; i <= N; i++) { - await loop.tick(i * dt, { isSynchronous: true, manualDraw: true }); + await loop.tick(i * dt, { isSynchronous: true, animation: { currentFrame: i, frameCount: N }, manualDraw: true }); const image = params.pass.getImageData(width, height, normalizedViewport); encoder.addFrameRgba(image.data); diff --git a/src/mol-plugin-state/animation/built-in/camera-spin.ts b/src/mol-plugin-state/animation/built-in/camera-spin.ts index d0e3a4a49580f52579a838acaebc8b1279408bd5..3cdcfaa838fd58a24bd7099b0c83e43474fe823c 100644 --- a/src/mol-plugin-state/animation/built-in/camera-spin.ts +++ b/src/mol-plugin-state/animation/built-in/camera-spin.ts @@ -39,13 +39,9 @@ export const AnimateCameraSpin = PluginStateAnimation.create({ return { kind: 'finished' }; } - const phase = clamp(t.current / ctx.params.durationInMs, 0, 1); - - if (phase >= 0.99999) { - ctx.plugin.canvas3d?.requestCameraReset({ snapshot, durationMs: 0 }); - return { kind: 'finished' }; - } - + const phase = t.animation + ? t.animation?.currentFrame / (t.animation.frameCount + 1) + : clamp(t.current / ctx.params.durationInMs, 0, 1); const angle = 2 * Math.PI * phase * ctx.params.speed * (ctx.params.direction === 'ccw' ? -1 : 1); Vec3.sub(_dir, snapshot.position, snapshot.target); @@ -55,6 +51,10 @@ export const AnimateCameraSpin = PluginStateAnimation.create({ const position = Vec3.add(Vec3(), snapshot.target, _dir); ctx.plugin.canvas3d?.requestCameraReset({ snapshot: { ...snapshot, position }, durationMs: 0 }); + if (phase >= 0.99999) { + return { kind: 'finished' }; + } + return { kind: 'next', state: animState }; } }); \ No newline at end of file diff --git a/src/mol-plugin-state/animation/model.ts b/src/mol-plugin-state/animation/model.ts index 5f46f7e94d56df437dc9fdb44813fe0c564a47bb..22fc489913698d4b243c6909385bce810fc0e997 100644 --- a/src/mol-plugin-state/animation/model.ts +++ b/src/mol-plugin-state/animation/model.ts @@ -51,7 +51,8 @@ namespace PluginStateAnimation { export interface Time { lastApplied: number, - current: number + current: number, + animation?: { currentFrame: number, frameCount: number } } export type ApplyResult<S> = { kind: 'finished' } | { kind: 'skip' } | { kind: 'next', state: S } diff --git a/src/mol-plugin-state/manager/animation.ts b/src/mol-plugin-state/manager/animation.ts index a87ecd417d9d4f84c7a31e0722b647169a9d6bb3..0939b80af95fe69e0cd6295da5f7fc4f6f222164 100644 --- a/src/mol-plugin-state/manager/animation.ts +++ b/src/mol-plugin-state/manager/animation.ts @@ -99,12 +99,12 @@ class PluginAnimationManager extends StatefulPluginComponent<PluginAnimationMana await this.start(); } - async tick(t: number, isSynchronous?: boolean) { + async tick(t: number, isSynchronous?: boolean, animation?: PluginAnimationManager.AnimationInfo) { this.currentTime = t; if (this.isStopped) return; - if (isSynchronous) { - await this.applyFrame(); + if (isSynchronous || animation) { + await this.applyFrame(animation); } else { this.applyAsync(); } @@ -165,12 +165,12 @@ class PluginAnimationManager extends StatefulPluginComponent<PluginAnimationMana } } - private async applyFrame() { + private async applyFrame(animation?: PluginAnimationManager.AnimationInfo) { const t = this.currentTime; if (this._current.startedTime < 0) this._current.startedTime = t; const newState = await this._current.anim.apply( this._current.state, - { lastApplied: this._current.lastTime, current: t - this._current.startedTime }, + { lastApplied: this._current.lastTime, current: t - this._current.startedTime, animation }, { params: this._current.paramValues, plugin: this.context }); if (newState.kind === 'finished') { @@ -228,6 +228,11 @@ class PluginAnimationManager extends StatefulPluginComponent<PluginAnimationMana } namespace PluginAnimationManager { + export interface AnimationInfo { + currentFrame: number, + frameCount: number + } + export interface Current { anim: PluginStateAnimation params: PD.Params, diff --git a/src/mol-plugin/animation-loop.ts b/src/mol-plugin/animation-loop.ts index 3553f011a5c6cc87d288125f0da5ee694d5aa01c..c68adf4ec6ee0d94fdea29fad456969ade3dcc27 100644 --- a/src/mol-plugin/animation-loop.ts +++ b/src/mol-plugin/animation-loop.ts @@ -6,6 +6,7 @@ import { PluginContext } from './context'; import { now } from '../mol-util/now'; +import { PluginAnimationManager } from '../mol-plugin-state/manager/animation'; export class PluginAnimationLoop { private currentFrame: any = void 0; @@ -15,8 +16,8 @@ export class PluginAnimationLoop { return this._isAnimating; } - async tick(t: number, options?: { isSynchronous?: boolean, manualDraw?: boolean }) { - await this.plugin.managers.animation.tick(t, options?.isSynchronous); + async tick(t: number, options?: { isSynchronous?: boolean, manualDraw?: boolean, animation?: PluginAnimationManager.AnimationInfo }) { + await this.plugin.managers.animation.tick(t, options?.isSynchronous, options?.animation); this.plugin.canvas3d?.tick(t as now.Timestamp, options); }