@ -60,30 +60,41 @@ namespace MWPhysics
// 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 bool isActor ( const btCollisionObject * obj )
{
assert ( obj ) ;
return obj - > getBroadphaseHandle ( ) - > m_collisionFilterGroup = = CollisionType_Actor ;
}
const btCollisionWorld * mColWorld ;
const btCollisionObject * mColObj ;
template < class Vec3 >
static bool isWalkableSlope ( const Vec3 & normal )
{
static const float sMaxSlopeCos = std : : cos ( osg : : DegreesToRadians ( sMaxSlope ) ) ;
return ( normal . z ( ) > sMaxSlopeCos ) ;
}
ActorTracer mTracer , mUpStepper , mDownStepper ;
bool mHaveMoved ;
static bool canStepDown ( const ActorTracer & stepper )
{
return stepper . mHitObject & & isWalkableSlope ( stepper . mPlaneNormal ) & & ! isActor ( stepper . mHitObject ) ;
}
public :
Stepper ( const btCollisionWorld * colWorld , const btCollisionObject * colObj )
: mColWorld ( colWorld )
, mColObj ( colObj )
, mHaveMoved ( true )
{ }
static bool stepMove ( const btCollisionObject * colobj , osg : : Vec3f & position ,
const osg : : Vec3f & toMove , float & remainingTime , const btCollisionWorld * collisionWorld )
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
@ -129,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 false ; // 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 .
@ -149,9 +162,9 @@ namespace MWPhysics
* + - - +
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
*/
osg : : Vec3f tracerPos = s tepper. mEndPos ;
tracer. doTrace ( colobj , tracerPos , tracerPos + toMove , collision World) ;
if ( t racer. mFraction < std : : numeric_limits < float > : : epsilon ( ) )
osg : : Vec3f tracerPos = mUpS tepper. mEndPos ;
mTracer. doTrace ( mColObj , tracerPos , tracerPos + toMove , mCol World) ;
if ( mT racer. mFraction < std : : numeric_limits < float > : : epsilon ( ) )
return false ; // didn't even move the smallest representable amount
/*
@ -169,36 +182,40 @@ namespace MWPhysics
* + - - + + - - +
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
*/
stepper. doTrace ( colobj , tracer . mEndPos , t racer. mEndPos - osg : : Vec3f ( 0.0f , 0.0f , sStepSizeDown ) , collision World) ;
if ( ! canStepDown ( s tepper) )
mDownStepper. doTrace ( mColObj , mTracer . mEndPos , mT racer. mEndPos - osg : : Vec3f ( 0.0f , 0.0f , sStepSizeDown ) , mCol World) ;
if ( ! canStepDown ( mDownS tepper) )
{
// Try again with increased step length
if ( t racer. mFraction < 1.0f | | toMove . length2 ( ) > sMinStep * sMinStep )
if ( mT racer. mFraction < 1.0f | | toMove . length2 ( ) > sMinStep * sMinStep )
return false ;
osg : : Vec3f direction = toMove ;
direction . normalize ( ) ;
tracer. doTrace ( colo bj, tracerPos , tracerPos + direction * sMinStep , collision World) ;
if ( t racer. mFraction < 0.001f )
mTracer. doTrace ( mColO bj, tracerPos , tracerPos + direction * sMinStep , mCol World) ;
if ( mT racer. mFraction < 0.001f )
return false ;
stepper. doTrace ( colobj , tracer . mEndPos , t racer. mEndPos - osg : : Vec3f ( 0.0f , 0.0f , sStepSizeDown ) , collision World) ;
if ( ! canStepDown ( s tepper) )
mDownStepper. doTrace ( mColObj , mTracer . mEndPos , mT racer. mEndPos - osg : : Vec3f ( 0.0f , 0.0f , sStepSizeDown ) , mCol World) ;
if ( ! canStepDown ( mDownS tepper) )
return false ;
}
if ( s tepper. mFraction < 1.0f )
if ( mDownS tepper. mFraction < 1.0f )
{
// 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
position = mDownStepper . mEndPos ;
remainingTime * = ( 1.0f - mTracer . mFraction ) ; // remaining time is proportional to remaining distance
mHaveMoved = true ;
return true ;
}
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 )
{
@ -329,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 .
@ -385,7 +402,7 @@ namespace MWPhysics
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
bool result = step Move( colobj , newPosition , velocity * remainingTime , remainingTime , collisionWorld ) ;
bool result = step per. step ( newPosition , velocity * remainingTime , remainingTime ) ;
if ( result )
{
// don't let pure water creatures move out of water after stepMove