1 #include "actor.hpp"
2 
3 #include <BulletCollision/CollisionShapes/btBoxShape.h>
4 #include <BulletCollision/CollisionDispatch/btCollisionWorld.h>
5 
6 #include <components/sceneutil/positionattitudetransform.hpp>
7 #include <components/resource/bulletshape.hpp>
8 #include <components/debug/debuglog.hpp>
9 #include <components/misc/convert.hpp>
10 
11 #include "../mwworld/class.hpp"
12 
13 #include "collisiontype.hpp"
14 #include "mtphysics.hpp"
15 
16 #include <cmath>
17 
18 namespace MWPhysics
19 {
20 
21 
Actor(const MWWorld::Ptr & ptr,const Resource::BulletShape * shape,PhysicsTaskScheduler * scheduler,bool canWaterWalk)22 Actor::Actor(const MWWorld::Ptr& ptr, const Resource::BulletShape* shape, PhysicsTaskScheduler* scheduler, bool canWaterWalk)
23   : mStandingOnPtr(nullptr), mCanWaterWalk(canWaterWalk), mWalkingOnWater(false)
24   , mCollisionObject(nullptr), mMeshTranslation(shape->mCollisionBox.center), mHalfExtents(shape->mCollisionBox.extents)
25   , mVelocity(0,0,0), mStuckFrames(0), mLastStuckPosition{0, 0, 0}
26   , mForce(0.f, 0.f, 0.f), mOnGround(true), mOnSlope(false)
27   , mInternalCollisionMode(true)
28   , mExternalCollisionMode(true)
29   , mTaskScheduler(scheduler)
30 {
31     mPtr = ptr;
32 
33     // We can not create actor without collisions - he will fall through the ground.
34     // In this case we should autogenerate collision box based on mesh shape
35     // (NPCs have bodyparts and use a different approach)
36     if (!ptr.getClass().isNpc() && mHalfExtents.length2() == 0.f)
37     {
38         if (shape->mCollisionShape)
39         {
40             btTransform transform;
41             transform.setIdentity();
42             btVector3 min;
43             btVector3 max;
44 
45             shape->mCollisionShape->getAabb(transform, min, max);
46             mHalfExtents.x() = (max[0] - min[0])/2.f;
47             mHalfExtents.y() = (max[1] - min[1])/2.f;
48             mHalfExtents.z() = (max[2] - min[2])/2.f;
49 
50             mMeshTranslation = osg::Vec3f(0.f, 0.f, mHalfExtents.z());
51         }
52 
53         if (mHalfExtents.length2() == 0.f)
54             Log(Debug::Error) << "Error: Failed to calculate bounding box for actor \"" << ptr.getCellRef().getRefId() << "\".";
55     }
56 
57     mShape.reset(new btBoxShape(Misc::Convert::toBullet(mHalfExtents)));
58     mRotationallyInvariant = (mMeshTranslation.x() == 0.0 && mMeshTranslation.y() == 0.0) && std::fabs(mHalfExtents.x() - mHalfExtents.y()) < 2.2;
59 
60     mConvexShape = static_cast<btConvexShape*>(mShape.get());
61 
62     mCollisionObject = std::make_unique<btCollisionObject>();
63     mCollisionObject->setCollisionFlags(btCollisionObject::CF_KINEMATIC_OBJECT);
64     mCollisionObject->setActivationState(DISABLE_DEACTIVATION);
65     mCollisionObject->setCollisionShape(mShape.get());
66     mCollisionObject->setUserPointer(this);
67 
68     updateScale();
69 
70     if(!mRotationallyInvariant)
71         updateRotation();
72 
73     updatePosition();
74     addCollisionMask(getCollisionMask());
75     updateCollisionObjectPosition();
76 }
77 
~Actor()78 Actor::~Actor()
79 {
80     mTaskScheduler->removeCollisionObject(mCollisionObject.get());
81 }
82 
enableCollisionMode(bool collision)83 void Actor::enableCollisionMode(bool collision)
84 {
85     mInternalCollisionMode.store(collision, std::memory_order_release);
86 }
87 
enableCollisionBody(bool collision)88 void Actor::enableCollisionBody(bool collision)
89 {
90     if (mExternalCollisionMode != collision)
91     {
92         mExternalCollisionMode = collision;
93         updateCollisionMask();
94     }
95 }
96 
addCollisionMask(int collisionMask)97 void Actor::addCollisionMask(int collisionMask)
98 {
99     mTaskScheduler->addCollisionObject(mCollisionObject.get(), CollisionType_Actor, collisionMask);
100 }
101 
updateCollisionMask()102 void Actor::updateCollisionMask()
103 {
104     mTaskScheduler->setCollisionFilterMask(mCollisionObject.get(), getCollisionMask());
105 }
106 
getCollisionMask() const107 int Actor::getCollisionMask() const
108 {
109     int collisionMask = CollisionType_World | CollisionType_HeightMap;
110     if (mExternalCollisionMode)
111         collisionMask |= CollisionType_Actor | CollisionType_Projectile | CollisionType_Door;
112     if (mCanWaterWalk)
113         collisionMask |= CollisionType_Water;
114     return collisionMask;
115 }
116 
updatePosition()117 void Actor::updatePosition()
118 {
119     std::scoped_lock lock(mPositionMutex);
120     const auto worldPosition = mPtr.getRefData().getPosition().asVec3();
121     mPreviousPosition = worldPosition;
122     mPosition = worldPosition;
123     mSimulationPosition = worldPosition;
124     mPositionOffset = osg::Vec3f();
125     mStandingOnPtr = nullptr;
126     mSkipCollisions = true;
127     mSkipSimulation = true;
128 }
129 
setSimulationPosition(const osg::Vec3f & position)130 void Actor::setSimulationPosition(const osg::Vec3f& position)
131 {
132     if (!std::exchange(mSkipSimulation, false))
133         mSimulationPosition = position;
134 }
135 
getSimulationPosition() const136 osg::Vec3f Actor::getSimulationPosition() const
137 {
138     return mSimulationPosition;
139 }
140 
getScaledMeshTranslation() const141 osg::Vec3f Actor::getScaledMeshTranslation() const
142 {
143     return mRotation * osg::componentMultiply(mMeshTranslation, mScale);
144 }
145 
updateCollisionObjectPosition()146 void Actor::updateCollisionObjectPosition()
147 {
148     std::scoped_lock lock(mPositionMutex);
149     mShape->setLocalScaling(Misc::Convert::toBullet(mScale));
150     osg::Vec3f newPosition = getScaledMeshTranslation() + mPosition;
151 
152     auto& trans = mCollisionObject->getWorldTransform();
153     trans.setOrigin(Misc::Convert::toBullet(newPosition));
154     trans.setRotation(Misc::Convert::toBullet(mRotation));
155     mCollisionObject->setWorldTransform(trans);
156 
157     mWorldPositionChanged = false;
158 }
159 
getCollisionObjectPosition() const160 osg::Vec3f Actor::getCollisionObjectPosition() const
161 {
162     std::scoped_lock lock(mPositionMutex);
163     return getScaledMeshTranslation() + mPosition;
164 }
165 
setPosition(const osg::Vec3f & position)166 bool Actor::setPosition(const osg::Vec3f& position)
167 {
168     std::scoped_lock lock(mPositionMutex);
169     applyOffsetChange();
170     bool hasChanged = mPosition != position || mWorldPositionChanged;
171     mPreviousPosition = mPosition;
172     mPosition = position;
173     return hasChanged;
174 }
175 
adjustPosition(const osg::Vec3f & offset,bool ignoreCollisions)176 void Actor::adjustPosition(const osg::Vec3f& offset, bool ignoreCollisions)
177 {
178     std::scoped_lock lock(mPositionMutex);
179     mPositionOffset += offset;
180     mSkipCollisions = mSkipCollisions || ignoreCollisions;
181 }
182 
applyOffsetChange()183 void Actor::applyOffsetChange()
184 {
185     if (mPositionOffset.length() == 0)
186         return;
187     mPosition += mPositionOffset;
188     mPreviousPosition += mPositionOffset;
189     mSimulationPosition += mPositionOffset;
190     mPositionOffset = osg::Vec3f();
191     mWorldPositionChanged = true;
192 }
193 
getPosition() const194 osg::Vec3f Actor::getPosition() const
195 {
196     return mPosition;
197 }
198 
getPreviousPosition() const199 osg::Vec3f Actor::getPreviousPosition() const
200 {
201     return mPreviousPosition;
202 }
203 
updateRotation()204 void Actor::updateRotation ()
205 {
206     std::scoped_lock lock(mPositionMutex);
207     mRotation = mPtr.getRefData().getBaseNode()->getAttitude();
208 }
209 
isRotationallyInvariant() const210 bool Actor::isRotationallyInvariant() const
211 {
212     return mRotationallyInvariant;
213 }
214 
updateScale()215 void Actor::updateScale()
216 {
217     std::scoped_lock lock(mPositionMutex);
218     float scale = mPtr.getCellRef().getScale();
219     osg::Vec3f scaleVec(scale,scale,scale);
220 
221     mPtr.getClass().adjustScale(mPtr, scaleVec, false);
222     mScale = scaleVec;
223 
224     scaleVec = osg::Vec3f(scale,scale,scale);
225     mPtr.getClass().adjustScale(mPtr, scaleVec, true);
226     mRenderingScale = scaleVec;
227 }
228 
getHalfExtents() const229 osg::Vec3f Actor::getHalfExtents() const
230 {
231     std::scoped_lock lock(mPositionMutex);
232     return osg::componentMultiply(mHalfExtents, mScale);
233 }
234 
getOriginalHalfExtents() const235 osg::Vec3f Actor::getOriginalHalfExtents() const
236 {
237     return mHalfExtents;
238 }
239 
getRenderingHalfExtents() const240 osg::Vec3f Actor::getRenderingHalfExtents() const
241 {
242     std::scoped_lock lock(mPositionMutex);
243     return osg::componentMultiply(mHalfExtents, mRenderingScale);
244 }
245 
setInertialForce(const osg::Vec3f & force)246 void Actor::setInertialForce(const osg::Vec3f &force)
247 {
248     mForce = force;
249 }
250 
setOnGround(bool grounded)251 void Actor::setOnGround(bool grounded)
252 {
253     mOnGround.store(grounded, std::memory_order_release);
254 }
255 
setOnSlope(bool slope)256 void Actor::setOnSlope(bool slope)
257 {
258     mOnSlope.store(slope, std::memory_order_release);
259 }
260 
isWalkingOnWater() const261 bool Actor::isWalkingOnWater() const
262 {
263     return mWalkingOnWater.load(std::memory_order_acquire);
264 }
265 
setWalkingOnWater(bool walkingOnWater)266 void Actor::setWalkingOnWater(bool walkingOnWater)
267 {
268     mWalkingOnWater.store(walkingOnWater, std::memory_order_release);
269 }
270 
setCanWaterWalk(bool waterWalk)271 void Actor::setCanWaterWalk(bool waterWalk)
272 {
273     if (waterWalk != mCanWaterWalk)
274     {
275         mCanWaterWalk = waterWalk;
276         updateCollisionMask();
277     }
278 }
279 
getStandingOnPtr() const280 MWWorld::Ptr Actor::getStandingOnPtr() const
281 {
282     std::scoped_lock lock(mPositionMutex);
283     return mStandingOnPtr;
284 }
285 
setStandingOnPtr(const MWWorld::Ptr & ptr)286 void Actor::setStandingOnPtr(const MWWorld::Ptr& ptr)
287 {
288     std::scoped_lock lock(mPositionMutex);
289     mStandingOnPtr = ptr;
290 }
291 
skipCollisions()292 bool Actor::skipCollisions()
293 {
294     return std::exchange(mSkipCollisions, false);
295 }
296 
setVelocity(osg::Vec3f velocity)297 void Actor::setVelocity(osg::Vec3f velocity)
298 {
299     mVelocity = velocity;
300 }
301 
velocity()302 osg::Vec3f Actor::velocity()
303 {
304     return std::exchange(mVelocity, osg::Vec3f());
305 }
306 
307 }
308