In preparation of renaming and grouping main.cpp shutdown variables:
[0ad.git] / source / renderer / Renderer.cpp
blobeb36876c67fba6e297a57b0ae9e2d5070438610c
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"
25 #include <map>
26 #include <set>
27 #include <algorithm>
29 #include <boost/algorithm/string.hpp>
31 #include "Renderer.h"
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"
40 #include "ps/Game.h"
41 #include "ps/Profile.h"
42 #include "ps/Filesystem.h"
43 #include "ps/World.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"
78 struct SScreenRect
80 GLint x1, y1, x2, y2;
83 ///////////////////////////////////////////////////////////////////////////////////
84 // CRendererStatsTable - Profile display of rendering stats
86 /**
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
91 * constructor.
93 class CRendererStatsTable : public AbstractProfileTable
95 NONCOPYABLE(CRendererStatsTable);
96 public:
97 CRendererStatsTable(const CRenderer::Stats& st);
99 // Implementation of AbstractProfileTable interface
100 CStr GetName();
101 CStr GetTitle();
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);
107 private:
108 /// Reference to the renderer singleton's stats
109 const CRenderer::Stats& Stats;
111 /// Column descriptions
112 std::vector<ProfileColumn> columnDescriptions;
114 enum {
115 Row_DrawCalls = 0,
116 Row_TerrainTris,
117 Row_WaterTris,
118 Row_ModelTris,
119 Row_OverlayTris,
120 Row_BlendSplats,
121 Row_Particles,
122 Row_VBReserved,
123 Row_VBAllocated,
124 Row_TextureMemory,
125 Row_ShadersLoaded,
127 // Must be last to count number of rows
128 NumberRows
132 // Construction
133 CRendererStatsTable::CRendererStatsTable(const CRenderer::Stats& st)
134 : 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()
143 return "renderer";
146 CStr CRendererStatsTable::GetTitle()
148 return "Renderer statistics";
151 size_t CRendererStatsTable::GetNumberRows()
153 return NumberRows;
156 const std::vector<ProfileColumn>& CRendererStatsTable::GetColumns()
158 return columnDescriptions;
161 CStr CRendererStatsTable::GetCellText(size_t row, size_t col)
163 char buf[256];
165 switch(row)
167 case Row_DrawCalls:
168 if (col == 0)
169 return "# draw calls";
170 sprintf_s(buf, sizeof(buf), "%lu", (unsigned long)Stats.m_DrawCalls);
171 return buf;
173 case Row_TerrainTris:
174 if (col == 0)
175 return "# terrain tris";
176 sprintf_s(buf, sizeof(buf), "%lu", (unsigned long)Stats.m_TerrainTris);
177 return buf;
179 case Row_WaterTris:
180 if (col == 0)
181 return "# water tris";
182 sprintf_s(buf, sizeof(buf), "%lu", (unsigned long)Stats.m_WaterTris);
183 return buf;
185 case Row_ModelTris:
186 if (col == 0)
187 return "# model tris";
188 sprintf_s(buf, sizeof(buf), "%lu", (unsigned long)Stats.m_ModelTris);
189 return buf;
191 case Row_OverlayTris:
192 if (col == 0)
193 return "# overlay tris";
194 sprintf_s(buf, sizeof(buf), "%lu", (unsigned long)Stats.m_OverlayTris);
195 return buf;
197 case Row_BlendSplats:
198 if (col == 0)
199 return "# blend splats";
200 sprintf_s(buf, sizeof(buf), "%lu", (unsigned long)Stats.m_BlendSplats);
201 return buf;
203 case Row_Particles:
204 if (col == 0)
205 return "# particles";
206 sprintf_s(buf, sizeof(buf), "%lu", (unsigned long)Stats.m_Particles);
207 return buf;
209 case Row_VBReserved:
210 if (col == 0)
211 return "VB reserved";
212 sprintf_s(buf, sizeof(buf), "%lu kB", (unsigned long)g_VBMan.GetBytesReserved() / 1024);
213 return buf;
215 case Row_VBAllocated:
216 if (col == 0)
217 return "VB allocated";
218 sprintf_s(buf, sizeof(buf), "%lu kB", (unsigned long)g_VBMan.GetBytesAllocated() / 1024);
219 return buf;
221 case Row_TextureMemory:
222 if (col == 0)
223 return "textures uploaded";
224 sprintf_s(buf, sizeof(buf), "%lu kB", (unsigned long)g_Renderer.GetTextureManager().GetBytesUploaded() / 1024);
225 return buf;
227 case Row_ShadersLoaded:
228 if (col == 0)
229 return "shader effects loaded";
230 sprintf_s(buf, sizeof(buf), "%lu", (unsigned long)g_Renderer.GetShaderManager().GetNumEffectsLoaded());
231 return buf;
233 default:
234 return "???";
238 AbstractProfileTable* CRendererStatsTable::GetChild(size_t UNUSED(row))
240 return 0;
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);
254 public:
255 /// true if CRenderer::Open has been called
256 bool IsOpen;
258 /// true if shaders need to be reloaded
259 bool ShadersDirty;
261 /// Table to display renderer stats in-game via profile system
262 CRendererStatsTable profileTable;
264 /// Shader manager
265 CShaderManager shaderManager;
267 /// Water manager
268 WaterManager waterManager;
270 /// Sky manager
271 SkyManager skyManager;
273 /// Texture manager
274 CTextureManager textureManager;
276 /// Terrain renderer
277 TerrainRenderer terrainRenderer;
279 /// Overlay renderer
280 OverlayRenderer overlayRenderer;
282 /// Particle manager
283 CParticleManager particleManager;
285 /// Particle renderer
286 ParticleRenderer particleRenderer;
288 /// Material manager
289 CMaterialManager materialManager;
291 /// Time manager
292 CTimeManager timeManager;
294 /// Shadow map
295 ShadowMap shadow;
297 /// Postprocessing effect manager
298 CPostprocManager postprocManager;
300 CFontManager fontManager;
302 SilhouetteRenderer silhouetteRenderer;
304 /// Various model renderers
305 struct Models
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;
334 } Model;
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)
349 CMatrix3D view;
350 camera.m_Orientation.GetInverse(view);
351 const CMatrix3D& proj = camera.GetProjection();
353 #if CONFIG2_GLES
354 #warning TODO: fix CRenderer camera handling for GLES (do not use global matrixes)
355 #else
356 glMatrixMode(GL_PROJECTION);
357 glLoadMatrixf(&proj._11);
359 glMatrixMode(GL_MODELVIEW);
360 glLoadMatrixf(&view._11);
361 #endif
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);
419 m_Width = 0;
420 m_Height = 0;
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";
460 CColor skycolor;
461 CFG_GET_VAL("skycolor", skystring);
462 if (skycolor.ParseString(skystring, 255.f))
463 SetClearColor(skycolor.AsSColor4ub());
465 #if CONFIG2_GLES
466 // Override config option since GLES only supports GLSL
467 m_Options.m_PreferGLSL = true;
468 #endif
470 m_ShadowZBias = 0.02f;
471 m_ShadowMapSize = 0;
473 m_LightEnv = NULL;
475 m_CurrentScene = NULL;
477 m_hCompositeAlphaMap = 0;
479 m_Stats.Reset();
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).
493 delete m;
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"))
512 m_Caps.m_VBO = true;
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;
529 #if CONFIG2_GLES
530 m_Caps.m_Shadows = true;
531 #else
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;
537 #endif
539 #if CONFIG2_GLES
540 m_Caps.m_PrettyWater = true;
541 #else
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;
544 #endif
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()
565 ENSURE(m->IsOpen);
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);
576 #if !CONFIG2_GLES
577 m->globalContext.Add(str_USE_SHADOW_SAMPLER, str_1);
578 #endif
581 if (m_LightEnv)
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));
599 else
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));
612 else
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)
623 m->IsOpen = true;
625 // Must query card capabilities before creating renderers that depend
626 // on card capabilities.
627 EnumCaps();
629 // Dimensions
630 m_Width = width;
631 m_Height = height;
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);
640 glCullFace(GL_BACK);
641 glFrontFace(GL_CCW);
642 glEnable(GL_CULL_FACE);
644 GLint bits;
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();
664 return true;
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();
673 m_Width = width;
674 m_Height = height;
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))
687 return;
689 switch (opt) {
690 case OPT_NOVBO:
691 m_Options.m_NoVBO = value;
692 break;
693 case OPT_SHADOWS:
694 m_Options.m_Shadows = value;
695 MakeShadersDirty();
696 break;
697 case OPT_WATEREFFECTS:
698 m_Options.m_WaterEffects = value;
699 break;
700 case OPT_WATERFANCYEFFECTS:
701 m_Options.m_WaterFancyEffects = value;
702 break;
703 case OPT_WATERREALDEPTH:
704 m_Options.m_WaterRealDepth = value;
705 break;
706 case OPT_WATERREFLECTION:
707 m_Options.m_WaterReflection = value;
708 break;
709 case OPT_WATERREFRACTION:
710 m_Options.m_WaterRefraction = value;
711 break;
712 case OPT_SHADOWSONWATER:
713 m_Options.m_WaterShadows = value;
714 break;
715 case OPT_SHADOWPCF:
716 m_Options.m_ShadowPCF = value;
717 MakeShadersDirty();
718 break;
719 case OPT_PARTICLES:
720 m_Options.m_Particles = value;
721 break;
722 case OPT_PREFERGLSL:
723 m_Options.m_PreferGLSL = value;
724 MakeShadersDirty();
725 RecomputeSystemShaderDefines();
726 break;
727 case OPT_FOG:
728 m_Options.m_Fog = value;
729 MakeShadersDirty();
730 break;
731 case OPT_SILHOUETTES:
732 m_Options.m_Silhouettes = value;
733 break;
734 case OPT_SHOWSKY:
735 m_Options.m_ShowSky = value;
736 break;
737 case OPT_SMOOTHLOS:
738 m_Options.m_SmoothLOS = value;
739 break;
740 case OPT_POSTPROC:
741 m_Options.m_Postproc = value;
742 break;
743 case OPT_DISPLAYFRUSTUM:
744 m_Options.m_DisplayFrustum = value;
745 break;
746 default:
747 debug_warn(L"CRenderer::SetOptionBool: unknown option");
748 break;
752 //////////////////////////////////////////////////////////////////////////////////////////
753 // GetOptionBool: get boolean renderer option
754 bool CRenderer::GetOptionBool(enum Option opt) const
756 switch (opt) {
757 case OPT_NOVBO:
758 return m_Options.m_NoVBO;
759 case OPT_SHADOWS:
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;
773 case OPT_SHADOWPCF:
774 return m_Options.m_ShadowPCF;
775 case OPT_PARTICLES:
776 return m_Options.m_Particles;
777 case OPT_PREFERGLSL:
778 return m_Options.m_PreferGLSL;
779 case OPT_FOG:
780 return m_Options.m_Fog;
781 case OPT_SILHOUETTES:
782 return m_Options.m_Silhouettes;
783 case OPT_SHOWSKY:
784 return m_Options.m_ShowSky;
785 case OPT_SMOOTHLOS:
786 return m_Options.m_SmoothLOS;
787 case OPT_POSTPROC:
788 return m_Options.m_Postproc;
789 case OPT_DISPLAYFRUSTUM:
790 return m_Options.m_DisplayFrustum;
791 default:
792 debug_warn(L"CRenderer::GetOptionBool: unknown option");
793 break;
796 return false;
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)
805 if (!m->IsOpen)
807 // Delay until Open() is called.
808 m_Options.m_RenderPath = rp;
809 return;
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))
816 rp = RP_SHADER;
817 else
818 rp = RP_FIXED;
821 if (rp == RP_SHADER)
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");
826 rp = RP_FIXED;
830 m_Options.m_RenderPath = rp;
832 MakeShadersDirty();
833 RecomputeSystemShaderDefines();
835 // We might need to regenerate some render data after changing path
836 if (g_Game)
837 g_Game->GetWorld()->GetTerrain()->MakeDirty(RENDERDATA_UPDATE_COLOR);
841 CStr CRenderer::GetRenderPathName(RenderPath rp)
843 switch(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)
853 if (name == "fixed")
854 return RP_FIXED;
855 if (name == "shader")
856 return RP_SHADER;
857 if (name == "default")
858 return RP_DEFAULT;
860 LOGWARNING("Unknown render path name '%s', assuming 'default'", name.c_str());
861 return RP_DEFAULT;
865 //////////////////////////////////////////////////////////////////////////////////////////
866 // BeginFrame: signal frame start
867 void CRenderer::BeginFrame()
869 PROFILE("begin frame");
871 // zero out all the per-frame stats
872 m_Stats.Reset();
874 // choose model renderers for this frame
876 if (m->ShadersDirty)
877 ReloadShaders();
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);
910 glCullFace(GL_BACK);
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");
938 #if CONFIG2_GLES
939 #warning TODO: implement wireface/edged rendering mode GLES
940 #else
941 // switch on wireframe if we need it
942 if (m_TerrainRenderMode == WIREFRAME)
944 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
946 #endif
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);
951 else
952 m->terrainRenderer.RenderTerrain(cullGroup);
955 #if !CONFIG2_GLES
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);
971 glLineWidth(2.0f);
973 // render tiles edges
974 m->terrainRenderer.RenderPatches(cullGroup);
976 // set color for outline
977 glColor3f(0, 0, 1);
978 glLineWidth(4.0f);
980 // render outline of each patch
981 m->terrainRenderer.RenderOutlines(cullGroup);
983 // .. and restore the renderstates
984 glLineWidth(1.0f);
985 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
987 #endif
990 void CRenderer::RenderModels(const CShaderDefines& context, int cullGroup)
992 PROFILE3_GPU("models");
994 int flags = 0;
996 #if !CONFIG2_GLES
997 if (m_ModelRenderMode == WIREFRAME)
999 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
1001 #endif
1003 m->CallModelRenderers(context, cullGroup, flags);
1005 #if !CONFIG2_GLES
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);
1022 #endif
1025 void CRenderer::RenderTransparentModels(const CShaderDefines& context, int cullGroup, ETransparentMode transparentMode, bool disableFaceCulling)
1027 PROFILE3_GPU("transparent models");
1029 int flags = 0;
1031 #if !CONFIG2_GLES
1032 // switch on wireframe if we need it
1033 if (m_ModelRenderMode == WIREFRAME)
1035 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
1037 #endif
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);
1058 #if !CONFIG2_GLES
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);
1076 #endif
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
1098 CVector4D q;
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
1108 matrix[2] = c.X;
1109 matrix[6] = c.Y;
1110 matrix[10] = c.Z - matrix[11];
1111 matrix[14] = c.W;
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
1126 fov *= 1.05f;
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));
1140 SViewPort vp;
1141 vp.m_Height = wm.m_RefTextureSize;
1142 vp.m_Width = wm.m_RefTextureSize;
1143 vp.m_X = 0;
1144 vp.m_Y = 0;
1145 camera.SetViewPort(vp);
1146 camera.SetProjection(m_ViewCamera.GetNearPlane(), m_ViewCamera.GetFarPlane(), fov);
1147 CMatrix3D scaleMat;
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
1165 fov *= 1.05f;
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.
1176 SViewPort vp;
1177 vp.m_Height = wm.m_RefTextureSize;
1178 vp.m_Width = wm.m_RefTextureSize;
1179 vp.m_X = 0;
1180 vp.m_Y = 0;
1181 camera.SetViewPort(vp);
1182 camera.SetProjection(m_ViewCamera.GetNearPlane(), m_ViewCamera.GetFarPlane(), fov);
1183 CMatrix3D scaleMat;
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.
1195 GLint fbo;
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);
1228 glFrontFace(GL_CW);
1230 if (!m_Options.m_WaterReflection)
1232 m->skyManager.RenderSky();
1233 ogl_WarnIfError();
1235 else
1237 // Render terrain and models
1238 RenderPatches(context, CULL_REFLECTIONS);
1239 ogl_WarnIfError();
1240 RenderModels(context, CULL_REFLECTIONS);
1241 ogl_WarnIfError();
1242 RenderTransparentModels(context, CULL_REFLECTIONS, TRANSPARENT, true);
1243 ogl_WarnIfError();
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);
1252 ogl_WarnIfError();
1255 glDisable(GL_SCISSOR_TEST);
1257 // Reset old camera
1258 m_ViewCamera = normalCamera;
1259 m->SetOpenGLCamera(m_ViewCamera);
1261 // rebind post-processing frambuffer.
1262 pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);
1264 return;
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.
1275 GLint fbo;
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);
1313 ogl_WarnIfError();
1314 RenderModels(context, CULL_REFRACTIONS);
1315 ogl_WarnIfError();
1316 RenderTransparentModels(context, CULL_REFRACTIONS, TRANSPARENT_OPAQUE, false);
1317 ogl_WarnIfError();
1319 glDisable(GL_SCISSOR_TEST);
1321 // Reset old camera
1322 m_ViewCamera = normalCamera;
1323 m->SetOpenGLCamera(m_ViewCamera);
1325 // rebind post-processing frambuffer.
1326 pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);
1328 return;
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
1346 // color.
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
1391 // on top.
1392 glDepthMask(0);
1394 else
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).
1398 glEnable(GL_BLEND);
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);
1417 // Restore state
1418 glDepthFunc(GL_LEQUAL);
1419 if (silhouetteAlpha == 1.f)
1421 glDepthMask(1);
1423 else
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)
1436 return;
1438 PROFILE3_GPU("particles");
1440 m->particleRenderer.RenderParticles(cullGroup);
1442 #if !CONFIG2_GLES
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);
1464 #endif
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;
1485 ogl_WarnIfError();
1487 // Set the camera
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);
1518 ogl_WarnIfError();
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);
1539 ogl_WarnIfError();
1541 // render debug-related terrain overlays
1542 ITerrainOverlay::RenderOverlaysBeforeWater();
1543 ogl_WarnIfError();
1545 // render other debug-related overlays before water (so they can be seen when underwater)
1546 m->overlayRenderer.RenderOverlaysBeforeWater();
1547 ogl_WarnIfError();
1549 RenderModels(context, cullGroup);
1550 ogl_WarnIfError();
1552 // render water
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);
1557 ogl_WarnIfError();
1559 m->terrainRenderer.RenderWater(context, cullGroup, &m->shadow);
1560 ogl_WarnIfError();
1562 // render transparent stuff again, but only the blended parts that overlap water
1563 RenderTransparentModels(context, cullGroup, TRANSPARENT_BLEND, false);
1564 ogl_WarnIfError();
1566 else
1568 // render transparent stuff, so it can overlap models/terrain
1569 RenderTransparentModels(context, cullGroup, TRANSPARENT, false);
1570 ogl_WarnIfError();
1573 // render debug-related terrain overlays
1574 ITerrainOverlay::RenderOverlaysAfterWater(cullGroup);
1575 ogl_WarnIfError();
1577 // render some other overlays after water (so they can be displayed on top of water)
1578 m->overlayRenderer.RenderOverlaysAfterWater();
1579 ogl_WarnIfError();
1581 // particles are transparent so render after water
1582 if (m_Options.m_Particles)
1584 RenderParticles(cullGroup);
1585 ogl_WarnIfError();
1588 if (m_Options.m_Postproc)
1590 m->postprocManager.ApplyPostproc();
1591 m->postprocManager.ReleaseRenderOutput();
1594 if (m_Options.m_Silhouettes)
1596 RenderSilhouettes(context);
1599 #if !CONFIG2_GLES
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);
1603 #endif
1605 // render debug lines
1606 if (m_Options.m_DisplayFrustum)
1608 DisplayFrustum();
1609 m->shadow.RenderDebugBounds();
1610 m->shadow.RenderDebugTexture();
1611 ogl_WarnIfError();
1614 m->silhouetteRenderer.RenderDebugOverlays(m_ViewCamera);
1616 // render overlays that should appear on top of all other objects
1617 m->overlayRenderer.RenderForegroundOverlays(m_ViewCamera);
1618 ogl_WarnIfError();
1622 ///////////////////////////////////////////////////////////////////////////////////////////////////
1623 // EndFrame: signal frame end
1624 void CRenderer::EndFrame()
1626 PROFILE3("end frame");
1628 // empty lists
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();
1642 ogl_tex_bind(0, 0);
1645 PROFILE3("error check");
1646 int err = glGetError();
1647 if (err)
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()
1661 #if CONFIG2_GLES
1662 #warning TODO: implement CRenderer::DisplayFrustum for GLES
1663 #else
1664 glDepthMask(0);
1665 glDisable(GL_CULL_FACE);
1666 glDisable(GL_TEXTURE_2D);
1668 glEnable(GL_BLEND);
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);
1680 glDepthMask(1);
1681 #endif
1684 ///////////////////////////////////////////////////////////////////////////////////////////////////
1685 // Text overlay rendering
1686 void CRenderer::RenderTextOverlays()
1688 PROFILE3_GPU("text overlays");
1690 if (m_DisplayTerrainPriorities)
1691 m->terrainRenderer.RenderPriorities(CULL_DEFAULT);
1693 ogl_WarnIfError();
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)
1712 m_Viewport = vp;
1713 glViewport((GLint)vp.m_X,(GLint)vp.m_Y,(GLsizei)vp.m_Width,(GLsizei)vp.m_Height);
1716 SViewPort CRenderer::GetViewport()
1718 return m_Viewport;
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))
1799 return;
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);
1810 else
1811 m->Model.TranspUnskinned->Submit(m_CurrentCullGroup, model);
1813 else
1815 if (requiresSkinning)
1816 m->Model.NormalSkinned->Submit(m_CurrentCullGroup, model);
1817 else
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;
1892 ogl_WarnIfError();
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);
1912 #if !CONFIG2_GLES
1913 if (tex) {
1914 glEnable(GL_TEXTURE_2D);
1915 } else {
1916 glDisable(GL_TEXTURE_2D);
1918 #endif
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.
1933 if(ht > 0)
1935 m_hCompositeAlphaMap = ht;
1936 return 0;
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] = {
1945 L"blendcircle.png",
1946 L"blendlshape.png",
1947 L"blendedge.png",
1948 L"blendedgecorner.png",
1949 L"blendedgetwocorners.png",
1950 L"blendfourcorners.png",
1951 L"blendtwooppositecorners.png",
1952 L"blendlshapecorner.png",
1953 L"blendtwocorners.png",
1954 L"blendcorner.png",
1955 L"blendtwoedges.png",
1956 L"blendthreecorners.png",
1957 L"blendushape.png",
1958 L"blendbad.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)
1963 size_t bpp = 0;
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
1978 if(i == 0)
1980 base = this_width;
1981 bpp = this_bpp;
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++)
1999 // get src of copy
2000 u8* src = 0;
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
2012 *dst++ = *src;
2013 *dst++ = *src;
2015 // copy a row
2016 for (size_t k = 0; k < base; k++)
2018 *dst++ = *src;
2019 src += srcstep;
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
2040 Tex t;
2041 (void)t.wrap(total_w, total_h, 8, TEX_GREY, data, 0);
2043 /*VfsPath filename("blendtex.png");
2045 DynArray da;
2046 RETURN_STATUS_IF_ERR(tex_encode(&t, filename.Extension(), &da));
2048 // write to disk
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);
2055 //else
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);
2066 return ret;
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();
2093 return INFO::OK;
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()
2144 return m->shadow;
2147 void CRenderer::ResetState()
2149 // Clear all emitters, that were created in previous games
2150 GetParticleManager().ClearUnattachedEmitters();