mirror of
https://github.com/overte-org/overte.git
synced 2025-07-10 21:18:54 +02:00
remove kinematic character controller implemention
This commit is contained in:
parent
0aa579225c
commit
d34b667e82
5 changed files with 0 additions and 515 deletions
|
@ -201,158 +201,6 @@ bool MyCharacterController::testRayShotgun(const glm::vec3& position, const glm:
|
||||||
return result.hitFraction < 1.0f;
|
return result.hitFraction < 1.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
glm::vec3 MyCharacterController::computeHMDStep(const glm::vec3& position, const glm::vec3& step) {
|
|
||||||
btVector3 stepDirection = glmToBullet(step);
|
|
||||||
btScalar stepLength = stepDirection.length();
|
|
||||||
if (stepLength < FLT_EPSILON) {
|
|
||||||
return glm::vec3(0.0f);
|
|
||||||
}
|
|
||||||
stepDirection /= stepLength;
|
|
||||||
|
|
||||||
// get _ghost ready for ray traces
|
|
||||||
btTransform transform = _rigidBody->getWorldTransform();
|
|
||||||
btVector3 newPosition = glmToBullet(position);
|
|
||||||
transform.setOrigin(newPosition);
|
|
||||||
btMatrix3x3 rotation = transform.getBasis();
|
|
||||||
_ghost.setWorldTransform(transform);
|
|
||||||
_ghost.refreshOverlappingPairCache();
|
|
||||||
|
|
||||||
// compute rotation that will orient local ray start points to face stepDirection
|
|
||||||
btVector3 forward = rotation * btVector3(0.0f, 0.0f, -1.0f);
|
|
||||||
btVector3 horizontalDirection = stepDirection - stepDirection.dot(_currentUp) * _currentUp;
|
|
||||||
btVector3 axis = forward.cross(horizontalDirection);
|
|
||||||
btScalar lengthAxis = axis.length();
|
|
||||||
if (lengthAxis > FLT_EPSILON) {
|
|
||||||
// non-zero sideways component
|
|
||||||
btScalar angle = asinf(lengthAxis / horizontalDirection.length());
|
|
||||||
if (stepDirection.dot(forward) < 0.0f) {
|
|
||||||
angle = PI - angle;
|
|
||||||
}
|
|
||||||
axis /= lengthAxis;
|
|
||||||
rotation = btMatrix3x3(btQuaternion(axis, angle)) * rotation;
|
|
||||||
} else if (stepDirection.dot(forward) < 0.0f) {
|
|
||||||
// backwards
|
|
||||||
rotation = btMatrix3x3(btQuaternion(_currentUp, PI)) * rotation;
|
|
||||||
}
|
|
||||||
|
|
||||||
CharacterRayResult rayResult(&_ghost);
|
|
||||||
btVector3 rayStart;
|
|
||||||
btVector3 rayEnd;
|
|
||||||
btVector3 penetration = btVector3(0.0f, 0.0f, 0.0f);
|
|
||||||
int32_t numPenetrations = 0;
|
|
||||||
|
|
||||||
{ // first we scan straight out from capsule center to see if we're stuck on anything
|
|
||||||
btScalar forwardRatio = 0.5f;
|
|
||||||
btScalar backRatio = 0.25f;
|
|
||||||
|
|
||||||
btVector3 radial;
|
|
||||||
bool stuck = false;
|
|
||||||
for (int32_t i = 0; i < _topPoints.size(); ++i) {
|
|
||||||
rayStart = rotation * _topPoints[i];
|
|
||||||
radial = rayStart - rayStart.dot(_currentUp) * _currentUp;
|
|
||||||
rayEnd = newPosition + rayStart + forwardRatio * radial;
|
|
||||||
rayStart += newPosition - backRatio * radial;
|
|
||||||
|
|
||||||
// reset rayResult for next test
|
|
||||||
rayResult.m_closestHitFraction = 1.0f;
|
|
||||||
rayResult.m_collisionObject = nullptr;
|
|
||||||
|
|
||||||
if (_ghost.rayTest(rayStart, rayEnd, rayResult)) {
|
|
||||||
btScalar totalRatio = backRatio + forwardRatio;
|
|
||||||
btScalar adjustedHitFraction = (rayResult.m_closestHitFraction * totalRatio - backRatio) / forwardRatio;
|
|
||||||
if (adjustedHitFraction < 0.0f) {
|
|
||||||
penetration += adjustedHitFraction * radial;
|
|
||||||
++numPenetrations;
|
|
||||||
} else {
|
|
||||||
stuck = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (numPenetrations > 0) {
|
|
||||||
if (numPenetrations > 1) {
|
|
||||||
penetration /= (btScalar)numPenetrations;
|
|
||||||
}
|
|
||||||
return bulletToGLM(penetration);
|
|
||||||
} else if (stuck) {
|
|
||||||
return glm::vec3(0.0f);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// if we get here then we're not stuck pushing into any surface
|
|
||||||
// so now we scan to see if the way before us is "walkable"
|
|
||||||
|
|
||||||
// scan the top
|
|
||||||
// NOTE: if we scan an extra distance forward we can detect flat surfaces that are too steep to walk on.
|
|
||||||
// The approximate extra distance can be derived with trigonometry.
|
|
||||||
//
|
|
||||||
// minimumForward = [ (maxStepHeight + radius / cosTheta - radius) * (cosTheta / sinTheta) - radius ]
|
|
||||||
//
|
|
||||||
// where: theta = max angle between floor normal and vertical
|
|
||||||
//
|
|
||||||
// if stepLength is not long enough we can add the difference.
|
|
||||||
//
|
|
||||||
btScalar cosTheta = _minFloorNormalDotUp;
|
|
||||||
btScalar sinTheta = sqrtf(1.0f - cosTheta * cosTheta);
|
|
||||||
const btScalar MIN_FORWARD_SLOP = 0.10f; // HACK: not sure why this is necessary to detect steepest walkable slope
|
|
||||||
btScalar forwardSlop = (_maxStepHeight + _radius / cosTheta - _radius) * (cosTheta / sinTheta) - (_radius + stepLength) + MIN_FORWARD_SLOP;
|
|
||||||
if (forwardSlop < 0.0f) {
|
|
||||||
// BIG step, no slop necessary
|
|
||||||
forwardSlop = 0.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
// we push the step forward by stepMargin to help reduce accidental penetration
|
|
||||||
const btScalar MIN_STEP_MARGIN = 0.04f;
|
|
||||||
btScalar stepMargin = glm::max(_radius, MIN_STEP_MARGIN);
|
|
||||||
btScalar expandedStepLength = stepLength + forwardSlop + stepMargin;
|
|
||||||
|
|
||||||
// loop over topPoints
|
|
||||||
bool walkable = true;
|
|
||||||
for (int32_t i = 0; i < _topPoints.size(); ++i) {
|
|
||||||
rayStart = newPosition + rotation * _topPoints[i];
|
|
||||||
rayEnd = rayStart + expandedStepLength * stepDirection;
|
|
||||||
|
|
||||||
// reset rayResult for next test
|
|
||||||
rayResult.m_closestHitFraction = 1.0f;
|
|
||||||
rayResult.m_collisionObject = nullptr;
|
|
||||||
|
|
||||||
if (_ghost.rayTest(rayStart, rayEnd, rayResult)) {
|
|
||||||
if (rayResult.m_hitNormalWorld.dot(_currentUp) < _minFloorNormalDotUp) {
|
|
||||||
walkable = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// scan the bottom
|
|
||||||
// TODO: implement sliding along sloped floors
|
|
||||||
bool steppingUp = false;
|
|
||||||
expandedStepLength = stepLength + MIN_FORWARD_SLOP + MIN_STEP_MARGIN;
|
|
||||||
for (int32_t i = _bottomPoints.size() - 1; i > -1; --i) {
|
|
||||||
rayStart = newPosition + rotation * _bottomPoints[i] - MIN_STEP_MARGIN * stepDirection;
|
|
||||||
rayEnd = rayStart + expandedStepLength * stepDirection;
|
|
||||||
|
|
||||||
// reset rayResult for next test
|
|
||||||
rayResult.m_closestHitFraction = 1.0f;
|
|
||||||
rayResult.m_collisionObject = nullptr;
|
|
||||||
|
|
||||||
if (_ghost.rayTest(rayStart, rayEnd, rayResult)) {
|
|
||||||
btScalar adjustedHitFraction = (rayResult.m_closestHitFraction * expandedStepLength - MIN_STEP_MARGIN) / (stepLength + MIN_FORWARD_SLOP);
|
|
||||||
if (adjustedHitFraction < 1.0f) {
|
|
||||||
steppingUp = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!walkable && steppingUp ) {
|
|
||||||
return glm::vec3(0.0f);
|
|
||||||
}
|
|
||||||
// else it might not be walkable, but we aren't steppingUp yet which means we can still move forward
|
|
||||||
|
|
||||||
// TODO: slide up ramps and fall off edges (then we can remove the vertical follow of Avatar's RigidBody)
|
|
||||||
return step;
|
|
||||||
}
|
|
||||||
|
|
||||||
btConvexHullShape* MyCharacterController::computeShape() const {
|
btConvexHullShape* MyCharacterController::computeShape() const {
|
||||||
// HACK: the avatar collides using convex hull with a collision margin equal to
|
// HACK: the avatar collides using convex hull with a collision margin equal to
|
||||||
// the old capsule radius. Two points define a capsule and additional points are
|
// the old capsule radius. Two points define a capsule and additional points are
|
||||||
|
|
|
@ -40,8 +40,6 @@ public:
|
||||||
/// return true if RayShotgun hits anything
|
/// return true if RayShotgun hits anything
|
||||||
bool testRayShotgun(const glm::vec3& position, const glm::vec3& step, RayShotgunResult& result);
|
bool testRayShotgun(const glm::vec3& position, const glm::vec3& step, RayShotgunResult& result);
|
||||||
|
|
||||||
glm::vec3 computeHMDStep(const glm::vec3& position, const glm::vec3& step);
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void initRayShotgun(const btCollisionWorld* world);
|
void initRayShotgun(const btCollisionWorld* world);
|
||||||
|
|
||||||
|
|
|
@ -126,10 +126,6 @@ void CharacterController::setDynamicsWorld(btDynamicsWorld* world) {
|
||||||
_ghost.setCollisionGroupAndMask(_collisionGroup, BULLET_COLLISION_MASK_MY_AVATAR & (~ _collisionGroup));
|
_ghost.setCollisionGroupAndMask(_collisionGroup, BULLET_COLLISION_MASK_MY_AVATAR & (~ _collisionGroup));
|
||||||
_ghost.setCollisionWorld(_dynamicsWorld);
|
_ghost.setCollisionWorld(_dynamicsWorld);
|
||||||
_ghost.setRadiusAndHalfHeight(_radius, _halfHeight);
|
_ghost.setRadiusAndHalfHeight(_radius, _halfHeight);
|
||||||
_ghost.setMaxStepHeight(0.75f * (_radius + _halfHeight));
|
|
||||||
_ghost.setMinWallAngle(PI / 4.0f);
|
|
||||||
_ghost.setUpDirection(_currentUp);
|
|
||||||
_ghost.setMotorOnly(true);
|
|
||||||
_ghost.setWorldTransform(_rigidBody->getWorldTransform());
|
_ghost.setWorldTransform(_rigidBody->getWorldTransform());
|
||||||
}
|
}
|
||||||
if (_dynamicsWorld) {
|
if (_dynamicsWorld) {
|
||||||
|
|
|
@ -45,22 +45,6 @@ void CharacterGhostObject::setRadiusAndHalfHeight(btScalar radius, btScalar half
|
||||||
_halfHeight = halfHeight;
|
_halfHeight = halfHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CharacterGhostObject::setUpDirection(const btVector3& up) {
|
|
||||||
btScalar length = up.length();
|
|
||||||
if (length > FLT_EPSILON) {
|
|
||||||
_upDirection /= length;
|
|
||||||
} else {
|
|
||||||
_upDirection = btVector3(0.0f, 1.0f, 0.0f);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CharacterGhostObject::setMotorVelocity(const btVector3& velocity) {
|
|
||||||
_motorVelocity = velocity;
|
|
||||||
if (_motorOnly) {
|
|
||||||
_linearVelocity = _motorVelocity;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// override of btCollisionObject::setCollisionShape()
|
// override of btCollisionObject::setCollisionShape()
|
||||||
void CharacterGhostObject::setCharacterShape(btConvexHullShape* shape) {
|
void CharacterGhostObject::setCharacterShape(btConvexHullShape* shape) {
|
||||||
assert(shape);
|
assert(shape);
|
||||||
|
@ -81,164 +65,6 @@ void CharacterGhostObject::setCollisionWorld(btCollisionWorld* world) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CharacterGhostObject::move(btScalar dt, btScalar overshoot, btScalar gravity) {
|
|
||||||
bool oldOnFloor = _onFloor;
|
|
||||||
_onFloor = false;
|
|
||||||
_steppingUp = false;
|
|
||||||
assert(_world && _inWorld);
|
|
||||||
updateVelocity(dt, gravity);
|
|
||||||
|
|
||||||
// resolve any penetrations before sweeping
|
|
||||||
int32_t MAX_LOOPS = 4;
|
|
||||||
int32_t numExtractions = 0;
|
|
||||||
btVector3 totalPosition(0.0f, 0.0f, 0.0f);
|
|
||||||
while (numExtractions < MAX_LOOPS) {
|
|
||||||
if (resolvePenetration(numExtractions)) {
|
|
||||||
numExtractions = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
totalPosition += getWorldTransform().getOrigin();
|
|
||||||
++numExtractions;
|
|
||||||
}
|
|
||||||
if (numExtractions > 1) {
|
|
||||||
// penetration resolution was probably oscillating between opposing objects
|
|
||||||
// so we use the average of the solutions
|
|
||||||
totalPosition /= btScalar(numExtractions);
|
|
||||||
btTransform transform = getWorldTransform();
|
|
||||||
transform.setOrigin(totalPosition);
|
|
||||||
setWorldTransform(transform);
|
|
||||||
|
|
||||||
// TODO: figure out how to untrap character
|
|
||||||
}
|
|
||||||
btTransform startTransform = getWorldTransform();
|
|
||||||
btVector3 startPosition = startTransform.getOrigin();
|
|
||||||
if (_onFloor) {
|
|
||||||
// resolvePenetration() pushed the avatar out of a floor so
|
|
||||||
// we must updateTraction() before using _linearVelocity
|
|
||||||
updateTraction(startPosition);
|
|
||||||
}
|
|
||||||
|
|
||||||
btScalar speed = _linearVelocity.length();
|
|
||||||
btVector3 forwardSweep = dt * _linearVelocity;
|
|
||||||
btScalar stepDistance = dt * speed;
|
|
||||||
btScalar MIN_SWEEP_DISTANCE = 0.0001f;
|
|
||||||
if (stepDistance < MIN_SWEEP_DISTANCE) {
|
|
||||||
// not moving, no need to sweep
|
|
||||||
updateTraction(startPosition);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// augment forwardSweep to help slow moving sweeps get over steppable ledges
|
|
||||||
const btScalar MIN_OVERSHOOT = 0.04f; // default margin
|
|
||||||
if (overshoot < MIN_OVERSHOOT) {
|
|
||||||
overshoot = MIN_OVERSHOOT;
|
|
||||||
}
|
|
||||||
btScalar longSweepDistance = stepDistance + overshoot;
|
|
||||||
forwardSweep *= longSweepDistance / stepDistance;
|
|
||||||
|
|
||||||
// step forward
|
|
||||||
CharacterSweepResult result(this);
|
|
||||||
btTransform nextTransform = startTransform;
|
|
||||||
nextTransform.setOrigin(startPosition + forwardSweep);
|
|
||||||
sweepTest(_characterShape, startTransform, nextTransform, result); // forward
|
|
||||||
|
|
||||||
if (!result.hasHit()) {
|
|
||||||
nextTransform.setOrigin(startPosition + (stepDistance / longSweepDistance) * forwardSweep);
|
|
||||||
setWorldTransform(nextTransform);
|
|
||||||
updateTraction(nextTransform.getOrigin());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
bool verticalOnly = btFabs(btFabs(_linearVelocity.dot(_upDirection)) - speed) < MIN_OVERSHOOT;
|
|
||||||
if (verticalOnly) {
|
|
||||||
// no need to step
|
|
||||||
nextTransform.setOrigin(startPosition + (result.m_closestHitFraction * stepDistance / longSweepDistance) * forwardSweep);
|
|
||||||
setWorldTransform(nextTransform);
|
|
||||||
|
|
||||||
if (result.m_hitNormalWorld.dot(_upDirection) > _maxWallNormalUpComponent) {
|
|
||||||
_floorNormal = result.m_hitNormalWorld;
|
|
||||||
_floorContact = result.m_hitPointWorld;
|
|
||||||
_steppingUp = false;
|
|
||||||
_onFloor = true;
|
|
||||||
_hovering = false;
|
|
||||||
}
|
|
||||||
updateTraction(nextTransform.getOrigin());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if this hit is obviously unsteppable
|
|
||||||
btVector3 hitFromBase = result.m_hitPointWorld - (startPosition - ((_radius + _halfHeight) * _upDirection));
|
|
||||||
btScalar hitHeight = hitFromBase.dot(_upDirection);
|
|
||||||
if (hitHeight > _maxStepHeight) {
|
|
||||||
// shape can't step over the obstacle so move forward as much as possible before we bail
|
|
||||||
btVector3 forwardTranslation = result.m_closestHitFraction * forwardSweep;
|
|
||||||
btScalar forwardDistance = forwardTranslation.length();
|
|
||||||
if (forwardDistance > stepDistance) {
|
|
||||||
forwardTranslation *= stepDistance / forwardDistance;
|
|
||||||
}
|
|
||||||
nextTransform.setOrigin(startPosition + forwardTranslation);
|
|
||||||
setWorldTransform(nextTransform);
|
|
||||||
_onFloor = _onFloor || oldOnFloor;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// if we get here then we hit something that might be steppable
|
|
||||||
|
|
||||||
// remember the forward sweep hit fraction for later
|
|
||||||
btScalar forwardSweepHitFraction = result.m_closestHitFraction;
|
|
||||||
|
|
||||||
// figure out how high we can step up
|
|
||||||
btScalar availableStepHeight = measureAvailableStepHeight();
|
|
||||||
|
|
||||||
// raise by availableStepHeight before sweeping forward
|
|
||||||
result.resetHitHistory();
|
|
||||||
startTransform.setOrigin(startPosition + availableStepHeight * _upDirection);
|
|
||||||
nextTransform.setOrigin(startTransform.getOrigin() + forwardSweep);
|
|
||||||
sweepTest(_characterShape, startTransform, nextTransform, result);
|
|
||||||
if (result.hasHit()) {
|
|
||||||
startTransform.setOrigin(startTransform.getOrigin() + result.m_closestHitFraction * forwardSweep);
|
|
||||||
} else {
|
|
||||||
startTransform = nextTransform;
|
|
||||||
}
|
|
||||||
|
|
||||||
// sweep down in search of future landing spot
|
|
||||||
result.resetHitHistory();
|
|
||||||
btVector3 downSweep = (- availableStepHeight) * _upDirection;
|
|
||||||
nextTransform.setOrigin(startTransform.getOrigin() + downSweep);
|
|
||||||
sweepTest(_characterShape, startTransform, nextTransform, result);
|
|
||||||
if (result.hasHit() && result.m_hitNormalWorld.dot(_upDirection) > _maxWallNormalUpComponent) {
|
|
||||||
// can stand on future landing spot, so we interpolate toward it
|
|
||||||
_floorNormal = result.m_hitNormalWorld;
|
|
||||||
_floorContact = result.m_hitPointWorld;
|
|
||||||
_steppingUp = true;
|
|
||||||
_onFloor = true;
|
|
||||||
_hovering = false;
|
|
||||||
nextTransform.setOrigin(startTransform.getOrigin() + result.m_closestHitFraction * downSweep);
|
|
||||||
btVector3 totalStep = nextTransform.getOrigin() - startPosition;
|
|
||||||
nextTransform.setOrigin(startPosition + (stepDistance / totalStep.length()) * totalStep);
|
|
||||||
updateTraction(nextTransform.getOrigin());
|
|
||||||
} else {
|
|
||||||
// either there is no future landing spot, or there is but we can't stand on it
|
|
||||||
// in any case: we go forward as much as possible
|
|
||||||
nextTransform.setOrigin(startPosition + forwardSweepHitFraction * (stepDistance / longSweepDistance) * forwardSweep);
|
|
||||||
_onFloor = _onFloor || oldOnFloor;
|
|
||||||
updateTraction(nextTransform.getOrigin());
|
|
||||||
}
|
|
||||||
setWorldTransform(nextTransform);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CharacterGhostObject::sweepTest(
|
|
||||||
const btConvexShape* shape,
|
|
||||||
const btTransform& start,
|
|
||||||
const btTransform& end,
|
|
||||||
CharacterSweepResult& result) const {
|
|
||||||
if (_world && _inWorld) {
|
|
||||||
assert(shape);
|
|
||||||
btScalar allowedPenetration = _world->getDispatchInfo().m_allowedCcdPenetration;
|
|
||||||
convexSweepTest(shape, start, end, result, allowedPenetration);
|
|
||||||
return result.hasHit();
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CharacterGhostObject::rayTest(const btVector3& start,
|
bool CharacterGhostObject::rayTest(const btVector3& start,
|
||||||
const btVector3& end,
|
const btVector3& end,
|
||||||
CharacterRayResult& result) const {
|
CharacterRayResult& result) const {
|
||||||
|
@ -248,82 +74,6 @@ bool CharacterGhostObject::rayTest(const btVector3& start,
|
||||||
return result.hasHit();
|
return result.hasHit();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CharacterGhostObject::measurePenetration(btVector3& minBoxOut, btVector3& maxBoxOut) {
|
|
||||||
// minBoxOut and maxBoxOut will be updated with penetration envelope.
|
|
||||||
// If one of the corner points is <0,0,0> then the penetration is resolvable in a single step,
|
|
||||||
// but if the space spanned by the two corners extends in both directions along at least one
|
|
||||||
// component then we the object is sandwiched between two opposing objects.
|
|
||||||
|
|
||||||
// We assume this object has just been moved to its current location, or else objects have been
|
|
||||||
// moved around it since the last step so we must update the overlapping pairs.
|
|
||||||
refreshOverlappingPairCache();
|
|
||||||
|
|
||||||
// compute collision details
|
|
||||||
btHashedOverlappingPairCache* pairCache = getOverlappingPairCache();
|
|
||||||
_world->getDispatcher()->dispatchAllCollisionPairs(pairCache, _world->getDispatchInfo(), _world->getDispatcher());
|
|
||||||
|
|
||||||
// loop over contact manifolds to compute the penetration box
|
|
||||||
minBoxOut = btVector3(0.0f, 0.0f, 0.0f);
|
|
||||||
maxBoxOut = btVector3(0.0f, 0.0f, 0.0f);
|
|
||||||
btManifoldArray manifoldArray;
|
|
||||||
|
|
||||||
int numPairs = pairCache->getNumOverlappingPairs();
|
|
||||||
for (int i = 0; i < numPairs; i++) {
|
|
||||||
manifoldArray.resize(0);
|
|
||||||
btBroadphasePair* collisionPair = &(pairCache->getOverlappingPairArray()[i]);
|
|
||||||
|
|
||||||
btCollisionObject* obj0 = static_cast<btCollisionObject*>(collisionPair->m_pProxy0->m_clientObject);
|
|
||||||
btCollisionObject* obj1 = static_cast<btCollisionObject*>(collisionPair->m_pProxy1->m_clientObject);
|
|
||||||
|
|
||||||
if ((obj0 && !obj0->hasContactResponse()) && (obj1 && !obj1->hasContactResponse())) {
|
|
||||||
// we know this probe has no contact response
|
|
||||||
// but neither does the other object so skip this manifold
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!collisionPair->m_algorithm) {
|
|
||||||
// null m_algorithm means the two shape types don't know how to collide!
|
|
||||||
// shouldn't fall in here but just in case
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
btScalar mostFloorPenetration = 0.0f;
|
|
||||||
collisionPair->m_algorithm->getAllContactManifolds(manifoldArray);
|
|
||||||
for (int j = 0; j < manifoldArray.size(); j++) {
|
|
||||||
btPersistentManifold* manifold = manifoldArray[j];
|
|
||||||
btScalar directionSign = (manifold->getBody0() == this) ? btScalar(1.0) : btScalar(-1.0);
|
|
||||||
for (int p = 0; p < manifold->getNumContacts(); p++) {
|
|
||||||
const btManifoldPoint& pt = manifold->getContactPoint(p);
|
|
||||||
if (pt.getDistance() > 0.0f) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// normal always points from object to character
|
|
||||||
btVector3 normal = directionSign * pt.m_normalWorldOnB;
|
|
||||||
|
|
||||||
btScalar penetrationDepth = pt.getDistance();
|
|
||||||
if (penetrationDepth < mostFloorPenetration) { // remember penetrationDepth is negative
|
|
||||||
btScalar normalDotUp = normal.dot(_upDirection);
|
|
||||||
if (normalDotUp > _maxWallNormalUpComponent) {
|
|
||||||
mostFloorPenetration = penetrationDepth;
|
|
||||||
_floorNormal = normal;
|
|
||||||
if (directionSign > 0.0f) {
|
|
||||||
_floorContact = pt.m_positionWorldOnA;
|
|
||||||
} else {
|
|
||||||
_floorContact = pt.m_positionWorldOnB;
|
|
||||||
}
|
|
||||||
_onFloor = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
btVector3 penetration = (-penetrationDepth) * normal;
|
|
||||||
minBoxOut.setMin(penetration);
|
|
||||||
maxBoxOut.setMax(penetration);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CharacterGhostObject::refreshOverlappingPairCache() {
|
void CharacterGhostObject::refreshOverlappingPairCache() {
|
||||||
assert(_world && _inWorld);
|
assert(_world && _inWorld);
|
||||||
btVector3 minAabb, maxAabb;
|
btVector3 minAabb, maxAabb;
|
||||||
|
@ -347,69 +97,3 @@ void CharacterGhostObject::addToWorld() {
|
||||||
_inWorld = true;
|
_inWorld = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CharacterGhostObject::resolvePenetration(int numTries) {
|
|
||||||
btVector3 minBox, maxBox;
|
|
||||||
measurePenetration(minBox, maxBox);
|
|
||||||
btVector3 restore = maxBox + minBox;
|
|
||||||
if (restore.length2() > 0.0f) {
|
|
||||||
btTransform transform = getWorldTransform();
|
|
||||||
transform.setOrigin(transform.getOrigin() + restore);
|
|
||||||
setWorldTransform(transform);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CharacterGhostObject::updateVelocity(btScalar dt, btScalar gravity) {
|
|
||||||
if (!_motorOnly) {
|
|
||||||
if (_hovering) {
|
|
||||||
_linearVelocity *= 0.999f; // HACK damping
|
|
||||||
} else {
|
|
||||||
_linearVelocity += (dt * gravity) * _upDirection;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CharacterGhostObject::updateHoverState(const btVector3& position) {
|
|
||||||
if (_onFloor) {
|
|
||||||
_hovering = false;
|
|
||||||
} else {
|
|
||||||
// cast a ray down looking for floor support
|
|
||||||
CharacterRayResult rayResult(this);
|
|
||||||
btScalar distanceToFeet = _radius + _halfHeight;
|
|
||||||
btScalar slop = 2.0f * getCollisionShape()->getMargin(); // slop to help ray start OUTSIDE the floor object
|
|
||||||
btVector3 startPos = position - ((distanceToFeet - slop) * _upDirection);
|
|
||||||
btVector3 endPos = startPos - (2.0f * distanceToFeet) * _upDirection;
|
|
||||||
rayTest(startPos, endPos, rayResult);
|
|
||||||
// we're hovering if the ray didn't hit anything or hit unstandable slope
|
|
||||||
_hovering = !rayResult.hasHit() || rayResult.m_hitNormalWorld.dot(_upDirection) < _maxWallNormalUpComponent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CharacterGhostObject::updateTraction(const btVector3& position) {
|
|
||||||
updateHoverState(position);
|
|
||||||
if (_hovering || _motorOnly) {
|
|
||||||
_linearVelocity = _motorVelocity;
|
|
||||||
} else if (_onFloor) {
|
|
||||||
// compute a velocity that swings the shape around the _floorContact
|
|
||||||
btVector3 leverArm = _floorContact - position;
|
|
||||||
btVector3 pathDirection = leverArm.cross(_motorVelocity.cross(leverArm));
|
|
||||||
btScalar pathLength = pathDirection.length();
|
|
||||||
if (pathLength > FLT_EPSILON) {
|
|
||||||
_linearVelocity = (_motorVelocity.length() / pathLength) * pathDirection;
|
|
||||||
} else {
|
|
||||||
_linearVelocity = btVector3(0.0f, 0.0f, 0.0f);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
btScalar CharacterGhostObject::measureAvailableStepHeight() const {
|
|
||||||
CharacterSweepResult result(this);
|
|
||||||
btTransform transform = getWorldTransform();
|
|
||||||
btTransform nextTransform = transform;
|
|
||||||
nextTransform.setOrigin(transform.getOrigin() + _maxStepHeight * _upDirection);
|
|
||||||
sweepTest(_characterShape, transform, nextTransform, result);
|
|
||||||
return result.m_closestHitFraction * _maxStepHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
|
@ -33,71 +33,30 @@ public:
|
||||||
|
|
||||||
void setRadiusAndHalfHeight(btScalar radius, btScalar halfHeight);
|
void setRadiusAndHalfHeight(btScalar radius, btScalar halfHeight);
|
||||||
void setUpDirection(const btVector3& up);
|
void setUpDirection(const btVector3& up);
|
||||||
void setMotorVelocity(const btVector3& velocity);
|
|
||||||
void setMinWallAngle(btScalar angle) { _maxWallNormalUpComponent = cosf(angle); }
|
|
||||||
void setMaxStepHeight(btScalar height) { _maxStepHeight = height; }
|
|
||||||
|
|
||||||
void setLinearVelocity(const btVector3& velocity) { _linearVelocity = velocity; }
|
|
||||||
const btVector3& getLinearVelocity() const { return _linearVelocity; }
|
|
||||||
|
|
||||||
void setCharacterShape(btConvexHullShape* shape);
|
void setCharacterShape(btConvexHullShape* shape);
|
||||||
|
|
||||||
void setCollisionWorld(btCollisionWorld* world);
|
void setCollisionWorld(btCollisionWorld* world);
|
||||||
|
|
||||||
void move(btScalar dt, btScalar overshoot, btScalar gravity);
|
|
||||||
|
|
||||||
bool sweepTest(const btConvexShape* shape,
|
|
||||||
const btTransform& start,
|
|
||||||
const btTransform& end,
|
|
||||||
CharacterSweepResult& result) const;
|
|
||||||
|
|
||||||
bool rayTest(const btVector3& start,
|
bool rayTest(const btVector3& start,
|
||||||
const btVector3& end,
|
const btVector3& end,
|
||||||
CharacterRayResult& result) const;
|
CharacterRayResult& result) const;
|
||||||
|
|
||||||
bool isHovering() const { return _hovering; }
|
|
||||||
void setHovering(bool hovering) { _hovering = hovering; }
|
|
||||||
void setMotorOnly(bool motorOnly) { _motorOnly = motorOnly; }
|
|
||||||
|
|
||||||
bool hasSupport() const { return _onFloor; }
|
|
||||||
bool isSteppingUp() const { return _steppingUp; }
|
|
||||||
const btVector3& getFloorNormal() const { return _floorNormal; }
|
|
||||||
|
|
||||||
void measurePenetration(btVector3& minBoxOut, btVector3& maxBoxOut);
|
|
||||||
void refreshOverlappingPairCache();
|
void refreshOverlappingPairCache();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void removeFromWorld();
|
void removeFromWorld();
|
||||||
void addToWorld();
|
void addToWorld();
|
||||||
|
|
||||||
bool resolvePenetration(int numTries);
|
|
||||||
void updateVelocity(btScalar dt, btScalar gravity);
|
|
||||||
void updateTraction(const btVector3& position);
|
|
||||||
btScalar measureAvailableStepHeight() const;
|
|
||||||
void updateHoverState(const btVector3& position);
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
btVector3 _upDirection { 0.0f, 1.0f, 0.0f }; // input, up in world-frame
|
|
||||||
btVector3 _motorVelocity { 0.0f, 0.0f, 0.0f }; // input, velocity character is trying to achieve
|
|
||||||
btVector3 _linearVelocity { 0.0f, 0.0f, 0.0f }; // internal, actual character velocity
|
|
||||||
btVector3 _floorNormal { 0.0f, 0.0f, 0.0f }; // internal, probable floor normal
|
|
||||||
btVector3 _floorContact { 0.0f, 0.0f, 0.0f }; // internal, last floor contact point
|
|
||||||
btCollisionWorld* _world { nullptr }; // input, pointer to world
|
btCollisionWorld* _world { nullptr }; // input, pointer to world
|
||||||
//btScalar _distanceToFeet { 0.0f }; // input, distance from object center to lowest point on shape
|
|
||||||
btScalar _halfHeight { 0.0f };
|
btScalar _halfHeight { 0.0f };
|
||||||
btScalar _radius { 0.0f };
|
btScalar _radius { 0.0f };
|
||||||
btScalar _maxWallNormalUpComponent { 0.0f }; // input: max vertical component of wall normal
|
|
||||||
btScalar _maxStepHeight { 0.0f }; // input, max step height the character can climb
|
|
||||||
btConvexHullShape* _characterShape { nullptr }; // input, shape of character
|
btConvexHullShape* _characterShape { nullptr }; // input, shape of character
|
||||||
CharacterGhostShape* _ghostShape { nullptr }; // internal, shape whose Aabb is used for overlap cache
|
CharacterGhostShape* _ghostShape { nullptr }; // internal, shape whose Aabb is used for overlap cache
|
||||||
int16_t _collisionFilterGroup { 0 };
|
int16_t _collisionFilterGroup { 0 };
|
||||||
int16_t _collisionFilterMask { 0 };
|
int16_t _collisionFilterMask { 0 };
|
||||||
bool _inWorld { false }; // internal, was added to world
|
bool _inWorld { false }; // internal, was added to world
|
||||||
bool _hovering { false }; // internal,
|
|
||||||
bool _onFloor { false }; // output, is actually standing on floor
|
|
||||||
bool _steppingUp { false }; // output, future sweep hit a steppable ledge
|
|
||||||
bool _hasFloor { false }; // output, has floor underneath to fall on
|
|
||||||
bool _motorOnly { false }; // input, _linearVelocity slaves to _motorVelocity
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_CharacterGhostObject_h
|
#endif // hifi_CharacterGhostObject_h
|
||||||
|
|
Loading…
Reference in a new issue