#include <math.h>
#include "ShadowMappingShader.h"
#include <Cg/cgGL.h>
#ifdef _MB_INTERNAL
#include <klib/kconfigfile.h>
#endif
#ifndef max
#define max(a,b) (((a) > (b)) ? (a) : (b))
#endif
#ifndef min
#define min(a,b) (((a) < (b)) ? (a) : (b))
#endif
namespace Graphics
{
ShadowMappingShader::ShadowMappingShader()
: mShadowPCFKernelSize(9)
, mUseHardwarePCF(false)
, mShadowStrength(1.0f)
{
for(
int i = 0;
i < kMaxLight;
i++ )
{
}
}
ShadowMappingShader::~ShadowMappingShader()
{
UninitializeBuffers();
if ( mShadowProjVertexShader )
{
cgDestroyProgram( mShadowProjVertexShader );
}
}
void ShadowMappingShader::SaveFrameBuffer()
{
glGetIntegerv( GL_FRAMEBUFFER_BINDING, &mLastFramebuffer );
if ( mLastFramebuffer > 0 )
{
glGetIntegerv( GL_MAX_DRAW_BUFFERS, &mMaxDrawBuffers );
GLint lActualUse = 0;
for ( GLint
i = 0;
i < mMaxDrawBuffers &&
i < MAX_DRAW_BUFFERS;
i++ )
{
glGetIntegerv( GL_DRAW_BUFFER0+
i, (GLint *)&(mDrawBuffers[
i]) );
if ( mDrawBuffers[i] == GL_NONE )
{
break;
}
lActualUse ++;
}
mMaxDrawBuffers = lActualUse;
}
else
{
mLastFramebuffer = 0;
}
}
void ShadowMappingShader::RestoreFrameBuffer()
{
if ( mLastFramebuffer > 0 )
{
glBindFramebuffer( GL_FRAMEBUFFER_EXT, mLastFramebuffer );
if ( mMaxDrawBuffers > 0 )
{
glDrawBuffers( mMaxDrawBuffers, &(mDrawBuffers[0]) );
}
}
}
bool ShadowMappingShader::Initialize(const char* pVertexShaderPath, const char* pPixelShaderPath, const char* pVertexProjShaderPath)
{
#ifdef _MB_INTERNAL
KConfigFile ConfigFile(CONFIGFILE);
const char* lConfigHardwarePCF = ConfigFile.GetOrSet( "Display", "UseHardwarePCF", "Yes", "(Yes/No)" );
mUseHardwarePCF = (
_strcmpi(lConfigHardwarePCF,
"Yes")==0);
#else
mUseHardwarePCF = true;
#endif
FBTrace(
"DynamicLightingShadow: %s\n", mUseHardwarePCF ?
"using hardware PCF" :
"hardware PCF not supported");
const char* lShaderOptions = mUseHardwarePCF ? "-DENABLE_SHADOWS=1 -DENABLE_HWPCF=1" : "-DENABLE_SHADOWS=1";
bool lResult = Shader::Initialize( pVertexShaderPath, pPixelShaderPath, lShaderOptions, lShaderOptions );
if ( !lResult )
{
return false;
}
mShadowProjVertexShader = CreateShader(mVertexProfile, pVertexProjShaderPath,
NULL);
if ( !mShadowProjVertexShader )
{
ShowError( cgGetLastListing(mContext) );
return false;
}
mParamShadowProjModel = cgGetNamedParameter( mShadowProjVertexShader, "ShadowModel" );
mParamShadowProjViewProjection = cgGetNamedParameter( mShadowProjVertexShader, "ShadowViewProj" );
mParamShadowPCFKernelSize = cgGetNamedParameter( mPixelShader, "ShadowPCFKernelSize" );
mParamShadowStrength = cgGetNamedParameter( mPixelShader, "ShadowStrength" );
char variableName[64];
for(
int i = 0;
i < kMaxLight; ++
i )
{
mParamShadowMVP[
i] = cgGetNamedParameter( mPixelShader, variableName );
mParamShadowMap[
i] = cgGetNamedParameter( mPixelShader, variableName );
sprintf_s( variableName,
"ShadowMapSize[%d]",
i );
mParamShadowMapSize[
i] = cgGetNamedParameter( mPixelShader, variableName );
assert(mParamShadowMVP[
i]);
assert(mParamShadowMap[i]);
assert(mParamShadowMapSize[i]);
}
return true;
}
bool ShadowMappingShader::InitializeBuffers( int pShadowMapSize, FBArrayTemplate<FBLight*>* pAffectingLightList )
{
SaveFrameBuffer();
int lLightCount = pAffectingLightList->GetCount();
for(
int i = 0;
i < lLightCount;
i++ )
{
FBLight* lLight = pAffectingLightList->GetAt(
i);
if( lShadowSupport )
{
InitializeBuffer(
i, pShadowMapSize );
}
else
{
if( mShadowMapSize[
i] > 0 )
{
}
}
}
for(
int i = lLightCount;
i < kMaxLight;
i++ )
{
if( mShadowMapSize[
i] > 0 )
{
}
}
glBindFramebuffer( GL_FRAMEBUFFER_EXT, 0 );
RestoreFrameBuffer();
return true;
}
bool ShadowMappingShader::InitializeBuffer( int pIndex, int pShadowMapSize )
{
if( pIndex < 0 || pIndex > kMaxLight )
{
return false;
}
if( mShadowMapSize[pIndex] != pShadowMapSize )
{
UninitializeBuffer( pIndex );
glGenTextures( 1, &mShadowMap[pIndex] );
glGenFramebuffers( 1, &mFrameBuffer[pIndex] );
glBindTexture( GL_TEXTURE_2D, mShadowMap[pIndex] );
glTexImage2D( GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32, pShadowMapSize, pShadowMapSize, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, 0 );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER );
float color[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
glTexParameterfv( GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, color );
if (mUseHardwarePCF)
{
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL );
}
else
{
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
}
glBindFramebuffer( GL_FRAMEBUFFER_EXT, mFrameBuffer[pIndex] );
glFramebufferTexture2D( GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, mShadowMap[pIndex], 0 );
glDrawBuffer( GL_FALSE );
glReadBuffer( GL_FALSE );
GLenum status = glCheckFramebufferStatus( GL_FRAMEBUFFER_EXT );
switch( status )
{
case GL_FRAMEBUFFER_COMPLETE_EXT:
break;
case GL_FRAMEBUFFER_UNSUPPORTED_EXT:
default:
ShowError("GL_FRAMEBUFFER_UNSUPPORTED_EXT");
return false;
break;
}
mShadowMapSize[pIndex] = pShadowMapSize;
}
return true;
}
bool ShadowMappingShader::UninitializeBuffers()
{
glDeleteTextures( kMaxLight, mShadowMap );
glDeleteFramebuffers( kMaxLight, mFrameBuffer );
glDeleteRenderbuffers( kMaxLight, mDepthRenderBuffer );
for(
int i = 0;
i < kMaxLight;
i++ )
{
}
return true;
}
bool ShadowMappingShader::UninitializeBuffer( int pIndex )
{
if( pIndex < 0 || pIndex > kMaxLight )
{
return false;
}
glDeleteTextures( 1, &mShadowMap[pIndex] );
glDeleteFramebuffers( 1, &mFrameBuffer[pIndex] );
glDeleteRenderbuffers( 1, &mDepthRenderBuffer[pIndex] );
mShadowMapSize[pIndex] = -1;
return true;
}
void ShadowMappingShader::BeginShading(FBRenderOptions* pRenderOptions, FBArrayTemplate<FBLight*>* pAffectingLightList)
{
Shader::BindShaderPrograms();
UploadParameters(pRenderOptions, pAffectingLightList);
}
void ShadowMappingShader::EndShading()
{
UnsetTextures();
Shader::UnbindShaderPrograms();
}
void ShadowMappingShader::UploadParameters( FBRenderOptions* pRenderOptions, FBArrayTemplate<FBLight*> *pAffectingLightList )
{
Shader::UploadParameters( pRenderOptions, pAffectingLightList );
FBRenderer* lRender = FBSystem::TheOne().Renderer;
FBMatrix lViewMatrix;
lRender->CurrentCamera->GetCameraMatrix( lViewMatrix,
kFBModelView );
if( pAffectingLightList )
{
double lOrigin[4] = { 0.0, 0.0, 0.0, 0.0 };
for(
int i = 0;
i < pAffectingLightList->GetCount();
i++ )
{
FBLight* lLight = pAffectingLightList->GetAt(
i);
{
lLightPos.
Set( lOrigin );
FBMatrix lLightWorldMat;
lLightPos[3] = 0.0;
cgSetParameter4dv( mParamLightPositions[i], lViewPosition.mValue );
}
}
}
for(
int i = 0;
i < kMaxLight;
i++ )
{
if( mShadowMapSize[
i] > 0 )
{
cgGLSetTextureParameter( mParamShadowMap[
i], mShadowMap[i] );
cgGLEnableTextureParameter( mParamShadowMap[i] );
}
else
{
cgGLDisableTextureParameter( mParamShadowMap[
i] );
}
cgSetParameter1i( mParamShadowMapSize[
i], mShadowMapSize[i] );
}
cgSetParameter1i( mParamShadowPCFKernelSize, mShadowPCFKernelSize );
cgSetParameter1f( mParamShadowStrength, mShadowStrength );
}
void ShadowMappingShader::ShaderPassModelDraw (
FBRenderOptions* pRenderOptions,
FBRenderingPass pPass, FBShaderModelInfo* pInfo)
{
FBMatrix modelMatrix;
pInfo->GetFBModel()->GetMatrix(modelMatrix);
for(
int i = 0;
i < kMaxLight;
i++ )
{
FBMatrix modelView;
FBMatrix mvp;
cgSetMatrixParameterdc( mParamShadowMVP[i], mvp);
}
Shader::ShaderPassModelDraw( pRenderOptions, pPass, pInfo );
}
void ShadowMappingShader::UploadShadowParameters( int pLightIndex, FBRenderOptions* pRenderOptions )
{
if( pLightIndex < 0 || pLightIndex > kMaxLight )
{
return;
}
FBMatrix vp;
FBMatrixMult( vp, mLightProjMatrix[pLightIndex], mLightViewMatrix[pLightIndex] );
cgSetMatrixParameterdc( mParamShadowProjViewProjection, vp );
}
void ShadowMappingShader::BindShadowPrograms()
{
cgGLBindProgram(mShadowProjVertexShader);
cgGLBindProgram(0);
cgGLEnableProfile(mVertexProfile);
}
void ShadowMappingShader::UnbindShadowPrograms()
{
UnbindShaderPrograms();
}
void ShadowMappingShader::UnsetTextures()
{
Graphics::Shader::UnsetTextures();
for(
int i = 0;
i < kMaxLight;
i++ )
{
cgGLDisableTextureParameter( mParamShadowMap[
i] );
}
}
bool ShadowMappingShader::RenderShadowMaps( int pShadowMapSize, FBRenderOptions* pRenderOptions, double pOffsetScale, double pOffsetBias, FBArrayTemplate<FBLight*>* pAffectingLightList, FBArrayTemplate<FBModel*>* pShadowCasters )
{
if( !pAffectingLightList || pAffectingLightList->GetCount() == 0 )
{
return false;
}
InitializeBuffers( pShadowMapSize, pAffectingLightList );
SaveFrameBuffer();
ComputeWorldBounds();
glPushAttrib( GL_VIEWPORT_BIT );
glViewport( 0, 0, pShadowMapSize, pShadowMapSize );
glPolygonOffset( GLfloat(pOffsetScale), GLfloat(pOffsetBias) );
glEnable( GL_POLYGON_OFFSET_FILL );
BindShadowPrograms();
int lLightCount = min( kMaxLight, pAffectingLightList->GetCount() );
for(
int i = 0;
i < lLightCount;
i++ )
{
glBindFramebuffer( GL_FRAMEBUFFER_EXT, mFrameBuffer[
i] );
glClearDepth( 1.0 );
glClear( GL_DEPTH_BUFFER_BIT );
FBLight* light = pAffectingLightList->GetAt(i);
ComputeLightMatrices( light, mLightViewMatrix[i], mLightProjMatrix[i] );
UploadShadowParameters( i, pRenderOptions );
RenderShadowModels( pShadowCasters );
}
UnbindShadowPrograms();
glDisable(GL_POLYGON_OFFSET_FILL);
glPopAttrib();
glBindFramebuffer( GL_FRAMEBUFFER_EXT, 0 );
RestoreFrameBuffer();
return true;
}
void ShadowMappingShader::RenderShadowModels( FBArrayTemplate<FBModel*>* pShadowCasters )
{
if( pShadowCasters )
{
for(
int i = 0;
i < pShadowCasters->GetCount();
i++ )
{
FBModel* lModel = pShadowCasters->GetAt(
i);
if( !lModel )
{
continue;
}
FBMatrix lModelMatrix;
lModel->GetMatrix( lModelMatrix );
cgSetMatrixParameterdc( mParamShadowProjModel, lModelMatrix );
RenderShadowModel( lModel );
}
}
else
{
FBRenderer* lRenderer = FBSystem().Renderer;
if( lRenderer )
{
for(
int i = 0;
i < lRenderer->DisplayableGeometryCount;
i++ )
{
FBModel* lModel = lRenderer->GetDisplayableGeometry(
i);
if( !lModel )
{
continue;
}
FBMatrix lModelMatrix;
lModel->GetMatrix( lModelMatrix );
cgSetMatrixParameterdc( mParamShadowProjModel, lModelMatrix );
RenderShadowModel( lModel );
}
}
}
}
void ShadowMappingShader::RenderShadowModel( FBModel* pModel )
{
if( !pModel )
{
return;
}
FBModelVertexData* lModelVertexData = pModel->ModelVertexData;
if( lModelVertexData && lModelVertexData->IsDrawable() )
{
const int lSubRegionCount = lModelVertexData->GetSubRegionCount();
if( lSubRegionCount )
{
lModelVertexData->EnableOGLVertexData();
for (int lSubRegionIndex = 0; lSubRegionIndex < lSubRegionCount; lSubRegionIndex++)
{
lModelVertexData->DrawSubRegion( lSubRegionIndex );
}
lModelVertexData->DisableOGLVertexData();
}
}
}
void ShadowMappingShader::SetShadowPCFKernelSize( int pShadowPCFKernelSize )
{
mShadowPCFKernelSize = pShadowPCFKernelSize;
}
void ShadowMappingShader::SetShadowStrength( float pShadowStrength )
{
mShadowStrength = pShadowStrength;
}
bool ShadowMappingShader::ComputeLightMatrices( FBLight* pLight, FBMatrix& pLightView, FBMatrix& pLightProj )
{
bool status = false;
{
status = ComputeSpotLightMatrices( pLight, pLightView, pLightProj );
}
{
status = ComputeInfiniteLightMatrices( pLight, pLightView, pLightProj );
}
return status;
}
bool ShadowMappingShader::ComputeSpotLightMatrices( FBLight* pLight, FBMatrix& pLightView, FBMatrix& pLightProj )
{
{
return false;
}
pLightView.Identity();
pLightProj.Identity();
{
1.0, 0.0, 0.0, 0.0,
0.0, 0.0, -1.0, 0.0,
0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 0.0, 1.0
};
FBMatrix baseMat( base );
FBMatrix rotationMat;
FBMatrix transformationMat;
transformationMat(3,0) = ((
FBVector3d)pLight->Translation)[0];
transformationMat(3,1) = ((
FBVector3d)pLight->Translation)[1];
transformationMat(3,2) = ((
FBVector3d)pLight->Translation)[2];
transformationMat(3,3) = 1.0f;
float fov = 1.2f * pLight->ConeAngle / 2.0f;
float fFar = (double) pLight->Intensity * 2.0f;
float fNear = 1.0f;
float top = tan(fov*3.14159f/360.0f) * fNear;
float bottom = -top;
float left = bottom;
float right = top;
double perspectiveValues[16] =
{
(2.0*fNear)/(right-left), 0, 0, 0,
0, (2.0*fNear)/(top-bottom), 0, 0,
0, 0, -(fFar+fNear)/(fFar-fNear), -(2.0f*fFar*fNear)/(fFar-fNear),
0, 0, -1.0f, 0
};
pLightProj = FBMatrix( perspectiveValues );
return true;
}
bool ShadowMappingShader::ComputeInfiniteLightMatrices( FBLight* pLight, FBMatrix& pLightView, FBMatrix& pLightProj )
{
{
return false;
}
pLightView.Identity();
pLightProj.Identity();
{
1.0, 0.0, 0.0, 0.0,
0.0, 0.0, -1.0, 0.0,
0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 0.0, 1.0
};
FBMatrix baseMat( base );
FBMatrix rotationMat;
FBMatrix transformationMat;
double radius;
GetWorldBounds( pLight, radius, newPos );
transformationMat(3,0) = newPos[0];
transformationMat(3,1) = newPos[1];
transformationMat(3,2) = newPos[2];
transformationMat(3,3) = 1.0f;
double left = -radius;
double right = radius;
double top = radius;
double bottom = -radius;
double fNear = 0.0f;
double fFar = radius * 2.0f;
double diffRL = 1.0 / (right - left);
double diffTB = 1.0 / (top - bottom);
double diffFN = 1.0 / (fFar - fNear);
double orthoValues[16] =
{
2.0 * diffRL, 0, 0, 0,
0, 2.0 * diffTB, 0, 0,
0, 0, -2.0 * diffFN, 0,
0, 0, -(fFar + fNear) * diffFN, 1.0
};
pLightProj = FBMatrix( orthoValues );
return true;
}
void ShadowMappingShader::ComputeWorldBounds()
{
double dblMin[4] = { DBL_MIN, DBL_MIN, DBL_MIN, 1.0 };
double dblMax[4] = { DBL_MAX, DBL_MAX, DBL_MAX, 1.0 };
mWorldMin.Set( dblMax );
mWorldMax.Set( dblMin );
ComputeWorldBounds( FBSystem().SceneRootModel );
}
void ShadowMappingShader::ComputeWorldBounds( FBModel* pModel, bool pRecursive )
{
bool lLightCam = pModel->Is( FBCamera::TypeInfo ) || pModel->Is( FBLight::TypeInfo ) ;
if( pModel->Geometry && !lLightCam )
{
pModel->GetBoundingBox( lMin, lMax );
l4Min[0] = lMin[0];
l4Min[1] = lMin[1];
l4Min[2] = lMin[2];
l4Min[3] = 1.0;
l4Max[0] = lMax[0];
l4Max[1] = lMax[1];
l4Max[2] = lMax[2];
l4Max[3] = 1.0;
FBMatrix lMtx;
pModel->GetMatrix( lMtx );
if( l4Min[0] < mWorldMin[0] ) mWorldMin[0] = l4Min[0];
if( l4Min[1] < mWorldMin[1] ) mWorldMin[1] = l4Min[1];
if( l4Min[2] < mWorldMin[2] ) mWorldMin[2] = l4Min[2];
if( l4Max[0] > mWorldMax[0] ) mWorldMax[0] = l4Max[0];
if( l4Max[1] > mWorldMax[1] ) mWorldMax[1] = l4Max[1];
if( l4Max[2] > mWorldMax[2] ) mWorldMax[2] = l4Max[2];
}
if( pRecursive )
{
for(
int i = 0;
i < pModel->Children.GetCount();
i++ )
{
ComputeWorldBounds( pModel->Children[
i] );
}
}
}
void ShadowMappingShader::GetWorldBounds( FBLight* pLight,
double& pRadius,
FBVector4d& pPos )
{
double lBaseLightDir[4] = { 0.0, -1.0, 0.0, 0.0 };
lDir.
Set( lBaseLightDir );
FBMatrix rotationMat;
double base[16] =
{
1.0, 0.0, 0.0, 0.0,
0.0, 0.0, -1.0, 0.0,
0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 0.0, 1.0
};
FBMatrix baseMat( base );
FBMatrix transformMat, invTransformMat;
double dblMin[4] = { DBL_MIN, DBL_MIN, DBL_MIN, 1.0 };
double dblMax[4] = { DBL_MAX, DBL_MAX, DBL_MAX, 1.0 };
lLightMax.Set( dblMin );
for(
int i = 0;
i < 3;
i++ )
{
if( lBounds[0][
i] < lLightMin[
i] ) { lLightMin[
i] = lBounds[0][
i]; }
if( lBounds[1][i] < lLightMin[i] ) { lLightMin[
i] = lBounds[1][
i]; }
if( lBounds[0][i] > lLightMax[i] ) { lLightMax[
i] = lBounds[0][
i]; }
if( lBounds[1][i] > lLightMax[i] ) { lLightMax[
i] = lBounds[1][
i]; }
}
center[0] = 0.5 * (lLightMin[0] + lLightMax[0]);
center[1] = 0.5 * (lLightMin[1] + lLightMax[1]);
center[2] = 0.5 * (lLightMin[2] + lLightMax[2]);
center[3] = 1.0;
double w = lLightMax[0] - lLightMin[0];
double h = lLightMax[1] - lLightMin[1];
double d = lLightMax[2] - lLightMin[2];
double sceneRadius = 0.5 * max( w, max(h, d) );
sceneRadius *= 1.1f;
lDir[0] *= sceneRadius;
lDir[1] *= sceneRadius;
lDir[2] *= sceneRadius;
newPos[0] = center[0] - lDir[0];
newPos[1] = center[1] - lDir[1];
newPos[2] = center[2] - lDir[2];
newPos[3] = 1.0;
pRadius = sceneRadius;
pPos = newPos;
}
}