1
0
mirror of https://gitlab.com/OpenMW/openmw.git synced 2025-02-22 12:39:59 +00:00

Movement solver tweaks

1) As much as I dislike it, upping the collision margin from 0.1 to 0.2
fixes bugs, particularly involving walking into upwards-slanted walls.

2) There were still some problems involving acute crevices/seams; they
were using the adjusted instead of unadjusted normal, and also they need
to bypass the don't-slide-upwards check to prevent (see #6379)

3) The move-away-from-what-we-just-hit code needs to run always, not
just on non-initial iterations. No idea why I did it this way before.

4) Force bullet to give actor boxes a tiny collision margin of 0.001
instead of the default 0.04. I can't tell whether this is actually
working or not, but it should reduce unexplained weirdness.

5) A piece of code that was meant to prevent bugs by short-circuiting
the movement solver if its direction changed more than 180 degrees
actually caused problems instead of preventing them, so I deleted it.
This commit is contained in:
wareya 2021-11-05 14:45:31 -04:00
parent b6d2c57de2
commit 49d2daee6a
3 changed files with 17 additions and 12 deletions

View File

@ -59,6 +59,7 @@ Actor::Actor(const MWWorld::Ptr& ptr, const Resource::BulletShape* shape, Physic
mRotationallyInvariant = (mMeshTranslation.x() == 0.0 && mMeshTranslation.y() == 0.0) && std::fabs(mOriginalHalfExtents.x() - mOriginalHalfExtents.y()) < 2.2; mRotationallyInvariant = (mMeshTranslation.x() == 0.0 && mMeshTranslation.y() == 0.0) && std::fabs(mOriginalHalfExtents.x() - mOriginalHalfExtents.y()) < 2.2;
mConvexShape = static_cast<btConvexShape*>(mShape.get()); mConvexShape = static_cast<btConvexShape*>(mShape.get());
mConvexShape->setMargin(0.001); // make sure bullet isn't using the huge default convex shape margin of 0.04
mCollisionObject = std::make_unique<btCollisionObject>(); mCollisionObject = std::make_unique<btCollisionObject>();
mCollisionObject->setCollisionFlags(btCollisionObject::CF_KINEMATIC_OBJECT); mCollisionObject->setCollisionFlags(btCollisionObject::CF_KINEMATIC_OBJECT);

View File

@ -15,7 +15,7 @@ namespace MWPhysics
// Arbitrary number. To prevent infinite loops. They shouldn't happen but it's good to be prepared. // Arbitrary number. To prevent infinite loops. They shouldn't happen but it's good to be prepared.
static constexpr int sMaxIterations = 8; static constexpr int sMaxIterations = 8;
// Allows for more precise movement solving without getting stuck or snagging too easily. // Allows for more precise movement solving without getting stuck or snagging too easily.
static constexpr float sCollisionMargin = 0.1f; static constexpr float sCollisionMargin = 0.2f;
// Allow for a small amount of penetration to prevent numerical precision issues from causing the "unstuck"ing code to run unnecessarily // Allow for a small amount of penetration to prevent numerical precision issues from causing the "unstuck"ing code to run unnecessarily
// Currently set to 0 because having the "unstuck"ing code run whenever possible prevents some glitchy snagging issues // Currently set to 0 because having the "unstuck"ing code run whenever possible prevents some glitchy snagging issues
static constexpr float sAllowedPenetration = 0.0f; static constexpr float sAllowedPenetration = 0.0f;

View File

@ -252,6 +252,8 @@ namespace MWPhysics
remainingTime *= (1.0f-tracer.mFraction); remainingTime *= (1.0f-tracer.mFraction);
auto planeNormal = tracer.mPlaneNormal; auto planeNormal = tracer.mPlaneNormal;
// need to know the unadjusted normal to handle certain types of seams properly
const auto origPlaneNormal = planeNormal;
// If we touched the ground this frame, and whatever we ran into is a wall of some sort, // If we touched the ground this frame, and whatever we ran into is a wall of some sort,
// pretend that its collision normal is pointing horizontally // pretend that its collision normal is pointing horizontally
@ -275,10 +277,11 @@ namespace MWPhysics
bool usedSeamLogic = false; bool usedSeamLogic = false;
// check for the current and previous collision planes forming an acute angle; slide along the seam if they do // check for the current and previous collision planes forming an acute angle; slide along the seam if they do
// for this, we want to use the original plane normal, or else certain types of geometry will snag
if(numTimesSlid > 0) if(numTimesSlid > 0)
{ {
auto dotA = lastSlideNormal * planeNormal; auto dotA = lastSlideNormal * origPlaneNormal;
auto dotB = lastSlideNormalFallback * planeNormal; auto dotB = lastSlideNormalFallback * origPlaneNormal;
if(numTimesSlid <= 1) // ignore fallback normal if this is only the first or second slide if(numTimesSlid <= 1) // ignore fallback normal if this is only the first or second slide
dotB = 1.0; dotB = 1.0;
if(dotA <= 0.0 || dotB <= 0.0) if(dotA <= 0.0 || dotB <= 0.0)
@ -291,14 +294,14 @@ namespace MWPhysics
lastSlideNormal = lastSlideNormalFallback; lastSlideNormal = lastSlideNormalFallback;
} }
auto constraintVector = bestNormal ^ planeNormal; // cross product auto constraintVector = bestNormal ^ origPlaneNormal; // cross product
if(constraintVector.length2() > 0) // only if it's not zero length if(constraintVector.length2() > 0) // only if it's not zero length
{ {
constraintVector.normalize(); constraintVector.normalize();
newVelocity = project(velocity, constraintVector); newVelocity = project(velocity, constraintVector);
// version of surface rejection for acute crevices/seams // version of surface rejection for acute crevices/seams
auto averageNormal = bestNormal + planeNormal; auto averageNormal = bestNormal + origPlaneNormal;
averageNormal.normalize(); averageNormal.normalize();
tracer.doTrace(actor.mCollisionObject, newPosition, newPosition + averageNormal*(sCollisionMargin*2.0), collisionWorld); tracer.doTrace(actor.mCollisionObject, newPosition, newPosition + averageNormal*(sCollisionMargin*2.0), collisionWorld);
newPosition = (newPosition + tracer.mEndPos)/2.0; newPosition = (newPosition + tracer.mEndPos)/2.0;
@ -309,27 +312,28 @@ namespace MWPhysics
} }
// otherwise just keep the normal vector rejection // otherwise just keep the normal vector rejection
// if this isn't the first iteration, or if the first iteration is also the last iteration,
// move away from the collision plane slightly, if possible // move away from the collision plane slightly, if possible
// this reduces getting stuck in some concave geometry, like the gaps above the railings in some ald'ruhn buildings // this reduces getting stuck in some concave geometry, like the gaps above the railings in some ald'ruhn buildings
// this is different from the normal collision margin, because the normal collision margin is along the movement path, // this is different from the normal collision margin, because the normal collision margin is along the movement path,
// but this is along the collision normal // but this is along the collision normal
if(!usedSeamLogic && (iterations > 0 || remainingTime < 0.01f)) if(!usedSeamLogic)
{ {
tracer.doTrace(actor.mCollisionObject, newPosition, newPosition + planeNormal*(sCollisionMargin*2.0), collisionWorld); tracer.doTrace(actor.mCollisionObject, newPosition, newPosition + planeNormal*(sCollisionMargin*2.0), collisionWorld);
newPosition = (newPosition + tracer.mEndPos)/2.0; newPosition = (newPosition + tracer.mEndPos)/2.0;
} }
// Do not allow sliding up steep slopes if there is gravity. // Do not allow sliding up steep slopes if there is gravity.
if (newPosition.z() >= swimlevel && !actor.mFlying && !isWalkableSlope(planeNormal)) // The purpose of this is to prevent air control from letting you slide up tall, unwalkable slopes.
// For that purpose, it is not necessary to do it when trying to slide along acute seams/crevices (i.e. usedSeamLogic)
// and doing so would actually break air control in some situations where vanilla allows air control.
// Vanilla actually allows you to slide up slopes as long as you're in the "walking" animation, which can be true even
// in the air, so allowing this for seams isn't a compatibility break.
if (newPosition.z() >= swimlevel && !actor.mFlying && !isWalkableSlope(planeNormal) && !usedSeamLogic)
newVelocity.z() = std::min(newVelocity.z(), velocity.z()); newVelocity.z() = std::min(newVelocity.z(), velocity.z());
if (newVelocity * origVelocity <= 0.0f)
break;
numTimesSlid += 1; numTimesSlid += 1;
lastSlideNormalFallback = lastSlideNormal; lastSlideNormalFallback = lastSlideNormal;
lastSlideNormal = planeNormal; lastSlideNormal = origPlaneNormal;
velocity = newVelocity; velocity = newVelocity;
} }
} }