//----------------------------------------------------------------------------- // 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 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."); }