@series-inc/rundot-syncplay 5.27.0 → 5.28.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser.d.ts +6 -4
- package/dist/browser.js +3 -2
- package/dist/creator.d.ts +6 -4
- package/dist/creator.js +3 -2
- package/dist/debug-wireframe.d.ts +22 -0
- package/dist/debug-wireframe.js +226 -0
- package/dist/index.d.ts +6 -4
- package/dist/index.js +3 -2
- package/dist/physics3d-voxel.d.ts +29 -0
- package/dist/physics3d-voxel.js +199 -0
- package/dist/physics3d.d.ts +31 -0
- package/dist/physics3d.js +130 -22
- package/package.json +7 -2
package/dist/browser.d.ts
CHANGED
|
@@ -24,8 +24,9 @@ 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, createDeterministicPhysicsWorld3D, createTrustedDeterministicPhysicsWorld3D, deriveInverseInertia, deterministicPhysicsBodyById3D, deterministicPhysicsCallback3D, overlapDeterministicPhysics3D, raycastDeterministicPhysics3D, raycastDeterministicPhysics3DDetailed, runDeterministicPhysics3DStress, setDeterministicBodyVelocity3D, setDeterministicKinematicPoses3D, shapeCastDeterministicPhysics3D, shapeCastDeterministicPhysics3DDetailed, stepDeterministicPhysicsWorld3D } from './physics3d.js';
|
|
28
|
-
export { cookDeterministicVoxelChunkCollider3D, raycastDeterministicVoxelChunk3D } from './physics3d-voxel.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';
|
|
29
|
+
export { generatePhysicsWorldDebugMesh } from './debug-wireframe.js';
|
|
29
30
|
export { cylinderHullVertices } from './physics3d-hull.js';
|
|
30
31
|
export { createDeterministicCosmeticPhysicsWorld3D, setDeterministicCosmeticPhysicsAuthoritativeBodyProxies3D, stepDeterministicCosmeticPhysicsWorld3D } from './cosmetic-physics3d.js';
|
|
31
32
|
export { stepDeterministicVehicle3D } from './physics3d-vehicle.js';
|
|
@@ -65,8 +66,9 @@ export type { CookedDeterministicNavmesh, DeterministicNavmeshAgent, Determinist
|
|
|
65
66
|
export type { ConfirmedSyncplayPresentationFrame, KinetixRuntimeFactory, NetworkedClientTransport, NetworkedSyncplayClient, NetworkedSyncplayClientInputDelayOptions, NetworkedSyncplayClientNetStats, NetworkedSyncplayClientOptions, NetworkedSyncplayClientPacingOptions, NetworkedSyncplayPreparationOptions, NetworkedSyncplaySessionDescriptor, NetworkedSyncplaySlotPresence, PreparedNetworkedSyncplayTransport, SyncplayPredictionMode, SyncplayRemoteInputPrediction } from './networked-client.js';
|
|
66
67
|
export type { SyncplayRunner, SyncplayRunnerConfig, SyncplayRunnerConnectionEvent, SyncplayRunnerOccupancy, SyncplayRunnerOfflineOptions, SyncplayRunnerPresentation, SyncplayRunnerSnapshot, SyncplayRunnerStatus, SyncplayRunnerTransport } from './runner.js';
|
|
67
68
|
export type { DeterministicBodyKind, DeterministicContactEvent2D, DeterministicContactEventType, DeterministicPhysicsAabbQuery2D, DeterministicPhysicsActivePair2D, DeterministicPhysicsBody2D, DeterministicPhysicsCircleCastQuery2D, DeterministicPhysicsConstraint2D, DeterministicPhysicsHit2D, DeterministicPhysicsQueryOptions2D, DeterministicPhysicsRaycastQuery2D, DeterministicPhysicsShape2D, DeterministicPhysicsShapeOverlapQuery2D, DeterministicPhysicsStepOptions2D, DeterministicPhysicsStepResult2D, DeterministicPhysicsWorld2D } from './physics2d.js';
|
|
68
|
-
export type { DeterministicContactCacheEntry3D, DeterministicJointCacheEntry3D, DeterministicKinematicPose3D, 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';
|
|
69
|
-
export type { DeterministicVoxelChunk3D, DeterministicVoxelCompoundShape3D, DeterministicVoxelRayHit3D } from './physics3d-voxel.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';
|
|
71
|
+
export type { PhysicsWorldDebugMesh, PhysicsWorldDebugMeshOptions } from './debug-wireframe.js';
|
|
70
72
|
export type { DeterministicAuthoritativeBodyProxy3D, DeterministicCosmeticPhysicsBody3D, DeterministicCosmeticPhysicsStepOptions3D, DeterministicCosmeticPhysicsStepResult3D, DeterministicCosmeticPhysicsWorld3D } from './cosmetic-physics3d.js';
|
|
71
73
|
export type { DeterministicPhysicsJoint3D } from './physics3d-joints.js';
|
|
72
74
|
export type { DeterministicVehicle3D, DeterministicVehicleImpulse3D, DeterministicVehicleInput3D, DeterministicVehicleStepOptions3D, DeterministicVehicleStepResult3D, DeterministicVehicleWheel3D, DeterministicVehicleWheelTelemetry3D } from './physics3d-vehicle.js';
|
package/dist/browser.js
CHANGED
|
@@ -25,8 +25,9 @@ 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, createDeterministicPhysicsWorld3D, createTrustedDeterministicPhysicsWorld3D, deriveInverseInertia, deterministicPhysicsBodyById3D, deterministicPhysicsCallback3D, overlapDeterministicPhysics3D, raycastDeterministicPhysics3D, raycastDeterministicPhysics3DDetailed, runDeterministicPhysics3DStress, setDeterministicBodyVelocity3D, setDeterministicKinematicPoses3D, shapeCastDeterministicPhysics3D, shapeCastDeterministicPhysics3DDetailed, stepDeterministicPhysicsWorld3D } from './physics3d.js';
|
|
29
|
-
export { cookDeterministicVoxelChunkCollider3D, raycastDeterministicVoxelChunk3D } from './physics3d-voxel.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';
|
|
30
|
+
export { generatePhysicsWorldDebugMesh } from './debug-wireframe.js';
|
|
30
31
|
export { cylinderHullVertices } from './physics3d-hull.js';
|
|
31
32
|
export { createDeterministicCosmeticPhysicsWorld3D, setDeterministicCosmeticPhysicsAuthoritativeBodyProxies3D, stepDeterministicCosmeticPhysicsWorld3D } from './cosmetic-physics3d.js';
|
|
32
33
|
export { stepDeterministicVehicle3D } from './physics3d-vehicle.js';
|
package/dist/creator.d.ts
CHANGED
|
@@ -12,12 +12,14 @@ 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, createDeterministicPhysicsWorld3D, deriveInverseInertia, deterministicPhysicsBodyById3D, deterministicPhysicsCallback3D, overlapDeterministicPhysics3D, raycastDeterministicPhysics3D, raycastDeterministicPhysics3DDetailed, runDeterministicPhysics3DStress, setDeterministicBodyVelocity3D, setDeterministicKinematicPoses3D, shapeCastDeterministicPhysics3D, shapeCastDeterministicPhysics3DDetailed, stepDeterministicPhysicsWorld3D, } from './physics3d.js';
|
|
16
|
-
export { cookDeterministicVoxelChunkCollider3D, raycastDeterministicVoxelChunk3D } from './physics3d-voxel.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';
|
|
17
|
+
export { generatePhysicsWorldDebugMesh } from './debug-wireframe.js';
|
|
17
18
|
export { cylinderHullVertices } from './physics3d-hull.js';
|
|
18
19
|
export { stepDeterministicVehicle3D } from './physics3d-vehicle.js';
|
|
19
|
-
export type { DeterministicContactCacheEntry3D, DeterministicJointCacheEntry3D, DeterministicKinematicPose3D, 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';
|
|
20
|
-
export type { DeterministicVoxelChunk3D, DeterministicVoxelCompoundShape3D, DeterministicVoxelRayHit3D } from './physics3d-voxel.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';
|
|
22
|
+
export type { PhysicsWorldDebugMesh, PhysicsWorldDebugMeshOptions } from './debug-wireframe.js';
|
|
21
23
|
export type { DeterministicPhysicsJoint3D } from './physics3d-joints.js';
|
|
22
24
|
export type { DeterministicVehicle3D, DeterministicVehicleImpulse3D, DeterministicVehicleInput3D, DeterministicVehicleStepOptions3D, DeterministicVehicleStepResult3D, DeterministicVehicleWheel3D, DeterministicVehicleWheelTelemetry3D, } from './physics3d-vehicle.js';
|
|
23
25
|
export type { DeterministicPhysicsMaterial } from './deterministic-primitives.js';
|
package/dist/creator.js
CHANGED
|
@@ -9,7 +9,8 @@ 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, createDeterministicPhysicsWorld3D, deriveInverseInertia, deterministicPhysicsBodyById3D, deterministicPhysicsCallback3D, overlapDeterministicPhysics3D, raycastDeterministicPhysics3D, raycastDeterministicPhysics3DDetailed, runDeterministicPhysics3DStress, setDeterministicBodyVelocity3D, setDeterministicKinematicPoses3D, shapeCastDeterministicPhysics3D, shapeCastDeterministicPhysics3DDetailed, stepDeterministicPhysicsWorld3D, } from './physics3d.js';
|
|
13
|
-
export { cookDeterministicVoxelChunkCollider3D, raycastDeterministicVoxelChunk3D } from './physics3d-voxel.js';
|
|
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';
|
|
14
|
+
export { generatePhysicsWorldDebugMesh } from './debug-wireframe.js';
|
|
14
15
|
export { cylinderHullVertices } from './physics3d-hull.js';
|
|
15
16
|
export { stepDeterministicVehicle3D } from './physics3d-vehicle.js';
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { DeterministicPhysicsWorld3D } from './physics3d.js';
|
|
2
|
+
export interface PhysicsWorldDebugMesh {
|
|
3
|
+
/** Flat vertex positions [x0, y0, z0, x1, y1, z1, ...] for gl.LINES. */
|
|
4
|
+
readonly positions: Float32Array;
|
|
5
|
+
/** Line segment index pairs [start0, end0, start1, end1, ...] for gl.LINES / Three.js LineSegments. */
|
|
6
|
+
readonly indices: Uint32Array;
|
|
7
|
+
}
|
|
8
|
+
export interface PhysicsWorldDebugMeshOptions {
|
|
9
|
+
readonly includeStatics?: boolean;
|
|
10
|
+
readonly includeKinematics?: boolean;
|
|
11
|
+
readonly includeDynamics?: boolean;
|
|
12
|
+
readonly includeTriggers?: boolean;
|
|
13
|
+
readonly includeSensors?: boolean;
|
|
14
|
+
readonly sphereSegments?: number;
|
|
15
|
+
readonly capsuleSegments?: number;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Generates flat position and index buffers of line segments representing all
|
|
19
|
+
* colliders in a deterministic physics world, compatible with Three.js LineSegments
|
|
20
|
+
* and WebGL line rendering pipelines.
|
|
21
|
+
*/
|
|
22
|
+
export declare function generatePhysicsWorldDebugMesh(world: DeterministicPhysicsWorld3D, options?: PhysicsWorldDebugMeshOptions): PhysicsWorldDebugMesh;
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import { canonicalQuarterTurns, quaternionFromQuarterTurns, rotatePointByQuat, } from './physics3d-shared.js';
|
|
2
|
+
import { buildSolverHull } from './physics3d-hull.js';
|
|
3
|
+
function createCircleLines(radius, segments, plane, center = { x: 0, y: 0, z: 0 }) {
|
|
4
|
+
const vertices = [];
|
|
5
|
+
const indices = [];
|
|
6
|
+
const step = (Math.PI * 2) / segments;
|
|
7
|
+
for (let i = 0; i < segments; i += 1) {
|
|
8
|
+
const angle = i * step;
|
|
9
|
+
const cos = Math.cos(angle) * radius;
|
|
10
|
+
const sin = Math.sin(angle) * radius;
|
|
11
|
+
if (plane === 'xy') {
|
|
12
|
+
vertices.push({ x: center.x + cos, y: center.y + sin, z: center.z });
|
|
13
|
+
}
|
|
14
|
+
else if (plane === 'xz') {
|
|
15
|
+
vertices.push({ x: center.x + cos, y: center.y, z: center.z + sin });
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
vertices.push({ x: center.x, y: center.y + cos, z: center.z + sin });
|
|
19
|
+
}
|
|
20
|
+
indices.push(i, (i + 1) % segments);
|
|
21
|
+
}
|
|
22
|
+
return { vertices, indices };
|
|
23
|
+
}
|
|
24
|
+
function createArcLines(radius, segments, plane, center, startAngle, endAngle) {
|
|
25
|
+
const vertices = [];
|
|
26
|
+
const indices = [];
|
|
27
|
+
const span = endAngle - startAngle;
|
|
28
|
+
for (let i = 0; i <= segments; i += 1) {
|
|
29
|
+
const angle = startAngle + (i / segments) * span;
|
|
30
|
+
const cos = Math.cos(angle) * radius;
|
|
31
|
+
const sin = Math.sin(angle) * radius;
|
|
32
|
+
if (plane === 'xy') {
|
|
33
|
+
vertices.push({ x: center.x + cos, y: center.y + sin, z: center.z });
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
vertices.push({ x: center.x, y: center.y + cos, z: center.z + sin });
|
|
37
|
+
}
|
|
38
|
+
if (i < segments) {
|
|
39
|
+
indices.push(i, i + 1);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return { vertices, indices };
|
|
43
|
+
}
|
|
44
|
+
function appendGeometry(target, source, transform) {
|
|
45
|
+
const vertexOffset = target.vertices.length;
|
|
46
|
+
for (const vertex of source.vertices) {
|
|
47
|
+
let transformed = vertex;
|
|
48
|
+
if (transform?.orientation !== undefined) {
|
|
49
|
+
transformed = rotatePointByQuat(transform.orientation, transformed);
|
|
50
|
+
}
|
|
51
|
+
if (transform?.offset !== undefined) {
|
|
52
|
+
transformed = {
|
|
53
|
+
x: transformed.x + transform.offset.x,
|
|
54
|
+
y: transformed.y + transform.offset.y,
|
|
55
|
+
z: transformed.z + transform.offset.z,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
target.vertices.push(transformed);
|
|
59
|
+
}
|
|
60
|
+
for (const idx of source.indices) {
|
|
61
|
+
target.indices.push(vertexOffset + idx);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
function buildShapeLines(shape, options) {
|
|
65
|
+
const result = { vertices: [], indices: [] };
|
|
66
|
+
const sphereSegs = options.sphereSegments ?? 16;
|
|
67
|
+
const capsuleSegs = options.capsuleSegments ?? 12;
|
|
68
|
+
if (shape.type === 'box') {
|
|
69
|
+
const { halfX, halfY, halfZ } = shape;
|
|
70
|
+
const verts = [
|
|
71
|
+
{ x: -halfX, y: -halfY, z: -halfZ },
|
|
72
|
+
{ x: halfX, y: -halfY, z: -halfZ },
|
|
73
|
+
{ x: halfX, y: -halfY, z: halfZ },
|
|
74
|
+
{ x: -halfX, y: -halfY, z: halfZ },
|
|
75
|
+
{ x: -halfX, y: halfY, z: -halfZ },
|
|
76
|
+
{ x: halfX, y: halfY, z: -halfZ },
|
|
77
|
+
{ x: halfX, y: halfY, z: halfZ },
|
|
78
|
+
{ x: -halfX, y: halfY, z: halfZ },
|
|
79
|
+
];
|
|
80
|
+
const indices = [
|
|
81
|
+
0, 1, 1, 2, 2, 3, 3, 0, // bottom
|
|
82
|
+
4, 5, 5, 6, 6, 7, 7, 4, // top
|
|
83
|
+
0, 4, 1, 5, 2, 6, 3, 7, // pillars
|
|
84
|
+
];
|
|
85
|
+
return { vertices: verts, indices };
|
|
86
|
+
}
|
|
87
|
+
if (shape.type === 'sphere') {
|
|
88
|
+
appendGeometry(result, createCircleLines(shape.radius, sphereSegs, 'xy'));
|
|
89
|
+
appendGeometry(result, createCircleLines(shape.radius, sphereSegs, 'xz'));
|
|
90
|
+
appendGeometry(result, createCircleLines(shape.radius, sphereSegs, 'yz'));
|
|
91
|
+
return result;
|
|
92
|
+
}
|
|
93
|
+
if (shape.type === 'capsule') {
|
|
94
|
+
const { radius: r, halfHeight: h } = shape;
|
|
95
|
+
const halfSegs = Math.max(4, Math.floor(capsuleSegs / 2));
|
|
96
|
+
// 4 cylinder side pillars
|
|
97
|
+
const baseIndex = result.vertices.length;
|
|
98
|
+
result.vertices.push({ x: -r, y: -h, z: 0 }, { x: -r, y: h, z: 0 }, { x: r, y: -h, z: 0 }, { x: r, y: h, z: 0 }, { x: 0, y: -h, z: -r }, { x: 0, y: h, z: -r }, { x: 0, y: -h, z: r }, { x: 0, y: h, z: r });
|
|
99
|
+
result.indices.push(baseIndex, baseIndex + 1, baseIndex + 2, baseIndex + 3, baseIndex + 4, baseIndex + 5, baseIndex + 6, baseIndex + 7);
|
|
100
|
+
// 2 horizontal equator rings
|
|
101
|
+
appendGeometry(result, createCircleLines(r, capsuleSegs, 'xz', { x: 0, y: h, z: 0 }));
|
|
102
|
+
appendGeometry(result, createCircleLines(r, capsuleSegs, 'xz', { x: 0, y: -h, z: 0 }));
|
|
103
|
+
// Top dome arcs
|
|
104
|
+
appendGeometry(result, createArcLines(r, halfSegs, 'xy', { x: 0, y: h, z: 0 }, Math.PI, 0));
|
|
105
|
+
appendGeometry(result, createArcLines(r, halfSegs, 'yz', { x: 0, y: h, z: 0 }, -Math.PI / 2, Math.PI / 2));
|
|
106
|
+
// Bottom dome arcs
|
|
107
|
+
appendGeometry(result, createArcLines(r, halfSegs, 'xy', { x: 0, y: -h, z: 0 }, Math.PI, Math.PI * 2));
|
|
108
|
+
appendGeometry(result, createArcLines(r, halfSegs, 'yz', { x: 0, y: -h, z: 0 }, Math.PI / 2, (3 * Math.PI) / 2));
|
|
109
|
+
return result;
|
|
110
|
+
}
|
|
111
|
+
if (shape.type === 'hull') {
|
|
112
|
+
const hullData = buildSolverHull(shape.vertices);
|
|
113
|
+
const vertices = [...hullData.vertices];
|
|
114
|
+
const indices = [];
|
|
115
|
+
for (const face of hullData.faces) {
|
|
116
|
+
const len = face.verts.length;
|
|
117
|
+
for (let i = 0; i < len; i += 1) {
|
|
118
|
+
indices.push(face.verts[i], face.verts[(i + 1) % len]);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return { vertices, indices };
|
|
122
|
+
}
|
|
123
|
+
if (shape.type === 'mesh') {
|
|
124
|
+
const vertices = [...shape.vertices];
|
|
125
|
+
const indices = [];
|
|
126
|
+
for (let i = 0; i < vertices.length; i += 3) {
|
|
127
|
+
indices.push(i, i + 1, i + 1, i + 2, i + 2, i);
|
|
128
|
+
}
|
|
129
|
+
return { vertices, indices };
|
|
130
|
+
}
|
|
131
|
+
if (shape.type === 'heightfield') {
|
|
132
|
+
const { columns, rows, scale, heights } = shape;
|
|
133
|
+
const vertices = [];
|
|
134
|
+
const indices = [];
|
|
135
|
+
for (let r = 0; r < rows; r += 1) {
|
|
136
|
+
for (let c = 0; c < columns; c += 1) {
|
|
137
|
+
const h = heights[c + columns * r] ?? 0;
|
|
138
|
+
vertices.push({ x: c * scale.x, y: h * scale.y, z: r * scale.z });
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
for (let r = 0; r < rows; r += 1) {
|
|
142
|
+
for (let c = 0; c < columns; c += 1) {
|
|
143
|
+
const curr = c + columns * r;
|
|
144
|
+
if (c + 1 < columns) {
|
|
145
|
+
indices.push(curr, curr + 1);
|
|
146
|
+
}
|
|
147
|
+
if (r + 1 < rows) {
|
|
148
|
+
indices.push(curr, curr + columns);
|
|
149
|
+
}
|
|
150
|
+
if (c + 1 < columns && r + 1 < rows) {
|
|
151
|
+
indices.push(curr + columns, curr + 1);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return { vertices, indices };
|
|
156
|
+
}
|
|
157
|
+
if (shape.type === 'compound') {
|
|
158
|
+
for (const child of shape.children) {
|
|
159
|
+
const childLines = buildShapeLines(child.shape, options);
|
|
160
|
+
const orientation = child.quarterTurns !== undefined
|
|
161
|
+
? quaternionFromQuarterTurns({
|
|
162
|
+
x: canonicalQuarterTurns(child.quarterTurns.x),
|
|
163
|
+
y: canonicalQuarterTurns(child.quarterTurns.y),
|
|
164
|
+
z: canonicalQuarterTurns(child.quarterTurns.z),
|
|
165
|
+
})
|
|
166
|
+
: undefined;
|
|
167
|
+
appendGeometry(result, childLines, {
|
|
168
|
+
offset: child.offset,
|
|
169
|
+
orientation,
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
return result;
|
|
173
|
+
}
|
|
174
|
+
return result;
|
|
175
|
+
}
|
|
176
|
+
function bodyMatchesFilter(body, options) {
|
|
177
|
+
if (body.kind === 'static' && options.includeStatics === false)
|
|
178
|
+
return false;
|
|
179
|
+
if (body.kind === 'kinematic' && options.includeKinematics === false)
|
|
180
|
+
return false;
|
|
181
|
+
if (body.kind === 'dynamic' && options.includeDynamics === false)
|
|
182
|
+
return false;
|
|
183
|
+
if (body.trigger === true && options.includeTriggers === false)
|
|
184
|
+
return false;
|
|
185
|
+
if (body.sensor === true && options.includeSensors === false)
|
|
186
|
+
return false;
|
|
187
|
+
return true;
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Generates flat position and index buffers of line segments representing all
|
|
191
|
+
* colliders in a deterministic physics world, compatible with Three.js LineSegments
|
|
192
|
+
* and WebGL line rendering pipelines.
|
|
193
|
+
*/
|
|
194
|
+
export function generatePhysicsWorldDebugMesh(world, options = {}) {
|
|
195
|
+
const worldVertices = [];
|
|
196
|
+
const worldIndices = [];
|
|
197
|
+
let vertexIndexOffset = 0;
|
|
198
|
+
for (const body of world.bodies) {
|
|
199
|
+
if (!bodyMatchesFilter(body, options)) {
|
|
200
|
+
continue;
|
|
201
|
+
}
|
|
202
|
+
const localGeometry = buildShapeLines(body.shape, options);
|
|
203
|
+
const orientation = body.orientation ?? (body.rx !== undefined || body.ry !== undefined || body.rz !== undefined
|
|
204
|
+
? quaternionFromQuarterTurns({
|
|
205
|
+
x: canonicalQuarterTurns(body.rx ?? 0),
|
|
206
|
+
y: canonicalQuarterTurns(body.ry ?? 0),
|
|
207
|
+
z: canonicalQuarterTurns(body.rz ?? 0),
|
|
208
|
+
})
|
|
209
|
+
: undefined);
|
|
210
|
+
for (const vertex of localGeometry.vertices) {
|
|
211
|
+
let worldPos = vertex;
|
|
212
|
+
if (orientation !== undefined) {
|
|
213
|
+
worldPos = rotatePointByQuat(orientation, worldPos);
|
|
214
|
+
}
|
|
215
|
+
worldVertices.push(worldPos.x + body.x, worldPos.y + body.y, worldPos.z + body.z);
|
|
216
|
+
}
|
|
217
|
+
for (const idx of localGeometry.indices) {
|
|
218
|
+
worldIndices.push(vertexIndexOffset + idx);
|
|
219
|
+
}
|
|
220
|
+
vertexIndexOffset += localGeometry.vertices.length;
|
|
221
|
+
}
|
|
222
|
+
return {
|
|
223
|
+
positions: new Float32Array(worldVertices),
|
|
224
|
+
indices: new Uint32Array(worldIndices),
|
|
225
|
+
};
|
|
226
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -27,8 +27,9 @@ 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, createDeterministicPhysicsWorld3D, createTrustedDeterministicPhysicsWorld3D, deriveInverseInertia, deterministicPhysicsBodyById3D, deterministicPhysicsCallback3D, overlapDeterministicPhysics3D, raycastDeterministicPhysics3D, raycastDeterministicPhysics3DDetailed, runDeterministicPhysics3DStress, setDeterministicBodyVelocity3D, setDeterministicKinematicPoses3D, shapeCastDeterministicPhysics3D, shapeCastDeterministicPhysics3DDetailed, stepDeterministicPhysicsWorld3D } from './physics3d.js';
|
|
31
|
-
export { cookDeterministicVoxelChunkCollider3D, raycastDeterministicVoxelChunk3D } from './physics3d-voxel.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';
|
|
32
|
+
export { generatePhysicsWorldDebugMesh } from './debug-wireframe.js';
|
|
32
33
|
export { cylinderHullVertices } from './physics3d-hull.js';
|
|
33
34
|
export { runDeterministicPhysics3DDestructionCert } from './physics3d-destruction-cert.js';
|
|
34
35
|
export { createDeterministicCosmeticPhysicsWorld3D, setDeterministicCosmeticPhysicsAuthoritativeBodyProxies3D, stepDeterministicCosmeticPhysicsWorld3D } from './cosmetic-physics3d.js';
|
|
@@ -77,8 +78,9 @@ export type { CookedDeterministicNavmesh, DeterministicNavmeshAgent, Determinist
|
|
|
77
78
|
export type { ConfirmedSyncplayPresentationFrame, KinetixRuntimeFactory, NetworkedClientTransport, NetworkedSyncplayClient, NetworkedSyncplayClientInputDelayOptions, NetworkedSyncplayClientNetStats, NetworkedSyncplayClientOptions, NetworkedSyncplayClientPacingOptions, NetworkedSyncplayPreparationOptions, NetworkedSyncplaySessionDescriptor, PreparedNetworkedSyncplayTransport, SyncplayPredictionMode, SyncplayRemoteInputPrediction } from './networked-client.js';
|
|
78
79
|
export type { SyncplayRunner, SyncplayRunnerConfig, SyncplayRunnerConnectionEvent, SyncplayRunnerOccupancy, SyncplayRunnerOfflineOptions, SyncplayRunnerPresentation, SyncplayRunnerSnapshot, SyncplayRunnerStatus, SyncplayRunnerTransport } from './runner.js';
|
|
79
80
|
export type { DeterministicBodyKind, DeterministicContactEvent2D, DeterministicContactEventType, DeterministicPhysicsAabbQuery2D, DeterministicPhysicsBody2D, DeterministicPhysicsCircleCastQuery2D, DeterministicPhysicsConstraint2D, DeterministicPhysicsHit2D, DeterministicPhysicsQueryOptions2D, DeterministicPhysicsRaycastQuery2D, DeterministicPhysicsShape2D, DeterministicPhysicsShapeOverlapQuery2D, DeterministicPhysicsStepOptions2D, DeterministicPhysicsStepResult2D, DeterministicPhysicsWorld2D } from './physics2d.js';
|
|
80
|
-
export type { DeterministicContactCacheEntry3D, DeterministicJointCacheEntry3D, DeterministicKinematicPose3D, 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';
|
|
81
|
-
export type { DeterministicVoxelChunk3D, DeterministicVoxelCompoundShape3D, DeterministicVoxelRayHit3D } from './physics3d-voxel.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';
|
|
83
|
+
export type { PhysicsWorldDebugMesh, PhysicsWorldDebugMeshOptions } from './debug-wireframe.js';
|
|
82
84
|
export type { DeterministicDestructionCertOptions, DeterministicDestructionCertResult } from './physics3d-destruction-cert.js';
|
|
83
85
|
export type { DeterministicAuthoritativeBodyProxy3D, DeterministicCosmeticPhysicsBody3D, DeterministicCosmeticPhysicsStepOptions3D, DeterministicCosmeticPhysicsStepResult3D, DeterministicCosmeticPhysicsWorld3D } from './cosmetic-physics3d.js';
|
|
84
86
|
export type { DeterministicPhysicsJoint3D } from './physics3d-joints.js';
|
package/dist/index.js
CHANGED
|
@@ -28,8 +28,9 @@ 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, createDeterministicPhysicsWorld3D, createTrustedDeterministicPhysicsWorld3D, deriveInverseInertia, deterministicPhysicsBodyById3D, deterministicPhysicsCallback3D, overlapDeterministicPhysics3D, raycastDeterministicPhysics3D, raycastDeterministicPhysics3DDetailed, runDeterministicPhysics3DStress, setDeterministicBodyVelocity3D, setDeterministicKinematicPoses3D, shapeCastDeterministicPhysics3D, shapeCastDeterministicPhysics3DDetailed, stepDeterministicPhysicsWorld3D } from './physics3d.js';
|
|
32
|
-
export { cookDeterministicVoxelChunkCollider3D, raycastDeterministicVoxelChunk3D } from './physics3d-voxel.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';
|
|
33
|
+
export { generatePhysicsWorldDebugMesh } from './debug-wireframe.js';
|
|
33
34
|
export { cylinderHullVertices } from './physics3d-hull.js';
|
|
34
35
|
export { runDeterministicPhysics3DDestructionCert } from './physics3d-destruction-cert.js';
|
|
35
36
|
export { createDeterministicCosmeticPhysicsWorld3D, setDeterministicCosmeticPhysicsAuthoritativeBodyProxies3D, stepDeterministicCosmeticPhysicsWorld3D } from './cosmetic-physics3d.js';
|
|
@@ -40,3 +40,32 @@ export declare function raycastDeterministicVoxelChunk3D(chunk: DeterministicVox
|
|
|
40
40
|
* of the occupancy grid.
|
|
41
41
|
*/
|
|
42
42
|
export declare function cookDeterministicVoxelChunkCollider3D(chunk: DeterministicVoxelChunk3D): DeterministicVoxelCompoundShape3D;
|
|
43
|
+
export interface VoxelCarveRegion3D {
|
|
44
|
+
readonly minX: number;
|
|
45
|
+
readonly minY: number;
|
|
46
|
+
readonly minZ: number;
|
|
47
|
+
readonly maxX: number;
|
|
48
|
+
readonly maxY: number;
|
|
49
|
+
readonly maxZ: number;
|
|
50
|
+
readonly operation: 'carve' | 'fill';
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
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
|
|
55
|
+
* if disjoint or no-op.
|
|
56
|
+
*/
|
|
57
|
+
export declare function carveDeterministicVoxelChunk3D(chunk: DeterministicVoxelChunk3D, region: VoxelCarveRegion3D): DeterministicVoxelChunk3D;
|
|
58
|
+
/**
|
|
59
|
+
* Re-cooks compound collider boxes intersecting a dirty AABB cell region,
|
|
60
|
+
* iteratively expanding the re-cook volume to cover any intersecting previously-merged
|
|
61
|
+
* boxes so uncarved solid voxels are never dropped. Preserves disjoint children
|
|
62
|
+
* without full-chunk re-cooking. Returns undefined if 0 voxels remain across the chunk.
|
|
63
|
+
*/
|
|
64
|
+
export declare function cookDeterministicVoxelSubregion3D(chunk: DeterministicVoxelChunk3D, existingShape: DeterministicVoxelCompoundShape3D, dirtyRegion: {
|
|
65
|
+
readonly minX: number;
|
|
66
|
+
readonly minY: number;
|
|
67
|
+
readonly minZ: number;
|
|
68
|
+
readonly maxX: number;
|
|
69
|
+
readonly maxY: number;
|
|
70
|
+
readonly maxZ: number;
|
|
71
|
+
}): DeterministicVoxelCompoundShape3D | undefined;
|
package/dist/physics3d-voxel.js
CHANGED
|
@@ -227,3 +227,202 @@ export function cookDeterministicVoxelChunkCollider3D(chunk) {
|
|
|
227
227
|
}
|
|
228
228
|
return { type: 'compound', children };
|
|
229
229
|
}
|
|
230
|
+
/**
|
|
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
|
+
* if disjoint or no-op.
|
|
234
|
+
*/
|
|
235
|
+
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));
|
|
243
|
+
if (startX > endX || startY > endY || startZ > endZ) {
|
|
244
|
+
return chunk;
|
|
245
|
+
}
|
|
246
|
+
const targetVal = region.operation === 'carve' ? 0 : 1;
|
|
247
|
+
let changed = false;
|
|
248
|
+
const nextOccupancy = [...occupancy];
|
|
249
|
+
for (let z = startZ; z <= endZ; z += 1) {
|
|
250
|
+
for (let y = startY; y <= endY; y += 1) {
|
|
251
|
+
for (let x = startX; x <= endX; x += 1) {
|
|
252
|
+
const index = x + dimX * (y + dimY * z);
|
|
253
|
+
if (nextOccupancy[index] !== targetVal) {
|
|
254
|
+
nextOccupancy[index] = targetVal;
|
|
255
|
+
changed = true;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
if (!changed) {
|
|
261
|
+
return chunk;
|
|
262
|
+
}
|
|
263
|
+
return {
|
|
264
|
+
dimX,
|
|
265
|
+
dimY,
|
|
266
|
+
dimZ,
|
|
267
|
+
cellSize,
|
|
268
|
+
occupancy: nextOccupancy,
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
function cookSubregionBoxes(chunk, minX, minY, minZ, maxX, maxY, maxZ) {
|
|
272
|
+
const { dimX, dimY, dimZ, cellSize, occupancy } = chunk;
|
|
273
|
+
const solid = (x, y, z) => occupancy[x + dimX * (y + dimY * z)] !== 0;
|
|
274
|
+
const consumed = new Uint8Array(dimX * dimY * dimZ);
|
|
275
|
+
const free = (x, y, z) => solid(x, y, z) && consumed[x + dimX * (y + dimY * z)] === 0;
|
|
276
|
+
const children = [];
|
|
277
|
+
for (let z = minZ; z <= maxZ; z += 1) {
|
|
278
|
+
for (let y = minY; y <= maxY; y += 1) {
|
|
279
|
+
for (let x = minX; x <= maxX; x += 1) {
|
|
280
|
+
if (!free(x, y, z)) {
|
|
281
|
+
continue;
|
|
282
|
+
}
|
|
283
|
+
let x1 = x;
|
|
284
|
+
while (x1 + 1 <= maxX && free(x1 + 1, y, z)) {
|
|
285
|
+
x1 += 1;
|
|
286
|
+
}
|
|
287
|
+
const rowFree = (yy, zz) => {
|
|
288
|
+
for (let xx = x; xx <= x1; xx += 1) {
|
|
289
|
+
if (!free(xx, yy, zz)) {
|
|
290
|
+
return false;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
return true;
|
|
294
|
+
};
|
|
295
|
+
let y1 = y;
|
|
296
|
+
while (y1 + 1 <= maxY && rowFree(y1 + 1, z)) {
|
|
297
|
+
y1 += 1;
|
|
298
|
+
}
|
|
299
|
+
const slabFree = (zz) => {
|
|
300
|
+
for (let yy = y; yy <= y1; yy += 1) {
|
|
301
|
+
if (!rowFree(yy, zz)) {
|
|
302
|
+
return false;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
return true;
|
|
306
|
+
};
|
|
307
|
+
let z1 = z;
|
|
308
|
+
while (z1 + 1 <= maxZ && slabFree(z1 + 1)) {
|
|
309
|
+
z1 += 1;
|
|
310
|
+
}
|
|
311
|
+
for (let zz = z; zz <= z1; zz += 1) {
|
|
312
|
+
for (let yy = y; yy <= y1; yy += 1) {
|
|
313
|
+
for (let xx = x; xx <= x1; xx += 1) {
|
|
314
|
+
consumed[xx + dimX * (yy + dimY * zz)] = 1;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
children.push({
|
|
319
|
+
shape: {
|
|
320
|
+
type: 'box',
|
|
321
|
+
halfX: ((x1 - x + 1) * cellSize) / 2,
|
|
322
|
+
halfY: ((y1 - y + 1) * cellSize) / 2,
|
|
323
|
+
halfZ: ((z1 - z + 1) * cellSize) / 2,
|
|
324
|
+
},
|
|
325
|
+
offset: {
|
|
326
|
+
x: ((x + x1) / 2) * cellSize,
|
|
327
|
+
y: ((y + y1) / 2) * cellSize,
|
|
328
|
+
z: ((z + z1) / 2) * cellSize,
|
|
329
|
+
},
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
return children;
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* Re-cooks compound collider boxes intersecting a dirty AABB cell region,
|
|
338
|
+
* iteratively expanding the re-cook volume to cover any intersecting previously-merged
|
|
339
|
+
* boxes so uncarved solid voxels are never dropped. Preserves disjoint children
|
|
340
|
+
* without full-chunk re-cooking. Returns undefined if 0 voxels remain across the chunk.
|
|
341
|
+
*/
|
|
342
|
+
export function cookDeterministicVoxelSubregion3D(chunk, existingShape, dirtyRegion) {
|
|
343
|
+
const { dimX, dimY, dimZ, cellSize } = chunk;
|
|
344
|
+
let activeMinX = Math.max(0, Math.min(dirtyRegion.minX, dirtyRegion.maxX));
|
|
345
|
+
let activeMaxX = Math.min(dimX - 1, Math.max(dirtyRegion.minX, dirtyRegion.maxX));
|
|
346
|
+
let activeMinY = Math.max(0, Math.min(dirtyRegion.minY, dirtyRegion.maxY));
|
|
347
|
+
let activeMaxY = Math.min(dimY - 1, Math.max(dirtyRegion.minY, dirtyRegion.maxY));
|
|
348
|
+
let activeMinZ = Math.max(0, Math.min(dirtyRegion.minZ, dirtyRegion.maxZ));
|
|
349
|
+
let activeMaxZ = Math.min(dimZ - 1, Math.max(dirtyRegion.minZ, dirtyRegion.maxZ));
|
|
350
|
+
if (activeMinX > activeMaxX || activeMinY > activeMaxY || activeMinZ > activeMaxZ) {
|
|
351
|
+
return existingShape;
|
|
352
|
+
}
|
|
353
|
+
// Iterative fixed-point expansion to absorb all intersecting merged boxes
|
|
354
|
+
let expanded = true;
|
|
355
|
+
while (expanded) {
|
|
356
|
+
expanded = false;
|
|
357
|
+
for (const child of existingShape.children) {
|
|
358
|
+
if (child.shape.type !== 'box') {
|
|
359
|
+
continue;
|
|
360
|
+
}
|
|
361
|
+
const boxMinX = Math.round((child.offset.x - child.shape.halfX) / cellSize + 0.5);
|
|
362
|
+
const boxMaxX = Math.round((child.offset.x + child.shape.halfX) / cellSize - 0.5);
|
|
363
|
+
const boxMinY = Math.round((child.offset.y - child.shape.halfY) / cellSize + 0.5);
|
|
364
|
+
const boxMaxY = Math.round((child.offset.y + child.shape.halfY) / cellSize - 0.5);
|
|
365
|
+
const boxMinZ = Math.round((child.offset.z - child.shape.halfZ) / cellSize + 0.5);
|
|
366
|
+
const boxMaxZ = Math.round((child.offset.z + child.shape.halfZ) / cellSize - 0.5);
|
|
367
|
+
const overlaps = boxMinX <= activeMaxX && boxMaxX >= activeMinX
|
|
368
|
+
&& boxMinY <= activeMaxY && boxMaxY >= activeMinY
|
|
369
|
+
&& boxMinZ <= activeMaxZ && boxMaxZ >= activeMinZ;
|
|
370
|
+
if (overlaps) {
|
|
371
|
+
if (boxMinX < activeMinX) {
|
|
372
|
+
activeMinX = Math.max(0, boxMinX);
|
|
373
|
+
expanded = true;
|
|
374
|
+
}
|
|
375
|
+
if (boxMaxX > activeMaxX) {
|
|
376
|
+
activeMaxX = Math.min(dimX - 1, boxMaxX);
|
|
377
|
+
expanded = true;
|
|
378
|
+
}
|
|
379
|
+
if (boxMinY < activeMinY) {
|
|
380
|
+
activeMinY = Math.max(0, boxMinY);
|
|
381
|
+
expanded = true;
|
|
382
|
+
}
|
|
383
|
+
if (boxMaxY > activeMaxY) {
|
|
384
|
+
activeMaxY = Math.min(dimY - 1, boxMaxY);
|
|
385
|
+
expanded = true;
|
|
386
|
+
}
|
|
387
|
+
if (boxMinZ < activeMinZ) {
|
|
388
|
+
activeMinZ = Math.max(0, boxMinZ);
|
|
389
|
+
expanded = true;
|
|
390
|
+
}
|
|
391
|
+
if (boxMaxZ > activeMaxZ) {
|
|
392
|
+
activeMaxZ = Math.min(dimZ - 1, boxMaxZ);
|
|
393
|
+
expanded = true;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
// Preserve disjoint children
|
|
399
|
+
const preservedChildren = [];
|
|
400
|
+
for (const child of existingShape.children) {
|
|
401
|
+
if (child.shape.type !== 'box') {
|
|
402
|
+
preservedChildren.push(child);
|
|
403
|
+
continue;
|
|
404
|
+
}
|
|
405
|
+
const boxMinX = Math.round((child.offset.x - child.shape.halfX) / cellSize + 0.5);
|
|
406
|
+
const boxMaxX = Math.round((child.offset.x + child.shape.halfX) / cellSize - 0.5);
|
|
407
|
+
const boxMinY = Math.round((child.offset.y - child.shape.halfY) / cellSize + 0.5);
|
|
408
|
+
const boxMaxY = Math.round((child.offset.y + child.shape.halfY) / cellSize - 0.5);
|
|
409
|
+
const boxMinZ = Math.round((child.offset.z - child.shape.halfZ) / cellSize + 0.5);
|
|
410
|
+
const boxMaxZ = Math.round((child.offset.z + child.shape.halfZ) / cellSize - 0.5);
|
|
411
|
+
const overlaps = boxMinX <= activeMaxX && boxMaxX >= activeMinX
|
|
412
|
+
&& boxMinY <= activeMaxY && boxMaxY >= activeMinY
|
|
413
|
+
&& boxMinZ <= activeMaxZ && boxMaxZ >= activeMinZ;
|
|
414
|
+
if (!overlaps) {
|
|
415
|
+
preservedChildren.push(child);
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
// Cook newly carved/filled subregion
|
|
419
|
+
const subCooked = cookSubregionBoxes(chunk, activeMinX, activeMinY, activeMinZ, activeMaxX, activeMaxY, activeMaxZ);
|
|
420
|
+
const finalChildren = [...preservedChildren, ...subCooked];
|
|
421
|
+
if (finalChildren.length === 0) {
|
|
422
|
+
return undefined;
|
|
423
|
+
}
|
|
424
|
+
return {
|
|
425
|
+
type: 'compound',
|
|
426
|
+
children: finalChildren,
|
|
427
|
+
};
|
|
428
|
+
}
|
package/dist/physics3d.d.ts
CHANGED
|
@@ -58,6 +58,24 @@ export type DeterministicPhysicsShape3D = DeterministicPhysicsPrimitiveShape3D |
|
|
|
58
58
|
readonly type: 'compound';
|
|
59
59
|
readonly children: readonly DeterministicPhysicsCompoundChild3D[];
|
|
60
60
|
};
|
|
61
|
+
/**
|
|
62
|
+
* Creates a centered axis-aligned box shape from full dimensional sizes (sizeX, sizeY, sizeZ).
|
|
63
|
+
*/
|
|
64
|
+
export declare function createBoxShapeFromExtents(sizeX: number, sizeY: number, sizeZ: number): Extract<DeterministicPhysicsShape3D, {
|
|
65
|
+
readonly type: 'box';
|
|
66
|
+
}>;
|
|
67
|
+
/**
|
|
68
|
+
* Creates a box shape from min and max bounding coordinates.
|
|
69
|
+
* Returns a simple box if centered at (0,0,0), or a single-child compound with offset if not centered.
|
|
70
|
+
*/
|
|
71
|
+
export declare function createBoxShapeFromMinMax(min: Point3D, max: Point3D): DeterministicPhysicsShape3D;
|
|
72
|
+
/**
|
|
73
|
+
* Creates an upright capsule shape anchored at its base (y = 0), matching character controller conventions.
|
|
74
|
+
* Encapsulates the offset into a single-child compound shape centered at y = halfHeight + radius.
|
|
75
|
+
*/
|
|
76
|
+
export declare function createCapsuleShapeFromBase(radius: number, totalHeight: number): Extract<DeterministicPhysicsShape3D, {
|
|
77
|
+
readonly type: 'compound';
|
|
78
|
+
}>;
|
|
61
79
|
export declare const deterministicPhysicsCallback3D: {
|
|
62
80
|
readonly collisionEnter: 1;
|
|
63
81
|
readonly collisionStay: 2;
|
|
@@ -220,6 +238,11 @@ export interface DeterministicKinematicPose3D {
|
|
|
220
238
|
readonly angularVelY?: number;
|
|
221
239
|
readonly angularVelZ?: number;
|
|
222
240
|
}
|
|
241
|
+
export interface DeterministicKinematicTransformBuffer3D {
|
|
242
|
+
readonly bodyIds: readonly string[];
|
|
243
|
+
/** Flat [x, y, z, qx, qy, qz, qw] buffer per body. Length must be bodyIds.length * 7. */
|
|
244
|
+
readonly transforms: Float64Array | Float32Array | readonly number[];
|
|
245
|
+
}
|
|
223
246
|
export interface DeterministicPhysicsScheduledQuery3D {
|
|
224
247
|
readonly id: string;
|
|
225
248
|
readonly phase: 'pre-physics' | 'post-physics';
|
|
@@ -389,6 +412,14 @@ export declare function applyDeterministicWorldEdit3D(world: DeterministicPhysic
|
|
|
389
412
|
export declare function setDeterministicKinematicPoses3D(world: DeterministicPhysicsWorld3D, poses: readonly DeterministicKinematicPose3D[], options?: {
|
|
390
413
|
readonly dtTicks?: number;
|
|
391
414
|
}): DeterministicPhysicsWorld3D;
|
|
415
|
+
/**
|
|
416
|
+
* Allocation-free flat buffer batch update of kinematic body transforms.
|
|
417
|
+
* Accepts flat [x, y, z, qx, qy, qz, qw] arrays with stride 7 per bodyId,
|
|
418
|
+
* eliminating caller-side object allocations on high-frequency animation loops.
|
|
419
|
+
*/
|
|
420
|
+
export declare function setDeterministicKinematicTransforms3D(world: DeterministicPhysicsWorld3D, buffer: DeterministicKinematicTransformBuffer3D, options?: {
|
|
421
|
+
readonly dtTicks?: number;
|
|
422
|
+
}): DeterministicPhysicsWorld3D;
|
|
392
423
|
/**
|
|
393
424
|
* Apply a deterministic impulse to a body (linear + angular via lever arm),
|
|
394
425
|
* waking the body and its contact island. Returns a new canonical world.
|
package/dist/physics3d.js
CHANGED
|
@@ -7,6 +7,73 @@ import { buildSolverHull, rayHullIntervalLocal, } from './physics3d-hull.js';
|
|
|
7
7
|
import { computeShapeMassProperties } from './physics3d-mass.js';
|
|
8
8
|
import { heightfieldLocalBounds, heightfieldLocalTriangles } from './physics3d-heightfield.js';
|
|
9
9
|
import { conservativeToi, toiProxiesOf, toiProxyOf, } from './physics3d-toi.js';
|
|
10
|
+
/**
|
|
11
|
+
* Creates a centered axis-aligned box shape from full dimensional sizes (sizeX, sizeY, sizeZ).
|
|
12
|
+
*/
|
|
13
|
+
export function createBoxShapeFromExtents(sizeX, sizeY, sizeZ) {
|
|
14
|
+
if (!Number.isFinite(sizeX) || sizeX <= 0 || !Number.isFinite(sizeY) || sizeY <= 0 || !Number.isFinite(sizeZ) || sizeZ <= 0) {
|
|
15
|
+
throw new Error(`createBoxShapeFromExtents: sizeX, sizeY, sizeZ must be positive finite numbers, received (${sizeX}, ${sizeY}, ${sizeZ})`);
|
|
16
|
+
}
|
|
17
|
+
return {
|
|
18
|
+
type: 'box',
|
|
19
|
+
halfX: sizeX / 2,
|
|
20
|
+
halfY: sizeY / 2,
|
|
21
|
+
halfZ: sizeZ / 2,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Creates a box shape from min and max bounding coordinates.
|
|
26
|
+
* Returns a simple box if centered at (0,0,0), or a single-child compound with offset if not centered.
|
|
27
|
+
*/
|
|
28
|
+
export function createBoxShapeFromMinMax(min, max) {
|
|
29
|
+
if (!Number.isFinite(min.x) || !Number.isFinite(min.y) || !Number.isFinite(min.z)
|
|
30
|
+
|| !Number.isFinite(max.x) || !Number.isFinite(max.y) || !Number.isFinite(max.z)) {
|
|
31
|
+
throw new Error('createBoxShapeFromMinMax: min and max coordinates must be finite numbers');
|
|
32
|
+
}
|
|
33
|
+
if (max.x <= min.x || max.y <= min.y || max.z <= min.z) {
|
|
34
|
+
throw new Error(`createBoxShapeFromMinMax: max must be strictly greater than min on all axes, received min (${min.x},${min.y},${min.z}) max (${max.x},${max.y},${max.z})`);
|
|
35
|
+
}
|
|
36
|
+
const halfX = (max.x - min.x) / 2;
|
|
37
|
+
const halfY = (max.y - min.y) / 2;
|
|
38
|
+
const halfZ = (max.z - min.z) / 2;
|
|
39
|
+
const cx = (min.x + max.x) / 2;
|
|
40
|
+
const cy = (min.y + max.y) / 2;
|
|
41
|
+
const cz = (min.z + max.z) / 2;
|
|
42
|
+
if (cx === 0 && cy === 0 && cz === 0) {
|
|
43
|
+
return { type: 'box', halfX, halfY, halfZ };
|
|
44
|
+
}
|
|
45
|
+
return {
|
|
46
|
+
type: 'compound',
|
|
47
|
+
children: [
|
|
48
|
+
{
|
|
49
|
+
shape: { type: 'box', halfX, halfY, halfZ },
|
|
50
|
+
offset: { x: cx, y: cy, z: cz },
|
|
51
|
+
},
|
|
52
|
+
],
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Creates an upright capsule shape anchored at its base (y = 0), matching character controller conventions.
|
|
57
|
+
* Encapsulates the offset into a single-child compound shape centered at y = halfHeight + radius.
|
|
58
|
+
*/
|
|
59
|
+
export function createCapsuleShapeFromBase(radius, totalHeight) {
|
|
60
|
+
if (!Number.isFinite(radius) || radius <= 0 || !Number.isFinite(totalHeight) || totalHeight <= 0) {
|
|
61
|
+
throw new Error(`createCapsuleShapeFromBase: radius and totalHeight must be positive finite numbers, received radius ${radius}, totalHeight ${totalHeight}`);
|
|
62
|
+
}
|
|
63
|
+
if (totalHeight <= 2 * radius) {
|
|
64
|
+
throw new Error(`createCapsuleShapeFromBase: totalHeight must be strictly greater than 2 * radius (${2 * radius}), received ${totalHeight}`);
|
|
65
|
+
}
|
|
66
|
+
const halfHeight = (totalHeight - 2 * radius) / 2;
|
|
67
|
+
return {
|
|
68
|
+
type: 'compound',
|
|
69
|
+
children: [
|
|
70
|
+
{
|
|
71
|
+
shape: { type: 'capsule', radius, halfHeight },
|
|
72
|
+
offset: { x: 0, y: halfHeight + radius, z: 0 },
|
|
73
|
+
},
|
|
74
|
+
],
|
|
75
|
+
};
|
|
76
|
+
}
|
|
10
77
|
export const deterministicPhysicsCallback3D = {
|
|
11
78
|
collisionEnter: 1,
|
|
12
79
|
collisionStay: 2,
|
|
@@ -281,24 +348,7 @@ export function applyDeterministicWorldEdit3D(world, edit) {
|
|
|
281
348
|
: world.jointCache.filter((entry) => !removeJointIds.has(entry.jointId)),
|
|
282
349
|
};
|
|
283
350
|
}
|
|
284
|
-
|
|
285
|
-
* Fast batch update of kinematic body transforms and velocities without
|
|
286
|
-
* allocating Set instances or re-sorting world bodies. Wakes touching dynamic
|
|
287
|
-
* bodies across active contact pairs. If linear velocity (vx, vy, vz) is omitted
|
|
288
|
-
* and dtTicks (> 0) is supplied, linear velocity is derived as (newPos - oldPos) / dtTicks.
|
|
289
|
-
*/
|
|
290
|
-
export function setDeterministicKinematicPoses3D(world, poses, options = {}) {
|
|
291
|
-
if (poses.length === 0) {
|
|
292
|
-
return world;
|
|
293
|
-
}
|
|
294
|
-
const dt = options.dtTicks !== undefined && options.dtTicks > 0 ? options.dtTicks : undefined;
|
|
295
|
-
const poseById = new Map();
|
|
296
|
-
for (const pose of poses) {
|
|
297
|
-
if (!Number.isFinite(pose.x) || !Number.isFinite(pose.y) || !Number.isFinite(pose.z)) {
|
|
298
|
-
throw new Error(`setDeterministicKinematicPoses3D: body ${pose.id} position must be finite numbers`);
|
|
299
|
-
}
|
|
300
|
-
poseById.set(pose.id, pose);
|
|
301
|
-
}
|
|
351
|
+
function applyKinematicUpdatesInternal(world, poseById, dt, callerName) {
|
|
302
352
|
const updatedIds = new Set();
|
|
303
353
|
const movedBodies = new Map();
|
|
304
354
|
const newBodies = world.bodies.map((body) => {
|
|
@@ -307,7 +357,7 @@ export function setDeterministicKinematicPoses3D(world, poses, options = {}) {
|
|
|
307
357
|
return body;
|
|
308
358
|
}
|
|
309
359
|
if (body.kind !== 'kinematic') {
|
|
310
|
-
throw new Error(
|
|
360
|
+
throw new Error(`${callerName}: body ${body.id} is ${body.kind}, expected kinematic`);
|
|
311
361
|
}
|
|
312
362
|
updatedIds.add(body.id);
|
|
313
363
|
let vx = pose.vx;
|
|
@@ -350,9 +400,9 @@ export function setDeterministicKinematicPoses3D(world, poses, options = {}) {
|
|
|
350
400
|
movedBodies.set(body.id, updated);
|
|
351
401
|
return updated;
|
|
352
402
|
});
|
|
353
|
-
for (const
|
|
354
|
-
if (!updatedIds.has(
|
|
355
|
-
throw new Error(
|
|
403
|
+
for (const id of poseById.keys()) {
|
|
404
|
+
if (!updatedIds.has(id)) {
|
|
405
|
+
throw new Error(`${callerName}: cannot update unknown kinematic body ${id}`);
|
|
356
406
|
}
|
|
357
407
|
}
|
|
358
408
|
// Wake any dynamic bodies that share active pairs with updated kinematics
|
|
@@ -379,6 +429,64 @@ export function setDeterministicKinematicPoses3D(world, poses, options = {}) {
|
|
|
379
429
|
bodies: finalBodies,
|
|
380
430
|
};
|
|
381
431
|
}
|
|
432
|
+
/**
|
|
433
|
+
* Fast batch update of kinematic body transforms and velocities without
|
|
434
|
+
* allocating Set instances or re-sorting world bodies. Wakes touching dynamic
|
|
435
|
+
* bodies across active contact pairs. If linear velocity (vx, vy, vz) is omitted
|
|
436
|
+
* and dtTicks (> 0) is supplied, linear velocity is derived as (newPos - oldPos) / dtTicks.
|
|
437
|
+
*/
|
|
438
|
+
export function setDeterministicKinematicPoses3D(world, poses, options = {}) {
|
|
439
|
+
if (poses.length === 0) {
|
|
440
|
+
return world;
|
|
441
|
+
}
|
|
442
|
+
const dt = options.dtTicks !== undefined && options.dtTicks > 0 ? options.dtTicks : undefined;
|
|
443
|
+
const poseById = new Map();
|
|
444
|
+
for (const pose of poses) {
|
|
445
|
+
if (!Number.isFinite(pose.x) || !Number.isFinite(pose.y) || !Number.isFinite(pose.z)) {
|
|
446
|
+
throw new Error(`setDeterministicKinematicPoses3D: body ${pose.id} position must be finite numbers`);
|
|
447
|
+
}
|
|
448
|
+
poseById.set(pose.id, pose);
|
|
449
|
+
}
|
|
450
|
+
return applyKinematicUpdatesInternal(world, poseById, dt, 'setDeterministicKinematicPoses3D');
|
|
451
|
+
}
|
|
452
|
+
/**
|
|
453
|
+
* Allocation-free flat buffer batch update of kinematic body transforms.
|
|
454
|
+
* Accepts flat [x, y, z, qx, qy, qz, qw] arrays with stride 7 per bodyId,
|
|
455
|
+
* eliminating caller-side object allocations on high-frequency animation loops.
|
|
456
|
+
*/
|
|
457
|
+
export function setDeterministicKinematicTransforms3D(world, buffer, options = {}) {
|
|
458
|
+
const { bodyIds, transforms } = buffer;
|
|
459
|
+
if (transforms.length !== bodyIds.length * 7) {
|
|
460
|
+
throw new Error(`setDeterministicKinematicTransforms3D: transforms length must equal bodyIds.length * 7 (${bodyIds.length * 7}), received ${transforms.length}`);
|
|
461
|
+
}
|
|
462
|
+
if (bodyIds.length === 0) {
|
|
463
|
+
return world;
|
|
464
|
+
}
|
|
465
|
+
const dt = options.dtTicks !== undefined && options.dtTicks > 0 ? options.dtTicks : undefined;
|
|
466
|
+
const poseById = new Map();
|
|
467
|
+
for (let i = 0; i < bodyIds.length; i += 1) {
|
|
468
|
+
const id = bodyIds[i];
|
|
469
|
+
const offset = i * 7;
|
|
470
|
+
const x = transforms[offset];
|
|
471
|
+
const y = transforms[offset + 1];
|
|
472
|
+
const z = transforms[offset + 2];
|
|
473
|
+
const qx = transforms[offset + 3];
|
|
474
|
+
const qy = transforms[offset + 4];
|
|
475
|
+
const qz = transforms[offset + 5];
|
|
476
|
+
const qw = transforms[offset + 6];
|
|
477
|
+
if (!Number.isFinite(x) || !Number.isFinite(y) || !Number.isFinite(z)
|
|
478
|
+
|| !Number.isFinite(qx) || !Number.isFinite(qy) || !Number.isFinite(qz) || !Number.isFinite(qw)) {
|
|
479
|
+
throw new Error(`setDeterministicKinematicTransforms3D: body ${id} transform contains non-finite numbers`);
|
|
480
|
+
}
|
|
481
|
+
poseById.set(id, {
|
|
482
|
+
x,
|
|
483
|
+
y,
|
|
484
|
+
z,
|
|
485
|
+
orientation: { x: qx, y: qy, z: qz, w: qw },
|
|
486
|
+
});
|
|
487
|
+
}
|
|
488
|
+
return applyKinematicUpdatesInternal(world, poseById, dt, 'setDeterministicKinematicTransforms3D');
|
|
489
|
+
}
|
|
382
490
|
/**
|
|
383
491
|
* Apply a deterministic impulse to a body (linear + angular via lever arm),
|
|
384
492
|
* waking the body and its contact island. Returns a new canonical world.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@series-inc/rundot-syncplay",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.28.0-beta.0",
|
|
4
4
|
"description": "Kinetix orchestration, rollback, replay, late-join, and deterministic networking for RUN.game",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -42,6 +42,11 @@
|
|
|
42
42
|
"import": "./dist/tools/index.js",
|
|
43
43
|
"default": "./dist/tools/index.js"
|
|
44
44
|
},
|
|
45
|
+
"./debug": {
|
|
46
|
+
"types": "./dist/debug-wireframe.d.ts",
|
|
47
|
+
"import": "./dist/debug-wireframe.js",
|
|
48
|
+
"default": "./dist/debug-wireframe.js"
|
|
49
|
+
},
|
|
45
50
|
"./modules/component-model": {
|
|
46
51
|
"types": "./dist/modules/component-model.d.ts",
|
|
47
52
|
"import": "./dist/modules/component-model.js",
|
|
@@ -163,7 +168,7 @@
|
|
|
163
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",
|
|
164
169
|
"test:sdk-contract": "node --import tsx --test tests/sdk-runtime-contract.test.ts",
|
|
165
170
|
"test:soak": "tsx --test tests/runtime-soak.test.ts",
|
|
166
|
-
"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-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-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-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-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-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",
|
|
167
172
|
"test:ci": "npm run build && npm run test",
|
|
168
173
|
"build:native": "node core/scripts/build-native.mjs",
|
|
169
174
|
"build:mobile-native": "node core/scripts/build-mobile-native.mjs",
|