mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-01-01 12:01:51 +00:00
123 lines
3.9 KiB
C++
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; });
|
|
}
|
|
|
|
}
|