@woosh/meep-engine 2.153.0 → 2.155.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/shape/ConvexHullShape3D.d.ts +112 -0
- package/src/core/geom/3d/shape/ConvexHullShape3D.d.ts.map +1 -0
- package/src/core/geom/3d/shape/ConvexHullShape3D.js +325 -0
- package/src/core/geom/vec3/v3_array_copy.d.ts +3 -3
- package/src/core/geom/vec3/v3_array_copy.d.ts.map +1 -1
- package/src/core/geom/vec3/v3_array_copy.js +2 -2
- package/src/core/geom/vec3/v3_cross.d.ts +17 -0
- package/src/core/geom/vec3/v3_cross.d.ts.map +1 -0
- package/src/core/geom/vec3/v3_cross.js +20 -0
- package/src/core/geom/vec3/v3_subtract.d.ts +16 -0
- package/src/core/geom/vec3/v3_subtract.d.ts.map +1 -0
- package/src/core/geom/vec3/v3_subtract.js +19 -0
- package/src/engine/graphics/ecs/decal/v2/FPDecalSystem.d.ts.map +1 -1
- package/src/engine/graphics/ecs/decal/v2/FPDecalSystem.js +8 -0
- package/src/engine/graphics/ecs/trail2d/Trail2D.d.ts +4 -0
- package/src/engine/graphics/ecs/trail2d/Trail2D.d.ts.map +1 -1
- package/src/engine/graphics/ecs/trail2d/Trail2D.js +21 -0
- package/src/engine/physics/PLAN.md +4 -4
- package/src/engine/physics/body/BodyStorage.d.ts +3 -1
- package/src/engine/physics/body/BodyStorage.d.ts.map +1 -1
- package/src/engine/physics/body/BodyStorage.js +452 -450
- package/src/engine/physics/body/SolverBodyState.d.ts.map +1 -1
- package/src/engine/physics/body/SolverBodyState.js +6 -5
- package/src/engine/physics/broadphase/generate_pairs.d.ts.map +1 -1
- package/src/engine/physics/broadphase/generate_pairs.js +9 -1
- package/src/engine/physics/ccd/linear_sweep.d.ts.map +1 -1
- package/src/engine/physics/ccd/linear_sweep.js +237 -238
- package/src/engine/physics/computeInterceptPoint.d.ts.map +1 -1
- package/src/engine/physics/computeInterceptPoint.js +8 -3
- package/src/engine/physics/contact/ManifoldStore.d.ts +0 -16
- package/src/engine/physics/contact/ManifoldStore.d.ts.map +1 -1
- package/src/engine/physics/contact/ManifoldStore.js +1 -38
- package/src/engine/physics/ecs/BodyKind.d.ts +3 -2
- package/src/engine/physics/ecs/BodyKind.d.ts.map +1 -1
- package/src/engine/physics/ecs/BodyKind.js +25 -24
- package/src/engine/physics/ecs/PhysicsEvents.d.ts +4 -5
- package/src/engine/physics/ecs/PhysicsEvents.d.ts.map +1 -1
- package/src/engine/physics/ecs/PhysicsEvents.js +15 -16
- package/src/engine/physics/ecs/PhysicsSystem.d.ts +5 -30
- package/src/engine/physics/ecs/PhysicsSystem.d.ts.map +1 -1
- package/src/engine/physics/ecs/PhysicsSystem.js +13 -45
- package/src/engine/physics/ecs/RigidBodySerializationAdapter.d.ts.map +1 -1
- package/src/engine/physics/ecs/RigidBodySerializationAdapter.js +85 -81
- package/src/engine/physics/ecs/is_sensor.d.ts +18 -0
- package/src/engine/physics/ecs/is_sensor.d.ts.map +1 -0
- package/src/engine/physics/ecs/is_sensor.js +27 -0
- package/src/engine/physics/events/ContactEventBuffer.d.ts +2 -1
- package/src/engine/physics/events/ContactEventBuffer.d.ts.map +1 -1
- package/src/engine/physics/events/ContactEventBuffer.js +84 -83
- package/src/engine/physics/gjk/gjk.d.ts +0 -26
- package/src/engine/physics/gjk/gjk.d.ts.map +1 -1
- package/src/engine/physics/gjk/gjk.js +3 -52
- package/src/engine/physics/gjk/gjk_epa_penetration.d.ts +20 -0
- package/src/engine/physics/gjk/gjk_epa_penetration.d.ts.map +1 -0
- package/src/engine/physics/gjk/gjk_epa_penetration.js +548 -0
- package/src/engine/physics/gjk/minkowski_support.d.ts +4 -9
- package/src/engine/physics/gjk/minkowski_support.d.ts.map +1 -1
- package/src/engine/physics/gjk/minkowski_support.js +70 -75
- package/src/engine/physics/gjk/mpr.d.ts +1 -1
- package/src/engine/physics/gjk/mpr.d.ts.map +1 -1
- package/src/engine/physics/gjk/mpr.js +362 -344
- package/src/engine/physics/island/IslandBuilder.d.ts.map +1 -1
- package/src/engine/physics/island/IslandBuilder.js +431 -428
- package/src/engine/physics/narrowphase/box_box_manifold.d.ts.map +1 -1
- package/src/engine/physics/narrowphase/box_box_manifold.js +4 -81
- package/src/engine/physics/narrowphase/box_triangle_contact.d.ts.map +1 -1
- package/src/engine/physics/narrowphase/box_triangle_contact.js +4 -39
- package/src/engine/physics/narrowphase/capsule_contacts.d.ts.map +1 -1
- package/src/engine/physics/narrowphase/capsule_contacts.js +459 -462
- package/src/engine/physics/narrowphase/clip_against_axis_uv.d.ts.map +1 -1
- package/src/engine/physics/narrowphase/clip_against_axis_uv.js +4 -1
- package/src/engine/physics/narrowphase/convex_convex_manifold.d.ts +83 -0
- package/src/engine/physics/narrowphase/convex_convex_manifold.d.ts.map +1 -0
- package/src/engine/physics/narrowphase/convex_convex_manifold.js +425 -0
- package/src/engine/physics/narrowphase/convex_decomposition.d.ts +32 -0
- package/src/engine/physics/narrowphase/convex_decomposition.d.ts.map +1 -0
- package/src/engine/physics/narrowphase/convex_decomposition.js +293 -0
- package/src/engine/physics/narrowphase/mesh_convex_hull.d.ts +41 -0
- package/src/engine/physics/narrowphase/mesh_convex_hull.d.ts.map +1 -0
- package/src/engine/physics/narrowphase/mesh_convex_hull.js +106 -0
- package/src/engine/physics/narrowphase/mesh_mesh_tet_manifold.d.ts +8 -0
- package/src/engine/physics/narrowphase/mesh_mesh_tet_manifold.d.ts.map +1 -0
- package/src/engine/physics/narrowphase/mesh_mesh_tet_manifold.js +117 -0
- package/src/engine/physics/narrowphase/narrowphase_step.d.ts.map +1 -1
- package/src/engine/physics/narrowphase/narrowphase_step.js +105 -102
- package/src/engine/physics/narrowphase/reduce_manifold_contacts.d.ts +29 -0
- package/src/engine/physics/narrowphase/reduce_manifold_contacts.d.ts.map +1 -0
- package/src/engine/physics/narrowphase/reduce_manifold_contacts.js +69 -0
- package/src/engine/physics/narrowphase/refine_ray_concave.d.ts.map +1 -1
- package/src/engine/physics/narrowphase/refine_ray_concave.js +152 -145
- package/src/engine/physics/narrowphase/sphere_box_contact.d.ts.map +1 -1
- package/src/engine/physics/narrowphase/sphere_box_contact.js +132 -123
- package/src/engine/physics/queries/overlap_shape.d.ts.map +1 -1
- package/src/engine/physics/queries/overlap_shape.js +16 -17
- package/src/engine/physics/queries/raycast.d.ts +5 -0
- package/src/engine/physics/queries/raycast.d.ts.map +1 -1
- package/src/engine/physics/queries/raycast.js +16 -8
- package/src/engine/physics/queries/shape_cast.d.ts.map +1 -1
- package/src/engine/physics/queries/shape_cast.js +13 -7
- package/src/engine/physics/solver/solve_contacts.d.ts.map +1 -1
- package/src/engine/physics/solver/solve_contacts.js +8 -11
- package/src/engine/physics/vehicle/RaycastVehicle.d.ts.map +1 -1
- package/src/engine/physics/vehicle/RaycastVehicle.js +339 -333
- package/src/engine/physics/gjk/expanding_polytope_algorithm.d.ts +0 -13
- package/src/engine/physics/gjk/expanding_polytope_algorithm.d.ts.map +0 -1
- package/src/engine/physics/gjk/expanding_polytope_algorithm.js +0 -399
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
import { BVH } from "../../../core/bvh2/bvh3/BVH.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Greedy convex decomposition of a tetrahedralised mesh into a small set of
|
|
5
|
+
* convex pieces, each with coplanar-merged polygon faces — the representation
|
|
6
|
+
* the contact clipper ({@link convex_convex_manifold}) consumes.
|
|
7
|
+
*
|
|
8
|
+
* Why: colliding two meshes as their raw tetrahedra means a SAT+clip per
|
|
9
|
+
* contacting tet pair. Merging adjacent tets into larger convex pieces collapses
|
|
10
|
+
* a convex sub-region (e.g. a box) to a SINGLE piece, cutting both the pair
|
|
11
|
+
* count and the per-pair work — the path to large tet counts.
|
|
12
|
+
*
|
|
13
|
+
* Pipeline:
|
|
14
|
+
* 1. Build face → tets adjacency from shared faces (sorted vertex triples).
|
|
15
|
+
* 2. Greedy region-grow: seed a piece from a tet, BFS-absorb face-neighbours
|
|
16
|
+
* while the union stays convex. Convexity test is definitive: every vertex
|
|
17
|
+
* of the candidate union lies behind (or on) every boundary-face plane.
|
|
18
|
+
* 3. Per piece: XOR the member tets' faces to get the boundary triangles
|
|
19
|
+
* (outward-oriented), coplanar-merge them into polygon faces by walking the
|
|
20
|
+
* directed boundary edges, and localise the vertex indices.
|
|
21
|
+
* 4. Build a BVH over the pieces' local AABBs for piece-pair culling.
|
|
22
|
+
*
|
|
23
|
+
* Output (cached on the MeshShape3D): `{ pieces, bvh }` where each piece is
|
|
24
|
+
* `{ vertices, face_offsets, face_loops, aabb }` — directly clippable.
|
|
25
|
+
*
|
|
26
|
+
* @author Alex Goldring
|
|
27
|
+
* @copyright Company Named Limited (c) 2026
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
// Quantisation for grouping coplanar faces (normal + plane offset).
|
|
31
|
+
const PLANE_NORMAL_TOL = 1e-4;
|
|
32
|
+
const PLANE_OFFSET_TOL = 1e-4;
|
|
33
|
+
|
|
34
|
+
// A tet face by index: the 3 face-vertex slots + the opposite-vertex slot.
|
|
35
|
+
// Exported so the convex-mesh fast path (mesh_convex_hull.js) shares the same
|
|
36
|
+
// tet-face convention rather than keeping a parallel copy.
|
|
37
|
+
export const FACE_OPP = [[1, 2, 3, 0], [0, 2, 3, 1], [0, 1, 3, 2], [0, 1, 2, 3]];
|
|
38
|
+
|
|
39
|
+
export function face_key(a, b, c) {
|
|
40
|
+
// Exact, collision-free order-independent key for a triangle's vertex triple.
|
|
41
|
+
let x = a, y = b, z = c, t;
|
|
42
|
+
if (x > y) { t = x; x = y; y = t; }
|
|
43
|
+
if (y > z) { t = y; y = z; z = t; }
|
|
44
|
+
if (x > y) { t = x; x = y; y = t; }
|
|
45
|
+
return x + "," + y + "," + z;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* @param {{tet_mesh, tet_positions, compute_bounding_box}} meshShape
|
|
50
|
+
* @returns {{pieces: Array<{vertices:Float32Array, face_offsets:Uint32Array, face_loops:Uint32Array, aabb:Float64Array}>, bvh: BVH}}
|
|
51
|
+
*/
|
|
52
|
+
export function build_convex_decomposition(meshShape) {
|
|
53
|
+
const tm = meshShape.tet_mesh;
|
|
54
|
+
const tp = meshShape.tet_positions;
|
|
55
|
+
const N = tm.count;
|
|
56
|
+
|
|
57
|
+
// Scale-relative epsilon for the convexity test.
|
|
58
|
+
const bb = new Float64Array(6);
|
|
59
|
+
meshShape.compute_bounding_box(bb);
|
|
60
|
+
const diag = Math.hypot(bb[3] - bb[0], bb[4] - bb[1], bb[5] - bb[2]) || 1;
|
|
61
|
+
const EPS = 1e-6 * diag;
|
|
62
|
+
|
|
63
|
+
// Tet vertices (global indices) and the 4 faces (each: 3 face verts + the
|
|
64
|
+
// opposite vertex, for outward orientation).
|
|
65
|
+
const tetV = new Int32Array(N * 4);
|
|
66
|
+
for (let t = 0; t < N; t++) {
|
|
67
|
+
for (let i = 0; i < 4; i++) tetV[t * 4 + i] = tm.getVertexIndex(t, i);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Adjacency: face-key → list of (tet) owners. Two tets sharing a face are
|
|
71
|
+
// neighbours. Exact triple stored alongside to resolve hash collisions.
|
|
72
|
+
const faceOwners = new Map(); // key → array of { tet, fi }
|
|
73
|
+
for (let t = 0; t < N; t++) {
|
|
74
|
+
for (let f = 0; f < 4; f++) {
|
|
75
|
+
const o = FACE_OPP[f];
|
|
76
|
+
const a = tetV[t * 4 + o[0]], b = tetV[t * 4 + o[1]], c = tetV[t * 4 + o[2]];
|
|
77
|
+
const k = face_key(a, b, c);
|
|
78
|
+
let arr = faceOwners.get(k);
|
|
79
|
+
if (arr === undefined) { arr = []; faceOwners.set(k, arr); }
|
|
80
|
+
arr.push({ tet: t, fi: f, a, b, c });
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
// tet → neighbour tets (sharing a face).
|
|
84
|
+
const neighbours = (t) => {
|
|
85
|
+
const out = [];
|
|
86
|
+
for (let f = 0; f < 4; f++) {
|
|
87
|
+
const o = FACE_OPP[f];
|
|
88
|
+
const a = tetV[t * 4 + o[0]], b = tetV[t * 4 + o[1]], c = tetV[t * 4 + o[2]];
|
|
89
|
+
const arr = faceOwners.get(face_key(a, b, c));
|
|
90
|
+
if (arr === undefined) continue;
|
|
91
|
+
for (const e of arr) if (e.tet !== t) out.push(e.tet);
|
|
92
|
+
}
|
|
93
|
+
return out;
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
const visited = new Uint8Array(N);
|
|
97
|
+
const pieces = [];
|
|
98
|
+
|
|
99
|
+
// Reusable boundary structure for the growing piece: key → oriented face.
|
|
100
|
+
for (let seed = 0; seed < N; seed++) {
|
|
101
|
+
if (visited[seed]) continue;
|
|
102
|
+
|
|
103
|
+
const boundary = new Map(); // key → {a,b,c (oriented), nx,ny,nz}
|
|
104
|
+
const pieceVerts = new Set();
|
|
105
|
+
const pieceTets = [];
|
|
106
|
+
|
|
107
|
+
// Toggle a tet's 4 faces against the boundary (its own inverse): a face
|
|
108
|
+
// shared with a piece tet cancels (becomes interior). Store just the
|
|
109
|
+
// triple — orientation is resolved later from the piece centroid.
|
|
110
|
+
const toggle_tet_faces = (t) => {
|
|
111
|
+
for (let f = 0; f < 4; f++) {
|
|
112
|
+
const o = FACE_OPP[f];
|
|
113
|
+
const a = tetV[t * 4 + o[0]], b = tetV[t * 4 + o[1]], c = tetV[t * 4 + o[2]];
|
|
114
|
+
const k = face_key(a, b, c);
|
|
115
|
+
if (boundary.has(k)) boundary.delete(k);
|
|
116
|
+
else boundary.set(k, { a, b, c });
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
const add_verts = (t) => { for (let i = 0; i < 4; i++) pieceVerts.add(tetV[t * 4 + i]); };
|
|
120
|
+
|
|
121
|
+
// Convex iff every boundary-face PLANE is supporting: all vertices lie
|
|
122
|
+
// on one side of it. Orientation-free — no need to know which side is
|
|
123
|
+
// outward, so it's immune to sliver/orientation issues.
|
|
124
|
+
const convex_with = (candVerts) => {
|
|
125
|
+
for (const [, fc] of boundary) {
|
|
126
|
+
const ax = tp[fc.a * 3], ay = tp[fc.a * 3 + 1], az = tp[fc.a * 3 + 2];
|
|
127
|
+
const e1x = tp[fc.b * 3] - ax, e1y = tp[fc.b * 3 + 1] - ay, e1z = tp[fc.b * 3 + 2] - az;
|
|
128
|
+
const e2x = tp[fc.c * 3] - ax, e2y = tp[fc.c * 3 + 1] - ay, e2z = tp[fc.c * 3 + 2] - az;
|
|
129
|
+
let nx = e1y * e2z - e1z * e2y, ny = e1z * e2x - e1x * e2z, nz = e1x * e2y - e1y * e2x;
|
|
130
|
+
const len = Math.sqrt(nx * nx + ny * ny + nz * nz) || 1;
|
|
131
|
+
nx /= len; ny /= len; nz /= len;
|
|
132
|
+
let pos = false, neg = false;
|
|
133
|
+
for (const v of pieceVerts) {
|
|
134
|
+
const d = nx * (tp[v * 3] - ax) + ny * (tp[v * 3 + 1] - ay) + nz * (tp[v * 3 + 2] - az);
|
|
135
|
+
if (d > EPS) pos = true; else if (d < -EPS) neg = true;
|
|
136
|
+
if (pos && neg) return false;
|
|
137
|
+
}
|
|
138
|
+
for (let i = 0; i < 4; i++) {
|
|
139
|
+
const v = candVerts[i];
|
|
140
|
+
const d = nx * (tp[v * 3] - ax) + ny * (tp[v * 3 + 1] - ay) + nz * (tp[v * 3 + 2] - az);
|
|
141
|
+
if (d > EPS) pos = true; else if (d < -EPS) neg = true;
|
|
142
|
+
if (pos && neg) return false;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return true;
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
visited[seed] = 1;
|
|
149
|
+
pieceTets.push(seed);
|
|
150
|
+
toggle_tet_faces(seed);
|
|
151
|
+
add_verts(seed);
|
|
152
|
+
|
|
153
|
+
const tried = new Set([seed]);
|
|
154
|
+
const frontier = neighbours(seed);
|
|
155
|
+
for (const n of frontier) tried.add(n);
|
|
156
|
+
|
|
157
|
+
while (frontier.length > 0) {
|
|
158
|
+
const t = frontier.pop();
|
|
159
|
+
if (visited[t]) continue;
|
|
160
|
+
|
|
161
|
+
// Tentatively add t: toggle its faces into the boundary, test
|
|
162
|
+
// convexity, then commit or revert (toggle is its own inverse).
|
|
163
|
+
const cand = [tetV[t * 4], tetV[t * 4 + 1], tetV[t * 4 + 2], tetV[t * 4 + 3]];
|
|
164
|
+
toggle_tet_faces(t);
|
|
165
|
+
if (convex_with(cand)) {
|
|
166
|
+
visited[t] = 1;
|
|
167
|
+
pieceTets.push(t);
|
|
168
|
+
add_verts(t);
|
|
169
|
+
for (const n of neighbours(t)) {
|
|
170
|
+
if (!visited[n] && !tried.has(n)) { tried.add(n); frontier.push(n); }
|
|
171
|
+
}
|
|
172
|
+
} else {
|
|
173
|
+
toggle_tet_faces(t); // revert
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
pieces.push(build_piece(tp, boundary));
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// BVH over piece local AABBs.
|
|
181
|
+
const bvh = new BVH();
|
|
182
|
+
bvh.release_all();
|
|
183
|
+
bvh.node_capacity = Math.max(1, pieces.length * 2 - 1);
|
|
184
|
+
for (let i = 0; i < pieces.length; i++) {
|
|
185
|
+
const node = bvh.allocate_node();
|
|
186
|
+
const a = pieces[i].aabb;
|
|
187
|
+
bvh.node_set_aabb_primitive(node, a[0], a[1], a[2], a[3], a[4], a[5]);
|
|
188
|
+
bvh.node_set_user_data(node, i);
|
|
189
|
+
bvh.insert_leaf(node);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
return { pieces, bvh };
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/** Build a clippable piece from its boundary triangles: coplanar-merge into
|
|
196
|
+
* polygon faces and localise vertex indices. `boundary` is a Map keyed by
|
|
197
|
+
* face_key → {a,b,c} (any winding; orientation is re-derived from the piece
|
|
198
|
+
* centroid). Exported so the convex-mesh fast path can build a single
|
|
199
|
+
* coplanar-merged hull from a whole mesh's boundary. */
|
|
200
|
+
export function build_piece(tp, boundary) {
|
|
201
|
+
// Piece centroid (from boundary vertices) — strictly interior for a convex
|
|
202
|
+
// piece, so it orients every face normal outward unambiguously.
|
|
203
|
+
const cset = new Set();
|
|
204
|
+
for (const [, fc] of boundary) { cset.add(fc.a); cset.add(fc.b); cset.add(fc.c); }
|
|
205
|
+
let pcx = 0, pcy = 0, pcz = 0;
|
|
206
|
+
for (const v of cset) { pcx += tp[v * 3]; pcy += tp[v * 3 + 1]; pcz += tp[v * 3 + 2]; }
|
|
207
|
+
const pn = cset.size || 1; pcx /= pn; pcy /= pn; pcz /= pn;
|
|
208
|
+
|
|
209
|
+
// Group boundary triangles by plane; each normal oriented away from the centroid.
|
|
210
|
+
const groups = []; // { nx,ny,nz, off, tris: [[a,b,c], ...] }
|
|
211
|
+
for (const [, fc] of boundary) {
|
|
212
|
+
const ax = tp[fc.a * 3], ay = tp[fc.a * 3 + 1], az = tp[fc.a * 3 + 2];
|
|
213
|
+
const e1x = tp[fc.b * 3] - ax, e1y = tp[fc.b * 3 + 1] - ay, e1z = tp[fc.b * 3 + 2] - az;
|
|
214
|
+
const e2x = tp[fc.c * 3] - ax, e2y = tp[fc.c * 3 + 1] - ay, e2z = tp[fc.c * 3 + 2] - az;
|
|
215
|
+
let nx = e1y * e2z - e1z * e2y, ny = e1z * e2x - e1x * e2z, nz = e1x * e2y - e1y * e2x;
|
|
216
|
+
const len = Math.hypot(nx, ny, nz) || 1;
|
|
217
|
+
nx /= len; ny /= len; nz /= len;
|
|
218
|
+
if (nx * (ax - pcx) + ny * (ay - pcy) + nz * (az - pcz) < 0) { nx = -nx; ny = -ny; nz = -nz; }
|
|
219
|
+
const off = nx * ax + ny * ay + nz * az;
|
|
220
|
+
let g = null;
|
|
221
|
+
for (const cand of groups) {
|
|
222
|
+
if (Math.abs(cand.nx * nx + cand.ny * ny + cand.nz * nz - 1) < PLANE_NORMAL_TOL
|
|
223
|
+
&& Math.abs(cand.off - off) < PLANE_OFFSET_TOL) { g = cand; break; }
|
|
224
|
+
}
|
|
225
|
+
if (g === null) { g = { nx, ny, nz, off, tris: [] }; groups.push(g); }
|
|
226
|
+
g.tris.push([fc.a, fc.b, fc.c]);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// Per group: each face of a convex piece is a convex polygon, so order its
|
|
230
|
+
// (boundary) vertices by angle around the face centroid in the plane, CCW
|
|
231
|
+
// w.r.t. the known outward normal. Robust regardless of triangulation.
|
|
232
|
+
const loops = []; // arrays of global vertex indices, CCW outward
|
|
233
|
+
for (const g of groups) {
|
|
234
|
+
const vset = new Set();
|
|
235
|
+
for (const [a, b, c] of g.tris) { vset.add(a); vset.add(b); vset.add(c); }
|
|
236
|
+
if (vset.size < 3) continue;
|
|
237
|
+
const varr = Array.from(vset);
|
|
238
|
+
|
|
239
|
+
let cx = 0, cy = 0, cz = 0;
|
|
240
|
+
for (const gv of varr) { cx += tp[gv * 3]; cy += tp[gv * 3 + 1]; cz += tp[gv * 3 + 2]; }
|
|
241
|
+
const inv = 1 / varr.length; cx *= inv; cy *= inv; cz *= inv;
|
|
242
|
+
|
|
243
|
+
// In-plane basis (u, v=n×u) from the outward normal n.
|
|
244
|
+
const nx = g.nx, ny = g.ny, nz = g.nz;
|
|
245
|
+
let ux, uy, uz;
|
|
246
|
+
const ax = Math.abs(nx), ay = Math.abs(ny), az = Math.abs(nz);
|
|
247
|
+
if (ax <= ay && ax <= az) { ux = 0; uy = -nz; uz = ny; }
|
|
248
|
+
else if (ay <= az) { ux = -nz; uy = 0; uz = nx; }
|
|
249
|
+
else { ux = -ny; uy = nx; uz = 0; }
|
|
250
|
+
const ul = Math.hypot(ux, uy, uz) || 1; ux /= ul; uy /= ul; uz /= ul;
|
|
251
|
+
const vx = ny * uz - nz * uy, vy = nz * ux - nx * uz, vz = nx * uy - ny * ux;
|
|
252
|
+
|
|
253
|
+
const items = varr.map((gv) => {
|
|
254
|
+
const dx = tp[gv * 3] - cx, dy = tp[gv * 3 + 1] - cy, dz = tp[gv * 3 + 2] - cz;
|
|
255
|
+
return { gv, ang: Math.atan2(dx * vx + dy * vy + dz * vz, dx * ux + dy * uy + dz * uz) };
|
|
256
|
+
});
|
|
257
|
+
items.sort((p, q) => p.ang - q.ang);
|
|
258
|
+
loops.push(items.map((it) => it.gv));
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// Localise vertices used by the loops.
|
|
262
|
+
const remap = new Map();
|
|
263
|
+
const verts = [];
|
|
264
|
+
const local_of = (g) => {
|
|
265
|
+
let l = remap.get(g);
|
|
266
|
+
if (l === undefined) {
|
|
267
|
+
l = verts.length / 3;
|
|
268
|
+
verts.push(tp[g * 3], tp[g * 3 + 1], tp[g * 3 + 2]);
|
|
269
|
+
remap.set(g, l);
|
|
270
|
+
}
|
|
271
|
+
return l;
|
|
272
|
+
};
|
|
273
|
+
const face_offsets = new Uint32Array(loops.length + 1);
|
|
274
|
+
const loop_flat = [];
|
|
275
|
+
for (let i = 0; i < loops.length; i++) {
|
|
276
|
+
face_offsets[i] = loop_flat.length;
|
|
277
|
+
for (const g of loops[i]) loop_flat.push(local_of(g));
|
|
278
|
+
}
|
|
279
|
+
face_offsets[loops.length] = loop_flat.length;
|
|
280
|
+
|
|
281
|
+
const vertices = new Float32Array(verts);
|
|
282
|
+
const aabb = new Float64Array(6);
|
|
283
|
+
let mnx = Infinity, mny = Infinity, mnz = Infinity, mxx = -Infinity, mxy = -Infinity, mxz = -Infinity;
|
|
284
|
+
for (let i = 0; i < vertices.length / 3; i++) {
|
|
285
|
+
const x = vertices[i * 3], y = vertices[i * 3 + 1], z = vertices[i * 3 + 2];
|
|
286
|
+
if (x < mnx) mnx = x; if (x > mxx) mxx = x;
|
|
287
|
+
if (y < mny) mny = y; if (y > mxy) mxy = y;
|
|
288
|
+
if (z < mnz) mnz = z; if (z > mxz) mxz = z;
|
|
289
|
+
}
|
|
290
|
+
aabb[0] = mnx; aabb[1] = mny; aabb[2] = mnz; aabb[3] = mxx; aabb[4] = mxy; aabb[5] = mxz;
|
|
291
|
+
|
|
292
|
+
return { vertices, face_offsets, face_loops: new Uint32Array(loop_flat), aabb };
|
|
293
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Convex fast path for a triangulated {@link MeshShape3D}: if the whole mesh is
|
|
3
|
+
* convex, represent it as a single coplanar-merged convex piece so it collides
|
|
4
|
+
* via one GJK + EPA + face-clip manifold (O(support queries)) instead of being
|
|
5
|
+
* greedily decomposed into many small SAT pieces.
|
|
6
|
+
*
|
|
7
|
+
* Why this matters: the greedy convex decomposition fragments even a convex mesh
|
|
8
|
+
* (a partial union of tets is generally NOT convex, so the order-dependent grow
|
|
9
|
+
* can't reconstruct the single convex region — a curved convex surface splits
|
|
10
|
+
* into dozens of pieces). Detecting global convexity and emitting ONE piece
|
|
11
|
+
* collapses that to a single collider, the path to large convex meshes.
|
|
12
|
+
*
|
|
13
|
+
* The piece is built by {@link build_piece} from the mesh's whole boundary, so
|
|
14
|
+
* its faces are coplanar-MERGED polygons (a box → 6 quads, not 12 triangles) —
|
|
15
|
+
* essential for the clipper to produce a full face-on-face patch rather than
|
|
16
|
+
* triangle slivers. A `support()` is attached (linear scan over the piece's
|
|
17
|
+
* localised surface vertices, so it stays O(surface), not O(all tets)).
|
|
18
|
+
*
|
|
19
|
+
* Cached on the (immutable) shape as `__convex_hull`: the piece if convex, or
|
|
20
|
+
* `null` if concave. Returned verbatim on subsequent calls.
|
|
21
|
+
*
|
|
22
|
+
* @author Alex Goldring
|
|
23
|
+
* @copyright Company Named Limited (c) 2026
|
|
24
|
+
*/
|
|
25
|
+
/**
|
|
26
|
+
* @param {{tet_mesh, tet_positions, compute_bounding_box}} meshShape
|
|
27
|
+
* @returns {{vertices:Float32Array, face_offsets:Uint32Array, face_loops:Uint32Array, aabb:Float64Array, support:Function}|null}
|
|
28
|
+
* a single coplanar-merged convex piece (with support) if the mesh is convex, else null
|
|
29
|
+
*/
|
|
30
|
+
export function get_mesh_convex_hull(meshShape: {
|
|
31
|
+
tet_mesh;
|
|
32
|
+
tet_positions;
|
|
33
|
+
compute_bounding_box;
|
|
34
|
+
}): {
|
|
35
|
+
vertices: Float32Array;
|
|
36
|
+
face_offsets: Uint32Array;
|
|
37
|
+
face_loops: Uint32Array;
|
|
38
|
+
aabb: Float64Array;
|
|
39
|
+
support: Function;
|
|
40
|
+
} | null;
|
|
41
|
+
//# sourceMappingURL=mesh_convex_hull.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mesh_convex_hull.d.ts","sourceRoot":"","sources":["../../../../../src/engine/physics/narrowphase/mesh_convex_hull.js"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH;;;;GAIG;AACH,gDAJW;IAAC,QAAQ,CAAC;IAAC,aAAa,CAAC;IAAC,oBAAoB,CAAA;CAAC,GAC7C;IAAC,QAAQ,EAAC,YAAY,CAAC;IAAC,YAAY,EAAC,WAAW,CAAC;IAAC,UAAU,EAAC,WAAW,CAAC;IAAC,IAAI,EAAC,YAAY,CAAC;IAAC,OAAO,WAAS;CAAC,GAAC,IAAI,CAQ/H"}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { build_piece, face_key, FACE_OPP } from "./convex_decomposition.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Convex fast path for a triangulated {@link MeshShape3D}: if the whole mesh is
|
|
5
|
+
* convex, represent it as a single coplanar-merged convex piece so it collides
|
|
6
|
+
* via one GJK + EPA + face-clip manifold (O(support queries)) instead of being
|
|
7
|
+
* greedily decomposed into many small SAT pieces.
|
|
8
|
+
*
|
|
9
|
+
* Why this matters: the greedy convex decomposition fragments even a convex mesh
|
|
10
|
+
* (a partial union of tets is generally NOT convex, so the order-dependent grow
|
|
11
|
+
* can't reconstruct the single convex region — a curved convex surface splits
|
|
12
|
+
* into dozens of pieces). Detecting global convexity and emitting ONE piece
|
|
13
|
+
* collapses that to a single collider, the path to large convex meshes.
|
|
14
|
+
*
|
|
15
|
+
* The piece is built by {@link build_piece} from the mesh's whole boundary, so
|
|
16
|
+
* its faces are coplanar-MERGED polygons (a box → 6 quads, not 12 triangles) —
|
|
17
|
+
* essential for the clipper to produce a full face-on-face patch rather than
|
|
18
|
+
* triangle slivers. A `support()` is attached (linear scan over the piece's
|
|
19
|
+
* localised surface vertices, so it stays O(surface), not O(all tets)).
|
|
20
|
+
*
|
|
21
|
+
* Cached on the (immutable) shape as `__convex_hull`: the piece if convex, or
|
|
22
|
+
* `null` if concave. Returned verbatim on subsequent calls.
|
|
23
|
+
*
|
|
24
|
+
* @author Alex Goldring
|
|
25
|
+
* @copyright Company Named Limited (c) 2026
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* @param {{tet_mesh, tet_positions, compute_bounding_box}} meshShape
|
|
30
|
+
* @returns {{vertices:Float32Array, face_offsets:Uint32Array, face_loops:Uint32Array, aabb:Float64Array, support:Function}|null}
|
|
31
|
+
* a single coplanar-merged convex piece (with support) if the mesh is convex, else null
|
|
32
|
+
*/
|
|
33
|
+
export function get_mesh_convex_hull(meshShape) {
|
|
34
|
+
if (meshShape.__convex_hull !== undefined) return meshShape.__convex_hull;
|
|
35
|
+
const hull = build_if_convex(meshShape);
|
|
36
|
+
meshShape.__convex_hull = hull;
|
|
37
|
+
return hull;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function build_if_convex(meshShape) {
|
|
41
|
+
const tm = meshShape.tet_mesh;
|
|
42
|
+
const tp = meshShape.tet_positions;
|
|
43
|
+
const N = tm.count;
|
|
44
|
+
if (N === 0) return null;
|
|
45
|
+
|
|
46
|
+
const bb = new Float64Array(6);
|
|
47
|
+
meshShape.compute_bounding_box(bb);
|
|
48
|
+
const diag = Math.hypot(bb[3] - bb[0], bb[4] - bb[1], bb[5] - bb[2]) || 1;
|
|
49
|
+
const EPS = 1e-6 * diag;
|
|
50
|
+
|
|
51
|
+
// Boundary faces = those owned by exactly one tet. Keep the owning tet's
|
|
52
|
+
// opposite vertex to orient the face outward for the convexity test.
|
|
53
|
+
const owners = new Map(); // key → { a, b, c, opp, count }
|
|
54
|
+
for (let t = 0; t < N; t++) {
|
|
55
|
+
for (let f = 0; f < 4; f++) {
|
|
56
|
+
const o = FACE_OPP[f];
|
|
57
|
+
const a = tm.getVertexIndex(t, o[0]);
|
|
58
|
+
const b = tm.getVertexIndex(t, o[1]);
|
|
59
|
+
const c = tm.getVertexIndex(t, o[2]);
|
|
60
|
+
const opp = tm.getVertexIndex(t, o[3]);
|
|
61
|
+
const k = face_key(a, b, c);
|
|
62
|
+
const e = owners.get(k);
|
|
63
|
+
if (e === undefined) owners.set(k, { a, b, c, opp, count: 1 });
|
|
64
|
+
else e.count++;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Convexity: every vertex behind (or on) every boundary-face plane.
|
|
69
|
+
const nVerts = tp.length / 3;
|
|
70
|
+
const boundary = new Map(); // key → {a,b,c} for build_piece
|
|
71
|
+
let boundaryCount = 0;
|
|
72
|
+
for (const [k, fc] of owners) {
|
|
73
|
+
if (fc.count !== 1) continue;
|
|
74
|
+
boundaryCount++;
|
|
75
|
+
const ax = tp[fc.a * 3], ay = tp[fc.a * 3 + 1], az = tp[fc.a * 3 + 2];
|
|
76
|
+
const e1x = tp[fc.b * 3] - ax, e1y = tp[fc.b * 3 + 1] - ay, e1z = tp[fc.b * 3 + 2] - az;
|
|
77
|
+
const e2x = tp[fc.c * 3] - ax, e2y = tp[fc.c * 3 + 1] - ay, e2z = tp[fc.c * 3 + 2] - az;
|
|
78
|
+
let nx = e1y * e2z - e1z * e2y, ny = e1z * e2x - e1x * e2z, nz = e1x * e2y - e1y * e2x;
|
|
79
|
+
const ox = tp[fc.opp * 3] - ax, oy = tp[fc.opp * 3 + 1] - ay, oz = tp[fc.opp * 3 + 2] - az;
|
|
80
|
+
if (nx * ox + ny * oy + nz * oz > 0) { nx = -nx; ny = -ny; nz = -nz; } // outward
|
|
81
|
+
const len = Math.sqrt(nx * nx + ny * ny + nz * nz) || 1;
|
|
82
|
+
const ix = nx / len, iy = ny / len, iz = nz / len;
|
|
83
|
+
for (let v = 0; v < nVerts; v++) {
|
|
84
|
+
const d = ix * (tp[v * 3] - ax) + iy * (tp[v * 3 + 1] - ay) + iz * (tp[v * 3 + 2] - az);
|
|
85
|
+
if (d > EPS) return null; // a vertex in front of a face → concave
|
|
86
|
+
}
|
|
87
|
+
boundary.set(k, { a: fc.a, b: fc.b, c: fc.c });
|
|
88
|
+
}
|
|
89
|
+
if (boundaryCount < 4) return null; // not a closed volume
|
|
90
|
+
|
|
91
|
+
// Coplanar-merged hull piece + a linear-scan support over its local verts.
|
|
92
|
+
const piece = build_piece(tp, boundary);
|
|
93
|
+
const v = piece.vertices;
|
|
94
|
+
const vc = v.length / 3;
|
|
95
|
+
piece.support = function (result, result_offset, dx, dy, dz) {
|
|
96
|
+
let bi = 0, best = -Infinity;
|
|
97
|
+
for (let i = 0; i < vc; i++) {
|
|
98
|
+
const d = dx * v[i * 3] + dy * v[i * 3 + 1] + dz * v[i * 3 + 2];
|
|
99
|
+
if (d > best) { best = d; bi = i; }
|
|
100
|
+
}
|
|
101
|
+
result[result_offset] = v[bi * 3];
|
|
102
|
+
result[result_offset + 1] = v[bi * 3 + 1];
|
|
103
|
+
result[result_offset + 2] = v[bi * 3 + 2];
|
|
104
|
+
};
|
|
105
|
+
return piece;
|
|
106
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Append the mesh-vs-mesh contacts via `append`, the narrowphase's
|
|
3
|
+
* `append_contact(count, wax,way,waz, wbx,wby,wbz, nx,ny,nz, depth, fid) → newCount`.
|
|
4
|
+
*
|
|
5
|
+
* @returns {number} new candidate count
|
|
6
|
+
*/
|
|
7
|
+
export function mesh_mesh_tet_contacts(count: any, shapeA: any, trA: any, shapeB: any, trB: any, append: any): number;
|
|
8
|
+
//# sourceMappingURL=mesh_mesh_tet_manifold.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mesh_mesh_tet_manifold.d.ts","sourceRoot":"","sources":["../../../../../src/engine/physics/narrowphase/mesh_mesh_tet_manifold.js"],"names":[],"mappings":"AAkDA;;;;;GAKG;AACH,+GAFa,MAAM,CA8DlB"}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { bvh_query_user_data_overlaps_aabb } from "../../../core/bvh2/bvh3/query/bvh_query_user_data_overlaps_aabb.js";
|
|
2
|
+
import { aabb3_transform_oriented } from "../../../core/geom/3d/aabb/aabb3_transform_oriented.js";
|
|
3
|
+
import { aabb_world_to_local } from "./decomposition/aabb_world_to_local.js";
|
|
4
|
+
import { build_convex_decomposition } from "./convex_decomposition.js";
|
|
5
|
+
import { convex_convex_manifold, CONVEX_CONVEX_OUT_LENGTH } from "./convex_convex_manifold.js";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Contact generation between two triangulated meshes, each treated as the union
|
|
9
|
+
* of its CONVEX PIECES (from greedy tet-merge — see {@link build_convex_decomposition}).
|
|
10
|
+
* Concave-vs-concave reduced to convex piece-vs-piece, the standard
|
|
11
|
+
* convex-decomposition approach.
|
|
12
|
+
*
|
|
13
|
+
* Only piece pairs whose AABBs overlap reach the SAT + face-clip manifold:
|
|
14
|
+
* project mesh B's world AABB into A's local frame and query A's piece BVH; for
|
|
15
|
+
* each candidate piece of A, project its world AABB into B's local frame and
|
|
16
|
+
* query B's piece BVH. One top-level broadphase leaf per body; the per-shape
|
|
17
|
+
* piece BVHs do the culling. Merging keeps the piece count (and the per-pair
|
|
18
|
+
* SAT cost) far below the raw tet count — the path to large tet counts.
|
|
19
|
+
*
|
|
20
|
+
* The decomposition is built once per shape and cached (shapes are immutable,
|
|
21
|
+
* shared, and the decomposition is in the shape's local frame). Per-frame, no
|
|
22
|
+
* cross-frame state → reset-and-resimulate determinism preserved.
|
|
23
|
+
*
|
|
24
|
+
* @author Alex Goldring
|
|
25
|
+
* @copyright Company Named Limited (c) 2026
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
/** Stop emitting once the candidate buffer is nearly full (capped at 4
|
|
29
|
+
* downstream); a localised resting contact never approaches this. */
|
|
30
|
+
const MAX_EMIT = 60;
|
|
31
|
+
|
|
32
|
+
const piece_out = new Float64Array(CONVEX_CONVEX_OUT_LENGTH);
|
|
33
|
+
let cand_a = new Uint32Array(4096);
|
|
34
|
+
let cand_b = new Uint32Array(4096);
|
|
35
|
+
const local_bbox = new Float64Array(6);
|
|
36
|
+
const world_aabb = new Float64Array(6);
|
|
37
|
+
const query_aabb = new Float64Array(6);
|
|
38
|
+
|
|
39
|
+
/** Build + cache the convex decomposition on the (immutable, shared) shape. */
|
|
40
|
+
function get_decomposition(shape) {
|
|
41
|
+
let d = shape.__convex_decomposition;
|
|
42
|
+
if (d === undefined) {
|
|
43
|
+
d = build_convex_decomposition(shape);
|
|
44
|
+
shape.__convex_decomposition = d;
|
|
45
|
+
}
|
|
46
|
+
return d;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function ensure(buf, n) { return buf.length >= n ? buf : new Uint32Array(n); }
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Append the mesh-vs-mesh contacts via `append`, the narrowphase's
|
|
53
|
+
* `append_contact(count, wax,way,waz, wbx,wby,wbz, nx,ny,nz, depth, fid) → newCount`.
|
|
54
|
+
*
|
|
55
|
+
* @returns {number} new candidate count
|
|
56
|
+
*/
|
|
57
|
+
export function mesh_mesh_tet_contacts(count, shapeA, trA, shapeB, trB, append) {
|
|
58
|
+
const decompA = get_decomposition(shapeA);
|
|
59
|
+
const decompB = get_decomposition(shapeB);
|
|
60
|
+
const piecesA = decompA.pieces;
|
|
61
|
+
const piecesB = decompB.pieces;
|
|
62
|
+
|
|
63
|
+
// Candidate pieces of A: those whose AABB overlaps B's world AABB (in A-local).
|
|
64
|
+
shapeB.compute_bounding_box(local_bbox);
|
|
65
|
+
aabb3_transform_oriented(
|
|
66
|
+
world_aabb, 0,
|
|
67
|
+
local_bbox[0], local_bbox[1], local_bbox[2], local_bbox[3], local_bbox[4], local_bbox[5],
|
|
68
|
+
trB.position.x, trB.position.y, trB.position.z,
|
|
69
|
+
trB.rotation.x, trB.rotation.y, trB.rotation.z, trB.rotation.w
|
|
70
|
+
);
|
|
71
|
+
aabb_world_to_local(
|
|
72
|
+
query_aabb, 0, world_aabb,
|
|
73
|
+
trA.position.x, trA.position.y, trA.position.z,
|
|
74
|
+
trA.rotation.x, trA.rotation.y, trA.rotation.z, trA.rotation.w
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
cand_a = ensure(cand_a, piecesA.length);
|
|
78
|
+
const nA = bvh_query_user_data_overlaps_aabb(cand_a, 0, decompA.bvh, query_aabb);
|
|
79
|
+
|
|
80
|
+
for (let i = 0; i < nA; i++) {
|
|
81
|
+
const pieceA = piecesA[cand_a[i]];
|
|
82
|
+
|
|
83
|
+
// Candidate pieces of B near this piece of A.
|
|
84
|
+
const ab = pieceA.aabb;
|
|
85
|
+
aabb3_transform_oriented(
|
|
86
|
+
world_aabb, 0,
|
|
87
|
+
ab[0], ab[1], ab[2], ab[3], ab[4], ab[5],
|
|
88
|
+
trA.position.x, trA.position.y, trA.position.z,
|
|
89
|
+
trA.rotation.x, trA.rotation.y, trA.rotation.z, trA.rotation.w
|
|
90
|
+
);
|
|
91
|
+
aabb_world_to_local(
|
|
92
|
+
query_aabb, 0, world_aabb,
|
|
93
|
+
trB.position.x, trB.position.y, trB.position.z,
|
|
94
|
+
trB.rotation.x, trB.rotation.y, trB.rotation.z, trB.rotation.w
|
|
95
|
+
);
|
|
96
|
+
cand_b = ensure(cand_b, piecesB.length);
|
|
97
|
+
const nB = bvh_query_user_data_overlaps_aabb(cand_b, 0, decompB.bvh, query_aabb);
|
|
98
|
+
|
|
99
|
+
for (let j = 0; j < nB; j++) {
|
|
100
|
+
const pieceB = piecesB[cand_b[j]];
|
|
101
|
+
if (!convex_convex_manifold(piece_out, pieceA, trA.position, trA.rotation, pieceB, trB.position, trB.rotation)) {
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
const cc = piece_out[3] | 0;
|
|
105
|
+
const nx = piece_out[0], ny = piece_out[1], nz = piece_out[2];
|
|
106
|
+
for (let k = 0; k < cc; k++) {
|
|
107
|
+
const base = 4 + k * 7;
|
|
108
|
+
count = append(count,
|
|
109
|
+
piece_out[base], piece_out[base + 1], piece_out[base + 2],
|
|
110
|
+
piece_out[base + 3], piece_out[base + 4], piece_out[base + 5],
|
|
111
|
+
nx, ny, nz, piece_out[base + 6], 0);
|
|
112
|
+
}
|
|
113
|
+
if (count >= MAX_EMIT) return count;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return count;
|
|
117
|
+
}
|
|
@@ -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":"AAk1CA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;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,CAuClB;AAED;;;;;;;;;;;GAWG;AACH,uFALW,MAAM,MAAM;IAAC,QAAQ,WAAW;IAAC,SAAS,YAAW;CAAC,CAAC,CAAC,QAiKlE;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,QA8G3D"}
|