source: ps/trunk/source/renderer/Renderer.cpp@ 9847

Last change on this file since 9847 was 9847, checked in by philip, 11 years ago

Fix rare use of uninitialised values

  • Property svn:eol-style set to native
File size: 60.2 KB
Line 
1/* Copyright (C) 2011 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/Game.h"
40#include "ps/Profile.h"
41#include "ps/Filesystem.h"
42#include "ps/World.h"
43#include "ps/Loader.h"
44#include "ps/ProfileViewer.h"
45#include "graphics/Camera.h"
46#include "graphics/GameView.h"
47#include "graphics/LightEnv.h"
48#include "graphics/Model.h"
49#include "graphics/ModelDef.h"
50#include "graphics/ParticleManager.h"
51#include "graphics/ShaderManager.h"
52#include "graphics/ShaderTechnique.h"
53#include "graphics/Terrain.h"
54#include "graphics/Texture.h"
55#include "graphics/TextureManager.h"
56#include "renderer/FixedFunctionModelRenderer.h"
57#include "renderer/HWLightingModelRenderer.h"
58#include "renderer/InstancingModelRenderer.h"
59#include "renderer/ModelRenderer.h"
60#include "renderer/OverlayRenderer.h"
61#include "renderer/ParticleRenderer.h"
62#include "renderer/PlayerRenderer.h"
63#include "renderer/RenderModifiers.h"
64#include "renderer/ShadowMap.h"
65#include "renderer/SkyManager.h"
66#include "renderer/TerrainOverlay.h"
67#include "renderer/TerrainRenderer.h"
68#include "renderer/TransparencyRenderer.h"
69#include "renderer/VertexBufferManager.h"
70#include "renderer/WaterManager.h"
71
72
73///////////////////////////////////////////////////////////////////////////////////
74// CRendererStatsTable - Profile display of rendering stats
75
76/**
77 * Class CRendererStatsTable: Implementation of AbstractProfileTable to
78 * display the renderer stats in-game.
79 *
80 * Accesses CRenderer::m_Stats by keeping the reference passed to the
81 * constructor.
82 */
83class CRendererStatsTable : public AbstractProfileTable
84{
85 NONCOPYABLE(CRendererStatsTable);
86public:
87 CRendererStatsTable(const CRenderer::Stats& st);
88
89 // Implementation of AbstractProfileTable interface
90 CStr GetName();
91 CStr GetTitle();
92 size_t GetNumberRows();
93 const std::vector<ProfileColumn>& GetColumns();
94 CStr GetCellText(size_t row, size_t col);
95 AbstractProfileTable* GetChild(size_t row);
96
97private:
98 /// Reference to the renderer singleton's stats
99 const CRenderer::Stats& Stats;
100
101 /// Column descriptions
102 std::vector<ProfileColumn> columnDescriptions;
103
104 enum {
105 Row_DrawCalls = 0,
106 Row_TerrainTris,
107 Row_WaterTris,
108 Row_ModelTris,
109 Row_BlendSplats,
110 Row_Particles,
111 Row_VBReserved,
112 Row_VBAllocated,
113
114 // Must be last to count number of rows
115 NumberRows
116 };
117};
118
119// Construction
120CRendererStatsTable::CRendererStatsTable(const CRenderer::Stats& st)
121 : Stats(st)
122{
123 columnDescriptions.push_back(ProfileColumn("Name", 230));
124 columnDescriptions.push_back(ProfileColumn("Value", 100));
125}
126
127// Implementation of AbstractProfileTable interface
128CStr CRendererStatsTable::GetName()
129{
130 return "renderer";
131}
132
133CStr CRendererStatsTable::GetTitle()
134{
135 return "Renderer statistics";
136}
137
138size_t CRendererStatsTable::GetNumberRows()
139{
140 return NumberRows;
141}
142
143const std::vector<ProfileColumn>& CRendererStatsTable::GetColumns()
144{
145 return columnDescriptions;
146}
147
148CStr CRendererStatsTable::GetCellText(size_t row, size_t col)
149{
150 char buf[256];
151
152 switch(row)
153 {
154 case Row_DrawCalls:
155 if (col == 0)
156 return "# draw calls";
157 sprintf_s(buf, sizeof(buf), "%lu", (unsigned long)Stats.m_DrawCalls);
158 return buf;
159
160 case Row_TerrainTris:
161 if (col == 0)
162 return "# terrain tris";
163 sprintf_s(buf, sizeof(buf), "%lu", (unsigned long)Stats.m_TerrainTris);
164 return buf;
165
166 case Row_WaterTris:
167 if (col == 0)
168 return "# water tris";
169 sprintf_s(buf, sizeof(buf), "%lu", (unsigned long)Stats.m_WaterTris);
170 return buf;
171
172 case Row_ModelTris:
173 if (col == 0)
174 return "# model tris";
175 sprintf_s(buf, sizeof(buf), "%lu", (unsigned long)Stats.m_ModelTris);
176 return buf;
177
178 case Row_BlendSplats:
179 if (col == 0)
180 return "# blend splats";
181 sprintf_s(buf, sizeof(buf), "%lu", (unsigned long)Stats.m_BlendSplats);
182 return buf;
183
184 case Row_Particles:
185 if (col == 0)
186 return "# particles";
187 sprintf_s(buf, sizeof(buf), "%lu", (unsigned long)Stats.m_Particles);
188 return buf;
189
190 case Row_VBReserved:
191 if (col == 0)
192 return "VB bytes reserved";
193 sprintf_s(buf, sizeof(buf), "%lu", (unsigned long)g_VBMan.GetBytesReserved());
194 return buf;
195
196 case Row_VBAllocated:
197 if (col == 0)
198 return "VB bytes allocated";
199 sprintf_s(buf, sizeof(buf), "%lu", (unsigned long)g_VBMan.GetBytesAllocated());
200 return buf;
201
202 default:
203 return "???";
204 }
205}
206
207AbstractProfileTable* CRendererStatsTable::GetChild(size_t UNUSED(row))
208{
209 return 0;
210}
211
212
213///////////////////////////////////////////////////////////////////////////////////
214// CRenderer implementation
215
216/**
217 * Struct CRendererInternals: Truly hide data that is supposed to be hidden
218 * in this structure so it won't even appear in header files.
219 */
220struct CRendererInternals
221{
222 NONCOPYABLE(CRendererInternals);
223public:
224 /// true if CRenderer::Open has been called
225 bool IsOpen;
226
227 /// true if shaders need to be reloaded
228 bool ShadersDirty;
229
230 /// Table to display renderer stats in-game via profile system
231 CRendererStatsTable profileTable;
232
233 /// Shader manager
234 CShaderManager shaderManager;
235
236 /// Water manager
237 WaterManager waterManager;
238
239 /// Sky manager
240 SkyManager skyManager;
241
242 /// Texture manager
243 CTextureManager textureManager;
244
245 /// Terrain renderer
246 TerrainRenderer* terrainRenderer;
247
248 /// Overlay renderer
249 OverlayRenderer overlayRenderer;
250
251 /// Particle manager
252 CParticleManager particleManager;
253
254 /// Particle renderer
255 ParticleRenderer particleRenderer;
256
257 /// Shadow map
258 ShadowMap* shadow;
259
260 /// Various model renderers
261 struct Models {
262 // The following model renderers are aliases for the appropriate real_*
263 // model renderers (depending on hardware availability and current settings)
264 // and must be used for actual model submission and rendering
265 ModelRenderer* Normal;
266 ModelRenderer* NormalInstancing;
267 ModelRenderer* Player;
268 ModelRenderer* PlayerInstancing;
269 ModelRenderer* Transp;
270
271 // "Palette" of available ModelRenderers. Do not use these directly for
272 // rendering and submission; use the aliases above instead.
273 ModelRenderer* pal_NormalFF;
274 ModelRenderer* pal_PlayerFF;
275 ModelRenderer* pal_TranspFF;
276 ModelRenderer* pal_TranspSortAll;
277
278 ModelRenderer* pal_NormalShader;
279 ModelRenderer* pal_NormalInstancingShader;
280 ModelRenderer* pal_PlayerShader;
281 ModelRenderer* pal_PlayerInstancingShader;
282 ModelRenderer* pal_TranspShader;
283
284 ModelVertexRendererPtr VertexFF;
285 ModelVertexRendererPtr VertexPolygonSort;
286 ModelVertexRendererPtr VertexRendererShader;
287 ModelVertexRendererPtr VertexInstancingShader;
288
289 // generic RenderModifiers that are supposed to be used directly
290 RenderModifierPtr ModWireframe;
291 RenderModifierPtr ModSolidColor;
292 RenderModifierPtr ModSolidPlayerColor;
293 RenderModifierPtr ModTransparentDepthShadow;
294
295 // RenderModifiers that are selected from the palette below
296 RenderModifierPtr ModNormal;
297 RenderModifierPtr ModNormalInstancing;
298 RenderModifierPtr ModPlayer;
299 RenderModifierPtr ModPlayerInstancing;
300 RenderModifierPtr ModSolid;
301 RenderModifierPtr ModSolidInstancing;
302 RenderModifierPtr ModSolidPlayer;
303 RenderModifierPtr ModSolidPlayerInstancing;
304 RenderModifierPtr ModTransparent;
305 RenderModifierPtr ModTransparentOpaque;
306 RenderModifierPtr ModTransparentBlend;
307
308 // Palette of available RenderModifiers
309 RenderModifierPtr ModPlainUnlit;
310 RenderModifierPtr ModPlayerUnlit;
311 RenderModifierPtr ModTransparentUnlit;
312 RenderModifierPtr ModTransparentOpaqueUnlit;
313 RenderModifierPtr ModTransparentBlendUnlit;
314
315 RenderModifierPtr ModShaderSolidColor;
316 RenderModifierPtr ModShaderSolidColorInstancing;
317 RenderModifierPtr ModShaderSolidPlayerColor;
318 RenderModifierPtr ModShaderSolidPlayerColorInstancing;
319 RenderModifierPtr ModShaderSolidTex;
320 LitRenderModifierPtr ModShaderNormal;
321 LitRenderModifierPtr ModShaderNormalInstancing;
322 LitRenderModifierPtr ModShaderPlayer;
323 LitRenderModifierPtr ModShaderPlayerInstancing;
324 LitRenderModifierPtr ModShaderTransparent;
325 LitRenderModifierPtr ModShaderTransparentOpaque;
326 LitRenderModifierPtr ModShaderTransparentBlend;
327 RenderModifierPtr ModShaderTransparentShadow;
328 } Model;
329
330
331 CRendererInternals() :
332 IsOpen(false), ShadersDirty(true), profileTable(g_Renderer.m_Stats), textureManager(g_VFS, false, false)
333 {
334 terrainRenderer = new TerrainRenderer();
335 shadow = new ShadowMap();
336
337 Model.pal_NormalFF = 0;
338 Model.pal_PlayerFF = 0;
339 Model.pal_TranspFF = 0;
340 Model.pal_TranspSortAll = 0;
341
342 Model.pal_NormalShader = 0;
343 Model.pal_NormalInstancingShader = 0;
344 Model.pal_PlayerShader = 0;
345 Model.pal_PlayerInstancingShader = 0;
346 Model.pal_TranspShader = 0;
347
348 Model.Normal = 0;
349 Model.NormalInstancing = 0;
350 Model.Player = 0;
351 Model.PlayerInstancing = 0;
352 Model.Transp = 0;
353 }
354
355 ~CRendererInternals()
356 {
357 delete shadow;
358 delete terrainRenderer;
359 }
360
361 /**
362 * Load the OpenGL projection and modelview matrices and the viewport according
363 * to the given camera.
364 */
365 void SetOpenGLCamera(const CCamera& camera)
366 {
367 CMatrix3D view;
368 camera.m_Orientation.GetInverse(view);
369 const CMatrix3D& proj = camera.GetProjection();
370
371 glMatrixMode(GL_PROJECTION);
372 glLoadMatrixf(&proj._11);
373
374 glMatrixMode(GL_MODELVIEW);
375 glLoadMatrixf(&view._11);
376
377 const SViewPort &vp = camera.GetViewPort();
378 glViewport((GLint)vp.m_X,(GLint)vp.m_Y,(GLsizei)vp.m_Width,(GLsizei)vp.m_Height);
379 }
380
381 /**
382 * Renders all non-transparent models with the given modifiers.
383 */
384 void CallModelRenderers(
385 const RenderModifierPtr& modNormal, const RenderModifierPtr& modNormalInstancing,
386 const RenderModifierPtr& modPlayer, const RenderModifierPtr& modPlayerInstancing,
387 int flags)
388 {
389 Model.Normal->Render(modNormal, flags);
390 if (Model.Normal != Model.NormalInstancing)
391 Model.NormalInstancing->Render(modNormalInstancing, flags);
392
393 Model.Player->Render(modPlayer, flags);
394 if (Model.Player != Model.PlayerInstancing)
395 Model.PlayerInstancing->Render(modPlayerInstancing, flags);
396 }
397
398 /**
399 * Filters all non-transparent models with the given modifiers.
400 */
401 void FilterModels(CModelFilter& filter, int passed, int flags = 0)
402 {
403 Model.Normal->Filter(filter, passed, flags);
404 if (Model.Normal != Model.NormalInstancing)
405 Model.NormalInstancing->Filter(filter, passed, flags);
406
407 Model.Player->Filter(filter, passed, flags);
408 if (Model.Player != Model.PlayerInstancing)
409 Model.PlayerInstancing->Filter(filter, passed, flags);
410 }
411};
412
413///////////////////////////////////////////////////////////////////////////////////
414// CRenderer constructor
415CRenderer::CRenderer()
416{
417 m = new CRendererInternals;
418 m_WaterManager = &m->waterManager;
419 m_SkyManager = &m->skyManager;
420
421 g_ProfileViewer.AddRootTable(&m->profileTable);
422
423 m_Width=0;
424 m_Height=0;
425 m_TerrainRenderMode=SOLID;
426 m_ModelRenderMode=SOLID;
427 m_ClearColor[0]=m_ClearColor[1]=m_ClearColor[2]=m_ClearColor[3]=0;
428
429 m_SortAllTransparent = false;
430 m_DisplayFrustum = false;
431 m_DisableCopyShadow = false;
432 m_DisplayTerrainPriorities = false;
433 m_FastPlayerColor = true;
434 m_SkipSubmit = false;
435
436 m_Options.m_NoVBO = false;
437 m_Options.m_RenderPath = RP_DEFAULT;
438 m_Options.m_FancyWater = false;
439 m_Options.m_Shadows = false;
440 m_Options.m_ShadowAlphaFix = true;
441 m_Options.m_ARBProgramShadow = true;
442 m_Options.m_ShadowPCF = false;
443
444 m_ShadowZBias = 0.02f;
445 m_ShadowMapSize = 0;
446
447 m_LightEnv = NULL;
448
449 m_CurrentScene = NULL;
450
451 m_hCompositeAlphaMap = 0;
452
453 m_Stats.Reset();
454
455 AddLocalProperty(L"fancyWater", &m_Options.m_FancyWater, false);
456 AddLocalProperty(L"horizonHeight", &m->skyManager.m_HorizonHeight, false);
457 AddLocalProperty(L"waterMurkiness", &m->waterManager.m_Murkiness, false);
458 AddLocalProperty(L"waterReflTintStrength", &m->waterManager.m_ReflectionTintStrength, false);
459 AddLocalProperty(L"waterRepeatPeriod", &m->waterManager.m_RepeatPeriod, false);
460 AddLocalProperty(L"waterShininess", &m->waterManager.m_Shininess, false);
461 AddLocalProperty(L"waterSpecularStrength", &m->waterManager.m_SpecularStrength, false);
462 AddLocalProperty(L"waterWaviness", &m->waterManager.m_Waviness, false);
463
464 RegisterFileReloadFunc(ReloadChangedFileCB, this);
465}
466
467///////////////////////////////////////////////////////////////////////////////////
468// CRenderer destructor
469CRenderer::~CRenderer()
470{
471 UnregisterFileReloadFunc(ReloadChangedFileCB, this);
472
473 // model rendering
474 delete m->Model.pal_NormalFF;
475 delete m->Model.pal_PlayerFF;
476 delete m->Model.pal_TranspFF;
477 delete m->Model.pal_TranspSortAll;
478
479 delete m->Model.pal_NormalShader;
480 delete m->Model.pal_NormalInstancingShader;
481 delete m->Model.pal_PlayerShader;
482 delete m->Model.pal_PlayerInstancingShader;
483 delete m->Model.pal_TranspShader;
484
485 // we no longer UnloadAlphaMaps / UnloadWaterTextures here -
486 // that is the responsibility of the module that asked for
487 // them to be loaded (i.e. CGameView).
488 delete m;
489}
490
491
492///////////////////////////////////////////////////////////////////////////////////
493// EnumCaps: build card cap bits
494void CRenderer::EnumCaps()
495{
496 // assume support for nothing
497 m_Caps.m_VBO = false;
498 m_Caps.m_ARBProgram = false;
499 m_Caps.m_ARBProgramShadow = false;
500 m_Caps.m_VertexShader = false;
501 m_Caps.m_FragmentShader = false;
502 m_Caps.m_Shadows = false;
503
504 // now start querying extensions
505 if (!m_Options.m_NoVBO) {
506 if (ogl_HaveExtension("GL_ARB_vertex_buffer_object")) {
507 m_Caps.m_VBO=true;
508 }
509 }
510
511 if (0 == ogl_HaveExtensions(0, "GL_ARB_vertex_program", "GL_ARB_fragment_program", NULL))
512 {
513 m_Caps.m_ARBProgram = true;
514 if (ogl_HaveExtension("GL_ARB_fragment_program_shadow"))
515 m_Caps.m_ARBProgramShadow = true;
516 }
517
518 if (0 == ogl_HaveExtensions(0, "GL_ARB_shader_objects", "GL_ARB_shading_language_100", NULL))
519 {
520 if (ogl_HaveExtension("GL_ARB_vertex_shader"))
521 m_Caps.m_VertexShader = true;
522 if (ogl_HaveExtension("GL_ARB_fragment_shader"))
523 m_Caps.m_FragmentShader = true;
524 }
525
526 if (0 == ogl_HaveExtensions(0, "GL_ARB_shadow", "GL_ARB_depth_texture", "GL_EXT_framebuffer_object", NULL))
527 {
528 if (ogl_max_tex_units >= 4)
529 m_Caps.m_Shadows = true;
530 }
531}
532
533void CRenderer::ReloadShaders()
534{
535 ENSURE(m->IsOpen);
536
537 typedef std::map<CStr, CStr> Defines;
538
539 Defines defNull;
540
541 Defines defBasic;
542 if (m_Options.m_Shadows)
543 {
544 defBasic["USE_SHADOW"] = "1";
545 if (m_Caps.m_ARBProgramShadow && m_Options.m_ARBProgramShadow)
546 defBasic["USE_FP_SHADOW"] = "1";
547 if (m_Options.m_ShadowPCF)
548 defBasic["USE_SHADOW_PCF"] = "1";
549 }
550
551 if (m_LightEnv)
552 defBasic["LIGHTING_MODEL_" + m_LightEnv->GetLightingModel()] = "1";
553
554 Defines defColored = defBasic;
555 defColored["USE_OBJECTCOLOR"] = "1";
556
557 Defines defTransparent = defBasic;
558 defTransparent["USE_TRANSPARENT"] = "1";
559
560 // TODO: it'd be nicer to load this technique from an XML file or something
561 CShaderPass passTransparentOpaque(m->shaderManager.LoadProgram("model_common", defTransparent));
562 passTransparentOpaque.AlphaFunc(GL_GREATER, 0.9375f);
563 passTransparentOpaque.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
564 CShaderTechnique techTransparentOpaque(passTransparentOpaque);
565
566 CShaderPass passTransparentBlend(m->shaderManager.LoadProgram("model_common", defTransparent));
567 passTransparentBlend.AlphaFunc(GL_GREATER, 0.0f);
568 passTransparentBlend.DepthFunc(GL_LESS);
569 passTransparentBlend.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
570 passTransparentBlend.DepthMask(0);
571 CShaderTechnique techTransparentBlend(passTransparentBlend);
572
573 CShaderTechnique techTransparent(passTransparentOpaque);
574 techTransparent.AddPass(passTransparentBlend);
575
576 CShaderPass passTransparentShadow(m->shaderManager.LoadProgram("solid_tex", defBasic));
577 passTransparentShadow.AlphaFunc(GL_GREATER, 0.4f);
578 CShaderTechnique techTransparentShadow(passTransparentShadow);
579
580 m->Model.ModShaderSolidColor = RenderModifierPtr(new ShaderRenderModifier(CShaderTechnique(m->shaderManager.LoadProgram(
581 "solid", defNull))));
582 m->Model.ModShaderSolidColorInstancing = RenderModifierPtr(new ShaderRenderModifier(CShaderTechnique(m->shaderManager.LoadProgram(
583 "solid_instancing", defNull))));
584
585 m->Model.ModShaderSolidPlayerColor = RenderModifierPtr(new ShaderRenderModifier(CShaderTechnique(m->shaderManager.LoadProgram(
586 "solid_player", defNull))));
587 m->Model.ModShaderSolidPlayerColorInstancing = RenderModifierPtr(new ShaderRenderModifier(CShaderTechnique(m->shaderManager.LoadProgram(
588 "solid_player_instancing", defNull))));
589
590 m->Model.ModShaderSolidTex = RenderModifierPtr(new ShaderRenderModifier(CShaderTechnique(m->shaderManager.LoadProgram(
591 "solid_tex", defNull))));
592
593 m->Model.ModShaderNormal = LitRenderModifierPtr(new ShaderRenderModifier(CShaderTechnique(m->shaderManager.LoadProgram(
594 "model_common", defBasic))));
595 m->Model.ModShaderNormalInstancing = LitRenderModifierPtr(new ShaderRenderModifier(CShaderTechnique(m->shaderManager.LoadProgram(
596 "model_common_instancing", defBasic))));
597
598 m->Model.ModShaderPlayer = LitRenderModifierPtr(new ShaderRenderModifier(CShaderTechnique(m->shaderManager.LoadProgram(
599 "model_common", defColored))));
600 m->Model.ModShaderPlayerInstancing = LitRenderModifierPtr(new ShaderRenderModifier(CShaderTechnique(m->shaderManager.LoadProgram(
601 "model_common_instancing", defColored))));
602
603 m->Model.ModShaderTransparent = LitRenderModifierPtr(new ShaderRenderModifier(
604 techTransparent));
605 m->Model.ModShaderTransparentOpaque = LitRenderModifierPtr(new ShaderRenderModifier(
606 techTransparentOpaque));
607 m->Model.ModShaderTransparentBlend = LitRenderModifierPtr(new ShaderRenderModifier(
608 techTransparentBlend));
609 m->Model.ModShaderTransparentShadow = LitRenderModifierPtr(new ShaderRenderModifier(
610 techTransparentShadow));
611
612 m->ShadersDirty = false;
613}
614
615bool CRenderer::Open(int width, int height)
616{
617 m->IsOpen = true;
618
619 // Must query card capabilities before creating renderers that depend
620 // on card capabilities.
621 EnumCaps();
622
623 // model rendering
624 m->Model.VertexFF = ModelVertexRendererPtr(new FixedFunctionModelRenderer);
625 m->Model.VertexPolygonSort = ModelVertexRendererPtr(new PolygonSortModelRenderer);
626 m->Model.VertexRendererShader = ModelVertexRendererPtr(new ShaderModelRenderer);
627 m->Model.VertexInstancingShader = ModelVertexRendererPtr(new InstancingModelRenderer);
628
629 m->Model.pal_NormalFF = new BatchModelRenderer(m->Model.VertexFF);
630 m->Model.pal_PlayerFF = new BatchModelRenderer(m->Model.VertexFF);
631 m->Model.pal_TranspFF = new SortModelRenderer(m->Model.VertexFF);
632
633 m->Model.pal_TranspSortAll = new SortModelRenderer(m->Model.VertexPolygonSort);
634
635 m->Model.pal_NormalShader = new BatchModelRenderer(m->Model.VertexRendererShader);
636 m->Model.pal_NormalInstancingShader = new BatchModelRenderer(m->Model.VertexInstancingShader);
637 m->Model.pal_PlayerShader = new BatchModelRenderer(m->Model.VertexRendererShader);
638 m->Model.pal_PlayerInstancingShader = new BatchModelRenderer(m->Model.VertexInstancingShader);
639 m->Model.pal_TranspShader = new SortModelRenderer(m->Model.VertexRendererShader);
640
641 m->Model.ModWireframe = RenderModifierPtr(new WireframeRenderModifier);
642 m->Model.ModPlainUnlit = RenderModifierPtr(new PlainRenderModifier);
643 SetFastPlayerColor(true);
644 m->Model.ModSolidColor = RenderModifierPtr(new SolidColorRenderModifier);
645 m->Model.ModSolidPlayerColor = RenderModifierPtr(new SolidPlayerColorRender);
646 m->Model.ModTransparentUnlit = RenderModifierPtr(new TransparentRenderModifier);
647 m->Model.ModTransparentOpaqueUnlit = RenderModifierPtr(new TransparentOpaqueRenderModifier);
648 m->Model.ModTransparentBlendUnlit = RenderModifierPtr(new TransparentBlendRenderModifier);
649 m->Model.ModTransparentDepthShadow = RenderModifierPtr(new TransparentDepthShadowModifier);
650
651 // Dimensions
652 m_Width = width;
653 m_Height = height;
654
655 // set packing parameters
656 glPixelStorei(GL_PACK_ALIGNMENT,1);
657 glPixelStorei(GL_UNPACK_ALIGNMENT,1);
658
659 // setup default state
660 glDepthFunc(GL_LEQUAL);
661 glEnable(GL_DEPTH_TEST);
662 glCullFace(GL_BACK);
663 glFrontFace(GL_CCW);
664 glEnable(GL_CULL_FACE);
665
666 GLint bits;
667 glGetIntegerv(GL_DEPTH_BITS,&bits);
668 LOGMESSAGE(L"CRenderer::Open: depth bits %d",bits);
669 glGetIntegerv(GL_STENCIL_BITS,&bits);
670 LOGMESSAGE(L"CRenderer::Open: stencil bits %d",bits);
671 glGetIntegerv(GL_ALPHA_BITS,&bits);
672 LOGMESSAGE(L"CRenderer::Open: alpha bits %d",bits);
673
674 // Validate the currently selected render path
675 SetRenderPath(m_Options.m_RenderPath);
676
677 return true;
678}
679
680// resize renderer view
681void CRenderer::Resize(int width,int height)
682{
683 // need to recreate the shadow map object to resize the shadow texture
684 m->shadow->RecreateTexture();
685
686 m_Width = width;
687 m_Height = height;
688}
689
690//////////////////////////////////////////////////////////////////////////////////////////
691// SetOptionBool: set boolean renderer option
692void CRenderer::SetOptionBool(enum Option opt,bool value)
693{
694 switch (opt) {
695 case OPT_NOVBO:
696 m_Options.m_NoVBO=value;
697 break;
698 case OPT_SHADOWS:
699 m_Options.m_Shadows=value;
700 MakeShadersDirty();
701 break;
702 case OPT_FANCYWATER:
703 m_Options.m_FancyWater=value;
704 break;
705 case OPT_SHADOWPCF:
706 m_Options.m_ShadowPCF=value;
707 break;
708 default:
709 debug_warn(L"CRenderer::SetOptionBool: unknown option");
710 break;
711 }
712}
713
714//////////////////////////////////////////////////////////////////////////////////////////
715// GetOptionBool: get boolean renderer option
716bool CRenderer::GetOptionBool(enum Option opt) const
717{
718 switch (opt) {
719 case OPT_NOVBO:
720 return m_Options.m_NoVBO;
721 case OPT_SHADOWS:
722 return m_Options.m_Shadows;
723 case OPT_FANCYWATER:
724 return m_Options.m_FancyWater;
725 case OPT_SHADOWPCF:
726 return m_Options.m_ShadowPCF;
727 default:
728 debug_warn(L"CRenderer::GetOptionBool: unknown option");
729 break;
730 }
731
732 return false;
733}
734
735void CRenderer::SetOptionFloat(enum Option opt, float val)
736{
737 switch(opt)
738 {
739 case OPT_LODBIAS:
740 m_Options.m_LodBias = val;
741 break;
742 default:
743 debug_warn(L"CRenderer::SetOptionFloat: unknown option");
744 break;
745 }
746}
747
748//////////////////////////////////////////////////////////////////////////////////////////
749// SetRenderPath: Select the preferred render path.
750// This may only be called before Open(), because the layout of vertex arrays and other
751// data may depend on the chosen render path.
752void CRenderer::SetRenderPath(RenderPath rp)
753{
754 if (!m->IsOpen)
755 {
756 // Delay until Open() is called.
757 m_Options.m_RenderPath = rp;
758 return;
759 }
760
761 // Renderer has been opened, so validate the selected renderpath
762 if (rp == RP_DEFAULT)
763 {
764 if (m_Caps.m_ARBProgram)
765 rp = RP_SHADER;
766 else
767 rp = RP_FIXED;
768 }
769
770 if (rp == RP_SHADER)
771 {
772 if (!m_Caps.m_ARBProgram)
773 {
774 LOGWARNING(L"Falling back to fixed function\n");
775 rp = RP_FIXED;
776 }
777 }
778
779 m_Options.m_RenderPath = rp;
780
781 // We might need to regenerate some render data after changing path
782 if (g_Game)
783 g_Game->GetWorld()->GetTerrain()->MakeDirty(RENDERDATA_UPDATE_COLOR);
784}
785
786
787CStr CRenderer::GetRenderPathName(RenderPath rp)
788{
789 switch(rp) {
790 case RP_DEFAULT: return "default";
791 case RP_FIXED: return "fixed";
792 case RP_SHADER: return "shader";
793 default: return "(invalid)";
794 }
795}
796
797CRenderer::RenderPath CRenderer::GetRenderPathByName(const CStr& name)
798{
799 if (name == "fixed")
800 return RP_FIXED;
801 if (name == "shader")
802 return RP_SHADER;
803 if (name == "default")
804 return RP_DEFAULT;
805
806 LOGWARNING(L"Unknown render path name '%hs', assuming 'default'", name.c_str());
807 return RP_DEFAULT;
808}
809
810
811//////////////////////////////////////////////////////////////////////////////////////////
812// SetFastPlayerColor
813void CRenderer::SetFastPlayerColor(bool fast)
814{
815 m_FastPlayerColor = fast;
816
817 if (m_FastPlayerColor)
818 {
819 if (!FastPlayerColorRender::IsAvailable())
820 {
821 LOGWARNING(L"Falling back to slower player color rendering.");
822 m_FastPlayerColor = false;
823 }
824 }
825
826 if (m_FastPlayerColor)
827 m->Model.ModPlayerUnlit = RenderModifierPtr(new FastPlayerColorRender);
828 else
829 m->Model.ModPlayerUnlit = RenderModifierPtr(new SlowPlayerColorRender);
830}
831
832//////////////////////////////////////////////////////////////////////////////////////////
833// BeginFrame: signal frame start
834void CRenderer::BeginFrame()
835{
836 PROFILE("begin frame");
837
838 // zero out all the per-frame stats
839 m_Stats.Reset();
840
841 // choose model renderers for this frame
842
843 if (m_Options.m_RenderPath == RP_SHADER)
844 {
845 if (m->ShadersDirty)
846 ReloadShaders();
847
848 m->Model.ModShaderNormal->SetShadowMap(m->shadow);
849 m->Model.ModShaderNormal->SetLightEnv(m_LightEnv);
850
851 m->Model.ModShaderNormalInstancing->SetShadowMap(m->shadow);
852 m->Model.ModShaderNormalInstancing->SetLightEnv(m_LightEnv);
853
854 m->Model.ModShaderPlayer->SetShadowMap(m->shadow);
855 m->Model.ModShaderPlayer->SetLightEnv(m_LightEnv);
856
857 m->Model.ModShaderPlayerInstancing->SetShadowMap(m->shadow);
858 m->Model.ModShaderPlayerInstancing->SetLightEnv(m_LightEnv);
859
860 m->Model.ModShaderTransparent->SetShadowMap(m->shadow);
861 m->Model.ModShaderTransparent->SetLightEnv(m_LightEnv);
862
863 m->Model.ModShaderTransparentOpaque->SetShadowMap(m->shadow);
864 m->Model.ModShaderTransparentOpaque->SetLightEnv(m_LightEnv);
865
866 m->Model.ModShaderTransparentBlend->SetShadowMap(m->shadow);
867 m->Model.ModShaderTransparentBlend->SetLightEnv(m_LightEnv);
868
869 m->Model.ModNormal = m->Model.ModShaderNormal;
870 m->Model.ModNormalInstancing = m->Model.ModShaderNormalInstancing;
871 m->Model.ModPlayer = m->Model.ModShaderPlayer;
872 m->Model.ModPlayerInstancing = m->Model.ModShaderPlayerInstancing;
873 m->Model.ModSolid = m->Model.ModShaderSolidColor;
874 m->Model.ModSolidInstancing = m->Model.ModShaderSolidColorInstancing;
875 m->Model.ModSolidPlayer = m->Model.ModShaderSolidPlayerColor;
876 m->Model.ModSolidPlayerInstancing = m->Model.ModShaderSolidPlayerColorInstancing;
877 m->Model.ModTransparent = m->Model.ModShaderTransparent;
878 m->Model.ModTransparentOpaque = m->Model.ModShaderTransparentOpaque;
879 m->Model.ModTransparentBlend = m->Model.ModShaderTransparentBlend;
880
881 m->Model.Normal = m->Model.pal_NormalShader;
882 m->Model.NormalInstancing = m->Model.pal_NormalInstancingShader;
883
884 m->Model.Player = m->Model.pal_PlayerShader;
885 m->Model.PlayerInstancing = m->Model.pal_PlayerInstancingShader;
886
887 m->Model.Transp = m->Model.pal_TranspShader;
888 }
889 else
890 {
891 m->Model.ModNormal = m->Model.ModPlainUnlit;
892 m->Model.ModNormalInstancing = m->Model.ModPlainUnlit;
893 m->Model.ModPlayer = m->Model.ModPlayerUnlit;
894 m->Model.ModPlayerInstancing = m->Model.ModPlayerUnlit;
895 m->Model.ModTransparent = m->Model.ModTransparentUnlit;
896 m->Model.ModTransparentOpaque = m->Model.ModTransparentOpaqueUnlit;
897 m->Model.ModTransparentBlend = m->Model.ModTransparentBlendUnlit;
898
899 m->Model.NormalInstancing = m->Model.pal_NormalFF;
900 m->Model.Normal = m->Model.pal_NormalFF;
901
902 m->Model.PlayerInstancing = m->Model.pal_PlayerFF;
903 m->Model.Player = m->Model.pal_PlayerFF;
904
905 m->Model.ModSolid = m->Model.ModSolidColor;
906 m->Model.ModSolidInstancing = m->Model.ModSolidColor;
907 m->Model.ModSolidPlayer = m->Model.ModSolidPlayerColor;
908 m->Model.ModSolidPlayerInstancing = m->Model.ModSolidPlayerColor;
909
910 if (m_SortAllTransparent)
911 m->Model.Transp = m->Model.pal_TranspSortAll;
912 else
913 m->Model.Transp = m->Model.pal_TranspFF;
914 }
915}
916
917//////////////////////////////////////////////////////////////////////////////////////////
918// SetClearColor: set color used to clear screen in BeginFrame()
919void CRenderer::SetClearColor(SColor4ub color)
920{
921 m_ClearColor[0] = float(color.R) / 255.0f;
922 m_ClearColor[1] = float(color.G) / 255.0f;
923 m_ClearColor[2] = float(color.B) / 255.0f;
924 m_ClearColor[3] = float(color.A) / 255.0f;
925}
926
927void CRenderer::RenderShadowMap()
928{
929 PROFILE("render shadow map");
930
931 m->shadow->BeginRender();
932
933 float shadowTransp = m_LightEnv->GetTerrainShadowTransparency();
934 glColor3f(shadowTransp, shadowTransp, shadowTransp);
935
936 // Figure out transparent rendering strategy
937 RenderModifierPtr transparentShadows;
938 if (GetRenderPath() == RP_SHADER)
939 {
940 transparentShadows = m->Model.ModShaderTransparentShadow;
941 }
942 else
943 {
944 transparentShadows = m->Model.ModTransparentDepthShadow;
945 }
946
947
948 {
949 PROFILE("render patches");
950 m->terrainRenderer->RenderPatches();
951 }
952
953 {
954 PROFILE("render models");
955 m->CallModelRenderers(m->Model.ModSolid, m->Model.ModSolidInstancing,
956 m->Model.ModSolid, m->Model.ModSolidInstancing, MODELFLAG_CASTSHADOWS);
957 }
958
959 {
960 PROFILE("render transparent models");
961 // disable face-culling for two-sided models
962 glDisable(GL_CULL_FACE);
963 m->Model.Transp->Render(transparentShadows, MODELFLAG_CASTSHADOWS);
964 glEnable(GL_CULL_FACE);
965 }
966
967 glColor3f(1.0, 1.0, 1.0);
968
969 m->shadow->EndRender();
970}
971
972void CRenderer::RenderPatches(const CFrustum* frustum)
973{
974 PROFILE("render patches");
975
976 bool filtered = false;
977 if (frustum)
978 {
979 if (!m->terrainRenderer->CullPatches(frustum))
980 return;
981
982 filtered = true;
983 }
984
985 // switch on wireframe if we need it
986 if (m_TerrainRenderMode == WIREFRAME)
987 {
988 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
989 }
990
991 // render all the patches, including blend pass
992 if (GetRenderPath() == RP_SHADER)
993 m->terrainRenderer->RenderTerrainShader((m_Caps.m_Shadows && m_Options.m_Shadows) ? m->shadow : 0, filtered);
994 else
995 m->terrainRenderer->RenderTerrain(filtered);
996
997
998 if (m_TerrainRenderMode == WIREFRAME)
999 {
1000 // switch wireframe off again
1001 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
1002 }
1003 else if (m_TerrainRenderMode == EDGED_FACES)
1004 {
1005 // edged faces: need to make a second pass over the data:
1006 // first switch on wireframe
1007 glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);
1008
1009 // setup some renderstate ..
1010 glDisable(GL_TEXTURE_2D);
1011 glColor3f(0.5f, 0.5f, 1.0f);
1012 glLineWidth(2.0f);
1013
1014 // render tiles edges
1015 m->terrainRenderer->RenderPatches(filtered);
1016
1017 // set color for outline
1018 glColor3f(0, 0, 1);
1019 glLineWidth(4.0f);
1020
1021 // render outline of each patch
1022 m->terrainRenderer->RenderOutlines(filtered);
1023
1024 // .. and restore the renderstates
1025 glLineWidth(1.0f);
1026 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
1027 }
1028}
1029
1030class CModelCuller : public CModelFilter
1031{
1032public:
1033 CModelCuller(const CFrustum& frustum) : m_Frustum(frustum) { }
1034
1035 bool Filter(CModel *model)
1036 {
1037 return m_Frustum.IsBoxVisible(CVector3D(0, 0, 0), model->GetBoundsRec());
1038 }
1039
1040private:
1041 const CFrustum& m_Frustum;
1042};
1043
1044void CRenderer::RenderModels(const CFrustum* frustum)
1045{
1046 PROFILE("render models");
1047
1048 int flags = 0;
1049 if (frustum)
1050 {
1051 flags = MODELFLAG_FILTERED;
1052 CModelCuller culler(*frustum);
1053 m->FilterModels(culler, flags);
1054 }
1055
1056 if (m_ModelRenderMode == WIREFRAME)
1057 {
1058 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
1059 }
1060
1061 m->CallModelRenderers(m->Model.ModNormal, m->Model.ModNormalInstancing,
1062 m->Model.ModPlayer, m->Model.ModPlayerInstancing, flags);
1063
1064 if (m_ModelRenderMode == WIREFRAME)
1065 {
1066 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
1067 }
1068 else if (m_ModelRenderMode == EDGED_FACES)
1069 {
1070 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
1071 glDisable(GL_TEXTURE_2D);
1072 glColor3f(1.0f, 1.0f, 0.0f);
1073
1074 m->CallModelRenderers(m->Model.ModSolid, m->Model.ModSolidInstancing,
1075 m->Model.ModSolid, m->Model.ModSolidInstancing, flags);
1076
1077 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
1078 }
1079}
1080
1081void CRenderer::RenderTransparentModels(ETransparentMode transparentMode, const CFrustum* frustum)
1082{
1083 PROFILE("render transparent models");
1084
1085 int flags = 0;
1086 if (frustum)
1087 {
1088 flags = MODELFLAG_FILTERED;
1089 CModelCuller culler(*frustum);
1090 m->Model.Transp->Filter(culler, flags);
1091 }
1092
1093 // switch on wireframe if we need it
1094 if (m_ModelRenderMode == WIREFRAME)
1095 {
1096 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
1097 }
1098
1099 // disable face culling for two-sided models in sub-renders
1100 if (flags)
1101 glDisable(GL_CULL_FACE);
1102
1103 if (transparentMode == TRANSPARENT_OPAQUE)
1104 m->Model.Transp->Render(m->Model.ModTransparentOpaque, flags);
1105 else if (transparentMode == TRANSPARENT_BLEND)
1106 m->Model.Transp->Render(m->Model.ModTransparentBlend, flags);
1107 else
1108 m->Model.Transp->Render(m->Model.ModTransparent, flags);
1109
1110 if (flags)
1111 glEnable(GL_CULL_FACE);
1112
1113 if (m_ModelRenderMode == WIREFRAME)
1114 {
1115 // switch wireframe off again
1116 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
1117 }
1118 else if (m_ModelRenderMode == EDGED_FACES)
1119 {
1120 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
1121 glDisable(GL_TEXTURE_2D);
1122 glColor3f(1.0f, 0.0f, 0.0f);
1123
1124 m->Model.Transp->Render(m->Model.ModSolid, flags);
1125
1126 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
1127 }
1128}
1129
1130
1131///////////////////////////////////////////////////////////////////////////////////////////////////
1132// GetModelViewProjectionMatrix: save the current OpenGL model-view-projection matrix
1133CMatrix3D CRenderer::GetModelViewProjectionMatrix()
1134{
1135 CMatrix3D proj;
1136 CMatrix3D view;
1137
1138 glGetFloatv(GL_PROJECTION_MATRIX, &proj._11);
1139 glGetFloatv(GL_MODELVIEW_MATRIX, &view._11);
1140
1141 return proj*view;
1142}
1143
1144
1145
1146///////////////////////////////////////////////////////////////////////////////////////////////////
1147// SetObliqueFrustumClipping: change the near plane to the given clip plane (in world space)
1148// Based on code from Game Programming Gems 5, from http://www.terathon.com/code/oblique.html
1149// - worldPlane is a clip plane in world space (worldPlane.Dot(v) >= 0 for any vector v passing the clipping test)
1150void CRenderer::SetObliqueFrustumClipping(const CVector4D& worldPlane)
1151{
1152 float matrix[16];
1153 CVector4D q;
1154
1155 // First, we'll convert the given clip plane to camera space, then we'll
1156 // Get the view matrix and normal matrix (top 3x3 part of view matrix)
1157 CMatrix3D normalMatrix = m_ViewCamera.m_Orientation.GetTranspose();
1158 CVector4D camPlane = normalMatrix.Transform(worldPlane);
1159
1160 // Grab the current projection matrix from OpenGL
1161 glGetFloatv(GL_PROJECTION_MATRIX, matrix);
1162
1163 // Calculate the clip-space corner point opposite the clipping plane
1164 // as (sgn(camPlane.x), sgn(camPlane.y), 1, 1) and
1165 // transform it into camera space by multiplying it
1166 // by the inverse of the projection matrix
1167
1168 q.m_X = (sgn(camPlane.m_X) - matrix[8]/matrix[11]) / matrix[0];
1169 q.m_Y = (sgn(camPlane.m_Y) - matrix[9]/matrix[11]) / matrix[5];
1170 q.m_Z = 1.0f/matrix[11];
1171 q.m_W = (1.0f - matrix[10]/matrix[11]) / matrix[14];
1172
1173 // Calculate the scaled plane vector
1174 CVector4D c = camPlane * (2.0f * matrix[11] / camPlane.Dot(q));
1175
1176 // Replace the third row of the projection matrix
1177 matrix[2] = c.m_X;
1178 matrix[6] = c.m_Y;
1179 matrix[10] = c.m_Z - matrix[11];
1180 matrix[14] = c.m_W;
1181
1182 // Load it back into OpenGL
1183 glMatrixMode(GL_PROJECTION);
1184 glLoadMatrixf(matrix);
1185
1186 glMatrixMode(GL_MODELVIEW);
1187}
1188
1189///////////////////////////////////////////////////////////////////////////////////////////////////
1190// RenderReflections: render the water reflections to the reflection texture
1191SScreenRect CRenderer::RenderReflections(const CBound& scissor)
1192{
1193 PROFILE("render reflections");
1194
1195 WaterManager& wm = m->waterManager;
1196
1197 // Remember old camera
1198 CCamera normalCamera = m_ViewCamera;
1199
1200 // Temporarily change the camera to one that is reflected.
1201 // Also, for texturing purposes, make it render to a view port the size of the
1202 // water texture, stretch the image according to our aspect ratio so it covers
1203 // the whole screen despite being rendered into a square, and cover slightly more
1204 // of the view so we can see wavy reflections of slightly off-screen objects.
1205 m_ViewCamera.m_Orientation.Scale(1, -1, 1);
1206 m_ViewCamera.m_Orientation.Translate(0, 2*wm.m_WaterHeight, 0);
1207 m_ViewCamera.UpdateFrustum(scissor);
1208 m_ViewCamera.ClipFrustum(CVector4D(0, 1, 0, -wm.m_WaterHeight));
1209
1210 SViewPort vp;
1211 vp.m_Height = wm.m_ReflectionTextureSize;
1212 vp.m_Width = wm.m_ReflectionTextureSize;
1213 vp.m_X = 0;
1214 vp.m_Y = 0;
1215 m_ViewCamera.SetViewPort(vp);
1216 m_ViewCamera.SetProjection(CGameView::defaultNear, CGameView::defaultFar, CGameView::defaultFOV*1.05f); // Slightly higher than view FOV
1217 CMatrix3D scaleMat;
1218 scaleMat.SetScaling(m_Height/float(std::max(1, m_Width)), 1.0f, 1.0f);
1219 m_ViewCamera.m_ProjMat = scaleMat * m_ViewCamera.m_ProjMat;
1220
1221 m->SetOpenGLCamera(m_ViewCamera);
1222
1223 CVector4D camPlane(0, 1, 0, -wm.m_WaterHeight);
1224 SetObliqueFrustumClipping(camPlane);
1225
1226 // Save the model-view-projection matrix so the shaders can use it for projective texturing
1227 wm.m_ReflectionMatrix = GetModelViewProjectionMatrix();
1228
1229 SScreenRect screenScissor;
1230 screenScissor.x1 = (GLint)floor((scissor[0].X*0.5f+0.5f)*vp.m_Width);
1231 screenScissor.y1 = (GLint)floor((scissor[0].Y*0.5f+0.5f)*vp.m_Height);
1232 screenScissor.x2 = (GLint)ceil((scissor[1].X*0.5f+0.5f)*vp.m_Width);
1233 screenScissor.y2 = (GLint)ceil((scissor[1].Y*0.5f+0.5f)*vp.m_Height);
1234
1235 if (screenScissor.x1 < screenScissor.x2 && screenScissor.y1 < screenScissor.y2)
1236 {
1237 glEnable(GL_SCISSOR_TEST);
1238 glScissor(screenScissor.x1, screenScissor.y1, screenScissor.x2 - screenScissor.x1, screenScissor.y2 - screenScissor.y1);
1239
1240 glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
1241
1242 glFrontFace(GL_CW);
1243
1244 // Render sky, terrain and models
1245 m->skyManager.RenderSky();
1246 ogl_WarnIfError();
1247 RenderPatches(&m_ViewCamera.GetFrustum());
1248 ogl_WarnIfError();
1249 RenderModels(&m_ViewCamera.GetFrustum());
1250 ogl_WarnIfError();
1251 RenderTransparentModels(TRANSPARENT_BLEND, &m_ViewCamera.GetFrustum());
1252 ogl_WarnIfError();
1253
1254 glFrontFace(GL_CCW);
1255
1256 glDisable(GL_SCISSOR_TEST);
1257
1258 // Copy the image to a texture
1259 pglActiveTextureARB(GL_TEXTURE0_ARB);
1260 glEnable(GL_TEXTURE_2D);
1261 glBindTexture(GL_TEXTURE_2D, wm.m_ReflectionTexture);
1262 glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
1263 screenScissor.x1, screenScissor.y1,
1264 screenScissor.x1, screenScissor.y1,
1265 screenScissor.x2 - screenScissor.x1, screenScissor.y2 - screenScissor.y1);
1266 }
1267
1268 // Reset old camera
1269 m_ViewCamera = normalCamera;
1270 m->SetOpenGLCamera(m_ViewCamera);
1271
1272 return screenScissor;
1273}
1274
1275
1276///////////////////////////////////////////////////////////////////////////////////////////////////
1277// RenderRefractions: render the water refractions to the refraction texture
1278SScreenRect CRenderer::RenderRefractions(const CBound &scissor)
1279{
1280 PROFILE("render refractions");
1281
1282 WaterManager& wm = m->waterManager;
1283
1284 // Remember old camera
1285 CCamera normalCamera = m_ViewCamera;
1286
1287 // Temporarily change the camera to make it render to a view port the size of the
1288 // water texture, stretch the image according to our aspect ratio so it covers
1289 // the whole screen despite being rendered into a square, and cover slightly more
1290 // of the view so we can see wavy refractions of slightly off-screen objects.
1291 m_ViewCamera.UpdateFrustum(scissor);
1292 m_ViewCamera.ClipFrustum(CVector4D(0, -1, 0, wm.m_WaterHeight));
1293
1294 SViewPort vp;
1295 vp.m_Height = wm.m_RefractionTextureSize;
1296 vp.m_Width = wm.m_RefractionTextureSize;
1297 vp.m_X = 0;
1298 vp.m_Y = 0;
1299 m_ViewCamera.SetViewPort(vp);
1300 m_ViewCamera.SetProjection(CGameView::defaultNear, CGameView::defaultFar, CGameView::defaultFOV*1.05f); // Slightly higher than view FOV
1301 CMatrix3D scaleMat;
1302 scaleMat.SetScaling(m_Height/float(std::max(1, m_Width)), 1.0f, 1.0f);
1303 m_ViewCamera.m_ProjMat = scaleMat * m_ViewCamera.m_ProjMat;
1304
1305 m->SetOpenGLCamera(m_ViewCamera);
1306
1307 CVector4D camPlane(0, -1, 0, wm.m_WaterHeight);
1308 SetObliqueFrustumClipping(camPlane);
1309
1310 // Save the model-view-projection matrix so the shaders can use it for projective texturing
1311 wm.m_RefractionMatrix = GetModelViewProjectionMatrix();
1312
1313 SScreenRect screenScissor;
1314 screenScissor.x1 = (GLint)floor((scissor[0].X*0.5f+0.5f)*vp.m_Width);
1315 screenScissor.y1 = (GLint)floor((scissor[0].Y*0.5f+0.5f)*vp.m_Height);
1316 screenScissor.x2 = (GLint)ceil((scissor[1].X*0.5f+0.5f)*vp.m_Width);
1317 screenScissor.y2 = (GLint)ceil((scissor[1].Y*0.5f+0.5f)*vp.m_Height);
1318 if (screenScissor.x1 < screenScissor.x2 && screenScissor.y1 < screenScissor.y2)
1319 {
1320 glEnable(GL_SCISSOR_TEST);
1321 glScissor(screenScissor.x1, screenScissor.y1, screenScissor.x2 - screenScissor.x1, screenScissor.y2 - screenScissor.y1);
1322
1323 glClearColor(0.5f, 0.5f, 0.5f, 1.0f); // a neutral gray to blend in with shores
1324 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
1325
1326 // Render terrain and models
1327 RenderPatches(&m_ViewCamera.GetFrustum());
1328 ogl_WarnIfError();
1329 RenderModels(&m_ViewCamera.GetFrustum());
1330 ogl_WarnIfError();
1331 RenderTransparentModels(TRANSPARENT_BLEND, &m_ViewCamera.GetFrustum());
1332 ogl_WarnIfError();
1333
1334 glDisable(GL_SCISSOR_TEST);
1335
1336 // Copy the image to a texture
1337 pglActiveTextureARB(GL_TEXTURE0_ARB);
1338 glEnable(GL_TEXTURE_2D);
1339 glBindTexture(GL_TEXTURE_2D, wm.m_RefractionTexture);
1340 glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
1341 screenScissor.x1, screenScissor.y1,
1342 screenScissor.x1, screenScissor.y1,
1343 screenScissor.x2 - screenScissor.x1, screenScissor.y2 - screenScissor.y1);
1344 }
1345
1346 // Reset old camera
1347 m_ViewCamera = normalCamera;
1348 m->SetOpenGLCamera(m_ViewCamera);
1349
1350 return screenScissor;
1351}
1352
1353
1354void CRenderer::RenderSilhouettes()
1355{
1356 PROFILE("render silhouettes");
1357
1358 // Render silhouettes of units hidden behind terrain or occluders.
1359 // To avoid breaking the standard rendering of alpha-blended objects, this
1360 // has to be done in a separate pass.
1361 // First we render all occluders into depth, then render all units with
1362 // inverted depth test so any behind an occluder will get drawn in a constant
1363 // colour.
1364
1365 float silhouetteAlpha = 0.75f;
1366
1367 // Silhouette blending requires an almost-universally-supported extension;
1368 // fall back to non-blended if unavailable
1369 if (!ogl_HaveExtension("GL_EXT_blend_color"))
1370 silhouetteAlpha = 1.f;
1371
1372 glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
1373
1374 glColorMask(0, 0, 0, 0);
1375
1376 // Render occluders:
1377
1378 {
1379 PROFILE("render patches");
1380
1381 // To prevent units displaying silhouettes when parts of their model
1382 // protrude into the ground, only occlude with the back faces of the
1383 // terrain (so silhouettes will still display when behind hills)
1384 glCullFace(GL_FRONT);
1385 m->terrainRenderer->RenderPatches();
1386 glCullFace(GL_BACK);
1387 }
1388
1389 {
1390 PROFILE("render model occluders");
1391 m->CallModelRenderers(m->Model.ModSolid, m->Model.ModSolidInstancing,
1392 m->Model.ModSolid, m->Model.ModSolidInstancing, MODELFLAG_SILHOUETTE_OCCLUDER);
1393 }
1394
1395 {
1396 PROFILE("render transparent occluders");
1397 if (GetRenderPath() == RP_SHADER)
1398 {
1399 glEnable(GL_ALPHA_TEST);
1400 glAlphaFunc(GL_GREATER, 0.4f);
1401 m->Model.Transp->Render(m->Model.ModShaderSolidTex, MODELFLAG_SILHOUETTE_OCCLUDER);
1402 glDisable(GL_ALPHA_TEST);
1403 }
1404 else
1405 {
1406 // Reuse the depth shadow modifier to get alpha-tested rendering
1407 m->Model.Transp->Render(m->Model.ModTransparentDepthShadow, MODELFLAG_SILHOUETTE_OCCLUDER);
1408 }
1409 }
1410
1411 glDepthFunc(GL_GEQUAL);
1412 glColorMask(1, 1, 1, 1);
1413
1414 // Render more efficiently if alpha == 1
1415 if (silhouetteAlpha == 1.f)
1416 {
1417 // Ideally we'd render objects back-to-front so nearer silhouettes would
1418 // appear on top, but sorting has non-zero cost. So we'll keep the depth
1419 // write enabled, to do the opposite - far objects will consistently appear
1420 // on top.
1421 glDepthMask(0);
1422 }
1423 else
1424 {
1425 // Since we can't sort, we'll use the stencil buffer to ensure we only draw
1426 // a pixel once (using the colour of whatever model happens to be drawn first).
1427 glEnable(GL_BLEND);
1428 glBlendFunc(GL_CONSTANT_ALPHA, GL_ONE_MINUS_CONSTANT_ALPHA);
1429 pglBlendColorEXT(0, 0, 0, silhouetteAlpha);
1430
1431 glEnable(GL_STENCIL_TEST);
1432 glStencilFunc(GL_NOTEQUAL, 1, (GLuint)-1);
1433 glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
1434 }
1435
1436 // TODO: For performance, we probably ought to do a quick raycasting check
1437 // to see which units are likely blocked by occluders and not bother
1438 // rendering any of the others
1439
1440 {
1441 PROFILE("render models");
1442 m->CallModelRenderers(m->Model.ModSolidPlayer, m->Model.ModSolidPlayerInstancing,
1443 m->Model.ModSolidPlayer, m->Model.ModSolidPlayerInstancing, MODELFLAG_SILHOUETTE_DISPLAY);
1444 // (This won't render transparent objects with SILHOUETTE_DISPLAY - will
1445 // we have any units that need that?)
1446 }
1447
1448 // Restore state
1449 glDepthFunc(GL_LEQUAL);
1450 if (silhouetteAlpha == 1.f)
1451 {
1452 glDepthMask(1);
1453 }
1454 else
1455 {
1456 glDisable(GL_BLEND);
1457 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1458 pglBlendColorEXT(0, 0, 0, 0);
1459 glDisable(GL_STENCIL_TEST);
1460 }
1461}
1462
1463void CRenderer::RenderParticles()
1464{
1465 // Only supported in shader modes
1466 if (GetRenderPath() != RP_SHADER)
1467 return;
1468
1469 PROFILE("render particles");
1470
1471 m->particleRenderer.RenderParticles();
1472
1473 if (m_ModelRenderMode == EDGED_FACES)
1474 {
1475 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
1476
1477 glDisable(GL_TEXTURE_2D);
1478 glColor3f(0.0f, 0.5f, 0.0f);
1479
1480 m->particleRenderer.RenderParticles(true);
1481
1482 glDisable(GL_TEXTURE_2D);
1483 glColor3f(0.0f, 1.0f, 0.0f);
1484
1485 m->particleRenderer.RenderBounds();
1486
1487 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
1488 }
1489}
1490
1491///////////////////////////////////////////////////////////////////////////////////////////////////
1492// RenderSubmissions: force rendering of any batched objects
1493void CRenderer::RenderSubmissions()
1494{
1495 PROFILE("render submissions");
1496
1497 ogl_WarnIfError();
1498
1499 // Set the camera
1500 m->SetOpenGLCamera(m_ViewCamera);
1501
1502 // Prepare model renderers
1503 PROFILE_START("prepare models");
1504 m->Model.Normal->PrepareModels();
1505 m->Model.Player->PrepareModels();
1506 if (m->Model.Normal != m->Model.NormalInstancing)
1507 m->Model.NormalInstancing->PrepareModels();
1508 if (m->Model.Player != m->Model.PlayerInstancing)
1509 m->Model.PlayerInstancing->PrepareModels();
1510 m->Model.Transp->PrepareModels();
1511 PROFILE_END("prepare models");
1512
1513 PROFILE_START("prepare terrain");
1514 m->terrainRenderer->PrepareForRendering();
1515 PROFILE_END("prepare terrain");
1516
1517 PROFILE_START("prepare overlays");
1518 m->overlayRenderer.PrepareForRendering();
1519 PROFILE_END("prepare overlays");
1520
1521 PROFILE_START("prepare particles");
1522 m->particleRenderer.PrepareForRendering();
1523 PROFILE_END("prepare particles");
1524
1525 if (m_Caps.m_Shadows && m_Options.m_Shadows && GetRenderPath() == RP_SHADER)
1526 {
1527 RenderShadowMap();
1528 }
1529
1530 // clear buffers
1531 PROFILE_START("clear buffers");
1532 glClearColor(m_ClearColor[0],m_ClearColor[1],m_ClearColor[2],m_ClearColor[3]);
1533 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
1534 PROFILE_END("clear buffers");
1535
1536 ogl_WarnIfError();
1537
1538 CBound waterScissor;
1539 if (m_WaterManager->m_RenderWater)
1540 {
1541 waterScissor = m->terrainRenderer->ScissorWater(m_ViewCamera.GetViewProjection());
1542 if (waterScissor.GetVolume() > 0 && m_WaterManager->WillRenderFancyWater())
1543 {
1544 SScreenRect reflectionScissor = RenderReflections(waterScissor);
1545 SScreenRect refractionScissor = RenderRefractions(waterScissor);
1546 SScreenRect dirty;
1547 dirty.x1 = std::min(reflectionScissor.x1, refractionScissor.x1);
1548 dirty.y1 = std::min(reflectionScissor.y1, refractionScissor.y1);
1549 dirty.x2 = std::max(reflectionScissor.x2, refractionScissor.x2);
1550 dirty.y2 = std::max(reflectionScissor.y2, refractionScissor.y2);
1551 if (dirty.x1 < dirty.x2 && dirty.y1 < dirty.y2)
1552 {
1553 glEnable(GL_SCISSOR_TEST);
1554 glScissor(dirty.x1, dirty.y1, dirty.x2 - dirty.x1, dirty.y2 - dirty.y1);
1555 glClearColor(m_ClearColor[0], m_ClearColor[1], m_ClearColor[2], m_ClearColor[3]);
1556 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
1557 glDisable(GL_SCISSOR_TEST);
1558 }
1559 }
1560 }
1561
1562 // render submitted patches and models
1563 RenderPatches();
1564 ogl_WarnIfError();
1565
1566 if (g_Game)
1567 {
1568// g_Game->GetWorld()->GetTerritoryManager()->RenderTerritories(); // TODO: implement in new sim system
1569 ogl_WarnIfError();
1570 }
1571
1572 // render debug-related terrain overlays
1573 TerrainOverlay::RenderOverlays();
1574 ogl_WarnIfError();
1575
1576 // render other debug-related overlays before water (so they can be displayed when underwater)
1577 PROFILE_START("render overlays");
1578 m->overlayRenderer.RenderOverlays();
1579 PROFILE_END("render overlays");
1580 ogl_WarnIfError();
1581
1582 RenderModels();
1583 ogl_WarnIfError();
1584
1585 // render water
1586 if (m_WaterManager->m_RenderWater && g_Game && waterScissor.GetVolume() > 0)
1587 {
1588 // render transparent stuff, but only the solid parts that can occlude block water
1589 RenderTransparentModels(TRANSPARENT_OPAQUE);
1590 ogl_WarnIfError();
1591
1592 m->terrainRenderer->RenderWater();
1593 ogl_WarnIfError();
1594
1595 // render transparent stuff again, but only the blended parts that overlap water
1596 RenderTransparentModels(TRANSPARENT_BLEND);
1597 ogl_WarnIfError();
1598 }
1599 else
1600 {
1601 // render transparent stuff, so it can overlap models/terrain
1602 RenderTransparentModels(TRANSPARENT);
1603 ogl_WarnIfError();
1604 }
1605
1606 // particles are transparent so render after water
1607 RenderParticles();
1608 ogl_WarnIfError();
1609
1610 RenderSilhouettes();
1611
1612 // Clean up texture blend mode so particles and other things render OK
1613 // (really this should be cleaned up by whoever set it)
1614 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
1615
1616 // render debug lines
1617 if (m_DisplayFrustum)
1618 {
1619 DisplayFrustum();
1620 m->shadow->RenderDebugDisplay();
1621 ogl_WarnIfError();
1622 }
1623
1624 // render overlays that should appear on top of all other objects
1625 PROFILE_START("render fg overlays");
1626 m->overlayRenderer.RenderForegroundOverlays(m_ViewCamera);
1627 PROFILE_END("render fg overlays");
1628 ogl_WarnIfError();
1629}
1630
1631///////////////////////////////////////////////////////////////////////////////////////////////////
1632// EndFrame: signal frame end
1633void CRenderer::EndFrame()
1634{
1635 PROFILE("end frame");
1636
1637 // empty lists
1638 m->terrainRenderer->EndFrame();
1639 m->overlayRenderer.EndFrame();
1640 m->particleRenderer.EndFrame();
1641
1642 // Finish model renderers
1643 m->Model.Normal->EndFrame();
1644 m->Model.Player->EndFrame();
1645 if (m->Model.Normal != m->Model.NormalInstancing)
1646 m->Model.NormalInstancing->EndFrame();
1647 if (m->Model.Player != m->Model.PlayerInstancing)
1648 m->Model.PlayerInstancing->EndFrame();
1649 m->Model.Transp->EndFrame();
1650
1651 ogl_tex_bind(0, 0);
1652
1653 if (glGetError())
1654 {
1655 ONCE(LOGERROR(L"CRenderer::EndFrame: GL errors occurred"));
1656 }
1657}
1658
1659
1660///////////////////////////////////////////////////////////////////////////////////////////////////
1661// DisplayFrustum: debug displays
1662// - white: cull camera frustum
1663// - red: bounds of shadow casting objects
1664void CRenderer::DisplayFrustum()
1665{
1666 glDepthMask(0);
1667 glDisable(GL_CULL_FACE);
1668
1669 glEnable(GL_BLEND);
1670 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1671 glColor4ub(255,255,255,64);
1672 m_CullCamera.Render(2);
1673 glDisable(GL_BLEND);
1674
1675 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
1676 glColor3ub(255,255,255);
1677 m_CullCamera.Render(2);
1678 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
1679
1680 glEnable(GL_CULL_FACE);
1681 glDepthMask(1);
1682}
1683
1684///////////////////////////////////////////////////////////////////////////////////////////////////
1685// Text overlay rendering
1686void CRenderer::RenderTextOverlays()
1687{
1688 PROFILE("render text overlays");
1689
1690 if (m_DisplayTerrainPriorities)
1691 m->terrainRenderer->RenderPriorities();
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.
1700void 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
1710void CRenderer::SetViewport(const SViewPort &vp)
1711{
1712 glViewport((GLint)vp.m_X,(GLint)vp.m_Y,(GLsizei)vp.m_Width,(GLsizei)vp.m_Height);
1713}
1714
1715void CRenderer::Submit(CPatch* patch)
1716{
1717 m->terrainRenderer->Submit(patch);
1718}
1719
1720void CRenderer::Submit(SOverlayLine* overlay)
1721{
1722 m->overlayRenderer.Submit(overlay);
1723}
1724
1725void CRenderer::Submit(SOverlaySprite* overlay)
1726{
1727 m->overlayRenderer.Submit(overlay);
1728}
1729
1730void CRenderer::Submit(CModelDecal* decal)
1731{
1732 m->terrainRenderer->Submit(decal);
1733}
1734
1735void CRenderer::Submit(CParticleEmitter* emitter)
1736{
1737 m->particleRenderer.Submit(emitter);
1738}
1739
1740void CRenderer::SubmitNonRecursive(CModel* model)
1741{
1742 if (model->GetFlags() & MODELFLAG_CASTSHADOWS) {
1743// PROFILE( "updating shadow bounds" );
1744 m->shadow->AddShadowedBound(model->GetBounds());
1745 }
1746
1747 // Tricky: The call to GetBounds() above can invalidate the position
1748 model->ValidatePosition();
1749
1750 bool canUseInstancing = false;
1751
1752 if (model->GetModelDef()->GetNumBones() == 0)
1753 canUseInstancing = true;
1754
1755 if (model->GetMaterial().IsPlayer())
1756 {
1757 if (canUseInstancing)
1758 m->Model.PlayerInstancing->Submit(model);
1759 else
1760 m->Model.Player->Submit(model);
1761 }
1762 else if (model->GetMaterial().UsesAlpha())
1763 {
1764 m->Model.Transp->Submit(model);
1765 }
1766 else
1767 {
1768 if (canUseInstancing)
1769 m->Model.NormalInstancing->Submit(model);
1770 else
1771 m->Model.Normal->Submit(model);
1772 }
1773}
1774
1775
1776///////////////////////////////////////////////////////////
1777// Render the given scene
1778void CRenderer::RenderScene(Scene& scene)
1779{
1780 m_CurrentScene = &scene;
1781
1782 CFrustum frustum = m_CullCamera.GetFrustum();
1783
1784 scene.EnumerateObjects(frustum, this);
1785
1786 m->particleManager.RenderSubmit(*this, frustum);
1787
1788 ogl_WarnIfError();
1789
1790 RenderSubmissions();
1791
1792 m_CurrentScene = NULL;
1793}
1794
1795Scene& CRenderer::GetScene()
1796{
1797 ENSURE(m_CurrentScene);
1798 return *m_CurrentScene;
1799}
1800
1801//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1802// BindTexture: bind a GL texture object to current active unit
1803void CRenderer::BindTexture(int unit,GLuint tex)
1804{
1805 pglActiveTextureARB(GL_TEXTURE0+unit);
1806
1807 glBindTexture(GL_TEXTURE_2D,tex);
1808 if (tex) {
1809 glEnable(GL_TEXTURE_2D);
1810 } else {
1811 glDisable(GL_TEXTURE_2D);
1812 }
1813}
1814
1815///////////////////////////////////////////////////////////////////////////////////////////////////
1816// LoadAlphaMaps: load the 14 default alpha maps, pack them into one composite texture and
1817// calculate the coordinate of each alphamap within this packed texture
1818int CRenderer::LoadAlphaMaps()
1819{
1820 const wchar_t* const key = L"(alpha map composite)";
1821 Handle ht = ogl_tex_find(key);
1822 // alpha map texture had already been created and is still in memory:
1823 // reuse it, do not load again.
1824 if(ht > 0)
1825 {
1826 m_hCompositeAlphaMap = ht;
1827 return 0;
1828 }
1829
1830 //
1831 // load all textures and store Handle in array
1832 //
1833 Handle textures[NumAlphaMaps] = {0};
1834 VfsPath path(L"art/textures/terrain/alphamaps/standard");
1835 const wchar_t* fnames[NumAlphaMaps] = {
1836 L"blendcircle.png",
1837 L"blendlshape.png",
1838 L"blendedge.png",
1839 L"blendedgecorner.png",
1840 L"blendedgetwocorners.png",
1841 L"blendfourcorners.png",
1842 L"blendtwooppositecorners.png",
1843 L"blendlshapecorner.png",
1844 L"blendtwocorners.png",
1845 L"blendcorner.png",
1846 L"blendtwoedges.png",
1847 L"blendthreecorners.png",
1848 L"blendushape.png",
1849 L"blendbad.png"
1850 };
1851 size_t base = 0; // texture width/height (see below)
1852 // for convenience, we require all alpha maps to be of the same BPP
1853 // (avoids another ogl_tex_get_size call, and doesn't hurt)
1854 size_t bpp = 0;
1855 for(size_t i=0;i<NumAlphaMaps;i++)
1856 {
1857 // note: these individual textures can be discarded afterwards;
1858 // we cache the composite.
1859 textures[i] = ogl_tex_load(g_VFS, path / fnames[i]);
1860 RETURN_STATUS_IF_ERR(textures[i]);
1861
1862 // get its size and make sure they are all equal.
1863 // (the packing algo assumes this)
1864 size_t this_width = 0, this_height = 0, this_bpp = 0; // fail-safe
1865 (void)ogl_tex_get_size(textures[i], &this_width, &this_height, &this_bpp);
1866 if(this_width != this_height)
1867 DEBUG_DISPLAY_ERROR(L"Alpha maps are not square");
1868 // .. first iteration: establish size
1869 if(i == 0)
1870 {
1871 base = this_width;
1872 bpp = this_bpp;
1873 }
1874 // .. not first: make sure texture size matches
1875 else if(base != this_width || bpp != this_bpp)
1876 DEBUG_DISPLAY_ERROR(L"Alpha maps are not identically sized (including pixel depth)");
1877 }
1878
1879 //
1880 // copy each alpha map (tile) into one buffer, arrayed horizontally.
1881 //
1882 size_t tile_w = 2+base+2; // 2 pixel border (avoids bilinear filtering artifacts)
1883 size_t total_w = round_up_to_pow2(tile_w * NumAlphaMaps);
1884 size_t total_h = base; ENSURE(is_pow2(total_h));
1885 shared_ptr<u8> data;
1886 AllocateAligned(data, total_w*total_h, maxSectorSize);
1887 // for each tile on row
1888 for (size_t i = 0; i < NumAlphaMaps; i++)
1889 {
1890 // get src of copy
1891 u8* src = 0;
1892 (void)ogl_tex_get_data(textures[i], &src);
1893
1894 size_t srcstep = bpp/8;
1895
1896 // get destination of copy
1897 u8* dst = data.get() + (i*tile_w);
1898
1899 // for each row of image
1900 for (size_t j = 0; j < base; j++)
1901 {
1902 // duplicate first pixel
1903 *dst++ = *src;
1904 *dst++ = *src;
1905
1906 // copy a row
1907 for (size_t k = 0; k < base; k++)
1908 {
1909 *dst++ = *src;
1910 src += srcstep;
1911 }
1912
1913 // duplicate last pixel
1914 *dst++ = *(src-srcstep);
1915 *dst++ = *(src-srcstep);
1916
1917 // advance write pointer for next row
1918 dst += total_w-tile_w;
1919 }
1920
1921 m_AlphaMapCoords[i].u0 = float(i*tile_w+2) / float(total_w);
1922 m_AlphaMapCoords[i].u1 = float((i+1)*tile_w-2) / float(total_w);
1923 m_AlphaMapCoords[i].v0 = 0.0f;
1924 m_AlphaMapCoords[i].v1 = 1.0f;
1925 }
1926
1927 for (size_t i = 0; i < NumAlphaMaps; i++)
1928 (void)ogl_tex_free(textures[i]);
1929
1930 // upload the composite texture
1931 Tex t;
1932 (void)tex_wrap(total_w, total_h, 8, TEX_GREY, data, 0, &t);
1933 m_hCompositeAlphaMap = ogl_tex_wrap(&t, g_VFS, key);
1934 (void)ogl_tex_set_filter(m_hCompositeAlphaMap, GL_LINEAR);
1935 (void)ogl_tex_set_wrap (m_hCompositeAlphaMap, GL_CLAMP_TO_EDGE);
1936 int ret = ogl_tex_upload(m_hCompositeAlphaMap, 0, 0, GL_INTENSITY);
1937
1938 return ret;
1939}
1940
1941///////////////////////////////////////////////////////////////////////////////////////////////////
1942// UnloadAlphaMaps: frees the resources allocates by LoadAlphaMaps
1943void CRenderer::UnloadAlphaMaps()
1944{
1945 ogl_tex_free(m_hCompositeAlphaMap);
1946 m_hCompositeAlphaMap = 0;
1947}
1948
1949
1950
1951Status CRenderer::ReloadChangedFileCB(void* param, const VfsPath& path)
1952{
1953 CRenderer* renderer = static_cast<CRenderer*>(param);
1954
1955 // If an alpha map changed, and we already loaded them, then reload them
1956 if (boost::algorithm::starts_with(path.string(), L"art/textures/terrain/alphamaps/"))
1957 {
1958 if (renderer->m_hCompositeAlphaMap)
1959 {
1960 renderer->UnloadAlphaMaps();
1961 renderer->LoadAlphaMaps();
1962 }
1963 }
1964
1965 return INFO::OK;
1966}
1967
1968void CRenderer::MakeShadersDirty()
1969{
1970 m->ShadersDirty = true;
1971}
1972
1973///////////////////////////////////////////////////////////////////////////////////////////////////
1974// Scripting Interface
1975
1976jsval CRenderer::JSI_GetFastPlayerColor(JSContext*)
1977{
1978 return ToJSVal(m_FastPlayerColor);
1979}
1980
1981void CRenderer::JSI_SetFastPlayerColor(JSContext* ctx, jsval newval)
1982{
1983 bool fast;
1984
1985 if (!ToPrimitive(ctx, newval, fast))
1986 return;
1987
1988 SetFastPlayerColor(fast);
1989}
1990
1991jsval CRenderer::JSI_GetRenderPath(JSContext*)
1992{
1993 return ToJSVal(GetRenderPathName(m_Options.m_RenderPath));
1994}
1995
1996void CRenderer::JSI_SetRenderPath(JSContext* ctx, jsval newval)
1997{
1998 CStr name;
1999
2000 if (!ToPrimitive(ctx, newval, name))
2001 return;
2002
2003 SetRenderPath(GetRenderPathByName(name));
2004}
2005
2006jsval CRenderer::JSI_GetDepthTextureBits(JSContext*)
2007{
2008 return ToJSVal(m->shadow->GetDepthTextureBits());
2009}
2010
2011void CRenderer::JSI_SetDepthTextureBits(JSContext* ctx, jsval newval)
2012{
2013 int depthTextureBits;
2014
2015 if (!ToPrimitive(ctx, newval, depthTextureBits))
2016 return;
2017
2018 m->shadow->SetDepthTextureBits(depthTextureBits);
2019}
2020
2021jsval CRenderer::JSI_GetShadows(JSContext*)
2022{
2023 return ToJSVal(m_Options.m_Shadows);
2024}
2025
2026void CRenderer::JSI_SetShadows(JSContext* ctx, jsval newval)
2027{
2028 if (!ToPrimitive(ctx, newval, m_Options.m_Shadows))
2029 return;
2030
2031 ReloadShaders();
2032}
2033
2034jsval CRenderer::JSI_GetShadowAlphaFix(JSContext*)
2035{
2036 return ToJSVal(m_Options.m_ShadowAlphaFix);
2037}
2038
2039void CRenderer::JSI_SetShadowAlphaFix(JSContext* ctx, jsval newval)
2040{
2041 if (!ToPrimitive(ctx, newval, m_Options.m_ShadowAlphaFix))
2042 return;
2043
2044 m->shadow->RecreateTexture();
2045}
2046
2047jsval CRenderer::JSI_GetShadowPCF(JSContext*)
2048{
2049 return ToJSVal(m_Options.m_ShadowPCF);
2050}
2051
2052void CRenderer::JSI_SetShadowPCF(JSContext* ctx, jsval newval)
2053{
2054 if (!ToPrimitive(ctx, newval, m_Options.m_ShadowPCF))
2055 return;
2056
2057 ReloadShaders();
2058}
2059
2060jsval CRenderer::JSI_GetSky(JSContext*)
2061{
2062 return ToJSVal(m->skyManager.GetSkySet());
2063}
2064
2065void CRenderer::JSI_SetSky(JSContext* ctx, jsval newval)
2066{
2067 CStrW skySet;
2068 if (!ToPrimitive<CStrW>(ctx, newval, skySet)) return;
2069 m->skyManager.SetSkySet(skySet);
2070}
2071
2072void CRenderer::ScriptingInit()
2073{
2074 AddProperty(L"fastPlayerColor", &CRenderer::JSI_GetFastPlayerColor, &CRenderer::JSI_SetFastPlayerColor);
2075 AddProperty(L"renderpath", &CRenderer::JSI_GetRenderPath, &CRenderer::JSI_SetRenderPath);
2076 AddProperty(L"sortAllTransparent", &CRenderer::m_SortAllTransparent);
2077 AddProperty(L"displayFrustum", &CRenderer::m_DisplayFrustum);
2078 AddProperty(L"shadowZBias", &CRenderer::m_ShadowZBias);
2079 AddProperty(L"shadowMapSize", &CRenderer::m_ShadowMapSize);
2080 AddProperty(L"disableCopyShadow", &CRenderer::m_DisableCopyShadow);
2081 AddProperty(L"shadows", &CRenderer::JSI_GetShadows, &CRenderer::JSI_SetShadows);
2082 AddProperty(L"depthTextureBits", &CRenderer::JSI_GetDepthTextureBits, &CRenderer::JSI_SetDepthTextureBits);
2083 AddProperty(L"shadowAlphaFix", &CRenderer::JSI_GetShadowAlphaFix, &CRenderer::JSI_SetShadowAlphaFix);
2084 AddProperty(L"shadowPCF", &CRenderer::JSI_GetShadowPCF, &CRenderer::JSI_SetShadowPCF);
2085 AddProperty(L"skipSubmit", &CRenderer::m_SkipSubmit);
2086 AddProperty(L"skySet", &CRenderer::JSI_GetSky, &CRenderer::JSI_SetSky);
2087
2088 CJSObject<CRenderer>::ScriptingInit("Renderer");
2089}
2090
2091
2092CTextureManager& CRenderer::GetTextureManager()
2093{
2094 return m->textureManager;
2095}
2096
2097CShaderManager& CRenderer::GetShaderManager()
2098{
2099 return m->shaderManager;
2100}
2101
2102CParticleManager& CRenderer::GetParticleManager()
2103{
2104 return m->particleManager;
2105}
Note: See TracBrowser for help on using the repository browser.