1 /* Copyright (C) 2017 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"
29 #include <boost/algorithm/string.hpp>
33 #include "lib/bits.h" // is_pow2
34 #include "lib/res/graphics/ogl_tex.h"
35 #include "lib/allocators/shared_ptr.h"
36 #include "maths/Matrix3D.h"
37 #include "maths/MathUtil.h"
38 #include "ps/CLogger.h"
39 #include "ps/ConfigDB.h"
41 #include "ps/Profile.h"
42 #include "ps/Filesystem.h"
44 #include "ps/Loader.h"
45 #include "ps/ProfileViewer.h"
46 #include "graphics/Camera.h"
47 #include "graphics/Decal.h"
48 #include "graphics/FontManager.h"
49 #include "graphics/GameView.h"
50 #include "graphics/LightEnv.h"
51 #include "graphics/LOSTexture.h"
52 #include "graphics/MaterialManager.h"
53 #include "graphics/Model.h"
54 #include "graphics/ModelDef.h"
55 #include "graphics/ParticleManager.h"
56 #include "graphics/Patch.h"
57 #include "graphics/ShaderManager.h"
58 #include "graphics/Terrain.h"
59 #include "graphics/Texture.h"
60 #include "graphics/TextureManager.h"
61 #include "renderer/HWLightingModelRenderer.h"
62 #include "renderer/InstancingModelRenderer.h"
63 #include "renderer/ModelRenderer.h"
64 #include "renderer/OverlayRenderer.h"
65 #include "renderer/ParticleRenderer.h"
66 #include "renderer/PostprocManager.h"
67 #include "renderer/RenderModifiers.h"
68 #include "renderer/ShadowMap.h"
69 #include "renderer/SilhouetteRenderer.h"
70 #include "renderer/SkyManager.h"
71 #include "renderer/TerrainOverlay.h"
72 #include "renderer/TerrainRenderer.h"
73 #include "renderer/TimeManager.h"
74 #include "renderer/VertexBufferManager.h"
75 #include "renderer/WaterManager.h"
76 #include "scriptinterface/ScriptInterface.h"
83 ///////////////////////////////////////////////////////////////////////////////////
84 // CRendererStatsTable - Profile display of rendering stats
87 * Class CRendererStatsTable: Implementation of AbstractProfileTable to
88 * display the renderer stats in-game.
90 * Accesses CRenderer::m_Stats by keeping the reference passed to the
93 class CRendererStatsTable
: public AbstractProfileTable
95 NONCOPYABLE(CRendererStatsTable
);
97 CRendererStatsTable(const CRenderer::Stats
& st
);
99 // Implementation of AbstractProfileTable interface
102 size_t GetNumberRows();
103 const std::vector
<ProfileColumn
>& GetColumns();
104 CStr
GetCellText(size_t row
, size_t col
);
105 AbstractProfileTable
* GetChild(size_t row
);
108 /// Reference to the renderer singleton's stats
109 const CRenderer::Stats
& Stats
;
111 /// Column descriptions
112 std::vector
<ProfileColumn
> columnDescriptions
;
127 // Must be last to count number of rows
133 CRendererStatsTable::CRendererStatsTable(const CRenderer::Stats
& st
)
136 columnDescriptions
.push_back(ProfileColumn("Name", 230));
137 columnDescriptions
.push_back(ProfileColumn("Value", 100));
140 // Implementation of AbstractProfileTable interface
141 CStr
CRendererStatsTable::GetName()
146 CStr
CRendererStatsTable::GetTitle()
148 return "Renderer statistics";
151 size_t CRendererStatsTable::GetNumberRows()
156 const std::vector
<ProfileColumn
>& CRendererStatsTable::GetColumns()
158 return columnDescriptions
;
161 CStr
CRendererStatsTable::GetCellText(size_t row
, size_t col
)
169 return "# draw calls";
170 sprintf_s(buf
, sizeof(buf
), "%lu", (unsigned long)Stats
.m_DrawCalls
);
173 case Row_TerrainTris
:
175 return "# terrain tris";
176 sprintf_s(buf
, sizeof(buf
), "%lu", (unsigned long)Stats
.m_TerrainTris
);
181 return "# water tris";
182 sprintf_s(buf
, sizeof(buf
), "%lu", (unsigned long)Stats
.m_WaterTris
);
187 return "# model tris";
188 sprintf_s(buf
, sizeof(buf
), "%lu", (unsigned long)Stats
.m_ModelTris
);
191 case Row_OverlayTris
:
193 return "# overlay tris";
194 sprintf_s(buf
, sizeof(buf
), "%lu", (unsigned long)Stats
.m_OverlayTris
);
197 case Row_BlendSplats
:
199 return "# blend splats";
200 sprintf_s(buf
, sizeof(buf
), "%lu", (unsigned long)Stats
.m_BlendSplats
);
205 return "# particles";
206 sprintf_s(buf
, sizeof(buf
), "%lu", (unsigned long)Stats
.m_Particles
);
211 return "VB reserved";
212 sprintf_s(buf
, sizeof(buf
), "%lu kB", (unsigned long)g_VBMan
.GetBytesReserved() / 1024);
215 case Row_VBAllocated
:
217 return "VB allocated";
218 sprintf_s(buf
, sizeof(buf
), "%lu kB", (unsigned long)g_VBMan
.GetBytesAllocated() / 1024);
221 case Row_TextureMemory
:
223 return "textures uploaded";
224 sprintf_s(buf
, sizeof(buf
), "%lu kB", (unsigned long)g_Renderer
.GetTextureManager().GetBytesUploaded() / 1024);
227 case Row_ShadersLoaded
:
229 return "shader effects loaded";
230 sprintf_s(buf
, sizeof(buf
), "%lu", (unsigned long)g_Renderer
.GetShaderManager().GetNumEffectsLoaded());
238 AbstractProfileTable
* CRendererStatsTable::GetChild(size_t UNUSED(row
))
244 ///////////////////////////////////////////////////////////////////////////////////
245 // CRenderer implementation
248 * Struct CRendererInternals: Truly hide data that is supposed to be hidden
249 * in this structure so it won't even appear in header files.
251 struct CRendererInternals
253 NONCOPYABLE(CRendererInternals
);
255 /// true if CRenderer::Open has been called
258 /// true if shaders need to be reloaded
261 /// Table to display renderer stats in-game via profile system
262 CRendererStatsTable profileTable
;
265 CShaderManager shaderManager
;
268 WaterManager waterManager
;
271 SkyManager skyManager
;
274 CTextureManager textureManager
;
277 TerrainRenderer terrainRenderer
;
280 OverlayRenderer overlayRenderer
;
283 CParticleManager particleManager
;
285 /// Particle renderer
286 ParticleRenderer particleRenderer
;
289 CMaterialManager materialManager
;
292 CTimeManager timeManager
;
297 /// Postprocessing effect manager
298 CPostprocManager postprocManager
;
300 CFontManager fontManager
;
302 SilhouetteRenderer silhouetteRenderer
;
304 /// Various model renderers
307 // NOTE: The current renderer design (with ModelRenderer, ModelVertexRenderer,
308 // RenderModifier, etc) is mostly a relic of an older design that implemented
309 // the different materials and rendering modes through extensive subclassing
310 // and hooking objects together in various combinations.
311 // The new design uses the CShaderManager API to abstract away the details
312 // of rendering, and uses a data-driven approach to materials, so there are
313 // now a small number of generic subclasses instead of many specialised subclasses,
314 // but most of the old infrastructure hasn't been refactored out yet and leads to
315 // some unwanted complexity.
317 // Submitted models are split on two axes:
318 // - Normal vs Transp[arent] - alpha-blended models are stored in a separate
319 // list so we can draw them above/below the alpha-blended water plane correctly
320 // - Skinned vs Unskinned - with hardware lighting we don't need to
321 // duplicate mesh data per model instance (except for skinned models),
322 // so non-skinned models get different ModelVertexRenderers
324 ModelRendererPtr NormalSkinned
;
325 ModelRendererPtr NormalUnskinned
; // == NormalSkinned if unskinned shader instancing not supported
326 ModelRendererPtr TranspSkinned
;
327 ModelRendererPtr TranspUnskinned
; // == TranspSkinned if unskinned shader instancing not supported
329 ModelVertexRendererPtr VertexRendererShader
;
330 ModelVertexRendererPtr VertexInstancingShader
;
331 ModelVertexRendererPtr VertexGPUSkinningShader
;
333 LitRenderModifierPtr ModShader
;
336 CShaderDefines globalContext
;
338 CRendererInternals() :
339 IsOpen(false), ShadersDirty(true), profileTable(g_Renderer
.m_Stats
), textureManager(g_VFS
, false, false)
344 * Load the OpenGL projection and modelview matrices and the viewport according
345 * to the given camera.
347 void SetOpenGLCamera(const CCamera
& camera
)
350 camera
.m_Orientation
.GetInverse(view
);
351 const CMatrix3D
& proj
= camera
.GetProjection();
354 #warning TODO: fix CRenderer camera handling for GLES (do not use global matrixes)
356 glMatrixMode(GL_PROJECTION
);
357 glLoadMatrixf(&proj
._11
);
359 glMatrixMode(GL_MODELVIEW
);
360 glLoadMatrixf(&view
._11
);
363 g_Renderer
.SetViewport(camera
.GetViewPort());
367 * Renders all non-alpha-blended models with the given context.
369 void CallModelRenderers(const CShaderDefines
& context
, int cullGroup
, int flags
)
371 CShaderDefines contextSkinned
= context
;
372 if (g_Renderer
.m_Options
.m_GPUSkinning
)
374 contextSkinned
.Add(str_USE_INSTANCING
, str_1
);
375 contextSkinned
.Add(str_USE_GPU_SKINNING
, str_1
);
377 Model
.NormalSkinned
->Render(Model
.ModShader
, contextSkinned
, cullGroup
, flags
);
379 if (Model
.NormalUnskinned
!= Model
.NormalSkinned
)
381 CShaderDefines contextUnskinned
= context
;
382 contextUnskinned
.Add(str_USE_INSTANCING
, str_1
);
383 Model
.NormalUnskinned
->Render(Model
.ModShader
, contextUnskinned
, cullGroup
, flags
);
388 * Renders all alpha-blended models with the given context.
390 void CallTranspModelRenderers(const CShaderDefines
& context
, int cullGroup
, int flags
)
392 CShaderDefines contextSkinned
= context
;
393 if (g_Renderer
.m_Options
.m_GPUSkinning
)
395 contextSkinned
.Add(str_USE_INSTANCING
, str_1
);
396 contextSkinned
.Add(str_USE_GPU_SKINNING
, str_1
);
398 Model
.TranspSkinned
->Render(Model
.ModShader
, contextSkinned
, cullGroup
, flags
);
400 if (Model
.TranspUnskinned
!= Model
.TranspSkinned
)
402 CShaderDefines contextUnskinned
= context
;
403 contextUnskinned
.Add(str_USE_INSTANCING
, str_1
);
404 Model
.TranspUnskinned
->Render(Model
.ModShader
, contextUnskinned
, cullGroup
, flags
);
409 ///////////////////////////////////////////////////////////////////////////////////
410 // CRenderer constructor
411 CRenderer::CRenderer()
413 m
= new CRendererInternals
;
414 m_WaterManager
= &m
->waterManager
;
415 m_SkyManager
= &m
->skyManager
;
417 g_ProfileViewer
.AddRootTable(&m
->profileTable
);
421 m_TerrainRenderMode
= SOLID
;
422 m_WaterRenderMode
= SOLID
;
423 m_ModelRenderMode
= SOLID
;
424 m_ClearColor
[0] = m_ClearColor
[1] = m_ClearColor
[2] = m_ClearColor
[3] = 0;
426 m_DisplayTerrainPriorities
= false;
427 m_SkipSubmit
= false;
429 m_Options
.m_NoVBO
= false;
430 m_Options
.m_RenderPath
= RP_DEFAULT
;
431 m_Options
.m_Shadows
= false;
432 m_Options
.m_WaterEffects
= false;
433 m_Options
.m_WaterFancyEffects
= false;
434 m_Options
.m_WaterRealDepth
= false;
435 m_Options
.m_WaterRefraction
= false;
436 m_Options
.m_WaterReflection
= false;
437 m_Options
.m_WaterShadows
= false;
438 m_Options
.m_ShadowAlphaFix
= true;
439 m_Options
.m_ARBProgramShadow
= true;
440 m_Options
.m_ShadowPCF
= false;
441 m_Options
.m_Particles
= false;
442 m_Options
.m_Silhouettes
= false;
443 m_Options
.m_PreferGLSL
= false;
444 m_Options
.m_Fog
= false;
445 m_Options
.m_ForceAlphaTest
= false;
446 m_Options
.m_GPUSkinning
= false;
447 m_Options
.m_SmoothLOS
= false;
448 m_Options
.m_Postproc
= false;
449 m_Options
.m_ShowSky
= false;
450 m_Options
.m_DisplayFrustum
= false;
452 // TODO: be more consistent in use of the config system
453 CFG_GET_VAL("preferglsl", m_Options
.m_PreferGLSL
);
454 CFG_GET_VAL("forcealphatest", m_Options
.m_ForceAlphaTest
);
455 CFG_GET_VAL("gpuskinning", m_Options
.m_GPUSkinning
);
456 CFG_GET_VAL("smoothlos", m_Options
.m_SmoothLOS
);
457 CFG_GET_VAL("postproc", m_Options
.m_Postproc
);
459 CStr skystring
= "0 0 0";
461 CFG_GET_VAL("skycolor", skystring
);
462 if (skycolor
.ParseString(skystring
, 255.f
))
463 SetClearColor(skycolor
.AsSColor4ub());
466 // Override config option since GLES only supports GLSL
467 m_Options
.m_PreferGLSL
= true;
470 m_ShadowZBias
= 0.02f
;
475 m_CurrentScene
= NULL
;
477 m_hCompositeAlphaMap
= 0;
481 RegisterFileReloadFunc(ReloadChangedFileCB
, this);
484 ///////////////////////////////////////////////////////////////////////////////////
485 // CRenderer destructor
486 CRenderer::~CRenderer()
488 UnregisterFileReloadFunc(ReloadChangedFileCB
, this);
490 // we no longer UnloadAlphaMaps / UnloadWaterTextures here -
491 // that is the responsibility of the module that asked for
492 // them to be loaded (i.e. CGameView).
497 ///////////////////////////////////////////////////////////////////////////////////
498 // EnumCaps: build card cap bits
499 void CRenderer::EnumCaps()
501 // assume support for nothing
502 m_Caps
.m_VBO
= false;
503 m_Caps
.m_ARBProgram
= false;
504 m_Caps
.m_ARBProgramShadow
= false;
505 m_Caps
.m_VertexShader
= false;
506 m_Caps
.m_FragmentShader
= false;
507 m_Caps
.m_Shadows
= false;
508 m_Caps
.m_PrettyWater
= false;
510 // now start querying extensions
511 if (!m_Options
.m_NoVBO
&& ogl_HaveExtension("GL_ARB_vertex_buffer_object"))
514 if (0 == ogl_HaveExtensions(0, "GL_ARB_vertex_program", "GL_ARB_fragment_program", NULL
))
516 m_Caps
.m_ARBProgram
= true;
517 if (ogl_HaveExtension("GL_ARB_fragment_program_shadow"))
518 m_Caps
.m_ARBProgramShadow
= true;
521 if (0 == ogl_HaveExtensions(0, "GL_ARB_shader_objects", "GL_ARB_shading_language_100", NULL
))
523 if (ogl_HaveExtension("GL_ARB_vertex_shader"))
524 m_Caps
.m_VertexShader
= true;
525 if (ogl_HaveExtension("GL_ARB_fragment_shader"))
526 m_Caps
.m_FragmentShader
= true;
530 m_Caps
.m_Shadows
= true;
532 if (0 == ogl_HaveExtensions(0, "GL_ARB_shadow", "GL_ARB_depth_texture", "GL_EXT_framebuffer_object", NULL
))
534 if (ogl_max_tex_units
>= 4)
535 m_Caps
.m_Shadows
= true;
540 m_Caps
.m_PrettyWater
= true;
542 if (0 == ogl_HaveExtensions(0, "GL_ARB_vertex_shader", "GL_ARB_fragment_shader", "GL_EXT_framebuffer_object", NULL
))
543 m_Caps
.m_PrettyWater
= true;
547 void CRenderer::RecomputeSystemShaderDefines()
549 CShaderDefines defines
;
551 if (GetRenderPath() == RP_SHADER
&& m_Caps
.m_ARBProgram
)
552 defines
.Add(str_SYS_HAS_ARB
, str_1
);
554 if (GetRenderPath() == RP_SHADER
&& m_Caps
.m_VertexShader
&& m_Caps
.m_FragmentShader
)
555 defines
.Add(str_SYS_HAS_GLSL
, str_1
);
557 if (m_Options
.m_PreferGLSL
)
558 defines
.Add(str_SYS_PREFER_GLSL
, str_1
);
560 m_SystemShaderDefines
= defines
;
563 void CRenderer::ReloadShaders()
567 m
->globalContext
= m_SystemShaderDefines
;
569 if (m_Caps
.m_Shadows
&& m_Options
.m_Shadows
)
571 m
->globalContext
.Add(str_USE_SHADOW
, str_1
);
572 if (m_Caps
.m_ARBProgramShadow
&& m_Options
.m_ARBProgramShadow
)
573 m
->globalContext
.Add(str_USE_FP_SHADOW
, str_1
);
574 if (m_Options
.m_ShadowPCF
)
575 m
->globalContext
.Add(str_USE_SHADOW_PCF
, str_1
);
577 m
->globalContext
.Add(str_USE_SHADOW_SAMPLER
, str_1
);
582 m
->globalContext
.Add(CStrIntern("LIGHTING_MODEL_" + m_LightEnv
->GetLightingModel()), str_1
);
584 if (m_Options
.m_PreferGLSL
&& m_Options
.m_Fog
)
585 m
->globalContext
.Add(str_USE_FOG
, str_1
);
587 m
->Model
.ModShader
= LitRenderModifierPtr(new ShaderRenderModifier());
589 bool cpuLighting
= (GetRenderPath() == RP_FIXED
);
590 m
->Model
.VertexRendererShader
= ModelVertexRendererPtr(new ShaderModelVertexRenderer(cpuLighting
));
591 m
->Model
.VertexInstancingShader
= ModelVertexRendererPtr(new InstancingModelRenderer(false, m_Options
.m_PreferGLSL
));
593 if (GetRenderPath() == RP_SHADER
&& m_Options
.m_GPUSkinning
) // TODO: should check caps and GLSL etc too
595 m
->Model
.VertexGPUSkinningShader
= ModelVertexRendererPtr(new InstancingModelRenderer(true, m_Options
.m_PreferGLSL
));
596 m
->Model
.NormalSkinned
= ModelRendererPtr(new ShaderModelRenderer(m
->Model
.VertexGPUSkinningShader
));
597 m
->Model
.TranspSkinned
= ModelRendererPtr(new ShaderModelRenderer(m
->Model
.VertexGPUSkinningShader
));
601 m
->Model
.VertexGPUSkinningShader
.reset();
602 m
->Model
.NormalSkinned
= ModelRendererPtr(new ShaderModelRenderer(m
->Model
.VertexRendererShader
));
603 m
->Model
.TranspSkinned
= ModelRendererPtr(new ShaderModelRenderer(m
->Model
.VertexRendererShader
));
606 // Use instancing renderers in shader mode
607 if (GetRenderPath() == RP_SHADER
)
609 m
->Model
.NormalUnskinned
= ModelRendererPtr(new ShaderModelRenderer(m
->Model
.VertexInstancingShader
));
610 m
->Model
.TranspUnskinned
= ModelRendererPtr(new ShaderModelRenderer(m
->Model
.VertexInstancingShader
));
614 m
->Model
.NormalUnskinned
= m
->Model
.NormalSkinned
;
615 m
->Model
.TranspUnskinned
= m
->Model
.TranspSkinned
;
618 m
->ShadersDirty
= false;
621 bool CRenderer::Open(int width
, int height
)
625 // Must query card capabilities before creating renderers that depend
626 // on card capabilities.
633 // set packing parameters
634 glPixelStorei(GL_PACK_ALIGNMENT
,1);
635 glPixelStorei(GL_UNPACK_ALIGNMENT
,1);
637 // setup default state
638 glDepthFunc(GL_LEQUAL
);
639 glEnable(GL_DEPTH_TEST
);
642 glEnable(GL_CULL_FACE
);
645 glGetIntegerv(GL_DEPTH_BITS
,&bits
);
646 LOGMESSAGE("CRenderer::Open: depth bits %d",bits
);
647 glGetIntegerv(GL_STENCIL_BITS
,&bits
);
648 LOGMESSAGE("CRenderer::Open: stencil bits %d",bits
);
649 glGetIntegerv(GL_ALPHA_BITS
,&bits
);
650 LOGMESSAGE("CRenderer::Open: alpha bits %d",bits
);
652 // Validate the currently selected render path
653 SetRenderPath(m_Options
.m_RenderPath
);
655 RecomputeSystemShaderDefines();
657 // Let component renderers perform one-time initialization after graphics capabilities and
658 // the shader path have been determined.
659 m
->overlayRenderer
.Initialize();
661 if (m_Options
.m_Postproc
)
662 m
->postprocManager
.Initialize();
667 // resize renderer view
668 void CRenderer::Resize(int width
, int height
)
670 // need to recreate the shadow map object to resize the shadow texture
671 m
->shadow
.RecreateTexture();
676 m
->postprocManager
.Resize();
678 m_WaterManager
->Resize();
681 //////////////////////////////////////////////////////////////////////////////////////////
682 // SetOptionBool: set boolean renderer option
683 void CRenderer::SetOptionBool(enum Option opt
,bool value
)
685 // Don't do anything if the option didn't change from its previous value.
686 if (value
== GetOptionBool(opt
))
691 m_Options
.m_NoVBO
= value
;
694 m_Options
.m_Shadows
= value
;
697 case OPT_WATEREFFECTS
:
698 m_Options
.m_WaterEffects
= value
;
700 case OPT_WATERFANCYEFFECTS
:
701 m_Options
.m_WaterFancyEffects
= value
;
703 case OPT_WATERREALDEPTH
:
704 m_Options
.m_WaterRealDepth
= value
;
706 case OPT_WATERREFLECTION
:
707 m_Options
.m_WaterReflection
= value
;
709 case OPT_WATERREFRACTION
:
710 m_Options
.m_WaterRefraction
= value
;
712 case OPT_SHADOWSONWATER
:
713 m_Options
.m_WaterShadows
= value
;
716 m_Options
.m_ShadowPCF
= value
;
720 m_Options
.m_Particles
= value
;
723 m_Options
.m_PreferGLSL
= value
;
725 RecomputeSystemShaderDefines();
728 m_Options
.m_Fog
= value
;
731 case OPT_SILHOUETTES
:
732 m_Options
.m_Silhouettes
= value
;
735 m_Options
.m_ShowSky
= value
;
738 m_Options
.m_SmoothLOS
= value
;
741 m_Options
.m_Postproc
= value
;
743 case OPT_DISPLAYFRUSTUM
:
744 m_Options
.m_DisplayFrustum
= value
;
747 debug_warn(L
"CRenderer::SetOptionBool: unknown option");
752 //////////////////////////////////////////////////////////////////////////////////////////
753 // GetOptionBool: get boolean renderer option
754 bool CRenderer::GetOptionBool(enum Option opt
) const
758 return m_Options
.m_NoVBO
;
760 return m_Options
.m_Shadows
;
761 case OPT_WATEREFFECTS
:
762 return m_Options
.m_WaterEffects
;
763 case OPT_WATERFANCYEFFECTS
:
764 return m_Options
.m_WaterFancyEffects
;
765 case OPT_WATERREALDEPTH
:
766 return m_Options
.m_WaterRealDepth
;
767 case OPT_WATERREFLECTION
:
768 return m_Options
.m_WaterReflection
;
769 case OPT_WATERREFRACTION
:
770 return m_Options
.m_WaterRefraction
;
771 case OPT_SHADOWSONWATER
:
772 return m_Options
.m_WaterShadows
;
774 return m_Options
.m_ShadowPCF
;
776 return m_Options
.m_Particles
;
778 return m_Options
.m_PreferGLSL
;
780 return m_Options
.m_Fog
;
781 case OPT_SILHOUETTES
:
782 return m_Options
.m_Silhouettes
;
784 return m_Options
.m_ShowSky
;
786 return m_Options
.m_SmoothLOS
;
788 return m_Options
.m_Postproc
;
789 case OPT_DISPLAYFRUSTUM
:
790 return m_Options
.m_DisplayFrustum
;
792 debug_warn(L
"CRenderer::GetOptionBool: unknown option");
799 //////////////////////////////////////////////////////////////////////////////////////////
800 // SetRenderPath: Select the preferred render path.
801 // This may only be called before Open(), because the layout of vertex arrays and other
802 // data may depend on the chosen render path.
803 void CRenderer::SetRenderPath(RenderPath rp
)
807 // Delay until Open() is called.
808 m_Options
.m_RenderPath
= rp
;
812 // Renderer has been opened, so validate the selected renderpath
813 if (rp
== RP_DEFAULT
)
815 if (m_Caps
.m_ARBProgram
|| (m_Caps
.m_VertexShader
&& m_Caps
.m_FragmentShader
&& m_Options
.m_PreferGLSL
))
823 if (!(m_Caps
.m_ARBProgram
|| (m_Caps
.m_VertexShader
&& m_Caps
.m_FragmentShader
&& m_Options
.m_PreferGLSL
)))
825 LOGWARNING("Falling back to fixed function\n");
830 m_Options
.m_RenderPath
= rp
;
833 RecomputeSystemShaderDefines();
835 // We might need to regenerate some render data after changing path
837 g_Game
->GetWorld()->GetTerrain()->MakeDirty(RENDERDATA_UPDATE_COLOR
);
841 CStr
CRenderer::GetRenderPathName(RenderPath rp
)
844 case RP_DEFAULT
: return "default";
845 case RP_FIXED
: return "fixed";
846 case RP_SHADER
: return "shader";
847 default: return "(invalid)";
851 CRenderer::RenderPath
CRenderer::GetRenderPathByName(const CStr
& name
)
855 if (name
== "shader")
857 if (name
== "default")
860 LOGWARNING("Unknown render path name '%s', assuming 'default'", name
.c_str());
865 //////////////////////////////////////////////////////////////////////////////////////////
866 // BeginFrame: signal frame start
867 void CRenderer::BeginFrame()
869 PROFILE("begin frame");
871 // zero out all the per-frame stats
874 // choose model renderers for this frame
879 m
->Model
.ModShader
->SetShadowMap(&m
->shadow
);
880 m
->Model
.ModShader
->SetLightEnv(m_LightEnv
);
883 //////////////////////////////////////////////////////////////////////////////////////////
884 void CRenderer::SetSimulation(CSimulation2
* simulation
)
886 // set current simulation context for terrain renderer
887 m
->terrainRenderer
.SetSimulation(simulation
);
890 // SetClearColor: set color used to clear screen in BeginFrame()
891 void CRenderer::SetClearColor(SColor4ub color
)
893 m_ClearColor
[0] = float(color
.R
) / 255.0f
;
894 m_ClearColor
[1] = float(color
.G
) / 255.0f
;
895 m_ClearColor
[2] = float(color
.B
) / 255.0f
;
896 m_ClearColor
[3] = float(color
.A
) / 255.0f
;
899 void CRenderer::RenderShadowMap(const CShaderDefines
& context
)
901 PROFILE3_GPU("shadow map");
903 m
->shadow
.BeginRender();
906 PROFILE("render patches");
907 glCullFace(GL_FRONT
);
908 glEnable(GL_CULL_FACE
);
909 m
->terrainRenderer
.RenderPatches(CULL_SHADOWS
);
913 CShaderDefines contextCast
= context
;
914 contextCast
.Add(str_MODE_SHADOWCAST
, str_1
);
917 PROFILE("render models");
918 m
->CallModelRenderers(contextCast
, CULL_SHADOWS
, MODELFLAG_CASTSHADOWS
);
922 PROFILE("render transparent models");
923 // disable face-culling for two-sided models
924 glDisable(GL_CULL_FACE
);
925 m
->CallTranspModelRenderers(contextCast
, CULL_SHADOWS
, MODELFLAG_CASTSHADOWS
);
926 glEnable(GL_CULL_FACE
);
929 m
->shadow
.EndRender();
931 m
->SetOpenGLCamera(m_ViewCamera
);
934 void CRenderer::RenderPatches(const CShaderDefines
& context
, int cullGroup
)
936 PROFILE3_GPU("patches");
939 #warning TODO: implement wireface/edged rendering mode GLES
941 // switch on wireframe if we need it
942 if (m_TerrainRenderMode
== WIREFRAME
)
944 glPolygonMode(GL_FRONT_AND_BACK
, GL_LINE
);
948 // render all the patches, including blend pass
949 if (GetRenderPath() == RP_SHADER
)
950 m
->terrainRenderer
.RenderTerrainShader(context
, cullGroup
, (m_Caps
.m_Shadows
&& m_Options
.m_Shadows
) ? &m
->shadow
: 0);
952 m
->terrainRenderer
.RenderTerrain(cullGroup
);
956 if (m_TerrainRenderMode
== WIREFRAME
)
958 // switch wireframe off again
959 glPolygonMode(GL_FRONT_AND_BACK
, GL_FILL
);
961 else if (m_TerrainRenderMode
== EDGED_FACES
)
963 // edged faces: need to make a second pass over the data:
964 // first switch on wireframe
965 glPolygonMode(GL_FRONT_AND_BACK
,GL_LINE
);
967 // setup some renderstate ..
968 pglActiveTextureARB(GL_TEXTURE0
);
969 glDisable(GL_TEXTURE_2D
);
970 glColor3f(0.5f
, 0.5f
, 1.0f
);
973 // render tiles edges
974 m
->terrainRenderer
.RenderPatches(cullGroup
);
976 // set color for outline
980 // render outline of each patch
981 m
->terrainRenderer
.RenderOutlines(cullGroup
);
983 // .. and restore the renderstates
985 glPolygonMode(GL_FRONT_AND_BACK
, GL_FILL
);
990 void CRenderer::RenderModels(const CShaderDefines
& context
, int cullGroup
)
992 PROFILE3_GPU("models");
997 if (m_ModelRenderMode
== WIREFRAME
)
999 glPolygonMode(GL_FRONT_AND_BACK
, GL_LINE
);
1003 m
->CallModelRenderers(context
, cullGroup
, flags
);
1006 if (m_ModelRenderMode
== WIREFRAME
)
1008 glPolygonMode(GL_FRONT_AND_BACK
, GL_FILL
);
1010 else if (m_ModelRenderMode
== EDGED_FACES
)
1012 CShaderDefines contextWireframe
= context
;
1013 contextWireframe
.Add(str_MODE_WIREFRAME
, str_1
);
1015 glPolygonMode(GL_FRONT_AND_BACK
, GL_LINE
);
1016 glDisable(GL_TEXTURE_2D
);
1018 m
->CallModelRenderers(contextWireframe
, cullGroup
, flags
);
1020 glPolygonMode(GL_FRONT_AND_BACK
, GL_FILL
);
1025 void CRenderer::RenderTransparentModels(const CShaderDefines
& context
, int cullGroup
, ETransparentMode transparentMode
, bool disableFaceCulling
)
1027 PROFILE3_GPU("transparent models");
1032 // switch on wireframe if we need it
1033 if (m_ModelRenderMode
== WIREFRAME
)
1035 glPolygonMode(GL_FRONT_AND_BACK
, GL_LINE
);
1039 // disable face culling for two-sided models in sub-renders
1040 if (disableFaceCulling
)
1041 glDisable(GL_CULL_FACE
);
1043 CShaderDefines contextOpaque
= context
;
1044 contextOpaque
.Add(str_ALPHABLEND_PASS_OPAQUE
, str_1
);
1046 CShaderDefines contextBlend
= context
;
1047 contextBlend
.Add(str_ALPHABLEND_PASS_BLEND
, str_1
);
1049 if (transparentMode
== TRANSPARENT
|| transparentMode
== TRANSPARENT_OPAQUE
)
1050 m
->CallTranspModelRenderers(contextOpaque
, cullGroup
, flags
);
1052 if (transparentMode
== TRANSPARENT
|| transparentMode
== TRANSPARENT_BLEND
)
1053 m
->CallTranspModelRenderers(contextBlend
, cullGroup
, flags
);
1055 if (disableFaceCulling
)
1056 glEnable(GL_CULL_FACE
);
1059 if (m_ModelRenderMode
== WIREFRAME
)
1061 // switch wireframe off again
1062 glPolygonMode(GL_FRONT_AND_BACK
, GL_FILL
);
1064 else if (m_ModelRenderMode
== EDGED_FACES
)
1066 CShaderDefines contextWireframe
= contextOpaque
;
1067 contextWireframe
.Add(str_MODE_WIREFRAME
, str_1
);
1069 glPolygonMode(GL_FRONT_AND_BACK
, GL_LINE
);
1070 glDisable(GL_TEXTURE_2D
);
1072 m
->CallTranspModelRenderers(contextWireframe
, cullGroup
, flags
);
1074 glPolygonMode(GL_FRONT_AND_BACK
, GL_FILL
);
1080 ///////////////////////////////////////////////////////////////////////////////////////////////////
1081 // SetObliqueFrustumClipping: change the near plane to the given clip plane (in world space)
1082 // Based on code from Game Programming Gems 5, from http://www.terathon.com/code/oblique.html
1083 // - worldPlane is a clip plane in world space (worldPlane.Dot(v) >= 0 for any vector v passing the clipping test)
1084 void CRenderer::SetObliqueFrustumClipping(CCamera
& camera
, const CVector4D
& worldPlane
) const
1086 // First, we'll convert the given clip plane to camera space, then we'll
1087 // Get the view matrix and normal matrix (top 3x3 part of view matrix)
1088 CMatrix3D normalMatrix
= camera
.m_Orientation
.GetTranspose();
1089 CVector4D camPlane
= normalMatrix
.Transform(worldPlane
);
1091 CMatrix3D matrix
= camera
.GetProjection();
1093 // Calculate the clip-space corner point opposite the clipping plane
1094 // as (sgn(camPlane.x), sgn(camPlane.y), 1, 1) and
1095 // transform it into camera space by multiplying it
1096 // by the inverse of the projection matrix
1099 q
.X
= (sgn(camPlane
.X
) - matrix
[8]/matrix
[11]) / matrix
[0];
1100 q
.Y
= (sgn(camPlane
.Y
) - matrix
[9]/matrix
[11]) / matrix
[5];
1101 q
.Z
= 1.0f
/matrix
[11];
1102 q
.W
= (1.0f
- matrix
[10]/matrix
[11]) / matrix
[14];
1104 // Calculate the scaled plane vector
1105 CVector4D c
= camPlane
* (2.0f
* matrix
[11] / camPlane
.Dot(q
));
1107 // Replace the third row of the projection matrix
1110 matrix
[10] = c
.Z
- matrix
[11];
1113 // Load it back into the camera
1114 camera
.SetProjection(matrix
);
1117 void CRenderer::ComputeReflectionCamera(CCamera
& camera
, const CBoundingBoxAligned
& scissor
) const
1119 WaterManager
& wm
= m
->waterManager
;
1121 float fov
= m_ViewCamera
.GetFOV();
1123 // Expand fov slightly since ripples can reflect parts of the scene that
1124 // are slightly outside the normal camera view, and we want to avoid any
1125 // noticeable edge-filtering artifacts
1128 camera
= m_ViewCamera
;
1130 // Temporarily change the camera to one that is reflected.
1131 // Also, for texturing purposes, make it render to a view port the size of the
1132 // water texture, stretch the image according to our aspect ratio so it covers
1133 // the whole screen despite being rendered into a square, and cover slightly more
1134 // of the view so we can see wavy reflections of slightly off-screen objects.
1135 camera
.m_Orientation
.Scale(1, -1, 1);
1136 camera
.m_Orientation
.Translate(0, 2*wm
.m_WaterHeight
, 0);
1137 camera
.UpdateFrustum(scissor
);
1138 camera
.ClipFrustum(CVector4D(0, 1, 0, -wm
.m_WaterHeight
));
1141 vp
.m_Height
= wm
.m_RefTextureSize
;
1142 vp
.m_Width
= wm
.m_RefTextureSize
;
1145 camera
.SetViewPort(vp
);
1146 camera
.SetProjection(m_ViewCamera
.GetNearPlane(), m_ViewCamera
.GetFarPlane(), fov
);
1148 scaleMat
.SetScaling(m_Height
/float(std::max(1, m_Width
)), 1.0f
, 1.0f
);
1149 camera
.m_ProjMat
= scaleMat
* camera
.m_ProjMat
;
1151 CVector4D
camPlane(0, 1, 0, -wm
.m_WaterHeight
+ 0.5f
);
1152 SetObliqueFrustumClipping(camera
, camPlane
);
1156 void CRenderer::ComputeRefractionCamera(CCamera
& camera
, const CBoundingBoxAligned
& scissor
) const
1158 WaterManager
& wm
= m
->waterManager
;
1160 float fov
= m_ViewCamera
.GetFOV();
1162 // Expand fov slightly since ripples can reflect parts of the scene that
1163 // are slightly outside the normal camera view, and we want to avoid any
1164 // noticeable edge-filtering artifacts
1167 camera
= m_ViewCamera
;
1169 // Temporarily change the camera to make it render to a view port the size of the
1170 // water texture, stretch the image according to our aspect ratio so it covers
1171 // the whole screen despite being rendered into a square, and cover slightly more
1172 // of the view so we can see wavy refractions of slightly off-screen objects.
1173 camera
.UpdateFrustum(scissor
);
1174 camera
.ClipFrustum(CVector4D(0, -1, 0, wm
.m_WaterHeight
+ 0.5f
)); // add some to avoid artifacts near steep shores.
1177 vp
.m_Height
= wm
.m_RefTextureSize
;
1178 vp
.m_Width
= wm
.m_RefTextureSize
;
1181 camera
.SetViewPort(vp
);
1182 camera
.SetProjection(m_ViewCamera
.GetNearPlane(), m_ViewCamera
.GetFarPlane(), fov
);
1184 scaleMat
.SetScaling(m_Height
/float(std::max(1, m_Width
)), 1.0f
, 1.0f
);
1185 camera
.m_ProjMat
= scaleMat
* camera
.m_ProjMat
;
1188 ///////////////////////////////////////////////////////////////////////////////////////////////////
1189 // RenderReflections: render the water reflections to the reflection texture
1190 void CRenderer::RenderReflections(const CShaderDefines
& context
, const CBoundingBoxAligned
& scissor
)
1192 PROFILE3_GPU("water reflections");
1194 // Save the post-processing framebuffer.
1196 glGetIntegerv(GL_FRAMEBUFFER_BINDING_EXT
, &fbo
);
1198 WaterManager
& wm
= m
->waterManager
;
1200 // Remember old camera
1201 CCamera normalCamera
= m_ViewCamera
;
1203 ComputeReflectionCamera(m_ViewCamera
, scissor
);
1205 m
->SetOpenGLCamera(m_ViewCamera
);
1207 // Save the model-view-projection matrix so the shaders can use it for projective texturing
1208 wm
.m_ReflectionMatrix
= m_ViewCamera
.GetViewProjection();
1210 float vpHeight
= wm
.m_RefTextureSize
;
1211 float vpWidth
= wm
.m_RefTextureSize
;
1213 SScreenRect screenScissor
;
1214 screenScissor
.x1
= (GLint
)floor((scissor
[0].X
*0.5f
+0.5f
)*vpWidth
);
1215 screenScissor
.y1
= (GLint
)floor((scissor
[0].Y
*0.5f
+0.5f
)*vpHeight
);
1216 screenScissor
.x2
= (GLint
)ceil((scissor
[1].X
*0.5f
+0.5f
)*vpWidth
);
1217 screenScissor
.y2
= (GLint
)ceil((scissor
[1].Y
*0.5f
+0.5f
)*vpHeight
);
1219 glEnable(GL_SCISSOR_TEST
);
1220 glScissor(screenScissor
.x1
, screenScissor
.y1
, screenScissor
.x2
- screenScissor
.x1
, screenScissor
.y2
- screenScissor
.y1
);
1222 // try binding the framebuffer
1223 pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT
, wm
.m_ReflectionFbo
);
1225 glClearColor(0.5f
,0.5f
,1.0f
,0.0f
);
1226 glClear(GL_COLOR_BUFFER_BIT
| GL_DEPTH_BUFFER_BIT
);
1230 if (!m_Options
.m_WaterReflection
)
1232 m
->skyManager
.RenderSky();
1237 // Render terrain and models
1238 RenderPatches(context
, CULL_REFLECTIONS
);
1240 RenderModels(context
, CULL_REFLECTIONS
);
1242 RenderTransparentModels(context
, CULL_REFLECTIONS
, TRANSPARENT
, true);
1245 glFrontFace(GL_CCW
);
1247 // Particles are always oriented to face the camera in the vertex shader,
1248 // so they don't need the inverted glFrontFace
1249 if (m_Options
.m_Particles
)
1251 RenderParticles(CULL_REFLECTIONS
);
1255 glDisable(GL_SCISSOR_TEST
);
1258 m_ViewCamera
= normalCamera
;
1259 m
->SetOpenGLCamera(m_ViewCamera
);
1261 // rebind post-processing frambuffer.
1262 pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT
, fbo
);
1268 ///////////////////////////////////////////////////////////////////////////////////////////////////
1269 // RenderRefractions: render the water refractions to the refraction texture
1270 void CRenderer::RenderRefractions(const CShaderDefines
& context
, const CBoundingBoxAligned
&scissor
)
1272 PROFILE3_GPU("water refractions");
1274 // Save the post-processing framebuffer.
1276 glGetIntegerv(GL_FRAMEBUFFER_BINDING_EXT
, &fbo
);
1278 WaterManager
& wm
= m
->waterManager
;
1280 // Remember old camera
1281 CCamera normalCamera
= m_ViewCamera
;
1283 ComputeRefractionCamera(m_ViewCamera
, scissor
);
1285 CVector4D
camPlane(0, -1, 0, wm
.m_WaterHeight
+ 2.0f
);
1286 SetObliqueFrustumClipping(m_ViewCamera
, camPlane
);
1288 m
->SetOpenGLCamera(m_ViewCamera
);
1290 // Save the model-view-projection matrix so the shaders can use it for projective texturing
1291 wm
.m_RefractionMatrix
= m_ViewCamera
.GetViewProjection();
1293 float vpHeight
= wm
.m_RefTextureSize
;
1294 float vpWidth
= wm
.m_RefTextureSize
;
1296 SScreenRect screenScissor
;
1297 screenScissor
.x1
= (GLint
)floor((scissor
[0].X
*0.5f
+0.5f
)*vpWidth
);
1298 screenScissor
.y1
= (GLint
)floor((scissor
[0].Y
*0.5f
+0.5f
)*vpHeight
);
1299 screenScissor
.x2
= (GLint
)ceil((scissor
[1].X
*0.5f
+0.5f
)*vpWidth
);
1300 screenScissor
.y2
= (GLint
)ceil((scissor
[1].Y
*0.5f
+0.5f
)*vpHeight
);
1302 glEnable(GL_SCISSOR_TEST
);
1303 glScissor(screenScissor
.x1
, screenScissor
.y1
, screenScissor
.x2
- screenScissor
.x1
, screenScissor
.y2
- screenScissor
.y1
);
1305 // try binding the framebuffer
1306 pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT
, wm
.m_RefractionFbo
);
1308 glClearColor(1.0f
,0.0f
,0.0f
,0.0f
);
1309 glClear(GL_COLOR_BUFFER_BIT
| GL_DEPTH_BUFFER_BIT
);
1311 // Render terrain and models
1312 RenderPatches(context
, CULL_REFRACTIONS
);
1314 RenderModels(context
, CULL_REFRACTIONS
);
1316 RenderTransparentModels(context
, CULL_REFRACTIONS
, TRANSPARENT_OPAQUE
, false);
1319 glDisable(GL_SCISSOR_TEST
);
1322 m_ViewCamera
= normalCamera
;
1323 m
->SetOpenGLCamera(m_ViewCamera
);
1325 // rebind post-processing frambuffer.
1326 pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT
, fbo
);
1331 void CRenderer::RenderSilhouettes(const CShaderDefines
& context
)
1333 PROFILE3_GPU("silhouettes");
1335 CShaderDefines contextOccluder
= context
;
1336 contextOccluder
.Add(str_MODE_SILHOUETTEOCCLUDER
, str_1
);
1338 CShaderDefines contextDisplay
= context
;
1339 contextDisplay
.Add(str_MODE_SILHOUETTEDISPLAY
, str_1
);
1341 // Render silhouettes of units hidden behind terrain or occluders.
1342 // To avoid breaking the standard rendering of alpha-blended objects, this
1343 // has to be done in a separate pass.
1344 // First we render all occluders into depth, then render all units with
1345 // inverted depth test so any behind an occluder will get drawn in a constant
1348 float silhouetteAlpha
= 0.75f
;
1350 // Silhouette blending requires an almost-universally-supported extension;
1351 // fall back to non-blended if unavailable
1352 if (!ogl_HaveExtension("GL_EXT_blend_color"))
1353 silhouetteAlpha
= 1.f
;
1355 glClear(GL_DEPTH_BUFFER_BIT
| GL_STENCIL_BUFFER_BIT
);
1357 glColorMask(0, 0, 0, 0);
1359 // Render occluders:
1362 PROFILE("render patches");
1364 // To prevent units displaying silhouettes when parts of their model
1365 // protrude into the ground, only occlude with the back faces of the
1366 // terrain (so silhouettes will still display when behind hills)
1367 glCullFace(GL_FRONT
);
1368 m
->terrainRenderer
.RenderPatches(CULL_SILHOUETTE_OCCLUDER
);
1369 glCullFace(GL_BACK
);
1373 PROFILE("render model occluders");
1374 m
->CallModelRenderers(contextOccluder
, CULL_SILHOUETTE_OCCLUDER
, 0);
1378 PROFILE("render transparent occluders");
1379 m
->CallTranspModelRenderers(contextOccluder
, CULL_SILHOUETTE_OCCLUDER
, 0);
1382 glDepthFunc(GL_GEQUAL
);
1383 glColorMask(1, 1, 1, 1);
1385 // Render more efficiently if alpha == 1
1386 if (silhouetteAlpha
== 1.f
)
1388 // Ideally we'd render objects back-to-front so nearer silhouettes would
1389 // appear on top, but sorting has non-zero cost. So we'll keep the depth
1390 // write enabled, to do the opposite - far objects will consistently appear
1396 // Since we can't sort, we'll use the stencil buffer to ensure we only draw
1397 // a pixel once (using the color of whatever model happens to be drawn first).
1399 glBlendFunc(GL_CONSTANT_ALPHA
, GL_ONE_MINUS_CONSTANT_ALPHA
);
1400 pglBlendColorEXT(0, 0, 0, silhouetteAlpha
);
1402 glEnable(GL_STENCIL_TEST
);
1403 glStencilFunc(GL_NOTEQUAL
, 1, (GLuint
)-1);
1404 glStencilOp(GL_KEEP
, GL_KEEP
, GL_REPLACE
);
1408 PROFILE("render model casters");
1409 m
->CallModelRenderers(contextDisplay
, CULL_SILHOUETTE_CASTER
, 0);
1413 PROFILE("render transparent casters");
1414 m
->CallTranspModelRenderers(contextDisplay
, CULL_SILHOUETTE_CASTER
, 0);
1418 glDepthFunc(GL_LEQUAL
);
1419 if (silhouetteAlpha
== 1.f
)
1425 glDisable(GL_BLEND
);
1426 glBlendFunc(GL_SRC_ALPHA
, GL_ONE_MINUS_SRC_ALPHA
);
1427 pglBlendColorEXT(0, 0, 0, 0);
1428 glDisable(GL_STENCIL_TEST
);
1432 void CRenderer::RenderParticles(int cullGroup
)
1434 // Only supported in shader modes
1435 if (GetRenderPath() != RP_SHADER
)
1438 PROFILE3_GPU("particles");
1440 m
->particleRenderer
.RenderParticles(cullGroup
);
1443 if (m_ModelRenderMode
== EDGED_FACES
)
1445 glPolygonMode(GL_FRONT_AND_BACK
, GL_LINE
);
1447 glDisable(GL_TEXTURE_2D
);
1448 glColor3f(0.0f
, 0.5f
, 0.0f
);
1450 m
->particleRenderer
.RenderParticles(true);
1452 CShaderTechniquePtr shaderTech
= g_Renderer
.GetShaderManager().LoadEffect(str_gui_solid
);
1453 shaderTech
->BeginPass();
1454 CShaderProgramPtr shader
= shaderTech
->GetShader();
1455 shader
->Uniform(str_color
, 0.0f
, 1.0f
, 0.0f
, 1.0f
);
1456 shader
->Uniform(str_transform
, m_ViewCamera
.GetViewProjection());
1458 m
->particleRenderer
.RenderBounds(cullGroup
, shader
);
1460 shaderTech
->EndPass();
1462 glPolygonMode(GL_FRONT_AND_BACK
, GL_FILL
);
1467 ///////////////////////////////////////////////////////////////////////////////////////////////////
1468 // RenderSubmissions: force rendering of any batched objects
1469 void CRenderer::RenderSubmissions(const CBoundingBoxAligned
& waterScissor
)
1471 PROFILE3("render submissions");
1473 GetScene().GetLOSTexture().InterpolateLOS();
1475 if (m_Options
.m_Postproc
)
1477 m
->postprocManager
.Initialize();
1478 m
->postprocManager
.CaptureRenderOutput();
1481 CShaderDefines context
= m
->globalContext
;
1483 int cullGroup
= CULL_DEFAULT
;
1488 m
->SetOpenGLCamera(m_ViewCamera
);
1490 // Prepare model renderers
1492 PROFILE3("prepare models");
1493 m
->Model
.NormalSkinned
->PrepareModels();
1494 m
->Model
.TranspSkinned
->PrepareModels();
1495 if (m
->Model
.NormalUnskinned
!= m
->Model
.NormalSkinned
)
1496 m
->Model
.NormalUnskinned
->PrepareModels();
1497 if (m
->Model
.TranspUnskinned
!= m
->Model
.TranspSkinned
)
1498 m
->Model
.TranspUnskinned
->PrepareModels();
1501 m
->terrainRenderer
.PrepareForRendering();
1503 m
->overlayRenderer
.PrepareForRendering();
1505 m
->particleRenderer
.PrepareForRendering(context
);
1507 if (m_Caps
.m_Shadows
&& m_Options
.m_Shadows
&& GetRenderPath() == RP_SHADER
)
1509 RenderShadowMap(context
);
1513 PROFILE3_GPU("clear buffers");
1514 glClearColor(m_ClearColor
[0], m_ClearColor
[1], m_ClearColor
[2], m_ClearColor
[3]);
1515 glClear(GL_COLOR_BUFFER_BIT
| GL_DEPTH_BUFFER_BIT
| GL_STENCIL_BUFFER_BIT
);
1520 if (m_WaterManager
->m_RenderWater
)
1522 if (waterScissor
.GetVolume() > 0 && m_WaterManager
->WillRenderFancyWater())
1524 PROFILE3_GPU("water scissor");
1525 RenderReflections(context
, waterScissor
);
1527 if (m_Options
.m_WaterRefraction
)
1528 RenderRefractions(context
, waterScissor
);
1532 if (m_Options
.m_ShowSky
)
1534 m
->skyManager
.RenderSky();
1537 // render submitted patches and models
1538 RenderPatches(context
, cullGroup
);
1541 // render debug-related terrain overlays
1542 ITerrainOverlay::RenderOverlaysBeforeWater();
1545 // render other debug-related overlays before water (so they can be seen when underwater)
1546 m
->overlayRenderer
.RenderOverlaysBeforeWater();
1549 RenderModels(context
, cullGroup
);
1553 if (m_WaterManager
->m_RenderWater
&& g_Game
&& waterScissor
.GetVolume() > 0)
1555 // render transparent stuff, but only the solid parts that can occlude block water
1556 RenderTransparentModels(context
, cullGroup
, TRANSPARENT_OPAQUE
, false);
1559 m
->terrainRenderer
.RenderWater(context
, cullGroup
, &m
->shadow
);
1562 // render transparent stuff again, but only the blended parts that overlap water
1563 RenderTransparentModels(context
, cullGroup
, TRANSPARENT_BLEND
, false);
1568 // render transparent stuff, so it can overlap models/terrain
1569 RenderTransparentModels(context
, cullGroup
, TRANSPARENT
, false);
1573 // render debug-related terrain overlays
1574 ITerrainOverlay::RenderOverlaysAfterWater(cullGroup
);
1577 // render some other overlays after water (so they can be displayed on top of water)
1578 m
->overlayRenderer
.RenderOverlaysAfterWater();
1581 // particles are transparent so render after water
1582 if (m_Options
.m_Particles
)
1584 RenderParticles(cullGroup
);
1588 if (m_Options
.m_Postproc
)
1590 m
->postprocManager
.ApplyPostproc();
1591 m
->postprocManager
.ReleaseRenderOutput();
1594 if (m_Options
.m_Silhouettes
)
1596 RenderSilhouettes(context
);
1600 // Clean up texture blend mode so particles and other things render OK
1601 // (really this should be cleaned up by whoever set it)
1602 glTexEnvi(GL_TEXTURE_ENV
, GL_TEXTURE_ENV_MODE
, GL_MODULATE
);
1605 // render debug lines
1606 if (m_Options
.m_DisplayFrustum
)
1609 m
->shadow
.RenderDebugBounds();
1610 m
->shadow
.RenderDebugTexture();
1614 m
->silhouetteRenderer
.RenderDebugOverlays(m_ViewCamera
);
1616 // render overlays that should appear on top of all other objects
1617 m
->overlayRenderer
.RenderForegroundOverlays(m_ViewCamera
);
1622 ///////////////////////////////////////////////////////////////////////////////////////////////////
1623 // EndFrame: signal frame end
1624 void CRenderer::EndFrame()
1626 PROFILE3("end frame");
1629 m
->terrainRenderer
.EndFrame();
1630 m
->overlayRenderer
.EndFrame();
1631 m
->particleRenderer
.EndFrame();
1632 m
->silhouetteRenderer
.EndFrame();
1634 // Finish model renderers
1635 m
->Model
.NormalSkinned
->EndFrame();
1636 m
->Model
.TranspSkinned
->EndFrame();
1637 if (m
->Model
.NormalUnskinned
!= m
->Model
.NormalSkinned
)
1638 m
->Model
.NormalUnskinned
->EndFrame();
1639 if (m
->Model
.TranspUnskinned
!= m
->Model
.TranspSkinned
)
1640 m
->Model
.TranspUnskinned
->EndFrame();
1645 PROFILE3("error check");
1646 int err
= glGetError();
1649 ONCE(LOGERROR("CRenderer::EndFrame: GL errors %s (%04x) occurred", ogl_GetErrorName(err
), err
));
1655 ///////////////////////////////////////////////////////////////////////////////////////////////////
1656 // DisplayFrustum: debug displays
1657 // - white: cull camera frustum
1658 // - red: bounds of shadow casting objects
1659 void CRenderer::DisplayFrustum()
1662 #warning TODO: implement CRenderer::DisplayFrustum for GLES
1665 glDisable(GL_CULL_FACE
);
1666 glDisable(GL_TEXTURE_2D
);
1669 glBlendFunc(GL_SRC_ALPHA
, GL_ONE_MINUS_SRC_ALPHA
);
1670 glColor4ub(255,255,255,64);
1671 m_CullCamera
.Render(2);
1672 glDisable(GL_BLEND
);
1674 glPolygonMode(GL_FRONT_AND_BACK
, GL_LINE
);
1675 glColor3ub(255,255,255);
1676 m_CullCamera
.Render(2);
1677 glPolygonMode(GL_FRONT_AND_BACK
, GL_FILL
);
1679 glEnable(GL_CULL_FACE
);
1684 ///////////////////////////////////////////////////////////////////////////////////////////////////
1685 // Text overlay rendering
1686 void CRenderer::RenderTextOverlays()
1688 PROFILE3_GPU("text overlays");
1690 if (m_DisplayTerrainPriorities
)
1691 m
->terrainRenderer
.RenderPriorities(CULL_DEFAULT
);
1696 ///////////////////////////////////////////////////////////////////////////////////////////////////
1697 // SetSceneCamera: setup projection and transform of camera and adjust viewport to current view
1698 // The camera always represents the actual camera used to render a scene, not any virtual camera
1699 // used for shadow rendering or reflections.
1700 void CRenderer::SetSceneCamera(const CCamera
& viewCamera
, const CCamera
& cullCamera
)
1702 m_ViewCamera
= viewCamera
;
1703 m_CullCamera
= cullCamera
;
1705 if (m_Caps
.m_Shadows
&& m_Options
.m_Shadows
&& GetRenderPath() == RP_SHADER
)
1706 m
->shadow
.SetupFrame(m_CullCamera
, m_LightEnv
->GetSunDir());
1710 void CRenderer::SetViewport(const SViewPort
&vp
)
1713 glViewport((GLint
)vp
.m_X
,(GLint
)vp
.m_Y
,(GLsizei
)vp
.m_Width
,(GLsizei
)vp
.m_Height
);
1716 SViewPort
CRenderer::GetViewport()
1721 void CRenderer::Submit(CPatch
* patch
)
1723 if (m_CurrentCullGroup
== CULL_DEFAULT
)
1725 m
->shadow
.AddShadowReceiverBound(patch
->GetWorldBounds());
1726 m
->silhouetteRenderer
.AddOccluder(patch
);
1729 if (m_CurrentCullGroup
== CULL_SHADOWS
)
1731 m
->shadow
.AddShadowCasterBound(patch
->GetWorldBounds());
1734 m
->terrainRenderer
.Submit(m_CurrentCullGroup
, patch
);
1737 void CRenderer::Submit(SOverlayLine
* overlay
)
1739 // Overlays are only needed in the default cull group for now,
1740 // so just ignore submissions to any other group
1741 if (m_CurrentCullGroup
== CULL_DEFAULT
)
1742 m
->overlayRenderer
.Submit(overlay
);
1745 void CRenderer::Submit(SOverlayTexturedLine
* overlay
)
1747 if (m_CurrentCullGroup
== CULL_DEFAULT
)
1748 m
->overlayRenderer
.Submit(overlay
);
1751 void CRenderer::Submit(SOverlaySprite
* overlay
)
1753 if (m_CurrentCullGroup
== CULL_DEFAULT
)
1754 m
->overlayRenderer
.Submit(overlay
);
1757 void CRenderer::Submit(SOverlayQuad
* overlay
)
1759 if (m_CurrentCullGroup
== CULL_DEFAULT
)
1760 m
->overlayRenderer
.Submit(overlay
);
1763 void CRenderer::Submit(SOverlaySphere
* overlay
)
1765 if (m_CurrentCullGroup
== CULL_DEFAULT
)
1766 m
->overlayRenderer
.Submit(overlay
);
1769 void CRenderer::Submit(CModelDecal
* decal
)
1771 // Decals can't cast shadows since they're flat on the terrain.
1772 // They can receive shadows, but the terrain under them will have
1773 // already been passed to AddShadowCasterBound, so don't bother
1774 // doing it again here.
1776 m
->terrainRenderer
.Submit(m_CurrentCullGroup
, decal
);
1779 void CRenderer::Submit(CParticleEmitter
* emitter
)
1781 m
->particleRenderer
.Submit(m_CurrentCullGroup
, emitter
);
1784 void CRenderer::SubmitNonRecursive(CModel
* model
)
1786 if (m_CurrentCullGroup
== CULL_DEFAULT
)
1788 m
->shadow
.AddShadowReceiverBound(model
->GetWorldBounds());
1790 if (model
->GetFlags() & MODELFLAG_SILHOUETTE_OCCLUDER
)
1791 m
->silhouetteRenderer
.AddOccluder(model
);
1792 if (model
->GetFlags() & MODELFLAG_SILHOUETTE_DISPLAY
)
1793 m
->silhouetteRenderer
.AddCaster(model
);
1796 if (m_CurrentCullGroup
== CULL_SHADOWS
)
1798 if (!(model
->GetFlags() & MODELFLAG_CASTSHADOWS
))
1801 m
->shadow
.AddShadowCasterBound(model
->GetWorldBounds());
1804 bool requiresSkinning
= (model
->GetModelDef()->GetNumBones() != 0);
1806 if (model
->GetMaterial().UsesAlphaBlending())
1808 if (requiresSkinning
)
1809 m
->Model
.TranspSkinned
->Submit(m_CurrentCullGroup
, model
);
1811 m
->Model
.TranspUnskinned
->Submit(m_CurrentCullGroup
, model
);
1815 if (requiresSkinning
)
1816 m
->Model
.NormalSkinned
->Submit(m_CurrentCullGroup
, model
);
1818 m
->Model
.NormalUnskinned
->Submit(m_CurrentCullGroup
, model
);
1823 ///////////////////////////////////////////////////////////
1824 // Render the given scene
1825 void CRenderer::RenderScene(Scene
& scene
)
1827 m_CurrentScene
= &scene
;
1829 CFrustum frustum
= m_CullCamera
.GetFrustum();
1831 m_CurrentCullGroup
= CULL_DEFAULT
;
1833 scene
.EnumerateObjects(frustum
, this);
1835 m
->particleManager
.RenderSubmit(*this, frustum
);
1837 if (m_Options
.m_Silhouettes
)
1839 m
->silhouetteRenderer
.ComputeSubmissions(m_ViewCamera
);
1841 m_CurrentCullGroup
= CULL_DEFAULT
;
1842 m
->silhouetteRenderer
.RenderSubmitOverlays(*this);
1844 m_CurrentCullGroup
= CULL_SILHOUETTE_OCCLUDER
;
1845 m
->silhouetteRenderer
.RenderSubmitOccluders(*this);
1847 m_CurrentCullGroup
= CULL_SILHOUETTE_CASTER
;
1848 m
->silhouetteRenderer
.RenderSubmitCasters(*this);
1851 if (m_Caps
.m_Shadows
&& m_Options
.m_Shadows
&& GetRenderPath() == RP_SHADER
)
1853 m_CurrentCullGroup
= CULL_SHADOWS
;
1855 CFrustum shadowFrustum
= m
->shadow
.GetShadowCasterCullFrustum();
1856 scene
.EnumerateObjects(shadowFrustum
, this);
1859 CBoundingBoxAligned waterScissor
;
1860 if (m_WaterManager
->m_RenderWater
)
1862 waterScissor
= m
->terrainRenderer
.ScissorWater(CULL_DEFAULT
, m_ViewCamera
.GetViewProjection());
1864 if (waterScissor
.GetVolume() > 0 && m_WaterManager
->WillRenderFancyWater())
1866 if (m_Options
.m_WaterReflection
)
1868 m_CurrentCullGroup
= CULL_REFLECTIONS
;
1870 CCamera reflectionCamera
;
1871 ComputeReflectionCamera(reflectionCamera
, waterScissor
);
1873 scene
.EnumerateObjects(reflectionCamera
.GetFrustum(), this);
1876 if (m_Options
.m_WaterRefraction
)
1878 m_CurrentCullGroup
= CULL_REFRACTIONS
;
1880 CCamera refractionCamera
;
1881 ComputeRefractionCamera(refractionCamera
, waterScissor
);
1883 scene
.EnumerateObjects(refractionCamera
.GetFrustum(), this);
1886 // Render the waves to the Fancy effects texture
1887 m_WaterManager
->RenderWaves(frustum
);
1890 m_CurrentCullGroup
= -1;
1894 RenderSubmissions(waterScissor
);
1896 m_CurrentScene
= NULL
;
1899 Scene
& CRenderer::GetScene()
1901 ENSURE(m_CurrentScene
);
1902 return *m_CurrentScene
;
1905 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1906 // BindTexture: bind a GL texture object to current active unit
1907 void CRenderer::BindTexture(int unit
, GLuint tex
)
1909 pglActiveTextureARB(GL_TEXTURE0
+unit
);
1911 glBindTexture(GL_TEXTURE_2D
, tex
);
1914 glEnable(GL_TEXTURE_2D
);
1916 glDisable(GL_TEXTURE_2D
);
1921 ///////////////////////////////////////////////////////////////////////////////////////////////////
1922 // LoadAlphaMaps: load the 14 default alpha maps, pack them into one composite texture and
1923 // calculate the coordinate of each alphamap within this packed texture
1924 // NB: A variant of this function is duplicated in TerrainTextureEntry.cpp, for use with the Shader
1925 // renderpath. This copy is kept to load the 'standard' maps for the fixed pipeline and should
1926 // be removed if/when the fixed pipeline goes.
1927 int CRenderer::LoadAlphaMaps()
1929 const wchar_t* const key
= L
"(alpha map composite)";
1930 Handle ht
= ogl_tex_find(key
);
1931 // alpha map texture had already been created and is still in memory:
1932 // reuse it, do not load again.
1935 m_hCompositeAlphaMap
= ht
;
1940 // load all textures and store Handle in array
1942 Handle textures
[NumAlphaMaps
] = {0};
1943 VfsPath
path(L
"art/textures/terrain/alphamaps/standard");
1944 const wchar_t* fnames
[NumAlphaMaps
] = {
1948 L
"blendedgecorner.png",
1949 L
"blendedgetwocorners.png",
1950 L
"blendfourcorners.png",
1951 L
"blendtwooppositecorners.png",
1952 L
"blendlshapecorner.png",
1953 L
"blendtwocorners.png",
1955 L
"blendtwoedges.png",
1956 L
"blendthreecorners.png",
1960 size_t base
= 0; // texture width/height (see below)
1961 // for convenience, we require all alpha maps to be of the same BPP
1962 // (avoids another ogl_tex_get_size call, and doesn't hurt)
1964 for(size_t i
=0;i
<NumAlphaMaps
;i
++)
1966 // note: these individual textures can be discarded afterwards;
1967 // we cache the composite.
1968 textures
[i
] = ogl_tex_load(g_VFS
, path
/ fnames
[i
]);
1969 RETURN_STATUS_IF_ERR(textures
[i
]);
1971 // get its size and make sure they are all equal.
1972 // (the packing algo assumes this)
1973 size_t this_width
= 0, this_height
= 0, this_bpp
= 0; // fail-safe
1974 (void)ogl_tex_get_size(textures
[i
], &this_width
, &this_height
, &this_bpp
);
1975 if(this_width
!= this_height
)
1976 DEBUG_DISPLAY_ERROR(L
"Alpha maps are not square");
1977 // .. first iteration: establish size
1983 // .. not first: make sure texture size matches
1984 else if(base
!= this_width
|| bpp
!= this_bpp
)
1985 DEBUG_DISPLAY_ERROR(L
"Alpha maps are not identically sized (including pixel depth)");
1989 // copy each alpha map (tile) into one buffer, arrayed horizontally.
1991 size_t tile_w
= 2+base
+2; // 2 pixel border (avoids bilinear filtering artifacts)
1992 size_t total_w
= round_up_to_pow2(tile_w
* NumAlphaMaps
);
1993 size_t total_h
= base
; ENSURE(is_pow2(total_h
));
1994 shared_ptr
<u8
> data
;
1995 AllocateAligned(data
, total_w
*total_h
, maxSectorSize
);
1996 // for each tile on row
1997 for (size_t i
= 0; i
< NumAlphaMaps
; i
++)
2001 (void)ogl_tex_get_data(textures
[i
], &src
);
2003 size_t srcstep
= bpp
/8;
2005 // get destination of copy
2006 u8
* dst
= data
.get() + (i
*tile_w
);
2008 // for each row of image
2009 for (size_t j
= 0; j
< base
; j
++)
2011 // duplicate first pixel
2016 for (size_t k
= 0; k
< base
; k
++)
2022 // duplicate last pixel
2023 *dst
++ = *(src
-srcstep
);
2024 *dst
++ = *(src
-srcstep
);
2026 // advance write pointer for next row
2027 dst
+= total_w
-tile_w
;
2030 m_AlphaMapCoords
[i
].u0
= float(i
*tile_w
+2) / float(total_w
);
2031 m_AlphaMapCoords
[i
].u1
= float((i
+1)*tile_w
-2) / float(total_w
);
2032 m_AlphaMapCoords
[i
].v0
= 0.0f
;
2033 m_AlphaMapCoords
[i
].v1
= 1.0f
;
2036 for (size_t i
= 0; i
< NumAlphaMaps
; i
++)
2037 (void)ogl_tex_free(textures
[i
]);
2039 // upload the composite texture
2041 (void)t
.wrap(total_w
, total_h
, 8, TEX_GREY
, data
, 0);
2043 /*VfsPath filename("blendtex.png");
2046 RETURN_STATUS_IF_ERR(tex_encode(&t, filename.Extension(), &da));
2049 //Status ret = INFO::OK;
2051 shared_ptr<u8> file = DummySharedPtr(da.base);
2052 const ssize_t bytes_written = g_VFS->CreateFile(filename, file, da.pos);
2053 if(bytes_written > 0)
2054 ENSURE(bytes_written == (ssize_t)da.pos);
2056 // ret = (Status)bytes_written;
2059 (void)da_free(&da);*/
2061 m_hCompositeAlphaMap
= ogl_tex_wrap(&t
, g_VFS
, key
);
2062 (void)ogl_tex_set_filter(m_hCompositeAlphaMap
, GL_LINEAR
);
2063 (void)ogl_tex_set_wrap (m_hCompositeAlphaMap
, GL_CLAMP_TO_EDGE
, GL_CLAMP_TO_EDGE
);
2064 int ret
= ogl_tex_upload(m_hCompositeAlphaMap
, GL_ALPHA
, 0, 0);
2069 ///////////////////////////////////////////////////////////////////////////////////////////////////
2070 // UnloadAlphaMaps: frees the resources allocates by LoadAlphaMaps
2071 void CRenderer::UnloadAlphaMaps()
2073 ogl_tex_free(m_hCompositeAlphaMap
);
2074 m_hCompositeAlphaMap
= 0;
2079 Status
CRenderer::ReloadChangedFileCB(void* param
, const VfsPath
& path
)
2081 CRenderer
* renderer
= static_cast<CRenderer
*>(param
);
2083 // If an alpha map changed, and we already loaded them, then reload them
2084 if (boost::algorithm::starts_with(path
.string(), L
"art/textures/terrain/alphamaps/"))
2086 if (renderer
->m_hCompositeAlphaMap
)
2088 renderer
->UnloadAlphaMaps();
2089 renderer
->LoadAlphaMaps();
2096 void CRenderer::MakeShadersDirty()
2098 m
->ShadersDirty
= true;
2099 m_WaterManager
->m_NeedsReloading
= true;
2102 CTextureManager
& CRenderer::GetTextureManager()
2104 return m
->textureManager
;
2107 CShaderManager
& CRenderer::GetShaderManager()
2109 return m
->shaderManager
;
2112 CParticleManager
& CRenderer::GetParticleManager()
2114 return m
->particleManager
;
2117 TerrainRenderer
& CRenderer::GetTerrainRenderer()
2119 return m
->terrainRenderer
;
2122 CTimeManager
& CRenderer::GetTimeManager()
2124 return m
->timeManager
;
2127 CMaterialManager
& CRenderer::GetMaterialManager()
2129 return m
->materialManager
;
2132 CPostprocManager
& CRenderer::GetPostprocManager()
2134 return m
->postprocManager
;
2137 CFontManager
& CRenderer::GetFontManager()
2139 return m
->fontManager
;
2142 ShadowMap
& CRenderer::GetShadowMap()
2147 void CRenderer::ResetState()
2149 // Clear all emitters, that were created in previous games
2150 GetParticleManager().ClearUnattachedEmitters();