Skip to content
Snippets Groups Projects
Commit 50525796 authored by David Sehnal's avatar David Sehnal
Browse files

camera transitions

parent e5e2b026
Branches
Tags
No related merge requests found
......@@ -6,9 +6,10 @@
*/
import { Mat4, Vec3, Vec4, EPSILON } from 'mol-math/linear-algebra'
import { Viewport, cameraLookAt, cameraProject, cameraUnproject } from './camera/util';
import { Viewport, cameraProject, cameraUnproject } from './camera/util';
import { Object3D } from 'mol-gl/object3d';
import { BehaviorSubject } from 'rxjs';
import { CameraTransitionManager } from './camera/transition';
export { Camera }
......@@ -25,6 +26,8 @@ class Camera implements Object3D {
readonly viewport: Viewport;
readonly state: Readonly<Camera.Snapshot> = Camera.createDefaultSnapshot();
readonly transition: CameraTransitionManager = new CameraTransitionManager(this);
get position() { return this.state.position; }
set position(v: Vec3) { Vec3.copy(this.state.position, v); }
......@@ -69,8 +72,8 @@ class Camera implements Object3D {
return changed;
}
setState(snapshot?: Partial<Camera.Snapshot>) {
Camera.copySnapshot(this.state, snapshot);
setState(snapshot: Partial<Camera.Snapshot>) {
this.transition.apply(snapshot);
}
getSnapshot() {
......@@ -79,13 +82,14 @@ class Camera implements Object3D {
return ret;
}
lookAt(target: Vec3) {
cameraLookAt(this.direction, this.up, this.position, target);
}
// lookAt(target: Vec3) {
// cameraLookAt(this.position, this.up, this.direction, target);
// }
translate(v: Vec3) {
Vec3.add(this.position, this.position, v)
}
// translate(v: Vec3) {
// Vec3.add(this.position, this.position, v);
// cameraLookAt(this.position, this.up, this.direction, this.target);
// }
project(out: Vec4, point: Vec3) {
return cameraProject(out, point, this.viewport, this.projectionView)
......@@ -133,6 +137,7 @@ namespace Camera {
mode: Mode,
position: Vec3,
// Normalized camera direction
direction: Vec3,
up: Vec3,
target: Vec3,
......
/**
* Copyright (c) 2018 Mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
*/
import { Camera } from 'mol-canvas3d/camera';
import { Quat, Vec3 } from 'mol-math/linear-algebra';
import { lerp } from 'mol-math/interpolate';
export { CameraTransitionManager }
class CameraTransitionManager {
private t = 0;
private func: CameraTransitionManager.TransitionFunc = CameraTransitionManager.defaultTransition;
private start = 0;
private inTransition = false;
private durationMs = 0;
private source: Camera.Snapshot = Camera.createDefaultSnapshot();
private target: Camera.Snapshot = Camera.createDefaultSnapshot();
private current = Camera.createDefaultSnapshot();
apply(to: Partial<Camera.Snapshot>, durationMs: number = 0, transition?: CameraTransitionManager.TransitionFunc) {
if (durationMs <= 0 || to.mode !== this.camera.state.mode) {
this.finish(to);
return;
}
Camera.copySnapshot(this.source, this.camera);
Camera.copySnapshot(this.target, this.camera);
Camera.copySnapshot(this.target, to);
this.inTransition = true;
this.func = transition || CameraTransitionManager.defaultTransition;
this.start = this.t;
this.durationMs = durationMs;
}
tick(t: number) {
this.t = t;
this.update();
}
private finish(to: Partial<Camera.Snapshot>) {
Camera.copySnapshot(this.camera.state, to);
this.inTransition = false;
}
private update() {
if (!this.inTransition) return;
const normalized = Math.min((this.t - this.start) / this.durationMs, 1);
if (normalized === 1) {
this.finish(this.target!);
return;
}
this.func(this.current, normalized, this.source, this.target);
Camera.copySnapshot(this.camera.state, this.current);
}
constructor(private camera: Camera) {
}
}
namespace CameraTransitionManager {
export type TransitionFunc = (out: Camera.Snapshot, t: number, source: Camera.Snapshot, target: Camera.Snapshot) => void
export function defaultTransition(out: Camera.Snapshot, t: number, source: Camera.Snapshot, target: Camera.Snapshot): void {
Camera.copySnapshot(out, target);
// Rotate direction
const rot = Quat.identity();
Quat.slerp(rot, rot, Quat.rotationTo(Quat.zero(), source.direction, target.direction), t);
Vec3.transformQuat(out.direction, source.direction, rot);
// Rotate up
Quat.setIdentity(rot);
Quat.slerp(rot, rot, Quat.rotationTo(Quat.zero(), source.up, target.up), t);
Vec3.transformQuat(out.up, source.up, rot);
// Lerp target
Vec3.lerp(out.target, source.target, target.target, t);
// Update position
const dist = -lerp(Vec3.distance(source.position, source.target), Vec3.distance(target.position, target.target), t);
Vec3.scale(out.position, out.direction, dist);
Vec3.add(out.position, out.position, out.target);
// Lerp other props
out.zoom = lerp(source.zoom, target.zoom, t);
out.fov = lerp(source.fov, target.fov, t);
out.near = lerp(source.near, target.near, t);
out.far = lerp(source.far, target.far, t);
out.fogNear = lerp(source.fogNear, target.fogNear, t);
out.fogFar = lerp(source.fogFar, target.fogFar, t);
}
}
\ No newline at end of file
......@@ -225,8 +225,10 @@ namespace Canvas3D {
}
function animate() {
const t = now();
camera.transition.tick(t);
draw(false)
if (now() - lastRenderTime > 200) {
if (t - lastRenderTime > 200) {
if (pickDirty) pick()
}
window.requestAnimationFrame(animate)
......
......@@ -46,6 +46,13 @@ namespace Quat {
return out;
}
export function setIdentity(out: Quat) {
out[0] = 0;
out[1] = 0;
out[2] = 0;
out[3] = 1;
}
export function create(x: number, y: number, z: number, w: number) {
const out = identity();
out[0] = x;
......
......@@ -30,9 +30,8 @@ export function Reset(ctx: PluginContext) {
}
export function SetSnapshot(ctx: PluginContext) {
PluginCommands.Camera.SetSnapshot.subscribe(ctx, ({ snapshot }) => {
ctx.canvas3d.camera.setState(snapshot);
ctx.canvas3d.requestDraw();
PluginCommands.Camera.SetSnapshot.subscribe(ctx, ({ snapshot, durationMs }) => {
ctx.canvas3d.camera.transition.apply(snapshot, durationMs);
})
}
......@@ -52,6 +51,6 @@ export function Snapshots(ctx: PluginContext) {
PluginCommands.Camera.Snapshots.Apply.subscribe(ctx, ({ id }) => {
const e = ctx.state.cameraSnapshots.getEntry(id);
return PluginCommands.Camera.SetSnapshot.dispatch(ctx, { snapshot: e.snapshot });
return PluginCommands.Camera.SetSnapshot.dispatch(ctx, { snapshot: e.snapshot, durationMs: 500 });
});
}
\ No newline at end of file
......@@ -8,7 +8,7 @@ import { PluginCommand } from './command';
import { Camera } from 'mol-canvas3d/camera';
export const Reset = PluginCommand<{}>({ isImmediate: true });
export const SetSnapshot = PluginCommand<{ snapshot: Camera.Snapshot }>({ isImmediate: true });
export const SetSnapshot = PluginCommand<{ snapshot: Camera.Snapshot, durationMs?: number }>({ isImmediate: true });
export const Snapshots = {
Add: PluginCommand<{ name?: string, description?: string }>({ isImmediate: true }),
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment