0ad/source/renderer/TerrainRenderer.cpp
Matei 1a8cb85e08 Some water-related bug fixes:
- Underwater tiles will be tested against the frustum with a bounding
box including the water surface, so the water gets drawn even if the
terrain is off-screen.
- Transparent objects (e.g. trees) should now interact with water
properly. I did this by rendering them twice, first before the water, so
it overlaps the underwater pieces, then after, so the above-water pieces
overlap the water (the water now writes Z as well as colour to allow
this). Before, only the first pass was done, so the tops of trees were
covered by water. There might be more efficient ways of doing this - the
best could be for the transparent objects to be drawn with a shader that
always either discards a pixel or writes alpha of 1.0 instead of using
2-pass depth write then colour write.

This was SVN commit r3904.
2006-05-29 00:49:09 +00:00

606 lines
20 KiB
C++

/**
* =========================================================================
* File : TerrainRenderer.cpp
* Project : Pyrogenesis
* Description : Terrain rendering (everything related to patches and
* : water) is encapsulated in TerrainRenderer
*
* @author Nicolai Hähnle <nicolai@wildfiregames.com>
* =========================================================================
*/
#include "precompiled.h"
#include "graphics/Camera.h"
#include "graphics/LightEnv.h"
#include "graphics/Patch.h"
#include "graphics/Terrain.h"
#include "graphics/GameView.h"
#include "maths/MathUtil.h"
#include "ps/Game.h"
#include "ps/Profile.h"
#include "simulation/LOSManager.h"
#include "renderer/PatchRData.h"
#include "renderer/Renderer.h"
#include "renderer/ShadowMap.h"
#include "renderer/TerrainRenderer.h"
#include "renderer/WaterManager.h"
#include "lib/res/graphics/ogl_shader.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;
/**
* VisiblePatches: Patches that were submitted for this frame
*
* @todo Merge this list with CPatchRData list
*/
std::vector<CPatch*> visiblePatches;
/// Fancy water shader
Handle fancyWaterShader;
};
///////////////////////////////////////////////////////////////////
// Construction/Destruction
TerrainRenderer::TerrainRenderer()
{
m = new TerrainRendererInternals();
m->phase = Phase_Submit;
m->fancyWaterShader = 0;
}
TerrainRenderer::~TerrainRenderer()
{
if( m->fancyWaterShader )
{
ogl_program_free( m->fancyWaterShader );
}
delete m;
}
///////////////////////////////////////////////////////////////////
// Submit a patch for rendering
void TerrainRenderer::Submit(CPatch* patch)
{
debug_assert(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(patch);
}
///////////////////////////////////////////////////////////////////
// Prepare for rendering
void TerrainRenderer::PrepareForRendering()
{
debug_assert(m->phase == Phase_Submit);
m->phase = Phase_Render;
}
///////////////////////////////////////////////////////////////////
// Clear submissions lists
void TerrainRenderer::EndFrame()
{
debug_assert(m->phase == Phase_Render);
m->visiblePatches.clear();
m->phase = Phase_Submit;
}
///////////////////////////////////////////////////////////////////
// Query if patches have been submitted this frame
bool TerrainRenderer::HaveSubmissions()
{
return m->visiblePatches.size() > 0;
}
///////////////////////////////////////////////////////////////////
// Full-featured terrain rendering with blending and everything
void TerrainRenderer::RenderTerrain(ShadowMap* shadow)
{
debug_assert(m->phase == Phase_Render);
// switch on required client states
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
// render everything fullbright
// set up texture environment for base pass
MICROLOG(L"base splat textures");
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_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PRIMARY_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_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);
for(uint i = 0; i < m->visiblePatches.size(); ++i)
{
CPatchRData* patchdata = (CPatchRData*)m->visiblePatches[i]->GetRenderData();
patchdata->RenderBase(true); // with LOS color
}
// 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);
// render blend passes for each patch
for(uint i = 0; i < m->visiblePatches.size(); ++i)
{
CPatchRData* patchdata = (CPatchRData*)m->visiblePatches[i]->GetRenderData();
patchdata->RenderBlends();
}
// Disable second texcoord array
pglClientActiveTextureARB(GL_TEXTURE1);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
// Now apply lighting
const CLightEnv& lightEnv = g_Renderer.GetLightEnv();
glBlendFunc(GL_DST_COLOR, GL_ZERO);
if (!shadow)
{
pglActiveTextureARB(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, 0);
// Shadow rendering disabled: Ambient + Diffuse
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, &lightEnv.m_TerrainAmbientColor.X);
}
else
{
const CMatrix3D& texturematrix = shadow->GetTextureMatrix();
pglActiveTextureARB(GL_TEXTURE0);
glMatrixMode(GL_TEXTURE);
glLoadMatrixf(&texturematrix._11);
glMatrixMode(GL_MODELVIEW);
glBindTexture(GL_TEXTURE_2D, shadow->GetTexture());
if (shadow->GetUseDepthTexture())
{
// Ambient + ShTranslucency * Diffuse * (1 - Shadow) + Diffuse * Shadow
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_PRIMARY_COLOR);
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);
float shadowTransp = g_Renderer.GetLightEnv().GetTerrainShadowTransparency();
float color[4] = { shadowTransp, shadowTransp, shadowTransp, 1.0 };
glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, color);
pglActiveTextureARB(GL_TEXTURE1);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_INTERPOLATE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PRIMARY_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PREVIOUS);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE2_RGB_ARB, GL_TEXTURE0);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_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);
pglActiveTextureARB(GL_TEXTURE2);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, shadow->GetTexture()); // Need a valid texture or the unit will be disabled
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, &lightEnv.m_TerrainAmbientColor.X);
}
else
{
// Ambient + Diffuse * Shadow
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_PREVIOUS);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
pglActiveTextureARB(GL_TEXTURE1); // + Ambient
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, &lightEnv.m_TerrainAmbientColor.X);
}
}
pglActiveTextureARB(GL_TEXTURE0);
pglClientActiveTextureARB(GL_TEXTURE0);
for (uint i = 0; i < m->visiblePatches.size(); ++i)
{
CPatchRData* patchdata = (CPatchRData*)m->visiblePatches[i]->GetRenderData();
patchdata->RenderStreams(STREAM_POS|STREAM_COLOR|STREAM_POSTOUV0, false);
}
glMatrixMode(GL_TEXTURE);
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
// restore OpenGL state
if ( shadow )
{
if ( shadow->GetUseDepthTexture() )
g_Renderer.BindTexture(2,0);
}
g_Renderer.BindTexture(1,0);
pglClientActiveTextureARB(GL_TEXTURE0);
pglActiveTextureARB(GL_TEXTURE0);
glDepthMask(1);
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()
{
debug_assert(m->phase == Phase_Render);
glEnableClientState(GL_VERTEX_ARRAY);
for(uint i = 0; i < m->visiblePatches.size(); ++i)
{
CPatchRData* patchdata = (CPatchRData*)m->visiblePatches[i]->GetRenderData();
patchdata->RenderStreams(STREAM_POS, true);
}
glDisableClientState(GL_VERTEX_ARRAY);
}
///////////////////////////////////////////////////////////////////
// Render outlines of submitted patches as lines
void TerrainRenderer::RenderOutlines()
{
glEnableClientState(GL_VERTEX_ARRAY);
for(uint i = 0; i < m->visiblePatches.size(); ++i)
{
CPatchRData* patchdata = (CPatchRData*)m->visiblePatches[i]->GetRenderData();
patchdata->RenderOutline();
}
glDisableClientState(GL_VERTEX_ARRAY);
}
///////////////////////////////////////////////////////////////////
// Render water that is part of the terrain
void TerrainRenderer::RenderWater()
{
PROFILE( "render water" );
bool fancy = g_Renderer.m_Options.m_FancyWater;
// If we're using fancy water, make sure its shader is loaded
if(fancy && !m->fancyWaterShader)
{
m->fancyWaterShader = ogl_program_load( "shaders/water_high.xml" );
}
//(Crappy) fresnel effect
CCamera* Camera=g_Game->GetView()->GetCamera();
CVector3D CamFace=Camera->m_Orientation.GetIn();
CamFace.Normalize();
float FresnelScalar = CamFace.Dot( CVector3D(0.0f, -1.0f, 0.0f) );
//Invert and set boundaries
FresnelScalar = (1 - FresnelScalar) * 0.4f + 0.6f;
CTerrain* terrain = g_Game->GetWorld()->GetTerrain();
int mapSize = terrain->GetVerticesPerSide();
CLOSManager* losMgr = g_Game->GetWorld()->GetLOSManager();
WaterManager* WaterMgr = g_Renderer.GetWaterManager();
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;
if(fancy)
{
ogl_tex_bind(WaterMgr->m_NormalMap[curTex], 0);
}
else
{
ogl_tex_bind(WaterMgr->m_WaterTexture[curTex], 0);
}
if(!fancy)
{
// Shift the texture coordinates to make it "flow"
glMatrixMode(GL_TEXTURE);
glLoadIdentity();
float tx = -fmod(time, 20.0)/20.0;
float ty = fmod(time, 35.0)/35.0;
glTranslatef(tx, ty, 0);
// Set up texture environment to multiply vertex RGB by texture RGB and use vertex alpha
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_PRIMARY_COLOR_ARB);
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);
}
// Set the proper LOD bias
glTexEnvf(GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, g_Renderer.m_Options.m_LodBias);
// Some offsets used to go around counterclockwise while keeping code concise
const int DX[] = {1,1,0,0};
const int DZ[] = {0,1,1,0};
GLint vertexDepth = 0; // water depth attribute, if using fancy water
if(fancy)
{
// Bind reflection and refraction textures on texture units 1 and 2
pglActiveTextureARB( GL_TEXTURE1_ARB );
glEnable( GL_TEXTURE_2D );
glBindTexture( GL_TEXTURE_2D, WaterMgr->m_ReflectionTexture );
pglActiveTextureARB( GL_TEXTURE2_ARB );
glEnable( GL_TEXTURE_2D );
glBindTexture( GL_TEXTURE_2D, WaterMgr->m_RefractionTexture );
// Bind water shader and set arguments
ogl_program_use( m->fancyWaterShader );
GLint ambient = ogl_program_get_uniform_location( m->fancyWaterShader, "ambient" );
GLint sunDir = ogl_program_get_uniform_location( m->fancyWaterShader, "sunDir" );
GLint sunColor = ogl_program_get_uniform_location( m->fancyWaterShader, "sunColor" );
GLint shininess = ogl_program_get_uniform_location( m->fancyWaterShader, "shininess" );
GLint waviness = ogl_program_get_uniform_location( m->fancyWaterShader, "waviness" );
GLint cameraPos = ogl_program_get_uniform_location( m->fancyWaterShader, "cameraPos" );
GLint reflectionMatrix = ogl_program_get_uniform_location( m->fancyWaterShader, "reflectionMatrix" );
GLint refractionMatrix = ogl_program_get_uniform_location( m->fancyWaterShader, "refractionMatrix" );
GLint normalMap = ogl_program_get_uniform_location( m->fancyWaterShader, "normalMap" );
GLint reflectionMap = ogl_program_get_uniform_location( m->fancyWaterShader, "reflectionMap" );
GLint refractionMap = ogl_program_get_uniform_location( m->fancyWaterShader, "refractionMap" );
const CLightEnv& lightEnv = g_Renderer.GetLightEnv();
pglUniform3fvARB( ambient, 1, &lightEnv.m_TerrainAmbientColor.X );
pglUniform3fvARB( sunDir, 1, &lightEnv.GetSunDir().X );
pglUniform3fvARB( sunColor, 1, &lightEnv.m_SunColor.X );
pglUniform1fARB( shininess, WaterMgr->m_Shininess );
pglUniform1fARB( waviness, WaterMgr->m_Waviness );
pglUniformMatrix4fvARB( reflectionMatrix, 1, false, &WaterMgr->m_ReflectionMatrix._11 );
pglUniformMatrix4fvARB( refractionMatrix, 1, false, &WaterMgr->m_RefractionMatrix._11 );
pglUniform1iARB( normalMap, 0 ); // texture unit 0
pglUniform1iARB( reflectionMap, 1 ); // texture unit 1
pglUniform1iARB( refractionMap, 2 ); // texture unit 2
const CCamera& camera = g_Renderer.GetViewCamera();
CVector3D camPos = camera.m_Orientation.GetTranslation();
pglUniform3fvARB( cameraPos, 1, &camPos.X );
vertexDepth = ogl_program_get_attrib_location( m->fancyWaterShader, "vertexDepth" );
}
float repeatPeriod = (fancy ? WaterMgr->m_RepeatPeriod : 16.0f);
glBegin(GL_QUADS);
for(size_t i=0; i<m->visiblePatches.size(); i++)
{
CPatch* patch = m->visiblePatches[i];
for(int dx=0; dx<PATCH_SIZE; dx++)
{
for(int dz=0; dz<PATCH_SIZE; dz++)
{
int x = (patch->m_X*PATCH_SIZE + dx);
int z = (patch->m_Z*PATCH_SIZE + dz);
// is any corner of the tile below the water height? if not, no point rendering it
bool shouldRender = false;
for(int j=0; j<4; j++)
{
float terrainHeight = terrain->getVertexGroundLevel(x + DX[j], z + DZ[j]);
if( terrainHeight < WaterMgr->m_WaterHeight )
{
shouldRender = true;
break;
}
}
if(!shouldRender)
{
continue;
}
for(int j=0; j<4; j++)
{
int ix = x + DX[j];
int iz = z + DZ[j];
float vertX = ix * CELL_SIZE;
float vertZ = iz * CELL_SIZE;
float terrainHeight = terrain->getVertexGroundLevel(ix, iz);
float alpha = clamp(
(WaterMgr->m_WaterHeight - terrainHeight) / WaterMgr->m_WaterFullDepth + WaterMgr->m_WaterAlphaOffset,
-100.0f, WaterMgr->m_WaterMaxAlpha);
float losMod = 1.0f;
for(int k=0; k<4; k++)
{
int tx = ix - DX[k];
int tz = iz - DZ[k];
if(tx >= 0 && tz >= 0 && tx <= mapSize-2 && tz <= mapSize-2)
{
ELOSStatus s = losMgr->GetStatus(tx, tz, g_Game->GetLocalPlayer());
if(s == LOS_EXPLORED && losMod > 0.7f)
losMod = 0.7f;
else if(s==LOS_UNEXPLORED && losMod > 0.0f)
losMod = 0.0f;
}
}
if(fancy)
{
pglVertexAttrib1fARB( vertexDepth, WaterMgr->m_WaterHeight - terrainHeight );
}
glColor4f(WaterMgr->m_WaterColor.r*losMod, WaterMgr->m_WaterColor.g*losMod, WaterMgr->m_WaterColor.b*losMod, alpha * FresnelScalar);
pglMultiTexCoord2fARB(GL_TEXTURE0, vertX/repeatPeriod, vertZ/repeatPeriod);
glVertex3f(vertX, WaterMgr->m_WaterHeight, vertZ);
}
} //end of x loop
} //end of z loop
}
glEnd();
if(fancy)
{
// Unbind the refraction/reflection textures and the shader
pglActiveTextureARB( GL_TEXTURE1_ARB );
glBindTexture( GL_TEXTURE_2D, 0 );
glDisable( GL_TEXTURE_2D );
pglActiveTextureARB( GL_TEXTURE2_ARB );
glBindTexture( GL_TEXTURE_2D, 0 );
glDisable( GL_TEXTURE_2D );
pglActiveTextureARB( GL_TEXTURE0_ARB );
ogl_program_use( 0 );
}
if(!fancy)
{
// Clean up the texture matrix and blend mode
glLoadIdentity();
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
}
glMatrixMode(GL_MODELVIEW);
glDisable(GL_BLEND);
glDisable(GL_TEXTURE_2D);
}