Preserve convex rail contacts and port native truck probes

This commit is contained in:
chazmm committed 2026-09-07 17:39:10 +01:00
1 parent e60841e871
commit 6bb16a6827
3 files changed
+99 -2

No files matched your search

@@ -3,6 +3,67 @@
use super::native_arithmetic::dot3;
type V = [f32; 4];
/// Native query entry: endpoints plus a spline owner, not three vectors.
#[derive(Clone, Copy, Debug)]
pub struct Primitive {
pub start: V,
pub end: V,
pub owner: u64,
}
#[derive(Clone, Copy, Debug)]
pub struct TruckContact {
pub position: V,
pub primitive: usize,
}
/// Truck rectangles from82C1FDC0. The geometry dimensions come from the
/// physics_grinds collection (TruckToWheel584, DeckCenterToTruck636).
/// Output order is positive board-forward truck, negative board-forward truck.
/// These are geometric contacts;82D89150 still decides whether to engage.
pub fn truck_contacts(
board: [V; 4],
flags_2484: u32,
truck_to_wheel: f32,
deck_center_to_truck: f32,
primitives: &[Primitive],
) -> [Option<TruckContact>; 2] {
if flags_2484 & 0x0020_0000 != 0 {
return [None; 2];
}
let [right, up, forward, position] = board;
//821659F8 /8208EA7C: the probe starts .02 below the board and extends
//another .20 down its local up axis. Do not substitute world vertical.
let centre = core::array::from_fn(|i| up[i].mul_add(-0.02, position[i]));
let side = scale(right, truck_to_wheel);
let along = scale(forward, deck_center_to_truck);
let down = scale(up, -0.2);
let centres = [add(centre, along), sub(centre, along)];
let mut result = [None; 2];
let mut distance = [1_000_000.0; 2]; //822F88D4, squared-distance ceiling.
for (primitive, edge) in primitives.iter().enumerate() {
for truck in 0..2 {
let a = add(centres[truck], side);
let b = sub(centres[truck], side);
let c = add(b, down);
let d = add(a, down);
//Native calls both triangles and retains the first triangle's
//intersection if both succeed, including a shared diagonal hit.
let first = segment_triangle(edge.start, edge.end, [a, b, c]);
let second = segment_triangle(edge.start, edge.end, [a, d, c]);
if let Some(position) = first.or(second) {
let delta = sub(position, centres[truck]);
let squared = dot3(delta, delta);
if distance[truck] > squared {
distance[truck] = squared;
result[truck] = Some(TruckContact { position, primitive });
}
}
}
}
result
}
/// 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.
@@ -26,6 +87,8 @@ pub fn segment_triangle(start: V, end: V, triangle: [V; 3]) -> Option<V> {
}
fn sub(a: V, b: V) -> V { core::array::from_fn(|i| a[i] - b[i]) }
fn add(a: V, b: V) -> V { core::array::from_fn(|i| a[i] + b[i]) }
fn scale(a: V, s: f32) -> V { a.map(|v| v * s) }
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]),
+12 -2
View File
@@ -109,15 +109,25 @@ pub(crate) fn surfaces() -> [Vec<[Vector3; 4]>; 3] {
pub(super) fn world(material: RetailContactMaterial) -> BoardWorld {
let mut triangles = Vec::new();
let rail_faces = crate::grind_world::surfaces();
for (tag, quads) in surfaces().into_iter().enumerate() {
for vertices in quads {
for indices in [[0, 2, 1], [0, 3, 2]] {
let rail_face = rail_faces.contains(&vertices);
for (half, indices) in [[0, 2, 1], [0, 3, 2]].into_iter().enumerate() {
// Closed rectangular rails have convex perimeter edges. Keep
// the coplanar triangulation diagonal non-convex. Without
// these flags,82AD3130 rejects narrow-rail edge contacts.
let convex = if rail_face {
if half == 0 { 0x60 } else { 0xc0 }
} else {
0
};
triangles.push(WorldTriangle {
triangle: triangle_from_volume(
indices.map(|i| vertices[i]),
0.0,
[1.0; 3],
TriangleFeature::ONE_SIDED,
TriangleFeature::ONE_SIDED | convex,
),
material,
tag: tag as u32,