Index: source/graphics/Camera.h =================================================================== --- source/graphics/Camera.h +++ source/graphics/Camera.h @@ -27,6 +27,8 @@ #include "maths/BoundingBoxAligned.h" #include "maths/Matrix3D.h" +#include + // view port struct SViewPort { @@ -61,13 +63,14 @@ void SetViewPort(const SViewPort& viewport); const SViewPort& GetViewPort() const { return m_ViewPort; } + float GetAspectRatio() const; float GetNearPlane() const { return m_NearPlane; } float GetFarPlane() const { return m_FarPlane; } float GetFOV() const { return m_FOV; } // Returns four points in camera space at given distance from camera - void GetCameraPlanePoints(float dist, CVector3D pts[4]) const; + void GetCameraPlanePoints(float dist, std::array& pts) const; // Build a ray passing through the screen coordinate (px, py) and the camera ///////////////////////////////////////////////////////////////////////////////////////// Index: source/graphics/Camera.cpp =================================================================== --- source/graphics/Camera.cpp +++ source/graphics/Camera.cpp @@ -52,15 +52,12 @@ m_FarPlane = farp; m_FOV = fov; - const float aspect = static_cast(m_ViewPort.m_Width) / static_cast(m_ViewPort.m_Height); - m_ProjMat.SetPerspective(m_FOV, aspect, m_NearPlane, m_FarPlane); + m_ProjMat.SetPerspective(m_FOV, GetAspectRatio(), m_NearPlane, m_FarPlane); } void CCamera::SetPerspectiveProjectionTile(int tiles, int tile_x, int tile_y) { - const float aspect = static_cast(m_ViewPort.m_Width) / static_cast(m_ViewPort.m_Height); - - m_ProjMat.SetPerspectiveTile(m_FOV, aspect, m_NearPlane, m_FarPlane, tiles, tile_x, tile_y); + m_ProjMat.SetPerspectiveTile(m_FOV, GetAspectRatio(), m_NearPlane, m_FarPlane, tiles, tile_x, tile_y); } // Updates the frustum planes. Should be called @@ -132,14 +129,15 @@ m_ViewPort.m_Height = viewport.m_Height; } +float CCamera::GetAspectRatio() const +{ + return static_cast(m_ViewPort.m_Width) / static_cast(m_ViewPort.m_Height); +} -/////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// GetCameraPlanePoints: return four points in camera space at given distance from camera -void CCamera::GetCameraPlanePoints(float dist, CVector3D pts[4]) const -{ - float aspect = float(m_ViewPort.m_Width)/float(m_ViewPort.m_Height); - float x = dist*aspect*tanf(m_FOV*0.5f); - float y = dist*tanf(m_FOV*0.5f); +void CCamera::GetCameraPlanePoints(float dist, std::array& pts) const +{ + const float y = dist * tanf(m_FOV * 0.5f); + const float x = y * GetAspectRatio(); pts[0].X = -x; pts[0].Y = -y; @@ -157,26 +155,26 @@ void CCamera::BuildCameraRay(int px, int py, CVector3D& origin, CVector3D& dir) const { - CVector3D cPts[4]; - GetCameraPlanePoints(m_FarPlane, cPts); + // Coordinates relative to the camera plane. + const float dx = (float)px / (float)g_Renderer.GetWidth(); + const float dy = 1 - (float)py / (float)g_Renderer.GetHeight(); - // transform to world space - CVector3D wPts[4]; - for (int i = 0; i < 4; i++) - wPts[i] = m_Orientation.Transform(cPts[i]); + std::array points; + GetCameraPlanePoints(m_FarPlane, points); - // get world space position of mouse point - float dx = (float)px / (float)g_Renderer.GetWidth(); - float dz = 1 - (float)py / (float)g_Renderer.GetHeight(); + // Transform from camera space to world space. + for (int i = 0; i < 4; i++) + points[i] = m_Orientation.Transform(points[i]); - CVector3D vdx = wPts[1] - wPts[0]; - CVector3D vdz = wPts[3] - wPts[0]; - CVector3D pt = wPts[0] + (vdx * dx) + (vdz * dz); + // Get world space position of mouse point at the far clipping plane. + CVector3D basisX = points[1] - points[0]; + CVector3D basisY = points[3] - points[0]; + CVector3D targetPoint = points[0] + (basisX * dx) + (basisY * dy); - // copy origin origin = m_Orientation.GetTranslation(); - // build direction - dir = pt - origin; + + // Build direction for the camera origin to the target point. + dir = targetPoint - origin; dir.Normalize(); } @@ -355,16 +353,14 @@ m_Orientation._41 = 0.0f; m_Orientation._42 = 0.0f; m_Orientation._43 = 0.0f; m_Orientation._44 = 1.0f; } - -/////////////////////////////////////////////////////////////////////////////////// // Render the camera's frustum void CCamera::Render(int intermediates) const { #if CONFIG2_GLES #warning TODO: implement camera frustum for GLES #else - CVector3D nearPoints[4]; - CVector3D farPoints[4]; + std::array nearPoints; + std::array farPoints; GetCameraPlanePoints(m_NearPlane, nearPoints); GetCameraPlanePoints(m_FarPlane, farPoints); Index: source/graphics/tests/test_Camera.h =================================================================== --- source/graphics/tests/test_Camera.h +++ source/graphics/tests/test_Camera.h @@ -71,7 +71,7 @@ CVector3D(0.0f, 0.0f, 0.0f), CVector3D(0.0f, 0.0f, 1.0f), CVector3D(0.0f, 1.0f, 0.0f) - ); + ); CMatrix3D projection; projection.SetOrtho(-10.0f, 10.0f, -10.0f, 10.0f, -10.0f, 10.0f); camera.SetProjection(projection); @@ -123,4 +123,41 @@ std::fabs(p1.m_Norm.Y - p2.m_Norm.Y) < EPS && std::fabs(p1.m_Norm.Z - p2.m_Norm.Z) < EPS; } + + void test_persepctive_plane_points() + { + SViewPort viewPort; + viewPort.m_X = 0; + viewPort.m_Y = 0; + viewPort.m_Width = 512; + viewPort.m_Height = 512; + + CCamera camera; + camera.SetViewPort(viewPort); + camera.LookAlong( + CVector3D(0.0f, 0.0f, 0.0f), + CVector3D(0.0f, 0.0f, 1.0f), + CVector3D(0.0f, 1.0f, 0.0f) + ); + camera.m_Orientation.SetTranslation(CVector3D(1.0f, 2.0f, 3.0f)); + camera.SetPerspectiveProjection(1.0f, 101.0f, DEGTORAD(90.0f)); + + std::array points; + + // Zero distance point is the origin of all camera rays, + // so all plane points should be stay there. + camera.GetCameraPlanePoints(0.0f, points); + for (const CVector3D& point : points) + TS_ASSERT_EQUALS(point, CVector3D(0.0f, 0.0f, 0.0f)); + + // Points lying on the far plane. + std::array expectedFarPoints = { + CVector3D(-101.0f, -101.0f, 101.0f), + CVector3D(101.0f, -101.0f, 101.0f), + CVector3D(101.0f, 101.0f, 101.0f), + CVector3D(-101.0f, 101.0f, 101.0f) + }; + camera.GetCameraPlanePoints(camera.GetFarPlane(), points); + TS_ASSERT_EQUALS(points, expectedFarPoints); + } };