Index: ps/trunk/binaries/data/mods/public/shaders/glsl/water_high.fs
===================================================================
--- ps/trunk/binaries/data/mods/public/shaders/glsl/water_high.fs (revision 22038)
+++ ps/trunk/binaries/data/mods/public/shaders/glsl/water_high.fs (revision 22039)
@@ -1,360 +1,352 @@
#version 110
// Environment settings
uniform vec3 ambient;
uniform vec3 sunDir;
uniform vec3 sunColor;
uniform mat4 skyBoxRot;
uniform vec3 cameraPos;
uniform sampler2D losMap;
uniform float waviness; // "Wildness" of the reflections and refractions; choose based on texture
uniform vec3 color; // color of the water
uniform vec3 tint; // Tint for refraction (used to simulate particles in water)
uniform float murkiness; // Amount of tint to blend in with the refracted color
uniform float windAngle;
varying vec2 WindCosSin;
uniform vec3 fogColor;
uniform vec2 fogParams;
uniform vec2 screenSize;
uniform float time;
varying float moddedTime;
varying vec3 worldPos;
varying float waterDepth;
varying vec2 waterInfo;
varying vec3 v;
varying vec4 normalCoords;
varying vec3 reflectionCoords;
varying vec3 refractionCoords;
varying vec2 losCoords;
varying float fwaviness;
uniform float mapSize;
uniform samplerCube skyCube;
uniform sampler2D normalMap;
uniform sampler2D normalMap2;
#if USE_FANCY_EFFECTS
uniform sampler2D waterEffectsTexNorm;
uniform sampler2D waterEffectsTexOther;
#endif
uniform vec4 waveParams1; // wavyEffect, BaseScale, Flattenism, Basebump
uniform vec4 waveParams2; // Smallintensity, Smallbase, Bigmovement, Smallmovement
uniform sampler2D reflectionMap;
#if USE_REFRACTION
uniform sampler2D refractionMap;
#endif
#if USE_REAL_DEPTH
uniform sampler2D depthTex;
#endif
#if USE_SHADOWS_ON_WATER && USE_SHADOW
varying vec4 v_shadow;
#if USE_SHADOW_SAMPLER
uniform sampler2DShadow shadowTex;
#if USE_SHADOW_PCF
uniform vec4 shadowScale;
#endif
#else
uniform sampler2D shadowTex;
#endif
float get_shadow(vec4 coords)
{
#if USE_SHADOWS_ON_WATER && !DISABLE_RECEIVE_SHADOWS
#if USE_SHADOW_SAMPLER
#if USE_SHADOW_PCF
vec2 offset = fract(coords.xy - 0.5);
vec4 size = vec4(offset + 1.0, 2.0 - offset);
vec4 weight = (vec4(1.0, 1.0, -0.5, -0.5) + (coords.xy - 0.5*offset).xyxy) * shadowScale.zwzw;
return (1.0/9.0)*dot(size.zxzx*size.wwyy,
vec4(shadow2D(shadowTex, vec3(weight.zw, coords.z)).r,
shadow2D(shadowTex, vec3(weight.xw, coords.z)).r,
shadow2D(shadowTex, vec3(weight.zy, coords.z)).r,
shadow2D(shadowTex, vec3(weight.xy, coords.z)).r));
#else
return shadow2D(shadowTex, coords.xyz).r;
#endif
#else
if (coords.z >= 1.0)
return 1.0;
return (coords.z <= texture2D(shadowTex, coords.xy).x ? 1.0 : 0.0);
#endif
#else
return 1.0;
#endif
}
#endif
// TODO: convert this to something not only for AABBs
struct Ray {
vec3 Origin;
vec3 Direction;
};
float IntersectBox (in Ray ray, in vec3 minimum, in vec3 maximum)
{
vec3 OMIN = ( minimum - ray.Origin ) / ray.Direction;
vec3 OMAX = ( maximum - ray.Origin ) / ray.Direction;
vec3 MAX = max ( OMAX, OMIN );
return min ( MAX.x, min ( MAX.y, MAX.z ) );
}
vec3 get_fog(vec3 color)
{
float density = fogParams.x;
float maxFog = fogParams.y;
const float LOG2 = 1.442695;
float z = gl_FragCoord.z / gl_FragCoord.w;
float fogFactor = exp2(-density * density * z * z * LOG2);
fogFactor = fogFactor * (1.0 - maxFog) + maxFog;
fogFactor = clamp(fogFactor, 0.0, 1.0);
return mix(fogColor, color, fogFactor);
}
void main()
{
float fresnel;
vec2 reflCoords, refrCoords;
vec3 reflColor, refrColor, specular;
float losMod, reflMod;
// Calculate water normals.
float wavyEffect = waveParams1.r;
float baseScale = waveParams1.g;
float flattenism = waveParams1.b;
float baseBump = waveParams1.a;
float BigMovement = waveParams2.b;
// This method uses 60 animated water frames. We're blending between each two frames
// Scale the normal textures by waviness so that big waviness means bigger waves.
vec3 ww1 = texture2D(normalMap, (normalCoords.st + normalCoords.zw * BigMovement*waviness/10.0) * (baseScale - waviness/wavyEffect)).xzy;
vec3 ww2 = texture2D(normalMap2, (normalCoords.st + normalCoords.zw * BigMovement*waviness/10.0) * (baseScale - waviness/wavyEffect)).xzy;
vec3 wwInterp = mix(ww1, ww2, moddedTime) - vec3(0.5,0.0,0.5);
ww1.x = wwInterp.x * WindCosSin.x - wwInterp.z * WindCosSin.y;
ww1.z = wwInterp.x * WindCosSin.y + wwInterp.z * WindCosSin.x;
ww1.y = wwInterp.y;
// Flatten them based on waviness.
vec3 n = normalize(mix(vec3(0.0,1.0,0.0), ww1, clamp(baseBump + fwaviness/flattenism,0.0,1.0)));
#if USE_FANCY_EFFECTS
vec4 fancyeffects = texture2D(waterEffectsTexNorm, gl_FragCoord.xy/screenSize);
n = mix(vec3(0.0,1.0,0.0), n,0.5 + waterInfo.r/2.0);
n.xz = mix(n.xz, fancyeffects.rb,fancyeffects.a/2.0);
#else
n = mix(vec3(0.0,1.0,0.0), n, 0.5 + waterInfo.r/2.0);
#endif
n = vec3(-n.x,n.y,-n.z); // The final wave normal vector.
// How perpendicular to the normal our view is. Used for fresnel.
float ndotv = clamp(dot(n, v),0.0,1.0);
// Fresnel for "how much reflection vs how much refraction".
fresnel = clamp(((pow(1.1 - ndotv, 2.0)) * 1.5), 0.1, 0.75); // Approximation. I'm using 1.1 and not 1.0 because it causes artifacts, see #1714
// Specular lighting vectors
vec3 specVector = reflect(sunDir, ww1);
// pow is undefined for null or negative values, except on intel it seems.
float specIntensity = clamp(pow(abs(dot(specVector, v)), 100.0), 0.0, 1.0);
specular = specIntensity*1.2 * mix(vec3(1.5), sunColor,0.5);
float depth;
#if USE_REAL_DEPTH
// Don't change these two. They should match the values in the config (TODO: dec uniforms).
float zNear = 2.0;
float zFar = 4096.0;
// Okay so here it's a tad complicated. I want to distort the depth buffer along the waves for a nice effect.
// However this causes a problem around underwater objects (think fishes): on some pixels, the depth will be seen as the same as the fishes'
// and the color will be grass ( cause I don't distort the refraction coord by exactly the same stuff)
// Also, things like towers with the feet in water would cause the buffer to see the depth as actually negative in some places.
// So what I do is first check the undistorted depth, then I compare with the distorted value and fix.
float water_b = gl_FragCoord.z;
float water_n = 2.0 * water_b - 1.0;
float waterDBuffer = 2.0 * zNear * zFar / (zFar + zNear - water_n * (zFar - zNear));
float undisto_z_b = texture2D(depthTex, (gl_FragCoord.xy) / screenSize).x;
float undisto_z_n = 2.0 * undisto_z_b - 1.0;
float waterDepth_undistorted = (2.0 * zNear * zFar / (zFar + zNear - undisto_z_n * (zFar - zNear)) - waterDBuffer);
vec2 depthCoord = clamp((gl_FragCoord.xy) / screenSize - n.xz*clamp( waterDepth_undistorted/400.0,0.0,0.05) , 0.001, 0.999);
float z_b = texture2D(depthTex, depthCoord).x;
if (z_b < undisto_z_b)
z_b = undisto_z_b;
float z_n = 2.0 * z_b - 1.0;
depth = (2.0 * zNear * zFar / (zFar + zNear - z_n * (zFar - zNear)) - waterDBuffer);
#else
depth = waterDepth / (min(0.5,v.y)*1.5*min(0.5,v.y)*2.0);
#endif
#if USE_FANCY_EFFECTS
depth = max(depth,fancyeffects.a);
#endif
#if USE_SHADOWS_ON_WATER && USE_SHADOW
float shadow = get_shadow(vec4(v_shadow.xy, v_shadow.zw));
#endif
// for refraction, we want to adjust the value by v.y slightly otherwise it gets too different between "from above" and "from the sides".
// And it looks weird (again, we are not used to seeing water from above).
float fixedVy = max(v.y,0.01);
float murky = mix(200.0,0.1,pow(murkiness,0.25));
#if USE_REFRACTION
// distoFactor controls the amount of distortion relative to wave normals.
float distoFactor = 0.5 + clamp(depth/2.0,0.0,7.0);
// Distort the texture coords under where the water is to simulate refraction.
refrCoords = (0.5 * refractionCoords.xy - n.xz * distoFactor) / refractionCoords.z + 0.5;
vec3 refColor = texture2D(refractionMap, refrCoords).rgb;
// Note, the refraction map is cleared using (255, 0, 0), so pixels outside of the water plane are pure red.
// If we get a pure red fragment, use an undistorted/less distorted coord instead.
if (refColor.r > 0.999 && refColor.g < 0.001)
{
refrCoords = (0.5*refractionCoords.xy) / refractionCoords.z + 0.5;
refColor = texture2D(refractionMap, refrCoords).rgb;
if (refColor.r > 0.999 && refColor.g < 0.001)
fresnel = 1.0;
}
else
{
// blur the refraction map, distoring using n so that it looks more random than it really is
// and thus looks much better.
float blur = (0.3+clamp(n.x,-0.1,0.1))/refractionCoords.z;
vec3 blurColor = texture2D(refractionMap, refrCoords + vec2(blur+n.x, blur+n.z)).rgb;
blurColor += texture2D(refractionMap, refrCoords + vec2(-blur, blur+n.z)).rgb;
blurColor += texture2D(refractionMap, refrCoords + vec2(-blur, -blur+n.x)).rgb;
blurColor += texture2D(refractionMap, refrCoords + vec2(blur+n.z, -blur)).rgb;
blurColor /= 4.0;
float blurFactor = (distoFactor/7.0);
refColor = (refColor + blurColor * blurFactor) / (1.0+blurFactor);
}
// Apply water tint and murk color.
float extFact = max(0.0,1.0 - (depth*fixedVy/murky));
float ColextFact = max(0.0,1.0 - (depth*fixedVy/murky));
vec3 colll = mix(refColor*tint,refColor,ColextFact);
refrColor = mix(color, colll, extFact);
#else
// Apply water tint and murk color only.
float extFact = max(0.0,1.0 - (depth*fixedVy/murky));
float ColextFact = max(0.0,1.0 - (depth*fixedVy/murky));
vec3 colll = mix(color*tint,color,ColextFact);
refrColor = mix(color, colll, extFact);
#endif
#if USE_REFLECTION
// Reflections
// We use real reflections against the skybox, and distort a texture of objects closer.
vec3 eye = reflect(v,n);
float refVY = clamp(v.y*2.0,0.05,1.0);
// Distort the reflection coords based on waves.
reflCoords = (0.5*reflectionCoords.xy - 15.0 * n.zx / refVY) / reflectionCoords.z + 0.5;
vec4 refTex = texture2D(reflectionMap, reflCoords);
reflColor = refTex.rgb;
if (refTex.a < 0.99)
{
- // Calculate where we intersect with the skycube.
- Ray myRay = Ray(vec3(worldPos.x/4.0,worldPos.y,worldPos.z/4.0),eye);
- vec3 start = vec3(-1500.0 + mapSize/2.0,-100.0,-1500.0 + mapSize/2.0);
- vec3 end = vec3(1500.0 + mapSize/2.0,500.0,1500.0 + mapSize/2.0);
- float tmin = IntersectBox(myRay,start,end);
- vec4 newpos = vec4(-worldPos.x/4.0,worldPos.y,-worldPos.z/4.0,1.0) + vec4(eye * tmin,0.0) - vec4(-mapSize/2.0,worldPos.y,-mapSize/2.0,0.0);
- newpos *= skyBoxRot;
- newpos.y *= 4.0;
// Interpolate between the sky color and nearby objects.
- reflColor = mix(textureCube(skyCube, newpos.rgb).rgb, refTex.rgb, refTex.a);
+ reflColor = mix(textureCube(skyCube, (vec4(eye, 0.0) * skyBoxRot).xyz).rgb, refTex.rgb, refTex.a);
}
// reflMod is used to reduce the intensity of sky reflections, which otherwise are too extreme.
reflMod = max(refTex.a, 0.75);
#else
reflMod = 0.75;
reflColor = vec3(0.15, 0.7, 0.82);
#endif
losMod = texture2D(losMap, losCoords.st).a;
losMod = losMod < 0.03 ? 0.0 : losMod;
vec3 color;
#if USE_SHADOWS_ON_WATER && USE_SHADOW
float fresShadow = mix(fresnel, fresnel*shadow, 0.05 + murkiness*0.2);
color = mix(refrColor, reflColor, fresShadow);
#else
color = mix(refrColor, reflColor, fresnel * reflMod);
#endif
#if USE_SHADOWS_ON_WATER && USE_SHADOW
color += shadow*specular;
#else
color += specular;
#endif
#if USE_FOG
color = get_fog(color);
#endif
#if USE_FANCY_EFFECTS
vec4 FoamEffects = texture2D(waterEffectsTexOther, gl_FragCoord.xy/screenSize);
vec3 foam1 = texture2D(normalMap, (normalCoords.st + normalCoords.zw * BigMovement*waviness/10.0) * (baseScale - waviness/wavyEffect)).aaa;
vec3 foam2 = texture2D(normalMap2, (normalCoords.st + normalCoords.zw * BigMovement*waviness/10.0) * (baseScale - waviness/wavyEffect)).aaa;
vec3 foam3 = texture2D(normalMap, normalCoords.st/6.0 - normalCoords.zw * 0.02).aaa;
vec3 foam4 = texture2D(normalMap2, normalCoords.st/6.0 - normalCoords.zw * 0.02).aaa;
vec3 foaminterp = mix(foam1, foam2, moddedTime);
foaminterp *= mix(foam3, foam4, moddedTime);
foam1.x = foaminterp.x * WindCosSin.x - foaminterp.z * WindCosSin.y;
color += FoamEffects.r * FoamEffects.a * 0.4 + pow(foam1.x * (5.0 + waviness), 2.6 - waviness / 5.5);
#endif
float alpha = 1.0;
#if !USE_REFRACTION
alpha = 1.4 - extFact;
#endif
#if USE_FANCY_EFFECTS
if (fancyeffects.a < 0.05 && waterDepth < -1.0 )
alpha = 0.0;
#endif
gl_FragColor = vec4(color * losMod, alpha);
}
Index: ps/trunk/source/renderer/SkyManager.cpp
===================================================================
--- ps/trunk/source/renderer/SkyManager.cpp (revision 22038)
+++ ps/trunk/source/renderer/SkyManager.cpp (revision 22039)
@@ -1,300 +1,299 @@
-/* Copyright (C) 2018 Wildfire Games.
+/* Copyright (C) 2019 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 .
*/
/*
* Sky settings, texture management and rendering.
*/
#include "precompiled.h"
#include
#include "graphics/LightEnv.h"
#include "graphics/ShaderManager.h"
#include "graphics/Terrain.h"
#include "graphics/TextureManager.h"
#include "lib/timer.h"
#include "lib/tex/tex.h"
#include "lib/res/graphics/ogl_tex.h"
#include "maths/MathUtil.h"
#include "ps/CStr.h"
#include "ps/CLogger.h"
#include "ps/Game.h"
#include "ps/Loader.h"
#include "ps/Filesystem.h"
#include "ps/World.h"
#include "renderer/SkyManager.h"
#include "renderer/Renderer.h"
SkyManager::SkyManager()
: m_RenderSky(true), m_HorizonHeight(-150.0f), m_SkyCubeMap(0)
{
}
///////////////////////////////////////////////////////////////////
// Load all sky textures
void SkyManager::LoadSkyTextures()
{
static const CStrW images[NUMBER_OF_TEXTURES + 1] = {
L"front",
L"back",
L"right",
L"left",
L"top",
L"top"
};
/*for (size_t i = 0; i < ARRAY_SIZE(m_SkyTexture); ++i)
{
VfsPath path = VfsPath("art/textures/skies") / m_SkySet / (Path::String(s_imageNames[i])+L".dds");
CTextureProperties textureProps(path);
textureProps.SetWrap(GL_CLAMP_TO_EDGE);
CTexturePtr texture = g_Renderer.GetTextureManager().CreateTexture(textureProps);
texture->Prefetch();
m_SkyTexture[i] = texture;
}*/
///////////////////////////////////////////////////////////////////////////
// HACK: THE HORRIBLENESS HERE IS OVER 9000. The following code is a HUGE hack and will be removed completely
// as soon as all the hardcoded GL_TEXTURE_2D references are corrected in the TextureManager/OGL/tex libs.
glGenTextures(1, &m_SkyCubeMap);
glBindTexture(GL_TEXTURE_CUBE_MAP, m_SkyCubeMap);
static const int types[] = {
GL_TEXTURE_CUBE_MAP_POSITIVE_X,
GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
GL_TEXTURE_CUBE_MAP_POSITIVE_Z,
GL_TEXTURE_CUBE_MAP_NEGATIVE_Z,
GL_TEXTURE_CUBE_MAP_POSITIVE_Y,
GL_TEXTURE_CUBE_MAP_NEGATIVE_Y
};
for (size_t i = 0; i < NUMBER_OF_TEXTURES + 1; ++i)
{
VfsPath path = VfsPath("art/textures/skies") / m_SkySet / (Path::String(images[i])+L".dds");
shared_ptr file;
size_t fileSize;
if (g_VFS->LoadFile(path, file, fileSize) != INFO::OK)
{
path = VfsPath("art/textures/skies") / m_SkySet / (Path::String(images[i])+L".dds.cached.dds");
if (g_VFS->LoadFile(path, file, fileSize) != INFO::OK)
{
glDeleteTextures(1, &m_SkyCubeMap);
LOGERROR("Error creating sky cubemap.");
return;
}
}
Tex tex;
tex.decode(file, fileSize);
tex.transform_to((tex.m_Flags | TEX_BOTTOM_UP | TEX_ALPHA) & ~(TEX_DXT | TEX_MIPMAPS));
u8* data = tex.get_data();
if (types[i] == GL_TEXTURE_CUBE_MAP_NEGATIVE_Y || types[i] == GL_TEXTURE_CUBE_MAP_POSITIVE_Y)
{
std::vector rotated(tex.m_DataSize);
for (size_t y = 0; y < tex.m_Height; ++y)
{
for (size_t x = 0; x < tex.m_Width; ++x)
{
size_t invx = y, invy = tex.m_Width-x-1;
rotated[(y*tex.m_Width + x) * 4 + 0] = data[(invy*tex.m_Width + invx) * 4 + 0];
rotated[(y*tex.m_Width + x) * 4 + 1] = data[(invy*tex.m_Width + invx) * 4 + 1];
rotated[(y*tex.m_Width + x) * 4 + 2] = data[(invy*tex.m_Width + invx) * 4 + 2];
rotated[(y*tex.m_Width + x) * 4 + 3] = data[(invy*tex.m_Width + invx) * 4 + 3];
}
}
glTexImage2D(types[i], 0, GL_RGB, tex.m_Width, tex.m_Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, &rotated[0]);
}
else
{
glTexImage2D(types[i], 0, GL_RGB, tex.m_Width, tex.m_Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
}
}
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
#if CONFIG2_GLES
#warning TODO: fix SkyManager::LoadSkyTextures for GLES
#else
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
#endif
glBindTexture(GL_TEXTURE_2D, 0);
///////////////////////////////////////////////////////////////////////////
}
///////////////////////////////////////////////////////////////////
// Switch to a different sky set (while the game is running)
void SkyManager::SetSkySet(const CStrW& newSet)
{
if (newSet == m_SkySet)
return;
if (m_SkyCubeMap)
{
glDeleteTextures(1, &m_SkyCubeMap);
m_SkyCubeMap = 0;
}
m_SkySet = newSet;
LoadSkyTextures();
}
///////////////////////////////////////////////////////////////////
// Generate list of available skies
std::vector SkyManager::GetSkySets() const
{
std::vector skies;
// Find all subdirectories in art/textures/skies
const VfsPath path(L"art/textures/skies/");
DirectoryNames subdirectories;
if (g_VFS->GetDirectoryEntries(path, 0, &subdirectories) != INFO::OK)
{
LOGERROR("Error opening directory '%s'", path.string8());
return std::vector(1, GetSkySet()); // just return what we currently have
}
for(size_t i = 0; i < subdirectories.size(); i++)
skies.push_back(subdirectories[i].string());
sort(skies.begin(), skies.end());
return skies;
}
///////////////////////////////////////////////////////////////////
// Render sky
void SkyManager::RenderSky()
{
#if CONFIG2_GLES
#warning TODO: implement SkyManager::RenderSky for GLES
#else
// Draw the sky as a small box around the map, with depth write enabled.
// This will be done before anything else is drawn so we'll be overlapped by everything else.
// Do nothing unless SetSkySet was called
if (m_SkySet.empty())
return;
- glDepthMask( GL_FALSE );
+ glDepthMask(GL_FALSE);
pglActiveTextureARB(GL_TEXTURE0_ARB);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
- // Translate so we are at the center of the map.
- ssize_t mapSize = g_Game->GetWorld()->GetTerrain()->GetVerticesPerSide();
- glTranslatef(mapSize*(TERRAIN_TILE_SIZE/2.0f), m_HorizonHeight, mapSize*(TERRAIN_TILE_SIZE/2.0f) );
+ // Translate so the sky center is at the camera space origin.
+ CVector3D cameraPos = g_Renderer.GetViewCamera().GetOrientation().GetTranslation();
+ glTranslatef(cameraPos.X, cameraPos.Y, cameraPos.Z);
// Rotate so that the "left" face, which contains the brightest part of each
// skymap, is in the direction of the sun from our light environment
- glRotatef( 180.0f /*+ 45.0f*/ + RADTODEG(g_Renderer.GetLightEnv().GetRotation()), 0.0f, 1.0f, 0.0f );
+ glRotatef(180.0f + RADTODEG(g_Renderer.GetLightEnv().GetRotation()), 0.0f, 1.0f, 0.0f);
- // Distance to draw the faces at
- const float D = 1500.0f; // distance from map center
- const float H = 500.0f; // height of the ceiling
- const float FH = -100.0f; // height of the "floor"
+ // Currently we have a hardcoded near plane in the projection matrix.
+ glScalef(10.0f, 10.0f, 10.0f);
CShaderProgramPtr shader;
CShaderTechniquePtr skytech;
if (g_Renderer.GetRenderPath() == CRenderer::RP_SHADER)
{
skytech = g_Renderer.GetShaderManager().LoadEffect(str_sky_simple);
skytech->BeginPass();
shader = skytech->GetShader();
shader->BindTexture(str_baseTex, m_SkyCubeMap);
}
else
{
glDisable(GL_TEXTURE_2D);
glEnable(GL_TEXTURE_CUBE_MAP);
glBindTexture(GL_TEXTURE_CUBE_MAP, m_SkyCubeMap);
}
glBegin(GL_QUADS);
// GL_TEXTURE_CUBE_MAP_NEGATIVE_X
- glTexCoord3f( +1, +1, +1 ); glVertex3f( -D, FH, -D );
- glTexCoord3f( +1, +1, -1 ); glVertex3f( -D, FH, +D );
- glTexCoord3f( +1, -1, -1 ); glVertex3f( -D, +H, +D );
- glTexCoord3f( +1, -1, +1 ); glVertex3f( -D, +H, -D );
+ glTexCoord3f(+1, +1, +1); glVertex3f(-1.0f, -1.0f, -1.0f);
+ glTexCoord3f(+1, +1, -1); glVertex3f(-1.0f, -1.0f, +1.0f);
+ glTexCoord3f(+1, -1, -1); glVertex3f(-1.0f, +1.0f, +1.0f);
+ glTexCoord3f(+1, -1, +1); glVertex3f(-1.0f, +1.0f, -1.0f);
// GL_TEXTURE_CUBE_MAP_POSITIVE_X
- glTexCoord3f( -1, +1, -1 ); glVertex3f( +D, FH, +D );
- glTexCoord3f( -1, +1, +1 ); glVertex3f( +D, FH, -D );
- glTexCoord3f( -1, -1, +1 ); glVertex3f( +D, +H, -D );
- glTexCoord3f( -1, -1, -1 ); glVertex3f( +D, +H, +D );
+ glTexCoord3f(-1, +1, -1); glVertex3f(+1.0f, -1.0f, +1.0f);
+ glTexCoord3f(-1, +1, +1); glVertex3f(+1.0f, -1.0f, -1.0f);
+ glTexCoord3f(-1, -1, +1); glVertex3f(+1.0f, +1.0f, -1.0f);
+ glTexCoord3f(-1, -1, -1); glVertex3f(+1.0f, +1.0f, +1.0f);
// GL_TEXTURE_CUBE_MAP_NEGATIVE_Y
- glTexCoord3f( -1, +1, +1 ); glVertex3f( +D, FH, -D );
- glTexCoord3f( -1, +1, -1 ); glVertex3f( +D, FH, +D );
- glTexCoord3f( +1, +1, -1 ); glVertex3f( -D, FH, +D );
- glTexCoord3f( +1, +1, +1 ); glVertex3f( -D, FH, -D );
+ glTexCoord3f(-1, +1, +1); glVertex3f(+1.0f, -1.0f, -1.0f);
+ glTexCoord3f(-1, +1, -1); glVertex3f(+1.0f, -1.0f, +1.0f);
+ glTexCoord3f(+1, +1, -1); glVertex3f(-1.0f, -1.0f, +1.0f);
+ glTexCoord3f(+1, +1, +1); glVertex3f(-1.0f, -1.0f, -1.0f);
// GL_TEXTURE_CUBE_MAP_POSITIVE_Y
- glTexCoord3f( +1, -1, +1 ); glVertex3f( -D, +H, -D );
- glTexCoord3f( +1, -1, -1 ); glVertex3f( -D, +H, +D );
- glTexCoord3f( -1, -1, -1 ); glVertex3f( +D, +H, +D );
- glTexCoord3f( -1, -1, +1 ); glVertex3f( +D, +H, -D );
+ glTexCoord3f(+1, -1, +1); glVertex3f(-1.0f, +1.0f, -1.0f);
+ glTexCoord3f(+1, -1, -1); glVertex3f(-1.0f, +1.0f, +1.0f);
+ glTexCoord3f(-1, -1, -1); glVertex3f(+1.0f, +1.0f, +1.0f);
+ glTexCoord3f(-1, -1, +1); glVertex3f(+1.0f, +1.0f, -1.0f);
// GL_TEXTURE_CUBE_MAP_NEGATIVE_Z
- glTexCoord3f( -1, +1, +1 ); glVertex3f( +D, FH, -D );
- glTexCoord3f( +1, +1, +1 ); glVertex3f( -D, FH, -D );
- glTexCoord3f( +1, -1, +1 ); glVertex3f( -D, +H, -D );
- glTexCoord3f( -1, -1, +1 ); glVertex3f( +D, +H, -D );
+ glTexCoord3f(-1, +1, +1); glVertex3f(+1.0f, -1.0f, -1.0f);
+ glTexCoord3f(+1, +1, +1); glVertex3f(-1.0f, -1.0f, -1.0f);
+ glTexCoord3f(+1, -1, +1); glVertex3f(-1.0f, +1.0f, -1.0f);
+ glTexCoord3f(-1, -1, +1); glVertex3f(+1.0f, +1.0f, -1.0f);
// GL_TEXTURE_CUBE_MAP_POSITIVE_Z
- glTexCoord3f( +1, +1, -1 ); glVertex3f( -D, FH, +D );
- glTexCoord3f( -1, +1, -1 ); glVertex3f( +D, FH, +D );
- glTexCoord3f( -1, -1, -1 ); glVertex3f( +D, +H, +D );
- glTexCoord3f( +1, -1, -1 ); glVertex3f( -D, +H, +D );
+ glTexCoord3f(+1, +1, -1); glVertex3f(-1.0f, -1.0f, +1.0f);
+ glTexCoord3f(-1, +1, -1); glVertex3f(+1.0f, -1.0f, +1.0f);
+ glTexCoord3f(-1, -1, -1); glVertex3f(+1.0f, +1.0f, +1.0f);
+ glTexCoord3f(+1, -1, -1); glVertex3f(-1.0f, +1.0f, +1.0f);
+
glEnd();
if (g_Renderer.GetRenderPath() == CRenderer::RP_SHADER)
{
skytech->EndPass();
}
else
{
glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
glDisable(GL_TEXTURE_CUBE_MAP);
glEnable(GL_TEXTURE_2D);
}
glPopMatrix();
- glDepthMask( GL_TRUE );
+ glDepthMask(GL_TRUE);
#endif
}
Index: ps/trunk/source/renderer/TerrainRenderer.cpp
===================================================================
--- ps/trunk/source/renderer/TerrainRenderer.cpp (revision 22038)
+++ ps/trunk/source/renderer/TerrainRenderer.cpp (revision 22039)
@@ -1,964 +1,964 @@
-/* Copyright (C) 2013 Wildfire Games.
+/* Copyright (C) 2019 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/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
/**
* 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::RenderTerrain(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 = g_Renderer.GetShaderManager().LoadProgram("fixed:dummy", CShaderDefines());
dummyShader->Bind();
// 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|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, 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)
{
#if CONFIG2_GLES
#warning TODO: implement TerrainRenderer::RenderTerrainOverlayTexture for GLES
UNUSED2(cullGroup);
UNUSED2(textureMatrix);
#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);
CShaderProgramPtr dummyShader = g_Renderer.GetShaderManager().LoadProgram("fixed:dummy", CShaderDefines());
dummyShader->Bind();
CPatchRData::RenderStreams(visiblePatches, dummyShader, STREAM_POS|STREAM_POSTOUV0);
dummyShader->Unbind();
// To make the overlay visible over water, render an additional map-sized
// water-height patch
CBoundingBoxAligned waterBounds;
for (size_t i = 0; i < visiblePatches.size(); ++i)
{
CPatchRData* data = visiblePatches[i];
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
};
glVertexPointer(3, GL_FLOAT, 3*sizeof(float), waterPos);
glTexCoordPointer(3, GL_FLOAT, 3*sizeof(float), waterPos);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}
glMatrixMode(GL_TEXTURE);
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
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)
{
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 = g_Renderer.GetShaderManager().LoadProgram("fixed:dummy", CShaderDefines());
dummyShader->Bind();
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_Renderer.m_Options.m_PreferGLSL && !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();
CVector3D camPos = camera.m_Orientation.GetTranslation();
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_waterEffectsTexNorm, WaterMgr->m_FancyTextureNormal);
m->fancyWaterShader->BindTexture(str_waterEffectsTexOther, WaterMgr->m_FancyTextureOther);
}
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_skyCube, g_Renderer.GetSkyManager()->GetSkyCube());
m->fancyWaterShader->BindTexture(str_reflectionMap, WaterMgr->m_ReflectionTexture);
m->fancyWaterShader->BindTexture(str_losMap, 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_WaterReflection)
{
// TODO: check that this rotates in the right direction.
CMatrix3D skyBoxRotation;
skyBoxRotation.SetIdentity();
- skyBoxRotation.RotateY(M_PI - 0.3f + lightEnv.GetRotation());
+ skyBoxRotation.RotateY(M_PI + lightEnv.GetRotation());
m->fancyWaterShader->Uniform(str_skyBoxRot, skyBoxRotation);
}
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_reflectionMatrix, WaterMgr->m_ReflectionMatrix);
m->fancyWaterShader->Uniform(str_refractionMatrix, WaterMgr->m_RefractionMatrix);
m->fancyWaterShader->Uniform(str_losMatrix, losTexture.GetTextureMatrix());
m->fancyWaterShader->Uniform(str_cameraPos, camPos);
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;
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.
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);
// 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);
CShaderProgramPtr dummyShader = g_Renderer.GetShaderManager().LoadProgram("fixed:dummy", CShaderDefines());
dummyShader->Bind();
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(dummyShader, false, true);
}
glDisableClientState(GL_VERTEX_ARRAY);
dummyShader->Unbind();
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_TEXTURE_2D);
#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();
}