tge/engine/sceneGraph/sceneState.cc
2017-04-17 06:17:10 -06:00

997 lines
29 KiB
C++
Executable File

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