-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathFrustumMesh.js
192 lines (148 loc) · 5.43 KB
/
FrustumMesh.js
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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
import { Ray, Vector3, BoxGeometry, Mesh, Matrix4 } from 'three';
import { getRay } from '../cahvore-utilities/index.js';
const tempVec = new Vector3();
const planeProjectedVec = new Vector3();
const inverseMatrix = new Matrix4();
const position = new Vector3();
const tempRay = new Ray();
/*
* Update the positions in a frustum based on the parameters by calling the CAHV or CAHVORE conversion methods.
* It is required for the x and y values of the positions to be between [0, 1] to convert into image space.
* options from createFrustumGeometry
* positions the flat array of positions we are modifying
*/
function updateFrustumPositions( camera, positions ) {
// if projectEnds is true then the near and far distances for the rays
// are projected onto the near and far planes
const { model, near, far, planarProjectionFactor } = camera;
const projectDirection = model.A.clone().normalize();
for ( let i = 0, l = positions.count; i < l; i ++ ) {
// get the x and y locations of the current vertex
position.fromBufferAttribute( positions, i );
// convert them into image space
// This is why the range must be between [0, 1]
position.x = position.x * model.width;
position.y = position.y * model.height;
getRay( model, position, tempRay );
// get the point at the given distance along the ray
tempRay.at( position.z < 0 ? near : far, tempVec );
// get the plane-projected version of the near / far point
const zSign = position.z < 0;
tempRay.direction.normalize();
tempRay.direction.multiplyScalar( 1 / tempRay.direction.dot( projectDirection ) );
planeProjectedVec.copy( tempRay.origin ).addScaledVector( tempRay.direction, zSign ? near : far );
// interpolate to the plane vector based on planar factor
tempVec.lerp( planeProjectedVec, planarProjectionFactor );
// set the position
positions.setXYZ( i, tempVec.x, tempVec.y, tempVec.z );
}
}
/*
* Create the geometry for the frustum. Takes CameraInfo.
*/
function createCahvoreFrustumGeometry( camera ) {
const { widthSegments, heightSegments } = camera;
const geom = new BoxGeometry( 1, 1, 1, widthSegments, heightSegments, 1 );
geom.translate( 0.5, 0.5, 0 );
const positions = geom.getAttribute( 'position' );
updateFrustumPositions( camera, positions );
geom.setAttribute( 'position', positions );
geom.computeVertexNormals();
return geom;
}
/**
* @typedef {Object} CameraParameters
* @param {('CAHV'|'CAHVOR'|'CAHVORE')} type CAHV, CAHVOR, or CAHVORE
* @param {Number} width max number of pixels in width
* @param {Number} height max number of pixels in height
* @param {Vector3} C input model center
* @param {Vector3} A input model axis
* @param {Vector3} H input model horiz
* @param {Vector3} V input model vert
* @param {Vector3|null} [O=null] input model optical axis, only required for CAHVORE
* @param {Vector3|null} [R=null] radial-distortion, only required for CAHVORE
* @param {Vector3|null} [E=null] entrance-pupil, only required for CAHVORE
* @param {Number} [linearity=1] linearity parameter, only required for CAHVORE
*/
/**
* @typedef {Object} CameraInfo
* @param {CameraParameters} model
* @param {Number} [near=0.085] the distance between the camera model and the near plane
* @param {Number} [far=10.0] the distance between the camera model and the far plane
* @param {Number} [widthSegments=16] the number of segments to create along the x axis (all sides)
* @param {Number} [heightSegments=16] the number of segments to create along the x axis (all sides)
* @param {Number} [planarProjectionFactor=0]
*/
/**
* Frustum for depicting the view volume of a camera.
* This will be transformed using CAHV or CAHVORE settings.
* @extends Mesh
*/
export class FrustumMesh extends Mesh {
/**
* @param {Material} material
*/
constructor( material ) {
super();
this.material = material || this.material;
}
/**
* Update the parameters of the CAHVORE frustum geometry.
* @param {CameraInfo} camera
*/
setFromCahvoreParameters( camera ) {
const defaultedCamera = {
model: {},
near: 0.085,
far: 10.0,
widthSegments: 16,
heightSegments: 16,
planarProjectionFactor: 0,
...camera,
};
defaultedCamera.model = {
type: 'CAHV',
C: null,
A: null,
H: null,
V: null,
O: null,
R: null,
E: null,
linearity: 1,
width: 1,
height: 1,
...defaultedCamera.model,
};
this.geometry.dispose();
this.geometry = createCahvoreFrustumGeometry( defaultedCamera );
}
/**
* Updates the linear frustum view based on the provided projection matrix, frame, near, and far values.
* @param {Matrix4} projectionMatrix
* @param {Matrix4} frame
* @param {Number} near
* @param {Number} far
*/
setFromProjectionMatrix( projectionMatrix, frame, near, far ) {
inverseMatrix.copy( projectionMatrix ).invert();
const geometry = new BoxGeometry();
const posAttr = geometry.getAttribute( 'position' );
for ( let i = 0, l = posAttr.count; i < l; i ++ ) {
tempVec.fromBufferAttribute( posAttr, i ).multiplyScalar( 2.0 );
const zSign = Math.sign( tempVec.z );
tempVec.applyMatrix4( inverseMatrix );
tempVec.multiplyScalar( 1 / Math.abs( tempVec.z ) );
const dist = zSign < 0 ? far : near;
tempVec.multiplyScalar( dist );
posAttr.setXYZ( i, tempVec.x, tempVec.y, tempVec.z );
}
geometry.applyMatrix4( frame );
this.geometry = geometry;
}
copy( source ) {
super.copy( source );
this.geometry.copy( source.geometry );
return this;
}
}