1
0
mirror of https://gitlab.com/OpenMW/openmw.git synced 2025-01-01 03:21:41 +00:00
OpenMW/apps/openmw/mwphysics/projectile.cpp
2022-10-09 10:39:43 +00:00

123 lines
3.9 KiB
C++

#include <memory>
#include <BulletCollision/CollisionShapes/btSphereShape.h>
#include <components/misc/convert.hpp>
#include "actor.hpp"
#include "collisiontype.hpp"
#include "mtphysics.hpp"
#include "object.hpp"
#include "projectile.hpp"
namespace MWPhysics
{
Projectile::Projectile(const MWWorld::Ptr& caster, const osg::Vec3f& position, float radius,
PhysicsTaskScheduler* scheduler, PhysicsSystem* physicssystem)
: PtrHolder(MWWorld::Ptr(), position)
, mHitWater(false)
, mActive(true)
, mHitTarget(nullptr)
, mPhysics(physicssystem)
, mTaskScheduler(scheduler)
{
mShape = std::make_unique<btSphereShape>(radius);
mConvexShape = static_cast<btConvexShape*>(mShape.get());
mCollisionObject = std::make_unique<btCollisionObject>();
mCollisionObject->setCollisionFlags(btCollisionObject::CF_KINEMATIC_OBJECT);
mCollisionObject->setActivationState(DISABLE_DEACTIVATION);
mCollisionObject->setCollisionShape(mShape.get());
mCollisionObject->setUserPointer(this);
mPosition = position;
mPreviousPosition = position;
mSimulationPosition = position;
setCaster(caster);
const int collisionMask = CollisionType_World | CollisionType_HeightMap | CollisionType_Actor
| CollisionType_Door | CollisionType_Water | CollisionType_Projectile;
mTaskScheduler->addCollisionObject(mCollisionObject.get(), CollisionType_Projectile, collisionMask);
updateCollisionObjectPosition();
}
Projectile::~Projectile()
{
if (!mActive)
mPhysics->reportCollision(mHitPosition, mHitNormal);
mTaskScheduler->removeCollisionObject(mCollisionObject.get());
}
void Projectile::updateCollisionObjectPosition()
{
std::scoped_lock lock(mMutex);
auto& trans = mCollisionObject->getWorldTransform();
trans.setOrigin(Misc::Convert::toBullet(mPosition));
mCollisionObject->setWorldTransform(trans);
}
void Projectile::hit(const btCollisionObject* target, btVector3 pos, btVector3 normal)
{
bool active = true;
if (!mActive.compare_exchange_strong(active, false, std::memory_order_relaxed) || !active)
return;
mHitTarget = target;
mHitPosition = pos;
mHitNormal = normal;
}
MWWorld::Ptr Projectile::getTarget() const
{
assert(!mActive);
auto* target = static_cast<PtrHolder*>(mHitTarget->getUserPointer());
return target ? target->getPtr() : MWWorld::Ptr();
}
MWWorld::Ptr Projectile::getCaster() const
{
return mCaster;
}
void Projectile::setCaster(const MWWorld::Ptr& caster)
{
mCaster = caster;
mCasterColObj = [this, &caster]() -> const btCollisionObject* {
const Actor* actor = mPhysics->getActor(caster);
if (actor)
return actor->getCollisionObject();
const Object* object = mPhysics->getObject(caster);
if (object)
return object->getCollisionObject();
return nullptr;
}();
}
void Projectile::setValidTargets(const std::vector<MWWorld::Ptr>& targets)
{
std::scoped_lock lock(mMutex);
mValidTargets.clear();
for (const auto& ptr : targets)
{
const auto* physicActor = mPhysics->getActor(ptr);
if (physicActor)
mValidTargets.push_back(physicActor->getCollisionObject());
}
}
bool Projectile::isValidTarget(const btCollisionObject* target) const
{
assert(target);
std::scoped_lock lock(mMutex);
if (mCasterColObj == target)
return false;
if (mValidTargets.empty())
return true;
return std::any_of(mValidTargets.begin(), mValidTargets.end(),
[target](const btCollisionObject* actor) { return target == actor; });
}
}