Skip to content

Commit 3d81685

Browse files
committed
Merge pull request #100209 from Flarkk/simplify_fix_projection
Simplify and fix `Projection`'s getter functions
2 parents c838fe2 + 1d416fc commit 3d81685

File tree

3 files changed

+234
-102
lines changed

3 files changed

+234
-102
lines changed

core/math/projection.cpp

Lines changed: 45 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -402,82 +402,35 @@ void Projection::set_frustum(real_t p_size, real_t p_aspect, Vector2 p_offset, r
402402
}
403403

404404
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.normalize();
412-
413-
return new_plane.d;
405+
// NOTE: This assumes z-facing near and far planes, i.e. that :
406+
// - the matrix is a projection across z-axis (i.e. is invertible and columns[0][1], [0][3], [1][0] and [1][3] == 0)
407+
// - near and far planes are z-facing (i.e. columns[0][2] and [1][2] == 0)
408+
return (columns[3][3] - columns[3][2]) / (columns[2][3] - columns[2][2]);
414409
}
415410

416411
real_t Projection::get_z_near() const {
417-
const real_t *matrix = (const real_t *)columns;
418-
Plane new_plane = Plane(matrix[3] + matrix[2],
419-
matrix[7] + matrix[6],
420-
matrix[11] + matrix[10],
421-
-matrix[15] - matrix[14]);
422-
423-
new_plane.normalize();
424-
return new_plane.d;
412+
// NOTE: This assumes z-facing near and far planes, i.e. that :
413+
// - the matrix is a projection across z-axis (i.e. is invertible and columns[0][1], [0][3], [1][0] and [1][3] == 0)
414+
// - near and far planes are z-facing (i.e. columns[0][2] and [1][2] == 0)
415+
return (columns[3][3] + columns[3][2]) / (columns[2][3] + columns[2][2]);
425416
}
426417

427418
Vector2 Projection::get_viewport_half_extents() const {
428-
const real_t *matrix = (const real_t *)columns;
429-
///////--- Near Plane ---///////
430-
Plane near_plane = Plane(matrix[3] + matrix[2],
431-
matrix[7] + matrix[6],
432-
matrix[11] + matrix[10],
433-
-matrix[15] - matrix[14]);
434-
near_plane.normalize();
435-
436-
///////--- Right Plane ---///////
437-
Plane right_plane = Plane(matrix[3] - matrix[0],
438-
matrix[7] - matrix[4],
439-
matrix[11] - matrix[8],
440-
-matrix[15] + matrix[12]);
441-
right_plane.normalize();
442-
443-
Plane top_plane = Plane(matrix[3] - matrix[1],
444-
matrix[7] - matrix[5],
445-
matrix[11] - matrix[9],
446-
-matrix[15] + matrix[13]);
447-
top_plane.normalize();
448-
449-
Vector3 res;
450-
near_plane.intersect_3(right_plane, top_plane, &res);
451-
452-
return Vector2(res.x, res.y);
419+
// NOTE: This assumes a symmetrical frustum, i.e. that :
420+
// - the matrix is a projection across z-axis (i.e. is invertible and columns[0][1], [0][3], [1][0] and [1][3] == 0)
421+
// - the projection plane is rectangular (i.e. columns[0][2] and [1][2] == 0 if columns[2][3] != 0)
422+
// - there is no offset / skew (i.e. columns[2][0] == columns[2][1] == 0)
423+
real_t w = -get_z_near() * columns[2][3] + columns[3][3];
424+
return Vector2(w / columns[0][0], w / columns[1][1]);
453425
}
454426

455427
Vector2 Projection::get_far_plane_half_extents() const {
456-
const real_t *matrix = (const real_t *)columns;
457-
///////--- Far Plane ---///////
458-
Plane far_plane = Plane(matrix[3] - matrix[2],
459-
matrix[7] - matrix[6],
460-
matrix[11] - matrix[10],
461-
-matrix[15] + matrix[14]);
462-
far_plane.normalize();
463-
464-
///////--- Right Plane ---///////
465-
Plane right_plane = Plane(matrix[3] - matrix[0],
466-
matrix[7] - matrix[4],
467-
matrix[11] - matrix[8],
468-
-matrix[15] + matrix[12]);
469-
right_plane.normalize();
470-
471-
Plane top_plane = Plane(matrix[3] - matrix[1],
472-
matrix[7] - matrix[5],
473-
matrix[11] - matrix[9],
474-
-matrix[15] + matrix[13]);
475-
top_plane.normalize();
476-
477-
Vector3 res;
478-
far_plane.intersect_3(right_plane, top_plane, &res);
479-
480-
return Vector2(res.x, res.y);
428+
// NOTE: This assumes a symmetrical frustum, i.e. that :
429+
// - the matrix is a projection across z-axis (i.e. is invertible and columns[0][1], [0][3], [1][0] and [1][3] == 0)
430+
// - the projection plane is rectangular (i.e. columns[0][2] and [1][2] == 0 if columns[2][3] != 0)
431+
// - there is no offset / skew (i.e. columns[2][0] == columns[2][1] == 0)
432+
real_t w = -get_z_far() * columns[2][3] + columns[3][3];
433+
return Vector2(w / columns[0][0], w / columns[1][1]);
481434
}
482435

483436
bool Projection::get_endpoints(const Transform3D &p_transform, Vector3 *p_8points) const {
@@ -919,53 +872,45 @@ Projection::operator String() const {
919872
}
920873

921874
real_t Projection::get_aspect() const {
922-
Vector2 vp_he = get_viewport_half_extents();
923-
return vp_he.x / vp_he.y;
875+
// NOTE: This assumes a rectangular projection plane, i.e. that :
876+
// - the matrix is a projection across z-axis (i.e. is invertible and columns[0][1], [0][3], [1][0] and [1][3] == 0)
877+
// - the projection plane is rectangular (i.e. columns[0][2] and [1][2] == 0 if columns[2][3] != 0)
878+
return columns[1][1] / columns[0][0];
924879
}
925880

926881
int Projection::get_pixels_per_meter(int p_for_pixel_width) const {
927-
Vector3 result = xform(Vector3(1, 0, -1));
928-
929-
return int((result.x * 0.5 + 0.5) * p_for_pixel_width);
882+
// NOTE: This assumes a rectangular projection plane, i.e. that :
883+
// - the matrix is a projection across z-axis (i.e. is invertible and columns[0][1], [0][3], [1][0] and [1][3] == 0)
884+
// - the projection plane is rectangular (i.e. columns[0][2] and [1][2] == 0 if columns[2][3] != 0)
885+
real_t width = 2 * (-get_z_near() * columns[2][3] + columns[3][3]) / columns[0][0];
886+
return p_for_pixel_width / width; // Note : return type should be real_t (kept as int for compatibility for now).
930887
}
931888

932889
bool Projection::is_orthogonal() const {
933-
return columns[3][3] == 1.0;
890+
// NOTE: This assumes that the matrix is a projection across z-axis
891+
// i.e. is invertible and columns[0][1], [0][3], [1][0] and [1][3] == 0
892+
return columns[2][3] == 0.0;
934893
}
935894

936895
real_t Projection::get_fov() const {
937-
const real_t *matrix = (const real_t *)columns;
938-
939-
Plane right_plane = Plane(matrix[3] - matrix[0],
940-
matrix[7] - matrix[4],
941-
matrix[11] - matrix[8],
942-
-matrix[15] + matrix[12]);
943-
right_plane.normalize();
944-
945-
if ((matrix[8] == 0) && (matrix[9] == 0)) {
946-
return Math::rad_to_deg(Math::acos(Math::abs(right_plane.normal.x))) * 2.0;
896+
// NOTE: This assumes a rectangular projection plane, i.e. that :
897+
// - the matrix is a projection across z-axis (i.e. is invertible and columns[0][1], [0][3], [1][0] and [1][3] == 0)
898+
// - the projection plane is rectangular (i.e. columns[0][2] and [1][2] == 0 if columns[2][3] != 0)
899+
if (columns[2][0] == 0) {
900+
return Math::rad_to_deg(2 * Math::atan2(1, columns[0][0]));
947901
} else {
948-
// our frustum is asymmetrical need to calculate the left planes angle separately..
949-
Plane left_plane = Plane(matrix[3] + matrix[0],
950-
matrix[7] + matrix[4],
951-
matrix[11] + matrix[8],
952-
matrix[15] + matrix[12]);
953-
left_plane.normalize();
954-
955-
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)));
902+
// The frustum is asymmetrical so we need to calculate the left and right angles separately.
903+
real_t right = Math::atan2(columns[2][0] + 1, columns[0][0]);
904+
real_t left = Math::atan2(columns[2][0] - 1, columns[0][0]);
905+
return Math::rad_to_deg(right - left);
956906
}
957907
}
958908

959909
real_t Projection::get_lod_multiplier() const {
960-
if (is_orthogonal()) {
961-
return get_viewport_half_extents().x;
962-
} else {
963-
const real_t zn = get_z_near();
964-
const real_t width = get_viewport_half_extents().x * 2.0f;
965-
return 1.0f / (zn / width);
966-
}
967-
968-
// Usage is lod_size / (lod_distance * multiplier) < threshold
910+
// NOTE: This assumes a rectangular projection plane, i.e. that :
911+
// - the matrix is a projection across z-axis (i.e. is invertible and columns[0][1], [0][3], [1][0] and [1][3] == 0)
912+
// - the projection plane is rectangular (i.e. columns[0][2] and [1][2] == 0 if columns[2][3] != 0)
913+
return 2 / columns[0][0];
969914
}
970915

971916
void Projection::make_scale(const Vector3 &p_scale) {

doc/classes/Projection.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@
207207
<return type="int" />
208208
<param index="0" name="for_pixel_width" type="int" />
209209
<description>
210-
Returns the number of pixels with the given pixel width displayed per meter, after this [Projection] is applied.
210+
Returns [param for_pixel_width] divided by the viewport's width measured in meters on the near plane, after this [Projection] is applied.
211211
</description>
212212
</method>
213213
<method name="get_projection_plane" qualifiers="const">

0 commit comments

Comments
 (0)