@jgengine/core 0.7.0 → 0.9.0
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 +63 -0
- package/README.md +2 -0
- package/dist/ai/flock.d.ts +36 -0
- package/dist/ai/flock.js +182 -0
- package/dist/ai/groupAssist.d.ts +19 -0
- package/dist/ai/groupAssist.js +59 -0
- package/dist/ai/laneSelect.d.ts +21 -0
- package/dist/ai/laneSelect.js +37 -0
- package/dist/ai/mobBrain.d.ts +68 -0
- package/dist/ai/mobBrain.js +115 -0
- package/dist/ai/spawnDirector.d.ts +5 -0
- package/dist/ai/spawnDirector.js +14 -2
- package/dist/ai/threat.d.ts +2 -0
- package/dist/ai/threat.js +34 -0
- package/dist/anim/easing.d.ts +14 -0
- package/dist/anim/easing.js +55 -0
- package/dist/behaviour/behaviour.d.ts +136 -0
- package/dist/behaviour/behaviour.js +388 -0
- package/dist/cards/cardPile.d.ts +1 -0
- package/dist/cards/cardPile.js +12 -3
- package/dist/cartridge/runtime.d.ts +15 -0
- package/dist/cartridge/runtime.js +556 -0
- package/dist/cartridge/spec.d.ts +188 -0
- package/dist/cartridge/spec.js +19 -0
- package/dist/cartridge/validate.d.ts +2 -0
- package/dist/cartridge/validate.js +78 -0
- package/dist/combat/abilityKit.d.ts +22 -2
- package/dist/combat/abilityKit.js +72 -13
- package/dist/combat/castRunner.d.ts +40 -0
- package/dist/combat/castRunner.js +56 -0
- package/dist/combat/comboPoints.d.ts +14 -0
- package/dist/combat/comboPoints.js +52 -0
- package/dist/combat/defensiveWindow.d.ts +1 -1
- package/dist/combat/defensiveWindow.js +5 -3
- package/dist/combat/effects.d.ts +1 -1
- package/dist/combat/effects.js +9 -6
- package/dist/combat/projectiles.d.ts +64 -6
- package/dist/combat/projectiles.js +232 -74
- package/dist/combat/resistance.d.ts +16 -4
- package/dist/combat/resistance.js +43 -7
- package/dist/combat/resourcePool.d.ts +20 -0
- package/dist/combat/resourcePool.js +55 -0
- package/dist/combat/shotOrigin.d.ts +40 -0
- package/dist/combat/shotOrigin.js +57 -0
- package/dist/combat/telegraph.d.ts +30 -1
- package/dist/combat/telegraph.js +61 -0
- package/dist/commands/commandRegistry.d.ts +1 -1
- package/dist/commands/commandRegistry.js +2 -1
- package/dist/crafting/recipe.js +4 -1
- package/dist/data/dataSource.d.ts +26 -0
- package/dist/data/dataSource.js +93 -0
- package/dist/data/devProxy.d.ts +11 -0
- package/dist/data/devProxy.js +38 -0
- package/dist/data/fetchJson.d.ts +20 -0
- package/dist/data/fetchJson.js +43 -0
- package/dist/data/jsonDataSource.d.ts +4 -0
- package/dist/data/jsonDataSource.js +6 -0
- package/dist/devtools/devtools.d.ts +205 -0
- package/dist/devtools/devtools.js +832 -0
- package/dist/devtools/transformTunables.d.ts +14 -0
- package/dist/devtools/transformTunables.js +38 -0
- package/dist/devtools/tunableSchema.d.ts +140 -0
- package/dist/devtools/tunableSchema.js +476 -0
- package/dist/faction/factions.d.ts +33 -0
- package/dist/faction/factions.js +76 -0
- package/dist/faction/reputation.d.ts +32 -0
- package/dist/faction/reputation.js +82 -0
- package/dist/game/chat.d.ts +68 -0
- package/dist/game/chat.js +173 -0
- package/dist/game/chatFilter.d.ts +15 -0
- package/dist/game/chatFilter.js +55 -0
- package/dist/game/controlGate.d.ts +4 -0
- package/dist/game/controlGate.js +7 -0
- package/dist/game/defineGame.d.ts +10 -2
- package/dist/game/defineGame.js +2 -1
- package/dist/game/events.d.ts +35 -0
- package/dist/game/gamePhase.d.ts +20 -0
- package/dist/game/gamePhase.js +21 -0
- package/dist/game/levelSequence.d.ts +44 -0
- package/dist/game/levelSequence.js +78 -0
- package/dist/game/lootTable.d.ts +10 -1
- package/dist/game/lootTable.js +27 -7
- package/dist/game/playableGame.d.ts +286 -6
- package/dist/game/playableGame.js +6 -1
- package/dist/game/progression.d.ts +1 -0
- package/dist/game/progression.js +23 -2
- package/dist/game/quest.d.ts +7 -3
- package/dist/game/quest.js +17 -8
- package/dist/game/race.d.ts +44 -1
- package/dist/game/race.js +116 -11
- package/dist/game/recordBook.d.ts +29 -0
- package/dist/game/recordBook.js +86 -0
- package/dist/game/social.d.ts +42 -0
- package/dist/game/social.js +122 -0
- package/dist/game/spawnPoints.d.ts +19 -0
- package/dist/game/spawnPoints.js +23 -0
- package/dist/game/talents.d.ts +47 -0
- package/dist/game/talents.js +140 -0
- package/dist/game/worldItem.d.ts +3 -0
- package/dist/game/worldItem.js +12 -6
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/input/actionBindings.d.ts +10 -0
- package/dist/input/actionBindings.js +14 -0
- package/dist/input/axisInput.d.ts +26 -3
- package/dist/input/axisInput.js +44 -4
- package/dist/input/bindingOverrides.d.ts +25 -0
- package/dist/input/bindingOverrides.js +95 -0
- package/dist/input/gestureSurface.d.ts +31 -0
- package/dist/input/gestureSurface.js +120 -0
- package/dist/input/pointer.d.ts +45 -0
- package/dist/input/pointer.js +48 -0
- package/dist/input/pointerAxis.d.ts +30 -0
- package/dist/input/pointerAxis.js +31 -0
- package/dist/input/touchScheme.d.ts +89 -0
- package/dist/input/touchScheme.js +153 -0
- package/dist/item/affix.d.ts +1 -1
- package/dist/item/affix.js +1 -19
- package/dist/meta/changelog.d.ts +1 -1
- package/dist/meta/changelog.js +59 -1
- package/dist/movement/dash.d.ts +3 -0
- package/dist/movement/dash.js +19 -5
- package/dist/movement/glideModel.d.ts +44 -0
- package/dist/movement/glideModel.js +54 -0
- package/dist/movement/grappleSwing.d.ts +34 -0
- package/dist/movement/grappleSwing.js +56 -0
- package/dist/movement/leaderTrail.d.ts +29 -0
- package/dist/movement/leaderTrail.js +67 -0
- package/dist/movement/movementModel.d.ts +36 -3
- package/dist/movement/movementModel.js +81 -5
- package/dist/movement/steering.d.ts +27 -0
- package/dist/movement/steering.js +40 -0
- package/dist/movement/voxelController.d.ts +61 -0
- package/dist/movement/voxelController.js +229 -0
- package/dist/multiplayer/chatContract.d.ts +42 -0
- package/dist/multiplayer/chatContract.js +57 -0
- package/dist/multiplayer/feedWriteGate.d.ts +10 -0
- package/dist/multiplayer/feedWriteGate.js +12 -0
- package/dist/multiplayer/identity.d.ts +13 -0
- package/dist/multiplayer/identity.js +20 -0
- package/dist/multiplayer/poseSyncGate.d.ts +1 -0
- package/dist/multiplayer/poseSyncGate.js +13 -1
- package/dist/multiplayer/presenceModel.d.ts +17 -2
- package/dist/multiplayer/presenceModel.js +30 -4
- package/dist/multiplayer/voiceContract.d.ts +44 -0
- package/dist/multiplayer/voiceContract.js +108 -0
- package/dist/nav/corridors.d.ts +30 -0
- package/dist/nav/corridors.js +69 -0
- package/dist/nav/navConstrain.d.ts +14 -0
- package/dist/nav/navConstrain.js +26 -0
- package/dist/nav/navFromEnvironment.d.ts +11 -0
- package/dist/nav/navFromEnvironment.js +16 -0
- package/dist/nav/navGrid.d.ts +10 -0
- package/dist/nav/navGrid.js +48 -5
- package/dist/nav/pathFollow.d.ts +4 -2
- package/dist/nav/pathFollow.js +5 -3
- package/dist/nav/railGraph.d.ts +66 -0
- package/dist/nav/railGraph.js +129 -0
- package/dist/nav/timetable.d.ts +41 -0
- package/dist/nav/timetable.js +94 -0
- package/dist/physics/ballisticSweep.d.ts +18 -0
- package/dist/physics/ballisticSweep.js +49 -0
- package/dist/physics/buoyancy.d.ts +6 -0
- package/dist/physics/buoyancy.js +14 -0
- package/dist/physics/flowTube.d.ts +32 -0
- package/dist/physics/flowTube.js +64 -0
- package/dist/physics/forceVolume.d.ts +27 -0
- package/dist/physics/forceVolume.js +43 -5
- package/dist/physics/kinematicVehicle.d.ts +52 -0
- package/dist/physics/kinematicVehicle.js +81 -0
- package/dist/physics/physicsWorld.d.ts +54 -8
- package/dist/physics/physicsWorld.js +400 -79
- package/dist/physics/ragdoll.js +1 -1
- package/dist/physics/spatialGrid.d.ts +1 -1
- package/dist/physics/spatialGrid.js +5 -0
- package/dist/physics/vehicleBody.d.ts +6 -0
- package/dist/physics/vehicleBody.js +18 -18
- package/dist/puzzle/cellGrid.d.ts +26 -0
- package/dist/puzzle/cellGrid.js +110 -0
- package/dist/puzzle/fallingPiece.d.ts +24 -0
- package/dist/puzzle/fallingPiece.js +51 -0
- package/dist/random/nameGen.d.ts +18 -0
- package/dist/random/nameGen.js +35 -0
- package/dist/random/rng.d.ts +2 -0
- package/dist/random/rng.js +23 -0
- package/dist/random/seedLink.d.ts +5 -0
- package/dist/random/seedLink.js +35 -0
- package/dist/runtime/adapter.d.ts +35 -0
- package/dist/runtime/adapter.js +37 -1
- package/dist/runtime/cameraDirector.d.ts +15 -0
- package/dist/runtime/cameraDirector.js +31 -0
- package/dist/runtime/commandRunner.d.ts +2 -2
- package/dist/runtime/commandRunner.js +2 -2
- package/dist/runtime/gameContext.d.ts +65 -2
- package/dist/runtime/gameContext.js +280 -9
- package/dist/runtime/gameRuntime.d.ts +5 -1
- package/dist/runtime/gameRuntime.js +13 -12
- package/dist/runtime/hostPersistence.d.ts +30 -1
- package/dist/runtime/hostPersistence.js +70 -5
- package/dist/runtime/inputSnapshot.d.ts +12 -0
- package/dist/runtime/inputSnapshot.js +17 -0
- package/dist/runtime/motionIntents.d.ts +33 -0
- package/dist/runtime/motionIntents.js +43 -0
- package/dist/runtime/snapshot.d.ts +1 -0
- package/dist/runtime/snapshot.js +1 -1
- package/dist/runtime/transport.d.ts +32 -0
- package/dist/runtime/visibility.d.ts +74 -0
- package/dist/runtime/visibility.js +127 -0
- package/dist/scene/colliders.d.ts +44 -0
- package/dist/scene/colliders.js +77 -0
- package/dist/scene/entityStore.d.ts +30 -5
- package/dist/scene/entityStore.js +83 -13
- package/dist/scene/movementSpeed.d.ts +35 -0
- package/dist/scene/movementSpeed.js +20 -0
- package/dist/scene/objectQuery.d.ts +29 -0
- package/dist/scene/objectQuery.js +140 -0
- package/dist/scene/objectStore.d.ts +13 -0
- package/dist/scene/objectStore.js +130 -6
- package/dist/scene/paintLayer.d.ts +15 -0
- package/dist/scene/paintLayer.js +53 -0
- package/dist/scene/sceneRaycast.d.ts +80 -0
- package/dist/scene/sceneRaycast.js +284 -0
- package/dist/scene/spatial.d.ts +17 -1
- package/dist/scene/spatial.js +140 -17
- package/dist/sensor/concealment.d.ts +21 -0
- package/dist/sensor/concealment.js +63 -0
- package/dist/sensor/freezeMonitor.d.ts +17 -0
- package/dist/sensor/freezeMonitor.js +35 -0
- package/dist/sensor/recordingBuffer.d.ts +6 -0
- package/dist/sensor/recordingBuffer.js +7 -0
- package/dist/sensor/replayLoop.d.ts +39 -0
- package/dist/sensor/replayLoop.js +64 -0
- package/dist/sensor/visionCone.d.ts +31 -0
- package/dist/sensor/visionCone.js +72 -0
- package/dist/session/extraction.d.ts +2 -2
- package/dist/session/extraction.js +13 -2
- package/dist/session/roles.d.ts +6 -0
- package/dist/session/roles.js +32 -0
- package/dist/session/roundState.d.ts +41 -17
- package/dist/session/roundState.js +67 -38
- package/dist/settings/settingsModel.d.ts +97 -0
- package/dist/settings/settingsModel.js +100 -0
- package/dist/stats/statModifiers.d.ts +5 -2
- package/dist/stats/statModifiers.js +32 -14
- package/dist/store/observableKeyedStore.js +8 -2
- package/dist/survival/decayMeter.d.ts +1 -1
- package/dist/survival/decayMeter.js +0 -1
- package/dist/tactics/fallingGrid.d.ts +62 -0
- package/dist/tactics/fallingGrid.js +158 -0
- package/dist/tactics/tacticalGrid.d.ts +11 -0
- package/dist/tactics/tacticalGrid.js +17 -0
- package/dist/time/beatClock.d.ts +18 -0
- package/dist/time/beatClock.js +24 -0
- package/dist/time/idleProgress.d.ts +29 -0
- package/dist/time/idleProgress.js +31 -0
- package/dist/time/serverTick.d.ts +7 -1
- package/dist/time/serverTick.js +27 -4
- package/dist/time/simClock.d.ts +25 -1
- package/dist/time/simClock.js +28 -3
- package/dist/time/stateSchedule.d.ts +57 -0
- package/dist/time/stateSchedule.js +121 -0
- package/dist/turn/commit.d.ts +2 -2
- package/dist/turn/commit.js +2 -2
- package/dist/turn/intent.d.ts +14 -0
- package/dist/turn/intent.js +29 -0
- package/dist/turn/turnLoop.d.ts +2 -0
- package/dist/turn/turnLoop.js +8 -1
- package/dist/ui/hudLayout.d.ts +58 -0
- package/dist/ui/hudLayout.js +207 -0
- package/dist/ui/hudScale.d.ts +44 -0
- package/dist/ui/hudScale.js +55 -0
- package/dist/visibility/assetStreaming.d.ts +66 -0
- package/dist/visibility/assetStreaming.js +201 -0
- package/dist/visibility/bounds.d.ts +66 -0
- package/dist/visibility/bounds.js +113 -0
- package/dist/visibility/camera.d.ts +21 -0
- package/dist/visibility/camera.js +3 -0
- package/dist/visibility/config.d.ts +21 -0
- package/dist/visibility/config.js +1 -0
- package/dist/visibility/diagnostics.d.ts +26 -0
- package/dist/visibility/diagnostics.js +36 -0
- package/dist/visibility/distance.d.ts +11 -0
- package/dist/visibility/distance.js +30 -0
- package/dist/visibility/frustum.d.ts +47 -0
- package/dist/visibility/frustum.js +186 -0
- package/dist/visibility/occlusion.d.ts +36 -0
- package/dist/visibility/occlusion.js +45 -0
- package/dist/visibility/settings.d.ts +75 -0
- package/dist/visibility/settings.js +49 -0
- package/dist/visibility/simulationCulling.d.ts +34 -0
- package/dist/visibility/simulationCulling.js +36 -0
- package/dist/visibility/spatialIndex.d.ts +45 -0
- package/dist/visibility/spatialIndex.js +198 -0
- package/dist/visibility/visibilitySystem.d.ts +90 -0
- package/dist/visibility/visibilitySystem.js +257 -0
- package/dist/world/buildings.d.ts +6 -0
- package/dist/world/buildings.js +152 -0
- package/dist/world/carve.d.ts +7 -2
- package/dist/world/carve.js +17 -7
- package/dist/world/cellStates.d.ts +45 -0
- package/dist/world/cellStates.js +103 -0
- package/dist/world/environmentSummary.d.ts +61 -0
- package/dist/world/environmentSummary.js +138 -0
- package/dist/world/features.d.ts +138 -9
- package/dist/world/features.js +73 -2
- package/dist/world/gridInstances.d.ts +12 -0
- package/dist/world/gridInstances.js +160 -0
- package/dist/world/lod.d.ts +17 -0
- package/dist/world/lod.js +66 -0
- package/dist/world/mapLayers.d.ts +60 -0
- package/dist/world/mapLayers.js +44 -0
- package/dist/world/massing.d.ts +45 -0
- package/dist/world/massing.js +581 -0
- package/dist/world/minimap.d.ts +13 -1
- package/dist/world/minimap.js +21 -2
- package/dist/world/placementController.d.ts +22 -1
- package/dist/world/placementController.js +56 -0
- package/dist/world/terrain.d.ts +62 -1
- package/dist/world/terrain.js +253 -9
- package/dist/world/voxelField.d.ts +54 -0
- package/dist/world/voxelField.js +241 -0
- package/dist/world/water.d.ts +7 -0
- package/dist/world/water.js +19 -4
- package/dist/world/weather.d.ts +3 -18
- package/dist/world/weather.js +4 -17
- package/dist/world/windZones.d.ts +47 -0
- package/dist/world/windZones.js +59 -0
- package/llms.txt +4983 -0
- package/package.json +14 -3
|
@@ -1,5 +1,14 @@
|
|
|
1
|
+
export const SHAPE_BOX = 0;
|
|
2
|
+
export const SHAPE_SPHERE = 1;
|
|
3
|
+
export const JOINT_FIXED = 0;
|
|
4
|
+
export const JOINT_HINGE = 1;
|
|
5
|
+
export const JOINT_DISTANCE = 2;
|
|
6
|
+
export const JOINT_SPRING = 3;
|
|
1
7
|
const FLAG_SLEEPING = 1;
|
|
2
8
|
const FLAG_STATIC = 2;
|
|
9
|
+
const FLAG_DEAD = 4;
|
|
10
|
+
/** Cap on `nx*ny*nz` broadphase cells — guards a huge-bounds/tiny-cellSize config from hanging `step()`. */
|
|
11
|
+
export const MAX_BROADPHASE_CELLS = 1_000_000;
|
|
3
12
|
/** Grid cell containing a point, clamped into the grid. Pure — exported for tests. */
|
|
4
13
|
export function cellCoord(value, min, cellSize, cells) {
|
|
5
14
|
const raw = Math.floor((value - min) / cellSize);
|
|
@@ -39,6 +48,8 @@ export class PhysicsWorld {
|
|
|
39
48
|
halfZ;
|
|
40
49
|
invMass;
|
|
41
50
|
flags;
|
|
51
|
+
/** `SHAPE_BOX` (0) or `SHAPE_SPHERE` (1) per body; spheres keep their radius in halfX/halfY/halfZ. */
|
|
52
|
+
shape;
|
|
42
53
|
/** 1 for a body that took part in a contact this frame; reset each `step()`. Render tint reads this. */
|
|
43
54
|
contact;
|
|
44
55
|
sleepTimer;
|
|
@@ -58,7 +69,7 @@ export class PhysicsWorld {
|
|
|
58
69
|
awakeList;
|
|
59
70
|
awakeSlot;
|
|
60
71
|
awakeCount = 0;
|
|
61
|
-
|
|
72
|
+
jKind;
|
|
62
73
|
jActive;
|
|
63
74
|
jBodyA;
|
|
64
75
|
jBodyB;
|
|
@@ -68,6 +79,9 @@ export class PhysicsWorld {
|
|
|
68
79
|
jAnchorBX;
|
|
69
80
|
jAnchorBY;
|
|
70
81
|
jAnchorBZ;
|
|
82
|
+
jAxisX;
|
|
83
|
+
jAxisY;
|
|
84
|
+
jAxisZ;
|
|
71
85
|
jRest;
|
|
72
86
|
jStiffness;
|
|
73
87
|
jDamping;
|
|
@@ -76,6 +90,12 @@ export class PhysicsWorld {
|
|
|
76
90
|
jFreeCount = 0;
|
|
77
91
|
jointHigh = 0;
|
|
78
92
|
jointActiveCount = 0;
|
|
93
|
+
// Removed bodies tombstone (FLAG_DEAD) rather than compact: `id` is the SoA slot itself, handed
|
|
94
|
+
// out to callers and stored raw in joints/game state, so a slot can never move once allocated.
|
|
95
|
+
// `bodyHigh` is the high-water mark of ever-used slots; `bodyFree` recycles dead ones for addBody.
|
|
96
|
+
bodyFree;
|
|
97
|
+
bodyFreeCount = 0;
|
|
98
|
+
bodyHigh = 0;
|
|
79
99
|
collisionListener = null;
|
|
80
100
|
collisionMinSpeed = 0;
|
|
81
101
|
collisionEvent = {
|
|
@@ -131,6 +151,7 @@ export class PhysicsWorld {
|
|
|
131
151
|
this.halfZ = new Float32Array(cap);
|
|
132
152
|
this.invMass = new Float32Array(cap);
|
|
133
153
|
this.flags = new Uint8Array(cap);
|
|
154
|
+
this.shape = new Uint8Array(cap);
|
|
134
155
|
this.contact = new Uint8Array(cap);
|
|
135
156
|
this.sleepTimer = new Uint16Array(cap);
|
|
136
157
|
this.prevX = new Float32Array(cap);
|
|
@@ -141,14 +162,20 @@ export class PhysicsWorld {
|
|
|
141
162
|
this.ny = Math.max(1, Math.ceil(span(1) / this.cellSize));
|
|
142
163
|
this.nz = Math.max(1, Math.ceil(span(2) / this.cellSize));
|
|
143
164
|
this.numCells = this.nx * this.ny * this.nz;
|
|
165
|
+
if (this.numCells > MAX_BROADPHASE_CELLS) {
|
|
166
|
+
throw new Error(`PhysicsWorld: broadphase grid too large (${this.nx}x${this.ny}x${this.nz} = ${this.numCells} cells, ` +
|
|
167
|
+
`cap ${MAX_BROADPHASE_CELLS}). bounds=[${config.bounds.min}]..[${config.bounds.max}] cellSize=${this.cellSize}; ` +
|
|
168
|
+
`raise cellSize or shrink bounds.`);
|
|
169
|
+
}
|
|
144
170
|
this.cellStart = new Int32Array(this.numCells + 1);
|
|
145
171
|
this.cursor = new Int32Array(this.numCells);
|
|
146
172
|
this.sorted = new Int32Array(cap);
|
|
147
173
|
this.bodyCell = new Int32Array(cap);
|
|
148
174
|
this.awakeList = new Int32Array(cap);
|
|
149
175
|
this.awakeSlot = new Int32Array(cap).fill(-1);
|
|
176
|
+
this.bodyFree = new Int32Array(cap);
|
|
150
177
|
const jc = this.jointCapacity;
|
|
151
|
-
this.
|
|
178
|
+
this.jKind = new Uint8Array(jc);
|
|
152
179
|
this.jActive = new Uint8Array(jc);
|
|
153
180
|
this.jBodyA = new Int32Array(jc);
|
|
154
181
|
this.jBodyB = new Int32Array(jc);
|
|
@@ -158,6 +185,9 @@ export class PhysicsWorld {
|
|
|
158
185
|
this.jAnchorBX = new Float32Array(jc);
|
|
159
186
|
this.jAnchorBY = new Float32Array(jc);
|
|
160
187
|
this.jAnchorBZ = new Float32Array(jc);
|
|
188
|
+
this.jAxisX = new Float32Array(jc);
|
|
189
|
+
this.jAxisY = new Float32Array(jc);
|
|
190
|
+
this.jAxisZ = new Float32Array(jc);
|
|
161
191
|
this.jRest = new Float32Array(jc);
|
|
162
192
|
this.jStiffness = new Float32Array(jc);
|
|
163
193
|
this.jDamping = new Float32Array(jc);
|
|
@@ -184,6 +214,10 @@ export class PhysicsWorld {
|
|
|
184
214
|
get count() {
|
|
185
215
|
return this.bodyCount;
|
|
186
216
|
}
|
|
217
|
+
/** One past the highest slot `addBody` has ever handed out — the range `removeBody` leaves holes in. */
|
|
218
|
+
get highWater() {
|
|
219
|
+
return this.bodyHigh;
|
|
220
|
+
}
|
|
187
221
|
get cells() {
|
|
188
222
|
return { nx: this.nx, ny: this.ny, nz: this.nz, total: this.numCells };
|
|
189
223
|
}
|
|
@@ -200,18 +234,25 @@ export class PhysicsWorld {
|
|
|
200
234
|
this.collisionMinSpeed = minApproachSpeed;
|
|
201
235
|
}
|
|
202
236
|
hingeJoint(options) {
|
|
203
|
-
|
|
237
|
+
const raw = options.axis ?? [0, 1, 0];
|
|
238
|
+
const len = Math.hypot(raw[0], raw[1], raw[2]);
|
|
239
|
+
if (!(len > 1e-9))
|
|
240
|
+
throw new Error("PhysicsWorld: hingeJoint axis must be non-zero");
|
|
241
|
+
return this.addJoint(options, JOINT_HINGE, 0, [raw[0] / len, raw[1] / len, raw[2] / len]);
|
|
204
242
|
}
|
|
205
243
|
fixedJoint(options) {
|
|
206
|
-
return this.addJoint(options,
|
|
244
|
+
return this.addJoint(options, JOINT_FIXED, 0, null);
|
|
207
245
|
}
|
|
208
246
|
distanceJoint(options) {
|
|
209
|
-
return this.addJoint(options,
|
|
247
|
+
return this.addJoint(options, JOINT_DISTANCE, null, null);
|
|
210
248
|
}
|
|
211
249
|
springJoint(options) {
|
|
212
|
-
return this.addJoint(options,
|
|
250
|
+
return this.addJoint(options, JOINT_SPRING, null, null);
|
|
213
251
|
}
|
|
214
|
-
addJoint(options,
|
|
252
|
+
addJoint(options, kind, restOverride, axis) {
|
|
253
|
+
if (kind !== JOINT_FIXED && kind !== JOINT_HINGE && kind !== JOINT_DISTANCE && kind !== JOINT_SPRING) {
|
|
254
|
+
throw new Error(`PhysicsWorld: joint kind ${kind} is not implemented`);
|
|
255
|
+
}
|
|
215
256
|
let id;
|
|
216
257
|
if (this.jFreeCount > 0) {
|
|
217
258
|
id = this.jFree[--this.jFreeCount];
|
|
@@ -232,7 +273,10 @@ export class PhysicsWorld {
|
|
|
232
273
|
this.jAnchorBX[id] = aB?.[0] ?? 0;
|
|
233
274
|
this.jAnchorBY[id] = aB?.[1] ?? 0;
|
|
234
275
|
this.jAnchorBZ[id] = aB?.[2] ?? 0;
|
|
235
|
-
this.
|
|
276
|
+
this.jKind[id] = kind;
|
|
277
|
+
this.jAxisX[id] = axis?.[0] ?? 0;
|
|
278
|
+
this.jAxisY[id] = axis?.[1] ?? 0;
|
|
279
|
+
this.jAxisZ[id] = axis?.[2] ?? 0;
|
|
236
280
|
this.jStiffness[id] = options.stiffness ?? 40;
|
|
237
281
|
this.jDamping[id] = options.damping ?? 6;
|
|
238
282
|
this.jMaxImpulse[id] = options.maxImpulse ?? Number.POSITIVE_INFINITY;
|
|
@@ -254,6 +298,12 @@ export class PhysicsWorld {
|
|
|
254
298
|
this.jAnchorBY[id] = y;
|
|
255
299
|
this.jAnchorBZ[id] = z;
|
|
256
300
|
}
|
|
301
|
+
/** Move a joint's local anchor offset on A (world axes, no rotation applied by the solver). */
|
|
302
|
+
setJointAnchorA(id, x, y, z) {
|
|
303
|
+
this.jAnchorAX[id] = x;
|
|
304
|
+
this.jAnchorAY[id] = y;
|
|
305
|
+
this.jAnchorAZ[id] = z;
|
|
306
|
+
}
|
|
257
307
|
setJointRest(id, restLength) {
|
|
258
308
|
this.jRest[id] = restLength;
|
|
259
309
|
}
|
|
@@ -291,9 +341,16 @@ export class PhysicsWorld {
|
|
|
291
341
|
return n;
|
|
292
342
|
}
|
|
293
343
|
addBody(options) {
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
344
|
+
let i;
|
|
345
|
+
if (this.bodyFreeCount > 0) {
|
|
346
|
+
i = this.bodyFree[--this.bodyFreeCount];
|
|
347
|
+
}
|
|
348
|
+
else {
|
|
349
|
+
if (this.bodyHigh >= this.capacity)
|
|
350
|
+
throw new Error("PhysicsWorld: capacity exceeded");
|
|
351
|
+
i = this.bodyHigh++;
|
|
352
|
+
}
|
|
353
|
+
this.bodyCount += 1;
|
|
297
354
|
this.posX[i] = options.position[0];
|
|
298
355
|
this.posY[i] = options.position[1];
|
|
299
356
|
this.posZ[i] = options.position[2];
|
|
@@ -301,9 +358,18 @@ export class PhysicsWorld {
|
|
|
301
358
|
this.velX[i] = v?.[0] ?? 0;
|
|
302
359
|
this.velY[i] = v?.[1] ?? 0;
|
|
303
360
|
this.velZ[i] = v?.[2] ?? 0;
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
361
|
+
if (options.shape === "sphere") {
|
|
362
|
+
this.halfX[i] = options.radius;
|
|
363
|
+
this.halfY[i] = options.radius;
|
|
364
|
+
this.halfZ[i] = options.radius;
|
|
365
|
+
this.shape[i] = SHAPE_SPHERE;
|
|
366
|
+
}
|
|
367
|
+
else {
|
|
368
|
+
this.halfX[i] = options.halfExtents[0];
|
|
369
|
+
this.halfY[i] = options.halfExtents[1];
|
|
370
|
+
this.halfZ[i] = options.halfExtents[2];
|
|
371
|
+
this.shape[i] = SHAPE_BOX;
|
|
372
|
+
}
|
|
307
373
|
const isStatic = options.static === true;
|
|
308
374
|
const mass = options.mass ?? 1;
|
|
309
375
|
this.invMass[i] = isStatic || mass <= 0 ? 0 : 1 / mass;
|
|
@@ -314,17 +380,69 @@ export class PhysicsWorld {
|
|
|
314
380
|
f |= FLAG_SLEEPING;
|
|
315
381
|
this.flags[i] = f;
|
|
316
382
|
this.sleepTimer[i] = 0;
|
|
383
|
+
this.contact[i] = 0;
|
|
317
384
|
this.awakeSlot[i] = -1;
|
|
318
385
|
this.bodyCell[i] = this.cellOf(i);
|
|
319
386
|
if ((f & FLAG_SLEEPING) === 0)
|
|
320
387
|
this.addAwake(i);
|
|
321
388
|
return i;
|
|
322
389
|
}
|
|
390
|
+
/**
|
|
391
|
+
* Remove a body: it drops out of integration, the solver, and the broadphase, and its slot is
|
|
392
|
+
* queued for the next `addBody` to reuse. `id` and every other body's `id` stay valid — a dead
|
|
393
|
+
* slot is tombstoned (`FLAG_DEAD`), never swapped, since ids are handed out raw and stored as-is
|
|
394
|
+
* in joints and game state. There is no persistent contact set to consult, so — conservatively —
|
|
395
|
+
* any sleeping body whose AABB touches the removed body's (padded by `slop`) is woken; a body
|
|
396
|
+
* that was resting on it always qualifies.
|
|
397
|
+
*/
|
|
398
|
+
removeBody(id) {
|
|
399
|
+
if (id < 0 || id >= this.bodyHigh)
|
|
400
|
+
return;
|
|
401
|
+
if ((this.flags[id] & FLAG_DEAD) !== 0)
|
|
402
|
+
return;
|
|
403
|
+
this.wakeTouching(id);
|
|
404
|
+
this.removeAwake(id);
|
|
405
|
+
this.flags[id] = FLAG_DEAD;
|
|
406
|
+
this.invMass[id] = 0;
|
|
407
|
+
this.velX[id] = 0;
|
|
408
|
+
this.velY[id] = 0;
|
|
409
|
+
this.velZ[id] = 0;
|
|
410
|
+
this.sleepTimer[id] = 0;
|
|
411
|
+
this.contact[id] = 0;
|
|
412
|
+
this.bodyCount -= 1;
|
|
413
|
+
this.bodyFree[this.bodyFreeCount++] = id;
|
|
414
|
+
}
|
|
415
|
+
wakeTouching(id) {
|
|
416
|
+
const pad = this.slop + 0.01;
|
|
417
|
+
const minX = this.posX[id] - this.halfX[id] - pad;
|
|
418
|
+
const maxX = this.posX[id] + this.halfX[id] + pad;
|
|
419
|
+
const minY = this.posY[id] - this.halfY[id] - pad;
|
|
420
|
+
const maxY = this.posY[id] + this.halfY[id] + pad;
|
|
421
|
+
const minZ = this.posZ[id] - this.halfZ[id] - pad;
|
|
422
|
+
const maxZ = this.posZ[id] + this.halfZ[id] + pad;
|
|
423
|
+
for (let j = 0; j < this.bodyHigh; j += 1) {
|
|
424
|
+
if (j === id)
|
|
425
|
+
continue;
|
|
426
|
+
const fj = this.flags[j];
|
|
427
|
+
if ((fj & (FLAG_DEAD | FLAG_STATIC)) !== 0 || (fj & FLAG_SLEEPING) === 0)
|
|
428
|
+
continue;
|
|
429
|
+
if (this.posX[j] - this.halfX[j] > maxX || this.posX[j] + this.halfX[j] < minX)
|
|
430
|
+
continue;
|
|
431
|
+
if (this.posY[j] - this.halfY[j] > maxY || this.posY[j] + this.halfY[j] < minY)
|
|
432
|
+
continue;
|
|
433
|
+
if (this.posZ[j] - this.halfZ[j] > maxZ || this.posZ[j] + this.halfZ[j] < minZ)
|
|
434
|
+
continue;
|
|
435
|
+
this.wake(j);
|
|
436
|
+
}
|
|
437
|
+
}
|
|
323
438
|
isSleeping(i) {
|
|
324
439
|
return (this.flags[i] & FLAG_SLEEPING) !== 0;
|
|
325
440
|
}
|
|
441
|
+
isAlive(i) {
|
|
442
|
+
return (this.flags[i] & FLAG_DEAD) === 0;
|
|
443
|
+
}
|
|
326
444
|
wake(i) {
|
|
327
|
-
if ((this.flags[i] & FLAG_STATIC) !== 0)
|
|
445
|
+
if ((this.flags[i] & (FLAG_STATIC | FLAG_DEAD)) !== 0)
|
|
328
446
|
return;
|
|
329
447
|
this.flags[i] &= ~FLAG_SLEEPING;
|
|
330
448
|
this.sleepTimer[i] = 0;
|
|
@@ -333,9 +451,40 @@ export class PhysicsWorld {
|
|
|
333
451
|
this.prevZ[i] = this.posZ[i];
|
|
334
452
|
this.addAwake(i);
|
|
335
453
|
}
|
|
454
|
+
/** Overwrite a body's velocity directly, waking it if asleep. */
|
|
455
|
+
setVelocity(id, x, y, z) {
|
|
456
|
+
if ((this.flags[id] & FLAG_DEAD) !== 0)
|
|
457
|
+
return;
|
|
458
|
+
this.velX[id] = x;
|
|
459
|
+
this.velY[id] = y;
|
|
460
|
+
this.velZ[id] = z;
|
|
461
|
+
this.wake(id);
|
|
462
|
+
}
|
|
463
|
+
/** Move a body, keeping its current velocity, and wake it if asleep. */
|
|
464
|
+
setPosition(id, x, y, z) {
|
|
465
|
+
if ((this.flags[id] & FLAG_DEAD) !== 0)
|
|
466
|
+
return;
|
|
467
|
+
this.posX[id] = x;
|
|
468
|
+
this.posY[id] = y;
|
|
469
|
+
this.posZ[id] = z;
|
|
470
|
+
this.prevX[id] = x;
|
|
471
|
+
this.prevY[id] = y;
|
|
472
|
+
this.prevZ[id] = z;
|
|
473
|
+
this.bodyCell[id] = this.cellOf(id);
|
|
474
|
+
this.wake(id);
|
|
475
|
+
}
|
|
476
|
+
/** Move a body and zero its velocity — a hard reset (respawn, teleporter) instead of a slide. */
|
|
477
|
+
teleport(id, x, y, z) {
|
|
478
|
+
this.setPosition(id, x, y, z);
|
|
479
|
+
if ((this.flags[id] & FLAG_DEAD) !== 0)
|
|
480
|
+
return;
|
|
481
|
+
this.velX[id] = 0;
|
|
482
|
+
this.velY[id] = 0;
|
|
483
|
+
this.velZ[id] = 0;
|
|
484
|
+
}
|
|
336
485
|
wakeAll() {
|
|
337
|
-
for (let i = 0; i < this.
|
|
338
|
-
if ((this.flags[i] & FLAG_STATIC) === 0) {
|
|
486
|
+
for (let i = 0; i < this.bodyHigh; i += 1) {
|
|
487
|
+
if ((this.flags[i] & (FLAG_STATIC | FLAG_DEAD)) === 0) {
|
|
339
488
|
this.flags[i] &= ~FLAG_SLEEPING;
|
|
340
489
|
this.sleepTimer[i] = 0;
|
|
341
490
|
this.addAwake(i);
|
|
@@ -344,6 +493,8 @@ export class PhysicsWorld {
|
|
|
344
493
|
}
|
|
345
494
|
clear() {
|
|
346
495
|
this.bodyCount = 0;
|
|
496
|
+
this.bodyHigh = 0;
|
|
497
|
+
this.bodyFreeCount = 0;
|
|
347
498
|
this.accumulator = 0;
|
|
348
499
|
this.awakeCount = 0;
|
|
349
500
|
this.jointHigh = 0;
|
|
@@ -360,7 +511,7 @@ export class PhysicsWorld {
|
|
|
360
511
|
let substeps = 0;
|
|
361
512
|
this.stats.contacts = 0;
|
|
362
513
|
this.stats.pairs = 0;
|
|
363
|
-
this.contact.fill(0, 0, this.
|
|
514
|
+
this.contact.fill(0, 0, this.bodyHigh);
|
|
364
515
|
while (this.accumulator >= this.fixedDt) {
|
|
365
516
|
this.substep(this.fixedDt);
|
|
366
517
|
this.accumulator -= this.fixedDt;
|
|
@@ -453,25 +604,59 @@ export class PhysicsWorld {
|
|
|
453
604
|
}
|
|
454
605
|
}
|
|
455
606
|
buildGrid() {
|
|
456
|
-
const n = this.
|
|
607
|
+
const n = this.bodyHigh;
|
|
457
608
|
const start = this.cellStart;
|
|
458
609
|
start.fill(0);
|
|
459
|
-
// Sleeping bodies never move, so their cell index is cached from addBody / their last awake
|
|
460
|
-
// substep — only awake bodies pay the cell recomputation each substep.
|
|
461
610
|
const awakeCount = this.awakeCount;
|
|
462
611
|
for (let a = 0; a < awakeCount; a += 1) {
|
|
463
612
|
const i = this.awakeList[a];
|
|
464
613
|
this.bodyCell[i] = this.cellOf(i);
|
|
465
614
|
}
|
|
466
|
-
|
|
467
|
-
|
|
615
|
+
const nx = this.nx;
|
|
616
|
+
const ny = this.ny;
|
|
617
|
+
for (let i = 0; i < n; i += 1) {
|
|
618
|
+
if ((this.flags[i] & FLAG_DEAD) !== 0)
|
|
619
|
+
continue;
|
|
620
|
+
const x0 = cellCoord(this.posX[i] - this.halfX[i], this.bounds.min[0], this.cellSize, this.nx);
|
|
621
|
+
const x1 = cellCoord(this.posX[i] + this.halfX[i], this.bounds.min[0], this.cellSize, this.nx);
|
|
622
|
+
const y0 = cellCoord(this.posY[i] - this.halfY[i], this.bounds.min[1], this.cellSize, this.ny);
|
|
623
|
+
const y1 = cellCoord(this.posY[i] + this.halfY[i], this.bounds.min[1], this.cellSize, this.ny);
|
|
624
|
+
const z0 = cellCoord(this.posZ[i] - this.halfZ[i], this.bounds.min[2], this.cellSize, this.nz);
|
|
625
|
+
const z1 = cellCoord(this.posZ[i] + this.halfZ[i], this.bounds.min[2], this.cellSize, this.nz);
|
|
626
|
+
for (let z = z0; z <= z1; z += 1) {
|
|
627
|
+
for (let y = y0; y <= y1; y += 1) {
|
|
628
|
+
const rowBase = (z * ny + y) * nx;
|
|
629
|
+
for (let x = x0; x <= x1; x += 1)
|
|
630
|
+
start[rowBase + x + 1] += 1;
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
}
|
|
468
634
|
for (let c = 0; c < this.numCells; c += 1) {
|
|
469
635
|
start[c + 1] += start[c];
|
|
470
636
|
this.cursor[c] = start[c];
|
|
471
637
|
}
|
|
638
|
+
const total = start[this.numCells];
|
|
639
|
+
if (total > this.sorted.length) {
|
|
640
|
+
this.sorted = new Int32Array(Math.max(total, this.sorted.length * 2));
|
|
641
|
+
}
|
|
472
642
|
for (let i = 0; i < n; i += 1) {
|
|
473
|
-
|
|
474
|
-
|
|
643
|
+
if ((this.flags[i] & FLAG_DEAD) !== 0)
|
|
644
|
+
continue;
|
|
645
|
+
const x0 = cellCoord(this.posX[i] - this.halfX[i], this.bounds.min[0], this.cellSize, this.nx);
|
|
646
|
+
const x1 = cellCoord(this.posX[i] + this.halfX[i], this.bounds.min[0], this.cellSize, this.nx);
|
|
647
|
+
const y0 = cellCoord(this.posY[i] - this.halfY[i], this.bounds.min[1], this.cellSize, this.ny);
|
|
648
|
+
const y1 = cellCoord(this.posY[i] + this.halfY[i], this.bounds.min[1], this.cellSize, this.ny);
|
|
649
|
+
const z0 = cellCoord(this.posZ[i] - this.halfZ[i], this.bounds.min[2], this.cellSize, this.nz);
|
|
650
|
+
const z1 = cellCoord(this.posZ[i] + this.halfZ[i], this.bounds.min[2], this.cellSize, this.nz);
|
|
651
|
+
for (let z = z0; z <= z1; z += 1) {
|
|
652
|
+
for (let y = y0; y <= y1; y += 1) {
|
|
653
|
+
const rowBase = (z * ny + y) * nx;
|
|
654
|
+
for (let x = x0; x <= x1; x += 1) {
|
|
655
|
+
const c = rowBase + x;
|
|
656
|
+
this.sorted[this.cursor[c]++] = i;
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
}
|
|
475
660
|
}
|
|
476
661
|
}
|
|
477
662
|
cellOf(i) {
|
|
@@ -483,26 +668,18 @@ export class PhysicsWorld {
|
|
|
483
668
|
solve(restitutionPass, countPass) {
|
|
484
669
|
const nx = this.nx;
|
|
485
670
|
const ny = this.ny;
|
|
486
|
-
const nz = this.nz;
|
|
487
|
-
const nxny = nx * ny;
|
|
488
|
-
// A contact may wake a sleeping body mid-pass, appending it to the awake list; re-read the
|
|
489
|
-
// count each iteration so it is solved this substep too.
|
|
490
671
|
for (let a = 0; a < this.awakeCount; a += 1) {
|
|
491
672
|
const i = this.awakeList[a];
|
|
492
|
-
const
|
|
493
|
-
const
|
|
494
|
-
const
|
|
495
|
-
const
|
|
496
|
-
const
|
|
497
|
-
const
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
const x0 = cx > 0 ? cx - 1 : cx;
|
|
501
|
-
const x1 = cx < nx - 1 ? cx + 1 : cx;
|
|
502
|
-
for (let z = z0; z <= z1; z += 1) {
|
|
503
|
-
for (let y = y0; y <= y1; y += 1) {
|
|
673
|
+
const ix0 = cellCoord(this.posX[i] - this.halfX[i], this.bounds.min[0], this.cellSize, this.nx);
|
|
674
|
+
const ix1 = cellCoord(this.posX[i] + this.halfX[i], this.bounds.min[0], this.cellSize, this.nx);
|
|
675
|
+
const iy0 = cellCoord(this.posY[i] - this.halfY[i], this.bounds.min[1], this.cellSize, this.ny);
|
|
676
|
+
const iy1 = cellCoord(this.posY[i] + this.halfY[i], this.bounds.min[1], this.cellSize, this.ny);
|
|
677
|
+
const iz0 = cellCoord(this.posZ[i] - this.halfZ[i], this.bounds.min[2], this.cellSize, this.nz);
|
|
678
|
+
const iz1 = cellCoord(this.posZ[i] + this.halfZ[i], this.bounds.min[2], this.cellSize, this.nz);
|
|
679
|
+
for (let z = iz0; z <= iz1; z += 1) {
|
|
680
|
+
for (let y = iy0; y <= iy1; y += 1) {
|
|
504
681
|
const rowBase = (z * ny + y) * nx;
|
|
505
|
-
for (let x =
|
|
682
|
+
for (let x = ix0; x <= ix1; x += 1) {
|
|
506
683
|
const cell = rowBase + x;
|
|
507
684
|
const end = this.cellStart[cell + 1];
|
|
508
685
|
for (let s = this.cellStart[cell]; s < end; s += 1) {
|
|
@@ -512,6 +689,14 @@ export class PhysicsWorld {
|
|
|
512
689
|
const jSleeping = (this.flags[j] & FLAG_SLEEPING) !== 0;
|
|
513
690
|
if (!jSleeping && j < i)
|
|
514
691
|
continue;
|
|
692
|
+
const jx0 = cellCoord(this.posX[j] - this.halfX[j], this.bounds.min[0], this.cellSize, this.nx);
|
|
693
|
+
const jy0 = cellCoord(this.posY[j] - this.halfY[j], this.bounds.min[1], this.cellSize, this.ny);
|
|
694
|
+
const jz0 = cellCoord(this.posZ[j] - this.halfZ[j], this.bounds.min[2], this.cellSize, this.nz);
|
|
695
|
+
const mx0 = ix0 > jx0 ? ix0 : jx0;
|
|
696
|
+
const my0 = iy0 > jy0 ? iy0 : jy0;
|
|
697
|
+
const mz0 = iz0 > jz0 ? iz0 : jz0;
|
|
698
|
+
if (x !== mx0 || y !== my0 || z !== mz0)
|
|
699
|
+
continue;
|
|
515
700
|
this.resolve(i, j, restitutionPass, countPass);
|
|
516
701
|
}
|
|
517
702
|
}
|
|
@@ -520,39 +705,94 @@ export class PhysicsWorld {
|
|
|
520
705
|
}
|
|
521
706
|
}
|
|
522
707
|
resolve(i, j, restitutionPass, countPass) {
|
|
523
|
-
const dx = this.posX[j] - this.posX[i];
|
|
524
|
-
const px = this.halfX[i] + this.halfX[j] - Math.abs(dx);
|
|
525
|
-
if (px <= 0)
|
|
526
|
-
return;
|
|
527
|
-
const dy = this.posY[j] - this.posY[i];
|
|
528
|
-
const py = this.halfY[i] + this.halfY[j] - Math.abs(dy);
|
|
529
|
-
if (py <= 0)
|
|
530
|
-
return;
|
|
531
|
-
const dz = this.posZ[j] - this.posZ[i];
|
|
532
|
-
const pz = this.halfZ[i] + this.halfZ[j] - Math.abs(dz);
|
|
533
|
-
if (pz <= 0)
|
|
534
|
-
return;
|
|
535
|
-
if (countPass) {
|
|
536
|
-
this.stats.pairs += 1;
|
|
537
|
-
this.stats.contacts += 1;
|
|
538
|
-
this.contact[i] = 1;
|
|
539
|
-
this.contact[j] = 1;
|
|
540
|
-
}
|
|
541
708
|
let nX = 0;
|
|
542
709
|
let nY = 0;
|
|
543
710
|
let nZ = 0;
|
|
544
|
-
let pen;
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
711
|
+
let pen = 0;
|
|
712
|
+
const sphereI = this.shape[i] === SHAPE_SPHERE;
|
|
713
|
+
const sphereJ = this.shape[j] === SHAPE_SPHERE;
|
|
714
|
+
let needsAxisNormal = true;
|
|
715
|
+
if (sphereI && sphereJ) {
|
|
716
|
+
const dx = this.posX[j] - this.posX[i];
|
|
717
|
+
const dy = this.posY[j] - this.posY[i];
|
|
718
|
+
const dz = this.posZ[j] - this.posZ[i];
|
|
719
|
+
const radiusSum = this.halfX[i] + this.halfX[j];
|
|
720
|
+
const dist2 = dx * dx + dy * dy + dz * dz;
|
|
721
|
+
if (dist2 >= radiusSum * radiusSum)
|
|
722
|
+
return;
|
|
723
|
+
const dist = Math.sqrt(dist2);
|
|
724
|
+
if (dist > 1e-9) {
|
|
725
|
+
nX = dx / dist;
|
|
726
|
+
nY = dy / dist;
|
|
727
|
+
nZ = dz / dist;
|
|
728
|
+
pen = radiusSum - dist;
|
|
729
|
+
}
|
|
730
|
+
else {
|
|
731
|
+
nY = 1;
|
|
732
|
+
pen = radiusSum;
|
|
733
|
+
}
|
|
734
|
+
needsAxisNormal = false;
|
|
548
735
|
}
|
|
549
|
-
else if (
|
|
550
|
-
|
|
551
|
-
|
|
736
|
+
else if (sphereI !== sphereJ) {
|
|
737
|
+
const box = sphereI ? j : i;
|
|
738
|
+
const sph = sphereI ? i : j;
|
|
739
|
+
const loX = this.posX[box] - this.halfX[box];
|
|
740
|
+
const hiX = this.posX[box] + this.halfX[box];
|
|
741
|
+
const loY = this.posY[box] - this.halfY[box];
|
|
742
|
+
const hiY = this.posY[box] + this.halfY[box];
|
|
743
|
+
const loZ = this.posZ[box] - this.halfZ[box];
|
|
744
|
+
const hiZ = this.posZ[box] + this.halfZ[box];
|
|
745
|
+
const sx = this.posX[sph];
|
|
746
|
+
const sy = this.posY[sph];
|
|
747
|
+
const sz = this.posZ[sph];
|
|
748
|
+
const dx = sx - (sx < loX ? loX : sx > hiX ? hiX : sx);
|
|
749
|
+
const dy = sy - (sy < loY ? loY : sy > hiY ? hiY : sy);
|
|
750
|
+
const dz = sz - (sz < loZ ? loZ : sz > hiZ ? hiZ : sz);
|
|
751
|
+
const dist2 = dx * dx + dy * dy + dz * dz;
|
|
752
|
+
if (dist2 > 1e-12) {
|
|
753
|
+
const radius = this.halfX[sph];
|
|
754
|
+
if (dist2 >= radius * radius)
|
|
755
|
+
return;
|
|
756
|
+
const dist = Math.sqrt(dist2);
|
|
757
|
+
const towardSphere = sph === j ? 1 / dist : -1 / dist;
|
|
758
|
+
nX = dx * towardSphere;
|
|
759
|
+
nY = dy * towardSphere;
|
|
760
|
+
nZ = dz * towardSphere;
|
|
761
|
+
pen = radius - dist;
|
|
762
|
+
needsAxisNormal = false;
|
|
763
|
+
}
|
|
552
764
|
}
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
765
|
+
if (needsAxisNormal) {
|
|
766
|
+
const dx = this.posX[j] - this.posX[i];
|
|
767
|
+
const px = this.halfX[i] + this.halfX[j] - Math.abs(dx);
|
|
768
|
+
if (px <= 0)
|
|
769
|
+
return;
|
|
770
|
+
const dy = this.posY[j] - this.posY[i];
|
|
771
|
+
const py = this.halfY[i] + this.halfY[j] - Math.abs(dy);
|
|
772
|
+
if (py <= 0)
|
|
773
|
+
return;
|
|
774
|
+
const dz = this.posZ[j] - this.posZ[i];
|
|
775
|
+
const pz = this.halfZ[i] + this.halfZ[j] - Math.abs(dz);
|
|
776
|
+
if (pz <= 0)
|
|
777
|
+
return;
|
|
778
|
+
if (px <= py && px <= pz) {
|
|
779
|
+
nX = dx < 0 ? -1 : 1;
|
|
780
|
+
pen = px;
|
|
781
|
+
}
|
|
782
|
+
else if (py <= pz) {
|
|
783
|
+
nY = dy < 0 ? -1 : 1;
|
|
784
|
+
pen = py;
|
|
785
|
+
}
|
|
786
|
+
else {
|
|
787
|
+
nZ = dz < 0 ? -1 : 1;
|
|
788
|
+
pen = pz;
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
if (countPass) {
|
|
792
|
+
this.stats.pairs += 1;
|
|
793
|
+
this.stats.contacts += 1;
|
|
794
|
+
this.contact[i] = 1;
|
|
795
|
+
this.contact[j] = 1;
|
|
556
796
|
}
|
|
557
797
|
const rvx = this.velX[j] - this.velX[i];
|
|
558
798
|
const rvy = this.velY[j] - this.velY[i];
|
|
@@ -627,6 +867,7 @@ export class PhysicsWorld {
|
|
|
627
867
|
for (let id = 0; id < this.jointHigh; id += 1) {
|
|
628
868
|
if (this.jActive[id] !== 1)
|
|
629
869
|
continue;
|
|
870
|
+
const kind = this.jKind[id];
|
|
630
871
|
const bA = this.jBodyA[id];
|
|
631
872
|
const bB = this.jBodyB[id];
|
|
632
873
|
const ax = (bA >= 0 ? this.posX[bA] : 0) + this.jAnchorAX[id];
|
|
@@ -635,9 +876,91 @@ export class PhysicsWorld {
|
|
|
635
876
|
const bx = bB >= 0 ? this.posX[bB] + this.jAnchorBX[id] : this.jAnchorBX[id];
|
|
636
877
|
const by = bB >= 0 ? this.posY[bB] + this.jAnchorBY[id] : this.jAnchorBY[id];
|
|
637
878
|
const bz = bB >= 0 ? this.posZ[bB] + this.jAnchorBZ[id] : this.jAnchorBZ[id];
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
879
|
+
let dx = bx - ax;
|
|
880
|
+
let dy = by - ay;
|
|
881
|
+
let dz = bz - az;
|
|
882
|
+
const imA = bA >= 0 ? this.invMass[bA] : 0;
|
|
883
|
+
const imB = bB >= 0 ? this.invMass[bB] : 0;
|
|
884
|
+
const imSum = imA + imB;
|
|
885
|
+
if (imSum === 0)
|
|
886
|
+
continue;
|
|
887
|
+
if (kind === JOINT_HINGE) {
|
|
888
|
+
const ux = this.jAxisX[id];
|
|
889
|
+
const uy = this.jAxisY[id];
|
|
890
|
+
const uz = this.jAxisZ[id];
|
|
891
|
+
const along = dx * ux + dy * uy + dz * uz;
|
|
892
|
+
dx -= along * ux;
|
|
893
|
+
dy -= along * uy;
|
|
894
|
+
dz -= along * uz;
|
|
895
|
+
const dist = Math.sqrt(dx * dx + dy * dy + dz * dz);
|
|
896
|
+
const vax = bA >= 0 ? this.velX[bA] : 0;
|
|
897
|
+
const vay = bA >= 0 ? this.velY[bA] : 0;
|
|
898
|
+
const vaz = bA >= 0 ? this.velZ[bA] : 0;
|
|
899
|
+
const vbx = bB >= 0 ? this.velX[bB] : 0;
|
|
900
|
+
const vby = bB >= 0 ? this.velY[bB] : 0;
|
|
901
|
+
const vbz = bB >= 0 ? this.velZ[bB] : 0;
|
|
902
|
+
let rvx = vbx - vax;
|
|
903
|
+
let rvy = vby - vay;
|
|
904
|
+
let rvz = vbz - vaz;
|
|
905
|
+
const rvAlong = rvx * ux + rvy * uy + rvz * uz;
|
|
906
|
+
rvx -= rvAlong * ux;
|
|
907
|
+
rvy -= rvAlong * uy;
|
|
908
|
+
rvz -= rvAlong * uz;
|
|
909
|
+
if (dist > 1e-9) {
|
|
910
|
+
const nX = dx / dist;
|
|
911
|
+
const nY = dy / dist;
|
|
912
|
+
const nZ = dz / dist;
|
|
913
|
+
if (dist > slop) {
|
|
914
|
+
if (bA >= 0)
|
|
915
|
+
this.wake(bA);
|
|
916
|
+
if (bB >= 0)
|
|
917
|
+
this.wake(bB);
|
|
918
|
+
}
|
|
919
|
+
const rvn = rvx * nX + rvy * nY + rvz * nZ;
|
|
920
|
+
const jn = -rvn / imSum;
|
|
921
|
+
if (bA >= 0) {
|
|
922
|
+
this.velX[bA] -= jn * imA * nX;
|
|
923
|
+
this.velY[bA] -= jn * imA * nY;
|
|
924
|
+
this.velZ[bA] -= jn * imA * nZ;
|
|
925
|
+
}
|
|
926
|
+
if (bB >= 0) {
|
|
927
|
+
this.velX[bB] += jn * imB * nX;
|
|
928
|
+
this.velY[bB] += jn * imB * nY;
|
|
929
|
+
this.velZ[bB] += jn * imB * nZ;
|
|
930
|
+
}
|
|
931
|
+
const corr = (dist * beta) / imSum;
|
|
932
|
+
if (bA >= 0) {
|
|
933
|
+
this.posX[bA] += corr * imA * nX;
|
|
934
|
+
this.posY[bA] += corr * imA * nY;
|
|
935
|
+
this.posZ[bA] += corr * imA * nZ;
|
|
936
|
+
}
|
|
937
|
+
if (bB >= 0) {
|
|
938
|
+
this.posX[bB] -= corr * imB * nX;
|
|
939
|
+
this.posY[bB] -= corr * imB * nY;
|
|
940
|
+
this.posZ[bB] -= corr * imB * nZ;
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
else {
|
|
944
|
+
const rvl = Math.sqrt(rvx * rvx + rvy * rvy + rvz * rvz);
|
|
945
|
+
if (rvl > 1e-9) {
|
|
946
|
+
const nX = rvx / rvl;
|
|
947
|
+
const nY = rvy / rvl;
|
|
948
|
+
const nZ = rvz / rvl;
|
|
949
|
+
const jn = -rvl / imSum;
|
|
950
|
+
if (bA >= 0) {
|
|
951
|
+
this.velX[bA] -= jn * imA * nX;
|
|
952
|
+
this.velY[bA] -= jn * imA * nY;
|
|
953
|
+
this.velZ[bA] -= jn * imA * nZ;
|
|
954
|
+
}
|
|
955
|
+
if (bB >= 0) {
|
|
956
|
+
this.velX[bB] += jn * imB * nX;
|
|
957
|
+
this.velY[bB] += jn * imB * nY;
|
|
958
|
+
this.velZ[bB] += jn * imB * nZ;
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
continue;
|
|
963
|
+
}
|
|
641
964
|
const dist = Math.sqrt(dx * dx + dy * dy + dz * dz);
|
|
642
965
|
if (dist < 1e-9)
|
|
643
966
|
continue;
|
|
@@ -645,11 +968,6 @@ export class PhysicsWorld {
|
|
|
645
968
|
const nY = dy / dist;
|
|
646
969
|
const nZ = dz / dist;
|
|
647
970
|
const c = dist - this.jRest[id];
|
|
648
|
-
const imA = bA >= 0 ? this.invMass[bA] : 0;
|
|
649
|
-
const imB = bB >= 0 ? this.invMass[bB] : 0;
|
|
650
|
-
const imSum = imA + imB;
|
|
651
|
-
if (imSum === 0)
|
|
652
|
-
continue;
|
|
653
971
|
const vax = bA >= 0 ? this.velX[bA] : 0;
|
|
654
972
|
const vay = bA >= 0 ? this.velY[bA] : 0;
|
|
655
973
|
const vaz = bA >= 0 ? this.velZ[bA] : 0;
|
|
@@ -657,7 +975,7 @@ export class PhysicsWorld {
|
|
|
657
975
|
const vby = bB >= 0 ? this.velY[bB] : 0;
|
|
658
976
|
const vbz = bB >= 0 ? this.velZ[bB] : 0;
|
|
659
977
|
const rvn = (vbx - vax) * nX + (vby - vay) * nY + (vbz - vaz) * nZ;
|
|
660
|
-
if (
|
|
978
|
+
if (kind === JOINT_SPRING) {
|
|
661
979
|
if (Math.abs(c) > slop) {
|
|
662
980
|
if (bA >= 0)
|
|
663
981
|
this.wake(bA);
|
|
@@ -682,6 +1000,9 @@ export class PhysicsWorld {
|
|
|
682
1000
|
}
|
|
683
1001
|
continue;
|
|
684
1002
|
}
|
|
1003
|
+
if (kind !== JOINT_FIXED && kind !== JOINT_DISTANCE) {
|
|
1004
|
+
throw new Error(`PhysicsWorld: joint kind ${kind} is not implemented`);
|
|
1005
|
+
}
|
|
685
1006
|
if (Math.abs(c) > slop) {
|
|
686
1007
|
if (bA >= 0)
|
|
687
1008
|
this.wake(bA);
|