mirror of
				https://github.com/OpenMW/openmw.git
				synced 2025-11-03 23:56:43 +00:00 
			
		
		
		
	Merge branch 'esm4-exterior' into 'master'
Loading ESM4 Exteriors See merge request OpenMW/openmw!2986
This commit is contained in:
		
						commit
						ab17d0947a
					
				
					 45 changed files with 405 additions and 236 deletions
				
			
		| 
						 | 
				
			
			@ -264,8 +264,7 @@ namespace NavMeshTool
 | 
			
		|||
            const osg::Vec2i cellPosition(cell.mData.mX, cell.mData.mY);
 | 
			
		||||
            const std::size_t cellObjectsBegin = data.mObjects.size();
 | 
			
		||||
            const auto cellWorldspace = Misc::StringUtils::lowerCase(
 | 
			
		||||
                (cell.isExterior() ? ESM::RefId::stringRefId(ESM::Cell::sDefaultWorldspace) : cell.mId)
 | 
			
		||||
                    .serializeText());
 | 
			
		||||
                (cell.isExterior() ? ESM::Cell::sDefaultWorldspaceId : cell.mId).serializeText());
 | 
			
		||||
            WorldspaceNavMeshInput& navMeshInput = [&]() -> WorldspaceNavMeshInput& {
 | 
			
		||||
                auto it = navMeshInputs.find(cellWorldspace);
 | 
			
		||||
                if (it == navMeshInputs.end())
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -337,7 +337,7 @@ std::pair<CSMWorld::UniversalId, std::string> CSMWorld::IdTable::view(int row) c
 | 
			
		|||
        return std::make_pair(UniversalId::Type_None, "");
 | 
			
		||||
 | 
			
		||||
    if (id[0] == '#')
 | 
			
		||||
        id = ESM::Cell::sDefaultWorldspace;
 | 
			
		||||
        id = ESM::Cell::sDefaultWorldspaceId.getValue();
 | 
			
		||||
 | 
			
		||||
    return std::make_pair(UniversalId(UniversalId::Type_Scene, id), hint);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -305,7 +305,8 @@ void CSVWorld::RegionMap::view()
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
    emit editRequest(
 | 
			
		||||
        CSMWorld::UniversalId(CSMWorld::UniversalId::Type_Scene, ESM::Cell::sDefaultWorldspace), hint.str());
 | 
			
		||||
        CSMWorld::UniversalId(CSMWorld::UniversalId::Type_Scene, ESM::Cell::sDefaultWorldspaceId.getValue()),
 | 
			
		||||
        hint.str());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void CSVWorld::RegionMap::viewInTable()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -47,7 +47,7 @@ CSVWorld::SceneSubView::SceneSubView(const CSMWorld::UniversalId& id, CSMDoc::Do
 | 
			
		|||
    CSVRender::WorldspaceWidget* worldspaceWidget = nullptr;
 | 
			
		||||
    widgetType whatWidget;
 | 
			
		||||
 | 
			
		||||
    if (Misc::StringUtils::ciEqual(id.getId(), ESM::Cell::sDefaultWorldspace))
 | 
			
		||||
    if (Misc::StringUtils::ciEqual(id.getId(), ESM::Cell::sDefaultWorldspaceId.getValue()))
 | 
			
		||||
    {
 | 
			
		||||
        whatWidget = widget_Paged;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -168,7 +168,8 @@ void CSVWorld::SceneSubView::cellSelectionChanged(const CSMWorld::UniversalId& i
 | 
			
		|||
 | 
			
		||||
void CSVWorld::SceneSubView::cellSelectionChanged(const CSMWorld::CellSelection& selection)
 | 
			
		||||
{
 | 
			
		||||
    setUniversalId(CSMWorld::UniversalId(CSMWorld::UniversalId::Type_Scene, ESM::Cell::sDefaultWorldspace));
 | 
			
		||||
    setUniversalId(
 | 
			
		||||
        CSMWorld::UniversalId(CSMWorld::UniversalId::Type_Scene, ESM::Cell::sDefaultWorldspaceId.getValue()));
 | 
			
		||||
    int size = selection.getSize();
 | 
			
		||||
 | 
			
		||||
    std::ostringstream stream;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -55,6 +55,7 @@ namespace ESM
 | 
			
		|||
    struct ItemLevList;
 | 
			
		||||
    struct TimeStamp;
 | 
			
		||||
    class RefId;
 | 
			
		||||
    struct ExteriorCellLocation;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
namespace MWPhysics
 | 
			
		||||
| 
						 | 
				
			
			@ -250,10 +251,6 @@ namespace MWBase
 | 
			
		|||
        ///< Move to interior cell.
 | 
			
		||||
        ///< @param changeEvent If false, do not trigger cell change flag or detect worldspace changes
 | 
			
		||||
 | 
			
		||||
        virtual void changeToExteriorCell(const ESM::Position& position, bool adjustPlayerPos, bool changeEvent = true)
 | 
			
		||||
            = 0;
 | 
			
		||||
        ///< Move to exterior cell.
 | 
			
		||||
        ///< @param changeEvent If false, do not trigger cell change flag or detect worldspace changes
 | 
			
		||||
        virtual void changeToCell(
 | 
			
		||||
            const ESM::RefId& cellId, const ESM::Position& position, bool adjustPlayerPos, bool changeEvent = true)
 | 
			
		||||
            = 0;
 | 
			
		||||
| 
						 | 
				
			
			@ -316,9 +313,6 @@ namespace MWBase
 | 
			
		|||
        /// relative to \a referenceObject (but the object may be placed somewhere else if the wanted location is
 | 
			
		||||
        /// obstructed).
 | 
			
		||||
 | 
			
		||||
        virtual void indexToPosition(int cellX, int cellY, float& x, float& y, bool centre = false) const = 0;
 | 
			
		||||
        ///< Convert cell numbers to position.
 | 
			
		||||
 | 
			
		||||
        virtual void queueMovement(const MWWorld::Ptr& ptr, const osg::Vec3f& velocity) = 0;
 | 
			
		||||
        ///< Queues movement for \a ptr (in local space), to be applied in the next call to
 | 
			
		||||
        /// doPhysics.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -17,7 +17,6 @@
 | 
			
		|||
#include "../mwworld/actionteleport.hpp"
 | 
			
		||||
#include "../mwworld/actiontrap.hpp"
 | 
			
		||||
#include "../mwworld/cellstore.hpp"
 | 
			
		||||
#include "../mwworld/cellutils.hpp"
 | 
			
		||||
#include "../mwworld/containerstore.hpp"
 | 
			
		||||
#include "../mwworld/customdata.hpp"
 | 
			
		||||
#include "../mwworld/esmstore.hpp"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -23,7 +23,6 @@
 | 
			
		|||
#include "../mwbase/world.hpp"
 | 
			
		||||
 | 
			
		||||
#include "../mwworld/cellstore.hpp"
 | 
			
		||||
#include "../mwworld/cellutils.hpp"
 | 
			
		||||
#include "../mwworld/esmstore.hpp"
 | 
			
		||||
#include "../mwworld/player.hpp"
 | 
			
		||||
#include "../mwworld/worldmodel.hpp"
 | 
			
		||||
| 
						 | 
				
			
			@ -277,7 +276,10 @@ namespace MWGui
 | 
			
		|||
 | 
			
		||||
        if (!mInterior)
 | 
			
		||||
        {
 | 
			
		||||
            cellIndex = MWWorld::positionToCellIndex(worldX, worldY);
 | 
			
		||||
            ESM::ExteriorCellLocation cellPos = ESM::positionToCellIndex(worldX, worldY);
 | 
			
		||||
            cellIndex.x() = cellPos.mX;
 | 
			
		||||
            cellIndex.y() = cellPos.mY;
 | 
			
		||||
 | 
			
		||||
            nX = (worldX - cellSize * cellIndex.x()) / cellSize;
 | 
			
		||||
            // Image space is -Y up, cells are Y up
 | 
			
		||||
            nY = 1 - (worldY - cellSize * cellIndex.y()) / cellSize;
 | 
			
		||||
| 
						 | 
				
			
			@ -583,8 +585,8 @@ namespace MWGui
 | 
			
		|||
            if (!entry.mMapTexture)
 | 
			
		||||
            {
 | 
			
		||||
                if (!mInterior)
 | 
			
		||||
                    requestMapRender(
 | 
			
		||||
                        &MWBase::Environment::get().getWorldModel()->getExterior(entry.mCellX, entry.mCellY));
 | 
			
		||||
                    requestMapRender(&MWBase::Environment::get().getWorldModel()->getExterior(
 | 
			
		||||
                        ESM::ExteriorCellLocation(entry.mCellX, entry.mCellY, ESM::Cell::sDefaultWorldspaceId)));
 | 
			
		||||
 | 
			
		||||
                osg::ref_ptr<osg::Texture2D> texture = mLocalMapRender->getMapTexture(entry.mCellX, entry.mCellY);
 | 
			
		||||
                if (texture)
 | 
			
		||||
| 
						 | 
				
			
			@ -641,7 +643,9 @@ namespace MWGui
 | 
			
		|||
            for (MapEntry& entry : mMaps)
 | 
			
		||||
            {
 | 
			
		||||
                if (!entry.mMapTexture && !widgetCropped(entry.mMapWidget, mLocalMap))
 | 
			
		||||
                    world->getDoorMarkers(worldModel->getExterior(entry.mCellX, entry.mCellY), doors);
 | 
			
		||||
                    world->getDoorMarkers(worldModel->getExterior(ESM::ExteriorCellLocation(
 | 
			
		||||
                                              entry.mCellX, entry.mCellY, ESM::Cell::sDefaultWorldspaceId)),
 | 
			
		||||
                        doors);
 | 
			
		||||
            }
 | 
			
		||||
            if (doors.empty())
 | 
			
		||||
                return;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -15,7 +15,6 @@
 | 
			
		|||
 | 
			
		||||
#include "../mwworld/actionteleport.hpp"
 | 
			
		||||
#include "../mwworld/cellstore.hpp"
 | 
			
		||||
#include "../mwworld/cellutils.hpp"
 | 
			
		||||
#include "../mwworld/class.hpp"
 | 
			
		||||
#include "../mwworld/containerstore.hpp"
 | 
			
		||||
#include "../mwworld/esmstore.hpp"
 | 
			
		||||
| 
						 | 
				
			
			@ -126,12 +125,11 @@ namespace MWGui
 | 
			
		|||
        {
 | 
			
		||||
            std::string_view cellname = transport[i].mCellName;
 | 
			
		||||
            bool interior = true;
 | 
			
		||||
            const osg::Vec2i cellIndex
 | 
			
		||||
                = MWWorld::positionToCellIndex(transport[i].mPos.pos[0], transport[i].mPos.pos[1]);
 | 
			
		||||
            const ESM::ExteriorCellLocation cellIndex
 | 
			
		||||
                = ESM::positionToCellIndex(transport[i].mPos.pos[0], transport[i].mPos.pos[1]);
 | 
			
		||||
            if (cellname.empty())
 | 
			
		||||
            {
 | 
			
		||||
                MWWorld::CellStore& cell
 | 
			
		||||
                    = MWBase::Environment::get().getWorldModel()->getExterior(cellIndex.x(), cellIndex.y());
 | 
			
		||||
                MWWorld::CellStore& cell = MWBase::Environment::get().getWorldModel()->getExterior(cellIndex);
 | 
			
		||||
                cellname = MWBase::Environment::get().getWorld()->getCellName(&cell);
 | 
			
		||||
                interior = false;
 | 
			
		||||
            }
 | 
			
		||||
| 
						 | 
				
			
			@ -194,8 +192,8 @@ namespace MWGui
 | 
			
		|||
        MWBase::Environment::get().getWindowManager()->exitCurrentGuiMode();
 | 
			
		||||
 | 
			
		||||
        MWBase::Environment::get().getWindowManager()->fadeScreenOut(1);
 | 
			
		||||
        osg::Vec2i posCell = MWWorld::positionToCellIndex(pos.pos[0], pos.pos[1]);
 | 
			
		||||
        ESM::RefId cellId = ESM::Cell::generateIdForCell(!interior, cellname, posCell.x(), posCell.y());
 | 
			
		||||
        const ESM::ExteriorCellLocation posCell = ESM::positionToCellIndex(pos.pos[0], pos.pos[1]);
 | 
			
		||||
        ESM::RefId cellId = ESM::Cell::generateIdForCell(!interior, cellname, posCell.mX, posCell.mY);
 | 
			
		||||
 | 
			
		||||
        // Teleports any followers, too.
 | 
			
		||||
        MWWorld::ActionTeleport action(cellId, pos, true);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -113,8 +113,10 @@ namespace MWLua
 | 
			
		|||
        addTimeBindings(api, context, true);
 | 
			
		||||
        api["getCellByName"]
 | 
			
		||||
            = [](std::string_view name) { return GCell{ &MWBase::Environment::get().getWorldModel()->getCell(name) }; };
 | 
			
		||||
        api["getExteriorCell"]
 | 
			
		||||
            = [](int x, int y) { return GCell{ &MWBase::Environment::get().getWorldModel()->getExterior(x, y) }; };
 | 
			
		||||
        api["getExteriorCell"] = [](int x, int y) {
 | 
			
		||||
            return GCell{ &MWBase::Environment::get().getWorldModel()->getExterior(
 | 
			
		||||
                ESM::ExteriorCellLocation(x, y, ESM::Cell::sDefaultWorldspaceId)) };
 | 
			
		||||
        };
 | 
			
		||||
        api["activeActors"] = GObjectList{ worldView->getActorsInScene() };
 | 
			
		||||
        api["createObject"] = [](std::string_view recordId, sol::optional<int> count) -> GObject {
 | 
			
		||||
            // Doesn't matter which cell to use because the new object will be in disabled state.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -10,7 +10,6 @@
 | 
			
		|||
 | 
			
		||||
#include "../mwclass/container.hpp"
 | 
			
		||||
 | 
			
		||||
#include "../mwworld/cellutils.hpp"
 | 
			
		||||
#include "../mwworld/class.hpp"
 | 
			
		||||
#include "../mwworld/timestamp.hpp"
 | 
			
		||||
#include "../mwworld/worldmodel.hpp"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -483,9 +483,11 @@ namespace MWPhysics
 | 
			
		|||
            mHeightFields.erase(heightfield);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const HeightField* PhysicsSystem::getHeightField(int x, int y) const
 | 
			
		||||
    const HeightField* PhysicsSystem::getHeightField(ESM::ExteriorCellLocation cellIndex) const
 | 
			
		||||
    {
 | 
			
		||||
        const auto heightField = mHeightFields.find(std::make_pair(x, y));
 | 
			
		||||
        if (ESM::isEsm4Ext(cellIndex.mWorldspace))
 | 
			
		||||
            return nullptr;
 | 
			
		||||
        const auto heightField = mHeightFields.find(std::make_pair(cellIndex.mX, cellIndex.mY));
 | 
			
		||||
        if (heightField == mHeightFields.end())
 | 
			
		||||
            return nullptr;
 | 
			
		||||
        return heightField->second.get();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -17,6 +17,7 @@
 | 
			
		|||
#include <osg/ref_ptr>
 | 
			
		||||
 | 
			
		||||
#include <components/detournavigator/collisionshapetype.hpp>
 | 
			
		||||
#include <components/esm/util.hpp>
 | 
			
		||||
 | 
			
		||||
#include "../mwworld/ptr.hpp"
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -189,7 +190,7 @@ namespace MWPhysics
 | 
			
		|||
 | 
			
		||||
        void removeHeightField(int x, int y);
 | 
			
		||||
 | 
			
		||||
        const HeightField* getHeightField(int x, int y) const;
 | 
			
		||||
        const HeightField* getHeightField(ESM::ExteriorCellLocation cellIndex) const;
 | 
			
		||||
 | 
			
		||||
        bool toggleCollisionMode();
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -18,8 +18,12 @@ namespace MWRender
 | 
			
		|||
        mCache = new CacheType;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    osg::ref_ptr<ESMTerrain::LandObject> LandManager::getLand(int x, int y)
 | 
			
		||||
    osg::ref_ptr<ESMTerrain::LandObject> LandManager::getLand(ESM::ExteriorCellLocation cellIndex)
 | 
			
		||||
    {
 | 
			
		||||
        if (ESM::isEsm4Ext(cellIndex.mWorldspace))
 | 
			
		||||
            return osg::ref_ptr<ESMTerrain::LandObject>(nullptr);
 | 
			
		||||
        int x = cellIndex.mX;
 | 
			
		||||
        int y = cellIndex.mY;
 | 
			
		||||
        osg::ref_ptr<osg::Object> obj = mCache->getRefFromObjectCache(std::make_pair(x, y));
 | 
			
		||||
        if (obj)
 | 
			
		||||
            return static_cast<ESMTerrain::LandObject*>(obj.get());
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,6 +3,7 @@
 | 
			
		|||
 | 
			
		||||
#include <osg/Object>
 | 
			
		||||
 | 
			
		||||
#include <components/esm/util.hpp>
 | 
			
		||||
#include <components/esm3terrain/storage.hpp>
 | 
			
		||||
#include <components/resource/resourcemanager.hpp>
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -20,7 +21,7 @@ namespace MWRender
 | 
			
		|||
        LandManager(int loadFlags);
 | 
			
		||||
 | 
			
		||||
        /// @note Will return nullptr if not found.
 | 
			
		||||
        osg::ref_ptr<ESMTerrain::LandObject> getLand(int x, int y);
 | 
			
		||||
        osg::ref_ptr<ESMTerrain::LandObject> getLand(ESM::ExteriorCellLocation cellIndex);
 | 
			
		||||
 | 
			
		||||
        void reportStats(unsigned int frameNumber, osg::Stats* stats) const override;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -68,7 +68,7 @@ namespace MWRender
 | 
			
		|||
 | 
			
		||||
    osg::ref_ptr<const ESMTerrain::LandObject> TerrainStorage::getLand(int cellX, int cellY)
 | 
			
		||||
    {
 | 
			
		||||
        return mLandManager->getLand(cellX, cellY);
 | 
			
		||||
        return mLandManager->getLand(ESM::ExteriorCellLocation(cellX, cellY, ESM::Cell::sDefaultWorldspaceId));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const ESM::LandTexture* TerrainStorage::getLandTexture(int index, short plugin)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -123,7 +123,10 @@ namespace MWScript
 | 
			
		|||
                MWBase::World* world = MWBase::Environment::get().getWorld();
 | 
			
		||||
                const MWWorld::Ptr playerPtr = world->getPlayerPtr();
 | 
			
		||||
 | 
			
		||||
                world->indexToPosition(x, y, pos.pos[0], pos.pos[1], true);
 | 
			
		||||
                osg::Vec2 posFromIndex
 | 
			
		||||
                    = ESM::indexToPosition(ESM::ExteriorCellLocation(x, y, ESM::Cell::sDefaultWorldspaceId), true);
 | 
			
		||||
                pos.pos[0] = posFromIndex.x();
 | 
			
		||||
                pos.pos[1] = posFromIndex.y();
 | 
			
		||||
                pos.pos[2] = 0;
 | 
			
		||||
 | 
			
		||||
                pos.rot[0] = pos.rot[1] = pos.rot[2] = 0;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -14,7 +14,6 @@
 | 
			
		|||
#include "../mwbase/world.hpp"
 | 
			
		||||
 | 
			
		||||
#include "../mwworld/cellstore.hpp"
 | 
			
		||||
#include "../mwworld/cellutils.hpp"
 | 
			
		||||
#include "../mwworld/class.hpp"
 | 
			
		||||
#include "../mwworld/manualref.hpp"
 | 
			
		||||
#include "../mwworld/player.hpp"
 | 
			
		||||
| 
						 | 
				
			
			@ -401,8 +400,9 @@ namespace MWScript
 | 
			
		|||
                    store = &worldModel->getCell(cellID);
 | 
			
		||||
                    if (store->isExterior())
 | 
			
		||||
                    {
 | 
			
		||||
                        const osg::Vec2i cellIndex = MWWorld::positionToCellIndex(x, y);
 | 
			
		||||
                        store = &worldModel->getExterior(cellIndex.x(), cellIndex.y());
 | 
			
		||||
                        const ESM::ExteriorCellLocation cellIndex
 | 
			
		||||
                            = ESM::positionToCellIndex(x, y, store->getCell()->getWorldSpace());
 | 
			
		||||
                        store = &worldModel->getExterior(cellIndex);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                catch (std::exception&)
 | 
			
		||||
| 
						 | 
				
			
			@ -416,8 +416,9 @@ namespace MWScript
 | 
			
		|||
                    Log(Debug::Warning) << error;
 | 
			
		||||
                    if (!isPlayer)
 | 
			
		||||
                        return;
 | 
			
		||||
                    const osg::Vec2i cellIndex = MWWorld::positionToCellIndex(x, y);
 | 
			
		||||
                    store = &worldModel->getExterior(cellIndex.x(), cellIndex.y());
 | 
			
		||||
                    const ESM::ExteriorCellLocation cellIndex
 | 
			
		||||
                        = ESM::positionToCellIndex(x, y, store->getCell()->getWorldSpace());
 | 
			
		||||
                    store = &worldModel->getExterior(cellIndex);
 | 
			
		||||
                }
 | 
			
		||||
                if (store)
 | 
			
		||||
                {
 | 
			
		||||
| 
						 | 
				
			
			@ -466,15 +467,15 @@ namespace MWScript
 | 
			
		|||
                {
 | 
			
		||||
                    world->getPlayer().setTeleported(true);
 | 
			
		||||
                }
 | 
			
		||||
                const osg::Vec2i cellIndex = MWWorld::positionToCellIndex(x, y);
 | 
			
		||||
                const ESM::ExteriorCellLocation cellIndex
 | 
			
		||||
                    = ESM::positionToCellIndex(x, y, ESM::Cell::sDefaultWorldspaceId);
 | 
			
		||||
 | 
			
		||||
                // another morrowind oddity: player will be moved to the exterior cell at this location,
 | 
			
		||||
                // non-player actors will move within the cell they are in.
 | 
			
		||||
                MWWorld::Ptr base = ptr;
 | 
			
		||||
                if (isPlayer)
 | 
			
		||||
                {
 | 
			
		||||
                    MWWorld::CellStore* cell
 | 
			
		||||
                        = &MWBase::Environment::get().getWorldModel()->getExterior(cellIndex.x(), cellIndex.y());
 | 
			
		||||
                    MWWorld::CellStore* cell = &MWBase::Environment::get().getWorldModel()->getExterior(cellIndex);
 | 
			
		||||
                    ptr = world->moveObject(ptr, cell, osg::Vec3(x, y, z));
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
| 
						 | 
				
			
			@ -567,8 +568,9 @@ namespace MWScript
 | 
			
		|||
                MWWorld::CellStore* store = nullptr;
 | 
			
		||||
                if (player.getCell()->isExterior())
 | 
			
		||||
                {
 | 
			
		||||
                    const osg::Vec2i cellIndex = MWWorld::positionToCellIndex(x, y);
 | 
			
		||||
                    store = &MWBase::Environment::get().getWorldModel()->getExterior(cellIndex.x(), cellIndex.y());
 | 
			
		||||
                    const ESM::ExteriorCellLocation cellIndex
 | 
			
		||||
                        = ESM::positionToCellIndex(x, y, player.getCell()->getCell()->getWorldSpace());
 | 
			
		||||
                    store = &MWBase::Environment::get().getWorldModel()->getExterior(cellIndex);
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                    store = player.getCell();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -563,12 +563,12 @@ void MWState::StateManager::loadGame(const Character* character, const std::file
 | 
			
		|||
        {
 | 
			
		||||
            // Cell no longer exists (i.e. changed game files), choose a default cell
 | 
			
		||||
            Log(Debug::Warning) << "Warning: Player character's cell no longer exists, changing to the default cell";
 | 
			
		||||
            MWWorld::CellStore& cell = MWBase::Environment::get().getWorldModel()->getExterior(0, 0);
 | 
			
		||||
            float x, y;
 | 
			
		||||
            MWBase::Environment::get().getWorld()->indexToPosition(0, 0, x, y, false);
 | 
			
		||||
            ESM::ExteriorCellLocation cellIndex(0, 0, ESM::Cell::sDefaultWorldspaceId);
 | 
			
		||||
            MWWorld::CellStore& cell = MWBase::Environment::get().getWorldModel()->getExterior(cellIndex);
 | 
			
		||||
            osg::Vec2 posFromIndex = ESM::indexToPosition(cellIndex, false);
 | 
			
		||||
            ESM::Position pos;
 | 
			
		||||
            pos.pos[0] = x;
 | 
			
		||||
            pos.pos[1] = y;
 | 
			
		||||
            pos.pos[0] = posFromIndex.x();
 | 
			
		||||
            pos.pos[1] = posFromIndex.y();
 | 
			
		||||
            pos.pos[2] = 0; // should be adjusted automatically (adjustPlayerPos=true)
 | 
			
		||||
            pos.rot[0] = 0;
 | 
			
		||||
            pos.rot[1] = 0;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -11,7 +11,6 @@
 | 
			
		|||
#include "../mwmechanics/creaturestats.hpp"
 | 
			
		||||
 | 
			
		||||
#include "../mwworld/cellstore.hpp"
 | 
			
		||||
#include "../mwworld/cellutils.hpp"
 | 
			
		||||
#include "../mwworld/class.hpp"
 | 
			
		||||
#include "../mwworld/worldmodel.hpp"
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,8 +2,12 @@
 | 
			
		|||
 | 
			
		||||
#include <components/esm3/loadcell.hpp>
 | 
			
		||||
#include <components/esm4/loadcell.hpp>
 | 
			
		||||
#include <components/esm4/loadwrld.hpp>
 | 
			
		||||
#include <components/misc/algorithm.hpp>
 | 
			
		||||
 | 
			
		||||
#include "../mwbase/environment.hpp"
 | 
			
		||||
#include "esmstore.hpp"
 | 
			
		||||
 | 
			
		||||
namespace MWWorld
 | 
			
		||||
{
 | 
			
		||||
    Cell::Cell(const ESM4::Cell& cell)
 | 
			
		||||
| 
						 | 
				
			
			@ -26,6 +30,12 @@ namespace MWWorld
 | 
			
		|||
            .mFogDensity = 1.f,}
 | 
			
		||||
            ,mWaterHeight(cell.mWaterHeight)
 | 
			
		||||
    {
 | 
			
		||||
        if (isExterior() && mWaterHeight == ESM4::Cell::sInvalidWaterLevel)
 | 
			
		||||
        {
 | 
			
		||||
            auto& worldStore = MWBase::Environment::get().getESMStore()->get<ESM4::World>();
 | 
			
		||||
            const ESM4::World* cellWorld = worldStore.find(mParent);
 | 
			
		||||
            mWaterHeight = cellWorld->mWaterLevel;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Cell::Cell(const ESM::Cell& cell)
 | 
			
		||||
| 
						 | 
				
			
			@ -39,7 +49,7 @@ namespace MWWorld
 | 
			
		|||
        , mNameID(cell.mName)
 | 
			
		||||
        , mRegion(cell.mRegion)
 | 
			
		||||
        , mId(cell.mId)
 | 
			
		||||
        , mParent(ESM::RefId::stringRefId(ESM::Cell::sDefaultWorldspace))
 | 
			
		||||
        , mParent(ESM::Cell::sDefaultWorldspaceId)
 | 
			
		||||
        , mMood{
 | 
			
		||||
            .mAmbiantColor = cell.mAmbi.mAmbient,
 | 
			
		||||
            .mDirectionalColor = cell.mAmbi.mSunlight,
 | 
			
		||||
| 
						 | 
				
			
			@ -48,6 +58,8 @@ namespace MWWorld
 | 
			
		|||
        }
 | 
			
		||||
        ,mWaterHeight(cell.mWater)
 | 
			
		||||
    {
 | 
			
		||||
        if (isExterior())
 | 
			
		||||
            mWaterHeight = -1.f;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::string Cell::getDescription() const
 | 
			
		||||
| 
						 | 
				
			
			@ -65,4 +77,9 @@ namespace MWWorld
 | 
			
		|||
        else
 | 
			
		||||
            return mId;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ESM::ExteriorCellLocation Cell::getExteriorCellLocation() const
 | 
			
		||||
    {
 | 
			
		||||
        return { mGridPos.x(), mGridPos.y(), getWorldSpace() };
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,6 +5,7 @@
 | 
			
		|||
 | 
			
		||||
#include <components/esm/esmbridge.hpp>
 | 
			
		||||
#include <components/esm/refid.hpp>
 | 
			
		||||
#include <components/esm/util.hpp>
 | 
			
		||||
 | 
			
		||||
namespace ESM
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			@ -48,6 +49,7 @@ namespace MWWorld
 | 
			
		|||
        float getWaterHeight() const { return mWaterHeight; }
 | 
			
		||||
        const ESM::RefId& getId() const { return mId; }
 | 
			
		||||
        ESM::RefId getWorldSpace() const;
 | 
			
		||||
        ESM::ExteriorCellLocation getExteriorCellLocation() const;
 | 
			
		||||
 | 
			
		||||
    private:
 | 
			
		||||
        bool mIsExterior;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -102,7 +102,8 @@ namespace MWWorld
 | 
			
		|||
                try
 | 
			
		||||
                {
 | 
			
		||||
                    mTerrain->cacheCell(mTerrainView.get(), mX, mY);
 | 
			
		||||
                    mPreloadedObjects.insert(mLandManager->getLand(mX, mY));
 | 
			
		||||
                    mPreloadedObjects.insert(
 | 
			
		||||
                        mLandManager->getLand(ESM::ExteriorCellLocation(mX, mY, ESM::Cell::sDefaultWorldspaceId)));
 | 
			
		||||
                }
 | 
			
		||||
                catch (std::exception&)
 | 
			
		||||
                {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -10,7 +10,6 @@
 | 
			
		|||
 | 
			
		||||
#include <apps/openmw/mwbase/environment.hpp>
 | 
			
		||||
#include <apps/openmw/mwbase/world.hpp>
 | 
			
		||||
#include <apps/openmw/mwworld/cellutils.hpp>
 | 
			
		||||
#include <apps/openmw/mwworld/esmstore.hpp>
 | 
			
		||||
 | 
			
		||||
namespace MWWorld
 | 
			
		||||
| 
						 | 
				
			
			@ -89,8 +88,8 @@ namespace MWWorld
 | 
			
		|||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                const osg::Vec2i index = positionToCellIndex(ref.mDoorDest.pos[0], ref.mDoorDest.pos[1]);
 | 
			
		||||
                return ESM::RefId::esm3ExteriorCell(index.x(), index.y());
 | 
			
		||||
                const auto cellPos = ESM::positionToCellIndex(ref.mDoorDest.pos[0], ref.mDoorDest.pos[1]);
 | 
			
		||||
                return ESM::RefId::esm3ExteriorCell(cellPos.mX, cellPos.mY);
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
        auto esm4Visit = [&](const ESM4::Reference& ref) -> ESM::RefId {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -664,7 +664,7 @@ namespace MWWorld
 | 
			
		|||
    float CellStore::getWaterLevel() const
 | 
			
		||||
    {
 | 
			
		||||
        if (isExterior())
 | 
			
		||||
            return -1;
 | 
			
		||||
            return getCell()->getWaterHeight();
 | 
			
		||||
        return mWaterLevel;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,19 +0,0 @@
 | 
			
		|||
#ifndef OPENMW_MWWORLD_CELLUTILS_H
 | 
			
		||||
#define OPENMW_MWWORLD_CELLUTILS_H
 | 
			
		||||
 | 
			
		||||
#include <components/misc/constants.hpp>
 | 
			
		||||
 | 
			
		||||
#include <osg/Vec2i>
 | 
			
		||||
 | 
			
		||||
#include <cmath>
 | 
			
		||||
 | 
			
		||||
namespace MWWorld
 | 
			
		||||
{
 | 
			
		||||
    inline osg::Vec2i positionToCellIndex(float x, float y)
 | 
			
		||||
    {
 | 
			
		||||
        return { static_cast<int>(std::floor(x / Constants::CellSizeInUnits)),
 | 
			
		||||
            static_cast<int>(std::floor(y / Constants::CellSizeInUnits)) };
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
| 
						 | 
				
			
			@ -14,6 +14,7 @@
 | 
			
		|||
#include <components/misc/algorithm.hpp>
 | 
			
		||||
 | 
			
		||||
#include <components/esm4/common.hpp>
 | 
			
		||||
#include <components/esm4/loadwrld.hpp>
 | 
			
		||||
#include <components/esm4/reader.hpp>
 | 
			
		||||
#include <components/esm4/readerutils.hpp>
 | 
			
		||||
#include <components/esmloader/load.hpp>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -42,6 +42,7 @@ namespace ESM4
 | 
			
		|||
    struct Ingredient;
 | 
			
		||||
    struct MiscItem;
 | 
			
		||||
    struct Weapon;
 | 
			
		||||
    struct World;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
namespace ESM
 | 
			
		||||
| 
						 | 
				
			
			@ -120,7 +121,7 @@ namespace MWWorld
 | 
			
		|||
            Store<ESM4::Static>, Store<ESM4::Cell>, Store<ESM4::Light>, Store<ESM4::Reference>, Store<ESM4::Activator>,
 | 
			
		||||
            Store<ESM4::Potion>, Store<ESM4::Ammunition>, Store<ESM4::Armor>, Store<ESM4::Book>, Store<ESM4::Clothing>,
 | 
			
		||||
            Store<ESM4::Container>, Store<ESM4::Door>, Store<ESM4::Ingredient>, Store<ESM4::MiscItem>,
 | 
			
		||||
            Store<ESM4::Weapon>>;
 | 
			
		||||
            Store<ESM4::Weapon>, Store<ESM4::World>>;
 | 
			
		||||
 | 
			
		||||
    private:
 | 
			
		||||
        template <typename T>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -13,6 +13,7 @@
 | 
			
		|||
#include <components/detournavigator/navigator.hpp>
 | 
			
		||||
#include <components/detournavigator/updateguard.hpp>
 | 
			
		||||
#include <components/esm/records.hpp>
 | 
			
		||||
#include <components/esm3/loadcell.hpp>
 | 
			
		||||
#include <components/loadinglistener/loadinglistener.hpp>
 | 
			
		||||
#include <components/misc/convert.hpp>
 | 
			
		||||
#include <components/misc/resourcehelpers.hpp>
 | 
			
		||||
| 
						 | 
				
			
			@ -39,7 +40,6 @@
 | 
			
		|||
 | 
			
		||||
#include "cellpreloader.hpp"
 | 
			
		||||
#include "cellstore.hpp"
 | 
			
		||||
#include "cellutils.hpp"
 | 
			
		||||
#include "cellvisitors.hpp"
 | 
			
		||||
#include "class.hpp"
 | 
			
		||||
#include "esmstore.hpp"
 | 
			
		||||
| 
						 | 
				
			
			@ -247,12 +247,12 @@ namespace
 | 
			
		|||
        return std::abs(cellPosition.first) + std::abs(cellPosition.second);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool isCellInCollection(int x, int y, MWWorld::Scene::CellStoreCollection& collection)
 | 
			
		||||
    bool isCellInCollection(ESM::ExteriorCellLocation cellIndex, MWWorld::Scene::CellStoreCollection& collection)
 | 
			
		||||
    {
 | 
			
		||||
        for (auto* cell : collection)
 | 
			
		||||
        {
 | 
			
		||||
            assert(cell->getCell()->isExterior());
 | 
			
		||||
            if (x == cell->getCell()->getGridX() && y == cell->getCell()->getGridY())
 | 
			
		||||
            if (cellIndex == cell->getCell()->getExteriorCellLocation())
 | 
			
		||||
                return true;
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
| 
						 | 
				
			
			@ -304,8 +304,8 @@ namespace MWWorld
 | 
			
		|||
    {
 | 
			
		||||
        if (mChangeCellGridRequest.has_value())
 | 
			
		||||
        {
 | 
			
		||||
            changeCellGrid(mChangeCellGridRequest->mPosition, mChangeCellGridRequest->mCell.x(),
 | 
			
		||||
                mChangeCellGridRequest->mCell.y(), mChangeCellGridRequest->mChangeEvent);
 | 
			
		||||
            changeCellGrid(mChangeCellGridRequest->mPosition, mChangeCellGridRequest->mCellIndex,
 | 
			
		||||
                mChangeCellGridRequest->mChangeEvent);
 | 
			
		||||
            mChangeCellGridRequest.reset();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -345,7 +345,8 @@ namespace MWWorld
 | 
			
		|||
 | 
			
		||||
        if (cell->getCell()->isExterior())
 | 
			
		||||
        {
 | 
			
		||||
            if (mPhysics->getHeightField(cellX, cellY) != nullptr)
 | 
			
		||||
            if (mPhysics->getHeightField(ESM::ExteriorCellLocation(cellX, cellY, cell->getCell()->getWorldSpace()))
 | 
			
		||||
                != nullptr)
 | 
			
		||||
                mNavigator.removeHeightfield(osg::Vec2i(cellX, cellY), navigatorUpdateGuard);
 | 
			
		||||
 | 
			
		||||
            mPhysics->removeHeightField(cellX, cellY);
 | 
			
		||||
| 
						 | 
				
			
			@ -390,10 +391,12 @@ namespace MWWorld
 | 
			
		|||
        const int cellX = cell.getCell()->getGridX();
 | 
			
		||||
        const int cellY = cell.getCell()->getGridY();
 | 
			
		||||
        const MWWorld::Cell& cellVariant = *cell.getCell();
 | 
			
		||||
        ESM::RefId worldspace = cellVariant.getWorldSpace();
 | 
			
		||||
        ESM::ExteriorCellLocation cellIndex(cellX, cellY, worldspace);
 | 
			
		||||
 | 
			
		||||
        if (cellVariant.isExterior())
 | 
			
		||||
        {
 | 
			
		||||
            osg::ref_ptr<const ESMTerrain::LandObject> land = mRendering.getLandManager()->getLand(cellX, cellY);
 | 
			
		||||
            osg::ref_ptr<const ESMTerrain::LandObject> land = mRendering.getLandManager()->getLand(cellIndex);
 | 
			
		||||
            const ESM::Land::LandData* data = land ? land->getData(ESM::Land::DATA_VHGT) : nullptr;
 | 
			
		||||
            const int verts = ESM::Land::LAND_SIZE;
 | 
			
		||||
            const int worldsize = ESM::Land::REAL_SIZE;
 | 
			
		||||
| 
						 | 
				
			
			@ -409,7 +412,7 @@ namespace MWWorld
 | 
			
		|||
                mPhysics->addHeightField(defaultHeight.data(), cellX, cellY, worldsize, verts,
 | 
			
		||||
                    ESM::Land::DEFAULT_HEIGHT, ESM::Land::DEFAULT_HEIGHT, land.get());
 | 
			
		||||
            }
 | 
			
		||||
            if (const auto heightField = mPhysics->getHeightField(cellX, cellY))
 | 
			
		||||
            if (const auto heightField = mPhysics->getHeightField(cellIndex))
 | 
			
		||||
            {
 | 
			
		||||
                const osg::Vec2i cellPosition(cellX, cellY);
 | 
			
		||||
                const btVector3& origin = heightField->getCollisionObject()->getWorldTransform().getOrigin();
 | 
			
		||||
| 
						 | 
				
			
			@ -464,7 +467,7 @@ namespace MWWorld
 | 
			
		|||
 | 
			
		||||
            if (cellVariant.isExterior())
 | 
			
		||||
            {
 | 
			
		||||
                if (const auto heightField = mPhysics->getHeightField(cellX, cellY))
 | 
			
		||||
                if (const auto heightField = mPhysics->getHeightField(cellIndex))
 | 
			
		||||
                    mNavigator.addWater(
 | 
			
		||||
                        osg::Vec2i(cellX, cellY), ESM::Land::REAL_SIZE, waterLevel, navigatorUpdateGuard);
 | 
			
		||||
            }
 | 
			
		||||
| 
						 | 
				
			
			@ -506,17 +509,20 @@ namespace MWWorld
 | 
			
		|||
 | 
			
		||||
    osg::Vec2i Scene::getNewGridCenter(const osg::Vec3f& pos, const osg::Vec2i* currentGridCenter) const
 | 
			
		||||
    {
 | 
			
		||||
        ESM::RefId worldspace
 | 
			
		||||
            = mCurrentCell ? mCurrentCell->getCell()->getWorldSpace() : ESM::Cell::sDefaultWorldspaceId;
 | 
			
		||||
        if (currentGridCenter)
 | 
			
		||||
        {
 | 
			
		||||
            float centerX, centerY;
 | 
			
		||||
            mWorld.indexToPosition(currentGridCenter->x(), currentGridCenter->y(), centerX, centerY, true);
 | 
			
		||||
            float distance = std::max(std::abs(centerX - pos.x()), std::abs(centerY - pos.y()));
 | 
			
		||||
            const float maxDistance
 | 
			
		||||
                = Constants::CellSizeInUnits / 2 + mCellLoadingThreshold; // 1/2 cell size + threshold
 | 
			
		||||
            osg::Vec2 center = ESM::indexToPosition(
 | 
			
		||||
                ESM::ExteriorCellLocation(currentGridCenter->x(), currentGridCenter->y(), worldspace), true);
 | 
			
		||||
            float distance = std::max(std::abs(center.x() - pos.x()), std::abs(center.y() - pos.y()));
 | 
			
		||||
            float cellSize = ESM::getCellSize(worldspace);
 | 
			
		||||
            const float maxDistance = cellSize / 2 + mCellLoadingThreshold; // 1/2 cell size + threshold
 | 
			
		||||
            if (distance <= maxDistance)
 | 
			
		||||
                return *currentGridCenter;
 | 
			
		||||
        }
 | 
			
		||||
        return positionToCellIndex(pos.x(), pos.y());
 | 
			
		||||
        ESM::ExteriorCellLocation cellPos = ESM::positionToCellIndex(pos.x(), pos.y(), worldspace);
 | 
			
		||||
        return { cellPos.mX, cellPos.mY };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void Scene::playerMoved(const osg::Vec3f& pos)
 | 
			
		||||
| 
						 | 
				
			
			@ -531,12 +537,15 @@ namespace MWWorld
 | 
			
		|||
 | 
			
		||||
    void Scene::requestChangeCellGrid(const osg::Vec3f& position, const osg::Vec2i& cell, bool changeEvent)
 | 
			
		||||
    {
 | 
			
		||||
        mChangeCellGridRequest = ChangeCellGridRequest{ position, cell, changeEvent };
 | 
			
		||||
        mChangeCellGridRequest = ChangeCellGridRequest{ position,
 | 
			
		||||
            ESM::ExteriorCellLocation(cell.x(), cell.y(), mCurrentCell->getCell()->getWorldSpace()), changeEvent };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void Scene::changeCellGrid(const osg::Vec3f& pos, int playerCellX, int playerCellY, bool changeEvent)
 | 
			
		||||
    void Scene::changeCellGrid(const osg::Vec3f& pos, ESM::ExteriorCellLocation playerCellIndex, bool changeEvent)
 | 
			
		||||
    {
 | 
			
		||||
        auto navigatorUpdateGuard = mNavigator.makeUpdateGuard();
 | 
			
		||||
        int playerCellX = playerCellIndex.mX;
 | 
			
		||||
        int playerCellY = playerCellIndex.mY;
 | 
			
		||||
 | 
			
		||||
        for (auto iter = mActiveCells.begin(); iter != mActiveCells.end();)
 | 
			
		||||
        {
 | 
			
		||||
| 
						 | 
				
			
			@ -551,12 +560,8 @@ namespace MWWorld
 | 
			
		|||
            else
 | 
			
		||||
                unloadCell(cell, navigatorUpdateGuard.get());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        mNavigator.setWorldspace(Misc::StringUtils::lowerCase(mWorld.getWorldModel()
 | 
			
		||||
                                                                  .getExterior(playerCellX, playerCellY)
 | 
			
		||||
                                                                  .getCell()
 | 
			
		||||
                                                                  ->getWorldSpace()
 | 
			
		||||
                                                                  .serializeText()),
 | 
			
		||||
        mNavigator.setWorldspace(
 | 
			
		||||
            mWorld.getWorldModel().getExterior(playerCellIndex).getCell()->getWorldSpace().serializeText(),
 | 
			
		||||
            navigatorUpdateGuard.get());
 | 
			
		||||
        mNavigator.updateBounds(pos, navigatorUpdateGuard.get());
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -578,9 +583,9 @@ namespace MWWorld
 | 
			
		|||
            {
 | 
			
		||||
                for (int y = playerCellY - range; y <= playerCellY + range; ++y)
 | 
			
		||||
                {
 | 
			
		||||
                    if (!isCellInCollection(x, y, collection))
 | 
			
		||||
                    if (!isCellInCollection(ESM::ExteriorCellLocation(x, y, playerCellIndex.mWorldspace), collection))
 | 
			
		||||
                    {
 | 
			
		||||
                        refsToLoad += mWorld.getWorldModel().getExterior(x, y).count();
 | 
			
		||||
                        refsToLoad += mWorld.getWorldModel().getExterior(playerCellIndex).count();
 | 
			
		||||
                        cellsPositionsToLoad.emplace_back(x, y);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
| 
						 | 
				
			
			@ -612,9 +617,10 @@ namespace MWWorld
 | 
			
		|||
 | 
			
		||||
        for (const auto& [x, y] : cellsPositionsToLoad)
 | 
			
		||||
        {
 | 
			
		||||
            if (!isCellInCollection(x, y, mActiveCells))
 | 
			
		||||
            ESM::ExteriorCellLocation indexToLoad = { x, y, playerCellIndex.mWorldspace };
 | 
			
		||||
            if (!isCellInCollection(indexToLoad, mActiveCells))
 | 
			
		||||
            {
 | 
			
		||||
                CellStore& cell = mWorld.getWorldModel().getExterior(x, y);
 | 
			
		||||
                CellStore& cell = mWorld.getWorldModel().getExterior(indexToLoad);
 | 
			
		||||
                loadCell(cell, loadingListener, changeEvent, pos, navigatorUpdateGuard.get());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -623,7 +629,7 @@ namespace MWWorld
 | 
			
		|||
 | 
			
		||||
        navigatorUpdateGuard.reset();
 | 
			
		||||
 | 
			
		||||
        CellStore& current = mWorld.getWorldModel().getExterior(playerCellX, playerCellY);
 | 
			
		||||
        CellStore& current = mWorld.getWorldModel().getExterior(playerCellIndex);
 | 
			
		||||
        MWBase::Environment::get().getWindowManager()->changeCell(¤t);
 | 
			
		||||
 | 
			
		||||
        if (changeEvent)
 | 
			
		||||
| 
						 | 
				
			
			@ -676,9 +682,9 @@ namespace MWWorld
 | 
			
		|||
            loadingListener->setLabel("#{OMWEngine:TestingExteriorCells} (" + std::to_string(i) + "/"
 | 
			
		||||
                + std::to_string(cells.getExtSize()) + ")...");
 | 
			
		||||
 | 
			
		||||
            CellStore& cell = mWorld.getWorldModel().getExterior(it->mData.mX, it->mData.mY);
 | 
			
		||||
            mNavigator.setWorldspace(Misc::StringUtils::lowerCase(cell.getCell()->getWorldSpace().serializeText()),
 | 
			
		||||
                navigatorUpdateGuard.get());
 | 
			
		||||
            CellStore& cell = mWorld.getWorldModel().getExterior(
 | 
			
		||||
                ESM::ExteriorCellLocation(it->mData.mX, it->mData.mY, ESM::Cell::sDefaultWorldspaceId));
 | 
			
		||||
            mNavigator.setWorldspace(cell.getCell()->getWorldSpace().serializeText(), navigatorUpdateGuard.get());
 | 
			
		||||
            const osg::Vec3f position
 | 
			
		||||
                = osg::Vec3f(it->mData.mX + 0.5f, it->mData.mY + 0.5f, 0) * Constants::CellSizeInUnits;
 | 
			
		||||
            mNavigator.updateBounds(position, navigatorUpdateGuard.get());
 | 
			
		||||
| 
						 | 
				
			
			@ -735,8 +741,7 @@ namespace MWWorld
 | 
			
		|||
                + std::to_string(cells.getIntSize()) + ")...");
 | 
			
		||||
 | 
			
		||||
            CellStore& cell = mWorld.getWorldModel().getInterior(it->mName);
 | 
			
		||||
            mNavigator.setWorldspace(Misc::StringUtils::lowerCase(cell.getCell()->getWorldSpace().serializeText()),
 | 
			
		||||
                navigatorUpdateGuard.get());
 | 
			
		||||
            mNavigator.setWorldspace(cell.getCell()->getWorldSpace().serializeText(), navigatorUpdateGuard.get());
 | 
			
		||||
            ESM::Position position;
 | 
			
		||||
            mWorld.findInteriorPosition(it->mName, position);
 | 
			
		||||
            mNavigator.updateBounds(position.asVec3(), navigatorUpdateGuard.get());
 | 
			
		||||
| 
						 | 
				
			
			@ -891,8 +896,7 @@ namespace MWWorld
 | 
			
		|||
 | 
			
		||||
        loadingListener->setProgressRange(cell.count());
 | 
			
		||||
 | 
			
		||||
        mNavigator.setWorldspace(
 | 
			
		||||
            Misc::StringUtils::lowerCase(cell.getCell()->getWorldSpace().serializeText()), navigatorUpdateGuard.get());
 | 
			
		||||
        mNavigator.setWorldspace(cell.getCell()->getWorldSpace().serializeText(), navigatorUpdateGuard.get());
 | 
			
		||||
        mNavigator.updateBounds(position.asVec3(), navigatorUpdateGuard.get());
 | 
			
		||||
 | 
			
		||||
        // Load cell.
 | 
			
		||||
| 
						 | 
				
			
			@ -932,7 +936,8 @@ namespace MWWorld
 | 
			
		|||
 | 
			
		||||
        const osg::Vec2i cellIndex(current.getCell()->getGridX(), current.getCell()->getGridY());
 | 
			
		||||
 | 
			
		||||
        changeCellGrid(position.asVec3(), cellIndex.x(), cellIndex.y(), changeEvent);
 | 
			
		||||
        changeCellGrid(position.asVec3(),
 | 
			
		||||
            ESM::ExteriorCellLocation(cellIndex.x(), cellIndex.y(), current.getCell()->getWorldSpace()), changeEvent);
 | 
			
		||||
 | 
			
		||||
        changePlayerCell(current, position, adjustPlayerPos);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1153,9 +1158,9 @@ namespace MWWorld
 | 
			
		|||
        int cellX, cellY;
 | 
			
		||||
        cellX = mCurrentGridCenter.x();
 | 
			
		||||
        cellY = mCurrentGridCenter.y();
 | 
			
		||||
        ESM::RefId extWorldspace = mWorld.getCurrentWorldspace();
 | 
			
		||||
 | 
			
		||||
        float centerX, centerY;
 | 
			
		||||
        mWorld.indexToPosition(cellX, cellY, centerX, centerY, true);
 | 
			
		||||
        float cellSize = ESM::getCellSize(extWorldspace);
 | 
			
		||||
 | 
			
		||||
        for (int dx = -halfGridSizePlusOne; dx <= halfGridSizePlusOne; ++dx)
 | 
			
		||||
        {
 | 
			
		||||
| 
						 | 
				
			
			@ -1164,20 +1169,18 @@ namespace MWWorld
 | 
			
		|||
                if (dy != halfGridSizePlusOne && dy != -halfGridSizePlusOne && dx != halfGridSizePlusOne
 | 
			
		||||
                    && dx != -halfGridSizePlusOne)
 | 
			
		||||
                    continue; // only care about the outer (not yet loaded) part of the grid
 | 
			
		||||
                ESM::ExteriorCellLocation cellIndex(cellX + dx, cellY + dy, extWorldspace);
 | 
			
		||||
                osg::Vec2 thisCellCenter = ESM::indexToPosition(cellIndex, true);
 | 
			
		||||
 | 
			
		||||
                float thisCellCenterX, thisCellCenterY;
 | 
			
		||||
                mWorld.indexToPosition(cellX + dx, cellY + dy, thisCellCenterX, thisCellCenterY, true);
 | 
			
		||||
 | 
			
		||||
                float dist
 | 
			
		||||
                    = std::max(std::abs(thisCellCenterX - playerPos.x()), std::abs(thisCellCenterY - playerPos.y()));
 | 
			
		||||
                float dist = std::max(
 | 
			
		||||
                    std::abs(thisCellCenter.x() - playerPos.x()), std::abs(thisCellCenter.y() - playerPos.y()));
 | 
			
		||||
                dist = std::min(dist,
 | 
			
		||||
                    std::max(
 | 
			
		||||
                        std::abs(thisCellCenterX - predictedPos.x()), std::abs(thisCellCenterY - predictedPos.y())));
 | 
			
		||||
                float loadDist = Constants::CellSizeInUnits / 2 + Constants::CellSizeInUnits - mCellLoadingThreshold
 | 
			
		||||
                    + mPreloadDistance;
 | 
			
		||||
                    std::max(std::abs(thisCellCenter.x() - predictedPos.x()),
 | 
			
		||||
                        std::abs(thisCellCenter.y() - predictedPos.y())));
 | 
			
		||||
                float loadDist = cellSize / 2 + cellSize - mCellLoadingThreshold + mPreloadDistance;
 | 
			
		||||
 | 
			
		||||
                if (dist < loadDist)
 | 
			
		||||
                    preloadCell(mWorld.getWorldModel().getExterior(cellX + dx, cellY + dy));
 | 
			
		||||
                    preloadCell(mWorld.getWorldModel().getExterior(cellIndex));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -1193,8 +1196,9 @@ namespace MWWorld
 | 
			
		|||
            {
 | 
			
		||||
                for (int dy = -mHalfGridSize; dy <= mHalfGridSize; ++dy)
 | 
			
		||||
                {
 | 
			
		||||
                    mPreloader->preload(
 | 
			
		||||
                        mWorld.getWorldModel().getExterior(x + dx, y + dy), mRendering.getReferenceTime());
 | 
			
		||||
                    mPreloader->preload(mWorld.getWorldModel().getExterior(
 | 
			
		||||
                                            ESM::ExteriorCellLocation(x + dx, y + dy, cell.getCell()->getWorldSpace())),
 | 
			
		||||
                        mRendering.getReferenceTime());
 | 
			
		||||
                    if (++numpreloaded >= mPreloader->getMaxCacheSize())
 | 
			
		||||
                        break;
 | 
			
		||||
                }
 | 
			
		||||
| 
						 | 
				
			
			@ -1257,12 +1261,12 @@ namespace MWWorld
 | 
			
		|||
    };
 | 
			
		||||
 | 
			
		||||
    void Scene::preloadFastTravelDestinations(const osg::Vec3f& playerPos, const osg::Vec3f& /*predictedPos*/,
 | 
			
		||||
        std::vector<PositionCellGrid>&
 | 
			
		||||
            exteriorPositions) // ignore predictedPos here since opening dialogue with travel service takes extra time
 | 
			
		||||
        std::vector<PositionCellGrid>& exteriorPositions) // ignore predictedPos here since opening dialogue with
 | 
			
		||||
                                                          // travel service takes extra time
 | 
			
		||||
    {
 | 
			
		||||
        const MWWorld::ConstPtr player = mWorld.getPlayerPtr();
 | 
			
		||||
        ListFastTravelDestinationsVisitor listVisitor(mPreloadDistance, player.getRefData().getPosition().asVec3());
 | 
			
		||||
 | 
			
		||||
        ESM::RefId extWorldspace = mWorld.getCurrentWorldspace();
 | 
			
		||||
        for (MWWorld::CellStore* cellStore : mActiveCells)
 | 
			
		||||
        {
 | 
			
		||||
            cellStore->forEachType<ESM::NPC>(listVisitor);
 | 
			
		||||
| 
						 | 
				
			
			@ -1276,8 +1280,8 @@ namespace MWWorld
 | 
			
		|||
            else
 | 
			
		||||
            {
 | 
			
		||||
                osg::Vec3f pos = dest.mPos.asVec3();
 | 
			
		||||
                const osg::Vec2i cellIndex = positionToCellIndex(pos.x(), pos.y());
 | 
			
		||||
                preloadCell(mWorld.getWorldModel().getExterior(cellIndex.x(), cellIndex.y()), true);
 | 
			
		||||
                const ESM::ExteriorCellLocation cellIndex = ESM::positionToCellIndex(pos.x(), pos.y(), extWorldspace);
 | 
			
		||||
                preloadCell(mWorld.getWorldModel().getExterior(cellIndex), true);
 | 
			
		||||
                exteriorPositions.emplace_back(pos, gridCenterToBounds(getNewGridCenter(pos)));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -13,6 +13,7 @@
 | 
			
		|||
#include <unordered_map>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
#include <components/esm/util.hpp>
 | 
			
		||||
#include <components/misc/constants.hpp>
 | 
			
		||||
 | 
			
		||||
namespace osg
 | 
			
		||||
| 
						 | 
				
			
			@ -79,7 +80,7 @@ namespace MWWorld
 | 
			
		|||
        struct ChangeCellGridRequest
 | 
			
		||||
        {
 | 
			
		||||
            osg::Vec3f mPosition;
 | 
			
		||||
            osg::Vec2i mCell;
 | 
			
		||||
            ESM::ExteriorCellLocation mCellIndex;
 | 
			
		||||
            bool mChangeEvent;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -117,7 +118,7 @@ namespace MWWorld
 | 
			
		|||
        osg::Vec2i mCurrentGridCenter;
 | 
			
		||||
 | 
			
		||||
        // Load and unload cells as necessary to create a cell grid with "X" and "Y" in the center
 | 
			
		||||
        void changeCellGrid(const osg::Vec3f& pos, int playerCellX, int playerCellY, bool changeEvent = true);
 | 
			
		||||
        void changeCellGrid(const osg::Vec3f& pos, ESM::ExteriorCellLocation playerCellIndex, bool changeEvent = true);
 | 
			
		||||
 | 
			
		||||
        void requestChangeCellGrid(const osg::Vec3f& position, const osg::Vec2i& cell, bool changeEvent = true);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -8,6 +8,7 @@
 | 
			
		|||
#include <components/esm/records.hpp>
 | 
			
		||||
#include <components/esm3/esmreader.hpp>
 | 
			
		||||
#include <components/esm3/esmwriter.hpp>
 | 
			
		||||
#include <components/esm4/loadwrld.hpp>
 | 
			
		||||
#include <components/loadinglistener/loadinglistener.hpp>
 | 
			
		||||
#include <components/misc/rng.hpp>
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1104,19 +1105,56 @@ namespace MWWorld
 | 
			
		|||
        return foundCell->second;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const ESM4::Cell* Store<ESM4::Cell>::searchExterior(ESM::ExteriorCellLocation cellIndex) const
 | 
			
		||||
    {
 | 
			
		||||
        const auto foundCell = mExteriors.find(cellIndex);
 | 
			
		||||
        if (foundCell == mExteriors.end())
 | 
			
		||||
            return nullptr;
 | 
			
		||||
        return foundCell->second;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ESM4::Cell* Store<ESM4::Cell>::insert(const ESM4::Cell& item, bool overrideOnly)
 | 
			
		||||
    {
 | 
			
		||||
        auto cellPtr = TypedDynamicStore<ESM4::Cell>::insert(item, overrideOnly);
 | 
			
		||||
        mCellNameIndex[cellPtr->mEditorId] = cellPtr;
 | 
			
		||||
        insertCell(cellPtr);
 | 
			
		||||
        return cellPtr;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ESM4::Cell* Store<ESM4::Cell>::insertStatic(const ESM4::Cell& item)
 | 
			
		||||
    {
 | 
			
		||||
        auto cellPtr = TypedDynamicStore<ESM4::Cell>::insertStatic(item);
 | 
			
		||||
        mCellNameIndex[cellPtr->mEditorId] = cellPtr;
 | 
			
		||||
        insertCell(cellPtr);
 | 
			
		||||
        return cellPtr;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void Store<ESM4::Cell>::insertCell(ESM4::Cell* cellPtr)
 | 
			
		||||
    {
 | 
			
		||||
        if (!cellPtr->mEditorId.empty())
 | 
			
		||||
            mCellNameIndex[cellPtr->mEditorId] = cellPtr;
 | 
			
		||||
        if (cellPtr->isExterior())
 | 
			
		||||
        {
 | 
			
		||||
            ESM::ExteriorCellLocation cellindex = { cellPtr->mX, cellPtr->mY, cellPtr->mParent };
 | 
			
		||||
            if (cellPtr->mCellFlags & ESM4::Rec_Persistent)
 | 
			
		||||
                mPersistentExteriors[cellindex] = cellPtr;
 | 
			
		||||
            else
 | 
			
		||||
                mExteriors[cellindex] = cellPtr;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void Store<ESM4::Cell>::clearDynamic()
 | 
			
		||||
    {
 | 
			
		||||
        for (auto& cellToDeleteIt : mDynamic)
 | 
			
		||||
        {
 | 
			
		||||
            ESM4::Cell& cellToDelete = cellToDeleteIt.second;
 | 
			
		||||
            if (cellToDelete.isExterior())
 | 
			
		||||
            {
 | 
			
		||||
                mExteriors.erase({ cellToDelete.mX, cellToDelete.mY, cellToDelete.mParent });
 | 
			
		||||
            }
 | 
			
		||||
            if (!cellToDelete.mEditorId.empty())
 | 
			
		||||
                mCellNameIndex.erase(cellToDelete.mEditorId);
 | 
			
		||||
        }
 | 
			
		||||
        MWWorld::TypedDynamicStore<ESM4::Cell>::clearDynamic();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template class MWWorld::TypedDynamicStore<ESM::Activator>;
 | 
			
		||||
| 
						 | 
				
			
			@ -1177,3 +1215,4 @@ template class MWWorld::TypedDynamicStore<ESM4::Light>;
 | 
			
		|||
template class MWWorld::TypedDynamicStore<ESM4::Reference, ESM::FormId>;
 | 
			
		||||
template class MWWorld::TypedDynamicStore<ESM4::Cell>;
 | 
			
		||||
template class MWWorld::TypedDynamicStore<ESM4::Weapon>;
 | 
			
		||||
template class MWWorld::TypedDynamicStore<ESM4::World>;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -9,6 +9,7 @@
 | 
			
		|||
#include <vector>
 | 
			
		||||
 | 
			
		||||
#include <components/esm/refid.hpp>
 | 
			
		||||
#include <components/esm/util.hpp>
 | 
			
		||||
#include <components/esm3/loadcell.hpp>
 | 
			
		||||
#include <components/esm3/loaddial.hpp>
 | 
			
		||||
#include <components/esm3/loadglob.hpp>
 | 
			
		||||
| 
						 | 
				
			
			@ -177,6 +178,7 @@ namespace MWWorld
 | 
			
		|||
    template <class T, class Id = ESM::RefId>
 | 
			
		||||
    class TypedDynamicStore : public DynamicStoreBase<Id>
 | 
			
		||||
    {
 | 
			
		||||
    protected:
 | 
			
		||||
        typedef std::unordered_map<Id, T> Static;
 | 
			
		||||
        Static mStatic;
 | 
			
		||||
        /// @par mShared usually preserves the record order as it came from the content files (this
 | 
			
		||||
| 
						 | 
				
			
			@ -283,10 +285,16 @@ namespace MWWorld
 | 
			
		|||
        std::unordered_map<std::string, ESM4::Cell*, Misc::StringUtils::CiHash, Misc::StringUtils::CiEqual>
 | 
			
		||||
            mCellNameIndex;
 | 
			
		||||
 | 
			
		||||
        std::unordered_map<ESM::ExteriorCellLocation, ESM4::Cell*> mExteriors;
 | 
			
		||||
        std::unordered_map<ESM::ExteriorCellLocation, ESM4::Cell*> mPersistentExteriors;
 | 
			
		||||
 | 
			
		||||
    public:
 | 
			
		||||
        const ESM4::Cell* searchCellName(std::string_view) const;
 | 
			
		||||
        const ESM4::Cell* searchExterior(ESM::ExteriorCellLocation cellIndex) const;
 | 
			
		||||
        ESM4::Cell* insert(const ESM4::Cell& item, bool overrideOnly = false);
 | 
			
		||||
        ESM4::Cell* insertStatic(const ESM4::Cell& item);
 | 
			
		||||
        void insertCell(ESM4::Cell* cell);
 | 
			
		||||
        void clearDynamic() override;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    template <>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -95,7 +95,6 @@
 | 
			
		|||
#include "projectilemanager.hpp"
 | 
			
		||||
#include "weather.hpp"
 | 
			
		||||
 | 
			
		||||
#include "cellutils.hpp"
 | 
			
		||||
#include "contentloader.hpp"
 | 
			
		||||
#include "esmloader.hpp"
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -353,9 +352,10 @@ namespace MWWorld
 | 
			
		|||
        if (bypass && !mStartCell.empty())
 | 
			
		||||
        {
 | 
			
		||||
            ESM::Position pos;
 | 
			
		||||
            if (!findExteriorPosition(mStartCell, pos).empty())
 | 
			
		||||
            ESM::RefId cellId = findExteriorPosition(mStartCell, pos);
 | 
			
		||||
            if (!cellId.empty())
 | 
			
		||||
            {
 | 
			
		||||
                changeToExteriorCell(pos, true);
 | 
			
		||||
                changeToCell(cellId, pos, true);
 | 
			
		||||
                adjustPosition(getPlayerPtr(), false);
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
| 
						 | 
				
			
			@ -379,8 +379,8 @@ namespace MWWorld
 | 
			
		|||
                pos.rot[1] = 0;
 | 
			
		||||
                pos.rot[2] = 0;
 | 
			
		||||
 | 
			
		||||
                osg::Vec2i exteriorCellPos = positionToCellIndex(pos.pos[0], pos.pos[1]);
 | 
			
		||||
                ESM::RefId cellId = ESM::RefId::esm3ExteriorCell(exteriorCellPos.x(), exteriorCellPos.y());
 | 
			
		||||
                ESM::ExteriorCellLocation exteriorCellPos = ESM::positionToCellIndex(pos.pos[0], pos.pos[1]);
 | 
			
		||||
                ESM::RefId cellId = ESM::RefId::esm3ExteriorCell(exteriorCellPos.mX, exteriorCellPos.mY);
 | 
			
		||||
                mWorldScene->changeToExteriorCell(cellId, pos, true);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -967,25 +967,6 @@ namespace MWWorld
 | 
			
		|||
        mRendering->getCamera()->instantTransition();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void World::changeToExteriorCell(const ESM::Position& position, bool adjustPlayerPos, bool changeEvent)
 | 
			
		||||
    {
 | 
			
		||||
        mPhysics->clearQueuedMovement();
 | 
			
		||||
        mDiscardMovements = true;
 | 
			
		||||
 | 
			
		||||
        if (changeEvent && mCurrentWorldSpace != ESM::Cell::sDefaultWorldspace)
 | 
			
		||||
        {
 | 
			
		||||
            // changed worldspace
 | 
			
		||||
            mProjectileManager->clear();
 | 
			
		||||
            mRendering->notifyWorldSpaceChanged();
 | 
			
		||||
        }
 | 
			
		||||
        removeContainerScripts(getPlayerPtr());
 | 
			
		||||
        osg::Vec2i exteriorCellPos = positionToCellIndex(position.pos[0], position.pos[1]);
 | 
			
		||||
        ESM::RefId cellId = ESM::RefId::esm3ExteriorCell(exteriorCellPos.x(), exteriorCellPos.y());
 | 
			
		||||
        mWorldScene->changeToExteriorCell(cellId, position, adjustPlayerPos, changeEvent);
 | 
			
		||||
        addContainerScripts(getPlayerPtr(), getPlayerPtr().getCell());
 | 
			
		||||
        mRendering->getCamera()->instantTransition();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void World::changeToCell(
 | 
			
		||||
        const ESM::RefId& cellId, const ESM::Position& position, bool adjustPlayerPos, bool changeEvent)
 | 
			
		||||
    {
 | 
			
		||||
| 
						 | 
				
			
			@ -1263,15 +1244,17 @@ namespace MWWorld
 | 
			
		|||
 | 
			
		||||
    MWWorld::Ptr World::moveObject(const Ptr& ptr, const osg::Vec3f& position, bool movePhysics, bool moveToActive)
 | 
			
		||||
    {
 | 
			
		||||
        const osg::Vec2i index = positionToCellIndex(position.x(), position.y());
 | 
			
		||||
 | 
			
		||||
        CellStore* cell = ptr.getCell();
 | 
			
		||||
        CellStore& newCell = mWorldModel.getExterior(index.x(), index.y());
 | 
			
		||||
        bool isCellActive
 | 
			
		||||
            = getPlayerPtr().isInCell() && getPlayerPtr().getCell()->isExterior() && mWorldScene->isCellActive(newCell);
 | 
			
		||||
        ESM::RefId worldspaceId
 | 
			
		||||
            = cell->isExterior() ? cell->getCell()->getWorldSpace() : ESM::Cell::sDefaultWorldspaceId;
 | 
			
		||||
        const ESM::ExteriorCellLocation index = ESM::positionToCellIndex(position.x(), position.y(), worldspaceId);
 | 
			
		||||
 | 
			
		||||
        CellStore* newCell = cell->isExterior() ? &mWorldModel.getExterior(index) : nullptr;
 | 
			
		||||
        bool isCellActive = getPlayerPtr().isInCell() && getPlayerPtr().getCell()->isExterior()
 | 
			
		||||
            && (newCell && mWorldScene->isCellActive(*newCell));
 | 
			
		||||
 | 
			
		||||
        if (cell->isExterior() || (moveToActive && isCellActive && ptr.getClass().isActor()))
 | 
			
		||||
            cell = &newCell;
 | 
			
		||||
            cell = newCell;
 | 
			
		||||
 | 
			
		||||
        return moveObject(ptr, cell, position, movePhysics);
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -1388,7 +1371,8 @@ namespace MWWorld
 | 
			
		|||
            && !(ptr.getClass().isPersistent(ptr) && ptr.getClass().getCreatureStats(ptr).isDeathAnimationFinished());
 | 
			
		||||
        if (force || !ptr.getClass().isActor() || (!isFlying(ptr) && !swims && isActorCollisionEnabled(ptr)))
 | 
			
		||||
        {
 | 
			
		||||
            osg::Vec3f traced = mPhysics->traceDown(ptr, pos, Constants::CellSizeInUnits);
 | 
			
		||||
            osg::Vec3f traced
 | 
			
		||||
                = mPhysics->traceDown(ptr, pos, ESM::getCellSize(ptr.getCell()->getCell()->getWorldSpace()));
 | 
			
		||||
            pos.z() = std::min(pos.z(), traced.z());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1423,9 +1407,9 @@ namespace MWWorld
 | 
			
		|||
            if (!mPhysics->castRay(pos, targetPos, MWPhysics::CollisionType_World | MWPhysics::CollisionType_Door).mHit)
 | 
			
		||||
                break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        targetPos.z() += distance / 2.f; // move up a bit to get out from geometry, will snap down later
 | 
			
		||||
        osg::Vec3f traced = mPhysics->traceDown(actor, targetPos, Constants::CellSizeInUnits);
 | 
			
		||||
        osg::Vec3f traced
 | 
			
		||||
            = mPhysics->traceDown(actor, targetPos, ESM::getCellSize(actor.getCell()->getCell()->getWorldSpace()));
 | 
			
		||||
        if (traced != pos)
 | 
			
		||||
        {
 | 
			
		||||
            esmPos.pos[0] = traced.x();
 | 
			
		||||
| 
						 | 
				
			
			@ -1509,20 +1493,6 @@ namespace MWWorld
 | 
			
		|||
        return placed;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void World::indexToPosition(int cellX, int cellY, float& x, float& y, bool centre) const
 | 
			
		||||
    {
 | 
			
		||||
        const int cellSize = Constants::CellSizeInUnits;
 | 
			
		||||
 | 
			
		||||
        x = static_cast<float>(cellSize * cellX);
 | 
			
		||||
        y = static_cast<float>(cellSize * cellY);
 | 
			
		||||
 | 
			
		||||
        if (centre)
 | 
			
		||||
        {
 | 
			
		||||
            x += cellSize / 2;
 | 
			
		||||
            y += cellSize / 2;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void World::queueMovement(const Ptr& ptr, const osg::Vec3f& velocity)
 | 
			
		||||
    {
 | 
			
		||||
        mPhysics->queueObjectMovement(ptr, velocity);
 | 
			
		||||
| 
						 | 
				
			
			@ -1921,6 +1891,14 @@ namespace MWWorld
 | 
			
		|||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ESM::RefId World::getCurrentWorldspace() const
 | 
			
		||||
    {
 | 
			
		||||
        const CellStore* cellStore = mWorldScene->getCurrentCell();
 | 
			
		||||
        if (cellStore)
 | 
			
		||||
            return cellStore->getCell()->getWorldSpace();
 | 
			
		||||
        return ESM::Cell::sDefaultWorldspaceId;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int World::getCurrentWeather() const
 | 
			
		||||
    {
 | 
			
		||||
        return mWeatherManager->getWeatherID();
 | 
			
		||||
| 
						 | 
				
			
			@ -2068,8 +2046,9 @@ namespace MWWorld
 | 
			
		|||
            throw std::runtime_error("copyObjectToCell(): cannot copy object to null cell");
 | 
			
		||||
        if (cell->isExterior())
 | 
			
		||||
        {
 | 
			
		||||
            const osg::Vec2i index = positionToCellIndex(pos.pos[0], pos.pos[1]);
 | 
			
		||||
            cell = &mWorldModel.getExterior(index.x(), index.y());
 | 
			
		||||
            const ESM::ExteriorCellLocation index
 | 
			
		||||
                = ESM::positionToCellIndex(pos.pos[0], pos.pos[1], cell->getCell()->getWorldSpace());
 | 
			
		||||
            cell = &mWorldModel.getExterior(index);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        MWWorld::Ptr dropped = object.getClass().copyToCell(object, *cell, pos, count);
 | 
			
		||||
| 
						 | 
				
			
			@ -2753,7 +2732,8 @@ namespace MWWorld
 | 
			
		|||
                if (xResult.ec == std::errc::result_out_of_range || yResult.ec == std::errc::result_out_of_range)
 | 
			
		||||
                    throw std::runtime_error("Cell coordinates out of range.");
 | 
			
		||||
                else if (xResult.ec == std::errc{} && yResult.ec == std::errc{})
 | 
			
		||||
                    ext = mWorldModel.getExterior(x, y).getCell();
 | 
			
		||||
                    ext = mWorldModel.getExterior(ESM::ExteriorCellLocation(x, y, ESM::Cell::sDefaultWorldspaceId))
 | 
			
		||||
                              .getCell();
 | 
			
		||||
                // ignore std::errc::invalid_argument, as this means that name probably refers to a interior cell
 | 
			
		||||
                // instead of comma separated coordinates
 | 
			
		||||
            }
 | 
			
		||||
| 
						 | 
				
			
			@ -2763,7 +2743,9 @@ namespace MWWorld
 | 
			
		|||
        {
 | 
			
		||||
            int x = ext->getGridX();
 | 
			
		||||
            int y = ext->getGridY();
 | 
			
		||||
            indexToPosition(x, y, pos.pos[0], pos.pos[1], true);
 | 
			
		||||
            osg::Vec2 posFromIndex = indexToPosition(ESM::ExteriorCellLocation(x, y, ext->getWorldSpace()), true);
 | 
			
		||||
            pos.pos[0] = posFromIndex.x();
 | 
			
		||||
            pos.pos[1] = posFromIndex.y();
 | 
			
		||||
 | 
			
		||||
            // Note: Z pos will be adjusted by adjustPosition later
 | 
			
		||||
            pos.pos[2] = 0;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -246,6 +246,8 @@ namespace MWWorld
 | 
			
		|||
 | 
			
		||||
        bool isCellQuasiExterior() const override;
 | 
			
		||||
 | 
			
		||||
        ESM::RefId getCurrentWorldspace() const;
 | 
			
		||||
 | 
			
		||||
        void getDoorMarkers(MWWorld::CellStore& cell, std::vector<DoorMarker>& out) override;
 | 
			
		||||
        ///< get a list of teleport door markers for a given cell, to be displayed on the local map
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -345,11 +347,6 @@ namespace MWWorld
 | 
			
		|||
        ///< Move to interior cell.
 | 
			
		||||
        ///< @param changeEvent If false, do not trigger cell change flag or detect worldspace changes
 | 
			
		||||
 | 
			
		||||
        void changeToExteriorCell(
 | 
			
		||||
            const ESM::Position& position, bool adjustPlayerPos, bool changeEvent = true) override;
 | 
			
		||||
        ///< Move to exterior cell.
 | 
			
		||||
        ///< @param changeEvent If false, do not trigger cell change flag or detect worldspace changes
 | 
			
		||||
 | 
			
		||||
        void changeToCell(const ESM::RefId& cellId, const ESM::Position& position, bool adjustPlayerPos,
 | 
			
		||||
            bool changeEvent = true) override;
 | 
			
		||||
        ///< @param changeEvent If false, do not trigger cell change flag or detect worldspace changes
 | 
			
		||||
| 
						 | 
				
			
			@ -403,9 +400,6 @@ namespace MWWorld
 | 
			
		|||
 | 
			
		||||
        float getMaxActivationDistance() const override;
 | 
			
		||||
 | 
			
		||||
        void indexToPosition(int cellX, int cellY, float& x, float& y, bool centre = false) const override;
 | 
			
		||||
        ///< Convert cell numbers to position.
 | 
			
		||||
 | 
			
		||||
        void queueMovement(const Ptr& ptr, const osg::Vec3f& velocity) override;
 | 
			
		||||
        ///< Queues movement for \a ptr (in local space), to be applied in the next call to
 | 
			
		||||
        /// doPhysics.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -8,6 +8,7 @@
 | 
			
		|||
#include <components/esm3/esmreader.hpp>
 | 
			
		||||
#include <components/esm3/esmwriter.hpp>
 | 
			
		||||
#include <components/esm3/loadregn.hpp>
 | 
			
		||||
#include <components/esm4/loadwrld.hpp>
 | 
			
		||||
#include <components/loadinglistener/loadinglistener.hpp>
 | 
			
		||||
#include <components/settings/settings.hpp>
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -15,7 +16,6 @@
 | 
			
		|||
#include "../mwbase/world.hpp"
 | 
			
		||||
 | 
			
		||||
#include "cellstore.hpp"
 | 
			
		||||
#include "cellutils.hpp"
 | 
			
		||||
#include "esmstore.hpp"
 | 
			
		||||
 | 
			
		||||
namespace
 | 
			
		||||
| 
						 | 
				
			
			@ -76,11 +76,11 @@ MWWorld::CellStore& MWWorld::WorldModel::getCellStore(const ESM::Cell* cell)
 | 
			
		|||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        std::map<std::pair<int, int>, CellStore*>::iterator result
 | 
			
		||||
            = mExteriors.find(std::make_pair(cell->getGridX(), cell->getGridY()));
 | 
			
		||||
        ESM::ExteriorCellLocation extIndex(cell->getGridX(), cell->getGridY(), ESM::Cell::sDefaultWorldspaceId);
 | 
			
		||||
        std::map<ESM::ExteriorCellLocation, CellStore*>::iterator result = mExteriors.find(extIndex);
 | 
			
		||||
 | 
			
		||||
        if (result == mExteriors.end())
 | 
			
		||||
            result = mExteriors.emplace(std::make_pair(cell->getGridX(), cell->getGridY()), cellStore).first;
 | 
			
		||||
            result = mExteriors.emplace(extIndex, cellStore).first;
 | 
			
		||||
 | 
			
		||||
        return *result->second;
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -160,33 +160,57 @@ MWWorld::WorldModel::WorldModel(const MWWorld::ESMStore& store, ESM::ReadersCach
 | 
			
		|||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
MWWorld::CellStore& MWWorld::WorldModel::getExterior(int x, int y)
 | 
			
		||||
MWWorld::CellStore& MWWorld::WorldModel::getExterior(ESM::ExteriorCellLocation cellIndex)
 | 
			
		||||
{
 | 
			
		||||
    std::map<std::pair<int, int>, CellStore*>::iterator result = mExteriors.find(std::make_pair(x, y));
 | 
			
		||||
    std::map<ESM::ExteriorCellLocation, CellStore*>::iterator result;
 | 
			
		||||
 | 
			
		||||
    result = mExteriors.find(cellIndex);
 | 
			
		||||
 | 
			
		||||
    if (result == mExteriors.end())
 | 
			
		||||
    {
 | 
			
		||||
        const ESM::Cell* cell = mStore.get<ESM::Cell>().search(x, y);
 | 
			
		||||
 | 
			
		||||
        if (!cell)
 | 
			
		||||
        if (!ESM::isEsm4Ext(cellIndex.mWorldspace))
 | 
			
		||||
        {
 | 
			
		||||
            // Cell isn't predefined. Make one on the fly.
 | 
			
		||||
            ESM::Cell record;
 | 
			
		||||
            record.mData.mFlags = ESM::Cell::HasWater;
 | 
			
		||||
            record.mData.mX = x;
 | 
			
		||||
            record.mData.mY = y;
 | 
			
		||||
            record.mWater = 0;
 | 
			
		||||
            record.mMapColor = 0;
 | 
			
		||||
            record.updateId();
 | 
			
		||||
            const ESM::Cell* cell = mStore.get<ESM::Cell>().search(cellIndex.mX, cellIndex.mY);
 | 
			
		||||
 | 
			
		||||
            cell = MWBase::Environment::get().getESMStore()->insert(record);
 | 
			
		||||
            if (cell == nullptr)
 | 
			
		||||
            {
 | 
			
		||||
                // Cell isn't predefined. Make one on the fly.
 | 
			
		||||
                ESM::Cell record;
 | 
			
		||||
                record.mData.mFlags = ESM::Cell::HasWater;
 | 
			
		||||
                record.mData.mX = cellIndex.mX;
 | 
			
		||||
                record.mData.mY = cellIndex.mY;
 | 
			
		||||
                record.mWater = 0;
 | 
			
		||||
                record.mMapColor = 0;
 | 
			
		||||
                record.updateId();
 | 
			
		||||
 | 
			
		||||
                cell = MWBase::Environment::get().getESMStore()->insert(record);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            CellStore* cellStore
 | 
			
		||||
                = &mCells.emplace(cell->mId, CellStore(MWWorld::Cell(*cell), mStore, mReaders)).first->second;
 | 
			
		||||
            result = mExteriors.emplace(cellIndex, cellStore).first;
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            const Store<ESM4::Cell>& cell4Store = mStore.get<ESM4::Cell>();
 | 
			
		||||
            bool exteriorExists = mStore.get<ESM4::World>().search(cellIndex.mWorldspace) != nullptr;
 | 
			
		||||
            const ESM4::Cell* cell = cell4Store.searchExterior(cellIndex);
 | 
			
		||||
            if (!exteriorExists)
 | 
			
		||||
                throw std::runtime_error("Exterior ESM4 world is not found: " + cellIndex.mWorldspace.toDebugString());
 | 
			
		||||
            if (cell == nullptr)
 | 
			
		||||
            {
 | 
			
		||||
                ESM4::Cell record;
 | 
			
		||||
                record.mParent = cellIndex.mWorldspace;
 | 
			
		||||
                record.mX = cellIndex.mX;
 | 
			
		||||
                record.mY = cellIndex.mY;
 | 
			
		||||
                record.mCellFlags = !ESM4::CELL_Interior;
 | 
			
		||||
                cell = MWBase::Environment::get().getESMStore()->insert(record);
 | 
			
		||||
            }
 | 
			
		||||
            CellStore* cellStore
 | 
			
		||||
                = &mCells.emplace(cell->mId, CellStore(MWWorld::Cell(*cell), mStore, mReaders)).first->second;
 | 
			
		||||
            result = mExteriors.emplace(cellIndex, cellStore).first;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        CellStore* cellStore
 | 
			
		||||
            = &mCells.emplace(cell->mId, CellStore(MWWorld::Cell(*cell), mStore, mReaders)).first->second;
 | 
			
		||||
        result = mExteriors.emplace(std::make_pair(x, y), cellStore).first;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (result->second->getState() != CellStore::State_Loaded)
 | 
			
		||||
    {
 | 
			
		||||
        result->second->load();
 | 
			
		||||
| 
						 | 
				
			
			@ -232,7 +256,8 @@ MWWorld::CellStore& MWWorld::WorldModel::getCell(const ESM::RefId& id)
 | 
			
		|||
        return result->second;
 | 
			
		||||
 | 
			
		||||
    if (const auto* exteriorId = id.getIf<ESM::ESM3ExteriorCellRefId>())
 | 
			
		||||
        return getExterior(exteriorId->getX(), exteriorId->getY());
 | 
			
		||||
        return getExterior(
 | 
			
		||||
            ESM::ExteriorCellLocation(exteriorId->getX(), exteriorId->getY(), ESM::Cell::sDefaultWorldspaceId));
 | 
			
		||||
 | 
			
		||||
    const ESM4::Cell* cell4 = mStore.get<ESM4::Cell>().search(id);
 | 
			
		||||
    CellStore* newCellStore = nullptr;
 | 
			
		||||
| 
						 | 
				
			
			@ -249,7 +274,8 @@ MWWorld::CellStore& MWWorld::WorldModel::getCell(const ESM::RefId& id)
 | 
			
		|||
    {
 | 
			
		||||
        std::pair<int, int> coord
 | 
			
		||||
            = std::make_pair(newCellStore->getCell()->getGridX(), newCellStore->getCell()->getGridY());
 | 
			
		||||
        mExteriors.emplace(coord, newCellStore);
 | 
			
		||||
        ESM::ExteriorCellLocation extIndex = { coord.first, coord.second, newCellStore->getCell()->getWorldSpace() };
 | 
			
		||||
        mExteriors.emplace(extIndex, newCellStore);
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
| 
						 | 
				
			
			@ -293,7 +319,7 @@ MWWorld::CellStore& MWWorld::WorldModel::getCell(std::string_view name)
 | 
			
		|||
    if (!cell)
 | 
			
		||||
        throw std::runtime_error(std::string("Can't find cell with name ") + std::string(name));
 | 
			
		||||
 | 
			
		||||
    return getExterior(cell->getGridX(), cell->getGridY());
 | 
			
		||||
    return getExterior(ESM::ExteriorCellLocation(cell->getGridX(), cell->getGridY(), ESM::Cell::sDefaultWorldspaceId));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
MWWorld::CellStore& MWWorld::WorldModel::getCellByPosition(
 | 
			
		||||
| 
						 | 
				
			
			@ -301,8 +327,11 @@ MWWorld::CellStore& MWWorld::WorldModel::getCellByPosition(
 | 
			
		|||
{
 | 
			
		||||
    if (cellInSameWorldSpace && !cellInSameWorldSpace->isExterior())
 | 
			
		||||
        return *cellInSameWorldSpace;
 | 
			
		||||
    const osg::Vec2i cellIndex = positionToCellIndex(pos.x(), pos.y());
 | 
			
		||||
    return getExterior(cellIndex.x(), cellIndex.y());
 | 
			
		||||
    ESM::RefId exteriorWorldspace
 | 
			
		||||
        = cellInSameWorldSpace ? cellInSameWorldSpace->getCell()->getWorldSpace() : ESM::Cell::sDefaultWorldspaceId;
 | 
			
		||||
    const ESM::ExteriorCellLocation cellIndex = ESM::positionToCellIndex(pos.x(), pos.y(), exteriorWorldspace);
 | 
			
		||||
 | 
			
		||||
    return getExterior(cellIndex);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
MWWorld::Ptr MWWorld::WorldModel::getPtr(const ESM::RefId& name, CellStore& cell)
 | 
			
		||||
| 
						 | 
				
			
			@ -342,8 +371,7 @@ MWWorld::Ptr MWWorld::WorldModel::getPtr(const ESM::RefId& name)
 | 
			
		|||
    // Then check cells that are already listed
 | 
			
		||||
    // Search in reverse, this is a workaround for an ambiguous chargen_plank reference in the vanilla game.
 | 
			
		||||
    // there is one at -22,16 and one at -2,-9, the latter should be used.
 | 
			
		||||
    for (std::map<std::pair<int, int>, CellStore*>::reverse_iterator iter = mExteriors.rbegin();
 | 
			
		||||
         iter != mExteriors.rend(); ++iter)
 | 
			
		||||
    for (auto iter = mExteriors.rbegin(); iter != mExteriors.rend(); ++iter)
 | 
			
		||||
    {
 | 
			
		||||
        Ptr ptr = getPtrAndCache(name, *iter->second);
 | 
			
		||||
        if (!ptr.isEmpty())
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,6 +7,7 @@
 | 
			
		|||
#include <unordered_map>
 | 
			
		||||
#include <variant>
 | 
			
		||||
 | 
			
		||||
#include <components/esm/util.hpp>
 | 
			
		||||
#include <components/misc/algorithm.hpp>
 | 
			
		||||
 | 
			
		||||
#include "cellstore.hpp"
 | 
			
		||||
| 
						 | 
				
			
			@ -42,7 +43,8 @@ namespace MWWorld
 | 
			
		|||
        ESM::ReadersCache& mReaders;
 | 
			
		||||
        mutable std::unordered_map<ESM::RefId, CellStore> mCells;
 | 
			
		||||
        mutable std::map<std::string, CellStore*, Misc::StringUtils::CiComp> mInteriors;
 | 
			
		||||
        mutable std::map<std::pair<int, int>, CellStore*> mExteriors;
 | 
			
		||||
 | 
			
		||||
        mutable std::map<ESM::ExteriorCellLocation, CellStore*> mExteriors;
 | 
			
		||||
        IdCache mIdCache;
 | 
			
		||||
        std::size_t mIdCacheIndex = 0;
 | 
			
		||||
        std::unordered_map<ESM::RefNum, Ptr> mPtrIndex;
 | 
			
		||||
| 
						 | 
				
			
			@ -63,7 +65,7 @@ namespace MWWorld
 | 
			
		|||
 | 
			
		||||
        void clear();
 | 
			
		||||
 | 
			
		||||
        CellStore& getExterior(int x, int y);
 | 
			
		||||
        CellStore& getExterior(ESM::ExteriorCellLocation cellIndex);
 | 
			
		||||
        CellStore& getInterior(std::string_view name);
 | 
			
		||||
        CellStore& getCell(std::string_view name); // interior or named exterior
 | 
			
		||||
        CellStore& getCell(const ESM::RefId& Id);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -20,6 +20,7 @@
 | 
			
		|||
#include <components/esm4/loadligh.hpp>
 | 
			
		||||
#include <components/esm4/loadrefr.hpp>
 | 
			
		||||
#include <components/esm4/loadstat.hpp>
 | 
			
		||||
#include <components/esm4/loadwrld.hpp>
 | 
			
		||||
#include <components/esm4/reader.hpp>
 | 
			
		||||
#include <components/esm4/readerutils.hpp>
 | 
			
		||||
#include <components/files/configurationmanager.hpp>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										16
									
								
								components/esm/util.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								components/esm/util.cpp
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,16 @@
 | 
			
		|||
#include "util.hpp"
 | 
			
		||||
 | 
			
		||||
osg::Vec2 ESM::indexToPosition(const ESM::ExteriorCellLocation& cellIndex, bool centre)
 | 
			
		||||
{
 | 
			
		||||
    const int cellSize = ESM::getCellSize(cellIndex.mWorldspace);
 | 
			
		||||
 | 
			
		||||
    float x = static_cast<float>(cellSize * cellIndex.mX);
 | 
			
		||||
    float y = static_cast<float>(cellSize * cellIndex.mY);
 | 
			
		||||
 | 
			
		||||
    if (centre)
 | 
			
		||||
    {
 | 
			
		||||
        x += cellSize / 2;
 | 
			
		||||
        y += cellSize / 2;
 | 
			
		||||
    }
 | 
			
		||||
    return osg::Vec2(x, y);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,9 +1,16 @@
 | 
			
		|||
#ifndef OPENMW_ESM_UTIL_H
 | 
			
		||||
#define OPENMW_ESM_UTIL_H
 | 
			
		||||
#include <cmath>
 | 
			
		||||
 | 
			
		||||
#include <osg/Quat>
 | 
			
		||||
#include <osg/Vec2>
 | 
			
		||||
#include <osg/Vec2i>
 | 
			
		||||
#include <osg/Vec3f>
 | 
			
		||||
 | 
			
		||||
#include <components/esm/refid.hpp>
 | 
			
		||||
#include <components/esm3/loadcell.hpp>
 | 
			
		||||
#include <components/misc/constants.hpp>
 | 
			
		||||
 | 
			
		||||
namespace ESM
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -42,6 +49,66 @@ namespace ESM
 | 
			
		|||
        operator osg::Vec3f() const { return osg::Vec3f(mValues[0], mValues[1], mValues[2]); }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    struct ExteriorCellLocation
 | 
			
		||||
    {
 | 
			
		||||
        int mX, mY;
 | 
			
		||||
        ESM::RefId mWorldspace;
 | 
			
		||||
 | 
			
		||||
        ExteriorCellLocation(int x, int y, ESM::RefId worldspace)
 | 
			
		||||
            : mX(x)
 | 
			
		||||
            , mY(y)
 | 
			
		||||
            , mWorldspace(worldspace)
 | 
			
		||||
        {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        bool operator==(const ExteriorCellLocation& other) const
 | 
			
		||||
        {
 | 
			
		||||
            return mX == other.mX && mY == other.mY && mWorldspace == other.mWorldspace;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        bool operator<(const ExteriorCellLocation& other) const
 | 
			
		||||
        {
 | 
			
		||||
            return std::make_tuple(mX, mY, mWorldspace) < std::make_tuple(other.mX, other.mY, other.mWorldspace);
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    inline bool isEsm4Ext(ESM::RefId worldspaceId)
 | 
			
		||||
    {
 | 
			
		||||
        return worldspaceId != ESM::Cell::sDefaultWorldspaceId;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    inline int getCellSize(ESM::RefId worldspaceId)
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        return isEsm4Ext(worldspaceId) ? Constants::ESM4CellSizeInUnits : Constants::CellSizeInUnits;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    inline ESM::ExteriorCellLocation positionToCellIndex(
 | 
			
		||||
        float x, float y, ESM::RefId worldspaceId = ESM::Cell::sDefaultWorldspaceId)
 | 
			
		||||
    {
 | 
			
		||||
        const float cellSize = getCellSize(worldspaceId);
 | 
			
		||||
        return { static_cast<int>(std::floor(x / cellSize)), static_cast<int>(std::floor(y / cellSize)), worldspaceId };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    osg::Vec2 indexToPosition(const ESM::ExteriorCellLocation& cellIndex, bool centre = false);
 | 
			
		||||
    ///< Convert cell numbers to position.
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
namespace std
 | 
			
		||||
{
 | 
			
		||||
    template <>
 | 
			
		||||
    struct hash<ESM::ExteriorCellLocation>
 | 
			
		||||
    {
 | 
			
		||||
        std::size_t operator()(const ESM::ExteriorCellLocation& toHash) const
 | 
			
		||||
        {
 | 
			
		||||
            // Compute individual hash values for first,
 | 
			
		||||
            // second and third and combine them using XOR
 | 
			
		||||
            // and bit shifting:
 | 
			
		||||
 | 
			
		||||
            return ((hash<int>()(toHash.mX) ^ (hash<int>()(toHash.mY) << 1)) >> 1)
 | 
			
		||||
                ^ (hash<ESM::RefId>()(toHash.mWorldspace) << 1);
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -39,7 +39,7 @@ namespace ESM
 | 
			
		|||
 | 
			
		||||
namespace ESM
 | 
			
		||||
{
 | 
			
		||||
    const std::string Cell::sDefaultWorldspace = "sys::default";
 | 
			
		||||
    const StringRefId Cell::sDefaultWorldspaceId = StringRefId("sys::default");
 | 
			
		||||
 | 
			
		||||
    // Some overloaded compare operators.
 | 
			
		||||
    bool operator==(const MovedCellRef& ref, const RefNum& refNum)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -66,7 +66,7 @@ namespace ESM
 | 
			
		|||
     */
 | 
			
		||||
    struct Cell
 | 
			
		||||
    {
 | 
			
		||||
        static const std::string sDefaultWorldspace;
 | 
			
		||||
        static const ESM::StringRefId sDefaultWorldspaceId;
 | 
			
		||||
 | 
			
		||||
        constexpr static RecNameInts sRecordId = REC_CELL;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -33,6 +33,7 @@
 | 
			
		|||
#include <cassert>
 | 
			
		||||
#include <cfloat> // FLT_MAX for gcc
 | 
			
		||||
#include <iostream> // FIXME: debug only
 | 
			
		||||
#include <limits>
 | 
			
		||||
#include <stdexcept>
 | 
			
		||||
 | 
			
		||||
#include "grouptype.hpp"
 | 
			
		||||
| 
						 | 
				
			
			@ -41,6 +42,8 @@
 | 
			
		|||
 | 
			
		||||
#include <components/esm/refid.hpp>
 | 
			
		||||
 | 
			
		||||
float ESM4::Cell::sInvalidWaterLevel = -200000.f;
 | 
			
		||||
 | 
			
		||||
// TODO: Try loading only EDID and XCLC (along with mFormId, mFlags and mParent)
 | 
			
		||||
//
 | 
			
		||||
// But, for external cells we may be scanning the whole record since we don't know if there is
 | 
			
		||||
| 
						 | 
				
			
			@ -55,7 +58,7 @@ void ESM4::Cell::load(ESM4::Reader& reader)
 | 
			
		|||
    mId = ESM::RefId::formIdRefId(mFormId);
 | 
			
		||||
    mFlags = reader.hdr().record.flags;
 | 
			
		||||
    mParent = ESM::RefId::formIdRefId(reader.currWorld());
 | 
			
		||||
 | 
			
		||||
    mWaterHeight = sInvalidWaterLevel;
 | 
			
		||||
    reader.clearCellGrid(); // clear until XCLC FIXME: somehow do this automatically?
 | 
			
		||||
 | 
			
		||||
    // Sometimes cell 0,0 does not have an XCLC sub record (e.g. ToddLand 000009BF)
 | 
			
		||||
| 
						 | 
				
			
			@ -76,6 +79,7 @@ void ESM4::Cell::load(ESM4::Reader& reader)
 | 
			
		|||
    reader.setCurrCell(mFormId); // save for LAND (and other children) to access later
 | 
			
		||||
    std::uint32_t esmVer = reader.esmVersion();
 | 
			
		||||
    bool isFONV = esmVer == ESM::VER_132 || esmVer == ESM::VER_133 || esmVer == ESM::VER_134;
 | 
			
		||||
    bool isSkyrim = (esmVer == ESM::VER_170 || esmVer == ESM::VER_094);
 | 
			
		||||
 | 
			
		||||
    while (reader.getSubRecordHeader())
 | 
			
		||||
    {
 | 
			
		||||
| 
						 | 
				
			
			@ -237,7 +241,11 @@ void ESM4::Cell::load(ESM4::Reader& reader)
 | 
			
		|||
                throw std::runtime_error("ESM4::CELL::load - Unknown subrecord " + ESM::printName(subHdr.typeId));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (isSkyrim) // Skyrim seems to have broken water level records. But the subrecord exists so it
 | 
			
		||||
                  // shouldn't be skipped.
 | 
			
		||||
    {
 | 
			
		||||
        mWaterHeight = sInvalidWaterLevel;
 | 
			
		||||
    }
 | 
			
		||||
    mReaderContext = reader.getContext();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -107,6 +107,8 @@ namespace ESM4
 | 
			
		|||
        int getGridX() const { return mX; }
 | 
			
		||||
        int getGridY() const { return mY; }
 | 
			
		||||
        bool isExterior() const { return !(mCellFlags & CELL_Interior); }
 | 
			
		||||
 | 
			
		||||
        static float sInvalidWaterLevel;
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -34,8 +34,8 @@
 | 
			
		|||
 | 
			
		||||
void ESM4::World::load(ESM4::Reader& reader)
 | 
			
		||||
{
 | 
			
		||||
    mFormId = reader.hdr().record.getFormId();
 | 
			
		||||
    reader.adjustFormId(mFormId);
 | 
			
		||||
    FormId formid = reader.hdr().record.getFormId();
 | 
			
		||||
    reader.adjustFormId(formid);
 | 
			
		||||
    mFlags = reader.hdr().record.flags;
 | 
			
		||||
 | 
			
		||||
    // It should be possible to save the current world formId automatically while reading in
 | 
			
		||||
| 
						 | 
				
			
			@ -45,7 +45,8 @@ void ESM4::World::load(ESM4::Reader& reader)
 | 
			
		|||
    // Alternatively it may be possible to figure it out by examining the group headers, but
 | 
			
		||||
    // apparently the label field is not reliable so the parent world formid may have been
 | 
			
		||||
    // corrupted by the use of ignore flag (TODO: should check to verify).
 | 
			
		||||
    reader.setCurrWorld(mFormId); // save for CELL later
 | 
			
		||||
    reader.setCurrWorld(formid); // save for CELL later
 | 
			
		||||
    mId = ESM::FormIdRefId(formid);
 | 
			
		||||
 | 
			
		||||
    std::uint32_t subSize = 0; // for XXXX sub record
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -34,6 +34,9 @@
 | 
			
		|||
#include "formid.hpp"
 | 
			
		||||
#include "grid.hpp"
 | 
			
		||||
 | 
			
		||||
#include <components/esm/defs.hpp>
 | 
			
		||||
#include <components/esm/refid.hpp>
 | 
			
		||||
 | 
			
		||||
namespace ESM4
 | 
			
		||||
{
 | 
			
		||||
    class Reader;
 | 
			
		||||
| 
						 | 
				
			
			@ -81,7 +84,7 @@ namespace ESM4
 | 
			
		|||
            float initialPitch;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        FormId mFormId; // from the header
 | 
			
		||||
        ESM::RefId mId; // from the header
 | 
			
		||||
        std::uint32_t mFlags; // from the header, see enum type RecordFlag for details
 | 
			
		||||
 | 
			
		||||
        std::string mEditorId;
 | 
			
		||||
| 
						 | 
				
			
			@ -129,6 +132,7 @@ namespace ESM4
 | 
			
		|||
 | 
			
		||||
        void load(ESM4::Reader& reader);
 | 
			
		||||
        // void save(ESM4::Writer& writer) const;
 | 
			
		||||
        static constexpr ESM::RecNameInts sRecordId = ESM::REC_WRLD4;
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -23,6 +23,7 @@ namespace Constants
 | 
			
		|||
 | 
			
		||||
    // Size of one exterior cell in game units
 | 
			
		||||
    constexpr int CellSizeInUnits = 8192;
 | 
			
		||||
    constexpr int ESM4CellSizeInUnits = 4096;
 | 
			
		||||
 | 
			
		||||
    // Size of active cell grid in cells (it is a square with the (2 * CellGridRadius + 1) cells side)
 | 
			
		||||
    constexpr int CellGridRadius = 1;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue