diff --git a/apps/openmw/mwphysics/actor.cpp b/apps/openmw/mwphysics/actor.cpp index 0f8814aca4..0688b98695 100644 --- a/apps/openmw/mwphysics/actor.cpp +++ b/apps/openmw/mwphysics/actor.cpp @@ -17,32 +17,29 @@ namespace MWPhysics { -Actor::Actor(const MWWorld::Ptr& ptr, osg::ref_ptr shape, btCollisionWorld* world) +Actor::Actor(const MWWorld::Ptr& ptr, const Resource::BulletShape* shape, btCollisionWorld* world) : mCanWaterWalk(false), mWalkingOnWater(false) - , mCollisionObject(nullptr), mForce(0.f, 0.f, 0.f), mOnGround(true), mOnSlope(false) + , mCollisionObject(nullptr), mMeshTranslation(shape->mCollisionBoxTranslate), mHalfExtents(shape->mCollisionBoxHalfExtents) + , mForce(0.f, 0.f, 0.f), mOnGround(true), mOnSlope(false) , mInternalCollisionMode(true) , mExternalCollisionMode(true) , mCollisionWorld(world) { mPtr = ptr; - mHalfExtents = shape->mCollisionBoxHalfExtents; - mMeshTranslation = shape->mCollisionBoxTranslate; - // We can not create actor without collisions - he will fall through the ground. // In this case we should autogenerate collision box based on mesh shape // (NPCs have bodyparts and use a different approach) if (!ptr.getClass().isNpc() && mHalfExtents.length2() == 0.f) { - const Resource::BulletShape* collisionShape = shape.get(); - if (collisionShape && collisionShape->mCollisionShape) + if (shape->mCollisionShape) { btTransform transform; transform.setIdentity(); btVector3 min; btVector3 max; - collisionShape->mCollisionShape->getAabb(transform, min, max); + shape->mCollisionShape->getAabb(transform, min, max); mHalfExtents.x() = (max[0] - min[0])/2.f; mHalfExtents.y() = (max[1] - min[1])/2.f; mHalfExtents.z() = (max[2] - min[2])/2.f; @@ -83,7 +80,7 @@ Actor::Actor(const MWWorld::Ptr& ptr, osg::ref_ptr Actor::~Actor() { - if (mCollisionObject.get()) + if (mCollisionObject) mCollisionWorld->removeCollisionObject(mCollisionObject.get()); } @@ -112,7 +109,7 @@ void Actor::updateCollisionMask() addCollisionMask(getCollisionMask()); } -int Actor::getCollisionMask() +int Actor::getCollisionMask() const { int collisionMask = CollisionType_World | CollisionType_HeightMap; if (mExternalCollisionMode) @@ -120,7 +117,6 @@ int Actor::getCollisionMask() if (mCanWaterWalk) collisionMask |= CollisionType_Water; return collisionMask; - } void Actor::updatePosition() diff --git a/apps/openmw/mwphysics/actor.hpp b/apps/openmw/mwphysics/actor.hpp index 8752f7feee..a6ff838ad8 100644 --- a/apps/openmw/mwphysics/actor.hpp +++ b/apps/openmw/mwphysics/actor.hpp @@ -21,12 +21,13 @@ namespace Resource namespace MWPhysics { + class PhysicsTaskScheduler; - class Actor : public PtrHolder + class Actor final : public PtrHolder { public: - Actor(const MWWorld::Ptr& ptr, osg::ref_ptr shape, btCollisionWorld* world); - ~Actor(); + Actor(const MWWorld::Ptr& ptr, const Resource::BulletShape* shape, btCollisionWorld* world); + ~Actor() override; /** * Sets the collisionMode for this actor. If disabled, the actor can fly and clip geometry. @@ -136,7 +137,7 @@ namespace MWPhysics /// Removes then re-adds the collision object to the dynamics world void updateCollisionMask(); void addCollisionMask(int collisionMask); - int getCollisionMask(); + int getCollisionMask() const; bool mCanWaterWalk; bool mWalkingOnWater; diff --git a/apps/openmw/mwphysics/object.cpp b/apps/openmw/mwphysics/object.cpp index f95a678235..b21a3e3ca8 100644 --- a/apps/openmw/mwphysics/object.cpp +++ b/apps/openmw/mwphysics/object.cpp @@ -14,9 +14,10 @@ namespace MWPhysics { - Object::Object(const MWWorld::Ptr& ptr, osg::ref_ptr shapeInstance) + Object::Object(const MWWorld::Ptr& ptr, osg::ref_ptr shapeInstance, btCollisionWorld* world) : mShapeInstance(shapeInstance) , mSolid(true) + , mCollisionWorld(world) { mPtr = ptr; @@ -31,6 +32,12 @@ namespace MWPhysics setOrigin(btVector3(pos[0], pos[1], pos[2])); } + Object::~Object() + { + if (mCollisionObject) + mCollisionWorld->removeCollisionObject(mCollisionObject.get()); + } + const Resource::BulletShapeInstance* Object::getShapeInstance() const { return mShapeInstance.get(); diff --git a/apps/openmw/mwphysics/object.hpp b/apps/openmw/mwphysics/object.hpp index b948433e36..87d1b04330 100644 --- a/apps/openmw/mwphysics/object.hpp +++ b/apps/openmw/mwphysics/object.hpp @@ -20,10 +20,11 @@ class btVector3; namespace MWPhysics { - class Object : public PtrHolder + class Object final : public PtrHolder { public: - Object(const MWWorld::Ptr& ptr, osg::ref_ptr shapeInstance); + Object(const MWWorld::Ptr& ptr, osg::ref_ptr shapeInstance, btCollisionWorld* world); + ~Object() override; const Resource::BulletShapeInstance* getShapeInstance() const; void setScale(float scale); @@ -42,6 +43,7 @@ namespace MWPhysics osg::ref_ptr mShapeInstance; std::map mRecIndexToNodePath; bool mSolid; + btCollisionWorld* mCollisionWorld; }; } diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index 07859c1e26..58d96de285 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -65,11 +65,11 @@ namespace MWPhysics { mResourceSystem->addResourceManager(mShapeManager.get()); - mCollisionConfiguration = new btDefaultCollisionConfiguration(); - mDispatcher = new btCollisionDispatcher(mCollisionConfiguration); - mBroadphase = new btDbvtBroadphase(); + mCollisionConfiguration = std::make_unique(); + mDispatcher = std::make_unique(mCollisionConfiguration.get()); + mBroadphase = std::make_unique(); - mCollisionWorld = new btCollisionWorld(mDispatcher, mBroadphase, mCollisionConfiguration); + mCollisionWorld = std::make_shared(mDispatcher.get(), mBroadphase.get(), mCollisionConfiguration.get()); // Don't update AABBs of all objects every frame. Most objects in MW are static, so we don't need this. // Should a "static" object ever be moved, we have to update its AABB manually using DynamicsWorld::updateSingleAabb. @@ -92,7 +92,7 @@ namespace MWPhysics { mResourceSystem->removeResourceManager(mShapeManager.get()); - if (mWaterCollisionObject.get()) + if (mWaterCollisionObject) mCollisionWorld->removeCollisionObject(mWaterCollisionObject.get()); for (auto& heightField : mHeightFields) @@ -101,21 +101,9 @@ namespace MWPhysics delete heightField.second; } - for (auto& object : mObjects) - { - mCollisionWorld->removeCollisionObject(object.second->getCollisionObject()); - delete object.second; - } + mObjects.clear(); + mActors.clear(); - for (auto& actor : mActors) - { - delete actor.second; - } - - delete mCollisionWorld; - delete mCollisionConfiguration; - delete mDispatcher; - delete mBroadphase; } void PhysicsSystem::setUnrefQueue(SceneUtil::UnrefQueue *unrefQueue) @@ -132,13 +120,13 @@ namespace MWPhysics { mDebugDrawEnabled = !mDebugDrawEnabled; - if (mDebugDrawEnabled && !mDebugDrawer.get()) + if (mDebugDrawEnabled && !mDebugDrawer) { - mDebugDrawer.reset(new MWRender::DebugDrawer(mParentNode, mCollisionWorld)); + mDebugDrawer.reset(new MWRender::DebugDrawer(mParentNode, mCollisionWorld.get())); mCollisionWorld->setDebugDrawer(mDebugDrawer.get()); mDebugDrawer->setDebugMode(mDebugDrawEnabled); } - else if (mDebugDrawer.get()) + else if (mDebugDrawer) mDebugDrawer->setDebugMode(mDebugDrawEnabled); return mDebugDrawEnabled; } @@ -175,7 +163,7 @@ namespace MWPhysics std::pair PhysicsSystem::getHitContact(const MWWorld::ConstPtr& actor, const osg::Vec3f &origin, const osg::Quat &orient, - float queryDistance, std::vector targets) + float queryDistance, std::vector& targets) { // First of all, try to hit where you aim to int hitmask = CollisionType_World | CollisionType_Door | CollisionType_HeightMap | CollisionType_Actor; @@ -373,7 +361,7 @@ namespace MWPhysics const osg::Vec3f startingPosition(actorPosition.x(), actorPosition.y(), actorPosition.z() + halfZ); const osg::Vec3f destinationPosition(actorPosition.x(), actorPosition.y(), waterlevel + halfZ); ActorTracer tracer; - tracer.doTrace(physicActor->getCollisionObject(), startingPosition, destinationPosition, mCollisionWorld); + tracer.doTrace(physicActor->getCollisionObject(), startingPosition, destinationPosition, mCollisionWorld.get()); return (tracer.mFraction >= 1.0f); } @@ -444,7 +432,7 @@ namespace MWPhysics if (found == mActors.end()) return ptr.getRefData().getPosition().asVec3(); else - return MovementSolver::traceDown(ptr, position, found->second, mCollisionWorld, maxHeight); + return MovementSolver::traceDown(ptr, position, found->second.get(), mCollisionWorld.get(), maxHeight); } void PhysicsSystem::addHeightField (const float* heights, int x, int y, float triSize, float sqrtVerts, float minH, float maxH, const osg::Object* holdObject) @@ -481,11 +469,11 @@ namespace MWPhysics if (!shapeInstance || !shapeInstance->getCollisionShape()) return; - Object *obj = new Object(ptr, shapeInstance); + auto obj = std::make_shared(ptr, shapeInstance, mCollisionWorld.get()); mObjects.emplace(ptr, obj); if (obj->isAnimated()) - mAnimatedObjects.insert(obj); + mAnimatedObjects.insert(obj.get()); mCollisionWorld->addCollisionObject(obj->getCollisionObject(), collisionType, CollisionType_Actor|CollisionType_HeightMap|CollisionType_Projectile); @@ -496,21 +484,17 @@ namespace MWPhysics ObjectMap::iterator found = mObjects.find(ptr); if (found != mObjects.end()) { - mCollisionWorld->removeCollisionObject(found->second->getCollisionObject()); - if (mUnrefQueue.get()) mUnrefQueue->push(found->second->getShapeInstance()); - mAnimatedObjects.erase(found->second); + mAnimatedObjects.erase(found->second.get()); - delete found->second; mObjects.erase(found); } ActorMap::iterator foundActor = mActors.find(ptr); if (foundActor != mActors.end()) { - delete foundActor->second; mActors.erase(foundActor); } } @@ -536,19 +520,19 @@ namespace MWPhysics ObjectMap::iterator found = mObjects.find(old); if (found != mObjects.end()) { - Object* obj = found->second; + auto obj = found->second; obj->updatePtr(updated); mObjects.erase(found); - mObjects.emplace(updated, obj); + mObjects.emplace(updated, std::move(obj)); } ActorMap::iterator foundActor = mActors.find(old); if (foundActor != mActors.end()) { - Actor* actor = foundActor->second; + auto actor = foundActor->second; actor->updatePtr(updated); mActors.erase(foundActor); - mActors.emplace(updated, actor); + mActors.emplace(updated, std::move(actor)); } updateCollisionMapPtr(mStandingCollisions, old, updated); @@ -558,7 +542,7 @@ namespace MWPhysics { ActorMap::iterator found = mActors.find(ptr); if (found != mActors.end()) - return found->second; + return found->second.get(); return nullptr; } @@ -566,7 +550,7 @@ namespace MWPhysics { ActorMap::const_iterator found = mActors.find(ptr); if (found != mActors.end()) - return found->second; + return found->second.get(); return nullptr; } @@ -574,7 +558,7 @@ namespace MWPhysics { ObjectMap::const_iterator found = mObjects.find(ptr); if (found != mObjects.end()) - return found->second; + return found->second.get(); return nullptr; } @@ -639,11 +623,9 @@ namespace MWPhysics void PhysicsSystem::addActor (const MWWorld::Ptr& ptr, const std::string& mesh) { osg::ref_ptr shape = mShapeManager->getShape(mesh); - if (!shape) - return; // Try to get shape from basic model as fallback for creatures - if (!ptr.getClass().isNpc() && shape->mCollisionBoxHalfExtents.length2() == 0) + if (!ptr.getClass().isNpc() && shape && shape->mCollisionBoxHalfExtents.length2() == 0) { const std::string fallbackModel = ptr.getClass().getModel(ptr); if (fallbackModel != mesh) @@ -652,8 +634,11 @@ namespace MWPhysics } } - Actor* actor = new Actor(ptr, shape, mCollisionWorld); - mActors.emplace(ptr, actor); + if (!shape) + return; + + auto actor = std::make_shared(ptr, shape, mCollisionWorld.get()); + mActors.emplace(ptr, std::move(actor)); } bool PhysicsSystem::toggleCollisionMode() @@ -691,14 +676,14 @@ namespace MWPhysics mStandingCollisions.clear(); } - const PtrVelocityList& PhysicsSystem::applyQueuedMovement(float dt) + const PtrPositionList& PhysicsSystem::applyQueuedMovement(float dt) { mMovementResults.clear(); mTimeAccum += dt; const int maxAllowedSteps = 20; - int numSteps = mTimeAccum / (mPhysicsDt); + int numSteps = mTimeAccum / mPhysicsDt; numSteps = std::min(numSteps, maxAllowedSteps); mTimeAccum -= numSteps * mPhysicsDt; @@ -711,26 +696,28 @@ namespace MWPhysics const MWWorld::Ptr player = MWMechanics::getPlayer(); const MWBase::World *world = MWBase::Environment::get().getWorld(); - for(auto& movementItem : mMovementQueue) + for (const auto& m : mMovementQueue) { - ActorMap::iterator foundActor = mActors.find(movementItem.first); + const auto& character = m.first; + const auto& movement = m.second; + const auto foundActor = mActors.find(character); if (foundActor == mActors.end()) // actor was already removed from the scene continue; - Actor* physicActor = foundActor->second; + auto physicActor = foundActor->second; float waterlevel = -std::numeric_limits::max(); - const MWWorld::CellStore *cell = movementItem.first.getCell(); + const MWWorld::CellStore *cell = character.getCell(); if(cell->getCell()->hasWater()) waterlevel = cell->getWaterLevel(); - const MWMechanics::MagicEffects& effects = movementItem.first.getClass().getCreatureStats(movementItem.first).getMagicEffects(); + const MWMechanics::MagicEffects& effects = character.getClass().getCreatureStats(character).getMagicEffects(); bool waterCollision = false; if (cell->getCell()->hasWater() && effects.get(ESM::MagicEffect::WaterWalking).getMagnitude()) { - if (!world->isUnderwater(movementItem.first.getCell(), osg::Vec3f(movementItem.first.getRefData().getPosition().asVec3()))) + if (!world->isUnderwater(character.getCell(), osg::Vec3f(character.getRefData().getPosition().asVec3()))) waterCollision = true; - else if (physicActor->getCollisionMode() && canMoveToWaterSurface(movementItem.first, waterlevel)) + else if (physicActor->getCollisionMode() && canMoveToWaterSurface(character, waterlevel)) { const osg::Vec3f actorPosition = physicActor->getPosition(); physicActor->setPosition(osg::Vec3f(actorPosition.x(), actorPosition.y(), waterlevel)); @@ -742,8 +729,8 @@ namespace MWPhysics // 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)); - bool flying = world->isFlying(movementItem.first); - bool swimming = world->isSwimming(movementItem.first); + bool flying = world->isFlying(character); + bool swimming = world->isSwimming(character); bool wasOnGround = physicActor->getOnGround(); osg::Vec3f position = physicActor->getPosition(); @@ -751,8 +738,8 @@ namespace MWPhysics bool positionChanged = false; for (int i=0; igetPtr(), physicActor, movementItem.second, mPhysicsDt, - flying, waterlevel, slowFall, mCollisionWorld, mStandingCollisions); + position = MovementSolver::move(position, physicActor->getPtr(), physicActor.get(), movement, mPhysicsDt, + flying, waterlevel, slowFall, mCollisionWorld.get(), mStandingCollisions); if (position != physicActor->getPosition()) positionChanged = true; physicActor->setPosition(position); // always set even if unchanged to make sure interpolation is correct @@ -765,25 +752,23 @@ namespace MWPhysics float heightDiff = position.z() - oldHeight; - MWMechanics::CreatureStats& stats = movementItem.first.getClass().getCreatureStats(movementItem.first); + MWMechanics::CreatureStats& stats = character.getClass().getCreatureStats(character); bool isStillOnGround = (numSteps > 0 && wasOnGround && physicActor->getOnGround()); if (isStillOnGround || flying || swimming || slowFall < 1) - stats.land(movementItem.first == player && (flying || swimming)); + stats.land(character == player && (flying || swimming)); else if (heightDiff < 0) stats.addToFallHeight(-heightDiff); - mMovementResults.emplace_back(movementItem.first, interpolated); + mMovementResults.emplace(character, interpolated); } - mMovementQueue.clear(); - return mMovementResults; } - void PhysicsSystem::stepSimulation(float dt) + void PhysicsSystem::stepSimulation() { - for (Object* animatedObject : mAnimatedObjects) - animatedObject->animateCollisionShapes(mCollisionWorld); + for (Object* animatedObject : mAnimatedObjects) + animatedObject->animateCollisionShapes(mCollisionWorld.get()); #ifndef BT_NO_PROFILE CProfileManager::Reset(); @@ -795,12 +780,12 @@ namespace MWPhysics { ObjectMap::iterator found = mObjects.find(object); if (found != mObjects.end()) - found->second->animateCollisionShapes(mCollisionWorld); + found->second->animateCollisionShapes(mCollisionWorld.get()); } void PhysicsSystem::debugDraw() { - if (mDebugDrawer.get()) + if (mDebugDrawer) mDebugDrawer->step(); } @@ -865,7 +850,7 @@ namespace MWPhysics void PhysicsSystem::updateWater() { - if (mWaterCollisionObject.get()) + if (mWaterCollisionObject) { mCollisionWorld->removeCollisionObject(mWaterCollisionObject.get()); } diff --git a/apps/openmw/mwphysics/physicssystem.hpp b/apps/openmw/mwphysics/physicssystem.hpp index 26005b396d..fd9b52cb6b 100644 --- a/apps/openmw/mwphysics/physicssystem.hpp +++ b/apps/openmw/mwphysics/physicssystem.hpp @@ -47,7 +47,8 @@ class btCollisionShape; namespace MWPhysics { - typedef std::vector > PtrVelocityList; + using PtrPositionList = std::map; + using CollisionMap = std::map; class HeightField; class Object; @@ -93,7 +94,7 @@ namespace MWPhysics bool toggleCollisionMode(); - void stepSimulation(float dt); + void stepSimulation(); void debugDraw(); std::vector getCollisions(const MWWorld::ConstPtr &ptr, int collisionGroup, int collisionMask) const; ///< get handles this object collides with @@ -102,7 +103,7 @@ namespace MWPhysics std::pair getHitContact(const MWWorld::ConstPtr& actor, const osg::Vec3f &origin, const osg::Quat &orientation, - float queryDistance, std::vector targets = std::vector()); + float queryDistance, std::vector& targets); /// Get distance from \a point to the collision shape of \a target. Uses a raycast to find where the @@ -146,7 +147,7 @@ namespace MWPhysics void queueObjectMovement(const MWWorld::Ptr &ptr, const osg::Vec3f &velocity); /// Apply all queued movements, then clear the list. - const PtrVelocityList& applyQueuedMovement(float dt); + const PtrPositionList& applyQueuedMovement(float dt); /// Clear the queued movements list without applying. void clearQueuedMovement(); @@ -192,37 +193,37 @@ namespace MWPhysics osg::ref_ptr mUnrefQueue; - btBroadphaseInterface* mBroadphase; - btDefaultCollisionConfiguration* mCollisionConfiguration; - btCollisionDispatcher* mDispatcher; - btCollisionWorld* mCollisionWorld; + std::unique_ptr mBroadphase; + std::unique_ptr mCollisionConfiguration; + std::unique_ptr mDispatcher; + std::shared_ptr mCollisionWorld; std::unique_ptr mShapeManager; Resource::ResourceSystem* mResourceSystem; - typedef std::map ObjectMap; + using ObjectMap = std::map>; ObjectMap mObjects; std::set mAnimatedObjects; // stores pointers to elements in mObjects - typedef std::map ActorMap; + using ActorMap = std::map>; ActorMap mActors; - typedef std::map, HeightField*> HeightFieldMap; + using HeightFieldMap = std::map, HeightField *>; HeightFieldMap mHeightFields; bool mDebugDrawEnabled; // Tracks standing collisions happening during a single frame. // This will detect standing on an object, but won't detect running e.g. against a wall. - typedef std::map CollisionMap; CollisionMap mStandingCollisions; // replaces all occurrences of 'old' in the map by 'updated', no matter if it's a key or value void updateCollisionMapPtr(CollisionMap& map, const MWWorld::Ptr &old, const MWWorld::Ptr &updated); + using PtrVelocityList = std::vector>; PtrVelocityList mMovementQueue; - PtrVelocityList mMovementResults; + PtrPositionList mMovementResults; float mTimeAccum; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 127fbb45a2..e363fdd3b3 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1494,24 +1494,22 @@ namespace MWWorld void World::doPhysics(float duration) { - mPhysics->stepSimulation(duration); + mPhysics->stepSimulation(); processDoors(duration); mProjectileManager->update(duration); - const MWPhysics::PtrVelocityList &results = mPhysics->applyQueuedMovement(duration); - MWPhysics::PtrVelocityList::const_iterator player(results.end()); - for(MWPhysics::PtrVelocityList::const_iterator iter(results.begin());iter != results.end();++iter) + const auto results = mPhysics->applyQueuedMovement(duration); + + for(const auto& result : results) { - if(iter->first == getPlayerPtr()) - { - // Handle player last, in case a cell transition occurs - player = iter; - continue; - } - moveObjectImp(iter->first, iter->second.x(), iter->second.y(), iter->second.z(), false); + // Handle player last, in case a cell transition occurs + if(result.first != getPlayerPtr()) + moveObjectImp(result.first, result.second.x(), result.second.y(), result.second.z(), false); } - if(player != results.end()) + + const auto player = results.find(getPlayerPtr()); + if (player != results.end()) moveObjectImp(player->first, player->second.x(), player->second.y(), player->second.z(), false); }