forked from teamnwah/openmw-tes3coop
Merge pull request #1158 from logzero/move3
[RFC] Movement solver experiments
This commit is contained in:
commit
708009eac4
3 changed files with 105 additions and 69 deletions
|
@ -55,29 +55,46 @@ namespace MWPhysics
|
|||
static const float sMaxSlope = 49.0f;
|
||||
static const float sStepSizeUp = 34.0f;
|
||||
static const float sStepSizeDown = 62.0f;
|
||||
static const float sMinStep = 10.f;
|
||||
|
||||
// Arbitrary number. To prevent infinite loops. They shouldn't happen but it's good to be prepared.
|
||||
static const int sMaxIterations = 8;
|
||||
|
||||
// FIXME: move to a separate file
|
||||
class MovementSolver
|
||||
static bool isActor(const btCollisionObject *obj)
|
||||
{
|
||||
assert(obj);
|
||||
return obj->getBroadphaseHandle()->m_collisionFilterGroup == CollisionType_Actor;
|
||||
}
|
||||
|
||||
template <class Vec3>
|
||||
static bool isWalkableSlope(const Vec3 &normal)
|
||||
{
|
||||
static const float sMaxSlopeCos = std::cos(osg::DegreesToRadians(sMaxSlope));
|
||||
return (normal.z() > sMaxSlopeCos);
|
||||
}
|
||||
|
||||
static bool canStepDown(const ActorTracer &stepper)
|
||||
{
|
||||
return stepper.mHitObject && isWalkableSlope(stepper.mPlaneNormal) && !isActor(stepper.mHitObject);
|
||||
}
|
||||
|
||||
class Stepper
|
||||
{
|
||||
private:
|
||||
static float getSlope(osg::Vec3f normal)
|
||||
{
|
||||
normal.normalize();
|
||||
return osg::RadiansToDegrees(std::acos(normal * osg::Vec3f(0.f, 0.f, 1.f)));
|
||||
}
|
||||
const btCollisionWorld *mColWorld;
|
||||
const btCollisionObject *mColObj;
|
||||
|
||||
enum StepMoveResult
|
||||
{
|
||||
Result_Blocked, // unable to move over obstacle
|
||||
Result_MaxSlope, // unable to end movement on this slope
|
||||
Result_Success
|
||||
};
|
||||
ActorTracer mTracer, mUpStepper, mDownStepper;
|
||||
bool mHaveMoved;
|
||||
|
||||
static StepMoveResult stepMove(const btCollisionObject *colobj, osg::Vec3f &position,
|
||||
const osg::Vec3f &toMove, float &remainingTime, const btCollisionWorld* collisionWorld)
|
||||
public:
|
||||
Stepper(const btCollisionWorld *colWorld, const btCollisionObject *colObj)
|
||||
: mColWorld(colWorld)
|
||||
, mColObj(colObj)
|
||||
, mHaveMoved(true)
|
||||
{}
|
||||
|
||||
bool step(osg::Vec3f &position, const osg::Vec3f &toMove, float &remainingTime)
|
||||
{
|
||||
/*
|
||||
* Slide up an incline or set of stairs. Should be called only after a
|
||||
|
@ -123,12 +140,14 @@ namespace MWPhysics
|
|||
* +--+ +--------
|
||||
* ==============================================
|
||||
*/
|
||||
ActorTracer tracer, stepper;
|
||||
|
||||
stepper.doTrace(colobj, position, position+osg::Vec3f(0.0f,0.0f,sStepSizeUp), collisionWorld);
|
||||
if(stepper.mFraction < std::numeric_limits<float>::epsilon())
|
||||
return Result_Blocked; // didn't even move the smallest representable amount
|
||||
// (TODO: shouldn't this be larger? Why bother with such a small amount?)
|
||||
if (mHaveMoved)
|
||||
{
|
||||
mHaveMoved = false;
|
||||
mUpStepper.doTrace(mColObj, position, position+osg::Vec3f(0.0f,0.0f,sStepSizeUp), mColWorld);
|
||||
if(mUpStepper.mFraction < std::numeric_limits<float>::epsilon())
|
||||
return false; // didn't even move the smallest representable amount
|
||||
// (TODO: shouldn't this be larger? Why bother with such a small amount?)
|
||||
}
|
||||
|
||||
/*
|
||||
* Try moving from the elevated position using tracer.
|
||||
|
@ -143,9 +162,10 @@ namespace MWPhysics
|
|||
* +--+
|
||||
* ==============================================
|
||||
*/
|
||||
tracer.doTrace(colobj, stepper.mEndPos, stepper.mEndPos + toMove, collisionWorld);
|
||||
if(tracer.mFraction < std::numeric_limits<float>::epsilon())
|
||||
return Result_Blocked; // didn't even move the smallest representable amount
|
||||
osg::Vec3f tracerPos = mUpStepper.mEndPos;
|
||||
mTracer.doTrace(mColObj, tracerPos, tracerPos + toMove, mColWorld);
|
||||
if(mTracer.mFraction < std::numeric_limits<float>::epsilon())
|
||||
return false; // didn't even move the smallest representable amount
|
||||
|
||||
/*
|
||||
* Try moving back down sStepSizeDown using stepper.
|
||||
|
@ -162,26 +182,40 @@ namespace MWPhysics
|
|||
* +--+ +--+
|
||||
* ==============================================
|
||||
*/
|
||||
stepper.doTrace(colobj, tracer.mEndPos, tracer.mEndPos-osg::Vec3f(0.0f,0.0f,sStepSizeDown), collisionWorld);
|
||||
if (getSlope(stepper.mPlaneNormal) > sMaxSlope)
|
||||
return Result_MaxSlope;
|
||||
if(stepper.mFraction < 1.0f)
|
||||
mDownStepper.doTrace(mColObj, mTracer.mEndPos, mTracer.mEndPos-osg::Vec3f(0.0f,0.0f,sStepSizeDown), mColWorld);
|
||||
if (!canStepDown(mDownStepper))
|
||||
{
|
||||
// Try again with increased step length
|
||||
if (mTracer.mFraction < 1.0f || toMove.length2() > sMinStep*sMinStep)
|
||||
return false;
|
||||
|
||||
osg::Vec3f direction = toMove;
|
||||
direction.normalize();
|
||||
mTracer.doTrace(mColObj, tracerPos, tracerPos + direction*sMinStep, mColWorld);
|
||||
if (mTracer.mFraction < 0.001f)
|
||||
return false;
|
||||
|
||||
mDownStepper.doTrace(mColObj, mTracer.mEndPos, mTracer.mEndPos-osg::Vec3f(0.0f,0.0f,sStepSizeDown), mColWorld);
|
||||
if (!canStepDown(mDownStepper))
|
||||
return false;
|
||||
}
|
||||
if (mDownStepper.mFraction < 1.0f)
|
||||
{
|
||||
// don't allow stepping up other actors
|
||||
if (stepper.mHitObject->getBroadphaseHandle()->m_collisionFilterGroup == CollisionType_Actor)
|
||||
return Result_Blocked;
|
||||
// only step down onto semi-horizontal surfaces. don't step down onto the side of a house or a wall.
|
||||
// TODO: stepper.mPlaneNormal does not appear to be reliable - needs more testing
|
||||
// NOTE: caller's variables 'position' & 'remainingTime' are modified here
|
||||
position = stepper.mEndPos;
|
||||
remainingTime *= (1.0f-tracer.mFraction); // remaining time is proportional to remaining distance
|
||||
return Result_Success;
|
||||
position = mDownStepper.mEndPos;
|
||||
remainingTime *= (1.0f-mTracer.mFraction); // remaining time is proportional to remaining distance
|
||||
mHaveMoved = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
return Result_Blocked;
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class MovementSolver
|
||||
{
|
||||
private:
|
||||
///Project a vector u on another vector v
|
||||
static inline osg::Vec3f project(const osg::Vec3f& u, const osg::Vec3f &v)
|
||||
{
|
||||
|
@ -229,14 +263,14 @@ namespace MWPhysics
|
|||
collisionWorld->rayTest(from, to, resultCallback1);
|
||||
|
||||
if (resultCallback1.hasHit() &&
|
||||
( (toOsg(resultCallback1.m_hitPointWorld) - tracer.mEndPos).length() > 35
|
||||
|| getSlope(tracer.mPlaneNormal) > sMaxSlope))
|
||||
( (toOsg(resultCallback1.m_hitPointWorld) - tracer.mEndPos).length2() > 35*35
|
||||
|| !isWalkableSlope(tracer.mPlaneNormal)))
|
||||
{
|
||||
actor->setOnGround(getSlope(toOsg(resultCallback1.m_hitNormalWorld)) <= sMaxSlope);
|
||||
actor->setOnGround(isWalkableSlope(resultCallback1.m_hitNormalWorld));
|
||||
return toOsg(resultCallback1.m_hitPointWorld) + osg::Vec3f(0.f, 0.f, 1.f);
|
||||
}
|
||||
|
||||
actor->setOnGround(getSlope(tracer.mPlaneNormal) <= sMaxSlope);
|
||||
actor->setOnGround(isWalkableSlope(tracer.mPlaneNormal));
|
||||
|
||||
return tracer.mEndPos;
|
||||
}
|
||||
|
@ -312,8 +346,8 @@ namespace MWPhysics
|
|||
velocity *= 1.f-(fStromWalkMult * (angleDegrees/180.f));
|
||||
}
|
||||
|
||||
Stepper stepper(collisionWorld, colobj);
|
||||
osg::Vec3f origVelocity = velocity;
|
||||
|
||||
osg::Vec3f newPosition = position;
|
||||
/*
|
||||
* A loop to find newPosition using tracer, if successful different from the starting position.
|
||||
|
@ -332,10 +366,7 @@ namespace MWPhysics
|
|||
newPosition.z() <= swimlevel)
|
||||
{
|
||||
const osg::Vec3f down(0,0,-1);
|
||||
float movelen = velocity.normalize();
|
||||
osg::Vec3f reflectdir = reflect(velocity, down);
|
||||
reflectdir.normalize();
|
||||
velocity = slide(reflectdir, down)*movelen;
|
||||
velocity = slide(velocity, down);
|
||||
// NOTE: remainingTime is unchanged before the loop continues
|
||||
continue; // velocity updated, calculate nextpos again
|
||||
}
|
||||
|
@ -364,19 +395,25 @@ namespace MWPhysics
|
|||
break;
|
||||
}
|
||||
|
||||
|
||||
osg::Vec3f oldPosition = newPosition;
|
||||
// We hit something. Try to step up onto it. (NOTE: stepMove does not allow stepping over)
|
||||
// NOTE: stepMove modifies newPosition if successful
|
||||
const float minStep = 10.f;
|
||||
StepMoveResult result = stepMove(colobj, newPosition, velocity*remainingTime, remainingTime, collisionWorld);
|
||||
if (result == Result_MaxSlope && (velocity*remainingTime).length() < minStep) // to make sure the maximum stepping distance isn't framerate-dependent or movement-speed dependent
|
||||
// We are touching something.
|
||||
if (tracer.mFraction < 1E-9f)
|
||||
{
|
||||
osg::Vec3f normalizedVelocity = velocity;
|
||||
normalizedVelocity.normalize();
|
||||
result = stepMove(colobj, newPosition, normalizedVelocity*minStep, remainingTime, collisionWorld);
|
||||
// Try to separate by backing off slighly to unstuck the solver
|
||||
const osg::Vec3f backOff = (newPosition - tracer.mHitPoint) * 1E-3f;
|
||||
newPosition += backOff;
|
||||
}
|
||||
if(result == Result_Success)
|
||||
|
||||
// We hit something. Check if we can step up.
|
||||
float hitHeight = tracer.mHitPoint.z() - tracer.mEndPos.z() + halfExtents.z();
|
||||
osg::Vec3f oldPosition = newPosition;
|
||||
bool result = false;
|
||||
if (hitHeight < sStepSizeUp && !isActor(tracer.mHitObject))
|
||||
{
|
||||
// Try to step up onto it.
|
||||
// NOTE: stepMove does not allow stepping over, modifies newPosition if successful
|
||||
result = stepper.step(newPosition, velocity*remainingTime, remainingTime);
|
||||
}
|
||||
if (result)
|
||||
{
|
||||
// don't let pure water creatures move out of water after stepMove
|
||||
if (ptr.getClass().isPureWaterCreature(ptr)
|
||||
|
@ -386,23 +423,19 @@ namespace MWPhysics
|
|||
else
|
||||
{
|
||||
// Can't move this way, try to find another spot along the plane
|
||||
osg::Vec3f direction = velocity;
|
||||
float movelen = direction.normalize();
|
||||
osg::Vec3f reflectdir = reflect(velocity, tracer.mPlaneNormal);
|
||||
reflectdir.normalize();
|
||||
osg::Vec3f newVelocity = slide(velocity, tracer.mPlaneNormal);
|
||||
|
||||
// Do not allow sliding upward if there is gravity.
|
||||
// Stepping will have taken care of that.
|
||||
if(!(newPosition.z() < swimlevel || isFlying))
|
||||
newVelocity.z() = std::min(newVelocity.z(), 0.0f);
|
||||
|
||||
osg::Vec3f newVelocity = slide(reflectdir, tracer.mPlaneNormal)*movelen;
|
||||
if ((newVelocity-velocity).length2() < 0.01)
|
||||
break;
|
||||
if ((velocity * origVelocity) <= 0.f)
|
||||
if ((newVelocity * origVelocity) <= 0.f)
|
||||
break; // ^ dot product
|
||||
|
||||
velocity = newVelocity;
|
||||
|
||||
// Do not allow sliding upward if there is gravity. Stepping will have taken
|
||||
// care of that.
|
||||
if(!(newPosition.z() < swimlevel || isFlying))
|
||||
velocity.z() = std::min(velocity.z(), 0.0f);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -413,7 +446,7 @@ namespace MWPhysics
|
|||
osg::Vec3f to = newPosition - (physicActor->getOnGround() ?
|
||||
osg::Vec3f(0,0,sStepSizeDown+2.f) : osg::Vec3f(0,0,2.f));
|
||||
tracer.doTrace(colobj, from, to, collisionWorld);
|
||||
if(tracer.mFraction < 1.0f && getSlope(tracer.mPlaneNormal) <= sMaxSlope
|
||||
if(tracer.mFraction < 1.0f && isWalkableSlope(tracer.mPlaneNormal)
|
||||
&& tracer.mHitObject->getBroadphaseHandle()->m_collisionFilterGroup != CollisionType_Actor)
|
||||
{
|
||||
const btCollisionObject* standingOn = tracer.mHitObject;
|
||||
|
|
|
@ -78,6 +78,7 @@ void ActorTracer::doTrace(const btCollisionObject *actor, const osg::Vec3f& star
|
|||
mFraction = newTraceCallback.m_closestHitFraction;
|
||||
mPlaneNormal = osg::Vec3f(tracehitnormal.x(), tracehitnormal.y(), tracehitnormal.z());
|
||||
mEndPos = (end-start)*mFraction + start;
|
||||
mHitPoint = toOsg(newTraceCallback.m_hitPointWorld);
|
||||
mHitObject = newTraceCallback.m_hitCollisionObject;
|
||||
}
|
||||
else
|
||||
|
@ -85,6 +86,7 @@ void ActorTracer::doTrace(const btCollisionObject *actor, const osg::Vec3f& star
|
|||
mEndPos = end;
|
||||
mPlaneNormal = osg::Vec3f(0.0f, 0.0f, 1.0f);
|
||||
mFraction = 1.0f;
|
||||
mHitPoint = end;
|
||||
mHitObject = NULL;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ namespace MWPhysics
|
|||
{
|
||||
osg::Vec3f mEndPos;
|
||||
osg::Vec3f mPlaneNormal;
|
||||
osg::Vec3f mHitPoint;
|
||||
const btCollisionObject* mHitObject;
|
||||
|
||||
float mFraction;
|
||||
|
|
Loading…
Reference in a new issue