mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-07-12 15:14:35 +00:00
Engine directory for ticket #1
This commit is contained in:
parent
352279af7a
commit
7dbfe6994d
3795 changed files with 1363358 additions and 0 deletions
352
Engine/source/lighting/common/blobShadow.cpp
Normal file
352
Engine/source/lighting/common/blobShadow.cpp
Normal 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;
|
||||
}
|
||||
87
Engine/source/lighting/common/blobShadow.h
Normal file
87
Engine/source/lighting/common/blobShadow.h
Normal 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
|
||||
61
Engine/source/lighting/common/lightMapParams.cpp
Normal file
61
Engine/source/lighting/common/lightMapParams.cpp
Normal 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;
|
||||
}
|
||||
54
Engine/source/lighting/common/lightMapParams.h
Normal file
54
Engine/source/lighting/common/lightMapParams.h
Normal 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
|
||||
582
Engine/source/lighting/common/projectedShadow.cpp
Normal file
582
Engine/source/lighting/common/projectedShadow.cpp
Normal 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;
|
||||
}
|
||||
127
Engine/source/lighting/common/projectedShadow.h
Normal file
127
Engine/source/lighting/common/projectedShadow.h
Normal 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_
|
||||
1110
Engine/source/lighting/common/sceneLighting.cpp
Normal file
1110
Engine/source/lighting/common/sceneLighting.cpp
Normal file
File diff suppressed because it is too large
Load diff
254
Engine/source/lighting/common/sceneLighting.h
Normal file
254
Engine/source/lighting/common/sceneLighting.h
Normal 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_
|
||||
66
Engine/source/lighting/common/sceneLightingGlobals.h
Normal file
66
Engine/source/lighting/common/sceneLightingGlobals.h
Normal 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;
|
||||
|
||||
144
Engine/source/lighting/common/scenePersist.cpp
Normal file
144
Engine/source/lighting/common/scenePersist.cpp
Normal 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;
|
||||
}
|
||||
68
Engine/source/lighting/common/scenePersist.h
Normal file
68
Engine/source/lighting/common/scenePersist.h
Normal 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_
|
||||
40
Engine/source/lighting/common/shadowBase.h
Normal file
40
Engine/source/lighting/common/shadowBase.h
Normal 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
|
||||
731
Engine/source/lighting/common/shadowVolumeBSP.cpp
Normal file
731
Engine/source/lighting/common/shadowVolumeBSP.cpp
Normal 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]], ¤tStore, 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;
|
||||
}
|
||||
154
Engine/source/lighting/common/shadowVolumeBSP.h
Normal file
154
Engine/source/lighting/common/shadowVolumeBSP.h
Normal 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
|
||||
Loading…
Add table
Add a link
Reference in a new issue