@series-inc/rundot-syncplay 5.28.0-beta.0 → 5.28.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/browser.d.ts CHANGED
@@ -24,8 +24,8 @@ export { createNetworkedSyncplayClient, prepareNetworkedSyncplayTransport } from
24
24
  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
- export { 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, raycastDeterministicVoxelChunk3D } from './physics3d-voxel.js';
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';
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, DeterministicPhysicsHit3D, DeterministicPhysicsHullShape3D, DeterministicPhysicsImpulseApplication3D, DeterministicPhysicsOverlapQuery3D, DeterministicPhysicsPrimitiveShape3D, DeterministicPhysicsQuaternion3D, DeterministicPhysicsQueryOptions3D, DeterministicPhysicsRaycastQuery3D, DeterministicPhysicsScheduledQuery3D, DeterministicPhysicsShape3D, DeterministicPhysicsShapeCastQuery3D, DeterministicPhysicsStepResult3D, DeterministicPhysicsVec3, DeterministicPhysicsWorld3D, DeterministicPhysicsWorldEdit3D } from './physics3d.js';
70
- export type { DeterministicVoxelChunk3D, DeterministicVoxelCompoundShape3D, DeterministicVoxelRayHit3D, VoxelCarveRegion3D } from './physics3d-voxel.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';
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
@@ -25,8 +25,8 @@ export { createNetworkedSyncplayClient, prepareNetworkedSyncplayTransport } from
25
25
  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
- export { 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, raycastDeterministicVoxelChunk3D } from './physics3d-voxel.js';
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';
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
@@ -12,14 +12,16 @@ export type { RecordedChecksum, RecordedCommandFrame, RecordedDebugEvent, Record
12
12
  export type { ReplayRuntimeFactory, ReplayVerificationConfig, ReplayVerificationExecution } from './replay.js';
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
- export { 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, raycastDeterministicVoxelChunk3D, } from './physics3d-voxel.js';
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';
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
- export type { DeterministicContactCacheEntry3D, DeterministicJointCacheEntry3D, DeterministicKinematicPose3D, DeterministicKinematicTransformBuffer3D, DeterministicPhysicsActivePair3D, DeterministicPhysicsAxisLock3D, DeterministicPhysicsBody3D, DeterministicPhysicsCallbackMask3D, DeterministicPhysicsCompoundChild3D, DeterministicPhysicsCompoundChildShape3D, DeterministicPhysicsConstraint3D, DeterministicPhysicsContactDetail3D, DeterministicPhysicsContactEvent3D, DeterministicPhysicsDetailedHit3D, DeterministicPhysicsHit3D, DeterministicPhysicsHullShape3D, DeterministicPhysicsImpulseApplication3D, DeterministicPhysicsOverlapQuery3D, DeterministicPhysicsPrimitiveShape3D, DeterministicPhysicsQuaternion3D, DeterministicPhysicsQueryOptions3D, DeterministicPhysicsRaycastQuery3D, DeterministicPhysicsScheduledQuery3D, DeterministicPhysicsShape3D, DeterministicPhysicsShapeCastQuery3D, DeterministicPhysicsStepResult3D, DeterministicPhysicsVec3, DeterministicPhysicsWorld3D, DeterministicPhysicsWorldEdit3D, } from './physics3d.js';
21
- export type { DeterministicVoxelChunk3D, DeterministicVoxelCompoundShape3D, DeterministicVoxelRayHit3D, VoxelCarveRegion3D, } from './physics3d-voxel.js';
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';
22
23
  export type { PhysicsWorldDebugMesh, PhysicsWorldDebugMeshOptions } from './debug-wireframe.js';
23
24
  export type { DeterministicPhysicsJoint3D } from './physics3d-joints.js';
24
25
  export type { DeterministicVehicle3D, DeterministicVehicleImpulse3D, DeterministicVehicleInput3D, DeterministicVehicleStepOptions3D, DeterministicVehicleStepResult3D, DeterministicVehicleWheel3D, DeterministicVehicleWheelTelemetry3D, } from './physics3d-vehicle.js';
25
26
  export type { DeterministicPhysicsMaterial } from './deterministic-primitives.js';
27
+ export type { DeterministicBinarySerializer, DeterministicDslDeclaration, DeterministicDslDeclarationKind, DeterministicDslDictionaryEntry, DeterministicDslDictionaryFieldKind, DeterministicDslField, DeterministicDslFieldKind, DeterministicDslFixedArrayFieldKind, DeterministicDslListFieldKind, DeterministicDslScalarFieldKind, DeterministicDslScalarValue, DeterministicDslSchema, DeterministicDslSetFieldKind, DeterministicDslValue, } from './schema-dsl-binary.js';
package/dist/creator.js CHANGED
@@ -9,8 +9,9 @@ export { runSyncplaySynctest } from './synctest.js';
9
9
  // create/step/edit, impulses, queries, vehicles, and voxel-chunk collider
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
- export { 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, raycastDeterministicVoxelChunk3D, } from './physics3d-voxel.js';
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';
14
14
  export { generatePhysicsWorldDebugMesh } from './debug-wireframe.js';
15
15
  export { cylinderHullVertices } from './physics3d-hull.js';
16
16
  export { stepDeterministicVehicle3D } from './physics3d-vehicle.js';
17
+ export { createDeterministicBinarySerializer, deterministicDslMaxPlayers, parseDeterministicSchemaDsl, stableDeterministicDslSchemaJson, } from './schema-dsl-binary.js';
package/dist/index.d.ts CHANGED
@@ -27,8 +27,8 @@ export { createNetworkedSyncplayClient, prepareNetworkedSyncplayTransport } from
27
27
  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
- export { 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, raycastDeterministicVoxelChunk3D } from './physics3d-voxel.js';
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';
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, DeterministicPhysicsHit3D, DeterministicPhysicsHullShape3D, DeterministicPhysicsImpulseApplication3D, DeterministicPhysicsOverlapQuery3D, DeterministicPhysicsPrimitiveShape3D, DeterministicPhysicsQuaternion3D, DeterministicPhysicsQueryOptions3D, DeterministicPhysicsRaycastQuery3D, DeterministicPhysicsScheduledQuery3D, DeterministicPhysicsShape3D, DeterministicPhysicsShapeCastQuery3D, DeterministicPhysicsStepResult3D, DeterministicPhysicsVec3, DeterministicPhysicsWorld3D, DeterministicPhysicsWorldEdit3D } from './physics3d.js';
82
- export type { DeterministicVoxelChunk3D, DeterministicVoxelCompoundShape3D, DeterministicVoxelRayHit3D, VoxelCarveRegion3D } from './physics3d-voxel.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';
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
@@ -28,8 +28,8 @@ export { createNetworkedSyncplayClient, prepareNetworkedSyncplayTransport } from
28
28
  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
- export { 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, raycastDeterministicVoxelChunk3D } from './physics3d-voxel.js';
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';
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';
@@ -87,7 +87,8 @@ function splitChunk(chunk) {
87
87
  const sx = axis === 'x' ? x + from : x;
88
88
  const sy = axis === 'y' ? y + from : y;
89
89
  const sz = axis === 'z' ? z + from : z;
90
- occupancy[x + dimX * (y + dimY * z)] = chunk.occupancy[sx + chunk.dimX * (sy + chunk.dimY * sz)];
90
+ const val = chunk.occupancy[sx + chunk.dimX * (sy + chunk.dimY * sz)];
91
+ occupancy[x + dimX * (y + dimY * z)] = (val !== undefined && val !== 0) ? 1 : 0;
91
92
  }
92
93
  }
93
94
  }
@@ -96,7 +97,13 @@ function splitChunk(chunk) {
96
97
  return { lower: slice(0, cut), upper: slice(cut, dims[axis]), axis, offset: cut * chunk.cellSize };
97
98
  }
98
99
  function occupiedCount(chunk) {
99
- return chunk.occupancy.reduce((sum, cell) => sum + (cell !== 0 ? 1 : 0), 0);
100
+ let count = 0;
101
+ for (let i = 0; i < chunk.occupancy.length; i += 1) {
102
+ if (chunk.occupancy[i] !== 0) {
103
+ count += 1;
104
+ }
105
+ }
106
+ return count;
100
107
  }
101
108
  export function runDeterministicPhysics3DDestructionCert(options = {}) {
102
109
  const chunks = options.chunks ?? 64;
@@ -1,4 +1,5 @@
1
1
  import type { DeterministicPhysicsRaycastQuery3D, DeterministicPhysicsShape3D } from './physics3d.js';
2
+ export type VoxelOccupancy3D = readonly (0 | 1)[] | Uint8Array;
2
3
  export interface DeterministicVoxelChunk3D {
3
4
  readonly dimX: number;
4
5
  readonly dimY: number;
@@ -6,7 +7,7 @@ export interface DeterministicVoxelChunk3D {
6
7
  /** Edge length of one voxel in world units. Voxel (x,y,z) centers at (x·cellSize, y·cellSize, z·cellSize) in body-local space. */
7
8
  readonly cellSize: number;
8
9
  /** Row-major occupancy: occupancy[x + dimX * (y + dimY * z)]; non-zero = solid. */
9
- readonly occupancy: readonly (0 | 1)[];
10
+ readonly occupancy: VoxelOccupancy3D;
10
11
  }
11
12
  export interface DeterministicVoxelRayHit3D {
12
13
  readonly hit: boolean;
@@ -40,7 +41,8 @@ export declare function raycastDeterministicVoxelChunk3D(chunk: DeterministicVox
40
41
  * of the occupancy grid.
41
42
  */
42
43
  export declare function cookDeterministicVoxelChunkCollider3D(chunk: DeterministicVoxelChunk3D): DeterministicVoxelCompoundShape3D;
43
- export interface VoxelCarveRegion3D {
44
+ export interface VoxelBoxCarveRegion3D {
45
+ readonly type?: 'box';
44
46
  readonly minX: number;
45
47
  readonly minY: number;
46
48
  readonly minZ: number;
@@ -49,9 +51,29 @@ export interface VoxelCarveRegion3D {
49
51
  readonly maxZ: number;
50
52
  readonly operation: 'carve' | 'fill';
51
53
  }
54
+ export interface VoxelSphereCarveRegion3D {
55
+ readonly type: 'sphere';
56
+ readonly centerX: number;
57
+ readonly centerY: number;
58
+ readonly centerZ: number;
59
+ readonly radius: number;
60
+ readonly operation: 'carve' | 'fill';
61
+ }
62
+ export interface VoxelCapsuleCarveRegion3D {
63
+ readonly type: 'capsule';
64
+ readonly x0: number;
65
+ readonly y0: number;
66
+ readonly z0: number;
67
+ readonly x1: number;
68
+ readonly y1: number;
69
+ readonly z1: number;
70
+ readonly radius: number;
71
+ readonly operation: 'carve' | 'fill';
72
+ }
73
+ export type VoxelCarveRegion3D = VoxelBoxCarveRegion3D | VoxelSphereCarveRegion3D | VoxelCapsuleCarveRegion3D;
52
74
  /**
53
- * Carves or fills a 3D axis-aligned cell region in a voxel chunk occupancy grid.
54
- * Computes interval intersection with chunk dimensions, returning chunk unchanged
75
+ * Carves or fills a 3D region (box, sphere, or capsule) in a voxel chunk occupancy grid.
76
+ * Computes bounding box intersection with chunk dimensions, returning chunk unchanged
55
77
  * if disjoint or no-op.
56
78
  */
57
79
  export declare function carveDeterministicVoxelChunk3D(chunk: DeterministicVoxelChunk3D, region: VoxelCarveRegion3D): DeterministicVoxelChunk3D;
@@ -69,3 +91,28 @@ export declare function cookDeterministicVoxelSubregion3D(chunk: DeterministicVo
69
91
  readonly maxY: number;
70
92
  readonly maxZ: number;
71
93
  }): DeterministicVoxelCompoundShape3D | undefined;
94
+ /**
95
+ * Alias for {@link cookDeterministicVoxelSubregion3D}.
96
+ * Re-cooks dirty subregions of a voxel chunk collider after carving or modifying voxels.
97
+ */
98
+ export declare const recookDeterministicVoxelSubregion3D: typeof cookDeterministicVoxelSubregion3D;
99
+ export type VoxelAnchorFace = '+x' | '-x' | '+y' | '-y' | '+z' | '-z';
100
+ export interface VoxelSubChunk3D {
101
+ readonly chunk: DeterministicVoxelChunk3D;
102
+ readonly offsetCells: {
103
+ readonly x: number;
104
+ readonly y: number;
105
+ readonly z: number;
106
+ };
107
+ readonly occupiedCount: number;
108
+ readonly anchored: boolean;
109
+ }
110
+ export interface SplitDeterministicVoxelChunkOptions3D {
111
+ readonly anchorFaces?: readonly VoxelAnchorFace[];
112
+ }
113
+ /**
114
+ * Partitions the non-zero voxels of a chunk into 6-connected components.
115
+ * Each component is tightly cropped into a sub-chunk with its origin offset in parent cells,
116
+ * occupied count, and whether any of its voxels touch the specified anchor faces.
117
+ */
118
+ export declare function splitDeterministicVoxelChunk3D(chunk: DeterministicVoxelChunk3D, options?: SplitDeterministicVoxelChunkOptions3D): readonly VoxelSubChunk3D[];
@@ -82,7 +82,7 @@ export function raycastDeterministicVoxelChunk3D(chunk, query) {
82
82
  let hitDistanceGrid = tMin;
83
83
  while (currentX >= 0 && currentX < dimX && currentY >= 0 && currentY < dimY && currentZ >= 0 && currentZ < dimZ) {
84
84
  const cellIndex = currentX + dimX * (currentY + dimY * currentZ);
85
- if (occupancy[cellIndex] === 1) {
85
+ if (occupancy[cellIndex] !== 0) {
86
86
  const worldDistance = hitDistanceGrid * cellSize;
87
87
  if (worldDistance > maxDistance) {
88
88
  return undefined;
@@ -155,10 +155,12 @@ export function cookDeterministicVoxelChunkCollider3D(chunk) {
155
155
  if (chunk.occupancy.length !== dimX * dimY * dimZ) {
156
156
  throw new Error(`cookDeterministicVoxelChunkCollider3D: occupancy length ${chunk.occupancy.length} must equal dimX*dimY*dimZ (${dimX * dimY * dimZ})`);
157
157
  }
158
- for (let index = 0; index < chunk.occupancy.length; index += 1) {
159
- const cell = chunk.occupancy[index];
160
- if (cell !== 0 && cell !== 1) {
161
- throw new Error(`cookDeterministicVoxelChunkCollider3D: occupancy[${index}] must be 0 or 1, received ${cell}`);
158
+ if (Array.isArray(chunk.occupancy)) {
159
+ for (let index = 0; index < chunk.occupancy.length; index += 1) {
160
+ const cell = chunk.occupancy[index];
161
+ if (cell !== 0 && cell !== 1) {
162
+ throw new Error(`cookDeterministicVoxelChunkCollider3D: occupancy[${index}] must be 0 or 1, received ${cell}`);
163
+ }
162
164
  }
163
165
  }
164
166
  const solid = (x, y, z) => chunk.occupancy[x + dimX * (y + dimY * z)] !== 0;
@@ -228,27 +230,100 @@ export function cookDeterministicVoxelChunkCollider3D(chunk) {
228
230
  return { type: 'compound', children };
229
231
  }
230
232
  /**
231
- * Carves or fills a 3D axis-aligned cell region in a voxel chunk occupancy grid.
232
- * Computes interval intersection with chunk dimensions, returning chunk unchanged
233
+ * Carves or fills a 3D region (box, sphere, or capsule) in a voxel chunk occupancy grid.
234
+ * Computes bounding box intersection with chunk dimensions, returning chunk unchanged
233
235
  * if disjoint or no-op.
234
236
  */
235
237
  export function carveDeterministicVoxelChunk3D(chunk, region) {
236
- const { dimX, dimY, dimZ, cellSize, occupancy } = chunk;
237
- const startX = Math.max(0, Math.min(region.minX, region.maxX));
238
- const endX = Math.min(dimX - 1, Math.max(region.minX, region.maxX));
239
- const startY = Math.max(0, Math.min(region.minY, region.maxY));
240
- const endY = Math.min(dimY - 1, Math.max(region.minY, region.maxY));
241
- const startZ = Math.max(0, Math.min(region.minZ, region.maxZ));
242
- const endZ = Math.min(dimZ - 1, Math.max(region.minZ, region.maxZ));
238
+ const { dimX, dimY, dimZ, cellSize } = chunk;
239
+ let startX = 0;
240
+ let endX = dimX - 1;
241
+ let startY = 0;
242
+ let endY = dimY - 1;
243
+ let startZ = 0;
244
+ let endZ = dimZ - 1;
245
+ let isInside;
246
+ if (region.type === 'sphere') {
247
+ const { centerX, centerY, centerZ, radius } = region;
248
+ if (radius <= 0) {
249
+ return chunk;
250
+ }
251
+ const rSq = radius * radius;
252
+ startX = Math.max(0, Math.floor(centerX - radius));
253
+ endX = Math.min(dimX - 1, Math.ceil(centerX + radius));
254
+ startY = Math.max(0, Math.floor(centerY - radius));
255
+ endY = Math.min(dimY - 1, Math.ceil(centerY + radius));
256
+ startZ = Math.max(0, Math.floor(centerZ - radius));
257
+ endZ = Math.min(dimZ - 1, Math.ceil(centerZ + radius));
258
+ isInside = (x, y, z) => {
259
+ const dx = x - centerX;
260
+ const dy = y - centerY;
261
+ const dz = z - centerZ;
262
+ return dx * dx + dy * dy + dz * dz <= rSq;
263
+ };
264
+ }
265
+ else if (region.type === 'capsule') {
266
+ const { x0, y0, z0, x1, y1, z1, radius } = region;
267
+ if (radius <= 0) {
268
+ return chunk;
269
+ }
270
+ const rSq = radius * radius;
271
+ startX = Math.max(0, Math.floor(Math.min(x0, x1) - radius));
272
+ endX = Math.min(dimX - 1, Math.ceil(Math.max(x0, x1) + radius));
273
+ startY = Math.max(0, Math.floor(Math.min(y0, y1) - radius));
274
+ endY = Math.min(dimY - 1, Math.ceil(Math.max(y0, y1) + radius));
275
+ startZ = Math.max(0, Math.floor(Math.min(z0, z1) - radius));
276
+ endZ = Math.min(dimZ - 1, Math.ceil(Math.max(z0, z1) + radius));
277
+ const segX = x1 - x0;
278
+ const segY = y1 - y0;
279
+ const segZ = z1 - z0;
280
+ const segLenSq = segX * segX + segY * segY + segZ * segZ;
281
+ if (segLenSq < 1e-9) {
282
+ isInside = (x, y, z) => {
283
+ const dx = x - x0;
284
+ const dy = y - y0;
285
+ const dz = z - z0;
286
+ return dx * dx + dy * dy + dz * dz <= rSq;
287
+ };
288
+ }
289
+ else {
290
+ isInside = (x, y, z) => {
291
+ const px = x - x0;
292
+ const py = y - y0;
293
+ const pz = z - z0;
294
+ const dot = px * segX + py * segY + pz * segZ;
295
+ const t = Math.max(0, Math.min(1, dot / segLenSq));
296
+ const projX = x0 + t * segX;
297
+ const projY = y0 + t * segY;
298
+ const projZ = z0 + t * segZ;
299
+ const dx = x - projX;
300
+ const dy = y - projY;
301
+ const dz = z - projZ;
302
+ return dx * dx + dy * dy + dz * dz <= rSq;
303
+ };
304
+ }
305
+ }
306
+ else {
307
+ startX = Math.max(0, Math.min(region.minX, region.maxX));
308
+ endX = Math.min(dimX - 1, Math.max(region.minX, region.maxX));
309
+ startY = Math.max(0, Math.min(region.minY, region.maxY));
310
+ endY = Math.min(dimY - 1, Math.max(region.minY, region.maxY));
311
+ startZ = Math.max(0, Math.min(region.minZ, region.maxZ));
312
+ endZ = Math.min(dimZ - 1, Math.max(region.minZ, region.maxZ));
313
+ isInside = () => true;
314
+ }
243
315
  if (startX > endX || startY > endY || startZ > endZ) {
244
316
  return chunk;
245
317
  }
246
318
  const targetVal = region.operation === 'carve' ? 0 : 1;
247
319
  let changed = false;
248
- const nextOccupancy = [...occupancy];
320
+ const nextOccupancy = chunk.occupancy instanceof Uint8Array ? new Uint8Array(chunk.occupancy) : [...chunk.occupancy];
249
321
  for (let z = startZ; z <= endZ; z += 1) {
250
322
  for (let y = startY; y <= endY; y += 1) {
251
323
  for (let x = startX; x <= endX; x += 1) {
324
+ if (!isInside(x, y, z)) {
325
+ continue;
326
+ }
252
327
  const index = x + dimX * (y + dimY * z);
253
328
  if (nextOccupancy[index] !== targetVal) {
254
329
  nextOccupancy[index] = targetVal;
@@ -426,3 +501,195 @@ export function cookDeterministicVoxelSubregion3D(chunk, existingShape, dirtyReg
426
501
  children: finalChildren,
427
502
  };
428
503
  }
504
+ /**
505
+ * Alias for {@link cookDeterministicVoxelSubregion3D}.
506
+ * Re-cooks dirty subregions of a voxel chunk collider after carving or modifying voxels.
507
+ */
508
+ export const recookDeterministicVoxelSubregion3D = cookDeterministicVoxelSubregion3D;
509
+ /**
510
+ * Partitions the non-zero voxels of a chunk into 6-connected components.
511
+ * Each component is tightly cropped into a sub-chunk with its origin offset in parent cells,
512
+ * occupied count, and whether any of its voxels touch the specified anchor faces.
513
+ */
514
+ export function splitDeterministicVoxelChunk3D(chunk, options) {
515
+ const { dimX, dimY, dimZ, cellSize, occupancy } = chunk;
516
+ const totalCells = dimX * dimY * dimZ;
517
+ if (totalCells === 0) {
518
+ return [];
519
+ }
520
+ const anchorFaces = new Set(options?.anchorFaces ?? []);
521
+ const checkAnchored = (x, y, z) => {
522
+ if (anchorFaces.has('-x') && x === 0)
523
+ return true;
524
+ if (anchorFaces.has('+x') && x === dimX - 1)
525
+ return true;
526
+ if (anchorFaces.has('-y') && y === 0)
527
+ return true;
528
+ if (anchorFaces.has('+y') && y === dimY - 1)
529
+ return true;
530
+ if (anchorFaces.has('-z') && z === 0)
531
+ return true;
532
+ if (anchorFaces.has('+z') && z === dimZ - 1)
533
+ return true;
534
+ return false;
535
+ };
536
+ const visited = new Uint8Array(totalCells);
537
+ const queueIdx = new Int32Array(totalCells);
538
+ const subChunks = [];
539
+ const sliceSize = dimX * dimY;
540
+ for (let z = 0; z < dimZ; z += 1) {
541
+ for (let y = 0; y < dimY; y += 1) {
542
+ for (let x = 0; x < dimX; x += 1) {
543
+ const seedIdx = x + dimX * y + sliceSize * z;
544
+ if (visited[seedIdx] !== 0 || occupancy[seedIdx] === 0) {
545
+ continue;
546
+ }
547
+ let head = 0;
548
+ let tail = 0;
549
+ queueIdx[tail++] = seedIdx;
550
+ visited[seedIdx] = 1;
551
+ let minX = x;
552
+ let maxX = x;
553
+ let minY = y;
554
+ let maxY = y;
555
+ let minZ = z;
556
+ let maxZ = z;
557
+ let isAnchored = checkAnchored(x, y, z);
558
+ while (head < tail) {
559
+ const currIdx = queueIdx[head++];
560
+ const cz = Math.floor(currIdx / sliceSize);
561
+ const rem = currIdx % sliceSize;
562
+ const cy = Math.floor(rem / dimX);
563
+ const cx = rem % dimX;
564
+ if (cx < minX)
565
+ minX = cx;
566
+ if (cx > maxX)
567
+ maxX = cx;
568
+ if (cy < minY)
569
+ minY = cy;
570
+ if (cy > maxY)
571
+ maxY = cy;
572
+ if (cz < minZ)
573
+ minZ = cz;
574
+ if (cz > maxZ)
575
+ maxZ = cz;
576
+ if (!isAnchored && checkAnchored(cx, cy, cz)) {
577
+ isAnchored = true;
578
+ }
579
+ if (cx > 0) {
580
+ const nIdx = currIdx - 1;
581
+ if (visited[nIdx] === 0 && occupancy[nIdx] !== 0) {
582
+ visited[nIdx] = 1;
583
+ queueIdx[tail++] = nIdx;
584
+ }
585
+ }
586
+ if (cx + 1 < dimX) {
587
+ const nIdx = currIdx + 1;
588
+ if (visited[nIdx] === 0 && occupancy[nIdx] !== 0) {
589
+ visited[nIdx] = 1;
590
+ queueIdx[tail++] = nIdx;
591
+ }
592
+ }
593
+ if (cy > 0) {
594
+ const nIdx = currIdx - dimX;
595
+ if (visited[nIdx] === 0 && occupancy[nIdx] !== 0) {
596
+ visited[nIdx] = 1;
597
+ queueIdx[tail++] = nIdx;
598
+ }
599
+ }
600
+ if (cy + 1 < dimY) {
601
+ const nIdx = currIdx + dimX;
602
+ if (visited[nIdx] === 0 && occupancy[nIdx] !== 0) {
603
+ visited[nIdx] = 1;
604
+ queueIdx[tail++] = nIdx;
605
+ }
606
+ }
607
+ if (cz > 0) {
608
+ const nIdx = currIdx - sliceSize;
609
+ if (visited[nIdx] === 0 && occupancy[nIdx] !== 0) {
610
+ visited[nIdx] = 1;
611
+ queueIdx[tail++] = nIdx;
612
+ }
613
+ }
614
+ if (cz + 1 < dimZ) {
615
+ const nIdx = currIdx + sliceSize;
616
+ if (visited[nIdx] === 0 && occupancy[nIdx] !== 0) {
617
+ visited[nIdx] = 1;
618
+ queueIdx[tail++] = nIdx;
619
+ }
620
+ }
621
+ }
622
+ const componentCount = tail;
623
+ const subDimX = maxX - minX + 1;
624
+ const subDimY = maxY - minY + 1;
625
+ const subDimZ = maxZ - minZ + 1;
626
+ const subTotal = subDimX * subDimY * subDimZ;
627
+ const subSlice = subDimX * subDimY;
628
+ let subOccupancy;
629
+ if (occupancy instanceof Uint8Array) {
630
+ const typed = new Uint8Array(subTotal);
631
+ for (let i = 0; i < tail; i += 1) {
632
+ const vIdx = queueIdx[i];
633
+ const vz = Math.floor(vIdx / sliceSize);
634
+ const vrem = vIdx % sliceSize;
635
+ const vy = Math.floor(vrem / dimX);
636
+ const vx = vrem % dimX;
637
+ const targetSubIdx = (vx - minX) + subDimX * (vy - minY) + subSlice * (vz - minZ);
638
+ typed[targetSubIdx] = occupancy[vIdx];
639
+ }
640
+ subOccupancy = typed;
641
+ }
642
+ else {
643
+ const arr = new Array(subTotal).fill(0);
644
+ for (let i = 0; i < tail; i += 1) {
645
+ const vIdx = queueIdx[i];
646
+ const vz = Math.floor(vIdx / sliceSize);
647
+ const vrem = vIdx % sliceSize;
648
+ const vy = Math.floor(vrem / dimX);
649
+ const vx = vrem % dimX;
650
+ const targetSubIdx = (vx - minX) + subDimX * (vy - minY) + subSlice * (vz - minZ);
651
+ arr[targetSubIdx] = occupancy[vIdx] !== 0 ? 1 : 0;
652
+ }
653
+ subOccupancy = arr;
654
+ }
655
+ subChunks.push({
656
+ chunk: {
657
+ dimX: subDimX,
658
+ dimY: subDimY,
659
+ dimZ: subDimZ,
660
+ cellSize,
661
+ occupancy: subOccupancy,
662
+ },
663
+ offsetCells: { x: minX, y: minY, z: minZ },
664
+ occupiedCount: componentCount,
665
+ anchored: isAnchored,
666
+ });
667
+ }
668
+ }
669
+ }
670
+ subChunks.sort((a, b) => {
671
+ if (a.anchored !== b.anchored) {
672
+ return a.anchored ? -1 : 1;
673
+ }
674
+ if (a.offsetCells.z !== b.offsetCells.z) {
675
+ return a.offsetCells.z - b.offsetCells.z;
676
+ }
677
+ if (a.offsetCells.y !== b.offsetCells.y) {
678
+ return a.offsetCells.y - b.offsetCells.y;
679
+ }
680
+ if (a.offsetCells.x !== b.offsetCells.x) {
681
+ return a.offsetCells.x - b.offsetCells.x;
682
+ }
683
+ if (a.chunk.dimZ !== b.chunk.dimZ) {
684
+ return a.chunk.dimZ - b.chunk.dimZ;
685
+ }
686
+ if (a.chunk.dimY !== b.chunk.dimY) {
687
+ return a.chunk.dimY - b.chunk.dimY;
688
+ }
689
+ if (a.chunk.dimX !== b.chunk.dimX) {
690
+ return a.chunk.dimX - b.chunk.dimX;
691
+ }
692
+ return a.occupiedCount - b.occupiedCount;
693
+ });
694
+ return subChunks;
695
+ }
@@ -108,18 +108,28 @@ export interface DeterministicPhysicsBody3D {
108
108
  readonly x: number;
109
109
  readonly y: number;
110
110
  readonly z: number;
111
+ /**
112
+ * Linear velocity along the X axis in units per tick (NOT per second).
113
+ * The solver integrates per tick (`dtTicks`), so positions advance as `x += vx * dtTicks`.
114
+ */
111
115
  readonly vx: number;
116
+ /** Linear velocity along the Y axis in units per tick (NOT per second). */
112
117
  readonly vy: number;
118
+ /** Linear velocity along the Z axis in units per tick (NOT per second). */
113
119
  readonly vz: number;
114
120
  readonly rx?: number;
115
121
  readonly ry?: number;
116
122
  readonly rz?: number;
123
+ /** Angular velocity around the X axis in radians per tick (NOT per second). */
117
124
  readonly avx?: number;
125
+ /** Angular velocity around the Y axis in radians per tick (NOT per second). */
118
126
  readonly avy?: number;
127
+ /** Angular velocity around the Z axis in radians per tick (NOT per second). */
119
128
  readonly avz?: number;
120
129
  readonly inertia?: number;
121
130
  /** Full 6-DOF orientation (opt-in); bodies without it are rotation-locked through the rigid solver. */
122
131
  readonly orientation?: DeterministicPhysicsQuaternion3D;
132
+ /** Angular velocity vector in radians per tick (NOT per second). */
123
133
  readonly angularVel?: DeterministicPhysicsVec3;
124
134
  readonly mass?: number;
125
135
  readonly linearDamping?: number;
@@ -272,6 +282,9 @@ export interface DeterministicPhysicsContactEvent3D {
272
282
  readonly sensor: boolean;
273
283
  /** Accumulated normal impulse (quantized) from the rigid solver; 0 for triggers/exits. */
274
284
  readonly normalImpulse?: number;
285
+ readonly point?: Point3D;
286
+ readonly normal?: Point3D;
287
+ readonly closingSpeed?: number;
275
288
  }
276
289
  export interface DeterministicPhysicsContactDetail3D {
277
290
  readonly pair: string;
@@ -384,6 +397,19 @@ export interface DeterministicPhysicsImpulseApplication3D {
384
397
  * fast path for per-frame controller output (vehicles, KCC pushes).
385
398
  */
386
399
  export declare function applyDeterministicImpulses3D(world: DeterministicPhysicsWorld3D, impulses: readonly DeterministicPhysicsImpulseApplication3D[]): DeterministicPhysicsWorld3D;
400
+ export interface DeterministicPhysicsForceApplication3D {
401
+ readonly bodyId: string;
402
+ readonly force?: Point3D;
403
+ readonly torque?: Point3D;
404
+ readonly worldPoint?: Point3D;
405
+ }
406
+ /**
407
+ * Apply a batch of continuous deterministic forces and torques over a duration (defaulting to 1 dt tick),
408
+ * accumulating linear and angular delta-v and waking touched contact islands.
409
+ */
410
+ export declare function applyDeterministicForces3D(world: DeterministicPhysicsWorld3D, forces: readonly DeterministicPhysicsForceApplication3D[], options?: {
411
+ readonly dtTicks?: number;
412
+ }): DeterministicPhysicsWorld3D;
387
413
  export interface DeterministicPhysicsWorldEdit3D {
388
414
  readonly add?: readonly DeterministicPhysicsBody3D[];
389
415
  readonly remove?: readonly string[];
@@ -427,18 +453,35 @@ export declare function setDeterministicKinematicTransforms3D(world: Determinist
427
453
  export declare function applyDeterministicImpulse3D(world: DeterministicPhysicsWorld3D, bodyId: string, impulse: Point3D, worldPoint?: Point3D): DeterministicPhysicsWorld3D;
428
454
  /** Set a body's linear (and optional angular) velocity deterministically, waking its island. */
429
455
  export declare function setDeterministicBodyVelocity3D(world: DeterministicPhysicsWorld3D, bodyId: string, velocity: Point3D, angularVelocity?: Point3D): DeterministicPhysicsWorld3D;
430
- export declare function stepDeterministicPhysicsWorld3D(world: DeterministicPhysicsWorld3D, options?: {
456
+ /**
457
+ * Options for advancing the deterministic 3D physics world snapshot-to-snapshot.
458
+ */
459
+ export interface DeterministicPhysicsStepOptions3D {
460
+ /**
461
+ * Acceleration due to gravity along the Y axis, in units per tick squared (or dtTicks).
462
+ *
463
+ * NOTE ON TIME UNITS:
464
+ * `stepDeterministicPhysicsWorld3D` integrates per tick (with `dtTicks`, default 1 tick).
465
+ * Therefore, `gravityY` and rigid body velocities (`vx`, `vy`, `vz`, `angularVel`)
466
+ * are expressed per tick (units/tick, rad/tick), NOT per second.
467
+ * For example, at a 30 Hz tick rate with standard -9.8 m/s^2 gravity,
468
+ * `gravityY = -9.8 / (30 * 30)` ≈ `-0.0108889` units/tick^2.
469
+ */
431
470
  readonly gravityY?: number;
432
471
  readonly scheduledQueries?: readonly DeterministicPhysicsScheduledQuery3D[];
433
472
  readonly constraints?: readonly DeterministicPhysicsConstraint3D[];
434
473
  readonly joints?: readonly DeterministicPhysicsJoint3D[];
474
+ /**
475
+ * Step duration measured in ticks (default: 1).
476
+ */
435
477
  readonly dtTicks?: number;
436
478
  readonly velocityIterations?: number;
437
479
  readonly substeps?: number;
438
480
  readonly computeChecksum?: boolean;
439
481
  readonly computeBroadphasePairs?: boolean;
440
482
  readonly pairs?: readonly (readonly [number, number])[];
441
- }): DeterministicPhysicsStepResult3D;
483
+ }
484
+ export declare function stepDeterministicPhysicsWorld3D(world: DeterministicPhysicsWorld3D, options?: DeterministicPhysicsStepOptions3D): DeterministicPhysicsStepResult3D;
442
485
  /** O(1) body lookup backed by the per-world query-index cache. */
443
486
  export declare function deterministicPhysicsBodyById3D(world: DeterministicPhysicsWorld3D, bodyId: string): DeterministicPhysicsBody3D | undefined;
444
487
  export declare function __staticTreeBuildCount(): number;
package/dist/physics3d.js CHANGED
@@ -257,6 +257,125 @@ export function applyDeterministicImpulses3D(world, impulses) {
257
257
  });
258
258
  return { ...world, bodies: normalizeBodies(bodies, 'trusted') };
259
259
  }
260
+ /**
261
+ * Apply a batch of continuous deterministic forces and torques over a duration (defaulting to 1 dt tick),
262
+ * accumulating linear and angular delta-v and waking touched contact islands.
263
+ */
264
+ export function applyDeterministicForces3D(world, forces, options) {
265
+ if (forces.length === 0) {
266
+ return world;
267
+ }
268
+ if (options?.dtTicks !== undefined && options.dtTicks <= 0) {
269
+ throw new Error(`applyDeterministicForces3D: dtTicks must be a positive finite number, received ${options.dtTicks}`);
270
+ }
271
+ const dt = options?.dtTicks ?? 1;
272
+ const bodyById = new Map(world.bodies.map((body) => [body.id, body]));
273
+ const linearDelta = new Map();
274
+ const angularDelta = new Map();
275
+ const invInertiaByBody = new Map();
276
+ for (const entry of forces) {
277
+ if (entry.force !== undefined) {
278
+ assertFinitePoint(entry.force, 'applyDeterministicForces3D: force');
279
+ }
280
+ if (entry.torque !== undefined) {
281
+ assertFinitePoint(entry.torque, 'applyDeterministicForces3D: torque');
282
+ }
283
+ if (entry.worldPoint !== undefined) {
284
+ assertFinitePoint(entry.worldPoint, 'applyDeterministicForces3D: worldPoint');
285
+ }
286
+ const target = bodyById.get(entry.bodyId);
287
+ if (target === undefined) {
288
+ throw new Error(`applyDeterministicForces3D: unknown body ${entry.bodyId}`);
289
+ }
290
+ if (entry.force !== undefined) {
291
+ const mass = target.mass ?? 1;
292
+ const baseInvMass = target.kind === 'dynamic' ? 1 / mass : 0;
293
+ const lockLinear = target.lockLinear ?? {};
294
+ const previousLinear = linearDelta.get(entry.bodyId) ?? { x: 0, y: 0, z: 0 };
295
+ linearDelta.set(entry.bodyId, {
296
+ x: previousLinear.x + entry.force.x * dt * (lockLinear.x === true ? 0 : baseInvMass),
297
+ y: previousLinear.y + entry.force.y * dt * (lockLinear.y === true ? 0 : baseInvMass),
298
+ z: previousLinear.z + entry.force.z * dt * (lockLinear.z === true ? 0 : baseInvMass),
299
+ });
300
+ }
301
+ let netTorque = undefined;
302
+ if (entry.torque !== undefined) {
303
+ netTorque = { x: entry.torque.x, y: entry.torque.y, z: entry.torque.z };
304
+ }
305
+ if (entry.force !== undefined && entry.worldPoint !== undefined && target.kind === 'dynamic') {
306
+ const center = worldCenterOfMassForBody(target);
307
+ const lever = { x: entry.worldPoint.x - center.x, y: entry.worldPoint.y - center.y, z: entry.worldPoint.z - center.z };
308
+ const torqueFromForce = cross(lever, entry.force);
309
+ netTorque = {
310
+ x: (netTorque?.x ?? 0) + torqueFromForce.x,
311
+ y: (netTorque?.y ?? 0) + torqueFromForce.y,
312
+ z: (netTorque?.z ?? 0) + torqueFromForce.z,
313
+ };
314
+ }
315
+ if (netTorque !== undefined && target.orientation !== undefined && target.kind === 'dynamic') {
316
+ let invInertia = invInertiaByBody.get(entry.bodyId);
317
+ if (invInertia === undefined) {
318
+ invInertia = worldInverseInertiaForBody(target);
319
+ invInertiaByBody.set(entry.bodyId, invInertia);
320
+ }
321
+ const angImpulse = { x: netTorque.x * dt, y: netTorque.y * dt, z: netTorque.z * dt };
322
+ const angularKick = mulMat3Vec(invInertia, angImpulse);
323
+ const previousAngular = angularDelta.get(entry.bodyId) ?? { x: 0, y: 0, z: 0 };
324
+ angularDelta.set(entry.bodyId, {
325
+ x: previousAngular.x + angularKick.x,
326
+ y: previousAngular.y + angularKick.y,
327
+ z: previousAngular.z + angularKick.z,
328
+ });
329
+ }
330
+ }
331
+ const touchedBodies = new Set();
332
+ for (const [bodyId, dv] of linearDelta.entries()) {
333
+ if (dv.x !== 0 || dv.y !== 0 || dv.z !== 0) {
334
+ touchedBodies.add(bodyId);
335
+ }
336
+ }
337
+ for (const [bodyId, dw] of angularDelta.entries()) {
338
+ if (dw.x !== 0 || dw.y !== 0 || dw.z !== 0) {
339
+ touchedBodies.add(bodyId);
340
+ }
341
+ }
342
+ const woken = new Set();
343
+ for (const bodyId of touchedBodies) {
344
+ for (const member of contactIslandFor(world, bodyId)) {
345
+ woken.add(member);
346
+ }
347
+ }
348
+ const bodies = world.bodies.map((body) => {
349
+ const dv = linearDelta.get(body.id);
350
+ const dw = angularDelta.get(body.id);
351
+ const hasKick = (dv !== undefined && (dv.x !== 0 || dv.y !== 0 || dv.z !== 0))
352
+ || (dw !== undefined && (dw.x !== 0 || dw.y !== 0 || dw.z !== 0));
353
+ const shouldWake = (hasKick || woken.has(body.id)) && body.kind === 'dynamic';
354
+ if (!hasKick && !shouldWake) {
355
+ return body;
356
+ }
357
+ const updated = {
358
+ ...body,
359
+ vx: dv !== undefined ? quantizeDistance(body.vx + dv.x) : body.vx,
360
+ vy: dv !== undefined ? quantizeDistance(body.vy + dv.y) : body.vy,
361
+ vz: dv !== undefined ? quantizeDistance(body.vz + dv.z) : body.vz,
362
+ sleeping: shouldWake ? false : body.sleeping,
363
+ lowVelFrames: shouldWake ? 0 : body.lowVelFrames,
364
+ };
365
+ if (dw === undefined) {
366
+ return updated;
367
+ }
368
+ return {
369
+ ...updated,
370
+ angularVel: {
371
+ x: quantizeDistance((body.angularVel?.x ?? 0) + dw.x),
372
+ y: quantizeDistance((body.angularVel?.y ?? 0) + dw.y),
373
+ z: quantizeDistance((body.angularVel?.z ?? 0) + dw.z),
374
+ },
375
+ };
376
+ });
377
+ return { ...world, bodies: normalizeBodies(bodies, 'trusted') };
378
+ }
260
379
  /**
261
380
  * Atomic deterministic topology edit: add/remove/replace bodies without
262
381
  * recreating the world. Preserves `frame`, the object identity and sleep state
@@ -2255,14 +2374,56 @@ function runRigidSolver(world, dt, gravityY, velocityIterations, substeps, joint
2255
2374
  .map((entry) => entry.jointId);
2256
2375
  const impulseByPair = new Map();
2257
2376
  const solidContacts = [];
2258
- for (const impulse of result.contactImpulses) {
2377
+ for (let i = 0; i < result.contactImpulses.length; i += 1) {
2378
+ const impulse = result.contactImpulses[i];
2379
+ const manifold = result.manifolds[i];
2259
2380
  impulseByPair.set(impulse.pair, quantizeDistance(impulse.normalImpulse));
2381
+ let point = undefined;
2382
+ let normal = undefined;
2383
+ let closingSpeed = undefined;
2384
+ if (manifold !== undefined && manifold.points.length > 0) {
2385
+ let deepest = manifold.points[0];
2386
+ for (let p = 1; p < manifold.points.length; p += 1) {
2387
+ if (manifold.points[p].penetration > deepest.penetration) {
2388
+ deepest = manifold.points[p];
2389
+ }
2390
+ }
2391
+ const sbA = solverBodies[impulse.a];
2392
+ point = {
2393
+ x: quantizeDistance(sbA.p.x + deepest.rA.x),
2394
+ y: quantizeDistance(sbA.p.y + deepest.rA.y),
2395
+ z: quantizeDistance(sbA.p.z + deepest.rA.z),
2396
+ };
2397
+ normal = {
2398
+ x: quantizeDistance(manifold.normal.x),
2399
+ y: quantizeDistance(manifold.normal.y),
2400
+ z: quantizeDistance(manifold.normal.z),
2401
+ };
2402
+ const bodyA = source[impulse.a];
2403
+ const bodyB = source[impulse.b];
2404
+ const sbB = solverBodies[impulse.b];
2405
+ const applyGravityA = sbA.isDynamic && !sbA.sleeping && sbA.invMassVec.y !== 0;
2406
+ const applyGravityB = sbB.isDynamic && !sbB.sleeping && sbB.invMassVec.y !== 0;
2407
+ const wA = bodyA.angularVel ?? { x: 0, y: 0, z: 0 };
2408
+ const wB = bodyB.angularVel ?? { x: 0, y: 0, z: 0 };
2409
+ const vAx = (bodyA.vx ?? 0) + (wA.y * deepest.rA.z - wA.z * deepest.rA.y);
2410
+ const vAy = (bodyA.vy ?? 0) + (applyGravityA ? gravityY * dt * sbA.gravityScale : 0) + (wA.z * deepest.rA.x - wA.x * deepest.rA.z);
2411
+ const vAz = (bodyA.vz ?? 0) + (wA.x * deepest.rA.y - wA.y * deepest.rA.x);
2412
+ const vBx = (bodyB.vx ?? 0) + (wB.y * deepest.rB.z - wB.z * deepest.rB.y);
2413
+ const vBy = (bodyB.vy ?? 0) + (applyGravityB ? gravityY * dt * sbB.gravityScale : 0) + (wB.z * deepest.rB.x - wB.x * deepest.rB.z);
2414
+ const vBz = (bodyB.vz ?? 0) + (wB.x * deepest.rB.y - wB.y * deepest.rB.x);
2415
+ const relApproach = (vAx - vBx) * manifold.normal.x + (vAy - vBy) * manifold.normal.y + (vAz - vBz) * manifold.normal.z;
2416
+ closingSpeed = quantizeDistance(Math.max(0, relApproach));
2417
+ }
2260
2418
  solidContacts.push({
2261
2419
  pair: impulse.pair,
2262
2420
  a: solverBodies[impulse.a].id,
2263
2421
  b: solverBodies[impulse.b].id,
2264
2422
  trigger: false,
2265
2423
  sensor: false,
2424
+ ...(point !== undefined ? { point } : {}),
2425
+ ...(normal !== undefined ? { normal } : {}),
2426
+ ...(closingSpeed !== undefined ? { closingSpeed } : {}),
2266
2427
  });
2267
2428
  }
2268
2429
  return { bodies, contactCache, jointCache, impulseByPair, solidContacts, narrowphaseContacts: result.manifolds.length, brokenJointIds };
@@ -1,11 +1,11 @@
1
- export type DeterministicDslScalarFieldKind = 'int' | 'fixed' | 'bool' | 'button' | 'string' | 'asset-ref' | 'entity-ref' | 'player-ref' | `asset-ref:${string}` | `enum:${string}` | `flags:${string}` | `bitset:${string}`;
1
+ export type DeterministicDslScalarFieldKind = 'int' | 'fixed' | 'bool' | 'button' | 'string' | 'bytes' | 'asset-ref' | 'entity-ref' | 'player-ref' | `asset-ref:${string}` | `enum:${string}` | `flags:${string}` | `bitset:${string}`;
2
2
  export type DeterministicDslFixedArrayFieldKind = `${DeterministicDslScalarFieldKind}[${number}]`;
3
3
  export type DeterministicDslListFieldKind = `list<${DeterministicDslScalarFieldKind}>`;
4
4
  export type DeterministicDslSetFieldKind = `set<${DeterministicDslScalarFieldKind}>`;
5
5
  export type DeterministicDslDictionaryFieldKind = `dictionary<${DeterministicDslScalarFieldKind},${DeterministicDslScalarFieldKind}>`;
6
6
  export type DeterministicDslFieldKind = DeterministicDslScalarFieldKind | `${DeterministicDslScalarFieldKind}[]` | DeterministicDslFixedArrayFieldKind | DeterministicDslListFieldKind | DeterministicDslSetFieldKind | DeterministicDslDictionaryFieldKind;
7
7
  export type DeterministicDslDeclarationKind = 'component' | 'singleton' | 'global' | 'input' | 'command' | 'event' | 'signal' | 'struct' | 'enum' | 'flags' | 'union';
8
- export type DeterministicDslScalarValue = boolean | number | string;
8
+ export type DeterministicDslScalarValue = boolean | number | string | Uint8Array;
9
9
  export interface DeterministicDslDictionaryEntry {
10
10
  readonly key: DeterministicDslScalarValue;
11
11
  readonly value: DeterministicDslScalarValue;
@@ -352,12 +352,25 @@ function appendField(bytes, kind, value) {
352
352
  if (Array.isArray(value)) {
353
353
  throw new Error(`Unexpected array for ${kind} field`);
354
354
  }
355
- if (typeof value !== 'boolean' && typeof value !== 'number' && typeof value !== 'string') {
355
+ if (typeof value !== 'boolean' && typeof value !== 'number' && typeof value !== 'string' && !(value instanceof Uint8Array)) {
356
356
  throw new Error(`Invalid ${kind} field value`);
357
357
  }
358
358
  appendScalarField(bytes, kind, value);
359
359
  }
360
360
  function appendScalarField(bytes, kind, value) {
361
+ if (kind === 'bytes') {
362
+ if (!(value instanceof Uint8Array)) {
363
+ throw new Error(`${kind} field requires Uint8Array value`);
364
+ }
365
+ if (value.byteLength > 65535) {
366
+ throw new Error('Bytes field too large');
367
+ }
368
+ appendUint16(bytes, value.byteLength);
369
+ for (let i = 0; i < value.byteLength; i += 1) {
370
+ bytes.push(value[i]);
371
+ }
372
+ return;
373
+ }
361
374
  if (kind === 'bool' || kind === 'button') {
362
375
  if (typeof value !== 'boolean') {
363
376
  throw new Error(`${kind} field requires boolean value`);
@@ -421,6 +434,16 @@ function readField(bytes, offset, kind) {
421
434
  return readScalarField(bytes, offset, kind);
422
435
  }
423
436
  function readScalarField(bytes, offset, kind) {
437
+ if (kind === 'bytes') {
438
+ const length = readUint16(bytes, offset);
439
+ const start = offset + 2;
440
+ const end = start + length;
441
+ assertAvailable(bytes, start, length);
442
+ return {
443
+ value: bytes.slice(start, end),
444
+ offset: end,
445
+ };
446
+ }
424
447
  if (kind === 'bool' || kind === 'button') {
425
448
  assertAvailable(bytes, offset, 1);
426
449
  if (bytes[offset] !== 0 && bytes[offset] !== 1) {
@@ -529,7 +552,7 @@ function normalizeDslFieldKind(rawKind) {
529
552
  .replace(/\bbitset<([A-Za-z_][A-Za-z0-9_]*)>/g, 'bitset:$1');
530
553
  }
531
554
  function scalarKindPattern() {
532
- return '(?:int|fixed|bool|button|string|asset-ref|entity-ref|player-ref|asset-ref:[A-Za-z_][A-Za-z0-9_]*|enum:[A-Za-z_][A-Za-z0-9_]*|flags:[A-Za-z_][A-Za-z0-9_]*|bitset:[A-Za-z_][A-Za-z0-9_]*)';
555
+ return '(?:int|fixed|bool|button|string|bytes|asset-ref|entity-ref|player-ref|asset-ref:[A-Za-z_][A-Za-z0-9_]*|enum:[A-Za-z_][A-Za-z0-9_]*|flags:[A-Za-z_][A-Za-z0-9_]*|bitset:[A-Za-z_][A-Za-z0-9_]*)';
533
556
  }
534
557
  function isTypedAssetRefKind(kind) {
535
558
  return kind.startsWith('asset-ref:');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@series-inc/rundot-syncplay",
3
- "version": "5.28.0-beta.0",
3
+ "version": "5.28.0-beta.2",
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-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.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-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",