added everything

This commit is contained in:
Metario
2017-04-17 06:17:10 -06:00
commit 9c6ff74f19
6121 changed files with 1625704 additions and 0 deletions

View File

@ -0,0 +1,445 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "sceneGraph/detailManager.h"
#include "ts/tsShapeInstance.h"
#include "ts/tsPartInstance.h"
#include "dgl/dgl.h"
// bias towards using same detail level as previous frame
#define MatchPreviousReward 0.99f
#define NotMatchPreviousPenalty 1.01f
// this is the pref value that the user should be able to set
F32 DetailManager::smDetailScale = 1.0f;
// this should be fixed at some large upper-bound
S32 DetailManager::smMaxPolyLimit = 20000;
// this should be fixed at some small lower-bound
S32 DetailManager::smMinPolyLimit = 2000;
// this determines how much we can under-shoot poly limit when reducing
S32 DetailManager::smLimitRange = 1000;
// default detail profile -- delay detailing out 2 times...
DetailManager::DetailProfile DetailManager::smDefaultProfile = { 0, 0, 2 };
DetailManager * DetailManager::smDetailManager = NULL;
S32 DetailManager::smPolysDidRender = 0;
S32 DetailManager::smPolysTriedToRender = 0;
S32 QSORT_CALLBACK FN_CDECL compareDetailData( const void * e1, const void * e2 )
{
const DetailManager::DetailData * dd1 = *(const DetailManager::DetailData**)e1;
const DetailManager::DetailData * dd2 = *(const DetailManager::DetailData**)e2;
if (dd1->priority < dd2->priority)
return -1;
else if (dd2->priority < dd1->priority)
return 1;
else
return 0;
}
//---------------------------------------------------------
DetailManager::DetailManager()
{
mInPrepRender = false;
}
DetailManager::~DetailManager()
{
U32 i;
for (i=0; i<mDetailData.size(); i++)
delete mDetailData[i];
mDetailData.clear();
for (i=0; i<mFreeDetailData.size(); i++)
delete mFreeDetailData[i];
mFreeDetailData.clear();
}
//---------------------------------------------------------
inline void DetailManager::bumpOne(DetailData * dd, S32 bump)
{
dd->dl = dd->dls[bump];
dd->intraDL = 1.0f;
mPolyCount -= dd->bump[bump];
for (S32 i=0; i<MAX_BUMP; i++)
dd->bump[i] -= dd->bump[bump];
}
void DetailManager::bumpAll(S32 bump)
{
for (U32 i=0; i<mDetailData.size(); i++)
bumpOne(mDetailData[i],bump);
}
//---------------------------------------------------------
void DetailManager::begin()
{
// enter prepRender stage
mInPrepRender = true;
// inc tag...
mTag++;
// clear poly count
mPolyCount = 0;
// set poly limit for this frame
mPolyLimit = (S32)(smMinPolyLimit + smDetailScale * (smMaxPolyLimit-smMinPolyLimit));
// clear bump count
for (U32 i=0; i<MAX_BUMP; i++)
mBumpPolyCount[i] = 0;
}
void DetailManager::end()
{
S32 i;
// leave prepRender stage
mInPrepRender = false;
// update poly count for new frame
smPolysDidRender = mPolyCount;
smPolysTriedToRender = mPolyCount;
// drop detailData for shapes rendered last time but not this time
for (i=mDetailData.size()-1; i>=0;i--)
if (mDetailData[i]->tag!=mTag)
{
// not rendering this time
mDetailData[i]->shapeInstance = NULL; // enough to disconnect it from shape instance...
mDetailData[i]->partInstance = NULL; // enough to disconnect it from part instance...
mFreeDetailData.push_back(mDetailData[i]);
mDetailData.erase(i);
}
// we may already be below the poly limit
if (mPolyCount<getMax(1,mPolyLimit))
// we accept the preferred details
return;
/*
// do as many full bumps as we can
S32 bump;
for (bump=0; bump<MAX_BUMP; bump++)
if (mPolyCount-mBumpPolyCount[bump]<mPolyLimit)
break;
if (bump)
// bump everyone this many times...
bumpAll(bump-1);
// update poly count for new frame
smPolysDidRender = mPolyCount;
if (bump==MAX_BUMP)
// can do no more...
return;
// compute priority function...sort
for (i=0; i<mDetailData.size(); i++)
computePriority(mDetailData[i],bump);
dQsort(mDetailData.address(),mDetailData.size(),sizeof(DetailData*),compareDetailData);
// keep reducing until done...
for (i=0; i<mDetailData.size(); i++)
{
if (mPolyCount-mDetailData[i]->bump[bump] > mPolyLimit-smLimitRange)
{
bumpOne(mDetailData[i],bump);
if (mPolyCount<mPolyLimit)
// we're done...
break;
}
}
*/
// update poly count for new frame
smPolysDidRender = mPolyCount;
}
//---------------------------------------------------------
void DetailManager::selectPotential(TSShapeInstance * si, F32 dist, F32 invScale, const DetailProfile * dp)
{
AssertFatal(mInPrepRender,"DetailManager::selectPotentialDetails");
dist *= invScale;
S32 dl = si->selectCurrentDetail2(dist);
if (dl<0)
// don't render
return;
DetailData * dd;
if (!si->mData || ((DetailData*)si->mData)->shapeInstance!=si)
{
// we weren't rendered last time
// get new detail data and set prevDL to -2 to encode that we're fresh meat...
si->mData = getNewDetailData();
dd = (DetailData*)si->mData;
dd->prevDL = -2;
}
else
{
// we were rendered last time
dd = (DetailData*)si->mData;
dd->prevDL = dd->dl;
}
dd->shapeInstance = si;
dd->partInstance = NULL;
dd->tag = mTag;
dd->dl = dl;
dd->pixelSize = -dist; //pixelRadius; BROKEN, but this'll be fine for now
dd->intraDL = si->getCurrentIntraDetail();
// add in poly count for preferred detail level
S32 polyCount = si->getShape()->details[dl].polyCount;
mPolyCount += polyCount;
S32 countFirst = 0;
S32 countMiddle = 0;
S32 countLast = 0;
for (S32 i=0; i<MAX_BUMP; i++)
{
// duplicate first detail...or...
bool dup = false;
if (dl==0 && countFirst<dp->skipFirst)
{
countFirst++;
dup = true;
}
// duplicate last detail...or...
if (dl==si->getShape()->mSmallestVisibleDL && countLast<dp->skipLast)
{
countLast++;
dup = true;
}
// duplicate other details...
if (countMiddle<dp->skipMiddle)
{
countMiddle++;
dup = true;
}
else
countMiddle = 0;
// find the next detail...
if (!dup)
{
if (dl==si->getShape()->mSmallestVisibleDL)
dl = -1;
else
dl++;
}
dd->dls[i] = dl;
S32 detailPolys = dl>=0 ? si->getShape()->details[dl].polyCount : 0;
dd->bump[i] = polyCount - detailPolys;
mBumpPolyCount[i] += dd->bump[i];
if (dl<0)
{
for (S32 j=i+1; j<MAX_BUMP; j++)
{
dd->dls[j] = -1;
dd->bump[j] = polyCount;
mBumpPolyCount[j] += dd->bump[j];
}
break;
}
}
}
//---------------------------------------------------------
void DetailManager::selectPotential(TSPartInstance * pi, F32 dist, F32 invScale, const DetailProfile * dp)
{
AssertFatal(mInPrepRender,"DetailManager::selectPotentialDetails");
dist *= invScale;
pi->selectCurrentDetail2(dist);
S32 dl = pi->getCurrentObjectDetail();
if (dl<0)
// don't render
return;
DetailData * dd;
if (!pi->mData || ((DetailData*)pi->mData)->partInstance!=pi)
{
// we weren't rendered last time
// get new detail data and set prevDL to -2 to encode that we're fresh meat...
pi->mData = getNewDetailData();
dd = (DetailData*)pi->mData;
dd->prevDL = -2;
}
else
{
// we were rendered last time
dd = (DetailData*)pi->mData;
dd->prevDL = dd->dl;
}
dd->shapeInstance = NULL;
dd->partInstance = pi;
dd->tag = mTag;
dd->dl = dl;
dd->pixelSize = -dist;
dd->intraDL = pi->getCurrentIntraDetail();
// add in poly count for preferred detail level
S32 polyCount = pi->getPolyCount(dl);
mPolyCount += polyCount;
S32 countFirst = 0;
S32 countMiddle = 0;
S32 countLast = 0;
for (S32 i=0; i<MAX_BUMP; i++)
{
// duplicate first detail...or...
bool dup = false;
if (dl==0 && countFirst<dp->skipFirst)
{
countFirst++;
dup = true;
}
// duplicate last detail...or...
if (dl==pi->getNumDetails()-1 && countLast<dp->skipLast)
{
countLast++;
dup = true;
}
// duplicate other details...
if (countMiddle<dp->skipMiddle)
{
countMiddle++;
dup = true;
}
else
countMiddle = 0;
// find the next detail...
if (!dup)
{
if (dl==pi->getNumDetails()-1)
dl = -1;
else
dl++;
}
dd->dls[i] = dl;
S32 detailPolys = pi->getPolyCount(dl);
dd->bump[i] = polyCount - detailPolys;
mBumpPolyCount[i] += dd->bump[i];
if (dl<0)
{
for (S32 j=i+1; j<MAX_BUMP; j++)
{
dd->dls[j] = -1;
dd->bump[j] = polyCount;
mBumpPolyCount[j] += dd->bump[j];
}
break;
}
}
}
//---------------------------------------------------------
bool DetailManager::selectCurrent(TSShapeInstance * si)
{
AssertFatal(!mInPrepRender,"DetailManager::selectCurrentDetail");
DetailData * dd = (DetailData*)si->mData;
if ( !dd || dd->shapeInstance != si)
// not rendering...
return false;
si->setCurrentDetail(dd->dl,dd->intraDL);
return true;
}
//---------------------------------------------------------
bool DetailManager::selectCurrent(TSPartInstance * pi)
{
AssertFatal(!mInPrepRender,"DetailManager::selectCurrentDetail");
DetailData * dd = (DetailData*)pi->mData;
if ( !dd || dd->partInstance != pi)
// not rendering...
return false;
pi->setCurrentObjectDetail(dd->dl);
pi->setCurrentIntraDetail(dd->intraDL);
return true;
}
//---------------------------------------------------------
void DetailManager::computePriority(DetailData * detailData, S32 bump)
{
S32 oldSize, newSize;
if (detailData->shapeInstance)
{
// shape instance...
if (bump>0)
oldSize = (S32)(detailData->dl>=0 ? detailData->shapeInstance->getShape()->details[detailData->dl].size : 0);
else
oldSize = (S32)detailData->pixelSize;
newSize = (S32)(detailData->dls[bump]>=0 ? detailData->shapeInstance->getShape()->details[detailData->dls[bump]].size : 0);
}
else
{
// part instance...
AssertFatal(detailData->partInstance,"DetailManager::computePriority");
if (bump>0)
oldSize = (S32)detailData->partInstance->getDetailSize(detailData->dl);
else
oldSize = (S32)detailData->pixelSize;
newSize = (S32)detailData->partInstance->getDetailSize(detailData->dls[bump]);
}
// priority weighted by both total bump and recent bump (total bump component helps break ties
// when recent bump has the same effect).
detailData->priority = 0.5f * (detailData->pixelSize - oldSize) + (oldSize - newSize);
// try to be consistent between frames...as much as possible
if (detailData->prevDL<0)
{
if (detailData->prevDL==-1)
{
// weren't visible last time...penalize for being visible this time, reward for being invisible
if (detailData->dls[bump]==-1)
detailData->priority *= MatchPreviousReward;
else if (detailData->dl!=-1)
detailData->priority *= NotMatchPreviousPenalty;
}
}
else
{
// reward for bumping us down if we are at a higher detail than last time,
// penalize for bumping us down if our detail is at or lower than last time
// Note: higher detail --> smaller dl number
if (detailData->dl > detailData->prevDL)
detailData->priority *= MatchPreviousReward;
else
detailData->priority *= NotMatchPreviousPenalty;
}
}
//---------------------------------------------------------
DetailManager::DetailData * DetailManager::getNewDetailData()
{
DetailData * ret;
if (mFreeDetailData.size())
{
ret = mFreeDetailData.last();
mFreeDetailData.decrement();
}
else
ret = new DetailData;
mDetailData.push_back(ret);
return ret;
}

108
engine/sceneGraph/detailManager.h Executable file
View File

@ -0,0 +1,108 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _DETAILMANAGER_H_
#define _DETAILMANAGER_H_
#ifndef _PLATFORMASSERT_H_
#include "platform/platformAssert.h"
#endif
#ifndef _MMATH_H_
#include "math/mMath.h"
#endif
#ifndef _TVECTOR_H_
#include "core/tVector.h"
#endif
#define MAX_BUMP 4
class TSShapeInstance;
class TSPartInstance;
class DetailManager
{
public:
struct DetailData
{
S32 tag;
TSShapeInstance * shapeInstance; // either this
TSPartInstance * partInstance; // or this
S16 bump[MAX_BUMP];
S16 dls[MAX_BUMP];
S32 dl;
F32 intraDL;
S32 prevDL;
F32 pixelSize;
F32 priority;
};
struct DetailProfile
{
S32 skipFirst; // for how many rounds do we want to skip "bumping" first detail
S32 skipMiddle; // for how many rounds do we want to skip "bumping" every middle detail
S32 skipLast; // for how many rounds do we want to skip "bumping" last detail
};
static DetailProfile smDefaultProfile;
private:
static DetailManager * smDetailManager;
static DetailManager * get() { AssertFatal(smDetailManager,"DetailManager::get"); return smDetailManager; }
S32 mTag;
S32 mPolyLimit; // our poly budget...computed at the start of each frame based on detail scale
bool mInPrepRender;
S32 mPolyCount; // current poly count
S32 mBumpPolyCount[MAX_BUMP]; // number of polys we save at each "bump" level
Vector<DetailData*> mDetailData;
Vector<DetailData*> mFreeDetailData;
void begin();
void end();
void selectPotential(TSShapeInstance * si, F32 dist, F32 invScale, const DetailProfile * dp = &smDefaultProfile);
bool selectCurrent(TSShapeInstance * si);
void selectPotential(TSPartInstance * pi, F32 dist, F32 invScale, const DetailProfile * dp = &smDefaultProfile);
bool selectCurrent(TSPartInstance * pi);
void bumpOne(DetailData*, S32 bump);
void bumpAll(S32 bump);
void computePriority(DetailData *, S32 bump);
DetailData * getNewDetailData();
public:
DetailManager();
~DetailManager();
static F32 smDetailScale; // user can adjust this
static S32 smMaxPolyLimit; // app sets this once -- or uses default value
static S32 smMinPolyLimit; // app sets this once -- or uses default value
static S32 smLimitRange; // app sets this once -- or uses default value
static S32 smPolysTriedToRender; // not used here, but can be read for misc. uses
static S32 smPolysDidRender; // not used here, but can be read for misc. uses
static void init() { AssertFatal(!smDetailManager,"DetailManger::init"); smDetailManager = new DetailManager; }
static void shutdown() { delete smDetailManager; smDetailManager = NULL; }
static void beginPrepRender() { get()->begin(); }
static void endPrepRender() { get()->end(); }
static void selectPotentialDetails(TSShapeInstance * si, F32 dist, F32 invScale) { get()->selectPotential(si,dist,invScale); }
static bool selectCurrentDetail(TSShapeInstance * si) { return get()->selectCurrent(si); }
static void selectPotentialDetails(TSPartInstance * pi, F32 dist, F32 invScale) { get()->selectPotential(pi,dist,invScale); }
static bool selectCurrentDetail(TSPartInstance * pi) { return get()->selectCurrent(pi); }
};
#endif

View File

@ -0,0 +1,6 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#pragma message( "Deprecated: lightManager.cc is deprecated and will be removed in the future. Replaced by lightingSystem/sgLightingManager.cc" )

View File

@ -0,0 +1,11 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _LIGHTMANAGER_H_
#define _LIGHTMANAGER_H_
#pragma message( "Deprecated: lightManager.h is deprecated and will be removed in the future. Replaced by lightingSystem/sgLightManager.h" )
#endif

1124
engine/sceneGraph/sceneGraph.cc Executable file

File diff suppressed because it is too large Load Diff

346
engine/sceneGraph/sceneGraph.h Executable file
View File

@ -0,0 +1,346 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _SCENEGRAPH_H_
#define _SCENEGRAPH_H_
//Includes
#ifndef _PLATFORM_H_
#include "platform/platform.h"
#endif
#ifndef _POLYLIST_H_
#include "core/polyList.h"
#endif
#ifndef _MRECT_H_
#include "math/mRect.h"
#endif
#ifndef _COLOR_H_
#include "core/color.h"
#endif
#ifndef _SCENEOBJECT_H_
#include "sim/sceneObject.h"
#endif
#ifndef _GTEXMANAGER_H_
#include "dgl/gTexManager.h"
#endif
#ifndef _GBITMAP_H_
#include "dgl/gBitmap.h"
#endif
#include "lightingSystem/sgLightManager.h"
class SceneState;
class NetConnection;
class Sky;
class TerrainBlock;
class DecalManager;
struct FogVolume
{
float visibleDistance;
float minHeight;
float maxHeight;
float percentage;
ColorF color;
};
enum FogConstants
{
MaxFogVolumes = 3,
FogTextureDistSize = 64,
FogTextureHeightSize = 64
};
//--------------------------------------------------------------------------
//-------------------------------------- SceneGraph
//
class SceneGraph
{
public:
SceneGraph(bool isClient);
~SceneGraph();
public:
/// @name SceneObject Management
/// @{
bool addObjectToScene(SceneObject*);
void removeObjectFromScene(SceneObject*);
void zoneInsert(SceneObject*);
void zoneRemove(SceneObject*);
/// @}
//
public:
/// @name Zone management
/// @{
void registerZones(SceneObject*, U32 numZones);
void unregisterZones(SceneObject*);
SceneObject* getZoneOwner(const U32 zone);
/// @}
public:
/// @name Rendering and Scope Management
/// @{
void renderScene(const U32 objectMask = 0xffffffff);
void scopeScene(const Point3F& scopePosition,
const F32 scopeDistance,
NetConnection* netConnection);
/// @}
public:
/// @name Camera
/// For objects, valid only during the rendering cycle
/// @{
const Point3F& getBaseCameraPosition() const;
const Point3F& getCurrCameraPosition() const;
/// @}
/// @name Fog/Visibility Management
/// @{
ColorF getFogColor() const;
F32 getFogDistance() const;
F32 getVisibleDistance() const;
F32 getFogDistanceMod() const;
F32 getVisibleDistanceMod() const;
void setFogDistance(F32 dist);
void setVisibleDistance(F32 dist);
void setFogColor(ColorF fogColor);
void setFogVolumes(U32 numFogVolumes, FogVolume *fogVolumes);
void getFogVolumes(U32 &numFogVolumes, FogVolume * &fogVolumes);
void buildFogTexture( SceneState *pState );
void buildFogTextureSpecial( SceneState *pState );
void getFogCoordData(F32 &invVisibleDistance, F32 &heightOffset, F32 &invHeightRange) const;
void getFogCoordPair(F32 dist, F32 z, F32 &x, F32 &y) const;
F32 getFogCoord(F32 dist, F32 z) const;
/// @}
U32 getStateKey() const { return(smStateKey); }
private:
void setBaseCameraPosition(const Point3F&);
void setCurrCameraPosition(const Point3F&);
//-------------------------------------- Private interface and data
protected:
TextureHandle mFogTexture;
TextureHandle mFogTextureIntensity;
static const U32 csmMaxTraversalDepth;
static U32 smStateKey;
public:
static F32 smVisibleDistanceMod;
public:
static bool useSpecial;
protected:
bool mIsClient;
bool mHazeArrayDirty;
static F32 mHazeArray[FogTextureDistSize];
static U32 mHazeArrayi[FogTextureDistSize];
static F32 mDistArray[FogTextureDistSize];
F32 mInvVisibleDistance;
F32 mHeightRangeBase;
F32 mHeightOffset;
F32 mInvHeightRange;
U32 mCurrZoneEnd;
U32 mNumActiveZones;
Point3F mBaseCameraPosition;
Point3F mCurrCameraPosition;
U32 mNumFogVolumes;
FogVolume mFogVolumes[MaxFogVolumes];
F32 mFogDistance;
F32 mVisibleDistance;
ColorF mFogColor;
LightManager mLightManager;
Sky* mCurrSky;
TerrainBlock* mCurrTerrain;
DecalManager* mCurrDecalManager;
Vector<SceneObject*> mWaterList;
void addRefPoolBlock();
SceneObjectRef* allocateObjectRef();
void freeObjectRef(SceneObjectRef*);
SceneObjectRef* mFreeRefPool;
Vector<SceneObjectRef*> mRefPoolBlocks;
static const U32 csmRefPoolBlockSize;
public:
LightManager * getLightManager();
Sky* getCurrentSky() { return mCurrSky; }
TerrainBlock* getCurrentTerrain() { return mCurrTerrain; }
DecalManager* getCurrentDecalManager() { return mCurrDecalManager; }
void getWaterObjectList(SimpleQueryList&);
TextureHandle getFogTexture() { return mFogTexture; }
TextureHandle getFogTextureIntensity() { return mFogTextureIntensity; }
// Object database for zone managers
protected:
struct ZoneManager {
SceneObject* obj;
U32 zoneRangeStart;
U32 numZones;
};
Vector<ZoneManager> mZoneManagers;
/// Zone Lists
///
/// @note The object refs in this are somewhat singular in that the object pointer does not
/// point to a referenced object, but the owner of that zone...
Vector<SceneObjectRef*> mZoneLists;
protected:
void buildSceneTree(SceneState*, SceneObject*, const U32, const U32, const U32);
void traverseSceneTree(SceneState* pState);
void treeTraverseVisit(SceneObject*, SceneState*, const U32);
void compactZonesCheck();
bool alreadyManagingZones(SceneObject*) const;
public:
void findZone(const Point3F&, SceneObject*&, U32&);
protected:
void rezoneObject(SceneObject*);
void addToWaterList(SceneObject* obj);
void removeFromWaterList(SceneObject* obj);
};
extern SceneGraph* gClientSceneGraph;
extern SceneGraph* gServerSceneGraph;
inline LightManager * SceneGraph::getLightManager()
{
return(&mLightManager);
}
inline const Point3F& SceneGraph::getBaseCameraPosition() const
{
return mBaseCameraPosition;
}
inline const Point3F& SceneGraph::getCurrCameraPosition() const
{
return mCurrCameraPosition;
}
inline void SceneGraph::setBaseCameraPosition(const Point3F& p)
{
mBaseCameraPosition = p;
}
inline void SceneGraph::getFogCoordData(F32 &invVisibleDistance, F32 &heightOffset, F32 &invHeightRange) const
{
invVisibleDistance = mInvVisibleDistance;
heightOffset = mHeightOffset;
invHeightRange = mInvHeightRange;
}
inline void SceneGraph::getFogCoordPair(F32 dist, F32 z, F32 &x, F32 &y) const
{
x = (getVisibleDistanceMod() - dist) * mInvVisibleDistance;
y = (z - mHeightOffset) * mInvHeightRange;
}
inline F32 SceneGraph::getFogCoord(F32 dist, F32 z) const
{
const GBitmap* pBitmap = mFogTexture.getBitmap();
AssertFatal(pBitmap->getFormat() == GBitmap::RGBA, "Error, wrong format for this query");
AssertFatal(pBitmap->getWidth() == 64 && pBitmap->getHeight() == 64, "Error, fog texture wrong dimensions");
S32 x = S32(((getVisibleDistanceMod() - dist) * mInvVisibleDistance) * 63.0f);
S32 y = S32(((z - mHeightOffset) * mInvHeightRange) * 63.0f);
U32 samplex = mClamp(x, 0, 63);
U32 sampley = mClamp(y, 0, 63);
return F32(pBitmap->pBits[(((sampley * 64) + samplex) * 4) + 3]) / 255.0f;
}
inline ColorF SceneGraph::getFogColor() const
{
return(mFogColor);
}
inline F32 SceneGraph::getFogDistance() const
{
return(mFogDistance);
}
inline F32 SceneGraph::getFogDistanceMod() const
{
return(mFogDistance * smVisibleDistanceMod);
}
inline F32 SceneGraph::getVisibleDistance() const
{
return(mVisibleDistance);
}
inline F32 SceneGraph::getVisibleDistanceMod() const
{
return(mVisibleDistance * smVisibleDistanceMod);
}
inline void SceneGraph::setCurrCameraPosition(const Point3F& p)
{
mCurrCameraPosition = p;
}
inline void SceneGraph::getFogVolumes(U32 &numFogVolumes, FogVolume * &fogVolumes)
{
numFogVolumes = mNumFogVolumes;
fogVolumes = mFogVolumes;
}
//--------------------------------------------------------------------------
inline SceneObjectRef* SceneGraph::allocateObjectRef()
{
if (mFreeRefPool == NULL) {
addRefPoolBlock();
}
AssertFatal(mFreeRefPool!=NULL, "Error, should always have a free reference here!");
SceneObjectRef* ret = mFreeRefPool;
mFreeRefPool = mFreeRefPool->nextInObj;
ret->nextInObj = NULL;
return ret;
}
inline void SceneGraph::freeObjectRef(SceneObjectRef* trash)
{
trash->nextInBin = NULL;
trash->prevInBin = NULL;
trash->nextInObj = mFreeRefPool;
mFreeRefPool = trash;
}
inline SceneObject* SceneGraph::getZoneOwner(const U32 zone)
{
AssertFatal(zone < mCurrZoneEnd, "Error, out of bounds zone selected!");
return mZoneLists[zone]->object;
}
#endif //_SCENEGRAPH_H_

3014
engine/sceneGraph/sceneLighting.cc Executable file

File diff suppressed because it is too large Load Diff

278
engine/sceneGraph/sceneLighting.h Executable file
View File

@ -0,0 +1,278 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _SceneLighting_H_
#define _SceneLighting_H_
#ifndef _SCENEOBJECT_H_
#include "sim/sceneObject.h"
#endif
#ifndef _TERRDATA_H_
#include "terrain/terrData.h"
#endif
#ifndef _INTERIORINSTANCE_H_
#include "interior/interiorInstance.h"
#endif
#ifndef _SHADOWVOLUMEBSP_H_
#include "sceneGraph/shadowVolumeBSP.h"
#endif
#include "lightingSystem/sgLightManager.h"
#include "lightingSystem/sgSceneLighting.h"
/*
#define TERRAIN_OVERRANGE 2.0f
//------------------------------------------------------------------------------
class SceneLighting : public SimObject
{
typedef SimObject Parent;
public:
struct PersistInfo
{
struct PersistChunk
{
enum {
MissionChunkType = 0,
InteriorChunkType,
TerrainChunkType
};
U32 mChunkType;
U32 mChunkCRC;
virtual ~PersistChunk() {}
virtual bool read(Stream &);
virtual bool write(Stream &);
};
struct MissionChunk : public PersistChunk
{
typedef PersistChunk Parent;
MissionChunk();
};
struct InteriorChunk : public PersistChunk
{
typedef PersistChunk Parent;
InteriorChunk();
~InteriorChunk();
Vector<U32> mDetailLightmapCount;
Vector<U32> mDetailLightmapIndices;
Vector<GBitmap*> mLightmaps;
bool mHasAlarmState;
Vector<U32> mDetailVertexCount;
Vector<ColorI> mVertexColorsNormal;
Vector<ColorI> mVertexColorsAlarm;
bool read(Stream &);
bool write(Stream &);
};
struct TerrainChunk : public PersistChunk
{
typedef PersistChunk Parent;
TerrainChunk();
~TerrainChunk();
U16 * mLightmap;
bool read(Stream &);
bool write(Stream &);
};
~PersistInfo();
Vector<PersistChunk*> mChunks;
static U32 smFileVersion;
bool read(Stream &);
bool write(Stream &);
};
U32 calcMissionCRC();
bool verifyMissionInfo(PersistInfo::PersistChunk *);
bool getMissionInfo(PersistInfo::PersistChunk *);
bool loadPersistInfo(const char *);
bool savePersistInfo(const char *);
class ObjectProxy;
class TerrainProxy;
class InteriorProxy;
enum {
SHADOW_DETAIL = -1
};
void addInterior(ShadowVolumeBSP *, InteriorProxy &, LightInfo *, S32);
ShadowVolumeBSP::SVPoly * buildInteriorPoly(ShadowVolumeBSP *, InteriorProxy &, Interior *, U32, LightInfo *, bool);
//------------------------------------------------------------------------------
/// 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 preLight(LightInfo *) {return(false);}
virtual void light(LightInfo *) {}
virtual void postLight(bool lastLight) {}
/// @}
/// @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 *);
/// @}
};
class InteriorProxy : public ObjectProxy
{
private:
typedef ObjectProxy Parent;
bool isShadowedBy(InteriorProxy *);
public:
InteriorProxy(SceneObject * obj);
~InteriorProxy();
InteriorInstance * operator->() {return(static_cast<InteriorInstance*>(static_cast<SceneObject*>(mObj)));}
InteriorInstance * getObject() {return(static_cast<InteriorInstance*>(static_cast<SceneObject*>(mObj)));}
// current light info
ShadowVolumeBSP * mBoxShadowBSP;
Vector<ShadowVolumeBSP::SVPoly*> mLitBoxSurfaces;
Vector<PlaneF> mOppositeBoxPlanes;
Vector<PlaneF> mTerrainTestPlanes;
// lighting interface
bool loadResources();
void init();
bool preLight(LightInfo *);
void light(LightInfo *);
void postLight(bool lastLight);
// persist
U32 getResourceCRC();
bool setPersistInfo(PersistInfo::PersistChunk *);
bool getPersistInfo(PersistInfo::PersistChunk *);
};
class TerrainProxy : public ObjectProxy
{
private:
typedef ObjectProxy Parent;
BitVector mShadowMask;
ShadowVolumeBSP * mShadowVolume;
ColorF * mLightmap;
void lightVector(LightInfo *);
struct SquareStackNode
{
U8 mLevel;
U16 mClipFlags;
Point2I mPos;
};
S32 testSquare(const Point3F &, const Point3F &, S32, F32, const Vector<PlaneF> &);
bool markInteriorShadow(InteriorProxy *);
public:
TerrainProxy(SceneObject * obj);
~TerrainProxy();
TerrainBlock * operator->() {return(static_cast<TerrainBlock*>(static_cast<SceneObject*>(mObj)));}
TerrainBlock * getObject() {return(static_cast<TerrainBlock*>(static_cast<SceneObject*>(mObj)));}
bool getShadowedSquares(const Vector<PlaneF> &, Vector<U16> &);
// lighting
void init();
bool preLight(LightInfo *);
void light(LightInfo *);
// persist
U32 getResourceCRC();
bool setPersistInfo(PersistInfo::PersistChunk *);
bool getPersistInfo(PersistInfo::PersistChunk *);
};
typedef Vector<ObjectProxy*> ObjectProxyList;
ObjectProxyList mSceneObjects;
ObjectProxyList mLitObjects;
LightInfoList mLights;
SceneLighting();
~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.
};
static bool lightScene(const char *, BitSet32 flags = 0);
static bool isLighting();
S32 mStartTime;
char mFileName[1024];
static bool smUseVertexLighting;
bool light(BitSet32);
void completed(bool success);
void processEvent(U32 light, S32 object);
void processCache();
// inlined
bool isTerrain(SceneObject *);
bool isInterior(SceneObject *);
};
//------------------------------------------------------------------------------
inline bool SceneLighting::isTerrain(SceneObject * obj)
{
return obj && ((obj->getTypeMask() & TerrainObjectType) != 0);
}
inline bool SceneLighting::isInterior(SceneObject * obj)
{
return obj && ((obj->getTypeMask() & InteriorObjectType) != 0);
}
*/
#endif

84
engine/sceneGraph/sceneRoot.cc Executable file
View File

@ -0,0 +1,84 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "sceneGraph/sceneRoot.h"
#include "sceneGraph/sceneGraph.h"
#include "sceneGraph/sceneState.h"
SceneRoot* gClientSceneRoot = NULL;
SceneRoot* gServerSceneRoot = NULL;
SceneRoot::SceneRoot()
{
mObjBox.min.set(-1e10, -1e10, -1e10);
mObjBox.max.set( 1e10, 1e10, 1e10);
resetWorldBox();
}
SceneRoot::~SceneRoot()
{
}
bool SceneRoot::onSceneAdd(SceneGraph* pGraph)
{
// _Cannot_ call the parent here. Must handle this ourselves so we can keep out of
// the zone graph...
// if (Parent::onSceneAdd(pGraph) == false)
// return false;
mSceneManager = pGraph;
mSceneManager->registerZones(this, 1);
AssertFatal(mZoneRangeStart == 0, "error, sceneroot must be first scene object zone manager!");
return true;
}
void SceneRoot::onSceneRemove()
{
AssertFatal(mZoneRangeStart == 0, "error, sceneroot must be first scene object zone manager!");
mSceneManager->unregisterZones(this);
mZoneRangeStart = 0xFFFFFFFF;
mSceneManager = NULL;
// _Cannot_ call the parent here. Must handle this ourselves so we can keep out of
// the zone graph...
// Parent::onSceneRemove();
}
bool SceneRoot::getOverlappingZones(SceneObject*, U32* zones, U32* numZones)
{
// If we are here, we always return the global zone.
zones[0] = 0;
*numZones = 1;
return false;
}
bool SceneRoot::prepRenderImage(SceneState* state, const U32 stateKey,
const U32,
const bool modifyBaseZoneState)
{
AssertFatal(modifyBaseZoneState == true, "error, should never be called unless in the upward traversal!");
AssertFatal(isLastState(state, stateKey) == false, "Error, should have been colored black in order to prevent double calls!");
setLastState(state, stateKey);
// We don't return a render image, or any portals, but we do setup the zone 0
// rendering parameters. We simply copy them from what is in the states baseZoneState
// structure, and mark the zone as rendered.
dMemcpy(state->getZoneStateNC(0).frustum, state->getBaseZoneState().frustum, sizeof(state->getZoneStateNC(0).frustum));
state->getZoneStateNC(0).viewport = state->getBaseZoneState().viewport;
state->getZoneStateNC(0).render = true;
return false;
}
bool SceneRoot::scopeObject(const Point3F& /*rootPosition*/,
const F32 /*rootDistance*/,
bool* zoneScopeState)
{
zoneScopeState[0] = true;
return false;
}

43
engine/sceneGraph/sceneRoot.h Executable file
View File

@ -0,0 +1,43 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _SCENEROOT_H_
#define _SCENEROOT_H_
//Includes
#ifndef _PLATFORM_H_
#include "platform/platform.h"
#endif
#ifndef _SCENEOBJECT_H_
#include "sim/sceneObject.h"
#endif
/// Root of scene graph.
class SceneRoot : public SceneObject
{
typedef SceneObject Parent;
protected:
bool onSceneAdd(SceneGraph *graph);
void onSceneRemove();
bool getOverlappingZones(SceneObject *obj, U32 *zones, U32 *numZones);
bool prepRenderImage(SceneState *state, const U32 stateKey, const U32 startZone,
const bool modifyBaseZoneState);
bool scopeObject(const Point3F& rootPosition,
const F32 rootDistance,
bool* zoneScopeState);
public:
SceneRoot();
~SceneRoot();
};
extern SceneRoot* gClientSceneRoot; ///< Client's scene graph root.
extern SceneRoot* gServerSceneRoot; ///< Server's scene graph root.
#endif //_SCENEROOT_H_

996
engine/sceneGraph/sceneState.cc Executable file
View File

@ -0,0 +1,996 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "sceneGraph/sceneState.h"
#include "sceneGraph/sgUtil.h"
#include "sim/sceneObject.h"
#include "dgl/dgl.h"
#include "sceneGraph/sceneGraph.h"
#include "terrain/sky.h"
#include "platform/profiler.h"
namespace {
S32 FN_CDECL
cmpImageFunc(const void* p1, const void* p2)
{
const SceneRenderImage* psri1 = *((const SceneRenderImage**)p1);
const SceneRenderImage* psri2 = *((const SceneRenderImage**)p2);
// Compares only non-transcluent images
AssertFatal(psri1->isTranslucent == false && psri2->isTranslucent == false,
"Error, only non-translucent images allowed here.");
if (psri1->sortType != psri2->sortType)
{
// Normal render images are setup in such a way that increasing order
// renders
return S32(psri1->sortType) - S32(psri2->sortType);
}
else
{
// Otherwise, sort on primary texture, as set by the sort key
return S32(psri1->textureSortKey) - S32(psri2->textureSortKey);
}
}
S32 FN_CDECL
cmpTPImageFunc(const void* p1, const void* p2)
{
const SceneRenderImage* psri1 = *((const SceneRenderImage**)p1);
const SceneRenderImage* psri2 = *((const SceneRenderImage**)p2);
// Compares only non-transcluent images
AssertFatal(psri1->isTranslucent == true && psri2->isTranslucent == true,
"Error, only non-translucent images allowed here.");
return S32(psri1->textureSortKey) - S32(psri2->textureSortKey);
}
S32 FN_CDECL
cmpPlaneImageFunc(const void* p1, const void* p2)
{
const SceneRenderImage** psri1 = (const SceneRenderImage**)p1;
const SceneRenderImage** psri2 = (const SceneRenderImage**)p2;
// Normal render images are setup in such a way that increasing order
// renders
if (((*psri2)->polyArea - (*psri1)->polyArea) < 0.0)
return -1;
else if (((*psri2)->polyArea - (*psri1)->polyArea) == 0.0)
return 0;
else
return 1;
}
S32 FN_CDECL
cmpPointImageFunc(const void* p1, const void* p2)
{
const SceneRenderImage* psri1 = *((const SceneRenderImage**)p1);
const SceneRenderImage* psri2 = *((const SceneRenderImage**)p2);
if (psri1->pointDistSq != psri2->pointDistSq)
{
if (psri1->pointDistSq > psri2->pointDistSq)
{
return -1;
}
else
{
return 1;
}
}
else
{
if (psri1->tieBreaker == true)
{
return -1;
}
else
{
return 1;
}
}
}
inline void renderImage(SceneState* state, SceneRenderImage* image)
{
PROFILE_START(SceneStateRenderImage);
#if defined(TORQUE_DEBUG)
S32 m, p, t0, t1, v[4];
F32 t0m[16], t1m[16];
dglGetTransformState(&m, &p, &t0, t0m, &t1, t1m, v);
#endif
image->obj->renderObject(state, image);
#if defined(TORQUE_DEBUG)
if (dglCheckState(m, p, t0, t0m, t1, t1m, v) == false) {
S32 bm, bp, bt0, bt1, bv[4];
F32 bt0m[16], bt1m[16];
dglGetTransformState(&bm, &bp, &bt0, bt0m, &bt1, bt1m, bv);
AssertFatal(false,
avar("Error, object of class %s either unbalanced the xform stacks, or didn't reset the viewport!"
" mv(%d %d) proj(%d %d) t0(%d %d), t1(%d %d) (%d %d %d %d: %d %d %d %d)",
image->obj->getClassName(),
m, bm, p, bp, t0, bt0, t1, bt1, v[0], v[1], v[2], v[3], bv[0], bv[1], bv[2], bv[3]));
}
#endif
PROFILE_END();
}
} // namespace {}
// MM/JF: Added for mirrorSubObject fix.
void SceneState::setupClipPlanes(ZoneState& rState)
{
F32 farOverNear = getFarPlane() / getNearPlane();
Point3F farPosLeftUp = Point3F(rState.frustum[0] * farOverNear, getFarPlane(), rState.frustum[3] * farOverNear);
Point3F farPosLeftDown = Point3F(rState.frustum[0] * farOverNear, getFarPlane(), rState.frustum[2] * farOverNear);
Point3F farPosRightUp = Point3F(rState.frustum[1] * farOverNear, getFarPlane(), rState.frustum[3] * farOverNear);
Point3F farPosRightDown = Point3F(rState.frustum[1] * farOverNear, getFarPlane(), rState.frustum[2] * farOverNear);
MatrixF temp = mModelview;
temp.inverse();
temp.mulP(farPosLeftUp);
temp.mulP(farPosLeftDown);
temp.mulP(farPosRightUp);
temp.mulP(farPosRightDown);
sgOrientClipPlanes(&rState.clipPlanes[0], getCameraPosition(), farPosLeftUp, farPosLeftDown, farPosRightUp, farPosRightDown);
rState.clipPlanesValid = true;
}
//--------------------------------------------------------------------------
//--------------------------------------
SceneState::SceneState(SceneState* parent,
const U32 numZones,
F64 left,
F64 right,
F64 bottom,
F64 top,
F64 nearPlane,
F64 farPlane,
RectI viewport,
const Point3F& camPos,
const MatrixF& modelview,
F32 fogDistance,
F32 visibleDistance,
ColorF fogColor,
U32 numFogVolumes,
FogVolume* fogVolumes,
TextureHandle envMap,
F32 visFactor)
{
mVisFactor = visFactor;
mParent = parent;
mFlipCull = false;
mBaseZoneState.render = false;
mBaseZoneState.clipPlanesValid = false;
mBaseZoneState.frustum[0] = left;
mBaseZoneState.frustum[1] = right;
mBaseZoneState.frustum[2] = bottom;
mBaseZoneState.frustum[3] = top;
mBaseZoneState.viewport = viewport;
#if defined(TORQUE_DEBUG)
// Avoid FPU exceptions in ZoneState constructors
dMemset(mBaseZoneState.clipPlanes, 0, (sizeof mBaseZoneState.clipPlanes));
#endif
mNearPlane = nearPlane;
mFarPlane = farPlane;
mModelview = modelview;
mCamPosition = camPos;
mFogDistance = fogDistance;
mVisibleDistance = visibleDistance;
mFogColor = fogColor;
mZoneStates.setSize(numZones);
for (U32 i = 0; i < numZones; i++)
{
mZoneStates[i].render = false;
mZoneStates[i].clipPlanesValid = false;
}
mPortalOwner = NULL;
mPortalIndex = 0xFFFFFFFF;
mNumFogVolumes = numFogVolumes;
mFogVolumes = fogVolumes;
setupFog();
mTerrainOverride = false;
mEnvironmentMap = envMap;
mRenderImages.reserve(128);
mTranslucentPlaneImages.reserve(128);
mTranslucentPointImages.reserve(128);
mTranslucentBeginImages.reserve(32);
mTranslucentEndImages.reserve(32);
mTranslucentBSP.reserve(64);
mTranslucentBSP.setSize(1);
mTranslucentBSP[0].riList = NULL;
mTranslucentBSP[0].frontIndex = 0xFFFF;
mTranslucentBSP[0].backIndex = 0xFFFF;
mTranslucentBSP[0].rimage = NULL;
}
SceneState::~SceneState()
{
U32 i;
for (i = 0; i < mSubsidiaries.size(); i++)
delete mSubsidiaries[i];
for (i = 0; i < mRenderImages.size(); i++)
delete mRenderImages[i];
for (i = 0; i < mTranslucentPlaneImages.size(); i++)
delete mTranslucentPlaneImages[i];
for (i = 0; i < mTranslucentPointImages.size(); i++)
delete mTranslucentPointImages[i];
for (i = 0; i < mTranslucentEndImages.size(); i++)
delete mTranslucentEndImages[i];
for (i = 0; i < mTranslucentBeginImages.size(); i++)
delete mTranslucentBeginImages[i];
}
void SceneState::setPortal(SceneObject* owner, const U32 index)
{
mPortalOwner = owner;
mPortalIndex = index;
}
void SceneState::insertRenderImage(SceneRenderImage* ri)
{
if (ri->isTranslucent == false)
mRenderImages.push_back(ri);
else
{
if (ri->sortType == SceneRenderImage::Plane)
{
mTranslucentPlaneImages.push_back(ri);
}
else if (ri->sortType == SceneRenderImage::Point)
{
mTranslucentPointImages.push_back(ri);
}
else if (ri->sortType == SceneRenderImage::BeginSort)
{
mTranslucentBeginImages.push_back(ri);
}
else
{
AssertFatal(ri->sortType == SceneRenderImage::EndSort, "Error, bad transcluent sortType");
mTranslucentEndImages.push_back(ri);
}
}
}
void SceneState::insertTransformPortal(SceneObject* owner, U32 portalIndex,
U32 globalZone, const Point3F& traversalStartPoint,
const bool flipCull)
{
mTransformPortals.increment();
mTransformPortals.last().owner = owner;
mTransformPortals.last().portalIndex = portalIndex;
mTransformPortals.last().globalZone = globalZone;
mTransformPortals.last().traverseStart = traversalStartPoint;
mTransformPortals.last().flipCull = flipCull;
}
void SceneState::sortRenderImages()
{
dQsort(mRenderImages.address(), mRenderImages.size(), sizeof(SceneRenderImage*), cmpImageFunc);
dQsort(mTranslucentPointImages.address(), mTranslucentPointImages.size(), sizeof(SceneRenderImage*), cmpTPImageFunc);
dQsort(mTranslucentPlaneImages.address(), mTranslucentPlaneImages.size(), sizeof(SceneRenderImage*), cmpPlaneImageFunc);
}
void SceneState::insertIntoNode(RenderBSPNode& rNode, SceneRenderImage* pImage, bool rendered)
{
if (rNode.frontIndex == 0xFFFF)
{
// Split the node
rNode.plane = pImage->plane;
rNode.frontIndex = mTranslucentBSP.size() + 0;
rNode.backIndex = mTranslucentBSP.size() + 1;
if (rendered)
rNode.rimage = pImage;
mTranslucentBSP.increment(2);
mTranslucentBSP[rNode.frontIndex].riList = NULL;
mTranslucentBSP[rNode.frontIndex].frontIndex = 0xFFFF;
mTranslucentBSP[rNode.frontIndex].backIndex = 0xFFFF;
mTranslucentBSP[rNode.frontIndex].rimage = NULL;
mTranslucentBSP[rNode.backIndex].riList = NULL;
mTranslucentBSP[rNode.backIndex].frontIndex = 0xFFFF;
mTranslucentBSP[rNode.backIndex].backIndex = 0xFFFF;
mTranslucentBSP[rNode.backIndex].rimage = NULL;
return;
}
// Determine which side we're on...
U32 mask = 0;
F32 dist = 0.0f;
for (U32 i = 0; i < 4; i++)
{
F32 d = rNode.plane.distToPlane(pImage->poly[i]);
if (d >= 0.0f)
{
mask |= (1 << i);
}
dist += d;
}
if (mask == 0xF)
{
// Front only
insertIntoNode(mTranslucentBSP[rNode.frontIndex], pImage);
}
else if (mask == 0)
{
// Back only
insertIntoNode(mTranslucentBSP[rNode.backIndex], pImage);
}
else
{
// Both
if (dist >= 0.0f)
{
// Render front
insertIntoNode(mTranslucentBSP[rNode.frontIndex], pImage, true);
insertIntoNode(mTranslucentBSP[rNode.backIndex], pImage, false);
}
else
{
// Render back
insertIntoNode(mTranslucentBSP[rNode.frontIndex], pImage, false);
insertIntoNode(mTranslucentBSP[rNode.backIndex], pImage, true);
}
}
}
void SceneState::buildTranslucentBSP()
{
U32 i;
for (i = 0; i < mTranslucentPlaneImages.size(); i++)
{
SceneRenderImage* pImage = mTranslucentPlaneImages[i];
AssertFatal(pImage->sortType == SceneRenderImage::Plane, "Error, bad sort type on plane list!");
insertIntoNode(mTranslucentBSP[0], pImage);
}
for (i = 0; i < mTranslucentPointImages.size(); i++)
{
SceneRenderImage* pImage = mTranslucentPointImages[i];
AssertFatal(pImage->sortType == SceneRenderImage::Point, "Error, bad sort type on point list!");
RenderBSPNode* pNode = &mTranslucentBSP[0];
while (true)
{
if (pNode->frontIndex != 0xFFFF)
{
if (pNode->plane.distToPlane(pImage->poly[0]) >= 0)
pNode = &mTranslucentBSP[pNode->frontIndex];
else
pNode = &mTranslucentBSP[pNode->backIndex];
}
else
{
pImage->pNext = pNode->riList;
pNode->riList = pImage;
break;
}
}
}
}
void SceneState::renderNode(RenderBSPNode& rNode)
{
if (rNode.frontIndex != 0xFFFF)
{
if (rNode.plane.distToPlane(mCamPosition) >= 0)
{
renderNode(mTranslucentBSP[rNode.backIndex]);
if (rNode.rimage != NULL)
renderImage(this, rNode.rimage);
renderNode(mTranslucentBSP[rNode.frontIndex]);
}
else
{
renderNode(mTranslucentBSP[rNode.frontIndex]);
if (rNode.rimage != NULL)
renderImage(this, rNode.rimage);
renderNode(mTranslucentBSP[rNode.backIndex]);
}
}
else
{
Vector<SceneRenderImage*> imageList(128);
SceneRenderImage* pImage = rNode.riList;
while (pImage != NULL)
{
pImage->pointDistSq = (mCamPosition - pImage->poly[0]).lenSquared();
imageList.push_back(pImage);
pImage = pImage->pNext;
}
dQsort(imageList.address(), imageList.size(), sizeof(SceneRenderImage*), cmpPointImageFunc);
for (U32 i = 0; i < imageList.size(); i++)
{
renderImage(this, imageList[i]);
}
}
}
void SceneState::renderCurrentImages()
{
sortRenderImages();
buildTranslucentBSP();
if (mPortalOwner != NULL)
{
// If we're a portalized object, we need to setup a user clip plane...
PlaneF clipPlane;
mPortalOwner->getWSPortalPlane(mPortalIndex, &clipPlane);
if (mFlipCull)
clipPlane.neg();
GLdouble planeEQ[4];
planeEQ[0] = clipPlane.x;
planeEQ[1] = clipPlane.y;
planeEQ[2] = clipPlane.z;
planeEQ[3] = clipPlane.d;
glClipPlane(GL_CLIP_PLANE0, planeEQ);
glEnable(GL_CLIP_PLANE0);
}
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
dglLoadMatrix(&mModelview);
U32 i;
for (i = 0; i < mRenderImages.size(); i++)
renderImage(this, mRenderImages[i]);
for (i = 0; i < mTranslucentBeginImages.size(); i++)
renderImage(this, mTranslucentBeginImages[i]);
renderNode(mTranslucentBSP[0]);
for (i = 0; i < mTranslucentEndImages.size(); i++)
renderImage(this, mTranslucentEndImages[i]);
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
if (mPortalOwner != NULL)
glDisable(GL_CLIP_PLANE0);
}
void SceneState::setupZoneProjection(const U32 zone)
{
const ZoneState& rState = getZoneState(zone);
AssertFatal(rState.render == true, "Error, should never set up a non-rendering zone!");
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
dglSetFrustum(rState.frustum[0], rState.frustum[1],
rState.frustum[2], rState.frustum[3],
getNearPlane(), getFarPlane(), dglIsOrtho());
glMatrixMode(GL_MODELVIEW);
dglSetViewport(rState.viewport);
}
void SceneState::setupObjectProjection(const SceneObject* obj)
{
RectI viewport;
F64 frustum[4] = { 1e10, -1e10, 1e10, -1e10 };
bool init = false;
SceneObjectRef* pWalk = obj->mZoneRefHead;
AssertFatal(pWalk != NULL, "Error, object must exist in at least one zone to call this!");
while (pWalk)
{
const ZoneState& rState = getZoneState(pWalk->zone);
if (rState.render == true)
{
// frustum
if (rState.frustum[0] < frustum[0]) frustum[0] = rState.frustum[0];
if (rState.frustum[1] > frustum[1]) frustum[1] = rState.frustum[1];
if (rState.frustum[2] < frustum[2]) frustum[2] = rState.frustum[2];
if (rState.frustum[3] > frustum[3]) frustum[3] = rState.frustum[3];
// viewport
if (init == false)
viewport = rState.viewport;
else
viewport.unionRects(rState.viewport);
init = true;
}
pWalk = pWalk->nextInObj;
}
//AssertFatal(init, "Error, at least one zone must be rendered here!");
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
dglSetFrustum(frustum[0], frustum[1],
frustum[2], frustum[3],
getNearPlane(), getFarPlane(), dglIsOrtho());
glMatrixMode(GL_MODELVIEW);
dglSetViewport(viewport);
}
void SceneState::setupBaseProjection()
{
const ZoneState& rState = getBaseZoneState();
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
dglSetFrustum(rState.frustum[0], rState.frustum[1],
rState.frustum[2], rState.frustum[3],
getNearPlane(), getFarPlane(), dglIsOrtho());
glMatrixMode(GL_MODELVIEW);
dglSetViewport(rState.viewport);
}
bool SceneState::isObjectRendered(const SceneObject* obj)
{
// Don't bother if it's globally bounded.
const SceneObjectRef* pWalk = obj->mZoneRefHead;
static F32 darkToOGLCoord[16] = { 1, 0, 0, 0,
0, 0, -1, 0,
0, 1, 0, 0,
0, 0, 0, 1 };
static MatrixF darkToOGLMatrix;
static bool matrixInitialized = false;
if (matrixInitialized == false)
{
F32* m = darkToOGLMatrix;
for (U32 i = 0; i < 16; i++)
m[i] = darkToOGLCoord[i];
darkToOGLMatrix.transpose();
matrixInitialized = true;
}
while (pWalk != NULL)
{
if (getZoneState(pWalk->zone).render == true)
{
ZoneState& rState = getZoneStateNC(pWalk->zone);
if (rState.clipPlanesValid == false)
{
setupClipPlanes(rState);
}
if(obj->isGlobalBounds())
return true;
const Box3F& rObjBox = obj->getObjBox();
const Point3F& rScale = obj->getScale();
Point3F center;
rObjBox.getCenter(&center);
center.convolve(rScale);
Point3F xRad((rObjBox.max.x - rObjBox.min.x) * 0.5 * rScale.x, 0, 0);
Point3F yRad(0, (rObjBox.max.y - rObjBox.min.y) * 0.5 * rScale.y, 0);
Point3F zRad(0, 0, (rObjBox.max.z - rObjBox.min.z) * 0.5 * rScale.z);
obj->getRenderTransform().mulP(center);
obj->getRenderTransform().mulV(xRad);
obj->getRenderTransform().mulV(yRad);
obj->getRenderTransform().mulV(zRad);
bool render = true;
for (U32 i = 0; i < 5; i++)
{
if (rState.clipPlanes[i].whichSideBox(center, xRad, yRad, zRad, Point3F(0, 0, 0)) == PlaneF::Back)
{
render = false;
break;
}
}
if (render)
return true;
}
pWalk = pWalk->nextInObj;
}
return false;
}
//--------------------------------------------------------------------------
//--------------------------------------
bool checkFogBandBoxVisible(F32 dist, F32 haze, F32 low, F32 high, Vector<SceneState::FogBand> &fb)
{
// if there are no fog bands, no fog - it's visible
if(!fb.size())
return true;
// if the first fog band is unfogged and the box
// is inside the band, it's visible
if(fb[0].isFog == false && low < fb[0].cap)
return true;
// check the case of the camera in a fog band
if(fb[0].isFog)
{
// if the low point is in the fog, we check that
if(low < fb[0].cap)
{
if(haze + dist * fb[0].factor < 1)
return true;
// if low and high are both in the fog band
// and low isn't visible, neither is high
if(high < fb[0].cap)
return false;
// check the high point...
F32 highDist = mSqrt(high * high + dist * dist - low * low);
return haze + (fb[0].cap / high) * highDist * fb[0].factor < 1;
}
// ok, both low and high are above the cap of the plane
// so we have to check only the high point (bigger triangle means less fog
// applied (higher top means steeper slope on the hypotenuse))
F32 highDist = mSqrt(high * high + dist * dist - low * low);
return haze + (fb[0].cap / high) * highDist * fb[0].factor < 1;
}
// ok, fb[0] is not fogged, meaning there is an empty layer
// followed by a fog plane, followed by the box.
// we only test the one fog volume for visibility of the box...
F32 fogStart = fb[0].cap;
F32 fogEnd = fogStart + fb[1].cap;
// if the low is in the fog band, we have to check
// low, followed by possibly high
// if low is above the fog band we only have to check high point
if(low > fogEnd)
{
// only check the high point through the fog
F32 highDist = mSqrt(high * high + dist * dist - low * low);
return haze + (fb[1].cap / high) * highDist * fb[1].factor < 1;
}
// last case, low is in the fog band
// check low vis:
if(haze + fb[1].factor * dist * (low - fogStart) / low < 1)
return true;
// if the high point is in the same fog band, it's not visible
if(high < fogEnd)
return false;
// ok, check the high point
F32 highDist = mSqrt(high * high + dist * dist - low * low);
return haze + (fb[1].cap / high) * highDist * fb[1].factor < 1;
}
bool SceneState::isBoxFogVisible(F32 dist, F32 top, F32 bottom)
{
F32 camZ = mCamPosition.z;
float haze = 0;
if(dist > mFogDistance)
{
float distFactor = (dist - mFogDistance) * mFogScale - 1.0;
haze = 1.0 - distFactor * distFactor;
}
F32 distSq = dist * dist;
// the object is below:
if(top < camZ)
{
return checkFogBandBoxVisible(dist, haze, camZ - top, camZ - bottom, mNegFogBands);
}
else if(bottom > camZ)
{
return checkFogBandBoxVisible(dist, haze, bottom - camZ, top - camZ, mPosFogBands);
}
else
{
// spans the fog...
if(!mNegFogBands.size() || !mPosFogBands.size() || !mPosFogBands[0].isFog)
return true;
// ok, we know there is at least one fog band and the camera is in it.
// check if the object is visible through the fog...
if(haze + dist * mPosFogBands[0].factor < 1)
return true;
// ok, check the top stretch...
// we know now that since the box spans the horizontal,
// that dist is a horizontal (deltaZ = 0)
// so we want the segment of the hypotenuse that goes through
// the fog.
F32 ht = top - camZ;
// don't do it if the top is in the fog
if(ht > mPosFogBands[0].cap)
{
if(haze + (mPosFogBands[0].cap / ht) * mSqrt(dist * dist + ht * ht) * mPosFogBands[0].factor < 1)
return true;
}
// ok, last chance, check the bottom segment
ht = camZ - bottom;
if(ht < mNegFogBands[0].cap)
return false;
return haze + (mNegFogBands[0].cap / ht) * mSqrt(dist * dist + ht * ht) * mNegFogBands[0].factor < 1;
}
}
void SceneState::setupFog()
{
if( mVisibleDistance == mFogDistance )
{
// FIXME: arbitrary large constant
mFogScale = 1000.0f;
}
else
{
mFogScale = 1.0 / (mVisibleDistance - mFogDistance);
}
// construct positive fog volumes
mPosFogBands.clear();
F32 camZ = mCamPosition.z;
S32 i;
for(i = 0; i < mNumFogVolumes; i++)
{
if(camZ < mFogVolumes[i].maxHeight)
break;
}
if(i < mNumFogVolumes)
{
float prevHeight = camZ;
for(;i < mNumFogVolumes; i++)
{
if(prevHeight < mFogVolumes[i].minHeight)
{
FogBand fb;
fb.isFog = false;
fb.color.set(mFogVolumes[i].color.red,
mFogVolumes[i].color.green,
mFogVolumes[i].color.blue,
mFogVolumes[i].color.alpha);
fb.cap = mFogVolumes[i].minHeight - prevHeight;
prevHeight = mFogVolumes[i].minHeight;
mPosFogBands.push_back(fb);
}
FogBand fb;
fb.isFog = true;
fb.cap = mFogVolumes[i].maxHeight - prevHeight;
fb.color.set(mFogVolumes[i].color.red,
mFogVolumes[i].color.green,
mFogVolumes[i].color.blue,
mFogVolumes[i].color.alpha);
fb.factor = (1 / (mFogVolumes[i].visibleDistance * mVisFactor)) * mFogVolumes[i].percentage;
prevHeight = mFogVolumes[i].maxHeight;
mPosFogBands.push_back(fb);
}
}
// construct negative fog volumes
mNegFogBands.clear();
for(i = mNumFogVolumes - 1; i >= 0; i--)
{
if(camZ > mFogVolumes[i].minHeight)
break;
}
if(i >= 0)
{
float prevHeight = camZ;
for(;i >= 0; i--)
{
if(prevHeight > mFogVolumes[i].maxHeight)
{
FogBand fb;
fb.isFog = false;
fb.cap = prevHeight - mFogVolumes[i].maxHeight;
prevHeight = mFogVolumes[i].maxHeight;
fb.color.set(mFogVolumes[i].color.red,
mFogVolumes[i].color.green,
mFogVolumes[i].color.blue,
mFogVolumes[i].color.alpha);
mNegFogBands.push_back(fb);
}
FogBand fb;
fb.isFog = true;
fb.cap = prevHeight - mFogVolumes[i].minHeight;
fb.factor = (1 / (mFogVolumes[i].visibleDistance * mVisFactor)) * mFogVolumes[i].percentage;
prevHeight = mFogVolumes[i].minHeight;
fb.color.set(mFogVolumes[i].color.red,
mFogVolumes[i].color.green,
mFogVolumes[i].color.blue,
mFogVolumes[i].color.alpha);
mNegFogBands.push_back(fb);
}
}
}
void SceneState::getFogs(float dist, float deltaZ, ColorF *array, U32 &numFogs)
{
numFogs = 0;
Vector<FogBand> *band;
if(deltaZ < 0)
{
deltaZ = -deltaZ;
band = &mNegFogBands;
}
else
band = &mPosFogBands;
float ht = deltaZ;
for(int i = 0; i < band->size(); i++)
{
FogBand &bnd = (*band)[i];
if(ht < bnd.cap)
{
if(bnd.isFog)
array[numFogs++] = ColorF(bnd.color.red, bnd.color.green, bnd.color.blue, dist * bnd.factor);
break;
}
float subDist = dist * bnd.cap / ht;
if(bnd.isFog)
{
array[numFogs++] = ColorF(bnd.color.red,
bnd.color.green,
bnd.color.blue,
subDist * bnd.factor);
}
dist -= subDist;
ht -= bnd.cap;
}
}
F32 SceneState::getFog(float dist, float deltaZ, S32 volKey)
{
float haze = 0;
Vector<FogBand> *band;
if(deltaZ < 0)
{
deltaZ = -deltaZ;
band = &mNegFogBands;
}
else
band = &mPosFogBands;
if(band->size() < 1)
return haze;
float ht = deltaZ;
FogBand &bnd = (*band)[volKey];
if(ht < bnd.cap)
{
if(bnd.isFog)
haze += dist * bnd.factor;
}
else
{
float subDist = dist * bnd.cap / ht;
if(bnd.isFog)
haze += subDist * bnd.factor;
}
return haze;
}
F32 SceneState::getFog(float dist, float deltaZ)
{
float haze = 0;
Vector<FogBand> *band;
if(deltaZ < 0)
{
deltaZ = -deltaZ;
band = &mNegFogBands;
}
else
band = &mPosFogBands;
float ht = deltaZ;
for(int i = 0; i < band->size(); i++)
{
FogBand &bnd = (*band)[i];
if(ht < bnd.cap)
{
if(bnd.isFog)
haze += dist * bnd.factor;
break;
}
float subDist = dist * bnd.cap / ht;
if(bnd.isFog)
haze += subDist * bnd.factor;
dist -= subDist;
ht -= bnd.cap;
}
return haze;
}
F32 SceneState::getHazeAndFog(float dist, float deltaZ) const
{
float haze = 0;
if(dist > mFogDistance) {
if (dist > mVisibleDistance)
return 1.0;
float distFactor = (dist - mFogDistance) * mFogScale - 1.0;
haze = 1.0 - distFactor * distFactor;
}
const Vector<FogBand> *band;
if(deltaZ < 0)
{
deltaZ = -deltaZ;
band = &mNegFogBands;
}
else
band = &mPosFogBands;
float ht = deltaZ;
for(int i = 0; i < band->size(); i++)
{
const FogBand &bnd = (*band)[i];
if(ht < bnd.cap)
{
if(bnd.isFog)
haze += dist * bnd.factor;
break;
}
float subDist = dist * bnd.cap / ht;
if(bnd.isFog)
haze += subDist * bnd.factor;
dist -= subDist;
ht -= bnd.cap;
}
if(haze > 1)
return 1;
return haze;
}
void SceneState::setImageRefPoint(SceneObject* obj, SceneRenderImage* image) const
{
const Box3F& rBox = obj->getObjBox();
Point3F objSpaceCamPosition = mCamPosition;
obj->getRenderWorldTransform().mulP(objSpaceCamPosition);
objSpaceCamPosition.convolveInverse(obj->getScale());
image->poly[0] = rBox.getClosestPoint(objSpaceCamPosition);
image->poly[0].convolve(obj->getScale());
obj->getRenderTransform().mulP(image->poly[0]);
}
//--------------------------------------------------------------------------
SceneRenderImage::~SceneRenderImage()
{
}

477
engine/sceneGraph/sceneState.h Executable file
View File

@ -0,0 +1,477 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _SCENESTATE_H_
#define _SCENESTATE_H_
#ifndef _PLATFORM_H_
#include "platform/platform.h"
#endif
#ifndef _PLATFORMASSERT_H_
#include "platform/platformAssert.h"
#endif
#ifndef _TVECTOR_H_
#include "core/tVector.h"
#endif
#ifndef _MRECT_H_
#include "math/mRect.h"
#endif
#ifndef _MMATRIX_H_
#include "math/mMatrix.h"
#endif
#ifndef _COLOR_H_
#include "core/color.h"
#endif
#ifndef _GTEXMANAGER_H_
#include "dgl/gTexManager.h"
#endif
class SceneObject;
/// A SceneRenderImage is used by the SceneState/SceneGraph to sort objects for rendering
/// order.
///
/// The scene graph calls prepRenderImage on every SceneObject it intends to render
/// every frame. The SceneObject will then insert one or more SceneRenderImages. These images
/// describe what will be rendered by the object, whether it will have transparency,
/// what type of object it is, etc.
/// These images are sorted by SortType, and then rendered. If any images are
/// translucent, the SceneState builds a BSP tree and then evaluates it back to front.
class SceneRenderImage
{
public:
/// Sort type indicates when the image should be rendered and how
/// it should be sorted.
///
/// The rendering order is this:
/// - Sky
/// - BeginSort
/// - Terrain
/// - Normal
/// - Point
/// - Plane
/// - EndSort
///
/// Point and Plane are only valid if the object is translucent.
/// The variables 'plane' and 'poly' are used for these sort types.
/// If the point type is used, the actual point is specified in poly[0]
enum SortType
{
Sky = 0,
Terrain = 1,
Normal = 2,
Point,
Plane,
EndSort,
BeginSort
};
SceneRenderImage()
: sortType(Normal),
isTranslucent(false),
tieBreaker(false),
useSmallTextures(false),
textureSortKey(0)
{
//
}
virtual ~SceneRenderImage();
SceneObject* obj; ///< The SceneObject this image represents.
bool isTranslucent; ///< Is this image translucent?
bool tieBreaker; ///< If two objects have the same points, this is used to determine sorting order.
bool useSmallTextures; ///< If this is set to true, the object will render using a low-res version of its textures.
SortType sortType;
PlaneF plane; ///< The plane if SortType::Plane is used.
Point3F poly[4]; ///< If SortType::Plane is used, this is the quad that defines the bounds for the
/// plane. If SortType::Point is used, then poly[0] is the point, and the rest is ignored.
F32 polyArea; ///< Area of the polygon defined by poly[].
F32 pointDistSq; ///< Distance from camera to poly[0] squared.
///
/// @note This is set inside SceneState.
U32 textureSortKey; ///< This is used to sort objects of the same SortType into order if the objects have
/// no translucency.
/// Linked list implementation.
///
/// @note NEVER set this. This is managed by Torque.
SceneRenderImage* pNext;
};
//--------------------------------------------------------------------------
//-------------------------------------- SceneState
//
struct FogVolume;
/// The SceneState describes the state of the scene being rendered. It keeps track
/// of the information that objects need to render properly with regard to the
/// camera position, any fog information, viewing frustum, the global environment
/// map for reflections, viewable distance and portal information.
class SceneState
{
friend class SceneGraph;
public:
struct FogBand
{
bool isFog;
float cap;
float factor;
ColorF color;
};
struct ZoneState {
bool render;
F64 frustum[4]; ///< Winding order is: left, right, bottom, top.
RectI viewport;
bool clipPlanesValid;
PlaneF clipPlanes[5];
};
/// Sets up the clipping planes using the parameters from a ZoneState.
///
/// @param zone ZoneState to initalize clipping to
void setupClipPlanes(ZoneState &zone);
/// Used to represent a portal which inserts a transformation into the scene.
struct TransformPortal {
SceneObject* owner;
U32 portalIndex;
U32 globalZone;
Point3F traverseStart;
bool flipCull;
};
public:
/// Constructor
///
/// @see SceneGraph::renderScene
SceneState(SceneState* parent,
const U32 numZones,
F64 left,
F64 right,
F64 bottom,
F64 top,
F64 nearPlane,
F64 farPlane,
RectI viewport,
const Point3F& camPos,
const MatrixF& modelview,
F32 fogDistance,
F32 visibleDistance,
ColorF fogColor,
U32 numFogVolumes,
FogVolume *fogVolumes,
TextureHandle environmentMap,
F32 visFactor);
~SceneState();
/// Sets the active portal.
///
/// @param owner Object which owns the portal (portalized object)
/// @param idx Index of the portal in the list of portal planes
void setPortal(SceneObject *owner, const U32 idx);
/// Assigns the reference point of the SceneRenderImage. For use with
/// translucent images.
///
/// @param obj SceneObject that belongs to the SceneRenderImage being manipulated
/// @param image SceneRenderImage being manipulated
void setImageRefPoint(SceneObject *obj, SceneRenderImage *image) const;
/// Returns the camera position this SceneState is using.
const Point3F& getCameraPosition() const { return mCamPosition; }
/// Returns the minimum distance something must be from the camera to not be culled.
F64 getNearPlane() const { return mNearPlane; }
/// Returns the maximum distance something can be from the camera to not be culled.
F64 getFarPlane() const { return mFarPlane; }
void setFarPlane(F64 farPlane) { mFarPlane = farPlane; }
/// Returns the base ZoneState.
///
/// @see ZoneState
const ZoneState& getBaseZoneState() const;
/// Returns the base ZoneState as a non-const reference.
///
/// @see getBaseZoneState
ZoneState& getBaseZoneStateNC();
/// Returns the ZoneState for a particular zone ID.
///
/// @param zoneId ZoneId
const ZoneState& getZoneState(const U32 zoneId) const;
/// Returns the ZoneState for a particular zone ID as a non-const reference.
///
/// @see getZoneState
/// @param zoneId ZoneId
ZoneState& getZoneStateNC(const U32 zoneId);
/// Adds an image to be rendered by this SceneState into the proper category.
///
/// @param image Image to add
void insertRenderImage(SceneRenderImage *image);
/// Adds a new transform portal to the SceneState.
///
/// @param owner SceneObject owner of the portal (portalized object).
/// @param portalIndex Index of the portal in the list of portal planes.
/// @param globalZone Index of the zone this portal is in in the list of ZoneStates.
/// @param traversalStartPoint Start point of the zone traversal.
/// @see SceneGraph::buildSceneTree
/// @see SceneGraph::findZone
///
/// @param flipCull If true, the portal plane will be flipped
void insertTransformPortal(SceneObject* owner, U32 portalIndex,
U32 globalZone, const Point3F& traversalStartPoint,
const bool flipCull);
/// This enables terrain to be drawn inside interiors.
void enableTerrainOverride();
/// Returns true if terrain is allowed to be drawn inside interiors.
bool isTerrainOverridden() const;
/// Sorts the list of images, builds the translucency BSP tree,
/// sets up the portal, then renders all images in the state.
void renderCurrentImages();
/// Returns true if the object in question is going to be rendered
/// as opposed to being culled out.
///
/// @param obj Object in question.
bool isObjectRendered(const SceneObject *obj);
/// Sets the viewport and projection up for the given zone.
///
/// @param zone Zone for which we want to set up a viewport/projection.
void setupZoneProjection(const U32 zone);
/// Sets up the projection matrix based on the zone the specified object is in.
///
/// @param object Object to use.
void setupObjectProjection(const SceneObject *object);
/// Sets up the projection matrix based on the base ZoneState.
void setupBaseProjection();
/// Returns the scale of the distance based fog [used for haze]
F32 getFogScale() const { return mFogScale; }
/// Returns the distance at which objects are no longer visible.
F32 getVisibleDistance() const;
/// Returns how far away fog is (ie, when objects will be fully fogged).
F32 getFogDistance() const;
/// Returns the color of the fog in the scene.
ColorF getFogColor() const;
/// Returns a texture handle to the environment map in use.
TextureHandle getEnvironmentMap() { return mEnvironmentMap; }
/// Sets up all the fog volumes in the Scene.
void setupFog();
/// Returns true if the box specified is visible, taking fog into account.
///
/// @param dist The length of the vector, projected on to the horizontal plane of the world, from the camera to the nearest point on the box
/// @param top The maximum z-value of the box
/// @param bottom The minimum z-value of the box
bool isBoxFogVisible(F32 dist, F32 top, F32 bottom);
/// Returns a value between 0.0 and 1.0 which indicates how obscured a point is by haze and fog.
///
/// @param dist Length of vector, projected onto horizontal world plane, from camera to the point.
/// @param deltaZ Z-offset of the camera.
F32 getHazeAndFog(float dist, float deltaZ) const;
/// Returns a value between 0.0 and 1.0 which indicates how obscured a point is by fog only.
///
/// @param dist Length of vector, projected onto horizontal world plane, from camera to the point.
/// @param deltaZ Z-offset of the camera.
F32 getFog(float dist, float deltaZ);
/// Returns a value between 0.0 and 1.0 which indicates how obscured a point is by fog only.
///
/// @param dist Length of vector, projected onto horizontal world plane, from camera to the point.
/// @param deltaZ Z-offset of the camera.
/// @param volKey Index of a particular band of volumetric fog.
F32 getFog(float dist, float deltaZ, S32 volKey);
/// Returns all fogs at a point.
///
/// @param dist Length of vector, projected onto horizontal world plane, from camera to the point.
/// @param deltaZ Z-offset of the camera.
/// @param array Array of fog colors at the point.
/// @param numFogs Number of fogs in the array.
void getFogs(float dist, float deltaZ, ColorF *array, U32 &numFogs);
/// Returns a value between 0.0 and 1.0 which indicates how obscured a point is by haze only.
///
/// @param dist Length of vector, projected onto horizontal world plane, from camera to the point.
F32 getHaze(F32 dist) const;
private:
/// Used to construct the BSP tree for sorting translucent images.
struct RenderBSPNode
{
PlaneF plane;
SceneRenderImage* riList;
U16 frontIndex;
U16 backIndex;
SceneRenderImage* rimage;
};
Vector<RenderBSPNode> mTranslucentBSP; ///< The BSP tree for translucent objects.
Vector<ZoneState> mZoneStates; ///< Collection of ZoneStates in the scene.
Vector<SceneRenderImage*> mRenderImages; ///< Collection of images to render.
Vector<SceneRenderImage*> mTranslucentPlaneImages; ///< Collection of SortType::Plane images.
Vector<SceneRenderImage*> mTranslucentPointImages; ///< Collection of SortType::Point images.
Vector<SceneRenderImage*> mTranslucentBeginImages; ///< Collection of SortType::BeginImage images.
Vector<SceneRenderImage*> mTranslucentEndImages; ///< Collection of SortType::EndImage images.
/// Sorts the render images.
void sortRenderImages();
/// Builds the BSP tree of translucent images.
void buildTranslucentBSP();
/// Inserts a translucent image into the translucent BSP tree.
///
/// @param rNode Root node.
/// @param image Image to insert.
/// @param rendered Used in recursion, don't mess with this!
void insertIntoNode(RenderBSPNode &rNode, SceneRenderImage *image, bool rendered = true);
/// Renders the translucency BSP tree.
///
/// @param rNode Root node.
void renderNode(RenderBSPNode &rNode);
bool mTerrainOverride; ///< If true, terrain is allowed to render inside interiors
Vector<SceneState*> mSubsidiaries; ///< Transform portals which have been processed by the scene traversal process
///
/// @note Closely related. Transform portals are turned into sorted mSubsidiaries
/// by the traversal process...
Vector<TransformPortal> mTransformPortals; ///< Collection of TransformPortals
Vector<FogBand> mPosFogBands; ///< Fog bands above the world plane
Vector<FogBand> mNegFogBands; ///< Fog bands below the world plane
ZoneState mBaseZoneState; ///< ZoneState of the base zone of the scene
Point3F mCamPosition; ///< Camera position in this state
F64 mNearPlane; ///< Minimum distance an object must be from the camera to get rendered
F64 mFarPlane; ///< Maximum distance an object can be from the camera to get rendered
F32 mVisFactor; ///< Visibility factor of the scene, used to modify fog
SceneState* mParent; ///< This is used for portals, if this is not NULL, then this SceneState belongs to a portal, and not the main SceneState
SceneObject* mPortalOwner; ///< SceneObject which owns the current portal
U32 mPortalIndex; ///< Index the current portal is in the list of portals
ColorF mFogColor; ///< Distance based, not volumetric, fog color
F32 mFogDistance; ///< Distance to distance-fog
F32 mVisibleDistance; ///< Visible distance of the scene
F32 mFogScale; ///< Fog scale of the distance based fog
U32 mNumFogVolumes; ///< Number of fog volumes in the scene
FogVolume *mFogVolumes; ///< Pointer to the array of fog volumes in the scene
TextureHandle mEnvironmentMap; ///< Current environment map
public:
bool mFlipCull; ///< If true the portal clipping plane will be reversed
MatrixF mModelview; ///< Modelview matrix this scene is based off of
/// Returns the fog bands above the world plane
Vector<FogBand> *getPosFogBands() { return &mPosFogBands; }
/// Returns the fog bands above the world plane [const]
const Vector<FogBand> *getPosFogBands() const { return &mPosFogBands; }
/// Returns the fog bands below the world planes
Vector<FogBand> *getNegFogBands() { return &mNegFogBands; }
/// Returns the fog bands below the world planes [const]
const Vector<FogBand> *getNegFogBands() const { return &mNegFogBands; }
};
inline F32 SceneState::getHaze(F32 dist) const
{
if(dist <= mFogDistance)
return 0.0f;
if (dist > mVisibleDistance)
return 1.0f;
F32 distFactor = (dist - mFogDistance) * mFogScale - 1.0f;
return 1.0f - distFactor * distFactor;
}
inline F32 SceneState::getVisibleDistance() const
{
return mVisibleDistance;
}
inline F32 SceneState::getFogDistance() const
{
return mFogDistance;
}
inline ColorF SceneState::getFogColor() const
{
return mFogColor;
}
inline const SceneState::ZoneState& SceneState::getBaseZoneState() const
{
return mBaseZoneState;
}
inline SceneState::ZoneState& SceneState::getBaseZoneStateNC()
{
return mBaseZoneState;
}
inline const SceneState::ZoneState& SceneState::getZoneState(const U32 zoneId) const
{
AssertFatal(zoneId < (U32)mZoneStates.size(), "Error, out of bounds zone!");
return mZoneStates[zoneId];
}
inline SceneState::ZoneState& SceneState::getZoneStateNC(const U32 zoneId)
{
AssertFatal(zoneId < (U32)mZoneStates.size(), "Error, out of bounds zone!");
return mZoneStates[zoneId];
}
inline void SceneState::enableTerrainOverride()
{
mTerrainOverride = true;
}
inline bool SceneState::isTerrainOverridden() const
{
return mTerrainOverride;
}
#endif // _H_SCENESTATE_

View File

@ -0,0 +1,428 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "sceneGraph/sceneGraph.h"
#include "sceneGraph/sgUtil.h"
#include "sim/sceneObject.h"
#include "sceneGraph/sceneState.h"
#include "math/mMatrix.h"
#include "dgl/dgl.h"
#include "core/polyList.h"
#include "terrain/terrData.h"
namespace {
class PotentialRenderList
{
public:
Point3F farPosLeftUp;
Point3F farPosLeftDown;
Point3F farPosRightUp;
Point3F farPosRightDown;
Point3F camPos;
F32 viewDistSquared;
Box3F mBox;
Vector<SceneObject*> mList;
PlaneF viewPlanes[5];
SceneState* mState;
public:
void insertObject(SceneObject* obj);
void setupClipPlanes(SceneState*);
};
// MM/JF: Added for mirrorSubObject fix.
void PotentialRenderList::setupClipPlanes(SceneState* state)
{
mState = state;
camPos = state->getCameraPosition();
F32 farOverNear = state->getFarPlane() / state->getNearPlane();
farPosLeftUp = Point3F(state->getBaseZoneState().frustum[0] * farOverNear, state->getFarPlane(), state->getBaseZoneState().frustum[3] * farOverNear);
farPosLeftDown = Point3F(state->getBaseZoneState().frustum[0] * farOverNear, state->getFarPlane(), state->getBaseZoneState().frustum[2] * farOverNear);
farPosRightUp = Point3F(state->getBaseZoneState().frustum[1] * farOverNear, state->getFarPlane(), state->getBaseZoneState().frustum[3] * farOverNear);
farPosRightDown = Point3F(state->getBaseZoneState().frustum[1] * farOverNear, state->getFarPlane(), state->getBaseZoneState().frustum[2] * farOverNear);
MatrixF temp = state->mModelview;
temp.inverse();
temp.mulP(farPosLeftUp);
temp.mulP(farPosLeftDown);
temp.mulP(farPosRightUp);
temp.mulP(farPosRightDown);
mBox.min = camPos;
mBox.min.setMin(farPosLeftUp);
mBox.min.setMin(farPosLeftDown);
mBox.min.setMin(farPosRightUp);
mBox.min.setMin(farPosRightDown);
mBox.max = camPos;
mBox.max.setMax(farPosLeftUp);
mBox.max.setMax(farPosLeftDown);
mBox.max.setMax(farPosRightUp);
mBox.max.setMax(farPosRightDown);
sgOrientClipPlanes(&viewPlanes[0], camPos, farPosLeftUp, farPosLeftDown, farPosRightUp, farPosRightDown);
}
void PotentialRenderList::insertObject(SceneObject* obj)
{
// Check to see if we need to render always.
if(obj->isGlobalBounds())
{
mList.push_back(obj);
return;
}
const Box3F& rObjBox = obj->getObjBox();
const Point3F& rScale = obj->getScale();
Point3F center;
rObjBox.getCenter(&center);
center.convolve(rScale);
Point3F xRad((rObjBox.max.x - rObjBox.min.x) * 0.5 * rScale.x, 0, 0);
Point3F yRad(0, (rObjBox.max.y - rObjBox.min.y) * 0.5 * rScale.y, 0);
Point3F zRad(0, 0, (rObjBox.max.z - rObjBox.min.z) * 0.5 * rScale.z);
obj->getRenderTransform().mulP(center);
obj->getRenderTransform().mulV(xRad);
obj->getRenderTransform().mulV(yRad);
obj->getRenderTransform().mulV(zRad);
bool render = true;
for (U32 i = 0; i < 5; i++)
{
if (viewPlanes[i].whichSideBox(center, xRad, yRad, zRad, Point3F(0, 0, 0)) == PlaneF::Back)
{
render = false;
break;
}
}
if (render)
mList.push_back(obj);
}
void prlInsertionCallback(SceneObject* obj, void *key)
{
PotentialRenderList* prList = (PotentialRenderList*)key;
if(obj->isGlobalBounds())
{
prList->insertObject(obj);
return;
}
Point3F closestPt = obj->getWorldBox().getClosestPoint(prList->camPos);
F32 lenSquared = (closestPt - prList->camPos).lenSquared();
if (lenSquared >= prList->viewDistSquared)
return;
F32 len = mSqrt(lenSquared);
F32 top = obj->getWorldBox().max.z;
F32 bottom = obj->getWorldBox().min.z;
if (prList->mState->isBoxFogVisible(len, top, bottom))
prList->insertObject(obj);
}
} // namespace {}
void SceneGraph::buildSceneTree(SceneState* state,
SceneObject* baseObject,
const U32 baseZone,
const U32 currDepth,
const U32 objectMask )
{
AssertFatal(this == gClientSceneGraph, "Error, only the client scenegraph can support this call!");
// Search proceeds from the baseObject, and starts in the baseZone.
// General Outline:
// - Traverse up the tree, stopping at either the root, or the last interior
// that prevents traversal outside
// - Query the container database for all objects intersecting the viewcone,
// which is clipped to the bounding box returned at the last stage of the
// above traversal.
// - Topo sort the returned objects.
// - Traverse through the list, calling setupZones on zone managers,
// and retreiving render images from all applicable objects (including
// ZM's)
// - This process may return "Transform portals", i.e., mirrors, rendered
// teleporters, etc. For each of these, create a new SceneState object
// subsidiary to state, and restart the traversal, with the new parameters,
// and the correct baseObject and baseZone.
// Objects (in particular, those managers that are part of the initial up
// traversal) keep track of whether or not they have returned a render image
// to the current state by a key, and the state object pointer.
smStateKey++;
// Save off the base state...
SceneState::ZoneState saveBase = state->getBaseZoneState();
SceneObject* pTraversalRoot = baseObject;
U32 rootZone = baseZone;
while (true)
{
if (pTraversalRoot->prepRenderImage(state, smStateKey, rootZone, true))
{
if (pTraversalRoot->getNumCurrZones() != 1)
Con::errorf(ConsoleLogEntry::General,
"Error, must have one and only one zone to be a traversal root. %s has %d",
pTraversalRoot->getName(), pTraversalRoot->getNumCurrZones());
rootZone = pTraversalRoot->getCurrZone(0);
pTraversalRoot = getZoneOwner(rootZone);
}
else
{
break;
}
}
// Restore the base state...
SceneState::ZoneState& rState = state->getBaseZoneStateNC();
rState = saveBase;
// Ok. Now we have renderimages for anything north of the object in the
// tree. Create the query polytope, and clip it to the bounding box of
// the traversalRoot object.
PotentialRenderList prl;
prl.setupClipPlanes(state);
prl.viewDistSquared = getVisibleDistanceMod() * getVisibleDistanceMod();
// We only have to clip the mBox field
AssertFatal(prl.mBox.isOverlapped(pTraversalRoot->getWorldBox()),
"Error, prl box must overlap the traversal root");
prl.mBox.min.setMax(pTraversalRoot->getWorldBox().min);
prl.mBox.max.setMin(pTraversalRoot->getWorldBox().max);
prl.mBox.min -= Point3F(5, 5, 5);
prl.mBox.max += Point3F(5, 5, 5);
AssertFatal(prl.mBox.isValidBox(), "Error, invalid query box created!");
// Query against the container database, storing the objects in the
// potentially rendered list. Note: we can query against the client
// container without testing, since only the client will be calling this
// function. This is assured by the assert at the top...
gClientContainer.findObjects(prl.mBox, objectMask, prlInsertionCallback, &prl);
// Clear the object colors
U32 i;
for (i = 0; i < prl.mList.size(); i++)
prl.mList[i]->setTraversalState( SceneObject::Pending );
for (i = 0; i < prl.mList.size(); i++)
if( prl.mList[i]->getTraversalState() == SceneObject::Pending )
treeTraverseVisit(prl.mList[i], state, smStateKey);
if (currDepth < csmMaxTraversalDepth && state->mTransformPortals.size() != 0)
{
// Need to handle the transform portals here.
//
for (U32 i = 0; i < state->mTransformPortals.size(); i++)
{
const SceneState::TransformPortal& rPortal = state->mTransformPortals[i];
const SceneState::ZoneState& rPZState = state->getZoneState(rPortal.globalZone);
AssertFatal(rPZState.render == true, "Error, should not have returned a portal if the zone isn't rendering!");
Point3F cameraPosition = state->getCameraPosition();
rPortal.owner->transformPosition(rPortal.portalIndex, cameraPosition);
// Setup the new modelview matrix...
MatrixF oldMV;
MatrixF newMV;
dglGetModelview(&oldMV);
rPortal.owner->transformModelview(rPortal.portalIndex, oldMV, &newMV);
// Here's the tricky bit. We have to derive a new frustum and viewport
// from the portal, but we have to do it in the NEW coordinate space.
// Seems easiest to dump the responsibility on the object that was rude
// enough to make us go to all this trouble...
F64 newFrustum[4];
RectI newViewport;
bool goodPortal = rPortal.owner->computeNewFrustum(rPortal.portalIndex, // which portal?
rPZState.frustum, // old view params
state->mNearPlane,
state->mFarPlane,
rPZState.viewport,
newFrustum, // new view params
newViewport,
state->mFlipCull);
if (goodPortal == false)
{
// Portal isn't visible, or is clipped out by the zone parameters...
continue;
}
SceneState* newState = new SceneState(state,
mCurrZoneEnd,
newFrustum[0],
newFrustum[1],
newFrustum[2],
newFrustum[3],
state->mNearPlane,
state->mFarPlane,
newViewport,
cameraPosition,
newMV,
mFogDistance,
mVisibleDistance,
mFogColor,
mNumFogVolumes,
mFogVolumes,
state->getEnvironmentMap(),
smVisibleDistanceMod);
newState->mFlipCull = state->mFlipCull ^ rPortal.flipCull;
newState->setPortal(rPortal.owner, rPortal.portalIndex);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
dglLoadMatrix(&newMV);
// Find the start zone. Note that in a traversal descent, we start from
// the traversePoint of the transform portal, which is conveniently in
// world space...
SceneObject* startObject;
U32 startZone;
findZone(rPortal.traverseStart, startObject, startZone);
buildSceneTree(newState, startObject, startZone, currDepth + 1, objectMask);
// Pop off the new modelview
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
// Push the subsidiary...
state->mSubsidiaries.push_back(newState);
}
}
// Ok, that's it!
}
bool terrCheck(TerrainBlock* pBlock,
SceneObject* pObj,
const Point3F camPos);
void SceneGraph::treeTraverseVisit(SceneObject* obj,
SceneState* state,
const U32 stateKey)
{
if (obj->getNumCurrZones() == 0)
{
obj->setTraversalState( SceneObject::Done );
return;
}
AssertFatal(obj->getTraversalState() == SceneObject::Pending,
"Wrong state for this stage of the traversal!");
obj->setTraversalState(SceneObject::Working); // TraversalState Not being updated correctly 'Gonzo'
SceneObjectRef* pWalk = obj->mZoneRefHead;
AssertFatal(pWalk != NULL, "Error, must belong to something!");
while (pWalk)
{
// Determine who owns this zone...
SceneObject* pOwner = getZoneOwner(pWalk->zone);
if( pOwner->getTraversalState() == SceneObject::Pending )
treeTraverseVisit(pOwner, state, stateKey);
pWalk = pWalk->nextInObj;
}
obj->setTraversalState( SceneObject::Done );//obj->setTraverseColor(SceneObject::Black);
// Cull it, but not if it's too low or there's no terrain to occlude against, or if it's global...
if (getCurrentTerrain() != NULL && obj->getWorldBox().min.x > -1e5 && !obj->isGlobalBounds())
{
bool doTerrCheck = true;
SceneObjectRef* pRef = obj->mZoneRefHead;
while (pRef != NULL)
{
if (pRef->zone != 0)
{
doTerrCheck = false;
break;
}
pRef = pRef->nextInObj;
}
if (doTerrCheck == true && terrCheck(getCurrentTerrain(), obj, state->getCameraPosition()) == true)
return;
}
obj->prepRenderImage(state, stateKey, 0xFFFFFFFF);
}
bool terrCheck(TerrainBlock* pBlock,
SceneObject* pObj,
const Point3F camPos)
{
// Don't try to occlude globally bounded objects.
if(pObj->isGlobalBounds())
return false;
Point3F localCamPos = camPos;
pBlock->getWorldTransform().mulP(localCamPos);
F32 height;
pBlock->getHeight(Point2F(localCamPos.x, localCamPos.y), &height);
bool aboveTerrain = (height <= localCamPos.z);
// Don't occlude if we're below the terrain. This prevents problems when
// looking out from underground bases...
if (aboveTerrain == false)
return false;
const Box3F& oBox = pObj->getObjBox();
F32 minSide = getMin(oBox.len_x(), oBox.len_y());
if (minSide > 85.0f)
return false;
const Box3F& rBox = pObj->getWorldBox();
Point3F ul(rBox.min.x, rBox.min.y, rBox.max.z);
Point3F ur(rBox.min.x, rBox.max.y, rBox.max.z);
Point3F ll(rBox.max.x, rBox.min.y, rBox.max.z);
Point3F lr(rBox.max.x, rBox.max.y, rBox.max.z);
pBlock->getWorldTransform().mulP(ul);
pBlock->getWorldTransform().mulP(ur);
pBlock->getWorldTransform().mulP(ll);
pBlock->getWorldTransform().mulP(lr);
Point3F xBaseL0_s = ul - localCamPos;
Point3F xBaseL0_e = lr - localCamPos;
Point3F xBaseL1_s = ur - localCamPos;
Point3F xBaseL1_e = ll - localCamPos;
static F32 checkPoints[3] = {0.75, 0.5, 0.25};
RayInfo rinfo;
for (U32 i = 0; i < 3; i++)
{
Point3F start = (xBaseL0_s * checkPoints[i]) + localCamPos;
Point3F end = (xBaseL0_e * checkPoints[i]) + localCamPos;
if (pBlock->castRay(start, end, &rinfo))
continue;
pBlock->getHeight(Point2F(start.x, start.y), &height);
if ((height <= start.z) == aboveTerrain)
continue;
start = (xBaseL1_s * checkPoints[i]) + localCamPos;
end = (xBaseL1_e * checkPoints[i]) + localCamPos;
if (pBlock->castRay(start, end, &rinfo))
continue;
Point3F test = (start + end) * 0.5;
if (pBlock->castRay(localCamPos, test, &rinfo) == false)
continue;
return true;
}
return false;
}

384
engine/sceneGraph/sgUtil.cc Executable file
View File

@ -0,0 +1,384 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "sceneGraph/sgUtil.h"
#include "math/mRect.h"
#include "math/mMatrix.h"
#include "dgl/dgl.h"
namespace {
// Static state for sgComputeNewFrustum
//
Point3F sgCamPoint;
MatrixF sgWSToOSMatrix;
MatrixF sgProjMatrix;
PlaneF sgOSPlaneFar;
PlaneF sgOSPlaneXMin;
PlaneF sgOSPlaneXMax;
PlaneF sgOSPlaneYMin;
PlaneF sgOSPlaneYMax;
void clipToPlane(Point3F* points, U32& rNumPoints, const PlaneF& rPlane)
{
S32 start = -1;
for (U32 i = 0; i < rNumPoints; i++) {
if (rPlane.whichSide(points[i]) == PlaneF::Front) {
start = i;
break;
}
}
// Nothing was in front of the plane...
if (start == -1) {
rNumPoints = 0;
return;
}
Point3F finalPoints[128];
U32 numFinalPoints = 0;
U32 baseStart = start;
U32 end = (start + 1) % rNumPoints;
while (end != baseStart) {
const Point3F& rStartPoint = points[start];
const Point3F& rEndPoint = points[end];
PlaneF::Side fSide = rPlane.whichSide(rStartPoint);
PlaneF::Side eSide = rPlane.whichSide(rEndPoint);
S32 code = fSide * 3 + eSide;
switch (code) {
case 4: // f f
case 3: // f o
case 1: // o f
case 0: // o o
// No Clipping required
finalPoints[numFinalPoints++] = points[start];
start = end;
end = (end + 1) % rNumPoints;
break;
case 2: { // f b
// In this case, we emit the front point, Insert the intersection,
// and advancing to point to first point that is in front or on...
//
finalPoints[numFinalPoints++] = points[start];
Point3F vector = rEndPoint - rStartPoint;
F32 t = -(rPlane.distToPlane(rStartPoint) / mDot(rPlane, vector));
Point3F intersection = rStartPoint + (vector * t);
finalPoints[numFinalPoints++] = intersection;
U32 endSeek = (end + 1) % rNumPoints;
while (rPlane.whichSide(points[endSeek]) == PlaneF::Back)
endSeek = (endSeek + 1) % rNumPoints;
end = endSeek;
start = (end + (rNumPoints - 1)) % rNumPoints;
const Point3F& rNewStartPoint = points[start];
const Point3F& rNewEndPoint = points[end];
vector = rNewEndPoint - rNewStartPoint;
t = -(rPlane.distToPlane(rNewStartPoint) / mDot(rPlane, vector));
intersection = rNewStartPoint + (vector * t);
points[start] = intersection;
}
break;
case -1: {// o b
// In this case, we emit the front point, and advance to point to first
// point that is in front or on...
//
finalPoints[numFinalPoints++] = points[start];
U32 endSeek = (end + 1) % rNumPoints;
while (rPlane.whichSide(points[endSeek]) == PlaneF::Back)
endSeek = (endSeek + 1) % rNumPoints;
end = endSeek;
start = (end + (rNumPoints - 1)) % rNumPoints;
const Point3F& rNewStartPoint = points[start];
const Point3F& rNewEndPoint = points[end];
Point3F vector = rNewEndPoint - rNewStartPoint;
F32 t = -(rPlane.distToPlane(rNewStartPoint) / mDot(rPlane, vector));
Point3F intersection = rNewStartPoint + (vector * t);
points[start] = intersection;
}
break;
case -2: // b f
case -3: // b o
case -4: // b b
// In the algorithm used here, this should never happen...
AssertISV(false, "SGUtil::clipToPlane: error in polygon clipper");
break;
default:
AssertFatal(false, "SGUtil::clipToPlane: bad outcode");
break;
}
}
// Emit the last point.
finalPoints[numFinalPoints++] = points[start];
AssertFatal(numFinalPoints >= 3, avar("Error, this shouldn't happen! Invalid winding in clipToPlane: %d", numFinalPoints));
// Copy the new rWinding, and we're set!
//
dMemcpy(points, finalPoints, numFinalPoints * sizeof(Point3F));
rNumPoints = numFinalPoints;
AssertISV(rNumPoints <= 128, "MaxWindingPoints exceeded in scenegraph. Fatal error.");
}
void fixupViewport(const F64* oldFrustum,
const RectI& oldViewport,
F64* newFrustum,
RectI& newViewport)
{
F64 widthV = newFrustum[1] - newFrustum[0];
F64 heightV = newFrustum[3] - newFrustum[2];
F64 fx0 = (newFrustum[0] - oldFrustum[0]) / (oldFrustum[1] - oldFrustum[0]);
F64 fx1 = (oldFrustum[1] - newFrustum[1]) / (oldFrustum[1] - oldFrustum[0]);
F64 dV0 = F64(oldViewport.point.x) + fx0 * F64(oldViewport.extent.x);
F64 dV1 = F64(oldViewport.point.x +
oldViewport.extent.x) - fx1 * F64(oldViewport.extent.x);
F64 fdV0 = mFloor(dV0);
F64 cdV1 = mCeil(dV1);
F64 new0 = newFrustum[0] - ((dV0 - fdV0) * (widthV / F64(oldViewport.extent.x)));
F64 new1 = newFrustum[1] + ((cdV1 - dV1) * (widthV / F64(oldViewport.extent.x)));
newFrustum[0] = new0;
newFrustum[1] = new1;
newViewport.point.x = S32(fdV0);
newViewport.extent.x = S32(cdV1) - newViewport.point.x;
F64 fy0 = (oldFrustum[3] - newFrustum[3]) / (oldFrustum[3] - oldFrustum[2]);
F64 fy1 = (newFrustum[2] - oldFrustum[2]) / (oldFrustum[3] - oldFrustum[2]);
dV0 = F64(oldViewport.point.y) + fy0 * F64(oldViewport.extent.y);
dV1 = F64(oldViewport.point.y + oldViewport.extent.y) - fy1 * F64(oldViewport.extent.y);
fdV0 = mFloor(dV0);
cdV1 = mCeil(dV1);
new0 = newFrustum[2] - ((cdV1 - dV1) * (heightV / F64(oldViewport.extent.y)));
new1 = newFrustum[3] + ((dV0 - fdV0) * (heightV / F64(oldViewport.extent.y)));
newFrustum[2] = new0;
newFrustum[3] = new1;
newViewport.point.y = S32(fdV0);
newViewport.extent.y = S32(cdV1) - newViewport.point.y;
}
bool projectClipAndBoundWinding(const SGWinding& rWinding, F64* pResult)
{
AssertFatal(rWinding.numPoints >= 3, "Error, that's not a winding!");
static Point3F windingPoints[128];
U32 i;
for (i = 0; i < rWinding.numPoints; i++)
windingPoints[i] = rWinding.points[i];
U32 numPoints = rWinding.numPoints;
clipToPlane(windingPoints, numPoints, sgOSPlaneFar);
if (numPoints != 0)
clipToPlane(windingPoints, numPoints, sgOSPlaneXMin);
if (numPoints != 0)
clipToPlane(windingPoints, numPoints, sgOSPlaneXMax);
if (numPoints != 0)
clipToPlane(windingPoints, numPoints, sgOSPlaneYMin);
if (numPoints != 0)
clipToPlane(windingPoints, numPoints, sgOSPlaneYMax);
if (numPoints == 0)
return false;
Point4F projPoint;
for (i = 0; i < numPoints; i++) {
projPoint.set(windingPoints[i].x, windingPoints[i].y, windingPoints[i].z, 1.0);
sgProjMatrix.mul(projPoint);
AssertFatal(projPoint.w != 0.0, "Error, that's bad! (Point projected with non-zero w.)");
projPoint.x /= projPoint.w;
projPoint.y /= projPoint.w;
if (projPoint.x < pResult[0])
pResult[0] = projPoint.x;
if (projPoint.x > pResult[1])
pResult[1] = projPoint.x;
if (projPoint.y < pResult[2])
pResult[2] = projPoint.y;
if (projPoint.y > pResult[3])
pResult[3] = projPoint.y;
}
if (pResult[0] < -1.0f) pResult[0] = -1.0f;
if (pResult[2] < -1.0f) pResult[2] = -1.0f;
if (pResult[1] > 1.0f) pResult[1] = 1.0f;
if (pResult[3] > 1.0f) pResult[3] = 1.0f;
return true;
}
} // namespace { }
//--------------------------------------------------------------------------
bool sgComputeNewFrustum(const F64* oldFrustum,
const F64 nearPlane,
const F64 farPlane,
const RectI& oldViewport,
const SGWinding* windings,
const U32 numWindings,
const MatrixF& modelview,
F64* newFrustum,
RectI& newViewport,
const bool flippedMatrix)
{
// Ok, here's the deal. We need to project and clip the portal windings given the
// above parameters. We then re-compute the new frustum, and return it.
// We need the projection matrix. This is an ugly way to do this...
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
dglSetFrustum(oldFrustum[0], oldFrustum[1],
oldFrustum[2], oldFrustum[3],
nearPlane, farPlane);
dglGetProjection(&sgProjMatrix);
glPopMatrix();
MatrixF finalModelView = modelview;
sgProjMatrix.mul(finalModelView);
finalModelView.inverse();
finalModelView.mulP(Point3F(0, 0, 0), &sgCamPoint);
sgWSToOSMatrix = finalModelView;
// Create the object space clipping planes...
Point3F ul(oldFrustum[0], nearPlane, oldFrustum[3]);
Point3F ur(oldFrustum[1], nearPlane, oldFrustum[3]);
Point3F ll(oldFrustum[0], nearPlane, oldFrustum[2]);
Point3F lr(oldFrustum[1], nearPlane, oldFrustum[2]);
Point3F farPlanePoint(0, farPlane, 0);
sgWSToOSMatrix.mulP(ul);
sgWSToOSMatrix.mulP(ur);
sgWSToOSMatrix.mulP(ll);
sgWSToOSMatrix.mulP(lr);
sgWSToOSMatrix.mulP(farPlanePoint);
sgOSPlaneFar.set(farPlanePoint, sgCamPoint - farPlanePoint);
sgOSPlaneXMin.set(sgCamPoint, ul, ll);
sgOSPlaneXMax.set(sgCamPoint, lr, ur);
sgOSPlaneYMin.set(sgCamPoint, ur, ul);
sgOSPlaneYMax.set(sgCamPoint, ll, lr);
if (flippedMatrix == true) {
sgOSPlaneXMin.neg();
sgOSPlaneXMax.neg();
sgOSPlaneYMin.neg();
sgOSPlaneYMax.neg();
}
bool goodResult = false;
newFrustum[0] = 1e10;
newFrustum[1] = -1e10;
newFrustum[2] = 1e10;
newFrustum[3] = -1e10;
for (U32 i = 0; i < numWindings; i++) {
if (projectClipAndBoundWinding(windings[i], newFrustum))
goodResult = true;
}
if (goodResult == true) {
// good rect, build frustum and viewport
F64 minx = ((newFrustum[0] + 1.0f) / 2.0f) * (oldFrustum[1] - oldFrustum[0]) + oldFrustum[0];
F64 maxx = ((newFrustum[1] + 1.0f) / 2.0f) * (oldFrustum[1] - oldFrustum[0]) + oldFrustum[0];
F64 miny = ((newFrustum[2] + 1.0f) / 2.0f) * (oldFrustum[3] - oldFrustum[2]) + oldFrustum[2];
F64 maxy = ((newFrustum[3] + 1.0f) / 2.0f) * (oldFrustum[3] - oldFrustum[2]) + oldFrustum[2];
RectD bogus(minx, miny, (maxx - minx), (maxy - miny));
newFrustum[0] = bogus.point.x; // left
newFrustum[1] = bogus.point.x + bogus.extent.x; // right
newFrustum[2] = bogus.point.y; // bottom
newFrustum[3] = bogus.point.y + bogus.extent.y; // top
fixupViewport(oldFrustum, oldViewport, newFrustum, newViewport);
return true;
} else {
// No portal visibility
return false;
}
}
void sgComputeOSFrustumPlanes(const F64 frustumParameters[6],
const MatrixF& worldSpaceToObjectSpace,
const Point3F& wsCamPoint,
PlaneF& outFarPlane,
PlaneF& outXMinPlane,
PlaneF& outXMaxPlane,
PlaneF& outYMinPlane,
PlaneF& outYMaxPlane)
{
// Create the object space clipping planes...
Point3F ul(frustumParameters[0] * 1000.0, frustumParameters[4] * 1000.0, frustumParameters[3] * 1000.0);
Point3F ur(frustumParameters[1] * 1000.0, frustumParameters[4] * 1000.0, frustumParameters[3] * 1000.0);
Point3F ll(frustumParameters[0] * 1000.0, frustumParameters[4] * 1000.0, frustumParameters[2] * 1000.0);
Point3F lr(frustumParameters[1] * 1000.0, frustumParameters[4] * 1000.0, frustumParameters[2] * 1000.0);
Point3F farPlane(0, frustumParameters[5], 0);
worldSpaceToObjectSpace.mulP(ul);
worldSpaceToObjectSpace.mulP(ur);
worldSpaceToObjectSpace.mulP(ll);
worldSpaceToObjectSpace.mulP(lr);
worldSpaceToObjectSpace.mulP(farPlane);
outFarPlane.set(farPlane, wsCamPoint - farPlane);
outXMinPlane.set(wsCamPoint, ul, ll);
outXMaxPlane.set(wsCamPoint, lr, ur);
outYMinPlane.set(wsCamPoint, ur, ul);
outYMaxPlane.set(wsCamPoint, ll, lr);
}
// MM/JF: Added for mirrorSubObject fix.
void sgOrientClipPlanes(
PlaneF * planes,
const Point3F & camPos,
const Point3F & leftUp,
const Point3F & leftDown,
const Point3F & rightUp,
const Point3F & rightDown)
{
AssertFatal(planes, "orientClipPlanes: NULL planes ptr");
planes[0].set(camPos, leftUp, leftDown);
planes[1].set(camPos, rightUp, leftUp);
planes[2].set(camPos, rightDown, rightUp);
planes[3].set(camPos, leftDown, rightDown);
planes[4].set(leftUp, rightUp, rightDown);
// clip-planes through mirror portal are inverted
PlaneF plane(leftUp, rightUp, rightDown);
if(plane.whichSide(camPos) == PlaneF::Back)
for(U32 i = 0; i < 5; i++)
planes[i].invert();
}

56
engine/sceneGraph/sgUtil.h Executable file
View File

@ -0,0 +1,56 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _SGUTIL_H_
#define _SGUTIL_H_
#ifndef _PLATFORM_H_
#include "platform/platform.h"
#endif
#ifndef _MPOINT_H_
#include "math/mPoint.h"
#endif
class RectI;
class MatrixF;
struct SGWinding
{
Point3F points[32];
U32 numPoints;
};
bool sgComputeNewFrustum(const F64* oldFrustum,
const F64 nearPlane,
const F64 farPlane,
const RectI& oldViewport,
const SGWinding* windings,
const U32 numWindings,
const MatrixF& modelview,
F64* newFrustum,
RectI& newViewport,
const bool flippedMatrix);
/// Compute frustrum planes.
///
/// Frustum parameters are:
/// - [0] = left
/// - [1] = right
/// - [2] = top
/// - [3] = bottom
/// - [4] = near
/// - [5] = far
void sgComputeOSFrustumPlanes(const F64 frustumParameters[6],
const MatrixF& worldSpaceToObjectSpace,
const Point3F& wsCamPoint,
PlaneF& outFarPlane,
PlaneF& outXMinPlane,
PlaneF& outXMaxPlane,
PlaneF& outYMinPlane,
PlaneF& outYMaxPlane);
void sgOrientClipPlanes(PlaneF * planes, const Point3F & camPos, const Point3F & leftUp, const Point3F & leftDown, const Point3F & rightUp, const Point3F & rightDown);
#endif // _H_SGUTIL_

View File

@ -0,0 +1,706 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "sceneGraph/shadowVolumeBSP.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;
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->mType != LightInfo::Vector)
return;
// build the poly
Point3F pointOffset = light->mDirection * 10.f;
// create the shadow volume
mShadowVolumes.increment();
SVNode ** traverse = &mShadowVolumes.last();
U32 shadowVolumeIndex = mShadowVolumes.size() - 1;
for(U32 i = 0; i < poly->mWindingCount; i++)
{
U32 j = (i + 1) % poly->mWindingCount;
if(poly->mWinding[i] == poly->mWinding[j])
continue;
(*traverse) = createNode();
Point3F & a = poly->mWinding[i];
Point3F & b = poly->mWinding[j];
Point3F c = b + pointOffset;
(*traverse)->mPlaneIndex = insertPlane(PlaneF(a,b,c));
(*traverse)->mShadowVolume = shadowVolumeIndex;
traverse = &(*traverse)->mFront;
}
// do the poly node
(*traverse) = createNode();
(*traverse)->mPlaneIndex = insertPlane(poly->mPlane);
(*traverse)->mShadowVolume = poly->mShadowVolume = shadowVolumeIndex;
}
ShadowVolumeBSP::SVPoly * ShadowVolumeBSP::copyPoly(SVPoly * src)
{
SVPoly * poly = createPoly();
dMemcpy(poly, src, sizeof(SVPoly));
poly->mTarget = 0;
poly->mNext = 0;
return(poly);
}
//------------------------------------------------------------------------------
void ShadowVolumeBSP::addToPolyList(SVPoly ** store, SVPoly * poly) const
{
poly->mNext = *store;
*store = poly;
}
//------------------------------------------------------------------------------
void ShadowVolumeBSP::clipPoly(SVNode * root, SVPoly ** store, SVPoly * poly)
{
if(!root)
{
recyclePoly(poly);
return;
}
const PlaneF & plane = getPlane(root->mPlaneIndex);
switch(whichSide(poly, plane))
{
case SVNode::On:
case SVNode::Back:
if(root->mBack)
clipPoly(root->mBack, store, poly);
else
addToPolyList(store, poly);
break;
case SVNode::Front:
// encountered POLY node?
if(!root->mFront)
{
recyclePoly(poly);
return;
}
else
clipPoly(root->mFront, store, poly);
break;
case SVNode::Split:
{
SVPoly * front;
SVPoly * back;
splitPoly(poly, plane, &front, &back);
AssertFatal(front && back, "ShadowVolumeBSP::clipPoly: invalid split");
recyclePoly(poly);
// front
if(!root->mFront)
{
recyclePoly(front);
return;
}
else
clipPoly(root->mFront, store, front);
// back
if(root->mBack)
clipPoly(root->mBack, store, back);
else
addToPolyList(store, back);
break;
}
}
}
// clip a poly to it's own shadow volume
void ShadowVolumeBSP::clipToSelf(SVNode * root, SVPoly ** store, SVPoly * poly)
{
if(!root)
{
addToPolyList(store, poly);
return;
}
const PlaneF & plane = getPlane(root->mPlaneIndex);
switch(whichSide(poly, plane))
{
case SVNode::Front:
clipToSelf(root->mFront, store, poly);
break;
case SVNode::On:
addToPolyList(store, poly);
break;
case SVNode::Back:
recyclePoly(poly);
break;
case SVNode::Split:
{
SVPoly * front = 0;
SVPoly * back = 0;
splitPoly(poly, plane, &front, &back);
AssertFatal(front && back, "ShadowVolumeBSP::clipToSelf: invalid split");
recyclePoly(poly);
recyclePoly(back);
clipToSelf(root->mFront, store, front);
break;
}
}
}
//------------------------------------------------------------------------------
F32 ShadowVolumeBSP::getPolySurfaceArea(SVPoly * poly) const
{
if(!poly)
return(0.f);
Point3F areaNorm(0,0,0);
for(U32 i = 0; i < poly->mWindingCount; i++)
{
U32 j = (i + 1) % poly->mWindingCount;
Point3F tmp;
mCross(poly->mWinding[i], poly->mWinding[j], &tmp);
areaNorm += tmp;
}
F32 area = mDot(poly->mPlane, areaNorm);
if(area < 0.f)
area *= -0.5f;
else
area *= 0.5f;
if(poly->mNext)
area += getPolySurfaceArea(poly->mNext);
return(area);
}
//------------------------------------------------------------------------------
F32 ShadowVolumeBSP::getClippedSurfaceArea(SVNode * root, SVPoly * poly)
{
SVPoly * store = 0;
clipPoly(root, &store, poly);
F32 area = getPolySurfaceArea(store);
recyclePoly(store);
return(area);
}
//-------------------------------------------------------------------------------
// Class SceneLighting::ShadowVolumeBSP
//-------------------------------------------------------------------------------
void ShadowVolumeBSP::movePolyList(SVPoly ** dest, SVPoly * list) const
{
while(list)
{
SVPoly * next = list->mNext;
addToPolyList(dest, list);
list = next;
}
}
F32 ShadowVolumeBSP::getLitSurfaceArea(SVPoly * poly, SurfaceInfo * surfaceInfo)
{
// clip the poly to the shadow volumes
SVPoly * polyStore = poly;
for(U32 i = 0; polyStore && (i < surfaceInfo->mShadowed.size()); i++)
{
SVPoly * polyList = 0;
SVPoly * traverse = polyStore;
while(traverse)
{
SVPoly * next = traverse->mNext;
traverse->mNext = 0;
SVPoly * currentStore = 0;
clipPoly(mShadowVolumes[surfaceInfo->mShadowed[i]], &currentStore, traverse);
if(currentStore)
movePolyList(&polyList, currentStore);
traverse = next;
}
polyStore = polyList;
}
// get the lit area
F32 area = getPolySurfaceArea(polyStore);
recyclePoly(polyStore);
return(area);
}
//------------------------------------------------------------------------------
void ShadowVolumeBSP::removeLastInterior()
{
if(!mSVRoot || !mFirstInteriorNode)
return;
AssertFatal(mFirstInteriorNode->mSurfaceInfo, "No surface info for first interior node!");
// reset the planes
mPlanes.setSize(mFirstInteriorNode->mPlaneIndex);
U32 i;
// flush the shadow volumes
for(i = mFirstInteriorNode->mShadowVolume; i < mShadowVolumes.size(); i++)
recycleNode(mShadowVolumes[i]);
mShadowVolumes.setSize(mFirstInteriorNode->mShadowVolume);
// flush the interior nodes
if(!mParentNodes.size() && (mFirstInteriorNode->mShadowVolume == mSVRoot->mShadowVolume))
{
recycleNode(mSVRoot);
mSVRoot = 0;
}
else
{
for(i = 0; i < mParentNodes.size(); i++)
{
recycleNode(mParentNodes[i]->mBack);
mParentNodes[i]->mBack = 0;
}
}
// flush the surfaces
for(i = 0; i < mSurfaces.size(); i++)
delete mSurfaces[i];
mSurfaces.clear();
mFirstInteriorNode = 0;
}

View File

@ -0,0 +1,142 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _SHADOWVOLUMEBSP_H_
#define _SHADOWVOLUMEBSP_H_
#ifndef _PLATFORM_H_
#include "platform/platform.h"
#endif
#ifndef _TVECTOR_H_
#include "core/tVector.h"
#endif
#ifndef _MMATH_H_
#include "math/mMath.h"
#endif
#ifndef _DATACHUNKER_H_
#include "core/dataChunker.h"
#endif
#ifndef _INTERIOR_H_
#include "interior/interior.h"
#endif
#include "lightingSystem/sgLightManager.h"
/// 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

View File

@ -0,0 +1,127 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "sceneGraph/windingClipper.h"
void sgUtil_clipToPlane(Point3F* points, U32& rNumPoints, const PlaneF& rPlane)
{
S32 start = -1;
for (U32 i = 0; i < rNumPoints; i++) {
if (rPlane.whichSide(points[i]) == PlaneF::Front) {
start = i;
break;
}
}
// Nothing was in front of the plane...
if (start == -1) {
rNumPoints = 0;
return;
}
Point3F finalPoints[128];
U32 numFinalPoints = 0;
U32 baseStart = start;
U32 end = (start + 1) % rNumPoints;
while (end != baseStart) {
const Point3F& rStartPoint = points[start];
const Point3F& rEndPoint = points[end];
PlaneF::Side fSide = rPlane.whichSide(rStartPoint);
PlaneF::Side eSide = rPlane.whichSide(rEndPoint);
S32 code = fSide * 3 + eSide;
switch (code) {
case 4: // f f
case 3: // f o
case 1: // o f
case 0: // o o
// No Clipping required
finalPoints[numFinalPoints++] = points[start];
start = end;
end = (end + 1) % rNumPoints;
break;
case 2: { // f b
// In this case, we emit the front point, Insert the intersection,
// and advancing to point to first point that is in front or on...
//
finalPoints[numFinalPoints++] = points[start];
Point3F vector = rEndPoint - rStartPoint;
F32 t = -(rPlane.distToPlane(rStartPoint) / mDot(rPlane, vector));
Point3F intersection = rStartPoint + (vector * t);
finalPoints[numFinalPoints++] = intersection;
U32 endSeek = (end + 1) % rNumPoints;
while (rPlane.whichSide(points[endSeek]) == PlaneF::Back)
endSeek = (endSeek + 1) % rNumPoints;
end = endSeek;
start = (end + (rNumPoints - 1)) % rNumPoints;
const Point3F& rNewStartPoint = points[start];
const Point3F& rNewEndPoint = points[end];
vector = rNewEndPoint - rNewStartPoint;
t = -(rPlane.distToPlane(rNewStartPoint) / mDot(rPlane, vector));
intersection = rNewStartPoint + (vector * t);
points[start] = intersection;
}
break;
case -1: {// o b
// In this case, we emit the front point, and advance to point to first
// point that is in front or on...
//
finalPoints[numFinalPoints++] = points[start];
U32 endSeek = (end + 1) % rNumPoints;
while (rPlane.whichSide(points[endSeek]) == PlaneF::Back)
endSeek = (endSeek + 1) % rNumPoints;
end = endSeek;
start = (end + (rNumPoints - 1)) % rNumPoints;
const Point3F& rNewStartPoint = points[start];
const Point3F& rNewEndPoint = points[end];
Point3F vector = rNewEndPoint - rNewStartPoint;
F32 t = -(rPlane.distToPlane(rNewStartPoint) / mDot(rPlane, vector));
Point3F intersection = rNewStartPoint + (vector * t);
points[start] = intersection;
}
break;
case -2: // b f
case -3: // b o
case -4: // b b
// In the algorithm used here, this should never happen...
AssertISV(false, "SGUtil::clipToPlane: error in polygon clipper");
break;
default:
AssertFatal(false, "SGUtil::clipToPlane: bad outcode");
break;
}
}
// Emit the last point.
finalPoints[numFinalPoints++] = points[start];
AssertFatal(numFinalPoints >= 3, avar("Error, this shouldn't happen! Invalid winding in clipToPlane: %d", numFinalPoints));
// Copy the new rWinding, and we're set!
//
dMemcpy(points, finalPoints, numFinalPoints * sizeof(Point3F));
rNumPoints = numFinalPoints;
AssertISV(rNumPoints <= 128, "MaxWindingPoints exceeded in scenegraph. Fatal error.");
}

View File

@ -0,0 +1,15 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _WINDINGCLIPPER_H_
#define _WINDINGCLIPPER_H_
#ifndef _MMATH_H_
#include "math/mMath.h"
#endif
void sgUtil_clipToPlane(Point3F* points, U32& rNumPoints, const PlaneF& rPlane);
#endif