CannonBody.re.ts 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  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. export default class CannonBody extends RE.Component {
  6. protected _isTrigger = false;
  7. protected _angularDamping = 0;
  8. protected _linearDamping = 0;
  9. protected _angularFactor = new THREE.Vector3(1, 1, 1);
  10. protected _linearFactor = new THREE.Vector3(1, 1, 1);
  11. protected _mass = 1;
  12. protected _useDefaultMass = true;
  13. protected _type = 0;
  14. protected typeOptions = [
  15. "Dynamic",
  16. "Static",
  17. "Kinematic",
  18. ];
  19. @RE.Prop("Select")
  20. get type() {
  21. return this._type;
  22. }
  23. set type(value: number) {
  24. this._type = value;
  25. let type: CANNON.BodyType = 1;
  26. if (value === 0) type = 1;
  27. if (value === 1) type = 2;
  28. if (value === 2) type = 4;
  29. this.body && (this.body.type = type);
  30. }
  31. @RE.Prop("Number")
  32. get angularDamping() {
  33. return this._angularDamping;
  34. }
  35. set angularDamping(value: number) {
  36. this._angularDamping = value;
  37. this.body && (this.body.angularDamping = value);
  38. }
  39. @RE.Prop("Number")
  40. get linearDamping() {
  41. return this._linearDamping;
  42. }
  43. set linearDamping(value: number) {
  44. this._linearDamping = value;
  45. this.body && (this.body.linearDamping = value);
  46. }
  47. @RE.Prop("Number")
  48. get mass() {
  49. return this._mass;
  50. }
  51. set mass(value: number) {
  52. this._mass = value;
  53. this.body && (this.body.mass = value);
  54. this.body && this.body.updateMassProperties();
  55. }
  56. @RE.Prop("Vector3")
  57. get linearFactor() {
  58. return this._linearFactor;
  59. }
  60. set linearFactor(value: THREE.Vector3) {
  61. this._linearFactor = value;
  62. this.body && (this.body.linearFactor.set(value.x, value.y, value.z));
  63. }
  64. @RE.Prop("Vector3")
  65. get angularFactor() {
  66. return this._angularFactor;
  67. }
  68. set angularFactor(value: THREE.Vector3) {
  69. this._angularFactor = value;
  70. this.body && (this.body.angularFactor.set(value.x, value.y, value.z));
  71. }
  72. @RE.Prop("Boolean")
  73. get isTrigger() {
  74. return this._isTrigger;
  75. }
  76. set isTrigger(value: boolean) {
  77. this._isTrigger = value;
  78. this.body && (this.body.isTrigger = value);
  79. }
  80. body: CANNON.Body;
  81. private worldPos = new THREE.Vector3();
  82. private worldRot = new THREE.Quaternion();
  83. private newBodyPos = new CANNON.Vec3();
  84. private newBodyRot = new CANNON.Quaternion();
  85. private newPos = new THREE.Vector3();
  86. private newRot = new THREE.Quaternion();
  87. private matrixA = new THREE.Matrix4();
  88. private matrixB = new THREE.Matrix4();
  89. private matrixC = new THREE.Matrix4();
  90. private onCollideCB: (event: {other: CANNON.Body, contact: CANNON.ContactEquation}) => void | undefined;
  91. private triggerCollision;
  92. static findByBody(body: CANNON.Body) {
  93. let bodyComponent: undefined | CannonBody;
  94. RE.traverseComponents(component => {
  95. if (bodyComponent) return;
  96. if (component instanceof CannonBody && component.body === body) {
  97. bodyComponent = component;
  98. }
  99. });
  100. return bodyComponent;
  101. }
  102. awake() {
  103. this.createBody();
  104. RE.Runtime.onStop(() => {
  105. this.handleOnCollide && this.body.removeEventListener('collide', this.handleOnCollide);
  106. });
  107. }
  108. start() {
  109. RogueCannon.getWorld().addBody(this.body);
  110. this.copyObjectTransform();
  111. }
  112. update() {
  113. if (this.body.mass !== this._mass) {
  114. this.mass = this._mass;
  115. }
  116. this.body && (this.body.type = this.getBodyType())
  117. this.body.type !== CANNON.BODY_TYPES.STATIC && this.updatePhysics();
  118. }
  119. afterUpdate() {
  120. if (this.triggerCollision !== undefined && this.onCollideCB) {
  121. this.onCollideCB(this.triggerCollision);
  122. this.triggerCollision = undefined;
  123. }
  124. }
  125. onBeforeRemoved() {
  126. RogueCannon.getWorld().removeBody(this.body);
  127. }
  128. onCollide(callback: (event: {other: CANNON.Body, contact: CANNON.ContactEquation}) => void) {
  129. this.onCollideCB = callback;
  130. this.body.removeEventListener('collide', this.handleOnCollide);
  131. this.body.addEventListener('collide', this.handleOnCollide);
  132. }
  133. private handleOnCollide = (event: {body: CANNON.Body, target: CANNON.Body, contact: CANNON.ContactEquation}) => {
  134. const bj = event.contact.bj;
  135. const bi = event.contact.bi;
  136. const collision = {
  137. other: bj !== this.body ? bj : bi,
  138. contact: event.contact,
  139. }
  140. this.triggerCollision = collision;
  141. }
  142. private getBodyType() {
  143. let type: CANNON.BodyType = 1;
  144. if (this._type === 0) type = 1;
  145. if (this._type === 1) type = 2;
  146. if (this._type === 2) type = 4;
  147. return type;
  148. }
  149. private createBody() {
  150. this.body = new CANNON.Body({
  151. type: this.getBodyType(),
  152. angularDamping: this.angularDamping,
  153. linearDamping: this.linearDamping,
  154. linearFactor: new CANNON.Vec3(this.linearFactor.x, this.linearFactor.y, this.linearFactor.z),
  155. angularFactor: new CANNON.Vec3(this.angularFactor.x, this.angularFactor.y, this.angularFactor.z),
  156. isTrigger: this.isTrigger,
  157. mass: this._mass,
  158. });
  159. this.copyObjectTransform();
  160. }
  161. protected createShape(): void {};
  162. protected copyObjectTransform() {
  163. this.object3d.parent?.updateMatrixWorld(true);
  164. this.object3d.getWorldPosition(this.worldPos);
  165. this.object3d.getWorldQuaternion(this.worldRot);
  166. this.newBodyPos.set(
  167. this.worldPos.x,
  168. this.worldPos.y,
  169. this.worldPos.z
  170. );
  171. this.newBodyRot.set(
  172. this.worldRot.x,
  173. this.worldRot.y,
  174. this.worldRot.z,
  175. this.worldRot.w
  176. );
  177. this.body.quaternion.copy(this.newBodyRot);
  178. this.body.position.copy(this.newBodyPos);
  179. }
  180. protected copyBodyTransform() {
  181. this.copyBodyPosition();
  182. this.copyBodyRotation();
  183. }
  184. private copyBodyPosition() {
  185. this.newPos.set(
  186. this.body.position.x,
  187. this.body.position.y,
  188. this.body.position.z
  189. );
  190. if (!this.object3d.parent) return;
  191. this.object3d.parent?.worldToLocal(this.newPos);
  192. this.object3d.position.copy(this.newPos);
  193. }
  194. private copyBodyRotation() {
  195. this.newRot.set(
  196. this.body.quaternion.x,
  197. this.body.quaternion.y,
  198. this.body.quaternion.z,
  199. this.body.quaternion.w
  200. );
  201. this.matrixA.makeRotationFromQuaternion(this.newRot);
  202. this.object3d.updateMatrixWorld();
  203. this.matrixB.copy((this.object3d.parent as THREE.Object3D).matrixWorld).invert();
  204. this.matrixC.extractRotation(this.matrixB);
  205. this.matrixA.premultiply(this.matrixC);
  206. this.object3d.quaternion.setFromRotationMatrix(this.matrixA);
  207. }
  208. private updatePhysics() {
  209. this.copyBodyTransform();
  210. }
  211. }
  212. RE.registerComponent(CannonBody);