@series-inc/rundot-syncplay 5.28.0-beta.3 → 5.28.0-beta.4

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/core/physics.d.ts CHANGED
@@ -276,6 +276,7 @@ export interface KinetixPhysicsStepResult {
276
276
  readonly world: KinetixPhysicsWorld;
277
277
  readonly contacts: readonly KinetixPhysicsContactEvent[];
278
278
  readonly brokenJointIds: readonly string[];
279
+ readonly brokenJoints: readonly string[];
279
280
  readonly scheduledQueryResults: readonly { readonly id: string; readonly hits: readonly KinetixPhysicsHit[] }[];
280
281
  readonly prePhysicsScheduledQueryResults: readonly { readonly id: string; readonly hits: readonly KinetixPhysicsHit[] }[];
281
282
  readonly postPhysicsScheduledQueryResults: readonly { readonly id: string; readonly hits: readonly KinetixPhysicsHit[] }[];
@@ -2953,6 +2953,7 @@ export function stepPhysicsWorld(world, options) {
2953
2953
  world: nextWorld,
2954
2954
  contacts: visibleEvents,
2955
2955
  brokenJointIds,
2956
+ brokenJoints: brokenJointIds,
2956
2957
  scheduledQueryResults,
2957
2958
  prePhysicsScheduledQueryResults,
2958
2959
  postPhysicsScheduledQueryResults,
package/dist/browser.d.ts CHANGED
@@ -25,7 +25,7 @@ export { createSyncplayRunner } from './runner.js';
25
25
  export { fbm2D, hash2, hashUint32, valueNoise1D, valueNoise2D } from './noise.js';
26
26
  export { circleCastDeterministicPhysics2D, createDeterministicPhysicsWorld2D, nearestDeterministicPhysicsHit2D, queryDeterministicPhysicsAabb2D, queryDeterministicPhysicsShapeOverlap2D, raycastAllDeterministicPhysics2D, raycastDeterministicPhysics2D, stepDeterministicPhysicsWorld2D } from './physics2d.js';
27
27
  export { applyDeterministicForces3D, applyDeterministicImpulse3D, applyDeterministicImpulses3D, applyDeterministicWorldEdit3D, contactDetailsDeterministicPhysics3D, createBoxShapeFromExtents, createBoxShapeFromMinMax, createCapsuleShapeFromBase, createDeterministicPhysicsWorld3D, createTrustedDeterministicPhysicsWorld3D, deriveInverseInertia, deterministicPhysicsBodyById3D, deterministicPhysicsCallback3D, overlapDeterministicPhysics3D, raycastDeterministicPhysics3D, raycastDeterministicPhysics3DDetailed, runDeterministicPhysics3DStress, setDeterministicBodyVelocity3D, setDeterministicKinematicPoses3D, setDeterministicKinematicTransforms3D, shapeCastDeterministicPhysics3D, shapeCastDeterministicPhysics3DDetailed, stepDeterministicPhysicsWorld3D } from './physics3d.js';
28
- export { carveDeterministicVoxelChunk3D, cookDeterministicVoxelChunkCollider3D, cookDeterministicVoxelSubregion3D, recookDeterministicVoxelSubregion3D, raycastDeterministicVoxelChunk3D, splitDeterministicVoxelChunk3D } from './physics3d-voxel.js';
28
+ export { carveDeterministicVoxelChunk3D, cookDeterministicVoxelChunkCollider3D, cookDeterministicVoxelSubregion3D, recookDeterministicVoxelSubregion3D, raycastDeterministicVoxelChunk3D, splitDeterministicVoxelChunk3D, splitDeterministicVoxelCompoundCollider3D } from './physics3d-voxel.js';
29
29
  export { generatePhysicsWorldDebugMesh } from './debug-wireframe.js';
30
30
  export { cylinderHullVertices } from './physics3d-hull.js';
31
31
  export { createDeterministicCosmeticPhysicsWorld3D, setDeterministicCosmeticPhysicsAuthoritativeBodyProxies3D, stepDeterministicCosmeticPhysicsWorld3D } from './cosmetic-physics3d.js';
@@ -66,8 +66,8 @@ export type { CookedDeterministicNavmesh, DeterministicNavmeshAgent, Determinist
66
66
  export type { ConfirmedSyncplayPresentationFrame, KinetixRuntimeFactory, NetworkedClientTransport, NetworkedSyncplayClient, NetworkedSyncplayClientInputDelayOptions, NetworkedSyncplayClientNetStats, NetworkedSyncplayClientOptions, NetworkedSyncplayClientPacingOptions, NetworkedSyncplayPreparationOptions, NetworkedSyncplaySessionDescriptor, NetworkedSyncplaySlotPresence, PreparedNetworkedSyncplayTransport, SyncplayPredictionMode, SyncplayRemoteInputPrediction } from './networked-client.js';
67
67
  export type { SyncplayRunner, SyncplayRunnerConfig, SyncplayRunnerConnectionEvent, SyncplayRunnerOccupancy, SyncplayRunnerOfflineOptions, SyncplayRunnerPresentation, SyncplayRunnerSnapshot, SyncplayRunnerStatus, SyncplayRunnerTransport } from './runner.js';
68
68
  export type { DeterministicBodyKind, DeterministicContactEvent2D, DeterministicContactEventType, DeterministicPhysicsAabbQuery2D, DeterministicPhysicsActivePair2D, DeterministicPhysicsBody2D, DeterministicPhysicsCircleCastQuery2D, DeterministicPhysicsConstraint2D, DeterministicPhysicsHit2D, DeterministicPhysicsQueryOptions2D, DeterministicPhysicsRaycastQuery2D, DeterministicPhysicsShape2D, DeterministicPhysicsShapeOverlapQuery2D, DeterministicPhysicsStepOptions2D, DeterministicPhysicsStepResult2D, DeterministicPhysicsWorld2D } from './physics2d.js';
69
- export type { DeterministicContactCacheEntry3D, DeterministicJointCacheEntry3D, DeterministicKinematicPose3D, DeterministicKinematicTransformBuffer3D, DeterministicPhysicsActivePair3D, DeterministicPhysicsAxisLock3D, DeterministicPhysicsBody3D, DeterministicPhysicsCallbackMask3D, DeterministicPhysicsCompoundChild3D, DeterministicPhysicsCompoundChildShape3D, DeterministicPhysicsConstraint3D, DeterministicPhysicsContactDetail3D, DeterministicPhysicsContactEvent3D, DeterministicPhysicsDetailedHit3D, DeterministicPhysicsForceApplication3D, DeterministicPhysicsHit3D, DeterministicPhysicsHullShape3D, DeterministicPhysicsImpulseApplication3D, DeterministicPhysicsOverlapQuery3D, DeterministicPhysicsPrimitiveShape3D, DeterministicPhysicsQuaternion3D, DeterministicPhysicsQueryOptions3D, DeterministicPhysicsRaycastQuery3D, DeterministicPhysicsScheduledQuery3D, DeterministicPhysicsShape3D, DeterministicPhysicsShapeCastQuery3D, DeterministicPhysicsStepOptions3D, DeterministicPhysicsStepResult3D, DeterministicPhysicsVec3, DeterministicPhysicsWorld3D, DeterministicPhysicsWorldEdit3D } from './physics3d.js';
70
- export type { DeterministicVoxelChunk3D, DeterministicVoxelCompoundShape3D, DeterministicVoxelRayHit3D, SplitDeterministicVoxelChunkOptions3D, VoxelAnchorFace, VoxelBoxCarveRegion3D, VoxelCapsuleCarveRegion3D, VoxelCarveRegion3D, VoxelOccupancy3D, VoxelSphereCarveRegion3D, VoxelSubChunk3D } from './physics3d-voxel.js';
69
+ export type { DeterministicContactCacheEntry3D, DeterministicFluidVolume3D, DeterministicJointCacheEntry3D, DeterministicKinematicPose3D, DeterministicKinematicTransformBuffer3D, DeterministicPhysicsAabb3D, DeterministicPhysicsActivePair3D, DeterministicPhysicsAxisLock3D, DeterministicPhysicsBody3D, DeterministicPhysicsCallbackMask3D, DeterministicPhysicsCompoundChild3D, DeterministicPhysicsCompoundChildShape3D, DeterministicPhysicsConstraint3D, DeterministicPhysicsContactDetail3D, DeterministicPhysicsContactEvent3D, DeterministicPhysicsDetailedHit3D, DeterministicPhysicsForceApplication3D, DeterministicPhysicsHit3D, DeterministicPhysicsHullShape3D, DeterministicPhysicsImpulseApplication3D, DeterministicPhysicsOverlapQuery3D, DeterministicPhysicsPrimitiveShape3D, DeterministicPhysicsQuaternion3D, DeterministicPhysicsQueryOptions3D, DeterministicPhysicsRaycastQuery3D, DeterministicPhysicsScheduledQuery3D, DeterministicPhysicsShape3D, DeterministicPhysicsShapeCastQuery3D, DeterministicPhysicsStepOptions3D, DeterministicPhysicsStepResult3D, DeterministicPhysicsVec3, DeterministicPhysicsWorld3D, DeterministicPhysicsWorldEdit3D } from './physics3d.js';
70
+ export type { DeterministicVoxelChunk3D, DeterministicVoxelCompoundShape3D, DeterministicVoxelCompoundSplitResult3D, DeterministicVoxelRayHit3D, SplitDeterministicVoxelChunkOptions3D, VoxelAnchorFace, VoxelBoxCarveRegion3D, VoxelCapsuleCarveRegion3D, VoxelCarveRegion3D, VoxelOccupancy3D, VoxelSphereCarveRegion3D, VoxelSubChunk3D } from './physics3d-voxel.js';
71
71
  export type { PhysicsWorldDebugMesh, PhysicsWorldDebugMeshOptions } from './debug-wireframe.js';
72
72
  export type { DeterministicAuthoritativeBodyProxy3D, DeterministicCosmeticPhysicsBody3D, DeterministicCosmeticPhysicsStepOptions3D, DeterministicCosmeticPhysicsStepResult3D, DeterministicCosmeticPhysicsWorld3D } from './cosmetic-physics3d.js';
73
73
  export type { DeterministicPhysicsJoint3D } from './physics3d-joints.js';
package/dist/browser.js CHANGED
@@ -26,7 +26,7 @@ export { createSyncplayRunner } from './runner.js';
26
26
  export { fbm2D, hash2, hashUint32, valueNoise1D, valueNoise2D } from './noise.js';
27
27
  export { circleCastDeterministicPhysics2D, createDeterministicPhysicsWorld2D, nearestDeterministicPhysicsHit2D, queryDeterministicPhysicsAabb2D, queryDeterministicPhysicsShapeOverlap2D, raycastAllDeterministicPhysics2D, raycastDeterministicPhysics2D, stepDeterministicPhysicsWorld2D } from './physics2d.js';
28
28
  export { applyDeterministicForces3D, applyDeterministicImpulse3D, applyDeterministicImpulses3D, applyDeterministicWorldEdit3D, contactDetailsDeterministicPhysics3D, createBoxShapeFromExtents, createBoxShapeFromMinMax, createCapsuleShapeFromBase, createDeterministicPhysicsWorld3D, createTrustedDeterministicPhysicsWorld3D, deriveInverseInertia, deterministicPhysicsBodyById3D, deterministicPhysicsCallback3D, overlapDeterministicPhysics3D, raycastDeterministicPhysics3D, raycastDeterministicPhysics3DDetailed, runDeterministicPhysics3DStress, setDeterministicBodyVelocity3D, setDeterministicKinematicPoses3D, setDeterministicKinematicTransforms3D, shapeCastDeterministicPhysics3D, shapeCastDeterministicPhysics3DDetailed, stepDeterministicPhysicsWorld3D } from './physics3d.js';
29
- export { carveDeterministicVoxelChunk3D, cookDeterministicVoxelChunkCollider3D, cookDeterministicVoxelSubregion3D, recookDeterministicVoxelSubregion3D, raycastDeterministicVoxelChunk3D, splitDeterministicVoxelChunk3D } from './physics3d-voxel.js';
29
+ export { carveDeterministicVoxelChunk3D, cookDeterministicVoxelChunkCollider3D, cookDeterministicVoxelSubregion3D, recookDeterministicVoxelSubregion3D, raycastDeterministicVoxelChunk3D, splitDeterministicVoxelChunk3D, splitDeterministicVoxelCompoundCollider3D } from './physics3d-voxel.js';
30
30
  export { generatePhysicsWorldDebugMesh } from './debug-wireframe.js';
31
31
  export { cylinderHullVertices } from './physics3d-hull.js';
32
32
  export { createDeterministicCosmeticPhysicsWorld3D, setDeterministicCosmeticPhysicsAuthoritativeBodyProxies3D, stepDeterministicCosmeticPhysicsWorld3D } from './cosmetic-physics3d.js';
package/dist/creator.d.ts CHANGED
@@ -13,13 +13,13 @@ export type { ReplayRuntimeFactory, ReplayVerificationConfig, ReplayVerification
13
13
  export type { SyncplaySession, SyncplaySessionPolicy } from './sdk-session.js';
14
14
  export type { InstantReplayInputFrame, InstantReplayResult, InstantReplayRunner, InstantReplayRunnerOptions, InstantReplayRunOptions, RunInstantReplayOptions, } from './instant-replay.js';
15
15
  export { applyDeterministicForces3D, applyDeterministicImpulse3D, applyDeterministicImpulses3D, applyDeterministicWorldEdit3D, contactDetailsDeterministicPhysics3D, createBoxShapeFromExtents, createBoxShapeFromMinMax, createCapsuleShapeFromBase, createDeterministicPhysicsWorld3D, deriveInverseInertia, deterministicPhysicsBodyById3D, deterministicPhysicsCallback3D, overlapDeterministicPhysics3D, raycastDeterministicPhysics3D, raycastDeterministicPhysics3DDetailed, runDeterministicPhysics3DStress, setDeterministicBodyVelocity3D, setDeterministicKinematicPoses3D, setDeterministicKinematicTransforms3D, shapeCastDeterministicPhysics3D, shapeCastDeterministicPhysics3DDetailed, stepDeterministicPhysicsWorld3D, } from './physics3d.js';
16
- export { carveDeterministicVoxelChunk3D, cookDeterministicVoxelChunkCollider3D, cookDeterministicVoxelSubregion3D, recookDeterministicVoxelSubregion3D, raycastDeterministicVoxelChunk3D, splitDeterministicVoxelChunk3D, } from './physics3d-voxel.js';
16
+ export { carveDeterministicVoxelChunk3D, cookDeterministicVoxelChunkCollider3D, cookDeterministicVoxelSubregion3D, recookDeterministicVoxelSubregion3D, raycastDeterministicVoxelChunk3D, splitDeterministicVoxelChunk3D, splitDeterministicVoxelCompoundCollider3D, } from './physics3d-voxel.js';
17
17
  export { generatePhysicsWorldDebugMesh } from './debug-wireframe.js';
18
18
  export { cylinderHullVertices } from './physics3d-hull.js';
19
19
  export { stepDeterministicVehicle3D } from './physics3d-vehicle.js';
20
20
  export { createDeterministicBinarySerializer, deterministicDslMaxPlayers, parseDeterministicSchemaDsl, stableDeterministicDslSchemaJson, } from './schema-dsl-binary.js';
21
- export type { DeterministicContactCacheEntry3D, DeterministicJointCacheEntry3D, DeterministicKinematicPose3D, DeterministicKinematicTransformBuffer3D, DeterministicPhysicsActivePair3D, DeterministicPhysicsAxisLock3D, DeterministicPhysicsBody3D, DeterministicPhysicsCallbackMask3D, DeterministicPhysicsCompoundChild3D, DeterministicPhysicsCompoundChildShape3D, DeterministicPhysicsConstraint3D, DeterministicPhysicsContactDetail3D, DeterministicPhysicsContactEvent3D, DeterministicPhysicsDetailedHit3D, DeterministicPhysicsForceApplication3D, DeterministicPhysicsHit3D, DeterministicPhysicsHullShape3D, DeterministicPhysicsImpulseApplication3D, DeterministicPhysicsOverlapQuery3D, DeterministicPhysicsPrimitiveShape3D, DeterministicPhysicsQuaternion3D, DeterministicPhysicsQueryOptions3D, DeterministicPhysicsRaycastQuery3D, DeterministicPhysicsScheduledQuery3D, DeterministicPhysicsShape3D, DeterministicPhysicsShapeCastQuery3D, DeterministicPhysicsStepOptions3D, DeterministicPhysicsStepResult3D, DeterministicPhysicsVec3, DeterministicPhysicsWorld3D, DeterministicPhysicsWorldEdit3D, } from './physics3d.js';
22
- export type { DeterministicVoxelChunk3D, DeterministicVoxelCompoundShape3D, DeterministicVoxelRayHit3D, SplitDeterministicVoxelChunkOptions3D, VoxelAnchorFace, VoxelBoxCarveRegion3D, VoxelCapsuleCarveRegion3D, VoxelCarveRegion3D, VoxelOccupancy3D, VoxelSphereCarveRegion3D, VoxelSubChunk3D, } from './physics3d-voxel.js';
21
+ export type { DeterministicContactCacheEntry3D, DeterministicFluidVolume3D, DeterministicJointCacheEntry3D, DeterministicKinematicPose3D, DeterministicKinematicTransformBuffer3D, DeterministicPhysicsAabb3D, DeterministicPhysicsActivePair3D, DeterministicPhysicsAxisLock3D, DeterministicPhysicsBody3D, DeterministicPhysicsCallbackMask3D, DeterministicPhysicsCompoundChild3D, DeterministicPhysicsCompoundChildShape3D, DeterministicPhysicsConstraint3D, DeterministicPhysicsContactDetail3D, DeterministicPhysicsContactEvent3D, DeterministicPhysicsDetailedHit3D, DeterministicPhysicsForceApplication3D, DeterministicPhysicsHit3D, DeterministicPhysicsHullShape3D, DeterministicPhysicsImpulseApplication3D, DeterministicPhysicsOverlapQuery3D, DeterministicPhysicsPrimitiveShape3D, DeterministicPhysicsQuaternion3D, DeterministicPhysicsQueryOptions3D, DeterministicPhysicsRaycastQuery3D, DeterministicPhysicsScheduledQuery3D, DeterministicPhysicsShape3D, DeterministicPhysicsShapeCastQuery3D, DeterministicPhysicsStepOptions3D, DeterministicPhysicsStepResult3D, DeterministicPhysicsVec3, DeterministicPhysicsWorld3D, DeterministicPhysicsWorldEdit3D, } from './physics3d.js';
22
+ export type { DeterministicVoxelChunk3D, DeterministicVoxelCompoundShape3D, DeterministicVoxelCompoundSplitResult3D, DeterministicVoxelRayHit3D, SplitDeterministicVoxelChunkOptions3D, VoxelAnchorFace, VoxelBoxCarveRegion3D, VoxelCapsuleCarveRegion3D, VoxelCarveRegion3D, VoxelOccupancy3D, VoxelSphereCarveRegion3D, VoxelSubChunk3D, } from './physics3d-voxel.js';
23
23
  export type { PhysicsWorldDebugMesh, PhysicsWorldDebugMeshOptions } from './debug-wireframe.js';
24
24
  export type { DeterministicPhysicsJoint3D } from './physics3d-joints.js';
25
25
  export type { DeterministicVehicle3D, DeterministicVehicleImpulse3D, DeterministicVehicleInput3D, DeterministicVehicleStepOptions3D, DeterministicVehicleStepResult3D, DeterministicVehicleWheel3D, DeterministicVehicleWheelTelemetry3D, } from './physics3d-vehicle.js';
package/dist/creator.js CHANGED
@@ -10,7 +10,7 @@ export { runSyncplaySynctest } from './synctest.js';
10
10
  // cooking. Cosmetic physics, KCC (movement3d), and the destruction-cert runner
11
11
  // stay off it — the first two are not requested, the third reads wall clock.
12
12
  export { applyDeterministicForces3D, applyDeterministicImpulse3D, applyDeterministicImpulses3D, applyDeterministicWorldEdit3D, contactDetailsDeterministicPhysics3D, createBoxShapeFromExtents, createBoxShapeFromMinMax, createCapsuleShapeFromBase, createDeterministicPhysicsWorld3D, deriveInverseInertia, deterministicPhysicsBodyById3D, deterministicPhysicsCallback3D, overlapDeterministicPhysics3D, raycastDeterministicPhysics3D, raycastDeterministicPhysics3DDetailed, runDeterministicPhysics3DStress, setDeterministicBodyVelocity3D, setDeterministicKinematicPoses3D, setDeterministicKinematicTransforms3D, shapeCastDeterministicPhysics3D, shapeCastDeterministicPhysics3DDetailed, stepDeterministicPhysicsWorld3D, } from './physics3d.js';
13
- export { carveDeterministicVoxelChunk3D, cookDeterministicVoxelChunkCollider3D, cookDeterministicVoxelSubregion3D, recookDeterministicVoxelSubregion3D, raycastDeterministicVoxelChunk3D, splitDeterministicVoxelChunk3D, } from './physics3d-voxel.js';
13
+ export { carveDeterministicVoxelChunk3D, cookDeterministicVoxelChunkCollider3D, cookDeterministicVoxelSubregion3D, recookDeterministicVoxelSubregion3D, raycastDeterministicVoxelChunk3D, splitDeterministicVoxelChunk3D, splitDeterministicVoxelCompoundCollider3D, } from './physics3d-voxel.js';
14
14
  export { generatePhysicsWorldDebugMesh } from './debug-wireframe.js';
15
15
  export { cylinderHullVertices } from './physics3d-hull.js';
16
16
  export { stepDeterministicVehicle3D } from './physics3d-vehicle.js';
package/dist/index.d.ts CHANGED
@@ -28,7 +28,7 @@ export { createSyncplayRunner } from './runner.js';
28
28
  export { fbm2D, hash2, hashUint32, valueNoise1D, valueNoise2D } from './noise.js';
29
29
  export { circleCastDeterministicPhysics2D, createDeterministicPhysicsWorld2D, nearestDeterministicPhysicsHit2D, queryDeterministicPhysicsAabb2D, queryDeterministicPhysicsShapeOverlap2D, raycastAllDeterministicPhysics2D, raycastDeterministicPhysics2D, stepDeterministicPhysicsWorld2D } from './physics2d.js';
30
30
  export { applyDeterministicForces3D, applyDeterministicImpulse3D, applyDeterministicImpulses3D, applyDeterministicWorldEdit3D, contactDetailsDeterministicPhysics3D, createBoxShapeFromExtents, createBoxShapeFromMinMax, createCapsuleShapeFromBase, createDeterministicPhysicsWorld3D, createTrustedDeterministicPhysicsWorld3D, deriveInverseInertia, deterministicPhysicsBodyById3D, deterministicPhysicsCallback3D, overlapDeterministicPhysics3D, raycastDeterministicPhysics3D, raycastDeterministicPhysics3DDetailed, runDeterministicPhysics3DStress, setDeterministicBodyVelocity3D, setDeterministicKinematicPoses3D, setDeterministicKinematicTransforms3D, shapeCastDeterministicPhysics3D, shapeCastDeterministicPhysics3DDetailed, stepDeterministicPhysicsWorld3D } from './physics3d.js';
31
- export { carveDeterministicVoxelChunk3D, cookDeterministicVoxelChunkCollider3D, cookDeterministicVoxelSubregion3D, recookDeterministicVoxelSubregion3D, raycastDeterministicVoxelChunk3D, splitDeterministicVoxelChunk3D } from './physics3d-voxel.js';
31
+ export { carveDeterministicVoxelChunk3D, cookDeterministicVoxelChunkCollider3D, cookDeterministicVoxelSubregion3D, recookDeterministicVoxelSubregion3D, raycastDeterministicVoxelChunk3D, splitDeterministicVoxelChunk3D, splitDeterministicVoxelCompoundCollider3D } from './physics3d-voxel.js';
32
32
  export { generatePhysicsWorldDebugMesh } from './debug-wireframe.js';
33
33
  export { cylinderHullVertices } from './physics3d-hull.js';
34
34
  export { runDeterministicPhysics3DDestructionCert } from './physics3d-destruction-cert.js';
@@ -78,8 +78,8 @@ export type { CookedDeterministicNavmesh, DeterministicNavmeshAgent, Determinist
78
78
  export type { ConfirmedSyncplayPresentationFrame, KinetixRuntimeFactory, NetworkedClientTransport, NetworkedSyncplayClient, NetworkedSyncplayClientInputDelayOptions, NetworkedSyncplayClientNetStats, NetworkedSyncplayClientOptions, NetworkedSyncplayClientPacingOptions, NetworkedSyncplayPreparationOptions, NetworkedSyncplaySessionDescriptor, PreparedNetworkedSyncplayTransport, SyncplayPredictionMode, SyncplayRemoteInputPrediction } from './networked-client.js';
79
79
  export type { SyncplayRunner, SyncplayRunnerConfig, SyncplayRunnerConnectionEvent, SyncplayRunnerOccupancy, SyncplayRunnerOfflineOptions, SyncplayRunnerPresentation, SyncplayRunnerSnapshot, SyncplayRunnerStatus, SyncplayRunnerTransport } from './runner.js';
80
80
  export type { DeterministicBodyKind, DeterministicContactEvent2D, DeterministicContactEventType, DeterministicPhysicsAabbQuery2D, DeterministicPhysicsBody2D, DeterministicPhysicsCircleCastQuery2D, DeterministicPhysicsConstraint2D, DeterministicPhysicsHit2D, DeterministicPhysicsQueryOptions2D, DeterministicPhysicsRaycastQuery2D, DeterministicPhysicsShape2D, DeterministicPhysicsShapeOverlapQuery2D, DeterministicPhysicsStepOptions2D, DeterministicPhysicsStepResult2D, DeterministicPhysicsWorld2D } from './physics2d.js';
81
- export type { DeterministicContactCacheEntry3D, DeterministicJointCacheEntry3D, DeterministicKinematicPose3D, DeterministicKinematicTransformBuffer3D, DeterministicPhysicsActivePair3D, DeterministicPhysicsAxisLock3D, DeterministicPhysicsBody3D, DeterministicPhysicsCallbackMask3D, DeterministicPhysicsCompoundChild3D, DeterministicPhysicsCompoundChildShape3D, DeterministicPhysicsConstraint3D, DeterministicPhysicsContactDetail3D, DeterministicPhysicsContactEvent3D, DeterministicPhysicsDetailedHit3D, DeterministicPhysicsForceApplication3D, DeterministicPhysicsHit3D, DeterministicPhysicsHullShape3D, DeterministicPhysicsImpulseApplication3D, DeterministicPhysicsOverlapQuery3D, DeterministicPhysicsPrimitiveShape3D, DeterministicPhysicsQuaternion3D, DeterministicPhysicsQueryOptions3D, DeterministicPhysicsRaycastQuery3D, DeterministicPhysicsScheduledQuery3D, DeterministicPhysicsShape3D, DeterministicPhysicsShapeCastQuery3D, DeterministicPhysicsStepOptions3D, DeterministicPhysicsStepResult3D, DeterministicPhysicsVec3, DeterministicPhysicsWorld3D, DeterministicPhysicsWorldEdit3D } from './physics3d.js';
82
- export type { DeterministicVoxelChunk3D, DeterministicVoxelCompoundShape3D, DeterministicVoxelRayHit3D, SplitDeterministicVoxelChunkOptions3D, VoxelAnchorFace, VoxelBoxCarveRegion3D, VoxelCapsuleCarveRegion3D, VoxelCarveRegion3D, VoxelOccupancy3D, VoxelSphereCarveRegion3D, VoxelSubChunk3D } from './physics3d-voxel.js';
81
+ export type { DeterministicContactCacheEntry3D, DeterministicFluidVolume3D, DeterministicJointCacheEntry3D, DeterministicKinematicPose3D, DeterministicKinematicTransformBuffer3D, DeterministicPhysicsAabb3D, DeterministicPhysicsActivePair3D, DeterministicPhysicsAxisLock3D, DeterministicPhysicsBody3D, DeterministicPhysicsCallbackMask3D, DeterministicPhysicsCompoundChild3D, DeterministicPhysicsCompoundChildShape3D, DeterministicPhysicsConstraint3D, DeterministicPhysicsContactDetail3D, DeterministicPhysicsContactEvent3D, DeterministicPhysicsDetailedHit3D, DeterministicPhysicsForceApplication3D, DeterministicPhysicsHit3D, DeterministicPhysicsHullShape3D, DeterministicPhysicsImpulseApplication3D, DeterministicPhysicsOverlapQuery3D, DeterministicPhysicsPrimitiveShape3D, DeterministicPhysicsQuaternion3D, DeterministicPhysicsQueryOptions3D, DeterministicPhysicsRaycastQuery3D, DeterministicPhysicsScheduledQuery3D, DeterministicPhysicsShape3D, DeterministicPhysicsShapeCastQuery3D, DeterministicPhysicsStepOptions3D, DeterministicPhysicsStepResult3D, DeterministicPhysicsVec3, DeterministicPhysicsWorld3D, DeterministicPhysicsWorldEdit3D } from './physics3d.js';
82
+ export type { DeterministicVoxelChunk3D, DeterministicVoxelCompoundShape3D, DeterministicVoxelCompoundSplitResult3D, DeterministicVoxelRayHit3D, SplitDeterministicVoxelChunkOptions3D, VoxelAnchorFace, VoxelBoxCarveRegion3D, VoxelCapsuleCarveRegion3D, VoxelCarveRegion3D, VoxelOccupancy3D, VoxelSphereCarveRegion3D, VoxelSubChunk3D } from './physics3d-voxel.js';
83
83
  export type { PhysicsWorldDebugMesh, PhysicsWorldDebugMeshOptions } from './debug-wireframe.js';
84
84
  export type { DeterministicDestructionCertOptions, DeterministicDestructionCertResult } from './physics3d-destruction-cert.js';
85
85
  export type { DeterministicAuthoritativeBodyProxy3D, DeterministicCosmeticPhysicsBody3D, DeterministicCosmeticPhysicsStepOptions3D, DeterministicCosmeticPhysicsStepResult3D, DeterministicCosmeticPhysicsWorld3D } from './cosmetic-physics3d.js';
package/dist/index.js CHANGED
@@ -29,7 +29,7 @@ export { createSyncplayRunner } from './runner.js';
29
29
  export { fbm2D, hash2, hashUint32, valueNoise1D, valueNoise2D } from './noise.js';
30
30
  export { circleCastDeterministicPhysics2D, createDeterministicPhysicsWorld2D, nearestDeterministicPhysicsHit2D, queryDeterministicPhysicsAabb2D, queryDeterministicPhysicsShapeOverlap2D, raycastAllDeterministicPhysics2D, raycastDeterministicPhysics2D, stepDeterministicPhysicsWorld2D } from './physics2d.js';
31
31
  export { applyDeterministicForces3D, applyDeterministicImpulse3D, applyDeterministicImpulses3D, applyDeterministicWorldEdit3D, contactDetailsDeterministicPhysics3D, createBoxShapeFromExtents, createBoxShapeFromMinMax, createCapsuleShapeFromBase, createDeterministicPhysicsWorld3D, createTrustedDeterministicPhysicsWorld3D, deriveInverseInertia, deterministicPhysicsBodyById3D, deterministicPhysicsCallback3D, overlapDeterministicPhysics3D, raycastDeterministicPhysics3D, raycastDeterministicPhysics3DDetailed, runDeterministicPhysics3DStress, setDeterministicBodyVelocity3D, setDeterministicKinematicPoses3D, setDeterministicKinematicTransforms3D, shapeCastDeterministicPhysics3D, shapeCastDeterministicPhysics3DDetailed, stepDeterministicPhysicsWorld3D } from './physics3d.js';
32
- export { carveDeterministicVoxelChunk3D, cookDeterministicVoxelChunkCollider3D, cookDeterministicVoxelSubregion3D, recookDeterministicVoxelSubregion3D, raycastDeterministicVoxelChunk3D, splitDeterministicVoxelChunk3D } from './physics3d-voxel.js';
32
+ export { carveDeterministicVoxelChunk3D, cookDeterministicVoxelChunkCollider3D, cookDeterministicVoxelSubregion3D, recookDeterministicVoxelSubregion3D, raycastDeterministicVoxelChunk3D, splitDeterministicVoxelChunk3D, splitDeterministicVoxelCompoundCollider3D } from './physics3d-voxel.js';
33
33
  export { generatePhysicsWorldDebugMesh } from './debug-wireframe.js';
34
34
  export { cylinderHullVertices } from './physics3d-hull.js';
35
35
  export { runDeterministicPhysics3DDestructionCert } from './physics3d-destruction-cert.js';
@@ -101,9 +101,13 @@ export type DeterministicPhysicsJoint3D = {
101
101
  readonly targetZ: number;
102
102
  readonly maxForce: number;
103
103
  readonly damping?: number;
104
+ readonly linearHertz?: number;
105
+ readonly linearDampingRatio?: number;
104
106
  readonly targetRotation?: Quat3D;
105
107
  readonly maxTorque?: number;
106
108
  readonly angularDamping?: number;
109
+ readonly angularHertz?: number;
110
+ readonly angularDampingRatio?: number;
107
111
  readonly breakDistance?: number;
108
112
  readonly breakImpulse?: number;
109
113
  };
@@ -626,6 +626,29 @@ function springJoint(joint, aIndex, bIndex, warm) {
626
626
  },
627
627
  };
628
628
  }
629
+ /**
630
+ * Box3D / Box2D v3 b3MakeSoft coefficient scheme:
631
+ * Translates (frequencyHz, dampingRatio, dt) into mass-independent constraint softness coefficients:
632
+ * biasRate = omega / a1, massScale = a2 / (1 + a2), impulseScale = 1 / (1 + a2).
633
+ * Unconditionally stable: biasRate * dt = (dt * omega) / (2 * damping + dt * omega) <= 1.0 always.
634
+ */
635
+ function makeSoft(hertz, dampingRatio, dt) {
636
+ if (hertz <= 0) {
637
+ return { biasRate: 0, massScale: 0, impulseScale: 0 };
638
+ }
639
+ const omega = TWO_PI * hertz;
640
+ const a1 = 2 * Math.max(0, dampingRatio) + dt * omega;
641
+ if (a1 <= 1e-18) {
642
+ return { biasRate: 0, massScale: 0, impulseScale: 0 };
643
+ }
644
+ const a2 = dt * omega * a1;
645
+ const a3 = 1 / (1 + a2);
646
+ return {
647
+ biasRate: omega / a1,
648
+ massScale: a2 * a3,
649
+ impulseScale: a3,
650
+ };
651
+ }
629
652
  function grabJoint(joint, bodyIndex, warm) {
630
653
  const state = {
631
654
  impulse: warm ? { x: warm.impulseX, y: warm.impulseY, z: warm.impulseZ } : { x: 0, y: 0, z: 0 },
@@ -636,13 +659,20 @@ function grabJoint(joint, bodyIndex, warm) {
636
659
  };
637
660
  let r = { x: 0, y: 0, z: 0 };
638
661
  let kInv = [0, 0, 0, 0, 0, 0, 0, 0, 0];
639
- let desiredVel = { x: 0, y: 0, z: 0 };
640
662
  const target = { x: joint.targetX, y: joint.targetY, z: joint.targetZ };
641
663
  const maxImpulse = joint.maxForce * 1;
642
664
  const targetRotation = joint.targetRotation !== undefined ? normalizeQuat(joint.targetRotation) : undefined;
643
665
  const maxTorque = joint.maxTorque ?? 0;
644
- const angularDamping = joint.angularDamping ?? joint.damping ?? 1;
645
- let desiredAngVel = { x: 0, y: 0, z: 0 };
666
+ const linearHertz = joint.linearHertz ?? 5.0;
667
+ const linearDampingRatio = joint.linearDampingRatio ?? joint.damping ?? 1.0;
668
+ const angularHertz = joint.angularHertz ?? 5.0;
669
+ const angularDampingRatio = joint.angularDampingRatio ?? joint.angularDamping ?? joint.damping ?? 1.0;
670
+ let linBiasRate = 0;
671
+ let linMassScale = 1;
672
+ let linImpulseScale = 0;
673
+ let angBiasVel = { x: 0, y: 0, z: 0 };
674
+ let angMassScale = 1;
675
+ let angImpulseScale = 0;
646
676
  let angMassMat;
647
677
  let angMassDiag;
648
678
  let broken = false;
@@ -671,10 +701,17 @@ function grabJoint(joint, bodyIndex, warm) {
671
701
  const skewR = skewMat3(r);
672
702
  const ang = mat3Multiply(mat3Multiply(skewR, ctx.invInertia[bodyIndex]), skewR);
673
703
  kInv = invertMat3(mat3Sub(linear, ang));
674
- const damping = joint.damping ?? 1;
675
- desiredVel = scaleVec3(subVec3(target, worldAnchor), damping / ctx.dt);
704
+ const linSoft = (joint.linearHertz !== undefined || joint.linearDampingRatio !== undefined)
705
+ ? makeSoft(linearHertz, linearDampingRatio, ctx.dt)
706
+ : undefined;
707
+ linBiasRate = linSoft ? linSoft.biasRate : (joint.damping ?? 1) / ctx.dt;
708
+ linMassScale = linSoft ? linSoft.massScale : 1.0;
709
+ linImpulseScale = linSoft ? linSoft.impulseScale : 0;
676
710
  ctx.applyToBody(bodyIndex, r, state.impulse);
677
711
  if (targetRotation !== undefined) {
712
+ const angSoft = makeSoft(angularHertz, angularDampingRatio, ctx.dt);
713
+ angMassScale = angSoft.massScale;
714
+ angImpulseScale = angSoft.impulseScale;
678
715
  const errorRotation = quatMultiply(quatConjugate(targetRotation), body.q);
679
716
  const sign = errorRotation.w < 0 ? -1 : 1;
680
717
  const error = {
@@ -682,7 +719,8 @@ function grabJoint(joint, bodyIndex, warm) {
682
719
  y: 2 * sign * errorRotation.y,
683
720
  z: 2 * sign * errorRotation.z,
684
721
  };
685
- desiredAngVel = scaleVec3(error, -angularDamping / ctx.dt);
722
+ // Box3D soft constraint bias rate: biasRate * dt <= 1.0 always, preventing single-step overshoot
723
+ angBiasVel = clampMagnitude(scaleVec3(error, -angSoft.biasRate), 10.0);
686
724
  const invI = ctx.invInertia[bodyIndex];
687
725
  const fullInv = invertMat3(invI);
688
726
  const isSingular = fullInv[0] === 0 && fullInv[4] === 0 && fullInv[8] === 0;
@@ -705,22 +743,34 @@ function grabJoint(joint, bodyIndex, warm) {
705
743
  return;
706
744
  }
707
745
  const body = ctx.bodies[bodyIndex];
746
+ const worldAnchor = addVec3(body.c, r);
747
+ const posError = subVec3(target, worldAnchor);
748
+ const desiredVel = scaleVec3(posError, linBiasRate);
708
749
  const currentVel = addVec3(body.v, crossVec3(body.w, r));
709
- const dImpulse = mulMat3Vec(kInv, subVec3(desiredVel, currentVel));
750
+ const deltaV = subVec3(desiredVel, currentVel);
751
+ const rawImpulse = mulMat3Vec(kInv, deltaV);
752
+ const dImpulse = subVec3(scaleVec3(rawImpulse, linMassScale), scaleVec3(state.impulse, linImpulseScale));
710
753
  const target2 = addVec3(state.impulse, dImpulse);
711
754
  const clamped = clampMagnitude(target2, maxImpulse * ctx.dt);
712
755
  const applied = subVec3(clamped, state.impulse);
713
756
  state.impulse = clamped;
714
757
  ctx.applyToBody(bodyIndex, r, applied);
715
758
  if (targetRotation !== undefined && maxTorque > 0) {
716
- const deltaW = subVec3(desiredAngVel, body.w);
717
- const dTorque = angMassMat !== undefined
759
+ const deltaW = subVec3(angBiasVel, body.w);
760
+ const rawTorque = angMassMat !== undefined
718
761
  ? mulMat3Vec(angMassMat, deltaW)
719
762
  : { x: deltaW.x * angMassDiag.x, y: deltaW.y * angMassDiag.y, z: deltaW.z * angMassDiag.z };
763
+ const dTorque = subVec3(scaleVec3(rawTorque, angMassScale), scaleVec3(torqueImpulse, angImpulseScale));
720
764
  const targetTorque = addVec3(torqueImpulse, dTorque);
721
765
  const clampedTorque = clampMagnitude(targetTorque, maxTorque * ctx.dt);
722
- const appliedTorque = subVec3(clampedTorque, torqueImpulse);
723
- torqueImpulse = clampedTorque;
766
+ let appliedTorque = subVec3(clampedTorque, torqueImpulse);
767
+ // Safeguard angular acceleration: clamp appliedTorque so Delta omega <= 0.5 rad/substep
768
+ const dw = mulMat3Vec(ctx.invInertia[bodyIndex], appliedTorque);
769
+ const dwLen = solverSqrt(dotVec3(dw, dw));
770
+ if (dwLen > 0.5) {
771
+ appliedTorque = scaleVec3(appliedTorque, 0.5 / dwLen);
772
+ }
773
+ torqueImpulse = addVec3(torqueImpulse, appliedTorque);
724
774
  ctx.applyAngularToBody(bodyIndex, appliedTorque);
725
775
  }
726
776
  },
@@ -766,6 +816,12 @@ function validateJoint(joint) {
766
816
  if (joint.damping !== undefined && (!Number.isFinite(joint.damping) || joint.damping < 0)) {
767
817
  throw new Error(`physics3d joint ${joint.id}: damping must be non-negative`);
768
818
  }
819
+ if (joint.linearHertz !== undefined && (!Number.isFinite(joint.linearHertz) || joint.linearHertz <= 0)) {
820
+ throw new Error(`physics3d joint ${joint.id}: linearHertz must be positive`);
821
+ }
822
+ if (joint.linearDampingRatio !== undefined && (!Number.isFinite(joint.linearDampingRatio) || joint.linearDampingRatio < 0)) {
823
+ throw new Error(`physics3d joint ${joint.id}: linearDampingRatio must be non-negative`);
824
+ }
769
825
  if (joint.targetRotation !== undefined) {
770
826
  const target = joint.targetRotation;
771
827
  if (!Number.isFinite(target.x) || !Number.isFinite(target.y) || !Number.isFinite(target.z) || !Number.isFinite(target.w)
@@ -784,6 +840,12 @@ function validateJoint(joint) {
784
840
  if (joint.angularDamping !== undefined && (!Number.isFinite(joint.angularDamping) || joint.angularDamping < 0)) {
785
841
  throw new Error(`physics3d joint ${joint.id}: angularDamping must be non-negative`);
786
842
  }
843
+ if (joint.angularHertz !== undefined && (!Number.isFinite(joint.angularHertz) || joint.angularHertz <= 0)) {
844
+ throw new Error(`physics3d joint ${joint.id}: angularHertz must be positive`);
845
+ }
846
+ if (joint.angularDampingRatio !== undefined && (!Number.isFinite(joint.angularDampingRatio) || joint.angularDampingRatio < 0)) {
847
+ throw new Error(`physics3d joint ${joint.id}: angularDampingRatio must be non-negative`);
848
+ }
787
849
  if (joint.breakDistance !== undefined && (!Number.isFinite(joint.breakDistance) || joint.breakDistance <= 0)) {
788
850
  throw new Error(`physics3d joint ${joint.id}: breakDistance must be positive`);
789
851
  }
@@ -141,6 +141,9 @@ export function worldInvInertia(rot, invI) {
141
141
  * identity so no NaN reaches state.
142
142
  */
143
143
  export function integrateQuat(q, w, dt) {
144
+ if (!Number.isFinite(dt) || dt <= 0 || !Number.isFinite(w.x) || !Number.isFinite(w.y) || !Number.isFinite(w.z)) {
145
+ return normalizeQuat(q);
146
+ }
144
147
  const hx = w.x * 0.5 * dt;
145
148
  const hy = w.y * 0.5 * dt;
146
149
  const hz = w.z * 0.5 * dt;
@@ -148,18 +151,34 @@ export function integrateQuat(q, w, dt) {
148
151
  const ny = q.y + (hy * q.w + hz * q.x - hx * q.z);
149
152
  const nz = q.z + (hz * q.w + hx * q.y - hy * q.x);
150
153
  const nw = q.w + (-hx * q.x - hy * q.y - hz * q.z);
151
- const length = solverSqrt(nx * nx + ny * ny + nz * nz + nw * nw);
152
- if (length === 0) {
153
- return { x: 0, y: 0, z: 0, w: 1 };
154
+ const maxComp = Math.max(Math.abs(nx), Math.abs(ny), Math.abs(nz), Math.abs(nw));
155
+ if (!Number.isFinite(maxComp) || maxComp === 0) {
156
+ return normalizeQuat(q);
157
+ }
158
+ const sx = nx / maxComp;
159
+ const sy = ny / maxComp;
160
+ const sz = nz / maxComp;
161
+ const sw = nw / maxComp;
162
+ const length = solverSqrt(sx * sx + sy * sy + sz * sz + sw * sw);
163
+ if (!Number.isFinite(length) || length === 0) {
164
+ return normalizeQuat(q);
154
165
  }
155
- return { x: nx / length, y: ny / length, z: nz / length, w: nw / length };
166
+ return { x: sx / length, y: sy / length, z: sz / length, w: sw / length };
156
167
  }
157
168
  export function normalizeQuat(q) {
158
- const length = solverSqrt(q.x * q.x + q.y * q.y + q.z * q.z + q.w * q.w);
159
- if (length === 0) {
169
+ const maxComp = Math.max(Math.abs(q.x), Math.abs(q.y), Math.abs(q.z), Math.abs(q.w));
170
+ if (!Number.isFinite(maxComp) || maxComp === 0) {
171
+ return { x: 0, y: 0, z: 0, w: 1 };
172
+ }
173
+ const sx = q.x / maxComp;
174
+ const sy = q.y / maxComp;
175
+ const sz = q.z / maxComp;
176
+ const sw = q.w / maxComp;
177
+ const length = solverSqrt(sx * sx + sy * sy + sz * sz + sw * sw);
178
+ if (!Number.isFinite(length) || length === 0) {
160
179
  return { x: 0, y: 0, z: 0, w: 1 };
161
180
  }
162
- return { x: q.x / length, y: q.y / length, z: q.z / length, w: q.w / length };
181
+ return { x: sx / length, y: sy / length, z: sz / length, w: sw / length };
163
182
  }
164
183
  export function quatConjugate(q) {
165
184
  return { x: -q.x, y: -q.y, z: -q.z, w: q.w };
@@ -249,6 +249,24 @@ export interface SolverWarmPoint {
249
249
  readonly tangentImpulse1: number;
250
250
  readonly tangentImpulse2: number;
251
251
  }
252
+ export interface DeterministicPhysicsAabb3D {
253
+ readonly minX: number;
254
+ readonly minY: number;
255
+ readonly minZ: number;
256
+ readonly maxX: number;
257
+ readonly maxY: number;
258
+ readonly maxZ: number;
259
+ }
260
+ export interface DeterministicFluidVolume3D {
261
+ readonly id: string;
262
+ readonly bounds: DeterministicPhysicsAabb3D;
263
+ readonly density?: number;
264
+ readonly linearDrag?: number;
265
+ readonly angularDrag?: number;
266
+ readonly flowVelocity?: Point3D;
267
+ readonly layer?: number;
268
+ readonly mask?: number;
269
+ }
252
270
  export interface SolverStepOptions {
253
271
  readonly dt: number;
254
272
  readonly gravityY: number;
@@ -262,6 +280,7 @@ export interface SolverStepOptions {
262
280
  readonly joints?: readonly SolverJointSolve[];
263
281
  /** Packed index pairs (min * WARM_KEY_BASE + max) whose contacts are suppressed (jointed pairs). */
264
282
  readonly noCollidePairs?: ReadonlySet<number>;
283
+ readonly fluids?: readonly DeterministicFluidVolume3D[];
265
284
  }
266
285
  export declare function combinedContactFriction(frictionA10: number, frictionB10: number): number;
267
286
  export interface SolverContactImpulse {
@@ -1435,6 +1435,286 @@ function takeConstraint() {
1435
1435
  constraintPool.high += 1;
1436
1436
  return slot;
1437
1437
  }
1438
+ function computeSubmergence(b, bounds) {
1439
+ let points = [];
1440
+ let volume = 0;
1441
+ let yMin = b.p.y;
1442
+ let yMax = b.p.y;
1443
+ let xMin = b.p.x;
1444
+ let xMax = b.p.x;
1445
+ let zMin = b.p.z;
1446
+ let zMax = b.p.z;
1447
+ const shape = b.shape;
1448
+ if (shape.kind === 'box') {
1449
+ const h = shape.half;
1450
+ volume = 8 * h.x * h.y * h.z;
1451
+ const corners = [
1452
+ { x: -h.x, y: -h.y, z: -h.z },
1453
+ { x: h.x, y: -h.y, z: -h.z },
1454
+ { x: -h.x, y: h.y, z: -h.z },
1455
+ { x: h.x, y: h.y, z: -h.z },
1456
+ { x: -h.x, y: -h.y, z: h.z },
1457
+ { x: h.x, y: -h.y, z: h.z },
1458
+ { x: -h.x, y: h.y, z: h.z },
1459
+ { x: h.x, y: h.y, z: h.z },
1460
+ ];
1461
+ points = corners.map((c) => addVec3(b.p, rotatePointByQuat(b.q, c)));
1462
+ xMin = points[0].x;
1463
+ xMax = points[0].x;
1464
+ yMin = points[0].y;
1465
+ yMax = points[0].y;
1466
+ zMin = points[0].z;
1467
+ zMax = points[0].z;
1468
+ for (let i = 1; i < points.length; i += 1) {
1469
+ const p = points[i];
1470
+ if (p.x < xMin)
1471
+ xMin = p.x;
1472
+ if (p.x > xMax)
1473
+ xMax = p.x;
1474
+ if (p.y < yMin)
1475
+ yMin = p.y;
1476
+ if (p.y > yMax)
1477
+ yMax = p.y;
1478
+ if (p.z < zMin)
1479
+ zMin = p.z;
1480
+ if (p.z > zMax)
1481
+ zMax = p.z;
1482
+ }
1483
+ }
1484
+ else if (shape.kind === 'sphere') {
1485
+ const r = shape.radius;
1486
+ volume = (4 / 3) * Math.PI * r * r * r;
1487
+ xMin = b.p.x - r;
1488
+ xMax = b.p.x + r;
1489
+ yMin = b.p.y - r;
1490
+ yMax = b.p.y + r;
1491
+ zMin = b.p.z - r;
1492
+ zMax = b.p.z + r;
1493
+ points = [b.p];
1494
+ }
1495
+ else if (shape.kind === 'capsule') {
1496
+ const r = shape.radius;
1497
+ const hh = shape.halfHeight;
1498
+ volume = (4 / 3) * Math.PI * r * r * r + 2 * Math.PI * r * r * hh;
1499
+ const pTop = addVec3(b.p, rotatePointByQuat(b.q, { x: 0, y: hh, z: 0 }));
1500
+ const pBot = addVec3(b.p, rotatePointByQuat(b.q, { x: 0, y: -hh, z: 0 }));
1501
+ xMin = Math.min(pTop.x, pBot.x) - r;
1502
+ xMax = Math.max(pTop.x, pBot.x) + r;
1503
+ yMin = Math.min(pTop.y, pBot.y) - r;
1504
+ yMax = Math.max(pTop.y, pBot.y) + r;
1505
+ zMin = Math.min(pTop.z, pBot.z) - r;
1506
+ zMax = Math.max(pTop.z, pBot.z) + r;
1507
+ points = [pBot, pTop];
1508
+ }
1509
+ else if (shape.kind === 'hull') {
1510
+ const verts = shape.data.vertices;
1511
+ volume = 1.0;
1512
+ points = verts.map((v) => addVec3(b.p, rotatePointByQuat(b.q, v)));
1513
+ if (points.length > 0) {
1514
+ xMin = points[0].x;
1515
+ xMax = points[0].x;
1516
+ yMin = points[0].y;
1517
+ yMax = points[0].y;
1518
+ zMin = points[0].z;
1519
+ zMax = points[0].z;
1520
+ for (let i = 1; i < points.length; i += 1) {
1521
+ const p = points[i];
1522
+ if (p.x < xMin)
1523
+ xMin = p.x;
1524
+ if (p.x > xMax)
1525
+ xMax = p.x;
1526
+ if (p.y < yMin)
1527
+ yMin = p.y;
1528
+ if (p.y > yMax)
1529
+ yMax = p.y;
1530
+ if (p.z < zMin)
1531
+ zMin = p.z;
1532
+ if (p.z > zMax)
1533
+ zMax = p.z;
1534
+ }
1535
+ volume = Math.max(0.001, (xMax - xMin) * (yMax - yMin) * (zMax - zMin) * 0.5);
1536
+ }
1537
+ }
1538
+ else if (shape.kind === 'compound') {
1539
+ volume = 0;
1540
+ for (const child of shape.children) {
1541
+ const childCenter = addVec3(b.p, rotatePointByQuat(b.q, child.offset));
1542
+ const childQ = quatMultiply(b.q, child.q);
1543
+ if (child.shape.kind === 'box') {
1544
+ const h = child.shape.half;
1545
+ volume += 8 * h.x * h.y * h.z;
1546
+ const corners = [
1547
+ { x: -h.x, y: -h.y, z: -h.z },
1548
+ { x: h.x, y: -h.y, z: -h.z },
1549
+ { x: -h.x, y: h.y, z: -h.z },
1550
+ { x: h.x, y: h.y, z: -h.z },
1551
+ { x: -h.x, y: -h.y, z: h.z },
1552
+ { x: h.x, y: -h.y, z: h.z },
1553
+ { x: -h.x, y: h.y, z: h.z },
1554
+ { x: h.x, y: h.y, z: h.z },
1555
+ ];
1556
+ points.push(...corners.map((c) => addVec3(childCenter, rotatePointByQuat(childQ, c))));
1557
+ }
1558
+ else if (child.shape.kind === 'sphere') {
1559
+ const r = child.shape.radius;
1560
+ volume += (4 / 3) * Math.PI * r * r * r;
1561
+ points.push(childCenter, { x: childCenter.x - r, y: childCenter.y, z: childCenter.z }, { x: childCenter.x + r, y: childCenter.y, z: childCenter.z }, { x: childCenter.x, y: childCenter.y - r, z: childCenter.z }, { x: childCenter.x, y: childCenter.y + r, z: childCenter.z }, { x: childCenter.x, y: childCenter.y, z: childCenter.z - r }, { x: childCenter.x, y: childCenter.y, z: childCenter.z + r });
1562
+ }
1563
+ else if (child.shape.kind === 'capsule') {
1564
+ const r = child.shape.radius;
1565
+ const hh = child.shape.halfHeight;
1566
+ volume += (4 / 3) * Math.PI * r * r * r + 2 * Math.PI * r * r * hh;
1567
+ const capTop = addVec3(childCenter, rotatePointByQuat(childQ, { x: 0, y: hh, z: 0 }));
1568
+ const capBot = addVec3(childCenter, rotatePointByQuat(childQ, { x: 0, y: -hh, z: 0 }));
1569
+ points.push(capTop, capBot, { x: capTop.x - r, y: capTop.y, z: capTop.z }, { x: capTop.x + r, y: capTop.y, z: capTop.z }, { x: capTop.x, y: capTop.y - r, z: capTop.z }, { x: capTop.x, y: capTop.y + r, z: capTop.z }, { x: capTop.x, y: capTop.y, z: capTop.z - r }, { x: capTop.x, y: capTop.y, z: capTop.z + r }, { x: capBot.x - r, y: capBot.y, z: capBot.z }, { x: capBot.x + r, y: capBot.y, z: capBot.z }, { x: capBot.x, y: capBot.y - r, z: capBot.z }, { x: capBot.x, y: capBot.y + r, z: capBot.z }, { x: capBot.x, y: capBot.y, z: capBot.z - r }, { x: capBot.x, y: capBot.y, z: capBot.z + r });
1570
+ }
1571
+ else if (child.shape.kind === 'hull') {
1572
+ const verts = child.shape.data.vertices;
1573
+ const hullPoints = verts.map((v) => addVec3(childCenter, rotatePointByQuat(childQ, v)));
1574
+ points.push(...hullPoints);
1575
+ if (hullPoints.length > 0) {
1576
+ let hxMin = hullPoints[0].x;
1577
+ let hxMax = hullPoints[0].x;
1578
+ let hyMin = hullPoints[0].y;
1579
+ let hyMax = hullPoints[0].y;
1580
+ let hzMin = hullPoints[0].z;
1581
+ let hzMax = hullPoints[0].z;
1582
+ for (let i = 1; i < hullPoints.length; i += 1) {
1583
+ const p = hullPoints[i];
1584
+ if (p.x < hxMin)
1585
+ hxMin = p.x;
1586
+ if (p.x > hxMax)
1587
+ hxMax = p.x;
1588
+ if (p.y < hyMin)
1589
+ hyMin = p.y;
1590
+ if (p.y > hyMax)
1591
+ hyMax = p.y;
1592
+ if (p.z < hzMin)
1593
+ hzMin = p.z;
1594
+ if (p.z > hzMax)
1595
+ hzMax = p.z;
1596
+ }
1597
+ volume += Math.max(0.001, (hxMax - hxMin) * (hyMax - hyMin) * (hzMax - hzMin) * 0.5);
1598
+ }
1599
+ }
1600
+ }
1601
+ if (points.length > 0) {
1602
+ xMin = points[0].x;
1603
+ xMax = points[0].x;
1604
+ yMin = points[0].y;
1605
+ yMax = points[0].y;
1606
+ zMin = points[0].z;
1607
+ zMax = points[0].z;
1608
+ for (let i = 1; i < points.length; i += 1) {
1609
+ const p = points[i];
1610
+ if (p.x < xMin)
1611
+ xMin = p.x;
1612
+ if (p.x > xMax)
1613
+ xMax = p.x;
1614
+ if (p.y < yMin)
1615
+ yMin = p.y;
1616
+ if (p.y > yMax)
1617
+ yMax = p.y;
1618
+ if (p.z < zMin)
1619
+ zMin = p.z;
1620
+ if (p.z > zMax)
1621
+ zMax = p.z;
1622
+ }
1623
+ }
1624
+ }
1625
+ if (xMax < bounds.minX || xMin > bounds.maxX || zMax < bounds.minZ || zMin > bounds.maxZ) {
1626
+ return undefined;
1627
+ }
1628
+ if (yMax <= bounds.minY || yMin >= bounds.maxY) {
1629
+ return undefined;
1630
+ }
1631
+ const yBottom = Math.max(bounds.minY, yMin);
1632
+ const yTop = Math.min(bounds.maxY, yMax);
1633
+ const span = Math.max(1e-6, yMax - yMin);
1634
+ const fraction = Math.min(1.0, Math.max(0.0, (yTop - yBottom) / span));
1635
+ let cbX = b.c.x;
1636
+ let cbY = (yBottom + yTop) / 2;
1637
+ let cbZ = b.c.z;
1638
+ if (fraction >= 1.0) {
1639
+ // Fully submerged: center of buoyancy coincides with body centroid (no phantom righting torque)
1640
+ cbX = b.c.x;
1641
+ cbY = b.c.y;
1642
+ cbZ = b.c.z;
1643
+ }
1644
+ else {
1645
+ // Partially submerged: depth-weighted average of submerged sample points relative to fluid surface
1646
+ let sumWeight = 0;
1647
+ let sumX = 0;
1648
+ let sumZ = 0;
1649
+ const surfaceY = bounds.maxY;
1650
+ for (const p of points) {
1651
+ if (p.y < surfaceY) {
1652
+ const depth = surfaceY - p.y;
1653
+ sumWeight += depth;
1654
+ sumX += p.x * depth;
1655
+ sumZ += p.z * depth;
1656
+ }
1657
+ }
1658
+ if (sumWeight > 1e-9) {
1659
+ cbX = sumX / sumWeight;
1660
+ cbZ = sumZ / sumWeight;
1661
+ }
1662
+ }
1663
+ return {
1664
+ fraction,
1665
+ centerOfBuoyancy: { x: cbX, y: cbY, z: cbZ },
1666
+ displacedVolume: volume,
1667
+ };
1668
+ }
1669
+ function applyFluidForces(bodies, invInertia, fluids, gravityY, dt) {
1670
+ for (let index = 0; index < bodies.length; index += 1) {
1671
+ const b = bodies[index];
1672
+ if (!solid(b) || !b.isDynamic) {
1673
+ continue;
1674
+ }
1675
+ for (const fluid of fluids) {
1676
+ if (fluid.layer !== undefined && fluid.mask !== undefined) {
1677
+ if ((fluid.mask & b.layer) === 0 || (b.mask & fluid.layer) === 0) {
1678
+ continue;
1679
+ }
1680
+ }
1681
+ const submergence = computeSubmergence(b, fluid.bounds);
1682
+ if (submergence === undefined || submergence.fraction <= 0) {
1683
+ continue;
1684
+ }
1685
+ const { fraction, centerOfBuoyancy, displacedVolume } = submergence;
1686
+ const fluidDensity = fluid.density ?? 1.0;
1687
+ const linearDragCoeff = fluid.linearDrag ?? 1.0;
1688
+ const angularDragCoeff = fluid.angularDrag ?? 0.5;
1689
+ const flowVel = fluid.flowVelocity ?? ZERO;
1690
+ const effG = gravityY * b.gravityScale;
1691
+ if (Math.abs(effG) > 0 && b.invMassVec.y > 0) {
1692
+ // Buoyant force opposes effective gravity
1693
+ const buoyantForceY = -effG * fluidDensity * displacedVolume * fraction;
1694
+ const buoyantAccelY = buoyantForceY * b.invMassVec.y;
1695
+ b.v.y += buoyantAccelY * dt;
1696
+ const rB = subVec3(centerOfBuoyancy, b.c);
1697
+ const torque = {
1698
+ x: -rB.z * buoyantForceY,
1699
+ y: 0,
1700
+ z: rB.x * buoyantForceY,
1701
+ };
1702
+ const angImpulse = scaleVec3(torque, dt);
1703
+ let dw = mulMat3Vec(invInertia[index], angImpulse);
1704
+ const dwLen = solverSqrt(dotVec3(dw, dw));
1705
+ if (dwLen > 0.5) {
1706
+ dw = scaleVec3(dw, 0.5 / dwLen);
1707
+ }
1708
+ b.w = addVec3(b.w, dw);
1709
+ }
1710
+ const vRel = subVec3(b.v, flowVel);
1711
+ const linDragFactor = 1 / (1 + linearDragCoeff * fraction * dt);
1712
+ b.v = addVec3(scaleVec3(vRel, linDragFactor), flowVel);
1713
+ const angDragFactor = 1 / (1 + angularDragCoeff * fraction * dt);
1714
+ b.w = scaleVec3(b.w, angDragFactor);
1715
+ }
1716
+ }
1717
+ }
1438
1718
  /** Runs one deterministic rigid-body step in place over `bodies`. */
1439
1719
  export function solveRigidStep(bodies, options) {
1440
1720
  /* istanbul ignore next -- worlds are validated far below this bound; guards the packed warm-cache key from silent index collisions. */
@@ -1589,6 +1869,25 @@ export function solveRigidStep(bodies, options) {
1589
1869
  b.lowVelFrames = 0;
1590
1870
  }
1591
1871
  }
1872
+ if (options.fluids && options.fluids.length > 0) {
1873
+ for (const b of bodies) {
1874
+ if (b.isDynamic && b.sleeping) {
1875
+ for (const f of options.fluids) {
1876
+ if (f.layer !== undefined && f.mask !== undefined) {
1877
+ if ((f.mask & b.layer) === 0 || (b.mask & f.layer) === 0) {
1878
+ continue;
1879
+ }
1880
+ }
1881
+ const sub = computeSubmergence(b, f.bounds);
1882
+ if (sub && sub.fraction > 0) {
1883
+ b.sleeping = false;
1884
+ b.lowVelFrames = 0;
1885
+ break;
1886
+ }
1887
+ }
1888
+ }
1889
+ }
1890
+ }
1592
1891
  for (const m of manifolds) {
1593
1892
  wakeIfContacted(bodies[m.a], bodies[m.b]);
1594
1893
  wakeIfContacted(bodies[m.b], bodies[m.a]);
@@ -1713,6 +2012,9 @@ export function solveRigidStep(bodies, options) {
1713
2012
  b.v.y = b.v.y + options.gravityY * b.gravityScale * h;
1714
2013
  }
1715
2014
  }
2015
+ if (options.fluids && options.fluids.length > 0) {
2016
+ applyFluidForces(bodies, invInertia, options.fluids, options.gravityY, h);
2017
+ }
1716
2018
  for (const joint of joints) {
1717
2019
  joint.prepare(jointCtx);
1718
2020
  }
@@ -116,3 +116,16 @@ export interface SplitDeterministicVoxelChunkOptions3D {
116
116
  * occupied count, and whether any of its voxels touch the specified anchor faces.
117
117
  */
118
118
  export declare function splitDeterministicVoxelChunk3D(chunk: DeterministicVoxelChunk3D, options?: SplitDeterministicVoxelChunkOptions3D): readonly VoxelSubChunk3D[];
119
+ export interface DeterministicVoxelCompoundSplitResult3D {
120
+ readonly compounds: readonly (DeterministicVoxelCompoundShape3D | undefined)[];
121
+ readonly preservedBoxesCount: number;
122
+ readonly recookedBoxesCount: number;
123
+ }
124
+ /**
125
+ * Incrementally slices a parent compound collider across partitioned sub-chunks
126
+ * from {@link splitDeterministicVoxelChunk3D}.
127
+ * Preserves intact child box colliders that fall entirely within a sub-chunk's volume
128
+ * without re-cooking, rebasing their offset coordinates to sub-chunk local space.
129
+ * Only re-cooks severed/split boundary voxels that were not covered by preserved boxes.
130
+ */
131
+ export declare function splitDeterministicVoxelCompoundCollider3D(parentCompound: DeterministicVoxelCompoundShape3D, subChunks: readonly VoxelSubChunk3D[]): DeterministicVoxelCompoundSplitResult3D;
@@ -693,3 +693,127 @@ export function splitDeterministicVoxelChunk3D(chunk, options) {
693
693
  });
694
694
  return subChunks;
695
695
  }
696
+ /**
697
+ * Incrementally slices a parent compound collider across partitioned sub-chunks
698
+ * from {@link splitDeterministicVoxelChunk3D}.
699
+ * Preserves intact child box colliders that fall entirely within a sub-chunk's volume
700
+ * without re-cooking, rebasing their offset coordinates to sub-chunk local space.
701
+ * Only re-cooks severed/split boundary voxels that were not covered by preserved boxes.
702
+ */
703
+ export function splitDeterministicVoxelCompoundCollider3D(parentCompound, subChunks) {
704
+ if (subChunks.length === 0) {
705
+ return {
706
+ compounds: [],
707
+ preservedBoxesCount: 0,
708
+ recookedBoxesCount: 0,
709
+ };
710
+ }
711
+ let totalPreservedBoxes = 0;
712
+ let totalRecookedBoxes = 0;
713
+ const compounds = [];
714
+ for (const subChunk of subChunks) {
715
+ const chunk = subChunk.chunk;
716
+ const cellSize = chunk.cellSize;
717
+ const scX = subChunk.offsetCells.x;
718
+ const scY = subChunk.offsetCells.y;
719
+ const scZ = subChunk.offsetCells.z;
720
+ const maxBoundX = scX + chunk.dimX - 1;
721
+ const maxBoundY = scY + chunk.dimY - 1;
722
+ const maxBoundZ = scZ + chunk.dimZ - 1;
723
+ const totalCells = chunk.dimX * chunk.dimY * chunk.dimZ;
724
+ const coveredInSubChunk = new Uint8Array(totalCells);
725
+ const preservedChildren = [];
726
+ for (const child of parentCompound.children) {
727
+ if (child.shape.type !== 'box') {
728
+ continue;
729
+ }
730
+ const boxMinX = Math.round((child.offset.x - child.shape.halfX) / cellSize + 0.5);
731
+ const boxMaxX = Math.round((child.offset.x + child.shape.halfX) / cellSize - 0.5);
732
+ const boxMinY = Math.round((child.offset.y - child.shape.halfY) / cellSize + 0.5);
733
+ const boxMaxY = Math.round((child.offset.y + child.shape.halfY) / cellSize - 0.5);
734
+ const boxMinZ = Math.round((child.offset.z - child.shape.halfZ) / cellSize + 0.5);
735
+ const boxMaxZ = Math.round((child.offset.z + child.shape.halfZ) / cellSize - 0.5);
736
+ if (boxMinX >= scX && boxMaxX <= maxBoundX &&
737
+ boxMinY >= scY && boxMaxY <= maxBoundY &&
738
+ boxMinZ >= scZ && boxMaxZ <= maxBoundZ) {
739
+ let isIntact = true;
740
+ for (let bz = boxMinZ; bz <= boxMaxZ && isIntact; bz++) {
741
+ for (let by = boxMinY; by <= boxMaxY && isIntact; by++) {
742
+ for (let bx = boxMinX; bx <= boxMaxX; bx++) {
743
+ const lx = bx - scX;
744
+ const ly = by - scY;
745
+ const lz = bz - scZ;
746
+ const idx = lx + chunk.dimX * (ly + chunk.dimY * lz);
747
+ if (chunk.occupancy[idx] === 0) {
748
+ isIntact = false;
749
+ break;
750
+ }
751
+ }
752
+ }
753
+ }
754
+ if (isIntact) {
755
+ for (let bz = boxMinZ; bz <= boxMaxZ; bz++) {
756
+ for (let by = boxMinY; by <= boxMaxY; by++) {
757
+ for (let bx = boxMinX; bx <= boxMaxX; bx++) {
758
+ const lx = bx - scX;
759
+ const ly = by - scY;
760
+ const lz = bz - scZ;
761
+ const idx = lx + chunk.dimX * (ly + chunk.dimY * lz);
762
+ coveredInSubChunk[idx] = 1;
763
+ }
764
+ }
765
+ }
766
+ preservedChildren.push({
767
+ shape: child.shape,
768
+ offset: {
769
+ x: child.offset.x - scX * cellSize,
770
+ y: child.offset.y - scY * cellSize,
771
+ z: child.offset.z - scZ * cellSize,
772
+ },
773
+ ...(child.quarterTurns !== undefined ? { quarterTurns: child.quarterTurns } : {}),
774
+ });
775
+ totalPreservedBoxes += 1;
776
+ }
777
+ }
778
+ }
779
+ let uncoveredCount = 0;
780
+ for (let i = 0; i < totalCells; i++) {
781
+ if (chunk.occupancy[i] !== 0 && coveredInSubChunk[i] === 0) {
782
+ uncoveredCount += 1;
783
+ }
784
+ }
785
+ let recookedChildren = [];
786
+ if (uncoveredCount > 0) {
787
+ const uncoveredOccupancy = new Uint8Array(totalCells);
788
+ for (let i = 0; i < totalCells; i++) {
789
+ if (chunk.occupancy[i] !== 0 && coveredInSubChunk[i] === 0) {
790
+ uncoveredOccupancy[i] = 1;
791
+ }
792
+ }
793
+ const uncoveredChunk = {
794
+ dimX: chunk.dimX,
795
+ dimY: chunk.dimY,
796
+ dimZ: chunk.dimZ,
797
+ cellSize: chunk.cellSize,
798
+ occupancy: uncoveredOccupancy,
799
+ };
800
+ recookedChildren = cookSubregionBoxes(uncoveredChunk, 0, 0, 0, chunk.dimX - 1, chunk.dimY - 1, chunk.dimZ - 1);
801
+ totalRecookedBoxes += recookedChildren.length;
802
+ }
803
+ const finalChildren = [...preservedChildren, ...recookedChildren];
804
+ if (finalChildren.length === 0) {
805
+ compounds.push(undefined);
806
+ }
807
+ else {
808
+ compounds.push({
809
+ type: 'compound',
810
+ children: finalChildren,
811
+ });
812
+ }
813
+ }
814
+ return {
815
+ compounds,
816
+ preservedBoxesCount: totalPreservedBoxes,
817
+ recookedBoxesCount: totalRecookedBoxes,
818
+ };
819
+ }
@@ -1,5 +1,7 @@
1
1
  import { type DeterministicPhysicsMaterial } from './deterministic-primitives.js';
2
2
  import { type Point3D } from './physics3d-shared.js';
3
+ import { type DeterministicPhysicsAabb3D, type DeterministicFluidVolume3D } from './physics3d-solver.js';
4
+ export type { DeterministicPhysicsAabb3D, DeterministicFluidVolume3D };
3
5
  import { type DeterministicPhysicsJoint3D } from './physics3d-joints.js';
4
6
  export type DeterministicPhysicsPrimitiveShape3D = {
5
7
  readonly type: 'box';
@@ -364,6 +366,8 @@ export interface DeterministicPhysicsStepResult3D {
364
366
  readonly contacts: readonly DeterministicPhysicsContactEvent3D[];
365
367
  /** Joint ids that broke this step (newly, sorted); sticky once broken. */
366
368
  readonly brokenJointIds: readonly string[];
369
+ /** Alias for brokenJointIds requested for ergonomics parity. */
370
+ readonly brokenJoints: readonly string[];
367
371
  readonly scheduledQueryResults: readonly {
368
372
  readonly id: string;
369
373
  readonly hits: readonly DeterministicPhysicsHit3D[];
@@ -480,6 +484,7 @@ export interface DeterministicPhysicsStepOptions3D {
480
484
  readonly computeChecksum?: boolean;
481
485
  readonly computeBroadphasePairs?: boolean;
482
486
  readonly pairs?: readonly (readonly [number, number])[];
487
+ readonly fluids?: readonly DeterministicFluidVolume3D[];
483
488
  }
484
489
  export declare function stepDeterministicPhysicsWorld3D(world: DeterministicPhysicsWorld3D, options?: DeterministicPhysicsStepOptions3D): DeterministicPhysicsStepResult3D;
485
490
  /** O(1) body lookup backed by the per-world query-index cache. */
package/dist/physics3d.js CHANGED
@@ -693,7 +693,52 @@ export function setDeterministicBodyVelocity3D(world, bodyId, velocity, angularV
693
693
  });
694
694
  return { ...world, bodies: normalizeBodies(bodies, 'trusted') };
695
695
  }
696
+ function validateFluidVolume(fluid) {
697
+ if (typeof fluid.id !== 'string' || fluid.id.length === 0) {
698
+ throw new Error('physics3d fluid: id must be a non-empty string');
699
+ }
700
+ const bounds = fluid.bounds;
701
+ if (!bounds || typeof bounds !== 'object') {
702
+ throw new Error(`physics3d fluid ${fluid.id}: bounds must be an object`);
703
+ }
704
+ const { minX, minY, minZ, maxX, maxY, maxZ } = bounds;
705
+ if (!Number.isFinite(minX) ||
706
+ !Number.isFinite(minY) ||
707
+ !Number.isFinite(minZ) ||
708
+ !Number.isFinite(maxX) ||
709
+ !Number.isFinite(maxY) ||
710
+ !Number.isFinite(maxZ)) {
711
+ throw new Error(`physics3d fluid ${fluid.id}: bounds must contain finite numbers`);
712
+ }
713
+ if (minX > maxX) {
714
+ throw new Error(`physics3d fluid ${fluid.id}: minX must not exceed maxX`);
715
+ }
716
+ if (minY > maxY) {
717
+ throw new Error(`physics3d fluid ${fluid.id}: minY must not exceed maxY`);
718
+ }
719
+ if (minZ > maxZ) {
720
+ throw new Error(`physics3d fluid ${fluid.id}: minZ must not exceed maxZ`);
721
+ }
722
+ if (fluid.density !== undefined && (!Number.isFinite(fluid.density) || fluid.density <= 0)) {
723
+ throw new Error(`physics3d fluid ${fluid.id}: density must be positive`);
724
+ }
725
+ if (fluid.linearDrag !== undefined && (!Number.isFinite(fluid.linearDrag) || fluid.linearDrag < 0)) {
726
+ throw new Error(`physics3d fluid ${fluid.id}: linearDrag must be non-negative`);
727
+ }
728
+ if (fluid.angularDrag !== undefined && (!Number.isFinite(fluid.angularDrag) || fluid.angularDrag < 0)) {
729
+ throw new Error(`physics3d fluid ${fluid.id}: angularDrag must be non-negative`);
730
+ }
731
+ if (fluid.flowVelocity !== undefined) {
732
+ const { x, y, z } = fluid.flowVelocity;
733
+ if (!Number.isFinite(x) || !Number.isFinite(y) || !Number.isFinite(z)) {
734
+ throw new Error(`physics3d fluid ${fluid.id}: flowVelocity must be finite`);
735
+ }
736
+ }
737
+ }
696
738
  export function stepDeterministicPhysicsWorld3D(world, options = {}) {
739
+ for (const fluid of options.fluids ?? []) {
740
+ validateFluidVolume(fluid);
741
+ }
697
742
  const scheduledQueries = sortScheduledQueries(options.scheduledQueries ?? []);
698
743
  const prePhysicsScheduledQueryResults = scheduledQueries
699
744
  .filter((query) => query.phase === 'pre-physics')
@@ -711,7 +756,7 @@ export function stepDeterministicPhysicsWorld3D(world, options = {}) {
711
756
  }
712
757
  const dt = options.dtTicks ?? 1;
713
758
  const gravityY = options.gravityY ?? 0;
714
- const rigid = runRigidSolver(world, dt, gravityY, velocityIterations, substeps, options.joints ?? [], options.pairs);
759
+ const rigid = runRigidSolver(world, dt, gravityY, velocityIterations, substeps, options.joints ?? [], options.pairs, options.fluids);
715
760
  const constrained = solveConstraints3D(rigid.bodies, options.constraints ?? []);
716
761
  const solvedBodies = constrained.bodies;
717
762
  // Dynamic-involved solid-collision events come from the solver's manifolds (so
@@ -783,6 +828,7 @@ export function stepDeterministicPhysicsWorld3D(world, options = {}) {
783
828
  world: nextWorld,
784
829
  contacts: events,
785
830
  brokenJointIds: rigid.brokenJointIds,
831
+ brokenJoints: rigid.brokenJointIds,
786
832
  scheduledQueryResults,
787
833
  prePhysicsScheduledQueryResults,
788
834
  postPhysicsScheduledQueryResults,
@@ -2236,7 +2282,7 @@ function sleepingRebuildIsNoop(body, sb) {
2236
2282
  && eq(q(sb.w.x), body.angularVel.x) && eq(q(sb.w.y), body.angularVel.y) && eq(q(sb.w.z), body.angularVel.z)
2237
2283
  && eq(sb.lowVelFrames, body.lowVelFrames);
2238
2284
  }
2239
- function runRigidSolver(world, dt, gravityY, velocityIterations, substeps, joints, pairs) {
2285
+ function runRigidSolver(world, dt, gravityY, velocityIterations, substeps, joints, pairs, fluids) {
2240
2286
  const source = world.bodies;
2241
2287
  const indexById = new Map(source.map((body, index) => [body.id, index]));
2242
2288
  const solverBodies = source.map((body, index) => buildSolverBody(body, index));
@@ -2281,6 +2327,7 @@ function runRigidSolver(world, dt, gravityY, velocityIterations, substeps, joint
2281
2327
  warm,
2282
2328
  pairs,
2283
2329
  joints: solverJoints,
2330
+ fluids,
2284
2331
  });
2285
2332
  // Post-solve safety clamp: no fast body may end past its TOI along its motion.
2286
2333
  for (const [index, clamp] of fastClamps) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@series-inc/rundot-syncplay",
3
- "version": "5.28.0-beta.3",
3
+ "version": "5.28.0-beta.4",
4
4
  "description": "Kinetix orchestration, rollback, replay, late-join, and deterministic networking for RUN.game",
5
5
  "repository": {
6
6
  "type": "git",
@@ -168,7 +168,7 @@
168
168
  "test:core": "node --expose-gc --import tsx --test --test-concurrency=1 tests/canonical.test.ts tests/canonical-portability.test.ts tests/runtime-session.test.ts tests/kinetix-runtime-session.test.ts tests/runtime-support.test.ts tests/runtime-guards.test.ts tests/r3f-render-adapter.test.ts tests/snapshot-cost.test.ts tests/sdkSession.test.ts tests/input-authority.test.ts tests/session-wire.test.ts tests/runtime-room-metadata.test.ts tests/session-snapshot.test.ts tests/authority-room.test.ts tests/networked-client-snapshot.test.ts tests/runner.test.ts tests/testing.test.ts tests/browser-session.test.ts tests/secret-config.test.ts tests/secret-protocol.test.ts tests/secret-authority.test.ts tests/secret-client.test.ts tests/late-join-checksum.test.ts tests/late-join-input-backfill.test.ts tests/replay-scoring.test.ts tests/bot-backfill.test.ts tests/latency-simulation.test.ts tests/production-input-budget.test.ts tests/time-sync.test.ts tests/match-log.test.ts tests/spectator.test.ts tests/synctest.test.ts tests/instant-replay.test.ts tests/session-promotion.test.ts tests/physics3d-broadphase-stats.test.ts tests/entrypoints.test.ts tests/export-surface.test.ts tests/module-seam.test.ts tests/module-contract-portability.test.ts tests/module-identity.test.ts tests/module-corpus.test.ts tests/component-model.test.ts tests/component-model-rollback.test.ts tests/component-model-integration.test.ts tests/actors-ai.test.ts tests/character-3d.test.ts tests/character-3d-certification.test.ts tests/combat-modules.test.ts tests/combat-modules-integration.test.ts tests/firearm.test.ts tests/gameplay-effects.test.ts tests/spatial-interaction.test.ts tests/stats-abilities.test.ts tests/inventory-equipment.test.ts tests/inventory-equipment-certification.test.ts tests/platformer-2d.test.ts tests/platformer-2d-certification.test.ts tests/module-promotion.test.ts tests/module-docs.test.ts tests/module-bundle-budget.test.ts tests/published-side-effects.test.ts tests/host-timing.test.ts tests/test-manifest.test.ts tests/coarse-region.test.ts tests/input-codec.test.ts tests/command-ingress.test.ts tests/session-reconfigure.test.ts tests/prediction-mode.test.ts tests/ephemeral-channel.test.ts tests/runner-surface.test.ts tests/runner-harness.test.ts tests/effects-adapter-bounds.test.ts tests/simulated-match-secrets.test.ts tests/run-glue-surface.test.mjs core/tests/core-boundary.test.mjs tests/math-core-parity.test.ts tests/math-profile.test.ts tests/movement3d-kcc-unit-contract.test.ts tests/movement3d-static-ground.test.ts",
169
169
  "test:sdk-contract": "node --import tsx --test tests/sdk-runtime-contract.test.ts",
170
170
  "test:soak": "tsx --test tests/runtime-soak.test.ts",
171
- "test:extended": "tsx --test tests/kcc-vehicle-characterization.test.ts tests/animation.test.ts tests/asset-cooking.test.ts tests/backend-proof-checksum-vectors.test.ts tests/bot-documents.test.ts tests/bot-primitives.test.ts tests/collider-cooking.test.ts tests/collision.test.ts tests/command-timeline.test.ts tests/config-bundle.test.ts tests/cosmetic-physics3d.test.ts tests/deterministic-primitives.test.ts tests/ecs-lite.test.ts tests/effects-adapter.test.ts tests/engine-runtime.test.ts tests/errors.test.ts tests/events.test.ts tests/input-button.test.ts tests/load-128-players.test.ts tests/math.test.ts tests/movement.test.ts tests/movement3d.test.ts tests/multiclient-authority.test.ts tests/navigation.test.ts tests/noise.test.ts tests/physics-cert.test.ts tests/physics2d.test.ts tests/physics3d-ccd.test.ts tests/physics3d-coverage.test.ts tests/physics3d-debug-wireframe.test.ts tests/physics3d-destruction-cert.test.ts tests/physics3d-forces-and-contacts.test.ts tests/physics3d-geometry-limits.test.ts tests/physics3d-heightfield.test.ts tests/physics3d-hull.test.ts tests/physics3d-joints.test.ts tests/physics3d-kinematic-mesh.test.ts tests/physics3d-kinematic-poses.test.ts tests/physics3d-kinematic-transforms.test.ts tests/physics3d-mass.test.ts tests/physics3d-materials.test.ts tests/physics3d-mesh-onesided.test.ts tests/physics3d-queries-capsule-childindex.test.ts tests/physics3d-query-index.test.ts tests/physics3d-recipes.test.ts tests/physics3d-rotation-convention.test.ts tests/physics3d-shape-builders.test.ts tests/physics3d-shared.test.ts tests/physics3d-sleeping-manifold-reuse.test.ts tests/physics3d-solver.test.ts tests/physics3d-static-memoization.test.ts tests/physics3d-static-overlap-memo.test.ts tests/physics3d-cylinder-hull.test.ts tests/physics3d-stacking.test.ts tests/physics3d-toi.test.ts tests/physics3d-vehicle.test.ts tests/physics3d-voxel-carving.test.ts tests/physics3d-voxel-dda.test.ts tests/physics3d-voxel-split.test.ts tests/physics3d-voxel.test.ts tests/physics3d-world-edit.test.ts tests/physics3d.test.ts tests/physics-vector-generator.test.ts tests/random.test.ts tests/rollback-history.test.ts tests/schema-artifacts.test.ts tests/schema-codegen.test.ts tests/schema-dsl-binary.test.ts tests/schema.test.ts tests/signals.test.ts tests/sparse-set.test.ts tests/static-checker.test.ts tests/system-lifecycle.test.ts tests/wire-canonical-sentinels.test.ts tests/ws-frame.test.ts",
171
+ "test:extended": "tsx --test tests/kcc-vehicle-characterization.test.ts tests/animation.test.ts tests/asset-cooking.test.ts tests/backend-proof-checksum-vectors.test.ts tests/bot-documents.test.ts tests/bot-primitives.test.ts tests/collider-cooking.test.ts tests/collision.test.ts tests/command-timeline.test.ts tests/config-bundle.test.ts tests/cosmetic-physics3d.test.ts tests/deterministic-primitives.test.ts tests/ecs-lite.test.ts tests/effects-adapter.test.ts tests/engine-runtime.test.ts tests/errors.test.ts tests/events.test.ts tests/input-button.test.ts tests/load-128-players.test.ts tests/math.test.ts tests/movement.test.ts tests/movement3d.test.ts tests/multiclient-authority.test.ts tests/navigation.test.ts tests/noise.test.ts tests/physics-cert.test.ts tests/physics2d.test.ts tests/physics3d-buoyancy.test.ts tests/physics3d-ccd.test.ts tests/physics3d-coverage.test.ts tests/physics3d-debug-wireframe.test.ts tests/physics3d-destruction-cert.test.ts tests/physics3d-forces-and-contacts.test.ts tests/physics3d-geometry-limits.test.ts tests/physics3d-heightfield.test.ts tests/physics3d-hull.test.ts tests/physics3d-joints.test.ts tests/physics3d-kinematic-mesh.test.ts tests/physics3d-kinematic-poses.test.ts tests/physics3d-kinematic-transforms.test.ts tests/physics3d-mass.test.ts tests/physics3d-materials.test.ts tests/physics3d-mesh-onesided.test.ts tests/physics3d-queries-capsule-childindex.test.ts tests/physics3d-query-index.test.ts tests/physics3d-recipes.test.ts tests/physics3d-rotation-convention.test.ts tests/physics3d-shape-builders.test.ts tests/physics3d-shared.test.ts tests/physics3d-sleeping-manifold-reuse.test.ts tests/physics3d-solver.test.ts tests/physics3d-static-memoization.test.ts tests/physics3d-static-overlap-memo.test.ts tests/physics3d-cylinder-hull.test.ts tests/physics3d-stacking.test.ts tests/physics3d-toi.test.ts tests/physics3d-vehicle.test.ts tests/physics3d-voxel-carving.test.ts tests/physics3d-voxel-dda.test.ts tests/physics3d-voxel-split.test.ts tests/physics3d-voxel.test.ts tests/physics3d-world-edit.test.ts tests/physics3d.test.ts tests/physics-vector-generator.test.ts tests/random.test.ts tests/rollback-history.test.ts tests/schema-artifacts.test.ts tests/schema-codegen.test.ts tests/schema-dsl-binary.test.ts tests/schema.test.ts tests/signals.test.ts tests/sparse-set.test.ts tests/static-checker.test.ts tests/system-lifecycle.test.ts tests/wire-canonical-sentinels.test.ts tests/ws-frame.test.ts",
172
172
  "test:ci": "npm run build && npm run test",
173
173
  "build:native": "node core/scripts/build-native.mjs",
174
174
  "build:mobile-native": "node core/scripts/build-mobile-native.mjs",