Engine directory for ticket #1

This commit is contained in:
DavidWyand-GG 2012-09-19 11:15:01 -04:00
parent 352279af7a
commit 7dbfe6994d
3795 changed files with 1363358 additions and 0 deletions

View file

@ -0,0 +1,352 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "platform/platform.h"
#include "lighting/common/blobShadow.h"
#include "gfx/primBuilder.h"
#include "gfx/gfxTextureManager.h"
#include "gfx/bitmap/gBitmap.h"
#include "math/mathUtils.h"
#include "lighting/lightInfo.h"
#include "lighting/lightingInterfaces.h"
#include "T3D/shapeBase.h"
#include "scene/sceneManager.h"
#include "lighting/lightManager.h"
#include "ts/tsMesh.h"
DepthSortList BlobShadow::smDepthSortList;
GFXTexHandle BlobShadow::smGenericShadowTexture = NULL;
S32 BlobShadow::smGenericShadowDim = 32;
U32 BlobShadow::smShadowMask = TerrainObjectType | InteriorObjectType;
F32 BlobShadow::smGenericRadiusSkew = 0.4f; // shrink radius of shape when it always uses generic shadow...
Box3F gBlobShadowBox;
SphereF gBlobShadowSphere;
Point3F gBlobShadowPoly[4];
//--------------------------------------------------------------
BlobShadow::BlobShadow(SceneObject* parentObject, LightInfo* light, TSShapeInstance* shapeInstance)
{
mParentObject = parentObject;
mShapeBase = dynamic_cast<ShapeBase*>(parentObject);
mParentLight = light;
mShapeInstance = shapeInstance;
mRadius = 0.0f;
mLastRenderTime = 0;
mDepthBias = -0.0002f;
generateGenericShadowBitmap(smGenericShadowDim);
setupStateBlocks();
}
void BlobShadow::setupStateBlocks()
{
GFXStateBlockDesc sh;
sh.cullDefined = true;
sh.cullMode = GFXCullNone;
sh.zDefined = true;
sh.zEnable = true;
sh.zWriteEnable = false;
sh.zBias = mDepthBias;
sh.blendDefined = true;
sh.blendEnable = true;
sh.blendSrc = GFXBlendSrcAlpha;
sh.blendDest = GFXBlendInvSrcAlpha;
sh.alphaDefined = true;
sh.alphaTestEnable = true;
sh.alphaTestFunc = GFXCmpGreater;
sh.alphaTestRef = 0;
sh.samplersDefined = true;
sh.samplers[0] = GFXSamplerStateDesc::getClampLinear();
mShadowSB = GFX->createStateBlock(sh);
}
BlobShadow::~BlobShadow()
{
mShadowBuffer = NULL;
}
bool BlobShadow::shouldRender(F32 camDist)
{
Point3F lightDir;
if (mShapeBase && mShapeBase->getFadeVal() < TSMesh::VISIBILITY_EPSILON)
return false;
F32 shadowLen = 10.0f * mShapeInstance->getShape()->radius;
Point3F pos = mShapeInstance->getShape()->center;
// this is a bit of a hack...move generic shadows towards feet/base of shape
pos *= 0.5f;
pos.convolve(mParentObject->getScale());
mParentObject->getRenderTransform().mulP(pos);
if(mParentLight->getType() == LightInfo::Vector)
{
lightDir = mParentLight->getDirection();
}
else
{
lightDir = pos - mParentLight->getPosition();
lightDir.normalize();
}
// pos is where shadow will be centered (in world space)
setRadius(mShapeInstance, mParentObject->getScale());
bool render = prepare(pos, lightDir, shadowLen);
return render;
}
void BlobShadow::generateGenericShadowBitmap(S32 dim)
{
if(smGenericShadowTexture)
return;
GBitmap * bitmap = new GBitmap(dim,dim,false,GFXFormatR8G8B8A8);
U8 * bits = bitmap->getWritableBits();
dMemset(bits, 0, dim*dim*4);
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;
U8 val = tmpF>0.99f ? 0 : (U8)(180.0f*(1.0f-tmpF)); // 180 out of 255 max
bits[(i*dim*4)+(j*4)+3] = val;
}
}
smGenericShadowTexture.set( bitmap, &GFXDefaultStaticDiffuseProfile, true, "BlobShadow" );
}
//--------------------------------------------------------------
void BlobShadow::setLightMatrices(const Point3F & lightDir, const Point3F & pos)
{
AssertFatal(mDot(lightDir,lightDir)>0.0001f,"BlobShadow::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 BlobShadow::setRadius(F32 radius)
{
mRadius = radius;
}
void BlobShadow::setRadius(TSShapeInstance * shapeInstance, const Point3F & scale)
{
const Box3F & bounds = shapeInstance->getShape()->bounds;
F32 dx = 0.5f * (bounds.maxExtents.x-bounds.minExtents.x) * scale.x;
F32 dy = 0.5f * (bounds.maxExtents.y-bounds.minExtents.y) * scale.y;
F32 dz = 0.5f * (bounds.maxExtents.z-bounds.minExtents.z) * scale.z;
mRadius = mSqrt(dx*dx+dy*dy+dz*dz);
}
//--------------------------------------------------------------
bool BlobShadow::prepare(const Point3F & pos, Point3F lightDir, F32 shadowLen)
{
if (mPartition.empty())
{
// --------------------------------------
// 1.
F32 dirMult = (1.0f) * (1.0f);
if (dirMult < 0.99f)
{
lightDir.z *= dirMult;
lightDir.z -= 1.0f - dirMult;
}
lightDir.normalize();
shadowLen *= (1.0f) * (1.0f);
// --------------------------------------
// 2. get polys
F32 radius = mRadius;
radius *= smGenericRadiusSkew;
buildPartition(pos,lightDir,radius,shadowLen);
}
if (mPartition.empty())
// no need to draw shadow if nothing to cast it onto
return false;
return true;
}
//--------------------------------------------------------------
void BlobShadow::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;
gBlobShadowBox.maxExtents.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;
gBlobShadowSphere.radius = gBlobShadowBox.maxExtents.len();
gBlobShadowSphere.center = p + y;
gBlobShadowBox.minExtents = y + p - gBlobShadowBox.maxExtents;
gBlobShadowBox.maxExtents += y + p;
// get polys
gClientContainer.findObjects(STATIC_COLLISION_TYPEMASK, BlobShadow::collisionCallback, this);
// setup partition list
gBlobShadowPoly[0].set(-radius,0,-radius);
gBlobShadowPoly[1].set(-radius,0, radius);
gBlobShadowPoly[2].set( radius,0, radius);
gBlobShadowPoly[3].set( radius,0,-radius);
mPartition.clear();
mPartitionVerts.clear();
smDepthSortList.depthPartition(gBlobShadowPoly,4,mPartition,mPartitionVerts);
if(mPartitionVerts.empty())
return;
// Find the rough distance of the shadow verts
// from the object position and use that to scale
// the visibleAlpha so that the shadow fades out
// the further away from you it gets
F32 dist = 0.0f;
// Calculate the center of the partition verts
Point3F shadowCenter(0.0f, 0.0f, 0.0f);
for (U32 i = 0; i < mPartitionVerts.size(); i++)
shadowCenter += mPartitionVerts[i];
shadowCenter /= mPartitionVerts.size();
mLightToWorld.mulP(shadowCenter);
dist = (p - shadowCenter).len();
// now set up tverts & colors
mShadowBuffer.set(GFX, mPartitionVerts.size(), GFXBufferTypeVolatile);
mShadowBuffer.lock();
F32 visibleAlpha = 255.0f;
if (mShapeBase && mShapeBase->getFadeVal())
visibleAlpha = mClampF(255.0f * mShapeBase->getFadeVal(), 0, 255);
visibleAlpha *= 1.0f - (dist / gBlobShadowSphere.radius);
F32 invRadius = 1.0f / radius;
for (S32 i=0; i<mPartitionVerts.size(); i++)
{
Point3F vert = mPartitionVerts[i];
mShadowBuffer[i].point.set(vert);
mShadowBuffer[i].color.set(255, 255, 255, visibleAlpha);
mShadowBuffer[i].texCoord.set(0.5f + 0.5f * mPartitionVerts[i].x * invRadius, 0.5f + 0.5f * mPartitionVerts[i].z * invRadius);
};
mShadowBuffer.unlock();
}
//--------------------------------------------------------------
void BlobShadow::collisionCallback(SceneObject * obj, void* thisPtr)
{
if (obj->getWorldBox().isOverlapped(gBlobShadowBox))
{
// only interiors clip...
ClippedPolyList::allowClipping = (obj->getTypeMask() & LIGHTMGR->getSceneLightingInterface()->mClippingMask) != 0;
obj->buildPolyList(PLC_Collision,&smDepthSortList,gBlobShadowBox,gBlobShadowSphere);
ClippedPolyList::allowClipping = true;
}
}
//--------------------------------------------------------------
void BlobShadow::render( F32 camDist, const TSRenderState &rdata )
{
mLastRenderTime = Platform::getRealMilliseconds();
GFX->pushWorldMatrix();
MatrixF world = GFX->getWorldMatrix();
world.mul(mLightToWorld);
GFX->setWorldMatrix(world);
GFX->disableShaders();
GFX->setStateBlock(mShadowSB);
GFX->setTexture(0, smGenericShadowTexture);
GFX->setVertexBuffer(mShadowBuffer);
for(U32 p=0; p<mPartition.size(); p++)
GFX->drawPrimitive(GFXTriangleFan, mPartition[p].vertexStart, (mPartition[p].vertexCount - 2));
// This is a bad nasty hack which forces the shadow to reconstruct itself every frame.
mPartition.clear();
GFX->popWorldMatrix();
}
void BlobShadow::deleteGenericShadowBitmap()
{
smGenericShadowTexture = NULL;
}

View file

@ -0,0 +1,87 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _BLOBSHADOW_H_
#define _BLOBSHADOW_H_
#include "collision/depthSortList.h"
#include "scene/sceneObject.h"
#include "ts/tsShapeInstance.h"
#include "lighting/common/shadowBase.h"
class ShapeBase;
class LightInfo;
class BlobShadow : public ShadowBase
{
F32 mRadius;
F32 mInvShadowDistance;
MatrixF mLightToWorld;
MatrixF mWorldToLight;
Vector<DepthSortList::Poly> mPartition;
Vector<Point3F> mPartitionVerts;
GFXVertexBufferHandle<GFXVertexPCT> mShadowBuffer;
static U32 smShadowMask;
static DepthSortList smDepthSortList;
static GFXTexHandle smGenericShadowTexture;
static F32 smGenericRadiusSkew;
static S32 smGenericShadowDim;
U32 mLastRenderTime;
static void collisionCallback(SceneObject*,void *);
private:
SceneObject* mParentObject;
ShapeBase* mShapeBase;
LightInfo* mParentLight;
TSShapeInstance* mShapeInstance;
GFXStateBlockRef mShadowSB;
F32 mDepthBias;
void setupStateBlocks();
void setLightMatrices(const Point3F & lightDir, const Point3F & pos);
void buildPartition(const Point3F & p, const Point3F & lightDir, F32 radius, F32 shadowLen);
public:
BlobShadow(SceneObject* parentobject, LightInfo* light, TSShapeInstance* shapeinstance);
~BlobShadow();
void setRadius(F32 radius);
void setRadius(TSShapeInstance *, const Point3F & scale);
bool prepare(const Point3F & pos, Point3F lightDir, F32 shadowLen);
bool shouldRender(F32 camDist);
void update( const SceneRenderState *state ) {}
void render( F32 camDist, const TSRenderState &rdata );
U32 getLastRenderTime() const { return mLastRenderTime; }
static void generateGenericShadowBitmap(S32 dim);
static void deleteGenericShadowBitmap();
};
#endif

View file

@ -0,0 +1,61 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "lighting/common/lightMapParams.h"
#include "core/stream/bitStream.h"
const LightInfoExType LightMapParams::Type( "LightMapParams" );
LightMapParams::LightMapParams( LightInfo *light ) :
representedInLightmap(false),
includeLightmappedGeometryInShadow(false),
shadowDarkenColor(0.0f, 0.0f, 0.0f, -1.0f)
{
}
LightMapParams::~LightMapParams()
{
}
void LightMapParams::set( const LightInfoEx *ex )
{
// TODO: Do we even need this?
}
void LightMapParams::packUpdate( BitStream *stream ) const
{
stream->writeFlag(representedInLightmap);
stream->writeFlag(includeLightmappedGeometryInShadow);
stream->write(shadowDarkenColor);
}
void LightMapParams::unpackUpdate( BitStream *stream )
{
representedInLightmap = stream->readFlag();
includeLightmappedGeometryInShadow = stream->readFlag();
stream->read(&shadowDarkenColor);
// Always make sure that the alpha value of the shadowDarkenColor is -1.0
shadowDarkenColor.alpha = -1.0f;
}

View file

@ -0,0 +1,54 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _LIGHTMAPPARAMS_H_
#define _LIGHTMAPPARAMS_H_
#ifndef _LIGHTINFO_H_
#include "lighting/lightInfo.h"
#endif
class LightMapParams : public LightInfoEx
{
public:
LightMapParams( LightInfo *light );
virtual ~LightMapParams();
/// The LightInfoEx hook type.
static const LightInfoExType Type;
// LightInfoEx
virtual void set( const LightInfoEx *ex );
virtual const LightInfoExType& getType() const { return Type; }
virtual void packUpdate( BitStream *stream ) const;
virtual void unpackUpdate( BitStream *stream );
public:
// We're leaving these public for easy access
// for console protected fields.
bool representedInLightmap; ///< This light is represented in lightmaps (static light, default: false)
ColorF shadowDarkenColor; ///< The color that should be used to multiply-blend dynamic shadows onto lightmapped geometry (ignored if 'representedInLightmap' is false)
bool includeLightmappedGeometryInShadow; ///< This light should render lightmapped geometry during its shadow-map update (ignored if 'representedInLightmap' is false)
};
#endif

View file

@ -0,0 +1,582 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "platform/platform.h"
#include "lighting/common/projectedShadow.h"
#include "gfx/primBuilder.h"
#include "gfx/gfxTextureManager.h"
#include "gfx/bitmap/gBitmap.h"
#include "gfx/gfxDebugEvent.h"
#include "math/mathUtils.h"
#include "lighting/lightInfo.h"
#include "lighting/lightingInterfaces.h"
#include "T3D/shapeBase.h"
#include "scene/sceneManager.h"
#include "lighting/lightManager.h"
#include "ts/tsMesh.h"
#include "T3D/decal/decalManager.h"
#include "T3D/decal/decalInstance.h"
#include "renderInstance/renderPassManager.h"
#include "renderInstance/renderMeshMgr.h"
#include "gfx/gfxTransformSaver.h"
#include "materials/customMaterialDefinition.h"
#include "materials/materialFeatureTypes.h"
#include "console/console.h"
#include "postFx/postEffect.h"
#include "lighting/basic/basicLightManager.h"
#include "lighting/shadowMap/shadowMatHook.h"
#include "materials/materialManager.h"
#include "lighting/shadowMap/lightShadowMap.h"
SimObjectPtr<RenderPassManager> ProjectedShadow::smRenderPass = NULL;
SimObjectPtr<PostEffect> ProjectedShadow::smShadowFilter = NULL;
F32 ProjectedShadow::smDepthAdjust = 10.0f;
float ProjectedShadow::smFadeStartPixelSize = 200.0f;
float ProjectedShadow::smFadeEndPixelSize = 35.0f;
GFX_ImplementTextureProfile( BLProjectedShadowProfile,
GFXTextureProfile::DiffuseMap,
GFXTextureProfile::PreserveSize |
GFXTextureProfile::RenderTarget |
GFXTextureProfile::Pooled,
GFXTextureProfile::None );
GFX_ImplementTextureProfile( BLProjectedShadowZProfile,
GFXTextureProfile::DiffuseMap,
GFXTextureProfile::PreserveSize |
GFXTextureProfile::ZTarget |
GFXTextureProfile::Pooled,
GFXTextureProfile::None );
ProjectedShadow::ProjectedShadow( SceneObject *object )
{
mParentObject = object;
mShapeBase = dynamic_cast<ShapeBase*>( object );
mRadius = 0;
mLastRenderTime = 0;
mUpdateTexture = false;
mShadowLength = 10.0f;
mDecalData = new DecalData;
mDecalData->skipVertexNormals = true;
mDecalInstance = NULL;
mLastLightDir.set( 0, 0, 0 );
mLastObjectPosition.set( object->getRenderPosition() );
mLastObjectScale.set( object->getScale() );
CustomMaterial *customMat = NULL;
Sim::findObject( "BL_ProjectedShadowMaterial", customMat );
if ( customMat )
{
mDecalData->material = customMat;
mDecalData->matInst = customMat->createMatInstance();
}
else
mDecalData->matInst = MATMGR->createMatInstance( "WarningMaterial" );
mDecalData->matInst->init( MATMGR->getDefaultFeatures(), getGFXVertexFormat<GFXVertexPNTT>() );
mCasterPositionSC = NULL;
mShadowLengthSC = NULL;
}
ProjectedShadow::~ProjectedShadow()
{
if ( mDecalInstance )
gDecalManager->removeDecal( mDecalInstance );
delete mDecalData;
mShadowTexture = NULL;
mRenderTarget = NULL;
}
bool ProjectedShadow::shouldRender( const SceneRenderState *state )
{
// Don't render if our object has been removed from the
// scene graph.
if( !mParentObject->getSceneManager() )
return false;
// Don't render if the ShapeBase
// object's fade value is greater
// than the visibility epsilon.
bool shapeFade = mShapeBase && mShapeBase->getFadeVal() < TSMesh::VISIBILITY_EPSILON;
// Get the shapebase datablock if we have one.
ShapeBaseData *data = NULL;
if ( mShapeBase )
data = static_cast<ShapeBaseData*>( mShapeBase->getDataBlock() );
// Also don't render if
// the camera distance is greater
// than the shadow length.
if ( shapeFade || !mDecalData ||
( mDecalInstance &&
mDecalInstance->calcPixelSize( state->getViewport().extent.y, state->getCameraPosition(), state->getWorldToScreenScale().y ) < mDecalInstance->mDataBlock->fadeEndPixelSize ) )
{
// Release our shadow texture
// so that others can grab it out
// of the pool.
mShadowTexture = NULL;
return false;
}
return true;
}
bool ProjectedShadow::_updateDecal( const SceneRenderState *state )
{
PROFILE_SCOPE( ProjectedShadow_UpdateDecal );
if ( !LIGHTMGR )
return false;
// Get the position of the decal first.
const Box3F &objBox = mParentObject->getObjBox();
const Point3F boxCenter = objBox.getCenter();
Point3F decalPos = boxCenter;
const MatrixF &renderTransform = mParentObject->getRenderTransform();
{
// Set up the decal position.
// We use the object space box center
// multiplied by the render transform
// of the object to ensure we benefit
// from interpolation.
MatrixF t( renderTransform );
t.setColumn(2,Point3F::UnitZ);
t.mulP( decalPos );
}
if ( mDecalInstance )
{
mDecalInstance->mPosition = decalPos;
if ( !shouldRender( state ) )
return false;
}
// Get the sunlight for the shadow projection.
// We want the LightManager to return NULL if it can't
// get the "real" sun, so we specify false for the useDefault parameter.
LightInfo *lights[4] = {0};
LightQuery query;
query.init( mParentObject->getWorldSphere() );
query.getLights( lights, 4 );
Point3F pos = renderTransform.getPosition();
Point3F lightDir( 0, 0, 0 );
Point3F tmp( 0, 0, 0 );
F32 weight = 0;
F32 range = 0;
U32 lightCount = 0;
F32 dist = 0;
F32 fade = 0;
for ( U32 i = 0; i < 4; i++ )
{
// If we got a NULL light,
// we're at the end of the list.
if ( !lights[i] )
break;
if ( !lights[i]->getCastShadows() )
continue;
if ( lights[i]->getType() != LightInfo::Point )
tmp = lights[i]->getDirection();
else
tmp = pos - lights[i]->getPosition();
range = lights[i]->getRange().x;
dist = ( (tmp.lenSquared()) / ((range * range) * 0.5f));
weight = mClampF( 1.0f - ( tmp.lenSquared() / (range * range)), 0.00001f, 1.0f );
if ( lights[i]->getType() == LightInfo::Vector )
fade = getMax( fade, 1.0f );
else
fade = getMax( fade, mClampF( 1.0f - dist, 0.00001f, 1.0f ) );
lightDir += tmp * weight;
lightCount++;
}
lightDir.normalize();
// No light... no shadow.
if ( !lights[0] )
return false;
// Has the light direction
// changed since last update?
bool lightDirChanged = !mLastLightDir.equal( lightDir );
// Has the parent object moved
// or scaled since the last update?
bool hasMoved = !mLastObjectPosition.equal( mParentObject->getRenderPosition() );
bool hasScaled = !mLastObjectScale.equal( mParentObject->getScale() );
// Set the last light direction
// to the current light direction.
mLastLightDir = lightDir;
mLastObjectPosition = mParentObject->getRenderPosition();
mLastObjectScale = mParentObject->getScale();
// Temps used to generate
// tangent vector for DecalInstance below.
VectorF right( 0, 0, 0 );
VectorF fwd( 0, 0, 0 );
VectorF tmpFwd( 0, 0, 0 );
U32 idx = lightDir.getLeastComponentIndex();
tmpFwd[idx] = 1.0f;
right = mCross( tmpFwd, lightDir );
fwd = mCross( lightDir, right );
right = mCross( fwd, lightDir );
right.normalize();
// Set up the world to light space
// matrix, along with proper position
// and rotation to be used as the world
// matrix for the render to texture later on.
static MatrixF sRotMat(EulerF( 0.0f, -(M_PI_F/2.0f), 0.0f));
mWorldToLight.identity();
MathUtils::getMatrixFromForwardVector( lightDir, &mWorldToLight );
mWorldToLight.setPosition( ( pos + boxCenter ) - ( ( (mRadius * smDepthAdjust) + 0.001f ) * lightDir ) );
mWorldToLight.mul( sRotMat );
mWorldToLight.inverse();
// Get the shapebase datablock if we have one.
ShapeBaseData *data = NULL;
if ( mShapeBase )
data = static_cast<ShapeBaseData*>( mShapeBase->getDataBlock() );
// We use the object box's extents multiplied
// by the object's scale divided by 2 for the radius
// because the object's worldsphere radius is not
// rotationally invariant.
mRadius = (objBox.getExtents() * mParentObject->getScale()).len() * 0.5f;
if ( data )
mRadius *= data->shadowSphereAdjust;
// Create the decal if we don't have one yet.
if ( !mDecalInstance )
mDecalInstance = gDecalManager->addDecal( decalPos,
lightDir,
right,
mDecalData,
1.0f,
0,
PermanentDecal | ClipDecal | CustomDecal );
if ( !mDecalInstance )
return false;
mDecalInstance->mVisibility = fade;
// Setup decal parameters.
mDecalInstance->mSize = mRadius * 2.0f;
mDecalInstance->mNormal = -lightDir;
mDecalInstance->mTangent = -right;
mDecalInstance->mRotAroundNormal = 0;
mDecalInstance->mPosition = decalPos;
mDecalInstance->mDataBlock = mDecalData;
// If the position of the world
// space box center is the same
// as the decal's position, and
// the light direction has not
// changed, we don't need to clip.
bool shouldClip = lightDirChanged || hasMoved || hasScaled;
// Now, check and see if the object is visible.
const Frustum &frust = state->getFrustum();
if ( frust.isCulled( SphereF( mDecalInstance->mPosition, mDecalInstance->mSize * mDecalInstance->mSize ) ) && !shouldClip )
return false;
F32 shadowLen = 10.0f;
if ( data )
shadowLen = data->shadowProjectionDistance;
const Point3F &boxExtents = objBox.getExtents();
mShadowLength = shadowLen * mParentObject->getScale().z;
// Set up clip depth, and box half
// offset for decal clipping.
Point2F clipParams( mShadowLength, (boxExtents.x + boxExtents.y) * 0.25f );
bool render = false;
bool clipSucceeded = true;
// Clip!
if ( shouldClip )
{
clipSucceeded = gDecalManager->clipDecal( mDecalInstance,
NULL,
&clipParams );
}
// If the clip failed,
// we'll return false in
// order to keep from
// unnecessarily rendering
// into the texture. If
// there was no reason to clip
// on this update, we'll assume we
// should update the texture.
render = clipSucceeded;
// Tell the DecalManager we've changed this decal.
gDecalManager->notifyDecalModified( mDecalInstance );
return render;
}
void ProjectedShadow::_calcScore( const SceneRenderState *state )
{
if ( !mDecalInstance )
return;
F32 pixRadius = mDecalInstance->calcPixelSize( state->getViewport().extent.y, state->getCameraPosition(), state->getWorldToScreenScale().y );
F32 pct = pixRadius / mDecalInstance->mDataBlock->fadeStartPixelSize;
U32 msSinceLastRender = Platform::getVirtualMilliseconds() - getLastRenderTime();
ShapeBaseData *data = NULL;
if ( mShapeBase )
data = static_cast<ShapeBaseData*>( mShapeBase->getDataBlock() );
// For every 1s this shadow hasn't been
// updated we'll add 10 to the score.
F32 secs = mFloor( (F32)msSinceLastRender / 1000.0f );
mScore = pct + secs;
mClampF( mScore, 0.0f, 2000.0f );
}
void ProjectedShadow::update( const SceneRenderState *state )
{
mUpdateTexture = true;
// Set the decal lod settings.
mDecalData->fadeStartPixelSize = smFadeStartPixelSize;
mDecalData->fadeEndPixelSize = smFadeEndPixelSize;
// Update our decal before
// we render to texture.
// If it fails, something bad happened
// (no light to grab/failed clip) and we should return.
if ( !_updateDecal( state ) )
{
// Release our shadow texture
// so that others can grab it out
// of the pool.
mShadowTexture = NULL;
mUpdateTexture = false;
return;
}
_calcScore( state );
if ( !mCasterPositionSC || !mCasterPositionSC->isValid() )
mCasterPositionSC = mDecalData->matInst->getMaterialParameterHandle( "$shadowCasterPosition" );
if ( !mShadowLengthSC || !mShadowLengthSC->isValid() )
mShadowLengthSC = mDecalData->matInst->getMaterialParameterHandle( "$shadowLength" );
MaterialParameters *matParams = mDecalData->matInst->getMaterialParameters();
matParams->setSafe( mCasterPositionSC, mParentObject->getRenderPosition() );
matParams->setSafe( mShadowLengthSC, mShadowLength / 4.0f );
}
void ProjectedShadow::render( F32 camDist, const TSRenderState &rdata )
{
if ( !mUpdateTexture )
return;
// Do the render to texture,
// DecalManager handles rendering
// the shadow onto the world.
_renderToTexture( camDist, rdata );
}
BaseMatInstance* ProjectedShadow::_getShadowMaterial( BaseMatInstance *inMat )
{
// See if we have an existing material hook.
ShadowMaterialHook *hook = static_cast<ShadowMaterialHook*>( inMat->getHook( ShadowMaterialHook::Type ) );
if ( !hook )
{
// Create a hook and initialize it using the incoming material.
hook = new ShadowMaterialHook;
hook->init( inMat );
inMat->addHook( hook );
}
return hook->getShadowMat( ShadowType_Spot );
}
void ProjectedShadow::_renderToTexture( F32 camDist, const TSRenderState &rdata )
{
PROFILE_SCOPE( ProjectedShadow_RenderToTexture );
GFXDEBUGEVENT_SCOPE( ProjectedShadow_RenderToTexture, ColorI( 255, 0, 0 ) );
RenderPassManager *renderPass = _getRenderPass();
if ( !renderPass )
return;
GFXTransformSaver saver;
// NOTE: GFXTransformSaver does not save/restore the frustum
// so we must save it here before we modify it.
F32 l, r, b, t, n, f;
bool ortho;
GFX->getFrustum( &l, &r, &b, &t, &n, &f, &ortho );
// Set the orthographic projection
// matrix up, to be based on the radius
// generated based on our shape.
GFX->setOrtho( -mRadius, mRadius, -mRadius, mRadius, 0.001f, (mRadius * 2) * smDepthAdjust, true );
// Set the world to light space
// matrix set up in shouldRender().
GFX->setWorldMatrix( mWorldToLight );
// Get the shapebase datablock if we have one.
ShapeBaseData *data = NULL;
if ( mShapeBase )
data = static_cast<ShapeBaseData*>( mShapeBase->getDataBlock() );
// Init or update the shadow texture size.
if ( mShadowTexture.isNull() || ( data && data->shadowSize != mShadowTexture.getWidth() ) )
{
U32 texSize = getNextPow2( data ? data->shadowSize : 256 * LightShadowMap::smShadowTexScalar );
mShadowTexture.set( texSize, texSize, GFXFormatR8G8B8A8, &PostFxTargetProfile, "BLShadow" );
}
GFX->pushActiveRenderTarget();
if ( !mRenderTarget )
mRenderTarget = GFX->allocRenderToTextureTarget();
mRenderTarget->attachTexture( GFXTextureTarget::DepthStencil, _getDepthTarget( mShadowTexture->getWidth(), mShadowTexture->getHeight() ) );
mRenderTarget->attachTexture( GFXTextureTarget::Color0, mShadowTexture );
GFX->setActiveRenderTarget( mRenderTarget );
GFX->clear( GFXClearZBuffer | GFXClearStencil | GFXClearTarget, ColorI( 0, 0, 0, 0 ), 1.0f, 0 );
const SceneRenderState *diffuseState = rdata.getSceneState();
SceneManager *sceneManager = diffuseState->getSceneManager();
SceneRenderState baseState
(
sceneManager,
SPT_Shadow,
SceneCameraState::fromGFXWithViewport( diffuseState->getViewport() ),
renderPass
);
baseState.getMaterialDelegate().bind( &ProjectedShadow::_getShadowMaterial );
baseState.setDiffuseCameraTransform( diffuseState->getCameraTransform() );
baseState.setWorldToScreenScale( diffuseState->getWorldToScreenScale() );
baseState.getCullingState().disableZoneCulling( true );
mParentObject->prepRenderImage( &baseState );
renderPass->renderPass( &baseState );
// Delete the SceneRenderState we allocated.
mRenderTarget->resolve();
GFX->popActiveRenderTarget();
// If we're close enough then filter the shadow.
if ( camDist < BasicLightManager::getShadowFilterDistance() )
{
if ( !smShadowFilter )
{
PostEffect *filter = NULL;
if ( !Sim::findObject( "BL_ShadowFilterPostFx", filter ) )
Con::errorf( "ProjectedShadow::_renderToTexture() - 'BL_ShadowFilterPostFx' not found!" );
smShadowFilter = filter;
}
if ( smShadowFilter )
smShadowFilter->process( NULL, mShadowTexture );
}
// Restore frustum
if (!ortho)
GFX->setFrustum(l, r, b, t, n, f);
else
GFX->setOrtho(l, r, b, t, n, f);
// Set the last render time.
mLastRenderTime = Platform::getVirtualMilliseconds();
// HACK: Will remove in future release!
mDecalInstance->mCustomTex = &mShadowTexture;
}
RenderPassManager* ProjectedShadow::_getRenderPass()
{
if ( smRenderPass.isNull() )
{
SimObject* renderPass = NULL;
if ( !Sim::findObject( "BL_ProjectedShadowRPM", renderPass ) )
Con::errorf( "ProjectedShadow::init() - 'BL_ProjectedShadowRPM' not initialized" );
else
smRenderPass = dynamic_cast<RenderPassManager*>(renderPass);
}
return smRenderPass;
}
GFXTextureObject* ProjectedShadow::_getDepthTarget( U32 width, U32 height )
{
// Get a depth texture target from the pooled profile
// which is returned as a temporary.
GFXTexHandle depthTex( width, height, GFXFormatD24S8, &BLProjectedShadowZProfile,
"ProjectedShadow::_getDepthTarget()" );
return depthTex;
}

View file

@ -0,0 +1,127 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _PROJECTEDSHADOW_H_
#define _PROJECTEDSHADOW_H_
#ifndef _DEPTHSORTLIST_H_
#include "collision/depthSortList.h"
#endif
#ifndef _SCENEOBJECT_H_
#include "scene/sceneObject.h"
#endif
#ifndef _TSSHAPEINSTANCE_H_
#include "ts/tsShapeInstance.h"
#endif
#ifndef _LIGHTINGSYSTEM_SHADOWBASE_H_
#include "lighting/common/shadowBase.h"
#endif
class ShapeBase;
class LightInfo;
class DecalData;
class DecalInstance;
class RenderPassManager;
class PostEffect;
class RenderMeshMgr;
class CustomMaterial;
class BaseMatInstance;
class MaterialParameterHandle;
GFX_DeclareTextureProfile( BLProjectedShadowProfile );
GFX_DeclareTextureProfile( BLProjectedShadowZProfile );
class ProjectedShadow : public ShadowBase
{
protected:
/// This parameter is used to
/// adjust the far plane out for our
/// orthographic render in order to
/// force our object towards one end of the
/// the eye space depth range.
static F32 smDepthAdjust;
F32 mRadius;
MatrixF mWorldToLight;
U32 mLastRenderTime;
F32 mShadowLength;
F32 mScore;
bool mUpdateTexture;
Point3F mLastObjectScale;
Point3F mLastObjectPosition;
VectorF mLastLightDir;
DecalData *mDecalData;
DecalInstance *mDecalInstance;
SceneObject *mParentObject;
ShapeBase *mShapeBase;
MaterialParameterHandle *mCasterPositionSC;
MaterialParameterHandle *mShadowLengthSC;
static SimObjectPtr<RenderPassManager> smRenderPass;
static SimObjectPtr<PostEffect> smShadowFilter;
static RenderPassManager* _getRenderPass();
GFXTexHandle mShadowTexture;
GFXTextureTargetRef mRenderTarget;
GFXTextureObject* _getDepthTarget( U32 width, U32 height );
void _renderToTexture( F32 camDist, const TSRenderState &rdata );
bool _updateDecal( const SceneRenderState *sceneState );
void _calcScore( const SceneRenderState *state );
/// Returns a spotlight shadow material for use when
/// rendering meshes into the projected shadow.
static BaseMatInstance* _getShadowMaterial( BaseMatInstance *inMat );
public:
/// @see DecalData
static float smFadeStartPixelSize;
static float smFadeEndPixelSize;
ProjectedShadow( SceneObject *object );
virtual ~ProjectedShadow();
bool shouldRender( const SceneRenderState *state );
void update( const SceneRenderState *state );
void render( F32 camDist, const TSRenderState &rdata );
U32 getLastRenderTime() const { return mLastRenderTime; }
const F32 getScore() const { return mScore; }
};
#endif // _PROJECTEDSHADOW_H_

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,254 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _SCENELIGHTING_H_
#define _SCENELIGHTING_H_
#ifndef _SCENEOBJECT_H_
#include "scene/sceneObject.h"
#endif
#ifndef _SGSCENEPERSIST_H_
#include "lighting/common/scenePersist.h"
#endif
#ifndef _LIGHTINFO_H_
#include "lighting/lightInfo.h"
#endif
class ShadowVolumeBSP;
class LightInfo;
class AvailableSLInterfaces;
class SceneLighting : public SimObject
{
typedef SimObject Parent;
protected:
AvailableSLInterfaces* mLightingInterfaces;
virtual void getMLName(const char* misName, const U32 missionCRC, const U32 buffSize, char* filenameBuffer);
public:
S32 sgTimeTemp;
S32 sgTimeTemp2;
virtual void sgNewEvent(U32 light, S32 object, U32 event);
virtual void sgLightingStartEvent();
virtual void sgLightingCompleteEvent();
virtual void sgTGEPassSetupEvent();
virtual void sgTGELightStartEvent(U32 light);
virtual void sgTGELightProcessEvent(U32 light, S32 object);
virtual void sgTGELightCompleteEvent(U32 light);
virtual void sgTGESetProgress(U32 light, S32 object);
virtual void sgSGPassSetupEvent();
virtual void sgSGObjectStartEvent(S32 object);
virtual void sgSGObjectProcessEvent(U32 light, S32 object);
virtual void sgSGObjectCompleteEvent(S32 object);
virtual void sgSGSetProgress(U32 light, S32 object);
// 'sg' prefix omitted to conform with existing 'addInterior' method...
void addStatic(ShadowVolumeBSP *shadowVolume, SceneObject *sceneobject, LightInfo *light, S32 level);
// persist objects moved to 'sgScenePersist.h' for clarity...
// everything below this line should be original code...
U32 calcMissionCRC();
bool verifyMissionInfo(PersistInfo::PersistChunk *);
bool getMissionInfo(PersistInfo::PersistChunk *);
bool loadPersistInfo(const char *);
bool savePersistInfo(const char *);
class ObjectProxy;
enum {
SHADOW_DETAIL = -1
};
//------------------------------------------------------------------------------
/// Create a proxy for each object to store data.
class ObjectProxy
{
public:
SimObjectPtr<SceneObject> mObj;
U32 mChunkCRC;
ObjectProxy(SceneObject * obj) : mObj(obj){mChunkCRC = 0;}
virtual ~ObjectProxy(){}
SceneObject * operator->() {return(mObj);}
SceneObject * getObject() {return(mObj);}
/// @name Lighting Interface
/// @{
virtual bool loadResources() {return(true);}
virtual void init() {}
virtual bool tgePreLight(LightInfo* light) { return preLight(light); }
virtual bool preLight(LightInfo *) {return(false);}
virtual void light(LightInfo *) {}
virtual void postLight(bool lastLight) {}
/// @}
/// @name Lighting events
/// @{
// Called when the lighting process begins
virtual void processLightingStart() {}
// Called when a TGELight event is started, return true if status has been reported to console
virtual void processTGELightProcessEvent(U32 curr, U32 max, LightInfo*) { Con::printf(" Lighting object %d of %d...", (curr+1), max); }
// Called for lighting kit lights
virtual bool processStartObjectLightingEvent(U32 current, U32 max) { Con::printf(" Lighting object %d of %d... %s: %s", (current+1), max, mObj->getClassName(), mObj->getName()); return true; }
// Called once per object and SG light - used for calling light on an object
virtual void processSGObjectProcessEvent(LightInfo* currLight) { light(currLight); };
/// @}
/// @name Persistence
///
/// We cache lighting information to cut down on load times.
///
/// There are flags such as ForceAlways and LoadOnly which allow you
/// to control this behaviour.
/// @{
bool calcValidation();
bool isValidChunk(PersistInfo::PersistChunk *);
virtual U32 getResourceCRC() = 0;
virtual bool setPersistInfo(PersistInfo::PersistChunk *);
virtual bool getPersistInfo(PersistInfo::PersistChunk *);
/// @}
// Called to figure out if this object should be added to the shadow volume
virtual bool supportsShadowVolume() { return false; }
// Called to retrieve the clip planes of the object. Currently used for terrain lighting, but could be used to speed up other
// lighting calculations.
virtual void getClipPlanes(Vector<PlaneF>& planes) { }
// Called to add the object to the shadow volume
virtual void addToShadowVolume(ShadowVolumeBSP * shadowVolume, LightInfo * light, S32 level) { } ;
};
typedef Vector<ObjectProxy*> ObjectProxyList;
ObjectProxyList mSceneObjects;
ObjectProxyList mLitObjects;
LightInfoList mLights;
SceneLighting(AvailableSLInterfaces* lightingInterfaces);
~SceneLighting();
enum Flags {
ForceAlways = BIT(0), ///< Regenerate the scene lighting no matter what.
ForceWritable = BIT(1), ///< Regenerate the scene lighting only if we can write to the lighting cache files.
LoadOnly = BIT(2), ///< Just load cached lighting data.
};
bool lightScene(const char *, BitSet32 flags = 0);
bool isLighting();
S32 mStartTime;
char mFileName[1024];
SceneManager * mSceneManager;
bool light(BitSet32);
void completed(bool success);
void processEvent(U32 light, S32 object);
void processCache();
};
class sgSceneLightingProcessEvent : public SimEvent
{
private:
U32 sgLightIndex;
S32 sgObjectIndex;
U32 sgEvent;
public:
enum sgEventTypes
{
sgLightingStartEventType,
sgLightingCompleteEventType,
sgSGPassSetupEventType,
sgSGObjectStartEventType,
sgSGObjectCompleteEventType,
sgSGObjectProcessEventType,
sgTGEPassSetupEventType,
sgTGELightStartEventType,
sgTGELightCompleteEventType,
sgTGELightProcessEventType
};
sgSceneLightingProcessEvent(U32 lightIndex, S32 objectIndex, U32 event)
{
sgLightIndex = lightIndex;
sgObjectIndex = objectIndex;
sgEvent = event;
}
void process(SimObject * object)
{
AssertFatal(object, "SceneLightingProcessEvent:: null event object!");
if(!object)
return;
SceneLighting *sl = static_cast<SceneLighting*>(object);
switch(sgEvent)
{
case sgLightingStartEventType:
sl->sgLightingStartEvent();
break;
case sgLightingCompleteEventType:
sl->sgLightingCompleteEvent();
break;
case sgTGEPassSetupEventType:
sl->sgTGEPassSetupEvent();
break;
case sgTGELightStartEventType:
sl->sgTGELightStartEvent(sgLightIndex);
break;
case sgTGELightProcessEventType:
sl->sgTGELightProcessEvent(sgLightIndex, sgObjectIndex);
break;
case sgTGELightCompleteEventType:
sl->sgTGELightCompleteEvent(sgLightIndex);
break;
case sgSGPassSetupEventType:
sl->sgSGPassSetupEvent();
break;
case sgSGObjectStartEventType:
sl->sgSGObjectStartEvent(sgObjectIndex);
break;
case sgSGObjectProcessEventType:
sl->sgSGObjectProcessEvent(sgLightIndex, sgObjectIndex);
break;
case sgSGObjectCompleteEventType:
sl->sgSGObjectCompleteEvent(sgObjectIndex);
break;
default:
return;
};
};
};
extern SceneLighting *gLighting;
#endif//_SGSCENELIGHTING_H_

View file

@ -0,0 +1,66 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
static const Point3F BoxNormals[] =
{
Point3F( 1, 0, 0),
Point3F(-1, 0, 0),
Point3F( 0, 1, 0),
Point3F( 0,-1, 0),
Point3F( 0, 0, 1),
Point3F( 0, 0,-1)
};
static U32 BoxVerts[][4] = {
{7,6,4,5}, // +x
{0,2,3,1}, // -x
{7,3,2,6}, // +y
{0,1,5,4}, // -y
{7,5,1,3}, // +z
{0,4,6,2} // -z
};
static U32 BoxSharedEdgeMask[][6] = {
{0, 0, 1, 4, 8, 2},
{0, 0, 2, 8, 4, 1},
{8, 2, 0, 0, 1, 4},
{4, 1, 0, 0, 2, 8},
{1, 4, 8, 2, 0, 0},
{2, 8, 4, 1, 0, 0}
};
static Point3F BoxPnts[] = {
Point3F(0,0,0),
Point3F(0,0,1),
Point3F(0,1,0),
Point3F(0,1,1),
Point3F(1,0,0),
Point3F(1,0,1),
Point3F(1,1,0),
Point3F(1,1,1)
};
extern SceneLighting *gLighting;
extern F32 gParellelVectorThresh;
extern F32 gPlaneNormThresh;
extern F32 gPlaneDistThresh;

View file

@ -0,0 +1,144 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "platform/platform.h"
#include "lighting/common/scenePersist.h"
#include "lighting/lightingInterfaces.h"
#include "scene/sceneManager.h"
#include "lighting/lightManager.h"
U32 PersistInfo::smFileVersion = 0x11;
PersistInfo::~PersistInfo()
{
for(U32 i = 0; i < mChunks.size(); i++)
delete mChunks[i];
}
//------------------------------------------------------------------------------
bool PersistInfo::read(Stream & stream)
{
U32 version;
if(!stream.read(&version) || version != smFileVersion)
return(false);
U32 numChunks;
if(!stream.read(&numChunks))
return(false);
if(numChunks == 0)
return(false);
// read in all the chunks
for(U32 i = 0; i < numChunks; i++)
{
U32 chunkType;
if(!stream.read(&chunkType))
return(false);
// MissionChunk must be first chunk
if(i == 0 && chunkType != PersistChunk::MissionChunkType)
return(false);
if(i != 0 && chunkType == PersistChunk::MissionChunkType)
return(false);
// Create the right chunk for the system
bool bChunkFound = false;
SceneLightingInterfaces sli = LIGHTMGR->getSceneLightingInterface()->mAvailableSystemInterfaces;
for(SceneLightingInterface** itr = sli.begin(); itr != sli.end() && !bChunkFound; itr++)
{
PersistInfo::PersistChunk* pc = (*itr)->createPersistChunk(chunkType);
if (pc != NULL)
{
mChunks.push_back(pc);
bChunkFound = true;
}
}
if (!bChunkFound)
{
// create the chunk
switch(chunkType)
{
case PersistChunk::MissionChunkType:
mChunks.push_back(new PersistInfo::MissionChunk);
break;
default:
return(false);
break;
}
}
// load the chunk info
if(!mChunks[i]->read(stream))
return(false);
}
return(true);
}
bool PersistInfo::write(Stream & stream)
{
if(!stream.write(smFileVersion))
return(false);
if(!stream.write((U32)mChunks.size()))
return(false);
for(U32 i = 0; i < mChunks.size(); i++)
{
if(!stream.write(mChunks[i]->mChunkType))
return(false);
if(!mChunks[i]->write(stream))
return(false);
}
return(true);
}
//------------------------------------------------------------------------------
// Class SceneLighting::PersistInfo::PersistChunk
//------------------------------------------------------------------------------
bool PersistInfo::PersistChunk::read(Stream & stream)
{
if(!stream.read(&mChunkCRC))
return(false);
return(true);
}
bool PersistInfo::PersistChunk::write(Stream & stream)
{
if(!stream.write(mChunkCRC))
return(false);
return(true);
}
//------------------------------------------------------------------------------
// Class SceneLighting::PersistInfo::MissionChunk
//------------------------------------------------------------------------------
PersistInfo::MissionChunk::MissionChunk()
{
mChunkType = PersistChunk::MissionChunkType;
}

View file

@ -0,0 +1,68 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _SGSCENEPERSIST_H_
#define _SGSCENEPERSIST_H_
#ifndef _TVECTOR_H_
#include "core/util/tVector.h"
#endif
class Stream;
struct PersistInfo
{
struct PersistChunk
{
enum {
MissionChunkType = 0,
InteriorChunkType,
TerrainChunkType,
AtlasLightMapChunkType
};
U32 mChunkType;
U32 mChunkCRC;
virtual ~PersistChunk() {}
virtual bool read(Stream &);
virtual bool write(Stream &);
};
struct MissionChunk : public PersistChunk
{
typedef PersistChunk Parent;
MissionChunk();
};
~PersistInfo();
Vector<PersistChunk*> mChunks;
static U32 smFileVersion;
bool read(Stream &);
bool write(Stream &);
};
#endif//_SGSCENEPERSIST_H_

View file

@ -0,0 +1,40 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _LIGHTINGSYSTEM_SHADOWBASE_H_
#define _LIGHTINGSYSTEM_SHADOWBASE_H_
class TSRenderState;
class ShadowBase
{
public:
virtual ~ShadowBase() {}
virtual bool shouldRender( const SceneRenderState *state ) = 0;
virtual void update( const SceneRenderState *state ) = 0;
virtual void render(F32 camDist, const TSRenderState &rdata ) = 0;
virtual U32 getLastRenderTime() const = 0;
virtual const F32 getScore() const = 0;
};
#endif

View file

@ -0,0 +1,731 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "platform/platform.h"
#include "lighting/common/shadowVolumeBSP.h"
#include "lighting/lightInfo.h"
#include "math/mPlane.h"
ShadowVolumeBSP::ShadowVolumeBSP() :
mSVRoot(0),
mNodeStore(0),
mPolyStore(0),
mFirstInteriorNode(0)
{
}
ShadowVolumeBSP::~ShadowVolumeBSP()
{
for(U32 i = 0; i < mSurfaces.size(); i++)
delete mSurfaces[i];
}
void ShadowVolumeBSP::insertShadowVolume(SVNode ** root, U32 volume)
{
SVNode * traverse = mShadowVolumes[volume];
// insert 'em
while(traverse)
{
// copy it
*root = createNode();
(*root)->mPlaneIndex = traverse->mPlaneIndex;
(*root)->mSurfaceInfo = traverse->mSurfaceInfo;
(*root)->mShadowVolume = traverse->mShadowVolume;
// do the next
root = &(*root)->mFront;
traverse = traverse->mFront;
}
}
ShadowVolumeBSP::SVNode::Side ShadowVolumeBSP::whichSide(SVPoly * poly, const PlaneF & plane) const
{
bool front = false;
bool back = false;
for(U32 i = 0; i < poly->mWindingCount; i++)
{
switch(plane.whichSide(poly->mWinding[i]))
{
case PlaneF::Front:
if(back)
return(SVNode::Split);
front = true;
break;
case PlaneF::Back:
if(front)
return(SVNode::Split);
back = true;
break;
default:
break;
}
}
AssertFatal(!(front && back), "ShadowVolumeBSP::whichSide - failed to classify poly");
if(!front && !back)
return(SVNode::On);
return(front ? SVNode::Front : SVNode::Back);
}
void ShadowVolumeBSP::splitPoly(SVPoly * poly, const PlaneF & plane, SVPoly ** front, SVPoly ** back)
{
PlaneF::Side sides[SVPoly::MaxWinding];
U32 i;
for(i = 0; i < poly->mWindingCount; i++)
sides[i] = plane.whichSide(poly->mWinding[i]);
// create the polys
(*front) = createPoly();
(*back) = createPoly();
// copy the info
(*front)->mWindingCount = (*back)->mWindingCount = 0;
(*front)->mPlane = (*back)->mPlane = poly->mPlane;
(*front)->mTarget = (*back)->mTarget = poly->mTarget;
(*front)->mSurfaceInfo = (*back)->mSurfaceInfo = poly->mSurfaceInfo;
(*front)->mShadowVolume = (*back)->mShadowVolume = poly->mShadowVolume;
//
for(i = 0; i < poly->mWindingCount; i++)
{
U32 j = (i+1) % poly->mWindingCount;
if(sides[i] == PlaneF::On)
{
(*front)->mWinding[(*front)->mWindingCount++] = poly->mWinding[i];
(*back)->mWinding[(*back)->mWindingCount++] = poly->mWinding[i];
}
else if(sides[i] == PlaneF::Front)
{
(*front)->mWinding[(*front)->mWindingCount++] = poly->mWinding[i];
if(sides[j] == PlaneF::Back)
{
const Point3F & a = poly->mWinding[i];
const Point3F & b = poly->mWinding[j];
F32 t = plane.intersect(a, b);
AssertFatal(t >=0 && t <= 1, "ShadowVolumeBSP::splitPoly - bad plane intersection");
Point3F pos;
pos.interpolate(a, b, t);
//
(*front)->mWinding[(*front)->mWindingCount++] =
(*back)->mWinding[(*back)->mWindingCount++] = pos;
}
}
else if(sides[i] == PlaneF::Back)
{
(*back)->mWinding[(*back)->mWindingCount++] = poly->mWinding[i];
if(sides[j] == PlaneF::Front)
{
const Point3F & a = poly->mWinding[i];
const Point3F & b = poly->mWinding[j];
F32 t = plane.intersect(a, b);
AssertFatal(t >=0 && t <= 1, "ShadowVolumeBSP::splitPoly - bad plane intersection");
Point3F pos;
pos.interpolate(a, b, t);
(*front)->mWinding[(*front)->mWindingCount++] =
(*back)->mWinding[(*back)->mWindingCount++] = pos;
}
}
}
AssertFatal((*front)->mWindingCount && (*back)->mWindingCount, "ShadowVolume::split - invalid split");
}
void ShadowVolumeBSP::addUniqueVolume(SurfaceInfo * surfaceInfo, U32 volume)
{
if(!surfaceInfo)
return;
for(U32 i = 0; i < surfaceInfo->mShadowed.size(); i++)
if(surfaceInfo->mShadowed[i] == volume)
return;
// add it
surfaceInfo->mShadowed.push_back(volume);
}
void ShadowVolumeBSP::insertPoly(SVNode ** root, SVPoly * poly)
{
if(!(*root))
{
insertShadowVolume(root, poly->mShadowVolume);
if(poly->mSurfaceInfo && !mFirstInteriorNode)
mFirstInteriorNode = mShadowVolumes[poly->mShadowVolume];
if(poly->mTarget)
addUniqueVolume(poly->mTarget->mSurfaceInfo, poly->mShadowVolume);
recyclePoly(poly);
return;
}
const PlaneF & plane = getPlane((*root)->mPlaneIndex);
//
switch(whichSide(poly, plane))
{
case SVNode::On:
case SVNode::Front:
insertPolyFront(root, poly);
break;
case SVNode::Back:
insertPolyBack(root, poly);
break;
case SVNode::Split:
{
SVPoly * front;
SVPoly * back;
splitPoly(poly, plane, &front, &back);
recyclePoly(poly);
insertPolyFront(root, front);
insertPolyBack(root, back);
break;
}
}
}
void ShadowVolumeBSP::insertPolyFront(SVNode ** root, SVPoly * poly)
{
// POLY type node?
if(!(*root)->mFront)
{
if(poly->mSurfaceInfo && !mFirstInteriorNode)
mFirstInteriorNode = mShadowVolumes[poly->mShadowVolume];
addUniqueVolume(poly->mSurfaceInfo, (*root)->mShadowVolume);
recyclePoly(poly);
}
else
insertPoly(&(*root)->mFront, poly);
}
void ShadowVolumeBSP::insertPolyBack(SVNode ** root, SVPoly * poly)
{
// list of nodes where an interior has been added
if(poly->mSurfaceInfo && !(*root)->mSurfaceInfo && !(*root)->mBack)
{
if(!mFirstInteriorNode)
mFirstInteriorNode = mShadowVolumes[poly->mShadowVolume];
mParentNodes.push_back(*root);
}
// POLY type node?
if(!(*root)->mFront)
{
poly->mTarget = (*root);
insertPoly(&(*root)->mBack, poly);
}
else
insertPoly(&(*root)->mBack, poly);
}
//------------------------------------------------------------------------------
ShadowVolumeBSP::SVNode * ShadowVolumeBSP::createNode()
{
SVNode * node;
if(mNodeStore)
{
node = mNodeStore;
mNodeStore = mNodeStore->mFront;
}
else
node = mNodeChunker.alloc();
//
node->mFront = node->mBack = 0;
node->mShadowVolume = 0;
node->mSurfaceInfo = 0;
return(node);
}
void ShadowVolumeBSP::recycleNode(SVNode * node)
{
if(!node)
return;
recycleNode(node->mFront);
recycleNode(node->mBack);
//
node->mFront = mNodeStore;
node->mBack = 0;
mNodeStore = node;
}
ShadowVolumeBSP::SVPoly * ShadowVolumeBSP::createPoly()
{
SVPoly * poly;
if(mPolyStore)
{
poly = mPolyStore;
mPolyStore = mPolyStore->mNext;
}
else
poly = mPolyChunker.alloc();
//
poly->mNext = 0;
poly->mTarget = 0;
poly->mSurfaceInfo = 0;
poly->mShadowVolume = 0;
poly->mWindingCount = 0;
for (U32 i = 0; i < SVPoly::MaxWinding; i++)
poly->mWinding[i] = Point3F(0.0f, 0.0f, 0.0f);
return(poly);
}
void ShadowVolumeBSP::recyclePoly(SVPoly * poly)
{
if(!poly)
return;
recyclePoly(poly->mNext);
//
poly->mNext = mPolyStore;
mPolyStore = poly;
}
U32 ShadowVolumeBSP::insertPlane(const PlaneF & plane)
{
mPlanes.push_back(plane);
return(mPlanes.size() - 1);
}
const PlaneF & ShadowVolumeBSP::getPlane(U32 index) const
{
AssertFatal(index < mPlanes.size(), "ShadowVolumeBSP::getPlane - index out of range");
return(mPlanes[index]);
}
ShadowVolumeBSP::SVNode * ShadowVolumeBSP::getShadowVolume(U32 index)
{
AssertFatal(index < mShadowVolumes.size(), "ShadowVolumeBSP::getShadowVolume - index out of range");
return(mShadowVolumes[index]);
}
bool ShadowVolumeBSP::testPoint(SVNode * root, const Point3F & pnt)
{
const PlaneF & plane = getPlane(root->mPlaneIndex);
switch(plane.whichSide(pnt))
{
case PlaneF::On:
if(!root->mFront)
return(true);
else
{
if(testPoint(root->mFront, pnt))
return(true);
else
{
if(!root->mBack)
return(false);
else
return(testPoint(root->mBack, pnt));
}
}
break;
//
case PlaneF::Front:
if(root->mFront)
return(testPoint(root->mFront, pnt));
else
return(true);
break;
//
case PlaneF::Back:
if(root->mBack)
return(testPoint(root->mBack, pnt));
else
return(false);
break;
}
return(false);
}
//------------------------------------------------------------------------------
bool ShadowVolumeBSP::testPoly(SVNode * root, SVPoly * poly)
{
const PlaneF & plane = getPlane(root->mPlaneIndex);
switch(whichSide(poly, plane))
{
case SVNode::On:
case SVNode::Front:
if(root->mFront)
return(testPoly(root->mFront, poly));
recyclePoly(poly);
return(true);
case SVNode::Back:
if(root->mBack)
return(testPoly(root->mBack, poly));
recyclePoly(poly);
break;
case SVNode::Split:
{
if(!root->mFront)
{
recyclePoly(poly);
return(true);
}
SVPoly * front;
SVPoly * back;
splitPoly(poly, plane, &front, &back);
recyclePoly(poly);
if(testPoly(root->mFront, front))
{
recyclePoly(back);
return(true);
}
if(root->mBack)
return(testPoly(root->mBack, back));
recyclePoly(back);
break;
}
}
return(false);
}
//------------------------------------------------------------------------------
void ShadowVolumeBSP::buildPolyVolume(SVPoly * poly, LightInfo * light)
{
if(light->getType() != LightInfo::Vector)
return;
// build the poly
Point3F pointOffset = light->getDirection() * 10.f;
// create the shadow volume
mShadowVolumes.increment();
SVNode ** traverse = &mShadowVolumes.last();
U32 shadowVolumeIndex = mShadowVolumes.size() - 1;
for(U32 i = 0; i < poly->mWindingCount; i++)
{
U32 j = (i + 1) % poly->mWindingCount;
if(poly->mWinding[i] == poly->mWinding[j])
continue;
(*traverse) = createNode();
Point3F & a = poly->mWinding[i];
Point3F & b = poly->mWinding[j];
Point3F c = b + pointOffset;
(*traverse)->mPlaneIndex = insertPlane(PlaneF(a,b,c));
(*traverse)->mShadowVolume = shadowVolumeIndex;
traverse = &(*traverse)->mFront;
}
// do the poly node
(*traverse) = createNode();
(*traverse)->mPlaneIndex = insertPlane(poly->mPlane);
(*traverse)->mShadowVolume = poly->mShadowVolume = shadowVolumeIndex;
}
ShadowVolumeBSP::SVPoly * ShadowVolumeBSP::copyPoly(SVPoly * src)
{
SVPoly * poly = createPoly();
dMemcpy(poly, src, sizeof(SVPoly));
poly->mTarget = 0;
poly->mNext = 0;
return(poly);
}
//------------------------------------------------------------------------------
void ShadowVolumeBSP::addToPolyList(SVPoly ** store, SVPoly * poly) const
{
poly->mNext = *store;
*store = poly;
}
//------------------------------------------------------------------------------
void ShadowVolumeBSP::clipPoly(SVNode * root, SVPoly ** store, SVPoly * poly)
{
if(!root)
{
recyclePoly(poly);
return;
}
const PlaneF & plane = getPlane(root->mPlaneIndex);
switch(whichSide(poly, plane))
{
case SVNode::On:
case SVNode::Back:
if(root->mBack)
clipPoly(root->mBack, store, poly);
else
addToPolyList(store, poly);
break;
case SVNode::Front:
// encountered POLY node?
if(!root->mFront)
{
recyclePoly(poly);
return;
}
else
clipPoly(root->mFront, store, poly);
break;
case SVNode::Split:
{
SVPoly * front;
SVPoly * back;
splitPoly(poly, plane, &front, &back);
AssertFatal(front && back, "ShadowVolumeBSP::clipPoly: invalid split");
recyclePoly(poly);
// front
if(!root->mFront)
{
recyclePoly(front);
return;
}
else
clipPoly(root->mFront, store, front);
// back
if(root->mBack)
clipPoly(root->mBack, store, back);
else
addToPolyList(store, back);
break;
}
}
}
// clip a poly to it's own shadow volume
void ShadowVolumeBSP::clipToSelf(SVNode * root, SVPoly ** store, SVPoly * poly)
{
if(!root)
{
addToPolyList(store, poly);
return;
}
const PlaneF & plane = getPlane(root->mPlaneIndex);
switch(whichSide(poly, plane))
{
case SVNode::Front:
clipToSelf(root->mFront, store, poly);
break;
case SVNode::On:
addToPolyList(store, poly);
break;
case SVNode::Back:
recyclePoly(poly);
break;
case SVNode::Split:
{
SVPoly * front = 0;
SVPoly * back = 0;
splitPoly(poly, plane, &front, &back);
AssertFatal(front && back, "ShadowVolumeBSP::clipToSelf: invalid split");
recyclePoly(poly);
recyclePoly(back);
clipToSelf(root->mFront, store, front);
break;
}
}
}
//------------------------------------------------------------------------------
F32 ShadowVolumeBSP::getPolySurfaceArea(SVPoly * poly) const
{
if(!poly)
return(0.f);
Point3F areaNorm(0,0,0);
for(U32 i = 0; i < poly->mWindingCount; i++)
{
U32 j = (i + 1) % poly->mWindingCount;
Point3F tmp;
mCross(poly->mWinding[i], poly->mWinding[j], &tmp);
areaNorm += tmp;
}
F32 area = mDot(poly->mPlane, areaNorm);
if(area < 0.f)
area *= -0.5f;
else
area *= 0.5f;
if(poly->mNext)
area += getPolySurfaceArea(poly->mNext);
return(area);
}
//------------------------------------------------------------------------------
F32 ShadowVolumeBSP::getClippedSurfaceArea(SVNode * root, SVPoly * poly)
{
SVPoly * store = 0;
clipPoly(root, &store, poly);
F32 area = getPolySurfaceArea(store);
recyclePoly(store);
return(area);
}
//-------------------------------------------------------------------------------
// Class SceneLighting::ShadowVolumeBSP
//-------------------------------------------------------------------------------
void ShadowVolumeBSP::movePolyList(SVPoly ** dest, SVPoly * list) const
{
while(list)
{
SVPoly * next = list->mNext;
addToPolyList(dest, list);
list = next;
}
}
F32 ShadowVolumeBSP::getLitSurfaceArea(SVPoly * poly, SurfaceInfo * surfaceInfo)
{
// clip the poly to the shadow volumes
SVPoly * polyStore = poly;
for(U32 i = 0; polyStore && (i < surfaceInfo->mShadowed.size()); i++)
{
SVPoly * polyList = 0;
SVPoly * traverse = polyStore;
while(traverse)
{
SVPoly * next = traverse->mNext;
traverse->mNext = 0;
SVPoly * currentStore = 0;
clipPoly(mShadowVolumes[surfaceInfo->mShadowed[i]], &currentStore, traverse);
if(currentStore)
movePolyList(&polyList, currentStore);
traverse = next;
}
polyStore = polyList;
}
// get the lit area
F32 area = getPolySurfaceArea(polyStore);
recyclePoly(polyStore);
return(area);
}
//------------------------------------------------------------------------------
void ShadowVolumeBSP::removeLastInterior()
{
if(!mSVRoot || !mFirstInteriorNode)
return;
AssertFatal(mFirstInteriorNode->mSurfaceInfo, "No surface info for first interior node!");
// reset the planes
mPlanes.setSize(mFirstInteriorNode->mPlaneIndex);
U32 i;
// flush the shadow volumes
for(i = mFirstInteriorNode->mShadowVolume; i < mShadowVolumes.size(); i++)
recycleNode(mShadowVolumes[i]);
mShadowVolumes.setSize(mFirstInteriorNode->mShadowVolume);
// flush the interior nodes
if(!mParentNodes.size() && (mFirstInteriorNode->mShadowVolume == mSVRoot->mShadowVolume))
{
recycleNode(mSVRoot);
mSVRoot = 0;
}
else
{
for(i = 0; i < mParentNodes.size(); i++)
{
recycleNode(mParentNodes[i]->mBack);
mParentNodes[i]->mBack = 0;
}
}
// flush the surfaces
for(i = 0; i < mSurfaces.size(); i++)
delete mSurfaces[i];
mSurfaces.clear();
mFirstInteriorNode = 0;
}

View file

@ -0,0 +1,154 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _SHADOWVOLUMEBSP_H_
#define _SHADOWVOLUMEBSP_H_
#ifndef _TVECTOR_H_
#include "core/util/tVector.h"
#endif
#ifndef _MMATH_H_
#include "math/mMath.h"
#endif
#ifndef _DATACHUNKER_H_
#include "core/dataChunker.h"
#endif
#ifndef _LIGHTMANAGER_H_
#include "lighting/lightManager.h"
#endif
/// Used to calculate shadows.
class ShadowVolumeBSP
{
public:
ShadowVolumeBSP();
~ShadowVolumeBSP();
struct SVNode;
struct SurfaceInfo
{
U32 mSurfaceIndex;
U32 mPlaneIndex;
Vector<U32> mShadowed;
SVNode * mShadowVolume;
};
struct SVNode
{
enum Side
{
Front = 0,
Back = 1,
On = 2,
Split = 3
};
SVNode * mFront;
SVNode * mBack;
U32 mPlaneIndex;
U32 mShadowVolume;
/// Used with shadowed interiors.
SurfaceInfo * mSurfaceInfo;
};
struct SVPoly
{
enum {
MaxWinding = 32
};
U32 mWindingCount;
Point3F mWinding[MaxWinding];
PlaneF mPlane;
SVNode * mTarget;
U32 mShadowVolume;
SVPoly * mNext;
SurfaceInfo * mSurfaceInfo;
};
void insertPoly(SVNode **, SVPoly *);
void insertPolyFront(SVNode **, SVPoly *);
void insertPolyBack(SVNode **, SVPoly *);
void splitPoly(SVPoly *, const PlaneF &, SVPoly **, SVPoly **);
void insertShadowVolume(SVNode **, U32);
void addUniqueVolume(SurfaceInfo *, U32);
SVNode::Side whichSide(SVPoly *, const PlaneF &) const;
//
bool testPoint(SVNode *, const Point3F &);
bool testPoly(SVNode *, SVPoly *);
void addToPolyList(SVPoly **, SVPoly *) const;
void clipPoly(SVNode *, SVPoly **, SVPoly *);
void clipToSelf(SVNode *, SVPoly **, SVPoly *);
F32 getPolySurfaceArea(SVPoly *) const;
F32 getClippedSurfaceArea(SVNode *, SVPoly *);
void movePolyList(SVPoly **, SVPoly *) const;
F32 getLitSurfaceArea(SVPoly *, SurfaceInfo *);
Vector<SurfaceInfo *> mSurfaces;
Chunker<SVNode> mNodeChunker;
Chunker<SVPoly> mPolyChunker;
SVNode * createNode();
void recycleNode(SVNode *);
SVPoly * createPoly();
void recyclePoly(SVPoly *);
U32 insertPlane(const PlaneF &);
const PlaneF & getPlane(U32) const;
//
SVNode * mSVRoot;
Vector<SVNode*> mShadowVolumes;
SVNode * getShadowVolume(U32);
Vector<PlaneF> mPlanes;
SVNode * mNodeStore;
SVPoly * mPolyStore;
// used to remove the last inserted interior from the tree
Vector<SVNode*> mParentNodes;
SVNode * mFirstInteriorNode;
void removeLastInterior();
/// @name Access functions
/// @{
void insertPoly(SVPoly * poly) {insertPoly(&mSVRoot, poly);}
bool testPoint(Point3F & pnt) {return(testPoint(mSVRoot, pnt));}
bool testPoly(SVPoly * poly) {return(testPoly(mSVRoot, poly));}
F32 getClippedSurfaceArea(SVPoly * poly) {return(getClippedSurfaceArea(mSVRoot, poly));}
/// @}
/// @name Helpers
/// @{
void buildPolyVolume(SVPoly *, LightInfo *);
SVPoly * copyPoly(SVPoly *);
/// @}
};
#endif