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