@needle-tools/engine 2.36.0-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.
- package/CHANGELOG.md +12 -0
- package/dist/needle-engine.d.ts +171 -141
- package/dist/needle-engine.js +451 -437
- package/dist/needle-engine.js.map +4 -4
- package/dist/needle-engine.min.js +82 -82
- package/dist/needle-engine.min.js.map +4 -4
- package/lib/engine/api.d.ts +1 -0
- package/lib/engine/api.js +1 -0
- package/lib/engine/api.js.map +1 -1
- package/lib/engine/debug/debug.d.ts +1 -0
- package/lib/engine/debug/debug.js +3 -0
- package/lib/engine/debug/debug.js.map +1 -1
- package/lib/engine/engine_gameobject.js +2 -1
- package/lib/engine/engine_gameobject.js.map +1 -1
- package/lib/engine/engine_physics.d.ts +33 -42
- package/lib/engine/engine_physics.js +431 -383
- package/lib/engine/engine_physics.js.map +1 -1
- package/lib/engine/engine_physics.types.d.ts +16 -0
- package/lib/engine/engine_physics.types.js +19 -0
- package/lib/engine/engine_physics.types.js.map +1 -0
- package/lib/engine/engine_serialization_core.d.ts +3 -0
- package/lib/engine/engine_serialization_core.js +5 -0
- package/lib/engine/engine_serialization_core.js.map +1 -1
- package/lib/engine/engine_setup.js +3 -1
- package/lib/engine/engine_setup.js.map +1 -1
- package/lib/engine/engine_types.d.ts +45 -26
- package/lib/engine/engine_types.js +24 -37
- package/lib/engine/engine_types.js.map +1 -1
- package/lib/engine/engine_util_decorator.d.ts +6 -0
- package/lib/engine/engine_util_decorator.js +54 -0
- package/lib/engine/engine_util_decorator.js.map +1 -0
- package/lib/engine/engine_utils.d.ts +1 -1
- package/lib/engine/engine_utils.js +1 -1
- package/lib/engine/engine_utils.js.map +1 -1
- package/lib/engine/extensions/NEEDLE_gameobject_data.js +2 -0
- package/lib/engine/extensions/NEEDLE_gameobject_data.js.map +1 -1
- package/lib/engine-components/Animation.d.ts +6 -5
- package/lib/engine-components/Animation.js +0 -7
- package/lib/engine-components/Animation.js.map +1 -1
- package/lib/engine-components/BoxHelperComponent.js +1 -0
- package/lib/engine-components/BoxHelperComponent.js.map +1 -1
- package/lib/engine-components/Collider.d.ts +7 -2
- package/lib/engine-components/Collider.js +27 -15
- package/lib/engine-components/Collider.js.map +1 -1
- package/lib/engine-components/Component.d.ts +6 -15
- package/lib/engine-components/Component.js +7 -112
- package/lib/engine-components/Component.js.map +1 -1
- package/lib/engine-components/DragControls.js +9 -6
- package/lib/engine-components/DragControls.js.map +1 -1
- package/lib/engine-components/NavMesh.d.ts +0 -5
- package/lib/engine-components/NavMesh.js +100 -10
- package/lib/engine-components/NavMesh.js.map +1 -1
- package/lib/engine-components/NestedGltf.js +2 -0
- package/lib/engine-components/NestedGltf.js.map +1 -1
- package/lib/engine-components/RigidBody.d.ts +40 -25
- package/lib/engine-components/RigidBody.js +253 -142
- package/lib/engine-components/RigidBody.js.map +1 -1
- package/lib/engine-components/SpatialTrigger.js +1 -1
- package/lib/engine-components/SpatialTrigger.js.map +1 -1
- package/lib/engine-components/SpectatorCamera.d.ts +1 -0
- package/lib/engine-components/SpectatorCamera.js +9 -2
- package/lib/engine-components/SpectatorCamera.js.map +1 -1
- package/lib/engine-components/SpringJoint.d.ts +0 -13
- package/lib/engine-components/SpringJoint.js +42 -41
- package/lib/engine-components/SpringJoint.js.map +1 -1
- package/lib/engine-components/VideoPlayer.js.map +1 -1
- package/lib/engine-components/WebXR.d.ts +1 -0
- package/lib/engine-components/WebXR.js +10 -2
- package/lib/engine-components/WebXR.js.map +1 -1
- package/lib/engine-components/WebXRController.js +12 -6
- package/lib/engine-components/WebXRController.js.map +1 -1
- package/lib/engine-components/codegen/components.d.ts +1 -3
- package/lib/engine-components/codegen/components.js +1 -3
- package/lib/engine-components/codegen/components.js.map +1 -1
- package/package.json +3 -4
- package/src/engine/api.ts +2 -1
- package/src/engine/codegen/register_types.js +4 -12
- package/src/engine/debug/debug.ts +4 -0
- package/src/engine/engine_gameobject.ts +4 -4
- package/src/engine/engine_physics.ts +460 -421
- package/src/engine/engine_physics.types.ts +19 -0
- package/src/engine/engine_serialization_core.ts +8 -1
- package/src/engine/engine_setup.ts +5 -1
- package/src/engine/engine_types.ts +82 -56
- package/src/engine/engine_util_decorator.ts +69 -0
- package/src/engine/engine_utils.ts +3 -3
- package/src/engine/extensions/NEEDLE_gameobject_data.ts +2 -0
- package/src/engine-components/Animation.ts +10 -12
- package/src/engine-components/BoxHelperComponent.ts +1 -0
- package/src/engine-components/Collider.ts +29 -29
- package/src/engine-components/Component.ts +15 -130
- package/src/engine-components/DragControls.ts +9 -5
- package/src/engine-components/NavMesh.ts +114 -115
- package/src/engine-components/NestedGltf.ts +2 -0
- package/src/engine-components/RigidBody.ts +258 -149
- package/src/engine-components/SpatialTrigger.ts +1 -1
- package/src/engine-components/SpectatorCamera.ts +10 -2
- package/src/engine-components/SpringJoint.ts +41 -41
- package/src/engine-components/VideoPlayer.ts +1 -2
- package/src/engine-components/WebXR.ts +12 -2
- package/src/engine-components/WebXRController.ts +16 -7
- package/src/engine-components/codegen/components.ts +1 -3
|
@@ -1,57 +1,39 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { Box3, Camera, Intersection, Layers, Matrix4, Mesh, Object3D, Quaternion, Ray, Raycaster, Sphere, Vector2, Vector3 } from 'three'
|
|
1
|
+
import { BasicDepthPacking, Box3, BufferAttribute, BufferGeometry, Camera, Intersection, Layers, LineBasicMaterial, LineSegments, Matrix4, Mesh, NormalAnimationBlendMode, Object3D, Quaternion, Ray, Raycaster, Sphere, Vector2, Vector3 } from 'three'
|
|
3
2
|
import { Context } from './engine_setup';
|
|
4
|
-
import cannonDebugger from 'cannon-es-debugger'
|
|
5
3
|
import { getParam } from "./engine_utils"
|
|
6
|
-
import { getWorldPosition, getWorldQuaternion, setWorldPositionXYZ, setWorldQuaternionXYZW } from "./engine_three_utils"
|
|
4
|
+
import { getWorldPosition, getWorldQuaternion, getWorldScale, setWorldPosition, setWorldPositionXYZ, setWorldQuaternionXYZW } from "./engine_three_utils"
|
|
7
5
|
import {
|
|
8
|
-
IComponent
|
|
9
|
-
IGameObject
|
|
6
|
+
IComponent,
|
|
7
|
+
IGameObject,
|
|
10
8
|
ICollider,
|
|
11
|
-
IRigidbody
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
9
|
+
IRigidbody,
|
|
10
|
+
Collision,
|
|
11
|
+
ICollisionContext,
|
|
12
|
+
ContactPoint,
|
|
13
|
+
Vec3
|
|
16
14
|
} from './engine_types';
|
|
17
|
-
import { Shape } from 'cannon-es';
|
|
18
15
|
import { InstancingUtil } from './engine_instancing';
|
|
19
16
|
import { foreachComponent } from './engine_gameobject';
|
|
20
17
|
|
|
18
|
+
import RAPIER, { ActiveEvents, Collider, ColliderDesc, EventQueue, RigidBody, TempContactManifold, World } from '@dimforge/rapier3d-compat';
|
|
19
|
+
import { CollisionDetectionMode, RigidbodyConstraints } from '../engine/engine_physics.types';
|
|
20
|
+
import { showBalloonWarning } from './debug/debug';
|
|
21
|
+
export type Rapier = typeof RAPIER;
|
|
22
|
+
|
|
21
23
|
|
|
22
24
|
const debugPhysics = getParam("debugphysics");
|
|
25
|
+
const debugColliderPlacement = getParam("debugphysicscolliders");
|
|
23
26
|
const debugCollisions = getParam("debugcollisions");
|
|
24
27
|
|
|
25
|
-
export class BodyOptions {
|
|
26
|
-
mass: number = 1;
|
|
27
|
-
kinematic: boolean = false;
|
|
28
|
-
physicsEvents: boolean = false;
|
|
29
|
-
drag: number = 0;
|
|
30
|
-
angularDrag: number = 0.05;
|
|
31
|
-
sleepThreshold: number = .01;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
// TODO: refactor to return some kind of handle for adding/removing
|
|
36
|
-
class PhysicsObject {
|
|
37
|
-
obj: Object3D;
|
|
38
|
-
parent: Object3D | null;
|
|
39
|
-
body: Body | null;
|
|
40
|
-
shapes: Array<Shape> = [];
|
|
41
|
-
collisonCallback: Function | null = null;
|
|
42
|
-
|
|
43
|
-
_hasRigidbody: boolean = false;
|
|
44
|
-
_didSleepLastStep: boolean = false;
|
|
45
28
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
this.body = body;
|
|
50
|
-
if (this.body)
|
|
51
|
-
this.body[$physicsKey] = obj;
|
|
52
|
-
}
|
|
29
|
+
declare type PhysicsBody = {
|
|
30
|
+
translation(): { x: number, y: number, z: number }
|
|
31
|
+
rotation(): { x: number, y: number, z: number, w: number }
|
|
53
32
|
}
|
|
54
33
|
|
|
34
|
+
const $componentKey = Symbol("needle component");
|
|
35
|
+
const $bodyKey = Symbol("physics body");
|
|
36
|
+
|
|
55
37
|
export class RaycastOptions {
|
|
56
38
|
ray: Ray | undefined = undefined;
|
|
57
39
|
cam: Camera | undefined | null = undefined;
|
|
@@ -225,484 +207,541 @@ export class Physics {
|
|
|
225
207
|
|
|
226
208
|
// physics simulation
|
|
227
209
|
|
|
228
|
-
|
|
210
|
+
private _tempPosition: Vector3 = new Vector3();
|
|
211
|
+
private _tempQuaternion: Quaternion = new Quaternion();
|
|
212
|
+
private _tempMatrix: Matrix4 = new Matrix4();
|
|
213
|
+
|
|
214
|
+
private static _didLoadPhysicsEngine: boolean = false;
|
|
229
215
|
|
|
230
216
|
private _isUpdatingPhysicsWorld: boolean = false;
|
|
217
|
+
get isUpdating(): boolean { return this._isUpdatingPhysicsWorld; }
|
|
218
|
+
|
|
231
219
|
|
|
232
220
|
private context: Context;
|
|
221
|
+
private world?: World;
|
|
222
|
+
private _hasCreatedWorld: boolean = false;
|
|
223
|
+
private eventQueue?: EventQueue;
|
|
224
|
+
private collisionHandler?: PhysicsCollisionHandler;
|
|
225
|
+
|
|
233
226
|
|
|
234
|
-
private
|
|
235
|
-
private objects:
|
|
227
|
+
// private rigidbodies: Array<IRigidbody | null> = [];
|
|
228
|
+
private objects: IComponent[] = [];
|
|
229
|
+
private bodies: PhysicsBody[] = [];
|
|
230
|
+
// private rigidbodiesLookup: Map<IRigidbody, RigidBody> = new Map<IRigidbody, RigidBody>();
|
|
231
|
+
// private kinematicColliders: Array<IComponent> = [];
|
|
232
|
+
// private rigidbodyLookup: Map<IRigidbody, IComponent[]> = new Map<IRigidbody, IComponent[]>();
|
|
233
|
+
// private objectLookup: Map<Object3D, IRigidbody> = new Map<Object3D, IRigidbody>();
|
|
236
234
|
|
|
237
|
-
private tempPosition: Vector3 = new Vector3();
|
|
238
|
-
private tempQuaternion: Quaternion = new Quaternion();
|
|
239
235
|
|
|
240
236
|
constructor(context: Context) {
|
|
241
237
|
this.context = context;
|
|
242
|
-
this.world.gravity.set(0, -9.82, 0);
|
|
243
|
-
if (debugPhysics) {
|
|
244
|
-
// https://www.npmjs.com/package/cannon-es-debugger
|
|
245
|
-
const opts = {};
|
|
246
|
-
opts["onInit"] = (_body: Body, mesh: Mesh, _shape: Shape) => {
|
|
247
|
-
// ignore in raycast
|
|
248
|
-
mesh.layers.set(-1);
|
|
249
|
-
};
|
|
250
|
-
cannonDebugger(context.scene, this.world.bodies, opts);
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
this.world.addEventListener("beginContact", this.onBeginContact.bind(this));
|
|
254
|
-
this.world.addEventListener("endContact", this.onEndContact.bind(this))
|
|
255
238
|
}
|
|
256
239
|
|
|
257
|
-
|
|
258
|
-
this.
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
this.
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
240
|
+
async createWorld() {
|
|
241
|
+
if (this._hasCreatedWorld) {
|
|
242
|
+
console.error("Invalid call to create physics world: world is already created");
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
this._hasCreatedWorld = true;
|
|
246
|
+
if (!Physics._didLoadPhysicsEngine) {
|
|
247
|
+
await RAPIER.init().then(() => RAPIER)
|
|
248
|
+
Physics._didLoadPhysicsEngine = true;
|
|
249
|
+
}
|
|
250
|
+
const gravity = { x: 0.0, y: -9.81, z: 0.0 };
|
|
251
|
+
this.world = new World(gravity);
|
|
267
252
|
}
|
|
268
253
|
|
|
269
|
-
|
|
270
|
-
|
|
254
|
+
addBoxCollider(collider: ICollider, center: Vector3, size: Vector3) {
|
|
255
|
+
const obj = collider.gameObject;
|
|
256
|
+
const scale = getWorldScale(obj, this._tempPosition).multiply(size);
|
|
257
|
+
scale.multiplyScalar(0.5);
|
|
258
|
+
const desc = ColliderDesc.cuboid(scale.x, scale.y, scale.z);
|
|
259
|
+
this.createCollider(collider, desc, center);
|
|
271
260
|
}
|
|
272
261
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
this.
|
|
276
|
-
|
|
262
|
+
addSphereCollider(collider: ICollider, center: Vector3, radius: number) {
|
|
263
|
+
const obj = collider.gameObject;
|
|
264
|
+
const scale = getWorldScale(obj, this._tempPosition).multiplyScalar(radius);
|
|
265
|
+
const desc = ColliderDesc.ball(scale.x);
|
|
266
|
+
this.createCollider(collider, desc, center);
|
|
277
267
|
}
|
|
278
268
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
if (
|
|
283
|
-
|
|
284
|
-
break;
|
|
285
|
-
}
|
|
269
|
+
addMeshCollider(collider: ICollider, mesh: Mesh, convex: boolean) {
|
|
270
|
+
const geo = mesh.geometry;
|
|
271
|
+
if (!geo) {
|
|
272
|
+
if (debugPhysics) console.warn("Missing mesh geometry", mesh.name);
|
|
273
|
+
return;
|
|
286
274
|
}
|
|
287
|
-
// dont add the body before it has shapes
|
|
288
|
-
// otherwise things like forces appplied in the frame before the shapes exist will be zeroed out
|
|
289
|
-
if (body.shapes.length > 0)
|
|
290
|
-
this.world.addBody(body);
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
public removeBody(go: GameObject, body: Body, removeCompletely: boolean = true) {
|
|
294
|
-
this.world.removeBody(body);
|
|
295
|
-
for (let i = 0; i < this.objects.length; i++) {
|
|
296
|
-
const reg = this.objects[i];
|
|
297
|
-
if (reg.obj === go) {
|
|
298
|
-
reg._hasRigidbody = false;
|
|
299
|
-
if (removeCompletely)
|
|
300
|
-
this.objects.splice(i, 1);
|
|
301
|
-
break;
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
275
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
276
|
+
let positions = geo.getAttribute("position").array as Float32Array;
|
|
277
|
+
const indices = geo.index?.array as Uint32Array;
|
|
278
|
+
|
|
279
|
+
// console.log(geo.center())
|
|
280
|
+
|
|
281
|
+
// scaling seems not supported yet https://github.com/dimforge/rapier/issues/243
|
|
282
|
+
const scale = getWorldScale(mesh, this._tempPosition)
|
|
283
|
+
if (Math.abs(scale.x - 1) > 0.0001 || Math.abs(scale.y - 1) > 0.0001 || Math.abs(scale.z - 1) > 0.0001) {
|
|
284
|
+
console.warn("Your model is using scaled mesh colliders which is not optimal for performance", mesh.name, Object.assign({}, scale), mesh);
|
|
285
|
+
// showBalloonWarning("Your model is using scaled mesh colliders which is not optimal for performance: " + mesh.name + ", consider using unscaled objects");
|
|
286
|
+
const scaledPositions = new Float32Array(positions.length);
|
|
287
|
+
for (let i = 0; i < positions.length; i += 3) {
|
|
288
|
+
scaledPositions[i] = positions[i] * scale.x;
|
|
289
|
+
scaledPositions[i + 1] = positions[i + 1] * scale.y;
|
|
290
|
+
scaledPositions[i + 2] = positions[i + 2] * scale.z;
|
|
314
291
|
}
|
|
292
|
+
positions = scaledPositions;
|
|
315
293
|
}
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
// TODO: make it work with rigibody in parent
|
|
319
|
-
public createBody(obj: Object3D, settings: BodyOptions): Body {
|
|
320
|
-
const body = this.internalCreateBody(obj, null);
|
|
321
|
-
if (settings.mass)
|
|
322
|
-
body.mass = settings.mass;
|
|
323
|
-
if (settings.kinematic)
|
|
324
|
-
body.type = Body.KINEMATIC;
|
|
325
|
-
else body.type = Body.DYNAMIC;
|
|
326
|
-
if (settings.drag)
|
|
327
|
-
body.linearDamping = settings.drag;
|
|
328
|
-
if (settings.angularDrag)
|
|
329
|
-
body.angularDamping = settings.angularDrag;
|
|
330
|
-
if (settings.sleepThreshold)
|
|
331
|
-
body.sleepSpeedLimit = settings.sleepThreshold;
|
|
332
294
|
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
this.objects.push(po);
|
|
295
|
+
const desc = convex ? ColliderDesc.convexMesh(positions) : ColliderDesc.trimesh(positions, indices);
|
|
296
|
+
if (desc) {
|
|
297
|
+
this.createCollider(collider, desc);
|
|
298
|
+
// col.setTranslationWrtParent(new Vector3(0,2,0));
|
|
338
299
|
|
|
339
|
-
if (debugPhysics) {
|
|
340
|
-
console.log("created new body", obj.name, body, body.sleepState, this.world.gravity);
|
|
341
300
|
}
|
|
342
|
-
|
|
343
|
-
if (settings.physicsEvents)
|
|
344
|
-
this.registerCollisionEvents(po);
|
|
345
|
-
|
|
346
|
-
return body;
|
|
347
301
|
}
|
|
348
302
|
|
|
349
|
-
|
|
303
|
+
private createCollider(collider: ICollider, desc: ColliderDesc, center?: Vector3) {
|
|
304
|
+
if (!this.world) throw new Error("Physics world not initialized");
|
|
305
|
+
const matrix = this._tempMatrix;
|
|
306
|
+
const {
|
|
307
|
+
rigidBody,
|
|
308
|
+
useExplicitMassProperties
|
|
309
|
+
} = this.getRigidbody(collider, this._tempMatrix);
|
|
350
310
|
|
|
351
|
-
|
|
352
|
-
|
|
311
|
+
matrix.decompose(this._tempPosition, this._tempQuaternion, new Vector3());
|
|
312
|
+
if (center)
|
|
313
|
+
this._tempPosition.add(center);
|
|
314
|
+
desc.setTranslation(this._tempPosition.x, this._tempPosition.y, this._tempPosition.z);
|
|
315
|
+
desc.setRotation(this._tempQuaternion);
|
|
353
316
|
|
|
354
|
-
|
|
355
|
-
.5 * scale.x * size.x,
|
|
356
|
-
.5 * scale.y * size.y,
|
|
357
|
-
.5 * scale.z * size.z
|
|
358
|
-
);
|
|
359
|
-
const shape = new Box(pos);
|
|
360
|
-
shape.collisionResponse = !trigger;
|
|
317
|
+
desc.setSensor(collider.isTrigger);
|
|
361
318
|
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
this.world.addBody(body);
|
|
368
|
-
if (this.isAlreadyRegistered(body)) return shape;
|
|
369
|
-
const po = new PhysicsObject(obj, body);
|
|
370
|
-
this.objects.push(po);
|
|
319
|
+
// if we want to use explicit mass properties, we need to set the collider density to 0
|
|
320
|
+
// otherwise rapier will compute the mass properties based on the collider shape and density
|
|
321
|
+
// https://rapier.rs/docs/user_guides/javascript/rigid_bodies#mass-properties
|
|
322
|
+
if (useExplicitMassProperties) {
|
|
323
|
+
// desc.setDensity(0);
|
|
371
324
|
}
|
|
372
|
-
return shape;
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
public addSphereCollider(obj: Object3D, center: Vector3, radius: number, rb: Rigidbody | null): Shape {
|
|
376
|
-
const scale = this.tempPosition;
|
|
377
|
-
obj.getWorldScale(scale);
|
|
378
325
|
|
|
379
|
-
const
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
326
|
+
const col = this.world.createCollider(desc, rigidBody);
|
|
327
|
+
col[$componentKey] = collider;
|
|
328
|
+
collider[$bodyKey] = col;
|
|
329
|
+
col.setActiveEvents(ActiveEvents.COLLISION_EVENTS);
|
|
330
|
+
this.objects.push(collider);
|
|
331
|
+
this.bodies.push(col);
|
|
332
|
+
return col;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
private getRigidbody(collider: ICollider, _matrix: Matrix4): { rigidBody: RigidBody, useExplicitMassProperties: boolean } {
|
|
336
|
+
|
|
337
|
+
if (!this.world) throw new Error("Physics world not initialized");
|
|
338
|
+
let rigidBody: RigidBody | null = null;
|
|
339
|
+
let useExplicitMassProperties = false;
|
|
340
|
+
|
|
341
|
+
if (collider.attachedRigidbody) {
|
|
342
|
+
|
|
343
|
+
const rb = collider.attachedRigidbody;
|
|
344
|
+
rigidBody = rb[$bodyKey];
|
|
345
|
+
useExplicitMassProperties = true;
|
|
346
|
+
if (!rigidBody) {
|
|
347
|
+
const kinematic = rb.isKinematic && !debugColliderPlacement;
|
|
348
|
+
if (debugPhysics)
|
|
349
|
+
console.log("Create rigidbody", kinematic);
|
|
350
|
+
const rigidBodyDesc = kinematic ? RAPIER.RigidBodyDesc.kinematicPositionBased() : RAPIER.RigidBodyDesc.dynamic();
|
|
351
|
+
const pos = getWorldPosition(collider.attachedRigidbody.gameObject);
|
|
352
|
+
rigidBodyDesc.setTranslation(pos.x, pos.y, pos.z);
|
|
353
|
+
rigidBodyDesc.setRotation(getWorldQuaternion(collider.attachedRigidbody.gameObject));
|
|
354
|
+
rigidBody = this.world.createRigidBody(rigidBodyDesc);
|
|
355
|
+
this.bodies.push(rigidBody);
|
|
356
|
+
this.objects.push(rb);
|
|
357
|
+
}
|
|
358
|
+
rigidBody[$componentKey] = rb;
|
|
359
|
+
rb[$bodyKey] = rigidBody;
|
|
360
|
+
this.internalUpdateProperties(rb, rigidBody);
|
|
361
|
+
this.getRigidbodyRelativeMatrix(collider.gameObject, rb.gameObject, _matrix);
|
|
385
362
|
|
|
386
|
-
const body = this.addShape(obj, shape, center, rb);
|
|
387
|
-
if (body !== null) {
|
|
388
|
-
this.world.addBody(body);
|
|
389
|
-
if (this.isAlreadyRegistered(body)) return shape;
|
|
390
|
-
const po = new PhysicsObject(obj, body);
|
|
391
|
-
this.objects.push(po);
|
|
392
363
|
}
|
|
393
|
-
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
public addMeshCollider(_obj: Object3D) {
|
|
397
|
-
// see https://github.com/schteppe/js/blob/master/demos/bunny.html
|
|
398
|
-
if (debugPhysics)
|
|
399
|
-
console.warn("TODO mesh collider not yet supported")
|
|
400
|
-
// const geometry: BufferGeometry = obj["geometry"];
|
|
401
|
-
// console.log(geometry);
|
|
402
|
-
// const size = geometry.boundingBox.max.clone();
|
|
403
|
-
// size.sub(geometry.boundingBox.min);
|
|
404
|
-
// console.log(size);
|
|
405
|
-
// this.addBoxCollider(obj, size);
|
|
406
|
-
|
|
407
|
-
// const verts = geometry.getAttribute("position").array;
|
|
408
|
-
// const faces = new Array<Array<number>>();
|
|
364
|
+
else {
|
|
409
365
|
|
|
410
|
-
|
|
366
|
+
const rigidBodyDesc = RAPIER.RigidBodyDesc.kinematicPositionBased();
|
|
367
|
+
const pos = getWorldPosition(collider.gameObject);
|
|
368
|
+
rigidBodyDesc.setTranslation(pos.x, pos.y, pos.z);
|
|
369
|
+
rigidBodyDesc.setRotation(getWorldQuaternion(collider.gameObject));
|
|
370
|
+
rigidBody = this.world.createRigidBody(rigidBodyDesc);
|
|
371
|
+
_matrix.identity();
|
|
372
|
+
rigidBody[$componentKey] = null;
|
|
411
373
|
|
|
412
|
-
|
|
413
|
-
// const i0 = geometry.index.array[i];
|
|
414
|
-
// const i1 = geometry.index.array[i + 1];
|
|
415
|
-
// const i2 = geometry.index.array[i + 2];
|
|
416
|
-
// const v0 = new Vector3(verts[i0 * 3], verts[i0 * 3 + 1], verts[i0 * 3 + 2]);
|
|
417
|
-
// const v1 = new Vector3(verts[i1 * 3], verts[i1 * 3 + 1], verts[i1 * 3 + 2]);
|
|
418
|
-
// const v2 = new Vector3(verts[i2 * 3], verts[i2 * 3 + 1], verts[i2 * 3 + 2]);
|
|
419
|
-
// const face = [v0, v1, v2];
|
|
420
|
-
// faces.push(face);
|
|
421
|
-
// }
|
|
422
|
-
// const convex = new ConvexBufferGeometry(faces);
|
|
374
|
+
}
|
|
423
375
|
|
|
424
|
-
|
|
425
|
-
// this.addShape(obj, shape);
|
|
376
|
+
return { rigidBody: rigidBody, useExplicitMassProperties: useExplicitMassProperties };
|
|
426
377
|
}
|
|
427
378
|
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
379
|
+
removeBody(obj: IComponent) {
|
|
380
|
+
const body = obj[$bodyKey];
|
|
381
|
+
obj[$bodyKey] = null;
|
|
382
|
+
if (body) {
|
|
383
|
+
const index = this.objects.findIndex(o => o === obj);
|
|
384
|
+
if (index >= 0) {
|
|
385
|
+
const body = this.bodies[index];
|
|
386
|
+
this.bodies.splice(index, 1);
|
|
387
|
+
this.objects.splice(index, 1);
|
|
388
|
+
|
|
389
|
+
if (body instanceof Collider) {
|
|
390
|
+
this.world?.removeCollider(body as Collider, true);
|
|
391
|
+
}
|
|
392
|
+
else if (body instanceof RigidBody) {
|
|
393
|
+
this.world?.removeRigidBody(body as RigidBody);
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
// check if we need to remove the rigidbody too
|
|
397
|
+
const col = obj as ICollider;
|
|
398
|
+
if (col.isCollider && col.attachedRigidbody) {
|
|
399
|
+
const rb = col.attachedRigidbody[$bodyKey];
|
|
400
|
+
if (rb && rb.numColliders() <= 0 && rb.world() === this.world) {
|
|
401
|
+
this.world?.removeRigidBody(rb);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
}
|
|
431
405
|
}
|
|
432
|
-
return false;
|
|
433
406
|
}
|
|
434
407
|
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
private addShape(obj: Object3D, shape: Shape, center: Vector3, rb: Rigidbody | null): Body | null {
|
|
439
|
-
|
|
440
|
-
let body: Body | null = null;
|
|
408
|
+
updateBody(comp: ICollider | IRigidbody, translation: boolean, rotation: boolean) {
|
|
409
|
+
if (comp.destroyed || !comp.gameObject) return;
|
|
410
|
+
if (!translation && !rotation) return;
|
|
441
411
|
|
|
442
|
-
if (
|
|
443
|
-
//
|
|
444
|
-
|
|
445
|
-
rb.initialize();
|
|
446
|
-
console.assert(rb.body ? true : false, "rigidbody didn't initialize / produce a physics body", rb);
|
|
447
|
-
body = rb.body;
|
|
412
|
+
if ((comp as ICollider).isCollider === true) {
|
|
413
|
+
// const collider = comp as ICollider;
|
|
414
|
+
console.warn("TODO: implement updating collider position");
|
|
448
415
|
}
|
|
449
416
|
else {
|
|
450
|
-
|
|
451
|
-
body =
|
|
452
|
-
body
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
if (body) {
|
|
456
|
-
// console.log(obj.name, obj.position, obj.rotation)
|
|
457
|
-
|
|
458
|
-
// the center is serialized from Unity so we need to move it into threejs space
|
|
459
|
-
// this should probably happen on export for colliders
|
|
460
|
-
center.x *= -1;
|
|
461
|
-
|
|
462
|
-
let wp = obj.position;
|
|
463
|
-
let wr = obj.quaternion;
|
|
464
|
-
|
|
465
|
-
// console.log(obj.name, wp)
|
|
466
|
-
|
|
467
|
-
if (rb && rb.gameObject !== obj) {
|
|
468
|
-
this.tempMat1.copy(obj.matrixWorld);
|
|
469
|
-
this.tempMat2.copy(rb.gameObject.matrixWorld).invert();
|
|
470
|
-
this.tempMat1.premultiply(this.tempMat2);
|
|
471
|
-
this.tempMat1.decompose(wp, wr, this.tempPosition);
|
|
417
|
+
const rigidbody = comp as IRigidbody;
|
|
418
|
+
const body = rigidbody[$bodyKey];
|
|
419
|
+
if (body) {
|
|
420
|
+
this.syncPhysicsBody(rigidbody.gameObject, body, translation, rotation);
|
|
472
421
|
}
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
const bp = body.position;
|
|
476
|
-
wp.x -= bp.x;
|
|
477
|
-
wp.y -= bp.y;
|
|
478
|
-
wp.z -= bp.z;
|
|
479
|
-
|
|
480
|
-
wr = getWorldQuaternion(obj);
|
|
481
|
-
const r = new Quaternion(body.quaternion.x, body.quaternion.y, body.quaternion.z, body.quaternion.w);
|
|
482
|
-
wr.multiply(r.invert());
|
|
483
|
-
}
|
|
484
|
-
// get rotation difference
|
|
485
|
-
|
|
486
|
-
wp.add(center);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
487
424
|
|
|
425
|
+
updateProperties(rigidbody: IRigidbody) {
|
|
426
|
+
const physicsBody = rigidbody[$bodyKey]
|
|
427
|
+
if (physicsBody) {
|
|
428
|
+
this.internalUpdateProperties(rigidbody, physicsBody);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
488
431
|
|
|
432
|
+
internal_getRigidbody(rb: IRigidbody): RigidBody | null {
|
|
433
|
+
return rb[$bodyKey] as RigidBody;
|
|
434
|
+
}
|
|
489
435
|
|
|
436
|
+
private internalUpdateProperties(rb: IRigidbody, rigidbody: RigidBody) {
|
|
437
|
+
// continuous collision detection
|
|
438
|
+
// https://rapier.rs/docs/user_guides/javascript/rigid_bodies#continuous-collision-detection
|
|
439
|
+
rigidbody.enableCcd(rb.collisionDetectionMode !== CollisionDetectionMode.Discrete);
|
|
440
|
+
rigidbody.setLinearDamping(rb.drag);
|
|
441
|
+
rigidbody.setAngularDamping(rb.angularDrag);
|
|
442
|
+
rigidbody.setGravityScale(rb.useGravity ? 1 : 0, true);
|
|
490
443
|
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
444
|
+
// https://rapier.rs/docs/user_guides/javascript/rigid_bodies#mass-properties
|
|
445
|
+
// rigidbody.setAdditionalMass(rb.mass, true);
|
|
446
|
+
// for (let i = 0; i < rigidbody.numColliders(); i++) {
|
|
447
|
+
// const collider = rigidbody.collider(i);
|
|
448
|
+
// if (collider) {
|
|
449
|
+
// collider.setMass(rb.mass);
|
|
450
|
+
// // const density = rb.mass / collider.shape.computeMassProperties().mass;
|
|
451
|
+
// }
|
|
452
|
+
// }
|
|
498
453
|
|
|
499
|
-
|
|
454
|
+
// lock rotations
|
|
455
|
+
rigidbody.setEnabledRotations(!rb.lockRotationX, !rb.lockRotationY, !rb.lockRotationZ, true);
|
|
456
|
+
rigidbody.setEnabledTranslations(!rb.lockPositionX, !rb.lockPositionY, !rb.lockPositionZ, true);
|
|
500
457
|
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
458
|
+
if (rb.isKinematic) {
|
|
459
|
+
rigidbody.setBodyType(RAPIER.RigidBodyType.KinematicPositionBased);
|
|
460
|
+
}
|
|
461
|
+
else {
|
|
462
|
+
rigidbody.setBodyType(RAPIER.RigidBodyType.Dynamic);
|
|
506
463
|
}
|
|
507
|
-
return body;
|
|
508
464
|
}
|
|
509
465
|
|
|
510
466
|
// private _lastStepTime: number | undefined = 0;
|
|
467
|
+
private lines?: LineSegments;
|
|
511
468
|
|
|
512
|
-
public step(
|
|
469
|
+
public step(_deltaTime?: number) {
|
|
470
|
+
if (!this.world) return;
|
|
513
471
|
this._isUpdatingPhysicsWorld = true;
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
this._isUpdatingPhysicsWorld = false;
|
|
517
|
-
if (debugPhysics && this.context.time.frameCount % 60 === 0) {
|
|
518
|
-
// console.log("physics world has " + this.world.bodies.length + " bodies", this.world);
|
|
472
|
+
if (!this.eventQueue) {
|
|
473
|
+
this.eventQueue = new EventQueue(false);
|
|
519
474
|
}
|
|
475
|
+
this.world.step(this.eventQueue);
|
|
476
|
+
this._isUpdatingPhysicsWorld = false;
|
|
477
|
+
this.updateDebugRendering(this.world);
|
|
520
478
|
}
|
|
521
479
|
|
|
522
|
-
private
|
|
523
|
-
|
|
480
|
+
private updateDebugRendering(world: World) {
|
|
481
|
+
if (debugPhysics || debugColliderPlacement) {
|
|
482
|
+
if (!this.lines) {
|
|
483
|
+
let material = new LineBasicMaterial({
|
|
484
|
+
color: 0xffffff,
|
|
485
|
+
// vertexColors: THREE.VertexColors
|
|
486
|
+
});
|
|
487
|
+
let geometry = new BufferGeometry();
|
|
488
|
+
this.lines = new LineSegments(geometry, material);
|
|
489
|
+
this.context.scene.add(this.lines);
|
|
490
|
+
}
|
|
491
|
+
const buffers = world.debugRender();
|
|
492
|
+
this.lines.geometry.setAttribute('position', new BufferAttribute(buffers.vertices, 3));
|
|
493
|
+
this.lines.geometry.setAttribute('color', new BufferAttribute(buffers.colors, 4));
|
|
494
|
+
}
|
|
495
|
+
}
|
|
524
496
|
|
|
525
497
|
public postStep() {
|
|
498
|
+
if (!this.world) return;
|
|
526
499
|
this._isUpdatingPhysicsWorld = true;
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
const body = entry.body;
|
|
530
|
-
if (!body || !body.world) continue;
|
|
531
|
-
const obj = entry.obj;
|
|
532
|
-
|
|
533
|
-
body.sleepTick(this.context.time.time);
|
|
500
|
+
this.syncObjects();
|
|
501
|
+
this._isUpdatingPhysicsWorld = false;
|
|
534
502
|
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
entry._didSleepLastStep = body.sleepState === Body.SLEEPING;
|
|
544
|
-
// if(body.sleepState === Body.SLEEPING) {
|
|
545
|
-
// console.log("SLEEP", body.name);
|
|
546
|
-
// }
|
|
547
|
-
// if (body.type == Body.KINEMATIC) continue;
|
|
548
|
-
|
|
549
|
-
if (body.type === Body.KINEMATIC) {
|
|
550
|
-
const wp = getWorldPosition(obj, this.temp);
|
|
551
|
-
body.position.set(wp.x, wp.y, wp.z);
|
|
552
|
-
const rot = getWorldQuaternion(obj, this.tempQuat);
|
|
553
|
-
body.quaternion.set(rot.x, rot.y, rot.z, rot.w);
|
|
554
|
-
continue;
|
|
555
|
-
}
|
|
503
|
+
if (this.eventQueue && !this.collisionHandler) {
|
|
504
|
+
this.collisionHandler = new PhysicsCollisionHandler(this.world, this.eventQueue);
|
|
505
|
+
}
|
|
506
|
+
if (this.collisionHandler) {
|
|
507
|
+
this.collisionHandler.handleCollisionEvents();
|
|
508
|
+
this.collisionHandler.update();
|
|
509
|
+
}
|
|
510
|
+
}
|
|
556
511
|
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
512
|
+
/** sync rendered objects with physics world (except for colliders without rigidbody) */
|
|
513
|
+
private syncObjects() {
|
|
514
|
+
if (debugColliderPlacement) return;
|
|
515
|
+
for (let i = 0; i < this.bodies.length; i++) {
|
|
516
|
+
const obj = this.objects[i];
|
|
517
|
+
const body = this.bodies[i] as Collider;
|
|
518
|
+
|
|
519
|
+
// if the collider is not attached to a rigidbody
|
|
520
|
+
// it means that its kinematic so we need to update its position
|
|
521
|
+
const col = (obj as ICollider);
|
|
522
|
+
if (col?.isCollider === true && !col.attachedRigidbody) {
|
|
523
|
+
const rigidbody = body.parent();
|
|
524
|
+
if (rigidbody)
|
|
525
|
+
this.syncPhysicsBody(obj.gameObject, rigidbody, true, true);
|
|
560
526
|
continue;
|
|
561
527
|
}
|
|
562
528
|
|
|
529
|
+
// sync
|
|
530
|
+
const pos = body.translation();
|
|
531
|
+
setWorldPositionXYZ(obj.gameObject, pos.x, pos.y, pos.z);
|
|
532
|
+
const rot = body.rotation();
|
|
533
|
+
setWorldQuaternionXYZW(obj.gameObject, rot.x, rot.y, rot.z, rot.w);
|
|
534
|
+
}
|
|
535
|
+
}
|
|
563
536
|
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
537
|
+
private syncPhysicsBody(obj: Object3D, body: RigidBody, translation: boolean, rotation: boolean) {
|
|
538
|
+
|
|
539
|
+
// const bodyType = body.bodyType();
|
|
540
|
+
// const previous = physicsBody.translation();
|
|
541
|
+
// const vel = physicsBody.linvel();
|
|
542
|
+
|
|
543
|
+
const worldPosition = getWorldPosition(obj, this._tempPosition);
|
|
544
|
+
const worldQuaternion = getWorldQuaternion(obj, this._tempQuaternion);
|
|
545
|
+
// physicsBody.setBodyType(RAPIER.RigidBodyType.Fixed);
|
|
546
|
+
if (translation)
|
|
547
|
+
body.setTranslation(worldPosition, false);
|
|
548
|
+
if (rotation)
|
|
549
|
+
body.setRotation(worldQuaternion, false);
|
|
550
|
+
// physicsBody.setLinvel(vel, false);
|
|
551
|
+
body.wakeUp();
|
|
552
|
+
|
|
553
|
+
// update velocity
|
|
554
|
+
// const pos = physicsBody.translation();
|
|
555
|
+
// pos.x -= previous.x;
|
|
556
|
+
// pos.y -= previous.y;
|
|
557
|
+
// pos.z -= previous.z;
|
|
558
|
+
// // threhold
|
|
559
|
+
// const t = 1;
|
|
560
|
+
// const canUpdateVelocity = Math.abs(pos.x) < t && Math.abs(pos.y) < t && Math.abs(pos.z) < t;
|
|
561
|
+
// if (canUpdateVelocity) {
|
|
562
|
+
// const damping = 1 + this.context.time.deltaTime;
|
|
563
|
+
// vel.x *= damping;
|
|
564
|
+
// vel.y *= damping;
|
|
565
|
+
// vel.z *= damping;
|
|
566
|
+
// vel.x += pos.x;
|
|
567
|
+
// vel.y += pos.y;
|
|
568
|
+
// vel.z += pos.z;
|
|
569
|
+
// console.log(vel);
|
|
570
|
+
// physicsBody.setLinvel(vel, true);
|
|
571
|
+
// }
|
|
572
|
+
// else if(debugPhysics) console.warn("Movement exceeded threshold, not updating velocity", pos);
|
|
570
573
|
|
|
571
|
-
|
|
572
|
-
|
|
574
|
+
// body.setBodyType(bodyType);
|
|
575
|
+
}
|
|
573
576
|
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
// this.rotatedCenter.copy(entry.center);
|
|
587
|
-
// const rot = this.tempQuaternion;
|
|
588
|
-
// rot.copy(obj.quaternion);
|
|
589
|
-
// // obj.getWorldQuaternion(this.tempQuaternion)
|
|
590
|
-
// this.rotatedCenter.applyQuaternion(rot);
|
|
591
|
-
// obj.getWorldScale(this.tempVector);
|
|
592
|
-
// this.rotatedCenter.divide(this.tempVector);
|
|
593
|
-
// obj.position.sub(this.rotatedCenter);
|
|
594
|
-
// }
|
|
577
|
+
private static _matricesBuffer: Matrix4[] = [];
|
|
578
|
+
private getRigidbodyRelativeMatrix(comp: Object3D, rigidbody: Object3D, mat: Matrix4, matrices?: Matrix4[]): Matrix4 {
|
|
579
|
+
// collect all matrices to the rigidbody and then build the rigidbody relative matrix
|
|
580
|
+
if (matrices === undefined) {
|
|
581
|
+
matrices = Physics._matricesBuffer;
|
|
582
|
+
matrices.length = 0;
|
|
583
|
+
}
|
|
584
|
+
if (comp === rigidbody) {
|
|
585
|
+
const scale = getWorldScale(comp, this._tempPosition);
|
|
586
|
+
mat.makeScale(scale.x, scale.y, scale.z);
|
|
587
|
+
for (let i = matrices.length - 1; i >= 0; i--) {
|
|
588
|
+
mat.multiply(matrices[i]);
|
|
595
589
|
}
|
|
590
|
+
return mat;
|
|
596
591
|
}
|
|
597
|
-
|
|
592
|
+
matrices.push(comp.matrix);
|
|
593
|
+
if (comp.parent) {
|
|
594
|
+
this.getRigidbodyRelativeMatrix(comp.parent, rigidbody, mat, matrices);
|
|
595
|
+
}
|
|
596
|
+
return mat;
|
|
598
597
|
}
|
|
599
598
|
|
|
600
|
-
private internalCreateBody(obj: Object3D, shape: Shape | undefined | null): Body {
|
|
601
599
|
|
|
602
|
-
|
|
603
|
-
body["_owner"] = obj;
|
|
604
|
-
body["_name"] = obj.name;
|
|
605
|
-
obj.getWorldPosition(this.tempPosition);
|
|
606
|
-
const pos = this.tempPosition;
|
|
607
|
-
body.position = new Vec3(pos.x, pos.y, pos.z);
|
|
600
|
+
}
|
|
608
601
|
|
|
609
|
-
const quat = this.tempQuaternion;
|
|
610
|
-
obj.getWorldQuaternion(quat);
|
|
611
|
-
body.quaternion = new PhysicsQuaternion(quat.x, quat.y, quat.z, quat.w);
|
|
612
602
|
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
body.updateMassProperties();
|
|
617
|
-
}
|
|
618
|
-
return body;
|
|
619
|
-
}
|
|
603
|
+
export interface IColliderProvider {
|
|
604
|
+
getCollider(obj: Object3D): ICollider;
|
|
605
|
+
}
|
|
620
606
|
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
// return entry;
|
|
626
|
-
// }
|
|
627
|
-
// return null;
|
|
628
|
-
// }
|
|
607
|
+
let colliderProvider: IColliderProvider | null = null;
|
|
608
|
+
export function registerColliderProvider(prov: IColliderProvider) {
|
|
609
|
+
colliderProvider = prov;
|
|
610
|
+
}
|
|
629
611
|
|
|
630
|
-
|
|
631
|
-
if (obj.collisonCallback) this.unregisterCollisionEvents(obj);
|
|
632
|
-
if (!obj.body) return;
|
|
633
|
-
const evt = evt => this.raiseCollisionEvents(obj.obj, evt);
|
|
634
|
-
obj.collisonCallback = evt.bind(this);
|
|
635
|
-
obj.body.addEventListener("collide", obj.collisonCallback);
|
|
636
|
-
}
|
|
612
|
+
class CollisionContext implements ICollisionContext {
|
|
637
613
|
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
if (!obj.body) return;
|
|
641
|
-
obj.body.removeEventListener("collide", obj.collisonCallback);
|
|
614
|
+
getCollider(obj: Object3D<Event>): ICollider {
|
|
615
|
+
return colliderProvider!.getCollider(obj);
|
|
642
616
|
}
|
|
643
617
|
|
|
644
|
-
|
|
645
|
-
// this is called after the object collide event so we dont really need it
|
|
646
|
-
// console.log("START");
|
|
647
|
-
}
|
|
618
|
+
}
|
|
648
619
|
|
|
649
|
-
private readonly collisionContext: ICollisionContext = new CollisionContext();
|
|
650
620
|
|
|
651
|
-
private raiseCollisionEvents(obj: Object3D, event: CannonCollision) {
|
|
652
|
-
const collision = new Collision(obj, event, this.collisionContext);
|
|
653
|
-
if (debugCollisions)
|
|
654
|
-
console.log("collision between", event.contact.bi, event.contact.bj, obj, event);
|
|
655
|
-
foreachComponent(obj, (c: Component) => {
|
|
656
|
-
c.__internalHandleCollision(collision, false);
|
|
657
|
-
});
|
|
658
621
|
|
|
659
|
-
// handle triggers
|
|
660
|
-
if (collision.collider && !collision.collider.attachedRigidbody && collision.collider.isTrigger) {
|
|
661
|
-
const collision2 = new Collision(collision.gameObject, event, this.collisionContext, true);
|
|
662
|
-
foreachComponent(collision.gameObject, (c: Component) => {
|
|
663
|
-
c.__internalHandleCollision(collision2, true);
|
|
664
|
-
});
|
|
665
|
-
}
|
|
666
|
-
}
|
|
667
622
|
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
// console.log("END", BODY_SLEEP_STATES, args.bodyB.sleepState);
|
|
671
|
-
const obj1 = args.bodyA[$physicsKey];
|
|
672
|
-
const obj2 = args.bodyB[$physicsKey];
|
|
673
|
-
// console.log(obj2);
|
|
623
|
+
/** responsible of processing collision events for the component system */
|
|
624
|
+
class PhysicsCollisionHandler {
|
|
674
625
|
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
});
|
|
626
|
+
readonly world: World;
|
|
627
|
+
readonly eventQueue: EventQueue;
|
|
678
628
|
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
629
|
+
constructor(world: World, eventQueue: EventQueue) {
|
|
630
|
+
this.world = world;
|
|
631
|
+
this.eventQueue = eventQueue;
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
private activeCollisions: Array<{ collider: ICollider, component: IComponent, collision: Collision }> = [];
|
|
635
|
+
private activeTriggers: Array<{ collider: ICollider, component: IComponent, otherCollider: ICollider }> = [];
|
|
636
|
+
|
|
637
|
+
handleCollisionEvents() {
|
|
638
|
+
if (!this.eventQueue) return;
|
|
639
|
+
if (!this.world) return;
|
|
640
|
+
this.eventQueue.drainCollisionEvents((handle1, handle2, started) => {
|
|
641
|
+
const col1 = this.world!.getCollider(handle1);
|
|
642
|
+
const col2 = this.world!.getCollider(handle2);
|
|
643
|
+
const colliderComponent1 = col1[$componentKey];
|
|
644
|
+
const colliderComponent2 = col2[$componentKey];
|
|
645
|
+
// console.log("EVT", colliderComponent1.name, colliderComponent2.name, started);
|
|
646
|
+
if (colliderComponent1 && colliderComponent2) {
|
|
647
|
+
if (started) {
|
|
648
|
+
this.onCollisionStarted(colliderComponent1, col1, colliderComponent2, col2);
|
|
649
|
+
this.onCollisionStarted(colliderComponent2, col2, colliderComponent1, col1);
|
|
650
|
+
}
|
|
651
|
+
else {
|
|
652
|
+
this.onCollisionEnded(colliderComponent1, colliderComponent2);
|
|
653
|
+
this.onCollisionEnded(colliderComponent2, colliderComponent1);
|
|
654
|
+
}
|
|
686
655
|
}
|
|
687
656
|
});
|
|
688
657
|
}
|
|
689
658
|
|
|
690
|
-
|
|
659
|
+
update() {
|
|
660
|
+
this.onHandleCollisionStay();
|
|
661
|
+
}
|
|
691
662
|
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
}
|
|
663
|
+
private onCollisionStarted(self: ICollider, selfBody: Collider, other: ICollider, otherBody: Collider) {
|
|
664
|
+
let collision: Collision | null = null;
|
|
695
665
|
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
666
|
+
// if one is a trigger we dont get collisions but want to raise the trigger events
|
|
667
|
+
if (self.isTrigger || other.isTrigger) {
|
|
668
|
+
foreachComponent(self.gameObject, (c: IComponent) => {
|
|
669
|
+
if (c.onTriggerEnter) {
|
|
670
|
+
c.onTriggerEnter(other);
|
|
671
|
+
}
|
|
672
|
+
this.activeTriggers.push({ collider: self, component: c, otherCollider: other });
|
|
673
|
+
});
|
|
674
|
+
}
|
|
675
|
+
else {
|
|
676
|
+
const object = self.gameObject;
|
|
677
|
+
// TODO: we dont respect the flip value here!
|
|
678
|
+
this.world.contactPair(selfBody, otherBody, (manifold, _flipped) => {
|
|
679
|
+
foreachComponent(object, (c: IComponent) => {
|
|
680
|
+
if (c.onCollisionEnter) {
|
|
681
|
+
if (!collision) {
|
|
682
|
+
const contacts: Array<ContactPoint> = [];
|
|
683
|
+
const normal = manifold.normal();
|
|
684
|
+
for (let i = 0; i < manifold.numContacts(); i++) {
|
|
685
|
+
const pt1 = manifold.localContactPoint1(i);
|
|
686
|
+
const dist = manifold.contactDist(i);
|
|
687
|
+
if (pt1) {
|
|
688
|
+
const contact = new ContactPoint(pt1, dist, normal);
|
|
689
|
+
contacts.push(contact);
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
collision = new Collision(object, other, contacts);
|
|
693
|
+
}
|
|
694
|
+
c.onCollisionEnter.call(c, collision);
|
|
695
|
+
this.activeCollisions.push({ collider: self, component: c, collision });
|
|
696
|
+
}
|
|
697
|
+
});
|
|
698
|
+
});
|
|
699
|
+
}
|
|
700
|
+
}
|
|
702
701
|
|
|
703
|
-
|
|
704
|
-
|
|
702
|
+
private onHandleCollisionStay() {
|
|
703
|
+
for (const active of this.activeCollisions) {
|
|
704
|
+
const c = active.component;
|
|
705
|
+
if (c.activeAndEnabled && c.onCollisionStay) {
|
|
706
|
+
const arg = active.collision;
|
|
707
|
+
c.onCollisionStay(arg);
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
for (const active of this.activeTriggers) {
|
|
711
|
+
const c = active.component;
|
|
712
|
+
if (c.activeAndEnabled && c.onTriggerStay) {
|
|
713
|
+
const arg = active.collider;
|
|
714
|
+
c.onTriggerStay(arg);
|
|
715
|
+
}
|
|
716
|
+
}
|
|
705
717
|
}
|
|
706
718
|
|
|
719
|
+
private onCollisionEnded(self: ICollider, other: ICollider) {
|
|
720
|
+
for (let i = 0; i < this.activeCollisions.length; i++) {
|
|
721
|
+
const active = this.activeCollisions[i];
|
|
722
|
+
const collider = active.collider;
|
|
723
|
+
if (collider === self && active.collision.collider === other) {
|
|
724
|
+
const c = active.component;
|
|
725
|
+
this.activeCollisions.splice(i, 1);
|
|
726
|
+
i--;
|
|
727
|
+
if (c.activeAndEnabled && c.onCollisionExit) {
|
|
728
|
+
const collision = active.collision;
|
|
729
|
+
c.onCollisionExit(collision);
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
for (let i = 0; i < this.activeTriggers.length; i++) {
|
|
734
|
+
const active = this.activeTriggers[i];
|
|
735
|
+
const collider = active.collider;
|
|
736
|
+
if (collider === self && active.otherCollider === other) {
|
|
737
|
+
const c = active.component;
|
|
738
|
+
this.activeTriggers.splice(i, 1);
|
|
739
|
+
i--;
|
|
740
|
+
if (c.activeAndEnabled && c.onTriggerExit) {
|
|
741
|
+
const collision = active.otherCollider;
|
|
742
|
+
c.onTriggerExit(collision);
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
}
|
|
707
747
|
}
|
|
708
|
-
|