From 21ce146d36a47c89f0c7451693ffb84b5559f8f2 Mon Sep 17 00:00:00 2001 From: Alexander Rose <alexander.rose@weirdbyte.de> Date: Mon, 13 May 2019 12:00:06 -0700 Subject: [PATCH] unified perpective/orthographic matrix creation signature --- src/mol-canvas3d/camera.ts | 52 ++++++++++++++++-- src/mol-math/linear-algebra/3d/mat4.ts | 75 +++++++++++++++----------- 2 files changed, 90 insertions(+), 37 deletions(-) diff --git a/src/mol-canvas3d/camera.ts b/src/mol-canvas3d/camera.ts index 731dc74e6..6dbc0f8d9 100644 --- a/src/mol-canvas3d/camera.ts +++ b/src/mol-canvas3d/camera.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author David Sehnal <david.sehnal@gmail.com> * @author Alexander Rose <alexander.rose@weirdbyte.de> @@ -25,6 +25,12 @@ class Camera implements Object3D { readonly viewport: Viewport; readonly state: Readonly<Camera.Snapshot> = Camera.createDefaultSnapshot(); + readonly viewOffset: Camera.ViewOffset = { + enabled: false, + fullWidth: 1, fullHeight: 1, + offsetX: 0, offsetY: 0, + width: 1, height: 1 + } readonly transition: CameraTransitionManager = new CameraTransitionManager(this); @@ -143,6 +149,30 @@ namespace Camera { fogFar: number } + /** + * Sets an offseted view in a larger frustum. This is useful for + * - multi-window or multi-monitor/multi-machine setups + * - jittering the camera position for + */ + export interface ViewOffset { + enabled: boolean, + fullWidth: number, + fullHeight: number, + offsetX: number, + offsetY: number, + width: number, + height: number + } + + export function setViewOffset(out: ViewOffset, fullWidth: number, fullHeight: number, offsetX: number, offsetY: number, width: number, height: number) { + out.fullWidth = fullWidth + out.fullHeight = fullHeight + out.offsetX = offsetX + out.offsetY = offsetY + out.width = width + out.height = height + } + export function createDefaultSnapshot(): Snapshot { return { mode: 'perspective', @@ -159,7 +189,7 @@ namespace Camera { fogFar: 10000, fov: Math.PI / 4, - zoom: 1 + zoom: 1, }; } @@ -178,7 +208,7 @@ namespace Camera { fogFar: number, fov: number, - zoom: number + zoom: number, } export function copySnapshot(out: Snapshot, source?: Partial<Snapshot>) { @@ -231,10 +261,22 @@ function updateOrtho(camera: Camera) { function updatePers(camera: Camera) { const aspect = camera.viewport.width / camera.viewport.height - const { fov, near, far } = camera.state; + const { state: { fov, near, far }, viewOffset } = camera; + + let top = near * Math.tan(0.5 * fov) + let height = 2 * top + let width = aspect * height + let left = -0.5 * width + + if (viewOffset && viewOffset.enabled) { + left += viewOffset.offsetX * width / viewOffset.fullWidth + top -= viewOffset.offsetY * height / viewOffset.fullHeight + width *= viewOffset.width / viewOffset.fullWidth + height *= viewOffset.height / viewOffset.fullHeight + } // build projection matrix - Mat4.perspective(camera.projection, fov, aspect, Math.abs(near), Math.abs(far)) + Mat4.perspective(camera.projection, left, left + width, top, top - height, near, far) // build view matrix Vec3.add(_center, camera.position, camera.direction) diff --git a/src/mol-math/linear-algebra/3d/mat4.ts b/src/mol-math/linear-algebra/3d/mat4.ts index 2bc177fab..335e7b462 100644 --- a/src/mol-math/linear-algebra/3d/mat4.ts +++ b/src/mol-math/linear-algebra/3d/mat4.ts @@ -806,51 +806,62 @@ namespace Mat4 { /** * Generates a perspective projection matrix with the given bounds */ - export function perspective(out: Mat4, fovy: number, aspect: number, near: number, far: number) { - const f = 1.0 / Math.tan(fovy / 2); - const nf = 1 / (near - far); - out[0] = f / aspect; + export function perspective(out: Mat4, left: number, right: number, top: number, bottom: number, near: number, far: number) { + const x = 2 * near / (right - left); + const y = 2 * near / (top - bottom); + + const a = (right + left) / (right - left); + const b = (top + bottom) / (top - bottom); + const c = - (far + near) / (far - near); + const d = - 2 * far * near / (far - near); + + out[0] = x; out[1] = 0; out[2] = 0; out[3] = 0; out[4] = 0; - out[5] = f; + out[5] = y; out[6] = 0; out[7] = 0; - out[8] = 0; - out[9] = 0; - out[10] = (far + near) * nf; + out[8] = a; + out[9] = b; + out[10] = c; out[11] = -1; - out[12] = 0; - out[13] = 0; - out[14] = (2 * far * near) * nf; - out[15] = 0; + out[ 12 ] = 0; + out[ 13 ] = 0; + out[ 14 ] = d; + out[ 15 ] = 0; return out; } - + /** * Generates a orthogonal projection matrix with the given bounds */ export function ortho(out: Mat4, left: number, right: number, bottom: number, top: number, near: number, far: number) { - const lr = 1 / (left - right); - const bt = 1 / (bottom - top); - const nf = 1 / (near - far); - out[0] = -2 * lr; - out[1] = 0; - out[2] = 0; - out[3] = 0; - out[4] = 0; - out[5] = -2 * bt; - out[6] = 0; - out[7] = 0; - out[8] = 0; - out[9] = 0; - out[10] = 2 * nf; - out[11] = 0; - out[12] = (left + right) * lr; - out[13] = (top + bottom) * bt; - out[14] = (far + near) * nf; - out[15] = 1; + const w = 1.0 / (right - left); + const h = 1.0 / (top - bottom); + const p = 1.0 / (far - near); + + const x = (right + left) * w; + const y = (top + bottom) * h; + const z = (far + near) * p; + + out[ 0 ] = 2 * w; + out[ 1 ] = 0; + out[ 2 ] = 0; + out[ 3 ] = 0; + out[ 4 ] = 0; + out[ 5 ] = 2 * h; + out[ 6 ] = 0; + out[ 7 ] = 0; + out[ 8 ] = 0; + out[ 9 ] = 0; + out[ 10 ] = - 2 * p; + out[ 11 ] = 0; + out[ 12 ] = - x; + out[ 13 ] = - y; + out[ 14 ] = - z; + out[ 15 ] = 1; return out; } -- GitLab