@woosh/meep-engine 2.140.0 → 2.142.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/package.json +1 -1
- package/src/core/geom/3d/quaternion/quat3_multiply.d.ts +21 -0
- package/src/core/geom/3d/quaternion/quat3_multiply.d.ts.map +1 -0
- package/src/core/geom/3d/quaternion/quat3_multiply.js +25 -0
- package/src/engine/control/first-person/prototype_first_person_controller.js +5 -0
- package/src/engine/graphics/render/buffer/simple-fx/ao/AmbientOcclusionPostProcessEffect.d.ts.map +1 -1
- package/src/engine/graphics/render/buffer/simple-fx/ao/AmbientOcclusionPostProcessEffect.js +67 -42
- package/src/engine/graphics/render/buffer/simple-fx/ao/SAOShader.d.ts +12 -22
- package/src/engine/graphics/render/buffer/simple-fx/ao/SAOShader.d.ts.map +1 -1
- package/src/engine/graphics/render/buffer/simple-fx/ao/SAOShader.js +340 -186
- package/src/engine/graphics/render/buffer/simple-fx/ao/SAOUpscaleShader.d.ts +44 -0
- package/src/engine/graphics/render/buffer/simple-fx/ao/SAOUpscaleShader.d.ts.map +1 -0
- package/src/engine/graphics/render/buffer/simple-fx/ao/SAOUpscaleShader.js +151 -0
- package/src/engine/graphics/render/buffer/simple-fx/ao/generateHilbertNoiseTexture.d.ts +14 -0
- package/src/engine/graphics/render/buffer/simple-fx/ao/generateHilbertNoiseTexture.d.ts.map +1 -0
- package/src/engine/graphics/render/buffer/simple-fx/ao/generateHilbertNoiseTexture.js +78 -0
- package/src/engine/physics/PLAN.md +705 -461
- package/src/engine/physics/REVIEW_002.md +151 -0
- package/src/engine/physics/REVIEW_003.md +166 -0
- package/src/engine/physics/constraint/DofMode.d.ts +28 -0
- package/src/engine/physics/constraint/DofMode.d.ts.map +1 -0
- package/src/engine/physics/constraint/DofMode.js +35 -0
- package/src/engine/physics/constraint/solve_constraints.d.ts +38 -0
- package/src/engine/physics/constraint/solve_constraints.d.ts.map +1 -0
- package/src/engine/physics/constraint/solve_constraints.js +673 -0
- package/src/engine/physics/ecs/Joint.d.ts +294 -0
- package/src/engine/physics/ecs/Joint.d.ts.map +1 -0
- package/src/engine/physics/ecs/Joint.js +402 -0
- package/src/engine/physics/ecs/PhysicsSystem.d.ts +52 -0
- package/src/engine/physics/ecs/PhysicsSystem.d.ts.map +1 -1
- package/src/engine/physics/ecs/PhysicsSystem.js +126 -4
- package/src/engine/physics/fluid/FluidField.d.ts +14 -10
- package/src/engine/physics/fluid/FluidField.d.ts.map +1 -1
- package/src/engine/physics/fluid/FluidField.js +14 -10
- package/src/engine/physics/fluid/FluidSimulator.d.ts.map +1 -1
- package/src/engine/physics/fluid/FluidSimulator.js +0 -1
- package/src/engine/physics/fluid/solver/v3_grid_compute_solid_neighbour_mask.d.ts +17 -10
- package/src/engine/physics/fluid/solver/v3_grid_compute_solid_neighbour_mask.d.ts.map +1 -1
- package/src/engine/physics/fluid/solver/v3_grid_compute_solid_neighbour_mask.js +18 -11
- package/src/engine/physics/fluid/solver/v3_grid_solve_pressure.d.ts +13 -10
- package/src/engine/physics/fluid/solver/v3_grid_solve_pressure.d.ts.map +1 -1
- package/src/engine/physics/fluid/solver/v3_grid_solve_pressure.js +18 -13
- package/src/engine/physics/fluid/solver/v3_grid_solve_pressure_pcg.d.ts +4 -3
- package/src/engine/physics/fluid/solver/v3_grid_solve_pressure_pcg.d.ts.map +1 -1
- package/src/engine/physics/fluid/solver/v3_grid_solve_pressure_pcg.js +15 -11
- package/src/engine/physics/fluid/solver/v3_grid_subtract_pressure_gradient.d.ts +24 -22
- package/src/engine/physics/fluid/solver/v3_grid_subtract_pressure_gradient.d.ts.map +1 -1
- package/src/engine/physics/fluid/solver/v3_grid_subtract_pressure_gradient.js +26 -22
- package/src/engine/physics/island/IslandBuilder.d.ts +4 -1
- package/src/engine/physics/island/IslandBuilder.d.ts.map +1 -1
- package/src/engine/physics/island/IslandBuilder.js +33 -16
- package/src/engine/physics/narrowphase/box_box_manifold.d.ts.map +1 -1
- package/src/engine/physics/narrowphase/box_box_manifold.js +27 -1
- package/src/engine/physics/narrowphase/narrowphase_step.d.ts +33 -0
- package/src/engine/physics/narrowphase/narrowphase_step.d.ts.map +1 -1
- package/src/engine/physics/narrowphase/narrowphase_step.js +75 -0
- package/src/engine/physics/narrowphase/ray_shapes.d.ts +66 -0
- package/src/engine/physics/narrowphase/ray_shapes.d.ts.map +1 -0
- package/src/engine/physics/narrowphase/ray_shapes.js +187 -0
- package/src/engine/physics/narrowphase/refine_ray_concave.d.ts +16 -0
- package/src/engine/physics/narrowphase/refine_ray_concave.d.ts.map +1 -0
- package/src/engine/physics/narrowphase/refine_ray_concave.js +145 -0
- package/src/engine/physics/narrowphase/refine_ray_hit.d.ts +39 -0
- package/src/engine/physics/narrowphase/refine_ray_hit.d.ts.map +1 -0
- package/src/engine/physics/narrowphase/refine_ray_hit.js +78 -0
- package/src/engine/physics/queries/raycast.d.ts +11 -9
- package/src/engine/physics/queries/raycast.d.ts.map +1 -1
- package/src/engine/physics/queries/raycast.js +108 -159
- package/src/engine/physics/solver/solve_contacts.d.ts +28 -0
- package/src/engine/physics/solver/solve_contacts.d.ts.map +1 -1
- package/src/engine/physics/solver/solve_contacts.js +169 -1
- package/src/engine/physics/vehicle/RaycastVehicle.d.ts +114 -0
- package/src/engine/physics/vehicle/RaycastVehicle.d.ts.map +1 -0
- package/src/engine/physics/vehicle/RaycastVehicle.js +333 -0
|
@@ -108,9 +108,10 @@ const MIC_SIGMA = 0.25;
|
|
|
108
108
|
* the hot path that prevents MIC-PCG from parallelizing onto a GPU without
|
|
109
109
|
* substantial reformulation. On single-thread JS that's not a concern.
|
|
110
110
|
*
|
|
111
|
-
*
|
|
112
|
-
*
|
|
113
|
-
*
|
|
111
|
+
* Cells with no fluid neighbours — solid cells (mask bit 7 set) and isolated
|
|
112
|
+
* fluid (mask = 0), both identified by `(mask & 63) === 0` — are excluded from
|
|
113
|
+
* the system: their pressure stays at whatever it was (typically 0), their
|
|
114
|
+
* entries in r, z, s, As stay 0, and they contribute nothing to dot products.
|
|
114
115
|
*
|
|
115
116
|
* @param {Float32Array|Float16Array} pressure Mutated in place. Pre-fill with 0
|
|
116
117
|
* or with the previous step's solution to warm-start.
|
|
@@ -173,9 +174,12 @@ export function v3_grid_solve_pressure_pcg(
|
|
|
173
174
|
// (Standard fluids practice — see Bridson §5.5. The alternative is to pin
|
|
174
175
|
// one cell to a Dirichlet value and exclude it from the system, but mean
|
|
175
176
|
// subtraction has the same effect with simpler bookkeeping.)
|
|
177
|
+
// A cell is a degree of freedom iff it has ≥1 fluid neighbour, i.e. any of
|
|
178
|
+
// the low 6 bits set. Solid cells (bit 7) and isolated fluid (all-zero) are
|
|
179
|
+
// both excluded by masking off bit 7 with & 63.
|
|
176
180
|
let fluid_count = 0;
|
|
177
181
|
for (let c = 0; c < cells; c++) {
|
|
178
|
-
if (neighbour_mask[c] !== 0) fluid_count++;
|
|
182
|
+
if ((neighbour_mask[c] & 63) !== 0) fluid_count++;
|
|
179
183
|
}
|
|
180
184
|
if (fluid_count === 0) {
|
|
181
185
|
return;
|
|
@@ -193,7 +197,7 @@ export function v3_grid_solve_pressure_pcg(
|
|
|
193
197
|
precon.fill(0);
|
|
194
198
|
for (let c = 0; c < cells; c++) {
|
|
195
199
|
const mask = neighbour_mask[c];
|
|
196
|
-
if (mask === 0) {
|
|
200
|
+
if ((mask & 63) === 0) {
|
|
197
201
|
continue;
|
|
198
202
|
}
|
|
199
203
|
const a_diag = POPCOUNT_6[mask];
|
|
@@ -246,7 +250,7 @@ export function v3_grid_solve_pressure_pcg(
|
|
|
246
250
|
|
|
247
251
|
for (let c = 0; c < cells; c++) {
|
|
248
252
|
const mask = neighbour_mask[c];
|
|
249
|
-
if (mask === 0) {
|
|
253
|
+
if ((mask & 63) === 0) {
|
|
250
254
|
r[c] = 0;
|
|
251
255
|
continue;
|
|
252
256
|
}
|
|
@@ -285,7 +289,7 @@ export function v3_grid_solve_pressure_pcg(
|
|
|
285
289
|
let s_dot_As = 0;
|
|
286
290
|
for (let c = 0; c < cells; c++) {
|
|
287
291
|
const mask = neighbour_mask[c];
|
|
288
|
-
if (mask === 0) {
|
|
292
|
+
if ((mask & 63) === 0) {
|
|
289
293
|
As[c] = 0;
|
|
290
294
|
continue;
|
|
291
295
|
}
|
|
@@ -379,11 +383,11 @@ export function v3_grid_solve_pressure_pcg(
|
|
|
379
383
|
function subtract_mean_fluid(v, mask_array, cells, fluid_count) {
|
|
380
384
|
let sum = 0;
|
|
381
385
|
for (let c = 0; c < cells; c++) {
|
|
382
|
-
if (mask_array[c] !== 0) sum += v[c];
|
|
386
|
+
if ((mask_array[c] & 63) !== 0) sum += v[c];
|
|
383
387
|
}
|
|
384
388
|
const mean = sum / fluid_count;
|
|
385
389
|
for (let c = 0; c < cells; c++) {
|
|
386
|
-
if (mask_array[c] !== 0) v[c] -= mean;
|
|
390
|
+
if ((mask_array[c] & 63) !== 0) v[c] -= mean;
|
|
387
391
|
}
|
|
388
392
|
}
|
|
389
393
|
|
|
@@ -391,7 +395,7 @@ function apply_preconditioner(r, z, precon, mask_array, cells, rx, slice) {
|
|
|
391
395
|
// Forward solve L · y = r. Store y in z (we won't read z[c'] for c' > c).
|
|
392
396
|
for (let c = 0; c < cells; c++) {
|
|
393
397
|
const mask = mask_array[c];
|
|
394
|
-
if (mask === 0) {
|
|
398
|
+
if ((mask & 63) === 0) {
|
|
395
399
|
z[c] = 0;
|
|
396
400
|
continue;
|
|
397
401
|
}
|
|
@@ -410,7 +414,7 @@ function apply_preconditioner(r, z, precon, mask_array, cells, rx, slice) {
|
|
|
410
414
|
// indices, processed first in reverse).
|
|
411
415
|
for (let c = cells - 1; c >= 0; c--) {
|
|
412
416
|
const mask = mask_array[c];
|
|
413
|
-
if (mask === 0) {
|
|
417
|
+
if ((mask & 63) === 0) {
|
|
414
418
|
continue;
|
|
415
419
|
}
|
|
416
420
|
let t = z[c]; // = y[c] from forward pass
|
|
@@ -13,25 +13,28 @@
|
|
|
13
13
|
* pressure (consistent with the Neumann condition used in the solve), which keeps the
|
|
14
14
|
* flow tangent to the boundary.
|
|
15
15
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* {@link FluidField.recomputeSolidNeighbourMask}).
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
* bit
|
|
24
|
-
* bit
|
|
25
|
-
* bit
|
|
26
|
-
* bit
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
16
|
+
* Everything this needs is read straight from `neighbour_mask`, the same pre-baked
|
|
17
|
+
* per-cell bitmask {@link v3_grid_solve_pressure} consumes (populated by
|
|
18
|
+
* {@link FluidField.recomputeSolidNeighbourMask}). Encoding:
|
|
19
|
+
*
|
|
20
|
+
* bit 0 (= 1) : -x neighbour is fluid
|
|
21
|
+
* bit 1 (= 2) : +x neighbour is fluid
|
|
22
|
+
* bit 2 (= 4) : -y neighbour is fluid
|
|
23
|
+
* bit 3 (= 8) : +y neighbour is fluid
|
|
24
|
+
* bit 4 (= 16) : -z neighbour is fluid
|
|
25
|
+
* bit 5 (= 32) : +z neighbour is fluid
|
|
26
|
+
* bit 7 (= 128) : this cell is itself solid
|
|
27
|
+
*
|
|
28
|
+
* A set neighbour bit means "that neighbour is fluid (in-bounds AND non-solid)", so a
|
|
29
|
+
* clear bit reflects the cell's own pressure — folding the boundary check and the
|
|
30
|
+
* solid-neighbour check into one register-resident bit-test per face.
|
|
31
|
+
*
|
|
32
|
+
* Bit 7 carries the self-solid flag that used to require the separate `solid` array.
|
|
33
|
+
* The solve can't use the low 6 bits for this (a solid cell and an isolated fluid cell
|
|
34
|
+
* both have zero fluid-neighbour bits) but needs opposite handling here — the solid
|
|
35
|
+
* must be zeroed for no-slip, while the isolated fluid's gradient already nets to zero
|
|
36
|
+
* (every face reflects its own pressure) and must be left alone. Bit 7 distinguishes
|
|
37
|
+
* them, so a single mask read drives both the zeroing and the gradient.
|
|
35
38
|
*
|
|
36
39
|
* @param {Float32Array} vel_x Mutated in place.
|
|
37
40
|
* @param {Float32Array} vel_y Mutated in place.
|
|
@@ -40,9 +43,8 @@
|
|
|
40
43
|
* @param {number} res_x
|
|
41
44
|
* @param {number} res_y
|
|
42
45
|
* @param {number} res_z
|
|
43
|
-
* @param {Uint8Array} solid Required (zero-filled for a wall-free domain).
|
|
44
46
|
* @param {Uint8Array} neighbour_mask Length ≥ res_x*res_y*res_z. Same buffer the
|
|
45
|
-
* pressure solve uses; MUST be recomputed whenever
|
|
47
|
+
* pressure solve uses; MUST be recomputed whenever the solid mask changes.
|
|
46
48
|
*/
|
|
47
|
-
export function v3_grid_subtract_pressure_gradient(vel_x: Float32Array, vel_y: Float32Array, vel_z: Float32Array, pressure: Float32Array, res_x: number, res_y: number, res_z: number,
|
|
49
|
+
export function v3_grid_subtract_pressure_gradient(vel_x: Float32Array, vel_y: Float32Array, vel_z: Float32Array, pressure: Float32Array, res_x: number, res_y: number, res_z: number, neighbour_mask: Uint8Array): void;
|
|
48
50
|
//# sourceMappingURL=v3_grid_subtract_pressure_gradient.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"v3_grid_subtract_pressure_gradient.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/physics/fluid/solver/v3_grid_subtract_pressure_gradient.js"],"names":[],"mappings":"AAEA
|
|
1
|
+
{"version":3,"file":"v3_grid_subtract_pressure_gradient.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/physics/fluid/solver/v3_grid_subtract_pressure_gradient.js"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AACH,0DAVW,YAAY,SACZ,YAAY,SACZ,YAAY,YACZ,YAAY,SACZ,MAAM,SACN,MAAM,SACN,MAAM,kBACN,UAAU,QAgDpB"}
|
|
@@ -15,25 +15,28 @@ import { assert } from "../../../../core/assert.js";
|
|
|
15
15
|
* pressure (consistent with the Neumann condition used in the solve), which keeps the
|
|
16
16
|
* flow tangent to the boundary.
|
|
17
17
|
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
* {@link FluidField.recomputeSolidNeighbourMask}).
|
|
21
|
-
* fluid (in-bounds AND non-solid)", so a clear bit reflects the cell's own pressure —
|
|
22
|
-
* folding the boundary check and the solid-neighbour check into one register-resident
|
|
23
|
-
* bit-test per face. Encoding:
|
|
18
|
+
* Everything this needs is read straight from `neighbour_mask`, the same pre-baked
|
|
19
|
+
* per-cell bitmask {@link v3_grid_solve_pressure} consumes (populated by
|
|
20
|
+
* {@link FluidField.recomputeSolidNeighbourMask}). Encoding:
|
|
24
21
|
*
|
|
25
|
-
* bit 0 (= 1)
|
|
26
|
-
* bit 1 (= 2)
|
|
27
|
-
* bit 2 (= 4)
|
|
28
|
-
* bit 3 (= 8)
|
|
29
|
-
* bit 4 (= 16)
|
|
30
|
-
* bit 5 (= 32)
|
|
22
|
+
* bit 0 (= 1) : -x neighbour is fluid
|
|
23
|
+
* bit 1 (= 2) : +x neighbour is fluid
|
|
24
|
+
* bit 2 (= 4) : -y neighbour is fluid
|
|
25
|
+
* bit 3 (= 8) : +y neighbour is fluid
|
|
26
|
+
* bit 4 (= 16) : -z neighbour is fluid
|
|
27
|
+
* bit 5 (= 32) : +z neighbour is fluid
|
|
28
|
+
* bit 7 (= 128) : this cell is itself solid
|
|
31
29
|
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
30
|
+
* A set neighbour bit means "that neighbour is fluid (in-bounds AND non-solid)", so a
|
|
31
|
+
* clear bit reflects the cell's own pressure — folding the boundary check and the
|
|
32
|
+
* solid-neighbour check into one register-resident bit-test per face.
|
|
33
|
+
*
|
|
34
|
+
* Bit 7 carries the self-solid flag that used to require the separate `solid` array.
|
|
35
|
+
* The solve can't use the low 6 bits for this (a solid cell and an isolated fluid cell
|
|
36
|
+
* both have zero fluid-neighbour bits) but needs opposite handling here — the solid
|
|
37
|
+
* must be zeroed for no-slip, while the isolated fluid's gradient already nets to zero
|
|
38
|
+
* (every face reflects its own pressure) and must be left alone. Bit 7 distinguishes
|
|
39
|
+
* them, so a single mask read drives both the zeroing and the gradient.
|
|
37
40
|
*
|
|
38
41
|
* @param {Float32Array} vel_x Mutated in place.
|
|
39
42
|
* @param {Float32Array} vel_y Mutated in place.
|
|
@@ -42,11 +45,10 @@ import { assert } from "../../../../core/assert.js";
|
|
|
42
45
|
* @param {number} res_x
|
|
43
46
|
* @param {number} res_y
|
|
44
47
|
* @param {number} res_z
|
|
45
|
-
* @param {Uint8Array} solid Required (zero-filled for a wall-free domain).
|
|
46
48
|
* @param {Uint8Array} neighbour_mask Length ≥ res_x*res_y*res_z. Same buffer the
|
|
47
|
-
* pressure solve uses; MUST be recomputed whenever
|
|
49
|
+
* pressure solve uses; MUST be recomputed whenever the solid mask changes.
|
|
48
50
|
*/
|
|
49
|
-
export function v3_grid_subtract_pressure_gradient(vel_x, vel_y, vel_z, pressure, res_x, res_y, res_z,
|
|
51
|
+
export function v3_grid_subtract_pressure_gradient(vel_x, vel_y, vel_z, pressure, res_x, res_y, res_z, neighbour_mask) {
|
|
50
52
|
const cell_count = res_x * res_y * res_z;
|
|
51
53
|
assert.greaterThanOrEqual(vel_x.length, cell_count, "vel_x covers grid");
|
|
52
54
|
assert.greaterThanOrEqual(vel_y.length, cell_count, "vel_y covers grid");
|
|
@@ -65,7 +67,10 @@ export function v3_grid_subtract_pressure_gradient(vel_x, vel_y, vel_z, pressure
|
|
|
65
67
|
for (let x = 0; x < res_x; x++) {
|
|
66
68
|
const c = z_off + y_off + x;
|
|
67
69
|
|
|
68
|
-
|
|
70
|
+
const mask = neighbour_mask[c];
|
|
71
|
+
|
|
72
|
+
if (mask & 128) {
|
|
73
|
+
// Solid cell (bit 7): enforce no-slip.
|
|
69
74
|
vel_x[c] = 0;
|
|
70
75
|
vel_y[c] = 0;
|
|
71
76
|
vel_z[c] = 0;
|
|
@@ -75,7 +80,6 @@ export function v3_grid_subtract_pressure_gradient(vel_x, vel_y, vel_z, pressure
|
|
|
75
80
|
// A clear bit means the neighbour is out-of-bounds or solid; reflect
|
|
76
81
|
// the cell's own pressure so that face contributes 0 to the gradient,
|
|
77
82
|
// consistent with ∂p/∂n = 0 at the wall.
|
|
78
|
-
const mask = neighbour_mask[c];
|
|
79
83
|
const p_xm = (mask & 1) ? pressure[c - 1] : pressure[c];
|
|
80
84
|
const p_xp = (mask & 2) ? pressure[c + 1] : pressure[c];
|
|
81
85
|
const p_ym = (mask & 4) ? pressure[c - res_x] : pressure[c];
|
|
@@ -117,8 +117,11 @@ export class IslandBuilder {
|
|
|
117
117
|
* @param {ManifoldStore} manifolds
|
|
118
118
|
* @param {RigidBody[]} bodies sparse, indexed by body index
|
|
119
119
|
* @param {Array[]} body_collider_lists sparse, indexed by body index
|
|
120
|
+
* @param {Joint[]} joints live joints (sparse). Jointed dynamic-dynamic
|
|
121
|
+
* bodies are unioned into the same island so a chain / ragdoll sleeps
|
|
122
|
+
* and wakes as a unit. Callers with no joints pass an empty array.
|
|
120
123
|
*/
|
|
121
|
-
build(storage: BodyStorage, manifolds: ManifoldStore, bodies: RigidBody[], body_collider_lists: any[][]): void;
|
|
124
|
+
build(storage: BodyStorage, manifolds: ManifoldStore, bodies: RigidBody[], body_collider_lists: any[][], joints: Joint[]): void;
|
|
122
125
|
/**
|
|
123
126
|
* Fill `body_offsets` + `body_data` with awake dynamic bodies grouped
|
|
124
127
|
* by island, sorted ascending within each island.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"IslandBuilder.d.ts","sourceRoot":"","sources":["../../../../../src/engine/physics/island/IslandBuilder.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"IslandBuilder.d.ts","sourceRoot":"","sources":["../../../../../src/engine/physics/island/IslandBuilder.js"],"names":[],"mappings":"AAQA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH;IAyFI;;;;;OAKG;IACH,gCAUC;IAtGG;;;;;OAKG;IACH,QAFU,WAAW,CAEY;IAEjC;;;;OAIG;IACH,gBAFU,UAAU,CAEoB;IAExC;;;OAGG;IACH,cAFU,MAAM,CAEK;IAErB;;;;OAIG;IACH,cAFU,WAAW,CAEiB;IAEtC;;;;OAIG;IACH,WAFU,WAAW,CAEe;IAEpC;;;OAGG;IACH,YAFU,MAAM,CAEG;IAEnB;;;;;OAKG;IACH,iBAFU,WAAW,CAEoB;IAEzC;;;;OAIG;IACH,cAFU,WAAW,CAEkB;IAEvC;;;OAGG;IACH,eAFU,MAAM,CAEM;IAEtB;;;;;OAKG;IACH,yBAA0C;IAE1C;;;;;OAKG;IACH,wBAA0C;IAE1C;;;;OAIG;IACH,kBAAmC;IAqBvC;;;;;;;;;;;OAWG;IACH,8DANW,WAAW,uBACX,OAAO,UACP,OAAO,QAgGjB;IAED;;;;OAIG;IACH,yBA+CC;IAED;;;;;;OAMG;IACH,4BAwCC;IAED;;;;;;;;OAQG;IACH,yBAcC;IAID;;;OAGG;IACH,+BAQC;IAED;;;OAGG;IACH,uCAOC;IAED;;;OAGG;IACH,oCAIC;IAED;;;OAGG;IACH,uCAIC;CACJ"}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import { ceilPowerOfTwo } from "../../../core/binary/operations/ceilPowerOfTwo.js";
|
|
1
2
|
import { body_id_index } from "../body/BodyStorage.js";
|
|
2
3
|
import { BodyKind } from "../ecs/BodyKind.js";
|
|
3
4
|
import { ColliderFlags } from "../ecs/ColliderFlags.js";
|
|
5
|
+
import { JOINT_WORLD } from "../ecs/Joint.js";
|
|
4
6
|
import { RigidBodyFlags } from "../ecs/RigidBodyFlags.js";
|
|
5
7
|
import { uf_find, uf_init, uf_union } from "./union_find.js";
|
|
6
8
|
|
|
@@ -149,8 +151,11 @@ export class IslandBuilder {
|
|
|
149
151
|
* @param {ManifoldStore} manifolds
|
|
150
152
|
* @param {RigidBody[]} bodies sparse, indexed by body index
|
|
151
153
|
* @param {Array[]} body_collider_lists sparse, indexed by body index
|
|
154
|
+
* @param {Joint[]} joints live joints (sparse). Jointed dynamic-dynamic
|
|
155
|
+
* bodies are unioned into the same island so a chain / ragdoll sleeps
|
|
156
|
+
* and wakes as a unit. Callers with no joints pass an empty array.
|
|
152
157
|
*/
|
|
153
|
-
build(storage, manifolds, bodies, body_collider_lists) {
|
|
158
|
+
build(storage, manifolds, bodies, body_collider_lists, joints) {
|
|
154
159
|
const hwm = storage.high_water_mark;
|
|
155
160
|
this.__ensure_body_capacity(hwm);
|
|
156
161
|
|
|
@@ -174,6 +179,29 @@ export class IslandBuilder {
|
|
|
174
179
|
}
|
|
175
180
|
}
|
|
176
181
|
|
|
182
|
+
// --- Pass 1b: union dynamic-dynamic bodies connected by a joint, so a
|
|
183
|
+
// chain / ragdoll forms one island (and so sleeps/wakes as a unit).
|
|
184
|
+
// Joint-to-world and joint-to-static/kinematic anchor the island
|
|
185
|
+
// without enlarging it — same rule as a static contact. Stale joint
|
|
186
|
+
// references (body unlinked / slot reused) are filtered by the
|
|
187
|
+
// generation-checked `is_valid`. (Empty when the caller has no
|
|
188
|
+
// joints — the loop is then a no-op.)
|
|
189
|
+
const jn = joints.length;
|
|
190
|
+
for (let i = 0; i < jn; i++) {
|
|
191
|
+
const joint = joints[i];
|
|
192
|
+
if (joint === undefined || joint === null) continue;
|
|
193
|
+
if (joint._bodyIdB === JOINT_WORLD) continue;
|
|
194
|
+
if (!storage.is_valid(joint._bodyIdA) || !storage.is_valid(joint._bodyIdB)) continue;
|
|
195
|
+
const idxA = body_id_index(joint._bodyIdA);
|
|
196
|
+
const idxB = body_id_index(joint._bodyIdB);
|
|
197
|
+
const rbA = bodies[idxA];
|
|
198
|
+
const rbB = bodies[idxB];
|
|
199
|
+
if (rbA === undefined || rbB === undefined) continue;
|
|
200
|
+
if (rbA.kind === BodyKind.Dynamic && rbB.kind === BodyKind.Dynamic) {
|
|
201
|
+
uf_union(parent, idxA, idxB);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
177
205
|
// --- Pass 2: collect distinct roots over awake dynamic bodies.
|
|
178
206
|
const island_of_body = this.island_of_body;
|
|
179
207
|
// Cheap reset: only the indices we may write are below hwm.
|
|
@@ -357,7 +385,7 @@ export class IslandBuilder {
|
|
|
357
385
|
*/
|
|
358
386
|
__ensure_body_capacity(n) {
|
|
359
387
|
if (this.parent.length < n) {
|
|
360
|
-
const cap =
|
|
388
|
+
const cap = ceilPowerOfTwo(n);
|
|
361
389
|
this.parent = new Uint32Array(cap);
|
|
362
390
|
this.island_of_body = new Int32Array(cap);
|
|
363
391
|
this.__root_to_island = new Int32Array(cap);
|
|
@@ -371,7 +399,7 @@ export class IslandBuilder {
|
|
|
371
399
|
*/
|
|
372
400
|
__ensure_island_count_capacity(n) {
|
|
373
401
|
if (this.body_offsets.length < n + 1) {
|
|
374
|
-
const cap =
|
|
402
|
+
const cap = ceilPowerOfTwo(n + 1);
|
|
375
403
|
this.body_offsets = new Uint32Array(cap);
|
|
376
404
|
this.contact_offsets = new Uint32Array(cap);
|
|
377
405
|
this.__cursors = new Uint32Array(cap);
|
|
@@ -384,7 +412,7 @@ export class IslandBuilder {
|
|
|
384
412
|
*/
|
|
385
413
|
__ensure_body_data_capacity(n) {
|
|
386
414
|
if (this.body_data.length < n) {
|
|
387
|
-
this.body_data = new Uint32Array(
|
|
415
|
+
this.body_data = new Uint32Array(ceilPowerOfTwo(n));
|
|
388
416
|
}
|
|
389
417
|
}
|
|
390
418
|
|
|
@@ -394,18 +422,7 @@ export class IslandBuilder {
|
|
|
394
422
|
*/
|
|
395
423
|
__ensure_contact_data_capacity(n) {
|
|
396
424
|
if (this.contact_data.length < n) {
|
|
397
|
-
this.contact_data = new Uint32Array(
|
|
425
|
+
this.contact_data = new Uint32Array(ceilPowerOfTwo(n));
|
|
398
426
|
}
|
|
399
427
|
}
|
|
400
428
|
}
|
|
401
|
-
|
|
402
|
-
/**
|
|
403
|
-
* @param {number} n
|
|
404
|
-
* @returns {number}
|
|
405
|
-
*/
|
|
406
|
-
function next_pow2(n) {
|
|
407
|
-
if (n <= 1) return 1;
|
|
408
|
-
let p = 1;
|
|
409
|
-
while (p < n) p <<= 1;
|
|
410
|
-
return p;
|
|
411
|
-
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"box_box_manifold.d.ts","sourceRoot":"","sources":["../../../../../src/engine/physics/narrowphase/box_box_manifold.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"box_box_manifold.d.ts","sourceRoot":"","sources":["../../../../../src/engine/physics/narrowphase/box_box_manifold.js"],"names":[],"mappings":"AAqJA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,sCAvBW,MAAM,EAAE,GAAC,YAAY,MACrB,MAAM,MACN,MAAM,MACN,MAAM,OACN,MAAM,OACN,MAAM,OACN,MAAM,OACN,MAAM,OACN,MAAM,OACN,MAAM,OACN,MAAM,MACN,MAAM,MACN,MAAM,MACN,MAAM,OACN,MAAM,OACN,MAAM,OACN,MAAM,OACN,MAAM,OACN,MAAM,OACN,MAAM,OACN,MAAM,GACJ,OAAO,CA8anB;AA3hBD;;;GAGG;AACH,iCAFU,MAAM,CAEoD"}
|
|
@@ -39,6 +39,28 @@ const MAX_CONTACTS = 4;
|
|
|
39
39
|
const CONTACT_STRIDE = 7;
|
|
40
40
|
const PARALLEL_EPS_SQR = 1e-8;
|
|
41
41
|
|
|
42
|
+
/**
|
|
43
|
+
* Reference-axis tie-break deadband. SAT picks the minimum-overlap axis, but
|
|
44
|
+
* for aligned boxes two axes (A's face normal and B's face normal along the
|
|
45
|
+
* contact direction) have *equal* overlap, so floating-point noise from a
|
|
46
|
+
* sub-degree wobble flips which one wins frame to frame. That flips the
|
|
47
|
+
* reference/incident assignment, which reorders the contact points, which
|
|
48
|
+
* flips the solver's Gauss-Seidel sweep order — injecting an alternating
|
|
49
|
+
* bias that makes symmetric stacks creep and never sleep.
|
|
50
|
+
*
|
|
51
|
+
* The deadband makes a later axis replace the current best only if it
|
|
52
|
+
* reduces the overlap by more than `best · TIE_REL + TIE_ABS`. Axes are
|
|
53
|
+
* tested A-faces, then B-faces, then edge-crosses, so ties resolve to the
|
|
54
|
+
* earlier axis (A's face, then a face over an edge) — a stable, consistent
|
|
55
|
+
* choice. Genuine minima (overlap smaller by more than the band) still win,
|
|
56
|
+
* so SAT correctness for non-aligned boxes is unchanged; only the
|
|
57
|
+
* noise-driven flip on near-perfect alignment is suppressed. Box2D's
|
|
58
|
+
* `b2CollidePolygons` uses the same relative+absolute hysteresis.
|
|
59
|
+
* @type {number}
|
|
60
|
+
*/
|
|
61
|
+
const TIE_REL = 0.02;
|
|
62
|
+
const TIE_ABS = 1e-5;
|
|
63
|
+
|
|
42
64
|
/**
|
|
43
65
|
* Length of `out` required by {@link box_box_manifold}.
|
|
44
66
|
* @type {number}
|
|
@@ -182,7 +204,11 @@ export function box_box_manifold(
|
|
|
182
204
|
const dist = proj < 0 ? -proj : proj;
|
|
183
205
|
const overlap = (rA + rB) - dist;
|
|
184
206
|
if (overlap < 0) return true;
|
|
185
|
-
|
|
207
|
+
// Deadband: the first axis always wins; a later axis replaces it only
|
|
208
|
+
// if it reduces the overlap past the hysteresis band. This biases ties
|
|
209
|
+
// toward earlier-tested axes (A faces > B faces > edges) so aligned
|
|
210
|
+
// boxes pick a stable reference instead of flip-flopping on noise.
|
|
211
|
+
if (best_source === -1 || overlap < best_overlap - (best_overlap * TIE_REL + TIE_ABS)) {
|
|
186
212
|
best_overlap = overlap;
|
|
187
213
|
const sign = proj > 0 ? -1 : 1; // normal points B→A
|
|
188
214
|
best_nx = ux * sign;
|
|
@@ -14,4 +14,37 @@ export function narrowphase_step(pair_list: PairList, manifolds: ManifoldStore,
|
|
|
14
14
|
collider: Collider;
|
|
15
15
|
transform: Transform;
|
|
16
16
|
}>>): void;
|
|
17
|
+
/**
|
|
18
|
+
* Re-detect contact GEOMETRY for one existing manifold slot at the bodies'
|
|
19
|
+
* current poses, updating the witness points / normal / depth of the slot's
|
|
20
|
+
* existing contacts in place. Does NOT change the contact count, the
|
|
21
|
+
* feature ids, or the accumulated impulses — it only refreshes geometry.
|
|
22
|
+
*
|
|
23
|
+
* This is the per-substep concave path (TGS): for a contact pair involving a
|
|
24
|
+
* concave body, the contact *feature* (which triangle is deepest, and its
|
|
25
|
+
* normal) genuinely changes as the body rocks, so the solver's cheap analytic
|
|
26
|
+
* refresh — which freezes the feature for the whole outer step — pumps energy
|
|
27
|
+
* in. Re-running the narrowphase geometry each substep gives a fresh,
|
|
28
|
+
* correct normal so the body settles instead of rocking. Convex pairs keep
|
|
29
|
+
* the analytic refresh and never call this (their feature is stable).
|
|
30
|
+
*
|
|
31
|
+
* Matching is by feature id (stable per-triangle for the decomposition path),
|
|
32
|
+
* so a contact's geometry tracks the same triangle across substeps. A contact
|
|
33
|
+
* whose triangle isn't found this substep keeps its previous geometry (a rare
|
|
34
|
+
* transient; the once-per-frame {@link narrowphase_step} re-establishes the
|
|
35
|
+
* contact set next outer step). Count never changes here, so the solver's
|
|
36
|
+
* per-contact scratch (sized once at prepare) stays aligned.
|
|
37
|
+
*
|
|
38
|
+
* @param {ManifoldStore} manifolds
|
|
39
|
+
* @param {number} slot
|
|
40
|
+
* @param {Array<{collider: Collider, transform: Transform}>} list_a
|
|
41
|
+
* @param {Array<{collider: Collider, transform: Transform}>} list_b
|
|
42
|
+
*/
|
|
43
|
+
export function redetect_pair_geometry(manifolds: ManifoldStore, slot: number, list_a: Array<{
|
|
44
|
+
collider: Collider;
|
|
45
|
+
transform: Transform;
|
|
46
|
+
}>, list_b: Array<{
|
|
47
|
+
collider: Collider;
|
|
48
|
+
transform: Transform;
|
|
49
|
+
}>): void;
|
|
17
50
|
//# sourceMappingURL=narrowphase_step.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"narrowphase_step.d.ts","sourceRoot":"","sources":["../../../../../src/engine/physics/narrowphase/narrowphase_step.js"],"names":[],"mappings":"AA6uCA;;;;;;;;;;;GAWG;AACH,uFALW,MAAM,MAAM;IAAC,QAAQ,WAAW;IAAC,SAAS,YAAW;CAAC,CAAC,CAAC,QAyJlE"}
|
|
1
|
+
{"version":3,"file":"narrowphase_step.d.ts","sourceRoot":"","sources":["../../../../../src/engine/physics/narrowphase/narrowphase_step.js"],"names":[],"mappings":"AA6uCA;;;;;;;;;;;GAWG;AACH,uFALW,MAAM,MAAM;IAAC,QAAQ,WAAW;IAAC,SAAS,YAAW;CAAC,CAAC,CAAC,QAyJlE;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,uEAJW,MAAM,UACN,MAAM;IAAC,QAAQ,WAAW;IAAC,SAAS,YAAW;CAAC,CAAC,UACjD,MAAM;IAAC,QAAQ,WAAW;IAAC,SAAS,YAAW;CAAC,CAAC,QAiD3D"}
|
|
@@ -1420,3 +1420,78 @@ export function narrowphase_step(pair_list, manifolds, lists) {
|
|
|
1420
1420
|
}
|
|
1421
1421
|
}
|
|
1422
1422
|
}
|
|
1423
|
+
|
|
1424
|
+
/**
|
|
1425
|
+
* Re-detect contact GEOMETRY for one existing manifold slot at the bodies'
|
|
1426
|
+
* current poses, updating the witness points / normal / depth of the slot's
|
|
1427
|
+
* existing contacts in place. Does NOT change the contact count, the
|
|
1428
|
+
* feature ids, or the accumulated impulses — it only refreshes geometry.
|
|
1429
|
+
*
|
|
1430
|
+
* This is the per-substep concave path (TGS): for a contact pair involving a
|
|
1431
|
+
* concave body, the contact *feature* (which triangle is deepest, and its
|
|
1432
|
+
* normal) genuinely changes as the body rocks, so the solver's cheap analytic
|
|
1433
|
+
* refresh — which freezes the feature for the whole outer step — pumps energy
|
|
1434
|
+
* in. Re-running the narrowphase geometry each substep gives a fresh,
|
|
1435
|
+
* correct normal so the body settles instead of rocking. Convex pairs keep
|
|
1436
|
+
* the analytic refresh and never call this (their feature is stable).
|
|
1437
|
+
*
|
|
1438
|
+
* Matching is by feature id (stable per-triangle for the decomposition path),
|
|
1439
|
+
* so a contact's geometry tracks the same triangle across substeps. A contact
|
|
1440
|
+
* whose triangle isn't found this substep keeps its previous geometry (a rare
|
|
1441
|
+
* transient; the once-per-frame {@link narrowphase_step} re-establishes the
|
|
1442
|
+
* contact set next outer step). Count never changes here, so the solver's
|
|
1443
|
+
* per-contact scratch (sized once at prepare) stays aligned.
|
|
1444
|
+
*
|
|
1445
|
+
* @param {ManifoldStore} manifolds
|
|
1446
|
+
* @param {number} slot
|
|
1447
|
+
* @param {Array<{collider: Collider, transform: Transform}>} list_a
|
|
1448
|
+
* @param {Array<{collider: Collider, transform: Transform}>} list_b
|
|
1449
|
+
*/
|
|
1450
|
+
export function redetect_pair_geometry(manifolds, slot, list_a, list_b) {
|
|
1451
|
+
if (list_a === undefined || list_b === undefined) return;
|
|
1452
|
+
const la_len = list_a.length;
|
|
1453
|
+
const lb_len = list_b.length;
|
|
1454
|
+
if (la_len === 0 || lb_len === 0) return;
|
|
1455
|
+
|
|
1456
|
+
const count = manifolds.contact_count(slot);
|
|
1457
|
+
if (count === 0) return;
|
|
1458
|
+
|
|
1459
|
+
const gjk_axis_buf = manifolds.slot_axis_buffer;
|
|
1460
|
+
const gjk_axis_off = manifolds.slot_axis_offset(slot);
|
|
1461
|
+
|
|
1462
|
+
let cc = 0;
|
|
1463
|
+
for (let a = 0; a < la_len; a++) {
|
|
1464
|
+
const ea = list_a[a];
|
|
1465
|
+
for (let b = 0; b < lb_len; b++) {
|
|
1466
|
+
const eb = list_b[b];
|
|
1467
|
+
cc = dispatch_pair(cc, ea.collider, ea.transform, eb.collider, eb.transform,
|
|
1468
|
+
gjk_axis_buf, gjk_axis_off);
|
|
1469
|
+
}
|
|
1470
|
+
}
|
|
1471
|
+
if (cc === 0) return; // nothing re-detected this substep — keep frozen geometry
|
|
1472
|
+
|
|
1473
|
+
const data = manifolds.data_buffer;
|
|
1474
|
+
const slot_off = manifolds.slot_data_offset(slot);
|
|
1475
|
+
for (let j = 0; j < count; j++) {
|
|
1476
|
+
const off = slot_off + j * CONTACT_STRIDE;
|
|
1477
|
+
const fid = data[off + 13];
|
|
1478
|
+
if (fid === 0) continue; // no feature info to match on
|
|
1479
|
+
for (let k = 0; k < cc; k++) {
|
|
1480
|
+
const co = k * CANDIDATE_STRIDE;
|
|
1481
|
+
if (candidates[co + 10] === fid) {
|
|
1482
|
+
// Overwrite geometry only: witnesses, normal, depth.
|
|
1483
|
+
data[off] = candidates[co];
|
|
1484
|
+
data[off + 1] = candidates[co + 1];
|
|
1485
|
+
data[off + 2] = candidates[co + 2];
|
|
1486
|
+
data[off + 3] = candidates[co + 3];
|
|
1487
|
+
data[off + 4] = candidates[co + 4];
|
|
1488
|
+
data[off + 5] = candidates[co + 5];
|
|
1489
|
+
data[off + 6] = candidates[co + 6];
|
|
1490
|
+
data[off + 7] = candidates[co + 7];
|
|
1491
|
+
data[off + 8] = candidates[co + 8];
|
|
1492
|
+
data[off + 9] = candidates[co + 9];
|
|
1493
|
+
break;
|
|
1494
|
+
}
|
|
1495
|
+
}
|
|
1496
|
+
}
|
|
1497
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* # Local-frame ray ↔ primitive intersections (raycast narrowphase)
|
|
3
|
+
*
|
|
4
|
+
* Each function intersects a ray, **expressed in the shape's local frame**,
|
|
5
|
+
* against a canonical primitive at the origin (sphere at the origin; box
|
|
6
|
+
* axis-aligned spanning `[-h, +h]`; capsule along the local Y axis). They
|
|
7
|
+
* return the hit distance `t` along the ray (`Infinity` on a miss) and write
|
|
8
|
+
* the **local** outward surface normal (unit) into `outNormal[0..2]`.
|
|
9
|
+
*
|
|
10
|
+
* The ray-narrowphase dispatch transforms the world ray into the body's local
|
|
11
|
+
* frame once (rotate by the inverse body rotation — a unit direction stays
|
|
12
|
+
* unit, so `t` is preserved), calls the matching primitive, then rotates the
|
|
13
|
+
* returned local normal back to world. Keeping the primitives canonical lets
|
|
14
|
+
* the box and capsule tests be axis-aligned (cheap slab / cylinder math) and
|
|
15
|
+
* shares a single transform across them.
|
|
16
|
+
*
|
|
17
|
+
* Conventions:
|
|
18
|
+
* - the ray direction is unit length (the caller guarantees it);
|
|
19
|
+
* - the first surface crossing at or after the origin within `tMax` is
|
|
20
|
+
* returned; a ray starting inside the shape returns its exit crossing;
|
|
21
|
+
* - the normal is the geometric outward surface normal at the hit.
|
|
22
|
+
*
|
|
23
|
+
* @author Alex Goldring
|
|
24
|
+
* @copyright Company Named Limited (c) 2026
|
|
25
|
+
*/
|
|
26
|
+
/**
|
|
27
|
+
* Ray vs a sphere of radius `r` centred at the local origin.
|
|
28
|
+
*
|
|
29
|
+
* @param {Float64Array} outNormal length-3, written on hit
|
|
30
|
+
* @param {number} ox @param {number} oy @param {number} oz ray origin (local)
|
|
31
|
+
* @param {number} dx @param {number} dy @param {number} dz ray dir (local, unit)
|
|
32
|
+
* @param {number} tMax
|
|
33
|
+
* @param {number} r sphere radius
|
|
34
|
+
* @returns {number} hit distance, or `Infinity` on miss
|
|
35
|
+
*/
|
|
36
|
+
export function ray_sphere_local(outNormal: Float64Array, ox: number, oy: number, oz: number, dx: number, dy: number, dz: number, tMax: number, r: number): number;
|
|
37
|
+
/**
|
|
38
|
+
* Ray vs an axis-aligned box spanning `[-hx,hx] × [-hy,hy] × [-hz,hz]` at the
|
|
39
|
+
* local origin (the canonical pose of {@link BoxShape3D}). Slab method, with
|
|
40
|
+
* the entry (or, origin-inside, exit) face's outward normal.
|
|
41
|
+
*
|
|
42
|
+
* @param {Float64Array} outNormal length-3, written on hit
|
|
43
|
+
* @param {number} ox @param {number} oy @param {number} oz ray origin (local)
|
|
44
|
+
* @param {number} dx @param {number} dy @param {number} dz ray dir (local, unit)
|
|
45
|
+
* @param {number} tMax
|
|
46
|
+
* @param {number} hx @param {number} hy @param {number} hz half-extents
|
|
47
|
+
* @returns {number} hit distance, or `Infinity` on miss
|
|
48
|
+
*/
|
|
49
|
+
export function ray_box_local(outNormal: Float64Array, ox: number, oy: number, oz: number, dx: number, dy: number, dz: number, tMax: number, hx: number, hy: number, hz: number): number;
|
|
50
|
+
/**
|
|
51
|
+
* Ray vs a capsule along the local Y axis: a cylinder of radius `r` over
|
|
52
|
+
* `y ∈ [−hh, hh]` capped by hemispheres of radius `r` at `(0, ±hh, 0)` — the
|
|
53
|
+
* canonical pose of {@link CapsuleShape3D} (`hh = height/2`). Tests the
|
|
54
|
+
* infinite cylinder (clamped to the segment) and the two cap spheres, taking
|
|
55
|
+
* the nearest valid crossing.
|
|
56
|
+
*
|
|
57
|
+
* @param {Float64Array} outNormal length-3, written on hit
|
|
58
|
+
* @param {number} ox @param {number} oy @param {number} oz ray origin (local)
|
|
59
|
+
* @param {number} dx @param {number} dy @param {number} dz ray dir (local, unit)
|
|
60
|
+
* @param {number} tMax
|
|
61
|
+
* @param {number} r capsule radius
|
|
62
|
+
* @param {number} hh half-height of the cylindrical section (`height/2`)
|
|
63
|
+
* @returns {number} hit distance, or `Infinity` on miss
|
|
64
|
+
*/
|
|
65
|
+
export function ray_capsule_local(outNormal: Float64Array, ox: number, oy: number, oz: number, dx: number, dy: number, dz: number, tMax: number, r: number, hh: number): number;
|
|
66
|
+
//# sourceMappingURL=ray_shapes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ray_shapes.d.ts","sourceRoot":"","sources":["../../../../../src/engine/physics/narrowphase/ray_shapes.js"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH;;;;;;;;;GASG;AACH,4CAPW,YAAY,MACZ,MAAM,MAAa,MAAM,MAAa,MAAM,MAC5C,MAAM,MAAa,MAAM,MAAa,MAAM,QAC5C,MAAM,KACN,MAAM,GACJ,MAAM,CAiBlB;AAED;;;;;;;;;;;GAWG;AACH,yCAPW,YAAY,MACZ,MAAM,MAAa,MAAM,MAAa,MAAM,MAC5C,MAAM,MAAa,MAAM,MAAa,MAAM,QAC5C,MAAM,MACN,MAAM,MAAa,MAAM,MAAa,MAAM,GAC1C,MAAM,CA+ClB;AAED;;;;;;;;;;;;;;GAcG;AACH,6CARW,YAAY,MACZ,MAAM,MAAa,MAAM,MAAa,MAAM,MAC5C,MAAM,MAAa,MAAM,MAAa,MAAM,QAC5C,MAAM,KACN,MAAM,MACN,MAAM,GACJ,MAAM,CA6DlB"}
|