@flighthq/physics3d 0.5.1-next.903.f434bc2 → 0.5.1-next.905.5fbf787

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 CHANGED
@@ -13,5 +13,5 @@ Import the supported application-facing API from `@flighthq/physics3d`. The `@fl
13
13
  This package is part of the locked-version Flight SDK graph. Applications may instead install and import `@flighthq/sdk` when package-level tree shaking is sufficient.
14
14
 
15
15
  - [Flight project](https://github.com/flighthq/flight)
16
- - [Source for @flighthq/physics3d@0.5.1-next.903.f434bc2](https://github.com/flighthq/flight/tree/f434bc233778d75aeea61995df3a8cfaec459a85/packages/physics3d)
17
- - [License](https://github.com/flighthq/flight/blob/f434bc233778d75aeea61995df3a8cfaec459a85/LICENSE.md)
16
+ - [Source for @flighthq/physics3d@0.5.1-next.905.5fbf787](https://github.com/flighthq/flight/tree/5fbf7874064c384307542d68b4dcb6a895ff0dcf/packages/physics3d)
17
+ - [License](https://github.com/flighthq/flight/blob/5fbf7874064c384307542d68b4dcb6a895ff0dcf/LICENSE.md)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flighthq/physics3d",
3
- "version": "0.5.1-next.903.f434bc2",
3
+ "version": "0.5.1-next.905.5fbf787",
4
4
  "author": "Joshua Granick and other contributors",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -38,13 +38,13 @@
38
38
  "clean:dist": "tsx ../../scripts/clean-package-dist.ts"
39
39
  },
40
40
  "dependencies": {
41
- "@flighthq/collision": "0.5.1-next.903.f434bc2",
42
- "@flighthq/entity": "0.5.1-next.903.f434bc2",
43
- "@flighthq/log": "0.5.1-next.903.f434bc2",
44
- "@flighthq/math": "0.5.1-next.903.f434bc2",
45
- "@flighthq/node": "0.5.1-next.903.f434bc2",
46
- "@flighthq/spatial": "0.5.1-next.903.f434bc2",
47
- "@flighthq/types": "0.5.1-next.903.f434bc2"
41
+ "@flighthq/collision": "0.5.1-next.905.5fbf787",
42
+ "@flighthq/entity": "0.5.1-next.905.5fbf787",
43
+ "@flighthq/log": "0.5.1-next.905.5fbf787",
44
+ "@flighthq/math": "0.5.1-next.905.5fbf787",
45
+ "@flighthq/node": "0.5.1-next.905.5fbf787",
46
+ "@flighthq/spatial": "0.5.1-next.905.5fbf787",
47
+ "@flighthq/types": "0.5.1-next.905.5fbf787"
48
48
  },
49
49
  "devDependencies": {
50
50
  "@flighthq/geometry": "*",
@@ -4,6 +4,12 @@ import { describe, expect, it } from 'vitest';
4
4
 
5
5
  import {
6
6
  createPhysics3DColliderWorldShape,
7
+ initializeCollisionBox3D,
8
+ initializeCollisionCapsule3D,
9
+ initializeCollisionCone3D,
10
+ initializeCollisionConvex3D,
11
+ initializeCollisionCylinder3D,
12
+ initializeCollisionSphere3D,
7
13
  updatePhysics3DColliderWorldShape,
8
14
  writePhysics3DColliderBounds,
9
15
  } from './colliderTransform';
@@ -64,6 +70,41 @@ describe('createPhysics3DColliderWorldShape', () => {
64
70
  });
65
71
  });
66
72
 
73
+ describe('initializeCollisionBox3D', () => {
74
+ it('is the construction initializer of createCollisionBox3D', () => {
75
+ expect(typeof initializeCollisionBox3D).toBe('function');
76
+ });
77
+ });
78
+
79
+ describe('initializeCollisionCapsule3D', () => {
80
+ it('is the construction initializer of createCollisionCapsule3D', () => {
81
+ expect(typeof initializeCollisionCapsule3D).toBe('function');
82
+ });
83
+ });
84
+ describe('initializeCollisionCone3D', () => {
85
+ it('is the construction initializer of createCollisionCone3D', () => {
86
+ expect(typeof initializeCollisionCone3D).toBe('function');
87
+ });
88
+ });
89
+
90
+ describe('initializeCollisionConvex3D', () => {
91
+ it('is the construction initializer of createCollisionConvex3D', () => {
92
+ expect(typeof initializeCollisionConvex3D).toBe('function');
93
+ });
94
+ });
95
+
96
+ describe('initializeCollisionCylinder3D', () => {
97
+ it('is the construction initializer of createCollisionCylinder3D', () => {
98
+ expect(typeof initializeCollisionCylinder3D).toBe('function');
99
+ });
100
+ });
101
+
102
+ describe('initializeCollisionSphere3D', () => {
103
+ it('is the construction initializer of createCollisionSphere3D', () => {
104
+ expect(typeof initializeCollisionSphere3D).toBe('function');
105
+ });
106
+ });
107
+
67
108
  describe('updatePhysics3DColliderWorldShape', () => {
68
109
  it('translates a sphere by the body position', () => {
69
110
  const body = createRigidBody3D('dynamic');
@@ -1,6 +1,11 @@
1
1
  import { describe, expect, it } from 'vitest';
2
2
 
3
- import { createPhysics3DContact, createPhysics3DContactPoint } from './contacts';
3
+ import {
4
+ createPhysics3DContact,
5
+ createPhysics3DContactPoint,
6
+ initializePhysics3DContact,
7
+ initializePhysics3DContactPoint,
8
+ } from './contacts';
4
9
 
5
10
  describe('createPhysics3DContact', () => {
6
11
  it('orders the pair by index whichever way round it is given', () => {
@@ -62,3 +67,14 @@ describe('createPhysics3DContactPoint', () => {
62
67
  ]);
63
68
  });
64
69
  });
70
+ describe('initializePhysics3DContact', () => {
71
+ it('is the construction initializer of createPhysics3DContact', () => {
72
+ expect(typeof initializePhysics3DContact).toBe('function');
73
+ });
74
+ });
75
+
76
+ describe('initializePhysics3DContactPoint', () => {
77
+ it('is the construction initializer of createPhysics3DContactPoint', () => {
78
+ expect(typeof initializePhysics3DContactPoint).toBe('function');
79
+ });
80
+ });
@@ -8,7 +8,11 @@ import type { CollisionBuiltInShape3D, Physics3DWorld, RigidBody3D } from '@flig
8
8
  import { beforeEach, describe, expect, it } from 'vitest';
9
9
 
10
10
  import { buildPhysics3DContacts } from './contactIntake';
11
- import { createPhysics3DDebugGeometry, writePhysics3DDebugGeometry } from './debugGeometry';
11
+ import {
12
+ createPhysics3DDebugGeometry,
13
+ initializePhysics3DDebugGeometry,
14
+ writePhysics3DDebugGeometry,
15
+ } from './debugGeometry';
12
16
  import { createPhysics3DBallAndSocketJoint } from './jointFactories';
13
17
  import { addPhysics3DJoint } from './jointRegistry';
14
18
  import { registerBuiltInPhysics3DJointSolvers } from './registerBuiltInPhysics3DJointSolvers';
@@ -57,6 +61,11 @@ describe('createPhysics3DDebugGeometry', () => {
57
61
  });
58
62
  });
59
63
 
64
+ describe('initializePhysics3DDebugGeometry', () => {
65
+ it('is the construction initializer of createPhysics3DDebugGeometry', () => {
66
+ expect(typeof initializePhysics3DDebugGeometry).toBe('function');
67
+ });
68
+ });
60
69
  describe('writePhysics3DDebugGeometry', () => {
61
70
  it('draws explicit mesh triangles and heightfield grid edges', () => {
62
71
  const world = createPhysics3DWorld();
@@ -8,6 +8,13 @@ import {
8
8
  createPhysics3DGeneric6DofJoint,
9
9
  createPhysics3DHingeJoint,
10
10
  createPhysics3DSliderJoint,
11
+ initializePhysics3DBallAndSocketJoint,
12
+ initializePhysics3DConeTwistJoint,
13
+ initializePhysics3DDistanceJoint,
14
+ initializePhysics3DFixedJoint,
15
+ initializePhysics3DGeneric6DofJoint,
16
+ initializePhysics3DHingeJoint,
17
+ initializePhysics3DSliderJoint,
11
18
  } from './jointFactories';
12
19
  import {
13
20
  Physics3DBallAndSocketJointKind,
@@ -242,3 +249,44 @@ describe('createPhysics3DSliderJoint', () => {
242
249
  expect(joint.maxMotorForce).toBe(15);
243
250
  });
244
251
  });
252
+ describe('initializePhysics3DBallAndSocketJoint', () => {
253
+ it('is the construction initializer of createPhysics3DBallAndSocketJoint', () => {
254
+ expect(typeof initializePhysics3DBallAndSocketJoint).toBe('function');
255
+ });
256
+ });
257
+
258
+ describe('initializePhysics3DConeTwistJoint', () => {
259
+ it('is the construction initializer of createPhysics3DConeTwistJoint', () => {
260
+ expect(typeof initializePhysics3DConeTwistJoint).toBe('function');
261
+ });
262
+ });
263
+
264
+ describe('initializePhysics3DDistanceJoint', () => {
265
+ it('is the construction initializer of createPhysics3DDistanceJoint', () => {
266
+ expect(typeof initializePhysics3DDistanceJoint).toBe('function');
267
+ });
268
+ });
269
+
270
+ describe('initializePhysics3DFixedJoint', () => {
271
+ it('is the construction initializer of createPhysics3DFixedJoint', () => {
272
+ expect(typeof initializePhysics3DFixedJoint).toBe('function');
273
+ });
274
+ });
275
+
276
+ describe('initializePhysics3DGeneric6DofJoint', () => {
277
+ it('is the construction initializer of createPhysics3DGeneric6DofJoint', () => {
278
+ expect(typeof initializePhysics3DGeneric6DofJoint).toBe('function');
279
+ });
280
+ });
281
+
282
+ describe('initializePhysics3DHingeJoint', () => {
283
+ it('is the construction initializer of createPhysics3DHingeJoint', () => {
284
+ expect(typeof initializePhysics3DHingeJoint).toBe('function');
285
+ });
286
+ });
287
+
288
+ describe('initializePhysics3DSliderJoint', () => {
289
+ it('is the construction initializer of createPhysics3DSliderJoint', () => {
290
+ expect(typeof initializePhysics3DSliderJoint).toBe('function');
291
+ });
292
+ });
@@ -8,6 +8,7 @@ import {
8
8
  createPhysics3DJointReaction,
9
9
  getPhysics3DJointReactionForce,
10
10
  getPhysics3DJointReactionTorque,
11
+ initializePhysics3DJointReaction,
11
12
  writePhysics3DJointReaction,
12
13
  } from './jointReaction';
13
14
  import { addPhysics3DJoint } from './jointRegistry';
@@ -117,6 +118,42 @@ describe('getPhysics3DJointReactionTorque', () => {
117
118
  });
118
119
  });
119
120
 
121
+ describe('initializePhysics3DJointReaction', () => {
122
+ it('is the construction initializer of createPhysics3DJointReaction', () => {
123
+ expect(typeof initializePhysics3DJointReaction).toBe('function');
124
+ });
125
+ });
126
+
127
+ interface HangingScene {
128
+ world: Physics3DWorld;
129
+ joint: ReturnType<typeof createPhysics3DBallAndSocketJoint>;
130
+ }
131
+
132
+ // One body hanging from a static anchor by a ball-and-socket, which is the simplest arrangement whose
133
+ // reaction has a value known without reference to the solver: it is the hanging weight.
134
+ function createHangingScene(mass: number, gravity: number): HangingScene {
135
+ const world = createPhysics3DWorld();
136
+ world.gravityY = -gravity;
137
+ registerBuiltInPhysics3DJointSolvers(world);
138
+ const anchor = createRigidBody3D('static');
139
+ addPhysics3DBody(world, anchor);
140
+ const hanging = createUnitBody(mass);
141
+ addPhysics3DBody(world, hanging);
142
+ const joint = createPhysics3DBallAndSocketJoint({ bodyA: 0, bodyB: 1 });
143
+ addPhysics3DJoint(world, joint);
144
+ return { world, joint };
145
+ }
146
+
147
+ function createUnitBody(mass: number): RigidBody3D {
148
+ const body = createRigidBody3D('dynamic');
149
+ const data = createPhysics3DMassData();
150
+ data.mass = mass;
151
+ data.inertiaXX = mass;
152
+ data.inertiaYY = mass;
153
+ data.inertiaZZ = mass;
154
+ setRigidBody3DMassData(body, data);
155
+ return body;
156
+ }
120
157
  describe('writePhysics3DJointReaction', () => {
121
158
  it('MEASURES THE WEIGHT A JOINT IS HOLDING UP', () => {
122
159
  // The reading that makes the API worth having, checked against a number derived from outside the
@@ -208,34 +245,3 @@ describe('writePhysics3DJointReaction', () => {
208
245
  expect(Math.abs(out.forceY)).toBeCloseTo(20, 0);
209
246
  });
210
247
  });
211
-
212
- interface HangingScene {
213
- world: Physics3DWorld;
214
- joint: ReturnType<typeof createPhysics3DBallAndSocketJoint>;
215
- }
216
-
217
- // One body hanging from a static anchor by a ball-and-socket, which is the simplest arrangement whose
218
- // reaction has a value known without reference to the solver: it is the hanging weight.
219
- function createHangingScene(mass: number, gravity: number): HangingScene {
220
- const world = createPhysics3DWorld();
221
- world.gravityY = -gravity;
222
- registerBuiltInPhysics3DJointSolvers(world);
223
- const anchor = createRigidBody3D('static');
224
- addPhysics3DBody(world, anchor);
225
- const hanging = createUnitBody(mass);
226
- addPhysics3DBody(world, hanging);
227
- const joint = createPhysics3DBallAndSocketJoint({ bodyA: 0, bodyB: 1 });
228
- addPhysics3DJoint(world, joint);
229
- return { world, joint };
230
- }
231
-
232
- function createUnitBody(mass: number): RigidBody3D {
233
- const body = createRigidBody3D('dynamic');
234
- const data = createPhysics3DMassData();
235
- data.mass = mass;
236
- data.inertiaXX = mass;
237
- data.inertiaYY = mass;
238
- data.inertiaZZ = mass;
239
- setRigidBody3DMassData(body, data);
240
- return body;
241
- }
@@ -4,15 +4,16 @@ import { describe, expect, it } from 'vitest';
4
4
  import {
5
5
  combinePhysics3DMassData,
6
6
  computePhysics3DBoxMassData,
7
+ computePhysics3DCapsuleMassData,
7
8
  computePhysics3DColliderMassData,
8
9
  computePhysics3DConeMassData,
9
- computePhysics3DCylinderMassData,
10
10
  computePhysics3DConvexHullMassData,
11
- computePhysics3DCapsuleMassData,
11
+ computePhysics3DCylinderMassData,
12
12
  computePhysics3DSphereMassData,
13
13
  createPhysics3DMassData,
14
- updateRigidBody3DMassData,
14
+ initializePhysics3DMassData,
15
15
  setRigidBody3DMassData,
16
+ updateRigidBody3DMassData,
16
17
  } from './massProperties';
17
18
  import { addPhysics3DBody, createPhysics3DCollider, createPhysics3DWorld, createRigidBody3D } from './world';
18
19
 
@@ -427,6 +428,12 @@ describe('createPhysics3DMassData', () => {
427
428
  });
428
429
  });
429
430
 
431
+ describe('initializePhysics3DMassData', () => {
432
+ it('is the construction initializer of createPhysics3DMassData', () => {
433
+ expect(typeof initializePhysics3DMassData).toBe('function');
434
+ });
435
+ });
436
+
430
437
  describe('setRigidBody3DMassData', () => {
431
438
  it('derives the inverse mass and a non-zero inverse tensor for a dynamic body', () => {
432
439
  const body = testBody('dynamic');
@@ -496,7 +503,6 @@ describe('setRigidBody3DMassData', () => {
496
503
  expect(body.centerY).toBe(4);
497
504
  });
498
505
  });
499
-
500
506
  describe('updateRigidBody3DMassData', () => {
501
507
  it('derives a body mass from its colliders', () => {
502
508
  const world = createPhysics3DWorld();
@@ -17,6 +17,8 @@ import { setRigidBody3DMassData } from './massProperties';
17
17
  import {
18
18
  createPhysics3DContactConstraint,
19
19
  createPhysics3DContactConstraintPoint,
20
+ initializePhysics3DContactConstraint,
21
+ initializePhysics3DContactConstraintPoint,
20
22
  preparePhysics3DContactConstraints,
21
23
  solvePhysics3DContactPositions,
22
24
  solvePhysics3DContactVelocities,
@@ -55,6 +57,18 @@ describe('createPhysics3DContactConstraintPoint', () => {
55
57
  });
56
58
  });
57
59
 
60
+ describe('initializePhysics3DContactConstraint', () => {
61
+ it('is the construction initializer of createPhysics3DContactConstraint', () => {
62
+ expect(typeof initializePhysics3DContactConstraint).toBe('function');
63
+ });
64
+ });
65
+
66
+ describe('initializePhysics3DContactConstraintPoint', () => {
67
+ it('is the construction initializer of createPhysics3DContactConstraintPoint', () => {
68
+ expect(typeof initializePhysics3DContactConstraintPoint).toBe('function');
69
+ });
70
+ });
71
+
58
72
  describe('preparePhysics3DContactConstraints', () => {
59
73
  it('builds a friction basis orthonormal to the contact normal', () => {
60
74
  const world = createFallingBoxWorld();
@@ -293,6 +307,93 @@ describe('solvePhysics3DContactPositions', () => {
293
307
  });
294
308
  });
295
309
 
310
+ function createContactPoint(featureId: number, depth: number): Physics3DContactPoint {
311
+ const point = createPhysics3DContactPointRecord();
312
+ point.depth = depth;
313
+ point.featureId = featureId;
314
+ point.rAY = -0.5;
315
+ point.rBY = 0.5;
316
+ return point;
317
+ }
318
+
319
+ function createContact(bodyA: number, bodyB: number): Physics3DContact {
320
+ const contact = createPhysics3DContactRecord(bodyA, bodyB);
321
+ contact.normalY = 1;
322
+ contact.pointCount = 1;
323
+ contact.points.push(createContactPoint(1, 0.01));
324
+ contact.touching = true;
325
+ return contact;
326
+ }
327
+
328
+ function createUnitBox(world: Physics3DWorld): RigidBody3D {
329
+ const body = createRigidBody3D();
330
+ const mass = createPhysics3DMassData();
331
+ computePhysics3DBoxMassData(0.5, 0.5, 0.5, 1, mass);
332
+ setRigidBody3DMassData(body, mass);
333
+ refreshRigidBody3DWorldInertia(body);
334
+ addPhysics3DBody(world, body);
335
+ return body;
336
+ }
337
+
338
+ // The solver reads the SOLVE ISLAND contact slices rather than `world.contacts`, so a world assembled
339
+ // by hand needs the same workspace `stepPhysics3D` builds before it reaches the solver. Without it every
340
+ // prepare finds nothing to do — which is the correct answer for a world with no awake islands, and the
341
+ // reason this is a factory step rather than something prepare rebuilds for itself.
342
+ //
343
+ // A test that changes what belongs in an island AFTER this — putting a body to sleep, disabling a
344
+ // contact, making a body static — has to call it again, exactly as a real step rebuilds every step.
345
+ function buildSolveWorkspace(world: Physics3DWorld): Physics3DWorld {
346
+ updatePhysics3DSleep(world, 1 / 60);
347
+ buildPhysics3DSolveIslands(world);
348
+ return world;
349
+ }
350
+
351
+ // One dynamic box whose single contact point references a body that is not in the world's contact
352
+ // partner slot — used for the basis and mass-denominator tests, where only body A matters.
353
+ function createFallingBoxWorld(): Physics3DWorld {
354
+ const world = createPhysics3DWorld();
355
+ const box = createUnitBox(world);
356
+ const other = createUnitBox(world);
357
+ world.contacts.push(createContact(box.index, other.index));
358
+ return buildSolveWorkspace(world);
359
+ }
360
+
361
+ // Two unit boxes with REAL colliders, overlapping vertically by `depth`, with contacts generated the
362
+ // way a step generates them. The position pass reads geometry, so this is the only fixture shape its
363
+ // tests can be written against.
364
+ function createOverlappingBoxWorld(depth: number): Physics3DWorld {
365
+ const world = createPhysics3DWorld();
366
+ const upper = createUnitBox(world);
367
+ const lower = createUnitBox(world);
368
+ setPhysics3DBodyType(lower, 'static');
369
+ for (const body of [upper, lower]) {
370
+ addPhysics3DCollider(
371
+ world,
372
+ body,
373
+ createPhysics3DCollider({ kind: 'aabb', minX: -0.5, minY: -0.5, minZ: -0.5, maxX: 0.5, maxY: 0.5, maxZ: 0.5 }),
374
+ );
375
+ }
376
+ upper.y = 1 - depth;
377
+ buildPhysics3DContacts(world);
378
+ return buildSolveWorkspace(world);
379
+ }
380
+
381
+ function createBoxOnGroundWorld(): Physics3DWorld {
382
+ const world = createPhysics3DWorld();
383
+ const box = createUnitBox(world);
384
+ const ground = createUnitBox(world);
385
+ setPhysics3DBodyType(ground, 'static');
386
+ world.contacts.push(createContact(box.index, ground.index));
387
+ return buildSolveWorkspace(world);
388
+ }
389
+
390
+ function createTwoDynamicBodyWorld(): Physics3DWorld {
391
+ const world = createPhysics3DWorld();
392
+ const upper = createUnitBox(world);
393
+ const lower = createUnitBox(world);
394
+ world.contacts.push(createContact(upper.index, lower.index));
395
+ return buildSolveWorkspace(world);
396
+ }
296
397
  describe('solvePhysics3DContactVelocities', () => {
297
398
  it('removes the approach velocity of a body resting on static ground', () => {
298
399
  const world = createBoxOnGroundWorld();
@@ -464,91 +565,3 @@ describe('warmStartPhysics3DContacts', () => {
464
565
  expect(Math.abs(warm.bodies[0].velocityY)).toBeLessThanOrEqual(Math.abs(cold.bodies[0].velocityY));
465
566
  });
466
567
  });
467
-
468
- function createContactPoint(featureId: number, depth: number): Physics3DContactPoint {
469
- const point = createPhysics3DContactPointRecord();
470
- point.depth = depth;
471
- point.featureId = featureId;
472
- point.rAY = -0.5;
473
- point.rBY = 0.5;
474
- return point;
475
- }
476
-
477
- function createContact(bodyA: number, bodyB: number): Physics3DContact {
478
- const contact = createPhysics3DContactRecord(bodyA, bodyB);
479
- contact.normalY = 1;
480
- contact.pointCount = 1;
481
- contact.points.push(createContactPoint(1, 0.01));
482
- contact.touching = true;
483
- return contact;
484
- }
485
-
486
- function createUnitBox(world: Physics3DWorld): RigidBody3D {
487
- const body = createRigidBody3D();
488
- const mass = createPhysics3DMassData();
489
- computePhysics3DBoxMassData(0.5, 0.5, 0.5, 1, mass);
490
- setRigidBody3DMassData(body, mass);
491
- refreshRigidBody3DWorldInertia(body);
492
- addPhysics3DBody(world, body);
493
- return body;
494
- }
495
-
496
- // The solver reads the SOLVE ISLAND contact slices rather than `world.contacts`, so a world assembled
497
- // by hand needs the same workspace `stepPhysics3D` builds before it reaches the solver. Without it every
498
- // prepare finds nothing to do — which is the correct answer for a world with no awake islands, and the
499
- // reason this is a factory step rather than something prepare rebuilds for itself.
500
- //
501
- // A test that changes what belongs in an island AFTER this — putting a body to sleep, disabling a
502
- // contact, making a body static — has to call it again, exactly as a real step rebuilds every step.
503
- function buildSolveWorkspace(world: Physics3DWorld): Physics3DWorld {
504
- updatePhysics3DSleep(world, 1 / 60);
505
- buildPhysics3DSolveIslands(world);
506
- return world;
507
- }
508
-
509
- // One dynamic box whose single contact point references a body that is not in the world's contact
510
- // partner slot — used for the basis and mass-denominator tests, where only body A matters.
511
- function createFallingBoxWorld(): Physics3DWorld {
512
- const world = createPhysics3DWorld();
513
- const box = createUnitBox(world);
514
- const other = createUnitBox(world);
515
- world.contacts.push(createContact(box.index, other.index));
516
- return buildSolveWorkspace(world);
517
- }
518
-
519
- // Two unit boxes with REAL colliders, overlapping vertically by `depth`, with contacts generated the
520
- // way a step generates them. The position pass reads geometry, so this is the only fixture shape its
521
- // tests can be written against.
522
- function createOverlappingBoxWorld(depth: number): Physics3DWorld {
523
- const world = createPhysics3DWorld();
524
- const upper = createUnitBox(world);
525
- const lower = createUnitBox(world);
526
- setPhysics3DBodyType(lower, 'static');
527
- for (const body of [upper, lower]) {
528
- addPhysics3DCollider(
529
- world,
530
- body,
531
- createPhysics3DCollider({ kind: 'aabb', minX: -0.5, minY: -0.5, minZ: -0.5, maxX: 0.5, maxY: 0.5, maxZ: 0.5 }),
532
- );
533
- }
534
- upper.y = 1 - depth;
535
- buildPhysics3DContacts(world);
536
- return buildSolveWorkspace(world);
537
- }
538
-
539
- function createBoxOnGroundWorld(): Physics3DWorld {
540
- const world = createPhysics3DWorld();
541
- const box = createUnitBox(world);
542
- const ground = createUnitBox(world);
543
- setPhysics3DBodyType(ground, 'static');
544
- world.contacts.push(createContact(box.index, ground.index));
545
- return buildSolveWorkspace(world);
546
- }
547
-
548
- function createTwoDynamicBodyWorld(): Physics3DWorld {
549
- const world = createPhysics3DWorld();
550
- const upper = createUnitBox(world);
551
- const lower = createUnitBox(world);
552
- world.contacts.push(createContact(upper.index, lower.index));
553
- return buildSolveWorkspace(world);
554
- }
package/src/world.test.ts CHANGED
@@ -15,6 +15,7 @@ import { addPhysics3DJoint } from './jointRegistry';
15
15
  import { computePhysics3DSphereMassData, createPhysics3DMassData, setRigidBody3DMassData } from './massProperties';
16
16
  import { registerBuiltInPhysics3DJointSolvers } from './registerBuiltInPhysics3DJointSolvers';
17
17
  import {
18
+ Physics3DWorldVersion,
18
19
  addPhysics3DBody,
19
20
  addPhysics3DCollider,
20
21
  applyPhysics3DForce,
@@ -29,8 +30,10 @@ import {
29
30
  createRigidBody3D,
30
31
  findPhysics3DBody,
31
32
  hydratePhysics3DWorld,
33
+ initializePhysics3DCollider,
34
+ initializePhysics3DWorld,
35
+ initializeRigidBody3D,
32
36
  invalidatePhysics3DCollider,
33
- Physics3DWorldVersion,
34
37
  removePhysics3DBody,
35
38
  removePhysics3DCollider,
36
39
  setPhysics3DBodyBullet,
@@ -541,6 +544,24 @@ describe('hydratePhysics3DWorld', () => {
541
544
  });
542
545
  });
543
546
 
547
+ describe('initializePhysics3DCollider', () => {
548
+ it('is the construction initializer of createPhysics3DCollider', () => {
549
+ expect(typeof initializePhysics3DCollider).toBe('function');
550
+ });
551
+ });
552
+
553
+ describe('initializePhysics3DWorld', () => {
554
+ it('is the construction initializer of createPhysics3DWorld', () => {
555
+ expect(typeof initializePhysics3DWorld).toBe('function');
556
+ });
557
+ });
558
+
559
+ describe('initializeRigidBody3D', () => {
560
+ it('is the construction initializer of createRigidBody3D', () => {
561
+ expect(typeof initializeRigidBody3D).toBe('function');
562
+ });
563
+ });
564
+
544
565
  describe('invalidatePhysics3DCollider', () => {
545
566
  it('rebuilds the mass properties after the local shape is edited in place', () => {
546
567
  const world = createPhysics3DWorld();
@@ -763,6 +784,38 @@ describe('removePhysics3DCollider', () => {
763
784
  });
764
785
  });
765
786
 
787
+ function contact(bodyA: number, bodyB: number): Physics3DWorld['contacts'][number] {
788
+ const value = createPhysics3DContact(bodyA, bodyB);
789
+ value.normalY = 1;
790
+ value.friction = 0.2;
791
+ value.touching = true;
792
+ return value;
793
+ }
794
+
795
+ function sphere(): RigidBody3D {
796
+ const body = createRigidBody3D();
797
+ const data = createPhysics3DMassData();
798
+ computePhysics3DSphereMassData(1, 1, data);
799
+ setRigidBody3DMassData(body, data);
800
+ return body;
801
+ }
802
+
803
+ beforeEach(() => {
804
+ registerBuiltInCollisionSupports3D();
805
+ registerBuiltInCollisionFaceQueries3D();
806
+ });
807
+
808
+ function colliderUnitBox(): CollisionBuiltInShape3D {
809
+ return { kind: 'aabb', minX: -0.5, minY: -0.5, minZ: -0.5, maxX: 0.5, maxY: 0.5, maxZ: 0.5 };
810
+ }
811
+
812
+ function addColliderTestBody(world: Physics3DWorld, y = 0): RigidBody3D {
813
+ const body = createRigidBody3D('dynamic');
814
+ body.y = y;
815
+ addPhysics3DBody(world, body);
816
+ return body;
817
+ }
818
+
766
819
  describe('setPhysics3DBodyBullet', () => {
767
820
  it('changes continuous-collision policy and wakes an owned body', () => {
768
821
  const world = createPhysics3DWorld();
@@ -843,38 +896,6 @@ describe('setPhysics3DBodySleepEnabled', () => {
843
896
  });
844
897
  });
845
898
 
846
- function contact(bodyA: number, bodyB: number): Physics3DWorld['contacts'][number] {
847
- const value = createPhysics3DContact(bodyA, bodyB);
848
- value.normalY = 1;
849
- value.friction = 0.2;
850
- value.touching = true;
851
- return value;
852
- }
853
-
854
- function sphere(): RigidBody3D {
855
- const body = createRigidBody3D();
856
- const data = createPhysics3DMassData();
857
- computePhysics3DSphereMassData(1, 1, data);
858
- setRigidBody3DMassData(body, data);
859
- return body;
860
- }
861
-
862
- beforeEach(() => {
863
- registerBuiltInCollisionSupports3D();
864
- registerBuiltInCollisionFaceQueries3D();
865
- });
866
-
867
- function colliderUnitBox(): CollisionBuiltInShape3D {
868
- return { kind: 'aabb', minX: -0.5, minY: -0.5, minZ: -0.5, maxX: 0.5, maxY: 0.5, maxZ: 0.5 };
869
- }
870
-
871
- function addColliderTestBody(world: Physics3DWorld, y = 0): RigidBody3D {
872
- const body = createRigidBody3D('dynamic');
873
- body.y = y;
874
- addPhysics3DBody(world, body);
875
- return body;
876
- }
877
-
878
899
  describe('setPhysics3DBodyTransform', () => {
879
900
  it('moves the body and refreshes the world inertia', () => {
880
901
  const body = sphere();
@@ -948,7 +969,6 @@ describe('setPhysics3DBodyTransform', () => {
948
969
  expect(neighbour.sleeping).toBe(false);
949
970
  });
950
971
  });
951
-
952
972
  describe('setPhysics3DBodyType', () => {
953
973
  it('declines to make a static terrain body movable', () => {
954
974
  const body = createRigidBody3D('static');
@@ -14,6 +14,9 @@ import {
14
14
  createPhysics3DQueryResult,
15
15
  createPhysics3DRayResult,
16
16
  createPhysics3DShapeCastResult,
17
+ initializePhysics3DQueryResult,
18
+ initializePhysics3DRayResult,
19
+ initializePhysics3DShapeCastResult,
17
20
  queryPhysics3DPoint,
18
21
  queryPhysics3DRay,
19
22
  queryPhysics3DRayClosest,
@@ -81,6 +84,24 @@ describe('createPhysics3DShapeCastResult', () => {
81
84
  });
82
85
  });
83
86
 
87
+ describe('initializePhysics3DQueryResult', () => {
88
+ it('is the construction initializer of createPhysics3DQueryResult', () => {
89
+ expect(typeof initializePhysics3DQueryResult).toBe('function');
90
+ });
91
+ });
92
+
93
+ describe('initializePhysics3DRayResult', () => {
94
+ it('is the construction initializer of createPhysics3DRayResult', () => {
95
+ expect(typeof initializePhysics3DRayResult).toBe('function');
96
+ });
97
+ });
98
+
99
+ describe('initializePhysics3DShapeCastResult', () => {
100
+ it('is the construction initializer of createPhysics3DShapeCastResult', () => {
101
+ expect(typeof initializePhysics3DShapeCastResult).toBe('function');
102
+ });
103
+ });
104
+
84
105
  describe('queryPhysics3DPoint', () => {
85
106
  it('finds the collider containing the point', () => {
86
107
  const world = createPhysics3DWorld();
@@ -311,7 +332,6 @@ describe('queryPhysics3DRay', () => {
311
332
  expect(out.hits[0].fraction).toBeCloseTo(4.5, 6);
312
333
  });
313
334
  });
314
-
315
335
  describe('queryPhysics3DRayClosest', () => {
316
336
  it('writes only the nearest hit', () => {
317
337
  const world = createPhysics3DWorld();