diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 5de64061a..2630098f5 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -14,7 +14,8 @@ set(GAME_HEADER source_group(game FILES ${GAME} ${GAME_HEADER}) add_openmw_dir (mwrender - renderingmanager debugging sky player animation npcanimation creatureanimation actors objects renderinginterface + renderingmanager debugging sky player animation npcanimation creatureanimation actors objects + renderinginterface localmap ) add_openmw_dir (mwinput diff --git a/apps/openmw/mwgui/layouts.cpp b/apps/openmw/mwgui/layouts.cpp index ebabc6faf..5c5a977d3 100644 --- a/apps/openmw/mwgui/layouts.cpp +++ b/apps/openmw/mwgui/layouts.cpp @@ -61,6 +61,8 @@ HUD::HUD(int width, int height, int fpsLevel) setSpellIcon("icons\\s\\b_tx_s_rstor_health.dds"); setSpellStatus(65, 100); setEffect("icons\\s\\tx_s_chameleon.dds"); + + LocalMapBase::init(minimap, this); } void HUD::setFPS(float fps) @@ -142,3 +144,161 @@ void HUD::setValue(const std::string& id, const MWMechanics::DynamicStat& v } } } + +void HUD::setPlayerDir(const float x, const float y) +{ + MyGUI::ISubWidget* main = compass->getSubWidgetMain(); + MyGUI::RotatingSkin* rotatingSubskin = main->castType(); + rotatingSubskin->setCenter(MyGUI::IntPoint(16,16)); + float angle = std::atan2(x,y); + rotatingSubskin->setAngle(angle); +} + +void HUD::setPlayerPos(const float x, const float y) +{ + MyGUI::IntSize size = minimap->getCanvasSize(); + MyGUI::IntPoint middle = MyGUI::IntPoint((1/3.f + x/3.f)*size.width,(1/3.f + y/3.f)*size.height); + MyGUI::IntCoord viewsize = minimap->getCoord(); + MyGUI::IntPoint pos(0.5*viewsize.width - middle.left, 0.5*viewsize.height - middle.top); + + minimap->setViewOffset(pos); + compass->setPosition(MyGUI::IntPoint(x*512-16, y*512-16)); +} + +MapWindow::MapWindow() + : Layout("openmw_map_window_layout.xml"), mGlobal(false) +{ + setCoord(500,0,320,300); + setText("WorldButton", "World"); + setImage("Compass", "textures\\compass.dds"); + + // Obviously you should override this later on + setCellName("No Cell Loaded"); + + getWidget(mLocalMap, "LocalMap"); + getWidget(mGlobalMap, "GlobalMap"); + getWidget(mPlayerArrow, "Compass"); + + getWidget(mButton, "WorldButton"); + mButton->eventMouseButtonClick += MyGUI::newDelegate(this, &MapWindow::onWorldButtonClicked); + + MyGUI::Button* eventbox; + getWidget(eventbox, "EventBox"); + eventbox->eventMouseDrag += MyGUI::newDelegate(this, &MapWindow::onMouseDrag); + eventbox->eventMouseButtonPressed += MyGUI::newDelegate(this, &MapWindow::onDragStart); + + LocalMapBase::init(mLocalMap, this); +} + +void MapWindow::setVisible(bool b) +{ + mMainWidget->setVisible(b); + if (b) + mVisible = true; + else + mVisible = false; +} + +void MapWindow::setCellName(const std::string& cellName) +{ + static_cast(mMainWidget)->setCaption(cellName); +} + +void MapWindow::setPlayerPos(const float x, const float y) +{ + if (mGlobal || mVisible) return; + MyGUI::IntSize size = mLocalMap->getCanvasSize(); + MyGUI::IntPoint middle = MyGUI::IntPoint((1/3.f + x/3.f)*size.width,(1/3.f + y/3.f)*size.height); + MyGUI::IntCoord viewsize = mLocalMap->getCoord(); + MyGUI::IntPoint pos(0.5*viewsize.width - middle.left, 0.5*viewsize.height - middle.top); + mLocalMap->setViewOffset(pos); + + mPlayerArrow->setPosition(MyGUI::IntPoint(x*512-16, y*512-16)); +} + +void MapWindow::setPlayerDir(const float x, const float y) +{ + if (!mVisible) return; + MyGUI::ISubWidget* main = mPlayerArrow->getSubWidgetMain(); + MyGUI::RotatingSkin* rotatingSubskin = main->castType(); + rotatingSubskin->setCenter(MyGUI::IntPoint(16,16)); + float angle = std::atan2(x,y); + rotatingSubskin->setAngle(angle); +} + +void MapWindow::onDragStart(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id) +{ + if (_id!=MyGUI::MouseButton::Left) return; + if (!mGlobal) + mLastDragPos = MyGUI::IntPoint(_left, _top); +} + +void MapWindow::onMouseDrag(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id) +{ + if (_id!=MyGUI::MouseButton::Left) return; + + if (!mGlobal) + { + MyGUI::IntPoint diff = MyGUI::IntPoint(_left, _top) - mLastDragPos; + mLocalMap->setViewOffset( mLocalMap->getViewOffset() + diff ); + + mLastDragPos = MyGUI::IntPoint(_left, _top); + } +} + +void MapWindow::onWorldButtonClicked(MyGUI::Widget* _sender) +{ + mGlobal = !mGlobal; + mGlobalMap->setVisible(mGlobal); + mLocalMap->setVisible(!mGlobal); + + mButton->setCaption( mGlobal ? "Local" : "World" ); +} + +void LocalMapBase::init(MyGUI::ScrollView* widget, OEngine::GUI::Layout* layout) +{ + mLocalMap = widget; + mLayout = layout; +} + +void LocalMapBase::setCellPrefix(const std::string& prefix) +{ + mPrefix = prefix; + mChanged = true; +} + +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 + 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(x + (mx-1)) + "_" + + boost::lexical_cast(y + (interior ? (my-1) : -1*(my-1))); + + 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; +} + diff --git a/apps/openmw/mwgui/layouts.hpp b/apps/openmw/mwgui/layouts.hpp index 71181e48d..8d9a41a22 100644 --- a/apps/openmw/mwgui/layouts.hpp +++ b/apps/openmw/mwgui/layouts.hpp @@ -14,6 +14,8 @@ #include "../mwmechanics/stat.hpp" #include "window_base.hpp" +#include + /* This file contains classes corresponding to window layouts defined in resources/mygui/ *.xml. @@ -29,7 +31,25 @@ namespace MWGui { - class HUD : public OEngine::GUI::Layout + class LocalMapBase + { + public: + void init(MyGUI::ScrollView* widget, OEngine::GUI::Layout* layout); + + void setCellPrefix(const std::string& prefix); + void setActiveCell(const int x, const int y, bool interior=false); + + protected: + int mCurX, mCurY; + bool mInterior; + MyGUI::ScrollView* mLocalMap; + std::string mPrefix; + bool mChanged; + + OEngine::GUI::Layout* mLayout; + }; + + class HUD : public OEngine::GUI::Layout, public LocalMapBase { public: HUD(int width, int height, int fpsLevel); @@ -43,13 +63,15 @@ namespace MWGui void setFPS(float fps); void setTriangleCount(size_t count); void setBatchCount(size_t count); + void setPlayerDir(const float x, const float y); + void setPlayerPos(const float x, const float y); MyGUI::ProgressPtr health, magicka, stamina; MyGUI::ImageBox *weapImage, *spellImage; MyGUI::ProgressPtr weapStatus, spellStatus; MyGUI::WidgetPtr effectBox; MyGUI::ImageBox* effect1; - MyGUI::ImageBox* minimap; + MyGUI::ScrollView* minimap; MyGUI::ImageBox* compass; MyGUI::ImageBox* crosshair; @@ -59,24 +81,27 @@ namespace MWGui MyGUI::TextBox* batchcounter; }; - class MapWindow : public OEngine::GUI::Layout + class MapWindow : public OEngine::GUI::Layout, public LocalMapBase { public: - MapWindow() - : Layout("openmw_map_window_layout.xml") - { - setCoord(500,0,320,300); - setText("WorldButton", "World"); - setImage("Compass", "compass.dds"); + MapWindow(); - // Obviously you should override this later on - setCellName("No Cell Loaded"); - } + void setVisible(bool b); + void setPlayerPos(const float x, const float y); + void setPlayerDir(const float x, const float y); + void setCellName(const std::string& cellName); + + private: + void onDragStart(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id); + void onMouseDrag(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id); + void onWorldButtonClicked(MyGUI::Widget* _sender); - void setCellName(const std::string& cellName) - { - static_cast(mMainWidget)->setCaption(cellName); - } + MyGUI::ScrollView* mGlobalMap; + MyGUI::ImageBox* mPlayerArrow; + MyGUI::Button* mButton; + MyGUI::IntPoint mLastDragPos; + bool mVisible; + bool mGlobal; }; class MainMenu : public OEngine::GUI::Layout diff --git a/apps/openmw/mwgui/window_manager.cpp b/apps/openmw/mwgui/window_manager.cpp index 8ac4b11d7..fa6dedc77 100644 --- a/apps/openmw/mwgui/window_manager.cpp +++ b/apps/openmw/mwgui/window_manager.cpp @@ -400,3 +400,47 @@ const ESMS::ESMStore& WindowManager::getStore() const { return environment.mWorld->getStore(); } + +void WindowManager::changeCell(MWWorld::Ptr::CellStore* cell) +{ + if (!(cell->cell->data.flags & ESM::Cell::Interior)) + { + std::string name; + if (cell->cell->name != "") + name = cell->cell->name; + else + name = cell->cell->region; + + map->setCellName( name ); + + map->setCellPrefix("Cell"); + hud->setCellPrefix("Cell"); + map->setActiveCell( cell->cell->data.gridX, cell->cell->data.gridY ); + hud->setActiveCell( cell->cell->data.gridX, cell->cell->data.gridY ); + } + else + { + map->setCellName( cell->cell->name ); + map->setCellPrefix( cell->cell->name ); + hud->setCellPrefix( cell->cell->name ); + } + +} + +void WindowManager::setInteriorMapTexture(const int x, const int y) +{ + map->setActiveCell(x,y, true); + hud->setActiveCell(x,y, true); +} + +void WindowManager::setPlayerPos(const float x, const float y) +{ + map->setPlayerPos(x,y); + hud->setPlayerPos(x,y); +} + +void WindowManager::setPlayerDir(const float x, const float y) +{ + map->setPlayerDir(x,y); + hud->setPlayerDir(x,y); +} diff --git a/apps/openmw/mwgui/window_manager.hpp b/apps/openmw/mwgui/window_manager.hpp index 0124c9239..582f438e8 100644 --- a/apps/openmw/mwgui/window_manager.hpp +++ b/apps/openmw/mwgui/window_manager.hpp @@ -18,6 +18,7 @@ #include #include #include "../mwmechanics/stat.hpp" +#include "../mwworld/ptr.hpp" #include "mode.hpp" namespace MyGUI @@ -152,6 +153,12 @@ namespace MWGui void setBounty (int bounty); ///< set the current bounty value void updateSkillArea(); ///< update display of skills, factions, birth sign, reputation and bounty + 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 setInteriorMapTexture(const int x, const int y); + ///< set the index of the map texture that should be used (for interiors) template void removeDialog(T*& dialog); ///< Casts to OEngine::GUI::Layout and calls removeDialog, then resets pointer to nullptr. diff --git a/apps/openmw/mwrender/localmap.cpp b/apps/openmw/mwrender/localmap.cpp new file mode 100644 index 000000000..b83a98220 --- /dev/null +++ b/apps/openmw/mwrender/localmap.cpp @@ -0,0 +1,312 @@ +#include "localmap.hpp" +#include "renderingmanager.hpp" + +#include "../mwworld/environment.hpp" +#include "../mwgui/window_manager.hpp" + +#include +#include + +using namespace MWRender; +using namespace Ogre; + +LocalMap::LocalMap(OEngine::Render::OgreRenderer* rend, MWWorld::Environment* env) +{ + mRendering = rend; + mEnvironment = env; + + mCellCamera = mRendering->getScene()->createCamera("CellCamera"); + mCellCamera->setProjectionType(PT_ORTHOGRAPHIC); + // look down -y + const float sqrt0pt5 = 0.707106781; + mCellCamera->setOrientation(Quaternion(sqrt0pt5, -sqrt0pt5, 0, 0)); +} + +LocalMap::~LocalMap() +{ + deleteBuffers(); +} + +void LocalMap::deleteBuffers() +{ + for (std::map::iterator it=mBuffers.begin(); + it != mBuffers.end(); ++it) + { + delete it->second; + } + mBuffers.clear(); +} + +void LocalMap::saveTexture(const std::string& texname, const std::string& filename) +{ + TexturePtr tex = TextureManager::getSingleton().getByName(texname); + if (tex.isNull()) return; + HardwarePixelBufferSharedPtr readbuffer = tex->getBuffer(); + readbuffer->lock(HardwareBuffer::HBL_NORMAL ); + const PixelBox &readrefpb = readbuffer->getCurrentLock(); + uchar *readrefdata = static_cast(readrefpb.data); + + Image img; + img = img.loadDynamicImage (readrefdata, tex->getWidth(), + tex->getHeight(), tex->getFormat()); + img.save("./" + filename); + + readbuffer->unlock(); +} + +std::string LocalMap::coordStr(const int x, const int y) +{ + return StringConverter::toString(x) + "_" + StringConverter::toString(y); +} + +void LocalMap::saveFogOfWar(MWWorld::Ptr::CellStore* cell) +{ + if (!mInterior) + { + /*saveTexture("Cell_"+coordStr(mCellX, mCellY)+"_fog", + "Cell_"+coordStr(mCellX, mCellY)+"_fog.png");*/ + } + else + { + 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 + const int segsX = std::ceil( length.x / sSize ); + const int segsY = std::ceil( length.y / sSize ); + + for (int x=0; xcell->data.gridX, cell->cell->data.gridY); + + int x = cell->cell->data.gridX; + int y = cell->cell->data.gridY; + + render((x+0.5)*sSize, (-y-0.5)*sSize, -10000, 10000, sSize, sSize, name); +} + +void LocalMap::requestMap(MWWorld::Ptr::CellStore* cell, + AxisAlignedBox bounds) +{ + 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 length = max-min; + Vector2 center(bounds.getCenter().x, bounds.getCenter().z); + + // divide into segments + const int segsX = std::ceil( length.x / sSize ); + const int segsY = std::ceil( length.y / sSize ); + + mInteriorName = cell->cell->name; + + for (int x=0; xcell->name + "_" + coordStr(x,y)); + } + } +} + +void LocalMap::render(const float x, const float y, + const float zlow, const float zhigh, + const float xw, const float yw, const std::string& texture) +{ + // disable fog + // changing FOG_MODE is not a solution when using shaders, thus we have to push linear start/end + const float fStart = mRendering->getScene()->getFogStart(); + const float fEnd = mRendering->getScene()->getFogEnd(); + const ColourValue& clr = mRendering->getScene()->getFogColour(); + mRendering->getScene()->setFog(FOG_LINEAR, clr, 0, 1000000, 10000000); + + // make everything visible + mRendering->getScene()->setAmbientLight(ColourValue(1,1,1)); + + mCellCamera->setPosition(Vector3(x, zhigh+100000, y)); + //mCellCamera->setFarClipDistance( (zhigh-zlow) * 1.1 ); + mCellCamera->setFarClipDistance(0); // infinite + + mCellCamera->setOrthoWindow(xw, yw); + + TexturePtr tex; + // try loading from memory + tex = TextureManager::getSingleton().getByName(texture); + if (tex.isNull()) + { + // try loading from disk + //if (boost::filesystem::exists(texture+".jpg")) + //{ + /// \todo + //} + //else + { + // render + tex = TextureManager::getSingleton().createManual( + texture, + ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, + TEX_TYPE_2D, + xw*sMapResolution/sSize, yw*sMapResolution/sSize, + 0, + PF_R8G8B8, + TU_RENDERTARGET); + + RenderTarget* rtt = tex->getBuffer()->getRenderTarget(); + rtt->setAutoUpdated(false); + Viewport* vp = rtt->addViewport(mCellCamera); + vp->setOverlaysEnabled(false); + vp->setShadowsEnabled(false); + vp->setBackgroundColour(ColourValue(0, 0, 0)); + //vp->setVisibilityMask( ... ); + + rtt->update(); + + // create "fog of war" texture + TexturePtr tex2 = TextureManager::getSingleton().createManual( + texture + "_fog", + ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, + TEX_TYPE_2D, + xw*sFogOfWarResolution/sSize, yw*sFogOfWarResolution/sSize, + 0, + PF_A8R8G8B8, + TU_DYNAMIC_WRITE_ONLY_DISCARDABLE); + + // create a buffer to use for dynamic operations + uint32* buffer = new uint32[sFogOfWarResolution*sFogOfWarResolution]; + + // initialize to (0, 0, 0, 1) + uint32* pointer = buffer; + for (int p=0; pgetBuffer()->lock(HardwareBuffer::HBL_DISCARD), buffer, sFogOfWarResolution*sFogOfWarResolution*4); + tex2->getBuffer()->unlock(); + + mBuffers[texture] = buffer; + + // save to cache for next time + //rtt->writeContentsToFile("./" + texture + ".jpg"); + } + } + + + // re-enable fog + mRendering->getScene()->setFog(FOG_LINEAR, clr, 0, fStart, fEnd); +} + +void LocalMap::updatePlayer (const Ogre::Vector3& position, const Ogre::Vector3& direction) +{ + if (sFogOfWarSkip != 0) + { + static int count=0; + if (++count % sFogOfWarSkip != 0) + return; + } + + // retrieve the x,y grid coordinates the player is in + int x,y; + Vector2 pos(position.x, position.z); + if (!mInterior) + { + x = std::ceil(pos.x / sSize)-1; + y = std::ceil(-pos.y / sSize)-1; + mCellX = x; + mCellY = y; + } + 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; + + mEnvironment->mWindowManager->setInteriorMapTexture(x,y); + } + + // convert from world coordinates to texture UV coordinates + float u,v; + std::string texName; + if (!mInterior) + { + 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); + + // explore radius (squared) + const float sqrExploreRadius = 0.01 * sFogOfWarResolution*sFogOfWarResolution; + + // get the appropriate fog of war texture + TexturePtr tex = TextureManager::getSingleton().getByName(texName+"_fog"); + if (!tex.isNull()) + { + // get its buffer + if (mBuffers.find(texName) == mBuffers.end()) return; + uint32* buffer = mBuffers[texName]; + uint32* pointer = buffer; + for (int texV = 0; texV> 24); + alpha = std::min( alpha, (uint8) (std::max(0.f, std::min(1.f, (sqrDist/sqrExploreRadius)))*255) ); + *((uint32*)pointer) = (alpha << 24); + + // move to next texel + ++pointer; + } + } + + // copy to the texture + memcpy(tex->getBuffer()->lock(HardwareBuffer::HBL_DISCARD), buffer, sFogOfWarResolution*sFogOfWarResolution*4); + tex->getBuffer()->unlock(); + } +} diff --git a/apps/openmw/mwrender/localmap.hpp b/apps/openmw/mwrender/localmap.hpp new file mode 100644 index 000000000..3bef475ea --- /dev/null +++ b/apps/openmw/mwrender/localmap.hpp @@ -0,0 +1,100 @@ +#ifndef _GAME_RENDER_LOCALMAP_H +#define _GAME_RENDER_LOCALMAP_H + +#include "../mwworld/ptr.hpp" + +#include + +namespace MWWorld +{ + class Environment; +} + +namespace MWRender +{ + /// + /// \brief Local map rendering + /// + class LocalMap + { + public: + LocalMap(OEngine::Render::OgreRenderer*, MWWorld::Environment* env); + ~LocalMap(); + + /** + * Request the local map for an exterior cell. + * @remarks It will either be loaded from a disk cache, + * or rendered if it is not already cached. + * @param exterior cell + */ + void requestMap (MWWorld::Ptr::CellStore* cell); + + /** + * Request the local map for an interior cell. + * @remarks It will either be loaded from a disk cache, + * or rendered if it is not already cached. + * @param interior cell + * @param bounding box of the cell + */ + void requestMap (MWWorld::Ptr::CellStore* cell, + Ogre::AxisAlignedBox bounds); + + /** + * Set the position & direction of the player. + * @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) + */ + void updatePlayer (const Ogre::Vector3& position, const Ogre::Vector3& direction); + + /** + * Save the fog of war for the current cell to disk. + * @remarks This should be called before loading a + * new cell, as well as when the game is quit. + * @param current cell + */ + void saveFogOfWar(MWWorld::Ptr::CellStore* cell); + + private: + OEngine::Render::OgreRenderer* mRendering; + MWWorld::Environment* mEnvironment; + + // 1024*1024 pixels for a cell + static const int sMapResolution = 1024; + + // the dynamic texture is a bottleneck, so don't set this too high + static const int sFogOfWarResolution = 32; + + // frames to skip before rendering fog of war + static const int sFogOfWarSkip = 2; + + // size of a map segment (for exteriors, 1 cell) + static const int sSize = 8192; + + Ogre::Camera* mCellCamera; + + void render(const float x, const float y, + const float zlow, const float zhigh, + const float xw, const float yw, + const std::string& texture); + + void saveTexture(const std::string& texname, const std::string& filename); + + std::string coordStr(const int x, const int y); + + // a buffer for the "fog of war" texture of the current cell. + // interior cells could be divided into multiple textures, + // so we store in a map. + std::map mBuffers; + + void deleteBuffers(); + + bool mInterior; + int mCellX, mCellY; + Ogre::AxisAlignedBox mBounds; + std::string mInteriorName; + }; + +} +#endif diff --git a/apps/openmw/mwrender/objects.cpp b/apps/openmw/mwrender/objects.cpp index 717064ada..e4e721227 100644 --- a/apps/openmw/mwrender/objects.cpp +++ b/apps/openmw/mwrender/objects.cpp @@ -109,6 +109,9 @@ void Objects::insertMesh (const MWWorld::Ptr& ptr, const std::string& mesh) // If it is set too low: // - there will be too many batches. sg->setRegionDimensions(Ogre::Vector3(2500,2500,2500)); + + mBounds[ptr.getCell()] = Ogre::AxisAlignedBox::BOX_NULL; + mBounds[ptr.getCell()].merge(ent->getBoundingBox()); } else { @@ -116,6 +119,7 @@ void Objects::insertMesh (const MWWorld::Ptr& ptr, const std::string& mesh) } sg->addEntity(ent,insert->_getDerivedPosition(),insert->_getDerivedOrientation(),insert->_getDerivedScale()); + mBounds[ptr.getCell()].merge(insert->_getDerivedPosition()); mRenderer.getScene()->destroyEntity(ent); } @@ -202,6 +206,9 @@ void Objects::removeCell(MWWorld::Ptr::CellStore* store) mRenderer.getScene()->destroyStaticGeometry (sg); sg = 0; } + + if(mBounds.find(store) != mBounds.end()) + mBounds.erase(store); } void Objects::buildStaticGeometry(ESMS::CellStore& cell) @@ -212,3 +219,8 @@ void Objects::buildStaticGeometry(ESMS::CellStore& cell) sg->build(); } } + +Ogre::AxisAlignedBox Objects::getDimensions(MWWorld::Ptr::CellStore* cell) +{ + return mBounds[cell]; +} diff --git a/apps/openmw/mwrender/objects.hpp b/apps/openmw/mwrender/objects.hpp index d58455b9f..1ca81331d 100644 --- a/apps/openmw/mwrender/objects.hpp +++ b/apps/openmw/mwrender/objects.hpp @@ -14,6 +14,7 @@ class Objects{ OEngine::Render::OgreRenderer &mRenderer; std::map mCellSceneNodes; std::map mStaticGeometry; + std::map mBounds; Ogre::SceneNode* mMwRoot; bool mIsStatic; static int uniqueID; @@ -42,6 +43,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); + Ogre::AxisAlignedBox getDimensions(MWWorld::Ptr::CellStore*); + ///< get a bounding box that encloses all objects in the specified cell + bool deleteObject (const MWWorld::Ptr& ptr); ///< \return found? diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 4f84d90a9..e2aea19c6 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -55,6 +55,8 @@ RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const mPlayer = new MWRender::Player (mRendering.getCamera(), playerNode); mSun = 0; + + mLocalMap = new MWRender::LocalMap(&mRendering, &environment); } RenderingManager::~RenderingManager () @@ -62,6 +64,7 @@ RenderingManager::~RenderingManager () //TODO: destroy mSun? delete mPlayer; delete mSkyManager; + delete mLocalMap; } MWRender::SkyManager* RenderingManager::getSkyManager() @@ -137,6 +140,8 @@ void RenderingManager::update (float duration){ mSkyManager->update(duration); mRendering.update(duration); + + mLocalMap->updatePlayer( mRendering.getCamera()->getRealPosition(), mRendering.getCamera()->getRealDirection() ); } void RenderingManager::skyEnable () @@ -327,4 +332,17 @@ void RenderingManager::setGlare(bool glare) mSkyManager->setGlare(glare); } +void RenderingManager::requestMap(MWWorld::Ptr::CellStore* cell) +{ + if (!(cell->cell->data.flags & ESM::Cell::Interior)) + mLocalMap->requestMap(cell); + else + mLocalMap->requestMap(cell, mObjects.getDimensions(cell)); +} + +void RenderingManager::preCellChange(MWWorld::Ptr::CellStore* cell) +{ + mLocalMap->saveFogOfWar(cell); +} + } // namespace diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index d84ee43e0..78a1d2fdb 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -24,6 +24,7 @@ #include "objects.hpp" #include "actors.hpp" #include "player.hpp" +#include "localmap.hpp" namespace Ogre { @@ -75,6 +76,9 @@ class RenderingManager: private RenderingInterface { /// when rebatching is needed and update automatically at the end of each frame. void cellAdded (MWWorld::Ptr::CellStore *store); + void preCellChange (MWWorld::Ptr::CellStore* store); + ///< this event is fired immediately before changing cell + void addObject (const MWWorld::Ptr& ptr); void removeObject (const MWWorld::Ptr& ptr); @@ -102,6 +106,9 @@ class RenderingManager: private RenderingInterface { int skyGetSecundaPhase() const; void skySetMoonColour (bool red); void configureAmbient(ESMS::CellStore &mCell); + + void requestMap (MWWorld::Ptr::CellStore* cell); + ///< request the local map for a cell /// configure fog according to cell void configureFog(ESMS::CellStore &mCell); @@ -148,6 +155,8 @@ class RenderingManager: private RenderingInterface { MWRender::Player *mPlayer; MWRender::Debugging mDebugging; + + MWRender::LocalMap* mLocalMap; }; } diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 47d5f1a2d..22955bf32 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -6,6 +6,8 @@ #include "../mwsound/soundmanager.hpp" +#include "../mwgui/window_manager.hpp" + #include "ptr.hpp" #include "environment.hpp" #include "player.hpp" @@ -101,7 +103,8 @@ namespace MWWorld insertCell(*cell, mEnvironment); mRendering.cellAdded (cell); mRendering.configureAmbient(*cell); - + mRendering.requestMap(cell); + mRendering.configureAmbient(*cell); } @@ -117,10 +120,14 @@ namespace MWWorld // TODO orientation mEnvironment.mMechanicsManager->addActor (mWorld->getPlayer().getPlayer()); mEnvironment.mMechanicsManager->watchActor (mWorld->getPlayer().getPlayer()); + + mEnvironment.mWindowManager->changeCell( mCurrentCell ); } void Scene::changeCell (int X, int Y, const ESM::Position& position, bool adjustPlayerPos) { + mRendering.preCellChange(mCurrentCell); + // remove active mEnvironment.mMechanicsManager->removeActor (mWorld->getPlayer().getPlayer()); diff --git a/files/mygui/CMakeLists.txt b/files/mygui/CMakeLists.txt index 9ea84747c..9a6cde7ba 100644 --- a/files/mygui/CMakeLists.txt +++ b/files/mygui/CMakeLists.txt @@ -42,6 +42,7 @@ configure_file("${SDIR}/openmw_layers.xml" "${DDIR}/openmw_layers.xml" COPYONLY) configure_file("${SDIR}/openmw_mainmenu_layout.xml" "${DDIR}/openmw_mainmenu_layout.xml" COPYONLY) configure_file("${SDIR}/openmw_mainmenu_skin.xml" "${DDIR}/openmw_mainmenu_skin.xml" COPYONLY) configure_file("${SDIR}/openmw_map_window_layout.xml" "${DDIR}/openmw_map_window_layout.xml" COPYONLY) +configure_file("${SDIR}/openmw_map_window_skin.xml" "${DDIR}/openmw_map_window_skin.xml" COPYONLY) configure_file("${SDIR}/openmw.pointer.xml" "${DDIR}/openmw.pointer.xml" COPYONLY) configure_file("${SDIR}/openmw_progress.skin.xml" "${DDIR}/openmw_progress.skin.xml" COPYONLY) configure_file("${SDIR}/openmw_stats_window_layout.xml" "${DDIR}/openmw_stats_window_layout.xml" COPYONLY) diff --git a/files/mygui/core.skin b/files/mygui/core.skin index 83dcd4b9f..28838c234 100644 --- a/files/mygui/core.skin +++ b/files/mygui/core.skin @@ -14,4 +14,7 @@ + + + diff --git a/files/mygui/core.xml b/files/mygui/core.xml index 30e01a8f3..5bec13aef 100644 --- a/files/mygui/core.xml +++ b/files/mygui/core.xml @@ -20,6 +20,7 @@ + diff --git a/files/mygui/openmw_hud_layout.xml b/files/mygui/openmw_hud_layout.xml index 86f4df172..20370770e 100644 --- a/files/mygui/openmw_hud_layout.xml +++ b/files/mygui/openmw_hud_layout.xml @@ -38,10 +38,48 @@ - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/files/mygui/openmw_list.skin.xml b/files/mygui/openmw_list.skin.xml index c3b97c752..98390367c 100644 --- a/files/mygui/openmw_list.skin.xml +++ b/files/mygui/openmw_list.skin.xml @@ -197,7 +197,7 @@ - + @@ -233,7 +233,7 @@ - + diff --git a/files/mygui/openmw_map_window_layout.xml b/files/mygui/openmw_map_window_layout.xml index 011487de7..f5c2c9991 100644 --- a/files/mygui/openmw_map_window_layout.xml +++ b/files/mygui/openmw_map_window_layout.xml @@ -2,8 +2,60 @@ - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/files/mygui/openmw_map_window_skin.xml b/files/mygui/openmw_map_window_skin.xml new file mode 100644 index 000000000..fbb7af7ae --- /dev/null +++ b/files/mygui/openmw_map_window_skin.xml @@ -0,0 +1,11 @@ + + + + + + + + + + +