@iwsdk/core 0.5.3 → 1.0.0-rc.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/README.md +22 -1
- package/dist/asset/asset-manager.d.ts.map +1 -1
- package/dist/asset/asset-manager.js +3 -2
- package/dist/asset/asset-manager.js.map +1 -1
- package/dist/camera/camera-system.d.ts +7 -3
- package/dist/camera/camera-system.d.ts.map +1 -1
- package/dist/camera/camera-system.js +81 -50
- package/dist/camera/camera-system.js.map +1 -1
- package/dist/camera/camera-utils.d.ts +5 -0
- package/dist/camera/camera-utils.d.ts.map +1 -1
- package/dist/camera/camera-utils.js +8 -1
- package/dist/camera/camera-utils.js.map +1 -1
- package/dist/depth/depth-sensing-system.d.ts +3 -0
- package/dist/depth/depth-sensing-system.d.ts.map +1 -1
- package/dist/depth/depth-sensing-system.js +55 -1
- package/dist/depth/depth-sensing-system.js.map +1 -1
- package/dist/ecs/world.d.ts +2 -0
- package/dist/ecs/world.d.ts.map +1 -1
- package/dist/ecs/world.js +4 -0
- package/dist/ecs/world.js.map +1 -1
- package/dist/grab/distance-grabbable.d.ts +5 -0
- package/dist/grab/distance-grabbable.d.ts.map +1 -1
- package/dist/grab/distance-grabbable.js +5 -0
- package/dist/grab/distance-grabbable.js.map +1 -1
- package/dist/grab/handles.d.ts +6 -0
- package/dist/grab/handles.d.ts.map +1 -1
- package/dist/grab/handles.js +46 -12
- package/dist/grab/handles.js.map +1 -1
- package/dist/grab/one-hand-grabbable.d.ts +5 -0
- package/dist/grab/one-hand-grabbable.d.ts.map +1 -1
- package/dist/grab/one-hand-grabbable.js +5 -0
- package/dist/grab/one-hand-grabbable.js.map +1 -1
- package/dist/grab/two-hands-grabbable.d.ts +5 -0
- package/dist/grab/two-hands-grabbable.d.ts.map +1 -1
- package/dist/grab/two-hands-grabbable.js +5 -0
- package/dist/grab/two-hands-grabbable.js.map +1 -1
- package/dist/index.js +7 -6
- package/dist/index.js.map +1 -1
- package/dist/init/index.d.ts +2 -2
- package/dist/init/index.d.ts.map +1 -1
- package/dist/init/session-grant.d.ts +34 -0
- package/dist/init/session-grant.d.ts.map +1 -0
- package/dist/init/session-grant.js +89 -0
- package/dist/init/session-grant.js.map +1 -0
- package/dist/init/world-initializer.d.ts +87 -4
- package/dist/init/world-initializer.d.ts.map +1 -1
- package/dist/init/world-initializer.js +48 -43
- package/dist/init/world-initializer.js.map +1 -1
- package/dist/init/xr.d.ts +19 -0
- package/dist/init/xr.d.ts.map +1 -1
- package/dist/init/xr.js +136 -41
- package/dist/init/xr.js.map +1 -1
- package/dist/input/canvas-pointer-system.d.ts +2 -0
- package/dist/input/canvas-pointer-system.d.ts.map +1 -1
- package/dist/input/canvas-pointer-system.js +33 -13
- package/dist/input/canvas-pointer-system.js.map +1 -1
- package/dist/input/input-system.d.ts.map +1 -1
- package/dist/input/input-system.js +5 -2
- package/dist/input/input-system.js.map +1 -1
- package/dist/layers/xr-cylinder-layer.d.ts +2 -1
- package/dist/layers/xr-cylinder-layer.d.ts.map +1 -1
- package/dist/layers/xr-cylinder-layer.js +2 -1
- package/dist/layers/xr-cylinder-layer.js.map +1 -1
- package/dist/layers/xr-layer-init.d.ts +19 -0
- package/dist/layers/xr-layer-init.d.ts.map +1 -0
- package/dist/layers/xr-layer-init.js +20 -0
- package/dist/layers/xr-layer-init.js.map +1 -0
- package/dist/layers/xr-layer-system.d.ts +41 -1
- package/dist/layers/xr-layer-system.d.ts.map +1 -1
- package/dist/layers/xr-layer-system.js +506 -132
- package/dist/layers/xr-layer-system.js.map +1 -1
- package/dist/layers/xr-layer-transform.d.ts +18 -0
- package/dist/layers/xr-layer-transform.d.ts.map +1 -0
- package/dist/layers/xr-layer-transform.js +115 -0
- package/dist/layers/xr-layer-transform.js.map +1 -0
- package/dist/layers/xr-quad-layer.d.ts +2 -1
- package/dist/layers/xr-quad-layer.d.ts.map +1 -1
- package/dist/layers/xr-quad-layer.js +2 -1
- package/dist/layers/xr-quad-layer.js.map +1 -1
- package/dist/level/level-component-applier.d.ts +2 -0
- package/dist/level/level-component-applier.d.ts.map +1 -1
- package/dist/level/level-component-applier.js +24 -5
- package/dist/level/level-component-applier.js.map +1 -1
- package/dist/mcp/ecs-debug-tools.d.ts +4 -1
- package/dist/mcp/ecs-debug-tools.d.ts.map +1 -1
- package/dist/mcp/ecs-debug-tools.js +113 -10
- package/dist/mcp/ecs-debug-tools.js.map +1 -1
- package/dist/mcp/index.d.ts +1 -0
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/mcp-runtime.d.ts +2 -0
- package/dist/mcp/mcp-runtime.d.ts.map +1 -1
- package/dist/mcp/mcp-runtime.js +41 -0
- package/dist/mcp/mcp-runtime.js.map +1 -1
- package/dist/mcp/ui-debug-tools.d.ts +56 -0
- package/dist/mcp/ui-debug-tools.d.ts.map +1 -0
- package/dist/mcp/ui-debug-tools.js +412 -0
- package/dist/mcp/ui-debug-tools.js.map +1 -0
- package/dist/physics/index.js +1 -1
- package/dist/physics/physics-runtime.d.ts +22 -0
- package/dist/physics/physics-runtime.d.ts.map +1 -0
- package/dist/physics/physics-runtime.js +423 -0
- package/dist/physics/physics-runtime.js.map +1 -0
- package/dist/physics/physics-system.d.ts +84 -108
- package/dist/physics/physics-system.d.ts.map +1 -1
- package/dist/physics/physics-system.js +686 -555
- package/dist/physics/physics-system.js.map +1 -1
- package/dist/physics/physics-transport.d.ts +20 -0
- package/dist/physics/physics-transport.d.ts.map +1 -0
- package/dist/physics/physics-transport.js +111 -0
- package/dist/physics/physics-transport.js.map +1 -0
- package/dist/physics/physics-worker-protocol.d.ts +144 -0
- package/dist/physics/physics-worker-protocol.d.ts.map +1 -0
- package/dist/physics/physics-worker-protocol.js +41 -0
- package/dist/physics/physics-worker-protocol.js.map +1 -0
- package/dist/physics/physics-worker-utils.d.ts +8 -0
- package/dist/physics/physics-worker-utils.d.ts.map +1 -0
- package/dist/physics/physics-worker-utils.js +12 -0
- package/dist/physics/physics-worker-utils.js.map +1 -0
- package/dist/physics/physics-worker.d.ts +8 -0
- package/dist/physics/physics-worker.d.ts.map +1 -0
- package/dist/physics/physics-worker.js +66 -0
- package/dist/physics/physics-worker.js.map +1 -0
- package/dist/project/normalize.js +7 -1
- package/dist/project/normalize.js.map +1 -1
- package/dist/project/types.d.ts +7 -1
- package/dist/project/types.d.ts.map +1 -1
- package/dist/project/validation.js +36 -2
- package/dist/project/validation.js.map +1 -1
- package/dist/scene-understanding/scene-understanding-system.d.ts +42 -6
- package/dist/scene-understanding/scene-understanding-system.d.ts.map +1 -1
- package/dist/scene-understanding/scene-understanding-system.js +391 -106
- package/dist/scene-understanding/scene-understanding-system.js.map +1 -1
- package/dist/schemas/iwsdk-project.v1.schema.json +20 -1
- package/dist/ui/follow.d.ts.map +1 -1
- package/dist/ui/follow.js +0 -1
- package/dist/ui/follow.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +1 -1
- package/package.json +5 -5
|
@@ -1,4 +1,7 @@
|
|
|
1
|
+
import { effect } from '@preact/signals-core';
|
|
1
2
|
import '../runtime/three.js';
|
|
3
|
+
import { createPhysicsTransport } from './physics-transport.js';
|
|
4
|
+
import { physicsExchangeByteLength, PHYSICS_HEADER_WORDS, PHYSICS_PROTOCOL_VERSION, getCapsuleAxisEndpoints, PHYSICS_OUTPUT_RECORD_FLOATS, PHYSICS_INPUT_RECORD_FLOATS } from './physics-worker-protocol.js';
|
|
2
5
|
import { PhysicsBody, DEFAULT_ANGULAR_DAMPING, DEFAULT_LINEAR_DAMPING, DEFAULT_GRAVITY_FACTOR, PhysicsState } from './physicsBody.js';
|
|
3
6
|
import { PhysicsManipulation } from './physicsManipulation.js';
|
|
4
7
|
import { PhysicsShape, PhysicsShapeType } from './physicsShape.js';
|
|
@@ -6,7 +9,7 @@ import { detectShapeFromGeometry, generateMergedGeometry, sequentialIndices } fr
|
|
|
6
9
|
import { createSystem } from '../ecs/system.js';
|
|
7
10
|
import { Types, ne } from 'elics';
|
|
8
11
|
import { Grabbed } from '../grab/grabbed.js';
|
|
9
|
-
import {
|
|
12
|
+
import { Mesh } from 'three';
|
|
10
13
|
|
|
11
14
|
/**
|
|
12
15
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
@@ -15,25 +18,11 @@ import { Vector3, Matrix4, Mesh } from 'three';
|
|
|
15
18
|
* LICENSE file in the root directory of this source tree.
|
|
16
19
|
*/
|
|
17
20
|
const ZERO_VECTOR = [0, 0, 0];
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
return null;
|
|
24
|
-
}
|
|
25
|
-
const halfSegment = totalHeight / 2 - radius;
|
|
26
|
-
if (halfSegment === 0) {
|
|
27
|
-
return [
|
|
28
|
-
[0, 0, 0],
|
|
29
|
-
[0, 0, 0],
|
|
30
|
-
];
|
|
31
|
-
}
|
|
32
|
-
return [
|
|
33
|
-
[0, -halfSegment, 0],
|
|
34
|
-
[0, halfSegment, 0],
|
|
35
|
-
];
|
|
36
|
-
}
|
|
21
|
+
const EXCHANGE_BUFFER_COUNT = 2;
|
|
22
|
+
const INITIAL_RESULT_CAPACITY = 64;
|
|
23
|
+
const DEFAULT_UPDATE_FREQUENCY = 60;
|
|
24
|
+
const MAX_UPDATE_FREQUENCY = 240;
|
|
25
|
+
const MAX_ACCUMULATED_STEPS = 4;
|
|
37
26
|
function vector3ToArray(value) {
|
|
38
27
|
return 'x' in value
|
|
39
28
|
? [value.x, value.y, value.z]
|
|
@@ -44,35 +33,41 @@ function quaternionToArray(value) {
|
|
|
44
33
|
? [value.x, value.y, value.z, value.w]
|
|
45
34
|
: [value[0], value[1], value[2], value[3]];
|
|
46
35
|
}
|
|
36
|
+
function createPendingCommand(handle) {
|
|
37
|
+
return {
|
|
38
|
+
handle,
|
|
39
|
+
flags: 0,
|
|
40
|
+
position: [0, 0, 0],
|
|
41
|
+
quaternion: [0, 0, 0, 1],
|
|
42
|
+
impulsePoint: [0, 0, 0],
|
|
43
|
+
impulse: [0, 0, 0],
|
|
44
|
+
linearVelocity: [0, 0, 0],
|
|
45
|
+
angularVelocity: [0, 0, 0],
|
|
46
|
+
gravityFactor: DEFAULT_GRAVITY_FACTOR,
|
|
47
|
+
linearDamping: DEFAULT_LINEAR_DAMPING,
|
|
48
|
+
angularDamping: DEFAULT_ANGULAR_DAMPING,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
function isZeroVector(value) {
|
|
52
|
+
return value[0] === 0 && value[1] === 0 && value[2] === 0;
|
|
53
|
+
}
|
|
47
54
|
/**
|
|
48
|
-
* Manages
|
|
49
|
-
*
|
|
50
|
-
* @remarks
|
|
51
|
-
* - Initializes Havok physics engine and creates a physics world with gravity.
|
|
52
|
-
* - Supports automatic physics shapes creation based on entity geometry when {@link PhysicsShapeType.Auto} is used.
|
|
53
|
-
* - Supports multiple collision shapes: Sphere, Box, Cylinder, ConvexHull, and TriMesh.
|
|
54
|
-
* - Synchronizes physics body transforms with Three.js Object3D positions and rotations using {@link PhysicsBody}.
|
|
55
|
-
* - Handles physics manipulations like applying forces and setting velocities in {@link PhysicsManipulation}.
|
|
56
|
-
* - Automatically cleans up physics resources when entities are removed.
|
|
55
|
+
* Manages a Havok simulation on either a dedicated worker or the main thread.
|
|
57
56
|
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
57
|
+
* Per-frame commands and poses travel through a pair of transferable
|
|
58
|
+
* ArrayBuffers.
|
|
59
|
+
* Both execution modes use the same binary protocol and shared Havok runtime.
|
|
60
|
+
* Worker mode alternates ownership of transferable ArrayBuffers, avoiding both
|
|
61
|
+
* structured-clone copies and SharedArrayBuffer's cross-origin-isolation
|
|
62
|
+
* requirement. Shape creation/removal uses infrequent control messages.
|
|
62
63
|
*
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
* })
|
|
69
|
-
* box.addComponent(PhysicsBody, { state: PhysicsState.Dynamic })
|
|
70
|
-
* ```
|
|
64
|
+
* @remarks
|
|
65
|
+
* - Supports Sphere, Box, Cylinder, Capsule, ConvexHull, TriMesh, and Auto shapes.
|
|
66
|
+
* - Synchronizes dynamic body poses and velocities back to ECS entities.
|
|
67
|
+
* - Sends grab targets, impulses, velocity edits, damping, and gravity changes through one transport contract.
|
|
68
|
+
* - Automatically releases bodies and shapes when entities are removed.
|
|
71
69
|
*
|
|
72
70
|
* @category Physics
|
|
73
|
-
* @see {@link PhysicsBody}
|
|
74
|
-
* @see {@link PhysicsShape}
|
|
75
|
-
* @see {@link PhysicsManipulation}
|
|
76
71
|
*/
|
|
77
72
|
class PhysicsSystem extends createSystem({
|
|
78
73
|
physicsEntities: {
|
|
@@ -82,11 +77,6 @@ class PhysicsSystem extends createSystem({
|
|
|
82
77
|
required: [PhysicsBody, PhysicsManipulation],
|
|
83
78
|
where: [ne(PhysicsBody, '_engineBody', 0)],
|
|
84
79
|
},
|
|
85
|
-
// Bodies whose gravityFactor / linearDamping / angularDamping differ from
|
|
86
|
-
// the defaults. The value predicate keeps each set limited to overridden
|
|
87
|
-
// bodies, so the common case (default values) is never visited by the
|
|
88
|
-
// per-frame reactive sync below, and the queries re-fire whenever the field
|
|
89
|
-
// is `setValue`-d.
|
|
90
80
|
gravityOverrides: {
|
|
91
81
|
required: [PhysicsBody, PhysicsShape],
|
|
92
82
|
where: [ne(PhysicsBody, 'gravityFactor', DEFAULT_GRAVITY_FACTOR)],
|
|
@@ -101,612 +91,753 @@ class PhysicsSystem extends createSystem({
|
|
|
101
91
|
},
|
|
102
92
|
}, {
|
|
103
93
|
gravity: { type: Types.Vec3, default: [0, -9.81, 0] },
|
|
94
|
+
useWorker: { type: Types.Boolean, default: true },
|
|
95
|
+
updateFrequency: { type: Types.Float32, default: DEFAULT_UPDATE_FREQUENCY },
|
|
96
|
+
interpolation: { type: Types.Boolean, default: true },
|
|
104
97
|
}) {
|
|
105
98
|
constructor() {
|
|
106
99
|
super(...arguments);
|
|
107
|
-
this.
|
|
108
|
-
this.
|
|
100
|
+
this.transportReady = false;
|
|
101
|
+
this.nextBodyHandle = 1;
|
|
102
|
+
this.nextSequence = 1;
|
|
103
|
+
this.bodyEntities = new Map();
|
|
104
|
+
this.bodySnapshots = new Map();
|
|
105
|
+
this.bodyResultSequenceFloors = new Map();
|
|
106
|
+
this.failedBodyEntities = new WeakSet();
|
|
107
|
+
this.pendingCommands = new Map();
|
|
108
|
+
this.commandPool = [];
|
|
109
|
+
this.freeExchangeBuffers = [];
|
|
110
|
+
this.completedExchanges = [];
|
|
111
|
+
this.inFlightExchangeCount = 0;
|
|
112
|
+
this.stepAccumulator = 0;
|
|
113
|
+
this.debugFlushWaiters = new Set();
|
|
109
114
|
}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
const
|
|
113
|
-
|
|
114
|
-
this.
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
115
|
+
init() {
|
|
116
|
+
var _a;
|
|
117
|
+
const initialByteLength = physicsExchangeByteLength(0, INITIAL_RESULT_CAPACITY);
|
|
118
|
+
for (let index = 0; index < EXCHANGE_BUFFER_COUNT; index++) {
|
|
119
|
+
this.freeExchangeBuffers.push(new ArrayBuffer(initialByteLength));
|
|
120
|
+
}
|
|
121
|
+
try {
|
|
122
|
+
const transport = createPhysicsTransport(this.config.useWorker.value);
|
|
123
|
+
transport.onmessage = (event) => this.handleTransportMessage(event.data);
|
|
124
|
+
transport.onerror = (event) => {
|
|
125
|
+
this.handleTransportFailure(`Physics transport error: ${event.message}`);
|
|
126
|
+
};
|
|
127
|
+
transport.onmessageerror = () => {
|
|
128
|
+
this.handleTransportFailure('Physics transport message could not be decoded');
|
|
129
|
+
};
|
|
130
|
+
this.transport = transport;
|
|
131
|
+
transport.postMessage({
|
|
132
|
+
type: 'init',
|
|
133
|
+
gravity: [...this.config.gravity.value],
|
|
134
|
+
});
|
|
135
|
+
this.cleanupFuncs.push(effect(() => {
|
|
136
|
+
var _a;
|
|
137
|
+
const gravity = this.config.gravity.value;
|
|
138
|
+
(_a = this.transport) === null || _a === void 0 ? void 0 : _a.postMessage({
|
|
139
|
+
type: 'set-gravity',
|
|
140
|
+
gravity: [gravity[0], gravity[1], gravity[2]],
|
|
141
|
+
});
|
|
142
|
+
}));
|
|
143
|
+
}
|
|
144
|
+
catch (error) {
|
|
145
|
+
(_a = this.transport) === null || _a === void 0 ? void 0 : _a.terminate();
|
|
146
|
+
this.transport = undefined;
|
|
147
|
+
console.error(this.config.useWorker.value
|
|
148
|
+
? 'Failed to start physics worker. Set features.physics.useWorker to false to run physics on the main thread:'
|
|
149
|
+
: 'Failed to start main-thread physics:', error);
|
|
150
|
+
}
|
|
151
|
+
this.cleanupFuncs.push(this.queries.physicsEntities.subscribe('disqualify', (entity) => {
|
|
152
|
+
this.removeBody(entity);
|
|
153
|
+
}));
|
|
137
154
|
this.subscribeReactiveOverrides();
|
|
155
|
+
this.cleanupFuncs.push(() => {
|
|
156
|
+
var _a;
|
|
157
|
+
(_a = this.transport) === null || _a === void 0 ? void 0 : _a.terminate();
|
|
158
|
+
this.transport = undefined;
|
|
159
|
+
this.transportReady = false;
|
|
160
|
+
this.bodyEntities.clear();
|
|
161
|
+
this.bodySnapshots.clear();
|
|
162
|
+
this.bodyResultSequenceFloors.clear();
|
|
163
|
+
this.failedBodyEntities = new WeakSet();
|
|
164
|
+
this.pendingCommands.clear();
|
|
165
|
+
this.freeExchangeBuffers.length = 0;
|
|
166
|
+
this.completedExchanges.length = 0;
|
|
167
|
+
this.stepAccumulator = 0;
|
|
168
|
+
this.pendingDebugStepDelta = undefined;
|
|
169
|
+
this.resolveDebugFlushWaiters(true);
|
|
170
|
+
});
|
|
138
171
|
}
|
|
139
172
|
/**
|
|
140
|
-
* Teleport an existing
|
|
141
|
-
*
|
|
142
|
-
* @remarks
|
|
143
|
-
* No-ops until the entity has a live {@link PhysicsBody}. The pose is pushed
|
|
144
|
-
* directly to Havok and mirrored onto `entity.object3D` immediately so callers
|
|
145
|
-
* do not need to wait for the next physics tick before reading the reset
|
|
146
|
-
* transform. Linear and angular velocity are cleared by default, which is the
|
|
147
|
-
* expected behavior for reset/home-position flows.
|
|
148
|
-
*
|
|
149
|
-
* @example Reset a fallen prop to its home pose
|
|
150
|
-
* ```ts
|
|
151
|
-
* const physics = world.getSystem(PhysicsSystem);
|
|
152
|
-
* physics.setBodyTransform(prop, {
|
|
153
|
-
* position: homePosition,
|
|
154
|
-
* quaternion: homeQuaternion,
|
|
155
|
-
* });
|
|
156
|
-
* ```
|
|
173
|
+
* Teleport an existing physics body and mirror the pose immediately on its
|
|
174
|
+
* Object3D. The runtime consumes the command on the next available exchange.
|
|
157
175
|
*/
|
|
158
176
|
setBodyTransform(entity, pose, options = {}) {
|
|
159
|
-
if (!
|
|
177
|
+
if (!entity.hasComponent(PhysicsBody)) {
|
|
160
178
|
return;
|
|
161
179
|
}
|
|
162
|
-
const
|
|
163
|
-
if (!
|
|
180
|
+
const handle = entity.getValue(PhysicsBody, '_engineBody');
|
|
181
|
+
if (!handle) {
|
|
164
182
|
return;
|
|
165
183
|
}
|
|
166
|
-
const body = [BigInt(engineBody)];
|
|
167
184
|
const position = vector3ToArray(pose.position);
|
|
168
185
|
const quaternion = quaternionToArray(pose.quaternion);
|
|
169
|
-
this.
|
|
186
|
+
const command = this.getPendingCommand(handle);
|
|
187
|
+
command.flags |= 2 /* PhysicsCommandFlag.SetTransform */;
|
|
188
|
+
command.position = position;
|
|
189
|
+
command.quaternion = quaternion;
|
|
190
|
+
if (this.bodySnapshots.has(handle)) {
|
|
191
|
+
this.bodyResultSequenceFloors.set(handle, this.nextSequence);
|
|
192
|
+
}
|
|
170
193
|
if (entity.object3D) {
|
|
171
194
|
entity.object3D.position.set(position[0], position[1], position[2]);
|
|
172
195
|
entity.object3D.quaternion.set(quaternion[0], quaternion[1], quaternion[2], quaternion[3]);
|
|
173
196
|
entity.object3D.updateMatrixWorld(true);
|
|
197
|
+
this.syncSnapshotToObject(handle, entity.object3D);
|
|
174
198
|
}
|
|
175
199
|
if (options.resetVelocity === false) {
|
|
176
200
|
return;
|
|
177
201
|
}
|
|
178
|
-
|
|
179
|
-
|
|
202
|
+
command.flags |=
|
|
203
|
+
4 /* PhysicsCommandFlag.ResetLinearVelocity */ |
|
|
204
|
+
8 /* PhysicsCommandFlag.ResetAngularVelocity */;
|
|
180
205
|
entity.getVectorView(PhysicsBody, '_linearVelocity').set(ZERO_VECTOR);
|
|
181
206
|
entity.getVectorView(PhysicsBody, '_angularVelocity').set(ZERO_VECTOR);
|
|
182
207
|
}
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
entity.
|
|
202
|
-
|
|
208
|
+
update(delta) {
|
|
209
|
+
this.applyCompletedExchange();
|
|
210
|
+
if (!this.transport) {
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
this.queries.physicsEntities.entities.forEach((entity) => {
|
|
214
|
+
this.ensureBody(entity);
|
|
215
|
+
const handle = entity.getValue(PhysicsBody, '_engineBody');
|
|
216
|
+
if (handle &&
|
|
217
|
+
entity.object3D &&
|
|
218
|
+
Grabbed.bitmask &&
|
|
219
|
+
entity.hasComponent(Grabbed)) {
|
|
220
|
+
const command = this.getPendingCommand(handle);
|
|
221
|
+
command.flags |= 1 /* PhysicsCommandFlag.SetTargetTransform */;
|
|
222
|
+
command.position[0] = entity.object3D.position.x;
|
|
223
|
+
command.position[1] = entity.object3D.position.y;
|
|
224
|
+
command.position[2] = entity.object3D.position.z;
|
|
225
|
+
command.quaternion[0] = entity.object3D.quaternion.x;
|
|
226
|
+
command.quaternion[1] = entity.object3D.quaternion.y;
|
|
227
|
+
command.quaternion[2] = entity.object3D.quaternion.z;
|
|
228
|
+
command.quaternion[3] = entity.object3D.quaternion.w;
|
|
229
|
+
this.syncSnapshotToObject(handle, entity.object3D);
|
|
203
230
|
}
|
|
204
231
|
});
|
|
205
|
-
this.
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
232
|
+
this.syncReactiveOverrides();
|
|
233
|
+
this.queueManipulations();
|
|
234
|
+
if (this.pendingDebugStepDelta !== undefined) {
|
|
235
|
+
this.stepAccumulator = 0;
|
|
236
|
+
this.trySendPendingDebugStep();
|
|
237
|
+
this.renderInterpolatedTransforms(1);
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
const stepInterval = this.getStepInterval();
|
|
241
|
+
this.stepAccumulator = Math.min(this.stepAccumulator + Math.max(0, delta), stepInterval * MAX_ACCUMULATED_STEPS);
|
|
242
|
+
if (this.transportReady) {
|
|
243
|
+
const stepCount = Math.min(Math.floor(this.stepAccumulator / stepInterval + 1e-9), MAX_ACCUMULATED_STEPS);
|
|
244
|
+
if (stepCount > 0 && this.sendExchange(stepInterval, stepCount)) {
|
|
245
|
+
this.stepAccumulator = Math.max(0, this.stepAccumulator - stepInterval * stepCount);
|
|
209
246
|
}
|
|
210
|
-
}
|
|
247
|
+
}
|
|
248
|
+
const interpolationAlpha = this.config.interpolation.value
|
|
249
|
+
? Math.min(this.stepAccumulator / stepInterval, 1)
|
|
250
|
+
: 1;
|
|
251
|
+
this.renderInterpolatedTransforms(interpolationAlpha);
|
|
211
252
|
}
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
253
|
+
/** Prepare one exact-delta physics tick for an ECS debugger step. */
|
|
254
|
+
prepareDebugFrame(delta) {
|
|
255
|
+
this.pendingDebugStepDelta =
|
|
256
|
+
Number.isFinite(delta) && delta > 0 ? delta : this.getStepInterval();
|
|
257
|
+
this.stepAccumulator = 0;
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Wait until all submitted physics work is reflected in ECS transforms.
|
|
261
|
+
* Used by debugger pause/step so worker and inline execution expose the same
|
|
262
|
+
* settled state at command boundaries.
|
|
263
|
+
*/
|
|
264
|
+
async flushDebugFrame() {
|
|
265
|
+
this.applyCompletedExchange();
|
|
266
|
+
this.trySendPendingDebugStep();
|
|
267
|
+
await this.waitForDebugWork();
|
|
268
|
+
this.applyCompletedExchange();
|
|
269
|
+
// Pausing between fixed ticks must commit queued commands without moving
|
|
270
|
+
// simulation time; a zero-step exchange preserves the old inline behavior.
|
|
271
|
+
if (this.pendingDebugStepDelta === undefined &&
|
|
272
|
+
this.pendingCommands.size > 0 &&
|
|
273
|
+
this.transportReady &&
|
|
274
|
+
this.sendExchange(0, 0)) {
|
|
275
|
+
await this.waitForDebugWork();
|
|
276
|
+
this.applyCompletedExchange();
|
|
277
|
+
}
|
|
278
|
+
this.renderInterpolatedTransforms(1);
|
|
279
|
+
for (const snapshot of this.bodySnapshots.values()) {
|
|
280
|
+
snapshot.previousPosition.copy(snapshot.currentPosition);
|
|
281
|
+
snapshot.previousQuaternion.copy(snapshot.currentQuaternion);
|
|
217
282
|
}
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
const
|
|
227
|
-
|
|
228
|
-
|
|
283
|
+
}
|
|
284
|
+
handleTransportMessage(message) {
|
|
285
|
+
switch (message.type) {
|
|
286
|
+
case 'ready':
|
|
287
|
+
this.transportReady = true;
|
|
288
|
+
this.trySendPendingDebugStep();
|
|
289
|
+
break;
|
|
290
|
+
case 'body-created': {
|
|
291
|
+
const entity = this.bodyEntities.get(message.handle);
|
|
292
|
+
if (entity === null || entity === void 0 ? void 0 : entity.hasComponent(PhysicsBody)) {
|
|
293
|
+
entity
|
|
294
|
+
.getVectorView(PhysicsBody, 'centerOfMass')
|
|
295
|
+
.set(message.centerOfMass);
|
|
296
|
+
}
|
|
297
|
+
break;
|
|
229
298
|
}
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
PhysicsBody.data._engineLinearDamping[entity.index] = linearDamping;
|
|
243
|
-
PhysicsBody.data._engineAngularDamping[entity.index] =
|
|
244
|
-
angularDamping;
|
|
245
|
-
}
|
|
299
|
+
case 'step-result':
|
|
300
|
+
this.inFlightExchangeCount = Math.max(0, this.inFlightExchangeCount - 1);
|
|
301
|
+
const ints = new Int32Array(message.buffer);
|
|
302
|
+
this.completedExchanges.push({
|
|
303
|
+
buffer: message.buffer,
|
|
304
|
+
ints,
|
|
305
|
+
floats: new Float32Array(message.buffer),
|
|
306
|
+
sequence: ints[1 /* PhysicsHeaderIndex.Sequence */],
|
|
307
|
+
});
|
|
308
|
+
if (this.pendingDebugStepDelta !== undefined) {
|
|
309
|
+
this.applyCompletedExchange();
|
|
310
|
+
this.trySendPendingDebugStep();
|
|
246
311
|
}
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
const
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
// Processing physics body motion here
|
|
259
|
-
const position = entity.object3D.position;
|
|
260
|
-
const quaternion = entity.object3D.quaternion;
|
|
261
|
-
if (Grabbed.bitmask && entity.hasComponent(Grabbed)) {
|
|
262
|
-
this.havok.HP_Body_SetTargetQTransform([BigInt(engineBody)], [
|
|
263
|
-
[position.x, position.y, position.z],
|
|
264
|
-
[quaternion.x, quaternion.y, quaternion.z, quaternion.w],
|
|
265
|
-
]);
|
|
266
|
-
return;
|
|
267
|
-
}
|
|
268
|
-
const bodyOffset = (_d = entity.getValue(PhysicsBody, '_engineOffset')) !== null && _d !== void 0 ? _d : 0;
|
|
269
|
-
// Read the 16-float transform straight out of the cached heap view.
|
|
270
|
-
// `bodyBuffer + bodyOffset` is 4-byte aligned (it was a valid
|
|
271
|
-
// Float32Array byteOffset previously), so `>> 2` is exact.
|
|
272
|
-
const heap = this.getHeapFloatView();
|
|
273
|
-
const base = (this.bodyBuffer + bodyOffset) >> 2;
|
|
274
|
-
for (let mi = 0; mi < 15; mi++) {
|
|
275
|
-
if ((mi & 3) != 3) {
|
|
276
|
-
this.matrixBuffer.elements[mi] = heap[base + mi];
|
|
277
|
-
}
|
|
312
|
+
if (this.inFlightExchangeCount === 0) {
|
|
313
|
+
this.resolveDebugFlushWaiters();
|
|
314
|
+
}
|
|
315
|
+
break;
|
|
316
|
+
case 'error':
|
|
317
|
+
if (message.handle !== undefined) {
|
|
318
|
+
console.error(`Physics runtime: ${message.message}`);
|
|
319
|
+
const entity = this.bodyEntities.get(message.handle);
|
|
320
|
+
if (entity) {
|
|
321
|
+
this.failedBodyEntities.add(entity);
|
|
322
|
+
this.setEngineHandles(entity, 0);
|
|
278
323
|
}
|
|
279
|
-
this.
|
|
280
|
-
this.
|
|
324
|
+
this.bodyEntities.delete(message.handle);
|
|
325
|
+
this.bodySnapshots.delete(message.handle);
|
|
326
|
+
this.bodyResultSequenceFloors.delete(message.handle);
|
|
327
|
+
this.discardPendingCommand(message.handle);
|
|
281
328
|
}
|
|
282
|
-
|
|
329
|
+
else {
|
|
330
|
+
this.handleTransportFailure(`Physics runtime: ${message.message}`);
|
|
331
|
+
}
|
|
332
|
+
break;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
handleTransportFailure(message) {
|
|
336
|
+
var _a;
|
|
337
|
+
console.error(this.config.useWorker.value
|
|
338
|
+
? `${message}. Set features.physics.useWorker to false to run physics on the main thread.`
|
|
339
|
+
: message);
|
|
340
|
+
this.transportReady = false;
|
|
341
|
+
(_a = this.transport) === null || _a === void 0 ? void 0 : _a.terminate();
|
|
342
|
+
this.transport = undefined;
|
|
343
|
+
this.inFlightExchangeCount = 0;
|
|
344
|
+
this.pendingDebugStepDelta = undefined;
|
|
345
|
+
this.resolveDebugFlushWaiters();
|
|
346
|
+
}
|
|
347
|
+
async waitForDebugWork() {
|
|
348
|
+
if (this.pendingDebugStepDelta === undefined &&
|
|
349
|
+
this.inFlightExchangeCount === 0) {
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
await new Promise((resolve) => {
|
|
353
|
+
this.debugFlushWaiters.add(resolve);
|
|
283
354
|
});
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
355
|
+
}
|
|
356
|
+
resolveDebugFlushWaiters(force = false) {
|
|
357
|
+
if (!force &&
|
|
358
|
+
(this.pendingDebugStepDelta !== undefined ||
|
|
359
|
+
this.inFlightExchangeCount > 0)) {
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
for (const resolve of this.debugFlushWaiters) {
|
|
363
|
+
resolve();
|
|
364
|
+
}
|
|
365
|
+
this.debugFlushWaiters.clear();
|
|
366
|
+
}
|
|
367
|
+
applyCompletedExchange() {
|
|
368
|
+
if (this.completedExchanges.length === 0) {
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
this.completedExchanges.sort((left, right) => left.sequence - right.sequence);
|
|
372
|
+
for (const exchange of this.completedExchanges) {
|
|
373
|
+
this.applyExchangeBuffer(exchange);
|
|
374
|
+
this.freeExchangeBuffers.push(exchange.buffer);
|
|
375
|
+
}
|
|
376
|
+
this.completedExchanges.length = 0;
|
|
377
|
+
}
|
|
378
|
+
applyExchangeBuffer({ ints, floats }) {
|
|
379
|
+
const status = ints[4 /* PhysicsHeaderIndex.Status */];
|
|
380
|
+
if (status & 1 /* PhysicsExchangeStatus.ResultsTruncated */) {
|
|
381
|
+
console.warn('Physics result buffer was unexpectedly truncated');
|
|
382
|
+
}
|
|
383
|
+
const resultCount = ints[3 /* PhysicsHeaderIndex.ResultCount */];
|
|
384
|
+
const sequence = ints[1 /* PhysicsHeaderIndex.Sequence */];
|
|
385
|
+
const stepCount = Math.max(1, ints[5 /* PhysicsHeaderIndex.StepCount */]);
|
|
386
|
+
for (let index = 0; index < resultCount; index++) {
|
|
387
|
+
const base = PHYSICS_HEADER_WORDS + index * PHYSICS_OUTPUT_RECORD_FLOATS;
|
|
388
|
+
const handle = floats[base + 0 /* PhysicsOutputOffset.Handle */];
|
|
389
|
+
const entity = this.bodyEntities.get(handle);
|
|
390
|
+
if (!(entity === null || entity === void 0 ? void 0 : entity.object3D) || !entity.hasComponent(PhysicsBody)) {
|
|
391
|
+
continue;
|
|
289
392
|
}
|
|
290
|
-
|
|
291
|
-
if (
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
this.
|
|
296
|
-
entity.object3D.position.x,
|
|
297
|
-
entity.object3D.position.y,
|
|
298
|
-
entity.object3D.position.z,
|
|
299
|
-
], [force[0] * delta, force[1] * delta, force[2] * delta]);
|
|
393
|
+
const sequenceFloor = this.bodyResultSequenceFloors.get(handle);
|
|
394
|
+
if (sequenceFloor !== undefined) {
|
|
395
|
+
if (sequence < sequenceFloor) {
|
|
396
|
+
continue;
|
|
397
|
+
}
|
|
398
|
+
this.bodyResultSequenceFloors.delete(handle);
|
|
300
399
|
}
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
400
|
+
const linearVelocity = entity.getVectorView(PhysicsBody, '_linearVelocity');
|
|
401
|
+
linearVelocity[0] = floats[base + 8 /* PhysicsOutputOffset.LinearVelocityX */];
|
|
402
|
+
linearVelocity[1] = floats[base + 9 /* PhysicsOutputOffset.LinearVelocityY */];
|
|
403
|
+
linearVelocity[2] = floats[base + 10 /* PhysicsOutputOffset.LinearVelocityZ */];
|
|
404
|
+
const angularVelocity = entity.getVectorView(PhysicsBody, '_angularVelocity');
|
|
405
|
+
angularVelocity[0] = floats[base + 11 /* PhysicsOutputOffset.AngularVelocityX */];
|
|
406
|
+
angularVelocity[1] = floats[base + 12 /* PhysicsOutputOffset.AngularVelocityY */];
|
|
407
|
+
angularVelocity[2] = floats[base + 13 /* PhysicsOutputOffset.AngularVelocityZ */];
|
|
408
|
+
if (Grabbed.bitmask && entity.hasComponent(Grabbed)) {
|
|
409
|
+
continue;
|
|
307
410
|
}
|
|
308
|
-
|
|
309
|
-
if (!
|
|
310
|
-
|
|
311
|
-
.every((element) => element === 0)) {
|
|
312
|
-
const angularVelocity = entity.getVectorView(PhysicsManipulation, 'angularVelocity');
|
|
313
|
-
this.havok.HP_Body_SetAngularVelocity([BigInt(engineBody)], [angularVelocity[0], angularVelocity[1], angularVelocity[2]]);
|
|
411
|
+
const snapshot = this.bodySnapshots.get(handle);
|
|
412
|
+
if (!snapshot) {
|
|
413
|
+
continue;
|
|
314
414
|
}
|
|
315
|
-
|
|
316
|
-
|
|
415
|
+
if (stepCount === 1) {
|
|
416
|
+
snapshot.previousPosition.copy(snapshot.currentPosition);
|
|
417
|
+
snapshot.previousQuaternion.copy(snapshot.currentQuaternion);
|
|
418
|
+
}
|
|
419
|
+
snapshot.currentPosition.set(floats[base + 1 /* PhysicsOutputOffset.PositionX */], floats[base + 2 /* PhysicsOutputOffset.PositionY */], floats[base + 3 /* PhysicsOutputOffset.PositionZ */]);
|
|
420
|
+
snapshot.currentQuaternion.set(floats[base + 4 /* PhysicsOutputOffset.QuaternionX */], floats[base + 5 /* PhysicsOutputOffset.QuaternionY */], floats[base + 6 /* PhysicsOutputOffset.QuaternionZ */], floats[base + 7 /* PhysicsOutputOffset.QuaternionW */]);
|
|
421
|
+
if (stepCount > 1) {
|
|
422
|
+
snapshot.previousPosition.copy(snapshot.currentPosition);
|
|
423
|
+
snapshot.previousQuaternion.copy(snapshot.currentQuaternion);
|
|
424
|
+
}
|
|
425
|
+
}
|
|
317
426
|
}
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
427
|
+
renderInterpolatedTransforms(alpha) {
|
|
428
|
+
for (const [handle, snapshot] of this.bodySnapshots) {
|
|
429
|
+
const entity = this.bodyEntities.get(handle);
|
|
430
|
+
if (!(entity === null || entity === void 0 ? void 0 : entity.object3D) ||
|
|
431
|
+
(Grabbed.bitmask && entity.hasComponent(Grabbed))) {
|
|
432
|
+
continue;
|
|
433
|
+
}
|
|
434
|
+
entity.object3D.position.lerpVectors(snapshot.previousPosition, snapshot.currentPosition, alpha);
|
|
435
|
+
entity.object3D.quaternion.slerpQuaternions(snapshot.previousQuaternion, snapshot.currentQuaternion, alpha);
|
|
321
436
|
}
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
this.
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
]);
|
|
328
|
-
this.havok.HP_Body_SetLinearDamping(body, linearDamping);
|
|
329
|
-
this.havok.HP_Body_SetAngularDamping(body, angularDamping);
|
|
330
|
-
this.havok.HP_Body_SetGravityFactor(body, gravityFactor);
|
|
331
|
-
const shapeMass = this.havok.HP_Shape_BuildMassProperties(shape);
|
|
332
|
-
const massProps = shapeMass[0] == this.havok.Result.RESULT_OK
|
|
333
|
-
? shapeMass[1]
|
|
334
|
-
: [[0, 0, 0], 1, [1, 1, 1], [0, 0, 0, 1]];
|
|
335
|
-
if (!centerOfMass.every((e) => e == Infinity)) {
|
|
336
|
-
massProps[0] = [centerOfMass[0], centerOfMass[1], centerOfMass[2]];
|
|
437
|
+
}
|
|
438
|
+
syncSnapshotToObject(handle, object3D) {
|
|
439
|
+
const snapshot = this.bodySnapshots.get(handle);
|
|
440
|
+
if (!snapshot) {
|
|
441
|
+
return;
|
|
337
442
|
}
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
centerOfMass[2] = massProps[0][2];
|
|
343
|
-
}
|
|
344
|
-
this.havok.HP_Body_SetMassProperties(body, massProps);
|
|
345
|
-
let motionType;
|
|
346
|
-
switch (state) {
|
|
347
|
-
case PhysicsState.Static:
|
|
348
|
-
motionType = this.havok.MotionType.STATIC;
|
|
349
|
-
break;
|
|
350
|
-
case PhysicsState.Kinematic:
|
|
351
|
-
motionType = this.havok.MotionType.KINEMATIC;
|
|
352
|
-
break;
|
|
353
|
-
case PhysicsState.Dynamic:
|
|
354
|
-
default:
|
|
355
|
-
motionType = this.havok.MotionType.DYNAMIC;
|
|
356
|
-
}
|
|
357
|
-
this.havok.HP_Body_SetMotionType(body, motionType);
|
|
358
|
-
this.havok.HP_World_AddBody(this.havokWorld, body, false);
|
|
359
|
-
return {
|
|
360
|
-
offset: this.havok.HP_Body_GetWorldTransformOffset(body)[1],
|
|
361
|
-
createdBody: body,
|
|
362
|
-
};
|
|
443
|
+
snapshot.previousPosition.copy(object3D.position);
|
|
444
|
+
snapshot.currentPosition.copy(object3D.position);
|
|
445
|
+
snapshot.previousQuaternion.copy(object3D.quaternion);
|
|
446
|
+
snapshot.currentQuaternion.copy(object3D.quaternion);
|
|
363
447
|
}
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
*/
|
|
371
|
-
syncReactiveOverrides() {
|
|
372
|
-
this.queries.gravityOverrides.entities.forEach((entity) => {
|
|
373
|
-
this.syncGravityFactor(entity);
|
|
374
|
-
});
|
|
375
|
-
this.queries.linearDampingOverrides.entities.forEach((entity) => {
|
|
376
|
-
this.syncLinearDamping(entity);
|
|
377
|
-
});
|
|
378
|
-
this.queries.angularDampingOverrides.entities.forEach((entity) => {
|
|
379
|
-
this.syncAngularDamping(entity);
|
|
380
|
-
});
|
|
448
|
+
getStepInterval() {
|
|
449
|
+
const configuredFrequency = this.config.updateFrequency.value;
|
|
450
|
+
const frequency = Number.isFinite(configuredFrequency) && configuredFrequency > 0
|
|
451
|
+
? Math.min(configuredFrequency, MAX_UPDATE_FREQUENCY)
|
|
452
|
+
: DEFAULT_UPDATE_FREQUENCY;
|
|
453
|
+
return 1 / frequency;
|
|
381
454
|
}
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
455
|
+
sendExchange(delta, stepCount = 1) {
|
|
456
|
+
const transport = this.transport;
|
|
457
|
+
const buffer = this.freeExchangeBuffers.pop();
|
|
458
|
+
if (!transport || !buffer) {
|
|
459
|
+
return false;
|
|
460
|
+
}
|
|
461
|
+
const resultCapacity = this.bodySnapshots.size;
|
|
462
|
+
const requiredByteLength = physicsExchangeByteLength(this.pendingCommands.size, resultCapacity);
|
|
463
|
+
const exchangeBuffer = buffer.byteLength >= requiredByteLength
|
|
464
|
+
? buffer
|
|
465
|
+
: new ArrayBuffer(requiredByteLength);
|
|
466
|
+
const ints = new Int32Array(exchangeBuffer);
|
|
467
|
+
const floats = new Float32Array(exchangeBuffer);
|
|
468
|
+
ints.fill(0, 0, PHYSICS_HEADER_WORDS);
|
|
469
|
+
ints[0 /* PhysicsHeaderIndex.Version */] = PHYSICS_PROTOCOL_VERSION;
|
|
470
|
+
const sequence = this.nextSequence++;
|
|
471
|
+
ints[1 /* PhysicsHeaderIndex.Sequence */] = sequence;
|
|
472
|
+
ints[2 /* PhysicsHeaderIndex.CommandCount */] = this.pendingCommands.size;
|
|
473
|
+
ints[5 /* PhysicsHeaderIndex.StepCount */] = stepCount;
|
|
474
|
+
floats[8 /* PhysicsHeaderIndex.Delta */] = delta;
|
|
475
|
+
let commandIndex = 0;
|
|
476
|
+
for (const command of this.pendingCommands.values()) {
|
|
477
|
+
this.writeCommand(floats, commandIndex++, command);
|
|
478
|
+
if (command.flags & 2 /* PhysicsCommandFlag.SetTransform */) {
|
|
479
|
+
this.bodyResultSequenceFloors.set(command.handle, sequence);
|
|
480
|
+
}
|
|
393
481
|
}
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
482
|
+
try {
|
|
483
|
+
transport.postMessage({ type: 'step', buffer: exchangeBuffer }, [
|
|
484
|
+
exchangeBuffer,
|
|
485
|
+
]);
|
|
486
|
+
this.inFlightExchangeCount++;
|
|
487
|
+
this.releasePendingCommands();
|
|
488
|
+
return true;
|
|
398
489
|
}
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
490
|
+
catch (error) {
|
|
491
|
+
this.freeExchangeBuffers.push(exchangeBuffer);
|
|
492
|
+
console.error('Failed to post physics exchange buffer:', error);
|
|
493
|
+
return false;
|
|
402
494
|
}
|
|
403
|
-
this.havok.HP_Body_SetGravityFactor([BigInt(engineBody)], target);
|
|
404
|
-
PhysicsBody.data._engineGravityFactor[index] = target;
|
|
405
495
|
}
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
* body. Diffs against `_engineLinearDamping` (the last value pushed) rather
|
|
409
|
-
* than reading back from Havok, so an unchanged body costs only a typed-array
|
|
410
|
-
* compare — no WASM call and no allocation. The Havok write and shadow update
|
|
411
|
-
* happen only on an actual change. No-op until the body exists.
|
|
412
|
-
* See {@link linearDampingOverrides}.
|
|
413
|
-
*/
|
|
414
|
-
syncLinearDamping(entity) {
|
|
415
|
-
if (!this.havok) {
|
|
496
|
+
trySendPendingDebugStep() {
|
|
497
|
+
if (this.pendingDebugStepDelta === undefined) {
|
|
416
498
|
return;
|
|
417
499
|
}
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
500
|
+
if (!this.transport) {
|
|
501
|
+
this.pendingDebugStepDelta = undefined;
|
|
502
|
+
this.resolveDebugFlushWaiters();
|
|
421
503
|
return;
|
|
422
504
|
}
|
|
423
|
-
|
|
424
|
-
if (PhysicsBody.data._engineLinearDamping[index] === target) {
|
|
505
|
+
if (!this.transportReady) {
|
|
425
506
|
return;
|
|
426
507
|
}
|
|
427
|
-
this.
|
|
428
|
-
|
|
508
|
+
if (this.sendExchange(this.pendingDebugStepDelta, 1)) {
|
|
509
|
+
this.pendingDebugStepDelta = undefined;
|
|
510
|
+
}
|
|
429
511
|
}
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
512
|
+
writeCommand(output, index, command) {
|
|
513
|
+
const base = PHYSICS_HEADER_WORDS + index * PHYSICS_INPUT_RECORD_FLOATS;
|
|
514
|
+
output[base + 0 /* PhysicsInputOffset.Handle */] = command.handle;
|
|
515
|
+
output[base + 1 /* PhysicsInputOffset.Flags */] = command.flags;
|
|
516
|
+
output[base + 2 /* PhysicsInputOffset.PositionX */] = command.position[0];
|
|
517
|
+
output[base + 3 /* PhysicsInputOffset.PositionY */] = command.position[1];
|
|
518
|
+
output[base + 4 /* PhysicsInputOffset.PositionZ */] = command.position[2];
|
|
519
|
+
output[base + 5 /* PhysicsInputOffset.QuaternionX */] = command.quaternion[0];
|
|
520
|
+
output[base + 6 /* PhysicsInputOffset.QuaternionY */] = command.quaternion[1];
|
|
521
|
+
output[base + 7 /* PhysicsInputOffset.QuaternionZ */] = command.quaternion[2];
|
|
522
|
+
output[base + 8 /* PhysicsInputOffset.QuaternionW */] = command.quaternion[3];
|
|
523
|
+
output[base + 9 /* PhysicsInputOffset.ImpulsePointX */] = command.impulsePoint[0];
|
|
524
|
+
output[base + 10 /* PhysicsInputOffset.ImpulsePointY */] = command.impulsePoint[1];
|
|
525
|
+
output[base + 11 /* PhysicsInputOffset.ImpulsePointZ */] = command.impulsePoint[2];
|
|
526
|
+
output[base + 12 /* PhysicsInputOffset.ImpulseX */] = command.impulse[0];
|
|
527
|
+
output[base + 13 /* PhysicsInputOffset.ImpulseY */] = command.impulse[1];
|
|
528
|
+
output[base + 14 /* PhysicsInputOffset.ImpulseZ */] = command.impulse[2];
|
|
529
|
+
output[base + 15 /* PhysicsInputOffset.LinearVelocityX */] =
|
|
530
|
+
command.linearVelocity[0];
|
|
531
|
+
output[base + 16 /* PhysicsInputOffset.LinearVelocityY */] =
|
|
532
|
+
command.linearVelocity[1];
|
|
533
|
+
output[base + 17 /* PhysicsInputOffset.LinearVelocityZ */] =
|
|
534
|
+
command.linearVelocity[2];
|
|
535
|
+
output[base + 18 /* PhysicsInputOffset.AngularVelocityX */] =
|
|
536
|
+
command.angularVelocity[0];
|
|
537
|
+
output[base + 19 /* PhysicsInputOffset.AngularVelocityY */] =
|
|
538
|
+
command.angularVelocity[1];
|
|
539
|
+
output[base + 20 /* PhysicsInputOffset.AngularVelocityZ */] =
|
|
540
|
+
command.angularVelocity[2];
|
|
541
|
+
output[base + 21 /* PhysicsInputOffset.GravityFactor */] = command.gravityFactor;
|
|
542
|
+
output[base + 22 /* PhysicsInputOffset.LinearDamping */] = command.linearDamping;
|
|
543
|
+
output[base + 23 /* PhysicsInputOffset.AngularDamping */] = command.angularDamping;
|
|
544
|
+
}
|
|
545
|
+
getPendingCommand(handle) {
|
|
546
|
+
var _a;
|
|
547
|
+
let command = this.pendingCommands.get(handle);
|
|
548
|
+
if (!command) {
|
|
549
|
+
command = (_a = this.commandPool.pop()) !== null && _a !== void 0 ? _a : createPendingCommand(handle);
|
|
550
|
+
command.handle = handle;
|
|
551
|
+
command.flags = 0;
|
|
552
|
+
command.impulse[0] = 0;
|
|
553
|
+
command.impulse[1] = 0;
|
|
554
|
+
command.impulse[2] = 0;
|
|
555
|
+
this.pendingCommands.set(handle, command);
|
|
556
|
+
}
|
|
557
|
+
return command;
|
|
558
|
+
}
|
|
559
|
+
releasePendingCommands() {
|
|
560
|
+
for (const command of this.pendingCommands.values()) {
|
|
561
|
+
command.flags = 0;
|
|
562
|
+
this.commandPool.push(command);
|
|
563
|
+
}
|
|
564
|
+
this.pendingCommands.clear();
|
|
565
|
+
}
|
|
566
|
+
discardPendingCommand(handle) {
|
|
567
|
+
const command = this.pendingCommands.get(handle);
|
|
568
|
+
if (command) {
|
|
569
|
+
command.flags = 0;
|
|
570
|
+
this.commandPool.push(command);
|
|
571
|
+
this.pendingCommands.delete(handle);
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
ensureBody(entity) {
|
|
575
|
+
var _a, _b, _c;
|
|
576
|
+
if (!this.transport || !entity.object3D) {
|
|
440
577
|
return;
|
|
441
578
|
}
|
|
442
|
-
|
|
443
|
-
const engineBody = PhysicsBody.data._engineBody[index];
|
|
444
|
-
if (!engineBody) {
|
|
579
|
+
if (this.failedBodyEntities.has(entity)) {
|
|
445
580
|
return;
|
|
446
581
|
}
|
|
447
|
-
|
|
448
|
-
if (PhysicsBody.data._engineAngularDamping[index] === target) {
|
|
582
|
+
if (entity.getValue(PhysicsBody, '_engineBody')) {
|
|
449
583
|
return;
|
|
450
584
|
}
|
|
451
|
-
this.
|
|
452
|
-
|
|
453
|
-
}
|
|
454
|
-
createHavokShapes(entity, dimensionsView) {
|
|
455
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t;
|
|
456
|
-
if (!entity.object3D) {
|
|
457
|
-
console.warn('PhysicsSystem: No object3D attached to entity', entity.index);
|
|
585
|
+
const serialized = this.serializeShape(entity, entity.object3D);
|
|
586
|
+
if (!serialized) {
|
|
458
587
|
return;
|
|
459
588
|
}
|
|
460
|
-
|
|
589
|
+
const handle = this.nextBodyHandle++;
|
|
590
|
+
const linearDamping = (_a = entity.getValue(PhysicsBody, 'linearDamping')) !== null && _a !== void 0 ? _a : DEFAULT_LINEAR_DAMPING;
|
|
591
|
+
const angularDamping = (_b = entity.getValue(PhysicsBody, 'angularDamping')) !== null && _b !== void 0 ? _b : DEFAULT_ANGULAR_DAMPING;
|
|
592
|
+
const gravityFactor = (_c = entity.getValue(PhysicsBody, 'gravityFactor')) !== null && _c !== void 0 ? _c : DEFAULT_GRAVITY_FACTOR;
|
|
593
|
+
const centerOfMass = entity.getVectorView(PhysicsBody, 'centerOfMass');
|
|
594
|
+
const message = {
|
|
595
|
+
type: 'add-body',
|
|
596
|
+
handle,
|
|
597
|
+
shape: serialized.descriptor,
|
|
598
|
+
state: entity.getValue(PhysicsBody, 'state'),
|
|
599
|
+
position: [
|
|
600
|
+
entity.object3D.position.x,
|
|
601
|
+
entity.object3D.position.y,
|
|
602
|
+
entity.object3D.position.z,
|
|
603
|
+
],
|
|
604
|
+
quaternion: [
|
|
605
|
+
entity.object3D.quaternion.x,
|
|
606
|
+
entity.object3D.quaternion.y,
|
|
607
|
+
entity.object3D.quaternion.z,
|
|
608
|
+
entity.object3D.quaternion.w,
|
|
609
|
+
],
|
|
610
|
+
linearDamping,
|
|
611
|
+
angularDamping,
|
|
612
|
+
gravityFactor,
|
|
613
|
+
centerOfMass: [centerOfMass[0], centerOfMass[1], centerOfMass[2]],
|
|
614
|
+
};
|
|
615
|
+
this.setEngineHandles(entity, handle);
|
|
616
|
+
PhysicsBody.data._engineGravityFactor[entity.index] = gravityFactor;
|
|
617
|
+
PhysicsBody.data._engineLinearDamping[entity.index] = linearDamping;
|
|
618
|
+
PhysicsBody.data._engineAngularDamping[entity.index] = angularDamping;
|
|
619
|
+
this.bodyEntities.set(handle, entity);
|
|
620
|
+
if (message.state !== PhysicsState.Static) {
|
|
621
|
+
this.bodySnapshots.set(handle, {
|
|
622
|
+
previousPosition: entity.object3D.position.clone(),
|
|
623
|
+
currentPosition: entity.object3D.position.clone(),
|
|
624
|
+
previousQuaternion: entity.object3D.quaternion.clone(),
|
|
625
|
+
currentQuaternion: entity.object3D.quaternion.clone(),
|
|
626
|
+
});
|
|
627
|
+
}
|
|
628
|
+
try {
|
|
629
|
+
this.transport.postMessage(message, serialized.transfer);
|
|
630
|
+
}
|
|
631
|
+
catch (error) {
|
|
632
|
+
this.failedBodyEntities.add(entity);
|
|
633
|
+
this.setEngineHandles(entity, 0);
|
|
634
|
+
this.bodyEntities.delete(handle);
|
|
635
|
+
this.bodySnapshots.delete(handle);
|
|
636
|
+
this.bodyResultSequenceFloors.delete(handle);
|
|
637
|
+
console.error('Failed to create physics body:', error);
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
serializeShape(entity, object3D) {
|
|
641
|
+
var _a, _b, _c, _d, _e;
|
|
642
|
+
const dimensionsView = entity.getVectorView(PhysicsShape, 'dimensions');
|
|
461
643
|
let shapeType = entity.getValue(PhysicsShape, 'shape');
|
|
462
644
|
if (shapeType === PhysicsShapeType.Auto) {
|
|
463
|
-
const detection = detectShapeFromGeometry(
|
|
464
|
-
// Update the entity's shape type and dimensions if they were auto-detected
|
|
645
|
+
const detection = detectShapeFromGeometry(object3D);
|
|
465
646
|
entity.setValue(PhysicsShape, 'shape', detection.shapeType);
|
|
466
647
|
shapeType = detection.shapeType;
|
|
467
648
|
if (detection.dimensions) {
|
|
468
|
-
// Re-read the updated dimensions view
|
|
469
649
|
dimensionsView.set(detection.dimensions);
|
|
470
650
|
}
|
|
471
651
|
}
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
(_g = entity.getValue(PhysicsShape, 'density')) !== null && _g !== void 0 ? _g : 1.0, (_h = entity.getValue(PhysicsShape, 'restitution')) !== null && _h !== void 0 ? _h : 0, (_j = entity.getValue(PhysicsShape, 'friction')) !== null && _j !== void 0 ? _j : 0.5);
|
|
497
|
-
if (cylinderShape) {
|
|
498
|
-
PhysicsShape.data._engineShape[entity.index] = Number(cylinderShape);
|
|
499
|
-
}
|
|
500
|
-
else {
|
|
501
|
-
console.warn('PhysicsSystem: Failed to create cylinder shape for entity', entity.index);
|
|
502
|
-
}
|
|
503
|
-
break;
|
|
504
|
-
}
|
|
505
|
-
case PhysicsShapeType.Capsules: {
|
|
506
|
-
const capsuleShape = this.createCapsuleShape(dimensionsView[0], // radius
|
|
507
|
-
dimensionsView[1], // total end-to-end height
|
|
508
|
-
(_k = entity.getValue(PhysicsShape, 'density')) !== null && _k !== void 0 ? _k : 1.0, (_l = entity.getValue(PhysicsShape, 'restitution')) !== null && _l !== void 0 ? _l : 0, (_m = entity.getValue(PhysicsShape, 'friction')) !== null && _m !== void 0 ? _m : 0.5);
|
|
509
|
-
if (capsuleShape) {
|
|
510
|
-
PhysicsShape.data._engineShape[entity.index] = Number(capsuleShape);
|
|
511
|
-
}
|
|
512
|
-
else {
|
|
513
|
-
console.warn('PhysicsSystem: Failed to create capsule shape for entity', entity.index);
|
|
514
|
-
}
|
|
515
|
-
break;
|
|
516
|
-
}
|
|
517
|
-
case PhysicsShapeType.ConvexHull: {
|
|
518
|
-
const convexHullShape = this.createConvexHullShape(entity.object3D, (_o = entity.getValue(PhysicsShape, 'density')) !== null && _o !== void 0 ? _o : 1.0, (_p = entity.getValue(PhysicsShape, 'restitution')) !== null && _p !== void 0 ? _p : 0, (_q = entity.getValue(PhysicsShape, 'friction')) !== null && _q !== void 0 ? _q : 0.5);
|
|
519
|
-
if (convexHullShape) {
|
|
520
|
-
PhysicsShape.data._engineShape[entity.index] =
|
|
521
|
-
Number(convexHullShape);
|
|
652
|
+
if (shapeType === PhysicsShapeType.Capsules &&
|
|
653
|
+
!getCapsuleAxisEndpoints(dimensionsView[0], dimensionsView[1])) {
|
|
654
|
+
console.warn('PhysicsSystem: Capsule dimensions require a positive radius and a total height greater than or equal to twice the radius');
|
|
655
|
+
return;
|
|
656
|
+
}
|
|
657
|
+
const descriptor = {
|
|
658
|
+
type: shapeType,
|
|
659
|
+
dimensions: [dimensionsView[0], dimensionsView[1], dimensionsView[2]],
|
|
660
|
+
density: (_a = entity.getValue(PhysicsShape, 'density')) !== null && _a !== void 0 ? _a : 1,
|
|
661
|
+
restitution: (_b = entity.getValue(PhysicsShape, 'restitution')) !== null && _b !== void 0 ? _b : 0,
|
|
662
|
+
friction: (_c = entity.getValue(PhysicsShape, 'friction')) !== null && _c !== void 0 ? _c : 0.5,
|
|
663
|
+
};
|
|
664
|
+
const transfer = [];
|
|
665
|
+
if (shapeType === PhysicsShapeType.ConvexHull ||
|
|
666
|
+
shapeType === PhysicsShapeType.TriMesh) {
|
|
667
|
+
const ownsGeometry = !(object3D instanceof Mesh);
|
|
668
|
+
const geometry = object3D instanceof Mesh
|
|
669
|
+
? object3D.geometry
|
|
670
|
+
: generateMergedGeometry(object3D);
|
|
671
|
+
try {
|
|
672
|
+
const positionAttribute = geometry.attributes.position;
|
|
673
|
+
if (!positionAttribute) {
|
|
674
|
+
console.warn(`PhysicsSystem: Failed to get vertices for ${shapeType} shape with object3D name ${object3D.name} &id ${object3D.id}`);
|
|
675
|
+
return;
|
|
522
676
|
}
|
|
523
|
-
|
|
524
|
-
|
|
677
|
+
const vertices = Float32Array.from(positionAttribute.array);
|
|
678
|
+
descriptor.vertices = vertices.buffer;
|
|
679
|
+
transfer.push(descriptor.vertices);
|
|
680
|
+
if (shapeType === PhysicsShapeType.TriMesh) {
|
|
681
|
+
const sourceIndices = (_e = (_d = geometry.index) === null || _d === void 0 ? void 0 : _d.array) !== null && _e !== void 0 ? _e : sequentialIndices(positionAttribute.count);
|
|
682
|
+
const indices = Uint32Array.from(sourceIndices);
|
|
683
|
+
descriptor.indices = indices.buffer;
|
|
684
|
+
transfer.push(descriptor.indices);
|
|
525
685
|
}
|
|
526
|
-
break;
|
|
527
686
|
}
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
PhysicsShape.data._engineShape[entity.index] = Number(triMeshShape);
|
|
687
|
+
finally {
|
|
688
|
+
if (ownsGeometry) {
|
|
689
|
+
geometry.dispose();
|
|
532
690
|
}
|
|
533
|
-
else {
|
|
534
|
-
console.warn('PhysicsSystem: Failed to create tri-mesh shape for entity', entity.index);
|
|
535
|
-
}
|
|
536
|
-
break;
|
|
537
691
|
}
|
|
538
692
|
}
|
|
693
|
+
return { descriptor, transfer };
|
|
539
694
|
}
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
695
|
+
removeBody(entity) {
|
|
696
|
+
var _a;
|
|
697
|
+
this.failedBodyEntities.delete(entity);
|
|
698
|
+
const handle = PhysicsBody.data._engineBody[entity.index];
|
|
699
|
+
if (!handle) {
|
|
543
700
|
return;
|
|
544
701
|
}
|
|
545
|
-
|
|
546
|
-
this.
|
|
547
|
-
this.
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
this.havok.MaterialCombine.MINIMUM,
|
|
552
|
-
this.havok.MaterialCombine.MAXIMUM,
|
|
553
|
-
]);
|
|
554
|
-
return ballShape;
|
|
702
|
+
(_a = this.transport) === null || _a === void 0 ? void 0 : _a.postMessage({ type: 'remove-body', handle });
|
|
703
|
+
this.bodyEntities.delete(handle);
|
|
704
|
+
this.bodySnapshots.delete(handle);
|
|
705
|
+
this.bodyResultSequenceFloors.delete(handle);
|
|
706
|
+
this.discardPendingCommand(handle);
|
|
707
|
+
this.setEngineHandles(entity, 0);
|
|
555
708
|
}
|
|
556
|
-
|
|
557
|
-
if (
|
|
558
|
-
|
|
559
|
-
|
|
709
|
+
setEngineHandles(entity, handle) {
|
|
710
|
+
if (entity.hasComponent(PhysicsBody)) {
|
|
711
|
+
entity.setValue(PhysicsBody, '_engineBody', handle);
|
|
712
|
+
entity.setValue(PhysicsBody, '_engineOffset', 0);
|
|
560
713
|
}
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
return boxShape;
|
|
571
|
-
}
|
|
572
|
-
createCylinderShape(radius, height, density, restitution, friction) {
|
|
573
|
-
if (!this.havok) {
|
|
574
|
-
console.warn('PhysicsSystem: Cannot create cylinder shape - Havok physics engine not initialized');
|
|
575
|
-
return;
|
|
714
|
+
else {
|
|
715
|
+
PhysicsBody.data._engineBody[entity.index] = handle;
|
|
716
|
+
PhysicsBody.data._engineOffset[entity.index] = 0;
|
|
717
|
+
}
|
|
718
|
+
if (entity.hasComponent(PhysicsShape)) {
|
|
719
|
+
entity.setValue(PhysicsShape, '_engineShape', handle);
|
|
720
|
+
}
|
|
721
|
+
else {
|
|
722
|
+
PhysicsShape.data._engineShape[entity.index] = handle;
|
|
576
723
|
}
|
|
577
|
-
const cylinderShape = this.havok.HP_Shape_CreateCylinder([0, -height / 2, 0], [0, height / 2, 0], radius)[1];
|
|
578
|
-
this.havok.HP_Shape_SetDensity(cylinderShape, density);
|
|
579
|
-
this.havok.HP_Shape_SetMaterial(cylinderShape, [
|
|
580
|
-
friction,
|
|
581
|
-
friction,
|
|
582
|
-
restitution,
|
|
583
|
-
this.havok.MaterialCombine.MINIMUM,
|
|
584
|
-
this.havok.MaterialCombine.MAXIMUM,
|
|
585
|
-
]);
|
|
586
|
-
return cylinderShape;
|
|
587
724
|
}
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
725
|
+
subscribeReactiveOverrides() {
|
|
726
|
+
this.cleanupFuncs.push(this.queries.gravityOverrides.subscribe('disqualify', (entity) => {
|
|
727
|
+
if (entity.hasComponent(PhysicsBody) &&
|
|
728
|
+
entity.hasComponent(PhysicsShape)) {
|
|
729
|
+
this.syncGravityFactor(entity);
|
|
730
|
+
}
|
|
731
|
+
}), this.queries.linearDampingOverrides.subscribe('disqualify', (entity) => {
|
|
732
|
+
if (entity.hasComponent(PhysicsBody) &&
|
|
733
|
+
entity.hasComponent(PhysicsShape)) {
|
|
734
|
+
this.syncLinearDamping(entity);
|
|
735
|
+
}
|
|
736
|
+
}), this.queries.angularDampingOverrides.subscribe('disqualify', (entity) => {
|
|
737
|
+
if (entity.hasComponent(PhysicsBody) &&
|
|
738
|
+
entity.hasComponent(PhysicsShape)) {
|
|
739
|
+
this.syncAngularDamping(entity);
|
|
740
|
+
}
|
|
741
|
+
}));
|
|
742
|
+
}
|
|
743
|
+
syncReactiveOverrides() {
|
|
744
|
+
this.queries.gravityOverrides.entities.forEach((entity) => {
|
|
745
|
+
this.syncGravityFactor(entity);
|
|
746
|
+
});
|
|
747
|
+
this.queries.linearDampingOverrides.entities.forEach((entity) => {
|
|
748
|
+
this.syncLinearDamping(entity);
|
|
749
|
+
});
|
|
750
|
+
this.queries.angularDampingOverrides.entities.forEach((entity) => {
|
|
751
|
+
this.syncAngularDamping(entity);
|
|
752
|
+
});
|
|
753
|
+
}
|
|
754
|
+
syncGravityFactor(entity) {
|
|
755
|
+
const index = entity.index;
|
|
756
|
+
const handle = PhysicsBody.data._engineBody[index];
|
|
757
|
+
if (!handle) {
|
|
591
758
|
return;
|
|
592
759
|
}
|
|
593
|
-
const
|
|
594
|
-
if (
|
|
595
|
-
console.warn('PhysicsSystem: Capsule dimensions require a positive radius and a total height greater than or equal to twice the radius');
|
|
760
|
+
const target = PhysicsBody.data.gravityFactor[index];
|
|
761
|
+
if (PhysicsBody.data._engineGravityFactor[index] === target) {
|
|
596
762
|
return;
|
|
597
763
|
}
|
|
598
|
-
const
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
friction,
|
|
603
|
-
friction,
|
|
604
|
-
restitution,
|
|
605
|
-
this.havok.MaterialCombine.MINIMUM,
|
|
606
|
-
this.havok.MaterialCombine.MAXIMUM,
|
|
607
|
-
]);
|
|
608
|
-
return capsuleShape;
|
|
764
|
+
const command = this.getPendingCommand(handle);
|
|
765
|
+
command.flags |= 128 /* PhysicsCommandFlag.SetGravityFactor */;
|
|
766
|
+
command.gravityFactor = target;
|
|
767
|
+
PhysicsBody.data._engineGravityFactor[index] = target;
|
|
609
768
|
}
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
769
|
+
syncLinearDamping(entity) {
|
|
770
|
+
const index = entity.index;
|
|
771
|
+
const handle = PhysicsBody.data._engineBody[index];
|
|
772
|
+
if (!handle) {
|
|
613
773
|
return;
|
|
614
774
|
}
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
const ownsGeometry = !(object3D instanceof Mesh);
|
|
618
|
-
const geometry = object3D instanceof Mesh
|
|
619
|
-
? object3D.geometry
|
|
620
|
-
: generateMergedGeometry(object3D);
|
|
621
|
-
const positionAttribute = geometry.attributes.position;
|
|
622
|
-
if (!positionAttribute) {
|
|
623
|
-
console.warn('PhysicsSystem: Failed to get vertices for convex hull shape with object3D name ' +
|
|
624
|
-
object3D.name +
|
|
625
|
-
' &id ' +
|
|
626
|
-
object3D.id);
|
|
627
|
-
if (ownsGeometry) {
|
|
628
|
-
geometry.dispose();
|
|
629
|
-
}
|
|
775
|
+
const target = PhysicsBody.data.linearDamping[index];
|
|
776
|
+
if (PhysicsBody.data._engineLinearDamping[index] === target) {
|
|
630
777
|
return;
|
|
631
778
|
}
|
|
632
|
-
const
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
geometry.dispose();
|
|
637
|
-
}
|
|
638
|
-
this.havok.HP_Shape_SetDensity(convexHullShape, density);
|
|
639
|
-
this.havok.HP_Shape_SetMaterial(convexHullShape, [
|
|
640
|
-
friction,
|
|
641
|
-
friction,
|
|
642
|
-
restitution,
|
|
643
|
-
this.havok.MaterialCombine.MINIMUM,
|
|
644
|
-
this.havok.MaterialCombine.MAXIMUM,
|
|
645
|
-
]);
|
|
646
|
-
return convexHullShape;
|
|
779
|
+
const command = this.getPendingCommand(handle);
|
|
780
|
+
command.flags |= 256 /* PhysicsCommandFlag.SetLinearDamping */;
|
|
781
|
+
command.linearDamping = target;
|
|
782
|
+
PhysicsBody.data._engineLinearDamping[index] = target;
|
|
647
783
|
}
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
784
|
+
syncAngularDamping(entity) {
|
|
785
|
+
const index = entity.index;
|
|
786
|
+
const handle = PhysicsBody.data._engineBody[index];
|
|
787
|
+
if (!handle) {
|
|
652
788
|
return;
|
|
653
789
|
}
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
const ownsGeometry = !(object3D instanceof Mesh);
|
|
657
|
-
const geometry = object3D instanceof Mesh
|
|
658
|
-
? object3D.geometry
|
|
659
|
-
: generateMergedGeometry(object3D);
|
|
660
|
-
const positionAttribute = geometry.attributes.position;
|
|
661
|
-
if (!positionAttribute) {
|
|
662
|
-
console.warn('PhysicsSystem: Failed to get vertices for tri-mesh shape with object3D name ' +
|
|
663
|
-
object3D.name +
|
|
664
|
-
' &id ' +
|
|
665
|
-
object3D.id);
|
|
666
|
-
if (ownsGeometry) {
|
|
667
|
-
geometry.dispose();
|
|
668
|
-
}
|
|
790
|
+
const target = PhysicsBody.data.angularDamping[index];
|
|
791
|
+
if (PhysicsBody.data._engineAngularDamping[index] === target) {
|
|
669
792
|
return;
|
|
670
793
|
}
|
|
671
|
-
const
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
const triMeshShape = this.havok.HP_Shape_CreateMesh(vertices.offset, vertices.numObjects / 3, indices.offset, indices.numObjects / 3)[1];
|
|
676
|
-
this.havok._free(vertices.offset);
|
|
677
|
-
this.havok._free(indices.offset);
|
|
678
|
-
if (ownsGeometry) {
|
|
679
|
-
geometry.dispose();
|
|
680
|
-
}
|
|
681
|
-
this.havok.HP_Shape_SetDensity(triMeshShape, density);
|
|
682
|
-
this.havok.HP_Shape_SetMaterial(triMeshShape, [
|
|
683
|
-
friction,
|
|
684
|
-
friction,
|
|
685
|
-
restitution,
|
|
686
|
-
this.havok.MaterialCombine.MINIMUM,
|
|
687
|
-
this.havok.MaterialCombine.MAXIMUM,
|
|
688
|
-
]);
|
|
689
|
-
return triMeshShape;
|
|
690
|
-
}
|
|
691
|
-
getVertices(vertices) {
|
|
692
|
-
const bytesPerFloat = 4;
|
|
693
|
-
const nBytes = vertices.length * bytesPerFloat;
|
|
694
|
-
const bufferBegin = this.havok._malloc(nBytes);
|
|
695
|
-
const ret = new Float32Array(this.havok.HEAPU8.buffer, bufferBegin, vertices.length);
|
|
696
|
-
for (let i = 0; i < vertices.length; i++) {
|
|
697
|
-
ret[i] = vertices[i];
|
|
698
|
-
}
|
|
699
|
-
return { offset: bufferBegin, numObjects: vertices.length };
|
|
794
|
+
const command = this.getPendingCommand(handle);
|
|
795
|
+
command.flags |= 512 /* PhysicsCommandFlag.SetAngularDamping */;
|
|
796
|
+
command.angularDamping = target;
|
|
797
|
+
PhysicsBody.data._engineAngularDamping[index] = target;
|
|
700
798
|
}
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
799
|
+
queueManipulations() {
|
|
800
|
+
this.queries.manipluatedEntities.entities.forEach((entity) => {
|
|
801
|
+
var _a, _b;
|
|
802
|
+
const handle = entity.getValue(PhysicsBody, '_engineBody');
|
|
803
|
+
if (!handle || !entity.object3D) {
|
|
804
|
+
return;
|
|
805
|
+
}
|
|
806
|
+
const force = entity.getVectorView(PhysicsManipulation, 'force');
|
|
807
|
+
const linearVelocity = entity.getVectorView(PhysicsManipulation, 'linearVelocity');
|
|
808
|
+
const angularVelocity = entity.getVectorView(PhysicsManipulation, 'angularVelocity');
|
|
809
|
+
const hasForce = !isZeroVector(force);
|
|
810
|
+
const hasLinearVelocity = !isZeroVector(linearVelocity);
|
|
811
|
+
const hasAngularVelocity = !isZeroVector(angularVelocity);
|
|
812
|
+
if (!hasForce && !hasLinearVelocity && !hasAngularVelocity) {
|
|
813
|
+
entity.removeComponent(PhysicsManipulation);
|
|
814
|
+
return;
|
|
815
|
+
}
|
|
816
|
+
const command = this.getPendingCommand(handle);
|
|
817
|
+
if (hasForce) {
|
|
818
|
+
command.flags |= 16 /* PhysicsCommandFlag.ApplyImpulse */;
|
|
819
|
+
const impulsePosition = (_b = (_a = this.bodySnapshots.get(handle)) === null || _a === void 0 ? void 0 : _a.currentPosition) !== null && _b !== void 0 ? _b : entity.object3D.position;
|
|
820
|
+
command.impulsePoint[0] = impulsePosition.x;
|
|
821
|
+
command.impulsePoint[1] = impulsePosition.y;
|
|
822
|
+
command.impulsePoint[2] = impulsePosition.z;
|
|
823
|
+
command.impulse[0] += force[0];
|
|
824
|
+
command.impulse[1] += force[1];
|
|
825
|
+
command.impulse[2] += force[2];
|
|
826
|
+
}
|
|
827
|
+
if (hasLinearVelocity) {
|
|
828
|
+
command.flags |= 32 /* PhysicsCommandFlag.SetLinearVelocity */;
|
|
829
|
+
command.linearVelocity[0] = linearVelocity[0];
|
|
830
|
+
command.linearVelocity[1] = linearVelocity[1];
|
|
831
|
+
command.linearVelocity[2] = linearVelocity[2];
|
|
832
|
+
}
|
|
833
|
+
if (hasAngularVelocity) {
|
|
834
|
+
command.flags |= 64 /* PhysicsCommandFlag.SetAngularVelocity */;
|
|
835
|
+
command.angularVelocity[0] = angularVelocity[0];
|
|
836
|
+
command.angularVelocity[1] = angularVelocity[1];
|
|
837
|
+
command.angularVelocity[2] = angularVelocity[2];
|
|
838
|
+
}
|
|
839
|
+
entity.removeComponent(PhysicsManipulation);
|
|
840
|
+
});
|
|
710
841
|
}
|
|
711
842
|
}
|
|
712
843
|
|