Index: ps/trunk/binaries/data/mods/public/shaders/arb/debug_overlay.fp =================================================================== --- ps/trunk/binaries/data/mods/public/shaders/arb/debug_overlay.fp (nonexistent) +++ ps/trunk/binaries/data/mods/public/shaders/arb/debug_overlay.fp (revision 24154) @@ -0,0 +1,7 @@ +!!ARBfp1.0 + +ATTRIB v_tex = fragment.texcoord[0]; + +TEX result.color, v_tex, texture[0], 2D; + +END Property changes on: ps/trunk/binaries/data/mods/public/shaders/arb/debug_overlay.fp ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Index: ps/trunk/binaries/data/mods/public/shaders/arb/debug_overlay.vp =================================================================== --- ps/trunk/binaries/data/mods/public/shaders/arb/debug_overlay.vp (nonexistent) +++ ps/trunk/binaries/data/mods/public/shaders/arb/debug_overlay.vp (revision 24154) @@ -0,0 +1,19 @@ +!!ARBvp1.0 + +PARAM transform[4] = { program.local[0..3] }; +PARAM textureTransform[4] = { program.local[4..7] }; + +ATTRIB position = vertex.position; +ATTRIB uv = vertex.texcoord[0]; + +DP4 result.position.x, transform[0], position; +DP4 result.position.y, transform[1], position; +DP4 result.position.z, transform[2], position; +DP4 result.position.w, transform[3], position; + +OUTPUT v_tex = result.texcoord[0]; + +DP4 v_tex.x, textureTransform[0], uv; +DP4 v_tex.y, textureTransform[1], uv; + +END Property changes on: ps/trunk/binaries/data/mods/public/shaders/arb/debug_overlay.vp ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Index: ps/trunk/binaries/data/mods/public/shaders/arb/debug_overlay.xml =================================================================== --- ps/trunk/binaries/data/mods/public/shaders/arb/debug_overlay.xml (nonexistent) +++ ps/trunk/binaries/data/mods/public/shaders/arb/debug_overlay.xml (revision 24154) @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + Property changes on: ps/trunk/binaries/data/mods/public/shaders/arb/debug_overlay.xml ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Index: ps/trunk/binaries/data/mods/public/shaders/effects/debug_overlay.xml =================================================================== --- ps/trunk/binaries/data/mods/public/shaders/effects/debug_overlay.xml (nonexistent) +++ ps/trunk/binaries/data/mods/public/shaders/effects/debug_overlay.xml (revision 24154) @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + Property changes on: ps/trunk/binaries/data/mods/public/shaders/effects/debug_overlay.xml ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Index: ps/trunk/binaries/data/mods/public/shaders/glsl/debug_overlay.fs =================================================================== --- ps/trunk/binaries/data/mods/public/shaders/glsl/debug_overlay.fs (nonexistent) +++ ps/trunk/binaries/data/mods/public/shaders/glsl/debug_overlay.fs (revision 24154) @@ -0,0 +1,11 @@ +#version 110 + +uniform sampler2D baseTex; + +varying vec2 v_tex; + +void main() +{ + vec4 base = texture2D(baseTex, v_tex); + gl_FragColor = base; +} Property changes on: ps/trunk/binaries/data/mods/public/shaders/glsl/debug_overlay.fs ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Index: ps/trunk/binaries/data/mods/public/shaders/glsl/debug_overlay.vs =================================================================== --- ps/trunk/binaries/data/mods/public/shaders/glsl/debug_overlay.vs (nonexistent) +++ ps/trunk/binaries/data/mods/public/shaders/glsl/debug_overlay.vs (revision 24154) @@ -0,0 +1,15 @@ +#version 110 + +uniform mat4 transform; +uniform mat4 textureTransform; + +attribute vec3 a_vertex; +attribute vec3 a_uv0; + +varying vec2 v_tex; + +void main() +{ + v_tex = (textureTransform * vec4(a_uv0, 1.0)).xy; + gl_Position = transform * vec4(a_vertex, 1.0); +} Property changes on: ps/trunk/binaries/data/mods/public/shaders/glsl/debug_overlay.vs ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Index: ps/trunk/binaries/data/mods/public/shaders/glsl/debug_overlay.xml =================================================================== --- ps/trunk/binaries/data/mods/public/shaders/glsl/debug_overlay.xml (nonexistent) +++ ps/trunk/binaries/data/mods/public/shaders/glsl/debug_overlay.xml (revision 24154) @@ -0,0 +1,13 @@ + + + + + + + + + + + + + Property changes on: ps/trunk/binaries/data/mods/public/shaders/glsl/debug_overlay.xml ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Index: ps/trunk/source/ps/CStrInternStatic.h =================================================================== --- ps/trunk/source/ps/CStrInternStatic.h (revision 24153) +++ ps/trunk/source/ps/CStrInternStatic.h (revision 24154) @@ -1,164 +1,165 @@ /* Copyright (C) 2020 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * 0 A.D. is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with 0 A.D. If not, see . */ // This file defines global CStrIntern variables, to avoid the cost of // constructing CStrInterns frequently at runtime. // // A line like // X(foo) // defines a variable str_foo with value "foo". // // A line like // X2(foo_0, "foo[0]") // defines a variable str_foo_0 with value "foo[0]". X(0) X(1) X(2) X(ALPHABLEND_PASS_BLEND) X(ALPHABLEND_PASS_OPAQUE) X(BLEND) X(BLOOM_NOP) X(BLOOM_PASS_H) X(BLOOM_PASS_V) X(DECAL) X(DISABLE_RECEIVE_SHADOWS) X(IGNORE_LOS) X(MINIMAP_BASE) X(MINIMAP_LINE) X(MINIMAP_LOS) X(MINIMAP_MASK) X(MINIMAP_POINT) X(MODE_SHADOWCAST) X(MODE_SILHOUETTEDISPLAY) X(MODE_SILHOUETTEOCCLUDER) X(MODE_WIREFRAME) X(SYS_HAS_ARB) X(SYS_HAS_GLSL) X(SYS_PREFER_GLSL) X(USE_FANCY_EFFECTS) X(USE_FP_SHADOW) X(USE_GPU_SKINNING) X(USE_INSTANCING) X(USE_NORMALS) X(USE_OBJECTCOLOR) X(USE_REAL_DEPTH) X(USE_REFLECTION) X(USE_REFRACTION) X(USE_SHADOW) X(USE_SHADOW_PCF) X(USE_SHADOW_SAMPLER) X(USE_SHADOWS_ON_WATER) X(USE_FOG) X(WATERTYPE_CLAP) X(WATERTYPE_LAKE) X2(_emptystring, "") X(a_apexPosition) X(a_otherPosition) X(a_retreatPosition) X(a_skinJoints) X(a_skinWeights) X(a_splashPosition) X(a_tangent) X(a_waterInfo) X(ambient) X(baseTex) X(blendTex) X(bloom) X(blurTex2) X(blurTex4) X(blurTex8) X(brightness) X(cameraPos) X(color) X(colorAdd) X(colorMul) +X(debug_overlay) X(delta) X(depthTex) X(foamTex) X(fogColor) X(fogParams) X(foreground_overlay) X(gui_add) X(gui_basic) X(gui_grayscale) X(gui_solid) X(gui_solid_mask) X(gui_text) X(hdr) X(height) X(instancingTransform) X(losMatrix) X(losTex) X(losTex1) X(losTex2) X(losTransform) X(los_interp) X(mapSize) X(maskTex) X(maskTextureTransform) X(minimap) X(modelViewMatrix) X(murkiness) X(normalMap) X(normalMap2) X(objectColor) X(overlay_solid) X(particle) X(particle_solid) X(playerColor) X(pointSize) X(qualityLevel) X(reflectionMap) X(reflectionMatrix) X(refractionMap) X(refractionMatrix) X(renderedTex) X(repeatScale) X2(sans_10, "sans-10"); X(saturation) X(screenSize) X(shadingColor) X(shadowScale) X(shadowTex) X(shadowTransform) X(sharpness) X(skinBlendMatrices) X2(skinBlendMatrices_0, "skinBlendMatrices[0]") X(skyBoxRot) X(skyCube) X(sky_simple) X(sunColor) X(sunDir) X(tex) X(texSize) X(textureTransform) X(time) X(tint) X(transform) X(translation) X(water_simple) X(waterEffectsTex) X(waterTex) X(waveTex) X(waviness) X(waveParams1) X(waveParams2) X(width) X(windAngle) X(zFar) X(zNear) Index: ps/trunk/source/renderer/TerrainOverlay.cpp =================================================================== --- ps/trunk/source/renderer/TerrainOverlay.cpp (revision 24153) +++ ps/trunk/source/renderer/TerrainOverlay.cpp (revision 24154) @@ -1,351 +1,351 @@ /* Copyright (C) 2020 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * 0 A.D. is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with 0 A.D. If not, see . */ #include "precompiled.h" #include "TerrainOverlay.h" #include "graphics/Color.h" #include "graphics/Terrain.h" #include "lib/bits.h" #include "lib/ogl.h" #include "maths/MathUtil.h" #include "ps/Game.h" #include "ps/Profile.h" #include "ps/World.h" #include "renderer/Renderer.h" #include "renderer/TerrainRenderer.h" #include "simulation2/system/SimContext.h" #include // Global overlay list management: static std::vector > g_TerrainOverlayList; ITerrainOverlay::ITerrainOverlay(int priority) { // Add to global list of overlays g_TerrainOverlayList.emplace_back(this, priority); // Sort by overlays by priority. Do stable sort so that adding/removing // overlays doesn't randomly disturb all the existing ones (which would // be noticeable if they have the same priority and overlap). std::stable_sort(g_TerrainOverlayList.begin(), g_TerrainOverlayList.end(), [](const std::pair& a, const std::pair& b) { return a.second < b.second; }); } ITerrainOverlay::~ITerrainOverlay() { std::vector >::iterator newEnd = std::remove_if(g_TerrainOverlayList.begin(), g_TerrainOverlayList.end(), [this](const std::pair& a) { return a.first == this; }); g_TerrainOverlayList.erase(newEnd, g_TerrainOverlayList.end()); } void ITerrainOverlay::RenderOverlaysBeforeWater() { if (g_TerrainOverlayList.empty()) return; PROFILE3_GPU("terrain overlays (before)"); for (size_t i = 0; i < g_TerrainOverlayList.size(); ++i) g_TerrainOverlayList[i].first->RenderBeforeWater(); } void ITerrainOverlay::RenderOverlaysAfterWater(int cullGroup) { if (g_TerrainOverlayList.empty()) return; PROFILE3_GPU("terrain overlays (after)"); for (size_t i = 0; i < g_TerrainOverlayList.size(); ++i) g_TerrainOverlayList[i].first->RenderAfterWater(cullGroup); } ////////////////////////////////////////////////////////////////////////// TerrainOverlay::TerrainOverlay(const CSimContext& simContext, int priority /* = 100 */) : ITerrainOverlay(priority), m_Terrain(&simContext.GetTerrain()) { } void TerrainOverlay::StartRender() { } void TerrainOverlay::EndRender() { } void TerrainOverlay::GetTileExtents( ssize_t& min_i_inclusive, ssize_t& min_j_inclusive, ssize_t& max_i_inclusive, ssize_t& max_j_inclusive) { // Default to whole map min_i_inclusive = min_j_inclusive = 0; max_i_inclusive = max_j_inclusive = m_Terrain->GetTilesPerSide()-1; } void TerrainOverlay::RenderBeforeWater() { if (!m_Terrain) return; // should never happen, but let's play it safe #if CONFIG2_GLES #warning TODO: implement TerrainOverlay::RenderOverlays for GLES #else glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glDepthMask(GL_FALSE); // To ensure that outlines are drawn on top of the terrain correctly (and // don't Z-fight and flicker nastily), draw them as QUADS with the LINE // PolygonMode, and use PolygonOffset to pull them towards the camera. // (See e.g. http://www.opengl.org/resources/faq/technical/polygonoffset.htm) glPolygonOffset(-1.f, -1.f); //glEnable(GL_POLYGON_OFFSET_LINE); glEnable(GL_POLYGON_OFFSET_FILL); pglActiveTextureARB(GL_TEXTURE0); glDisable(GL_TEXTURE_2D); StartRender(); ssize_t min_i, min_j, max_i, max_j; GetTileExtents(min_i, min_j, max_i, max_j); // Clamp the min to 0, but the max to -1 - so tile -1 can never be rendered, // but if unclamped_max<0 then no tiles at all will be rendered. And the same // for the upper limit. min_i = Clamp(min_i, 0, m_Terrain->GetTilesPerSide()); min_j = Clamp(min_j, 0, m_Terrain->GetTilesPerSide()); max_i = Clamp(max_i, -1, m_Terrain->GetTilesPerSide()-1); max_j = Clamp(max_j, -1, m_Terrain->GetTilesPerSide()-1); for (m_j = min_j; m_j <= max_j; ++m_j) for (m_i = min_i; m_i <= max_i; ++m_i) ProcessTile(m_i, m_j); EndRender(); // Clean up state changes glEnable(GL_CULL_FACE); glEnable(GL_DEPTH_TEST); //glDisable(GL_POLYGON_OFFSET_LINE); glDisable(GL_POLYGON_OFFSET_FILL); glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); glDepthMask(GL_TRUE); glDisable(GL_BLEND); #endif } void TerrainOverlay::RenderTile(const CColor& color, bool draw_hidden) { RenderTile(color, draw_hidden, m_i, m_j); } void TerrainOverlay::RenderTile(const CColor& color, bool draw_hidden, ssize_t i, ssize_t j) { // TODO: unnecessary computation calls has been removed but we should use // a vertex buffer or a vertex shader with a texture. // Not sure if it's possible on old OpenGL. if (draw_hidden) { glDisable(GL_DEPTH_TEST); glDisable(GL_CULL_FACE); } else { glEnable(GL_DEPTH_TEST); glEnable(GL_CULL_FACE); } #if CONFIG2_GLES #warning TODO: implement TerrainOverlay::RenderTile for GLES #else glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); CVector3D pos[2][2]; for (int di = 0; di < 2; ++di) for (int dj = 0; dj < 2; ++dj) m_Terrain->CalcPosition(i + di, j + dj, pos[di][dj]); glBegin(GL_TRIANGLES); glColor4fv(color.FloatArray()); if (m_Terrain->GetTriangulationDir(i, j)) { glVertex3fv(pos[0][0].GetFloatArray()); glVertex3fv(pos[1][0].GetFloatArray()); glVertex3fv(pos[0][1].GetFloatArray()); glVertex3fv(pos[1][0].GetFloatArray()); glVertex3fv(pos[1][1].GetFloatArray()); glVertex3fv(pos[0][1].GetFloatArray()); } else { glVertex3fv(pos[0][0].GetFloatArray()); glVertex3fv(pos[1][0].GetFloatArray()); glVertex3fv(pos[1][1].GetFloatArray()); glVertex3fv(pos[1][1].GetFloatArray()); glVertex3fv(pos[0][1].GetFloatArray()); glVertex3fv(pos[0][0].GetFloatArray()); } glEnd(); #endif } void TerrainOverlay::RenderTileOutline(const CColor& color, int line_width, bool draw_hidden) { RenderTileOutline(color, line_width, draw_hidden, m_i, m_j); } void TerrainOverlay::RenderTileOutline(const CColor& color, int line_width, bool draw_hidden, ssize_t i, ssize_t j) { if (draw_hidden) { glDisable(GL_DEPTH_TEST); glDisable(GL_CULL_FACE); } else { glEnable(GL_DEPTH_TEST); glEnable(GL_CULL_FACE); } #if CONFIG2_GLES #warning TODO: implement TerrainOverlay::RenderTileOutline for GLES #else glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); if (line_width != 1) glLineWidth((float)line_width); CVector3D pos; glBegin(GL_QUADS); glColor4fv(color.FloatArray()); m_Terrain->CalcPosition(i, j, pos); glVertex3fv(pos.GetFloatArray()); m_Terrain->CalcPosition(i+1, j, pos); glVertex3fv(pos.GetFloatArray()); m_Terrain->CalcPosition(i+1, j+1, pos); glVertex3fv(pos.GetFloatArray()); m_Terrain->CalcPosition(i, j+1, pos); glVertex3fv(pos.GetFloatArray()); glEnd(); if (line_width != 1) glLineWidth(1.0f); #endif } ////////////////////////////////////////////////////////////////////////// TerrainTextureOverlay::TerrainTextureOverlay(float texelsPerTile, int priority) : ITerrainOverlay(priority), m_TexelsPerTile(texelsPerTile), m_Texture(0), m_TextureW(0), m_TextureH(0) { glGenTextures(1, &m_Texture); } TerrainTextureOverlay::~TerrainTextureOverlay() { glDeleteTextures(1, &m_Texture); } void TerrainTextureOverlay::RenderAfterWater(int cullGroup) { CTerrain* terrain = g_Game->GetWorld()->GetTerrain(); ssize_t w = (ssize_t)(terrain->GetTilesPerSide() * m_TexelsPerTile); ssize_t h = (ssize_t)(terrain->GetTilesPerSide() * m_TexelsPerTile); pglActiveTextureARB(GL_TEXTURE0); // Recreate the texture with new size if necessary if (round_up_to_pow2(w) != m_TextureW || round_up_to_pow2(h) != m_TextureH) { m_TextureW = round_up_to_pow2(w); m_TextureH = round_up_to_pow2(h); glBindTexture(GL_TEXTURE_2D, m_Texture); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_TextureW, m_TextureH, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); } u8* data = (u8*)calloc(w * h, 4); BuildTextureRGBA(data, w, h); glBindTexture(GL_TEXTURE_2D, m_Texture); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, data); free(data); CMatrix3D matrix; matrix.SetZero(); matrix._11 = m_TexelsPerTile / (m_TextureW * TERRAIN_TILE_SIZE); matrix._23 = m_TexelsPerTile / (m_TextureH * TERRAIN_TILE_SIZE); matrix._44 = 1; - g_Renderer.GetTerrainRenderer().RenderTerrainOverlayTexture(cullGroup, matrix); + g_Renderer.GetTerrainRenderer().RenderTerrainOverlayTexture(cullGroup, matrix, m_Texture); } SColor4ub TerrainTextureOverlay::GetColor(size_t idx, u8 alpha) const { static u8 colors[][3] = { { 255, 0, 0 }, { 0, 255, 0 }, { 0, 0, 255 }, { 255, 255, 0 }, { 255, 0, 255 }, { 0, 255, 255 }, { 255, 255, 255 }, { 127, 0, 0 }, { 0, 127, 0 }, { 0, 0, 127 }, { 127, 127, 0 }, { 127, 0, 127 }, { 0, 127, 127 }, { 127, 127, 127}, { 255, 127, 0 }, { 127, 255, 0 }, { 255, 0, 127 }, { 127, 0, 255}, { 0, 255, 127 }, { 0, 127, 255}, { 255, 127, 127}, { 127, 255, 127}, { 127, 127, 255}, { 127, 255, 255 }, { 255, 127, 255 }, { 255, 255, 127 }, }; size_t c = idx % ARRAY_SIZE(colors); return SColor4ub(colors[c][0], colors[c][1], colors[c][2], alpha); } Index: ps/trunk/source/renderer/TerrainRenderer.cpp =================================================================== --- ps/trunk/source/renderer/TerrainRenderer.cpp (revision 24153) +++ ps/trunk/source/renderer/TerrainRenderer.cpp (revision 24154) @@ -1,952 +1,946 @@ /* Copyright (C) 2020 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * 0 A.D. is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with 0 A.D. If not, see . */ /* * Terrain rendering (everything related to patches and water) is * encapsulated in TerrainRenderer */ #include "precompiled.h" #include "graphics/Camera.h" #include "graphics/Decal.h" #include "graphics/LightEnv.h" #include "graphics/LOSTexture.h" #include "graphics/Patch.h" #include "graphics/GameView.h" #include "graphics/Model.h" #include "graphics/ShaderManager.h" #include "renderer/ShadowMap.h" #include "renderer/SkyManager.h" #include "graphics/TerritoryTexture.h" #include "graphics/TextRenderer.h" #include "maths/MathUtil.h" #include "ps/Filesystem.h" #include "ps/CLogger.h" #include "ps/Game.h" #include "ps/Profile.h" #include "ps/World.h" #include "renderer/DecalRData.h" #include "renderer/PatchRData.h" #include "renderer/Renderer.h" #include "renderer/RenderingOptions.h" #include "renderer/ShadowMap.h" #include "renderer/TerrainRenderer.h" #include "renderer/VertexArray.h" #include "renderer/WaterManager.h" #include "tools/atlas/GameInterface/GameLoop.h" extern GameLoopState* g_AtlasGameLoop; /////////////////////////////////////////////////////////////////////////////////////////////// // TerrainRenderer implementation namespace { CShaderProgramPtr GetDummyShader() { const char* shaderName; if (g_RenderingOptions.GetRenderPath() == RenderPath::SHADER) { if (g_RenderingOptions.GetPreferGLSL()) shaderName = "glsl/dummy"; else shaderName = "arb/dummy"; } else shaderName = "fixed:dummy"; return g_Renderer.GetShaderManager().LoadProgram(shaderName, CShaderDefines()); } } // anonymous namespace /** * TerrainRenderer keeps track of which phase it is in, to detect * when Submit, PrepareForRendering etc. are called in the wrong order. */ enum Phase { Phase_Submit, Phase_Render }; /** * Struct TerrainRendererInternals: Internal variables used by the TerrainRenderer class. */ struct TerrainRendererInternals { /// Which phase (submitting or rendering patches) are we in right now? Phase phase; /// Patches that were submitted for this frame std::vector visiblePatches[CRenderer::CULL_MAX]; /// Decals that were submitted for this frame std::vector visibleDecals[CRenderer::CULL_MAX]; /// Fancy water shader CShaderProgramPtr fancyWaterShader; CSimulation2* simulation; }; /////////////////////////////////////////////////////////////////// // Construction/Destruction TerrainRenderer::TerrainRenderer() { m = new TerrainRendererInternals(); m->phase = Phase_Submit; } TerrainRenderer::~TerrainRenderer() { delete m; } void TerrainRenderer::SetSimulation(CSimulation2* simulation) { m->simulation = simulation; } /////////////////////////////////////////////////////////////////// // Submit a patch for rendering void TerrainRenderer::Submit(int cullGroup, CPatch* patch) { ENSURE(m->phase == Phase_Submit); CPatchRData* data = (CPatchRData*)patch->GetRenderData(); if (data == 0) { // no renderdata for patch, create it now data = new CPatchRData(patch, m->simulation); patch->SetRenderData(data); } data->Update(m->simulation); m->visiblePatches[cullGroup].push_back(data); } /////////////////////////////////////////////////////////////////// // Submit a decal for rendering void TerrainRenderer::Submit(int cullGroup, CModelDecal* decal) { ENSURE(m->phase == Phase_Submit); CDecalRData* data = (CDecalRData*)decal->GetRenderData(); if (data == 0) { // no renderdata for decal, create it now data = new CDecalRData(decal, m->simulation); decal->SetRenderData(data); } data->Update(m->simulation); m->visibleDecals[cullGroup].push_back(data); } /////////////////////////////////////////////////////////////////// // Prepare for rendering void TerrainRenderer::PrepareForRendering() { ENSURE(m->phase == Phase_Submit); m->phase = Phase_Render; } /////////////////////////////////////////////////////////////////// // Clear submissions lists void TerrainRenderer::EndFrame() { ENSURE(m->phase == Phase_Render || m->phase == Phase_Submit); for (int i = 0; i < CRenderer::CULL_MAX; ++i) { m->visiblePatches[i].clear(); m->visibleDecals[i].clear(); } m->phase = Phase_Submit; } /////////////////////////////////////////////////////////////////// // Full-featured terrain rendering with blending and everything void TerrainRenderer::RenderTerrainFixed(int cullGroup) { #if CONFIG2_GLES UNUSED2(cullGroup); #else ENSURE(m->phase == Phase_Render); std::vector& visiblePatches = m->visiblePatches[cullGroup]; std::vector& visibleDecals = m->visibleDecals[cullGroup]; if (visiblePatches.empty() && visibleDecals.empty()) return; CShaderProgramPtr dummyShader = GetDummyShader(); dummyShader->Bind(); dummyShader->Uniform(str_transform, g_Renderer.GetViewCamera().GetViewProjection()); dummyShader->Uniform(str_color, CColor(0.0f, 0.0f, 0.0f, 1.0f)); // render the solid black sides of the map first g_Renderer.BindTexture(0, 0); glEnableClientState(GL_VERTEX_ARRAY); glColor3f(0, 0, 0); PROFILE_START("render terrain sides"); for (size_t i = 0; i < visiblePatches.size(); ++i) visiblePatches[i]->RenderSides(dummyShader); PROFILE_END("render terrain sides"); // switch on required client states glEnableClientState(GL_TEXTURE_COORD_ARRAY); // render everything fullbright // set up texture environment for base pass pglActiveTextureARB(GL_TEXTURE0); pglClientActiveTextureARB(GL_TEXTURE0); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_REPLACE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR); // Set alpha to 1.0 glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_CONSTANT); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA); static const float one[4] = { 1.f, 1.f, 1.f, 1.f }; glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, one); PROFILE_START("render terrain base"); CPatchRData::RenderBases(visiblePatches, CShaderDefines(), NULL, true, dummyShader); PROFILE_END("render terrain base"); // render blends // switch on the composite alpha map texture (void)ogl_tex_bind(g_Renderer.m_hCompositeAlphaMap, 1); // switch on second uv set pglClientActiveTextureARB(GL_TEXTURE1); glEnableClientState(GL_TEXTURE_COORD_ARRAY); // setup additional texenv required by blend pass pglActiveTextureARB(GL_TEXTURE1); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_REPLACE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_TEXTURE); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_ONE_MINUS_SRC_ALPHA); // switch on blending glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // no need to write to the depth buffer a second time glDepthMask(0); // The decal color array contains lighting data, which we don't want in this non-shader mode glDisableClientState(GL_COLOR_ARRAY); // render blend passes for each patch PROFILE_START("render terrain blends"); CPatchRData::RenderBlends(visiblePatches, CShaderDefines(), NULL, true, dummyShader); PROFILE_END("render terrain blends"); // Disable second texcoord array pglClientActiveTextureARB(GL_TEXTURE1); glDisableClientState(GL_TEXTURE_COORD_ARRAY); // Render terrain decals g_Renderer.BindTexture(1, 0); pglActiveTextureARB(GL_TEXTURE0); pglClientActiveTextureARB(GL_TEXTURE0); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_TEXTURE); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_TEXTURE); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA); PROFILE_START("render terrain decals"); CDecalRData::RenderDecals(visibleDecals, CShaderDefines(), NULL, true, dummyShader); PROFILE_END("render terrain decals"); // Now apply lighting const CLightEnv& lightEnv = g_Renderer.GetLightEnv(); pglClientActiveTextureARB(GL_TEXTURE0); glEnableClientState(GL_COLOR_ARRAY); // diffuse lighting colors // The vertex color is scaled by 0.5 to permit overbrightness without clamping. // We therefore need to draw Clamp((texture*lighting)*2.0), where 'texture' // is what previous passes drew onto the framebuffer, and 'lighting' is the // color computed by this pass. // We can do that with blending by getting it to draw dst*src + src*dst: glBlendFunc(GL_DST_COLOR, GL_SRC_COLOR); // Scale the ambient color by 0.5 to match the vertex diffuse colors float terrainAmbientColor[4] = { lightEnv.m_TerrainAmbientColor.X * 0.5f, lightEnv.m_TerrainAmbientColor.Y * 0.5f, lightEnv.m_TerrainAmbientColor.Z * 0.5f, 1.f }; CLOSTexture& losTexture = g_Renderer.GetScene().GetLOSTexture(); int streamflags = STREAM_POS; pglActiveTextureARB(GL_TEXTURE0); // We're not going to use a texture here, but we have to have a valid texture // bound else the texture unit will be disabled. // We should still have a bound splat texture from some earlier rendering, // so assume that's still valid to use. // (TODO: That's a bit of an ugly hack.) // No shadows: (Ambient + Diffuse) * LOS glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_ADD); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_CONSTANT); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_PREVIOUS); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA); glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, terrainAmbientColor); losTexture.BindTexture(1); pglClientActiveTextureARB(GL_TEXTURE1); glEnableClientState(GL_TEXTURE_COORD_ARRAY); streamflags |= STREAM_POSTOUV1; glMatrixMode(GL_TEXTURE); glLoadMatrixf(&losTexture.GetTextureMatrix()._11); glMatrixMode(GL_MODELVIEW); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_TEXTURE); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_ALPHA); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_PREVIOUS); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA); pglActiveTextureARB(GL_TEXTURE0); pglClientActiveTextureARB(GL_TEXTURE0); PROFILE_START("render terrain streams"); CPatchRData::RenderStreams(visiblePatches, dummyShader, streamflags); PROFILE_END("render terrain streams"); glMatrixMode(GL_TEXTURE); glLoadIdentity(); glMatrixMode(GL_MODELVIEW); // restore OpenGL state g_Renderer.BindTexture(1, 0); pglClientActiveTextureARB(GL_TEXTURE1); glDisableClientState(GL_TEXTURE_COORD_ARRAY); glMatrixMode(GL_TEXTURE); glLoadIdentity(); glMatrixMode(GL_MODELVIEW); pglClientActiveTextureARB(GL_TEXTURE0); pglActiveTextureARB(GL_TEXTURE0); glDepthMask(1); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glDisable(GL_BLEND); glDisableClientState(GL_COLOR_ARRAY); glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_TEXTURE_COORD_ARRAY); dummyShader->Unbind(); #endif } -void TerrainRenderer::RenderTerrainOverlayTexture(int cullGroup, CMatrix3D& textureMatrix) +void TerrainRenderer::RenderTerrainOverlayTexture(int cullGroup, CMatrix3D& textureMatrix, GLuint texture) { #if CONFIG2_GLES #warning TODO: implement TerrainRenderer::RenderTerrainOverlayTexture for GLES UNUSED2(cullGroup); UNUSED2(textureMatrix); + UNUSED2(texture); #else ENSURE(m->phase == Phase_Render); std::vector& visiblePatches = m->visiblePatches[cullGroup]; - glEnableClientState(GL_VERTEX_ARRAY); - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - - pglActiveTextureARB(GL_TEXTURE0); glEnable(GL_TEXTURE_2D); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glDepthMask(0); glDisable(GL_DEPTH_TEST); - glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); - - glMatrixMode(GL_TEXTURE); - glLoadMatrixf(&textureMatrix._11); - glMatrixMode(GL_MODELVIEW); + CShaderTechniquePtr debugOverlayTech = + g_Renderer.GetShaderManager().LoadEffect(str_debug_overlay); + debugOverlayTech->BeginPass(); + CShaderProgramPtr debugOverlayShader = debugOverlayTech->GetShader(); + + debugOverlayShader->Bind(); + debugOverlayShader->BindTexture(str_baseTex, texture); + debugOverlayShader->Uniform(str_transform, g_Renderer.GetViewCamera().GetViewProjection()); + debugOverlayShader->Uniform(str_textureTransform, textureMatrix); + CPatchRData::RenderStreams(visiblePatches, debugOverlayShader, STREAM_POS | STREAM_POSTOUV0); - CShaderProgramPtr dummyShader = GetDummyShader(); - dummyShader->Bind(); - dummyShader->Uniform(str_transform, g_Renderer.GetViewCamera().GetViewProjection()); - dummyShader->Uniform(str_color, CColor(0.0f, 0.0f, 0.0f, 1.0f)); - CPatchRData::RenderStreams(visiblePatches, dummyShader, STREAM_POS | STREAM_POSTOUV0); - dummyShader->Unbind(); + glEnable(GL_DEPTH_TEST); // To make the overlay visible over water, render an additional map-sized - // water-height patch + // water-height patch. CBoundingBoxAligned waterBounds; - for (size_t i = 0; i < visiblePatches.size(); ++i) - { - CPatchRData* data = visiblePatches[i]; + for (CPatchRData* data : visiblePatches) waterBounds += data->GetWaterBounds(); - } if (!waterBounds.IsEmpty()) { - float h = g_Renderer.GetWaterManager()->m_WaterHeight + 0.05f; // add a delta to avoid z-fighting - float waterPos[] = { - waterBounds[0].X, h, waterBounds[0].Z, - waterBounds[1].X, h, waterBounds[0].Z, - waterBounds[0].X, h, waterBounds[1].Z, - waterBounds[1].X, h, waterBounds[1].Z + // Add a delta to avoid z-fighting. + const float height = g_Renderer.GetWaterManager()->m_WaterHeight + 0.05f; + const float waterPos[] = { + waterBounds[0].X, height, waterBounds[0].Z, + waterBounds[1].X, height, waterBounds[0].Z, + waterBounds[0].X, height, waterBounds[1].Z, + waterBounds[1].X, height, waterBounds[1].Z }; - glVertexPointer(3, GL_FLOAT, 3*sizeof(float), waterPos); - glTexCoordPointer(3, GL_FLOAT, 3*sizeof(float), waterPos); + + const GLsizei stride = sizeof(float) * 3; + debugOverlayShader->VertexPointer(3, GL_FLOAT, stride, waterPos); + debugOverlayShader->TexCoordPointer(GL_TEXTURE0, 3, GL_FLOAT, stride, waterPos); + debugOverlayShader->AssertPointersBound(); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); } - glMatrixMode(GL_TEXTURE); - glLoadIdentity(); - glMatrixMode(GL_MODELVIEW); + debugOverlayShader->Unbind(); + debugOverlayTech->EndPass(); glDepthMask(1); - glEnable(GL_DEPTH_TEST); glDisable(GL_BLEND); - glDisableClientState(GL_COLOR_ARRAY); - glDisableClientState(GL_VERTEX_ARRAY); - glDisableClientState(GL_TEXTURE_COORD_ARRAY); #endif } /////////////////////////////////////////////////////////////////// /** * Set up all the uniforms for a shader pass. */ void TerrainRenderer::PrepareShader(const CShaderProgramPtr& shader, ShadowMap* shadow) { shader->Uniform(str_transform, g_Renderer.GetViewCamera().GetViewProjection()); shader->Uniform(str_cameraPos, g_Renderer.GetViewCamera().GetOrientation().GetTranslation()); const CLightEnv& lightEnv = g_Renderer.GetLightEnv(); if (shadow) { shader->BindTexture(str_shadowTex, shadow->GetTexture()); shader->Uniform(str_shadowTransform, shadow->GetTextureMatrix()); int width = shadow->GetWidth(); int height = shadow->GetHeight(); shader->Uniform(str_shadowScale, width, height, 1.0f / width, 1.0f / height); } CLOSTexture& los = g_Renderer.GetScene().GetLOSTexture(); shader->BindTexture(str_losTex, los.GetTextureSmooth()); shader->Uniform(str_losTransform, los.GetTextureMatrix()[0], los.GetTextureMatrix()[12], 0.f, 0.f); shader->Uniform(str_ambient, lightEnv.m_TerrainAmbientColor); shader->Uniform(str_sunColor, lightEnv.m_SunColor); shader->Uniform(str_sunDir, lightEnv.GetSunDir()); shader->Uniform(str_fogColor, lightEnv.m_FogColor); shader->Uniform(str_fogParams, lightEnv.m_FogFactor, lightEnv.m_FogMax, 0.f, 0.f); } void TerrainRenderer::RenderTerrainShader(const CShaderDefines& context, int cullGroup, ShadowMap* shadow) { ENSURE(m->phase == Phase_Render); std::vector& visiblePatches = m->visiblePatches[cullGroup]; std::vector& visibleDecals = m->visibleDecals[cullGroup]; if (visiblePatches.empty() && visibleDecals.empty()) return; // render the solid black sides of the map first CShaderTechniquePtr techSolid = g_Renderer.GetShaderManager().LoadEffect(str_gui_solid); techSolid->BeginPass(); CShaderProgramPtr shaderSolid = techSolid->GetShader(); shaderSolid->Uniform(str_transform, g_Renderer.GetViewCamera().GetViewProjection()); shaderSolid->Uniform(str_color, 0.0f, 0.0f, 0.0f, 1.0f); PROFILE_START("render terrain sides"); for (size_t i = 0; i < visiblePatches.size(); ++i) visiblePatches[i]->RenderSides(shaderSolid); PROFILE_END("render terrain sides"); techSolid->EndPass(); PROFILE_START("render terrain base"); CPatchRData::RenderBases(visiblePatches, context, shadow); PROFILE_END("render terrain base"); // no need to write to the depth buffer a second time glDepthMask(0); // render blend passes for each patch PROFILE_START("render terrain blends"); CPatchRData::RenderBlends(visiblePatches, context, shadow, false); PROFILE_END("render terrain blends"); PROFILE_START("render terrain decals"); CDecalRData::RenderDecals(visibleDecals, context, shadow, false); PROFILE_END("render terrain decals"); // restore OpenGL state g_Renderer.BindTexture(1, 0); g_Renderer.BindTexture(2, 0); g_Renderer.BindTexture(3, 0); glDepthMask(1); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glDisable(GL_BLEND); } /////////////////////////////////////////////////////////////////// // Render un-textured patches as polygons void TerrainRenderer::RenderPatches(int cullGroup, const CColor& color) { ENSURE(m->phase == Phase_Render); std::vector& visiblePatches = m->visiblePatches[cullGroup]; if (visiblePatches.empty()) return; #if CONFIG2_GLES #warning TODO: implement TerrainRenderer::RenderPatches for GLES #else CShaderProgramPtr dummyShader = GetDummyShader(); dummyShader->Bind(); dummyShader->Uniform(str_transform, g_Renderer.GetViewCamera().GetViewProjection()); dummyShader->Uniform(str_color, color); glEnableClientState(GL_VERTEX_ARRAY); CPatchRData::RenderStreams(visiblePatches, dummyShader, STREAM_POS); glDisableClientState(GL_VERTEX_ARRAY); dummyShader->Unbind(); #endif } /////////////////////////////////////////////////////////////////// // Render outlines of submitted patches as lines void TerrainRenderer::RenderOutlines(int cullGroup) { ENSURE(m->phase == Phase_Render); std::vector& visiblePatches = m->visiblePatches[cullGroup]; if (visiblePatches.empty()) return; #if CONFIG2_GLES #warning TODO: implement TerrainRenderer::RenderOutlines for GLES #else glEnableClientState(GL_VERTEX_ARRAY); for (size_t i = 0; i < visiblePatches.size(); ++i) visiblePatches[i]->RenderOutline(); glDisableClientState(GL_VERTEX_ARRAY); #endif } /////////////////////////////////////////////////////////////////// // Scissor rectangle of water patches CBoundingBoxAligned TerrainRenderer::ScissorWater(int cullGroup, const CMatrix3D &viewproj) { std::vector& visiblePatches = m->visiblePatches[cullGroup]; CBoundingBoxAligned scissor; for (size_t i = 0; i < visiblePatches.size(); ++i) { CPatchRData* data = visiblePatches[i]; const CBoundingBoxAligned& waterBounds = data->GetWaterBounds(); if (waterBounds.IsEmpty()) continue; CVector4D v1 = viewproj.Transform(CVector4D(waterBounds[0].X, waterBounds[1].Y, waterBounds[0].Z, 1.0f)); CVector4D v2 = viewproj.Transform(CVector4D(waterBounds[1].X, waterBounds[1].Y, waterBounds[0].Z, 1.0f)); CVector4D v3 = viewproj.Transform(CVector4D(waterBounds[0].X, waterBounds[1].Y, waterBounds[1].Z, 1.0f)); CVector4D v4 = viewproj.Transform(CVector4D(waterBounds[1].X, waterBounds[1].Y, waterBounds[1].Z, 1.0f)); CBoundingBoxAligned screenBounds; #define ADDBOUND(v1, v2, v3, v4) \ if (v1.Z >= -v1.W) \ screenBounds += CVector3D(v1.X, v1.Y, v1.Z) * (1.0f / v1.W); \ else \ { \ float t = v1.Z + v1.W; \ if (v2.Z > -v2.W) \ { \ CVector4D c2 = v1 + (v2 - v1) * (t / (t - (v2.Z + v2.W))); \ screenBounds += CVector3D(c2.X, c2.Y, c2.Z) * (1.0f / c2.W); \ } \ if (v3.Z > -v3.W) \ { \ CVector4D c3 = v1 + (v3 - v1) * (t / (t - (v3.Z + v3.W))); \ screenBounds += CVector3D(c3.X, c3.Y, c3.Z) * (1.0f / c3.W); \ } \ if (v4.Z > -v4.W) \ { \ CVector4D c4 = v1 + (v4 - v1) * (t / (t - (v4.Z + v4.W))); \ screenBounds += CVector3D(c4.X, c4.Y, c4.Z) * (1.0f / c4.W); \ } \ } ADDBOUND(v1, v2, v3, v4); ADDBOUND(v2, v1, v3, v4); ADDBOUND(v3, v1, v2, v4); ADDBOUND(v4, v1, v2, v3); #undef ADDBOUND if (screenBounds[0].X >= 1.0f || screenBounds[1].X <= -1.0f || screenBounds[0].Y >= 1.0f || screenBounds[1].Y <= -1.0f) continue; scissor += screenBounds; } return CBoundingBoxAligned(CVector3D(Clamp(scissor[0].X, -1.0f, 1.0f), Clamp(scissor[0].Y, -1.0f, 1.0f), -1.0f), CVector3D(Clamp(scissor[1].X, -1.0f, 1.0f), Clamp(scissor[1].Y, -1.0f, 1.0f), 1.0f)); } // Render fancy water bool TerrainRenderer::RenderFancyWater(const CShaderDefines& context, int cullGroup, ShadowMap* shadow) { PROFILE3_GPU("fancy water"); WaterManager* WaterMgr = g_Renderer.GetWaterManager(); CShaderDefines defines = context; // If we're using fancy water, make sure its shader is loaded if (!m->fancyWaterShader || WaterMgr->m_NeedsReloading) { if (WaterMgr->m_WaterRealDepth) defines.Add(str_USE_REAL_DEPTH, str_1); if (WaterMgr->m_WaterFancyEffects) defines.Add(str_USE_FANCY_EFFECTS, str_1); if (WaterMgr->m_WaterRefraction) defines.Add(str_USE_REFRACTION, str_1); if (WaterMgr->m_WaterReflection) defines.Add(str_USE_REFLECTION, str_1); if (shadow && WaterMgr->m_WaterShadows) defines.Add(str_USE_SHADOWS_ON_WATER, str_1); // haven't updated the ARB shader yet so I'll always load the GLSL /*if (!g_RenderingOptions.GetPreferGLSL() && !superFancy) m->fancyWaterShader = g_Renderer.GetShaderManager().LoadProgram("arb/water_high", defines); else*/ m->fancyWaterShader = g_Renderer.GetShaderManager().LoadProgram("glsl/water_high", defines); if (!m->fancyWaterShader) { LOGERROR("Failed to load water shader. Falling back to fixed pipeline water.\n"); WaterMgr->m_RenderWater = false; return false; } WaterMgr->m_NeedsReloading = false; } CLOSTexture& losTexture = g_Renderer.GetScene().GetLOSTexture(); // creating the real depth texture using the depth buffer. if (WaterMgr->m_WaterRealDepth) { if (WaterMgr->m_depthTT == 0) { GLuint depthTex; glGenTextures(1, (GLuint*)&depthTex); WaterMgr->m_depthTT = depthTex; glBindTexture(GL_TEXTURE_2D, WaterMgr->m_depthTT); glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, g_Renderer.GetWidth(), g_Renderer.GetHeight(), 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE,NULL); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); } else { glBindTexture(GL_TEXTURE_2D, WaterMgr->m_depthTT); glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, 0, 0, g_Renderer.GetWidth(), g_Renderer.GetHeight(), 0); } glBindTexture(GL_TEXTURE_2D, 0); } // Calculating the advanced informations about Foam and all if the quality calls for it. /*if (WaterMgr->m_NeedInfoUpdate && (WaterMgr->m_WaterFoam || WaterMgr->m_WaterCoastalWaves)) { WaterMgr->m_NeedInfoUpdate = false; WaterMgr->CreateSuperfancyInfo(); }*/ double time = WaterMgr->m_WaterTexTimer; double period = 8; int curTex = (int)(time*60/period) % 60; int nexTex = (curTex + 1) % 60; float repeatPeriod = WaterMgr->m_RepeatPeriod; // Render normals and foam to a framebuffer if we're in fancy effects if (WaterMgr->m_WaterFancyEffects) { // Save the post-processing framebuffer. GLint fbo; glGetIntegerv(GL_FRAMEBUFFER_BINDING_EXT, &fbo); pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, WaterMgr->m_FancyEffectsFBO); glDisable(GL_BLEND); glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LEQUAL); glDisable(GL_CULL_FACE); // Overwrite waves that would be behind the ground. CShaderProgramPtr dummyShader = g_Renderer.GetShaderManager().LoadProgram("glsl/gui_solid", CShaderDefines()); dummyShader->Bind(); dummyShader->Uniform(str_transform, g_Renderer.GetViewCamera().GetViewProjection()); dummyShader->Uniform(str_color, 0.0f, 0.0f, 0.0f, 0.0f); std::vector& visiblePatches = m->visiblePatches[cullGroup]; for (size_t i = 0; i < visiblePatches.size(); ++i) { CPatchRData* data = visiblePatches[i]; data->RenderWater(dummyShader, true, true); } dummyShader->Unbind(); glEnable(GL_CULL_FACE); pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo); } glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LEQUAL); m->fancyWaterShader->Bind(); const CCamera& camera = g_Renderer.GetViewCamera(); m->fancyWaterShader->BindTexture(str_normalMap, WaterMgr->m_NormalMap[curTex]); m->fancyWaterShader->BindTexture(str_normalMap2, WaterMgr->m_NormalMap[nexTex]); if (WaterMgr->m_WaterFancyEffects) { m->fancyWaterShader->BindTexture(str_waterEffectsTex, WaterMgr->m_FancyTexture); } if (WaterMgr->m_WaterRealDepth) m->fancyWaterShader->BindTexture(str_depthTex, WaterMgr->m_depthTT); if (WaterMgr->m_WaterRefraction) m->fancyWaterShader->BindTexture(str_refractionMap, WaterMgr->m_RefractionTexture); if (WaterMgr->m_WaterReflection) m->fancyWaterShader->BindTexture(str_reflectionMap, WaterMgr->m_ReflectionTexture); m->fancyWaterShader->BindTexture(str_losTex, losTexture.GetTextureSmooth()); const CLightEnv& lightEnv = g_Renderer.GetLightEnv(); m->fancyWaterShader->Uniform(str_transform, g_Renderer.GetViewCamera().GetViewProjection()); //TODO: bind only what's needed if (WaterMgr->m_WaterRefraction || WaterMgr->m_WaterReflection) { m->fancyWaterShader->BindTexture(str_skyCube, g_Renderer.GetSkyManager()->GetSkyCube()); // TODO: check that this rotates in the right direction. CMatrix3D skyBoxRotation; skyBoxRotation.SetIdentity(); skyBoxRotation.RotateY(M_PI + lightEnv.GetRotation()); m->fancyWaterShader->Uniform(str_skyBoxRot, skyBoxRotation); if (WaterMgr->m_WaterRefraction) m->fancyWaterShader->Uniform(str_refractionMatrix, WaterMgr->m_RefractionMatrix); if (WaterMgr->m_WaterReflection) m->fancyWaterShader->Uniform(str_reflectionMatrix, WaterMgr->m_ReflectionMatrix); } m->fancyWaterShader->Uniform(str_ambient, lightEnv.m_TerrainAmbientColor); m->fancyWaterShader->Uniform(str_sunDir, lightEnv.GetSunDir()); m->fancyWaterShader->Uniform(str_sunColor, lightEnv.m_SunColor); m->fancyWaterShader->Uniform(str_color, WaterMgr->m_WaterColor); m->fancyWaterShader->Uniform(str_tint, WaterMgr->m_WaterTint); m->fancyWaterShader->Uniform(str_waviness, WaterMgr->m_Waviness); m->fancyWaterShader->Uniform(str_murkiness, WaterMgr->m_Murkiness); m->fancyWaterShader->Uniform(str_windAngle, WaterMgr->m_WindAngle); m->fancyWaterShader->Uniform(str_repeatScale, 1.0f / repeatPeriod); m->fancyWaterShader->Uniform(str_losMatrix, losTexture.GetTextureMatrix()); m->fancyWaterShader->Uniform(str_cameraPos, camera.GetOrientation().GetTranslation()); if (WaterMgr->m_WaterRealDepth) { m->fancyWaterShader->Uniform(str_zNear, camera.GetNearPlane()); m->fancyWaterShader->Uniform(str_zFar, camera.GetFarPlane()); } m->fancyWaterShader->Uniform(str_fogColor, lightEnv.m_FogColor); m->fancyWaterShader->Uniform(str_fogParams, lightEnv.m_FogFactor, lightEnv.m_FogMax, 0.f, 0.f); m->fancyWaterShader->Uniform(str_time, (float)time); m->fancyWaterShader->Uniform(str_screenSize, (float)g_Renderer.GetWidth(), (float)g_Renderer.GetHeight(), 0.0f, 0.0f); if (WaterMgr->m_WaterType == L"clap") { m->fancyWaterShader->Uniform(str_waveParams1, 30.0f,1.5f,20.0f,0.03f); m->fancyWaterShader->Uniform(str_waveParams2, 0.5f,0.0f,0.0f,0.0f); } else if (WaterMgr->m_WaterType == L"lake") { m->fancyWaterShader->Uniform(str_waveParams1, 8.5f,1.5f,15.0f,0.03f); m->fancyWaterShader->Uniform(str_waveParams2, 0.2f,0.0f,0.0f,0.07f); } else { m->fancyWaterShader->Uniform(str_waveParams1, 15.0f,0.8f,10.0f,0.1f); m->fancyWaterShader->Uniform(str_waveParams2, 0.3f,0.0f,0.1f,0.3f); } if (shadow && WaterMgr->m_WaterShadows) { m->fancyWaterShader->BindTexture(str_shadowTex, shadow->GetTexture()); m->fancyWaterShader->Uniform(str_shadowTransform, shadow->GetTextureMatrix()); int width = shadow->GetWidth(); int height = shadow->GetHeight(); m->fancyWaterShader->Uniform(str_shadowScale, width, height, 1.0f / width, 1.0f / height); } std::vector& visiblePatches = m->visiblePatches[cullGroup]; for (size_t i = 0; i < visiblePatches.size(); ++i) { CPatchRData* data = visiblePatches[i]; data->RenderWater(m->fancyWaterShader); } m->fancyWaterShader->Unbind(); glDepthFunc(GL_LEQUAL); glDisable(GL_BLEND); return true; } void TerrainRenderer::RenderSimpleWater(int cullGroup) { #if CONFIG2_GLES UNUSED2(cullGroup); #else PROFILE3_GPU("simple water"); WaterManager* WaterMgr = g_Renderer.GetWaterManager(); CLOSTexture& losTexture = g_Game->GetView()->GetLOSTexture(); glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LEQUAL); double time = WaterMgr->m_WaterTexTimer; double period = 1.6f; int curTex = (int)(time*60/period) % 60; CShaderTechniquePtr waterSimpleTech = g_Renderer.GetShaderManager().LoadEffect(str_water_simple); waterSimpleTech->BeginPass(); CShaderProgramPtr waterSimpleShader = waterSimpleTech->GetShader(); waterSimpleShader->Bind(); waterSimpleShader->BindTexture(str_baseTex, WaterMgr->m_WaterTexture[curTex]); waterSimpleShader->BindTexture(str_losTex, losTexture.GetTextureSmooth()); waterSimpleShader->Uniform(str_transform, g_Renderer.GetViewCamera().GetViewProjection()); waterSimpleShader->Uniform(str_losMatrix, losTexture.GetTextureMatrix()); waterSimpleShader->Uniform(str_time, static_cast(time)); waterSimpleShader->Uniform(str_color, WaterMgr->m_WaterColor); glEnableClientState(GL_VERTEX_ARRAY); std::vector& visiblePatches = m->visiblePatches[cullGroup]; for (size_t i = 0; i < visiblePatches.size(); ++i) { CPatchRData* data = visiblePatches[i]; data->RenderWater(waterSimpleShader, false, true); } glDisableClientState(GL_VERTEX_ARRAY); waterSimpleShader->Unbind(); g_Renderer.BindTexture(1, 0); pglActiveTextureARB(GL_TEXTURE0_ARB); glDisable(GL_TEXTURE_2D); waterSimpleTech->EndPass(); #endif } /////////////////////////////////////////////////////////////////// // Render water that is part of the terrain void TerrainRenderer::RenderWater(const CShaderDefines& context, int cullGroup, ShadowMap* shadow) { WaterManager* WaterMgr = g_Renderer.GetWaterManager(); WaterMgr->UpdateQuality(); if (!WaterMgr->WillRenderFancyWater()) RenderSimpleWater(cullGroup); else RenderFancyWater(context, cullGroup, shadow); } void TerrainRenderer::RenderPriorities(int cullGroup) { PROFILE("priorities"); ENSURE(m->phase == Phase_Render); CShaderTechniquePtr tech = g_Renderer.GetShaderManager().LoadEffect(str_gui_text); tech->BeginPass(); CTextRenderer textRenderer(tech->GetShader()); textRenderer.Font(CStrIntern("mono-stroke-10")); textRenderer.Color(1.0f, 1.0f, 0.0f); std::vector& visiblePatches = m->visiblePatches[cullGroup]; for (size_t i = 0; i < visiblePatches.size(); ++i) visiblePatches[i]->RenderPriorities(textRenderer); textRenderer.Render(); tech->EndPass(); } Index: ps/trunk/source/renderer/TerrainRenderer.h =================================================================== --- ps/trunk/source/renderer/TerrainRenderer.h (revision 24153) +++ ps/trunk/source/renderer/TerrainRenderer.h (revision 24154) @@ -1,167 +1,167 @@ /* Copyright (C) 2020 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * 0 A.D. is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with 0 A.D. If not, see . */ /* * Terrain rendering (everything related to patches and water) is * encapsulated in TerrainRenderer */ #ifndef INCLUDED_TERRAINRENDERER #define INCLUDED_TERRAINRENDERER #include "maths/BoundingBoxAligned.h" class CPatch; class CSimulation2; class ShadowMap; class WaterManager; struct TerrainRendererInternals; /** * Class TerrainRenderer: Render everything related to the terrain, * especially patches and water. */ class TerrainRenderer { friend class CPatchRData; friend class CDecalRData; public: TerrainRenderer(); ~TerrainRenderer(); /** * Set the simulation context for this frame. * Call at start of frame, before any other Submits. */ void SetSimulation(CSimulation2* simulation); /** * Submit: Add a patch for rendering in this frame. * * preconditions : PrepareForRendering must not have been called * for this frame yet. * The patch must not have been submitted in this frame yet (i.e. you * can only submit a frame once). * * @param patch the patch */ void Submit(int cullGroup, CPatch* patch); /** * Submit: Add a terrain decal for rendering in this frame. */ void Submit(int cullGroup, CModelDecal* decal); /** * PrepareForRendering: Prepare internal data structures like vertex * buffers for rendering. * * All patches must have been submitted before the call to * PrepareForRendering. * PrepareForRendering must be called before any rendering calls. */ void PrepareForRendering(); /** * EndFrame: Remove all patches from the list of submitted patches. */ void EndFrame(); /** * RenderTerrain: Render textured terrain (including blends between * different terrain types). * * preconditions : PrepareForRendering must have been called this * frame before calling RenderTerrain. */ void RenderTerrainFixed(int cullGroup); /** * Render textured terrain, as with RenderTerrainFixed, but using shaders * instead of multitexturing. * * @param shadow A prepared shadow map, in case rendering with shadows is enabled. */ void RenderTerrainShader(const CShaderDefines& context, int cullGroup, ShadowMap* shadow); /** * RenderPatches: Render all patches un-textured as polygons. * * preconditions : PrepareForRendering must have been called this * frame before calling RenderPatches. * * @param filtered If true then only render objects that passed CullPatches. * @param color Fill color of the patches. */ void RenderPatches(int cullGroup, const CColor& color = CColor(0.0f, 0.0f, 0.0f, 1.0f)); /** * RenderOutlines: Render the outline of patches as lines. * * preconditions : PrepareForRendering must have been called this * frame before calling RenderOutlines. * * @param filtered If true then only render objects that passed CullPatches. */ void RenderOutlines(int cullGroup); /** * RenderWater: Render water for all patches that have been submitted * this frame. * * preconditions : PrepareForRendering must have been called this * frame before calling RenderWater. */ void RenderWater(const CShaderDefines& context, int cullGroup, ShadowMap* shadow); /** * Calculate a scissor rectangle for the visible water patches. */ CBoundingBoxAligned ScissorWater(int cullGroup, const CMatrix3D& viewproj); /** * Render priority text for all submitted patches, for debugging. */ void RenderPriorities(int cullGroup); /** * Render texture unit 0 over the terrain mesh, with UV coords calculated * by the given texture matrix. * Intended for use by TerrainTextureOverlay. */ - void RenderTerrainOverlayTexture(int cullGroup, CMatrix3D& textureMatrix); + void RenderTerrainOverlayTexture(int cullGroup, CMatrix3D& textureMatrix, GLuint texture); private: TerrainRendererInternals* m; /** * RenderFancyWater: internal rendering method for fancy water. * Returns false if unable to render with fancy water. */ bool RenderFancyWater(const CShaderDefines& context, int cullGroup, ShadowMap* shadow); /** * RenderSimpleWater: internal rendering method for water */ void RenderSimpleWater(int cullGroup); static void PrepareShader(const CShaderProgramPtr& shader, ShadowMap* shadow); }; #endif // INCLUDED_TERRAINRENDERER