Better slope handling, worse sudden elevation change handling.

This commit is contained in:
MaxYari 2025-02-21 22:08:41 +01:00
parent e8e9fdafa2
commit a2d77ae164
3 changed files with 41 additions and 41 deletions

View file

@ -1,4 +1,4 @@
#include "actor.hpp"
#include "actor.hpp"
#include <BulletCollision/CollisionShapes/btCylinderShape.h>
@ -36,6 +36,7 @@ namespace MWPhysics
, mExternalCollisionMode(true)
, mActive(false)
, mTaskScheduler(scheduler)
, mCurrentDeltaZ(0.0f)
{
// We can not create actor without collisions - he will fall through the ground.
// In this case we should autogenerate collision box based on mesh shape

View file

@ -146,6 +146,8 @@ namespace MWPhysics
void setActive(bool value) { mActive = value; }
float mCurrentDeltaZ;
DetourNavigator::CollisionShapeType getCollisionShapeType() const { return mCollisionShapeType; }
private:

View file

@ -775,82 +775,72 @@ namespace MWPhysics
osg::ref_ptr<osg::MatrixTransform> leftFootBone;
osg::ref_ptr<osg::MatrixTransform> rightFootBone;
// Cast rays from actor feet and pull the actor down to the ground
float desiredDeltaZ = 0.0f;
if (physicActor.get()->getOnGround())
{
// Finding left leg bones
// Finding leg bones
std::vector<std::string> leftLegBoneChainNames = { "Bip01 L Thigh", "Bip01 L Calf", "Bip01 L Foot" };
std::vector<std::string> rightLegBoneChainNames = { "Bip01 R Thigh", "Bip01 R Calf", "Bip01 R Foot" };
gatherMatchingBones(
actptr.getRefData().getBaseNode()->asGroup(), leftLegBoneChainNames, leftLegBoneChain);
// Finding right leg bones
std::vector<std::string> rightLegBoneChainNames = { "Bip01 R Thigh", "Bip01 R Calf", "Bip01 R Foot" };
gatherMatchingBones(
actptr.getRefData().getBaseNode()->asGroup(), rightLegBoneChainNames, rightLegBoneChain);
// Left foot stuff and ray cast
osg::Matrix leftFootWorldMatrix;
// Compute world transformation for foot bones
osg::Matrix leftFootWorldMatrix, rightFootWorldMatrix;
if (leftLegBoneChain.size() == leftLegBoneChainNames.size())
leftFootBone = leftLegBoneChain.back();
if (leftFootBone)
leftFootWorldMatrix = osg::computeLocalToWorld(leftFootBone->getParentalNodePaths()[0]);
osg::Vec3f leftFootPos
= leftFootBone ? static_cast<osg::Vec3f>(leftFootWorldMatrix.getTrans()) : colliderPos;
leftRayCast = castRay(leftFootPos, leftFootPos - osg::Vec3f(0.0f, 0.0f, 50.0f),
CollisionType_HeightMap + CollisionType_World);
// Right foot stuff and ray cast
osg::Matrix rightFootWorldMatrix;
if (rightLegBoneChain.size() == rightLegBoneChainNames.size())
rightFootBone = rightLegBoneChain.back();
if (rightFootBone)
rightFootWorldMatrix = osg::computeLocalToWorld(rightFootBone->getParentalNodePaths()[0]);
osg::Vec3f rightFootPos
= rightFootBone ? static_cast<osg::Vec3f>(rightFootWorldMatrix.getTrans()) : colliderPos;
// Perform raycasts
leftRayCast = castRay(leftFootPos, leftFootPos - osg::Vec3f(0.0f, 0.0f, 50.0f),
CollisionType_HeightMap + CollisionType_World);
rightRayCast = castRay(rightFootPos, rightFootPos - osg::Vec3f(0.0f, 0.0f, 50.0f),
CollisionType_HeightMap + CollisionType_World);
// Determine the largest raycast distance and adjust the position accordingly
// Determine the desired delta Z
if (leftRayCast.mHit && rightRayCast.mHit)
{
if (leftRayCast.mHitPos.z() < rightRayCast.mHitPos.z())
{
desiredPos.z() = leftRayCast.mHitPos.z();
}
else
{
desiredPos.z() = rightRayCast.mHitPos.z();
}
desiredDeltaZ = std::min(leftRayCast.mHitPos.z(), rightRayCast.mHitPos.z()) - colliderPos.z();
}
else if (leftRayCast.mHit)
{
desiredPos.z() = leftRayCast.mHitPos.z();
desiredDeltaZ = leftRayCast.mHitPos.z() - colliderPos.z();
}
else if (rightRayCast.mHit)
{
desiredPos.z() = rightRayCast.mHitPos.z();
desiredDeltaZ = rightRayCast.mHitPos.z() - colliderPos.z();
}
}
// Lerp actor Z position to smooth out sudden elevation transitions
// Note: current solution is bad, this smoothing results in actors lagging behind on downslopes,
// ideally only actual sudden jarring changes should be detected and smoothed out, but not slopes.
osg::Vec3 lerpedPos = desiredPos;
lerpedPos.z() = std::lerp(visualPos.z(), desiredPos.z(), mPhysicsDt * armatureZLerpSpeed);
// Lerp the current delta Z towards the desired delta Z
physicActor->mCurrentDeltaZ
= std::lerp(physicActor->mCurrentDeltaZ, desiredDeltaZ, mPhysicsDt * armatureZLerpSpeed);
// For some reason player and actors supposed to be kept separately, so we do that here
// Calculate the new armature position using the lerped delta Z
osg::Vec3 armaturePos = colliderPos;
armaturePos.z() += physicActor->mCurrentDeltaZ;
// For some reason player and actors supposed to be kept separately
if (physicActor.get() != player)
mActorsPositions.emplace_back(physicActor->getPtr(), physicActor->getSimulationPosition());
// Finally adjust position based on lerped data, this will adjust both player and actors
world->moveObject(actptr, lerpedPos, false, false);
// Finally adjust position based on the new armature position
world->moveObject(actptr, armaturePos, false, false);
// At this point actor is moved down (if necessary) under its collider to touch the ground level, one of its
// feet is most likely clipping through the ground, we raycast to detect that and use ik to bend the leg up
// Detecting righ leg clipping
MWPhysics::RayCastingResult rightRayUpCast;
MWPhysics::RayCastingResult leftRayUpCast;
// Casting up from feet to detect clipping
MWPhysics::RayCastingResult rightRayUpCast, leftRayUpCast;
if (rightFootBone)
{
auto rightFootWorldMatrix = osg::computeLocalToWorld(rightFootBone->getParentalNodePaths()[0]);
@ -859,13 +849,12 @@ namespace MWPhysics
CollisionType_HeightMap + CollisionType_World);
}
// IK-fixing the right leg
if (rightRayUpCast.mHit)
{
auto& debugRender = world->getRenderingManager()->getDebugDrawer();
/*debugRender.drawCube(rightRayCast.mHitPos, osg::Vec3(10.0, 10.0, 10.0), Debug::colorBlue);*/
// Perform IK for right leg
if (rightLegBoneChain.size() > 0)
{
// Note that its possible to provide a pole direction to specify in which direction a knee should
@ -881,7 +870,6 @@ namespace MWPhysics
}
}
// Detecting left leg clipping
if (leftFootBone)
{
auto leftFootWorldMatrix = osg::computeLocalToWorld(leftFootBone->getParentalNodePaths()[0]);
@ -890,12 +878,12 @@ namespace MWPhysics
CollisionType_HeightMap + CollisionType_World);
}
// IK-fixing the left leg
if (leftRayUpCast.mHit)
{
auto& debugRender = world->getRenderingManager()->getDebugDrawer();
/*debugRender.drawCube(leftRayCast.mHitPos, osg::Vec3(10.0, 10.0, 10.0), Debug::colorBlue);*/
// Perform IK for left leg
if (leftLegBoneChain.size() > 0)
{
EasyIK leftLegIK(leftLegBoneChain, leftRayUpCast.mHitPos + osg::Vec3f(0.0f, 0.0f, leftFootHeight),
@ -907,6 +895,15 @@ namespace MWPhysics
}
}
}
/*for (const auto& [ptr, pos] : mActorsPositions)
{
world->moveObject(ptr, desiredPos, false, false);
}
if (player != nullptr)
world->moveObject(player->getPtr(), player->getSimulationPosition(), false, false);*/
}
void PhysicsSystem::updateAnimatedCollisionShape(const MWWorld::Ptr& object)