@flighthq/physics2d-abi 0.4.0-edge.1908.751021c
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/contract.d.ts +6 -0
- package/dist/contract.d.ts.map +1 -0
- package/dist/contract.js +6 -0
- package/dist/contract.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/physics2DAbi.d.ts +11 -0
- package/dist/physics2DAbi.d.ts.map +1 -0
- package/dist/physics2DAbi.js +32 -0
- package/dist/physics2DAbi.js.map +1 -0
- package/dist/physics2DAbiBuffer.d.ts +10 -0
- package/dist/physics2DAbiBuffer.d.ts.map +1 -0
- package/dist/physics2DAbiBuffer.js +81 -0
- package/dist/physics2DAbiBuffer.js.map +1 -0
- package/dist/physics2DAbiCommand.d.ts +17 -0
- package/dist/physics2DAbiCommand.d.ts.map +1 -0
- package/dist/physics2DAbiCommand.js +459 -0
- package/dist/physics2DAbiCommand.js.map +1 -0
- package/dist/physics2DAbiLayout.d.ts +220 -0
- package/dist/physics2DAbiLayout.d.ts.map +1 -0
- package/dist/physics2DAbiLayout.js +250 -0
- package/dist/physics2DAbiLayout.js.map +1 -0
- package/dist/physics2DAbiQuery.d.ts +7 -0
- package/dist/physics2DAbiQuery.d.ts.map +1 -0
- package/dist/physics2DAbiQuery.js +16 -0
- package/dist/physics2DAbiQuery.js.map +1 -0
- package/dist/referencePhysics2DAbi.d.ts +3 -0
- package/dist/referencePhysics2DAbi.d.ts.map +1 -0
- package/dist/referencePhysics2DAbi.js +1089 -0
- package/dist/referencePhysics2DAbi.js.map +1 -0
- package/package.json +50 -0
- package/src/physics2DAbi.test.ts +348 -0
- package/src/physics2DAbiBuffer.test.ts +121 -0
- package/src/physics2DAbiCommand.test.ts +337 -0
- package/src/physics2DAbiLayout.test.ts +293 -0
- package/src/physics2DAbiQuery.test.ts +145 -0
- package/src/referencePhysics2DAbi.test.ts +271 -0
|
@@ -0,0 +1,1089 @@
|
|
|
1
|
+
import { addPhysics2DBody, addPhysics2DCollider, addPhysics2DJoint, applyPhysics2DForce, applyPhysics2DForceAtPoint, applyPhysics2DLinearImpulse, applyPhysics2DLinearImpulseAtPoint, applyPhysics2DTorque, createPhysics2DCollider, createPhysics2DDistanceJoint, createPhysics2DGearJoint, createPhysics2DJointReaction, createPhysics2DMouseJoint, createPhysics2DPrismaticJoint, createPhysics2DPulleyJoint, createPhysics2DQueryResult, createPhysics2DRayResult, createPhysics2DRevoluteJoint, createPhysics2DRopeJoint, createPhysics2DShapeCastResult, createPhysics2DWeldJoint, createPhysics2DWheelJoint, createPhysics2DWorld, createRigidBody2D, isPhysics2DBodyStateValid, isPhysics2DContactStateValid, isPhysics2DGravityValid, isPhysics2DJointStateValid, isPhysics2DPreviousTimestepValid, isPhysics2DSolverConfigValid, isPhysics2DTimestepValid, queryPhysics2DPoint, queryPhysics2DRay, queryPhysics2DRayClosest, queryPhysics2DRegion, queryPhysics2DShapeCast, registerBuiltInPhysics2DJointSolvers, removePhysics2DBody, removePhysics2DCollider, removePhysics2DJoint, setPhysics2DBodyBullet, setPhysics2DBodyFixedRotation, setPhysics2DBodySleepEnabled, setPhysics2DBodyTransform, setPhysics2DBodyType, stepPhysics2D, wakePhysics2DBody, writePhysics2DJointReaction, } from '@flighthq/physics2d/contract';
|
|
2
|
+
import { Physics2DAbiBodyFlag, Physics2DAbiBodyType, Physics2DAbiBodyValueStride, Physics2DAbiCapability, Physics2DAbiCommandByteLength, Physics2DAbiCommandHeaderByteLength, Physics2DAbiCommandKind, Physics2DAbiCommandMagic, Physics2DAbiCommandRecordHeaderByteLength, Physics2DAbiContactFlag, Physics2DAbiContactIdStride, Physics2DAbiContactPointValueStride, Physics2DAbiContactValue, Physics2DAbiContactValueStride, Physics2DAbiJointFlag, Physics2DAbiJointKind, Physics2DAbiJointKindValueCount, Physics2DAbiJointValueStride, Physics2DAbiMaxContactPoints, Physics2DAbiQueryValueStride, Physics2DAbiSetColliderPayloadOffset, Physics2DAbiSetJointPayloadOffset, Physics2DAbiShapeHeaderByteLength, Physics2DAbiShapeKind, Physics2DAbiVersion, } from './physics2DAbiLayout';
|
|
3
|
+
// The executable specification for the Physics2D ABI: a real `Physics2DWorld` behind persistent
|
|
4
|
+
// handles, caller-chosen ids, and packed buffers. It exists to make the wire contract testable rather
|
|
5
|
+
// than to be fast — a native shadow replaces this one constructor and inherits every codec above it.
|
|
6
|
+
export function createReferencePhysics2DAbi() {
|
|
7
|
+
const worlds = new Map();
|
|
8
|
+
let nextWorldHandle = 1;
|
|
9
|
+
return {
|
|
10
|
+
version: Physics2DAbiVersion,
|
|
11
|
+
capabilities: Physics2DAbiCapability.ContactHooks |
|
|
12
|
+
Physics2DAbiCapability.PersistentWorlds |
|
|
13
|
+
Physics2DAbiCapability.Queries |
|
|
14
|
+
Physics2DAbiCapability.SelectiveReadback,
|
|
15
|
+
createWorld() {
|
|
16
|
+
if (nextWorldHandle > 0xffffffff)
|
|
17
|
+
return 0;
|
|
18
|
+
const handle = nextWorldHandle;
|
|
19
|
+
nextWorldHandle += 1;
|
|
20
|
+
const world = createPhysics2DWorld();
|
|
21
|
+
registerBuiltInPhysics2DJointSolvers(world);
|
|
22
|
+
worlds.set(handle, createReferenceWorld(world));
|
|
23
|
+
return handle;
|
|
24
|
+
},
|
|
25
|
+
destroyWorld(handle) {
|
|
26
|
+
if (worlds.get(handle)?.stepping === true)
|
|
27
|
+
return false;
|
|
28
|
+
return worlds.delete(handle);
|
|
29
|
+
},
|
|
30
|
+
getWorldStatus(handle) {
|
|
31
|
+
const state = worlds.get(handle);
|
|
32
|
+
if (state === undefined)
|
|
33
|
+
return 'Stale';
|
|
34
|
+
return state.stepping ? 'Busy' : 'Ready';
|
|
35
|
+
},
|
|
36
|
+
execute(handle, commands, out) {
|
|
37
|
+
const state = worlds.get(handle);
|
|
38
|
+
if (state === undefined)
|
|
39
|
+
return failExecution(out, 'StaleWorld', 0, Physics2DAbiCommandHeaderByteLength, 0);
|
|
40
|
+
if (state.stepping)
|
|
41
|
+
return failExecution(out, 'BusyWorld', 0, Physics2DAbiCommandHeaderByteLength, 0);
|
|
42
|
+
return executeCommands(state, commands, out);
|
|
43
|
+
},
|
|
44
|
+
step(handle, dt, hooks) {
|
|
45
|
+
const state = worlds.get(handle);
|
|
46
|
+
if (state === undefined)
|
|
47
|
+
return 'StaleWorld';
|
|
48
|
+
if (state.stepping)
|
|
49
|
+
return 'BusyWorld';
|
|
50
|
+
return stepReferenceWorld(state, dt, hooks);
|
|
51
|
+
},
|
|
52
|
+
readBodies(handle, bodyIds, out) {
|
|
53
|
+
const state = worlds.get(handle);
|
|
54
|
+
if (state === undefined || state.stepping)
|
|
55
|
+
return false;
|
|
56
|
+
readBodies(state, bodyIds, out);
|
|
57
|
+
return true;
|
|
58
|
+
},
|
|
59
|
+
readContacts(handle, selection, out) {
|
|
60
|
+
const state = worlds.get(handle);
|
|
61
|
+
if (state === undefined || state.stepping)
|
|
62
|
+
return false;
|
|
63
|
+
readContacts(state, selection, out);
|
|
64
|
+
return true;
|
|
65
|
+
},
|
|
66
|
+
readJoints(handle, out) {
|
|
67
|
+
const state = worlds.get(handle);
|
|
68
|
+
if (state === undefined || state.stepping)
|
|
69
|
+
return false;
|
|
70
|
+
readJoints(state, out);
|
|
71
|
+
return true;
|
|
72
|
+
},
|
|
73
|
+
queryPoint(handle, x, y, filter, out) {
|
|
74
|
+
const state = worlds.get(handle);
|
|
75
|
+
if (state === undefined || state.stepping)
|
|
76
|
+
return false;
|
|
77
|
+
queryPhysics2DPoint(state.world, x, y, state.query, filter ?? undefined);
|
|
78
|
+
writeQueryHits(state, state.query, out);
|
|
79
|
+
return true;
|
|
80
|
+
},
|
|
81
|
+
queryRay(handle, originX, originY, directionX, directionY, maxFraction, closest, filter, out) {
|
|
82
|
+
const state = worlds.get(handle);
|
|
83
|
+
if (state === undefined || state.stepping)
|
|
84
|
+
return false;
|
|
85
|
+
const query = closest ? queryPhysics2DRayClosest : queryPhysics2DRay;
|
|
86
|
+
query(state.world, originX, originY, directionX, directionY, state.ray, maxFraction, filter ?? undefined);
|
|
87
|
+
writeRayHits(state, out);
|
|
88
|
+
return true;
|
|
89
|
+
},
|
|
90
|
+
queryRegion(handle, region, filter, out) {
|
|
91
|
+
const state = worlds.get(handle);
|
|
92
|
+
if (state === undefined || state.stepping)
|
|
93
|
+
return false;
|
|
94
|
+
queryPhysics2DRegion(state.world, region, state.query, filter ?? undefined);
|
|
95
|
+
writeQueryHits(state, state.query, out);
|
|
96
|
+
return true;
|
|
97
|
+
},
|
|
98
|
+
queryShapeCast(handle, shape, dx, dy, maxFraction, filter, out) {
|
|
99
|
+
const state = worlds.get(handle);
|
|
100
|
+
if (state === undefined || state.stepping)
|
|
101
|
+
return false;
|
|
102
|
+
queryPhysics2DShapeCast(state.world, shape, dx, dy, state.shapeCast, maxFraction, filter ?? undefined);
|
|
103
|
+
writeShapeCastHit(state, out);
|
|
104
|
+
return true;
|
|
105
|
+
},
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
function createReferenceWorld(world) {
|
|
109
|
+
let state;
|
|
110
|
+
state = {
|
|
111
|
+
world,
|
|
112
|
+
bodyById: new Map(),
|
|
113
|
+
idByBody: new Map(),
|
|
114
|
+
bodyIds: [],
|
|
115
|
+
colliderById: new Map(),
|
|
116
|
+
idByCollider: new Map(),
|
|
117
|
+
jointById: new Map(),
|
|
118
|
+
idByJoint: new Map(),
|
|
119
|
+
jointIds: [],
|
|
120
|
+
brokenJoints: [],
|
|
121
|
+
query: createPhysics2DQueryResult(),
|
|
122
|
+
ray: createPhysics2DRayResult(),
|
|
123
|
+
shapeCast: createPhysics2DShapeCastResult(),
|
|
124
|
+
reaction: createPhysics2DJointReaction(),
|
|
125
|
+
bodyValues: new Array(Physics2DAbiBodyValueStride).fill(0),
|
|
126
|
+
jointCommonValues: new Array(6).fill(0),
|
|
127
|
+
jointKindValues: new Array(Physics2DAbiJointKindValueCount).fill(0),
|
|
128
|
+
activeHooks: null,
|
|
129
|
+
stepping: false,
|
|
130
|
+
lastStepDt: 0,
|
|
131
|
+
preSolveHook(_world, contact) {
|
|
132
|
+
const hooks = state.activeHooks;
|
|
133
|
+
if (hooks?.preSolve !== null && hooks?.preSolve !== undefined) {
|
|
134
|
+
invokeContactHook(state, contact, hooks.buffer, hooks.preSolve);
|
|
135
|
+
}
|
|
136
|
+
},
|
|
137
|
+
postSolveHook(_world, contact) {
|
|
138
|
+
const hooks = state.activeHooks;
|
|
139
|
+
if (hooks?.postSolve !== null && hooks?.postSolve !== undefined) {
|
|
140
|
+
invokeContactHook(state, contact, hooks.buffer, hooks.postSolve);
|
|
141
|
+
}
|
|
142
|
+
},
|
|
143
|
+
};
|
|
144
|
+
return state;
|
|
145
|
+
}
|
|
146
|
+
function executeCommands(state, commands, out) {
|
|
147
|
+
if (!isCommandBufferValid(commands)) {
|
|
148
|
+
return failExecution(out, 'InvalidBuffer', 0, Physics2DAbiCommandHeaderByteLength, 0);
|
|
149
|
+
}
|
|
150
|
+
const view = new DataView(commands.data.buffer, commands.data.byteOffset, commands.data.byteLength);
|
|
151
|
+
let byteOffset = Physics2DAbiCommandHeaderByteLength;
|
|
152
|
+
for (let commandIndex = 0; commandIndex < commands.commandCount; commandIndex += 1) {
|
|
153
|
+
if (byteOffset + Physics2DAbiCommandRecordHeaderByteLength > commands.byteLength) {
|
|
154
|
+
return failExecution(out, 'InvalidBuffer', commandIndex, byteOffset, 0);
|
|
155
|
+
}
|
|
156
|
+
const kind = view.getUint32(byteOffset, true);
|
|
157
|
+
const byteLength = view.getUint32(byteOffset + 4, true);
|
|
158
|
+
if (byteLength < Physics2DAbiCommandRecordHeaderByteLength ||
|
|
159
|
+
byteLength % 8 !== 0 ||
|
|
160
|
+
byteOffset + byteLength > commands.byteLength) {
|
|
161
|
+
return failExecution(out, 'InvalidBuffer', commandIndex, byteOffset, kind);
|
|
162
|
+
}
|
|
163
|
+
const record = {
|
|
164
|
+
view,
|
|
165
|
+
start: byteOffset,
|
|
166
|
+
payload: byteOffset + Physics2DAbiCommandRecordHeaderByteLength,
|
|
167
|
+
byteLength,
|
|
168
|
+
kind,
|
|
169
|
+
objectId: view.getUint32(byteOffset + 8, true),
|
|
170
|
+
relatedId: view.getUint32(byteOffset + 12, true),
|
|
171
|
+
};
|
|
172
|
+
const status = executeCommand(state, record);
|
|
173
|
+
if (status !== 'Complete')
|
|
174
|
+
return failExecution(out, status, commandIndex, byteOffset, kind);
|
|
175
|
+
byteOffset += byteLength;
|
|
176
|
+
}
|
|
177
|
+
if (byteOffset !== commands.byteLength) {
|
|
178
|
+
return failExecution(out, 'InvalidBuffer', commands.commandCount, byteOffset, 0);
|
|
179
|
+
}
|
|
180
|
+
out.status = 'Complete';
|
|
181
|
+
out.commandIndex = commands.commandCount;
|
|
182
|
+
out.byteOffset = byteOffset;
|
|
183
|
+
out.commandKind = 0;
|
|
184
|
+
return true;
|
|
185
|
+
}
|
|
186
|
+
function executeCommand(state, command) {
|
|
187
|
+
if (command.kind === Physics2DAbiCommandKind.SetGravity)
|
|
188
|
+
return executeSetGravity(state, command);
|
|
189
|
+
if (command.kind === Physics2DAbiCommandKind.SetSolverConfig)
|
|
190
|
+
return executeSetSolverConfig(state, command);
|
|
191
|
+
if (command.kind === Physics2DAbiCommandKind.SetBody)
|
|
192
|
+
return executeSetBody(state, command);
|
|
193
|
+
if (command.kind === Physics2DAbiCommandKind.DestroyBody)
|
|
194
|
+
return executeDestroyBody(state, command);
|
|
195
|
+
if (command.kind === Physics2DAbiCommandKind.SetCollider)
|
|
196
|
+
return executeSetCollider(state, command);
|
|
197
|
+
if (command.kind === Physics2DAbiCommandKind.DestroyCollider)
|
|
198
|
+
return executeDestroyCollider(state, command);
|
|
199
|
+
if (command.kind === Physics2DAbiCommandKind.SetJoint)
|
|
200
|
+
return executeSetJoint(state, command);
|
|
201
|
+
if (command.kind === Physics2DAbiCommandKind.DestroyJoint)
|
|
202
|
+
return executeDestroyJoint(state, command);
|
|
203
|
+
if (command.kind === Physics2DAbiCommandKind.ApplyForce) {
|
|
204
|
+
return executeBodyAction(state, command, (body, x, y) => applyPhysics2DForce(body, x, y));
|
|
205
|
+
}
|
|
206
|
+
if (command.kind === Physics2DAbiCommandKind.ApplyForceAtPoint) {
|
|
207
|
+
return executeBodyPointAction(state, command, applyPhysics2DForceAtPoint);
|
|
208
|
+
}
|
|
209
|
+
if (command.kind === Physics2DAbiCommandKind.ApplyLinearImpulse) {
|
|
210
|
+
return executeBodyAction(state, command, (body, x, y) => applyPhysics2DLinearImpulse(body, x, y));
|
|
211
|
+
}
|
|
212
|
+
if (command.kind === Physics2DAbiCommandKind.ApplyLinearImpulseAtPoint) {
|
|
213
|
+
return executeBodyPointAction(state, command, applyPhysics2DLinearImpulseAtPoint);
|
|
214
|
+
}
|
|
215
|
+
// The plane's single torque scalar travels in the first slot of the shared body-action record, so the
|
|
216
|
+
// remaining three must be zero rather than merely ignored: silently accepting a vector here would let
|
|
217
|
+
// a 3D-shaped caller believe its Y and Z torque had been applied.
|
|
218
|
+
if (command.kind === Physics2DAbiCommandKind.ApplyTorque) {
|
|
219
|
+
return executeBodyAction(state, command, (body, x) => applyPhysics2DTorque(body, x), true);
|
|
220
|
+
}
|
|
221
|
+
if (command.kind === Physics2DAbiCommandKind.WakeBody)
|
|
222
|
+
return executeWakeBody(state, command);
|
|
223
|
+
return 'InvalidCommand';
|
|
224
|
+
}
|
|
225
|
+
function executeSetGravity(state, command) {
|
|
226
|
+
if (command.byteLength !== Physics2DAbiCommandByteLength.SetGravity ||
|
|
227
|
+
command.objectId !== 0 ||
|
|
228
|
+
command.relatedId !== 0) {
|
|
229
|
+
return 'InvalidCommand';
|
|
230
|
+
}
|
|
231
|
+
const x = command.view.getFloat64(command.payload, true);
|
|
232
|
+
const y = command.view.getFloat64(command.payload + 8, true);
|
|
233
|
+
if (!Number.isFinite(x) || !Number.isFinite(y))
|
|
234
|
+
return 'RejectedMutation';
|
|
235
|
+
state.world.gravityX = x;
|
|
236
|
+
state.world.gravityY = y;
|
|
237
|
+
return 'Complete';
|
|
238
|
+
}
|
|
239
|
+
function executeSetSolverConfig(state, command) {
|
|
240
|
+
if (command.byteLength !== Physics2DAbiCommandByteLength.SetSolverConfig ||
|
|
241
|
+
command.objectId !== 0 ||
|
|
242
|
+
command.relatedId !== 0) {
|
|
243
|
+
return 'InvalidCommand';
|
|
244
|
+
}
|
|
245
|
+
const flags = command.view.getUint32(command.payload, true);
|
|
246
|
+
if ((flags & 0b111) !== flags ||
|
|
247
|
+
command.view.getUint32(command.payload + 4, true) !== 0 ||
|
|
248
|
+
command.view.getUint32(command.payload + 24, true) !== 0 ||
|
|
249
|
+
command.view.getUint32(command.payload + 28, true) !== 0) {
|
|
250
|
+
return 'InvalidCommand';
|
|
251
|
+
}
|
|
252
|
+
const config = {
|
|
253
|
+
allowSleeping: (flags & 1) !== 0,
|
|
254
|
+
continuousCollision: (flags & (1 << 1)) !== 0,
|
|
255
|
+
warmStarting: (flags & (1 << 2)) !== 0,
|
|
256
|
+
maxCcdSubsteps: command.view.getUint32(command.payload + 8, true),
|
|
257
|
+
maxCcdRotationSubsteps: command.view.getUint32(command.payload + 12, true),
|
|
258
|
+
velocityIterations: command.view.getUint32(command.payload + 16, true),
|
|
259
|
+
positionIterations: command.view.getUint32(command.payload + 20, true),
|
|
260
|
+
sleepLinearThreshold: command.view.getFloat64(command.payload + 32, true),
|
|
261
|
+
sleepAngularThreshold: command.view.getFloat64(command.payload + 40, true),
|
|
262
|
+
timeToSleep: command.view.getFloat64(command.payload + 48, true),
|
|
263
|
+
penetrationSlop: command.view.getFloat64(command.payload + 56, true),
|
|
264
|
+
positionCorrection: command.view.getFloat64(command.payload + 64, true),
|
|
265
|
+
restitutionThreshold: command.view.getFloat64(command.payload + 72, true),
|
|
266
|
+
};
|
|
267
|
+
if (!isPhysics2DSolverConfigValid(config))
|
|
268
|
+
return 'RejectedMutation';
|
|
269
|
+
state.world.config = config;
|
|
270
|
+
return 'Complete';
|
|
271
|
+
}
|
|
272
|
+
// Mass, inertia, and centre of mass are absent from the mutation path on purpose. Physics2D DERIVES
|
|
273
|
+
// them from collider geometry and density, and re-derives them whenever a collider, body type, or
|
|
274
|
+
// fixed-rotation flag changes. Accepting them here would let a caller author a body whose mass
|
|
275
|
+
// contradicts its own shape, and the next collider command would silently overwrite it anyway. They
|
|
276
|
+
// remain on the wire for readback, which is the direction in which they are meaningful.
|
|
277
|
+
function executeSetBody(state, command) {
|
|
278
|
+
if (command.byteLength !== Physics2DAbiCommandByteLength.SetBody ||
|
|
279
|
+
!isObjectId(command.objectId) ||
|
|
280
|
+
command.relatedId !== 0) {
|
|
281
|
+
return 'InvalidCommand';
|
|
282
|
+
}
|
|
283
|
+
const flags = command.view.getUint32(command.payload, true);
|
|
284
|
+
if ((flags & PHYSICS2D_ABI_BODY_FLAG_MASK) !== flags || command.view.getUint32(command.payload + 4, true) !== 0) {
|
|
285
|
+
return 'InvalidCommand';
|
|
286
|
+
}
|
|
287
|
+
const type = decodeBodyType(flags & Physics2DAbiBodyFlag.TypeMask);
|
|
288
|
+
const values = readFloat64Values(command, 8, state.bodyValues);
|
|
289
|
+
if (type === null || !isBodyValueBlockValid(values))
|
|
290
|
+
return 'RejectedMutation';
|
|
291
|
+
const existing = state.bodyById.get(command.objectId);
|
|
292
|
+
const body = existing ?? createRigidBody2D(type, values[0], values[1], values[2]);
|
|
293
|
+
if (existing === undefined) {
|
|
294
|
+
addPhysics2DBody(state.world, body);
|
|
295
|
+
state.bodyById.set(command.objectId, body);
|
|
296
|
+
state.idByBody.set(body, command.objectId);
|
|
297
|
+
insertSorted(state.bodyIds, command.objectId);
|
|
298
|
+
}
|
|
299
|
+
if (!setPhysics2DBodyType(state.world, body, type))
|
|
300
|
+
return 'RejectedMutation';
|
|
301
|
+
if (!setPhysics2DBodyFixedRotation(state.world, body, (flags & Physics2DAbiBodyFlag.FixedRotation) !== 0)) {
|
|
302
|
+
return 'RejectedMutation';
|
|
303
|
+
}
|
|
304
|
+
if (!setPhysics2DBodyBullet(state.world, body, (flags & Physics2DAbiBodyFlag.Bullet) !== 0)) {
|
|
305
|
+
return 'RejectedMutation';
|
|
306
|
+
}
|
|
307
|
+
if (!setPhysics2DBodySleepEnabled(state.world, body, (flags & Physics2DAbiBodyFlag.SleepEnabled) !== 0)) {
|
|
308
|
+
return 'RejectedMutation';
|
|
309
|
+
}
|
|
310
|
+
if (!setPhysics2DBodyTransform(state.world, body, values[0], values[1], values[2]))
|
|
311
|
+
return 'RejectedMutation';
|
|
312
|
+
body.velocityX = values[3];
|
|
313
|
+
body.velocityY = values[4];
|
|
314
|
+
body.angularVelocity = values[5];
|
|
315
|
+
body.forceX = values[6];
|
|
316
|
+
body.forceY = values[7];
|
|
317
|
+
body.torque = values[8];
|
|
318
|
+
body.linearDamping = values[13];
|
|
319
|
+
body.angularDamping = values[14];
|
|
320
|
+
body.gravityScale = values[15];
|
|
321
|
+
body.sleepTimer = values[16];
|
|
322
|
+
if ((flags & Physics2DAbiBodyFlag.Sleeping) === 0)
|
|
323
|
+
wakePhysics2DBody(body);
|
|
324
|
+
else
|
|
325
|
+
body.sleeping = body.type !== 'static';
|
|
326
|
+
return 'Complete';
|
|
327
|
+
}
|
|
328
|
+
function executeDestroyBody(state, command) {
|
|
329
|
+
if (command.byteLength !== Physics2DAbiCommandByteLength.DestroyBody ||
|
|
330
|
+
!isObjectId(command.objectId) ||
|
|
331
|
+
command.relatedId !== 0) {
|
|
332
|
+
return 'InvalidCommand';
|
|
333
|
+
}
|
|
334
|
+
const body = state.bodyById.get(command.objectId);
|
|
335
|
+
if (body === undefined)
|
|
336
|
+
return 'MissingBody';
|
|
337
|
+
for (const [id, held] of state.colliderById) {
|
|
338
|
+
if (held.bodyId !== command.objectId)
|
|
339
|
+
continue;
|
|
340
|
+
state.colliderById.delete(id);
|
|
341
|
+
state.idByCollider.delete(held.collider);
|
|
342
|
+
}
|
|
343
|
+
for (const [id, joint] of state.jointById) {
|
|
344
|
+
if (joint.bodyA !== body.index && joint.bodyB !== body.index)
|
|
345
|
+
continue;
|
|
346
|
+
state.jointById.delete(id);
|
|
347
|
+
state.idByJoint.delete(joint);
|
|
348
|
+
removeSorted(state.jointIds, id);
|
|
349
|
+
}
|
|
350
|
+
removePhysics2DBody(state.world, body);
|
|
351
|
+
state.bodyById.delete(command.objectId);
|
|
352
|
+
state.idByBody.delete(body);
|
|
353
|
+
removeSorted(state.bodyIds, command.objectId);
|
|
354
|
+
return 'Complete';
|
|
355
|
+
}
|
|
356
|
+
function executeSetCollider(state, command) {
|
|
357
|
+
if (!isObjectId(command.objectId) ||
|
|
358
|
+
!isObjectId(command.relatedId) ||
|
|
359
|
+
command.byteLength < Physics2DAbiCommandByteLength.SetColliderMinimum) {
|
|
360
|
+
return 'InvalidCommand';
|
|
361
|
+
}
|
|
362
|
+
const body = state.bodyById.get(command.relatedId);
|
|
363
|
+
if (body === undefined)
|
|
364
|
+
return 'MissingBody';
|
|
365
|
+
const sensorFlag = command.view.getUint32(command.payload, true);
|
|
366
|
+
if (sensorFlag > 1)
|
|
367
|
+
return 'InvalidCommand';
|
|
368
|
+
const shapeKind = command.view.getUint32(command.payload + Physics2DAbiSetColliderPayloadOffset.Shape, true);
|
|
369
|
+
if (shapeKind < Physics2DAbiShapeKind.Circle || shapeKind > Physics2DAbiShapeKind.Point) {
|
|
370
|
+
return 'UnsupportedShape';
|
|
371
|
+
}
|
|
372
|
+
const shape = readShape(command, command.payload + Physics2DAbiSetColliderPayloadOffset.Shape);
|
|
373
|
+
if (shape === null)
|
|
374
|
+
return 'InvalidCommand';
|
|
375
|
+
if (!isShapeStateValid(shape))
|
|
376
|
+
return 'RejectedMutation';
|
|
377
|
+
const density = command.view.getFloat64(command.payload + 16, true);
|
|
378
|
+
const friction = command.view.getFloat64(command.payload + 24, true);
|
|
379
|
+
const restitution = command.view.getFloat64(command.payload + 32, true);
|
|
380
|
+
if (![density, friction, restitution].every((value) => Number.isFinite(value) && value >= 0)) {
|
|
381
|
+
return 'RejectedMutation';
|
|
382
|
+
}
|
|
383
|
+
const collider = createPhysics2DCollider(shape, { density, friction, restitution }, sensorFlag === 1, {
|
|
384
|
+
categoryBits: command.view.getUint32(command.payload + 4, true),
|
|
385
|
+
maskBits: command.view.getUint32(command.payload + 8, true),
|
|
386
|
+
groupIndex: command.view.getInt32(command.payload + 12, true),
|
|
387
|
+
});
|
|
388
|
+
// Replacing an id detaches the previous collider first, so an id names one live collider at a time
|
|
389
|
+
// and a caller may resend a whole body's colliders without accumulating them.
|
|
390
|
+
const previous = state.colliderById.get(command.objectId);
|
|
391
|
+
if (previous !== undefined) {
|
|
392
|
+
const previousBody = state.bodyById.get(previous.bodyId);
|
|
393
|
+
if (previousBody !== undefined)
|
|
394
|
+
removePhysics2DCollider(state.world, previousBody, previous.collider);
|
|
395
|
+
state.idByCollider.delete(previous.collider);
|
|
396
|
+
}
|
|
397
|
+
addPhysics2DCollider(state.world, body, collider);
|
|
398
|
+
state.colliderById.set(command.objectId, { bodyId: command.relatedId, collider });
|
|
399
|
+
state.idByCollider.set(collider, command.objectId);
|
|
400
|
+
return 'Complete';
|
|
401
|
+
}
|
|
402
|
+
function executeDestroyCollider(state, command) {
|
|
403
|
+
if (command.byteLength !== Physics2DAbiCommandByteLength.DestroyCollider ||
|
|
404
|
+
!isObjectId(command.objectId) ||
|
|
405
|
+
command.relatedId !== 0) {
|
|
406
|
+
return 'InvalidCommand';
|
|
407
|
+
}
|
|
408
|
+
const held = state.colliderById.get(command.objectId);
|
|
409
|
+
if (held === undefined)
|
|
410
|
+
return 'MissingCollider';
|
|
411
|
+
const body = state.bodyById.get(held.bodyId);
|
|
412
|
+
if (body !== undefined)
|
|
413
|
+
removePhysics2DCollider(state.world, body, held.collider);
|
|
414
|
+
state.colliderById.delete(command.objectId);
|
|
415
|
+
state.idByCollider.delete(held.collider);
|
|
416
|
+
return 'Complete';
|
|
417
|
+
}
|
|
418
|
+
function executeSetJoint(state, command) {
|
|
419
|
+
if (command.byteLength !== Physics2DAbiCommandByteLength.SetJoint ||
|
|
420
|
+
!isObjectId(command.objectId) ||
|
|
421
|
+
command.relatedId !== 0) {
|
|
422
|
+
return 'InvalidCommand';
|
|
423
|
+
}
|
|
424
|
+
const kind = command.view.getUint32(command.payload + Physics2DAbiSetJointPayloadOffset.Kind, true);
|
|
425
|
+
const bodyAId = command.view.getUint32(command.payload + Physics2DAbiSetJointPayloadOffset.BodyA, true);
|
|
426
|
+
const bodyBId = command.view.getUint32(command.payload + Physics2DAbiSetJointPayloadOffset.BodyB, true);
|
|
427
|
+
if (kind < Physics2DAbiJointKind.Distance || kind > Physics2DAbiJointKind.Gear)
|
|
428
|
+
return 'UnsupportedJoint';
|
|
429
|
+
if (!isObjectId(bodyAId) || !isObjectId(bodyBId))
|
|
430
|
+
return 'InvalidCommand';
|
|
431
|
+
const flags = command.view.getUint32(command.payload + Physics2DAbiSetJointPayloadOffset.Flags, true);
|
|
432
|
+
if ((flags & getJointFlagMask(kind)) !== flags)
|
|
433
|
+
return 'InvalidCommand';
|
|
434
|
+
const bodyA = state.bodyById.get(bodyAId);
|
|
435
|
+
const bodyB = state.bodyById.get(bodyBId);
|
|
436
|
+
if (bodyA === undefined || bodyB === undefined)
|
|
437
|
+
return 'MissingBody';
|
|
438
|
+
const common = readFloat64Values(command, Physics2DAbiSetJointPayloadOffset.CommonValues, state.jointCommonValues);
|
|
439
|
+
const values = readFloat64Values(command, Physics2DAbiSetJointPayloadOffset.KindValues, state.jointKindValues);
|
|
440
|
+
if (!isJointValueBlockValid(common, values))
|
|
441
|
+
return 'RejectedMutation';
|
|
442
|
+
const joint = createJoint(kind, bodyA.index, bodyB.index, flags, common, values);
|
|
443
|
+
if (joint === null)
|
|
444
|
+
return 'UnsupportedJoint';
|
|
445
|
+
const previous = state.jointById.get(command.objectId);
|
|
446
|
+
if (previous !== undefined) {
|
|
447
|
+
removePhysics2DJoint(state.world, previous);
|
|
448
|
+
state.idByJoint.delete(previous);
|
|
449
|
+
removeSorted(state.jointIds, command.objectId);
|
|
450
|
+
}
|
|
451
|
+
addPhysics2DJoint(state.world, joint);
|
|
452
|
+
state.jointById.set(command.objectId, joint);
|
|
453
|
+
state.idByJoint.set(joint, command.objectId);
|
|
454
|
+
insertSorted(state.jointIds, command.objectId);
|
|
455
|
+
state.brokenJoints = state.brokenJoints.filter((broken) => broken.id !== command.objectId);
|
|
456
|
+
return 'Complete';
|
|
457
|
+
}
|
|
458
|
+
function executeDestroyJoint(state, command) {
|
|
459
|
+
if (command.byteLength !== Physics2DAbiCommandByteLength.DestroyJoint ||
|
|
460
|
+
!isObjectId(command.objectId) ||
|
|
461
|
+
command.relatedId !== 0) {
|
|
462
|
+
return 'InvalidCommand';
|
|
463
|
+
}
|
|
464
|
+
const joint = state.jointById.get(command.objectId);
|
|
465
|
+
if (joint === undefined)
|
|
466
|
+
return 'MissingJoint';
|
|
467
|
+
removePhysics2DJoint(state.world, joint);
|
|
468
|
+
state.jointById.delete(command.objectId);
|
|
469
|
+
state.idByJoint.delete(joint);
|
|
470
|
+
removeSorted(state.jointIds, command.objectId);
|
|
471
|
+
return 'Complete';
|
|
472
|
+
}
|
|
473
|
+
function executeBodyAction(state, command, apply, scalarOnly = false) {
|
|
474
|
+
if (command.byteLength !== Physics2DAbiCommandByteLength.BodyAction ||
|
|
475
|
+
!isObjectId(command.objectId) ||
|
|
476
|
+
command.relatedId !== 0) {
|
|
477
|
+
return 'InvalidCommand';
|
|
478
|
+
}
|
|
479
|
+
const x = command.view.getFloat64(command.payload, true);
|
|
480
|
+
const y = command.view.getFloat64(command.payload + 8, true);
|
|
481
|
+
if (command.view.getFloat64(command.payload + 16, true) !== 0 ||
|
|
482
|
+
command.view.getFloat64(command.payload + 24, true) !== 0 ||
|
|
483
|
+
(scalarOnly && y !== 0)) {
|
|
484
|
+
return 'InvalidCommand';
|
|
485
|
+
}
|
|
486
|
+
const body = state.bodyById.get(command.objectId);
|
|
487
|
+
if (body === undefined)
|
|
488
|
+
return 'MissingBody';
|
|
489
|
+
return apply(body, x, y) ? 'Complete' : 'RejectedMutation';
|
|
490
|
+
}
|
|
491
|
+
function executeBodyPointAction(state, command, apply) {
|
|
492
|
+
if (command.byteLength !== Physics2DAbiCommandByteLength.BodyAction ||
|
|
493
|
+
!isObjectId(command.objectId) ||
|
|
494
|
+
command.relatedId !== 0) {
|
|
495
|
+
return 'InvalidCommand';
|
|
496
|
+
}
|
|
497
|
+
const body = state.bodyById.get(command.objectId);
|
|
498
|
+
if (body === undefined)
|
|
499
|
+
return 'MissingBody';
|
|
500
|
+
const applied = apply(body, command.view.getFloat64(command.payload, true), command.view.getFloat64(command.payload + 8, true), command.view.getFloat64(command.payload + 16, true), command.view.getFloat64(command.payload + 24, true));
|
|
501
|
+
return applied ? 'Complete' : 'RejectedMutation';
|
|
502
|
+
}
|
|
503
|
+
function executeWakeBody(state, command) {
|
|
504
|
+
if (command.byteLength !== Physics2DAbiCommandByteLength.WakeBody ||
|
|
505
|
+
!isObjectId(command.objectId) ||
|
|
506
|
+
command.relatedId !== 0) {
|
|
507
|
+
return 'InvalidCommand';
|
|
508
|
+
}
|
|
509
|
+
const body = state.bodyById.get(command.objectId);
|
|
510
|
+
if (body === undefined)
|
|
511
|
+
return 'MissingBody';
|
|
512
|
+
wakePhysics2DBody(body);
|
|
513
|
+
return 'Complete';
|
|
514
|
+
}
|
|
515
|
+
function createJoint(kind, bodyA, bodyB, flags, common, values) {
|
|
516
|
+
const base = {
|
|
517
|
+
bodyA,
|
|
518
|
+
bodyB,
|
|
519
|
+
localAnchorAX: common[0],
|
|
520
|
+
localAnchorAY: common[1],
|
|
521
|
+
localAnchorBX: common[2],
|
|
522
|
+
localAnchorBY: common[3],
|
|
523
|
+
collideConnected: (flags & Physics2DAbiJointFlag.CollideConnected) !== 0,
|
|
524
|
+
breakForce: common[4],
|
|
525
|
+
breakTorque: common[5],
|
|
526
|
+
};
|
|
527
|
+
if (kind === Physics2DAbiJointKind.Distance) {
|
|
528
|
+
return createPhysics2DDistanceJoint({
|
|
529
|
+
...base,
|
|
530
|
+
length: values[0],
|
|
531
|
+
frequencyHz: values[1],
|
|
532
|
+
dampingRatio: values[2],
|
|
533
|
+
});
|
|
534
|
+
}
|
|
535
|
+
if (kind === Physics2DAbiJointKind.Revolute) {
|
|
536
|
+
return createPhysics2DRevoluteJoint({
|
|
537
|
+
...base,
|
|
538
|
+
lowerAngle: values[0],
|
|
539
|
+
upperAngle: values[1],
|
|
540
|
+
referenceAngle: values[2],
|
|
541
|
+
motorSpeed: values[3],
|
|
542
|
+
maxMotorTorque: values[4],
|
|
543
|
+
limitFrequencyHz: values[5],
|
|
544
|
+
limitDampingRatio: values[6],
|
|
545
|
+
enableMotor: (flags & Physics2DAbiJointFlag.EnableMotor) !== 0,
|
|
546
|
+
enableLimit: (flags & Physics2DAbiJointFlag.EnableLimit) !== 0,
|
|
547
|
+
enableLimitSpring: (flags & Physics2DAbiJointFlag.EnableLimitSpring) !== 0,
|
|
548
|
+
});
|
|
549
|
+
}
|
|
550
|
+
if (kind === Physics2DAbiJointKind.Prismatic) {
|
|
551
|
+
return createPhysics2DPrismaticJoint({
|
|
552
|
+
...base,
|
|
553
|
+
localAxisAX: values[0],
|
|
554
|
+
localAxisAY: values[1],
|
|
555
|
+
referenceAngle: values[2],
|
|
556
|
+
lowerTranslation: values[3],
|
|
557
|
+
upperTranslation: values[4],
|
|
558
|
+
motorSpeed: values[5],
|
|
559
|
+
maxMotorForce: values[6],
|
|
560
|
+
limitFrequencyHz: values[7],
|
|
561
|
+
limitDampingRatio: values[8],
|
|
562
|
+
enableMotor: (flags & Physics2DAbiJointFlag.EnableMotor) !== 0,
|
|
563
|
+
enableLimit: (flags & Physics2DAbiJointFlag.EnableLimit) !== 0,
|
|
564
|
+
enableLimitSpring: (flags & Physics2DAbiJointFlag.EnableLimitSpring) !== 0,
|
|
565
|
+
});
|
|
566
|
+
}
|
|
567
|
+
if (kind === Physics2DAbiJointKind.Weld) {
|
|
568
|
+
return createPhysics2DWeldJoint({ ...base, referenceAngle: values[0] });
|
|
569
|
+
}
|
|
570
|
+
if (kind === Physics2DAbiJointKind.Wheel) {
|
|
571
|
+
return createPhysics2DWheelJoint({
|
|
572
|
+
...base,
|
|
573
|
+
localAxisAX: values[0],
|
|
574
|
+
localAxisAY: values[1],
|
|
575
|
+
restTranslation: values[2],
|
|
576
|
+
frequencyHz: values[3],
|
|
577
|
+
dampingRatio: values[4],
|
|
578
|
+
motorSpeed: values[5],
|
|
579
|
+
maxMotorTorque: values[6],
|
|
580
|
+
enableMotor: (flags & Physics2DAbiJointFlag.EnableMotor) !== 0,
|
|
581
|
+
});
|
|
582
|
+
}
|
|
583
|
+
if (kind === Physics2DAbiJointKind.Rope) {
|
|
584
|
+
return createPhysics2DRopeJoint({ ...base, maxLength: values[0] });
|
|
585
|
+
}
|
|
586
|
+
if (kind === Physics2DAbiJointKind.Mouse) {
|
|
587
|
+
// The odd one out: a mouse joint drags ONE body toward a world point, so the factory sets both
|
|
588
|
+
// endpoints to the same body and reads the anchor from the B slot. The record therefore has to
|
|
589
|
+
// name that body twice, and a caller that names two different ones is describing a joint Physics2D
|
|
590
|
+
// has no way to build rather than one it merely dislikes.
|
|
591
|
+
if (bodyA !== bodyB)
|
|
592
|
+
return null;
|
|
593
|
+
return createPhysics2DMouseJoint({
|
|
594
|
+
body: bodyA,
|
|
595
|
+
localAnchorX: common[2],
|
|
596
|
+
localAnchorY: common[3],
|
|
597
|
+
breakForce: common[4],
|
|
598
|
+
breakTorque: common[5],
|
|
599
|
+
targetX: values[0],
|
|
600
|
+
targetY: values[1],
|
|
601
|
+
maxForce: values[2],
|
|
602
|
+
frequencyHz: values[3],
|
|
603
|
+
dampingRatio: values[4],
|
|
604
|
+
});
|
|
605
|
+
}
|
|
606
|
+
if (kind === Physics2DAbiJointKind.Pulley) {
|
|
607
|
+
return createPhysics2DPulleyJoint({
|
|
608
|
+
...base,
|
|
609
|
+
groundAnchorAX: values[0],
|
|
610
|
+
groundAnchorAY: values[1],
|
|
611
|
+
groundAnchorBX: values[2],
|
|
612
|
+
groundAnchorBY: values[3],
|
|
613
|
+
ratio: values[4],
|
|
614
|
+
constant: values[5],
|
|
615
|
+
});
|
|
616
|
+
}
|
|
617
|
+
if (kind === Physics2DAbiJointKind.Gear) {
|
|
618
|
+
return createPhysics2DGearJoint({
|
|
619
|
+
...base,
|
|
620
|
+
axisAX: values[0],
|
|
621
|
+
axisAY: values[1],
|
|
622
|
+
axisBX: values[2],
|
|
623
|
+
axisBY: values[3],
|
|
624
|
+
ratio: values[4],
|
|
625
|
+
constant: values[5],
|
|
626
|
+
coordinateA: (flags & Physics2DAbiJointFlag.LinearCoordinateA) !== 0 ? 'linear' : 'angular',
|
|
627
|
+
coordinateB: (flags & Physics2DAbiJointFlag.LinearCoordinateB) !== 0 ? 'linear' : 'angular',
|
|
628
|
+
});
|
|
629
|
+
}
|
|
630
|
+
return null;
|
|
631
|
+
}
|
|
632
|
+
function readShape(command, byteOffset) {
|
|
633
|
+
if (byteOffset + Physics2DAbiShapeHeaderByteLength > command.start + command.byteLength)
|
|
634
|
+
return null;
|
|
635
|
+
const kind = command.view.getUint32(byteOffset, true);
|
|
636
|
+
const scalarCount = command.view.getUint32(byteOffset + 4, true);
|
|
637
|
+
const integerCount = command.view.getUint32(byteOffset + 8, true);
|
|
638
|
+
const version = command.view.getUint32(byteOffset + 12, true);
|
|
639
|
+
// No 2D built-in carries integers or a payload version. Refusing a non-zero value rather than
|
|
640
|
+
// ignoring it is what lets a future kind that does carry them be introduced without an older reader
|
|
641
|
+
// silently accepting a record whose tail it never read.
|
|
642
|
+
if (integerCount !== 0 || version !== 0)
|
|
643
|
+
return null;
|
|
644
|
+
const scalarsAt = byteOffset + Physics2DAbiShapeHeaderByteLength;
|
|
645
|
+
if (scalarsAt + scalarCount * 8 !== command.start + command.byteLength)
|
|
646
|
+
return null;
|
|
647
|
+
const scalars = [];
|
|
648
|
+
for (let i = 0; i < scalarCount; i += 1)
|
|
649
|
+
scalars.push(command.view.getFloat64(scalarsAt + i * 8, true));
|
|
650
|
+
for (const scalar of scalars)
|
|
651
|
+
if (!Number.isFinite(scalar))
|
|
652
|
+
return null;
|
|
653
|
+
if (kind === Physics2DAbiShapeKind.Circle && scalarCount === 3) {
|
|
654
|
+
return { kind: 'circle', x: scalars[0], y: scalars[1], radius: scalars[2] };
|
|
655
|
+
}
|
|
656
|
+
if (kind === Physics2DAbiShapeKind.Aabb && scalarCount === 4) {
|
|
657
|
+
return { kind: 'aabb', minX: scalars[0], minY: scalars[1], maxX: scalars[2], maxY: scalars[3] };
|
|
658
|
+
}
|
|
659
|
+
if (kind === Physics2DAbiShapeKind.Obb && scalarCount === 5) {
|
|
660
|
+
return { kind: 'obb', x: scalars[0], y: scalars[1], halfW: scalars[2], halfH: scalars[3], rotation: scalars[4] };
|
|
661
|
+
}
|
|
662
|
+
if (kind === Physics2DAbiShapeKind.Capsule && scalarCount === 5) {
|
|
663
|
+
return { kind: 'capsule', x0: scalars[0], y0: scalars[1], x1: scalars[2], y1: scalars[3], radius: scalars[4] };
|
|
664
|
+
}
|
|
665
|
+
if (kind === Physics2DAbiShapeKind.Polygon && scalarCount % 2 === 0) {
|
|
666
|
+
return { kind: 'polygon', points: scalars };
|
|
667
|
+
}
|
|
668
|
+
if (kind === Physics2DAbiShapeKind.Segment && scalarCount === 4) {
|
|
669
|
+
return { kind: 'segment', x0: scalars[0], y0: scalars[1], x1: scalars[2], y1: scalars[3] };
|
|
670
|
+
}
|
|
671
|
+
if (kind === Physics2DAbiShapeKind.Point && scalarCount === 2) {
|
|
672
|
+
return { kind: 'point', x: scalars[0], y: scalars[1] };
|
|
673
|
+
}
|
|
674
|
+
return null;
|
|
675
|
+
}
|
|
676
|
+
function stepReferenceWorld(state, dt, hooks) {
|
|
677
|
+
if (hooks !== null && (hooks.preSolve !== null || hooks.postSolve !== null) && !hasHookCapacity(hooks.buffer)) {
|
|
678
|
+
return 'InsufficientHookBuffer';
|
|
679
|
+
}
|
|
680
|
+
if (!canStep(state.world, dt))
|
|
681
|
+
return 'Declined';
|
|
682
|
+
state.activeHooks = hooks;
|
|
683
|
+
state.world.contactHooks.preSolve = hooks?.preSolve != null ? state.preSolveHook : null;
|
|
684
|
+
state.world.contactHooks.postSolve = hooks?.postSolve != null ? state.postSolveHook : null;
|
|
685
|
+
state.stepping = true;
|
|
686
|
+
try {
|
|
687
|
+
stepPhysics2D(state.world, dt);
|
|
688
|
+
}
|
|
689
|
+
finally {
|
|
690
|
+
state.stepping = false;
|
|
691
|
+
state.activeHooks = null;
|
|
692
|
+
state.world.contactHooks.preSolve = null;
|
|
693
|
+
state.world.contactHooks.postSolve = null;
|
|
694
|
+
}
|
|
695
|
+
state.lastStepDt = dt;
|
|
696
|
+
collectBrokenJoints(state);
|
|
697
|
+
return 'Complete';
|
|
698
|
+
}
|
|
699
|
+
// Physics2D removes a broken joint from the world, so its ABI id has to be retired here or a later
|
|
700
|
+
// SetJoint reusing that id would find a stale entry. The load that broke it is retained for one
|
|
701
|
+
// readback, which is the whole of what a caller can learn about a joint that no longer exists.
|
|
702
|
+
function collectBrokenJoints(state) {
|
|
703
|
+
state.brokenJoints = [];
|
|
704
|
+
for (const broken of state.world.jointEvents.broke) {
|
|
705
|
+
const id = state.idByJoint.get(broken.joint);
|
|
706
|
+
if (id === undefined)
|
|
707
|
+
continue;
|
|
708
|
+
state.brokenJoints.push({ id, forceX: broken.forceX, forceY: broken.forceY, torque: broken.torque });
|
|
709
|
+
state.jointById.delete(id);
|
|
710
|
+
state.idByJoint.delete(broken.joint);
|
|
711
|
+
removeSorted(state.jointIds, id);
|
|
712
|
+
}
|
|
713
|
+
state.brokenJoints.sort((a, b) => a.id - b.id);
|
|
714
|
+
}
|
|
715
|
+
function canStep(world, dt) {
|
|
716
|
+
return (isPhysics2DTimestepValid(dt) &&
|
|
717
|
+
isPhysics2DPreviousTimestepValid(world) &&
|
|
718
|
+
isPhysics2DGravityValid(world) &&
|
|
719
|
+
isPhysics2DSolverConfigValid(world.config) &&
|
|
720
|
+
isPhysics2DBodyStateValid(world) &&
|
|
721
|
+
isPhysics2DContactStateValid(world) &&
|
|
722
|
+
isPhysics2DJointStateValid(world));
|
|
723
|
+
}
|
|
724
|
+
function hasHookCapacity(buffer) {
|
|
725
|
+
return (getContactCapacity(buffer) >= 1 &&
|
|
726
|
+
buffer.pointFeatureIds.length >= Physics2DAbiMaxContactPoints &&
|
|
727
|
+
buffer.pointValues.length >= Physics2DAbiMaxContactPoints * Physics2DAbiContactPointValueStride);
|
|
728
|
+
}
|
|
729
|
+
function invokeContactHook(state, contact, buffer, hook) {
|
|
730
|
+
clearContactBuffer(buffer);
|
|
731
|
+
writeContact(state, contact, buffer, 0, 0);
|
|
732
|
+
buffer.count = 1;
|
|
733
|
+
buffer.pointCount = Math.min(contact.pointCount, Physics2DAbiMaxContactPoints);
|
|
734
|
+
buffer.requiredCount = 1;
|
|
735
|
+
buffer.requiredPointCount = contact.pointCount;
|
|
736
|
+
hook(buffer);
|
|
737
|
+
const enabled = (buffer.flags[0] & Physics2DAbiContactFlag.Enabled) !== 0;
|
|
738
|
+
// Row zero: a hook is handed exactly one contact, so the stride never multiplies anything here.
|
|
739
|
+
const friction = buffer.values[Physics2DAbiContactValue.Friction];
|
|
740
|
+
const restitution = buffer.values[Physics2DAbiContactValue.Restitution];
|
|
741
|
+
if (!Number.isFinite(friction) || !Number.isFinite(restitution)) {
|
|
742
|
+
throw new RangeError('Physics2D ABI contact hook wrote a non-finite friction or restitution');
|
|
743
|
+
}
|
|
744
|
+
contact.enabled = enabled;
|
|
745
|
+
contact.friction = friction;
|
|
746
|
+
contact.restitution = restitution;
|
|
747
|
+
}
|
|
748
|
+
function readBodies(state, bodyIds, out) {
|
|
749
|
+
const capacity = Math.min(out.ids.length, out.flags.length, Math.floor(out.values.length / Physics2DAbiBodyValueStride));
|
|
750
|
+
let written = 0;
|
|
751
|
+
let required = 0;
|
|
752
|
+
if (bodyIds === null) {
|
|
753
|
+
for (const id of state.bodyIds) {
|
|
754
|
+
const body = state.bodyById.get(id);
|
|
755
|
+
if (body === undefined)
|
|
756
|
+
continue;
|
|
757
|
+
required += 1;
|
|
758
|
+
if (written < capacity)
|
|
759
|
+
writeBody(id, body, out, written++);
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
else {
|
|
763
|
+
for (let i = 0; i < bodyIds.length; i += 1) {
|
|
764
|
+
const body = state.bodyById.get(bodyIds[i]);
|
|
765
|
+
if (body === undefined)
|
|
766
|
+
continue;
|
|
767
|
+
required += 1;
|
|
768
|
+
if (written < capacity)
|
|
769
|
+
writeBody(bodyIds[i], body, out, written++);
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
out.count = written;
|
|
773
|
+
out.requiredCount = required;
|
|
774
|
+
}
|
|
775
|
+
function readContacts(state, selection, out) {
|
|
776
|
+
const source = selection === 'Began'
|
|
777
|
+
? state.world.events.began
|
|
778
|
+
: selection === 'Ended'
|
|
779
|
+
? state.world.events.ended
|
|
780
|
+
: state.world.contacts;
|
|
781
|
+
const capacity = getContactCapacity(out);
|
|
782
|
+
const pointCapacity = Math.min(out.pointFeatureIds.length, Math.floor(out.pointValues.length / Physics2DAbiContactPointValueStride));
|
|
783
|
+
clearContactBuffer(out);
|
|
784
|
+
let written = 0;
|
|
785
|
+
let pointsWritten = 0;
|
|
786
|
+
let requiredPoints = 0;
|
|
787
|
+
// Once one contact does not fit, publishing stops. Skipping it and continuing would write a
|
|
788
|
+
// SUBSEQUENCE while `count` claims a prefix, so a caller that grew its buffer and re-read would get
|
|
789
|
+
// a different set rather than a superset — and a wide contact could hide behind a narrow one that
|
|
790
|
+
// happened to fit after it.
|
|
791
|
+
let prefixFits = true;
|
|
792
|
+
for (const contact of source) {
|
|
793
|
+
requiredPoints += contact.pointCount;
|
|
794
|
+
if (!prefixFits || written >= capacity || pointsWritten + contact.pointCount > pointCapacity) {
|
|
795
|
+
prefixFits = false;
|
|
796
|
+
continue;
|
|
797
|
+
}
|
|
798
|
+
writeContact(state, contact, out, written, pointsWritten);
|
|
799
|
+
pointsWritten += contact.pointCount;
|
|
800
|
+
written += 1;
|
|
801
|
+
}
|
|
802
|
+
out.count = written;
|
|
803
|
+
out.pointCount = pointsWritten;
|
|
804
|
+
out.requiredCount = source.length;
|
|
805
|
+
out.requiredPointCount = requiredPoints;
|
|
806
|
+
}
|
|
807
|
+
function readJoints(state, out) {
|
|
808
|
+
const capacity = Math.min(out.ids.length, out.flags.length, Math.floor(out.values.length / Physics2DAbiJointValueStride));
|
|
809
|
+
let written = 0;
|
|
810
|
+
for (const id of state.jointIds) {
|
|
811
|
+
const joint = state.jointById.get(id);
|
|
812
|
+
if (joint === undefined)
|
|
813
|
+
continue;
|
|
814
|
+
if (written < capacity) {
|
|
815
|
+
const at = written * Physics2DAbiJointValueStride;
|
|
816
|
+
const reported = writePhysics2DJointReaction(state.world, joint, state.lastStepDt, state.reaction);
|
|
817
|
+
out.ids[written] = id;
|
|
818
|
+
out.flags[written] = 0;
|
|
819
|
+
out.values[at] = reported ? state.reaction.forceX : 0;
|
|
820
|
+
out.values[at + 1] = reported ? state.reaction.forceY : 0;
|
|
821
|
+
out.values[at + 2] = reported ? state.reaction.torque : 0;
|
|
822
|
+
}
|
|
823
|
+
written += 1;
|
|
824
|
+
}
|
|
825
|
+
for (const broken of state.brokenJoints) {
|
|
826
|
+
if (written < capacity) {
|
|
827
|
+
const at = written * Physics2DAbiJointValueStride;
|
|
828
|
+
out.ids[written] = broken.id;
|
|
829
|
+
out.flags[written] = Physics2DAbiJointFlag.Broken;
|
|
830
|
+
out.values[at] = broken.forceX;
|
|
831
|
+
out.values[at + 1] = broken.forceY;
|
|
832
|
+
out.values[at + 2] = broken.torque;
|
|
833
|
+
}
|
|
834
|
+
written += 1;
|
|
835
|
+
}
|
|
836
|
+
out.requiredCount = written;
|
|
837
|
+
out.count = Math.min(written, capacity);
|
|
838
|
+
state.brokenJoints = [];
|
|
839
|
+
}
|
|
840
|
+
function writeBody(id, body, out, at) {
|
|
841
|
+
out.ids[at] = id;
|
|
842
|
+
out.flags[at] = encodeBodyFlags(body);
|
|
843
|
+
const base = at * Physics2DAbiBodyValueStride;
|
|
844
|
+
out.values[base] = body.x;
|
|
845
|
+
out.values[base + 1] = body.y;
|
|
846
|
+
out.values[base + 2] = body.angle;
|
|
847
|
+
out.values[base + 3] = body.velocityX;
|
|
848
|
+
out.values[base + 4] = body.velocityY;
|
|
849
|
+
out.values[base + 5] = body.angularVelocity;
|
|
850
|
+
out.values[base + 6] = body.forceX;
|
|
851
|
+
out.values[base + 7] = body.forceY;
|
|
852
|
+
out.values[base + 8] = body.torque;
|
|
853
|
+
out.values[base + 9] = body.mass;
|
|
854
|
+
out.values[base + 10] = body.inertia;
|
|
855
|
+
out.values[base + 11] = body.centerX;
|
|
856
|
+
out.values[base + 12] = body.centerY;
|
|
857
|
+
out.values[base + 13] = body.linearDamping;
|
|
858
|
+
out.values[base + 14] = body.angularDamping;
|
|
859
|
+
out.values[base + 15] = body.gravityScale;
|
|
860
|
+
out.values[base + 16] = body.sleepTimer;
|
|
861
|
+
}
|
|
862
|
+
function writeContact(state, contact, out, at, pointStart) {
|
|
863
|
+
const idBase = at * Physics2DAbiContactIdStride;
|
|
864
|
+
const bodyA = state.world.bodyByIndex.get(contact.bodyA);
|
|
865
|
+
const bodyB = state.world.bodyByIndex.get(contact.bodyB);
|
|
866
|
+
out.ids[idBase] = bodyA === undefined ? 0 : (state.idByBody.get(bodyA) ?? 0);
|
|
867
|
+
out.ids[idBase + 1] = bodyB === undefined ? 0 : (state.idByBody.get(bodyB) ?? 0);
|
|
868
|
+
out.ids[idBase + 2] = getColliderId(state, bodyA, contact.colliderA);
|
|
869
|
+
out.ids[idBase + 3] = getColliderId(state, bodyB, contact.colliderB);
|
|
870
|
+
let flags = 0;
|
|
871
|
+
if (contact.enabled)
|
|
872
|
+
flags |= Physics2DAbiContactFlag.Enabled;
|
|
873
|
+
if (contact.sensor)
|
|
874
|
+
flags |= Physics2DAbiContactFlag.Sensor;
|
|
875
|
+
if (contact.touching)
|
|
876
|
+
flags |= Physics2DAbiContactFlag.Touching;
|
|
877
|
+
out.flags[at] = flags;
|
|
878
|
+
const valueBase = at * Physics2DAbiContactValueStride;
|
|
879
|
+
out.values[valueBase] = contact.normalX;
|
|
880
|
+
out.values[valueBase + 1] = contact.normalY;
|
|
881
|
+
out.values[valueBase + 2] = contact.friction;
|
|
882
|
+
out.values[valueBase + 3] = contact.restitution;
|
|
883
|
+
out.pointStarts[at] = pointStart;
|
|
884
|
+
const pointCount = Math.min(contact.pointCount, Physics2DAbiMaxContactPoints);
|
|
885
|
+
out.pointCounts[at] = pointCount;
|
|
886
|
+
for (let i = 0; i < pointCount; i += 1) {
|
|
887
|
+
const point = contact.points[i];
|
|
888
|
+
const pointBase = (pointStart + i) * Physics2DAbiContactPointValueStride;
|
|
889
|
+
out.pointFeatureIds[pointStart + i] = point.featureId;
|
|
890
|
+
out.pointValues[pointBase] = point.x;
|
|
891
|
+
out.pointValues[pointBase + 1] = point.y;
|
|
892
|
+
out.pointValues[pointBase + 2] = point.depth;
|
|
893
|
+
out.pointValues[pointBase + 3] = point.rAX;
|
|
894
|
+
out.pointValues[pointBase + 4] = point.rAY;
|
|
895
|
+
out.pointValues[pointBase + 5] = point.rBX;
|
|
896
|
+
out.pointValues[pointBase + 6] = point.rBY;
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
function getColliderId(state, body, colliderIndex) {
|
|
900
|
+
if (body === undefined)
|
|
901
|
+
return 0;
|
|
902
|
+
const collider = body.colliders[colliderIndex];
|
|
903
|
+
return collider === undefined ? 0 : (state.idByCollider.get(collider) ?? 0);
|
|
904
|
+
}
|
|
905
|
+
function clearContactBuffer(out) {
|
|
906
|
+
out.count = 0;
|
|
907
|
+
out.pointCount = 0;
|
|
908
|
+
out.requiredCount = 0;
|
|
909
|
+
out.requiredPointCount = 0;
|
|
910
|
+
}
|
|
911
|
+
function getContactCapacity(out) {
|
|
912
|
+
return Math.min(Math.floor(out.ids.length / Physics2DAbiContactIdStride), out.flags.length, out.pointStarts.length, out.pointCounts.length, Math.floor(out.values.length / Physics2DAbiContactValueStride));
|
|
913
|
+
}
|
|
914
|
+
function writeQueryHits(state, source, out) {
|
|
915
|
+
const capacity = getQueryCapacity(out);
|
|
916
|
+
const written = Math.min(source.hitCount, capacity);
|
|
917
|
+
for (let i = 0; i < written; i += 1) {
|
|
918
|
+
writeQueryIdentity(state, source.hits[i], out, i);
|
|
919
|
+
clearQueryValues(out, i);
|
|
920
|
+
}
|
|
921
|
+
out.count = written;
|
|
922
|
+
out.requiredCount = source.hitCount;
|
|
923
|
+
}
|
|
924
|
+
function writeRayHits(state, out) {
|
|
925
|
+
const hits = state.ray.hits;
|
|
926
|
+
const capacity = getQueryCapacity(out);
|
|
927
|
+
const written = Math.min(state.ray.hitCount, capacity);
|
|
928
|
+
for (let i = 0; i < written; i += 1) {
|
|
929
|
+
writeQueryIdentity(state, hits[i], out, i);
|
|
930
|
+
writeQueryValues(out, i, hits[i].fraction, hits[i].x, hits[i].y, hits[i].normalX, hits[i].normalY);
|
|
931
|
+
}
|
|
932
|
+
out.count = written;
|
|
933
|
+
out.requiredCount = state.ray.hitCount;
|
|
934
|
+
}
|
|
935
|
+
function writeShapeCastHit(state, out) {
|
|
936
|
+
const result = state.shapeCast;
|
|
937
|
+
if (!result.hit || getQueryCapacity(out) < 1) {
|
|
938
|
+
out.count = 0;
|
|
939
|
+
out.requiredCount = result.hit ? 1 : 0;
|
|
940
|
+
return;
|
|
941
|
+
}
|
|
942
|
+
out.bodyIds[0] = result.body === null ? 0 : (state.idByBody.get(result.body) ?? 0);
|
|
943
|
+
out.colliderIds[0] = result.collider === null ? 0 : (state.idByCollider.get(result.collider) ?? 0);
|
|
944
|
+
writeQueryValues(out, 0, result.fraction, result.x, result.y, result.normalX, result.normalY);
|
|
945
|
+
out.count = 1;
|
|
946
|
+
out.requiredCount = 1;
|
|
947
|
+
}
|
|
948
|
+
function writeQueryIdentity(state, hit, out, at) {
|
|
949
|
+
out.bodyIds[at] = state.idByBody.get(hit.body) ?? 0;
|
|
950
|
+
out.colliderIds[at] = state.idByCollider.get(hit.collider) ?? 0;
|
|
951
|
+
}
|
|
952
|
+
function clearQueryValues(out, at) {
|
|
953
|
+
const base = at * Physics2DAbiQueryValueStride;
|
|
954
|
+
for (let i = 0; i < Physics2DAbiQueryValueStride; i += 1)
|
|
955
|
+
out.values[base + i] = 0;
|
|
956
|
+
}
|
|
957
|
+
function writeQueryValues(out, at, fraction, x, y, normalX, normalY) {
|
|
958
|
+
const base = at * Physics2DAbiQueryValueStride;
|
|
959
|
+
out.values[base] = fraction;
|
|
960
|
+
out.values[base + 1] = x;
|
|
961
|
+
out.values[base + 2] = y;
|
|
962
|
+
out.values[base + 3] = normalX;
|
|
963
|
+
out.values[base + 4] = normalY;
|
|
964
|
+
}
|
|
965
|
+
function getQueryCapacity(out) {
|
|
966
|
+
return Math.min(out.bodyIds.length, out.colliderIds.length, Math.floor(out.values.length / Physics2DAbiQueryValueStride));
|
|
967
|
+
}
|
|
968
|
+
function isCommandBufferValid(commands) {
|
|
969
|
+
if (!Number.isSafeInteger(commands.byteLength) ||
|
|
970
|
+
commands.byteLength < Physics2DAbiCommandHeaderByteLength ||
|
|
971
|
+
commands.byteLength > commands.data.byteLength ||
|
|
972
|
+
!Number.isSafeInteger(commands.commandCount) ||
|
|
973
|
+
commands.commandCount < 0) {
|
|
974
|
+
return false;
|
|
975
|
+
}
|
|
976
|
+
const view = new DataView(commands.data.buffer, commands.data.byteOffset, commands.data.byteLength);
|
|
977
|
+
return (view.getUint32(0, true) === Physics2DAbiCommandMagic &&
|
|
978
|
+
view.getUint32(4, true) === Physics2DAbiVersion &&
|
|
979
|
+
view.getUint32(8, true) === commands.byteLength &&
|
|
980
|
+
view.getUint32(12, true) === commands.commandCount);
|
|
981
|
+
}
|
|
982
|
+
function failExecution(out, status, commandIndex, byteOffset, commandKind) {
|
|
983
|
+
out.status = status;
|
|
984
|
+
out.commandIndex = commandIndex;
|
|
985
|
+
out.byteOffset = byteOffset;
|
|
986
|
+
out.commandKind = commandKind;
|
|
987
|
+
return false;
|
|
988
|
+
}
|
|
989
|
+
function readFloat64Values(command, payloadByteOffset, values) {
|
|
990
|
+
const at = command.payload + payloadByteOffset;
|
|
991
|
+
for (let i = 0; i < values.length; i += 1)
|
|
992
|
+
values[i] = command.view.getFloat64(at + i * 8, true);
|
|
993
|
+
return values;
|
|
994
|
+
}
|
|
995
|
+
function decodeBodyType(type) {
|
|
996
|
+
if (type === Physics2DAbiBodyType.Dynamic)
|
|
997
|
+
return 'dynamic';
|
|
998
|
+
if (type === Physics2DAbiBodyType.Kinematic)
|
|
999
|
+
return 'kinematic';
|
|
1000
|
+
if (type === Physics2DAbiBodyType.Static)
|
|
1001
|
+
return 'static';
|
|
1002
|
+
return null;
|
|
1003
|
+
}
|
|
1004
|
+
function encodeBodyFlags(body) {
|
|
1005
|
+
let flags = body.type === 'dynamic'
|
|
1006
|
+
? Physics2DAbiBodyType.Dynamic
|
|
1007
|
+
: body.type === 'kinematic'
|
|
1008
|
+
? Physics2DAbiBodyType.Kinematic
|
|
1009
|
+
: Physics2DAbiBodyType.Static;
|
|
1010
|
+
if (body.fixedRotation)
|
|
1011
|
+
flags |= Physics2DAbiBodyFlag.FixedRotation;
|
|
1012
|
+
if (body.bullet)
|
|
1013
|
+
flags |= Physics2DAbiBodyFlag.Bullet;
|
|
1014
|
+
if (body.sleeping)
|
|
1015
|
+
flags |= Physics2DAbiBodyFlag.Sleeping;
|
|
1016
|
+
if (body.sleepEnabled)
|
|
1017
|
+
flags |= Physics2DAbiBodyFlag.SleepEnabled;
|
|
1018
|
+
return flags;
|
|
1019
|
+
}
|
|
1020
|
+
function areFinite(values) {
|
|
1021
|
+
for (const value of values)
|
|
1022
|
+
if (!Number.isFinite(value))
|
|
1023
|
+
return false;
|
|
1024
|
+
return true;
|
|
1025
|
+
}
|
|
1026
|
+
function isBodyValueBlockValid(values) {
|
|
1027
|
+
return areFinite(values) && values[13] >= 0 && values[14] >= 0 && values[16] >= 0;
|
|
1028
|
+
}
|
|
1029
|
+
// Match the Physics2D step validator rather than the manifold validator: point and segment colliders
|
|
1030
|
+
// are legal authored state even though they deliberately have no contact-manifold path.
|
|
1031
|
+
function isShapeStateValid(shape) {
|
|
1032
|
+
if (shape.kind === 'circle')
|
|
1033
|
+
return shape.radius > 0;
|
|
1034
|
+
if (shape.kind === 'aabb')
|
|
1035
|
+
return shape.maxX > shape.minX && shape.maxY > shape.minY;
|
|
1036
|
+
if (shape.kind === 'obb')
|
|
1037
|
+
return shape.halfW > 0 && shape.halfH > 0;
|
|
1038
|
+
if (shape.kind === 'capsule')
|
|
1039
|
+
return shape.radius > 0;
|
|
1040
|
+
if (shape.kind === 'polygon')
|
|
1041
|
+
return shape.points.length >= 6 && (shape.points.length & 1) === 0;
|
|
1042
|
+
return true;
|
|
1043
|
+
}
|
|
1044
|
+
function isJointValueBlockValid(common, values) {
|
|
1045
|
+
if (!areFinite(values) || !areFinite(common.slice(0, 4)))
|
|
1046
|
+
return false;
|
|
1047
|
+
return isBreakThresholdValid(common[4]) && isBreakThresholdValid(common[5]);
|
|
1048
|
+
}
|
|
1049
|
+
function isBreakThresholdValid(value) {
|
|
1050
|
+
return !Number.isNaN(value) && value >= 0;
|
|
1051
|
+
}
|
|
1052
|
+
function isObjectId(value) {
|
|
1053
|
+
return Number.isInteger(value) && value > 0 && value <= 0xffffffff;
|
|
1054
|
+
}
|
|
1055
|
+
// Which flag bits a joint record may set, by kind. Anything outside the mask is InvalidCommand rather
|
|
1056
|
+
// than ignored, so a caller that sets a motor bit on a weld learns it at the boundary instead of
|
|
1057
|
+
// wondering why its motor never ran.
|
|
1058
|
+
function getJointFlagMask(kind) {
|
|
1059
|
+
const common = Physics2DAbiJointFlag.CollideConnected;
|
|
1060
|
+
if (kind === Physics2DAbiJointKind.Revolute || kind === Physics2DAbiJointKind.Prismatic) {
|
|
1061
|
+
return (common |
|
|
1062
|
+
Physics2DAbiJointFlag.EnableMotor |
|
|
1063
|
+
Physics2DAbiJointFlag.EnableLimit |
|
|
1064
|
+
Physics2DAbiJointFlag.EnableLimitSpring);
|
|
1065
|
+
}
|
|
1066
|
+
if (kind === Physics2DAbiJointKind.Wheel)
|
|
1067
|
+
return common | Physics2DAbiJointFlag.EnableMotor;
|
|
1068
|
+
if (kind === Physics2DAbiJointKind.Gear) {
|
|
1069
|
+
return common | Physics2DAbiJointFlag.LinearCoordinateA | Physics2DAbiJointFlag.LinearCoordinateB;
|
|
1070
|
+
}
|
|
1071
|
+
return common;
|
|
1072
|
+
}
|
|
1073
|
+
function insertSorted(values, value) {
|
|
1074
|
+
let at = values.length;
|
|
1075
|
+
while (at > 0 && values[at - 1] > value)
|
|
1076
|
+
at -= 1;
|
|
1077
|
+
values.splice(at, 0, value);
|
|
1078
|
+
}
|
|
1079
|
+
function removeSorted(values, value) {
|
|
1080
|
+
const at = values.indexOf(value);
|
|
1081
|
+
if (at >= 0)
|
|
1082
|
+
values.splice(at, 1);
|
|
1083
|
+
}
|
|
1084
|
+
const PHYSICS2D_ABI_BODY_FLAG_MASK = Physics2DAbiBodyFlag.TypeMask |
|
|
1085
|
+
Physics2DAbiBodyFlag.FixedRotation |
|
|
1086
|
+
Physics2DAbiBodyFlag.Bullet |
|
|
1087
|
+
Physics2DAbiBodyFlag.Sleeping |
|
|
1088
|
+
Physics2DAbiBodyFlag.SleepEnabled;
|
|
1089
|
+
//# sourceMappingURL=referencePhysics2DAbi.js.map
|