forked from mirror/openmw-tes3mp
Savegame: store fog of war (Closes #1177)
This commit is contained in:
parent
7b46e9f914
commit
a4a9794417
13 changed files with 381 additions and 87 deletions
|
@ -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"
|
||||
|
@ -46,7 +48,6 @@ LocalMap::LocalMap(OEngine::Render::OgreRenderer* rend, MWRender::RenderingManag
|
|||
|
||||
LocalMap::~LocalMap()
|
||||
{
|
||||
deleteBuffers();
|
||||
}
|
||||
|
||||
const Ogre::Vector2 LocalMap::rotatePoint(const Ogre::Vector2& p, const Ogre::Vector2& c, const float angle)
|
||||
|
@ -55,59 +56,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)
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
||||
std::string LocalMap::coordStr(const int x, const int y)
|
||||
{
|
||||
return StringConverter::toString(x) + "_" + StringConverter::toString(y);
|
||||
}
|
||||
|
||||
void LocalMap::clear()
|
||||
{
|
||||
// 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 +151,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;
|
||||
|
||||
// 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 +198,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 +248,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 +259,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;
|
||||
|
||||
render(newcenter.x - center.x, newcenter.y - center.y, zMin, zMax, sSize, sSize,
|
||||
cell->getCell()->mName + "_" + coordStr(x,y));
|
||||
std::string texturePrefix = 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_DISCARDABLE);
|
||||
}
|
||||
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)
|
||||
|
@ -249,31 +398,6 @@ void LocalMap::render(const float x, const float y,
|
|||
vp->setMaterialScheme("local_map");
|
||||
|
||||
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
|
||||
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;
|
||||
}
|
||||
|
||||
mRenderingManager->enableLights(true);
|
||||
|
@ -304,6 +428,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;
|
||||
|
||||
|
@ -414,6 +541,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);
|
||||
|
||||
|
@ -104,17 +112,20 @@ 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();
|
||||
|
||||
bool mInterior;
|
||||
int mCellX, mCellY;
|
||||
Ogre::AxisAlignedBox mBounds;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
40
components/esm/fogstate.cpp
Normal file
40
components/esm/fogstate.cpp
Normal file
|
@ -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");
|
||||
}
|
||||
}
|
38
components/esm/fogstate.hpp
Normal file
38
components/esm/fogstate.hpp
Normal file
|
@ -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
|
Loading…
Reference in a new issue