mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-01-09 03:39:14 +00:00
07fa1803f7
collision handling and castRay() to avoid calling getPtr(). It is a step forward removing the mutex inside of PtrHolder. Do the same for DeepestNotMeContactTestResultCallback. It is used only for not-ranged combat for now, but do it anyway for parity with all other callback. This way, once the PtrHolder mutex is gone one will not have to worry about wether it is safe to use the callback in a specific context. To avoid use-after-free with projectile / projectile collision, defer deletion of projectile. Since instead of storing a copy of target Ptr we have a pointer to its collision object, we can't delete projectiles until after we finished iterating over the loops.
102 lines
4.6 KiB
C++
102 lines
4.6 KiB
C++
#include "actorconvexcallback.hpp"
|
|
#include "collisiontype.hpp"
|
|
#include "contacttestwrapper.h"
|
|
|
|
#include <BulletCollision/CollisionDispatch/btCollisionObject.h>
|
|
#include <components/misc/convert.hpp>
|
|
|
|
#include "projectile.hpp"
|
|
|
|
namespace MWPhysics
|
|
{
|
|
class ActorOverlapTester : public btCollisionWorld::ContactResultCallback
|
|
{
|
|
public:
|
|
bool overlapping = false;
|
|
|
|
btScalar addSingleResult(btManifoldPoint& cp,
|
|
const btCollisionObjectWrapper* colObj0Wrap,
|
|
int partId0,
|
|
int index0,
|
|
const btCollisionObjectWrapper* colObj1Wrap,
|
|
int partId1,
|
|
int index1) override
|
|
{
|
|
if(cp.getDistance() <= 0.0f)
|
|
overlapping = true;
|
|
return btScalar(1);
|
|
}
|
|
};
|
|
|
|
ActorConvexCallback::ActorConvexCallback(const btCollisionObject *me, const btVector3 &motion, btScalar minCollisionDot, const btCollisionWorld * world)
|
|
: btCollisionWorld::ClosestConvexResultCallback(btVector3(0.0, 0.0, 0.0), btVector3(0.0, 0.0, 0.0)),
|
|
mMe(me), mMotion(motion), mMinCollisionDot(minCollisionDot), mWorld(world)
|
|
{
|
|
}
|
|
|
|
btScalar ActorConvexCallback::addSingleResult(btCollisionWorld::LocalConvexResult& convexResult, bool normalInWorldSpace)
|
|
{
|
|
if (convexResult.m_hitCollisionObject == mMe)
|
|
return btScalar(1);
|
|
|
|
// override data for actor-actor collisions
|
|
// vanilla Morrowind seems to make overlapping actors collide as though they are both cylinders with a diameter of the distance between them
|
|
// For some reason this doesn't work as well as it should when using capsules, but it still helps a lot.
|
|
if(convexResult.m_hitCollisionObject->getBroadphaseHandle()->m_collisionFilterGroup == CollisionType_Actor)
|
|
{
|
|
ActorOverlapTester isOverlapping;
|
|
// FIXME: This is absolutely terrible and bullet should feel terrible for not making contactPairTest const-correct.
|
|
ContactTestWrapper::contactPairTest(const_cast<btCollisionWorld*>(mWorld), const_cast<btCollisionObject*>(mMe), const_cast<btCollisionObject*>(convexResult.m_hitCollisionObject), isOverlapping);
|
|
|
|
if(isOverlapping.overlapping)
|
|
{
|
|
auto originA = Misc::Convert::toOsg(mMe->getWorldTransform().getOrigin());
|
|
auto originB = Misc::Convert::toOsg(convexResult.m_hitCollisionObject->getWorldTransform().getOrigin());
|
|
osg::Vec3f motion = Misc::Convert::toOsg(mMotion);
|
|
osg::Vec3f normal = (originA-originB);
|
|
normal.z() = 0;
|
|
normal.normalize();
|
|
// only collide if horizontally moving towards the hit actor (note: the motion vector appears to be inverted)
|
|
// FIXME: This kinda screws with standing on actors that walk up slopes for some reason. Makes you fall through them.
|
|
// It happens in vanilla Morrowind too, but much less often.
|
|
// I tried hunting down why but couldn't figure it out. Possibly a stair stepping or ground ejection bug.
|
|
if(normal * motion > 0.0f)
|
|
{
|
|
convexResult.m_hitFraction = 0.0f;
|
|
convexResult.m_hitNormalLocal = Misc::Convert::toBullet(normal);
|
|
return ClosestConvexResultCallback::addSingleResult(convexResult, true);
|
|
}
|
|
else
|
|
{
|
|
return btScalar(1);
|
|
}
|
|
}
|
|
}
|
|
if (convexResult.m_hitCollisionObject->getBroadphaseHandle()->m_collisionFilterGroup == CollisionType_Projectile)
|
|
{
|
|
auto* projectileHolder = static_cast<Projectile*>(convexResult.m_hitCollisionObject->getUserPointer());
|
|
if (!projectileHolder->isActive())
|
|
return btScalar(1);
|
|
if (projectileHolder->isValidTarget(mMe))
|
|
projectileHolder->hit(mMe, convexResult.m_hitPointLocal, convexResult.m_hitNormalLocal);
|
|
return btScalar(1);
|
|
}
|
|
|
|
btVector3 hitNormalWorld;
|
|
if (normalInWorldSpace)
|
|
hitNormalWorld = convexResult.m_hitNormalLocal;
|
|
else
|
|
{
|
|
///need to transform normal into worldspace
|
|
hitNormalWorld = convexResult.m_hitCollisionObject->getWorldTransform().getBasis()*convexResult.m_hitNormalLocal;
|
|
}
|
|
|
|
// dot product of the motion vector against the collision contact normal
|
|
btScalar dotCollision = mMotion.dot(hitNormalWorld);
|
|
if (dotCollision <= mMinCollisionDot)
|
|
return btScalar(1);
|
|
|
|
return ClosestConvexResultCallback::addSingleResult(convexResult, normalInWorldSpace);
|
|
}
|
|
}
|