CannonBody.re.ts 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  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.props.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.props.num()
  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.props.num()
  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.props.num()
  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.props.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.props.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.props.checkbox()
  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. if (!this.body) {
  104. this.createBody();
  105. }
  106. RE.Runtime.onStop(() => {
  107. this.handleOnCollide && this.body.removeEventListener('collide', this.handleOnCollide);
  108. });
  109. }
  110. start() {
  111. RogueCannon.getWorld().addBody(this.body);
  112. this.copyObjectTransform();
  113. }
  114. onDisabled() {
  115. RogueCannon.getWorld().removeBody(this.body);
  116. }
  117. beforeUpdate() {
  118. if (this.body.mass !== this._mass) {
  119. this.mass = this._mass;
  120. }
  121. this.body && (this.body.type = this.getBodyType())
  122. this.body.type !== CANNON.BODY_TYPES.STATIC && this.updatePhysics();
  123. if (this.triggerCollision !== undefined && this.onCollideCB) {
  124. this.onCollideCB(this.triggerCollision);
  125. this.triggerCollision = undefined;
  126. }
  127. }
  128. onBeforeRemoved() {
  129. RogueCannon.getWorld().removeBody(this.body);
  130. }
  131. onCollisionEnterListeners: ((event: {other: CannonBody, contact: CANNON.ContactEquation}) => void)[] = [];
  132. onCollisionStayListeners: ((event: {other: CannonBody, contact: CANNON.ContactEquation}) => void)[] = [];
  133. onCollisionExitListeners: ((event: {other: CannonBody, contact: CANNON.ContactEquation}) => void)[] = [];
  134. onCollisionEnter(cb: (event: {other: CannonBody, contact: CANNON.ContactEquation}) => void) {
  135. this.onCollisionEnterListeners.push(cb);
  136. }
  137. onCollisionStay(cb: (event: {other: CannonBody, contact: CANNON.ContactEquation}) => void) {
  138. this.onCollisionStayListeners.push(cb);
  139. }
  140. onCollisionExit(cb: (event: {other: CannonBody, contact: CANNON.ContactEquation}) => void) {
  141. this.onCollisionExitListeners.push(cb);
  142. }
  143. onCollide(callback: (event: {other: CANNON.Body, contact: CANNON.ContactEquation}) => void) {
  144. this.onCollideCB = callback;
  145. this.body.removeEventListener('collide', this.handleOnCollide);
  146. this.body.addEventListener('collide', this.handleOnCollide);
  147. }
  148. setQuaternion(quaternion: THREE.Quaternion) {
  149. const q = quaternion;
  150. this.body.quaternion.set(q.x, q.y, q.z, q.w);
  151. }
  152. setPosition(position: THREE.Vector3) {
  153. const pos = position;
  154. this.body.position.set(pos.x, pos.y, pos.z);
  155. }
  156. private handleOnCollide = (event: {body: CANNON.Body, target: CANNON.Body, contact: CANNON.ContactEquation}) => {
  157. const bj = event.contact.bj;
  158. const bi = event.contact.bi;
  159. const collision = {
  160. other: bj !== this.body ? bj : bi,
  161. contact: event.contact,
  162. }
  163. this.triggerCollision = collision;
  164. }
  165. private getBodyType() {
  166. let type: CANNON.BodyType = 1;
  167. if (this._type === 0) type = 1;
  168. if (this._type === 1) type = 2;
  169. if (this._type === 2) type = 4;
  170. return type;
  171. }
  172. private createBody() {
  173. this.body = new CANNON.Body({
  174. type: this.getBodyType(),
  175. angularDamping: this.angularDamping,
  176. linearDamping: this.linearDamping,
  177. linearFactor: new CANNON.Vec3(this.linearFactor.x, this.linearFactor.y, this.linearFactor.z),
  178. angularFactor: new CANNON.Vec3(this.angularFactor.x, this.angularFactor.y, this.angularFactor.z),
  179. isTrigger: this.isTrigger,
  180. mass: this._mass,
  181. });
  182. this.copyObjectTransform();
  183. }
  184. protected createShape(): void {};
  185. protected copyObjectTransform() {
  186. this.object3d.parent?.updateMatrixWorld(true);
  187. this.object3d.getWorldPosition(this.worldPos);
  188. this.object3d.getWorldQuaternion(this.worldRot);
  189. this.newBodyPos.set(
  190. this.worldPos.x,
  191. this.worldPos.y,
  192. this.worldPos.z
  193. );
  194. this.newBodyRot.set(
  195. this.worldRot.x,
  196. this.worldRot.y,
  197. this.worldRot.z,
  198. this.worldRot.w
  199. );
  200. this.body.quaternion.copy(this.newBodyRot);
  201. this.body.position.copy(this.newBodyPos);
  202. }
  203. protected copyBodyTransform() {
  204. this.copyBodyPosition();
  205. this.copyBodyRotation();
  206. }
  207. private copyBodyPosition() {
  208. this.newPos.set(
  209. this.body.interpolatedPosition.x,
  210. this.body.interpolatedPosition.y,
  211. this.body.interpolatedPosition.z
  212. );
  213. if (!this.object3d.parent) return;
  214. this.object3d.parent?.worldToLocal(this.newPos);
  215. this.object3d.position.copy(this.newPos);
  216. }
  217. private copyBodyRotation() {
  218. this.newRot.set(
  219. this.body.quaternion.x,
  220. this.body.quaternion.y,
  221. this.body.quaternion.z,
  222. this.body.quaternion.w
  223. );
  224. this.matrixA.makeRotationFromQuaternion(this.newRot);
  225. this.object3d.updateMatrixWorld();
  226. this.matrixB.copy((this.object3d.parent as THREE.Object3D).matrixWorld).invert();
  227. this.matrixC.extractRotation(this.matrixB);
  228. this.matrixA.premultiply(this.matrixC);
  229. this.object3d.quaternion.setFromRotationMatrix(this.matrixA);
  230. }
  231. private updatePhysics() {
  232. this.copyBodyTransform();
  233. }
  234. }
  235. RE.registerComponent(CannonBody);