Index: ps/trunk/binaries/data/mods/public/shaders/arb/terrain_base.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/shaders/arb/terrain_base.xml (revision 24123)
+++ ps/trunk/binaries/data/mods/public/shaders/arb/terrain_base.xml (revision 24124)
@@ -1,26 +1,27 @@
+
-
+
-
+
-
+
-
+
Index: ps/trunk/binaries/data/mods/public/shaders/arb/terrain_blend.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/shaders/arb/terrain_blend.xml (revision 24123)
+++ ps/trunk/binaries/data/mods/public/shaders/arb/terrain_blend.xml (revision 24124)
@@ -1,28 +1,29 @@
+
-
+
Index: ps/trunk/binaries/data/mods/public/shaders/arb/terrain_common.vp
===================================================================
--- ps/trunk/binaries/data/mods/public/shaders/arb/terrain_common.vp (revision 24123)
+++ ps/trunk/binaries/data/mods/public/shaders/arb/terrain_common.vp (revision 24124)
@@ -1,64 +1,68 @@
!!ARBvp1.0
PARAM sunColor = program.local[0];
PARAM textureTransform = program.local[1];
PARAM losTransform = program.local[2];
PARAM shadowTransform[4] = { program.local[3..6] };
+PARAM sunDir = program.local[8];
#if USE_FP_SHADOW && USE_SHADOW_PCF
PARAM shadowScale = program.local[7];
#endif
TEMP lighting;
//// Compute position and normal:
ATTRIB position = vertex.position;
DP4 result.position.x, state.matrix.mvp.row[0], position;
DP4 result.position.y, state.matrix.mvp.row[1], position;
DP4 result.position.z, state.matrix.mvp.row[2], position;
DP4 result.position.w, state.matrix.mvp.row[3], position;
//// Compute lighting:
// Diffuse factor is precomputed in vertex attribute
// Scale diffuse to allow overbrightness (since result.color will be clamped to [0, 1])
-MUL lighting, vertex.color, 0.5;
+//
+DP3 lighting, -sunDir, vertex.normal;
+MAX lighting, 0.0, lighting; // DP3_SAT isn't available here.
+MUL lighting, lighting, 0.5;
// Apply light color
MUL result.color, lighting, sunColor;
//// Texture coordinates:
#if DECAL
MOV result.texcoord[0], vertex.texcoord[0];
#else
// Compute texcoords from position and terrain-texture-dependent transform.
// textureTransform is stored as [c, -s, s, 0],
// and we want texcoord = (x*c + z*-s, x*-s + z*-c, 0, 1)
DP3 result.texcoord[0].x, textureTransform.xyww, position.xzww;
DP3 result.texcoord[0].y, -textureTransform.zxww, position.xzww;
MOV result.texcoord[0].z, 0;
MOV result.texcoord[0].w, 1;
#endif
#if BLEND
MOV result.texcoord[1], vertex.texcoord[1];
#endif
#if USE_SHADOW
#if USE_FP_SHADOW && USE_SHADOW_PCF
TEMP shadowtc;
DP4 shadowtc.x, shadowTransform[0], position;
DP4 shadowtc.y, shadowTransform[1], position;
MUL result.texcoord[2].xy, shadowtc, shadowScale;
- #else
+ #else
DP4 result.texcoord[2].x, shadowTransform[0], position;
DP4 result.texcoord[2].y, shadowTransform[1], position;
#endif
DP4 result.texcoord[2].z, shadowTransform[2], position;
DP4 result.texcoord[2].w, shadowTransform[3], position;
#endif
MAD result.texcoord[3], position.xzzz, losTransform.x, losTransform.y;
END
Index: ps/trunk/binaries/data/mods/public/shaders/arb/terrain_decal.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/shaders/arb/terrain_decal.xml (revision 24123)
+++ ps/trunk/binaries/data/mods/public/shaders/arb/terrain_decal.xml (revision 24124)
@@ -1,27 +1,28 @@
+
-
+
Index: ps/trunk/binaries/data/mods/public/shaders/glsl/terrain_base.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/shaders/glsl/terrain_base.xml (revision 24123)
+++ ps/trunk/binaries/data/mods/public/shaders/glsl/terrain_base.xml (revision 24124)
@@ -1,18 +1,16 @@
-
-
+
-
-
+
-
+
Index: ps/trunk/binaries/data/mods/public/shaders/glsl/terrain_blend.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/shaders/glsl/terrain_blend.xml (revision 24123)
+++ ps/trunk/binaries/data/mods/public/shaders/glsl/terrain_blend.xml (revision 24124)
@@ -1,19 +1,17 @@
-
-
Index: ps/trunk/binaries/data/mods/public/shaders/glsl/terrain_common.fs
===================================================================
--- ps/trunk/binaries/data/mods/public/shaders/glsl/terrain_common.fs (revision 24123)
+++ ps/trunk/binaries/data/mods/public/shaders/glsl/terrain_common.fs (revision 24124)
@@ -1,256 +1,256 @@
#version 120
uniform sampler2D baseTex;
uniform sampler2D blendTex;
uniform sampler2D losTex;
uniform sampler2D normTex;
uniform sampler2D specTex;
#if USE_SHADOW
uniform float shadowAngle;
#if USE_SHADOW_SAMPLER
uniform sampler2DShadow shadowTex;
#if USE_SHADOW_PCF
uniform vec4 shadowScale;
#endif
#else
uniform sampler2D shadowTex;
#endif
#endif
uniform vec3 shadingColor;
uniform vec3 ambient;
uniform vec3 sunColor;
uniform vec3 sunDir;
uniform vec3 fogColor;
uniform vec2 fogParams;
uniform vec2 textureTransform;
varying vec3 v_lighting;
#if USE_SHADOW
varying vec4 v_shadow;
#endif
varying vec2 v_los;
varying vec2 v_blend;
#if USE_TRIPLANAR
varying vec3 v_tex;
#else
varying vec2 v_tex;
#endif
#if USE_SPECULAR
uniform float specularPower;
uniform vec3 specularColor;
#endif
#if USE_SPECULAR || USE_NORMAL_MAP || USE_SPECULAR_MAP || USE_AO
uniform vec4 effectSettings;
#endif
varying vec3 v_normal;
#if USE_SPECULAR || USE_NORMAL_MAP || USE_SPECULAR_MAP
#if USE_NORMAL_MAP
varying vec4 v_tangent;
varying vec3 v_bitangent;
#endif
#if USE_SPECULAR || USE_SPECULAR_MAP
varying vec3 v_half;
#endif
#endif
float get_shadow()
{
float shadowBias = 0.0005;
#if USE_SHADOW && !DISABLE_RECEIVE_SHADOWS
float biasedShdwZ = v_shadow.z - shadowBias;
#if USE_SHADOW_SAMPLER
#if USE_SHADOW_PCF
vec2 offset = fract(v_shadow.xy - 0.5);
vec4 size = vec4(offset + 1.0, 2.0 - offset);
vec4 weight = (vec4(1.0, 1.0, -0.5, -0.5) + (v_shadow.xy - 0.5*offset).xyxy) * shadowScale.zwzw;
return (1.0/9.0)*dot(size.zxzx*size.wwyy,
vec4(shadow2D(shadowTex, vec3(weight.zw, biasedShdwZ)).r,
shadow2D(shadowTex, vec3(weight.xw, biasedShdwZ)).r,
shadow2D(shadowTex, vec3(weight.zy, biasedShdwZ)).r,
shadow2D(shadowTex, vec3(weight.xy, biasedShdwZ)).r));
#else
return shadow2D(shadowTex, vec3(v_shadow.xy, biasedShdwZ)).r;
#endif
#else
if (biasedShdwZ >= 1.0)
return 1.0;
return (biasedShdwZ < texture2D(shadowTex, v_shadow.xy).x ? 1.0 : 0.0);
#endif
#else
return 1.0;
#endif
}
#if USE_TRIPLANAR
vec4 triplanar(sampler2D sampler, vec3 wpos)
{
float tighten = 0.4679;
vec3 blending = abs(normalize(v_normal)) - tighten;
blending = max(blending, 0.0);
blending /= vec3(blending.x + blending.y + blending.z);
vec3 signedBlending = sign(v_normal) * blending;
vec3 coords = wpos;
coords.xyz /= 32.0; // Ugh.
vec4 col1 = texture2D(sampler, coords.yz);
vec4 col2 = texture2D(sampler, coords.zx);
vec4 col3 = texture2D(sampler, coords.yx);
vec4 colBlended = col1 * blending.x + col2 * blending.y + col3 * blending.z;
return colBlended;
}
vec4 triplanarNormals(sampler2D sampler, vec3 wpos)
{
float tighten = 0.4679;
vec3 blending = abs(normalize(v_normal)) - tighten;
blending = max(blending, 0.0);
blending /= vec3(blending.x + blending.y + blending.z);
vec3 signedBlending = sign(v_normal) * blending;
vec3 coords = wpos;
coords.xyz /= 32.0; // Ugh.
vec4 col1 = texture2D(sampler, coords.yz).xyzw;
col1.y = 1.0 - col1.y;
vec4 col2 = texture2D(sampler, coords.zx).yxzw;
col2.y = 1.0 - col2.y;
vec4 col3 = texture2D(sampler, coords.yx).yxzw;
col3.y = 1.0 - col3.y;
vec4 colBlended = col1 * blending.x + col2 * blending.y + col3 * blending.z;
return colBlended;
}
#endif
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()
{
#if BLEND
// Use alpha from blend texture
gl_FragColor.a = 1.0 - texture2D(blendTex, v_blend).a;
#if USE_GRASS
if (gl_FragColor.a < LAYER / 10.0)
discard;
#endif
#else
gl_FragColor.a = 1.0;
#endif
#if USE_TRIPLANAR
vec4 tex = triplanar(baseTex, v_tex);
#else
vec4 tex = texture2D(baseTex, v_tex.xy);
#endif
#if USE_GRASS && LAYER
if (tex.a < 0.05)
discard;
#endif
#if DECAL
// Use alpha from main texture
gl_FragColor.a = tex.a;
#endif
vec3 texdiffuse = tex.rgb;
#if USE_SPECULAR || USE_SPECULAR_MAP || USE_NORMAL_MAP
vec3 normal = v_normal;
#endif
#if USE_NORMAL_MAP
float sign = v_tangent.w;
mat3 tbn = mat3(v_tangent.xyz, v_bitangent * -sign, v_normal);
#if USE_TRIPLANAR
vec3 ntex = triplanarNormals(normTex, v_tex).rgb * 2.0 - 1.0;
#else
vec3 ntex = texture2D(normTex, v_tex).rgb * 2.0 - 1.0;
#endif
normal = normalize(tbn * ntex);
vec3 bumplight = max(dot(-sunDir, normal), 0.0) * sunColor;
- vec3 sundiffuse = (bumplight - v_lighting.rgb) * effectSettings.x + v_lighting.rgb;
+ vec3 sundiffuse = (bumplight - v_lighting.rgb) * effectSettings.x + v_lighting.rgb;
#else
vec3 sundiffuse = v_lighting;
#endif
vec4 specular = vec4(0.0);
#if USE_SPECULAR || USE_SPECULAR_MAP
vec3 specCol;
float specPow;
#if USE_SPECULAR_MAP
#if USE_TRIPLANAR
vec4 s = triplanar(specTex, v_tex);
#else
vec4 s = texture2D(specTex, v_tex);
#endif
specCol = s.rgb;
specular.a = s.a;
specPow = effectSettings.y;
#else
specCol = specularColor;
specPow = specularPower;
#endif
specular.rgb = sunColor * specCol * pow(max(0.0, dot(normalize(normal), v_half)), specPow);
#endif
vec3 color = (texdiffuse * sundiffuse + specular.rgb) * get_shadow() + texdiffuse * ambient;
#if USE_SPECULAR_MAP && USE_SELF_LIGHT
color = mix(texdiffuse, color, specular.a);
#endif
#if USE_FOG
color = get_fog(color);
#endif
float los = texture2D(losTex, v_los).a;
los = los < 0.03 ? 0.0 : los;
color *= los;
#if DECAL
color *= shadingColor;
#endif
gl_FragColor.rgb = color;
#if USE_GRASS
gl_FragColor.a = tex.a;
#endif
}
Index: ps/trunk/binaries/data/mods/public/shaders/glsl/terrain_common.vs
===================================================================
--- ps/trunk/binaries/data/mods/public/shaders/glsl/terrain_common.vs (revision 24123)
+++ ps/trunk/binaries/data/mods/public/shaders/glsl/terrain_common.vs (revision 24124)
@@ -1,120 +1,119 @@
#version 120
uniform mat4 transform;
uniform vec3 cameraPos;
#ifdef GL_ES
uniform mediump vec3 sunDir;
uniform mediump vec3 sunColor;
#else
uniform vec3 sunDir;
uniform vec3 sunColor;
#endif
uniform vec2 textureTransform;
uniform vec2 losTransform;
uniform mat4 shadowTransform;
#if USE_SHADOW_SAMPLER && USE_SHADOW_PCF
uniform vec4 shadowScale;
#endif
varying vec3 v_lighting;
#if USE_SHADOW
varying vec4 v_shadow;
#endif
varying vec2 v_los;
varying vec2 v_blend;
#if USE_TRIPLANAR
varying vec3 v_tex;
#else
varying vec2 v_tex;
#endif
varying vec3 v_normal;
#if USE_SPECULAR || USE_NORMAL_MAP || USE_SPECULAR_MAP
#if USE_NORMAL_MAP
varying vec4 v_tangent;
varying vec3 v_bitangent;
#endif
#if USE_SPECULAR || USE_SPECULAR_MAP
varying vec3 v_half;
#endif
#endif
attribute vec3 a_vertex;
attribute vec3 a_normal;
-attribute vec3 a_color;
attribute vec2 a_uv0;
attribute vec2 a_uv1;
void main()
{
vec4 position = vec4(a_vertex, 1.0);
#if USE_GRASS && LAYER
position.y = a_vertex.y + (a_normal.y * 0.015 * LAYER);
#endif
gl_Position = transform * position;
- v_lighting = a_color * sunColor;
-
+ v_lighting = clamp(-dot(a_normal, sunDir), 0.0, 1.0) * sunColor;
+
#if DECAL
v_tex.xy = a_uv0;
#else
#if USE_TRIPLANAR
v_tex = a_vertex;
#else
// Compute texcoords from position and terrain-texture-dependent transform
float c = textureTransform.x;
float s = -textureTransform.y;
v_tex = vec2(a_vertex.x * c + a_vertex.z * -s, a_vertex.x * -s + a_vertex.z * -c);
#endif
#if GL_ES
// XXX: Ugly hack to hide some precision issues in GLES
#if USE_TRIPLANAR
v_tex = mod(v_tex, vec3(9.0, 9.0, 9.0));
#else
v_tex = mod(v_tex, vec2(9.0, 9.0));
#endif
#endif
#endif
#if BLEND
v_blend = a_uv1;
#endif
#if USE_SHADOW
v_shadow = shadowTransform * vec4(a_vertex, 1.0);
#if USE_SHADOW_SAMPLER && USE_SHADOW_PCF
v_shadow.xy *= shadowScale.xy;
- #endif
+ #endif
#endif
-
+
v_normal = a_normal;
#if USE_SPECULAR || USE_NORMAL_MAP || USE_SPECULAR_MAP || USE_TRIPLANAR
#if USE_NORMAL_MAP
vec3 t = vec3(1.0, 0.0, 0.0);
t = normalize(t - v_normal * dot(v_normal, t));
v_tangent = vec4(t, -1.0);
v_bitangent = cross(v_normal, t);
#endif
#if USE_SPECULAR || USE_SPECULAR_MAP
vec3 eyeVec = cameraPos.xyz - position.xyz;
#if USE_SPECULAR || USE_SPECULAR_MAP
vec3 sunVec = -sunDir;
v_half = normalize(sunVec + normalize(eyeVec));
#endif
#endif
#endif
v_los = a_vertex.xz * losTransform.x + losTransform.yy;
}
Index: ps/trunk/binaries/data/mods/public/shaders/glsl/terrain_decal.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/shaders/glsl/terrain_decal.xml (revision 24123)
+++ ps/trunk/binaries/data/mods/public/shaders/glsl/terrain_decal.xml (revision 24124)
@@ -1,19 +1,17 @@
-
-
Index: ps/trunk/source/graphics/LightEnv.h
===================================================================
--- ps/trunk/source/graphics/LightEnv.h (revision 24123)
+++ ps/trunk/source/graphics/LightEnv.h (revision 24124)
@@ -1,151 +1,127 @@
-/* Copyright (C) 2019 Wildfire Games.
+/* Copyright (C) 2020 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see .
*/
/*
* CLightEnv, a class describing the current lights
*/
#ifndef INCLUDED_LIGHTENV
#define INCLUDED_LIGHTENV
#include "graphics/Color.h"
#include "maths/MathUtil.h"
#include "maths/Vector3D.h"
class CMapWriter;
class CMapReader;
/**
* Class CLightEnv: description of a lighting environment - contains all the
* necessary parameters for representation of the lighting within a scenario
*/
class CLightEnv
{
public:
RGBColor m_SunColor;
RGBColor m_TerrainAmbientColor;
RGBColor m_UnitsAmbientColor;
RGBColor m_FogColor;
float m_FogFactor;
float m_FogMax;
float m_Brightness, m_Contrast, m_Saturation, m_Bloom;
CLightEnv();
float GetElevation() const { return m_Elevation; }
float GetRotation() const { return m_Rotation; }
const CVector3D& GetSunDir() const { return m_SunDir; }
void SetElevation(float f);
void SetRotation(float f);
/**
* Calculate brightness of a point of a unit with the given normal vector,
* for rendering with CPU lighting.
* The resulting color contains both ambient and diffuse light.
* To cope with sun overbrightness, the color is scaled by 0.5.
*
* @param normal normal vector (must have length 1)
*/
RGBColor EvaluateUnitScaled(const CVector3D& normal) const
{
float dot = -normal.Dot(m_SunDir);
RGBColor color = m_UnitsAmbientColor;
if (dot > 0)
color += m_SunColor * dot;
return color * 0.5f;
}
- /**
- * Compute the diffuse sun lighting color on terrain, for rendering with CPU lighting.
- * To cope with sun overbrightness, the color is scaled by 0.5.
- *
- * @param normal normal vector (must have length 1)
- */
- SColor4ub EvaluateTerrainDiffuseScaled(const CVector3D& normal) const
- {
- float dot = -normal.Dot(m_SunDir);
- return ConvertRGBColorTo4ub(m_SunColor * dot * 0.5f);
- }
-
- /**
- * Compute the diffuse sun lighting factor on terrain, for rendering with shader lighting.
- *
- * @param normal normal vector (must have length 1)
- */
- SColor4ub EvaluateTerrainDiffuseFactor(const CVector3D& normal) const
- {
- float dot = -normal.Dot(m_SunDir);
- u8 c = static_cast(Clamp(dot * 255.f, 0.f, 255.f));
- return SColor4ub(c, c, c, 255);
- }
-
// Comparison operators
bool operator==(const CLightEnv& o) const
{
return m_Elevation == o.m_Elevation &&
m_Rotation == o.m_Rotation &&
m_SunColor == o.m_SunColor &&
m_TerrainAmbientColor == o.m_TerrainAmbientColor &&
m_UnitsAmbientColor == o.m_UnitsAmbientColor &&
m_FogColor == o.m_FogColor &&
m_FogFactor == o.m_FogFactor &&
m_FogMax == o.m_FogMax &&
m_Brightness == o.m_Brightness &&
m_Contrast == o.m_Contrast &&
m_Saturation == o.m_Saturation &&
m_Bloom == o.m_Bloom;
}
bool operator!=(const CLightEnv& o) const
{
return !(*this == o);
}
private:
friend class CMapWriter;
friend class CMapReader;
friend class CXMLReader;
/**
* Height of sun above the horizon, in radians.
* For example, an elevation of M_PI/2 means the sun is straight up.
*/
float m_Elevation;
/**
* Direction of sun on the compass, in radians.
* For example, a rotation of zero means the sun is in the direction (0,0,-1)
* and a rotation of M_PI/2 means the sun is in the direction (1,0,0) (not taking
* elevation into account).
*/
float m_Rotation;
/**
* Vector corresponding to m_Elevation and m_Rotation.
* Updated by CalculateSunDirection.
*/
CVector3D m_SunDir;
void CalculateSunDirection();
};
#endif // INCLUDED_LIGHTENV
Index: ps/trunk/source/renderer/DecalRData.cpp
===================================================================
--- ps/trunk/source/renderer/DecalRData.cpp (revision 24123)
+++ ps/trunk/source/renderer/DecalRData.cpp (revision 24124)
@@ -1,292 +1,279 @@
-/* Copyright (C) 2019 Wildfire Games.
+/* Copyright (C) 2020 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see .
*/
#include "precompiled.h"
#include "DecalRData.h"
#include "graphics/Decal.h"
-#include "graphics/LightEnv.h"
#include "graphics/Model.h"
#include "graphics/ShaderManager.h"
#include "graphics/Terrain.h"
#include "graphics/TextureManager.h"
#include "ps/CLogger.h"
#include "ps/Game.h"
#include "ps/Profile.h"
#include "renderer/Renderer.h"
#include "renderer/TerrainRenderer.h"
#include "simulation2/Simulation2.h"
#include "simulation2/components/ICmpWaterManager.h"
// TODO: Currently each decal is a separate CDecalRData. We might want to use
// lots of decals for special effects like shadows, footprints, etc, in which
// case we should probably redesign this to batch them all together for more
// efficient rendering.
CDecalRData::CDecalRData(CModelDecal* decal, CSimulation2* simulation)
: m_Decal(decal), m_IndexArray(GL_STATIC_DRAW), m_Array(GL_STATIC_DRAW), m_Simulation(simulation)
{
m_Position.type = GL_FLOAT;
m_Position.elems = 3;
m_Array.AddAttribute(&m_Position);
m_Normal.type = GL_FLOAT;
m_Normal.elems = 3;
m_Array.AddAttribute(&m_Normal);
- m_DiffuseColor.type = GL_UNSIGNED_BYTE;
- m_DiffuseColor.elems = 4;
- m_Array.AddAttribute(&m_DiffuseColor);
-
m_UV.type = GL_FLOAT;
m_UV.elems = 2;
m_Array.AddAttribute(&m_UV);
BuildArrays();
}
CDecalRData::~CDecalRData()
{
}
void CDecalRData::Update(CSimulation2* simulation)
{
m_Simulation = simulation;
if (m_UpdateFlags != 0)
{
BuildArrays();
m_UpdateFlags = 0;
}
}
void CDecalRData::RenderDecals(std::vector& decals, const CShaderDefines& context,
ShadowMap* shadow, bool isDummyShader, const CShaderProgramPtr& dummy)
{
CShaderDefines contextDecal = context;
contextDecal.Add(str_DECAL, str_1);
for (size_t i = 0; i < decals.size(); ++i)
{
CDecalRData *decal = decals[i];
CMaterial &material = decal->m_Decal->m_Decal.m_Material;
if (material.GetShaderEffect().length() == 0)
{
LOGERROR("Terrain renderer failed to load shader effect.\n");
continue;
}
int numPasses = 1;
CShaderTechniquePtr techBase;
if (!isDummyShader)
{
techBase = g_Renderer.GetShaderManager().LoadEffect(
material.GetShaderEffect(), contextDecal, material.GetShaderDefines(0));
if (!techBase)
{
LOGERROR("Terrain renderer failed to load shader effect (%s)\n",
material.GetShaderEffect().string().c_str());
continue;
}
numPasses = techBase->GetNumPasses();
}
for (int pass = 0; pass < numPasses; ++pass)
{
if (!isDummyShader)
{
techBase->BeginPass(pass);
TerrainRenderer::PrepareShader(techBase->GetShader(), shadow);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}
const CShaderProgramPtr& shader = isDummyShader ? dummy : techBase->GetShader(pass);
if (material.GetSamplers().size() != 0)
{
const CMaterial::SamplersVector& samplers = material.GetSamplers();
size_t samplersNum = samplers.size();
for (size_t s = 0; s < samplersNum; ++s)
{
const CMaterial::TextureSampler& samp = samplers[s];
shader->BindTexture(samp.Name, samp.Sampler);
}
material.GetStaticUniforms().BindUniforms(shader);
// TODO: Need to handle floating decals correctly. In particular, we need
// to render non-floating before water and floating after water (to get
// the blending right), and we also need to apply the correct lighting in
// each case, which doesn't really seem possible with the current
// TerrainRenderer.
// Also, need to mark the decals as dirty when water height changes.
// glDisable(GL_TEXTURE_2D);
// m_Decal->GetBounds().Render();
// glEnable(GL_TEXTURE_2D);
u8* base = decal->m_Array.Bind();
GLsizei stride = (GLsizei)decal->m_Array.GetStride();
u8* indexBase = decal->m_IndexArray.Bind();
#if !CONFIG2_GLES
if (isDummyShader)
{
glColor3fv(decal->m_Decal->GetShadingColor().FloatArray());
}
else
#endif
{
shader->Uniform(str_shadingColor, decal->m_Decal->GetShadingColor());
}
shader->VertexPointer(3, GL_FLOAT, stride, base + decal->m_Position.offset);
shader->NormalPointer(GL_FLOAT, stride, base + decal->m_Normal.offset);
- shader->ColorPointer(4, GL_UNSIGNED_BYTE, stride, base + decal->m_DiffuseColor.offset);
shader->TexCoordPointer(GL_TEXTURE0, 2, GL_FLOAT, stride, base + decal->m_UV.offset);
shader->AssertPointersBound();
if (!g_Renderer.m_SkipSubmit)
{
glDrawElements(GL_TRIANGLES, (GLsizei)decal->m_IndexArray.GetNumVertices(), GL_UNSIGNED_SHORT, indexBase);
}
// bump stats
g_Renderer.m_Stats.m_DrawCalls++;
g_Renderer.m_Stats.m_TerrainTris += decal->m_IndexArray.GetNumVertices() / 3;
CVertexBuffer::Unbind();
}
if (!isDummyShader)
{
glDisable(GL_BLEND);
techBase->EndPass();
}
}
}
}
void CDecalRData::BuildArrays()
{
PROFILE("decal build");
const SDecal& decal = m_Decal->m_Decal;
// TODO: Currently this constructs an axis-aligned bounding rectangle around
// the decal. It would be more efficient for rendering if we excluded tiles
// that are outside the (non-axis-aligned) decal rectangle.
ssize_t i0, j0, i1, j1;
m_Decal->CalcVertexExtents(i0, j0, i1, j1);
// Construct vertex data arrays
CmpPtr cmpWaterManager(*m_Simulation, SYSTEM_ENTITY);
m_Array.SetNumVertices((i1-i0+1)*(j1-j0+1));
m_Array.Layout();
VertexArrayIterator Position = m_Position.GetIterator();
VertexArrayIterator Normal = m_Normal.GetIterator();
- VertexArrayIterator DiffuseColor = m_DiffuseColor.GetIterator();
VertexArrayIterator UV = m_UV.GetIterator();
- const CLightEnv& lightEnv = g_Renderer.GetLightEnv();
- bool cpuLighting = (g_RenderingOptions.GetRenderPath() == RenderPath::FIXED);
-
for (ssize_t j = j0; j <= j1; ++j)
{
for (ssize_t i = i0; i <= i1; ++i)
{
CVector3D pos;
m_Decal->m_Terrain->CalcPosition(i, j, pos);
if (decal.m_Floating && cmpWaterManager)
pos.Y = std::max(pos.Y, cmpWaterManager->GetExactWaterLevel(pos.X, pos.Z));
*Position = pos;
++Position;
CVector3D normal;
m_Decal->m_Terrain->CalcNormal(i, j, normal);
*Normal = normal;
Normal++;
- *DiffuseColor = cpuLighting ? lightEnv.EvaluateTerrainDiffuseScaled(normal) : lightEnv.EvaluateTerrainDiffuseFactor(normal);
- ++DiffuseColor;
-
// Map from world space back into decal texture space
CVector3D inv = m_Decal->GetInvTransform().Transform(pos);
(*UV)[0] = 0.5f + (inv.X - decal.m_OffsetX) / decal.m_SizeX;
(*UV)[1] = 0.5f - (inv.Z - decal.m_OffsetZ) / decal.m_SizeZ; // flip V to match our texture convention
++UV;
}
}
m_Array.Upload();
m_Array.FreeBackingStore();
// Construct index arrays for each terrain tile
m_IndexArray.SetNumVertices((i1-i0)*(j1-j0)*6);
m_IndexArray.Layout();
VertexArrayIterator Index = m_IndexArray.GetIterator();
u16 base = 0;
ssize_t w = i1-i0+1;
for (ssize_t dj = 0; dj < j1-j0; ++dj)
{
for (ssize_t di = 0; di < i1-i0; ++di)
{
bool dir = m_Decal->m_Terrain->GetTriangulationDir(i0+di, j0+dj);
if (dir)
{
*Index++ = u16(((dj+0)*w+(di+0))+base);
*Index++ = u16(((dj+0)*w+(di+1))+base);
*Index++ = u16(((dj+1)*w+(di+0))+base);
*Index++ = u16(((dj+0)*w+(di+1))+base);
*Index++ = u16(((dj+1)*w+(di+1))+base);
*Index++ = u16(((dj+1)*w+(di+0))+base);
}
else
{
*Index++ = u16(((dj+0)*w+(di+0))+base);
*Index++ = u16(((dj+0)*w+(di+1))+base);
*Index++ = u16(((dj+1)*w+(di+1))+base);
*Index++ = u16(((dj+1)*w+(di+1))+base);
*Index++ = u16(((dj+1)*w+(di+0))+base);
*Index++ = u16(((dj+0)*w+(di+0))+base);
}
}
}
m_IndexArray.Upload();
m_IndexArray.FreeBackingStore();
}
Index: ps/trunk/source/renderer/DecalRData.h
===================================================================
--- ps/trunk/source/renderer/DecalRData.h (revision 24123)
+++ ps/trunk/source/renderer/DecalRData.h (revision 24124)
@@ -1,60 +1,59 @@
-/* Copyright (C) 2015 Wildfire Games.
+/* Copyright (C) 2020 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see .
*/
#ifndef INCLUDED_DECALRDATA
#define INCLUDED_DECALRDATA
#include "graphics/Camera.h"
#include "graphics/RenderableObject.h"
#include "graphics/ShaderProgramPtr.h"
#include "renderer/VertexArray.h"
class CModelDecal;
class CShaderDefines;
class CSimulation2;
class ShadowMap;
class CDecalRData : public CRenderData
{
public:
CDecalRData(CModelDecal* decal, CSimulation2* simulation);
~CDecalRData();
void Update(CSimulation2* simulation);
static void RenderDecals(std::vector& decals, const CShaderDefines& context,
ShadowMap* shadow, bool isDummyShader=false, const CShaderProgramPtr& dummy=CShaderProgramPtr());
CModelDecal* GetDecal() { return m_Decal; }
private:
void BuildArrays();
VertexIndexArray m_IndexArray;
VertexArray m_Array;
VertexArray::Attribute m_Position;
VertexArray::Attribute m_Normal;
- VertexArray::Attribute m_DiffuseColor;
VertexArray::Attribute m_UV;
CModelDecal* m_Decal;
CSimulation2* m_Simulation;
};
#endif // INCLUDED_DECALRDATA
Index: ps/trunk/source/renderer/PatchRData.cpp
===================================================================
--- ps/trunk/source/renderer/PatchRData.cpp (revision 24123)
+++ ps/trunk/source/renderer/PatchRData.cpp (revision 24124)
@@ -1,1542 +1,1524 @@
-/* Copyright (C) 2019 Wildfire Games.
+/* Copyright (C) 2020 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see .
*/
#include "precompiled.h"
#include "renderer/PatchRData.h"
#include "graphics/GameView.h"
#include "graphics/LightEnv.h"
#include "graphics/LOSTexture.h"
#include "graphics/Patch.h"
#include "graphics/ShaderManager.h"
#include "graphics/Terrain.h"
#include "graphics/TextRenderer.h"
#include "lib/alignment.h"
#include "lib/allocators/arena.h"
#include "maths/MathUtil.h"
#include "ps/CLogger.h"
#include "ps/Game.h"
#include "ps/GameSetup/Config.h"
#include "ps/Profile.h"
#include "ps/Pyrogenesis.h"
#include "ps/World.h"
#include "renderer/AlphaMapCalculator.h"
#include "renderer/Renderer.h"
#include "renderer/TerrainRenderer.h"
#include "renderer/WaterManager.h"
#include "simulation2/components/ICmpWaterManager.h"
#include "simulation2/Simulation2.h"
#include
#include
#include
const ssize_t BlendOffsets[9][2] = {
{ 0, -1 },
{ -1, -1 },
{ -1, 0 },
{ -1, 1 },
{ 0, 1 },
{ 1, 1 },
{ 1, 0 },
{ 1, -1 },
{ 0, 0 }
};
///////////////////////////////////////////////////////////////////
// CPatchRData constructor
CPatchRData::CPatchRData(CPatch* patch, CSimulation2* simulation) :
m_Patch(patch), m_VBSides(0),
m_VBBase(0), m_VBBaseIndices(0),
m_VBBlends(0), m_VBBlendIndices(0),
m_VBWater(0), m_VBWaterIndices(0),
m_VBWaterShore(0), m_VBWaterIndicesShore(0),
m_Simulation(simulation)
{
ENSURE(patch);
Build();
}
///////////////////////////////////////////////////////////////////
// CPatchRData destructor
CPatchRData::~CPatchRData()
{
// release vertex buffer chunks
if (m_VBSides) g_VBMan.Release(m_VBSides);
if (m_VBBase) g_VBMan.Release(m_VBBase);
if (m_VBBaseIndices) g_VBMan.Release(m_VBBaseIndices);
if (m_VBBlends) g_VBMan.Release(m_VBBlends);
if (m_VBBlendIndices) g_VBMan.Release(m_VBBlendIndices);
if (m_VBWater) g_VBMan.Release(m_VBWater);
if (m_VBWaterIndices) g_VBMan.Release(m_VBWaterIndices);
if (m_VBWaterShore) g_VBMan.Release(m_VBWaterShore);
if (m_VBWaterIndicesShore) g_VBMan.Release(m_VBWaterIndicesShore);
}
/**
* Represents a blend for a single tile, texture and shape.
*/
struct STileBlend
{
CTerrainTextureEntry* m_Texture;
int m_Priority;
u16 m_TileMask; // bit n set if this blend contains neighbour tile BlendOffsets[n]
struct DecreasingPriority
{
bool operator()(const STileBlend& a, const STileBlend& b) const
{
if (a.m_Priority > b.m_Priority)
return true;
if (a.m_Priority < b.m_Priority)
return false;
if (a.m_Texture && b.m_Texture)
return a.m_Texture->GetTag() > b.m_Texture->GetTag();
return false;
}
};
struct CurrentTile
{
bool operator()(const STileBlend& a) const
{
return (a.m_TileMask & (1 << 8)) != 0;
}
};
};
/**
* Represents the ordered collection of blends drawn on a particular tile.
*/
struct STileBlendStack
{
u8 i, j;
std::vector blends; // back of vector is lowest-priority texture
};
/**
* Represents a batched collection of blends using the same texture.
*/
struct SBlendLayer
{
struct Tile
{
u8 i, j;
u8 shape;
};
CTerrainTextureEntry* m_Texture;
std::vector m_Tiles;
};
void CPatchRData::BuildBlends()
{
PROFILE3("build blends");
m_BlendSplats.clear();
std::vector blendVertices;
std::vector blendIndices;
CTerrain* terrain = m_Patch->m_Parent;
std::vector blendStacks;
blendStacks.reserve(PATCH_SIZE*PATCH_SIZE);
// For each tile in patch ..
for (ssize_t j = 0; j < PATCH_SIZE; ++j)
{
for (ssize_t i = 0; i < PATCH_SIZE; ++i)
{
ssize_t gx = m_Patch->m_X * PATCH_SIZE + i;
ssize_t gz = m_Patch->m_Z * PATCH_SIZE + j;
std::vector blends;
blends.reserve(9);
// Compute a blend for every tile in the 3x3 square around this tile
for (size_t n = 0; n < 9; ++n)
{
ssize_t ox = gx + BlendOffsets[n][1];
ssize_t oz = gz + BlendOffsets[n][0];
CMiniPatch* nmp = terrain->GetTile(ox, oz);
if (!nmp)
continue;
STileBlend blend;
blend.m_Texture = nmp->GetTextureEntry();
blend.m_Priority = nmp->GetPriority();
blend.m_TileMask = 1 << n;
blends.push_back(blend);
}
// Sort the blends, highest priority first
std::sort(blends.begin(), blends.end(), STileBlend::DecreasingPriority());
STileBlendStack blendStack;
blendStack.i = i;
blendStack.j = j;
// Put the blends into the tile's stack, merging any adjacent blends with the same texture
for (size_t k = 0; k < blends.size(); ++k)
{
if (!blendStack.blends.empty() && blendStack.blends.back().m_Texture == blends[k].m_Texture)
blendStack.blends.back().m_TileMask |= blends[k].m_TileMask;
else
blendStack.blends.push_back(blends[k]);
}
// Remove blends that are after (i.e. lower priority than) the current tile
// (including the current tile), since we don't want to render them on top of
// the tile's base texture
blendStack.blends.erase(
std::find_if(blendStack.blends.begin(), blendStack.blends.end(), STileBlend::CurrentTile()),
blendStack.blends.end());
blendStacks.push_back(blendStack);
}
}
// Given the blend stack per tile, we want to batch together as many blends as possible.
// Group them into a series of layers (each of which has a single texture):
// (This is effectively a topological sort / linearisation of the partial order induced
// by the per-tile stacks, preferring to make tiles with equal textures adjacent.)
std::vector blendLayers;
while (true)
{
if (!blendLayers.empty())
{
// Try to grab as many tiles as possible that match our current layer,
// from off the blend stacks of all the tiles
CTerrainTextureEntry* tex = blendLayers.back().m_Texture;
for (size_t k = 0; k < blendStacks.size(); ++k)
{
if (!blendStacks[k].blends.empty() && blendStacks[k].blends.back().m_Texture == tex)
{
SBlendLayer::Tile t = { blendStacks[k].i, blendStacks[k].j, (u8)blendStacks[k].blends.back().m_TileMask };
blendLayers.back().m_Tiles.push_back(t);
blendStacks[k].blends.pop_back();
}
// (We've already merged adjacent entries of the same texture in each stack,
// so we don't need to bother looping to check the next entry in this stack again)
}
}
// We've grabbed as many tiles as possible; now we need to start a new layer.
// The new layer's texture could come from the back of any non-empty stack;
// choose the longest stack as a heuristic to reduce the number of layers
CTerrainTextureEntry* bestTex = NULL;
size_t bestStackSize = 0;
for (size_t k = 0; k < blendStacks.size(); ++k)
{
if (blendStacks[k].blends.size() > bestStackSize)
{
bestStackSize = blendStacks[k].blends.size();
bestTex = blendStacks[k].blends.back().m_Texture;
}
}
// If all our stacks were empty, we're done
if (bestStackSize == 0)
break;
// Otherwise add the new layer, then loop back and start filling it in
SBlendLayer layer;
layer.m_Texture = bestTex;
blendLayers.push_back(layer);
}
// Now build outgoing splats
m_BlendSplats.resize(blendLayers.size());
for (size_t k = 0; k < blendLayers.size(); ++k)
{
SSplat& splat = m_BlendSplats[k];
splat.m_IndexStart = blendIndices.size();
splat.m_Texture = blendLayers[k].m_Texture;
for (size_t t = 0; t < blendLayers[k].m_Tiles.size(); ++t)
{
SBlendLayer::Tile& tile = blendLayers[k].m_Tiles[t];
AddBlend(blendVertices, blendIndices, tile.i, tile.j, tile.shape, splat.m_Texture);
}
splat.m_IndexCount = blendIndices.size() - splat.m_IndexStart;
}
// Release existing vertex buffer chunks
if (m_VBBlends)
{
g_VBMan.Release(m_VBBlends);
m_VBBlends = 0;
}
if (m_VBBlendIndices)
{
g_VBMan.Release(m_VBBlendIndices);
m_VBBlendIndices = 0;
}
if (blendVertices.size())
{
// Construct vertex buffer
m_VBBlends = g_VBMan.Allocate(sizeof(SBlendVertex), blendVertices.size(), GL_STATIC_DRAW, GL_ARRAY_BUFFER);
m_VBBlends->m_Owner->UpdateChunkVertices(m_VBBlends, &blendVertices[0]);
// Update the indices to include the base offset of the vertex data
for (size_t k = 0; k < blendIndices.size(); ++k)
blendIndices[k] += m_VBBlends->m_Index;
m_VBBlendIndices = g_VBMan.Allocate(sizeof(u16), blendIndices.size(), GL_STATIC_DRAW, GL_ELEMENT_ARRAY_BUFFER);
m_VBBlendIndices->m_Owner->UpdateChunkVertices(m_VBBlendIndices, &blendIndices[0]);
}
}
void CPatchRData::AddBlend(std::vector& blendVertices, std::vector& blendIndices,
u16 i, u16 j, u8 shape, CTerrainTextureEntry* texture)
{
CTerrain* terrain = m_Patch->m_Parent;
ssize_t gx = m_Patch->m_X * PATCH_SIZE + i;
ssize_t gz = m_Patch->m_Z * PATCH_SIZE + j;
// uses the current neighbour texture
BlendShape8 shape8;
for (size_t m = 0; m < 8; ++m)
shape8[m] = (shape & (1 << m)) ? 0 : 1;
// calculate the required alphamap and the required rotation of the alphamap from blendshape
unsigned int alphamapflags;
int alphamap = CAlphaMapCalculator::Calculate(shape8, alphamapflags);
// now actually render the blend tile (if we need one)
if (alphamap == -1)
return;
float u0 = texture->m_TerrainAlpha->second.m_AlphaMapCoords[alphamap].u0;
float u1 = texture->m_TerrainAlpha->second.m_AlphaMapCoords[alphamap].u1;
float v0 = texture->m_TerrainAlpha->second.m_AlphaMapCoords[alphamap].v0;
float v1 = texture->m_TerrainAlpha->second.m_AlphaMapCoords[alphamap].v1;
if (alphamapflags & BLENDMAP_FLIPU)
std::swap(u0, u1);
if (alphamapflags & BLENDMAP_FLIPV)
std::swap(v0, v1);
int base = 0;
if (alphamapflags & BLENDMAP_ROTATE90)
base = 1;
else if (alphamapflags & BLENDMAP_ROTATE180)
base = 2;
else if (alphamapflags & BLENDMAP_ROTATE270)
base = 3;
SBlendVertex vtx[4];
vtx[(base + 0) % 4].m_AlphaUVs[0] = u0;
vtx[(base + 0) % 4].m_AlphaUVs[1] = v0;
vtx[(base + 1) % 4].m_AlphaUVs[0] = u1;
vtx[(base + 1) % 4].m_AlphaUVs[1] = v0;
vtx[(base + 2) % 4].m_AlphaUVs[0] = u1;
vtx[(base + 2) % 4].m_AlphaUVs[1] = v1;
vtx[(base + 3) % 4].m_AlphaUVs[0] = u0;
vtx[(base + 3) % 4].m_AlphaUVs[1] = v1;
SBlendVertex dst;
- const CLightEnv& lightEnv = g_Renderer.GetLightEnv();
CVector3D normal;
- bool cpuLighting = (g_RenderingOptions.GetRenderPath() == RenderPath::FIXED);
-
size_t index = blendVertices.size();
terrain->CalcPosition(gx, gz, dst.m_Position);
terrain->CalcNormal(gx, gz, normal);
dst.m_Normal = normal;
- dst.m_DiffuseColor = cpuLighting ? lightEnv.EvaluateTerrainDiffuseScaled(normal) : lightEnv.EvaluateTerrainDiffuseFactor(normal);
dst.m_AlphaUVs[0] = vtx[0].m_AlphaUVs[0];
dst.m_AlphaUVs[1] = vtx[0].m_AlphaUVs[1];
blendVertices.push_back(dst);
terrain->CalcPosition(gx + 1, gz, dst.m_Position);
terrain->CalcNormal(gx + 1, gz, normal);
dst.m_Normal = normal;
- dst.m_DiffuseColor = cpuLighting ? lightEnv.EvaluateTerrainDiffuseScaled(normal) : lightEnv.EvaluateTerrainDiffuseFactor(normal);
dst.m_AlphaUVs[0] = vtx[1].m_AlphaUVs[0];
dst.m_AlphaUVs[1] = vtx[1].m_AlphaUVs[1];
blendVertices.push_back(dst);
terrain->CalcPosition(gx + 1, gz + 1, dst.m_Position);
terrain->CalcNormal(gx + 1, gz + 1, normal);
dst.m_Normal = normal;
- dst.m_DiffuseColor = cpuLighting ? lightEnv.EvaluateTerrainDiffuseScaled(normal) : lightEnv.EvaluateTerrainDiffuseFactor(normal);
dst.m_AlphaUVs[0] = vtx[2].m_AlphaUVs[0];
dst.m_AlphaUVs[1] = vtx[2].m_AlphaUVs[1];
blendVertices.push_back(dst);
terrain->CalcPosition(gx, gz + 1, dst.m_Position);
terrain->CalcNormal(gx, gz + 1, normal);
dst.m_Normal = normal;
- dst.m_DiffuseColor = cpuLighting ? lightEnv.EvaluateTerrainDiffuseScaled(normal) : lightEnv.EvaluateTerrainDiffuseFactor(normal);
dst.m_AlphaUVs[0] = vtx[3].m_AlphaUVs[0];
dst.m_AlphaUVs[1] = vtx[3].m_AlphaUVs[1];
blendVertices.push_back(dst);
bool dir = terrain->GetTriangulationDir(gx, gz);
if (dir)
{
blendIndices.push_back(index+0);
blendIndices.push_back(index+1);
blendIndices.push_back(index+3);
blendIndices.push_back(index+1);
blendIndices.push_back(index+2);
blendIndices.push_back(index+3);
}
else
{
blendIndices.push_back(index+0);
blendIndices.push_back(index+1);
blendIndices.push_back(index+2);
blendIndices.push_back(index+2);
blendIndices.push_back(index+3);
blendIndices.push_back(index+0);
}
}
void CPatchRData::BuildIndices()
{
PROFILE3("build indices");
CTerrain* terrain = m_Patch->m_Parent;
ssize_t px = m_Patch->m_X * PATCH_SIZE;
ssize_t pz = m_Patch->m_Z * PATCH_SIZE;
// must have allocated some vertices before trying to build corresponding indices
ENSURE(m_VBBase);
// number of vertices in each direction in each patch
ssize_t vsize=PATCH_SIZE+1;
// PATCH_SIZE must be 2^8-2 or less to not overflow u16 indices buffer. Thankfully this is always true.
ENSURE(vsize*vsize < 65536);
std::vector indices;
indices.reserve(PATCH_SIZE * PATCH_SIZE * 4);
// release existing splats
m_Splats.clear();
// build grid of textures on this patch
std::vector textures;
CTerrainTextureEntry* texgrid[PATCH_SIZE][PATCH_SIZE];
for (ssize_t j=0;jm_MiniPatches[j][i].GetTextureEntry();
texgrid[j][i]=tex;
if (std::find(textures.begin(),textures.end(),tex)==textures.end()) {
textures.push_back(tex);
}
}
}
// now build base splats from interior textures
m_Splats.resize(textures.size());
// build indices for base splats
size_t base=m_VBBase->m_Index;
for (size_t i=0;iGetTriangulationDir(px+i, pz+j);
if (dir)
{
indices.push_back(u16(((j+0)*vsize+(i+0))+base));
indices.push_back(u16(((j+0)*vsize+(i+1))+base));
indices.push_back(u16(((j+1)*vsize+(i+0))+base));
indices.push_back(u16(((j+0)*vsize+(i+1))+base));
indices.push_back(u16(((j+1)*vsize+(i+1))+base));
indices.push_back(u16(((j+1)*vsize+(i+0))+base));
}
else
{
indices.push_back(u16(((j+0)*vsize+(i+0))+base));
indices.push_back(u16(((j+0)*vsize+(i+1))+base));
indices.push_back(u16(((j+1)*vsize+(i+1))+base));
indices.push_back(u16(((j+1)*vsize+(i+1))+base));
indices.push_back(u16(((j+1)*vsize+(i+0))+base));
indices.push_back(u16(((j+0)*vsize+(i+0))+base));
}
}
}
}
splat.m_IndexCount=indices.size()-splat.m_IndexStart;
}
// Release existing vertex buffer chunk
if (m_VBBaseIndices)
{
g_VBMan.Release(m_VBBaseIndices);
m_VBBaseIndices = 0;
}
ENSURE(indices.size());
// Construct vertex buffer
m_VBBaseIndices = g_VBMan.Allocate(sizeof(u16), indices.size(), GL_STATIC_DRAW, GL_ELEMENT_ARRAY_BUFFER);
m_VBBaseIndices->m_Owner->UpdateChunkVertices(m_VBBaseIndices, &indices[0]);
}
void CPatchRData::BuildVertices()
{
PROFILE3("build vertices");
// create both vertices and lighting colors
// number of vertices in each direction in each patch
- ssize_t vsize=PATCH_SIZE+1;
+ ssize_t vsize = PATCH_SIZE + 1;
std::vector vertices;
- vertices.resize(vsize*vsize);
+ vertices.resize(vsize * vsize);
// get index of this patch
- ssize_t px=m_Patch->m_X;
- ssize_t pz=m_Patch->m_Z;
+ ssize_t px = m_Patch->m_X;
+ ssize_t pz = m_Patch->m_Z;
- CTerrain* terrain=m_Patch->m_Parent;
- const CLightEnv& lightEnv = g_Renderer.GetLightEnv();
-
- bool cpuLighting = (g_RenderingOptions.GetRenderPath() == RenderPath::FIXED);
+ CTerrain* terrain = m_Patch->m_Parent;
// build vertices
- for (ssize_t j=0;jCalcPosition(ix,iz,vertices[v].m_Position);
+ terrain->CalcPosition(ix, iz, vertices[v].m_Position);
- // Calculate diffuse lighting for this vertex
- // Ambient is added by the lighting pass (since ambient is the same
- // for all vertices, it need not be stored in the vertex structure)
CVector3D normal;
- terrain->CalcNormal(ix,iz,normal);
-
+ terrain->CalcNormal(ix, iz, normal);
vertices[v].m_Normal = normal;
-
- vertices[v].m_DiffuseColor = cpuLighting ? lightEnv.EvaluateTerrainDiffuseScaled(normal) : lightEnv.EvaluateTerrainDiffuseFactor(normal);
}
}
// upload to vertex buffer
if (!m_VBBase)
m_VBBase = g_VBMan.Allocate(sizeof(SBaseVertex), vsize * vsize, GL_STATIC_DRAW, GL_ARRAY_BUFFER);
m_VBBase->m_Owner->UpdateChunkVertices(m_VBBase, &vertices[0]);
}
void CPatchRData::BuildSide(std::vector& vertices, CPatchSideFlags side)
{
ssize_t vsize = PATCH_SIZE + 1;
CTerrain* terrain = m_Patch->m_Parent;
CmpPtr cmpWaterManager(*m_Simulation, SYSTEM_ENTITY);
for (ssize_t k = 0; k < vsize; k++)
{
ssize_t gx = m_Patch->m_X * PATCH_SIZE;
ssize_t gz = m_Patch->m_Z * PATCH_SIZE;
switch (side)
{
case CPATCH_SIDE_NEGX: gz += k; break;
case CPATCH_SIDE_POSX: gx += PATCH_SIZE; gz += PATCH_SIZE-k; break;
case CPATCH_SIDE_NEGZ: gx += PATCH_SIZE-k; break;
case CPATCH_SIDE_POSZ: gz += PATCH_SIZE; gx += k; break;
}
CVector3D pos;
terrain->CalcPosition(gx, gz, pos);
// Clamp the height to the water level
float waterHeight = 0.f;
if (cmpWaterManager)
waterHeight = cmpWaterManager->GetExactWaterLevel(pos.X, pos.Z);
pos.Y = std::max(pos.Y, waterHeight);
SSideVertex v0, v1;
v0.m_Position = pos;
v1.m_Position = pos;
v1.m_Position.Y = 0;
// If this is the start of this tristrip, but we've already got a partial
// tristrip, add a couple of degenerate triangles to join the strips properly
if (k == 0 && !vertices.empty())
{
vertices.push_back(vertices.back());
vertices.push_back(v1);
}
// Now add the new triangles
vertices.push_back(v1);
vertices.push_back(v0);
}
}
void CPatchRData::BuildSides()
{
PROFILE3("build sides");
std::vector sideVertices;
int sideFlags = m_Patch->GetSideFlags();
// If no sides are enabled, we don't need to do anything
if (!sideFlags)
return;
// For each side, generate a tristrip by adding a vertex at ground/water
// level and a vertex underneath at height 0.
if (sideFlags & CPATCH_SIDE_NEGX)
BuildSide(sideVertices, CPATCH_SIDE_NEGX);
if (sideFlags & CPATCH_SIDE_POSX)
BuildSide(sideVertices, CPATCH_SIDE_POSX);
if (sideFlags & CPATCH_SIDE_NEGZ)
BuildSide(sideVertices, CPATCH_SIDE_NEGZ);
if (sideFlags & CPATCH_SIDE_POSZ)
BuildSide(sideVertices, CPATCH_SIDE_POSZ);
if (sideVertices.empty())
return;
if (!m_VBSides)
m_VBSides = g_VBMan.Allocate(sizeof(SSideVertex), sideVertices.size(), GL_STATIC_DRAW, GL_ARRAY_BUFFER);
m_VBSides->m_Owner->UpdateChunkVertices(m_VBSides, &sideVertices[0]);
}
void CPatchRData::Build()
{
BuildVertices();
BuildSides();
BuildIndices();
BuildBlends();
BuildWater();
}
void CPatchRData::Update(CSimulation2* simulation)
{
m_Simulation = simulation;
if (m_UpdateFlags!=0) {
// TODO,RC 11/04/04 - need to only rebuild necessary bits of renderdata rather
// than everything; it's complicated slightly because the blends are dependent
// on both vertex and index data
BuildVertices();
BuildSides();
BuildIndices();
BuildBlends();
BuildWater();
m_UpdateFlags=0;
}
}
// Types used for glMultiDrawElements batching:
// To minimise the cost of memory allocations, everything used for computing
// batches uses a arena allocator. (All allocations are short-lived so we can
// just throw away the whole arena at the end of each frame.)
// std::map types with appropriate arena allocators and default comparison operator
#define POOLED_BATCH_MAP(Key, Value) \
std::map, ProxyAllocator, Allocators::DynamicArena > >
// Equivalent to "m[k]", when it returns a arena-allocated std::map (since we can't
// use the default constructor in that case)
template
typename M::mapped_type& PooledMapGet(M& m, const typename M::key_type& k, Allocators::DynamicArena& arena)
{
return m.insert(std::make_pair(k,
typename M::mapped_type(typename M::mapped_type::key_compare(), typename M::mapped_type::allocator_type(arena))
)).first->second;
}
// Equivalent to "m[k]", when it returns a std::pair of arena-allocated std::vectors
template
typename M::mapped_type& PooledPairGet(M& m, const typename M::key_type& k, Allocators::DynamicArena& arena)
{
return m.insert(std::make_pair(k, std::make_pair(
typename M::mapped_type::first_type(typename M::mapped_type::first_type::allocator_type(arena)),
typename M::mapped_type::second_type(typename M::mapped_type::second_type::allocator_type(arena))
))).first->second;
}
// Each multidraw batch has a list of index counts, and a list of pointers-to-first-indexes
typedef std::pair >, std::vector > > BatchElements;
// Group batches by index buffer
typedef POOLED_BATCH_MAP(CVertexBuffer*, BatchElements) IndexBufferBatches;
// Group batches by vertex buffer
typedef POOLED_BATCH_MAP(CVertexBuffer*, IndexBufferBatches) VertexBufferBatches;
// Group batches by texture
typedef POOLED_BATCH_MAP(CTerrainTextureEntry*, VertexBufferBatches) TextureBatches;
void CPatchRData::RenderBases(const std::vector& patches, const CShaderDefines& context,
ShadowMap* shadow, bool isDummyShader, const CShaderProgramPtr& dummy)
{
Allocators::DynamicArena arena(1 * MiB);
TextureBatches batches (TextureBatches::key_compare(), (TextureBatches::allocator_type(arena)));
PROFILE_START("compute batches");
// Collect all the patches' base splats into their appropriate batches
for (size_t i = 0; i < patches.size(); ++i)
{
CPatchRData* patch = patches[i];
for (size_t j = 0; j < patch->m_Splats.size(); ++j)
{
SSplat& splat = patch->m_Splats[j];
BatchElements& batch = PooledPairGet(
PooledMapGet(
PooledMapGet(batches, splat.m_Texture, arena),
patch->m_VBBase->m_Owner, arena
),
patch->m_VBBaseIndices->m_Owner, arena
);
batch.first.push_back(splat.m_IndexCount);
u8* indexBase = patch->m_VBBaseIndices->m_Owner->GetBindAddress();
batch.second.push_back(indexBase + sizeof(u16)*(patch->m_VBBaseIndices->m_Index + splat.m_IndexStart));
}
}
PROFILE_END("compute batches");
// Render each batch
for (TextureBatches::iterator itt = batches.begin(); itt != batches.end(); ++itt)
{
int numPasses = 1;
CShaderTechniquePtr techBase;
if (!isDummyShader)
{
if (itt->first->GetMaterial().GetShaderEffect().length() == 0)
{
LOGERROR("Terrain renderer failed to load shader effect.\n");
continue;
}
techBase = g_Renderer.GetShaderManager().LoadEffect(itt->first->GetMaterial().GetShaderEffect(),
context, itt->first->GetMaterial().GetShaderDefines(0));
numPasses = techBase->GetNumPasses();
}
for (int pass = 0; pass < numPasses; ++pass)
{
if (!isDummyShader)
{
techBase->BeginPass(pass);
TerrainRenderer::PrepareShader(techBase->GetShader(), shadow);
}
const CShaderProgramPtr& shader = isDummyShader ? dummy : techBase->GetShader(pass);
if (itt->first->GetMaterial().GetSamplers().size() != 0)
{
const CMaterial::SamplersVector& samplers = itt->first->GetMaterial().GetSamplers();
size_t samplersNum = samplers.size();
for (size_t s = 0; s < samplersNum; ++s)
{
const CMaterial::TextureSampler& samp = samplers[s];
shader->BindTexture(samp.Name, samp.Sampler);
}
itt->first->GetMaterial().GetStaticUniforms().BindUniforms(shader);
#if !CONFIG2_GLES
if (isDummyShader)
{
glMatrixMode(GL_TEXTURE);
glLoadMatrixf(itt->first->GetTextureMatrix());
glMatrixMode(GL_MODELVIEW);
}
else
#endif
{
float c = itt->first->GetTextureMatrix()[0];
float ms = itt->first->GetTextureMatrix()[8];
shader->Uniform(str_textureTransform, c, ms, -ms, 0.f);
}
}
else
{
shader->BindTexture(str_baseTex, g_Renderer.GetTextureManager().GetErrorTexture());
}
for (VertexBufferBatches::iterator itv = itt->second.begin(); itv != itt->second.end(); ++itv)
{
GLsizei stride = sizeof(SBaseVertex);
SBaseVertex *base = (SBaseVertex *)itv->first->Bind();
shader->VertexPointer(3, GL_FLOAT, stride, &base->m_Position[0]);
- shader->ColorPointer(4, GL_UNSIGNED_BYTE, stride, &base->m_DiffuseColor);
shader->NormalPointer(GL_FLOAT, stride, &base->m_Normal[0]);
shader->TexCoordPointer(GL_TEXTURE0, 3, GL_FLOAT, stride, &base->m_Position[0]);
shader->AssertPointersBound();
for (IndexBufferBatches::iterator it = itv->second.begin(); it != itv->second.end(); ++it)
{
it->first->Bind();
BatchElements& batch = it->second;
if (!g_Renderer.m_SkipSubmit)
{
// Don't use glMultiDrawElements here since it doesn't have a significant
// performance impact and it suffers from various driver bugs (e.g. it breaks
// in Mesa 7.10 swrast with index VBOs)
for (size_t i = 0; i < batch.first.size(); ++i)
glDrawElements(GL_TRIANGLES, batch.first[i], GL_UNSIGNED_SHORT, batch.second[i]);
}
g_Renderer.m_Stats.m_DrawCalls++;
g_Renderer.m_Stats.m_TerrainTris += std::accumulate(batch.first.begin(), batch.first.end(), 0) / 3;
}
}
if (!isDummyShader)
techBase->EndPass();
}
}
#if !CONFIG2_GLES
if (isDummyShader)
{
glMatrixMode(GL_TEXTURE);
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
}
#endif
CVertexBuffer::Unbind();
}
/**
* Helper structure for RenderBlends.
*/
struct SBlendBatch
{
SBlendBatch(Allocators::DynamicArena& arena) :
m_Batches(VertexBufferBatches::key_compare(), VertexBufferBatches::allocator_type(arena))
{
}
CTerrainTextureEntry* m_Texture;
VertexBufferBatches m_Batches;
};
/**
* Helper structure for RenderBlends.
*/
struct SBlendStackItem
{
SBlendStackItem(CVertexBuffer::VBChunk* v, CVertexBuffer::VBChunk* i,
const std::vector& s, Allocators::DynamicArena& arena) :
vertices(v), indices(i), splats(s.begin(), s.end(), SplatStack::allocator_type(arena))
{
}
typedef std::vector > SplatStack;
CVertexBuffer::VBChunk* vertices;
CVertexBuffer::VBChunk* indices;
SplatStack splats;
};
void CPatchRData::RenderBlends(const std::vector& patches, const CShaderDefines& context,
ShadowMap* shadow, bool isDummyShader, const CShaderProgramPtr& dummy)
{
Allocators::DynamicArena arena(1 * MiB);
typedef std::vector > BatchesStack;
BatchesStack batches((BatchesStack::allocator_type(arena)));
CShaderDefines contextBlend = context;
contextBlend.Add(str_BLEND, str_1);
PROFILE_START("compute batches");
// Reserve an arbitrary size that's probably big enough in most cases,
// to avoid heavy reallocations
batches.reserve(256);
typedef std::vector > BlendStacks;
BlendStacks blendStacks((BlendStacks::allocator_type(arena)));
blendStacks.reserve(patches.size());
// Extract all the blend splats from each patch
for (size_t i = 0; i < patches.size(); ++i)
{
CPatchRData* patch = patches[i];
if (!patch->m_BlendSplats.empty())
{
blendStacks.push_back(SBlendStackItem(patch->m_VBBlends, patch->m_VBBlendIndices, patch->m_BlendSplats, arena));
// Reverse the splats so the first to be rendered is at the back of the list
std::reverse(blendStacks.back().splats.begin(), blendStacks.back().splats.end());
}
}
// Rearrange the collection of splats to be grouped by texture, preserving
// order of splats within each patch:
// (This is exactly the same algorithm used in CPatchRData::BuildBlends,
// but applied to patch-sized splats rather than to tile-sized splats;
// see that function for comments on the algorithm.)
while (true)
{
if (!batches.empty())
{
CTerrainTextureEntry* tex = batches.back().m_Texture;
for (size_t k = 0; k < blendStacks.size(); ++k)
{
SBlendStackItem::SplatStack& splats = blendStacks[k].splats;
if (!splats.empty() && splats.back().m_Texture == tex)
{
CVertexBuffer::VBChunk* vertices = blendStacks[k].vertices;
CVertexBuffer::VBChunk* indices = blendStacks[k].indices;
BatchElements& batch = PooledPairGet(PooledMapGet(batches.back().m_Batches, vertices->m_Owner, arena), indices->m_Owner, arena);
batch.first.push_back(splats.back().m_IndexCount);
u8* indexBase = indices->m_Owner->GetBindAddress();
batch.second.push_back(indexBase + sizeof(u16)*(indices->m_Index + splats.back().m_IndexStart));
splats.pop_back();
}
}
}
CTerrainTextureEntry* bestTex = NULL;
size_t bestStackSize = 0;
for (size_t k = 0; k < blendStacks.size(); ++k)
{
SBlendStackItem::SplatStack& splats = blendStacks[k].splats;
if (splats.size() > bestStackSize)
{
bestStackSize = splats.size();
bestTex = splats.back().m_Texture;
}
}
if (bestStackSize == 0)
break;
SBlendBatch layer(arena);
layer.m_Texture = bestTex;
batches.push_back(layer);
}
PROFILE_END("compute batches");
CVertexBuffer* lastVB = NULL;
for (BatchesStack::iterator itt = batches.begin(); itt != batches.end(); ++itt)
{
if (itt->m_Texture->GetMaterial().GetSamplers().size() == 0)
continue;
int numPasses = 1;
CShaderTechniquePtr techBase;
if (!isDummyShader)
{
techBase = g_Renderer.GetShaderManager().LoadEffect(itt->m_Texture->GetMaterial().GetShaderEffect(), contextBlend, itt->m_Texture->GetMaterial().GetShaderDefines(0));
numPasses = techBase->GetNumPasses();
}
CShaderProgramPtr previousShader;
for (int pass = 0; pass < numPasses; ++pass)
{
if (!isDummyShader)
{
techBase->BeginPass(pass);
TerrainRenderer::PrepareShader(techBase->GetShader(), shadow);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}
const CShaderProgramPtr& shader = isDummyShader ? dummy : techBase->GetShader(pass);
if (itt->m_Texture)
{
const CMaterial::SamplersVector& samplers = itt->m_Texture->GetMaterial().GetSamplers();
size_t samplersNum = samplers.size();
for (size_t s = 0; s < samplersNum; ++s)
{
const CMaterial::TextureSampler& samp = samplers[s];
shader->BindTexture(samp.Name, samp.Sampler);
}
shader->BindTexture(str_blendTex, itt->m_Texture->m_TerrainAlpha->second.m_hCompositeAlphaMap);
itt->m_Texture->GetMaterial().GetStaticUniforms().BindUniforms(shader);
#if !CONFIG2_GLES
if (isDummyShader)
{
pglClientActiveTextureARB(GL_TEXTURE0);
glMatrixMode(GL_TEXTURE);
glLoadMatrixf(itt->m_Texture->GetTextureMatrix());
glMatrixMode(GL_MODELVIEW);
}
else
#endif
{
float c = itt->m_Texture->GetTextureMatrix()[0];
float ms = itt->m_Texture->GetTextureMatrix()[8];
shader->Uniform(str_textureTransform, c, ms, -ms, 0.f);
}
}
else
{
shader->BindTexture(str_baseTex, g_Renderer.GetTextureManager().GetErrorTexture());
}
for (VertexBufferBatches::iterator itv = itt->m_Batches.begin(); itv != itt->m_Batches.end(); ++itv)
{
// Rebind the VB only if it changed since the last batch
if (itv->first != lastVB || shader != previousShader)
{
lastVB = itv->first;
previousShader = shader;
GLsizei stride = sizeof(SBlendVertex);
SBlendVertex *base = (SBlendVertex *)itv->first->Bind();
shader->VertexPointer(3, GL_FLOAT, stride, &base->m_Position[0]);
- shader->ColorPointer(4, GL_UNSIGNED_BYTE, stride, &base->m_DiffuseColor);
shader->NormalPointer(GL_FLOAT, stride, &base->m_Normal[0]);
shader->TexCoordPointer(GL_TEXTURE0, 3, GL_FLOAT, stride, &base->m_Position[0]);
shader->TexCoordPointer(GL_TEXTURE1, 2, GL_FLOAT, stride, &base->m_AlphaUVs[0]);
}
shader->AssertPointersBound();
for (IndexBufferBatches::iterator it = itv->second.begin(); it != itv->second.end(); ++it)
{
it->first->Bind();
BatchElements& batch = it->second;
if (!g_Renderer.m_SkipSubmit)
{
for (size_t i = 0; i < batch.first.size(); ++i)
glDrawElements(GL_TRIANGLES, batch.first[i], GL_UNSIGNED_SHORT, batch.second[i]);
}
g_Renderer.m_Stats.m_DrawCalls++;
g_Renderer.m_Stats.m_BlendSplats++;
g_Renderer.m_Stats.m_TerrainTris += std::accumulate(batch.first.begin(), batch.first.end(), 0) / 3;
}
}
if (!isDummyShader)
{
glDisable(GL_BLEND);
techBase->EndPass();
}
}
}
#if !CONFIG2_GLES
if (isDummyShader)
{
pglClientActiveTextureARB(GL_TEXTURE0);
glMatrixMode(GL_TEXTURE);
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
}
#endif
CVertexBuffer::Unbind();
}
void CPatchRData::RenderStreams(const std::vector& patches, const CShaderProgramPtr& shader, int streamflags)
{
// Each batch has a list of index counts, and a list of pointers-to-first-indexes
typedef std::pair, std::vector > BatchElements;
// Group batches by index buffer
typedef std::map IndexBufferBatches;
// Group batches by vertex buffer
typedef std::map VertexBufferBatches;
VertexBufferBatches batches;
PROFILE_START("compute batches");
// Collect all the patches into their appropriate batches
for (size_t i = 0; i < patches.size(); ++i)
{
CPatchRData* patch = patches[i];
BatchElements& batch = batches[patch->m_VBBase->m_Owner][patch->m_VBBaseIndices->m_Owner];
batch.first.push_back(patch->m_VBBaseIndices->m_Count);
u8* indexBase = patch->m_VBBaseIndices->m_Owner->GetBindAddress();
batch.second.push_back(indexBase + sizeof(u16)*(patch->m_VBBaseIndices->m_Index));
}
PROFILE_END("compute batches");
- ENSURE(!(streamflags & ~(STREAM_POS|STREAM_COLOR|STREAM_POSTOUV0|STREAM_POSTOUV1)));
+ ENSURE(!(streamflags & ~(STREAM_POS|STREAM_POSTOUV0|STREAM_POSTOUV1)));
// Render each batch
for (VertexBufferBatches::iterator itv = batches.begin(); itv != batches.end(); ++itv)
{
GLsizei stride = sizeof(SBaseVertex);
SBaseVertex *base = (SBaseVertex *)itv->first->Bind();
shader->VertexPointer(3, GL_FLOAT, stride, &base->m_Position);
if (streamflags & STREAM_POSTOUV0)
shader->TexCoordPointer(GL_TEXTURE0, 3, GL_FLOAT, stride, &base->m_Position);
if (streamflags & STREAM_POSTOUV1)
shader->TexCoordPointer(GL_TEXTURE1, 3, GL_FLOAT, stride, &base->m_Position);
- if (streamflags & STREAM_COLOR)
- shader->ColorPointer(4, GL_UNSIGNED_BYTE, stride, &base->m_DiffuseColor);
shader->AssertPointersBound();
for (IndexBufferBatches::iterator it = itv->second.begin(); it != itv->second.end(); ++it)
{
it->first->Bind();
BatchElements& batch = it->second;
if (!g_Renderer.m_SkipSubmit)
{
for (size_t i = 0; i < batch.first.size(); ++i)
glDrawElements(GL_TRIANGLES, batch.first[i], GL_UNSIGNED_SHORT, batch.second[i]);
}
g_Renderer.m_Stats.m_DrawCalls++;
g_Renderer.m_Stats.m_TerrainTris += std::accumulate(batch.first.begin(), batch.first.end(), 0) / 3;
}
}
CVertexBuffer::Unbind();
}
void CPatchRData::RenderOutline()
{
CTerrain* terrain = m_Patch->m_Parent;
ssize_t gx = m_Patch->m_X * PATCH_SIZE;
ssize_t gz = m_Patch->m_Z * PATCH_SIZE;
CVector3D pos;
std::vector line;
ssize_t i, j;
for (i = 0, j = 0; i <= PATCH_SIZE; ++i)
{
terrain->CalcPosition(gx + i, gz + j, pos);
line.push_back(pos);
}
for (i = PATCH_SIZE, j = 1; j <= PATCH_SIZE; ++j)
{
terrain->CalcPosition(gx + i, gz + j, pos);
line.push_back(pos);
}
for (i = PATCH_SIZE-1, j = PATCH_SIZE; i >= 0; --i)
{
terrain->CalcPosition(gx + i, gz + j, pos);
line.push_back(pos);
}
for (i = 0, j = PATCH_SIZE-1; j >= 0; --j)
{
terrain->CalcPosition(gx + i, gz + j, pos);
line.push_back(pos);
}
#if CONFIG2_GLES
#warning TODO: implement CPatchRData::RenderOutlines for GLES
#else
glVertexPointer(3, GL_FLOAT, sizeof(CVector3D), &line[0]);
glDrawArrays(GL_LINE_STRIP, 0, line.size());
#endif
}
void CPatchRData::RenderSides(CShaderProgramPtr& shader)
{
ENSURE(m_UpdateFlags==0);
if (!m_VBSides)
return;
glDisable(GL_CULL_FACE);
SSideVertex *base = (SSideVertex *)m_VBSides->m_Owner->Bind();
// setup data pointers
GLsizei stride = sizeof(SSideVertex);
shader->VertexPointer(3, GL_FLOAT, stride, &base->m_Position);
shader->AssertPointersBound();
if (!g_Renderer.m_SkipSubmit)
glDrawArrays(GL_TRIANGLE_STRIP, m_VBSides->m_Index, (GLsizei)m_VBSides->m_Count);
// bump stats
g_Renderer.m_Stats.m_DrawCalls++;
g_Renderer.m_Stats.m_TerrainTris += m_VBSides->m_Count - 2;
CVertexBuffer::Unbind();
glEnable(GL_CULL_FACE);
}
void CPatchRData::RenderPriorities(CTextRenderer& textRenderer)
{
CTerrain* terrain = m_Patch->m_Parent;
const CCamera& camera = *(g_Game->GetView()->GetCamera());
for (ssize_t j = 0; j < PATCH_SIZE; ++j)
{
for (ssize_t i = 0; i < PATCH_SIZE; ++i)
{
ssize_t gx = m_Patch->m_X * PATCH_SIZE + i;
ssize_t gz = m_Patch->m_Z * PATCH_SIZE + j;
CVector3D pos;
terrain->CalcPosition(gx, gz, pos);
// Move a bit towards the center of the tile
pos.X += TERRAIN_TILE_SIZE/4.f;
pos.Z += TERRAIN_TILE_SIZE/4.f;
float x, y;
camera.GetScreenCoordinates(pos, x, y);
textRenderer.PrintfAt(x, y, L"%d", m_Patch->m_MiniPatches[j][i].Priority);
}
}
}
//
// Water build and rendering
//
// Build vertex buffer for water vertices over our patch
void CPatchRData::BuildWater()
{
PROFILE3("build water");
// Number of vertices in each direction in each patch
ENSURE(PATCH_SIZE % water_cell_size == 0);
if (m_VBWater)
{
g_VBMan.Release(m_VBWater);
m_VBWater = nullptr;
}
if (m_VBWaterIndices)
{
g_VBMan.Release(m_VBWaterIndices);
m_VBWaterIndices = nullptr;
}
if (m_VBWaterShore)
{
g_VBMan.Release(m_VBWaterShore);
m_VBWaterShore = nullptr;
}
if (m_VBWaterIndicesShore)
{
g_VBMan.Release(m_VBWaterIndicesShore);
m_VBWaterIndicesShore = nullptr;
}
m_WaterBounds.SetEmpty();
// We need to use this to access the water manager or we may not have the
// actual values but some compiled-in defaults
CmpPtr cmpWaterManager(*m_Simulation, SYSTEM_ENTITY);
if (!cmpWaterManager)
return;
// Build data for water
std::vector water_vertex_data;
std::vector water_indices;
u16 water_index_map[PATCH_SIZE+1][PATCH_SIZE+1];
memset(water_index_map, 0xFF, sizeof(water_index_map));
// Build data for shore
std::vector water_vertex_data_shore;
std::vector water_indices_shore;
u16 water_shore_index_map[PATCH_SIZE+1][PATCH_SIZE+1];
memset(water_shore_index_map, 0xFF, sizeof(water_shore_index_map));
WaterManager* WaterMgr = g_Renderer.GetWaterManager();
CPatch* patch = m_Patch;
CTerrain* terrain = patch->m_Parent;
ssize_t mapSize = terrain->GetVerticesPerSide();
// Top-left coordinates of our patch.
ssize_t px = m_Patch->m_X * PATCH_SIZE;
ssize_t pz = m_Patch->m_Z * PATCH_SIZE;
// To whoever implements different water heights, this is a TODO: water height)
float waterHeight = cmpWaterManager->GetExactWaterLevel(0.0f,0.0f);
// The 4 points making a water tile.
int moves[4][2] = {
{0, 0},
{water_cell_size, 0},
{0, water_cell_size},
{water_cell_size, water_cell_size}
};
// Where to look for when checking for water for shore tiles.
int check[10][2] = {
{0, 0},
{water_cell_size, 0},
{water_cell_size*2, 0},
{0, water_cell_size},
{0, water_cell_size*2},
{water_cell_size, water_cell_size},
{water_cell_size*2, water_cell_size*2},
{-water_cell_size, 0},
{0, -water_cell_size},
{-water_cell_size, -water_cell_size}
};
// build vertices, uv, and shader varying
for (ssize_t z = 0; z < PATCH_SIZE; z += water_cell_size)
{
for (ssize_t x = 0; x < PATCH_SIZE; x += water_cell_size)
{
// Check that this tile is close to water
bool nearWater = false;
for (size_t test = 0; test < 10; ++test)
if (terrain->GetVertexGroundLevel(x + px + check[test][0], z + pz + check[test][1]) < waterHeight)
nearWater = true;
if (!nearWater)
continue;
// This is actually lying and I should call CcmpTerrain
/*if (!terrain->IsOnMap(x+x1, z+z1)
&& !terrain->IsOnMap(x+x1, z+z1 + water_cell_size)
&& !terrain->IsOnMap(x+x1 + water_cell_size, z+z1)
&& !terrain->IsOnMap(x+x1 + water_cell_size, z+z1 + water_cell_size))
continue;*/
for (int i = 0; i < 4; ++i)
{
if (water_index_map[z+moves[i][1]][x+moves[i][0]] != 0xFFFF)
continue;
ssize_t xx = x + px + moves[i][0];
ssize_t zz = z + pz + moves[i][1];
SWaterVertex vertex;
terrain->CalcPosition(xx,zz, vertex.m_Position);
float depth = waterHeight - vertex.m_Position.Y;
vertex.m_Position.Y = waterHeight;
m_WaterBounds += vertex.m_Position;
vertex.m_WaterData = CVector2D(WaterMgr->m_WindStrength[xx + zz*mapSize], depth);
water_index_map[z+moves[i][1]][x+moves[i][0]] = water_vertex_data.size();
water_vertex_data.push_back(vertex);
}
water_indices.push_back(water_index_map[z + moves[2][1]][x + moves[2][0]]);
water_indices.push_back(water_index_map[z + moves[0][1]][x + moves[0][0]]);
water_indices.push_back(water_index_map[z + moves[1][1]][x + moves[1][0]]);
water_indices.push_back(water_index_map[z + moves[1][1]][x + moves[1][0]]);
water_indices.push_back(water_index_map[z + moves[3][1]][x + moves[3][0]]);
water_indices.push_back(water_index_map[z + moves[2][1]][x + moves[2][0]]);
// Check id this tile is partly over land.
// If so add a square over the terrain. This is necessary to render waves that go on shore.
if (terrain->GetVertexGroundLevel(x+px, z+pz) < waterHeight &&
terrain->GetVertexGroundLevel(x+px + water_cell_size, z+pz) < waterHeight &&
terrain->GetVertexGroundLevel(x+px, z+pz+water_cell_size) < waterHeight &&
terrain->GetVertexGroundLevel(x+px + water_cell_size, z+pz+water_cell_size) < waterHeight)
continue;
for (int i = 0; i < 4; ++i)
{
if (water_shore_index_map[z+moves[i][1]][x+moves[i][0]] != 0xFFFF)
continue;
ssize_t xx = x + px + moves[i][0];
ssize_t zz = z + pz + moves[i][1];
SWaterVertex vertex;
terrain->CalcPosition(xx,zz, vertex.m_Position);
vertex.m_Position.Y += 0.02f;
m_WaterBounds += vertex.m_Position;
vertex.m_WaterData = CVector2D(0.0f, -5.0f);
water_shore_index_map[z+moves[i][1]][x+moves[i][0]] = water_vertex_data_shore.size();
water_vertex_data_shore.push_back(vertex);
}
if (terrain->GetTriangulationDir(x + px, z + pz))
{
water_indices_shore.push_back(water_shore_index_map[z + moves[2][1]][x + moves[2][0]]);
water_indices_shore.push_back(water_shore_index_map[z + moves[0][1]][x + moves[0][0]]);
water_indices_shore.push_back(water_shore_index_map[z + moves[1][1]][x + moves[1][0]]);
water_indices_shore.push_back(water_shore_index_map[z + moves[1][1]][x + moves[1][0]]);
water_indices_shore.push_back(water_shore_index_map[z + moves[3][1]][x + moves[3][0]]);
water_indices_shore.push_back(water_shore_index_map[z + moves[2][1]][x + moves[2][0]]);
}
else
{
water_indices_shore.push_back(water_shore_index_map[z + moves[3][1]][x + moves[3][0]]);
water_indices_shore.push_back(water_shore_index_map[z + moves[2][1]][x + moves[2][0]]);
water_indices_shore.push_back(water_shore_index_map[z + moves[0][1]][x + moves[0][0]]);
water_indices_shore.push_back(water_shore_index_map[z + moves[3][1]][x + moves[3][0]]);
water_indices_shore.push_back(water_shore_index_map[z + moves[0][1]][x + moves[0][0]]);
water_indices_shore.push_back(water_shore_index_map[z + moves[1][1]][x + moves[1][0]]);
}
}
}
// No vertex buffers if no data generated
if (!water_indices.empty())
{
m_VBWater = g_VBMan.Allocate(sizeof(SWaterVertex), water_vertex_data.size(), GL_STATIC_DRAW, GL_ARRAY_BUFFER);
m_VBWater->m_Owner->UpdateChunkVertices(m_VBWater, &water_vertex_data[0]);
m_VBWaterIndices = g_VBMan.Allocate(sizeof(GLushort), water_indices.size(), GL_STATIC_DRAW, GL_ELEMENT_ARRAY_BUFFER);
m_VBWaterIndices->m_Owner->UpdateChunkVertices(m_VBWaterIndices, &water_indices[0]);
}
if (!water_indices_shore.empty())
{
m_VBWaterShore = g_VBMan.Allocate(sizeof(SWaterVertex), water_vertex_data_shore.size(), GL_STATIC_DRAW, GL_ARRAY_BUFFER);
m_VBWaterShore->m_Owner->UpdateChunkVertices(m_VBWaterShore, &water_vertex_data_shore[0]);
// Construct indices buffer
m_VBWaterIndicesShore = g_VBMan.Allocate(sizeof(GLushort), water_indices_shore.size(), GL_STATIC_DRAW, GL_ELEMENT_ARRAY_BUFFER);
m_VBWaterIndicesShore->m_Owner->UpdateChunkVertices(m_VBWaterIndicesShore, &water_indices_shore[0]);
}
}
void CPatchRData::RenderWater(CShaderProgramPtr& shader, bool onlyShore, bool fixedPipeline)
{
ASSERT(m_UpdateFlags==0);
if (g_Renderer.m_SkipSubmit || (!m_VBWater && !m_VBWaterShore))
return;
#if !CONFIG2_GLES
if (g_Renderer.m_WaterRenderMode == WIREFRAME)
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
#endif
if (m_VBWater != 0x0 && !onlyShore)
{
SWaterVertex *base=(SWaterVertex *)m_VBWater->m_Owner->Bind();
// setup data pointers
GLsizei stride = sizeof(SWaterVertex);
shader->VertexPointer(3, GL_FLOAT, stride, &base[m_VBWater->m_Index].m_Position);
if (!fixedPipeline)
shader->VertexAttribPointer(str_a_waterInfo, 2, GL_FLOAT, false, stride, &base[m_VBWater->m_Index].m_WaterData);
shader->AssertPointersBound();
u8* indexBase = m_VBWaterIndices->m_Owner->Bind();
glDrawElements(GL_TRIANGLES, (GLsizei) m_VBWaterIndices->m_Count,
GL_UNSIGNED_SHORT, indexBase + sizeof(u16)*(m_VBWaterIndices->m_Index));
g_Renderer.m_Stats.m_DrawCalls++;
g_Renderer.m_Stats.m_WaterTris += m_VBWaterIndices->m_Count / 3;
}
if (m_VBWaterShore != 0x0 &&
g_Renderer.GetWaterManager()->m_WaterEffects &&
g_Renderer.GetWaterManager()->m_WaterFancyEffects)
{
SWaterVertex *base=(SWaterVertex *)m_VBWaterShore->m_Owner->Bind();
GLsizei stride = sizeof(SWaterVertex);
shader->VertexPointer(3, GL_FLOAT, stride, &base[m_VBWaterShore->m_Index].m_Position);
if (!fixedPipeline)
shader->VertexAttribPointer(str_a_waterInfo, 2, GL_FLOAT, false, stride, &base[m_VBWaterShore->m_Index].m_WaterData);
shader->AssertPointersBound();
u8* indexBase = m_VBWaterIndicesShore->m_Owner->Bind();
glDrawElements(GL_TRIANGLES, (GLsizei) m_VBWaterIndicesShore->m_Count,
GL_UNSIGNED_SHORT, indexBase + sizeof(u16)*(m_VBWaterIndicesShore->m_Index));
g_Renderer.m_Stats.m_DrawCalls++;
g_Renderer.m_Stats.m_WaterTris += m_VBWaterIndicesShore->m_Count / 3;
}
CVertexBuffer::Unbind();
#if !CONFIG2_GLES
if (g_Renderer.m_WaterRenderMode == WIREFRAME)
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
#endif
}
Index: ps/trunk/source/renderer/PatchRData.h
===================================================================
--- ps/trunk/source/renderer/PatchRData.h (revision 24123)
+++ ps/trunk/source/renderer/PatchRData.h (revision 24124)
@@ -1,182 +1,176 @@
-/* Copyright (C) 2019 Wildfire Games.
+/* Copyright (C) 2020 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see .
*/
#ifndef INCLUDED_PATCHRDATA
#define INCLUDED_PATCHRDATA
#include "graphics/SColor.h"
#include "maths/Vector2D.h"
#include "maths/Vector3D.h"
#include "graphics/Patch.h"
#include "graphics/RenderableObject.h"
#include "graphics/ShaderProgramPtr.h"
#include "renderer/VertexBufferManager.h"
#include
class CPatch;
class CShaderDefines;
class CSimulation2;
class CTerrainTextureEntry;
class CTextRenderer;
class ShadowMap;
//////////////////////////////////////////////////////////////////////////////////////////////////
// CPatchRData: class encapsulating logic for rendering terrain patches; holds per
// patch data, plus some supporting static functions for batching, etc
class CPatchRData : public CRenderData
{
public:
CPatchRData(CPatch* patch, CSimulation2* simulation);
~CPatchRData();
void Update(CSimulation2* simulation);
void RenderOutline();
void RenderSides(CShaderProgramPtr& shader);
void RenderPriorities(CTextRenderer& textRenderer);
void RenderWater(CShaderProgramPtr& shader, bool onlyShore = false, bool fixedPipeline = false);
static void RenderBases(const std::vector& patches, const CShaderDefines& context,
ShadowMap* shadow, bool isDummyShader=false, const CShaderProgramPtr& dummy=CShaderProgramPtr());
static void RenderBlends(const std::vector& patches, const CShaderDefines& context,
ShadowMap* shadow, bool isDummyShader=false, const CShaderProgramPtr& dummy=CShaderProgramPtr());
static void RenderStreams(const std::vector& patches, const CShaderProgramPtr& shader, int streamflags);
CPatch* GetPatch() { return m_Patch; }
static void PrepareShader(const CShaderProgramPtr& shader, ShadowMap* shadow);
const CBoundingBoxAligned& GetWaterBounds() const { return m_WaterBounds; }
private:
friend struct SBlendStackItem;
struct SSplat {
SSplat() : m_Texture(0), m_IndexCount(0) {}
// texture to apply during splat
CTerrainTextureEntry* m_Texture;
// offset into the index array for this patch where splat starts
size_t m_IndexStart;
// number of indices used by splat
size_t m_IndexCount;
};
struct SBaseVertex {
// vertex position
CVector3D m_Position;
- // diffuse color from sunlight
- SColor4ub m_DiffuseColor;
CVector3D m_Normal;
// pad to a power of two
- u8 m_Padding[4];
+ u8 m_Padding[8];
};
cassert(sizeof(SBaseVertex) == 32);
struct SSideVertex {
// vertex position
CVector3D m_Position;
// pad to a power of two
u8 m_Padding[4];
};
cassert(sizeof(SSideVertex) == 16);
struct SBlendVertex {
// vertex position
CVector3D m_Position;
- // diffuse color from sunlight
- SColor4ub m_DiffuseColor;
// vertex uvs for alpha texture
float m_AlphaUVs[2];
CVector3D m_Normal;
- // pad to a power of two
- u8 m_Padding[28];
};
- cassert(sizeof(SBlendVertex) == 64);
+ cassert(sizeof(SBlendVertex) == 32);
// Mixed Fancy/Simple water vertex description data structure
struct SWaterVertex {
// vertex position
CVector3D m_Position;
CVector2D m_WaterData;
// pad to a power of two
u8 m_Padding[12];
};
cassert(sizeof(SWaterVertex) == 32);
// build this renderdata object
void Build();
void AddBlend(std::vector& blendVertices, std::vector& blendIndices,
u16 i, u16 j, u8 shape, CTerrainTextureEntry* texture);
void BuildBlends();
void BuildIndices();
void BuildVertices();
void BuildSides();
void BuildSide(std::vector& vertices, CPatchSideFlags side);
// owner patch
CPatch* m_Patch;
// vertex buffer handle for side vertices
CVertexBuffer::VBChunk* m_VBSides;
// vertex buffer handle for base vertices
CVertexBuffer::VBChunk* m_VBBase;
// vertex buffer handle for base vertex indices
CVertexBuffer::VBChunk* m_VBBaseIndices;
// vertex buffer handle for blend vertices
CVertexBuffer::VBChunk* m_VBBlends;
// vertex buffer handle for blend vertex indices
CVertexBuffer::VBChunk* m_VBBlendIndices;
// list of base splats to apply to this patch
std::vector m_Splats;
// splats used in blend pass
std::vector m_BlendSplats;
// boundary of water in this patch
CBoundingBoxAligned m_WaterBounds;
// Water vertex buffer
CVertexBuffer::VBChunk* m_VBWater;
CVertexBuffer::VBChunk* m_VBWaterShore;
// Water indices buffer
CVertexBuffer::VBChunk* m_VBWaterIndices;
CVertexBuffer::VBChunk* m_VBWaterIndicesShore;
CSimulation2* m_Simulation;
// Build water vertices and indices (vertex buffer and data vector)
void BuildWater();
// parameter allowing a varying number of triangles per patch for LOD
// MUST be an exact divisor of PATCH_SIZE
// compiled const for the moment until/if dynamic water LOD is offered
// savings would be mostly beneficial for GPU or simple water
static const ssize_t water_cell_size = 1;
};
#endif // INCLUDED_PATCHRDATA
Index: ps/trunk/source/renderer/TerrainRenderer.cpp
===================================================================
--- ps/trunk/source/renderer/TerrainRenderer.cpp (revision 24123)
+++ ps/trunk/source/renderer/TerrainRenderer.cpp (revision 24124)
@@ -1,991 +1,991 @@
/* Copyright (C) 2020 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see .
*/
/*
* Terrain rendering (everything related to patches and water) is
* encapsulated in TerrainRenderer
*/
#include "precompiled.h"
#include "graphics/Camera.h"
#include "graphics/Decal.h"
#include "graphics/LightEnv.h"
#include "graphics/LOSTexture.h"
#include "graphics/Patch.h"
#include "graphics/GameView.h"
#include "graphics/Model.h"
#include "graphics/ShaderManager.h"
#include "renderer/ShadowMap.h"
#include "renderer/SkyManager.h"
#include "graphics/TerritoryTexture.h"
#include "graphics/TextRenderer.h"
#include "maths/MathUtil.h"
#include "ps/Filesystem.h"
#include "ps/CLogger.h"
#include "ps/Game.h"
#include "ps/Profile.h"
#include "ps/World.h"
#include "renderer/DecalRData.h"
#include "renderer/PatchRData.h"
#include "renderer/Renderer.h"
#include "renderer/RenderingOptions.h"
#include "renderer/ShadowMap.h"
#include "renderer/TerrainRenderer.h"
#include "renderer/VertexArray.h"
#include "renderer/WaterManager.h"
#include "tools/atlas/GameInterface/GameLoop.h"
extern GameLoopState* g_AtlasGameLoop;
///////////////////////////////////////////////////////////////////////////////////////////////
// TerrainRenderer implementation
namespace
{
CShaderProgramPtr GetDummyShader()
{
const char* shaderName;
if (g_RenderingOptions.GetRenderPath() == RenderPath::SHADER)
{
if (g_RenderingOptions.GetPreferGLSL())
shaderName = "glsl/dummy";
else
shaderName = "arb/dummy";
}
else
shaderName = "fixed:dummy";
return g_Renderer.GetShaderManager().LoadProgram(shaderName, CShaderDefines());
}
} // anonymous namespace
/**
* TerrainRenderer keeps track of which phase it is in, to detect
* when Submit, PrepareForRendering etc. are called in the wrong order.
*/
enum Phase {
Phase_Submit,
Phase_Render
};
/**
* Struct TerrainRendererInternals: Internal variables used by the TerrainRenderer class.
*/
struct TerrainRendererInternals
{
/// Which phase (submitting or rendering patches) are we in right now?
Phase phase;
/// Patches that were submitted for this frame
std::vector visiblePatches[CRenderer::CULL_MAX];
/// Decals that were submitted for this frame
std::vector visibleDecals[CRenderer::CULL_MAX];
/// Fancy water shader
CShaderProgramPtr fancyWaterShader;
CSimulation2* simulation;
};
///////////////////////////////////////////////////////////////////
// Construction/Destruction
TerrainRenderer::TerrainRenderer()
{
m = new TerrainRendererInternals();
m->phase = Phase_Submit;
}
TerrainRenderer::~TerrainRenderer()
{
delete m;
}
void TerrainRenderer::SetSimulation(CSimulation2* simulation)
{
m->simulation = simulation;
}
///////////////////////////////////////////////////////////////////
// Submit a patch for rendering
void TerrainRenderer::Submit(int cullGroup, CPatch* patch)
{
ENSURE(m->phase == Phase_Submit);
CPatchRData* data = (CPatchRData*)patch->GetRenderData();
if (data == 0)
{
// no renderdata for patch, create it now
data = new CPatchRData(patch, m->simulation);
patch->SetRenderData(data);
}
data->Update(m->simulation);
m->visiblePatches[cullGroup].push_back(data);
}
///////////////////////////////////////////////////////////////////
// Submit a decal for rendering
void TerrainRenderer::Submit(int cullGroup, CModelDecal* decal)
{
ENSURE(m->phase == Phase_Submit);
CDecalRData* data = (CDecalRData*)decal->GetRenderData();
if (data == 0)
{
// no renderdata for decal, create it now
data = new CDecalRData(decal, m->simulation);
decal->SetRenderData(data);
}
data->Update(m->simulation);
m->visibleDecals[cullGroup].push_back(data);
}
///////////////////////////////////////////////////////////////////
// Prepare for rendering
void TerrainRenderer::PrepareForRendering()
{
ENSURE(m->phase == Phase_Submit);
m->phase = Phase_Render;
}
///////////////////////////////////////////////////////////////////
// Clear submissions lists
void TerrainRenderer::EndFrame()
{
ENSURE(m->phase == Phase_Render || m->phase == Phase_Submit);
for (int i = 0; i < CRenderer::CULL_MAX; ++i)
{
m->visiblePatches[i].clear();
m->visibleDecals[i].clear();
}
m->phase = Phase_Submit;
}
///////////////////////////////////////////////////////////////////
// Full-featured terrain rendering with blending and everything
void TerrainRenderer::RenderTerrainFixed(int cullGroup)
{
#if CONFIG2_GLES
UNUSED2(cullGroup);
#else
ENSURE(m->phase == Phase_Render);
std::vector& visiblePatches = m->visiblePatches[cullGroup];
std::vector& visibleDecals = m->visibleDecals[cullGroup];
if (visiblePatches.empty() && visibleDecals.empty())
return;
CShaderProgramPtr dummyShader = GetDummyShader();
dummyShader->Bind();
// 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;
+ int streamflags = STREAM_POS;
pglActiveTextureARB(GL_TEXTURE0);
// We're not going to use a texture here, but we have to have a valid texture
// bound else the texture unit will be disabled.
// We should still have a bound splat texture from some earlier rendering,
// so assume that's still valid to use.
// (TODO: That's a bit of an ugly hack.)
// No shadows: (Ambient + Diffuse) * LOS
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_ADD);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_CONSTANT);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_PREVIOUS);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, terrainAmbientColor);
losTexture.BindTexture(1);
pglClientActiveTextureARB(GL_TEXTURE1);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
streamflags |= STREAM_POSTOUV1;
glMatrixMode(GL_TEXTURE);
glLoadMatrixf(&losTexture.GetTextureMatrix()._11);
glMatrixMode(GL_MODELVIEW);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_ALPHA);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_PREVIOUS);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
pglActiveTextureARB(GL_TEXTURE0);
pglClientActiveTextureARB(GL_TEXTURE0);
PROFILE_START("render terrain streams");
CPatchRData::RenderStreams(visiblePatches, dummyShader, streamflags);
PROFILE_END("render terrain streams");
glMatrixMode(GL_TEXTURE);
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
// restore OpenGL state
g_Renderer.BindTexture(1, 0);
pglClientActiveTextureARB(GL_TEXTURE1);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glMatrixMode(GL_TEXTURE);
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
pglClientActiveTextureARB(GL_TEXTURE0);
pglActiveTextureARB(GL_TEXTURE0);
glDepthMask(1);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_BLEND);
glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
dummyShader->Unbind();
#endif
}
void TerrainRenderer::RenderTerrainOverlayTexture(int cullGroup, CMatrix3D& textureMatrix)
{
#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 = GetDummyShader();
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 = GetDummyShader();
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_RenderingOptions.GetPreferGLSL() && !superFancy)
m->fancyWaterShader = g_Renderer.GetShaderManager().LoadProgram("arb/water_high", defines);
else*/
m->fancyWaterShader = g_Renderer.GetShaderManager().LoadProgram("glsl/water_high", defines);
if (!m->fancyWaterShader)
{
LOGERROR("Failed to load water shader. Falling back to fixed pipeline water.\n");
WaterMgr->m_RenderWater = false;
return false;
}
WaterMgr->m_NeedsReloading = false;
}
CLOSTexture& losTexture = g_Renderer.GetScene().GetLOSTexture();
// creating the real depth texture using the depth buffer.
if (WaterMgr->m_WaterRealDepth)
{
if (WaterMgr->m_depthTT == 0)
{
GLuint depthTex;
glGenTextures(1, (GLuint*)&depthTex);
WaterMgr->m_depthTT = depthTex;
glBindTexture(GL_TEXTURE_2D, WaterMgr->m_depthTT);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, g_Renderer.GetWidth(), g_Renderer.GetHeight(), 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE,NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
}
else
{
glBindTexture(GL_TEXTURE_2D, WaterMgr->m_depthTT);
glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, 0, 0, g_Renderer.GetWidth(), g_Renderer.GetHeight(), 0);
}
glBindTexture(GL_TEXTURE_2D, 0);
}
// Calculating the advanced informations about Foam and all if the quality calls for it.
/*if (WaterMgr->m_NeedInfoUpdate && (WaterMgr->m_WaterFoam || WaterMgr->m_WaterCoastalWaves))
{
WaterMgr->m_NeedInfoUpdate = false;
WaterMgr->CreateSuperfancyInfo();
}*/
double time = WaterMgr->m_WaterTexTimer;
double period = 8;
int curTex = (int)(time*60/period) % 60;
int nexTex = (curTex + 1) % 60;
float repeatPeriod = WaterMgr->m_RepeatPeriod;
// Render normals and foam to a framebuffer if we're in fancy effects
if (WaterMgr->m_WaterFancyEffects)
{
// Save the post-processing framebuffer.
GLint fbo;
glGetIntegerv(GL_FRAMEBUFFER_BINDING_EXT, &fbo);
pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, WaterMgr->m_FancyEffectsFBO);
glDisable(GL_BLEND);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
glDisable(GL_CULL_FACE);
// Overwrite waves that would be behind the ground.
CShaderProgramPtr dummyShader = g_Renderer.GetShaderManager().LoadProgram("glsl/gui_solid", CShaderDefines());
dummyShader->Bind();
dummyShader->Uniform(str_transform, g_Renderer.GetViewCamera().GetViewProjection());
dummyShader->Uniform(str_color, 0.0f, 0.0f, 0.0f, 0.0f);
std::vector& visiblePatches = m->visiblePatches[cullGroup];
for (size_t i = 0; i < visiblePatches.size(); ++i)
{
CPatchRData* data = visiblePatches[i];
data->RenderWater(dummyShader, true, true);
}
dummyShader->Unbind();
glEnable(GL_CULL_FACE);
pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);
}
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
m->fancyWaterShader->Bind();
const CCamera& camera = g_Renderer.GetViewCamera();
m->fancyWaterShader->BindTexture(str_normalMap, WaterMgr->m_NormalMap[curTex]);
m->fancyWaterShader->BindTexture(str_normalMap2, WaterMgr->m_NormalMap[nexTex]);
if (WaterMgr->m_WaterFancyEffects)
{
m->fancyWaterShader->BindTexture(str_waterEffectsTex, WaterMgr->m_FancyTexture);
}
if (WaterMgr->m_WaterRealDepth)
m->fancyWaterShader->BindTexture(str_depthTex, WaterMgr->m_depthTT);
if (WaterMgr->m_WaterRefraction)
m->fancyWaterShader->BindTexture(str_refractionMap, WaterMgr->m_RefractionTexture);
if (WaterMgr->m_WaterReflection)
m->fancyWaterShader->BindTexture(str_reflectionMap, WaterMgr->m_ReflectionTexture);
m->fancyWaterShader->BindTexture(str_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_WaterRefraction || WaterMgr->m_WaterReflection)
{
m->fancyWaterShader->BindTexture(str_skyCube, g_Renderer.GetSkyManager()->GetSkyCube());
// TODO: check that this rotates in the right direction.
CMatrix3D skyBoxRotation;
skyBoxRotation.SetIdentity();
skyBoxRotation.RotateY(M_PI + lightEnv.GetRotation());
m->fancyWaterShader->Uniform(str_skyBoxRot, skyBoxRotation);
if (WaterMgr->m_WaterRefraction)
m->fancyWaterShader->Uniform(str_refractionMatrix, WaterMgr->m_RefractionMatrix);
if (WaterMgr->m_WaterReflection)
m->fancyWaterShader->Uniform(str_reflectionMatrix, WaterMgr->m_ReflectionMatrix);
}
m->fancyWaterShader->Uniform(str_sunDir, lightEnv.GetSunDir());
m->fancyWaterShader->Uniform(str_sunColor, lightEnv.m_SunColor);
m->fancyWaterShader->Uniform(str_color, WaterMgr->m_WaterColor);
m->fancyWaterShader->Uniform(str_tint, WaterMgr->m_WaterTint);
m->fancyWaterShader->Uniform(str_waviness, WaterMgr->m_Waviness);
m->fancyWaterShader->Uniform(str_murkiness, WaterMgr->m_Murkiness);
m->fancyWaterShader->Uniform(str_windAngle, WaterMgr->m_WindAngle);
m->fancyWaterShader->Uniform(str_repeatScale, 1.0f / repeatPeriod);
m->fancyWaterShader->Uniform(str_losMatrix, losTexture.GetTextureMatrix());
m->fancyWaterShader->Uniform(str_cameraPos, camera.GetOrientation().GetTranslation());
if (WaterMgr->m_WaterRealDepth)
{
m->fancyWaterShader->Uniform(str_zNear, camera.GetNearPlane());
m->fancyWaterShader->Uniform(str_zFar, camera.GetFarPlane());
}
m->fancyWaterShader->Uniform(str_fogColor, lightEnv.m_FogColor);
m->fancyWaterShader->Uniform(str_fogParams, lightEnv.m_FogFactor, lightEnv.m_FogMax, 0.f, 0.f);
m->fancyWaterShader->Uniform(str_time, (float)time);
m->fancyWaterShader->Uniform(str_screenSize, (float)g_Renderer.GetWidth(), (float)g_Renderer.GetHeight(), 0.0f, 0.0f);
if (WaterMgr->m_WaterType == L"clap")
{
m->fancyWaterShader->Uniform(str_waveParams1, 30.0f,1.5f,20.0f,0.03f);
m->fancyWaterShader->Uniform(str_waveParams2, 0.5f,0.0f,0.0f,0.0f);
}
else if (WaterMgr->m_WaterType == L"lake")
{
m->fancyWaterShader->Uniform(str_waveParams1, 8.5f,1.5f,15.0f,0.03f);
m->fancyWaterShader->Uniform(str_waveParams2, 0.2f,0.0f,0.0f,0.07f);
}
else
{
m->fancyWaterShader->Uniform(str_waveParams1, 15.0f,0.8f,10.0f,0.1f);
m->fancyWaterShader->Uniform(str_waveParams2, 0.3f,0.0f,0.1f,0.3f);
}
if (shadow && WaterMgr->m_WaterShadows)
{
m->fancyWaterShader->BindTexture(str_shadowTex, shadow->GetTexture());
m->fancyWaterShader->Uniform(str_shadowTransform, shadow->GetTextureMatrix());
int width = shadow->GetWidth();
int height = shadow->GetHeight();
m->fancyWaterShader->Uniform(str_shadowScale, width, height, 1.0f / width, 1.0f / height);
}
std::vector& visiblePatches = m->visiblePatches[cullGroup];
for (size_t i = 0; i < visiblePatches.size(); ++i)
{
CPatchRData* data = visiblePatches[i];
data->RenderWater(m->fancyWaterShader);
}
m->fancyWaterShader->Unbind();
glDepthFunc(GL_LEQUAL);
glDisable(GL_BLEND);
return true;
}
void TerrainRenderer::RenderSimpleWater(int cullGroup)
{
#if CONFIG2_GLES
UNUSED2(cullGroup);
#else
PROFILE3_GPU("simple water");
WaterManager* WaterMgr = g_Renderer.GetWaterManager();
CLOSTexture& losTexture = g_Game->GetView()->GetLOSTexture();
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
double time = WaterMgr->m_WaterTexTimer;
double period = 1.6f;
int curTex = (int)(time*60/period) % 60;
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 = GetDummyShader();
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();
}