mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-01-16 00:49:54 +00:00
b13afd758c
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
303 lines
8 KiB
C++
303 lines
8 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());
|
|
}
|
|
|
|
}
|