Graphics optimisations and features from eihrul.

Add shadow filtering (PCF) option.
Fix ugly shadow saturation in old lighting mode.
Fix fancy water shader.
Fix camera matrix computation.
Support scissoring of camera frustum.
Optimise vertex skinning.
Inline various matrix functions.
Support filtering of the list of submitted models before a rendering
pass, for more precise culling.
Optimise water renderer (fixes #721, based on patch by ortalo).
Use scissoring when generating reflection/refraction textures.
Skip reflection/refraction texture generation when no water is visible.
Render alpha-blended objects differently (fixes #434).
Reduce shadow swimming effects.

This was SVN commit r9814.
This commit is contained in:
Ykkrosh 2011-07-12 23:48:05 +00:00
parent 5ada37e8fa
commit b08e142193
48 changed files with 1625 additions and 795 deletions

View File

@ -29,6 +29,7 @@ bpp = 0
fancywater = true
shadows = true
shadowpcf = true
vsync = false
nos3tc = false

View File

@ -15,7 +15,7 @@ function openMenuDialog()
function openSettingsDialog()
{
var settings = getGUIObjectByName("settingsDialogPanel");
g_SessionDialog.open("Settings", null, settings, 340, 224, null);
g_SessionDialog.open("Settings", null, settings, 340, 252, null);
}
function openChat()

View File

@ -300,7 +300,7 @@
<object name="settingsDialogPanel"
sprite="genericPanel"
type="image"
size="50%-180 50%-200 50%+180 50%+50"
size="80%-180 50%-200 50%+180 50%+50"
hidden="true"
z="30"
>
@ -310,23 +310,30 @@
<action on="Load">if (renderer.shadows) this.checked = true; else this.checked = false;</action>
<action on="Press">renderer.shadows = this.checked;</action>
</object>
<!-- Settings / Shadow PCF -->
<object size="0 35 100%-80 60" type="text" style="settingsText" ghost="true">Enable Shadow Filtering</object>
<object name="shadowPCFCheckbox" size="100%-56 40 100%-30 65" type="checkbox" style="wheatCrossBox" checked="true">
<action on="Load">if (renderer.shadowPCF) this.checked = true; else this.checked = false;</action>
<action on="Press">renderer.shadowPCF = this.checked;</action>
</object>
<!-- Settings / Water -->
<object size="0 35 100%-80 60" type="text" style="settingsText" ghost="true">Enable Water Reflections</object>
<object name="fancyWaterCheckbox" size="100%-56 40 100%-30 65" type="checkbox" style="wheatCrossBox" checked="true">
<object size="0 60 100%-80 85" type="text" style="settingsText" ghost="true">Enable Water Reflections</object>
<object name="fancyWaterCheckbox" size="100%-56 65 100%-30 90" type="checkbox" style="wheatCrossBox" checked="true">
<action on="Load">if (renderer.fancyWater) this.checked = true; else this.checked = false;</action>
<action on="Press">renderer.fancyWater = this.checked;</action>
</object>
<!-- Settings / Music-->
<object size="0 60 100%-80 85" type="text" style="settingsText" ghost="true">Enable Music</object>
<object size="100%-56 65 100%-30 90" type="checkbox" style="wheatCrossBox" checked="true">
<object size="0 85 100%-80 110" type="text" style="settingsText" ghost="true">Enable Music</object>
<object size="100%-56 90 100%-30 115" type="checkbox" style="wheatCrossBox" checked="true">
<action on="Press">if (this.checked) startMusic(); else stopMusic();</action>
</object>
<!-- Settings / Dev Overlay -->
<object size="0 85 100%-80 110" type="text" style="settingsText" ghost="true">Developer Overlay</object>
<object size="100%-56 90 100%-30 115" type="checkbox" style="wheatCrossBox" checked="false">
<object size="0 110 100%-80 135" type="text" style="settingsText" ghost="true">Developer Overlay</object>
<object size="100%-56 115 100%-30 140" type="checkbox" style="wheatCrossBox" checked="false">
<action on="Press">toggleDeveloperOverlay();</action>
</object>
</object>

View File

@ -142,6 +142,7 @@ function RunDetection(settings)
var disable_audio = undefined;
var disable_s3tc = undefined;
var disable_shadows = undefined;
var disable_shadowpcf = undefined;
var disable_fancywater = undefined;
var override_renderpath = undefined;
@ -212,6 +213,7 @@ function RunDetection(settings)
)
{
disable_shadows = true;
disable_shadowpcf = true;
}
// Fragment-shader water is really slow on most Intel devices,
@ -222,6 +224,7 @@ function RunDetection(settings)
)
{
disable_fancywater = true;
disable_shadowpcf = true;
}
// http://trac.wildfiregames.com/ticket/780
@ -238,6 +241,7 @@ function RunDetection(settings)
"disable_audio": disable_audio,
"disable_s3tc": disable_s3tc,
"disable_shadows": disable_shadows,
"disable_shadowpcf": disable_shadowpcf,
"disable_fancywater": disable_fancywater,
"override_renderpath": override_renderpath,
};
@ -269,6 +273,9 @@ global.RunHardwareDetection = function(settings)
if (output.disable_shadows !== undefined)
Engine.SetDisableShadows(output.disable_shadows);
if (output.disable_shadowpcf !== undefined)
Engine.SetDisableShadowPCF(output.disable_shadowpcf);
if (output.disable_fancywater !== undefined)
Engine.SetDisableFancyWater(output.disable_fancywater);

View File

@ -29,7 +29,7 @@ for each (var settings in hwdetectTestData)
var os = (settings.os_linux ? "linux" : settings.os_macosx ? "macosx" : settings.os_win ? "win" : "???");
var disabled = [];
for each (var d in ["disable_audio", "disable_s3tc", "disable_shadows", "disable_fancywater", "override_renderpath"])
for each (var d in ["disable_audio", "disable_s3tc", "disable_shadows", "disable_shadowpcf", "disable_fancywater", "override_renderpath"])
if (output[d] !== undefined)
disabled.push(d+"="+output[d])

View File

@ -20,6 +20,12 @@
PARAM shadingColor = program.local[1];
PARAM ambient = program.local[2];
#ifdef USE_SHADOW_PCF
PARAM shadowOffsets1 = program.local[3];
PARAM shadowOffsets2 = program.local[4];
TEMP offset;
#endif
TEMP tex;
TEMP temp;
TEMP diffuse;
@ -42,14 +48,32 @@ TEX tex, fragment.texcoord[0], texture[0], 2D;
// (diffuse is 2*fragment.color due to clamp-avoidance in the vertex program)
#ifdef USE_SHADOW
#ifdef USE_FP_SHADOW
TEX temp, fragment.texcoord[1], texture[1], SHADOW2D;
#ifdef USE_SHADOW_PCF
MOV offset.zw, fragment.texcoord[1];
ADD offset.xy, fragment.texcoord[1], shadowOffsets1;
TEX temp.x, offset, texture[1], SHADOW2D;
ADD offset.xy, fragment.texcoord[1], shadowOffsets1.zwzw;
TEX temp.y, offset, texture[1], SHADOW2D;
ADD offset.xy, fragment.texcoord[1], shadowOffsets2;
TEX temp.z, offset, texture[1], SHADOW2D;
ADD offset.xy, fragment.texcoord[1], shadowOffsets2.zwzw;
TEX temp.w, offset, texture[1], SHADOW2D;
DP4 temp, temp, 0.25;
#else
TEX temp, fragment.texcoord[1], texture[1], SHADOW2D;
#endif
#else
TEX tex, fragment.texcoord[1], texture[1], 2D;
MOV_SAT temp.z, fragment.texcoord[1].z;
SGE temp, tex.x, temp.z;
#endif
MUL diffuse.rgb, fragment.color, 2.0;
MAD_MAYBE_SAT temp.rgb, diffuse, temp, ambient;
#ifdef CLAMP_LIGHTING
MAD_SAT diffuse.rgb, fragment.color, 2.0, ambient;
LRP temp.rgb, temp, diffuse, ambient;
#else
MUL diffuse.rgb, fragment.color, 2.0;
MAD temp.rgb, diffuse, temp, ambient;
#endif
MUL color.rgb, color, temp;
#else
MAD_MAYBE_SAT temp.rgb, fragment.color, 2.0, ambient;

View File

@ -19,6 +19,8 @@
<uniform name="objectColor" loc="0" type="vec3"/>
<uniform name="shadingColor" loc="1" type="vec3"/>
<uniform name="ambient" loc="2" type="vec3"/>
<uniform name="shadowOffsets1" loc="3" type="vec4"/>
<uniform name="shadowOffsets2" loc="4" type="vec4"/>
</fragment>
</program>

View File

@ -22,6 +22,8 @@
<uniform name="objectColor" loc="0" type="vec3"/>
<uniform name="shadingColor" loc="1" type="vec3"/>
<uniform name="ambient" loc="2" type="vec3"/>
<uniform name="shadowOffsets1" loc="3" type="vec4"/>
<uniform name="shadowOffsets2" loc="4" type="vec4"/>
</fragment>
</program>

View File

@ -13,6 +13,8 @@
<uniform name="losTex" loc="3" type="sampler2D"/>
<uniform name="ambient" loc="0" type="vec3"/>
<uniform name="shadowOffsets1" loc="2" type="vec4"/>
<uniform name="shadowOffsets2" loc="3" type="vec4"/>
</fragment>
</program>

View File

@ -16,6 +16,8 @@
<uniform name="losTex" loc="3" type="sampler2D"/>
<uniform name="ambient" loc="0" type="vec3"/>
<uniform name="shadowOffsets1" loc="2" type="vec4"/>
<uniform name="shadowOffsets2" loc="3" type="vec4"/>
</fragment>
</program>

View File

@ -19,6 +19,12 @@ PARAM ambient = program.local[0];
PARAM shadingColor = program.local[1];
#endif
#ifdef USE_SHADOW_PCF
PARAM shadowOffsets1 = program.local[2];
PARAM shadowOffsets2 = program.local[3];
TEMP offset;
#endif
TEMP tex;
TEMP temp;
TEMP diffuse;
@ -43,14 +49,32 @@ TEX color, fragment.texcoord[0], texture[0], 2D;
// (diffuse is 2*fragment.color due to clamp-avoidance in the vertex program)
#ifdef USE_SHADOW
#ifdef USE_FP_SHADOW
TEX temp, fragment.texcoord[2], texture[2], SHADOW2D;
#ifdef USE_SHADOW_PCF
MOV offset.zw, fragment.texcoord[2];
ADD offset.xy, fragment.texcoord[2], shadowOffsets1;
TEX temp.x, offset, texture[2], SHADOW2D;
ADD offset.xy, fragment.texcoord[2], shadowOffsets1.zwzw;
TEX temp.y, offset, texture[2], SHADOW2D;
ADD offset.xy, fragment.texcoord[2], shadowOffsets2;
TEX temp.z, offset, texture[2], SHADOW2D;
ADD offset.xy, fragment.texcoord[2], shadowOffsets2.zwzw;
TEX temp.w, offset, texture[2], SHADOW2D;
DP4 temp, temp, 0.25;
#else
TEX temp, fragment.texcoord[2], texture[2], SHADOW2D;
#endif
#else
TEX tex, fragment.texcoord[2], texture[2], 2D;
MOV_SAT temp.z, fragment.texcoord[2].z;
SGE temp, tex.x, temp.z;
#endif
MUL diffuse.rgb, fragment.color, 2.0;
MAD_MAYBE_SAT temp.rgb, diffuse, temp, ambient;
#ifdef CLAMP_LIGHTING
MAD_SAT diffuse.rgb, fragment.color, 2.0, ambient;
LRP temp.rgb, temp, diffuse, ambient;
#else
MUL diffuse.rgb, fragment.color, 2.0;
MAD temp.rgb, diffuse, temp, ambient;
#endif
MUL color.rgb, color, temp;
#else
MAD_MAYBE_SAT temp.rgb, fragment.color, 2.0, ambient;

View File

@ -16,6 +16,8 @@
<uniform name="ambient" loc="0" type="vec3"/>
<uniform name="shadingColor" loc="1" type="vec3"/>
<uniform name="shadowOffsets1" loc="2" type="vec4"/>
<uniform name="shadowOffsets2" loc="3" type="vec4"/>
</fragment>
</program>

View File

@ -16,7 +16,6 @@ uniform vec3 reflectionTint; // Tint for reflection (used for really muddy water
uniform float reflectionTintStrength; // Strength of reflection tint (how much of it to mix in)
varying vec3 worldPos;
varying float w;
varying float waterDepth;
void main()
@ -24,7 +23,6 @@ void main()
vec3 n, l, h, v; // Normal, light vector, half-vector and view vector (vector to eye)
float ndotl, ndoth, ndotv;
float fresnel;
float myMurkiness; // Murkiness and tint at this pixel (tweaked based on lighting and depth)
float t; // Temporary variable
vec2 reflCoords, refrCoords;
vec3 reflColor, refrColor, specular;
@ -39,21 +37,16 @@ void main()
ndoth = dot(n, h);
ndotv = dot(n, v);
myMurkiness = murkiness * min(waterDepth / fullDepth, 1.0);
fresnel = pow(1.0 - ndotv, 0.8); // A rather random Fresnel approximation
refrCoords = 0.5 * (gl_TexCoord[2].xy / gl_TexCoord[2].w) + 0.5; // Unbias texture coords
refrCoords -= 0.8 * waviness * n.xz / w; // Refractions can be slightly less wavy
reflCoords = 0.5 * (gl_TexCoord[1].xy / gl_TexCoord[1].w) + 0.5; // Unbias texture coords
reflCoords += waviness * n.xz / w;
refrCoords = (0.5*gl_TexCoord[2].xy - 0.8*waviness*n.xz) / gl_TexCoord[2].w + 0.5; // Unbias texture coords
reflCoords = (0.5*gl_TexCoord[1].xy + waviness*n.xz) / gl_TexCoord[1].w + 0.5; // Unbias texture coords
reflColor = mix(texture2D(reflectionMap, reflCoords).rgb, sunColor * reflectionTint,
reflectionTintStrength);
refrColor = (0.5 + 0.5*ndotl) * mix(texture2D(refractionMap, refrCoords).rgb, sunColor * tint,
myMurkiness);
murkiness * clamp(waterDepth / fullDepth, 0.0, 1.0)); // Murkiness and tint at this pixel (tweaked based on lighting and depth)
specular = pow(max(0.0, ndoth), shininess) * sunColor * specularStrength;

View File

@ -1,22 +1,19 @@
uniform mat4 reflectionMatrix;
uniform mat4 refractionMatrix;
uniform mat4 losMatrix;
uniform vec4 translation;
attribute float vertexDepth;
uniform float repeatScale;
uniform vec2 translation;
varying vec3 worldPos;
varying float w;
varying float waterDepth;
void main()
{
worldPos = gl_Vertex.xyz;
waterDepth = vertexDepth;
gl_TexCoord[0] = gl_MultiTexCoord0 + translation;
waterDepth = dot(gl_Color.xyz, vec3(255.0, -255.0, 1.0));
gl_TexCoord[0].st = gl_Vertex.xz*repeatScale + translation;
gl_TexCoord[1] = reflectionMatrix * gl_Vertex; // projective texturing
gl_TexCoord[2] = reflectionMatrix * gl_Vertex;
gl_TexCoord[2] = refractionMatrix * gl_Vertex;
gl_TexCoord[3] = losMatrix * gl_Vertex;
w = gl_TexCoord[1].w;
gl_Position = ftransform();
}

View File

@ -34,7 +34,7 @@
#include "renderer/Renderer.h"
#include "renderer/WaterManager.h"
CCamera::CCamera ()
CCamera::CCamera()
{
// set viewport to something anything should handle, but should be initialised
// to window size before use
@ -44,38 +44,35 @@ CCamera::CCamera ()
m_ViewPort.m_Height = 600;
}
CCamera::~CCamera ()
CCamera::~CCamera()
{
}
void CCamera::SetProjection (float nearp, float farp, float fov)
void CCamera::SetProjection(float nearp, float farp, float fov)
{
m_NearPlane = nearp;
m_FarPlane = farp;
m_FOV = fov;
float Aspect = (float)m_ViewPort.m_Width/(float)m_ViewPort.m_Height;
float w = tanf (m_FOV*0.5f*Aspect);
float h = tanf (m_FOV*0.5f);
float aspect = (float)m_ViewPort.m_Width/(float)m_ViewPort.m_Height;
float f = 1.0f/tanf(m_FOV/2);
m_ProjMat.SetZero ();
m_ProjMat._11 = 1/w;
m_ProjMat._22 = 1/h;
m_ProjMat._33 = (m_FarPlane+m_NearPlane)/(m_FarPlane-m_NearPlane);
m_ProjMat._34 = -2*m_FarPlane*m_NearPlane/(m_FarPlane-m_NearPlane);
m_ProjMat._11 = f/aspect;
m_ProjMat._22 = f;
m_ProjMat._33 = -(m_FarPlane+m_NearPlane)/(m_NearPlane-m_FarPlane);
m_ProjMat._34 = 2*m_FarPlane*m_NearPlane/(m_NearPlane-m_FarPlane);
m_ProjMat._43 = 1.0f;
}
void CCamera::SetProjectionTile (int tiles, int tile_x, int tile_y)
void CCamera::SetProjectionTile(int tiles, int tile_x, int tile_y)
{
float Aspect = (float)m_ViewPort.m_Width/(float)m_ViewPort.m_Height;
float w = tanf (m_FOV*0.5f*Aspect) / tiles;
float h = tanf (m_FOV*0.5f) / tiles;
float aspect = (float)m_ViewPort.m_Width/(float)m_ViewPort.m_Height;
float f = 1.0f/tanf(m_FOV/2);
m_ProjMat._11 = 1/w;
m_ProjMat._22 = 1/h;
m_ProjMat._11 = tiles*f/aspect;
m_ProjMat._22 = tiles*f;
m_ProjMat._13 = -(1-tiles + 2*tile_x);
m_ProjMat._23 = -(1-tiles + 2*tile_y);
}
@ -83,7 +80,7 @@ void CCamera::SetProjectionTile (int tiles, int tile_x, int tile_y)
//Updates the frustum planes. Should be called
//everytime the view or projection matrices are
//altered.
void CCamera::UpdateFrustum ()
void CCamera::UpdateFrustum(const CBound& scissor)
{
CMatrix3D MatFinal;
CMatrix3D MatView;
@ -92,46 +89,51 @@ void CCamera::UpdateFrustum ()
MatFinal = m_ProjMat * MatView;
//get the RIGHT plane
m_ViewFrustum.SetNumPlanes (6);
// get the RIGHT plane
m_ViewFrustum.SetNumPlanes(6);
m_ViewFrustum.m_aPlanes[0].m_Norm.X = MatFinal._41-MatFinal._11;
m_ViewFrustum.m_aPlanes[0].m_Norm.Y = MatFinal._42-MatFinal._12;
m_ViewFrustum.m_aPlanes[0].m_Norm.Z = MatFinal._43-MatFinal._13;
m_ViewFrustum.m_aPlanes[0].m_Dist = MatFinal._44-MatFinal._14;
m_ViewFrustum.m_aPlanes[0].m_Norm.X = scissor[1].X*MatFinal._41 - MatFinal._11;
m_ViewFrustum.m_aPlanes[0].m_Norm.Y = scissor[1].X*MatFinal._42 - MatFinal._12;
m_ViewFrustum.m_aPlanes[0].m_Norm.Z = scissor[1].X*MatFinal._43 - MatFinal._13;
m_ViewFrustum.m_aPlanes[0].m_Dist = scissor[1].X*MatFinal._44 - MatFinal._14;
//get the LEFT plane
m_ViewFrustum.m_aPlanes[1].m_Norm.X = MatFinal._41+MatFinal._11;
m_ViewFrustum.m_aPlanes[1].m_Norm.Y = MatFinal._42+MatFinal._12;
m_ViewFrustum.m_aPlanes[1].m_Norm.Z = MatFinal._43+MatFinal._13;
m_ViewFrustum.m_aPlanes[1].m_Dist = MatFinal._44+MatFinal._14;
// get the LEFT plane
m_ViewFrustum.m_aPlanes[1].m_Norm.X = -scissor[0].X*MatFinal._41 + MatFinal._11;
m_ViewFrustum.m_aPlanes[1].m_Norm.Y = -scissor[0].X*MatFinal._42 + MatFinal._12;
m_ViewFrustum.m_aPlanes[1].m_Norm.Z = -scissor[0].X*MatFinal._43 + MatFinal._13;
m_ViewFrustum.m_aPlanes[1].m_Dist = -scissor[0].X*MatFinal._44 + MatFinal._14;
//get the BOTTOM plane
m_ViewFrustum.m_aPlanes[2].m_Norm.X = MatFinal._41+MatFinal._21;
m_ViewFrustum.m_aPlanes[2].m_Norm.Y = MatFinal._42+MatFinal._22;
m_ViewFrustum.m_aPlanes[2].m_Norm.Z = MatFinal._43+MatFinal._23;
m_ViewFrustum.m_aPlanes[2].m_Dist = MatFinal._44+MatFinal._24;
// get the BOTTOM plane
m_ViewFrustum.m_aPlanes[2].m_Norm.X = -scissor[0].Y*MatFinal._41 + MatFinal._21;
m_ViewFrustum.m_aPlanes[2].m_Norm.Y = -scissor[0].Y*MatFinal._42 + MatFinal._22;
m_ViewFrustum.m_aPlanes[2].m_Norm.Z = -scissor[0].Y*MatFinal._43 + MatFinal._23;
m_ViewFrustum.m_aPlanes[2].m_Dist = -scissor[0].Y*MatFinal._44 + MatFinal._24;
//get the TOP plane
m_ViewFrustum.m_aPlanes[3].m_Norm.X = MatFinal._41-MatFinal._21;
m_ViewFrustum.m_aPlanes[3].m_Norm.Y = MatFinal._42-MatFinal._22;
m_ViewFrustum.m_aPlanes[3].m_Norm.Z = MatFinal._43-MatFinal._23;
m_ViewFrustum.m_aPlanes[3].m_Dist = MatFinal._44-MatFinal._24;
// get the TOP plane
m_ViewFrustum.m_aPlanes[3].m_Norm.X = scissor[1].Y*MatFinal._41 - MatFinal._21;
m_ViewFrustum.m_aPlanes[3].m_Norm.Y = scissor[1].Y*MatFinal._42 - MatFinal._22;
m_ViewFrustum.m_aPlanes[3].m_Norm.Z = scissor[1].Y*MatFinal._43 - MatFinal._23;
m_ViewFrustum.m_aPlanes[3].m_Dist = scissor[1].Y*MatFinal._44 - MatFinal._24;
//get the FAR plane
m_ViewFrustum.m_aPlanes[4].m_Norm.X = MatFinal._41-MatFinal._31;
m_ViewFrustum.m_aPlanes[4].m_Norm.Y = MatFinal._42-MatFinal._32;
m_ViewFrustum.m_aPlanes[4].m_Norm.Z = MatFinal._43-MatFinal._33;
m_ViewFrustum.m_aPlanes[4].m_Dist = MatFinal._44-MatFinal._34;
// get the FAR plane
m_ViewFrustum.m_aPlanes[4].m_Norm.X = scissor[1].Z*MatFinal._41 - MatFinal._31;
m_ViewFrustum.m_aPlanes[4].m_Norm.Y = scissor[1].Z*MatFinal._42 - MatFinal._32;
m_ViewFrustum.m_aPlanes[4].m_Norm.Z = scissor[1].Z*MatFinal._43 - MatFinal._33;
m_ViewFrustum.m_aPlanes[4].m_Dist = scissor[1].Z*MatFinal._44 - MatFinal._34;
//get the NEAR plane
m_ViewFrustum.m_aPlanes[5].m_Norm.X = MatFinal._41+MatFinal._31;
m_ViewFrustum.m_aPlanes[5].m_Norm.Y = MatFinal._42+MatFinal._32;
m_ViewFrustum.m_aPlanes[5].m_Norm.Z = MatFinal._43+MatFinal._33;
m_ViewFrustum.m_aPlanes[5].m_Dist = MatFinal._44+MatFinal._34;
// get the NEAR plane
m_ViewFrustum.m_aPlanes[5].m_Norm.X = -scissor[0].Z*MatFinal._41 + MatFinal._31;
m_ViewFrustum.m_aPlanes[5].m_Norm.Y = -scissor[0].Z*MatFinal._42 + MatFinal._32;
m_ViewFrustum.m_aPlanes[5].m_Norm.Z = -scissor[0].Z*MatFinal._43 + MatFinal._33;
m_ViewFrustum.m_aPlanes[5].m_Dist = -scissor[0].Z*MatFinal._44 + MatFinal._34;
}
void CCamera::SetViewPort (const SViewPort& viewport)
void CCamera::ClipFrustum(const CPlane& clipPlane)
{
m_ViewFrustum.AddPlane(clipPlane);
}
void CCamera::SetViewPort(const SViewPort& viewport)
{
m_ViewPort.m_X = viewport.m_X;
m_ViewPort.m_Y = viewport.m_Y;

View File

@ -24,6 +24,7 @@
#define INCLUDED_CAMERA
#include "Frustum.h"
#include "maths/Bound.h"
#include "maths/Matrix3D.h"
// view port
@ -35,27 +36,32 @@ struct SViewPort
size_t m_Height;
};
class CCamera
{
public:
CCamera ();
~CCamera ();
CCamera();
~CCamera();
// Methods for projection
void SetProjection (float nearp, float farp, float fov);
void SetProjectionTile (int tiles, int tile_x, int tile_y);
CMatrix3D& GetProjection () { return m_ProjMat; }
const CMatrix3D& GetProjection () const { return m_ProjMat; }
void SetProjection(float nearp, float farp, float fov);
void SetProjectionTile(int tiles, int tile_x, int tile_y);
CMatrix3D& GetProjection() { return m_ProjMat; }
const CMatrix3D& GetProjection() const { return m_ProjMat; }
CMatrix3D& GetOrientation() { return m_Orientation; }
const CMatrix3D& GetOrientation() const { return m_Orientation; }
CMatrix3D GetViewProjection() { return m_ProjMat * m_Orientation.GetInverse(); }
// Updates the frustum planes. Should be called
// everytime the view or projection matrices are
// altered.
void UpdateFrustum ();
const CFrustum& GetFrustum () const { return m_ViewFrustum; }
void UpdateFrustum(const CBound& scissor = CBound(CVector3D(-1.0f, -1.0f, -1.0f), CVector3D(1.0f, 1.0f, 1.0f)));
void ClipFrustum(const CPlane& clipPlane);
const CFrustum& GetFrustum() const { return m_ViewFrustum; }
void SetViewPort (const SViewPort& viewport);
const SViewPort& GetViewPort () const { return m_ViewPort; }
void SetViewPort(const SViewPort& viewport);
const SViewPort& GetViewPort() const { return m_ViewPort; }
// getters
float GetNearPlane() const { return m_NearPlane; }

View File

@ -49,6 +49,17 @@ void CFrustum::SetNumPlanes (size_t num)
m_NumPlanes = MAX_NUM_FRUSTUM_PLANES-1;
}
void CFrustum::AddPlane (const CPlane& plane)
{
if (m_NumPlanes >= MAX_NUM_FRUSTUM_PLANES)
{
debug_warn(L"CFrustum::AddPlane: Too many planes");
return;
}
m_aPlanes[m_NumPlanes++] = plane;
}
bool CFrustum::IsPointVisible (const CVector3D &point) const
{
PLANESIDE Side;

View File

@ -48,6 +48,8 @@ public:
size_t GetNumPlanes() const { return m_NumPlanes; }
void AddPlane (const CPlane& plane);
//The following methods return true if the shape is
//partially or completely in front of the frustum planes
bool IsPointVisible (const CVector3D &point) const;

View File

@ -81,18 +81,21 @@ bool CModel::InitModel(const CModelDefPtr& modeldef)
size_t numBones = modeldef->GetNumBones();
if (numBones != 0)
{
size_t numBlends = modeldef->GetNumBlends();
// allocate matrices for bone transformations
m_BoneMatrices = new CMatrix3D[numBones];
m_BoneMatrices = new CMatrix3D[numBones + numBlends];
for (size_t i = 0; i < numBones + numBlends; ++i)
{
m_BoneMatrices[i].SetIdentity();
}
m_InverseBindBoneMatrices = new CMatrix3D[numBones];
// store default pose until animation assigned
CBoneState* defpose = modeldef->GetBones();
for (size_t i = 0; i < numBones; ++i)
{
m_BoneMatrices[i].SetIdentity();
m_BoneMatrices[i].Rotate(defpose[i].m_Rotation);
m_BoneMatrices[i].Translate(defpose[i].m_Translation);
m_InverseBindBoneMatrices[i].SetIdentity();
m_InverseBindBoneMatrices[i].Translate(-defpose[i].m_Translation);
m_InverseBindBoneMatrices[i].Rotate(defpose[i].m_Rotation.GetInverse());
@ -100,7 +103,7 @@ bool CModel::InitModel(const CModelDefPtr& modeldef)
}
m_PositionValid = true;
return true;
}
@ -111,7 +114,10 @@ void CModel::CalcBounds()
{
// Need to calculate the object bounds first, if that hasn't already been done
if (! (m_Anim && m_Anim->m_AnimDef))
CalcObjectBounds();
{
if (m_ObjectBounds.IsEmpty())
CalcObjectBounds();
}
else
{
if (m_Anim->m_ObjectBounds.IsEmpty())
@ -180,7 +186,7 @@ void CModel::CalcAnimatedObjectBound(CSkeletonAnimDef* anim,CBound& result)
// extend bounds by vertex positions at the frame
for (size_t i=0;i<numverts;i++)
{
result += CModelDef::SkinPoint(verts[i], GetAnimatedBoneMatrices(), GetInverseBindBoneMatrices());
result += CModelDef::SkinPoint(verts[i], GetAnimatedBoneMatrices());
}
// advance to next frame
m_AnimTime += anim->GetFrameTime();
@ -322,6 +328,16 @@ void CModel::ValidatePosition()
prop.m_Model->SetTransform(proptransform);
prop.m_Model->ValidatePosition();
}
if (m_BoneMatrices)
{
for (size_t i = 0; i < m_pModelDef->GetNumBones(); i++)
{
m_BoneMatrices[i] = m_BoneMatrices[i] * m_InverseBindBoneMatrices[i];
}
m_pModelDef->BlendBoneMatrices(m_BoneMatrices);
}
}

View File

@ -40,7 +40,7 @@ class CSkeletonAnimManager;
#define MODELFLAG_NOLOOPANIMATION (1<<1)
#define MODELFLAG_SILHOUETTE_DISPLAY (1<<2)
#define MODELFLAG_SILHOUETTE_OCCLUDER (1<<3)
#define MODELFLAG_FILTERED (1<<4) // used internally by renderer
///////////////////////////////////////////////////////////////////////////////
// CModel: basically, a mesh object - holds the texturing and skinning

View File

@ -27,24 +27,20 @@
#include "maths/Vector4D.h"
CVector3D CModelDef::SkinPoint(const SModelVertex& vtx,
const CMatrix3D newPoseMatrices[],
const CMatrix3D inverseBindMatrices[])
const CMatrix3D newPoseMatrices[])
{
CVector3D result (0, 0, 0);
for (int i = 0; i < SVertexBlend::SIZE && vtx.m_Blend.m_Bone[i] != 0xff; ++i)
{
CVector3D bindSpace = inverseBindMatrices[vtx.m_Blend.m_Bone[i]].Transform(vtx.m_Coords);
CVector3D worldSpace = newPoseMatrices[vtx.m_Blend.m_Bone[i]].Transform(bindSpace);
result += worldSpace * vtx.m_Blend.m_Weight[i];
result += newPoseMatrices[vtx.m_Blend.m_Bone[i]].Transform(vtx.m_Coords) * vtx.m_Blend.m_Weight[i];
}
return result;
}
CVector3D CModelDef::SkinNormal(const SModelVertex& vtx,
const CMatrix3D newPoseMatrices[],
const CMatrix3D inverseBindMatrices[])
const CMatrix3D newPoseMatrices[])
{
// To be correct, the normal vectors apparently need to be multiplied by the
// inverse of the transpose. Unfortunately inverses are slow.
@ -73,9 +69,7 @@ CVector3D CModelDef::SkinNormal(const SModelVertex& vtx,
for (int i = 0; i < SVertexBlend::SIZE && vtx.m_Blend.m_Bone[i] != 0xff; ++i)
{
CVector3D bindSpace = inverseBindMatrices[vtx.m_Blend.m_Bone[i]].Rotate(vtx.m_Norm);
CVector3D worldSpace = newPoseMatrices[vtx.m_Blend.m_Bone[i]].Rotate(bindSpace);
result += worldSpace * vtx.m_Blend.m_Weight[i];
result += newPoseMatrices[vtx.m_Blend.m_Bone[i]].Rotate(vtx.m_Norm) * vtx.m_Blend.m_Weight[i];
}
// If there was more than one influence, the result is probably not going
@ -94,27 +88,15 @@ void CModelDef::SkinPointsAndNormals(
const VertexArrayIterator<CVector3D>& Position,
const VertexArrayIterator<CVector3D>& Normal,
const SModelVertex* vertices,
const CMatrix3D newPoseMatrices[],
const CMatrix3D inverseBindMatrices[])
const size_t* blendIndices,
const CMatrix3D newPoseMatrices[])
{
for (size_t j = 0; j < numVertices; ++j)
{
const SModelVertex vtx = vertices[j];
const SModelVertex& vtx = vertices[j];
CVector3D pos(0, 0, 0);
CVector3D normal(0, 0, 0);
for (int i = 0; i < SVertexBlend::SIZE && vtx.m_Blend.m_Bone[i] != 0xff; ++i)
{
CVector3D posBindSpace = inverseBindMatrices[vtx.m_Blend.m_Bone[i]].Transform(vtx.m_Coords);
CVector3D normBindSpace = inverseBindMatrices[vtx.m_Blend.m_Bone[i]].Rotate(vtx.m_Norm);
CVector3D posWorldSpace = newPoseMatrices[vtx.m_Blend.m_Bone[i]].Transform(posBindSpace);
CVector3D normWorldSpace = newPoseMatrices[vtx.m_Blend.m_Bone[i]].Rotate(normBindSpace);
pos += posWorldSpace * vtx.m_Blend.m_Weight[i];
normal += normWorldSpace * vtx.m_Blend.m_Weight[i];
}
Position[j] = newPoseMatrices[blendIndices[j]].Transform(vtx.m_Coords);
Normal[j] = newPoseMatrices[blendIndices[j]].Rotate(vtx.m_Norm);
// If there was more than one influence, the result is probably not going
// to be of unit length (since it's a weighted sum of several independent
@ -122,16 +104,30 @@ void CModelDef::SkinPointsAndNormals(
// (It's fairly common to only have one influence, so it seems sensible to
// optimise that case a bit.)
if (vtx.m_Blend.m_Bone[1] != 0xff) // if more than one influence
normal.Normalize();
Normal[j].Normalize();
}
}
Position[j] = pos;
Normal[j] = normal;
void CModelDef::BlendBoneMatrices(
CMatrix3D boneMatrices[])
{
for (size_t i = 0; i < m_NumBlends; ++i)
{
const SVertexBlend& blend = m_pBlends[i];
CMatrix3D& boneMatrix = boneMatrices[m_NumBones + i];
boneMatrix.Blend(boneMatrices[blend.m_Bone[0]], blend.m_Weight[0]);
boneMatrix.AddBlend(boneMatrices[blend.m_Bone[1]], blend.m_Weight[1]);
for (size_t j = 2; j < SVertexBlend::SIZE && blend.m_Bone[j] != 0xFF; ++j)
{
boneMatrix.AddBlend(boneMatrices[blend.m_Bone[j]], blend.m_Weight[j]);
}
}
}
// CModelDef Constructor
CModelDef::CModelDef()
: m_NumVertices(0), m_pVertices(0), m_NumFaces(0), m_pFaces(0), m_NumBones(0), m_Bones(0),
CModelDef::CModelDef() :
m_NumVertices(0), m_pVertices(0), m_NumFaces(0), m_pFaces(0), m_NumBones(0), m_Bones(0),
m_NumBlends(0), m_pBlends(0), m_pBlendIndices(0),
m_Name(L"[not loaded]")
{
}
@ -144,6 +140,8 @@ CModelDef::~CModelDef()
delete[] m_pVertices;
delete[] m_pFaces;
delete[] m_Bones;
delete[] m_pBlends;
delete[] m_pBlendIndices;
}
// FindPropPoint: find and return pointer to prop point matching given name;
@ -187,6 +185,34 @@ CModelDef* CModelDef::Load(const VfsPath& filename, const VfsPath& name)
{
mdef->m_Bones=new CBoneState[mdef->m_NumBones];
unpacker.UnpackRaw(mdef->m_Bones,mdef->m_NumBones*sizeof(CBoneState));
mdef->m_pBlendIndices = new size_t[mdef->m_NumVertices];
std::vector<SVertexBlend> blends;
for (size_t i = 0; i < mdef->m_NumVertices; i++)
{
const SVertexBlend &blend = mdef->m_pVertices[i].m_Blend;
if (blend.m_Bone[1] == 0xFF)
{
mdef->m_pBlendIndices[i] = blend.m_Bone[0];
}
else
{
// If there's already a vertex using the same blend as this, then
// reuse its entry from blends; otherwise add the new one to blends
size_t j;
for (j = 0; j < blends.size(); j++)
{
if (blend == blends[j]) break;
}
if (j >= blends.size())
blends.push_back(blend);
mdef->m_pBlendIndices[i] = mdef->m_NumBones + j;
}
}
mdef->m_NumBlends = blends.size();
mdef->m_pBlends = new SVertexBlend[mdef->m_NumBlends];
std::copy(blends.begin(), blends.end(), mdef->m_pBlends);
}
if (unpacker.GetVersion() >= 2)
@ -232,10 +258,6 @@ CModelDef* CModelDef::Load(const VfsPath& filename, const VfsPath& name)
if (mdef->m_NumBones) // only do skinned models
{
CMatrix3D identity;
identity.SetIdentity();
std::vector<CMatrix3D> identityBones (mdef->m_NumBones, identity);
std::vector<CMatrix3D> bindPose (mdef->m_NumBones);
for (size_t i = 0; i < mdef->m_NumBones; ++i)
@ -247,8 +269,8 @@ CModelDef* CModelDef::Load(const VfsPath& filename, const VfsPath& name)
for (size_t i = 0; i < mdef->m_NumVertices; ++i)
{
mdef->m_pVertices[i].m_Coords = SkinPoint(mdef->m_pVertices[i], &bindPose[0], &identityBones[0]);
mdef->m_pVertices[i].m_Norm = SkinNormal(mdef->m_pVertices[i], &bindPose[0], &identityBones[0]);
mdef->m_pVertices[i].m_Coords = SkinPoint(mdef->m_pVertices[i], &bindPose[0]);
mdef->m_pVertices[i].m_Norm = SkinNormal(mdef->m_pVertices[i], &bindPose[0]);
}
}
}

View File

@ -28,6 +28,7 @@
#include "lib/file/vfs/vfs_path.h"
#include "renderer/VertexArray.h"
#include <map>
#include <cstring>
class CBoneState;
@ -57,6 +58,11 @@ struct SVertexBlend
u8 m_Bone[SIZE];
// weight of the influence; all weights sum to 1
float m_Weight[SIZE];
bool operator==(const SVertexBlend& o) const
{
return !memcmp(m_Bone, o.m_Bone, sizeof(m_Bone)) && !memcmp(m_Weight, o.m_Weight, sizeof(m_Weight));
}
};
///////////////////////////////////////////////////////////////////////////////
@ -124,17 +130,22 @@ public:
public:
// accessor: get vertex data
size_t GetNumVertices() const { return (size_t)m_NumVertices; }
size_t GetNumVertices() const { return m_NumVertices; }
SModelVertex* GetVertices() const { return m_pVertices; }
// accessor: get face data
size_t GetNumFaces() const { return (size_t)m_NumFaces; }
size_t GetNumFaces() const { return m_NumFaces; }
SModelFace* GetFaces() const { return m_pFaces; }
// accessor: get bone data
size_t GetNumBones() const { return (size_t)m_NumBones; }
size_t GetNumBones() const { return m_NumBones; }
CBoneState* GetBones() const { return m_Bones; }
// accessor: get blend data
size_t GetNumBlends() const { return m_NumBlends; }
SVertexBlend* GetBlends() const { return m_pBlends; }
size_t* GetBlendIndices() const { return m_pBlendIndices; }
// find and return pointer to prop point matching given name; return
// null if no match (case insensitive search)
const SPropPoint* FindPropPoint(const char* name) const;
@ -145,7 +156,7 @@ public:
* @return new world-space vertex coordinates
*/
static CVector3D SkinPoint(const SModelVertex& vtx,
const CMatrix3D newPoseMatrices[], const CMatrix3D inverseBindMatrices[]);
const CMatrix3D newPoseMatrices[]);
/**
* Transform the given vertex's normal from the bind pose into the new pose.
@ -153,7 +164,7 @@ public:
* @return new world-space vertex normal
*/
static CVector3D SkinNormal(const SModelVertex& vtx,
const CMatrix3D newPoseMatrices[], const CMatrix3D inverseBindMatrices[]);
const CMatrix3D newPoseMatrices[]);
/**
* Transform vertices' positions and normals.
@ -165,8 +176,13 @@ public:
const VertexArrayIterator<CVector3D>& Position,
const VertexArrayIterator<CVector3D>& Normal,
const SModelVertex* vertices,
const CMatrix3D newPoseMatrices[],
const CMatrix3D inverseBindMatrices[]);
const size_t* blendIndices,
const CMatrix3D newPoseMatrices[]);
/**
* Blend bone matrices together to fill bone palette.
*/
void BlendBoneMatrices(CMatrix3D boneMatrices[]);
/**
* Register renderer private data. Use the key to
@ -200,6 +216,10 @@ public:
// bone data - default model pose
size_t m_NumBones;
CBoneState* m_Bones;
// blend data
size_t m_NumBlends;
SVertexBlend *m_pBlends;
size_t* m_pBlendIndices;
// prop point data
std::vector<SPropPoint> m_PropPoints;

View File

@ -21,7 +21,7 @@
CShaderPass::CShaderPass(const CShaderProgramPtr& shader) :
m_Shader(shader),
m_HasAlpha(false), m_HasBlend(false), m_HasColorMask(false), m_HasDepthMask(false)
m_HasAlpha(false), m_HasBlend(false), m_HasColorMask(false), m_HasDepthMask(false), m_HasDepthFunc(false)
{
}
@ -46,6 +46,9 @@ void CShaderPass::Bind()
if (m_HasDepthMask)
glDepthMask(m_DepthMask);
if (m_HasDepthFunc)
glDepthFunc(m_DepthFunc);
}
void CShaderPass::Unbind()
@ -67,6 +70,9 @@ void CShaderPass::Unbind()
if (m_HasDepthMask)
glDepthMask(1);
if (m_HasDepthFunc)
glDepthFunc(GL_LEQUAL);
}
void CShaderPass::AlphaFunc(GLenum func, GLclampf ref)
@ -98,6 +104,12 @@ void CShaderPass::DepthMask(GLboolean mask)
m_DepthMask = mask;
}
void CShaderPass::DepthFunc(GLenum func)
{
m_HasDepthFunc = true;
m_DepthFunc = func;
}
CShaderTechnique::CShaderTechnique(const CShaderPass& pass)
{

View File

@ -33,6 +33,7 @@ public:
void BlendFunc(GLenum src, GLenum dst);
void ColorMask(GLboolean r, GLboolean g, GLboolean b, GLboolean a);
void DepthMask(GLboolean mask);
void DepthFunc(GLenum func);
/**
* Set all the GL state that was previously specified on this pass.
@ -65,6 +66,9 @@ private:
bool m_HasDepthMask;
GLboolean m_DepthMask;
bool m_HasDepthFunc;
GLenum m_DepthFunc;
};
/**

View File

@ -32,36 +32,6 @@
#include "maths/Matrix3D.h"
///////////////////////////////////////////////////////////////////////////////
// operator+=: extend this bound to include given bound
CBound& CBound::operator+=(const CBound& b)
{
#define CMPT(c) \
if (b[0].c < m_Data[0].c) m_Data[0].c = b[0].c; \
if (b[1].c > m_Data[1].c) m_Data[1].c = b[1].c
CMPT(X);
CMPT(Y);
CMPT(Z);
#undef CMPT
return *this;
}
///////////////////////////////////////////////////////////////////////////////
// operator+=: extend this bound to include given point
CBound& CBound::operator+=(const CVector3D& pt)
{
#define CMPT(c) \
if (pt.c < m_Data[0].c) m_Data[0].c = pt.c; \
if (pt.c > m_Data[1].c) m_Data[1].c = pt.c
CMPT(X);
CMPT(Y);
CMPT(Z);
#undef CMPT
return *this;
}
///////////////////////////////////////////////////////////////////////////////
// RayIntersect: intersect ray with this bound; return true
// if ray hits (and store entry and exit times), or false
@ -156,7 +126,7 @@ void CBound::SetEmpty()
///////////////////////////////////////////////////////////////////////////////
// IsEmpty: tests whether this bound is empty
bool CBound::IsEmpty()
bool CBound::IsEmpty() const
{
return (m_Data[0].X == FLT_MAX && m_Data[0].Y == FLT_MAX && m_Data[0].Z == FLT_MAX
&& m_Data[1].X == -FLT_MAX && m_Data[1].Y == -FLT_MAX && m_Data[1].Z == -FLT_MAX);

View File

@ -44,17 +44,38 @@ public:
const CVector3D& operator[](int index) const { return m_Data[index]; }
void SetEmpty();
bool IsEmpty();
bool IsEmpty() const;
CBound& operator+=(const CBound& b);
CBound& operator+=(const CVector3D& pt);
void Extend(const CVector3D& min, const CVector3D& max)
{
if (min.X < m_Data[0].X) m_Data[0].X = min.X;
if (min.Y < m_Data[0].Y) m_Data[0].Y = min.Y;
if (min.Z < m_Data[0].Z) m_Data[0].Z = min.Z;
if (max.X > m_Data[1].X) m_Data[1].X = max.X;
if (max.Y > m_Data[1].Y) m_Data[1].Y = max.Y;
if (max.Z > m_Data[1].Z) m_Data[1].Z = max.Z;
}
// operator+=: extend this bound to include given bound
CBound& operator+=(const CBound& b)
{
Extend(b.m_Data[0], b.m_Data[1]);
return *this;
}
// operator+=: extend this bound to include given point
CBound& operator+=(const CVector3D& pt)
{
Extend(pt, pt);
return *this;
}
bool RayIntersect(const CVector3D& origin,const CVector3D& dir,float& tmin,float& tmax) const;
// return the volume of this bounding box
float GetVolume() const {
CVector3D v=m_Data[1]-m_Data[0];
return v.X*v.Y*v.Z;
return std::max(v.X, 0.0f)*std::max(v.Y, 0.0f)*std::max(v.Z, 0.0f);
}
// return the centre of this bounding box

View File

@ -23,7 +23,7 @@
#define SQR(x) ((x) * (x))
template <typename T>
T Interpolate(const T& a, const T& b, float l)
inline T Interpolate(const T& a, const T& b, float l)
{
return a + (b - a) * l;
}

View File

@ -26,109 +26,6 @@
#include "Quaternion.h"
#include "Vector4D.h"
CMatrix3D::CMatrix3D ()
{
}
CMatrix3D::CMatrix3D(
float a11, float a12, float a13, float a14,
float a21, float a22, float a23, float a24,
float a31, float a32, float a33, float a34,
float a41, float a42, float a43, float a44) :
_11(a11), _12(a12), _13(a13), _14(a14),
_21(a21), _22(a22), _23(a23), _24(a24),
_31(a31), _32(a32), _33(a33), _34(a34),
_41(a41), _42(a42), _43(a43), _44(a44)
{
}
CMatrix3D::CMatrix3D(float data[])
{
for(int i=0; i<16; i++)
{
_data[i] = data[i];
}
}
//Matrix multiplication
CMatrix3D CMatrix3D::operator*(const CMatrix3D& matrix) const
{
return CMatrix3D(
_11*matrix._11 + _12*matrix._21 + _13*matrix._31 + _14*matrix._41,
_11*matrix._12 + _12*matrix._22 + _13*matrix._32 + _14*matrix._42,
_11*matrix._13 + _12*matrix._23 + _13*matrix._33 + _14*matrix._43,
_11*matrix._14 + _12*matrix._24 + _13*matrix._34 + _14*matrix._44,
_21*matrix._11 + _22*matrix._21 + _23*matrix._31 + _24*matrix._41,
_21*matrix._12 + _22*matrix._22 + _23*matrix._32 + _24*matrix._42,
_21*matrix._13 + _22*matrix._23 + _23*matrix._33 + _24*matrix._43,
_21*matrix._14 + _22*matrix._24 + _23*matrix._34 + _24*matrix._44,
_31*matrix._11 + _32*matrix._21 + _33*matrix._31 + _34*matrix._41,
_31*matrix._12 + _32*matrix._22 + _33*matrix._32 + _34*matrix._42,
_31*matrix._13 + _32*matrix._23 + _33*matrix._33 + _34*matrix._43,
_31*matrix._14 + _32*matrix._24 + _33*matrix._34 + _34*matrix._44,
_41*matrix._11 + _42*matrix._21 + _43*matrix._31 + _44*matrix._41,
_41*matrix._12 + _42*matrix._22 + _43*matrix._32 + _44*matrix._42,
_41*matrix._13 + _42*matrix._23 + _43*matrix._33 + _44*matrix._43,
_41*matrix._14 + _42*matrix._24 + _43*matrix._34 + _44*matrix._44
);
}
//Matrix multiplication/assignment
CMatrix3D& CMatrix3D::operator*=(const CMatrix3D& matrix)
{
Concatenate(matrix);
return *this;
}
//Matrix scaling
CMatrix3D CMatrix3D::operator*(float f) const
{
CMatrix3D tmp;
for (int i=0;i<16;i++) {
tmp._data[i]=_data[i]*f;
}
return tmp;
}
//Matrix scaling/assignment
CMatrix3D& CMatrix3D::operator*=(float f)
{
for (int i=0;i<16;i++) {
_data[i]*=f;
}
return *this;
}
//Matrix addition
CMatrix3D CMatrix3D::operator+(const CMatrix3D& m) const
{
CMatrix3D tmp;
for (int i=0;i<16;i++) {
tmp._data[i]=_data[i]+m._data[i];
}
return tmp;
}
//Matrix addition/assignment
CMatrix3D& CMatrix3D::operator+=(const CMatrix3D& m)
{
for (int i=0;i<16;i++) {
_data[i]+=m._data[i];
}
return *this;
}
bool CMatrix3D::operator==(const CMatrix3D &matrix) const
{
for (int i = 0; i < 16; ++i)
if (matrix._data[i] != _data[i])
return false;
return true;
}
//Sets the identity matrix
void CMatrix3D::SetIdentity ()
{
@ -275,11 +172,6 @@ void CMatrix3D::Translate(const CVector3D &vector)
_34 += vector.Z;
}
void CMatrix3D::Concatenate(const CMatrix3D& m)
{
(*this)=m*(*this);
}
CVector3D CMatrix3D::GetTranslation() const
{
return CVector3D(_14, _24, _34);
@ -343,22 +235,6 @@ CVector3D CMatrix3D::GetIn() const
return CVector3D(_13, _23, _33);
}
//Transform a vector by this matrix
CVector4D CMatrix3D::Transform(const CVector4D &vector) const
{
CVector4D result;
Transform(vector,result);
return result;
}
void CMatrix3D::Transform(const CVector4D& vector,CVector4D& result) const
{
result[0] = _11*vector[0] + _12*vector[1] + _13*vector[2] + _14*vector[3];
result[1] = _21*vector[0] + _22*vector[1] + _23*vector[2] + _24*vector[3];
result[2] = _31*vector[0] + _32*vector[1] + _33*vector[2] + _34*vector[3];
result[3] = _41*vector[0] + _42*vector[1] + _43*vector[2] + _44*vector[3];
}
///////////////////////////////////////////////////////////////////////////////
// RotateTransposed: rotate a vector by the transpose of this matrix
CVector3D CMatrix3D::RotateTransposed(const CVector3D& vector) const

View File

@ -24,8 +24,8 @@
#define INCLUDED_MATRIX3D
#include "maths/Vector3D.h"
#include "maths/Vector4D.h"
class CVector4D;
class CQuaternion;
/////////////////////////////////////////////////////////////////////////
@ -49,10 +49,29 @@ public:
public:
// constructors
CMatrix3D();
CMatrix3D(float a11,float a12,float a13,float a14,float a21,float a22,float a23,float a24,
float a31,float a32,float a33,float a34,float a41,float a42,float a43,float a44);
CMatrix3D(float data[]);
CMatrix3D ()
{
}
CMatrix3D(
float a11, float a12, float a13, float a14,
float a21, float a22, float a23, float a24,
float a31, float a32, float a33, float a34,
float a41, float a42, float a43, float a44) :
_11(a11), _12(a12), _13(a13), _14(a14),
_21(a21), _22(a22), _23(a23), _24(a24),
_31(a31), _32(a32), _33(a33), _34(a34),
_41(a41), _42(a42), _43(a43), _44(a44)
{
}
CMatrix3D(float data[]) :
_11(data[0]), _21(data[1]), _31(data[2]), _41(data[3]),
_12(data[4]), _22(data[5]), _32(data[6]), _42(data[7]),
_13(data[8]), _23(data[9]), _33(data[10]), _43(data[11]),
_14(data[12]), _24(data[13]), _34(data[14]), _44(data[15])
{
}
// accessors to individual elements of matrix
float& operator()(int col,int row) {
@ -63,20 +82,78 @@ public:
}
// matrix multiplication
CMatrix3D operator*(const CMatrix3D &matrix) const;
CMatrix3D operator*(const CMatrix3D &matrix) const
{
return CMatrix3D(
_11*matrix._11 + _12*matrix._21 + _13*matrix._31 + _14*matrix._41,
_11*matrix._12 + _12*matrix._22 + _13*matrix._32 + _14*matrix._42,
_11*matrix._13 + _12*matrix._23 + _13*matrix._33 + _14*matrix._43,
_11*matrix._14 + _12*matrix._24 + _13*matrix._34 + _14*matrix._44,
_21*matrix._11 + _22*matrix._21 + _23*matrix._31 + _24*matrix._41,
_21*matrix._12 + _22*matrix._22 + _23*matrix._32 + _24*matrix._42,
_21*matrix._13 + _22*matrix._23 + _23*matrix._33 + _24*matrix._43,
_21*matrix._14 + _22*matrix._24 + _23*matrix._34 + _24*matrix._44,
_31*matrix._11 + _32*matrix._21 + _33*matrix._31 + _34*matrix._41,
_31*matrix._12 + _32*matrix._22 + _33*matrix._32 + _34*matrix._42,
_31*matrix._13 + _32*matrix._23 + _33*matrix._33 + _34*matrix._43,
_31*matrix._14 + _32*matrix._24 + _33*matrix._34 + _34*matrix._44,
_41*matrix._11 + _42*matrix._21 + _43*matrix._31 + _44*matrix._41,
_41*matrix._12 + _42*matrix._22 + _43*matrix._32 + _44*matrix._42,
_41*matrix._13 + _42*matrix._23 + _43*matrix._33 + _44*matrix._43,
_41*matrix._14 + _42*matrix._24 + _43*matrix._34 + _44*matrix._44
);
}
// matrix multiplication/assignment
CMatrix3D& operator*=(const CMatrix3D &matrix);
CMatrix3D& operator*=(const CMatrix3D &matrix)
{
Concatenate(matrix);
return *this;
}
// matrix scaling
CMatrix3D operator*(float f) const;
// matrix scaling/assignment
CMatrix3D& operator*=(float f);
CMatrix3D operator*(float f) const
{
return CMatrix3D(
_11*f, _12*f, _13*f, _14*f,
_21*f, _22*f, _23*f, _24*f,
_31*f, _32*f, _33*f, _34*f,
_41*f, _42*f, _43*f, _44*f
);
}
// matrix addition
CMatrix3D operator+(const CMatrix3D &matrix) const;
CMatrix3D operator+(const CMatrix3D &m) const
{
return CMatrix3D(
_11+m._11, _12+m._12, _13+m._13, _14+m._14,
_21+m._21, _22+m._22, _23+m._23, _24+m._24,
_31+m._31, _32+m._32, _33+m._33, _34+m._34,
_41+m._41, _42+m._42, _43+m._43, _44+m._44
);
}
// matrix addition/assignment
CMatrix3D& operator+=(const CMatrix3D &matrix);
CMatrix3D& operator+=(const CMatrix3D &m)
{
_11 += m._11; _21 += m._21; _31 += m._31; _41 += m._41;
_12 += m._12; _22 += m._22; _32 += m._32; _42 += m._42;
_13 += m._13; _23 += m._23; _33 += m._33; _43 += m._43;
_14 += m._14; _24 += m._24; _34 += m._34; _44 += m._44;
return *this;
}
// equality
bool operator==(const CMatrix3D &matrix) const;
bool operator==(const CMatrix3D &m) const
{
return _11 == m._11 && _21 == m._21 && _31 == m._31 && _41 == m._41 &&
_12 == m._12 && _22 == m._22 && _32 == m._32 && _42 == m._42 &&
_13 == m._13 && _23 == m._23 && _33 == m._33 && _43 == m._43 &&
_14 == m._14 && _24 == m._24 && _34 == m._34 && _44 == m._44;
}
// set this matrix to the identity matrix
void SetIdentity();
@ -84,7 +161,28 @@ public:
void SetZero();
// concatenate arbitrary matrix onto this matrix
void Concatenate(const CMatrix3D& m);
void Concatenate(const CMatrix3D& m)
{
(*this) = m * (*this);
}
// blend matrix using only 4x3 subset
void Blend(const CMatrix3D& m, float f)
{
_11 = m._11*f; _21 = m._21*f; _31 = m._31*f;
_12 = m._12*f; _22 = m._22*f; _32 = m._32*f;
_13 = m._13*f; _23 = m._23*f; _33 = m._33*f;
_14 = m._14*f; _24 = m._24*f; _34 = m._34*f;
}
// blend matrix using only 4x3 and add onto existing blend
void AddBlend(const CMatrix3D& m, float f)
{
_11 += m._11*f; _21 += m._21*f; _31 += m._31*f;
_12 += m._12*f; _22 += m._22*f; _32 += m._32*f;
_13 += m._13*f; _23 += m._23*f; _33 += m._33*f;
_14 += m._14*f; _24 += m._24*f; _34 += m._34*f;
}
// set this matrix to a rotation matrix for a rotation about X axis of given angle
void SetXRotation(float angle);
@ -144,7 +242,7 @@ public:
float GetYRotation() const;
// transform a 3D vector by this matrix
CVector3D Transform (const CVector3D &vector) const
CVector3D Transform(const CVector3D &vector) const
{
CVector3D result;
Transform(vector, result);
@ -158,6 +256,22 @@ public:
result.Z = _31*vector.X + _32*vector.Y + _33*vector.Z + _34;
}
// transform a 4D vector by this matrix
CVector4D Transform(const CVector4D &vector) const
{
CVector4D result;
Transform(vector, result);
return result;
}
void Transform(const CVector4D& vector, CVector4D& result) const
{
result[0] = _11*vector[0] + _12*vector[1] + _13*vector[2] + _14*vector[3];
result[1] = _21*vector[0] + _22*vector[1] + _23*vector[2] + _24*vector[3];
result[2] = _31*vector[0] + _32*vector[1] + _33*vector[2] + _34*vector[3];
result[3] = _41*vector[0] + _42*vector[1] + _43*vector[2] + _44*vector[3];
}
// rotate a vector by this matrix
CVector3D Rotate(const CVector3D& vector) const
{
@ -173,10 +287,6 @@ public:
result.Z = _31*vector.X + _32*vector.Y + _33*vector.Z;
}
// transform a 4D vector by this matrix
void Transform(const CVector4D& vector,CVector4D& result) const;
CVector4D Transform(const CVector4D& vector) const;
// rotate a vector by the transpose of this matrix
void RotateTransposed(const CVector3D& vector,CVector3D& result) const;
CVector3D RotateTransposed(const CVector3D& vector) const;

View File

@ -26,6 +26,7 @@
#define INCLUDED_PLANE
#include "Vector3D.h"
#include "Vector4D.h"
enum PLANESIDE
{
@ -38,6 +39,7 @@ class CPlane
{
public:
CPlane ();
CPlane (const CVector4D& coeffs) : m_Norm(coeffs[0], coeffs[1], coeffs[2]), m_Dist(coeffs[3]) { }
//sets the plane equation from 3 points on that plane
void Set (const CVector3D &p1, const CVector3D &p2, const CVector3D &p3);

View File

@ -34,6 +34,7 @@ bool g_NoGLAutoMipmap = false;
bool g_NoGLVBO = false;
bool g_Shadows = false;
bool g_ShadowPCF = false;
bool g_FancyWater = false;
float g_LodBias = 0.0f;
@ -72,6 +73,7 @@ static void LoadGlobals()
CFG_GET_USER_VAL("noautomipmap", Bool, g_NoGLAutoMipmap);
CFG_GET_USER_VAL("novbo", Bool, g_NoGLVBO);
CFG_GET_USER_VAL("shadows", Bool, g_Shadows);
CFG_GET_USER_VAL("shadowpcf", Bool, g_ShadowPCF);
CFG_GET_USER_VAL("fancywater", Bool, g_FancyWater);
CFG_GET_USER_VAL("renderpath", String, g_RenderPath);

View File

@ -45,6 +45,8 @@ extern bool g_NoGLVBO;
extern bool g_Shadows;
// flag to switch on reflective/refractive water
extern bool g_FancyWater;
// flag to switch on shadow PCF
extern bool g_ShadowPCF;
extern float g_LodBias;
extern float g_Gamma;

View File

@ -549,6 +549,7 @@ static void InitRenderer()
g_Renderer.SetOptionBool(CRenderer::OPT_FANCYWATER,g_FancyWater);
g_Renderer.SetRenderPath(CRenderer::GetRenderPathByName(g_RenderPath));
g_Renderer.SetOptionFloat(CRenderer::OPT_LODBIAS, g_LodBias);
g_Renderer.SetOptionBool(CRenderer::OPT_SHADOWPCF, g_ShadowPCF);
// create terrain related stuff
new CTerrainTextureManager;

View File

@ -115,6 +115,12 @@ void SetDisableShadows(void* UNUSED(cbdata), bool disabled)
g_Shadows = !disabled;
}
void SetDisableShadowPCF(void* UNUSED(cbdata), bool disabled)
{
if (!IsOverridden("shadowpcf"))
g_ShadowPCF = !disabled;
}
void SetDisableFancyWater(void* UNUSED(cbdata), bool disabled)
{
if (!IsOverridden("fancywater"))
@ -136,6 +142,7 @@ void RunHardwareDetection()
scriptInterface.RegisterFunction<void, bool, &SetDisableAudio>("SetDisableAudio");
scriptInterface.RegisterFunction<void, bool, &SetDisableS3TC>("SetDisableS3TC");
scriptInterface.RegisterFunction<void, bool, &SetDisableShadows>("SetDisableShadows");
scriptInterface.RegisterFunction<void, bool, &SetDisableShadowPCF>("SetDisableShadowPCF");
scriptInterface.RegisterFunction<void, bool, &SetDisableFancyWater>("SetDisableFancyWater");
scriptInterface.RegisterFunction<void, std::string, &SetRenderPath>("SetRenderPath");

View File

@ -34,6 +34,8 @@ public:
void Render(const CShaderProgramPtr& shader);
CModelDecal* GetDecal() { return m_Decal; }
private:
void BuildArrays();

View File

@ -84,7 +84,7 @@ void ModelRenderer::BuildPositionAndNormals(
return;
}
CModelDef::SkinPointsAndNormals(numVertices, Position, Normal, vertices, model->GetAnimatedBoneMatrices(), model->GetInverseBindBoneMatrices());
CModelDef::SkinPointsAndNormals(numVertices, Position, Normal, vertices, mdef->GetBlendIndices(), model->GetAnimatedBoneMatrices());
}
else
@ -253,6 +253,7 @@ struct BatchModelRendererInternals
}
void RenderAllModels(const RenderModifierPtr& modifier, int filterflags, int pass, int streamflags);
void FilterAllModels(CModelFilter& filter, int passed, int filterflags);
};
BMRModelData::~BMRModelData()
@ -442,7 +443,7 @@ void BatchModelRendererInternals::RenderAllModels(
ENSURE(bmrdata->GetKey() == this);
if (filterflags && !(model->GetFlags()&filterflags))
if (filterflags && !(model->GetFlags() & filterflags))
continue;
modifier->PrepareModel(pass, model);
@ -452,4 +453,33 @@ void BatchModelRendererInternals::RenderAllModels(
}
}
void BatchModelRenderer::Filter(CModelFilter& filter, int passed, int flags)
{
if (!HaveSubmissions())
return;
m->FilterAllModels(filter, passed, flags);
}
// Recompute filter flags
void BatchModelRendererInternals::FilterAllModels(CModelFilter& filter, int passed, int filterflags)
{
for(BMRModelDefTracker* mdeftracker = submissions; mdeftracker; mdeftracker = mdeftracker->m_Next)
{
for(size_t idx = 0; idx < mdeftracker->m_Slots; ++idx)
{
BMRModelData* bmrdata = mdeftracker->m_ModelSlots[idx];
for(; bmrdata; bmrdata = bmrdata->m_Next)
{
CModel* model = bmrdata->GetModel();
if (filterflags && !(model->GetFlags() & filterflags))
continue;
if (filter.Filter(model))
model->SetFlags(model->GetFlags() | passed);
else
model->SetFlags(model->GetFlags() & ~passed);
}
}
}
}

View File

@ -42,6 +42,12 @@ typedef shared_ptr<ModelVertexRenderer> ModelVertexRendererPtr;
class CModel;
class CModelFilter
{
public:
virtual ~CModelFilter() {}
virtual bool Filter(CModel* model) = 0;
};
/**
* Class CModelRData: Render data that is maintained per CModel.
@ -168,6 +174,17 @@ public:
*/
virtual void Render(const RenderModifierPtr& modifier, int flags) = 0;
/**
* Filter: Filter submitted models, setting the passed flags on any models
* that pass the filter, and clearing them from models that fail.
*
* @param filter Filter to select a subset of models.
* @param passed Flags to be set/cleared.
* @param flags If non-zero, only models that contain @p flags
* have the filter test applied.
*/
virtual void Filter(CModelFilter& filter, int passed, int flags = 0) = 0;
/**
* CopyPositionAndNormals: Copy unanimated object-space vertices and
* normals into the given vertex array.
@ -268,6 +285,7 @@ public:
virtual void EndFrame();
virtual bool HaveSubmissions();
virtual void Render(const RenderModifierPtr& modifier, int flags);
virtual void Filter(CModelFilter& filter, int passed, int flags);
private:
BatchModelRendererInternals* m;

View File

@ -57,7 +57,10 @@ const ssize_t BlendOffsets[9][2] = {
///////////////////////////////////////////////////////////////////
// CPatchRData constructor
CPatchRData::CPatchRData(CPatch* patch) :
m_Patch(patch), m_VBSides(0), m_VBBase(0), m_VBBaseIndices(0), m_VBBlends(0), m_VBBlendIndices(0)
m_Patch(patch), m_VBSides(0),
m_VBBase(0), m_VBBaseIndices(0),
m_VBBlends(0), m_VBBlendIndices(0),
m_VBWater(0), m_VBWaterIndices(0)
{
ENSURE(patch);
Build();
@ -73,6 +76,8 @@ CPatchRData::~CPatchRData()
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);
}
const float uvFactor = 0.125f / sqrt(2.f);
@ -645,6 +650,7 @@ void CPatchRData::Build()
BuildSides();
BuildIndices();
BuildBlends();
BuildWater();
}
void CPatchRData::Update()
@ -657,6 +663,7 @@ void CPatchRData::Update()
BuildSides();
BuildIndices();
BuildBlends();
BuildWater();
m_UpdateFlags=0;
}
@ -1127,3 +1134,166 @@ void CPatchRData::RenderPriorities()
}
}
}
//
// Water build and rendering
//
// Build vertex buffer for water vertices over our patch
void CPatchRData::BuildWater()
{
// 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 = 0;
}
if (m_VBWaterIndices)
{
g_VBMan.Release(m_VBWaterIndices);
m_VBWaterIndices = 0;
}
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<ICmpWaterManager> cmpWaterManager(*g_Game->GetSimulation2(), SYSTEM_ENTITY);
if (cmpWaterManager.null())
return;
// Build data for water
std::vector<SWaterVertex> water_vertex_data;
std::vector<GLushort> water_indices;
u16 water_index_map[PATCH_SIZE+1][PATCH_SIZE+1];
memset(water_index_map, 0xFF, sizeof(water_index_map));
// TODO: This is not (yet) exported via the ICmp interface so... we stick to these values which can be compiled in defaults
WaterManager* WaterMgr = g_Renderer.GetWaterManager();
CPatch* patch = m_Patch;
CTerrain* terrain = patch->m_Parent;
ssize_t x1 = m_Patch->m_X*PATCH_SIZE;
ssize_t z1 = m_Patch->m_Z*PATCH_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 the edge at x is partially underwater
float startTerrainHeight[2] = { terrain->GetVertexGroundLevel(x+x1, z+z1), terrain->GetVertexGroundLevel(x+x1, z+z1 + water_cell_size) };
float startWaterHeight[2] = { cmpWaterManager->GetExactWaterLevel(x+x1, z+z1), cmpWaterManager->GetExactWaterLevel(x+x1, z+z1 + water_cell_size) };
if (startTerrainHeight[0] >= startWaterHeight[0] && startTerrainHeight[1] >= startWaterHeight[1])
continue;
// Move x back one cell (unless at start of patch), then scan rightwards
bool belowWater = true;
ssize_t stripStart;
for (stripStart = x = std::max(x-water_cell_size, (ssize_t)0); x <= PATCH_SIZE; x += water_cell_size)
{
// If this edge is not underwater, and neither is the previous edge
// (i.e. belowWater == false), then stop this strip since we've reached
// a cell that's entirely above water
float terrainHeight[2] = { terrain->GetVertexGroundLevel(x+x1, z+z1), terrain->GetVertexGroundLevel(x+x1, z+z1 + water_cell_size) };
float waterHeight[2] = { cmpWaterManager->GetExactWaterLevel(x+x1, z+z1), cmpWaterManager->GetExactWaterLevel(x+x1, z+z1 + water_cell_size) };
if (terrainHeight[0] >= waterHeight[0] && terrainHeight[1] >= waterHeight[1])
{
if (!belowWater)
break;
belowWater = false;
}
else
belowWater = true;
// Edge (x,z)-(x,z+1) is at least partially underwater, so extend the water plane strip across it
// Compute vertex data for the 2 points on the edge
for (int j = 0; j < 2; j++)
{
// Check if we already computed this vertex from an earlier strip
if (water_index_map[z+j*water_cell_size][x] != 0xFFFF)
continue;
SWaterVertex vertex;
terrain->CalcPosition(x+x1, z+z1 + j*water_cell_size, vertex.m_Position);
float depth = waterHeight[j] - vertex.m_Position.Y;
vertex.m_Position.Y = waterHeight[j];
m_WaterBounds += vertex.m_Position;
// NB: Usually this factor is view dependent, but for performance reasons
// we do not take it into account with basic non-shader based water.
// Average constant Fresnel effect for non-fancy water
float alpha = clamp(depth / WaterMgr->m_WaterFullDepth + WaterMgr->m_WaterAlphaOffset, WaterMgr->m_WaterAlphaOffset, WaterMgr->m_WaterMaxAlpha);
// Split the depth data across 24 bits, so the fancy-water shader can reconstruct
// the depth value while the simple-water can just use the precomputed alpha
float depthInt = floor(depth);
float depthFrac = depth - depthInt;
vertex.m_DepthData = SColor4ub(
u8(clamp(depthInt, 0.0f, 255.0f)),
u8(clamp(-depthInt, 0.0f, 255.0f)),
u8(clamp(depthFrac*255.0f, 0.0f, 255.0f)),
u8(clamp(alpha*255.0f, 0.0f, 255.0f)));
water_index_map[z+j*water_cell_size][x] = water_vertex_data.size();
water_vertex_data.push_back(vertex);
}
// If this was not the first x in the strip, then add a quad
// using the computed vertex data
if (x <= stripStart)
continue;
water_indices.push_back(water_index_map[z + water_cell_size][x - water_cell_size]);
water_indices.push_back(water_index_map[z][x - water_cell_size]);
water_indices.push_back(water_index_map[z][x]);
water_indices.push_back(water_index_map[z + water_cell_size][x]);
}
}
}
// no vertex buffers if no data generated
if (water_indices.size() == 0)
return;
// allocate vertex buffer
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]);
// Construct indices buffer
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]);
}
void CPatchRData::RenderWater()
{
ASSERT(m_UpdateFlags==0);
if (!m_VBWater)
return;
SWaterVertex *base=(SWaterVertex *)m_VBWater->m_Owner->Bind();
// setup data pointers
GLsizei stride = sizeof(SWaterVertex);
glColorPointer(4, GL_UNSIGNED_BYTE, stride, &base[m_VBWater->m_Index].m_DepthData);
glVertexPointer(3, GL_FLOAT, stride, &base[m_VBWater->m_Index].m_Position);
// render
if (!g_Renderer.m_SkipSubmit) {
u8* indexBase = m_VBWaterIndices->m_Owner->Bind();
glDrawElements(GL_QUADS, (GLsizei) m_VBWaterIndices->m_Count,
GL_UNSIGNED_SHORT, indexBase + sizeof(u16)*(m_VBWaterIndices->m_Index));
}
// bump stats
g_Renderer.m_Stats.m_DrawCalls++;
g_Renderer.m_Stats.m_WaterTris += m_VBWaterIndices->m_Count / 2;
CVertexBuffer::Unbind();
}

View File

@ -41,12 +41,16 @@ public:
void RenderSides();
void RenderPriorities();
void RenderWater();
static void RenderBases(const std::vector<CPatchRData*>& patches);
static void RenderBlends(const std::vector<CPatchRData*>& patches);
static void RenderStreams(const std::vector<CPatchRData*>& patches, int streamflags);
CPatch* GetPatch() { return m_Patch; }
const CBound& GetWaterBounds() const { return m_WaterBounds; }
private:
friend struct SBlendStackItem;
@ -93,6 +97,17 @@ private:
};
cassert(sizeof(SBlendVertex) == 32);
// Mixed Fancy/Simple water vertex description data structure
struct SWaterVertex {
// vertex position
CVector3D m_Position;
// (p,q,r, a) where
// p*255 + q*-255 + r = depth of water
// a = depth-dependent alpha
SColor4ub m_DepthData;
};
cassert(sizeof(SWaterVertex) == 16);
// build this renderdata object
void Build();
@ -128,6 +143,24 @@ private:
// splats used in blend pass
std::vector<SSplat> m_BlendSplats;
// boundary of water in this patch
CBound m_WaterBounds;
// Water vertex buffer
CVertexBuffer::VBChunk* m_VBWater;
// Water indices buffer
CVertexBuffer::VBChunk* m_VBWaterIndices;
// 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

View File

@ -236,6 +236,10 @@ int ShaderRenderModifier::BeginPass(int pass)
{
shader->BindTexture("shadowTex", GetShadowMap()->GetTexture());
shader->Uniform("shadowTransform", GetShadowMap()->GetTextureMatrix());
const float* offsets = GetShadowMap()->GetFilterOffsets();
shader->Uniform("shadowOffsets1", offsets[0], offsets[1], offsets[2], offsets[3]);
shader->Uniform("shadowOffsets2", offsets[4], offsets[5], offsets[6], offsets[7]);
}
if (GetLightEnv())

View File

@ -104,6 +104,7 @@ private:
enum {
Row_DrawCalls = 0,
Row_TerrainTris,
Row_WaterTris,
Row_ModelTris,
Row_BlendSplats,
Row_Particles,
@ -162,6 +163,12 @@ CStr CRendererStatsTable::GetCellText(size_t row, size_t col)
sprintf_s(buf, sizeof(buf), "%lu", (unsigned long)Stats.m_TerrainTris);
return buf;
case Row_WaterTris:
if (col == 0)
return "# water tris";
sprintf_s(buf, sizeof(buf), "%lu", (unsigned long)Stats.m_WaterTris);
return buf;
case Row_ModelTris:
if (col == 0)
return "# model tris";
@ -295,11 +302,15 @@ public:
RenderModifierPtr ModSolidPlayer;
RenderModifierPtr ModSolidPlayerInstancing;
RenderModifierPtr ModTransparent;
RenderModifierPtr ModTransparentOpaque;
RenderModifierPtr ModTransparentBlend;
// Palette of available RenderModifiers
RenderModifierPtr ModPlainUnlit;
RenderModifierPtr ModPlayerUnlit;
RenderModifierPtr ModTransparentUnlit;
RenderModifierPtr ModTransparentOpaqueUnlit;
RenderModifierPtr ModTransparentBlendUnlit;
RenderModifierPtr ModShaderSolidColor;
RenderModifierPtr ModShaderSolidColorInstancing;
@ -311,6 +322,8 @@ public:
LitRenderModifierPtr ModShaderPlayer;
LitRenderModifierPtr ModShaderPlayerInstancing;
LitRenderModifierPtr ModShaderTransparent;
LitRenderModifierPtr ModShaderTransparentOpaque;
LitRenderModifierPtr ModShaderTransparentBlend;
RenderModifierPtr ModShaderTransparentShadow;
} Model;
@ -381,6 +394,20 @@ public:
if (Model.Player != Model.PlayerInstancing)
Model.PlayerInstancing->Render(modPlayerInstancing, flags);
}
/**
* Filters all non-transparent models with the given modifiers.
*/
void FilterModels(CModelFilter& filter, int passed, int flags = 0)
{
Model.Normal->Filter(filter, passed, flags);
if (Model.Normal != Model.NormalInstancing)
Model.NormalInstancing->Filter(filter, passed, flags);
Model.Player->Filter(filter, passed, flags);
if (Model.Player != Model.PlayerInstancing)
Model.PlayerInstancing->Filter(filter, passed, flags);
}
};
///////////////////////////////////////////////////////////////////////////////////
@ -412,6 +439,7 @@ CRenderer::CRenderer()
m_Options.m_Shadows = false;
m_Options.m_ShadowAlphaFix = true;
m_Options.m_ARBProgramShadow = true;
m_Options.m_ShadowPCF = false;
m_ShadowZBias = 0.02f;
m_ShadowMapSize = 0;
@ -514,6 +542,8 @@ void CRenderer::ReloadShaders()
defBasic["USE_SHADOW"] = "1";
if (m_Caps.m_ARBProgramShadow && m_Options.m_ARBProgramShadow)
defBasic["USE_FP_SHADOW"] = "1";
if (m_Options.m_ShadowPCF)
defBasic["USE_SHADOW_PCF"] = "1";
}
if (m_LightEnv)
@ -526,15 +556,20 @@ void CRenderer::ReloadShaders()
defTransparent["USE_TRANSPARENT"] = "1";
// TODO: it'd be nicer to load this technique from an XML file or something
CShaderPass passTransparent0(m->shaderManager.LoadProgram("solid_tex", defNull));
passTransparent0.AlphaFunc(GL_GREATER, 0.975f);
passTransparent0.ColorMask(0, 0, 0, 0);
CShaderPass passTransparent1(m->shaderManager.LoadProgram("model_common", defTransparent));
passTransparent1.AlphaFunc(GL_GREATER, 0.0f);
passTransparent1.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
passTransparent1.DepthMask(0);
CShaderTechnique techTransparent(passTransparent0);
techTransparent.AddPass(passTransparent1);
CShaderPass passTransparentOpaque(m->shaderManager.LoadProgram("model_common", defTransparent));
passTransparentOpaque.AlphaFunc(GL_GREATER, 0.9375f);
passTransparentOpaque.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
CShaderTechnique techTransparentOpaque(passTransparentOpaque);
CShaderPass passTransparentBlend(m->shaderManager.LoadProgram("model_common", defTransparent));
passTransparentBlend.AlphaFunc(GL_GREATER, 0.0f);
passTransparentBlend.DepthFunc(GL_LESS);
passTransparentBlend.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
passTransparentBlend.DepthMask(0);
CShaderTechnique techTransparentBlend(passTransparentBlend);
CShaderTechnique techTransparent(passTransparentOpaque);
techTransparent.AddPass(passTransparentBlend);
CShaderPass passTransparentShadow(m->shaderManager.LoadProgram("solid_tex", defBasic));
passTransparentShadow.AlphaFunc(GL_GREATER, 0.4f);
@ -565,6 +600,10 @@ void CRenderer::ReloadShaders()
m->Model.ModShaderTransparent = LitRenderModifierPtr(new ShaderRenderModifier(
techTransparent));
m->Model.ModShaderTransparentOpaque = LitRenderModifierPtr(new ShaderRenderModifier(
techTransparentOpaque));
m->Model.ModShaderTransparentBlend = LitRenderModifierPtr(new ShaderRenderModifier(
techTransparentBlend));
m->Model.ModShaderTransparentShadow = LitRenderModifierPtr(new ShaderRenderModifier(
techTransparentShadow));
@ -603,6 +642,8 @@ bool CRenderer::Open(int width, int height)
m->Model.ModSolidColor = RenderModifierPtr(new SolidColorRenderModifier);
m->Model.ModSolidPlayerColor = RenderModifierPtr(new SolidPlayerColorRender);
m->Model.ModTransparentUnlit = RenderModifierPtr(new TransparentRenderModifier);
m->Model.ModTransparentOpaqueUnlit = RenderModifierPtr(new TransparentOpaqueRenderModifier);
m->Model.ModTransparentBlendUnlit = RenderModifierPtr(new TransparentBlendRenderModifier);
m->Model.ModTransparentDepthShadow = RenderModifierPtr(new TransparentDepthShadowModifier);
// Dimensions
@ -659,6 +700,9 @@ void CRenderer::SetOptionBool(enum Option opt,bool value)
case OPT_FANCYWATER:
m_Options.m_FancyWater=value;
break;
case OPT_SHADOWPCF:
m_Options.m_ShadowPCF=value;
break;
default:
debug_warn(L"CRenderer::SetOptionBool: unknown option");
break;
@ -676,6 +720,8 @@ bool CRenderer::GetOptionBool(enum Option opt) const
return m_Options.m_Shadows;
case OPT_FANCYWATER:
return m_Options.m_FancyWater;
case OPT_SHADOWPCF:
return m_Options.m_ShadowPCF;
default:
debug_warn(L"CRenderer::GetOptionBool: unknown option");
break;
@ -812,6 +858,12 @@ void CRenderer::BeginFrame()
m->Model.ModShaderTransparent->SetShadowMap(m->shadow);
m->Model.ModShaderTransparent->SetLightEnv(m_LightEnv);
m->Model.ModShaderTransparentOpaque->SetShadowMap(m->shadow);
m->Model.ModShaderTransparentOpaque->SetLightEnv(m_LightEnv);
m->Model.ModShaderTransparentBlend->SetShadowMap(m->shadow);
m->Model.ModShaderTransparentBlend->SetLightEnv(m_LightEnv);
m->Model.ModNormal = m->Model.ModShaderNormal;
m->Model.ModNormalInstancing = m->Model.ModShaderNormalInstancing;
m->Model.ModPlayer = m->Model.ModShaderPlayer;
@ -821,6 +873,8 @@ void CRenderer::BeginFrame()
m->Model.ModSolidPlayer = m->Model.ModShaderSolidPlayerColor;
m->Model.ModSolidPlayerInstancing = m->Model.ModShaderSolidPlayerColorInstancing;
m->Model.ModTransparent = m->Model.ModShaderTransparent;
m->Model.ModTransparentOpaque = m->Model.ModShaderTransparentOpaque;
m->Model.ModTransparentBlend = m->Model.ModShaderTransparentBlend;
m->Model.Normal = m->Model.pal_NormalShader;
m->Model.NormalInstancing = m->Model.pal_NormalInstancingShader;
@ -837,6 +891,8 @@ void CRenderer::BeginFrame()
m->Model.ModPlayer = m->Model.ModPlayerUnlit;
m->Model.ModPlayerInstancing = m->Model.ModPlayerUnlit;
m->Model.ModTransparent = m->Model.ModTransparentUnlit;
m->Model.ModTransparentOpaque = m->Model.ModTransparentOpaqueUnlit;
m->Model.ModTransparentBlend = m->Model.ModTransparentBlendUnlit;
m->Model.NormalInstancing = m->Model.pal_NormalFF;
m->Model.Normal = m->Model.pal_NormalFF;
@ -919,10 +975,19 @@ void CRenderer::RenderShadowMap()
m->shadow->EndRender();
}
void CRenderer::RenderPatches()
void CRenderer::RenderPatches(const CFrustum* frustum)
{
PROFILE("render patches");
bool filtered = false;
if (frustum)
{
if (!m->terrainRenderer->CullPatches(frustum))
return;
filtered = true;
}
// switch on wireframe if we need it
if (m_TerrainRenderMode == WIREFRAME)
{
@ -931,9 +996,9 @@ void CRenderer::RenderPatches()
// render all the patches, including blend pass
if (GetRenderPath() == RP_SHADER)
m->terrainRenderer->RenderTerrainShader((m_Caps.m_Shadows && m_Options.m_Shadows) ? m->shadow : 0);
m->terrainRenderer->RenderTerrainShader((m_Caps.m_Shadows && m_Options.m_Shadows) ? m->shadow : 0, filtered);
else
m->terrainRenderer->RenderTerrain();
m->terrainRenderer->RenderTerrain(filtered);
if (m_TerrainRenderMode == WIREFRAME)
@ -953,14 +1018,14 @@ void CRenderer::RenderPatches()
glLineWidth(2.0f);
// render tiles edges
m->terrainRenderer->RenderPatches();
m->terrainRenderer->RenderPatches(filtered);
// set color for outline
glColor3f(0, 0, 1);
glLineWidth(4.0f);
// render outline of each patch
m->terrainRenderer->RenderOutlines();
m->terrainRenderer->RenderOutlines(filtered);
// .. and restore the renderstates
glLineWidth(1.0f);
@ -968,17 +1033,39 @@ void CRenderer::RenderPatches()
}
}
void CRenderer::RenderModels()
class CModelCuller : public CModelFilter
{
public:
CModelCuller(const CFrustum& frustum) : m_Frustum(frustum) { }
bool Filter(CModel *model)
{
return m_Frustum.IsBoxVisible(CVector3D(0, 0, 0), model->GetBoundsRec());
}
private:
const CFrustum& m_Frustum;
};
void CRenderer::RenderModels(const CFrustum* frustum)
{
PROFILE("render models");
int flags = 0;
if (frustum)
{
flags = MODELFLAG_FILTERED;
CModelCuller culler(*frustum);
m->FilterModels(culler, flags);
}
if (m_ModelRenderMode == WIREFRAME)
{
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
}
m->CallModelRenderers(m->Model.ModNormal, m->Model.ModNormalInstancing,
m->Model.ModPlayer, m->Model.ModPlayerInstancing, 0);
m->Model.ModPlayer, m->Model.ModPlayerInstancing, flags);
if (m_ModelRenderMode == WIREFRAME)
{
@ -991,23 +1078,36 @@ void CRenderer::RenderModels()
glColor3f(1.0f, 1.0f, 0.0f);
m->CallModelRenderers(m->Model.ModSolid, m->Model.ModSolidInstancing,
m->Model.ModSolid, m->Model.ModSolidInstancing, 0);
m->Model.ModSolid, m->Model.ModSolidInstancing, flags);
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
}
}
void CRenderer::RenderTransparentModels()
void CRenderer::RenderTransparentModels(ETransparentMode transparentMode, const CFrustum* frustum)
{
PROFILE("render transparent models");
int flags = 0;
if (frustum)
{
flags = MODELFLAG_FILTERED;
CModelCuller culler(*frustum);
m->Model.Transp->Filter(culler, flags);
}
// switch on wireframe if we need it
if (m_ModelRenderMode == WIREFRAME)
{
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
}
m->Model.Transp->Render(m->Model.ModTransparent, 0);
if (transparentMode == TRANSPARENT_OPAQUE)
m->Model.Transp->Render(m->Model.ModTransparentOpaque, flags);
else if (transparentMode == TRANSPARENT_BLEND)
m->Model.Transp->Render(m->Model.ModTransparentBlend, flags);
else
m->Model.Transp->Render(m->Model.ModTransparent, flags);
if (m_ModelRenderMode == WIREFRAME)
{
@ -1020,7 +1120,7 @@ void CRenderer::RenderTransparentModels()
glDisable(GL_TEXTURE_2D);
glColor3f(1.0f, 0.0f, 0.0f);
m->Model.Transp->Render(m->Model.ModSolid, 0);
m->Model.Transp->Render(m->Model.ModSolid, flags);
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
}
@ -1034,10 +1134,10 @@ CMatrix3D CRenderer::GetModelViewProjectionMatrix()
CMatrix3D proj;
CMatrix3D view;
glGetFloatv( GL_PROJECTION_MATRIX, &proj._11 );
glGetFloatv( GL_MODELVIEW_MATRIX, &view._11 );
glGetFloatv(GL_PROJECTION_MATRIX, &proj._11);
glGetFloatv(GL_MODELVIEW_MATRIX, &view._11);
return( proj * view );
return proj*view;
}
@ -1045,74 +1145,49 @@ CMatrix3D CRenderer::GetModelViewProjectionMatrix()
///////////////////////////////////////////////////////////////////////////////////////////////////
// SetObliqueFrustumClipping: change the near plane to the given clip plane (in world space)
// Based on code from Game Programming Gems 5, from http://www.terathon.com/code/oblique.html
// - cp is a clip plane in camera space (cp.Dot(v) = 0 for any vector v on the plane)
// - sign is 1 or -1, to specify the side to clip on
void CRenderer::SetObliqueFrustumClipping(const CVector4D& cp, int sign)
// - worldPlane is a clip plane in world space (worldPlane.Dot(v) >= 0 for any vector v passing the clipping test)
void CRenderer::SetObliqueFrustumClipping(const CVector4D& worldPlane)
{
float matrix[16];
CVector4D q;
float matrix[16];
CVector4D q;
// First, we'll convert the given clip plane to camera space, then we'll
// Get the view matrix and normal matrix (top 3x3 part of view matrix)
CMatrix3D viewMatrix;
m_ViewCamera.m_Orientation.GetInverse(viewMatrix);
CMatrix3D normalMatrix = viewMatrix;
normalMatrix._14 = 0;
normalMatrix._24 = 0;
normalMatrix._34 = 0;
normalMatrix._44 = 1;
normalMatrix._41 = 0;
normalMatrix._42 = 0;
normalMatrix._43 = 0;
CMatrix3D normalMatrix = m_ViewCamera.m_Orientation.GetTranspose();
CVector4D camPlane = normalMatrix.Transform(worldPlane);
// Convert the normal to camera space
CVector4D planeNormal(cp.m_X, cp.m_Y, cp.m_Z, 0);
planeNormal = normalMatrix.Transform(planeNormal);
planeNormal.Normalize();
// Grab the current projection matrix from OpenGL
glGetFloatv(GL_PROJECTION_MATRIX, matrix);
// Find a point on the plane: we'll take the normal times -D
float oldD = cp.m_W;
CVector4D pointOnPlane(-oldD * cp.m_X, -oldD * cp.m_Y, -oldD * cp.m_Z, 1);
pointOnPlane = viewMatrix.Transform(pointOnPlane);
float newD = -pointOnPlane.Dot(planeNormal);
// Now create a clip plane from the new normal and new D
CVector4D camPlane = planeNormal;
camPlane.m_W = newD;
// Grab the current projection matrix from OpenGL
glGetFloatv(GL_PROJECTION_MATRIX, matrix);
// Calculate the clip-space corner point opposite the clipping plane
// as (sgn(camPlane.x), sgn(camPlane.y), 1, 1) and
// transform it into camera space by multiplying it
// by the inverse of the projection matrix
// Calculate the clip-space corner point opposite the clipping plane
// as (sgn(camPlane.x), sgn(camPlane.y), 1, 1) and
// transform it into camera space by multiplying it
// by the inverse of the projection matrix
q.m_X = (sgn(camPlane.m_X) + matrix[8]) / matrix[0];
q.m_Y = (sgn(camPlane.m_Y) + matrix[9]) / matrix[5];
q.m_Z = -1.0f;
q.m_W = (1.0f + matrix[10]) / matrix[14];
// Calculate the scaled plane vector
CVector4D c = camPlane * (sign * 2.0f / camPlane.Dot(q));
// Replace the third row of the projection matrix
matrix[2] = c.m_X;
matrix[6] = c.m_Y;
matrix[10] = c.m_Z + 1.0f;
matrix[14] = c.m_W;
q.m_X = (sgn(camPlane.m_X) - matrix[8]/matrix[11]) / matrix[0];
q.m_Y = (sgn(camPlane.m_Y) - matrix[9]/matrix[11]) / matrix[5];
q.m_Z = 1.0f/matrix[11];
q.m_W = (1.0f - matrix[10]/matrix[11]) / matrix[14];
// Load it back into OpenGL
glMatrixMode(GL_PROJECTION);
glLoadMatrixf(matrix);
// Calculate the scaled plane vector
CVector4D c = camPlane * (2.0f * matrix[11] / camPlane.Dot(q));
// Replace the third row of the projection matrix
matrix[2] = c.m_X;
matrix[6] = c.m_Y;
matrix[10] = c.m_Z - matrix[11];
matrix[14] = c.m_W;
// Load it back into OpenGL
glMatrixMode(GL_PROJECTION);
glLoadMatrixf(matrix);
glMatrixMode(GL_MODELVIEW);
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// RenderReflections: render the water reflections to the reflection texture
void CRenderer::RenderReflections()
SScreenRect CRenderer::RenderReflections(const CBound& scissor)
{
PROFILE("render reflections");
@ -1126,9 +1201,11 @@ void CRenderer::RenderReflections()
// water texture, stretch the image according to our aspect ratio so it covers
// the whole screen despite being rendered into a square, and cover slightly more
// of the view so we can see wavy reflections of slightly off-screen objects.
m_ViewCamera.m_Orientation.Translate(0, -wm.m_WaterHeight, 0);
m_ViewCamera.m_Orientation.Scale(1, -1, 1);
m_ViewCamera.m_Orientation.Translate(0, wm.m_WaterHeight, 0);
m_ViewCamera.m_Orientation.Translate(0, 2*wm.m_WaterHeight, 0);
m_ViewCamera.UpdateFrustum(scissor);
m_ViewCamera.ClipFrustum(CVector4D(0, 1, 0, -wm.m_WaterHeight));
SViewPort vp;
vp.m_Height = wm.m_ReflectionTextureSize;
vp.m_Width = wm.m_ReflectionTextureSize;
@ -1143,52 +1220,61 @@ void CRenderer::RenderReflections()
m->SetOpenGLCamera(m_ViewCamera);
CVector4D camPlane(0, 1, 0, -wm.m_WaterHeight);
SetObliqueFrustumClipping(camPlane, -1);
SetObliqueFrustumClipping(camPlane);
// Save the model-view-projection matrix so the shaders can use it for projective texturing
wm.m_ReflectionMatrix = GetModelViewProjectionMatrix();
// Disable backface culling so trees render properly (it might also be possible to flip
// the culling direction here, but this seems to lead to problems)
glDisable(GL_CULL_FACE);
SScreenRect screenScissor;
screenScissor.x1 = (GLint)floor((scissor[0].X*0.5f+0.5f)*vp.m_Width);
screenScissor.y1 = (GLint)floor((scissor[0].Y*0.5f+0.5f)*vp.m_Height);
screenScissor.x2 = (GLint)ceil((scissor[1].X*0.5f+0.5f)*vp.m_Width);
screenScissor.y2 = (GLint)ceil((scissor[1].Y*0.5f+0.5f)*vp.m_Height);
// Make the depth buffer work backwards; there seems to be some oddness with
// oblique frustum clipping and the "sign" parameter here
glClearDepth(0);
glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
glDepthFunc(GL_GEQUAL);
if (screenScissor.x1 < screenScissor.x2 && screenScissor.y1 < screenScissor.y2)
{
glEnable(GL_SCISSOR_TEST);
glScissor(screenScissor.x1, screenScissor.y1, screenScissor.x2 - screenScissor.x1, screenScissor.y2 - screenScissor.y1);
// Render sky, terrain and models
m->skyManager.RenderSky();
ogl_WarnIfError();
RenderPatches();
ogl_WarnIfError();
RenderModels();
ogl_WarnIfError();
RenderTransparentModels();
ogl_WarnIfError();
glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
// Copy the image to a texture
pglActiveTextureARB(GL_TEXTURE0_ARB);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, wm.m_ReflectionTexture);
glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0,
(GLsizei)wm.m_ReflectionTextureSize, (GLsizei)wm.m_ReflectionTextureSize);
glFrontFace(GL_CW);
//Reset old camera and re-enable backface culling
// Render sky, terrain and models
m->skyManager.RenderSky();
ogl_WarnIfError();
RenderPatches(&m_ViewCamera.GetFrustum());
ogl_WarnIfError();
RenderModels(&m_ViewCamera.GetFrustum());
ogl_WarnIfError();
RenderTransparentModels(TRANSPARENT_BLEND, &m_ViewCamera.GetFrustum());
ogl_WarnIfError();
glFrontFace(GL_CCW);
glDisable(GL_SCISSOR_TEST);
// Copy the image to a texture
pglActiveTextureARB(GL_TEXTURE0_ARB);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, wm.m_ReflectionTexture);
glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
screenScissor.x1, screenScissor.y1,
screenScissor.x1, screenScissor.y1,
screenScissor.x2 - screenScissor.x1, screenScissor.y2 - screenScissor.y1);
}
// Reset old camera
m_ViewCamera = normalCamera;
m->SetOpenGLCamera(m_ViewCamera);
glEnable(GL_CULL_FACE);
//glClearDepth(1);
//glClear(GL_DEPTH_BUFFER_BIT);
//glDepthFunc(GL_LEQUAL);
return screenScissor;
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// RenderRefractions: render the water refractions to the refraction texture
void CRenderer::RenderRefractions()
SScreenRect CRenderer::RenderRefractions(const CBound &scissor)
{
PROFILE("render refractions");
@ -1201,6 +1287,9 @@ void CRenderer::RenderRefractions()
// water texture, stretch the image according to our aspect ratio so it covers
// the whole screen despite being rendered into a square, and cover slightly more
// of the view so we can see wavy refractions of slightly off-screen objects.
m_ViewCamera.UpdateFrustum(scissor);
m_ViewCamera.ClipFrustum(CVector4D(0, -1, 0, wm.m_WaterHeight));
SViewPort vp;
vp.m_Height = wm.m_RefractionTextureSize;
vp.m_Width = wm.m_RefractionTextureSize;
@ -1211,43 +1300,53 @@ void CRenderer::RenderRefractions()
CMatrix3D scaleMat;
scaleMat.SetScaling(m_Height/float(std::max(1, m_Width)), 1.0f, 1.0f);
m_ViewCamera.m_ProjMat = scaleMat * m_ViewCamera.m_ProjMat;
m->SetOpenGLCamera(m_ViewCamera);
CVector4D camPlane(0, 1, 0, -wm.m_WaterHeight);
SetObliqueFrustumClipping(camPlane, -1);
CVector4D camPlane(0, -1, 0, wm.m_WaterHeight);
SetObliqueFrustumClipping(camPlane);
// Save the model-view-projection matrix so the shaders can use it for projective texturing
wm.m_RefractionMatrix = GetModelViewProjectionMatrix();
// Make the depth buffer work backwards; there seems to be some oddness with
// oblique frustum clipping and the "sign" parameter here
glClearDepth(0);
glClearColor(0.5f, 0.5f, 0.5f, 1.0f); // a neutral gray to blend in with shores
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
glDepthFunc(GL_GEQUAL);
SScreenRect screenScissor;
screenScissor.x1 = (GLint)floor((scissor[0].X*0.5f+0.5f)*vp.m_Width);
screenScissor.y1 = (GLint)floor((scissor[0].Y*0.5f+0.5f)*vp.m_Height);
screenScissor.x2 = (GLint)ceil((scissor[1].X*0.5f+0.5f)*vp.m_Width);
screenScissor.y2 = (GLint)ceil((scissor[1].Y*0.5f+0.5f)*vp.m_Height);
if (screenScissor.x1 < screenScissor.x2 && screenScissor.y1 < screenScissor.y2)
{
glEnable(GL_SCISSOR_TEST);
glScissor(screenScissor.x1, screenScissor.y1, screenScissor.x2 - screenScissor.x1, screenScissor.y2 - screenScissor.y1);
// Render terrain and models
RenderPatches();
ogl_WarnIfError();
RenderModels();
ogl_WarnIfError();
RenderTransparentModels();
ogl_WarnIfError();
glClearColor(0.5f, 0.5f, 0.5f, 1.0f); // a neutral gray to blend in with shores
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
// Copy the image to a texture
pglActiveTextureARB(GL_TEXTURE0_ARB);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, wm.m_RefractionTexture);
glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0,
(GLsizei)wm.m_RefractionTextureSize, (GLsizei)wm.m_RefractionTextureSize);
// Render terrain and models
RenderPatches(&m_ViewCamera.GetFrustum());
ogl_WarnIfError();
RenderModels(&m_ViewCamera.GetFrustum());
ogl_WarnIfError();
RenderTransparentModels(TRANSPARENT_BLEND, &m_ViewCamera.GetFrustum());
ogl_WarnIfError();
//Reset old camera and re-enable backface culling
glDisable(GL_SCISSOR_TEST);
// Copy the image to a texture
pglActiveTextureARB(GL_TEXTURE0_ARB);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, wm.m_RefractionTexture);
glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
screenScissor.x1, screenScissor.y1,
screenScissor.x1, screenScissor.y1,
screenScissor.x2 - screenScissor.x1, screenScissor.y2 - screenScissor.y1);
}
// Reset old camera
m_ViewCamera = normalCamera;
m->SetOpenGLCamera(m_ViewCamera);
glEnable(GL_CULL_FACE);
glClearDepth(1);
glDepthFunc(GL_LEQUAL);
return screenScissor;
}
@ -1435,13 +1534,28 @@ void CRenderer::RenderSubmissions()
ogl_WarnIfError();
if (m_WaterManager->m_RenderWater && m_WaterManager->WillRenderFancyWater())
CBound waterScissor;
if (m_WaterManager->m_RenderWater)
{
// render reflected and refracted scenes, then re-clear the screen
RenderReflections();
RenderRefractions();
glClearColor(m_ClearColor[0],m_ClearColor[1],m_ClearColor[2],m_ClearColor[3]);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
waterScissor = m->terrainRenderer->ScissorWater(m_ViewCamera.GetViewProjection());
if (waterScissor.GetVolume() > 0 && m_WaterManager->WillRenderFancyWater())
{
SScreenRect reflectionScissor = RenderReflections(waterScissor);
SScreenRect refractionScissor = RenderRefractions(waterScissor);
SScreenRect dirty;
dirty.x1 = std::min(reflectionScissor.x1, refractionScissor.x1);
dirty.y1 = std::min(reflectionScissor.y1, refractionScissor.y1);
dirty.x2 = std::max(reflectionScissor.x2, refractionScissor.x2);
dirty.y2 = std::max(reflectionScissor.y2, refractionScissor.y2);
if (dirty.x1 < dirty.x2 && dirty.y1 < dirty.y2)
{
glEnable(GL_SCISSOR_TEST);
glScissor(dirty.x1, dirty.y1, dirty.x2 - dirty.x1, dirty.y2 - dirty.y1);
glClearColor(m_ClearColor[0], m_ClearColor[1], m_ClearColor[2], m_ClearColor[3]);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
glDisable(GL_SCISSOR_TEST);
}
}
}
// render submitted patches and models
@ -1467,27 +1581,25 @@ void CRenderer::RenderSubmissions()
RenderModels();
ogl_WarnIfError();
// render transparent stuff, so it can overlap models/terrain
RenderTransparentModels();
ogl_WarnIfError();
// render water
if (m_WaterManager->m_RenderWater && g_Game)
if (m_WaterManager->m_RenderWater && g_Game && waterScissor.GetVolume() > 0)
{
// render transparent stuff, but only the solid parts that can occlude block water
RenderTransparentModels(TRANSPARENT_OPAQUE);
ogl_WarnIfError();
m->terrainRenderer->RenderWater();
ogl_WarnIfError();
// render transparent stuff again, so it can overlap the water
RenderTransparentModels();
// render transparent stuff again, but only the blended parts that overlap water
RenderTransparentModels(TRANSPARENT_BLEND);
ogl_WarnIfError();
}
else
{
// render transparent stuff, so it can overlap models/terrain
RenderTransparentModels(TRANSPARENT);
ogl_WarnIfError();
// TODO: Maybe think of a better way to deal with transparent objects;
// they can appear both under and above water (seaweed vs. trees), but doing
// 2 renders causes (a) inefficiency and (b) darker over-water objects (e.g.
// trees) than usual because the transparent bits get overwritten twice.
// This doesn't look particularly bad, but it is noticeable if you try
// turning the water off. On the other hand every user will have water
// on all the time, so it might not be worth worrying about.
}
// particles are transparent so render after water
@ -1931,6 +2043,19 @@ void CRenderer::JSI_SetShadowAlphaFix(JSContext* ctx, jsval newval)
m->shadow->RecreateTexture();
}
jsval CRenderer::JSI_GetShadowPCF(JSContext*)
{
return ToJSVal(m_Options.m_ShadowPCF);
}
void CRenderer::JSI_SetShadowPCF(JSContext* ctx, jsval newval)
{
if (!ToPrimitive(ctx, newval, m_Options.m_ShadowPCF))
return;
ReloadShaders();
}
jsval CRenderer::JSI_GetSky(JSContext*)
{
return ToJSVal(m->skyManager.GetSkySet());
@ -1955,6 +2080,7 @@ void CRenderer::ScriptingInit()
AddProperty(L"shadows", &CRenderer::JSI_GetShadows, &CRenderer::JSI_SetShadows);
AddProperty(L"depthTextureBits", &CRenderer::JSI_GetDepthTextureBits, &CRenderer::JSI_SetDepthTextureBits);
AddProperty(L"shadowAlphaFix", &CRenderer::JSI_GetShadowAlphaFix, &CRenderer::JSI_SetShadowAlphaFix);
AddProperty(L"shadowPCF", &CRenderer::JSI_GetShadowPCF, &CRenderer::JSI_SetShadowPCF);
AddProperty(L"skipSubmit", &CRenderer::m_SkipSubmit);
AddProperty(L"skySet", &CRenderer::JSI_GetSky, &CRenderer::JSI_SetSky);

View File

@ -49,6 +49,9 @@ class CParticleManager;
// rendering modes
enum ERenderMode { WIREFRAME, SOLID, EDGED_FACES };
// transparency modes
enum ETransparentMode { TRANSPARENT, TRANSPARENT_OPAQUE, TRANSPARENT_BLEND };
// stream flags
#define STREAM_POS (1 << 0)
#define STREAM_NORMAL (1 << 1)
@ -65,6 +68,11 @@ enum ERenderMode { WIREFRAME, SOLID, EDGED_FACES };
// access to sole renderer object
#define g_Renderer CRenderer::GetSingleton()
struct SScreenRect
{
GLint x1, y1, x2, y2;
};
///////////////////////////////////////////////////////////////////////////////////////////
// CRenderer: base renderer class - primary interface to the rendering engine
struct CRendererInternals;
@ -81,7 +89,8 @@ public:
OPT_NOVBO,
OPT_SHADOWS,
OPT_FANCYWATER,
OPT_LODBIAS
OPT_LODBIAS,
OPT_SHADOWPCF
};
enum RenderPath {
@ -104,6 +113,8 @@ public:
size_t m_DrawCalls;
// number of terrain triangles drawn
size_t m_TerrainTris;
// number of water triangles drawn
size_t m_WaterTris;
// number of (non-transparent) model triangles drawn
size_t m_ModelTris;
// number of splat passes for alphamapping
@ -121,6 +132,7 @@ public:
RenderPath m_RenderPath;
bool m_ShadowAlphaFix;
bool m_ARBProgramShadow;
bool m_ShadowPCF;
} m_Options;
struct Caps {
@ -315,6 +327,8 @@ protected:
void JSI_SetShadows(JSContext* ctx, jsval newval);
jsval JSI_GetShadowAlphaFix(JSContext*);
void JSI_SetShadowAlphaFix(JSContext* ctx, jsval newval);
jsval JSI_GetShadowPCF(JSContext*);
void JSI_SetShadowPCF(JSContext* ctx, jsval newval);
jsval JSI_GetSky(JSContext*);
void JSI_SetSky(JSContext* ctx, jsval newval);
@ -331,11 +345,11 @@ protected:
void RenderSubmissions();
// patch rendering stuff
void RenderPatches();
void RenderPatches(const CFrustum* frustum = 0);
// model rendering stuff
void RenderModels();
void RenderTransparentModels();
void RenderModels(const CFrustum* frustum = 0);
void RenderTransparentModels(ETransparentMode transparentMode, const CFrustum* frustum = 0);
void RenderSilhouettes();
@ -345,14 +359,14 @@ protected:
void RenderShadowMap();
// render water reflection and refraction textures
void RenderReflections();
void RenderRefractions();
SScreenRect RenderReflections(const CBound& scissor);
SScreenRect RenderRefractions(const CBound& scissor);
// debugging
void DisplayFrustum();
// enable oblique frustum clipping with the given clip plane
void SetObliqueFrustumClipping(const CVector4D& clipPlane, int sign);
void SetObliqueFrustumClipping(const CVector4D& clipPlane);
void ReloadShaders();

View File

@ -78,6 +78,8 @@ struct ShadowMapInternals
// alpha texture which is attached to the FBO as a workaround.
GLuint DummyTexture;
float FilterOffsets[8];
// Helper functions
void CalcShadowMatrices();
void CreateTexture();
@ -221,6 +223,13 @@ void ShadowMapInternals::CalcShadowMatrices()
ShadowBound.IntersectFrustumConservative(LightspaceCamera.GetFrustum());
// round off the shadow boundaries to sane increments to help reduce swim effect
float boundInc = 16.0f;
ShadowBound[0].X = floor(ShadowBound[0].X / boundInc) * boundInc;
ShadowBound[0].Y = floor(ShadowBound[0].Y / boundInc) * boundInc;
ShadowBound[1].X = ceil(ShadowBound[1].X / boundInc) * boundInc;
ShadowBound[1].Y = ceil(ShadowBound[1].Y / boundInc) * boundInc;
// minimum Z bound must not be clipped too much, because objects that lie outside
// the shadow bounds cannot cast shadows either
// the 2.0 is rather arbitrary: it should be big enough so that we won't accidently miss
@ -242,11 +251,15 @@ void ShadowMapInternals::CalcShadowMatrices()
scale.Y = 2.0 / scale.Y;
scale.Z = 2.0 / scale.Z;
// make sure a given world position falls on a consistent shadowmap texel fractional offset
float offsetX = fmod(ShadowBound[0].X - LightTransform._14, 2.0f/(scale.X*EffectiveWidth));
float offsetY = fmod(ShadowBound[0].Y - LightTransform._24, 2.0f/(scale.Y*EffectiveHeight));
LightProjection.SetZero();
LightProjection._11 = scale.X;
LightProjection._14 = shift.X * scale.X;
LightProjection._14 = (shift.X + offsetX) * scale.X;
LightProjection._22 = scale.Y;
LightProjection._24 = shift.Y * scale.Y;
LightProjection._24 = (shift.Y + offsetY) * scale.Y;
LightProjection._33 = scale.Z;
LightProjection._34 = shift.Z * scale.Z + renderer.m_ShadowZBias;
LightProjection._44 = 1.0;
@ -255,19 +268,15 @@ void ShadowMapInternals::CalcShadowMatrices()
// Calculate texture matrix by creating the clip space to texture coordinate matrix
// and then concatenating all matrices that have been calculated so far
CMatrix3D lightToTex;
float texscalex = (float)EffectiveWidth / (float)Width;
float texscaley = (float)EffectiveHeight / (float)Height;
float texscalez = 1.0;
texscalex = texscalex / (ShadowBound[1].X - ShadowBound[0].X);
texscaley = texscaley / (ShadowBound[1].Y - ShadowBound[0].Y);
texscalez = texscalez / (ShadowBound[1].Z - ShadowBound[0].Z);
float texscalex = scale.X * 0.5f * (float)EffectiveWidth / (float)Width;
float texscaley = scale.Y * 0.5f * (float)EffectiveHeight / (float)Height;
float texscalez = scale.Z * 0.5f;
lightToTex.SetZero();
lightToTex._11 = texscalex;
lightToTex._14 = -ShadowBound[0].X * texscalex;
lightToTex._14 = (offsetX - ShadowBound[0].X) * texscalex;
lightToTex._22 = texscaley;
lightToTex._24 = -ShadowBound[0].Y * texscaley;
lightToTex._24 = (offsetY - ShadowBound[0].Y) * texscaley;
lightToTex._33 = texscalez;
lightToTex._34 = -ShadowBound[0].Z * texscalez;
lightToTex._44 = 1.0;
@ -359,7 +368,7 @@ void ShadowMapInternals::CreateTexture()
glTexImage2D(GL_TEXTURE_2D, 0, format, Width, Height, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_DEPTH_TEXTURE_MODE, GL_LUMINANCE);
glTexParameteri(GL_TEXTURE_2D, GL_DEPTH_TEXTURE_MODE, GL_INTENSITY);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);
@ -397,6 +406,18 @@ void ShadowMapInternals::CreateTexture()
// Disable shadow rendering (but let the user try again if they want)
g_Renderer.m_Options.m_Shadows = false;
}
FilterOffsets[0] = -0.4f/Width;
FilterOffsets[1] = 1.0f/Height;
FilterOffsets[2] = -1.0f/Width;
FilterOffsets[3] = -0.4f/Height;
FilterOffsets[4] = 0.4f/Width;
FilterOffsets[5] = -1.0f/Height;
FilterOffsets[6] = 1.0f/Width;
FilterOffsets[7] = 0.4f/Height;
}
@ -496,6 +517,10 @@ void ShadowMap::SetDepthTextureBits(int bits)
}
}
const float* ShadowMap::GetFilterOffsets() const
{
return m->FilterOffsets;
}
//////////////////////////////////////////////////////////////////////////////
// RenderDebugDisplay: debug visualizations

View File

@ -118,6 +118,11 @@ public:
*/
void RenderDebugDisplay();
/**
* Get offsets for PCF filtering.
*/
const float* GetFilterOffsets() const;
private:
ShadowMapInternals* m;
};

View File

@ -76,9 +76,11 @@ struct TerrainRendererInternals
/// Patches that were submitted for this frame
std::vector<CPatchRData*> visiblePatches;
std::vector<CPatchRData*> filteredPatches;
/// Decals that were submitted for this frame
std::vector<CDecalRData*> visibleDecals;
std::vector<CDecalRData*> filteredDecals;
/// Fancy water shader
Handle fancyWaterShader;
@ -163,19 +165,45 @@ void TerrainRenderer::EndFrame()
}
///////////////////////////////////////////////////////////////////
// Culls patches and decals against a frustum.
bool TerrainRenderer::CullPatches(const CFrustum* frustum)
{
m->filteredPatches.clear();
for (std::vector<CPatchRData*>::iterator it = m->visiblePatches.begin(); it != m->visiblePatches.end(); it++)
{
if (frustum->IsBoxVisible(CVector3D(0, 0, 0), (*it)->GetPatch()->GetBounds()))
m->filteredPatches.push_back(*it);
}
m->filteredDecals.clear();
for (std::vector<CDecalRData*>::iterator it = m->visibleDecals.begin(); it != m->visibleDecals.end(); it++)
{
if (frustum->IsBoxVisible(CVector3D(0, 0, 0), (*it)->GetDecal()->GetBounds()))
m->filteredDecals.push_back(*it);
}
return !m->filteredPatches.empty() || !m->filteredDecals.empty();
}
///////////////////////////////////////////////////////////////////
// Full-featured terrain rendering with blending and everything
void TerrainRenderer::RenderTerrain()
void TerrainRenderer::RenderTerrain(bool filtered)
{
ENSURE(m->phase == Phase_Render);
std::vector<CPatchRData*>& visiblePatches = filtered ? m->filteredPatches : m->visiblePatches;
std::vector<CDecalRData*>& visibleDecals = filtered ? m->filteredDecals : m->visibleDecals;
if (visiblePatches.empty() && visibleDecals.empty())
return;
// render the solid black sides of the map first
g_Renderer.BindTexture(0, 0);
glEnableClientState(GL_VERTEX_ARRAY);
glColor3f(0, 0, 0);
PROFILE_START("render terrain sides");
for (size_t i = 0; i < m->visiblePatches.size(); ++i)
m->visiblePatches[i]->RenderSides();
for (size_t i = 0; i < visiblePatches.size(); ++i)
visiblePatches[i]->RenderSides();
PROFILE_END("render terrain sides");
// switch on required client states
@ -198,7 +226,7 @@ void TerrainRenderer::RenderTerrain()
glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, one);
PROFILE_START("render terrain base");
CPatchRData::RenderBases(m->visiblePatches);
CPatchRData::RenderBases(visiblePatches);
PROFILE_END("render terrain base");
// render blends
@ -231,7 +259,7 @@ void TerrainRenderer::RenderTerrain()
// render blend passes for each patch
PROFILE_START("render terrain blends");
CPatchRData::RenderBlends(m->visiblePatches);
CPatchRData::RenderBlends(visiblePatches);
PROFILE_END("render terrain blends");
// Disable second texcoord array
@ -255,8 +283,8 @@ void TerrainRenderer::RenderTerrain()
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
PROFILE_START("render terrain decals");
for (size_t i = 0; i < m->visibleDecals.size(); ++i)
m->visibleDecals[i]->Render(CShaderProgramPtr());
for (size_t i = 0; i < visibleDecals.size(); ++i)
visibleDecals[i]->Render(CShaderProgramPtr());
PROFILE_END("render terrain decals");
@ -323,7 +351,7 @@ void TerrainRenderer::RenderTerrain()
pglClientActiveTextureARB(GL_TEXTURE0);
PROFILE_START("render terrain streams");
CPatchRData::RenderStreams(m->visiblePatches, streamflags);
CPatchRData::RenderStreams(visiblePatches, streamflags);
PROFILE_END("render terrain streams");
glMatrixMode(GL_TEXTURE);
@ -363,6 +391,10 @@ void TerrainRenderer::PrepareShader(const CShaderProgramPtr& shader, ShadowMap*
{
shader->BindTexture("shadowTex", shadow->GetTexture());
shader->Uniform("shadowTransform", shadow->GetTextureMatrix());
const float* offsets = shadow->GetFilterOffsets();
shader->Uniform("shadowOffsets1", offsets[0], offsets[1], offsets[2], offsets[3]);
shader->Uniform("shadowOffsets2", offsets[4], offsets[5], offsets[6], offsets[7]);
}
CLOSTexture& los = g_Renderer.GetScene().GetLOSTexture();
@ -373,10 +405,15 @@ void TerrainRenderer::PrepareShader(const CShaderProgramPtr& shader, ShadowMap*
shader->Uniform("sunColor", lightEnv.m_SunColor);
}
void TerrainRenderer::RenderTerrainShader(ShadowMap* shadow)
void TerrainRenderer::RenderTerrainShader(ShadowMap* shadow, bool filtered)
{
ENSURE(m->phase == Phase_Render);
std::vector<CPatchRData*>& visiblePatches = filtered ? m->filteredPatches : m->visiblePatches;
std::vector<CDecalRData*>& visibleDecals = filtered ? m->filteredDecals : m->visibleDecals;
if (visiblePatches.empty() && visibleDecals.empty())
return;
CShaderManager& shaderManager = g_Renderer.GetShaderManager();
typedef std::map<CStr, CStr> Defines;
@ -386,6 +423,8 @@ void TerrainRenderer::RenderTerrainShader(ShadowMap* shadow)
defBasic["USE_SHADOW"] = "1";
if (g_Renderer.m_Caps.m_ARBProgramShadow && g_Renderer.m_Options.m_ARBProgramShadow)
defBasic["USE_FP_SHADOW"] = "1";
if (g_Renderer.m_Options.m_ShadowPCF)
defBasic["USE_SHADOW_PCF"] = "1";
}
defBasic["LIGHTING_MODEL_" + g_Renderer.GetLightEnv().GetLightingModel()] = "1";
@ -399,8 +438,8 @@ void TerrainRenderer::RenderTerrainShader(ShadowMap* shadow)
glEnableClientState(GL_VERTEX_ARRAY);
glColor3f(0, 0, 0);
PROFILE_START("render terrain sides");
for (size_t i = 0; i < m->visiblePatches.size(); ++i)
m->visiblePatches[i]->RenderSides();
for (size_t i = 0; i < visiblePatches.size(); ++i)
visiblePatches[i]->RenderSides();
PROFILE_END("render terrain sides");
// switch on required client states
@ -411,7 +450,7 @@ void TerrainRenderer::RenderTerrainShader(ShadowMap* shadow)
PrepareShader(shaderBase, shadow);
PROFILE_START("render terrain base");
CPatchRData::RenderBases(m->visiblePatches);
CPatchRData::RenderBases(visiblePatches);
PROFILE_END("render terrain base");
shaderBase->Unbind();
@ -437,7 +476,7 @@ void TerrainRenderer::RenderTerrainShader(ShadowMap* shadow)
// render blend passes for each patch
PROFILE_START("render terrain blends");
CPatchRData::RenderBlends(m->visiblePatches);
CPatchRData::RenderBlends(visiblePatches);
PROFILE_END("render terrain blends");
// Disable second texcoord array
@ -456,8 +495,8 @@ void TerrainRenderer::RenderTerrainShader(ShadowMap* shadow)
pglClientActiveTextureARB(GL_TEXTURE0);
PROFILE_START("render terrain decals");
for (size_t i = 0; i < m->visibleDecals.size(); ++i)
m->visibleDecals[i]->Render(shaderDecal);
for (size_t i = 0; i < visibleDecals.size(); ++i)
visibleDecals[i]->Render(shaderDecal);
PROFILE_END("render terrain decals");
shaderDecal->Unbind();
@ -480,56 +519,111 @@ void TerrainRenderer::RenderTerrainShader(ShadowMap* shadow)
///////////////////////////////////////////////////////////////////
// Render un-textured patches as polygons
void TerrainRenderer::RenderPatches()
void TerrainRenderer::RenderPatches(bool filtered)
{
ENSURE(m->phase == Phase_Render);
std::vector<CPatchRData*>& visiblePatches = filtered ? m->filteredPatches : m->visiblePatches;
if (visiblePatches.empty())
return;
glEnableClientState(GL_VERTEX_ARRAY);
CPatchRData::RenderStreams(m->visiblePatches, STREAM_POS);
CPatchRData::RenderStreams(visiblePatches, STREAM_POS);
glDisableClientState(GL_VERTEX_ARRAY);
}
///////////////////////////////////////////////////////////////////
// Render outlines of submitted patches as lines
void TerrainRenderer::RenderOutlines()
void TerrainRenderer::RenderOutlines(bool filtered)
{
ENSURE(m->phase == Phase_Render);
std::vector<CPatchRData*>& visiblePatches = filtered ? m->filteredPatches : m->visiblePatches;
if (visiblePatches.empty())
return;
glEnableClientState(GL_VERTEX_ARRAY);
for (size_t i = 0; i < m->visiblePatches.size(); ++i)
m->visiblePatches[i]->RenderOutline();
for (size_t i = 0; i < visiblePatches.size(); ++i)
visiblePatches[i]->RenderOutline();
glDisableClientState(GL_VERTEX_ARRAY);
}
///////////////////////////////////////////////////////////////////
// Render water that is part of the terrain
void TerrainRenderer::RenderWater()
// Scissor rectangle of water patches
CBound TerrainRenderer::ScissorWater(const CMatrix3D &viewproj)
{
PROFILE( "render water" );
CBound scissor;
for (size_t i = 0; i < m->visiblePatches.size(); ++i)
{
CPatchRData* data = m->visiblePatches[i];
const CBound& waterBounds = data->GetWaterBounds();
if (waterBounds.IsEmpty())
continue;
WaterManager* WaterMgr = g_Renderer.GetWaterManager();
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));
CBound screenBounds;
#define ADDBOUND(v1, v2, v3, v4) \
if (v1[2] >= -v1[3]) \
screenBounds += CVector3D(v1[0], v1[1], v1[2]) * (1.0f / v1[3]); \
else \
{ \
float t = v1[2] + v1[3]; \
if (v2[2] > -v2[3]) \
{ \
CVector4D c2 = v1 + (v2 - v1) * (t / (t - (v2[2] + v2[3]))); \
screenBounds += CVector3D(c2[0], c2[1], c2[2]) * (1.0f / c2[3]); \
} \
if (v3[2] > -v3[3]) \
{ \
CVector4D c3 = v1 + (v3 - v1) * (t / (t - (v3[2] + v3[3]))); \
screenBounds += CVector3D(c3[0], c3[1], c3[2]) * (1.0f / c3[3]); \
} \
if (v4[2] > -v4[3]) \
{ \
CVector4D c4 = v1 + (v4 - v1) * (t / (t - (v4[2] + v4[3]))); \
screenBounds += CVector3D(c4[0], c4[1], c4[2]) * (1.0f / c4[3]); \
} \
}
ADDBOUND(v1, v2, v3, v4);
ADDBOUND(v2, v1, v3, v4);
ADDBOUND(v3, v1, v2, v4);
ADDBOUND(v4, v1, v2, v3);
#undef ADDBOUND
if (screenBounds[0].X >= 1.0f || screenBounds[1].X <= -1.0f || screenBounds[0].Y >= 1.0f || screenBounds[1].Y <= -1.0f)
continue;
scissor += screenBounds;
}
return CBound(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));
}
bool fancy = WaterMgr->WillRenderFancyWater();
// Render fancy water
bool TerrainRenderer::RenderFancyWater()
{
PROFILE("render fancy water");
// If we're using fancy water, make sure its shader is loaded
if(fancy && !m->fancyWaterShader)
if (!m->fancyWaterShader)
{
Handle h = ogl_program_load(g_VFS, L"shaders/water_high.xml");
if (h < 0)
{
LOGERROR(L"Failed to load water shader. Falling back to non-fancy water.\n");
g_Renderer.m_Options.m_FancyWater = false;
fancy = false;
return false;
}
else
{
m->fancyWaterShader = h;
}
}
CTerrain* terrain = g_Game->GetWorld()->GetTerrain(); // TODO: stop using g_Game
WaterManager* WaterMgr = g_Renderer.GetWaterManager();
CLOSTexture& losTexture = g_Renderer.GetScene().GetLOSTexture();
glEnable(GL_BLEND);
@ -538,58 +632,15 @@ void TerrainRenderer::RenderWater()
glDepthFunc(GL_LEQUAL);
double time = WaterMgr->m_WaterTexTimer;
double period = 1.6;
int curTex = (int)(time*60/period) % 60;
if(fancy)
{
WaterMgr->m_NormalMap[curTex]->Bind();
}
else
{
WaterMgr->m_WaterTexture[curTex]->Bind();
}
WaterMgr->m_NormalMap[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;
if(!fancy)
{
// Perform the shifting by modifying the texture matrix
glMatrixMode(GL_TEXTURE);
glLoadIdentity();
glTranslatef(tx, ty, 0);
// Set up texture environment to multiply vertex RGB by texture RGB and use vertex alpha
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PRIMARY_COLOR_ARB);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_PRIMARY_COLOR_ARB);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
// Multiply by LOS texture
losTexture.BindTexture(1);
pglClientActiveTextureARB(GL_TEXTURE1);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glLoadMatrixf(losTexture.GetTextureMatrix());
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);
}
float repeatPeriod = WaterMgr->m_RepeatPeriod;
// Set the proper LOD bias
glTexEnvf(GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, g_Renderer.m_Options.m_LodBias);
@ -597,179 +648,200 @@ void TerrainRenderer::RenderWater()
const CCamera& camera = g_Renderer.GetViewCamera();
CVector3D camPos = camera.m_Orientation.GetTranslation();
GLint vertexDepth = 0; // water depth attribute, if using fancy water
// Bind reflection and refraction textures on texture units 1 and 2
pglActiveTextureARB(GL_TEXTURE1_ARB);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, WaterMgr->m_ReflectionTexture);
pglActiveTextureARB(GL_TEXTURE2_ARB);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, WaterMgr->m_RefractionTexture);
if(fancy)
losTexture.BindTexture(3);
// Bind water shader and set arguments
ogl_program_use(m->fancyWaterShader);
GLint ambient = ogl_program_get_uniform_location(m->fancyWaterShader, "ambient");
GLint sunDir = ogl_program_get_uniform_location(m->fancyWaterShader, "sunDir");
GLint sunColor = ogl_program_get_uniform_location(m->fancyWaterShader, "sunColor");
GLint cameraPos = ogl_program_get_uniform_location(m->fancyWaterShader, "cameraPos");
GLint shininess = ogl_program_get_uniform_location(m->fancyWaterShader, "shininess");
GLint specularStrength = ogl_program_get_uniform_location(m->fancyWaterShader, "specularStrength");
GLint waviness = ogl_program_get_uniform_location(m->fancyWaterShader, "waviness");
GLint murkiness = ogl_program_get_uniform_location(m->fancyWaterShader, "murkiness");
GLint fullDepth = ogl_program_get_uniform_location(m->fancyWaterShader, "fullDepth");
GLint tint = ogl_program_get_uniform_location(m->fancyWaterShader, "tint");
GLint reflectionTint = ogl_program_get_uniform_location(m->fancyWaterShader, "reflectionTint");
GLint reflectionTintStrength = ogl_program_get_uniform_location(m->fancyWaterShader, "reflectionTintStrength");
GLint translation = ogl_program_get_uniform_location(m->fancyWaterShader, "translation");
GLint repeatScale = ogl_program_get_uniform_location(m->fancyWaterShader, "repeatScale");
GLint reflectionMatrix = ogl_program_get_uniform_location(m->fancyWaterShader, "reflectionMatrix");
GLint refractionMatrix = ogl_program_get_uniform_location(m->fancyWaterShader, "refractionMatrix");
GLint losMatrix = ogl_program_get_uniform_location(m->fancyWaterShader, "losMatrix");
GLint normalMap = ogl_program_get_uniform_location(m->fancyWaterShader, "normalMap");
GLint reflectionMap = ogl_program_get_uniform_location(m->fancyWaterShader, "reflectionMap");
GLint refractionMap = ogl_program_get_uniform_location(m->fancyWaterShader, "refractionMap");
GLint losMap = ogl_program_get_uniform_location(m->fancyWaterShader, "losMap");
const CLightEnv& lightEnv = g_Renderer.GetLightEnv();
pglUniform3fvARB(ambient, 1, &lightEnv.m_TerrainAmbientColor.X);
pglUniform3fvARB(sunDir, 1, &lightEnv.GetSunDir().X);
pglUniform3fvARB(sunColor, 1, &lightEnv.m_SunColor.X);
pglUniform1fARB(shininess, WaterMgr->m_Shininess);
pglUniform1fARB(specularStrength, WaterMgr->m_SpecularStrength);
pglUniform1fARB(waviness, WaterMgr->m_Waviness);
pglUniform1fARB(murkiness, WaterMgr->m_Murkiness);
pglUniform1fARB(fullDepth, WaterMgr->m_WaterFullDepth);
pglUniform3fvARB(tint, 1, WaterMgr->m_WaterTint.FloatArray());
pglUniform1fARB(reflectionTintStrength, WaterMgr->m_ReflectionTintStrength);
pglUniform3fvARB(reflectionTint, 1, WaterMgr->m_ReflectionTint.FloatArray());
pglUniform2fARB(translation, tx, ty);
pglUniform1fARB(repeatScale, 1.0f / repeatPeriod);
pglUniformMatrix4fvARB(reflectionMatrix, 1, false, &WaterMgr->m_ReflectionMatrix._11);
pglUniformMatrix4fvARB(refractionMatrix, 1, false, &WaterMgr->m_RefractionMatrix._11);
pglUniformMatrix4fvARB(losMatrix, 1, false, losTexture.GetTextureMatrix());
pglUniform1iARB(normalMap, 0); // texture unit 0
pglUniform1iARB(reflectionMap, 1); // texture unit 1
pglUniform1iARB(refractionMap, 2); // texture unit 2
pglUniform1iARB(losMap, 3); // texture unit 3
pglUniform3fvARB(cameraPos, 1, &camPos.X);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
for (size_t i = 0; i < m->visiblePatches.size(); ++i)
{
// Bind reflection and refraction textures on texture units 1 and 2
pglActiveTextureARB( GL_TEXTURE1_ARB );
glEnable( GL_TEXTURE_2D );
glBindTexture( GL_TEXTURE_2D, WaterMgr->m_ReflectionTexture );
pglActiveTextureARB( GL_TEXTURE2_ARB );
glEnable( GL_TEXTURE_2D );
glBindTexture( GL_TEXTURE_2D, WaterMgr->m_RefractionTexture );
losTexture.BindTexture(3);
// Bind water shader and set arguments
ogl_program_use( m->fancyWaterShader );
GLint ambient = ogl_program_get_uniform_location( m->fancyWaterShader, "ambient" );
GLint sunDir = ogl_program_get_uniform_location( m->fancyWaterShader, "sunDir" );
GLint sunColor = ogl_program_get_uniform_location( m->fancyWaterShader, "sunColor" );
GLint cameraPos = ogl_program_get_uniform_location( m->fancyWaterShader, "cameraPos" );
GLint shininess = ogl_program_get_uniform_location( m->fancyWaterShader, "shininess" );
GLint specularStrength = ogl_program_get_uniform_location( m->fancyWaterShader, "specularStrength" );
GLint waviness = ogl_program_get_uniform_location( m->fancyWaterShader, "waviness" );
GLint murkiness = ogl_program_get_uniform_location( m->fancyWaterShader, "murkiness" );
GLint fullDepth = ogl_program_get_uniform_location( m->fancyWaterShader, "fullDepth" );
GLint tint = ogl_program_get_uniform_location( m->fancyWaterShader, "tint" );
GLint reflectionTint = ogl_program_get_uniform_location( m->fancyWaterShader, "reflectionTint" );
GLint reflectionTintStrength = ogl_program_get_uniform_location( m->fancyWaterShader, "reflectionTintStrength" );
GLint translation = ogl_program_get_uniform_location( m->fancyWaterShader, "translation" );
GLint reflectionMatrix = ogl_program_get_uniform_location( m->fancyWaterShader, "reflectionMatrix" );
GLint refractionMatrix = ogl_program_get_uniform_location( m->fancyWaterShader, "refractionMatrix" );
GLint losMatrix = ogl_program_get_uniform_location( m->fancyWaterShader, "losMatrix" );
GLint normalMap = ogl_program_get_uniform_location( m->fancyWaterShader, "normalMap" );
GLint reflectionMap = ogl_program_get_uniform_location( m->fancyWaterShader, "reflectionMap" );
GLint refractionMap = ogl_program_get_uniform_location( m->fancyWaterShader, "refractionMap" );
GLint losMap = ogl_program_get_uniform_location( m->fancyWaterShader, "losMap" );
const CLightEnv& lightEnv = g_Renderer.GetLightEnv();
pglUniform3fvARB( ambient, 1, &lightEnv.m_TerrainAmbientColor.X );
pglUniform3fvARB( sunDir, 1, &lightEnv.GetSunDir().X );
pglUniform3fvARB( sunColor, 1, &lightEnv.m_SunColor.X );
pglUniform1fARB( shininess, WaterMgr->m_Shininess );
pglUniform1fARB( specularStrength, WaterMgr->m_SpecularStrength );
pglUniform1fARB( waviness, WaterMgr->m_Waviness );
pglUniform1fARB( murkiness, WaterMgr->m_Murkiness );
pglUniform1fARB( fullDepth, WaterMgr->m_WaterFullDepth );
pglUniform3fvARB( tint, 1, WaterMgr->m_WaterTint.FloatArray() );
pglUniform1fARB( reflectionTintStrength, WaterMgr->m_ReflectionTintStrength );
pglUniform3fvARB( reflectionTint, 1, WaterMgr->m_ReflectionTint.FloatArray() );
pglUniform4fARB( translation, tx, ty, 0, 0 );
pglUniformMatrix4fvARB( reflectionMatrix, 1, false, &WaterMgr->m_ReflectionMatrix._11 );
pglUniformMatrix4fvARB( refractionMatrix, 1, false, &WaterMgr->m_RefractionMatrix._11 );
pglUniformMatrix4fvARB( losMatrix, 1, false, losTexture.GetTextureMatrix() );
pglUniform1iARB( normalMap, 0 ); // texture unit 0
pglUniform1iARB( reflectionMap, 1 ); // texture unit 1
pglUniform1iARB( refractionMap, 2 ); // texture unit 2
pglUniform1iARB( losMap, 3 ); // texture unit 3
pglUniform3fvARB( cameraPos, 1, &camPos.X );
vertexDepth = ogl_program_get_attrib_location( m->fancyWaterShader, "vertexDepth" );
}
float repeatPeriod = (fancy ? WaterMgr->m_RepeatPeriod : 16.0f);
glBegin(GL_QUADS);
for(size_t i=0; i<m->visiblePatches.size(); i++)
{
CPatch* patch = m->visiblePatches[i]->GetPatch();
for(ssize_t dx=0; dx<PATCH_SIZE; dx++)
{
for(ssize_t dz=0; dz<PATCH_SIZE; dz++)
{
ssize_t x = (patch->m_X*PATCH_SIZE + dx);
ssize_t z = (patch->m_Z*PATCH_SIZE + dz);
// Some offsets used to go around counterclockwise while keeping code concise
const int DX[] = {1,1,0,0};
const int DZ[] = {0,1,1,0};
// is any corner of the tile below the water height? if not, no point rendering it
bool shouldRender = false;
for (int j = 0; j < 4; j++)
{
float terrainHeight = terrain->GetVertexGroundLevel(x + DX[j], z + DZ[j]);
if (terrainHeight < WaterMgr->m_WaterHeight)
{
shouldRender = true;
break;
}
}
if (!shouldRender)
continue;
for (int j=0; j<4; j++)
{
ssize_t ix = x + DX[j];
ssize_t iz = z + DZ[j];
float vertX = ix * CELL_SIZE;
float vertZ = iz * CELL_SIZE;
float terrainHeight = terrain->GetVertexGroundLevel(ix, iz);
if (fancy)
{
pglVertexAttrib1fARB(vertexDepth, WaterMgr->m_WaterHeight - terrainHeight);
pglMultiTexCoord2fARB(GL_TEXTURE0, vertX/repeatPeriod, vertZ/repeatPeriod);
glVertex3f(vertX, WaterMgr->m_WaterHeight, vertZ);
}
else
{
float alpha = clamp( (WaterMgr->m_WaterHeight - terrainHeight) / WaterMgr->m_WaterFullDepth + WaterMgr->m_WaterAlphaOffset,
WaterMgr->m_WaterAlphaOffset, WaterMgr->m_WaterMaxAlpha);
// (Crappy) fresnel effect
CVector3D CamFaceVertex=CVector3D(vertX,WaterMgr->m_WaterHeight,vertZ)-camPos;
CamFaceVertex.Normalize();
float FresnelScalar = CamFaceVertex.Dot(CVector3D(0.0f, -1.0f, 0.0f));
// Invert and set boundaries
FresnelScalar = 1.f - (FresnelScalar * 0.6);
glColor4f(WaterMgr->m_WaterColor.r,
WaterMgr->m_WaterColor.g,
WaterMgr->m_WaterColor.b,
alpha * FresnelScalar);
pglMultiTexCoord2fARB(GL_TEXTURE0, vertX/repeatPeriod, vertZ/repeatPeriod);
pglMultiTexCoord3fARB(GL_TEXTURE1, vertX, WaterMgr->m_WaterHeight, vertZ);
glVertex3f(vertX, WaterMgr->m_WaterHeight, vertZ);
}
}
} //end of x loop
} //end of z loop
}
glEnd();
if (fancy)
{
// Unbind the LOS/refraction/reflection textures and the shader
g_Renderer.BindTexture(3, 0);
g_Renderer.BindTexture(2, 0);
g_Renderer.BindTexture(1, 0);
pglActiveTextureARB(GL_TEXTURE0_ARB);
ogl_program_use(0);
CPatchRData* data = m->visiblePatches[i];
data->RenderWater();
}
if (!fancy)
glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
// Unbind the LOS/refraction/reflection textures and the shader
g_Renderer.BindTexture(3, 0);
g_Renderer.BindTexture(2, 0);
g_Renderer.BindTexture(1, 0);
pglActiveTextureARB(GL_TEXTURE0_ARB);
ogl_program_use(0);
glDisable(GL_BLEND);
return true;
}
void TerrainRenderer::RenderSimpleWater()
{
PROFILE("render simple water");
WaterManager* WaterMgr = g_Renderer.GetWaterManager();
CLOSTexture& losTexture = g_Game->GetView()->GetLOSTexture();
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
double time = WaterMgr->m_WaterTexTimer;
double period = 1.6f;
int curTex = (int)(time*60/period) % 60;
WaterMgr->m_WaterTexture[curTex]->Bind();
// Shift the texture coordinates by these amounts to make the water "flow"
float tx = -fmod(time, 81.0)/81.0;
float ty = -fmod(time, 34.0)/34.0;
float repeatPeriod = 16.0f;
// Perform the shifting by using texture coordinate generation
GLfloat texgenS0[4] = { 1/repeatPeriod, 0, 0, tx };
GLfloat texgenT0[4] = { 0, 0, 1/repeatPeriod, ty };
glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
glTexGenfv(GL_S, GL_OBJECT_PLANE, texgenS0);
glTexGenfv(GL_T, GL_OBJECT_PLANE, texgenT0);
glEnable(GL_TEXTURE_GEN_S);
glEnable(GL_TEXTURE_GEN_T);
// Set up texture environment to multiply vertex RGB by texture RGB and use vertex alpha
GLfloat waterColor[4] = { WaterMgr->m_WaterColor.r, WaterMgr->m_WaterColor.g, WaterMgr->m_WaterColor.b, 1.0f };
glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, waterColor);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_CONSTANT);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_PRIMARY_COLOR_ARB);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
// Multiply by LOS texture
losTexture.BindTexture(1);
const float *losMatrix = losTexture.GetTextureMatrix();
GLfloat texgenS1[4] = { losMatrix[0], losMatrix[4], losMatrix[8], losMatrix[12] };
GLfloat texgenT1[4] = { losMatrix[1], losMatrix[5], losMatrix[9], losMatrix[13] };
glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
glTexGenfv(GL_S, GL_OBJECT_PLANE, texgenS1);
glTexGenfv(GL_T, GL_OBJECT_PLANE, texgenT1);
glEnable(GL_TEXTURE_GEN_S);
glEnable(GL_TEXTURE_GEN_T);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_ALPHA);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_PREVIOUS);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
// Set the proper LOD bias
glTexEnvf(GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, g_Renderer.m_Options.m_LodBias);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
for (size_t i = 0; i < m->visiblePatches.size(); ++i)
{
g_Renderer.BindTexture(1, 0);
pglClientActiveTextureARB(GL_TEXTURE1_ARB);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glLoadIdentity();
pglActiveTextureARB(GL_TEXTURE0_ARB);
pglClientActiveTextureARB(GL_TEXTURE0_ARB);
// Clean up the texture matrix and blend mode
glLoadIdentity();
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
CPatchRData* data = m->visiblePatches[i];
data->RenderWater();
}
glMatrixMode(GL_MODELVIEW);
glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
g_Renderer.BindTexture(1, 0);
glDisable(GL_TEXTURE_GEN_S);
glDisable(GL_TEXTURE_GEN_T);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
pglActiveTextureARB(GL_TEXTURE0_ARB);
// Clean up the texture matrix and blend mode
glDisable(GL_TEXTURE_GEN_S);
glDisable(GL_TEXTURE_GEN_T);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
glDisable(GL_BLEND);
glDisable(GL_TEXTURE_2D);
}
///////////////////////////////////////////////////////////////////
// Render water that is part of the terrain
void TerrainRenderer::RenderWater()
{
WaterManager* WaterMgr = g_Renderer.GetWaterManager();
if (!WaterMgr->WillRenderFancyWater() || !RenderFancyWater())
RenderSimpleWater();
}
void TerrainRenderer::RenderPriorities()
{
PROFILE("render priorities");

View File

@ -71,38 +71,52 @@ public:
*/
void EndFrame();
/**
* CullPatches: Culls patches and decals against a frustum,
* and stores the results in a special filtered list that
* is used when calling render functions with @p filtered true.
*/
bool CullPatches(const CFrustum* frustum);
/**
* RenderTerrain: Render textured terrain (including blends between
* different terrain types).
*
* preconditions : PrepareForRendering must have been called this
* frame before calling RenderTerrain.
*
* @param filtered If true then only render objects that passed CullPatches.
*/
void RenderTerrain();
void RenderTerrain(bool filtered = false);
/**
* Render textured terrain, as with RenderTerrain, but using shaders
* instead of multitexturing.
*
* @param shadow A prepared shadow map, in case rendering with shadows is enabled.
* @param filtered If true then only render objects that passed CullPatches.
*/
void RenderTerrainShader(ShadowMap* shadow);
void RenderTerrainShader(ShadowMap* shadow, bool filtered = false);
/**
* RenderPatches: Render all patches un-textured as polygons.
*
* preconditions : PrepareForRendering must have been called this
* frame before calling RenderPatches.
*
* @param filtered If true then only render objects that passed CullPatches.
*/
void RenderPatches();
void RenderPatches(bool filtered = false);
/**
* RenderOutlines: Render the outline of patches as lines.
*
* preconditions : PrepareForRendering must have been called this
* frame before calling RenderOutlines.
*
* @param filtered If true then only render objects that passed CullPatches.
*/
void RenderOutlines();
void RenderOutlines(bool filtered = false);
/**
* RenderWater: Render water for all patches that have been submitted
@ -113,6 +127,11 @@ public:
*/
void RenderWater();
/**
* Calculate a scissor rectangle for the visible water patches.
*/
CBound ScissorWater(const CMatrix3D& viewproj);
/**
* Render priority text for all submitted patches, for debugging.
*/
@ -121,6 +140,17 @@ public:
private:
TerrainRendererInternals* m;
/**
* RenderFancyWater: internal rendering method for fancy water.
* Returns false if unable to render with fancy water.
*/
bool RenderFancyWater();
/**
* RenderSimpleWater: internal rendering method for water
*/
void RenderSimpleWater();
void PrepareShader(const CShaderProgramPtr& shader, ShadowMap* shadow);
};

View File

@ -540,7 +540,21 @@ void SortModelRenderer::Render(const RenderModifierPtr& modifier, int flags)
} while(!modifier->EndPass(pass++));
}
void SortModelRenderer::Filter(CModelFilter& filter, int passed, int flags)
{
for (std::vector<SModel*>::iterator it = m->models.begin(); it != m->models.end(); ++it)
{
SModel* smdl = *it;
CModel* mdl = smdl->GetModel();
if (flags && !(mdl->GetFlags() & flags))
continue;
if (filter.Filter(mdl))
mdl->SetFlags(mdl->GetFlags() | passed);
else
mdl->SetFlags(mdl->GetFlags() & ~passed);
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// TransparentRenderModifier implementation
@ -555,67 +569,49 @@ TransparentRenderModifier::~TransparentRenderModifier()
int TransparentRenderModifier::BeginPass(int pass)
{
// First pass: opaque areas only.
// Second pass: blended areas.
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PRIMARY_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR);
// just pass through texture's alpha
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);
// Set the proper LOD bias
glTexEnvf(GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, g_Renderer.m_Options.m_LodBias);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_ALPHA_TEST);
if (pass == 0)
{
// First pass: Put down Z for opaque parts of the model,
// don't touch the color buffer.
glDepthMask(1);
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_CONSTANT);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
// just pass through texture's alpha
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);
// Set the proper LOD bias
glTexEnvf(GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, g_Renderer.m_Options.m_LodBias);
glEnable(GL_ALPHA_TEST);
glAlphaFunc(GL_GREATER,0.975f);
// render everything with color writes off to setup depth buffer correctly
glColorMask(0,0,0,0);
return STREAM_POS|STREAM_UV0;
glAlphaFunc(GL_GREATER, 0.9375f);
}
else
{
// Second pass: Put down color, disable Z write
glColorMask(1,1,1,1);
glAlphaFunc(GL_GREATER, 0.0f);
glDepthFunc(GL_LESS);
glDepthMask(0);
// setup texture environment to modulate diffuse color with texture color
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PRIMARY_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR);
glAlphaFunc(GL_GREATER,0);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
// Set the proper LOD bias
glTexEnvf(GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, g_Renderer.m_Options.m_LodBias);
return STREAM_POS|STREAM_COLOR|STREAM_UV0;
}
return STREAM_POS|STREAM_COLOR|STREAM_UV0;
}
bool TransparentRenderModifier::EndPass(int pass)
{
if (pass == 0)
return false; // multi-pass
glDisable(GL_BLEND);
glDisable(GL_ALPHA_TEST);
if (pass == 0)
return false;
glDepthFunc(GL_LEQUAL);
glDepthMask(1);
return true;
@ -632,6 +628,129 @@ void TransparentRenderModifier::PrepareModel(int UNUSED(pass), CModel* UNUSED(mo
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// TransparentOpaqueRenderModifier implementation
TransparentOpaqueRenderModifier::TransparentOpaqueRenderModifier()
{
}
TransparentOpaqueRenderModifier::~TransparentOpaqueRenderModifier()
{
}
int TransparentOpaqueRenderModifier::BeginPass(int pass)
{
ENSURE(pass == 0);
// Put down opaque parts of the model only.
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PRIMARY_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR);
// just pass through texture's alpha
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);
// Set the proper LOD bias
glTexEnvf(GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, g_Renderer.m_Options.m_LodBias);
glEnable(GL_ALPHA_TEST);
glAlphaFunc(GL_GREATER, 0.9375f);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
return STREAM_POS|STREAM_COLOR|STREAM_UV0;
}
bool TransparentOpaqueRenderModifier::EndPass(int UNUSED(pass))
{
glDisable(GL_ALPHA_TEST);
glDisable(GL_BLEND);
return true;
}
void TransparentOpaqueRenderModifier::PrepareTexture(int UNUSED(pass), CTexturePtr& texture)
{
texture->Bind(0);
}
void TransparentOpaqueRenderModifier::PrepareModel(int UNUSED(pass), CModel* UNUSED(model))
{
// No per-model setup necessary
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// TransparentBlendRenderModifier implementation
TransparentBlendRenderModifier::TransparentBlendRenderModifier()
{
}
TransparentBlendRenderModifier::~TransparentBlendRenderModifier()
{
}
int TransparentBlendRenderModifier::BeginPass(int pass)
{
ENSURE(pass == 0);
// Put down blended parts of the model only.
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PRIMARY_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR);
// just pass through texture's alpha
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);
// Set the proper LOD bias
glTexEnvf(GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, g_Renderer.m_Options.m_LodBias);
glEnable(GL_ALPHA_TEST);
glAlphaFunc(GL_GREATER,0);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDepthFunc(GL_LESS);
glDepthMask(0);
return STREAM_POS|STREAM_COLOR|STREAM_UV0;
}
bool TransparentBlendRenderModifier::EndPass(int UNUSED(pass))
{
glDisable(GL_ALPHA_TEST);
glDisable(GL_BLEND);
glDepthFunc(GL_LEQUAL);
glDepthMask(1);
return true;
}
void TransparentBlendRenderModifier::PrepareTexture(int UNUSED(pass), CTexturePtr& texture)
{
texture->Bind(0);
}
void TransparentBlendRenderModifier::PrepareModel(int UNUSED(pass), CModel* UNUSED(model))
{
// No per-model setup necessary
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// TransparentDepthShadowModifier implementation

View File

@ -85,6 +85,7 @@ public:
void EndFrame();
bool HaveSubmissions();
void Render(const RenderModifierPtr& modifier, int flags);
void Filter(CModelFilter& filter, int passed, int flags);
private:
SortModelRendererInternals* m;
@ -106,9 +107,41 @@ public:
bool EndPass(int pass);
void PrepareTexture(int pass, CTexturePtr& texture);
void PrepareModel(int pass, CModel* model);
};
/**
* Class TransparentOpaqueRenderModifier: Modifier for transparent models,
* including alpha blending and lighting, Opaque pass only.
*/
class TransparentOpaqueRenderModifier : public RenderModifier
{
public:
TransparentOpaqueRenderModifier();
~TransparentOpaqueRenderModifier();
// Implementation
int BeginPass(int pass);
bool EndPass(int pass);
void PrepareTexture(int pass, CTexturePtr& texture);
void PrepareModel(int pass, CModel* model);
};
/**
* Class TransparentBlendRenderModifier: Modifier for transparent models,
* including alpha blending and lighting. Blend pass only.
*/
class TransparentBlendRenderModifier : public RenderModifier
{
public:
TransparentBlendRenderModifier();
~TransparentBlendRenderModifier();
// Implementation
int BeginPass(int pass);
bool EndPass(int pass);
void PrepareTexture(int pass, CTexturePtr& texture);
void PrepareModel(int pass, CModel* model);
};
/**
* Class TransparentDepthShadowModifier: Use to render shadow data for