mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-01-19 21:23:52 +00:00
Merge branch 'projectile_physics' into 'master'
Precise projectile physics (closes #4201) Closes #4201 See merge request OpenMW/openmw!550
This commit is contained in:
commit
d2c5de5211
17 changed files with 260 additions and 226 deletions
|
@ -21,6 +21,7 @@
|
||||||
Bug #4039: Multiple followers should have the same following distance
|
Bug #4039: Multiple followers should have the same following distance
|
||||||
Bug #4055: Local scripts don't inherit variables from their base record
|
Bug #4055: Local scripts don't inherit variables from their base record
|
||||||
Bug #4083: Door animation freezes when colliding with actors
|
Bug #4083: Door animation freezes when colliding with actors
|
||||||
|
Bug #4201: Projectile-projectile collision
|
||||||
Bug #4247: Cannot walk up stairs in Ebonheart docks
|
Bug #4247: Cannot walk up stairs in Ebonheart docks
|
||||||
Bug #4363: Editor: Defect in Clone Function for Dialogue Info records
|
Bug #4363: Editor: Defect in Clone Function for Dialogue Info records
|
||||||
Bug #4447: Actor collision capsule shape allows looking through some walls
|
Bug #4447: Actor collision capsule shape allows looking through some walls
|
||||||
|
|
|
@ -73,7 +73,7 @@ add_openmw_dir (mwworld
|
||||||
add_openmw_dir (mwphysics
|
add_openmw_dir (mwphysics
|
||||||
physicssystem trace collisiontype actor convert object heightfield closestnotmerayresultcallback
|
physicssystem trace collisiontype actor convert object heightfield closestnotmerayresultcallback
|
||||||
contacttestresultcallback deepestnotmecontacttestresultcallback stepper movementsolver projectile
|
contacttestresultcallback deepestnotmecontacttestresultcallback stepper movementsolver projectile
|
||||||
closestnotmeconvexresultcallback raycasting mtphysics contacttestwrapper
|
actorconvexcallback raycasting mtphysics contacttestwrapper projectileconvexcallback
|
||||||
)
|
)
|
||||||
|
|
||||||
add_openmw_dir (mwclass
|
add_openmw_dir (mwclass
|
||||||
|
|
|
@ -60,7 +60,10 @@ namespace MWMechanics
|
||||||
void CastSpell::inflict(const MWWorld::Ptr &target, const MWWorld::Ptr &caster,
|
void CastSpell::inflict(const MWWorld::Ptr &target, const MWWorld::Ptr &caster,
|
||||||
const ESM::EffectList &effects, ESM::RangeType range, bool reflected, bool exploded)
|
const ESM::EffectList &effects, ESM::RangeType range, bool reflected, bool exploded)
|
||||||
{
|
{
|
||||||
if (!target.isEmpty() && target.getClass().isActor())
|
if (target.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (target.getClass().isActor())
|
||||||
{
|
{
|
||||||
// Early-out for characters that have departed.
|
// Early-out for characters that have departed.
|
||||||
const auto& stats = target.getClass().getCreatureStats(target);
|
const auto& stats = target.getClass().getCreatureStats(target);
|
||||||
|
@ -82,7 +85,7 @@ namespace MWMechanics
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().search (mId);
|
const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().search (mId);
|
||||||
if (spell && !target.isEmpty() && (spell->mData.mType == ESM::Spell::ST_Disease || spell->mData.mType == ESM::Spell::ST_Blight))
|
if (spell && target.getClass().isActor() && (spell->mData.mType == ESM::Spell::ST_Disease || spell->mData.mType == ESM::Spell::ST_Blight))
|
||||||
{
|
{
|
||||||
int requiredResistance = (spell->mData.mType == ESM::Spell::ST_Disease) ?
|
int requiredResistance = (spell->mData.mType == ESM::Spell::ST_Disease) ?
|
||||||
ESM::MagicEffect::ResistCommonDisease
|
ESM::MagicEffect::ResistCommonDisease
|
||||||
|
@ -105,13 +108,13 @@ namespace MWMechanics
|
||||||
// This is required for Weakness effects in a spell to apply to any subsequent effects in the spell.
|
// This is required for Weakness effects in a spell to apply to any subsequent effects in the spell.
|
||||||
// Otherwise, they'd only apply after the whole spell was added.
|
// Otherwise, they'd only apply after the whole spell was added.
|
||||||
MagicEffects targetEffects;
|
MagicEffects targetEffects;
|
||||||
if (!target.isEmpty() && target.getClass().isActor())
|
if (target.getClass().isActor())
|
||||||
targetEffects += target.getClass().getCreatureStats(target).getMagicEffects();
|
targetEffects += target.getClass().getCreatureStats(target).getMagicEffects();
|
||||||
|
|
||||||
bool castByPlayer = (!caster.isEmpty() && caster == getPlayer());
|
bool castByPlayer = (!caster.isEmpty() && caster == getPlayer());
|
||||||
|
|
||||||
ActiveSpells targetSpells;
|
ActiveSpells targetSpells;
|
||||||
if (!target.isEmpty() && target.getClass().isActor())
|
if (target.getClass().isActor())
|
||||||
targetSpells = target.getClass().getCreatureStats(target).getActiveSpells();
|
targetSpells = target.getClass().getCreatureStats(target).getActiveSpells();
|
||||||
|
|
||||||
bool canCastAnEffect = false; // For bound equipment.If this remains false
|
bool canCastAnEffect = false; // For bound equipment.If this remains false
|
||||||
|
@ -123,7 +126,7 @@ namespace MWMechanics
|
||||||
|
|
||||||
int currentEffectIndex = 0;
|
int currentEffectIndex = 0;
|
||||||
for (std::vector<ESM::ENAMstruct>::const_iterator effectIt (effects.mList.begin());
|
for (std::vector<ESM::ENAMstruct>::const_iterator effectIt (effects.mList.begin());
|
||||||
!target.isEmpty() && effectIt != effects.mList.end(); ++effectIt, ++currentEffectIndex)
|
effectIt != effects.mList.end(); ++effectIt, ++currentEffectIndex)
|
||||||
{
|
{
|
||||||
if (effectIt->mRange != range)
|
if (effectIt->mRange != range)
|
||||||
continue;
|
continue;
|
||||||
|
@ -267,7 +270,7 @@ namespace MWMechanics
|
||||||
}
|
}
|
||||||
|
|
||||||
// Re-casting a summon effect will remove the creature from previous castings of that effect.
|
// Re-casting a summon effect will remove the creature from previous castings of that effect.
|
||||||
if (isSummoningEffect(effectIt->mEffectID) && !target.isEmpty() && target.getClass().isActor())
|
if (isSummoningEffect(effectIt->mEffectID) && target.getClass().isActor())
|
||||||
{
|
{
|
||||||
CreatureStats& targetStats = target.getClass().getCreatureStats(target);
|
CreatureStats& targetStats = target.getClass().getCreatureStats(target);
|
||||||
ESM::SummonKey key(effectIt->mEffectID, mId, currentEffectIndex);
|
ESM::SummonKey key(effectIt->mEffectID, mId, currentEffectIndex);
|
||||||
|
@ -310,18 +313,16 @@ namespace MWMechanics
|
||||||
if (!exploded)
|
if (!exploded)
|
||||||
MWBase::Environment::get().getWorld()->explodeSpell(mHitPosition, effects, caster, target, range, mId, mSourceName, mFromProjectile);
|
MWBase::Environment::get().getWorld()->explodeSpell(mHitPosition, effects, caster, target, range, mId, mSourceName, mFromProjectile);
|
||||||
|
|
||||||
if (!target.isEmpty()) {
|
if (!reflectedEffects.mList.empty())
|
||||||
if (!reflectedEffects.mList.empty())
|
inflict(caster, target, reflectedEffects, range, true, exploded);
|
||||||
inflict(caster, target, reflectedEffects, range, true, exploded);
|
|
||||||
|
|
||||||
if (!appliedLastingEffects.empty())
|
if (!appliedLastingEffects.empty())
|
||||||
{
|
{
|
||||||
int casterActorId = -1;
|
int casterActorId = -1;
|
||||||
if (!caster.isEmpty() && caster.getClass().isActor())
|
if (!caster.isEmpty() && caster.getClass().isActor())
|
||||||
casterActorId = caster.getClass().getCreatureStats(caster).getActorId();
|
casterActorId = caster.getClass().getCreatureStats(caster).getActorId();
|
||||||
target.getClass().getCreatureStats(target).getActiveSpells().addSpell(mId, mStack, appliedLastingEffects,
|
target.getClass().getCreatureStats(target).getActiveSpells().addSpell(mId, mStack, appliedLastingEffects,
|
||||||
mSourceName, casterActorId);
|
mSourceName, casterActorId);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
|
||||||
#include "closestnotmeconvexresultcallback.hpp"
|
#include "actorconvexcallback.hpp"
|
||||||
#include "collisiontype.hpp"
|
#include "collisiontype.hpp"
|
||||||
#include "contacttestwrapper.h"
|
#include "contacttestwrapper.h"
|
||||||
|
|
||||||
|
@ -31,13 +31,13 @@ namespace MWPhysics
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
ClosestNotMeConvexResultCallback::ClosestNotMeConvexResultCallback(const btCollisionObject *me, const btVector3 &motion, btScalar minCollisionDot, const btCollisionWorld * world)
|
ActorConvexCallback::ActorConvexCallback(const btCollisionObject *me, const btVector3 &motion, btScalar minCollisionDot, const btCollisionWorld * world)
|
||||||
: btCollisionWorld::ClosestConvexResultCallback(btVector3(0.0, 0.0, 0.0), btVector3(0.0, 0.0, 0.0)),
|
: btCollisionWorld::ClosestConvexResultCallback(btVector3(0.0, 0.0, 0.0), btVector3(0.0, 0.0, 0.0)),
|
||||||
mMe(me), mMotion(motion), mMinCollisionDot(minCollisionDot), mWorld(world)
|
mMe(me), mMotion(motion), mMinCollisionDot(minCollisionDot), mWorld(world)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
btScalar ClosestNotMeConvexResultCallback::addSingleResult(btCollisionWorld::LocalConvexResult& convexResult, bool normalInWorldSpace)
|
btScalar ActorConvexCallback::addSingleResult(btCollisionWorld::LocalConvexResult& convexResult, bool normalInWorldSpace)
|
||||||
{
|
{
|
||||||
if (convexResult.m_hitCollisionObject == mMe)
|
if (convexResult.m_hitCollisionObject == mMe)
|
||||||
return btScalar(1);
|
return btScalar(1);
|
|
@ -1,5 +1,5 @@
|
||||||
#ifndef OPENMW_MWPHYSICS_CLOSESTNOTMECONVEXRESULTCALLBACK_H
|
#ifndef OPENMW_MWPHYSICS_ACTORCONVEXCALLBACK_H
|
||||||
#define OPENMW_MWPHYSICS_CLOSESTNOTMECONVEXRESULTCALLBACK_H
|
#define OPENMW_MWPHYSICS_ACTORCONVEXCALLBACK_H
|
||||||
|
|
||||||
#include <BulletCollision/CollisionDispatch/btCollisionWorld.h>
|
#include <BulletCollision/CollisionDispatch/btCollisionWorld.h>
|
||||||
|
|
||||||
|
@ -7,10 +7,10 @@ class btCollisionObject;
|
||||||
|
|
||||||
namespace MWPhysics
|
namespace MWPhysics
|
||||||
{
|
{
|
||||||
class ClosestNotMeConvexResultCallback : public btCollisionWorld::ClosestConvexResultCallback
|
class ActorConvexCallback : public btCollisionWorld::ClosestConvexResultCallback
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ClosestNotMeConvexResultCallback(const btCollisionObject *me, const btVector3 &motion, btScalar minCollisionDot, const btCollisionWorld * world);
|
ActorConvexCallback(const btCollisionObject *me, const btVector3 &motion, btScalar minCollisionDot, const btCollisionWorld * world);
|
||||||
|
|
||||||
btScalar addSingleResult(btCollisionWorld::LocalConvexResult& convexResult,bool normalInWorldSpace) override;
|
btScalar addSingleResult(btCollisionWorld::LocalConvexResult& convexResult,bool normalInWorldSpace) override;
|
||||||
|
|
|
@ -7,16 +7,13 @@
|
||||||
|
|
||||||
#include "../mwworld/class.hpp"
|
#include "../mwworld/class.hpp"
|
||||||
|
|
||||||
#include "actor.hpp"
|
|
||||||
#include "collisiontype.hpp"
|
|
||||||
#include "projectile.hpp"
|
|
||||||
#include "ptrholder.hpp"
|
#include "ptrholder.hpp"
|
||||||
|
|
||||||
namespace MWPhysics
|
namespace MWPhysics
|
||||||
{
|
{
|
||||||
ClosestNotMeRayResultCallback::ClosestNotMeRayResultCallback(const btCollisionObject* me, std::vector<const btCollisionObject*> targets, const btVector3& from, const btVector3& to, Projectile* proj)
|
ClosestNotMeRayResultCallback::ClosestNotMeRayResultCallback(const btCollisionObject* me, std::vector<const btCollisionObject*> targets, const btVector3& from, const btVector3& to)
|
||||||
: btCollisionWorld::ClosestRayResultCallback(from, to)
|
: btCollisionWorld::ClosestRayResultCallback(from, to)
|
||||||
, mMe(me), mTargets(std::move(targets)), mProjectile(proj)
|
, mMe(me), mTargets(std::move(targets))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,9 +22,6 @@ namespace MWPhysics
|
||||||
if (rayResult.m_collisionObject == mMe)
|
if (rayResult.m_collisionObject == mMe)
|
||||||
return 1.f;
|
return 1.f;
|
||||||
|
|
||||||
if (mProjectile && rayResult.m_collisionObject == mProjectile->getCollisionObject())
|
|
||||||
return 1.f;
|
|
||||||
|
|
||||||
if (!mTargets.empty())
|
if (!mTargets.empty())
|
||||||
{
|
{
|
||||||
if ((std::find(mTargets.begin(), mTargets.end(), rayResult.m_collisionObject) == mTargets.end()))
|
if ((std::find(mTargets.begin(), mTargets.end(), rayResult.m_collisionObject) == mTargets.end()))
|
||||||
|
@ -38,27 +32,6 @@ namespace MWPhysics
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
btCollisionWorld::ClosestRayResultCallback::addSingleResult(rayResult, normalInWorldSpace);
|
return btCollisionWorld::ClosestRayResultCallback::addSingleResult(rayResult, normalInWorldSpace);
|
||||||
if (mProjectile)
|
|
||||||
{
|
|
||||||
switch (rayResult.m_collisionObject->getBroadphaseHandle()->m_collisionFilterGroup)
|
|
||||||
{
|
|
||||||
case CollisionType_Actor:
|
|
||||||
{
|
|
||||||
auto* target = static_cast<Actor*>(rayResult.m_collisionObject->getUserPointer());
|
|
||||||
mProjectile->hit(target->getPtr(), m_hitPointWorld, m_hitNormalWorld);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case CollisionType_Projectile:
|
|
||||||
{
|
|
||||||
auto* target = static_cast<Projectile*>(rayResult.m_collisionObject->getUserPointer());
|
|
||||||
target->hit(mProjectile->getPtr(), m_hitPointWorld, m_hitNormalWorld);
|
|
||||||
mProjectile->hit(target->getPtr(), m_hitPointWorld, m_hitNormalWorld);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return rayResult.m_hitFraction;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,13 +14,13 @@ namespace MWPhysics
|
||||||
class ClosestNotMeRayResultCallback : public btCollisionWorld::ClosestRayResultCallback
|
class ClosestNotMeRayResultCallback : public btCollisionWorld::ClosestRayResultCallback
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ClosestNotMeRayResultCallback(const btCollisionObject* me, std::vector<const btCollisionObject*> targets, const btVector3& from, const btVector3& to, Projectile* proj=nullptr);
|
ClosestNotMeRayResultCallback(const btCollisionObject* me, std::vector<const btCollisionObject*> targets, const btVector3& from, const btVector3& to);
|
||||||
|
|
||||||
btScalar addSingleResult(btCollisionWorld::LocalRayResult& rayResult, bool normalInWorldSpace) override;
|
btScalar addSingleResult(btCollisionWorld::LocalRayResult& rayResult, bool normalInWorldSpace) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const btCollisionObject* mMe;
|
const btCollisionObject* mMe;
|
||||||
const std::vector<const btCollisionObject*> mTargets;
|
const std::vector<const btCollisionObject*> mTargets;
|
||||||
Projectile* mProjectile;
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -55,6 +55,7 @@
|
||||||
#include "deepestnotmecontacttestresultcallback.hpp"
|
#include "deepestnotmecontacttestresultcallback.hpp"
|
||||||
#include "closestnotmerayresultcallback.hpp"
|
#include "closestnotmerayresultcallback.hpp"
|
||||||
#include "contacttestresultcallback.hpp"
|
#include "contacttestresultcallback.hpp"
|
||||||
|
#include "projectileconvexcallback.hpp"
|
||||||
#include "constants.hpp"
|
#include "constants.hpp"
|
||||||
#include "movementsolver.hpp"
|
#include "movementsolver.hpp"
|
||||||
#include "mtphysics.hpp"
|
#include "mtphysics.hpp"
|
||||||
|
@ -246,7 +247,7 @@ namespace MWPhysics
|
||||||
return 0.f;
|
return 0.f;
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
RayCastingResult PhysicsSystem::castRay(const osg::Vec3f &from, const osg::Vec3f &to, const MWWorld::ConstPtr& ignore, std::vector<MWWorld::Ptr> targets, int mask, int group) const
|
||||||
{
|
{
|
||||||
if (from == to)
|
if (from == to)
|
||||||
{
|
{
|
||||||
|
@ -283,7 +284,7 @@ namespace MWPhysics
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ClosestNotMeRayResultCallback resultCallback(me, targetCollisionObjects, btFrom, btTo, getProjectile(projId));
|
ClosestNotMeRayResultCallback resultCallback(me, targetCollisionObjects, btFrom, btTo);
|
||||||
resultCallback.m_collisionFilterGroup = group;
|
resultCallback.m_collisionFilterGroup = group;
|
||||||
resultCallback.m_collisionFilterMask = mask;
|
resultCallback.m_collisionFilterMask = mask;
|
||||||
|
|
||||||
|
@ -580,15 +581,44 @@ namespace MWPhysics
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PhysicsSystem::updateProjectile(const int projectileId, const osg::Vec3f &position)
|
void PhysicsSystem::updateProjectile(const int projectileId, const osg::Vec3f &position) const
|
||||||
{
|
{
|
||||||
ProjectileMap::iterator foundProjectile = mProjectiles.find(projectileId);
|
const auto foundProjectile = mProjectiles.find(projectileId);
|
||||||
if (foundProjectile != mProjectiles.end())
|
assert(foundProjectile != mProjectiles.end());
|
||||||
{
|
auto* projectile = foundProjectile->second.get();
|
||||||
foundProjectile->second->setPosition(position);
|
|
||||||
mTaskScheduler->updateSingleAabb(foundProjectile->second);
|
btVector3 btFrom = Misc::Convert::toBullet(projectile->getPosition());
|
||||||
|
btVector3 btTo = Misc::Convert::toBullet(position);
|
||||||
|
|
||||||
|
if (btFrom == btTo)
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
const auto casterPtr = projectile->getCaster();
|
||||||
|
const auto* caster = [this,&casterPtr]() -> const btCollisionObject*
|
||||||
|
{
|
||||||
|
const Actor* actor = getActor(casterPtr);
|
||||||
|
if (actor)
|
||||||
|
return actor->getCollisionObject();
|
||||||
|
const Object* object = getObject(casterPtr);
|
||||||
|
if (object)
|
||||||
|
return object->getCollisionObject();
|
||||||
|
return nullptr;
|
||||||
|
}();
|
||||||
|
assert(caster);
|
||||||
|
|
||||||
|
ProjectileConvexCallback resultCallback(caster, btFrom, btTo, projectile);
|
||||||
|
resultCallback.m_collisionFilterMask = 0xff;
|
||||||
|
resultCallback.m_collisionFilterGroup = CollisionType_Projectile;
|
||||||
|
|
||||||
|
const btQuaternion btrot = btQuaternion::getIdentity();
|
||||||
|
btTransform from_ (btrot, btFrom);
|
||||||
|
btTransform to_ (btrot, btTo);
|
||||||
|
|
||||||
|
mTaskScheduler->convexSweepTest(projectile->getConvexShape(), from_, to_, resultCallback);
|
||||||
|
|
||||||
|
const auto newpos = projectile->isActive() ? position : Misc::Convert::toOsg(resultCallback.m_hitPointWorld);
|
||||||
|
projectile->setPosition(newpos);
|
||||||
|
mTaskScheduler->updateSingleAabb(foundProjectile->second);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PhysicsSystem::updateRotation(const MWWorld::Ptr &ptr)
|
void PhysicsSystem::updateRotation(const MWWorld::Ptr &ptr)
|
||||||
|
@ -651,10 +681,10 @@ namespace MWPhysics
|
||||||
mActors.emplace(ptr, std::move(actor));
|
mActors.emplace(ptr, std::move(actor));
|
||||||
}
|
}
|
||||||
|
|
||||||
int PhysicsSystem::addProjectile (const MWWorld::Ptr& caster, const osg::Vec3f& position)
|
int PhysicsSystem::addProjectile (const MWWorld::Ptr& caster, const osg::Vec3f& position, float radius, bool canTraverseWater)
|
||||||
{
|
{
|
||||||
mProjectileId++;
|
mProjectileId++;
|
||||||
auto projectile = std::make_shared<Projectile>(mProjectileId, caster, position, mTaskScheduler.get(), this);
|
auto projectile = std::make_shared<Projectile>(caster, position, radius, canTraverseWater, mTaskScheduler.get(), this);
|
||||||
mProjectiles.emplace(mProjectileId, std::move(projectile));
|
mProjectiles.emplace(mProjectileId, std::move(projectile));
|
||||||
|
|
||||||
return mProjectileId;
|
return mProjectileId;
|
||||||
|
@ -863,7 +893,7 @@ namespace MWPhysics
|
||||||
mWaterCollisionShape.reset(new btStaticPlaneShape(btVector3(0,0,1), mWaterHeight));
|
mWaterCollisionShape.reset(new btStaticPlaneShape(btVector3(0,0,1), mWaterHeight));
|
||||||
mWaterCollisionObject->setCollisionShape(mWaterCollisionShape.get());
|
mWaterCollisionObject->setCollisionShape(mWaterCollisionShape.get());
|
||||||
mTaskScheduler->addCollisionObject(mWaterCollisionObject.get(), CollisionType_Water,
|
mTaskScheduler->addCollisionObject(mWaterCollisionObject.get(), CollisionType_Water,
|
||||||
CollisionType_Actor);
|
CollisionType_Actor|CollisionType_Projectile);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PhysicsSystem::isAreaOccupiedByOtherActor(const osg::Vec3f& position, const float radius, const MWWorld::ConstPtr& ignore) const
|
bool PhysicsSystem::isAreaOccupiedByOtherActor(const osg::Vec3f& position, const float radius, const MWWorld::ConstPtr& ignore) const
|
||||||
|
|
|
@ -124,8 +124,8 @@ namespace MWPhysics
|
||||||
void addObject (const MWWorld::Ptr& ptr, const std::string& mesh, int collisionType = CollisionType_World);
|
void addObject (const MWWorld::Ptr& ptr, const std::string& mesh, int collisionType = CollisionType_World);
|
||||||
void addActor (const MWWorld::Ptr& ptr, const std::string& mesh);
|
void addActor (const MWWorld::Ptr& ptr, const std::string& mesh);
|
||||||
|
|
||||||
int addProjectile(const MWWorld::Ptr& caster, const osg::Vec3f& position);
|
int addProjectile(const MWWorld::Ptr& caster, const osg::Vec3f& position, float radius, bool canTraverseWater);
|
||||||
void updateProjectile(const int projectileId, const osg::Vec3f &position);
|
void updateProjectile(const int projectileId, const osg::Vec3f &position) const;
|
||||||
void removeProjectile(const int projectileId);
|
void removeProjectile(const int projectileId);
|
||||||
|
|
||||||
void updatePtr (const MWWorld::Ptr& old, const MWWorld::Ptr& updated);
|
void updatePtr (const MWWorld::Ptr& old, const MWWorld::Ptr& updated);
|
||||||
|
@ -174,7 +174,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.
|
/// @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(),
|
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>(),
|
std::vector<MWWorld::Ptr> targets = std::vector<MWWorld::Ptr>(),
|
||||||
int mask = CollisionType_World|CollisionType_HeightMap|CollisionType_Actor|CollisionType_Door, int group=0xff, int projId=-1) const override;
|
int mask = CollisionType_World|CollisionType_HeightMap|CollisionType_Actor|CollisionType_Door, int group=0xff) const override;
|
||||||
|
|
||||||
RayCastingResult castSphere(const osg::Vec3f& from, const osg::Vec3f& to, float radius) const override;
|
RayCastingResult castSphere(const osg::Vec3f& from, const osg::Vec3f& to, float radius) const override;
|
||||||
|
|
||||||
|
|
|
@ -13,19 +13,22 @@
|
||||||
#include "../mwworld/class.hpp"
|
#include "../mwworld/class.hpp"
|
||||||
|
|
||||||
#include "collisiontype.hpp"
|
#include "collisiontype.hpp"
|
||||||
|
#include "memory"
|
||||||
#include "mtphysics.hpp"
|
#include "mtphysics.hpp"
|
||||||
#include "projectile.hpp"
|
#include "projectile.hpp"
|
||||||
|
|
||||||
namespace MWPhysics
|
namespace MWPhysics
|
||||||
{
|
{
|
||||||
Projectile::Projectile(int projectileId, const MWWorld::Ptr& caster, const osg::Vec3f& position, PhysicsTaskScheduler* scheduler, PhysicsSystem* physicssystem)
|
Projectile::Projectile(const MWWorld::Ptr& caster, const osg::Vec3f& position, float radius, bool canCrossWaterSurface, PhysicsTaskScheduler* scheduler, PhysicsSystem* physicssystem)
|
||||||
: mActive(true)
|
: mCanCrossWaterSurface(canCrossWaterSurface)
|
||||||
|
, mCrossedWaterSurface(false)
|
||||||
|
, mActive(true)
|
||||||
, mCaster(caster)
|
, mCaster(caster)
|
||||||
|
, mWaterHitPosition(std::nullopt)
|
||||||
, mPhysics(physicssystem)
|
, mPhysics(physicssystem)
|
||||||
, mTaskScheduler(scheduler)
|
, mTaskScheduler(scheduler)
|
||||||
, mProjectileId(projectileId)
|
|
||||||
{
|
{
|
||||||
mShape.reset(new btSphereShape(1.f));
|
mShape = std::make_unique<btSphereShape>(radius);
|
||||||
mConvexShape = static_cast<btConvexShape*>(mShape.get());
|
mConvexShape = static_cast<btConvexShape*>(mShape.get());
|
||||||
|
|
||||||
mCollisionObject = std::make_unique<btCollisionObject>();
|
mCollisionObject = std::make_unique<btCollisionObject>();
|
||||||
|
@ -67,6 +70,17 @@ void Projectile::setPosition(const osg::Vec3f &position)
|
||||||
mTransformUpdatePending = true;
|
mTransformUpdatePending = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
osg::Vec3f Projectile::getPosition() const
|
||||||
|
{
|
||||||
|
std::scoped_lock lock(mMutex);
|
||||||
|
return Misc::Convert::toOsg(mLocalTransform.getOrigin());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Projectile::canTraverseWater() const
|
||||||
|
{
|
||||||
|
return mCanCrossWaterSurface;
|
||||||
|
}
|
||||||
|
|
||||||
void Projectile::hit(MWWorld::Ptr target, btVector3 pos, btVector3 normal)
|
void Projectile::hit(MWWorld::Ptr target, btVector3 pos, btVector3 normal)
|
||||||
{
|
{
|
||||||
if (!mActive.load(std::memory_order_acquire))
|
if (!mActive.load(std::memory_order_acquire))
|
||||||
|
@ -78,12 +92,6 @@ void Projectile::hit(MWWorld::Ptr target, btVector3 pos, btVector3 normal)
|
||||||
mActive.store(false, std::memory_order_release);
|
mActive.store(false, std::memory_order_release);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Projectile::activate()
|
|
||||||
{
|
|
||||||
assert(!mActive);
|
|
||||||
mActive.store(true, std::memory_order_release);
|
|
||||||
}
|
|
||||||
|
|
||||||
MWWorld::Ptr Projectile::getCaster() const
|
MWWorld::Ptr Projectile::getCaster() const
|
||||||
{
|
{
|
||||||
std::scoped_lock lock(mMutex);
|
std::scoped_lock lock(mMutex);
|
||||||
|
@ -108,21 +116,32 @@ bool Projectile::isValidTarget(const MWWorld::Ptr& target) const
|
||||||
if (mCaster == target)
|
if (mCaster == target)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!mValidTargets.empty())
|
if (target.isEmpty() || mValidTargets.empty())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
bool validTarget = false;
|
||||||
|
for (const auto& targetActor : mValidTargets)
|
||||||
{
|
{
|
||||||
bool validTarget = false;
|
if (targetActor == target)
|
||||||
for (const auto& targetActor : mValidTargets)
|
|
||||||
{
|
{
|
||||||
if (targetActor == target)
|
validTarget = true;
|
||||||
{
|
break;
|
||||||
validTarget = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return validTarget;
|
|
||||||
}
|
}
|
||||||
return true;
|
return validTarget;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<btVector3> Projectile::getWaterHitPosition()
|
||||||
|
{
|
||||||
|
return std::exchange(mWaterHitPosition, std::nullopt);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Projectile::setWaterHitPosition(btVector3 pos)
|
||||||
|
{
|
||||||
|
if (mCrossedWaterSurface)
|
||||||
|
return;
|
||||||
|
mCrossedWaterSurface = true;
|
||||||
|
mWaterHitPosition = pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
#include "components/misc/convert.hpp"
|
#include "components/misc/convert.hpp"
|
||||||
|
|
||||||
|
@ -32,7 +33,7 @@ namespace MWPhysics
|
||||||
class Projectile final : public PtrHolder
|
class Projectile final : public PtrHolder
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Projectile(const int projectileId, const MWWorld::Ptr& caster, const osg::Vec3f& position, PhysicsTaskScheduler* scheduler, PhysicsSystem* physicssystem);
|
Projectile(const MWWorld::Ptr& caster, const osg::Vec3f& position, float radius, bool canCrossWaterSurface, PhysicsTaskScheduler* scheduler, PhysicsSystem* physicssystem);
|
||||||
~Projectile() override;
|
~Projectile() override;
|
||||||
|
|
||||||
btConvexShape* getConvexShape() const { return mConvexShape; }
|
btConvexShape* getConvexShape() const { return mConvexShape; }
|
||||||
|
@ -40,17 +41,13 @@ namespace MWPhysics
|
||||||
void commitPositionChange();
|
void commitPositionChange();
|
||||||
|
|
||||||
void setPosition(const osg::Vec3f& position);
|
void setPosition(const osg::Vec3f& position);
|
||||||
|
osg::Vec3f getPosition() const;
|
||||||
|
|
||||||
btCollisionObject* getCollisionObject() const
|
btCollisionObject* getCollisionObject() const
|
||||||
{
|
{
|
||||||
return mCollisionObject.get();
|
return mCollisionObject.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
int getProjectileId() const
|
|
||||||
{
|
|
||||||
return mProjectileId;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isActive() const
|
bool isActive() const
|
||||||
{
|
{
|
||||||
return mActive.load(std::memory_order_acquire);
|
return mActive.load(std::memory_order_acquire);
|
||||||
|
@ -65,18 +62,16 @@ namespace MWPhysics
|
||||||
MWWorld::Ptr getCaster() const;
|
MWWorld::Ptr getCaster() const;
|
||||||
void setCaster(MWWorld::Ptr caster);
|
void setCaster(MWWorld::Ptr caster);
|
||||||
|
|
||||||
osg::Vec3f getHitPos() const
|
bool canTraverseWater() const;
|
||||||
{
|
|
||||||
assert(!mActive);
|
|
||||||
return Misc::Convert::toOsg(mHitPosition);
|
|
||||||
}
|
|
||||||
|
|
||||||
void hit(MWWorld::Ptr target, btVector3 pos, btVector3 normal);
|
void hit(MWWorld::Ptr target, btVector3 pos, btVector3 normal);
|
||||||
void activate();
|
|
||||||
|
|
||||||
void setValidTargets(const std::vector<MWWorld::Ptr>& targets);
|
void setValidTargets(const std::vector<MWWorld::Ptr>& targets);
|
||||||
bool isValidTarget(const MWWorld::Ptr& target) const;
|
bool isValidTarget(const MWWorld::Ptr& target) const;
|
||||||
|
|
||||||
|
std::optional<btVector3> getWaterHitPosition();
|
||||||
|
void setWaterHitPosition(btVector3 pos);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
std::unique_ptr<btCollisionShape> mShape;
|
std::unique_ptr<btCollisionShape> mShape;
|
||||||
|
@ -85,9 +80,12 @@ namespace MWPhysics
|
||||||
std::unique_ptr<btCollisionObject> mCollisionObject;
|
std::unique_ptr<btCollisionObject> mCollisionObject;
|
||||||
btTransform mLocalTransform;
|
btTransform mLocalTransform;
|
||||||
bool mTransformUpdatePending;
|
bool mTransformUpdatePending;
|
||||||
|
bool mCanCrossWaterSurface;
|
||||||
|
bool mCrossedWaterSurface;
|
||||||
std::atomic<bool> mActive;
|
std::atomic<bool> mActive;
|
||||||
MWWorld::Ptr mCaster;
|
MWWorld::Ptr mCaster;
|
||||||
MWWorld::Ptr mHitTarget;
|
MWWorld::Ptr mHitTarget;
|
||||||
|
std::optional<btVector3> mWaterHitPosition;
|
||||||
btVector3 mHitPosition;
|
btVector3 mHitPosition;
|
||||||
btVector3 mHitNormal;
|
btVector3 mHitNormal;
|
||||||
|
|
||||||
|
@ -95,15 +93,11 @@ namespace MWPhysics
|
||||||
|
|
||||||
mutable std::mutex mMutex;
|
mutable std::mutex mMutex;
|
||||||
|
|
||||||
osg::Vec3f mPosition;
|
|
||||||
|
|
||||||
PhysicsSystem *mPhysics;
|
PhysicsSystem *mPhysics;
|
||||||
PhysicsTaskScheduler *mTaskScheduler;
|
PhysicsTaskScheduler *mTaskScheduler;
|
||||||
|
|
||||||
Projectile(const Projectile&);
|
Projectile(const Projectile&);
|
||||||
Projectile& operator=(const Projectile&);
|
Projectile& operator=(const Projectile&);
|
||||||
|
|
||||||
int mProjectileId;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
67
apps/openmw/mwphysics/projectileconvexcallback.cpp
Normal file
67
apps/openmw/mwphysics/projectileconvexcallback.cpp
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
#include "../mwworld/class.hpp"
|
||||||
|
|
||||||
|
#include "actor.hpp"
|
||||||
|
#include "collisiontype.hpp"
|
||||||
|
#include "projectile.hpp"
|
||||||
|
#include "projectileconvexcallback.hpp"
|
||||||
|
#include "ptrholder.hpp"
|
||||||
|
|
||||||
|
namespace MWPhysics
|
||||||
|
{
|
||||||
|
ProjectileConvexCallback::ProjectileConvexCallback(const btCollisionObject* me, const btVector3& from, const btVector3& to, Projectile* proj)
|
||||||
|
: btCollisionWorld::ClosestConvexResultCallback(from, to)
|
||||||
|
, mMe(me), mProjectile(proj)
|
||||||
|
{
|
||||||
|
assert(mProjectile);
|
||||||
|
}
|
||||||
|
|
||||||
|
btScalar ProjectileConvexCallback::addSingleResult(btCollisionWorld::LocalConvexResult& result, bool normalInWorldSpace)
|
||||||
|
{
|
||||||
|
// don't hit the caster
|
||||||
|
if (result.m_hitCollisionObject == mMe)
|
||||||
|
return 1.f;
|
||||||
|
|
||||||
|
// don't hit the projectile
|
||||||
|
if (result.m_hitCollisionObject == mProjectile->getCollisionObject())
|
||||||
|
return 1.f;
|
||||||
|
|
||||||
|
btCollisionWorld::ClosestConvexResultCallback::addSingleResult(result, normalInWorldSpace);
|
||||||
|
switch (result.m_hitCollisionObject->getBroadphaseHandle()->m_collisionFilterGroup)
|
||||||
|
{
|
||||||
|
case CollisionType_Actor:
|
||||||
|
{
|
||||||
|
auto* target = static_cast<Actor*>(result.m_hitCollisionObject->getUserPointer());
|
||||||
|
if (!mProjectile->isValidTarget(target->getPtr()))
|
||||||
|
return 1.f;
|
||||||
|
mProjectile->hit(target->getPtr(), result.m_hitPointLocal, result.m_hitNormalLocal);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CollisionType_Projectile:
|
||||||
|
{
|
||||||
|
auto* target = static_cast<Projectile*>(result.m_hitCollisionObject->getUserPointer());
|
||||||
|
if (!mProjectile->isValidTarget(target->getCaster()))
|
||||||
|
return 1.f;
|
||||||
|
target->hit(mProjectile->getPtr(), m_hitPointWorld, m_hitNormalWorld);
|
||||||
|
mProjectile->hit(target->getPtr(), m_hitPointWorld, m_hitNormalWorld);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CollisionType_Water:
|
||||||
|
{
|
||||||
|
mProjectile->setWaterHitPosition(m_hitPointWorld);
|
||||||
|
if (mProjectile->canTraverseWater())
|
||||||
|
return 1.f;
|
||||||
|
mProjectile->hit(MWWorld::Ptr(), m_hitPointWorld, m_hitNormalWorld);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
mProjectile->hit(MWWorld::Ptr(), m_hitPointWorld, m_hitNormalWorld);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.m_hitFraction;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
27
apps/openmw/mwphysics/projectileconvexcallback.hpp
Normal file
27
apps/openmw/mwphysics/projectileconvexcallback.hpp
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
#ifndef OPENMW_MWPHYSICS_PROJECTILECONVEXCALLBACK_H
|
||||||
|
#define OPENMW_MWPHYSICS_PROJECTILECONVEXCALLBACK_H
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <BulletCollision/CollisionDispatch/btCollisionWorld.h>
|
||||||
|
|
||||||
|
class btCollisionObject;
|
||||||
|
|
||||||
|
namespace MWPhysics
|
||||||
|
{
|
||||||
|
class Projectile;
|
||||||
|
|
||||||
|
class ProjectileConvexCallback : public btCollisionWorld::ClosestConvexResultCallback
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ProjectileConvexCallback(const btCollisionObject* me, const btVector3& from, const btVector3& to, Projectile* proj);
|
||||||
|
|
||||||
|
btScalar addSingleResult(btCollisionWorld::LocalConvexResult& result, bool normalInWorldSpace) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const btCollisionObject* mMe;
|
||||||
|
Projectile* mProjectile;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -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.
|
/// @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(),
|
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>(),
|
std::vector<MWWorld::Ptr> targets = std::vector<MWWorld::Ptr>(),
|
||||||
int mask = CollisionType_World|CollisionType_HeightMap|CollisionType_Actor|CollisionType_Door, int group=0xff, int projId=-1) const = 0;
|
int mask = CollisionType_World|CollisionType_HeightMap|CollisionType_Actor|CollisionType_Door, int group=0xff) const = 0;
|
||||||
|
|
||||||
virtual RayCastingResult castSphere(const osg::Vec3f& from, const osg::Vec3f& to, float radius) const = 0;
|
virtual RayCastingResult castSphere(const osg::Vec3f& from, const osg::Vec3f& to, float radius) const = 0;
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
#include "collisiontype.hpp"
|
#include "collisiontype.hpp"
|
||||||
#include "actor.hpp"
|
#include "actor.hpp"
|
||||||
#include "closestnotmeconvexresultcallback.hpp"
|
#include "actorconvexcallback.hpp"
|
||||||
|
|
||||||
namespace MWPhysics
|
namespace MWPhysics
|
||||||
{
|
{
|
||||||
|
@ -24,7 +24,7 @@ void ActorTracer::doTrace(const btCollisionObject *actor, const osg::Vec3f& star
|
||||||
to.setOrigin(btend);
|
to.setOrigin(btend);
|
||||||
|
|
||||||
const btVector3 motion = btstart-btend;
|
const btVector3 motion = btstart-btend;
|
||||||
ClosestNotMeConvexResultCallback newTraceCallback(actor, motion, btScalar(0.0), world);
|
ActorConvexCallback newTraceCallback(actor, motion, btScalar(0.0), world);
|
||||||
// Inherit the actor's collision group and mask
|
// Inherit the actor's collision group and mask
|
||||||
newTraceCallback.m_collisionFilterGroup = actor->getBroadphaseHandle()->m_collisionFilterGroup;
|
newTraceCallback.m_collisionFilterGroup = actor->getBroadphaseHandle()->m_collisionFilterGroup;
|
||||||
newTraceCallback.m_collisionFilterMask = actor->getBroadphaseHandle()->m_collisionFilterMask;
|
newTraceCallback.m_collisionFilterMask = actor->getBroadphaseHandle()->m_collisionFilterMask;
|
||||||
|
@ -62,7 +62,7 @@ void ActorTracer::findGround(const Actor* actor, const osg::Vec3f& start, const
|
||||||
btTransform to(trans.getBasis(), btend);
|
btTransform to(trans.getBasis(), btend);
|
||||||
|
|
||||||
const btVector3 motion = btstart-btend;
|
const btVector3 motion = btstart-btend;
|
||||||
ClosestNotMeConvexResultCallback newTraceCallback(actor->getCollisionObject(), motion, btScalar(0.0), world);
|
ActorConvexCallback newTraceCallback(actor->getCollisionObject(), motion, btScalar(0.0), world);
|
||||||
// Inherit the actor's collision group and mask
|
// Inherit the actor's collision group and mask
|
||||||
newTraceCallback.m_collisionFilterGroup = actor->getCollisionObject()->getBroadphaseHandle()->m_collisionFilterGroup;
|
newTraceCallback.m_collisionFilterGroup = actor->getCollisionObject()->getBroadphaseHandle()->m_collisionFilterGroup;
|
||||||
newTraceCallback.m_collisionFilterMask = actor->getCollisionObject()->getBroadphaseHandle()->m_collisionFilterMask;
|
newTraceCallback.m_collisionFilterMask = actor->getCollisionObject()->getBroadphaseHandle()->m_collisionFilterMask;
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
#include <osg/PositionAttitudeTransform>
|
#include <osg/PositionAttitudeTransform>
|
||||||
|
|
||||||
#include <components/debug/debuglog.hpp>
|
#include <components/debug/debuglog.hpp>
|
||||||
|
@ -43,6 +44,7 @@
|
||||||
|
|
||||||
#include "../mwsound/sound.hpp"
|
#include "../mwsound/sound.hpp"
|
||||||
|
|
||||||
|
#include "../mwphysics/collisiontype.hpp"
|
||||||
#include "../mwphysics/physicssystem.hpp"
|
#include "../mwphysics/physicssystem.hpp"
|
||||||
#include "../mwphysics/projectile.hpp"
|
#include "../mwphysics/projectile.hpp"
|
||||||
|
|
||||||
|
@ -187,7 +189,7 @@ namespace MWWorld
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
void ProjectileManager::createModel(State &state, const std::string &model, const osg::Vec3f& pos, const osg::Quat& orient,
|
float ProjectileManager::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)
|
bool rotate, bool createLight, osg::Vec4 lightDiffuseColor, std::string texture)
|
||||||
{
|
{
|
||||||
state.mNode = new osg::PositionAttitudeTransform;
|
state.mNode = new osg::PositionAttitudeTransform;
|
||||||
|
@ -251,6 +253,7 @@ namespace MWWorld
|
||||||
state.mNode->accept(assignVisitor);
|
state.mNode->accept(assignVisitor);
|
||||||
|
|
||||||
MWRender::overrideFirstRootTexture(texture, mResourceSystem, projectile);
|
MWRender::overrideFirstRootTexture(texture, mResourceSystem, projectile);
|
||||||
|
return projectile->getBound().radius();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProjectileManager::update(State& state, float duration)
|
void ProjectileManager::update(State& state, float duration)
|
||||||
|
@ -305,7 +308,7 @@ namespace MWWorld
|
||||||
|
|
||||||
osg::Vec4 lightDiffuseColor = getMagicBoltLightDiffuseColor(state.mEffects);
|
osg::Vec4 lightDiffuseColor = getMagicBoltLightDiffuseColor(state.mEffects);
|
||||||
|
|
||||||
createModel(state, ptr.getClass().getModel(ptr), pos, orient, true, true, lightDiffuseColor, texture);
|
const auto radius = createModel(state, ptr.getClass().getModel(ptr), pos, orient, true, true, lightDiffuseColor, texture);
|
||||||
|
|
||||||
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
|
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
|
||||||
for (const std::string &soundid : state.mSoundIds)
|
for (const std::string &soundid : state.mSoundIds)
|
||||||
|
@ -316,7 +319,7 @@ namespace MWWorld
|
||||||
state.mSounds.push_back(sound);
|
state.mSounds.push_back(sound);
|
||||||
}
|
}
|
||||||
|
|
||||||
state.mProjectileId = mPhysics->addProjectile(caster, pos);
|
state.mProjectileId = mPhysics->addProjectile(caster, pos, radius, false);
|
||||||
state.mToDelete = false;
|
state.mToDelete = false;
|
||||||
mMagicBolts.push_back(state);
|
mMagicBolts.push_back(state);
|
||||||
}
|
}
|
||||||
|
@ -340,7 +343,7 @@ namespace MWWorld
|
||||||
if (!ptr.getClass().getEnchantment(ptr).empty())
|
if (!ptr.getClass().getEnchantment(ptr).empty())
|
||||||
SceneUtil::addEnchantedGlow(state.mNode, mResourceSystem, ptr.getClass().getEnchantmentColor(ptr));
|
SceneUtil::addEnchantedGlow(state.mNode, mResourceSystem, ptr.getClass().getEnchantmentColor(ptr));
|
||||||
|
|
||||||
state.mProjectileId = mPhysics->addProjectile(actor, pos);
|
state.mProjectileId = mPhysics->addProjectile(actor, pos, 1.f, true);
|
||||||
state.mToDelete = false;
|
state.mToDelete = false;
|
||||||
mProjectiles.push_back(state);
|
mProjectiles.push_back(state);
|
||||||
}
|
}
|
||||||
|
@ -407,15 +410,7 @@ namespace MWWorld
|
||||||
float speed = fTargetSpellMaxSpeed * magicBoltState.mSpeed;
|
float speed = fTargetSpellMaxSpeed * magicBoltState.mSpeed;
|
||||||
osg::Vec3f direction = orient * osg::Vec3f(0,1,0);
|
osg::Vec3f direction = orient * osg::Vec3f(0,1,0);
|
||||||
direction.normalize();
|
direction.normalize();
|
||||||
osg::Vec3f pos(magicBoltState.mNode->getPosition());
|
osg::Vec3f newPos = projectile->getPosition() + direction * duration * speed;
|
||||||
osg::Vec3f newPos = pos + direction * duration * speed;
|
|
||||||
|
|
||||||
for (const auto& sound : magicBoltState.mSounds)
|
|
||||||
sound->setPosition(newPos);
|
|
||||||
|
|
||||||
magicBoltState.mNode->setPosition(newPos);
|
|
||||||
|
|
||||||
mPhysics->updateProjectile(magicBoltState.mProjectileId, newPos);
|
|
||||||
|
|
||||||
update(magicBoltState, duration);
|
update(magicBoltState, duration);
|
||||||
|
|
||||||
|
@ -425,41 +420,7 @@ namespace MWWorld
|
||||||
caster.getClass().getCreatureStats(caster).getAiSequence().getCombatTargets(targetActors);
|
caster.getClass().getCreatureStats(caster).getAiSequence().getCombatTargets(targetActors);
|
||||||
projectile->setValidTargets(targetActors);
|
projectile->setValidTargets(targetActors);
|
||||||
|
|
||||||
// Check for impact
|
mPhysics->updateProjectile(magicBoltState.mProjectileId, newPos);
|
||||||
// TODO: use a proper btRigidBody / btGhostObject?
|
|
||||||
const auto result = mPhysics->castRay(pos, newPos, caster, targetActors, 0xff, MWPhysics::CollisionType_Projectile, magicBoltState.mProjectileId);
|
|
||||||
|
|
||||||
bool hit = false;
|
|
||||||
if (result.mHit)
|
|
||||||
{
|
|
||||||
hit = true;
|
|
||||||
if (result.mHitObject.isEmpty())
|
|
||||||
{
|
|
||||||
// terrain or projectile
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
MWMechanics::CastSpell cast(caster, result.mHitObject);
|
|
||||||
cast.mHitPosition = pos;
|
|
||||||
cast.mId = magicBoltState.mSpellId;
|
|
||||||
cast.mSourceName = magicBoltState.mSourceName;
|
|
||||||
cast.mStack = false;
|
|
||||||
cast.inflict(result.mHitObject, caster, magicBoltState.mEffects, ESM::RT_Target, false, true);
|
|
||||||
mPhysics->reportCollision(Misc::Convert::toBullet(result.mHitPos), Misc::Convert::toBullet(result.mHitNormal));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Explodes when hitting water
|
|
||||||
if (MWBase::Environment::get().getWorld()->isUnderwater(MWMechanics::getPlayer().getCell(), newPos))
|
|
||||||
hit = true;
|
|
||||||
|
|
||||||
if (hit)
|
|
||||||
{
|
|
||||||
MWBase::Environment::get().getWorld()->explodeSpell(pos, magicBoltState.mEffects, caster, result.mHitObject,
|
|
||||||
ESM::RT_Target, magicBoltState.mSpellId, magicBoltState.mSourceName);
|
|
||||||
|
|
||||||
cleanupMagicBolt(magicBoltState);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -467,9 +428,6 @@ namespace MWWorld
|
||||||
{
|
{
|
||||||
for (auto& projectileState : mProjectiles)
|
for (auto& projectileState : mProjectiles)
|
||||||
{
|
{
|
||||||
if (projectileState.mToDelete)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
auto* projectile = mPhysics->getProjectile(projectileState.mProjectileId);
|
auto* projectile = mPhysics->getProjectile(projectileState.mProjectileId);
|
||||||
if (!projectile->isActive())
|
if (!projectile->isActive())
|
||||||
continue;
|
continue;
|
||||||
|
@ -477,8 +435,7 @@ namespace MWWorld
|
||||||
// simulating aerodynamics at all
|
// simulating aerodynamics at all
|
||||||
projectileState.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(projectileState.mNode->getPosition());
|
osg::Vec3f newPos = projectile->getPosition() + projectileState.mVelocity * duration;
|
||||||
osg::Vec3f newPos = pos + projectileState.mVelocity * duration;
|
|
||||||
|
|
||||||
// rotation does not work well for throwing projectiles - their roll angle will depend on shooting direction.
|
// rotation does not work well for throwing projectiles - their roll angle will depend on shooting direction.
|
||||||
if (!projectileState.mThrown)
|
if (!projectileState.mThrown)
|
||||||
|
@ -488,10 +445,6 @@ namespace MWWorld
|
||||||
projectileState.mNode->setAttitude(orient);
|
projectileState.mNode->setAttitude(orient);
|
||||||
}
|
}
|
||||||
|
|
||||||
projectileState.mNode->setPosition(newPos);
|
|
||||||
|
|
||||||
mPhysics->updateProjectile(projectileState.mProjectileId, newPos);
|
|
||||||
|
|
||||||
update(projectileState, duration);
|
update(projectileState, duration);
|
||||||
|
|
||||||
MWWorld::Ptr caster = projectileState.getCaster();
|
MWWorld::Ptr caster = projectileState.getCaster();
|
||||||
|
@ -502,36 +455,7 @@ namespace MWWorld
|
||||||
caster.getClass().getCreatureStats(caster).getAiSequence().getCombatTargets(targetActors);
|
caster.getClass().getCreatureStats(caster).getAiSequence().getCombatTargets(targetActors);
|
||||||
projectile->setValidTargets(targetActors);
|
projectile->setValidTargets(targetActors);
|
||||||
|
|
||||||
// Check for impact
|
mPhysics->updateProjectile(projectileState.mProjectileId, newPos);
|
||||||
// TODO: use a proper btRigidBody / btGhostObject?
|
|
||||||
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(), 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (caster.isEmpty())
|
|
||||||
caster = result.mHitObject;
|
|
||||||
|
|
||||||
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(projectileState);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -543,17 +467,19 @@ namespace MWWorld
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
auto* projectile = mPhysics->getProjectile(projectileState.mProjectileId);
|
auto* projectile = mPhysics->getProjectile(projectileState.mProjectileId);
|
||||||
|
|
||||||
|
if (const auto hitWaterPos = projectile->getWaterHitPosition())
|
||||||
|
mRendering->emitWaterRipple(Misc::Convert::toOsg(*hitWaterPos));
|
||||||
|
|
||||||
|
const auto pos = projectile->getPosition();
|
||||||
|
projectileState.mNode->setPosition(pos);
|
||||||
|
|
||||||
if (projectile->isActive())
|
if (projectile->isActive())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
const auto target = projectile->getTarget();
|
const auto target = projectile->getTarget();
|
||||||
const auto pos = projectile->getHitPos();
|
auto caster = projectileState.getCaster();
|
||||||
MWWorld::Ptr caster = projectileState.getCaster();
|
|
||||||
assert(target != caster);
|
assert(target != caster);
|
||||||
if (!projectile->isValidTarget(target))
|
|
||||||
{
|
|
||||||
projectile->activate();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (caster.isEmpty())
|
if (caster.isEmpty())
|
||||||
caster = target;
|
caster = target;
|
||||||
|
@ -569,9 +495,8 @@ namespace MWWorld
|
||||||
bow = *invIt;
|
bow = *invIt;
|
||||||
}
|
}
|
||||||
|
|
||||||
projectileState.mHitPosition = pos;
|
|
||||||
cleanupProjectile(projectileState);
|
|
||||||
MWMechanics::projectileHit(caster, target, bow, projectileRef.getPtr(), pos, projectileState.mAttackStrength);
|
MWMechanics::projectileHit(caster, target, bow, projectileRef.getPtr(), pos, projectileState.mAttackStrength);
|
||||||
|
cleanupProjectile(projectileState);
|
||||||
}
|
}
|
||||||
for (auto& magicBoltState : mMagicBolts)
|
for (auto& magicBoltState : mMagicBolts)
|
||||||
{
|
{
|
||||||
|
@ -579,20 +504,18 @@ namespace MWWorld
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
auto* projectile = mPhysics->getProjectile(magicBoltState.mProjectileId);
|
auto* projectile = mPhysics->getProjectile(magicBoltState.mProjectileId);
|
||||||
|
|
||||||
|
const auto pos = projectile->getPosition();
|
||||||
|
magicBoltState.mNode->setPosition(pos);
|
||||||
|
for (const auto& sound : magicBoltState.mSounds)
|
||||||
|
sound->setPosition(pos);
|
||||||
|
|
||||||
if (projectile->isActive())
|
if (projectile->isActive())
|
||||||
continue;
|
continue;
|
||||||
const auto target = projectile->getTarget();
|
|
||||||
const auto pos = projectile->getHitPos();
|
|
||||||
MWWorld::Ptr caster = magicBoltState.getCaster();
|
|
||||||
assert(target != caster);
|
|
||||||
if (!projectile->isValidTarget(target))
|
|
||||||
{
|
|
||||||
projectile->activate();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
magicBoltState.mHitPosition = pos;
|
const auto target = projectile->getTarget();
|
||||||
cleanupMagicBolt(magicBoltState);
|
const auto caster = magicBoltState.getCaster();
|
||||||
|
assert(target != caster);
|
||||||
|
|
||||||
MWMechanics::CastSpell cast(caster, target);
|
MWMechanics::CastSpell cast(caster, target);
|
||||||
cast.mHitPosition = pos;
|
cast.mHitPosition = pos;
|
||||||
|
@ -602,6 +525,7 @@ namespace MWWorld
|
||||||
cast.inflict(target, caster, magicBoltState.mEffects, ESM::RT_Target, false, true);
|
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);
|
MWBase::Environment::get().getWorld()->explodeSpell(pos, magicBoltState.mEffects, caster, target, ESM::RT_Target, magicBoltState.mSpellId, magicBoltState.mSourceName);
|
||||||
|
cleanupMagicBolt(magicBoltState);
|
||||||
}
|
}
|
||||||
mProjectiles.erase(std::remove_if(mProjectiles.begin(), mProjectiles.end(), [](const State& state) { return state.mToDelete; }),
|
mProjectiles.erase(std::remove_if(mProjectiles.begin(), mProjectiles.end(), [](const State& state) { return state.mToDelete; }),
|
||||||
mProjectiles.end());
|
mProjectiles.end());
|
||||||
|
@ -702,7 +626,7 @@ namespace MWWorld
|
||||||
int weaponType = ptr.get<ESM::Weapon>()->mBase->mData.mType;
|
int weaponType = ptr.get<ESM::Weapon>()->mBase->mData.mType;
|
||||||
state.mThrown = MWMechanics::getWeaponType(weaponType)->mWeaponClass == ESM::WeaponType::Thrown;
|
state.mThrown = MWMechanics::getWeaponType(weaponType)->mWeaponClass == ESM::WeaponType::Thrown;
|
||||||
|
|
||||||
state.mProjectileId = mPhysics->addProjectile(state.getCaster(), osg::Vec3f(esm.mPosition));
|
state.mProjectileId = mPhysics->addProjectile(state.getCaster(), osg::Vec3f(esm.mPosition), 1.f, true);
|
||||||
}
|
}
|
||||||
catch(...)
|
catch(...)
|
||||||
{
|
{
|
||||||
|
@ -747,7 +671,6 @@ namespace MWWorld
|
||||||
MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), state.mIdMagic.at(0));
|
MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), state.mIdMagic.at(0));
|
||||||
MWWorld::Ptr ptr = ref.getPtr();
|
MWWorld::Ptr ptr = ref.getPtr();
|
||||||
model = ptr.getClass().getModel(ptr);
|
model = ptr.getClass().getModel(ptr);
|
||||||
state.mProjectileId = mPhysics->addProjectile(state.getCaster(), osg::Vec3f(esm.mPosition));
|
|
||||||
}
|
}
|
||||||
catch(...)
|
catch(...)
|
||||||
{
|
{
|
||||||
|
@ -755,7 +678,8 @@ namespace MWWorld
|
||||||
}
|
}
|
||||||
|
|
||||||
osg::Vec4 lightDiffuseColor = getMagicBoltLightDiffuseColor(state.mEffects);
|
osg::Vec4 lightDiffuseColor = getMagicBoltLightDiffuseColor(state.mEffects);
|
||||||
createModel(state, model, osg::Vec3f(esm.mPosition), osg::Quat(esm.mOrientation), true, true, lightDiffuseColor, texture);
|
const auto radius = createModel(state, model, osg::Vec3f(esm.mPosition), osg::Quat(esm.mOrientation), true, true, lightDiffuseColor, texture);
|
||||||
|
state.mProjectileId = mPhysics->addProjectile(state.getCaster(), osg::Vec3f(esm.mPosition), radius, false);
|
||||||
|
|
||||||
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
|
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
|
||||||
for (const std::string &soundid : state.mSoundIds)
|
for (const std::string &soundid : state.mSoundIds)
|
||||||
|
|
|
@ -80,8 +80,6 @@ namespace MWWorld
|
||||||
int mActorId;
|
int mActorId;
|
||||||
int mProjectileId;
|
int mProjectileId;
|
||||||
|
|
||||||
osg::Vec3f mHitPosition;
|
|
||||||
|
|
||||||
// TODO: this will break when the game is saved and reloaded, since there is currently
|
// 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.
|
// no way to write identifiers for non-actors to a savegame.
|
||||||
MWWorld::Ptr mCasterHandle;
|
MWWorld::Ptr mCasterHandle;
|
||||||
|
@ -132,7 +130,7 @@ namespace MWWorld
|
||||||
void moveProjectiles(float dt);
|
void moveProjectiles(float dt);
|
||||||
void moveMagicBolts(float dt);
|
void moveMagicBolts(float dt);
|
||||||
|
|
||||||
void createModel (State& state, const std::string& model, const osg::Vec3f& pos, const osg::Quat& orient,
|
float 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 = "");
|
bool rotate, bool createLight, osg::Vec4 lightDiffuseColor, std::string texture = "");
|
||||||
void update (State& state, float duration);
|
void update (State& state, float duration);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue