643 lines
20 KiB
C++
Executable File
643 lines
20 KiB
C++
Executable File
//-----------------------------------------------------------------------------
|
|
// Torque Game Engine
|
|
// Copyright (C) GarageGames.com, Inc.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#include "game/shadow.h"
|
|
#include "sim/sceneObject.h"
|
|
#include "dgl/dgl.h"
|
|
#include "core/fileStream.h"
|
|
#include "core/bitRender.h"
|
|
#include "platform/platform.h"
|
|
#include "interior/interiorInstance.h"
|
|
#include "lightingSystem/sgLighting.h"
|
|
#include "lightingSystem/sgObjectShadows.h"
|
|
|
|
DepthSortList Shadow::smDepthSortList;
|
|
TextureHandle* Shadow::smGenericShadowTexture = NULL;
|
|
S32 Shadow::smGenericShadowDim = 32;
|
|
S32 Shadow::smInstanceCount = 0;
|
|
U32 Shadow::smShadowMask = TerrainObjectType | InteriorObjectType;
|
|
F32 Shadow::smShapeDetailScale = 1.0f;
|
|
S32 Shadow::smShapeDetailMin = 2; // never select anything less than this detail unless other value supplied
|
|
F32 Shadow::smSmallestVisibleSize = 6.0f; // stop drawing shadows when less than 6 pixels in size
|
|
F32 Shadow::smLightDirSkew = 0.0f;
|
|
F32 Shadow::smLightLenSkew = 0.0f;
|
|
F32 Shadow::smGenericRadiusSkew = 0.4f; // shrink radius of shape when it always uses generic shadow...
|
|
bool Shadow::smAlwaysUseGenericBmp = false;
|
|
F32 Shadow::smGlobalShadowDetail = 1.0f;
|
|
|
|
Vector<U32> gShadowBits(__FILE__, __LINE__);
|
|
Box3F gShadowBox;
|
|
SphereF gShadowSphere;
|
|
Point3F gShadowPoly[4];
|
|
|
|
Shadow::DistanceDetail Shadow::smDefaultDistanceDetails[] =
|
|
{
|
|
{ 0.0f, 0.0f, 0.0f },
|
|
{ 100.0f, 0.0f, 0.0f },
|
|
{ 500.0f, 1.0f, 0.5f },
|
|
{ 1000.0f, 1.0f, 0.7f }
|
|
};
|
|
|
|
Shadow::PixelSizeDetail sgShadowDetailHighest[] =
|
|
{
|
|
{ 130.0f, 5, 256, 1, false },
|
|
{ 25.0f, 10, 128, 1, false },
|
|
{ 5.0f, 20, 64, 1, false },
|
|
{ 0.0f, 0, 0, 0, true }
|
|
};
|
|
|
|
Shadow::PixelSizeDetail sgShadowDetailHigh[] =
|
|
{
|
|
{ 130.0f, 10, 256, 1, false },
|
|
{ 25.0f, 20, 128, 1, false },
|
|
{ 7.0f, 50, 64, 0, false },
|
|
{ 0.0f, 0, 0, 0, true }
|
|
};
|
|
|
|
Shadow::PixelSizeDetail sgShadowDetailMedium[] =
|
|
{
|
|
{ 130.0f, 10, 128, 1, false },
|
|
{ 25.0f, 20, 128, 1, false },
|
|
{ 7.0f, 50, 64, 0, false },
|
|
{ 0.0f, 0, 0, 0, true }
|
|
};
|
|
|
|
Shadow::PixelSizeDetail sgShadowDetailLow[] =
|
|
{
|
|
{ 130.0f, 20, 64, 1, false },
|
|
{ 25.0f, 40, 64, 1, false },
|
|
{ 7.0f, 100, 32, 1, false },
|
|
{ 0.0f, 0, 0, 0, true }
|
|
};
|
|
|
|
Shadow::PixelSizeDetail sgShadowDetailLowest[] =
|
|
{
|
|
{ 130.0f, 25, 64, 1, false },
|
|
{ 25.0f, 50, 64, 1, false },
|
|
{ 10.0f, 100, 32, 1, false },
|
|
{ 0.0f, 0, 0, 0, true }
|
|
};
|
|
|
|
Shadow::PixelSizeDetail sgShadowDetailDamnNearOff[] =
|
|
{
|
|
{ 130.0f, 25, 64, 1, false },
|
|
{ 25.0f, 75, 32, 1, false },
|
|
{ 10.0f, 0, 0, 0, true },
|
|
{ 0.0f, 0, 0, 0, true }
|
|
};
|
|
|
|
Shadow::PixelSizeDetail sgShadowDetailGeneric[] =
|
|
{
|
|
{ 130.0f, 0, 0, 0, true },
|
|
{ 25.0f, 0, 0, 0, true },
|
|
{ 10.0f, 0, 0, 0, true },
|
|
{ 0.0f, 0, 0, 0, true }
|
|
};
|
|
|
|
Shadow::PixelSizeDetail *sgShadowDetailList[] = {sgShadowDetailHighest, sgShadowDetailHigh,
|
|
sgShadowDetailMedium, sgShadowDetailLow, sgShadowDetailLowest, sgShadowDetailDamnNearOff,
|
|
sgShadowDetailGeneric};
|
|
S32 sgShadowDetailListCount = sizeof(sgShadowDetailList) / sizeof(Shadow::PixelSizeDetail *);
|
|
|
|
|
|
void Shadow::setGlobalShadowDetailLevel(F32 d)
|
|
{
|
|
smGlobalShadowDetail = d;
|
|
|
|
smShapeDetailScale = 0.5f + d*0.5f;
|
|
smShapeDetailMin = d>0.49f ? 2 : 3;
|
|
smSmallestVisibleSize = d>0.7f ? 6 : (d>0.4f ? 7 : (d>0.3f ? 8 : (d>0.2f ? 9 : 10)));
|
|
smLightDirSkew = 0.85f * (1.0f-d);
|
|
smLightLenSkew = (1.0f-d) * 0.5f;
|
|
}
|
|
|
|
//--------------------------------------------------------------
|
|
|
|
Shadow::Shadow()
|
|
{
|
|
mBitmap = NULL;
|
|
mRadius = 0.0f;
|
|
mSettings.alwaysUseGenericBmp = false;
|
|
mSettings.noAnimate = false;
|
|
mSettings.noMove = false;
|
|
|
|
setDefaultDetailTables();
|
|
|
|
if (smInstanceCount == 0)
|
|
{
|
|
GBitmap* bitmap = generateGenericShadowBitmap(smGenericShadowDim);
|
|
smGenericShadowTexture = new TextureHandle(NULL,bitmap);
|
|
// bitmap now owned by texture manager, so we don't delete it
|
|
}
|
|
smInstanceCount++;
|
|
|
|
sgLastRenderTime = 0;
|
|
sgPreviousShadowTime = 0;
|
|
sgPreviousShadowLightingVector = VectorF(0, 0, -1);
|
|
}
|
|
|
|
Shadow::~Shadow()
|
|
{
|
|
AssertFatal(smInstanceCount > 0, "Error, more destructors than constructors?");
|
|
smInstanceCount--;
|
|
if (smInstanceCount == 0) {
|
|
delete smGenericShadowTexture;
|
|
smGenericShadowTexture = NULL;
|
|
}
|
|
}
|
|
|
|
GBitmap * Shadow::generateGenericShadowBitmap(S32 dim)
|
|
{
|
|
GBitmap * bitmap = new GBitmap(dim,dim,false,GBitmap::Luminance);
|
|
U8 * bits = bitmap->getWritableBits();
|
|
S32 center = dim >> 1;
|
|
F32 invRadiusSq = 1.0f / (F32)(center*center);
|
|
F32 tmpF;
|
|
for (S32 i=0; i<dim; i++)
|
|
for (S32 j=0; j<dim; j++)
|
|
{
|
|
tmpF = (F32)((i-center)*(i-center)+(j-center)*(j-center)) * invRadiusSq;
|
|
bits[i*dim+j] = (U8)(tmpF>0.99f ? 0 : 180 - 180.0f*tmpF); // 180 out of 255 max
|
|
}
|
|
return bitmap;
|
|
}
|
|
|
|
//--------------------------------------------------------------
|
|
|
|
void Shadow::setDetailTables(const Shadow::DistanceDetail * dd, S32 ddCount, const Shadow::PixelSizeDetail * psd, S32 psdCount)
|
|
{
|
|
sgLastShadowDetailSize = mClamp(LightManager::sgGetDynamicShadowQuality(), S32(0), (sgShadowDetailListCount - 1));
|
|
LightManager::sgSetDynamicShadowQuality(sgLastShadowDetailSize);
|
|
|
|
mDistanceDetails = dd;
|
|
mPixelSizeDetails = sgShadowDetailList[sgLastShadowDetailSize];
|
|
mNumDistanceDetails = ddCount;
|
|
mNumPixelSizeDetails = psdCount;
|
|
}
|
|
|
|
void Shadow::setDefaultDetailTables()
|
|
{
|
|
setDetailTables(smDefaultDistanceDetails,
|
|
sizeof(smDefaultDistanceDetails)/sizeof(DistanceDetail),
|
|
sgShadowDetailHighest,
|
|
sizeof(sgShadowDetailHighest)/sizeof(PixelSizeDetail));
|
|
}
|
|
|
|
void Shadow::findDistanceDetail(F32 dist, DistanceDetail * dd)
|
|
{
|
|
for (S32 i=0; i<mNumDistanceDetails; i++)
|
|
{
|
|
if (dist<=mDistanceDetails[i].dist)
|
|
{
|
|
if (i==0)
|
|
*dd=mDistanceDetails[i];
|
|
else
|
|
{
|
|
F32 interp = (dist-mDistanceDetails[i].dist)/(mDistanceDetails[i-1].dist-mDistanceDetails[i].dist);
|
|
dd->directionSkew = interp * mDistanceDetails[i-1].directionSkew + (1.0f-interp) * mDistanceDetails[i].directionSkew;
|
|
dd->lengthSkew = interp * mDistanceDetails[i-1].lengthSkew + (1.0f-interp) * mDistanceDetails[i].lengthSkew;
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
*dd = mDistanceDetails[mNumDistanceDetails-1];
|
|
}
|
|
|
|
void Shadow::findPixelSizeDetail(F32 pixelSize, const PixelSizeDetail ** psd)
|
|
{
|
|
*psd = NULL;
|
|
for (S32 i=0; i<mNumPixelSizeDetails; i++)
|
|
{
|
|
*psd = &mPixelSizeDetails[i];
|
|
if (pixelSize>=mPixelSizeDetails[i].size)
|
|
return;
|
|
}
|
|
}
|
|
|
|
S32 Shadow::selectShapeDetail(TSShapeInstance * shapeInstance, F32 dist, F32 scale, S32 detailMin)
|
|
{
|
|
if (detailMin<0)
|
|
detailMin = Shadow::smShapeDetailMin;
|
|
|
|
S32 dl = shapeInstance->selectCurrentDetail( dglProjectRadius(dist,scale * shapeInstance->getShape()->radius * dglGetPixelScale() * TSShapeInstance::smDetailAdjust * Shadow::smShapeDetailScale));
|
|
if (dl>=0 && dl < detailMin)
|
|
shapeInstance->setCurrentDetail(getMin(detailMin,shapeInstance->getShape()->mSmallestVisibleDL));
|
|
return shapeInstance->getCurrentDetail();
|
|
}
|
|
|
|
//--------------------------------------------------------------
|
|
|
|
void Shadow::setLightMatrices(const Point3F & lightDir, const Point3F & pos)
|
|
{
|
|
AssertFatal(mDot(lightDir,lightDir)>0.0001f,"Shadow::setLightDir: light direction must be a non-zero vector.");
|
|
|
|
// construct light matrix
|
|
Point3F x,z;
|
|
if (mFabs(lightDir.z)>0.001f)
|
|
{
|
|
// mCross(Point3F(1,0,0),lightDir,&z);
|
|
z.x = 0.0f;
|
|
z.y = lightDir.z;
|
|
z.z = -lightDir.y;
|
|
z.normalize();
|
|
mCross(lightDir,z,&x);
|
|
}
|
|
else
|
|
{
|
|
mCross(lightDir,Point3F(0,0,1),&x);
|
|
x.normalize();
|
|
mCross(x,lightDir,&z);
|
|
}
|
|
|
|
mLightToWorld.identity();
|
|
mLightToWorld.setColumn(0,x);
|
|
mLightToWorld.setColumn(1,lightDir);
|
|
mLightToWorld.setColumn(2,z);
|
|
mLightToWorld.setColumn(3,pos);
|
|
|
|
mWorldToLight = mLightToWorld;
|
|
mWorldToLight.inverse();
|
|
}
|
|
|
|
void Shadow::setRadius(F32 radius)
|
|
{
|
|
mRadius = radius;
|
|
}
|
|
|
|
void Shadow::setRadius(TSShapeInstance * shapeInstance, const Point3F & scale)
|
|
{
|
|
const Box3F & bounds = shapeInstance->getShape()->bounds;
|
|
F32 dx = 0.5f * (bounds.max.x-bounds.min.x) * scale.x;
|
|
F32 dy = 0.5f * (bounds.max.y-bounds.min.y) * scale.y;
|
|
F32 dz = 0.5f * (bounds.max.z-bounds.min.z) * scale.z;
|
|
mRadius = mSqrt(dx*dx+dy*dy+dz*dz);
|
|
}
|
|
|
|
//--------------------------------------------------------------
|
|
|
|
void Shadow::beginRenderToBitmap()
|
|
{
|
|
AssertFatal(mBitmap,"Shadow::beginRenderToBitmap had no bitmap to render to!");
|
|
|
|
// clean slate...
|
|
gShadowBits.setSize(mSettings.bmpDim * (mSettings.bmpDim>>5));
|
|
dMemset(gShadowBits.address(),0,mSettings.bmpDim*(mSettings.bmpDim>>3)); // dMemset deals in bytes not words, hence the shift by 3 not 5
|
|
}
|
|
|
|
void Shadow::endRenderToBitmap()
|
|
{
|
|
// copy to bitmap
|
|
if (mSettings.blur==1)
|
|
// blur
|
|
BitRender::bitTo8Bit_3(gShadowBits.address(),(U32*)mBitmap->getWritableBits(),mSettings.bmpDim);
|
|
else
|
|
// non-blur version:
|
|
BitRender::bitTo8Bit(gShadowBits.address(),(U32*)mBitmap->getWritableBits(),mSettings.bmpDim);
|
|
|
|
mShadowTexture.refresh();
|
|
}
|
|
|
|
void Shadow::renderToBitmap(TSShapeInstance * shapeInstance, const MatrixF & transform, const Point3F & pos, Point3F scale)
|
|
{
|
|
AssertFatal(mBitmap,"Shadow::renderToShadow: must call beginRenderToBitmap first");
|
|
|
|
MatrixF mat;
|
|
mat.mul(mWorldToLight,transform);
|
|
|
|
// everything gets scaled by this amount so that we map onto the bitmap correctly
|
|
F32 k = ((F32)mSettings.bmpDim)/(2.0f*mRadius);
|
|
|
|
// this scales everything but the last row of the matrix (we do that below)
|
|
scale *= k;
|
|
mat.scale(scale);
|
|
|
|
// we want pos to map to bitmap center...
|
|
// the following is a bit convoluted...but it is correct...
|
|
Point3F p,p2;
|
|
mat.getColumn(3,&p);
|
|
mWorldToLight.mulP(pos,&p2);
|
|
p -= p2;
|
|
p *= k;
|
|
F32 halfDim = mSettings.bmpDim>>1;
|
|
p.x += halfDim;
|
|
p.z += halfDim;
|
|
mat.setColumn(3,p); // shape center now falls on bitmap center...
|
|
|
|
shapeInstance->animate();
|
|
shapeInstance->renderShadow(shapeInstance->getCurrentDetail(),mat,mSettings.bmpDim,gShadowBits.address());
|
|
}
|
|
|
|
//--------------------------------------------------------------
|
|
|
|
bool Shadow::prepare(const Point3F & pos, Point3F lightDir, F32 shadowLen, const Point3F & scale, F32 dist, F32 fogAmount, TSShapeInstance * shapeInstance)
|
|
{
|
|
// 0. use pixel size to do early reject test
|
|
// 1. use distance from camera to do 1st pass detail selection (light direction, shadow volume distance)
|
|
// 2. get polys to project shadow onto, build shadow partition
|
|
// 3. use pixel size to do 2nd pass detail selection (shape detail level, bitmap dimension, blur routine,
|
|
// sample frequency, substitute generic shadow bitmap)
|
|
|
|
// NOTES:
|
|
// If using cached partition, do 1&2 only if we don't have a partition yet...
|
|
// If non-animating shape, do step 3 and re-compute bitmap only if detail info changes...
|
|
|
|
detectShadowDetailSizeChange();
|
|
|
|
// --------------------------------------
|
|
// 0.
|
|
F32 maxScale = getMax(scale.x,getMax(scale.y,scale.z));
|
|
F32 pixelSize = dglProjectRadius(dist/maxScale,shapeInstance->getShape()->radius) * dglGetPixelScale() * TSShapeInstance::smDetailAdjust;
|
|
F32 smallest = getMax(Shadow::smSmallestVisibleSize,shapeInstance->getShape()->mSmallestVisibleSize);
|
|
if (pixelSize * Shadow::smShapeDetailScale < smallest)
|
|
return false;
|
|
|
|
// fade over distance from viewer
|
|
F32 pseudoFog = smallest/(pixelSize * Shadow::smShapeDetailScale);
|
|
if (pseudoFog>0.5f)
|
|
pseudoFog = 2.0f * (pseudoFog-0.5f);
|
|
else
|
|
pseudoFog = 0.0f;
|
|
if (fogAmount<pseudoFog)
|
|
{
|
|
if (pseudoFog>=0.99f)
|
|
// shadow faded out
|
|
return false;
|
|
fogAmount = pseudoFog;
|
|
}
|
|
|
|
// find detail information
|
|
DistanceDetail dd;
|
|
findDistanceDetail(mSettings.noMove ? 0.0f : dist,&dd);
|
|
const PixelSizeDetail * psd;
|
|
findPixelSizeDetail(pixelSize,&psd);
|
|
|
|
if (!mSettings.noMove || mPartition.empty())
|
|
{
|
|
// --------------------------------------
|
|
// 1.
|
|
F32 dirMult = (1.0f - dd.directionSkew) * (1.0f - smLightDirSkew);
|
|
if (dirMult < 0.99f)
|
|
{
|
|
lightDir.z *= dirMult;
|
|
lightDir.z -= 1.0f - dirMult;
|
|
}
|
|
lightDir.normalize();
|
|
shadowLen *= (1.0f - dd.lengthSkew) * (1.0f - smLightLenSkew);
|
|
|
|
// --------------------------------------
|
|
// 2. get polys
|
|
F32 radius = mRadius;
|
|
if (psd->genericShadowBmp || mSettings.alwaysUseGenericBmp || smAlwaysUseGenericBmp)
|
|
radius *= smGenericRadiusSkew;
|
|
buildPartition(pos,lightDir,radius,shadowLen);
|
|
}
|
|
updatePartition(fogAmount);
|
|
if (mPartition.empty())
|
|
// no need to draw shadow if nothing to cast it onto
|
|
return false;
|
|
|
|
// --------------------------------------
|
|
// 3.
|
|
// do we need a new bitmap? anim rate, bmp dim, generic vs generated
|
|
mSettings.needBmp = false;
|
|
if (mSettings.alwaysUseGenericBmp || smAlwaysUseGenericBmp || psd->genericShadowBmp)
|
|
// use generic bitmap -- get rid of old bmp if it's there
|
|
mBitmap = NULL;
|
|
else
|
|
{
|
|
U32 time = Platform::getVirtualMilliseconds();
|
|
bool expired = time-mSettings.lastBmpTime > psd->frameExpiration;
|
|
bool propertyChange = !mBitmap || psd->bmpDim!=mSettings.bmpDim || psd->blur!=mSettings.blur;
|
|
if ( (expired && !mSettings.noAnimate) || propertyChange)
|
|
{
|
|
// need to generate a new bmp
|
|
mSettings.blur = psd->blur;
|
|
mSettings.lastBmpTime = Platform::getVirtualMilliseconds();
|
|
mSettings.needBmp = true;
|
|
|
|
if (mSettings.bmpDim!=psd->bmpDim || !mBitmap)
|
|
{
|
|
// allocate new bitmap...register texture (no need to delete old one, owned by texture handle)
|
|
mSettings.bmpDim = psd->bmpDim;
|
|
mBitmap = new GBitmap(mSettings.bmpDim,mSettings.bmpDim,false,GBitmap::Luminance);
|
|
mShadowTexture.set(NULL,mBitmap);
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//--------------------------------------------------------------
|
|
|
|
void Shadow::buildPartition(const Point3F & p, const Point3F & lightDir, F32 radius, F32 shadowLen)
|
|
{
|
|
setLightMatrices(lightDir,p);
|
|
|
|
Point3F extent(2.0f*radius,shadowLen,2.0f*radius);
|
|
smDepthSortList.clear();
|
|
smDepthSortList.set(mWorldToLight,extent);
|
|
smDepthSortList.setInterestNormal(lightDir);
|
|
|
|
if (shadowLen<1.0f)
|
|
// no point in even this short of a shadow...
|
|
shadowLen = 1.0f;
|
|
mInvShadowDistance = 1.0f / shadowLen;
|
|
|
|
// build world space box and sphere around shadow
|
|
|
|
Point3F x,y,z;
|
|
mLightToWorld.getColumn(0,&x);
|
|
mLightToWorld.getColumn(1,&y);
|
|
mLightToWorld.getColumn(2,&z);
|
|
x *= radius;
|
|
y *= shadowLen;
|
|
z *= radius;
|
|
gShadowBox.max.set(mFabs(x.x)+mFabs(y.x)+mFabs(z.x),
|
|
mFabs(x.y)+mFabs(y.y)+mFabs(z.y),
|
|
mFabs(x.z)+mFabs(y.z)+mFabs(z.z));
|
|
y *= 0.5f;
|
|
gShadowSphere.radius = gShadowBox.max.len();
|
|
gShadowSphere.center = p + y;
|
|
gShadowBox.min = y + p - gShadowBox.max;
|
|
gShadowBox.max += y + p;
|
|
|
|
// get polys
|
|
|
|
gClientContainer.findObjects(smShadowMask,Shadow::collisionCallback,this);
|
|
|
|
// setup partition list
|
|
gShadowPoly[0].set(-radius,0,-radius);
|
|
gShadowPoly[1].set(-radius,0, radius);
|
|
gShadowPoly[2].set( radius,0, radius);
|
|
gShadowPoly[3].set( radius,0,-radius);
|
|
|
|
mPartition.clear();
|
|
mPartitionVerts.clear();
|
|
smDepthSortList.depthPartition(gShadowPoly,4,mPartition,mPartitionVerts);
|
|
|
|
// now set up tverts & colors
|
|
mPartitionColors.setSize(mPartitionVerts.size());
|
|
mPartitionTVerts.setSize(mPartitionVerts.size());
|
|
F32 invRadius = 1.0f / radius;
|
|
for (S32 i=0; i<mPartitionVerts.size(); i++)
|
|
mPartitionTVerts[i].set(0.5f + 0.5f * mPartitionVerts[i].x * invRadius, 0.5f + 0.5f * mPartitionVerts[i].z * invRadius);
|
|
}
|
|
|
|
void Shadow::detectShadowDetailSizeChange()
|
|
{
|
|
if(sgLastShadowDetailSize != LightManager::sgGetDynamicShadowQuality())
|
|
{
|
|
setDefaultDetailTables();
|
|
}
|
|
}
|
|
|
|
void Shadow::updatePartition(F32 fog)
|
|
{
|
|
fog = 1.0f-fog;
|
|
fog *= SG_DYNAMIC_SHADOW_INTENSITY;
|
|
|
|
for (S32 i=0; i<mPartitionVerts.size(); i++)
|
|
{
|
|
F32 val = 1.0f - mPartitionVerts[i].y * mInvShadowDistance;
|
|
val *= fog * sgObjectShadows::sgCurrentFade;
|
|
mPartitionColors[i].set(val, val, val, val);
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------
|
|
|
|
void Shadow::collisionCallback(SceneObject * obj, void* thisPtr)
|
|
{
|
|
Shadow * shadow = reinterpret_cast<Shadow*>(thisPtr);
|
|
if (obj->getWorldBox().isOverlapped(gShadowBox))
|
|
{
|
|
// only interiors clip...
|
|
ClippedPolyList::allowClipping = (dynamic_cast<InteriorInstance *>(obj) != NULL);
|
|
obj->buildPolyList(&smDepthSortList,gShadowBox,gShadowSphere);
|
|
ClippedPolyList::allowClipping = true;
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------
|
|
|
|
void Shadow::render()
|
|
{
|
|
bool wasLit = glIsEnabled(GL_LIGHTING);
|
|
glDisable(GL_LIGHTING);
|
|
|
|
// push light matrix
|
|
glPushMatrix();
|
|
dglMultMatrix(&mLightToWorld);
|
|
|
|
// set up texture environment
|
|
glEnable(GL_TEXTURE_2D);
|
|
glEnable(GL_BLEND);
|
|
glDepthMask(GL_FALSE);
|
|
glBlendFunc(GL_ZERO,GL_ONE_MINUS_SRC_COLOR);
|
|
glTexEnvf(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_MODULATE);
|
|
if (mBitmap)
|
|
glBindTexture(GL_TEXTURE_2D, mShadowTexture.getGLName());
|
|
else {
|
|
AssertFatal(smGenericShadowTexture != NULL, "Error, shadow texture not initialized!");
|
|
glBindTexture(GL_TEXTURE_2D, smGenericShadowTexture->getGLName());
|
|
}
|
|
|
|
|
|
// set up arrays
|
|
if( TSMesh::getOverrideFade() >= 1.0f )
|
|
{
|
|
glEnableClientState(GL_COLOR_ARRAY);
|
|
glColorPointer(4,GL_FLOAT,0,mPartitionColors.address());
|
|
}
|
|
else
|
|
{
|
|
F32 color = TSMesh::getOverrideFade();
|
|
glColor4f( color, color, color, color );
|
|
}
|
|
|
|
|
|
glEnableClientState(GL_VERTEX_ARRAY);
|
|
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
|
glVertexPointer(3,GL_FLOAT,0,mPartitionVerts.address());
|
|
glTexCoordPointer(2,GL_FLOAT,0,mPartitionTVerts.address());
|
|
|
|
bool lockArrays = dglDoesSupportCompiledVertexArray();
|
|
|
|
if (lockArrays)
|
|
glLockArraysEXT(0,mPartitionVerts.size());
|
|
|
|
// fight z-fighting
|
|
glEnable(GL_POLYGON_OFFSET_FILL);
|
|
glPolygonOffset(-2,-2);
|
|
|
|
// draw
|
|
for (S32 i=0; i<mPartition.size(); i++)
|
|
glDrawArrays(GL_POLYGON,mPartition[i].vertexStart,mPartition[i].vertexCount);
|
|
|
|
//
|
|
if (lockArrays)
|
|
glUnlockArraysEXT();
|
|
glPopMatrix();
|
|
|
|
// reset gl enviromnet
|
|
glDisable(GL_TEXTURE_2D);
|
|
glDisable(GL_BLEND);
|
|
glDepthMask(GL_TRUE);
|
|
glDisable(GL_POLYGON_OFFSET_FILL);
|
|
glDisableClientState(GL_VERTEX_ARRAY);
|
|
glDisableClientState(GL_COLOR_ARRAY);
|
|
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
|
if (wasLit)
|
|
glEnable(GL_LIGHTING);
|
|
}
|
|
|
|
//--------------------------------------------------------------
|
|
|
|
#ifdef TORQUE_DEBUG
|
|
void Shadow::dumpSort()
|
|
{
|
|
#define printDump(a) {const char * b = a; file.write(dStrlen(b),b);}
|
|
|
|
FileStream file;
|
|
|
|
file.open("poly.txt",FileStream::Write);
|
|
S32 i;
|
|
printDump("Pre-sort: \r\n\r\n");
|
|
for (i=0; i<smDepthSortList.mPolyIndexList.size(); i++)
|
|
{
|
|
DepthSortList::Poly * poly;
|
|
DepthSortList::PolyExtents * polyExtent;
|
|
smDepthSortList.getOrderedPoly((U32)i,&poly,&polyExtent);
|
|
for (S32 j=0; j<poly->vertexCount; j++)
|
|
{
|
|
Point3F p = smDepthSortList.mVertexList[smDepthSortList.mIndexList[poly->vertexStart+j]].point;
|
|
printDump(avar("(%5.3f, %5.3f, %5.3f) ",p.x,p.y,p.z));
|
|
}
|
|
printDump("\r\n");
|
|
}
|
|
smDepthSortList.sort();
|
|
|
|
printDump("\r\n\r\nPost-sort: \r\n\r\n");
|
|
for (i=0; i<smDepthSortList.mPolyIndexList.size(); i++)
|
|
{
|
|
DepthSortList::Poly * poly;
|
|
DepthSortList::PolyExtents * polyExtent;
|
|
smDepthSortList.getOrderedPoly(i,&poly,&polyExtent);
|
|
for (S32 j=0; j<poly->vertexCount; j++)
|
|
{
|
|
Point3F p = smDepthSortList.mVertexList[smDepthSortList.mIndexList[poly->vertexStart+j]].point;
|
|
printDump(avar("(%5.3f, %5.3f, %5.3f) ",p.x,p.y,p.z));
|
|
}
|
|
printDump("\r\n");
|
|
}
|
|
|
|
file.close();
|
|
}
|
|
#endif
|
|
|
|
|
|
|