mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-01-01 12:01:51 +00:00
125 lines
5.6 KiB
C++
125 lines
5.6 KiB
C++
#include "trace.h"
|
|
|
|
#include <components/misc/convert.hpp>
|
|
|
|
#include <BulletCollision/CollisionDispatch/btCollisionWorld.h>
|
|
#include <BulletCollision/CollisionShapes/btConvexShape.h>
|
|
|
|
#include "actor.hpp"
|
|
#include "actorconvexcallback.hpp"
|
|
#include "collisiontype.hpp"
|
|
|
|
namespace MWPhysics
|
|
{
|
|
|
|
ActorConvexCallback sweepHelper(const btCollisionObject* actor, const btVector3& from, const btVector3& to,
|
|
const btCollisionWorld* world, bool actorFilter)
|
|
{
|
|
const btTransform& trans = actor->getWorldTransform();
|
|
btTransform transFrom(trans);
|
|
btTransform transTo(trans);
|
|
transFrom.setOrigin(from);
|
|
transTo.setOrigin(to);
|
|
|
|
const btCollisionShape* shape = actor->getCollisionShape();
|
|
assert(shape->isConvex());
|
|
|
|
const btVector3 motion
|
|
= from - to; // FIXME: this is backwards; means ActorConvexCallback is doing dot product tests backwards too
|
|
ActorConvexCallback traceCallback(actor, motion, btScalar(0.0), world);
|
|
// Inherit the actor's collision group and mask
|
|
traceCallback.m_collisionFilterGroup = actor->getBroadphaseHandle()->m_collisionFilterGroup;
|
|
traceCallback.m_collisionFilterMask = actor->getBroadphaseHandle()->m_collisionFilterMask;
|
|
if (actorFilter)
|
|
traceCallback.m_collisionFilterMask &= ~CollisionType_Actor;
|
|
|
|
world->convexSweepTest(static_cast<const btConvexShape*>(shape), transFrom, transTo, traceCallback);
|
|
return traceCallback;
|
|
}
|
|
|
|
void ActorTracer::doTrace(const btCollisionObject* actor, const osg::Vec3f& start, const osg::Vec3f& end,
|
|
const btCollisionWorld* world, bool attempt_short_trace)
|
|
{
|
|
const btVector3 btstart = Misc::Convert::toBullet(start);
|
|
btVector3 btend = Misc::Convert::toBullet(end);
|
|
|
|
// Because Bullet's collision trace tests touch *all* geometry in its path, a lot of long collision tests
|
|
// will unnecessarily test against complex meshes that are dozens of units away. This wouldn't normally be
|
|
// a problem, but bullet isn't the fastest in the world when it comes to doing tests against triangle meshes.
|
|
// Therefore, we try out a short trace first, then only fall back to the full length trace if needed.
|
|
// This trace needs to be at least a couple units long, but there's no one particular ideal length.
|
|
// The length of 2.1 chosen here is a "works well in practice after testing a few random lengths" value.
|
|
// (Also, we only do this short test if the intended collision trace is long enough for it to make sense.)
|
|
const float fallback_length = 2.1f;
|
|
bool doing_short_trace = false;
|
|
// For some reason, typical scenes perform a little better if we increase the threshold length for the length
|
|
// test. (Multiplying by 2 in 'square distance' units gives us about 1.4x the threshold length. In benchmarks
|
|
// this was
|
|
// slightly better for the performance of normal scenes than 4.0, and just plain better than 1.0.)
|
|
if (attempt_short_trace && (btend - btstart).length2() > fallback_length * fallback_length * 2.0)
|
|
{
|
|
btend = btstart + (btend - btstart).normalized() * fallback_length;
|
|
doing_short_trace = true;
|
|
}
|
|
|
|
const auto traceCallback = sweepHelper(actor, btstart, btend, world, false);
|
|
|
|
// Copy the hit data over to our trace results struct:
|
|
if (traceCallback.hasHit())
|
|
{
|
|
mFraction = traceCallback.m_closestHitFraction;
|
|
// ensure fraction is correct (covers intended distance traveled instead of actual distance traveled)
|
|
if (doing_short_trace && (end - start).length2() > 0.0)
|
|
mFraction *= (btend - btstart).length() / (end - start).length();
|
|
mPlaneNormal = Misc::Convert::toOsg(traceCallback.m_hitNormalWorld);
|
|
mEndPos = (end - start) * mFraction + start;
|
|
mHitPoint = Misc::Convert::toOsg(traceCallback.m_hitPointWorld);
|
|
mHitObject = traceCallback.m_hitCollisionObject;
|
|
}
|
|
else
|
|
{
|
|
if (doing_short_trace)
|
|
{
|
|
btend = Misc::Convert::toBullet(end);
|
|
const auto newTraceCallback = sweepHelper(actor, btstart, btend, world, false);
|
|
|
|
if (newTraceCallback.hasHit())
|
|
{
|
|
mFraction = newTraceCallback.m_closestHitFraction;
|
|
mPlaneNormal = Misc::Convert::toOsg(newTraceCallback.m_hitNormalWorld);
|
|
mEndPos = (end - start) * mFraction + start;
|
|
mHitPoint = Misc::Convert::toOsg(newTraceCallback.m_hitPointWorld);
|
|
mHitObject = newTraceCallback.m_hitCollisionObject;
|
|
return;
|
|
}
|
|
}
|
|
// fallthrough
|
|
mEndPos = end;
|
|
mPlaneNormal = osg::Vec3f(0.0f, 0.0f, 1.0f);
|
|
mFraction = 1.0f;
|
|
mHitPoint = end;
|
|
mHitObject = nullptr;
|
|
}
|
|
}
|
|
|
|
void ActorTracer::findGround(
|
|
const Actor* actor, const osg::Vec3f& start, const osg::Vec3f& end, const btCollisionWorld* world)
|
|
{
|
|
const auto traceCallback = sweepHelper(
|
|
actor->getCollisionObject(), Misc::Convert::toBullet(start), Misc::Convert::toBullet(end), world, true);
|
|
if (traceCallback.hasHit())
|
|
{
|
|
mFraction = traceCallback.m_closestHitFraction;
|
|
mPlaneNormal = Misc::Convert::toOsg(traceCallback.m_hitNormalWorld);
|
|
mEndPos = (end - start) * mFraction + start;
|
|
}
|
|
else
|
|
{
|
|
mEndPos = end;
|
|
mPlaneNormal = osg::Vec3f(0.0f, 0.0f, 1.0f);
|
|
mFraction = 1.0f;
|
|
}
|
|
}
|
|
|
|
}
|