ToyMaker Game Engine 0.0.2
ToyMaker is a game engine developed and maintained by Zoheb Shujauddin.
Loading...
Searching...
No Matches
math.hpp
Go to the documentation of this file.
1
12
13#ifndef TOYMAKERENGINE_SPATIALQUERYMATH_H
14#define TOYMAKERENGINE_SPATIALQUERYMATH_H
15
16#include <glm/glm.hpp>
17
18#include "types.hpp"
19
20namespace ToyMaker {
21
22 static constexpr uint8_t kMaxIterations { 64 };
23
30 std::pair<bool, glm::vec3> computeIntersection(const Ray& ray, const Plane& plane);
37 std::pair<bool, glm::vec3> computeIntersection(const Ray& ray, const AreaTriangle& triangle);
47 std::pair<uint8_t, std::pair<glm::vec3, glm::vec3>> computeIntersections(const Ray& ray, const AxisAlignedBounds& axisAlignedBounds);
55 std::pair<uint8_t, std::pair<glm::vec3, glm::vec3>> computeIntersections(const Ray& ray, const ObjectBounds& objectbounds);
56
61 bool overlaps(const glm::vec3& point, const ObjectBounds& bounds);
66 inline bool overlaps(const ObjectBounds& bounds, const glm::vec3& point) {
67 return overlaps(point, bounds);
68 }
69
73 bool overlaps(const Ray& ray, const ObjectBounds& bounds);
78 inline bool overlaps(const ObjectBounds& bounds, const Ray& ray) {
79 return overlaps(ray, bounds);
80 }
81
86 bool overlaps(const glm::vec3& point, const AxisAlignedBounds& bounds);
92 inline bool overlaps(const AxisAlignedBounds& bounds, const glm::vec3& point) {
93 return overlaps(point, bounds);
94 }
95
100 bool overlaps(const Ray& ray, const AxisAlignedBounds& bounds);
106 inline bool overlaps(const AxisAlignedBounds& bounds, const Ray& ray) {
107 return overlaps(ray, bounds);
108 }
109
114 bool overlaps(const AxisAlignedBounds& one, const AxisAlignedBounds& two);
120 bool overlaps(const ObjectBounds& one, const ObjectBounds& two);
126 bool overlaps(const AxisAlignedBounds& one, const ObjectBounds& two);
132 inline bool overlaps(const ObjectBounds& one, const AxisAlignedBounds& two) {
133 return overlaps(two, one);
134 }
135
151 template <typename T, typename U>
152 std::pair<bool, Simplex> gjkOverlaps(const T& one, const U& two);
153
166 template <typename T, typename U>
167 Polytope buildPolytope(const T& one, const U& two, const Simplex& gjkSimplex);
168
185 template <typename T, typename U>
186 Collision checkCollision(const T& one, const U& two);
187
193 bool contains(const glm::vec3& point, const ObjectBounds& bounds);
199 bool contains(const glm::vec3& point, const AxisAlignedBounds& bounds);
205 bool contains(const Ray& ray, const AxisAlignedBounds& bounds);
211 bool contains(const Ray& ray, const ObjectBounds& bounds);
217 bool contains(const AxisAlignedBounds& one, const AxisAlignedBounds& two);
223 bool contains(const ObjectBounds& one, const AxisAlignedBounds& two);
224
233 inline std::pair<glm::vec3, glm::vec3> deriveTangents(const glm::vec3& vector) {
234 assert(isFinite(vector) && "Vector must be finite");
235 assert(squareDistance(vector) && "Zero vectors are invalid as basis for tangents");
236
237 const auto normalized { glm::normalize(vector) };
238 const glm::vec3 tangentPrelim { ((glm::abs(normalized.x) >= .57735f) ?
239 glm::vec3{ normalized.y, -normalized.x, 0.f } : glm::vec3{ 0.f, normalized.z, -normalized.y }
240 ) };
241 const glm::vec3 tangent2 {
242 glm::normalize(glm::cross(tangentPrelim, normalized))
243 };
244
245 return { glm::normalize(glm::cross(normalized, tangent2)), tangent2 };
246 }
247
257 template<typename T, typename U>
258 inline glm::vec3 minkowskiDifference(const T& one, const U& two, const glm::vec3& along) {
259 const glm::vec3 supportOne { one.getSupportAlong(along) };
260 const glm::vec3 supportTwo { two.getSupportAlong(-along) };
261 return supportOne - supportTwo;
262 }
263
275 template<typename T, typename U>
276 inline std::tuple<glm::vec3, glm::vec3, glm::vec3> minkowskiDifferenceFull(const T& one, const U& two, const glm::vec3& along) {
277 const glm::vec3 supportOne { one.getSupportAlong(along) };
278 const glm::vec3 supportTwo { two.getSupportAlong(-along) };
279 return { supportOne - supportTwo, supportOne, supportTwo };
280 }
281
282 template <typename T, typename U>
283 inline std::pair<bool, Simplex> gjkOverlaps(const T& one, const U& two) {
284 assert(one.isSensible() && two.isSensible() && "Invalid object bounds provided");
285
286 // Both need to be non-degenerate bounds to actually overlap
287 if(!one.isPositiveStrict() || !two.isPositiveStrict()) {
288 return { false, Simplex {} };
289 }
290
291
292 glm::vec3 searchDirection { two.getComputedWorldPosition() - one.getComputedWorldPosition() };
293
294 // Two non-degenerate objects share the same origin, obviously they overlap
295 if(squareDistance(searchDirection) == 0.f) {
296 return { true, Simplex {} };
297 }
298
299 Simplex simplex {};
300 auto fullDifference { minkowskiDifferenceFull(one, two, searchDirection) };
301 const auto& candidatePoint { std::get<0>(fullDifference) };
302 const auto& supportA { std::get<1>(fullDifference) };
303 const auto& supportB { std::get<2>(fullDifference) };
304 simplex.append(candidatePoint, supportA, supportB);
305
306 // we go towards the origin from the first point we found
307 searchDirection = squareDistance(simplex.mPoints[0]) ? -simplex.mPoints[0]: glm::vec3 { 1.f, 0.f, 0.f };
308 bool found { false };
309 for(uint8_t iteration { 0 }; iteration < kMaxIterations; ++iteration) {
310
311 // We force a tetrahedron to form by picking arbitrary search directions
312 // in the event our degenerate simplex contains (0, 0, 0)
313 if(!squareDistance(searchDirection)) {
314 const float signMult { (iteration & 1)? -1.f: 1.f };
315
316 // Pick a new search direction to expand 1-simplex (line) to 2-simplex (triangle)
317 if(simplex.mNPoints == 2) {
318 const float signMult2 { (iteration & 2)? -1.f: 1.f };
319 const glm::vec3 dirAB { glm::normalize(simplex.mPoints[1] - simplex.mPoints[0]) };
320 assert(squareDistance(dirAB) > 0 && "A and B should never be the same point");
321
322 const glm::vec3 dirOffset { (dirAB.x != 0.f || dirAB.z != 0.f)?
323 glm::vec3{ 0.f, signMult * 1.f, 0.f } : glm::vec3{ signMult2 * 1.f, 0.f, signMult * 1.f }
324 };
325
326 searchDirection = -dirAB + dirOffset;
327
328 // Pick a new search direction to expand 2-simplex (triangle) to 3-simplex (tetrahedron)
329 } else if(simplex.mNPoints == 3) {
330 const glm::vec3 normABC { glm::normalize(glm::cross(simplex.mPoints[1] - simplex.mPoints[0], simplex.mPoints[2] - simplex.mPoints[0])) };
331 searchDirection = signMult * normABC;
332
333 } else {
334 assert(false && "A simplex with 4 points is not degenerate, and there's no solving necessary here");
335 }
336 }
337
338 fullDifference = minkowskiDifferenceFull(one, two, searchDirection);
339
340 // We couldn't find a point past the origin in this direction, so obviously there is no intersection
341 if(glm::dot(candidatePoint, searchDirection) <= 0) {
342 return { false, simplex };
343 }
344
345 // This will return false generally when we're trying to fabricate
346 // a search direction. In that case we just fabricate another one at
347 // the start of the loop.
348 if(!simplex.append(candidatePoint, supportA, supportB)) {
349 searchDirection = glm::vec3 { 0.f, 0.f, 0.f };
350 continue;
351 }
352
353 const auto result { simplex.evaluate() };
354 if(result.first) {
355 return { true, simplex };
356 }
357
358 searchDirection = result.second;
359 }
360 assert(false && "We should not have exited the loop without finding a simplex");
361 return { false, simplex };
362 }
363
364 template <typename T, typename U>
365 inline Collision checkCollision(const T& one, const U& two) {
366 // check via GJK whether there's any overlap at all
367 const std::pair<bool, Simplex> overlapResult { gjkOverlaps(one, two) };
368 if(!overlapResult.first) {
369 return {
370 .mCollided { false },
371 };
372 }
373
374 // allocate result storage
375 Collision collisionResult { Collision {
376 .mCollided { true },
377 } };
378
379
380 // build a polytope containing the closest surface to the origin in
381 // the Minkowski difference between the objects
382 const Polytope polytope { buildPolytope(one, two, overlapResult.second) };
383
384 // derive barycentric coordinates for the contact point relative to the
385 // triangle in the difference shape it lies on
386 const glm::vec3 closestPoint { polytope.getClosestPoint() };
387 const AreaTriangle closestTriangle { polytope.getClosestTriangle() };
388 const glm::mat3 barycentricSolver { glm::inverse(glm::mat3 {
389 closestTriangle.mPoints[0], closestTriangle.mPoints[1], closestTriangle.mPoints[2]
390 }) };
391 const glm::vec3 barycentricCoordinates {
392 barycentricSolver * closestPoint
393 };
394
395 // Use the barycentric coordinates to derive contact point for both shapes
396 const AreaTriangle closestTriangleOne {
398 };
399 const AreaTriangle closestTriangleTwo {
401 };
402 const glm::mat3 barycentricOne {
403 closestTriangleOne.mPoints[0], closestTriangleOne.mPoints[1], closestTriangleOne.mPoints[2]
404 };
405 const glm::mat3 barycentricTwo {
406 closestTriangleTwo.mPoints[0], closestTriangleTwo.mPoints[1], closestTriangleTwo.mPoints[2]
407 };
408 collisionResult.mContactA.mPoint = barycentricOne * barycentricCoordinates;
409 collisionResult.mContactB.mPoint = barycentricTwo * barycentricCoordinates;
410
411 // build tangent vectors to the normal (used for friction calculations)
412 const auto tangentPair { deriveTangents(closestPoint) };
413 collisionResult.mContactB.mNormal = glm::normalize(closestPoint);
414 collisionResult.mContactB.mTangent1 = glm::normalize(tangentPair.first);
415 collisionResult.mContactB.mTangent2 = glm::normalize(tangentPair.second);
416 collisionResult.mContactA.mNormal = -collisionResult.mContactB.mNormal;
417 collisionResult.mContactA.mTangent1 = -collisionResult.mContactB.mTangent1;
418 collisionResult.mContactA.mTangent2 = -collisionResult.mContactB.mTangent2;
419
420 // set penetration depth value for both contacts
421 collisionResult.mContactA.mPenetrationDepth = collisionResult.mContactB.mPenetrationDepth = glm::length(closestPoint);
422
423 return collisionResult;
424 }
425
426 template <typename T, typename U>
427 inline Polytope buildPolytope(const T& one, const U& two, const Simplex& simplex) {
428 Polytope polytope { simplex };
429 glm::vec3 searchDirection { polytope.getNextSearch() };
430 assert(squareDistance(searchDirection) > 0 && "Search direction should always be non-zero");
431 assert(isFinite(searchDirection) && "Search direction should always be finite");
432
433 // Compute first support point
434 std::tuple<glm::vec3, glm::vec3, glm::vec3> differenceFull { minkowskiDifferenceFull(one, two, searchDirection) };
435
436 // These reference types can be thought of as "views" into the
437 // differenceFull struct
438 const glm::vec3& supportA { std::get<1>(differenceFull) };
439 const glm::vec3& supportB { std::get<2>(differenceFull) };
440 const glm::vec3& difference { std::get<0>(differenceFull) };
441
442 // Keep building out the expanding polytope until either the minimum translation vector is found or we run out of iterations
443 uint8_t iterationsLeft { kMaxIterations };
444 while(polytope.append(difference, supportA, supportB) && iterationsLeft--) {
445 searchDirection = polytope.getNextSearch();
446 differenceFull = minkowskiDifferenceFull(one, two, searchDirection);
447 }
448
449 return polytope;
450 }
451}
452
453#endif
An object containing a coarse simplified representation, AABB, of spatially queryable objects.
Definition types.hpp:1138
Primitive for EPA algorithm.
Definition types.hpp:708
AreaTriangle getClosestTriangle() const
Returns the closest polytope triangle.
Definition types.cpp:88
AreaTriangle getClosestTriangleSupportB() const
Gets the points of shape B that were responsible for generating the closest triangle of the polytope.
Definition types.cpp:106
bool append(const glm::vec3 &newPoint, const glm::vec3 &supportA, const glm::vec3 &supportB)
Appends a new point, replacing the topmost triangle in the polygon with 3 more triangles including th...
Definition types.cpp:138
glm::vec3 getClosestPoint() const
Returns the closest point to the origin on the triangle face closest to the origin.
Definition types.cpp:74
AreaTriangle getClosestTriangleSupportA() const
Gets the points of shape A that were responsible for generating the closest triangle of the polytope.
Definition types.cpp:97
glm::vec3 getNextSearch() const
Returns the next search direction for a point to add to the polytope.
Definition types.cpp:63
bool contains(const glm::vec3 &point, const ObjectBounds &bounds)
Returns whether point is contained by bounds.
Definition math.cpp:289
std::pair< bool, glm::vec3 > computeIntersection(const Ray &ray, const Plane &plane)
Returns a bool-vector pair, with bool indicating whether a point of intersection was found,...
Definition math.cpp:44
glm::vec3 minkowskiDifference(const T &one, const U &two, const glm::vec3 &along)
Closely associated with GJK; finds the Minkowski difference between two shapes situated somewhere in ...
Definition math.hpp:258
std::pair< uint8_t, std::pair< glm::vec3, glm::vec3 > > computeIntersections(const Ray &ray, const AxisAlignedBounds &axisAlignedBounds)
Returns an unsigned int and vector-pair pair, with unsigned indicating whether any and how many point...
Definition math.cpp:127
bool overlaps(const glm::vec3 &point, const ObjectBounds &bounds)
Returns a bool value indicating whether point is contained by bounds.
Definition math.cpp:168
std::pair< glm::vec3, glm::vec3 > deriveTangents(const glm::vec3 &vector)
Returns a pair of tangents to an input vector.
Definition math.hpp:233
std::tuple< glm::vec3, glm::vec3, glm::vec3 > minkowskiDifferenceFull(const T &one, const U &two, const glm::vec3 &along)
Closely associated with GJK; finds the Minkowski difference between two shapes situated somewhere in ...
Definition math.hpp:276
bool isFinite(float number)
Tests whether a given number is finite.
Definition types.hpp:57
Namespace containing all class definitions and functions related to the ToyMaker engine.
Definition application.hpp:24
std::pair< bool, Simplex > gjkOverlaps(const T &one, const U &two)
GJK implementation based on Casey Muratori's video lecture, which is available here.
Definition math.hpp:283
Collision checkCollision(const T &one, const U &two)
Checks whether a pair of convex 3D shapes are colliding, returning information about the collision if...
Definition math.hpp:365
Polytope buildPolytope(const T &one, const U &two, const Simplex &gjkSimplex)
EPA implementation, used to derive contact information for intersections between convex shapes.
Definition math.hpp:427
A set of 3 points located in the world forming a (hopefully sensible) triangle.
Definition types.hpp:392
std::array< glm::vec3, 3 > mPoints
The points of the triangle, where each point has 3 components.
Definition types.hpp:397
Data representing everything about a collision.
Definition types.hpp:888
Contact mContactA
Contact information relative to the first collision shape.
Definition types.hpp:899
Contact mContactB
Contact information relative to the second collision shape.
Definition types.hpp:905
float mPenetrationDepth
The length by which to move this object in the direction of the contact normal to separate the collid...
Definition types.hpp:874
glm::vec3 mTangent1
Worldspace tangent orthogonal to the contact normal and the other contact tangent.
Definition types.hpp:861
glm::vec3 mNormal
Worldspace normal pointing inward from the surface of this shape such that moving in this direction b...
Definition types.hpp:855
glm::vec3 mPoint
The worldspace point on the surface of this shape that made contact with the other shape.
Definition types.hpp:848
glm::vec3 mTangent2
Worldspace tangent orthogonal to the contact normal and the other contact tangent.
Definition types.hpp:867
A component defining the true bounds of a spatially queryable object situated somewhere in the world.
Definition types.hpp:917
A set of numbers describing a plane situated somewhere in the world.
Definition types.hpp:564
A set of numbers describing a ray with its source at some finite point in the world,...
Definition types.hpp:511
Primitive for GJK algorithm.
Definition types.hpp:613
std::pair< bool, glm::vec3 > evaluate()
Tries to find a 3-simplex that encloses the origin.
Definition types.cpp:581
bool append(const glm::vec3 &candidatePoint, const glm::vec3 &supportA, const glm::vec3 &supportB)
Adds a new point to the simplex.
Definition types.hpp:645
uint8_t mNPoints
The number of points in this simplex.
Definition types.hpp:637
std::array< glm::vec3, 4 > mPoints
Points representing the simplex, derived by finding the Minksowski difference of support points on tw...
Definition types.hpp:619
Classes and structs representing data related to the engine's spatial query system (the precursor to ...