diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 946a9a5dd..52697d670 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -270,7 +270,7 @@ namespace MWBase virtual MWWorld::Ptr moveObject (const MWWorld::Ptr& ptr, float x, float y, float z) = 0; ///< @return an updated Ptr in case the Ptr's cell changes - virtual MWWorld::Ptr moveObject(const MWWorld::Ptr &ptr, MWWorld::CellStore* newCell, float x, float y, float z) = 0; + virtual MWWorld::Ptr moveObject(const MWWorld::Ptr &ptr, MWWorld::CellStore* newCell, float x, float y, float z, bool movePhysics=true) = 0; ///< @return an updated Ptr virtual void scaleObject (const MWWorld::Ptr& ptr, float scale) = 0; diff --git a/apps/openmw/mwphysics/actor.cpp b/apps/openmw/mwphysics/actor.cpp index c99754b5c..61022da28 100644 --- a/apps/openmw/mwphysics/actor.cpp +++ b/apps/openmw/mwphysics/actor.cpp @@ -45,8 +45,7 @@ Actor::Actor(const MWWorld::Ptr& ptr, osg::ref_ptr updateRotation(); updateScale(); - // already called by updateScale() - //updatePosition(); + updatePosition(); updateCollisionMask(); } @@ -86,19 +85,44 @@ void Actor::updatePosition() { osg::Vec3f position = mPtr.getRefData().getPosition().asVec3(); + mPosition = position; + mPreviousPosition = position; + + updateCollisionObjectPosition(); +} + +void Actor::updateCollisionObjectPosition() +{ btTransform tr = mCollisionObject->getWorldTransform(); osg::Vec3f scaledTranslation = mRotation * osg::componentMultiply(mMeshTranslation, mScale); - osg::Vec3f newPosition = scaledTranslation + position; - + osg::Vec3f newPosition = scaledTranslation + mPosition; tr.setOrigin(toBullet(newPosition)); mCollisionObject->setWorldTransform(tr); } -osg::Vec3f Actor::getPosition() const +osg::Vec3f Actor::getCollisionObjectPosition() const { return toOsg(mCollisionObject->getWorldTransform().getOrigin()); } +void Actor::setPosition(const osg::Vec3f &position) +{ + mPreviousPosition = mPosition; + + mPosition = position; + updateCollisionObjectPosition(); +} + +osg::Vec3f Actor::getPosition() const +{ + return mPosition; +} + +osg::Vec3f Actor::getPreviousPosition() const +{ + return mPreviousPosition; +} + void Actor::updateRotation () { btTransform tr = mCollisionObject->getWorldTransform(); @@ -106,7 +130,7 @@ void Actor::updateRotation () tr.setRotation(toBullet(mRotation)); mCollisionObject->setWorldTransform(tr); - updatePosition(); + updateCollisionObjectPosition(); } void Actor::updateScale() @@ -122,7 +146,7 @@ void Actor::updateScale() mPtr.getClass().adjustScale(mPtr, scaleVec, true); mRenderingScale = scaleVec; - updatePosition(); + updateCollisionObjectPosition(); } osg::Vec3f Actor::getHalfExtents() const diff --git a/apps/openmw/mwphysics/actor.hpp b/apps/openmw/mwphysics/actor.hpp index 5755def74..b238547e1 100644 --- a/apps/openmw/mwphysics/actor.hpp +++ b/apps/openmw/mwphysics/actor.hpp @@ -68,8 +68,15 @@ namespace MWPhysics void updateScale(); void updateRotation(); + + /** + * Set mPosition and mPreviousPosition to the position in the Ptr's RefData. This should be used + * when an object is "instantly" moved/teleported as opposed to being moved by the physics simulation. + */ void updatePosition(); + void updateCollisionObjectPosition(); + /** * Returns the half extents of the collision body (scaled according to collision scale) */ @@ -79,8 +86,17 @@ namespace MWPhysics * Returns the position of the collision body * @note The collision shape's origin is in its center, so the position returned can be described as center of the actor collision box in world space. */ + osg::Vec3f getCollisionObjectPosition() const; + + /** + * Store the current position into mPreviousPosition, then move to this position. + */ + void setPosition(const osg::Vec3f& position); + osg::Vec3f getPosition() const; + osg::Vec3f getPreviousPosition() const; + /** * Returns the half extents of the collision body (scaled according to rendering scale) * @note The reason we need this extra method is because of an inconsistency in MW - NPC race scales aren't applied to the collision shape, @@ -138,6 +154,7 @@ namespace MWPhysics osg::Vec3f mScale; osg::Vec3f mRenderingScale; osg::Vec3f mPosition; + osg::Vec3f mPreviousPosition; osg::Vec3f mForce; bool mOnGround; diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index 9f1cfc682..ddb3d0c85 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -234,13 +234,11 @@ namespace MWPhysics } } - static osg::Vec3f move(const MWWorld::Ptr &ptr, Actor* physicActor, const osg::Vec3f &movement, float time, + static osg::Vec3f move(osg::Vec3f position, const MWWorld::Ptr &ptr, Actor* physicActor, const osg::Vec3f &movement, float time, bool isFlying, float waterlevel, float slowFall, btCollisionWorld* collisionWorld, std::map& standingCollisionTracker) { const ESM::Position& refpos = ptr.getRefData().getPosition(); - osg::Vec3f position(refpos.asVec3()); - // Early-out for totally static creatures // (Not sure if gravity should still apply?) if (!ptr.getClass().isMobile(ptr)) @@ -944,8 +942,8 @@ namespace MWPhysics if (!physactor1 || !physactor2) return false; - osg::Vec3f pos1 (physactor1->getPosition() + osg::Vec3f(0,0,physactor1->getHalfExtents().z() * 0.8)); // eye level - osg::Vec3f pos2 (physactor2->getPosition() + osg::Vec3f(0,0,physactor2->getHalfExtents().z() * 0.8)); + osg::Vec3f pos1 (physactor1->getCollisionObjectPosition() + osg::Vec3f(0,0,physactor1->getHalfExtents().z() * 0.8)); // eye level + osg::Vec3f pos2 (physactor2->getCollisionObjectPosition() + osg::Vec3f(0,0,physactor2->getHalfExtents().z() * 0.8)); RayResult result = castRay(pos1, pos2, MWWorld::Ptr(), CollisionType_World|CollisionType_HeightMap|CollisionType_Door); @@ -1007,11 +1005,11 @@ namespace MWPhysics return osg::Vec3f(); } - osg::Vec3f PhysicsSystem::getPosition(const MWWorld::ConstPtr &actor) const + osg::Vec3f PhysicsSystem::getCollisionObjectPosition(const MWWorld::ConstPtr &actor) const { const Actor* physactor = getActor(actor); if (physactor) - return physactor->getPosition(); + return physactor->getCollisionObjectPosition(); else return osg::Vec3f(); } @@ -1296,54 +1294,65 @@ namespace MWPhysics mMovementResults.clear(); mTimeAccum += dt; - if(mTimeAccum >= 1.0f/60.0f) + const float physicsDt = 1.f/60.0f; + int numSteps = mTimeAccum / (physicsDt); + mTimeAccum -= numSteps * physicsDt; + + if (numSteps) { // Collision events should be available on every frame mStandingCollisions.clear(); + } - const MWBase::World *world = MWBase::Environment::get().getWorld(); - PtrVelocityList::iterator iter = mMovementQueue.begin(); - for(;iter != mMovementQueue.end();++iter) - { - float waterlevel = -std::numeric_limits::max(); - const MWWorld::CellStore *cell = iter->first.getCell(); - if(cell->getCell()->hasWater()) - waterlevel = cell->getWaterLevel(); + const MWBase::World *world = MWBase::Environment::get().getWorld(); + PtrVelocityList::iterator iter = mMovementQueue.begin(); + for(;iter != mMovementQueue.end();++iter) + { + float waterlevel = -std::numeric_limits::max(); + const MWWorld::CellStore *cell = iter->first.getCell(); + if(cell->getCell()->hasWater()) + waterlevel = cell->getWaterLevel(); - float oldHeight = iter->first.getRefData().getPosition().pos[2]; - const MWMechanics::MagicEffects& effects = iter->first.getClass().getCreatureStats(iter->first).getMagicEffects(); + const MWMechanics::MagicEffects& effects = iter->first.getClass().getCreatureStats(iter->first).getMagicEffects(); - bool waterCollision = false; - if (effects.get(ESM::MagicEffect::WaterWalking).getMagnitude() - && cell->getCell()->hasWater() - && !world->isUnderwater(iter->first.getCell(), - osg::Vec3f(iter->first.getRefData().getPosition().asVec3()))) - waterCollision = true; + bool waterCollision = false; + if (effects.get(ESM::MagicEffect::WaterWalking).getMagnitude() + && cell->getCell()->hasWater() + && !world->isUnderwater(iter->first.getCell(), + osg::Vec3f(iter->first.getRefData().getPosition().asVec3()))) + waterCollision = true; - ActorMap::iterator foundActor = mActors.find(iter->first); - if (foundActor == mActors.end()) // actor was already removed from the scene - continue; - Actor* physicActor = foundActor->second; - physicActor->setCanWaterWalk(waterCollision); + ActorMap::iterator foundActor = mActors.find(iter->first); + if (foundActor == mActors.end()) // actor was already removed from the scene + continue; + Actor* physicActor = foundActor->second; + physicActor->setCanWaterWalk(waterCollision); - // Slow fall reduces fall speed by a factor of (effect magnitude / 200) - float slowFall = 1.f - std::max(0.f, std::min(1.f, effects.get(ESM::MagicEffect::SlowFall).getMagnitude() * 0.005f)); + // Slow fall reduces fall speed by a factor of (effect magnitude / 200) + float slowFall = 1.f - std::max(0.f, std::min(1.f, effects.get(ESM::MagicEffect::SlowFall).getMagnitude() * 0.005f)); - osg::Vec3f newpos = MovementSolver::move(physicActor->getPtr(), physicActor, iter->second, mTimeAccum, - world->isFlying(iter->first), - waterlevel, slowFall, mCollisionWorld, mStandingCollisions); + osg::Vec3f position = physicActor->getPosition(); + float oldHeight = position.z(); + for (int i=0; igetPtr(), physicActor, iter->second, physicsDt, + world->isFlying(iter->first), + waterlevel, slowFall, mCollisionWorld, mStandingCollisions); + physicActor->setPosition(position); + } - float heightDiff = newpos.z() - oldHeight; + float interpolationFactor = mTimeAccum / physicsDt; + osg::Vec3f interpolated = position * interpolationFactor + physicActor->getPreviousPosition() * (1.f - interpolationFactor); - if (heightDiff < 0) - iter->first.getClass().getCreatureStats(iter->first).addToFallHeight(-heightDiff); + float heightDiff = position.z() - oldHeight; - mMovementResults.push_back(std::make_pair(iter->first, newpos)); - } + if (heightDiff < 0) + iter->first.getClass().getCreatureStats(iter->first).addToFallHeight(-heightDiff); - mTimeAccum = 0.0f; + mMovementResults.push_back(std::make_pair(iter->first, interpolated)); } + mMovementQueue.clear(); return mMovementResults; diff --git a/apps/openmw/mwphysics/physicssystem.hpp b/apps/openmw/mwphysics/physicssystem.hpp index 110b59268..62ebf4f0e 100644 --- a/apps/openmw/mwphysics/physicssystem.hpp +++ b/apps/openmw/mwphysics/physicssystem.hpp @@ -129,7 +129,7 @@ namespace MWPhysics /// Get the position of the collision shape for the actor. Use together with getHalfExtents() to get the collision bounds in world space. /// @note The collision shape's origin is in its center, so the position returned can be described as center of the actor collision box in world space. - osg::Vec3f getPosition(const MWWorld::ConstPtr& actor) const; + osg::Vec3f getCollisionObjectPosition(const MWWorld::ConstPtr& actor) const; /// Queues velocity movement for a Ptr. If a Ptr is already queued, its velocity will /// be overwritten. Valid until the next call to applyQueuedMovement. diff --git a/apps/openmw/mwworld/projectilemanager.cpp b/apps/openmw/mwworld/projectilemanager.cpp index b8150ce9c..881530c8a 100644 --- a/apps/openmw/mwworld/projectilemanager.cpp +++ b/apps/openmw/mwworld/projectilemanager.cpp @@ -117,7 +117,7 @@ namespace MWWorld const ESM::EffectList &effects, const Ptr &caster, const std::string &sourceName, const osg::Vec3f& fallbackDirection) { - osg::Vec3f pos = mPhysics->getPosition(caster) + osg::Vec3f(0,0,mPhysics->getHalfExtents(caster).z() * 0.5); // Spawn at 0.75 * ActorHeight + osg::Vec3f pos = mPhysics->getCollisionObjectPosition(caster) + osg::Vec3f(0,0,mPhysics->getHalfExtents(caster).z() * 0.5); // Spawn at 0.75 * ActorHeight if (MWBase::Environment::get().getWorld()->isUnderwater(caster.getCell(), pos)) // Underwater casting not possible return; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 137dac42e..98517f543 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1113,7 +1113,7 @@ namespace MWWorld } } - MWWorld::Ptr World::moveObject(const Ptr &ptr, CellStore* newCell, float x, float y, float z) + MWWorld::Ptr World::moveObject(const Ptr &ptr, CellStore* newCell, float x, float y, float z, bool movePhysics) { ESM::Position pos = ptr.getRefData().getPosition(); @@ -1201,7 +1201,8 @@ namespace MWWorld if (haveToMove && newPtr.getRefData().getBaseNode()) { mRendering->moveObject(newPtr, vec); - mPhysics->updatePosition(newPtr); + if (movePhysics) + mPhysics->updatePosition(newPtr); } if (isPlayer) { @@ -1210,7 +1211,7 @@ namespace MWWorld return newPtr; } - MWWorld::Ptr World::moveObjectImp(const Ptr& ptr, float x, float y, float z) + MWWorld::Ptr World::moveObjectImp(const Ptr& ptr, float x, float y, float z, bool movePhysics) { CellStore *cell = ptr.getCell(); @@ -1221,7 +1222,7 @@ namespace MWWorld cell = getExterior(cellX, cellY); } - return moveObject(ptr, cell, x, y, z); + return moveObject(ptr, cell, x, y, z, movePhysics); } MWWorld::Ptr World::moveObject (const Ptr& ptr, float x, float y, float z) @@ -1373,10 +1374,10 @@ namespace MWWorld player = iter; continue; } - moveObjectImp(iter->first, iter->second.x(), iter->second.y(), iter->second.z()); + moveObjectImp(iter->first, iter->second.x(), iter->second.y(), iter->second.z(), false); } if(player != results.end()) - moveObjectImp(player->first, player->second.x(), player->second.y(), player->second.z()); + moveObjectImp(player->first, player->second.x(), player->second.y(), player->second.z(), false); mPhysics->debugDraw(); } @@ -3206,7 +3207,7 @@ namespace MWWorld osg::Vec3f World::aimToTarget(const ConstPtr &actor, const MWWorld::ConstPtr& target) { osg::Vec3f weaponPos = getActorHeadTransform(actor).getTrans(); - osg::Vec3f targetPos = mPhysics->getPosition(target); + osg::Vec3f targetPos = mPhysics->getCollisionObjectPosition(target); return (targetPos - weaponPos); } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index cf9321da5..6cc5cdc11 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -119,7 +119,7 @@ namespace MWWorld void rotateObjectImp (const Ptr& ptr, const osg::Vec3f& rot, bool adjust); - Ptr moveObjectImp (const Ptr& ptr, float x, float y, float z); + Ptr moveObjectImp (const Ptr& ptr, float x, float y, float z, bool movePhysics=true); ///< @return an updated Ptr in case the Ptr's cell changes Ptr copyObjectToCell(const ConstPtr &ptr, CellStore* cell, ESM::Position pos, int count, bool adjustPos); @@ -355,7 +355,7 @@ namespace MWWorld virtual MWWorld::Ptr moveObject (const Ptr& ptr, float x, float y, float z); ///< @return an updated Ptr in case the Ptr's cell changes - virtual MWWorld::Ptr moveObject (const Ptr& ptr, CellStore* newCell, float x, float y, float z); + virtual MWWorld::Ptr moveObject (const Ptr& ptr, CellStore* newCell, float x, float y, float z, bool movePhysics=true); ///< @return an updated Ptr virtual void scaleObject (const Ptr& ptr, float scale);