1
2
3
4
5
6
7
8
9
10
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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
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
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
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
//! Handing a mesh to a physics engine, and knowing first whether it will take it.
//!
//! A collider is not a render mesh with a different material. A renderer will
//! draw a zero-area triangle, an unwelded seam and an open surface without
//! complaint; a physics engine will variously refuse them, build a broken
//! adjacency structure from them, or answer "is this point inside" wrongly. This
//! module says which of those a given [`MeshBuffer`] would hit, **before** it is
//! handed over.
//!
//! # What this deliberately does not do
//!
//! Depend on a physics engine. `parry3d` is a **dev-dependency** here, not an
//! optional feature, and the ticket that asked for the feature is amended rather
//! than followed. Three reasons:
//!
//! - **`parry3d` takes `Vec<[u32; 3]>` for its index buffer** — the architecture
//! research calls it *"the most math-opinionated crate in the ecosystem"* and
//! notes it still uses plain arrays there. So the only thing a conversion adds
//! is mapping `[f32; 3]` to parry's vector type, which is one line at the call
//! site and is shown below.
//! - **The core crate stays at one dependency in every configuration.** An
//! optional dependency is still a version this manifest has to track, and
//! `parry3d` is pre-1.0 and moving fast — it migrated off nalgebra onto
//! glam/glamx in 0.26.0, which is why it wants glam `^0.33` where the engine
//! version this workspace pins against wants `^0.32`. That skew is documented
//! in `CLAUDE.md`'s hard version pins and is the reason every public signature
//! here is an array.
//! - **The acceptance criterion is still met, and by parry itself.**
//! `a_carved_shape_builds_a_parry_trimesh` builds a real `TriMesh` from a
//! carved field and runs parry's own topology check on it. A dev-dependency
//! does not propagate, so `cargo tree -p isomesh -e normal` is still two
//! packages.
//!
//! The conversion, in full, for a consumer who wants it:
//!
//! ```ignore
//! let indices = isomesh::collider::triangle_indices(&mesh);
//! let vertices = mesh.positions.iter().map(|p| Vector::new(p[0], p[1], p[2])).collect();
//! let trimesh = TriMesh::new(vertices, indices)?;
//! ```
//!
//! # Weld first. This is the part that bites.
//!
//! Meshing a world in chunks produces, at every seam, two vertices at the same
//! position that no index shares. A renderer cannot tell. A physics engine sees a
//! **boundary edge** — a hole — and a character walks through it. A-013 measured
//! the seam: 80 boundary edges and 40 duplicated vertices between two chunks of a
//! torus, closing to zero after a weld (M-46). So
//! [`readiness`] reports `duplicate_vertices` first, and
//! `an_unwelded_seam_is_reported_as_not_ready` is the test that keeps it honest.
use Vec;
use crate;
use crate::;
/// The index buffer in the shape a physics engine wants it.
///
/// [`MeshBuffer::indices`](MeshBuffer) is flat, because that is what a GPU takes.
/// `parry3d` — and every other engine checked — takes triples. A trailing partial
/// triangle is dropped, exactly as the validator counts it in `trailing_indices`
/// rather than rounding it up into a triangle nobody asked for.
/// What a physics engine would make of this mesh.
///
/// Every count is read from the T-001 validator rather than recomputed, so this
/// cannot drift from what the rest of the suite reports about the same mesh. What
/// is added is the *interpretation*: which of those numbers a collider actually
/// cares about, and which it does not.
/// Read a mesh through a collider's eyes.
///
/// Runs the T-001 validator and reinterprets it. `cfg` decides the weld epsilon
/// and the degenerate-area threshold, both relative to the grid spacing — see
/// [`ValidateConfig::from_cell_size`].
/// The same reinterpretation, from a report the caller already has.
///
/// Exists so a caller that has just validated a mesh does not validate it twice;
/// the validator is a multi-pass walk over every triangle and is not free.