LCOV - code coverage report
Current view: top level - source/graphics - Camera.cpp (source / functions) Hit Total Coverage
Test: 0 A.D. test coverage report Lines: 155 235 66.0 %
Date: 2023-01-19 00:18:29 Functions: 14 20 70.0 %

          Line data    Source code
       1             : /* Copyright (C) 2022 Wildfire Games.
       2             :  * This file is part of 0 A.D.
       3             :  *
       4             :  * 0 A.D. is free software: you can redistribute it and/or modify
       5             :  * it under the terms of the GNU General Public License as published by
       6             :  * the Free Software Foundation, either version 2 of the License, or
       7             :  * (at your option) any later version.
       8             :  *
       9             :  * 0 A.D. is distributed in the hope that it will be useful,
      10             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      11             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12             :  * GNU General Public License for more details.
      13             :  *
      14             :  * You should have received a copy of the GNU General Public License
      15             :  * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.
      16             :  */
      17             : 
      18             : #include "precompiled.h"
      19             : 
      20             : #include "Camera.h"
      21             : 
      22             : #include "graphics/HFTracer.h"
      23             : #include "graphics/Terrain.h"
      24             : #include "maths/MathUtil.h"
      25             : #include "maths/Vector2D.h"
      26             : #include "maths/Vector4D.h"
      27             : #include "ps/Game.h"
      28             : #include "ps/World.h"
      29             : #include "renderer/Renderer.h"
      30             : #include "renderer/SceneRenderer.h"
      31             : #include "renderer/WaterManager.h"
      32             : 
      33          37 : CCamera::CCamera()
      34             : {
      35             :     // Set viewport to something anything should handle, but should be initialised
      36             :     // to window size before use.
      37          37 :     m_ViewPort.m_X = 0;
      38          37 :     m_ViewPort.m_Y = 0;
      39          37 :     m_ViewPort.m_Width = 800;
      40          37 :     m_ViewPort.m_Height = 600;
      41          37 : }
      42             : 
      43             : CCamera::~CCamera() = default;
      44             : 
      45           2 : void CCamera::SetProjection(const CMatrix3D& matrix)
      46             : {
      47           2 :     m_ProjType = ProjectionType::CUSTOM;
      48           2 :     m_ProjMat = matrix;
      49           2 : }
      50             : 
      51           0 : void CCamera::SetProjectionFromCamera(const CCamera& camera)
      52             : {
      53           0 :     m_ProjType = camera.m_ProjType;
      54           0 :     m_NearPlane = camera.m_NearPlane;
      55           0 :     m_FarPlane = camera.m_FarPlane;
      56           0 :     if (m_ProjType == ProjectionType::PERSPECTIVE)
      57             :     {
      58           0 :         m_FOV = camera.m_FOV;
      59             :     }
      60           0 :     else if (m_ProjType == ProjectionType::ORTHO)
      61             :     {
      62           0 :         m_OrthoScale = camera.m_OrthoScale;
      63             :     }
      64           0 :     m_ProjMat = camera.m_ProjMat;
      65           0 : }
      66             : 
      67           5 : void CCamera::SetOrthoProjection(float nearp, float farp, float scale)
      68             : {
      69           5 :     m_ProjType = ProjectionType::ORTHO;
      70           5 :     m_NearPlane = nearp;
      71           5 :     m_FarPlane = farp;
      72           5 :     m_OrthoScale = scale;
      73             : 
      74           5 :     const float halfHeight = 0.5f * m_OrthoScale;
      75           5 :     const float halfWidth = halfHeight * GetAspectRatio();
      76           5 :     m_ProjMat.SetOrtho(-halfWidth, halfWidth, -halfHeight, halfHeight, m_NearPlane, m_FarPlane);
      77           5 : }
      78             : 
      79           7 : void CCamera::SetPerspectiveProjection(float nearp, float farp, float fov)
      80             : {
      81           7 :     m_ProjType = ProjectionType::PERSPECTIVE;
      82           7 :     m_NearPlane = nearp;
      83           7 :     m_FarPlane = farp;
      84           7 :     m_FOV = fov;
      85             : 
      86           7 :     m_ProjMat.SetPerspective(m_FOV, GetAspectRatio(), m_NearPlane, m_FarPlane);
      87           7 : }
      88             : 
      89             : // Updates the frustum planes. Should be called
      90             : // everytime the view or projection matrices are
      91             : // altered.
      92           4 : void CCamera::UpdateFrustum(const CBoundingBoxAligned& scissor)
      93             : {
      94           4 :     CMatrix3D MatFinal;
      95           4 :     CMatrix3D MatView;
      96             : 
      97           4 :     m_Orientation.GetInverse(MatView);
      98             : 
      99           4 :     MatFinal = m_ProjMat * MatView;
     100             : 
     101           4 :     m_ViewFrustum.SetNumPlanes(6);
     102             : 
     103             :     // get the RIGHT plane
     104           4 :     m_ViewFrustum[0].m_Norm.X = scissor[1].X*MatFinal._41 - MatFinal._11;
     105           4 :     m_ViewFrustum[0].m_Norm.Y = scissor[1].X*MatFinal._42 - MatFinal._12;
     106           4 :     m_ViewFrustum[0].m_Norm.Z = scissor[1].X*MatFinal._43 - MatFinal._13;
     107           4 :     m_ViewFrustum[0].m_Dist   = scissor[1].X*MatFinal._44 - MatFinal._14;
     108             : 
     109             :     // get the LEFT plane
     110           4 :     m_ViewFrustum[1].m_Norm.X = -scissor[0].X*MatFinal._41 + MatFinal._11;
     111           4 :     m_ViewFrustum[1].m_Norm.Y = -scissor[0].X*MatFinal._42 + MatFinal._12;
     112           4 :     m_ViewFrustum[1].m_Norm.Z = -scissor[0].X*MatFinal._43 + MatFinal._13;
     113           4 :     m_ViewFrustum[1].m_Dist   = -scissor[0].X*MatFinal._44 + MatFinal._14;
     114             : 
     115             :     // get the BOTTOM plane
     116           4 :     m_ViewFrustum[2].m_Norm.X = -scissor[0].Y*MatFinal._41 + MatFinal._21;
     117           4 :     m_ViewFrustum[2].m_Norm.Y = -scissor[0].Y*MatFinal._42 + MatFinal._22;
     118           4 :     m_ViewFrustum[2].m_Norm.Z = -scissor[0].Y*MatFinal._43 + MatFinal._23;
     119           4 :     m_ViewFrustum[2].m_Dist   = -scissor[0].Y*MatFinal._44 + MatFinal._24;
     120             : 
     121             :     // get the TOP plane
     122           4 :     m_ViewFrustum[3].m_Norm.X = scissor[1].Y*MatFinal._41 - MatFinal._21;
     123           4 :     m_ViewFrustum[3].m_Norm.Y = scissor[1].Y*MatFinal._42 - MatFinal._22;
     124           4 :     m_ViewFrustum[3].m_Norm.Z = scissor[1].Y*MatFinal._43 - MatFinal._23;
     125           4 :     m_ViewFrustum[3].m_Dist   = scissor[1].Y*MatFinal._44 - MatFinal._24;
     126             : 
     127             :     // get the FAR plane
     128           4 :     m_ViewFrustum[4].m_Norm.X = scissor[1].Z*MatFinal._41 - MatFinal._31;
     129           4 :     m_ViewFrustum[4].m_Norm.Y = scissor[1].Z*MatFinal._42 - MatFinal._32;
     130           4 :     m_ViewFrustum[4].m_Norm.Z = scissor[1].Z*MatFinal._43 - MatFinal._33;
     131           4 :     m_ViewFrustum[4].m_Dist   = scissor[1].Z*MatFinal._44 - MatFinal._34;
     132             : 
     133             :     // get the NEAR plane
     134           4 :     m_ViewFrustum[5].m_Norm.X = -scissor[0].Z*MatFinal._41 + MatFinal._31;
     135           4 :     m_ViewFrustum[5].m_Norm.Y = -scissor[0].Z*MatFinal._42 + MatFinal._32;
     136           4 :     m_ViewFrustum[5].m_Norm.Z = -scissor[0].Z*MatFinal._43 + MatFinal._33;
     137           4 :     m_ViewFrustum[5].m_Dist   = -scissor[0].Z*MatFinal._44 + MatFinal._34;
     138             : 
     139          28 :     for (size_t i = 0; i < 6; ++i)
     140          24 :         m_ViewFrustum[i].Normalize();
     141           4 : }
     142             : 
     143           0 : void CCamera::ClipFrustum(const CPlane& clipPlane)
     144             : {
     145           0 :     CPlane normClipPlane = clipPlane;
     146           0 :     normClipPlane.Normalize();
     147           0 :     m_ViewFrustum.AddPlane(normClipPlane);
     148           0 : }
     149             : 
     150          13 : void CCamera::SetViewPort(const SViewPort& viewport)
     151             : {
     152          13 :     m_ViewPort.m_X = viewport.m_X;
     153          13 :     m_ViewPort.m_Y = viewport.m_Y;
     154          13 :     m_ViewPort.m_Width = viewport.m_Width;
     155          13 :     m_ViewPort.m_Height = viewport.m_Height;
     156          13 : }
     157             : 
     158          28 : float CCamera::GetAspectRatio() const
     159             : {
     160          28 :     return static_cast<float>(m_ViewPort.m_Width) / static_cast<float>(m_ViewPort.m_Height);
     161             : }
     162             : 
     163          18 : void CCamera::GetViewQuad(float dist, Quad& quad) const
     164             : {
     165          18 :     if (m_ProjType == ProjectionType::CUSTOM)
     166             :     {
     167           3 :         const CMatrix3D invProjection = m_ProjMat.GetInverse();
     168             :         const std::array<CVector2D, 4> ndcCorners = {
     169             :             CVector2D{-1.0f, -1.0f}, CVector2D{1.0f, -1.0f},
     170           3 :             CVector2D{1.0f, 1.0f}, CVector2D{-1.0f, 1.0f}};
     171          15 :         for (size_t idx = 0; idx < 4; ++idx)
     172             :         {
     173          12 :             const CVector2D& corner = ndcCorners[idx];
     174             :             CVector4D nearCorner =
     175          12 :                 invProjection.Transform(CVector4D(corner.X, corner.Y, -1.0f, 1.0f));
     176          12 :             nearCorner /= nearCorner.W;
     177             :             CVector4D farCorner =
     178          12 :                 invProjection.Transform(CVector4D(corner.X, corner.Y, 1.0f, 1.0f));
     179          12 :             farCorner /= farCorner.W;
     180          12 :             const float t = (dist - nearCorner.Z) / (farCorner.Z - nearCorner.Z);
     181          12 :             const CVector4D quadCorner = nearCorner * (1.0 - t) + farCorner * t;
     182          12 :             quad[idx].X = quadCorner.X;
     183          12 :             quad[idx].Y = quadCorner.Y;
     184          12 :             quad[idx].Z = quadCorner.Z;
     185             :         }
     186           3 :         return;
     187             :     }
     188             : 
     189          15 :     const float y = m_ProjType == ProjectionType::PERSPECTIVE ? dist * tanf(m_FOV * 0.5f) : m_OrthoScale * 0.5f;
     190          15 :     const float x = y * GetAspectRatio();
     191             : 
     192          15 :     quad[0].X = -x;
     193          15 :     quad[0].Y = -y;
     194          15 :     quad[0].Z = dist;
     195          15 :     quad[1].X = x;
     196          15 :     quad[1].Y = -y;
     197          15 :     quad[1].Z = dist;
     198          15 :     quad[2].X = x;
     199          15 :     quad[2].Y = y;
     200          15 :     quad[2].Z = dist;
     201          15 :     quad[3].X = -x;
     202          15 :     quad[3].Y = y;
     203          15 :     quad[3].Z = dist;
     204             : }
     205             : 
     206           6 : void CCamera::BuildCameraRay(int px, int py, CVector3D& origin, CVector3D& dir) const
     207             : {
     208           6 :     ENSURE(m_ProjType == ProjectionType::PERSPECTIVE || m_ProjType == ProjectionType::ORTHO);
     209             : 
     210             :     // Coordinates relative to the camera plane.
     211           6 :     const float dx = static_cast<float>(px) / m_ViewPort.m_Width;
     212           6 :     const float dy = 1.0f - static_cast<float>(py) / m_ViewPort.m_Height;
     213             : 
     214           6 :     Quad points;
     215           6 :     GetViewQuad(m_FarPlane, points);
     216             : 
     217             :     // Transform from camera space to world space.
     218          30 :     for (CVector3D& point : points)
     219          24 :         point = m_Orientation.Transform(point);
     220             : 
     221             :     // Get world space position of mouse point at the far clipping plane.
     222           6 :     const CVector3D basisX = points[1] - points[0];
     223           6 :     const CVector3D basisY = points[3] - points[0];
     224             : 
     225           6 :     if (m_ProjType == ProjectionType::PERSPECTIVE)
     226             :     {
     227             :         // Build direction for the camera origin to the target point.
     228           3 :         origin = m_Orientation.GetTranslation();
     229           3 :         CVector3D targetPoint = points[0] + (basisX * dx) + (basisY * dy);
     230           3 :         dir = targetPoint - origin;
     231             :     }
     232           3 :     else if (m_ProjType == ProjectionType::ORTHO)
     233             :     {
     234           3 :         origin = m_Orientation.GetTranslation() + (basisX * (dx - 0.5f)) + (basisY * (dy - 0.5f));
     235           3 :         dir = m_Orientation.GetIn();
     236             :     }
     237           6 :     dir.Normalize();
     238           6 : }
     239             : 
     240           0 : void CCamera::GetScreenCoordinates(const CVector3D& world, float& x, float& y) const
     241             : {
     242           0 :     CMatrix3D transform = m_ProjMat * m_Orientation.GetInverse();
     243             : 
     244           0 :     CVector4D screenspace = transform.Transform(CVector4D(world.X, world.Y, world.Z, 1.0f));
     245             : 
     246           0 :     x = screenspace.X / screenspace.W;
     247           0 :     y = screenspace.Y / screenspace.W;
     248           0 :     x = (x + 1) * 0.5f * m_ViewPort.m_Width;
     249           0 :     y = (1 - y) * 0.5f * m_ViewPort.m_Height;
     250           0 : }
     251             : 
     252           0 : CVector3D CCamera::GetWorldCoordinates(int px, int py, bool aboveWater) const
     253             : {
     254           0 :     CHFTracer tracer(g_Game->GetWorld()->GetTerrain());
     255             :     int x, z;
     256           0 :     CVector3D origin, dir, delta, terrainPoint, waterPoint;
     257             : 
     258           0 :     BuildCameraRay(px, py, origin, dir);
     259             : 
     260           0 :     bool gotTerrain = tracer.RayIntersect(origin, dir, x, z, terrainPoint);
     261             : 
     262           0 :     if (!aboveWater)
     263             :     {
     264           0 :         if (gotTerrain)
     265           0 :             return terrainPoint;
     266             : 
     267             :         // Off the edge of the world?
     268             :         // Work out where it /would/ hit, if the map were extended out to infinity with average height.
     269           0 :         return GetWorldCoordinates(px, py, 50.0f);
     270             :     }
     271             : 
     272           0 :     CPlane plane;
     273           0 :     plane.Set(CVector3D(0.f, 1.f, 0.f),                                     // upwards normal
     274           0 :         CVector3D(0.f, g_Renderer.GetSceneRenderer().GetWaterManager().m_WaterHeight, 0.f));    // passes through water plane
     275             : 
     276           0 :     bool gotWater = plane.FindRayIntersection( origin, dir, &waterPoint );
     277             : 
     278             :     // Clamp the water intersection to within the map's bounds, so that
     279             :     // we'll always return a valid position on the map
     280           0 :     ssize_t mapSize = g_Game->GetWorld()->GetTerrain()->GetVerticesPerSide();
     281           0 :     if (gotWater)
     282             :     {
     283           0 :         waterPoint.X = Clamp<float>(waterPoint.X, 0.f, (mapSize - 1) * TERRAIN_TILE_SIZE);
     284           0 :         waterPoint.Z = Clamp<float>(waterPoint.Z, 0.f, (mapSize - 1) * TERRAIN_TILE_SIZE);
     285             :     }
     286             : 
     287           0 :     if (gotTerrain)
     288             :     {
     289           0 :         if (gotWater)
     290             :         {
     291             :             // Intersecting both heightmap and water plane; choose the closest of those
     292           0 :             if ((origin - terrainPoint).LengthSquared() < (origin - waterPoint).LengthSquared())
     293           0 :                 return terrainPoint;
     294             :             else
     295           0 :                 return waterPoint;
     296             :         }
     297             :         else
     298             :         {
     299             :             // Intersecting heightmap but parallel to water plane
     300           0 :             return terrainPoint;
     301             :         }
     302             :     }
     303             :     else
     304             :     {
     305           0 :         if (gotWater)
     306             :         {
     307             :             // Only intersecting water plane
     308           0 :             return waterPoint;
     309             :         }
     310             :         else
     311             :         {
     312             :             // Not intersecting terrain or water; just return 0,0,0.
     313           0 :             return CVector3D(0.f, 0.f, 0.f);
     314             :         }
     315             :     }
     316             : 
     317             : }
     318             : 
     319           0 : CVector3D CCamera::GetWorldCoordinates(int px, int py, float h) const
     320             : {
     321           0 :     CPlane plane;
     322           0 :     plane.Set(CVector3D(0.f, 1.f, 0.f), CVector3D(0.f, h, 0.f)); // upwards normal, passes through h
     323             : 
     324           0 :     CVector3D origin, dir, delta, currentTarget;
     325             : 
     326           0 :     BuildCameraRay(px, py, origin, dir);
     327             : 
     328           0 :     if (plane.FindRayIntersection(origin, dir, &currentTarget))
     329           0 :         return currentTarget;
     330             : 
     331             :     // No intersection with the infinite plane - nothing sensible can be returned,
     332             :     // so just choose an arbitrary point on the plane
     333           0 :     return CVector3D(0.f, h, 0.f);
     334             : }
     335             : 
     336           0 : CVector3D CCamera::GetFocus() const
     337             : {
     338             :     // Basically the same as GetWorldCoordinates
     339             : 
     340           0 :     CHFTracer tracer(g_Game->GetWorld()->GetTerrain());
     341             :     int x, z;
     342             : 
     343           0 :     CVector3D origin, dir, delta, terrainPoint, waterPoint;
     344             : 
     345           0 :     origin = m_Orientation.GetTranslation();
     346           0 :     dir = m_Orientation.GetIn();
     347             : 
     348           0 :     bool gotTerrain = tracer.RayIntersect(origin, dir, x, z, terrainPoint);
     349             : 
     350           0 :     CPlane plane;
     351           0 :     plane.Set(CVector3D(0.f, 1.f, 0.f),                                     // upwards normal
     352           0 :         CVector3D(0.f, g_Renderer.GetSceneRenderer().GetWaterManager().m_WaterHeight, 0.f));    // passes through water plane
     353             : 
     354           0 :     bool gotWater = plane.FindRayIntersection( origin, dir, &waterPoint );
     355             : 
     356             :     // Clamp the water intersection to within the map's bounds, so that
     357             :     // we'll always return a valid position on the map
     358           0 :     ssize_t mapSize = g_Game->GetWorld()->GetTerrain()->GetVerticesPerSide();
     359           0 :     if (gotWater)
     360             :     {
     361           0 :         waterPoint.X = Clamp<float>(waterPoint.X, 0.f, (mapSize - 1) * TERRAIN_TILE_SIZE);
     362           0 :         waterPoint.Z = Clamp<float>(waterPoint.Z, 0.f, (mapSize - 1) * TERRAIN_TILE_SIZE);
     363             :     }
     364             : 
     365           0 :     if (gotTerrain)
     366             :     {
     367           0 :         if (gotWater)
     368             :         {
     369             :             // Intersecting both heightmap and water plane; choose the closest of those
     370           0 :             if ((origin - terrainPoint).LengthSquared() < (origin - waterPoint).LengthSquared())
     371           0 :                 return terrainPoint;
     372             :             else
     373           0 :                 return waterPoint;
     374             :         }
     375             :         else
     376             :         {
     377             :             // Intersecting heightmap but parallel to water plane
     378           0 :             return terrainPoint;
     379             :         }
     380             :     }
     381             :     else
     382             :     {
     383           0 :         if (gotWater)
     384             :         {
     385             :             // Only intersecting water plane
     386           0 :             return waterPoint;
     387             :         }
     388             :         else
     389             :         {
     390             :             // Not intersecting terrain or water; just return 0,0,0.
     391           0 :             return CVector3D(0.f, 0.f, 0.f);
     392             :         }
     393             :     }
     394             : }
     395             : 
     396          11 : CBoundingBoxAligned CCamera::GetBoundsInViewPort(const CBoundingBoxAligned& boundigBox) const
     397             : {
     398          11 :     const CVector3D cameraPosition = GetOrientation().GetTranslation();
     399          11 :     if (boundigBox.IsPointInside(cameraPosition))
     400           1 :         return CBoundingBoxAligned(CVector3D(-1.0f, -1.0f, 0.0f), CVector3D(1.0f, 1.0f, 0.0f));
     401             : 
     402          10 :     const CMatrix3D viewProjection = GetViewProjection();
     403          10 :     CBoundingBoxAligned viewPortBounds;
     404             : #define ADD_VISIBLE_POINT_TO_VIEWBOUNDS(POSITION) STMT( \
     405             :         CVector4D v = viewProjection.Transform(CVector4D((POSITION).X, (POSITION).Y, (POSITION).Z, 1.0f)); \
     406             :         if (v.W != 0.0f) \
     407             :             viewPortBounds += CVector3D(v.X, v.Y, v.Z) * (1.0f / v.W); )
     408             : 
     409          10 :     std::array<CVector3D, 8> worldPositions;
     410             :     std::array<bool, 8> isBehindNearPlane;
     411          10 :     const CVector3D lookDirection = GetOrientation().GetIn();
     412             :     // Check corners.
     413          90 :     for (size_t idx = 0; idx < 8; ++idx)
     414             :     {
     415          80 :         worldPositions[idx] = CVector3D(boundigBox[(idx >> 0) & 0x1].X, boundigBox[(idx >> 1) & 0x1].Y, boundigBox[(idx >> 2) & 0x1].Z);
     416          80 :         isBehindNearPlane[idx] = lookDirection.Dot(worldPositions[idx]) < lookDirection.Dot(cameraPosition) + GetNearPlane();
     417          80 :         if (!isBehindNearPlane[idx])
     418          68 :             ADD_VISIBLE_POINT_TO_VIEWBOUNDS(worldPositions[idx]);
     419             :     }
     420             :     // Check edges for intersections with the near plane.
     421          90 :     for (size_t idxBegin = 0; idxBegin < 8; ++idxBegin)
     422         320 :         for (size_t nextComponent = 0; nextComponent < 3; ++nextComponent)
     423             :         {
     424         240 :             const size_t idxEnd = idxBegin | (1u << nextComponent);
     425         240 :             if (idxBegin == idxEnd || isBehindNearPlane[idxBegin] == isBehindNearPlane[idxEnd])
     426         460 :                 continue;
     427          12 :             CVector3D intersection;
     428             :             // Intersect the segment with the near plane.
     429          12 :             if (!m_ViewFrustum[5].FindLineSegIntersection(worldPositions[idxBegin], worldPositions[idxEnd], &intersection))
     430           4 :                 continue;
     431           8 :             ADD_VISIBLE_POINT_TO_VIEWBOUNDS(intersection);
     432             :         }
     433             : #undef ADD_VISIBLE_POINT_TO_VIEWBOUNDS
     434          10 :     if (viewPortBounds[0].X >= 1.0f || viewPortBounds[1].X <= -1.0f || viewPortBounds[0].Y >= 1.0f || viewPortBounds[1].Y <= -1.0f)
     435           4 :         return CBoundingBoxAligned{};
     436           6 :     return viewPortBounds;
     437             : }
     438             : 
     439           9 : void CCamera::LookAt(const CVector3D& camera, const CVector3D& focus, const CVector3D& up)
     440             : {
     441           9 :     CVector3D delta = focus - camera;
     442           9 :     LookAlong(camera, delta, up);
     443           9 : }
     444             : 
     445          13 : void CCamera::LookAlong(const CVector3D& camera, CVector3D orientation, CVector3D up)
     446             : {
     447          13 :     orientation.Normalize();
     448          13 :     up.Normalize();
     449          13 :     const CVector3D s = orientation.Cross(up);
     450          13 :     up = s.Cross(orientation);
     451             : 
     452          13 :     m_Orientation._11 = -s.X;   m_Orientation._12 = up.X;   m_Orientation._13 = orientation.X;  m_Orientation._14 = camera.X;
     453          13 :     m_Orientation._21 = -s.Y;   m_Orientation._22 = up.Y;   m_Orientation._23 = orientation.Y;  m_Orientation._24 = camera.Y;
     454          13 :     m_Orientation._31 = -s.Z;   m_Orientation._32 = up.Z;   m_Orientation._33 = orientation.Z;  m_Orientation._34 = camera.Z;
     455          13 :     m_Orientation._41 = 0.0f;   m_Orientation._42 = 0.0f;   m_Orientation._43 = 0.0f;           m_Orientation._44 = 1.0f;
     456          16 : }

Generated by: LCOV version 1.13