1 //
2 // Copyright (c) 2008-2017 the Urho3D project.
3 //
4 // Permission is hereby granted, free of charge, to any person obtaining a copy
5 // of this software and associated documentation files (the "Software"), to deal
6 // in the Software without restriction, including without limitation the rights
7 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 // copies of the Software, and to permit persons to whom the Software is
9 // furnished to do so, subject to the following conditions:
10 //
11 // The above copyright notice and this permission notice shall be included in
12 // all copies or substantial portions of the Software.
13 //
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 // THE SOFTWARE.
21 //
22
23 #include "../Precompiled.h"
24
25 #include "../Core/Context.h"
26 #include "../Core/Profiler.h"
27 #include "../IO/Log.h"
28 #include "../IO/MemoryBuffer.h"
29 #include "../Physics/CollisionShape.h"
30 #include "../Physics/Constraint.h"
31 #include "../Physics/PhysicsUtils.h"
32 #include "../Physics/PhysicsWorld.h"
33 #include "../Physics/RigidBody.h"
34 #include "../Resource/ResourceCache.h"
35 #include "../Resource/ResourceEvents.h"
36 #include "../Scene/Scene.h"
37 #include "../Scene/SceneEvents.h"
38 #include "../Scene/SmoothedTransform.h"
39
40 #include <Bullet/BulletDynamics/Dynamics/btDiscreteDynamicsWorld.h>
41 #include <Bullet/BulletDynamics/Dynamics/btRigidBody.h>
42 #include <Bullet/BulletCollision/CollisionShapes/btCompoundShape.h>
43
44 namespace Urho3D
45 {
46
47 static const float DEFAULT_MASS = 0.0f;
48 static const float DEFAULT_FRICTION = 0.5f;
49 static const float DEFAULT_RESTITUTION = 0.0f;
50 static const float DEFAULT_ROLLING_FRICTION = 0.0f;
51 static const unsigned DEFAULT_COLLISION_LAYER = 0x1;
52 static const unsigned DEFAULT_COLLISION_MASK = M_MAX_UNSIGNED;
53
54 static const char* collisionEventModeNames[] =
55 {
56 "Never",
57 "When Active",
58 "Always",
59 0
60 };
61
62 extern const char* PHYSICS_CATEGORY;
63
RigidBody(Context * context)64 RigidBody::RigidBody(Context* context) :
65 Component(context),
66 gravityOverride_(Vector3::ZERO),
67 centerOfMass_(Vector3::ZERO),
68 mass_(DEFAULT_MASS),
69 collisionLayer_(DEFAULT_COLLISION_LAYER),
70 collisionMask_(DEFAULT_COLLISION_MASK),
71 collisionEventMode_(COLLISION_ACTIVE),
72 lastPosition_(Vector3::ZERO),
73 lastRotation_(Quaternion::IDENTITY),
74 kinematic_(false),
75 trigger_(false),
76 useGravity_(true),
77 readdBody_(false),
78 inWorld_(false),
79 enableMassUpdate_(true),
80 hasSimulated_(false)
81 {
82 compoundShape_ = new btCompoundShape();
83 shiftedCompoundShape_ = new btCompoundShape();
84 }
85
~RigidBody()86 RigidBody::~RigidBody()
87 {
88 ReleaseBody();
89
90 if (physicsWorld_)
91 physicsWorld_->RemoveRigidBody(this);
92 }
93
RegisterObject(Context * context)94 void RigidBody::RegisterObject(Context* context)
95 {
96 context->RegisterFactory<RigidBody>(PHYSICS_CATEGORY);
97
98 URHO3D_ACCESSOR_ATTRIBUTE("Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
99 URHO3D_MIXED_ACCESSOR_ATTRIBUTE("Physics Rotation", GetRotation, SetRotation, Quaternion, Quaternion::IDENTITY, AM_FILE | AM_NOEDIT);
100 URHO3D_MIXED_ACCESSOR_ATTRIBUTE("Physics Position", GetPosition, SetPosition, Vector3, Vector3::ZERO, AM_FILE | AM_NOEDIT);
101 URHO3D_ATTRIBUTE("Mass", float, mass_, DEFAULT_MASS, AM_DEFAULT);
102 URHO3D_ACCESSOR_ATTRIBUTE("Friction", GetFriction, SetFriction, float, DEFAULT_FRICTION, AM_DEFAULT);
103 URHO3D_MIXED_ACCESSOR_ATTRIBUTE("Anisotropic Friction", GetAnisotropicFriction, SetAnisotropicFriction, Vector3, Vector3::ONE,
104 AM_DEFAULT);
105 URHO3D_ACCESSOR_ATTRIBUTE("Rolling Friction", GetRollingFriction, SetRollingFriction, float, DEFAULT_ROLLING_FRICTION, AM_DEFAULT);
106 URHO3D_ACCESSOR_ATTRIBUTE("Restitution", GetRestitution, SetRestitution, float, DEFAULT_RESTITUTION, AM_DEFAULT);
107 URHO3D_MIXED_ACCESSOR_ATTRIBUTE("Linear Velocity", GetLinearVelocity, SetLinearVelocity, Vector3, Vector3::ZERO,
108 AM_DEFAULT | AM_LATESTDATA);
109 URHO3D_MIXED_ACCESSOR_ATTRIBUTE("Angular Velocity", GetAngularVelocity, SetAngularVelocity, Vector3, Vector3::ZERO, AM_FILE);
110 URHO3D_MIXED_ACCESSOR_ATTRIBUTE("Linear Factor", GetLinearFactor, SetLinearFactor, Vector3, Vector3::ONE, AM_DEFAULT);
111 URHO3D_MIXED_ACCESSOR_ATTRIBUTE("Angular Factor", GetAngularFactor, SetAngularFactor, Vector3, Vector3::ONE, AM_DEFAULT);
112 URHO3D_ACCESSOR_ATTRIBUTE("Linear Damping", GetLinearDamping, SetLinearDamping, float, 0.0f, AM_DEFAULT);
113 URHO3D_ACCESSOR_ATTRIBUTE("Angular Damping", GetAngularDamping, SetAngularDamping, float, 0.0f, AM_DEFAULT);
114 URHO3D_ACCESSOR_ATTRIBUTE("Linear Rest Threshold", GetLinearRestThreshold, SetLinearRestThreshold, float, 0.8f, AM_DEFAULT);
115 URHO3D_ACCESSOR_ATTRIBUTE("Angular Rest Threshold", GetAngularRestThreshold, SetAngularRestThreshold, float, 1.0f, AM_DEFAULT);
116 URHO3D_ATTRIBUTE("Collision Layer", int, collisionLayer_, DEFAULT_COLLISION_LAYER, AM_DEFAULT);
117 URHO3D_ATTRIBUTE("Collision Mask", int, collisionMask_, DEFAULT_COLLISION_MASK, AM_DEFAULT);
118 URHO3D_ACCESSOR_ATTRIBUTE("Contact Threshold", GetContactProcessingThreshold, SetContactProcessingThreshold, float, BT_LARGE_FLOAT,
119 AM_DEFAULT);
120 URHO3D_ACCESSOR_ATTRIBUTE("CCD Radius", GetCcdRadius, SetCcdRadius, float, 0.0f, AM_DEFAULT);
121 URHO3D_ACCESSOR_ATTRIBUTE("CCD Motion Threshold", GetCcdMotionThreshold, SetCcdMotionThreshold, float, 0.0f, AM_DEFAULT);
122 URHO3D_ACCESSOR_ATTRIBUTE("Network Angular Velocity", GetNetAngularVelocityAttr, SetNetAngularVelocityAttr, PODVector<unsigned char>,
123 Variant::emptyBuffer, AM_NET | AM_LATESTDATA | AM_NOEDIT);
124 URHO3D_ENUM_ATTRIBUTE("Collision Event Mode", collisionEventMode_, collisionEventModeNames, COLLISION_ACTIVE, AM_DEFAULT);
125 URHO3D_ACCESSOR_ATTRIBUTE("Use Gravity", GetUseGravity, SetUseGravity, bool, true, AM_DEFAULT);
126 URHO3D_ATTRIBUTE("Is Kinematic", bool, kinematic_, false, AM_DEFAULT);
127 URHO3D_ATTRIBUTE("Is Trigger", bool, trigger_, false, AM_DEFAULT);
128 URHO3D_ACCESSOR_ATTRIBUTE("Gravity Override", GetGravityOverride, SetGravityOverride, Vector3, Vector3::ZERO, AM_DEFAULT);
129 }
130
OnSetAttribute(const AttributeInfo & attr,const Variant & src)131 void RigidBody::OnSetAttribute(const AttributeInfo& attr, const Variant& src)
132 {
133 Serializable::OnSetAttribute(attr, src);
134
135 // Change of any non-accessor attribute requires the rigid body to be re-added to the physics world
136 if (!attr.accessor_)
137 readdBody_ = true;
138 }
139
ApplyAttributes()140 void RigidBody::ApplyAttributes()
141 {
142 if (readdBody_)
143 AddBodyToWorld();
144 }
145
OnSetEnabled()146 void RigidBody::OnSetEnabled()
147 {
148 bool enabled = IsEnabledEffective();
149
150 if (enabled && !inWorld_)
151 AddBodyToWorld();
152 else if (!enabled && inWorld_)
153 RemoveBodyFromWorld();
154 }
155
getWorldTransform(btTransform & worldTrans) const156 void RigidBody::getWorldTransform(btTransform& worldTrans) const
157 {
158 // We may be in a pathological state where a RigidBody exists without a scene node when this callback is fired,
159 // so check to be sure
160 if (node_)
161 {
162 lastPosition_ = node_->GetWorldPosition();
163 lastRotation_ = node_->GetWorldRotation();
164 worldTrans.setOrigin(ToBtVector3(lastPosition_ + lastRotation_ * centerOfMass_));
165 worldTrans.setRotation(ToBtQuaternion(lastRotation_));
166 }
167
168 hasSimulated_ = true;
169 }
170
setWorldTransform(const btTransform & worldTrans)171 void RigidBody::setWorldTransform(const btTransform& worldTrans)
172 {
173 Quaternion newWorldRotation = ToQuaternion(worldTrans.getRotation());
174 Vector3 newWorldPosition = ToVector3(worldTrans.getOrigin()) - newWorldRotation * centerOfMass_;
175 RigidBody* parentRigidBody = 0;
176
177 // It is possible that the RigidBody component has been kept alive via a shared pointer,
178 // while its scene node has already been destroyed
179 if (node_)
180 {
181 // If the rigid body is parented to another rigid body, can not set the transform immediately.
182 // In that case store it to PhysicsWorld for delayed assignment
183 Node* parent = node_->GetParent();
184 if (parent != GetScene() && parent)
185 parentRigidBody = parent->GetComponent<RigidBody>();
186
187 if (!parentRigidBody)
188 ApplyWorldTransform(newWorldPosition, newWorldRotation);
189 else
190 {
191 DelayedWorldTransform delayed;
192 delayed.rigidBody_ = this;
193 delayed.parentRigidBody_ = parentRigidBody;
194 delayed.worldPosition_ = newWorldPosition;
195 delayed.worldRotation_ = newWorldRotation;
196 physicsWorld_->AddDelayedWorldTransform(delayed);
197 }
198
199 MarkNetworkUpdate();
200 }
201
202 hasSimulated_ = true;
203 }
204
DrawDebugGeometry(DebugRenderer * debug,bool depthTest)205 void RigidBody::DrawDebugGeometry(DebugRenderer* debug, bool depthTest)
206 {
207 if (debug && physicsWorld_ && body_ && IsEnabledEffective())
208 {
209 physicsWorld_->SetDebugRenderer(debug);
210 physicsWorld_->SetDebugDepthTest(depthTest);
211
212 btDiscreteDynamicsWorld* world = physicsWorld_->GetWorld();
213 world->debugDrawObject(body_->getWorldTransform(), shiftedCompoundShape_.Get(), IsActive() ? btVector3(1.0f, 1.0f, 1.0f) :
214 btVector3(0.0f, 1.0f, 0.0f));
215
216 physicsWorld_->SetDebugRenderer(0);
217 }
218 }
219
SetMass(float mass)220 void RigidBody::SetMass(float mass)
221 {
222 mass = Max(mass, 0.0f);
223
224 if (mass != mass_)
225 {
226 mass_ = mass;
227 AddBodyToWorld();
228 MarkNetworkUpdate();
229 }
230 }
231
SetPosition(const Vector3 & position)232 void RigidBody::SetPosition(const Vector3& position)
233 {
234 if (body_)
235 {
236 btTransform& worldTrans = body_->getWorldTransform();
237 worldTrans.setOrigin(ToBtVector3(position + ToQuaternion(worldTrans.getRotation()) * centerOfMass_));
238
239 // When forcing the physics position, set also interpolated position so that there is no jitter
240 // When not inside the simulation loop, this may lead to erratic movement of parented rigidbodies
241 // so skip in that case. Exception made before first simulation tick so that interpolation position
242 // of e.g. instantiated prefabs will be correct from the start
243 if (!hasSimulated_ || physicsWorld_->IsSimulating())
244 {
245 btTransform interpTrans = body_->getInterpolationWorldTransform();
246 interpTrans.setOrigin(worldTrans.getOrigin());
247 body_->setInterpolationWorldTransform(interpTrans);
248 }
249
250 Activate();
251 MarkNetworkUpdate();
252 }
253 }
254
SetRotation(const Quaternion & rotation)255 void RigidBody::SetRotation(const Quaternion& rotation)
256 {
257 if (body_)
258 {
259 Vector3 oldPosition = GetPosition();
260 btTransform& worldTrans = body_->getWorldTransform();
261 worldTrans.setRotation(ToBtQuaternion(rotation));
262 if (!centerOfMass_.Equals(Vector3::ZERO))
263 worldTrans.setOrigin(ToBtVector3(oldPosition + rotation * centerOfMass_));
264
265 if (!hasSimulated_ || physicsWorld_->IsSimulating())
266 {
267 btTransform interpTrans = body_->getInterpolationWorldTransform();
268 interpTrans.setRotation(worldTrans.getRotation());
269 if (!centerOfMass_.Equals(Vector3::ZERO))
270 interpTrans.setOrigin(worldTrans.getOrigin());
271 body_->setInterpolationWorldTransform(interpTrans);
272 }
273
274 body_->updateInertiaTensor();
275
276 Activate();
277 MarkNetworkUpdate();
278 }
279 }
280
SetTransform(const Vector3 & position,const Quaternion & rotation)281 void RigidBody::SetTransform(const Vector3& position, const Quaternion& rotation)
282 {
283 if (body_)
284 {
285 btTransform& worldTrans = body_->getWorldTransform();
286 worldTrans.setRotation(ToBtQuaternion(rotation));
287 worldTrans.setOrigin(ToBtVector3(position + rotation * centerOfMass_));
288
289 if (!hasSimulated_ || physicsWorld_->IsSimulating())
290 {
291 btTransform interpTrans = body_->getInterpolationWorldTransform();
292 interpTrans.setOrigin(worldTrans.getOrigin());
293 interpTrans.setRotation(worldTrans.getRotation());
294 body_->setInterpolationWorldTransform(interpTrans);
295 }
296
297 body_->updateInertiaTensor();
298
299 Activate();
300 MarkNetworkUpdate();
301 }
302 }
303
SetLinearVelocity(const Vector3 & velocity)304 void RigidBody::SetLinearVelocity(const Vector3& velocity)
305 {
306 if (body_)
307 {
308 body_->setLinearVelocity(ToBtVector3(velocity));
309 if (velocity != Vector3::ZERO)
310 Activate();
311 MarkNetworkUpdate();
312 }
313 }
314
SetLinearFactor(const Vector3 & factor)315 void RigidBody::SetLinearFactor(const Vector3& factor)
316 {
317 if (body_)
318 {
319 body_->setLinearFactor(ToBtVector3(factor));
320 MarkNetworkUpdate();
321 }
322 }
323
SetLinearRestThreshold(float threshold)324 void RigidBody::SetLinearRestThreshold(float threshold)
325 {
326 if (body_)
327 {
328 body_->setSleepingThresholds(threshold, body_->getAngularSleepingThreshold());
329 MarkNetworkUpdate();
330 }
331 }
332
SetLinearDamping(float damping)333 void RigidBody::SetLinearDamping(float damping)
334 {
335 if (body_)
336 {
337 body_->setDamping(damping, body_->getAngularDamping());
338 MarkNetworkUpdate();
339 }
340 }
341
SetAngularVelocity(const Vector3 & velocity)342 void RigidBody::SetAngularVelocity(const Vector3& velocity)
343 {
344 if (body_)
345 {
346 body_->setAngularVelocity(ToBtVector3(velocity));
347 if (velocity != Vector3::ZERO)
348 Activate();
349 MarkNetworkUpdate();
350 }
351 }
352
SetAngularFactor(const Vector3 & factor)353 void RigidBody::SetAngularFactor(const Vector3& factor)
354 {
355 if (body_)
356 {
357 body_->setAngularFactor(ToBtVector3(factor));
358 MarkNetworkUpdate();
359 }
360 }
361
SetAngularRestThreshold(float threshold)362 void RigidBody::SetAngularRestThreshold(float threshold)
363 {
364 if (body_)
365 {
366 body_->setSleepingThresholds(body_->getLinearSleepingThreshold(), threshold);
367 MarkNetworkUpdate();
368 }
369 }
370
SetAngularDamping(float damping)371 void RigidBody::SetAngularDamping(float damping)
372 {
373 if (body_)
374 {
375 body_->setDamping(body_->getLinearDamping(), damping);
376 MarkNetworkUpdate();
377 }
378 }
379
SetFriction(float friction)380 void RigidBody::SetFriction(float friction)
381 {
382 if (body_)
383 {
384 body_->setFriction(friction);
385 MarkNetworkUpdate();
386 }
387 }
388
SetAnisotropicFriction(const Vector3 & friction)389 void RigidBody::SetAnisotropicFriction(const Vector3& friction)
390 {
391 if (body_)
392 {
393 body_->setAnisotropicFriction(ToBtVector3(friction));
394 MarkNetworkUpdate();
395 }
396 }
397
SetRollingFriction(float friction)398 void RigidBody::SetRollingFriction(float friction)
399 {
400 if (body_)
401 {
402 body_->setRollingFriction(friction);
403 MarkNetworkUpdate();
404 }
405 }
406
SetRestitution(float restitution)407 void RigidBody::SetRestitution(float restitution)
408 {
409 if (body_)
410 {
411 body_->setRestitution(restitution);
412 MarkNetworkUpdate();
413 }
414 }
415
SetContactProcessingThreshold(float threshold)416 void RigidBody::SetContactProcessingThreshold(float threshold)
417 {
418 if (body_)
419 {
420 body_->setContactProcessingThreshold(threshold);
421 MarkNetworkUpdate();
422 }
423 }
424
SetCcdRadius(float radius)425 void RigidBody::SetCcdRadius(float radius)
426 {
427 radius = Max(radius, 0.0f);
428 if (body_)
429 {
430 body_->setCcdSweptSphereRadius(radius);
431 MarkNetworkUpdate();
432 }
433 }
434
SetCcdMotionThreshold(float threshold)435 void RigidBody::SetCcdMotionThreshold(float threshold)
436 {
437 threshold = Max(threshold, 0.0f);
438 if (body_)
439 {
440 body_->setCcdMotionThreshold(threshold);
441 MarkNetworkUpdate();
442 }
443 }
444
SetUseGravity(bool enable)445 void RigidBody::SetUseGravity(bool enable)
446 {
447 if (enable != useGravity_)
448 {
449 useGravity_ = enable;
450 UpdateGravity();
451 MarkNetworkUpdate();
452 }
453 }
454
SetGravityOverride(const Vector3 & gravity)455 void RigidBody::SetGravityOverride(const Vector3& gravity)
456 {
457 if (gravity != gravityOverride_)
458 {
459 gravityOverride_ = gravity;
460 UpdateGravity();
461 MarkNetworkUpdate();
462 }
463 }
464
SetKinematic(bool enable)465 void RigidBody::SetKinematic(bool enable)
466 {
467 if (enable != kinematic_)
468 {
469 kinematic_ = enable;
470 AddBodyToWorld();
471 MarkNetworkUpdate();
472 }
473 }
474
SetTrigger(bool enable)475 void RigidBody::SetTrigger(bool enable)
476 {
477 if (enable != trigger_)
478 {
479 trigger_ = enable;
480 AddBodyToWorld();
481 MarkNetworkUpdate();
482 }
483 }
484
SetCollisionLayer(unsigned layer)485 void RigidBody::SetCollisionLayer(unsigned layer)
486 {
487 if (layer != collisionLayer_)
488 {
489 collisionLayer_ = layer;
490 AddBodyToWorld();
491 MarkNetworkUpdate();
492 }
493 }
494
SetCollisionMask(unsigned mask)495 void RigidBody::SetCollisionMask(unsigned mask)
496 {
497 if (mask != collisionMask_)
498 {
499 collisionMask_ = mask;
500 AddBodyToWorld();
501 MarkNetworkUpdate();
502 }
503 }
504
SetCollisionLayerAndMask(unsigned layer,unsigned mask)505 void RigidBody::SetCollisionLayerAndMask(unsigned layer, unsigned mask)
506 {
507 if (layer != collisionLayer_ || mask != collisionMask_)
508 {
509 collisionLayer_ = layer;
510 collisionMask_ = mask;
511 AddBodyToWorld();
512 MarkNetworkUpdate();
513 }
514 }
515
SetCollisionEventMode(CollisionEventMode mode)516 void RigidBody::SetCollisionEventMode(CollisionEventMode mode)
517 {
518 collisionEventMode_ = mode;
519 MarkNetworkUpdate();
520 }
521
ApplyForce(const Vector3 & force)522 void RigidBody::ApplyForce(const Vector3& force)
523 {
524 if (body_ && force != Vector3::ZERO)
525 {
526 Activate();
527 body_->applyCentralForce(ToBtVector3(force));
528 }
529 }
530
ApplyForce(const Vector3 & force,const Vector3 & position)531 void RigidBody::ApplyForce(const Vector3& force, const Vector3& position)
532 {
533 if (body_ && force != Vector3::ZERO)
534 {
535 Activate();
536 body_->applyForce(ToBtVector3(force), ToBtVector3(position - centerOfMass_));
537 }
538 }
539
ApplyTorque(const Vector3 & torque)540 void RigidBody::ApplyTorque(const Vector3& torque)
541 {
542 if (body_ && torque != Vector3::ZERO)
543 {
544 Activate();
545 body_->applyTorque(ToBtVector3(torque));
546 }
547 }
548
ApplyImpulse(const Vector3 & impulse)549 void RigidBody::ApplyImpulse(const Vector3& impulse)
550 {
551 if (body_ && impulse != Vector3::ZERO)
552 {
553 Activate();
554 body_->applyCentralImpulse(ToBtVector3(impulse));
555 }
556 }
557
ApplyImpulse(const Vector3 & impulse,const Vector3 & position)558 void RigidBody::ApplyImpulse(const Vector3& impulse, const Vector3& position)
559 {
560 if (body_ && impulse != Vector3::ZERO)
561 {
562 Activate();
563 body_->applyImpulse(ToBtVector3(impulse), ToBtVector3(position - centerOfMass_));
564 }
565 }
566
ApplyTorqueImpulse(const Vector3 & torque)567 void RigidBody::ApplyTorqueImpulse(const Vector3& torque)
568 {
569 if (body_ && torque != Vector3::ZERO)
570 {
571 Activate();
572 body_->applyTorqueImpulse(ToBtVector3(torque));
573 }
574 }
575
ResetForces()576 void RigidBody::ResetForces()
577 {
578 if (body_)
579 body_->clearForces();
580 }
581
Activate()582 void RigidBody::Activate()
583 {
584 if (body_ && mass_ > 0.0f)
585 body_->activate(true);
586 }
587
ReAddBodyToWorld()588 void RigidBody::ReAddBodyToWorld()
589 {
590 if (body_ && inWorld_)
591 AddBodyToWorld();
592 }
593
DisableMassUpdate()594 void RigidBody::DisableMassUpdate()
595 {
596 enableMassUpdate_ = false;
597 }
598
EnableMassUpdate()599 void RigidBody::EnableMassUpdate()
600 {
601 if (!enableMassUpdate_)
602 {
603 enableMassUpdate_ = true;
604 UpdateMass();
605 }
606 }
607
GetPosition() const608 Vector3 RigidBody::GetPosition() const
609 {
610 if (body_)
611 {
612 const btTransform& transform = body_->getWorldTransform();
613 return ToVector3(transform.getOrigin()) - ToQuaternion(transform.getRotation()) * centerOfMass_;
614 }
615 else
616 return Vector3::ZERO;
617 }
618
GetRotation() const619 Quaternion RigidBody::GetRotation() const
620 {
621 return body_ ? ToQuaternion(body_->getWorldTransform().getRotation()) : Quaternion::IDENTITY;
622 }
623
GetLinearVelocity() const624 Vector3 RigidBody::GetLinearVelocity() const
625 {
626 return body_ ? ToVector3(body_->getLinearVelocity()) : Vector3::ZERO;
627 }
628
GetLinearFactor() const629 Vector3 RigidBody::GetLinearFactor() const
630 {
631 return body_ ? ToVector3(body_->getLinearFactor()) : Vector3::ZERO;
632 }
633
GetVelocityAtPoint(const Vector3 & position) const634 Vector3 RigidBody::GetVelocityAtPoint(const Vector3& position) const
635 {
636 return body_ ? ToVector3(body_->getVelocityInLocalPoint(ToBtVector3(position - centerOfMass_))) : Vector3::ZERO;
637 }
638
GetLinearRestThreshold() const639 float RigidBody::GetLinearRestThreshold() const
640 {
641 return body_ ? body_->getLinearSleepingThreshold() : 0.0f;
642 }
643
GetLinearDamping() const644 float RigidBody::GetLinearDamping() const
645 {
646 return body_ ? body_->getLinearDamping() : 0.0f;
647 }
648
GetAngularVelocity() const649 Vector3 RigidBody::GetAngularVelocity() const
650 {
651 return body_ ? ToVector3(body_->getAngularVelocity()) : Vector3::ZERO;
652 }
653
GetAngularFactor() const654 Vector3 RigidBody::GetAngularFactor() const
655 {
656 return body_ ? ToVector3(body_->getAngularFactor()) : Vector3::ZERO;
657 }
658
GetAngularRestThreshold() const659 float RigidBody::GetAngularRestThreshold() const
660 {
661 return body_ ? body_->getAngularSleepingThreshold() : 0.0f;
662 }
663
GetAngularDamping() const664 float RigidBody::GetAngularDamping() const
665 {
666 return body_ ? body_->getAngularDamping() : 0.0f;
667 }
668
GetFriction() const669 float RigidBody::GetFriction() const
670 {
671 return body_ ? body_->getFriction() : 0.0f;
672 }
673
GetAnisotropicFriction() const674 Vector3 RigidBody::GetAnisotropicFriction() const
675 {
676 return body_ ? ToVector3(body_->getAnisotropicFriction()) : Vector3::ZERO;
677 }
678
GetRollingFriction() const679 float RigidBody::GetRollingFriction() const
680 {
681 return body_ ? body_->getRollingFriction() : 0.0f;
682 }
683
GetRestitution() const684 float RigidBody::GetRestitution() const
685 {
686 return body_ ? body_->getRestitution() : 0.0f;
687 }
688
GetContactProcessingThreshold() const689 float RigidBody::GetContactProcessingThreshold() const
690 {
691 return body_ ? body_->getContactProcessingThreshold() : 0.0f;
692 }
693
GetCcdRadius() const694 float RigidBody::GetCcdRadius() const
695 {
696 return body_ ? body_->getCcdSweptSphereRadius() : 0.0f;
697 }
698
GetCcdMotionThreshold() const699 float RigidBody::GetCcdMotionThreshold() const
700 {
701 return body_ ? body_->getCcdMotionThreshold() : 0.0f;
702 }
703
IsActive() const704 bool RigidBody::IsActive() const
705 {
706 return body_ ? body_->isActive() : false;
707 }
708
GetCollidingBodies(PODVector<RigidBody * > & result) const709 void RigidBody::GetCollidingBodies(PODVector<RigidBody*>& result) const
710 {
711 if (physicsWorld_)
712 physicsWorld_->GetCollidingBodies(result, this);
713 else
714 result.Clear();
715 }
716
ApplyWorldTransform(const Vector3 & newWorldPosition,const Quaternion & newWorldRotation)717 void RigidBody::ApplyWorldTransform(const Vector3& newWorldPosition, const Quaternion& newWorldRotation)
718 {
719 // In case of holding an extra reference to the RigidBody, this could be called in a situation
720 // where node is already null
721 if (!node_ || !physicsWorld_)
722 return;
723
724 physicsWorld_->SetApplyingTransforms(true);
725
726 // Apply transform to the SmoothedTransform component instead of node transform if available
727 if (smoothedTransform_)
728 {
729 smoothedTransform_->SetTargetWorldPosition(newWorldPosition);
730 smoothedTransform_->SetTargetWorldRotation(newWorldRotation);
731 lastPosition_ = newWorldPosition;
732 lastRotation_ = newWorldRotation;
733 }
734 else
735 {
736 node_->SetWorldPosition(newWorldPosition);
737 node_->SetWorldRotation(newWorldRotation);
738 lastPosition_ = node_->GetWorldPosition();
739 lastRotation_ = node_->GetWorldRotation();
740 }
741
742 physicsWorld_->SetApplyingTransforms(false);
743 }
744
UpdateMass()745 void RigidBody::UpdateMass()
746 {
747 if (!body_ || !enableMassUpdate_)
748 return;
749
750 btTransform principal;
751 principal.setRotation(btQuaternion::getIdentity());
752 principal.setOrigin(btVector3(0.0f, 0.0f, 0.0f));
753
754 // Calculate center of mass shift from all the collision shapes
755 unsigned numShapes = (unsigned)compoundShape_->getNumChildShapes();
756 if (numShapes)
757 {
758 PODVector<float> masses(numShapes);
759 for (unsigned i = 0; i < numShapes; ++i)
760 {
761 // The actual mass does not matter, divide evenly between child shapes
762 masses[i] = 1.0f;
763 }
764
765 btVector3 inertia(0.0f, 0.0f, 0.0f);
766 compoundShape_->calculatePrincipalAxisTransform(&masses[0], principal, inertia);
767 }
768
769 // Add child shapes to shifted compound shape with adjusted offset
770 while (shiftedCompoundShape_->getNumChildShapes())
771 shiftedCompoundShape_->removeChildShapeByIndex(shiftedCompoundShape_->getNumChildShapes() - 1);
772 for (unsigned i = 0; i < numShapes; ++i)
773 {
774 btTransform adjusted = compoundShape_->getChildTransform(i);
775 adjusted.setOrigin(adjusted.getOrigin() - principal.getOrigin());
776 shiftedCompoundShape_->addChildShape(adjusted, compoundShape_->getChildShape(i));
777 }
778
779 // If shifted compound shape has only one child with no offset/rotation, use the child shape
780 // directly as the rigid body collision shape for better collision detection performance
781 bool useCompound = !numShapes || numShapes > 1;
782 if (!useCompound)
783 {
784 const btTransform& childTransform = shiftedCompoundShape_->getChildTransform(0);
785 if (!ToVector3(childTransform.getOrigin()).Equals(Vector3::ZERO) ||
786 !ToQuaternion(childTransform.getRotation()).Equals(Quaternion::IDENTITY))
787 useCompound = true;
788 }
789
790 btCollisionShape* oldCollisionShape = body_->getCollisionShape();
791 body_->setCollisionShape(useCompound ? shiftedCompoundShape_.Get() : shiftedCompoundShape_->getChildShape(0));
792
793 // If we have one shape and this is a triangle mesh, we use a custom material callback in order to adjust internal edges
794 if (!useCompound && body_->getCollisionShape()->getShapeType() == SCALED_TRIANGLE_MESH_SHAPE_PROXYTYPE &&
795 physicsWorld_->GetInternalEdge())
796 body_->setCollisionFlags(body_->getCollisionFlags() | btCollisionObject::CF_CUSTOM_MATERIAL_CALLBACK);
797 else
798 body_->setCollisionFlags(body_->getCollisionFlags() & ~btCollisionObject::CF_CUSTOM_MATERIAL_CALLBACK);
799
800 // Reapply rigid body position with new center of mass shift
801 Vector3 oldPosition = GetPosition();
802 centerOfMass_ = ToVector3(principal.getOrigin());
803 SetPosition(oldPosition);
804
805 // Calculate final inertia
806 btVector3 localInertia(0.0f, 0.0f, 0.0f);
807 if (mass_ > 0.0f)
808 shiftedCompoundShape_->calculateLocalInertia(mass_, localInertia);
809 body_->setMassProps(mass_, localInertia);
810 body_->updateInertiaTensor();
811
812 // Reapply constraint positions for new center of mass shift
813 if (node_)
814 {
815 for (PODVector<Constraint*>::Iterator i = constraints_.Begin(); i != constraints_.End(); ++i)
816 (*i)->ApplyFrames();
817 }
818
819 // Readd body to world to reset Bullet collision cache if collision shape was changed (issue #2064)
820 if (inWorld_ && body_->getCollisionShape() != oldCollisionShape && physicsWorld_)
821 {
822 btDiscreteDynamicsWorld* world = physicsWorld_->GetWorld();
823 world->removeRigidBody(body_.Get());
824 world->addRigidBody(body_.Get(), (short)collisionLayer_, (short)collisionMask_);
825 }
826 }
827
UpdateGravity()828 void RigidBody::UpdateGravity()
829 {
830 if (physicsWorld_ && body_)
831 {
832 btDiscreteDynamicsWorld* world = physicsWorld_->GetWorld();
833
834 int flags = body_->getFlags();
835 if (useGravity_ && gravityOverride_ == Vector3::ZERO)
836 flags &= ~BT_DISABLE_WORLD_GRAVITY;
837 else
838 flags |= BT_DISABLE_WORLD_GRAVITY;
839 body_->setFlags(flags);
840
841 if (useGravity_)
842 {
843 // If override vector is zero, use world's gravity
844 if (gravityOverride_ == Vector3::ZERO)
845 body_->setGravity(world->getGravity());
846 else
847 body_->setGravity(ToBtVector3(gravityOverride_));
848 }
849 else
850 body_->setGravity(btVector3(0.0f, 0.0f, 0.0f));
851 }
852 }
853
SetNetAngularVelocityAttr(const PODVector<unsigned char> & value)854 void RigidBody::SetNetAngularVelocityAttr(const PODVector<unsigned char>& value)
855 {
856 float maxVelocity = physicsWorld_ ? physicsWorld_->GetMaxNetworkAngularVelocity() : DEFAULT_MAX_NETWORK_ANGULAR_VELOCITY;
857 MemoryBuffer buf(value);
858 SetAngularVelocity(buf.ReadPackedVector3(maxVelocity));
859 }
860
GetNetAngularVelocityAttr() const861 const PODVector<unsigned char>& RigidBody::GetNetAngularVelocityAttr() const
862 {
863 float maxVelocity = physicsWorld_ ? physicsWorld_->GetMaxNetworkAngularVelocity() : DEFAULT_MAX_NETWORK_ANGULAR_VELOCITY;
864 attrBuffer_.Clear();
865 attrBuffer_.WritePackedVector3(GetAngularVelocity(), maxVelocity);
866 return attrBuffer_.GetBuffer();
867 }
868
AddConstraint(Constraint * constraint)869 void RigidBody::AddConstraint(Constraint* constraint)
870 {
871 constraints_.Push(constraint);
872 }
873
RemoveConstraint(Constraint * constraint)874 void RigidBody::RemoveConstraint(Constraint* constraint)
875 {
876 constraints_.Remove(constraint);
877 // A constraint being removed should possibly cause the object to eg. start falling, so activate
878 Activate();
879 }
880
ReleaseBody()881 void RigidBody::ReleaseBody()
882 {
883 if (body_)
884 {
885 // Release all constraints which refer to this body
886 // Make a copy for iteration
887 PODVector<Constraint*> constraints = constraints_;
888 for (PODVector<Constraint*>::Iterator i = constraints.Begin(); i != constraints.End(); ++i)
889 (*i)->ReleaseConstraint();
890
891 RemoveBodyFromWorld();
892
893 body_.Reset();
894 }
895 }
896
OnMarkedDirty(Node * node)897 void RigidBody::OnMarkedDirty(Node* node)
898 {
899 // If node transform changes, apply it back to the physics transform. However, do not do this when a SmoothedTransform
900 // is in use, because in that case the node transform will be constantly updated into smoothed, possibly non-physical
901 // states; rather follow the SmoothedTransform target transform directly
902 // Also, for kinematic objects Bullet asks the position from us, so we do not need to apply ourselves
903 // (exception: initial setting of transform)
904 if ((!kinematic_ || !hasSimulated_) && (!physicsWorld_ || !physicsWorld_->IsApplyingTransforms()) && !smoothedTransform_)
905 {
906 // Physics operations are not safe from worker threads
907 Scene* scene = GetScene();
908 if (scene && scene->IsThreadedUpdate())
909 {
910 scene->DelayedMarkedDirty(this);
911 return;
912 }
913
914 // Check if transform has changed from the last one set in ApplyWorldTransform()
915 Vector3 newPosition = node_->GetWorldPosition();
916 Quaternion newRotation = node_->GetWorldRotation();
917
918 if (!newRotation.Equals(lastRotation_))
919 {
920 lastRotation_ = newRotation;
921 SetRotation(newRotation);
922 }
923 if (!newPosition.Equals(lastPosition_))
924 {
925 lastPosition_ = newPosition;
926 SetPosition(newPosition);
927 }
928 }
929 }
930
OnNodeSet(Node * node)931 void RigidBody::OnNodeSet(Node* node)
932 {
933 if (node)
934 node->AddListener(this);
935 }
936
OnSceneSet(Scene * scene)937 void RigidBody::OnSceneSet(Scene* scene)
938 {
939 if (scene)
940 {
941 if (scene == node_)
942 URHO3D_LOGWARNING(GetTypeName() + " should not be created to the root scene node");
943
944 physicsWorld_ = scene->GetOrCreateComponent<PhysicsWorld>();
945 physicsWorld_->AddRigidBody(this);
946
947 AddBodyToWorld();
948 }
949 else
950 {
951 ReleaseBody();
952
953 if (physicsWorld_)
954 physicsWorld_->RemoveRigidBody(this);
955 }
956 }
957
AddBodyToWorld()958 void RigidBody::AddBodyToWorld()
959 {
960 if (!physicsWorld_)
961 return;
962
963 URHO3D_PROFILE(AddBodyToWorld);
964
965 if (mass_ < 0.0f)
966 mass_ = 0.0f;
967
968 if (body_)
969 RemoveBodyFromWorld();
970 else
971 {
972 // Correct inertia will be calculated below
973 btVector3 localInertia(0.0f, 0.0f, 0.0f);
974 body_ = new btRigidBody(mass_, this, shiftedCompoundShape_.Get(), localInertia);
975 body_->setUserPointer(this);
976
977 // Check for existence of the SmoothedTransform component, which should be created by now in network client mode.
978 // If it exists, subscribe to its change events
979 smoothedTransform_ = GetComponent<SmoothedTransform>();
980 if (smoothedTransform_)
981 {
982 SubscribeToEvent(smoothedTransform_, E_TARGETPOSITION, URHO3D_HANDLER(RigidBody, HandleTargetPosition));
983 SubscribeToEvent(smoothedTransform_, E_TARGETROTATION, URHO3D_HANDLER(RigidBody, HandleTargetRotation));
984 }
985
986 // Check if CollisionShapes already exist in the node and add them to the compound shape.
987 // Do not update mass yet, but do it once all shapes have been added
988 PODVector<CollisionShape*> shapes;
989 node_->GetComponents<CollisionShape>(shapes);
990 for (PODVector<CollisionShape*>::Iterator i = shapes.Begin(); i != shapes.End(); ++i)
991 (*i)->NotifyRigidBody(false);
992
993 // Check if this node contains Constraint components that were waiting for the rigid body to be created, and signal them
994 // to create themselves now
995 PODVector<Constraint*> constraints;
996 node_->GetComponents<Constraint>(constraints);
997 for (PODVector<Constraint*>::Iterator i = constraints.Begin(); i != constraints.End(); ++i)
998 (*i)->CreateConstraint();
999 }
1000
1001 UpdateMass();
1002 UpdateGravity();
1003
1004 int flags = body_->getCollisionFlags();
1005 if (trigger_)
1006 flags |= btCollisionObject::CF_NO_CONTACT_RESPONSE;
1007 else
1008 flags &= ~btCollisionObject::CF_NO_CONTACT_RESPONSE;
1009 if (kinematic_)
1010 flags |= btCollisionObject::CF_KINEMATIC_OBJECT;
1011 else
1012 flags &= ~btCollisionObject::CF_KINEMATIC_OBJECT;
1013 body_->setCollisionFlags(flags);
1014 body_->forceActivationState(kinematic_ ? DISABLE_DEACTIVATION : ISLAND_SLEEPING);
1015
1016 if (!IsEnabledEffective())
1017 return;
1018
1019 btDiscreteDynamicsWorld* world = physicsWorld_->GetWorld();
1020 world->addRigidBody(body_.Get(), (short)collisionLayer_, (short)collisionMask_);
1021 inWorld_ = true;
1022 readdBody_ = false;
1023 hasSimulated_ = false;
1024
1025 if (mass_ > 0.0f)
1026 Activate();
1027 else
1028 {
1029 SetLinearVelocity(Vector3::ZERO);
1030 SetAngularVelocity(Vector3::ZERO);
1031 }
1032 }
1033
RemoveBodyFromWorld()1034 void RigidBody::RemoveBodyFromWorld()
1035 {
1036 if (physicsWorld_ && body_ && inWorld_)
1037 {
1038 btDiscreteDynamicsWorld* world = physicsWorld_->GetWorld();
1039 world->removeRigidBody(body_.Get());
1040 inWorld_ = false;
1041 }
1042 }
1043
HandleTargetPosition(StringHash eventType,VariantMap & eventData)1044 void RigidBody::HandleTargetPosition(StringHash eventType, VariantMap& eventData)
1045 {
1046 // Copy the smoothing target position to the rigid body
1047 if (!physicsWorld_ || !physicsWorld_->IsApplyingTransforms())
1048 SetPosition(static_cast<SmoothedTransform*>(GetEventSender())->GetTargetWorldPosition());
1049 }
1050
HandleTargetRotation(StringHash eventType,VariantMap & eventData)1051 void RigidBody::HandleTargetRotation(StringHash eventType, VariantMap& eventData)
1052 {
1053 // Copy the smoothing target rotation to the rigid body
1054 if (!physicsWorld_ || !physicsWorld_->IsApplyingTransforms())
1055 SetRotation(static_cast<SmoothedTransform*>(GetEventSender())->GetTargetWorldRotation());
1056 }
1057
1058 }
1059