Skip to content

Commit

Permalink
Merge branch 'skating-olympics' into 'master'
Browse files Browse the repository at this point in the history
De-jank stationary animations

Closes #3330

See merge request OpenMW/openmw!3631
  • Loading branch information
Capostrophic committed Dec 20, 2023
2 parents d97563c + 00b1cd8 commit 78a8f9d
Show file tree
Hide file tree
Showing 12 changed files with 142 additions and 32 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
------

Bug #2623: Snowy Granius doesn't prioritize conjuration spells
Bug #3330: Backward displacement of the character when attacking in 3rd person
Bug #3438: NPCs can't hit bull netch with melee weapons
Bug #3842: Body part skeletons override the main skeleton
Bug #4127: Weapon animation looks choppy
Expand Down
6 changes: 5 additions & 1 deletion apps/openmw/mwbase/world.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -295,9 +295,13 @@ namespace MWBase
/// relative to \a referenceObject (but the object may be placed somewhere else if the wanted location is
/// obstructed).

virtual void queueMovement(const MWWorld::Ptr& ptr, const osg::Vec3f& velocity) = 0;
virtual void queueMovement(
const MWWorld::Ptr& ptr, const osg::Vec3f& velocity, float duration, bool jump = false)
= 0;
///< Queues movement for \a ptr (in local space), to be applied in the next call to
/// doPhysics.
/// \param duration The duration this speed shall be held, starting at current simulation time
/// \param jump Whether the movement shall be run over time, or immediately added as inertia instead

virtual void updateAnimatedCollisionShape(const MWWorld::Ptr& ptr) = 0;

Expand Down
6 changes: 4 additions & 2 deletions apps/openmw/mwmechanics/character.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2421,7 +2421,9 @@ namespace MWMechanics
}

if (!isMovementAnimationControlled() && !isScriptedAnimPlaying())
world->queueMovement(mPtr, vec);
{
world->queueMovement(mPtr, vec, duration, mInJump && mJumpState == JumpState_None);
}
}

movement = vec;
Expand Down Expand Up @@ -2494,7 +2496,7 @@ namespace MWMechanics
}

// Update movement
world->queueMovement(mPtr, movement);
world->queueMovement(mPtr, movement, duration, mInJump && mJumpState == JumpState_None);
}

mSkipAnim = false;
Expand Down
11 changes: 9 additions & 2 deletions apps/openmw/mwphysics/movementsolver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,8 @@ namespace MWPhysics
}

// Now that we have the effective movement vector, apply wind forces to it
// TODO: This will cause instability in idle animations and other in-place animations. Should include a flag for
// this when queueing up movement
if (worldData.mIsInStorm && velocity.length() > 0)
{
osg::Vec3f stormDirection = worldData.mStormDirection;
Expand Down Expand Up @@ -200,7 +202,8 @@ namespace MWPhysics

for (int iterations = 0; iterations < sMaxIterations && remainingTime > 0.0001f; ++iterations)
{
osg::Vec3f nextpos = newPosition + velocity * remainingTime;
osg::Vec3f diff = velocity * remainingTime;
osg::Vec3f nextpos = newPosition + diff;
bool underwater = newPosition.z() < swimlevel;

// If not able to fly, don't allow to swim up into the air
Expand All @@ -212,7 +215,11 @@ namespace MWPhysics
continue; // velocity updated, calculate nextpos again
}

if ((newPosition - nextpos).length2() > 0.0001)
// Note, we use an epsilon of 1e-6 instead of std::numeric_limits<float>::epsilon() to avoid doing extremely
// small steps. But if we make it any larger we'll start rejecting subtle movements from e.g. idle
// animations. Note that, although both these comparisons to 1e-6 are logically the same, they test separate
// floating point accuracy cases.
if (diff.length2() > 1e-6 && (newPosition - nextpos).length2() > 1e-6)
{
// trace to where character would go if there were no obstructions
tracer.doTrace(actor.mCollisionObject, newPosition, nextpos, collisionWorld, actor.mIsOnGround);
Expand Down
88 changes: 80 additions & 8 deletions apps/openmw/mwphysics/mtphysics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include "../mwrender/bulletdebugdraw.hpp"

#include "../mwworld/class.hpp"
#include "../mwworld/datetimemanager.hpp"

#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
Expand All @@ -35,6 +36,7 @@
#include "object.hpp"
#include "physicssystem.hpp"
#include "projectile.hpp"
#include "ptrholder.hpp"

namespace MWPhysics
{
Expand Down Expand Up @@ -195,6 +197,67 @@ namespace
void operator()(MWPhysics::ProjectileSimulation& /*sim*/) const {}
};

struct InitMovement
{
int mSteps = 0;
float mDelta = 0.f;
float mSimulationTime = 0.f;

// Returns how the actor or projectile wants to move between startTime and endTime
osg::Vec3f takeMovement(MWPhysics::PtrHolder& actor, float startTime, float endTime) const
{
osg::Vec3f movement = osg::Vec3f();
actor.eraseMovementIf([&](MWPhysics::Movement& v) {
if (v.mJump)
return false;
float start = std::max(v.mSimulationTimeStart, startTime);
float stop = std::min(v.mSimulationTimeStop, endTime);
movement += v.mVelocity * (stop - start);
if (std::abs(stop - v.mSimulationTimeStop) < 0.0001f)
return true;
return false;
});

return movement;
}

std::optional<osg::Vec3f> takeInertia(MWPhysics::PtrHolder& actor, float startTime) const
{
std::optional<osg::Vec3f> inertia = std::nullopt;
actor.eraseMovementIf([&](MWPhysics::Movement& v) {
if (v.mJump && v.mSimulationTimeStart >= startTime)
{
inertia = v.mVelocity;
return true;
}
return false;
});
return inertia;
}

void operator()(auto& sim) const
{
if (mSteps <= 0 || mDelta < 0.00001f)
return;

auto locked = sim.lock();
if (!locked.has_value())
return;

auto& [ptrHolder, frameDataRef] = *locked;

// Because takeMovement() returns movement instead of velocity, convert it back to velocity for the
// movement solver
osg::Vec3f velocity
= takeMovement(*ptrHolder, mSimulationTime, mSimulationTime + mDelta * mSteps) / (mSteps * mDelta);
// takeInertia() returns a velocity and should be taken over the velocity calculated above to initiate a
// jump
auto inertia = takeInertia(*ptrHolder, mSimulationTime);

frameDataRef.get().mMovement += inertia.value_or(velocity);
}
};

struct PreStep
{
btCollisionWorld* mCollisionWorld;
Expand Down Expand Up @@ -501,18 +564,18 @@ namespace MWPhysics
return std::make_tuple(numSteps, actualDelta);
}

void PhysicsTaskScheduler::applyQueuedMovements(float& timeAccum, std::vector<Simulation>& simulations,
osg::Timer_t frameStart, unsigned int frameNumber, osg::Stats& stats)
void PhysicsTaskScheduler::applyQueuedMovements(float& timeAccum, float simulationTime,
std::vector<Simulation>& simulations, osg::Timer_t frameStart, unsigned int frameNumber, osg::Stats& stats)
{
assert(mSimulations != &simulations);

waitForWorkers();
prepareWork(timeAccum, simulations, frameStart, frameNumber, stats);
prepareWork(timeAccum, simulationTime, simulations, frameStart, frameNumber, stats);
if (mWorkersSync != nullptr)
mWorkersSync->wakeUpWorkers();
}

void PhysicsTaskScheduler::prepareWork(float& timeAccum, std::vector<Simulation>& simulations,
void PhysicsTaskScheduler::prepareWork(float& timeAccum, float simulationTime, std::vector<Simulation>& simulations,
osg::Timer_t frameStart, unsigned int frameNumber, osg::Stats& stats)
{
// This function run in the main thread.
Expand All @@ -522,6 +585,9 @@ namespace MWPhysics

double timeStart = mTimer->tick();

// The simulation time when the movement solving begins.
float simulationTimeStart = simulationTime - timeAccum;

// start by finishing previous background computation
if (mNumThreads != 0)
{
Expand All @@ -536,10 +602,15 @@ namespace MWPhysics
timeAccum -= numSteps * newDelta;

// init
const Visitors::InitPosition vis{ mCollisionWorld };
const Visitors::InitPosition initPositionVisitor{ mCollisionWorld };
for (auto& sim : simulations)
{
std::visit(vis, sim);
std::visit(initPositionVisitor, sim);
}
const Visitors::InitMovement initMovementVisitor{ numSteps, newDelta, simulationTimeStart };
for (auto& sim : simulations)
{
std::visit(initMovementVisitor, sim);
}
mPrevStepCount = numSteps;
mRemainingSteps = numSteps;
Expand All @@ -552,10 +623,10 @@ namespace MWPhysics
mNextJob.store(0, std::memory_order_release);

if (mAdvanceSimulation)
{
mWorldFrameData = std::make_unique<WorldFrameData>();

if (mAdvanceSimulation)
mBudgetCursor += 1;
}

if (mNumThreads == 0)
{
Expand Down Expand Up @@ -864,6 +935,7 @@ namespace MWPhysics
std::remove_if(mLOSCache.begin(), mLOSCache.end(), [](const LOSRequest& req) { return req.mStale; }),
mLOSCache.end());
}

mTimeEnd = mTimer->tick();
if (mWorkersSync != nullptr)
mWorkersSync->workIsDone();
Expand Down
8 changes: 4 additions & 4 deletions apps/openmw/mwphysics/mtphysics.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ namespace MWPhysics
/// @param timeAccum accumulated time from previous run to interpolate movements
/// @param actorsData per actor data needed to compute new positions
/// @return new position of each actor
void applyQueuedMovements(float& timeAccum, std::vector<Simulation>& simulations, osg::Timer_t frameStart,
unsigned int frameNumber, osg::Stats& stats);
void applyQueuedMovements(float& timeAccum, float simulationTime, std::vector<Simulation>& simulations,
osg::Timer_t frameStart, unsigned int frameNumber, osg::Stats& stats);

void resetSimulation(const ActorMap& actors);

Expand Down Expand Up @@ -87,8 +87,8 @@ namespace MWPhysics
void afterPostSim();
void syncWithMainThread();
void waitForWorkers();
void prepareWork(float& timeAccum, std::vector<Simulation>& simulations, osg::Timer_t frameStart,
unsigned int frameNumber, osg::Stats& stats);
void prepareWork(float& timeAccum, float simulationTime, std::vector<Simulation>& simulations,
osg::Timer_t frameStart, unsigned int frameNumber, osg::Stats& stats);

std::unique_ptr<WorldFrameData> mWorldFrameData;
std::vector<Simulation>* mSimulations = nullptr;
Expand Down
17 changes: 11 additions & 6 deletions apps/openmw/mwphysics/physicssystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
#include "../mwrender/bulletdebugdraw.hpp"

#include "../mwworld/class.hpp"
#include "../mwworld/datetimemanager.hpp"

#include "actor.hpp"
#include "collisiontype.hpp"
Expand Down Expand Up @@ -623,18 +624,20 @@ namespace MWPhysics
return false;
}

void PhysicsSystem::queueObjectMovement(const MWWorld::Ptr& ptr, const osg::Vec3f& velocity)
void PhysicsSystem::queueObjectMovement(
const MWWorld::Ptr& ptr, const osg::Vec3f& velocity, float duration, bool jump)
{
float start = MWBase::Environment::get().getWorld()->getTimeManager()->getSimulationTime();
ActorMap::iterator found = mActors.find(ptr.mRef);
if (found != mActors.end())
found->second->setVelocity(velocity);
found->second->queueMovement(velocity, start, start + duration, jump);
}

void PhysicsSystem::clearQueuedMovement()
{
for (const auto& [_, actor] : mActors)
{
actor->setVelocity(osg::Vec3f());
actor->clearMovement();
actor->setInertialForce(osg::Vec3f());
}
}
Expand Down Expand Up @@ -722,8 +725,10 @@ namespace MWPhysics
{
std::vector<Simulation>& simulations = mSimulations[mSimulationsCounter++ % mSimulations.size()];
prepareSimulation(mTimeAccum >= mPhysicsDt, simulations);
float simulationTime = MWBase::Environment::get().getWorld()->getTimeManager()->getSimulationTime() + dt;
// modifies mTimeAccum
mTaskScheduler->applyQueuedMovements(mTimeAccum, simulations, frameStart, frameNumber, stats);
mTaskScheduler->applyQueuedMovements(
mTimeAccum, simulationTime, simulations, frameStart, frameNumber, stats);
}
}

Expand Down Expand Up @@ -907,7 +912,7 @@ namespace MWPhysics
->mValue.getFloat()))
, mSlowFall(slowFall)
, mRotation()
, mMovement(actor.velocity())
, mMovement()
, mWaterlevel(waterlevel)
, mHalfExtentsZ(actor.getHalfExtents().z())
, mOldHeight(0)
Expand All @@ -922,7 +927,7 @@ namespace MWPhysics

ProjectileFrameData::ProjectileFrameData(Projectile& projectile)
: mPosition(projectile.getPosition())
, mMovement(projectile.velocity())
, mMovement()
, mCaster(projectile.getCasterCollisionObject())
, mCollisionObject(projectile.getCollisionObject())
, mProjectile(&projectile)
Expand Down
3 changes: 2 additions & 1 deletion apps/openmw/mwphysics/physicssystem.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,8 @@ namespace MWPhysics

/// Queues velocity movement for a Ptr. If a Ptr is already queued, its velocity will
/// be overwritten. Valid until the next call to stepSimulation
void queueObjectMovement(const MWWorld::Ptr& ptr, const osg::Vec3f& velocity);
void queueObjectMovement(
const MWWorld::Ptr& ptr, const osg::Vec3f& velocity, float duration, bool jump = false);

/// Clear the queued movements list without applying.
void clearQueuedMovement();
Expand Down
19 changes: 16 additions & 3 deletions apps/openmw/mwphysics/ptrholder.hpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#ifndef OPENMW_MWPHYSICS_PTRHOLDER_H
#define OPENMW_MWPHYSICS_PTRHOLDER_H

#include <list>
#include <memory>
#include <mutex>
#include <utility>
Expand All @@ -13,6 +14,14 @@

namespace MWPhysics
{
struct Movement
{
osg::Vec3f mVelocity = osg::Vec3f();
float mSimulationTimeStart = 0.f; // The time at which this movement begun
float mSimulationTimeStop = 0.f; // The time at which this movement finished
bool mJump = false;
};

class PtrHolder
{
public:
Expand All @@ -32,9 +41,13 @@ namespace MWPhysics

btCollisionObject* getCollisionObject() const { return mCollisionObject.get(); }

void setVelocity(osg::Vec3f velocity) { mVelocity = velocity; }
void clearMovement() { mMovement = {}; }
void queueMovement(osg::Vec3f velocity, float simulationTimeStart, float simulationTimeStop, bool jump = false)
{
mMovement.push_back(Movement{ velocity, simulationTimeStart, simulationTimeStop, jump });
}

osg::Vec3f velocity() { return std::exchange(mVelocity, osg::Vec3f()); }
void eraseMovementIf(const auto& predicate) { std::erase_if(mMovement, predicate); }

void setSimulationPosition(const osg::Vec3f& position) { mSimulationPosition = position; }

Expand All @@ -53,7 +66,7 @@ namespace MWPhysics
protected:
MWWorld::Ptr mPtr;
std::unique_ptr<btCollisionObject> mCollisionObject;
osg::Vec3f mVelocity;
std::list<Movement> mMovement;
osg::Vec3f mSimulationPosition;
osg::Vec3d mPosition;
osg::Vec3d mPreviousPosition;
Expand Down
Loading

0 comments on commit 78a8f9d

Please sign in to comment.