1 #include "scene.hpp" 2 3 #include <limits> 4 #include <chrono> 5 #include <thread> 6 7 #include <BulletCollision/CollisionDispatch/btCollisionObject.h> 8 #include <BulletCollision/CollisionShapes/btCompoundShape.h> 9 10 #include <components/debug/debuglog.hpp> 11 #include <components/loadinglistener/loadinglistener.hpp> 12 #include <components/misc/resourcehelpers.hpp> 13 #include <components/settings/settings.hpp> 14 #include <components/resource/resourcesystem.hpp> 15 #include <components/resource/scenemanager.hpp> 16 #include <components/resource/bulletshape.hpp> 17 #include <components/sceneutil/unrefqueue.hpp> 18 #include <components/sceneutil/positionattitudetransform.hpp> 19 #include <components/detournavigator/navigator.hpp> 20 #include <components/detournavigator/debug.hpp> 21 #include <components/misc/convert.hpp> 22 23 #include "../mwbase/environment.hpp" 24 #include "../mwbase/world.hpp" 25 #include "../mwbase/soundmanager.hpp" 26 #include "../mwbase/mechanicsmanager.hpp" 27 #include "../mwbase/windowmanager.hpp" 28 29 #include "../mwrender/renderingmanager.hpp" 30 #include "../mwrender/landmanager.hpp" 31 32 #include "../mwphysics/physicssystem.hpp" 33 #include "../mwphysics/actor.hpp" 34 #include "../mwphysics/object.hpp" 35 #include "../mwphysics/heightfield.hpp" 36 37 #include "player.hpp" 38 #include "localscripts.hpp" 39 #include "esmstore.hpp" 40 #include "class.hpp" 41 #include "cellvisitors.hpp" 42 #include "cellstore.hpp" 43 #include "cellpreloader.hpp" 44 45 namespace 46 { 47 using MWWorld::RotationOrder; 48 makeActorOsgQuat(const ESM::Position & position)49 osg::Quat makeActorOsgQuat(const ESM::Position& position) 50 { 51 return osg::Quat(position.rot[2], osg::Vec3(0, 0, -1)); 52 } 53 makeInversedOrderObjectOsgQuat(const ESM::Position & position)54 osg::Quat makeInversedOrderObjectOsgQuat(const ESM::Position& position) 55 { 56 const float xr = position.rot[0]; 57 const float yr = position.rot[1]; 58 const float zr = position.rot[2]; 59 60 return osg::Quat(xr, osg::Vec3(-1, 0, 0)) 61 * osg::Quat(yr, osg::Vec3(0, -1, 0)) 62 * osg::Quat(zr, osg::Vec3(0, 0, -1)); 63 } 64 makeObjectOsgQuat(const ESM::Position & position)65 osg::Quat makeObjectOsgQuat(const ESM::Position& position) 66 { 67 const float xr = position.rot[0]; 68 const float yr = position.rot[1]; 69 const float zr = position.rot[2]; 70 71 return osg::Quat(zr, osg::Vec3(0, 0, -1)) 72 * osg::Quat(yr, osg::Vec3(0, -1, 0)) 73 * osg::Quat(xr, osg::Vec3(-1, 0, 0)); 74 } 75 setNodeRotation(const MWWorld::Ptr & ptr,MWRender::RenderingManager & rendering,RotationOrder order)76 void setNodeRotation(const MWWorld::Ptr& ptr, MWRender::RenderingManager& rendering, RotationOrder order) 77 { 78 if (!ptr.getRefData().getBaseNode()) 79 return; 80 81 rendering.rotateObject(ptr, 82 ptr.getClass().isActor() 83 ? makeActorOsgQuat(ptr.getRefData().getPosition()) 84 : (order == RotationOrder::inverse 85 ? makeInversedOrderObjectOsgQuat(ptr.getRefData().getPosition()) 86 : makeObjectOsgQuat(ptr.getRefData().getPosition())) 87 ); 88 } 89 getModel(const MWWorld::Ptr & ptr,const VFS::Manager * vfs)90 std::string getModel(const MWWorld::Ptr &ptr, const VFS::Manager *vfs) 91 { 92 bool useAnim = ptr.getClass().useAnim(); 93 std::string model = ptr.getClass().getModel(ptr); 94 if (useAnim) 95 model = Misc::ResourceHelpers::correctActorModelPath(model, vfs); 96 97 const std::string &id = ptr.getCellRef().getRefId(); 98 if (id == "prisonmarker" || id == "divinemarker" || id == "templemarker" || id == "northmarker") 99 model = ""; // marker objects that have a hardcoded function in the game logic, should be hidden from the player 100 return model; 101 } 102 addObject(const MWWorld::Ptr & ptr,MWPhysics::PhysicsSystem & physics,MWRender::RenderingManager & rendering,std::set<ESM::RefNum> & pagedRefs)103 void addObject(const MWWorld::Ptr& ptr, MWPhysics::PhysicsSystem& physics, 104 MWRender::RenderingManager& rendering, std::set<ESM::RefNum>& pagedRefs) 105 { 106 if (ptr.getRefData().getBaseNode() || physics.getActor(ptr)) 107 { 108 Log(Debug::Warning) << "Warning: Tried to add " << ptr.getCellRef().getRefId() << " to the scene twice"; 109 return; 110 } 111 112 bool useAnim = ptr.getClass().useAnim(); 113 std::string model = getModel(ptr, rendering.getResourceSystem()->getVFS()); 114 115 const ESM::RefNum& refnum = ptr.getCellRef().getRefNum(); 116 if (!refnum.hasContentFile() || pagedRefs.find(refnum) == pagedRefs.end()) 117 ptr.getClass().insertObjectRendering(ptr, model, rendering); 118 else 119 ptr.getRefData().setBaseNode(new SceneUtil::PositionAttitudeTransform); // FIXME remove this when physics code is fixed not to depend on basenode 120 setNodeRotation(ptr, rendering, RotationOrder::direct); 121 122 ptr.getClass().insertObject (ptr, model, physics); 123 124 if (useAnim) 125 MWBase::Environment::get().getMechanicsManager()->add(ptr); 126 127 if (ptr.getClass().isActor()) 128 rendering.addWaterRippleEmitter(ptr); 129 130 // Restore effect particles 131 MWBase::Environment::get().getWorld()->applyLoopingParticles(ptr); 132 } 133 addObject(const MWWorld::Ptr & ptr,const MWPhysics::PhysicsSystem & physics,DetourNavigator::Navigator & navigator)134 void addObject(const MWWorld::Ptr& ptr, const MWPhysics::PhysicsSystem& physics, DetourNavigator::Navigator& navigator) 135 { 136 if (const auto object = physics.getObject(ptr)) 137 { 138 if (ptr.getClass().isDoor() && !ptr.getCellRef().getTeleport()) 139 { 140 btVector3 aabbMin; 141 btVector3 aabbMax; 142 object->getShapeInstance()->getCollisionShape()->getAabb(btTransform::getIdentity(), aabbMin, aabbMax); 143 144 const auto center = (aabbMax + aabbMin) * 0.5f; 145 146 const auto distanceFromDoor = MWBase::Environment::get().getWorld()->getMaxActivationDistance() * 0.5f; 147 const auto toPoint = aabbMax.x() - aabbMin.x() < aabbMax.y() - aabbMin.y() 148 ? btVector3(distanceFromDoor, 0, 0) 149 : btVector3(0, distanceFromDoor, 0); 150 151 const auto transform = object->getTransform(); 152 const btTransform closedDoorTransform( 153 Misc::Convert::toBullet(makeObjectOsgQuat(ptr.getCellRef().getPosition())), 154 transform.getOrigin() 155 ); 156 157 const auto start = Misc::Convert::makeOsgVec3f(closedDoorTransform(center + toPoint)); 158 const auto startPoint = physics.castRay(start, start - osg::Vec3f(0, 0, 1000), ptr, {}, 159 MWPhysics::CollisionType_World | MWPhysics::CollisionType_HeightMap | MWPhysics::CollisionType_Water); 160 const auto connectionStart = startPoint.mHit ? startPoint.mHitPos : start; 161 162 const auto end = Misc::Convert::makeOsgVec3f(closedDoorTransform(center - toPoint)); 163 const auto endPoint = physics.castRay(end, end - osg::Vec3f(0, 0, 1000), ptr, {}, 164 MWPhysics::CollisionType_World | MWPhysics::CollisionType_HeightMap | MWPhysics::CollisionType_Water); 165 const auto connectionEnd = endPoint.mHit ? endPoint.mHitPos : end; 166 167 navigator.addObject( 168 DetourNavigator::ObjectId(object), 169 DetourNavigator::DoorShapes(object->getShapeInstance(), connectionStart, connectionEnd), 170 transform 171 ); 172 } 173 else 174 { 175 navigator.addObject( 176 DetourNavigator::ObjectId(object), 177 DetourNavigator::ObjectShapes(object->getShapeInstance()), 178 object->getTransform() 179 ); 180 } 181 } 182 else if (physics.getActor(ptr)) 183 { 184 navigator.addAgent(MWBase::Environment::get().getWorld()->getPathfindingHalfExtents(ptr)); 185 } 186 } 187 188 struct InsertVisitor 189 { 190 MWWorld::CellStore& mCell; 191 Loading::Listener& mLoadingListener; 192 bool mTest; 193 194 std::vector<MWWorld::Ptr> mToInsert; 195 196 InsertVisitor (MWWorld::CellStore& cell, Loading::Listener& loadingListener, bool test); 197 198 bool operator() (const MWWorld::Ptr& ptr); 199 200 template <class AddObject> 201 void insert(AddObject&& addObject); 202 }; 203 InsertVisitor(MWWorld::CellStore & cell,Loading::Listener & loadingListener,bool test)204 InsertVisitor::InsertVisitor (MWWorld::CellStore& cell, Loading::Listener& loadingListener, bool test) 205 : mCell (cell), mLoadingListener (loadingListener), mTest(test) 206 {} 207 operator ()(const MWWorld::Ptr & ptr)208 bool InsertVisitor::operator() (const MWWorld::Ptr& ptr) 209 { 210 // do not insert directly as we can't modify the cell from within the visitation 211 // CreatureLevList::insertObjectRendering may spawn a new creature 212 mToInsert.push_back(ptr); 213 return true; 214 } 215 216 template <class AddObject> insert(AddObject && addObject)217 void InsertVisitor::insert(AddObject&& addObject) 218 { 219 for (MWWorld::Ptr& ptr : mToInsert) 220 { 221 if (!ptr.getRefData().isDeleted() && ptr.getRefData().isEnabled()) 222 { 223 try 224 { 225 addObject(ptr); 226 } 227 catch (const std::exception& e) 228 { 229 std::string error ("failed to render '" + ptr.getCellRef().getRefId() + "': "); 230 Log(Debug::Error) << error + e.what(); 231 } 232 } 233 234 if (!mTest) 235 mLoadingListener.increaseProgress (1); 236 } 237 } 238 239 struct PositionVisitor 240 { operator ()__anon9f03a3e90111::PositionVisitor241 bool operator() (const MWWorld::Ptr& ptr) 242 { 243 if (!ptr.getRefData().isDeleted() && ptr.getRefData().isEnabled()) 244 ptr.getClass().adjustPosition (ptr, false); 245 return true; 246 } 247 }; 248 getCellPositionDistanceToOrigin(const std::pair<int,int> & cellPosition)249 int getCellPositionDistanceToOrigin(const std::pair<int, int>& cellPosition) 250 { 251 return std::abs(cellPosition.first) + std::abs(cellPosition.second); 252 } 253 254 } 255 256 257 namespace MWWorld 258 { 259 removeFromPagedRefs(const Ptr & ptr)260 void Scene::removeFromPagedRefs(const Ptr &ptr) 261 { 262 const ESM::RefNum& refnum = ptr.getCellRef().getRefNum(); 263 if (refnum.hasContentFile() && mPagedRefs.erase(refnum)) 264 { 265 if (!ptr.getRefData().getBaseNode()) return; 266 ptr.getClass().insertObjectRendering(ptr, getModel(ptr, mRendering.getResourceSystem()->getVFS()), mRendering); 267 setNodeRotation(ptr, mRendering, RotationOrder::direct); 268 reloadTerrain(); 269 } 270 } 271 updateObjectPosition(const Ptr & ptr,const osg::Vec3f & pos,bool movePhysics)272 void Scene::updateObjectPosition(const Ptr &ptr, const osg::Vec3f &pos, bool movePhysics) 273 { 274 mRendering.moveObject(ptr, pos); 275 if (movePhysics) 276 { 277 mPhysics->updatePosition(ptr); 278 } 279 } 280 updateObjectRotation(const Ptr & ptr,RotationOrder order)281 void Scene::updateObjectRotation(const Ptr &ptr, RotationOrder order) 282 { 283 setNodeRotation(ptr, mRendering, order); 284 mPhysics->updateRotation(ptr); 285 } 286 updateObjectScale(const Ptr & ptr)287 void Scene::updateObjectScale(const Ptr &ptr) 288 { 289 float scale = ptr.getCellRef().getScale(); 290 osg::Vec3f scaleVec (scale, scale, scale); 291 ptr.getClass().adjustScale(ptr, scaleVec, true); 292 mRendering.scaleObject(ptr, scaleVec); 293 mPhysics->updateScale(ptr); 294 } 295 update(float duration,bool paused)296 void Scene::update (float duration, bool paused) 297 { 298 mPreloader->updateCache(mRendering.getReferenceTime()); 299 preloadCells(duration); 300 301 mRendering.update (duration, paused); 302 } 303 unloadCell(CellStoreCollection::iterator iter,bool test)304 void Scene::unloadCell (CellStoreCollection::iterator iter, bool test) 305 { 306 if (!test) 307 Log(Debug::Info) << "Unloading cell " << (*iter)->getCell()->getDescription(); 308 309 const auto navigator = MWBase::Environment::get().getWorld()->getNavigator(); 310 ListAndResetObjectsVisitor visitor; 311 312 (*iter)->forEach(visitor); 313 const auto world = MWBase::Environment::get().getWorld(); 314 for (const auto& ptr : visitor.mObjects) 315 { 316 if (const auto object = mPhysics->getObject(ptr)) 317 navigator->removeObject(DetourNavigator::ObjectId(object)); 318 else if (mPhysics->getActor(ptr)) 319 { 320 navigator->removeAgent(world->getPathfindingHalfExtents(ptr)); 321 mRendering.removeActorPath(ptr); 322 } 323 mPhysics->remove(ptr); 324 } 325 326 const auto cellX = (*iter)->getCell()->getGridX(); 327 const auto cellY = (*iter)->getCell()->getGridY(); 328 329 if ((*iter)->getCell()->isExterior()) 330 { 331 if (const auto heightField = mPhysics->getHeightField(cellX, cellY)) 332 navigator->removeObject(DetourNavigator::ObjectId(heightField)); 333 mPhysics->removeHeightField(cellX, cellY); 334 } 335 336 if ((*iter)->getCell()->hasWater()) 337 navigator->removeWater(osg::Vec2i(cellX, cellY)); 338 339 if (const auto pathgrid = world->getStore().get<ESM::Pathgrid>().search(*(*iter)->getCell())) 340 navigator->removePathgrid(*pathgrid); 341 342 const auto player = world->getPlayerPtr(); 343 navigator->update(player.getRefData().getPosition().asVec3()); 344 345 MWBase::Environment::get().getMechanicsManager()->drop (*iter); 346 347 mRendering.removeCell(*iter); 348 MWBase::Environment::get().getWindowManager()->removeCell(*iter); 349 350 MWBase::Environment::get().getWorld()->getLocalScripts().clearCell (*iter); 351 352 MWBase::Environment::get().getSoundManager()->stopSound (*iter); 353 mActiveCells.erase(*iter); 354 } 355 loadCell(CellStore * cell,Loading::Listener * loadingListener,bool respawn,bool test)356 void Scene::loadCell (CellStore *cell, Loading::Listener* loadingListener, bool respawn, bool test) 357 { 358 std::pair<CellStoreCollection::iterator, bool> result = mActiveCells.insert(cell); 359 360 if(result.second) 361 { 362 if (test) 363 Log(Debug::Info) << "Testing cell " << cell->getCell()->getDescription(); 364 else 365 Log(Debug::Info) << "Loading cell " << cell->getCell()->getDescription(); 366 367 float verts = ESM::Land::LAND_SIZE; 368 float worldsize = ESM::Land::REAL_SIZE; 369 370 const auto world = MWBase::Environment::get().getWorld(); 371 const auto navigator = world->getNavigator(); 372 373 const int cellX = cell->getCell()->getGridX(); 374 const int cellY = cell->getCell()->getGridY(); 375 376 // Load terrain physics first... 377 if (!test && cell->getCell()->isExterior()) 378 { 379 osg::ref_ptr<const ESMTerrain::LandObject> land = mRendering.getLandManager()->getLand(cellX, cellY); 380 const ESM::Land::LandData* data = land ? land->getData(ESM::Land::DATA_VHGT) : nullptr; 381 if (data) 382 { 383 mPhysics->addHeightField (data->mHeights, cellX, cellY, worldsize / (verts-1), verts, data->mMinHeight, data->mMaxHeight, land.get()); 384 } 385 else 386 { 387 static std::vector<float> defaultHeight; 388 defaultHeight.resize(verts*verts, ESM::Land::DEFAULT_HEIGHT); 389 mPhysics->addHeightField (&defaultHeight[0], cell->getCell()->getGridX(), cell->getCell()->getGridY(), worldsize / (verts-1), verts, ESM::Land::DEFAULT_HEIGHT, ESM::Land::DEFAULT_HEIGHT, land.get()); 390 } 391 392 if (const auto heightField = mPhysics->getHeightField(cellX, cellY)) 393 navigator->addObject(DetourNavigator::ObjectId(heightField), heightField, *heightField->getShape(), 394 heightField->getCollisionObject()->getWorldTransform()); 395 } 396 397 if (const auto pathgrid = world->getStore().get<ESM::Pathgrid>().search(*cell->getCell())) 398 navigator->addPathgrid(*cell->getCell(), *pathgrid); 399 400 // register local scripts 401 // do this before insertCell, to make sure we don't add scripts from levelled creature spawning twice 402 MWBase::Environment::get().getWorld()->getLocalScripts().addCell (cell); 403 404 if (respawn) 405 cell->respawn(); 406 407 // ... then references. This is important for adjustPosition to work correctly. 408 insertCell (*cell, loadingListener, test); 409 410 mRendering.addCell(cell); 411 if (!test) 412 { 413 MWBase::Environment::get().getWindowManager()->addCell(cell); 414 bool waterEnabled = cell->getCell()->hasWater() || cell->isExterior(); 415 float waterLevel = cell->getWaterLevel(); 416 mRendering.setWaterEnabled(waterEnabled); 417 if (waterEnabled) 418 { 419 mPhysics->enableWater(waterLevel); 420 mRendering.setWaterHeight(waterLevel); 421 422 if (cell->getCell()->isExterior()) 423 { 424 if (const auto heightField = mPhysics->getHeightField(cellX, cellY)) 425 navigator->addWater(osg::Vec2i(cellX, cellY), ESM::Land::REAL_SIZE, 426 cell->getWaterLevel(), heightField->getCollisionObject()->getWorldTransform()); 427 } 428 else 429 { 430 navigator->addWater(osg::Vec2i(cellX, cellY), std::numeric_limits<int>::max(), 431 cell->getWaterLevel(), btTransform::getIdentity()); 432 } 433 } 434 else 435 mPhysics->disableWater(); 436 437 const auto player = MWBase::Environment::get().getWorld()->getPlayerPtr(); 438 439 navigator->update(player.getRefData().getPosition().asVec3()); 440 441 if (!cell->isExterior() && !(cell->getCell()->mData.mFlags & ESM::Cell::QuasiEx)) 442 { 443 mRendering.configureAmbient(cell->getCell()); 444 } 445 } 446 } 447 448 mPreloader->notifyLoaded(cell); 449 } 450 clear()451 void Scene::clear() 452 { 453 CellStoreCollection::iterator active = mActiveCells.begin(); 454 while (active!=mActiveCells.end()) 455 unloadCell (active++); 456 assert(mActiveCells.empty()); 457 mCurrentCell = nullptr; 458 459 mPreloader->clear(); 460 } 461 gridCenterToBounds(const osg::Vec2i & centerCell) const462 osg::Vec4i Scene::gridCenterToBounds(const osg::Vec2i& centerCell) const 463 { 464 return osg::Vec4i(centerCell.x()-mHalfGridSize,centerCell.y()-mHalfGridSize,centerCell.x()+mHalfGridSize+1,centerCell.y()+mHalfGridSize+1); 465 } 466 getNewGridCenter(const osg::Vec3f & pos,const osg::Vec2i * currentGridCenter) const467 osg::Vec2i Scene::getNewGridCenter(const osg::Vec3f &pos, const osg::Vec2i* currentGridCenter) const 468 { 469 if (currentGridCenter) 470 { 471 float centerX, centerY; 472 MWBase::Environment::get().getWorld()->indexToPosition(currentGridCenter->x(), currentGridCenter->y(), centerX, centerY, true); 473 float distance = std::max(std::abs(centerX-pos.x()), std::abs(centerY-pos.y())); 474 const float maxDistance = Constants::CellSizeInUnits / 2 + mCellLoadingThreshold; // 1/2 cell size + threshold 475 if (distance <= maxDistance) 476 return *currentGridCenter; 477 } 478 osg::Vec2i newCenter; 479 MWBase::Environment::get().getWorld()->positionToIndex(pos.x(), pos.y(), newCenter.x(), newCenter.y()); 480 return newCenter; 481 } 482 playerMoved(const osg::Vec3f & pos)483 void Scene::playerMoved(const osg::Vec3f &pos) 484 { 485 const auto navigator = MWBase::Environment::get().getWorld()->getNavigator(); 486 const auto player = MWBase::Environment::get().getWorld()->getPlayerPtr(); 487 navigator->updatePlayerPosition(player.getRefData().getPosition().asVec3()); 488 489 if (!mCurrentCell || !mCurrentCell->isExterior()) 490 return; 491 492 osg::Vec2i newCell = getNewGridCenter(pos, &mCurrentGridCenter); 493 if (newCell != mCurrentGridCenter) 494 changeCellGrid(pos, newCell.x(), newCell.y()); 495 } 496 changeCellGrid(const osg::Vec3f & pos,int playerCellX,int playerCellY,bool changeEvent)497 void Scene::changeCellGrid (const osg::Vec3f &pos, int playerCellX, int playerCellY, bool changeEvent) 498 { 499 CellStoreCollection::iterator active = mActiveCells.begin(); 500 while (active!=mActiveCells.end()) 501 { 502 if ((*active)->getCell()->isExterior()) 503 { 504 if (std::abs (playerCellX-(*active)->getCell()->getGridX())<=mHalfGridSize && 505 std::abs (playerCellY-(*active)->getCell()->getGridY())<=mHalfGridSize) 506 { 507 // keep cells within the new grid 508 ++active; 509 continue; 510 } 511 } 512 unloadCell (active++); 513 } 514 515 mCurrentGridCenter = osg::Vec2i(playerCellX, playerCellY); 516 osg::Vec4i newGrid = gridCenterToBounds(mCurrentGridCenter); 517 mRendering.setActiveGrid(newGrid); 518 519 preloadTerrain(pos, true); 520 mPagedRefs.clear(); 521 mRendering.getPagedRefnums(newGrid, mPagedRefs); 522 523 std::size_t refsToLoad = 0; 524 std::vector<std::pair<int, int>> cellsPositionsToLoad; 525 // get the number of refs to load 526 for (int x = playerCellX - mHalfGridSize; x <= playerCellX + mHalfGridSize; ++x) 527 { 528 for (int y = playerCellY - mHalfGridSize; y <= playerCellY + mHalfGridSize; ++y) 529 { 530 CellStoreCollection::iterator iter = mActiveCells.begin(); 531 532 while (iter!=mActiveCells.end()) 533 { 534 assert ((*iter)->getCell()->isExterior()); 535 536 if (x==(*iter)->getCell()->getGridX() && 537 y==(*iter)->getCell()->getGridY()) 538 break; 539 540 ++iter; 541 } 542 543 if (iter==mActiveCells.end()) 544 { 545 refsToLoad += MWBase::Environment::get().getWorld()->getExterior(x, y)->count(); 546 cellsPositionsToLoad.emplace_back(x, y); 547 } 548 } 549 } 550 551 Loading::Listener* loadingListener = MWBase::Environment::get().getWindowManager()->getLoadingScreen(); 552 Loading::ScopedLoad load(loadingListener); 553 std::string loadingExteriorText = "#{sLoadingMessage3}"; 554 loadingListener->setLabel(loadingExteriorText); 555 loadingListener->setProgressRange(refsToLoad); 556 557 const auto getDistanceToPlayerCell = [&] (const std::pair<int, int>& cellPosition) 558 { 559 return std::abs(cellPosition.first - playerCellX) + std::abs(cellPosition.second - playerCellY); 560 }; 561 562 const auto getCellPositionPriority = [&] (const std::pair<int, int>& cellPosition) 563 { 564 return std::make_pair(getDistanceToPlayerCell(cellPosition), getCellPositionDistanceToOrigin(cellPosition)); 565 }; 566 567 std::sort(cellsPositionsToLoad.begin(), cellsPositionsToLoad.end(), 568 [&] (const std::pair<int, int>& lhs, const std::pair<int, int>& rhs) { 569 return getCellPositionPriority(lhs) < getCellPositionPriority(rhs); 570 }); 571 572 // Load cells 573 for (const auto& cellPosition : cellsPositionsToLoad) 574 { 575 const auto x = cellPosition.first; 576 const auto y = cellPosition.second; 577 578 CellStoreCollection::iterator iter = mActiveCells.begin(); 579 580 while (iter != mActiveCells.end()) 581 { 582 assert ((*iter)->getCell()->isExterior()); 583 584 if (x == (*iter)->getCell()->getGridX() && 585 y == (*iter)->getCell()->getGridY()) 586 break; 587 588 ++iter; 589 } 590 591 if (iter == mActiveCells.end()) 592 { 593 CellStore *cell = MWBase::Environment::get().getWorld()->getExterior(x, y); 594 595 loadCell (cell, loadingListener, changeEvent); 596 } 597 } 598 599 CellStore* current = MWBase::Environment::get().getWorld()->getExterior(playerCellX, playerCellY); 600 MWBase::Environment::get().getWindowManager()->changeCell(current); 601 602 if (changeEvent) 603 mCellChanged = true; 604 605 mNavigator.wait(*loadingListener, DetourNavigator::WaitConditionType::requiredTilesPresent); 606 } 607 testExteriorCells()608 void Scene::testExteriorCells() 609 { 610 // Note: temporary disable ICO to decrease memory usage 611 mRendering.getResourceSystem()->getSceneManager()->setIncrementalCompileOperation(nullptr); 612 613 mRendering.getResourceSystem()->setExpiryDelay(1.f); 614 615 const MWWorld::Store<ESM::Cell> &cells = MWBase::Environment::get().getWorld()->getStore().get<ESM::Cell>(); 616 617 Loading::Listener* loadingListener = MWBase::Environment::get().getWindowManager()->getLoadingScreen(); 618 Loading::ScopedLoad load(loadingListener); 619 loadingListener->setProgressRange(cells.getExtSize()); 620 621 MWWorld::Store<ESM::Cell>::iterator it = cells.extBegin(); 622 int i = 1; 623 for (; it != cells.extEnd(); ++it) 624 { 625 loadingListener->setLabel("Testing exterior cells ("+std::to_string(i)+"/"+std::to_string(cells.getExtSize())+")..."); 626 627 CellStoreCollection::iterator iter = mActiveCells.begin(); 628 629 CellStore *cell = MWBase::Environment::get().getWorld()->getExterior(it->mData.mX, it->mData.mY); 630 loadCell (cell, loadingListener, false, true); 631 632 iter = mActiveCells.begin(); 633 while (iter != mActiveCells.end()) 634 { 635 if (it->isExterior() && it->mData.mX == (*iter)->getCell()->getGridX() && 636 it->mData.mY == (*iter)->getCell()->getGridY()) 637 { 638 unloadCell(iter, true); 639 break; 640 } 641 642 ++iter; 643 } 644 645 mRendering.getResourceSystem()->updateCache(mRendering.getReferenceTime()); 646 mRendering.getUnrefQueue()->flush(mRendering.getWorkQueue()); 647 648 loadingListener->increaseProgress (1); 649 i++; 650 } 651 652 mRendering.getResourceSystem()->getSceneManager()->setIncrementalCompileOperation(mRendering.getIncrementalCompileOperation()); 653 mRendering.getResourceSystem()->setExpiryDelay(Settings::Manager::getFloat("cache expiry delay", "Cells")); 654 } 655 testInteriorCells()656 void Scene::testInteriorCells() 657 { 658 // Note: temporary disable ICO to decrease memory usage 659 mRendering.getResourceSystem()->getSceneManager()->setIncrementalCompileOperation(nullptr); 660 661 mRendering.getResourceSystem()->setExpiryDelay(1.f); 662 663 const MWWorld::Store<ESM::Cell> &cells = MWBase::Environment::get().getWorld()->getStore().get<ESM::Cell>(); 664 665 Loading::Listener* loadingListener = MWBase::Environment::get().getWindowManager()->getLoadingScreen(); 666 Loading::ScopedLoad load(loadingListener); 667 loadingListener->setProgressRange(cells.getIntSize()); 668 669 int i = 1; 670 MWWorld::Store<ESM::Cell>::iterator it = cells.intBegin(); 671 for (; it != cells.intEnd(); ++it) 672 { 673 loadingListener->setLabel("Testing interior cells ("+std::to_string(i)+"/"+std::to_string(cells.getIntSize())+")..."); 674 675 CellStore *cell = MWBase::Environment::get().getWorld()->getInterior(it->mName); 676 loadCell (cell, loadingListener, false, true); 677 678 CellStoreCollection::iterator iter = mActiveCells.begin(); 679 while (iter != mActiveCells.end()) 680 { 681 assert (!(*iter)->getCell()->isExterior()); 682 683 if (it->mName == (*iter)->getCell()->mName) 684 { 685 unloadCell(iter, true); 686 break; 687 } 688 689 ++iter; 690 } 691 692 mRendering.getResourceSystem()->updateCache(mRendering.getReferenceTime()); 693 mRendering.getUnrefQueue()->flush(mRendering.getWorkQueue()); 694 695 loadingListener->increaseProgress (1); 696 i++; 697 } 698 699 mRendering.getResourceSystem()->getSceneManager()->setIncrementalCompileOperation(mRendering.getIncrementalCompileOperation()); 700 mRendering.getResourceSystem()->setExpiryDelay(Settings::Manager::getFloat("cache expiry delay", "Cells")); 701 } 702 changePlayerCell(CellStore * cell,const ESM::Position & pos,bool adjustPlayerPos)703 void Scene::changePlayerCell(CellStore *cell, const ESM::Position &pos, bool adjustPlayerPos) 704 { 705 mCurrentCell = cell; 706 707 mRendering.enableTerrain(cell->isExterior()); 708 709 MWBase::World *world = MWBase::Environment::get().getWorld(); 710 MWWorld::Ptr old = world->getPlayerPtr(); 711 world->getPlayer().setCell(cell); 712 713 MWWorld::Ptr player = world->getPlayerPtr(); 714 mRendering.updatePlayerPtr(player); 715 716 if (adjustPlayerPos) { 717 world->moveObject(player, pos.pos[0], pos.pos[1], pos.pos[2]); 718 719 float x = pos.rot[0]; 720 float y = pos.rot[1]; 721 float z = pos.rot[2]; 722 world->rotateObject(player, x, y, z); 723 724 player.getClass().adjustPosition(player, true); 725 } 726 727 MWBase::Environment::get().getMechanicsManager()->updateCell(old, player); 728 MWBase::Environment::get().getWindowManager()->watchActor(player); 729 730 mPhysics->updatePtr(old, player); 731 732 world->adjustSky(); 733 734 mLastPlayerPos = player.getRefData().getPosition().asVec3(); 735 } 736 Scene(MWRender::RenderingManager & rendering,MWPhysics::PhysicsSystem * physics,DetourNavigator::Navigator & navigator)737 Scene::Scene (MWRender::RenderingManager& rendering, MWPhysics::PhysicsSystem *physics, 738 DetourNavigator::Navigator& navigator) 739 : mCurrentCell (nullptr), mCellChanged (false), mPhysics(physics), mRendering(rendering), mNavigator(navigator) 740 , mCellLoadingThreshold(1024.f) 741 , mPreloadDistance(Settings::Manager::getInt("preload distance", "Cells")) 742 , mPreloadEnabled(Settings::Manager::getBool("preload enabled", "Cells")) 743 , mPreloadExteriorGrid(Settings::Manager::getBool("preload exterior grid", "Cells")) 744 , mPreloadDoors(Settings::Manager::getBool("preload doors", "Cells")) 745 , mPreloadFastTravel(Settings::Manager::getBool("preload fast travel", "Cells")) 746 , mPredictionTime(Settings::Manager::getFloat("prediction time", "Cells")) 747 { 748 mPreloader.reset(new CellPreloader(rendering.getResourceSystem(), physics->getShapeManager(), rendering.getTerrain(), rendering.getLandManager())); 749 mPreloader->setWorkQueue(mRendering.getWorkQueue()); 750 751 mPreloader->setUnrefQueue(rendering.getUnrefQueue()); 752 mPhysics->setUnrefQueue(rendering.getUnrefQueue()); 753 754 rendering.getResourceSystem()->setExpiryDelay(Settings::Manager::getFloat("cache expiry delay", "Cells")); 755 756 mPreloader->setExpiryDelay(Settings::Manager::getFloat("preload cell expiry delay", "Cells")); 757 mPreloader->setMinCacheSize(Settings::Manager::getInt("preload cell cache min", "Cells")); 758 mPreloader->setMaxCacheSize(Settings::Manager::getInt("preload cell cache max", "Cells")); 759 mPreloader->setPreloadInstances(Settings::Manager::getBool("preload instances", "Cells")); 760 } 761 ~Scene()762 Scene::~Scene() 763 { 764 } 765 hasCellChanged() const766 bool Scene::hasCellChanged() const 767 { 768 return mCellChanged; 769 } 770 getActiveCells() const771 const Scene::CellStoreCollection& Scene::getActiveCells() const 772 { 773 return mActiveCells; 774 } 775 changeToInteriorCell(const std::string & cellName,const ESM::Position & position,bool adjustPlayerPos,bool changeEvent)776 void Scene::changeToInteriorCell (const std::string& cellName, const ESM::Position& position, bool adjustPlayerPos, bool changeEvent) 777 { 778 CellStore *cell = MWBase::Environment::get().getWorld()->getInterior(cellName); 779 bool useFading = (mCurrentCell != nullptr); 780 if (useFading) 781 MWBase::Environment::get().getWindowManager()->fadeScreenOut(0.5); 782 783 Loading::Listener* loadingListener = MWBase::Environment::get().getWindowManager()->getLoadingScreen(); 784 std::string loadingInteriorText = "#{sLoadingMessage2}"; 785 loadingListener->setLabel(loadingInteriorText); 786 Loading::ScopedLoad load(loadingListener); 787 788 if(mCurrentCell != nullptr && *mCurrentCell == *cell) 789 { 790 MWBase::World *world = MWBase::Environment::get().getWorld(); 791 world->moveObject(world->getPlayerPtr(), position.pos[0], position.pos[1], position.pos[2]); 792 793 float x = position.rot[0]; 794 float y = position.rot[1]; 795 float z = position.rot[2]; 796 world->rotateObject(world->getPlayerPtr(), x, y, z); 797 798 if (adjustPlayerPos) 799 world->getPlayerPtr().getClass().adjustPosition(world->getPlayerPtr(), true); 800 MWBase::Environment::get().getWindowManager()->fadeScreenIn(0.5); 801 return; 802 } 803 804 Log(Debug::Info) << "Changing to interior"; 805 806 // unload 807 CellStoreCollection::iterator active = mActiveCells.begin(); 808 while (active!=mActiveCells.end()) 809 unloadCell (active++); 810 811 loadingListener->setProgressRange(cell->count()); 812 813 // Load cell. 814 mPagedRefs.clear(); 815 loadCell (cell, loadingListener, changeEvent); 816 817 changePlayerCell(cell, position, adjustPlayerPos); 818 819 // adjust fog 820 mRendering.configureFog(mCurrentCell->getCell()); 821 822 // Sky system 823 MWBase::Environment::get().getWorld()->adjustSky(); 824 825 if (changeEvent) 826 mCellChanged = true; 827 828 if (useFading) 829 MWBase::Environment::get().getWindowManager()->fadeScreenIn(0.5); 830 831 MWBase::Environment::get().getWindowManager()->changeCell(mCurrentCell); 832 833 mNavigator.wait(*loadingListener, DetourNavigator::WaitConditionType::requiredTilesPresent); 834 } 835 changeToExteriorCell(const ESM::Position & position,bool adjustPlayerPos,bool changeEvent)836 void Scene::changeToExteriorCell (const ESM::Position& position, bool adjustPlayerPos, bool changeEvent) 837 { 838 int x = 0; 839 int y = 0; 840 841 MWBase::Environment::get().getWorld()->positionToIndex (position.pos[0], position.pos[1], x, y); 842 843 if (changeEvent) 844 MWBase::Environment::get().getWindowManager()->fadeScreenOut(0.5); 845 846 changeCellGrid(position.asVec3(), x, y, changeEvent); 847 848 CellStore* current = MWBase::Environment::get().getWorld()->getExterior(x, y); 849 changePlayerCell(current, position, adjustPlayerPos); 850 851 if (changeEvent) 852 MWBase::Environment::get().getWindowManager()->fadeScreenIn(0.5); 853 } 854 getCurrentCell()855 CellStore* Scene::getCurrentCell () 856 { 857 return mCurrentCell; 858 } 859 markCellAsUnchanged()860 void Scene::markCellAsUnchanged() 861 { 862 mCellChanged = false; 863 } 864 insertCell(CellStore & cell,Loading::Listener * loadingListener,bool test)865 void Scene::insertCell (CellStore &cell, Loading::Listener* loadingListener, bool test) 866 { 867 InsertVisitor insertVisitor (cell, *loadingListener, test); 868 cell.forEach (insertVisitor); 869 insertVisitor.insert([&] (const MWWorld::Ptr& ptr) { addObject(ptr, *mPhysics, mRendering, mPagedRefs); }); 870 insertVisitor.insert([&] (const MWWorld::Ptr& ptr) { addObject(ptr, *mPhysics, mNavigator); }); 871 872 // do adjustPosition (snapping actors to ground) after objects are loaded, so we don't depend on the loading order 873 PositionVisitor posVisitor; 874 cell.forEach (posVisitor); 875 } 876 addObjectToScene(const Ptr & ptr)877 void Scene::addObjectToScene (const Ptr& ptr) 878 { 879 try 880 { 881 addObject(ptr, *mPhysics, mRendering, mPagedRefs); 882 addObject(ptr, *mPhysics, mNavigator); 883 MWBase::Environment::get().getWorld()->scaleObject(ptr, ptr.getCellRef().getScale()); 884 const auto navigator = MWBase::Environment::get().getWorld()->getNavigator(); 885 const auto player = MWBase::Environment::get().getWorld()->getPlayerPtr(); 886 navigator->update(player.getRefData().getPosition().asVec3()); 887 } 888 catch (std::exception& e) 889 { 890 Log(Debug::Error) << "failed to render '" << ptr.getCellRef().getRefId() << "': " << e.what(); 891 } 892 } 893 removeObjectFromScene(const Ptr & ptr)894 void Scene::removeObjectFromScene (const Ptr& ptr) 895 { 896 MWBase::Environment::get().getMechanicsManager()->remove (ptr); 897 MWBase::Environment::get().getSoundManager()->stopSound3D (ptr); 898 const auto navigator = MWBase::Environment::get().getWorld()->getNavigator(); 899 if (const auto object = mPhysics->getObject(ptr)) 900 { 901 navigator->removeObject(DetourNavigator::ObjectId(object)); 902 const auto player = MWBase::Environment::get().getWorld()->getPlayerPtr(); 903 navigator->update(player.getRefData().getPosition().asVec3()); 904 } 905 else if (mPhysics->getActor(ptr)) 906 { 907 navigator->removeAgent(MWBase::Environment::get().getWorld()->getPathfindingHalfExtents(ptr)); 908 } 909 mPhysics->remove(ptr); 910 mRendering.removeObject (ptr); 911 if (ptr.getClass().isActor()) 912 mRendering.removeWaterRippleEmitter(ptr); 913 ptr.getRefData().setBaseNode(nullptr); 914 } 915 isCellActive(const CellStore & cell)916 bool Scene::isCellActive(const CellStore &cell) 917 { 918 CellStoreCollection::iterator active = mActiveCells.begin(); 919 while (active != mActiveCells.end()) { 920 if (**active == cell) { 921 return true; 922 } 923 ++active; 924 } 925 return false; 926 } 927 searchPtrViaActorId(int actorId)928 Ptr Scene::searchPtrViaActorId (int actorId) 929 { 930 for (CellStoreCollection::const_iterator iter (mActiveCells.begin()); 931 iter!=mActiveCells.end(); ++iter) 932 if (Ptr ptr = (*iter)->searchViaActorId (actorId)) 933 return ptr; 934 935 return Ptr(); 936 } 937 938 class PreloadMeshItem : public SceneUtil::WorkItem 939 { 940 public: PreloadMeshItem(const std::string & mesh,Resource::SceneManager * sceneManager)941 PreloadMeshItem(const std::string& mesh, Resource::SceneManager* sceneManager) 942 : mMesh(mesh), mSceneManager(sceneManager) 943 { 944 } 945 doWork()946 void doWork() override 947 { 948 try 949 { 950 mSceneManager->getTemplate(mMesh); 951 } 952 catch (std::exception&) 953 { 954 } 955 } 956 private: 957 std::string mMesh; 958 Resource::SceneManager* mSceneManager; 959 }; 960 preload(const std::string & mesh,bool useAnim)961 void Scene::preload(const std::string &mesh, bool useAnim) 962 { 963 std::string mesh_ = mesh; 964 if (useAnim) 965 mesh_ = Misc::ResourceHelpers::correctActorModelPath(mesh_, mRendering.getResourceSystem()->getVFS()); 966 967 if (!mRendering.getResourceSystem()->getSceneManager()->checkLoaded(mesh_, mRendering.getReferenceTime())) 968 mRendering.getWorkQueue()->addWorkItem(new PreloadMeshItem(mesh_, mRendering.getResourceSystem()->getSceneManager())); 969 } 970 preloadCells(float dt)971 void Scene::preloadCells(float dt) 972 { 973 if (dt<=1e-06) return; 974 std::vector<PositionCellGrid> exteriorPositions; 975 976 const MWWorld::ConstPtr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); 977 osg::Vec3f playerPos = player.getRefData().getPosition().asVec3(); 978 osg::Vec3f moved = playerPos - mLastPlayerPos; 979 osg::Vec3f predictedPos = playerPos + moved / dt * mPredictionTime; 980 981 if (mCurrentCell->isExterior()) 982 exteriorPositions.emplace_back(predictedPos, gridCenterToBounds(getNewGridCenter(predictedPos, &mCurrentGridCenter))); 983 984 mLastPlayerPos = playerPos; 985 986 if (mPreloadEnabled) 987 { 988 if (mPreloadDoors) 989 preloadTeleportDoorDestinations(playerPos, predictedPos, exteriorPositions); 990 if (mPreloadExteriorGrid) 991 preloadExteriorGrid(playerPos, predictedPos); 992 if (mPreloadFastTravel) 993 preloadFastTravelDestinations(playerPos, predictedPos, exteriorPositions); 994 } 995 996 mPreloader->setTerrainPreloadPositions(exteriorPositions); 997 } 998 preloadTeleportDoorDestinations(const osg::Vec3f & playerPos,const osg::Vec3f & predictedPos,std::vector<PositionCellGrid> & exteriorPositions)999 void Scene::preloadTeleportDoorDestinations(const osg::Vec3f& playerPos, const osg::Vec3f& predictedPos, std::vector<PositionCellGrid>& exteriorPositions) 1000 { 1001 std::vector<MWWorld::ConstPtr> teleportDoors; 1002 for (const MWWorld::CellStore* cellStore : mActiveCells) 1003 { 1004 typedef MWWorld::CellRefList<ESM::Door>::List DoorList; 1005 const DoorList &doors = cellStore->getReadOnlyDoors().mList; 1006 for (auto& door : doors) 1007 { 1008 if (!door.mRef.getTeleport()) 1009 { 1010 continue; 1011 } 1012 teleportDoors.emplace_back(&door, cellStore); 1013 } 1014 } 1015 1016 for (const MWWorld::ConstPtr& door : teleportDoors) 1017 { 1018 float sqrDistToPlayer = (playerPos - door.getRefData().getPosition().asVec3()).length2(); 1019 sqrDistToPlayer = std::min(sqrDistToPlayer, (predictedPos - door.getRefData().getPosition().asVec3()).length2()); 1020 1021 if (sqrDistToPlayer < mPreloadDistance*mPreloadDistance) 1022 { 1023 try 1024 { 1025 if (!door.getCellRef().getDestCell().empty()) 1026 preloadCell(MWBase::Environment::get().getWorld()->getInterior(door.getCellRef().getDestCell())); 1027 else 1028 { 1029 osg::Vec3f pos = door.getCellRef().getDoorDest().asVec3(); 1030 int x,y; 1031 MWBase::Environment::get().getWorld()->positionToIndex (pos.x(), pos.y(), x, y); 1032 preloadCell(MWBase::Environment::get().getWorld()->getExterior(x,y), true); 1033 exteriorPositions.emplace_back(pos, gridCenterToBounds(getNewGridCenter(pos))); 1034 } 1035 } 1036 catch (std::exception&) 1037 { 1038 // ignore error for now, would spam the log too much 1039 } 1040 } 1041 } 1042 } 1043 preloadExteriorGrid(const osg::Vec3f & playerPos,const osg::Vec3f & predictedPos)1044 void Scene::preloadExteriorGrid(const osg::Vec3f& playerPos, const osg::Vec3f& predictedPos) 1045 { 1046 if (!MWBase::Environment::get().getWorld()->isCellExterior()) 1047 return; 1048 1049 int halfGridSizePlusOne = mHalfGridSize + 1; 1050 1051 1052 int cellX,cellY; 1053 cellX = mCurrentGridCenter.x(); cellY = mCurrentGridCenter.y(); 1054 1055 float centerX, centerY; 1056 MWBase::Environment::get().getWorld()->indexToPosition(cellX, cellY, centerX, centerY, true); 1057 1058 for (int dx = -halfGridSizePlusOne; dx <= halfGridSizePlusOne; ++dx) 1059 { 1060 for (int dy = -halfGridSizePlusOne; dy <= halfGridSizePlusOne; ++dy) 1061 { 1062 if (dy != halfGridSizePlusOne && dy != -halfGridSizePlusOne && dx != halfGridSizePlusOne && dx != -halfGridSizePlusOne) 1063 continue; // only care about the outer (not yet loaded) part of the grid 1064 1065 float thisCellCenterX, thisCellCenterY; 1066 MWBase::Environment::get().getWorld()->indexToPosition(cellX+dx, cellY+dy, thisCellCenterX, thisCellCenterY, true); 1067 1068 float dist = std::max(std::abs(thisCellCenterX - playerPos.x()), std::abs(thisCellCenterY - playerPos.y())); 1069 dist = std::min(dist,std::max(std::abs(thisCellCenterX - predictedPos.x()), std::abs(thisCellCenterY - predictedPos.y()))); 1070 float loadDist = Constants::CellSizeInUnits / 2 + Constants::CellSizeInUnits - mCellLoadingThreshold + mPreloadDistance; 1071 1072 if (dist < loadDist) 1073 preloadCell(MWBase::Environment::get().getWorld()->getExterior(cellX+dx, cellY+dy)); 1074 } 1075 } 1076 } 1077 preloadCell(CellStore * cell,bool preloadSurrounding)1078 void Scene::preloadCell(CellStore *cell, bool preloadSurrounding) 1079 { 1080 if (preloadSurrounding && cell->isExterior()) 1081 { 1082 int x = cell->getCell()->getGridX(); 1083 int y = cell->getCell()->getGridY(); 1084 unsigned int numpreloaded = 0; 1085 for (int dx = -mHalfGridSize; dx <= mHalfGridSize; ++dx) 1086 { 1087 for (int dy = -mHalfGridSize; dy <= mHalfGridSize; ++dy) 1088 { 1089 mPreloader->preload(MWBase::Environment::get().getWorld()->getExterior(x+dx, y+dy), mRendering.getReferenceTime()); 1090 if (++numpreloaded >= mPreloader->getMaxCacheSize()) 1091 break; 1092 } 1093 } 1094 } 1095 else 1096 mPreloader->preload(cell, mRendering.getReferenceTime()); 1097 } 1098 preloadTerrain(const osg::Vec3f & pos,bool sync)1099 void Scene::preloadTerrain(const osg::Vec3f &pos, bool sync) 1100 { 1101 std::vector<PositionCellGrid> vec; 1102 vec.emplace_back(pos, gridCenterToBounds(getNewGridCenter(pos))); 1103 if (sync && mRendering.pagingUnlockCache()) 1104 mPreloader->abortTerrainPreloadExcept(nullptr); 1105 else 1106 mPreloader->abortTerrainPreloadExcept(&vec[0]); 1107 mPreloader->setTerrainPreloadPositions(vec); 1108 if (!sync) return; 1109 1110 Loading::Listener* loadingListener = MWBase::Environment::get().getWindowManager()->getLoadingScreen(); 1111 Loading::ScopedLoad load(loadingListener); 1112 int progress = 0, initialProgress = -1, progressRange = 0; 1113 while (!mPreloader->syncTerrainLoad(vec, progress, progressRange, mRendering.getReferenceTime())) 1114 { 1115 if (initialProgress == -1) 1116 { 1117 loadingListener->setLabel("#{sLoadingMessage4}"); 1118 initialProgress = progress; 1119 } 1120 if (progress) 1121 { 1122 loadingListener->setProgressRange(std::max(0, progressRange-initialProgress)); 1123 loadingListener->setProgress(progress-initialProgress); 1124 } 1125 else 1126 loadingListener->setProgress(0); 1127 std::this_thread::sleep_for(std::chrono::milliseconds(5)); 1128 } 1129 } 1130 reloadTerrain()1131 void Scene::reloadTerrain() 1132 { 1133 mPreloader->setTerrainPreloadPositions(std::vector<PositionCellGrid>()); 1134 } 1135 1136 struct ListFastTravelDestinationsVisitor 1137 { ListFastTravelDestinationsVisitorMWWorld::ListFastTravelDestinationsVisitor1138 ListFastTravelDestinationsVisitor(float preloadDist, const osg::Vec3f& playerPos) 1139 : mPreloadDist(preloadDist) 1140 , mPlayerPos(playerPos) 1141 { 1142 } 1143 operator ()MWWorld::ListFastTravelDestinationsVisitor1144 bool operator()(const MWWorld::Ptr& ptr) 1145 { 1146 if ((ptr.getRefData().getPosition().asVec3() - mPlayerPos).length2() > mPreloadDist * mPreloadDist) 1147 return true; 1148 1149 if (ptr.getClass().isNpc()) 1150 { 1151 const std::vector<ESM::Transport::Dest>& transport = ptr.get<ESM::NPC>()->mBase->mTransport.mList; 1152 mList.insert(mList.begin(), transport.begin(), transport.end()); 1153 } 1154 else 1155 { 1156 const std::vector<ESM::Transport::Dest>& transport = ptr.get<ESM::Creature>()->mBase->mTransport.mList; 1157 mList.insert(mList.begin(), transport.begin(), transport.end()); 1158 } 1159 return true; 1160 } 1161 float mPreloadDist; 1162 osg::Vec3f mPlayerPos; 1163 std::vector<ESM::Transport::Dest> mList; 1164 }; 1165 preloadFastTravelDestinations(const osg::Vec3f & playerPos,const osg::Vec3f &,std::vector<PositionCellGrid> & exteriorPositions)1166 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 1167 { 1168 const MWWorld::ConstPtr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); 1169 ListFastTravelDestinationsVisitor listVisitor(mPreloadDistance, player.getRefData().getPosition().asVec3()); 1170 1171 for (MWWorld::CellStore* cellStore : mActiveCells) 1172 { 1173 cellStore->forEachType<ESM::NPC>(listVisitor); 1174 cellStore->forEachType<ESM::Creature>(listVisitor); 1175 } 1176 1177 for (ESM::Transport::Dest& dest : listVisitor.mList) 1178 { 1179 if (!dest.mCellName.empty()) 1180 preloadCell(MWBase::Environment::get().getWorld()->getInterior(dest.mCellName)); 1181 else 1182 { 1183 osg::Vec3f pos = dest.mPos.asVec3(); 1184 int x,y; 1185 MWBase::Environment::get().getWorld()->positionToIndex( pos.x(), pos.y(), x, y); 1186 preloadCell(MWBase::Environment::get().getWorld()->getExterior(x,y), true); 1187 exteriorPositions.emplace_back(pos, gridCenterToBounds(getNewGridCenter(pos))); 1188 } 1189 } 1190 } 1191 } 1192