util.ts 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. /**
  2. * Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author Alexander Rose <alexander.rose@weirdbyte.de>
  5. */
  6. import { Mat4, Vec3, Vec4, EPSILON } from '../../mol-math/linear-algebra'
  7. export { Viewport }
  8. type Viewport = {
  9. x: number
  10. y: number
  11. width: number
  12. height: number
  13. }
  14. function Viewport() {
  15. return Viewport.zero()
  16. }
  17. namespace Viewport {
  18. export function zero(): Viewport {
  19. return { x: 0, y: 0, width: 0, height: 0 }
  20. }
  21. export function create(x: number, y: number, width: number, height: number): Viewport {
  22. return { x, y, width, height }
  23. }
  24. export function clone(viewport: Viewport): Viewport {
  25. return { ...viewport }
  26. }
  27. export function copy(target: Viewport, source: Viewport): Viewport {
  28. return Object.assign(target, source)
  29. }
  30. export function set(viewport: Viewport, x: number, y: number, width: number, height: number): Viewport {
  31. viewport.x = x
  32. viewport.y = y
  33. viewport.width = width
  34. viewport.height = height
  35. return viewport
  36. }
  37. export function toVec4(v4: Vec4, viewport: Viewport): Vec4 {
  38. v4[0] = viewport.x
  39. v4[1] = viewport.y
  40. v4[2] = viewport.width
  41. v4[3] = viewport.height
  42. return v4
  43. }
  44. export function equals(a: Viewport, b: Viewport) {
  45. return a.x === b.x && a.y === b.y && a.width === b.width && a.height === b.height
  46. }
  47. }
  48. //
  49. const tmpVec3 = Vec3()
  50. /** Modifies the direction & up vectors in place, both are normalized */
  51. export function cameraLookAt(position: Vec3, up: Vec3, direction: Vec3, target: Vec3) {
  52. Vec3.sub(tmpVec3, target, position)
  53. Vec3.normalize(tmpVec3, tmpVec3)
  54. if (!Vec3.isZero(tmpVec3)) {
  55. // change direction vector to look at target
  56. const d = Vec3.dot(tmpVec3, up)
  57. if (Math.abs(d - 1) < EPSILON) { // parallel
  58. Vec3.scale(up, direction, -1)
  59. } else if (Math.abs(d + 1) < EPSILON) { // anti parallel
  60. Vec3.copy(up, direction)
  61. }
  62. Vec3.copy(direction, tmpVec3)
  63. // normalize up vector
  64. Vec3.cross(tmpVec3, direction, up)
  65. Vec3.normalize(tmpVec3, tmpVec3)
  66. Vec3.cross(up, tmpVec3, direction)
  67. Vec3.normalize(up, up)
  68. }
  69. }
  70. const NEAR_RANGE = 0
  71. const FAR_RANGE = 1
  72. const tmpVec4 = Vec4()
  73. /** Transform point into 2D window coordinates. */
  74. export function cameraProject (out: Vec4, point: Vec3, viewport: Viewport, projectionView: Mat4) {
  75. const { x: vX, y: vY, width: vWidth, height: vHeight } = viewport
  76. // clip space -> NDC -> window coordinates, implicit 1.0 for w component
  77. Vec4.set(tmpVec4, point[0], point[1], point[2], 1.0)
  78. // transform into clip space
  79. Vec4.transformMat4(tmpVec4, tmpVec4, projectionView)
  80. // transform into NDC
  81. const w = tmpVec4[3]
  82. if (w !== 0) {
  83. tmpVec4[0] /= w
  84. tmpVec4[1] /= w
  85. tmpVec4[2] /= w
  86. }
  87. // transform into window coordinates, set fourth component is (1/clip.w) as in gl_FragCoord.w
  88. out[0] = vX + vWidth / 2 * tmpVec4[0] + (0 + vWidth / 2)
  89. out[1] = vY + vHeight / 2 * tmpVec4[1] + (0 + vHeight / 2)
  90. out[2] = (FAR_RANGE - NEAR_RANGE) / 2 * tmpVec4[2] + (FAR_RANGE + NEAR_RANGE) / 2
  91. out[3] = w === 0 ? 0 : 1 / w
  92. return out
  93. }
  94. /**
  95. * Transform point from screen space to 3D coordinates.
  96. * The point must have x and y set to 2D window coordinates and z between 0 (near) and 1 (far).
  97. */
  98. export function cameraUnproject (out: Vec3, point: Vec3, viewport: Viewport, inverseProjectionView: Mat4) {
  99. const { x: vX, y: vY, width: vWidth, height: vHeight } = viewport
  100. const x = point[0] - vX
  101. const y = (vHeight - point[1] - 1) - vY
  102. const z = point[2]
  103. out[0] = (2 * x) / vWidth - 1
  104. out[1] = (2 * y) / vHeight - 1
  105. out[2] = 2 * z - 1
  106. return Vec3.transformMat4(out, out, inverseProjectionView)
  107. }