1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-02-05 21:45:33 +00:00

Account for waterwalking when updating position. Otherwise we might

trace down the actor at waterlevel at the wrong coordinate.

Triggered by multimark mod with waterwalking effect.
This commit is contained in:
fredzio 2021-04-09 19:11:26 +02:00
parent fe2a97ee39
commit 6e1c67a9ae
3 changed files with 27 additions and 26 deletions

View file

@ -317,7 +317,7 @@ namespace MWPhysics
// init
for (auto& data : actorsData)
data.updatePosition();
data.updatePosition(mCollisionWorld);
mPrevStepCount = numSteps;
mRemainingSteps = numSteps;
mTimeAccum = timeAccum;

View file

@ -60,6 +60,22 @@
#include "movementsolver.hpp"
#include "mtphysics.hpp"
namespace
{
bool canMoveToWaterSurface(const MWPhysics::Actor* physicActor, const float waterlevel, btCollisionWorld* world)
{
if (!physicActor)
return false;
const float halfZ = physicActor->getHalfExtents().z();
const osg::Vec3f actorPosition = physicActor->getPosition();
const osg::Vec3f startingPosition(actorPosition.x(), actorPosition.y(), actorPosition.z() + halfZ);
const osg::Vec3f destinationPosition(actorPosition.x(), actorPosition.y(), waterlevel + halfZ);
MWPhysics::ActorTracer tracer;
tracer.doTrace(physicActor->getCollisionObject(), startingPosition, destinationPosition, world);
return (tracer.mFraction >= 1.0f);
}
}
namespace MWPhysics
{
PhysicsSystem::PhysicsSystem(Resource::ResourceSystem* resourceSystem, osg::ref_ptr<osg::Group> parentNode)
@ -347,16 +363,7 @@ namespace MWPhysics
bool PhysicsSystem::canMoveToWaterSurface(const MWWorld::ConstPtr &actor, const float waterlevel)
{
const Actor* physicActor = getActor(actor);
if (!physicActor)
return false;
const float halfZ = physicActor->getHalfExtents().z();
const osg::Vec3f actorPosition = physicActor->getPosition();
const osg::Vec3f startingPosition(actorPosition.x(), actorPosition.y(), actorPosition.z() + halfZ);
const osg::Vec3f destinationPosition(actorPosition.x(), actorPosition.y(), waterlevel + halfZ);
ActorTracer tracer;
tracer.doTrace(physicActor->getCollisionObject(), startingPosition, destinationPosition, mCollisionWorld.get());
return (tracer.mFraction >= 1.0f);
return ::canMoveToWaterSurface(getActor(actor), waterlevel, mCollisionWorld.get());
}
osg::Vec3f PhysicsSystem::getHalfExtents(const MWWorld::ConstPtr &actor) const
@ -772,16 +779,10 @@ namespace MWPhysics
const MWMechanics::MagicEffects& effects = character.getClass().getCreatureStats(character).getMagicEffects();
bool waterCollision = false;
bool moveToWaterSurface = false;
if (cell->getCell()->hasWater() && effects.get(ESM::MagicEffect::WaterWalking).getMagnitude())
{
if (!world->isUnderwater(character.getCell(), osg::Vec3f(character.getRefData().getPosition().asVec3())))
if (physicActor->getCollisionMode() || !world->isUnderwater(character.getCell(), osg::Vec3f(character.getRefData().getPosition().asVec3())))
waterCollision = true;
else if (physicActor->getCollisionMode() && canMoveToWaterSurface(character, waterlevel))
{
moveToWaterSurface = true;
waterCollision = true;
}
}
physicActor->setCanWaterWalk(waterCollision);
@ -794,7 +795,7 @@ namespace MWPhysics
if (!willSimulate)
standingOn = physicActor->getStandingOnPtr();
actorsFrameData.emplace_back(std::move(physicActor), standingOn, moveToWaterSurface, movement, slowFall, waterlevel);
actorsFrameData.emplace_back(std::move(physicActor), standingOn, waterCollision, movement, slowFall, waterlevel);
}
mMovementQueue.clear();
return actorsFrameData;
@ -937,9 +938,9 @@ namespace MWPhysics
}
ActorFrameData::ActorFrameData(const std::shared_ptr<Actor>& actor, const MWWorld::Ptr standingOn,
bool moveToWaterSurface, osg::Vec3f movement, float slowFall, float waterlevel)
bool waterCollision, osg::Vec3f movement, float slowFall, float waterlevel)
: mActor(actor), mActorRaw(actor.get()), mStandingOn(standingOn),
mDidJump(false), mNeedLand(false), mMoveToWaterSurface(moveToWaterSurface),
mDidJump(false), mNeedLand(false), mWaterCollision(waterCollision),
mWaterlevel(waterlevel), mSlowFall(slowFall), mOldHeight(0), mFallHeight(0), mMovement(movement), mPosition(), mRefpos()
{
const MWBase::World *world = MWBase::Environment::get().getWorld();
@ -953,7 +954,7 @@ namespace MWPhysics
mWasOnGround = actor->getOnGround();
}
void ActorFrameData::updatePosition()
void ActorFrameData::updatePosition(btCollisionWorld* world)
{
mActorRaw->updateWorldPosition();
// If physics runs "fast enough", position are interpolated without simulation
@ -961,10 +962,10 @@ namespace MWPhysics
// regardless of simulation speed.
mActorRaw->applyOffsetChange();
mPosition = mActorRaw->getPosition();
if (mMoveToWaterSurface)
if (mWaterCollision && mPosition.z() < mWaterlevel && canMoveToWaterSurface(mActorRaw, mWaterlevel, world))
{
mPosition.z() = mWaterlevel;
MWBase::Environment::get().getWorld()->moveObject(mActorRaw->getPtr(), mPosition.x(), mPosition.y(), mPosition.z());
MWBase::Environment::get().getWorld()->moveObject(mActorRaw->getPtr(), mPosition.x(), mPosition.y(), mPosition.z(), false);
}
mOldHeight = mPosition.z();
mRefpos = mActorRaw->getPtr().getRefData().getPosition();

View file

@ -79,7 +79,7 @@ namespace MWPhysics
struct ActorFrameData
{
ActorFrameData(const std::shared_ptr<Actor>& actor, const MWWorld::Ptr standingOn, bool moveToWaterSurface, osg::Vec3f movement, float slowFall, float waterlevel);
void updatePosition();
void updatePosition(btCollisionWorld* world);
std::weak_ptr<Actor> mActor;
Actor* mActorRaw;
MWWorld::Ptr mStandingOn;
@ -90,7 +90,7 @@ namespace MWPhysics
bool mDidJump;
bool mFloatToSurface;
bool mNeedLand;
bool mMoveToWaterSurface;
bool mWaterCollision;
float mWaterlevel;
float mSlowFall;
float mOldHeight;