Merge remote-tracking branch 'scrawl/master'

deque
Marc Zinnschlag 11 years ago
commit 368c868623

@ -200,8 +200,8 @@ namespace MWBase
virtual bool getFullHelp() const = 0;
virtual void setInteriorMapTexture(const int x, const int y) = 0;
///< set the index of the map texture that should be used (for interiors)
virtual void setActiveMap(int x, int y, bool interior) = 0;
///< set the indices of the map texture that should be used
/// sets the visibility of the drowning bar
virtual void setDrowningBarVisibility(bool visible) = 0;

@ -65,11 +65,11 @@ namespace MWGui
{
MyGUI::ImageBox* map = mLocalMap->createWidget<MyGUI::ImageBox>("ImageBox",
MyGUI::IntCoord(mx*widgetSize, my*widgetSize, widgetSize, widgetSize),
MyGUI::Align::Top | MyGUI::Align::Left, "Map_" + boost::lexical_cast<std::string>(mx) + "_" + boost::lexical_cast<std::string>(my));
MyGUI::Align::Top | MyGUI::Align::Left);
MyGUI::ImageBox* fog = map->createWidget<MyGUI::ImageBox>("ImageBox",
MyGUI::IntCoord(0, 0, widgetSize, widgetSize),
MyGUI::Align::Top | MyGUI::Align::Left, "Map_" + boost::lexical_cast<std::string>(mx) + "_" + boost::lexical_cast<std::string>(my) + "_fog");
MyGUI::Align::Top | MyGUI::Align::Left);
if (!mMapDragAndDrop)
{
@ -93,11 +93,6 @@ namespace MWGui
{
mFogOfWar = !mFogOfWar;
applyFogOfWar();
// clear all previous door markers
for (std::vector<MyGUI::Widget*>::iterator it = mDoorMarkerWidgets.begin(); it != mDoorMarkerWidgets.end(); ++it)
MyGUI::Gui::getInstance().destroyWidget(*it);
mDoorMarkerWidgets.clear();
}
void LocalMapBase::applyFogOfWar()
@ -165,8 +160,9 @@ namespace MWGui
markerPos.cellX = cellX;
markerPos.cellY = cellY;
widgetPos = MyGUI::IntPoint(nX * 512 + (1+cellX-mCurX) * 512,
nY * 512 + (1+cellY-mCurY) * 512);
// Image space is -Y up, cells are Y up
widgetPos = MyGUI::IntPoint(nX * 512 + (1+(cellX-mCurX)) * 512,
nY * 512 + (1-(cellY-mCurY)) * 512);
}
markerPos.nX = nX;
@ -241,7 +237,7 @@ namespace MWGui
8, 8);
++counter;
MyGUI::Button* markerWidget = mLocalMap->createWidget<MyGUI::Button>("ButtonImage",
widgetCoord, MyGUI::Align::Default, "Door" + boost::lexical_cast<std::string>(counter));
widgetCoord, MyGUI::Align::Default);
markerWidget->setImageResource("DoorMarker");
markerWidget->setUserString("ToolTipType", "Layout");
markerWidget->setUserString("ToolTipLayout", "TextToolTipOneLine");
@ -343,7 +339,7 @@ namespace MWGui
8, 8);
++counter;
MyGUI::ImageBox* markerWidget = mLocalMap->createWidget<MyGUI::ImageBox>("ImageBox",
widgetCoord, MyGUI::Align::Default, "Marker" + boost::lexical_cast<std::string>(counter));
widgetCoord, MyGUI::Align::Default);
markerWidget->setImageTexture(markerTexture);
markerWidget->setUserString("IsMarker", "true");
markerWidget->setUserData(markerPos);
@ -375,7 +371,7 @@ namespace MWGui
widgetPos.top - 4,
8, 8);
MyGUI::ImageBox* markerWidget = mLocalMap->createWidget<MyGUI::ImageBox>("ImageBox",
widgetCoord, MyGUI::Align::Default, "MarkerMarked");
widgetCoord, MyGUI::Align::Default);
markerWidget->setImageTexture("textures\\menu_map_smark.dds");
markerWidget->setUserString("IsMarker", "true");
markerWidget->setUserData(markerPos);
@ -447,7 +443,7 @@ namespace MWGui
static int _counter=0;
MyGUI::Button* markerWidget = mGlobalMapOverlay->createWidget<MyGUI::Button>("ButtonImage",
widgetCoord, MyGUI::Align::Default, "Door" + boost::lexical_cast<std::string>(_counter));
widgetCoord, MyGUI::Align::Default);
markerWidget->setImageResource("DoorMarker");
markerWidget->setUserString("ToolTipType", "Layout");
markerWidget->setUserString("ToolTipLayout", "TextToolTipOneLine");

@ -766,8 +766,6 @@ namespace MWGui
mMap->setCellPrefix("Cell");
mHud->setCellPrefix("Cell");
mMap->setActiveCell (cell->getCell()->getGridX(), cell->getCell()->getGridY());
mHud->setActiveCell (cell->getCell()->getGridX(), cell->getCell()->getGridY());
}
else
{
@ -783,10 +781,10 @@ namespace MWGui
}
}
void WindowManager::setInteriorMapTexture(const int x, const int y)
void WindowManager::setActiveMap(int x, int y, bool interior)
{
mMap->setActiveCell(x,y, true);
mHud->setActiveCell(x,y, true);
mMap->setActiveCell(x,y, interior);
mHud->setActiveCell(x,y, interior);
}
void WindowManager::setPlayerPos(const float x, const float y)

@ -192,8 +192,8 @@ namespace MWGui
virtual void toggleFullHelp(); ///< show extra info in item tooltips (owner, script)
virtual bool getFullHelp() const;
virtual void setInteriorMapTexture(const int x, const int y);
///< set the index of the map texture that should be used (for interiors)
virtual void setActiveMap(int x, int y, bool interior);
///< set the indices of the map texture that should be used
/// sets the visibility of the drowning bar
virtual void setDrowningBarVisibility(bool visible);

@ -9,6 +9,8 @@
#include <OgreRenderTexture.h>
#include <OgreViewport.h>
#include <components/esm/fogstate.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
#include "../mwbase/windowmanager.hpp"
@ -42,11 +44,28 @@ LocalMap::LocalMap(OEngine::Render::OgreRenderer* rend, MWRender::RenderingManag
mLight->setDirection (Ogre::Vector3(0.3, 0.3, -0.7));
mLight->setVisible (false);
mLight->setDiffuseColour (ColourValue(0.7,0.7,0.7));
mRenderTexture = TextureManager::getSingleton().createManual(
"localmap/rtt",
ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
TEX_TYPE_2D,
sMapResolution, sMapResolution,
0,
PF_R8G8B8,
TU_RENDERTARGET);
mRenderTarget = mRenderTexture->getBuffer()->getRenderTarget();
mRenderTarget->setAutoUpdated(false);
Viewport* vp = mRenderTarget->addViewport(mCellCamera);
vp->setOverlaysEnabled(false);
vp->setShadowsEnabled(false);
vp->setBackgroundColour(ColourValue(0, 0, 0));
vp->setVisibilityMask(RV_Map);
vp->setMaterialScheme("local_map");
}
LocalMap::~LocalMap()
{
deleteBuffers();
}
const Ogre::Vector2 LocalMap::rotatePoint(const Ogre::Vector2& p, const Ogre::Vector2& c, const float angle)
@ -55,59 +74,83 @@ const Ogre::Vector2 LocalMap::rotatePoint(const Ogre::Vector2& p, const Ogre::Ve
Math::Sin(angle) * (p.x - c.x) + Math::Cos(angle) * (p.y - c.y) + c.y);
}
void LocalMap::deleteBuffers()
{
mBuffers.clear();
}
void LocalMap::saveTexture(const std::string& texname, const std::string& filename)
std::string LocalMap::coordStr(const int x, const int y)
{
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<uchar*>(readrefpb.data);
Image img;
img = img.loadDynamicImage (readrefdata, tex->getWidth(),
tex->getHeight(), tex->getFormat());
img.save("./" + filename);
readbuffer->unlock();
return StringConverter::toString(x) + "_" + StringConverter::toString(y);
}
std::string LocalMap::coordStr(const int x, const int y)
void LocalMap::clear()
{
return StringConverter::toString(x) + "_" + StringConverter::toString(y);
// Not actually removing the Textures here. That doesnt appear to work properly. It seems MyGUI still keeps some pointers.
mBuffers.clear();
}
void LocalMap::saveFogOfWar(MWWorld::CellStore* cell)
{
if (!mInterior)
{
/*saveTexture("Cell_"+coordStr(mCellX, mCellY)+"_fog",
"Cell_"+coordStr(mCellX, mCellY)+"_fog.png");*/
std::string textureName = "Cell_"+coordStr(cell->getCell()->getGridX(), cell->getCell()->getGridY())+"_fog";
std::auto_ptr<ESM::FogState> fog (new ESM::FogState());
fog->mFogTextures.push_back(ESM::FogTexture());
TexturePtr tex = TextureManager::getSingleton().getByName(textureName);
if (tex.isNull())
return;
Ogre::Image image;
tex->convertToImage(image);
Ogre::DataStreamPtr encoded = image.encode("tga");
fog->mFogTextures.back().mImageData.resize(encoded->size());
encoded->read(&fog->mFogTextures.back().mImageData[0], encoded->size());
cell->setFog(fog.release());
}
else
{
Vector2 min(mBounds.getMinimum().x, mBounds.getMinimum().y);
Vector2 max(mBounds.getMaximum().x, mBounds.getMaximum().y);
Vector2 length = max-min;
// divide into segments
const int segsX = std::ceil( length.x / sSize );
const int segsY = std::ceil( length.y / sSize );
mInteriorName = cell->getCell()->mName;
std::auto_ptr<ESM::FogState> fog (new ESM::FogState());
fog->mBounds.mMinX = mBounds.getMinimum().x;
fog->mBounds.mMaxX = mBounds.getMaximum().x;
fog->mBounds.mMinY = mBounds.getMinimum().y;
fog->mBounds.mMaxY = mBounds.getMaximum().y;
fog->mNorthMarkerAngle = mAngle;
fog->mFogTextures.reserve(segsX*segsY);
for (int x=0; x<segsX; ++x)
{
for (int y=0; y<segsY; ++y)
{
/*saveTexture(
mInteriorName + "_" + coordStr(x,y) + "_fog",
mInteriorName + "_" + coordStr(x,y) + "_fog.png");*/
std::string textureName = cell->getCell()->mName + "_" + coordStr(x,y) + "_fog";
TexturePtr tex = TextureManager::getSingleton().getByName(textureName);
if (tex.isNull())
return;
Ogre::Image image;
tex->convertToImage(image);
fog->mFogTextures.push_back(ESM::FogTexture());
Ogre::DataStreamPtr encoded = image.encode("tga");
fog->mFogTextures.back().mImageData.resize(encoded->size());
encoded->read(&fog->mFogTextures.back().mImageData[0], encoded->size());
fog->mFogTextures.back().mX = x;
fog->mFogTextures.back().mY = y;
}
}
cell->setFog(fog.release());
}
}
@ -126,29 +169,34 @@ void LocalMap::requestMap(MWWorld::CellStore* cell, float zMin, float zMax)
mCameraPosNode->setPosition(Vector3(0,0,0));
render((x+0.5)*sSize, (y+0.5)*sSize, zMin, zMax, sSize, sSize, name);
if (mBuffers.find(name) == mBuffers.end())
{
if (cell->getFog())
loadFogOfWar(name, cell->getFog()->mFogTextures.back());
else
createFogOfWar(name);
}
}
void LocalMap::requestMap(MWWorld::CellStore* cell,
AxisAlignedBox bounds)
{
// if we're in an empty cell, don't bother rendering anything
// If we're in an empty cell, bail out
// The operations in this function are only valid for finite bounds
if (bounds.isNull ())
return;
mInterior = true;
mBounds = bounds;
float zMin = mBounds.getMinimum().z;
float zMax = mBounds.getMaximum().z;
mBounds = bounds;
// Get the cell's NorthMarker rotation. This is used to rotate the entire map.
const Vector2& north = MWBase::Environment::get().getWorld()->getNorthVector(cell);
Radian angle = Ogre::Math::ATan2 (north.x, north.y);
Radian angle = Ogre::Math::ATan2 (north.x, north.y) + Ogre::Degree(2);
mAngle = angle.valueRadians();
mCellCamera->setOrientation(Quaternion::IDENTITY);
mCameraRotNode->setOrientation(Quaternion(Math::Cos(mAngle/2.f), 0, 0, -Math::Sin(mAngle/2.f)));
// rotate the cell and merge the rotated corners to the bounding box
// Rotate the cell and merge the rotated corners to the bounding box
Vector2 _center(bounds.getCenter().x, bounds.getCenter().y);
Vector3 _c1 = bounds.getCorner(AxisAlignedBox::FAR_LEFT_BOTTOM);
Vector3 _c2 = bounds.getCorner(AxisAlignedBox::FAR_RIGHT_BOTTOM);
@ -168,9 +216,48 @@ void LocalMap::requestMap(MWWorld::CellStore* cell,
mBounds.merge(Vector3(c3.x, c3.y, 0));
mBounds.merge(Vector3(c4.x, c4.y, 0));
// apply a little padding
mBounds.setMinimum (mBounds.getMinimum() - Vector3(500,500,0));
mBounds.setMaximum (mBounds.getMaximum() + Vector3(500,500,0));
// Do NOT change padding! This will break older savegames.
// If the padding really needs to be changed, then it must be saved in the ESM::FogState and
// assume the old (500) value as default for older savegames.
const int padding = 500;
// Apply a little padding
mBounds.setMinimum (mBounds.getMinimum() - Vector3(padding,padding,0));
mBounds.setMaximum (mBounds.getMaximum() + Vector3(padding,padding,0));
float zMin = mBounds.getMinimum().z;
float zMax = mBounds.getMaximum().z;
// If there is fog state in the CellStore (e.g. when it came from a savegame) we need to do some checks
// to see if this state is still valid.
// Both the cell bounds and the NorthMarker rotation could be changed by the content files or exchanged models.
// If they changed by too much (for bounds, < padding is considered acceptable) then parts of the interior might not
// be covered by the map anymore.
// The following code detects this, and discards the CellStore's fog state if it needs to.
if (cell->getFog())
{
ESM::FogState* fog = cell->getFog();
Ogre::Vector3 newMin (fog->mBounds.mMinX, fog->mBounds.mMinY, zMin);
Ogre::Vector3 newMax (fog->mBounds.mMaxX, fog->mBounds.mMaxY, zMax);
Ogre::Vector3 minDiff = newMin - mBounds.getMinimum();
Ogre::Vector3 maxDiff = newMax - mBounds.getMaximum();
if (std::abs(minDiff.x) > 500 || std::abs(minDiff.y) > 500
|| std::abs(maxDiff.x) > 500 || std::abs(maxDiff.y) > 500
|| std::abs(mAngle - fog->mNorthMarkerAngle) > Ogre::Degree(5).valueRadians())
{
// Nuke it
cell->setFog(NULL);
}
else
{
// Looks sane, use it
mBounds = Ogre::AxisAlignedBox(newMin, newMax);
mAngle = fog->mNorthMarkerAngle;
}
}
Vector2 center(mBounds.getCenter().x, mBounds.getCenter().y);
@ -179,6 +266,9 @@ void LocalMap::requestMap(MWWorld::CellStore* cell,
Vector2 length = max-min;
mCellCamera->setOrientation(Quaternion::IDENTITY);
mCameraRotNode->setOrientation(Quaternion(Math::Cos(mAngle/2.f), 0, 0, -Math::Sin(mAngle/2.f)));
mCameraPosNode->setPosition(Vector3(center.x, center.y, 0));
// divide into segments
@ -187,19 +277,96 @@ void LocalMap::requestMap(MWWorld::CellStore* cell,
mInteriorName = cell->getCell()->mName;
int i=0;
for (int x=0; x<segsX; ++x)
{
for (int y=0; y<segsY; ++y)
{
Vector2 start = min + Vector2(sSize*x,sSize*y);
Vector2 newcenter = start + 4096;
Vector2 newcenter = start + sSize/2;
std::string texturePrefix = cell->getCell()->mName + "_" + coordStr(x,y);
render(newcenter.x - center.x, newcenter.y - center.y, zMin, zMax, sSize, sSize,
cell->getCell()->mName + "_" + coordStr(x,y));
render(newcenter.x - center.x, newcenter.y - center.y, zMin, zMax, sSize, sSize, texturePrefix);
if (!cell->getFog())
createFogOfWar(texturePrefix);
else
{
ESM::FogState* fog = cell->getFog();
// We are using the same bounds and angle as we were using when the textures were originally made. Segments should come out the same.
assert (i < int(fog->mFogTextures.size()));
ESM::FogTexture& esm = fog->mFogTextures[i];
loadFogOfWar(texturePrefix, esm);
}
++i;
}
}
}
void LocalMap::createFogOfWar(const std::string& texturePrefix)
{
const std::string texName = texturePrefix + "_fog";
TexturePtr tex = createFogOfWarTexture(texName);
// create a buffer to use for dynamic operations
std::vector<uint32> buffer;
// initialize to (0, 0, 0, 1)
buffer.resize(sFogOfWarResolution*sFogOfWarResolution, (255 << 24));
// upload to the texture
memcpy(tex->getBuffer()->lock(HardwareBuffer::HBL_DISCARD), &buffer[0], sFogOfWarResolution*sFogOfWarResolution*4);
tex->getBuffer()->unlock();
mBuffers[texturePrefix] = buffer;
}
Ogre::TexturePtr LocalMap::createFogOfWarTexture(const std::string &texName)
{
TexturePtr tex = TextureManager::getSingleton().getByName(texName);
if (tex.isNull())
{
tex = TextureManager::getSingleton().createManual(
texName,
ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
TEX_TYPE_2D,
sFogOfWarResolution, sFogOfWarResolution,
0,
PF_A8R8G8B8,
TU_DYNAMIC_WRITE_ONLY);
}
else
tex->unload();
return tex;
}
void LocalMap::loadFogOfWar (const std::string& texturePrefix, ESM::FogTexture& esm)
{
std::vector<char>& data = esm.mImageData;
Ogre::DataStreamPtr stream(new Ogre::MemoryDataStream(&data[0], data.size()));
Ogre::Image image;
image.load(stream, "tga");
assert (image.getWidth() == sFogOfWarResolution && image.getHeight() == sFogOfWarResolution);
std::string texName = texturePrefix + "_fog";
Ogre::TexturePtr tex = createFogOfWarTexture(texName);
tex->loadImage(image);
// create a buffer to use for dynamic operations
std::vector<uint32> buffer;
buffer.resize(sFogOfWarResolution*sFogOfWarResolution);
memcpy(&buffer[0], image.getData(), image.getSize());
mBuffers[texturePrefix] = buffer;
}
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)
@ -229,51 +396,17 @@ void LocalMap::render(const float x, const float y,
if (tex.isNull())
{
// 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(RV_Map);
vp->setMaterialScheme("local_map");
mRenderTarget->update();
rtt->update();
// create "fog of war" texture
TexturePtr tex2 = TextureManager::getSingleton().createManual(
texture + "_fog",
// create a new texture and blit to it
Ogre::TexturePtr tex = TextureManager::getSingleton().createManual(
texture,
ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
TEX_TYPE_2D,
xw*sFogOfWarResolution/sSize, yw*sFogOfWarResolution/sSize,
sMapResolution, sMapResolution,
0,
PF_A8R8G8B8,
TU_DYNAMIC_WRITE_ONLY_DISCARDABLE);
// create a buffer to use for dynamic operations
std::vector<uint32> buffer;
buffer.resize(sFogOfWarResolution*sFogOfWarResolution);
// initialize to (0, 0, 0, 1)
for (int p=0; p<sFogOfWarResolution*sFogOfWarResolution; ++p)
{
buffer[p] = (255 << 24);
}
memcpy(tex2->getBuffer()->lock(HardwareBuffer::HBL_DISCARD), &buffer[0], sFogOfWarResolution*sFogOfWarResolution*4);
tex2->getBuffer()->unlock();
mBuffers[texture] = buffer;
PF_R8G8B8);
tex->getBuffer()->blit(mRenderTexture->getBuffer());
}
mRenderingManager->enableLights(true);
@ -304,6 +437,9 @@ bool LocalMap::isPositionExplored (float nX, float nY, int x, int y, bool interi
if (mBuffers.find(texName) == mBuffers.end())
return false;
nX = std::max(0.f, std::min(1.f, nX));
nY = std::max(0.f, std::min(1.f, nY));
int texU = (sFogOfWarResolution-1) * nX;
int texV = (sFogOfWarResolution-1) * nY;
@ -339,10 +475,7 @@ void LocalMap::updatePlayer (const Ogre::Vector3& position, const Ogre::Quaterni
mCellX = x;
mCellY = y;
}
else
{
MWBase::Environment::get().getWindowManager()->setInteriorMapTexture(x,y);
}
MWBase::Environment::get().getWindowManager()->setActiveMap(x,y,mInterior);
// convert from world coordinates to texture UV coordinates
std::string texBaseName;
@ -414,6 +547,8 @@ void LocalMap::updatePlayer (const Ogre::Vector3& position, const Ogre::Quaterni
}
// copy to the texture
// NOTE: Could be optimized later. We actually only need to update the region that changed.
// Not a big deal at the moment, the FoW is only 32x32 anyway.
memcpy(tex->getBuffer()->lock(HardwareBuffer::HBL_DISCARD), &aBuffer[0], sFogOfWarResolution*sFogOfWarResolution*4);
tex->getBuffer()->unlock();
}

@ -11,6 +11,11 @@ namespace MWWorld
class CellStore;
}
namespace ESM
{
class FogTexture;
}
namespace MWRender
{
class RenderingManager;
@ -24,6 +29,11 @@ namespace MWRender
LocalMap(OEngine::Render::OgreRenderer*, MWRender::RenderingManager* rendering);
~LocalMap();
/**
* Clear all savegame-specific data (i.e. fog of war textures)
*/
void clear();
/**
* Request the local map for an exterior cell.
* @remarks It will either be loaded from a disk cache,
@ -54,10 +64,8 @@ namespace MWRender
void updatePlayer (const Ogre::Vector3& position, const Ogre::Quaternion& orientation);
/**
* 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
* Save the fog of war for this cell to its CellStore.
* @remarks This should be called when unloading a cell, and for all active cells prior to saving the game.
*/
void saveFogOfWar(MWWorld::CellStore* cell);
@ -76,7 +84,6 @@ namespace MWRender
OEngine::Render::OgreRenderer* mRendering;
MWRender::RenderingManager* mRenderingManager;
// 1024*1024 pixels for a cell
static const int sMapResolution = 512;
// the dynamic texture is a bottleneck, so don't set this too high
@ -104,16 +111,23 @@ namespace MWRender
const float xw, const float yw,
const std::string& texture);
void saveTexture(const std::string& texname, const std::string& filename);
// Creates a fog of war texture and initializes it to full black
void createFogOfWar(const std::string& texturePrefix);
// Loads a fog of war texture from its ESM struct
void loadFogOfWar(const std::string& texturePrefix, ESM::FogTexture& esm); // FogTexture not const because MemoryDataStream doesn't accept it
Ogre::TexturePtr createFogOfWarTexture(const std::string& name);
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.
// A buffer for the "fog of war" textures of the current cell.
// Both interior and exterior maps are possibly divided into multiple textures.
std::map <std::string, std::vector<Ogre::uint32> > mBuffers;
void deleteBuffers();
// The render texture we will use to create the map images
Ogre::TexturePtr mRenderTexture;
Ogre::RenderTarget* mRenderTarget;
bool mInterior;
int mCellX, mCellY;

@ -216,13 +216,14 @@ OEngine::Render::Fader* RenderingManager::getFader()
return mRendering.getFader();
}
MWRender::Camera* RenderingManager::getCamera() const
MWRender::Camera* RenderingManager::getCamera() const
{
return mCamera;
}
void RenderingManager::removeCell (MWWorld::CellStore *store)
{
mLocalMap->saveFogOfWar(store);
mObjects->removeCell(store);
mActors->removeCell(store);
mDebugging->cellRemoved(store);
@ -671,7 +672,7 @@ void RenderingManager::requestMap(MWWorld::CellStore* cell)
mLocalMap->requestMap(cell, mObjects->getDimensions(cell));
}
void RenderingManager::preCellChange(MWWorld::CellStore* cell)
void RenderingManager::writeFog(MWWorld::CellStore* cell)
{
mLocalMap->saveFogOfWar(cell);
}
@ -1057,4 +1058,9 @@ void RenderingManager::spawnEffect(const std::string &model, const std::string &
mEffectManager->addEffect(model, texture, worldPosition, scale);
}
void RenderingManager::clear()
{
mLocalMap->clear();
}
} // namespace

@ -107,12 +107,15 @@ public:
void cellAdded (MWWorld::CellStore *store);
void waterAdded(MWWorld::CellStore *store);
/// Clear all savegame-specific data (i.e. fog of war textures)
void clear();
void enableTerrain(bool enable);
void removeWater();
void preCellChange (MWWorld::CellStore* store);
///< this event is fired immediately before changing cell
/// Write current fog of war for this cell to the CellStore
void writeFog (MWWorld::CellStore* store);
void addObject (const MWWorld::Ptr& ptr);
void removeObject (const MWWorld::Ptr& ptr);

@ -314,6 +314,7 @@ void MWState::StateManager::loadGame (const Character *character, const Slot *sl
case ESM::REC_PLAY:
case ESM::REC_CSTA:
case ESM::REC_WTHR:
case ESM::REC_DYNA:
MWBase::Environment::get().getWorld()->readRecord (reader, n.val, contentFileMap);
break;

@ -78,6 +78,7 @@ void MWWorld::Cells::writeCell (ESM::ESMWriter& writer, CellStore& cell) const
writer.startRecord (ESM::REC_CSTA);
cellState.mId.save (writer);
cellState.save (writer);
cell.writeFog(writer);
cell.writeReferences (writer);
writer.endRecord (ESM::REC_CSTA);
}
@ -319,6 +320,9 @@ bool MWWorld::Cells::readRecord (ESM::ESMReader& reader, int32_t type,
state.load (reader);
cellStore->loadState (state);
if (state.mHasFogOfWar)
cellStore->readFog(reader);
if (cellStore->getState()!=CellStore::State_Loaded)
cellStore->load (mStore, mReader);

@ -11,6 +11,7 @@
#include <components/esm/containerstate.hpp>
#include <components/esm/npcstate.hpp>
#include <components/esm/creaturestate.hpp>
#include <components/esm/fogstate.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
@ -533,6 +534,21 @@ namespace MWWorld
state.mWaterLevel = mWaterLevel;
state.mWaterLevel = mWaterLevel;
state.mHasFogOfWar = (mFogState.get() ? 1 : 0);
}
void CellStore::writeFog(ESM::ESMWriter &writer) const
{
if (mFogState.get())
{
mFogState->save(writer, mCell->mData.mFlags & ESM::Cell::Interior);
}
}
void CellStore::readFog(ESM::ESMReader &reader)
{
mFogState.reset(new ESM::FogState());
mFogState->load(reader);
}
void CellStore::writeReferences (ESM::ESMWriter& writer) const
@ -697,4 +713,14 @@ namespace MWWorld
{
return mPathgridGraph.aStarSearch(start, end);
}
void CellStore::setFog(ESM::FogState *fog)
{
mFogState.reset(fog);
}
ESM::FogState* CellStore::getFog() const
{
return mFogState.get();
}
}

@ -3,22 +3,28 @@
#include <algorithm>
#include <stdexcept>
#include <boost/shared_ptr.hpp>
#include "livecellref.hpp"
#include "esmstore.hpp"
#include "cellreflist.hpp"
#include <components/esm/fogstate.hpp>
#include "../mwmechanics/pathgrid.hpp" // TODO: maybe belongs in mwworld
namespace ESM
{
struct CellState;
struct FogState;
}
namespace MWWorld
{
class Ptr;
/// \brief Mutable state of a cell
class CellStore
{
@ -31,6 +37,11 @@ namespace MWWorld
private:
// Even though fog actually belongs to the player and not cells,
// it makes sense to store it here since we need it once for each cell.
// Note this is NULL until the cell is explored to save some memory
boost::shared_ptr<ESM::FogState> mFogState;
const ESM::Cell *mCell;
State mState;
bool mHasState;
@ -84,6 +95,11 @@ namespace MWWorld
void setWaterLevel (float level);
void setFog (ESM::FogState* fog);
///< \note Takes ownership of the pointer
ESM::FogState* getFog () const;
int count() const;
///< Return total number of references, including deleted ones.
@ -134,6 +150,10 @@ namespace MWWorld
void saveState (ESM::CellState& state) const;
void writeFog (ESM::ESMWriter& writer) const;
void readFog (ESM::ESMReader& reader);
void writeReferences (ESM::ESMWriter& writer) const;
void readReferences (ESM::ESMReader& reader, const std::map<int, int>& contentFileMap);

@ -141,8 +141,8 @@ void ESMStore::setUp()
int ESMStore::countSavedGameRecords() const
{
return
mPotions.getDynamicSize()
return 1 // DYNA (dynamic name counter)
+mPotions.getDynamicSize()
+mArmors.getDynamicSize()
+mBooks.getDynamicSize()
+mClasses.getDynamicSize()
@ -155,6 +155,13 @@ void ESMStore::setUp()
void ESMStore::write (ESM::ESMWriter& writer, Loading::Listener& progress) const
{
writer.startRecord(ESM::REC_DYNA);
writer.startSubRecord("COUN");
writer.writeT(mDynamicCount);
writer.endRecord("COUN");
writer.endRecord(ESM::REC_DYNA);
progress.increaseProgress();
mPotions.write (writer, progress);
mArmors.write (writer, progress);
mBooks.write (writer, progress);
@ -197,6 +204,11 @@ void ESMStore::setUp()
return true;
case ESM::REC_DYNA:
reader.getSubNameIs("COUN");
reader.getHT(mDynamicCount);
return true;
default:
return false;

@ -166,16 +166,17 @@ namespace MWWorld
template <class T>
const T *insert(const T &x) {
std::ostringstream id;
id << "$dynamic" << mDynamicCount++;
Store<T> &store = const_cast<Store<T> &>(get<T>());
if (store.search(x.mId) != 0) {
if (store.search(id.str()) != 0) {
std::ostringstream msg;
msg << "Try to override existing record '" << x.mId << "'";
msg << "Try to override existing record '" << id.str() << "'";
throw std::runtime_error(msg.str());
}
T record = x;
std::ostringstream id;
id << "$dynamic" << mDynamicCount++;
record.mId = id.str();
T *ptr = store.insert(record);
@ -189,10 +190,13 @@ namespace MWWorld
template <class T>
const T *insertStatic(const T &x) {
std::ostringstream id;
id << "$dynamic" << mDynamicCount++;
Store<T> &store = const_cast<Store<T> &>(get<T>());
if (store.search(x.mId) != 0) {
if (store.search(id.str()) != 0) {
std::ostringstream msg;
msg << "Try to override existing record '" << x.mId << "'";
msg << "Try to override existing record '" << id.str() << "'";
throw std::runtime_error(msg.str());
}
T record = x;
@ -225,17 +229,18 @@ namespace MWWorld
template <>
inline const ESM::NPC *ESMStore::insert<ESM::NPC>(const ESM::NPC &npc) {
std::ostringstream id;
id << "$dynamic" << mDynamicCount++;
if (Misc::StringUtils::ciEqual(npc.mId, "player")) {
return mNpcs.insert(npc);
} else if (mNpcs.search(npc.mId) != 0) {
} else if (mNpcs.search(id.str()) != 0) {
std::ostringstream msg;
msg << "Try to override existing record '" << npc.mId << "'";
msg << "Try to override existing record '" << id.str() << "'";
throw std::runtime_error(msg.str());
}
ESM::NPC record = npc;
std::ostringstream id;
id << "$dynamic" << mDynamicCount++;
record.mId = id.str();
ESM::NPC *ptr = mNpcs.insert(record);

@ -20,10 +20,15 @@ void MWWorld::LiveCellRefBase::loadImp (const ESM::ObjectState& state)
if (state.mHasLocals)
{
std::string scriptId = mClass->getScript (ptr);
mData.setLocals (*MWBase::Environment::get().getWorld()->getStore().
get<ESM::Script>().find (scriptId));
mData.getLocals().read (state.mLocals, scriptId);
// Make sure we still have a script. It could have been coming from a content file that is no longer active.
if (!scriptId.empty())
{
if (const ESM::Script* script = MWBase::Environment::get().getWorld()->getStore().get<ESM::Script>().search (scriptId))
{
mData.setLocals (*script);
mData.getLocals().read (state.mLocals, scriptId);
}
}
}
mClass->readAdditionalState (ptr, state);

@ -231,6 +231,8 @@ namespace MWWorld
void World::clear()
{
mRendering->clear();
mLocalScripts.clear();
mPlayer->clear();
@ -277,6 +279,14 @@ namespace MWWorld
void World::write (ESM::ESMWriter& writer, Loading::Listener& progress) const
{
// Active cells could have a dirty fog of war, sync it to the CellStore first
for (Scene::CellStoreCollection::const_iterator iter (mWorldScene->getActiveCells().begin());
iter!=mWorldScene->getActiveCells().end(); ++iter)
{
CellStore* cellstore = *iter;
mRendering->writeFog(cellstore);
}
mCells.write (writer, progress);
mStore.write (writer, progress);
mGlobalVariables.write (writer, progress);

@ -45,7 +45,7 @@ add_component_dir (esm
loadnpc loadpgrd loadrace loadregn loadscpt loadskil loadsndg loadsoun loadspel loadsscr loadstat
loadweap records aipackage effectlist spelllist variant variantimp loadtes3 cellref filter
savedgame journalentry queststate locals globalscript player objectstate cellid cellstate globalmap lightstate inventorystate containerstate npcstate creaturestate dialoguestate statstate
npcstats creaturestats weatherstate quickkeys
npcstats creaturestats weatherstate quickkeys fogstate
)
add_component_dir (misc

@ -8,10 +8,15 @@ void ESM::CellState::load (ESMReader &esm)
{
mWaterLevel = 0;
esm.getHNOT (mWaterLevel, "WLVL");
mHasFogOfWar = false;
esm.getHNOT (mHasFogOfWar, "HFOW");
}
void ESM::CellState::save (ESMWriter &esm) const
{
if (!mId.mPaged)
esm.writeHNT ("WLVL", mWaterLevel);
}
esm.writeHNT("HFOW", mHasFogOfWar);
}

@ -17,9 +17,11 @@ namespace ESM
float mWaterLevel;
int mHasFogOfWar; // Do we have fog of war state (0 or 1)? (see fogstate.hpp)
void load (ESMReader &esm);
void save (ESMWriter &esm) const;
};
}
#endif
#endif

@ -100,6 +100,7 @@ enum RecNameInts
REC_DIAS = 0x53414944,
REC_WTHR = 0x52485457,
REC_KEYS = FourCC<'K','E','Y','S'>::value,
REC_DYNA = FourCC<'D','Y','N','A'>::value,
// format 1
REC_FILT = 0x544C4946

@ -0,0 +1,40 @@
#include "fogstate.hpp"
#include "esmreader.hpp"
#include "esmwriter.hpp"
void ESM::FogState::load (ESMReader &esm)
{
esm.getHNOT(mBounds, "BOUN");
esm.getHNOT(mNorthMarkerAngle, "ANGL");
while (esm.isNextSub("FTEX"))
{
esm.getSubHeader();
FogTexture tex;
esm.getT(tex.mX);
esm.getT(tex.mY);
size_t imageSize = esm.getSubSize()-sizeof(int)*2;
tex.mImageData.resize(imageSize);
esm.getExact(&tex.mImageData[0], imageSize);
mFogTextures.push_back(tex);
}
}
void ESM::FogState::save (ESMWriter &esm, bool interiorCell) const
{
if (interiorCell)
{
esm.writeHNT("BOUN", mBounds);
esm.writeHNT("ANGL", mNorthMarkerAngle);
}
for (std::vector<FogTexture>::const_iterator it = mFogTextures.begin(); it != mFogTextures.end(); ++it)
{
esm.startSubRecord("FTEX");
esm.writeT(it->mX);
esm.writeT(it->mY);
esm.write(&it->mImageData[0], it->mImageData.size());
esm.endRecord("FTEX");
}
}

@ -0,0 +1,38 @@
#ifndef OPENMW_ESM_FOGSTATE_H
#define OPENMW_ESM_FOGSTATE_H
#include <vector>
namespace ESM
{
class ESMReader;
class ESMWriter;
struct FogTexture
{
int mX, mY; // Only used for interior cells
std::vector<char> mImageData;
};
// format 0, saved games only
// Fog of war state
struct FogState
{
// Only used for interior cells
float mNorthMarkerAngle;
struct Bounds
{
float mMinX;
float mMinY;
float mMaxX;
float mMaxY;
} mBounds;
std::vector<FogTexture> mFogTextures;
void load (ESMReader &esm);
void save (ESMWriter &esm, bool interiorCell) const;
};
}
#endif

@ -10,6 +10,10 @@
<Property key="VerticalAlignment" value="false"/>
<Property key="MoveToClick" value="true"/>
<Property key="WheelPage" value="36"/>
<Property key="ViewPage" value="36"/>
<Property key="Page" value="36"/>
<!-- Tracker must be last to be on top and receive mouse events -->
<Child type="Button" skin="MW_Box" offset="18 0 54 14" align="Stretch" name="Background"/>
@ -47,6 +51,10 @@
<Property key="MinTrackSize" value="14"/>
<Property key="MoveToClick" value="true"/>
<Property key="WheelPage" value="36"/>
<Property key="ViewPage" value="36"/>
<Property key="Page" value="36"/>
<!-- Background widget trick that must go first to be placed behind Track and FirstPart/SecondPart widgets, provides the bar texture -->
<Child type="Button" skin="MW_Box" offset="0 18 14 55" align="Stretch" name="Background"/>

Loading…
Cancel
Save