mirror of
				https://github.com/TES3MP/openmw-tes3mp.git
				synced 2025-10-31 23:26:43 +00:00 
			
		
		
		
	Modify projectile collision to work with async physics
This commit is contained in:
		
							parent
							
								
									dc7b48e92e
								
							
						
					
					
						commit
						66fe3b0d38
					
				
					 12 changed files with 245 additions and 209 deletions
				
			
		|  | @ -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; | ||||
| 
 | ||||
|  |  | |||
|  | @ -26,12 +26,13 @@ namespace MWPhysics | |||
|         if (convexResult.m_hitCollisionObject->getBroadphaseHandle()->m_collisionFilterGroup == CollisionType_Projectile) | ||||
|         { | ||||
|             Projectile* projectileHolder = static_cast<Projectile*>(convexResult.m_hitCollisionObject->getUserPointer()); | ||||
|             int projectileId = projectileHolder->getProjectileId(); | ||||
|             if (!projectileHolder->isActive()) | ||||
|                 return btScalar(1); | ||||
|             PtrHolder* targetHolder = static_cast<PtrHolder*>(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); | ||||
|         } | ||||
| 
 | ||||
|  |  | |||
|  | @ -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<Projectile>(p)) | ||||
|             { | ||||
|                 projectile->commitPositionChange(); | ||||
|                 mCollisionWorld->updateSingleAabb(projectile->getCollisionObject()); | ||||
|             } | ||||
|         }; | ||||
|     } | ||||
| 
 | ||||
|  |  | |||
|  | @ -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<MWWorld::Ptr> 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<MWWorld::Ptr> 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<PtrHolder*>(resultCallback.m_collisionObject->getUserPointer())) | ||||
|             { | ||||
|                 result.mHitObject = ptrHolder->getPtr(); | ||||
| 
 | ||||
|                 if (group == CollisionType_Projectile) | ||||
|                 { | ||||
|                     Projectile* projectile = static_cast<Projectile*>(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<Projectile>(mProjectileId, position, mTaskScheduler.get()); | ||||
|         mProjectiles.emplace(mProjectileId, std::move(projectile)); | ||||
| 
 | ||||
|         return mProjectileId; | ||||
|     } | ||||
|  |  | |||
|  | @ -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<MWWorld::Ptr> targets = std::vector<MWWorld::Ptr>(), | ||||
|  | @ -282,7 +275,7 @@ namespace MWPhysics | |||
| 
 | ||||
|             ActorMap mActors; | ||||
| 
 | ||||
|             using ProjectileMap = std::map<int, Projectile *>; | ||||
|             using ProjectileMap = std::map<int, std::shared_ptr<Projectile>>; | ||||
|             ProjectileMap mProjectiles; | ||||
| 
 | ||||
|             using HeightFieldMap = std::map<std::pair<int, int>, HeightField *>; | ||||
|  | @ -295,7 +288,7 @@ namespace MWPhysics | |||
| 
 | ||||
|             float mTimeAccum; | ||||
| 
 | ||||
|             int mProjectileId; | ||||
|             unsigned int mProjectileId; | ||||
| 
 | ||||
|             float mWaterHeight; | ||||
|             bool mWaterEnabled; | ||||
|  |  | |||
|  | @ -1,24 +1,28 @@ | |||
| #include "projectile.hpp" | ||||
| #include <memory> | ||||
| 
 | ||||
| #include <BulletCollision/CollisionShapes/btSphereShape.h> | ||||
| #include <BulletCollision/CollisionDispatch/btCollisionWorld.h> | ||||
| 
 | ||||
| #include <components/sceneutil/positionattitudetransform.hpp> | ||||
| #include <components/resource/bulletshape.hpp> | ||||
| #include <LinearMath/btVector3.h> | ||||
| 
 | ||||
| #include <components/debug/debuglog.hpp> | ||||
| #include <components/misc/convert.hpp> | ||||
| #include <components/resource/bulletshape.hpp> | ||||
| #include <components/sceneutil/positionattitudetransform.hpp> | ||||
| 
 | ||||
| #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<btConvexShape*>(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<std::mutex> lock(mPositionMutex); | ||||
|     if (mTransformUpdatePending) | ||||
|     { | ||||
|         mCollisionObject->setWorldTransform(mLocalTransform); | ||||
|         mTransformUpdatePending = false; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void Projectile::setPosition(const osg::Vec3f &position) | ||||
| { | ||||
|     mPosition = position; | ||||
|     updateCollisionObjectPosition(); | ||||
|     std::unique_lock<std::mutex> 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<std::mutex> 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); | ||||
| } | ||||
| } | ||||
|  |  | |||
|  | @ -1,18 +1,23 @@ | |||
| #ifndef OPENMW_MWPHYSICS_PROJECTILE_H | ||||
| #define OPENMW_MWPHYSICS_PROJECTILE_H | ||||
| 
 | ||||
| #include <atomic> | ||||
| #include <memory> | ||||
| #include <mutex> | ||||
| 
 | ||||
| #include <components/misc/convert.hpp> | ||||
| 
 | ||||
| #include "ptrholder.hpp" | ||||
| 
 | ||||
| #include <osg/Vec3f> | ||||
| #include <osg/Quat> | ||||
| #include <osg/ref_ptr> | ||||
| 
 | ||||
| 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<btCollisionShape> mShape; | ||||
|         btConvexShape* mConvexShape; | ||||
| 
 | ||||
|         std::unique_ptr<btCollisionObject> mCollisionObject; | ||||
|         btTransform mLocalTransform; | ||||
|         bool mTransformUpdatePending; | ||||
|         std::atomic<bool> 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&); | ||||
|  |  | |||
|  | @ -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<MWWorld::Ptr> targets = std::vector<MWWorld::Ptr>(), | ||||
|                     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; | ||||
| 
 | ||||
|  |  | |||
|  | @ -2,6 +2,7 @@ | |||
| 
 | ||||
| #include <iomanip> | ||||
| 
 | ||||
| #include <memory> | ||||
| #include <osg/PositionAttitudeTransform> | ||||
| 
 | ||||
| #include <components/debug/debuglog.hpp> | ||||
|  | @ -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<ProjectileState>::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<MagicBoltState>::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<MagicBoltState>::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<ESM::GameSetting>() | ||||
|                         .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<MWWorld::Ptr> 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<ProjectileState>::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<MWWorld::Ptr> 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<ProjectileState>::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<MagicBoltState>::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<ProjectileState>::iterator it = mProjectiles.begin(); it != mProjectiles.end(); ++it) | ||||
|         { | ||||
|             cleanupProjectile(*it); | ||||
|         } | ||||
|         for (auto& mProjectile : mProjectiles) | ||||
|             cleanupProjectile(mProjectile); | ||||
|         mProjectiles.clear(); | ||||
|         for (std::vector<MagicBoltState>::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 | ||||
|             { | ||||
|  |  | |||
|  | @ -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 | ||||
|  |  | |||
|  | @ -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) | ||||
|  |  | |||
|  | @ -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; | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue