diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 73fa92529..71a45a92c 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -541,6 +541,8 @@ namespace MWBase /// Resets all actors in the current active cells to their original location within that cell. virtual void resetActors() = 0; + + virtual bool isWalkingOnWater (const MWWorld::Ptr& actor) = 0; }; } diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 6ed11b71f..4fbc55089 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -250,7 +250,6 @@ void RenderingManager::cellAdded (MWWorld::CellStore *store) mObjects->buildStaticGeometry (*store); sh::Factory::getInstance().unloadUnreferencedMaterials(); mDebugging->cellAdded(store); - waterAdded(store); } void RenderingManager::addObject (const MWWorld::Ptr& ptr){ @@ -421,18 +420,12 @@ void RenderingManager::postRenderTargetUpdate(const RenderTargetEvent &evt) mOcclusionQuery->setActive(false); } -void RenderingManager::waterAdded (MWWorld::CellStore *store) +void RenderingManager::setWaterEnabled(bool enable) { - if (store->getCell()->mData.mFlags & ESM::Cell::HasWater) - { - mWater->changeCell (store->getCell()); - mWater->setActive(true); - } - else - removeWater(); + mWater->setActive(enable); } -void RenderingManager::setWaterHeight(const float height) +void RenderingManager::setWaterHeight(float height) { mWater->setHeight(height); } diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index 2da084074..c3eedce7b 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -100,7 +100,6 @@ public: /// \todo this function should be removed later. Instead the rendering subsystems should track /// when rebatching is needed and update automatically at the end of each frame. void cellAdded (MWWorld::CellStore *store); - void waterAdded(MWWorld::CellStore *store); /// Clear all savegame-specific data (i.e. fog of war textures) void clear(); @@ -121,7 +120,8 @@ public: /// Updates an object's rotation void rotateObject (const MWWorld::Ptr& ptr); - void setWaterHeight(const float height); + void setWaterHeight(float height); + void setWaterEnabled(bool enabled); bool toggleWater(); bool toggleWorld(); diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index cec6c6eb1..7c39939ff 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -278,8 +278,10 @@ namespace MWWorld if (!ptr.getClass().canWalk(ptr) && !ptr.getClass().canFly(ptr) && !ptr.getClass().canSwim(ptr)) return position; - /* Anything to collide with? */ OEngine::Physic::PhysicActor *physicActor = engine->getCharacter(ptr.getRefData().getHandle()); + // Reset per-frame data + physicActor->setWalkingOnWater(false); + /* Anything to collide with? */ if(!physicActor || !physicActor->getCollisionMode()) { return position + (Ogre::Quaternion(Ogre::Radian(refpos.rot[2]), Ogre::Vector3::NEGATIVE_UNIT_Z) * @@ -457,6 +459,8 @@ namespace MWWorld { standingCollisionTracker[ptr.getRefData().getHandle()] = body->mName; } + if (standingOn->getBroadphaseHandle()->m_collisionFilterGroup == OEngine::Physic::CollisionType_Water) + physicActor->setWalkingOnWater(true); newPosition.z = tracer.mEndPos.z + 1.0f; @@ -485,7 +489,7 @@ namespace MWWorld PhysicsSystem::PhysicsSystem(OEngine::Render::OgreRenderer &_rend) : - mRender(_rend), mEngine(0), mTimeAccum(0.0f) + mRender(_rend), mEngine(0), mTimeAccum(0.0f), mWaterEnabled(false), mWaterHeight(0) { // Create physics. shapeLoader is deleted by the physic engine NifBullet::ManualBulletShapeLoader* shapeLoader = new NifBullet::ManualBulletShapeLoader(); @@ -494,6 +498,8 @@ namespace MWWorld PhysicsSystem::~PhysicsSystem() { + if (mWaterCollisionObject.get()) + mEngine->mDynamicsWorld->removeCollisionObject(mWaterCollisionObject.get()); delete mEngine; } @@ -855,14 +861,8 @@ namespace MWWorld Ogre::Vector3(iter->first.getRefData().getPosition().pos))) waterCollision = true; - btStaticPlaneShape planeShape(btVector3(0,0,1), waterlevel); - btCollisionObject object; - object.setCollisionShape(&planeShape); - - // TODO: this seems to have a slight performance impact - if (waterCollision) - mEngine->mDynamicsWorld->addCollisionObject(&object, - 0xff, OEngine::Physic::CollisionType_Actor); + OEngine::Physic::PhysicActor *physicActor = mEngine->getCharacter(iter->first.getRefData().getHandle()); + physicActor->setCanWaterWalk(waterCollision); // 100 points of slowfall reduce gravity by 90% (this is just a guess) float slowFall = 1-std::min(std::max(0.f, (effects.get(ESM::MagicEffect::SlowFall).getMagnitude() / 100.f) * 0.9f), 0.9f); @@ -871,9 +871,6 @@ namespace MWWorld world->isFlying(iter->first), waterlevel, slowFall, mEngine, mCollisions, mStandingCollisions); - if (waterCollision) - mEngine->mDynamicsWorld->removeCollisionObject(&object); - float heightDiff = newpos.z - oldHeight; if (heightDiff < 0) @@ -949,4 +946,48 @@ namespace MWWorld } } + void PhysicsSystem::disableWater() + { + if (mWaterEnabled) + { + mWaterEnabled = false; + updateWater(); + } + } + + void PhysicsSystem::enableWater(float height) + { + if (!mWaterEnabled || mWaterHeight != height) + { + mWaterEnabled = true; + mWaterHeight = height; + updateWater(); + } + } + + void PhysicsSystem::setWaterHeight(float height) + { + if (mWaterHeight != height) + { + mWaterHeight = height; + updateWater(); + } + } + + void PhysicsSystem::updateWater() + { + if (mWaterCollisionObject.get()) + { + mEngine->mDynamicsWorld->removeCollisionObject(mWaterCollisionObject.get()); + } + + if (!mWaterEnabled) + return; + + mWaterCollisionObject.reset(new btCollisionObject()); + mWaterCollisionShape.reset(new btStaticPlaneShape(btVector3(0,0,1), mWaterHeight)); + mWaterCollisionObject->setCollisionShape(mWaterCollisionShape.get()); + mEngine->mDynamicsWorld->addCollisionObject(mWaterCollisionObject.get(), OEngine::Physic::CollisionType_Water, + OEngine::Physic::CollisionType_Actor); + } } diff --git a/apps/openmw/mwworld/physicssystem.hpp b/apps/openmw/mwworld/physicssystem.hpp index e66c179b0..7dc8acaa1 100644 --- a/apps/openmw/mwworld/physicssystem.hpp +++ b/apps/openmw/mwworld/physicssystem.hpp @@ -1,6 +1,8 @@ #ifndef GAME_MWWORLD_PHYSICSSYSTEM_H #define GAME_MWWORLD_PHYSICSSYSTEM_H +#include + #include #include @@ -32,6 +34,10 @@ namespace MWWorld PhysicsSystem (OEngine::Render::OgreRenderer &_rend); ~PhysicsSystem (); + void enableWater(float height); + void setWaterHeight(float height); + void disableWater(); + void addObject (const MWWorld::Ptr& ptr, bool placeable=false); void addActor (const MWWorld::Ptr& ptr); @@ -108,6 +114,8 @@ namespace MWWorld private: + void updateWater(); + OEngine::Render::OgreRenderer &mRender; OEngine::Physic::PhysicEngine* mEngine; std::map handleToMesh; @@ -124,6 +132,12 @@ namespace MWWorld float mTimeAccum; + float mWaterHeight; + float mWaterEnabled; + + std::auto_ptr mWaterCollisionObject; + std::auto_ptr mWaterCollisionShape; + PhysicsSystem (const PhysicsSystem&); PhysicsSystem& operator= (const PhysicsSystem&); }; diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 6a25faf21..66c9f5b71 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -238,6 +238,15 @@ namespace MWWorld insertCell (*cell, true, loadingListener); mRendering.cellAdded (cell); + bool waterEnabled = cell->getCell()->hasWater(); + mRendering.setWaterEnabled(waterEnabled); + if (waterEnabled) + { + mPhysics->enableWater(cell->getWaterLevel()); + mRendering.setWaterHeight(cell->getWaterLevel()); + } + else + mPhysics->disableWater(); mRendering.configureAmbient(*cell); } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 28a44d3ba..00ba1a6e2 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1660,6 +1660,7 @@ namespace MWWorld void World::setWaterHeight(const float height) { + mPhysics->setWaterHeight(height); mRendering->setWaterHeight(height); } @@ -3071,4 +3072,12 @@ namespace MWWorld cellstore->forEach(functor); } } + + bool World::isWalkingOnWater(const Ptr &actor) + { + OEngine::Physic::PhysicActor* physicActor = mPhysEngine->getCharacter(actor.getRefData().getHandle()); + if (physicActor && physicActor->isWalkingOnWater()) + return true; + return false; + } } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 1a25edbe5..a05d450d5 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -615,6 +615,8 @@ namespace MWWorld /// Resets all actors in the current active cells to their original location within that cell. virtual void resetActors(); + + virtual bool isWalkingOnWater (const MWWorld::Ptr& actor); }; } diff --git a/libs/openengine/bullet/physic.cpp b/libs/openengine/bullet/physic.cpp index f7c18048e..0c61253bf 100644 --- a/libs/openengine/bullet/physic.cpp +++ b/libs/openengine/bullet/physic.cpp @@ -74,6 +74,8 @@ namespace Physic , mExternalCollisionMode(true) , mForce(0.0f) , mScale(scale) + , mWalkingOnWater(false) + , mCanWaterWalk(false) { if (!NifBullet::getBoundingBox(mMesh, mHalfExtents, mMeshTranslation, mMeshOrientation)) { @@ -103,8 +105,7 @@ namespace Physic setPosition(position); setRotation(rotation); - mEngine->mDynamicsWorld->addRigidBody(mBody, CollisionType_Actor, - CollisionType_Actor|CollisionType_World|CollisionType_HeightMap|CollisionType_Projectile); + updateCollisionMask(); } PhysicActor::~PhysicActor() @@ -123,10 +124,22 @@ namespace Physic void PhysicActor::enableCollisionBody(bool collision) { - assert(mBody); - if(collision && !mExternalCollisionMode) enableCollisionBody(); - if(!collision && mExternalCollisionMode) disableCollisionBody(); - mExternalCollisionMode = collision; + if (mExternalCollisionMode != collision) + { + mExternalCollisionMode = collision; + updateCollisionMask(); + } + } + + void PhysicActor::updateCollisionMask() + { + mEngine->mDynamicsWorld->removeRigidBody(mBody); + int collisionMask = CollisionType_World | CollisionType_HeightMap; + if (mExternalCollisionMode) + collisionMask |= CollisionType_Actor | CollisionType_Projectile; + if (mCanWaterWalk) + collisionMask |= CollisionType_Water; + mEngine->mDynamicsWorld->addRigidBody(mBody, CollisionType_Actor, collisionMask); } const Ogre::Vector3& PhysicActor::getPosition() const @@ -178,18 +191,23 @@ namespace Physic mOnGround = grounded; } - void PhysicActor::disableCollisionBody() + bool PhysicActor::isWalkingOnWater() const { - mEngine->mDynamicsWorld->removeRigidBody(mBody); - mEngine->mDynamicsWorld->addRigidBody(mBody, CollisionType_Actor, - CollisionType_World|CollisionType_HeightMap); + return mWalkingOnWater; } - void PhysicActor::enableCollisionBody() + void PhysicActor::setWalkingOnWater(bool walkingOnWater) { - mEngine->mDynamicsWorld->removeRigidBody(mBody); - mEngine->mDynamicsWorld->addRigidBody(mBody, CollisionType_Actor, - CollisionType_Actor|CollisionType_World|CollisionType_HeightMap|CollisionType_Projectile); + mWalkingOnWater = walkingOnWater; + } + + void PhysicActor::setCanWaterWalk(bool waterWalk) + { + if (waterWalk != mCanWaterWalk) + { + mCanWaterWalk = waterWalk; + updateCollisionMask(); + } } //////////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/libs/openengine/bullet/physic.hpp b/libs/openengine/bullet/physic.hpp index ff799af50..a77b60ab6 100644 --- a/libs/openengine/bullet/physic.hpp +++ b/libs/openengine/bullet/physic.hpp @@ -47,7 +47,8 @@ namespace Physic CollisionType_Actor = 1<<1, // mShape;