Browse Source

pass state to animation teardown

David Sehnal 4 years ago
parent
commit
e2c411fefe

+ 24 - 21
src/extensions/mp4-export/encoder.ts

@@ -1,6 +1,5 @@
 import * as HME from 'h264-mp4-encoder';
-import { canvasToBlob } from '../../mol-canvas3d/util';
-import { AnimateAssemblyUnwind } from '../../mol-plugin-state/animation/built-in/assembly-unwind';
+import { AnimateCameraSpin } from '../../mol-plugin-state/animation/built-in/camera-spin';
 import { PluginContext } from '../../mol-plugin/context';
 
 export class Mp4Encoder {
@@ -21,54 +20,58 @@ export class Mp4Encoder {
 
     async generate() {
         // const w = 1024, h = 768;
-        const w = 1920, h = 1080;
+        // const w = 1920, h = 1080;
+        const w = 1280, h = 720;
 
         const encoder = await HME.createH264MP4Encoder();
         encoder.width = w;
         encoder.height = h;
         encoder.frameRate = 30;
+        encoder.quantizationParameter = 15;
         encoder.initialize();
 
         console.log('creating image pass');
         const pass = this.createImagePass();
 
-        const canvas = document.createElement('canvas');
-        canvas.width = w;
-        canvas.height = h;
-        const canvasCtx = canvas.getContext('2d')!;
+        // const canvas = document.createElement('canvas');
+        // canvas.width = w;
+        // canvas.height = h;
+        // const canvasCtx = canvas.getContext('2d')!;
 
         const loop = this.plugin.animationLoop;
 
         loop.stop();
         loop.resetTime(0);
 
-        const durationMs = 3000;
+        const durationMs = 5000;
         const fps = encoder.frameRate;
 
-        await this.plugin.managers.animation.play(AnimateAssemblyUnwind, { durationInMs: 3000, playOnce: true, target: 'all' });
+        // await this.plugin.managers.animation.play(AnimateAssemblyUnwind, { durationInMs: 3000, playOnce: true, target: 'all' });
+        await this.plugin.managers.animation.play(AnimateCameraSpin, { durationInMs: durationMs, direction: 'cw' });
 
-        const imageData: Uint8ClampedArray[] = [];
+        // const imageData: Uint8ClampedArray[] = [];
         const N = Math.ceil(durationMs / 1000 * fps);
-        const dt = durationMs / (N - 1);
+        const dt = durationMs / (N);
         console.log({ w, h });
-        for (let i = 0; i < N; i++) {
+        for (let i = 0; i <= N; i++) {
 
             const t = i * dt;
             await loop.tick(t, { isSynchronous: true, manualDraw: true });
 
             const image = pass.getImageData(w, h);
-            if (i === 0) canvasCtx.putImageData(image, 0, 0);
+            encoder.addFrameRgba(image.data);
+            // if (i === 0) canvasCtx.putImageData(image, 0, 0);
 
-            imageData.push(image.data);
-            console.log(`frame ${i + 1}/${N}`);
+            // imageData.push(image.data);
+            console.log(`frame ${i + 1}/${N + 1}`);
             // await this.sleep();
         }
 
-        let ii = 0;
-        for (const f of imageData) {
-            encoder.addFrameRgba(f);
-            console.log(`added ${++ii}/${N}`);
-        }
+        // let ii = 0;
+        // for (const f of imageData) {
+        //     encoder.addFrameRgba(f);
+        //     console.log(`added ${++ii}/${N}`);
+        // }
 
         console.log('finalizing');
         encoder.finalize();
@@ -80,7 +83,7 @@ export class Mp4Encoder {
         await this.plugin.managers.animation.stop();
         loop.start();
 
-        return { movie: uint8Array, image: await canvasToBlob(canvas, 'png') };
+        return { movie: uint8Array };
     }
 
     constructor(private plugin: PluginContext) {

+ 1 - 1
src/extensions/mp4-export/ui.tsx

@@ -5,7 +5,7 @@ import { download } from '../../mol-util/download';
 import { Mp4Encoder } from './encoder';
 
 interface State {
-    data?: { movie: Uint8Array, image: Blob };
+    data?: { movie: Uint8Array };
 }
 
 export class Mp4EncoderTestUI extends CollapsableControls<{}, State> {

+ 1 - 1
src/mol-plugin-state/animation/built-in/assembly-unwind.ts

@@ -56,7 +56,7 @@ export const AnimateAssemblyUnwind = PluginStateAnimation.create({
 
         return update.commit({ doNotUpdateCurrent: true });
     },
-    teardown(_, plugin) {
+    teardown(_, __, plugin) {
         const state = plugin.state.data;
         const reprs = state.select(StateSelection.Generators.ofType(PluginStateObject.Molecule.Structure.Representation3DState)
             .withTag('animate-assembly-unwind'));

+ 21 - 17
src/mol-plugin-state/animation/built-in/camera-spin.ts

@@ -12,40 +12,44 @@ import { PluginStateAnimation } from '../model';
 
 const _dir = Vec3(), _axis = Vec3(), _rot = Quat();
 
+type State = { snapshot: Camera.Snapshot };
+
 export const AnimateCameraSpin = PluginStateAnimation.create({
     name: 'built-in.animate-camera-spin',
     display: { name: 'Camera Spin' },
     params: () => ({
         durationInMs: PD.Numeric(4000, { min: 100, max: 20000, step: 100 }),
-        direction: PD.Select<'cw' | 'ccw'>('cw', [['cw', 'Clockwise'], ['ccw', 'Counter Clockwise']], { cycle: true }),
-        skipLastFrame: PD.Boolean(true)
+        direction: PD.Select<'cw' | 'ccw'>('cw', [['cw', 'Clockwise'], ['ccw', 'Counter Clockwise']], { cycle: true })
     }),
-    initialState: () => ({ }),
-    async apply(animState: { snapshot: Camera.Snapshot }, t, ctx) {
+    initialState: (_, ctx) => ({ snapshot: ctx.canvas3d?.camera.getSnapshot()! }) as State,
+    teardown: (_, state: State, ctx) => {
+        ctx.canvas3d?.requestCameraReset({ snapshot: state.snapshot, durationMs: 0 });
+    },
+    async apply(animState: State, t, ctx) {
         if (t.current === 0) {
             return { kind: 'next', state: animState };
-        } else if (ctx.params.skipLastFrame && t.current >= ctx.params.durationInMs) {
+        }
+
+        const snapshot = animState.snapshot;
+        if (snapshot.radiusMax < 0.0001) {
             return { kind: 'finished' };
         }
 
-        const camera = ctx.plugin.canvas3d?.camera!;
-        if (camera.state.radiusMax < 0.0001) {
+        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 delta = clamp((t.current - t.lastApplied) / ctx.params.durationInMs, 0, 1);
-        const angle = 2 * Math.PI * delta * (ctx.params.direction === 'ccw' ? -1 : 1);
+        const angle = 2 * Math.PI * phase * (ctx.params.direction === 'ccw' ? -1 : 1);
 
-        Vec3.sub(_dir, camera.position, camera.target);
-        Vec3.normalize(_axis, camera.up);
+        Vec3.sub(_dir, snapshot.position, snapshot.target);
+        Vec3.normalize(_axis, snapshot.up);
         Quat.setAxisAngle(_rot, _axis, angle);
         Vec3.transformQuat(_dir, _dir, _rot);
-        const position = Vec3.add(Vec3(), camera.target, _dir);
-        ctx.plugin.canvas3d?.requestCameraReset({ snapshot: { position }, durationMs: 0 });
-
-        if (t.current >= ctx.params.durationInMs) {
-            return { kind: 'finished' };
-        }
+        const position = Vec3.add(Vec3(), snapshot.target, _dir);
+        ctx.plugin.canvas3d?.requestCameraReset({ snapshot: { ...snapshot, position }, durationMs: 0 });
 
         return { kind: 'next', state: animState };
     }

+ 1 - 1
src/mol-plugin-state/animation/built-in/explode-units.ts

@@ -37,7 +37,7 @@ export const AnimateUnitsExplode = PluginStateAnimation.create({
 
         return update.commit({ doNotUpdateCurrent: true });
     },
-    teardown(_, plugin) {
+    teardown(_, __, plugin) {
         const state = plugin.state.data;
         const reprs = state.select(StateSelection.Generators.ofType(PluginStateObject.Molecule.Structure.Representation3DState)
             .withTag('animate-units-explode'));

+ 1 - 1
src/mol-plugin-state/animation/model.ts

@@ -22,7 +22,7 @@ interface PluginStateAnimation<P = any, S = any> {
 
     // TODO: support state in setup/teardown?
     setup?(params: P, ctx: PluginContext): void | Promise<void>,
-    teardown?(params: P, ctx: PluginContext): void | Promise<void>,
+    teardown?(params: P, state: S, ctx: PluginContext): void | Promise<void>,
 
     /**
      * Apply the current frame and modify the state.

+ 1 - 1
src/mol-plugin-state/manager/animation.ts

@@ -134,7 +134,7 @@ class PluginAnimationManager extends StatefulPluginComponent<PluginAnimationMana
         if (this.state.animationState !== 'stopped') {
             const anim = this._current.anim;
             if (anim.teardown) {
-                await anim.teardown(this._current.paramValues, this.context);
+                await anim.teardown(this._current.paramValues, this._current.state, this.context);
             }
 
             this.updateState({ animationState: 'stopped' });