CannonConfig.re.ts 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. import * as RE from 'rogue-engine';
  2. import * as CANNON from 'cannon-es';
  3. import * as THREE from 'three';
  4. import * as RogueCannon from '../Lib/RogueCannon';
  5. import CannonBody from './CannonBody.re';
  6. export default class CannonConfig extends RE.Component {
  7. private _defaultFriction = 0.01;
  8. private _defaultRestitution = 0;
  9. @RE.props.num() maxSubSteps: number = 1;
  10. @RE.props.num()
  11. get defaultFriction() {
  12. return this._defaultFriction;
  13. }
  14. set defaultFriction(value: number) {
  15. this._defaultFriction = value;
  16. RogueCannon.getWorld().defaultContactMaterial.friction = value;
  17. }
  18. @RE.props.num()
  19. get defaultRestitution() {
  20. return this._defaultRestitution;
  21. }
  22. set defaultRestitution(value: number) {
  23. this._defaultRestitution = value;
  24. RogueCannon.getWorld().defaultContactMaterial.restitution = value;
  25. }
  26. @RE.props.vector3() gravity: THREE.Vector3 = new THREE.Vector3(0, -9.82, 0);
  27. private contacts: CANNON.ContactEquation[] = [];
  28. // At the index of the body.id we hold the CannonBody with which body is colliding.
  29. // private activeCollisions: (CANNON.ContactEquation[])[] = [];
  30. private activeCollisions: {[id: string]: boolean} = {};
  31. start() {
  32. this.contacts = [];
  33. this.activeCollisions = {};
  34. CANNON.Body.idCounter = 0;
  35. CANNON.ContactEquation.idCounter = 0;
  36. CANNON.Shape.idCounter = 0;
  37. CANNON.Equation.idCounter = 0;
  38. const world = new CANNON.World()
  39. world.nextId = 0;
  40. RogueCannon.setWorld(world);
  41. RogueCannon.getWorld().gravity.set(this.gravity.x, this.gravity.y, this.gravity.z);
  42. RogueCannon.getWorld().broadphase = new CANNON.NaiveBroadphase();
  43. RogueCannon.getWorld().defaultContactMaterial.friction = this.defaultFriction;
  44. RogueCannon.getWorld().defaultContactMaterial.restitution = this.defaultRestitution;
  45. }
  46. beforeUpdate() {
  47. RogueCannon.getWorld().step(RE.Runtime.deltaTime, RE.Runtime.deltaTime, this.maxSubSteps || 1);
  48. }
  49. afterUpdate() {
  50. this.checkCollisions();
  51. }
  52. checkCollisions() {
  53. const contacts = RogueCannon.getWorld().contacts;
  54. const newContacts: CANNON.ContactEquation[] = [];
  55. contacts.forEach(contact => {
  56. newContacts[contact.id] = contact;
  57. const key1 = contact.bi.id + "_" + contact.bj.id;
  58. const key2 = contact.bj.id + "_" + contact.bi.id;
  59. const key1Collision = this.activeCollisions[key1];
  60. const key2Collision = this.activeCollisions[key2];
  61. if (!this.contacts[contact.id] && !key1Collision && !key2Collision) {
  62. this.activeCollisions[key1] = true;
  63. this.activeCollisions[key2] = true;
  64. this.sendNewCollisionEvents(contact);
  65. }
  66. });
  67. this.contacts.forEach(contact => {
  68. const key1 = contact.bi.id + "_" + contact.bj.id;
  69. const key2 = contact.bj.id + "_" + contact.bi.id;
  70. const foundActiveCollision = this.findActiveCollision(newContacts, contact);
  71. const newContact = newContacts[contact.id];
  72. if ((newContact && newContact.enabled) || foundActiveCollision) {
  73. this.sendOnCollisionStayEvents(contact);
  74. }
  75. if ((!newContact || (newContact && !newContact.enabled)) && !foundActiveCollision) {
  76. this.activeCollisions[key1] = false;
  77. this.activeCollisions[key2] = false;
  78. this.sendOnCollisionExitEvents(contact);
  79. }
  80. });
  81. this.contacts = newContacts;
  82. }
  83. private findActiveCollision(contactList: CANNON.ContactEquation[], sample: CANNON.ContactEquation) {
  84. return contactList.find(contact => {
  85. if (!contact || !contact.enabled) return false;
  86. if (contact.bi === sample.bi && contact.bj === sample.bj) return true;
  87. if (contact.bi === sample.bj && contact.bj === sample.bi) return true;
  88. return false;
  89. });
  90. }
  91. private sendOnCollisionStayEvents(contact: CANNON.ContactEquation) {
  92. const bodyA = CannonBody.findByBody(contact.bi);
  93. const bodyB = CannonBody.findByBody(contact.bj);
  94. if (!bodyB || !bodyA) return;
  95. bodyA.onCollisionStayListeners.forEach(cb => {
  96. cb({other: bodyB, contact});
  97. });
  98. bodyB.onCollisionStayListeners.forEach(cb => {
  99. cb({other: bodyA, contact});
  100. });
  101. }
  102. private sendOnCollisionExitEvents(contact: CANNON.ContactEquation) {
  103. const bodyA = CannonBody.findByBody(contact.bi);
  104. const bodyB = CannonBody.findByBody(contact.bj);
  105. if (!bodyB || !bodyA) return;
  106. bodyA.onCollisionExitListeners.forEach(cb => {
  107. cb({other: bodyB, contact});
  108. });
  109. bodyB.onCollisionExitListeners.forEach(cb => {
  110. cb({other: bodyA, contact});
  111. });
  112. }
  113. private sendNewCollisionEvents(contact: CANNON.ContactEquation) {
  114. const bodyA = CannonBody.findByBody(contact.bi);
  115. const bodyB = CannonBody.findByBody(contact.bj);
  116. if (!bodyB || !bodyA) return;
  117. bodyA.onCollisionEnterListeners.forEach(cb => {
  118. cb({other: bodyB, contact});
  119. });
  120. bodyB.onCollisionEnterListeners.forEach(cb => {
  121. cb({other: bodyA, contact});
  122. });
  123. }
  124. }
  125. RE.registerComponent( CannonConfig );