Path: blob/master/tests/core/math/test_projection.cpp
23450 views
/**************************************************************************/1/* test_projection.cpp */2/**************************************************************************/3/* This file is part of: */4/* GODOT ENGINE */5/* https://godotengine.org */6/**************************************************************************/7/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */8/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */9/* */10/* Permission is hereby granted, free of charge, to any person obtaining */11/* a copy of this software and associated documentation files (the */12/* "Software"), to deal in the Software without restriction, including */13/* without limitation the rights to use, copy, modify, merge, publish, */14/* distribute, sublicense, and/or sell copies of the Software, and to */15/* permit persons to whom the Software is furnished to do so, subject to */16/* the following conditions: */17/* */18/* The above copyright notice and this permission notice shall be */19/* included in all copies or substantial portions of the Software. */20/* */21/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */22/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */23/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */24/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */25/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */26/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */27/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */28/**************************************************************************/2930#include "tests/test_macros.h"3132TEST_FORCE_LINK(test_projection)3334#include "core/math/aabb.h"35#include "core/math/plane.h"36#include "core/math/projection.h"37#include "core/math/rect2.h"38#include "core/math/transform_3d.h"3940namespace TestProjection {4142TEST_CASE("[Projection] Construction") {43Projection default_proj;4445CHECK(default_proj[0].is_equal_approx(Vector4(1, 0, 0, 0)));46CHECK(default_proj[1].is_equal_approx(Vector4(0, 1, 0, 0)));47CHECK(default_proj[2].is_equal_approx(Vector4(0, 0, 1, 0)));48CHECK(default_proj[3].is_equal_approx(Vector4(0, 0, 0, 1)));4950Projection from_vec4(51Vector4(1, 2, 3, 4),52Vector4(5, 6, 7, 8),53Vector4(9, 10, 11, 12),54Vector4(13, 14, 15, 16));5556CHECK(from_vec4[0].is_equal_approx(Vector4(1, 2, 3, 4)));57CHECK(from_vec4[1].is_equal_approx(Vector4(5, 6, 7, 8)));58CHECK(from_vec4[2].is_equal_approx(Vector4(9, 10, 11, 12)));59CHECK(from_vec4[3].is_equal_approx(Vector4(13, 14, 15, 16)));6061Transform3D transform(62Basis(63Vector3(1, 0, 0),64Vector3(0, 2, 0),65Vector3(0, 0, 3)),66Vector3(4, 5, 6));6768Projection from_transform(transform);6970CHECK(from_transform[0].is_equal_approx(Vector4(1, 0, 0, 0)));71CHECK(from_transform[1].is_equal_approx(Vector4(0, 2, 0, 0)));72CHECK(from_transform[2].is_equal_approx(Vector4(0, 0, 3, 0)));73CHECK(from_transform[3].is_equal_approx(Vector4(4, 5, 6, 1)));74}7576TEST_CASE("[Projection] set_zero()") {77Projection proj;78proj.set_zero();7980for (int i = 0; i < 4; i++) {81for (int j = 0; j < 4; j++) {82CHECK(proj.columns[i][j] == 0);83}84}85}8687TEST_CASE("[Projection] set_identity()") {88Projection proj;89proj.set_identity();9091for (int i = 0; i < 4; i++) {92for (int j = 0; j < 4; j++) {93CHECK(proj.columns[i][j] == (i == j ? 1 : 0));94}95}96}9798TEST_CASE("[Projection] determinant()") {99Projection proj(100Vector4(1, 5, 9, 13),101Vector4(2, 6, 11, 15),102Vector4(4, 7, 11, 15),103Vector4(4, 8, 12, 16));104105CHECK(proj.determinant() == -12);106}107108TEST_CASE("[Projection] Inverse and invert") {109SUBCASE("[Projection] Arbitrary projection matrix inversion") {110Projection proj(111Vector4(1, 5, 9, 13),112Vector4(2, 6, 11, 15),113Vector4(4, 7, 11, 15),114Vector4(4, 8, 12, 16));115116Projection inverse_truth(117Vector4(-4.0 / 12, 0, 1, -8.0 / 12),118Vector4(8.0 / 12, -1, -1, 16.0 / 12),119Vector4(-20.0 / 12, 2, -1, 5.0 / 12),120Vector4(1, -1, 1, -0.75));121122Projection inverse = proj.inverse();123CHECK(inverse[0].is_equal_approx(inverse_truth[0]));124CHECK(inverse[1].is_equal_approx(inverse_truth[1]));125CHECK(inverse[2].is_equal_approx(inverse_truth[2]));126CHECK(inverse[3].is_equal_approx(inverse_truth[3]));127128proj.invert();129CHECK(proj[0].is_equal_approx(inverse_truth[0]));130CHECK(proj[1].is_equal_approx(inverse_truth[1]));131CHECK(proj[2].is_equal_approx(inverse_truth[2]));132CHECK(proj[3].is_equal_approx(inverse_truth[3]));133}134135SUBCASE("[Projection] Orthogonal projection matrix inversion") {136Projection p = Projection::create_orthogonal(-125.0f, 125.0f, -125.0f, 125.0f, 0.01f, 25.0f);137p = p.inverse() * p;138139CHECK(p[0].is_equal_approx(Vector4(1, 0, 0, 0)));140CHECK(p[1].is_equal_approx(Vector4(0, 1, 0, 0)));141CHECK(p[2].is_equal_approx(Vector4(0, 0, 1, 0)));142CHECK(p[3].is_equal_approx(Vector4(0, 0, 0, 1)));143}144145SUBCASE("[Projection] Perspective projection matrix inversion") {146Projection p = Projection::create_perspective(90.0f, 1.77777f, 0.05f, 4000.0f);147p = p.inverse() * p;148149CHECK(p[0].is_equal_approx(Vector4(1, 0, 0, 0)));150CHECK(p[1].is_equal_approx(Vector4(0, 1, 0, 0)));151CHECK(p[2].is_equal_approx(Vector4(0, 0, 1, 0)));152CHECK(p[3].is_equal_approx(Vector4(0, 0, 0, 1)));153}154}155156TEST_CASE("[Projection] Matrix product") {157Projection proj1(158Vector4(1, 5, 9, 13),159Vector4(2, 6, 11, 15),160Vector4(4, 7, 11, 15),161Vector4(4, 8, 12, 16));162163Projection proj2(164Vector4(0, 1, 2, 3),165Vector4(10, 11, 12, 13),166Vector4(20, 21, 22, 23),167Vector4(30, 31, 32, 33));168169Projection prod = proj1 * proj2;170171CHECK(prod[0].is_equal_approx(Vector4(22, 44, 69, 93)));172CHECK(prod[1].is_equal_approx(Vector4(132, 304, 499, 683)));173CHECK(prod[2].is_equal_approx(Vector4(242, 564, 929, 1273)));174CHECK(prod[3].is_equal_approx(Vector4(352, 824, 1359, 1863)));175}176177TEST_CASE("[Projection] Vector transformation") {178Projection proj(179Vector4(1, 5, 9, 13),180Vector4(2, 6, 11, 15),181Vector4(4, 7, 11, 15),182Vector4(4, 8, 12, 16));183184Vector4 vec4(1, 2, 3, 4);185CHECK(proj.xform(vec4).is_equal_approx(Vector4(33, 70, 112, 152)));186CHECK(proj.xform_inv(vec4).is_equal_approx(Vector4(90, 107, 111, 120)));187188Vector3 vec3(1, 2, 3);189CHECK(proj.xform(vec3).is_equal_approx(Vector3(21, 46, 76) / 104));190}191192TEST_CASE("[Projection] Plane transformation") {193Projection proj(194Vector4(1, 5, 9, 13),195Vector4(2, 6, 11, 15),196Vector4(4, 7, 11, 15),197Vector4(4, 8, 12, 16));198199Plane plane(1, 2, 3, 4);200CHECK(proj.xform4(plane).is_equal_approx(Plane(33, 70, 112, 152)));201}202203TEST_CASE("[Projection] Values access") {204Projection proj(205Vector4(00, 01, 02, 03),206Vector4(10, 11, 12, 13),207Vector4(20, 21, 22, 23),208Vector4(30, 31, 32, 33));209210CHECK(proj[0] == Vector4(00, 01, 02, 03));211CHECK(proj[1] == Vector4(10, 11, 12, 13));212CHECK(proj[2] == Vector4(20, 21, 22, 23));213CHECK(proj[3] == Vector4(30, 31, 32, 33));214}215216TEST_CASE("[Projection] flip_y() and flipped_y()") {217Projection proj(218Vector4(00, 01, 02, 03),219Vector4(10, 11, 12, 13),220Vector4(20, 21, 22, 23),221Vector4(30, 31, 32, 33));222223Projection flipped = proj.flipped_y();224225CHECK(flipped[0] == proj[0]);226CHECK(flipped[1] == -proj[1]);227CHECK(flipped[2] == proj[2]);228CHECK(flipped[3] == proj[3]);229230proj.flip_y();231232CHECK(proj[0] == flipped[0]);233CHECK(proj[1] == flipped[1]);234CHECK(proj[2] == flipped[2]);235CHECK(proj[3] == flipped[3]);236}237238TEST_CASE("[Projection] Jitter offset") {239Projection proj(240Vector4(00, 01, 02, 03),241Vector4(10, 11, 12, 13),242Vector4(20, 21, 22, 23),243Vector4(30, 31, 32, 33));244245Projection offsetted = proj.jitter_offseted(Vector2(1, 2));246247CHECK(offsetted[0] == proj[0]);248CHECK(offsetted[1] == proj[1]);249CHECK(offsetted[2] == proj[2]);250CHECK(offsetted[3] == proj[3] + Vector4(1, 2, 0, 0));251252proj.add_jitter_offset(Vector2(1, 2));253254CHECK(proj[0] == offsetted[0]);255CHECK(proj[1] == offsetted[1]);256CHECK(proj[2] == offsetted[2]);257CHECK(proj[3] == offsetted[3]);258}259260TEST_CASE("[Projection] Adjust znear") {261Projection persp = Projection::create_perspective(90, 0.5, 1, 50, false);262Projection adjusted = persp.perspective_znear_adjusted(2);263264CHECK(adjusted[0] == persp[0]);265CHECK(adjusted[1] == persp[1]);266CHECK(adjusted[2].is_equal_approx(Vector4(persp[2][0], persp[2][1], -1.083333, persp[2][3])));267CHECK(adjusted[3].is_equal_approx(Vector4(persp[3][0], persp[3][1], -4.166666, persp[3][3])));268269persp.adjust_perspective_znear(2);270271CHECK(persp[0] == adjusted[0]);272CHECK(persp[1] == adjusted[1]);273CHECK(persp[2] == adjusted[2]);274CHECK(persp[3] == adjusted[3]);275}276277TEST_CASE("[Projection] Set light bias") {278Projection proj;279proj.set_light_bias();280281CHECK(proj[0] == Vector4(0.5, 0, 0, 0));282CHECK(proj[1] == Vector4(0, 0.5, 0, 0));283CHECK(proj[2] == Vector4(0, 0, 0.5, 0));284CHECK(proj[3] == Vector4(0.5, 0.5, 0.5, 1));285}286287TEST_CASE("[Projection] Depth correction") {288Projection corrected = Projection::create_depth_correction(true);289290CHECK(corrected[0] == Vector4(1, 0, 0, 0));291CHECK(corrected[1] == Vector4(0, -1, 0, 0));292CHECK(corrected[2] == Vector4(0, 0, -0.5, 0));293CHECK(corrected[3] == Vector4(0, 0, 0.5, 1));294295Projection proj;296proj.set_depth_correction(true, true, true);297298CHECK(proj[0] == corrected[0]);299CHECK(proj[1] == corrected[1]);300CHECK(proj[2] == corrected[2]);301CHECK(proj[3] == corrected[3]);302303proj.set_depth_correction(false, true, true);304305CHECK(proj[0] == Vector4(1, 0, 0, 0));306CHECK(proj[1] == Vector4(0, 1, 0, 0));307CHECK(proj[2] == Vector4(0, 0, -0.5, 0));308CHECK(proj[3] == Vector4(0, 0, 0.5, 1));309310proj.set_depth_correction(false, false, true);311312CHECK(proj[0] == Vector4(1, 0, 0, 0));313CHECK(proj[1] == Vector4(0, 1, 0, 0));314CHECK(proj[2] == Vector4(0, 0, 0.5, 0));315CHECK(proj[3] == Vector4(0, 0, 0.5, 1));316317proj.set_depth_correction(false, false, false);318319CHECK(proj[0] == Vector4(1, 0, 0, 0));320CHECK(proj[1] == Vector4(0, 1, 0, 0));321CHECK(proj[2] == Vector4(0, 0, 1, 0));322CHECK(proj[3] == Vector4(0, 0, 0, 1));323324proj.set_depth_correction(true, true, false);325326CHECK(proj[0] == Vector4(1, 0, 0, 0));327CHECK(proj[1] == Vector4(0, -1, 0, 0));328CHECK(proj[2] == Vector4(0, 0, -1, 0));329CHECK(proj[3] == Vector4(0, 0, 0, 1));330}331332TEST_CASE("[Projection] Light atlas rect") {333Projection rect = Projection::create_light_atlas_rect(Rect2(1, 2, 30, 40));334335CHECK(rect[0] == Vector4(30, 0, 0, 0));336CHECK(rect[1] == Vector4(0, 40, 0, 0));337CHECK(rect[2] == Vector4(0, 0, 1, 0));338CHECK(rect[3] == Vector4(1, 2, 0, 1));339340Projection proj;341proj.set_light_atlas_rect(Rect2(1, 2, 30, 40));342343CHECK(proj[0] == rect[0]);344CHECK(proj[1] == rect[1]);345CHECK(proj[2] == rect[2]);346CHECK(proj[3] == rect[3]);347}348349TEST_CASE("[Projection] Make scale") {350Projection proj;351proj.make_scale(Vector3(2, 3, 4));352353CHECK(proj[0] == Vector4(2, 0, 0, 0));354CHECK(proj[1] == Vector4(0, 3, 0, 0));355CHECK(proj[2] == Vector4(0, 0, 4, 0));356CHECK(proj[3] == Vector4(0, 0, 0, 1));357}358359TEST_CASE("[Projection] Scale translate to fit aabb") {360Projection fit = Projection::create_fit_aabb(AABB(Vector3(), Vector3(0.1, 0.2, 0.4)));361362CHECK(fit[0] == Vector4(20, 0, 0, 0));363CHECK(fit[1] == Vector4(0, 10, 0, 0));364CHECK(fit[2] == Vector4(0, 0, 5, 0));365CHECK(fit[3] == Vector4(-1, -1, -1, 1));366367Projection proj;368proj.scale_translate_to_fit(AABB(Vector3(), Vector3(0.1, 0.2, 0.4)));369370CHECK(proj[0] == fit[0]);371CHECK(proj[1] == fit[1]);372CHECK(proj[2] == fit[2]);373CHECK(proj[3] == fit[3]);374}375376TEST_CASE("[Projection] Perspective") {377Projection persp = Projection::create_perspective(90, 0.5, 5, 15, false);378379CHECK(persp[0].is_equal_approx(Vector4(2, 0, 0, 0)));380CHECK(persp[1].is_equal_approx(Vector4(0, 1, 0, 0)));381CHECK(persp[2].is_equal_approx(Vector4(0, 0, -2, -1)));382CHECK(persp[3].is_equal_approx(Vector4(0, 0, -15, 0)));383384Projection proj;385proj.set_perspective(90, 0.5, 5, 15, false);386387CHECK(proj[0] == persp[0]);388CHECK(proj[1] == persp[1]);389CHECK(proj[2] == persp[2]);390CHECK(proj[3] == persp[3]);391}392393TEST_CASE("[Projection] Frustum") {394Projection frustum = Projection::create_frustum(15, 20, 10, 12, 5, 15);395396CHECK(frustum[0].is_equal_approx(Vector4(2, 0, 0, 0)));397CHECK(frustum[1].is_equal_approx(Vector4(0, 5, 0, 0)));398CHECK(frustum[2].is_equal_approx(Vector4(7, 11, -2, -1)));399CHECK(frustum[3].is_equal_approx(Vector4(0, 0, -15, 0)));400401Projection proj;402proj.set_frustum(15, 20, 10, 12, 5, 15);403404CHECK(proj[0] == frustum[0]);405CHECK(proj[1] == frustum[1]);406CHECK(proj[2] == frustum[2]);407CHECK(proj[3] == frustum[3]);408}409410TEST_CASE("[Projection] Ortho") {411Projection ortho = Projection::create_orthogonal(15, 20, 10, 12, 5, 15);412413CHECK(ortho[0].is_equal_approx(Vector4(0.4, 0, 0, 0)));414CHECK(ortho[1].is_equal_approx(Vector4(0, 1, 0, 0)));415CHECK(ortho[2].is_equal_approx(Vector4(0, 0, -0.2, 0)));416CHECK(ortho[3].is_equal_approx(Vector4(-7, -11, -2, 1)));417418Projection proj;419proj.set_orthogonal(15, 20, 10, 12, 5, 15);420421CHECK(proj[0] == ortho[0]);422CHECK(proj[1] == ortho[1]);423CHECK(proj[2] == ortho[2]);424CHECK(proj[3] == ortho[3]);425}426427TEST_CASE("[Projection] get_fovy()") {428double fov = Projection::get_fovy(90, 0.5);429CHECK(fov == doctest::Approx(53.1301));430}431432TEST_CASE("[Projection] Perspective values extraction") {433Projection persp = Projection::create_perspective(90, 0.5, 1, 50, true);434435double znear = persp.get_z_near();436double zfar = persp.get_z_far();437double aspect = persp.get_aspect();438double fov = persp.get_fov();439440CHECK(znear == doctest::Approx(1));441CHECK(zfar == doctest::Approx(50));442CHECK(aspect == doctest::Approx(0.5));443CHECK(fov == doctest::Approx(90));444445persp.set_perspective(38, 1.3, 0.2, 8, false);446447znear = persp.get_z_near();448zfar = persp.get_z_far();449aspect = persp.get_aspect();450fov = persp.get_fov();451452CHECK(znear == doctest::Approx(0.2));453CHECK(zfar == doctest::Approx(8));454CHECK(aspect == doctest::Approx(1.3));455CHECK(fov == doctest::Approx(Projection::get_fovy(38, 1.3)));456457persp.set_perspective(47, 2.5, 0.9, 14, true);458459znear = persp.get_z_near();460zfar = persp.get_z_far();461aspect = persp.get_aspect();462fov = persp.get_fov();463464CHECK(znear == doctest::Approx(0.9));465CHECK(zfar == doctest::Approx(14));466CHECK(aspect == doctest::Approx(2.5));467CHECK(fov == doctest::Approx(47));468}469470TEST_CASE("[Projection] Frustum values extraction") {471Projection frustum = Projection::create_frustum_aspect(1.0, 4.0 / 3.0, Vector2(0.5, -0.25), 0.5, 50, true);472473double znear = frustum.get_z_near();474double zfar = frustum.get_z_far();475double aspect = frustum.get_aspect();476double fov = frustum.get_fov();477478CHECK(znear == doctest::Approx(0.5));479CHECK(zfar == doctest::Approx(50));480CHECK(aspect == doctest::Approx(4.0 / 3.0));481CHECK(fov == doctest::Approx(Math::rad_to_deg(Math::atan(2.0))));482483frustum.set_frustum(2.0, 1.5, Vector2(-0.5, 2), 2, 12, false);484485znear = frustum.get_z_near();486zfar = frustum.get_z_far();487aspect = frustum.get_aspect();488fov = frustum.get_fov();489490CHECK(znear == doctest::Approx(2));491CHECK(zfar == doctest::Approx(12));492CHECK(aspect == doctest::Approx(1.5));493CHECK(fov == doctest::Approx(Math::rad_to_deg(Math::atan(1.0) + Math::atan(0.5))));494}495496TEST_CASE("[Projection] Orthographic values extraction") {497Projection ortho = Projection::create_orthogonal(-2, 3, -0.5, 1.5, 1.2, 15);498499double znear = ortho.get_z_near();500double zfar = ortho.get_z_far();501double aspect = ortho.get_aspect();502503CHECK(znear == doctest::Approx(1.2));504CHECK(zfar == doctest::Approx(15));505CHECK(aspect == doctest::Approx(2.5));506507ortho.set_orthogonal(-7, 2, 2.5, 5.5, 0.5, 6);508509znear = ortho.get_z_near();510zfar = ortho.get_z_far();511aspect = ortho.get_aspect();512513CHECK(znear == doctest::Approx(0.5));514CHECK(zfar == doctest::Approx(6));515CHECK(aspect == doctest::Approx(3));516}517518TEST_CASE("[Projection] Orthographic check") {519Projection persp = Projection::create_perspective(90, 0.5, 1, 50, false);520Projection ortho = Projection::create_orthogonal(15, 20, 10, 12, 5, 15);521522CHECK(!persp.is_orthogonal());523CHECK(ortho.is_orthogonal());524}525526TEST_CASE("[Projection] Planes extraction") {527Projection persp = Projection::create_perspective(90, 1, 1, 40, false);528Vector<Plane> planes = persp.get_projection_planes(Transform3D());529530CHECK(planes[Projection::PLANE_NEAR].normalized().is_equal_approx(Plane(0, 0, 1, -1)));531CHECK(planes[Projection::PLANE_FAR].normalized().is_equal_approx(Plane(0, 0, -1, 40)));532CHECK(planes[Projection::PLANE_LEFT].normalized().is_equal_approx(Plane(-0.707107, 0, 0.707107, 0)));533CHECK(planes[Projection::PLANE_TOP].normalized().is_equal_approx(Plane(0, 0.707107, 0.707107, 0)));534CHECK(planes[Projection::PLANE_RIGHT].normalized().is_equal_approx(Plane(0.707107, 0, 0.707107, 0)));535CHECK(planes[Projection::PLANE_BOTTOM].normalized().is_equal_approx(Plane(0, -0.707107, 0.707107, 0)));536537Plane plane_array[6]{538persp.get_projection_plane(Projection::PLANE_NEAR),539persp.get_projection_plane(Projection::PLANE_FAR),540persp.get_projection_plane(Projection::PLANE_LEFT),541persp.get_projection_plane(Projection::PLANE_TOP),542persp.get_projection_plane(Projection::PLANE_RIGHT),543persp.get_projection_plane(Projection::PLANE_BOTTOM)544};545546CHECK(plane_array[Projection::PLANE_NEAR].normalized().is_equal_approx(planes[Projection::PLANE_NEAR].normalized()));547CHECK(plane_array[Projection::PLANE_FAR].normalized().is_equal_approx(planes[Projection::PLANE_FAR].normalized()));548CHECK(plane_array[Projection::PLANE_LEFT].normalized().is_equal_approx(planes[Projection::PLANE_LEFT].normalized()));549CHECK(plane_array[Projection::PLANE_TOP].normalized().is_equal_approx(planes[Projection::PLANE_TOP].normalized()));550CHECK(plane_array[Projection::PLANE_RIGHT].normalized().is_equal_approx(planes[Projection::PLANE_RIGHT].normalized()));551CHECK(plane_array[Projection::PLANE_BOTTOM].normalized().is_equal_approx(planes[Projection::PLANE_BOTTOM].normalized()));552}553554TEST_CASE("[Projection] Perspective Half extents") {555constexpr real_t sqrt3 = 1.7320508;556Projection persp = Projection::create_perspective(90, 1, 1, 40, false);557Vector2 ne = persp.get_viewport_half_extents();558Vector2 fe = persp.get_far_plane_half_extents();559560CHECK(ne.is_equal_approx(Vector2(1, 1) * 1));561CHECK(fe.is_equal_approx(Vector2(1, 1) * 40));562563persp.set_perspective(120, sqrt3, 0.8, 10, true);564ne = persp.get_viewport_half_extents();565fe = persp.get_far_plane_half_extents();566567CHECK(ne.is_equal_approx(Vector2(sqrt3, 1.0) * 0.8));568CHECK(fe.is_equal_approx(Vector2(sqrt3, 1.0) * 10));569570persp.set_perspective(60, 1.2, 0.5, 15, false);571ne = persp.get_viewport_half_extents();572fe = persp.get_far_plane_half_extents();573574CHECK(ne.is_equal_approx(Vector2(sqrt3 / 3 * 1.2, sqrt3 / 3) * 0.5));575CHECK(fe.is_equal_approx(Vector2(sqrt3 / 3 * 1.2, sqrt3 / 3) * 15));576}577578TEST_CASE("[Projection] Orthographic Half extents") {579Projection ortho = Projection::create_orthogonal(-3, 3, -1.5, 1.5, 1.2, 15);580Vector2 ne = ortho.get_viewport_half_extents();581Vector2 fe = ortho.get_far_plane_half_extents();582583CHECK(ne.is_equal_approx(Vector2(3, 1.5)));584CHECK(fe.is_equal_approx(Vector2(3, 1.5)));585586ortho.set_orthogonal(-7, 7, -2.5, 2.5, 0.5, 6);587ne = ortho.get_viewport_half_extents();588fe = ortho.get_far_plane_half_extents();589590CHECK(ne.is_equal_approx(Vector2(7, 2.5)));591CHECK(fe.is_equal_approx(Vector2(7, 2.5)));592}593594TEST_CASE("[Projection] Endpoints") {595constexpr real_t sqrt3 = 1.7320508;596Projection persp = Projection::create_perspective(90, 1, 1, 40, false);597Vector3 ep[8];598persp.get_endpoints(Transform3D(), ep);599600CHECK(ep[0].is_equal_approx(Vector3(-1, 1, -1) * 40));601CHECK(ep[1].is_equal_approx(Vector3(-1, -1, -1) * 40));602CHECK(ep[2].is_equal_approx(Vector3(1, 1, -1) * 40));603CHECK(ep[3].is_equal_approx(Vector3(1, -1, -1) * 40));604CHECK(ep[4].is_equal_approx(Vector3(-1, 1, -1) * 1));605CHECK(ep[5].is_equal_approx(Vector3(-1, -1, -1) * 1));606CHECK(ep[6].is_equal_approx(Vector3(1, 1, -1) * 1));607CHECK(ep[7].is_equal_approx(Vector3(1, -1, -1) * 1));608609persp.set_perspective(120, sqrt3, 0.8, 10, true);610persp.get_endpoints(Transform3D(), ep);611612CHECK(ep[0].is_equal_approx(Vector3(-sqrt3, 1, -1) * 10));613CHECK(ep[1].is_equal_approx(Vector3(-sqrt3, -1, -1) * 10));614CHECK(ep[2].is_equal_approx(Vector3(sqrt3, 1, -1) * 10));615CHECK(ep[3].is_equal_approx(Vector3(sqrt3, -1, -1) * 10));616CHECK(ep[4].is_equal_approx(Vector3(-sqrt3, 1, -1) * 0.8));617CHECK(ep[5].is_equal_approx(Vector3(-sqrt3, -1, -1) * 0.8));618CHECK(ep[6].is_equal_approx(Vector3(sqrt3, 1, -1) * 0.8));619CHECK(ep[7].is_equal_approx(Vector3(sqrt3, -1, -1) * 0.8));620621persp.set_perspective(60, 1.2, 0.5, 15, false);622persp.get_endpoints(Transform3D(), ep);623624CHECK(ep[0].is_equal_approx(Vector3(-sqrt3 / 3 * 1.2, sqrt3 / 3, -1) * 15));625CHECK(ep[1].is_equal_approx(Vector3(-sqrt3 / 3 * 1.2, -sqrt3 / 3, -1) * 15));626CHECK(ep[2].is_equal_approx(Vector3(sqrt3 / 3 * 1.2, sqrt3 / 3, -1) * 15));627CHECK(ep[3].is_equal_approx(Vector3(sqrt3 / 3 * 1.2, -sqrt3 / 3, -1) * 15));628CHECK(ep[4].is_equal_approx(Vector3(-sqrt3 / 3 * 1.2, sqrt3 / 3, -1) * 0.5));629CHECK(ep[5].is_equal_approx(Vector3(-sqrt3 / 3 * 1.2, -sqrt3 / 3, -1) * 0.5));630CHECK(ep[6].is_equal_approx(Vector3(sqrt3 / 3 * 1.2, sqrt3 / 3, -1) * 0.5));631CHECK(ep[7].is_equal_approx(Vector3(sqrt3 / 3 * 1.2, -sqrt3 / 3, -1) * 0.5));632}633634TEST_CASE("[Projection] LOD multiplier") {635constexpr real_t sqrt3 = 1.7320508;636Projection proj;637real_t multiplier;638639proj.set_perspective(60, 1, 1, 40, false);640multiplier = proj.get_lod_multiplier();641CHECK(multiplier == doctest::Approx(2 * sqrt3 / 3));642643proj.set_perspective(120, 1.5, 0.5, 20, false);644multiplier = proj.get_lod_multiplier();645CHECK(multiplier == doctest::Approx(3 * sqrt3));646647proj.set_orthogonal(15, 20, 10, 12, 5, 15);648multiplier = proj.get_lod_multiplier();649CHECK(multiplier == doctest::Approx(5));650651proj.set_orthogonal(-5, 15, -8, 10, 1.5, 10);652multiplier = proj.get_lod_multiplier();653CHECK(multiplier == doctest::Approx(20));654655proj.set_frustum(1.0, 4.0 / 3.0, Vector2(0.5, -0.25), 0.5, 50, false);656multiplier = proj.get_lod_multiplier();657CHECK(multiplier == doctest::Approx(8.0 / 3.0));658659proj.set_frustum(2.0, 1.2, Vector2(-0.1, 0.8), 1, 10, true);660multiplier = proj.get_lod_multiplier();661CHECK(multiplier == doctest::Approx(2));662}663664TEST_CASE("[Projection] Pixels per meter") {665constexpr real_t sqrt3 = 1.7320508;666Projection proj;667int ppm;668669proj.set_perspective(60, 1, 1, 40, false);670ppm = proj.get_pixels_per_meter(1024);671CHECK(ppm == int(1536.0f / sqrt3));672673proj.set_perspective(120, 1.5, 0.5, 20, false);674ppm = proj.get_pixels_per_meter(1200);675CHECK(ppm == int(800.0f / sqrt3));676677proj.set_orthogonal(15, 20, 10, 12, 5, 15);678ppm = proj.get_pixels_per_meter(500);679CHECK(ppm == 100);680681proj.set_orthogonal(-5, 15, -8, 10, 1.5, 10);682ppm = proj.get_pixels_per_meter(640);683CHECK(ppm == 32);684685proj.set_frustum(1.0, 4.0 / 3.0, Vector2(0.5, -0.25), 0.5, 50, false);686ppm = proj.get_pixels_per_meter(2048);687CHECK(ppm == 1536);688689proj.set_frustum(2.0, 1.2, Vector2(-0.1, 0.8), 1, 10, true);690ppm = proj.get_pixels_per_meter(800);691CHECK(ppm == 400);692}693694} // namespace TestProjection695696697