LCOV - code coverage report
Current view: top level - source/renderer - SceneRenderer.cpp (source / functions) Hit Total Coverage
Test: 0 A.D. test coverage report Lines: 25 547 4.6 %
Date: 2023-01-19 00:18:29 Functions: 10 54 18.5 %

          Line data    Source code
       1             : /* Copyright (C) 2023 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 "SceneRenderer.h"
      21             : 
      22             : #include "graphics/Camera.h"
      23             : #include "graphics/Decal.h"
      24             : #include "graphics/GameView.h"
      25             : #include "graphics/LightEnv.h"
      26             : #include "graphics/LOSTexture.h"
      27             : #include "graphics/MaterialManager.h"
      28             : #include "graphics/MiniMapTexture.h"
      29             : #include "graphics/Model.h"
      30             : #include "graphics/ModelDef.h"
      31             : #include "graphics/ParticleManager.h"
      32             : #include "graphics/Patch.h"
      33             : #include "graphics/ShaderManager.h"
      34             : #include "graphics/TerritoryTexture.h"
      35             : #include "graphics/Terrain.h"
      36             : #include "graphics/Texture.h"
      37             : #include "graphics/TextureManager.h"
      38             : #include "maths/Matrix3D.h"
      39             : #include "maths/MathUtil.h"
      40             : #include "ps/CLogger.h"
      41             : #include "ps/ConfigDB.h"
      42             : #include "ps/CStrInternStatic.h"
      43             : #include "ps/Game.h"
      44             : #include "ps/Profile.h"
      45             : #include "ps/VideoMode.h"
      46             : #include "ps/World.h"
      47             : #include "renderer/backend/IDevice.h"
      48             : #include "renderer/DebugRenderer.h"
      49             : #include "renderer/HWLightingModelRenderer.h"
      50             : #include "renderer/InstancingModelRenderer.h"
      51             : #include "renderer/ModelRenderer.h"
      52             : #include "renderer/OverlayRenderer.h"
      53             : #include "renderer/ParticleRenderer.h"
      54             : #include "renderer/Renderer.h"
      55             : #include "renderer/RenderingOptions.h"
      56             : #include "renderer/RenderModifiers.h"
      57             : #include "renderer/ShadowMap.h"
      58             : #include "renderer/SilhouetteRenderer.h"
      59             : #include "renderer/SkyManager.h"
      60             : #include "renderer/TerrainOverlay.h"
      61             : #include "renderer/TerrainRenderer.h"
      62             : #include "renderer/WaterManager.h"
      63             : 
      64             : #include <algorithm>
      65             : 
      66             : struct SScreenRect
      67             : {
      68             :     int x1, y1, x2, y2;
      69             : };
      70             : 
      71             : /**
      72             :  * Struct CSceneRendererInternals: Truly hide data that is supposed to be hidden
      73             :  * in this structure so it won't even appear in header files.
      74             :  */
      75             : class CSceneRenderer::Internals
      76             : {
      77             :     NONCOPYABLE(Internals);
      78             : public:
      79           6 :     Internals() = default;
      80           6 :     ~Internals() = default;
      81             : 
      82             :     /// Water manager
      83             :     WaterManager waterManager;
      84             : 
      85             :     /// Sky manager
      86             :     SkyManager skyManager;
      87             : 
      88             :     /// Terrain renderer
      89             :     TerrainRenderer terrainRenderer;
      90             : 
      91             :     /// Overlay renderer
      92             :     OverlayRenderer overlayRenderer;
      93             : 
      94             :     /// Particle manager
      95             :     CParticleManager particleManager;
      96             : 
      97             :     /// Particle renderer
      98             :     ParticleRenderer particleRenderer;
      99             : 
     100             :     /// Material manager
     101             :     CMaterialManager materialManager;
     102             : 
     103             :     /// Shadow map
     104             :     ShadowMap shadow;
     105             : 
     106             :     SilhouetteRenderer silhouetteRenderer;
     107             : 
     108             :     /// Various model renderers
     109          12 :     struct Models
     110             :     {
     111             :         // NOTE: The current renderer design (with ModelRenderer, ModelVertexRenderer,
     112             :         // RenderModifier, etc) is mostly a relic of an older design that implemented
     113             :         // the different materials and rendering modes through extensive subclassing
     114             :         // and hooking objects together in various combinations.
     115             :         // The new design uses the CShaderManager API to abstract away the details
     116             :         // of rendering, and uses a data-driven approach to materials, so there are
     117             :         // now a small number of generic subclasses instead of many specialised subclasses,
     118             :         // but most of the old infrastructure hasn't been refactored out yet and leads to
     119             :         // some unwanted complexity.
     120             : 
     121             :         // Submitted models are split on two axes:
     122             :         //  - Normal vs Transp[arent] - alpha-blended models are stored in a separate
     123             :         //    list so we can draw them above/below the alpha-blended water plane correctly
     124             :         //  - Skinned vs Unskinned - with hardware lighting we don't need to
     125             :         //    duplicate mesh data per model instance (except for skinned models),
     126             :         //    so non-skinned models get different ModelVertexRenderers
     127             : 
     128             :         ModelRendererPtr NormalSkinned;
     129             :         ModelRendererPtr NormalUnskinned; // == NormalSkinned if unskinned shader instancing not supported
     130             :         ModelRendererPtr TranspSkinned;
     131             :         ModelRendererPtr TranspUnskinned; // == TranspSkinned if unskinned shader instancing not supported
     132             : 
     133             :         ModelVertexRendererPtr VertexRendererShader;
     134             :         ModelVertexRendererPtr VertexInstancingShader;
     135             :         ModelVertexRendererPtr VertexGPUSkinningShader;
     136             : 
     137             :         LitRenderModifierPtr ModShader;
     138             :     } Model;
     139             : 
     140             :     CShaderDefines globalContext;
     141             : 
     142             :     /**
     143             :      * Renders all non-alpha-blended models with the given context.
     144             :      */
     145           0 :     void CallModelRenderers(
     146             :         Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
     147             :         const CShaderDefines& context, int cullGroup, int flags)
     148             :     {
     149           0 :         CShaderDefines contextSkinned = context;
     150           0 :         if (g_RenderingOptions.GetGPUSkinning())
     151             :         {
     152           0 :             contextSkinned.Add(str_USE_INSTANCING, str_1);
     153           0 :             contextSkinned.Add(str_USE_GPU_SKINNING, str_1);
     154             :         }
     155           0 :         Model.NormalSkinned->Render(deviceCommandContext, Model.ModShader, contextSkinned, cullGroup, flags);
     156             : 
     157           0 :         if (Model.NormalUnskinned != Model.NormalSkinned)
     158             :         {
     159           0 :             CShaderDefines contextUnskinned = context;
     160           0 :             contextUnskinned.Add(str_USE_INSTANCING, str_1);
     161           0 :             Model.NormalUnskinned->Render(deviceCommandContext, Model.ModShader, contextUnskinned, cullGroup, flags);
     162             :         }
     163           0 :     }
     164             : 
     165             :     /**
     166             :      * Renders all alpha-blended models with the given context.
     167             :      */
     168           0 :     void CallTranspModelRenderers(
     169             :         Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
     170             :         const CShaderDefines& context, int cullGroup, int flags)
     171             :     {
     172           0 :         CShaderDefines contextSkinned = context;
     173           0 :         if (g_RenderingOptions.GetGPUSkinning())
     174             :         {
     175           0 :             contextSkinned.Add(str_USE_INSTANCING, str_1);
     176           0 :             contextSkinned.Add(str_USE_GPU_SKINNING, str_1);
     177             :         }
     178           0 :         Model.TranspSkinned->Render(deviceCommandContext, Model.ModShader, contextSkinned, cullGroup, flags);
     179             : 
     180           0 :         if (Model.TranspUnskinned != Model.TranspSkinned)
     181             :         {
     182           0 :             CShaderDefines contextUnskinned = context;
     183           0 :             contextUnskinned.Add(str_USE_INSTANCING, str_1);
     184           0 :             Model.TranspUnskinned->Render(deviceCommandContext, Model.ModShader, contextUnskinned, cullGroup, flags);
     185             :         }
     186           0 :     }
     187             : };
     188             : 
     189           6 : CSceneRenderer::CSceneRenderer()
     190             : {
     191           6 :     m = std::make_unique<Internals>();
     192             : 
     193           6 :     m_TerrainRenderMode = SOLID;
     194           6 :     m_WaterRenderMode = SOLID;
     195           6 :     m_ModelRenderMode = SOLID;
     196           6 :     m_OverlayRenderMode = SOLID;
     197             : 
     198           6 :     m_DisplayTerrainPriorities = false;
     199             : 
     200           6 :     m_LightEnv = nullptr;
     201             : 
     202           6 :     m_CurrentScene = nullptr;
     203           6 : }
     204             : 
     205          12 : CSceneRenderer::~CSceneRenderer()
     206             : {
     207             :     // We no longer UnloadWaterTextures here -
     208             :     // that is the responsibility of the module that asked for
     209             :     // them to be loaded (i.e. CGameView).
     210           6 :     m.reset();
     211           6 : }
     212             : 
     213           0 : void CSceneRenderer::ReloadShaders()
     214             : {
     215           0 :     m->globalContext = CShaderDefines();
     216             : 
     217           0 :     Renderer::Backend::IDevice* device = g_VideoMode.GetBackendDevice();
     218             : 
     219           0 :     if (g_RenderingOptions.GetShadows())
     220             :     {
     221           0 :         m->globalContext.Add(str_USE_SHADOW, str_1);
     222           0 :         if (device->GetBackend() == Renderer::Backend::Backend::GL_ARB &&
     223           0 :             device->GetCapabilities().ARBShadersShadow)
     224             :         {
     225           0 :             m->globalContext.Add(str_USE_FP_SHADOW, str_1);
     226             :         }
     227           0 :         if (g_RenderingOptions.GetShadowPCF())
     228           0 :             m->globalContext.Add(str_USE_SHADOW_PCF, str_1);
     229           0 :         const int cascadeCount = m->shadow.GetCascadeCount();
     230           0 :         ENSURE(1 <= cascadeCount && cascadeCount <= 4);
     231           0 :         const CStrIntern cascadeCountStr[5] = {str_0, str_1, str_2, str_3, str_4};
     232           0 :         m->globalContext.Add(str_SHADOWS_CASCADE_COUNT, cascadeCountStr[cascadeCount]);
     233             : #if !CONFIG2_GLES
     234           0 :         m->globalContext.Add(str_USE_SHADOW_SAMPLER, str_1);
     235             : #endif
     236             :     }
     237             : 
     238           0 :     m->globalContext.Add(str_RENDER_DEBUG_MODE,
     239             :         RenderDebugModeEnum::ToString(g_RenderingOptions.GetRenderDebugMode()));
     240             : 
     241           0 :     if (device->GetBackend() != Renderer::Backend::Backend::GL_ARB && g_RenderingOptions.GetFog())
     242           0 :         m->globalContext.Add(str_USE_FOG, str_1);
     243             : 
     244           0 :     m->Model.ModShader = LitRenderModifierPtr(new ShaderRenderModifier());
     245             : 
     246           0 :     ENSURE(g_RenderingOptions.GetRenderPath() != RenderPath::FIXED);
     247           0 :     m->Model.VertexRendererShader = ModelVertexRendererPtr(new ShaderModelVertexRenderer());
     248           0 :     m->Model.VertexInstancingShader = ModelVertexRendererPtr(new InstancingModelRenderer(false, device->GetBackend() != Renderer::Backend::Backend::GL_ARB));
     249             : 
     250           0 :     if (g_RenderingOptions.GetGPUSkinning()) // TODO: should check caps and GLSL etc too
     251             :     {
     252           0 :         m->Model.VertexGPUSkinningShader = ModelVertexRendererPtr(new InstancingModelRenderer(true, device->GetBackend() != Renderer::Backend::Backend::GL_ARB));
     253           0 :         m->Model.NormalSkinned = ModelRendererPtr(new ShaderModelRenderer(m->Model.VertexGPUSkinningShader));
     254           0 :         m->Model.TranspSkinned = ModelRendererPtr(new ShaderModelRenderer(m->Model.VertexGPUSkinningShader));
     255             :     }
     256             :     else
     257             :     {
     258           0 :         m->Model.VertexGPUSkinningShader.reset();
     259           0 :         m->Model.NormalSkinned = ModelRendererPtr(new ShaderModelRenderer(m->Model.VertexRendererShader));
     260           0 :         m->Model.TranspSkinned = ModelRendererPtr(new ShaderModelRenderer(m->Model.VertexRendererShader));
     261             :     }
     262             : 
     263           0 :     m->Model.NormalUnskinned = ModelRendererPtr(new ShaderModelRenderer(m->Model.VertexInstancingShader));
     264           0 :     m->Model.TranspUnskinned = ModelRendererPtr(new ShaderModelRenderer(m->Model.VertexInstancingShader));
     265           0 : }
     266             : 
     267           6 : void CSceneRenderer::Initialize()
     268             : {
     269             :     // Let component renderers perform one-time initialization after graphics capabilities and
     270             :     // the shader path have been determined.
     271           6 :     m->waterManager.Initialize();
     272           6 :     m->terrainRenderer.Initialize();
     273           6 :     m->overlayRenderer.Initialize();
     274           6 : }
     275             : 
     276             : // resize renderer view
     277           0 : void CSceneRenderer::Resize(int UNUSED(width), int UNUSED(height))
     278             : {
     279             :     // need to recreate the shadow map object to resize the shadow texture
     280           0 :     m->shadow.RecreateTexture();
     281             : 
     282           0 :     m->waterManager.RecreateOrLoadTexturesIfNeeded();
     283           0 : }
     284             : 
     285           0 : void CSceneRenderer::BeginFrame()
     286             : {
     287             :     // choose model renderers for this frame
     288           0 :     m->Model.ModShader->SetShadowMap(&m->shadow);
     289           0 :     m->Model.ModShader->SetLightEnv(m_LightEnv);
     290           0 : }
     291             : 
     292           0 : void CSceneRenderer::SetSimulation(CSimulation2* simulation)
     293             : {
     294             :     // set current simulation context for terrain renderer
     295           0 :     m->terrainRenderer.SetSimulation(simulation);
     296           0 : }
     297             : 
     298           0 : void CSceneRenderer::RenderShadowMap(
     299             :     Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
     300             :     const CShaderDefines& context)
     301             : {
     302           0 :     PROFILE3_GPU("shadow map");
     303           0 :     GPU_SCOPED_LABEL(deviceCommandContext, "Render shadow map");
     304             : 
     305           0 :     CShaderDefines shadowsContext = context;
     306           0 :     shadowsContext.Add(str_PASS_SHADOWS, str_1);
     307             : 
     308           0 :     CShaderDefines contextCast = shadowsContext;
     309           0 :     contextCast.Add(str_MODE_SHADOWCAST, str_1);
     310             : 
     311           0 :     m->shadow.BeginRender(deviceCommandContext);
     312             : 
     313           0 :     const int cascadeCount = m->shadow.GetCascadeCount();
     314           0 :     ENSURE(0 <= cascadeCount && cascadeCount <= 4);
     315           0 :     for (int cascade = 0; cascade < cascadeCount; ++cascade)
     316             :     {
     317           0 :         m->shadow.PrepareCamera(deviceCommandContext, cascade);
     318             : 
     319           0 :         const int cullGroup = CULL_SHADOWS_CASCADE_0 + cascade;
     320             :         {
     321           0 :             PROFILE("render patches");
     322           0 :             m->terrainRenderer.RenderPatches(deviceCommandContext, cullGroup, shadowsContext);
     323             :         }
     324             : 
     325             :         {
     326           0 :             PROFILE("render models");
     327           0 :             m->CallModelRenderers(deviceCommandContext, contextCast, cullGroup, MODELFLAG_CASTSHADOWS);
     328             :         }
     329             : 
     330             :         {
     331           0 :             PROFILE("render transparent models");
     332           0 :             m->CallTranspModelRenderers(deviceCommandContext, contextCast, cullGroup, MODELFLAG_CASTSHADOWS);
     333             :         }
     334             :     }
     335             : 
     336           0 :     m->shadow.EndRender(deviceCommandContext);
     337           0 : }
     338             : 
     339           0 : void CSceneRenderer::RenderPatches(
     340             :     Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
     341             :     const CShaderDefines& context, int cullGroup)
     342             : {
     343           0 :     PROFILE3_GPU("patches");
     344           0 :     GPU_SCOPED_LABEL(deviceCommandContext, "Render patches");
     345             : 
     346             :     // Switch on wireframe if we need it.
     347           0 :     CShaderDefines localContext = context;
     348           0 :     if (m_TerrainRenderMode == WIREFRAME)
     349           0 :         localContext.Add(str_MODE_WIREFRAME, str_1);
     350             : 
     351             :     // Render all the patches, including blend pass.
     352           0 :     m->terrainRenderer.RenderTerrainShader(deviceCommandContext, localContext, cullGroup,
     353           0 :         g_RenderingOptions.GetShadows() ? &m->shadow : nullptr);
     354             : 
     355           0 :     if (m_TerrainRenderMode == EDGED_FACES)
     356             :     {
     357           0 :         localContext.Add(str_MODE_WIREFRAME, str_1);
     358             :         // Edged faces: need to make a second pass over the data.
     359             : 
     360             :         // Render tiles edges.
     361           0 :         m->terrainRenderer.RenderPatches(
     362           0 :             deviceCommandContext, cullGroup, localContext, CColor(0.5f, 0.5f, 1.0f, 1.0f));
     363             : 
     364             :         // Render outline of each patch.
     365           0 :         m->terrainRenderer.RenderOutlines(deviceCommandContext, cullGroup);
     366             :     }
     367           0 : }
     368             : 
     369           0 : void CSceneRenderer::RenderModels(
     370             :     Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
     371             :     const CShaderDefines& context, int cullGroup)
     372             : {
     373           0 :     PROFILE3_GPU("models");
     374           0 :     GPU_SCOPED_LABEL(deviceCommandContext, "Render models");
     375             : 
     376           0 :     int flags = 0;
     377             : 
     378           0 :     CShaderDefines localContext = context;
     379             : 
     380           0 :     if (m_ModelRenderMode == WIREFRAME)
     381           0 :         localContext.Add(str_MODE_WIREFRAME, str_1);
     382             : 
     383           0 :     m->CallModelRenderers(deviceCommandContext, localContext, cullGroup, flags);
     384             : 
     385           0 :     if (m_ModelRenderMode == EDGED_FACES)
     386             :     {
     387           0 :         localContext.Add(str_MODE_WIREFRAME_SOLID, str_1);
     388           0 :         m->CallModelRenderers(deviceCommandContext, localContext, cullGroup, flags);
     389             :     }
     390           0 : }
     391             : 
     392           0 : void CSceneRenderer::RenderTransparentModels(
     393             :     Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
     394             :     const CShaderDefines& context, int cullGroup, ETransparentMode transparentMode)
     395             : {
     396           0 :     PROFILE3_GPU("transparent models");
     397           0 :     GPU_SCOPED_LABEL(deviceCommandContext, "Render transparent models");
     398             : 
     399           0 :     int flags = 0;
     400             : 
     401           0 :     CShaderDefines contextOpaque = context;
     402           0 :     contextOpaque.Add(str_ALPHABLEND_PASS_OPAQUE, str_1);
     403             : 
     404           0 :     CShaderDefines contextBlend = context;
     405           0 :     contextBlend.Add(str_ALPHABLEND_PASS_BLEND, str_1);
     406             : 
     407           0 :     if (m_ModelRenderMode == WIREFRAME)
     408             :     {
     409           0 :         contextOpaque.Add(str_MODE_WIREFRAME, str_1);
     410           0 :         contextBlend.Add(str_MODE_WIREFRAME, str_1);
     411             :     }
     412             : 
     413           0 :     if (transparentMode == TRANSPARENT || transparentMode == TRANSPARENT_OPAQUE)
     414           0 :         m->CallTranspModelRenderers(deviceCommandContext, contextOpaque, cullGroup, flags);
     415             : 
     416           0 :     if (transparentMode == TRANSPARENT || transparentMode == TRANSPARENT_BLEND)
     417           0 :         m->CallTranspModelRenderers(deviceCommandContext, contextBlend, cullGroup, flags);
     418             : 
     419           0 :     if (m_ModelRenderMode == EDGED_FACES)
     420             :     {
     421           0 :         CShaderDefines contextWireframe = contextOpaque;
     422           0 :         contextWireframe.Add(str_MODE_WIREFRAME, str_1);
     423             : 
     424           0 :         m->CallTranspModelRenderers(deviceCommandContext, contextWireframe, cullGroup, flags);
     425             :     }
     426           0 : }
     427             : 
     428             : // SetObliqueFrustumClipping: change the near plane to the given clip plane (in world space)
     429             : // Based on code from Game Programming Gems 5, from http://www.terathon.com/code/oblique.html
     430             : // - worldPlane is a clip plane in world space (worldPlane.Dot(v) >= 0 for any vector v passing the clipping test)
     431           0 : void CSceneRenderer::SetObliqueFrustumClipping(CCamera& camera, const CVector4D& worldPlane) const
     432             : {
     433             :     // First, we'll convert the given clip plane to camera space, then we'll
     434             :     // Get the view matrix and normal matrix (top 3x3 part of view matrix)
     435           0 :     CMatrix3D normalMatrix = camera.GetOrientation().GetTranspose();
     436           0 :     CVector4D camPlane = normalMatrix.Transform(worldPlane);
     437             : 
     438           0 :     CMatrix3D matrix = camera.GetProjection();
     439             : 
     440             :     // Calculate the clip-space corner point opposite the clipping plane
     441             :     // as (sgn(camPlane.x), sgn(camPlane.y), 1, 1) and
     442             :     // transform it into camera space by multiplying it
     443             :     // by the inverse of the projection matrix
     444             : 
     445           0 :     CVector4D q;
     446           0 :     q.X = (Sign(camPlane.X) - matrix[8] / matrix[11]) / matrix[0];
     447           0 :     q.Y = (Sign(camPlane.Y) - matrix[9] / matrix[11]) / matrix[5];
     448           0 :     q.Z = 1.0f / matrix[11];
     449           0 :     q.W = (1.0f - matrix[10] / matrix[11]) / matrix[14];
     450             : 
     451             :     // Calculate the scaled plane vector
     452           0 :     CVector4D c = camPlane * (2.0f * matrix[11] / camPlane.Dot(q));
     453             : 
     454             :     // Replace the third row of the projection matrix
     455           0 :     matrix[2] = c.X;
     456           0 :     matrix[6] = c.Y;
     457           0 :     matrix[10] = c.Z - matrix[11];
     458           0 :     matrix[14] = c.W;
     459             : 
     460             :     // Load it back into the camera
     461           0 :     camera.SetProjection(matrix);
     462           0 : }
     463             : 
     464           0 : void CSceneRenderer::ComputeReflectionCamera(CCamera& camera, const CBoundingBoxAligned& scissor) const
     465             : {
     466           0 :     WaterManager& wm = m->waterManager;
     467             : 
     468           0 :     CMatrix3D projection;
     469           0 :     if (m_ViewCamera.GetProjectionType() == CCamera::ProjectionType::PERSPECTIVE)
     470             :     {
     471           0 :         const float aspectRatio = 1.0f;
     472             :         // Expand fov slightly since ripples can reflect parts of the scene that
     473             :         // are slightly outside the normal camera view, and we want to avoid any
     474             :         // noticeable edge-filtering artifacts
     475           0 :         projection.SetPerspective(m_ViewCamera.GetFOV() * 1.05f, aspectRatio, m_ViewCamera.GetNearPlane(), m_ViewCamera.GetFarPlane());
     476             :     }
     477             :     else
     478           0 :         projection = m_ViewCamera.GetProjection();
     479             : 
     480           0 :     camera = m_ViewCamera;
     481             : 
     482             :     // Temporarily change the camera to one that is reflected.
     483             :     // Also, for texturing purposes, make it render to a view port the size of the
     484             :     // water texture, stretch the image according to our aspect ratio so it covers
     485             :     // the whole screen despite being rendered into a square, and cover slightly more
     486             :     // of the view so we can see wavy reflections of slightly off-screen objects.
     487           0 :     camera.m_Orientation.Scale(1, -1, 1);
     488           0 :     camera.m_Orientation.Translate(0, 2 * wm.m_WaterHeight, 0);
     489           0 :     camera.UpdateFrustum(scissor);
     490             :     // Clip slightly above the water to improve reflections of objects on the water
     491             :     // when the reflections are distorted.
     492           0 :     camera.ClipFrustum(CVector4D(0, 1, 0, -wm.m_WaterHeight + 2.0f));
     493             : 
     494             :     SViewPort vp;
     495           0 :     vp.m_Height = wm.m_RefTextureSize;
     496           0 :     vp.m_Width = wm.m_RefTextureSize;
     497           0 :     vp.m_X = 0;
     498           0 :     vp.m_Y = 0;
     499           0 :     camera.SetViewPort(vp);
     500           0 :     camera.SetProjection(projection);
     501           0 :     CMatrix3D scaleMat;
     502           0 :     scaleMat.SetScaling(g_Renderer.GetHeight() / static_cast<float>(std::max(1, g_Renderer.GetWidth())), 1.0f, 1.0f);
     503           0 :     camera.SetProjection(scaleMat * camera.GetProjection());
     504             : 
     505           0 :     CVector4D camPlane(0, 1, 0, -wm.m_WaterHeight + 0.5f);
     506           0 :     SetObliqueFrustumClipping(camera, camPlane);
     507           0 : }
     508             : 
     509           0 : void CSceneRenderer::ComputeRefractionCamera(CCamera& camera, const CBoundingBoxAligned& scissor) const
     510             : {
     511           0 :     WaterManager& wm = m->waterManager;
     512             : 
     513           0 :     CMatrix3D projection;
     514           0 :     if (m_ViewCamera.GetProjectionType() == CCamera::ProjectionType::PERSPECTIVE)
     515             :     {
     516           0 :         const float aspectRatio = 1.0f;
     517             :         // Expand fov slightly since ripples can reflect parts of the scene that
     518             :         // are slightly outside the normal camera view, and we want to avoid any
     519             :         // noticeable edge-filtering artifacts
     520           0 :         projection.SetPerspective(m_ViewCamera.GetFOV() * 1.05f, aspectRatio, m_ViewCamera.GetNearPlane(), m_ViewCamera.GetFarPlane());
     521             :     }
     522             :     else
     523           0 :         projection = m_ViewCamera.GetProjection();
     524             : 
     525           0 :     camera = m_ViewCamera;
     526             : 
     527             :     // Temporarily change the camera to make it render to a view port the size of the
     528             :     // water texture, stretch the image according to our aspect ratio so it covers
     529             :     // the whole screen despite being rendered into a square, and cover slightly more
     530             :     // of the view so we can see wavy refractions of slightly off-screen objects.
     531           0 :     camera.UpdateFrustum(scissor);
     532           0 :     camera.ClipFrustum(CVector4D(0, -1, 0, wm.m_WaterHeight + 0.5f));   // add some to avoid artifacts near steep shores.
     533             : 
     534             :     SViewPort vp;
     535           0 :     vp.m_Height = wm.m_RefTextureSize;
     536           0 :     vp.m_Width = wm.m_RefTextureSize;
     537           0 :     vp.m_X = 0;
     538           0 :     vp.m_Y = 0;
     539           0 :     camera.SetViewPort(vp);
     540           0 :     camera.SetProjection(projection);
     541           0 :     CMatrix3D scaleMat;
     542           0 :     scaleMat.SetScaling(g_Renderer.GetHeight() / static_cast<float>(std::max(1, g_Renderer.GetWidth())), 1.0f, 1.0f);
     543           0 :     camera.SetProjection(scaleMat * camera.GetProjection());
     544           0 : }
     545             : 
     546             : // RenderReflections: render the water reflections to the reflection texture
     547           0 : void CSceneRenderer::RenderReflections(
     548             :     Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
     549             :     const CShaderDefines& context, const CBoundingBoxAligned& scissor)
     550             : {
     551           0 :     PROFILE3_GPU("water reflections");
     552           0 :     GPU_SCOPED_LABEL(deviceCommandContext, "Render water reflections");
     553             : 
     554           0 :     WaterManager& wm = m->waterManager;
     555             : 
     556             :     // Remember old camera
     557           0 :     CCamera normalCamera = m_ViewCamera;
     558             : 
     559           0 :     ComputeReflectionCamera(m_ViewCamera, scissor);
     560             :     const CBoundingBoxAligned reflectionScissor =
     561           0 :         m->terrainRenderer.ScissorWater(CULL_DEFAULT, m_ViewCamera);
     562           0 :     if (reflectionScissor.IsEmpty())
     563             :     {
     564           0 :         m_ViewCamera = normalCamera;
     565           0 :         return;
     566             :     }
     567             : 
     568             :     // Save the model-view-projection matrix so the shaders can use it for projective texturing
     569           0 :     wm.m_ReflectionMatrix = m_ViewCamera.GetViewProjection();
     570           0 :     if (g_VideoMode.GetBackendDevice()->GetBackend() == Renderer::Backend::Backend::VULKAN)
     571             :     {
     572           0 :         CMatrix3D flip;
     573           0 :         flip.SetIdentity();
     574           0 :         flip._22 = -1.0f;
     575           0 :         wm.m_ReflectionMatrix = flip * wm.m_ReflectionMatrix;
     576             :     }
     577             : 
     578           0 :     float vpHeight = wm.m_RefTextureSize;
     579           0 :     float vpWidth = wm.m_RefTextureSize;
     580             : 
     581             :     SScreenRect screenScissor;
     582           0 :     screenScissor.x1 = static_cast<int>(floor((reflectionScissor[0].X * 0.5f + 0.5f) * vpWidth));
     583           0 :     screenScissor.y1 = static_cast<int>(floor((reflectionScissor[0].Y * 0.5f + 0.5f) * vpHeight));
     584           0 :     screenScissor.x2 = static_cast<int>(ceil((reflectionScissor[1].X * 0.5f + 0.5f) * vpWidth));
     585           0 :     screenScissor.y2 = static_cast<int>(ceil((reflectionScissor[1].Y * 0.5f + 0.5f) * vpHeight));
     586             : 
     587           0 :     deviceCommandContext->BeginFramebufferPass(wm.m_ReflectionFramebuffer.get());
     588             : 
     589           0 :     Renderer::Backend::IDeviceCommandContext::Rect viewportRect{};
     590           0 :     viewportRect.width = vpWidth;
     591           0 :     viewportRect.height = vpHeight;
     592           0 :     deviceCommandContext->SetViewports(1, &viewportRect);
     593             : 
     594             :     Renderer::Backend::IDeviceCommandContext::Rect scissorRect;
     595           0 :     scissorRect.x = screenScissor.x1;
     596           0 :     scissorRect.y = screenScissor.y1;
     597           0 :     scissorRect.width = screenScissor.x2 - screenScissor.x1;
     598           0 :     scissorRect.height = screenScissor.y2 - screenScissor.y1;
     599           0 :     deviceCommandContext->SetScissors(1, &scissorRect);
     600             : 
     601           0 :     CShaderDefines reflectionsContext = context;
     602           0 :     reflectionsContext.Add(str_PASS_REFLECTIONS, str_1);
     603             : 
     604             :     // Render terrain and models
     605           0 :     RenderPatches(deviceCommandContext, reflectionsContext, CULL_REFLECTIONS);
     606           0 :     RenderModels(deviceCommandContext, reflectionsContext, CULL_REFLECTIONS);
     607           0 :     RenderTransparentModels(deviceCommandContext, reflectionsContext, CULL_REFLECTIONS, TRANSPARENT);
     608             : 
     609             :     // Particles are always oriented to face the camera in the vertex shader,
     610             :     // so they don't need the inverted cull face.
     611           0 :     if (g_RenderingOptions.GetParticles())
     612             :     {
     613           0 :         RenderParticles(deviceCommandContext, CULL_REFLECTIONS);
     614             :     }
     615             : 
     616           0 :     deviceCommandContext->SetScissors(0, nullptr);
     617           0 :     deviceCommandContext->EndFramebufferPass();
     618             : 
     619             :     // Reset old camera
     620           0 :     m_ViewCamera = normalCamera;
     621             : }
     622             : 
     623             : // RenderRefractions: render the water refractions to the refraction texture
     624           0 : void CSceneRenderer::RenderRefractions(
     625             :     Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
     626             :     const CShaderDefines& context, const CBoundingBoxAligned &scissor)
     627             : {
     628           0 :     PROFILE3_GPU("water refractions");
     629           0 :     GPU_SCOPED_LABEL(deviceCommandContext, "Render water refractions");
     630             : 
     631           0 :     WaterManager& wm = m->waterManager;
     632             : 
     633             :     // Remember old camera
     634           0 :     CCamera normalCamera = m_ViewCamera;
     635             : 
     636           0 :     ComputeRefractionCamera(m_ViewCamera, scissor);
     637             :     const CBoundingBoxAligned refractionScissor =
     638           0 :         m->terrainRenderer.ScissorWater(CULL_DEFAULT, m_ViewCamera);
     639           0 :     if (refractionScissor.IsEmpty())
     640             :     {
     641           0 :         m_ViewCamera = normalCamera;
     642           0 :         return;
     643             :     }
     644             : 
     645           0 :     CVector4D camPlane(0, -1, 0, wm.m_WaterHeight + 2.0f);
     646           0 :     SetObliqueFrustumClipping(m_ViewCamera, camPlane);
     647             : 
     648             :     // Save the model-view-projection matrix so the shaders can use it for projective texturing
     649           0 :     wm.m_RefractionMatrix = m_ViewCamera.GetViewProjection();
     650           0 :     wm.m_RefractionProjInvMatrix = m_ViewCamera.GetProjection().GetInverse();
     651           0 :     wm.m_RefractionViewInvMatrix = m_ViewCamera.GetOrientation();
     652             : 
     653           0 :     if (g_VideoMode.GetBackendDevice()->GetBackend() == Renderer::Backend::Backend::VULKAN)
     654             :     {
     655           0 :         CMatrix3D flip;
     656           0 :         flip.SetIdentity();
     657           0 :         flip._22 = -1.0f;
     658           0 :         wm.m_RefractionMatrix = flip * wm.m_RefractionMatrix;
     659           0 :         wm.m_RefractionProjInvMatrix = wm.m_RefractionProjInvMatrix * flip;
     660             :     }
     661             : 
     662           0 :     float vpHeight = wm.m_RefTextureSize;
     663           0 :     float vpWidth = wm.m_RefTextureSize;
     664             : 
     665             :     SScreenRect screenScissor;
     666           0 :     screenScissor.x1 = static_cast<int>(floor((refractionScissor[0].X * 0.5f + 0.5f) * vpWidth));
     667           0 :     screenScissor.y1 = static_cast<int>(floor((refractionScissor[0].Y * 0.5f + 0.5f) * vpHeight));
     668           0 :     screenScissor.x2 = static_cast<int>(ceil((refractionScissor[1].X * 0.5f + 0.5f) * vpWidth));
     669           0 :     screenScissor.y2 = static_cast<int>(ceil((refractionScissor[1].Y * 0.5f + 0.5f) * vpHeight));
     670             : 
     671           0 :     deviceCommandContext->BeginFramebufferPass(wm.m_RefractionFramebuffer.get());
     672             : 
     673           0 :     Renderer::Backend::IDeviceCommandContext::Rect viewportRect{};
     674           0 :     viewportRect.width = vpWidth;
     675           0 :     viewportRect.height = vpHeight;
     676           0 :     deviceCommandContext->SetViewports(1, &viewportRect);
     677             : 
     678             :     Renderer::Backend::IDeviceCommandContext::Rect scissorRect;
     679           0 :     scissorRect.x = screenScissor.x1;
     680           0 :     scissorRect.y = screenScissor.y1;
     681           0 :     scissorRect.width = screenScissor.x2 - screenScissor.x1;
     682           0 :     scissorRect.height = screenScissor.y2 - screenScissor.y1;
     683           0 :     deviceCommandContext->SetScissors(1, &scissorRect);
     684             : 
     685             :     // Render terrain and models
     686           0 :     RenderPatches(deviceCommandContext, context, CULL_REFRACTIONS);
     687             : 
     688             :     // Render debug-related terrain overlays to make it visible under water.
     689           0 :     ITerrainOverlay::RenderOverlaysBeforeWater(deviceCommandContext);
     690             : 
     691           0 :     RenderModels(deviceCommandContext, context, CULL_REFRACTIONS);
     692           0 :     RenderTransparentModels(deviceCommandContext, context, CULL_REFRACTIONS, TRANSPARENT_OPAQUE);
     693             : 
     694           0 :     deviceCommandContext->SetScissors(0, nullptr);
     695           0 :     deviceCommandContext->EndFramebufferPass();
     696             : 
     697             :     // Reset old camera
     698           0 :     m_ViewCamera = normalCamera;
     699             : }
     700             : 
     701           0 : void CSceneRenderer::RenderSilhouettes(
     702             :     Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
     703             :     const CShaderDefines& context)
     704             : {
     705           0 :     PROFILE3_GPU("silhouettes");
     706           0 :     GPU_SCOPED_LABEL(deviceCommandContext, "Render silhouettes");
     707             : 
     708           0 :     CShaderDefines contextOccluder = context;
     709           0 :     contextOccluder.Add(str_MODE_SILHOUETTEOCCLUDER, str_1);
     710             : 
     711           0 :     CShaderDefines contextDisplay = context;
     712           0 :     contextDisplay.Add(str_MODE_SILHOUETTEDISPLAY, str_1);
     713             : 
     714             :     // Render silhouettes of units hidden behind terrain or occluders.
     715             :     // To avoid breaking the standard rendering of alpha-blended objects, this
     716             :     // has to be done in a separate pass.
     717             :     // First we render all occluders into depth, then render all units with
     718             :     // inverted depth test so any behind an occluder will get drawn in a constant
     719             :     // color.
     720             : 
     721             :     // TODO: do we need clear here?
     722           0 :     deviceCommandContext->ClearFramebuffer(false, true, true);
     723             : 
     724             :     // Render occluders:
     725             : 
     726             :     {
     727           0 :         PROFILE("render patches");
     728           0 :         m->terrainRenderer.RenderPatches(deviceCommandContext, CULL_SILHOUETTE_OCCLUDER, contextOccluder);
     729             :     }
     730             : 
     731             :     {
     732           0 :         PROFILE("render model occluders");
     733           0 :         m->CallModelRenderers(deviceCommandContext, contextOccluder, CULL_SILHOUETTE_OCCLUDER, 0);
     734             :     }
     735             : 
     736             :     {
     737           0 :         PROFILE("render transparent occluders");
     738           0 :         m->CallTranspModelRenderers(deviceCommandContext, contextOccluder, CULL_SILHOUETTE_OCCLUDER, 0);
     739             :     }
     740             : 
     741             :     // Since we can't sort, we'll use the stencil buffer to ensure we only draw
     742             :     // a pixel once (using the color of whatever model happens to be drawn first).
     743             :     {
     744           0 :         PROFILE("render model casters");
     745           0 :         m->CallModelRenderers(deviceCommandContext, contextDisplay, CULL_SILHOUETTE_CASTER, 0);
     746             :     }
     747             : 
     748             :     {
     749           0 :         PROFILE("render transparent casters");
     750           0 :         m->CallTranspModelRenderers(deviceCommandContext, contextDisplay, CULL_SILHOUETTE_CASTER, 0);
     751             :     }
     752           0 : }
     753             : 
     754           0 : void CSceneRenderer::RenderParticles(
     755             :     Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
     756             :     int cullGroup)
     757             : {
     758           0 :     PROFILE3_GPU("particles");
     759           0 :     GPU_SCOPED_LABEL(deviceCommandContext, "Render particles");
     760             : 
     761           0 :     m->particleRenderer.RenderParticles(
     762           0 :         deviceCommandContext, cullGroup, m_ModelRenderMode == WIREFRAME);
     763             : 
     764           0 :     if (m_ModelRenderMode == EDGED_FACES)
     765             :     {
     766           0 :         m->particleRenderer.RenderParticles(
     767             :             deviceCommandContext, cullGroup, true);
     768           0 :         m->particleRenderer.RenderBounds(cullGroup);
     769             :     }
     770           0 : }
     771             : 
     772           0 : void CSceneRenderer::PrepareSubmissions(
     773             :     Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
     774             :     const CBoundingBoxAligned& waterScissor)
     775             : {
     776           0 :     PROFILE3("prepare submissions");
     777           0 :     GPU_SCOPED_LABEL(deviceCommandContext, "Prepare submissions");
     778             : 
     779           0 :     m->skyManager.LoadAndUploadSkyTexturesIfNeeded(deviceCommandContext);
     780             : 
     781           0 :     GetScene().GetLOSTexture().InterpolateLOS(deviceCommandContext);
     782           0 :     GetScene().GetTerritoryTexture().UpdateIfNeeded(deviceCommandContext);
     783           0 :     GetScene().GetMiniMapTexture().Render(
     784           0 :         deviceCommandContext, GetScene().GetLOSTexture(), GetScene().GetTerritoryTexture());
     785             : 
     786           0 :     CShaderDefines context = m->globalContext;
     787             : 
     788             :     // Prepare model renderers
     789             :     {
     790           0 :     PROFILE3("prepare models");
     791           0 :     m->Model.NormalSkinned->PrepareModels();
     792           0 :     m->Model.TranspSkinned->PrepareModels();
     793           0 :     if (m->Model.NormalUnskinned != m->Model.NormalSkinned)
     794           0 :         m->Model.NormalUnskinned->PrepareModels();
     795           0 :     if (m->Model.TranspUnskinned != m->Model.TranspSkinned)
     796           0 :         m->Model.TranspUnskinned->PrepareModels();
     797             :     }
     798             : 
     799           0 :     m->terrainRenderer.PrepareForRendering();
     800             : 
     801           0 :     m->overlayRenderer.PrepareForRendering();
     802             : 
     803           0 :     m->particleRenderer.PrepareForRendering(context);
     804             : 
     805             :     {
     806           0 :         PROFILE3("upload models");
     807           0 :         m->Model.NormalSkinned->UploadModels(deviceCommandContext);
     808           0 :         m->Model.TranspSkinned->UploadModels(deviceCommandContext);
     809           0 :         if (m->Model.NormalUnskinned != m->Model.NormalSkinned)
     810           0 :             m->Model.NormalUnskinned->UploadModels(deviceCommandContext);
     811           0 :         if (m->Model.TranspUnskinned != m->Model.TranspSkinned)
     812           0 :             m->Model.TranspUnskinned->UploadModels(deviceCommandContext);
     813             :     }
     814             : 
     815           0 :     m->overlayRenderer.Upload(deviceCommandContext);
     816             : 
     817           0 :     m->particleRenderer.Upload(deviceCommandContext);
     818             : 
     819           0 :     if (g_RenderingOptions.GetShadows())
     820             :     {
     821           0 :         RenderShadowMap(deviceCommandContext, context);
     822             :     }
     823             : 
     824           0 :     if (m->waterManager.m_RenderWater)
     825             :     {
     826           0 :         if (waterScissor.GetVolume() > 0 && m->waterManager.WillRenderFancyWater())
     827             :         {
     828           0 :             m->waterManager.UpdateQuality();
     829             : 
     830           0 :             PROFILE3_GPU("water scissor");
     831           0 :             if (g_RenderingOptions.GetWaterReflection())
     832           0 :                 RenderReflections(deviceCommandContext, context, waterScissor);
     833             : 
     834           0 :             if (g_RenderingOptions.GetWaterRefraction())
     835           0 :                 RenderRefractions(deviceCommandContext, context, waterScissor);
     836             : 
     837           0 :             if (g_RenderingOptions.GetWaterFancyEffects())
     838           0 :                 m->terrainRenderer.RenderWaterFoamOccluders(deviceCommandContext, CULL_DEFAULT);
     839             :         }
     840             :     }
     841           0 : }
     842             : 
     843           0 : void CSceneRenderer::RenderSubmissions(
     844             :     Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
     845             :     const CBoundingBoxAligned& waterScissor)
     846             : {
     847           0 :     PROFILE3("render submissions");
     848           0 :     GPU_SCOPED_LABEL(deviceCommandContext, "Render submissions");
     849             : 
     850           0 :     CShaderDefines context = m->globalContext;
     851             : 
     852           0 :     constexpr int cullGroup = CULL_DEFAULT;
     853             : 
     854           0 :     m->skyManager.RenderSky(deviceCommandContext);
     855             : 
     856             :     // render submitted patches and models
     857           0 :     RenderPatches(deviceCommandContext, context, cullGroup);
     858             : 
     859             :     // render debug-related terrain overlays
     860           0 :     ITerrainOverlay::RenderOverlaysBeforeWater(deviceCommandContext);
     861             : 
     862             :     // render other debug-related overlays before water (so they can be seen when underwater)
     863           0 :     m->overlayRenderer.RenderOverlaysBeforeWater(deviceCommandContext);
     864             : 
     865           0 :     RenderModels(deviceCommandContext, context, cullGroup);
     866             : 
     867             :     // render water
     868           0 :     if (m->waterManager.m_RenderWater && g_Game && waterScissor.GetVolume() > 0)
     869             :     {
     870           0 :         if (m->waterManager.WillRenderFancyWater())
     871             :         {
     872             :             // Render transparent stuff, but only the solid parts that can occlude block water.
     873           0 :             RenderTransparentModels(deviceCommandContext, context, cullGroup, TRANSPARENT_OPAQUE);
     874             : 
     875           0 :             m->terrainRenderer.RenderWater(deviceCommandContext, context, cullGroup, &m->shadow);
     876             : 
     877             :             // Render transparent stuff again, but only the blended parts that overlap water.
     878           0 :             RenderTransparentModels(deviceCommandContext, context, cullGroup, TRANSPARENT_BLEND);
     879             :         }
     880             :         else
     881             :         {
     882           0 :             m->terrainRenderer.RenderWater(deviceCommandContext, context, cullGroup, &m->shadow);
     883             : 
     884             :             // Render transparent stuff, so it can overlap models/terrain.
     885           0 :             RenderTransparentModels(deviceCommandContext, context, cullGroup, TRANSPARENT);
     886             :         }
     887             :     }
     888             :     else
     889             :     {
     890             :         // render transparent stuff, so it can overlap models/terrain
     891           0 :         RenderTransparentModels(deviceCommandContext, context, cullGroup, TRANSPARENT);
     892             :     }
     893             : 
     894             :     // render debug-related terrain overlays
     895           0 :     ITerrainOverlay::RenderOverlaysAfterWater(deviceCommandContext, cullGroup);
     896             : 
     897             :     // render some other overlays after water (so they can be displayed on top of water)
     898           0 :     m->overlayRenderer.RenderOverlaysAfterWater(deviceCommandContext);
     899             : 
     900             :     // particles are transparent so render after water
     901           0 :     if (g_RenderingOptions.GetParticles())
     902             :     {
     903           0 :         RenderParticles(deviceCommandContext, cullGroup);
     904             :     }
     905             : 
     906             :     // render debug lines
     907           0 :     if (g_RenderingOptions.GetDisplayFrustum())
     908           0 :         DisplayFrustum();
     909             : 
     910           0 :     if (g_RenderingOptions.GetDisplayShadowsFrustum())
     911           0 :         m->shadow.RenderDebugBounds();
     912             : 
     913           0 :     m->silhouetteRenderer.RenderDebugBounds(deviceCommandContext);
     914           0 : }
     915             : 
     916           0 : void CSceneRenderer::EndFrame()
     917             : {
     918             :     // empty lists
     919           0 :     m->terrainRenderer.EndFrame();
     920           0 :     m->overlayRenderer.EndFrame();
     921           0 :     m->particleRenderer.EndFrame();
     922           0 :     m->silhouetteRenderer.EndFrame();
     923             : 
     924             :     // Finish model renderers
     925           0 :     m->Model.NormalSkinned->EndFrame();
     926           0 :     m->Model.TranspSkinned->EndFrame();
     927           0 :     if (m->Model.NormalUnskinned != m->Model.NormalSkinned)
     928           0 :         m->Model.NormalUnskinned->EndFrame();
     929           0 :     if (m->Model.TranspUnskinned != m->Model.TranspSkinned)
     930           0 :         m->Model.TranspUnskinned->EndFrame();
     931           0 : }
     932             : 
     933           0 : void CSceneRenderer::DisplayFrustum()
     934             : {
     935           0 :     g_Renderer.GetDebugRenderer().DrawCameraFrustum(m_CullCamera, CColor(1.0f, 1.0f, 1.0f, 0.25f), 2);
     936           0 :     g_Renderer.GetDebugRenderer().DrawCameraFrustum(m_CullCamera, CColor(1.0f, 1.0f, 1.0f, 1.0f), 2, true);
     937           0 : }
     938             : 
     939             : // Text overlay rendering
     940           0 : void CSceneRenderer::RenderTextOverlays(CCanvas2D& canvas)
     941             : {
     942           0 :     PROFILE3_GPU("text overlays");
     943             : 
     944           0 :     if (m_DisplayTerrainPriorities)
     945           0 :         m->terrainRenderer.RenderPriorities(canvas, CULL_DEFAULT);
     946           0 : }
     947             : 
     948             : // SetSceneCamera: setup projection and transform of camera and adjust viewport to current view
     949             : // The camera always represents the actual camera used to render a scene, not any virtual camera
     950             : // used for shadow rendering or reflections.
     951           0 : void CSceneRenderer::SetSceneCamera(const CCamera& viewCamera, const CCamera& cullCamera)
     952             : {
     953           0 :     m_ViewCamera = viewCamera;
     954           0 :     m_CullCamera = cullCamera;
     955             : 
     956           0 :     if (g_RenderingOptions.GetShadows())
     957           0 :         m->shadow.SetupFrame(m_CullCamera, m_LightEnv->GetSunDir());
     958           0 : }
     959             : 
     960           0 : void CSceneRenderer::Submit(CPatch* patch)
     961             : {
     962           0 :     if (m_CurrentCullGroup == CULL_DEFAULT)
     963             :     {
     964           0 :         m->shadow.AddShadowReceiverBound(patch->GetWorldBounds());
     965           0 :         m->silhouetteRenderer.AddOccluder(patch);
     966             :     }
     967             : 
     968           0 :     if (CULL_SHADOWS_CASCADE_0 <= m_CurrentCullGroup && m_CurrentCullGroup <= CULL_SHADOWS_CASCADE_3)
     969             :     {
     970           0 :         const int cascade = m_CurrentCullGroup - CULL_SHADOWS_CASCADE_0;
     971           0 :         m->shadow.AddShadowCasterBound(cascade, patch->GetWorldBounds());
     972             :     }
     973             : 
     974           0 :     m->terrainRenderer.Submit(m_CurrentCullGroup, patch);
     975           0 : }
     976             : 
     977           0 : void CSceneRenderer::Submit(SOverlayLine* overlay)
     978             : {
     979             :     // Overlays are only needed in the default cull group for now,
     980             :     // so just ignore submissions to any other group
     981           0 :     if (m_CurrentCullGroup == CULL_DEFAULT)
     982           0 :         m->overlayRenderer.Submit(overlay);
     983           0 : }
     984             : 
     985           0 : void CSceneRenderer::Submit(SOverlayTexturedLine* overlay)
     986             : {
     987           0 :     if (m_CurrentCullGroup == CULL_DEFAULT)
     988           0 :         m->overlayRenderer.Submit(overlay);
     989           0 : }
     990             : 
     991           0 : void CSceneRenderer::Submit(SOverlaySprite* overlay)
     992             : {
     993           0 :     if (m_CurrentCullGroup == CULL_DEFAULT)
     994           0 :         m->overlayRenderer.Submit(overlay);
     995           0 : }
     996             : 
     997           0 : void CSceneRenderer::Submit(SOverlayQuad* overlay)
     998             : {
     999           0 :     if (m_CurrentCullGroup == CULL_DEFAULT)
    1000           0 :         m->overlayRenderer.Submit(overlay);
    1001           0 : }
    1002             : 
    1003           0 : void CSceneRenderer::Submit(SOverlaySphere* overlay)
    1004             : {
    1005           0 :     if (m_CurrentCullGroup == CULL_DEFAULT)
    1006           0 :         m->overlayRenderer.Submit(overlay);
    1007           0 : }
    1008             : 
    1009           0 : void CSceneRenderer::Submit(CModelDecal* decal)
    1010             : {
    1011             :     // Decals can't cast shadows since they're flat on the terrain.
    1012             :     // They can receive shadows, but the terrain under them will have
    1013             :     // already been passed to AddShadowCasterBound, so don't bother
    1014             :     // doing it again here.
    1015             : 
    1016           0 :     m->terrainRenderer.Submit(m_CurrentCullGroup, decal);
    1017           0 : }
    1018             : 
    1019           0 : void CSceneRenderer::Submit(CParticleEmitter* emitter)
    1020             : {
    1021           0 :     m->particleRenderer.Submit(m_CurrentCullGroup, emitter);
    1022           0 : }
    1023             : 
    1024           0 : void CSceneRenderer::SubmitNonRecursive(CModel* model)
    1025             : {
    1026           0 :     if (m_CurrentCullGroup == CULL_DEFAULT)
    1027             :     {
    1028           0 :         m->shadow.AddShadowReceiverBound(model->GetWorldBounds());
    1029             : 
    1030           0 :         if (model->GetFlags() & MODELFLAG_SILHOUETTE_OCCLUDER)
    1031           0 :             m->silhouetteRenderer.AddOccluder(model);
    1032           0 :         if (model->GetFlags() & MODELFLAG_SILHOUETTE_DISPLAY)
    1033           0 :             m->silhouetteRenderer.AddCaster(model);
    1034             :     }
    1035             : 
    1036           0 :     if (CULL_SHADOWS_CASCADE_0 <= m_CurrentCullGroup && m_CurrentCullGroup <= CULL_SHADOWS_CASCADE_3)
    1037             :     {
    1038           0 :         if (!(model->GetFlags() & MODELFLAG_CASTSHADOWS))
    1039           0 :             return;
    1040             : 
    1041           0 :         const int cascade = m_CurrentCullGroup - CULL_SHADOWS_CASCADE_0;
    1042           0 :         m->shadow.AddShadowCasterBound(cascade, model->GetWorldBounds());
    1043             :     }
    1044             : 
    1045           0 :     bool requiresSkinning = (model->GetModelDef()->GetNumBones() != 0);
    1046             : 
    1047           0 :     if (model->GetMaterial().UsesAlphaBlending())
    1048             :     {
    1049           0 :         if (requiresSkinning)
    1050           0 :             m->Model.TranspSkinned->Submit(m_CurrentCullGroup, model);
    1051             :         else
    1052           0 :             m->Model.TranspUnskinned->Submit(m_CurrentCullGroup, model);
    1053             :     }
    1054             :     else
    1055             :     {
    1056           0 :         if (requiresSkinning)
    1057           0 :             m->Model.NormalSkinned->Submit(m_CurrentCullGroup, model);
    1058             :         else
    1059           0 :             m->Model.NormalUnskinned->Submit(m_CurrentCullGroup, model);
    1060             :     }
    1061             : }
    1062             : 
    1063           0 : void CSceneRenderer::PrepareScene(
    1064             :     Renderer::Backend::IDeviceCommandContext* deviceCommandContext, Scene& scene)
    1065             : {
    1066           0 :     m_CurrentScene = &scene;
    1067             : 
    1068           0 :     CFrustum frustum = m_CullCamera.GetFrustum();
    1069             : 
    1070           0 :     m_CurrentCullGroup = CULL_DEFAULT;
    1071             : 
    1072           0 :     scene.EnumerateObjects(frustum, this);
    1073             : 
    1074           0 :     m->particleManager.RenderSubmit(*this, frustum);
    1075             : 
    1076           0 :     if (g_RenderingOptions.GetSilhouettes())
    1077             :     {
    1078           0 :         m->silhouetteRenderer.ComputeSubmissions(m_ViewCamera);
    1079             : 
    1080           0 :         m_CurrentCullGroup = CULL_DEFAULT;
    1081           0 :         m->silhouetteRenderer.RenderSubmitOverlays(*this);
    1082             : 
    1083           0 :         m_CurrentCullGroup = CULL_SILHOUETTE_OCCLUDER;
    1084           0 :         m->silhouetteRenderer.RenderSubmitOccluders(*this);
    1085             : 
    1086           0 :         m_CurrentCullGroup = CULL_SILHOUETTE_CASTER;
    1087           0 :         m->silhouetteRenderer.RenderSubmitCasters(*this);
    1088             :     }
    1089             : 
    1090           0 :     if (g_RenderingOptions.GetShadows())
    1091             :     {
    1092           0 :         for (int cascade = 0; cascade <= m->shadow.GetCascadeCount(); ++cascade)
    1093             :         {
    1094           0 :             m_CurrentCullGroup = CULL_SHADOWS_CASCADE_0 + cascade;
    1095           0 :             const CFrustum shadowFrustum = m->shadow.GetShadowCasterCullFrustum(cascade);
    1096           0 :             scene.EnumerateObjects(shadowFrustum, this);
    1097             :         }
    1098             :     }
    1099             : 
    1100           0 :     if (m->waterManager.m_RenderWater)
    1101             :     {
    1102           0 :         m_WaterScissor = m->terrainRenderer.ScissorWater(CULL_DEFAULT, m_ViewCamera);
    1103             : 
    1104           0 :         if (m_WaterScissor.GetVolume() > 0 && m->waterManager.WillRenderFancyWater())
    1105             :         {
    1106           0 :             if (g_RenderingOptions.GetWaterReflection())
    1107             :             {
    1108           0 :                 m_CurrentCullGroup = CULL_REFLECTIONS;
    1109             : 
    1110           0 :                 CCamera reflectionCamera;
    1111           0 :                 ComputeReflectionCamera(reflectionCamera, m_WaterScissor);
    1112             : 
    1113           0 :                 scene.EnumerateObjects(reflectionCamera.GetFrustum(), this);
    1114             :             }
    1115             : 
    1116           0 :             if (g_RenderingOptions.GetWaterRefraction())
    1117             :             {
    1118           0 :                 m_CurrentCullGroup = CULL_REFRACTIONS;
    1119             : 
    1120           0 :                 CCamera refractionCamera;
    1121           0 :                 ComputeRefractionCamera(refractionCamera, m_WaterScissor);
    1122             : 
    1123           0 :                 scene.EnumerateObjects(refractionCamera.GetFrustum(), this);
    1124             :             }
    1125             : 
    1126             :             // Render the waves to the Fancy effects texture
    1127           0 :             m->waterManager.RenderWaves(deviceCommandContext, frustum);
    1128             :         }
    1129             :     }
    1130             :     else
    1131           0 :         m_WaterScissor = CBoundingBoxAligned{};
    1132             : 
    1133           0 :     m_CurrentCullGroup = -1;
    1134             : 
    1135           0 :     PrepareSubmissions(deviceCommandContext, m_WaterScissor);
    1136           0 : }
    1137             : 
    1138           0 : void CSceneRenderer::RenderScene(
    1139             :     Renderer::Backend::IDeviceCommandContext* deviceCommandContext)
    1140             : {
    1141           0 :     ENSURE(m_CurrentScene);
    1142           0 :     RenderSubmissions(deviceCommandContext, m_WaterScissor);
    1143           0 : }
    1144             : 
    1145           0 : void CSceneRenderer::RenderSceneOverlays(
    1146             :     Renderer::Backend::IDeviceCommandContext* deviceCommandContext)
    1147             : {
    1148           0 :     if (g_RenderingOptions.GetSilhouettes())
    1149             :     {
    1150           0 :         RenderSilhouettes(deviceCommandContext, m->globalContext);
    1151             :     }
    1152             : 
    1153           0 :     m->silhouetteRenderer.RenderDebugOverlays(deviceCommandContext);
    1154             : 
    1155             :     // Render overlays that should appear on top of all other objects.
    1156           0 :     m->overlayRenderer.RenderForegroundOverlays(deviceCommandContext, m_ViewCamera);
    1157             : 
    1158           0 :     m_CurrentScene = nullptr;
    1159           0 : }
    1160             : 
    1161           0 : Scene& CSceneRenderer::GetScene()
    1162             : {
    1163           0 :     ENSURE(m_CurrentScene);
    1164           0 :     return *m_CurrentScene;
    1165             : }
    1166             : 
    1167           6 : void CSceneRenderer::MakeShadersDirty()
    1168             : {
    1169           6 :     m->waterManager.m_NeedsReloading = true;
    1170           6 : }
    1171             : 
    1172           0 : WaterManager& CSceneRenderer::GetWaterManager()
    1173             : {
    1174           0 :     return m->waterManager;
    1175             : }
    1176             : 
    1177           0 : SkyManager& CSceneRenderer::GetSkyManager()
    1178             : {
    1179           0 :     return m->skyManager;
    1180             : }
    1181             : 
    1182           0 : CParticleManager& CSceneRenderer::GetParticleManager()
    1183             : {
    1184           0 :     return m->particleManager;
    1185             : }
    1186             : 
    1187           0 : TerrainRenderer& CSceneRenderer::GetTerrainRenderer()
    1188             : {
    1189           0 :     return m->terrainRenderer;
    1190             : }
    1191             : 
    1192           0 : CMaterialManager& CSceneRenderer::GetMaterialManager()
    1193             : {
    1194           0 :     return m->materialManager;
    1195             : }
    1196             : 
    1197           0 : ShadowMap& CSceneRenderer::GetShadowMap()
    1198             : {
    1199           0 :     return m->shadow;
    1200             : }
    1201             : 
    1202           0 : void CSceneRenderer::ResetState()
    1203             : {
    1204             :     // Clear all emitters, that were created in previous games
    1205           0 :     GetParticleManager().ClearUnattachedEmitters();
    1206           3 : }

Generated by: LCOV version 1.13