forked from 0ad/0ad
683 lines
19 KiB
C++
683 lines
19 KiB
C++
/* Copyright (C) 2012 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/>.
|
|
*/
|
|
|
|
/*
|
|
* Shadow mapping related texture and matrix management
|
|
*/
|
|
|
|
#include "precompiled.h"
|
|
|
|
#include "gui/GUIutil.h"
|
|
#include "lib/bits.h"
|
|
#include "lib/ogl.h"
|
|
#include "ps/CLogger.h"
|
|
#include "ps/Profile.h"
|
|
|
|
#include "graphics/LightEnv.h"
|
|
#include "graphics/ShaderManager.h"
|
|
|
|
#include "maths/BoundingBoxAligned.h"
|
|
#include "maths/MathUtil.h"
|
|
#include "maths/Matrix3D.h"
|
|
|
|
#include "renderer/Renderer.h"
|
|
#include "renderer/ShadowMap.h"
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// ShadowMap implementation
|
|
|
|
/**
|
|
* Struct ShadowMapInternals: Internal data for the ShadowMap implementation
|
|
*/
|
|
struct ShadowMapInternals
|
|
{
|
|
// bit depth for the depth texture
|
|
int DepthTextureBits;
|
|
// the EXT_framebuffer_object framebuffer
|
|
GLuint Framebuffer;
|
|
// handle of shadow map
|
|
GLuint Texture;
|
|
// width, height of shadow map
|
|
int Width, Height;
|
|
// used width, height of shadow map
|
|
int EffectiveWidth, EffectiveHeight;
|
|
// 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
|
|
CMatrix3D LightProjection;
|
|
// Transform world space into light space; calculated on SetupFrame
|
|
CMatrix3D LightTransform;
|
|
// Transform world space into texture space of the shadow map;
|
|
// calculated on BeginRender, after the final shadow bounds are known
|
|
CMatrix3D TextureMatrix;
|
|
|
|
// transform light space into world space
|
|
CMatrix3D InvLightTransform;
|
|
// bounding box of shadowed objects in light space
|
|
CBoundingBoxAligned ShadowBound;
|
|
|
|
// Camera transformed into light space
|
|
CCamera LightspaceCamera;
|
|
|
|
// Some drivers (at least some Intel Mesa ones) appear to handle alpha testing
|
|
// incorrectly when the FBO has only a depth attachment.
|
|
// When m_ShadowAlphaFix is true, we use DummyTexture to store a useless
|
|
// alpha texture which is attached to the FBO as a workaround.
|
|
GLuint DummyTexture;
|
|
|
|
// Copy of renderer's standard view camera, saved between
|
|
// BeginRender and EndRender while we replace it with the shadow camera
|
|
CCamera SavedViewCamera;
|
|
|
|
// Helper functions
|
|
void CalcShadowMatrices();
|
|
void CreateTexture();
|
|
};
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Construction/Destruction
|
|
ShadowMap::ShadowMap()
|
|
{
|
|
m = new ShadowMapInternals;
|
|
m->Framebuffer = 0;
|
|
m->Texture = 0;
|
|
m->DummyTexture = 0;
|
|
m->Width = 0;
|
|
m->Height = 0;
|
|
m->EffectiveWidth = 0;
|
|
m->EffectiveHeight = 0;
|
|
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.)
|
|
|
|
// Avoid using uninitialised values in AddShadowedBound if SetupFrame wasn't called first
|
|
m->LightTransform.SetIdentity();
|
|
}
|
|
|
|
|
|
ShadowMap::~ShadowMap()
|
|
{
|
|
if (m->Texture)
|
|
glDeleteTextures(1, &m->Texture);
|
|
if (m->DummyTexture)
|
|
glDeleteTextures(1, &m->DummyTexture);
|
|
if (m->Framebuffer)
|
|
pglDeleteFramebuffersEXT(1, &m->Framebuffer);
|
|
|
|
delete m;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// 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->DummyTexture)
|
|
glDeleteTextures(1, &m->DummyTexture);
|
|
if (m->Framebuffer)
|
|
pglDeleteFramebuffersEXT(1, &m->Framebuffer);
|
|
|
|
m->Texture = 0;
|
|
m->DummyTexture = 0;
|
|
m->Framebuffer = 0;
|
|
|
|
// (Texture will be constructed in next SetupFrame)
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// SetupFrame: camera and light direction for this frame
|
|
void ShadowMap::SetupFrame(const CCamera& camera, const CVector3D& lightdir)
|
|
{
|
|
if (!m->Texture)
|
|
m->CreateTexture();
|
|
|
|
CVector3D z = lightdir;
|
|
CVector3D y;
|
|
CVector3D x = camera.m_Orientation.GetIn();
|
|
CVector3D eyepos = camera.m_Orientation.GetTranslation();
|
|
|
|
z.Normalize();
|
|
x -= z * z.Dot(x);
|
|
if (x.Length() < 0.001)
|
|
{
|
|
// 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
|
|
m->LightTransform._11 = x.X;
|
|
m->LightTransform._12 = x.Y;
|
|
m->LightTransform._13 = x.Z;
|
|
|
|
// Y axis perpendicular to light and view direction
|
|
m->LightTransform._21 = y.X;
|
|
m->LightTransform._22 = y.Y;
|
|
m->LightTransform._23 = y.Z;
|
|
|
|
// Z axis is in direction of light
|
|
m->LightTransform._31 = z.X;
|
|
m->LightTransform._32 = z.Y;
|
|
m->LightTransform._33 = z.Z;
|
|
|
|
// eye is at the origin of the coordinate system
|
|
m->LightTransform._14 = -x.Dot(eyepos);
|
|
m->LightTransform._24 = -y.Dot(eyepos);
|
|
m->LightTransform._34 = -z.Dot(eyepos);
|
|
|
|
m->LightTransform._41 = 0.0;
|
|
m->LightTransform._42 = 0.0;
|
|
m->LightTransform._43 = 0.0;
|
|
m->LightTransform._44 = 1.0;
|
|
|
|
m->LightTransform.GetInverse(m->InvLightTransform);
|
|
m->ShadowBound.SetEmpty();
|
|
|
|
//
|
|
m->LightspaceCamera = camera;
|
|
m->LightspaceCamera.m_Orientation = m->LightTransform * camera.m_Orientation;
|
|
m->LightspaceCamera.UpdateFrustum();
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// AddShadowedBound: add a world-space bounding box to the bounds of shadowed
|
|
// objects
|
|
void ShadowMap::AddShadowedBound(const CBoundingBoxAligned& bounds)
|
|
{
|
|
CBoundingBoxAligned lightspacebounds;
|
|
|
|
bounds.Transform(m->LightTransform, lightspacebounds);
|
|
m->ShadowBound += lightspacebounds;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// CalcShadowMatrices: calculate required matrices for shadow map generation - the light's
|
|
// projection and transformation matrices
|
|
void ShadowMapInternals::CalcShadowMatrices()
|
|
{
|
|
CRenderer& renderer = g_Renderer;
|
|
float minZ = ShadowBound[0].Z;
|
|
|
|
ShadowBound.IntersectFrustumConservative(LightspaceCamera.GetFrustum());
|
|
|
|
// ShadowBound might have been empty to begin with, producing an empty result
|
|
if (ShadowBound.IsEmpty())
|
|
{
|
|
// no-op
|
|
LightProjection.SetIdentity();
|
|
TextureMatrix = LightTransform;
|
|
return;
|
|
}
|
|
|
|
// 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 accidentally miss
|
|
// a shadow generator, and small enough not to affect Z precision
|
|
ShadowBound[0].Z = minZ - 2.0;
|
|
|
|
// Setup orthogonal projection (lightspace -> clip space) for shadowmap rendering
|
|
CVector3D scale = ShadowBound[1] - ShadowBound[0];
|
|
CVector3D shift = (ShadowBound[1] + ShadowBound[0]) * -0.5;
|
|
|
|
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;
|
|
|
|
scale.X = 2.0 / scale.X;
|
|
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 + offsetX) * scale.X;
|
|
LightProjection._22 = 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;
|
|
|
|
// Calculate texture matrix by creating the clip space to texture coordinate matrix
|
|
// and then concatenating all matrices that have been calculated so far
|
|
|
|
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;
|
|
|
|
CMatrix3D lightToTex;
|
|
lightToTex.SetZero();
|
|
lightToTex._11 = texscalex;
|
|
lightToTex._14 = (offsetX - ShadowBound[0].X) * texscalex;
|
|
lightToTex._22 = texscaley;
|
|
lightToTex._24 = (offsetY - ShadowBound[0].Y) * texscaley;
|
|
lightToTex._33 = texscalez;
|
|
lightToTex._34 = -ShadowBound[0].Z * texscalez;
|
|
lightToTex._44 = 1.0;
|
|
|
|
TextureMatrix = lightToTex * LightTransform;
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Create the shadow map
|
|
void ShadowMapInternals::CreateTexture()
|
|
{
|
|
// Cleanup
|
|
if (Texture)
|
|
{
|
|
glDeleteTextures(1, &Texture);
|
|
Texture = 0;
|
|
}
|
|
if (DummyTexture)
|
|
{
|
|
glDeleteTextures(1, &DummyTexture);
|
|
DummyTexture = 0;
|
|
}
|
|
if (Framebuffer)
|
|
{
|
|
pglDeleteFramebuffersEXT(1, &Framebuffer);
|
|
Framebuffer = 0;
|
|
}
|
|
|
|
pglGenFramebuffersEXT(1, &Framebuffer);
|
|
|
|
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/height
|
|
Width = Height = (int)round_up_to_pow2((unsigned)std::max(g_Renderer.GetWidth(), g_Renderer.GetHeight()));
|
|
}
|
|
// Clamp to the maximum texture size
|
|
Width = std::min(Width, (int)ogl_max_tex_size);
|
|
Height = std::min(Height, (int)ogl_max_tex_size);
|
|
|
|
// Since we're using a framebuffer object, the whole texture is available
|
|
EffectiveWidth = Width;
|
|
EffectiveHeight = Height;
|
|
|
|
const char* formatname;
|
|
|
|
switch(DepthTextureBits)
|
|
{
|
|
case 16: formatname = "DEPTH_COMPONENT16"; break;
|
|
case 24: formatname = "DEPTH_COMPONENT24"; break;
|
|
case 32: formatname = "DEPTH_COMPONENT32"; break;
|
|
default: formatname = "DEPTH_COMPONENT"; break;
|
|
}
|
|
|
|
LOGMESSAGE(L"Creating shadow texture (size %dx%d) (format = %hs)",
|
|
Width, Height, formatname);
|
|
|
|
|
|
if (g_Renderer.m_Options.m_ShadowAlphaFix)
|
|
{
|
|
glGenTextures(1, &DummyTexture);
|
|
g_Renderer.BindTexture(0, DummyTexture);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, Width, Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
|
|
}
|
|
|
|
glGenTextures(1, &Texture);
|
|
g_Renderer.BindTexture(0, Texture);
|
|
|
|
GLenum format;
|
|
|
|
#if CONFIG2_GLES
|
|
format = GL_DEPTH_COMPONENT;
|
|
#else
|
|
switch (DepthTextureBits)
|
|
{
|
|
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;
|
|
}
|
|
#endif
|
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0, format, Width, Height, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, NULL);
|
|
// GLES requires type == UNSIGNED_SHORT or UNSIGNED_INT
|
|
|
|
// 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);
|
|
|
|
#if CONFIG2_GLES
|
|
// GLES doesn't do depth comparisons, so treat it as a
|
|
// basic unfiltered depth texture
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
#else
|
|
// Enable automatic depth comparisons
|
|
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);
|
|
|
|
// Use GL_LINEAR to trigger automatic PCF on some devices
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
#endif
|
|
|
|
ogl_WarnIfError();
|
|
|
|
// bind to framebuffer object
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, Framebuffer);
|
|
|
|
pglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, Texture, 0);
|
|
|
|
if (g_Renderer.m_Options.m_ShadowAlphaFix)
|
|
{
|
|
pglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, DummyTexture, 0);
|
|
}
|
|
else
|
|
{
|
|
#if CONFIG2_GLES
|
|
#warning TODO: figure out whether the glDrawBuffer/glReadBuffer stuff is needed, since it is not supported by GLES
|
|
#else
|
|
glDrawBuffer(GL_NONE);
|
|
#endif
|
|
}
|
|
|
|
#if !CONFIG2_GLES
|
|
glReadBuffer(GL_NONE);
|
|
#endif
|
|
|
|
ogl_WarnIfError();
|
|
|
|
GLenum status = pglCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
|
|
|
|
pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
|
|
|
|
if (status != GL_FRAMEBUFFER_COMPLETE_EXT)
|
|
{
|
|
LOGWARNING(L"Framebuffer object incomplete: 0x%04X", status);
|
|
|
|
// Disable shadow rendering (but let the user try again if they want)
|
|
g_Renderer.m_Options.m_Shadows = false;
|
|
}
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// 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
|
|
|
|
// Calc remaining shadow matrices
|
|
m->CalcShadowMatrices();
|
|
|
|
{
|
|
PROFILE("bind framebuffer");
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m->Framebuffer);
|
|
}
|
|
|
|
// clear buffers
|
|
{
|
|
PROFILE("clear depth texture");
|
|
// In case we used m_ShadowAlphaFix, we ought to clear the unused
|
|
// color buffer too, else Mali 400 drivers get confused.
|
|
// Might as well clear stencil too for completeness.
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
|
|
glColorMask(0,0,0,0);
|
|
}
|
|
|
|
// setup viewport
|
|
glViewport(0, 0, m->EffectiveWidth, m->EffectiveHeight);
|
|
|
|
m->SavedViewCamera = g_Renderer.GetViewCamera();
|
|
|
|
CCamera c = m->SavedViewCamera;
|
|
c.SetProjection(m->LightProjection);
|
|
c.GetOrientation() = m->InvLightTransform;
|
|
g_Renderer.SetViewCamera(c);
|
|
|
|
#if !CONFIG2_GLES
|
|
glMatrixMode(GL_PROJECTION);
|
|
glLoadMatrixf(&m->LightProjection._11);
|
|
glMatrixMode(GL_MODELVIEW);
|
|
glLoadMatrixf(&m->LightTransform._11);
|
|
#endif
|
|
|
|
glEnable(GL_SCISSOR_TEST);
|
|
glScissor(1,1, m->EffectiveWidth-2, m->EffectiveHeight-2);
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Finish rendering into shadow map texture
|
|
void ShadowMap::EndRender()
|
|
{
|
|
glDisable(GL_SCISSOR_TEST);
|
|
|
|
g_Renderer.SetViewCamera(m->SavedViewCamera);
|
|
|
|
{
|
|
PROFILE("unbind framebuffer");
|
|
pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
|
|
}
|
|
|
|
glViewport(0, 0, g_Renderer.GetWidth(), g_Renderer.GetHeight());
|
|
|
|
glColorMask(1,1,1,1);
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Retrieve the texture handle and texture matrix for shadowing
|
|
GLuint ShadowMap::GetTexture() const
|
|
{
|
|
return m->Texture;
|
|
}
|
|
|
|
const CMatrix3D& ShadowMap::GetTextureMatrix() const
|
|
{
|
|
return m->TextureMatrix;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// 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;
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Depth texture size
|
|
int ShadowMap::GetWidth() const
|
|
{
|
|
return m->Width;
|
|
}
|
|
|
|
int ShadowMap::GetHeight() const
|
|
{
|
|
return m->Height;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
void ShadowMap::RenderDebugBounds()
|
|
{
|
|
CShaderTechniquePtr shaderTech = g_Renderer.GetShaderManager().LoadEffect("gui_solid");
|
|
shaderTech->BeginPass();
|
|
CShaderProgramPtr shader = shaderTech->GetShader();
|
|
|
|
glDepthMask(0);
|
|
glDisable(GL_CULL_FACE);
|
|
|
|
// Render shadow bound
|
|
shader->Uniform("transform", g_Renderer.GetViewCamera().GetViewProjection() * m->InvLightTransform);
|
|
|
|
glEnable(GL_BLEND);
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
shader->Uniform("color", 0.0f, 0.0f, 1.0f, 0.25f);
|
|
m->ShadowBound.Render(shader);
|
|
glDisable(GL_BLEND);
|
|
|
|
shader->Uniform("color", 0.0f, 0.0f, 1.0f, 1.0f);
|
|
m->ShadowBound.RenderOutline(shader);
|
|
|
|
// Draw a funny line/triangle direction indicator thing for unknown reasons
|
|
float shadowLineVerts[] = {
|
|
0.0, 0.0, 0.0,
|
|
0.0, 0.0, 50.0,
|
|
|
|
0.0, 0.0, 50.0,
|
|
50.0, 0.0, 50.0,
|
|
|
|
50.0, 0.0, 50.0,
|
|
0.0, 50.0, 50.0,
|
|
|
|
0.0, 50.0, 50.0,
|
|
0.0, 0.0, 50.0
|
|
};
|
|
shader->VertexPointer(3, GL_FLOAT, 0, shadowLineVerts);
|
|
shader->AssertPointersBound();
|
|
glDrawArrays(GL_LINES, 0, 8);
|
|
|
|
shaderTech->EndPass();
|
|
|
|
#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
|
|
|
|
glEnable(GL_CULL_FACE);
|
|
glDepthMask(1);
|
|
}
|
|
|
|
void ShadowMap::RenderDebugTexture()
|
|
{
|
|
glDepthMask(0);
|
|
|
|
glDisable(GL_DEPTH_TEST);
|
|
|
|
#if !CONFIG2_GLES
|
|
g_Renderer.BindTexture(0, m->Texture);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_NONE);
|
|
#endif
|
|
|
|
CShaderTechniquePtr texTech = g_Renderer.GetShaderManager().LoadEffect("gui_basic");
|
|
texTech->BeginPass();
|
|
CShaderProgramPtr texShader = texTech->GetShader();
|
|
|
|
texShader->Uniform("transform", GetDefaultGuiMatrix());
|
|
texShader->BindTexture("tex", m->Texture);
|
|
|
|
float s = 256.f;
|
|
float boxVerts[] = {
|
|
0,0, 0,s, s,0,
|
|
s,0, 0,s, s,s
|
|
};
|
|
float boxUV[] = {
|
|
0,0, 0,1, 1,0,
|
|
1,0, 0,1, 1,1
|
|
};
|
|
|
|
texShader->VertexPointer(2, GL_FLOAT, 0, boxVerts);
|
|
texShader->TexCoordPointer(GL_TEXTURE0, 2, GL_FLOAT, 0, boxUV);
|
|
texShader->AssertPointersBound();
|
|
glDrawArrays(GL_TRIANGLES, 0, 6);
|
|
|
|
texTech->EndPass();
|
|
|
|
#if !CONFIG2_GLES
|
|
g_Renderer.BindTexture(0, m->Texture);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE);
|
|
#endif
|
|
|
|
glEnable(GL_DEPTH_TEST);
|
|
glDepthMask(1);
|
|
}
|