@gg-web-engine/rapier3d 0.0.72 → 0.0.74
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/dist/components/rapier-3d-character-controller.component.d.ts +13 -8
- package/dist/components/rapier-3d-character-controller.component.js +49 -8
- package/dist/components/rapier-3d-raycast-vehicle.component.d.ts +47 -2
- package/dist/components/rapier-3d-raycast-vehicle.component.js +153 -45
- package/dist/components/rapier-3d-rigid-body.component.d.ts +23 -1
- package/dist/components/rapier-3d-rigid-body.component.js +92 -3
- package/dist/components/rapier-3d-trigger.component.d.ts +7 -6
- package/dist/components/rapier-3d-trigger.component.js +6 -0
- package/dist/components/rapier-3d-world.component.d.ts +35 -1
- package/dist/components/rapier-3d-world.component.js +59 -4
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -1
- package/dist/rapier-3d-factory.d.ts +3 -2
- package/dist/rapier-3d-factory.js +24 -5
- package/dist/types.d.ts +2 -1
- package/package.json +5 -5
- package/test/components/rapier-3d-player-character-controller-integration.spec.ts +3 -0
- package/test/components/rapier-3d-raycast-vehicle.component.spec.ts +160 -0
- package/test/components/rapier-3d-rigid-body-sleep.spec.ts +75 -0
- package/test/components/rapier-3d-rigid-body.component.spec.ts +68 -0
- package/test/components/rapier-3d-trigger-character-controller-integration.spec.ts +85 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CharacterController3dOptions, CollisionGroup, DebugBody3DSettings,
|
|
1
|
+
import { CharacterController3dOptions, CollisionGroup, DebugBody3DSettings, ICharacterController3dComponent, IEntity, Point3, Point4 } from '@gg-web-engine/core';
|
|
2
2
|
import { Collider, InteractionGroups, KinematicCharacterController, RigidBody, RigidBodyDesc } from '@dimforge/rapier3d-compat';
|
|
3
3
|
import { Rapier3dWorldComponent } from './rapier-3d-world.component';
|
|
4
4
|
import { Rapier3dRigidBodyComponent } from './rapier-3d-rigid-body.component';
|
|
@@ -22,18 +22,23 @@ import { Rapier3dGgWorld, Rapier3dPhysicsTypeDocRepo } from '../types';
|
|
|
22
22
|
* specific to this component; a normal per-frame game loop that calls `physicsWorld.simulate()`
|
|
23
23
|
* every tick already satisfies it after the first tick.
|
|
24
24
|
*
|
|
25
|
-
* Note:
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
25
|
+
* Note: this component's native body handle *is* registered in
|
|
26
|
+
* `Rapier3dWorldComponent.handleIdEntityMap` (see `addToWorld`/`removeFromWorld` below), alongside
|
|
27
|
+
* ordinary `Rapier3dRigidBodyComponent`/`Rapier3dTriggerComponent` handles - this is what lets a
|
|
28
|
+
* `Trigger3dEntity`'s `onEntityEntered`/`onEntityLeft` fire for a player walking through it, not just
|
|
29
|
+
* for ordinary rigid bodies/vehicle chassis (`Rapier3dWorldComponent.dispatchCollisionEvents` resolves
|
|
30
|
+
* a sensor-overlap pair's components through this same map). `world.raycast()` deliberately still does
|
|
31
|
+
* *not* resolve a hit against a character controller back to this component (filtered out in
|
|
32
|
+
* `raycast()` itself) - widening that too would mean widening the public `raycast()` return-type
|
|
33
|
+
* generic repo-wide for a case outside `IPhysicsWorldComponent.raycast`'s own documented contract
|
|
34
|
+
* (`PTypeDoc['rigidBody'] | PTypeDoc['trigger']`), so it's left resolving to `hitBody: undefined`
|
|
35
|
+
* there, same as before.
|
|
31
36
|
*/
|
|
32
37
|
export declare class Rapier3dCharacterControllerComponent implements ICharacterController3dComponent<Rapier3dPhysicsTypeDocRepo> {
|
|
33
38
|
protected readonly world: Rapier3dWorldComponent;
|
|
34
39
|
protected readonly options: Required<CharacterController3dOptions>;
|
|
35
40
|
protected _bodyDescr: RigidBodyDesc;
|
|
36
|
-
entity:
|
|
41
|
+
entity: IEntity | null;
|
|
37
42
|
name: string;
|
|
38
43
|
readonly radius: number;
|
|
39
44
|
readonly centersDistance: number;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { BitMask, DebugBody3DSettings, Pnt3, Qtrn, warnOnce, } from '@gg-web-engine/core';
|
|
2
|
-
import { Quaternion, RigidBodyDesc, Vector3, } from '@dimforge/rapier3d-compat';
|
|
2
|
+
import { ActiveCollisionTypes, ActiveEvents, QueryFilterFlags, Quaternion, RigidBodyDesc, Vector3, } from '@dimforge/rapier3d-compat';
|
|
3
3
|
/**
|
|
4
4
|
* A capsule-shaped kinematic character controller backed by Rapier's own `KinematicCharacterController`
|
|
5
5
|
* (`world.createCharacterController`). `move()` is made fully synchronous (see
|
|
@@ -19,12 +19,17 @@ import { Quaternion, RigidBodyDesc, Vector3, } from '@dimforge/rapier3d-compat';
|
|
|
19
19
|
* specific to this component; a normal per-frame game loop that calls `physicsWorld.simulate()`
|
|
20
20
|
* every tick already satisfies it after the first tick.
|
|
21
21
|
*
|
|
22
|
-
* Note:
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
22
|
+
* Note: this component's native body handle *is* registered in
|
|
23
|
+
* `Rapier3dWorldComponent.handleIdEntityMap` (see `addToWorld`/`removeFromWorld` below), alongside
|
|
24
|
+
* ordinary `Rapier3dRigidBodyComponent`/`Rapier3dTriggerComponent` handles - this is what lets a
|
|
25
|
+
* `Trigger3dEntity`'s `onEntityEntered`/`onEntityLeft` fire for a player walking through it, not just
|
|
26
|
+
* for ordinary rigid bodies/vehicle chassis (`Rapier3dWorldComponent.dispatchCollisionEvents` resolves
|
|
27
|
+
* a sensor-overlap pair's components through this same map). `world.raycast()` deliberately still does
|
|
28
|
+
* *not* resolve a hit against a character controller back to this component (filtered out in
|
|
29
|
+
* `raycast()` itself) - widening that too would mean widening the public `raycast()` return-type
|
|
30
|
+
* generic repo-wide for a case outside `IPhysicsWorldComponent.raycast`'s own documented contract
|
|
31
|
+
* (`PTypeDoc['rigidBody'] | PTypeDoc['trigger']`), so it's left resolving to `hitBody: undefined`
|
|
32
|
+
* there, same as before.
|
|
28
33
|
*/
|
|
29
34
|
export class Rapier3dCharacterControllerComponent {
|
|
30
35
|
get up() {
|
|
@@ -211,7 +216,18 @@ export class Rapier3dCharacterControllerComponent {
|
|
|
211
216
|
}
|
|
212
217
|
}
|
|
213
218
|
const desired = new Vector3(desiredTranslation.x, desiredTranslation.y, desiredTranslation.z);
|
|
214
|
-
this._nativeController.computeColliderMovement(this._nativeCollider, desired,
|
|
219
|
+
this._nativeController.computeColliderMovement(this._nativeCollider, desired,
|
|
220
|
+
// `computeColliderMovement`'s own default (no `filterFlags`) treats a sensor collider as a
|
|
221
|
+
// solid obstacle, exactly like any real one - confirmed empirically (a character walking
|
|
222
|
+
// straight at a `Trigger`'s volume physically stopped dead at its boundary instead of walking
|
|
223
|
+
// through it, well before Rapier's own `KINEMATIC_FIXED` intersection event ever had a chance
|
|
224
|
+
// to fire - see `Rapier3dWorldComponent.handleIdEntityMap`'s doc for that separate, now also
|
|
225
|
+
// fixed, half of this). `EXCLUDE_SENSORS` is required so a character can walk into/through a
|
|
226
|
+
// trigger's volume at all, matching what "trigger" means everywhere else in this engine (a
|
|
227
|
+
// sensor with no collision response, see `ITrigger3dComponent`) and what `AmmoCharacterControllerComponent`'s
|
|
228
|
+
// own sweep-based mover already does (Ammo's `CF_NO_CONTACT_RESPONSE` ghost trigger was never a
|
|
229
|
+
// solid obstacle to begin with).
|
|
230
|
+
QueryFilterFlags.EXCLUDE_SENSORS, undefined, this.ignoredBodiesFilterPredicate());
|
|
215
231
|
const computed = this._nativeController.computedMovement();
|
|
216
232
|
const current = this._nativeBody.translation();
|
|
217
233
|
const next = new Vector3(current.x + computed.x, current.y + computed.y, current.z + computed.z);
|
|
@@ -352,6 +368,11 @@ export class Rapier3dCharacterControllerComponent {
|
|
|
352
368
|
bd.setTranslation(pos.x, pos.y, pos.z);
|
|
353
369
|
bd.setRotation(new Quaternion(rot.x, rot.y, rot.z, rot.w));
|
|
354
370
|
const comp = new Rapier3dCharacterControllerComponent(this.world, this.options, bd);
|
|
371
|
+
// `this.options.up` is only ever read once, in the constructor - the live `up` setter (used by
|
|
372
|
+
// any caller that rotates the character after construction) never writes back to it, so it goes
|
|
373
|
+
// stale the moment `up` changes; copy the CURRENT value here instead, the same way
|
|
374
|
+
// `collisionGroups` already does below.
|
|
375
|
+
comp.up = this.up;
|
|
355
376
|
comp.collisionGroups = this.collisionGroups;
|
|
356
377
|
return comp;
|
|
357
378
|
}
|
|
@@ -366,6 +387,24 @@ export class Rapier3dCharacterControllerComponent {
|
|
|
366
387
|
radius: this.radius,
|
|
367
388
|
centersDistance: this.centersDistance,
|
|
368
389
|
})[0];
|
|
390
|
+
// Both needed for `Rapier3dWorldComponent.dispatchCollisionEvents` to ever see a pair involving
|
|
391
|
+
// this character controller (sensor overlap *or* real contact):
|
|
392
|
+
// - `ActiveCollisionTypes` gates which pairs even reach narrow-phase at all, gated by the two
|
|
393
|
+
// bodies' *rigid-body* types - Rapier's own default (`ActiveCollisionTypes.DEFAULT`) is only
|
|
394
|
+
// `DYNAMIC_DYNAMIC | DYNAMIC_FIXED | DYNAMIC_KINEMATIC`; this `kinematicPositionBased` body
|
|
395
|
+
// paired against a `static` trigger is `KINEMATIC_FIXED`, which `DEFAULT` excludes entirely -
|
|
396
|
+
// confirmed empirically (a character parked motionless inside a trigger's volume for a full
|
|
397
|
+
// second of simulated time never fired `onEntityEntered` until this was set). `.ALL` covers
|
|
398
|
+
// every other rigid-body-type combination this character could ever meet too (another
|
|
399
|
+
// kinematic character, a `Trigger` that's itself kinematic, etc.), not just this one pairing.
|
|
400
|
+
// - `ActiveEvents.COLLISION_EVENTS` is the separate, per-*collider*, "actually emit an event for
|
|
401
|
+
// an allowed pair" flag - a trigger's own sensor collider already sets it (see
|
|
402
|
+
// `Rapier3dFactory.createTrigger`) and one side being enough is normally sufficient (see
|
|
403
|
+
// `Rapier3dFactory.createRigidBody`'s doc), but this collider sets it too regardless, so a real
|
|
404
|
+
// (non-sensor) contact against another kinematic body - which would have neither side pre-set -
|
|
405
|
+
// still produces events consistently.
|
|
406
|
+
colliderDescr.setActiveCollisionTypes(ActiveCollisionTypes.ALL);
|
|
407
|
+
colliderDescr.setActiveEvents(ActiveEvents.COLLISION_EVENTS);
|
|
369
408
|
this._nativeCollider = nativeWorld.createCollider(colliderDescr, this._nativeBody);
|
|
370
409
|
this._nativeCollider.setCollisionGroups(this.collisionGroups);
|
|
371
410
|
this._nativeController = nativeWorld.createCharacterController(this.options.offset);
|
|
@@ -377,6 +416,7 @@ export class Rapier3dCharacterControllerComponent {
|
|
|
377
416
|
if (this.options.snapToGroundDistance > 0) {
|
|
378
417
|
this._nativeController.enableSnapToGround(this.options.snapToGroundDistance);
|
|
379
418
|
}
|
|
419
|
+
this.world.handleIdEntityMap.set(this._nativeBody.handle, this);
|
|
380
420
|
// Rapier's own `KinematicCharacterController` has a built-in equivalent of `pushMass`
|
|
381
421
|
// (`setApplyImpulsesToDynamicBodies(true)` + `setCharacterMass(...)`) that looked like the
|
|
382
422
|
// obvious way to implement pushing here - no hand-rolled logic needed, unlike
|
|
@@ -405,6 +445,7 @@ export class Rapier3dCharacterControllerComponent {
|
|
|
405
445
|
this._nativeController = null;
|
|
406
446
|
}
|
|
407
447
|
if (this._nativeBody) {
|
|
448
|
+
this.world.handleIdEntityMap.delete(this._nativeBody.handle);
|
|
408
449
|
if (this._nativeCollider) {
|
|
409
450
|
this.world.nativeWorld.removeCollider(this._nativeCollider, false);
|
|
410
451
|
this._nativeCollider = null;
|
|
@@ -3,26 +3,71 @@ import { DynamicRayCastVehicleController } from '@dimforge/rapier3d-compat';
|
|
|
3
3
|
import { Rapier3dRigidBodyComponent } from './rapier-3d-rigid-body.component';
|
|
4
4
|
import { Rapier3dWorldComponent } from './rapier-3d-world.component';
|
|
5
5
|
import { Rapier3dGgWorld, Rapier3dPhysicsTypeDocRepo } from '../types';
|
|
6
|
+
/**
|
|
7
|
+
* Rapier's `DynamicRayCastVehicleController` (`world.createVehicleController`) is a thin wrapper
|
|
8
|
+
* around wheel raycasting only - nothing steps it automatically as part of `World.step()`.
|
|
9
|
+
* Instead, `updateVehicle(dt, ...)` must be called once per tick *before* `world.step()` - it directly writes the chassis's own
|
|
10
|
+
* `linvel`/`angvel` from that tick's suspension/engine/brake forces, which `world.step()` then
|
|
11
|
+
* integrates like any other dynamic body's velocity. This component registers itself into
|
|
12
|
+
* `Rapier3dWorldComponent.raycastVehicles` on `addToWorld`/`removeFromWorld` so the world component
|
|
13
|
+
* can drive that call centrally from `simulate()` - see that class's doc.
|
|
14
|
+
*
|
|
15
|
+
* Like `Rapier3dCharacterControllerComponent`/`Rapier3dTriggerComponent`, this class and
|
|
16
|
+
* `Rapier3dWorldComponent` import each other (the world needs this class purely as a type for its
|
|
17
|
+
* `raycastVehicles` set, the vehicle needs the world's concrete type for its constructor/`addToWorld`
|
|
18
|
+
* parameter) - this circular import is an established, safe pattern in this package (see those two
|
|
19
|
+
* classes), not specific to this one.
|
|
20
|
+
*/
|
|
6
21
|
export declare class Rapier3dRaycastVehicleComponent extends Rapier3dRigidBodyComponent implements IRaycastVehicleComponent<Rapier3dPhysicsTypeDocRepo> {
|
|
7
22
|
protected readonly world: Rapier3dWorldComponent;
|
|
8
23
|
private chassisBody;
|
|
9
24
|
protected _nativeVehicle: DynamicRayCastVehicleController | null;
|
|
10
25
|
get nativeVehicle(): DynamicRayCastVehicleController | null;
|
|
11
|
-
private
|
|
26
|
+
private readonly wheels;
|
|
12
27
|
constructor(world: Rapier3dWorldComponent, chassisBody: Rapier3dRigidBodyComponent);
|
|
13
28
|
get wheelSpeed(): number;
|
|
14
29
|
addToWorld(world: Rapier3dGgWorld): void;
|
|
15
30
|
removeFromWorld(world: Rapier3dGgWorld, dispose?: boolean): void;
|
|
31
|
+
/**
|
|
32
|
+
* Called once per `simulate()` tick by `Rapier3dWorldComponent`, *before* `World.step()` - see
|
|
33
|
+
* this class's own doc for why. `filterGroups` threads this vehicle's own collision groups
|
|
34
|
+
* (`this.collisionGroups`, inherited from `Rapier3dRigidBodyComponent` and already packed in the
|
|
35
|
+
* `InteractionGroups` layout Rapier expects) into the wheels' own suspension raycasts, so a
|
|
36
|
+
* vehicle in one collision group doesn't get held up by suspension force from a floor it isn't
|
|
37
|
+
* meant to interact with - without this, only the chassis's own broadphase collision would
|
|
38
|
+
* respect collision groups, not the ray-cast-based wheel/ground detection (see
|
|
39
|
+
* `gg-engine-physics-adapter`'s testing guidance on this). `EXCLUDE_SENSORS` keeps a `Trigger`'s
|
|
40
|
+
* sensor volume from ever acting as solid ground for a wheel, mirroring
|
|
41
|
+
* `Rapier3dCharacterControllerComponent.move()`'s identical guard.
|
|
42
|
+
*/
|
|
43
|
+
stepVehicleController(dt: number): void;
|
|
44
|
+
private attachWheel;
|
|
16
45
|
addWheel(options: WheelOptions, suspensionOptions: SuspensionOptions): void;
|
|
17
46
|
setSteering(wheelIndex: number, steering: number): void;
|
|
18
47
|
applyEngineForce(wheelIndex: number, force: number): void;
|
|
19
48
|
applyBrake(wheelIndex: number, force: number): void;
|
|
20
49
|
isWheelTouchesGround(wheelIndex: number): boolean;
|
|
50
|
+
/**
|
|
51
|
+
* Rapier's controller exposes no single call that bakes suspension travel, steering and roll into
|
|
52
|
+
* one transform for rendering - only the individual pieces
|
|
53
|
+
* (`wheelHardPoint`/`wheelSuspensionLength`/`wheelDirectionCs`/`wheelAxleCs`/`wheelSteering`/
|
|
54
|
+
* `wheelRotation`), which this method composes by hand:
|
|
55
|
+
* - **Position**: `wheelHardPoint` is already world-space (the ray-cast's own start point, fixed
|
|
56
|
+
* relative to the chassis) - moving it `wheelSuspensionLength` further along the *world-space*
|
|
57
|
+
* suspension direction (`wheelDirectionCs` rotated by the chassis's current rotation) lands
|
|
58
|
+
* exactly on the wheel's current (compressed-by-however-much) center, airborne or grounded alike.
|
|
59
|
+
* - **Rotation**: composed as chassis rotation ∘ steering (about the chassis's local up axis,
|
|
60
|
+
* `Pnt3.Z` - only ever nonzero for wheels `RaycastVehicle3dEntity` actually steers) ∘ roll (about
|
|
61
|
+
* this wheel's own configured local axle, `wheelRotation`'s accumulated spin angle). This is a
|
|
62
|
+
* best-effort reconstruction, not something read back verbatim from the native engine - document
|
|
63
|
+
* as a known limitation rather than chasing exactness, same spirit as
|
|
64
|
+
* `Rapier3dCharacterControllerComponent`'s ground-normal approximation.
|
|
65
|
+
*/
|
|
21
66
|
getWheelTransform(wheelIndex: number): {
|
|
22
67
|
position: Point3;
|
|
23
68
|
rotation: Point4;
|
|
24
69
|
};
|
|
25
70
|
resetSuspension(): void;
|
|
26
71
|
clone(): Rapier3dRaycastVehicleComponent;
|
|
27
|
-
|
|
72
|
+
resetMotion(): void;
|
|
28
73
|
}
|
|
@@ -1,6 +1,21 @@
|
|
|
1
1
|
import { Pnt3, Qtrn, } from '@gg-web-engine/core';
|
|
2
|
-
import {
|
|
2
|
+
import { QueryFilterFlags } from '@dimforge/rapier3d-compat';
|
|
3
3
|
import { Rapier3dRigidBodyComponent } from './rapier-3d-rigid-body.component';
|
|
4
|
+
/**
|
|
5
|
+
* Rapier's `DynamicRayCastVehicleController` (`world.createVehicleController`) is a thin wrapper
|
|
6
|
+
* around wheel raycasting only - nothing steps it automatically as part of `World.step()`.
|
|
7
|
+
* Instead, `updateVehicle(dt, ...)` must be called once per tick *before* `world.step()` - it directly writes the chassis's own
|
|
8
|
+
* `linvel`/`angvel` from that tick's suspension/engine/brake forces, which `world.step()` then
|
|
9
|
+
* integrates like any other dynamic body's velocity. This component registers itself into
|
|
10
|
+
* `Rapier3dWorldComponent.raycastVehicles` on `addToWorld`/`removeFromWorld` so the world component
|
|
11
|
+
* can drive that call centrally from `simulate()` - see that class's doc.
|
|
12
|
+
*
|
|
13
|
+
* Like `Rapier3dCharacterControllerComponent`/`Rapier3dTriggerComponent`, this class and
|
|
14
|
+
* `Rapier3dWorldComponent` import each other (the world needs this class purely as a type for its
|
|
15
|
+
* `raycastVehicles` set, the vehicle needs the world's concrete type for its constructor/`addToWorld`
|
|
16
|
+
* parameter) - this circular import is an established, safe pattern in this package (see those two
|
|
17
|
+
* classes), not specific to this one.
|
|
18
|
+
*/
|
|
4
19
|
export class Rapier3dRaycastVehicleComponent extends Rapier3dRigidBodyComponent {
|
|
5
20
|
get nativeVehicle() {
|
|
6
21
|
return this._nativeVehicle;
|
|
@@ -10,86 +25,179 @@ export class Rapier3dRaycastVehicleComponent extends Rapier3dRigidBodyComponent
|
|
|
10
25
|
this.world = world;
|
|
11
26
|
this.chassisBody = chassisBody;
|
|
12
27
|
this._nativeVehicle = null;
|
|
13
|
-
this.
|
|
28
|
+
this.wheels = [];
|
|
14
29
|
}
|
|
15
30
|
get wheelSpeed() {
|
|
16
31
|
var _a;
|
|
17
|
-
return ((
|
|
32
|
+
return ((_a = this.nativeVehicle) === null || _a === void 0 ? void 0 : _a.currentVehicleSpeed()) || 0;
|
|
18
33
|
}
|
|
19
34
|
addToWorld(world) {
|
|
20
35
|
super.addToWorld(world);
|
|
21
|
-
|
|
36
|
+
const nativeWorld = this.world.nativeWorld;
|
|
37
|
+
this._nativeVehicle = nativeWorld.createVehicleController(this._nativeBody);
|
|
38
|
+
// matches this engine's own Z-up/Y-forward convention (see `RaycastVehicle3dEntity`'s own doc:
|
|
39
|
+
// "car mesh and physics body direction has to be pointing: y front, z up") - axis indices are
|
|
40
|
+
// 0 = x, 1 = y, 2 = z.
|
|
22
41
|
this._nativeVehicle.indexUpAxis = 2;
|
|
42
|
+
// this pinned `@dimforge/rapier3d-compat` build's own setter is genuinely named
|
|
43
|
+
// `setIndexForwardAxis` (a setter *property*, not a typo introduced by this adapter) - see
|
|
44
|
+
// `ray_cast_vehicle_controller.d.ts`'s `set setIndexForwardAxis(axis: number)`.
|
|
23
45
|
this._nativeVehicle.setIndexForwardAxis = 1;
|
|
24
|
-
for (const
|
|
25
|
-
this._nativeVehicle
|
|
46
|
+
for (const wheel of this.wheels) {
|
|
47
|
+
this.attachWheel(this._nativeVehicle, wheel);
|
|
26
48
|
}
|
|
27
|
-
|
|
49
|
+
this.world.raycastVehicles.add(this);
|
|
28
50
|
}
|
|
29
51
|
removeFromWorld(world, dispose) {
|
|
30
52
|
if (world.physicsWorld != this.world) {
|
|
31
53
|
throw new Error('Rapier3D bodies cannot be shared between different worlds');
|
|
32
54
|
}
|
|
33
|
-
|
|
34
|
-
|
|
55
|
+
this.world.raycastVehicles.delete(this);
|
|
56
|
+
if (this._nativeVehicle) {
|
|
57
|
+
this.world.nativeWorld.removeVehicleController(this._nativeVehicle);
|
|
58
|
+
// `removeVehicleController` only unregisters it from the world's own bookkeeping - the JS
|
|
59
|
+
// wrapper's own native handle still needs an explicit `.free()`, which nothing else ever
|
|
60
|
+
// reaches (see this class's own doc and `gg-engine-physics-adapter-rapier`). Always freed here
|
|
61
|
+
// (not gated behind `dispose`, mirroring `Rapier3dRigidBodyComponent.removeFromWorld`'s own
|
|
62
|
+
// unconditional native cleanup - a vehicle controller isn't reconstructible from a stored
|
|
63
|
+
// descriptor the way a rigid body is, so keeping a stale, removed-but-unfreed reference around
|
|
64
|
+
// for a possible future re-`addToWorld` would only leak it, never usefully resurrect it).
|
|
65
|
+
this._nativeVehicle.free();
|
|
66
|
+
this._nativeVehicle = null;
|
|
35
67
|
}
|
|
36
68
|
super.removeFromWorld(world, dispose);
|
|
37
|
-
|
|
38
|
-
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Called once per `simulate()` tick by `Rapier3dWorldComponent`, *before* `World.step()` - see
|
|
72
|
+
* this class's own doc for why. `filterGroups` threads this vehicle's own collision groups
|
|
73
|
+
* (`this.collisionGroups`, inherited from `Rapier3dRigidBodyComponent` and already packed in the
|
|
74
|
+
* `InteractionGroups` layout Rapier expects) into the wheels' own suspension raycasts, so a
|
|
75
|
+
* vehicle in one collision group doesn't get held up by suspension force from a floor it isn't
|
|
76
|
+
* meant to interact with - without this, only the chassis's own broadphase collision would
|
|
77
|
+
* respect collision groups, not the ray-cast-based wheel/ground detection (see
|
|
78
|
+
* `gg-engine-physics-adapter`'s testing guidance on this). `EXCLUDE_SENSORS` keeps a `Trigger`'s
|
|
79
|
+
* sensor volume from ever acting as solid ground for a wheel, mirroring
|
|
80
|
+
* `Rapier3dCharacterControllerComponent.move()`'s identical guard.
|
|
81
|
+
*/
|
|
82
|
+
stepVehicleController(dt) {
|
|
83
|
+
if (!this._nativeVehicle) {
|
|
84
|
+
return;
|
|
39
85
|
}
|
|
86
|
+
this._nativeVehicle.updateVehicle(dt, QueryFilterFlags.EXCLUDE_SENSORS, this.collisionGroups);
|
|
87
|
+
}
|
|
88
|
+
attachWheel(nativeVehicle, wheel) {
|
|
89
|
+
nativeVehicle.addWheel(wheel.connectionPointCs, wheel.directionCs, wheel.axleCs, wheel.restLength, wheel.radius);
|
|
90
|
+
const i = nativeVehicle.numWheels() - 1;
|
|
91
|
+
nativeVehicle.setWheelSuspensionStiffness(i, wheel.suspension.stiffness);
|
|
92
|
+
nativeVehicle.setWheelSuspensionRelaxation(i, wheel.suspension.damping);
|
|
93
|
+
nativeVehicle.setWheelSuspensionCompression(i, wheel.suspension.compression);
|
|
94
|
+
nativeVehicle.setWheelMaxSuspensionTravel(i, wheel.options.maxTravel);
|
|
95
|
+
nativeVehicle.setWheelFrictionSlip(i, wheel.options.frictionSlip);
|
|
96
|
+
// Rapier has no single field named "roll influence" - `WheelOptions.rollInfluence` (how much a
|
|
97
|
+
// wheel resists roll-inducing side force, per `IRaycastVehicleComponent`'s doc) is approximated
|
|
98
|
+
// with the closest native equivalent, side-friction stiffness. Best-effort, same spirit as
|
|
99
|
+
// `Rapier3dCharacterControllerComponent`'s documented ground-normal approximation - not
|
|
100
|
+
// guaranteed to produce an identical feel for a given numeric value across physics engines.
|
|
101
|
+
nativeVehicle.setWheelSideFrictionStiffness(i, wheel.options.rollInfluence);
|
|
40
102
|
}
|
|
41
103
|
addWheel(options, suspensionOptions) {
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
Pnt3.nZ,
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
104
|
+
const wheel = {
|
|
105
|
+
connectionPointCs: Pnt3.clone(options.position),
|
|
106
|
+
directionCs: Pnt3.nZ,
|
|
107
|
+
// One constant axle for every wheel, left or right. Confirmed empirically that flipping this
|
|
108
|
+
// per side (an earlier version of this method used `options.isLeft ? Pnt3.X : Pnt3.nX`, on the
|
|
109
|
+
// theory that it would make `getWheelTransform`'s roll rotation spin each side's mesh the
|
|
110
|
+
// visually correct way) breaks *driving* outright: Rapier's engine-force/friction model treats
|
|
111
|
+
// `axleCs` as the wheel's forward-tire-direction reference, so a flipped axle on one side
|
|
112
|
+
// applies that side's engine force in the opposite world direction from the other side - equal
|
|
113
|
+
// and opposite forward forces exactly cancel, and the chassis never moves (a real regression
|
|
114
|
+
// test - drive under engine force and assert net displacement - is what caught this; a
|
|
115
|
+
// settle-only test cannot, since it never applies engine force at all). Left/right visual
|
|
116
|
+
// mirroring of the wheel mesh is already handled adapter-agnostically at the entity level
|
|
117
|
+
// (`RaycastVehicle3dEntity`'s own `wheelLocalRotation`, derived from `WheelOptions.isLeft`), so
|
|
118
|
+
// `getWheelTransform` below doesn't need to (and must not) compensate for it again itself.
|
|
119
|
+
axleCs: Pnt3.X,
|
|
120
|
+
restLength: suspensionOptions.restLength,
|
|
121
|
+
radius: options.tyreRadius,
|
|
122
|
+
options,
|
|
123
|
+
suspension: suspensionOptions,
|
|
124
|
+
};
|
|
125
|
+
this.wheels.push(wheel);
|
|
126
|
+
if (this._nativeVehicle) {
|
|
127
|
+
this.attachWheel(this._nativeVehicle, wheel);
|
|
52
128
|
}
|
|
53
129
|
}
|
|
54
130
|
setSteering(wheelIndex, steering) {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
this.nativeVehicle.setWheelSteering(wheelIndex, steering);
|
|
131
|
+
var _a;
|
|
132
|
+
(_a = this.nativeVehicle) === null || _a === void 0 ? void 0 : _a.setWheelSteering(wheelIndex, steering);
|
|
58
133
|
}
|
|
59
134
|
applyEngineForce(wheelIndex, force) {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
this.nativeVehicle.setWheelEngineForce(wheelIndex, force);
|
|
135
|
+
var _a;
|
|
136
|
+
(_a = this.nativeVehicle) === null || _a === void 0 ? void 0 : _a.setWheelEngineForce(wheelIndex, force);
|
|
63
137
|
}
|
|
64
138
|
applyBrake(wheelIndex, force) {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
this.nativeVehicle.setWheelBrake(wheelIndex, force);
|
|
139
|
+
var _a;
|
|
140
|
+
(_a = this.nativeVehicle) === null || _a === void 0 ? void 0 : _a.setWheelBrake(wheelIndex, force);
|
|
68
141
|
}
|
|
69
142
|
isWheelTouchesGround(wheelIndex) {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
// TODO
|
|
73
|
-
return true; //this.nativeVehicle.wheelIsInContact(wheelIndex);
|
|
143
|
+
var _a, _b;
|
|
144
|
+
return (_b = (_a = this.nativeVehicle) === null || _a === void 0 ? void 0 : _a.wheelIsInContact(wheelIndex)) !== null && _b !== void 0 ? _b : false;
|
|
74
145
|
}
|
|
146
|
+
/**
|
|
147
|
+
* Rapier's controller exposes no single call that bakes suspension travel, steering and roll into
|
|
148
|
+
* one transform for rendering - only the individual pieces
|
|
149
|
+
* (`wheelHardPoint`/`wheelSuspensionLength`/`wheelDirectionCs`/`wheelAxleCs`/`wheelSteering`/
|
|
150
|
+
* `wheelRotation`), which this method composes by hand:
|
|
151
|
+
* - **Position**: `wheelHardPoint` is already world-space (the ray-cast's own start point, fixed
|
|
152
|
+
* relative to the chassis) - moving it `wheelSuspensionLength` further along the *world-space*
|
|
153
|
+
* suspension direction (`wheelDirectionCs` rotated by the chassis's current rotation) lands
|
|
154
|
+
* exactly on the wheel's current (compressed-by-however-much) center, airborne or grounded alike.
|
|
155
|
+
* - **Rotation**: composed as chassis rotation ∘ steering (about the chassis's local up axis,
|
|
156
|
+
* `Pnt3.Z` - only ever nonzero for wheels `RaycastVehicle3dEntity` actually steers) ∘ roll (about
|
|
157
|
+
* this wheel's own configured local axle, `wheelRotation`'s accumulated spin angle). This is a
|
|
158
|
+
* best-effort reconstruction, not something read back verbatim from the native engine - document
|
|
159
|
+
* as a known limitation rather than chasing exactness, same spirit as
|
|
160
|
+
* `Rapier3dCharacterControllerComponent`'s ground-normal approximation.
|
|
161
|
+
*/
|
|
75
162
|
getWheelTransform(wheelIndex) {
|
|
76
|
-
|
|
163
|
+
var _a, _b, _c;
|
|
164
|
+
if (!this.nativeVehicle) {
|
|
77
165
|
return { position: Pnt3.O, rotation: Qtrn.O };
|
|
166
|
+
}
|
|
78
167
|
const chassisRotation = this.rotation;
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
168
|
+
const hardPoint = this.nativeVehicle.wheelHardPoint(wheelIndex);
|
|
169
|
+
const directionCs = this.nativeVehicle.wheelDirectionCs(wheelIndex);
|
|
170
|
+
const suspensionLength = (_a = this.nativeVehicle.wheelSuspensionLength(wheelIndex)) !== null && _a !== void 0 ? _a : 0;
|
|
171
|
+
let position = Pnt3.O;
|
|
172
|
+
if (hardPoint && directionCs) {
|
|
173
|
+
const worldDirection = Pnt3.rot(Pnt3.clone(directionCs), chassisRotation);
|
|
174
|
+
position = Pnt3.add(Pnt3.clone(hardPoint), Pnt3.scalarMult(worldDirection, suspensionLength));
|
|
175
|
+
}
|
|
176
|
+
const axleCs = this.nativeVehicle.wheelAxleCs(wheelIndex);
|
|
177
|
+
const rollAngle = (_b = this.nativeVehicle.wheelRotation(wheelIndex)) !== null && _b !== void 0 ? _b : 0;
|
|
178
|
+
const steerAngle = (_c = this.nativeVehicle.wheelSteering(wheelIndex)) !== null && _c !== void 0 ? _c : 0;
|
|
179
|
+
const rollRotation = axleCs ? Qtrn.rotAround(Qtrn.O, Pnt3.clone(axleCs), rollAngle) : Qtrn.O;
|
|
180
|
+
const steerRotation = Qtrn.rotAround(Qtrn.O, Pnt3.Z, steerAngle);
|
|
181
|
+
const rotation = Qtrn.combineRotations(chassisRotation, steerRotation, rollRotation);
|
|
182
|
+
return { position, rotation };
|
|
83
183
|
}
|
|
84
184
|
resetSuspension() {
|
|
85
|
-
//
|
|
185
|
+
// No native equivalent: Rapier doesn't expose a settable "current suspension length" (only the
|
|
186
|
+
// rest length/travel bounds that shape it). Not load-bearing, either - the very next
|
|
187
|
+
// `stepVehicleController` tick re-derives every wheel's suspension length from a fresh ray-cast
|
|
188
|
+
// against the vehicle's (by then already reset) position, so a teleport/respawn recovers on its
|
|
189
|
+
// own within one tick without this. Kept as a documented no-op purely for interface conformance,
|
|
190
|
+
// same spirit as other best-effort gaps in this file.
|
|
86
191
|
}
|
|
87
192
|
clone() {
|
|
88
|
-
|
|
193
|
+
const comp = new Rapier3dRaycastVehicleComponent(this.world, this.chassisBody.clone());
|
|
194
|
+
for (const wheel of this.wheels) {
|
|
195
|
+
comp.addWheel(wheel.options, wheel.suspension);
|
|
196
|
+
}
|
|
197
|
+
return comp;
|
|
89
198
|
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
super.dispose();
|
|
199
|
+
resetMotion() {
|
|
200
|
+
this.resetSuspension();
|
|
201
|
+
super.resetMotion();
|
|
94
202
|
}
|
|
95
203
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Body3DOptions, CollisionEvent, CollisionGroup, DebugBody3DSettings, Entity3d, IRigidBody3dComponent, Point3, Point4, Shape3DDescriptor } from '@gg-web-engine/core';
|
|
1
|
+
import { Body3DOptions, BodyOptions, CollisionEvent, CollisionGroup, DebugBody3DSettings, Entity3d, IRigidBody3dComponent, Point3, Point4, Shape3DDescriptor } from '@gg-web-engine/core';
|
|
2
2
|
import { Collider, ColliderDesc, InteractionGroups, RigidBody, RigidBodyDesc } from '@dimforge/rapier3d-compat';
|
|
3
3
|
import { Observable, Subject } from 'rxjs';
|
|
4
4
|
import { Rapier3dWorldComponent } from './rapier-3d-world.component';
|
|
@@ -19,6 +19,25 @@ export declare class Rapier3dRigidBodyComponent implements IRigidBody3dComponent
|
|
|
19
19
|
get angularVelocity(): Point3;
|
|
20
20
|
set angularVelocity(value: Point3);
|
|
21
21
|
readonly debugBodySettings: DebugBody3DSettings;
|
|
22
|
+
/**
|
|
23
|
+
* See `IRigidBodyComponent.bodyOptions`'s own doc. Reads straight off `_bodyDescr`/
|
|
24
|
+
* `_colliderDescr`/`_colliderOptions` (also what `addToWorld` itself builds the native body/
|
|
25
|
+
* colliders from, and what `factoryProps`/`clone()` already round-trip) rather than the native
|
|
26
|
+
* body/colliders - this engine's own API never mutates any of `bodyType`/`mass`/`friction`/
|
|
27
|
+
* `restitution`/`ccd` after construction, so the stored descriptor is exactly as accurate as a
|
|
28
|
+
* native query would be.
|
|
29
|
+
*
|
|
30
|
+
* `mass` is **not** `_bodyDescr.mass` - unlike `packages/rapier2d`, `Rapier3dFactory.createRigidBodyDescr`
|
|
31
|
+
* deliberately sets mass on each collider (`ColliderDesc.setMass`), not on the body descriptor
|
|
32
|
+
* (see that method's own doc for why: a `RigidBodyDesc.mass` is "additional" point mass with no
|
|
33
|
+
* rotational inertia of its own, wrong for a body whose inertia should scale with its actual
|
|
34
|
+
* mass). `_bodyDescr.mass` is therefore always its unused default (`0`) regardless of what was
|
|
35
|
+
* actually requested - the real total is the sum of every collider's own `mass`, which
|
|
36
|
+
* `ColliderDesc` only carries meaningfully once `setMass`/`setMassProperties` was actually called
|
|
37
|
+
* on it (true for every collider of a `dynamic` body, per `createRigidBodyDescr`; a `static`/
|
|
38
|
+
* `kinematic_*` body never calls either, so this correctly sums to `0` for one of those instead).
|
|
39
|
+
*/
|
|
40
|
+
get bodyOptions(): Readonly<BodyOptions>;
|
|
22
41
|
protected _nativeBody: RigidBody | null;
|
|
23
42
|
protected _nativeBodyColliders: Collider[] | null;
|
|
24
43
|
get nativeBody(): RigidBody | null;
|
|
@@ -67,5 +86,8 @@ export declare class Rapier3dRigidBodyComponent implements IRigidBody3dComponent
|
|
|
67
86
|
addToWorld(world: Rapier3dGgWorld): void;
|
|
68
87
|
removeFromWorld(world: Rapier3dGgWorld, dispose?: boolean): void;
|
|
69
88
|
resetMotion(): void;
|
|
89
|
+
get isSleeping(): boolean;
|
|
90
|
+
wakeUp(): void;
|
|
91
|
+
sleep(): void;
|
|
70
92
|
dispose(): void;
|
|
71
93
|
}
|
|
@@ -1,6 +1,20 @@
|
|
|
1
1
|
import { BitMask, DebugBody3DSettings, Pnt3, Qtrn, } from '@gg-web-engine/core';
|
|
2
|
-
import { ColliderDesc, Quaternion, RigidBodyDesc, RigidBodyType, Vector3, } from '@dimforge/rapier3d-compat';
|
|
2
|
+
import { ColliderDesc, MassPropsMode, Quaternion, RigidBodyDesc, RigidBodyType, Vector3, } from '@dimforge/rapier3d-compat';
|
|
3
3
|
import { Subject } from 'rxjs';
|
|
4
|
+
/** Inverse of `Rapier3dFactory.createRigidBodyDescr`'s own `BodyType -> RigidBodyType` mapping -
|
|
5
|
+
* backs `Rapier3dRigidBodyComponent.bodyOptions`. */
|
|
6
|
+
function rapierBodyTypeToBodyType(status) {
|
|
7
|
+
switch (status) {
|
|
8
|
+
case RigidBodyType.Fixed:
|
|
9
|
+
return 'static';
|
|
10
|
+
case RigidBodyType.KinematicPositionBased:
|
|
11
|
+
return 'kinematic_pos';
|
|
12
|
+
case RigidBodyType.KinematicVelocityBased:
|
|
13
|
+
return 'kinematic_vel';
|
|
14
|
+
default:
|
|
15
|
+
return 'dynamic';
|
|
16
|
+
}
|
|
17
|
+
}
|
|
4
18
|
export class Rapier3dRigidBodyComponent {
|
|
5
19
|
get position() {
|
|
6
20
|
return Pnt3.clone(this.nativeBody ? this.nativeBody.translation() : this._bodyDescr.translation);
|
|
@@ -62,6 +76,35 @@ export class Rapier3dRigidBodyComponent {
|
|
|
62
76
|
this.nativeBody.setAngvel(new Vector3(value.x, value.y, value.z), true);
|
|
63
77
|
}
|
|
64
78
|
}
|
|
79
|
+
/**
|
|
80
|
+
* See `IRigidBodyComponent.bodyOptions`'s own doc. Reads straight off `_bodyDescr`/
|
|
81
|
+
* `_colliderDescr`/`_colliderOptions` (also what `addToWorld` itself builds the native body/
|
|
82
|
+
* colliders from, and what `factoryProps`/`clone()` already round-trip) rather than the native
|
|
83
|
+
* body/colliders - this engine's own API never mutates any of `bodyType`/`mass`/`friction`/
|
|
84
|
+
* `restitution`/`ccd` after construction, so the stored descriptor is exactly as accurate as a
|
|
85
|
+
* native query would be.
|
|
86
|
+
*
|
|
87
|
+
* `mass` is **not** `_bodyDescr.mass` - unlike `packages/rapier2d`, `Rapier3dFactory.createRigidBodyDescr`
|
|
88
|
+
* deliberately sets mass on each collider (`ColliderDesc.setMass`), not on the body descriptor
|
|
89
|
+
* (see that method's own doc for why: a `RigidBodyDesc.mass` is "additional" point mass with no
|
|
90
|
+
* rotational inertia of its own, wrong for a body whose inertia should scale with its actual
|
|
91
|
+
* mass). `_bodyDescr.mass` is therefore always its unused default (`0`) regardless of what was
|
|
92
|
+
* actually requested - the real total is the sum of every collider's own `mass`, which
|
|
93
|
+
* `ColliderDesc` only carries meaningfully once `setMass`/`setMassProperties` was actually called
|
|
94
|
+
* on it (true for every collider of a `dynamic` body, per `createRigidBodyDescr`; a `static`/
|
|
95
|
+
* `kinematic_*` body never calls either, so this correctly sums to `0` for one of those instead).
|
|
96
|
+
*/
|
|
97
|
+
get bodyOptions() {
|
|
98
|
+
return {
|
|
99
|
+
bodyType: rapierBodyTypeToBodyType(this._bodyDescr.status),
|
|
100
|
+
mass: this._colliderDescr.reduce((sum, cd) => sum + (cd.massPropsMode !== MassPropsMode.Density ? cd.mass : 0), 0),
|
|
101
|
+
friction: this._colliderOptions.friction,
|
|
102
|
+
restitution: this._colliderOptions.restitution,
|
|
103
|
+
ccd: this._bodyDescr.ccdEnabled,
|
|
104
|
+
ownCollisionGroups: this.ownCollisionGroups,
|
|
105
|
+
interactWithCollisionGroups: this.interactWithCollisionGroups,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
65
108
|
get nativeBody() {
|
|
66
109
|
return this._nativeBody;
|
|
67
110
|
}
|
|
@@ -100,7 +143,35 @@ export class Rapier3dRigidBodyComponent {
|
|
|
100
143
|
const d = new ColliderDesc(cd.shape);
|
|
101
144
|
d.setTranslation(cd.translation.x, cd.translation.y, cd.translation.z);
|
|
102
145
|
d.setRotation(Object.assign({}, cd.rotation));
|
|
103
|
-
|
|
146
|
+
// `cd.mass`/`centerOfMass`/`principalAngularInertia`/`angularInertiaLocalFrame` are only
|
|
147
|
+
// meaningful once something has actually switched `cd` into `MassPropsMode.MassProps` (via
|
|
148
|
+
// `setMassProperties`) or `.Mass` (via `setMass`/`ColliderDesc.mass`) - otherwise they're just
|
|
149
|
+
// a freshly-constructed `ColliderDesc`'s zeroed placeholder defaults (confirmed empirically:
|
|
150
|
+
// `mass: 0`, `principalAngularInertia: {0,0,0}`), irrelevant under the default
|
|
151
|
+
// `MassPropsMode.Density` mode, which instead derives mass *and* rotational inertia
|
|
152
|
+
// automatically from the shape and `cd.density`. Unconditionally copying those placeholders via
|
|
153
|
+
// `setMassProperties` here used to force every `factoryProps`-built collider (every `clone()`,
|
|
154
|
+
// and - since `Rapier3dRaycastVehicleComponent` builds its own body this same way - every
|
|
155
|
+
// raycast vehicle chassis) into an explicit zero-mass, zero-rotational-inertia `MassProps` mode
|
|
156
|
+
// regardless of the original's real mode, silently discarding the shape-derived inertia tensor
|
|
157
|
+
// a normal `factory.createRigidBody()` body gets for free. The practical symptom this caused:
|
|
158
|
+
// Rapier's own solver reduces "torque / (zero-plus-epsilon) angular inertia" to no angular
|
|
159
|
+
// acceleration at all, so a vehicle chassis built this way could never yaw - confirmed
|
|
160
|
+
// empirically (steering a vehicle chassis produced pure sideways-sliding translation with
|
|
161
|
+
// `angvel` staying exactly `{0,0,0}` every tick, never even a small nonzero value, which a
|
|
162
|
+
// merely-large-but-nonzero inertia tensor would still have produced). Fix: only propagate
|
|
163
|
+
// explicit mass properties when the original was actually in one of those two modes; otherwise
|
|
164
|
+
// just copy `density` and let Rapier re-derive mass/inertia from the (identical) shape, exactly
|
|
165
|
+
// like the original was computed.
|
|
166
|
+
if (cd.massPropsMode === MassPropsMode.MassProps) {
|
|
167
|
+
d.setMassProperties(cd.mass, cd.centerOfMass, cd.principalAngularInertia, cd.angularInertiaLocalFrame);
|
|
168
|
+
}
|
|
169
|
+
else if (cd.massPropsMode === MassPropsMode.Mass) {
|
|
170
|
+
d.setMass(cd.mass);
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
d.setDensity(cd.density);
|
|
174
|
+
}
|
|
104
175
|
d.setFriction(cd.friction);
|
|
105
176
|
d.setEnabled(cd.enabled);
|
|
106
177
|
d.setRestitution(cd.restitution);
|
|
@@ -128,7 +199,7 @@ export class Rapier3dRigidBodyComponent {
|
|
|
128
199
|
this.debugBodySettings = new DebugBody3DSettings(this._bodyDescr.status == RigidBodyType.Fixed
|
|
129
200
|
? { type: 'RIGID_STATIC' }
|
|
130
201
|
: this._bodyDescr.status == RigidBodyType.Dynamic
|
|
131
|
-
? { type: 'RIGID_DYNAMIC', sleeping: () =>
|
|
202
|
+
? { type: 'RIGID_DYNAMIC', sleeping: () => this.isSleeping }
|
|
132
203
|
: { type: 'RIGID_KINEMATIC' }, this.shape);
|
|
133
204
|
this._nativeBody = null;
|
|
134
205
|
this._nativeBodyColliders = null;
|
|
@@ -242,6 +313,24 @@ export class Rapier3dRigidBodyComponent {
|
|
|
242
313
|
this._nativeBody.setAngvel(new Vector3(0, 0, 0), false);
|
|
243
314
|
this._nativeBody.setLinvel(new Vector3(0, 0, 0), false);
|
|
244
315
|
}
|
|
316
|
+
get isSleeping() {
|
|
317
|
+
var _a;
|
|
318
|
+
return this._bodyDescr.status !== RigidBodyType.Fixed && !!((_a = this._nativeBody) === null || _a === void 0 ? void 0 : _a.isSleeping());
|
|
319
|
+
}
|
|
320
|
+
wakeUp() {
|
|
321
|
+
var _a;
|
|
322
|
+
if (this._bodyDescr.status === RigidBodyType.Fixed) {
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
(_a = this._nativeBody) === null || _a === void 0 ? void 0 : _a.wakeUp();
|
|
326
|
+
}
|
|
327
|
+
sleep() {
|
|
328
|
+
var _a;
|
|
329
|
+
if (this._bodyDescr.status === RigidBodyType.Fixed) {
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
(_a = this._nativeBody) === null || _a === void 0 ? void 0 : _a.sleep();
|
|
333
|
+
}
|
|
245
334
|
dispose() {
|
|
246
335
|
if (this.nativeBody) {
|
|
247
336
|
this.removeFromWorld({ physicsWorld: this.world }, true);
|