RigidBody2D.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594
  1. // Copyright (c) 2008-2022 the Urho3D project
  2. // License: MIT
  3. #include "../Precompiled.h"
  4. #include "../Core/Context.h"
  5. #include "../IO/Log.h"
  6. #include "../Physics2D/CollisionShape2D.h"
  7. #include "../Physics2D/Constraint2D.h"
  8. #include "../Physics2D/PhysicsUtils2D.h"
  9. #include "../Physics2D/PhysicsWorld2D.h"
  10. #include "../Physics2D/RigidBody2D.h"
  11. #include "../Scene/Scene.h"
  12. #include "../DebugNew.h"
  13. namespace Urho3D
  14. {
  15. extern const char* PHYSICS2D_CATEGORY;
  16. static const BodyType2D DEFAULT_BODYTYPE = BT_STATIC;
  17. static const char* bodyTypeNames[] =
  18. {
  19. "Static",
  20. "Kinematic",
  21. "Dynamic",
  22. nullptr
  23. };
  24. RigidBody2D::RigidBody2D(Context* context) :
  25. Component(context),
  26. useFixtureMass_(true),
  27. body_(nullptr)
  28. {
  29. // Make sure the massData members are zero-initialized.
  30. massData_.mass = 0.0f;
  31. massData_.I = 0.0f;
  32. massData_.center.SetZero();
  33. }
  34. RigidBody2D::~RigidBody2D()
  35. {
  36. if (physicsWorld_)
  37. {
  38. ReleaseBody();
  39. physicsWorld_->RemoveRigidBody(this);
  40. }
  41. }
  42. void RigidBody2D::RegisterObject(Context* context)
  43. {
  44. context->RegisterFactory<RigidBody2D>(PHYSICS2D_CATEGORY);
  45. URHO3D_ACCESSOR_ATTRIBUTE("Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
  46. URHO3D_ENUM_ACCESSOR_ATTRIBUTE("Body Type", GetBodyType, SetBodyType, BodyType2D, bodyTypeNames, DEFAULT_BODYTYPE, AM_DEFAULT);
  47. URHO3D_ACCESSOR_ATTRIBUTE("Mass", GetMass, SetMass, float, 0.0f, AM_DEFAULT);
  48. URHO3D_ACCESSOR_ATTRIBUTE("Inertia", GetInertia, SetInertia, float, 0.0f, AM_DEFAULT);
  49. URHO3D_MIXED_ACCESSOR_ATTRIBUTE("Mass Center", GetMassCenter, SetMassCenter, Vector2, Vector2::ZERO, AM_DEFAULT);
  50. URHO3D_ACCESSOR_ATTRIBUTE("Use Fixture Mass", GetUseFixtureMass, SetUseFixtureMass, bool, true, AM_DEFAULT);
  51. URHO3D_ACCESSOR_ATTRIBUTE("Linear Damping", GetLinearDamping, SetLinearDamping, float, 0.0f, AM_DEFAULT);
  52. URHO3D_ACCESSOR_ATTRIBUTE("Angular Damping", GetAngularDamping, SetAngularDamping, float, 0.0f, AM_DEFAULT);
  53. URHO3D_ACCESSOR_ATTRIBUTE("Allow Sleep", IsAllowSleep, SetAllowSleep, bool, true, AM_DEFAULT);
  54. URHO3D_ACCESSOR_ATTRIBUTE("Fixed Rotation", IsFixedRotation, SetFixedRotation, bool, false, AM_DEFAULT);
  55. URHO3D_ACCESSOR_ATTRIBUTE("Bullet", IsBullet, SetBullet, bool, false, AM_DEFAULT);
  56. URHO3D_ACCESSOR_ATTRIBUTE("Gravity Scale", GetGravityScale, SetGravityScale, float, 1.0f, AM_DEFAULT);
  57. URHO3D_ACCESSOR_ATTRIBUTE("Awake", IsAwake, SetAwake, bool, true, AM_DEFAULT);
  58. URHO3D_MIXED_ACCESSOR_ATTRIBUTE("Linear Velocity", GetLinearVelocity, SetLinearVelocity, Vector2, Vector2::ZERO, AM_DEFAULT);
  59. URHO3D_ACCESSOR_ATTRIBUTE("Angular Velocity", GetAngularVelocity, SetAngularVelocity, float, 0.0f, AM_DEFAULT);
  60. }
  61. void RigidBody2D::OnSetEnabled()
  62. {
  63. bool enabled = IsEnabledEffective();
  64. bodyDef_.enabled = enabled;
  65. if (body_)
  66. body_->SetEnabled(enabled);
  67. MarkNetworkUpdate();
  68. }
  69. void RigidBody2D::SetBodyType(BodyType2D type)
  70. {
  71. auto bodyType = (b2BodyType)type;
  72. if (body_)
  73. {
  74. body_->SetType(bodyType);
  75. // Mass data was reset to keep it legal (e.g. static body should have mass 0).
  76. // If not using fixture mass, reassign our mass data now
  77. if (!useFixtureMass_)
  78. body_->SetMassData(&massData_);
  79. }
  80. else
  81. {
  82. if (bodyDef_.type == bodyType)
  83. return;
  84. bodyDef_.type = bodyType;
  85. }
  86. MarkNetworkUpdate();
  87. }
  88. void RigidBody2D::SetMass(float mass)
  89. {
  90. mass = Max(mass, 0.0f);
  91. if (massData_.mass == mass)
  92. return;
  93. massData_.mass = mass;
  94. if (!useFixtureMass_ && body_)
  95. body_->SetMassData(&massData_);
  96. MarkNetworkUpdate();
  97. }
  98. void RigidBody2D::SetInertia(float inertia)
  99. {
  100. inertia = Max(inertia, 0.0f);
  101. if (massData_.I == inertia)
  102. return;
  103. massData_.I = inertia;
  104. if (!useFixtureMass_ && body_)
  105. body_->SetMassData(&massData_);
  106. MarkNetworkUpdate();
  107. }
  108. void RigidBody2D::SetMassCenter(const Vector2& center)
  109. {
  110. b2Vec2 b2Center = ToB2Vec2(center);
  111. if (massData_.center == b2Center)
  112. return;
  113. massData_.center = b2Center;
  114. if (!useFixtureMass_ && body_)
  115. body_->SetMassData(&massData_);
  116. MarkNetworkUpdate();
  117. }
  118. void RigidBody2D::SetUseFixtureMass(bool useFixtureMass)
  119. {
  120. if (useFixtureMass_ == useFixtureMass)
  121. return;
  122. useFixtureMass_ = useFixtureMass;
  123. if (body_)
  124. {
  125. if (useFixtureMass_)
  126. body_->ResetMassData();
  127. else
  128. body_->SetMassData(&massData_);
  129. }
  130. MarkNetworkUpdate();
  131. }
  132. void RigidBody2D::SetLinearDamping(float linearDamping)
  133. {
  134. if (body_)
  135. body_->SetLinearDamping(linearDamping);
  136. else
  137. {
  138. if (bodyDef_.linearDamping == linearDamping)
  139. return;
  140. bodyDef_.linearDamping = linearDamping;
  141. }
  142. MarkNetworkUpdate();
  143. }
  144. void RigidBody2D::SetAngularDamping(float angularDamping)
  145. {
  146. if (body_)
  147. body_->SetAngularDamping(angularDamping);
  148. else
  149. {
  150. if (bodyDef_.angularDamping == angularDamping)
  151. return;
  152. bodyDef_.angularDamping = angularDamping;
  153. }
  154. MarkNetworkUpdate();
  155. }
  156. void RigidBody2D::SetAllowSleep(bool allowSleep)
  157. {
  158. if (body_)
  159. body_->SetSleepingAllowed(allowSleep);
  160. else
  161. {
  162. if (bodyDef_.allowSleep == allowSleep)
  163. return;
  164. bodyDef_.allowSleep = allowSleep;
  165. }
  166. MarkNetworkUpdate();
  167. }
  168. void RigidBody2D::SetFixedRotation(bool fixedRotation)
  169. {
  170. if (body_)
  171. {
  172. body_->SetFixedRotation(fixedRotation);
  173. // Mass data was reset to keep it legal (e.g. non-rotating body should have inertia 0).
  174. // If not using fixture mass, reassign our mass data now
  175. if (!useFixtureMass_)
  176. body_->SetMassData(&massData_);
  177. }
  178. else
  179. {
  180. if (bodyDef_.fixedRotation == fixedRotation)
  181. return;
  182. bodyDef_.fixedRotation = fixedRotation;
  183. }
  184. MarkNetworkUpdate();
  185. }
  186. void RigidBody2D::SetBullet(bool bullet)
  187. {
  188. if (body_)
  189. body_->SetBullet(bullet);
  190. else
  191. {
  192. if (bodyDef_.bullet == bullet)
  193. return;
  194. bodyDef_.bullet = bullet;
  195. }
  196. MarkNetworkUpdate();
  197. }
  198. void RigidBody2D::SetGravityScale(float gravityScale)
  199. {
  200. if (body_)
  201. body_->SetGravityScale(gravityScale);
  202. else
  203. {
  204. if (bodyDef_.gravityScale == gravityScale)
  205. return;
  206. bodyDef_.gravityScale = gravityScale;
  207. }
  208. MarkNetworkUpdate();
  209. }
  210. void RigidBody2D::SetAwake(bool awake)
  211. {
  212. if (body_)
  213. body_->SetAwake(awake);
  214. else
  215. {
  216. if (bodyDef_.awake == awake)
  217. return;
  218. bodyDef_.awake = awake;
  219. }
  220. MarkNetworkUpdate();
  221. }
  222. void RigidBody2D::SetLinearVelocity(const Vector2& linearVelocity)
  223. {
  224. b2Vec2 b2linearVelocity = ToB2Vec2(linearVelocity);
  225. if (body_)
  226. body_->SetLinearVelocity(b2linearVelocity);
  227. else
  228. {
  229. if (bodyDef_.linearVelocity == b2linearVelocity)
  230. return;
  231. bodyDef_.linearVelocity = b2linearVelocity;
  232. }
  233. MarkNetworkUpdate();
  234. }
  235. void RigidBody2D::SetAngularVelocity(float angularVelocity)
  236. {
  237. if (body_)
  238. body_->SetAngularVelocity(angularVelocity);
  239. else
  240. {
  241. if (bodyDef_.angularVelocity == angularVelocity)
  242. return;
  243. bodyDef_.angularVelocity = angularVelocity;
  244. }
  245. MarkNetworkUpdate();
  246. }
  247. void RigidBody2D::ApplyForce(const Vector2& force, const Vector2& point, bool wake)
  248. {
  249. if (body_ && force != Vector2::ZERO)
  250. body_->ApplyForce(ToB2Vec2(force), ToB2Vec2(point), wake);
  251. }
  252. void RigidBody2D::ApplyForceToCenter(const Vector2& force, bool wake)
  253. {
  254. if (body_ && force != Vector2::ZERO)
  255. body_->ApplyForceToCenter(ToB2Vec2(force), wake);
  256. }
  257. void RigidBody2D::ApplyTorque(float torque, bool wake)
  258. {
  259. if (body_ && torque != 0)
  260. body_->ApplyTorque(torque, wake);
  261. }
  262. void RigidBody2D::ApplyLinearImpulse(const Vector2& impulse, const Vector2& point, bool wake)
  263. {
  264. if (body_ && impulse != Vector2::ZERO)
  265. body_->ApplyLinearImpulse(ToB2Vec2(impulse), ToB2Vec2(point), wake);
  266. }
  267. void RigidBody2D::ApplyLinearImpulseToCenter(const Vector2& impulse, bool wake)
  268. {
  269. if (body_ && impulse != Vector2::ZERO)
  270. body_->ApplyLinearImpulseToCenter(ToB2Vec2(impulse), wake);
  271. }
  272. void RigidBody2D::ApplyAngularImpulse(float impulse, bool wake)
  273. {
  274. if (body_)
  275. body_->ApplyAngularImpulse(impulse, wake);
  276. }
  277. void RigidBody2D::CreateBody()
  278. {
  279. if (body_)
  280. return;
  281. if (!physicsWorld_ || !physicsWorld_->GetWorld())
  282. return;
  283. bodyDef_.position = ToB2Vec2(node_->GetWorldPosition());
  284. bodyDef_.angle = node_->GetWorldRotation().RollAngle() * M_DEGTORAD;
  285. body_ = physicsWorld_->GetWorld()->CreateBody(&bodyDef_);
  286. body_->GetUserData().pointer = (uintptr_t)this;
  287. for (unsigned i = 0; i < collisionShapes_.Size(); ++i)
  288. {
  289. if (collisionShapes_[i])
  290. collisionShapes_[i]->CreateFixture();
  291. }
  292. if (!useFixtureMass_)
  293. body_->SetMassData(&massData_);
  294. for (unsigned i = 0; i < constraints_.Size(); ++i)
  295. {
  296. if (constraints_[i])
  297. constraints_[i]->CreateJoint();
  298. }
  299. }
  300. void RigidBody2D::ReleaseBody()
  301. {
  302. if (!body_)
  303. return;
  304. if (!physicsWorld_ || !physicsWorld_->GetWorld())
  305. return;
  306. // Make a copy for iteration
  307. Vector<WeakPtr<Constraint2D>> constraints = constraints_;
  308. for (unsigned i = 0; i < constraints.Size(); ++i)
  309. {
  310. if (constraints[i])
  311. constraints[i]->ReleaseJoint();
  312. }
  313. for (unsigned i = 0; i < collisionShapes_.Size(); ++i)
  314. {
  315. if (collisionShapes_[i])
  316. collisionShapes_[i]->ReleaseFixture();
  317. }
  318. physicsWorld_->GetWorld()->DestroyBody(body_);
  319. body_ = nullptr;
  320. }
  321. void RigidBody2D::ApplyWorldTransform()
  322. {
  323. if (!body_ || !node_)
  324. return;
  325. // If the rigid body is parented to another rigid body, can not set the transform immediately.
  326. // In that case store it to PhysicsWorld2D for delayed assignment
  327. RigidBody2D* parentRigidBody = nullptr;
  328. Node* parent = node_->GetParent();
  329. if (parent != GetScene() && parent)
  330. parentRigidBody = parent->GetComponent<RigidBody2D>();
  331. // If body is not parented and is static or sleeping, no need to update
  332. if (!parentRigidBody && (!body_->IsEnabled() || body_->GetType() == b2_staticBody || !body_->IsAwake()))
  333. return;
  334. const b2Transform& transform = body_->GetTransform();
  335. Vector3 newWorldPosition = node_->GetWorldPosition();
  336. newWorldPosition.x_ = transform.p.x;
  337. newWorldPosition.y_ = transform.p.y;
  338. Quaternion newWorldRotation(transform.q.GetAngle() * M_RADTODEG, Vector3::FORWARD);
  339. if (parentRigidBody)
  340. {
  341. DelayedWorldTransform2D delayed;
  342. delayed.rigidBody_ = this;
  343. delayed.parentRigidBody_ = parentRigidBody;
  344. delayed.worldPosition_ = newWorldPosition;
  345. delayed.worldRotation_ = newWorldRotation;
  346. physicsWorld_->AddDelayedWorldTransform(delayed);
  347. }
  348. else
  349. ApplyWorldTransform(newWorldPosition, newWorldRotation);
  350. }
  351. void RigidBody2D::ApplyWorldTransform(const Vector3& newWorldPosition, const Quaternion& newWorldRotation)
  352. {
  353. if (newWorldPosition != node_->GetWorldPosition() || newWorldRotation != node_->GetWorldRotation())
  354. {
  355. // Do not feed changed position back to simulation now
  356. physicsWorld_->SetApplyingTransforms(true);
  357. node_->SetWorldPosition(newWorldPosition);
  358. node_->SetWorldRotation(newWorldRotation);
  359. physicsWorld_->SetApplyingTransforms(false);
  360. }
  361. }
  362. void RigidBody2D::AddCollisionShape2D(CollisionShape2D* collisionShape)
  363. {
  364. if (!collisionShape)
  365. return;
  366. WeakPtr<CollisionShape2D> collisionShapePtr(collisionShape);
  367. if (collisionShapes_.Contains(collisionShapePtr))
  368. return;
  369. collisionShapes_.Push(collisionShapePtr);
  370. }
  371. void RigidBody2D::RemoveCollisionShape2D(CollisionShape2D* collisionShape)
  372. {
  373. if (!collisionShape)
  374. return;
  375. WeakPtr<CollisionShape2D> collisionShapePtr(collisionShape);
  376. collisionShapes_.Remove(collisionShapePtr);
  377. }
  378. void RigidBody2D::AddConstraint2D(Constraint2D* constraint)
  379. {
  380. if (!constraint)
  381. return;
  382. WeakPtr<Constraint2D> constraintPtr(constraint);
  383. if (constraints_.Contains(constraintPtr))
  384. return;
  385. constraints_.Push(constraintPtr);
  386. }
  387. void RigidBody2D::RemoveConstraint2D(Constraint2D* constraint)
  388. {
  389. if (!constraint)
  390. return;
  391. WeakPtr<Constraint2D> constraintPtr(constraint);
  392. constraints_.Remove(constraintPtr);
  393. }
  394. float RigidBody2D::GetMass() const
  395. {
  396. if (!useFixtureMass_)
  397. return massData_.mass;
  398. else
  399. return body_ ? body_->GetMass() : 0.0f;
  400. }
  401. float RigidBody2D::GetInertia() const
  402. {
  403. if (!useFixtureMass_)
  404. return massData_.I;
  405. else
  406. return body_ ? body_->GetInertia() : 0.0f;
  407. }
  408. Vector2 RigidBody2D::GetMassCenter() const
  409. {
  410. if (!useFixtureMass_)
  411. return ToVector2(massData_.center);
  412. else
  413. return body_ ? ToVector2(body_->GetLocalCenter()) : Vector2::ZERO;
  414. }
  415. bool RigidBody2D::IsAwake() const
  416. {
  417. return body_ ? body_->IsAwake() : bodyDef_.awake;
  418. }
  419. Vector2 RigidBody2D::GetLinearVelocity() const
  420. {
  421. return ToVector2(body_ ? body_->GetLinearVelocity() : bodyDef_.linearVelocity);
  422. }
  423. float RigidBody2D::GetAngularVelocity() const
  424. {
  425. return body_ ? body_->GetAngularVelocity() : bodyDef_.angularVelocity;
  426. }
  427. void RigidBody2D::OnNodeSet(Node* node)
  428. {
  429. if (node)
  430. {
  431. node->AddListener(this);
  432. PODVector<CollisionShape2D*> shapes;
  433. node_->GetDerivedComponents<CollisionShape2D>(shapes);
  434. for (PODVector<CollisionShape2D*>::Iterator i = shapes.Begin(); i != shapes.End(); ++i)
  435. {
  436. (*i)->CreateFixture();
  437. AddCollisionShape2D(*i);
  438. }
  439. }
  440. }
  441. void RigidBody2D::OnSceneSet(Scene* scene)
  442. {
  443. if (scene)
  444. {
  445. physicsWorld_ = scene->GetDerivedComponent<PhysicsWorld2D>();
  446. if (!physicsWorld_)
  447. physicsWorld_ = scene->CreateComponent<PhysicsWorld2D>();
  448. CreateBody();
  449. physicsWorld_->AddRigidBody(this);
  450. }
  451. else
  452. {
  453. if (physicsWorld_)
  454. {
  455. ReleaseBody();
  456. physicsWorld_->RemoveRigidBody(this);
  457. physicsWorld_.Reset();
  458. }
  459. }
  460. }
  461. void RigidBody2D::OnMarkedDirty(Node* node)
  462. {
  463. if (physicsWorld_ && physicsWorld_->IsApplyingTransforms())
  464. return;
  465. // Physics operations are not safe from worker threads
  466. Scene* scene = GetScene();
  467. if (scene && scene->IsThreadedUpdate())
  468. {
  469. scene->DelayedMarkedDirty(this);
  470. return;
  471. }
  472. // Check if transform has changed from the last one set in ApplyWorldTransform()
  473. b2Vec2 newPosition = ToB2Vec2(node_->GetWorldPosition());
  474. float newAngle = node_->GetWorldRotation().RollAngle() * M_DEGTORAD;
  475. if (!body_)
  476. {
  477. bodyDef_.position = newPosition;
  478. bodyDef_.angle = newAngle;
  479. }
  480. else if (newPosition != body_->GetPosition() || newAngle != body_->GetAngle())
  481. body_->SetTransform(newPosition, newAngle);
  482. }
  483. }