//----------------------------------------------------------------------------- // 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 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 &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 *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 *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 *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 *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() { }