added everything
This commit is contained in:
445
engine/sceneGraph/detailManager.cc
Executable file
445
engine/sceneGraph/detailManager.cc
Executable 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
108
engine/sceneGraph/detailManager.h
Executable 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
|
6
engine/sceneGraph/lightManager.cc
Executable file
6
engine/sceneGraph/lightManager.cc
Executable 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" )
|
11
engine/sceneGraph/lightManager.h
Executable file
11
engine/sceneGraph/lightManager.h
Executable 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
1124
engine/sceneGraph/sceneGraph.cc
Executable file
File diff suppressed because it is too large
Load Diff
346
engine/sceneGraph/sceneGraph.h
Executable file
346
engine/sceneGraph/sceneGraph.h
Executable 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
3014
engine/sceneGraph/sceneLighting.cc
Executable file
File diff suppressed because it is too large
Load Diff
278
engine/sceneGraph/sceneLighting.h
Executable file
278
engine/sceneGraph/sceneLighting.h
Executable 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
84
engine/sceneGraph/sceneRoot.cc
Executable 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
43
engine/sceneGraph/sceneRoot.h
Executable 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
996
engine/sceneGraph/sceneState.cc
Executable 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(¢er);
|
||||
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
477
engine/sceneGraph/sceneState.h
Executable 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_
|
||||
|
428
engine/sceneGraph/sceneTraversal.cc
Executable file
428
engine/sceneGraph/sceneTraversal.cc
Executable 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(¢er);
|
||||
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
384
engine/sceneGraph/sgUtil.cc
Executable 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
56
engine/sceneGraph/sgUtil.h
Executable 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_
|
706
engine/sceneGraph/shadowVolumeBSP.cc
Executable file
706
engine/sceneGraph/shadowVolumeBSP.cc
Executable 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]], ¤tStore, traverse);
|
||||
|
||||
if(currentStore)
|
||||
movePolyList(&polyList, currentStore);
|
||||
|
||||
traverse = next;
|
||||
}
|
||||
polyStore = polyList;
|
||||
}
|
||||
|
||||
// get the lit area
|
||||
F32 area = getPolySurfaceArea(polyStore);
|
||||
recyclePoly(polyStore);
|
||||
return(area);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void ShadowVolumeBSP::removeLastInterior()
|
||||
{
|
||||
if(!mSVRoot || !mFirstInteriorNode)
|
||||
return;
|
||||
|
||||
AssertFatal(mFirstInteriorNode->mSurfaceInfo, "No surface info for first interior node!");
|
||||
|
||||
// reset the planes
|
||||
mPlanes.setSize(mFirstInteriorNode->mPlaneIndex);
|
||||
|
||||
U32 i;
|
||||
|
||||
// flush the shadow volumes
|
||||
for(i = mFirstInteriorNode->mShadowVolume; i < mShadowVolumes.size(); i++)
|
||||
recycleNode(mShadowVolumes[i]);
|
||||
mShadowVolumes.setSize(mFirstInteriorNode->mShadowVolume);
|
||||
|
||||
// flush the interior nodes
|
||||
if(!mParentNodes.size() && (mFirstInteriorNode->mShadowVolume == mSVRoot->mShadowVolume))
|
||||
{
|
||||
recycleNode(mSVRoot);
|
||||
mSVRoot = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
for(i = 0; i < mParentNodes.size(); i++)
|
||||
{
|
||||
recycleNode(mParentNodes[i]->mBack);
|
||||
mParentNodes[i]->mBack = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// flush the surfaces
|
||||
for(i = 0; i < mSurfaces.size(); i++)
|
||||
delete mSurfaces[i];
|
||||
mSurfaces.clear();
|
||||
|
||||
mFirstInteriorNode = 0;
|
||||
}
|
142
engine/sceneGraph/shadowVolumeBSP.h
Executable file
142
engine/sceneGraph/shadowVolumeBSP.h
Executable 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
|
127
engine/sceneGraph/windingClipper.cc
Executable file
127
engine/sceneGraph/windingClipper.cc
Executable 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.");
|
||||
}
|
15
engine/sceneGraph/windingClipper.h
Executable file
15
engine/sceneGraph/windingClipper.h
Executable 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
|
Reference in New Issue
Block a user