1
0
mirror of https://gitlab.com/OpenMW/openmw.git synced 2025-01-16 16:10:58 +00:00
OpenMW/apps/openmw/mwphysics/actor.cpp
fredzio b13afd758c Remove both racy and useless code.
Actor's position can be determined in 3 ways:
1/ as a result of physics simulation
2/ after a script require a relative position change (SetPos, Move)
3/ absolutely set from games mechanics event (teleport) or script
(PositionCell)

In case 1/, RefData::mPosition is updated with the physics simulation result
In case 2/, when RefData::mPosition is updated, physics simulation is informed of the change and update accordingly
In case 3/, when RefData::mPosition is updated, the physics simulation state is reset

In all 3 cases, we don't need to check the RefData::mPosition to get a
correct behaviour.

TSAN reported the following data race:
  Read of size 4 at 0x7b50005b75b0 by thread T12 (mutexes: write M656173, write M84859534346343880):
    #0 ESM::Position::asVec3() const /build/openmw/openmw/master2/.build/freebsd/TSAN/../../.././components/esm/defs.hpp:55:27 (openmw+0xb809d5)
    #1 MWPhysics::Actor::updateWorldPosition() /build/openmw/openmw/master2/.build/freebsd/TSAN/../../../apps/openmw/mwphysics/actor.cpp:131:59 (openmw+0xb809d5)
    #2 MWPhysics::Actor::setPosition(osg::Vec3f const&) /build/openmw/openmw/master2/.build/freebsd/TSAN/../../../apps/openmw/mwphysics/actor.cpp:177:5 (openmw+0xb809d5)
    #3 MWPhysics::PhysicsTaskScheduler::updateActorsPositions() /build/openmw/openmw/master2/.build/freebsd/TSAN/../../../apps/openmw/mwphysics/mtphysics.cpp:524:28 (openmw+0xb91ac0)
    #4 MWPhysics::PhysicsTaskScheduler::afterPostStep() /build/openmw/openmw/master2/.build/freebsd/TSAN/../../../apps/openmw/mwphysics/mtphysics.cpp:614:13 (openmw+0xb915e7)
    #5 MWPhysics::PhysicsTaskScheduler::worker()::$_5::operator()() const /build/openmw/openmw/master2/.build/freebsd/TSAN/../../../apps/openmw/mwphysics/mtphysics.cpp:498:45 (openmw+0xb915e7)
    #6 void Misc::Barrier::wait<MWPhysics::PhysicsTaskScheduler::worker()::$_5>(MWPhysics::PhysicsTaskScheduler::worker()::$_5&&) /build/openmw/openmw/master2/.build/freebsd/TSAN/../../.././components/misc/barrier.hpp:30:21 (openmw+0xb915e7)
    #7 MWPhysics::PhysicsTaskScheduler::worker() /build/openmw/openmw/master2/.build/freebsd/TSAN/../../../apps/openmw/mwphysics/mtphysics.cpp:498:31 (openmw+0xb915e7)
    #8 MWPhysics::PhysicsTaskScheduler::PhysicsTaskScheduler(float, btCollisionWorld*, MWRender::DebugDrawer*)::$_0::operator()() const /build/openmw/openmw/master2/.build/freebsd/TSAN/../../../apps/openmw/mwphysics/mtphysics.cpp:162:45 (openmw+0xb92630)
    #9 decltype(std::__1::forward<MWPhysics::PhysicsTaskScheduler::PhysicsTaskScheduler(float, btCollisionWorld*, MWRender::DebugDrawer*)::$_0>(fp)()) std::__1::__invoke<MWPhysics::PhysicsTaskScheduler::PhysicsTaskScheduler(float, btCollisionWorld*, MWRender::DebugDrawer*)::$_0>(MWPhysics::PhysicsTaskScheduler::PhysicsTaskScheduler(float, btCollisionWorld*, MWRender::DebugDrawer*)::$_0&&) /usr/include/c++/v1/type_traits:3899:1 (openmw+0xb92630)
    #10 void std::__1::__thread_execute<std::__1::unique_ptr<std::__1::__thread_struct, std::__1::default_delete<std::__1::__thread_struct> >, MWPhysics::PhysicsTaskScheduler::PhysicsTaskScheduler(float, btCollisionWorld*, MWRender::DebugDrawer*)::$_0>(std::__1::tuple<std::__1::unique_ptr<std::__1::__thread_struct, std::__1::default_delete<std::__1::__thread_struct> >, MWPhysics::PhysicsTaskScheduler::PhysicsTaskScheduler(float, btCollisionWorld*, MWRender::DebugDrawer*)::$_0>&, std::__1::__tuple_indices<>) /usr/include/c++/v1/thread:280:5 (openmw+0xb92630)
    #11 void* std::__1::__thread_proxy<std::__1::tuple<std::__1::unique_ptr<std::__1::__thread_struct, std::__1::default_delete<std::__1::__thread_struct> >, MWPhysics::PhysicsTaskScheduler::PhysicsTaskScheduler(float, btCollisionWorld*, MWRender::DebugDrawer*)::$_0> >(void*) /usr/include/c++/v1/thread:291:5 (openmw+0xb92630)

  Previous write of size 8 at 0x7b50005b75b0 by main thread:
    #0 memcpy /wrkdirs/usr/ports/devel/llvm-devel/work-default/llvm-project-3f6753efe1990a928ed120bd907940a9fb3e2fc3/compiler-rt/lib/tsan/../sanitizer_common/sanitizer_common_interceptors.inc:827:5 (openmw+0x55a057)
    #1 MWWorld::RefData::setPosition(ESM::Position const&) /build/openmw/openmw/master2/.build/freebsd/TSAN/../../../apps/openmw/mwworld/refdata.cpp:216:19 (openmw+0xa3de1c)
    #2 MWWorld::World::moveObject(MWWorld::Ptr const&, MWWorld::CellStore*, float, float, float, bool) /build/openmw/openmw/master2/.build/freebsd/TSAN/../../../apps/openmw/mwworld/worldimp.cpp:1130:26 (openmw+0xa57300)
    #3 MWWorld::World::moveObject(MWWorld::Ptr const&, float, float, float, bool, bool) /build/openmw/openmw/master2/.build/freebsd/TSAN/../../../apps/openmw/mwworld/worldimp.cpp:1253:16 (openmw+0xa580c8)
    #4 MWWorld::World::doPhysics(float, unsigned long long, unsigned int, osg::Stats&) /build/openmw/openmw/master2/.build/freebsd/TSAN/../../../apps/openmw/mwworld/worldimp.cpp:1530:17 (openmw+0xa5af8f)
    #5 MWWorld::World::updatePhysics(float, bool, unsigned long long, unsigned int, osg::Stats&) /build/openmw/openmw/master2/.build/freebsd/TSAN/../../../apps/openmw/mwworld/worldimp.cpp:1862:13 (openmw+0xa61a7c)
    #6 OMW::Engine::frame(float) /build/openmw/openmw/master2/.build/freebsd/TSAN/../../../apps/openmw/engine.cpp:333:42 (openmw+0xcce9e7)
    #7 OMW::Engine::go() /build/openmw/openmw/master2/.build/freebsd/TSAN/../../../apps/openmw/engine.cpp:935:14 (openmw+0xcd86ed)
    #8 runApplication(int, char**) /build/openmw/openmw/master2/.build/freebsd/TSAN/../../../apps/openmw/main.cpp:296:17 (openmw+0xcbffac)
    #9 wrapApplication(int (*)(int, char**), int, char**, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&) /build/openmw/openmw/master2/.build/freebsd/TSAN/../../../components/debug/debugging.cpp:205:15 (openmw+0x1335442)
    #10 main /build/openmw/openmw/master2/.build/freebsd/TSAN/../../../apps/openmw/main.cpp:308:12 (openmw+0xcc008a)
:wqa
2021-05-20 20:46:44 +02:00

304 lines
8.0 KiB
C++

#include "actor.hpp"
#include <BulletCollision/CollisionShapes/btBoxShape.h>
#include <BulletCollision/CollisionDispatch/btCollisionWorld.h>
#include <components/sceneutil/positionattitudetransform.hpp>
#include <components/resource/bulletshape.hpp>
#include <components/debug/debuglog.hpp>
#include <components/misc/convert.hpp>
#include "../mwworld/class.hpp"
#include "collisiontype.hpp"
#include "mtphysics.hpp"
#include <cmath>
namespace MWPhysics
{
Actor::Actor(const MWWorld::Ptr& ptr, const Resource::BulletShape* shape, PhysicsTaskScheduler* scheduler)
: mStandingOnPtr(nullptr), mCanWaterWalk(false), mWalkingOnWater(false)
, mCollisionObject(nullptr), mMeshTranslation(shape->mCollisionBox.center), mHalfExtents(shape->mCollisionBox.extents)
, mVelocity(0,0,0), mStuckFrames(0), mLastStuckPosition{0, 0, 0}
, mForce(0.f, 0.f, 0.f), mOnGround(true), mOnSlope(false)
, mInternalCollisionMode(true)
, mExternalCollisionMode(true)
, mTaskScheduler(scheduler)
{
mPtr = ptr;
// We can not create actor without collisions - he will fall through the ground.
// In this case we should autogenerate collision box based on mesh shape
// (NPCs have bodyparts and use a different approach)
if (!ptr.getClass().isNpc() && mHalfExtents.length2() == 0.f)
{
if (shape->mCollisionShape)
{
btTransform transform;
transform.setIdentity();
btVector3 min;
btVector3 max;
shape->mCollisionShape->getAabb(transform, min, max);
mHalfExtents.x() = (max[0] - min[0])/2.f;
mHalfExtents.y() = (max[1] - min[1])/2.f;
mHalfExtents.z() = (max[2] - min[2])/2.f;
mMeshTranslation = osg::Vec3f(0.f, 0.f, mHalfExtents.z());
}
if (mHalfExtents.length2() == 0.f)
Log(Debug::Error) << "Error: Failed to calculate bounding box for actor \"" << ptr.getCellRef().getRefId() << "\".";
}
mShape.reset(new btBoxShape(Misc::Convert::toBullet(mHalfExtents)));
mRotationallyInvariant = (mMeshTranslation.x() == 0.0 && mMeshTranslation.y() == 0.0) && std::fabs(mHalfExtents.x() - mHalfExtents.y()) < 2.2;
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);
updateScale();
if(!mRotationallyInvariant)
updateRotation();
updatePosition();
addCollisionMask(getCollisionMask());
updateCollisionObjectPosition();
}
Actor::~Actor()
{
mTaskScheduler->removeCollisionObject(mCollisionObject.get());
}
void Actor::enableCollisionMode(bool collision)
{
mInternalCollisionMode.store(collision, std::memory_order_release);
}
void Actor::enableCollisionBody(bool collision)
{
if (mExternalCollisionMode != collision)
{
mExternalCollisionMode = collision;
updateCollisionMask();
}
}
void Actor::addCollisionMask(int collisionMask)
{
mTaskScheduler->addCollisionObject(mCollisionObject.get(), CollisionType_Actor, collisionMask);
}
void Actor::updateCollisionMask()
{
mTaskScheduler->setCollisionFilterMask(mCollisionObject.get(), getCollisionMask());
}
int Actor::getCollisionMask() const
{
int collisionMask = CollisionType_World | CollisionType_HeightMap;
if (mExternalCollisionMode)
collisionMask |= CollisionType_Actor | CollisionType_Projectile | CollisionType_Door;
if (mCanWaterWalk)
collisionMask |= CollisionType_Water;
return collisionMask;
}
void Actor::updatePosition()
{
std::scoped_lock lock(mPositionMutex);
const auto worldPosition = mPtr.getRefData().getPosition().asVec3();
mPreviousPosition = worldPosition;
mPosition = worldPosition;
mSimulationPosition = worldPosition;
mPositionOffset = osg::Vec3f();
mStandingOnPtr = nullptr;
mSkipCollisions = true;
}
void Actor::setSimulationPosition(const osg::Vec3f& position)
{
mSimulationPosition = position;
}
osg::Vec3f Actor::getSimulationPosition() const
{
return mSimulationPosition;
}
osg::Vec3f Actor::getScaledMeshTranslation() const
{
return mRotation * osg::componentMultiply(mMeshTranslation, mScale);
}
void Actor::updateCollisionObjectPosition()
{
std::scoped_lock lock(mPositionMutex);
mShape->setLocalScaling(Misc::Convert::toBullet(mScale));
osg::Vec3f scaledTranslation = mRotation * osg::componentMultiply(mMeshTranslation, mScale);
osg::Vec3f newPosition = scaledTranslation + mPosition;
mLocalTransform.setOrigin(Misc::Convert::toBullet(newPosition));
mLocalTransform.setRotation(Misc::Convert::toBullet(mRotation));
mCollisionObject->setWorldTransform(mLocalTransform);
mWorldPositionChanged = false;
}
osg::Vec3f Actor::getCollisionObjectPosition() const
{
std::scoped_lock lock(mPositionMutex);
return Misc::Convert::toOsg(mLocalTransform.getOrigin());
}
bool Actor::setPosition(const osg::Vec3f& position)
{
std::scoped_lock lock(mPositionMutex);
applyOffsetChange();
bool hasChanged = mPosition != position || mWorldPositionChanged;
mPreviousPosition = mPosition;
mPosition = position;
return hasChanged;
}
void Actor::adjustPosition(const osg::Vec3f& offset, bool ignoreCollisions)
{
std::scoped_lock lock(mPositionMutex);
mPositionOffset += offset;
mSkipCollisions = mSkipCollisions || ignoreCollisions;
}
void Actor::applyOffsetChange()
{
if (mPositionOffset.length() == 0)
return;
mPosition += mPositionOffset;
mPreviousPosition += mPositionOffset;
mSimulationPosition += mPositionOffset;
mPositionOffset = osg::Vec3f();
mWorldPositionChanged = true;
}
osg::Vec3f Actor::getPosition() const
{
return mPosition;
}
osg::Vec3f Actor::getPreviousPosition() const
{
return mPreviousPosition;
}
void Actor::updateRotation ()
{
std::scoped_lock lock(mPositionMutex);
mRotation = mPtr.getRefData().getBaseNode()->getAttitude();
}
bool Actor::isRotationallyInvariant() const
{
return mRotationallyInvariant;
}
void Actor::updateScale()
{
std::scoped_lock lock(mPositionMutex);
float scale = mPtr.getCellRef().getScale();
osg::Vec3f scaleVec(scale,scale,scale);
mPtr.getClass().adjustScale(mPtr, scaleVec, false);
mScale = scaleVec;
scaleVec = osg::Vec3f(scale,scale,scale);
mPtr.getClass().adjustScale(mPtr, scaleVec, true);
mRenderingScale = scaleVec;
}
osg::Vec3f Actor::getHalfExtents() const
{
std::scoped_lock lock(mPositionMutex);
return osg::componentMultiply(mHalfExtents, mScale);
}
osg::Vec3f Actor::getOriginalHalfExtents() const
{
return mHalfExtents;
}
osg::Vec3f Actor::getRenderingHalfExtents() const
{
std::scoped_lock lock(mPositionMutex);
return osg::componentMultiply(mHalfExtents, mRenderingScale);
}
void Actor::setInertialForce(const osg::Vec3f &force)
{
mForce = force;
}
void Actor::setOnGround(bool grounded)
{
mOnGround.store(grounded, std::memory_order_release);
}
void Actor::setOnSlope(bool slope)
{
mOnSlope.store(slope, std::memory_order_release);
}
bool Actor::isWalkingOnWater() const
{
return mWalkingOnWater.load(std::memory_order_acquire);
}
void Actor::setWalkingOnWater(bool walkingOnWater)
{
mWalkingOnWater.store(walkingOnWater, std::memory_order_release);
}
void Actor::setCanWaterWalk(bool waterWalk)
{
if (waterWalk != mCanWaterWalk)
{
mCanWaterWalk = waterWalk;
updateCollisionMask();
}
}
MWWorld::Ptr Actor::getStandingOnPtr() const
{
std::scoped_lock lock(mPositionMutex);
return mStandingOnPtr;
}
void Actor::setStandingOnPtr(const MWWorld::Ptr& ptr)
{
std::scoped_lock lock(mPositionMutex);
mStandingOnPtr = ptr;
}
bool Actor::skipCollisions()
{
return std::exchange(mSkipCollisions, false);
}
void Actor::setVelocity(osg::Vec3f velocity)
{
mVelocity = velocity;
}
osg::Vec3f Actor::velocity()
{
return std::exchange(mVelocity, osg::Vec3f());
}
}