@needle-tools/engine 2.35.5-pre → 2.37.0-pre

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (168) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/dist/needle-engine.d.ts +312 -232
  3. package/dist/needle-engine.js +456 -437
  4. package/dist/needle-engine.js.map +4 -4
  5. package/dist/needle-engine.min.js +81 -76
  6. package/dist/needle-engine.min.js.map +4 -4
  7. package/lib/engine/api.d.ts +1 -0
  8. package/lib/engine/api.js +1 -0
  9. package/lib/engine/api.js.map +1 -1
  10. package/lib/engine/debug/debug.d.ts +1 -0
  11. package/lib/engine/debug/debug.js +3 -0
  12. package/lib/engine/debug/debug.js.map +1 -1
  13. package/lib/engine/debug/debug_overlay.js +12 -1
  14. package/lib/engine/debug/debug_overlay.js.map +1 -1
  15. package/lib/engine/engine_element_loading.js +1 -1
  16. package/lib/engine/engine_element_loading.js.map +1 -1
  17. package/lib/engine/engine_gameobject.d.ts +1 -0
  18. package/lib/engine/engine_gameobject.js +15 -2
  19. package/lib/engine/engine_gameobject.js.map +1 -1
  20. package/lib/engine/engine_gltf_builtin_components.js +4 -0
  21. package/lib/engine/engine_gltf_builtin_components.js.map +1 -1
  22. package/lib/engine/engine_mainloop_utils.d.ts +1 -1
  23. package/lib/engine/engine_mainloop_utils.js +7 -3
  24. package/lib/engine/engine_mainloop_utils.js.map +1 -1
  25. package/lib/engine/engine_physics.d.ts +49 -57
  26. package/lib/engine/engine_physics.js +464 -417
  27. package/lib/engine/engine_physics.js.map +1 -1
  28. package/lib/engine/engine_physics.types.d.ts +16 -0
  29. package/lib/engine/engine_physics.types.js +19 -0
  30. package/lib/engine/engine_physics.types.js.map +1 -0
  31. package/lib/engine/engine_serialization_core.d.ts +3 -0
  32. package/lib/engine/engine_serialization_core.js +19 -6
  33. package/lib/engine/engine_serialization_core.js.map +1 -1
  34. package/lib/engine/engine_setup.js +4 -2
  35. package/lib/engine/engine_setup.js.map +1 -1
  36. package/lib/engine/engine_time.d.ts +1 -0
  37. package/lib/engine/engine_time.js +1 -0
  38. package/lib/engine/engine_time.js.map +1 -1
  39. package/lib/engine/engine_types.d.ts +46 -26
  40. package/lib/engine/engine_types.js +24 -37
  41. package/lib/engine/engine_types.js.map +1 -1
  42. package/lib/engine/engine_typestore.d.ts +1 -0
  43. package/lib/engine/engine_typestore.js +1 -0
  44. package/lib/engine/engine_typestore.js.map +1 -1
  45. package/lib/engine/engine_util_decorator.d.ts +6 -0
  46. package/lib/engine/engine_util_decorator.js +54 -0
  47. package/lib/engine/engine_util_decorator.js.map +1 -0
  48. package/lib/engine/engine_utils.d.ts +1 -1
  49. package/lib/engine/engine_utils.js +2 -2
  50. package/lib/engine/engine_utils.js.map +1 -1
  51. package/lib/engine/extensions/NEEDLE_animator_controller_model.js.map +1 -1
  52. package/lib/engine/extensions/NEEDLE_gameobject_data.js +2 -0
  53. package/lib/engine/extensions/NEEDLE_gameobject_data.js.map +1 -1
  54. package/lib/engine/extensions/NEEDLE_techniques_webgl.js +5 -0
  55. package/lib/engine/extensions/NEEDLE_techniques_webgl.js.map +1 -1
  56. package/lib/engine-components/Animation.d.ts +5 -0
  57. package/lib/engine-components/Animation.js +14 -0
  58. package/lib/engine-components/Animation.js.map +1 -1
  59. package/lib/engine-components/AnimatorController.d.ts +1 -0
  60. package/lib/engine-components/AnimatorController.js +14 -7
  61. package/lib/engine-components/AnimatorController.js.map +1 -1
  62. package/lib/engine-components/BoxHelperComponent.d.ts +2 -2
  63. package/lib/engine-components/BoxHelperComponent.js +28 -9
  64. package/lib/engine-components/BoxHelperComponent.js.map +1 -1
  65. package/lib/engine-components/Collider.d.ts +7 -2
  66. package/lib/engine-components/Collider.js +27 -15
  67. package/lib/engine-components/Collider.js.map +1 -1
  68. package/lib/engine-components/Component.d.ts +8 -16
  69. package/lib/engine-components/Component.js +18 -117
  70. package/lib/engine-components/Component.js.map +1 -1
  71. package/lib/engine-components/DragControls.js +9 -6
  72. package/lib/engine-components/DragControls.js.map +1 -1
  73. package/lib/engine-components/GroundProjection.d.ts +2 -0
  74. package/lib/engine-components/GroundProjection.js +18 -6
  75. package/lib/engine-components/GroundProjection.js.map +1 -1
  76. package/lib/engine-components/NavMesh.d.ts +0 -5
  77. package/lib/engine-components/NavMesh.js +100 -10
  78. package/lib/engine-components/NavMesh.js.map +1 -1
  79. package/lib/engine-components/NestedGltf.js +2 -0
  80. package/lib/engine-components/NestedGltf.js.map +1 -1
  81. package/lib/engine-components/ReflectionProbe.d.ts +22 -0
  82. package/lib/engine-components/ReflectionProbe.js +134 -0
  83. package/lib/engine-components/ReflectionProbe.js.map +1 -0
  84. package/lib/engine-components/Renderer.d.ts +13 -2
  85. package/lib/engine-components/Renderer.js +96 -45
  86. package/lib/engine-components/Renderer.js.map +1 -1
  87. package/lib/engine-components/RigidBody.d.ts +40 -25
  88. package/lib/engine-components/RigidBody.js +253 -142
  89. package/lib/engine-components/RigidBody.js.map +1 -1
  90. package/lib/engine-components/SpatialTrigger.js +1 -1
  91. package/lib/engine-components/SpatialTrigger.js.map +1 -1
  92. package/lib/engine-components/SpectatorCamera.d.ts +1 -0
  93. package/lib/engine-components/SpectatorCamera.js +9 -2
  94. package/lib/engine-components/SpectatorCamera.js.map +1 -1
  95. package/lib/engine-components/SpringJoint.d.ts +0 -13
  96. package/lib/engine-components/SpringJoint.js +42 -41
  97. package/lib/engine-components/SpringJoint.js.map +1 -1
  98. package/lib/engine-components/VideoPlayer.js.map +1 -1
  99. package/lib/engine-components/WebARSessionRoot.d.ts +7 -7
  100. package/lib/engine-components/WebARSessionRoot.js +7 -7
  101. package/lib/engine-components/WebARSessionRoot.js.map +1 -1
  102. package/lib/engine-components/WebXR.d.ts +10 -8
  103. package/lib/engine-components/WebXR.js +50 -26
  104. package/lib/engine-components/WebXR.js.map +1 -1
  105. package/lib/engine-components/WebXRAvatar.d.ts +4 -5
  106. package/lib/engine-components/WebXRAvatar.js +9 -8
  107. package/lib/engine-components/WebXRAvatar.js.map +1 -1
  108. package/lib/engine-components/WebXRController.d.ts +21 -21
  109. package/lib/engine-components/WebXRController.js +90 -68
  110. package/lib/engine-components/WebXRController.js.map +1 -1
  111. package/lib/engine-components/WebXRGrabRendering.d.ts +3 -3
  112. package/lib/engine-components/WebXRGrabRendering.js +2 -2
  113. package/lib/engine-components/WebXRGrabRendering.js.map +1 -1
  114. package/lib/engine-components/WebXRSync.d.ts +8 -8
  115. package/lib/engine-components/WebXRSync.js +15 -15
  116. package/lib/engine-components/WebXRSync.js.map +1 -1
  117. package/lib/engine-components/codegen/components.d.ts +2 -3
  118. package/lib/engine-components/codegen/components.js +2 -3
  119. package/lib/engine-components/codegen/components.js.map +1 -1
  120. package/lib/engine-components/ui/EventSystem.d.ts +1 -0
  121. package/lib/engine-components/ui/EventSystem.js +21 -1
  122. package/lib/engine-components/ui/EventSystem.js.map +1 -1
  123. package/package.json +3 -4
  124. package/src/engine/api.ts +2 -1
  125. package/src/engine/codegen/register_types.js +291 -6
  126. package/src/engine/debug/debug.ts +4 -0
  127. package/src/engine/debug/debug_overlay.ts +9 -2
  128. package/src/engine/engine_element_loading.ts +1 -1
  129. package/src/engine/engine_gameobject.ts +19 -6
  130. package/src/engine/engine_gltf_builtin_components.ts +5 -1
  131. package/src/engine/engine_mainloop_utils.ts +7 -3
  132. package/src/engine/engine_physics.ts +508 -469
  133. package/src/engine/engine_physics.types.ts +19 -0
  134. package/src/engine/engine_serialization_core.ts +22 -8
  135. package/src/engine/engine_setup.ts +6 -2
  136. package/src/engine/engine_time.ts +2 -0
  137. package/src/engine/engine_types.ts +82 -55
  138. package/src/engine/engine_typestore.ts +2 -0
  139. package/src/engine/engine_util_decorator.ts +69 -0
  140. package/src/engine/engine_utils.ts +6 -5
  141. package/src/engine/extensions/EXT_texture_exr.js +1 -1
  142. package/src/engine/extensions/NEEDLE_animator_controller_model.ts +2 -1
  143. package/src/engine/extensions/NEEDLE_gameobject_data.ts +2 -0
  144. package/src/engine/extensions/NEEDLE_techniques_webgl.ts +7 -0
  145. package/src/engine-components/Animation.ts +14 -1
  146. package/src/engine-components/AnimatorController.ts +19 -9
  147. package/src/engine-components/BoxHelperComponent.ts +30 -9
  148. package/src/engine-components/Collider.ts +29 -29
  149. package/src/engine-components/Component.ts +26 -135
  150. package/src/engine-components/DragControls.ts +9 -5
  151. package/src/engine-components/GroundProjection.ts +22 -7
  152. package/src/engine-components/NavMesh.ts +114 -115
  153. package/src/engine-components/NestedGltf.ts +2 -0
  154. package/src/engine-components/ReflectionProbe.ts +141 -0
  155. package/src/engine-components/Renderer.ts +796 -737
  156. package/src/engine-components/RigidBody.ts +258 -149
  157. package/src/engine-components/SpatialTrigger.ts +1 -1
  158. package/src/engine-components/SpectatorCamera.ts +10 -2
  159. package/src/engine-components/SpringJoint.ts +41 -41
  160. package/src/engine-components/VideoPlayer.ts +1 -2
  161. package/src/engine-components/WebARSessionRoot.ts +16 -16
  162. package/src/engine-components/WebXR.ts +65 -50
  163. package/src/engine-components/WebXRAvatar.ts +16 -16
  164. package/src/engine-components/WebXRController.ts +143 -112
  165. package/src/engine-components/WebXRGrabRendering.ts +6 -6
  166. package/src/engine-components/WebXRSync.ts +20 -20
  167. package/src/engine-components/codegen/components.ts +2 -3
  168. package/src/engine-components/ui/EventSystem.ts +26 -3
@@ -1,38 +1,15 @@
1
- import * as CANNON from 'cannon-es';
2
- import * as THREE from 'three';
3
- import cannonDebugger from 'cannon-es-debugger';
4
- import * as utils from "./engine_utils";
5
- import * as threeutils from "./engine_three_utils";
6
- import { $physicsKey, Collision } from './engine_types';
7
- import { InstancingUtil } from './engine_instancing';
1
+ import { Box3, BufferAttribute, BufferGeometry, Layers, LineBasicMaterial, LineSegments, Matrix4, Quaternion, Raycaster, Sphere, Vector2, Vector3 } from 'three';
2
+ import { getParam } from "./engine_utils";
3
+ import { getWorldPosition, getWorldQuaternion, getWorldScale, setWorldPositionXYZ, setWorldQuaternionXYZW } from "./engine_three_utils";
4
+ import { Collision, ContactPoint } from './engine_types';
8
5
  import { foreachComponent } from './engine_gameobject';
9
- const debugPhysics = utils.getParam("debugphysics");
10
- const debugCollisions = utils.getParam("debugcollisions");
11
- export class BodyOptions {
12
- mass = 1;
13
- kinematic = false;
14
- physicsEvents = false;
15
- drag = 0;
16
- angularDrag = 0.05;
17
- sleepThreshold = .01;
18
- }
19
- // TODO: refactor to return some kind of handle for adding/removing
20
- class PhysicsObject {
21
- obj;
22
- parent;
23
- body;
24
- shapes = [];
25
- collisonCallback = null;
26
- _hasRigidbody = false;
27
- _didSleepLastStep = false;
28
- constructor(obj, body) {
29
- this.obj = obj;
30
- this.parent = obj.parent;
31
- this.body = body;
32
- if (this.body)
33
- this.body[$physicsKey] = obj;
34
- }
35
- }
6
+ import RAPIER, { ActiveEvents, Collider, ColliderDesc, EventQueue, RigidBody, World } from '@dimforge/rapier3d-compat';
7
+ import { CollisionDetectionMode } from '../engine/engine_physics.types';
8
+ const debugPhysics = getParam("debugphysics");
9
+ const debugColliderPlacement = getParam("debugphysicscolliders");
10
+ const debugCollisions = getParam("debugcollisions");
11
+ const $componentKey = Symbol("needle component");
12
+ const $bodyKey = Symbol("physics body");
36
13
  export class RaycastOptions {
37
14
  ray = undefined;
38
15
  cam = undefined;
@@ -48,13 +25,13 @@ export class RaycastOptions {
48
25
  ignore = undefined;
49
26
  screenPointFromOffset(ox, oy) {
50
27
  if (this.screenPoint === undefined)
51
- this.screenPoint = new THREE.Vector2();
28
+ this.screenPoint = new Vector2();
52
29
  this.screenPoint.x = ox / window.innerWidth * 2 - 1;
53
30
  this.screenPoint.y = -(oy / window.innerHeight) * 2 + 1;
54
31
  }
55
32
  setMask(mask) {
56
33
  if (!this.layerMask)
57
- this.layerMask = new THREE.Layers();
34
+ this.layerMask = new Layers();
58
35
  const lm = this.layerMask;
59
36
  if (lm)
60
37
  lm.mask = mask;
@@ -75,7 +52,7 @@ export class SphereIntersection {
75
52
  }
76
53
  export class Physics {
77
54
  // raycasting
78
- raycaster = new THREE.Raycaster();
55
+ raycaster = new Raycaster();
79
56
  defaultRaycastOptions = new RaycastOptions();
80
57
  targetBuffer = new Array(1);
81
58
  defaultThresholds = {
@@ -86,56 +63,50 @@ export class Physics {
86
63
  Sprite: {}
87
64
  };
88
65
  sphereResults = new Array();
89
- sphereMask = new THREE.Layers();
90
- sphereOverlap(spherePos, radius) {
66
+ sphereMask = new Layers();
67
+ sphereOverlap(spherePos, radius, traverseChildsAfterHit = true) {
91
68
  this.sphereResults.length = 0;
92
69
  if (!this.context.scene)
93
70
  return this.sphereResults;
94
- const sphere = new THREE.Sphere(spherePos, radius);
71
+ const sphere = new Sphere(spherePos, radius);
95
72
  const mask = this.sphereMask;
96
73
  mask.enableAll();
97
74
  mask.disable(2);
98
- // mask testing
99
- // const dummy = new THREE.Layers();
100
- // dummy.set(2);
101
- // console.log(dummy.mask, mask.test(dummy))
102
75
  for (const ch of this.context.scene.children) {
103
- const i = this.onSphereOverlap(ch, sphere, mask);
104
- if (i)
105
- this.sphereResults.push(i);
76
+ this.onSphereOverlap(ch, sphere, mask, this.sphereResults, traverseChildsAfterHit);
106
77
  }
107
78
  return this.sphereResults.sort((a, b) => a.distance - b.distance);
108
79
  }
109
- tempBoundingBox = new THREE.Box3();
110
- onSphereOverlap(obj, sp, mask) {
111
- if (obj.type === "Mesh") {
112
- if (!obj.layers.test(mask))
113
- return null;
80
+ tempBoundingBox = new Box3();
81
+ onSphereOverlap(obj, sp, mask, results, traverseChildsAfterHit) {
82
+ if (obj.type === "Mesh" && obj.layers.test(mask)) {
114
83
  const mesh = obj;
115
84
  const geo = mesh.geometry;
116
85
  if (!geo.boundingBox)
117
86
  geo.computeBoundingBox();
118
- if (!geo.boundingBox)
119
- return null;
120
- if (mesh.matrixWorldNeedsUpdate)
121
- mesh.updateMatrixWorld();
122
- const test = this.tempBoundingBox.copy(geo.boundingBox).applyMatrix4(mesh.matrixWorld);
123
- if (sp.intersectsBox(test)) {
124
- // console.log(obj, obj.layers.test(mask), obj.layers.mask, mask.mask);
125
- const wp = threeutils.getWorldPosition(obj);
126
- const dist = wp.distanceTo(sp.center);
127
- const int = new SphereIntersection(obj, dist, sp.center.clone());
128
- return int;
87
+ if (geo.boundingBox) {
88
+ if (mesh.matrixWorldNeedsUpdate)
89
+ mesh.updateMatrixWorld();
90
+ const test = this.tempBoundingBox.copy(geo.boundingBox).applyMatrix4(mesh.matrixWorld);
91
+ if (sp.intersectsBox(test)) {
92
+ // console.log(obj, obj.layers.test(mask), obj.layers.mask, mask.mask);
93
+ const wp = getWorldPosition(obj);
94
+ const dist = wp.distanceTo(sp.center);
95
+ const int = new SphereIntersection(obj, dist, sp.center.clone());
96
+ results.push(int);
97
+ if (!traverseChildsAfterHit)
98
+ return;
99
+ }
129
100
  }
130
101
  }
131
- else if (obj.children) {
102
+ if (obj.children) {
132
103
  for (const ch of obj.children) {
133
- const i = this.onSphereOverlap(ch, sp, mask);
134
- if (i)
135
- return i;
104
+ const len = results.length;
105
+ this.onSphereOverlap(ch, sp, mask, results, traverseChildsAfterHit);
106
+ if (len != results.length && !traverseChildsAfterHit)
107
+ return;
136
108
  }
137
109
  }
138
- return null;
139
110
  }
140
111
  raycastFromRay(ray, options = null) {
141
112
  const opts = options ?? this.defaultRaycastOptions;
@@ -179,9 +150,9 @@ export class Physics {
179
150
  results = this.defaultRaycastOptions.results;
180
151
  }
181
152
  // layermask
182
- // https://github.com/mrdoob/three.js/blob/master/src/core/Layers.js
153
+ // https://github.com/mrdoob/js/blob/master/src/core/Layers.js
183
154
  if (options.layerMask !== undefined) {
184
- if (options.layerMask instanceof THREE.Layers)
155
+ if (options.layerMask instanceof Layers)
185
156
  rc.layers.mask = options.layerMask.mask;
186
157
  else
187
158
  rc.layers.mask = options.layerMask;
@@ -202,385 +173,343 @@ export class Physics {
202
173
  return results;
203
174
  }
204
175
  // physics simulation
205
- get isUpdating() { return this._isUpdatingPhysicsWorld; }
176
+ _tempPosition = new Vector3();
177
+ _tempQuaternion = new Quaternion();
178
+ _tempMatrix = new Matrix4();
179
+ static _didLoadPhysicsEngine = false;
206
180
  _isUpdatingPhysicsWorld = false;
181
+ get isUpdating() { return this._isUpdatingPhysicsWorld; }
207
182
  context;
208
- world = new CANNON.World();
183
+ world;
184
+ _hasCreatedWorld = false;
185
+ eventQueue;
186
+ collisionHandler;
187
+ // private rigidbodies: Array<IRigidbody | null> = [];
209
188
  objects = [];
210
- tempPosition = new THREE.Vector3();
211
- tempQuaternion = new THREE.Quaternion();
189
+ bodies = [];
190
+ // private rigidbodiesLookup: Map<IRigidbody, RigidBody> = new Map<IRigidbody, RigidBody>();
191
+ // private kinematicColliders: Array<IComponent> = [];
192
+ // private rigidbodyLookup: Map<IRigidbody, IComponent[]> = new Map<IRigidbody, IComponent[]>();
193
+ // private objectLookup: Map<Object3D, IRigidbody> = new Map<Object3D, IRigidbody>();
212
194
  constructor(context) {
213
195
  this.context = context;
214
- this.world.gravity.set(0, -9.82, 0);
215
- if (debugPhysics) {
216
- // https://www.npmjs.com/package/cannon-es-debugger
217
- const opts = {};
218
- opts["onInit"] = (_body, mesh, _shape) => {
219
- // ignore in raycast
220
- mesh.layers.set(-1);
221
- };
222
- cannonDebugger(context.scene, this.world.bodies, opts);
223
- }
224
- this.world.addEventListener("beginContact", this.onBeginContact.bind(this));
225
- this.world.addEventListener("endContact", this.onEndContact.bind(this));
226
- }
227
- addPreStepListener(listener) {
228
- this.world.addEventListener("preStep", listener);
229
- }
230
- addPostStepListener(listener) {
231
- this.world.addEventListener("postStep", listener);
232
- }
233
- addConstraint(constraint) {
234
- this.world.addConstraint(constraint);
235
- }
236
- setGravity(vec) {
237
- this.world.gravity.set(vec.x, vec.y, vec.z);
238
- }
239
- multiplyGravity(vec) {
240
- this.world.gravity.x *= vec.x;
241
- this.world.gravity.y *= vec.y;
242
- this.world.gravity.z *= vec.z;
243
- }
244
- addBody(go, body) {
245
- for (let i = 0; i < this.objects.length; i++) {
246
- const reg = this.objects[i];
247
- if (reg.obj === go) {
248
- reg._hasRigidbody = true;
249
- break;
196
+ }
197
+ async createWorld() {
198
+ if (this._hasCreatedWorld) {
199
+ console.error("Invalid call to create physics world: world is already created");
200
+ return;
201
+ }
202
+ this._hasCreatedWorld = true;
203
+ if (!Physics._didLoadPhysicsEngine) {
204
+ await RAPIER.init().then(() => RAPIER);
205
+ Physics._didLoadPhysicsEngine = true;
206
+ }
207
+ const gravity = { x: 0.0, y: -9.81, z: 0.0 };
208
+ this.world = new World(gravity);
209
+ }
210
+ addBoxCollider(collider, center, size) {
211
+ const obj = collider.gameObject;
212
+ const scale = getWorldScale(obj, this._tempPosition).multiply(size);
213
+ scale.multiplyScalar(0.5);
214
+ const desc = ColliderDesc.cuboid(scale.x, scale.y, scale.z);
215
+ this.createCollider(collider, desc, center);
216
+ }
217
+ addSphereCollider(collider, center, radius) {
218
+ const obj = collider.gameObject;
219
+ const scale = getWorldScale(obj, this._tempPosition).multiplyScalar(radius);
220
+ const desc = ColliderDesc.ball(scale.x);
221
+ this.createCollider(collider, desc, center);
222
+ }
223
+ addMeshCollider(collider, mesh, convex) {
224
+ const geo = mesh.geometry;
225
+ if (!geo) {
226
+ if (debugPhysics)
227
+ console.warn("Missing mesh geometry", mesh.name);
228
+ return;
229
+ }
230
+ let positions = geo.getAttribute("position").array;
231
+ const indices = geo.index?.array;
232
+ // console.log(geo.center())
233
+ // scaling seems not supported yet https://github.com/dimforge/rapier/issues/243
234
+ const scale = getWorldScale(mesh, this._tempPosition);
235
+ if (Math.abs(scale.x - 1) > 0.0001 || Math.abs(scale.y - 1) > 0.0001 || Math.abs(scale.z - 1) > 0.0001) {
236
+ console.warn("Your model is using scaled mesh colliders which is not optimal for performance", mesh.name, Object.assign({}, scale), mesh);
237
+ // showBalloonWarning("Your model is using scaled mesh colliders which is not optimal for performance: " + mesh.name + ", consider using unscaled objects");
238
+ const scaledPositions = new Float32Array(positions.length);
239
+ for (let i = 0; i < positions.length; i += 3) {
240
+ scaledPositions[i] = positions[i] * scale.x;
241
+ scaledPositions[i + 1] = positions[i + 1] * scale.y;
242
+ scaledPositions[i + 2] = positions[i + 2] * scale.z;
250
243
  }
244
+ positions = scaledPositions;
245
+ }
246
+ const desc = convex ? ColliderDesc.convexMesh(positions) : ColliderDesc.trimesh(positions, indices);
247
+ if (desc) {
248
+ this.createCollider(collider, desc);
249
+ // col.setTranslationWrtParent(new Vector3(0,2,0));
250
+ }
251
+ }
252
+ createCollider(collider, desc, center) {
253
+ if (!this.world)
254
+ throw new Error("Physics world not initialized");
255
+ const matrix = this._tempMatrix;
256
+ const { rigidBody, useExplicitMassProperties } = this.getRigidbody(collider, this._tempMatrix);
257
+ matrix.decompose(this._tempPosition, this._tempQuaternion, new Vector3());
258
+ if (center)
259
+ this._tempPosition.add(center);
260
+ desc.setTranslation(this._tempPosition.x, this._tempPosition.y, this._tempPosition.z);
261
+ desc.setRotation(this._tempQuaternion);
262
+ desc.setSensor(collider.isTrigger);
263
+ // if we want to use explicit mass properties, we need to set the collider density to 0
264
+ // otherwise rapier will compute the mass properties based on the collider shape and density
265
+ // https://rapier.rs/docs/user_guides/javascript/rigid_bodies#mass-properties
266
+ if (useExplicitMassProperties) {
267
+ // desc.setDensity(0);
251
268
  }
252
- // dont add the body before it has shapes
253
- // otherwise things like forces appplied in the frame before the shapes exist will be zeroed out
254
- if (body.shapes.length > 0)
255
- this.world.addBody(body);
256
- }
257
- removeBody(go, body, removeCompletely = true) {
258
- this.world.removeBody(body);
259
- for (let i = 0; i < this.objects.length; i++) {
260
- const reg = this.objects[i];
261
- if (reg.obj === go) {
262
- reg._hasRigidbody = false;
263
- if (removeCompletely)
264
- this.objects.splice(i, 1);
265
- break;
269
+ const col = this.world.createCollider(desc, rigidBody);
270
+ col[$componentKey] = collider;
271
+ collider[$bodyKey] = col;
272
+ col.setActiveEvents(ActiveEvents.COLLISION_EVENTS);
273
+ this.objects.push(collider);
274
+ this.bodies.push(col);
275
+ return col;
276
+ }
277
+ getRigidbody(collider, _matrix) {
278
+ if (!this.world)
279
+ throw new Error("Physics world not initialized");
280
+ let rigidBody = null;
281
+ let useExplicitMassProperties = false;
282
+ if (collider.attachedRigidbody) {
283
+ const rb = collider.attachedRigidbody;
284
+ rigidBody = rb[$bodyKey];
285
+ useExplicitMassProperties = true;
286
+ if (!rigidBody) {
287
+ const kinematic = rb.isKinematic && !debugColliderPlacement;
288
+ if (debugPhysics)
289
+ console.log("Create rigidbody", kinematic);
290
+ const rigidBodyDesc = kinematic ? RAPIER.RigidBodyDesc.kinematicPositionBased() : RAPIER.RigidBodyDesc.dynamic();
291
+ const pos = getWorldPosition(collider.attachedRigidbody.gameObject);
292
+ rigidBodyDesc.setTranslation(pos.x, pos.y, pos.z);
293
+ rigidBodyDesc.setRotation(getWorldQuaternion(collider.attachedRigidbody.gameObject));
294
+ rigidBody = this.world.createRigidBody(rigidBodyDesc);
295
+ this.bodies.push(rigidBody);
296
+ this.objects.push(rb);
266
297
  }
298
+ rigidBody[$componentKey] = rb;
299
+ rb[$bodyKey] = rigidBody;
300
+ this.internalUpdateProperties(rb, rigidBody);
301
+ this.getRigidbodyRelativeMatrix(collider.gameObject, rb.gameObject, _matrix);
302
+ }
303
+ else {
304
+ const rigidBodyDesc = RAPIER.RigidBodyDesc.kinematicPositionBased();
305
+ const pos = getWorldPosition(collider.gameObject);
306
+ rigidBodyDesc.setTranslation(pos.x, pos.y, pos.z);
307
+ rigidBodyDesc.setRotation(getWorldQuaternion(collider.gameObject));
308
+ rigidBody = this.world.createRigidBody(rigidBodyDesc);
309
+ _matrix.identity();
310
+ rigidBody[$componentKey] = null;
267
311
  }
312
+ return { rigidBody: rigidBody, useExplicitMassProperties: useExplicitMassProperties };
268
313
  }
269
- removeShape(obj, shape) {
270
- for (const reg of this.objects) {
271
- if (reg.obj === obj) {
272
- if (reg.body) {
273
- reg.body.removeShape(shape);
274
- reg.body.updateMassProperties();
314
+ removeBody(obj) {
315
+ const body = obj[$bodyKey];
316
+ obj[$bodyKey] = null;
317
+ if (body) {
318
+ const index = this.objects.findIndex(o => o === obj);
319
+ if (index >= 0) {
320
+ const body = this.bodies[index];
321
+ this.bodies.splice(index, 1);
322
+ this.objects.splice(index, 1);
323
+ if (body instanceof Collider) {
324
+ this.world?.removeCollider(body, true);
325
+ }
326
+ else if (body instanceof RigidBody) {
327
+ this.world?.removeRigidBody(body);
328
+ }
329
+ // check if we need to remove the rigidbody too
330
+ const col = obj;
331
+ if (col.isCollider && col.attachedRigidbody) {
332
+ const rb = col.attachedRigidbody[$bodyKey];
333
+ if (rb && rb.numColliders() <= 0 && rb.world() === this.world) {
334
+ this.world?.removeRigidBody(rb);
335
+ }
275
336
  }
276
- return;
277
337
  }
278
338
  }
279
339
  }
280
- // TODO: make it work with rigibody in parent
281
- createBody(obj, settings) {
282
- const body = this.internalCreateBody(obj, null);
283
- if (settings.mass)
284
- body.mass = settings.mass;
285
- if (settings.kinematic)
286
- body.type = CANNON.Body.KINEMATIC;
287
- else
288
- body.type = CANNON.Body.DYNAMIC;
289
- if (settings.drag)
290
- body.linearDamping = settings.drag;
291
- if (settings.angularDrag)
292
- body.angularDamping = settings.angularDrag;
293
- if (settings.sleepThreshold)
294
- body.sleepSpeedLimit = settings.sleepThreshold;
295
- if (body.shapes.length > 0)
296
- this.world.addBody(body);
297
- const po = new PhysicsObject(obj, body);
298
- po._hasRigidbody = true;
299
- this.objects.push(po);
300
- if (debugPhysics) {
301
- console.log("created new body", obj.name, body, body.sleepState, this.world.gravity);
302
- }
303
- if (settings.physicsEvents)
304
- this.registerCollisionEvents(po);
305
- return body;
306
- }
307
- addBoxCollider(obj, trigger, center, size, rb) {
308
- const scale = this.tempPosition;
309
- obj.getWorldScale(scale);
310
- const pos = new CANNON.Vec3(.5 * scale.x * size.x, .5 * scale.y * size.y, .5 * scale.z * size.z);
311
- const shape = new CANNON.Box(pos);
312
- shape.collisionResponse = !trigger;
313
- center = center.clone();
314
- center.multiply(scale);
315
- const body = this.addShape(obj, shape, center, rb);
316
- if (body !== null) {
317
- this.world.addBody(body);
318
- if (this.isAlreadyRegistered(body))
319
- return shape;
320
- const po = new PhysicsObject(obj, body);
321
- this.objects.push(po);
322
- }
323
- return shape;
324
- }
325
- addSphereCollider(obj, center, radius, rb) {
326
- const scale = this.tempPosition;
327
- obj.getWorldScale(scale);
328
- const factor = Math.max(scale.x, scale.y, scale.z);
329
- const shape = new CANNON.Sphere(radius * factor);
330
- // shape.collisionResponse = !trigger;
331
- center = center.clone();
332
- center.multiply(scale);
333
- const body = this.addShape(obj, shape, center, rb);
334
- if (body !== null) {
335
- this.world.addBody(body);
336
- if (this.isAlreadyRegistered(body))
337
- return shape;
338
- const po = new PhysicsObject(obj, body);
339
- this.objects.push(po);
340
- }
341
- return shape;
342
- }
343
- addMeshCollider(_obj) {
344
- // see https://github.com/schteppe/cannon.js/blob/master/demos/bunny.html
345
- if (debugPhysics)
346
- console.warn("TODO mesh collider not yet supported");
347
- // const geometry: THREE.BufferGeometry = obj["geometry"];
348
- // console.log(geometry);
349
- // const size = geometry.boundingBox.max.clone();
350
- // size.sub(geometry.boundingBox.min);
351
- // console.log(size);
352
- // this.addBoxCollider(obj, size);
353
- // const verts = geometry.getAttribute("position").array;
354
- // const faces = new Array<Array<number>>();
355
- // console.log(geometry);
356
- // for (let i = 0; i < geometry.index.array.length; i += 3) {
357
- // const i0 = geometry.index.array[i];
358
- // const i1 = geometry.index.array[i + 1];
359
- // const i2 = geometry.index.array[i + 2];
360
- // const v0 = new THREE.Vector3(verts[i0 * 3], verts[i0 * 3 + 1], verts[i0 * 3 + 2]);
361
- // const v1 = new THREE.Vector3(verts[i1 * 3], verts[i1 * 3 + 1], verts[i1 * 3 + 2]);
362
- // const v2 = new THREE.Vector3(verts[i2 * 3], verts[i2 * 3 + 1], verts[i2 * 3 + 2]);
363
- // const face = [v0, v1, v2];
364
- // faces.push(face);
340
+ updateBody(comp, translation, rotation) {
341
+ if (comp.destroyed || !comp.gameObject)
342
+ return;
343
+ if (!translation && !rotation)
344
+ return;
345
+ if (comp.isCollider === true) {
346
+ // const collider = comp as ICollider;
347
+ console.warn("TODO: implement updating collider position");
348
+ }
349
+ else {
350
+ const rigidbody = comp;
351
+ const body = rigidbody[$bodyKey];
352
+ if (body) {
353
+ this.syncPhysicsBody(rigidbody.gameObject, body, translation, rotation);
354
+ }
355
+ }
356
+ }
357
+ updateProperties(rigidbody) {
358
+ const physicsBody = rigidbody[$bodyKey];
359
+ if (physicsBody) {
360
+ this.internalUpdateProperties(rigidbody, physicsBody);
361
+ }
362
+ }
363
+ internal_getRigidbody(rb) {
364
+ return rb[$bodyKey];
365
+ }
366
+ internalUpdateProperties(rb, rigidbody) {
367
+ // continuous collision detection
368
+ // https://rapier.rs/docs/user_guides/javascript/rigid_bodies#continuous-collision-detection
369
+ rigidbody.enableCcd(rb.collisionDetectionMode !== CollisionDetectionMode.Discrete);
370
+ rigidbody.setLinearDamping(rb.drag);
371
+ rigidbody.setAngularDamping(rb.angularDrag);
372
+ rigidbody.setGravityScale(rb.useGravity ? 1 : 0, true);
373
+ // https://rapier.rs/docs/user_guides/javascript/rigid_bodies#mass-properties
374
+ // rigidbody.setAdditionalMass(rb.mass, true);
375
+ // for (let i = 0; i < rigidbody.numColliders(); i++) {
376
+ // const collider = rigidbody.collider(i);
377
+ // if (collider) {
378
+ // collider.setMass(rb.mass);
379
+ // // const density = rb.mass / collider.shape.computeMassProperties().mass;
380
+ // }
365
381
  // }
366
- // const convex = new THREE.ConvexBufferGeometry(faces);
367
- // var shape = new CANNON.ConvexPolyhedron({ verts, faces });
368
- // this.addShape(obj, shape);
369
- }
370
- isAlreadyRegistered(body) {
371
- for (const obj of this.objects) {
372
- if (obj.body === body)
373
- return true;
374
- }
375
- return false;
376
- }
377
- tempMat1 = new THREE.Matrix4();
378
- tempMat2 = new THREE.Matrix4();
379
- addShape(obj, shape, center, rb) {
380
- let body = null;
381
- if (rb) {
382
- // if (debugPhysics)
383
- // console.log("get rb body", rb);
384
- rb.initialize();
385
- console.assert(rb.body ? true : false, "rigidbody didn't initialize / produce a physics body", rb);
386
- body = rb.body;
382
+ // lock rotations
383
+ rigidbody.setEnabledRotations(!rb.lockRotationX, !rb.lockRotationY, !rb.lockRotationZ, true);
384
+ rigidbody.setEnabledTranslations(!rb.lockPositionX, !rb.lockPositionY, !rb.lockPositionZ, true);
385
+ if (rb.isKinematic) {
386
+ rigidbody.setBodyType(RAPIER.RigidBodyType.KinematicPositionBased);
387
387
  }
388
388
  else {
389
- // console.log("has no rb", obj);
390
- body = this.internalCreateBody(obj, null);
391
- body.type = CANNON.Body.KINEMATIC;
389
+ rigidbody.setBodyType(RAPIER.RigidBodyType.Dynamic);
392
390
  }
393
- if (body) {
394
- // console.log(obj.name, obj.position, obj.rotation)
395
- // the center is serialized from Unity so we need to move it into threejs space
396
- // this should probably happen on export for colliders
397
- center.x *= -1;
398
- let wp = obj.position;
399
- let wr = obj.quaternion;
400
- // console.log(obj.name, wp)
401
- if (rb && rb.gameObject !== obj) {
402
- this.tempMat1.copy(obj.matrixWorld);
403
- this.tempMat2.copy(rb.gameObject.matrixWorld).invert();
404
- this.tempMat1.premultiply(this.tempMat2);
405
- this.tempMat1.decompose(wp, wr, this.tempPosition);
406
- }
407
- else {
408
- wp = threeutils.getWorldPosition(obj);
409
- const bp = body.position;
410
- wp.x -= bp.x;
411
- wp.y -= bp.y;
412
- wp.z -= bp.z;
413
- wr = threeutils.getWorldQuaternion(obj);
414
- const r = new THREE.Quaternion(body.quaternion.x, body.quaternion.y, body.quaternion.z, body.quaternion.w);
415
- wr.multiply(r.invert());
416
- }
417
- // get rotation difference
418
- wp.add(center);
419
- // if (rb) {
420
- // this.tempMat.setPosition(wp);
421
- // this.tempMat.makeRotationFromQuaternion(wr);
422
- // this.tempMat.multiplyMatrices(this.tempMat, rb?.gameObject.matrix);
423
- // this.tempMat.decompose(this.tempPosition, this.tempQuaternion, new THREE.Vector3());
424
- // wp.copy(this.tempPosition);
425
- // }
426
- // wp.applyQuaternion(wr);
427
- const pos = new CANNON.Vec3(wp.x, wp.y, wp.z);
428
- const rot = new CANNON.Quaternion(wr.x, wr.y, wr.z, wr.w);
429
- body.addShape(shape, pos, rot);
430
- body.updateMassProperties();
431
- this.world.addBody(body);
432
- }
433
- return body;
434
391
  }
435
392
  // private _lastStepTime: number | undefined = 0;
436
- step(deltaTime) {
393
+ lines;
394
+ step(_deltaTime) {
395
+ if (!this.world)
396
+ return;
437
397
  this._isUpdatingPhysicsWorld = true;
438
- deltaTime = Math.min(deltaTime, 1 / 30);
439
- this.world.step(deltaTime);
398
+ if (!this.eventQueue) {
399
+ this.eventQueue = new EventQueue(false);
400
+ }
401
+ this.world.step(this.eventQueue);
440
402
  this._isUpdatingPhysicsWorld = false;
441
- if (debugPhysics && this.context.time.frameCount % 60 === 0) {
442
- // console.log("physics world has " + this.world.bodies.length + " bodies", this.world);
403
+ this.updateDebugRendering(this.world);
404
+ }
405
+ updateDebugRendering(world) {
406
+ if (debugPhysics || debugColliderPlacement) {
407
+ if (!this.lines) {
408
+ let material = new LineBasicMaterial({
409
+ color: 0xffffff,
410
+ // vertexColors: THREE.VertexColors
411
+ });
412
+ let geometry = new BufferGeometry();
413
+ this.lines = new LineSegments(geometry, material);
414
+ this.context.scene.add(this.lines);
415
+ }
416
+ const buffers = world.debugRender();
417
+ this.lines.geometry.setAttribute('position', new BufferAttribute(buffers.vertices, 3));
418
+ this.lines.geometry.setAttribute('color', new BufferAttribute(buffers.colors, 4));
443
419
  }
444
420
  }
445
- temp = new THREE.Vector3();
446
- tempQuat = new THREE.Quaternion();
447
421
  postStep() {
422
+ if (!this.world)
423
+ return;
448
424
  this._isUpdatingPhysicsWorld = true;
449
- for (let i = 0; i < this.objects.length; i++) {
450
- const entry = this.objects[i];
451
- const body = entry.body;
452
- if (!body || !body.world)
453
- continue;
454
- body.sleepTick(this.context.time.time);
455
- if (debugPhysics) {
456
- if (!entry._didSleepLastStep && body.sleepState === CANNON.Body.SLEEPING) {
457
- console.log("BODY SLEEPING", body);
458
- }
459
- else if (entry._didSleepLastStep && body.sleepState !== CANNON.Body.SLEEPING) {
460
- console.log("BODY WOKE UP", body);
461
- }
462
- }
463
- entry._didSleepLastStep = body.sleepState === CANNON.Body.SLEEPING;
464
- // if(body.sleepState === CANNON.Body.SLEEPING) {
465
- // console.log("SLEEP", body.name);
466
- // }
467
- // if (body.type == CANNON.Body.KINEMATIC) continue;
468
- const obj = entry.obj;
469
- if (body.type === CANNON.Body.KINEMATIC) {
470
- const wp = threeutils.getWorldPosition(obj, this.temp);
471
- body.position.set(wp.x, wp.y, wp.z);
472
- const rot = threeutils.getWorldQuaternion(obj, this.tempQuat);
473
- body.quaternion.set(rot.x, rot.y, rot.z, rot.w);
474
- continue;
475
- }
476
- // when reparenting (e.g. attached to controller) I think it doesnt work with previous parent? need to test again, to tired now
477
- if (entry.parent && obj.parent === entry.parent) {
478
- threeutils.setWorldQuaternionXYZW(obj, body.quaternion.x, body.quaternion.y, body.quaternion.z, body.quaternion.w);
479
- const p = body.position;
480
- threeutils.setWorldPositionXYZ(obj, p.x, p.y, p.z);
481
- if (body.velocity.length() > body.sleepSpeedLimit) {
482
- InstancingUtil.markDirty(obj);
483
- }
484
- // this.worldToLocal.x = body.position.x;
485
- // this.worldToLocal.y = body.position.y;
486
- // this.worldToLocal.z = body.position.z;
487
- // const pos = entry.parent.worldToLocal(this.worldToLocal);
488
- // obj.position.x = pos.x;
489
- // obj.position.y = pos.y;
490
- // obj.position.z = pos.z;
491
- // if (entry.center) {
492
- // this.rotatedCenter.copy(entry.center);
493
- // const rot = this.tempQuaternion;
494
- // rot.copy(obj.quaternion);
495
- // // obj.getWorldQuaternion(this.tempQuaternion)
496
- // this.rotatedCenter.applyQuaternion(rot);
497
- // obj.getWorldScale(this.tempVector);
498
- // this.rotatedCenter.divide(this.tempVector);
499
- // obj.position.sub(this.rotatedCenter);
500
- // }
501
- }
502
- }
425
+ this.syncObjects();
503
426
  this._isUpdatingPhysicsWorld = false;
427
+ if (this.eventQueue && !this.collisionHandler) {
428
+ this.collisionHandler = new PhysicsCollisionHandler(this.world, this.eventQueue);
429
+ }
430
+ if (this.collisionHandler) {
431
+ this.collisionHandler.handleCollisionEvents();
432
+ this.collisionHandler.update();
433
+ }
504
434
  }
505
- internalCreateBody(obj, shape) {
506
- const body = new CANNON.Body();
507
- body["_owner"] = obj;
508
- body["_name"] = obj.name;
509
- obj.getWorldPosition(this.tempPosition);
510
- const pos = this.tempPosition;
511
- body.position = new CANNON.Vec3(pos.x, pos.y, pos.z);
512
- const quat = this.tempQuaternion;
513
- obj.getWorldQuaternion(quat);
514
- body.quaternion = new CANNON.Quaternion(quat.x, quat.y, quat.z, quat.w);
515
- body.type = CANNON.Body.KINEMATIC;
516
- if (shape) {
517
- body.addShape(shape);
518
- body.updateMassProperties();
519
- }
520
- return body;
521
- }
522
- // private findObject(obj: THREE.Object3D): PhysicsObject | null {
523
- // for (let i = 0; i < this.objects.length; i++) {
524
- // const entry = this.objects[i];
525
- // if (entry.obj == obj)
526
- // return entry;
527
- // }
528
- // return null;
529
- // }
530
- registerCollisionEvents(obj) {
531
- if (obj.collisonCallback)
532
- this.unregisterCollisionEvents(obj);
533
- if (!obj.body)
534
- return;
535
- const evt = evt => this.raiseCollisionEvents(obj.obj, evt);
536
- obj.collisonCallback = evt.bind(this);
537
- obj.body.addEventListener("collide", obj.collisonCallback);
538
- }
539
- unregisterCollisionEvents(obj) {
540
- if (!obj.collisonCallback)
541
- return;
542
- if (!obj.body)
435
+ /** sync rendered objects with physics world (except for colliders without rigidbody) */
436
+ syncObjects() {
437
+ if (debugColliderPlacement)
543
438
  return;
544
- obj.body.removeEventListener("collide", obj.collisonCallback);
545
- }
546
- onBeginContact(_) {
547
- // this is called after the object collide event so we dont really need it
548
- // console.log("START");
549
- }
550
- collisionContext = new CollisionContext();
551
- raiseCollisionEvents(obj, event) {
552
- const collision = new Collision(obj, event, this.collisionContext);
553
- if (debugCollisions)
554
- console.log("collision between", event.contact.bi, event.contact.bj, obj, event);
555
- foreachComponent(obj, (c) => {
556
- c.__internalHandleCollision(collision, false);
557
- });
558
- // handle triggers
559
- if (collision.collider && !collision.collider.attachedRigidbody && collision.collider.isTrigger) {
560
- const collision2 = new Collision(collision.gameObject, event, this.collisionContext, true);
561
- foreachComponent(collision.gameObject, (c) => {
562
- c.__internalHandleCollision(collision2, true);
563
- });
439
+ for (let i = 0; i < this.bodies.length; i++) {
440
+ const obj = this.objects[i];
441
+ const body = this.bodies[i];
442
+ // if the collider is not attached to a rigidbody
443
+ // it means that its kinematic so we need to update its position
444
+ const col = obj;
445
+ if (col?.isCollider === true && !col.attachedRigidbody) {
446
+ const rigidbody = body.parent();
447
+ if (rigidbody)
448
+ this.syncPhysicsBody(obj.gameObject, rigidbody, true, true);
449
+ continue;
450
+ }
451
+ // sync
452
+ const pos = body.translation();
453
+ setWorldPositionXYZ(obj.gameObject, pos.x, pos.y, pos.z);
454
+ const rot = body.rotation();
455
+ setWorldQuaternionXYZW(obj.gameObject, rot.x, rot.y, rot.z, rot.w);
564
456
  }
565
457
  }
566
- onEndContact(args) {
567
- // if(args.bodyB.sleepState !== CANNON.Body.AWAKE) return;
568
- // console.log("END", CANNON.BODY_SLEEP_STATES, args.bodyB.sleepState);
569
- const obj1 = args.bodyA[$physicsKey];
570
- const obj2 = args.bodyB[$physicsKey];
571
- // console.log(obj2);
572
- foreachComponent(obj2, (c) => {
573
- c.__internalHandleExitCollisionEvent(obj1, false);
574
- });
575
- // TODO: stop iterating when we found the collider
576
- foreachComponent(obj1, c => {
577
- const collider = c;
578
- if (collider.isCollider && !collider.attachedRigidbody && collider.isTrigger) {
579
- foreachComponent(collider.gameObject, (c) => {
580
- c.__internalHandleExitCollisionEvent(obj2, true);
581
- });
458
+ syncPhysicsBody(obj, body, translation, rotation) {
459
+ // const bodyType = body.bodyType();
460
+ // const previous = physicsBody.translation();
461
+ // const vel = physicsBody.linvel();
462
+ const worldPosition = getWorldPosition(obj, this._tempPosition);
463
+ const worldQuaternion = getWorldQuaternion(obj, this._tempQuaternion);
464
+ // physicsBody.setBodyType(RAPIER.RigidBodyType.Fixed);
465
+ if (translation)
466
+ body.setTranslation(worldPosition, false);
467
+ if (rotation)
468
+ body.setRotation(worldQuaternion, false);
469
+ // physicsBody.setLinvel(vel, false);
470
+ body.wakeUp();
471
+ // update velocity
472
+ // const pos = physicsBody.translation();
473
+ // pos.x -= previous.x;
474
+ // pos.y -= previous.y;
475
+ // pos.z -= previous.z;
476
+ // // threhold
477
+ // const t = 1;
478
+ // const canUpdateVelocity = Math.abs(pos.x) < t && Math.abs(pos.y) < t && Math.abs(pos.z) < t;
479
+ // if (canUpdateVelocity) {
480
+ // const damping = 1 + this.context.time.deltaTime;
481
+ // vel.x *= damping;
482
+ // vel.y *= damping;
483
+ // vel.z *= damping;
484
+ // vel.x += pos.x;
485
+ // vel.y += pos.y;
486
+ // vel.z += pos.z;
487
+ // console.log(vel);
488
+ // physicsBody.setLinvel(vel, true);
489
+ // }
490
+ // else if(debugPhysics) console.warn("Movement exceeded threshold, not updating velocity", pos);
491
+ // body.setBodyType(bodyType);
492
+ }
493
+ static _matricesBuffer = [];
494
+ getRigidbodyRelativeMatrix(comp, rigidbody, mat, matrices) {
495
+ // collect all matrices to the rigidbody and then build the rigidbody relative matrix
496
+ if (matrices === undefined) {
497
+ matrices = Physics._matricesBuffer;
498
+ matrices.length = 0;
499
+ }
500
+ if (comp === rigidbody) {
501
+ const scale = getWorldScale(comp, this._tempPosition);
502
+ mat.makeScale(scale.x, scale.y, scale.z);
503
+ for (let i = matrices.length - 1; i >= 0; i--) {
504
+ mat.multiply(matrices[i]);
582
505
  }
583
- });
506
+ return mat;
507
+ }
508
+ matrices.push(comp.matrix);
509
+ if (comp.parent) {
510
+ this.getRigidbodyRelativeMatrix(comp.parent, rigidbody, mat, matrices);
511
+ }
512
+ return mat;
584
513
  }
585
514
  }
586
515
  let colliderProvider = null;
@@ -592,4 +521,122 @@ class CollisionContext {
592
521
  return colliderProvider.getCollider(obj);
593
522
  }
594
523
  }
524
+ /** responsible of processing collision events for the component system */
525
+ class PhysicsCollisionHandler {
526
+ world;
527
+ eventQueue;
528
+ constructor(world, eventQueue) {
529
+ this.world = world;
530
+ this.eventQueue = eventQueue;
531
+ }
532
+ activeCollisions = [];
533
+ activeTriggers = [];
534
+ handleCollisionEvents() {
535
+ if (!this.eventQueue)
536
+ return;
537
+ if (!this.world)
538
+ return;
539
+ this.eventQueue.drainCollisionEvents((handle1, handle2, started) => {
540
+ const col1 = this.world.getCollider(handle1);
541
+ const col2 = this.world.getCollider(handle2);
542
+ const colliderComponent1 = col1[$componentKey];
543
+ const colliderComponent2 = col2[$componentKey];
544
+ // console.log("EVT", colliderComponent1.name, colliderComponent2.name, started);
545
+ if (colliderComponent1 && colliderComponent2) {
546
+ if (started) {
547
+ this.onCollisionStarted(colliderComponent1, col1, colliderComponent2, col2);
548
+ this.onCollisionStarted(colliderComponent2, col2, colliderComponent1, col1);
549
+ }
550
+ else {
551
+ this.onCollisionEnded(colliderComponent1, colliderComponent2);
552
+ this.onCollisionEnded(colliderComponent2, colliderComponent1);
553
+ }
554
+ }
555
+ });
556
+ }
557
+ update() {
558
+ this.onHandleCollisionStay();
559
+ }
560
+ onCollisionStarted(self, selfBody, other, otherBody) {
561
+ let collision = null;
562
+ // if one is a trigger we dont get collisions but want to raise the trigger events
563
+ if (self.isTrigger || other.isTrigger) {
564
+ foreachComponent(self.gameObject, (c) => {
565
+ if (c.onTriggerEnter) {
566
+ c.onTriggerEnter(other);
567
+ }
568
+ this.activeTriggers.push({ collider: self, component: c, otherCollider: other });
569
+ });
570
+ }
571
+ else {
572
+ const object = self.gameObject;
573
+ // TODO: we dont respect the flip value here!
574
+ this.world.contactPair(selfBody, otherBody, (manifold, _flipped) => {
575
+ foreachComponent(object, (c) => {
576
+ if (c.onCollisionEnter) {
577
+ if (!collision) {
578
+ const contacts = [];
579
+ const normal = manifold.normal();
580
+ for (let i = 0; i < manifold.numContacts(); i++) {
581
+ const pt1 = manifold.localContactPoint1(i);
582
+ const dist = manifold.contactDist(i);
583
+ if (pt1) {
584
+ const contact = new ContactPoint(pt1, dist, normal);
585
+ contacts.push(contact);
586
+ }
587
+ }
588
+ collision = new Collision(object, other, contacts);
589
+ }
590
+ c.onCollisionEnter.call(c, collision);
591
+ this.activeCollisions.push({ collider: self, component: c, collision });
592
+ }
593
+ });
594
+ });
595
+ }
596
+ }
597
+ onHandleCollisionStay() {
598
+ for (const active of this.activeCollisions) {
599
+ const c = active.component;
600
+ if (c.activeAndEnabled && c.onCollisionStay) {
601
+ const arg = active.collision;
602
+ c.onCollisionStay(arg);
603
+ }
604
+ }
605
+ for (const active of this.activeTriggers) {
606
+ const c = active.component;
607
+ if (c.activeAndEnabled && c.onTriggerStay) {
608
+ const arg = active.collider;
609
+ c.onTriggerStay(arg);
610
+ }
611
+ }
612
+ }
613
+ onCollisionEnded(self, other) {
614
+ for (let i = 0; i < this.activeCollisions.length; i++) {
615
+ const active = this.activeCollisions[i];
616
+ const collider = active.collider;
617
+ if (collider === self && active.collision.collider === other) {
618
+ const c = active.component;
619
+ this.activeCollisions.splice(i, 1);
620
+ i--;
621
+ if (c.activeAndEnabled && c.onCollisionExit) {
622
+ const collision = active.collision;
623
+ c.onCollisionExit(collision);
624
+ }
625
+ }
626
+ }
627
+ for (let i = 0; i < this.activeTriggers.length; i++) {
628
+ const active = this.activeTriggers[i];
629
+ const collider = active.collider;
630
+ if (collider === self && active.otherCollider === other) {
631
+ const c = active.component;
632
+ this.activeTriggers.splice(i, 1);
633
+ i--;
634
+ if (c.activeAndEnabled && c.onTriggerExit) {
635
+ const collision = active.otherCollider;
636
+ c.onTriggerExit(collision);
637
+ }
638
+ }
639
+ }
640
+ }
641
+ }
595
642
  //# sourceMappingURL=engine_physics.js.map