Skip to main content

banished_core/
picking.rs

1//! Picking: which triangle a ray hits, as the engine finds the object under the pointer.
2//!
3//! Original: `Picking::Scene::Intersect`, Runtime `0x180126f50` (the triangle test).
4
5/// Test a ray against one triangle. Returns the distance along the ray when the ray hits the
6/// triangle no farther than `maximum`.
7///
8/// Only front faces count. A hit on an edge, at distance zero or exactly at the current maximum
9/// counts too, and an equal distance replaces the earlier hit, as in the original's order.
10pub fn triangle(
11    origin: [f32; 3],
12    direction: [f32; 3],
13    vertices: [[f32; 3]; 3],
14    maximum: f32,
15) -> Option<f32> {
16    let [a, b, c] = vertices;
17    let e = [b[0] - a[0], b[1] - a[1], b[2] - a[2]];
18    let f = [c[0] - a[0], c[1] - a[1], c[2] - a[2]];
19    let d = direction;
20    let p = [
21        f[2] * d[1] - f[1] * d[2],
22        f[0] * d[2] - f[2] * d[0],
23        f[1] * d[0] - f[0] * d[1],
24    ];
25    let det = p[1] * e[1] + p[0] * e[0] + p[2] * e[2];
26    let s = [origin[0] - a[0], origin[1] - a[1], origin[2] - a[2]];
27    let q = [
28        s[1] * e[2] - s[2] * e[1],
29        s[2] * e[0] - s[0] * e[2],
30        s[0] * e[1] - s[1] * e[0],
31    ];
32    let u = s[1] * p[1] + s[0] * p[0] + s[2] * p[2];
33    let v = q[1] * d[1] + q[0] * d[0] + q[2] * d[2];
34    if !(0.0 < det && 0.0 <= u && u <= det && 0.0 <= v && v + u <= det) {
35        return None;
36    }
37    let t = (q[1] * f[1] + q[0] * f[0] + q[2] * f[2]) * (1.0 / det);
38    (0.0 <= t && t <= maximum).then_some(t)
39}
40
41/// The hit triangle's unit face normal as `Intersect` gives it: `(b − a) × (c − a)` scaled by
42/// `1 / sqrtf(y² + x² + z²)` (left as it is when that length is zero).
43pub fn face_normal(vertices: [[f32; 3]; 3]) -> [f32; 3] {
44    let [a, b, c] = vertices;
45    let e = [b[0] - a[0], b[1] - a[1], b[2] - a[2]];
46    let f = [c[0] - a[0], c[1] - a[1], c[2] - a[2]];
47    let n = [
48        f[2] * e[1] - f[1] * e[2],
49        f[0] * e[2] - f[2] * e[0],
50        f[1] * e[0] - f[0] * e[1],
51    ];
52    let len = (n[1] * n[1] + n[0] * n[0] + n[2] * n[2]).sqrt();
53    if len == 0.0 {
54        return n;
55    }
56    let inv = 1.0 / len;
57    [inv * n[0], inv * n[1], inv * n[2]]
58}
59
60/// The point at distance `t` along a ray.
61pub fn point(origin: [f32; 3], direction: [f32; 3], t: f32) -> [f32; 3] {
62    [
63        t * direction[0] + origin[0],
64        t * direction[1] + origin[1],
65        t * direction[2] + origin[2],
66    ]
67}