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 { @@ -39,6 +41,9 @@ class CCamera { public: + // Represents camera viewport or frustum side in 3D space. + using Quad = std::array; + CCamera(); ~CCamera(); @@ -61,13 +66,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; + // Returns a quad of view in camera space at given distance from camera. + void GetViewQuad(float dist, Quad& quad) 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,51 +129,52 @@ 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); - - pts[0].X = -x; - pts[0].Y = -y; - pts[0].Z = dist; - pts[1].X = x; - pts[1].Y = -y; - pts[1].Z = dist; - pts[2].X = x; - pts[2].Y = y; - pts[2].Z = dist; - pts[3].X = -x; - pts[3].Y = y; - pts[3].Z = dist; +void CCamera::GetViewQuad(float dist, Quad& quad) const +{ + const float y = dist * tanf(m_FOV * 0.5f); + const float x = y * GetAspectRatio(); + + quad[0].X = -x; + quad[0].Y = -y; + quad[0].Z = dist; + quad[1].X = x; + quad[1].Y = -y; + quad[1].Z = dist; + quad[2].X = x; + quad[2].Y = y; + quad[2].Z = dist; + quad[3].X = -x; + quad[3].Y = y; + quad[3].Z = dist; } 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 = static_cast(px) / g_Renderer.GetWidth(); + const float dy = 1.0f - static_cast(py) / g_Renderer.GetHeight(); + + Quad points; + GetViewQuad(m_FarPlane, points); + + // Transform from camera space to world space. + for (CVector3D& point : points) + point = m_Orientation.Transform(point); + + // 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); - // transform to world space - CVector3D wPts[4]; - for (int i = 0; i < 4; i++) - wPts[i] = m_Orientation.Transform(cPts[i]); - - // get world space position of mouse point - float dx = (float)px / (float)g_Renderer.GetWidth(); - float dz = 1 - (float)py / (float)g_Renderer.GetHeight(); - - CVector3D vdx = wPts[1] - wPts[0]; - CVector3D vdz = wPts[3] - wPts[0]; - CVector3D pt = wPts[0] + (vdx * dx) + (vdz * dz); - - // 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,19 +353,17 @@ 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]; + Quad nearPoints; + Quad farPoints; - GetCameraPlanePoints(m_NearPlane, nearPoints); - GetCameraPlanePoints(m_FarPlane, farPoints); + GetViewQuad(m_NearPlane, nearPoints); + GetViewQuad(m_FarPlane, farPoints); for(int i = 0; i < 4; i++) { nearPoints[i] = m_Orientation.Transform(nearPoints[i]); 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)); + + CCamera::Quad quad; + + // Zero distance point is the origin of all camera rays, + // so all plane points should be stay there. + camera.GetViewQuad(0.0f, quad); + for (const CVector3D& point : quad) + TS_ASSERT_EQUALS(point, CVector3D(0.0f, 0.0f, 0.0f)); + + // Points lying on the far plane. + CCamera::Quad expectedFarQuad = { + 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.GetViewQuad(camera.GetFarPlane(), quad); + TS_ASSERT_EQUALS(quad, expectedFarQuad); + } };