]> git.localhorst.tv Git - blank.git/blob - src/geometry/geometry.cpp
1e84fbb9068d1749fbe5a3edcc81519aa7718e87
[blank.git] / src / geometry / geometry.cpp
1 #include "const.hpp"
2 #include "distance.hpp"
3 #include "primitive.hpp"
4 #include "rotation.hpp"
5
6 #include <limits>
7 #include <ostream>
8 #include <glm/gtx/io.hpp>
9 #include <glm/gtx/matrix_cross_product.hpp>
10 #include <glm/gtx/optimum_pow.hpp>
11 #include <glm/gtx/transform.hpp>
12
13
14 namespace blank {
15
16 glm::mat3 find_rotation(const glm::vec3 &a, const glm::vec3 &b) noexcept {
17         glm::vec3 v(cross(a, b));
18         if (iszero(v)) {
19                 // a and b are parallel
20                 if (iszero(a - b)) {
21                         // a and b are identical
22                         return glm::mat3(1.0f);
23                 } else {
24                         // a and b are opposite
25                         // create arbitrary unit vector perpendicular to a and
26                         // rotate 180° around it
27                         glm::vec3 arb(a);
28                         if (std::abs(a.x - 1.0f) > std::numeric_limits<float>::epsilon()) {
29                                 arb.x += 1.0f;
30                         } else {
31                                 arb.y += 1.0f;
32                         }
33                         glm::vec3 axis(normalize(cross(a, arb)));
34                         return glm::mat3(glm::rotate(PI, axis));
35                 }
36         }
37         float mv = length2(v);
38         float c = dot(a, b);
39         float f = (1 - c) / mv;
40         glm::mat3 vx(matrixCross3(v));
41         return glm::mat3(1.0f) + vx + (pow2(vx) * f);
42 }
43
44 std::ostream &operator <<(std::ostream &out, const AABB &box) {
45         return out << "AABB(" << box.min << ", " << box.max << ')';
46 }
47
48 std::ostream &operator <<(std::ostream &out, const Ray &ray) {
49         return out << "Ray(" << ray.orig << ", " << ray.dir << ')';
50 }
51
52 bool Intersection(
53         const Ray &ray,
54         const AABB &aabb,
55         const glm::mat4 &M,
56         float *dist,
57         glm::vec3 *normal
58 ) noexcept {
59         float t_min = 0.0f;
60         float t_max = std::numeric_limits<float>::infinity();
61         const glm::vec3 aabb_pos(M[3].x, M[3].y, M[3].z);
62         const glm::vec3 delta = aabb_pos - ray.orig;
63
64         glm::vec3 t1(t_min, t_min, t_min), t2(t_max, t_max, t_max);
65
66         for (int i = 0; i < 3; ++i) {
67                 const glm::vec3 axis(M[i].x, M[i].y, M[i].z);
68                 const float e = glm::dot(axis, delta);
69                 const float f = glm::dot(axis, ray.dir);
70
71                 if (std::abs(f) > std::numeric_limits<float>::epsilon()) {
72                         t1[i] = (e + aabb.min[i]) / f;
73                         t2[i] = (e + aabb.max[i]) / f;
74
75                         t_min = std::max(t_min, std::min(t1[i], t2[i]));
76                         t_max = std::min(t_max, std::max(t1[i], t2[i]));
77
78                         if (t_max < t_min) {
79                                 return false;
80                         }
81                 } else {
82                         if (aabb.min[i] - e > 0.0f || aabb.max[i] - e < 0.0f) {
83                                 return false;
84                         }
85                 }
86         }
87
88         glm::vec3 min_all(min(t1, t2));
89
90         if (dist) {
91                 *dist = t_min;
92         }
93         if (normal) {
94                 if (min_all.x > min_all.y) {
95                         if (min_all.x > min_all.z) {
96                                 normal->x = t2.x < t1.x ? 1 : -1;
97                         } else {
98                                 normal->z = t2.z < t1.z ? 1 : -1;
99                         }
100                 } else if (min_all.y > min_all.z) {
101                         normal->y = t2.y < t1.y ? 1 : -1;
102                 } else {
103                         normal->z = t2.z < t1.z ? 1 : -1;
104                 }
105         }
106         return true;
107 }
108
109
110 bool Intersection(
111         const AABB &a_box,
112         const glm::mat4 &a_m,
113         const AABB &b_box,
114         const glm::mat4 &b_m,
115         float &depth,
116         glm::vec3 &normal
117 ) noexcept {
118         glm::vec3 a_corners[8] = {
119                 glm::vec3(a_m * glm::vec4(a_box.min.x, a_box.min.y, a_box.min.z, 1)),
120                 glm::vec3(a_m * glm::vec4(a_box.min.x, a_box.min.y, a_box.max.z, 1)),
121                 glm::vec3(a_m * glm::vec4(a_box.min.x, a_box.max.y, a_box.min.z, 1)),
122                 glm::vec3(a_m * glm::vec4(a_box.min.x, a_box.max.y, a_box.max.z, 1)),
123                 glm::vec3(a_m * glm::vec4(a_box.max.x, a_box.min.y, a_box.min.z, 1)),
124                 glm::vec3(a_m * glm::vec4(a_box.max.x, a_box.min.y, a_box.max.z, 1)),
125                 glm::vec3(a_m * glm::vec4(a_box.max.x, a_box.max.y, a_box.min.z, 1)),
126                 glm::vec3(a_m * glm::vec4(a_box.max.x, a_box.max.y, a_box.max.z, 1)),
127         };
128
129         glm::vec3 b_corners[8] = {
130                 glm::vec3(b_m * glm::vec4(b_box.min.x, b_box.min.y, b_box.min.z, 1)),
131                 glm::vec3(b_m * glm::vec4(b_box.min.x, b_box.min.y, b_box.max.z, 1)),
132                 glm::vec3(b_m * glm::vec4(b_box.min.x, b_box.max.y, b_box.min.z, 1)),
133                 glm::vec3(b_m * glm::vec4(b_box.min.x, b_box.max.y, b_box.max.z, 1)),
134                 glm::vec3(b_m * glm::vec4(b_box.max.x, b_box.min.y, b_box.min.z, 1)),
135                 glm::vec3(b_m * glm::vec4(b_box.max.x, b_box.min.y, b_box.max.z, 1)),
136                 glm::vec3(b_m * glm::vec4(b_box.max.x, b_box.max.y, b_box.min.z, 1)),
137                 glm::vec3(b_m * glm::vec4(b_box.max.x, b_box.max.y, b_box.max.z, 1)),
138         };
139
140         glm::vec3 axes[15] = {
141                 glm::vec3(a_m[0]),
142                 glm::vec3(a_m[1]),
143                 glm::vec3(a_m[2]),
144                 glm::vec3(b_m[0]),
145                 glm::vec3(b_m[1]),
146                 glm::vec3(b_m[2]),
147                 normalize(cross(glm::vec3(a_m[0]), glm::vec3(b_m[0]))),
148                 normalize(cross(glm::vec3(a_m[0]), glm::vec3(b_m[1]))),
149                 normalize(cross(glm::vec3(a_m[0]), glm::vec3(b_m[2]))),
150                 normalize(cross(glm::vec3(a_m[1]), glm::vec3(b_m[0]))),
151                 normalize(cross(glm::vec3(a_m[1]), glm::vec3(b_m[1]))),
152                 normalize(cross(glm::vec3(a_m[1]), glm::vec3(b_m[2]))),
153                 normalize(cross(glm::vec3(a_m[2]), glm::vec3(b_m[0]))),
154                 normalize(cross(glm::vec3(a_m[2]), glm::vec3(b_m[1]))),
155                 normalize(cross(glm::vec3(a_m[2]), glm::vec3(b_m[2]))),
156         };
157
158         depth = std::numeric_limits<float>::infinity();
159         int min_axis = 0;
160
161         int cur_axis = 0;
162         for (const glm::vec3 &axis : axes) {
163                 if (any(isnan(axis))) {
164                         // can result from the cross products if A and B have parallel axes
165                         ++cur_axis;
166                         continue;
167                 }
168                 float a_min = std::numeric_limits<float>::infinity();
169                 float a_max = -std::numeric_limits<float>::infinity();
170                 for (const glm::vec3 &corner : a_corners) {
171                         float val = glm::dot(corner, axis);
172                         a_min = std::min(a_min, val);
173                         a_max = std::max(a_max, val);
174                 }
175
176                 float b_min = std::numeric_limits<float>::infinity();
177                 float b_max = -std::numeric_limits<float>::infinity();
178                 for (const glm::vec3 &corner : b_corners) {
179                         float val = glm::dot(corner, axis);
180                         b_min = std::min(b_min, val);
181                         b_max = std::max(b_max, val);
182                 }
183
184                 if (a_max < b_min || b_max < a_min) return false;
185
186                 float overlap = std::min(a_max, b_max) - std::max(a_min, b_min);
187                 if (overlap < depth) {
188                         depth = overlap;
189                         min_axis = cur_axis;
190                 }
191
192                 ++cur_axis;
193         }
194
195         normal = axes[min_axis];
196         return true;
197 }
198
199
200 std::ostream &operator <<(std::ostream &out, const Plane &plane) {
201         return out << "Plane(" << plane.normal << ", " << plane.dist << ')';
202 }
203
204 std::ostream &operator <<(std::ostream &out, const Frustum &frustum) {
205         return out << "Frustum(" << std::endl
206                 << "\tleft:   " << frustum.plane[0] << std::endl
207                 << "\tright:  " << frustum.plane[1] << std::endl
208                 << "\tbottom: " << frustum.plane[2] << std::endl
209                 << "\ttop:    " << frustum.plane[3] << std::endl
210                 << "\tnear:   " << frustum.plane[4] << std::endl
211                 << "\tfar:    " << frustum.plane[5] << std::endl
212                 << ')';
213 }
214
215 bool CullTest(const AABB &box, const glm::mat4 &MVP) noexcept {
216         // transform corners into clip space
217         glm::vec4 corners[8] = {
218                 { box.min.x, box.min.y, box.min.z, 1.0f },
219                 { box.min.x, box.min.y, box.max.z, 1.0f },
220                 { box.min.x, box.max.y, box.min.z, 1.0f },
221                 { box.min.x, box.max.y, box.max.z, 1.0f },
222                 { box.max.x, box.min.y, box.min.z, 1.0f },
223                 { box.max.x, box.min.y, box.max.z, 1.0f },
224                 { box.max.x, box.max.y, box.min.z, 1.0f },
225                 { box.max.x, box.max.y, box.max.z, 1.0f },
226         };
227
228         // check how many corners lie outside
229         int hits[6] = { 0, 0, 0, 0, 0, 0 };
230         for (glm::vec4 &corner : corners) {
231                 corner = MVP * corner;
232                 // replacing this with *= 1/w is effectively more expensive
233                 corner /= corner.w;
234                 hits[0] += (corner.x >  1.0f);
235                 hits[1] += (corner.x < -1.0f);
236                 hits[2] += (corner.y >  1.0f);
237                 hits[3] += (corner.y < -1.0f);
238                 hits[4] += (corner.z >  1.0f);
239                 hits[5] += (corner.z < -1.0f);
240         }
241
242         // if all corners are outside any given clip plane, the test is true
243         for (int hit : hits) {
244                 if (hit == 8) return true;
245         }
246
247         // otherwise the box might still get culled completely, but can't say for sure ;)
248         return false;
249 }
250
251 bool CullTest(const AABB &box, const Frustum &frustum) noexcept {
252         for (const Plane &plane : frustum.plane) {
253                 const glm::vec3 np(
254                         ((plane.normal.x > 0.0f) ? box.max.x : box.min.x),
255                         ((plane.normal.y > 0.0f) ? box.max.y : box.min.y),
256                         ((plane.normal.z > 0.0f) ? box.max.z : box.min.z)
257                 );
258                 const float dp = dot(plane.normal, np);
259                 // cull if nearest point is on the "outside" side of the plane
260                 if (dp < -plane.dist) return true;
261         }
262         return false;
263 }
264
265 }