123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129 |
- /**
- * Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
- *
- * @author Alexander Rose <alexander.rose@weirdbyte.de>
- */
- import { Mat4, Vec3, Vec4, EPSILON } from '../../mol-math/linear-algebra'
- export { Viewport }
- type Viewport = {
- x: number
- y: number
- width: number
- height: number
- }
- function Viewport() {
- return Viewport.zero()
- }
- namespace Viewport {
- export function zero(): Viewport {
- return { x: 0, y: 0, width: 0, height: 0 }
- }
- export function create(x: number, y: number, width: number, height: number): Viewport {
- return { x, y, width, height }
- }
- export function clone(viewport: Viewport): Viewport {
- return { ...viewport }
- }
- export function copy(target: Viewport, source: Viewport): Viewport {
- return Object.assign(target, source)
- }
- export function set(viewport: Viewport, x: number, y: number, width: number, height: number): Viewport {
- viewport.x = x
- viewport.y = y
- viewport.width = width
- viewport.height = height
- return viewport
- }
- export function toVec4(v4: Vec4, viewport: Viewport): Vec4 {
- v4[0] = viewport.x
- v4[1] = viewport.y
- v4[2] = viewport.width
- v4[3] = viewport.height
- return v4
- }
- export function equals(a: Viewport, b: Viewport) {
- return a.x === b.x && a.y === b.y && a.width === b.width && a.height === b.height
- }
- }
- //
- const tmpVec3 = Vec3()
- /** Modifies the direction & up vectors in place, both are normalized */
- export function cameraLookAt(position: Vec3, up: Vec3, direction: Vec3, target: Vec3) {
- Vec3.sub(tmpVec3, target, position)
- Vec3.normalize(tmpVec3, tmpVec3)
- if (!Vec3.isZero(tmpVec3)) {
- // change direction vector to look at target
- const d = Vec3.dot(tmpVec3, up)
- if (Math.abs(d - 1) < EPSILON) { // parallel
- Vec3.scale(up, direction, -1)
- } else if (Math.abs(d + 1) < EPSILON) { // anti parallel
- Vec3.copy(up, direction)
- }
- Vec3.copy(direction, tmpVec3)
- // normalize up vector
- Vec3.cross(tmpVec3, direction, up)
- Vec3.normalize(tmpVec3, tmpVec3)
- Vec3.cross(up, tmpVec3, direction)
- Vec3.normalize(up, up)
- }
- }
- const NEAR_RANGE = 0
- const FAR_RANGE = 1
- const tmpVec4 = Vec4()
- /** Transform point into 2D window coordinates. */
- export function cameraProject (out: Vec4, point: Vec3, viewport: Viewport, projectionView: Mat4) {
- const { x: vX, y: vY, width: vWidth, height: vHeight } = viewport
- // clip space -> NDC -> window coordinates, implicit 1.0 for w component
- Vec4.set(tmpVec4, point[0], point[1], point[2], 1.0)
- // transform into clip space
- Vec4.transformMat4(tmpVec4, tmpVec4, projectionView)
- // transform into NDC
- const w = tmpVec4[3]
- if (w !== 0) {
- tmpVec4[0] /= w
- tmpVec4[1] /= w
- tmpVec4[2] /= w
- }
- // transform into window coordinates, set fourth component is (1/clip.w) as in gl_FragCoord.w
- out[0] = vX + vWidth / 2 * tmpVec4[0] + (0 + vWidth / 2)
- out[1] = vY + vHeight / 2 * tmpVec4[1] + (0 + vHeight / 2)
- out[2] = (FAR_RANGE - NEAR_RANGE) / 2 * tmpVec4[2] + (FAR_RANGE + NEAR_RANGE) / 2
- out[3] = w === 0 ? 0 : 1 / w
- return out
- }
- /**
- * Transform point from screen space to 3D coordinates.
- * The point must have x and y set to 2D window coordinates and z between 0 (near) and 1 (far).
- */
- export function cameraUnproject (out: Vec3, point: Vec3, viewport: Viewport, inverseProjectionView: Mat4) {
- const { x: vX, y: vY, width: vWidth, height: vHeight } = viewport
- const x = point[0] - vX
- const y = (vHeight - point[1] - 1) - vY
- const z = point[2]
- out[0] = (2 * x) / vWidth - 1
- out[1] = (2 * y) / vHeight - 1
- out[2] = 2 * z - 1
- return Vec3.transformMat4(out, out, inverseProjectionView)
- }
|