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 : }
|