Implement cell loading threshold (Fixes #1874)

The cell loading threshold (default: 1024 units) prevents exterior cell loading until the player has travelled part-way into the next cell. This gets rid of excessive cell loadings when walking along an exterior cell border.

Lower the maximum allowed view distance in options menu to accomodate. Change setting name so that old settings files are upgraded.
deque
scrawl 10 years ago
parent eda296f1e3
commit 7d36a202a8

@ -177,7 +177,7 @@ namespace MWBase
virtual void changeCell(MWWorld::CellStore* cell) = 0;
///< change the active cell
virtual void setPlayerPos(const float x, const float y) = 0;
virtual void setPlayerPos(int cellX, int cellY, const float x, const float y) = 0;
///< set player position in map space
virtual void setPlayerDir(const float x, const float y) = 0;

@ -137,7 +137,7 @@ namespace MWBase
virtual MWWorld::LocalScripts& getLocalScripts() = 0;
virtual bool hasCellChanged() const = 0;
///< Has the player moved to a different cell, since the last frame?
///< Has the set of active cells changed, since the last frame?
virtual bool isCellExterior() const = 0;

@ -159,8 +159,6 @@ namespace MWGui
, mLocalMap(NULL)
, mPrefix()
, mChanged(true)
, mLastPositionX(0.0f)
, mLastPositionY(0.0f)
, mLastDirectionX(0.0f)
, mLastDirectionY(0.0f)
, mCompass(NULL)
@ -425,24 +423,24 @@ namespace MWGui
mLocalMap->getParent()->_updateChilds();
}
void LocalMapBase::setPlayerPos(const float x, const float y)
void LocalMapBase::setPlayerPos(int cellX, int cellY, const float nx, const float ny)
{
updateMagicMarkers();
if (x == mLastPositionX && y == mLastPositionY)
return;
notifyPlayerUpdate ();
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);
MyGUI::IntPoint pos(widgetSize+nx*widgetSize-16, widgetSize+ny*widgetSize-16);
pos.left += (cellX - mCurX) * widgetSize;
pos.top -= (cellY - mCurY) * widgetSize;
mCompass->setPosition(MyGUI::IntPoint(widgetSize+x*widgetSize-16, widgetSize+y*widgetSize-16));
mLastPositionX = x;
mLastPositionY = y;
if (pos != mCompass->getPosition())
{
mCompass->setPosition(pos);
MyGUI::IntPoint middle (pos.left+16, pos.top+16);
MyGUI::IntCoord viewsize = mLocalMap->getCoord();
MyGUI::IntPoint viewOffset(0.5*viewsize.width - middle.left, 0.5*viewsize.height - middle.top);
mLocalMap->setViewOffset(viewOffset);
}
}
void LocalMapBase::setPlayerDir(const float x, const float y)

@ -75,7 +75,7 @@ namespace MWGui
void setCellPrefix(const std::string& prefix);
void setActiveCell(const int x, const int y, bool interior=false);
void setPlayerDir(const float x, const float y);
void setPlayerPos(const float x, const float y);
void setPlayerPos(int cellX, int cellY, const float nx, const float ny);
void onFrame(float dt);
@ -129,8 +129,6 @@ namespace MWGui
float mMarkerUpdateTimer;
float mLastPositionX;
float mLastPositionY;
float mLastDirectionX;
float mLastDirectionY;
};

@ -880,9 +880,6 @@ namespace MWGui
mMap->addVisitedLocation ("#{sCell=" + name + "}", cell->getCell()->getGridX (), cell->getCell()->getGridY ());
mMap->cellExplored (cell->getCell()->getGridX(), cell->getCell()->getGridY());
mMap->setCellPrefix("Cell");
mHud->setCellPrefix("Cell");
}
else
{
@ -900,14 +897,20 @@ namespace MWGui
void WindowManager::setActiveMap(int x, int y, bool interior)
{
if (!interior)
{
mMap->setCellPrefix("Cell");
mHud->setCellPrefix("Cell");
}
mMap->setActiveCell(x,y, interior);
mHud->setActiveCell(x,y, interior);
}
void WindowManager::setPlayerPos(const float x, const float y)
void WindowManager::setPlayerPos(int cellX, int cellY, const float x, const float y)
{
mMap->setPlayerPos(x,y);
mHud->setPlayerPos(x,y);
mMap->setPlayerPos(cellX, cellY, x, y);
mHud->setPlayerPos(cellX, cellY, x, y);
}
void WindowManager::setPlayerDir(const float x, const float y)

@ -183,7 +183,7 @@ namespace MWGui
virtual void updateSkillArea(); ///< update display of skills, factions, birth sign, reputation and bounty
virtual void changeCell(MWWorld::CellStore* cell); ///< change the active cell
virtual void setPlayerPos(const float x, const float y); ///< set player position in map space
virtual void setPlayerPos(int cellX, int cellY, const float x, const float y); ///< set player position in map space
virtual void setPlayerDir(const float x, const float y); ///< set player view direction in map space
virtual void setFocusObject(const MWWorld::Ptr& focus);

@ -25,7 +25,7 @@ using namespace MWRender;
using namespace Ogre;
LocalMap::LocalMap(OEngine::Render::OgreRenderer* rend, MWRender::RenderingManager* rendering) :
mInterior(false), mCellX(0), mCellY(0)
mInterior(false)
{
mRendering = rend;
mRenderingManager = rendering;
@ -522,10 +522,9 @@ void LocalMap::updatePlayer (const Ogre::Vector3& position, const Ogre::Quaterni
{
x = std::ceil(pos.x / sSize)-1;
y = std::ceil(pos.y / sSize)-1;
mCellX = x;
mCellY = y;
}
MWBase::Environment::get().getWindowManager()->setActiveMap(x,y,mInterior);
else
MWBase::Environment::get().getWindowManager()->setActiveMap(x,y,mInterior);
// convert from world coordinates to texture UV coordinates
std::string texBaseName;
@ -540,7 +539,7 @@ void LocalMap::updatePlayer (const Ogre::Vector3& position, const Ogre::Quaterni
texBaseName = mInteriorName + "_";
}
MWBase::Environment::get().getWindowManager()->setPlayerPos(u, v);
MWBase::Environment::get().getWindowManager()->setPlayerPos(x, y, u, v);
MWBase::Environment::get().getWindowManager()->setPlayerDir(playerdirection.x, playerdirection.y);
// explore radius (squared)

@ -134,7 +134,6 @@ namespace MWRender
Ogre::RenderTarget* mRenderTarget;
bool mInterior;
int mCellX, mCellY;
Ogre::AxisAlignedBox mBounds;
std::string mInteriorName;
};

@ -499,7 +499,7 @@ bool RenderingManager::toggleRenderMode(int mode)
}
}
void RenderingManager::configureFog(MWWorld::CellStore &mCell)
void RenderingManager::configureFog(const MWWorld::CellStore &mCell)
{
Ogre::ColourValue color;
color.setAsABGR (mCell.getCell()->mAmbi.mFog);
@ -510,7 +510,7 @@ void RenderingManager::configureFog(MWWorld::CellStore &mCell)
void RenderingManager::configureFog(const float density, const Ogre::ColourValue& colour)
{
mFogColour = colour;
float max = Settings::Manager::getFloat("max viewing distance", "Viewing distance");
float max = Settings::Manager::getFloat("viewing distance", "Viewing distance");
if (density == 0)
{
@ -742,7 +742,7 @@ void RenderingManager::processChangedSettings(const Settings::CategorySettingVec
{
setMenuTransparency(Settings::Manager::getFloat("menu transparency", "GUI"));
}
else if (it->second == "max viewing distance" && it->first == "Viewing distance")
else if (it->second == "viewing distance" && it->first == "Viewing distance")
{
if (!MWBase::Environment::get().getWorld()->isCellExterior() && !MWBase::Environment::get().getWorld()->isCellQuasiExterior()
&& MWBase::Environment::get().getWorld()->getPlayerPtr().mCell)

@ -183,7 +183,7 @@ public:
///< request the local map for a cell
/// configure fog according to cell
void configureFog(MWWorld::CellStore &mCell);
void configureFog(const MWWorld::CellStore &mCell);
/// configure fog manually
void configureFog(const float density, const Ogre::ColourValue& colour);

@ -5,7 +5,7 @@
#include <components/nif/niffile.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp" /// FIXME
#include "../mwbase/world.hpp"
#include "../mwbase/soundmanager.hpp"
#include "../mwbase/mechanicsmanager.hpp"
#include "../mwbase/windowmanager.hpp"
@ -117,6 +117,28 @@ namespace MWWorld
}
}
void Scene::getGridCenter(int &cellX, int &cellY)
{
int maxX = std::numeric_limits<int>().min();
int maxY = std::numeric_limits<int>().min();
int minX = std::numeric_limits<int>().max();
int minY = std::numeric_limits<int>().max();
CellStoreCollection::iterator iter = mActiveCells.begin();
while (iter!=mActiveCells.end())
{
assert ((*iter)->getCell()->isExterior());
int x = (*iter)->getCell()->getGridX();
int y = (*iter)->getCell()->getGridY();
maxX = std::max(x, maxX);
maxY = std::max(y, maxY);
minX = std::min(x, minX);
minY = std::min(y, minY);
++iter;
}
cellX = (minX + maxX) / 2;
cellY = (minY + maxY) / 2;
}
void Scene::update (float duration, bool paused)
{
if (mNeedMapUpdate)
@ -126,6 +148,13 @@ namespace MWWorld
for (CellStoreCollection::iterator active = mActiveCells.begin(); active!=mActiveCells.end(); ++active)
mRendering.requestMap(*active);
mNeedMapUpdate = false;
if (mCurrentCell->isExterior())
{
int cellX, cellY;
getGridCenter(cellX, cellY);
MWBase::Environment::get().getWindowManager()->setActiveMap(cellX,cellY,false);
}
}
mRendering.update (duration, paused);
@ -213,41 +242,6 @@ namespace MWWorld
MWBase::Environment::get().getWorld()->getLocalScripts().addCell (cell);
}
void Scene::playerCellChange(CellStore *cell, const ESM::Position& pos, bool adjustPlayerPos)
{
MWBase::World *world = MWBase::Environment::get().getWorld();
MWWorld::Ptr old = world->getPlayerPtr();
world->getPlayer().setCell(cell);
MWWorld::Ptr player = world->getPlayerPtr();
mRendering.updatePlayerPtr(player);
if (adjustPlayerPos) {
world->moveObject(player, pos.pos[0], pos.pos[1], pos.pos[2]);
float x = Ogre::Radian(pos.rot[0]).valueDegrees();
float y = Ogre::Radian(pos.rot[1]).valueDegrees();
float z = Ogre::Radian(pos.rot[2]).valueDegrees();
world->rotateObject(player, x, y, z);
player.getClass().adjustPosition(player, true);
}
MWBase::MechanicsManager *mechMgr =
MWBase::Environment::get().getMechanicsManager();
mechMgr->updateCell(old, player);
mechMgr->watchActor(player);
mRendering.updateTerrain();
// Delay the map update until scripts have been given a chance to run.
// If we don't do this, objects that should be disabled will still appear on the map.
mNeedMapUpdate = true;
MWBase::Environment::get().getWindowManager()->changeCell(mCurrentCell);
}
void Scene::changeToVoid()
{
CellStoreCollection::iterator active = mActiveCells.begin();
@ -257,7 +251,28 @@ namespace MWWorld
mCurrentCell = NULL;
}
void Scene::changeCell (int X, int Y, const ESM::Position& position, bool adjustPlayerPos)
void Scene::playerMoved(const Ogre::Vector3 &pos)
{
if (!mCurrentCell || !mCurrentCell->isExterior())
return;
// figure out the center of the current cell grid (*not* necessarily mCurrentCell, which is the cell the player is in)
int cellX, cellY;
getGridCenter(cellX, cellY);
float centerX, centerY;
MWBase::Environment::get().getWorld()->indexToPosition(cellX, cellY, centerX, centerY, true);
const float maxDistance = 8192/2 + 1024; // 1/2 cell size + threshold
float distance = std::max(std::abs(centerX-pos.x), std::abs(centerY-pos.y));
if (distance > maxDistance)
{
int newX, newY;
MWBase::Environment::get().getWorld()->positionToIndex(pos.x, pos.y, newX, newY);
changeCellGrid(newX, newY);
mRendering.updateTerrain();
}
}
void Scene::changeCellGrid (int X, int Y)
{
Loading::Listener* loadingListener = MWBase::Environment::get().getWindowManager()->getLoadingScreen();
Loading::ScopedLoad load(loadingListener);
@ -286,6 +301,7 @@ namespace MWWorld
int refsToLoad = 0;
// get the number of refs to load
for (int x=X-1; x<=X+1; ++x)
{
for (int y=Y-1; y<=Y+1; ++y)
{
CellStoreCollection::iterator iter = mActiveCells.begin();
@ -304,11 +320,13 @@ namespace MWWorld
if (iter==mActiveCells.end())
refsToLoad += MWBase::Environment::get().getWorld()->getExterior(x, y)->count();
}
}
loadingListener->setProgressRange(refsToLoad);
// Load cells
for (int x=X-1; x<=X+1; ++x)
{
for (int y=Y-1; y<=Y+1; ++y)
{
CellStoreCollection::iterator iter = mActiveCells.begin();
@ -331,32 +349,47 @@ namespace MWWorld
loadCell (cell, loadingListener);
}
}
}
// find current cell
CellStoreCollection::iterator iter = mActiveCells.begin();
CellStore* current = MWBase::Environment::get().getWorld()->getExterior(X,Y);
MWBase::Environment::get().getWindowManager()->changeCell(current);
while (iter!=mActiveCells.end())
{
assert ((*iter)->getCell()->isExterior());
mCellChanged = true;
if (X==(*iter)->getCell()->getGridX() &&
Y==(*iter)->getCell()->getGridY())
break;
// Delay the map update until scripts have been given a chance to run.
// If we don't do this, objects that should be disabled will still appear on the map.
mNeedMapUpdate = true;
}
++iter;
}
void Scene::changePlayerCell(CellStore *cell, const ESM::Position &pos, bool adjustPlayerPos)
{
mCurrentCell = cell;
assert (iter!=mActiveCells.end());
MWBase::World *world = MWBase::Environment::get().getWorld();
MWWorld::Ptr old = world->getPlayerPtr();
world->getPlayer().setCell(cell);
mCurrentCell = *iter;
MWWorld::Ptr player = world->getPlayerPtr();
mRendering.updatePlayerPtr(player);
// adjust player
playerCellChange (mCurrentCell, position, adjustPlayerPos);
if (adjustPlayerPos) {
world->moveObject(player, pos.pos[0], pos.pos[1], pos.pos[2]);
// Sky system
MWBase::Environment::get().getWorld()->adjustSky();
float x = Ogre::Radian(pos.rot[0]).valueDegrees();
float y = Ogre::Radian(pos.rot[1]).valueDegrees();
float z = Ogre::Radian(pos.rot[2]).valueDegrees();
world->rotateObject(player, x, y, z);
mCellChanged = true;
player.getClass().adjustPosition(player, true);
}
MWBase::MechanicsManager *mechMgr =
MWBase::Environment::get().getMechanicsManager();
mechMgr->updateCell(old, player);
mechMgr->watchActor(player);
MWBase::Environment::get().getWorld()->adjustSky();
}
//We need the ogre renderer and a scene node.
@ -427,33 +460,39 @@ namespace MWWorld
// Load cell.
std::cout << "cellName: " << cell->getCell()->mName << std::endl;
//Loading Interior loading text
loadCell (cell, loadingListener);
mCurrentCell = cell;
changePlayerCell(cell, position, true);
// adjust fog
mRendering.configureFog(*mCurrentCell);
// adjust player
playerCellChange (mCurrentCell, position);
// Sky system
MWBase::Environment::get().getWorld()->adjustSky();
mCellChanged = true;
MWBase::Environment::get().getWindowManager()->fadeScreenIn(0.5);
MWBase::Environment::get().getWindowManager()->changeCell(mCurrentCell);
// Delay the map update until scripts have been given a chance to run.
// If we don't do this, objects that should be disabled will still appear on the map.
mNeedMapUpdate = true;
}
void Scene::changeToExteriorCell (const ESM::Position& position)
void Scene::changeToExteriorCell (const ESM::Position& position, bool adjustPlayerPos)
{
int x = 0;
int y = 0;
MWBase::Environment::get().getWorld()->positionToIndex (position.pos[0], position.pos[1], x, y);
changeCell (x, y, position, true);
changeCellGrid(x, y);
CellStore* current = MWBase::Environment::get().getWorld()->getExterior(x, y);
changePlayerCell(current, position, adjustPlayerPos);
mRendering.updateTerrain();
}
CellStore* Scene::getCurrentCell ()

@ -60,11 +60,13 @@ namespace MWWorld
bool mNeedMapUpdate;
void playerCellChange (CellStore *cell, const ESM::Position& position,
bool adjustPlayerPos = true);
void insertCell (CellStore &cell, bool rescale, Loading::Listener* loadingListener);
// Load and unload cells as necessary to create a cell grid with "X" and "Y" in the center
void changeCellGrid (int X, int Y);
void getGridCenter(int& cellX, int& cellY);
public:
Scene (MWRender::RenderingManager& rendering, PhysicsSystem *physics);
@ -75,19 +77,21 @@ namespace MWWorld
void loadCell (CellStore *cell, Loading::Listener* loadingListener);
void changeCell (int X, int Y, const ESM::Position& position, bool adjustPlayerPos);
void playerMoved (const Ogre::Vector3& pos);
void changePlayerCell (CellStore* newCell, const ESM::Position& position, bool adjustPlayerPos);
CellStore* getCurrentCell ();
CellStore *getCurrentCell();
const CellStoreCollection& getActiveCells () const;
bool hasCellChanged() const;
///< Has the player moved to a different cell, since the last frame?
///< Has the set of active cells changed, since the last frame?
void changeToInteriorCell (const std::string& cellName, const ESM::Position& position);
///< Move to interior cell.
void changeToExteriorCell (const ESM::Position& position);
void changeToExteriorCell (const ESM::Position& position, bool adjustPlayerPos);
///< Move to exterior cell.
void changeToVoid();

@ -241,7 +241,7 @@ namespace MWWorld
pos.rot[0] = 0;
pos.rot[1] = 0;
pos.rot[2] = 0;
mWorldScene->changeToExteriorCell(pos);
mWorldScene->changeToExteriorCell(pos, true);
}
}
@ -920,7 +920,7 @@ namespace MWWorld
mRendering->notifyWorldSpaceChanged();
}
removeContainerScripts(getPlayerPtr());
mWorldScene->changeToExteriorCell(position);
mWorldScene->changeToExteriorCell(position, true);
addContainerScripts(getPlayerPtr(), getPlayerPtr().getCell());
}
@ -1057,9 +1057,10 @@ namespace MWWorld
changeToInteriorCell(Misc::StringUtils::lowerCase(newCell->getCell()->mName), pos);
else
{
int cellX = newCell->getCell()->getGridX();
int cellY = newCell->getCell()->getGridY();
mWorldScene->changeCell(cellX, cellY, pos, false);
if (mWorldScene->isCellActive(*newCell))
mWorldScene->changePlayerCell(newCell, pos, false);
else
mWorldScene->changeToExteriorCell(pos, false);
}
addContainerScripts (getPlayerPtr(), newCell);
}
@ -1120,6 +1121,10 @@ namespace MWWorld
mRendering->moveObject(ptr, vec);
mPhysics->moveObject (ptr);
}
if (isPlayer)
{
mWorldScene->playerMoved (vec);
}
}
bool World::moveObjectImp(const Ptr& ptr, float x, float y, float z)
@ -1565,7 +1570,7 @@ namespace MWWorld
bool World::isCellExterior() const
{
CellStore *currentCell = mWorldScene->getCurrentCell();
const CellStore *currentCell = mWorldScene->getCurrentCell();
if (currentCell)
{
return currentCell->getCell()->isExterior();
@ -1575,7 +1580,7 @@ namespace MWWorld
bool World::isCellQuasiExterior() const
{
CellStore *currentCell = mWorldScene->getCurrentCell();
const CellStore *currentCell = mWorldScene->getCurrentCell();
if (currentCell)
{
if (!(currentCell->getCell()->mData.mFlags & ESM::Cell::QuasiEx))

@ -198,7 +198,7 @@ namespace MWWorld
virtual LocalScripts& getLocalScripts();
virtual bool hasCellChanged() const;
///< Has the player moved to a different cell, since the last frame?
///< Has the set of active cells changed, since the last frame?
virtual bool isCellExterior() const;

@ -2,8 +2,7 @@
<MyGUI type="Layout">
<Widget type="Window" skin="MW_Window" position="0 0 400 400" layer="Console" name="_Main">
<Property key="Caption" value="#{sConsoleTitle}"/>
<Property key="MinSize" value="400 245"/>
<Property key="MaxSize" value="2000 2000"/>
<Property key="MinSize" value="40 40"/>
<Property key="Visible" value="false"/>
<!-- Log window -->

@ -346,10 +346,10 @@
<Property key="Page" value="300"/>
<UserString key="SettingType" value="Slider"/>
<UserString key="SettingCategory" value="Viewing distance"/>
<UserString key="SettingName" value="max viewing distance"/>
<UserString key="SettingName" value="viewing distance"/>
<UserString key="SettingValueType" value="Float"/>
<UserString key="SettingMin" value="2000"/>
<UserString key="SettingMax" value="5600"/>
<UserString key="SettingMax" value="4600"/>
</Widget>
<Widget type="TextBox" skin="SandText" position="4 178 332 18" align="Left Top">
<Property key="Caption" value="#{sNear}"/>

@ -122,8 +122,12 @@ small object size = 250
# Rendering distance for small objects
small object distance = 3500
# Max viewing distance at clear weather conditions
max viewing distance = 5600
# Viewing distance at normal weather conditions
# The maximum distance with no pop-in will be: (see RenderingManager::configureFog)
# viewing distance / minimum weather fog depth (.69) * view frustum factor <= cell size (8192) - loading threshold (1024)
# view frustum factor takes into account that the view frustum end is a plane, so at the edges of the screen you can see further than you should be able to.
# exact factor would depend on FOV
viewing distance = 4600
# Distance at which fog starts (proportional to viewing distance)
fog start factor = 0.5

Loading…
Cancel
Save