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