diff --git a/apps/openmw/mwgui/layouts.cpp b/apps/openmw/mwgui/layouts.cpp index de74214eef..223c072b7c 100644 --- a/apps/openmw/mwgui/layouts.cpp +++ b/apps/openmw/mwgui/layouts.cpp @@ -278,6 +278,7 @@ LocalMapBase::LocalMapBase() : mCurX(0) , mCurY(0) , mInterior(false) + , mFogOfWar(true) , mLocalMap(NULL) , mPrefix() , mChanged(true) @@ -297,6 +298,32 @@ void LocalMapBase::setCellPrefix(const std::string& prefix) mChanged = true; } +void LocalMapBase::toggleFogOfWar() +{ + mFogOfWar = !mFogOfWar; + applyFogOfWar(); +} + +void LocalMapBase::applyFogOfWar() +{ + for (int mx=0; mx<3; ++mx) + { + for (int my=0; my<3; ++my) + { + std::string name = "Map_" + boost::lexical_cast(mx) + "_" + + boost::lexical_cast(my); + std::string image = mPrefix+"_"+ boost::lexical_cast(mCurX + (mx-1)) + "_" + + boost::lexical_cast(mCurY + (mInterior ? (my-1) : -1*(my-1))); + MyGUI::ImageBox* fog; + mLayout->getWidget(fog, name+"_fog"); + fog->setImageTexture(mFogOfWar ? + ((MyGUI::RenderManager::getInstance().getTexture(image+"_fog") != 0) ? image+"_fog" + : "black.png" ) + : ""); + } + } +} + void LocalMapBase::setActiveCell(const int x, const int y, bool interior) { if (x==mCurX && y==mCurY && mInterior==interior && !mChanged) return; // don't do anything if we're still in the same cell @@ -312,23 +339,17 @@ void LocalMapBase::setActiveCell(const int x, const int y, bool interior) MyGUI::ImageBox* box; mLayout->getWidget(box, name); - MyGUI::ImageBox* fog; - mLayout->getWidget(fog, name+"_fog"); if (MyGUI::RenderManager::getInstance().getTexture(image) != 0) box->setImageTexture(image); else box->setImageTexture("black.png"); - - if (MyGUI::RenderManager::getInstance().getTexture(image+"_fog") != 0) - fog->setImageTexture(image+"_fog"); - else - fog->setImageTexture("black.png"); } } mInterior = interior; mCurX = x; mCurY = y; mChanged = false; + applyFogOfWar(); } diff --git a/apps/openmw/mwgui/layouts.hpp b/apps/openmw/mwgui/layouts.hpp index 614479ccce..6719e967cd 100644 --- a/apps/openmw/mwgui/layouts.hpp +++ b/apps/openmw/mwgui/layouts.hpp @@ -40,12 +40,17 @@ namespace MWGui void setCellPrefix(const std::string& prefix); void setActiveCell(const int x, const int y, bool interior=false); + void toggleFogOfWar(); + protected: int mCurX, mCurY; bool mInterior; MyGUI::ScrollView* mLocalMap; std::string mPrefix; bool mChanged; + bool mFogOfWar; + + void applyFogOfWar(); OEngine::GUI::Layout* mLayout; }; diff --git a/apps/openmw/mwgui/window_manager.cpp b/apps/openmw/mwgui/window_manager.cpp index 49b6e644d3..cf0c072682 100644 --- a/apps/openmw/mwgui/window_manager.cpp +++ b/apps/openmw/mwgui/window_manager.cpp @@ -455,3 +455,9 @@ void WindowManager::setPlayerDir(const float x, const float y) map->setPlayerDir(x,y); hud->setPlayerDir(x,y); } + +void WindowManager::toggleFogOfWar() +{ + map->toggleFogOfWar(); + hud->toggleFogOfWar(); +} diff --git a/apps/openmw/mwgui/window_manager.hpp b/apps/openmw/mwgui/window_manager.hpp index 582f438e8f..204774f5fe 100644 --- a/apps/openmw/mwgui/window_manager.hpp +++ b/apps/openmw/mwgui/window_manager.hpp @@ -156,6 +156,8 @@ namespace MWGui void changeCell(MWWorld::Ptr::CellStore* cell); ///< change the active cell void setPlayerPos(const float x, const float y); ///< set player position in map space void setPlayerDir(const float x, const float y); ///< set player view direction in map space + + void toggleFogOfWar(); void setInteriorMapTexture(const int x, const int y); ///< set the index of the map texture that should be used (for interiors) diff --git a/apps/openmw/mwrender/localmap.cpp b/apps/openmw/mwrender/localmap.cpp index ed218dc97d..e88557f204 100644 --- a/apps/openmw/mwrender/localmap.cpp +++ b/apps/openmw/mwrender/localmap.cpp @@ -2,6 +2,7 @@ #include "renderingmanager.hpp" #include "../mwworld/environment.hpp" +#include "../mwworld/world.hpp" #include "../mwgui/window_manager.hpp" #include @@ -10,16 +11,24 @@ using namespace MWRender; using namespace Ogre; -LocalMap::LocalMap(OEngine::Render::OgreRenderer* rend, MWWorld::Environment* env) +LocalMap::LocalMap(OEngine::Render::OgreRenderer* rend, MWRender::RenderingManager* rendering, MWWorld::Environment* env) : + mInterior(false), mCellX(0), mCellY(0) { mRendering = rend; + mRenderingManager = rendering; mEnvironment = env; - + + mCameraPosNode = mRendering->getScene()->getRootSceneNode()->createChildSceneNode(); + mCameraRotNode = mCameraPosNode->createChildSceneNode(); + mCameraNode = mCameraRotNode->createChildSceneNode(); + mCellCamera = mRendering->getScene()->createCamera("CellCamera"); mCellCamera->setProjectionType(PT_ORTHOGRAPHIC); // look down -y const float sqrt0pt5 = 0.707106781; mCellCamera->setOrientation(Quaternion(sqrt0pt5, -sqrt0pt5, 0, 0)); + + mCameraNode->attachObject(mCellCamera); } LocalMap::~LocalMap() @@ -27,6 +36,12 @@ LocalMap::~LocalMap() deleteBuffers(); } +const Ogre::Vector2 LocalMap::rotatePoint(const Ogre::Vector2& p, const Ogre::Vector2& c, const float angle) +{ + return Vector2( Math::Cos(angle) * (p.x - c.x) - Math::Sin(angle) * (p.y - c.y) + c.x, + Math::Sin(angle) * (p.x - c.x) + Math::Cos(angle) * (p.y - c.y) + c.y); +} + void LocalMap::deleteBuffers() { mBuffers.clear(); @@ -65,9 +80,6 @@ void LocalMap::saveFogOfWar(MWWorld::Ptr::CellStore* cell) { Vector2 min(mBounds.getMinimum().x, mBounds.getMinimum().z); Vector2 max(mBounds.getMaximum().x, mBounds.getMaximum().z); - /// \todo why is this workaround needed? - min *= 1.3; - max *= 1.3; Vector2 length = max-min; // divide into segments @@ -90,11 +102,15 @@ void LocalMap::requestMap(MWWorld::Ptr::CellStore* cell) { mInterior = false; + mCameraRotNode->setOrientation(Quaternion::IDENTITY); + std::string name = "Cell_"+coordStr(cell->cell->data.gridX, cell->cell->data.gridY); int x = cell->cell->data.gridX; int y = cell->cell->data.gridY; + mCameraPosNode->setPosition(Vector3(0,0,0)); + render((x+0.5)*sSize, (-y-0.5)*sSize, -10000, 10000, sSize, sSize, name); } @@ -103,17 +119,29 @@ void LocalMap::requestMap(MWWorld::Ptr::CellStore* cell, { mInterior = true; mBounds = bounds; - - Vector2 z(bounds.getMaximum().y, bounds.getMinimum().y); - Vector2 min(bounds.getMinimum().x, bounds.getMinimum().z); - Vector2 max(bounds.getMaximum().x, bounds.getMaximum().z); - /// \todo why is this workaround needed? - min *= 1.3; - max *= 1.3; + Vector2 z(mBounds.getMaximum().y, mBounds.getMinimum().y); + + const Vector2& north = mEnvironment->mWorld->getNorthVector(cell); + Radian angle(std::atan2(-north.x, -north.y)); + mAngle = angle.valueRadians(); + mCameraRotNode->setOrientation(Quaternion(Math::Cos(angle/2.f), 0, Math::Sin(angle/2.f), 0)); + + mBounds.merge(mCameraRotNode->convertWorldToLocalPosition(bounds.getCorner(AxisAlignedBox::NEAR_LEFT_BOTTOM))); + mBounds.merge(mCameraRotNode->convertWorldToLocalPosition(bounds.getCorner(AxisAlignedBox::FAR_LEFT_BOTTOM))); + mBounds.merge(mCameraRotNode->convertWorldToLocalPosition(bounds.getCorner(AxisAlignedBox::NEAR_RIGHT_BOTTOM))); + mBounds.merge(mCameraRotNode->convertWorldToLocalPosition(bounds.getCorner(AxisAlignedBox::FAR_RIGHT_BOTTOM))); + + mBounds.scale(Vector3(2,2,2)); + + Vector2 center(mBounds.getCenter().x, mBounds.getCenter().z); + + Vector2 min(mBounds.getMinimum().x, mBounds.getMinimum().z); + Vector2 max(mBounds.getMaximum().x, mBounds.getMaximum().z); Vector2 length = max-min; - Vector2 center(bounds.getCenter().x, bounds.getCenter().z); + + mCameraPosNode->setPosition(Vector3(center.x, 0, center.y)); // divide into segments const int segsX = std::ceil( length.x / sSize ); @@ -128,7 +156,7 @@ void LocalMap::requestMap(MWWorld::Ptr::CellStore* cell, Vector2 start = min + Vector2(sSize*x,sSize*y); Vector2 newcenter = start + 4096; - render(newcenter.x, newcenter.y, z.y, z.x, sSize, sSize, + render(newcenter.x - center.x, newcenter.y - center.y, z.y, z.x, sSize, sSize, cell->cell->name + "_" + coordStr(x,y)); } } @@ -147,8 +175,9 @@ void LocalMap::render(const float x, const float y, // make everything visible mRendering->getScene()->setAmbientLight(ColourValue(1,1,1)); + mRenderingManager->disableLights(); - mCellCamera->setPosition(Vector3(x, zhigh+100000, y)); + mCameraNode->setPosition(Vector3(x, zhigh+100000, y)); //mCellCamera->setFarClipDistance( (zhigh-zlow) * 1.1 ); mCellCamera->setFarClipDistance(0); // infinite @@ -215,13 +244,14 @@ void LocalMap::render(const float x, const float y, //rtt->writeContentsToFile("./" + texture + ".jpg"); } } - + + mRenderingManager->enableLights(); // re-enable fog mRendering->getScene()->setFog(FOG_LINEAR, clr, 0, fStart, fEnd); } -void LocalMap::updatePlayer (const Ogre::Vector3& position, const Ogre::Vector3& direction) +void LocalMap::updatePlayer (const Ogre::Vector3& position, const Ogre::Quaternion& orientation) { if (sFogOfWarSkip != 0) { @@ -232,7 +262,19 @@ void LocalMap::updatePlayer (const Ogre::Vector3& position, const Ogre::Vector3& // retrieve the x,y grid coordinates the player is in int x,y; - Vector2 pos(position.x, position.z); + Vector3 _pos(position.x, 0, position.z); + Vector2 pos(_pos.x, _pos.z); + + if (mInterior) + { + pos = rotatePoint(pos, Vector2(mBounds.getCenter().x, mBounds.getCenter().z), mAngle); + } + + + Vector3 playerdirection = -mCameraRotNode->convertWorldToLocalOrientation(orientation).zAxis(); + + Vector2 min(mBounds.getMinimum().x, mBounds.getMinimum().z); + if (!mInterior) { x = std::ceil(pos.x / sSize)-1; @@ -242,9 +284,6 @@ void LocalMap::updatePlayer (const Ogre::Vector3& position, const Ogre::Vector3& } else { - Vector2 min(mBounds.getMinimum().x, mBounds.getMinimum().z); - min *= 1.3; - x = std::ceil((pos.x - min.x)/sSize)-1; y = std::ceil((pos.y - min.y)/sSize)-1; @@ -259,20 +298,17 @@ void LocalMap::updatePlayer (const Ogre::Vector3& position, const Ogre::Vector3& u = std::abs((pos.x - (sSize*x))/sSize); v = 1-std::abs((pos.y + (sSize*y))/sSize); texName = "Cell_"+coordStr(x,y); - } else { - Vector2 min(mBounds.getMinimum().x, mBounds.getMinimum().z); - min *= 1.3; - u = (pos.x - min.x - sSize*x)/sSize; v = (pos.y - min.y - sSize*y)/sSize; texName = mInteriorName + "_" + coordStr(x,y); } + mEnvironment->mWindowManager->setPlayerPos(u, v); - mEnvironment->mWindowManager->setPlayerDir(direction.x, -direction.z); + mEnvironment->mWindowManager->setPlayerDir(playerdirection.x, -playerdirection.z); // explore radius (squared) const float sqrExploreRadius = 0.01 * sFogOfWarResolution*sFogOfWarResolution; diff --git a/apps/openmw/mwrender/localmap.hpp b/apps/openmw/mwrender/localmap.hpp index efbccf8848..95685167c3 100644 --- a/apps/openmw/mwrender/localmap.hpp +++ b/apps/openmw/mwrender/localmap.hpp @@ -12,13 +12,15 @@ namespace MWWorld namespace MWRender { + class RenderingManager; + /// /// \brief Local map rendering /// class LocalMap { public: - LocalMap(OEngine::Render::OgreRenderer*, MWWorld::Environment* env); + LocalMap(OEngine::Render::OgreRenderer*, MWRender::RenderingManager* rendering, MWWorld::Environment* env); ~LocalMap(); /** @@ -44,9 +46,9 @@ namespace MWRender * @remarks This is used to draw a "fog of war" effect * to hide areas on the map the player has not discovered yet. * @param position (OGRE coordinates) - * @param view direction (OGRE coordinates) + * @param camera orientation (OGRE coordinates) */ - void updatePlayer (const Ogre::Vector3& position, const Ogre::Vector3& direction); + void updatePlayer (const Ogre::Vector3& position, const Ogre::Quaternion& orientation); /** * Save the fog of war for the current cell to disk. @@ -58,6 +60,7 @@ namespace MWRender private: OEngine::Render::OgreRenderer* mRendering; + MWRender::RenderingManager* mRenderingManager; MWWorld::Environment* mEnvironment; // 1024*1024 pixels for a cell @@ -73,6 +76,12 @@ namespace MWRender static const int sSize = 8192; Ogre::Camera* mCellCamera; + Ogre::SceneNode* mCameraNode; + Ogre::SceneNode* mCameraPosNode; + Ogre::SceneNode* mCameraRotNode; + + float mAngle; + const Ogre::Vector2 rotatePoint(const Ogre::Vector2& p, const Ogre::Vector2& c, const float angle); void render(const float x, const float y, const float zlow, const float zhigh, diff --git a/apps/openmw/mwrender/objects.cpp b/apps/openmw/mwrender/objects.cpp index f52953692e..39ab0b0897 100644 --- a/apps/openmw/mwrender/objects.cpp +++ b/apps/openmw/mwrender/objects.cpp @@ -168,6 +168,7 @@ void Objects::insertLight (const MWWorld::Ptr& ptr, float r, float g, float b, f assert(insert); Ogre::Light *light = mRenderer.getScene()->createLight(); light->setDiffuseColour (r, g, b); + mLights.push_back(light->getName()); float cval=0.0f, lval=0.0f, qval=0.0f; @@ -273,3 +274,34 @@ Ogre::AxisAlignedBox Objects::getDimensions(MWWorld::Ptr::CellStore* cell) { return mBounds[cell]; } + +void Objects::enableLights() +{ + std::vector::iterator it = mLights.begin(); + while (it != mLights.end()) + { + if (mMwRoot->getCreator()->hasLight(*it)) + { + mMwRoot->getCreator()->getLight(*it)->setVisible(true); + ++it; + } + else + it = mLights.erase(it); + } +} + +void Objects::disableLights() +{ + std::vector::iterator it = mLights.begin(); + while (it != mLights.end()) + { + if (mMwRoot->getCreator()->hasLight(*it)) + { + mMwRoot->getCreator()->getLight(*it)->setVisible(false); + ++it; + } + else + it = mLights.erase(it); + } +} + diff --git a/apps/openmw/mwrender/objects.hpp b/apps/openmw/mwrender/objects.hpp index 265de875be..5911aa4ccf 100644 --- a/apps/openmw/mwrender/objects.hpp +++ b/apps/openmw/mwrender/objects.hpp @@ -16,6 +16,7 @@ class Objects{ std::map mStaticGeometry; std::map mStaticGeometrySmall; std::map mBounds; + std::vector mLights; Ogre::SceneNode* mMwRoot; bool mIsStatic; static int uniqueID; @@ -44,6 +45,9 @@ public: void insertMesh (const MWWorld::Ptr& ptr, const std::string& mesh); void insertLight (const MWWorld::Ptr& ptr, float r, float g, float b, float radius); + void enableLights(); + void disableLights(); + Ogre::AxisAlignedBox getDimensions(MWWorld::Ptr::CellStore*); ///< get a bounding box that encloses all objects in the specified cell diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 7f4b1ec396..5dc41dc4b2 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -65,7 +65,7 @@ RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const mSun = 0; mDebugging = new Debugging(mMwRoot, environment, engine); - mLocalMap = new MWRender::LocalMap(&mRendering, &environment); + mLocalMap = new MWRender::LocalMap(&mRendering, this, &environment); } RenderingManager::~RenderingManager () @@ -178,7 +178,7 @@ void RenderingManager::update (float duration){ mRendering.update(duration); - mLocalMap->updatePlayer( mRendering.getCamera()->getRealPosition(), mRendering.getCamera()->getRealDirection() ); + mLocalMap->updatePlayer( mRendering.getCamera()->getRealPosition(), mRendering.getCamera()->getRealOrientation() ); checkUnderwater(); } @@ -408,4 +408,14 @@ void RenderingManager::preCellChange(MWWorld::Ptr::CellStore* cell) mLocalMap->saveFogOfWar(cell); } +void RenderingManager::disableLights() +{ + mObjects.disableLights(); +} + +void RenderingManager::enableLights() +{ + mObjects.enableLights(); +} + } // namespace diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index fc32b4dd2b..27cd67bbc0 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -106,6 +106,9 @@ class RenderingManager: private RenderingInterface { void sunEnable(); void sunDisable(); + void disableLights(); + void enableLights(); + bool occlusionQuerySupported() { return mOcclusionQuery->supported(); }; OcclusionQuery* getOcclusionQuery() { return mOcclusionQuery; }; diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index 2fdf9b2cd6..781f7abd5d 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -746,6 +746,7 @@ void SkyManager::setGlare(const float glare) Vector3 SkyManager::getRealSunPos() { + if (!mCreated) return Vector3(0,0,0); return mSun->getNode()->_getDerivedPosition(); } diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index 913f5992ed..df955ec198 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -127,5 +127,6 @@ op 0x2000141: GetWaterLevel op 0x2000142: SetWaterLevel op 0x2000143: ModWaterLevel op 0x2000144: ToggleWater, twa -op 0x2000145: TogglePathgrid -opcodes 0x2000146-0x3ffffff unused +op 0x2000145: ToggleFogOfWar (tfow) +op 0x2000146: TogglePathgrid +opcodes 0x2000147-0x3ffffff unused diff --git a/apps/openmw/mwscript/guiextensions.cpp b/apps/openmw/mwscript/guiextensions.cpp index 484c0d3ab5..426378efca 100644 --- a/apps/openmw/mwscript/guiextensions.cpp +++ b/apps/openmw/mwscript/guiextensions.cpp @@ -67,6 +67,19 @@ namespace MWScript } }; + class OpToggleFogOfWar : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + InterpreterContext& context = + static_cast (runtime.getContext()); + + context.getEnvironment().mWindowManager->toggleFogOfWar(); + } + }; + const int opcodeEnableBirthMenu = 0x200000e; const int opcodeEnableClassMenu = 0x200000f; const int opcodeEnableNameMenu = 0x2000010; @@ -79,6 +92,7 @@ namespace MWScript const int opcodeEnableRest = 0x2000017; const int opcodeShowRestMenu = 0x2000018; const int opcodeGetButtonPressed = 0x2000137; + const int opcodeToggleFogOfWar = 0x2000145; void registerExtensions (Compiler::Extensions& extensions) { @@ -100,6 +114,9 @@ namespace MWScript extensions.registerInstruction ("showrestmenu", "", opcodeShowRestMenu); extensions.registerFunction ("getbuttonpressed", 'l', "", opcodeGetButtonPressed); + + extensions.registerInstruction ("togglefogofwar", "", opcodeToggleFogOfWar); + extensions.registerInstruction ("tfow", "", opcodeToggleFogOfWar); } void installOpcodes (Interpreter::Interpreter& interpreter) @@ -135,6 +152,8 @@ namespace MWScript new OpShowDialogue (MWGui::GM_Rest)); interpreter.installSegment5 (opcodeGetButtonPressed, new OpGetButtonPressed); + + interpreter.installSegment5 (opcodeToggleFogOfWar, new OpToggleFogOfWar); } } } diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index 59cb7ecaf3..a0770b9a88 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -217,7 +217,7 @@ namespace MWScript const int opcodeFadeOut = 0x200013d; const int opcodeFadeTo = 0x200013e; const int opcodeToggleWater = 0x2000144; - const int opcodeTogglePathgrid = 0x2000145; + const int opcodeTogglePathgrid = 0x2000146; void registerExtensions (Compiler::Extensions& extensions) { diff --git a/apps/openmw/mwworld/world.cpp b/apps/openmw/mwworld/world.cpp index 3a15f42bae..a7252353dd 100644 --- a/apps/openmw/mwworld/world.cpp +++ b/apps/openmw/mwworld/world.cpp @@ -864,6 +864,18 @@ namespace MWWorld return mRendering->getFader(); } + Ogre::Vector2 World::getNorthVector(Ptr::CellStore* cell) + { + ESMS::CellRefList statics = cell->statics; + ESMS::LiveCellRef* ref = statics.find("northmarker"); + if (!ref) + return Vector2(0, 1); + Ogre::SceneNode* node = ref->mData.getBaseNode(); + Vector3 dir = node->_getDerivedOrientation().yAxis(); + Vector2 d = Vector2(dir.x, dir.z); + return d; + } + void World::setWaterHeight(const float height) { mRendering->setWaterHeight(height); diff --git a/apps/openmw/mwworld/world.hpp b/apps/openmw/mwworld/world.hpp index 0d46fe4e81..92540f82b7 100644 --- a/apps/openmw/mwworld/world.hpp +++ b/apps/openmw/mwworld/world.hpp @@ -139,6 +139,9 @@ namespace MWWorld bool isCellExterior() const; bool isCellQuasiExterior() const; + Ogre::Vector2 getNorthVector(Ptr::CellStore* cell); + ///< get north vector (OGRE coordinates) for given interior cell + Globals::Data& getGlobalVariable (const std::string& name); Globals::Data getGlobalVariable (const std::string& name) const;