1pub use nalgebra_glm as glm;
2
3#[derive(Debug)]
4pub struct Triangle {
5 pub p0: glm::Vec3,
6 pub p1: glm::Vec3,
7 pub p2: glm::Vec3,
8 pub normal: glm::Vec3,
9}
10
11impl Triangle {
12 pub fn from_points(p0: &glm::Vec3, p1: &glm::Vec3, p2: &glm::Vec3) -> Triangle {
13 Triangle {
14 p0: *p0,
15 p1: *p1,
16 p2: *p2,
17 normal: (p1 - p0).cross(&(p2 - p0)).normalize(),
18 }
19 }
20}
21
22#[derive(Debug)]
23pub struct Bounds {
24 pub center: glm::Vec3,
25 pub radius: f32,
26 pub cone_apex: glm::Vec3,
27 pub cone_axis: glm::Vec3,
28 pub cone_cutoff: f32,
29}
30
31pub fn compute_bounding_sphere(points: &[glm::Vec3]) -> (glm::Vec3, f32) {
32 let mut pmin = [0; 3];
33 let mut pmax = [0; 3];
34
35 for (idx, p) in points.iter().enumerate() {
36 for axis in 0..3 {
37 if p[axis] < points[pmin[axis]][axis] {
38 pmin[axis] = idx
39 }
40
41 if p[axis] > points[pmax[axis]][axis] {
42 pmax[axis] = idx
43 }
44 }
45 }
46
47 let mut paxisd2 = 0.0;
48 let mut paxis = 0;
49
50 for axis in 0..3 {
51 let p1 = points[pmin[axis]];
52 let p2 = points[pmax[axis]];
53
54 let d2 = glm::length2(&(p2 - p1));
55
56 if d2 > paxisd2 {
57 paxisd2 = d2;
58 paxis = axis;
59 }
60 }
61
62 let p1 = points[pmin[paxis]];
63 let p2 = points[pmax[paxis]];
64
65 let mut center = (p1 + p2) * 0.5;
66 let mut radius = paxisd2.sqrt() * 0.5;
67
68 for p in points.iter() {
69 let d2 = glm::length2(&(p - center));
70
71 if d2 > radius * radius {
72 let d = d2.sqrt();
73
74 let k = 0.5 + (radius / d) * 0.5;
75
76 center = center * k + p * (1.0 - k);
77 radius = (radius + d) * 0.5;
78 }
79 }
80
81 (center, radius)
82}
83
84pub fn compute_cone(triangles: &[Triangle]) -> Bounds {
85 let mut corners = vec![];
86 let mut normals = vec![];
87
88 for tri in triangles.iter() {
89 corners.push(tri.p0);
90 corners.push(tri.p1);
91 corners.push(tri.p2);
92 normals.push(tri.normal);
93 }
94
95 let point_sphere = compute_bounding_sphere(&corners);
96 let normal_sphere = compute_bounding_sphere(&normals);
97
98 let center = point_sphere.0;
99 let axis = glm::normalize(&normal_sphere.0);
100
101 let mut min_dot = 1.0;
102
103 for n in normals.iter() {
104 let dot = glm::dot(&n, &axis);
105 if dot < min_dot {
106 min_dot = dot;
107 }
108 }
109
110 if min_dot <= 0.1 {
114 return Bounds {
115 center,
116 radius: point_sphere.1,
117 cone_apex: glm::vec3(0.0, 0.0, 0.0),
118 cone_axis: glm::vec3(0.0, 0.0, 0.0),
119 cone_cutoff: 0.0,
120 };
121 }
122
123 let mut maxt = 0.0;
124 for tri in triangles.iter() {
125 for p in [tri.p0, tri.p1, tri.p2].iter() {
126 let c = center - p;
127 let dc = glm::dot(&c, &tri.normal);
128 let dn = glm::dot(&axis, &tri.normal);
129 let t = dc / dn;
130
131 if t > maxt {
132 maxt = t;
133 }
134 }
135 }
136
137 Bounds {
138 center,
139 radius: point_sphere.1,
140 cone_apex: center - axis * maxt,
141 cone_axis: axis,
142 cone_cutoff: (1.0 - min_dot * min_dot).sqrt(),
143 }
144}