LCOV - code coverage report
Current view: top level - source/renderer - ShadowMap.cpp (source / functions) Hit Total Coverage
Test: 0 A.D. test coverage report Lines: 0 363 0.0 %
Date: 2022-03-08 13:03:03 Functions: 0 20 0.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 "ShadowMap.h"
      21             : 
      22             : #include "graphics/Camera.h"
      23             : #include "graphics/LightEnv.h"
      24             : #include "graphics/ShaderManager.h"
      25             : #include "gui/GUIMatrix.h"
      26             : #include "lib/bits.h"
      27             : #include "lib/ogl.h"
      28             : #include "maths/BoundingBoxAligned.h"
      29             : #include "maths/Brush.h"
      30             : #include "maths/Frustum.h"
      31             : #include "maths/MathUtil.h"
      32             : #include "maths/Matrix3D.h"
      33             : #include "ps/CLogger.h"
      34             : #include "ps/ConfigDB.h"
      35             : #include "ps/CStrInternStatic.h"
      36             : #include "ps/Profile.h"
      37             : #include "ps/VideoMode.h"
      38             : #include "renderer/backend/gl/Device.h"
      39             : #include "renderer/backend/gl/Texture.h"
      40             : #include "renderer/DebugRenderer.h"
      41             : #include "renderer/Renderer.h"
      42             : #include "renderer/RenderingOptions.h"
      43             : #include "renderer/SceneRenderer.h"
      44             : 
      45             : #include <array>
      46             : 
      47             : namespace
      48             : {
      49             : 
      50             : constexpr int MAX_CASCADE_COUNT = 4;
      51             : 
      52             : constexpr float DEFAULT_SHADOWS_CUTOFF_DISTANCE = 300.0f;
      53             : constexpr float DEFAULT_CASCADE_DISTANCE_RATIO = 1.7f;
      54             : 
      55             : } // anonymous namespace
      56             : 
      57             : /**
      58             :  * Struct ShadowMapInternals: Internal data for the ShadowMap implementation
      59             :  */
      60             : struct ShadowMapInternals
      61             : {
      62             :     std::unique_ptr<Renderer::Backend::GL::CFramebuffer> Framebuffer;
      63             :     std::unique_ptr<Renderer::Backend::GL::CTexture> Texture;
      64             : 
      65             :     // bit depth for the depth texture
      66             :     int DepthTextureBits;
      67             :     // width, height of shadow map
      68             :     int Width, Height;
      69             :     // Shadow map quality (-1 - Low, 0 - Medium, 1 - High, 2 - Very High)
      70             :     int QualityLevel;
      71             :     // used width, height of shadow map
      72             :     int EffectiveWidth, EffectiveHeight;
      73             : 
      74             :     // Transform world space into light space; calculated on SetupFrame
      75             :     CMatrix3D LightTransform;
      76             : 
      77             :     // transform light space into world space
      78             :     CMatrix3D InvLightTransform;
      79             :     CBoundingBoxAligned ShadowReceiverBound;
      80             : 
      81             :     int CascadeCount;
      82             :     float CascadeDistanceRatio;
      83             :     float ShadowsCutoffDistance;
      84             :     bool ShadowsCoverMap;
      85             : 
      86             :     struct Cascade
      87             :     {
      88             :         // transform light space into projected light space
      89             :         // in projected light space, the shadowbound box occupies the [-1..1] cube
      90             :         // calculated on BeginRender, after the final shadow bounds are known
      91             :         CMatrix3D LightProjection;
      92             :         float Distance;
      93             :         CBoundingBoxAligned FrustumBBAA;
      94             :         CBoundingBoxAligned ConvexBounds;
      95             :         CBoundingBoxAligned ShadowRenderBound;
      96             :         // Bounding box of shadowed objects in the light space.
      97             :         CBoundingBoxAligned ShadowCasterBound;
      98             :         // Transform world space into texture space of the shadow map;
      99             :         // calculated on BeginRender, after the final shadow bounds are known
     100             :         CMatrix3D TextureMatrix;
     101             :         // View port of the shadow texture where the cascade should be rendered.
     102             :         SViewPort ViewPort;
     103             :     };
     104             :     std::array<Cascade, MAX_CASCADE_COUNT> Cascades;
     105             : 
     106             :     // Camera transformed into light space
     107             :     CCamera LightspaceCamera;
     108             : 
     109             :     // Some drivers (at least some Intel Mesa ones) appear to handle alpha testing
     110             :     // incorrectly when the FBO has only a depth attachment.
     111             :     // When m_ShadowAlphaFix is true, we use DummyTexture to store a useless
     112             :     // alpha texture which is attached to the FBO as a workaround.
     113             :     std::unique_ptr<Renderer::Backend::GL::CTexture> DummyTexture;
     114             : 
     115             :     // Copy of renderer's standard view camera, saved between
     116             :     // BeginRender and EndRender while we replace it with the shadow camera
     117             :     CCamera SavedViewCamera;
     118             : 
     119             :     void CalculateShadowMatrices(const int cascade);
     120             :     void CreateTexture();
     121             :     void UpdateCascadesParameters();
     122             : };
     123             : 
     124           0 : void ShadowMapInternals::UpdateCascadesParameters()
     125             : {
     126           0 :     CascadeCount = 1;
     127           0 :     CFG_GET_VAL("shadowscascadecount", CascadeCount);
     128             : 
     129           0 :     if (CascadeCount < 1 || CascadeCount > MAX_CASCADE_COUNT || g_VideoMode.GetBackend() == CVideoMode::Backend::GL_ARB)
     130           0 :         CascadeCount = 1;
     131             : 
     132           0 :     ShadowsCoverMap = false;
     133           0 :     CFG_GET_VAL("shadowscovermap", ShadowsCoverMap);
     134           0 : }
     135             : 
     136           0 : void CalculateBoundsForCascade(
     137             :     const CCamera& camera, const CMatrix3D& lightTransform,
     138             :     const float nearPlane, const float farPlane, CBoundingBoxAligned* bbaa,
     139             :     CBoundingBoxAligned* frustumBBAA)
     140             : {
     141           0 :     frustumBBAA->SetEmpty();
     142             : 
     143             :     // We need to calculate a circumscribed sphere for the camera to
     144             :     // create a rotation stable bounding box.
     145           0 :     const CVector3D cameraIn = camera.m_Orientation.GetIn();
     146           0 :     const CVector3D cameraTranslation = camera.m_Orientation.GetTranslation();
     147           0 :     const CVector3D centerNear = cameraTranslation + cameraIn * nearPlane;
     148           0 :     const CVector3D centerDist = cameraTranslation + cameraIn * farPlane;
     149             : 
     150             :     // We can solve 3D problem in 2D space, because the frustum is
     151             :     // symmetric by 2 planes. Than means we can use only one corner
     152             :     // to find a circumscribed sphere.
     153           0 :     CCamera::Quad corners;
     154             : 
     155           0 :     camera.GetViewQuad(nearPlane, corners);
     156           0 :     for (CVector3D& corner : corners)
     157           0 :         corner = camera.GetOrientation().Transform(corner);
     158           0 :     const CVector3D cornerNear = corners[0];
     159           0 :     for (const CVector3D& corner : corners)
     160           0 :         *frustumBBAA += lightTransform.Transform(corner);
     161             : 
     162           0 :     camera.GetViewQuad(farPlane, corners);
     163           0 :     for (CVector3D& corner : corners)
     164           0 :         corner = camera.GetOrientation().Transform(corner);
     165           0 :     const CVector3D cornerDist = corners[0];
     166           0 :     for (const CVector3D& corner : corners)
     167           0 :         *frustumBBAA += lightTransform.Transform(corner);
     168             : 
     169             :     // We solve 2D case for the right trapezoid.
     170           0 :     const float firstBase = (cornerNear - centerNear).Length();
     171           0 :     const float secondBase = (cornerDist - centerDist).Length();
     172           0 :     const float height = (centerDist - centerNear).Length();
     173           0 :     const float distanceToCenter =
     174           0 :         (height * height + secondBase * secondBase - firstBase * firstBase) * 0.5f / height;
     175             : 
     176           0 :     CVector3D position = cameraTranslation + cameraIn * (nearPlane + distanceToCenter);
     177           0 :     const float radius = (cornerNear - position).Length();
     178             : 
     179             :     // We need to convert the bounding box to the light space.
     180           0 :     position = lightTransform.Rotate(position);
     181             : 
     182           0 :     const float insets = 0.2f;
     183           0 :     *bbaa = CBoundingBoxAligned(position, position);
     184           0 :     bbaa->Expand(radius);
     185           0 :     bbaa->Expand(insets);
     186           0 : }
     187             : 
     188           0 : ShadowMap::ShadowMap()
     189             : {
     190           0 :     m = new ShadowMapInternals;
     191           0 :     m->Framebuffer = 0;
     192           0 :     m->Width = 0;
     193           0 :     m->Height = 0;
     194           0 :     m->QualityLevel = 0;
     195           0 :     m->EffectiveWidth = 0;
     196           0 :     m->EffectiveHeight = 0;
     197           0 :     m->DepthTextureBits = 0;
     198             :     // DepthTextureBits: 24/32 are very much faster than 16, on GeForce 4 and FX;
     199             :     // but they're very much slower on Radeon 9800.
     200             :     // In both cases, the default (no specified depth) is fast, so we just use
     201             :     // that by default and hope it's alright. (Otherwise, we'd probably need to
     202             :     // do some kind of hardware detection to work out what to use.)
     203             : 
     204             :     // Avoid using uninitialised values in AddShadowedBound if SetupFrame wasn't called first
     205           0 :     m->LightTransform.SetIdentity();
     206             : 
     207           0 :     m->UpdateCascadesParameters();
     208           0 : }
     209             : 
     210           0 : ShadowMap::~ShadowMap()
     211             : {
     212           0 :     m->Framebuffer.reset();
     213           0 :     m->Texture.reset();
     214           0 :     m->DummyTexture.reset();
     215             : 
     216           0 :     delete m;
     217           0 : }
     218             : 
     219             : // Force the texture/buffer/etc to be recreated, particularly when the renderer's
     220             : // size has changed
     221           0 : void ShadowMap::RecreateTexture()
     222             : {
     223           0 :     m->Framebuffer.reset();
     224           0 :     m->Texture.reset();
     225           0 :     m->DummyTexture.reset();
     226             : 
     227           0 :     m->UpdateCascadesParameters();
     228             : 
     229             :     // (Texture will be constructed in next SetupFrame)
     230           0 : }
     231             : 
     232             : // SetupFrame: camera and light direction for this frame
     233           0 : void ShadowMap::SetupFrame(const CCamera& camera, const CVector3D& lightdir)
     234             : {
     235           0 :     if (!m->Texture)
     236           0 :         m->CreateTexture();
     237             : 
     238           0 :     CVector3D x(0, 1, 0), eyepos;
     239             : 
     240           0 :     CVector3D z = lightdir;
     241           0 :     z.Normalize();
     242           0 :     x -= z * z.Dot(x);
     243           0 :     if (x.Length() < 0.001)
     244             :     {
     245             :         // this is invoked if the camera and light directions almost coincide
     246             :         // assumption: light direction has a significant Z component
     247           0 :         x = CVector3D(1.0, 0.0, 0.0);
     248           0 :         x -= z * z.Dot(x);
     249             :     }
     250           0 :     x.Normalize();
     251           0 :     CVector3D y = z.Cross(x);
     252             : 
     253             :     // X axis perpendicular to light direction, flowing along with view direction
     254           0 :     m->LightTransform._11 = x.X;
     255           0 :     m->LightTransform._12 = x.Y;
     256           0 :     m->LightTransform._13 = x.Z;
     257             : 
     258             :     // Y axis perpendicular to light and view direction
     259           0 :     m->LightTransform._21 = y.X;
     260           0 :     m->LightTransform._22 = y.Y;
     261           0 :     m->LightTransform._23 = y.Z;
     262             : 
     263             :     // Z axis is in direction of light
     264           0 :     m->LightTransform._31 = z.X;
     265           0 :     m->LightTransform._32 = z.Y;
     266           0 :     m->LightTransform._33 = z.Z;
     267             : 
     268             :     // eye is at the origin of the coordinate system
     269           0 :     m->LightTransform._14 = -x.Dot(eyepos);
     270           0 :     m->LightTransform._24 = -y.Dot(eyepos);
     271           0 :     m->LightTransform._34 = -z.Dot(eyepos);
     272             : 
     273           0 :     m->LightTransform._41 = 0.0;
     274           0 :     m->LightTransform._42 = 0.0;
     275           0 :     m->LightTransform._43 = 0.0;
     276           0 :     m->LightTransform._44 = 1.0;
     277             : 
     278           0 :     m->LightTransform.GetInverse(m->InvLightTransform);
     279           0 :     m->ShadowReceiverBound.SetEmpty();
     280             : 
     281           0 :     m->LightspaceCamera = camera;
     282           0 :     m->LightspaceCamera.m_Orientation = m->LightTransform * camera.m_Orientation;
     283           0 :     m->LightspaceCamera.UpdateFrustum();
     284             : 
     285           0 :     m->ShadowsCutoffDistance = DEFAULT_SHADOWS_CUTOFF_DISTANCE;
     286           0 :     m->CascadeDistanceRatio = DEFAULT_CASCADE_DISTANCE_RATIO;
     287           0 :     CFG_GET_VAL("shadowscutoffdistance", m->ShadowsCutoffDistance);
     288           0 :     CFG_GET_VAL("shadowscascadedistanceratio", m->CascadeDistanceRatio);
     289           0 :     m->CascadeDistanceRatio = Clamp(m->CascadeDistanceRatio, 1.1f, 16.0f);
     290             : 
     291           0 :     m->Cascades[GetCascadeCount() - 1].Distance = m->ShadowsCutoffDistance;
     292           0 :     for (int cascade = GetCascadeCount() - 2; cascade >= 0; --cascade)
     293           0 :         m->Cascades[cascade].Distance = m->Cascades[cascade + 1].Distance / m->CascadeDistanceRatio;
     294             : 
     295           0 :     if (GetCascadeCount() == 1 || m->ShadowsCoverMap)
     296             :     {
     297           0 :         m->Cascades[0].ViewPort =
     298           0 :             SViewPort{1, 1, m->EffectiveWidth - 2, m->EffectiveHeight - 2};
     299           0 :         if (m->ShadowsCoverMap)
     300           0 :             m->Cascades[0].Distance = camera.GetFarPlane();
     301             :     }
     302             :     else
     303             :     {
     304           0 :         for (int cascade = 0; cascade < GetCascadeCount(); ++cascade)
     305             :         {
     306           0 :             const int offsetX = (cascade & 0x1) ? m->EffectiveWidth / 2 : 0;
     307           0 :             const int offsetY = (cascade & 0x2) ? m->EffectiveHeight / 2 : 0;
     308           0 :             m->Cascades[cascade].ViewPort =
     309           0 :                 SViewPort{offsetX + 1, offsetY + 1,
     310           0 :                 m->EffectiveWidth / 2 - 2, m->EffectiveHeight / 2 - 2};
     311             :         }
     312             :     }
     313             : 
     314           0 :     for (int cascadeIdx = 0; cascadeIdx < GetCascadeCount(); ++cascadeIdx)
     315             :     {
     316           0 :         ShadowMapInternals::Cascade& cascade = m->Cascades[cascadeIdx];
     317             : 
     318           0 :         const float nearPlane = cascadeIdx > 0 ?
     319           0 :             m->Cascades[cascadeIdx - 1].Distance : camera.GetNearPlane();
     320           0 :         const float farPlane = cascade.Distance;
     321             : 
     322           0 :         CalculateBoundsForCascade(camera, m->LightTransform,
     323             :             nearPlane, farPlane, &cascade.ConvexBounds, &cascade.FrustumBBAA);
     324           0 :         cascade.ShadowCasterBound.SetEmpty();
     325             :     }
     326           0 : }
     327             : 
     328             : // AddShadowedBound: add a world-space bounding box to the bounds of shadowed
     329             : // objects
     330           0 : void ShadowMap::AddShadowCasterBound(const int cascade, const CBoundingBoxAligned& bounds)
     331             : {
     332           0 :     CBoundingBoxAligned lightspacebounds;
     333             : 
     334           0 :     bounds.Transform(m->LightTransform, lightspacebounds);
     335           0 :     m->Cascades[cascade].ShadowCasterBound += lightspacebounds;
     336           0 : }
     337             : 
     338           0 : void ShadowMap::AddShadowReceiverBound(const CBoundingBoxAligned& bounds)
     339             : {
     340           0 :     CBoundingBoxAligned lightspacebounds;
     341             : 
     342           0 :     bounds.Transform(m->LightTransform, lightspacebounds);
     343           0 :     m->ShadowReceiverBound += lightspacebounds;
     344           0 : }
     345             : 
     346           0 : CFrustum ShadowMap::GetShadowCasterCullFrustum(const int cascade)
     347             : {
     348             :     // Get the bounds of all objects that can receive shadows
     349           0 :     CBoundingBoxAligned bound = m->ShadowReceiverBound;
     350             : 
     351             :     // Intersect with the camera frustum, so the shadow map doesn't have to get
     352             :     // stretched to cover the off-screen parts of large models
     353           0 :     bound.IntersectFrustumConservative(m->Cascades[cascade].FrustumBBAA.ToFrustum());
     354             : 
     355             :     // ShadowBound might have been empty to begin with, producing an empty result
     356           0 :     if (bound.IsEmpty())
     357             :     {
     358             :         // CFrustum can't easily represent nothingness, so approximate it with
     359             :         // a single point which won't match many objects
     360           0 :         bound += CVector3D(0.0f, 0.0f, 0.0f);
     361           0 :         return bound.ToFrustum();
     362             :     }
     363             : 
     364             :     // Extend the bounds a long way towards the light source, to encompass
     365             :     // all objects that might cast visible shadows.
     366             :     // (The exact constant was picked entirely arbitrarily.)
     367           0 :     bound[0].Z -= 1000.f;
     368             : 
     369           0 :     CFrustum frustum = bound.ToFrustum();
     370           0 :     frustum.Transform(m->InvLightTransform);
     371           0 :     return frustum;
     372             : }
     373             : 
     374             : // CalculateShadowMatrices: calculate required matrices for shadow map generation - the light's
     375             : // projection and transformation matrices
     376           0 : void ShadowMapInternals::CalculateShadowMatrices(const int cascade)
     377             : {
     378           0 :     CBoundingBoxAligned& shadowRenderBound = Cascades[cascade].ShadowRenderBound;
     379           0 :     shadowRenderBound = Cascades[cascade].ConvexBounds;
     380             : 
     381           0 :     if (ShadowsCoverMap)
     382             :     {
     383             :         // Start building the shadow map to cover all objects that will receive shadows
     384           0 :         CBoundingBoxAligned receiverBound = ShadowReceiverBound;
     385             : 
     386             :         // Intersect with the camera frustum, so the shadow map doesn't have to get
     387             :         // stretched to cover the off-screen parts of large models
     388           0 :         receiverBound.IntersectFrustumConservative(LightspaceCamera.GetFrustum());
     389             : 
     390             :         // Intersect with the shadow caster bounds, because there's no point
     391             :         // wasting space around the edges of the shadow map that we're not going
     392             :         // to draw into
     393           0 :         shadowRenderBound[0].X = std::max(receiverBound[0].X, Cascades[cascade].ShadowCasterBound[0].X);
     394           0 :         shadowRenderBound[0].Y = std::max(receiverBound[0].Y, Cascades[cascade].ShadowCasterBound[0].Y);
     395           0 :         shadowRenderBound[1].X = std::min(receiverBound[1].X, Cascades[cascade].ShadowCasterBound[1].X);
     396           0 :         shadowRenderBound[1].Y = std::min(receiverBound[1].Y, Cascades[cascade].ShadowCasterBound[1].Y);
     397             :     }
     398           0 :     else if (CascadeCount > 1)
     399             :     {
     400             :         // We need to offset the cascade to its place on the texture.
     401           0 :         const CVector3D size = (shadowRenderBound[1] - shadowRenderBound[0]) * 0.5f;
     402           0 :         if (!(cascade & 0x1))
     403           0 :             shadowRenderBound[1].X += size.X * 2.0f;
     404             :         else
     405           0 :             shadowRenderBound[0].X -= size.X * 2.0f;
     406           0 :         if (!(cascade & 0x2))
     407           0 :             shadowRenderBound[1].Y += size.Y * 2.0f;
     408             :         else
     409           0 :             shadowRenderBound[0].Y -= size.Y * 2.0f;
     410             :     }
     411             : 
     412             :     // Set the near and far planes to include just the shadow casters,
     413             :     // so we make full use of the depth texture's range. Add a bit of a
     414             :     // delta so we don't accidentally clip objects that are directly on
     415             :     // the planes.
     416           0 :     shadowRenderBound[0].Z = Cascades[cascade].ShadowCasterBound[0].Z - 2.f;
     417           0 :     shadowRenderBound[1].Z = Cascades[cascade].ShadowCasterBound[1].Z + 2.f;
     418             : 
     419             :     // Setup orthogonal projection (lightspace -> clip space) for shadowmap rendering
     420           0 :     CVector3D scale = shadowRenderBound[1] - shadowRenderBound[0];
     421           0 :     CVector3D shift = (shadowRenderBound[1] + shadowRenderBound[0]) * -0.5;
     422             : 
     423           0 :     if (scale.X < 1.0)
     424           0 :         scale.X = 1.0;
     425           0 :     if (scale.Y < 1.0)
     426           0 :         scale.Y = 1.0;
     427           0 :     if (scale.Z < 1.0)
     428           0 :         scale.Z = 1.0;
     429             : 
     430           0 :     scale.X = 2.0 / scale.X;
     431           0 :     scale.Y = 2.0 / scale.Y;
     432           0 :     scale.Z = 2.0 / scale.Z;
     433             : 
     434             :     // make sure a given world position falls on a consistent shadowmap texel fractional offset
     435           0 :     float offsetX = fmod(shadowRenderBound[0].X - LightTransform._14, 2.0f/(scale.X*EffectiveWidth));
     436           0 :     float offsetY = fmod(shadowRenderBound[0].Y - LightTransform._24, 2.0f/(scale.Y*EffectiveHeight));
     437             : 
     438           0 :     CMatrix3D& lightProjection = Cascades[cascade].LightProjection;
     439           0 :     lightProjection.SetZero();
     440           0 :     lightProjection._11 = scale.X;
     441           0 :     lightProjection._14 = (shift.X + offsetX) * scale.X;
     442           0 :     lightProjection._22 = scale.Y;
     443           0 :     lightProjection._24 = (shift.Y + offsetY) * scale.Y;
     444           0 :     lightProjection._33 = scale.Z;
     445           0 :     lightProjection._34 = shift.Z * scale.Z;
     446           0 :     lightProjection._44 = 1.0;
     447             : 
     448             :     // Calculate texture matrix by creating the clip space to texture coordinate matrix
     449             :     // and then concatenating all matrices that have been calculated so far
     450             : 
     451           0 :     float texscalex = scale.X * 0.5f * (float)EffectiveWidth / (float)Width;
     452           0 :     float texscaley = scale.Y * 0.5f * (float)EffectiveHeight / (float)Height;
     453           0 :     float texscalez = scale.Z * 0.5f;
     454             : 
     455           0 :     CMatrix3D lightToTex;
     456           0 :     lightToTex.SetZero();
     457           0 :     lightToTex._11 = texscalex;
     458           0 :     lightToTex._14 = (offsetX - shadowRenderBound[0].X) * texscalex;
     459           0 :     lightToTex._22 = texscaley;
     460           0 :     lightToTex._24 = (offsetY - shadowRenderBound[0].Y) * texscaley;
     461           0 :     lightToTex._33 = texscalez;
     462           0 :     lightToTex._34 = -shadowRenderBound[0].Z * texscalez;
     463           0 :     lightToTex._44 = 1.0;
     464             : 
     465           0 :     Cascades[cascade].TextureMatrix = lightToTex * LightTransform;
     466           0 : }
     467             : 
     468             : // Create the shadow map
     469           0 : void ShadowMapInternals::CreateTexture()
     470             : {
     471             :     // Cleanup
     472           0 :     Framebuffer.reset();
     473           0 :     Texture.reset();
     474           0 :     DummyTexture.reset();
     475             : 
     476           0 :     Renderer::Backend::GL::CDevice* backendDevice = g_VideoMode.GetBackendDevice();
     477             : 
     478           0 :     CFG_GET_VAL("shadowquality", QualityLevel);
     479             : 
     480             :     // Get shadow map size as next power of two up from view width/height.
     481           0 :     int shadowMapSize;
     482           0 :     switch (QualityLevel)
     483             :     {
     484             :     // Low
     485           0 :     case -1:
     486           0 :         shadowMapSize = 512;
     487           0 :         break;
     488             :     // High
     489           0 :     case 1:
     490           0 :         shadowMapSize = 2048;
     491           0 :         break;
     492             :     // Ultra
     493           0 :     case 2:
     494           0 :         shadowMapSize = std::max(round_up_to_pow2(std::max(g_Renderer.GetWidth(), g_Renderer.GetHeight())) * 4, 4096);
     495           0 :         break;
     496             :     // Medium as is
     497           0 :     default:
     498           0 :         shadowMapSize = 1024;
     499           0 :         break;
     500             :     }
     501             : 
     502             :     // Clamp to the maximum texture size.
     503           0 :     shadowMapSize = std::min(
     504           0 :         shadowMapSize, static_cast<int>(backendDevice->GetCapabilities().maxTextureSize));
     505             : 
     506           0 :     Width = Height = shadowMapSize;
     507             : 
     508             :     // Since we're using a framebuffer object, the whole texture is available
     509           0 :     EffectiveWidth = Width;
     510           0 :     EffectiveHeight = Height;
     511             : 
     512           0 :     const char* formatName;
     513           0 :     Renderer::Backend::Format backendFormat = Renderer::Backend::Format::UNDEFINED;
     514             : #if CONFIG2_GLES
     515             :     formatName = "DEPTH_COMPONENT";
     516             :     backendFormat = Renderer::Backend::Format::D24;
     517             : #else
     518           0 :     switch (DepthTextureBits)
     519             :     {
     520             :     case 16: formatName = "Format::D16"; backendFormat = Renderer::Backend::Format::D16; break;
     521           0 :     case 24: formatName = "Format::D24"; backendFormat = Renderer::Backend::Format::D24; break;
     522           0 :     case 32: formatName = "Format::D32"; backendFormat = Renderer::Backend::Format::D32;  break;
     523           0 :     default: formatName = "Format::D24"; backendFormat = Renderer::Backend::Format::D24; break;
     524             :     }
     525             : #endif
     526           0 :     ENSURE(formatName);
     527             : 
     528           0 :     LOGMESSAGE("Creating shadow texture (size %dx%d) (format = %s)",
     529             :         Width, Height, formatName);
     530             : 
     531           0 :     if (g_RenderingOptions.GetShadowAlphaFix())
     532             :     {
     533           0 :         DummyTexture = backendDevice->CreateTexture2D("ShadowMapDummy",
     534           0 :             Renderer::Backend::Format::R8G8B8A8_UNORM, Width, Height,
     535           0 :             Renderer::Backend::Sampler::MakeDefaultSampler(
     536             :                 Renderer::Backend::Sampler::Filter::NEAREST,
     537           0 :                 Renderer::Backend::Sampler::AddressMode::CLAMP_TO_EDGE));
     538             :     }
     539             : 
     540           0 :     Renderer::Backend::Sampler::Desc samplerDesc =
     541             :         Renderer::Backend::Sampler::MakeDefaultSampler(
     542             : #if CONFIG2_GLES
     543             :             // GLES doesn't do depth comparisons, so treat it as a
     544             :             // basic unfiltered depth texture
     545             :             Renderer::Backend::Sampler::Filter::NEAREST,
     546             : #else
     547             :             // Use LINEAR to trigger automatic PCF on some devices.
     548             :             Renderer::Backend::Sampler::Filter::LINEAR,
     549             : #endif
     550           0 :             Renderer::Backend::Sampler::AddressMode::CLAMP_TO_EDGE);
     551             :     // Enable automatic depth comparisons
     552           0 :     samplerDesc.compareEnabled = true;
     553           0 :     samplerDesc.compareOp = Renderer::Backend::CompareOp::LESS_OR_EQUAL;
     554             : 
     555           0 :     Texture = backendDevice->CreateTexture2D("ShadowMapDepth",
     556           0 :         backendFormat, Width, Height, samplerDesc);
     557             : 
     558           0 :     Framebuffer = backendDevice->CreateFramebuffer("ShadowMapFramebuffer",
     559           0 :         g_RenderingOptions.GetShadowAlphaFix() ? DummyTexture.get() : nullptr, Texture.get());
     560             : 
     561           0 :     if (!Framebuffer)
     562             :     {
     563           0 :         LOGERROR("Failed to create shadows framebuffer");
     564             : 
     565             :         // Disable shadow rendering (but let the user try again if they want).
     566           0 :         g_RenderingOptions.SetShadows(false);
     567             :     }
     568           0 : }
     569             : 
     570             : // Set up to render into shadow map texture
     571           0 : void ShadowMap::BeginRender()
     572             : {
     573           0 :     Renderer::Backend::GL::CDeviceCommandContext* deviceCommandContext =
     574           0 :         g_Renderer.GetDeviceCommandContext();
     575             : 
     576           0 :     {
     577           0 :         PROFILE("bind framebuffer");
     578           0 :         ENSURE(m->Framebuffer);
     579           0 :         deviceCommandContext->SetFramebuffer(m->Framebuffer.get());
     580             :     }
     581             : 
     582             :     // clear buffers
     583           0 :     {
     584           0 :         PROFILE("clear depth texture");
     585             :         // In case we used m_ShadowAlphaFix, we ought to clear the unused
     586             :         // color buffer too, else Mali 400 drivers get confused.
     587             :         // Might as well clear stencil too for completeness.
     588           0 :         deviceCommandContext->ClearFramebuffer();
     589             :     }
     590             : 
     591           0 :     m->SavedViewCamera = g_Renderer.GetSceneRenderer().GetViewCamera();
     592           0 : }
     593             : 
     594           0 : void ShadowMap::PrepareCamera(const int cascade)
     595             : {
     596           0 :     m->CalculateShadowMatrices(cascade);
     597             : 
     598           0 :     const SViewPort vp = { 0, 0, m->EffectiveWidth, m->EffectiveHeight };
     599           0 :     g_Renderer.SetViewport(vp);
     600             : 
     601           0 :     CCamera camera = m->SavedViewCamera;
     602           0 :     camera.SetProjection(m->Cascades[cascade].LightProjection);
     603           0 :     camera.GetOrientation() = m->InvLightTransform;
     604           0 :     g_Renderer.GetSceneRenderer().SetViewCamera(camera);
     605             : 
     606           0 :     const SViewPort& cascadeViewPort = m->Cascades[cascade].ViewPort;
     607           0 :     Renderer::Backend::GL::CDeviceCommandContext::Rect scissorRect;
     608           0 :     scissorRect.x = cascadeViewPort.m_X;
     609           0 :     scissorRect.y = cascadeViewPort.m_Y;
     610           0 :     scissorRect.width = cascadeViewPort.m_Width;
     611           0 :     scissorRect.height = cascadeViewPort.m_Height;
     612           0 :     g_Renderer.GetDeviceCommandContext()->SetScissors(1, &scissorRect);
     613           0 : }
     614             : 
     615             : // Finish rendering into shadow map texture
     616           0 : void ShadowMap::EndRender()
     617             : {
     618           0 :     g_Renderer.GetDeviceCommandContext()->SetScissors(0, nullptr);
     619             : 
     620           0 :     g_Renderer.GetSceneRenderer().SetViewCamera(m->SavedViewCamera);
     621             : 
     622           0 :     {
     623           0 :         PROFILE("unbind framebuffer");
     624           0 :         g_Renderer.GetDeviceCommandContext()->SetFramebuffer(
     625             :             g_VideoMode.GetBackendDevice()->GetCurrentBackbuffer());
     626             :     }
     627             : 
     628           0 :     const SViewPort vp = { 0, 0, g_Renderer.GetWidth(), g_Renderer.GetHeight() };
     629           0 :     g_Renderer.SetViewport(vp);
     630           0 : }
     631             : 
     632           0 : void ShadowMap::BindTo(const CShaderProgramPtr& shader) const
     633             : {
     634           0 :     if (!shader->GetTextureBinding(str_shadowTex).Active() || !m->Texture)
     635           0 :         return;
     636             : 
     637           0 :     shader->BindTexture(str_shadowTex, m->Texture.get());
     638           0 :     shader->Uniform(str_shadowScale, m->Width, m->Height, 1.0f / m->Width, 1.0f / m->Height);
     639           0 :     const CVector3D cameraForward = g_Renderer.GetSceneRenderer().GetCullCamera().GetOrientation().GetIn();
     640           0 :     shader->Uniform(str_cameraForward, cameraForward.X, cameraForward.Y, cameraForward.Z,
     641           0 :         cameraForward.Dot(g_Renderer.GetSceneRenderer().GetCullCamera().GetOrientation().GetTranslation()));
     642           0 :     if (GetCascadeCount() == 1)
     643             :     {
     644           0 :         shader->Uniform(str_shadowTransform, m->Cascades[0].TextureMatrix);
     645           0 :         shader->Uniform(str_shadowDistance, m->Cascades[0].Distance);
     646             :     }
     647             :     else
     648             :     {
     649           0 :         std::vector<float> shadowDistances;
     650           0 :         std::vector<CMatrix3D> shadowTransforms;
     651           0 :         for (const ShadowMapInternals::Cascade& cascade : m->Cascades)
     652             :         {
     653           0 :             shadowDistances.emplace_back(cascade.Distance);
     654           0 :             shadowTransforms.emplace_back(cascade.TextureMatrix);
     655             :         }
     656           0 :         shader->Uniform(str_shadowTransforms_0, GetCascadeCount(), shadowTransforms.data());
     657           0 :         shader->Uniform(str_shadowTransforms, GetCascadeCount(), shadowTransforms.data());
     658           0 :         shader->Uniform(str_shadowDistances_0, GetCascadeCount(), shadowDistances.data());
     659           0 :         shader->Uniform(str_shadowDistances, GetCascadeCount(), shadowDistances.data());
     660             :     }
     661             : }
     662             : 
     663             : // Depth texture bits
     664           0 : int ShadowMap::GetDepthTextureBits() const
     665             : {
     666           0 :     return m->DepthTextureBits;
     667             : }
     668             : 
     669           0 : void ShadowMap::SetDepthTextureBits(int bits)
     670             : {
     671           0 :     if (bits != m->DepthTextureBits)
     672             :     {
     673           0 :         m->Texture.reset();
     674           0 :         m->Width = m->Height = 0;
     675             : 
     676           0 :         m->DepthTextureBits = bits;
     677             :     }
     678           0 : }
     679             : 
     680           0 : void ShadowMap::RenderDebugBounds()
     681             : {
     682             :     // Render various shadow bounds:
     683             :     //  Yellow = bounds of objects in view frustum that receive shadows
     684             :     //  Red = culling frustum used to find potential shadow casters
     685             :     //  Blue = frustum used for rendering the shadow map
     686             : 
     687           0 :     const CMatrix3D transform = g_Renderer.GetSceneRenderer().GetViewCamera().GetViewProjection() * m->InvLightTransform;
     688             : 
     689           0 :     g_Renderer.GetDebugRenderer().DrawBoundingBox(
     690           0 :         m->ShadowReceiverBound, CColor(1.0f, 1.0f, 0.0f, 1.0f), transform, true);
     691             : 
     692           0 :     for (int cascade = 0; cascade < GetCascadeCount(); ++cascade)
     693             :     {
     694           0 :         g_Renderer.GetDebugRenderer().DrawBoundingBox(
     695           0 :             m->Cascades[cascade].ShadowRenderBound, CColor(0.0f, 0.0f, 1.0f, 0.10f), transform);
     696           0 :         g_Renderer.GetDebugRenderer().DrawBoundingBox(
     697           0 :             m->Cascades[cascade].ShadowRenderBound, CColor(0.0f, 0.0f, 1.0f, 0.5f), transform, true);
     698             : 
     699           0 :         const CFrustum frustum = GetShadowCasterCullFrustum(cascade);
     700             :         // We don't have a function to create a brush directly from a frustum, so use
     701             :         // the ugly approach of creating a large cube and then intersecting with the frustum
     702           0 :         const CBoundingBoxAligned dummy(CVector3D(-1e4, -1e4, -1e4), CVector3D(1e4, 1e4, 1e4));
     703           0 :         CBrush brush(dummy);
     704           0 :         CBrush frustumBrush;
     705           0 :         brush.Intersect(frustum, frustumBrush);
     706             : 
     707           0 :         g_Renderer.GetDebugRenderer().DrawBrush(frustumBrush, CColor(1.0f, 0.0f, 0.0f, 0.1f));
     708           0 :         g_Renderer.GetDebugRenderer().DrawBrush(frustumBrush, CColor(1.0f, 0.0f, 0.0f, 0.1f), true);
     709             :     }
     710             : 
     711           0 :     ogl_WarnIfError();
     712           0 : }
     713             : 
     714           0 : void ShadowMap::RenderDebugTexture(
     715             :     Renderer::Backend::GL::CDeviceCommandContext* deviceCommandContext)
     716             : {
     717           0 :     if (!m->Texture)
     718           0 :         return;
     719             : 
     720             : #if !CONFIG2_GLES
     721           0 :     deviceCommandContext->BindTexture(0, GL_TEXTURE_2D, m->Texture->GetHandle());
     722           0 :     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_NONE);
     723             : #endif
     724             : 
     725           0 :     CShaderTechniquePtr texTech = g_Renderer.GetShaderManager().LoadEffect(str_canvas2d);
     726           0 :     texTech->BeginPass();
     727           0 :     deviceCommandContext->SetGraphicsPipelineState(
     728             :         texTech->GetGraphicsPipelineStateDesc());
     729             : 
     730           0 :     const CShaderProgramPtr& texShader = texTech->GetShader();
     731             : 
     732           0 :     texShader->Uniform(str_transform, GetDefaultGuiMatrix());
     733           0 :     texShader->BindTexture(str_tex, m->Texture.get());
     734           0 :     texShader->Uniform(str_colorAdd, CColor(0.0f, 0.0f, 0.0f, 1.0f));
     735           0 :     texShader->Uniform(str_colorMul, CColor(1.0f, 1.0f, 1.0f, 0.0f));
     736           0 :     texShader->Uniform(str_grayscaleFactor, 0.0f);
     737             : 
     738           0 :     float s = 256.f;
     739           0 :     float boxVerts[] =
     740             :     {
     741             :         0,0, 0,s, s,0,
     742             :         s,0, 0,s, s,s
     743           0 :     };
     744           0 :     float boxUV[] =
     745             :     {
     746             :         0,0, 0,1, 1,0,
     747             :         1,0, 0,1, 1,1
     748             :     };
     749             : 
     750           0 :     texShader->VertexPointer(
     751           0 :         Renderer::Backend::Format::R32G32_SFLOAT, 0, boxVerts);
     752           0 :     texShader->TexCoordPointer(
     753           0 :         GL_TEXTURE0, Renderer::Backend::Format::R32G32_SFLOAT, 0, boxUV);
     754           0 :     texShader->AssertPointersBound();
     755           0 :     deviceCommandContext->Draw(0, 6);
     756             : 
     757           0 :     texTech->EndPass();
     758             : 
     759             : #if !CONFIG2_GLES
     760           0 :     deviceCommandContext->BindTexture(0, GL_TEXTURE_2D, m->Texture->GetHandle());
     761           0 :     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE);
     762             : #endif
     763             : 
     764           0 :     ogl_WarnIfError();
     765             : }
     766             : 
     767           0 : int ShadowMap::GetCascadeCount() const
     768             : {
     769             : #if CONFIG2_GLES
     770             :     return 1;
     771             : #else
     772           0 :     return m->ShadowsCoverMap ? 1 : m->CascadeCount;
     773             : #endif
     774             : }

Generated by: LCOV version 1.13