diff --git a/crates/skate-core/src/physics/grind_contact.rs b/crates/skate-core/src/physics/grind_contact.rs new file mode 100644 index 00000000..854d0f1b --- /dev/null +++ b/crates/skate-core/src/physics/grind_contact.rs @@ -0,0 +1,33 @@ +//! Native grind contact leaf82C1E2C8. This is distinct from the tolerant wheel +//! triangle query: exact parallel rejection and closed segment/barycentric bounds. +use super::native_arithmetic::dot3; +type V = [f32; 4]; + +/// Intersect a grind primitive segment with a board/truck probe triangle. +/// Returns the native hit position; acquisition/scoring belongs to the caller. +/// Both windings are accepted. Failed branches do not synthesize a contact. +pub fn segment_triangle(start: V, end: V, triangle: [V; 3]) -> Option { + let [a, b, c] = triangle; + let ac = sub(c, a); + let ab = sub(b, a); + let direction = sub(end, start); + let normal = cross(ab, ac); + let denominator = -dot3(direction, normal); + if denominator == 0.0 { return None; } + let from = sub(start, a); + let inverse = 1.0 / denominator; + let t = inverse * dot3(from, normal); + if t < 0.0 || t > 1.0 { return None; } + let side = cross(from, direction); + let u = inverse * dot3(ac, side); + let v = (1.0 / -denominator) * dot3(ab, side); + if u + v > 1.0 || u < 0.0 || v < 0.0 { return None; } + Some(core::array::from_fn(|i| direction[i].mul_add(t, start[i]))) +} + +fn sub(a: V, b: V) -> V { core::array::from_fn(|i| a[i] - b[i]) } +fn cross(a: V, b: V) -> V { + [(-a[2]).mul_add(b[1], a[1] * b[2]), + (-a[0]).mul_add(b[2], a[2] * b[0]), + (-a[1]).mul_add(b[0], a[0] * b[1]), 0.0] +} diff --git a/crates/skate-core/src/physics/mod.rs b/crates/skate-core/src/physics/mod.rs index 10c4c9ab..4daaf636 100644 --- a/crates/skate-core/src/physics/mod.rs +++ b/crates/skate-core/src/physics/mod.rs @@ -40,6 +40,7 @@ pub mod solver; pub mod startup; mod triangle_closest; pub mod triangle_query; +pub mod grind_contact; mod triangle_sweep; mod triangle_sweep_roots; mod triangle_sweep_walk; diff --git a/docs/GRIND_INTEGRATION.md b/docs/GRIND_INTEGRATION.md index 909b35e6..5bbb01bf 100644 --- a/docs/GRIND_INTEGRATION.md +++ b/docs/GRIND_INTEGRATION.md @@ -37,6 +37,10 @@ its truncated pseudocode is not sufficient to port the nonempty branches. queries (`82C1FDC0`, `82C20130`, `82C1FB98`, `82C1F968`) before scoring. For example `82D89150` handles a two-truck candidate; it is not a nearest-rail angle classifier. Selection output has candidate byte384 in a416-byte record. +- `82C1E2C8` is now ported in core `physics/grind_contact.rs`: the exact + segment/triangle leaf used by those truck/deck queries. It retains the + zero-denominator rejection and closed0..1 bounds, unlike wheel probes with + their endpoint tolerance. It does not itself accept a grind. - `82D8AB08`: post-input manager publication precedes state selection. - Native selector maps actual candidate grind type0..5 to states401,400,402, 403,404,405. `player_state/selection.rs` currently publishes no candidate.