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