From 18a139cd66dada5a42b93a7f26bb46c7929fa744 Mon Sep 17 00:00:00 2001 From: Jason Hooks Date: Sat, 24 Mar 2012 22:03:08 -0400 Subject: [PATCH] adding up and down move buttons --- apps/openmw/mwclass/npc.cpp | 5 +- apps/openmw/mwinput/inputmanager.cpp | 16 + apps/openmw/mwmechanics/movement.hpp | 3 +- apps/openmw/mwrender/objects.cpp | 2 + apps/openmw/mwworld/physicssystem.cpp | 15 +- apps/openmw/mwworld/player.cpp | 9 + apps/openmw/mwworld/player.hpp | 1 + apps/openmw/physicssystem.cpp | 225 +++++++ libs/openengine/bullet/physic.cpp | 4 +- libs/openengine/bullet/pmove.cpp | 27 +- libs/openengine/bullet/pmove.h | 4 +- libs/openengine/bullet/trace.cpp | 4 +- libs/openengine/bullet/weather.cpp | 811 ++++++++++++++++++++++++++ libs/openengine/bullet/weather.hpp | 272 +++++++++ 14 files changed, 1374 insertions(+), 24 deletions(-) create mode 100644 apps/openmw/physicssystem.cpp create mode 100644 libs/openengine/bullet/weather.cpp create mode 100644 libs/openengine/bullet/weather.hpp diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 83a94d27d1..522fce3a3c 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -269,8 +269,9 @@ namespace MWClass { Ogre::Vector3 vector (0, 0, 0); - vector.x = - getMovementSettings (ptr).mLeftRight * 200; - vector.y = getMovementSettings (ptr).mForwardBackward * 200; + vector.x = - getMovementSettings (ptr).mLeftRight * 127; + vector.y = getMovementSettings (ptr).mForwardBackward * 127; + vector.z = getMovementSettings(ptr).mUpDown * 127; if (getStance (ptr, Run, false)) vector *= 2; diff --git a/apps/openmw/mwinput/inputmanager.cpp b/apps/openmw/mwinput/inputmanager.cpp index 88534dddae..074f8a906a 100644 --- a/apps/openmw/mwinput/inputmanager.cpp +++ b/apps/openmw/mwinput/inputmanager.cpp @@ -60,6 +60,7 @@ namespace MWInput A_CycleWeaponRight, A_ToggleSneak, //Toggles Sneak, add Push-Sneak later A_ToggleWalk, //Toggle Walking/Running + A_Crouch, A_QuickSave, A_QuickLoad, @@ -259,6 +260,9 @@ namespace MWInput poller.bind(A_MoveRight, KC_D); poller.bind(A_MoveForward, KC_W); poller.bind(A_MoveBackward, KC_S); + + poller.bind(A_Jump, KC_E); + poller.bind(A_Crouch, KC_LCONTROL); } //NOTE: Used to check for movement keys @@ -306,6 +310,18 @@ namespace MWInput else player.setForwardBackward (0); + if (poller.isDown(A_Jump)) + { + + player.setUpDown (1); + } + else if (poller.isDown(A_Crouch)) + { + player.setUpDown (-1); + } + else + player.setUpDown (0); + return true; } diff --git a/apps/openmw/mwmechanics/movement.hpp b/apps/openmw/mwmechanics/movement.hpp index a555ac010a..11eb83151e 100644 --- a/apps/openmw/mwmechanics/movement.hpp +++ b/apps/openmw/mwmechanics/movement.hpp @@ -8,8 +8,9 @@ namespace MWMechanics { signed char mLeftRight; // 1: wants to move left, -1: wants to move right signed char mForwardBackward; // 1:wants to move forward, -1: wants to move backward + signed char mUpDown; - Movement() : mLeftRight (0), mForwardBackward (0) {} + Movement() : mLeftRight (0), mForwardBackward (0), mUpDown(0) {} }; } diff --git a/apps/openmw/mwrender/objects.cpp b/apps/openmw/mwrender/objects.cpp index 717064ada5..0d19a30130 100644 --- a/apps/openmw/mwrender/objects.cpp +++ b/apps/openmw/mwrender/objects.cpp @@ -56,8 +56,10 @@ void Objects::insertBegin (const MWWorld::Ptr& ptr, bool enabled, bool static_) Ogre::SceneNode* insert = cellnode->createChildSceneNode(); const float *f = ptr.getRefData().getPosition().pos; + insert->setPosition(f[0], f[1], f[2]); insert->setScale(ptr.getCellRef().scale, ptr.getCellRef().scale, ptr.getCellRef().scale); + // Convert MW rotation to a quaternion: f = ptr.getCellRef().pos.rot; diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index 11a43c7d30..d54f4696a2 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -103,7 +103,7 @@ namespace MWWorld playerphysics->ps.viewangles.x = 0; playerphysics->ps.viewangles.z = 0; - playerphysics->ps.viewangles.y = both.getYaw().valueDegrees() *-1 + 90; + playerphysics->ps.viewangles.y = both.getYaw().valueDegrees() *-1 + 90; //playerphysics->ps.viewangles.z = both.getPitch().valueDegrees(); @@ -113,6 +113,7 @@ namespace MWWorld Ogre::Vector3 dir1(iter->second.x,iter->second.z,-iter->second.y); pm_ref.rightmove = -dir1.x; pm_ref.forwardmove = dir1.z; + pm_ref.upmove = dir1.y; @@ -127,6 +128,7 @@ namespace MWWorld Ogre::Vector3 dir1(iter->second.x,iter->second.z,-iter->second.y); pm_ref.rightmove = -dir1.x; pm_ref.forwardmove = dir1.z; + pm_ref.upmove = dir.y; dir = 0.025*(quat*dir1); } @@ -135,7 +137,7 @@ namespace MWWorld //set the walk direction act->setWalkDirection(btVector3(dir.x,-dir.z,dir.y)); } - //mEngine->stepSimulation(duration); + mEngine->stepSimulation(duration); Pmove(playerphysics); std::vector< std::pair > response; for(std::map::iterator it = mEngine->PhysicActorMap.begin(); it != mEngine->PhysicActorMap.end();it++) @@ -147,7 +149,7 @@ namespace MWWorld coord = playerphysics->ps.origin; //std::cout << "Coord" << coord << "\n"; - coord = Ogre::Vector3(coord.x, coord.z, coord.y); //x, z, -y + //coord = Ogre::Vector3(coord.x, coord.z, coord.y); //x, z, -y } response.push_back(std::pair(it->first, coord)); @@ -218,6 +220,10 @@ namespace MWWorld bool PhysicsSystem::toggleCollisionMode() { + if(playerphysics->ps.move_type==PM_NOCLIP) + playerphysics->ps.move_type=PM_NORMAL; + else + playerphysics->ps.move_type=PM_NOCLIP; for(std::map::iterator it = mEngine->PhysicActorMap.begin(); it != mEngine->PhysicActorMap.end();it++) { if (it->first=="player") @@ -248,7 +254,10 @@ namespace MWWorld } void PhysicsSystem::insertObjectPhysics(const MWWorld::Ptr& ptr, const std::string model){ + Ogre::SceneNode* node = ptr.getRefData().getBaseNode(); + Ogre::Vector3 objPos = node->getPosition(); + addObject (node->getName(), model, node->getOrientation(), node->getScale().x, node->getPosition()); } diff --git a/apps/openmw/mwworld/player.cpp b/apps/openmw/mwworld/player.cpp index 5bfb82138c..884a72c3a6 100644 --- a/apps/openmw/mwworld/player.cpp +++ b/apps/openmw/mwworld/player.cpp @@ -80,6 +80,14 @@ namespace MWWorld MWWorld::Class::get (ptr).getMovementSettings (ptr).mForwardBackward = value; } + void Player::setUpDown(int value) + { + MWWorld::Ptr ptr = getPlayer(); + + + + MWWorld::Class::get (ptr).getMovementSettings (ptr).mUpDown = value; + } void Player::toggleRunning() { @@ -89,4 +97,5 @@ namespace MWWorld MWWorld::Class::get (ptr).setStance (ptr, MWWorld::Class::Run, !running); } + } diff --git a/apps/openmw/mwworld/player.hpp b/apps/openmw/mwworld/player.hpp index 01c71da437..da74fe6ded 100644 --- a/apps/openmw/mwworld/player.hpp +++ b/apps/openmw/mwworld/player.hpp @@ -111,6 +111,7 @@ namespace MWWorld void setLeftRight (int value); void setForwardBackward (int value); + void setUpDown(int value); void toggleRunning(); }; diff --git a/apps/openmw/physicssystem.cpp b/apps/openmw/physicssystem.cpp new file mode 100644 index 0000000000..a07358f8ee --- /dev/null +++ b/apps/openmw/physicssystem.cpp @@ -0,0 +1,225 @@ +#include + +#include "physicssystem.hpp" +#include "../mwworld/ptr.hpp" +#include "../mwworld/world.hpp" // FIXME +#include + +#include "OgreRoot.h" +#include "OgreRenderWindow.h" +#include "OgreSceneManager.h" +#include "OgreViewport.h" +#include "OgreCamera.h" +#include "OgreTextureManager.h" + + +using namespace Ogre; +namespace MWWorld +{ + + PhysicsSystem::PhysicsSystem(OEngine::Render::OgreRenderer &_rend) : + mRender(_rend), mEngine(0), mFreeFly (true) + { + // Create physics. shapeLoader is deleted by the physic engine + NifBullet::ManualBulletShapeLoader* shapeLoader = new NifBullet::ManualBulletShapeLoader(); + mEngine = new OEngine::Physic::PhysicEngine(shapeLoader); + } + + PhysicsSystem::~PhysicsSystem() + { + delete mEngine; + + } + OEngine::Physic::PhysicEngine* PhysicsSystem::getEngine() + { + return mEngine; + } + + std::pair PhysicsSystem::getFacedHandle (MWWorld::World& world) + { + std::string handle = ""; + + //get a ray pointing to the center of the viewport + Ray centerRay = mRender.getCamera()->getCameraToViewportRay( + mRender.getViewport()->getWidth()/2, + mRender.getViewport()->getHeight()/2); + //let's avoid the capsule shape of the player. + centerRay.setOrigin(centerRay.getOrigin() + 20*centerRay.getDirection()); + btVector3 from(centerRay.getOrigin().x,-centerRay.getOrigin().z,centerRay.getOrigin().y); + btVector3 to(centerRay.getPoint(500).x,-centerRay.getPoint(500).z,centerRay.getPoint(500).y); + + return mEngine->rayTest(from,to); + } + + bool PhysicsSystem::castRay(const Vector3& from, const Vector3& to) + { + btVector3 _from, _to; + _from = btVector3(from.x, from.y, from.z); + _to = btVector3(to.x, to.y, to.z); + + std::pair result = mEngine->rayTest(_from, _to); + + return !(result.first == ""); + } + + + std::vector< std::pair > PhysicsSystem::doPhysics (float duration, + const std::vector >& actors) + { + //set the DebugRenderingMode. To disable it,set it to 0 + //eng->setDebugRenderingMode(1); + + //set the walkdirection to 0 (no movement) for every actor) + for(std::map::iterator it = mEngine->PhysicActorMap.begin(); it != mEngine->PhysicActorMap.end();it++) + { + OEngine::Physic::PhysicActor* act = it->second; + act->setWalkDirection(btVector3(0,0,0)); + } + + for (std::vector >::const_iterator iter (actors.begin()); + iter!=actors.end(); ++iter) + { + OEngine::Physic::PhysicActor* act = mEngine->getCharacter(iter->first); + + //dirty stuff to get the camera orientation. Must be changed! + + Ogre::SceneNode *sceneNode = mRender.getScene()->getSceneNode (iter->first); + Ogre::Vector3 dir; + Ogre::Node* yawNode = sceneNode->getChildIterator().getNext(); + Ogre::Node* pitchNode = yawNode->getChildIterator().getNext(); + if(mFreeFly) + { + Ogre::Quaternion yawQuat = yawNode->getOrientation(); + Ogre::Quaternion pitchQuat = pitchNode->getOrientation(); + Ogre::Vector3 dir1(iter->second.x,iter->second.z,-iter->second.y); + dir = 0.07*(yawQuat*pitchQuat*dir1); + } + else + { + Ogre::Quaternion quat = yawNode->getOrientation(); + Ogre::Vector3 dir1(iter->second.x,iter->second.z,-iter->second.y); + dir = 0.025*(quat*dir1); + } + + //set the walk direction + act->setWalkDirection(btVector3(dir.x,-dir.z,dir.y)); + } + mEngine->stepSimulation(duration); + + std::vector< std::pair > response; + for(std::map::iterator it = mEngine->PhysicActorMap.begin(); it != mEngine->PhysicActorMap.end();it++) + { + btVector3 newPos = it->second->getPosition(); + Ogre::Vector3 coord(newPos.x(), newPos.y(), newPos.z()); + + response.push_back(std::pair(it->first, coord)); + } + return response; + } + + void PhysicsSystem::addObject (const std::string& handle, const std::string& mesh, + const Ogre::Quaternion& rotation, float scale, const Ogre::Vector3& position) + { + OEngine::Physic::RigidBody* body = mEngine->createRigidBody(mesh,handle,scale); + mEngine->addRigidBody(body); + btTransform tr; + tr.setOrigin(btVector3(position.x,position.y,position.z)); + std::cout << "Position object:" << position << "\n"; + tr.setRotation(btQuaternion(rotation.x,rotation.y,rotation.z,rotation.w)); + body->setWorldTransform(tr); + } + + void PhysicsSystem::addActor (const std::string& handle, const std::string& mesh, + const Ogre::Vector3& position) + { + //TODO:optimize this. Searching the std::map isn't very efficient i think. + mEngine->addCharacter(handle); + OEngine::Physic::PhysicActor* act = mEngine->getCharacter(handle); + act->setPosition(btVector3(position.x,position.y,position.z)); + } + + void PhysicsSystem::removeObject (const std::string& handle) + { + //TODO:check if actor??? + mEngine->removeCharacter(handle); + mEngine->removeRigidBody(handle); + mEngine->deleteRigidBody(handle); + } + + void PhysicsSystem::moveObject (const std::string& handle, const Ogre::Vector3& position) + { + if (OEngine::Physic::RigidBody* body = mEngine->getRigidBody(handle)) + { + // TODO very dirty hack to avoid crash during setup -> needs cleaning up to allow + // start positions others than 0, 0, 0 + btTransform tr = body->getWorldTransform(); + tr.setOrigin(btVector3(position.x,position.y,position.z)); + body->setWorldTransform(tr); + } + if (OEngine::Physic::PhysicActor* act = mEngine->getCharacter(handle)) + { + // TODO very dirty hack to avoid crash during setup -> needs cleaning up to allow + // start positions others than 0, 0, 0 + act->setPosition(btVector3(position.x,position.y,position.z)); + } + } + + void PhysicsSystem::rotateObject (const std::string& handle, const Ogre::Quaternion& rotation) + { + if (OEngine::Physic::PhysicActor* act = mEngine->getCharacter(handle)) + { + // TODO very dirty hack to avoid crash during setup -> needs cleaning up to allow + // start positions others than 0, 0, 0 + act->setRotation(btQuaternion(rotation.x, rotation.y, rotation.z, rotation.w)); + } + } + + void PhysicsSystem::scaleObject (const std::string& handle, float scale) + { + + } + + bool PhysicsSystem::toggleCollisionMode() + { + for(std::map::iterator it = mEngine->PhysicActorMap.begin(); it != mEngine->PhysicActorMap.end();it++) + { + if (it->first=="player") + { + OEngine::Physic::PhysicActor* act = it->second; + + bool cmode = act->getCollisionMode(); + if(cmode) + { + act->enableCollisions(false); + act->setGravity(0.); + act->setVerticalVelocity(0); + mFreeFly = true; + return false; + } + else + { + mFreeFly = false; + act->enableCollisions(true); + act->setGravity(4.); + act->setVerticalVelocity(0); + return true; + } + } + } + + throw std::logic_error ("can't find player"); + } + + void PhysicsSystem::insertObjectPhysics(const MWWorld::Ptr& ptr, const std::string model){ + Ogre::SceneNode* node = ptr.getRefData().getBaseNode(); + addObject (node->getName(), model, node->getOrientation(), + node->getScale().x, node->getPosition()); + } + + void PhysicsSystem::insertActorPhysics(const MWWorld::Ptr& ptr, const std::string model){ + Ogre::SceneNode* node = ptr.getRefData().getBaseNode(); + // std::cout << "Adding node with name" << node->getName(); + addActor (node->getName(), model, node->getPosition()); + } + +} diff --git a/libs/openengine/bullet/physic.cpp b/libs/openengine/bullet/physic.cpp index 07bad30535..911f6279bd 100644 --- a/libs/openengine/bullet/physic.cpp +++ b/libs/openengine/bullet/physic.cpp @@ -156,10 +156,10 @@ namespace Physic solver = new btSequentialImpulseConstraintSolver; //TODO: memory leak? - btOverlappingPairCache* pairCache = new btSortedOverlappingPairCache(); + //btOverlappingPairCache* pairCache = new btSortedOverlappingPairCache(); //pairCache->setInternalGhostPairCallback( new btGhostPairCallback() ); - broadphase = new btDbvtBroadphase(pairCache); + broadphase = new btDbvtBroadphase(); // The world. dynamicsWorld = new btDiscreteDynamicsWorld(dispatcher,broadphase,solver,collisionConfiguration); diff --git a/libs/openengine/bullet/pmove.cpp b/libs/openengine/bullet/pmove.cpp index 45fe84f4fc..71c15fe45f 100644 --- a/libs/openengine/bullet/pmove.cpp +++ b/libs/openengine/bullet/pmove.cpp @@ -786,7 +786,7 @@ static void PM_WaterMove( playerMove* const pm ) wishvel = pml.forward * scale * pm->cmd.forwardmove + pml.right * scale * pm->cmd.rightmove; //wishvel[2] += scale * pm->cmd.upmove; - wishvel.y += pm->cmd.upmove * scale; + wishvel.z += pm->cmd.upmove * scale; } //VectorCopy (wishvel, wishdir); @@ -1094,21 +1094,22 @@ void AngleVectors( const Ogre::Vector3& angles, Ogre::Vector3* const forward, Og if (forward) { forward->x = cp * cy; - forward->z = cp * sy; - forward->y = -sp; + forward->y = cp * sy; + forward->z = -sp; } if (right) { right->x = (-1 * sr * sp * cy + -1 * cr * -sy); - right->z = (-1 * sr * sp * sy + -1 * cr * cy); - right->y = 0.0f;//-1 * sp * cp; + right->y = (-1 * sr * sp * sy + -1 * cr * cy); + right->z = 0.0f;//-1 * sp * cp; } if (up) { - up->x = (cr * sp * cy + -sr * -sy); - up->z = (cr * sp * sy + -sr * cy); - up->y = cr * cp; + up->x =(cr * sp * cy + -sr * -sy); + up->y=(cr * sp * sy + -sr * cy); + up->z = cr * cp; } + } void PM_GroundTraceMissed() @@ -1356,7 +1357,7 @@ static void PM_CrashLand( void ) static void PM_GroundTrace( void ) { - std::cout << "Ground trace\n"; + //std::cout << "Ground trace\n"; Ogre::Vector3 point; traceResults trace; @@ -1578,9 +1579,12 @@ static void PM_NoclipMove( void ) //for (i=0 ; i<3 ; i++) //wishvel[i] = pml.forward[i] * fmove + pml.right[i] * smove; + std::cout << "Forward" << pml.forward << "\n"; + std::cout << "Right" << pml.right << "\n"; + std::cout << "Up" << pml.up << "\n"; wishvel = pml.forward * fmove + pml.right * smove; //wishvel[2] += pm->cmd.upmove; - wishvel.y += pm->cmd.upmove; + wishvel.z += pm->cmd.upmove; //VectorCopy (wishvel, wishdir); wishdir = wishvel; @@ -1720,7 +1724,6 @@ void PM_SetWaterLevel( playerMove* const pm ) void PmoveSingle (playerMove* const pmove) { - std::cout << "Pmove single\n"; //pm = pmove; // Aedra doesn't support Q3-style VM traps D: //while(1); @@ -1944,7 +1947,7 @@ void PmoveSingle (playerMove* const pmove) else { // airborne - std::cout << "AIRMOVE\n"; + //std::cout << "AIRMOVE\n"; PM_AirMove(); //bprintf("AirMove\n"); } diff --git a/libs/openengine/bullet/pmove.h b/libs/openengine/bullet/pmove.h index e90cc3b35d..5dfd18f6dc 100644 --- a/libs/openengine/bullet/pmove.h +++ b/libs/openengine/bullet/pmove.h @@ -90,9 +90,9 @@ struct playerMove { struct playerStruct { - playerStruct() : gravity(50.0f), speed(320.0f), pmove_framecount(20), groundEntityNum(ENTITYNUM_NONE), commandTime(40), move_type(PM_NORMAL), pm_time(0) + playerStruct() : gravity(50.0f), speed(320.0f), pmove_framecount(20), groundEntityNum(ENTITYNUM_NONE), commandTime(40), move_type(PM_NOCLIP), pm_time(0) { - origin = Ogre::Vector3(733.164f,1000.0f, 839.432f); + origin = Ogre::Vector3(733.164f,900.0f, 839.432f); velocity = Ogre::Vector3(0.0f, 0.0f, 0.0f); viewangles = Ogre::Vector3(0.0f, 0.0f, 0.0f); diff --git a/libs/openengine/bullet/trace.cpp b/libs/openengine/bullet/trace.cpp index 49e12064ee..57d071f170 100644 --- a/libs/openengine/bullet/trace.cpp +++ b/libs/openengine/bullet/trace.cpp @@ -112,8 +112,8 @@ const bool NewPhysicsTrace(NewPhysTraceResults* const out, const Ogre::Vector3& float y2 = to.getOrigin().getY(); float z2 = to.getOrigin().getZ(); - std::cout << "BtFrom: " << x << "," << y << "," << z << "\n"; - std::cout << "BtTo: " << x2 << "," << y2 << "," << z2 << "\n"; + //std::cout << "BtFrom: " << x << "," << y << "," << z << "\n"; + //std::cout << "BtTo: " << x2 << "," << y2 << "," << z2 << "\n"; //std::cout << "BtTo: " << to.getOrigin().getX() << "," << to.getOrigin().getY() << "," << to.getOrigin().getZ() << "\n"; diff --git a/libs/openengine/bullet/weather.cpp b/libs/openengine/bullet/weather.cpp new file mode 100644 index 0000000000..90afc4e78b --- /dev/null +++ b/libs/openengine/bullet/weather.cpp @@ -0,0 +1,811 @@ +#include "weather.hpp" +#include "world.hpp" +#include "player.hpp" + +#include "../mwrender/renderingmanager.hpp" +#include "../mwsound/soundmanager.hpp" + +#include +#include +#include + +#include + +using namespace Ogre; +using namespace MWWorld; +using namespace MWSound; + +#define lerp(x, y) (x * (1-factor) + y * factor) + +const std::string WeatherGlobals::mThunderSoundID0 = "Thunder0"; +const std::string WeatherGlobals::mThunderSoundID1 = "Thunder1"; +const std::string WeatherGlobals::mThunderSoundID2 = "Thunder2"; +const std::string WeatherGlobals::mThunderSoundID3 = "Thunder3"; +const float WeatherGlobals::mSunriseTime = 8; +const float WeatherGlobals::mSunsetTime = 18; +const float WeatherGlobals::mSunriseDuration = 2; +const float WeatherGlobals::mSunsetDuration = 2; +const float WeatherGlobals::mWeatherUpdateTime = 20.f; +const float WeatherGlobals::mThunderFrequency = .4; +const float WeatherGlobals::mThunderThreshold = 0.6; +const float WeatherGlobals::mThunderSoundDelay = 0.25; + +WeatherManager::WeatherManager(MWRender::RenderingManager* rendering, Environment* env) : + mHour(14), mCurrentWeather("clear"), mFirstUpdate(true), mWeatherUpdateTime(0), + mThunderFlash(0), mThunderChance(0), mThunderChanceNeeded(50), mThunderSoundDelay(0) +{ + mRendering = rendering; + mEnvironment = env; + + #define clr(r,g,b) ColourValue(r/255.f, g/255.f, b/255.f) + + /// \todo read these from Morrowind.ini + Weather clear; + clear.mCloudTexture = "tx_sky_clear.dds"; + clear.mCloudsMaximumPercent = 1.0; + clear.mTransitionDelta = 0.015; + clear.mSkySunriseColor = clr(118, 141, 164); + clear.mSkyDayColor = clr(95, 135, 203); + clear.mSkySunsetColor = clr(56, 89, 129); + clear.mSkyNightColor = clr(9, 10, 11); + clear.mFogSunriseColor = clr(255, 189, 157); + clear.mFogDayColor = clr(206, 227, 255); + clear.mFogSunsetColor = clr(255, 189, 157); + clear.mFogNightColor = clr(9, 10, 11); + clear.mAmbientSunriseColor = clr(47, 66, 96); + clear.mAmbientDayColor = clr(137, 140, 160); + clear.mAmbientSunsetColor = clr(68, 75, 96); + clear.mAmbientNightColor = clr(32, 35, 42); + clear.mSunSunriseColor = clr(242, 159, 99); + clear.mSunDayColor = clr(255, 252, 238); + clear.mSunSunsetColor = clr(255, 115, 79); + clear.mSunNightColor = clr(59, 97, 176); + clear.mSunDiscSunsetColor = clr(255, 189, 157); + clear.mLandFogDayDepth = 0.69; + clear.mLandFogNightDepth = 0.69; + clear.mWindSpeed = 0.1; + clear.mCloudSpeed = 1.25; + clear.mGlareView = 1.0; + mWeatherSettings["clear"] = clear; + + Weather cloudy; + cloudy.mCloudTexture = "tx_sky_cloudy.dds"; + cloudy.mCloudsMaximumPercent = 1.0; + cloudy.mTransitionDelta = 0.015; + cloudy.mSkySunriseColor = clr(126, 158, 173); + cloudy.mSkyDayColor = clr(117, 160, 215); + cloudy.mSkySunsetColor = clr(111, 114, 159); + cloudy.mSkyNightColor = clr(9, 10, 11); + cloudy.mFogSunriseColor = clr(255, 207, 149); + cloudy.mFogDayColor = clr(245, 235, 224); + cloudy.mFogSunsetColor = clr(255, 155, 106); + cloudy.mFogNightColor = clr(9, 10, 11); + cloudy.mAmbientSunriseColor = clr(66, 74, 87); + cloudy.mAmbientDayColor = clr(137, 145, 160); + cloudy.mAmbientSunsetColor = clr(71, 80, 92); + cloudy.mAmbientNightColor = clr(32, 39, 54); + cloudy.mSunSunriseColor = clr(241, 177, 99); + cloudy.mSunDayColor = clr(255, 236, 221); + cloudy.mSunSunsetColor = clr(255, 89, 00); + cloudy.mSunNightColor = clr(77, 91, 124); + cloudy.mSunDiscSunsetColor = clr(255, 202, 179); + cloudy.mLandFogDayDepth = 0.72; + cloudy.mLandFogNightDepth = 0.72; + cloudy.mWindSpeed = 0.2; + cloudy.mCloudSpeed = 2; + cloudy.mGlareView = 1.0; + mWeatherSettings["cloudy"] = cloudy; + + Weather foggy; + foggy.mCloudTexture = "tx_sky_foggy.dds"; + foggy.mCloudsMaximumPercent = 1.0; + foggy.mTransitionDelta = 0.015; + foggy.mSkySunriseColor = clr(197, 190, 180); + foggy.mSkyDayColor = clr(184, 211, 228); + foggy.mSkySunsetColor = clr(142, 159, 176); + foggy.mSkyNightColor = clr(18, 23, 28); + foggy.mFogSunriseColor = clr(173, 164, 148); + foggy.mFogDayColor = clr(150, 187, 209); + foggy.mFogSunsetColor = clr(113, 135, 157); + foggy.mFogNightColor = clr(19, 24, 29); + foggy.mAmbientSunriseColor = clr(48, 43, 37); + foggy.mAmbientDayColor = clr(92, 109, 120); + foggy.mAmbientSunsetColor = clr(28, 33, 39); + foggy.mAmbientNightColor = clr(28, 33, 39); + foggy.mSunSunriseColor = clr(177, 162, 137); + foggy.mSunDayColor = clr(111, 131, 151); + foggy.mSunSunsetColor = clr(125, 157, 189); + foggy.mSunNightColor = clr(81, 100, 119); + foggy.mSunDiscSunsetColor = clr(223, 223, 223); + foggy.mLandFogDayDepth = 1.0; + foggy.mLandFogNightDepth = 1.9; + foggy.mWindSpeed = 0; + foggy.mCloudSpeed = 1.25; + foggy.mGlareView = 0.25; + mWeatherSettings["foggy"] = foggy; + + Weather thunderstorm; + thunderstorm.mCloudTexture = "tx_sky_thunder.dds"; + thunderstorm.mCloudsMaximumPercent = 0.66; + thunderstorm.mTransitionDelta = 0.03; + thunderstorm.mSkySunriseColor = clr(35, 36, 39); + thunderstorm.mSkyDayColor = clr(97, 104, 115); + thunderstorm.mSkySunsetColor = clr(35, 36, 39); + thunderstorm.mSkyNightColor = clr(19, 20, 22); + thunderstorm.mFogSunriseColor = clr(70, 74, 85); + thunderstorm.mFogDayColor = clr(97, 104, 115); + thunderstorm.mFogSunsetColor = clr(70, 74, 85); + thunderstorm.mFogNightColor = clr(19, 20, 22); + thunderstorm.mAmbientSunriseColor = clr(54, 54, 54); + thunderstorm.mAmbientDayColor = clr(90, 90, 90); + thunderstorm.mAmbientSunsetColor = clr(54, 54, 54); + thunderstorm.mAmbientNightColor = clr(49, 51, 54); + thunderstorm.mSunSunriseColor = clr(91, 99, 122); + thunderstorm.mSunDayColor = clr(138, 144, 155); + thunderstorm.mSunSunsetColor = clr(96, 101, 117); + thunderstorm.mSunNightColor = clr(55, 76, 110); + thunderstorm.mSunDiscSunsetColor = clr(128, 128, 128); + thunderstorm.mLandFogDayDepth = 1; + thunderstorm.mLandFogNightDepth = 1.15; + thunderstorm.mWindSpeed = 0.5; + thunderstorm.mCloudSpeed = 3; + thunderstorm.mGlareView = 0; + thunderstorm.mRainLoopSoundID = "rain heavy"; + mWeatherSettings["thunderstorm"] = thunderstorm; + + Weather rain; + rain.mCloudTexture = "tx_sky_rainy.dds"; + rain.mCloudsMaximumPercent = 0.66; + rain.mTransitionDelta = 0.015; + rain.mSkySunriseColor = clr(71, 74, 75); + rain.mSkyDayColor = clr(116, 120, 122); + rain.mSkySunsetColor = clr(73, 73, 73); + rain.mSkyNightColor = clr(24, 25, 26); + rain.mFogSunriseColor = clr(71, 74, 75); + rain.mFogDayColor = clr(116, 120, 122); + rain.mFogSunsetColor = clr(73, 73, 73); + rain.mFogNightColor = clr(24, 25, 26); + rain.mAmbientSunriseColor = clr(97, 90, 88); + rain.mAmbientDayColor = clr(105, 110, 113); + rain.mAmbientSunsetColor = clr(88, 97, 97); + rain.mAmbientNightColor = clr(50, 55, 67); + rain.mSunSunriseColor = clr(131, 122, 120); + rain.mSunDayColor = clr(149, 157, 170); + rain.mSunSunsetColor = clr(120, 126, 131); + rain.mSunNightColor = clr(50, 62, 101); + rain.mSunDiscSunsetColor = clr(128, 128, 128); + rain.mLandFogDayDepth = 0.8; + rain.mLandFogNightDepth = 0.8; + rain.mWindSpeed = 0.3; + rain.mCloudSpeed = 2; + rain.mGlareView = 0; + rain.mRainLoopSoundID = "rain"; + mWeatherSettings["rain"] = rain; + + Weather overcast; + overcast.mCloudTexture = "tx_sky_overcast.dds"; + overcast.mCloudsMaximumPercent = 1.0; + overcast.mTransitionDelta = 0.015; + overcast.mSkySunriseColor = clr(91, 99, 106); + overcast.mSkyDayColor = clr(143, 146, 149); + overcast.mSkySunsetColor = clr(108, 115, 121); + overcast.mSkyNightColor = clr(19, 22, 25); + overcast.mFogSunriseColor = clr(91, 99, 106); + overcast.mFogDayColor = clr(143, 146, 149); + overcast.mFogSunsetColor = clr(108, 115, 121); + overcast.mFogNightColor = clr(19, 22, 25); + overcast.mAmbientSunriseColor = clr(84, 88, 92); + overcast.mAmbientDayColor = clr(93, 96, 105); + overcast.mAmbientSunsetColor = clr(83, 77, 75); + overcast.mAmbientNightColor = clr(57, 60, 66); + overcast.mSunSunriseColor = clr(87, 125, 163); + overcast.mSunDayColor = clr(163, 169, 183); + overcast.mSunSunsetColor = clr(85, 103, 157); + overcast.mSunNightColor = clr(32, 54, 100); + overcast.mSunDiscSunsetColor = clr(128, 128, 128); + overcast.mLandFogDayDepth = 0.7; + overcast.mLandFogNightDepth = 0.7; + overcast.mWindSpeed = 0.2; + overcast.mCloudSpeed = 1.5; + overcast.mGlareView = 0; + mWeatherSettings["overcast"] = overcast; + + Weather ashstorm; + ashstorm.mCloudTexture = "tx_sky_ashstorm.dds"; + ashstorm.mCloudsMaximumPercent = 1.0; + ashstorm.mTransitionDelta = 0.035; + ashstorm.mSkySunriseColor = clr(91, 56, 51); + ashstorm.mSkyDayColor = clr(124, 73, 58); + ashstorm.mSkySunsetColor = clr(106, 55, 40); + ashstorm.mSkyNightColor = clr(20, 21, 22); + ashstorm.mFogSunriseColor = clr(91, 56, 51); + ashstorm.mFogDayColor = clr(124, 73, 58); + ashstorm.mFogSunsetColor = clr(106, 55, 40); + ashstorm.mFogNightColor = clr(20, 21, 22); + ashstorm.mAmbientSunriseColor = clr(52, 42, 37); + ashstorm.mAmbientDayColor = clr(75, 49, 41); + ashstorm.mAmbientSunsetColor = clr(48, 39, 35); + ashstorm.mAmbientNightColor = clr(36, 42, 49); + ashstorm.mSunSunriseColor = clr(184, 91, 71); + ashstorm.mSunDayColor = clr(228, 139, 114); + ashstorm.mSunSunsetColor = clr(185, 86, 57); + ashstorm.mSunNightColor = clr(54, 66, 74); + ashstorm.mSunDiscSunsetColor = clr(128, 128, 128); + ashstorm.mLandFogDayDepth = 1.1; + ashstorm.mLandFogNightDepth = 1.2; + ashstorm.mWindSpeed = 0.8; + ashstorm.mCloudSpeed = 7; + ashstorm.mGlareView = 0; + ashstorm.mAmbientLoopSoundID = "ashstorm"; + mWeatherSettings["ashstorm"] = ashstorm; + + Weather blight; + blight.mCloudTexture = "tx_sky_blight.dds"; + blight.mCloudsMaximumPercent = 1.0; + blight.mTransitionDelta = 0.04; + blight.mSkySunriseColor = clr(90, 35, 35); + blight.mSkyDayColor = clr(90, 35, 35); + blight.mSkySunsetColor = clr(92, 33, 33); + blight.mSkyNightColor = clr(44, 14, 14); + blight.mFogSunriseColor = clr(90, 35, 35); + blight.mFogDayColor = clr(128, 19, 19); + blight.mFogSunsetColor = clr(92, 33, 33); + blight.mFogNightColor = clr(44, 14, 14); + blight.mAmbientSunriseColor = clr(61, 40, 40); + blight.mAmbientDayColor = clr(79, 54, 54); + blight.mAmbientSunsetColor = clr(61, 40, 40); + blight.mAmbientNightColor = clr(56, 58, 62); + blight.mSunSunriseColor = clr(180, 78, 78); + blight.mSunDayColor = clr(224, 84, 84); + blight.mSunSunsetColor = clr(180, 78, 78); + blight.mSunNightColor = clr(61, 91, 143); + blight.mSunDiscSunsetColor = clr(128, 128, 128); + blight.mLandFogDayDepth = 1.1; + blight.mLandFogNightDepth = 1.2; + blight.mWindSpeed = 0.9; + blight.mCloudSpeed = 9; + blight.mGlareView = 0; + blight.mAmbientLoopSoundID = "blight"; + mWeatherSettings["blight"] = blight; + + Weather snow; + snow.mCloudTexture = "tx_bm_sky_snow.dds"; + snow.mCloudsMaximumPercent = 1.0; + snow.mTransitionDelta = 0.014; + snow.mSkySunriseColor = clr(196, 91, 91); + snow.mSkyDayColor = clr(153, 158, 166); + snow.mSkySunsetColor = clr(96, 115, 134); + snow.mSkyNightColor = clr(31, 35, 39); + snow.mFogSunriseColor = clr(106, 91, 91); + snow.mFogDayColor = clr(153, 158, 166); + snow.mFogSunsetColor = clr(96, 115, 134); + snow.mFogNightColor = clr(31, 35, 39); + snow.mAmbientSunriseColor = clr(92, 84, 84); + snow.mAmbientDayColor = clr(93, 96, 105); + snow.mAmbientSunsetColor = clr(70, 79, 87); + snow.mAmbientNightColor = clr(49, 58, 68); + snow.mSunSunriseColor = clr(141, 109, 109); + snow.mSunDayColor = clr(163, 169, 183); + snow.mSunSunsetColor = clr(101, 121, 141); + snow.mSunNightColor = clr(55, 66, 77); + snow.mSunDiscSunsetColor = clr(128, 128, 128); + snow.mLandFogDayDepth = 1.0; + snow.mLandFogNightDepth = 1.2; + snow.mWindSpeed = 0; + snow.mCloudSpeed = 1.5; + snow.mGlareView = 0; + mWeatherSettings["snow"] = snow; + + Weather blizzard; + blizzard.mCloudTexture = "tx_bm_sky_blizzard.dds"; + blizzard.mCloudsMaximumPercent = 1.0; + blizzard.mTransitionDelta = 0.030; + blizzard.mSkySunriseColor = clr(91, 99, 106); + blizzard.mSkyDayColor = clr(121, 133, 145); + blizzard.mSkySunsetColor = clr(108, 115, 121); + blizzard.mSkyNightColor = clr(27, 29, 31); + blizzard.mFogSunriseColor = clr(91, 99, 106); + blizzard.mFogDayColor = clr(121, 133, 145); + blizzard.mFogSunsetColor = clr(108, 115, 121); + blizzard.mFogNightColor = clr(21, 24, 28); + blizzard.mAmbientSunriseColor = clr(84, 88, 92); + blizzard.mAmbientDayColor = clr(93, 96, 105); + blizzard.mAmbientSunsetColor = clr(83, 77, 75); + blizzard.mAmbientNightColor = clr(53, 62, 70); + blizzard.mSunSunriseColor = clr(114, 128, 146); + blizzard.mSunDayColor = clr(163, 169, 183); + blizzard.mSunSunsetColor = clr(106, 114, 136); + blizzard.mSunNightColor = clr(57, 66, 74); + blizzard.mSunDiscSunsetColor = clr(128, 128, 128); + blizzard.mLandFogDayDepth = 2.8; + blizzard.mLandFogNightDepth = 3.0; + blizzard.mWindSpeed = 0.9; + blizzard.mCloudSpeed = 7.5; + blizzard.mGlareView = 0; + blizzard.mAmbientLoopSoundID = "BM Blizzard"; + mWeatherSettings["blizzard"] = blizzard; +} + +void WeatherManager::setWeather(const String& weather, bool instant) +{ + if (instant || mFirstUpdate) + { + mNextWeather = ""; + mCurrentWeather = weather; + mFirstUpdate = false; + } + else + { + if (mNextWeather != "") + { + // transition more than 50% finished? + if (mRemainingTransitionTime/(mWeatherSettings[mCurrentWeather].mTransitionDelta*24.f*60) <= 0.5) + mCurrentWeather = mNextWeather; + } + + mNextWeather = weather; + mRemainingTransitionTime = mWeatherSettings[mCurrentWeather].mTransitionDelta*24.f*60; + } +} + +WeatherResult WeatherManager::getResult(const String& weather) +{ + const Weather& current = mWeatherSettings[weather]; + WeatherResult result; + + result.mCloudTexture = current.mCloudTexture; + result.mCloudBlendFactor = 0; + result.mCloudOpacity = current.mCloudsMaximumPercent; + result.mWindSpeed = current.mWindSpeed; + result.mCloudSpeed = current.mCloudSpeed; + result.mGlareView = current.mGlareView; + result.mAmbientLoopSoundID = current.mAmbientLoopSoundID; + result.mSunColor = current.mSunDiscSunsetColor; + + const float fade_duration = current.mTransitionDelta * 24.f; + + result.mNight = (mHour < 6.f+fade_duration || mHour > 20.f-fade_duration); + + result.mFogDepth = result.mNight ? current.mLandFogNightDepth : current.mLandFogDayDepth; + + // night + if (mHour <= (WeatherGlobals::mSunriseTime-WeatherGlobals::mSunriseDuration) + || mHour >= (WeatherGlobals::mSunsetTime+WeatherGlobals::mSunsetDuration)) + { + result.mFogColor = current.mFogNightColor; + result.mAmbientColor = current.mAmbientNightColor; + result.mSunColor = current.mSunNightColor; + result.mSkyColor = current.mSkyNightColor; + result.mNightFade = 1.f; + } + + // sunrise + else if (mHour >= (WeatherGlobals::mSunriseTime-WeatherGlobals::mSunriseDuration) && mHour <= WeatherGlobals::mSunriseTime) + { + if (mHour <= (WeatherGlobals::mSunriseTime-WeatherGlobals::mSunriseDuration+fade_duration)) + { + // fade in + float advance = (WeatherGlobals::mSunriseTime-WeatherGlobals::mSunriseDuration+fade_duration)-mHour; + float factor = (advance / fade_duration); + result.mFogColor = lerp(current.mFogSunriseColor, current.mFogNightColor); + result.mAmbientColor = lerp(current.mAmbientSunriseColor, current.mAmbientNightColor); + result.mSunColor = lerp(current.mSunSunriseColor, current.mSunNightColor); + result.mSkyColor = lerp(current.mSkySunriseColor, current.mSkyNightColor); + result.mNightFade = factor; + } + else if (mHour >= (WeatherGlobals::mSunriseTime-fade_duration)) + { + // fade out + float advance = mHour-(WeatherGlobals::mSunriseTime-fade_duration); + float factor = advance / fade_duration; + result.mFogColor = lerp(current.mFogSunriseColor, current.mFogDayColor); + result.mAmbientColor = lerp(current.mAmbientSunriseColor, current.mAmbientDayColor); + result.mSunColor = lerp(current.mSunSunriseColor, current.mSunDayColor); + result.mSkyColor = lerp(current.mSkySunriseColor, current.mSkyDayColor); + } + else + { + result.mFogColor = current.mFogSunriseColor; + result.mAmbientColor = current.mAmbientSunriseColor; + result.mSunColor = current.mSunSunriseColor; + result.mSkyColor = current.mSkySunriseColor; + } + } + + // day + else if (mHour >= (WeatherGlobals::mSunriseTime) && mHour <= (WeatherGlobals::mSunsetTime)) + { + result.mFogColor = current.mFogDayColor; + result.mAmbientColor = current.mAmbientDayColor; + result.mSunColor = current.mSunDayColor; + result.mSkyColor = current.mSkyDayColor; + } + + // sunset + else if (mHour >= (WeatherGlobals::mSunsetTime) && mHour <= (WeatherGlobals::mSunsetTime+WeatherGlobals::mSunsetDuration)) + { + if (mHour <= (WeatherGlobals::mSunsetTime+fade_duration)) + { + // fade in + float advance = (WeatherGlobals::mSunsetTime+fade_duration)-mHour; + float factor = (advance / fade_duration); + result.mFogColor = lerp(current.mFogSunsetColor, current.mFogDayColor); + result.mAmbientColor = lerp(current.mAmbientSunsetColor, current.mAmbientDayColor); + result.mSunColor = lerp(current.mSunSunsetColor, current.mSunDayColor); + result.mSkyColor = lerp(current.mSkySunsetColor, current.mSkyDayColor); + } + else if (mHour >= (WeatherGlobals::mSunsetTime+WeatherGlobals::mSunsetDuration-fade_duration)) + { + // fade out + float advance = mHour-(WeatherGlobals::mSunsetTime+WeatherGlobals::mSunsetDuration-fade_duration); + float factor = advance / fade_duration; + result.mFogColor = lerp(current.mFogSunsetColor, current.mFogNightColor); + result.mAmbientColor = lerp(current.mAmbientSunsetColor, current.mAmbientNightColor); + result.mSunColor = lerp(current.mSunSunsetColor, current.mSunNightColor); + result.mSkyColor = lerp(current.mSkySunsetColor, current.mSkyNightColor); + result.mNightFade = factor; + } + else + { + result.mFogColor = current.mFogSunsetColor; + result.mAmbientColor = current.mAmbientSunsetColor; + result.mSunColor = current.mSunSunsetColor; + result.mSkyColor = current.mSkySunsetColor; + } + } + + return result; +} + +WeatherResult WeatherManager::transition(float factor) +{ + const WeatherResult& current = getResult(mCurrentWeather); + const WeatherResult& other = getResult(mNextWeather); + WeatherResult result; + + result.mCloudTexture = current.mCloudTexture; + result.mNextCloudTexture = other.mCloudTexture; + result.mCloudBlendFactor = factor; + + result.mCloudOpacity = lerp(current.mCloudOpacity, other.mCloudOpacity); + result.mFogColor = lerp(current.mFogColor, other.mFogColor); + result.mSunColor = lerp(current.mSunColor, other.mSunColor); + result.mSkyColor = lerp(current.mSkyColor, other.mSkyColor); + + result.mAmbientColor = lerp(current.mAmbientColor, other.mAmbientColor); + result.mSunDiscColor = lerp(current.mSunDiscColor, other.mSunDiscColor); + result.mFogDepth = lerp(current.mFogDepth, other.mFogDepth); + result.mWindSpeed = lerp(current.mWindSpeed, other.mWindSpeed); + result.mCloudSpeed = lerp(current.mCloudSpeed, other.mCloudSpeed); + result.mCloudOpacity = lerp(current.mCloudOpacity, other.mCloudOpacity); + result.mGlareView = lerp(current.mGlareView, other.mGlareView); + + result.mNight = current.mNight; + + // sound change behaviour: + // if 'other' has a new sound, switch to it after 1/2 of the transition length + if (other.mAmbientLoopSoundID != "") + result.mAmbientLoopSoundID = factor>0.5 ? other.mAmbientLoopSoundID : current.mAmbientLoopSoundID; + // if 'current' has a sound and 'other' does not have a sound, turn off the sound immediately + else if (current.mAmbientLoopSoundID != "") + result.mAmbientLoopSoundID = ""; + + return result; +} + +void WeatherManager::update(float duration) +{ + mWeatherUpdateTime -= duration; + if (mEnvironment->mWorld->isCellExterior() || mEnvironment->mWorld->isCellQuasiExterior()) + { + std::string regionstr = mEnvironment->mWorld->getPlayer().getPlayer().getCell()->cell->region; + boost::algorithm::to_lower(regionstr); + + if (mWeatherUpdateTime <= 0 || regionstr != mCurrentRegion) + { + mCurrentRegion = regionstr; + mWeatherUpdateTime = WeatherGlobals::mWeatherUpdateTime*60.f; + + std::string weather; + + if (mRegionOverrides.find(regionstr) != mRegionOverrides.end()) + weather = mRegionOverrides[regionstr]; + else + { + // get weather probabilities for the current region + const ESM::Region *region = mEnvironment->mWorld->getStore().regions.find (regionstr); + + float clear = region->data.clear/255.f; + float cloudy = region->data.cloudy/255.f; + float foggy = region->data.foggy/255.f; + float overcast = region->data.overcast/255.f; + float rain = region->data.rain/255.f; + float thunder = region->data.thunder/255.f; + float ash = region->data.ash/255.f; + float blight = region->data.blight/255.f; + float snow = region->data.a/255.f; + float blizzard = region->data.b/255.f; + + // re-scale to 100 percent + const float total = clear+cloudy+foggy+overcast+rain+thunder+ash+blight+snow+blizzard; + + srand(time(NULL)); + float random = ((rand()%100)/100.f) * total; + + if (random >= snow+blight+ash+thunder+rain+overcast+foggy+cloudy+clear) + weather = "blizzard"; + else if (random >= blight+ash+thunder+rain+overcast+foggy+cloudy+clear) + weather = "snow"; + else if (random >= ash+thunder+rain+overcast+foggy+cloudy+clear) + weather = "blight"; + else if (random >= thunder+rain+overcast+foggy+cloudy+clear) + weather = "ashstorm"; + else if (random >= rain+overcast+foggy+cloudy+clear) + weather = "thunderstorm"; + else if (random >= overcast+foggy+cloudy+clear) + weather = "rain"; + else if (random >= foggy+cloudy+clear) + weather = "overcast"; + else if (random >= cloudy+clear) + weather = "foggy"; + else if (random >= clear) + weather = "cloudy"; + else + weather = "clear"; + } + + setWeather(weather, false); + /* + std::cout << "roll result: " << random << std::endl; + + std::cout << regionstr << " weather probabilities: " << clear << " " << cloudy << " " << foggy << " " + << overcast << " " << rain << " " << thunder << " " << ash << " " << blight << " " << snow << " " + << blizzard << std::endl; + + std::cout << "New weather : " << weather << std::endl; + */ + } + + WeatherResult result; + + if (mNextWeather != "") + { + mRemainingTransitionTime -= duration; + if (mRemainingTransitionTime < 0) + { + mCurrentWeather = mNextWeather; + mNextWeather = ""; + } + } + + if (mNextWeather != "") + result = transition(1-(mRemainingTransitionTime/(mWeatherSettings[mCurrentWeather].mTransitionDelta*24.f*60))); + else + result = getResult(mCurrentWeather); + + mRendering->configureFog(result.mFogDepth, result.mFogColor); + + // disable sun during night + if (mHour >= WeatherGlobals::mSunsetTime+WeatherGlobals::mSunsetDuration + || mHour <= WeatherGlobals::mSunriseTime-WeatherGlobals::mSunriseDuration) + mRendering->getSkyManager()->sunDisable(); + else + { + // during day, calculate sun angle + float height = 1-std::abs(((mHour-13)/7.f)); + int facing = mHour > 13.f ? 1 : -1; + Vector3 final( + (1-height)*facing, + (1-height)*facing, + height); + mRendering->setSunDirection(final); + + mRendering->getSkyManager()->sunEnable(); + } + + // moon calculations + float night; + if (mHour >= 14) + night = mHour-14; + else if (mHour <= 10) + night = mHour+10; + else + night = 0; + + night /= 20.f; + + if (night != 0) + { + float moonHeight = 1-std::abs((night-0.5)*2); + int facing = (mHour > 0.f && mHour<12.f) ? 1 : -1; + Vector3 masser( + (1-moonHeight)*facing, + (1-moonHeight)*facing, + moonHeight); + + Vector3 secunda( + (1-moonHeight)*facing*0.8, + (1-moonHeight)*facing*1.25, + moonHeight); + + mRendering->getSkyManager()->setMasserDirection(masser); + mRendering->getSkyManager()->setSecundaDirection(secunda); + mRendering->getSkyManager()->masserEnable(); + mRendering->getSkyManager()->secundaEnable(); + + float hour_fade; + if (mHour >= 7.f && mHour <= 14.f) + hour_fade = 1-(mHour-7)/3.f; + else if (mHour >= 14 && mHour <= 15.f) + hour_fade = mHour-14; + else + hour_fade = 1; + + float secunda_angle_fade; + float masser_angle_fade; + float angle = moonHeight*90.f; + + if (angle >= 30 && angle <= 50) + secunda_angle_fade = (angle-30)/20.f; + else if (angle <30) + secunda_angle_fade = 0.f; + else + secunda_angle_fade = 1.f; + + if (angle >= 40 && angle <= 50) + masser_angle_fade = (angle-40)/10.f; + else if (angle <40) + masser_angle_fade = 0.f; + else + masser_angle_fade = 1.f; + + masser_angle_fade *= hour_fade; + secunda_angle_fade *= hour_fade; + + mRendering->getSkyManager()->setMasserFade(masser_angle_fade); + mRendering->getSkyManager()->setSecundaFade(secunda_angle_fade); + } + else + { + mRendering->getSkyManager()->masserDisable(); + mRendering->getSkyManager()->secundaDisable(); + } + + if (mCurrentWeather == "thunderstorm" && mNextWeather == "") + { + if (mThunderFlash > 0) + { + // play the sound after a delay + mThunderSoundDelay -= duration; + if (mThunderSoundDelay <= 0) + { + // pick a random sound + int sound = rand() % 4; + std::string soundname; + if (sound == 0) soundname = WeatherGlobals::mThunderSoundID0; + else if (sound == 1) soundname = WeatherGlobals::mThunderSoundID1; + else if (sound == 2) soundname = WeatherGlobals::mThunderSoundID2; + else if (sound == 3) soundname = WeatherGlobals::mThunderSoundID3; + mEnvironment->mSoundManager->playSound(soundname, 1.0, 1.0); + mThunderSoundDelay = 1000; + } + + mThunderFlash -= duration; + if (mThunderFlash > 0) + mRendering->getSkyManager()->setThunder( mThunderFlash / WeatherGlobals::mThunderThreshold ); + else + { + srand(time(NULL)); + mThunderChanceNeeded = rand() % 100; + mThunderChance = 0; + mRendering->getSkyManager()->setThunder( 0.f ); + } + } + else + { + // no thunder active + mThunderChance += duration*4; // chance increases by 4 percent every second + if (mThunderChance >= mThunderChanceNeeded) + { + mThunderFlash = WeatherGlobals::mThunderThreshold; + + mRendering->getSkyManager()->setThunder( mThunderFlash / WeatherGlobals::mThunderThreshold ); + + mThunderSoundDelay = WeatherGlobals::mThunderSoundDelay; + } + } + } + else + mRendering->getSkyManager()->setThunder(0.f); + + mRendering->setAmbientColour(result.mAmbientColor); + mRendering->sunEnable(); + mRendering->setSunColour(result.mSunColor); + + mRendering->getSkyManager()->setWeather(result); + } + else + { + mRendering->sunDisable(); + mRendering->skyDisable(); + mRendering->getSkyManager()->setThunder(0.f); + } +} + +void WeatherManager::setHour(const float hour) +{ + // accelerate a bit for testing + /* + mHour += 0.005; + + if (mHour >= 24.f) mHour = 0.f; + + std::cout << "hour " << mHour << std::endl; + */ + + mHour = hour; +} + +void WeatherManager::setDate(const int day, const int month) +{ + mDay = day; + mMonth = month; +} + +unsigned int WeatherManager::getWeatherID() const +{ + // Source: http://www.uesp.net/wiki/Tes3Mod:GetCurrentWeather + + if (mCurrentWeather == "clear") + return 0; + else if (mCurrentWeather == "cloudy") + return 1; + else if (mCurrentWeather == "foggy") + return 2; + else if (mCurrentWeather == "overcast") + return 3; + else if (mCurrentWeather == "rain") + return 4; + else if (mCurrentWeather == "thunderstorm") + return 5; + else if (mCurrentWeather == "ashstorm") + return 6; + else if (mCurrentWeather == "blight") + return 7; + else if (mCurrentWeather == "snow") + return 8; + else if (mCurrentWeather == "blizzard") + return 9; + + else + return 0; +} + +void WeatherManager::changeWeather(const std::string& region, const unsigned int id) +{ + std::string weather; + if (id==0) + weather = "clear"; + else if (id==1) + weather = "cloudy"; + else if (id==2) + weather = "foggy"; + else if (id==3) + weather = "overcast"; + else if (id==4) + weather = "rain"; + else if (id==5) + weather = "thunderstorm"; + else if (id==6) + weather = "ashstorm"; + else if (id==7) + weather = "blight"; + else if (id==8) + weather = "snow"; + else if (id==9) + weather = "blizzard"; + else + weather = "clear"; + + mRegionOverrides[region] = weather; +} diff --git a/libs/openengine/bullet/weather.hpp b/libs/openengine/bullet/weather.hpp new file mode 100644 index 0000000000..9353f7cd1d --- /dev/null +++ b/libs/openengine/bullet/weather.hpp @@ -0,0 +1,272 @@ +#ifndef GAME_MWWORLD_WEATHER_H +#define GAME_MWWORLD_WEATHER_H + +#include +#include + +namespace MWRender +{ + class RenderingManager; +} + +namespace MWWorld +{ + class Environment; + + /// Global weather manager properties (according to INI) + struct WeatherGlobals + { + /* + [Weather] + EnvReduceColor=255,255,255,255 + LerpCloseColor=037,046,048,255 + BumpFadeColor=230,239,255,255 + AlphaReduce=0.35 + Minimum Time Between Environmental Sounds=1.0 + Maximum Time Between Environmental Sounds=5.0 + Sun Glare Fader Max=0.5 + Sun Glare Fader Angle Max=30.0 + Sun Glare Fader Color=222,095,039 + Timescale Clouds=0 + Precip Gravity=575 + Hours Between Weather Changes=20 + Rain Ripples=1 + Rain Ripple Radius=1024 + Rain Ripples Per Drop=1 + Rain Ripple Scale=0.3 + Rain Ripple Speed=1.0 + Fog Depth Change Speed=3 + Sunrise Time=6 + Sunset Time=18 + Sunrise Duration=2 + Sunset Duration=2 + Sky Pre-Sunrise Time=.5 + Sky Post-Sunrise Time=1 + Sky Pre-Sunset Time=1.5 + Sky Post-Sunset Time=.5 + Ambient Pre-Sunrise Time=.5 + Ambient Post-Sunrise Time=2 + Ambient Pre-Sunset Time=1 + Ambient Post-Sunset Time=1.25 + Fog Pre-Sunrise Time=.5 + Fog Post-Sunrise Time=1 + Fog Pre-Sunset Time=2 + Fog Post-Sunset Time=1 + Sun Pre-Sunrise Time=0 + Sun Post-Sunrise Time=0 + Sun Pre-Sunset Time=1 + Sun Post-Sunset Time=1.25 + Stars Post-Sunset Start=1 + Stars Pre-Sunrise Finish=2 + Stars Fading Duration=2 + Snow Ripples=0 + Snow Ripple Radius=1024 + Snow Ripples Per Flake=1 + Snow Ripple Scale=0.3 + Snow Ripple Speed=1.0 + Snow Gravity Scale=0.1 + Snow High Kill=700 + Snow Low Kill=150 + + + [Moons] + Masser Size=94 + Masser Fade In Start=14 + Masser Fade In Finish=15 + Masser Fade Out Start=7 + Masser Fade Out Finish=10 + Masser Axis Offset=35 + Masser Speed=.5 + Masser Daily Increment=1 + Masser Fade Start Angle=50 + Masser Fade End Angle=40 + Masser Moon Shadow Early Fade Angle=0.5 + Secunda Size=40 + Secunda Fade In Start=14 + Secunda Fade In Finish=15 + Secunda Fade Out Start=7 + Secunda Fade Out Finish=10 + Secunda Axis Offset=50 + Secunda Speed=.6 + Secunda Daily Increment=1.2 + Secunda Fade Start Angle=50 + Secunda Fade End Angle=30 + Secunda Moon Shadow Early Fade Angle=0.5 + Script Color=255,20,20 + */ + + static const float mSunriseTime; + static const float mSunsetTime; + static const float mSunriseDuration; + static const float mSunsetDuration; + + static const float mWeatherUpdateTime; + + // morrowind sets these per-weather, but since they are only used by 'thunderstorm' + // weather setting anyway, we can just as well set them globally + static const float mThunderFrequency; + static const float mThunderThreshold; + static const float mThunderSoundDelay; + static const std::string mThunderSoundID0; + static const std::string mThunderSoundID1; + static const std::string mThunderSoundID2; + static const std::string mThunderSoundID3; + }; + + /// Defines the actual weather that results from weather setting (see below), time of day and weather transition + struct WeatherResult + { + Ogre::String mCloudTexture; + Ogre::String mNextCloudTexture; + float mCloudBlendFactor; + + Ogre::ColourValue mFogColor; + + Ogre::ColourValue mAmbientColor; + + Ogre::ColourValue mSkyColor; + + Ogre::ColourValue mSunColor; + + Ogre::ColourValue mSunDiscColor; + + float mFogDepth; + + float mWindSpeed; + + float mCloudSpeed; + + float mCloudOpacity; + + float mGlareView; + + bool mNight; // use night skybox + float mNightFade; // fading factor for night skybox + + Ogre::String mAmbientLoopSoundID; + }; + + + /// Defines a single weather setting (according to INI) + struct Weather + { + Ogre::String mCloudTexture; + + // Sky (atmosphere) colors + Ogre::ColourValue mSkySunriseColor, + mSkyDayColor, + mSkySunsetColor, + mSkyNightColor; + + // Fog colors + Ogre::ColourValue mFogSunriseColor, + mFogDayColor, + mFogSunsetColor, + mFogNightColor; + + // Ambient lighting colors + Ogre::ColourValue mAmbientSunriseColor, + mAmbientDayColor, + mAmbientSunsetColor, + mAmbientNightColor; + + // Sun (directional) lighting colors + Ogre::ColourValue mSunSunriseColor, + mSunDayColor, + mSunSunsetColor, + mSunNightColor; + + // Fog depth/density + float mLandFogDayDepth, + mLandFogNightDepth; + + // Color modulation for the sun itself during sunset (not completely sure) + Ogre::ColourValue mSunDiscSunsetColor; + + // Duration of weather transition (in days) + float mTransitionDelta; + + // No idea what this one is used for? + float mWindSpeed; + + // Cloud animation speed multiplier + float mCloudSpeed; + + // Multiplier for clouds transparency + float mCloudsMaximumPercent; + + // Value between 0 and 1, defines the strength of the sun glare effect + float mGlareView; + + // Sound effect + // This is used for Blight, Ashstorm and Blizzard (Bloodmoon) + Ogre::String mAmbientLoopSoundID; + + // Rain sound effect + Ogre::String mRainLoopSoundID; + + /// \todo disease chance + }; + + /// + /// Interface for weather settings + /// + class WeatherManager + { + public: + WeatherManager(MWRender::RenderingManager*, MWWorld::Environment*); + + /** + * Change the weather in the specified region + * @param region that should be changed + * @param ID of the weather setting to shift to + */ + void changeWeather(const std::string& region, const unsigned int id); + + /** + * Per-frame update + * @param duration + */ + void update(float duration); + + void setHour(const float hour); + + void setDate(const int day, const int month); + + unsigned int getWeatherID() const; + + private: + float mHour; + int mDay, mMonth; + + MWRender::RenderingManager* mRendering; + MWWorld::Environment* mEnvironment; + + std::map mWeatherSettings; + + std::map mRegionOverrides; + + Ogre::String mCurrentWeather; + Ogre::String mNextWeather; + + std::string mCurrentRegion; + + bool mFirstUpdate; + + float mWeatherUpdateTime; + + float mRemainingTransitionTime; + + float mThunderFlash; + float mThunderChance; + float mThunderChanceNeeded; + float mThunderSoundDelay; + + WeatherResult transition(const float factor); + WeatherResult getResult(const Ogre::String& weather); + + void setWeather(const Ogre::String& weather, bool instant=false); + }; +} + +#endif // GAME_MWWORLD_WEATHER_H