Merge 'remotes/trunk'
[0ad.git] / source / renderer / TerrainRenderer.cpp
blobe7fd33221e33cae502f8e72a97cf22649ff73da7
1 /* Copyright (C) 2013 Wildfire Games.
2 * This file is part of 0 A.D.
4 * 0 A.D. is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 2 of the License, or
7 * (at your option) any later version.
9 * 0 A.D. is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
19 * Terrain rendering (everything related to patches and water) is
20 * encapsulated in TerrainRenderer
23 #include "precompiled.h"
25 #include "graphics/Camera.h"
26 #include "graphics/Decal.h"
27 #include "graphics/LightEnv.h"
28 #include "graphics/LOSTexture.h"
29 #include "graphics/Patch.h"
30 #include "graphics/GameView.h"
31 #include "graphics/Model.h"
32 #include "graphics/ShaderManager.h"
33 #include "renderer/ShadowMap.h"
34 #include "renderer/SkyManager.h"
35 #include "graphics/TerritoryTexture.h"
36 #include "graphics/TextRenderer.h"
38 #include "maths/MathUtil.h"
40 #include "ps/Filesystem.h"
41 #include "ps/CLogger.h"
42 #include "ps/Game.h"
43 #include "ps/Profile.h"
44 #include "ps/World.h"
46 #include "renderer/DecalRData.h"
47 #include "renderer/PatchRData.h"
48 #include "renderer/Renderer.h"
49 #include "renderer/ShadowMap.h"
50 #include "renderer/TerrainRenderer.h"
51 #include "renderer/VertexArray.h"
52 #include "renderer/WaterManager.h"
54 #include "tools/atlas/GameInterface/GameLoop.h"
56 extern GameLoopState* g_AtlasGameLoop;
58 ///////////////////////////////////////////////////////////////////////////////////////////////
59 // TerrainRenderer implementation
62 /**
63 * TerrainRenderer keeps track of which phase it is in, to detect
64 * when Submit, PrepareForRendering etc. are called in the wrong order.
66 enum Phase {
67 Phase_Submit,
68 Phase_Render
72 /**
73 * Struct TerrainRendererInternals: Internal variables used by the TerrainRenderer class.
75 struct TerrainRendererInternals
77 /// Which phase (submitting or rendering patches) are we in right now?
78 Phase phase;
80 /// Patches that were submitted for this frame
81 std::vector<CPatchRData*> visiblePatches[CRenderer::CULL_MAX];
83 /// Decals that were submitted for this frame
84 std::vector<CDecalRData*> visibleDecals[CRenderer::CULL_MAX];
86 /// Fancy water shader
87 CShaderProgramPtr fancyWaterShader;
89 CSimulation2* simulation;
94 ///////////////////////////////////////////////////////////////////
95 // Construction/Destruction
96 TerrainRenderer::TerrainRenderer()
98 m = new TerrainRendererInternals();
99 m->phase = Phase_Submit;
102 TerrainRenderer::~TerrainRenderer()
104 delete m;
107 void TerrainRenderer::SetSimulation(CSimulation2* simulation)
109 m->simulation = simulation;
112 ///////////////////////////////////////////////////////////////////
113 // Submit a patch for rendering
114 void TerrainRenderer::Submit(int cullGroup, CPatch* patch)
116 ENSURE(m->phase == Phase_Submit);
118 CPatchRData* data = (CPatchRData*)patch->GetRenderData();
119 if (data == 0)
121 // no renderdata for patch, create it now
122 data = new CPatchRData(patch, m->simulation);
123 patch->SetRenderData(data);
125 data->Update(m->simulation);
127 m->visiblePatches[cullGroup].push_back(data);
130 ///////////////////////////////////////////////////////////////////
131 // Submit a decal for rendering
132 void TerrainRenderer::Submit(int cullGroup, CModelDecal* decal)
134 ENSURE(m->phase == Phase_Submit);
136 CDecalRData* data = (CDecalRData*)decal->GetRenderData();
137 if (data == 0)
139 // no renderdata for decal, create it now
140 data = new CDecalRData(decal, m->simulation);
141 decal->SetRenderData(data);
143 data->Update(m->simulation);
145 m->visibleDecals[cullGroup].push_back(data);
148 ///////////////////////////////////////////////////////////////////
149 // Prepare for rendering
150 void TerrainRenderer::PrepareForRendering()
152 ENSURE(m->phase == Phase_Submit);
154 m->phase = Phase_Render;
157 ///////////////////////////////////////////////////////////////////
158 // Clear submissions lists
159 void TerrainRenderer::EndFrame()
161 ENSURE(m->phase == Phase_Render || m->phase == Phase_Submit);
163 for (int i = 0; i < CRenderer::CULL_MAX; ++i)
165 m->visiblePatches[i].clear();
166 m->visibleDecals[i].clear();
169 m->phase = Phase_Submit;
173 ///////////////////////////////////////////////////////////////////
174 // Full-featured terrain rendering with blending and everything
175 void TerrainRenderer::RenderTerrain(int cullGroup)
177 #if CONFIG2_GLES
178 UNUSED2(cullGroup);
179 #else
180 ENSURE(m->phase == Phase_Render);
182 std::vector<CPatchRData*>& visiblePatches = m->visiblePatches[cullGroup];
183 std::vector<CDecalRData*>& visibleDecals = m->visibleDecals[cullGroup];
184 if (visiblePatches.empty() && visibleDecals.empty())
185 return;
187 CShaderProgramPtr dummyShader = g_Renderer.GetShaderManager().LoadProgram("fixed:dummy", CShaderDefines());
188 dummyShader->Bind();
190 // render the solid black sides of the map first
191 g_Renderer.BindTexture(0, 0);
192 glEnableClientState(GL_VERTEX_ARRAY);
193 glColor3f(0, 0, 0);
194 PROFILE_START("render terrain sides");
195 for (size_t i = 0; i < visiblePatches.size(); ++i)
196 visiblePatches[i]->RenderSides(dummyShader);
197 PROFILE_END("render terrain sides");
199 // switch on required client states
200 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
202 // render everything fullbright
203 // set up texture environment for base pass
204 pglActiveTextureARB(GL_TEXTURE0);
205 pglClientActiveTextureARB(GL_TEXTURE0);
206 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
207 glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_REPLACE);
208 glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE);
209 glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
211 // Set alpha to 1.0
212 glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);
213 glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_CONSTANT);
214 glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
215 static const float one[4] = { 1.f, 1.f, 1.f, 1.f };
216 glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, one);
218 PROFILE_START("render terrain base");
219 CPatchRData::RenderBases(visiblePatches, CShaderDefines(), NULL, true, dummyShader);
220 PROFILE_END("render terrain base");
222 // render blends
223 // switch on the composite alpha map texture
224 (void)ogl_tex_bind(g_Renderer.m_hCompositeAlphaMap, 1);
226 // switch on second uv set
227 pglClientActiveTextureARB(GL_TEXTURE1);
228 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
230 // setup additional texenv required by blend pass
231 pglActiveTextureARB(GL_TEXTURE1);
232 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
233 glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_REPLACE);
234 glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS);
235 glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
236 glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);
237 glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_TEXTURE);
238 glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_ONE_MINUS_SRC_ALPHA);
240 // switch on blending
241 glEnable(GL_BLEND);
242 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
244 // no need to write to the depth buffer a second time
245 glDepthMask(0);
247 // The decal color array contains lighting data, which we don't want in this non-shader mode
248 glDisableClientState(GL_COLOR_ARRAY);
250 // render blend passes for each patch
251 PROFILE_START("render terrain blends");
252 CPatchRData::RenderBlends(visiblePatches, CShaderDefines(), NULL, true, dummyShader);
253 PROFILE_END("render terrain blends");
255 // Disable second texcoord array
256 pglClientActiveTextureARB(GL_TEXTURE1);
257 glDisableClientState(GL_TEXTURE_COORD_ARRAY);
260 // Render terrain decals
262 g_Renderer.BindTexture(1, 0);
263 pglActiveTextureARB(GL_TEXTURE0);
264 pglClientActiveTextureARB(GL_TEXTURE0);
265 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
266 glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE);
267 glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS);
268 glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
269 glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_TEXTURE);
270 glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR);
271 glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);
272 glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_TEXTURE);
273 glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
275 PROFILE_START("render terrain decals");
276 CDecalRData::RenderDecals(visibleDecals, CShaderDefines(), NULL, true, dummyShader);
277 PROFILE_END("render terrain decals");
280 // Now apply lighting
281 const CLightEnv& lightEnv = g_Renderer.GetLightEnv();
283 pglClientActiveTextureARB(GL_TEXTURE0);
284 glEnableClientState(GL_COLOR_ARRAY); // diffuse lighting colors
286 // The vertex color is scaled by 0.5 to permit overbrightness without clamping.
287 // We therefore need to draw clamp((texture*lighting)*2.0), where 'texture'
288 // is what previous passes drew onto the framebuffer, and 'lighting' is the
289 // color computed by this pass.
290 // We can do that with blending by getting it to draw dst*src + src*dst:
291 glBlendFunc(GL_DST_COLOR, GL_SRC_COLOR);
293 // Scale the ambient color by 0.5 to match the vertex diffuse colors
294 float terrainAmbientColor[4] = {
295 lightEnv.m_TerrainAmbientColor.X * 0.5f,
296 lightEnv.m_TerrainAmbientColor.Y * 0.5f,
297 lightEnv.m_TerrainAmbientColor.Z * 0.5f,
301 CLOSTexture& losTexture = g_Renderer.GetScene().GetLOSTexture();
303 int streamflags = STREAM_POS|STREAM_COLOR;
305 pglActiveTextureARB(GL_TEXTURE0);
306 // We're not going to use a texture here, but we have to have a valid texture
307 // bound else the texture unit will be disabled.
308 // We should still have a bound splat texture from some earlier rendering,
309 // so assume that's still valid to use.
310 // (TODO: That's a bit of an ugly hack.)
312 // No shadows: (Ambient + Diffuse) * LOS
313 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
314 glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_ADD);
315 glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS);
316 glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
317 glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_CONSTANT);
318 glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR);
319 glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);
320 glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_PREVIOUS);
321 glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
323 glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, terrainAmbientColor);
325 losTexture.BindTexture(1);
326 pglClientActiveTextureARB(GL_TEXTURE1);
327 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
328 streamflags |= STREAM_POSTOUV1;
330 glMatrixMode(GL_TEXTURE);
331 glLoadMatrixf(&losTexture.GetTextureMatrix()._11);
332 glMatrixMode(GL_MODELVIEW);
334 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
335 glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE);
336 glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS);
337 glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
338 glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_TEXTURE);
339 glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_ALPHA);
340 glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);
341 glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_PREVIOUS);
342 glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
344 pglActiveTextureARB(GL_TEXTURE0);
345 pglClientActiveTextureARB(GL_TEXTURE0);
347 PROFILE_START("render terrain streams");
348 CPatchRData::RenderStreams(visiblePatches, dummyShader, streamflags);
349 PROFILE_END("render terrain streams");
351 glMatrixMode(GL_TEXTURE);
352 glLoadIdentity();
353 glMatrixMode(GL_MODELVIEW);
355 // restore OpenGL state
356 g_Renderer.BindTexture(1, 0);
358 pglClientActiveTextureARB(GL_TEXTURE1);
359 glDisableClientState(GL_TEXTURE_COORD_ARRAY);
360 glMatrixMode(GL_TEXTURE);
361 glLoadIdentity();
362 glMatrixMode(GL_MODELVIEW);
364 pglClientActiveTextureARB(GL_TEXTURE0);
365 pglActiveTextureARB(GL_TEXTURE0);
367 glDepthMask(1);
368 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
369 glDisable(GL_BLEND);
370 glDisableClientState(GL_COLOR_ARRAY);
371 glDisableClientState(GL_VERTEX_ARRAY);
372 glDisableClientState(GL_TEXTURE_COORD_ARRAY);
374 dummyShader->Unbind();
375 #endif
378 void TerrainRenderer::RenderTerrainOverlayTexture(int cullGroup, CMatrix3D& textureMatrix)
380 #if CONFIG2_GLES
381 #warning TODO: implement TerrainRenderer::RenderTerrainOverlayTexture for GLES
382 UNUSED2(cullGroup);
383 UNUSED2(textureMatrix);
384 #else
385 ENSURE(m->phase == Phase_Render);
387 std::vector<CPatchRData*>& visiblePatches = m->visiblePatches[cullGroup];
389 glEnableClientState(GL_VERTEX_ARRAY);
390 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
392 pglActiveTextureARB(GL_TEXTURE0);
393 glEnable(GL_TEXTURE_2D);
394 glEnable(GL_BLEND);
395 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
396 glDepthMask(0);
397 glDisable(GL_DEPTH_TEST);
399 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
401 glMatrixMode(GL_TEXTURE);
402 glLoadMatrixf(&textureMatrix._11);
403 glMatrixMode(GL_MODELVIEW);
405 CShaderProgramPtr dummyShader = g_Renderer.GetShaderManager().LoadProgram("fixed:dummy", CShaderDefines());
406 dummyShader->Bind();
407 CPatchRData::RenderStreams(visiblePatches, dummyShader, STREAM_POS|STREAM_POSTOUV0);
408 dummyShader->Unbind();
410 // To make the overlay visible over water, render an additional map-sized
411 // water-height patch
412 CBoundingBoxAligned waterBounds;
413 for (size_t i = 0; i < visiblePatches.size(); ++i)
415 CPatchRData* data = visiblePatches[i];
416 waterBounds += data->GetWaterBounds();
418 if (!waterBounds.IsEmpty())
420 float h = g_Renderer.GetWaterManager()->m_WaterHeight + 0.05f; // add a delta to avoid z-fighting
421 float waterPos[] = {
422 waterBounds[0].X, h, waterBounds[0].Z,
423 waterBounds[1].X, h, waterBounds[0].Z,
424 waterBounds[0].X, h, waterBounds[1].Z,
425 waterBounds[1].X, h, waterBounds[1].Z
427 glVertexPointer(3, GL_FLOAT, 3*sizeof(float), waterPos);
428 glTexCoordPointer(3, GL_FLOAT, 3*sizeof(float), waterPos);
429 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
432 glMatrixMode(GL_TEXTURE);
433 glLoadIdentity();
434 glMatrixMode(GL_MODELVIEW);
436 glDepthMask(1);
437 glEnable(GL_DEPTH_TEST);
438 glDisable(GL_BLEND);
439 glDisableClientState(GL_COLOR_ARRAY);
440 glDisableClientState(GL_VERTEX_ARRAY);
441 glDisableClientState(GL_TEXTURE_COORD_ARRAY);
442 #endif
446 ///////////////////////////////////////////////////////////////////
449 * Set up all the uniforms for a shader pass.
451 void TerrainRenderer::PrepareShader(const CShaderProgramPtr& shader, ShadowMap* shadow)
453 shader->Uniform(str_transform, g_Renderer.GetViewCamera().GetViewProjection());
454 shader->Uniform(str_cameraPos, g_Renderer.GetViewCamera().GetOrientation().GetTranslation());
456 const CLightEnv& lightEnv = g_Renderer.GetLightEnv();
458 if (shadow)
460 shader->BindTexture(str_shadowTex, shadow->GetTexture());
461 shader->Uniform(str_shadowTransform, shadow->GetTextureMatrix());
462 int width = shadow->GetWidth();
463 int height = shadow->GetHeight();
464 shader->Uniform(str_shadowScale, width, height, 1.0f / width, 1.0f / height);
467 CLOSTexture& los = g_Renderer.GetScene().GetLOSTexture();
468 shader->BindTexture(str_losTex, los.GetTextureSmooth());
469 shader->Uniform(str_losTransform, los.GetTextureMatrix()[0], los.GetTextureMatrix()[12], 0.f, 0.f);
471 shader->Uniform(str_ambient, lightEnv.m_TerrainAmbientColor);
472 shader->Uniform(str_sunColor, lightEnv.m_SunColor);
473 shader->Uniform(str_sunDir, lightEnv.GetSunDir());
475 shader->Uniform(str_fogColor, lightEnv.m_FogColor);
476 shader->Uniform(str_fogParams, lightEnv.m_FogFactor, lightEnv.m_FogMax, 0.f, 0.f);
479 void TerrainRenderer::RenderTerrainShader(const CShaderDefines& context, int cullGroup, ShadowMap* shadow)
481 ENSURE(m->phase == Phase_Render);
483 std::vector<CPatchRData*>& visiblePatches = m->visiblePatches[cullGroup];
484 std::vector<CDecalRData*>& visibleDecals = m->visibleDecals[cullGroup];
485 if (visiblePatches.empty() && visibleDecals.empty())
486 return;
488 // render the solid black sides of the map first
489 CShaderTechniquePtr techSolid = g_Renderer.GetShaderManager().LoadEffect(str_gui_solid);
490 techSolid->BeginPass();
491 CShaderProgramPtr shaderSolid = techSolid->GetShader();
492 shaderSolid->Uniform(str_transform, g_Renderer.GetViewCamera().GetViewProjection());
493 shaderSolid->Uniform(str_color, 0.0f, 0.0f, 0.0f, 1.0f);
495 PROFILE_START("render terrain sides");
496 for (size_t i = 0; i < visiblePatches.size(); ++i)
497 visiblePatches[i]->RenderSides(shaderSolid);
498 PROFILE_END("render terrain sides");
500 techSolid->EndPass();
502 PROFILE_START("render terrain base");
503 CPatchRData::RenderBases(visiblePatches, context, shadow);
504 PROFILE_END("render terrain base");
506 // no need to write to the depth buffer a second time
507 glDepthMask(0);
509 // render blend passes for each patch
510 PROFILE_START("render terrain blends");
511 CPatchRData::RenderBlends(visiblePatches, context, shadow, false);
512 PROFILE_END("render terrain blends");
514 PROFILE_START("render terrain decals");
515 CDecalRData::RenderDecals(visibleDecals, context, shadow, false);
516 PROFILE_END("render terrain decals");
518 // restore OpenGL state
519 g_Renderer.BindTexture(1, 0);
520 g_Renderer.BindTexture(2, 0);
521 g_Renderer.BindTexture(3, 0);
523 glDepthMask(1);
524 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
525 glDisable(GL_BLEND);
529 ///////////////////////////////////////////////////////////////////
530 // Render un-textured patches as polygons
531 void TerrainRenderer::RenderPatches(int cullGroup)
533 ENSURE(m->phase == Phase_Render);
535 std::vector<CPatchRData*>& visiblePatches = m->visiblePatches[cullGroup];
536 if (visiblePatches.empty())
537 return;
539 #if CONFIG2_GLES
540 #warning TODO: implement TerrainRenderer::RenderPatches for GLES
541 #else
542 CShaderProgramPtr dummyShader = g_Renderer.GetShaderManager().LoadProgram("fixed:dummy", CShaderDefines());
543 dummyShader->Bind();
545 glEnableClientState(GL_VERTEX_ARRAY);
546 CPatchRData::RenderStreams(visiblePatches, dummyShader, STREAM_POS);
547 glDisableClientState(GL_VERTEX_ARRAY);
549 dummyShader->Unbind();
550 #endif
554 ///////////////////////////////////////////////////////////////////
555 // Render outlines of submitted patches as lines
556 void TerrainRenderer::RenderOutlines(int cullGroup)
558 ENSURE(m->phase == Phase_Render);
560 std::vector<CPatchRData*>& visiblePatches = m->visiblePatches[cullGroup];
561 if (visiblePatches.empty())
562 return;
564 #if CONFIG2_GLES
565 #warning TODO: implement TerrainRenderer::RenderOutlines for GLES
566 #else
567 glEnableClientState(GL_VERTEX_ARRAY);
568 for (size_t i = 0; i < visiblePatches.size(); ++i)
569 visiblePatches[i]->RenderOutline();
570 glDisableClientState(GL_VERTEX_ARRAY);
571 #endif
575 ///////////////////////////////////////////////////////////////////
576 // Scissor rectangle of water patches
577 CBoundingBoxAligned TerrainRenderer::ScissorWater(int cullGroup, const CMatrix3D &viewproj)
579 std::vector<CPatchRData*>& visiblePatches = m->visiblePatches[cullGroup];
581 CBoundingBoxAligned scissor;
582 for (size_t i = 0; i < visiblePatches.size(); ++i)
584 CPatchRData* data = visiblePatches[i];
585 const CBoundingBoxAligned& waterBounds = data->GetWaterBounds();
586 if (waterBounds.IsEmpty())
587 continue;
589 CVector4D v1 = viewproj.Transform(CVector4D(waterBounds[0].X, waterBounds[1].Y, waterBounds[0].Z, 1.0f));
590 CVector4D v2 = viewproj.Transform(CVector4D(waterBounds[1].X, waterBounds[1].Y, waterBounds[0].Z, 1.0f));
591 CVector4D v3 = viewproj.Transform(CVector4D(waterBounds[0].X, waterBounds[1].Y, waterBounds[1].Z, 1.0f));
592 CVector4D v4 = viewproj.Transform(CVector4D(waterBounds[1].X, waterBounds[1].Y, waterBounds[1].Z, 1.0f));
593 CBoundingBoxAligned screenBounds;
594 #define ADDBOUND(v1, v2, v3, v4) \
595 if (v1.Z >= -v1.W) \
596 screenBounds += CVector3D(v1.X, v1.Y, v1.Z) * (1.0f / v1.W); \
597 else \
599 float t = v1.Z + v1.W; \
600 if (v2.Z > -v2.W) \
602 CVector4D c2 = v1 + (v2 - v1) * (t / (t - (v2.Z + v2.W))); \
603 screenBounds += CVector3D(c2.X, c2.Y, c2.Z) * (1.0f / c2.W); \
605 if (v3.Z > -v3.W) \
607 CVector4D c3 = v1 + (v3 - v1) * (t / (t - (v3.Z + v3.W))); \
608 screenBounds += CVector3D(c3.X, c3.Y, c3.Z) * (1.0f / c3.W); \
610 if (v4.Z > -v4.W) \
612 CVector4D c4 = v1 + (v4 - v1) * (t / (t - (v4.Z + v4.W))); \
613 screenBounds += CVector3D(c4.X, c4.Y, c4.Z) * (1.0f / c4.W); \
616 ADDBOUND(v1, v2, v3, v4);
617 ADDBOUND(v2, v1, v3, v4);
618 ADDBOUND(v3, v1, v2, v4);
619 ADDBOUND(v4, v1, v2, v3);
620 #undef ADDBOUND
621 if (screenBounds[0].X >= 1.0f || screenBounds[1].X <= -1.0f || screenBounds[0].Y >= 1.0f || screenBounds[1].Y <= -1.0f)
622 continue;
623 scissor += screenBounds;
625 return CBoundingBoxAligned(CVector3D(clamp(scissor[0].X, -1.0f, 1.0f), clamp(scissor[0].Y, -1.0f, 1.0f), -1.0f),
626 CVector3D(clamp(scissor[1].X, -1.0f, 1.0f), clamp(scissor[1].Y, -1.0f, 1.0f), 1.0f));
629 // Render fancy water
630 bool TerrainRenderer::RenderFancyWater(const CShaderDefines& context, int cullGroup, ShadowMap* shadow)
632 PROFILE3_GPU("fancy water");
634 WaterManager* WaterMgr = g_Renderer.GetWaterManager();
635 CShaderDefines defines = context;
637 // If we're using fancy water, make sure its shader is loaded
638 if (!m->fancyWaterShader || WaterMgr->m_NeedsReloading)
640 if (WaterMgr->m_WaterRealDepth)
641 defines.Add(str_USE_REAL_DEPTH, str_1);
642 if (WaterMgr->m_WaterFancyEffects)
643 defines.Add(str_USE_FANCY_EFFECTS, str_1);
644 if (WaterMgr->m_WaterRefraction)
645 defines.Add(str_USE_REFRACTION, str_1);
646 if (WaterMgr->m_WaterReflection)
647 defines.Add(str_USE_REFLECTION, str_1);
648 if (shadow && WaterMgr->m_WaterShadows)
649 defines.Add(str_USE_SHADOWS_ON_WATER, str_1);
651 // haven't updated the ARB shader yet so I'll always load the GLSL
652 /*if (!g_Renderer.m_Options.m_PreferGLSL && !superFancy)
653 m->fancyWaterShader = g_Renderer.GetShaderManager().LoadProgram("arb/water_high", defines);
654 else*/
655 m->fancyWaterShader = g_Renderer.GetShaderManager().LoadProgram("glsl/water_high", defines);
657 if (!m->fancyWaterShader)
659 LOGERROR("Failed to load water shader. Falling back to fixed pipeline water.\n");
660 WaterMgr->m_RenderWater = false;
661 return false;
663 WaterMgr->m_NeedsReloading = false;
666 CLOSTexture& losTexture = g_Renderer.GetScene().GetLOSTexture();
668 // creating the real depth texture using the depth buffer.
669 if (WaterMgr->m_WaterRealDepth)
671 if (WaterMgr->m_depthTT == 0)
673 GLuint depthTex;
674 glGenTextures(1, (GLuint*)&depthTex);
675 WaterMgr->m_depthTT = depthTex;
676 glBindTexture(GL_TEXTURE_2D, WaterMgr->m_depthTT);
677 glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, g_Renderer.GetWidth(), g_Renderer.GetHeight(), 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE,NULL);
678 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
679 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
680 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
681 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
683 else
685 glBindTexture(GL_TEXTURE_2D, WaterMgr->m_depthTT);
686 glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, 0, 0, g_Renderer.GetWidth(), g_Renderer.GetHeight(), 0);
688 glBindTexture(GL_TEXTURE_2D, 0);
690 // Calculating the advanced informations about Foam and all if the quality calls for it.
691 /*if (WaterMgr->m_NeedInfoUpdate && (WaterMgr->m_WaterFoam || WaterMgr->m_WaterCoastalWaves))
693 WaterMgr->m_NeedInfoUpdate = false;
694 WaterMgr->CreateSuperfancyInfo();
697 double time = WaterMgr->m_WaterTexTimer;
698 double period = 8;
699 int curTex = (int)(time*60/period) % 60;
700 int nexTex = (curTex + 1) % 60;
702 float repeatPeriod = WaterMgr->m_RepeatPeriod;
704 // Render normals and foam to a framebuffer if we're in fancy effects
705 if (WaterMgr->m_WaterFancyEffects)
707 // Save the post-processing framebuffer.
708 GLint fbo;
709 glGetIntegerv(GL_FRAMEBUFFER_BINDING_EXT, &fbo);
711 pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, WaterMgr->m_FancyEffectsFBO);
713 glDisable(GL_BLEND);
714 glEnable(GL_DEPTH_TEST);
715 glDepthFunc(GL_LEQUAL);
717 glDisable(GL_CULL_FACE);
718 // Overwrite waves that would be behind the ground.
719 CShaderProgramPtr dummyShader = g_Renderer.GetShaderManager().LoadProgram("glsl/gui_solid", CShaderDefines());
720 dummyShader->Bind();
722 dummyShader->Uniform(str_transform, g_Renderer.GetViewCamera().GetViewProjection());
723 dummyShader->Uniform(str_color, 0.0f, 0.0f, 0.0f, 0.0f);
724 std::vector<CPatchRData*>& visiblePatches = m->visiblePatches[cullGroup];
725 for (size_t i = 0; i < visiblePatches.size(); ++i)
727 CPatchRData* data = visiblePatches[i];
728 data->RenderWater(dummyShader, true, true);
730 dummyShader->Unbind();
732 glEnable(GL_CULL_FACE);
733 pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);
735 glEnable(GL_BLEND);
736 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
737 glEnable(GL_DEPTH_TEST);
738 glDepthFunc(GL_LEQUAL);
740 m->fancyWaterShader->Bind();
742 const CCamera& camera = g_Renderer.GetViewCamera();
743 CVector3D camPos = camera.m_Orientation.GetTranslation();
745 m->fancyWaterShader->BindTexture(str_normalMap, WaterMgr->m_NormalMap[curTex]);
746 m->fancyWaterShader->BindTexture(str_normalMap2, WaterMgr->m_NormalMap[nexTex]);
748 if (WaterMgr->m_WaterFancyEffects)
750 m->fancyWaterShader->BindTexture(str_waterEffectsTexNorm, WaterMgr->m_FancyTextureNormal);
751 m->fancyWaterShader->BindTexture(str_waterEffectsTexOther, WaterMgr->m_FancyTextureOther);
754 if (WaterMgr->m_WaterRealDepth)
755 m->fancyWaterShader->BindTexture(str_depthTex, WaterMgr->m_depthTT);
757 if (WaterMgr->m_WaterRefraction)
758 m->fancyWaterShader->BindTexture(str_refractionMap, WaterMgr->m_RefractionTexture);
759 if (WaterMgr->m_WaterReflection)
760 m->fancyWaterShader->BindTexture(str_skyCube, g_Renderer.GetSkyManager()->GetSkyCube());
762 m->fancyWaterShader->BindTexture(str_reflectionMap, WaterMgr->m_ReflectionTexture);
763 m->fancyWaterShader->BindTexture(str_losMap, losTexture.GetTextureSmooth());
765 const CLightEnv& lightEnv = g_Renderer.GetLightEnv();
767 m->fancyWaterShader->Uniform(str_transform, g_Renderer.GetViewCamera().GetViewProjection());
769 //TODO: bind only what's needed
770 if (WaterMgr->m_WaterReflection)
772 // TODO: check that this rotates in the right direction.
773 CMatrix3D skyBoxRotation;
774 skyBoxRotation.SetIdentity();
775 skyBoxRotation.RotateY(M_PI - 0.3f + lightEnv.GetRotation());
776 m->fancyWaterShader->Uniform(str_skyBoxRot, skyBoxRotation);
778 m->fancyWaterShader->Uniform(str_sunDir, lightEnv.GetSunDir());
779 m->fancyWaterShader->Uniform(str_sunColor, lightEnv.m_SunColor);
780 m->fancyWaterShader->Uniform(str_color, WaterMgr->m_WaterColor);
781 m->fancyWaterShader->Uniform(str_tint, WaterMgr->m_WaterTint);
782 m->fancyWaterShader->Uniform(str_waviness, WaterMgr->m_Waviness);
783 m->fancyWaterShader->Uniform(str_murkiness, WaterMgr->m_Murkiness);
784 m->fancyWaterShader->Uniform(str_windAngle, WaterMgr->m_WindAngle);
785 m->fancyWaterShader->Uniform(str_repeatScale, 1.0f / repeatPeriod);
786 m->fancyWaterShader->Uniform(str_reflectionMatrix, WaterMgr->m_ReflectionMatrix);
787 m->fancyWaterShader->Uniform(str_refractionMatrix, WaterMgr->m_RefractionMatrix);
788 m->fancyWaterShader->Uniform(str_losMatrix, losTexture.GetTextureMatrix());
789 m->fancyWaterShader->Uniform(str_cameraPos, camPos);
790 m->fancyWaterShader->Uniform(str_fogColor, lightEnv.m_FogColor);
791 m->fancyWaterShader->Uniform(str_fogParams, lightEnv.m_FogFactor, lightEnv.m_FogMax, 0.f, 0.f);
792 m->fancyWaterShader->Uniform(str_time, (float)time);
793 m->fancyWaterShader->Uniform(str_screenSize, (float)g_Renderer.GetWidth(), (float)g_Renderer.GetHeight(), 0.0f, 0.0f);
795 if (WaterMgr->m_WaterType == L"clap")
797 m->fancyWaterShader->Uniform(str_waveParams1, 30.0f,1.5f,20.0f,0.03f);
798 m->fancyWaterShader->Uniform(str_waveParams2, 0.5f,0.0f,0.0f,0.0f);
800 else if (WaterMgr->m_WaterType == L"lake")
802 m->fancyWaterShader->Uniform(str_waveParams1, 8.5f,1.5f,15.0f,0.03f);
803 m->fancyWaterShader->Uniform(str_waveParams2, 0.2f,0.0f,0.0f,0.07f);
805 else
807 m->fancyWaterShader->Uniform(str_waveParams1, 15.0f,0.8f,10.0f,0.1f);
808 m->fancyWaterShader->Uniform(str_waveParams2, 0.3f,0.0f,0.1f,0.3f);
811 if (shadow && WaterMgr->m_WaterShadows)
813 m->fancyWaterShader->BindTexture(str_shadowTex, shadow->GetTexture());
814 m->fancyWaterShader->Uniform(str_shadowTransform, shadow->GetTextureMatrix());
815 int width = shadow->GetWidth();
816 int height = shadow->GetHeight();
817 m->fancyWaterShader->Uniform(str_shadowScale, width, height, 1.0f / width, 1.0f / height);
820 std::vector<CPatchRData*>& visiblePatches = m->visiblePatches[cullGroup];
821 for (size_t i = 0; i < visiblePatches.size(); ++i)
823 CPatchRData* data = visiblePatches[i];
824 data->RenderWater(m->fancyWaterShader);
826 m->fancyWaterShader->Unbind();
828 glDepthFunc(GL_LEQUAL);
829 glDisable(GL_BLEND);
831 return true;
834 void TerrainRenderer::RenderSimpleWater(int cullGroup)
836 #if CONFIG2_GLES
837 UNUSED2(cullGroup);
838 #else
839 PROFILE3_GPU("simple water");
841 WaterManager* WaterMgr = g_Renderer.GetWaterManager();
842 CLOSTexture& losTexture = g_Game->GetView()->GetLOSTexture();
844 glEnable(GL_DEPTH_TEST);
845 glDepthFunc(GL_LEQUAL);
847 double time = WaterMgr->m_WaterTexTimer;
848 double period = 1.6f;
849 int curTex = (int)(time*60/period) % 60;
851 WaterMgr->m_WaterTexture[curTex]->Bind();
853 // Shift the texture coordinates by these amounts to make the water "flow"
854 float tx = -fmod(time, 81.0)/81.0;
855 float ty = -fmod(time, 34.0)/34.0;
856 float repeatPeriod = 16.0f;
858 // Perform the shifting by using texture coordinate generation
859 GLfloat texgenS0[4] = { 1/repeatPeriod, 0, 0, tx };
860 GLfloat texgenT0[4] = { 0, 0, 1/repeatPeriod, ty };
861 glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
862 glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
863 glTexGenfv(GL_S, GL_OBJECT_PLANE, texgenS0);
864 glTexGenfv(GL_T, GL_OBJECT_PLANE, texgenT0);
865 glEnable(GL_TEXTURE_GEN_S);
866 glEnable(GL_TEXTURE_GEN_T);
868 // Set up texture environment to multiply vertex RGB by texture RGB.
869 GLfloat waterColor[4] = { WaterMgr->m_WaterColor.r, WaterMgr->m_WaterColor.g, WaterMgr->m_WaterColor.b, 1.0f };
870 glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, waterColor);
871 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
872 glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE);
873 glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE);
874 glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
875 glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_CONSTANT);
876 glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR);
879 // Multiply by LOS texture
880 losTexture.BindTexture(1);
881 CMatrix3D losMatrix = losTexture.GetTextureMatrix();
882 GLfloat texgenS1[4] = { losMatrix[0], losMatrix[4], losMatrix[8], losMatrix[12] };
883 GLfloat texgenT1[4] = { losMatrix[1], losMatrix[5], losMatrix[9], losMatrix[13] };
884 glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
885 glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
886 glTexGenfv(GL_S, GL_OBJECT_PLANE, texgenS1);
887 glTexGenfv(GL_T, GL_OBJECT_PLANE, texgenT1);
888 glEnable(GL_TEXTURE_GEN_S);
889 glEnable(GL_TEXTURE_GEN_T);
891 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
892 glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE);
893 glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS);
894 glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
895 glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_TEXTURE);
896 glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_ALPHA);
898 CShaderProgramPtr dummyShader = g_Renderer.GetShaderManager().LoadProgram("fixed:dummy", CShaderDefines());
899 dummyShader->Bind();
901 glEnableClientState(GL_VERTEX_ARRAY);
903 std::vector<CPatchRData*>& visiblePatches = m->visiblePatches[cullGroup];
904 for (size_t i = 0; i < visiblePatches.size(); ++i)
906 CPatchRData* data = visiblePatches[i];
907 data->RenderWater(dummyShader, false, true);
910 glDisableClientState(GL_VERTEX_ARRAY);
912 dummyShader->Unbind();
914 g_Renderer.BindTexture(1, 0);
916 glDisable(GL_TEXTURE_GEN_S);
917 glDisable(GL_TEXTURE_GEN_T);
918 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
920 pglActiveTextureARB(GL_TEXTURE0_ARB);
922 // Clean up the texture matrix and blend mode
923 glDisable(GL_TEXTURE_GEN_S);
924 glDisable(GL_TEXTURE_GEN_T);
925 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
927 glDisable(GL_TEXTURE_2D);
928 #endif
931 ///////////////////////////////////////////////////////////////////
932 // Render water that is part of the terrain
933 void TerrainRenderer::RenderWater(const CShaderDefines& context, int cullGroup, ShadowMap* shadow)
935 WaterManager* WaterMgr = g_Renderer.GetWaterManager();
937 WaterMgr->UpdateQuality();
939 if (!WaterMgr->WillRenderFancyWater())
940 RenderSimpleWater(cullGroup);
941 else
942 RenderFancyWater(context, cullGroup, shadow);
945 void TerrainRenderer::RenderPriorities(int cullGroup)
947 PROFILE("priorities");
949 ENSURE(m->phase == Phase_Render);
951 CShaderTechniquePtr tech = g_Renderer.GetShaderManager().LoadEffect(str_gui_text);
952 tech->BeginPass();
953 CTextRenderer textRenderer(tech->GetShader());
955 textRenderer.Font(CStrIntern("mono-stroke-10"));
956 textRenderer.Color(1.0f, 1.0f, 0.0f);
958 std::vector<CPatchRData*>& visiblePatches = m->visiblePatches[cullGroup];
959 for (size_t i = 0; i < visiblePatches.size(); ++i)
960 visiblePatches[i]->RenderPriorities(textRenderer);
962 textRenderer.Render();
963 tech->EndPass();