]> git.localhorst.tv Git - gong.git/blob - src/geometry/primitive.hpp
sphere/sphere intersection test
[gong.git] / src / geometry / primitive.hpp
1 #ifndef GONG_GEOMETRY_PRIMITIVE_HPP_
2 #define GONG_GEOMETRY_PRIMITIVE_HPP_
3
4 #include "../graphics/glm.hpp"
5
6 #include <algorithm>
7 #include <iosfwd>
8 #include <glm/gtx/norm.hpp>
9
10
11 namespace gong {
12 namespace geometry {
13
14 struct AABB {
15         glm::vec3 min;
16         glm::vec3 max;
17
18         void Adjust() noexcept {
19                 if (max.x < min.x) std::swap(max.x, min.x);
20                 if (max.y < min.y) std::swap(max.y, min.y);
21                 if (max.z < min.z) std::swap(max.z, min.z);
22         }
23
24         glm::vec3 Center() const noexcept {
25                 return min + (max - min) * 0.5f;
26         }
27
28         /// return distance between origin and farthest vertex
29         float OriginRadius() const noexcept {
30                 glm::vec3 high(glm::max(glm::abs(min), glm::abs(max)));
31                 return glm::length(high);
32         }
33
34         void Position(const glm::vec3 &center) noexcept {
35                 const glm::vec3 halfsize((max - min) * 0.5f);
36                 min = center - halfsize;
37                 max = center + halfsize;
38         }
39         void Move(const glm::vec3 &delta) noexcept {
40                 min += delta;
41                 max += delta;
42         }
43 };
44
45 std::ostream &operator <<(std::ostream &, const AABB &);
46
47 bool Intersection(const AABB &, const AABB &) noexcept;
48
49 // TODO: this should really use setters/getters for dir and inv_dir so
50 //       manipulating code doesn't "forget" to call Update()
51 struct Ray {
52         glm::vec3 orig;
53         glm::vec3 dir;
54
55         glm::vec3 inv_dir;
56
57         void Update() noexcept {
58                 inv_dir = 1.0f / dir;
59         }
60
61         /// get shortest distance of this ray's line to given point
62         float Distance(const glm::vec3 &point) const noexcept {
63                 // d = |(x2-x1)×(x1-x0)|/|x2-x1|
64                 // where x0 is point, and x1 and x2 are points on the line
65                 // for derivation, see http://mathworld.wolfram.com/Point-LineDistance3-Dimensional.html
66                 // x1 = orig
67                 // x2-x1 = dir, which means |x2-x1| is 1.0
68                 return glm::length(glm::cross(dir, orig - point));
69         }
70         float DistanceSquared(const glm::vec3 &point) const noexcept {
71                 return glm::length2(glm::cross(dir, orig - point));
72         }
73 };
74
75 std::ostream &operator <<(std::ostream &, const Ray &);
76
77 /// axis aligned boolean ray/box intersection test
78 /// if true, dist constains distance from ray's origin to intersection point
79 bool Intersection(
80         const Ray &,
81         const AABB &,
82         float &dist) noexcept;
83
84 /// detailed oriented ray/box intersection test
85 bool Intersection(
86         const Ray &,
87         const AABB &,
88         const glm::mat4 &M,
89         float *dist = nullptr,
90         glm::vec3 *normal = nullptr) noexcept;
91
92 /// matrices may translate and rotate, but must not scale/shear/etc
93 /// (basically the first three columns must have unit length)
94 bool Intersection(
95         const AABB &a_box,
96         const glm::mat4 &a_m,
97         const AABB &b_box,
98         const glm::mat4 &b_m,
99         float &depth,
100         glm::vec3 &normal) noexcept;
101
102 /// Plane defined by a surface norml and distance to the origin such that
103 /// the point (normal * dist) lies on the plane.
104 struct Plane {
105         glm::vec3 normal;
106         float dist;
107
108         float &A() noexcept { return normal.x; }
109         float &B() noexcept { return normal.y; }
110         float &C() noexcept { return normal.z; }
111         float &D() noexcept { return dist; }
112         float A() const noexcept { return normal.x; }
113         float B() const noexcept { return normal.y; }
114         float C() const noexcept { return normal.z; }
115         float D() const noexcept { return dist; }
116
117         Plane(const glm::vec3 &n, float d)
118         : normal(n), dist(d) { }
119         explicit Plane(const glm::vec4 &abcd)
120         : normal(abcd), dist(abcd.w) { }
121
122         void Normalize() noexcept {
123                 const float l = glm::length(normal);
124                 normal /= l;
125                 dist /= l;
126         }
127 };
128
129 std::ostream &operator <<(std::ostream &, const Plane &);
130
131 /// Shortest distance from point to plane.
132 float Distance(const glm::vec3 &point, const Plane &plane);
133 /// Shortest distance from point to plane with sign indicating whether
134 /// it's in front of (positive, in direction of normal) or behind
135 /// (negative, counter direction of normal) the surface.
136 float SignedDistance(const glm::vec3 &point, const Plane &plane);
137
138 struct Frustum {
139         Plane plane[6];
140         Plane &Left() noexcept { return plane[0]; }
141         Plane &Right() noexcept { return plane[1]; }
142         Plane &Bottom() noexcept { return plane[2]; }
143         Plane &Top() noexcept { return plane[3]; }
144         Plane &Near() noexcept { return plane[4]; }
145         Plane &Far() noexcept { return plane[5]; }
146         const Plane &Left() const noexcept { return plane[0]; }
147         const Plane &Right() const noexcept { return plane[1]; }
148         const Plane &Bottom() const noexcept { return plane[2]; }
149         const Plane &Top() const noexcept { return plane[3]; }
150         const Plane &Near() const noexcept { return plane[4]; }
151         const Plane &Far() const noexcept { return plane[5]; }
152
153         /// create frustum from transposed MVP
154         explicit Frustum(const glm::mat4 &mat)
155         : plane{
156                 Plane{ mat[3] + mat[0] },
157                 Plane{ mat[3] - mat[0] },
158                 Plane{ mat[3] + mat[1] },
159                 Plane{ mat[3] - mat[1] },
160                 Plane{ mat[3] + mat[2] },
161                 Plane{ mat[3] - mat[2] },
162         } { }
163
164         void Normalize() noexcept {
165                 for (Plane &p : plane) {
166                         p.Normalize();
167                 }
168         }
169 };
170
171 std::ostream &operator <<(std::ostream &, const Plane &);
172 std::ostream &operator <<(std::ostream &, const Frustum &);
173
174 bool CullTest(const AABB &box, const glm::mat4 &) noexcept;
175 bool CullTest(const AABB &box, const Frustum &) noexcept;
176
177 struct Sphere {
178
179         glm::vec3 origin;
180         float radius;
181
182         void Position(const glm::vec3 &center) noexcept {
183                 origin = center;
184         }
185         void Move(const glm::vec3 &delta) noexcept {
186                 origin += delta;
187         }
188
189 };
190
191 std::ostream &operator <<(std::ostream &, const Sphere &);
192
193 /// Two spheres intersection test.
194 bool Intersection(
195         const Sphere &,
196         const Sphere &,
197         float &dist,
198         glm::vec3 &norm) noexcept;
199
200 /// Test for intersection of sphere with double sided infinite plane.
201 /// If true, dist will hold the smaller interpenetration depth and norm
202 /// the respective contact normal.
203 bool Intersection(
204         const Sphere &sphere,
205         const Plane &plane,
206         float &dist,
207         glm::vec3 &norm) noexcept;
208
209 /// Test for intersection of sphere with half space defined by the
210 /// backface of given plane.
211 /// In all cases, dist will hold the distance between the near points
212 /// of plane and sphere. Contact normal will always be the plane's normal.
213 bool Intersection(
214         const Sphere &sphere,
215         const Plane &plane,
216         float &dist) noexcept;
217
218 }
219 }
220
221 #endif