2017-04-17 06:17:10 -06:00

1872 lines
62 KiB
C++
Executable File

//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "terrain/sky.h"
#include "math/mMath.h"
#include "dgl/dgl.h"
#include "console/console.h"
#include "console/consoleTypes.h"
#include "core/bitStream.h"
#include "sceneGraph/sceneGraph.h"
#include "sceneGraph/sceneState.h"
#include "terrain/terrData.h"
#include "sim/sceneObject.h"
#include "math/mathIO.h"
#include "sceneGraph/windingClipper.h"
#include "platform/profiler.h"
// This dependency on game code needs to be removed.
#include "game/fx/particleEngine.h"
//
#define HORIZON 0.0f
#define OFFSET_HEIGHT 60.0f
#define FOG_BAN_DETAIL 8
#define RAD (2 * M_PI)
// To increase this visible distance cap you must up the value of this define and increase
// the amount of memory allocated to the FrameAllocator. This can be done changing the
// FrameAllocator::init() call found in main.cc.
// WARNING: increase the maximum visible distance can cause major blending and performance issues!
#define MAXVISIBLEDISTANCE 2000.0f
IMPLEMENT_CO_NETOBJECT_V1(Sky);
//Static Sky variables
bool Sky::smCloudsOn = true;
bool Sky::smCloudOutlineOn = false;
bool Sky::smSkyOn = true;
S32 Sky::smNumCloudsOn = MAX_NUM_LAYERS;
//Static Cloud variables
StormInfo Cloud::mGStormData;
F32 Cloud::mRadius;
//---------------------------------------------------------------------------
Sky::Sky()
{
mNumCloudLayers = 0;
mTypeMask |= EnvironmentObjectType;
mNetFlags.set(Ghostable | ScopeAlways);
mVisibleDistance = 250;
mFogDistance = 250;
mSkyTexturesOn = true;
mRenderBoxBottom = false;
mNumFogVolumes = 0;
mFogLine = 0.0f;
mRealFog = 0;
mFogColor.set(128, 128, 128);
mSolidFillColor.set(0.0, 1.0, 0,0);
mWindVelocity.set(1.f, 0.f, 0.f);
mWindDir.set(0.f, 0.f);
mEffectPrecip = false;
mNoRenderBans = false;
mLastVisDisMod = -1;
for(U32 j = 0; j < MaxFogVolumes; j++)
{
mFogVolumes[j].visibleDistance = -1;
mFogVolumes[j].percentage = 1.0f;
mFogVolumes[j].color.red = mFogColor.red;
mFogVolumes[j].color.green = mFogColor.green;
mFogVolumes[j].color.blue = mFogColor.blue;
}
for(S32 i = 0; i < MAX_NUM_LAYERS; ++i)
{
mCloudText[i] = "";
mCloudSpeed[i] = 0.0001 * (1 + (i * 1));
mCloudHeight[i] = 0;
}
mStormCloudData.state = isDone;
mStormCloudData.speed = 0.0f;
mStormCloudData.time = 0.0f;
mStormFogData.time = 0.0f;
mStormFogData.current = 0;
mStormFogData.lastTime = 0;
mStormFogData.startTime = 0;
mStormFogData.endPercentage = -1.0f;
for(S32 x = 0; x < MaxFogVolumes; ++x)
{
mStormFogData.volume[x].active = false;
mStormFogData.volume[x].state = isDone;
mStormFogData.volume[x].speed = 0.0f;
mStormFogData.volume[x].endPercentage = 1.0f;
}
mFogTime = 0.0f;
mFogVolume = -1;
mStormCloudsOn = true;
mStormFogOn = false;
mSetFog = true;
mLastForce16Bit = false;
mLastForcePaletted = false;
}
//------------------------------------------------------------------------------
Sky::~Sky()
{
}
//---------------------------------------------------------------------------
bool Sky::onAdd()
{
if(!Parent::onAdd())
return false;
mObjBox.min.set(-1e9, -1e9, -1e9);
mObjBox.max.set( 1e9, 1e9, 1e9);
resetWorldBox();
// Find out how many fog layers we have.
for(mNumFogVolumes = 0; mNumFogVolumes < MaxFogVolumes; mNumFogVolumes++)
if(mFogVolumes[mNumFogVolumes].visibleDistance == -1 || mFogVolumes[mNumFogVolumes].visibleDistance == 0)
break;
// Storm fog layers are off by default, others are on.
for(S32 i = 0; i < mNumFogVolumes; ++i)
mFogVolumes[i].percentage = mStormFogData.volume[i].active? 0.0f: 1.0f;
if(isClientObject())
{
if(!loadDml())
return false;
initSkyData();
}
else
{
setWindVelocity(mWindVelocity);
assignName("Sky");
}
addToScene();
setVisibility();
return true;
}
//---------------------------------------------------------------------------
void Sky::initSkyData()
{
calcPoints();
mWindDir = Point2F(mWindVelocity.x, mWindVelocity.y);
mWindDir.normalize();
for(S32 i = 0; i < MAX_NUM_LAYERS; ++i)
{
mCloudLayer[i].setHeights(mCloudHeight[i], mCloudHeight[i]-0.05f, 0.05f);
mCloudLayer[i].setSpeed(mWindDir * mCloudSpeed[i]);
mCloudLayer[i].setPoints();
}
setVisibility();
}
//---------------------------------------------------------------------------
void Sky::setVisibility()
{
if(mSceneManager)
{
extern bool sgForce16BitTexture;
extern bool sgForcePalettedTexture;
mRealFogColor.red = S32(mCeil(mFogColor.red * 255.0f));
mRealFogColor.green = S32(mCeil(mFogColor.green * 255.0f));
mRealFogColor.blue = S32(mCeil(mFogColor.blue * 255.0f));
mRealFogColor.alpha = 255;
if(sgForce16BitTexture)
{
U8 temp = (mRealFogColor.red >> 4) & 0xF;
mRealFogColor.red = (temp << 4) | temp;
temp = (mRealFogColor.green >> 4) & 0xF;
mRealFogColor.green = (temp << 4) | temp;
temp = (mRealFogColor.blue >> 4) & 0xF;
mRealFogColor.blue = (temp << 4) | temp;
mSceneManager->setFogColor((ColorF)mRealFogColor);
}
else
mSceneManager->setFogColor(mFogColor);
mRealSkyColor.red = S32(mSolidFillColor.red * 255.0f);
mRealSkyColor.green = S32(mSolidFillColor.green * 255.0f);
mRealSkyColor.blue = S32(mSolidFillColor.blue * 255.0f);
mSceneManager->setFogDistance(mFogDistance);
mSceneManager->setVisibleDistance(mVisibleDistance);
mSceneManager->setFogVolumes(mNumFogVolumes, mFogVolumes);
mLastForce16Bit = sgForce16BitTexture;
mLastForcePaletted = sgForcePalettedTexture;
}
}
//---------------------------------------------------------------------------
void Sky::setWindVelocity(const Point3F & vel)
{
mWindVelocity = vel;
ParticleEngine::setWindVelocity(vel);
if(isServerObject())
setMaskBits(WindMask);
}
Point3F Sky::getWindVelocity()
{
return(mWindVelocity);
}
//---------------------------------------------------------------------------
void Sky::onRemove()
{
removeFromScene();
Parent::onRemove();
}
//---------------------------------------------------------------------------
ConsoleMethod( Sky, stormClouds, void, 4, 4, "(bool show, float duration)")
{
object->stormCloudsOn(dAtoi(argv[2]), dAtof(argv[3]));
}
ConsoleMethod( Sky, stormFog, void, 4, 4, "(float percent, float duration)")
{
object->stormFogOn(dAtof(argv[2]), dAtof(argv[3]));
}
ConsoleMethod( Sky, realFog, void, 6, 6, "( bool show, float max, float min, float speed )")
{
object->stormRealFog(dAtoi(argv[2]), dAtof(argv[3]), dAtof(argv[4]), dAtof(argv[5]));
}
ConsoleMethod( Sky, getWindVelocity, const char *, 2, 2, "()")
{
char * retBuf = Con::getReturnBuffer(128);
Point3F vel = object->getWindVelocity();
dSprintf(retBuf, 128, "%g %g %g", vel.x, vel.y, vel.z);
return(retBuf);
}
ConsoleMethod( Sky, applySkyChanges, void, 2, 2, "() - Apply any changes.")
{
object->applySkyChanges();
}
//---------------------------------------------------------------------------
ConsoleMethod( Sky, setWindVelocity, void, 5, 5, "(float x, float y, float z)")
{
if(object->isClientObject())
return;
Point3F vel(dAtof(argv[2]), dAtof(argv[3]), dAtof(argv[4]));
object->setWindVelocity(vel);
}
ConsoleMethod( Sky, stormCloudsShow, void, 3, 3, "(bool showClouds)")
{
object->stormCloudsShow(dAtob(argv[2]));
}
ConsoleMethod( Sky, stormFogShow, void, 3, 3, "(bool show)")
{
object->stormFogShow(dAtob(argv[2]));
}
//---------------------------------------------------------------------------
bool Sky::processArguments(S32, const char**)
{
return true;
}
//---------------------------------------------------------------------------
void Sky::initPersistFields()
{
Parent::initPersistFields();
addGroup("Media");
addField("materialList", TypeFilename, Offset(mMaterialListName,Sky));
endGroup("Media");
addGroup("Clouds");
addField("cloudText", TypeString, Offset(mCloudText,Sky),MAX_NUM_LAYERS);
addField("cloudHeightPer", TypeF32, Offset(mCloudHeight,Sky),MAX_NUM_LAYERS);
addField("cloudSpeed1", TypeF32, Offset(mCloudSpeed[0],Sky));
addField("cloudSpeed2", TypeF32, Offset(mCloudSpeed[1],Sky));
addField("cloudSpeed3", TypeF32, Offset(mCloudSpeed[2],Sky));
endGroup("Clouds");
addGroup("Visibility");
addField("visibleDistance", TypeF32, Offset(mVisibleDistance, Sky));
endGroup("Visibility");
addGroup("Fog");
addField("fogDistance", TypeF32, Offset(mFogDistance, Sky));
addField("fogColor", TypeColorF, Offset(mFogColor, Sky));
// Should be using the console's array support for these...
addField("fogStorm1", TypeBool, Offset(mStormFogData.volume[0].active, Sky));
addField("fogStorm2", TypeBool, Offset(mStormFogData.volume[1].active, Sky));
addField("fogStorm3", TypeBool, Offset(mStormFogData.volume[2].active, Sky));
addField("fogVolume1", TypePoint3F, Offset(mFogVolumes[0], Sky));
addField("fogVolume2", TypePoint3F, Offset(mFogVolumes[1], Sky));
addField("fogVolume3", TypePoint3F, Offset(mFogVolumes[2], Sky));
addField("fogVolumeColor1", TypeColorF, Offset(mFogVolumes[0].color, Sky));
addField("fogVolumeColor2", TypeColorF, Offset(mFogVolumes[1].color, Sky));
addField("fogVolumeColor3", TypeColorF, Offset(mFogVolumes[2].color, Sky));
endGroup("Fog");
addGroup("Wind");
addField("windVelocity", TypePoint3F, Offset(mWindVelocity, Sky));
addField("windEffectPrecipitation", TypeBool, Offset(mEffectPrecip, Sky));
endGroup("Wind");
addGroup("Misc");
addField("SkySolidColor", TypeColorF, Offset(mSolidFillColor, Sky));
addField("useSkyTextures", TypeBool, Offset(mSkyTexturesOn, Sky));
addField("renderBottomTexture", TypeBool, Offset(mRenderBoxBottom, Sky));
addField("noRenderBans", TypeBool, Offset(mNoRenderBans, Sky));
endGroup("Misc");
}
void Sky::consoleInit()
{
#if defined(TORQUE_DEBUG)
Con::addVariable("pref::CloudOutline", TypeBool, &smCloudOutlineOn);
#endif
Con::addVariable("pref::CloudsOn", TypeBool, &smCloudsOn);
Con::addVariable("pref::NumCloudLayers", TypeS32, &smNumCloudsOn);
Con::addVariable("pref::SkyOn", TypeBool, &smSkyOn);
}
//---------------------------------------------------------------------------
void Sky::unpackUpdate(NetConnection *, BitStream *stream)
{
if(stream->readFlag())
{
mMaterialListName = stream->readSTString();
loadDml();
stream->read(&mFogColor.red);
stream->read(&mFogColor.green);
stream->read(&mFogColor.blue);
stream->read(&mNumFogVolumes);
stream->read(&mSkyTexturesOn);
stream->read(&mRenderBoxBottom);
stream->read(&mSolidFillColor.red);
stream->read(&mSolidFillColor.green);
stream->read(&mSolidFillColor.blue);
stream->read(&mEffectPrecip);
mNoRenderBans = stream->readFlag();
U32 i;
for(i = 0; i < mNumFogVolumes; i++)
{
stream->read(&mFogVolumes[i].visibleDistance);
stream->read(&mFogVolumes[i].minHeight);
stream->read(&mFogVolumes[i].maxHeight);
stream->read(&mFogVolumes[i].color.red);
stream->read(&mFogVolumes[i].color.green);
stream->read(&mFogVolumes[i].color.blue);
stream->read(&mStormFogData.volume[i].active);
}
for(i = 0; i < MAX_NUM_LAYERS; i++)
{
mCloudText[i] = stream->readSTString();
stream->read(&mCloudHeight[i]);
stream->read(&mCloudSpeed[i]);
}
initSkyData();
Point3F vel;
if(mathRead(*stream, &vel))
setWindVelocity(vel);
stream->read(&mFogVolume);
if(stream->readFlag())
{
U32 state, stormTimeDiff;
stream->read(&mFogPercentage);
stream->read(&mStormFogData.time);
stream->read(&state);
stream->read(&stormTimeDiff);
stream->read(&mStormFogData.endPercentage);
mStormFogData.state = SkyState(state);
if(mStormFogData.time)
{
Con::printf("Server Storm Time: %u",stormTimeDiff);
Con::printf("Get Current Time: %u",Sim::getCurrentTime());
mStormFogOn = true;
mStormFogData.lastTime = Sim::getCurrentTime() - stormTimeDiff;
Con::printf("READ OFFSET: %g", F32(Sim::getCurrentTime() - mStormFogData.lastTime) / 32.0f);
for(S32 x = 0; x < mNumFogVolumes; ++x)
{
if (mStormFogData.volume[x].active)
mFogVolumes[x].percentage = mFogPercentage;
mStormFogData.volume[x].endPercentage =
mStormFogData.volume[x].active? mStormFogData.endPercentage:
mFogVolumes[x].percentage;
mStormFogData.volume[x].speed = (mStormFogData.endPercentage - mFogVolumes[x].percentage) / ((mStormFogData.time * 32.0f) / (F32)mNumFogVolumes);
mStormFogData.volume[x].state = mStormFogData.state;
if(mStormFogData.volume[x].state == comingIn)
mStormFogData.current = 0;
else
mStormFogData.current = mNumFogVolumes-1;
}
}
}
}
if(stream->readFlag())
stream->read(&mStormCloudsOn);
if(stream->readFlag())
{
stream->read(&mStormFogOn);
if(!mStormFogOn)
{
for(S32 x = 0; x < mNumFogVolumes; x++)
if (mStormFogData.volume[x].active)
mFogVolumes[x].percentage = 0.0f;
mSetFog = true;
}
}
if(stream->readFlag())
{
stream->read(&mVisibleDistance);
stream->read(&mFogDistance);
initSkyData();
}
if(stream->readFlag())
{
U32 state;
stream->read(&state);
mStormCloudData.state = SkyState(state);
stream->read(&mStormCloudData.time);
if(mStormCloudData.time > 0.0f)
{
mStormCloudData.speed = ((mRadius * 2) * F32(mNumCloudLayers + 1)) / (mStormCloudData.time * 32.0f);
if(mNumCloudLayers)
mStormCloudData.fadeSpeed = 1.0f / (((mStormCloudData.time * 32.0f) / F32(mNumCloudLayers + 1)) / mNumCloudLayers);
startStorm();
}
}
if(stream->readFlag())
{
stream->read(&mFogPercentage);
stream->read(&mStormFogData.time);
stream->read(&mFogVolume);
if(mStormFogData.time)
{
mStormFogData.lastTime = Sim::getCurrentTime();
startStormFog();
}
}
if(stream->readFlag())
{
stream->read(&mRealFog);
stream->read(&mRealFogMax);
stream->read(&mRealFogMin);
stream->read(&mRealFogSpeed);
if(mRealFog)
{
for(S32 x = 0; x < mNumFogVolumes; ++x)
{
mStormFogData.volume[x].lastPercentage = mRealFogMax;
mStormFogData.volume[x].endPercentage = mRealFogMin;
mStormFogData.volume[x].speed = -(((mNumFogVolumes-x)*(mNumFogVolumes-x)) * mRealFogSpeed);
}
F32 save = mStormFogData.volume[0].speed;
mStormFogData.volume[0].speed = mStormFogData.volume[1].speed;
mStormFogData.volume[1].speed = save;
}
}
if(stream->readFlag())
{
Point3F vel;
if(mathRead(*stream, &vel))
setWindVelocity(vel);
}
}
//---------------------------------------------------------------------------
U32 Sky::packUpdate(NetConnection *, U32 mask, BitStream *stream)
{
if(stream->writeFlag(mask & InitMask))
{
stream->writeString(mMaterialListName);
stream->write(mFogColor.red);
stream->write(mFogColor.green);
stream->write(mFogColor.blue);
stream->write(mNumFogVolumes);
stream->write(mSkyTexturesOn);
stream->write(mRenderBoxBottom);
stream->write(mSolidFillColor.red);
stream->write(mSolidFillColor.green);
stream->write(mSolidFillColor.blue);
stream->write(mEffectPrecip);
stream->writeFlag(mNoRenderBans);
U32 i;
for(i = 0; i < mNumFogVolumes; i++)
{
stream->write(mFogVolumes[i].visibleDistance);
stream->write(mFogVolumes[i].minHeight);
stream->write(mFogVolumes[i].maxHeight);
stream->write(mFogVolumes[i].color.red);
stream->write(mFogVolumes[i].color.green);
stream->write(mFogVolumes[i].color.blue);
stream->write(mStormFogData.volume[i].active);
}
for(i = 0; i < MAX_NUM_LAYERS; i++)
{
stream->writeString(mCloudText[i]);
stream->write(mCloudHeight[i]);
stream->write(mCloudSpeed[i]);
}
mathWrite(*stream, mWindVelocity);
stream->write(mFogVolume);
U32 currentTime = Sim::getCurrentTime();
U32 stormTimeDiff = currentTime - mStormFogData.startTime;
if(stream->writeFlag(F32(stormTimeDiff) / 1000.0f < mStormFogData.time))
{
stream->write(mFogPercentage);
stream->write(mStormFogData.time);
stream->write(U32(mStormFogData.state));
stream->write(stormTimeDiff);
stream->write(mStormFogData.endPercentage);
Con::printf("WRITE OFFSET: %g", F32(stormTimeDiff) / 32.0f);
}
}
if(stream->writeFlag(mask & StormCloudsOnMask))
stream->write(mStormCloudsOn);
if(stream->writeFlag(mask & StormFogOnMask && !(mask & InitMask)))
stream->write(mStormFogOn);
if(stream->writeFlag(mask & VisibilityMask))
{
if (mVisibleDistance > MAXVISIBLEDISTANCE)
{
Con::errorf("Error: visibleDistance is capped at %g.", MAXVISIBLEDISTANCE);
Con::errorf("To increase this limitation requires C++ code changes.");
Con::errorf("Please see comment at the top of sky.cc");
mVisibleDistance = MAXVISIBLEDISTANCE;
}
stream->write(mVisibleDistance);
stream->write(mFogDistance);
}
if(stream->writeFlag(mask & StormCloudMask))
{
stream->write(U32(mStormCloudData.state));
stream->write(mStormCloudData.time);
}
if(stream->writeFlag(mask & StormFogMask && !(mask & InitMask)) )
{
stream->write(mStormFogData.endPercentage);
stream->write(mStormFogData.time);
stream->write(mFogVolume);
mStormFogData.startTime = Sim::getCurrentTime();
}
if(stream->writeFlag(mask & StormRealFogMask))
{
stream->write(mRealFog);
stream->write(mRealFogMax);
stream->write(mRealFogMin);
stream->write(mRealFogSpeed);
}
if(stream->writeFlag(mask & WindMask))
mathWrite(*stream, mWindVelocity);
return 0;
}
//---------------------------------------------------------------------------
void Sky::inspectPostApply()
{
for(mNumFogVolumes = 0; mNumFogVolumes < MaxFogVolumes; mNumFogVolumes++)
if(mFogVolumes[mNumFogVolumes].visibleDistance == -1 || mFogVolumes[mNumFogVolumes].visibleDistance == 0)
break;
setMaskBits(InitMask | VisibilityMask);
}
//---------------------------------------------------------------------------
void Sky::renderObject(SceneState* state, SceneRenderImage*)
{
PROFILE_START(Sky_renderObject);
AssertFatal(dglIsInCanonicalState(), "Error, GL not in canonical state on entry");
extern bool sgForce16BitTexture;
extern bool sgForcePalettedTexture;
if(mLastForce16Bit != sgForce16BitTexture || mLastForcePaletted != sgForcePalettedTexture)
setVisibility();
RectI viewport;
dglGetViewport(&viewport);
// Clear the objects viewport to the fog color. This is something of a dirty trick,
// since we want an identity projection matrix here...
glMatrixMode(GL_PROJECTION);
glPushMatrix();
state->setupObjectProjection(this);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
glEnable(GL_BLEND);
glBlendFunc(GL_ONE, GL_ZERO);
glColor3ub(U8(mRealFogColor.red), U8(mRealFogColor.green), U8(mRealFogColor.blue));
glBegin(GL_TRIANGLE_FAN);
glVertex3f(-1, -1, 1);
glVertex3f(-1, 1, 1);
glVertex3f( 1, 1, 1);
glVertex3f( 1, -1, 1);
glEnd();
glPopMatrix();
// On input. Finalize the projection matrix...
state->setupObjectProjection(this);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
Point3F camPos = state->getCameraPosition();
glTranslatef(camPos.x,camPos.y,camPos.z);
glEnable(GL_TEXTURE_2D);
glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_REPLACE);
glDepthMask(GL_FALSE);
render(state);
glDisable(GL_TEXTURE_2D);
glDisable(GL_BLEND);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
glDepthMask(GL_TRUE);
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
dglSetViewport(viewport);
AssertFatal(dglIsInCanonicalState(), "Error, GL not in canonical state on exit");
PROFILE_END();
}
//---------------------------------------------------------------------------
bool Sky::prepRenderImage(SceneState* state, const U32 stateKey,
const U32 startZone, const bool modifyBaseState)
{
startZone, modifyBaseState;
AssertFatal(modifyBaseState == false, "Error, should never be called with this parameter set");
AssertFatal(startZone == 0xFFFFFFFF, "Error, startZone should indicate -1");
if (isLastState(state, stateKey))
return false;
setLastState(state, stateKey);
// This should be sufficient for most objects that don't manage zones, and
// don't need to return a specialized RenderImage...
if (state->isObjectRendered(this)) {
SceneRenderImage* image = new SceneRenderImage;
image->obj = this;
image->sortType = SceneRenderImage::Sky;
state->insertRenderImage(image);
}
return false;
}
//---------------------------------------------------------------------------
void Sky::render(SceneState *state)
{
PROFILE_START(SkyRender);
F32 banHeights[2] = {-(mSpherePt.z-1),-(mSpherePt.z-1)};
F32 alphaBan[2] = {0.0f, 0.0f};
F32 depthInFog = 0.0f;
Point3F camPos;
S32 index=0;
if(gClientSceneGraph)
{
F32 currentVisDis = gClientSceneGraph->getVisibleDistanceMod();
if(mLastVisDisMod != currentVisDis)
{
calcPoints();
for(S32 i = 0; i < MAX_NUM_LAYERS; ++i)
mCloudLayer[i].setPoints();
mLastVisDisMod = currentVisDis;
}
}
if(mNumFogVolumes)
{
camPos = state->getCameraPosition();
depthInFog = -(camPos.z - mFogLine);
}
// Calculats alpha values and ban heights
if(depthInFog > 0.0f)
calcAlphas_Heights(camPos.z, banHeights, alphaBan, depthInFog);
else // Not in fog so setup default values
{
alphaBan[0] = 0.0f;
alphaBan[1] = 0.0f;
banHeights[0] = HORIZON;
banHeights[1] = banHeights[0] + OFFSET_HEIGHT;
}
// if lower ban is at top of box then no cliping plan is needed
if(banHeights[0] >= mSpherePt.z)
banHeights[0] = banHeights[1] = mSpherePt.z;
//Renders the 6 sides of the sky box
if(alphaBan[1] < 1.0f || mNumFogVolumes == 0)
renderSkyBox(banHeights[0], alphaBan[1]);
// if completly fogged out then no need to render
if(alphaBan[1] < 1.0f || depthInFog < 0.0f)
{
if(smCloudsOn && mStormCloudsOn && smSkyOn)
{
F32 ang = mAtan(banHeights[0],mSkyBoxPt.x);
F32 xyval = mSin(ang);
F32 zval = mCos(ang);
PlaneF planes[4];
planes[0] = PlaneF(xyval, 0.0f, zval, 0.0f);
planes[1] = PlaneF(-xyval, 0.0f, zval, 0.0f);
planes[2] = PlaneF(0.0f, xyval, zval, 0.0f);
planes[3] = PlaneF(0.0f, -xyval, zval, 0.0f);
S32 numRender = (smNumCloudsOn > mNumCloudLayers) ? mNumCloudLayers : smNumCloudsOn;
for(S32 x = 0; x < numRender; ++x)
mCloudLayer[x].render(Sim::getCurrentTime(), x, smCloudOutlineOn, mNumCloudLayers, planes);
}
if(!mNoRenderBans)
{
Point3F banPoints[2][MAX_BAN_POINTS];
Point3F cornerPoints[MAX_BAN_POINTS];
// Calculate upper, lower, and corner ban points
calcBans(banHeights, banPoints, cornerPoints);
glDisable(GL_TEXTURE_2D);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// Renders the side, top, and corner bans
renderBans(alphaBan, banHeights, banPoints, cornerPoints);
}
glDisable(GL_BLEND);
glEnable(GL_TEXTURE_2D);
}
if(mSetFog)
{
mSceneManager->setFogVolumes(mNumFogVolumes, mFogVolumes);
mSetFog = false;
}
if(mStormFogOn && mStormFogData.volume[mStormFogData.current].state != isDone)
updateFog();
if(mStormFogOn && mRealFog)
updateRealFog();
PROFILE_END();
}
//---------------------------------------------------------------------------
void Sky::calcAlphas_Heights(F32 zCamPos, F32 *banHeights,
F32 *alphaBan, F32 depthInFog)
{
F32 sideA, lower = 0.0f, upper = 0.0f;
F32 visValue, visDis = 0.0f;
F32 visRatio, ratioVal, setVis;
visDis = setVis = mVisibleDistance +
(mFogVolumes[mNumFogVolumes-1].visibleDistance - mVisibleDistance) *
mFogVolumes[mNumFogVolumes-1].percentage;
for(S32 x = 0; x < mNumFogVolumes-1; ++x)
{
F32 distance = mVisibleDistance +
(mFogVolumes[x].visibleDistance - mVisibleDistance) *
mFogVolumes[x].percentage;
if (distance < setVis)
{
visValue = (zCamPos < mFogVolumes[x].minHeight) ?
mFogVolumes[x].maxHeight - mFogVolumes[x].minHeight :
mFogVolumes[x].maxHeight - zCamPos;
if(visValue > 0.0f)
{
ratioVal = setVis / distance;
visRatio = visValue / distance;
visDis -= (distance * visRatio) * ratioVal;
}
}
}
//Calculate upper Height
if(visDis > 0.0f)
upper = (mSkyBoxPt.x*depthInFog)/(visDis * 0.2f);
banHeights[1] = mSpherePt.z;
if(upper < mSpherePt.z)
{
banHeights[1] = upper;
if(banHeights[1] < OFFSET_HEIGHT)
banHeights[1] = OFFSET_HEIGHT + HORIZON;
}
if(visDis > depthInFog)
{
sideA = mSqrt((visDis*visDis)-(depthInFog*depthInFog));
lower = (mSkyBoxPt.x*depthInFog)/sideA;
//Calculate lower Height
banHeights[0] = mSpherePt.z;
if(lower < mSpherePt.z)
banHeights[0] = lower;
if(banHeights[0] == mSpherePt.z && banHeights[1] == mSpherePt.z)
{
F32 temp = ((lower - mSpherePt.z) * (sideA/depthInFog));
if(temp <= mSkyBoxPt.x)
alphaBan[1] = temp/mSkyBoxPt.x;
else
alphaBan[1] = 1.0f;
alphaBan[0] = 1.0f;
}
else
{
alphaBan[0] = banHeights[0]/mSpherePt.z;
alphaBan[1] = 0.0f;
}
}
else
{
alphaBan[1] = alphaBan[0] = 1.0f;
banHeights[0] = banHeights[1] = mSpherePt.z;
}
banHeights[0] *= mFogVolumes[0].percentage;
banHeights[1] *= mFogVolumes[0].percentage;
if(banHeights[1] < OFFSET_HEIGHT)
banHeights[1] = OFFSET_HEIGHT + HORIZON;
}
void Sky::setRenderPoints(Point3F* renderPoints, S32 index)
{
renderPoints[0].set(mPoints[index].x, mPoints[index].y, mPoints[index].z);
renderPoints[1].set(mPoints[index+1].x, mPoints[index+1].y, mPoints[index+1].z);
renderPoints[2].set(mPoints[index+6].x, mPoints[index+6].y, mPoints[index+6].z);
renderPoints[3].set(mPoints[index+5].x, mPoints[index+5].y, mPoints[index+5].z);
}
void Sky::calcTexCoords(Point2F* texCoords, Point3F* renderPoints, S32 index)
{
for(S32 x = 0; x < 4; ++x)
texCoords[x].set(mTexCoord[x].x, mTexCoord[x].y);
S32 length = (S32)(mFabs(mPoints[index].z) + mFabs(mPoints[index + 5].z));
F32 per = mPoints[index].z - renderPoints[3].z;
texCoords[3].y = texCoords[2].y = (per / length);
}
//---------------------------------------------------------------------------
void Sky::renderSkyBox(F32 lowerBanHeight, F32 alphaBanUpper)
{
S32 side, index=0, val;
U32 numPoints;
Point3F renderPoints[4];
Point2F texCoords[4];
if(!mSkyTexturesOn || !smSkyOn)
{
glDisable(GL_TEXTURE_2D);
glColor3ub(mRealSkyColor.red, mRealSkyColor.green, mRealSkyColor.blue);
}
for(side = 0; side < ((mRenderBoxBottom) ? 6 : 5); ++side)
{
if((lowerBanHeight != mSpherePt.z || (side == 4 && alphaBanUpper < 1.0f)) && mSkyHandle[side])
{
glBindTexture(GL_TEXTURE_2D,mSkyHandle[side].getGLName());
if(side < 4)
{
numPoints = 4;
setRenderPoints(renderPoints, index);
if(!mNoRenderBans)
sgUtil_clipToPlane(renderPoints, numPoints, PlaneF(0.0f, 0.0f, 1.0f, -lowerBanHeight));
if(numPoints)
{
calcTexCoords(texCoords, renderPoints, index);
glBegin(GL_QUADS);
glTexCoord2f(texCoords[0].x, texCoords[0].y);
glVertex3f(renderPoints[0].x, renderPoints[0].y, renderPoints[0].z);
glTexCoord2f(texCoords[1].x, texCoords[1].y);
glVertex3f(renderPoints[1].x, renderPoints[1].y, renderPoints[1].z);
glTexCoord2f(texCoords[3].x, texCoords[3].y);
glVertex3f(renderPoints[2].x, renderPoints[2].y, renderPoints[2].z);
glTexCoord2f(texCoords[2].x, texCoords[2].y);
glVertex3f(renderPoints[3].x, renderPoints[3].y, renderPoints[3].z);
glEnd();
}
++index;
}
else
{
index = 3;
val = -1;
if(side == 5)
{
index = 5;
val = 1;
}
glBegin(GL_QUADS);
glTexCoord2f(mTexCoord[0].x, mTexCoord[0].y);
glVertex3f(mPoints[index].x, mPoints[index].y, mPoints[index].z);
glTexCoord2f(mTexCoord[1].x, mTexCoord[1].y);
glVertex3f(mPoints[index+(1*val)].x, mPoints[index+(1*val)].y, mPoints[index+(1*val)].z);
glTexCoord2f(mTexCoord[3].x, mTexCoord[3].y);
glVertex3f(mPoints[index+(2*val)].x, mPoints[index+(2*val)].y, mPoints[index+(2*val)].z);
glTexCoord2f(mTexCoord[2].x, mTexCoord[2].y);
glVertex3f(mPoints[index+(3*val)].x, mPoints[index+(3*val)].y, mPoints[index+(3*val)].z);
glEnd();
}
}
}
if(!mSkyTexturesOn)
glEnable(GL_TEXTURE_2D);
}
//---------------------------------------------------------------------------
void Sky::calcBans(F32 *banHeights, Point3F banPoints[][MAX_BAN_POINTS], Point3F *cornerPoints)
{
F32 incRad = RAD / F32(FOG_BAN_DETAIL*2);
MatrixF ban;
Point4F point;
S32 index, x;
F32 height = banHeights[0];
F32 value = banHeights[0] / mSkyBoxPt.z;
F32 mulVal = -(mSqrt(1-(value*value))); // lowerBan Multiple
index=0;
// Calculates the upper and lower bans
for(x=0; x < 2; ++x)
{
for(F32 angle=0.0f; angle <= RAD+incRad ; angle+=incRad)
{
ban.set(Point3F(0.0f, 0.0f, angle));
point.set(mulVal*mSkyBoxPt.x,0.0f,0.0f,1.0f);
ban.mul(point);
banPoints[x][index++].set(point.x,point.y,height);
}
height = banHeights[1];
value = banHeights[1] / mSkyBoxPt.x;
mulVal = -(mSqrt(1-(value*value))); // upperBan Multiple
index = 0;
}
// Calculates the filler points needed between the lower ban and the clipping plane
index = 2;
cornerPoints[0].set(mPoints[3].x, mPoints[3].y, banHeights[0]-1);
cornerPoints[1].set(mPoints[3].x, 0.0f, banHeights[0]-1);
for(x = 0; x < (FOG_BAN_DETAIL/2.0f) + 1.0f; ++x)
cornerPoints[index++].set(banPoints[0][x].x, banPoints[0][x].y, banPoints[0][x].z);
cornerPoints[index].set(0.0f, mPoints[3].y, banHeights[0]-1 );
}
//---------------------------------------------------------------------------
void Sky::renderBans(F32 *alphaBan, F32 *banHeights, Point3F banPoints[][MAX_BAN_POINTS], Point3F *cornerPoints)
{
S32 side, x, index = 0;
F32 angle;
U8 UalphaIn = U8(alphaBan[1]*255);
U8 UalphaOut = U8(alphaBan[0]*255);
//Renders the side bans
if(banHeights[0] < mSpherePt.z)
{
glBegin(GL_TRIANGLE_STRIP);
for(x=0;x<FOG_BAN_DETAIL*2+1;++x)
{
glColor4ub(U8(mRealFogColor.red), U8(mRealFogColor.green), U8(mRealFogColor.blue), 255);
glVertex3f(banPoints[0][index].x,banPoints[0][index].y,banPoints[0][index].z);
glColor4ub(U8(mRealFogColor.red), U8(mRealFogColor.green), U8(mRealFogColor.blue), UalphaOut);
glVertex3f(banPoints[1][index].x,banPoints[1][index].y,banPoints[1][index].z);
++index;
}
glEnd();
}
//Renders the top ban
glBegin(GL_TRIANGLE_FAN);
glColor4ub(U8(mRealFogColor.red), U8(mRealFogColor.green), U8(mRealFogColor.blue), UalphaIn);
glVertex3f(mTopCenterPt.x, mTopCenterPt.y, mTopCenterPt.z);
for(x=0;x<FOG_BAN_DETAIL*2+1;++x)
{
glColor4ub(U8(mRealFogColor.red), U8(mRealFogColor.green), U8(mRealFogColor.blue), UalphaOut);
glVertex3f(banPoints[1][x].x, banPoints[1][x].y, banPoints[1][x].z);
}
glEnd();
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
angle = 0.0f;
//Renders the filler
for(side=0;side<4;++side)
{
glRotatef(angle,0.0,0.0,1.0);
glBegin(GL_TRIANGLE_FAN);
for(x=0;x<FOG_BAN_DETAIL;++x)
{
glColor4ub(U8(mRealFogColor.red), U8(mRealFogColor.green), U8(mRealFogColor.blue), 255);
glVertex3f(cornerPoints[x].x, cornerPoints[x].y, cornerPoints[x].z);
}
glEnd();
angle += 90.0f;
}
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
}
//---------------------------------------------------------------------------
void Sky::startStorm()
{
mStormCloudsOn = true;
Cloud::startStorm(mStormCloudData.state);
for(int i = 0; i < mNumCloudLayers; ++i)
mCloudLayer[i].calcStorm(mStormCloudData.speed, mStormCloudData.fadeSpeed);
}
void Sky::stormCloudsShow(bool show)
{
mStormCloudsOn = show;
setMaskBits(StormCloudsOnMask);
}
void Sky::stormFogShow(bool show)
{
mStormFogOn = show;
setMaskBits(StormFogOnMask);
}
//---------------------------------------------------------------------------
void Sky::startStormFog()
{
if(mStormFogOn)
for(S32 x = 0; x < mNumFogVolumes; ++x)
mFogVolumes[x].percentage = mStormFogData.volume[x].endPercentage;
mStormFogOn = true;
if(mFogVolume < 0)
for(S32 x = 0; x < mNumFogVolumes; ++x)
{
mStormFogData.volume[x].speed = (mFogPercentage - mFogVolumes[x].percentage) / ((mStormFogData.time * 32.0f) / (F32)mNumFogVolumes);
mStormFogData.volume[x].state = (mStormFogData.volume[x].endPercentage > mFogPercentage) ? goingOut : comingIn;
mStormFogData.volume[x].endPercentage = mStormFogData.volume[x].active? mFogPercentage:
mFogVolumes[x].percentage;
if(mStormFogData.volume[x].state == comingIn)
mStormFogData.current = 0;
else
mStormFogData.current = mNumFogVolumes-1;
}
else if(mFogVolume < mNumFogVolumes)
{
mStormFogData.volume[mFogVolume].speed = (mFogPercentage - mFogVolumes[mFogVolume].percentage) / ((mStormFogData.time * 32.0f) / (F32)mNumFogVolumes);
mStormFogData.volume[mFogVolume].state = (mFogVolumes[mFogVolume].percentage > mFogPercentage) ? goingOut : comingIn;
mStormFogData.volume[mFogVolume].endPercentage = mFogPercentage;
mStormFogData.current = mFogVolume;
}
}
//---------------------------------------------------------------------------
void Sky::updateFog()
{
F32 overFlow, offset = 1.0f;
U32 currentTime = Sim::getCurrentTime();
if(mStormFogData.lastTime != 0)
offset = F32(currentTime - mStormFogData.lastTime) / 32.0f;
Con::printf("OFFSET: %g", offset);
mStormFogData.lastTime = currentTime;
mFogVolumes[mStormFogData.current].percentage += (mStormFogData.volume[mStormFogData.current].speed * offset);
do
{
//Con::printf("CURRENT: %d PERCENTAGE: %g TIME: %u",mStormFogData.current, mFogVolumes[mStormFogData.current].percentage, currentTime);
overFlow = 0.0f;
if(mStormFogData.volume[mStormFogData.current].state == comingIn && mFogVolumes[mStormFogData.current].percentage >= mStormFogData.volume[mStormFogData.current].endPercentage)
{
overFlow = mFogVolumes[mStormFogData.current].percentage - mStormFogData.volume[mStormFogData.current].endPercentage;
mFogVolumes[mStormFogData.current].percentage = mStormFogData.volume[mStormFogData.current].endPercentage;
mStormFogData.volume[mStormFogData.current].state = isDone;
if(++mStormFogData.current >= mNumFogVolumes)
{
mStormFogData.current -= 1;
mStormFogData.lastTime = 0;
mStormFogOn = false;
Con::printf("FOG IS DONE");
}
else
mFogVolumes[mStormFogData.current].percentage += overFlow;
}
else if(mStormFogData.volume[mStormFogData.current].state == goingOut && mFogVolumes[mStormFogData.current].percentage <= mStormFogData.volume[mStormFogData.current].endPercentage)
{
overFlow = mStormFogData.volume[mStormFogData.current].endPercentage - mFogVolumes[mStormFogData.current].percentage;
mFogVolumes[mStormFogData.current].percentage = mStormFogData.volume[mStormFogData.current].endPercentage;
mStormFogData.volume[mStormFogData.current].state = isDone;
if(--mStormFogData.current < 0)
{
mStormFogData.current += 1;
mStormFogData.lastTime = 0;
mStormFogOn = false;
Con::printf("FOG IS DONE");
}
else
mFogVolumes[mStormFogData.current].percentage -= overFlow;
}
} while(overFlow > 0.0f && mStormFogOn);
// if(mStormFogData.volume[mStormFogData.current].state != done)
mSceneManager->setFogVolumes(mNumFogVolumes, mFogVolumes);
}
//---------------------------------------------------------------------------
void Sky::updateRealFog()
{
for(S32 x = 0; x < mNumFogVolumes; ++x)
{
mFogVolumes[x].percentage += mStormFogData.volume[x].speed;
if((mStormFogData.volume[x].speed < 0.0f && mFogVolumes[x].percentage <= mStormFogData.volume[x].endPercentage) ||
(mStormFogData.volume[x].speed > 0.0f && mFogVolumes[x].percentage >= mStormFogData.volume[x].endPercentage))
{
mFogVolumes[x].percentage = mStormFogData.volume[x].endPercentage;
F32 save = mStormFogData.volume[x].lastPercentage;
mStormFogData.volume[x].lastPercentage = mStormFogData.volume[x].endPercentage;
mStormFogData.volume[x].endPercentage = save;
mStormFogData.volume[x].speed *= -1;
}
}
mSceneManager->setFogVolumes(mNumFogVolumes, mFogVolumes);
}
//---------------------------------------------------------------------------
void Sky::calcPoints()
{
S32 x, y, xval = 1, yval = -1, zval = 1;
F32 textureDem;
F32 visDisMod = mVisibleDistance;
if(gClientSceneGraph)
visDisMod = gClientSceneGraph->getVisibleDistanceMod();
mRadius = visDisMod * 0.95f;
Cloud::setRadius(mRadius);
Point3F tpt(1,1,1);
tpt.normalize(mRadius);
mPoints[0] = mPoints[4] = Point3F(-tpt.x, -tpt.y, tpt.z);
mPoints[5] = mPoints[9] = Point3F(-tpt.x, -tpt.y, -tpt.z);
for(x = 1; x < 4; ++x)
{
mPoints[x] = Point3F(tpt.x * xval, tpt.y * yval, tpt.z);
mPoints[x+5] = Point3F(tpt.x * xval, tpt.y * yval, -tpt.z);
if(yval > 0 && xval > 0)
xval *= -1;
if(yval < 0)
yval *= -1;
}
mFogLine = 0.0f;
for(x = 0; x < mNumFogVolumes; ++x)
mFogLine = (mFogVolumes[x].maxHeight > mFogLine) ? mFogVolumes[x].maxHeight : mFogLine;
textureDem = 512;
if(mSkyHandle[0])
textureDem = mSkyHandle[0].getWidth();
for(y = 0; y < 2 ; ++y)
for(x = 0; x < 2 ; ++x)
{
mTexCoord[x+(y*2)].set(x,y);
mTexCoord[x+(y*2)] *= (textureDem-1.0f)/textureDem;
mTexCoord[x+(y*2)] += Point2F(0.5 / textureDem, 0.5 / textureDem);
}
mSpherePt = mSkyBoxPt = mPoints[1];
mSpherePt.set(mSpherePt.x,0.0f,mSpherePt.z);
mSpherePt.normalize(mSkyBoxPt.x);
mTopCenterPt.set(0.0f,0.0f,mSkyBoxPt.z);
}
//---------------------------------------------------------------------------
bool Sky::loadDml()
{
char path[1024], *p;
dStrcpy(path, mMaterialListName);
if ((p = dStrrchr(path, '/')) != NULL)
*p = 0;
mNumCloudLayers = 0;
Stream *stream = ResourceManager->openStream(mMaterialListName);
if (stream==NULL)
{
#if defined(TORQUE_DEBUG)
Con::warnf("Sky material list is missing: %s", mMaterialListName);
#else
// ASSERT?? !!!!!!TBD
#endif
return false;
}
else
{// !!!!!TBD dhc - there's no fricking error checking here or in materialList.read!!!!
mMaterialList.read(*stream);
ResourceManager->closeStream(stream);
if(!mMaterialList.load(SkyTexture, path))
return false;
for(S32 x = 0; x < 6; ++x)
{
mMaterialList.getMaterial(x).setClamp(true);
mSkyHandle[x] = mMaterialList.getMaterial(x);
}
for(S32 x = 0; x < mMaterialList.size() - CloudMaterialOffset; ++x, ++mNumCloudLayers)
{
mMaterialList.getMaterial(x + CloudMaterialOffset).setClamp(false);
mCloudLayer[x].setTexture(mMaterialList.getMaterial(x + CloudMaterialOffset));
}
}
return true;
}
//---------------------------------------------------------------------------
void Sky::updateVisibility()
{
setVisibility();
setMaskBits(VisibilityMask);
}
//---------------------------------------------------------------------------
void Sky::stormCloudsOn(S32 state, F32 time)
{
mStormCloudData.state = (state) ? comingIn : goingOut;
mStormCloudData.time = time;
setMaskBits(StormCloudMask);
}
//---------------------------------------------------------------------------
void Sky::stormFogOn(F32 percentage, F32 time)
{
mStormFogData.time = time;
if(mStormFogData.endPercentage >= 0.0f)
{
mStormFogData.state = (mStormFogData.endPercentage > percentage) ? goingOut : comingIn;
mFogPercentage = mStormFogData.endPercentage;
}
else
mStormFogData.state = (mFogPercentage > percentage) ? goingOut : comingIn;
mStormFogData.endPercentage = percentage;
setMaskBits(StormFogMask);
}
//---------------------------------------------------------------------------
void Sky::stormRealFog(S32 value, F32 max, F32 min, F32 speed)
{
mRealFog = value;
mRealFogMax = max;
mRealFogMin = min;
mRealFogSpeed = speed;
setMaskBits(StormRealFogMask);
}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
// Cloud Code
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
Cloud::Cloud()
{
mDown=5;
mOver=1;
mBaseOffset.set(0, 0);
mTextureScale.set(1, 1);
mCenterHeight=0.5;
mInnerHeight=0.45;
mEdgeHeight=0.4;
mLastTime = 0;
mOffset=0;
mSpeed.set(1,1);
mGStormData.currentCloud = MAX_NUM_LAYERS;
mGStormData.fadeSpeed = 0.0f;
mGStormData.StormOn = false;
mGStormData.stormState = isDone;
for(int i = 0; i < 25; ++i)
stormAlpha[i] = 1.0f;
mRadius = 1.0f;
}
//---------------------------------------------------------------------------
Cloud::~Cloud()
{
}
//---------------------------------------------------------------------------
void Cloud::updateCoord()
{
mBaseOffset += mSpeed*mOffset;
if(mSpeed.x < 0)
mBaseOffset.x -= mCeil(mBaseOffset.x);
else
mBaseOffset.x -= mFloor(mBaseOffset.x);
if(mSpeed.y < 0)
mBaseOffset.y -= mCeil(mBaseOffset.y);
else
mBaseOffset.y -= mFloor(mBaseOffset.y);
}
//---------------------------------------------------------------------------
void Cloud::setHeights(F32 cHeight, F32 iHeight, F32 eHeight)
{
mCenterHeight = cHeight;
mInnerHeight = iHeight;
mEdgeHeight = eHeight;
}
//---------------------------------------------------------------------------
void Cloud::setTexture(TextureHandle textHand)
{
if(textHand)
{
mCloudHandle = textHand;
AssertFatal(bool(mCloudHandle) != false, "Error, couldn't load cloud layer bitmap");
}
}
//---------------------------------------------------------------------------
void Cloud::setSpeed(Point2F setSpeed)
{
mSpeed = setSpeed;
}
//---------------------------------------------------------------------------
void Cloud::setPoints()
{
S32 x, y;
F32 xyDiff = (mRadius - -mRadius)/4;
F32 cDis = mRadius*mCenterHeight,
upDis = mRadius*mInnerHeight,
edgeZ = mRadius*mEdgeHeight;
F32 zValue[25] = {edgeZ,edgeZ,edgeZ,edgeZ,edgeZ,edgeZ,upDis,upDis,upDis,edgeZ,edgeZ,upDis,cDis,upDis,edgeZ,edgeZ,upDis,upDis,upDis,edgeZ,edgeZ,edgeZ,edgeZ,edgeZ,edgeZ};
for(y = 0; y < 5; ++y)
for(x = 0; x < 5; ++x)
mPoints[y*5+x].set(-mRadius+(xyDiff*x),mRadius - (xyDiff*y),zValue[y*5+x]);
//Used to modify the corners of the cloud layers to make them one plane..
Point3F vec = (mPoints[5] + ((mPoints[1] - mPoints[5]) * 0.5f)) - mPoints[6];
mPoints[0] = mPoints[6] + (vec * 2.0f);
vec = (mPoints[9] + ((mPoints[3] - mPoints[9]) * 0.5f)) - mPoints[8];
mPoints[4] = mPoints[8] + (vec * 2.0f);
vec = (mPoints[21] + ((mPoints[15] - mPoints[21]) * 0.5f)) - mPoints[16];
mPoints[20] = mPoints[16] + (vec * 2.0f);
vec = (mPoints[23] + ((mPoints[19] - mPoints[23]) * 0.5f)) - mPoints[18];
mPoints[24] = mPoints[18] + (vec * 2.0f);
calcAlpha();
}
//---------------------------------------------------------------------------
void Cloud::calcAlpha()
{
for(S32 i = 0; i < 25; ++i)
{
mAlpha[i] = 1.3f - ((mPoints[i] - Point3F(0, 0, mPoints[i].z)).len())/mRadius;
if(mAlpha[i] < 0.4f)
mAlpha[i]=0.0f;
else if(mAlpha[i] > 0.8f)
mAlpha[i] = 1.0f;
}
}
//---------------------------------------------------------------------------
void Cloud::render(U32 currentTime, U32 cloudLayer, bool outlineOn, S32 numLayers, PlaneF* planes)
{
mGStormData.numCloudLayers = numLayers;
mOffset = 1.0f;
U32 numPoints;
Point3F renderPoints[128];
Point2F renderTexPoints[128];
F32 renderAlpha[128];
F32 renderSAlpha[128];
if(mLastTime != 0)
mOffset = (currentTime - mLastTime)/32.0f;
mLastTime=currentTime;
if(!mCloudHandle || (mGStormData.StormOn && mGStormData.currentCloud < cloudLayer))
return;
S32 start=0, i, j, k;
updateCoord();
for(S32 x = 0; x < 5; x++)
for(S32 y = 0; y < 5; y++)
mTexCoords[y * 5 + x].set ( x * mTextureScale.x + mBaseOffset.x,
y * mTextureScale.y - mBaseOffset.y);
if(mGStormData.StormOn && mGStormData.currentCloud == cloudLayer)
updateStorm();
if(!outlineOn)
{
glBindTexture(GL_TEXTURE_2D, mCloudHandle.getGLName());
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}
for(i = 0; i < 4; ++i)
{
start = i * 5;
for(j = 0; j < 4; ++j )
{
numPoints = 4;
setRenderPoints(renderPoints, renderTexPoints, renderAlpha, renderSAlpha, start);
for(S32 i = 0; i < 4; ++i)
clipToPlane(renderPoints, renderTexPoints, renderAlpha, renderSAlpha,
numPoints, planes[i]);
if(!outlineOn)
glBegin(GL_TRIANGLE_FAN);
else
glBegin(GL_LINE_LOOP);
for(k = 0; k < numPoints; ++k)
{
glColor4f(1.0,1.0,1.0, renderAlpha[k]*renderSAlpha[k]);
glTexCoord2f(renderTexPoints[k].x,renderTexPoints[k].y);
glVertex3f(renderPoints[k].x,renderPoints[k].y,renderPoints[k].z);
}
glEnd();
++start;
}
}
glDisable(GL_BLEND);
}
void Cloud::setRenderPoints(Point3F* renderPoints, Point2F* renderTexPoints,
F32* renderAlpha, F32* renderSAlpha, S32 index)
{
S32 offset[4] = {0,5,6,1};
for(S32 x = 0; x < 4; ++x)
{
renderPoints[x].set(mPoints[index+offset[x]].x, mPoints[index+offset[x]].y, mPoints[index+offset[x]].z);
renderTexPoints[x].set(mTexCoords[index+offset[x]].x, mTexCoords[index+offset[x]].y);
renderAlpha[x] = mAlpha[index+offset[x]];
renderSAlpha[x] = stormAlpha[index+offset[x]];
}
}
//---------------------------------------------------------------------------
void Cloud::setTextPer(F32 cloudTextPer)
{
mTextureScale.set(cloudTextPer / 4.0, cloudTextPer / 4.0);
}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
// Storm Code
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
void Cloud::updateStorm()
{
if(!mGStormData.FadeOut && !mGStormData.FadeIn) {
alphaCenter += (stormUpdate * mOffset);
F32 update, center;
if(mGStormData.stormDir == 'x') {
update = stormUpdate.x;
center = alphaCenter.x;
}
else {
update = stormUpdate.y;
center = alphaCenter.y;
}
if(mGStormData.stormState == comingIn) {
if((update > 0 && center > 0) || (update < 0 && center < 0))
mGStormData.FadeIn = true;
}
else
if((update > 0 && center > mRadius*2) || (update < 0 && center < -mRadius*2)) {
// Con::printf("Cloud %d is done.", mGStormData.currentCloud);
mGStormData.StormOn = --mGStormData.currentCloud >= 0;
if(mGStormData.StormOn) {
mGStormData.FadeOut = true;
return;
}
}
}
calcStormAlpha();
}
//---------------------------------------------------------------------------
void Cloud::calcStormAlpha()
{
if(mGStormData.FadeIn)
{
bool done = true;
for(int i = 0; i < 25; ++i)
{
stormAlpha[i] += (mGStormData.fadeSpeed * mOffset);
if(stormAlpha[i] >= 1.0f)
stormAlpha[i] = 1.0f;
else
done = false;
}
if(done)
{
// Con::printf("Cloud %d is done.", mGStormData.currentCloud);
mGStormData.StormOn = ++mGStormData.currentCloud < mGStormData.numCloudLayers;
mGStormData.FadeIn = false;
}
}
else if(mGStormData.FadeOut)
{
bool done = true;
for(int i = 0; i < 25; ++i)
{
stormAlpha[i] -= (mGStormData.fadeSpeed * mOffset);
if(stormAlpha[i] <= mAlphaSave[i])
stormAlpha[i] = mAlphaSave[i];
else
done = false;
}
if(done)
mGStormData.FadeOut = false;
}
else
for(int i = 0; i < 25; ++i)
{
stormAlpha[i] = 1.0f -((Point3F(mPoints[i].x-alphaCenter.x, mPoints[i].y-alphaCenter.y, mPoints[i].z).len())/mRadius);
if(stormAlpha[i] < 0.0f)
stormAlpha[i]=0.0f;
else if(stormAlpha[i] > 1.0f)
stormAlpha[i] = 1.0f;
}
}
//---------------------------------------------------------------------------
void Cloud::calcStorm(F32 speed, F32 fadeSpeed)
{
float tempX, tempY;
float windSlop = 0.0f;
if(mSpeed.x != 0)
windSlop = mSpeed.y/mSpeed.x;
tempX = (mSpeed.x < 0) ? -mSpeed.x : mSpeed.x;
tempY = (mSpeed.y < 0) ? -mSpeed.y : mSpeed.y;
if(tempX >= tempY)
{
alphaCenter.x =(mSpeed.x < 0) ? mRadius * -2 : mRadius * 2;
alphaCenter.y = windSlop*alphaCenter.x;
stormUpdate.x = alphaCenter.x > 0.0f ? -speed : speed;
stormUpdate.y = alphaCenter.y > 0.0f ? -speed * windSlop : speed * windSlop;
mGStormData.stormDir = 'x';
}
else
{
alphaCenter.y = (mSpeed.y < 0) ? mRadius * 2 : mRadius * -2;
alphaCenter.x = windSlop * alphaCenter.y;
/* if(windSlop != 0)
alphaCenter.x = (1/windSlop)*alphaCenter.y;
else
alphaCenter.x = 0.0f;
*/
stormUpdate.y = alphaCenter.y > 0.0f ? -speed : speed;
stormUpdate.x = alphaCenter.x > 0.0f ? -speed * (1/windSlop) : speed * (1/windSlop);
mGStormData.stormDir = 'y';
}
mGStormData.fadeSpeed = fadeSpeed;
for(int i = 0; i < 25; ++i)
{
mAlphaSave[i] = 1.0f - (mPoints[i].len()/mRadius);
if(mAlphaSave[i] < 0.0f)
mAlphaSave[i]=0.0f;
else if(mAlphaSave[i] > 1.0f)
mAlphaSave[i] = 1.0f;
}
if(mGStormData.stormState == goingOut)
alphaCenter.set(0.0f, 0.0f);
}
//---------------------------------------------------------------------------
void Cloud::startStorm(SkyState state)
{
mGStormData.StormOn = true;
mGStormData.stormState = state;
if(state == goingOut)
{
mGStormData.FadeOut= true;
mGStormData.FadeIn = false;
mGStormData.currentCloud = mGStormData.numCloudLayers - 1;
}
else
{
mGStormData.FadeIn = false;
mGStormData.FadeOut= false;
mGStormData.currentCloud = 0;
}
}
void Cloud::clipToPlane(Point3F* points, Point2F* texPoints, F32* alphaPoints,
F32* sAlphaPoints, 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;
}
U32 numFinalPoints = 0;
Point3F finalPoints[128];
Point2F finalTexPoints[128];
F32 finalAlpha[128];
F32 finalSAlpha[128];
U32 baseStart = start;
U32 end = (start + 1) % rNumPoints;
while (end != baseStart) {
const Point3F& rStartPoint = points[start];
const Point3F& rEndPoint = points[end];
const Point2F& rStartTexPoint = texPoints[start];
const Point2F& rEndTexPoint = texPoints[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
//Alpha
finalAlpha[numFinalPoints] = alphaPoints[start];
finalSAlpha[numFinalPoints] = sAlphaPoints[start];
//Points
finalPoints[numFinalPoints] = points[start];
finalTexPoints[numFinalPoints++] = texPoints[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...
//Alpha
finalAlpha[numFinalPoints] = alphaPoints[start];
finalSAlpha[numFinalPoints] = sAlphaPoints[start];
//Points
finalPoints[numFinalPoints] = points[start];
finalTexPoints[numFinalPoints++] = texPoints[start];
Point3F vector = rEndPoint - rStartPoint;
F32 t = -(rPlane.distToPlane(rStartPoint) / mDot(rPlane, vector));
//Alpha
finalAlpha[numFinalPoints] = alphaPoints[start]+ ((alphaPoints[end] - alphaPoints[start]) * t);
finalSAlpha[numFinalPoints] = sAlphaPoints[start]+ ((sAlphaPoints[end] - sAlphaPoints[start]) * t);
//Polygon Points
Point3F intersection = rStartPoint + (vector * t);
finalPoints[numFinalPoints] = intersection;
//Texture Points
Point2F texVec = rEndTexPoint - rStartTexPoint;
Point2F texIntersection = rStartTexPoint + (texVec * t);
finalTexPoints[numFinalPoints++] = texIntersection;
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];
const Point2F& rNewStartTexPoint = texPoints[start];
const Point2F& rNewEndTexPoint = texPoints[end];
vector = rNewEndPoint - rNewStartPoint;
t = -(rPlane.distToPlane(rNewStartPoint) / mDot(rPlane, vector));
//Alpha
alphaPoints[start] = alphaPoints[start]+ ((alphaPoints[end] - alphaPoints[start]) * t);
sAlphaPoints[start] = sAlphaPoints[start]+ ((sAlphaPoints[end] - sAlphaPoints[start]) * t);
//Polygon Points
intersection = rNewStartPoint + (vector * t);
points[start] = intersection;
//Texture Points
texVec = rNewEndTexPoint - rNewStartTexPoint;
texIntersection = rNewStartTexPoint + (texVec * t);
texPoints[start] = texIntersection;
}
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...
//
//Alpha
finalAlpha[numFinalPoints] = alphaPoints[start];
finalSAlpha[numFinalPoints] = sAlphaPoints[start];
//Points
finalPoints[numFinalPoints] = points[start];
finalTexPoints[numFinalPoints++] = texPoints[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];
const Point2F& rNewStartTexPoint = texPoints[start];
const Point2F& rNewEndTexPoint = texPoints[end];
Point3F vector = rNewEndPoint - rNewStartPoint;
F32 t = -(rPlane.distToPlane(rNewStartPoint) / mDot(rPlane, vector));
//Alpha
alphaPoints[start] = alphaPoints[start] + ((alphaPoints[end] - alphaPoints[start]) * t);
sAlphaPoints[start] = sAlphaPoints[start] + ((sAlphaPoints[end] - sAlphaPoints[start]) * t);
//Polygon Points
Point3F intersection = rNewStartPoint + (vector * t);
points[start] = intersection;
//Texture Points
Point2F texVec = rNewEndTexPoint - rNewStartTexPoint;
Point2F texIntersection = rNewStartTexPoint + (texVec * t);
texPoints[start] = texIntersection;
}
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.
//Alpha
finalAlpha[numFinalPoints] = alphaPoints[start];
finalSAlpha[numFinalPoints] = sAlphaPoints[start];
//Points
finalPoints[numFinalPoints] = points[start];
finalTexPoints[numFinalPoints++] = texPoints[start];
AssertFatal(numFinalPoints >= 3, avar("Error, this shouldn't happen! Invalid winding in clipToPlane: %d", numFinalPoints));
// Copy the new rWinding, and we're set!
//Alpha
dMemcpy(alphaPoints, finalAlpha, numFinalPoints * sizeof(F32));
dMemcpy(sAlphaPoints, finalSAlpha, numFinalPoints * sizeof(F32));
//Points
dMemcpy(points, finalPoints, numFinalPoints * sizeof(Point3F));
dMemcpy(texPoints, finalTexPoints, numFinalPoints * sizeof(Point2F));
rNumPoints = numFinalPoints;
AssertISV(rNumPoints <= 128, "MaxWindingPoints exceeded in scenegraph. Fatal error.");
}