Giga Geometry Implementation

This commit is contained in:
2024-04-08 13:27:56 -04:00
parent 0aff68b63e
commit d7b2157b0c
32 changed files with 1235 additions and 126 deletions

View File

@@ -0,0 +1,60 @@
// @file GJK.h
/// Implementation of the Gilbert-Johnson-Keerthi (GJK) convex polyhedron intersection test
#include <J3ML/LinearAlgebra.h>
#include <J3ML/Geometry.h>
#pragma once
namespace J3ML::Algorithms
{
Vector3 UpdateSimplex(Vector3 *s, int &n);
#define SUPPORT(dir, minS, maxS) (a.ExtremePoint(dir, maxS) - b.ExtremePoint(-dir, minS));
template <typename A, typename B>
bool GJKIntersect(const A &a, const B &b)
{
Vector3 support[4];
// Start with an arbitrary point in the Minkowski set shape.
support[0] = a.AnyPointFast() - b.AnyPointFast();
if (support[0].LengthSquared() < 1e-7f) // Robustness check: Test if the first arbitrary point we guessed produced the zero vector we are looking for!
return true;
Vector3 d = -support[0]; // First search direction is straight toward the origin from the found point.
int n = 1; // Stores the current number of points in the search simplex.
int nIterations = 50; // Robustness check: Limit the maximum number of iterations to perform to avoid infinite loop if types A or B are buggy!
while (nIterations-- > 0)
{
// Compute the extreme point to the direction d in the Minkowski set shape.
float maxS, minS;
Vector3 newSupport = SUPPORT(d, minS, maxS);
// If the most extreme point in that search direction did not walk past the origin, then the origin cannot be contained in the Minkowski
// convex shape, and the two convex objects a and b do not share a common point - no intersection!
if (minS + maxS < 0.f)
return false;
// Add the newly evaluated point to the search simplex
assert(n < 4);
support[n++] = newSupport;
// Examine the current simplex, prune a redundant part of it, and produce the next search direction.
d = UpdateSimplex(support, n);
if (n == 0) // Was the origin contained in the current simplex? If so, then the convex shapes a and b do share a common point - intersection!
return true;
}
return false;
}
// This computes GJL intersection, but by first translating both objects to a coordinate frame that is as closely
// centered around world origin as possible, to gain floating point precision.
template <typename A, typename B>
bool FloatingPointOffsetedGJKIntersect(const A &a, const B &b)
{
AABB ab = a.MinimalEnclosingAABB();
AABB bb = b.MinimalEnclosingAABB();
Vector3 offset = (Vector3::Min(ab.minPoint, bb.minPoint) + Vector3::Max(ab.maxPoint, bb.maxPoint)) * 0.5f;
const Vector3 floatingPtPrecisionOffset = -offset;
return GJLIntersect(a.Translated(floatingPtPrecisionOffset), b.Translated(floatingPtPrecisionOffset));
}
}

View File

@@ -25,31 +25,23 @@ namespace J3ML::Geometry
public:
Vector3 minPoint;
Vector3 maxPoint;
public:
static int NumFaces() { return 6; }
static int NumEdges() { return 12; }
static int NumVertices() { return 8; }
public:
AABB();
AABB(const Vector3& min, const Vector3& max);
Vector3 HalfDiagonal() const { return HalfSize(); }
static int NumFaces() { return 6; }
static int NumEdges() { return 12; }
static int NumVertices() { return 8; }
static AABB FromCenterAndSize(const Vector3 &center, const Vector3 &size);
float MinX() const;
float MinY() const;
float MinZ() const;
float MaxX() const;
float MaxY() const;
float MaxZ() const;
/// Returns the smallest sphere that contains this AABB.
@@ -79,8 +71,7 @@ namespace J3ML::Geometry
Vector3 CornerPoint(int cornerIndex) const;
Vector3 ExtremePoint(const Vector3 &direction) const;
Vector3 ExtremePoint(const Vector3 &direction, float &projectionDistance);
Vector3 ExtremePoint(const Vector3 &direction, float &projectionDistance) const;
Vector3 PointOnEdge(int edgeIndex, float u) const;
@@ -126,8 +117,11 @@ namespace J3ML::Geometry
bool Intersects(const Frustum& frustum) const;
bool Intersects(const Polyhedron& polyhedron) const;
TriangleMesh Triangulate(int numFacesX, int numFacesY, int numFacesZ, bool ccwIsFrontFacing) const;
/// Finds the set intersection of this and the given AABB.
/** @return This function returns the AABB that is contained in both this and the given AABB.
@todo Add Intersection(OBB/Polyhedron). */
AABB Intersection(const AABB& rhs) const;
bool IntersectLineAABB(const Vector3& linePos, const Vector3& lineDir, float tNear, float tFar) const;
void SetFrom(const Vector3 *pVector3, int i);
@@ -154,6 +148,27 @@ namespace J3ML::Geometry
void Enclose(const OBB &obb);
bool TestAxis(const Vector3& axis, const Vector3& v0, const Vector3& v1, const Vector3& v2) const;
bool Intersects(const LineSegment &lineSegment) const;
/// Computes the intersection of a line, ray or line segment and an AABB.
/** Based on "T. Kay, J. Kajiya. Ray Tracing Complex Scenes. SIGGRAPH 1986 vol 20, number 4. pp. 269-"
http://www.siggraph.org/education/materials/HyperGraph/raytrace/rtinter3.htm
@param linePos The starting position of the line.
@param lineDir The direction of the line. This direction vector must be normalized!
@param tNear [in, out] For the test, the input line is treated as a line segment. Pass in the signed distance
from the line origin to the start of the line. For a Line-AABB test, -FLOAT_INF is typically passed here.
For a Ray-AABB test, 0.0f should be inputted. If intersection occurs, the signed distance from line origin
to the line entry point in the AABB is returned here.
@param tFar [in, out] Pass in the signed distance from the line origin to the end of the line. For Line-AABB and
Ray-AABB tests, pass in FLOAT_INF. For a LineSegment-AABB test, pass in the length of the line segment here.
If intersection occurs, the signed distance from line origin to the line exit point in the AABB
is returned here.
@return True if an intersection occurs, false otherwise.
@note This is a low level utility function. It may be more convenient to use one of the AABB::Intersects()
functions instead.
@see Intersects(). */
bool IntersectLineAABB(const Vector3& linePos, const Vector3& lineDir, float tNear, float tFar) const;
bool IntersectLineAABB_CPP(const Vector3 &linePos, const Vector3 &lineDir, float &tNear, float &tFar) const;
};

View File

@@ -24,10 +24,23 @@ namespace J3ML::Geometry
LineSegment l;
// Specifies the radius of this capsule
float r;
public:
Capsule();
Capsule(const LineSegment& endPoints, float radius);
Capsule(const Vector3& bottomPt, const Vector3& topPt, float radius);
/// Quickly returns an arbitrary point inside this Capsule. Used in GJK intersection test.
inline Vector3 AnyPointFast() const { return l.A; }
/// Computes the extreme point of this Capsule in the given direction.
/** An extreme point is a farthest point of this Capsule in the given direction. Given a direction,
this point is not necessarily unique.
@param direction The direction vector of the direction to find the extreme point. This vector may
be unnormalized, but may not be null.
@return The extreme point of this Capsule in the given direction. */
Vector3 ExtremePoint(const Vector3 &direction) const;
Vector3 ExtremePoint(const Vector3 &direction, float &projectionDistance) const;
bool IsDegenerate() const;
float Height() const;
float Diameter() const;

View File

@@ -139,7 +139,23 @@ namespace J3ML::Geometry
Matrix4x4 projectionMatrix;
Matrix4x4 viewProjectionMatrix;
public: /// Methods
Frustum();
Frustum()
: type(FrustumType::Invalid),
pos(Vector3::NaN),
front(Vector3::NaN),
up(Vector3::NaN),
nearPlaneDistance(NAN),
farPlaneDistance(NAN),
worldMatrix(Matrix4x4::NaN),
viewProjectionMatrix(Matrix4x4::NaN)
{
// For conveniency, allow automatic initialization of the graphics API and handedness in use.
// If neither of the #defines are set, user must specify per-instance.
}
/// Quickly returns an arbitrary point inside this Frustum. Used in GJK intersection test.
inline Vector3 AnyPointFast() const { return CornerPoint(0); }
static Frustum CreateFrustumFromCamera(const CoordinateFrame& cam, float aspect, float fovY, float zNear, float zFar);
AABB MinimalEnclosingAABB() const;
@@ -175,7 +191,13 @@ namespace J3ML::Geometry
Vector3 NearPlanePos(float x, float y) const;
Vector3 FarPlanePos(float x, float y) const;
Vector3 WorldRight() const;
Vector3 WorldRight() const
{
if (handedness == FrustumHandedness::Right)
return Vector3::Cross(front, up);
else
return Vector3::Cross(up, front);
}
Plane TopPlane() const;
Plane BottomPlane() const;
@@ -211,5 +233,11 @@ namespace J3ML::Geometry
void ProjectToAxis(const Vector3 &direction, float &outMin, float &outMax) const;
void GetCornerPoints(Vector3 *outPointArray) const;
Vector3 ExtremePoint(const Vector3 &direction, float &projectionDistance) const;
LineSegment Edge(int edgeIndex) const;
bool Intersects(const Line &line) const;
};
}

View File

@@ -25,11 +25,35 @@ namespace J3ML::Geometry
class LineSegment
{
public:
LineSegment();
LineSegment(const Vector3& a, const Vector3& b);
Vector3 A;
Vector3 B;
bool Contains(const Vector3&) const;
public:
LineSegment();
LineSegment(const Vector3& a, const Vector3& b);
/// Computes the closest point on this line segment to the given object.
/** @param d [out] If specified, this parameter receives the normalized distance along
this line segment which specifies the closest point on this line segment to
the specified point.
@return The closest point on this line segment to the given object.
@see Contains(), Distance(), Intersects(). */
Vector3 ClosestPoint(const Vector3 &point) const;
bool Contains(const Vector3& point, float distanceThreshold = 1e-3f) const;
/// Quickly returns an arbitrary point inside this LineSegment. Used in GJK intersection test.
inline Vector3 AnyPointFast() const { return A; }
/// Computes an extreme point of this LineSegment in the given direction.
/** An extreme point is a farthest point along this LineSegment in the given direction. Given a direction,
this point is not necessarily unique.
@param direction The direction vector of the direction to find the extreme point. This vector may
be unnormalized, but may not be null.
@return An extreme point of this LineSegment in the given direction. The returned point is always
either a or b.
@see a, b.*/
Vector3 ExtremePoint(const Vector3 &direction) const;
Vector3 ExtremePoint(const Vector3 &direction, float &projectionDistance) const;
void ProjectToAxis(const Vector3 &direction, float &outMin, float &outMax) const;
Vector3 GetPoint(float d) const;
@@ -60,5 +84,9 @@ namespace J3ML::Geometry
Vector3 ClosestPoint(const LineSegment &other, float &d, float &d2) const;
Vector3 ClosestPoint(const Line &other, float &d, float &d2) const;
bool Intersects(const LineSegment &segment) const;
};
LineSegment operator *(const Matrix4x4 &transform, const LineSegment &l);
}

View File

@@ -39,6 +39,8 @@ namespace J3ML::Geometry {
return aabb;
}
bool Intersects(const LineSegment &lineSegment) const;
Sphere MinimalEnclosingSphere() const;
Sphere MaximalContainedSphere() const;
Vector3 Size() const;
@@ -53,7 +55,7 @@ namespace J3ML::Geometry {
Vector3 CenterPoint() const;
Vector3 Centroid() const;
Vector3 AnyPointFast() const;
Vector3 AnyPointFast() const { return pos; }
float Volume() const;
float SurfaceArea() const;
@@ -96,5 +98,9 @@ namespace J3ML::Geometry {
void SetFrom(const AABB &aabb, const Quaternion &transform);
bool Intersects(const Plane& plane) const;
Matrix4x4 LocalToWorld() const;
Matrix4x4 WorldToLocal() const;
};
}

View File

@@ -80,5 +80,7 @@ namespace J3ML::Geometry
bool Intersects(const Polyhedron &polyhedron) const;
LineSegment Project(const LineSegment &segment);
Vector3 Project(const Vector3 &point) const;
};
}

View File

@@ -11,6 +11,10 @@ namespace J3ML::Geometry {
{
public:
std::vector<Vector3> vertices;
/// Quickly returns an arbitrary point inside this AABB. Used in GJK intersection test.
Vector3 AnyPointFast() const { return !vertices.empty() ? vertices[0] : Vector3::NaN; }
AABB MinimalEnclosingAABB() const;
int NumVertices() const;
Vector3 Vertex(int vertexIndex) const;
@@ -32,9 +36,6 @@ namespace J3ML::Geometry {
bool Intersects(const Frustum &frustum) const;
bool Intersects(const Polyhedron &polyhedron) const;
bool Intersects(const Sphere &sphere) const;
protected:
std::vector<Triangle> Triangulate() const;
/// Tests if this polygon is planar.
/** A polygon is planar if all its vertices lie on the same plane.
@@ -56,8 +57,31 @@ namespace J3ML::Geometry {
bool Contains(const Polygon &worldSpacePolygon, float polygonThickness) const;
bool Contains(const Vector3 &worldSpacePoint, float polygonThicknessSq) const;
bool Contains(const Vector3 &worldSpacePoint, float polygonThicknessSq = 1e-2f) const;
bool Contains(const LineSegment &worldSpaceLineSegment, float polygonThickness) const;
bool Contains(const Triangle &worldSpaceTriangle, float polygonThickness) const;
LineSegment Edge(int i) const;
bool ConvexIntersects(const AABB &aabb) const;
bool ConvexIntersects(const OBB &obb) const;
bool ConvexIntersects(const Frustum &frustum) const;
Vector3 MapFrom2D(const Vector2 &point) const;
bool Intersects2D(const LineSegment &segment) const;
Vector3 ClosestPoint(const LineSegment &lineSegment) const;
Vector3 ClosestPoint(const LineSegment &lineSegment, Vector3 *lineSegmentPt) const;
Vector3 ClosestPoint(const Vector3 &point) const;
protected:
};
}

View File

@@ -66,9 +66,7 @@ namespace J3ML::Geometry
bool Intersects(const Polygon&) const;
bool Intersects(const Frustum&) const;
bool Intersects(const Sphere&) const;
bool Intersects(const Capsule&) const;
float FaceContainmentDistance2D(int i, Vector3 vector3) const;
bool Intersects(const Capsule& capsule) const;
bool IsClosed() const;
@@ -103,11 +101,11 @@ namespace J3ML::Geometry
/// and a negative value if the given point is outside the face. The magnitude of the return value reports a pseudo-distance
/// from the point to the nearest edge of the face polygon. This is used as a robustness/stability criterion to estimate how
/// numerically believable the result is.
float FaceContainmentDistance2D(int faceIndex, const Vector3 &worldSpacePoint, float polygonThickness) const;
float FaceContainmentDistance2D(int faceIndex, const Vector3 &worldSpacePoint, float polygonThickness = 1e-5f) const;
void ProjectToAxis(const Vector3 &direction, float &outMin, float &outMax) const;
Vector3 ClosestPoint(const LineSegment& lineSegment, Vector3 *lineSegmentPt) const;
protected:
private:
};
}

View File

@@ -42,6 +42,7 @@ namespace J3ML::Geometry
explicit Ray(const LineSegment& lineSegment);
bool IsFinite() const;
Vector3 GetPoint(float distance) const;
RaycastResult Cast(const Triangle& target, float maxDistance = 99999999);
RaycastResult Cast(const Plane& target, float maxDistance = 99999999);
RaycastResult Cast(const AABB& target, float maxDistance = 99999999);

View File

@@ -18,8 +18,22 @@ namespace J3ML::Geometry
public:
Vector3 Position;
float Radius;
public:
Sphere() {}
Sphere(const Vector3& pos, float radius) : Position(pos), Radius(radius) {}
/// Quickly returns an arbitrary point inside this Sphere. Used in GJK intersection test.
Vector3 AnyPointFast() const { return Position; }
/// Computes the extreme point of this Sphere in the given direction.
/** An extreme point is a farthest point of this Sphere in the given direction. For
a Sphere, this point is unique.
@param direction The direction vector of the direction to find the extreme point. This vector may
be unnormalized, but may not be null.
@return The extreme point of this Sphere in the given direction. */
Vector3 ExtremePoint(const Vector3 &direction) const;
Vector3 ExtremePoint(const Vector3 &direction, float &projectionDistance) const;
void Translate(const Vector3& offset)
{
Position = Position + offset;
@@ -56,6 +70,7 @@ namespace J3ML::Geometry
{
return Position.DistanceSquared(point) <= Radius*Radius;
}
bool Contains(const Vector3& point, float epsilon) const
{
return Position.DistanceSquared(point) <= Radius*Radius + epsilon;

View File

@@ -2,6 +2,7 @@
#include <J3ML/Geometry/Common.h>
#include <J3ML/LinearAlgebra.h>
#include <cfloat>
namespace J3ML::Geometry
{
@@ -11,15 +12,39 @@ namespace J3ML::Geometry
Vector3 V0;
Vector3 V1;
Vector3 V2;
public:
float DistanceSq(const Vector3 &point) const;
bool Intersects(const AABB& aabb) const;
bool Intersects(const Capsule& capsule) const;
AABB BoundingAABB() const;
bool Contains(const Vector3&) const;
/// Tests if the given object is fully contained inside this triangle.
/** @param triangleThickness An epsilon threshold value to use for this test. triangleThicknessSq is the squared version of this parameter.
This specifies the maximum distance the given object can lie from the plane defined by this triangle.
@see Distance(), Intersects(), ClosestPoint().
@todo Add Triangle::Contains(Circle) and Triangle::Contains(Disc). */
bool Contains(const Vector3& point, float triangleThicknessSq = 1e-5f) const;
bool Contains(const LineSegment& lineSeg, float triangleThickness = 1e-3f) const;
bool Contains(const Triangle& triangle, float triangleThickness = 1e-3f) const;
void ProjectToAxis(const Vector3 &axis, float &dMin, float &dMax) const;
/// Quickly returns an arbitrary point inside this Triangle. Used in GJK intersection test.
inline Vector3 AnyPointFast() const { return V0; }
/// Computes an extreme point of this Triangle in the given direction.
/** An extreme point is a farthest point of this Triangle in the given direction. Given a direction,
this point is not necessarily unique.
@param direction The direction vector of the direction to find the extreme point. This vector may
be unnormalized, but may not be null.
@return An extreme point of this Triangle in the given direction. The returned point is always a
vertex of this Triangle.
@see Vertex(). */
Vector3 ExtremePoint(const Vector3 &direction) const;
Vector3 ExtremePoint(const Vector3 &direction, float &projectionDistance) const;
static float IntersectLineTri(const Vector3 &linePos, const Vector3 &lineDir, const Vector3 &v0, const Vector3 &v1,
const Vector3 &v2, float &u, float &v);

View File

@@ -43,7 +43,7 @@ namespace J3ML::Math
{
bool EqualAbs(float a, float b, float epsilon = 1e-3f);
float RecipFast(float x);
// Coming soon: Units Namespace
// For Dimensional Analysis
/*

View File

@@ -121,10 +121,19 @@ namespace J3ML::LinearAlgebra {
void SetRow(int row, const Vector4& rowVector);
void SetRow(int row, float m_r0, float m_r1, float m_r2, float m_r3);
void SetCol(int col, const Vector3& colVector, float m_c3);
void SetCol(int col, const Vector4& colVector);
void SetCol(int col, float m_c0, float m_c1, float m_c2, float m_c3);
void SetCol(int col, const float *data);
Vector4 GetRow(int index) const;
Vector4 GetColumn(int index) const;
Vector3 GetRow3(int index) const;
Vector3 GetColumn3(int index) const;
Vector4 Col(int i) const;
Vector4 Row(int i) const;
Vector4 Col3(int i) const;
Vector4 Row3(int i) const;
Vector3 GetScale() const
{
@@ -264,9 +273,13 @@ namespace J3ML::LinearAlgebra {
/// Returns true if this matrix is seen to contain a "projective" part,
/// i.e. whether the last row of this matrix differs from [0 0 0 1]
bool ContainsProjection(float epsilon = 1e-3f) const;
void InverseOrthonormal();
protected:
float elems[4][4];
Vector3 TransformDir(float tx, float ty, float tz) const;
};
}

View File

@@ -11,6 +11,8 @@ namespace J3ML::LinearAlgebra {
// A 3D (x, y, z) ordered pair.
class Vector3 {
public:
float x = 0;
float y = 0;
float z = 0;
@@ -36,6 +38,7 @@ public:
Vector3();
// Constructs a new Vector3 with the value (X, Y, Z)
Vector3(float X, float Y, float Z);
Vector3(const Vector2& XY, float Z);
Vector3(const Vector3& rhs); // Copy Constructor
Vector3(Vector3&&) = default; // Move Constructor
/// Constructs this float3 from a C array, to the value (data[0], data[1], data[2]).

View File

@@ -42,7 +42,18 @@ namespace J3ML::LinearAlgebra {
float &operator[](std::size_t index);
bool IsWithinMarginOfError(const Vector4& rhs, float margin=0.0001f) const;
bool IsNormalized(float epsilonSq = 1e-5f) const;
float LengthSqXYZ() const;
bool IsNormalized3(float epsilonSq = 1e-5f) const
{
return std::abs(LengthSqXYZ()-1.f) <= epsilonSq;
}
bool IsNormalized4(float epsilonSq = 1e-5f) const
{
return std::abs(LengthSquared()-1.f) <= epsilonSq;
}
bool IsNormalized(float epsilonSq = 1e-5f) const { return IsNormalized4(epsilonSq); }
bool IsZero(float epsilonSq = 1e-6f) const;
bool IsFinite() const;
bool IsPerpendicular(const Vector4& other, float epsilonSq=1e-5f) const
@@ -74,7 +85,9 @@ namespace J3ML::LinearAlgebra {
// the cross product only has the orthogonality property in 3 and 7 dimensions
// You should consider instead looking at Gram-Schmidt Orthogonalization
// to find orthonormal vectors.
Vector4 Cross(const Vector4& rhs) const;
Vector4 Cross3(const Vector3& rhs) const;
Vector4 Cross3(const Vector4& rhs) const;
Vector4 Cross(const Vector4& rhs) const { return Cross3(rhs); }
Vector4 Normalize() const;
Vector4 Lerp(const Vector4& goal, float alpha) const;