mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-01-30 03:15:32 +00:00
Merge branch 'movement_refactoring' into 'master'
Refactoring related to "smooth movement" See merge request OpenMW/openmw!285 (cherry picked from commit 6eaf0a389d5aed3b74ab1a7cf89574612f964bdf) e847b4c8 Split getSpeed() to getMaxSpeed() and getCurrentSpeed() a96c46bc Refactor calculation of movement.mSpeedFactor 03ee9090 Use getMaxSpeed instead of getCurrentSpeed where it makes sense. a178af5c Create helper functions `normalizeAngle` and `rotateVec2f`
This commit is contained in:
parent
51bec5948f
commit
f90a049702
16 changed files with 97 additions and 83 deletions
|
@ -91,4 +91,13 @@ namespace MWClass
|
|||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
float Actor::getCurrentSpeed(const MWWorld::Ptr& ptr) const
|
||||
{
|
||||
const MWMechanics::Movement& movementSettings = ptr.getClass().getMovementSettings(ptr);
|
||||
float moveSpeed = this->getMaxSpeed(ptr) * movementSettings.mSpeedFactor;
|
||||
if (movementSettings.mIsStrafing)
|
||||
moveSpeed *= 0.75f;
|
||||
return moveSpeed;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ namespace MWClass
|
|||
virtual void block(const MWWorld::Ptr &ptr) const;
|
||||
|
||||
virtual osg::Vec3f getRotationVector(const MWWorld::Ptr& ptr) const;
|
||||
///< Return desired rotations, as euler angles.
|
||||
///< Return desired rotations, as euler angles. Sets getMovementSettings(ptr).mRotation to zero.
|
||||
|
||||
virtual float getEncumbrance(const MWWorld::Ptr& ptr) const;
|
||||
///< Returns total weight of objects inside this object (including modifications from magic
|
||||
|
@ -42,6 +42,9 @@ namespace MWClass
|
|||
|
||||
virtual bool isActor() const;
|
||||
|
||||
/// Return current movement speed.
|
||||
virtual float getCurrentSpeed(const MWWorld::Ptr& ptr) const;
|
||||
|
||||
// not implemented
|
||||
Actor(const Actor&);
|
||||
Actor& operator= (const Actor&);
|
||||
|
|
|
@ -500,7 +500,7 @@ namespace MWClass
|
|||
registerClass (typeid (ESM::Creature).name(), instance);
|
||||
}
|
||||
|
||||
float Creature::getSpeed(const MWWorld::Ptr &ptr) const
|
||||
float Creature::getMaxSpeed(const MWWorld::Ptr &ptr) const
|
||||
{
|
||||
const MWMechanics::CreatureStats& stats = getCreatureStats(ptr);
|
||||
|
||||
|
@ -532,11 +532,6 @@ namespace MWClass
|
|||
else
|
||||
moveSpeed = getWalkSpeed(ptr);
|
||||
|
||||
const MWMechanics::Movement& movementSettings = ptr.getClass().getMovementSettings(ptr);
|
||||
if (movementSettings.mIsStrafing)
|
||||
moveSpeed *= 0.75f;
|
||||
moveSpeed *= movementSettings.mSpeedFactor;
|
||||
|
||||
return moveSpeed;
|
||||
}
|
||||
|
||||
|
|
|
@ -94,7 +94,7 @@ namespace MWClass
|
|||
virtual MWMechanics::Movement& getMovementSettings (const MWWorld::Ptr& ptr) const;
|
||||
///< Return desired movement.
|
||||
|
||||
float getSpeed (const MWWorld::Ptr& ptr) const;
|
||||
float getMaxSpeed (const MWWorld::Ptr& ptr) const;
|
||||
|
||||
static void registerSelf();
|
||||
|
||||
|
|
|
@ -936,7 +936,7 @@ namespace MWClass
|
|||
return ref->mBase->mScript;
|
||||
}
|
||||
|
||||
float Npc::getSpeed(const MWWorld::Ptr& ptr) const
|
||||
float Npc::getMaxSpeed(const MWWorld::Ptr& ptr) const
|
||||
{
|
||||
const MWMechanics::CreatureStats& stats = getCreatureStats(ptr);
|
||||
if (stats.isParalyzed() || stats.getKnockedDown() || stats.isDead())
|
||||
|
@ -979,11 +979,6 @@ namespace MWClass
|
|||
if(npcdata->mNpcStats.isWerewolf() && running && npcdata->mNpcStats.getDrawState() == MWMechanics::DrawState_Nothing)
|
||||
moveSpeed *= gmst.fWereWolfRunMult->mValue.getFloat();
|
||||
|
||||
const MWMechanics::Movement& movementSettings = ptr.getClass().getMovementSettings(ptr);
|
||||
if (movementSettings.mIsStrafing)
|
||||
moveSpeed *= 0.75f;
|
||||
moveSpeed *= movementSettings.mSpeedFactor;
|
||||
|
||||
return moveSpeed;
|
||||
}
|
||||
|
||||
|
|
|
@ -84,8 +84,8 @@ namespace MWClass
|
|||
virtual std::string getScript (const MWWorld::ConstPtr& ptr) const;
|
||||
///< Return name of the script attached to ptr
|
||||
|
||||
virtual float getSpeed (const MWWorld::Ptr& ptr) const;
|
||||
///< Return movement speed.
|
||||
virtual float getMaxSpeed (const MWWorld::Ptr& ptr) const;
|
||||
///< Return maximal movement speed.
|
||||
|
||||
virtual float getJump(const MWWorld::Ptr &ptr) const;
|
||||
///< Return jump velocity (not accounting for movement)
|
||||
|
|
|
@ -472,9 +472,6 @@ namespace MWMechanics
|
|||
|
||||
void Actors::updateMovementSpeed(const MWWorld::Ptr& actor)
|
||||
{
|
||||
float previousSpeedFactor = actor.getClass().getMovementSettings(actor).mSpeedFactor;
|
||||
float newSpeedFactor = 1.f;
|
||||
|
||||
CreatureStats &stats = actor.getClass().getCreatureStats(actor);
|
||||
MWMechanics::AiSequence& seq = stats.getAiSequence();
|
||||
|
||||
|
@ -484,10 +481,13 @@ namespace MWMechanics
|
|||
osg::Vec3f actorPos = actor.getRefData().getPosition().asVec3();
|
||||
float distance = (targetPos - actorPos).length();
|
||||
if (distance < DECELERATE_DISTANCE)
|
||||
newSpeedFactor = std::max(0.7f, 0.1f * previousSpeedFactor * (distance/64.f + 2.f));
|
||||
{
|
||||
float speedCoef = std::max(0.7f, 0.1f * (distance/64.f + 2.f));
|
||||
auto& movement = actor.getClass().getMovementSettings(actor);
|
||||
movement.mPosition[0] *= speedCoef;
|
||||
movement.mPosition[1] *= speedCoef;
|
||||
}
|
||||
}
|
||||
|
||||
actor.getClass().getMovementSettings(actor).mSpeedFactor = newSpeedFactor;
|
||||
}
|
||||
|
||||
void Actors::updateGreetingState(const MWWorld::Ptr& actor, Actor& actorState, bool turnOnly)
|
||||
|
|
|
@ -144,7 +144,7 @@ bool MWMechanics::AiPackage::pathTo(const MWWorld::Ptr& actor, const osg::Vec3f&
|
|||
mTimer = 0;
|
||||
}
|
||||
|
||||
const float actorTolerance = 2 * actor.getClass().getSpeed(actor) * duration
|
||||
const float actorTolerance = 2 * actor.getClass().getMaxSpeed(actor) * duration
|
||||
+ 1.2 * std::max(halfExtents.x(), halfExtents.y());
|
||||
const float pointTolerance = std::max(MIN_TOLERANCE, actorTolerance);
|
||||
|
||||
|
@ -300,7 +300,7 @@ bool MWMechanics::AiPackage::checkWayIsClearForActor(const osg::Vec3f& startPoin
|
|||
if (canActorMoveByZAxis(actor))
|
||||
return true;
|
||||
|
||||
const float actorSpeed = actor.getClass().getSpeed(actor);
|
||||
const float actorSpeed = actor.getClass().getMaxSpeed(actor);
|
||||
const float maxAvoidDist = AI_REACTION_TIME * actorSpeed + actorSpeed / getAngularVelocity(actorSpeed) * 2; // *2 - for reliability
|
||||
const float distToTarget = osg::Vec2f(endPoint.x(), endPoint.y()).length();
|
||||
|
||||
|
@ -360,7 +360,7 @@ bool MWMechanics::AiPackage::isNearInactiveCell(osg::Vec3f position)
|
|||
bool MWMechanics::AiPackage::isReachableRotatingOnTheRun(const MWWorld::Ptr& actor, const osg::Vec3f& dest)
|
||||
{
|
||||
// get actor's shortest radius for moving in circle
|
||||
float speed = actor.getClass().getSpeed(actor);
|
||||
float speed = actor.getClass().getMaxSpeed(actor);
|
||||
speed += speed * 0.1f; // 10% real speed inaccuracy
|
||||
float radius = speed / getAngularVelocity(speed);
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
|
||||
#include <iostream>
|
||||
|
||||
#include <components/misc/mathutil.hpp>
|
||||
#include <components/misc/rng.hpp>
|
||||
|
||||
#include <components/settings/settings.hpp>
|
||||
|
@ -51,15 +52,6 @@
|
|||
namespace
|
||||
{
|
||||
|
||||
// Wraps a value to (-PI, PI]
|
||||
void wrap(float& rad)
|
||||
{
|
||||
if (rad>0)
|
||||
rad = std::fmod(rad+osg::PI, 2.0f*osg::PI)-osg::PI;
|
||||
else
|
||||
rad = std::fmod(rad-osg::PI, 2.0f*osg::PI)+osg::PI;
|
||||
}
|
||||
|
||||
std::string getBestAttack (const ESM::Weapon* weapon)
|
||||
{
|
||||
int slash = (weapon->mData.mSlash[0] + weapon->mData.mSlash[1])/2;
|
||||
|
@ -1958,23 +1950,19 @@ void CharacterController::update(float duration, bool animationOnly)
|
|||
|
||||
osg::Vec3f rot = cls.getRotationVector(mPtr);
|
||||
osg::Vec3f vec(movementSettings.asVec3());
|
||||
vec.normalize();
|
||||
|
||||
float analogueMult = 1.0f;
|
||||
if (isPlayer)
|
||||
{
|
||||
// TODO: Move this code to mwinput.
|
||||
// Joystick analogue movement.
|
||||
float xAxis = std::abs(movementSettings.mPosition[0]);
|
||||
float yAxis = std::abs(movementSettings.mPosition[1]);
|
||||
analogueMult = std::max(xAxis, yAxis);
|
||||
movementSettings.mSpeedFactor = std::max(std::abs(vec.x()), std::abs(vec.y()));
|
||||
|
||||
// Due to the half way split between walking/running, we multiply speed by 2 while walking, unless a keyboard was used.
|
||||
if(!isrunning && !sneak && !flying && analogueMult <= 0.5f)
|
||||
analogueMult *= 2.f;
|
||||
|
||||
movementSettings.mSpeedFactor = analogueMult;
|
||||
}
|
||||
if(!isrunning && !sneak && !flying && movementSettings.mSpeedFactor <= 0.5f)
|
||||
movementSettings.mSpeedFactor *= 2.f;
|
||||
} else
|
||||
movementSettings.mSpeedFactor = std::min(vec.length(), 1.f);
|
||||
vec.normalize();
|
||||
|
||||
float effectiveRotation = rot.z();
|
||||
static const bool turnToMovementDirection = Settings::Manager::getBool("turn to movement direction", "Game");
|
||||
|
@ -2007,7 +1995,7 @@ void CharacterController::update(float duration, bool animationOnly)
|
|||
else
|
||||
mAnimation->setUpperBodyYawRadians(stats.getSideMovementAngle() / 4);
|
||||
|
||||
speed = cls.getSpeed(mPtr);
|
||||
speed = cls.getCurrentSpeed(mPtr);
|
||||
vec.x() *= speed;
|
||||
vec.y() *= speed;
|
||||
|
||||
|
@ -2077,7 +2065,7 @@ void CharacterController::update(float duration, bool animationOnly)
|
|||
}
|
||||
}
|
||||
fatigueLoss *= duration;
|
||||
fatigueLoss *= analogueMult;
|
||||
fatigueLoss *= movementSettings.mSpeedFactor;
|
||||
DynamicStat<float> fatigue = cls.getCreatureStats(mPtr).getFatigue();
|
||||
|
||||
if (!godmode)
|
||||
|
@ -2908,13 +2896,10 @@ void CharacterController::updateHeadTracking(float duration)
|
|||
zAngleRadians = std::atan2(direction.x(), direction.y()) - std::atan2(actorDirection.x(), actorDirection.y());
|
||||
xAngleRadians = -std::asin(direction.z());
|
||||
|
||||
wrap(zAngleRadians);
|
||||
wrap(xAngleRadians);
|
||||
|
||||
xAngleRadians = std::min(xAngleRadians, osg::DegreesToRadians(40.f));
|
||||
xAngleRadians = std::max(xAngleRadians, osg::DegreesToRadians(-40.f));
|
||||
zAngleRadians = std::min(zAngleRadians, osg::DegreesToRadians(30.f));
|
||||
zAngleRadians = std::max(zAngleRadians, osg::DegreesToRadians(-30.f));
|
||||
const double xLimit = osg::DegreesToRadians(40.0);
|
||||
const double zLimit = osg::DegreesToRadians(30.0);
|
||||
zAngleRadians = osg::clampBetween(Misc::normalizeAngle(zAngleRadians), -xLimit, xLimit);
|
||||
xAngleRadians = osg::clampBetween(Misc::normalizeAngle(xAngleRadians), -zLimit, zLimit);
|
||||
}
|
||||
|
||||
float factor = duration*5;
|
||||
|
|
|
@ -8,8 +8,14 @@ namespace MWMechanics
|
|||
/// Desired movement for an actor
|
||||
struct Movement
|
||||
{
|
||||
// Desired movement. Direction is relative to the current orientation.
|
||||
// Length of the vector controls desired speed. 0 - stay, 0.5 - half-speed, 1.0 - max speed.
|
||||
float mPosition[3];
|
||||
// Desired rotation delta (euler angles).
|
||||
float mRotation[3];
|
||||
|
||||
// Controlled by CharacterController, should not be changed from other places.
|
||||
// These fields can not be private fields in CharacterController, because Actor::getCurrentSpeed uses it.
|
||||
float mSpeedFactor;
|
||||
bool mIsStrafing;
|
||||
|
||||
|
|
|
@ -122,7 +122,7 @@ namespace MWMechanics
|
|||
|
||||
if (mWalkState != WalkState::Evade)
|
||||
{
|
||||
const float distSameSpot = DIST_SAME_SPOT * actor.getClass().getSpeed(actor) * duration;
|
||||
const float distSameSpot = DIST_SAME_SPOT * actor.getClass().getCurrentSpeed(actor) * duration;
|
||||
const float prevDistance = (destination - mPrev).length();
|
||||
const float currentDistance = (destination - position).length();
|
||||
const float movedDistance = prevDistance - currentDistance;
|
||||
|
|
|
@ -32,7 +32,7 @@ bool smoothTurn(const MWWorld::Ptr& actor, float targetAngleRadians, int axis, f
|
|||
if (absDiff < epsilonRadians)
|
||||
return true;
|
||||
|
||||
float limit = getAngularVelocity(actor.getClass().getSpeed(actor)) * MWBase::Environment::get().getFrameDuration();
|
||||
float limit = getAngularVelocity(actor.getClass().getMaxSpeed(actor)) * MWBase::Environment::get().getFrameDuration();
|
||||
if (absDiff > limit)
|
||||
diff = osg::sign(diff) * limit;
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
#include <osg/Camera>
|
||||
|
||||
#include <components/misc/mathutil.hpp>
|
||||
#include <components/sceneutil/positionattitudetransform.hpp>
|
||||
#include <components/settings/settings.hpp>
|
||||
|
||||
|
@ -200,7 +201,7 @@ namespace MWRender
|
|||
updateFocalPointOffset(duration);
|
||||
updatePosition();
|
||||
|
||||
float speed = mTrackingPtr.getClass().getSpeed(mTrackingPtr);
|
||||
float speed = mTrackingPtr.getClass().getCurrentSpeed(mTrackingPtr);
|
||||
speed /= (1.f + speed / 500.f);
|
||||
float maxDelta = 300.f * duration;
|
||||
mSmoothedSpeed += osg::clampBetween(speed - mSmoothedSpeed, -maxDelta, maxDelta);
|
||||
|
@ -249,7 +250,7 @@ namespace MWRender
|
|||
{
|
||||
if (!mStandingPreviewAllowed)
|
||||
return;
|
||||
float speed = mTrackingPtr.getClass().getSpeed(mTrackingPtr);
|
||||
float speed = mTrackingPtr.getClass().getCurrentSpeed(mTrackingPtr);
|
||||
bool combat = mTrackingPtr.getClass().isActor() &&
|
||||
mTrackingPtr.getClass().getCreatureStats(mTrackingPtr).getDrawState() != MWMechanics::DrawState_Nothing;
|
||||
bool standingStill = speed == 0 && !combat && !mFirstPersonView;
|
||||
|
@ -396,12 +397,7 @@ namespace MWRender
|
|||
|
||||
void Camera::setYaw(float angle)
|
||||
{
|
||||
if (angle > osg::PI) {
|
||||
angle -= osg::PI*2;
|
||||
} else if (angle < -osg::PI) {
|
||||
angle += osg::PI*2;
|
||||
}
|
||||
mYaw = angle;
|
||||
mYaw = Misc::normalizeAngle(angle);
|
||||
}
|
||||
|
||||
void Camera::setPitch(float angle)
|
||||
|
@ -538,16 +534,8 @@ namespace MWRender
|
|||
return;
|
||||
}
|
||||
|
||||
mDeferredRotation.x() = -ptr.getRefData().getPosition().rot[0] - mPitch;
|
||||
mDeferredRotation.z() = -ptr.getRefData().getPosition().rot[2] - mYaw;
|
||||
if (mDeferredRotation.x() > osg::PI)
|
||||
mDeferredRotation.x() -= 2 * osg::PI;
|
||||
if (mDeferredRotation.x() < -osg::PI)
|
||||
mDeferredRotation.x() += 2 * osg::PI;
|
||||
if (mDeferredRotation.z() > osg::PI)
|
||||
mDeferredRotation.z() -= 2 * osg::PI;
|
||||
if (mDeferredRotation.z() < -osg::PI)
|
||||
mDeferredRotation.z() += 2 * osg::PI;
|
||||
mDeferredRotation.x() = Misc::normalizeAngle(-ptr.getRefData().getPosition().rot[0] - mPitch);
|
||||
mDeferredRotation.z() = Misc::normalizeAngle(-ptr.getRefData().getPosition().rot[2] - mYaw);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -159,7 +159,12 @@ namespace MWWorld
|
|||
return "";
|
||||
}
|
||||
|
||||
float Class::getSpeed (const Ptr& ptr) const
|
||||
float Class::getMaxSpeed (const Ptr& ptr) const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
float Class::getCurrentSpeed (const Ptr& ptr) const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -172,8 +172,15 @@ namespace MWWorld
|
|||
///< Return name of the script attached to ptr (default implementation: return an empty
|
||||
/// string).
|
||||
|
||||
virtual float getSpeed (const Ptr& ptr) const;
|
||||
///< Return movement speed.
|
||||
virtual float getWalkSpeed(const Ptr& ptr) const;
|
||||
virtual float getRunSpeed(const Ptr& ptr) const;
|
||||
virtual float getSwimSpeed(const Ptr& ptr) const;
|
||||
|
||||
/// Return maximal movement speed for the current state.
|
||||
virtual float getMaxSpeed(const Ptr& ptr) const;
|
||||
|
||||
/// Return current movement speed.
|
||||
virtual float getCurrentSpeed(const Ptr& ptr) const;
|
||||
|
||||
virtual float getJump(const MWWorld::Ptr &ptr) const;
|
||||
///< Return jump velocity (not accounting for movement)
|
||||
|
@ -182,7 +189,7 @@ namespace MWWorld
|
|||
///< Return desired movement.
|
||||
|
||||
virtual osg::Vec3f getRotationVector (const Ptr& ptr) const;
|
||||
///< Return desired rotations, as euler angles.
|
||||
///< Return desired rotations, as euler angles. Sets getMovementSettings(ptr).mRotation to zero.
|
||||
|
||||
virtual std::pair<std::vector<int>, bool> getEquipmentSlots (const ConstPtr& ptr) const;
|
||||
///< \return first: Return IDs of the slot this object can be equipped in; second: can object
|
||||
|
@ -364,12 +371,6 @@ namespace MWWorld
|
|||
virtual void setBaseAISetting(const std::string& id, MWMechanics::CreatureStats::AiSetting setting, int value) const;
|
||||
|
||||
virtual void modifyBaseInventory(const std::string& actorId, const std::string& itemId, int amount) const;
|
||||
|
||||
virtual float getWalkSpeed(const Ptr& ptr) const;
|
||||
|
||||
virtual float getRunSpeed(const Ptr& ptr) const;
|
||||
|
||||
virtual float getSwimSpeed(const Ptr& ptr) const;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
27
components/misc/mathutil.hpp
Normal file
27
components/misc/mathutil.hpp
Normal file
|
@ -0,0 +1,27 @@
|
|||
#ifndef MISC_MATHUTIL_H
|
||||
#define MISC_MATHUTIL_H
|
||||
|
||||
#include <osg/Math>
|
||||
#include <osg/Vec2f>
|
||||
|
||||
namespace Misc
|
||||
{
|
||||
|
||||
/// Normalizes given angle to the range [-PI, PI]. E.g. PI*3/2 -> -PI/2.
|
||||
inline double normalizeAngle(double angle)
|
||||
{
|
||||
double fullTurns = angle / (2 * osg::PI) + 0.5;
|
||||
return (fullTurns - floor(fullTurns) - 0.5) * (2 * osg::PI);
|
||||
}
|
||||
|
||||
/// Rotates given 2d vector counterclockwise. Angle is in radians.
|
||||
inline osg::Vec2f rotateVec2f(osg::Vec2f vec, float angle)
|
||||
{
|
||||
float s = std::sin(angle);
|
||||
float c = std::cos(angle);
|
||||
return osg::Vec2f(vec.x() * c + vec.y() * -s, vec.x() * s + vec.y() * c);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
Loading…
Reference in a new issue