@woosh/meep-engine 2.147.0 → 2.149.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/topology/struct/binary/io/bt_mesh_bridge_islands.d.ts +23 -0
- package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_bridge_islands.d.ts.map +1 -0
- package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_bridge_islands.js +295 -0
- package/src/core/math/spline/spline3_hermite_intersection_spline3_hermite.d.ts +4 -4
- package/src/core/math/spline/spline3_hermite_intersection_spline3_hermite.d.ts.map +1 -1
- package/src/core/math/spline/spline3_hermite_intersection_spline3_hermite.js +48 -52
- package/src/core/math/spline/spline3_hermite_intersection_spline3_hermite_2d.d.ts +23 -21
- package/src/core/math/spline/spline3_hermite_intersection_spline3_hermite_2d.d.ts.map +1 -1
- package/src/core/math/spline/spline3_hermite_intersection_spline3_hermite_2d.js +41 -406
- package/src/core/math/spline/spline3_hermite_intersection_spline3_hermite_nd.d.ts +5 -4
- package/src/core/math/spline/spline3_hermite_intersection_spline3_hermite_nd.d.ts.map +1 -1
- package/src/core/math/spline/spline3_hermite_intersection_spline3_hermite_nd.js +400 -395
- package/src/engine/navigation/mesh/NavigationMesh.d.ts +6 -2
- package/src/engine/navigation/mesh/NavigationMesh.d.ts.map +1 -1
- package/src/engine/navigation/mesh/NavigationMesh.js +234 -212
- package/src/engine/navigation/mesh/bt_mesh_face_find_path.d.ts +7 -3
- package/src/engine/navigation/mesh/bt_mesh_face_find_path.d.ts.map +1 -1
- package/src/engine/navigation/mesh/bt_mesh_face_find_path.js +67 -73
- package/src/engine/navigation/mesh/build/enforce_agent_height_clearance.d.ts +16 -5
- package/src/engine/navigation/mesh/build/enforce_agent_height_clearance.d.ts.map +1 -1
- package/src/engine/navigation/mesh/build/enforce_agent_height_clearance.js +262 -147
- package/src/engine/navigation/mesh/build/navmesh_build_topology.d.ts.map +1 -1
- package/src/engine/navigation/mesh/build/navmesh_build_topology.js +33 -3
- package/src/engine/navigation/mesh/bvh_query_nearest_face.d.ts +4 -1
- package/src/engine/navigation/mesh/bvh_query_nearest_face.d.ts.map +1 -1
- package/src/engine/navigation/mesh/bvh_query_nearest_face.js +164 -131
- package/src/engine/physics/body/SolverBodyState.d.ts +142 -0
- package/src/engine/physics/body/SolverBodyState.d.ts.map +1 -0
- package/src/engine/physics/body/SolverBodyState.js +251 -0
- package/src/engine/physics/broadphase/generate_pairs.d.ts +2 -1
- package/src/engine/physics/broadphase/generate_pairs.d.ts.map +1 -1
- package/src/engine/physics/broadphase/generate_pairs.js +110 -108
- package/src/engine/physics/constraint/solve_constraints.d.ts.map +1 -1
- package/src/engine/physics/constraint/solve_constraints.js +691 -673
- package/src/engine/physics/ecs/PhysicsSystem.d.ts +21 -18
- package/src/engine/physics/ecs/PhysicsSystem.d.ts.map +1 -1
- package/src/engine/physics/ecs/PhysicsSystem.js +223 -91
- package/src/engine/physics/inertia/world_inverse_inertia.d.ts +23 -0
- package/src/engine/physics/inertia/world_inverse_inertia.d.ts.map +1 -1
- package/src/engine/physics/inertia/world_inverse_inertia.js +116 -77
- package/src/engine/physics/integration/integrate_position.d.ts +11 -1
- package/src/engine/physics/integration/integrate_position.d.ts.map +1 -1
- package/src/engine/physics/integration/integrate_position.js +97 -79
- package/src/engine/physics/integration/integrate_velocity.d.ts +12 -3
- package/src/engine/physics/integration/integrate_velocity.d.ts.map +1 -1
- package/src/engine/physics/integration/integrate_velocity.js +201 -160
- package/src/engine/physics/narrowphase/box_box_manifold.d.ts.map +1 -1
- package/src/engine/physics/narrowphase/box_box_manifold.js +750 -665
- package/src/engine/physics/narrowphase/box_triangle_contact.d.ts.map +1 -1
- package/src/engine/physics/narrowphase/box_triangle_contact.js +4 -34
- package/src/engine/physics/narrowphase/clip_against_axis_uv.d.ts +16 -0
- package/src/engine/physics/narrowphase/clip_against_axis_uv.d.ts.map +1 -0
- package/src/engine/physics/narrowphase/clip_against_axis_uv.js +49 -0
- package/src/engine/physics/narrowphase/narrowphase_step.d.ts.map +1 -1
- package/src/engine/physics/narrowphase/narrowphase_step.js +24 -3
- package/src/engine/physics/queries/raycast.d.ts.map +1 -1
- package/src/engine/physics/queries/raycast.js +201 -198
- package/src/engine/physics/solver/solve_contacts.d.ts +2 -2
- package/src/engine/physics/solver/solve_contacts.d.ts.map +1 -1
- package/src/engine/physics/solver/solve_contacts.js +1341 -1173
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"box_triangle_contact.d.ts","sourceRoot":"","sources":["../../../../../src/engine/physics/narrowphase/box_triangle_contact.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"box_triangle_contact.d.ts","sourceRoot":"","sources":["../../../../../src/engine/physics/narrowphase/box_triangle_contact.js"],"names":[],"mappings":"AAyLA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,0CAtBW,MAAM,EAAE,GAAC,YAAY,OACrB,MAAM,OACN,MAAM,OACN,MAAM,OACN,MAAM,OACN,MAAM,OACN,MAAM,OACN,MAAM,OACN,MAAM,OACN,MAAM,OACN,MAAM,MACN,MAAM,MACN,MAAM,MACN,MAAM,MACN,MAAM,MACN,MAAM,MACN,MAAM,MACN,MAAM,MACN,MAAM,MACN,MAAM,GACJ,OAAO,CAuJnB;AAvSD;;;GAGG;AACH,sCAFU,MAAM,CAEyD"}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
line3_closest_points_segment_segment
|
|
3
|
+
} from "../../../core/geom/3d/line/line3_closest_points_segment_segment.js";
|
|
2
4
|
import { quat3_to_matrix3 } from "../../../core/geom/3d/quaternion/quat3_to_matrix3.js";
|
|
5
|
+
import { clip_against_axis_uv } from "./clip_against_axis_uv.js";
|
|
3
6
|
|
|
4
7
|
/**
|
|
5
8
|
* Multi-point manifold construction for an oriented box vs. a triangle.
|
|
@@ -97,39 +100,6 @@ function projected_box_half_extent(axes, hx, hy, hz, ux, uy, uz) {
|
|
|
97
100
|
+ (pz < 0 ? -pz : pz) * hz;
|
|
98
101
|
}
|
|
99
102
|
|
|
100
|
-
/**
|
|
101
|
-
* Sutherland-Hodgman clip of `points_in` against the axis-aligned
|
|
102
|
-
* half-plane `coord_idx ≤ bound` (or `coord_idx ≥ bound` when
|
|
103
|
-
* `keep_below` is false). 2D stride 2; result to `points_out`.
|
|
104
|
-
* Returns surviving vertex count.
|
|
105
|
-
*/
|
|
106
|
-
function clip_against_axis_uv(points_in, point_count, points_out, coord_idx, bound, keep_below) {
|
|
107
|
-
let out_count = 0;
|
|
108
|
-
for (let i = 0; i < point_count; i++) {
|
|
109
|
-
const j = (i + 1) % point_count;
|
|
110
|
-
const ax = points_in[i * 2], ay = points_in[i * 2 + 1];
|
|
111
|
-
const bx = points_in[j * 2], by = points_in[j * 2 + 1];
|
|
112
|
-
const av = coord_idx === 0 ? ax : ay;
|
|
113
|
-
const bv = coord_idx === 0 ? bx : by;
|
|
114
|
-
const a_inside = keep_below ? (av <= bound) : (av >= bound);
|
|
115
|
-
const b_inside = keep_below ? (bv <= bound) : (bv >= bound);
|
|
116
|
-
|
|
117
|
-
if (a_inside) {
|
|
118
|
-
points_out[out_count * 2] = ax;
|
|
119
|
-
points_out[out_count * 2 + 1] = ay;
|
|
120
|
-
out_count++;
|
|
121
|
-
}
|
|
122
|
-
if (a_inside !== b_inside) {
|
|
123
|
-
const denom = bv - av;
|
|
124
|
-
const t = denom !== 0 ? (bound - av) / denom : 0;
|
|
125
|
-
points_out[out_count * 2] = ax + (bx - ax) * t;
|
|
126
|
-
points_out[out_count * 2 + 1] = ay + (by - ay) * t;
|
|
127
|
-
out_count++;
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
return out_count;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
103
|
/**
|
|
134
104
|
* Sutherland-Hodgman clip of `points_in` against the general half-plane
|
|
135
105
|
* `(p - (line_ox, line_oy)) · (line_nx, line_ny) ≤ 0`. 2D stride 2;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sutherland-Hodgman clip of `points_in` against the axis-aligned
|
|
3
|
+
* half-plane `coord_idx ≤ bound` (or `coord_idx ≥ bound` when
|
|
4
|
+
* `keep_below` is false). 2D stride 2; result to `points_out`.
|
|
5
|
+
* Returns surviving vertex count.
|
|
6
|
+
*
|
|
7
|
+
* @param {Float64Array} points_in
|
|
8
|
+
* @param {number} point_count
|
|
9
|
+
* @param {Float64Array} points_out
|
|
10
|
+
* @param {number} coord_idx 0 = u, 1 = v
|
|
11
|
+
* @param {number} bound
|
|
12
|
+
* @param {boolean} keep_below true to keep points with coord ≤ bound
|
|
13
|
+
* @returns {number} surviving vertex count
|
|
14
|
+
*/
|
|
15
|
+
export function clip_against_axis_uv(points_in: Float64Array, point_count: number, points_out: Float64Array, coord_idx: number, bound: number, keep_below: boolean): number;
|
|
16
|
+
//# sourceMappingURL=clip_against_axis_uv.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clip_against_axis_uv.d.ts","sourceRoot":"","sources":["../../../../../src/engine/physics/narrowphase/clip_against_axis_uv.js"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,gDARW,YAAY,eACZ,MAAM,cACN,YAAY,aACZ,MAAM,SACN,MAAM,cACN,OAAO,GACL,MAAM,CAoClB"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sutherland-Hodgman clip of `points_in` against the axis-aligned
|
|
3
|
+
* half-plane `coord_idx ≤ bound` (or `coord_idx ≥ bound` when
|
|
4
|
+
* `keep_below` is false). 2D stride 2; result to `points_out`.
|
|
5
|
+
* Returns surviving vertex count.
|
|
6
|
+
*
|
|
7
|
+
* @param {Float64Array} points_in
|
|
8
|
+
* @param {number} point_count
|
|
9
|
+
* @param {Float64Array} points_out
|
|
10
|
+
* @param {number} coord_idx 0 = u, 1 = v
|
|
11
|
+
* @param {number} bound
|
|
12
|
+
* @param {boolean} keep_below true to keep points with coord ≤ bound
|
|
13
|
+
* @returns {number} surviving vertex count
|
|
14
|
+
*/
|
|
15
|
+
export function clip_against_axis_uv(points_in, point_count, points_out, coord_idx, bound, keep_below) {
|
|
16
|
+
let out_count = 0;
|
|
17
|
+
|
|
18
|
+
for (let i = 0; i < point_count; i++) {
|
|
19
|
+
const j = (i + 1) % point_count;
|
|
20
|
+
|
|
21
|
+
const ax = points_in[i * 2], ay = points_in[i * 2 + 1];
|
|
22
|
+
const bx = points_in[j * 2], by = points_in[j * 2 + 1];
|
|
23
|
+
|
|
24
|
+
const av = coord_idx === 0 ? ax : ay;
|
|
25
|
+
const bv = coord_idx === 0 ? bx : by;
|
|
26
|
+
|
|
27
|
+
const a_inside = keep_below ? (av <= bound) : (av >= bound);
|
|
28
|
+
const b_inside = keep_below ? (bv <= bound) : (bv >= bound);
|
|
29
|
+
|
|
30
|
+
if (a_inside) {
|
|
31
|
+
points_out[out_count * 2] = ax;
|
|
32
|
+
points_out[out_count * 2 + 1] = ay;
|
|
33
|
+
out_count++;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (a_inside !== b_inside) {
|
|
37
|
+
|
|
38
|
+
const denom = bv - av;
|
|
39
|
+
const t = denom !== 0 ? (bound - av) / denom : 0;
|
|
40
|
+
|
|
41
|
+
points_out[out_count * 2] = ax + (bx - ax) * t;
|
|
42
|
+
points_out[out_count * 2 + 1] = ay + (by - ay) * t;
|
|
43
|
+
|
|
44
|
+
out_count++;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return out_count;
|
|
49
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"narrowphase_step.d.ts","sourceRoot":"","sources":["../../../../../src/engine/physics/narrowphase/narrowphase_step.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"narrowphase_step.d.ts","sourceRoot":"","sources":["../../../../../src/engine/physics/narrowphase/narrowphase_step.js"],"names":[],"mappings":"AA8zCA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,qDAVW,YAAY,GAAC,MAAM,EAAE,iCAGrB;IAAC,CAAC,EAAC,MAAM,CAAC;IAAA,CAAC,EAAC,MAAM,CAAC;IAAA,CAAC,EAAC,MAAM,CAAA;CAAC,QAC5B;IAAC,CAAC,EAAC,MAAM,CAAC;IAAA,CAAC,EAAC,MAAM,CAAC;IAAA,CAAC,EAAC,MAAM,CAAC;IAAA,CAAC,EAAC,MAAM,CAAA;CAAC,iCAErC;IAAC,CAAC,EAAC,MAAM,CAAC;IAAA,CAAC,EAAC,MAAM,CAAC;IAAA,CAAC,EAAC,MAAM,CAAA;CAAC,QAC5B;IAAC,CAAC,EAAC,MAAM,CAAC;IAAA,CAAC,EAAC,MAAM,CAAC;IAAA,CAAC,EAAC,MAAM,CAAC;IAAA,CAAC,EAAC,MAAM,CAAA;CAAC,GACnC,MAAM,CAyClB;AAED;;;;;;;;;;;GAWG;AACH,uFALW,MAAM,MAAM;IAAC,QAAQ,WAAW;IAAC,SAAS,YAAW;CAAC,CAAC,CAAC,QA4KlE;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,QAkH3D"}
|
|
@@ -403,9 +403,12 @@ function dispatch_pair(count, colA, trA, colB, trB, gjk_axis_buf = null, gjk_axi
|
|
|
403
403
|
// `deepest_pair_penetration` query passes bare `{shape}` adapters with no
|
|
404
404
|
// material fields — it never writes to a manifold, so 0 is fine there.
|
|
405
405
|
const fa = colA.friction, fb = colB.friction;
|
|
406
|
+
|
|
406
407
|
if (fa !== undefined && fb !== undefined) {
|
|
408
|
+
|
|
407
409
|
g_pair_friction = combine_friction(fa, fb);
|
|
408
410
|
g_pair_restitution = combine_restitution(colA.restitution, colB.restitution);
|
|
411
|
+
|
|
409
412
|
} else {
|
|
410
413
|
g_pair_friction = 0;
|
|
411
414
|
g_pair_restitution = 0;
|
|
@@ -1429,22 +1432,29 @@ export function narrowphase_step(pair_list, manifolds, lists) {
|
|
|
1429
1432
|
for (let i = 0; i < count; i++) {
|
|
1430
1433
|
const idA = pair_list.get_a(i);
|
|
1431
1434
|
const idB = pair_list.get_b(i);
|
|
1435
|
+
|
|
1432
1436
|
const idxA = body_id_index(idA);
|
|
1433
1437
|
const idxB = body_id_index(idB);
|
|
1434
1438
|
|
|
1435
1439
|
const list_a = lists[idxA];
|
|
1436
1440
|
const list_b = lists[idxB];
|
|
1441
|
+
|
|
1437
1442
|
const slot = manifolds.find(idA, idB);
|
|
1438
1443
|
|
|
1439
1444
|
if (list_a === undefined || list_b === undefined
|
|
1440
|
-
|| list_a.length === 0 || list_b.length === 0
|
|
1445
|
+
|| list_a.length === 0 || list_b.length === 0
|
|
1446
|
+
) {
|
|
1447
|
+
|
|
1441
1448
|
manifolds.clear_contacts(slot);
|
|
1442
1449
|
continue;
|
|
1450
|
+
|
|
1443
1451
|
}
|
|
1444
1452
|
|
|
1445
1453
|
let cand_count = 0;
|
|
1454
|
+
|
|
1446
1455
|
const la_len = list_a.length;
|
|
1447
1456
|
const lb_len = list_b.length;
|
|
1457
|
+
|
|
1448
1458
|
// Per-manifold cached GJK separating-axis seed. Threaded into
|
|
1449
1459
|
// dispatch_pair for the GJK fallback paths. For single-collider
|
|
1450
1460
|
// bodies (the common case) there's no contention across the
|
|
@@ -1453,12 +1463,23 @@ export function narrowphase_step(pair_list, manifolds, lists) {
|
|
|
1453
1463
|
// seed (better than a cold (1, 0, 0) restart every frame).
|
|
1454
1464
|
const gjk_axis_buf = manifolds.slot_axis_buffer;
|
|
1455
1465
|
const gjk_axis_off = manifolds.slot_axis_offset(slot);
|
|
1466
|
+
|
|
1456
1467
|
for (let a = 0; a < la_len; a++) {
|
|
1457
1468
|
const ea = list_a[a];
|
|
1469
|
+
|
|
1458
1470
|
for (let b = 0; b < lb_len; b++) {
|
|
1459
1471
|
const eb = list_b[b];
|
|
1460
|
-
|
|
1461
|
-
|
|
1472
|
+
|
|
1473
|
+
cand_count = dispatch_pair(
|
|
1474
|
+
cand_count,
|
|
1475
|
+
ea.collider,
|
|
1476
|
+
ea.transform,
|
|
1477
|
+
eb.collider,
|
|
1478
|
+
eb.transform,
|
|
1479
|
+
gjk_axis_buf,
|
|
1480
|
+
gjk_axis_off
|
|
1481
|
+
);
|
|
1482
|
+
|
|
1462
1483
|
}
|
|
1463
1484
|
}
|
|
1464
1485
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"raycast.d.ts","sourceRoot":"","sources":["../../../../../src/engine/physics/queries/raycast.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"raycast.d.ts","sourceRoot":"","sources":["../../../../../src/engine/physics/queries/raycast.js"],"names":[],"mappings":"AA8IA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,yGAJmB,MAAM,yBAAsB,OAAO,GAEzC,OAAO,CAwCnB"}
|
|
@@ -1,198 +1,201 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
} from "../../../core/
|
|
9
|
-
import { returnTrue } from "../../../core/function/returnTrue.js";
|
|
10
|
-
import {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
*
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
const
|
|
37
|
-
const
|
|
38
|
-
const
|
|
39
|
-
const
|
|
40
|
-
const
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
* @param {
|
|
61
|
-
* @param {
|
|
62
|
-
* @param {number}
|
|
63
|
-
* @param {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
if (
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
const
|
|
108
|
-
const
|
|
109
|
-
if (
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
*
|
|
145
|
-
*
|
|
146
|
-
*
|
|
147
|
-
*
|
|
148
|
-
*
|
|
149
|
-
*
|
|
150
|
-
*
|
|
151
|
-
*
|
|
152
|
-
*
|
|
153
|
-
*
|
|
154
|
-
*
|
|
155
|
-
*
|
|
156
|
-
* @param {
|
|
157
|
-
*
|
|
158
|
-
* @
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
if (
|
|
187
|
-
|
|
188
|
-
const
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
1
|
+
import {
|
|
2
|
+
COLUMN_CHILD_1,
|
|
3
|
+
COLUMN_CHILD_2,
|
|
4
|
+
COLUMN_USER_DATA,
|
|
5
|
+
ELEMENT_WORD_COUNT,
|
|
6
|
+
NULL_NODE,
|
|
7
|
+
} from "../../../core/bvh2/bvh3/BVH.js";
|
|
8
|
+
import { SCRATCH_UINT32_TRAVERSAL_STACK } from "../../../core/collection/SCRATCH_UINT32_TRAVERSAL_STACK.js";
|
|
9
|
+
import { returnTrue } from "../../../core/function/returnTrue.js";
|
|
10
|
+
import {
|
|
11
|
+
aabb3_near_distance_to_intersection_ray_segment
|
|
12
|
+
} from "../../../core/geom/3d/aabb/aabb3_near_distance_to_intersection_ray_segment.js";
|
|
13
|
+
import { body_id_index } from "../body/BodyStorage.js";
|
|
14
|
+
import { RAY_REFINE_UNSUPPORTED, refine_ray_hit } from "../narrowphase/refine_ray_hit.js";
|
|
15
|
+
|
|
16
|
+
const stack = SCRATCH_UINT32_TRAVERSAL_STACK;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Reusable nearest-hit accumulator + scratch. Module-scoped so {@link raycast}
|
|
20
|
+
* doesn't allocate per call; the physics step is single-threaded so contention
|
|
21
|
+
* isn't a concern.
|
|
22
|
+
* @type {{best_t:number, best_body:number}}
|
|
23
|
+
*/
|
|
24
|
+
const acc = { best_t: Infinity, best_body: 0 };
|
|
25
|
+
const best_normal = new Float64Array(3); // winning hit's world normal
|
|
26
|
+
const cand_normal = new Float64Array(3); // per-leaf candidate normal
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Outward AABB-face normal at a hit point — the fallback normal for a leaf
|
|
30
|
+
* whose shape has no exact ray test (composite convex). The hit point is
|
|
31
|
+
* projected into the AABB's normalised local space (centre = 0, faces = ±1);
|
|
32
|
+
* the dominant component's axis is the entry face and its sign the outward
|
|
33
|
+
* normal.
|
|
34
|
+
*/
|
|
35
|
+
function aabb_face_normal(out, min_x, min_y, min_z, max_x, max_y, max_z, hx, hy, hz) {
|
|
36
|
+
const cx = (min_x + max_x) * 0.5;
|
|
37
|
+
const cy = (min_y + max_y) * 0.5;
|
|
38
|
+
const cz = (min_z + max_z) * 0.5;
|
|
39
|
+
const px = (hx - cx) * 2 / (max_x - min_x);
|
|
40
|
+
const py = (hy - cy) * 2 / (max_y - min_y);
|
|
41
|
+
const pz = (hz - cz) * 2 / (max_z - min_z);
|
|
42
|
+
const apx = px < 0 ? -px : px;
|
|
43
|
+
const apy = py < 0 ? -py : py;
|
|
44
|
+
const apz = pz < 0 ? -pz : pz;
|
|
45
|
+
if (apx >= apy && apx >= apz) { out[0] = px >= 0 ? 1 : -1; out[1] = 0; out[2] = 0; }
|
|
46
|
+
else if (apy >= apz) { out[0] = 0; out[1] = py >= 0 ? 1 : -1; out[2] = 0; }
|
|
47
|
+
else { out[0] = 0; out[1] = 0; out[2] = pz >= 0 ? 1 : -1; }
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Walk a single BVH along a ray, refining each crossing leaf against its true
|
|
52
|
+
* shape geometry and keeping the nearest confirmed hit.
|
|
53
|
+
*
|
|
54
|
+
* Pruning stays on the *inflated leaf AABB* entry distance `t_near`: a shape
|
|
55
|
+
* hit is always at or beyond its tight AABB entry, which is at or beyond the
|
|
56
|
+
* inflated-AABB entry, so a subtree whose AABB entry is past the current best
|
|
57
|
+
* cannot contain a closer hit. A leaf whose ray crosses the fat AABB but misses
|
|
58
|
+
* the true shape contributes nothing — the correctness gain over broadphase.
|
|
59
|
+
*
|
|
60
|
+
* @param {BVH} bvh @param {number} root
|
|
61
|
+
* @param {PhysicsSystem} system
|
|
62
|
+
* @param {number} ox @param {number} oy @param {number} oz ray origin
|
|
63
|
+
* @param {number} dx @param {number} dy @param {number} dz ray dir (unit)
|
|
64
|
+
* @param {number} inv_dx @param {number} inv_dy @param {number} inv_dz
|
|
65
|
+
* @param {number} max_distance
|
|
66
|
+
* @param {(entity:number, collider:Collider)=>boolean} filter
|
|
67
|
+
*/
|
|
68
|
+
function bvh_raycast_nearest(
|
|
69
|
+
bvh, root, system,
|
|
70
|
+
ox, oy, oz, dx, dy, dz,
|
|
71
|
+
inv_dx, inv_dy, inv_dz,
|
|
72
|
+
max_distance, filter
|
|
73
|
+
) {
|
|
74
|
+
if (root === NULL_NODE) return;
|
|
75
|
+
|
|
76
|
+
const float32 = bvh.__data_float32;
|
|
77
|
+
const uint32 = bvh.__data_uint32;
|
|
78
|
+
|
|
79
|
+
let pointer = stack.pointer;
|
|
80
|
+
const stack_top = pointer;
|
|
81
|
+
stack[pointer++] = root;
|
|
82
|
+
|
|
83
|
+
while (pointer > stack_top) {
|
|
84
|
+
pointer--;
|
|
85
|
+
const node = stack[pointer];
|
|
86
|
+
const address = node * ELEMENT_WORD_COUNT;
|
|
87
|
+
|
|
88
|
+
const t_near = aabb3_near_distance_to_intersection_ray_segment(
|
|
89
|
+
float32[address], float32[address + 1], float32[address + 2],
|
|
90
|
+
float32[address + 3], float32[address + 4], float32[address + 5],
|
|
91
|
+
ox, oy, oz,
|
|
92
|
+
inv_dx, inv_dy, inv_dz,
|
|
93
|
+
0, max_distance
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
// No intersection or this subtree can't beat the best known hit — prune.
|
|
97
|
+
if (t_near >= acc.best_t) continue;
|
|
98
|
+
|
|
99
|
+
const child_1 = uint32[address + COLUMN_CHILD_1];
|
|
100
|
+
if (child_1 !== NULL_NODE) {
|
|
101
|
+
stack[pointer++] = uint32[address + COLUMN_CHILD_2];
|
|
102
|
+
stack[pointer++] = child_1;
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Leaf — refine against the true shape.
|
|
107
|
+
const body_id = uint32[address + COLUMN_USER_DATA];
|
|
108
|
+
const entity = system.entityOf(body_id);
|
|
109
|
+
if (entity < 0) continue; // unlinked concurrently
|
|
110
|
+
const idx = body_id_index(body_id);
|
|
111
|
+
const collider = system.__primary_collider(idx);
|
|
112
|
+
if (collider === null) continue;
|
|
113
|
+
if (!filter(entity, collider)) continue;
|
|
114
|
+
|
|
115
|
+
const tr = system.__transforms[idx];
|
|
116
|
+
const refined = refine_ray_hit(
|
|
117
|
+
collider.shape, tr.position, tr.rotation,
|
|
118
|
+
ox, oy, oz, dx, dy, dz, acc.best_t, cand_normal
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
if (refined === RAY_REFINE_UNSUPPORTED) {
|
|
122
|
+
// No exact ray test for this shape — keep the broadphase AABB hit.
|
|
123
|
+
if (t_near < acc.best_t) {
|
|
124
|
+
acc.best_t = t_near;
|
|
125
|
+
acc.best_body = body_id;
|
|
126
|
+
aabb_face_normal(best_normal,
|
|
127
|
+
float32[address], float32[address + 1], float32[address + 2],
|
|
128
|
+
float32[address + 3], float32[address + 4], float32[address + 5],
|
|
129
|
+
ox + dx * t_near, oy + dy * t_near, oz + dz * t_near);
|
|
130
|
+
}
|
|
131
|
+
} else if (refined < acc.best_t) { // a refined miss is Infinity → never wins
|
|
132
|
+
acc.best_t = refined;
|
|
133
|
+
acc.best_body = body_id;
|
|
134
|
+
best_normal[0] = cand_normal[0];
|
|
135
|
+
best_normal[1] = cand_normal[1];
|
|
136
|
+
best_normal[2] = cand_normal[2];
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
stack.pointer = stack_top;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Raycast against both broadphase trees (static + dynamic) of a
|
|
145
|
+
* {@link PhysicsSystem}, refined against each candidate's true shape geometry.
|
|
146
|
+
* Fills `result` with the nearest hit and returns `true` on hit, `false` on
|
|
147
|
+
* miss. `result.t` is the exact surface distance and `result.normal` the true
|
|
148
|
+
* surface normal for sphere / box / capsule / mesh / heightmap colliders;
|
|
149
|
+
* composite convex shapes (no exact ray test yet) fall back to the broadphase
|
|
150
|
+
* AABB hit + AABB-face normal.
|
|
151
|
+
*
|
|
152
|
+
* Multi-collider bodies resolve their primary (first-attached) collider — the
|
|
153
|
+
* BVH leaf encodes only `body_id`; per-collider rays need the leaf user-data to
|
|
154
|
+
* carry the collider index (future work).
|
|
155
|
+
*
|
|
156
|
+
* @param {PhysicsSystem} system
|
|
157
|
+
* @param {Ray3} ray origin + unit direction + `tMax`
|
|
158
|
+
* @param {PhysicsSurfacePoint} result populated on hit; untouched on miss
|
|
159
|
+
* @param {(entity:number, collider:Collider)=>boolean} [filter] called once per
|
|
160
|
+
* crossing leaf; defaults to {@link returnTrue}.
|
|
161
|
+
* @returns {boolean} true on hit, false on miss
|
|
162
|
+
*/
|
|
163
|
+
export function raycast(system, ray, result, filter = returnTrue) {
|
|
164
|
+
const ox = ray.origin_x, oy = ray.origin_y, oz = ray.origin_z;
|
|
165
|
+
const dx = ray.direction_x, dy = ray.direction_y, dz = ray.direction_z;
|
|
166
|
+
const max_distance = ray.tMax;
|
|
167
|
+
|
|
168
|
+
acc.best_t = max_distance;
|
|
169
|
+
acc.best_body = 0;
|
|
170
|
+
best_normal[0] = 0; best_normal[1] = 0; best_normal[2] = 0;
|
|
171
|
+
|
|
172
|
+
const inv_dx = 1 / dx, inv_dy = 1 / dy, inv_dz = 1 / dz;
|
|
173
|
+
|
|
174
|
+
bvh_raycast_nearest(
|
|
175
|
+
system.staticBvh, system.staticBvh.root, system,
|
|
176
|
+
ox, oy, oz, dx, dy, dz, inv_dx, inv_dy, inv_dz, max_distance, filter
|
|
177
|
+
);
|
|
178
|
+
bvh_raycast_nearest(
|
|
179
|
+
system.dynamicBvh, system.dynamicBvh.root, system,
|
|
180
|
+
ox, oy, oz, dx, dy, dz, inv_dx, inv_dy, inv_dz, max_distance, filter
|
|
181
|
+
);
|
|
182
|
+
|
|
183
|
+
// Any hit updates best_t to strictly below max_distance (both the refined
|
|
184
|
+
// and AABB-fallback paths require t < current best); a miss leaves it at
|
|
185
|
+
// max_distance. This is body_id-agnostic — the first body packs to id 0.
|
|
186
|
+
if (acc.best_t >= max_distance) return false;
|
|
187
|
+
|
|
188
|
+
const entity = system.entityOf(acc.best_body);
|
|
189
|
+
if (entity < 0) return false; // body unlinked concurrently → treat as miss
|
|
190
|
+
|
|
191
|
+
const t = acc.best_t;
|
|
192
|
+
const rp = result.position;
|
|
193
|
+
const rn = result.normal;
|
|
194
|
+
rp[0] = ox + dx * t; rp[1] = oy + dy * t; rp[2] = oz + dz * t;
|
|
195
|
+
rn[0] = best_normal[0]; rn[1] = best_normal[1]; rn[2] = best_normal[2];
|
|
196
|
+
result.t = t;
|
|
197
|
+
result.entity = entity;
|
|
198
|
+
result.body_id = acc.best_body;
|
|
199
|
+
|
|
200
|
+
return true;
|
|
201
|
+
}
|
|
@@ -48,9 +48,9 @@ export function warm_start_contacts(manifolds: ManifoldStore, system: PhysicsSys
|
|
|
48
48
|
* for the small per-step rotation), so they are not recomputed here.
|
|
49
49
|
*
|
|
50
50
|
* @param {ManifoldStore} manifolds
|
|
51
|
-
* @param {
|
|
51
|
+
* @param {Transform[]} transforms
|
|
52
52
|
*/
|
|
53
|
-
export function refresh_contacts(manifolds: ManifoldStore,
|
|
53
|
+
export function refresh_contacts(manifolds: ManifoldStore, transforms: Transform[]): void;
|
|
54
54
|
/**
|
|
55
55
|
* Stage 2b (per substep) — the concave counterpart of {@link refresh_contacts}.
|
|
56
56
|
*
|