From 2c39760bd50284a809ba9fc091d01929e23eb3f1 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 5 Feb 2013 12:45:10 -0800 Subject: [PATCH] Move the movement solver code to mwworld's physics system --- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/mwmechanics/actors.cpp | 1 + apps/openmw/mwmechanics/character.cpp | 26 +--- apps/openmw/mwmechanics/character.hpp | 4 - apps/openmw/mwmechanics/movementsolver.cpp | 164 --------------------- apps/openmw/mwmechanics/movementsolver.hpp | 41 ------ apps/openmw/mwworld/physicssystem.cpp | 152 +++++++++++++++++++ apps/openmw/mwworld/physicssystem.hpp | 2 + apps/openmw/mwworld/worldimp.cpp | 21 +++ 9 files changed, 184 insertions(+), 229 deletions(-) delete mode 100644 apps/openmw/mwmechanics/movementsolver.cpp delete mode 100644 apps/openmw/mwmechanics/movementsolver.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 184a00d509..a491ea5ce0 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -64,7 +64,7 @@ add_openmw_dir (mwclass add_openmw_dir (mwmechanics mechanicsmanagerimp stat character creaturestats magiceffects movement actors activators drawstate spells activespells npcstats aipackage aisequence alchemy aiwander aitravel aifollow - aiescort aiactivate movementsolver + aiescort aiactivate ) add_openmw_dir (mwbase diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index e01303c02c..a8c05f17e3 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -265,6 +265,7 @@ namespace MWMechanics Ogre::Vector3 movement = iter->second.update(duration); mMovement.push_back(std::make_pair(iter->first, movement)); } + MWBase::Environment::get().getWorld()->doPhysics(mMovement, duration); mMovement.clear(); } diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 27c1dc851c..3899b05ab7 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -29,8 +29,6 @@ #include "../mwworld/class.hpp" -#include "movementsolver.hpp" - namespace MWMechanics { @@ -79,7 +77,6 @@ static void getStateInfo(CharacterState state, std::string *group) CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Animation *anim, CharacterState state, bool loop) : mPtr(ptr), mAnimation(anim), mState(state), mSkipAnim(false) { - mMovementSolver = new MovementSolver(); if(!mAnimation) return; @@ -98,7 +95,6 @@ CharacterController::CharacterController(const CharacterController &rhs) , mCurrentGroup(rhs.mCurrentGroup), mState(rhs.mState) , mSkipAnim(rhs.mSkipAnim) { - mMovementSolver = new MovementSolver(); if(!mAnimation) return; /* We've been copied. Update the animation with the new controller. */ @@ -107,7 +103,6 @@ CharacterController::CharacterController(const CharacterController &rhs) CharacterController::~CharacterController() { - delete mMovementSolver; } @@ -181,21 +176,14 @@ Ogre::Vector3 CharacterController::update(float duration) } mSkipAnim = false; - if(duration > 0.0f) - { - const ESM::Position &refpos = mPtr.getRefData().getPosition(); + const ESM::Position &refpos = mPtr.getRefData().getPosition(); + // Rotates first around z, then y, then x + movement = (Ogre::Quaternion(Ogre::Radian(-refpos.rot[0]), Ogre::Vector3::UNIT_X)* + Ogre::Quaternion(Ogre::Radian(-refpos.rot[1]), Ogre::Vector3::UNIT_Y)* + Ogre::Quaternion(Ogre::Radian(-refpos.rot[2]), Ogre::Vector3::UNIT_Z)) * + movement; - // Rotates first around z, then y, then x - movement = (Ogre::Quaternion(Ogre::Radian(-refpos.rot[0]), Ogre::Vector3::UNIT_X)* - Ogre::Quaternion(Ogre::Radian(-refpos.rot[1]), Ogre::Vector3::UNIT_Y)* - Ogre::Quaternion(Ogre::Radian(-refpos.rot[2]), Ogre::Vector3::UNIT_Z)) * - movement; - - Ogre::Vector3 res = mMovementSolver->move(mPtr, movement, duration); - MWBase::Environment::get().getWorld()->moveObject(mPtr, res.x, res.y, res.z); - } - - return Ogre::Vector3(0.0f); + return movement; } diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index efd90ca196..adb6364a0c 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -13,8 +13,6 @@ namespace MWRender namespace MWMechanics { -class MovementSolver; - enum CharacterState { CharState_Idle, CharState_Idle2, @@ -51,8 +49,6 @@ class CharacterController CharacterState mState; bool mSkipAnim; - MovementSolver *mMovementSolver; - protected: /* Called by the animation whenever a new text key is reached. */ void markerEvent(float time, const std::string &evt); diff --git a/apps/openmw/mwmechanics/movementsolver.cpp b/apps/openmw/mwmechanics/movementsolver.cpp deleted file mode 100644 index 544cb2ab88..0000000000 --- a/apps/openmw/mwmechanics/movementsolver.cpp +++ /dev/null @@ -1,164 +0,0 @@ -#include "movementsolver.hpp" - -#include "libs/openengine/bullet/trace.h" -#include "libs/openengine/bullet/physic.hpp" - -#include "../mwworld/ptr.hpp" -#include "../mwbase/environment.hpp" -#include "../mwbase/world.hpp" - -#include - - -namespace MWMechanics -{ - -static const float sMaxSlope = 45.0f; - -MovementSolver::MovementSolver() - : mEngine(MWBase::Environment::get().getWorld()->getPhysicEngine()) -{ -} - -MovementSolver::~MovementSolver() -{ - // nothing to do -} - -void MovementSolver::clipVelocity(const Ogre::Vector3& in, const Ogre::Vector3& normal, Ogre::Vector3& out, const float overbounce) -{ - //Math stuff. Basically just project the velocity vector onto the plane represented by the normal. - //More specifically, it projects velocity onto the normal, takes that result, multiplies it by overbounce and then subtracts it from velocity. - float backoff; - - backoff = in.dotProduct(normal); - if(backoff < 0.0f) - backoff *= overbounce; - else - backoff /= overbounce; - - out = in - (normal*backoff); -} - -void MovementSolver::projectVelocity(Ogre::Vector3& velocity, const Ogre::Vector3& direction) -{ - Ogre::Vector3 normalizedDirection(direction); - normalizedDirection.normalise(); - - // no divide by normalizedDirection.length necessary because it's normalized - velocity = normalizedDirection * velocity.dotProduct(normalizedDirection); -} - -bool MovementSolver::stepMove(Ogre::Vector3& position, const Ogre::Vector3 &velocity, float remainingTime, float verticalRotation, const Ogre::Vector3 &halfExtents, bool isInterior) -{ - traceResults trace; // no initialization needed - - newtrace(&trace, position+Ogre::Vector3(0.0f,0.0f,STEPSIZE), - position+Ogre::Vector3(0.0f,0.0f,STEPSIZE)+velocity*remainingTime, - halfExtents, verticalRotation, isInterior, mEngine); - if(trace.fraction == 0.0f || (trace.fraction != 1.0f && getSlope(trace.planenormal) > sMaxSlope)) - return false; - - newtrace(&trace, trace.endpos, trace.endpos-Ogre::Vector3(0,0,STEPSIZE), halfExtents, verticalRotation, isInterior, mEngine); - if(getSlope(trace.planenormal) < sMaxSlope) - { - // only step down onto semi-horizontal surfaces. don't step down onto the side of a house or a wall. - position = trace.endpos; - return true; - } - - return false; -} - -float MovementSolver::getSlope(const Ogre::Vector3 &normal) -{ - return normal.angleBetween(Ogre::Vector3(0.0f,0.0f,1.0f)).valueDegrees(); -} - - -Ogre::Vector3 MovementSolver::move(const MWWorld::Ptr &ptr, const Ogre::Vector3 &movement, float time) -{ - Ogre::Vector3 position(ptr.getRefData().getPosition().pos); - - /* Anything to collide with? */ - OEngine::Physic::PhysicActor *physicActor = mEngine->getCharacter(ptr.getRefData().getHandle()); - if(!physicActor || !physicActor->getCollisionMode()) - return position + movement; - - traceResults trace; //no initialization needed - int iterations=0, maxIterations=50; //arbitrary number. To prevent infinite loops. They shouldn't happen but it's good to be prepared. - - float verticalVelocity = physicActor->getVerticalForce(); - Ogre::Vector3 horizontalVelocity = movement/time; - Ogre::Vector3 velocity(horizontalVelocity.x, horizontalVelocity.y, verticalVelocity); // we need a copy of the velocity before we start clipping it for steps - Ogre::Vector3 clippedVelocity(horizontalVelocity.x, horizontalVelocity.y, verticalVelocity); - - float remainingTime = time; - bool isInterior = !ptr.getCell()->isExterior(); - float verticalRotation = physicActor->getRotation().getYaw().valueDegrees(); - Ogre::Vector3 halfExtents = physicActor->getHalfExtents(); - - Ogre::Vector3 lastNormal(0.0f); - Ogre::Vector3 currentNormal(0.0f); - Ogre::Vector3 up(0.0f, 0.0f, 1.0f); - Ogre::Vector3 newPosition = position; - - newtrace(&trace, position, position+Ogre::Vector3(0,0,-10), halfExtents, verticalRotation, isInterior, mEngine); - if(trace.fraction < 1.0f) - { - if(getSlope(trace.planenormal) > sMaxSlope) - { - // if we're on a really steep slope, don't listen to user input - clippedVelocity.x = clippedVelocity.y = 0.0f; - } - else - { - // if we're within 10 units of the ground, force velocity to track the ground - clipVelocity(clippedVelocity, trace.planenormal, clippedVelocity, 1.0f); - } - } - - do { - // trace to where character would go if there were no obstructions - newtrace(&trace, newPosition, newPosition+clippedVelocity*remainingTime, halfExtents, verticalRotation, isInterior, mEngine); - newPosition = trace.endpos; - currentNormal = trace.planenormal; - remainingTime = remainingTime * (1.0f-trace.fraction); - - // check for obstructions - if(trace.fraction != 1.0f) - { - //std::cout<<"angle: "< sMaxSlope || currentNormal == lastNormal) - { - if(stepMove(newPosition, velocity, remainingTime, verticalRotation, halfExtents, mEngine)) - std::cout<< "stepped" <setVerticalForce(verticalVelocity); - - return newPosition; -} - -} diff --git a/apps/openmw/mwmechanics/movementsolver.hpp b/apps/openmw/mwmechanics/movementsolver.hpp deleted file mode 100644 index 1c56df0364..0000000000 --- a/apps/openmw/mwmechanics/movementsolver.hpp +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef GAME_MWMECHANICS_MOVEMENTSOLVER_H -#define GAME_MWMECHANICS_MOVEMENTSOLVER_H - -#include - -namespace MWWorld -{ - class Ptr; -} - -namespace OEngine -{ - namespace Physic - { - class PhysicEngine; - } -} - -namespace MWMechanics -{ - class MovementSolver - { - public: - MovementSolver(); - virtual ~MovementSolver(); - - Ogre::Vector3 move(const MWWorld::Ptr &ptr, const Ogre::Vector3 &movement, float time); - - private: - bool stepMove(Ogre::Vector3& position, const Ogre::Vector3 &velocity, float remainingTime, float verticalRotation, const Ogre::Vector3 &halfExtents, bool isInterior); - - void clipVelocity(const Ogre::Vector3& in, const Ogre::Vector3& normal, Ogre::Vector3& out, const float overbounce); - void projectVelocity(Ogre::Vector3& velocity, const Ogre::Vector3& direction); - - float getSlope(const Ogre::Vector3 &normal); - - OEngine::Physic::PhysicEngine *mEngine; - }; -} - -#endif /* GAME_MWMECHANICS_MOVEMENTSOLVER_H */ diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index c95abc5a0f..2d83c9a040 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -21,6 +21,153 @@ using namespace Ogre; namespace MWWorld { + static const float sMaxSlope = 45.0f; + + class MovementSolver + { + private: + static bool stepMove(Ogre::Vector3& position, const Ogre::Vector3 &velocity, float remainingTime, + float verticalRotation, const Ogre::Vector3 &halfExtents, bool isInterior, + OEngine::Physic::PhysicEngine *engine) + { + traceResults trace; // no initialization needed + + newtrace(&trace, position+Ogre::Vector3(0.0f,0.0f,STEPSIZE), + position+Ogre::Vector3(0.0f,0.0f,STEPSIZE)+velocity*remainingTime, + halfExtents, verticalRotation, isInterior, engine); + if(trace.fraction == 0.0f || (trace.fraction != 1.0f && getSlope(trace.planenormal) > sMaxSlope)) + return false; + + newtrace(&trace, trace.endpos, trace.endpos-Ogre::Vector3(0,0,STEPSIZE), halfExtents, verticalRotation, isInterior, engine); + if(getSlope(trace.planenormal) < sMaxSlope) + { + // only step down onto semi-horizontal surfaces. don't step down onto the side of a house or a wall. + position = trace.endpos; + return true; + } + + return false; + } + + static void clipVelocity(const Ogre::Vector3& in, const Ogre::Vector3& normal, Ogre::Vector3& out, + const float overbounce) + { + //Math stuff. Basically just project the velocity vector onto the plane represented by the normal. + //More specifically, it projects velocity onto the normal, takes that result, multiplies it by overbounce and then subtracts it from velocity. + float backoff; + + backoff = in.dotProduct(normal); + if(backoff < 0.0f) + backoff *= overbounce; + else + backoff /= overbounce; + + out = in - (normal*backoff); + } + + static void projectVelocity(Ogre::Vector3& velocity, const Ogre::Vector3& direction) + { + Ogre::Vector3 normalizedDirection(direction); + normalizedDirection.normalise(); + + // no divide by normalizedDirection.length necessary because it's normalized + velocity = normalizedDirection * velocity.dotProduct(normalizedDirection); + } + + static float getSlope(const Ogre::Vector3 &normal) + { + return normal.angleBetween(Ogre::Vector3(0.0f,0.0f,1.0f)).valueDegrees(); + } + + public: + static Ogre::Vector3 move(const MWWorld::Ptr &ptr, const Ogre::Vector3 &movement, float time, + OEngine::Physic::PhysicEngine *engine) + { + Ogre::Vector3 position(ptr.getRefData().getPosition().pos); + + /* Anything to collide with? */ + OEngine::Physic::PhysicActor *physicActor = engine->getCharacter(ptr.getRefData().getHandle()); + if(!physicActor || !physicActor->getCollisionMode()) + return position + movement; + + traceResults trace; //no initialization needed + int iterations=0, maxIterations=50; //arbitrary number. To prevent infinite loops. They shouldn't happen but it's good to be prepared. + + float verticalVelocity = physicActor->getVerticalForce(); + Ogre::Vector3 horizontalVelocity = movement/time; + Ogre::Vector3 velocity(horizontalVelocity.x, horizontalVelocity.y, verticalVelocity); // we need a copy of the velocity before we start clipping it for steps + Ogre::Vector3 clippedVelocity(horizontalVelocity.x, horizontalVelocity.y, verticalVelocity); + + float remainingTime = time; + bool isInterior = !ptr.getCell()->isExterior(); + float verticalRotation = physicActor->getRotation().getYaw().valueDegrees(); + Ogre::Vector3 halfExtents = physicActor->getHalfExtents(); + + Ogre::Vector3 lastNormal(0.0f); + Ogre::Vector3 currentNormal(0.0f); + Ogre::Vector3 up(0.0f, 0.0f, 1.0f); + Ogre::Vector3 newPosition = position; + + newtrace(&trace, position, position+Ogre::Vector3(0,0,-10), halfExtents, verticalRotation, isInterior, engine); + if(trace.fraction < 1.0f) + { + if(getSlope(trace.planenormal) > sMaxSlope) + { + // if we're on a really steep slope, don't listen to user input + clippedVelocity.x = clippedVelocity.y = 0.0f; + } + else + { + // if we're within 10 units of the ground, force velocity to track the ground + clipVelocity(clippedVelocity, trace.planenormal, clippedVelocity, 1.0f); + } + } + + do { + // trace to where character would go if there were no obstructions + newtrace(&trace, newPosition, newPosition+clippedVelocity*remainingTime, halfExtents, verticalRotation, isInterior, engine); + newPosition = trace.endpos; + currentNormal = trace.planenormal; + remainingTime = remainingTime * (1.0f-trace.fraction); + + // check for obstructions + if(trace.fraction != 1.0f) + { + //std::cout<<"angle: "< sMaxSlope || currentNormal == lastNormal) + { + if(stepMove(newPosition, velocity, remainingTime, verticalRotation, halfExtents, isInterior, engine)) + std::cout<< "stepped" <setVerticalForce(verticalVelocity); + + return newPosition; + } + }; + + PhysicsSystem::PhysicsSystem(OEngine::Render::OgreRenderer &_rend) : mRender(_rend), mEngine(0), mFreeFly (true) { @@ -185,6 +332,11 @@ namespace MWWorld } } + Ogre::Vector3 PhysicsSystem::move(const MWWorld::Ptr &ptr, const Ogre::Vector3 &movement, float time) + { + return MovementSolver::move(ptr, movement, time, mEngine); + } + void PhysicsSystem::addHeightField (float* heights, int x, int y, float yoffset, diff --git a/apps/openmw/mwworld/physicssystem.hpp b/apps/openmw/mwworld/physicssystem.hpp index 06b29d2901..d897c78e9e 100644 --- a/apps/openmw/mwworld/physicssystem.hpp +++ b/apps/openmw/mwworld/physicssystem.hpp @@ -35,6 +35,8 @@ namespace MWWorld bool toggleCollisionMode(); + Ogre::Vector3 move(const MWWorld::Ptr &ptr, const Ogre::Vector3 &movement, float time); + std::pair getFacedHandle (MWWorld::World& world, float queryDistance); std::vector < std::pair > getFacedHandles (float queryDistance); std::vector < std::pair > getFacedHandles (float mouseX, float mouseY, float queryDistance); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index d900f555c9..fb31c54ab2 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -836,6 +836,27 @@ namespace MWWorld void World::doPhysics(const PtrMovementList &actors, float duration) { + /* No duration? Shouldn't be any movement, then. */ + if(duration <= 0.0f) + return; + + PtrMovementList::const_iterator player(actors.end()); + for(PtrMovementList::const_iterator iter(actors.begin());iter != actors.end();iter++) + { + if(iter->first.getRefData().getHandle() == "player") + { + /* Handle player last, in case a cell transition occurs */ + player = iter; + continue; + } + Ogre::Vector3 vec = mPhysics->move(iter->first, iter->second, duration); + moveObjectImp(iter->first, vec.x, vec.y, vec.z); + } + if(player != actors.end()) + { + Ogre::Vector3 vec = mPhysics->move(player->first, player->second, duration); + moveObjectImp(player->first, vec.x, vec.y, vec.z); + } } bool World::toggleCollisionMode()