2009-04-18 19:00:33 +02:00
|
|
|
/* Copyright (C) 2009 Wildfire Games.
|
|
|
|
* This file is part of 0 A.D.
|
|
|
|
*
|
|
|
|
* 0 A.D. is free software: you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation, either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* 0 A.D. is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
2009-04-18 19:51:05 +02:00
|
|
|
/*
|
|
|
|
* Shadow mapping related texture and matrix management
|
2006-01-07 02:04:26 +01:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include "precompiled.h"
|
|
|
|
|
had to remove uint and ulong from lib/types.h due to conflict with other library.
this snowballed into a massive search+destroy of the hodgepodge of
mostly equivalent types we had in use (int, uint, unsigned, unsigned
int, i32, u32, ulong, uintN).
it is more efficient to use 64-bit types in 64-bit mode, so the
preferred default is size_t (for anything remotely resembling a size or
index). tile coordinates are ssize_t to allow more efficient conversion
to/from floating point. flags are int because we almost never need more
than 15 distinct bits, bit test/set is not slower and int is fastest to
type. finally, some data that is pretty much directly passed to OpenGL
is now typed accordingly.
after several hours, the code now requires fewer casts and less
guesswork.
other changes:
- unit and player IDs now have an "invalid id" constant in the
respective class to avoid casting and -1
- fix some endian/64-bit bugs in the map (un)packing. added a
convenience function to write/read a size_t.
- ia32: change CPUID interface to allow passing in ecx (required for
cache topology detection, which I need at work). remove some unneeded
functions from asm, replace with intrinsics where possible.
This was SVN commit r5942.
2008-05-11 20:48:32 +02:00
|
|
|
#include "lib/bits.h"
|
2006-06-02 04:10:27 +02:00
|
|
|
#include "lib/ogl.h"
|
|
|
|
#include "ps/CLogger.h"
|
2006-01-07 02:04:26 +01:00
|
|
|
|
|
|
|
#include "graphics/LightEnv.h"
|
|
|
|
|
|
|
|
#include "maths/Bound.h"
|
|
|
|
#include "maths/MathUtil.h"
|
|
|
|
#include "maths/Matrix3D.h"
|
|
|
|
|
|
|
|
#include "renderer/Renderer.h"
|
|
|
|
#include "renderer/ShadowMap.h"
|
|
|
|
|
2009-11-03 22:46:35 +01:00
|
|
|
#define LOG_CATEGORY L"graphics"
|
2006-02-11 19:04:32 +01:00
|
|
|
|
2006-01-07 02:04:26 +01:00
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// ShadowMap implementation
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Struct ShadowMapInternals: Internal data for the ShadowMap implementation
|
|
|
|
*/
|
|
|
|
struct ShadowMapInternals
|
|
|
|
{
|
2006-02-11 19:04:32 +01:00
|
|
|
// whether we're using depth texture or luminance map
|
|
|
|
bool UseDepthTexture;
|
2006-02-16 00:50:24 +01:00
|
|
|
// bit depth for the depth texture, if used
|
|
|
|
int DepthTextureBits;
|
2006-03-26 19:36:33 +02:00
|
|
|
// if non-zero, we're using EXT_framebuffer_object for shadow rendering,
|
|
|
|
// and this is the framebuffer
|
|
|
|
GLuint Framebuffer;
|
2006-01-07 02:04:26 +01:00
|
|
|
// handle of shadow map
|
|
|
|
GLuint Texture;
|
|
|
|
// width, height of shadow map
|
2007-02-09 18:04:55 +01:00
|
|
|
int Width, Height;
|
2006-03-26 19:36:33 +02:00
|
|
|
// used width, height of shadow map
|
2007-02-09 18:04:55 +01:00
|
|
|
int EffectiveWidth, EffectiveHeight;
|
2006-02-11 01:26:40 +01:00
|
|
|
// transform light space into projected light space
|
|
|
|
// in projected light space, the shadowbound box occupies the [-1..1] cube
|
|
|
|
// calculated on BeginRender, after the final shadow bounds are known
|
2006-01-07 02:04:26 +01:00
|
|
|
CMatrix3D LightProjection;
|
2006-02-11 01:26:40 +01:00
|
|
|
// Transform world space into light space; calculated on SetupFrame
|
2006-01-07 02:04:26 +01:00
|
|
|
CMatrix3D LightTransform;
|
2006-02-11 01:26:40 +01:00
|
|
|
// Transform world space into texture space of the shadow map;
|
|
|
|
// calculated on BeginRender, after the final shadow bounds are known
|
2006-01-07 02:04:26 +01:00
|
|
|
CMatrix3D TextureMatrix;
|
2006-01-29 18:34:45 +01:00
|
|
|
|
|
|
|
// transform light space into world space
|
|
|
|
CMatrix3D InvLightTransform;
|
|
|
|
// bounding box of shadowed objects in light space
|
2006-02-11 01:26:40 +01:00
|
|
|
CBound ShadowBound;
|
2006-01-29 18:34:45 +01:00
|
|
|
|
2006-03-26 23:58:48 +02:00
|
|
|
// Camera transformed into light space
|
|
|
|
CCamera LightspaceCamera;
|
|
|
|
|
2006-01-07 02:04:26 +01:00
|
|
|
// Helper functions
|
2006-02-11 01:26:40 +01:00
|
|
|
void CalcShadowMatrices();
|
|
|
|
void CreateTexture();
|
2006-01-07 02:04:26 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Construction/Destruction
|
|
|
|
ShadowMap::ShadowMap()
|
|
|
|
{
|
|
|
|
m = new ShadowMapInternals;
|
2006-03-26 19:36:33 +02:00
|
|
|
m->Framebuffer = 0;
|
2006-01-07 02:04:26 +01:00
|
|
|
m->Texture = 0;
|
2006-02-11 19:04:32 +01:00
|
|
|
m->Width = 0;
|
|
|
|
m->Height = 0;
|
2006-03-26 19:36:33 +02:00
|
|
|
m->EffectiveWidth = 0;
|
|
|
|
m->EffectiveHeight = 0;
|
2006-02-11 19:04:32 +01:00
|
|
|
m->UseDepthTexture = false;
|
2007-02-10 21:53:48 +01:00
|
|
|
m->DepthTextureBits = 0;
|
|
|
|
// DepthTextureBits: 24/32 are very much faster than 16, on GeForce 4 and FX;
|
|
|
|
// but they're very much slower on Radeon 9800.
|
|
|
|
// In both cases, the default (no specified depth) is fast, so we just use
|
|
|
|
// that by default and hope it's alright. (Otherwise, we'd probably need to
|
|
|
|
// do some kind of hardware detection to work out what to use.)
|
2006-01-07 02:04:26 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ShadowMap::~ShadowMap()
|
|
|
|
{
|
|
|
|
if (m->Texture)
|
|
|
|
glDeleteTextures(1, &m->Texture);
|
2006-03-26 19:36:33 +02:00
|
|
|
if (m->Framebuffer)
|
|
|
|
pglDeleteFramebuffersEXT(1, &m->Framebuffer);
|
2006-01-07 02:04:26 +01:00
|
|
|
|
|
|
|
delete m;
|
|
|
|
}
|
|
|
|
|
2006-05-31 18:42:50 +02:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Force the texture/buffer/etc to be recreated, particularly when the renderer's
|
|
|
|
// size has changed
|
|
|
|
void ShadowMap::RecreateTexture()
|
|
|
|
{
|
|
|
|
if (m->Texture)
|
|
|
|
glDeleteTextures(1, &m->Texture);
|
|
|
|
if (m->Framebuffer)
|
|
|
|
pglDeleteFramebuffersEXT(1, &m->Framebuffer);
|
|
|
|
|
|
|
|
m->Texture = 0;
|
|
|
|
m->Framebuffer = 0;
|
|
|
|
|
|
|
|
// (Texture will be constructed in next SetupFrame)
|
|
|
|
}
|
|
|
|
|
2006-01-29 18:34:45 +01:00
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
2006-03-26 19:36:33 +02:00
|
|
|
// SetupFrame: camera and light direction for this frame
|
2006-02-11 01:26:40 +01:00
|
|
|
void ShadowMap::SetupFrame(const CCamera& camera, const CVector3D& lightdir)
|
2006-01-29 18:34:45 +01:00
|
|
|
{
|
2006-02-11 01:26:40 +01:00
|
|
|
if (!m->Texture)
|
|
|
|
m->CreateTexture();
|
|
|
|
|
2006-01-29 18:34:45 +01:00
|
|
|
CVector3D z = lightdir;
|
|
|
|
CVector3D y;
|
|
|
|
CVector3D x = camera.m_Orientation.GetIn();
|
|
|
|
CVector3D eyepos = camera.m_Orientation.GetTranslation();
|
|
|
|
|
|
|
|
z.Normalize();
|
|
|
|
x -= z * z.Dot(x);
|
2007-05-02 14:07:08 +02:00
|
|
|
if (x.Length() < 0.001)
|
2006-01-29 18:34:45 +01:00
|
|
|
{
|
|
|
|
// this is invoked if the camera and light directions almost coincide
|
|
|
|
// assumption: light direction has a significant Z component
|
|
|
|
x = CVector3D(1.0, 0.0, 0.0);
|
|
|
|
x -= z * z.Dot(x);
|
|
|
|
}
|
|
|
|
x.Normalize();
|
|
|
|
y = z.Cross(x);
|
|
|
|
|
|
|
|
// X axis perpendicular to light direction, flowing along with view direction
|
2006-02-11 01:26:40 +01:00
|
|
|
m->LightTransform._11 = x.X;
|
|
|
|
m->LightTransform._12 = x.Y;
|
|
|
|
m->LightTransform._13 = x.Z;
|
2006-01-29 18:34:45 +01:00
|
|
|
|
|
|
|
// Y axis perpendicular to light and view direction
|
2006-02-11 01:26:40 +01:00
|
|
|
m->LightTransform._21 = y.X;
|
|
|
|
m->LightTransform._22 = y.Y;
|
|
|
|
m->LightTransform._23 = y.Z;
|
2006-01-29 18:34:45 +01:00
|
|
|
|
|
|
|
// Z axis is in direction of light
|
2006-02-11 01:26:40 +01:00
|
|
|
m->LightTransform._31 = z.X;
|
|
|
|
m->LightTransform._32 = z.Y;
|
|
|
|
m->LightTransform._33 = z.Z;
|
2006-01-29 18:34:45 +01:00
|
|
|
|
|
|
|
// eye is at the origin of the coordinate system
|
2006-02-11 01:26:40 +01:00
|
|
|
m->LightTransform._14 = -x.Dot(eyepos);
|
|
|
|
m->LightTransform._24 = -y.Dot(eyepos);
|
|
|
|
m->LightTransform._34 = -z.Dot(eyepos);
|
2006-01-29 18:34:45 +01:00
|
|
|
|
2006-02-11 01:26:40 +01:00
|
|
|
m->LightTransform._41 = 0.0;
|
|
|
|
m->LightTransform._42 = 0.0;
|
|
|
|
m->LightTransform._43 = 0.0;
|
|
|
|
m->LightTransform._44 = 1.0;
|
2006-01-29 18:34:45 +01:00
|
|
|
|
2006-02-11 01:26:40 +01:00
|
|
|
m->LightTransform.GetInverse(m->InvLightTransform);
|
|
|
|
m->ShadowBound.SetEmpty();
|
2006-03-26 23:58:48 +02:00
|
|
|
|
|
|
|
//
|
|
|
|
m->LightspaceCamera = camera;
|
|
|
|
m->LightspaceCamera.m_Orientation = m->LightTransform * camera.m_Orientation;
|
|
|
|
m->LightspaceCamera.UpdateFrustum();
|
2006-01-29 18:34:45 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
// AddShadowedBound: add a world-space bounding box to the bounds of shadowed
|
|
|
|
// objects
|
|
|
|
void ShadowMap::AddShadowedBound(const CBound& bounds)
|
|
|
|
{
|
|
|
|
CBound lightspacebounds;
|
|
|
|
|
2006-02-11 01:26:40 +01:00
|
|
|
bounds.Transform(m->LightTransform, lightspacebounds);
|
|
|
|
m->ShadowBound += lightspacebounds;
|
2006-01-07 02:04:26 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// CalcShadowMatrices: calculate required matrices for shadow map generation - the light's
|
|
|
|
// projection and transformation matrices
|
2006-02-11 01:26:40 +01:00
|
|
|
void ShadowMapInternals::CalcShadowMatrices()
|
2006-01-07 02:04:26 +01:00
|
|
|
{
|
2006-02-11 01:26:40 +01:00
|
|
|
CRenderer& renderer = g_Renderer;
|
2006-01-07 02:04:26 +01:00
|
|
|
|
2006-03-26 23:58:48 +02:00
|
|
|
float minZ = ShadowBound[0].Z;
|
|
|
|
|
|
|
|
ShadowBound.IntersectFrustumConservative(LightspaceCamera.GetFrustum());
|
|
|
|
|
|
|
|
// 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
|
|
|
|
// a shadow generator, and small enough not to affect Z precision
|
|
|
|
ShadowBound[0].Z = minZ - 2.0;
|
|
|
|
|
2006-02-11 01:26:40 +01:00
|
|
|
// Setup orthogonal projection (lightspace -> clip space) for shadowmap rendering
|
|
|
|
CVector3D scale = ShadowBound[1] - ShadowBound[0];
|
|
|
|
CVector3D shift = (ShadowBound[1] + ShadowBound[0]) * -0.5;
|
2006-01-07 02:04:26 +01:00
|
|
|
|
2006-02-11 01:26:40 +01:00
|
|
|
if (scale.X < 1.0)
|
|
|
|
scale.X = 1.0;
|
|
|
|
if (scale.Y < 1.0)
|
|
|
|
scale.Y = 1.0;
|
|
|
|
if (scale.Z < 1.0)
|
|
|
|
scale.Z = 1.0;
|
2006-01-07 02:04:26 +01:00
|
|
|
|
2006-02-11 01:26:40 +01:00
|
|
|
scale.X = 2.0 / scale.X;
|
|
|
|
scale.Y = 2.0 / scale.Y;
|
|
|
|
scale.Z = 2.0 / scale.Z;
|
2006-01-07 02:04:26 +01:00
|
|
|
|
2006-02-11 01:26:40 +01:00
|
|
|
LightProjection.SetZero();
|
|
|
|
LightProjection._11 = scale.X;
|
|
|
|
LightProjection._14 = shift.X * scale.X;
|
|
|
|
LightProjection._22 = scale.Y;
|
|
|
|
LightProjection._24 = shift.Y * scale.Y;
|
|
|
|
LightProjection._33 = scale.Z;
|
2006-02-13 15:18:20 +01:00
|
|
|
LightProjection._34 = shift.Z * scale.Z + renderer.m_ShadowZBias;
|
2006-02-11 01:26:40 +01:00
|
|
|
LightProjection._44 = 1.0;
|
|
|
|
|
|
|
|
|
|
|
|
// Calculate texture matrix by creating the clip space to texture coordinate matrix
|
|
|
|
// and then concatenating all matrices that have been calculated so far
|
2006-02-13 15:18:20 +01:00
|
|
|
CMatrix3D lightToTex;
|
2006-03-26 19:36:33 +02:00
|
|
|
float texscalex = (float)EffectiveWidth / (float)Width;
|
|
|
|
float texscaley = (float)EffectiveHeight / (float)Height;
|
2006-02-13 15:18:20 +01:00
|
|
|
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);
|
|
|
|
|
|
|
|
lightToTex.SetZero();
|
|
|
|
lightToTex._11 = texscalex;
|
|
|
|
lightToTex._14 = -ShadowBound[0].X * texscalex;
|
|
|
|
lightToTex._22 = texscaley;
|
|
|
|
lightToTex._24 = -ShadowBound[0].Y * texscaley;
|
|
|
|
lightToTex._33 = texscalez;
|
|
|
|
lightToTex._34 = -ShadowBound[0].Z * texscalez;
|
|
|
|
lightToTex._44 = 1.0;
|
|
|
|
|
|
|
|
TextureMatrix = lightToTex * LightTransform;
|
2006-01-07 02:04:26 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2006-05-31 18:42:50 +02:00
|
|
|
//////////////////////////////////////////////////////////////////////////
|
2006-02-11 01:26:40 +01:00
|
|
|
// Create the shadow map
|
|
|
|
void ShadowMapInternals::CreateTexture()
|
2006-01-07 02:04:26 +01:00
|
|
|
{
|
2006-03-26 19:36:33 +02:00
|
|
|
// Cleanup
|
2006-02-11 01:26:40 +01:00
|
|
|
if (Texture)
|
2006-03-26 19:36:33 +02:00
|
|
|
{
|
2006-02-11 01:26:40 +01:00
|
|
|
glDeleteTextures(1, &Texture);
|
2006-03-26 19:36:33 +02:00
|
|
|
Texture = 0;
|
|
|
|
}
|
|
|
|
if (Framebuffer)
|
|
|
|
{
|
|
|
|
pglDeleteFramebuffersEXT(1, &Framebuffer);
|
|
|
|
Framebuffer = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Prepare FBO if available
|
|
|
|
// Note: luminance is not an RGB format, so a luminance texture cannot be used
|
|
|
|
// as a color buffer
|
|
|
|
if (UseDepthTexture && g_Renderer.GetCapabilities().m_FramebufferObject)
|
|
|
|
{
|
|
|
|
pglGenFramebuffersEXT(1, &Framebuffer);
|
|
|
|
}
|
2006-02-11 01:26:40 +01:00
|
|
|
|
2007-02-09 18:04:55 +01:00
|
|
|
if (g_Renderer.m_ShadowMapSize != 0)
|
|
|
|
{
|
|
|
|
// non-default option to override the size
|
|
|
|
Width = Height = g_Renderer.m_ShadowMapSize;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// get shadow map size as next power of two up from view width and height
|
2010-01-01 16:33:07 +01:00
|
|
|
Width = (int)round_up_to_pow2((unsigned)g_Renderer.GetWidth());
|
|
|
|
Height = (int)round_up_to_pow2((unsigned)g_Renderer.GetHeight());
|
2007-02-09 18:04:55 +01:00
|
|
|
}
|
|
|
|
// Clamp to the maximum texture size
|
|
|
|
Width = std::min(Width, (int)ogl_max_tex_size);
|
|
|
|
Height = std::min(Height, (int)ogl_max_tex_size);
|
2006-03-26 19:36:33 +02:00
|
|
|
|
2007-02-09 18:04:55 +01:00
|
|
|
// If we're using a framebuffer object, the whole texture is available; otherwise
|
|
|
|
// we're limited to the part of the screen buffer that is actually visible
|
2006-03-26 19:36:33 +02:00
|
|
|
if (Framebuffer)
|
|
|
|
{
|
|
|
|
EffectiveWidth = Width;
|
|
|
|
EffectiveHeight = Height;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2007-02-09 18:04:55 +01:00
|
|
|
EffectiveWidth = std::min(Width, g_Renderer.GetWidth());
|
|
|
|
EffectiveHeight = std::min(Height, g_Renderer.GetHeight());
|
2006-03-26 19:36:33 +02:00
|
|
|
}
|
2006-02-11 01:26:40 +01:00
|
|
|
|
2006-02-16 00:50:24 +01:00
|
|
|
const char* formatname = "LUMINANCE";
|
|
|
|
|
|
|
|
if (UseDepthTexture)
|
|
|
|
{
|
2007-02-09 18:04:55 +01:00
|
|
|
switch(DepthTextureBits)
|
|
|
|
{
|
2006-02-16 00:50:24 +01:00
|
|
|
case 16: formatname = "DEPTH_COMPONENT16"; break;
|
|
|
|
case 24: formatname = "DEPTH_COMPONENT24"; break;
|
|
|
|
case 32: formatname = "DEPTH_COMPONENT32"; break;
|
|
|
|
default: formatname = "DEPTH_COMPONENT"; break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-11-03 22:46:35 +01:00
|
|
|
LOG(CLogger::Normal, LOG_CATEGORY, L"Creating shadow texture (size %dx%d) (format = %hs)%ls",
|
|
|
|
Width, Height, formatname, Framebuffer ? L" (using EXT_framebuffer_object)" : L"");
|
2006-02-11 19:04:32 +01:00
|
|
|
|
2006-02-11 01:26:40 +01:00
|
|
|
// create texture object
|
|
|
|
glGenTextures(1, &Texture);
|
|
|
|
g_Renderer.BindTexture(0, Texture);
|
|
|
|
|
2007-02-09 18:04:55 +01:00
|
|
|
int size = Width*Height;
|
2006-02-11 19:04:32 +01:00
|
|
|
|
|
|
|
if (UseDepthTexture)
|
|
|
|
{
|
2006-02-16 00:50:24 +01:00
|
|
|
GLenum format;
|
|
|
|
|
2007-02-09 18:04:55 +01:00
|
|
|
switch(DepthTextureBits)
|
|
|
|
{
|
2006-02-16 00:50:24 +01:00
|
|
|
case 16: format = GL_DEPTH_COMPONENT16; break;
|
|
|
|
case 24: format = GL_DEPTH_COMPONENT24; break;
|
|
|
|
case 32: format = GL_DEPTH_COMPONENT32; break;
|
|
|
|
default: format = GL_DEPTH_COMPONENT; break;
|
|
|
|
}
|
|
|
|
|
2006-02-11 19:04:32 +01:00
|
|
|
float* buf = new float[size];
|
2007-02-09 18:04:55 +01:00
|
|
|
for (int i = 0; i < size; i++)
|
|
|
|
buf[i] = 1.0;
|
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0, format, Width, Height, 0, GL_DEPTH_COMPONENT, GL_FLOAT, buf);
|
2006-02-11 19:04:32 +01:00
|
|
|
delete[] buf;
|
2006-02-14 21:38:51 +01:00
|
|
|
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_DEPTH_TEXTURE_MODE, GL_LUMINANCE);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);
|
2006-02-11 19:04:32 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2007-02-09 18:04:55 +01:00
|
|
|
u32* buf = new u32[size];
|
|
|
|
for (int i = 0; i < size; i++)
|
|
|
|
buf[i] = 0x00ffffff;
|
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE8, Width, Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, buf);
|
2006-02-11 19:04:32 +01:00
|
|
|
delete[] buf;
|
|
|
|
}
|
2006-02-11 01:26:40 +01:00
|
|
|
|
|
|
|
// set texture parameters
|
|
|
|
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
2006-02-11 19:04:32 +01:00
|
|
|
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
2006-03-26 19:36:33 +02:00
|
|
|
|
|
|
|
// bind to framebuffer object
|
|
|
|
if (Framebuffer)
|
|
|
|
{
|
|
|
|
debug_assert(UseDepthTexture);
|
|
|
|
|
|
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
|
|
pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, Framebuffer);
|
|
|
|
|
|
|
|
pglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,
|
|
|
|
GL_TEXTURE_2D, Texture, 0);
|
|
|
|
|
|
|
|
glDrawBuffer(GL_NONE);
|
|
|
|
glReadBuffer(GL_NONE);
|
|
|
|
|
|
|
|
GLenum status = pglCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
|
|
|
|
|
|
|
|
pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
|
|
|
|
|
|
|
|
if (status != GL_FRAMEBUFFER_COMPLETE_EXT)
|
|
|
|
{
|
2009-11-03 22:46:35 +01:00
|
|
|
LOG(CLogger::Warning, LOG_CATEGORY, L"Framebuffer object incomplete: %04d", status);
|
2006-03-26 19:36:33 +02:00
|
|
|
|
|
|
|
pglDeleteFramebuffersEXT(1, &Framebuffer);
|
|
|
|
Framebuffer = 0;
|
2007-02-09 18:04:55 +01:00
|
|
|
EffectiveWidth = std::min(Width, g_Renderer.GetWidth());
|
|
|
|
EffectiveHeight = std::min(Height, g_Renderer.GetHeight());
|
2006-03-26 19:36:33 +02:00
|
|
|
}
|
|
|
|
}
|
2006-01-07 02:04:26 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Set up to render into shadow map texture
|
|
|
|
void ShadowMap::BeginRender()
|
|
|
|
{
|
|
|
|
// HACK HACK: this depends in non-obvious ways on the behaviour of the caller
|
2006-01-29 18:34:45 +01:00
|
|
|
|
2006-02-11 01:26:40 +01:00
|
|
|
// Calc remaining shadow matrices
|
|
|
|
m->CalcShadowMatrices();
|
|
|
|
|
2006-03-26 19:36:33 +02:00
|
|
|
if (m->Framebuffer)
|
|
|
|
{
|
|
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
|
|
pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m->Framebuffer);
|
|
|
|
}
|
|
|
|
|
2006-01-07 02:04:26 +01:00
|
|
|
// clear buffers
|
2006-02-11 19:04:32 +01:00
|
|
|
if (m->UseDepthTexture)
|
|
|
|
{
|
2006-03-26 19:36:33 +02:00
|
|
|
glClear(GL_DEPTH_BUFFER_BIT);
|
2006-02-11 19:04:32 +01:00
|
|
|
glColorMask(0,0,0,0);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
glClearColor(1,1,1,0);
|
2006-03-26 19:36:33 +02:00
|
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
2006-02-11 19:04:32 +01:00
|
|
|
}
|
2006-01-07 02:04:26 +01:00
|
|
|
|
|
|
|
// setup viewport
|
2006-03-26 19:36:33 +02:00
|
|
|
glViewport(0, 0, m->EffectiveWidth, m->EffectiveHeight);
|
2006-01-07 02:04:26 +01:00
|
|
|
|
|
|
|
glMatrixMode(GL_PROJECTION);
|
|
|
|
glPushMatrix();
|
|
|
|
glLoadMatrixf(&m->LightProjection._11);
|
|
|
|
|
|
|
|
glMatrixMode(GL_MODELVIEW);
|
|
|
|
glPushMatrix();
|
|
|
|
glLoadMatrixf(&m->LightTransform._11);
|
|
|
|
|
|
|
|
glEnable(GL_SCISSOR_TEST);
|
2006-03-26 19:36:33 +02:00
|
|
|
glScissor(1,1, m->EffectiveWidth-2, m->EffectiveHeight-2);
|
2006-01-07 02:04:26 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Finish rendering into shadow map texture
|
|
|
|
void ShadowMap::EndRender()
|
|
|
|
{
|
|
|
|
glDisable(GL_SCISSOR_TEST);
|
|
|
|
|
|
|
|
// copy result into shadow map texture
|
2006-03-26 19:36:33 +02:00
|
|
|
if (m->Framebuffer)
|
|
|
|
{
|
|
|
|
pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
|
|
|
|
}
|
|
|
|
else
|
2006-02-16 00:50:24 +01:00
|
|
|
{
|
2006-03-26 19:36:33 +02:00
|
|
|
if (!g_Renderer.GetDisableCopyShadow())
|
|
|
|
{
|
|
|
|
g_Renderer.BindTexture(0, m->Texture);
|
2007-02-09 18:04:55 +01:00
|
|
|
glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, m->EffectiveWidth, m->EffectiveHeight);
|
2006-03-26 19:36:33 +02:00
|
|
|
}
|
2006-02-16 00:50:24 +01:00
|
|
|
}
|
2006-01-07 02:04:26 +01:00
|
|
|
|
2006-03-26 19:36:33 +02:00
|
|
|
glViewport(0, 0, g_Renderer.GetWidth(), g_Renderer.GetHeight());
|
|
|
|
|
2006-02-11 19:04:32 +01:00
|
|
|
if (m->UseDepthTexture)
|
|
|
|
{
|
|
|
|
glColorMask(1,1,1,1);
|
|
|
|
}
|
|
|
|
|
2006-01-07 02:04:26 +01:00
|
|
|
// restore matrix stack
|
|
|
|
glPopMatrix();
|
|
|
|
glMatrixMode(GL_PROJECTION);
|
|
|
|
glPopMatrix();
|
|
|
|
glMatrixMode(GL_MODELVIEW);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Retrieve the texture handle and texture matrix for shadowing
|
2006-03-26 01:54:20 +01:00
|
|
|
GLuint ShadowMap::GetTexture() const
|
2006-01-07 02:04:26 +01:00
|
|
|
{
|
|
|
|
return m->Texture;
|
|
|
|
}
|
|
|
|
|
2006-03-26 01:54:20 +01:00
|
|
|
const CMatrix3D& ShadowMap::GetTextureMatrix() const
|
2006-01-07 02:04:26 +01:00
|
|
|
{
|
|
|
|
return m->TextureMatrix;
|
|
|
|
}
|
2006-01-29 18:34:45 +01:00
|
|
|
|
|
|
|
|
2006-02-11 19:04:32 +01:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Using depth textures vs. a simple luminance map
|
2006-03-26 01:54:20 +01:00
|
|
|
bool ShadowMap::GetUseDepthTexture() const
|
2006-02-11 19:04:32 +01:00
|
|
|
{
|
|
|
|
return m->UseDepthTexture;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ShadowMap::SetUseDepthTexture(bool depthTexture)
|
|
|
|
{
|
|
|
|
if (depthTexture)
|
|
|
|
{
|
|
|
|
if (!g_Renderer.GetCapabilities().m_DepthTextureShadows)
|
|
|
|
{
|
2009-11-03 22:46:35 +01:00
|
|
|
LOG(CLogger::Warning, LOG_CATEGORY, L"Depth textures are not supported by your graphics card/driver. Fallback to luminance map (no self-shadowing)!");
|
2006-02-11 19:04:32 +01:00
|
|
|
depthTexture = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (depthTexture != m->UseDepthTexture)
|
|
|
|
{
|
|
|
|
if (m->Texture)
|
|
|
|
{
|
|
|
|
glDeleteTextures(1, &m->Texture);
|
|
|
|
m->Texture = 0;
|
|
|
|
}
|
|
|
|
m->Width = m->Height = 0;
|
|
|
|
|
|
|
|
m->UseDepthTexture = depthTexture;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-02-16 00:50:24 +01:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Depth texture bits
|
|
|
|
int ShadowMap::GetDepthTextureBits() const
|
|
|
|
{
|
|
|
|
return m->DepthTextureBits;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ShadowMap::SetDepthTextureBits(int bits)
|
|
|
|
{
|
|
|
|
if (bits != m->DepthTextureBits)
|
|
|
|
{
|
|
|
|
if (m->Texture)
|
|
|
|
{
|
|
|
|
glDeleteTextures(1, &m->Texture);
|
|
|
|
m->Texture = 0;
|
|
|
|
}
|
|
|
|
m->Width = m->Height = 0;
|
|
|
|
|
|
|
|
m->DepthTextureBits = bits;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-01-29 18:34:45 +01:00
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
// RenderDebugDisplay: debug visualizations
|
|
|
|
// - blue: objects in shadow
|
|
|
|
void ShadowMap::RenderDebugDisplay()
|
|
|
|
{
|
2006-02-11 01:26:40 +01:00
|
|
|
glDepthMask(0);
|
|
|
|
glDisable(GL_CULL_FACE);
|
|
|
|
|
|
|
|
// Render shadow bound
|
2006-01-29 18:34:45 +01:00
|
|
|
glMatrixMode(GL_MODELVIEW);
|
|
|
|
glPushMatrix();
|
|
|
|
glMultMatrixf(&m->InvLightTransform._11);
|
|
|
|
|
|
|
|
glEnable(GL_BLEND);
|
|
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
|
|
glColor4ub(0,0,255,64);
|
2006-02-11 01:26:40 +01:00
|
|
|
m->ShadowBound.Render();
|
2006-01-29 18:34:45 +01:00
|
|
|
glDisable(GL_BLEND);
|
|
|
|
|
|
|
|
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
|
|
|
|
glColor3ub(0,0,255);
|
2006-02-11 01:26:40 +01:00
|
|
|
m->ShadowBound.Render();
|
2006-01-29 18:34:45 +01:00
|
|
|
|
|
|
|
glBegin(GL_LINES);
|
|
|
|
glVertex3f(0.0, 0.0, 0.0);
|
|
|
|
glVertex3f(0.0, 0.0, 50.0);
|
|
|
|
glEnd();
|
|
|
|
glBegin(GL_POLYGON);
|
|
|
|
glVertex3f(0.0, 0.0, 50.0);
|
|
|
|
glVertex3f(50.0, 0.0, 50.0);
|
|
|
|
glVertex3f(0.0, 50.0, 50.0);
|
|
|
|
glEnd();
|
|
|
|
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
|
2006-02-11 01:26:40 +01:00
|
|
|
glPopMatrix();
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
CMatrix3D InvTexTransform;
|
|
|
|
|
|
|
|
m->TextureMatrix.GetInverse(InvTexTransform);
|
|
|
|
|
|
|
|
// Render representative texture rectangle
|
|
|
|
glPushMatrix();
|
|
|
|
glMultMatrixf(&InvTexTransform._11);
|
|
|
|
|
|
|
|
glEnable(GL_BLEND);
|
|
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
|
|
glColor4ub(255,0,0,64);
|
|
|
|
glBegin(GL_QUADS);
|
|
|
|
glVertex3f(0.0, 0.0, 0.0);
|
|
|
|
glVertex3f(1.0, 0.0, 0.0);
|
|
|
|
glVertex3f(1.0, 1.0, 0.0);
|
|
|
|
glVertex3f(0.0, 1.0, 0.0);
|
|
|
|
glEnd();
|
|
|
|
glDisable(GL_BLEND);
|
|
|
|
|
|
|
|
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
|
|
|
|
glColor3ub(255,0,0);
|
|
|
|
glBegin(GL_QUADS);
|
|
|
|
glVertex3f(0.0, 0.0, 0.0);
|
|
|
|
glVertex3f(1.0, 0.0, 0.0);
|
|
|
|
glVertex3f(1.0, 1.0, 0.0);
|
|
|
|
glVertex3f(0.0, 1.0, 0.0);
|
|
|
|
glEnd();
|
|
|
|
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
|
|
|
|
glPopMatrix();
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Render the shadow map
|
|
|
|
glMatrixMode(GL_PROJECTION);
|
|
|
|
glPushMatrix();
|
|
|
|
glLoadIdentity();
|
|
|
|
glOrtho(0.0, 1.0, 1.0, 0.0, -1.0, 1.0);
|
|
|
|
glMatrixMode(GL_MODELVIEW);
|
|
|
|
glPushMatrix();
|
|
|
|
glLoadIdentity();
|
|
|
|
|
|
|
|
glDisable(GL_DEPTH_TEST);
|
|
|
|
g_Renderer.BindTexture(0, m->Texture);
|
2006-02-11 19:04:32 +01:00
|
|
|
if (m->UseDepthTexture)
|
|
|
|
{
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_NONE);
|
|
|
|
}
|
2006-02-19 22:16:54 +01:00
|
|
|
glColor3f(1.0f, 1.0f, 1.0f);
|
2006-02-11 01:26:40 +01:00
|
|
|
glBegin(GL_QUADS);
|
2006-02-19 22:16:54 +01:00
|
|
|
glTexCoord2f(0.0f, 0.0f); glVertex2f(0.0f, 0.0f);
|
|
|
|
glTexCoord2f(1.0f, 0.0f); glVertex2f(0.2f, 0.0f);
|
|
|
|
glTexCoord2f(1.0f, 1.0f); glVertex2f(0.2f, 0.2f);
|
|
|
|
glTexCoord2f(0.0f, 1.0f); glVertex2f(0.0f, 0.2f);
|
2006-02-11 01:26:40 +01:00
|
|
|
glEnd();
|
2006-02-14 21:38:51 +01:00
|
|
|
if (m->UseDepthTexture)
|
|
|
|
{
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE);
|
|
|
|
}
|
2006-02-11 01:26:40 +01:00
|
|
|
|
|
|
|
glEnable(GL_CULL_FACE);
|
2006-01-29 18:34:45 +01:00
|
|
|
|
2006-02-11 01:26:40 +01:00
|
|
|
glMatrixMode(GL_PROJECTION);
|
|
|
|
glPopMatrix();
|
|
|
|
glMatrixMode(GL_MODELVIEW);
|
2006-01-29 18:34:45 +01:00
|
|
|
glPopMatrix();
|
2006-02-11 01:26:40 +01:00
|
|
|
|
|
|
|
glEnable(GL_DEPTH_TEST);
|
|
|
|
glDepthMask(1);
|
2006-01-29 18:34:45 +01:00
|
|
|
}
|