1 /* Copyright (C) 2017 Wildfire Games.
2 * This file is part of 0 A.D.
3 *
4 * 0 A.D. is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * 0 A.D. is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 /*
19 * higher level interface on top of OpenGL to render basic objects:
20 * terrain, models, sprites, particles etc.
21 */
22
23 #include "precompiled.h"
24
25 #include <map>
26 #include <set>
27 #include <algorithm>
28
29 #include <boost/algorithm/string.hpp>
30
31 #include "Renderer.h"
32
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"
77
78 struct SScreenRect
79 {
80 GLint x1, y1, x2, y2;
81 };
82
83 ///////////////////////////////////////////////////////////////////////////////////
84 // CRendererStatsTable - Profile display of rendering stats
85
86 /**
87 * Class CRendererStatsTable: Implementation of AbstractProfileTable to
88 * display the renderer stats in-game.
89 *
90 * Accesses CRenderer::m_Stats by keeping the reference passed to the
91 * constructor.
92 */
93 class CRendererStatsTable : public AbstractProfileTable
94 {
95 NONCOPYABLE(CRendererStatsTable);
96 public:
97 CRendererStatsTable(const CRenderer::Stats& st);
98
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);
106
107 private:
108 /// Reference to the renderer singleton's stats
109 const CRenderer::Stats& Stats;
110
111 /// Column descriptions
112 std::vector<ProfileColumn> columnDescriptions;
113
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,
126
127 // Must be last to count number of rows
128 NumberRows
129 };
130 };
131
132 // Construction
CRendererStatsTable(const CRenderer::Stats & st)133 CRendererStatsTable::CRendererStatsTable(const CRenderer::Stats& st)
134 : Stats(st)
135 {
136 columnDescriptions.push_back(ProfileColumn("Name", 230));
137 columnDescriptions.push_back(ProfileColumn("Value", 100));
138 }
139
140 // Implementation of AbstractProfileTable interface
GetName()141 CStr CRendererStatsTable::GetName()
142 {
143 return "renderer";
144 }
145
GetTitle()146 CStr CRendererStatsTable::GetTitle()
147 {
148 return "Renderer statistics";
149 }
150
GetNumberRows()151 size_t CRendererStatsTable::GetNumberRows()
152 {
153 return NumberRows;
154 }
155
GetColumns()156 const std::vector<ProfileColumn>& CRendererStatsTable::GetColumns()
157 {
158 return columnDescriptions;
159 }
160
GetCellText(size_t row,size_t col)161 CStr CRendererStatsTable::GetCellText(size_t row, size_t col)
162 {
163 char buf[256];
164
165 switch(row)
166 {
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;
172
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;
178
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;
184
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;
190
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;
196
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;
202
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;
208
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;
214
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;
220
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;
226
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;
232
233 default:
234 return "???";
235 }
236 }
237
GetChild(size_t UNUSED (row))238 AbstractProfileTable* CRendererStatsTable::GetChild(size_t UNUSED(row))
239 {
240 return 0;
241 }
242
243
244 ///////////////////////////////////////////////////////////////////////////////////
245 // CRenderer implementation
246
247 /**
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.
250 */
251 struct CRendererInternals
252 {
253 NONCOPYABLE(CRendererInternals);
254 public:
255 /// true if CRenderer::Open has been called
256 bool IsOpen;
257
258 /// true if shaders need to be reloaded
259 bool ShadersDirty;
260
261 /// Table to display renderer stats in-game via profile system
262 CRendererStatsTable profileTable;
263
264 /// Shader manager
265 CShaderManager shaderManager;
266
267 /// Water manager
268 WaterManager waterManager;
269
270 /// Sky manager
271 SkyManager skyManager;
272
273 /// Texture manager
274 CTextureManager textureManager;
275
276 /// Terrain renderer
277 TerrainRenderer terrainRenderer;
278
279 /// Overlay renderer
280 OverlayRenderer overlayRenderer;
281
282 /// Particle manager
283 CParticleManager particleManager;
284
285 /// Particle renderer
286 ParticleRenderer particleRenderer;
287
288 /// Material manager
289 CMaterialManager materialManager;
290
291 /// Time manager
292 CTimeManager timeManager;
293
294 /// Shadow map
295 ShadowMap shadow;
296
297 /// Postprocessing effect manager
298 CPostprocManager postprocManager;
299
300 CFontManager fontManager;
301
302 SilhouetteRenderer silhouetteRenderer;
303
304 /// Various model renderers
305 struct Models
306 {
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.
316
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
323
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
328
329 ModelVertexRendererPtr VertexRendererShader;
330 ModelVertexRendererPtr VertexInstancingShader;
331 ModelVertexRendererPtr VertexGPUSkinningShader;
332
333 LitRenderModifierPtr ModShader;
334 } Model;
335
336 CShaderDefines globalContext;
337
CRendererInternalsCRendererInternals338 CRendererInternals() :
339 IsOpen(false), ShadersDirty(true), profileTable(g_Renderer.m_Stats), textureManager(g_VFS, false, false)
340 {
341 }
342
343 /**
344 * Load the OpenGL projection and modelview matrices and the viewport according
345 * to the given camera.
346 */
SetOpenGLCameraCRendererInternals347 void SetOpenGLCamera(const CCamera& camera)
348 {
349 CMatrix3D view;
350 camera.m_Orientation.GetInverse(view);
351 const CMatrix3D& proj = camera.GetProjection();
352
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);
358
359 glMatrixMode(GL_MODELVIEW);
360 glLoadMatrixf(&view._11);
361 #endif
362
363 g_Renderer.SetViewport(camera.GetViewPort());
364 }
365
366 /**
367 * Renders all non-alpha-blended models with the given context.
368 */
CallModelRenderersCRendererInternals369 void CallModelRenderers(const CShaderDefines& context, int cullGroup, int flags)
370 {
371 CShaderDefines contextSkinned = context;
372 if (g_Renderer.m_Options.m_GPUSkinning)
373 {
374 contextSkinned.Add(str_USE_INSTANCING, str_1);
375 contextSkinned.Add(str_USE_GPU_SKINNING, str_1);
376 }
377 Model.NormalSkinned->Render(Model.ModShader, contextSkinned, cullGroup, flags);
378
379 if (Model.NormalUnskinned != Model.NormalSkinned)
380 {
381 CShaderDefines contextUnskinned = context;
382 contextUnskinned.Add(str_USE_INSTANCING, str_1);
383 Model.NormalUnskinned->Render(Model.ModShader, contextUnskinned, cullGroup, flags);
384 }
385 }
386
387 /**
388 * Renders all alpha-blended models with the given context.
389 */
CallTranspModelRenderersCRendererInternals390 void CallTranspModelRenderers(const CShaderDefines& context, int cullGroup, int flags)
391 {
392 CShaderDefines contextSkinned = context;
393 if (g_Renderer.m_Options.m_GPUSkinning)
394 {
395 contextSkinned.Add(str_USE_INSTANCING, str_1);
396 contextSkinned.Add(str_USE_GPU_SKINNING, str_1);
397 }
398 Model.TranspSkinned->Render(Model.ModShader, contextSkinned, cullGroup, flags);
399
400 if (Model.TranspUnskinned != Model.TranspSkinned)
401 {
402 CShaderDefines contextUnskinned = context;
403 contextUnskinned.Add(str_USE_INSTANCING, str_1);
404 Model.TranspUnskinned->Render(Model.ModShader, contextUnskinned, cullGroup, flags);
405 }
406 }
407 };
408
409 ///////////////////////////////////////////////////////////////////////////////////
410 // CRenderer constructor
CRenderer()411 CRenderer::CRenderer()
412 {
413 m = new CRendererInternals;
414 m_WaterManager = &m->waterManager;
415 m_SkyManager = &m->skyManager;
416
417 g_ProfileViewer.AddRootTable(&m->profileTable);
418
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;
425
426 m_DisplayTerrainPriorities = false;
427 m_SkipSubmit = false;
428
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;
451
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);
458
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());
464
465 #if CONFIG2_GLES
466 // Override config option since GLES only supports GLSL
467 m_Options.m_PreferGLSL = true;
468 #endif
469
470 m_ShadowZBias = 0.02f;
471 m_ShadowMapSize = 0;
472
473 m_LightEnv = NULL;
474
475 m_CurrentScene = NULL;
476
477 m_hCompositeAlphaMap = 0;
478
479 m_Stats.Reset();
480
481 RegisterFileReloadFunc(ReloadChangedFileCB, this);
482 }
483
484 ///////////////////////////////////////////////////////////////////////////////////
485 // CRenderer destructor
~CRenderer()486 CRenderer::~CRenderer()
487 {
488 UnregisterFileReloadFunc(ReloadChangedFileCB, this);
489
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;
494 }
495
496
497 ///////////////////////////////////////////////////////////////////////////////////
498 // EnumCaps: build card cap bits
EnumCaps()499 void CRenderer::EnumCaps()
500 {
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;
509
510 // now start querying extensions
511 if (!m_Options.m_NoVBO && ogl_HaveExtension("GL_ARB_vertex_buffer_object"))
512 m_Caps.m_VBO = true;
513
514 if (0 == ogl_HaveExtensions(0, "GL_ARB_vertex_program", "GL_ARB_fragment_program", NULL))
515 {
516 m_Caps.m_ARBProgram = true;
517 if (ogl_HaveExtension("GL_ARB_fragment_program_shadow"))
518 m_Caps.m_ARBProgramShadow = true;
519 }
520
521 if (0 == ogl_HaveExtensions(0, "GL_ARB_shader_objects", "GL_ARB_shading_language_100", NULL))
522 {
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;
527 }
528
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))
533 {
534 if (ogl_max_tex_units >= 4)
535 m_Caps.m_Shadows = true;
536 }
537 #endif
538
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
545 }
546
RecomputeSystemShaderDefines()547 void CRenderer::RecomputeSystemShaderDefines()
548 {
549 CShaderDefines defines;
550
551 if (GetRenderPath() == RP_SHADER && m_Caps.m_ARBProgram)
552 defines.Add(str_SYS_HAS_ARB, str_1);
553
554 if (GetRenderPath() == RP_SHADER && m_Caps.m_VertexShader && m_Caps.m_FragmentShader)
555 defines.Add(str_SYS_HAS_GLSL, str_1);
556
557 if (m_Options.m_PreferGLSL)
558 defines.Add(str_SYS_PREFER_GLSL, str_1);
559
560 m_SystemShaderDefines = defines;
561 }
562
ReloadShaders()563 void CRenderer::ReloadShaders()
564 {
565 ENSURE(m->IsOpen);
566
567 m->globalContext = m_SystemShaderDefines;
568
569 if (m_Caps.m_Shadows && m_Options.m_Shadows)
570 {
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
579 }
580
581 if (m_LightEnv)
582 m->globalContext.Add(CStrIntern("LIGHTING_MODEL_" + m_LightEnv->GetLightingModel()), str_1);
583
584 if (m_Options.m_PreferGLSL && m_Options.m_Fog)
585 m->globalContext.Add(str_USE_FOG, str_1);
586
587 m->Model.ModShader = LitRenderModifierPtr(new ShaderRenderModifier());
588
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));
592
593 if (GetRenderPath() == RP_SHADER && m_Options.m_GPUSkinning) // TODO: should check caps and GLSL etc too
594 {
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));
598 }
599 else
600 {
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));
604 }
605
606 // Use instancing renderers in shader mode
607 if (GetRenderPath() == RP_SHADER)
608 {
609 m->Model.NormalUnskinned = ModelRendererPtr(new ShaderModelRenderer(m->Model.VertexInstancingShader));
610 m->Model.TranspUnskinned = ModelRendererPtr(new ShaderModelRenderer(m->Model.VertexInstancingShader));
611 }
612 else
613 {
614 m->Model.NormalUnskinned = m->Model.NormalSkinned;
615 m->Model.TranspUnskinned = m->Model.TranspSkinned;
616 }
617
618 m->ShadersDirty = false;
619 }
620
Open(int width,int height)621 bool CRenderer::Open(int width, int height)
622 {
623 m->IsOpen = true;
624
625 // Must query card capabilities before creating renderers that depend
626 // on card capabilities.
627 EnumCaps();
628
629 // Dimensions
630 m_Width = width;
631 m_Height = height;
632
633 // set packing parameters
634 glPixelStorei(GL_PACK_ALIGNMENT,1);
635 glPixelStorei(GL_UNPACK_ALIGNMENT,1);
636
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);
643
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);
651
652 // Validate the currently selected render path
653 SetRenderPath(m_Options.m_RenderPath);
654
655 RecomputeSystemShaderDefines();
656
657 // Let component renderers perform one-time initialization after graphics capabilities and
658 // the shader path have been determined.
659 m->overlayRenderer.Initialize();
660
661 if (m_Options.m_Postproc)
662 m->postprocManager.Initialize();
663
664 return true;
665 }
666
667 // resize renderer view
Resize(int width,int height)668 void CRenderer::Resize(int width, int height)
669 {
670 // need to recreate the shadow map object to resize the shadow texture
671 m->shadow.RecreateTexture();
672
673 m_Width = width;
674 m_Height = height;
675
676 m->postprocManager.Resize();
677
678 m_WaterManager->Resize();
679 }
680
681 //////////////////////////////////////////////////////////////////////////////////////////
682 // SetOptionBool: set boolean renderer option
SetOptionBool(enum Option opt,bool value)683 void CRenderer::SetOptionBool(enum Option opt,bool value)
684 {
685 // Don't do anything if the option didn't change from its previous value.
686 if (value == GetOptionBool(opt))
687 return;
688
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;
749 }
750 }
751
752 //////////////////////////////////////////////////////////////////////////////////////////
753 // GetOptionBool: get boolean renderer option
GetOptionBool(enum Option opt) const754 bool CRenderer::GetOptionBool(enum Option opt) const
755 {
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;
794 }
795
796 return false;
797 }
798
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.
SetRenderPath(RenderPath rp)803 void CRenderer::SetRenderPath(RenderPath rp)
804 {
805 if (!m->IsOpen)
806 {
807 // Delay until Open() is called.
808 m_Options.m_RenderPath = rp;
809 return;
810 }
811
812 // Renderer has been opened, so validate the selected renderpath
813 if (rp == RP_DEFAULT)
814 {
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;
819 }
820
821 if (rp == RP_SHADER)
822 {
823 if (!(m_Caps.m_ARBProgram || (m_Caps.m_VertexShader && m_Caps.m_FragmentShader && m_Options.m_PreferGLSL)))
824 {
825 LOGWARNING("Falling back to fixed function\n");
826 rp = RP_FIXED;
827 }
828 }
829
830 m_Options.m_RenderPath = rp;
831
832 MakeShadersDirty();
833 RecomputeSystemShaderDefines();
834
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);
838 }
839
840
GetRenderPathName(RenderPath rp)841 CStr CRenderer::GetRenderPathName(RenderPath rp)
842 {
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)";
848 }
849 }
850
GetRenderPathByName(const CStr & name)851 CRenderer::RenderPath CRenderer::GetRenderPathByName(const CStr& name)
852 {
853 if (name == "fixed")
854 return RP_FIXED;
855 if (name == "shader")
856 return RP_SHADER;
857 if (name == "default")
858 return RP_DEFAULT;
859
860 LOGWARNING("Unknown render path name '%s', assuming 'default'", name.c_str());
861 return RP_DEFAULT;
862 }
863
864
865 //////////////////////////////////////////////////////////////////////////////////////////
866 // BeginFrame: signal frame start
BeginFrame()867 void CRenderer::BeginFrame()
868 {
869 PROFILE("begin frame");
870
871 // zero out all the per-frame stats
872 m_Stats.Reset();
873
874 // choose model renderers for this frame
875
876 if (m->ShadersDirty)
877 ReloadShaders();
878
879 m->Model.ModShader->SetShadowMap(&m->shadow);
880 m->Model.ModShader->SetLightEnv(m_LightEnv);
881 }
882
883 //////////////////////////////////////////////////////////////////////////////////////////
SetSimulation(CSimulation2 * simulation)884 void CRenderer::SetSimulation(CSimulation2* simulation)
885 {
886 // set current simulation context for terrain renderer
887 m->terrainRenderer.SetSimulation(simulation);
888 }
889
890 // SetClearColor: set color used to clear screen in BeginFrame()
SetClearColor(SColor4ub color)891 void CRenderer::SetClearColor(SColor4ub color)
892 {
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;
897 }
898
RenderShadowMap(const CShaderDefines & context)899 void CRenderer::RenderShadowMap(const CShaderDefines& context)
900 {
901 PROFILE3_GPU("shadow map");
902
903 m->shadow.BeginRender();
904
905 {
906 PROFILE("render patches");
907 glCullFace(GL_FRONT);
908 glEnable(GL_CULL_FACE);
909 m->terrainRenderer.RenderPatches(CULL_SHADOWS);
910 glCullFace(GL_BACK);
911 }
912
913 CShaderDefines contextCast = context;
914 contextCast.Add(str_MODE_SHADOWCAST, str_1);
915
916 {
917 PROFILE("render models");
918 m->CallModelRenderers(contextCast, CULL_SHADOWS, MODELFLAG_CASTSHADOWS);
919 }
920
921 {
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);
927 }
928
929 m->shadow.EndRender();
930
931 m->SetOpenGLCamera(m_ViewCamera);
932 }
933
RenderPatches(const CShaderDefines & context,int cullGroup)934 void CRenderer::RenderPatches(const CShaderDefines& context, int cullGroup)
935 {
936 PROFILE3_GPU("patches");
937
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)
943 {
944 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
945 }
946 #endif
947
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);
953
954
955 #if !CONFIG2_GLES
956 if (m_TerrainRenderMode == WIREFRAME)
957 {
958 // switch wireframe off again
959 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
960 }
961 else if (m_TerrainRenderMode == EDGED_FACES)
962 {
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);
966
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);
972
973 // render tiles edges
974 m->terrainRenderer.RenderPatches(cullGroup);
975
976 // set color for outline
977 glColor3f(0, 0, 1);
978 glLineWidth(4.0f);
979
980 // render outline of each patch
981 m->terrainRenderer.RenderOutlines(cullGroup);
982
983 // .. and restore the renderstates
984 glLineWidth(1.0f);
985 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
986 }
987 #endif
988 }
989
RenderModels(const CShaderDefines & context,int cullGroup)990 void CRenderer::RenderModels(const CShaderDefines& context, int cullGroup)
991 {
992 PROFILE3_GPU("models");
993
994 int flags = 0;
995
996 #if !CONFIG2_GLES
997 if (m_ModelRenderMode == WIREFRAME)
998 {
999 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
1000 }
1001 #endif
1002
1003 m->CallModelRenderers(context, cullGroup, flags);
1004
1005 #if !CONFIG2_GLES
1006 if (m_ModelRenderMode == WIREFRAME)
1007 {
1008 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
1009 }
1010 else if (m_ModelRenderMode == EDGED_FACES)
1011 {
1012 CShaderDefines contextWireframe = context;
1013 contextWireframe.Add(str_MODE_WIREFRAME, str_1);
1014
1015 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
1016 glDisable(GL_TEXTURE_2D);
1017
1018 m->CallModelRenderers(contextWireframe, cullGroup, flags);
1019
1020 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
1021 }
1022 #endif
1023 }
1024
RenderTransparentModels(const CShaderDefines & context,int cullGroup,ETransparentMode transparentMode,bool disableFaceCulling)1025 void CRenderer::RenderTransparentModels(const CShaderDefines& context, int cullGroup, ETransparentMode transparentMode, bool disableFaceCulling)
1026 {
1027 PROFILE3_GPU("transparent models");
1028
1029 int flags = 0;
1030
1031 #if !CONFIG2_GLES
1032 // switch on wireframe if we need it
1033 if (m_ModelRenderMode == WIREFRAME)
1034 {
1035 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
1036 }
1037 #endif
1038
1039 // disable face culling for two-sided models in sub-renders
1040 if (disableFaceCulling)
1041 glDisable(GL_CULL_FACE);
1042
1043 CShaderDefines contextOpaque = context;
1044 contextOpaque.Add(str_ALPHABLEND_PASS_OPAQUE, str_1);
1045
1046 CShaderDefines contextBlend = context;
1047 contextBlend.Add(str_ALPHABLEND_PASS_BLEND, str_1);
1048
1049 if (transparentMode == TRANSPARENT || transparentMode == TRANSPARENT_OPAQUE)
1050 m->CallTranspModelRenderers(contextOpaque, cullGroup, flags);
1051
1052 if (transparentMode == TRANSPARENT || transparentMode == TRANSPARENT_BLEND)
1053 m->CallTranspModelRenderers(contextBlend, cullGroup, flags);
1054
1055 if (disableFaceCulling)
1056 glEnable(GL_CULL_FACE);
1057
1058 #if !CONFIG2_GLES
1059 if (m_ModelRenderMode == WIREFRAME)
1060 {
1061 // switch wireframe off again
1062 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
1063 }
1064 else if (m_ModelRenderMode == EDGED_FACES)
1065 {
1066 CShaderDefines contextWireframe = contextOpaque;
1067 contextWireframe.Add(str_MODE_WIREFRAME, str_1);
1068
1069 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
1070 glDisable(GL_TEXTURE_2D);
1071
1072 m->CallTranspModelRenderers(contextWireframe, cullGroup, flags);
1073
1074 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
1075 }
1076 #endif
1077 }
1078
1079
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)
SetObliqueFrustumClipping(CCamera & camera,const CVector4D & worldPlane) const1084 void CRenderer::SetObliqueFrustumClipping(CCamera& camera, const CVector4D& worldPlane) const
1085 {
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);
1090
1091 CMatrix3D matrix = camera.GetProjection();
1092
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
1097
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];
1103
1104 // Calculate the scaled plane vector
1105 CVector4D c = camPlane * (2.0f * matrix[11] / camPlane.Dot(q));
1106
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;
1112
1113 // Load it back into the camera
1114 camera.SetProjection(matrix);
1115 }
1116
ComputeReflectionCamera(CCamera & camera,const CBoundingBoxAligned & scissor) const1117 void CRenderer::ComputeReflectionCamera(CCamera& camera, const CBoundingBoxAligned& scissor) const
1118 {
1119 WaterManager& wm = m->waterManager;
1120
1121 float fov = m_ViewCamera.GetFOV();
1122
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;
1127
1128 camera = m_ViewCamera;
1129
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));
1139
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;
1150
1151 CVector4D camPlane(0, 1, 0, -wm.m_WaterHeight + 0.5f);
1152 SetObliqueFrustumClipping(camera, camPlane);
1153
1154 }
1155
ComputeRefractionCamera(CCamera & camera,const CBoundingBoxAligned & scissor) const1156 void CRenderer::ComputeRefractionCamera(CCamera& camera, const CBoundingBoxAligned& scissor) const
1157 {
1158 WaterManager& wm = m->waterManager;
1159
1160 float fov = m_ViewCamera.GetFOV();
1161
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;
1166
1167 camera = m_ViewCamera;
1168
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.
1175
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;
1186 }
1187
1188 ///////////////////////////////////////////////////////////////////////////////////////////////////
1189 // RenderReflections: render the water reflections to the reflection texture
RenderReflections(const CShaderDefines & context,const CBoundingBoxAligned & scissor)1190 void CRenderer::RenderReflections(const CShaderDefines& context, const CBoundingBoxAligned& scissor)
1191 {
1192 PROFILE3_GPU("water reflections");
1193
1194 // Save the post-processing framebuffer.
1195 GLint fbo;
1196 glGetIntegerv(GL_FRAMEBUFFER_BINDING_EXT, &fbo);
1197
1198 WaterManager& wm = m->waterManager;
1199
1200 // Remember old camera
1201 CCamera normalCamera = m_ViewCamera;
1202
1203 ComputeReflectionCamera(m_ViewCamera, scissor);
1204
1205 m->SetOpenGLCamera(m_ViewCamera);
1206
1207 // Save the model-view-projection matrix so the shaders can use it for projective texturing
1208 wm.m_ReflectionMatrix = m_ViewCamera.GetViewProjection();
1209
1210 float vpHeight = wm.m_RefTextureSize;
1211 float vpWidth = wm.m_RefTextureSize;
1212
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);
1218
1219 glEnable(GL_SCISSOR_TEST);
1220 glScissor(screenScissor.x1, screenScissor.y1, screenScissor.x2 - screenScissor.x1, screenScissor.y2 - screenScissor.y1);
1221
1222 // try binding the framebuffer
1223 pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, wm.m_ReflectionFbo);
1224
1225 glClearColor(0.5f,0.5f,1.0f,0.0f);
1226 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1227
1228 glFrontFace(GL_CW);
1229
1230 if (!m_Options.m_WaterReflection)
1231 {
1232 m->skyManager.RenderSky();
1233 ogl_WarnIfError();
1234 }
1235 else
1236 {
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();
1244 }
1245 glFrontFace(GL_CCW);
1246
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)
1250 {
1251 RenderParticles(CULL_REFLECTIONS);
1252 ogl_WarnIfError();
1253 }
1254
1255 glDisable(GL_SCISSOR_TEST);
1256
1257 // Reset old camera
1258 m_ViewCamera = normalCamera;
1259 m->SetOpenGLCamera(m_ViewCamera);
1260
1261 // rebind post-processing frambuffer.
1262 pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);
1263
1264 return;
1265 }
1266
1267
1268 ///////////////////////////////////////////////////////////////////////////////////////////////////
1269 // RenderRefractions: render the water refractions to the refraction texture
RenderRefractions(const CShaderDefines & context,const CBoundingBoxAligned & scissor)1270 void CRenderer::RenderRefractions(const CShaderDefines& context, const CBoundingBoxAligned &scissor)
1271 {
1272 PROFILE3_GPU("water refractions");
1273
1274 // Save the post-processing framebuffer.
1275 GLint fbo;
1276 glGetIntegerv(GL_FRAMEBUFFER_BINDING_EXT, &fbo);
1277
1278 WaterManager& wm = m->waterManager;
1279
1280 // Remember old camera
1281 CCamera normalCamera = m_ViewCamera;
1282
1283 ComputeRefractionCamera(m_ViewCamera, scissor);
1284
1285 CVector4D camPlane(0, -1, 0, wm.m_WaterHeight + 2.0f);
1286 SetObliqueFrustumClipping(m_ViewCamera, camPlane);
1287
1288 m->SetOpenGLCamera(m_ViewCamera);
1289
1290 // Save the model-view-projection matrix so the shaders can use it for projective texturing
1291 wm.m_RefractionMatrix = m_ViewCamera.GetViewProjection();
1292
1293 float vpHeight = wm.m_RefTextureSize;
1294 float vpWidth = wm.m_RefTextureSize;
1295
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);
1301
1302 glEnable(GL_SCISSOR_TEST);
1303 glScissor(screenScissor.x1, screenScissor.y1, screenScissor.x2 - screenScissor.x1, screenScissor.y2 - screenScissor.y1);
1304
1305 // try binding the framebuffer
1306 pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, wm.m_RefractionFbo);
1307
1308 glClearColor(1.0f,0.0f,0.0f,0.0f);
1309 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1310
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();
1318
1319 glDisable(GL_SCISSOR_TEST);
1320
1321 // Reset old camera
1322 m_ViewCamera = normalCamera;
1323 m->SetOpenGLCamera(m_ViewCamera);
1324
1325 // rebind post-processing frambuffer.
1326 pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);
1327
1328 return;
1329 }
1330
RenderSilhouettes(const CShaderDefines & context)1331 void CRenderer::RenderSilhouettes(const CShaderDefines& context)
1332 {
1333 PROFILE3_GPU("silhouettes");
1334
1335 CShaderDefines contextOccluder = context;
1336 contextOccluder.Add(str_MODE_SILHOUETTEOCCLUDER, str_1);
1337
1338 CShaderDefines contextDisplay = context;
1339 contextDisplay.Add(str_MODE_SILHOUETTEDISPLAY, str_1);
1340
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.
1347
1348 float silhouetteAlpha = 0.75f;
1349
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;
1354
1355 glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
1356
1357 glColorMask(0, 0, 0, 0);
1358
1359 // Render occluders:
1360
1361 {
1362 PROFILE("render patches");
1363
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);
1370 }
1371
1372 {
1373 PROFILE("render model occluders");
1374 m->CallModelRenderers(contextOccluder, CULL_SILHOUETTE_OCCLUDER, 0);
1375 }
1376
1377 {
1378 PROFILE("render transparent occluders");
1379 m->CallTranspModelRenderers(contextOccluder, CULL_SILHOUETTE_OCCLUDER, 0);
1380 }
1381
1382 glDepthFunc(GL_GEQUAL);
1383 glColorMask(1, 1, 1, 1);
1384
1385 // Render more efficiently if alpha == 1
1386 if (silhouetteAlpha == 1.f)
1387 {
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);
1393 }
1394 else
1395 {
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);
1401
1402 glEnable(GL_STENCIL_TEST);
1403 glStencilFunc(GL_NOTEQUAL, 1, (GLuint)-1);
1404 glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
1405 }
1406
1407 {
1408 PROFILE("render model casters");
1409 m->CallModelRenderers(contextDisplay, CULL_SILHOUETTE_CASTER, 0);
1410 }
1411
1412 {
1413 PROFILE("render transparent casters");
1414 m->CallTranspModelRenderers(contextDisplay, CULL_SILHOUETTE_CASTER, 0);
1415 }
1416
1417 // Restore state
1418 glDepthFunc(GL_LEQUAL);
1419 if (silhouetteAlpha == 1.f)
1420 {
1421 glDepthMask(1);
1422 }
1423 else
1424 {
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);
1429 }
1430 }
1431
RenderParticles(int cullGroup)1432 void CRenderer::RenderParticles(int cullGroup)
1433 {
1434 // Only supported in shader modes
1435 if (GetRenderPath() != RP_SHADER)
1436 return;
1437
1438 PROFILE3_GPU("particles");
1439
1440 m->particleRenderer.RenderParticles(cullGroup);
1441
1442 #if !CONFIG2_GLES
1443 if (m_ModelRenderMode == EDGED_FACES)
1444 {
1445 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
1446
1447 glDisable(GL_TEXTURE_2D);
1448 glColor3f(0.0f, 0.5f, 0.0f);
1449
1450 m->particleRenderer.RenderParticles(true);
1451
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());
1457
1458 m->particleRenderer.RenderBounds(cullGroup, shader);
1459
1460 shaderTech->EndPass();
1461
1462 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
1463 }
1464 #endif
1465 }
1466
1467 ///////////////////////////////////////////////////////////////////////////////////////////////////
1468 // RenderSubmissions: force rendering of any batched objects
RenderSubmissions(const CBoundingBoxAligned & waterScissor)1469 void CRenderer::RenderSubmissions(const CBoundingBoxAligned& waterScissor)
1470 {
1471 PROFILE3("render submissions");
1472
1473 GetScene().GetLOSTexture().InterpolateLOS();
1474
1475 if (m_Options.m_Postproc)
1476 {
1477 m->postprocManager.Initialize();
1478 m->postprocManager.CaptureRenderOutput();
1479 }
1480
1481 CShaderDefines context = m->globalContext;
1482
1483 int cullGroup = CULL_DEFAULT;
1484
1485 ogl_WarnIfError();
1486
1487 // Set the camera
1488 m->SetOpenGLCamera(m_ViewCamera);
1489
1490 // Prepare model renderers
1491 {
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();
1499 }
1500
1501 m->terrainRenderer.PrepareForRendering();
1502
1503 m->overlayRenderer.PrepareForRendering();
1504
1505 m->particleRenderer.PrepareForRendering(context);
1506
1507 if (m_Caps.m_Shadows && m_Options.m_Shadows && GetRenderPath() == RP_SHADER)
1508 {
1509 RenderShadowMap(context);
1510 }
1511
1512 {
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);
1516 }
1517
1518 ogl_WarnIfError();
1519
1520 if (m_WaterManager->m_RenderWater)
1521 {
1522 if (waterScissor.GetVolume() > 0 && m_WaterManager->WillRenderFancyWater())
1523 {
1524 PROFILE3_GPU("water scissor");
1525 RenderReflections(context, waterScissor);
1526
1527 if (m_Options.m_WaterRefraction)
1528 RenderRefractions(context, waterScissor);
1529 }
1530 }
1531
1532 if (m_Options.m_ShowSky)
1533 {
1534 m->skyManager.RenderSky();
1535 }
1536
1537 // render submitted patches and models
1538 RenderPatches(context, cullGroup);
1539 ogl_WarnIfError();
1540
1541 // render debug-related terrain overlays
1542 ITerrainOverlay::RenderOverlaysBeforeWater();
1543 ogl_WarnIfError();
1544
1545 // render other debug-related overlays before water (so they can be seen when underwater)
1546 m->overlayRenderer.RenderOverlaysBeforeWater();
1547 ogl_WarnIfError();
1548
1549 RenderModels(context, cullGroup);
1550 ogl_WarnIfError();
1551
1552 // render water
1553 if (m_WaterManager->m_RenderWater && g_Game && waterScissor.GetVolume() > 0)
1554 {
1555 // render transparent stuff, but only the solid parts that can occlude block water
1556 RenderTransparentModels(context, cullGroup, TRANSPARENT_OPAQUE, false);
1557 ogl_WarnIfError();
1558
1559 m->terrainRenderer.RenderWater(context, cullGroup, &m->shadow);
1560 ogl_WarnIfError();
1561
1562 // render transparent stuff again, but only the blended parts that overlap water
1563 RenderTransparentModels(context, cullGroup, TRANSPARENT_BLEND, false);
1564 ogl_WarnIfError();
1565 }
1566 else
1567 {
1568 // render transparent stuff, so it can overlap models/terrain
1569 RenderTransparentModels(context, cullGroup, TRANSPARENT, false);
1570 ogl_WarnIfError();
1571 }
1572
1573 // render debug-related terrain overlays
1574 ITerrainOverlay::RenderOverlaysAfterWater(cullGroup);
1575 ogl_WarnIfError();
1576
1577 // render some other overlays after water (so they can be displayed on top of water)
1578 m->overlayRenderer.RenderOverlaysAfterWater();
1579 ogl_WarnIfError();
1580
1581 // particles are transparent so render after water
1582 if (m_Options.m_Particles)
1583 {
1584 RenderParticles(cullGroup);
1585 ogl_WarnIfError();
1586 }
1587
1588 if (m_Options.m_Postproc)
1589 {
1590 m->postprocManager.ApplyPostproc();
1591 m->postprocManager.ReleaseRenderOutput();
1592 }
1593
1594 if (m_Options.m_Silhouettes)
1595 {
1596 RenderSilhouettes(context);
1597 }
1598
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
1604
1605 // render debug lines
1606 if (m_Options.m_DisplayFrustum)
1607 {
1608 DisplayFrustum();
1609 m->shadow.RenderDebugBounds();
1610 m->shadow.RenderDebugTexture();
1611 ogl_WarnIfError();
1612 }
1613
1614 m->silhouetteRenderer.RenderDebugOverlays(m_ViewCamera);
1615
1616 // render overlays that should appear on top of all other objects
1617 m->overlayRenderer.RenderForegroundOverlays(m_ViewCamera);
1618 ogl_WarnIfError();
1619
1620 }
1621
1622 ///////////////////////////////////////////////////////////////////////////////////////////////////
1623 // EndFrame: signal frame end
EndFrame()1624 void CRenderer::EndFrame()
1625 {
1626 PROFILE3("end frame");
1627
1628 // empty lists
1629 m->terrainRenderer.EndFrame();
1630 m->overlayRenderer.EndFrame();
1631 m->particleRenderer.EndFrame();
1632 m->silhouetteRenderer.EndFrame();
1633
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();
1641
1642 ogl_tex_bind(0, 0);
1643
1644 {
1645 PROFILE3("error check");
1646 int err = glGetError();
1647 if (err)
1648 {
1649 ONCE(LOGERROR("CRenderer::EndFrame: GL errors %s (%04x) occurred", ogl_GetErrorName(err), err));
1650 }
1651 }
1652 }
1653
1654
1655 ///////////////////////////////////////////////////////////////////////////////////////////////////
1656 // DisplayFrustum: debug displays
1657 // - white: cull camera frustum
1658 // - red: bounds of shadow casting objects
DisplayFrustum()1659 void CRenderer::DisplayFrustum()
1660 {
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);
1667
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);
1673
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);
1678
1679 glEnable(GL_CULL_FACE);
1680 glDepthMask(1);
1681 #endif
1682 }
1683
1684 ///////////////////////////////////////////////////////////////////////////////////////////////////
1685 // Text overlay rendering
RenderTextOverlays()1686 void CRenderer::RenderTextOverlays()
1687 {
1688 PROFILE3_GPU("text overlays");
1689
1690 if (m_DisplayTerrainPriorities)
1691 m->terrainRenderer.RenderPriorities(CULL_DEFAULT);
1692
1693 ogl_WarnIfError();
1694 }
1695
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.
SetSceneCamera(const CCamera & viewCamera,const CCamera & cullCamera)1700 void CRenderer::SetSceneCamera(const CCamera& viewCamera, const CCamera& cullCamera)
1701 {
1702 m_ViewCamera = viewCamera;
1703 m_CullCamera = cullCamera;
1704
1705 if (m_Caps.m_Shadows && m_Options.m_Shadows && GetRenderPath() == RP_SHADER)
1706 m->shadow.SetupFrame(m_CullCamera, m_LightEnv->GetSunDir());
1707 }
1708
1709
SetViewport(const SViewPort & vp)1710 void CRenderer::SetViewport(const SViewPort &vp)
1711 {
1712 m_Viewport = vp;
1713 glViewport((GLint)vp.m_X,(GLint)vp.m_Y,(GLsizei)vp.m_Width,(GLsizei)vp.m_Height);
1714 }
1715
GetViewport()1716 SViewPort CRenderer::GetViewport()
1717 {
1718 return m_Viewport;
1719 }
1720
Submit(CPatch * patch)1721 void CRenderer::Submit(CPatch* patch)
1722 {
1723 if (m_CurrentCullGroup == CULL_DEFAULT)
1724 {
1725 m->shadow.AddShadowReceiverBound(patch->GetWorldBounds());
1726 m->silhouetteRenderer.AddOccluder(patch);
1727 }
1728
1729 if (m_CurrentCullGroup == CULL_SHADOWS)
1730 {
1731 m->shadow.AddShadowCasterBound(patch->GetWorldBounds());
1732 }
1733
1734 m->terrainRenderer.Submit(m_CurrentCullGroup, patch);
1735 }
1736
Submit(SOverlayLine * overlay)1737 void CRenderer::Submit(SOverlayLine* overlay)
1738 {
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);
1743 }
1744
Submit(SOverlayTexturedLine * overlay)1745 void CRenderer::Submit(SOverlayTexturedLine* overlay)
1746 {
1747 if (m_CurrentCullGroup == CULL_DEFAULT)
1748 m->overlayRenderer.Submit(overlay);
1749 }
1750
Submit(SOverlaySprite * overlay)1751 void CRenderer::Submit(SOverlaySprite* overlay)
1752 {
1753 if (m_CurrentCullGroup == CULL_DEFAULT)
1754 m->overlayRenderer.Submit(overlay);
1755 }
1756
Submit(SOverlayQuad * overlay)1757 void CRenderer::Submit(SOverlayQuad* overlay)
1758 {
1759 if (m_CurrentCullGroup == CULL_DEFAULT)
1760 m->overlayRenderer.Submit(overlay);
1761 }
1762
Submit(SOverlaySphere * overlay)1763 void CRenderer::Submit(SOverlaySphere* overlay)
1764 {
1765 if (m_CurrentCullGroup == CULL_DEFAULT)
1766 m->overlayRenderer.Submit(overlay);
1767 }
1768
Submit(CModelDecal * decal)1769 void CRenderer::Submit(CModelDecal* decal)
1770 {
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.
1775
1776 m->terrainRenderer.Submit(m_CurrentCullGroup, decal);
1777 }
1778
Submit(CParticleEmitter * emitter)1779 void CRenderer::Submit(CParticleEmitter* emitter)
1780 {
1781 m->particleRenderer.Submit(m_CurrentCullGroup, emitter);
1782 }
1783
SubmitNonRecursive(CModel * model)1784 void CRenderer::SubmitNonRecursive(CModel* model)
1785 {
1786 if (m_CurrentCullGroup == CULL_DEFAULT)
1787 {
1788 m->shadow.AddShadowReceiverBound(model->GetWorldBounds());
1789
1790 if (model->GetFlags() & MODELFLAG_SILHOUETTE_OCCLUDER)
1791 m->silhouetteRenderer.AddOccluder(model);
1792 if (model->GetFlags() & MODELFLAG_SILHOUETTE_DISPLAY)
1793 m->silhouetteRenderer.AddCaster(model);
1794 }
1795
1796 if (m_CurrentCullGroup == CULL_SHADOWS)
1797 {
1798 if (!(model->GetFlags() & MODELFLAG_CASTSHADOWS))
1799 return;
1800
1801 m->shadow.AddShadowCasterBound(model->GetWorldBounds());
1802 }
1803
1804 bool requiresSkinning = (model->GetModelDef()->GetNumBones() != 0);
1805
1806 if (model->GetMaterial().UsesAlphaBlending())
1807 {
1808 if (requiresSkinning)
1809 m->Model.TranspSkinned->Submit(m_CurrentCullGroup, model);
1810 else
1811 m->Model.TranspUnskinned->Submit(m_CurrentCullGroup, model);
1812 }
1813 else
1814 {
1815 if (requiresSkinning)
1816 m->Model.NormalSkinned->Submit(m_CurrentCullGroup, model);
1817 else
1818 m->Model.NormalUnskinned->Submit(m_CurrentCullGroup, model);
1819 }
1820 }
1821
1822
1823 ///////////////////////////////////////////////////////////
1824 // Render the given scene
RenderScene(Scene & scene)1825 void CRenderer::RenderScene(Scene& scene)
1826 {
1827 m_CurrentScene = &scene;
1828
1829 CFrustum frustum = m_CullCamera.GetFrustum();
1830
1831 m_CurrentCullGroup = CULL_DEFAULT;
1832
1833 scene.EnumerateObjects(frustum, this);
1834
1835 m->particleManager.RenderSubmit(*this, frustum);
1836
1837 if (m_Options.m_Silhouettes)
1838 {
1839 m->silhouetteRenderer.ComputeSubmissions(m_ViewCamera);
1840
1841 m_CurrentCullGroup = CULL_DEFAULT;
1842 m->silhouetteRenderer.RenderSubmitOverlays(*this);
1843
1844 m_CurrentCullGroup = CULL_SILHOUETTE_OCCLUDER;
1845 m->silhouetteRenderer.RenderSubmitOccluders(*this);
1846
1847 m_CurrentCullGroup = CULL_SILHOUETTE_CASTER;
1848 m->silhouetteRenderer.RenderSubmitCasters(*this);
1849 }
1850
1851 if (m_Caps.m_Shadows && m_Options.m_Shadows && GetRenderPath() == RP_SHADER)
1852 {
1853 m_CurrentCullGroup = CULL_SHADOWS;
1854
1855 CFrustum shadowFrustum = m->shadow.GetShadowCasterCullFrustum();
1856 scene.EnumerateObjects(shadowFrustum, this);
1857 }
1858
1859 CBoundingBoxAligned waterScissor;
1860 if (m_WaterManager->m_RenderWater)
1861 {
1862 waterScissor = m->terrainRenderer.ScissorWater(CULL_DEFAULT, m_ViewCamera.GetViewProjection());
1863
1864 if (waterScissor.GetVolume() > 0 && m_WaterManager->WillRenderFancyWater())
1865 {
1866 if (m_Options.m_WaterReflection)
1867 {
1868 m_CurrentCullGroup = CULL_REFLECTIONS;
1869
1870 CCamera reflectionCamera;
1871 ComputeReflectionCamera(reflectionCamera, waterScissor);
1872
1873 scene.EnumerateObjects(reflectionCamera.GetFrustum(), this);
1874 }
1875
1876 if (m_Options.m_WaterRefraction)
1877 {
1878 m_CurrentCullGroup = CULL_REFRACTIONS;
1879
1880 CCamera refractionCamera;
1881 ComputeRefractionCamera(refractionCamera, waterScissor);
1882
1883 scene.EnumerateObjects(refractionCamera.GetFrustum(), this);
1884 }
1885 }
1886 // Render the waves to the Fancy effects texture
1887 m_WaterManager->RenderWaves(frustum);
1888 }
1889
1890 m_CurrentCullGroup = -1;
1891
1892 ogl_WarnIfError();
1893
1894 RenderSubmissions(waterScissor);
1895
1896 m_CurrentScene = NULL;
1897 }
1898
GetScene()1899 Scene& CRenderer::GetScene()
1900 {
1901 ENSURE(m_CurrentScene);
1902 return *m_CurrentScene;
1903 }
1904
1905 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1906 // BindTexture: bind a GL texture object to current active unit
BindTexture(int unit,GLuint tex)1907 void CRenderer::BindTexture(int unit, GLuint tex)
1908 {
1909 pglActiveTextureARB(GL_TEXTURE0+unit);
1910
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);
1917 }
1918 #endif
1919 }
1920
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.
LoadAlphaMaps()1927 int CRenderer::LoadAlphaMaps()
1928 {
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)
1934 {
1935 m_hCompositeAlphaMap = ht;
1936 return 0;
1937 }
1938
1939 //
1940 // load all textures and store Handle in array
1941 //
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"
1959 };
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++)
1965 {
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]);
1970
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)
1979 {
1980 base = this_width;
1981 bpp = this_bpp;
1982 }
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)");
1986 }
1987
1988 //
1989 // copy each alpha map (tile) into one buffer, arrayed horizontally.
1990 //
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++)
1998 {
1999 // get src of copy
2000 u8* src = 0;
2001 (void)ogl_tex_get_data(textures[i], &src);
2002
2003 size_t srcstep = bpp/8;
2004
2005 // get destination of copy
2006 u8* dst = data.get() + (i*tile_w);
2007
2008 // for each row of image
2009 for (size_t j = 0; j < base; j++)
2010 {
2011 // duplicate first pixel
2012 *dst++ = *src;
2013 *dst++ = *src;
2014
2015 // copy a row
2016 for (size_t k = 0; k < base; k++)
2017 {
2018 *dst++ = *src;
2019 src += srcstep;
2020 }
2021
2022 // duplicate last pixel
2023 *dst++ = *(src-srcstep);
2024 *dst++ = *(src-srcstep);
2025
2026 // advance write pointer for next row
2027 dst += total_w-tile_w;
2028 }
2029
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;
2034 }
2035
2036 for (size_t i = 0; i < NumAlphaMaps; i++)
2037 (void)ogl_tex_free(textures[i]);
2038
2039 // upload the composite texture
2040 Tex t;
2041 (void)t.wrap(total_w, total_h, 8, TEX_GREY, data, 0);
2042
2043 /*VfsPath filename("blendtex.png");
2044
2045 DynArray da;
2046 RETURN_STATUS_IF_ERR(tex_encode(&t, filename.Extension(), &da));
2047
2048 // write to disk
2049 //Status ret = INFO::OK;
2050 {
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;
2057 }
2058
2059 (void)da_free(&da);*/
2060
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);
2065
2066 return ret;
2067 }
2068
2069 ///////////////////////////////////////////////////////////////////////////////////////////////////
2070 // UnloadAlphaMaps: frees the resources allocates by LoadAlphaMaps
UnloadAlphaMaps()2071 void CRenderer::UnloadAlphaMaps()
2072 {
2073 ogl_tex_free(m_hCompositeAlphaMap);
2074 m_hCompositeAlphaMap = 0;
2075 }
2076
2077
2078
ReloadChangedFileCB(void * param,const VfsPath & path)2079 Status CRenderer::ReloadChangedFileCB(void* param, const VfsPath& path)
2080 {
2081 CRenderer* renderer = static_cast<CRenderer*>(param);
2082
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/"))
2085 {
2086 if (renderer->m_hCompositeAlphaMap)
2087 {
2088 renderer->UnloadAlphaMaps();
2089 renderer->LoadAlphaMaps();
2090 }
2091 }
2092
2093 return INFO::OK;
2094 }
2095
MakeShadersDirty()2096 void CRenderer::MakeShadersDirty()
2097 {
2098 m->ShadersDirty = true;
2099 m_WaterManager->m_NeedsReloading = true;
2100 }
2101
GetTextureManager()2102 CTextureManager& CRenderer::GetTextureManager()
2103 {
2104 return m->textureManager;
2105 }
2106
GetShaderManager()2107 CShaderManager& CRenderer::GetShaderManager()
2108 {
2109 return m->shaderManager;
2110 }
2111
GetParticleManager()2112 CParticleManager& CRenderer::GetParticleManager()
2113 {
2114 return m->particleManager;
2115 }
2116
GetTerrainRenderer()2117 TerrainRenderer& CRenderer::GetTerrainRenderer()
2118 {
2119 return m->terrainRenderer;
2120 }
2121
GetTimeManager()2122 CTimeManager& CRenderer::GetTimeManager()
2123 {
2124 return m->timeManager;
2125 }
2126
GetMaterialManager()2127 CMaterialManager& CRenderer::GetMaterialManager()
2128 {
2129 return m->materialManager;
2130 }
2131
GetPostprocManager()2132 CPostprocManager& CRenderer::GetPostprocManager()
2133 {
2134 return m->postprocManager;
2135 }
2136
GetFontManager()2137 CFontManager& CRenderer::GetFontManager()
2138 {
2139 return m->fontManager;
2140 }
2141
GetShadowMap()2142 ShadowMap& CRenderer::GetShadowMap()
2143 {
2144 return m->shadow;
2145 }
2146
ResetState()2147 void CRenderer::ResetState()
2148 {
2149 // Clear all emitters, that were created in previous games
2150 GetParticleManager().ClearUnattachedEmitters();
2151 }
2152