-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathperspective.js
More file actions
122 lines (104 loc) · 3.32 KB
/
Copy pathperspective.js
File metadata and controls
122 lines (104 loc) · 3.32 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
import { vec3, mat4 } from "pex-math";
import Camera from "./camera.js";
/**
* A class to create a perspective camera
* @augments Camera
*/
class PerspectiveCamera extends Camera {
static get DEFAULT_OPTIONS() {
return {
fov: Math.PI / 3,
};
}
/**
* Create an instance of PerspectiveCamera
* @param {import("./types.js").CameraOptions & import("./types.js").PerspectiveCameraOptions} opts
*/
constructor(opts = {}) {
super();
this.set({
...Camera.DEFAULT_OPTIONS,
...PerspectiveCamera.DEFAULT_OPTIONS,
...opts,
});
}
/**
* Update the camera
* @param {import("./types.js").CameraOptions & import("./types.js").PerspectiveCameraOptions} opts
*/
set(opts) {
super.set(opts);
if (opts.fov || opts.aspect || opts.near || opts.far || opts.view) {
if (this.view) {
const aspectRatio = this.view.totalSize[0] / this.view.totalSize[1];
const top = Math.tan(this.fov * 0.5) * this.near;
const bottom = -top;
const left = aspectRatio * bottom;
const right = aspectRatio * top;
const width = Math.abs(right - left);
const height = Math.abs(top - bottom);
const widthNormalized = width / this.view.totalSize[0];
const heightNormalized = height / this.view.totalSize[1];
const l = left + this.view.offset[0] * widthNormalized;
const r =
left + (this.view.offset[0] + this.view.size[0]) * widthNormalized;
const b =
top - (this.view.offset[1] + this.view.size[1]) * heightNormalized;
const t = top - this.view.offset[1] * heightNormalized;
mat4.frustum(this.projectionMatrix, l, r, b, t, this.near, this.far);
} else {
mat4.perspective(
this.projectionMatrix,
this.fov,
this.aspect,
this.near,
this.far,
);
}
}
}
/**
* Create a picking ray in view (camera) coordinates
* @param {number} x mouse x
* @param {number} y mouse y
* @param {number} windowWidth
* @param {number} windowHeight
* @returns {import("pex-geom").ray}
*/
getViewRay(x, y, windowWidth, windowHeight) {
if (this.view) {
x += this.view.offset[0];
y += this.view.offset[1];
windowWidth = this.view.totalSize[0];
windowHeight = this.view.totalSize[1];
}
let nx = (2 * x) / windowWidth - 1;
let ny = 1 - (2 * y) / windowHeight;
const hNear = 2 * Math.tan(this.fov / 2) * this.near;
const wNear = hNear * this.aspect;
nx *= wNear * 0.5;
ny *= hNear * 0.5;
// [origin, direction]
return [[0, 0, 0], vec3.normalize([nx, ny, -this.near])];
}
/**
* Create a picking ray in world coordinates
* @param {number} x
* @param {number} y
* @param {number} windowWidth
* @param {number} windowHeight
* @returns {import("pex-geom").ray}
*/
getWorldRay(x, y, windowWidth, windowHeight) {
let ray = this.getViewRay(x, y, windowWidth, windowHeight);
const origin = ray[0];
const direction = ray[1];
vec3.multMat4(origin, this.invViewMatrix);
// this is correct as origin is [0, 0, 0] so direction is also a point
vec3.multMat4(direction, this.invViewMatrix);
// TODO: is this necessary?
vec3.normalize(vec3.sub(direction, origin));
return ray;
}
}
export default PerspectiveCamera;