1 /* Copyright (C) 2021 Wildfire Games.
2 * This file is part of 0 A.D.
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.
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.
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/>.
19 * higher level interface on top of OpenGL to render basic objects:
20 * terrain, models, sprites, particles etc.
23 #include "precompiled.h"
27 #include "lib/bits.h" // is_pow2
28 #include "lib/res/graphics/ogl_tex.h"
29 #include "lib/allocators/shared_ptr.h"
30 #include "maths/Matrix3D.h"
31 #include "maths/MathUtil.h"
32 #include "ps/CLogger.h"
33 #include "ps/ConfigDB.h"
34 #include "ps/CStrInternStatic.h"
36 #include "ps/Profile.h"
37 #include "ps/Filesystem.h"
39 #include "ps/Loader.h"
40 #include "ps/ProfileViewer.h"
41 #include "graphics/Camera.h"
42 #include "graphics/Decal.h"
43 #include "graphics/FontManager.h"
44 #include "graphics/GameView.h"
45 #include "graphics/LightEnv.h"
46 #include "graphics/LOSTexture.h"
47 #include "graphics/MaterialManager.h"
48 #include "graphics/Model.h"
49 #include "graphics/ModelDef.h"
50 #include "graphics/ParticleManager.h"
51 #include "graphics/Patch.h"
52 #include "graphics/ShaderManager.h"
53 #include "graphics/Terrain.h"
54 #include "graphics/Texture.h"
55 #include "graphics/TextureManager.h"
56 #include "renderer/DebugRenderer.h"
57 #include "renderer/HWLightingModelRenderer.h"
58 #include "renderer/InstancingModelRenderer.h"
59 #include "renderer/ModelRenderer.h"
60 #include "renderer/OverlayRenderer.h"
61 #include "renderer/ParticleRenderer.h"
62 #include "renderer/PostprocManager.h"
63 #include "renderer/RenderingOptions.h"
64 #include "renderer/RenderModifiers.h"
65 #include "renderer/ShadowMap.h"
66 #include "renderer/SilhouetteRenderer.h"
67 #include "renderer/SkyManager.h"
68 #include "renderer/TerrainOverlay.h"
69 #include "renderer/TerrainRenderer.h"
70 #include "renderer/TimeManager.h"
71 #include "renderer/VertexBufferManager.h"
72 #include "renderer/WaterManager.h"
75 #include <boost/algorithm/string.hpp>
84 ///////////////////////////////////////////////////////////////////////////////////
85 // CRendererStatsTable - Profile display of rendering stats
88 * Class CRendererStatsTable: Implementation of AbstractProfileTable to
89 * display the renderer stats in-game.
91 * Accesses CRenderer::m_Stats by keeping the reference passed to the
94 class CRendererStatsTable
: public AbstractProfileTable
96 NONCOPYABLE(CRendererStatsTable
);
98 CRendererStatsTable(const CRenderer::Stats
& st
);
100 // Implementation of AbstractProfileTable interface
103 size_t GetNumberRows();
104 const std::vector
<ProfileColumn
>& GetColumns();
105 CStr
GetCellText(size_t row
, size_t col
);
106 AbstractProfileTable
* GetChild(size_t row
);
109 /// Reference to the renderer singleton's stats
110 const CRenderer::Stats
& Stats
;
112 /// Column descriptions
113 std::vector
<ProfileColumn
> columnDescriptions
;
128 // Must be last to count number of rows
134 CRendererStatsTable::CRendererStatsTable(const CRenderer::Stats
& st
)
137 columnDescriptions
.push_back(ProfileColumn("Name", 230));
138 columnDescriptions
.push_back(ProfileColumn("Value", 100));
141 // Implementation of AbstractProfileTable interface
142 CStr
CRendererStatsTable::GetName()
147 CStr
CRendererStatsTable::GetTitle()
149 return "Renderer statistics";
152 size_t CRendererStatsTable::GetNumberRows()
157 const std::vector
<ProfileColumn
>& CRendererStatsTable::GetColumns()
159 return columnDescriptions
;
162 CStr
CRendererStatsTable::GetCellText(size_t row
, size_t col
)
170 return "# draw calls";
171 sprintf_s(buf
, sizeof(buf
), "%lu", (unsigned long)Stats
.m_DrawCalls
);
174 case Row_TerrainTris
:
176 return "# terrain tris";
177 sprintf_s(buf
, sizeof(buf
), "%lu", (unsigned long)Stats
.m_TerrainTris
);
182 return "# water tris";
183 sprintf_s(buf
, sizeof(buf
), "%lu", (unsigned long)Stats
.m_WaterTris
);
188 return "# model tris";
189 sprintf_s(buf
, sizeof(buf
), "%lu", (unsigned long)Stats
.m_ModelTris
);
192 case Row_OverlayTris
:
194 return "# overlay tris";
195 sprintf_s(buf
, sizeof(buf
), "%lu", (unsigned long)Stats
.m_OverlayTris
);
198 case Row_BlendSplats
:
200 return "# blend splats";
201 sprintf_s(buf
, sizeof(buf
), "%lu", (unsigned long)Stats
.m_BlendSplats
);
206 return "# particles";
207 sprintf_s(buf
, sizeof(buf
), "%lu", (unsigned long)Stats
.m_Particles
);
212 return "VB reserved";
213 sprintf_s(buf
, sizeof(buf
), "%lu kB", (unsigned long)g_VBMan
.GetBytesReserved() / 1024);
216 case Row_VBAllocated
:
218 return "VB allocated";
219 sprintf_s(buf
, sizeof(buf
), "%lu kB", (unsigned long)g_VBMan
.GetBytesAllocated() / 1024);
222 case Row_TextureMemory
:
224 return "textures uploaded";
225 sprintf_s(buf
, sizeof(buf
), "%lu kB", (unsigned long)g_Renderer
.GetTextureManager().GetBytesUploaded() / 1024);
228 case Row_ShadersLoaded
:
230 return "shader effects loaded";
231 sprintf_s(buf
, sizeof(buf
), "%lu", (unsigned long)g_Renderer
.GetShaderManager().GetNumEffectsLoaded());
239 AbstractProfileTable
* CRendererStatsTable::GetChild(size_t UNUSED(row
))
245 ///////////////////////////////////////////////////////////////////////////////////
246 // CRenderer implementation
249 * Struct CRendererInternals: Truly hide data that is supposed to be hidden
250 * in this structure so it won't even appear in header files.
252 struct CRendererInternals
254 NONCOPYABLE(CRendererInternals
);
256 /// true if CRenderer::Open has been called
259 /// true if shaders need to be reloaded
262 /// Table to display renderer stats in-game via profile system
263 CRendererStatsTable profileTable
;
266 CShaderManager shaderManager
;
269 WaterManager waterManager
;
272 SkyManager skyManager
;
275 CTextureManager textureManager
;
278 TerrainRenderer terrainRenderer
;
281 OverlayRenderer overlayRenderer
;
284 CParticleManager particleManager
;
286 /// Particle renderer
287 ParticleRenderer particleRenderer
;
290 CMaterialManager materialManager
;
293 CTimeManager timeManager
;
298 /// Postprocessing effect manager
299 CPostprocManager postprocManager
;
301 CDebugRenderer debugRenderer
;
303 CFontManager fontManager
;
305 SilhouetteRenderer silhouetteRenderer
;
307 /// Various model renderers
310 // NOTE: The current renderer design (with ModelRenderer, ModelVertexRenderer,
311 // RenderModifier, etc) is mostly a relic of an older design that implemented
312 // the different materials and rendering modes through extensive subclassing
313 // and hooking objects together in various combinations.
314 // The new design uses the CShaderManager API to abstract away the details
315 // of rendering, and uses a data-driven approach to materials, so there are
316 // now a small number of generic subclasses instead of many specialised subclasses,
317 // but most of the old infrastructure hasn't been refactored out yet and leads to
318 // some unwanted complexity.
320 // Submitted models are split on two axes:
321 // - Normal vs Transp[arent] - alpha-blended models are stored in a separate
322 // list so we can draw them above/below the alpha-blended water plane correctly
323 // - Skinned vs Unskinned - with hardware lighting we don't need to
324 // duplicate mesh data per model instance (except for skinned models),
325 // so non-skinned models get different ModelVertexRenderers
327 ModelRendererPtr NormalSkinned
;
328 ModelRendererPtr NormalUnskinned
; // == NormalSkinned if unskinned shader instancing not supported
329 ModelRendererPtr TranspSkinned
;
330 ModelRendererPtr TranspUnskinned
; // == TranspSkinned if unskinned shader instancing not supported
332 ModelVertexRendererPtr VertexRendererShader
;
333 ModelVertexRendererPtr VertexInstancingShader
;
334 ModelVertexRendererPtr VertexGPUSkinningShader
;
336 LitRenderModifierPtr ModShader
;
339 CShaderDefines globalContext
;
341 CRendererInternals() :
342 IsOpen(false), ShadersDirty(true), profileTable(g_Renderer
.m_Stats
), textureManager(g_VFS
, false, false)
347 * Renders all non-alpha-blended models with the given context.
349 void CallModelRenderers(const CShaderDefines
& context
, int cullGroup
, int flags
)
351 CShaderDefines contextSkinned
= context
;
352 if (g_RenderingOptions
.GetGPUSkinning())
354 contextSkinned
.Add(str_USE_INSTANCING
, str_1
);
355 contextSkinned
.Add(str_USE_GPU_SKINNING
, str_1
);
357 Model
.NormalSkinned
->Render(Model
.ModShader
, contextSkinned
, cullGroup
, flags
);
359 if (Model
.NormalUnskinned
!= Model
.NormalSkinned
)
361 CShaderDefines contextUnskinned
= context
;
362 contextUnskinned
.Add(str_USE_INSTANCING
, str_1
);
363 Model
.NormalUnskinned
->Render(Model
.ModShader
, contextUnskinned
, cullGroup
, flags
);
368 * Renders all alpha-blended models with the given context.
370 void CallTranspModelRenderers(const CShaderDefines
& context
, int cullGroup
, int flags
)
372 CShaderDefines contextSkinned
= context
;
373 if (g_RenderingOptions
.GetGPUSkinning())
375 contextSkinned
.Add(str_USE_INSTANCING
, str_1
);
376 contextSkinned
.Add(str_USE_GPU_SKINNING
, str_1
);
378 Model
.TranspSkinned
->Render(Model
.ModShader
, contextSkinned
, cullGroup
, flags
);
380 if (Model
.TranspUnskinned
!= Model
.TranspSkinned
)
382 CShaderDefines contextUnskinned
= context
;
383 contextUnskinned
.Add(str_USE_INSTANCING
, str_1
);
384 Model
.TranspUnskinned
->Render(Model
.ModShader
, contextUnskinned
, cullGroup
, flags
);
389 ///////////////////////////////////////////////////////////////////////////////////
390 // CRenderer constructor
391 CRenderer::CRenderer()
393 m
= new CRendererInternals
;
394 m_WaterManager
= &m
->waterManager
;
395 m_SkyManager
= &m
->skyManager
;
397 g_ProfileViewer
.AddRootTable(&m
->profileTable
);
401 m_TerrainRenderMode
= SOLID
;
402 m_WaterRenderMode
= SOLID
;
403 m_ModelRenderMode
= SOLID
;
404 m_OverlayRenderMode
= SOLID
;
405 m_ClearColor
[0] = m_ClearColor
[1] = m_ClearColor
[2] = m_ClearColor
[3] = 0;
407 m_DisplayTerrainPriorities
= false;
408 m_SkipSubmit
= false;
410 CStr skystring
= "0 0 0";
412 CFG_GET_VAL("skycolor", skystring
);
413 if (skycolor
.ParseString(skystring
, 255.f
))
414 SetClearColor(skycolor
.AsSColor4ub());
416 m_LightEnv
= nullptr;
418 m_CurrentScene
= nullptr;
420 m_hCompositeAlphaMap
= 0;
424 RegisterFileReloadFunc(ReloadChangedFileCB
, this);
427 ///////////////////////////////////////////////////////////////////////////////////
428 // CRenderer destructor
429 CRenderer::~CRenderer()
431 UnregisterFileReloadFunc(ReloadChangedFileCB
, this);
433 // we no longer UnloadAlphaMaps / UnloadWaterTextures here -
434 // that is the responsibility of the module that asked for
435 // them to be loaded (i.e. CGameView).
440 ///////////////////////////////////////////////////////////////////////////////////
441 // EnumCaps: build card cap bits
442 void CRenderer::EnumCaps()
444 // assume support for nothing
445 m_Caps
.m_VBO
= false;
446 m_Caps
.m_ARBProgram
= false;
447 m_Caps
.m_ARBProgramShadow
= false;
448 m_Caps
.m_VertexShader
= false;
449 m_Caps
.m_FragmentShader
= false;
450 m_Caps
.m_Shadows
= false;
451 m_Caps
.m_PrettyWater
= false;
453 // now start querying extensions
454 if (!g_RenderingOptions
.GetNoVBO() && ogl_HaveExtension("GL_ARB_vertex_buffer_object"))
457 if (0 == ogl_HaveExtensions(0, "GL_ARB_vertex_program", "GL_ARB_fragment_program", NULL
))
459 m_Caps
.m_ARBProgram
= true;
460 if (ogl_HaveExtension("GL_ARB_fragment_program_shadow"))
461 m_Caps
.m_ARBProgramShadow
= true;
464 if (0 == ogl_HaveExtensions(0, "GL_ARB_shader_objects", "GL_ARB_shading_language_100", NULL
))
466 if (ogl_HaveExtension("GL_ARB_vertex_shader"))
467 m_Caps
.m_VertexShader
= true;
468 if (ogl_HaveExtension("GL_ARB_fragment_shader"))
469 m_Caps
.m_FragmentShader
= true;
473 m_Caps
.m_Shadows
= true;
475 if (0 == ogl_HaveExtensions(0, "GL_ARB_shadow", "GL_ARB_depth_texture", "GL_EXT_framebuffer_object", NULL
))
477 if (ogl_max_tex_units
>= 4)
478 m_Caps
.m_Shadows
= true;
483 m_Caps
.m_PrettyWater
= true;
485 if (0 == ogl_HaveExtensions(0, "GL_ARB_vertex_shader", "GL_ARB_fragment_shader", "GL_EXT_framebuffer_object", NULL
))
486 m_Caps
.m_PrettyWater
= true;
490 void CRenderer::RecomputeSystemShaderDefines()
492 CShaderDefines defines
;
494 if (m_Caps
.m_ARBProgram
)
495 defines
.Add(str_SYS_HAS_ARB
, str_1
);
497 if (m_Caps
.m_VertexShader
&& m_Caps
.m_FragmentShader
)
498 defines
.Add(str_SYS_HAS_GLSL
, str_1
);
500 if (g_RenderingOptions
.GetPreferGLSL())
501 defines
.Add(str_SYS_PREFER_GLSL
, str_1
);
503 m_SystemShaderDefines
= defines
;
506 void CRenderer::ReloadShaders()
510 m
->globalContext
= m_SystemShaderDefines
;
512 if (m_Caps
.m_Shadows
&& g_RenderingOptions
.GetShadows())
514 m
->globalContext
.Add(str_USE_SHADOW
, str_1
);
515 if (m_Caps
.m_ARBProgramShadow
&& g_RenderingOptions
.GetARBProgramShadow())
516 m
->globalContext
.Add(str_USE_FP_SHADOW
, str_1
);
517 if (g_RenderingOptions
.GetShadowPCF())
518 m
->globalContext
.Add(str_USE_SHADOW_PCF
, str_1
);
520 m
->globalContext
.Add(str_USE_SHADOW_SAMPLER
, str_1
);
524 if (g_RenderingOptions
.GetPreferGLSL() && g_RenderingOptions
.GetFog())
525 m
->globalContext
.Add(str_USE_FOG
, str_1
);
527 m
->Model
.ModShader
= LitRenderModifierPtr(new ShaderRenderModifier());
529 ENSURE(g_RenderingOptions
.GetRenderPath() != RenderPath::FIXED
);
530 m
->Model
.VertexRendererShader
= ModelVertexRendererPtr(new ShaderModelVertexRenderer());
531 m
->Model
.VertexInstancingShader
= ModelVertexRendererPtr(new InstancingModelRenderer(false, g_RenderingOptions
.GetPreferGLSL()));
533 if (g_RenderingOptions
.GetGPUSkinning()) // TODO: should check caps and GLSL etc too
535 m
->Model
.VertexGPUSkinningShader
= ModelVertexRendererPtr(new InstancingModelRenderer(true, g_RenderingOptions
.GetPreferGLSL()));
536 m
->Model
.NormalSkinned
= ModelRendererPtr(new ShaderModelRenderer(m
->Model
.VertexGPUSkinningShader
));
537 m
->Model
.TranspSkinned
= ModelRendererPtr(new ShaderModelRenderer(m
->Model
.VertexGPUSkinningShader
));
541 m
->Model
.VertexGPUSkinningShader
.reset();
542 m
->Model
.NormalSkinned
= ModelRendererPtr(new ShaderModelRenderer(m
->Model
.VertexRendererShader
));
543 m
->Model
.TranspSkinned
= ModelRendererPtr(new ShaderModelRenderer(m
->Model
.VertexRendererShader
));
546 m
->Model
.NormalUnskinned
= ModelRendererPtr(new ShaderModelRenderer(m
->Model
.VertexInstancingShader
));
547 m
->Model
.TranspUnskinned
= ModelRendererPtr(new ShaderModelRenderer(m
->Model
.VertexInstancingShader
));
549 m
->ShadersDirty
= false;
552 bool CRenderer::Open(int width
, int height
)
556 // Must query card capabilities before creating renderers that depend
557 // on card capabilities.
564 // set packing parameters
565 glPixelStorei(GL_PACK_ALIGNMENT
,1);
566 glPixelStorei(GL_UNPACK_ALIGNMENT
,1);
568 // setup default state
569 glDepthFunc(GL_LEQUAL
);
570 glEnable(GL_DEPTH_TEST
);
573 glEnable(GL_CULL_FACE
);
576 glGetIntegerv(GL_DEPTH_BITS
,&bits
);
577 LOGMESSAGE("CRenderer::Open: depth bits %d",bits
);
578 glGetIntegerv(GL_STENCIL_BITS
,&bits
);
579 LOGMESSAGE("CRenderer::Open: stencil bits %d",bits
);
580 glGetIntegerv(GL_ALPHA_BITS
,&bits
);
581 LOGMESSAGE("CRenderer::Open: alpha bits %d",bits
);
583 // Validate the currently selected render path
584 SetRenderPath(g_RenderingOptions
.GetRenderPath());
586 RecomputeSystemShaderDefines();
588 // Let component renderers perform one-time initialization after graphics capabilities and
589 // the shader path have been determined.
590 m
->overlayRenderer
.Initialize();
592 if (g_RenderingOptions
.GetPostProc())
593 m
->postprocManager
.Initialize();
598 // resize renderer view
599 void CRenderer::Resize(int width
, int height
)
601 // need to recreate the shadow map object to resize the shadow texture
602 m
->shadow
.RecreateTexture();
607 m
->postprocManager
.Resize();
609 m_WaterManager
->Resize();
612 //////////////////////////////////////////////////////////////////////////////////////////
613 // SetRenderPath: Select the preferred render path.
614 // This may only be called before Open(), because the layout of vertex arrays and other
615 // data may depend on the chosen render path.
616 void CRenderer::SetRenderPath(RenderPath rp
)
620 // Delay until Open() is called.
624 // Renderer has been opened, so validate the selected renderpath
625 if (rp
== RenderPath::DEFAULT
)
627 if (m_Caps
.m_ARBProgram
|| (m_Caps
.m_VertexShader
&& m_Caps
.m_FragmentShader
&& g_RenderingOptions
.GetPreferGLSL()))
628 rp
= RenderPath::SHADER
;
630 rp
= RenderPath::FIXED
;
633 if (rp
== RenderPath::SHADER
)
635 if (!(m_Caps
.m_ARBProgram
|| (m_Caps
.m_VertexShader
&& m_Caps
.m_FragmentShader
&& g_RenderingOptions
.GetPreferGLSL())))
637 LOGWARNING("Falling back to fixed function\n");
638 rp
= RenderPath::FIXED
;
642 // TODO: remove this once capabilities have been properly extracted and the above checks have been moved elsewhere.
643 g_RenderingOptions
.m_RenderPath
= rp
;
646 RecomputeSystemShaderDefines();
648 // We might need to regenerate some render data after changing path
650 g_Game
->GetWorld()->GetTerrain()->MakeDirty(RENDERDATA_UPDATE_COLOR
);
653 //////////////////////////////////////////////////////////////////////////////////////////
654 // BeginFrame: signal frame start
655 void CRenderer::BeginFrame()
657 PROFILE("begin frame");
659 // zero out all the per-frame stats
662 // choose model renderers for this frame
667 m
->Model
.ModShader
->SetShadowMap(&m
->shadow
);
668 m
->Model
.ModShader
->SetLightEnv(m_LightEnv
);
671 //////////////////////////////////////////////////////////////////////////////////////////
672 void CRenderer::SetSimulation(CSimulation2
* simulation
)
674 // set current simulation context for terrain renderer
675 m
->terrainRenderer
.SetSimulation(simulation
);
678 // SetClearColor: set color used to clear screen in BeginFrame()
679 void CRenderer::SetClearColor(SColor4ub color
)
681 m_ClearColor
[0] = float(color
.R
) / 255.0f
;
682 m_ClearColor
[1] = float(color
.G
) / 255.0f
;
683 m_ClearColor
[2] = float(color
.B
) / 255.0f
;
684 m_ClearColor
[3] = float(color
.A
) / 255.0f
;
687 void CRenderer::RenderShadowMap(const CShaderDefines
& context
)
689 PROFILE3_GPU("shadow map");
691 m
->shadow
.BeginRender();
694 PROFILE("render patches");
695 glCullFace(GL_FRONT
);
696 glEnable(GL_CULL_FACE
);
697 m
->terrainRenderer
.RenderPatches(CULL_SHADOWS
);
701 CShaderDefines contextCast
= context
;
702 contextCast
.Add(str_MODE_SHADOWCAST
, str_1
);
705 PROFILE("render models");
706 m
->CallModelRenderers(contextCast
, CULL_SHADOWS
, MODELFLAG_CASTSHADOWS
);
710 PROFILE("render transparent models");
711 // disable face-culling for two-sided models
712 glDisable(GL_CULL_FACE
);
713 m
->CallTranspModelRenderers(contextCast
, CULL_SHADOWS
, MODELFLAG_CASTSHADOWS
);
714 glEnable(GL_CULL_FACE
);
717 m
->shadow
.EndRender();
719 SetViewport(m_ViewCamera
.GetViewPort());
722 void CRenderer::RenderPatches(const CShaderDefines
& context
, int cullGroup
)
724 PROFILE3_GPU("patches");
727 #warning TODO: implement wireface/edged rendering mode GLES
729 // switch on wireframe if we need it
730 if (m_TerrainRenderMode
== WIREFRAME
)
732 glPolygonMode(GL_FRONT_AND_BACK
, GL_LINE
);
736 // render all the patches, including blend pass
737 ENSURE(g_RenderingOptions
.GetRenderPath() != RenderPath::FIXED
);
738 m
->terrainRenderer
.RenderTerrainShader(context
, cullGroup
, (m_Caps
.m_Shadows
&& g_RenderingOptions
.GetShadows()) ? &m
->shadow
: 0);
741 if (m_TerrainRenderMode
== WIREFRAME
)
743 // switch wireframe off again
744 glPolygonMode(GL_FRONT_AND_BACK
, GL_FILL
);
746 else if (m_TerrainRenderMode
== EDGED_FACES
)
748 // edged faces: need to make a second pass over the data:
749 // first switch on wireframe
750 glPolygonMode(GL_FRONT_AND_BACK
, GL_LINE
);
752 // setup some renderstate ..
753 pglActiveTextureARB(GL_TEXTURE0
);
754 glDisable(GL_TEXTURE_2D
);
757 // render tiles edges
758 m
->terrainRenderer
.RenderPatches(cullGroup
, CColor(0.5f
, 0.5f
, 1.0f
, 1.0f
));
762 // render outline of each patch
763 m
->terrainRenderer
.RenderOutlines(cullGroup
);
765 // .. and restore the renderstates
767 glPolygonMode(GL_FRONT_AND_BACK
, GL_FILL
);
772 void CRenderer::RenderModels(const CShaderDefines
& context
, int cullGroup
)
774 PROFILE3_GPU("models");
779 if (m_ModelRenderMode
== WIREFRAME
)
781 glPolygonMode(GL_FRONT_AND_BACK
, GL_LINE
);
785 m
->CallModelRenderers(context
, cullGroup
, flags
);
788 if (m_ModelRenderMode
== WIREFRAME
)
790 glPolygonMode(GL_FRONT_AND_BACK
, GL_FILL
);
792 else if (m_ModelRenderMode
== EDGED_FACES
)
794 CShaderDefines contextWireframe
= context
;
795 contextWireframe
.Add(str_MODE_WIREFRAME
, str_1
);
797 glPolygonMode(GL_FRONT_AND_BACK
, GL_LINE
);
798 glDisable(GL_TEXTURE_2D
);
800 m
->CallModelRenderers(contextWireframe
, cullGroup
, flags
);
802 glPolygonMode(GL_FRONT_AND_BACK
, GL_FILL
);
807 void CRenderer::RenderTransparentModels(const CShaderDefines
& context
, int cullGroup
, ETransparentMode transparentMode
, bool disableFaceCulling
)
809 PROFILE3_GPU("transparent models");
814 // switch on wireframe if we need it
815 if (m_ModelRenderMode
== WIREFRAME
)
817 glPolygonMode(GL_FRONT_AND_BACK
, GL_LINE
);
821 // disable face culling for two-sided models in sub-renders
822 if (disableFaceCulling
)
823 glDisable(GL_CULL_FACE
);
825 CShaderDefines contextOpaque
= context
;
826 contextOpaque
.Add(str_ALPHABLEND_PASS_OPAQUE
, str_1
);
828 CShaderDefines contextBlend
= context
;
829 contextBlend
.Add(str_ALPHABLEND_PASS_BLEND
, str_1
);
831 if (transparentMode
== TRANSPARENT
|| transparentMode
== TRANSPARENT_OPAQUE
)
832 m
->CallTranspModelRenderers(contextOpaque
, cullGroup
, flags
);
834 if (transparentMode
== TRANSPARENT
|| transparentMode
== TRANSPARENT_BLEND
)
835 m
->CallTranspModelRenderers(contextBlend
, cullGroup
, flags
);
837 if (disableFaceCulling
)
838 glEnable(GL_CULL_FACE
);
841 if (m_ModelRenderMode
== WIREFRAME
)
843 // switch wireframe off again
844 glPolygonMode(GL_FRONT_AND_BACK
, GL_FILL
);
846 else if (m_ModelRenderMode
== EDGED_FACES
)
848 CShaderDefines contextWireframe
= contextOpaque
;
849 contextWireframe
.Add(str_MODE_WIREFRAME
, str_1
);
851 glPolygonMode(GL_FRONT_AND_BACK
, GL_LINE
);
852 glDisable(GL_TEXTURE_2D
);
854 m
->CallTranspModelRenderers(contextWireframe
, cullGroup
, flags
);
856 glPolygonMode(GL_FRONT_AND_BACK
, GL_FILL
);
862 ///////////////////////////////////////////////////////////////////////////////////////////////////
863 // SetObliqueFrustumClipping: change the near plane to the given clip plane (in world space)
864 // Based on code from Game Programming Gems 5, from http://www.terathon.com/code/oblique.html
865 // - worldPlane is a clip plane in world space (worldPlane.Dot(v) >= 0 for any vector v passing the clipping test)
866 void CRenderer::SetObliqueFrustumClipping(CCamera
& camera
, const CVector4D
& worldPlane
) const
868 // First, we'll convert the given clip plane to camera space, then we'll
869 // Get the view matrix and normal matrix (top 3x3 part of view matrix)
870 CMatrix3D normalMatrix
= camera
.GetOrientation().GetTranspose();
871 CVector4D camPlane
= normalMatrix
.Transform(worldPlane
);
873 CMatrix3D matrix
= camera
.GetProjection();
875 // Calculate the clip-space corner point opposite the clipping plane
876 // as (sgn(camPlane.x), sgn(camPlane.y), 1, 1) and
877 // transform it into camera space by multiplying it
878 // by the inverse of the projection matrix
881 q
.X
= (sgn(camPlane
.X
) - matrix
[8]/matrix
[11]) / matrix
[0];
882 q
.Y
= (sgn(camPlane
.Y
) - matrix
[9]/matrix
[11]) / matrix
[5];
883 q
.Z
= 1.0f
/matrix
[11];
884 q
.W
= (1.0f
- matrix
[10]/matrix
[11]) / matrix
[14];
886 // Calculate the scaled plane vector
887 CVector4D c
= camPlane
* (2.0f
* matrix
[11] / camPlane
.Dot(q
));
889 // Replace the third row of the projection matrix
892 matrix
[10] = c
.Z
- matrix
[11];
895 // Load it back into the camera
896 camera
.SetProjection(matrix
);
899 void CRenderer::ComputeReflectionCamera(CCamera
& camera
, const CBoundingBoxAligned
& scissor
) const
901 WaterManager
& wm
= m
->waterManager
;
903 CMatrix3D projection
;
904 if (m_ViewCamera
.GetProjectionType() == CCamera::ProjectionType::PERSPECTIVE
)
906 const float aspectRatio
= 1.0f
;
907 // Expand fov slightly since ripples can reflect parts of the scene that
908 // are slightly outside the normal camera view, and we want to avoid any
909 // noticeable edge-filtering artifacts
910 projection
.SetPerspective(m_ViewCamera
.GetFOV() * 1.05f
, aspectRatio
, m_ViewCamera
.GetNearPlane(), m_ViewCamera
.GetFarPlane());
913 projection
= m_ViewCamera
.GetProjection();
915 camera
= m_ViewCamera
;
917 // Temporarily change the camera to one that is reflected.
918 // Also, for texturing purposes, make it render to a view port the size of the
919 // water texture, stretch the image according to our aspect ratio so it covers
920 // the whole screen despite being rendered into a square, and cover slightly more
921 // of the view so we can see wavy reflections of slightly off-screen objects.
922 camera
.m_Orientation
.Scale(1, -1, 1);
923 camera
.m_Orientation
.Translate(0, 2 * wm
.m_WaterHeight
, 0);
924 camera
.UpdateFrustum(scissor
);
925 // Clip slightly above the water to improve reflections of objects on the water
926 // when the reflections are distorted.
927 camera
.ClipFrustum(CVector4D(0, 1, 0, -wm
.m_WaterHeight
+ 2.0f
));
930 vp
.m_Height
= wm
.m_RefTextureSize
;
931 vp
.m_Width
= wm
.m_RefTextureSize
;
934 camera
.SetViewPort(vp
);
935 camera
.SetProjection(projection
);
937 scaleMat
.SetScaling(m_Height
/ static_cast<float>(std::max(1, m_Width
)), 1.0f
, 1.0f
);
938 camera
.SetProjection(scaleMat
* camera
.GetProjection());
940 CVector4D
camPlane(0, 1, 0, -wm
.m_WaterHeight
+ 0.5f
);
941 SetObliqueFrustumClipping(camera
, camPlane
);
944 void CRenderer::ComputeRefractionCamera(CCamera
& camera
, const CBoundingBoxAligned
& scissor
) const
946 WaterManager
& wm
= m
->waterManager
;
948 CMatrix3D projection
;
949 if (m_ViewCamera
.GetProjectionType() == CCamera::ProjectionType::PERSPECTIVE
)
951 const float aspectRatio
= 1.0f
;
952 // Expand fov slightly since ripples can reflect parts of the scene that
953 // are slightly outside the normal camera view, and we want to avoid any
954 // noticeable edge-filtering artifacts
955 projection
.SetPerspective(m_ViewCamera
.GetFOV() * 1.05f
, aspectRatio
, m_ViewCamera
.GetNearPlane(), m_ViewCamera
.GetFarPlane());
958 projection
= m_ViewCamera
.GetProjection();
960 camera
= m_ViewCamera
;
962 // Temporarily change the camera to make it render to a view port the size of the
963 // water texture, stretch the image according to our aspect ratio so it covers
964 // the whole screen despite being rendered into a square, and cover slightly more
965 // of the view so we can see wavy refractions of slightly off-screen objects.
966 camera
.UpdateFrustum(scissor
);
967 camera
.ClipFrustum(CVector4D(0, -1, 0, wm
.m_WaterHeight
+ 0.5f
)); // add some to avoid artifacts near steep shores.
970 vp
.m_Height
= wm
.m_RefTextureSize
;
971 vp
.m_Width
= wm
.m_RefTextureSize
;
974 camera
.SetViewPort(vp
);
975 camera
.SetProjection(projection
);
977 scaleMat
.SetScaling(m_Height
/ static_cast<float>(std::max(1, m_Width
)), 1.0f
, 1.0f
);
978 camera
.SetProjection(scaleMat
* camera
.GetProjection());
981 ///////////////////////////////////////////////////////////////////////////////////////////////////
982 // RenderReflections: render the water reflections to the reflection texture
983 void CRenderer::RenderReflections(const CShaderDefines
& context
, const CBoundingBoxAligned
& scissor
)
985 PROFILE3_GPU("water reflections");
987 WaterManager
& wm
= m
->waterManager
;
989 // Remember old camera
990 CCamera normalCamera
= m_ViewCamera
;
992 ComputeReflectionCamera(m_ViewCamera
, scissor
);
993 const CBoundingBoxAligned reflectionScissor
=
994 m
->terrainRenderer
.ScissorWater(CULL_DEFAULT
, m_ViewCamera
);
996 SetViewport(m_ViewCamera
.GetViewPort());
998 // Save the model-view-projection matrix so the shaders can use it for projective texturing
999 wm
.m_ReflectionMatrix
= m_ViewCamera
.GetViewProjection();
1001 float vpHeight
= wm
.m_RefTextureSize
;
1002 float vpWidth
= wm
.m_RefTextureSize
;
1004 SScreenRect screenScissor
;
1005 screenScissor
.x1
= (GLint
)floor((reflectionScissor
[0].X
*0.5f
+0.5f
)*vpWidth
);
1006 screenScissor
.y1
= (GLint
)floor((reflectionScissor
[0].Y
*0.5f
+0.5f
)*vpHeight
);
1007 screenScissor
.x2
= (GLint
)ceil((reflectionScissor
[1].X
*0.5f
+0.5f
)*vpWidth
);
1008 screenScissor
.y2
= (GLint
)ceil((reflectionScissor
[1].Y
*0.5f
+0.5f
)*vpHeight
);
1010 glEnable(GL_SCISSOR_TEST
);
1011 glScissor(screenScissor
.x1
, screenScissor
.y1
, screenScissor
.x2
- screenScissor
.x1
, screenScissor
.y2
- screenScissor
.y1
);
1013 // try binding the framebuffer
1014 pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT
, wm
.m_ReflectionFbo
);
1016 glClearColor(0.5f
, 0.5f
, 1.0f
, 0.0f
);
1017 glClear(GL_COLOR_BUFFER_BIT
| GL_DEPTH_BUFFER_BIT
);
1021 if (!g_RenderingOptions
.GetWaterReflection())
1023 m
->skyManager
.RenderSky();
1028 // Render terrain and models
1029 RenderPatches(context
, CULL_REFLECTIONS
);
1031 RenderModels(context
, CULL_REFLECTIONS
);
1033 RenderTransparentModels(context
, CULL_REFLECTIONS
, TRANSPARENT
, true);
1036 glFrontFace(GL_CCW
);
1038 // Particles are always oriented to face the camera in the vertex shader,
1039 // so they don't need the inverted glFrontFace
1040 if (g_RenderingOptions
.GetParticles())
1042 RenderParticles(CULL_REFLECTIONS
);
1046 glDisable(GL_SCISSOR_TEST
);
1049 m_ViewCamera
= normalCamera
;
1050 SetViewport(m_ViewCamera
.GetViewPort());
1052 pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT
, 0);
1056 ///////////////////////////////////////////////////////////////////////////////////////////////////
1057 // RenderRefractions: render the water refractions to the refraction texture
1058 void CRenderer::RenderRefractions(const CShaderDefines
& context
, const CBoundingBoxAligned
&scissor
)
1060 PROFILE3_GPU("water refractions");
1062 WaterManager
& wm
= m
->waterManager
;
1064 // Remember old camera
1065 CCamera normalCamera
= m_ViewCamera
;
1067 ComputeRefractionCamera(m_ViewCamera
, scissor
);
1068 const CBoundingBoxAligned refractionScissor
=
1069 m
->terrainRenderer
.ScissorWater(CULL_DEFAULT
, m_ViewCamera
);
1071 CVector4D
camPlane(0, -1, 0, wm
.m_WaterHeight
+ 2.0f
);
1072 SetObliqueFrustumClipping(m_ViewCamera
, camPlane
);
1074 SetViewport(m_ViewCamera
.GetViewPort());
1076 // Save the model-view-projection matrix so the shaders can use it for projective texturing
1077 wm
.m_RefractionMatrix
= m_ViewCamera
.GetViewProjection();
1078 wm
.m_RefractionProjInvMatrix
= m_ViewCamera
.GetProjection().GetInverse();
1079 wm
.m_RefractionViewInvMatrix
= m_ViewCamera
.GetOrientation();
1081 float vpHeight
= wm
.m_RefTextureSize
;
1082 float vpWidth
= wm
.m_RefTextureSize
;
1084 SScreenRect screenScissor
;
1085 screenScissor
.x1
= (GLint
)floor((refractionScissor
[0].X
*0.5f
+0.5f
)*vpWidth
);
1086 screenScissor
.y1
= (GLint
)floor((refractionScissor
[0].Y
*0.5f
+0.5f
)*vpHeight
);
1087 screenScissor
.x2
= (GLint
)ceil((refractionScissor
[1].X
*0.5f
+0.5f
)*vpWidth
);
1088 screenScissor
.y2
= (GLint
)ceil((refractionScissor
[1].Y
*0.5f
+0.5f
)*vpHeight
);
1090 glEnable(GL_SCISSOR_TEST
);
1091 glScissor(screenScissor
.x1
, screenScissor
.y1
, screenScissor
.x2
- screenScissor
.x1
, screenScissor
.y2
- screenScissor
.y1
);
1093 // try binding the framebuffer
1094 pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT
, wm
.m_RefractionFbo
);
1096 glClearColor(1.0f
, 0.0f
, 0.0f
, 0.0f
);
1097 glClear(GL_COLOR_BUFFER_BIT
| GL_DEPTH_BUFFER_BIT
);
1099 // Render terrain and models
1100 RenderPatches(context
, CULL_REFRACTIONS
);
1102 RenderModels(context
, CULL_REFRACTIONS
);
1104 RenderTransparentModels(context
, CULL_REFRACTIONS
, TRANSPARENT_OPAQUE
, false);
1107 glDisable(GL_SCISSOR_TEST
);
1110 m_ViewCamera
= normalCamera
;
1111 SetViewport(m_ViewCamera
.GetViewPort());
1113 pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT
, 0);
1116 void CRenderer::RenderSilhouettes(const CShaderDefines
& context
)
1118 PROFILE3_GPU("silhouettes");
1120 CShaderDefines contextOccluder
= context
;
1121 contextOccluder
.Add(str_MODE_SILHOUETTEOCCLUDER
, str_1
);
1123 CShaderDefines contextDisplay
= context
;
1124 contextDisplay
.Add(str_MODE_SILHOUETTEDISPLAY
, str_1
);
1126 // Render silhouettes of units hidden behind terrain or occluders.
1127 // To avoid breaking the standard rendering of alpha-blended objects, this
1128 // has to be done in a separate pass.
1129 // First we render all occluders into depth, then render all units with
1130 // inverted depth test so any behind an occluder will get drawn in a constant
1133 float silhouetteAlpha
= 0.75f
;
1135 // Silhouette blending requires an almost-universally-supported extension;
1136 // fall back to non-blended if unavailable
1137 if (!ogl_HaveExtension("GL_EXT_blend_color"))
1138 silhouetteAlpha
= 1.f
;
1140 glClear(GL_DEPTH_BUFFER_BIT
| GL_STENCIL_BUFFER_BIT
);
1142 glColorMask(0, 0, 0, 0);
1144 // Render occluders:
1147 PROFILE("render patches");
1149 // To prevent units displaying silhouettes when parts of their model
1150 // protrude into the ground, only occlude with the back faces of the
1151 // terrain (so silhouettes will still display when behind hills)
1152 glCullFace(GL_FRONT
);
1153 m
->terrainRenderer
.RenderPatches(CULL_SILHOUETTE_OCCLUDER
);
1154 glCullFace(GL_BACK
);
1158 PROFILE("render model occluders");
1159 m
->CallModelRenderers(contextOccluder
, CULL_SILHOUETTE_OCCLUDER
, 0);
1163 PROFILE("render transparent occluders");
1164 m
->CallTranspModelRenderers(contextOccluder
, CULL_SILHOUETTE_OCCLUDER
, 0);
1167 glDepthFunc(GL_GEQUAL
);
1168 glColorMask(1, 1, 1, 1);
1170 // Render more efficiently if alpha == 1
1171 if (silhouetteAlpha
== 1.f
)
1173 // Ideally we'd render objects back-to-front so nearer silhouettes would
1174 // appear on top, but sorting has non-zero cost. So we'll keep the depth
1175 // write enabled, to do the opposite - far objects will consistently appear
1181 // Since we can't sort, we'll use the stencil buffer to ensure we only draw
1182 // a pixel once (using the color of whatever model happens to be drawn first).
1184 glBlendFunc(GL_CONSTANT_ALPHA
, GL_ONE_MINUS_CONSTANT_ALPHA
);
1185 pglBlendColorEXT(0, 0, 0, silhouetteAlpha
);
1187 glEnable(GL_STENCIL_TEST
);
1188 glStencilFunc(GL_NOTEQUAL
, 1, (GLuint
)-1);
1189 glStencilOp(GL_KEEP
, GL_KEEP
, GL_REPLACE
);
1193 PROFILE("render model casters");
1194 m
->CallModelRenderers(contextDisplay
, CULL_SILHOUETTE_CASTER
, 0);
1198 PROFILE("render transparent casters");
1199 m
->CallTranspModelRenderers(contextDisplay
, CULL_SILHOUETTE_CASTER
, 0);
1203 glDepthFunc(GL_LEQUAL
);
1204 if (silhouetteAlpha
== 1.f
)
1210 glDisable(GL_BLEND
);
1211 glBlendFunc(GL_SRC_ALPHA
, GL_ONE_MINUS_SRC_ALPHA
);
1212 pglBlendColorEXT(0, 0, 0, 0);
1213 glDisable(GL_STENCIL_TEST
);
1217 void CRenderer::RenderParticles(int cullGroup
)
1219 PROFILE3_GPU("particles");
1221 m
->particleRenderer
.RenderParticles(cullGroup
);
1224 if (m_ModelRenderMode
== EDGED_FACES
)
1226 glPolygonMode(GL_FRONT_AND_BACK
, GL_LINE
);
1228 m
->particleRenderer
.RenderParticles(true);
1229 m
->particleRenderer
.RenderBounds(cullGroup
);
1231 glPolygonMode(GL_FRONT_AND_BACK
, GL_FILL
);
1236 ///////////////////////////////////////////////////////////////////////////////////////////////////
1237 // RenderSubmissions: force rendering of any batched objects
1238 void CRenderer::RenderSubmissions(const CBoundingBoxAligned
& waterScissor
)
1240 PROFILE3("render submissions");
1242 GetScene().GetLOSTexture().InterpolateLOS();
1244 CShaderDefines context
= m
->globalContext
;
1246 int cullGroup
= CULL_DEFAULT
;
1251 SetViewport(m_ViewCamera
.GetViewPort());
1253 // Prepare model renderers
1255 PROFILE3("prepare models");
1256 m
->Model
.NormalSkinned
->PrepareModels();
1257 m
->Model
.TranspSkinned
->PrepareModels();
1258 if (m
->Model
.NormalUnskinned
!= m
->Model
.NormalSkinned
)
1259 m
->Model
.NormalUnskinned
->PrepareModels();
1260 if (m
->Model
.TranspUnskinned
!= m
->Model
.TranspSkinned
)
1261 m
->Model
.TranspUnskinned
->PrepareModels();
1264 m
->terrainRenderer
.PrepareForRendering();
1266 m
->overlayRenderer
.PrepareForRendering();
1268 m
->particleRenderer
.PrepareForRendering(context
);
1270 if (m_Caps
.m_Shadows
&& g_RenderingOptions
.GetShadows())
1272 RenderShadowMap(context
);
1277 if (m_WaterManager
->m_RenderWater
)
1279 if (waterScissor
.GetVolume() > 0 && m_WaterManager
->WillRenderFancyWater())
1281 PROFILE3_GPU("water scissor");
1282 RenderReflections(context
, waterScissor
);
1284 if (g_RenderingOptions
.GetWaterRefraction())
1285 RenderRefractions(context
, waterScissor
);
1289 if (g_RenderingOptions
.GetPostProc())
1291 // We have to update the post process manager with real near/far planes
1292 // that we use for the scene rendering.
1293 m
->postprocManager
.SetDepthBufferClipPlanes(
1294 m_ViewCamera
.GetNearPlane(), m_ViewCamera
.GetFarPlane()
1296 m
->postprocManager
.Initialize();
1297 m
->postprocManager
.CaptureRenderOutput();
1301 PROFILE3_GPU("clear buffers");
1302 glClearColor(m_ClearColor
[0], m_ClearColor
[1], m_ClearColor
[2], m_ClearColor
[3]);
1303 glClear(GL_COLOR_BUFFER_BIT
| GL_DEPTH_BUFFER_BIT
| GL_STENCIL_BUFFER_BIT
);
1306 m
->skyManager
.RenderSky();
1308 // render submitted patches and models
1309 RenderPatches(context
, cullGroup
);
1312 // render debug-related terrain overlays
1313 ITerrainOverlay::RenderOverlaysBeforeWater();
1316 // render other debug-related overlays before water (so they can be seen when underwater)
1317 m
->overlayRenderer
.RenderOverlaysBeforeWater();
1320 RenderModels(context
, cullGroup
);
1324 if (m_WaterManager
->m_RenderWater
&& g_Game
&& waterScissor
.GetVolume() > 0)
1326 if (m_WaterManager
->WillRenderFancyWater())
1328 // Render transparent stuff, but only the solid parts that can occlude block water.
1329 RenderTransparentModels(context
, cullGroup
, TRANSPARENT_OPAQUE
, false);
1332 m
->terrainRenderer
.RenderWater(context
, cullGroup
, &m
->shadow
);
1335 // Render transparent stuff again, but only the blended parts that overlap water.
1336 RenderTransparentModels(context
, cullGroup
, TRANSPARENT_BLEND
, false);
1341 m
->terrainRenderer
.RenderWater(context
, cullGroup
, &m
->shadow
);
1344 // Render transparent stuff, so it can overlap models/terrain.
1345 RenderTransparentModels(context
, cullGroup
, TRANSPARENT
, false);
1351 // render transparent stuff, so it can overlap models/terrain
1352 RenderTransparentModels(context
, cullGroup
, TRANSPARENT
, false);
1356 // render debug-related terrain overlays
1357 ITerrainOverlay::RenderOverlaysAfterWater(cullGroup
);
1360 // render some other overlays after water (so they can be displayed on top of water)
1361 m
->overlayRenderer
.RenderOverlaysAfterWater();
1364 // particles are transparent so render after water
1365 if (g_RenderingOptions
.GetParticles())
1367 RenderParticles(cullGroup
);
1371 if (g_RenderingOptions
.GetPostProc())
1373 if (g_Renderer
.GetPostprocManager().IsMultisampleEnabled())
1374 g_Renderer
.GetPostprocManager().ResolveMultisampleFramebuffer();
1376 m
->postprocManager
.ApplyPostproc();
1377 m
->postprocManager
.ReleaseRenderOutput();
1380 if (g_RenderingOptions
.GetSilhouettes())
1382 RenderSilhouettes(context
);
1385 // render debug lines
1386 if (g_RenderingOptions
.GetDisplayFrustum())
1389 if (g_RenderingOptions
.GetDisplayShadowsFrustum())
1391 m
->shadow
.RenderDebugBounds();
1392 m
->shadow
.RenderDebugTexture();
1395 m
->silhouetteRenderer
.RenderDebugOverlays(m_ViewCamera
);
1397 // render overlays that should appear on top of all other objects
1398 m
->overlayRenderer
.RenderForegroundOverlays(m_ViewCamera
);
1403 ///////////////////////////////////////////////////////////////////////////////////////////////////
1404 // EndFrame: signal frame end
1405 void CRenderer::EndFrame()
1407 PROFILE3("end frame");
1410 m
->terrainRenderer
.EndFrame();
1411 m
->overlayRenderer
.EndFrame();
1412 m
->particleRenderer
.EndFrame();
1413 m
->silhouetteRenderer
.EndFrame();
1415 // Finish model renderers
1416 m
->Model
.NormalSkinned
->EndFrame();
1417 m
->Model
.TranspSkinned
->EndFrame();
1418 if (m
->Model
.NormalUnskinned
!= m
->Model
.NormalSkinned
)
1419 m
->Model
.NormalUnskinned
->EndFrame();
1420 if (m
->Model
.TranspUnskinned
!= m
->Model
.TranspSkinned
)
1421 m
->Model
.TranspUnskinned
->EndFrame();
1426 void CRenderer::OnSwapBuffers()
1428 bool checkGLErrorAfterSwap
= false;
1429 CFG_GET_VAL("gl.checkerrorafterswap", checkGLErrorAfterSwap
);
1430 if (!checkGLErrorAfterSwap
)
1432 PROFILE3("error check");
1433 // We have to check GL errors after SwapBuffer to avoid possible
1434 // synchronizations during rendering.
1435 if (GLenum err
= glGetError())
1436 ONCE(LOGERROR("GL error %s (0x%04x) occurred", ogl_GetErrorName(err
), err
));
1439 ///////////////////////////////////////////////////////////////////////////////////////////////////
1440 // DisplayFrustum: debug displays
1441 // - white: cull camera frustum
1442 // - red: bounds of shadow casting objects
1443 void CRenderer::DisplayFrustum()
1446 #warning TODO: implement CRenderer::DisplayFrustum for GLES
1449 glDisable(GL_CULL_FACE
);
1450 glDisable(GL_TEXTURE_2D
);
1453 glBlendFunc(GL_SRC_ALPHA
, GL_ONE_MINUS_SRC_ALPHA
);
1454 GetDebugRenderer().DrawCameraFrustum(m_CullCamera
, CColor(1.0f
, 1.0f
, 1.0f
, 0.25f
), 2);
1455 glDisable(GL_BLEND
);
1457 glPolygonMode(GL_FRONT_AND_BACK
, GL_LINE
);
1458 GetDebugRenderer().DrawCameraFrustum(m_CullCamera
, CColor(1.0f
, 1.0f
, 1.0f
, 1.0f
), 2);
1459 glPolygonMode(GL_FRONT_AND_BACK
, GL_FILL
);
1461 glEnable(GL_CULL_FACE
);
1468 ///////////////////////////////////////////////////////////////////////////////////////////////////
1469 // Text overlay rendering
1470 void CRenderer::RenderTextOverlays()
1472 PROFILE3_GPU("text overlays");
1474 if (m_DisplayTerrainPriorities
)
1475 m
->terrainRenderer
.RenderPriorities(CULL_DEFAULT
);
1480 ///////////////////////////////////////////////////////////////////////////////////////////////////
1481 // SetSceneCamera: setup projection and transform of camera and adjust viewport to current view
1482 // The camera always represents the actual camera used to render a scene, not any virtual camera
1483 // used for shadow rendering or reflections.
1484 void CRenderer::SetSceneCamera(const CCamera
& viewCamera
, const CCamera
& cullCamera
)
1486 m_ViewCamera
= viewCamera
;
1487 m_CullCamera
= cullCamera
;
1489 if (m_Caps
.m_Shadows
&& g_RenderingOptions
.GetShadows())
1490 m
->shadow
.SetupFrame(m_CullCamera
, m_LightEnv
->GetSunDir());
1494 void CRenderer::SetViewport(const SViewPort
&vp
)
1497 glViewport((GLint
)vp
.m_X
,(GLint
)vp
.m_Y
,(GLsizei
)vp
.m_Width
,(GLsizei
)vp
.m_Height
);
1500 SViewPort
CRenderer::GetViewport()
1505 void CRenderer::Submit(CPatch
* patch
)
1507 if (m_CurrentCullGroup
== CULL_DEFAULT
)
1509 m
->shadow
.AddShadowReceiverBound(patch
->GetWorldBounds());
1510 m
->silhouetteRenderer
.AddOccluder(patch
);
1513 if (m_CurrentCullGroup
== CULL_SHADOWS
)
1515 m
->shadow
.AddShadowCasterBound(patch
->GetWorldBounds());
1518 m
->terrainRenderer
.Submit(m_CurrentCullGroup
, patch
);
1521 void CRenderer::Submit(SOverlayLine
* overlay
)
1523 // Overlays are only needed in the default cull group for now,
1524 // so just ignore submissions to any other group
1525 if (m_CurrentCullGroup
== CULL_DEFAULT
)
1526 m
->overlayRenderer
.Submit(overlay
);
1529 void CRenderer::Submit(SOverlayTexturedLine
* overlay
)
1531 if (m_CurrentCullGroup
== CULL_DEFAULT
)
1532 m
->overlayRenderer
.Submit(overlay
);
1535 void CRenderer::Submit(SOverlaySprite
* overlay
)
1537 if (m_CurrentCullGroup
== CULL_DEFAULT
)
1538 m
->overlayRenderer
.Submit(overlay
);
1541 void CRenderer::Submit(SOverlayQuad
* overlay
)
1543 if (m_CurrentCullGroup
== CULL_DEFAULT
)
1544 m
->overlayRenderer
.Submit(overlay
);
1547 void CRenderer::Submit(SOverlaySphere
* overlay
)
1549 if (m_CurrentCullGroup
== CULL_DEFAULT
)
1550 m
->overlayRenderer
.Submit(overlay
);
1553 void CRenderer::Submit(CModelDecal
* decal
)
1555 // Decals can't cast shadows since they're flat on the terrain.
1556 // They can receive shadows, but the terrain under them will have
1557 // already been passed to AddShadowCasterBound, so don't bother
1558 // doing it again here.
1560 m
->terrainRenderer
.Submit(m_CurrentCullGroup
, decal
);
1563 void CRenderer::Submit(CParticleEmitter
* emitter
)
1565 m
->particleRenderer
.Submit(m_CurrentCullGroup
, emitter
);
1568 void CRenderer::SubmitNonRecursive(CModel
* model
)
1570 if (m_CurrentCullGroup
== CULL_DEFAULT
)
1572 m
->shadow
.AddShadowReceiverBound(model
->GetWorldBounds());
1574 if (model
->GetFlags() & MODELFLAG_SILHOUETTE_OCCLUDER
)
1575 m
->silhouetteRenderer
.AddOccluder(model
);
1576 if (model
->GetFlags() & MODELFLAG_SILHOUETTE_DISPLAY
)
1577 m
->silhouetteRenderer
.AddCaster(model
);
1580 if (m_CurrentCullGroup
== CULL_SHADOWS
)
1582 if (!(model
->GetFlags() & MODELFLAG_CASTSHADOWS
))
1585 m
->shadow
.AddShadowCasterBound(model
->GetWorldBounds());
1588 bool requiresSkinning
= (model
->GetModelDef()->GetNumBones() != 0);
1590 if (model
->GetMaterial().UsesAlphaBlending())
1592 if (requiresSkinning
)
1593 m
->Model
.TranspSkinned
->Submit(m_CurrentCullGroup
, model
);
1595 m
->Model
.TranspUnskinned
->Submit(m_CurrentCullGroup
, model
);
1599 if (requiresSkinning
)
1600 m
->Model
.NormalSkinned
->Submit(m_CurrentCullGroup
, model
);
1602 m
->Model
.NormalUnskinned
->Submit(m_CurrentCullGroup
, model
);
1607 ///////////////////////////////////////////////////////////
1608 // Render the given scene
1609 void CRenderer::RenderScene(Scene
& scene
)
1611 m_CurrentScene
= &scene
;
1613 CFrustum frustum
= m_CullCamera
.GetFrustum();
1615 m_CurrentCullGroup
= CULL_DEFAULT
;
1617 scene
.EnumerateObjects(frustum
, this);
1619 m
->particleManager
.RenderSubmit(*this, frustum
);
1621 if (g_RenderingOptions
.GetSilhouettes())
1623 m
->silhouetteRenderer
.ComputeSubmissions(m_ViewCamera
);
1625 m_CurrentCullGroup
= CULL_DEFAULT
;
1626 m
->silhouetteRenderer
.RenderSubmitOverlays(*this);
1628 m_CurrentCullGroup
= CULL_SILHOUETTE_OCCLUDER
;
1629 m
->silhouetteRenderer
.RenderSubmitOccluders(*this);
1631 m_CurrentCullGroup
= CULL_SILHOUETTE_CASTER
;
1632 m
->silhouetteRenderer
.RenderSubmitCasters(*this);
1635 if (m_Caps
.m_Shadows
&& g_RenderingOptions
.GetShadows())
1637 m_CurrentCullGroup
= CULL_SHADOWS
;
1639 CFrustum shadowFrustum
= m
->shadow
.GetShadowCasterCullFrustum();
1640 scene
.EnumerateObjects(shadowFrustum
, this);
1643 CBoundingBoxAligned waterScissor
;
1644 if (m_WaterManager
->m_RenderWater
)
1646 waterScissor
= m
->terrainRenderer
.ScissorWater(CULL_DEFAULT
, m_ViewCamera
);
1648 if (waterScissor
.GetVolume() > 0 && m_WaterManager
->WillRenderFancyWater())
1650 if (g_RenderingOptions
.GetWaterReflection())
1652 m_CurrentCullGroup
= CULL_REFLECTIONS
;
1654 CCamera reflectionCamera
;
1655 ComputeReflectionCamera(reflectionCamera
, waterScissor
);
1657 scene
.EnumerateObjects(reflectionCamera
.GetFrustum(), this);
1660 if (g_RenderingOptions
.GetWaterRefraction())
1662 m_CurrentCullGroup
= CULL_REFRACTIONS
;
1664 CCamera refractionCamera
;
1665 ComputeRefractionCamera(refractionCamera
, waterScissor
);
1667 scene
.EnumerateObjects(refractionCamera
.GetFrustum(), this);
1670 // Render the waves to the Fancy effects texture
1671 m_WaterManager
->RenderWaves(frustum
);
1675 m_CurrentCullGroup
= -1;
1679 RenderSubmissions(waterScissor
);
1681 m_CurrentScene
= NULL
;
1684 Scene
& CRenderer::GetScene()
1686 ENSURE(m_CurrentScene
);
1687 return *m_CurrentScene
;
1690 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1691 // BindTexture: bind a GL texture object to current active unit
1692 void CRenderer::BindTexture(int unit
, GLuint tex
)
1694 pglActiveTextureARB(GL_TEXTURE0
+unit
);
1696 glBindTexture(GL_TEXTURE_2D
, tex
);
1699 glEnable(GL_TEXTURE_2D
);
1701 glDisable(GL_TEXTURE_2D
);
1706 ///////////////////////////////////////////////////////////////////////////////////////////////////
1707 // LoadAlphaMaps: load the 14 default alpha maps, pack them into one composite texture and
1708 // calculate the coordinate of each alphamap within this packed texture
1709 // NB: A variant of this function is duplicated in TerrainTextureEntry.cpp, for use with the Shader
1710 // renderpath. This copy is kept to load the 'standard' maps for the fixed pipeline and should
1711 // be removed if/when the fixed pipeline goes.
1712 int CRenderer::LoadAlphaMaps()
1714 const wchar_t* const key
= L
"(alpha map composite)";
1715 Handle ht
= ogl_tex_find(key
);
1716 // alpha map texture had already been created and is still in memory:
1717 // reuse it, do not load again.
1720 m_hCompositeAlphaMap
= ht
;
1725 // load all textures and store Handle in array
1727 Handle textures
[NumAlphaMaps
] = {0};
1728 VfsPath
path(L
"art/textures/terrain/alphamaps/standard");
1729 const wchar_t* fnames
[NumAlphaMaps
] = {
1733 L
"blendedgecorner.png",
1734 L
"blendedgetwocorners.png",
1735 L
"blendfourcorners.png",
1736 L
"blendtwooppositecorners.png",
1737 L
"blendlshapecorner.png",
1738 L
"blendtwocorners.png",
1740 L
"blendtwoedges.png",
1741 L
"blendthreecorners.png",
1745 size_t base
= 0; // texture width/height (see below)
1746 // for convenience, we require all alpha maps to be of the same BPP
1747 // (avoids another ogl_tex_get_size call, and doesn't hurt)
1749 for(size_t i
=0;i
<NumAlphaMaps
;i
++)
1751 // note: these individual textures can be discarded afterwards;
1752 // we cache the composite.
1753 textures
[i
] = ogl_tex_load(g_VFS
, path
/ fnames
[i
]);
1754 RETURN_STATUS_IF_ERR(textures
[i
]);
1756 // get its size and make sure they are all equal.
1757 // (the packing algo assumes this)
1758 size_t this_width
= 0, this_height
= 0, this_bpp
= 0; // fail-safe
1759 ignore_result(ogl_tex_get_size(textures
[i
], &this_width
, &this_height
, &this_bpp
));
1760 if(this_width
!= this_height
)
1761 DEBUG_DISPLAY_ERROR(L
"Alpha maps are not square");
1762 // .. first iteration: establish size
1768 // .. not first: make sure texture size matches
1769 else if(base
!= this_width
|| bpp
!= this_bpp
)
1770 DEBUG_DISPLAY_ERROR(L
"Alpha maps are not identically sized (including pixel depth)");
1774 // copy each alpha map (tile) into one buffer, arrayed horizontally.
1776 size_t tile_w
= 2+base
+2; // 2 pixel border (avoids bilinear filtering artifacts)
1777 size_t total_w
= round_up_to_pow2(tile_w
* NumAlphaMaps
);
1778 size_t total_h
= base
; ENSURE(is_pow2(total_h
));
1779 shared_ptr
<u8
> data
;
1780 AllocateAligned(data
, total_w
*total_h
, maxSectorSize
);
1781 // for each tile on row
1782 for (size_t i
= 0; i
< NumAlphaMaps
; i
++)
1786 ignore_result(ogl_tex_get_data(textures
[i
], &src
));
1788 size_t srcstep
= bpp
/8;
1790 // get destination of copy
1791 u8
* dst
= data
.get() + (i
*tile_w
);
1793 // for each row of image
1794 for (size_t j
= 0; j
< base
; j
++)
1796 // duplicate first pixel
1801 for (size_t k
= 0; k
< base
; k
++)
1807 // duplicate last pixel
1808 *dst
++ = *(src
-srcstep
);
1809 *dst
++ = *(src
-srcstep
);
1811 // advance write pointer for next row
1812 dst
+= total_w
-tile_w
;
1815 m_AlphaMapCoords
[i
].u0
= float(i
*tile_w
+2) / float(total_w
);
1816 m_AlphaMapCoords
[i
].u1
= float((i
+1)*tile_w
-2) / float(total_w
);
1817 m_AlphaMapCoords
[i
].v0
= 0.0f
;
1818 m_AlphaMapCoords
[i
].v1
= 1.0f
;
1821 for (size_t i
= 0; i
< NumAlphaMaps
; i
++)
1822 ignore_result(ogl_tex_free(textures
[i
]));
1824 // upload the composite texture
1826 ignore_result(t
.wrap(total_w
, total_h
, 8, TEX_GREY
, data
, 0));
1828 /*VfsPath filename("blendtex.png");
1831 RETURN_STATUS_IF_ERR(tex_encode(&t, filename.Extension(), &da));
1834 //Status ret = INFO::OK;
1836 shared_ptr<u8> file = DummySharedPtr(da.base);
1837 const ssize_t bytes_written = g_VFS->CreateFile(filename, file, da.pos);
1838 if(bytes_written > 0)
1839 ENSURE(bytes_written == (ssize_t)da.pos);
1841 // ret = (Status)bytes_written;
1844 ignore_result(da_free(&da));*/
1846 m_hCompositeAlphaMap
= ogl_tex_wrap(&t
, g_VFS
, key
);
1847 ignore_result(ogl_tex_set_filter(m_hCompositeAlphaMap
, GL_LINEAR
));
1848 ignore_result(ogl_tex_set_wrap (m_hCompositeAlphaMap
, GL_CLAMP_TO_EDGE
, GL_CLAMP_TO_EDGE
));
1849 int ret
= ogl_tex_upload(m_hCompositeAlphaMap
, GL_ALPHA
, 0, 0);
1854 ///////////////////////////////////////////////////////////////////////////////////////////////////
1855 // UnloadAlphaMaps: frees the resources allocates by LoadAlphaMaps
1856 void CRenderer::UnloadAlphaMaps()
1858 ogl_tex_free(m_hCompositeAlphaMap
);
1859 m_hCompositeAlphaMap
= 0;
1864 Status
CRenderer::ReloadChangedFileCB(void* param
, const VfsPath
& path
)
1866 CRenderer
* renderer
= static_cast<CRenderer
*>(param
);
1868 // If an alpha map changed, and we already loaded them, then reload them
1869 if (boost::algorithm::starts_with(path
.string(), L
"art/textures/terrain/alphamaps/"))
1871 if (renderer
->m_hCompositeAlphaMap
)
1873 renderer
->UnloadAlphaMaps();
1874 renderer
->LoadAlphaMaps();
1881 void CRenderer::MakeShadersDirty()
1883 m
->ShadersDirty
= true;
1884 m_WaterManager
->m_NeedsReloading
= true;
1887 CTextureManager
& CRenderer::GetTextureManager()
1889 return m
->textureManager
;
1892 CShaderManager
& CRenderer::GetShaderManager()
1894 return m
->shaderManager
;
1897 CParticleManager
& CRenderer::GetParticleManager()
1899 return m
->particleManager
;
1902 TerrainRenderer
& CRenderer::GetTerrainRenderer()
1904 return m
->terrainRenderer
;
1907 CTimeManager
& CRenderer::GetTimeManager()
1909 return m
->timeManager
;
1912 CMaterialManager
& CRenderer::GetMaterialManager()
1914 return m
->materialManager
;
1917 CPostprocManager
& CRenderer::GetPostprocManager()
1919 return m
->postprocManager
;
1922 CDebugRenderer
& CRenderer::GetDebugRenderer()
1924 return m
->debugRenderer
;
1927 CFontManager
& CRenderer::GetFontManager()
1929 return m
->fontManager
;
1932 ShadowMap
& CRenderer::GetShadowMap()
1937 void CRenderer::ResetState()
1939 // Clear all emitters, that were created in previous games
1940 GetParticleManager().ClearUnattachedEmitters();