Index: ps/trunk/source/graphics/Camera.cpp =================================================================== --- ps/trunk/source/graphics/Camera.cpp (revision 22032) +++ ps/trunk/source/graphics/Camera.cpp (revision 22033) @@ -1,430 +1,424 @@ -/* Copyright (C) 2017 Wildfire Games. +/* Copyright (C) 2019 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * 0 A.D. is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with 0 A.D. If not, see . */ /* * CCamera holds a view and a projection matrix. It also has a frustum * which can be used to cull objects for rendering. */ #include "precompiled.h" #include "Camera.h" #include "graphics/HFTracer.h" #include "graphics/Terrain.h" #include "lib/ogl.h" #include "maths/MathUtil.h" #include "maths/Vector4D.h" #include "ps/Game.h" #include "ps/World.h" #include "renderer/Renderer.h" #include "renderer/WaterManager.h" CCamera::CCamera() { // set viewport to something anything should handle, but should be initialised // to window size before use m_ViewPort.m_X = 0; m_ViewPort.m_Y = 0; m_ViewPort.m_Width = 800; m_ViewPort.m_Height = 600; } -CCamera::~CCamera() -{ -} +CCamera::~CCamera() = default; void CCamera::SetProjection(float nearp, float farp, float fov) { m_NearPlane = nearp; 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); } void CCamera::SetProjectionTile(int tiles, int tile_x, int tile_y) { const float aspect = static_cast(m_ViewPort.m_Width) / static_cast(m_ViewPort.m_Height); - const float f = 1.f / tanf(m_FOV / 2.f); - m_ProjMat._11 = tiles * f / aspect; - m_ProjMat._22 = tiles * f; - m_ProjMat._13 = -(1 - tiles + 2 * tile_x); - m_ProjMat._23 = -(1 - tiles + 2 * tile_y); + m_ProjMat.SetPerspectiveTile(m_FOV, aspect, m_NearPlane, m_FarPlane, tiles, tile_x, tile_y); } -//Updates the frustum planes. Should be called -//everytime the view or projection matrices are -//altered. +// Updates the frustum planes. Should be called +// everytime the view or projection matrices are +// altered. void CCamera::UpdateFrustum(const CBoundingBoxAligned& scissor) { CMatrix3D MatFinal; CMatrix3D MatView; m_Orientation.GetInverse(MatView); MatFinal = m_ProjMat * MatView; m_ViewFrustum.SetNumPlanes(6); // get the RIGHT plane m_ViewFrustum.m_aPlanes[0].m_Norm.X = scissor[1].X*MatFinal._41 - MatFinal._11; m_ViewFrustum.m_aPlanes[0].m_Norm.Y = scissor[1].X*MatFinal._42 - MatFinal._12; m_ViewFrustum.m_aPlanes[0].m_Norm.Z = scissor[1].X*MatFinal._43 - MatFinal._13; m_ViewFrustum.m_aPlanes[0].m_Dist = scissor[1].X*MatFinal._44 - MatFinal._14; // get the LEFT plane m_ViewFrustum.m_aPlanes[1].m_Norm.X = -scissor[0].X*MatFinal._41 + MatFinal._11; m_ViewFrustum.m_aPlanes[1].m_Norm.Y = -scissor[0].X*MatFinal._42 + MatFinal._12; m_ViewFrustum.m_aPlanes[1].m_Norm.Z = -scissor[0].X*MatFinal._43 + MatFinal._13; m_ViewFrustum.m_aPlanes[1].m_Dist = -scissor[0].X*MatFinal._44 + MatFinal._14; // get the BOTTOM plane m_ViewFrustum.m_aPlanes[2].m_Norm.X = -scissor[0].Y*MatFinal._41 + MatFinal._21; m_ViewFrustum.m_aPlanes[2].m_Norm.Y = -scissor[0].Y*MatFinal._42 + MatFinal._22; m_ViewFrustum.m_aPlanes[2].m_Norm.Z = -scissor[0].Y*MatFinal._43 + MatFinal._23; m_ViewFrustum.m_aPlanes[2].m_Dist = -scissor[0].Y*MatFinal._44 + MatFinal._24; // get the TOP plane m_ViewFrustum.m_aPlanes[3].m_Norm.X = scissor[1].Y*MatFinal._41 - MatFinal._21; m_ViewFrustum.m_aPlanes[3].m_Norm.Y = scissor[1].Y*MatFinal._42 - MatFinal._22; m_ViewFrustum.m_aPlanes[3].m_Norm.Z = scissor[1].Y*MatFinal._43 - MatFinal._23; m_ViewFrustum.m_aPlanes[3].m_Dist = scissor[1].Y*MatFinal._44 - MatFinal._24; // get the FAR plane m_ViewFrustum.m_aPlanes[4].m_Norm.X = scissor[1].Z*MatFinal._41 - MatFinal._31; m_ViewFrustum.m_aPlanes[4].m_Norm.Y = scissor[1].Z*MatFinal._42 - MatFinal._32; m_ViewFrustum.m_aPlanes[4].m_Norm.Z = scissor[1].Z*MatFinal._43 - MatFinal._33; m_ViewFrustum.m_aPlanes[4].m_Dist = scissor[1].Z*MatFinal._44 - MatFinal._34; // get the NEAR plane m_ViewFrustum.m_aPlanes[5].m_Norm.X = -scissor[0].Z*MatFinal._41 + MatFinal._31; m_ViewFrustum.m_aPlanes[5].m_Norm.Y = -scissor[0].Z*MatFinal._42 + MatFinal._32; m_ViewFrustum.m_aPlanes[5].m_Norm.Z = -scissor[0].Z*MatFinal._43 + MatFinal._33; m_ViewFrustum.m_aPlanes[5].m_Dist = -scissor[0].Z*MatFinal._44 + MatFinal._34; for (size_t i = 0; i < 6; ++i) m_ViewFrustum.m_aPlanes[i].Normalize(); } void CCamera::ClipFrustum(const CPlane& clipPlane) { CPlane normClipPlane = clipPlane; normClipPlane.Normalize(); m_ViewFrustum.AddPlane(normClipPlane); } void CCamera::SetViewPort(const SViewPort& viewport) { m_ViewPort.m_X = viewport.m_X; m_ViewPort.m_Y = viewport.m_Y; m_ViewPort.m_Width = viewport.m_Width; m_ViewPort.m_Height = 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::BuildCameraRay(int px, int py, CVector3D& origin, CVector3D& dir) const { CVector3D cPts[4]; GetCameraPlanePoints(m_FarPlane, cPts); // 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; dir.Normalize(); } void CCamera::GetScreenCoordinates(const CVector3D& world, float& x, float& y) const { CMatrix3D transform = m_ProjMat * m_Orientation.GetInverse(); CVector4D screenspace = transform.Transform(CVector4D(world.X, world.Y, world.Z, 1.0f)); x = screenspace.X / screenspace.W; y = screenspace.Y / screenspace.W; x = (x + 1) * 0.5f * g_Renderer.GetWidth(); y = (1 - y) * 0.5f * g_Renderer.GetHeight(); } CVector3D CCamera::GetWorldCoordinates(int px, int py, bool aboveWater) const { CHFTracer tracer(g_Game->GetWorld()->GetTerrain()); int x, z; CVector3D origin, dir, delta, terrainPoint, waterPoint; BuildCameraRay(px, py, origin, dir); bool gotTerrain = tracer.RayIntersect(origin, dir, x, z, terrainPoint); if (!aboveWater) { if (gotTerrain) return terrainPoint; // Off the edge of the world? // Work out where it /would/ hit, if the map were extended out to infinity with average height. return GetWorldCoordinates(px, py, 50.0f); } CPlane plane; plane.Set(CVector3D(0.f, 1.f, 0.f), // upwards normal CVector3D(0.f, g_Renderer.GetWaterManager()->m_WaterHeight, 0.f)); // passes through water plane bool gotWater = plane.FindRayIntersection( origin, dir, &waterPoint ); // Clamp the water intersection to within the map's bounds, so that // we'll always return a valid position on the map ssize_t mapSize = g_Game->GetWorld()->GetTerrain()->GetVerticesPerSide(); if (gotWater) { waterPoint.X = clamp(waterPoint.X, 0.f, (float)((mapSize-1)*TERRAIN_TILE_SIZE)); waterPoint.Z = clamp(waterPoint.Z, 0.f, (float)((mapSize-1)*TERRAIN_TILE_SIZE)); } if (gotTerrain) { if (gotWater) { // Intersecting both heightmap and water plane; choose the closest of those if ((origin - terrainPoint).LengthSquared() < (origin - waterPoint).LengthSquared()) return terrainPoint; else return waterPoint; } else { // Intersecting heightmap but parallel to water plane return terrainPoint; } } else { if (gotWater) { // Only intersecting water plane return waterPoint; } else { // Not intersecting terrain or water; just return 0,0,0. return CVector3D(0.f, 0.f, 0.f); } } } CVector3D CCamera::GetWorldCoordinates(int px, int py, float h) const { CPlane plane; plane.Set(CVector3D(0.f, 1.f, 0.f), CVector3D(0.f, h, 0.f)); // upwards normal, passes through h CVector3D origin, dir, delta, currentTarget; BuildCameraRay(px, py, origin, dir); if (plane.FindRayIntersection(origin, dir, ¤tTarget)) return currentTarget; // No intersection with the infinite plane - nothing sensible can be returned, // so just choose an arbitrary point on the plane return CVector3D(0.f, h, 0.f); } CVector3D CCamera::GetFocus() const { // Basically the same as GetWorldCoordinates CHFTracer tracer(g_Game->GetWorld()->GetTerrain()); int x, z; CVector3D origin, dir, delta, terrainPoint, waterPoint; origin = m_Orientation.GetTranslation(); dir = m_Orientation.GetIn(); bool gotTerrain = tracer.RayIntersect(origin, dir, x, z, terrainPoint); CPlane plane; plane.Set(CVector3D(0.f, 1.f, 0.f), // upwards normal CVector3D(0.f, g_Renderer.GetWaterManager()->m_WaterHeight, 0.f)); // passes through water plane bool gotWater = plane.FindRayIntersection( origin, dir, &waterPoint ); // Clamp the water intersection to within the map's bounds, so that // we'll always return a valid position on the map ssize_t mapSize = g_Game->GetWorld()->GetTerrain()->GetVerticesPerSide(); if (gotWater) { waterPoint.X = clamp(waterPoint.X, 0.f, (float)((mapSize-1)*TERRAIN_TILE_SIZE)); waterPoint.Z = clamp(waterPoint.Z, 0.f, (float)((mapSize-1)*TERRAIN_TILE_SIZE)); } if (gotTerrain) { if (gotWater) { // Intersecting both heightmap and water plane; choose the closest of those if ((origin - terrainPoint).LengthSquared() < (origin - waterPoint).LengthSquared()) return terrainPoint; else return waterPoint; } else { // Intersecting heightmap but parallel to water plane return terrainPoint; } } else { if (gotWater) { // Only intersecting water plane return waterPoint; } else { // Not intersecting terrain or water; just return 0,0,0. return CVector3D(0.f, 0.f, 0.f); } } } void CCamera::LookAt(const CVector3D& camera, const CVector3D& target, const CVector3D& up) { CVector3D delta = target - camera; LookAlong(camera, delta, up); } void CCamera::LookAlong(const CVector3D& camera, CVector3D orientation, CVector3D up) { orientation.Normalize(); up.Normalize(); CVector3D s = orientation.Cross(up); m_Orientation._11 = -s.X; m_Orientation._12 = up.X; m_Orientation._13 = orientation.X; m_Orientation._14 = camera.X; m_Orientation._21 = -s.Y; m_Orientation._22 = up.Y; m_Orientation._23 = orientation.Y; m_Orientation._24 = camera.Y; m_Orientation._31 = -s.Z; m_Orientation._32 = up.Z; m_Orientation._33 = orientation.Z; m_Orientation._34 = camera.Z; 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]; GetCameraPlanePoints(m_NearPlane, nearPoints); GetCameraPlanePoints(m_FarPlane, farPoints); for(int i = 0; i < 4; i++) { nearPoints[i] = m_Orientation.Transform(nearPoints[i]); farPoints[i] = m_Orientation.Transform(farPoints[i]); } // near plane glBegin(GL_POLYGON); glVertex3fv(&nearPoints[0].X); glVertex3fv(&nearPoints[1].X); glVertex3fv(&nearPoints[2].X); glVertex3fv(&nearPoints[3].X); glEnd(); // far plane glBegin(GL_POLYGON); glVertex3fv(&farPoints[0].X); glVertex3fv(&farPoints[1].X); glVertex3fv(&farPoints[2].X); glVertex3fv(&farPoints[3].X); glEnd(); // connection lines glBegin(GL_QUAD_STRIP); glVertex3fv(&nearPoints[0].X); glVertex3fv(&farPoints[0].X); glVertex3fv(&nearPoints[1].X); glVertex3fv(&farPoints[1].X); glVertex3fv(&nearPoints[2].X); glVertex3fv(&farPoints[2].X); glVertex3fv(&nearPoints[3].X); glVertex3fv(&farPoints[3].X); glVertex3fv(&nearPoints[0].X); glVertex3fv(&farPoints[0].X); glEnd(); // intermediate planes CVector3D intermediatePoints[4]; for(int i = 0; i < intermediates; ++i) { float t = (i+1.0)/(intermediates+1.0); for(int j = 0; j < 4; ++j) intermediatePoints[j] = nearPoints[j]*t + farPoints[j]*(1.0-t); glBegin(GL_POLYGON); glVertex3fv(&intermediatePoints[0].X); glVertex3fv(&intermediatePoints[1].X); glVertex3fv(&intermediatePoints[2].X); glVertex3fv(&intermediatePoints[3].X); glEnd(); } #endif } Index: ps/trunk/source/graphics/Camera.h =================================================================== --- ps/trunk/source/graphics/Camera.h (revision 22032) +++ ps/trunk/source/graphics/Camera.h (revision 22033) @@ -1,126 +1,125 @@ -/* Copyright (C) 2017 Wildfire Games. +/* Copyright (C) 2019 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * 0 A.D. is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with 0 A.D. If not, see . */ /* * CCamera holds a view and a projection matrix. It also has a frustum * which can be used to cull objects for rendering. */ #ifndef INCLUDED_CAMERA #define INCLUDED_CAMERA #include "Frustum.h" #include "maths/BoundingBoxAligned.h" #include "maths/Matrix3D.h" // view port struct SViewPort { int m_X; int m_Y; int m_Width; int m_Height; }; class CCamera { public: CCamera(); ~CCamera(); // Methods for projection void SetProjection(float nearp, float farp, float fov); void SetProjection(const CMatrix3D& matrix) { m_ProjMat = matrix; } void SetProjectionTile(int tiles, int tile_x, int tile_y); CMatrix3D& GetProjection() { return m_ProjMat; } const CMatrix3D& GetProjection() const { return m_ProjMat; } CMatrix3D& GetOrientation() { return m_Orientation; } const CMatrix3D& GetOrientation() const { return m_Orientation; } CMatrix3D GetViewProjection() const { return m_ProjMat * m_Orientation.GetInverse(); } // Updates the frustum planes. Should be called // everytime the view or projection matrices are // altered. void UpdateFrustum(const CBoundingBoxAligned& scissor = CBoundingBoxAligned(CVector3D(-1.0f, -1.0f, -1.0f), CVector3D(1.0f, 1.0f, 1.0f))); void ClipFrustum(const CPlane& clipPlane); const CFrustum& GetFrustum() const { return m_ViewFrustum; } void SetViewPort(const SViewPort& viewport); const SViewPort& GetViewPort() const { return m_ViewPort; } - // getters float GetNearPlane() const { return m_NearPlane; } float GetFarPlane() const { return m_FarPlane; } float GetFOV() const { return m_FOV; } - // return four points in camera space at given distance from camera - void GetCameraPlanePoints(float dist,CVector3D pts[4]) const; + // Returns four points in camera space at given distance from camera + void GetCameraPlanePoints(float dist, CVector3D pts[4]) const; // Build a ray passing through the screen coordinate (px, py) and the camera ///////////////////////////////////////////////////////////////////////////////////////// // BuildCameraRay: calculate origin and ray direction of a ray through // the pixel (px,py) on the screen void BuildCameraRay(int px, int py, CVector3D& origin, CVector3D& dir) const; // General helpers that seem to fit here // Get the screen-space coordinates corresponding to a given world-space position void GetScreenCoordinates(const CVector3D& world, float& x, float& y) const; // Get the point on the terrain corresponding to pixel (px,py) (or the mouse coordinates) // The aboveWater parameter determines whether we want to stop at the water plane or also get underwater points CVector3D GetWorldCoordinates(int px, int py, bool aboveWater=false) const; // Get the point on the plane at height h corresponding to pixel (px,py) CVector3D GetWorldCoordinates(int px, int py, float h) const; // Get the point on the terrain (or water plane) the camera is pointing towards CVector3D GetFocus() const; // Build an orientation matrix from camera position, camera focus point, and up-vector void LookAt(const CVector3D& camera, const CVector3D& orientation, const CVector3D& up); // Build an orientation matrix from camera position, camera orientation, and up-vector void LookAlong(const CVector3D& camera, CVector3D focus, CVector3D up); /** * Render: Renders the camera's frustum in world space. * The caller should set the color using glColorXy before calling Render. * * @param intermediates determines how many intermediate distance planes should * be hinted at between the near and far planes */ void Render(int intermediates = 0) const; public: // This is the orientation matrix. The inverse of this // is the view matrix CMatrix3D m_Orientation; // Should not be tweaked externally if possible CMatrix3D m_ProjMat; private: float m_NearPlane; float m_FarPlane; float m_FOV; SViewPort m_ViewPort; CFrustum m_ViewFrustum; }; -#endif +#endif // INCLUDED_CAMERA Index: ps/trunk/source/maths/Matrix3D.cpp =================================================================== --- ps/trunk/source/maths/Matrix3D.cpp (revision 22032) +++ ps/trunk/source/maths/Matrix3D.cpp (revision 22033) @@ -1,433 +1,444 @@ -/* Copyright (C) 2017 Wildfire Games. +/* Copyright (C) 2019 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * 0 A.D. is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with 0 A.D. If not, see . */ /* * A Matrix class used for holding and manipulating transformation * info. */ #include "precompiled.h" #include "Matrix3D.h" #include "Quaternion.h" #include "Vector4D.h" //Sets the identity matrix void CMatrix3D::SetIdentity () { _11=1.0f; _12=0.0f; _13=0.0f; _14=0.0f; _21=0.0f; _22=1.0f; _23=0.0f; _24=0.0f; _31=0.0f; _32=0.0f; _33=1.0f; _34=0.0f; _41=0.0f; _42=0.0f; _43=0.0f; _44=1.0f; } //Sets the zero matrix void CMatrix3D::SetZero() { _11=0.0f; _12=0.0f; _13=0.0f; _14=0.0f; _21=0.0f; _22=0.0f; _23=0.0f; _24=0.0f; _31=0.0f; _32=0.0f; _33=0.0f; _34=0.0f; _41=0.0f; _42=0.0f; _43=0.0f; _44=0.0f; } void CMatrix3D::SetOrtho(float left, float right, float bottom, float top, float near, float far) { // Based on OpenGL spec SetZero(); _11 = 2 / (right - left); _22 = 2 / (top - bottom); _33 = -2 / (far - near); _44 = 1; _14 = -(right + left) / (right - left); _24 = -(top + bottom) / (top - bottom); _34 = -(far + near) / (far - near); } void CMatrix3D::SetPerspective(float fov, float aspect, float near, float far) { const float f = 1.f / tanf(fov / 2.f); SetZero(); _11 = f / aspect; _22 = f; _33 = -(far + near) / (near - far); _34 = 2 * far * near / (near - far); _43 = 1; } +void CMatrix3D::SetPerspectiveTile(float fov, float aspect, float near, float far, int tiles, int tile_x, int tile_y) +{ + const float f = 1.f / tanf(fov / 2.f); + + SetPerspective(fov, aspect, near, far); + _11 = tiles * f / aspect; + _22 = tiles * f; + _13 = -(1 - tiles + 2 * tile_x); + _23 = -(1 - tiles + 2 * tile_y); +} + //The following clear the matrix and set the //rotation of each of the 3 axes void CMatrix3D::SetXRotation (float angle) { const float Cos = cosf (angle); const float Sin = sinf (angle); _11=1.0f; _12=0.0f; _13=0.0f; _14=0.0f; _21=0.0f; _22=Cos; _23=-Sin; _24=0.0f; _31=0.0f; _32=Sin; _33=Cos; _34=0.0f; _41=0.0f; _42=0.0f; _43=0.0f; _44=1.0f; } void CMatrix3D::SetYRotation (float angle) { const float Cos = cosf (angle); const float Sin = sinf (angle); _11=Cos; _12=0.0f; _13=Sin; _14=0.0f; _21=0.0f; _22=1.0f; _23=0.0f; _24=0.0f; _31=-Sin; _32=0.0f; _33=Cos; _34=0.0f; _41=0.0f; _42=0.0f; _43=0.0f; _44=1.0f; } void CMatrix3D::SetZRotation (float angle) { const float Cos = cosf (angle); const float Sin = sinf (angle); _11=Cos; _12=-Sin; _13=0.0f; _14=0.0f; _21=Sin; _22=Cos; _23=0.0f; _24=0.0f; _31=0.0f; _32=0.0f; _33=1.0f; _34=0.0f; _41=0.0f; _42=0.0f; _43=0.0f; _44=1.0f; } //The following apply a rotation to the matrix //about each of the axes; void CMatrix3D::RotateX (float angle) { const float Cos = cosf (angle); const float Sin = sinf (angle); const float tmp_21 = _21; const float tmp_22 = _22; const float tmp_23 = _23; const float tmp_24 = _24; _21 = Cos * _21 - Sin * _31; _22 = Cos * _22 - Sin * _32; _23 = Cos * _23 - Sin * _33; _24 = Cos * _24 - Sin * _34; _31 = Sin * tmp_21 + Cos * _31; _32 = Sin * tmp_22 + Cos * _32; _33 = Sin * tmp_23 + Cos * _33; _34 = Sin * tmp_24 + Cos * _34; } void CMatrix3D::RotateY (float angle) { const float Cos = cosf (angle); const float Sin = sinf (angle); const float tmp_11 = _11; const float tmp_12 = _12; const float tmp_13 = _13; const float tmp_14 = _14; _11 = Cos * _11 + Sin * _31; _12 = Cos * _12 + Sin * _32; _13 = Cos * _13 + Sin * _33; _14 = Cos * _14 + Sin * _34; _31 = -Sin * tmp_11 + Cos * _31; _32 = -Sin * tmp_12 + Cos * _32; _33 = -Sin * tmp_13 + Cos * _33; _34 = -Sin * tmp_14 + Cos * _34; } void CMatrix3D::RotateZ (float angle) { const float Cos = cosf (angle); const float Sin = sinf (angle); const float tmp_11 = _11; const float tmp_12 = _12; const float tmp_13 = _13; const float tmp_14 = _14; _11 = Cos * _11 - Sin * _21; _12 = Cos * _12 - Sin * _22; _13 = Cos * _13 - Sin * _23; _14 = Cos * _14 - Sin * _24; _21 = Sin * tmp_11 + Cos * _21; _22 = Sin * tmp_12 + Cos * _22; _23 = Sin * tmp_13 + Cos * _23; _24 = Sin * tmp_14 + Cos * _24; } //Sets the translation of the matrix void CMatrix3D::SetTranslation (float x, float y, float z) { _11=1.0f; _12=0.0f; _13=0.0f; _14=x; _21=0.0f; _22=1.0f; _23=0.0f; _24=y; _31=0.0f; _32=0.0f; _33=1.0f; _34=z; _41=0.0f; _42=0.0f; _43=0.0f; _44=1.0f; } void CMatrix3D::SetTranslation(const CVector3D& vector) { SetTranslation(vector.X, vector.Y, vector.Z); } //Applies a translation to the matrix void CMatrix3D::Translate(float x, float y, float z) { _14 += x; _24 += y; _34 += z; } void CMatrix3D::Translate(const CVector3D &vector) { _14 += vector.X; _24 += vector.Y; _34 += vector.Z; } void CMatrix3D::PostTranslate(float x, float y, float z) { // Equivalent to "m.SetTranslation(x, y, z); *this = *this * m;" _14 += _11*x + _12*y + _13*z; _24 += _21*x + _22*y + _23*z; _34 += _31*x + _32*y + _33*z; _44 += _41*x + _42*y + _43*z; } CVector3D CMatrix3D::GetTranslation() const { return CVector3D(_14, _24, _34); } //Clears and sets the scaling of the matrix void CMatrix3D::SetScaling (float x_scale, float y_scale, float z_scale) { _11=x_scale; _12=0.0f; _13=0.0f; _14=0.0f; _21=0.0f; _22=y_scale; _23=0.0f; _24=0.0f; _31=0.0f; _32=0.0f; _33=z_scale; _34=0.0f; _41=0.0f; _42=0.0f; _43=0.0f; _44=1.0f; } //Scales the matrix void CMatrix3D::Scale (float x_scale, float y_scale, float z_scale) { _11 *= x_scale; _12 *= x_scale; _13 *= x_scale; _14 *= x_scale; _21 *= y_scale; _22 *= y_scale; _23 *= y_scale; _24 *= y_scale; _31 *= z_scale; _32 *= z_scale; _33 *= z_scale; _34 *= z_scale; } //Returns the transpose of the matrix. For orthonormal //matrices, this is the same is the inverse matrix CMatrix3D CMatrix3D::GetTranspose() const { return CMatrix3D( _11, _21, _31, _41, _12, _22, _32, _42, _13, _23, _33, _43, _14, _24, _34, _44); } //Get a vector which points to the left of the matrix CVector3D CMatrix3D::GetLeft() const { return CVector3D(-_11, -_21, -_31); } //Get a vector which points up from the matrix CVector3D CMatrix3D::GetUp() const { return CVector3D(_12, _22, _32); } //Get a vector which points to front of the matrix CVector3D CMatrix3D::GetIn() const { return CVector3D(_13, _23, _33); } /////////////////////////////////////////////////////////////////////////////// // RotateTransposed: rotate a vector by the transpose of this matrix CVector3D CMatrix3D::RotateTransposed(const CVector3D& vector) const { CVector3D result; RotateTransposed(vector,result); return result; } /////////////////////////////////////////////////////////////////////////////// // RotateTransposed: rotate a vector by the transpose of this matrix void CMatrix3D::RotateTransposed(const CVector3D& vector,CVector3D& result) const { result.X = _11*vector.X + _21*vector.Y + _31*vector.Z; result.Y = _12*vector.X + _22*vector.Y + _32*vector.Z; result.Z = _13*vector.X + _23*vector.Y + _33*vector.Z; } void CMatrix3D::GetInverse(CMatrix3D& dst) const { float tmp[12]; // temp array for pairs float src[16]; // array of transpose source matrix float det; // determinant // transpose matrix for (int i = 0; i < 4; ++i) { src[i] = _data[i*4]; src[i + 4] = _data[i*4 + 1]; src[i + 8] = _data[i*4 + 2]; src[i + 12] = _data[i*4 + 3]; } // calculate pairs for first 8 elements (cofactors) tmp[0] = src[10] * src[15]; tmp[1] = src[11] * src[14]; tmp[2] = src[9] * src[15]; tmp[3] = src[11] * src[13]; tmp[4] = src[9] * src[14]; tmp[5] = src[10] * src[13]; tmp[6] = src[8] * src[15]; tmp[7] = src[11] * src[12]; tmp[8] = src[8] * src[14]; tmp[9] = src[10] * src[12]; tmp[10] = src[8] * src[13]; tmp[11] = src[9] * src[12]; // calculate first 8 elements (cofactors) dst._data[0] = (tmp[0]-tmp[1])*src[5] + (tmp[3]-tmp[2])*src[6] + (tmp[4]-tmp[5])*src[7]; dst._data[1] = (tmp[1]-tmp[0])*src[4] + (tmp[6]-tmp[7])*src[6] + (tmp[9]-tmp[8])*src[7]; dst._data[2] = (tmp[2]-tmp[3])*src[4] + (tmp[7]-tmp[6])*src[5] + (tmp[10]-tmp[11])*src[7]; dst._data[3] = (tmp[5]-tmp[4])*src[4] + (tmp[8]-tmp[9])*src[5] + (tmp[11]-tmp[10])*src[6]; dst._data[4] = (tmp[1]-tmp[0])*src[1] + (tmp[2]-tmp[3])*src[2] + (tmp[5]-tmp[4])*src[3]; dst._data[5] = (tmp[0]-tmp[1])*src[0] + (tmp[7]-tmp[6])*src[2] + (tmp[8]-tmp[9])*src[3]; dst._data[6] = (tmp[3]-tmp[2])*src[0] + (tmp[6]-tmp[7])*src[1] + (tmp[11]-tmp[10])*src[3]; dst._data[7] = (tmp[4]-tmp[5])*src[0] + (tmp[9]-tmp[8])*src[1] + (tmp[10]-tmp[11])*src[2]; // calculate pairs for second 8 elements (cofactors) tmp[0] = src[2]*src[7]; tmp[1] = src[3]*src[6]; tmp[2] = src[1]*src[7]; tmp[3] = src[3]*src[5]; tmp[4] = src[1]*src[6]; tmp[5] = src[2]*src[5]; tmp[6] = src[0]*src[7]; tmp[7] = src[3]*src[4]; tmp[8] = src[0]*src[6]; tmp[9] = src[2]*src[4]; tmp[10] = src[0]*src[5]; tmp[11] = src[1]*src[4]; // calculate second 8 elements (cofactors) dst._data[8] = (tmp[0]-tmp[1])*src[13] + (tmp[3]-tmp[2])*src[14] + (tmp[4]-tmp[5])*src[15]; dst._data[9] = (tmp[1]-tmp[0])*src[12] + (tmp[6]-tmp[7])*src[14] + (tmp[9]-tmp[8])*src[15]; dst._data[10] = (tmp[2]-tmp[3])*src[12] + (tmp[7]-tmp[6])*src[13] + (tmp[10]-tmp[11])*src[15]; dst._data[11] = (tmp[5]-tmp[4])*src[12] + (tmp[8]-tmp[9])*src[13] + (tmp[11]-tmp[10])*src[14]; dst._data[12] = (tmp[2]-tmp[3])*src[10] + (tmp[5]-tmp[4])*src[11] + (tmp[1]-tmp[0])*src[9]; dst._data[13] = (tmp[7]-tmp[6])*src[10] + (tmp[8]-tmp[9])*src[11] + (tmp[0]-tmp[1])*src[8]; dst._data[14] = (tmp[6]-tmp[7])*src[9] + (tmp[11]-tmp[10])*src[11] + (tmp[3]-tmp[2])*src[8]; dst._data[15] = (tmp[10]-tmp[11])*src[10] + (tmp[4]-tmp[5])*src[8] + (tmp[9]-tmp[8])*src[9]; // calculate matrix inverse det=src[0]*dst._data[0]+src[1]*dst._data[1]+src[2]*dst._data[2]+src[3]*dst._data[3]; det = 1/det; for ( int j = 0; j < 16; j++) { dst._data[j] *= det; } } CMatrix3D CMatrix3D::GetInverse() const { CMatrix3D r; GetInverse(r); return r; } void CMatrix3D::Rotate(const CQuaternion& quat) { CMatrix3D rotationMatrix=quat.ToMatrix(); Concatenate(rotationMatrix); } CQuaternion CMatrix3D::GetRotation() const { float tr = _data2d[0][0] + _data2d[1][1] + _data2d[2][2]; int next[] = { 1, 2, 0 }; float quat[4]; if (tr > 0.f) { float s = sqrtf(tr + 1.f); quat[3] = s * 0.5f; s = 0.5f / s; quat[0] = (_data2d[1][2] - _data2d[2][1]) * s; quat[1] = (_data2d[2][0] - _data2d[0][2]) * s; quat[2] = (_data2d[0][1] - _data2d[1][0]) * s; } else { int i = 0; if (_data2d[1][1] > _data2d[0][0]) i = 1; if (_data2d[2][2] > _data2d[i][i]) i = 2; int j = next[i]; int k = next[j]; float s = sqrtf((_data2d[i][i] - (_data2d[j][j] + _data2d[k][k])) + 1.f); quat[i] = s * 0.5f; if (s != 0.f) s = 0.5f / s; quat[3] = (_data2d[j][k] - _data2d[k][j]) * s; quat[j] = (_data2d[i][j] + _data2d[j][i]) * s; quat[k] = (_data2d[i][k] + _data2d[k][i]) * s; } return CQuaternion(quat[0], quat[1], quat[2], quat[3]); } void CMatrix3D::SetRotation(const CQuaternion& quat) { quat.ToMatrix(*this); } float CMatrix3D::GetYRotation() const { // Project the X axis vector onto the XZ plane CVector3D axis = -GetLeft(); axis.Y = 0; // Normalise projected vector float len = axis.Length(); if (len < 0.0001f) return 0.f; axis *= 1.0f/len; // Negate the return angle to match the SetYRotation convention return -atan2(axis.Z, axis.X); } Index: ps/trunk/source/maths/Matrix3D.h =================================================================== --- ps/trunk/source/maths/Matrix3D.h (revision 22032) +++ ps/trunk/source/maths/Matrix3D.h (revision 22033) @@ -1,324 +1,325 @@ -/* Copyright (C) 2017 Wildfire Games. +/* Copyright (C) 2019 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * 0 A.D. is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with 0 A.D. If not, see . */ /* * A Matrix class used for holding and manipulating transformation * info. */ #ifndef INCLUDED_MATRIX3D #define INCLUDED_MATRIX3D #include "maths/Vector3D.h" #include "maths/Vector4D.h" class CQuaternion; ///////////////////////////////////////////////////////////////////////// // CMatrix3D: a 4x4 matrix class for common operations in 3D class CMatrix3D { public: // the matrix data itself - accessible as either longhand names // or via a flat or 2d array // NOTE: _xy means row x, column y in the mathematical notation of this matrix, so don't be // fooled by the way they're listed below union { struct { float _11, _21, _31, _41; float _12, _22, _32, _42; float _13, _23, _33, _43; float _14, _24, _34, _44; }; float _data[16]; float _data2d[4][4]; // (Be aware that m(0,2) == _data2d[2][0] == _13, etc. This is to be considered a feature.) }; public: // constructors CMatrix3D () { } CMatrix3D( float a11, float a12, float a13, float a14, float a21, float a22, float a23, float a24, float a31, float a32, float a33, float a34, float a41, float a42, float a43, float a44) : _11(a11), _12(a12), _13(a13), _14(a14), _21(a21), _22(a22), _23(a23), _24(a24), _31(a31), _32(a32), _33(a33), _34(a34), _41(a41), _42(a42), _43(a43), _44(a44) { } CMatrix3D(float data[]) : _11(data[0]), _21(data[1]), _31(data[2]), _41(data[3]), _12(data[4]), _22(data[5]), _32(data[6]), _42(data[7]), _13(data[8]), _23(data[9]), _33(data[10]), _43(data[11]), _14(data[12]), _24(data[13]), _34(data[14]), _44(data[15]) { } // accessors to individual elements of matrix // NOTE: in this function definition, 'col' and 'row' represent the column and row into the // internal element matrix which is the transposed of the mathematical notation, so the first // and second arguments here are actually the row and column into the mathematical notation. float& operator()(int col, int row) { return _data[row*4+col]; } const float& operator()(int col, int row) const { return _data[row*4+col]; } float& operator[](int idx) { return _data[idx]; } const float& operator[](int idx) const { return _data[idx]; } // matrix multiplication CMatrix3D operator*(const CMatrix3D &matrix) const { return CMatrix3D( _11*matrix._11 + _12*matrix._21 + _13*matrix._31 + _14*matrix._41, _11*matrix._12 + _12*matrix._22 + _13*matrix._32 + _14*matrix._42, _11*matrix._13 + _12*matrix._23 + _13*matrix._33 + _14*matrix._43, _11*matrix._14 + _12*matrix._24 + _13*matrix._34 + _14*matrix._44, _21*matrix._11 + _22*matrix._21 + _23*matrix._31 + _24*matrix._41, _21*matrix._12 + _22*matrix._22 + _23*matrix._32 + _24*matrix._42, _21*matrix._13 + _22*matrix._23 + _23*matrix._33 + _24*matrix._43, _21*matrix._14 + _22*matrix._24 + _23*matrix._34 + _24*matrix._44, _31*matrix._11 + _32*matrix._21 + _33*matrix._31 + _34*matrix._41, _31*matrix._12 + _32*matrix._22 + _33*matrix._32 + _34*matrix._42, _31*matrix._13 + _32*matrix._23 + _33*matrix._33 + _34*matrix._43, _31*matrix._14 + _32*matrix._24 + _33*matrix._34 + _34*matrix._44, _41*matrix._11 + _42*matrix._21 + _43*matrix._31 + _44*matrix._41, _41*matrix._12 + _42*matrix._22 + _43*matrix._32 + _44*matrix._42, _41*matrix._13 + _42*matrix._23 + _43*matrix._33 + _44*matrix._43, _41*matrix._14 + _42*matrix._24 + _43*matrix._34 + _44*matrix._44 ); } // matrix multiplication/assignment CMatrix3D& operator*=(const CMatrix3D &matrix) { Concatenate(matrix); return *this; } // matrix scaling CMatrix3D operator*(float f) const { return CMatrix3D( _11*f, _12*f, _13*f, _14*f, _21*f, _22*f, _23*f, _24*f, _31*f, _32*f, _33*f, _34*f, _41*f, _42*f, _43*f, _44*f ); } // matrix addition CMatrix3D operator+(const CMatrix3D &m) const { return CMatrix3D( _11+m._11, _12+m._12, _13+m._13, _14+m._14, _21+m._21, _22+m._22, _23+m._23, _24+m._24, _31+m._31, _32+m._32, _33+m._33, _34+m._34, _41+m._41, _42+m._42, _43+m._43, _44+m._44 ); } // matrix addition/assignment CMatrix3D& operator+=(const CMatrix3D &m) { _11 += m._11; _21 += m._21; _31 += m._31; _41 += m._41; _12 += m._12; _22 += m._22; _32 += m._32; _42 += m._42; _13 += m._13; _23 += m._23; _33 += m._33; _43 += m._43; _14 += m._14; _24 += m._24; _34 += m._34; _44 += m._44; return *this; } // equality bool operator==(const CMatrix3D &m) const { return _11 == m._11 && _21 == m._21 && _31 == m._31 && _41 == m._41 && _12 == m._12 && _22 == m._22 && _32 == m._32 && _42 == m._42 && _13 == m._13 && _23 == m._23 && _33 == m._33 && _43 == m._43 && _14 == m._14 && _24 == m._24 && _34 == m._34 && _44 == m._44; } // inequality bool operator!=(const CMatrix3D& m) const { return !(*this == m); } // set this matrix to the identity matrix void SetIdentity(); // set this matrix to the zero matrix void SetZero(); // set this matrix to the orthogonal projection matrix (as with glOrtho) void SetOrtho(float left, float right, float bottom, float top, float near, float far); // set this matrix to the perspective projection matrix void SetPerspective(float fov, float aspect, float near, float far); + void SetPerspectiveTile(float fov, float aspect, float near, float far, int tiles, int tile_x, int tile_y); // concatenate arbitrary matrix onto this matrix void Concatenate(const CMatrix3D& m) { (*this) = m * (*this); } // blend matrix using only 4x3 subset void Blend(const CMatrix3D& m, float f) { _11 = m._11*f; _21 = m._21*f; _31 = m._31*f; _12 = m._12*f; _22 = m._22*f; _32 = m._32*f; _13 = m._13*f; _23 = m._23*f; _33 = m._33*f; _14 = m._14*f; _24 = m._24*f; _34 = m._34*f; } // blend matrix using only 4x3 and add onto existing blend void AddBlend(const CMatrix3D& m, float f) { _11 += m._11*f; _21 += m._21*f; _31 += m._31*f; _12 += m._12*f; _22 += m._22*f; _32 += m._32*f; _13 += m._13*f; _23 += m._23*f; _33 += m._33*f; _14 += m._14*f; _24 += m._24*f; _34 += m._34*f; } // set this matrix to a rotation matrix for a rotation about X axis of given angle void SetXRotation(float angle); // set this matrix to a rotation matrix for a rotation about Y axis of given angle void SetYRotation(float angle); // set this matrix to a rotation matrix for a rotation about Z axis of given angle void SetZRotation(float angle); // set this matrix to a rotation described by given quaternion void SetRotation(const CQuaternion& quat); // concatenate a rotation about the X axis onto this matrix void RotateX(float angle); // concatenate a rotation about the Y axis onto this matrix void RotateY(float angle); // concatenate a rotation about the Z axis onto this matrix void RotateZ(float angle); // concatenate a rotation described by given quaternion void Rotate(const CQuaternion& quat); // sets this matrix to the given translation matrix (any existing transformation will be overwritten) void SetTranslation(float x, float y, float z); void SetTranslation(const CVector3D& vector); // concatenate given translation onto this matrix. Assumes the current // matrix is an affine transformation (i.e. the bottom row is [0,0,0,1]) // as an optimisation. void Translate(float x, float y, float z); void Translate(const CVector3D& vector); // apply translation after this matrix (M = M * T) void PostTranslate(float x, float y, float z); // set this matrix to the given scaling matrix void SetScaling(float x_scale, float y_scale, float z_scale); // concatenate given scaling matrix onto this matrix void Scale(float x_scale, float y_scale, float z_scale); // calculate the inverse of this matrix, store in dst void GetInverse(CMatrix3D& dst) const; // return the inverse of this matrix CMatrix3D GetInverse() const; // calculate the transpose of this matrix, store in dst CMatrix3D GetTranspose() const; // return the translation component of this matrix CVector3D GetTranslation() const; // return left vector, derived from rotation CVector3D GetLeft() const; // return up vector, derived from rotation CVector3D GetUp() const; // return forward vector, derived from rotation CVector3D GetIn() const; // return a quaternion representing the matrix's rotation CQuaternion GetRotation() const; // return the angle of rotation around the Y axis in range [-pi,pi] // (based on projecting the X axis onto the XZ plane) float GetYRotation() const; // transform a 3D vector by this matrix CVector3D Transform(const CVector3D &vector) const { CVector3D result; Transform(vector, result); return result; } void Transform(const CVector3D& vector, CVector3D& result) const { result.X = _11*vector.X + _12*vector.Y + _13*vector.Z + _14; result.Y = _21*vector.X + _22*vector.Y + _23*vector.Z + _24; result.Z = _31*vector.X + _32*vector.Y + _33*vector.Z + _34; } // transform a 4D vector by this matrix CVector4D Transform(const CVector4D &vector) const { CVector4D result; Transform(vector, result); return result; } void Transform(const CVector4D& vector, CVector4D& result) const { result.X = _11*vector.X + _12*vector.Y + _13*vector.Z + _14*vector.W; result.Y = _21*vector.X + _22*vector.Y + _23*vector.Z + _24*vector.W; result.Z = _31*vector.X + _32*vector.Y + _33*vector.Z + _34*vector.W; result.W = _41*vector.X + _42*vector.Y + _43*vector.Z + _44*vector.W; } // rotate a vector by this matrix CVector3D Rotate(const CVector3D& vector) const { CVector3D result; Rotate(vector, result); return result; } void Rotate(const CVector3D& vector, CVector3D& result) const { result.X = _11*vector.X + _12*vector.Y + _13*vector.Z; result.Y = _21*vector.X + _22*vector.Y + _23*vector.Z; result.Z = _31*vector.X + _32*vector.Y + _33*vector.Z; } // rotate a vector by the transpose of this matrix void RotateTransposed(const CVector3D& vector,CVector3D& result) const; CVector3D RotateTransposed(const CVector3D& vector) const; }; -#endif +#endif // INCLUDED_MATRIX3D