diff --git a/AUTHORS.md b/AUTHORS.md index a1f00bca10..2080f12a99 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -229,6 +229,7 @@ Programmers Yuri Krupenin zelurker Noah Gooder + Andrew Appuhamy (andrew-app) Documentation ------------- diff --git a/CHANGELOG.md b/CHANGELOG.md index 25df64c65c..4b7ef5b62a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ Bug #5596: Effects in constant spells should not be merged Bug #5621: Drained stats cannot be restored Bug #5755: Active grid object paging - disappearing textures + Bug #5766: Active grid object paging - disappearing textures Bug #5788: Texture editing parses the selected indexes wrongly Bug #5801: A multi-effect spell with the intervention effects and recall always favors Almsivi intervention Bug #5842: GetDisposition adds temporary disposition change from different actors @@ -40,6 +41,7 @@ Bug #6133: Cannot reliably sneak or steal in the sight of the NPCs siding with player Bug #6143: Capturing a screenshot makes engine to be a temporary unresponsive Bug #6165: Paralyzed player character can pickup items when the inventory is open + Bug #6172: Some creatures can't open doors Bug #6174: Spellmaking and Enchanting sliders differences from vanilla Bug #6184: Command and Calm and Demoralize and Frenzy and Rally magic effects inconsistencies with vanilla Bug #6197: Infinite Casting Loop @@ -49,6 +51,7 @@ Bug #6283: Avis Dorsey follows you after her death Bug #6289: Keyword search in dialogues expected the text to be all ASCII characters Bug #6302: Teleporting disabled actor breaks its disabled state + Bug #6307: Pathfinding in Infidelities quest from Tribunal addon is broken Feature #890: OpenMW-CS: Column filtering Feature #2554: Modifying an object triggers the instances table to scroll to the corresponding record Feature #2780: A way to see current OpenMW version in the console @@ -60,6 +63,7 @@ Feature #5198: Implement "Magic effect expired" event Feature #5454: Clear active spells from actor when he disappears from scene Feature #5489: MCP: Telekinesis fix for activators + Feature #5737: Handle instance move from one cell to another Feature #5996: Support Lua scripts in OpenMW Feature #6017: Separate persistent and temporary cell references when saving Feature #6032: Reverse-z depth buffer diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 22fe42c5ed..8b33095fde 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -653,7 +653,8 @@ namespace MWBase virtual bool hasCollisionWithDoor(const MWWorld::ConstPtr& door, const osg::Vec3f& position, const osg::Vec3f& destination) const = 0; - virtual bool isAreaOccupiedByOtherActor(const osg::Vec3f& position, const float radius, const MWWorld::ConstPtr& ignore) const = 0; + virtual bool isAreaOccupiedByOtherActor(const osg::Vec3f& position, const float radius, + const MWWorld::ConstPtr& ignore, std::vector* occupyingActors = nullptr) const = 0; virtual void reportStats(unsigned int frameNumber, osg::Stats& stats) const = 0; diff --git a/apps/openmw/mwclass/activator.cpp b/apps/openmw/mwclass/activator.cpp index e4ba7af4f0..6285bdbf7e 100644 --- a/apps/openmw/mwclass/activator.cpp +++ b/apps/openmw/mwclass/activator.cpp @@ -38,15 +38,15 @@ namespace MWClass } } - void Activator::insertObject(const MWWorld::Ptr& ptr, const std::string& model, const osg::Quat& rotation, MWPhysics::PhysicsSystem& physics, bool skipAnimated) const + void Activator::insertObject(const MWWorld::Ptr& ptr, const std::string& model, const osg::Quat& rotation, MWPhysics::PhysicsSystem& physics) const { - insertObjectPhysics(ptr, model, rotation, physics, skipAnimated); + insertObjectPhysics(ptr, model, rotation, physics); } - void Activator::insertObjectPhysics(const MWWorld::Ptr& ptr, const std::string& model, const osg::Quat& rotation, MWPhysics::PhysicsSystem& physics, bool skipAnimated) const + void Activator::insertObjectPhysics(const MWWorld::Ptr& ptr, const std::string& model, const osg::Quat& rotation, MWPhysics::PhysicsSystem& physics) const { if(!model.empty()) - physics.addObject(ptr, model, rotation, MWPhysics::CollisionType_World, skipAnimated); + physics.addObject(ptr, model, rotation, MWPhysics::CollisionType_World); } std::string Activator::getModel(const MWWorld::ConstPtr &ptr) const diff --git a/apps/openmw/mwclass/activator.hpp b/apps/openmw/mwclass/activator.hpp index 32bf1564c9..48a679e0b7 100644 --- a/apps/openmw/mwclass/activator.hpp +++ b/apps/openmw/mwclass/activator.hpp @@ -17,9 +17,9 @@ namespace MWClass void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const override; ///< Add reference into a cell for rendering - void insertObject(const MWWorld::Ptr& ptr, const std::string& model, const osg::Quat& rotation, MWPhysics::PhysicsSystem& physics, bool skipAnimated = false) const override; + void insertObject(const MWWorld::Ptr& ptr, const std::string& model, const osg::Quat& rotation, MWPhysics::PhysicsSystem& physics) const override; - void insertObjectPhysics(const MWWorld::Ptr& ptr, const std::string& model, const osg::Quat& rotation, MWPhysics::PhysicsSystem& physics, bool skipAnimated) const override; + void insertObjectPhysics(const MWWorld::Ptr& ptr, const std::string& model, const osg::Quat& rotation, MWPhysics::PhysicsSystem& physics) const override; std::string getName (const MWWorld::ConstPtr& ptr) const override; ///< \return name or ID; can return an empty string. diff --git a/apps/openmw/mwclass/actor.cpp b/apps/openmw/mwclass/actor.cpp index b142fd2643..ad43bd6e5f 100644 --- a/apps/openmw/mwclass/actor.cpp +++ b/apps/openmw/mwclass/actor.cpp @@ -22,7 +22,7 @@ namespace MWClass MWBase::Environment::get().getWorld()->adjustPosition(ptr, force); } - void Actor::insertObject(const MWWorld::Ptr& ptr, const std::string& model, const osg::Quat& rotation, MWPhysics::PhysicsSystem& physics, bool skipAnimated) const + void Actor::insertObject(const MWWorld::Ptr& ptr, const std::string& model, const osg::Quat& rotation, MWPhysics::PhysicsSystem& physics) const { if (!model.empty()) { diff --git a/apps/openmw/mwclass/actor.hpp b/apps/openmw/mwclass/actor.hpp index 6a5d4c5150..886ffe4771 100644 --- a/apps/openmw/mwclass/actor.hpp +++ b/apps/openmw/mwclass/actor.hpp @@ -24,7 +24,7 @@ namespace MWClass ///< Adjust position to stand on ground. Must be called post model load /// @param force do this even if the ptr is flying - void insertObject(const MWWorld::Ptr& ptr, const std::string& model, const osg::Quat& rotation, MWPhysics::PhysicsSystem& physics, bool skipAnimated = false) const override; + void insertObject(const MWWorld::Ptr& ptr, const std::string& model, const osg::Quat& rotation, MWPhysics::PhysicsSystem& physics) const override; bool useAnim() const override; diff --git a/apps/openmw/mwclass/container.cpp b/apps/openmw/mwclass/container.cpp index 051fbafeb5..06980f55da 100644 --- a/apps/openmw/mwclass/container.cpp +++ b/apps/openmw/mwclass/container.cpp @@ -106,15 +106,15 @@ namespace MWClass } } - void Container::insertObject(const MWWorld::Ptr& ptr, const std::string& model, const osg::Quat& rotation, MWPhysics::PhysicsSystem& physics, bool skipAnimated) const + void Container::insertObject(const MWWorld::Ptr& ptr, const std::string& model, const osg::Quat& rotation, MWPhysics::PhysicsSystem& physics) const { - insertObjectPhysics(ptr, model, rotation, physics, skipAnimated); + insertObjectPhysics(ptr, model, rotation, physics); } - void Container::insertObjectPhysics(const MWWorld::Ptr& ptr, const std::string& model, const osg::Quat& rotation, MWPhysics::PhysicsSystem& physics, bool skipAnimated) const + void Container::insertObjectPhysics(const MWWorld::Ptr& ptr, const std::string& model, const osg::Quat& rotation, MWPhysics::PhysicsSystem& physics) const { if(!model.empty()) - physics.addObject(ptr, model, rotation, MWPhysics::CollisionType_World, skipAnimated); + physics.addObject(ptr, model, rotation, MWPhysics::CollisionType_World); } std::string Container::getModel(const MWWorld::ConstPtr &ptr) const diff --git a/apps/openmw/mwclass/container.hpp b/apps/openmw/mwclass/container.hpp index 9407dab243..0b290a73e1 100644 --- a/apps/openmw/mwclass/container.hpp +++ b/apps/openmw/mwclass/container.hpp @@ -42,8 +42,8 @@ namespace MWClass void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const override; ///< Add reference into a cell for rendering - void insertObject(const MWWorld::Ptr& ptr, const std::string& model, const osg::Quat& rotation, MWPhysics::PhysicsSystem& physics, bool skipAnimated = false) const override; - void insertObjectPhysics(const MWWorld::Ptr& ptr, const std::string& model, const osg::Quat& rotation, MWPhysics::PhysicsSystem& physics, bool skipAnimated = false) const override; + void insertObject(const MWWorld::Ptr& ptr, const std::string& model, const osg::Quat& rotation, MWPhysics::PhysicsSystem& physics) const override; + void insertObjectPhysics(const MWWorld::Ptr& ptr, const std::string& model, const osg::Quat& rotation, MWPhysics::PhysicsSystem& physics) const override; std::string getName (const MWWorld::ConstPtr& ptr) const override; ///< \return name or ID; can return an empty string. diff --git a/apps/openmw/mwclass/door.cpp b/apps/openmw/mwclass/door.cpp index 70c4a27582..b5fe705ca6 100644 --- a/apps/openmw/mwclass/door.cpp +++ b/apps/openmw/mwclass/door.cpp @@ -55,9 +55,9 @@ namespace MWClass } } - void Door::insertObject(const MWWorld::Ptr& ptr, const std::string& model, const osg::Quat& rotation, MWPhysics::PhysicsSystem& physics, bool skipAnimated) const + void Door::insertObject(const MWWorld::Ptr& ptr, const std::string& model, const osg::Quat& rotation, MWPhysics::PhysicsSystem& physics) const { - insertObjectPhysics(ptr, model, rotation, physics, skipAnimated); + insertObjectPhysics(ptr, model, rotation, physics); // Resume the door's opening/closing animation if it wasn't finished if (ptr.getRefData().getCustomData()) @@ -70,10 +70,10 @@ namespace MWClass } } - void Door::insertObjectPhysics(const MWWorld::Ptr& ptr, const std::string& model, const osg::Quat& rotation, MWPhysics::PhysicsSystem& physics, bool skipAnimated) const + void Door::insertObjectPhysics(const MWWorld::Ptr& ptr, const std::string& model, const osg::Quat& rotation, MWPhysics::PhysicsSystem& physics) const { if(!model.empty()) - physics.addObject(ptr, model, rotation, MWPhysics::CollisionType_Door, skipAnimated); + physics.addObject(ptr, model, rotation, MWPhysics::CollisionType_Door); } bool Door::isDoor() const diff --git a/apps/openmw/mwclass/door.hpp b/apps/openmw/mwclass/door.hpp index 1fd59f8f52..f9288a88ce 100644 --- a/apps/openmw/mwclass/door.hpp +++ b/apps/openmw/mwclass/door.hpp @@ -18,8 +18,8 @@ namespace MWClass void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const override; ///< Add reference into a cell for rendering - void insertObject(const MWWorld::Ptr& ptr, const std::string& model, const osg::Quat& rotation, MWPhysics::PhysicsSystem& physics, bool skipAnimated = false) const override; - void insertObjectPhysics(const MWWorld::Ptr& ptr, const std::string& model, const osg::Quat& rotation, MWPhysics::PhysicsSystem& physics, bool skipAnimated = false) const override; + void insertObject(const MWWorld::Ptr& ptr, const std::string& model, const osg::Quat& rotation, MWPhysics::PhysicsSystem& physics) const override; + void insertObjectPhysics(const MWWorld::Ptr& ptr, const std::string& model, const osg::Quat& rotation, MWPhysics::PhysicsSystem& physics) const override; bool isDoor() const override; diff --git a/apps/openmw/mwclass/light.cpp b/apps/openmw/mwclass/light.cpp index 0281861980..69cc1a09bf 100644 --- a/apps/openmw/mwclass/light.cpp +++ b/apps/openmw/mwclass/light.cpp @@ -33,13 +33,13 @@ namespace MWClass renderingInterface.getObjects().insertModel(ptr, model, true, !(ref->mBase->mData.mFlags & ESM::Light::OffDefault)); } - void Light::insertObject(const MWWorld::Ptr& ptr, const std::string& model, const osg::Quat& rotation, MWPhysics::PhysicsSystem& physics, bool skipAnimated) const + void Light::insertObject(const MWWorld::Ptr& ptr, const std::string& model, const osg::Quat& rotation, MWPhysics::PhysicsSystem& physics) const { MWWorld::LiveCellRef *ref = ptr.get(); assert (ref->mBase != nullptr); - insertObjectPhysics(ptr, model, rotation, physics, skipAnimated); + insertObjectPhysics(ptr, model, rotation, physics); if (!ref->mBase->mSound.empty() && !(ref->mBase->mData.mFlags & ESM::Light::OffDefault)) MWBase::Environment::get().getSoundManager()->playSound3D(ptr, ref->mBase->mSound, 1.0, 1.0, @@ -47,11 +47,11 @@ namespace MWClass MWSound::PlayMode::Loop); } - void Light::insertObjectPhysics(const MWWorld::Ptr& ptr, const std::string& model, const osg::Quat& rotation, MWPhysics::PhysicsSystem& physics, bool skipAnimated) const + void Light::insertObjectPhysics(const MWWorld::Ptr& ptr, const std::string& model, const osg::Quat& rotation, MWPhysics::PhysicsSystem& physics) const { // TODO: add option somewhere to enable collision for placeable objects if (!model.empty() && (ptr.get()->mBase->mData.mFlags & ESM::Light::Carry) == 0) - physics.addObject(ptr, model, rotation, MWPhysics::CollisionType_World, skipAnimated); + physics.addObject(ptr, model, rotation, MWPhysics::CollisionType_World); } bool Light::useAnim() const diff --git a/apps/openmw/mwclass/light.hpp b/apps/openmw/mwclass/light.hpp index 206abede43..e8aa4e5878 100644 --- a/apps/openmw/mwclass/light.hpp +++ b/apps/openmw/mwclass/light.hpp @@ -14,8 +14,8 @@ namespace MWClass void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const override; ///< Add reference into a cell for rendering - void insertObject(const MWWorld::Ptr& ptr, const std::string& model, const osg::Quat& rotation, MWPhysics::PhysicsSystem& physics, bool skipAnimated = false) const override; - void insertObjectPhysics(const MWWorld::Ptr& ptr, const std::string& model, const osg::Quat& rotation, MWPhysics::PhysicsSystem& physics, bool skipAnimated = false) const override; + void insertObject(const MWWorld::Ptr& ptr, const std::string& model, const osg::Quat& rotation, MWPhysics::PhysicsSystem& physics) const override; + void insertObjectPhysics(const MWWorld::Ptr& ptr, const std::string& model, const osg::Quat& rotation, MWPhysics::PhysicsSystem& physics) const override; bool useAnim() const override; diff --git a/apps/openmw/mwclass/static.cpp b/apps/openmw/mwclass/static.cpp index 5cbe219580..0805ca3dd1 100644 --- a/apps/openmw/mwclass/static.cpp +++ b/apps/openmw/mwclass/static.cpp @@ -23,15 +23,15 @@ namespace MWClass } } - void Static::insertObject(const MWWorld::Ptr& ptr, const std::string& model, const osg::Quat& rotation, MWPhysics::PhysicsSystem& physics, bool skipAnimated) const + void Static::insertObject(const MWWorld::Ptr& ptr, const std::string& model, const osg::Quat& rotation, MWPhysics::PhysicsSystem& physics) const { - insertObjectPhysics(ptr, model, rotation, physics, skipAnimated); + insertObjectPhysics(ptr, model, rotation, physics); } - void Static::insertObjectPhysics(const MWWorld::Ptr& ptr, const std::string& model, const osg::Quat& rotation, MWPhysics::PhysicsSystem& physics, bool skipAnimated) const + void Static::insertObjectPhysics(const MWWorld::Ptr& ptr, const std::string& model, const osg::Quat& rotation, MWPhysics::PhysicsSystem& physics) const { if(!model.empty()) - physics.addObject(ptr, model, rotation, MWPhysics::CollisionType_World, skipAnimated); + physics.addObject(ptr, model, rotation, MWPhysics::CollisionType_World); } std::string Static::getModel(const MWWorld::ConstPtr &ptr) const diff --git a/apps/openmw/mwclass/static.hpp b/apps/openmw/mwclass/static.hpp index 7c326987a8..c747eebf2f 100644 --- a/apps/openmw/mwclass/static.hpp +++ b/apps/openmw/mwclass/static.hpp @@ -14,8 +14,8 @@ namespace MWClass void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const override; ///< Add reference into a cell for rendering - void insertObject(const MWWorld::Ptr& ptr, const std::string& model, const osg::Quat& rotation, MWPhysics::PhysicsSystem& physics, bool skipAnimated = false) const override; - void insertObjectPhysics(const MWWorld::Ptr& ptr, const std::string& model, const osg::Quat& rotation, MWPhysics::PhysicsSystem& physics, bool skipAnimated = false) const override; + void insertObject(const MWWorld::Ptr& ptr, const std::string& model, const osg::Quat& rotation, MWPhysics::PhysicsSystem& physics) const override; + void insertObjectPhysics(const MWWorld::Ptr& ptr, const std::string& model, const osg::Quat& rotation, MWPhysics::PhysicsSystem& physics) const override; std::string getName (const MWWorld::ConstPtr& ptr) const override; ///< \return name or ID; can return an empty string. diff --git a/apps/openmw/mwinput/controllermanager.cpp b/apps/openmw/mwinput/controllermanager.cpp index 7a91f8643b..fa10ce03cd 100644 --- a/apps/openmw/mwinput/controllermanager.cpp +++ b/apps/openmw/mwinput/controllermanager.cpp @@ -99,8 +99,8 @@ namespace MWInput // We keep track of our own mouse position, so that moving the mouse while in // game mode does not move the position of the GUI cursor float uiScale = MWBase::Environment::get().getWindowManager()->getScalingFactor(); - float xMove = xAxis * dt * 1500.0f / uiScale; - float yMove = yAxis * dt * 1500.0f / uiScale; + float xMove = xAxis * dt * 1500.0f / uiScale * mGamepadCursorSpeed; + float yMove = yAxis * dt * 1500.0f / uiScale * mGamepadCursorSpeed; float mouseWheelMove = -zAxis * dt * 1500.0f; if (xMove != 0 || yMove != 0 || mouseWheelMove != 0) diff --git a/apps/openmw/mwlua/actions.cpp b/apps/openmw/mwlua/actions.cpp index 1f75760f7c..aad70d1a57 100644 --- a/apps/openmw/mwlua/actions.cpp +++ b/apps/openmw/mwlua/actions.cpp @@ -51,8 +51,8 @@ namespace MWLua std::array usedSlots; std::fill(usedSlots.begin(), usedSlots.end(), false); - constexpr int anySlot = -1; - auto tryEquipToSlot = [&actor, &store, &usedSlots, &worldView, anySlot](int slot, const Item& item) -> bool + static constexpr int anySlot = -1; + auto tryEquipToSlot = [&actor, &store, &usedSlots, &worldView](int slot, const Item& item) -> bool { auto old_it = slot != anySlot ? store.getSlot(slot) : store.end(); MWWorld::Ptr itemPtr; diff --git a/apps/openmw/mwlua/nearbybindings.cpp b/apps/openmw/mwlua/nearbybindings.cpp index 8eae46a9b7..011d0ae9f3 100644 --- a/apps/openmw/mwlua/nearbybindings.cpp +++ b/apps/openmw/mwlua/nearbybindings.cpp @@ -47,8 +47,6 @@ namespace MWLua return LObject(getId(r.mHitObject), worldView->getObjectRegistry()); }); - constexpr int defaultCollisionType = MWPhysics::CollisionType_World | MWPhysics::CollisionType_HeightMap | - MWPhysics::CollisionType_Actor | MWPhysics::CollisionType_Door; api["COLLISION_TYPE"] = LuaUtil::makeReadOnly(context.mLua->sol().create_table_with( "World", MWPhysics::CollisionType_World, "Door", MWPhysics::CollisionType_Door, @@ -56,12 +54,12 @@ namespace MWLua "HeightMap", MWPhysics::CollisionType_HeightMap, "Projectile", MWPhysics::CollisionType_Projectile, "Water", MWPhysics::CollisionType_Water, - "Default", defaultCollisionType)); + "Default", MWPhysics::CollisionType_Default)); - api["castRay"] = [defaultCollisionType](const osg::Vec3f& from, const osg::Vec3f& to, sol::optional options) + api["castRay"] = [](const osg::Vec3f& from, const osg::Vec3f& to, sol::optional options) { MWWorld::Ptr ignore; - int collisionType = defaultCollisionType; + int collisionType = MWPhysics::CollisionType_Default; float radius = 0; if (options) { @@ -80,7 +78,7 @@ namespace MWLua } }; // TODO: async raycasting - /*api["asyncCastRay"] = [luaManager = context.mLuaManager, defaultCollisionType]( + /*api["asyncCastRay"] = [luaManager = context.mLuaManager]( const Callback& luaCallback, const osg::Vec3f& from, const osg::Vec3f& to, sol::optional options) { std::function callback = diff --git a/apps/openmw/mwmechanics/actor.cpp b/apps/openmw/mwmechanics/actor.cpp index a5c55633ac..5801ea751d 100644 --- a/apps/openmw/mwmechanics/actor.cpp +++ b/apps/openmw/mwmechanics/actor.cpp @@ -5,6 +5,7 @@ namespace MWMechanics { Actor::Actor(const MWWorld::Ptr &ptr, MWRender::Animation *animation) + : mPositionAdjusted(false) { mCharacterController.reset(new CharacterController(ptr, animation)); } @@ -58,4 +59,14 @@ namespace MWMechanics { mIsTurningToPlayer = turning; } + + void Actor::setPositionAdjusted(bool adjusted) + { + mPositionAdjusted = adjusted; + } + + bool Actor::getPositionAdjusted() const + { + return mPositionAdjusted; + } } diff --git a/apps/openmw/mwmechanics/actor.hpp b/apps/openmw/mwmechanics/actor.hpp index be4f425378..c25fc1cb73 100644 --- a/apps/openmw/mwmechanics/actor.hpp +++ b/apps/openmw/mwmechanics/actor.hpp @@ -48,6 +48,9 @@ namespace MWMechanics return mEngageCombat.update(duration); } + void setPositionAdjusted(bool adjusted); + bool getPositionAdjusted() const; + private: std::unique_ptr mCharacterController; int mGreetingTimer{0}; @@ -55,6 +58,7 @@ namespace MWMechanics GreetingState mGreetingState{Greet_None}; bool mIsTurningToPlayer{false}; Misc::DeviatingPeriodicTimer mEngageCombat{1.0f, 0.25f, Misc::Rng::deviate(0, 0.25f)}; + bool mPositionAdjusted; }; } diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index d71a8f53ee..bde18aa584 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -1608,7 +1608,14 @@ namespace MWMechanics continue; } else if (!isPlayer) + { iter->first.getRefData().getBaseNode()->setNodeMask(MWRender::Mask_Actor); + if (!iter->second->getPositionAdjusted()) + { + iter->first.getClass().adjustPosition(iter->first, false); + iter->second->setPositionAdjusted(true); + } + } const bool isDead = iter->first.getClass().getCreatureStats(iter->first).isDead(); if (!isDead && (!godmode || !isPlayer) && iter->first.getClass().getCreatureStats(iter->first).isParalyzed()) diff --git a/apps/openmw/mwmechanics/aipackage.cpp b/apps/openmw/mwmechanics/aipackage.cpp index 46e4729a8a..b1f4355a39 100644 --- a/apps/openmw/mwmechanics/aipackage.cpp +++ b/apps/openmw/mwmechanics/aipackage.cpp @@ -34,6 +34,11 @@ namespace const float actorTolerance = 2 * speed * duration + 1.2 * std::max(halfExtents.x(), halfExtents.y()); return std::max(MWMechanics::MIN_TOLERANCE, actorTolerance); } + + bool canOpenDoors(const MWWorld::Ptr& ptr) + { + return ptr.getClass().isBipedal(ptr) || ptr.getClass().hasInventoryStore(ptr); + } } MWMechanics::AiPackage::AiPackage(AiPackageTypeId typeId, const Options& options) : @@ -118,7 +123,7 @@ bool MWMechanics::AiPackage::pathTo(const MWWorld::Ptr& actor, const osg::Vec3f& if (!isDestReached && timerStatus == Misc::TimerStatus::Elapsed) { - if (actor.getClass().isBipedal(actor)) + if (canOpenDoors(actor)) openDoors(actor); const bool wasShortcutting = mIsShortcutting; @@ -232,7 +237,7 @@ void MWMechanics::AiPackage::evadeObstacles(const MWWorld::Ptr& actor) static float distance = MWBase::Environment::get().getWorld()->getMaxActivationDistance(); const MWWorld::Ptr door = getNearbyDoor(actor, distance); - if (!door.isEmpty() && actor.getClass().isBipedal(actor)) + if (!door.isEmpty() && canOpenDoors(actor)) { openDoors(actor); } @@ -443,9 +448,13 @@ DetourNavigator::Flags MWMechanics::AiPackage::getNavigatorFlags(const MWWorld:: result |= DetourNavigator::Flag_swim; if (actorClass.canWalk(actor) && actor.getClass().getWalkSpeed(actor) > 0) + { result |= DetourNavigator::Flag_walk; + if (getTypeId() == AiPackageTypeId::Travel) + result |= DetourNavigator::Flag_usePathgrid; + } - if (actorClass.isBipedal(actor) && getTypeId() != AiPackageTypeId::Wander) + if (canOpenDoors(actor) && getTypeId() != AiPackageTypeId::Wander) result |= DetourNavigator::Flag_openDoor; return result; @@ -457,20 +466,31 @@ DetourNavigator::AreaCosts MWMechanics::AiPackage::getAreaCosts(const MWWorld::P const DetourNavigator::Flags flags = getNavigatorFlags(actor); const MWWorld::Class& actorClass = actor.getClass(); - if (flags & DetourNavigator::Flag_swim) - costs.mWater = divOrMax(costs.mWater, actorClass.getSwimSpeed(actor)); + const float swimSpeed = (flags & DetourNavigator::Flag_swim) == 0 + ? 0.0f + : actorClass.getSwimSpeed(actor); - if (flags & DetourNavigator::Flag_walk) + const float walkSpeed = [&] { - float walkCost; + if ((flags & DetourNavigator::Flag_walk) == 0) + return 0.0f; if (getTypeId() == AiPackageTypeId::Wander) - walkCost = divOrMax(1.0, actorClass.getWalkSpeed(actor)); - else - walkCost = divOrMax(1.0, actorClass.getRunSpeed(actor)); - costs.mDoor = costs.mDoor * walkCost; - costs.mPathgrid = costs.mPathgrid * walkCost; - costs.mGround = costs.mGround * walkCost; - } + return actorClass.getWalkSpeed(actor); + return actorClass.getRunSpeed(actor); + } (); + + const float maxSpeed = std::max(swimSpeed, walkSpeed); + + if (maxSpeed == 0) + return costs; + + const float swimFactor = swimSpeed / maxSpeed; + const float walkFactor = walkSpeed / maxSpeed; + + costs.mWater = divOrMax(costs.mWater, swimFactor); + costs.mDoor = divOrMax(costs.mDoor, walkFactor); + costs.mPathgrid = divOrMax(costs.mPathgrid, walkFactor); + costs.mGround = divOrMax(costs.mGround, walkFactor); return costs; } diff --git a/apps/openmw/mwmechanics/aitravel.cpp b/apps/openmw/mwmechanics/aitravel.cpp index 91fe96a2aa..2594adcb34 100644 --- a/apps/openmw/mwmechanics/aitravel.cpp +++ b/apps/openmw/mwmechanics/aitravel.cpp @@ -1,5 +1,7 @@ #include "aitravel.hpp" +#include + #include #include "../mwbase/environment.hpp" @@ -23,6 +25,11 @@ bool isWithinMaxRange(const osg::Vec3f& pos1, const osg::Vec3f& pos2) return (pos1 - pos2).length2() <= 7168*7168; } + float getActorRadius(const MWWorld::ConstPtr& actor) + { + const osg::Vec3f halfExtents = MWBase::Environment::get().getWorld()->getPathfindingHalfExtents(actor); + return std::max(halfExtents.x(), std::max(halfExtents.y(), halfExtents.z())); + } } namespace MWMechanics @@ -70,16 +77,24 @@ namespace MWMechanics // Unfortunately, with vanilla assets destination is sometimes blocked by other actor. // If we got close to target, check for actors nearby. If they are, finish AI package. - int destinationTolerance = 64; - if (distance(actorPos, targetPos) <= destinationTolerance) + if (mDestinationCheck.update(duration) == Misc::TimerStatus::Elapsed) { - std::vector targetActors; - std::pair result = MWBase::Environment::get().getWorld()->getHitContact(actor, destinationTolerance, targetActors); - - if (!result.first.isEmpty()) + std::vector occupyingActors; + if (isAreaOccupiedByOtherActor(actor, targetPos, &occupyingActors)) { - actor.getClass().getMovementSettings(actor).mPosition[1] = 0; - return true; + const float actorRadius = getActorRadius(actor); + const float distanceToTarget = distance(actorPos, targetPos); + for (const MWWorld::Ptr& other : occupyingActors) + { + const float otherRadius = getActorRadius(other); + const auto [minRadius, maxRadius] = std::minmax(actorRadius, otherRadius); + constexpr float toleranceFactor = 1.25; + if (minRadius * toleranceFactor + maxRadius > distanceToTarget) + { + actor.getClass().getMovementSettings(actor).mPosition[1] = 0; + return true; + } + } } } diff --git a/apps/openmw/mwmechanics/aitravel.hpp b/apps/openmw/mwmechanics/aitravel.hpp index 2ea2a8f717..ee4ff9ad4d 100644 --- a/apps/openmw/mwmechanics/aitravel.hpp +++ b/apps/openmw/mwmechanics/aitravel.hpp @@ -52,6 +52,8 @@ namespace MWMechanics const float mZ; const bool mHidden; + + AiReactionTimer mDestinationCheck; }; struct AiInternalTravel final : public AiTravel diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 624abe10f9..9c84555404 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -85,14 +85,6 @@ namespace MWMechanics return MWBase::Environment::get().getWorld()->castRay(position, visibleDestination, mask, actor); } - bool isAreaOccupiedByOtherActor(const MWWorld::ConstPtr &actor, const osg::Vec3f& destination) - { - const auto world = MWBase::Environment::get().getWorld(); - const osg::Vec3f halfExtents = world->getPathfindingHalfExtents(actor); - const auto maxHalfExtent = std::max(halfExtents.x(), std::max(halfExtents.y(), halfExtents.z())); - return world->isAreaOccupiedByOtherActor(destination, 2 * maxHalfExtent, actor); - } - void stopMovement(const MWWorld::Ptr& actor) { actor.getClass().getMovementSettings(actor).mPosition[0] = 0; diff --git a/apps/openmw/mwmechanics/obstacle.cpp b/apps/openmw/mwmechanics/obstacle.cpp index 88325ee7c7..344cc82058 100644 --- a/apps/openmw/mwmechanics/obstacle.cpp +++ b/apps/openmw/mwmechanics/obstacle.cpp @@ -4,6 +4,8 @@ #include "../mwworld/class.hpp" #include "../mwworld/cellstore.hpp" +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" #include "movement.hpp" @@ -72,6 +74,15 @@ namespace MWMechanics return MWWorld::Ptr(); // none found } + bool isAreaOccupiedByOtherActor(const MWWorld::ConstPtr& actor, const osg::Vec3f& destination, + std::vector* occupyingActors) + { + const auto world = MWBase::Environment::get().getWorld(); + const osg::Vec3f halfExtents = world->getPathfindingHalfExtents(actor); + const auto maxHalfExtent = std::max(halfExtents.x(), std::max(halfExtents.y(), halfExtents.z())); + return world->isAreaOccupiedByOtherActor(destination, 2 * maxHalfExtent, actor, occupyingActors); + } + ObstacleCheck::ObstacleCheck() : mWalkState(WalkState::Initial) , mStateDuration(0) diff --git a/apps/openmw/mwmechanics/obstacle.hpp b/apps/openmw/mwmechanics/obstacle.hpp index b574bab67f..24bd5ed1c1 100644 --- a/apps/openmw/mwmechanics/obstacle.hpp +++ b/apps/openmw/mwmechanics/obstacle.hpp @@ -3,9 +3,12 @@ #include +#include + namespace MWWorld { class Ptr; + class ConstPtr; } namespace MWMechanics @@ -21,6 +24,9 @@ namespace MWMechanics /** \return Pointer to the door, or empty pointer if none exists **/ const MWWorld::Ptr getNearbyDoor(const MWWorld::Ptr& actor, float minDist); + bool isAreaOccupiedByOtherActor(const MWWorld::ConstPtr& actor, const osg::Vec3f& destination, + std::vector* occupyingActors = nullptr); + class ObstacleCheck { public: diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp index 7e63190edb..4b06993a49 100644 --- a/apps/openmw/mwmechanics/pathfinding.cpp +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -397,7 +397,7 @@ namespace MWMechanics mPath.clear(); } - if (status != DetourNavigator::Status::NavMeshNotFound && mPath.empty()) + if (status != DetourNavigator::Status::NavMeshNotFound && mPath.empty() && (flags & DetourNavigator::Flag_usePathgrid) == 0) { status = buildPathByNavigatorImpl(actor, startPoint, endPoint, halfExtents, flags | DetourNavigator::Flag_usePathgrid, areaCosts, endTolerance, pathType, std::back_inserter(mPath)); diff --git a/apps/openmw/mwphysics/hasspherecollisioncallback.hpp b/apps/openmw/mwphysics/hasspherecollisioncallback.hpp index 275325cf67..fc8725f5f4 100644 --- a/apps/openmw/mwphysics/hasspherecollisioncallback.hpp +++ b/apps/openmw/mwphysics/hasspherecollisioncallback.hpp @@ -22,28 +22,36 @@ namespace MWPhysics return nearest.distance(position) < radius; } + template class HasSphereCollisionCallback final : public btBroadphaseAabbCallback { public: HasSphereCollisionCallback(const btVector3& position, const btScalar radius, btCollisionObject* object, - const int mask, const int group) + const int mask, const int group, OnCollision* onCollision) : mPosition(position), mRadius(radius), mCollisionObject(object), mCollisionFilterMask(mask), - mCollisionFilterGroup(group) + mCollisionFilterGroup(group), + mOnCollision(onCollision) { } bool process(const btBroadphaseProxy* proxy) override { - if (mResult) + if (mResult && mOnCollision == nullptr) return false; const auto collisionObject = static_cast(proxy->m_clientObject); - if (collisionObject == mCollisionObject) + if (collisionObject == mCollisionObject + || !needsCollision(*proxy) + || !testAabbAgainstSphere(proxy->m_aabbMin, proxy->m_aabbMax, mPosition, mRadius)) return true; - if (needsCollision(*proxy)) - mResult = testAabbAgainstSphere(proxy->m_aabbMin, proxy->m_aabbMax, mPosition, mRadius); + mResult = true; + if (mOnCollision != nullptr) + { + (*mOnCollision)(collisionObject); + return true; + } return !mResult; } @@ -58,6 +66,7 @@ namespace MWPhysics btCollisionObject* mCollisionObject; int mCollisionFilterMask; int mCollisionFilterGroup; + OnCollision* mOnCollision; bool mResult = false; bool needsCollision(const btBroadphaseProxy& proxy) const diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index 903e383370..a8c39efee2 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -492,7 +492,7 @@ namespace MWPhysics return heightField->second.get(); } - void PhysicsSystem::addObject (const MWWorld::Ptr& ptr, const std::string& mesh, osg::Quat rotation, int collisionType, bool skipAnimated) + void PhysicsSystem::addObject (const MWWorld::Ptr& ptr, const std::string& mesh, osg::Quat rotation, int collisionType) { if (ptr.mRef->mData.mPhysicsPostponed) return; @@ -500,9 +500,6 @@ namespace MWPhysics if (!shapeInstance || !shapeInstance->getCollisionShape()) return; - if (skipAnimated && shapeInstance->isAnimated()) - return; - assert(!getObject(ptr)); auto obj = std::make_shared(ptr, shapeInstance, rotation, collisionType, mTaskScheduler.get()); @@ -923,7 +920,8 @@ namespace MWPhysics CollisionType_Actor|CollisionType_Projectile); } - bool PhysicsSystem::isAreaOccupiedByOtherActor(const osg::Vec3f& position, const float radius, const MWWorld::ConstPtr& ignore) const + bool PhysicsSystem::isAreaOccupiedByOtherActor(const osg::Vec3f& position, const float radius, + const MWWorld::ConstPtr& ignore, std::vector* occupyingActors) const { btCollisionObject* object = nullptr; const auto it = mActors.find(ignore.mRef); @@ -934,7 +932,19 @@ namespace MWPhysics const auto aabbMax = bulletPosition + btVector3(radius, radius, radius); const int mask = MWPhysics::CollisionType_Actor; const int group = 0xff; - HasSphereCollisionCallback callback(bulletPosition, radius, object, mask, group); + if (occupyingActors == nullptr) + { + HasSphereCollisionCallback callback(bulletPosition, radius, object, mask, group, + static_cast(nullptr)); + mTaskScheduler->aabbTest(aabbMin, aabbMax, callback); + return callback.getResult(); + } + const auto onCollision = [&] (const btCollisionObject* object) + { + if (PtrHolder* holder = static_cast(object->getUserPointer())) + occupyingActors->push_back(holder->getPtr()); + }; + HasSphereCollisionCallback callback(bulletPosition, radius, object, mask, group, &onCollision); mTaskScheduler->aabbTest(aabbMin, aabbMax, callback); return callback.getResult(); } diff --git a/apps/openmw/mwphysics/physicssystem.hpp b/apps/openmw/mwphysics/physicssystem.hpp index 52515b563f..4fb7a3518f 100644 --- a/apps/openmw/mwphysics/physicssystem.hpp +++ b/apps/openmw/mwphysics/physicssystem.hpp @@ -128,7 +128,7 @@ namespace MWPhysics void setWaterHeight(float height); void disableWater(); - void addObject (const MWWorld::Ptr& ptr, const std::string& mesh, osg::Quat rotation, int collisionType = CollisionType_World, bool skipAnimated = false); + void addObject (const MWWorld::Ptr& ptr, const std::string& mesh, osg::Quat rotation, int collisionType = CollisionType_World); void addActor (const MWWorld::Ptr& ptr, const std::string& mesh); int addProjectile(const MWWorld::Ptr& caster, const osg::Vec3f& position, const std::string& mesh, bool computeRadius); @@ -252,7 +252,8 @@ namespace MWPhysics std::for_each(mAnimatedObjects.begin(), mAnimatedObjects.end(), function); } - bool isAreaOccupiedByOtherActor(const osg::Vec3f& position, const float radius, const MWWorld::ConstPtr& ignore) const; + bool isAreaOccupiedByOtherActor(const osg::Vec3f& position, const float radius, + const MWWorld::ConstPtr& ignore, std::vector* occupyingActors) const; void reportStats(unsigned int frameNumber, osg::Stats& stats) const; void reportCollision(const btVector3& position, const btVector3& normal); diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 07b0418f2b..be143510b2 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -486,6 +486,7 @@ namespace MWRender defaultMat->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(1,1,1,1)); defaultMat->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4f(0.f, 0.f, 0.f, 0.f)); sceneRoot->getOrCreateStateSet()->setAttribute(defaultMat); + sceneRoot->getOrCreateStateSet()->addUniform(new osg::Uniform("emissiveMult", 1.f)); mFog.reset(new FogManager()); diff --git a/apps/openmw/mwworld/cellvisitors.hpp b/apps/openmw/mwworld/cellvisitors.hpp index 81e1248e6d..77f33fa84b 100644 --- a/apps/openmw/mwworld/cellvisitors.hpp +++ b/apps/openmw/mwworld/cellvisitors.hpp @@ -25,16 +25,6 @@ namespace MWWorld } }; - struct ListObjectsVisitor - { - std::vector mObjects; - - bool operator() (const MWWorld::Ptr& ptr) - { - mObjects.push_back (ptr); - return true; - } - }; } #endif diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index 55e39de55e..da4dd9d99e 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -30,12 +30,12 @@ namespace MWWorld } - void Class::insertObject(const Ptr& ptr, const std::string& mesh, const osg::Quat& rotation, MWPhysics::PhysicsSystem& physics, bool skipAnimated) const + void Class::insertObject(const Ptr& ptr, const std::string& mesh, const osg::Quat& rotation, MWPhysics::PhysicsSystem& physics) const { } - void Class::insertObjectPhysics(const Ptr& ptr, const std::string& mesh, const osg::Quat& rotation, MWPhysics::PhysicsSystem& physics, bool skipAnimated) const + void Class::insertObjectPhysics(const Ptr& ptr, const std::string& mesh, const osg::Quat& rotation, MWPhysics::PhysicsSystem& physics) const {} bool Class::apply (const MWWorld::Ptr& ptr, const std::string& id, const MWWorld::Ptr& actor) const diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index 4779a3065b..8e451ea580 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -78,9 +78,9 @@ namespace MWWorld } virtual void insertObjectRendering (const Ptr& ptr, const std::string& mesh, MWRender::RenderingInterface& renderingInterface) const; - virtual void insertObject(const Ptr& ptr, const std::string& mesh, const osg::Quat& rotation, MWPhysics::PhysicsSystem& physics, bool skipAnimated = false) const; + virtual void insertObject(const Ptr& ptr, const std::string& mesh, const osg::Quat& rotation, MWPhysics::PhysicsSystem& physics) const; ///< Add reference into a cell for rendering (default implementation: don't render anything). - virtual void insertObjectPhysics(const Ptr& ptr, const std::string& mesh, const osg::Quat& rotation, MWPhysics::PhysicsSystem& physics, bool skipAnimated = false) const; + virtual void insertObjectPhysics(const Ptr& ptr, const std::string& mesh, const osg::Quat& rotation, MWPhysics::PhysicsSystem& physics) const; virtual std::string getName (const ConstPtr& ptr) const = 0; ///< \return name or ID; can return an empty string. diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 57a5bd18f7..ed68a46059 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -104,7 +104,7 @@ namespace } void addObject(const MWWorld::Ptr& ptr, MWPhysics::PhysicsSystem& physics, - MWRender::RenderingManager& rendering, std::set& pagedRefs, bool onlyPhysics) + MWRender::RenderingManager& rendering, std::set& pagedRefs) { if (ptr.getRefData().getBaseNode() || physics.getActor(ptr)) { @@ -114,12 +114,6 @@ namespace std::string model = getModel(ptr, rendering.getResourceSystem()->getVFS()); const auto rotation = makeNodeRotation(ptr, RotationOrder::direct); - if (onlyPhysics && !physics.getObject(ptr) && !ptr.getClass().isActor()) - { - // When we preload physics object we need to skip animated objects. They are dependant on the scene graph which doesn't yet exist. - ptr.getClass().insertObject (ptr, model, rotation, physics, true); - return; - } const ESM::RefNum& refnum = ptr.getCellRef().getRefNum(); if (!refnum.hasContentFile() || pagedRefs.find(refnum) == pagedRefs.end()) @@ -137,8 +131,7 @@ namespace // Restore effect particles MWBase::Environment::get().getWorld()->applyLoopingParticles(ptr); - if (!physics.getObject(ptr)) - ptr.getClass().insertObject (ptr, model, rotation, physics); + ptr.getClass().insertObject (ptr, model, rotation, physics); MWBase::Environment::get().getLuaManager()->objectAddedToScene(ptr); } @@ -201,11 +194,10 @@ namespace { MWWorld::CellStore& mCell; Loading::Listener* mLoadingListener; - bool mOnlyObjects; std::vector mToInsert; - InsertVisitor (MWWorld::CellStore& cell, Loading::Listener* loadingListener, bool onlyObjects); + InsertVisitor (MWWorld::CellStore& cell, Loading::Listener* loadingListener); bool operator() (const MWWorld::Ptr& ptr); @@ -213,8 +205,8 @@ namespace void insert(AddObject&& addObject); }; - InsertVisitor::InsertVisitor (MWWorld::CellStore& cell, Loading::Listener* loadingListener, bool onlyObjects) - : mCell(cell), mLoadingListener(loadingListener), mOnlyObjects(onlyObjects) + InsertVisitor::InsertVisitor (MWWorld::CellStore& cell, Loading::Listener* loadingListener) + : mCell(cell), mLoadingListener(loadingListener) {} bool InsertVisitor::operator() (const MWWorld::Ptr& ptr) @@ -230,7 +222,7 @@ namespace { for (MWWorld::Ptr& ptr : mToInsert) { - if (!ptr.getRefData().isDeleted() && ptr.getRefData().isEnabled() && (!mOnlyObjects || !ptr.getClass().isActor())) + if (!ptr.getRefData().isDeleted() && ptr.getRefData().isEnabled()) { try { @@ -248,16 +240,6 @@ namespace } } - struct PositionVisitor - { - bool operator() (const MWWorld::Ptr& ptr) - { - if (!ptr.getRefData().isDeleted() && ptr.getRefData().isEnabled()) - ptr.getClass().adjustPosition (ptr, false); - return true; - } - }; - int getCellPositionDistanceToOrigin(const std::pair& cellPosition) { return std::abs(cellPosition.first) + std::abs(cellPosition.second); @@ -324,39 +306,12 @@ namespace MWWorld mRendering.update (duration, paused); } - void Scene::unloadInactiveCell (CellStore* cell) + void Scene::unloadCell(CellStore* cell) { - assert(mActiveCells.find(cell) == mActiveCells.end()); - assert(mInactiveCells.find(cell) != mInactiveCells.end()); - - Log(Debug::Info) << "Unloading cell " << cell->getCell()->getDescription(); - - ListObjectsVisitor visitor; - - cell->forEach(visitor); - for (const auto& ptr : visitor.mObjects) - { - mPhysics->remove(ptr); - ptr.mRef->mData.mPhysicsPostponed = false; - } - - if (cell->getCell()->isExterior()) - { - const auto cellX = cell->getCell()->getGridX(); - const auto cellY = cell->getCell()->getGridY(); - mPhysics->removeHeightField(cellX, cellY); - } - - mInactiveCells.erase(cell); - } - - void Scene::deactivateCell(CellStore* cell) - { - assert(mInactiveCells.find(cell) != mInactiveCells.end()); if (mActiveCells.find(cell) == mActiveCells.end()) return; - Log(Debug::Info) << "Deactivate cell " << cell->getCell()->getDescription(); + Log(Debug::Info) << "Unloading cell " << cell->getCell()->getDescription(); ListAndResetObjectsVisitor visitor; @@ -367,8 +322,8 @@ namespace MWWorld if (const auto object = mPhysics->getObject(ptr)) { mNavigator.removeObject(DetourNavigator::ObjectId(object)); - if (object->isAnimated()) - mPhysics->remove(ptr); + mPhysics->remove(ptr); + ptr.mRef->mData.mPhysicsPostponed = false; } else if (mPhysics->getActor(ptr)) { @@ -386,6 +341,8 @@ namespace MWWorld { if (mPhysics->getHeightField(cellX, cellY) != nullptr) mNavigator.removeHeightfield(osg::Vec2i(cellX, cellY)); + + mPhysics->removeHeightField(cellX, cellY); } if (cell->getCell()->hasWater()) @@ -408,12 +365,11 @@ namespace MWWorld mActiveCells.erase(cell); } - void Scene::activateCell(CellStore *cell, Loading::Listener* loadingListener, bool respawn) + void Scene::loadCell(CellStore *cell, Loading::Listener* loadingListener, bool respawn) { using DetourNavigator::HeightfieldShape; assert(mActiveCells.find(cell) == mActiveCells.end()); - assert(mInactiveCells.find(cell) != mInactiveCells.end()); mActiveCells.insert(cell); Log(Debug::Info) << "Loading cell " << cell->getCell()->getDescription(); @@ -425,13 +381,25 @@ namespace MWWorld if (cell->getCell()->isExterior()) { + osg::ref_ptr land = mRendering.getLandManager()->getLand(cellX, cellY); + const ESM::Land::LandData* data = land ? land->getData(ESM::Land::DATA_VHGT) : nullptr; + const float verts = ESM::Land::LAND_SIZE; + const float worldsize = ESM::Land::REAL_SIZE; + if (data) + { + mPhysics->addHeightField (data->mHeights, cellX, cellY, worldsize / (verts-1), verts, data->mMinHeight, data->mMaxHeight, land.get()); + } + else + { + static std::vector defaultHeight; + defaultHeight.resize(verts*verts, ESM::Land::DEFAULT_HEIGHT); + mPhysics->addHeightField (&defaultHeight[0], cellX, cellY, worldsize / (verts-1), verts, ESM::Land::DEFAULT_HEIGHT, ESM::Land::DEFAULT_HEIGHT, land.get()); + } if (const auto heightField = mPhysics->getHeightField(cellX, cellY)) { const osg::Vec2i cellPosition(cellX, cellY); const btVector3& origin = heightField->getCollisionObject()->getWorldTransform().getOrigin(); const osg::Vec3f shift(origin.x(), origin.y(), origin.z()); - const osg::ref_ptr land = mRendering.getLandManager()->getLand(cellX, cellY); - const ESM::Land::LandData* const data = land == nullptr ? nullptr : land->getData(ESM::Land::DATA_VHGT); const HeightfieldShape shape = [&] () -> HeightfieldShape { if (data == nullptr) @@ -462,7 +430,7 @@ namespace MWWorld if (respawn) cell->respawn(); - insertCell(*cell, loadingListener, false); + insertCell(*cell, loadingListener); mRendering.addCell(cell); @@ -511,49 +479,14 @@ namespace MWWorld mPreloader->notifyLoaded(cell); } - void Scene::loadInactiveCell(CellStore *cell, Loading::Listener* loadingListener) - { - assert(mActiveCells.find(cell) == mActiveCells.end()); - assert(mInactiveCells.find(cell) == mInactiveCells.end()); - mInactiveCells.insert(cell); - - Log(Debug::Info) << "Loading inactive cell " << cell->getCell()->getDescription(); - - if (cell->getCell()->isExterior()) - { - float verts = ESM::Land::LAND_SIZE; - float worldsize = ESM::Land::REAL_SIZE; - - const int cellX = cell->getCell()->getGridX(); - const int cellY = cell->getCell()->getGridY(); - - osg::ref_ptr land = mRendering.getLandManager()->getLand(cellX, cellY); - const ESM::Land::LandData* data = land ? land->getData(ESM::Land::DATA_VHGT) : nullptr; - if (data) - { - mPhysics->addHeightField (data->mHeights, cellX, cellY, worldsize / (verts-1), verts, data->mMinHeight, data->mMaxHeight, land.get()); - } - else - { - static std::vector defaultHeight; - defaultHeight.resize(verts*verts, ESM::Land::DEFAULT_HEIGHT); - mPhysics->addHeightField (&defaultHeight[0], cellX, cellY, worldsize / (verts-1), verts, ESM::Land::DEFAULT_HEIGHT, ESM::Land::DEFAULT_HEIGHT, land.get()); - } - } - - insertCell(*cell, loadingListener, true); - } - void Scene::clear() { - for (auto iter = mInactiveCells.begin(); iter!=mInactiveCells.end(); ) + for (auto iter = mActiveCells.begin(); iter!=mActiveCells.end(); ) { auto* cell = *iter++; - deactivateCell(cell); - unloadInactiveCell (cell); + unloadCell (cell); } assert(mActiveCells.empty()); - assert(mInactiveCells.empty()); mCurrentCell = nullptr; mPreloader->clear(); @@ -595,7 +528,7 @@ namespace MWWorld void Scene::changeCellGrid (const osg::Vec3f &pos, int playerCellX, int playerCellY, bool changeEvent) { - for (auto iter = mInactiveCells.begin(); iter != mInactiveCells.end(); ) + for (auto iter = mActiveCells.begin(); iter != mActiveCells.end(); ) { auto* cell = *iter++; if (cell->getCell()->isExterior()) @@ -603,16 +536,10 @@ namespace MWWorld const auto dx = std::abs(playerCellX - cell->getCell()->getGridX()); const auto dy = std::abs(playerCellY - cell->getCell()->getGridY()); if (dx > mHalfGridSize || dy > mHalfGridSize) - deactivateCell(cell); - - if (dx > mHalfGridSize+1 || dy > mHalfGridSize+1) - unloadInactiveCell(cell); + unloadCell(cell); } else - { - deactivateCell(cell); - unloadInactiveCell(cell); - } + unloadCell (cell); } mCurrentGridCenter = osg::Vec2i(playerCellX, playerCellY); @@ -662,7 +589,6 @@ namespace MWWorld } auto cellsPositionsToLoad = cellsToLoad(mActiveCells,mHalfGridSize); - auto cellsPositionsToLoadInactive = cellsToLoad(mInactiveCells,mHalfGridSize+1); Loading::Listener* loadingListener = MWBase::Environment::get().getWindowManager()->getLoadingScreen(); Loading::ScopedLoad load(loadingListener); @@ -685,26 +611,12 @@ namespace MWWorld return getCellPositionPriority(lhs) < getCellPositionPriority(rhs); }); - std::sort(cellsPositionsToLoadInactive.begin(), cellsPositionsToLoadInactive.end(), - [&] (const std::pair& lhs, const std::pair& rhs) { - return getCellPositionPriority(lhs) < getCellPositionPriority(rhs); - }); - - // Load cells - for (const auto& [x,y] : cellsPositionsToLoadInactive) - { - if (!isCellInCollection(x, y, mInactiveCells)) - { - CellStore *cell = MWBase::Environment::get().getWorld()->getExterior(x, y); - loadInactiveCell (cell, loadingListener); - } - } for (const auto& [x,y] : cellsPositionsToLoad) { if (!isCellInCollection(x, y, mActiveCells)) { CellStore *cell = MWBase::Environment::get().getWorld()->getExterior(x, y); - activateCell (cell, loadingListener, changeEvent); + loadCell (cell, loadingListener, changeEvent); } } @@ -737,17 +649,15 @@ namespace MWWorld loadingListener->setLabel("Testing exterior cells ("+std::to_string(i)+"/"+std::to_string(cells.getExtSize())+")..."); CellStore *cell = MWBase::Environment::get().getWorld()->getExterior(it->mData.mX, it->mData.mY); - loadInactiveCell(cell, nullptr); - activateCell(cell, nullptr, false); + loadCell(cell, nullptr, false); - auto iter = mInactiveCells.begin(); - while (iter != mInactiveCells.end()) + auto iter = mActiveCells.begin(); + while (iter != mActiveCells.end()) { if (it->isExterior() && it->mData.mX == (*iter)->getCell()->getGridX() && it->mData.mY == (*iter)->getCell()->getGridY()) { - deactivateCell(*iter); - unloadInactiveCell(*iter); + unloadCell(*iter); break; } @@ -785,18 +695,16 @@ namespace MWWorld loadingListener->setLabel("Testing interior cells ("+std::to_string(i)+"/"+std::to_string(cells.getIntSize())+")..."); CellStore *cell = MWBase::Environment::get().getWorld()->getInterior(it->mName); - loadInactiveCell(cell, nullptr); - activateCell(cell, nullptr, false); + loadCell(cell, nullptr, false); - auto iter = mInactiveCells.begin(); - while (iter != mInactiveCells.end()) + auto iter = mActiveCells.begin(); + while (iter != mActiveCells.end()) { assert (!(*iter)->getCell()->isExterior()); if (it->mName == (*iter)->getCell()->mName) { - deactivateCell(*iter); - unloadInactiveCell(*iter); + unloadCell(*iter); break; } @@ -915,21 +823,18 @@ namespace MWWorld Log(Debug::Info) << "Changing to interior"; // unload - for (auto iter = mInactiveCells.begin(); iter!=mInactiveCells.end(); ) + for (auto iter = mActiveCells.begin(); iter!=mActiveCells.end(); ) { auto* cellToUnload = *iter++; - deactivateCell(cellToUnload); - unloadInactiveCell(cellToUnload); + unloadCell(cellToUnload); } assert(mActiveCells.empty()); - assert(mInactiveCells.empty()); loadingListener->setProgressRange(cell->count()); // Load cell. mPagedRefs.clear(); - loadInactiveCell (cell, loadingListener); - activateCell (cell, loadingListener, changeEvent); + loadCell(cell, loadingListener, changeEvent); changePlayerCell(cell, position, adjustPlayerPos); @@ -979,26 +884,19 @@ namespace MWWorld mCellChanged = false; } - void Scene::insertCell (CellStore &cell, Loading::Listener* loadingListener, bool onlyObjects) + void Scene::insertCell (CellStore &cell, Loading::Listener* loadingListener) { - InsertVisitor insertVisitor(cell, loadingListener, onlyObjects); + InsertVisitor insertVisitor(cell, loadingListener); cell.forEach (insertVisitor); - insertVisitor.insert([&] (const MWWorld::Ptr& ptr) { addObject(ptr, *mPhysics, mRendering, mPagedRefs, onlyObjects); }); - if (!onlyObjects) - { - insertVisitor.insert([&] (const MWWorld::Ptr& ptr) { addObject(ptr, *mPhysics, mNavigator); }); - - // do adjustPosition (snapping actors to ground) after objects are loaded, so we don't depend on the loading order - PositionVisitor posVisitor; - cell.forEach (posVisitor); - } + insertVisitor.insert([&] (const MWWorld::Ptr& ptr) { addObject(ptr, *mPhysics, mRendering, mPagedRefs); }); + insertVisitor.insert([&] (const MWWorld::Ptr& ptr) { addObject(ptr, *mPhysics, mNavigator); }); } void Scene::addObjectToScene (const Ptr& ptr) { try { - addObject(ptr, *mPhysics, mRendering, mPagedRefs, false); + addObject(ptr, *mPhysics, mRendering, mPagedRefs); addObject(ptr, *mPhysics, mNavigator); MWBase::Environment::get().getWorld()->scaleObject(ptr, ptr.getCellRef().getScale()); const auto player = MWBase::Environment::get().getWorld()->getPlayerPtr(); diff --git a/apps/openmw/mwworld/scene.hpp b/apps/openmw/mwworld/scene.hpp index 86cb1ee5b7..623bddbc82 100644 --- a/apps/openmw/mwworld/scene.hpp +++ b/apps/openmw/mwworld/scene.hpp @@ -77,7 +77,6 @@ namespace MWWorld CellStore* mCurrentCell; // the cell the player is in CellStoreCollection mActiveCells; - CellStoreCollection mInactiveCells; bool mCellChanged; MWPhysics::PhysicsSystem *mPhysics; MWRender::RenderingManager& mRendering; @@ -100,7 +99,7 @@ namespace MWWorld std::vector> mWorkItems; - void insertCell(CellStore &cell, Loading::Listener* loadingListener, bool onlyObjects); + void insertCell(CellStore &cell, Loading::Listener* loadingListener); osg::Vec2i mCurrentGridCenter; // Load and unload cells as necessary to create a cell grid with "X" and "Y" in the center @@ -116,10 +115,8 @@ namespace MWWorld osg::Vec4i gridCenterToBounds(const osg::Vec2i ¢erCell) const; osg::Vec2i getNewGridCenter(const osg::Vec3f &pos, const osg::Vec2i *currentGridCenter = nullptr) const; - void unloadInactiveCell(CellStore* cell); - void deactivateCell(CellStore* cell); - void activateCell(CellStore *cell, Loading::Listener* loadingListener, bool respawn); - void loadInactiveCell(CellStore *cell, Loading::Listener* loadingListener); + void unloadCell(CellStore* cell); + void loadCell(CellStore *cell, Loading::Listener* loadingListener, bool respawn); public: diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index b9b8af0987..299cc8676b 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -3923,9 +3923,10 @@ namespace MWWorld return btRayAabb(localFrom, localTo, aabbMin, aabbMax, hitDistance, hitNormal); } - bool World::isAreaOccupiedByOtherActor(const osg::Vec3f& position, const float radius, const MWWorld::ConstPtr& ignore) const + bool World::isAreaOccupiedByOtherActor(const osg::Vec3f& position, const float radius, + const MWWorld::ConstPtr& ignore, std::vector* occupyingActors) const { - return mPhysics->isAreaOccupiedByOtherActor(position, radius, ignore); + return mPhysics->isAreaOccupiedByOtherActor(position, radius, ignore, occupyingActors); } void World::reportStats(unsigned int frameNumber, osg::Stats& stats) const diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index d6b7eb3fbf..6e48f045c0 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -735,7 +735,8 @@ namespace MWWorld bool hasCollisionWithDoor(const MWWorld::ConstPtr& door, const osg::Vec3f& position, const osg::Vec3f& destination) const override; - bool isAreaOccupiedByOtherActor(const osg::Vec3f& position, const float radius, const MWWorld::ConstPtr& ignore) const override; + bool isAreaOccupiedByOtherActor(const osg::Vec3f& position, const float radius, + const MWWorld::ConstPtr& ignore, std::vector* occupyingActors) const override; void reportStats(unsigned int frameNumber, osg::Stats& stats) const override; diff --git a/components/lua/scriptscontainer.cpp b/components/lua/scriptscontainer.cpp index b6882b0988..703381a453 100644 --- a/components/lua/scriptscontainer.cpp +++ b/components/lua/scriptscontainer.cpp @@ -328,6 +328,12 @@ namespace LuaUtil std::make_heap(mHoursTimersQueue.begin(), mHoursTimersQueue.end()); } + ScriptsContainer::~ScriptsContainer() + { + for (auto& [_, script] : mScripts) + script.mHiddenData[ScriptId::KEY] = sol::nil; + } + void ScriptsContainer::removeAllScripts() { for (auto& [_, script] : mScripts) diff --git a/components/lua/scriptscontainer.hpp b/components/lua/scriptscontainer.hpp index 0bf50b8793..69aa18e940 100644 --- a/components/lua/scriptscontainer.hpp +++ b/components/lua/scriptscontainer.hpp @@ -75,7 +75,7 @@ namespace LuaUtil ScriptsContainer(LuaUtil::LuaState* lua, std::string_view namePrefix); ScriptsContainer(const ScriptsContainer&) = delete; ScriptsContainer(ScriptsContainer&&) = delete; - virtual ~ScriptsContainer() { removeAllScripts(); } + virtual ~ScriptsContainer(); // Adds package that will be available (via `require`) for all scripts in the container. // Automatically applies LuaUtil::makeReadOnly to the package. diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 0432aef5b4..a564844ce6 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -1134,8 +1134,6 @@ namespace NifOsg trans->addChild(toAttach); parentNode->addChild(trans); } - // create partsys stateset in order to pass in ShaderVisitor like all other Drawables - partsys->getOrCreateStateSet(); } void handleNiGeometryData(osg::Geometry *geometry, const Nif::NiGeometryData* data, const std::vector& boundTextures, const std::string& name) @@ -1923,8 +1921,6 @@ namespace NifOsg void applyDrawableProperties(osg::Node* node, const std::vector& properties, SceneUtil::CompositeStateSetUpdater* composite, bool hasVertexColors, int animflags) { - osg::StateSet* stateset = node->getOrCreateStateSet(); - // Specular lighting is enabled by default, but there's a quirk... bool specEnabled = true; osg::ref_ptr mat (new osg::Material); @@ -2006,15 +2002,15 @@ namespace NifOsg if (blendFunc->getDestination() == GL_DST_ALPHA) blendFunc->setDestination(GL_ONE); blendFunc = shareAttribute(blendFunc); - stateset->setAttributeAndModes(blendFunc, osg::StateAttribute::ON); + node->getOrCreateStateSet()->setAttributeAndModes(blendFunc, osg::StateAttribute::ON); bool noSort = (alphaprop->flags>>13)&1; if (!noSort) - stateset->setRenderingHint(osg::StateSet::TRANSPARENT_BIN); + node->getOrCreateStateSet()->setRenderingHint(osg::StateSet::TRANSPARENT_BIN); else - stateset->setRenderBinToInherit(); + node->getOrCreateStateSet()->setRenderBinToInherit(); } - else + else if (osg::StateSet* stateset = node->getStateSet()) { stateset->removeAttribute(osg::StateAttribute::BLENDFUNC); stateset->removeMode(GL_BLEND); @@ -2025,9 +2021,9 @@ namespace NifOsg { osg::ref_ptr alphaFunc (new osg::AlphaFunc(getTestMode((alphaprop->flags>>10)&0x7), alphaprop->data.threshold/255.f)); alphaFunc = shareAttribute(alphaFunc); - stateset->setAttributeAndModes(alphaFunc, osg::StateAttribute::ON); + node->getOrCreateStateSet()->setAttributeAndModes(alphaFunc, osg::StateAttribute::ON); } - else + else if (osg::StateSet* stateset = node->getStateSet()) { stateset->removeAttribute(osg::StateAttribute::ALPHAFUNC); stateset->removeMode(GL_ALPHA_TEST); @@ -2083,8 +2079,10 @@ namespace NifOsg mat = shareAttribute(mat); + osg::StateSet* stateset = node->getOrCreateStateSet(); stateset->setAttributeAndModes(mat, osg::StateAttribute::ON); - stateset->addUniform(new osg::Uniform("emissiveMult", emissiveMult)); + if (emissiveMult != 1.f) + stateset->addUniform(new osg::Uniform("emissiveMult", emissiveMult)); } }; diff --git a/components/shader/shadervisitor.cpp b/components/shader/shadervisitor.cpp index 0d0710853f..cc2b781cfd 100644 --- a/components/shader/shadervisitor.cpp +++ b/components/shader/shadervisitor.cpp @@ -116,7 +116,6 @@ namespace Shader , mImageManager(imageManager) , mDefaultShaderPrefix(defaultShaderPrefix) { - mRequirements.emplace_back(); } void ShaderVisitor::setForceShaders(bool force) @@ -421,7 +420,10 @@ namespace Shader void ShaderVisitor::pushRequirements(osg::Node& node) { - mRequirements.push_back(mRequirements.back()); + if (mRequirements.empty()) + mRequirements.emplace_back(); + else + mRequirements.push_back(mRequirements.back()); mRequirements.back().mNode = &node; } diff --git a/docs/source/reference/modding/paths.rst b/docs/source/reference/modding/paths.rst index bc955b703d..db5000527b 100644 --- a/docs/source/reference/modding/paths.rst +++ b/docs/source/reference/modding/paths.rst @@ -29,7 +29,7 @@ Savegames +--------------+-----------------------------------------------------------------------------------------------------+ | OS | Location | +==============+=====================================================================================================+ -| Linux | ``$HOME/.local/share/openmw/saves`` | +| Linux | ``$HOME/.local/share/openmw/saves`` | +--------------+-----------------------------------------------------------------------------------------------------+ | Mac | ``$HOME/Library/Application\ Support/openmw/saves`` | +--------------+---------------+-------------------------------------------------------------------------------------+