1
0
forked from 0ad/0ad
0ad/source/renderer/TerrainRenderer.cpp

814 lines
27 KiB
C++
Raw Normal View History

/* Copyright (C) 2012 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 <http://www.gnu.org/licenses/>.
*/
/*
* 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/Terrain.h"
#include "graphics/GameView.h"
#include "graphics/Model.h"
#include "graphics/ShaderManager.h"
#include "graphics/TerritoryTexture.h"
#include "maths/MathUtil.h"
#include "ps/Filesystem.h"
#include "ps/CLogger.h"
#include "ps/Font.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/ShadowMap.h"
#include "renderer/TerrainRenderer.h"
#include "renderer/VertexArray.h"
#include "renderer/WaterManager.h"
///////////////////////////////////////////////////////////////////////////////////////////////
// TerrainRenderer implementation
/**
* 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<CPatchRData*> visiblePatches;
std::vector<CPatchRData*> filteredPatches;
/// Decals that were submitted for this frame
std::vector<CDecalRData*> visibleDecals;
std::vector<CDecalRData*> filteredDecals;
/// Fancy water shader
CShaderProgramPtr fancyWaterShader;
};
///////////////////////////////////////////////////////////////////
// Construction/Destruction
TerrainRenderer::TerrainRenderer()
{
m = new TerrainRendererInternals();
m->phase = Phase_Submit;
}
TerrainRenderer::~TerrainRenderer()
{
delete m;
}
///////////////////////////////////////////////////////////////////
// Submit a patch for rendering
void TerrainRenderer::Submit(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);
patch->SetRenderData(data);
}
data->Update();
m->visiblePatches.push_back(data);
}
///////////////////////////////////////////////////////////////////
// Submit a decal for rendering
void TerrainRenderer::Submit(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);
decal->SetRenderData(data);
}
data->Update();
m->visibleDecals.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);
m->visiblePatches.clear();
m->visibleDecals.clear();
m->phase = Phase_Submit;
}
///////////////////////////////////////////////////////////////////
// Culls patches and decals against a frustum.
bool TerrainRenderer::CullPatches(const CFrustum* frustum)
{
m->filteredPatches.clear();
for (std::vector<CPatchRData*>::iterator it = m->visiblePatches.begin(); it != m->visiblePatches.end(); ++it)
{
if (frustum->IsBoxVisible(CVector3D(0, 0, 0), (*it)->GetPatch()->GetWorldBounds()))
m->filteredPatches.push_back(*it);
}
m->filteredDecals.clear();
for (std::vector<CDecalRData*>::iterator it = m->visibleDecals.begin(); it != m->visibleDecals.end(); ++it)
{
if (frustum->IsBoxVisible(CVector3D(0, 0, 0), (*it)->GetDecal()->GetWorldBounds()))
m->filteredDecals.push_back(*it);
}
return !m->filteredPatches.empty() || !m->filteredDecals.empty();
}
///////////////////////////////////////////////////////////////////
// Full-featured terrain rendering with blending and everything
void TerrainRenderer::RenderTerrain(bool filtered)
{
ENSURE(m->phase == Phase_Render);
std::vector<CPatchRData*>& visiblePatches = filtered ? m->filteredPatches : m->visiblePatches;
std::vector<CDecalRData*>& visibleDecals = filtered ? m->filteredDecals : m->visibleDecals;
if (visiblePatches.empty() && visibleDecals.empty())
return;
// 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();
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, CShaderProgramPtr());
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, CShaderProgramPtr());
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");
for (size_t i = 0; i < visibleDecals.size(); ++i)
visibleDecals[i]->Render(CShaderProgramPtr());
PROFILE_END("render terrain decals");
// Now apply lighting
const CLightEnv& lightEnv = g_Renderer.GetLightEnv();
pglClientActiveTextureARB(GL_TEXTURE0);
glEnableClientState(GL_COLOR_ARRAY); // diffuse lighting colours
glBlendFunc(GL_DST_COLOR, GL_ZERO);
// GL_TEXTURE_ENV_COLOR requires four floats, so we shouldn't use the RGBColor directly
float terrainAmbientColor[4] = {
lightEnv.m_TerrainAmbientColor.X,
lightEnv.m_TerrainAmbientColor.Y,
lightEnv.m_TerrainAmbientColor.Z,
1.f
};
CLOSTexture& losTexture = g_Renderer.GetScene().GetLOSTexture();
int streamflags = STREAM_POS|STREAM_COLOR;
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, 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);
}
///////////////////////////////////////////////////////////////////
/**
* Set up all the uniforms for a shader pass.
*/
void TerrainRenderer::PrepareShader(const CShaderProgramPtr& shader, ShadowMap* shadow)
{
const CLightEnv& lightEnv = g_Renderer.GetLightEnv();
if (shadow)
{
shader->BindTexture("shadowTex", shadow->GetTexture());
shader->Uniform("shadowTransform", shadow->GetTextureMatrix());
const float* offsets = shadow->GetFilterOffsets();
shader->Uniform("shadowOffsets1", offsets[0], offsets[1], offsets[2], offsets[3]);
shader->Uniform("shadowOffsets2", offsets[4], offsets[5], offsets[6], offsets[7]);
}
CLOSTexture& los = g_Renderer.GetScene().GetLOSTexture();
shader->BindTexture("losTex", los.GetTexture());
shader->Uniform("losTransform", los.GetTextureMatrix()[0], los.GetTextureMatrix()[12], 0.f, 0.f);
shader->Uniform("ambient", lightEnv.m_TerrainAmbientColor);
shader->Uniform("sunColor", lightEnv.m_SunColor);
}
void TerrainRenderer::RenderTerrainShader(ShadowMap* shadow, bool filtered)
{
ENSURE(m->phase == Phase_Render);
std::vector<CPatchRData*>& visiblePatches = filtered ? m->filteredPatches : m->visiblePatches;
std::vector<CDecalRData*>& visibleDecals = filtered ? m->filteredDecals : m->visibleDecals;
if (visiblePatches.empty() && visibleDecals.empty())
return;
CShaderManager& shaderManager = g_Renderer.GetShaderManager();
typedef std::map<CStr, CStr> Defines;
Defines defBasic;
if (shadow)
{
defBasic["USE_SHADOW"] = "1";
if (g_Renderer.m_Caps.m_ARBProgramShadow && g_Renderer.m_Options.m_ARBProgramShadow)
defBasic["USE_FP_SHADOW"] = "1";
if (g_Renderer.m_Options.m_ShadowPCF)
defBasic["USE_SHADOW_PCF"] = "1";
}
defBasic["LIGHTING_MODEL_" + g_Renderer.GetLightEnv().GetLightingModel()] = "1";
CShaderProgramPtr shaderBase(shaderManager.LoadProgram("terrain_base", defBasic));
CShaderProgramPtr shaderBlend(shaderManager.LoadProgram("terrain_blend", defBasic));
CShaderProgramPtr shaderDecal(shaderManager.LoadProgram("terrain_decal", defBasic));
// 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();
PROFILE_END("render terrain sides");
// switch on required client states
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glEnableClientState(GL_COLOR_ARRAY); // diffuse lighting colours
shaderBase->Bind();
PrepareShader(shaderBase, shadow);
PROFILE_START("render terrain base");
CPatchRData::RenderBases(visiblePatches, shaderBase);
PROFILE_END("render terrain base");
shaderBase->Unbind();
// render blends
shaderBlend->Bind();
PrepareShader(shaderBlend, shadow);
// 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);
// 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);
// render blend passes for each patch
PROFILE_START("render terrain blends");
CPatchRData::RenderBlends(visiblePatches, shaderBlend);
PROFILE_END("render terrain blends");
// Disable second texcoord array
pglClientActiveTextureARB(GL_TEXTURE1);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
shaderBlend->Unbind();
// Render terrain decals
shaderDecal->Bind();
PrepareShader(shaderDecal, shadow);
g_Renderer.BindTexture(1, 0);
pglActiveTextureARB(GL_TEXTURE0);
pglClientActiveTextureARB(GL_TEXTURE0);
PROFILE_START("render terrain decals");
for (size_t i = 0; i < visibleDecals.size(); ++i)
visibleDecals[i]->Render(shaderDecal);
PROFILE_END("render terrain decals");
shaderDecal->Unbind();
// restore OpenGL state
g_Renderer.BindTexture(1, 0);
g_Renderer.BindTexture(2, 0);
g_Renderer.BindTexture(3, 0);
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);
}
///////////////////////////////////////////////////////////////////
// Render un-textured patches as polygons
void TerrainRenderer::RenderPatches(bool filtered)
{
ENSURE(m->phase == Phase_Render);
std::vector<CPatchRData*>& visiblePatches = filtered ? m->filteredPatches : m->visiblePatches;
if (visiblePatches.empty())
return;
glEnableClientState(GL_VERTEX_ARRAY);
CPatchRData::RenderStreams(visiblePatches, STREAM_POS);
glDisableClientState(GL_VERTEX_ARRAY);
}
///////////////////////////////////////////////////////////////////
// Render outlines of submitted patches as lines
void TerrainRenderer::RenderOutlines(bool filtered)
{
ENSURE(m->phase == Phase_Render);
std::vector<CPatchRData*>& visiblePatches = filtered ? m->filteredPatches : m->visiblePatches;
if (visiblePatches.empty())
return;
glEnableClientState(GL_VERTEX_ARRAY);
for (size_t i = 0; i < visiblePatches.size(); ++i)
visiblePatches[i]->RenderOutline();
glDisableClientState(GL_VERTEX_ARRAY);
}
///////////////////////////////////////////////////////////////////
// Scissor rectangle of water patches
CBoundingBoxAligned TerrainRenderer::ScissorWater(const CMatrix3D &viewproj)
{
CBoundingBoxAligned scissor;
for (size_t i = 0; i < m->visiblePatches.size(); ++i)
{
CPatchRData* data = m->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[2] >= -v1[3]) \
screenBounds += CVector3D(v1[0], v1[1], v1[2]) * (1.0f / v1[3]); \
else \
{ \
float t = v1[2] + v1[3]; \
if (v2[2] > -v2[3]) \
{ \
CVector4D c2 = v1 + (v2 - v1) * (t / (t - (v2[2] + v2[3]))); \
screenBounds += CVector3D(c2[0], c2[1], c2[2]) * (1.0f / c2[3]); \
} \
if (v3[2] > -v3[3]) \
{ \
CVector4D c3 = v1 + (v3 - v1) * (t / (t - (v3[2] + v3[3]))); \
screenBounds += CVector3D(c3[0], c3[1], c3[2]) * (1.0f / c3[3]); \
} \
if (v4[2] > -v4[3]) \
{ \
CVector4D c4 = v1 + (v4 - v1) * (t / (t - (v4[2] + v4[3]))); \
screenBounds += CVector3D(c4[0], c4[1], c4[2]) * (1.0f / c4[3]); \
} \
}
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()
{
PROFILE3_GPU("fancy water");
// If we're using fancy water, make sure its shader is loaded
if (!m->fancyWaterShader)
{
std::map<CStr, CStr> defNull;
m->fancyWaterShader = g_Renderer.GetShaderManager().LoadProgram("water_high", defNull);
if (!m->fancyWaterShader)
{
LOGERROR(L"Failed to load water shader. Falling back to non-fancy water.\n");
g_Renderer.m_Options.m_FancyWater = false;
return false;
}
}
WaterManager* WaterMgr = g_Renderer.GetWaterManager();
CLOSTexture& losTexture = g_Renderer.GetScene().GetLOSTexture();
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
double time = WaterMgr->m_WaterTexTimer;
double period = 1.6;
int curTex = (int)(time*60/period) % 60;
m->fancyWaterShader->Bind();
m->fancyWaterShader->BindTexture("normalMap", WaterMgr->m_NormalMap[curTex]);
// Shift the texture coordinates by these amounts to make the water "flow"
float tx = -fmod(time, 81.0)/81.0;
float ty = -fmod(time, 34.0)/34.0;
float repeatPeriod = WaterMgr->m_RepeatPeriod;
// Set the proper LOD bias
glTexEnvf(GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, g_Renderer.m_Options.m_LodBias);
const CCamera& camera = g_Renderer.GetViewCamera();
CVector3D camPos = camera.m_Orientation.GetTranslation();
// Bind reflection and refraction textures
m->fancyWaterShader->BindTexture("reflectionMap", WaterMgr->m_ReflectionTexture);
m->fancyWaterShader->BindTexture("refractionMap", WaterMgr->m_RefractionTexture);
m->fancyWaterShader->BindTexture("losMap", losTexture.GetTexture());
const CLightEnv& lightEnv = g_Renderer.GetLightEnv();
m->fancyWaterShader->Uniform("ambient", lightEnv.m_TerrainAmbientColor);
m->fancyWaterShader->Uniform("sunDir", lightEnv.GetSunDir());
m->fancyWaterShader->Uniform("sunColor", lightEnv.m_SunColor.X);
m->fancyWaterShader->Uniform("shininess", WaterMgr->m_Shininess);
m->fancyWaterShader->Uniform("specularStrength", WaterMgr->m_SpecularStrength);
m->fancyWaterShader->Uniform("waviness", WaterMgr->m_Waviness);
m->fancyWaterShader->Uniform("murkiness", WaterMgr->m_Murkiness);
m->fancyWaterShader->Uniform("fullDepth", WaterMgr->m_WaterFullDepth);
m->fancyWaterShader->Uniform("tint", WaterMgr->m_WaterTint);
m->fancyWaterShader->Uniform("reflectionTintStrength", WaterMgr->m_ReflectionTintStrength);
m->fancyWaterShader->Uniform("reflectionTint", WaterMgr->m_ReflectionTint);
m->fancyWaterShader->Uniform("translation", tx, ty);
m->fancyWaterShader->Uniform("repeatScale", 1.0f / repeatPeriod);
m->fancyWaterShader->Uniform("reflectionMatrix", WaterMgr->m_ReflectionMatrix);
m->fancyWaterShader->Uniform("refractionMatrix", WaterMgr->m_RefractionMatrix);
m->fancyWaterShader->Uniform("losMatrix", losTexture.GetTextureMatrix());
m->fancyWaterShader->Uniform("cameraPos", camPos);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
for (size_t i = 0; i < m->visiblePatches.size(); ++i)
{
CPatchRData* data = m->visiblePatches[i];
data->RenderWater();
}
glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
m->fancyWaterShader->Unbind();
pglActiveTextureARB(GL_TEXTURE0_ARB);
glDisable(GL_BLEND);
return true;
}
void TerrainRenderer::RenderSimpleWater()
{
PROFILE3_GPU("simple water");
WaterManager* WaterMgr = g_Renderer.GetWaterManager();
CLOSTexture& losTexture = g_Game->GetView()->GetLOSTexture();
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
double time = WaterMgr->m_WaterTexTimer;
double period = 1.6f;
int curTex = (int)(time*60/period) % 60;
WaterMgr->m_WaterTexture[curTex]->Bind();
// Shift the texture coordinates by these amounts to make the water "flow"
float tx = -fmod(time, 81.0)/81.0;
float ty = -fmod(time, 34.0)/34.0;
float repeatPeriod = 16.0f;
// Perform the shifting by using texture coordinate generation
GLfloat texgenS0[4] = { 1/repeatPeriod, 0, 0, tx };
GLfloat texgenT0[4] = { 0, 0, 1/repeatPeriod, ty };
glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
glTexGenfv(GL_S, GL_OBJECT_PLANE, texgenS0);
glTexGenfv(GL_T, GL_OBJECT_PLANE, texgenT0);
glEnable(GL_TEXTURE_GEN_S);
glEnable(GL_TEXTURE_GEN_T);
// Set up texture environment to multiply vertex RGB by texture RGB and use vertex alpha
GLfloat waterColor[4] = { WaterMgr->m_WaterColor.r, WaterMgr->m_WaterColor.g, WaterMgr->m_WaterColor.b, 1.0f };
glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, waterColor);
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_TEXTURE);
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_PRIMARY_COLOR_ARB);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
// Multiply by LOS texture
losTexture.BindTexture(1);
CMatrix3D losMatrix = losTexture.GetTextureMatrix();
GLfloat texgenS1[4] = { losMatrix[0], losMatrix[4], losMatrix[8], losMatrix[12] };
GLfloat texgenT1[4] = { losMatrix[1], losMatrix[5], losMatrix[9], losMatrix[13] };
glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
glTexGenfv(GL_S, GL_OBJECT_PLANE, texgenS1);
glTexGenfv(GL_T, GL_OBJECT_PLANE, texgenT1);
glEnable(GL_TEXTURE_GEN_S);
glEnable(GL_TEXTURE_GEN_T);
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);
// Set the proper LOD bias
glTexEnvf(GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, g_Renderer.m_Options.m_LodBias);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
for (size_t i = 0; i < m->visiblePatches.size(); ++i)
{
CPatchRData* data = m->visiblePatches[i];
data->RenderWater();
}
glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
g_Renderer.BindTexture(1, 0);
glDisable(GL_TEXTURE_GEN_S);
glDisable(GL_TEXTURE_GEN_T);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
pglActiveTextureARB(GL_TEXTURE0_ARB);
// Clean up the texture matrix and blend mode
glDisable(GL_TEXTURE_GEN_S);
glDisable(GL_TEXTURE_GEN_T);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
glDisable(GL_BLEND);
glDisable(GL_TEXTURE_2D);
}
///////////////////////////////////////////////////////////////////
// Render water that is part of the terrain
void TerrainRenderer::RenderWater()
{
WaterManager* WaterMgr = g_Renderer.GetWaterManager();
if (!WaterMgr->WillRenderFancyWater() || !RenderFancyWater())
RenderSimpleWater();
}
void TerrainRenderer::RenderPriorities()
{
PROFILE("priorities");
ENSURE(m->phase == Phase_Render);
CFont font(L"mono-stroke-10");
font.Bind();
glColor3f(1, 1, 0);
for (size_t i = 0; i < m->visiblePatches.size(); ++i)
m->visiblePatches[i]->RenderPriorities();
}