From dc7b48e92ed2ddfaed796df236811bfc5f7005d8 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Wed, 13 Feb 2019 11:30:16 +0400 Subject: [PATCH 1/4] Generate physics collisions for projectiles (bug #3372) Remove redundant now mHit field --- CHANGELOG.md | 1 + apps/openmw/CMakeLists.txt | 4 +- apps/openmw/mwbase/world.hpp | 2 + apps/openmw/mwphysics/actor.cpp | 2 +- .../closestnotmeconvexresultcallback.cpp | 22 +++- .../closestnotmerayresultcallback.cpp | 14 +- .../closestnotmerayresultcallback.hpp | 3 +- apps/openmw/mwphysics/object.cpp | 2 +- apps/openmw/mwphysics/physicssystem.cpp | 54 +++++++- apps/openmw/mwphysics/physicssystem.hpp | 22 +++- apps/openmw/mwphysics/projectile.cpp | 64 +++++++++ apps/openmw/mwphysics/projectile.hpp | 68 ++++++++++ apps/openmw/mwphysics/trace.cpp | 3 + apps/openmw/mwworld/projectilemanager.cpp | 123 ++++++++++++++++-- apps/openmw/mwworld/projectilemanager.hpp | 7 + apps/openmw/mwworld/worldimp.cpp | 5 + apps/openmw/mwworld/worldimp.hpp | 2 + 17 files changed, 372 insertions(+), 26 deletions(-) create mode 100644 apps/openmw/mwphysics/projectile.cpp create mode 100644 apps/openmw/mwphysics/projectile.hpp diff --git a/CHANGELOG.md b/CHANGELOG.md index 2f38ce17f7..e545156760 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ Bug #2473: Unable to overstock merchants Bug #2798: Mutable ESM records Bug #2976 [reopened]: Issues combining settings from the command line and both config files + Bug #3372: Projectiles and magic bolts go through moving targets Bug #3676: NiParticleColorModifier isn't applied properly Bug #3714: Savegame fails to load due to conflict between SpellState and MagicEffects Bug #3789: Crash in visitEffectSources while in battle diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index d943c7836b..6b6134dfb9 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -66,13 +66,13 @@ add_openmw_dir (mwworld cells localscripts customdata inventorystore ptr actionopen actionread actionharvest actionequip timestamp actionalchemy cellstore actionapply actioneat store esmstore recordcmp fallback actionrepair actionsoulgem livecellref actiondoor - contentloader esmloader actiontrap cellreflist cellref physicssystem weather projectilemanager + contentloader esmloader actiontrap cellreflist cellref weather projectilemanager cellpreloader datetimemanager ) add_openmw_dir (mwphysics physicssystem trace collisiontype actor convert object heightfield closestnotmerayresultcallback - contacttestresultcallback deepestnotmecontacttestresultcallback stepper movementsolver + contacttestresultcallback deepestnotmecontacttestresultcallback stepper movementsolver projectile closestnotmeconvexresultcallback raycasting mtphysics ) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index d4f1d2f8ab..c4b3771af0 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -610,6 +610,8 @@ namespace MWBase virtual bool isPlayerInJail() const = 0; + virtual void manualProjectileHit(int projectileId, const MWWorld::Ptr& target, const osg::Vec3f& pos) = 0; + virtual void rest(double hours) = 0; virtual void rechargeItems(double duration, bool activeOnly) = 0; diff --git a/apps/openmw/mwphysics/actor.cpp b/apps/openmw/mwphysics/actor.cpp index 0e557378df..77b07cde53 100644 --- a/apps/openmw/mwphysics/actor.cpp +++ b/apps/openmw/mwphysics/actor.cpp @@ -70,7 +70,7 @@ Actor::Actor(const MWWorld::Ptr& ptr, const Resource::BulletShape* shape, Physic mCollisionObject->setCollisionFlags(btCollisionObject::CF_KINEMATIC_OBJECT); mCollisionObject->setActivationState(DISABLE_DEACTIVATION); mCollisionObject->setCollisionShape(mShape.get()); - mCollisionObject->setUserPointer(static_cast(this)); + mCollisionObject->setUserPointer(this); updateRotation(); updateScale(); diff --git a/apps/openmw/mwphysics/closestnotmeconvexresultcallback.cpp b/apps/openmw/mwphysics/closestnotmeconvexresultcallback.cpp index ddfdb8a42f..3b97a3a341 100644 --- a/apps/openmw/mwphysics/closestnotmeconvexresultcallback.cpp +++ b/apps/openmw/mwphysics/closestnotmeconvexresultcallback.cpp @@ -2,6 +2,14 @@ #include +#include + +#include "../mwbase/world.hpp" +#include "../mwbase/environment.hpp" + +#include "collisiontype.hpp" +#include "projectile.hpp" + namespace MWPhysics { ClosestNotMeConvexResultCallback::ClosestNotMeConvexResultCallback(const btCollisionObject *me, const btVector3 &motion, btScalar minCollisionDot) @@ -10,11 +18,23 @@ namespace MWPhysics { } - btScalar ClosestNotMeConvexResultCallback::addSingleResult(btCollisionWorld::LocalConvexResult& convexResult,bool normalInWorldSpace) + btScalar ClosestNotMeConvexResultCallback::addSingleResult(btCollisionWorld::LocalConvexResult& convexResult, bool normalInWorldSpace) { if (convexResult.m_hitCollisionObject == mMe) return btScalar(1); + if (convexResult.m_hitCollisionObject->getBroadphaseHandle()->m_collisionFilterGroup == CollisionType_Projectile) + { + Projectile* projectileHolder = static_cast(convexResult.m_hitCollisionObject->getUserPointer()); + int projectileId = projectileHolder->getProjectileId(); + PtrHolder* targetHolder = static_cast(mMe->getUserPointer()); + const MWWorld::Ptr target = targetHolder->getPtr(); + + osg::Vec3f pos = Misc::Convert::makeOsgVec3f(convexResult.m_hitPointLocal); + MWBase::Environment::get().getWorld()->manualProjectileHit(projectileId, target, pos); + return btScalar(1); + } + btVector3 hitNormalWorld; if (normalInWorldSpace) hitNormalWorld = convexResult.m_hitNormalLocal; diff --git a/apps/openmw/mwphysics/closestnotmerayresultcallback.cpp b/apps/openmw/mwphysics/closestnotmerayresultcallback.cpp index 86763a7933..43e1d5c628 100644 --- a/apps/openmw/mwphysics/closestnotmerayresultcallback.cpp +++ b/apps/openmw/mwphysics/closestnotmerayresultcallback.cpp @@ -6,13 +6,14 @@ #include "../mwworld/class.hpp" +#include "projectile.hpp" #include "ptrholder.hpp" namespace MWPhysics { - ClosestNotMeRayResultCallback::ClosestNotMeRayResultCallback(const btCollisionObject* me, const std::vector& targets, const btVector3& from, const btVector3& to) + ClosestNotMeRayResultCallback::ClosestNotMeRayResultCallback(const btCollisionObject* me, const std::vector& targets, const btVector3& from, const btVector3& to, int projId) : btCollisionWorld::ClosestRayResultCallback(from, to) - , mMe(me), mTargets(targets) + , mMe(me), mTargets(targets), mProjectileId(projId) { } @@ -29,6 +30,15 @@ namespace MWPhysics return 1.f; } } + + if (mProjectileId >= 0) + { + PtrHolder* holder = static_cast(rayResult.m_collisionObject->getUserPointer()); + Projectile* projectile = dynamic_cast(holder); + if (projectile && projectile->getProjectileId() == mProjectileId) + return 1.f; + } + return btCollisionWorld::ClosestRayResultCallback::addSingleResult(rayResult, normalInWorldSpace); } } diff --git a/apps/openmw/mwphysics/closestnotmerayresultcallback.hpp b/apps/openmw/mwphysics/closestnotmerayresultcallback.hpp index 23d52998ca..d27d64c535 100644 --- a/apps/openmw/mwphysics/closestnotmerayresultcallback.hpp +++ b/apps/openmw/mwphysics/closestnotmerayresultcallback.hpp @@ -12,12 +12,13 @@ namespace MWPhysics class ClosestNotMeRayResultCallback : public btCollisionWorld::ClosestRayResultCallback { public: - ClosestNotMeRayResultCallback(const btCollisionObject* me, const std::vector& targets, const btVector3& from, const btVector3& to); + ClosestNotMeRayResultCallback(const btCollisionObject* me, const std::vector& targets, const btVector3& from, const btVector3& to, int projId=-1); btScalar addSingleResult(btCollisionWorld::LocalRayResult& rayResult, bool normalInWorldSpace) override; private: const btCollisionObject* mMe; const std::vector mTargets; + const int mProjectileId; }; } diff --git a/apps/openmw/mwphysics/object.cpp b/apps/openmw/mwphysics/object.cpp index c822bbcbe0..910f7bf15c 100644 --- a/apps/openmw/mwphysics/object.cpp +++ b/apps/openmw/mwphysics/object.cpp @@ -24,7 +24,7 @@ namespace MWPhysics mCollisionObject.reset(new btCollisionObject); mCollisionObject->setCollisionShape(shapeInstance->getCollisionShape()); - mCollisionObject->setUserPointer(static_cast(this)); + mCollisionObject->setUserPointer(this); setScale(ptr.getCellRef().getScale()); setRotation(Misc::Convert::toBullet(ptr.getRefData().getBaseNode()->getAttitude())); diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index b750970d9c..8933c9d025 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -46,6 +46,8 @@ #include "collisiontype.hpp" #include "actor.hpp" + +#include "projectile.hpp" #include "trace.h" #include "object.hpp" #include "heightfield.hpp" @@ -64,6 +66,7 @@ namespace MWPhysics , mResourceSystem(resourceSystem) , mDebugDrawEnabled(false) , mTimeAccum(0.0f) + , mProjectileId(0) , mWaterHeight(0) , mWaterEnabled(false) , mParentNode(parentNode) @@ -113,6 +116,10 @@ namespace MWPhysics mObjects.clear(); mActors.clear(); + for (ProjectileMap::iterator it = mProjectiles.begin(); it != mProjectiles.end(); ++it) + { + delete it->second; + } } void PhysicsSystem::setUnrefQueue(SceneUtil::UnrefQueue *unrefQueue) @@ -248,7 +255,7 @@ namespace MWPhysics return 0.f; } - RayCastingResult PhysicsSystem::castRay(const osg::Vec3f &from, const osg::Vec3f &to, const MWWorld::ConstPtr& ignore, std::vector targets, int mask, int group) const + PhysicsSystem::RayResult PhysicsSystem::castRay(const osg::Vec3f &from, const osg::Vec3f &to, const MWWorld::ConstPtr& ignore, std::vector targets, int mask, int group, int projId) const { if (from == to) { @@ -285,7 +292,7 @@ namespace MWPhysics } } - ClosestNotMeRayResultCallback resultCallback(me, targetCollisionObjects, btFrom, btTo); + ClosestNotMeRayResultCallback resultCallback(me, targetCollisionObjects, btFrom, btTo, projId); resultCallback.m_collisionFilterGroup = group; resultCallback.m_collisionFilterMask = mask; @@ -298,7 +305,17 @@ namespace MWPhysics result.mHitPos = Misc::Convert::toOsg(resultCallback.m_hitPointWorld); result.mHitNormal = Misc::Convert::toOsg(resultCallback.m_hitNormalWorld); if (PtrHolder* ptrHolder = static_cast(resultCallback.m_collisionObject->getUserPointer())) + { result.mHitObject = ptrHolder->getPtr(); + + if (group == CollisionType_Projectile) + { + Projectile* projectile = static_cast(ptrHolder); + result.mProjectileId = projectile->getProjectileId(); + } + else + result.mProjectileId = -1; + } } return result; } @@ -502,6 +519,19 @@ namespace MWPhysics } } + void PhysicsSystem::removeProjectile(const int projectileId) + { + ProjectileMap::iterator foundProjectile = mProjectiles.find(projectileId); + if (foundProjectile != mProjectiles.end()) + { + delete foundProjectile->second; + mProjectiles.erase(foundProjectile); + + if (projectileId == mProjectileId) + mProjectileId--; + } + } + void PhysicsSystem::updatePtr(const MWWorld::Ptr &old, const MWWorld::Ptr &updated) { ObjectMap::iterator found = mObjects.find(old); @@ -572,6 +602,17 @@ namespace MWPhysics } } + void PhysicsSystem::updateProjectile(const int projectileId, const osg::Vec3f &position) + { + ProjectileMap::iterator foundProjectile = mProjectiles.find(projectileId); + if (foundProjectile != mProjectiles.end()) + { + foundProjectile->second->setPosition(position); + mCollisionWorld->updateSingleAabb(foundProjectile->second->getCollisionObject()); + return; + } + } + void PhysicsSystem::updateRotation(const MWWorld::Ptr &ptr) { ObjectMap::iterator found = mObjects.find(ptr); @@ -632,6 +673,15 @@ namespace MWPhysics mActors.emplace(ptr, std::move(actor)); } + int PhysicsSystem::addProjectile (const osg::Vec3f& position) + { + mProjectileId++; + Projectile* projectile = new Projectile(mProjectileId, position, mCollisionWorld); + mProjectiles.insert(std::make_pair(mProjectileId, projectile)); + + return mProjectileId; + } + bool PhysicsSystem::toggleCollisionMode() { ActorMap::iterator found = mActors.find(MWMechanics::getPlayer()); diff --git a/apps/openmw/mwphysics/physicssystem.hpp b/apps/openmw/mwphysics/physicssystem.hpp index 4137b1e098..4b2aa0384d 100644 --- a/apps/openmw/mwphysics/physicssystem.hpp +++ b/apps/openmw/mwphysics/physicssystem.hpp @@ -56,6 +56,7 @@ namespace MWPhysics class Object; class Actor; class PhysicsTaskScheduler; + class Projectile; using ActorMap = std::map>; @@ -127,6 +128,10 @@ namespace MWPhysics void addObject (const MWWorld::Ptr& ptr, const std::string& mesh, int collisionType = CollisionType_World); void addActor (const MWWorld::Ptr& ptr, const std::string& mesh); + int addProjectile(const osg::Vec3f& position); + void updateProjectile(const int projectileId, const osg::Vec3f &position); + void removeProjectile(const int projectileId); + void updatePtr (const MWWorld::Ptr& old, const MWWorld::Ptr& updated); Actor* getActor(const MWWorld::Ptr& ptr); @@ -141,7 +146,6 @@ namespace MWPhysics void updateRotation (const MWWorld::Ptr& ptr); void updatePosition (const MWWorld::Ptr& ptr); - void addHeightField (const float* heights, int x, int y, float triSize, float sqrtVerts, float minH, float maxH, const osg::Object* holdObject); void removeHeightField (int x, int y); @@ -169,10 +173,19 @@ namespace MWPhysics /// \note Only Actor targets are supported at the moment. float getHitDistance(const osg::Vec3f& point, const MWWorld::ConstPtr& target) const override; + struct RayResult + { + bool mHit; + osg::Vec3f mHitPos; + osg::Vec3f mHitNormal; + MWWorld::Ptr mHitObject; + int mProjectileId; + }; + /// @param me Optional, a Ptr to ignore in the list of results. targets are actors to filter for, ignoring all other actors. RayCastingResult castRay(const osg::Vec3f &from, const osg::Vec3f &to, const MWWorld::ConstPtr& ignore = MWWorld::ConstPtr(), std::vector targets = std::vector(), - int mask = CollisionType_World|CollisionType_HeightMap|CollisionType_Actor|CollisionType_Door, int group=0xff) const override; + int mask = CollisionType_World|CollisionType_HeightMap|CollisionType_Actor|CollisionType_Door, int group=0xff, int projId=-1) const override; RayCastingResult castSphere(const osg::Vec3f& from, const osg::Vec3f& to, float radius) const override; @@ -269,6 +282,9 @@ namespace MWPhysics ActorMap mActors; + using ProjectileMap = std::map; + ProjectileMap mProjectiles; + using HeightFieldMap = std::map, HeightField *>; HeightFieldMap mHeightFields; @@ -279,6 +295,8 @@ namespace MWPhysics float mTimeAccum; + int mProjectileId; + float mWaterHeight; bool mWaterEnabled; diff --git a/apps/openmw/mwphysics/projectile.cpp b/apps/openmw/mwphysics/projectile.cpp new file mode 100644 index 0000000000..f129b8f78c --- /dev/null +++ b/apps/openmw/mwphysics/projectile.cpp @@ -0,0 +1,64 @@ +#include "projectile.hpp" + +#include +#include + +#include +#include +#include +#include + +#include "../mwworld/class.hpp" + +#include "collisiontype.hpp" + +namespace MWPhysics +{ +Projectile::Projectile(int projectileId, const osg::Vec3f& position, btCollisionWorld* world) + : mCollisionWorld(world) +{ + mProjectileId = projectileId; + + mShape.reset(new btSphereShape(1.f)); + mConvexShape = static_cast(mShape.get()); + + mCollisionObject.reset(new btCollisionObject); + mCollisionObject->setCollisionFlags(btCollisionObject::CF_KINEMATIC_OBJECT); + mCollisionObject->setActivationState(DISABLE_DEACTIVATION); + mCollisionObject->setCollisionShape(mShape.get()); + mCollisionObject->setUserPointer(this); + + setPosition(position); + + const int collisionMask = CollisionType_World | CollisionType_HeightMap | + CollisionType_Actor | CollisionType_Door | CollisionType_Water; + mCollisionWorld->addCollisionObject(mCollisionObject.get(), CollisionType_Projectile, collisionMask); +} + +Projectile::~Projectile() +{ + if (mCollisionObject.get()) + mCollisionWorld->removeCollisionObject(mCollisionObject.get()); +} + +void Projectile::updateCollisionObjectPosition() +{ + btTransform tr = mCollisionObject->getWorldTransform(); + // osg::Vec3f scaledTranslation = mRotation * mMeshTranslation; + // osg::Vec3f newPosition = scaledTranslation + mPosition; + tr.setOrigin(Misc::Convert::toBullet(mPosition)); + mCollisionObject->setWorldTransform(tr); +} + +void Projectile::setPosition(const osg::Vec3f &position) +{ + mPosition = position; + updateCollisionObjectPosition(); +} + +osg::Vec3f Projectile::getPosition() const +{ + return mPosition; +} + +} diff --git a/apps/openmw/mwphysics/projectile.hpp b/apps/openmw/mwphysics/projectile.hpp new file mode 100644 index 0000000000..180802555b --- /dev/null +++ b/apps/openmw/mwphysics/projectile.hpp @@ -0,0 +1,68 @@ +#ifndef OPENMW_MWPHYSICS_PROJECTILE_H +#define OPENMW_MWPHYSICS_PROJECTILE_H + +#include + +#include "ptrholder.hpp" + +#include +#include +#include + +class btCollisionWorld; +class btCollisionShape; +class btCollisionObject; +class btConvexShape; + +namespace Resource +{ + class BulletShape; +} + +namespace MWPhysics +{ + class Projectile : public PtrHolder + { + public: + Projectile(const int projectileId, const osg::Vec3f& position, btCollisionWorld* world); + ~Projectile(); + + btConvexShape* getConvexShape() const { return mConvexShape; } + + void updateCollisionObjectPosition(); + + void setPosition(const osg::Vec3f& position); + + osg::Vec3f getPosition() const; + + btCollisionObject* getCollisionObject() const + { + return mCollisionObject.get(); + } + + int getProjectileId() const + { + return mProjectileId; + } + + private: + + std::unique_ptr mShape; + btConvexShape* mConvexShape; + + std::unique_ptr mCollisionObject; + + osg::Vec3f mPosition; + + btCollisionWorld* mCollisionWorld; + + Projectile(const Projectile&); + Projectile& operator=(const Projectile&); + + int mProjectileId; + }; + +} + + +#endif diff --git a/apps/openmw/mwphysics/trace.cpp b/apps/openmw/mwphysics/trace.cpp index 58082f4db2..4d2fccdb7b 100644 --- a/apps/openmw/mwphysics/trace.cpp +++ b/apps/openmw/mwphysics/trace.cpp @@ -5,6 +5,9 @@ #include #include +#include "../mwbase/world.hpp" +#include "../mwbase/environment.hpp" + #include "collisiontype.hpp" #include "actor.hpp" #include "closestnotmeconvexresultcallback.hpp" diff --git a/apps/openmw/mwworld/projectilemanager.cpp b/apps/openmw/mwworld/projectilemanager.cpp index 39cdc3e722..3b61e0522a 100644 --- a/apps/openmw/mwworld/projectilemanager.cpp +++ b/apps/openmw/mwworld/projectilemanager.cpp @@ -302,6 +302,7 @@ namespace MWWorld MWWorld::Ptr ptr = ref.getPtr(); osg::Vec4 lightDiffuseColor = getMagicBoltLightDiffuseColor(state.mEffects); + createModel(state, ptr.getClass().getModel(ptr), pos, orient, true, true, lightDiffuseColor, texture); MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); @@ -312,7 +313,8 @@ namespace MWWorld if (sound) state.mSounds.push_back(sound); } - + + state.mProjectileId = mPhysics->addProjectile(pos); mMagicBolts.push_back(state); } @@ -325,7 +327,6 @@ namespace MWWorld state.mIdArrow = projectile.getCellRef().getRefId(); state.mCasterHandle = actor; state.mAttackStrength = attackStrength; - int type = projectile.get()->mBase->mData.mType; state.mThrown = MWMechanics::getWeaponType(type)->mWeaponClass == ESM::WeaponType::Thrown; @@ -336,6 +337,7 @@ namespace MWWorld if (!ptr.getClass().getEnchantment(ptr).empty()) SceneUtil::addEnchantedGlow(state.mNode, mResourceSystem, ptr.getClass().getEnchantmentColor(ptr)); + state.mProjectileId = mPhysics->addProjectile(pos); mProjectiles.push_back(state); } @@ -416,6 +418,8 @@ namespace MWWorld it->mNode->setPosition(newPos); + mPhysics->updateProjectile(it->mProjectileId, newPos); + update(*it, duration); // For AI actors, get combat targets to use in the ray cast. Only those targets will return a positive hit result. @@ -425,7 +429,7 @@ namespace MWWorld // Check for impact // TODO: use a proper btRigidBody / btGhostObject? - MWPhysics::RayCastingResult result = mPhysics->castRay(pos, newPos, caster, targetActors, 0xff, MWPhysics::CollisionType_Projectile); + MWPhysics::PhysicsSystem::RayResult result = mPhysics->castRay(pos, newPos, caster, targetActors, 0xff, MWPhysics::CollisionType_Projectile, it->mProjectileId); bool hit = false; if (result.mHit) @@ -433,7 +437,7 @@ namespace MWWorld hit = true; if (result.mHitObject.isEmpty()) { - // terrain + // terrain or projectile } else { @@ -456,11 +460,7 @@ namespace MWWorld MWBase::Environment::get().getWorld()->explodeSpell(pos, it->mEffects, caster, result.mHitObject, ESM::RT_Target, it->mSpellId, it->mSourceName); - MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); - for (size_t soundIter = 0; soundIter != it->mSounds.size(); soundIter++) - sndMgr->stopSound(it->mSounds.at(soundIter)); - - mParent->removeChild(it->mNode); + cleanupMagicBolt(*it); it = mMagicBolts.erase(it); continue; @@ -491,6 +491,8 @@ namespace MWWorld it->mNode->setPosition(newPos); + mPhysics->updateProjectile(it->mProjectileId, newPos); + update(*it, duration); MWWorld::Ptr caster = it->getCaster(); @@ -502,15 +504,14 @@ namespace MWWorld // Check for impact // TODO: use a proper btRigidBody / btGhostObject? - MWPhysics::RayCastingResult result = mPhysics->castRay(pos, newPos, caster, targetActors, 0xff, MWPhysics::CollisionType_Projectile); + MWPhysics::PhysicsSystem::RayResult result = mPhysics->castRay(pos, newPos, caster, targetActors, 0xff, MWPhysics::CollisionType_Projectile, it->mProjectileId); bool underwater = MWBase::Environment::get().getWorld()->isUnderwater(MWMechanics::getPlayer().getCell(), newPos); if (result.mHit || underwater) { - MWWorld::ManualRef projectileRef(MWBase::Environment::get().getWorld()->getStore(), it->mIdArrow); - // Try to get a Ptr to the bow that was used. It might no longer exist. + MWWorld::ManualRef projectileRef(MWBase::Environment::get().getWorld()->getStore(), it->mIdArrow); MWWorld::Ptr bow = projectileRef.getPtr(); if (!caster.isEmpty() && it->mIdArrow != it->mBowId) { @@ -529,8 +530,9 @@ namespace MWWorld if (underwater) mRendering->emitWaterRipple(newPos); - mParent->removeChild(it->mNode); + cleanupProjectile(*it); it = mProjectiles.erase(it); + continue; } @@ -538,14 +540,105 @@ namespace MWWorld } } + void ProjectileManager::manualHit(int projectileId, const MWWorld::Ptr& target, const osg::Vec3f& pos) + { + for (std::vector::iterator it = mProjectiles.begin(); it != mProjectiles.end(); ++it) + { + if (it->mProjectileId == projectileId) + { + MWWorld::Ptr caster = it->getCaster(); + if (caster == target) + return; + + if (!isValidTarget(caster, target)) + return; + + if (caster.isEmpty()) + caster = target; + + // Try to get a Ptr to the bow that was used. It might no longer exist. + MWWorld::ManualRef projectileRef(MWBase::Environment::get().getWorld()->getStore(), it->mIdArrow); + MWWorld::Ptr bow = projectileRef.getPtr(); + if (!caster.isEmpty() && it->mIdArrow != it->mBowId) + { + MWWorld::InventoryStore& inv = caster.getClass().getInventoryStore(caster); + MWWorld::ContainerStoreIterator invIt = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); + if (invIt != inv.end() && Misc::StringUtils::ciEqual(invIt->getCellRef().getRefId(), it->mBowId)) + bow = *invIt; + } + + it->mHitPosition = pos; + cleanupProjectile(*it); + MWMechanics::projectileHit(caster, target, bow, projectileRef.getPtr(), pos, it->mAttackStrength); + mProjectiles.erase(it); + return; + } + } + for (std::vector::iterator it = mMagicBolts.begin(); it != mMagicBolts.end(); ++it) + { + if (it->mProjectileId == projectileId) + { + MWWorld::Ptr caster = it->getCaster(); + if (caster == target) + return; + + if (!isValidTarget(caster, target)) + return; + + it->mHitPosition = pos; + cleanupMagicBolt(*it); + + MWMechanics::CastSpell cast(caster, target); + cast.mHitPosition = pos; + cast.mId = it->mSpellId; + cast.mSourceName = it->mSourceName; + cast.mStack = false; + cast.inflict(target, caster, it->mEffects, ESM::RT_Target, false, true); + + MWBase::Environment::get().getWorld()->explodeSpell(pos, it->mEffects, caster, target, ESM::RT_Target, it->mSpellId, it->mSourceName); + mMagicBolts.erase(it); + + return; + } + } + } + + bool ProjectileManager::isValidTarget(const MWWorld::Ptr& caster, const MWWorld::Ptr& target) + { + // For AI actors, get combat targets to use in the ray cast. Only those targets will return a positive hit result. + std::vector targetActors; + if (!caster.isEmpty() && caster.getClass().isActor() && caster != MWMechanics::getPlayer()) + { + caster.getClass().getCreatureStats(caster).getAiSequence().getCombatTargets(targetActors); + if (!targetActors.empty()) + { + bool validTarget = false; + for (MWWorld::Ptr& targetActor : targetActors) + { + if (targetActor == target) + { + validTarget = true; + break; + } + } + + return validTarget; + } + } + + return true; + } + void ProjectileManager::cleanupProjectile(ProjectileManager::ProjectileState& state) { mParent->removeChild(state.mNode); + mPhysics->removeProjectile(state.mProjectileId); } void ProjectileManager::cleanupMagicBolt(ProjectileManager::MagicBoltState& state) { mParent->removeChild(state.mNode); + mPhysics->removeProjectile(state.mProjectileId); for (size_t soundIter = 0; soundIter != state.mSounds.size(); soundIter++) { MWBase::Environment::get().getSoundManager()->stopSound(state.mSounds.at(soundIter)); @@ -626,9 +719,10 @@ namespace MWWorld MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), esm.mId); MWWorld::Ptr ptr = ref.getPtr(); model = ptr.getClass().getModel(ptr); - int weaponType = ptr.get()->mBase->mData.mType; state.mThrown = MWMechanics::getWeaponType(weaponType)->mWeaponClass == ESM::WeaponType::Thrown; + + state.mProjectileId = mPhysics->addProjectile(osg::Vec3f(esm.mPosition)); } catch(...) { @@ -672,6 +766,7 @@ namespace MWWorld MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), state.mIdMagic.at(0)); MWWorld::Ptr ptr = ref.getPtr(); model = ptr.getClass().getModel(ptr); + state.mProjectileId = mPhysics->addProjectile(osg::Vec3f(esm.mPosition)); } catch(...) { diff --git a/apps/openmw/mwworld/projectilemanager.hpp b/apps/openmw/mwworld/projectilemanager.hpp index c7b1056b72..58e0ecbf8f 100644 --- a/apps/openmw/mwworld/projectilemanager.hpp +++ b/apps/openmw/mwworld/projectilemanager.hpp @@ -56,6 +56,8 @@ namespace MWWorld void update(float dt); + void manualHit(int projectileId, const MWWorld::Ptr& target, const osg::Vec3f& pos); + /// Removes all current projectiles. Should be called when switching to a new worldspace. void clear(); @@ -76,6 +78,9 @@ namespace MWWorld std::shared_ptr mEffectAnimationTime; int mActorId; + int mProjectileId; + + osg::Vec3f mHitPosition; // TODO: this will break when the game is saved and reloaded, since there is currently // no way to write identifiers for non-actors to a savegame. @@ -125,6 +130,8 @@ namespace MWWorld void moveProjectiles(float dt); void moveMagicBolts(float dt); + bool isValidTarget(const MWWorld::Ptr& caster, const MWWorld::Ptr& target); + void createModel (State& state, const std::string& model, const osg::Vec3f& pos, const osg::Quat& orient, bool rotate, bool createLight, osg::Vec4 lightDiffuseColor, std::string texture = ""); void update (State& state, float duration); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 49e6a729a8..931791fbc9 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -843,6 +843,11 @@ namespace MWWorld } } + void World::manualProjectileHit(int projectileId, const MWWorld::Ptr& target, const osg::Vec3f& pos) + { + mProjectileManager->manualHit(projectileId, target, pos); + } + void World::disable (const Ptr& reference) { if (!reference.getRefData().isEnabled()) diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 1fc5ebc20d..a913108ad2 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -584,6 +584,8 @@ namespace MWWorld RestPermitted canRest() const override; ///< check if the player is allowed to rest + void manualProjectileHit(int projectileId, const MWWorld::Ptr& target, const osg::Vec3f& pos); + void rest(double hours) override; void rechargeItems(double duration, bool activeOnly) override; From 66fe3b0d38469f280a1f6ec4747b5ed8d889d9bd Mon Sep 17 00:00:00 2001 From: fredzio Date: Fri, 23 Oct 2020 20:27:07 +0200 Subject: [PATCH 2/4] Modify projectile collision to work with async physics --- apps/openmw/mwbase/world.hpp | 2 - .../closestnotmeconvexresultcallback.cpp | 5 +- apps/openmw/mwphysics/mtphysics.cpp | 6 + apps/openmw/mwphysics/physicssystem.cpp | 38 +-- apps/openmw/mwphysics/physicssystem.hpp | 15 +- apps/openmw/mwphysics/projectile.cpp | 58 ++-- apps/openmw/mwphysics/projectile.hpp | 59 +++- apps/openmw/mwphysics/raycasting.hpp | 2 +- apps/openmw/mwworld/projectilemanager.cpp | 257 +++++++++--------- apps/openmw/mwworld/projectilemanager.hpp | 4 +- apps/openmw/mwworld/worldimp.cpp | 6 +- apps/openmw/mwworld/worldimp.hpp | 2 - 12 files changed, 245 insertions(+), 209 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index c4b3771af0..d4f1d2f8ab 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -610,8 +610,6 @@ namespace MWBase virtual bool isPlayerInJail() const = 0; - virtual void manualProjectileHit(int projectileId, const MWWorld::Ptr& target, const osg::Vec3f& pos) = 0; - virtual void rest(double hours) = 0; virtual void rechargeItems(double duration, bool activeOnly) = 0; diff --git a/apps/openmw/mwphysics/closestnotmeconvexresultcallback.cpp b/apps/openmw/mwphysics/closestnotmeconvexresultcallback.cpp index 3b97a3a341..b709811f41 100644 --- a/apps/openmw/mwphysics/closestnotmeconvexresultcallback.cpp +++ b/apps/openmw/mwphysics/closestnotmeconvexresultcallback.cpp @@ -26,12 +26,13 @@ namespace MWPhysics if (convexResult.m_hitCollisionObject->getBroadphaseHandle()->m_collisionFilterGroup == CollisionType_Projectile) { Projectile* projectileHolder = static_cast(convexResult.m_hitCollisionObject->getUserPointer()); - int projectileId = projectileHolder->getProjectileId(); + if (!projectileHolder->isActive()) + return btScalar(1); PtrHolder* targetHolder = static_cast(mMe->getUserPointer()); const MWWorld::Ptr target = targetHolder->getPtr(); osg::Vec3f pos = Misc::Convert::makeOsgVec3f(convexResult.m_hitPointLocal); - MWBase::Environment::get().getWorld()->manualProjectileHit(projectileId, target, pos); + projectileHolder->hit(target, pos); return btScalar(1); } diff --git a/apps/openmw/mwphysics/mtphysics.cpp b/apps/openmw/mwphysics/mtphysics.cpp index 50cfd808ff..a78a307883 100644 --- a/apps/openmw/mwphysics/mtphysics.cpp +++ b/apps/openmw/mwphysics/mtphysics.cpp @@ -17,6 +17,7 @@ #include "mtphysics.hpp" #include "object.hpp" #include "physicssystem.hpp" +#include "projectile.hpp" namespace { @@ -455,6 +456,11 @@ namespace MWPhysics object->commitPositionChange(); mCollisionWorld->updateSingleAabb(object->getCollisionObject()); } + else if (const auto projectile = std::dynamic_pointer_cast(p)) + { + projectile->commitPositionChange(); + mCollisionWorld->updateSingleAabb(projectile->getCollisionObject()); + } }; } diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index 8933c9d025..78010fb0ee 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -115,11 +115,7 @@ namespace MWPhysics mObjects.clear(); mActors.clear(); - - for (ProjectileMap::iterator it = mProjectiles.begin(); it != mProjectiles.end(); ++it) - { - delete it->second; - } + mProjectiles.clear(); } void PhysicsSystem::setUnrefQueue(SceneUtil::UnrefQueue *unrefQueue) @@ -255,7 +251,7 @@ namespace MWPhysics return 0.f; } - PhysicsSystem::RayResult PhysicsSystem::castRay(const osg::Vec3f &from, const osg::Vec3f &to, const MWWorld::ConstPtr& ignore, std::vector targets, int mask, int group, int projId) const + RayCastingResult PhysicsSystem::castRay(const osg::Vec3f &from, const osg::Vec3f &to, const MWWorld::ConstPtr& ignore, std::vector targets, int mask, int group, int projId) const { if (from == to) { @@ -305,17 +301,7 @@ namespace MWPhysics result.mHitPos = Misc::Convert::toOsg(resultCallback.m_hitPointWorld); result.mHitNormal = Misc::Convert::toOsg(resultCallback.m_hitNormalWorld); if (PtrHolder* ptrHolder = static_cast(resultCallback.m_collisionObject->getUserPointer())) - { result.mHitObject = ptrHolder->getPtr(); - - if (group == CollisionType_Projectile) - { - Projectile* projectile = static_cast(ptrHolder); - result.mProjectileId = projectile->getProjectileId(); - } - else - result.mProjectileId = -1; - } } return result; } @@ -523,13 +509,7 @@ namespace MWPhysics { ProjectileMap::iterator foundProjectile = mProjectiles.find(projectileId); if (foundProjectile != mProjectiles.end()) - { - delete foundProjectile->second; mProjectiles.erase(foundProjectile); - - if (projectileId == mProjectileId) - mProjectileId--; - } } void PhysicsSystem::updatePtr(const MWWorld::Ptr &old, const MWWorld::Ptr &updated) @@ -583,6 +563,14 @@ namespace MWPhysics return nullptr; } + Projectile* PhysicsSystem::getProjectile(int projectileId) const + { + ProjectileMap::const_iterator found = mProjectiles.find(projectileId); + if (found != mProjectiles.end()) + return found->second.get(); + return nullptr; + } + void PhysicsSystem::updateScale(const MWWorld::Ptr &ptr) { ObjectMap::iterator found = mObjects.find(ptr); @@ -608,7 +596,7 @@ namespace MWPhysics if (foundProjectile != mProjectiles.end()) { foundProjectile->second->setPosition(position); - mCollisionWorld->updateSingleAabb(foundProjectile->second->getCollisionObject()); + mTaskScheduler->updateSingleAabb(foundProjectile->second); return; } } @@ -676,8 +664,8 @@ namespace MWPhysics int PhysicsSystem::addProjectile (const osg::Vec3f& position) { mProjectileId++; - Projectile* projectile = new Projectile(mProjectileId, position, mCollisionWorld); - mProjectiles.insert(std::make_pair(mProjectileId, projectile)); + auto projectile = std::make_shared(mProjectileId, position, mTaskScheduler.get()); + mProjectiles.emplace(mProjectileId, std::move(projectile)); return mProjectileId; } diff --git a/apps/openmw/mwphysics/physicssystem.hpp b/apps/openmw/mwphysics/physicssystem.hpp index 4b2aa0384d..6721a2d918 100644 --- a/apps/openmw/mwphysics/physicssystem.hpp +++ b/apps/openmw/mwphysics/physicssystem.hpp @@ -139,6 +139,8 @@ namespace MWPhysics const Object* getObject(const MWWorld::ConstPtr& ptr) const; + Projectile* getProjectile(int projectileId) const; + // Object or Actor void remove (const MWWorld::Ptr& ptr); @@ -173,15 +175,6 @@ namespace MWPhysics /// \note Only Actor targets are supported at the moment. float getHitDistance(const osg::Vec3f& point, const MWWorld::ConstPtr& target) const override; - struct RayResult - { - bool mHit; - osg::Vec3f mHitPos; - osg::Vec3f mHitNormal; - MWWorld::Ptr mHitObject; - int mProjectileId; - }; - /// @param me Optional, a Ptr to ignore in the list of results. targets are actors to filter for, ignoring all other actors. RayCastingResult castRay(const osg::Vec3f &from, const osg::Vec3f &to, const MWWorld::ConstPtr& ignore = MWWorld::ConstPtr(), std::vector targets = std::vector(), @@ -282,7 +275,7 @@ namespace MWPhysics ActorMap mActors; - using ProjectileMap = std::map; + using ProjectileMap = std::map>; ProjectileMap mProjectiles; using HeightFieldMap = std::map, HeightField *>; @@ -295,7 +288,7 @@ namespace MWPhysics float mTimeAccum; - int mProjectileId; + unsigned int mProjectileId; float mWaterHeight; bool mWaterEnabled; diff --git a/apps/openmw/mwphysics/projectile.cpp b/apps/openmw/mwphysics/projectile.cpp index f129b8f78c..8abf238cf2 100644 --- a/apps/openmw/mwphysics/projectile.cpp +++ b/apps/openmw/mwphysics/projectile.cpp @@ -1,24 +1,28 @@ -#include "projectile.hpp" +#include #include #include -#include -#include +#include + #include #include +#include +#include #include "../mwworld/class.hpp" #include "collisiontype.hpp" +#include "mtphysics.hpp" +#include "projectile.hpp" namespace MWPhysics { -Projectile::Projectile(int projectileId, const osg::Vec3f& position, btCollisionWorld* world) - : mCollisionWorld(world) +Projectile::Projectile(int projectileId, const osg::Vec3f& position, PhysicsTaskScheduler* scheduler) + : mActive(true) + , mTaskScheduler(scheduler) + , mProjectileId(projectileId) { - mProjectileId = projectileId; - mShape.reset(new btSphereShape(1.f)); mConvexShape = static_cast(mShape.get()); @@ -32,33 +36,47 @@ Projectile::Projectile(int projectileId, const osg::Vec3f& position, btCollision const int collisionMask = CollisionType_World | CollisionType_HeightMap | CollisionType_Actor | CollisionType_Door | CollisionType_Water; - mCollisionWorld->addCollisionObject(mCollisionObject.get(), CollisionType_Projectile, collisionMask); + mTaskScheduler->addCollisionObject(mCollisionObject.get(), CollisionType_Projectile, collisionMask); + + commitPositionChange(); } Projectile::~Projectile() { - if (mCollisionObject.get()) - mCollisionWorld->removeCollisionObject(mCollisionObject.get()); + if (mCollisionObject) + mTaskScheduler->removeCollisionObject(mCollisionObject.get()); } -void Projectile::updateCollisionObjectPosition() +void Projectile::commitPositionChange() { - btTransform tr = mCollisionObject->getWorldTransform(); - // osg::Vec3f scaledTranslation = mRotation * mMeshTranslation; - // osg::Vec3f newPosition = scaledTranslation + mPosition; - tr.setOrigin(Misc::Convert::toBullet(mPosition)); - mCollisionObject->setWorldTransform(tr); + std::unique_lock lock(mPositionMutex); + if (mTransformUpdatePending) + { + mCollisionObject->setWorldTransform(mLocalTransform); + mTransformUpdatePending = false; + } } void Projectile::setPosition(const osg::Vec3f &position) { - mPosition = position; - updateCollisionObjectPosition(); + std::unique_lock lock(mPositionMutex); + mLocalTransform.setOrigin(Misc::Convert::toBullet(position)); + mTransformUpdatePending = true; } -osg::Vec3f Projectile::getPosition() const +void Projectile::hit(MWWorld::Ptr target, osg::Vec3f pos) { - return mPosition; + if (!mActive.load(std::memory_order_acquire)) + return; + std::unique_lock lock(mPositionMutex); + mHitTarget = target; + mHitPosition = pos; + mActive.store(false, std::memory_order_release); } +void Projectile::activate() +{ + assert(!mActive); + mActive.store(true, std::memory_order_release); +} } diff --git a/apps/openmw/mwphysics/projectile.hpp b/apps/openmw/mwphysics/projectile.hpp index 180802555b..7ad0a31246 100644 --- a/apps/openmw/mwphysics/projectile.hpp +++ b/apps/openmw/mwphysics/projectile.hpp @@ -1,18 +1,23 @@ #ifndef OPENMW_MWPHYSICS_PROJECTILE_H #define OPENMW_MWPHYSICS_PROJECTILE_H +#include #include +#include + +#include #include "ptrholder.hpp" -#include -#include -#include - -class btCollisionWorld; -class btCollisionShape; class btCollisionObject; +class btCollisionShape; class btConvexShape; +class btVector3; + +namespace osg +{ + class Vec3f; +} namespace Resource { @@ -21,20 +26,21 @@ namespace Resource namespace MWPhysics { - class Projectile : public PtrHolder + class PhysicsTaskScheduler; + class PhysicsSystem; + + class Projectile final : public PtrHolder { public: - Projectile(const int projectileId, const osg::Vec3f& position, btCollisionWorld* world); - ~Projectile(); + Projectile(const int projectileId, const osg::Vec3f& position, PhysicsTaskScheduler* scheduler); + ~Projectile() override; btConvexShape* getConvexShape() const { return mConvexShape; } - void updateCollisionObjectPosition(); + void commitPositionChange(); void setPosition(const osg::Vec3f& position); - osg::Vec3f getPosition() const; - btCollisionObject* getCollisionObject() const { return mCollisionObject.get(); @@ -45,16 +51,43 @@ namespace MWPhysics return mProjectileId; } + bool isActive() const + { + return mActive.load(std::memory_order_acquire); + } + + MWWorld::Ptr getTarget() const + { + assert(!mActive); + return mHitTarget; + } + + osg::Vec3f getHitPos() const + { + assert(!mActive); + return Misc::Convert::toOsg(mHitPosition); + } + + void hit(MWWorld::Ptr target, osg::Vec3f pos); + void activate(); + private: std::unique_ptr mShape; btConvexShape* mConvexShape; std::unique_ptr mCollisionObject; + btTransform mLocalTransform; + bool mTransformUpdatePending; + std::atomic mActive; + MWWorld::Ptr mHitTarget; + osg::Vec3f mHitPosition; + + mutable std::mutex mPositionMutex; osg::Vec3f mPosition; - btCollisionWorld* mCollisionWorld; + PhysicsTaskScheduler *mTaskScheduler; Projectile(const Projectile&); Projectile& operator=(const Projectile&); diff --git a/apps/openmw/mwphysics/raycasting.hpp b/apps/openmw/mwphysics/raycasting.hpp index 7afbe93214..8ca6965d8e 100644 --- a/apps/openmw/mwphysics/raycasting.hpp +++ b/apps/openmw/mwphysics/raycasting.hpp @@ -29,7 +29,7 @@ namespace MWPhysics /// @param me Optional, a Ptr to ignore in the list of results. targets are actors to filter for, ignoring all other actors. virtual RayCastingResult castRay(const osg::Vec3f &from, const osg::Vec3f &to, const MWWorld::ConstPtr& ignore = MWWorld::ConstPtr(), std::vector targets = std::vector(), - int mask = CollisionType_World|CollisionType_HeightMap|CollisionType_Actor|CollisionType_Door, int group=0xff) const = 0; + int mask = CollisionType_World|CollisionType_HeightMap|CollisionType_Actor|CollisionType_Door, int group=0xff, int projId=-1) const = 0; virtual RayCastingResult castSphere(const osg::Vec3f& from, const osg::Vec3f& to, float radius) const = 0; diff --git a/apps/openmw/mwworld/projectilemanager.cpp b/apps/openmw/mwworld/projectilemanager.cpp index 3b61e0522a..626c57ac32 100644 --- a/apps/openmw/mwworld/projectilemanager.cpp +++ b/apps/openmw/mwworld/projectilemanager.cpp @@ -2,6 +2,7 @@ #include +#include #include #include @@ -43,6 +44,7 @@ #include "../mwsound/sound.hpp" #include "../mwphysics/physicssystem.hpp" +#include "../mwphysics/projectile.hpp" namespace { @@ -284,7 +286,7 @@ namespace MWWorld else state.mActorId = -1; - std::string texture = ""; + std::string texture; state.mEffects = getMagicBoltData(state.mIdMagic, state.mSoundIds, state.mSpeed, texture, state.mSourceName, state.mSpellId); @@ -315,6 +317,7 @@ namespace MWWorld } state.mProjectileId = mPhysics->addProjectile(pos); + state.mToDelete = false; mMagicBolts.push_back(state); } @@ -338,6 +341,7 @@ namespace MWWorld SceneUtil::addEnchantedGlow(state.mNode, mResourceSystem, ptr.getClass().getEnchantmentColor(ptr)); state.mProjectileId = mPhysics->addProjectile(pos); + state.mToDelete = false; mProjectiles.push_back(state); } @@ -362,65 +366,58 @@ namespace MWWorld return (state.mNode->getPosition() - playerPos).length2() >= farawayThreshold*farawayThreshold; }; - for (std::vector::iterator it = mProjectiles.begin(); it != mProjectiles.end();) + for (auto& projectileState : mProjectiles) { - if (isCleanable(*it)) - { - cleanupProjectile(*it); - it = mProjectiles.erase(it); - } - else - ++it; + if (isCleanable(projectileState)) + cleanupProjectile(projectileState); } - for (std::vector::iterator it = mMagicBolts.begin(); it != mMagicBolts.end();) + for (auto& magicBoltState : mMagicBolts) { - if (isCleanable(*it)) - { - cleanupMagicBolt(*it); - it = mMagicBolts.erase(it); - } - else - ++it; + if (isCleanable(magicBoltState)) + cleanupMagicBolt(magicBoltState); } } } void ProjectileManager::moveMagicBolts(float duration) { - for (std::vector::iterator it = mMagicBolts.begin(); it != mMagicBolts.end();) + for (auto& magicBoltState : mMagicBolts) { + if (magicBoltState.mToDelete) + continue; + + const auto* projectile = mPhysics->getProjectile(magicBoltState.mProjectileId); + if (!projectile->isActive()) + continue; // If the actor caster is gone, the magic bolt needs to be removed from the scene during the next frame. - MWWorld::Ptr caster = it->getCaster(); + MWWorld::Ptr caster = magicBoltState.getCaster(); if (!caster.isEmpty() && caster.getClass().isActor()) { if (caster.getRefData().getCount() <= 0 || caster.getClass().getCreatureStats(caster).isDead()) { - cleanupMagicBolt(*it); - it = mMagicBolts.erase(it); + cleanupMagicBolt(magicBoltState); continue; } } - osg::Quat orient = it->mNode->getAttitude(); + osg::Quat orient = magicBoltState.mNode->getAttitude(); static float fTargetSpellMaxSpeed = MWBase::Environment::get().getWorld()->getStore().get() .find("fTargetSpellMaxSpeed")->mValue.getFloat(); - float speed = fTargetSpellMaxSpeed * it->mSpeed; + float speed = fTargetSpellMaxSpeed * magicBoltState.mSpeed; osg::Vec3f direction = orient * osg::Vec3f(0,1,0); direction.normalize(); - osg::Vec3f pos(it->mNode->getPosition()); + osg::Vec3f pos(magicBoltState.mNode->getPosition()); osg::Vec3f newPos = pos + direction * duration * speed; - for (size_t soundIter = 0; soundIter != it->mSounds.size(); soundIter++) - { - it->mSounds.at(soundIter)->setPosition(newPos); - } + for (const auto& sound : magicBoltState.mSounds) + sound->setPosition(newPos); - it->mNode->setPosition(newPos); + magicBoltState.mNode->setPosition(newPos); - mPhysics->updateProjectile(it->mProjectileId, newPos); + mPhysics->updateProjectile(magicBoltState.mProjectileId, newPos); - update(*it, duration); + update(magicBoltState, duration); // For AI actors, get combat targets to use in the ray cast. Only those targets will return a positive hit result. std::vector targetActors; @@ -429,7 +426,7 @@ namespace MWWorld // Check for impact // TODO: use a proper btRigidBody / btGhostObject? - MWPhysics::PhysicsSystem::RayResult result = mPhysics->castRay(pos, newPos, caster, targetActors, 0xff, MWPhysics::CollisionType_Projectile, it->mProjectileId); + const auto result = mPhysics->castRay(pos, newPos, caster, targetActors, 0xff, MWPhysics::CollisionType_Projectile, magicBoltState.mProjectileId); bool hit = false; if (result.mHit) @@ -443,10 +440,10 @@ namespace MWWorld { MWMechanics::CastSpell cast(caster, result.mHitObject); cast.mHitPosition = pos; - cast.mId = it->mSpellId; - cast.mSourceName = it->mSourceName; + cast.mId = magicBoltState.mSpellId; + cast.mSourceName = magicBoltState.mSourceName; cast.mStack = false; - cast.inflict(result.mHitObject, caster, it->mEffects, ESM::RT_Target, false, true); + cast.inflict(result.mHitObject, caster, magicBoltState.mEffects, ESM::RT_Target, false, true); mPhysics->reportCollision(Misc::Convert::toBullet(result.mHitPos), Misc::Convert::toBullet(result.mHitNormal)); } } @@ -457,45 +454,46 @@ namespace MWWorld if (hit) { - MWBase::Environment::get().getWorld()->explodeSpell(pos, it->mEffects, caster, result.mHitObject, - ESM::RT_Target, it->mSpellId, it->mSourceName); + MWBase::Environment::get().getWorld()->explodeSpell(pos, magicBoltState.mEffects, caster, result.mHitObject, + ESM::RT_Target, magicBoltState.mSpellId, magicBoltState.mSourceName); - cleanupMagicBolt(*it); - - it = mMagicBolts.erase(it); - continue; + cleanupMagicBolt(magicBoltState); } - else - ++it; } } void ProjectileManager::moveProjectiles(float duration) { - for (std::vector::iterator it = mProjectiles.begin(); it != mProjectiles.end();) + for (auto& projectileState : mProjectiles) { + if (projectileState.mToDelete) + continue; + + const auto* projectile = mPhysics->getProjectile(projectileState.mProjectileId); + if (!projectile->isActive()) + continue; // gravity constant - must be way lower than the gravity affecting actors, since we're not // simulating aerodynamics at all - it->mVelocity -= osg::Vec3f(0, 0, Constants::GravityConst * Constants::UnitsPerMeter * 0.1f) * duration; + projectileState.mVelocity -= osg::Vec3f(0, 0, Constants::GravityConst * Constants::UnitsPerMeter * 0.1f) * duration; - osg::Vec3f pos(it->mNode->getPosition()); - osg::Vec3f newPos = pos + it->mVelocity * duration; + osg::Vec3f pos(projectileState.mNode->getPosition()); + osg::Vec3f newPos = pos + projectileState.mVelocity * duration; // rotation does not work well for throwing projectiles - their roll angle will depend on shooting direction. - if (!it->mThrown) + if (!projectileState.mThrown) { osg::Quat orient; - orient.makeRotate(osg::Vec3f(0,1,0), it->mVelocity); - it->mNode->setAttitude(orient); + orient.makeRotate(osg::Vec3f(0,1,0), projectileState.mVelocity); + projectileState.mNode->setAttitude(orient); } - it->mNode->setPosition(newPos); + projectileState.mNode->setPosition(newPos); - mPhysics->updateProjectile(it->mProjectileId, newPos); + mPhysics->updateProjectile(projectileState.mProjectileId, newPos); - update(*it, duration); + update(projectileState, duration); - MWWorld::Ptr caster = it->getCaster(); + MWWorld::Ptr caster = projectileState.getCaster(); // For AI actors, get combat targets to use in the ray cast. Only those targets will return a positive hit result. std::vector targetActors; @@ -504,103 +502,107 @@ namespace MWWorld // Check for impact // TODO: use a proper btRigidBody / btGhostObject? - MWPhysics::PhysicsSystem::RayResult result = mPhysics->castRay(pos, newPos, caster, targetActors, 0xff, MWPhysics::CollisionType_Projectile, it->mProjectileId); + const auto result = mPhysics->castRay(pos, newPos, caster, targetActors, 0xff, MWPhysics::CollisionType_Projectile, projectileState.mProjectileId); bool underwater = MWBase::Environment::get().getWorld()->isUnderwater(MWMechanics::getPlayer().getCell(), newPos); if (result.mHit || underwater) { // Try to get a Ptr to the bow that was used. It might no longer exist. - MWWorld::ManualRef projectileRef(MWBase::Environment::get().getWorld()->getStore(), it->mIdArrow); + MWWorld::ManualRef projectileRef(MWBase::Environment::get().getWorld()->getStore(), projectileState.mIdArrow); MWWorld::Ptr bow = projectileRef.getPtr(); - if (!caster.isEmpty() && it->mIdArrow != it->mBowId) + if (!caster.isEmpty() && projectileState.mIdArrow != projectileState.mBowId) { MWWorld::InventoryStore& inv = caster.getClass().getInventoryStore(caster); MWWorld::ContainerStoreIterator invIt = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); - if (invIt != inv.end() && Misc::StringUtils::ciEqual(invIt->getCellRef().getRefId(), it->mBowId)) + if (invIt != inv.end() && Misc::StringUtils::ciEqual(invIt->getCellRef().getRefId(), projectileState.mBowId)) bow = *invIt; } if (caster.isEmpty()) caster = result.mHitObject; - MWMechanics::projectileHit(caster, result.mHitObject, bow, projectileRef.getPtr(), result.mHit ? result.mHitPos : newPos, it->mAttackStrength); + MWMechanics::projectileHit(caster, result.mHitObject, bow, projectileRef.getPtr(), result.mHit ? result.mHitPos : newPos, projectileState.mAttackStrength); mPhysics->reportCollision(Misc::Convert::toBullet(result.mHitPos), Misc::Convert::toBullet(result.mHitNormal)); if (underwater) mRendering->emitWaterRipple(newPos); - cleanupProjectile(*it); - it = mProjectiles.erase(it); - - continue; + cleanupProjectile(projectileState); } - - ++it; } } - void ProjectileManager::manualHit(int projectileId, const MWWorld::Ptr& target, const osg::Vec3f& pos) + void ProjectileManager::processHits() { - for (std::vector::iterator it = mProjectiles.begin(); it != mProjectiles.end(); ++it) + for (auto& projectileState : mProjectiles) { - if (it->mProjectileId == projectileId) + if (projectileState.mToDelete) + continue; + + auto* projectile = mPhysics->getProjectile(projectileState.mProjectileId); + if (projectile->isActive()) + continue; + const auto target = projectile->getTarget(); + const auto pos = projectile->getHitPos(); + MWWorld::Ptr caster = projectileState.getCaster(); + if (caster == target || !isValidTarget(caster, target)) { - MWWorld::Ptr caster = it->getCaster(); - if (caster == target) - return; - - if (!isValidTarget(caster, target)) - return; - - if (caster.isEmpty()) - caster = target; - - // Try to get a Ptr to the bow that was used. It might no longer exist. - MWWorld::ManualRef projectileRef(MWBase::Environment::get().getWorld()->getStore(), it->mIdArrow); - MWWorld::Ptr bow = projectileRef.getPtr(); - if (!caster.isEmpty() && it->mIdArrow != it->mBowId) - { - MWWorld::InventoryStore& inv = caster.getClass().getInventoryStore(caster); - MWWorld::ContainerStoreIterator invIt = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); - if (invIt != inv.end() && Misc::StringUtils::ciEqual(invIt->getCellRef().getRefId(), it->mBowId)) - bow = *invIt; - } - - it->mHitPosition = pos; - cleanupProjectile(*it); - MWMechanics::projectileHit(caster, target, bow, projectileRef.getPtr(), pos, it->mAttackStrength); - mProjectiles.erase(it); - return; + projectile->activate(); + continue; } + + if (caster.isEmpty()) + caster = target; + + // Try to get a Ptr to the bow that was used. It might no longer exist. + MWWorld::ManualRef projectileRef(MWBase::Environment::get().getWorld()->getStore(), projectileState.mIdArrow); + MWWorld::Ptr bow = projectileRef.getPtr(); + if (!caster.isEmpty() && projectileState.mIdArrow != projectileState.mBowId) + { + MWWorld::InventoryStore& inv = caster.getClass().getInventoryStore(caster); + MWWorld::ContainerStoreIterator invIt = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); + if (invIt != inv.end() && Misc::StringUtils::ciEqual(invIt->getCellRef().getRefId(), projectileState.mBowId)) + bow = *invIt; + } + + projectileState.mHitPosition = pos; + cleanupProjectile(projectileState); + MWMechanics::projectileHit(caster, target, bow, projectileRef.getPtr(), pos, projectileState.mAttackStrength); } - for (std::vector::iterator it = mMagicBolts.begin(); it != mMagicBolts.end(); ++it) + for (auto& magicBoltState : mMagicBolts) { - if (it->mProjectileId == projectileId) + if (magicBoltState.mToDelete) + continue; + + auto* projectile = mPhysics->getProjectile(magicBoltState.mProjectileId); + if (projectile->isActive()) + continue; + const auto target = projectile->getTarget(); + const auto pos = projectile->getHitPos(); + MWWorld::Ptr caster = magicBoltState.getCaster(); + if (caster == target || !isValidTarget(caster, target)) { - MWWorld::Ptr caster = it->getCaster(); - if (caster == target) - return; - - if (!isValidTarget(caster, target)) - return; - - it->mHitPosition = pos; - cleanupMagicBolt(*it); - - MWMechanics::CastSpell cast(caster, target); - cast.mHitPosition = pos; - cast.mId = it->mSpellId; - cast.mSourceName = it->mSourceName; - cast.mStack = false; - cast.inflict(target, caster, it->mEffects, ESM::RT_Target, false, true); - - MWBase::Environment::get().getWorld()->explodeSpell(pos, it->mEffects, caster, target, ESM::RT_Target, it->mSpellId, it->mSourceName); - mMagicBolts.erase(it); - - return; + projectile->activate(); + continue; } + + magicBoltState.mHitPosition = pos; + cleanupMagicBolt(magicBoltState); + + MWMechanics::CastSpell cast(caster, target); + cast.mHitPosition = pos; + cast.mId = magicBoltState.mSpellId; + cast.mSourceName = magicBoltState.mSourceName; + cast.mStack = false; + cast.inflict(target, caster, magicBoltState.mEffects, ESM::RT_Target, false, true); + + MWBase::Environment::get().getWorld()->explodeSpell(pos, magicBoltState.mEffects, caster, target, ESM::RT_Target, magicBoltState.mSpellId, magicBoltState.mSourceName); } + mProjectiles.erase(std::remove_if(mProjectiles.begin(), mProjectiles.end(), [](const State& state) { return state.mToDelete; }), + mProjectiles.end()); + mMagicBolts.erase(std::remove_if(mMagicBolts.begin(), mMagicBolts.end(), [](const State& state) { return state.mToDelete; }), + mMagicBolts.end()); } bool ProjectileManager::isValidTarget(const MWWorld::Ptr& caster, const MWWorld::Ptr& target) @@ -633,12 +635,14 @@ namespace MWWorld { mParent->removeChild(state.mNode); mPhysics->removeProjectile(state.mProjectileId); + state.mToDelete = true; } void ProjectileManager::cleanupMagicBolt(ProjectileManager::MagicBoltState& state) { mParent->removeChild(state.mNode); mPhysics->removeProjectile(state.mProjectileId); + state.mToDelete = true; for (size_t soundIter = 0; soundIter != state.mSounds.size(); soundIter++) { MWBase::Environment::get().getSoundManager()->stopSound(state.mSounds.at(soundIter)); @@ -647,15 +651,12 @@ namespace MWWorld void ProjectileManager::clear() { - for (std::vector::iterator it = mProjectiles.begin(); it != mProjectiles.end(); ++it) - { - cleanupProjectile(*it); - } + for (auto& mProjectile : mProjectiles) + cleanupProjectile(mProjectile); mProjectiles.clear(); - for (std::vector::iterator it = mMagicBolts.begin(); it != mMagicBolts.end(); ++it) - { - cleanupMagicBolt(*it); - } + + for (auto& mMagicBolt : mMagicBolts) + cleanupMagicBolt(mMagicBolt); mMagicBolts.clear(); } @@ -712,6 +713,7 @@ namespace MWWorld state.mVelocity = esm.mVelocity; state.mIdArrow = esm.mId; state.mAttackStrength = esm.mAttackStrength; + state.mToDelete = false; std::string model; try @@ -734,7 +736,7 @@ namespace MWWorld mProjectiles.push_back(state); return true; } - else if (type == ESM::REC_MPRJ) + if (type == ESM::REC_MPRJ) { ESM::MagicBoltState esm; esm.load(reader); @@ -743,7 +745,8 @@ namespace MWWorld state.mIdMagic.push_back(esm.mId); state.mSpellId = esm.mSpellId; state.mActorId = esm.mActorId; - std::string texture = ""; + state.mToDelete = false; + std::string texture; try { diff --git a/apps/openmw/mwworld/projectilemanager.hpp b/apps/openmw/mwworld/projectilemanager.hpp index 58e0ecbf8f..d589e985e5 100644 --- a/apps/openmw/mwworld/projectilemanager.hpp +++ b/apps/openmw/mwworld/projectilemanager.hpp @@ -56,7 +56,7 @@ namespace MWWorld void update(float dt); - void manualHit(int projectileId, const MWWorld::Ptr& target, const osg::Vec3f& pos); + void processHits(); /// Removes all current projectiles. Should be called when switching to a new worldspace. void clear(); @@ -93,6 +93,8 @@ namespace MWWorld // MW-id of an arrow projectile std::string mIdArrow; + + bool mToDelete; }; struct MagicBoltState : public State diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 931791fbc9..9a2dd11819 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -843,11 +843,6 @@ namespace MWWorld } } - void World::manualProjectileHit(int projectileId, const MWWorld::Ptr& target, const osg::Vec3f& pos) - { - mProjectileManager->manualHit(projectileId, target, pos); - } - void World::disable (const Ptr& reference) { if (!reference.getRefData().isEnabled()) @@ -1498,6 +1493,7 @@ namespace MWWorld mProjectileManager->update(duration); const auto results = mPhysics->applyQueuedMovement(duration, mDiscardMovements, frameStart, frameNumber, stats); + mProjectileManager->processHits(); mDiscardMovements = false; for(const auto& [actor, position]: results) diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index a913108ad2..1fc5ebc20d 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -584,8 +584,6 @@ namespace MWWorld RestPermitted canRest() const override; ///< check if the player is allowed to rest - void manualProjectileHit(int projectileId, const MWWorld::Ptr& target, const osg::Vec3f& pos); - void rest(double hours) override; void rechargeItems(double duration, bool activeOnly) override; From 7e85235220dfefca9f44f75098c271709e70e001 Mon Sep 17 00:00:00 2001 From: fredzio Date: Sat, 31 Oct 2020 14:01:14 +0100 Subject: [PATCH 3/4] Projectile to projectile collision --- .../closestnotmeconvexresultcallback.cpp | 4 +-- .../closestnotmerayresultcallback.cpp | 28 +++++++++++++------ .../closestnotmerayresultcallback.hpp | 6 ++-- apps/openmw/mwphysics/physicssystem.cpp | 4 +-- apps/openmw/mwphysics/projectile.cpp | 12 ++++++-- apps/openmw/mwphysics/projectile.hpp | 10 ++++--- 6 files changed, 41 insertions(+), 23 deletions(-) diff --git a/apps/openmw/mwphysics/closestnotmeconvexresultcallback.cpp b/apps/openmw/mwphysics/closestnotmeconvexresultcallback.cpp index b709811f41..bbffe16a8e 100644 --- a/apps/openmw/mwphysics/closestnotmeconvexresultcallback.cpp +++ b/apps/openmw/mwphysics/closestnotmeconvexresultcallback.cpp @@ -30,9 +30,7 @@ namespace MWPhysics return btScalar(1); PtrHolder* targetHolder = static_cast(mMe->getUserPointer()); const MWWorld::Ptr target = targetHolder->getPtr(); - - osg::Vec3f pos = Misc::Convert::makeOsgVec3f(convexResult.m_hitPointLocal); - projectileHolder->hit(target, pos); + projectileHolder->hit(target, convexResult.m_hitPointLocal, convexResult.m_hitNormalLocal); return btScalar(1); } diff --git a/apps/openmw/mwphysics/closestnotmerayresultcallback.cpp b/apps/openmw/mwphysics/closestnotmerayresultcallback.cpp index 43e1d5c628..422ca78bd5 100644 --- a/apps/openmw/mwphysics/closestnotmerayresultcallback.cpp +++ b/apps/openmw/mwphysics/closestnotmerayresultcallback.cpp @@ -1,19 +1,22 @@ #include "closestnotmerayresultcallback.hpp" #include +#include #include #include "../mwworld/class.hpp" +#include "actor.hpp" +#include "collisiontype.hpp" #include "projectile.hpp" #include "ptrholder.hpp" namespace MWPhysics { - ClosestNotMeRayResultCallback::ClosestNotMeRayResultCallback(const btCollisionObject* me, const std::vector& targets, const btVector3& from, const btVector3& to, int projId) + ClosestNotMeRayResultCallback::ClosestNotMeRayResultCallback(const btCollisionObject* me, std::vector targets, const btVector3& from, const btVector3& to, Projectile* proj) : btCollisionWorld::ClosestRayResultCallback(from, to) - , mMe(me), mTargets(targets), mProjectileId(projId) + , mMe(me), mTargets(std::move(targets)), mProjectile(proj) { } @@ -25,20 +28,27 @@ namespace MWPhysics { if ((std::find(mTargets.begin(), mTargets.end(), rayResult.m_collisionObject) == mTargets.end())) { - PtrHolder* holder = static_cast(rayResult.m_collisionObject->getUserPointer()); + auto* holder = static_cast(rayResult.m_collisionObject->getUserPointer()); if (holder && !holder->getPtr().isEmpty() && holder->getPtr().getClass().isActor()) return 1.f; } } - if (mProjectileId >= 0) + btCollisionWorld::ClosestRayResultCallback::addSingleResult(rayResult, normalInWorldSpace); + if (mProjectile) { - PtrHolder* holder = static_cast(rayResult.m_collisionObject->getUserPointer()); - Projectile* projectile = dynamic_cast(holder); - if (projectile && projectile->getProjectileId() == mProjectileId) - return 1.f; + auto* holder = static_cast(rayResult.m_collisionObject->getUserPointer()); + if (auto* target = dynamic_cast(holder)) + { + mProjectile->hit(target->getPtr(), m_hitPointWorld, m_hitNormalWorld); + } + else if (auto* target = dynamic_cast(holder)) + { + target->hit(mProjectile->getPtr(), m_hitPointWorld, m_hitNormalWorld); + mProjectile->hit(target->getPtr(), m_hitPointWorld, m_hitNormalWorld); + } } - return btCollisionWorld::ClosestRayResultCallback::addSingleResult(rayResult, normalInWorldSpace); + return rayResult.m_hitFraction; } } diff --git a/apps/openmw/mwphysics/closestnotmerayresultcallback.hpp b/apps/openmw/mwphysics/closestnotmerayresultcallback.hpp index d27d64c535..b86427165a 100644 --- a/apps/openmw/mwphysics/closestnotmerayresultcallback.hpp +++ b/apps/openmw/mwphysics/closestnotmerayresultcallback.hpp @@ -9,16 +9,18 @@ class btCollisionObject; namespace MWPhysics { + class Projectile; + class ClosestNotMeRayResultCallback : public btCollisionWorld::ClosestRayResultCallback { public: - ClosestNotMeRayResultCallback(const btCollisionObject* me, const std::vector& targets, const btVector3& from, const btVector3& to, int projId=-1); + ClosestNotMeRayResultCallback(const btCollisionObject* me, std::vector targets, const btVector3& from, const btVector3& to, Projectile* proj=nullptr); btScalar addSingleResult(btCollisionWorld::LocalRayResult& rayResult, bool normalInWorldSpace) override; private: const btCollisionObject* mMe; const std::vector mTargets; - const int mProjectileId; + Projectile* mProjectile; }; } diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index 78010fb0ee..7da2a69643 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -288,7 +288,7 @@ namespace MWPhysics } } - ClosestNotMeRayResultCallback resultCallback(me, targetCollisionObjects, btFrom, btTo, projId); + ClosestNotMeRayResultCallback resultCallback(me, targetCollisionObjects, btFrom, btTo, getProjectile(projId)); resultCallback.m_collisionFilterGroup = group; resultCallback.m_collisionFilterMask = mask; @@ -664,7 +664,7 @@ namespace MWPhysics int PhysicsSystem::addProjectile (const osg::Vec3f& position) { mProjectileId++; - auto projectile = std::make_shared(mProjectileId, position, mTaskScheduler.get()); + auto projectile = std::make_shared(mProjectileId, position, mTaskScheduler.get(), this); mProjectiles.emplace(mProjectileId, std::move(projectile)); return mProjectileId; diff --git a/apps/openmw/mwphysics/projectile.cpp b/apps/openmw/mwphysics/projectile.cpp index 8abf238cf2..cbc5b19814 100644 --- a/apps/openmw/mwphysics/projectile.cpp +++ b/apps/openmw/mwphysics/projectile.cpp @@ -18,8 +18,9 @@ namespace MWPhysics { -Projectile::Projectile(int projectileId, const osg::Vec3f& position, PhysicsTaskScheduler* scheduler) +Projectile::Projectile(int projectileId, const osg::Vec3f& position, PhysicsTaskScheduler* scheduler, PhysicsSystem* physicssystem) : mActive(true) + , mPhysics(physicssystem) , mTaskScheduler(scheduler) , mProjectileId(projectileId) { @@ -35,7 +36,7 @@ Projectile::Projectile(int projectileId, const osg::Vec3f& position, PhysicsTask setPosition(position); const int collisionMask = CollisionType_World | CollisionType_HeightMap | - CollisionType_Actor | CollisionType_Door | CollisionType_Water; + CollisionType_Actor | CollisionType_Door | CollisionType_Water | CollisionType_Projectile; mTaskScheduler->addCollisionObject(mCollisionObject.get(), CollisionType_Projectile, collisionMask); commitPositionChange(); @@ -44,7 +45,11 @@ Projectile::Projectile(int projectileId, const osg::Vec3f& position, PhysicsTask Projectile::~Projectile() { if (mCollisionObject) + { + if (!mActive) + mPhysics->reportCollision(mHitPosition, mHitNormal); mTaskScheduler->removeCollisionObject(mCollisionObject.get()); + } } void Projectile::commitPositionChange() @@ -64,13 +69,14 @@ void Projectile::setPosition(const osg::Vec3f &position) mTransformUpdatePending = true; } -void Projectile::hit(MWWorld::Ptr target, osg::Vec3f pos) +void Projectile::hit(MWWorld::Ptr target, btVector3 pos, btVector3 normal) { if (!mActive.load(std::memory_order_acquire)) return; std::unique_lock lock(mPositionMutex); mHitTarget = target; mHitPosition = pos; + mHitNormal = normal; mActive.store(false, std::memory_order_release); } diff --git a/apps/openmw/mwphysics/projectile.hpp b/apps/openmw/mwphysics/projectile.hpp index 7ad0a31246..6e8aba60b8 100644 --- a/apps/openmw/mwphysics/projectile.hpp +++ b/apps/openmw/mwphysics/projectile.hpp @@ -5,7 +5,7 @@ #include #include -#include +#include "components/misc/convert.hpp" #include "ptrholder.hpp" @@ -32,7 +32,7 @@ namespace MWPhysics class Projectile final : public PtrHolder { public: - Projectile(const int projectileId, const osg::Vec3f& position, PhysicsTaskScheduler* scheduler); + Projectile(const int projectileId, const osg::Vec3f& position, PhysicsTaskScheduler* scheduler, PhysicsSystem* physicssystem); ~Projectile() override; btConvexShape* getConvexShape() const { return mConvexShape; } @@ -68,7 +68,7 @@ namespace MWPhysics return Misc::Convert::toOsg(mHitPosition); } - void hit(MWWorld::Ptr target, osg::Vec3f pos); + void hit(MWWorld::Ptr target, btVector3 pos, btVector3 normal); void activate(); private: @@ -81,12 +81,14 @@ namespace MWPhysics bool mTransformUpdatePending; std::atomic mActive; MWWorld::Ptr mHitTarget; - osg::Vec3f mHitPosition; + btVector3 mHitPosition; + btVector3 mHitNormal; mutable std::mutex mPositionMutex; osg::Vec3f mPosition; + PhysicsSystem *mPhysics; PhysicsTaskScheduler *mTaskScheduler; Projectile(const Projectile&); From 4fbe1ed12cbe2e2adaa4b95eefee42a24af8f5a4 Mon Sep 17 00:00:00 2001 From: fredzio Date: Sat, 21 Nov 2020 16:26:45 +0100 Subject: [PATCH 4/4] Ignore caster collision shape. Sometimes the magic bolt get launched inside too near its caster. --- .../mwphysics/closestnotmeconvexresultcallback.cpp | 12 ++++++++---- apps/openmw/mwphysics/physicssystem.cpp | 4 ++-- apps/openmw/mwphysics/physicssystem.hpp | 2 +- apps/openmw/mwphysics/projectile.cpp | 3 ++- apps/openmw/mwphysics/projectile.hpp | 5 ++++- apps/openmw/mwworld/projectilemanager.cpp | 14 ++++++++------ 6 files changed, 25 insertions(+), 15 deletions(-) diff --git a/apps/openmw/mwphysics/closestnotmeconvexresultcallback.cpp b/apps/openmw/mwphysics/closestnotmeconvexresultcallback.cpp index bbffe16a8e..8f72422765 100644 --- a/apps/openmw/mwphysics/closestnotmeconvexresultcallback.cpp +++ b/apps/openmw/mwphysics/closestnotmeconvexresultcallback.cpp @@ -25,13 +25,17 @@ namespace MWPhysics if (convexResult.m_hitCollisionObject->getBroadphaseHandle()->m_collisionFilterGroup == CollisionType_Projectile) { - Projectile* projectileHolder = static_cast(convexResult.m_hitCollisionObject->getUserPointer()); + auto* projectileHolder = static_cast(convexResult.m_hitCollisionObject->getUserPointer()); if (!projectileHolder->isActive()) return btScalar(1); - PtrHolder* targetHolder = static_cast(mMe->getUserPointer()); + auto* targetHolder = static_cast(mMe->getUserPointer()); const MWWorld::Ptr target = targetHolder->getPtr(); - projectileHolder->hit(target, convexResult.m_hitPointLocal, convexResult.m_hitNormalLocal); - return btScalar(1); + // do nothing if we hit the caster. Sometimes the launching origin is inside of caster collision shape + if (projectileHolder->getCaster() != target) + { + projectileHolder->hit(target, convexResult.m_hitPointLocal, convexResult.m_hitNormalLocal); + return btScalar(1); + } } btVector3 hitNormalWorld; diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index 7da2a69643..324b1db2d7 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -661,10 +661,10 @@ namespace MWPhysics mActors.emplace(ptr, std::move(actor)); } - int PhysicsSystem::addProjectile (const osg::Vec3f& position) + int PhysicsSystem::addProjectile (const MWWorld::Ptr& caster, const osg::Vec3f& position) { mProjectileId++; - auto projectile = std::make_shared(mProjectileId, position, mTaskScheduler.get(), this); + auto projectile = std::make_shared(mProjectileId, caster, position, mTaskScheduler.get(), this); mProjectiles.emplace(mProjectileId, std::move(projectile)); return mProjectileId; diff --git a/apps/openmw/mwphysics/physicssystem.hpp b/apps/openmw/mwphysics/physicssystem.hpp index 6721a2d918..b33d829a46 100644 --- a/apps/openmw/mwphysics/physicssystem.hpp +++ b/apps/openmw/mwphysics/physicssystem.hpp @@ -128,7 +128,7 @@ namespace MWPhysics void addObject (const MWWorld::Ptr& ptr, const std::string& mesh, int collisionType = CollisionType_World); void addActor (const MWWorld::Ptr& ptr, const std::string& mesh); - int addProjectile(const osg::Vec3f& position); + int addProjectile(const MWWorld::Ptr& caster, const osg::Vec3f& position); void updateProjectile(const int projectileId, const osg::Vec3f &position); void removeProjectile(const int projectileId); diff --git a/apps/openmw/mwphysics/projectile.cpp b/apps/openmw/mwphysics/projectile.cpp index cbc5b19814..5f5bc778ba 100644 --- a/apps/openmw/mwphysics/projectile.cpp +++ b/apps/openmw/mwphysics/projectile.cpp @@ -18,8 +18,9 @@ namespace MWPhysics { -Projectile::Projectile(int projectileId, const osg::Vec3f& position, PhysicsTaskScheduler* scheduler, PhysicsSystem* physicssystem) +Projectile::Projectile(int projectileId, const MWWorld::Ptr& caster, const osg::Vec3f& position, PhysicsTaskScheduler* scheduler, PhysicsSystem* physicssystem) : mActive(true) + , mCaster(caster) , mPhysics(physicssystem) , mTaskScheduler(scheduler) , mProjectileId(projectileId) diff --git a/apps/openmw/mwphysics/projectile.hpp b/apps/openmw/mwphysics/projectile.hpp index 6e8aba60b8..ed0fdce150 100644 --- a/apps/openmw/mwphysics/projectile.hpp +++ b/apps/openmw/mwphysics/projectile.hpp @@ -32,7 +32,7 @@ namespace MWPhysics class Projectile final : public PtrHolder { public: - Projectile(const int projectileId, const osg::Vec3f& position, PhysicsTaskScheduler* scheduler, PhysicsSystem* physicssystem); + Projectile(const int projectileId, const MWWorld::Ptr& caster, const osg::Vec3f& position, PhysicsTaskScheduler* scheduler, PhysicsSystem* physicssystem); ~Projectile() override; btConvexShape* getConvexShape() const { return mConvexShape; } @@ -62,6 +62,8 @@ namespace MWPhysics return mHitTarget; } + MWWorld::Ptr getCaster() const { return mCaster; } + osg::Vec3f getHitPos() const { assert(!mActive); @@ -80,6 +82,7 @@ namespace MWPhysics btTransform mLocalTransform; bool mTransformUpdatePending; std::atomic mActive; + MWWorld::Ptr mCaster; MWWorld::Ptr mHitTarget; btVector3 mHitPosition; btVector3 mHitNormal; diff --git a/apps/openmw/mwworld/projectilemanager.cpp b/apps/openmw/mwworld/projectilemanager.cpp index 626c57ac32..dba4289a23 100644 --- a/apps/openmw/mwworld/projectilemanager.cpp +++ b/apps/openmw/mwworld/projectilemanager.cpp @@ -316,7 +316,7 @@ namespace MWWorld state.mSounds.push_back(sound); } - state.mProjectileId = mPhysics->addProjectile(pos); + state.mProjectileId = mPhysics->addProjectile(caster, pos); state.mToDelete = false; mMagicBolts.push_back(state); } @@ -340,7 +340,7 @@ namespace MWWorld if (!ptr.getClass().getEnchantment(ptr).empty()) SceneUtil::addEnchantedGlow(state.mNode, mResourceSystem, ptr.getClass().getEnchantmentColor(ptr)); - state.mProjectileId = mPhysics->addProjectile(pos); + state.mProjectileId = mPhysics->addProjectile(actor, pos); state.mToDelete = false; mProjectiles.push_back(state); } @@ -546,7 +546,8 @@ namespace MWWorld const auto target = projectile->getTarget(); const auto pos = projectile->getHitPos(); MWWorld::Ptr caster = projectileState.getCaster(); - if (caster == target || !isValidTarget(caster, target)) + assert(target != caster); + if (!isValidTarget(caster, target)) { projectile->activate(); continue; @@ -581,7 +582,8 @@ namespace MWWorld const auto target = projectile->getTarget(); const auto pos = projectile->getHitPos(); MWWorld::Ptr caster = magicBoltState.getCaster(); - if (caster == target || !isValidTarget(caster, target)) + assert(target != caster); + if (!isValidTarget(caster, target)) { projectile->activate(); continue; @@ -724,7 +726,7 @@ namespace MWWorld int weaponType = ptr.get()->mBase->mData.mType; state.mThrown = MWMechanics::getWeaponType(weaponType)->mWeaponClass == ESM::WeaponType::Thrown; - state.mProjectileId = mPhysics->addProjectile(osg::Vec3f(esm.mPosition)); + state.mProjectileId = mPhysics->addProjectile(state.getCaster(), osg::Vec3f(esm.mPosition)); } catch(...) { @@ -769,7 +771,7 @@ namespace MWWorld MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), state.mIdMagic.at(0)); MWWorld::Ptr ptr = ref.getPtr(); model = ptr.getClass().getModel(ptr); - state.mProjectileId = mPhysics->addProjectile(osg::Vec3f(esm.mPosition)); + state.mProjectileId = mPhysics->addProjectile(state.getCaster(), osg::Vec3f(esm.mPosition)); } catch(...) {