Darker AO for the stone quary
[0ad.git] / source / renderer / Renderer.cpp
bloba687ee050e469ade4fa334c7a4cce0d693e6c171
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"
25 #include "Renderer.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"
35 #include "ps/Game.h"
36 #include "ps/Profile.h"
37 #include "ps/Filesystem.h"
38 #include "ps/World.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"
74 #include <algorithm>
75 #include <boost/algorithm/string.hpp>
76 #include <map>
77 #include <set>
79 struct SScreenRect
81 GLint x1, y1, x2, y2;
84 ///////////////////////////////////////////////////////////////////////////////////
85 // CRendererStatsTable - Profile display of rendering stats
87 /**
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
92 * constructor.
94 class CRendererStatsTable : public AbstractProfileTable
96 NONCOPYABLE(CRendererStatsTable);
97 public:
98 CRendererStatsTable(const CRenderer::Stats& st);
100 // Implementation of AbstractProfileTable interface
101 CStr GetName();
102 CStr GetTitle();
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);
108 private:
109 /// Reference to the renderer singleton's stats
110 const CRenderer::Stats& Stats;
112 /// Column descriptions
113 std::vector<ProfileColumn> columnDescriptions;
115 enum {
116 Row_DrawCalls = 0,
117 Row_TerrainTris,
118 Row_WaterTris,
119 Row_ModelTris,
120 Row_OverlayTris,
121 Row_BlendSplats,
122 Row_Particles,
123 Row_VBReserved,
124 Row_VBAllocated,
125 Row_TextureMemory,
126 Row_ShadersLoaded,
128 // Must be last to count number of rows
129 NumberRows
133 // Construction
134 CRendererStatsTable::CRendererStatsTable(const CRenderer::Stats& st)
135 : 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()
144 return "renderer";
147 CStr CRendererStatsTable::GetTitle()
149 return "Renderer statistics";
152 size_t CRendererStatsTable::GetNumberRows()
154 return NumberRows;
157 const std::vector<ProfileColumn>& CRendererStatsTable::GetColumns()
159 return columnDescriptions;
162 CStr CRendererStatsTable::GetCellText(size_t row, size_t col)
164 char buf[256];
166 switch(row)
168 case Row_DrawCalls:
169 if (col == 0)
170 return "# draw calls";
171 sprintf_s(buf, sizeof(buf), "%lu", (unsigned long)Stats.m_DrawCalls);
172 return buf;
174 case Row_TerrainTris:
175 if (col == 0)
176 return "# terrain tris";
177 sprintf_s(buf, sizeof(buf), "%lu", (unsigned long)Stats.m_TerrainTris);
178 return buf;
180 case Row_WaterTris:
181 if (col == 0)
182 return "# water tris";
183 sprintf_s(buf, sizeof(buf), "%lu", (unsigned long)Stats.m_WaterTris);
184 return buf;
186 case Row_ModelTris:
187 if (col == 0)
188 return "# model tris";
189 sprintf_s(buf, sizeof(buf), "%lu", (unsigned long)Stats.m_ModelTris);
190 return buf;
192 case Row_OverlayTris:
193 if (col == 0)
194 return "# overlay tris";
195 sprintf_s(buf, sizeof(buf), "%lu", (unsigned long)Stats.m_OverlayTris);
196 return buf;
198 case Row_BlendSplats:
199 if (col == 0)
200 return "# blend splats";
201 sprintf_s(buf, sizeof(buf), "%lu", (unsigned long)Stats.m_BlendSplats);
202 return buf;
204 case Row_Particles:
205 if (col == 0)
206 return "# particles";
207 sprintf_s(buf, sizeof(buf), "%lu", (unsigned long)Stats.m_Particles);
208 return buf;
210 case Row_VBReserved:
211 if (col == 0)
212 return "VB reserved";
213 sprintf_s(buf, sizeof(buf), "%lu kB", (unsigned long)g_VBMan.GetBytesReserved() / 1024);
214 return buf;
216 case Row_VBAllocated:
217 if (col == 0)
218 return "VB allocated";
219 sprintf_s(buf, sizeof(buf), "%lu kB", (unsigned long)g_VBMan.GetBytesAllocated() / 1024);
220 return buf;
222 case Row_TextureMemory:
223 if (col == 0)
224 return "textures uploaded";
225 sprintf_s(buf, sizeof(buf), "%lu kB", (unsigned long)g_Renderer.GetTextureManager().GetBytesUploaded() / 1024);
226 return buf;
228 case Row_ShadersLoaded:
229 if (col == 0)
230 return "shader effects loaded";
231 sprintf_s(buf, sizeof(buf), "%lu", (unsigned long)g_Renderer.GetShaderManager().GetNumEffectsLoaded());
232 return buf;
234 default:
235 return "???";
239 AbstractProfileTable* CRendererStatsTable::GetChild(size_t UNUSED(row))
241 return 0;
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);
255 public:
256 /// true if CRenderer::Open has been called
257 bool IsOpen;
259 /// true if shaders need to be reloaded
260 bool ShadersDirty;
262 /// Table to display renderer stats in-game via profile system
263 CRendererStatsTable profileTable;
265 /// Shader manager
266 CShaderManager shaderManager;
268 /// Water manager
269 WaterManager waterManager;
271 /// Sky manager
272 SkyManager skyManager;
274 /// Texture manager
275 CTextureManager textureManager;
277 /// Terrain renderer
278 TerrainRenderer terrainRenderer;
280 /// Overlay renderer
281 OverlayRenderer overlayRenderer;
283 /// Particle manager
284 CParticleManager particleManager;
286 /// Particle renderer
287 ParticleRenderer particleRenderer;
289 /// Material manager
290 CMaterialManager materialManager;
292 /// Time manager
293 CTimeManager timeManager;
295 /// Shadow map
296 ShadowMap shadow;
298 /// Postprocessing effect manager
299 CPostprocManager postprocManager;
301 CDebugRenderer debugRenderer;
303 CFontManager fontManager;
305 SilhouetteRenderer silhouetteRenderer;
307 /// Various model renderers
308 struct Models
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;
337 } Model;
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);
399 m_Width = 0;
400 m_Height = 0;
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";
411 CColor skycolor;
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;
422 m_Stats.Reset();
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).
436 delete m;
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"))
455 m_Caps.m_VBO = true;
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;
472 #if CONFIG2_GLES
473 m_Caps.m_Shadows = true;
474 #else
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;
480 #endif
482 #if CONFIG2_GLES
483 m_Caps.m_PrettyWater = true;
484 #else
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;
487 #endif
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()
508 ENSURE(m->IsOpen);
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);
519 #if !CONFIG2_GLES
520 m->globalContext.Add(str_USE_SHADOW_SAMPLER, str_1);
521 #endif
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));
539 else
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)
554 m->IsOpen = true;
556 // Must query card capabilities before creating renderers that depend
557 // on card capabilities.
558 EnumCaps();
560 // Dimensions
561 m_Width = width;
562 m_Height = height;
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);
571 glCullFace(GL_BACK);
572 glFrontFace(GL_CCW);
573 glEnable(GL_CULL_FACE);
575 GLint bits;
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();
595 return true;
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();
604 m_Width = width;
605 m_Height = height;
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)
618 if (!m->IsOpen)
620 // Delay until Open() is called.
621 return;
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;
629 else
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;
645 MakeShadersDirty();
646 RecomputeSystemShaderDefines();
648 // We might need to regenerate some render data after changing path
649 if (g_Game)
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
660 m_Stats.Reset();
662 // choose model renderers for this frame
664 if (m->ShadersDirty)
665 ReloadShaders();
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);
698 glCullFace(GL_BACK);
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");
726 #if CONFIG2_GLES
727 #warning TODO: implement wireface/edged rendering mode GLES
728 #else
729 // switch on wireframe if we need it
730 if (m_TerrainRenderMode == WIREFRAME)
732 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
734 #endif
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);
740 #if !CONFIG2_GLES
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);
755 glLineWidth(2.0f);
757 // render tiles edges
758 m->terrainRenderer.RenderPatches(cullGroup, CColor(0.5f, 0.5f, 1.0f, 1.0f));
760 glLineWidth(4.0f);
762 // render outline of each patch
763 m->terrainRenderer.RenderOutlines(cullGroup);
765 // .. and restore the renderstates
766 glLineWidth(1.0f);
767 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
769 #endif
772 void CRenderer::RenderModels(const CShaderDefines& context, int cullGroup)
774 PROFILE3_GPU("models");
776 int flags = 0;
778 #if !CONFIG2_GLES
779 if (m_ModelRenderMode == WIREFRAME)
781 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
783 #endif
785 m->CallModelRenderers(context, cullGroup, flags);
787 #if !CONFIG2_GLES
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);
804 #endif
807 void CRenderer::RenderTransparentModels(const CShaderDefines& context, int cullGroup, ETransparentMode transparentMode, bool disableFaceCulling)
809 PROFILE3_GPU("transparent models");
811 int flags = 0;
813 #if !CONFIG2_GLES
814 // switch on wireframe if we need it
815 if (m_ModelRenderMode == WIREFRAME)
817 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
819 #endif
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);
840 #if !CONFIG2_GLES
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);
858 #endif
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
880 CVector4D q;
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
890 matrix[2] = c.X;
891 matrix[6] = c.Y;
892 matrix[10] = c.Z - matrix[11];
893 matrix[14] = c.W;
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());
912 else
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));
929 SViewPort vp;
930 vp.m_Height = wm.m_RefTextureSize;
931 vp.m_Width = wm.m_RefTextureSize;
932 vp.m_X = 0;
933 vp.m_Y = 0;
934 camera.SetViewPort(vp);
935 camera.SetProjection(projection);
936 CMatrix3D scaleMat;
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());
957 else
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.
969 SViewPort vp;
970 vp.m_Height = wm.m_RefTextureSize;
971 vp.m_Width = wm.m_RefTextureSize;
972 vp.m_X = 0;
973 vp.m_Y = 0;
974 camera.SetViewPort(vp);
975 camera.SetProjection(projection);
976 CMatrix3D scaleMat;
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);
1019 glFrontFace(GL_CW);
1021 if (!g_RenderingOptions.GetWaterReflection())
1023 m->skyManager.RenderSky();
1024 ogl_WarnIfError();
1026 else
1028 // Render terrain and models
1029 RenderPatches(context, CULL_REFLECTIONS);
1030 ogl_WarnIfError();
1031 RenderModels(context, CULL_REFLECTIONS);
1032 ogl_WarnIfError();
1033 RenderTransparentModels(context, CULL_REFLECTIONS, TRANSPARENT, true);
1034 ogl_WarnIfError();
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);
1043 ogl_WarnIfError();
1046 glDisable(GL_SCISSOR_TEST);
1048 // Reset old camera
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);
1101 ogl_WarnIfError();
1102 RenderModels(context, CULL_REFRACTIONS);
1103 ogl_WarnIfError();
1104 RenderTransparentModels(context, CULL_REFRACTIONS, TRANSPARENT_OPAQUE, false);
1105 ogl_WarnIfError();
1107 glDisable(GL_SCISSOR_TEST);
1109 // Reset old camera
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
1131 // color.
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
1176 // on top.
1177 glDepthMask(0);
1179 else
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).
1183 glEnable(GL_BLEND);
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);
1202 // Restore state
1203 glDepthFunc(GL_LEQUAL);
1204 if (silhouetteAlpha == 1.f)
1206 glDepthMask(1);
1208 else
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);
1223 #if !CONFIG2_GLES
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);
1233 #endif
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;
1248 ogl_WarnIfError();
1250 // Set the camera
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);
1275 ogl_WarnIfError();
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);
1310 ogl_WarnIfError();
1312 // render debug-related terrain overlays
1313 ITerrainOverlay::RenderOverlaysBeforeWater();
1314 ogl_WarnIfError();
1316 // render other debug-related overlays before water (so they can be seen when underwater)
1317 m->overlayRenderer.RenderOverlaysBeforeWater();
1318 ogl_WarnIfError();
1320 RenderModels(context, cullGroup);
1321 ogl_WarnIfError();
1323 // render water
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);
1330 ogl_WarnIfError();
1332 m->terrainRenderer.RenderWater(context, cullGroup, &m->shadow);
1333 ogl_WarnIfError();
1335 // Render transparent stuff again, but only the blended parts that overlap water.
1336 RenderTransparentModels(context, cullGroup, TRANSPARENT_BLEND, false);
1337 ogl_WarnIfError();
1339 else
1341 m->terrainRenderer.RenderWater(context, cullGroup, &m->shadow);
1342 ogl_WarnIfError();
1344 // Render transparent stuff, so it can overlap models/terrain.
1345 RenderTransparentModels(context, cullGroup, TRANSPARENT, false);
1346 ogl_WarnIfError();
1349 else
1351 // render transparent stuff, so it can overlap models/terrain
1352 RenderTransparentModels(context, cullGroup, TRANSPARENT, false);
1353 ogl_WarnIfError();
1356 // render debug-related terrain overlays
1357 ITerrainOverlay::RenderOverlaysAfterWater(cullGroup);
1358 ogl_WarnIfError();
1360 // render some other overlays after water (so they can be displayed on top of water)
1361 m->overlayRenderer.RenderOverlaysAfterWater();
1362 ogl_WarnIfError();
1364 // particles are transparent so render after water
1365 if (g_RenderingOptions.GetParticles())
1367 RenderParticles(cullGroup);
1368 ogl_WarnIfError();
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())
1387 DisplayFrustum();
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);
1399 ogl_WarnIfError();
1403 ///////////////////////////////////////////////////////////////////////////////////////////////////
1404 // EndFrame: signal frame end
1405 void CRenderer::EndFrame()
1407 PROFILE3("end frame");
1409 // empty lists
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();
1423 ogl_tex_bind(0, 0);
1426 void CRenderer::OnSwapBuffers()
1428 bool checkGLErrorAfterSwap = false;
1429 CFG_GET_VAL("gl.checkerrorafterswap", checkGLErrorAfterSwap);
1430 if (!checkGLErrorAfterSwap)
1431 return;
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()
1445 #if CONFIG2_GLES
1446 #warning TODO: implement CRenderer::DisplayFrustum for GLES
1447 #else
1448 glDepthMask(0);
1449 glDisable(GL_CULL_FACE);
1450 glDisable(GL_TEXTURE_2D);
1452 glEnable(GL_BLEND);
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);
1462 glDepthMask(1);
1463 #endif
1465 ogl_WarnIfError();
1468 ///////////////////////////////////////////////////////////////////////////////////////////////////
1469 // Text overlay rendering
1470 void CRenderer::RenderTextOverlays()
1472 PROFILE3_GPU("text overlays");
1474 if (m_DisplayTerrainPriorities)
1475 m->terrainRenderer.RenderPriorities(CULL_DEFAULT);
1477 ogl_WarnIfError();
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)
1496 m_Viewport = vp;
1497 glViewport((GLint)vp.m_X,(GLint)vp.m_Y,(GLsizei)vp.m_Width,(GLsizei)vp.m_Height);
1500 SViewPort CRenderer::GetViewport()
1502 return m_Viewport;
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))
1583 return;
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);
1594 else
1595 m->Model.TranspUnskinned->Submit(m_CurrentCullGroup, model);
1597 else
1599 if (requiresSkinning)
1600 m->Model.NormalSkinned->Submit(m_CurrentCullGroup, model);
1601 else
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;
1677 ogl_WarnIfError();
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);
1697 #if !CONFIG2_GLES
1698 if (tex) {
1699 glEnable(GL_TEXTURE_2D);
1700 } else {
1701 glDisable(GL_TEXTURE_2D);
1703 #endif
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.
1718 if(ht > 0)
1720 m_hCompositeAlphaMap = ht;
1721 return 0;
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] = {
1730 L"blendcircle.png",
1731 L"blendlshape.png",
1732 L"blendedge.png",
1733 L"blendedgecorner.png",
1734 L"blendedgetwocorners.png",
1735 L"blendfourcorners.png",
1736 L"blendtwooppositecorners.png",
1737 L"blendlshapecorner.png",
1738 L"blendtwocorners.png",
1739 L"blendcorner.png",
1740 L"blendtwoedges.png",
1741 L"blendthreecorners.png",
1742 L"blendushape.png",
1743 L"blendbad.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)
1748 size_t bpp = 0;
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
1763 if(i == 0)
1765 base = this_width;
1766 bpp = this_bpp;
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++)
1784 // get src of copy
1785 u8* src = 0;
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
1797 *dst++ = *src;
1798 *dst++ = *src;
1800 // copy a row
1801 for (size_t k = 0; k < base; k++)
1803 *dst++ = *src;
1804 src += srcstep;
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
1825 Tex t;
1826 ignore_result(t.wrap(total_w, total_h, 8, TEX_GREY, data, 0));
1828 /*VfsPath filename("blendtex.png");
1830 DynArray da;
1831 RETURN_STATUS_IF_ERR(tex_encode(&t, filename.Extension(), &da));
1833 // write to disk
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);
1840 //else
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);
1851 return ret;
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();
1878 return INFO::OK;
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()
1934 return m->shadow;
1937 void CRenderer::ResetState()
1939 // Clear all emitters, that were created in previous games
1940 GetParticleManager().ClearUnattachedEmitters();