diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 625758d640..6479a1a404 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -10,6 +10,7 @@ #include #include +#include #include @@ -658,7 +659,7 @@ 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, std::vector* occupyingActors = nullptr) const = 0; + const Misc::Span& ignore, std::vector* occupyingActors = nullptr) const = 0; virtual void reportStats(unsigned int frameNumber, osg::Stats& stats) const = 0; diff --git a/apps/openmw/mwmechanics/aitravel.cpp b/apps/openmw/mwmechanics/aitravel.cpp index 919685e93d..770d2f7ba6 100644 --- a/apps/openmw/mwmechanics/aitravel.cpp +++ b/apps/openmw/mwmechanics/aitravel.cpp @@ -83,7 +83,7 @@ namespace MWMechanics if (mDestinationCheck.update(duration) == Misc::TimerStatus::Elapsed) { std::vector occupyingActors; - if (isAreaOccupiedByOtherActor(actor, targetPos, &occupyingActors)) + if (isAreaOccupiedByOtherActor(actor, targetPos, true, &occupyingActors)) { const float actorRadius = getActorRadius(actor); const float distanceToTarget = distance(actorPos, targetPos); diff --git a/apps/openmw/mwmechanics/obstacle.cpp b/apps/openmw/mwmechanics/obstacle.cpp index 344cc82058..3d64eae862 100644 --- a/apps/openmw/mwmechanics/obstacle.cpp +++ b/apps/openmw/mwmechanics/obstacle.cpp @@ -1,5 +1,7 @@ #include "obstacle.hpp" +#include + #include #include "../mwworld/class.hpp" @@ -74,13 +76,19 @@ namespace MWMechanics return MWWorld::Ptr(); // none found } - bool isAreaOccupiedByOtherActor(const MWWorld::ConstPtr& actor, const osg::Vec3f& destination, + bool isAreaOccupiedByOtherActor(const MWWorld::ConstPtr& actor, const osg::Vec3f& destination, bool ignorePlayer, 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); + if (ignorePlayer) + { + const std::array ignore {actor, world->getPlayerConstPtr()}; + return world->isAreaOccupiedByOtherActor(destination, 2 * maxHalfExtent, ignore, occupyingActors); + } + const std::array ignore {actor}; + return world->isAreaOccupiedByOtherActor(destination, 2 * maxHalfExtent, ignore, occupyingActors); } ObstacleCheck::ObstacleCheck() diff --git a/apps/openmw/mwmechanics/obstacle.hpp b/apps/openmw/mwmechanics/obstacle.hpp index 24bd5ed1c1..2026f22129 100644 --- a/apps/openmw/mwmechanics/obstacle.hpp +++ b/apps/openmw/mwmechanics/obstacle.hpp @@ -24,7 +24,7 @@ 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, + bool isAreaOccupiedByOtherActor(const MWWorld::ConstPtr& actor, const osg::Vec3f& destination, bool ignorePlayer = false, std::vector* occupyingActors = nullptr); class ObstacleCheck diff --git a/apps/openmw/mwphysics/hasspherecollisioncallback.hpp b/apps/openmw/mwphysics/hasspherecollisioncallback.hpp index a01ab96301..2d271a8fba 100644 --- a/apps/openmw/mwphysics/hasspherecollisioncallback.hpp +++ b/apps/openmw/mwphysics/hasspherecollisioncallback.hpp @@ -22,15 +22,15 @@ namespace MWPhysics return nearest.distance(position) < radius; } - template + template class HasSphereCollisionCallback final : public btBroadphaseAabbCallback { public: - HasSphereCollisionCallback(const btVector3& position, const btScalar radius, btCollisionObject* object, - const int mask, const int group, OnCollision* onCollision) + HasSphereCollisionCallback(const btVector3& position, const btScalar radius, const int mask, const int group, + const Ignore& ignore, OnCollision* onCollision) : mPosition(position), mRadius(radius), - mCollisionObject(object), + mIgnore(ignore), mCollisionFilterMask(mask), mCollisionFilterGroup(group), mOnCollision(onCollision) @@ -42,7 +42,7 @@ namespace MWPhysics if (mResult && mOnCollision == nullptr) return false; const auto collisionObject = static_cast(proxy->m_clientObject); - if (collisionObject == mCollisionObject + if (mIgnore(collisionObject) || !needsCollision(*proxy) || !testAabbAgainstSphere(proxy->m_aabbMin, proxy->m_aabbMax, mPosition, mRadius)) return true; @@ -63,7 +63,7 @@ namespace MWPhysics private: btVector3 mPosition; btScalar mRadius; - btCollisionObject* mCollisionObject; + Ignore mIgnore; int mCollisionFilterMask; int mCollisionFilterGroup; OnCollision* mOnCollision; diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index bbd3447b6a..06c2420df1 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -2,7 +2,11 @@ #include #include + #include +#include +#include + #include #include #include @@ -884,12 +888,19 @@ namespace MWPhysics } bool PhysicsSystem::isAreaOccupiedByOtherActor(const osg::Vec3f& position, const float radius, - const MWWorld::ConstPtr& ignore, std::vector* occupyingActors) const + const Misc::Span& ignore, std::vector* occupyingActors) const { - btCollisionObject* object = nullptr; - const auto it = mActors.find(ignore.mRef); - if (it != mActors.end()) - object = it->second->getCollisionObject(); + std::vector ignoredObjects; + ignoredObjects.reserve(ignore.size()); + for (const auto& v : ignore) + if (const auto it = mActors.find(v.mRef); it != mActors.end()) + ignoredObjects.push_back(it->second->getCollisionObject()); + std::sort(ignoredObjects.begin(), ignoredObjects.end()); + ignoredObjects.erase(std::unique(ignoredObjects.begin(), ignoredObjects.end()), ignoredObjects.end()); + const auto ignoreFilter = [&] (const btCollisionObject* v) + { + return std::binary_search(ignoredObjects.begin(), ignoredObjects.end(), v); + }; const auto bulletPosition = Misc::Convert::toBullet(position); const auto aabbMin = bulletPosition - btVector3(radius, radius, radius); const auto aabbMax = bulletPosition + btVector3(radius, radius, radius); @@ -897,7 +908,7 @@ namespace MWPhysics const int group = 0xff; if (occupyingActors == nullptr) { - HasSphereCollisionCallback callback(bulletPosition, radius, object, mask, group, + HasSphereCollisionCallback callback(bulletPosition, radius, mask, group, ignoreFilter, static_cast(nullptr)); mTaskScheduler->aabbTest(aabbMin, aabbMax, callback); return callback.getResult(); @@ -907,7 +918,7 @@ namespace MWPhysics if (PtrHolder* holder = static_cast(object->getUserPointer())) occupyingActors->push_back(holder->getPtr()); }; - HasSphereCollisionCallback callback(bulletPosition, radius, object, mask, group, &onCollision); + HasSphereCollisionCallback callback(bulletPosition, radius, mask, group, ignoreFilter, &onCollision); mTaskScheduler->aabbTest(aabbMin, aabbMax, callback); return callback.getResult(); } diff --git a/apps/openmw/mwphysics/physicssystem.hpp b/apps/openmw/mwphysics/physicssystem.hpp index b165f10761..4afafede4f 100644 --- a/apps/openmw/mwphysics/physicssystem.hpp +++ b/apps/openmw/mwphysics/physicssystem.hpp @@ -16,6 +16,8 @@ #include #include +#include + #include "../mwworld/ptr.hpp" #include "collisiontype.hpp" @@ -277,7 +279,7 @@ namespace MWPhysics } bool isAreaOccupiedByOtherActor(const osg::Vec3f& position, const float radius, - const MWWorld::ConstPtr& ignore, std::vector* occupyingActors) const; + const Misc::Span& 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/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 7f652d9520..ea2441847f 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -3997,7 +3997,7 @@ namespace MWWorld } bool World::isAreaOccupiedByOtherActor(const osg::Vec3f& position, const float radius, - const MWWorld::ConstPtr& ignore, std::vector* occupyingActors) const + const Misc::Span& ignore, std::vector* occupyingActors) const { return mPhysics->isAreaOccupiedByOtherActor(position, radius, ignore, occupyingActors); } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 399b7232de..0dd79fb778 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -736,7 +736,7 @@ 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, std::vector* occupyingActors) const override; + const Misc::Span& ignore, std::vector* occupyingActors) const override; void reportStats(unsigned int frameNumber, osg::Stats& stats) const override; diff --git a/components/misc/span.hpp b/components/misc/span.hpp new file mode 100644 index 0000000000..83a424a674 --- /dev/null +++ b/components/misc/span.hpp @@ -0,0 +1,36 @@ +#ifndef OPENMW_COMPONENTS_MISC_SPAN_H +#define OPENMW_COMPONENTS_MISC_SPAN_H + +#include + +namespace Misc +{ + template + class Span + { + public: + constexpr Span() = default; + + constexpr Span(T* pointer, std::size_t size) + : mPointer(pointer) + , mSize(size) + {} + + template + constexpr Span(Range& range) + : Span(range.data(), range.size()) + {} + + constexpr T* begin() const { return mPointer; } + + constexpr T* end() const { return mPointer + mSize; } + + constexpr std::size_t size() const { return mSize; } + + private: + T* mPointer = nullptr; + std::size_t mSize = 0; + }; +} + +#endif