1 | /**************************************************************************/ |
2 | /* projection.cpp */ |
3 | /**************************************************************************/ |
4 | /* This file is part of: */ |
5 | /* GODOT ENGINE */ |
6 | /* https://godotengine.org */ |
7 | /**************************************************************************/ |
8 | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ |
9 | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ |
10 | /* */ |
11 | /* Permission is hereby granted, free of charge, to any person obtaining */ |
12 | /* a copy of this software and associated documentation files (the */ |
13 | /* "Software"), to deal in the Software without restriction, including */ |
14 | /* without limitation the rights to use, copy, modify, merge, publish, */ |
15 | /* distribute, sublicense, and/or sell copies of the Software, and to */ |
16 | /* permit persons to whom the Software is furnished to do so, subject to */ |
17 | /* the following conditions: */ |
18 | /* */ |
19 | /* The above copyright notice and this permission notice shall be */ |
20 | /* included in all copies or substantial portions of the Software. */ |
21 | /* */ |
22 | /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ |
23 | /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ |
24 | /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ |
25 | /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ |
26 | /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ |
27 | /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ |
28 | /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ |
29 | /**************************************************************************/ |
30 | |
31 | #include "projection.h" |
32 | |
33 | #include "core/math/aabb.h" |
34 | #include "core/math/math_funcs.h" |
35 | #include "core/math/plane.h" |
36 | #include "core/math/rect2.h" |
37 | #include "core/math/transform_3d.h" |
38 | #include "core/string/ustring.h" |
39 | |
40 | float Projection::determinant() const { |
41 | return columns[0][3] * columns[1][2] * columns[2][1] * columns[3][0] - columns[0][2] * columns[1][3] * columns[2][1] * columns[3][0] - |
42 | columns[0][3] * columns[1][1] * columns[2][2] * columns[3][0] + columns[0][1] * columns[1][3] * columns[2][2] * columns[3][0] + |
43 | columns[0][2] * columns[1][1] * columns[2][3] * columns[3][0] - columns[0][1] * columns[1][2] * columns[2][3] * columns[3][0] - |
44 | columns[0][3] * columns[1][2] * columns[2][0] * columns[3][1] + columns[0][2] * columns[1][3] * columns[2][0] * columns[3][1] + |
45 | columns[0][3] * columns[1][0] * columns[2][2] * columns[3][1] - columns[0][0] * columns[1][3] * columns[2][2] * columns[3][1] - |
46 | columns[0][2] * columns[1][0] * columns[2][3] * columns[3][1] + columns[0][0] * columns[1][2] * columns[2][3] * columns[3][1] + |
47 | columns[0][3] * columns[1][1] * columns[2][0] * columns[3][2] - columns[0][1] * columns[1][3] * columns[2][0] * columns[3][2] - |
48 | columns[0][3] * columns[1][0] * columns[2][1] * columns[3][2] + columns[0][0] * columns[1][3] * columns[2][1] * columns[3][2] + |
49 | columns[0][1] * columns[1][0] * columns[2][3] * columns[3][2] - columns[0][0] * columns[1][1] * columns[2][3] * columns[3][2] - |
50 | columns[0][2] * columns[1][1] * columns[2][0] * columns[3][3] + columns[0][1] * columns[1][2] * columns[2][0] * columns[3][3] + |
51 | columns[0][2] * columns[1][0] * columns[2][1] * columns[3][3] - columns[0][0] * columns[1][2] * columns[2][1] * columns[3][3] - |
52 | columns[0][1] * columns[1][0] * columns[2][2] * columns[3][3] + columns[0][0] * columns[1][1] * columns[2][2] * columns[3][3]; |
53 | } |
54 | |
55 | void Projection::set_identity() { |
56 | for (int i = 0; i < 4; i++) { |
57 | for (int j = 0; j < 4; j++) { |
58 | columns[i][j] = (i == j) ? 1 : 0; |
59 | } |
60 | } |
61 | } |
62 | |
63 | void Projection::set_zero() { |
64 | for (int i = 0; i < 4; i++) { |
65 | for (int j = 0; j < 4; j++) { |
66 | columns[i][j] = 0; |
67 | } |
68 | } |
69 | } |
70 | |
71 | Plane Projection::xform4(const Plane &p_vec4) const { |
72 | Plane ret; |
73 | |
74 | ret.normal.x = columns[0][0] * p_vec4.normal.x + columns[1][0] * p_vec4.normal.y + columns[2][0] * p_vec4.normal.z + columns[3][0] * p_vec4.d; |
75 | ret.normal.y = columns[0][1] * p_vec4.normal.x + columns[1][1] * p_vec4.normal.y + columns[2][1] * p_vec4.normal.z + columns[3][1] * p_vec4.d; |
76 | ret.normal.z = columns[0][2] * p_vec4.normal.x + columns[1][2] * p_vec4.normal.y + columns[2][2] * p_vec4.normal.z + columns[3][2] * p_vec4.d; |
77 | ret.d = columns[0][3] * p_vec4.normal.x + columns[1][3] * p_vec4.normal.y + columns[2][3] * p_vec4.normal.z + columns[3][3] * p_vec4.d; |
78 | return ret; |
79 | } |
80 | |
81 | Vector4 Projection::xform(const Vector4 &p_vec4) const { |
82 | return Vector4( |
83 | columns[0][0] * p_vec4.x + columns[1][0] * p_vec4.y + columns[2][0] * p_vec4.z + columns[3][0] * p_vec4.w, |
84 | columns[0][1] * p_vec4.x + columns[1][1] * p_vec4.y + columns[2][1] * p_vec4.z + columns[3][1] * p_vec4.w, |
85 | columns[0][2] * p_vec4.x + columns[1][2] * p_vec4.y + columns[2][2] * p_vec4.z + columns[3][2] * p_vec4.w, |
86 | columns[0][3] * p_vec4.x + columns[1][3] * p_vec4.y + columns[2][3] * p_vec4.z + columns[3][3] * p_vec4.w); |
87 | } |
88 | Vector4 Projection::xform_inv(const Vector4 &p_vec4) const { |
89 | return Vector4( |
90 | columns[0][0] * p_vec4.x + columns[0][1] * p_vec4.y + columns[0][2] * p_vec4.z + columns[0][3] * p_vec4.w, |
91 | columns[1][0] * p_vec4.x + columns[1][1] * p_vec4.y + columns[1][2] * p_vec4.z + columns[1][3] * p_vec4.w, |
92 | columns[2][0] * p_vec4.x + columns[2][1] * p_vec4.y + columns[2][2] * p_vec4.z + columns[2][3] * p_vec4.w, |
93 | columns[3][0] * p_vec4.x + columns[3][1] * p_vec4.y + columns[3][2] * p_vec4.z + columns[3][3] * p_vec4.w); |
94 | } |
95 | |
96 | void Projection::adjust_perspective_znear(real_t p_new_znear) { |
97 | real_t zfar = get_z_far(); |
98 | real_t znear = p_new_znear; |
99 | |
100 | real_t deltaZ = zfar - znear; |
101 | columns[2][2] = -(zfar + znear) / deltaZ; |
102 | columns[3][2] = -2 * znear * zfar / deltaZ; |
103 | } |
104 | |
105 | Projection Projection::create_depth_correction(bool p_flip_y) { |
106 | Projection proj; |
107 | proj.set_depth_correction(p_flip_y); |
108 | return proj; |
109 | } |
110 | |
111 | Projection Projection::create_light_atlas_rect(const Rect2 &p_rect) { |
112 | Projection proj; |
113 | proj.set_light_atlas_rect(p_rect); |
114 | return proj; |
115 | } |
116 | |
117 | Projection Projection::create_perspective(real_t p_fovy_degrees, real_t p_aspect, real_t p_z_near, real_t p_z_far, bool p_flip_fov) { |
118 | Projection proj; |
119 | proj.set_perspective(p_fovy_degrees, p_aspect, p_z_near, p_z_far, p_flip_fov); |
120 | return proj; |
121 | } |
122 | |
123 | Projection Projection::create_perspective_hmd(real_t p_fovy_degrees, real_t p_aspect, real_t p_z_near, real_t p_z_far, bool p_flip_fov, int p_eye, real_t p_intraocular_dist, real_t p_convergence_dist) { |
124 | Projection proj; |
125 | proj.set_perspective(p_fovy_degrees, p_aspect, p_z_near, p_z_far, p_flip_fov, p_eye, p_intraocular_dist, p_convergence_dist); |
126 | return proj; |
127 | } |
128 | |
129 | Projection Projection::create_for_hmd(int p_eye, real_t p_aspect, real_t p_intraocular_dist, real_t p_display_width, real_t p_display_to_lens, real_t p_oversample, real_t p_z_near, real_t p_z_far) { |
130 | Projection proj; |
131 | proj.set_for_hmd(p_eye, p_aspect, p_intraocular_dist, p_display_width, p_display_to_lens, p_oversample, p_z_near, p_z_far); |
132 | return proj; |
133 | } |
134 | |
135 | Projection Projection::create_orthogonal(real_t p_left, real_t p_right, real_t p_bottom, real_t p_top, real_t p_znear, real_t p_zfar) { |
136 | Projection proj; |
137 | proj.set_orthogonal(p_left, p_right, p_bottom, p_top, p_znear, p_zfar); |
138 | return proj; |
139 | } |
140 | |
141 | Projection Projection::create_orthogonal_aspect(real_t p_size, real_t p_aspect, real_t p_znear, real_t p_zfar, bool p_flip_fov) { |
142 | Projection proj; |
143 | proj.set_orthogonal(p_size, p_aspect, p_znear, p_zfar, p_flip_fov); |
144 | return proj; |
145 | } |
146 | |
147 | Projection Projection::create_frustum(real_t p_left, real_t p_right, real_t p_bottom, real_t p_top, real_t p_near, real_t p_far) { |
148 | Projection proj; |
149 | proj.set_frustum(p_left, p_right, p_bottom, p_top, p_near, p_far); |
150 | return proj; |
151 | } |
152 | |
153 | Projection Projection::create_frustum_aspect(real_t p_size, real_t p_aspect, Vector2 p_offset, real_t p_near, real_t p_far, bool p_flip_fov) { |
154 | Projection proj; |
155 | proj.set_frustum(p_size, p_aspect, p_offset, p_near, p_far, p_flip_fov); |
156 | return proj; |
157 | } |
158 | |
159 | Projection Projection::create_fit_aabb(const AABB &p_aabb) { |
160 | Projection proj; |
161 | proj.scale_translate_to_fit(p_aabb); |
162 | return proj; |
163 | } |
164 | |
165 | Projection Projection::perspective_znear_adjusted(real_t p_new_znear) const { |
166 | Projection proj = *this; |
167 | proj.adjust_perspective_znear(p_new_znear); |
168 | return proj; |
169 | } |
170 | |
171 | Plane Projection::get_projection_plane(Planes p_plane) const { |
172 | const real_t *matrix = (const real_t *)columns; |
173 | |
174 | switch (p_plane) { |
175 | case PLANE_NEAR: { |
176 | Plane new_plane = Plane(matrix[3] + matrix[2], |
177 | matrix[7] + matrix[6], |
178 | matrix[11] + matrix[10], |
179 | matrix[15] + matrix[14]); |
180 | |
181 | new_plane.normal = -new_plane.normal; |
182 | new_plane.normalize(); |
183 | return new_plane; |
184 | } |
185 | case PLANE_FAR: { |
186 | Plane new_plane = Plane(matrix[3] - matrix[2], |
187 | matrix[7] - matrix[6], |
188 | matrix[11] - matrix[10], |
189 | matrix[15] - matrix[14]); |
190 | |
191 | new_plane.normal = -new_plane.normal; |
192 | new_plane.normalize(); |
193 | return new_plane; |
194 | } |
195 | case PLANE_LEFT: { |
196 | Plane new_plane = Plane(matrix[3] + matrix[0], |
197 | matrix[7] + matrix[4], |
198 | matrix[11] + matrix[8], |
199 | matrix[15] + matrix[12]); |
200 | |
201 | new_plane.normal = -new_plane.normal; |
202 | new_plane.normalize(); |
203 | return new_plane; |
204 | } |
205 | case PLANE_TOP: { |
206 | Plane new_plane = Plane(matrix[3] - matrix[1], |
207 | matrix[7] - matrix[5], |
208 | matrix[11] - matrix[9], |
209 | matrix[15] - matrix[13]); |
210 | |
211 | new_plane.normal = -new_plane.normal; |
212 | new_plane.normalize(); |
213 | return new_plane; |
214 | } |
215 | case PLANE_RIGHT: { |
216 | Plane new_plane = Plane(matrix[3] - matrix[0], |
217 | matrix[7] - matrix[4], |
218 | matrix[11] - matrix[8], |
219 | matrix[15] - matrix[12]); |
220 | |
221 | new_plane.normal = -new_plane.normal; |
222 | new_plane.normalize(); |
223 | return new_plane; |
224 | } |
225 | case PLANE_BOTTOM: { |
226 | Plane new_plane = Plane(matrix[3] + matrix[1], |
227 | matrix[7] + matrix[5], |
228 | matrix[11] + matrix[9], |
229 | matrix[15] + matrix[13]); |
230 | |
231 | new_plane.normal = -new_plane.normal; |
232 | new_plane.normalize(); |
233 | return new_plane; |
234 | } |
235 | } |
236 | |
237 | return Plane(); |
238 | } |
239 | |
240 | Projection Projection::flipped_y() const { |
241 | Projection proj = *this; |
242 | proj.flip_y(); |
243 | return proj; |
244 | } |
245 | |
246 | Projection Projection ::jitter_offseted(const Vector2 &p_offset) const { |
247 | Projection proj = *this; |
248 | proj.add_jitter_offset(p_offset); |
249 | return proj; |
250 | } |
251 | |
252 | void Projection::set_perspective(real_t p_fovy_degrees, real_t p_aspect, real_t p_z_near, real_t p_z_far, bool p_flip_fov) { |
253 | if (p_flip_fov) { |
254 | p_fovy_degrees = get_fovy(p_fovy_degrees, 1.0 / p_aspect); |
255 | } |
256 | |
257 | real_t sine, cotangent, deltaZ; |
258 | real_t radians = Math::deg_to_rad(p_fovy_degrees / 2.0); |
259 | |
260 | deltaZ = p_z_far - p_z_near; |
261 | sine = Math::sin(radians); |
262 | |
263 | if ((deltaZ == 0) || (sine == 0) || (p_aspect == 0)) { |
264 | return; |
265 | } |
266 | cotangent = Math::cos(radians) / sine; |
267 | |
268 | set_identity(); |
269 | |
270 | columns[0][0] = cotangent / p_aspect; |
271 | columns[1][1] = cotangent; |
272 | columns[2][2] = -(p_z_far + p_z_near) / deltaZ; |
273 | columns[2][3] = -1; |
274 | columns[3][2] = -2 * p_z_near * p_z_far / deltaZ; |
275 | columns[3][3] = 0; |
276 | } |
277 | |
278 | void Projection::set_perspective(real_t p_fovy_degrees, real_t p_aspect, real_t p_z_near, real_t p_z_far, bool p_flip_fov, int p_eye, real_t p_intraocular_dist, real_t p_convergence_dist) { |
279 | if (p_flip_fov) { |
280 | p_fovy_degrees = get_fovy(p_fovy_degrees, 1.0 / p_aspect); |
281 | } |
282 | |
283 | real_t left, right, modeltranslation, ymax, xmax, frustumshift; |
284 | |
285 | ymax = p_z_near * tan(Math::deg_to_rad(p_fovy_degrees / 2.0)); |
286 | xmax = ymax * p_aspect; |
287 | frustumshift = (p_intraocular_dist / 2.0) * p_z_near / p_convergence_dist; |
288 | |
289 | switch (p_eye) { |
290 | case 1: { // left eye |
291 | left = -xmax + frustumshift; |
292 | right = xmax + frustumshift; |
293 | modeltranslation = p_intraocular_dist / 2.0; |
294 | } break; |
295 | case 2: { // right eye |
296 | left = -xmax - frustumshift; |
297 | right = xmax - frustumshift; |
298 | modeltranslation = -p_intraocular_dist / 2.0; |
299 | } break; |
300 | default: { // mono, should give the same result as set_perspective(p_fovy_degrees,p_aspect,p_z_near,p_z_far,p_flip_fov) |
301 | left = -xmax; |
302 | right = xmax; |
303 | modeltranslation = 0.0; |
304 | } break; |
305 | } |
306 | |
307 | set_frustum(left, right, -ymax, ymax, p_z_near, p_z_far); |
308 | |
309 | // translate matrix by (modeltranslation, 0.0, 0.0) |
310 | Projection cm; |
311 | cm.set_identity(); |
312 | cm.columns[3][0] = modeltranslation; |
313 | *this = *this * cm; |
314 | } |
315 | |
316 | void Projection::set_for_hmd(int p_eye, real_t p_aspect, real_t p_intraocular_dist, real_t p_display_width, real_t p_display_to_lens, real_t p_oversample, real_t p_z_near, real_t p_z_far) { |
317 | // we first calculate our base frustum on our values without taking our lens magnification into account. |
318 | real_t f1 = (p_intraocular_dist * 0.5) / p_display_to_lens; |
319 | real_t f2 = ((p_display_width - p_intraocular_dist) * 0.5) / p_display_to_lens; |
320 | real_t f3 = (p_display_width / 4.0) / p_display_to_lens; |
321 | |
322 | // now we apply our oversample factor to increase our FOV. how much we oversample is always a balance we strike between performance and how much |
323 | // we're willing to sacrifice in FOV. |
324 | real_t add = ((f1 + f2) * (p_oversample - 1.0)) / 2.0; |
325 | f1 += add; |
326 | f2 += add; |
327 | f3 *= p_oversample; |
328 | |
329 | // always apply KEEP_WIDTH aspect ratio |
330 | f3 /= p_aspect; |
331 | |
332 | switch (p_eye) { |
333 | case 1: { // left eye |
334 | set_frustum(-f2 * p_z_near, f1 * p_z_near, -f3 * p_z_near, f3 * p_z_near, p_z_near, p_z_far); |
335 | } break; |
336 | case 2: { // right eye |
337 | set_frustum(-f1 * p_z_near, f2 * p_z_near, -f3 * p_z_near, f3 * p_z_near, p_z_near, p_z_far); |
338 | } break; |
339 | default: { // mono, does not apply here! |
340 | } break; |
341 | } |
342 | } |
343 | |
344 | void Projection::set_orthogonal(real_t p_left, real_t p_right, real_t p_bottom, real_t p_top, real_t p_znear, real_t p_zfar) { |
345 | set_identity(); |
346 | |
347 | columns[0][0] = 2.0 / (p_right - p_left); |
348 | columns[3][0] = -((p_right + p_left) / (p_right - p_left)); |
349 | columns[1][1] = 2.0 / (p_top - p_bottom); |
350 | columns[3][1] = -((p_top + p_bottom) / (p_top - p_bottom)); |
351 | columns[2][2] = -2.0 / (p_zfar - p_znear); |
352 | columns[3][2] = -((p_zfar + p_znear) / (p_zfar - p_znear)); |
353 | columns[3][3] = 1.0; |
354 | } |
355 | |
356 | void Projection::set_orthogonal(real_t p_size, real_t p_aspect, real_t p_znear, real_t p_zfar, bool p_flip_fov) { |
357 | if (!p_flip_fov) { |
358 | p_size *= p_aspect; |
359 | } |
360 | |
361 | set_orthogonal(-p_size / 2, +p_size / 2, -p_size / p_aspect / 2, +p_size / p_aspect / 2, p_znear, p_zfar); |
362 | } |
363 | |
364 | void Projection::set_frustum(real_t p_left, real_t p_right, real_t p_bottom, real_t p_top, real_t p_near, real_t p_far) { |
365 | ERR_FAIL_COND(p_right <= p_left); |
366 | ERR_FAIL_COND(p_top <= p_bottom); |
367 | ERR_FAIL_COND(p_far <= p_near); |
368 | |
369 | real_t *te = &columns[0][0]; |
370 | real_t x = 2 * p_near / (p_right - p_left); |
371 | real_t y = 2 * p_near / (p_top - p_bottom); |
372 | |
373 | real_t a = (p_right + p_left) / (p_right - p_left); |
374 | real_t b = (p_top + p_bottom) / (p_top - p_bottom); |
375 | real_t c = -(p_far + p_near) / (p_far - p_near); |
376 | real_t d = -2 * p_far * p_near / (p_far - p_near); |
377 | |
378 | te[0] = x; |
379 | te[1] = 0; |
380 | te[2] = 0; |
381 | te[3] = 0; |
382 | te[4] = 0; |
383 | te[5] = y; |
384 | te[6] = 0; |
385 | te[7] = 0; |
386 | te[8] = a; |
387 | te[9] = b; |
388 | te[10] = c; |
389 | te[11] = -1; |
390 | te[12] = 0; |
391 | te[13] = 0; |
392 | te[14] = d; |
393 | te[15] = 0; |
394 | } |
395 | |
396 | void Projection::set_frustum(real_t p_size, real_t p_aspect, Vector2 p_offset, real_t p_near, real_t p_far, bool p_flip_fov) { |
397 | if (!p_flip_fov) { |
398 | p_size *= p_aspect; |
399 | } |
400 | |
401 | set_frustum(-p_size / 2 + p_offset.x, +p_size / 2 + p_offset.x, -p_size / p_aspect / 2 + p_offset.y, +p_size / p_aspect / 2 + p_offset.y, p_near, p_far); |
402 | } |
403 | |
404 | real_t Projection::get_z_far() const { |
405 | const real_t *matrix = (const real_t *)columns; |
406 | Plane new_plane = Plane(matrix[3] - matrix[2], |
407 | matrix[7] - matrix[6], |
408 | matrix[11] - matrix[10], |
409 | matrix[15] - matrix[14]); |
410 | |
411 | new_plane.normal = -new_plane.normal; |
412 | new_plane.normalize(); |
413 | |
414 | return new_plane.d; |
415 | } |
416 | |
417 | real_t Projection::get_z_near() const { |
418 | const real_t *matrix = (const real_t *)columns; |
419 | Plane new_plane = Plane(matrix[3] + matrix[2], |
420 | matrix[7] + matrix[6], |
421 | matrix[11] + matrix[10], |
422 | -matrix[15] - matrix[14]); |
423 | |
424 | new_plane.normalize(); |
425 | return new_plane.d; |
426 | } |
427 | |
428 | Vector2 Projection::get_viewport_half_extents() const { |
429 | const real_t *matrix = (const real_t *)columns; |
430 | ///////--- Near Plane ---/////// |
431 | Plane near_plane = Plane(matrix[3] + matrix[2], |
432 | matrix[7] + matrix[6], |
433 | matrix[11] + matrix[10], |
434 | -matrix[15] - matrix[14]); |
435 | near_plane.normalize(); |
436 | |
437 | ///////--- Right Plane ---/////// |
438 | Plane right_plane = Plane(matrix[3] - matrix[0], |
439 | matrix[7] - matrix[4], |
440 | matrix[11] - matrix[8], |
441 | -matrix[15] + matrix[12]); |
442 | right_plane.normalize(); |
443 | |
444 | Plane top_plane = Plane(matrix[3] - matrix[1], |
445 | matrix[7] - matrix[5], |
446 | matrix[11] - matrix[9], |
447 | -matrix[15] + matrix[13]); |
448 | top_plane.normalize(); |
449 | |
450 | Vector3 res; |
451 | near_plane.intersect_3(right_plane, top_plane, &res); |
452 | |
453 | return Vector2(res.x, res.y); |
454 | } |
455 | |
456 | Vector2 Projection::get_far_plane_half_extents() const { |
457 | const real_t *matrix = (const real_t *)columns; |
458 | ///////--- Far Plane ---/////// |
459 | Plane far_plane = Plane(matrix[3] - matrix[2], |
460 | matrix[7] - matrix[6], |
461 | matrix[11] - matrix[10], |
462 | -matrix[15] + matrix[14]); |
463 | far_plane.normalize(); |
464 | |
465 | ///////--- Right Plane ---/////// |
466 | Plane right_plane = Plane(matrix[3] - matrix[0], |
467 | matrix[7] - matrix[4], |
468 | matrix[11] - matrix[8], |
469 | -matrix[15] + matrix[12]); |
470 | right_plane.normalize(); |
471 | |
472 | Plane top_plane = Plane(matrix[3] - matrix[1], |
473 | matrix[7] - matrix[5], |
474 | matrix[11] - matrix[9], |
475 | -matrix[15] + matrix[13]); |
476 | top_plane.normalize(); |
477 | |
478 | Vector3 res; |
479 | far_plane.intersect_3(right_plane, top_plane, &res); |
480 | |
481 | return Vector2(res.x, res.y); |
482 | } |
483 | |
484 | bool Projection::get_endpoints(const Transform3D &p_transform, Vector3 *p_8points) const { |
485 | Vector<Plane> planes = get_projection_planes(Transform3D()); |
486 | const Planes intersections[8][3] = { |
487 | { PLANE_FAR, PLANE_LEFT, PLANE_TOP }, |
488 | { PLANE_FAR, PLANE_LEFT, PLANE_BOTTOM }, |
489 | { PLANE_FAR, PLANE_RIGHT, PLANE_TOP }, |
490 | { PLANE_FAR, PLANE_RIGHT, PLANE_BOTTOM }, |
491 | { PLANE_NEAR, PLANE_LEFT, PLANE_TOP }, |
492 | { PLANE_NEAR, PLANE_LEFT, PLANE_BOTTOM }, |
493 | { PLANE_NEAR, PLANE_RIGHT, PLANE_TOP }, |
494 | { PLANE_NEAR, PLANE_RIGHT, PLANE_BOTTOM }, |
495 | }; |
496 | |
497 | for (int i = 0; i < 8; i++) { |
498 | Vector3 point; |
499 | Plane a = planes[intersections[i][0]]; |
500 | Plane b = planes[intersections[i][1]]; |
501 | Plane c = planes[intersections[i][2]]; |
502 | bool res = a.intersect_3(b, c, &point); |
503 | ERR_FAIL_COND_V(!res, false); |
504 | p_8points[i] = p_transform.xform(point); |
505 | } |
506 | |
507 | return true; |
508 | } |
509 | |
510 | Vector<Plane> Projection::get_projection_planes(const Transform3D &p_transform) const { |
511 | /** Fast Plane Extraction from combined modelview/projection matrices. |
512 | * References: |
513 | * https://web.archive.org/web/20011221205252/https://www.markmorley.com/opengl/frustumculling.html |
514 | * https://web.archive.org/web/20061020020112/https://www2.ravensoft.com/users/ggribb/plane%20extraction.pdf |
515 | */ |
516 | |
517 | Vector<Plane> planes; |
518 | planes.resize(6); |
519 | |
520 | const real_t *matrix = (const real_t *)columns; |
521 | |
522 | Plane new_plane; |
523 | |
524 | ///////--- Near Plane ---/////// |
525 | new_plane = Plane(matrix[3] + matrix[2], |
526 | matrix[7] + matrix[6], |
527 | matrix[11] + matrix[10], |
528 | matrix[15] + matrix[14]); |
529 | |
530 | new_plane.normal = -new_plane.normal; |
531 | new_plane.normalize(); |
532 | |
533 | planes.write[0] = p_transform.xform(new_plane); |
534 | |
535 | ///////--- Far Plane ---/////// |
536 | new_plane = Plane(matrix[3] - matrix[2], |
537 | matrix[7] - matrix[6], |
538 | matrix[11] - matrix[10], |
539 | matrix[15] - matrix[14]); |
540 | |
541 | new_plane.normal = -new_plane.normal; |
542 | new_plane.normalize(); |
543 | |
544 | planes.write[1] = p_transform.xform(new_plane); |
545 | |
546 | ///////--- Left Plane ---/////// |
547 | new_plane = Plane(matrix[3] + matrix[0], |
548 | matrix[7] + matrix[4], |
549 | matrix[11] + matrix[8], |
550 | matrix[15] + matrix[12]); |
551 | |
552 | new_plane.normal = -new_plane.normal; |
553 | new_plane.normalize(); |
554 | |
555 | planes.write[2] = p_transform.xform(new_plane); |
556 | |
557 | ///////--- Top Plane ---/////// |
558 | new_plane = Plane(matrix[3] - matrix[1], |
559 | matrix[7] - matrix[5], |
560 | matrix[11] - matrix[9], |
561 | matrix[15] - matrix[13]); |
562 | |
563 | new_plane.normal = -new_plane.normal; |
564 | new_plane.normalize(); |
565 | |
566 | planes.write[3] = p_transform.xform(new_plane); |
567 | |
568 | ///////--- Right Plane ---/////// |
569 | new_plane = Plane(matrix[3] - matrix[0], |
570 | matrix[7] - matrix[4], |
571 | matrix[11] - matrix[8], |
572 | matrix[15] - matrix[12]); |
573 | |
574 | new_plane.normal = -new_plane.normal; |
575 | new_plane.normalize(); |
576 | |
577 | planes.write[4] = p_transform.xform(new_plane); |
578 | |
579 | ///////--- Bottom Plane ---/////// |
580 | new_plane = Plane(matrix[3] + matrix[1], |
581 | matrix[7] + matrix[5], |
582 | matrix[11] + matrix[9], |
583 | matrix[15] + matrix[13]); |
584 | |
585 | new_plane.normal = -new_plane.normal; |
586 | new_plane.normalize(); |
587 | |
588 | planes.write[5] = p_transform.xform(new_plane); |
589 | |
590 | return planes; |
591 | } |
592 | |
593 | Projection Projection::inverse() const { |
594 | Projection cm = *this; |
595 | cm.invert(); |
596 | return cm; |
597 | } |
598 | |
599 | void Projection::invert() { |
600 | int i, j, k; |
601 | int pvt_i[4], pvt_j[4]; /* Locations of pivot matrix */ |
602 | real_t pvt_val; /* Value of current pivot element */ |
603 | real_t hold; /* Temporary storage */ |
604 | real_t determinant = 1.0f; |
605 | for (k = 0; k < 4; k++) { |
606 | /** Locate k'th pivot element **/ |
607 | pvt_val = columns[k][k]; /** Initialize for search **/ |
608 | pvt_i[k] = k; |
609 | pvt_j[k] = k; |
610 | for (i = k; i < 4; i++) { |
611 | for (j = k; j < 4; j++) { |
612 | if (Math::abs(columns[i][j]) > Math::abs(pvt_val)) { |
613 | pvt_i[k] = i; |
614 | pvt_j[k] = j; |
615 | pvt_val = columns[i][j]; |
616 | } |
617 | } |
618 | } |
619 | |
620 | /** Product of pivots, gives determinant when finished **/ |
621 | determinant *= pvt_val; |
622 | if (Math::is_zero_approx(determinant)) { |
623 | return; /** Matrix is singular (zero determinant). **/ |
624 | } |
625 | |
626 | /** "Interchange" rows (with sign change stuff) **/ |
627 | i = pvt_i[k]; |
628 | if (i != k) { /** If rows are different **/ |
629 | for (j = 0; j < 4; j++) { |
630 | hold = -columns[k][j]; |
631 | columns[k][j] = columns[i][j]; |
632 | columns[i][j] = hold; |
633 | } |
634 | } |
635 | |
636 | /** "Interchange" columns **/ |
637 | j = pvt_j[k]; |
638 | if (j != k) { /** If columns are different **/ |
639 | for (i = 0; i < 4; i++) { |
640 | hold = -columns[i][k]; |
641 | columns[i][k] = columns[i][j]; |
642 | columns[i][j] = hold; |
643 | } |
644 | } |
645 | |
646 | /** Divide column by minus pivot value **/ |
647 | for (i = 0; i < 4; i++) { |
648 | if (i != k) { |
649 | columns[i][k] /= (-pvt_val); |
650 | } |
651 | } |
652 | |
653 | /** Reduce the matrix **/ |
654 | for (i = 0; i < 4; i++) { |
655 | hold = columns[i][k]; |
656 | for (j = 0; j < 4; j++) { |
657 | if (i != k && j != k) { |
658 | columns[i][j] += hold * columns[k][j]; |
659 | } |
660 | } |
661 | } |
662 | |
663 | /** Divide row by pivot **/ |
664 | for (j = 0; j < 4; j++) { |
665 | if (j != k) { |
666 | columns[k][j] /= pvt_val; |
667 | } |
668 | } |
669 | |
670 | /** Replace pivot by reciprocal (at last we can touch it). **/ |
671 | columns[k][k] = 1.0 / pvt_val; |
672 | } |
673 | |
674 | /* That was most of the work, one final pass of row/column interchange */ |
675 | /* to finish */ |
676 | for (k = 4 - 2; k >= 0; k--) { /* Don't need to work with 1 by 1 corner*/ |
677 | i = pvt_j[k]; /* Rows to swap correspond to pivot COLUMN */ |
678 | if (i != k) { /* If rows are different */ |
679 | for (j = 0; j < 4; j++) { |
680 | hold = columns[k][j]; |
681 | columns[k][j] = -columns[i][j]; |
682 | columns[i][j] = hold; |
683 | } |
684 | } |
685 | |
686 | j = pvt_i[k]; /* Columns to swap correspond to pivot ROW */ |
687 | if (j != k) { /* If columns are different */ |
688 | for (i = 0; i < 4; i++) { |
689 | hold = columns[i][k]; |
690 | columns[i][k] = -columns[i][j]; |
691 | columns[i][j] = hold; |
692 | } |
693 | } |
694 | } |
695 | } |
696 | |
697 | void Projection::flip_y() { |
698 | for (int i = 0; i < 4; i++) { |
699 | columns[1][i] = -columns[1][i]; |
700 | } |
701 | } |
702 | |
703 | Projection::Projection() { |
704 | set_identity(); |
705 | } |
706 | |
707 | Projection Projection::operator*(const Projection &p_matrix) const { |
708 | Projection new_matrix; |
709 | |
710 | for (int j = 0; j < 4; j++) { |
711 | for (int i = 0; i < 4; i++) { |
712 | real_t ab = 0; |
713 | for (int k = 0; k < 4; k++) { |
714 | ab += columns[k][i] * p_matrix.columns[j][k]; |
715 | } |
716 | new_matrix.columns[j][i] = ab; |
717 | } |
718 | } |
719 | |
720 | return new_matrix; |
721 | } |
722 | |
723 | void Projection::set_depth_correction(bool p_flip_y) { |
724 | real_t *m = &columns[0][0]; |
725 | |
726 | m[0] = 1; |
727 | m[1] = 0.0; |
728 | m[2] = 0.0; |
729 | m[3] = 0.0; |
730 | m[4] = 0.0; |
731 | m[5] = p_flip_y ? -1 : 1; |
732 | m[6] = 0.0; |
733 | m[7] = 0.0; |
734 | m[8] = 0.0; |
735 | m[9] = 0.0; |
736 | m[10] = 0.5; |
737 | m[11] = 0.0; |
738 | m[12] = 0.0; |
739 | m[13] = 0.0; |
740 | m[14] = 0.5; |
741 | m[15] = 1.0; |
742 | } |
743 | |
744 | void Projection::set_light_bias() { |
745 | real_t *m = &columns[0][0]; |
746 | |
747 | m[0] = 0.5; |
748 | m[1] = 0.0; |
749 | m[2] = 0.0; |
750 | m[3] = 0.0; |
751 | m[4] = 0.0; |
752 | m[5] = 0.5; |
753 | m[6] = 0.0; |
754 | m[7] = 0.0; |
755 | m[8] = 0.0; |
756 | m[9] = 0.0; |
757 | m[10] = 0.5; |
758 | m[11] = 0.0; |
759 | m[12] = 0.5; |
760 | m[13] = 0.5; |
761 | m[14] = 0.5; |
762 | m[15] = 1.0; |
763 | } |
764 | |
765 | void Projection::set_light_atlas_rect(const Rect2 &p_rect) { |
766 | real_t *m = &columns[0][0]; |
767 | |
768 | m[0] = p_rect.size.width; |
769 | m[1] = 0.0; |
770 | m[2] = 0.0; |
771 | m[3] = 0.0; |
772 | m[4] = 0.0; |
773 | m[5] = p_rect.size.height; |
774 | m[6] = 0.0; |
775 | m[7] = 0.0; |
776 | m[8] = 0.0; |
777 | m[9] = 0.0; |
778 | m[10] = 1.0; |
779 | m[11] = 0.0; |
780 | m[12] = p_rect.position.x; |
781 | m[13] = p_rect.position.y; |
782 | m[14] = 0.0; |
783 | m[15] = 1.0; |
784 | } |
785 | |
786 | Projection::operator String() const { |
787 | String str; |
788 | for (int i = 0; i < 4; i++) { |
789 | for (int j = 0; j < 4; j++) { |
790 | str += String((j > 0) ? ", " : "\n" ) + rtos(columns[i][j]); |
791 | } |
792 | } |
793 | |
794 | return str; |
795 | } |
796 | |
797 | real_t Projection::get_aspect() const { |
798 | Vector2 vp_he = get_viewport_half_extents(); |
799 | return vp_he.x / vp_he.y; |
800 | } |
801 | |
802 | int Projection::get_pixels_per_meter(int p_for_pixel_width) const { |
803 | Vector3 result = xform(Vector3(1, 0, -1)); |
804 | |
805 | return int((result.x * 0.5 + 0.5) * p_for_pixel_width); |
806 | } |
807 | |
808 | bool Projection::is_orthogonal() const { |
809 | return columns[3][3] == 1.0; |
810 | } |
811 | |
812 | real_t Projection::get_fov() const { |
813 | const real_t *matrix = (const real_t *)columns; |
814 | |
815 | Plane right_plane = Plane(matrix[3] - matrix[0], |
816 | matrix[7] - matrix[4], |
817 | matrix[11] - matrix[8], |
818 | -matrix[15] + matrix[12]); |
819 | right_plane.normalize(); |
820 | |
821 | if ((matrix[8] == 0) && (matrix[9] == 0)) { |
822 | return Math::rad_to_deg(Math::acos(Math::abs(right_plane.normal.x))) * 2.0; |
823 | } else { |
824 | // our frustum is asymmetrical need to calculate the left planes angle separately.. |
825 | Plane left_plane = Plane(matrix[3] + matrix[0], |
826 | matrix[7] + matrix[4], |
827 | matrix[11] + matrix[8], |
828 | matrix[15] + matrix[12]); |
829 | left_plane.normalize(); |
830 | |
831 | return Math::rad_to_deg(Math::acos(Math::abs(left_plane.normal.x))) + Math::rad_to_deg(Math::acos(Math::abs(right_plane.normal.x))); |
832 | } |
833 | } |
834 | |
835 | float Projection::get_lod_multiplier() const { |
836 | if (is_orthogonal()) { |
837 | return get_viewport_half_extents().x; |
838 | } else { |
839 | float zn = get_z_near(); |
840 | float width = get_viewport_half_extents().x * 2.0; |
841 | return 1.0 / (zn / width); |
842 | } |
843 | |
844 | // Usage is lod_size / (lod_distance * multiplier) < threshold |
845 | } |
846 | |
847 | void Projection::make_scale(const Vector3 &p_scale) { |
848 | set_identity(); |
849 | columns[0][0] = p_scale.x; |
850 | columns[1][1] = p_scale.y; |
851 | columns[2][2] = p_scale.z; |
852 | } |
853 | |
854 | void Projection::scale_translate_to_fit(const AABB &p_aabb) { |
855 | Vector3 min = p_aabb.position; |
856 | Vector3 max = p_aabb.position + p_aabb.size; |
857 | |
858 | columns[0][0] = 2 / (max.x - min.x); |
859 | columns[1][0] = 0; |
860 | columns[2][0] = 0; |
861 | columns[3][0] = -(max.x + min.x) / (max.x - min.x); |
862 | |
863 | columns[0][1] = 0; |
864 | columns[1][1] = 2 / (max.y - min.y); |
865 | columns[2][1] = 0; |
866 | columns[3][1] = -(max.y + min.y) / (max.y - min.y); |
867 | |
868 | columns[0][2] = 0; |
869 | columns[1][2] = 0; |
870 | columns[2][2] = 2 / (max.z - min.z); |
871 | columns[3][2] = -(max.z + min.z) / (max.z - min.z); |
872 | |
873 | columns[0][3] = 0; |
874 | columns[1][3] = 0; |
875 | columns[2][3] = 0; |
876 | columns[3][3] = 1; |
877 | } |
878 | |
879 | void Projection::add_jitter_offset(const Vector2 &p_offset) { |
880 | columns[3][0] += p_offset.x; |
881 | columns[3][1] += p_offset.y; |
882 | } |
883 | |
884 | Projection::operator Transform3D() const { |
885 | Transform3D tr; |
886 | const real_t *m = &columns[0][0]; |
887 | |
888 | tr.basis.rows[0][0] = m[0]; |
889 | tr.basis.rows[1][0] = m[1]; |
890 | tr.basis.rows[2][0] = m[2]; |
891 | |
892 | tr.basis.rows[0][1] = m[4]; |
893 | tr.basis.rows[1][1] = m[5]; |
894 | tr.basis.rows[2][1] = m[6]; |
895 | |
896 | tr.basis.rows[0][2] = m[8]; |
897 | tr.basis.rows[1][2] = m[9]; |
898 | tr.basis.rows[2][2] = m[10]; |
899 | |
900 | tr.origin.x = m[12]; |
901 | tr.origin.y = m[13]; |
902 | tr.origin.z = m[14]; |
903 | |
904 | return tr; |
905 | } |
906 | |
907 | Projection::Projection(const Vector4 &p_x, const Vector4 &p_y, const Vector4 &p_z, const Vector4 &p_w) { |
908 | columns[0] = p_x; |
909 | columns[1] = p_y; |
910 | columns[2] = p_z; |
911 | columns[3] = p_w; |
912 | } |
913 | |
914 | Projection::Projection(const Transform3D &p_transform) { |
915 | const Transform3D &tr = p_transform; |
916 | real_t *m = &columns[0][0]; |
917 | |
918 | m[0] = tr.basis.rows[0][0]; |
919 | m[1] = tr.basis.rows[1][0]; |
920 | m[2] = tr.basis.rows[2][0]; |
921 | m[3] = 0.0; |
922 | m[4] = tr.basis.rows[0][1]; |
923 | m[5] = tr.basis.rows[1][1]; |
924 | m[6] = tr.basis.rows[2][1]; |
925 | m[7] = 0.0; |
926 | m[8] = tr.basis.rows[0][2]; |
927 | m[9] = tr.basis.rows[1][2]; |
928 | m[10] = tr.basis.rows[2][2]; |
929 | m[11] = 0.0; |
930 | m[12] = tr.origin.x; |
931 | m[13] = tr.origin.y; |
932 | m[14] = tr.origin.z; |
933 | m[15] = 1.0; |
934 | } |
935 | |
936 | Projection::~Projection() { |
937 | } |
938 | |