@forgeax/engine-physics-rapier3d 0.1.20 → 0.1.23
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/index.mjs +126 -57
- package/dist/index.mjs.map +1 -1
- package/dist/rapier-physics-world-3d.d.ts +1 -1
- package/dist/rapier-physics-world-3d.d.ts.map +1 -1
- package/package.json +7 -7
- package/src/__tests__/physics-rapier3d.unit.test.ts +306 -316
- package/src/rapier-physics-world-3d.ts +179 -72
- package/dist/.tsbuildinfo +0 -1
package/dist/index.mjs
CHANGED
|
@@ -1,11 +1,39 @@
|
|
|
1
1
|
import { defineSystem, FixedTime, componentDefinition, Disabled, FixedUpdate } from '@forgeax/engine-ecs';
|
|
2
|
-
import {
|
|
2
|
+
import { componentId } from '@forgeax/engine-ecs/internal';
|
|
3
|
+
import { readStructuralEvidence } from '@forgeax/engine-ecs/projection';
|
|
3
4
|
import { vec3, quat, mat4 } from '@forgeax/engine-math';
|
|
4
5
|
import { CollidingEntities, PhysicsError, PHYSICS_ERROR_HINTS, CharacterController, rigidBodyTypeFromF32, RigidBody, Collider, colliderShapeFromF32, RIGID_BODY_TYPE_STATIC, registerPhysicsComponents, PhysicsSet } from '@forgeax/engine-physics';
|
|
5
6
|
import { ChildOf } from '@forgeax/engine-scene';
|
|
6
7
|
import { PhysicsError as PhysicsError$1 } from '@forgeax/engine-types';
|
|
7
8
|
|
|
8
9
|
// src/rapier-physics-world-3d.ts
|
|
10
|
+
function createPhysicsChangeQueries(world, components) {
|
|
11
|
+
return components.map((component) => {
|
|
12
|
+
const result = world.query({ changed: [component] });
|
|
13
|
+
if (!result.ok) throw result.error;
|
|
14
|
+
return { component, query: result.value };
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
function drainPhysicsChangeQueries(queries) {
|
|
18
|
+
for (const entry of queries) {
|
|
19
|
+
for (const _span of entry.query.spans().unwrap()) {
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
function ensurePhysicsDelta(deltas, entity) {
|
|
24
|
+
let delta = deltas.get(entity);
|
|
25
|
+
if (delta === void 0) {
|
|
26
|
+
delta = {
|
|
27
|
+
transformChanged: false,
|
|
28
|
+
colliderChanged: false,
|
|
29
|
+
rigidBodyChanged: false,
|
|
30
|
+
characterControllerChanged: false,
|
|
31
|
+
characterControllerStructureChanged: false
|
|
32
|
+
};
|
|
33
|
+
deltas.set(entity, delta);
|
|
34
|
+
}
|
|
35
|
+
return delta;
|
|
36
|
+
}
|
|
9
37
|
var DEG_TO_RAD = Math.PI / 180;
|
|
10
38
|
function applyKccTuning(ctrl, cc) {
|
|
11
39
|
ctrl.setMaxSlopeClimbAngle(cc.maxSlopeClimbDeg * DEG_TO_RAD);
|
|
@@ -403,7 +431,11 @@ var RapierPhysicsWorld3D = class {
|
|
|
403
431
|
if (physicsRowIsStatic(row)) transformlessStatic.add(row.entity);
|
|
404
432
|
continue;
|
|
405
433
|
}
|
|
406
|
-
const descriptor = readPhysicsSyncDescriptor(
|
|
434
|
+
const descriptor = readPhysicsSyncDescriptor(
|
|
435
|
+
row,
|
|
436
|
+
state.transformComponent,
|
|
437
|
+
state.globalTransformComponent
|
|
438
|
+
);
|
|
407
439
|
if (descriptor !== void 0) descriptors.push(descriptor);
|
|
408
440
|
}
|
|
409
441
|
this.resetForFullReconcile(transformlessStatic);
|
|
@@ -415,6 +447,9 @@ var RapierPhysicsWorld3D = class {
|
|
|
415
447
|
descriptor.collider
|
|
416
448
|
);
|
|
417
449
|
}
|
|
450
|
+
drainPhysicsChangeQueries(state.changeQueries);
|
|
451
|
+
state.structuralCursor = readStructuralEvidence(state.world, state.structuralCursor).cursor;
|
|
452
|
+
state.structureEpoch = state.world.getStructureEpoch();
|
|
418
453
|
state.initialized = true;
|
|
419
454
|
}
|
|
420
455
|
reconcilePhysicsDelta(state, entity, delta) {
|
|
@@ -431,7 +466,11 @@ var RapierPhysicsWorld3D = class {
|
|
|
431
466
|
this.reconcileTransformlessCompatibility(entity, physicsRowIsStatic(row));
|
|
432
467
|
return;
|
|
433
468
|
}
|
|
434
|
-
const descriptor = readPhysicsSyncDescriptor(
|
|
469
|
+
const descriptor = readPhysicsSyncDescriptor(
|
|
470
|
+
row,
|
|
471
|
+
state.transformComponent,
|
|
472
|
+
state.globalTransformComponent
|
|
473
|
+
);
|
|
435
474
|
if (descriptor === void 0) {
|
|
436
475
|
this.removeEntity(entity);
|
|
437
476
|
return;
|
|
@@ -449,12 +488,14 @@ var RapierPhysicsWorld3D = class {
|
|
|
449
488
|
const finalOffset = descriptor.characterControllerOffset;
|
|
450
489
|
if (!descriptor.hasCharacterController) {
|
|
451
490
|
this.removeKccController(entity);
|
|
452
|
-
} else if (hadCachedReceipt && (
|
|
491
|
+
} else if (hadCachedReceipt && (finalOffset === void 0 || !Object.is(cachedOffset, finalOffset))) {
|
|
453
492
|
this.removeKccController(entity);
|
|
454
493
|
if (finalOffset !== void 0) this.ensureKcc(entity, finalOffset);
|
|
455
494
|
}
|
|
456
495
|
}
|
|
457
|
-
if (!delta.transformChanged && !delta.
|
|
496
|
+
if (!delta.transformChanged && !(delta.characterControllerStructureChanged && !descriptor.hasCharacterController)) {
|
|
497
|
+
return;
|
|
498
|
+
}
|
|
458
499
|
const bodyType = rigidBodyTypeFromF32(descriptor.rigidBody.type);
|
|
459
500
|
if (bodyType === "static") {
|
|
460
501
|
this.syncAuthoredPose(entity, descriptor.transform, descriptor.collider, "static");
|
|
@@ -463,29 +504,37 @@ var RapierPhysicsWorld3D = class {
|
|
|
463
504
|
}
|
|
464
505
|
}
|
|
465
506
|
/** @internal ECS system bridge; consumers should register PhysicsSyncBackend. */
|
|
466
|
-
_syncFromEcs(world, transformComponent) {
|
|
507
|
+
_syncFromEcs(world, transformComponent, globalTransformComponent = world.components.resolve("GlobalTransform")) {
|
|
467
508
|
this.assertActive("syncFromEcs");
|
|
509
|
+
if (globalTransformComponent === void 0) return;
|
|
468
510
|
let state = this.syncState;
|
|
469
|
-
if (state === void 0 || state.world !== world || state.transformComponent !== transformComponent) {
|
|
511
|
+
if (state === void 0 || state.world !== world || state.transformComponent !== transformComponent || state.globalTransformComponent !== globalTransformComponent) {
|
|
470
512
|
const queryResult = world.query({
|
|
471
513
|
read: [Collider],
|
|
472
|
-
optional: [
|
|
514
|
+
optional: [
|
|
515
|
+
transformComponent,
|
|
516
|
+
globalTransformComponent,
|
|
517
|
+
RigidBody,
|
|
518
|
+
CharacterController,
|
|
519
|
+
ChildOf
|
|
520
|
+
]
|
|
473
521
|
});
|
|
474
522
|
if (!queryResult.ok) return;
|
|
475
523
|
state = {
|
|
476
524
|
world,
|
|
477
525
|
transformComponent,
|
|
526
|
+
globalTransformComponent,
|
|
478
527
|
query: queryResult.value,
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
528
|
+
changeQueries: createPhysicsChangeQueries(world, [
|
|
529
|
+
transformComponent,
|
|
530
|
+
globalTransformComponent,
|
|
531
|
+
Collider,
|
|
532
|
+
RigidBody,
|
|
533
|
+
CharacterController,
|
|
534
|
+
ChildOf
|
|
535
|
+
]),
|
|
536
|
+
structuralCursor: readStructuralEvidence(world, 0).cursor,
|
|
537
|
+
structureEpoch: world.getStructureEpoch(),
|
|
489
538
|
initialized: false
|
|
490
539
|
};
|
|
491
540
|
this.syncState = state;
|
|
@@ -494,46 +543,64 @@ var RapierPhysicsWorld3D = class {
|
|
|
494
543
|
this.fullReconcilePhysicsState(state);
|
|
495
544
|
return;
|
|
496
545
|
}
|
|
497
|
-
const evidence = state.projection.poll();
|
|
498
|
-
if (evidence.status === "rebuild") {
|
|
499
|
-
this.fullReconcilePhysicsState(state);
|
|
500
|
-
return;
|
|
501
|
-
}
|
|
502
|
-
if (evidence.changes.length === 0) return;
|
|
503
546
|
const deltas = /* @__PURE__ */ new Map();
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
colliderChanged: false,
|
|
510
|
-
rigidBodyChanged: false,
|
|
511
|
-
characterControllerChanged: false,
|
|
512
|
-
characterControllerRemoved: false
|
|
513
|
-
};
|
|
514
|
-
deltas.set(change.entity, delta);
|
|
515
|
-
}
|
|
516
|
-
if (change.kind === "entity-removed") continue;
|
|
517
|
-
if (change.component === void 0) {
|
|
547
|
+
if (state.structureEpoch !== world.getStructureEpoch()) {
|
|
548
|
+
const structural = readStructuralEvidence(world, state.structuralCursor);
|
|
549
|
+
state.structuralCursor = structural.cursor;
|
|
550
|
+
state.structureEpoch = world.getStructureEpoch();
|
|
551
|
+
if (structural.status === "overflow") {
|
|
518
552
|
this.fullReconcilePhysicsState(state);
|
|
519
553
|
return;
|
|
520
554
|
}
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
555
|
+
const transformId = componentId(transformComponent);
|
|
556
|
+
const globalTransformId = componentId(globalTransformComponent);
|
|
557
|
+
const colliderId = componentId(Collider);
|
|
558
|
+
const rigidBodyId = componentId(RigidBody);
|
|
559
|
+
const characterControllerId = componentId(CharacterController);
|
|
560
|
+
const childOfId = componentId(ChildOf);
|
|
561
|
+
const disabledId = componentId(Disabled);
|
|
562
|
+
for (const evidence of structural.events) {
|
|
563
|
+
const id = evidence.componentId;
|
|
564
|
+
if (evidence.kind !== "spawn" && evidence.kind !== "despawn" && id !== transformId && id !== globalTransformId && id !== colliderId && id !== rigidBodyId && id !== characterControllerId && id !== childOfId && id !== disabledId) {
|
|
565
|
+
continue;
|
|
566
|
+
}
|
|
567
|
+
const delta = ensurePhysicsDelta(deltas, evidence.entity);
|
|
568
|
+
if (evidence.kind === "spawn" || evidence.kind === "despawn") {
|
|
569
|
+
delta.transformChanged = true;
|
|
570
|
+
delta.colliderChanged = true;
|
|
571
|
+
delta.rigidBodyChanged = true;
|
|
572
|
+
delta.characterControllerChanged = true;
|
|
573
|
+
delta.characterControllerStructureChanged = true;
|
|
574
|
+
} else if (id === transformId || id === globalTransformId || id === childOfId) {
|
|
575
|
+
delta.transformChanged = true;
|
|
576
|
+
} else if (id === colliderId || id === disabledId) {
|
|
577
|
+
delta.colliderChanged = true;
|
|
578
|
+
} else if (id === rigidBodyId) {
|
|
579
|
+
delta.rigidBodyChanged = true;
|
|
580
|
+
} else if (id === characterControllerId) {
|
|
581
|
+
delta.characterControllerChanged = true;
|
|
582
|
+
delta.characterControllerStructureChanged = true;
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
for (const entry of state.changeQueries) {
|
|
587
|
+
for (const span of entry.query.spans().unwrap()) {
|
|
588
|
+
for (const rawEntity of span.entities) {
|
|
589
|
+
const entity = rawEntity;
|
|
590
|
+
const delta = ensurePhysicsDelta(deltas, entity);
|
|
591
|
+
if (entry.component === transformComponent || entry.component === globalTransformComponent || entry.component === ChildOf) {
|
|
592
|
+
delta.transformChanged = true;
|
|
593
|
+
} else if (entry.component === Collider) {
|
|
594
|
+
delta.colliderChanged = true;
|
|
595
|
+
} else if (entry.component === RigidBody) {
|
|
596
|
+
delta.rigidBodyChanged = true;
|
|
597
|
+
} else if (entry.component === CharacterController) {
|
|
598
|
+
delta.characterControllerChanged = true;
|
|
599
|
+
}
|
|
600
|
+
}
|
|
535
601
|
}
|
|
536
602
|
}
|
|
603
|
+
if (deltas.size === 0) return;
|
|
537
604
|
for (const [entity, delta] of deltas) this.reconcilePhysicsDelta(state, entity, delta);
|
|
538
605
|
}
|
|
539
606
|
/**
|
|
@@ -763,13 +830,14 @@ function physicsRowIsStatic(row) {
|
|
|
763
830
|
const rigidBody = row.get(RigidBody);
|
|
764
831
|
return rigidBody !== void 0 && rigidBodyTypeFromF32(rigidBody.type) === "static";
|
|
765
832
|
}
|
|
766
|
-
function readPhysicsSyncDescriptor(row, transformComponent) {
|
|
833
|
+
function readPhysicsSyncDescriptor(row, transformComponent, globalTransformComponent) {
|
|
767
834
|
const transformData = row.get(transformComponent);
|
|
835
|
+
const globalTransformData = row.get(globalTransformComponent);
|
|
768
836
|
const colliderData = row.get(Collider);
|
|
769
837
|
if (transformData === void 0 || colliderData === void 0) return void 0;
|
|
770
|
-
const useWorldPose = row.has(ChildOf) && hasReadableWorldPose(
|
|
838
|
+
const useWorldPose = row.has(ChildOf) && hasReadableWorldPose(globalTransformData?.world);
|
|
771
839
|
if (useWorldPose) {
|
|
772
|
-
poseScratchWorld.set(
|
|
840
|
+
poseScratchWorld.set(globalTransformData.world);
|
|
773
841
|
mat4.decompose(poseScratchPosition, poseScratchRotation, poseScratchScale, poseScratchWorld);
|
|
774
842
|
} else {
|
|
775
843
|
poseScratchPosition[0] = transformData.pos[0] ?? 0;
|
|
@@ -853,7 +921,8 @@ var PhysicsSyncBackend = defineSystem({
|
|
|
853
921
|
after: ["propagateTransformsFixed"],
|
|
854
922
|
fn: (world) => {
|
|
855
923
|
const transformComponent = resolveTransform(world);
|
|
856
|
-
|
|
924
|
+
const globalTransformComponent = world.components.resolve("GlobalTransform");
|
|
925
|
+
if (transformComponent === void 0 || globalTransformComponent === void 0) return;
|
|
857
926
|
let pw;
|
|
858
927
|
try {
|
|
859
928
|
pw = world.getResource("PhysicsWorld");
|
|
@@ -861,7 +930,7 @@ var PhysicsSyncBackend = defineSystem({
|
|
|
861
930
|
return;
|
|
862
931
|
}
|
|
863
932
|
pw.applyPendingTeleports();
|
|
864
|
-
pw._syncFromEcs(world, transformComponent);
|
|
933
|
+
pw._syncFromEcs(world, transformComponent, globalTransformComponent);
|
|
865
934
|
}
|
|
866
935
|
});
|
|
867
936
|
var PhysicsStepSimulation = defineSystem({
|