diff --git a/include/J3ML/Algorithm/SAT.h b/include/J3ML/Algorithm/SAT.h new file mode 100644 index 0000000..5caa60c --- /dev/null +++ b/include/J3ML/Algorithm/SAT.h @@ -0,0 +1,7 @@ +#pragma once + + +namespace J3ML::Algorithm +{ + +} \ No newline at end of file diff --git a/include/J3ML/Geometry/AABB.h b/include/J3ML/Geometry/AABB.h index 9074c2b..19dc27a 100644 --- a/include/J3ML/Geometry/AABB.h +++ b/include/J3ML/Geometry/AABB.h @@ -8,86 +8,173 @@ namespace J3ML::Geometry { - - using namespace J3ML::LinearAlgebra; using J3ML::Algorithm::RNG; - // A 3D axis-aligned bounding box - // This data structure can be used to represent coarse bounds of objects, in situations where detailed triangle-level - // computations can be avoided. In physics systems, bounding boxes are used as an efficient early-out test for geometry - // intersection queries. - // the 'Axis-aligned' part in the name means that the local axes of this bounding box are restricted to align with the - // axes of the world space coordinate system. This makes computation involving AABB's very fast, since AABB's cannot - // be arbitrarily oriented in the space with respect to each other. - // If you need to represent a box in 3D space with arbitrary orientation, see the class OBB. */ + /// @brief A 3D axis-aligned bounding box. + /// This data structure can be used to represent coarse bounds of objects, in situations where detailed triangle-level + /// computations can be avoided. In physics systems, bounding boxes are used as an efficient early-out test for geometry + /// intersection queries. + /// the 'Axis-aligned' part in the name means that the local axes of this bounding box are restricted to align with the + /// axes of the world space coordinate system. This makes computation involving AABB's very fast, since AABB's cannot + /// be arbitrarily oriented in the space with respect to each other. + /// If you need to represent a box in 3D space with arbitrary orientation, see the class OBB. */ class AABB : public Shape { public: + /// Specifies the minimum extent of this AABB in the world space x, y and z axes. Vector3 minPoint; + /// Specifies the maximum extent of this AABB in the world space x, y and z axes. [similarOverload: minPoint] Vector3 maxPoint; public: static int NumFaces() { return 6; } static int NumEdges() { return 12; } static int NumVertices() { return 8; } public: + /// The default constructor does not initialize any members of this class. + /** This means that the values of the members minPoint and maxPoint are undefined after creating a new AABB using this + default constructor. Remember to assign to them before use. + @see minPoint, maxPoint. */ AABB(); + + /// Constructs this AABB by specifying the minimum and maximum extending corners of the box. + /** @see minPoint, maxPoint. */ AABB(const Vector3& min, const Vector3& max); + /// Constructs this AABB to enclose the given OBB. + /** This constructor computes the optimal minimum volume AABB that encloses the given OBB. + @note Since an AABB cannot generally represent an OBB, this conversion is not exact, but the returned AABB + specifies a larger volume. + @see class OBB. */ + explicit AABB(const OBB &obb); + + /// Constructs this AABB to enclose the given Sphere. + /** @see class Sphere. */ + explicit AABB(const Sphere &s); + Vector3 HalfDiagonal() const { return HalfSize(); } static AABB FromCenterAndSize(const Vector3 ¢er, const Vector3 &size); + + /// Returns the minimum world-space coordinate along the given axis. float MinX() const; - float MinY() const; - float MinZ() const; + float MinY() const; ///< [similarOverload: MinX] + float MinZ() const; ///< [similarOverload: MinX] + + /// Returns the maximum world-space coordinate along the given axis. float MaxX() const; - float MaxY() const; - float MaxZ() const; + float MaxY() const; ///< [similarOverload: MaxX] + float MaxZ() const; ///< [similarOverload: MaxX] /// Returns the smallest sphere that contains this AABB. /// This function computes the minimal volume sphere that contains all the points inside this AABB Sphere MinimalEnclosingSphere() const; + /// [similarOverload: Size] + /** Returns Size()/2. + @see Size(), HalfDiagonal(). */ Vector3 HalfSize() const; /// Returns the largest sphere that can fit inside this AABB /// This function computes the largest sphere that can fit inside this AABB. Sphere MaximalContainedSphere() const; + /// Tests if this AABB is finite. + /** @return True if the member variables of this AABB are valid floats and do not contain NaNs or infs, and false otherwise. + @see IsDegenerate(), minPoint, maxPoint. */ bool IsFinite() const; + /// @return The center point of this AABB. Vector3 Centroid() const; + /// Returns the side lengths of this AABB in x, y and z directions. + /** The returned vector is equal to the diagonal vector of this AABB, i.e. it spans from the + minimum corner of the AABB to the maximum corner of the AABB. + @see HalfSize(), Diagonal(). */ Vector3 Size() const; // Quickly returns an arbitrary point inside this AABB Vector3 AnyPointFast() const; + /// Generates a point inside this AABB. + /** @param x A normalized value between [0,1]. This specifies the point position along the world x axis. + @param y A normalized value between [0,1]. This specifies the point position along the world y axis. + @param z A normalized value between [0,1]. This specifies the point position along the world z axis. + @return A point inside this AABB at point specified by given parameters. + @see Edge(), CornerPoint(), PointOnEdge(), FaceCenterPoint(), FacePoint(). */ Vector3 PointInside(float x, float y, float z) const; // Returns an edge of this AABB LineSegment Edge(int edgeIndex) const; + /// Returns a corner point of this AABB. + /** This function generates one of the eight corner points of this AABB. + @param cornerIndex The index of the corner point to generate, in the range [0, 7]. + The points are returned in the order 0: ---, 1: --+, 2: -+-, 3: -++, 4: +--, 5: +-+, 6: ++-, 7: +++. (corresponding the XYZ axis directions). + @todo Draw which index generates which corner point. + @see PointInside(), Edge(), PointOnEdge(), FaceCenterPoint(), FacePoint(), GetCornerPoints(). */ Vector3 CornerPoint(int cornerIndex) const; + /// Computes an extreme point of this AABB in the given direction. + /** An extreme point is a farthest point of this AABB 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 AABB in the given direction. The returned point is always a + corner point of this AABB. + @see CornerPoint(). */ Vector3 ExtremePoint(const Vector3 &direction) const; Vector3 ExtremePoint(const Vector3 &direction, float &projectionDistance) const; + /// Returns a point on an edge of this AABB. + /** @param edgeIndex The index of the edge to generate a point to, in the range [0, 11]. @todo Document which index generates which one. + @param u A normalized value between [0,1]. This specifies the relative distance of the point along the edge. + @see PointInside(), CornerPoint(), CornerPoint(), FaceCenterPoint(), FacePoint(). */ Vector3 PointOnEdge(int edgeIndex, float u) const; + /// Returns the point at the center of the given face of this AABB. + /** @param faceIndex The index of the AABB face to generate the point at. The valid range is [0, 5]. + This index corresponds to the planes in the order (-X, +X, -Y, +Y, -Z, +Z). + @see PointInside(), CornerPoint(), PointOnEdge(), PointOnEdge(), FacePoint(). */ Vector3 FaceCenterPoint(int faceIndex) const; + /// Generates a point at the surface of the given face of this AABB. + /** @param faceIndex The index of the AABB face to generate the point at. The valid range is [0, 5]. + This index corresponds to the planes in the order (-X, +X, -Y, +Y, -Z, +Z). + @param u A normalized value between [0, 1]. + @param v A normalized value between [0, 1]. + @see PointInside(), CornerPoint(), PointOnEdge(), PointOnEdge(), FaceCenterPoint(). */ Vector3 FacePoint(int faceIndex, float u, float v) const; + /// Returns the surface normal direction vector the given face points towards. + /** @param faceIndex The index of the AABB face to generate the point at. The valid range is [0, 5]. + This index corresponds to the planes in the order (-X, +X, -Y, +Y, -Z, +Z). + @see FacePoint(), FacePlane(). */ Vector3 FaceNormal(int faceIndex) const; + /// Computes the plane equation of the given face of this AABB. + /** @param faceIndex The index of the AABB face. The valid range is [0, 5]. + This index corresponds to the planes in the order (-X, +X, -Y, +Y, -Z, +Z). + @return The plane equation the specified face lies on. The normal of this plane points outwards from this AABB. + @see FacePoint(), FaceNormal(), GetFacePlanes(). */ Plane FacePlane(int faceIndex) const; + void ProjectToAxis(const Vector3 &direction, float &outMin, float &outMax) const; + /// Generates an AABB that encloses the given point set. + /** This function finds the smallest AABB that contains the given set of points. + @param pointArray A pointer to an array of points to enclose inside an AABB. + @param numPoints The number of elements in the pointArray list. + @see SetFrom(). */ static AABB MinimalEnclosingAABB(const Vector3 *pointArray, int numPoints); - float GetVolume() const; - float GetSurfaceArea() const; + AABB MinimalEnclosingAABB() const { return *this;} + /// Computes the volume of this AABB. + /** @see SurfaceArea(), IsDegenerate(). */ + float Volume() const; + /// Computes the surface area of the faces of this AABB. + /** @see Volume(). */ + float SurfaceArea() const; Vector3 GetClosestPoint(const Vector3& point) const; void Translate(const Vector3& offset); @@ -95,9 +182,23 @@ namespace J3ML::Geometry AABB TransformAABB(const Matrix3x3& transform); AABB TransformAABB(const Matrix4x4& transform); AABB TransformAABB(const Quaternion& transform); + /// Applies a transformation to this AABB and returns the resulting OBB. + /** Transforming an AABB produces an oriented bounding box. This set of functions does not apply the transformation + to this object itself, but instead returns the OBB that results in the transformation. + @param transform The transformation to apply to this AABB. This function assumes that this + transformation does not contain shear, nonuniform scaling or perspective properties, i.e. that the fourth + row of the float4x4 is [0 0 0 1]. + @see Translate(), Scale(), TransformAsAABB(), classes float3x3, float3x4, float4x4, Quat. */ OBB Transform(const Matrix3x3& transform) const; OBB Transform(const Matrix4x4& transform) const; OBB Transform(const Quaternion& transform) const; + + /// Tests if the given object is fully contained inside this AABB. + /** This function returns true if the given object lies inside this AABB, and false otherwise. + @note The comparison is performed using less-or-equal, so the faces of this AABB count as being inside, but + due to float inaccuracies, this cannot generally be relied upon. + @todo Add Contains(Circle/Disc/Sphere/Capsule). + @see Distance(), Intersects(), ClosestPoint(). */ bool Contains(const Vector3& point) const; bool Contains(const Vector3& aabbMinPoint, const Vector3& aabbMaxPoint) const; bool Contains(const LineSegment& lineSegment) const; @@ -109,27 +210,93 @@ namespace J3ML::Geometry bool Contains(const Frustum& frustum) const; bool Contains(const Polyhedron& polyhedron) const; bool Contains(const Capsule& capsule) const; - // Tests whether this AABB and the given object intersect. + + /// Tests whether this AABB and the given object intersect. + /** Both objects are treated as "solid", meaning that if one of the objects is fully contained inside + another, this function still returns true. (e.g. in case a line segment is contained inside this AABB, + or this AABB is contained inside a Sphere, etc.) + @param ray The first parameter of this function specifies the other object to test against. + @param dNear [out] If specified, receives the parametric distance along the line denoting where the + line entered this AABB. + @param dFar [out] If specified, receives the parametric distance along the line denoting where the + line exited this AABB. + @see Contains(), Distance(), ClosestPoint(). + @note If you do not need the intersection intervals, you should call the functions without these + parameters in the function signature for optimal performance. + @todo Add Intersects(Circle/Disc). */ bool Intersects(const Ray& ray, float dNear, float dFar) const; bool Intersects(const Capsule& capsule) const; bool Intersects(const Triangle& triangle) const; bool Intersects(const Polygon& polygon) const; bool Intersects(const Frustum& frustum) const; bool Intersects(const Polyhedron& polyhedron) const; + + /** For reference documentation on the Sphere-AABB intersection test, see Christer Ericson's Real-Time Collision Detection, p. 165. [groupSyntax] + @param sphere The first parameter of this function specifies the other object to test against. + @param closestPointOnAABB [out] Returns the closest point on this AABB to the given sphere. This pointer + may be null. */ + bool Intersects(const Sphere &sphere, Vector3 *closestPointOnAABB = 0) const; + + /// Generates an unindexed triangle mesh representation of this AABB. + /** @param numFacesX The number of faces to generate along the X axis. This value must be >= 1. + @param numFacesY The number of faces to generate along the Y axis. This value must be >= 1. + @param numFacesZ The number of faces to generate along the Z axis. This value must be >= 1. + @param outPos [out] An array of size numVertices which will receive a triangle list + of vertex positions. Cannot be null. + @param outNormal [out] An array of size numVertices which will receive vertex normals. + If this parameter is null, vertex normals are not returned. + @param outUV [out] An array of size numVertices which will receive vertex UV coordinates. + If this parameter is null, a UV mapping is not generated. + @param ccwIsFrontFacing If true, then the front-facing direction of the faces will be the sides + with counterclockwise winding order. Otherwise, the faces are generated in clockwise winding order. + The number of vertices that outPos, outNormal and outUV must be able to contain is + (x*y + x*z + y*z)*2*6. If x==y==z==1, then a total of 36 vertices are required. Call + NumVerticesInTriangulation to obtain this value. + @see ToPolyhedron(), ToEdgeList(), NumVerticesInTriangulation(). */ TriangleMesh Triangulate(int numFacesX, int numFacesY, int numFacesZ, bool ccwIsFrontFacing) const; + /// Returns the number of vertices that the Triangulate() function will output with the given subdivision parameters. + /** @see Triangulate(). */ + static int NumVerticesInTriangulation(int numFacesX, int numFacesY, int numFacesZ) + { + return (numFacesX*numFacesY + numFacesX*numFacesZ + numFacesY*numFacesZ)*2*6; + } + + /// Returns the number of vertices that the ToEdgeList() function will output. + /** @see ToEdgeList(). */ + static int NumVerticesInEdgeList() + { + return 4*3*2; + } + /// 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; + /// Sets this AABB to enclose the given set of points. + /** @param pointArray A pointer to an array of points to enclose inside an AABB. + @param numPoints The number of elements in the pointArray list. + @see MinimalEnclosingAABB(). */ void SetFrom(const Vector3 *pVector3, int i); + /// Sets this AABB by specifying its center and size. + /** @param center The center point of this AABB. + @param size A vector that specifies the size of this AABB in x, y and z directions. + @see SetFrom(), FromCenterAndSize(). */ void SetFromCenterAndSize(const Vector3 ¢er, const Vector3 &size); + /// Sets this AABB to enclose the given OBB. + /** This function computes the minimal axis-aligned bounding box for the given oriented bounding box. If the orientation + of the OBB is not aligned with the world axes, this conversion is not exact and loosens the volume of the bounding box. + @param obb The oriented bounding box to convert into this AABB. + @todo Implement SetFrom(Polyhedron). + @see SetCenter(), class OBB. */ void SetFrom(const OBB &obb); + /// Sets this AABB to enclose the given sphere. + /** This function computes the smallest possible AABB (in terms of volume) that contains the given sphere, and stores the result in this structure. */ void SetFrom(const Sphere &s); Vector3 GetRandomPointInside(RNG& rng) const; @@ -137,15 +304,28 @@ namespace J3ML::Geometry Vector3 GetRandomPointOnEdge(RNG& rng) const; Vector3 GetRandomCornerPoint(RNG& rng) const; + /// Sets this structure to a degenerate AABB that does not have any volume. + /** This function is useful for initializing the AABB to "null" before a loop of calls to Enclose(), + which incrementally expands the bounds of this AABB to enclose the given objects. + @see Enclose(). */ void SetNegativeInfinity(); + /// Expands this AABB to enclose the given object. + /** This function computes an AABB that encloses both this AABB and the specified object, and stores the resulting + AABB into this. + @note The generated AABB is not necessarily the optimal enclosing AABB for this AABB and the given object. */ void Enclose(const Vector3 &point); - void Enclose(const Vector3 &aabbMinPt, const Vector3 &aabbMaxPt); - void Enclose(const LineSegment &lineSegment); - void Enclose(const OBB &obb); + void Enclose(const Sphere &sphere); + void Enclose(const Triangle &triangle); + void Enclose(const Capsule &capsule); + void Enclose(const Frustum &frustum); + void Enclose(const Polygon &polygon); + void Enclose(const Polyhedron &polyhedron); + void Enclose(const Vector3 *pointArray, int numPoints); + bool TestAxis(const Vector3& axis, const Vector3& v0, const Vector3& v1, const Vector3& v2) const; diff --git a/include/J3ML/Geometry/Capsule.h b/include/J3ML/Geometry/Capsule.h index 31c59ca..0bdcee6 100644 --- a/include/J3ML/Geometry/Capsule.h +++ b/include/J3ML/Geometry/Capsule.h @@ -2,7 +2,7 @@ #include "LineSegment.h" #include "Shape.h" -#include +#include #include namespace J3ML::Geometry @@ -16,7 +16,7 @@ namespace J3ML::Geometry b = std::move(temp); } - using namespace LinearAlgebra; + /// A 3D cylinder with spherical ends. class Capsule : public Shape { public: @@ -25,13 +25,39 @@ namespace J3ML::Geometry // Specifies the radius of this capsule float r; public: + /// The default constructor does not initialize any members of this class. + /** This means that the values of the members l and r are both undefined after creating a new capsule using + this default constructor. Remember to assign to them before use. + @see l, r. */ Capsule(); + /// Constructs a new capsule by explicitly specifying the member variables. + /** @param endPoints Specifies the line segment of the capsule. + @param radius Specifies the size of this capsule. + @see l, r. */ Capsule(const LineSegment& endPoints, float radius); + /// Constructs a new capsule by explicitly specifying the member variables. + /** This constructor is equivalent to calling Capsule(LineSegment(bottomPoint, topPoint), radius), but provided + here for conveniency. + @see l, r. */ 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; } + /// Generates a point that perhaps lies inside this capsule. + /** @param height A normalized value between [0,1]. This specifies the point position along the height line of this capsule. + @param x A normalized value between [0,1]. This specifies the x coordinate on the plane of the circle cross-section specified by l. + @param y A normalized value between [0,1]. This specifies the y coordinate on the plane of the circle cross-section specified by l. + @note This function will generate points uniformly, but they do not necessarily lie inside the capsule. + @see PointInside(). */ + Vector3 UniformPointPerhapsInside(float height, float x, float y) const; + + /// Returns the Sphere defining the 'bottom' section of this Capsule (corresponding to the endpoint l.a) + Sphere SphereA() const; + + /// Returns the Sphere defining the 'top' section of this Capsule (corresponding to the endpoint l.b) + Sphere SphereB() const; + /// 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. @@ -40,40 +66,135 @@ namespace J3ML::Geometry @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; - + /// Tests if this Capsule is degenerate. + /** @return True if this Capsule does not span a strictly positive volume. */ bool IsDegenerate() const; + /// Computes the total height of this capsule, i.e. LineLength() + Diameter(). + /** + @see LineLength(). */ float Height() const; + /// Computes the diameter of this capsule. float Diameter() const; + /// Returns the bottom-most point of this Capsule. + /** + @note The bottom-most point is only a naming convention, and does not correspond to the bottom-most point along any world axis. The returned + point is simply the point at the far end of this Capsule where the point l.a resides. + @note The bottom-most point of the capsule is different than the point l.a. The returned point is the point at the very far + edge of this capsule, and does not lie on the internal line. See the attached diagram. + @see Top(), l. */ Vector3 Bottom() const; + /// Returns the center point of this Capsule. + /** + @return The point (l.a + l.b) / 2. This point is the center of mass for this capsule. + @see l, Bottom(), Top(). */ Vector3 Center() const; - Vector3 Centroid() const; + Vector3 Centroid() const; ///< [similarOverload: Center] + + /// Returns the direction from the bottommost point towards the topmost point of this Capsule. + /** + @return The normalized direction vector from l.a to l.b. + @see l. */ + Vector3 UpDirection() const; + + /// Computes the volume of this Capsule. + /** @return pi * r^2 * |b-a| + 4 * pi * r^2 / 3. + @see SurfaceArea(). */ + float Volume() const; + + /// Computes the surface area of this Capsule. + /** @return 2 * pi * r * |b-a| + 4 * pi * r^2. + @see Volume(). */ + float SurfaceArea() const; + + /// Returns the cross-section circle at the given height of this Capsule. + /** + @param l A normalized parameter between [0,1]. l == 0 returns a degenerate circle of radius 0 at the bottom of this Capsule, and l == 1 + will return a degenerate circle of radius 0 at the top of this Capsule. */ + //Circle CrossSection(float l) const; + Vector3 ExtremePoint(const Vector3& direction); + + /// Returns the smallest AABB that encloses this capsule. + /** @see MinimalEnclosingOBB(). */ AABB MinimalEnclosingAABB() const; + /// Returns the smallest OBB that encloses this capsule. + /** @see MinimalEnclosingAABB(). */ + OBB MinimalEnclosingOBB() const; + + /// Projects this Capsule onto the given 1D axis direction vector. + /** This function collapses this Capsule onto an 1D axis for the purposes of e.g. separate axis test computations. + The function returns a 1D range [outMin, outMax] denoting the interval of the projection. + @param direction The 1D axis to project to. This vector may be unnormalized, in which case the output + of this function gets scaled by the length of this vector. + @param outMin [out] Returns the minimum extent of this object along the projection axis. + @param outMax [out] Returns the maximum extent of this object along the projection axis. */ void ProjectToAxis(const Vector3 &direction, float &outMin, float &outMax) const; + /// Returns the topmost point of this Capsule. + /** + @note The topmost point is only a naming convention, and does not correspond to the topmost point along any world axis. The returned + point is simply the point at the far end of this Capsule where the point l.b resides. + @note The topmost point of the capsule is different than the point l.b. The returned point is the point at the very far + edge of this capsule, and does not lie on the internal line. See the attached diagram. + @see Bottom(), l. */ + Vector3 Top() const; + + /// Applies a transformation to this capsule. + /** @param transform The transformation to apply to this capsule. This transformation must be + affine, and must contain an orthogonal set of column vectors (may not contain shear or projection). + The transformation can only contain uniform scale, and may not contain mirroring. + @see Translate(), Scale(), classes Matrix3x3, Matrix4x4, Quaternion. */ + void Transform(const Matrix3x3 &transform); + void Transform(const Matrix4x4 &transform); + void Transform(const Quaternion &transform); + + /// Computes the closest point inside this capsule to the given point. + /** If the target point lies inside this capsule, then that point is returned. + @see Distance(), Contains(), Intersects(). + @todo Add ClosestPoint(Line/Ray/LineSegment/Plane/Triangle/Polygon/Circle/Disc/AABB/OBB/Sphere/Capsule/Frustum/Polyhedron). */ + Vector3 ClosestPoint(const Vector3 &targetPoint) const; + + + /// Computes the distance between this capsule and the given object. + /** This function finds the nearest pair of points on this and the given object, and computes their distance. + If the two objects intersect, or one object is contained inside the other, the returned distance is zero. + @todo Add Distance(Triangle/Polygon/Circle/Disc/Capsule). + @see Contains(), Intersects(), ClosestPoint(). */ + float Distance(const Vector3 &point) const; + float Distance(const Plane &plane) const; + float Distance(const Sphere &sphere) const; + float Distance(const Ray &ray) const; + float Distance(const Line &line) const; + float Distance(const LineSegment &lineSegment) const; + float Distance(const Capsule &capsule) const; + + /// Tests if the given object is fully contained inside this capsule. + /** This function returns true if the given object lies inside this capsule, and false otherwise. + @note The comparison is performed using less-or-equal, so the surface of this capsule count as being inside, but + due to float inaccuracies, this cannot generally be relied upon. + @todo Add Contains(Circle/Disc/Sphere/Capsule). + @see Distance(), Intersects(), ClosestPoint(). */ + bool Contains(const Vector3 &point) const; + bool Contains(const LineSegment &lineSegment) const; + bool Contains(const Triangle &triangle) const; + bool Contains(const Polygon &polygon) const; + bool Contains(const AABB &aabb) const; + bool Contains(const OBB &obb) const; + bool Contains(const Frustum &frustum) const; + bool Contains(const Polyhedron &polyhedron) const; + bool Intersects(const Plane &plane) const; - bool Intersects(const Ray &ray) const; - bool Intersects(const Line &line) const; - bool Intersects(const LineSegment &lineSegment) const; - bool Intersects(const AABB &aabb) const; - bool Intersects(const OBB &obb) const; - bool Intersects(const Sphere &sphere) const; - bool Intersects(const Capsule &capsule) const; - bool Intersects(const Triangle &triangle) const; - bool Intersects(const Polygon &polygon) const; - bool Intersects(const Frustum &frustum) const; - bool Intersects(const Polyhedron &polyhedron) const; }; } \ No newline at end of file diff --git a/include/J3ML/Geometry/Frustum.h b/include/J3ML/Geometry/Frustum.h index 0d5f9e6..29cb823 100644 --- a/include/J3ML/Geometry/Frustum.h +++ b/include/J3ML/Geometry/Frustum.h @@ -51,7 +51,7 @@ namespace J3ML::Geometry D3D, }; - /// The handedness rule in J3ML bundles together two different conventions related to the camera: + /// @brief The handedness rule in J3ML bundles together two different conventions related to the camera: /// * the chirality of the world and view spaces, /// * the fixed local front direction of the Frustum. /// @note The world and view spaces are always assumed to the same chirality, meaning that Frustum::ViewMatrix() @@ -74,10 +74,12 @@ namespace J3ML::Geometry Right }; - /// Represents either an orthographic or a perspective viewing frustum. + /// @brief Represents either an orthographic or a perspective viewing frustum. + /// @see FrustumType + /// @see FrustumProjectiveSpace + /// @see FrustumHandedness class Frustum : public Shape { - public: /// Members - + public: // Members /// Specifies whether this frustum is a perspective or an orthographic frustum. FrustumType type; @@ -138,85 +140,228 @@ namespace J3ML::Geometry Matrix4x4 worldMatrix; Matrix4x4 projectionMatrix; Matrix4x4 viewProjectionMatrix; - public: /// Methods - 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. - } + public: + /// The default constructor creates an uninitialized Frustum object. + /** This means that the values of the members type, projectiveSpace, handedness, pos, front, up, nearPlaneDistance, farPlaneDistance, horizontalFov/orthographicWidth and + verticalFov/orthographicHeight are all NaN after creating a new Frustum using this + default constructor. Remember to assign to them before use. + @note As an exception to other classes in MathGeoLib, this class initializes its members to NaNs, whereas the other classes leave the members uninitialized. This difference + is because the Frustum class implements a caching mechanism where world, projection and viewProj matrices are recomputed on demand, which does not work nicely together + if the defaults were uninitialized. + */ + Frustum(); /// 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); + /// Returns the tightest AABB that contains this Frustum. + /** This function computes the optimal minimum volume AABB that encloses this Frustum. + @note Since an AABB cannot generally represent a Frustum, this conversion is not exact, but the returned AABB + specifies a larger volume. + @see MinimalEnclosingOBB(), ToPolyhedron(). */ AABB MinimalEnclosingAABB() const; + /// Returns the tightest OBB that encloses this Frustum. + /** This function computes the optimal minimum volume OBB that encloses this Frustum. + @note If the type of this frustum is Perspective, this conversion is not exact, but the returned OBB specifies + a larger volume. If the type of this Frustum is orthographic, this conversion is exact, since the shape of an + orthographic Frustum is an OBB. + @see MinimalEnclosingAABB(), ToPolyhedron(). */ OBB MinimalEnclosingOBB() const; + /// Sets the type of this Frustum. + /** @note Calling this function recomputes the cached view and projection matrices of this Frustum. + @see SetViewPlaneDistances(), SetFrame(), SetPos(), SetFront(), SetUp(), SetPerspective(), SetOrthographic(), ProjectiveSpace(), Handedness(). */ void SetKind(FrustumProjectiveSpace projectiveSpace, FrustumHandedness handedness); + /// Sets the depth clip distances of this Frustum. + /** @param nearPlaneDistance The z distance from the eye point to the position of the Frustum near clip plane. Always pass a positive value here. + @param farPlaneDistance The z distance from the eye point to the position of the Frustum far clip plane. Always pass a value that is larger than nearClipDistance. + @note Calling this function recomputes the cached projection matrix of this Frustum. + @see SetKind(), SetFrame(), SetPos(), SetFront(), SetUp(), SetPerspective(), SetOrthographic(), NearPlaneDistance(), FarPlaneDistance(). */ void SetViewPlaneDistances(float nearPlaneDistance, float farPlaneDistance); + /// Specifies the full coordinate space of this Frustum in one call. + /** @note Calling this function recomputes the cached world matrix of this Frustum. + @note As a micro-optimization, prefer this function over the individual SetPos/SetFront/SetUp functions if you need to do a batch of two or more changes, to avoid + redundant recomputation of the world matrix. + @see SetKind(), SetViewPlaneDistances(), SetPos(), SetFront(), SetUp(), SetPerspective(), SetOrthographic(), Pos(), Front(), Up(). */ void SetFrame(const Vector3& pos, const Vector3& front, const Vector3& up); + /// Sets the world-space position of this Frustum. + /** @note Calling this function recomputes the cached world matrix of this Frustum. + @see SetKind(), SetViewPlaneDistances(), SetFrame(), SetFront(), SetUp(), SetPerspective(), SetOrthographic(), Pos(). */ void SetPos(const Vector3& pos); + /// Sets the world-space direction the Frustum eye is looking towards. + /** @note Calling this function recomputes the cached world matrix of this Frustum. + @see SetKind(), SetViewPlaneDistances(), SetFrame(), SetPos(), SetUp(), SetPerspective(), SetOrthographic(), Front(). */ void SetFront(const Vector3& front); + /// Sets the world-space camera up direction vector of this Frustum. + /** @note Calling this function recomputes the cached world matrix of this Frustum. + @see SetKind(), SetViewPlaneDistances(), SetFrame(), SetPos(), SetFront(), SetPerspective(), SetOrthographic(), Up(). */ void SetUp(const Vector3& up); + /// Makes this Frustum use a perspective projection formula with the given FOV parameters. + /** A Frustum that uses the perspective projection is shaped like a pyramid that is cut from the top, and has a + base with a rectangular area. + @note Calling this function recomputes the cached projection matrix of this Frustum. + @see SetKind(), SetViewPlaneDistances(), SetFrame(), SetPos(), SetFront(), SetUp(), SetOrthographic(), HorizontalFov(), VerticalFov(), SetHorizontalFovAndAspectRatio(), SetVerticalFovAndAspectRatio(). */ void SetPerspective(float horizontalFov, float verticalFov); + /// Makes this Frustum use an orthographic projection formula with the given FOV parameters. + /** A Frustum that uses the orthographic projection is shaded like a cube (an OBB). + @note Calling this function recomputes the cached projection matrix of this Frustum. + @see SetKind(), SetViewPlaneDistances(), SetFrame(), SetPos(), SetFront(), SetUp(), SetOrthographic(), OrthographicWidth(), OrthographicHeight(). */ void SetOrthographic(float orthographicWidth, float orthographicHeight); + /// Returns the handedness of the projection formula used by this Frustum. + /** @see SetKind(), FrustumHandedness. */ FrustumHandedness Handedness() const { return handedness; } + /// Returns the type of the projection formula used by this Frustum. + /** @see SetPerspective(), SetOrthographic(), FrustumType. */ FrustumType Type() const { return type; } + /// Returns the convention of the post-projective space used by this Frustum. + /** @see SetKind(), FrustumProjectiveSpace. */ FrustumProjectiveSpace ProjectiveSpace() const { return projectiveSpace;} + /// Returns the world-space position of this Frustum. + /** @see SetPos(), Front(), Up(). */ const Vector3 &Pos() const {return pos;} + /// Returns the world-space camera look-at direction of this Frustum. + /** @see Pos(), SetFront(), Up(). */ const Vector3 &Front() const { return front; } + /// Returns the world-space camera up direction of this Frustum. + /** @see Pos(), Front(), SetUp(). */ const Vector3 &Up() const { return up; } + /// Returns the distance from the Frustum eye to the near clip plane. + /** @see SetViewPlaneDistances(), FarPlaneDistance(). */ float NearPlaneDistance() const { return nearPlaneDistance; } + /// Returns the distance from the Frustum eye to the far clip plane. + /** @see SetViewPlaneDistances(), NearPlaneDistance(). */ float FarPlaneDistance() const { return farPlaneDistance;} + /// Returns the horizontal field-of-view used by this Frustum, in radians. + /** @note Calling this function when the Frustum is not set to use perspective projection will return values that are meaningless. + @see SetPerspective(), Type(), VerticalFov(). */ float HorizontalFov() const { return horizontalFov;} + /// Returns the vertical field-of-view used by this Frustum, in radians. + /** @note Calling this function when the Frustum is not set to use perspective projection will return values that are meaningless. + @see SetPerspective(), Type(), HorizontalFov(). */ float VerticalFov() const { return verticalFov;} + /// Returns the world-space width of this Frustum. + /** @note Calling this function when the Frustum is not set to use orthographic projection will return values that are meaningless. + @see SetOrthographic(), Type(), OrthographicHeight(). */ float OrthographicWidth() const { return orthographicWidth; } + /// Returns the world-space height of this Frustum. + /** @note Calling this function when the Frustum is not set to use orthographic projection will return values that are meaningless. + @see SetOrthographic(), Type(), OrthographicWidth(). */ float OrthograhpicHeight() const { return orthographicHeight; } + /// Returns the number of line segment edges that this Frustum is made up of, which is always 12. + /** This function is used in template-based algorithms to provide an unified API for iterating over the features of a Polyhedron. */ int NumEdges() const { return 12; } + /// Returns the aspect ratio of the view rectangle on the near plane. + /** The aspect ratio is the ratio of the width of the viewing rectangle to its height. This can also be computed by + the expression horizontalFov / verticalFov. To produce a proper non-stretched image when rendering, this + aspect ratio should match the aspect ratio of the actual render target (e.g. 4:3, 16:9 or 16:10 in full screen mode). + @see horizontalFov, verticalFov. */ float AspectRatio() const; - + /// Makes this Frustum use a perspective projection formula with the given horizontal FOV parameter and aspect ratio. + /** Specifies the horizontal and vertical field-of-view values for this Frustum based on the given horizontal FOV + and the screen size aspect ratio. + @note Calling this function recomputes the cached projection matrix of this Frustum. + @see SetPerspective(), SetVerticalFovAndAspectRatio(). */ void SetHorizontalFovAndAspectRatio(float horizontalFov, float aspectRatio); - + /// Makes this Frustum use a perspective projection formula with the given vertical FOV parameter and aspect ratio. + /** Specifies the horizontal and vertical field-of-view values for this Frustum based on the given vertical FOV + and the screen size aspect ratio. + @note Calling this function recomputes the cached projection matrix of this Frustum. + @see SetPerspective(), SetHorizontalFovAndAspectRatio(). */ + void SetVerticalFovAndAspectRatio(float verticalFov, float aspectRatio); Vector3 CornerPoint(int cornerIndex) const; Vector3 NearPlanePos(float x, float y) const; Vector3 FarPlanePos(float x, float y) const; - Vector3 WorldRight() const - { - if (handedness == FrustumHandedness::Right) - return Vector3::Cross(front, up); - else - return Vector3::Cross(up, front); - } + /// Computes the direction vector that points logically to the right-hand side of the Frustum. + /** This vector together with the member variables 'front' and 'up' form the orthonormal basis of the view frustum. + @see pos, front. */ + Vector3 WorldRight() const; - Plane TopPlane() const; - Plane BottomPlane() const; - Plane RightPlane() const; + Plane TopPlane() const; ///< [similarOverload: LeftPlane] [hideIndex] + Plane BottomPlane() const; ///< [similarOverload: LeftPlane] [hideIndex] + Plane RightPlane() const; ///< [similarOverload: LeftPlane] [hideIndex] + /// Returns the plane equation of the specified side of this Frustum. + /** The normal vector of the returned plane points outwards from the volume inside the frustum. + This means the negative half-space of the Frustum is the space inside the Frustum. + [indexTitle: Left/Right/Top/BottomPlane] + @see NearPlane(), FarPlane(), GetPlane(), GetPlanes(). */ Plane LeftPlane() const; + /// Computes the plane equation of the far plane of this Frustum. [similarOverload: NearPlane] + /** The normal vector of the returned plane points outwards from the volume inside the frustum, i.e. away from the eye point. + (towards front). This means the negative half-space of the Frustum is the space inside the Frustum. + @see front, FarPlane(), LeftPlane(), RightPlane(), TopPlane(), BottomPlane(), GetPlane(), GetPlanes(). */ Plane FarPlane() const; + /// Computes the plane equation of the near plane of this Frustum. + /** The normal vector of the returned plane points outwards from the volume inside the frustum, i.e. towards the eye point + (towards -front). This means the negative half-space of the Frustum is the space inside the Frustum. + @see front, FarPlane(), LeftPlane(), RightPlane(), TopPlane(), BottomPlane(), GetPlane(), GetPlanes(). */ Plane NearPlane() const; + /// Computes the width of the near plane quad in world space units. + /** @see NearPlaneHeight(). */ float NearPlaneWidth() const; + /// Computes the height of the near plane quad in world space units. + /** @see NearPlaneHeight(). */ float NearPlaneHeight() const; - + /// Moves this Frustum by the given offset vector. + /** @note This function operates in-place. + @param offset The world space offset to apply to the position of this Frustum. + @see Transform(). */ void Translate(const Vector3& offset); + /// Applies a transformation to this Frustum. + /** @param transform The transformation to apply to this Frustum. This transformation must be + * affine, and must contain an orthogoal set of column vectors (may not contain shear or projection). + * The transformation can only contain uniform + * @see Translate(), Scale(), classes Matrix3x3, Matrix4x4, Quaternion + */ void Transform(const Matrix3x3& transform); void Transform(const Matrix4x4& transform); void Transform(const Quaternion& transform); - + /// Converts this Frustum to a Polyhedron. + /** This function returns a Polyhedron representation of this Frustum. This conversion is exact, meaning that the returned + Polyhedron represents exactly the same set of points that this Frustum does. + @see MinimalEnclosingAABB(), MinimalEnclosingOBB(). */ Polyhedron ToPolyhedron() const; + /// Converts this Frustum to a PBVolume. + /** This function returns a plane-bounded volume representation of this Frustum. The conversion is exact, meaning that the + returned PBVolume<6> represents exactly the same set of points that this Frustum does. + @see ToPolyhedron(). */ + //PBVolume<6> ToPBVolume() const; + + /// Tests if the given object is fully contained inside this Frustum. + /** This function returns true if the given object lies inside this Frustum, and false otherwise. + @note The comparison is performed using less-or-equal, so the faces of this Frustum count as being inside, but + due to float inaccuracies, this cannot generally be relied upon. + @todo Add Contains(Circle/Disc/Sphere/Capsule). + @see Distance(), Intersects(), ClosestPoint(). */ + bool Contains(const Vector3 &point) const; + bool Contains(const LineSegment &lineSegment) const; + bool Contains(const Triangle &triangle) const; + bool Contains(const Polygon &polygon) const; + bool Contains(const AABB &aabb) const; + bool Contains(const OBB &obb) const; + bool Contains(const Frustum &frustum) const; + bool Contains(const Polyhedron &polyhedron) const; + + /// Computes the distance between this Frustum and the given object. + /** This function finds the nearest pair of points on this and the given object, and computes their distance. + If the two objects intersect, or one object is contained inside the other, the returned distance is zero. + @todo Add Frustum::Distance(Line/Ray/LineSegment/Plane/Triangle/Polygon/Circle/Disc/AABB/OBB/Capsule/Frustum/Polyhedron). + @see Contains(), Intersects(), ClosestPoint(). */ + float Distance(const Vector3 &point) const; + + /// Tests whether this Frustum and the given object intersect. + /** Both objects are treated as "solid", meaning that if one of the objects is fully contained inside + another, this function still returns true. (e.g. in case a line segment is contained inside this Frustum, + or this Frustum is contained inside a Sphere, etc.) + The first parameter of this function specifies the other object to test against. + @see Contains(), Distance(), ClosestPoint(). + @todo Add Intersects(Circle/Disc). */ bool Intersects(const Ray& ray) const; //bool Intersects(const Line& line) const; bool Intersects(const LineSegment& lineSegment) const; @@ -229,7 +374,13 @@ namespace J3ML::Geometry bool Intersects(const Capsule& obb) const; bool Intersects(const Frustum& plane) const; bool Intersects(const Polyhedron& triangle) const; - + /// Projects this Frustum onto the given 1D axis direction vector. + /** This function collapses this Frustum onto an 1D axis for the purposes of e.g. separate axis test computations. + The function returns a 1D range [outMin, outMax] denoting the interval of the projection. + @param direction The 1D axis to project to. This vector may be unnormalized, in which case the output + of this function gets scaled by the length of this vector. + @param outMin [out] Returns the minimum extent of this object along the projection axis. + @param outMax [out] Returns the maximum extent of this object along the projection axis. */ void ProjectToAxis(const Vector3 &direction, float &outMin, float &outMax) const; void GetCornerPoints(Vector3 *outPointArray) const; @@ -240,4 +391,8 @@ namespace J3ML::Geometry bool Intersects(const Line &line) const; }; + + Frustum operator * (const Matrix3x3& transform, const Frustum& frustum); + Frustum operator * (const Matrix4x4& transform, const Frustum& frustum); + Frustum operator * (const Quaternion& transform, const Frustum& frustum); } \ No newline at end of file diff --git a/include/J3ML/Geometry/KDTree.h b/include/J3ML/Geometry/KDTree.h new file mode 100644 index 0000000..e1f79ba --- /dev/null +++ b/include/J3ML/Geometry/KDTree.h @@ -0,0 +1,12 @@ +#pragma once + + +namespace J3ML::Geometry +{ + + /// A KD-tree accelleration structure for static geometry. + class KdTree + { + + }; +} \ No newline at end of file diff --git a/include/J3ML/LinearAlgebra/Matrix4x4.h b/include/J3ML/LinearAlgebra/Matrix4x4.h index b6cf9fa..ba0f7dd 100644 --- a/include/J3ML/LinearAlgebra/Matrix4x4.h +++ b/include/J3ML/LinearAlgebra/Matrix4x4.h @@ -11,11 +11,10 @@ namespace J3ML::LinearAlgebra { - /// A 4-by-4 matrix for affine transformations and perspective projections of 3D geometry. - /* This matrix can represent the most generic form of transformations for 3D objects, - * including perspective projections, which a 4-by-3 cannot store, - * and translations, which a 3-by-3 cannot represent. - * The elements of this matrix are + /// @brief A 4-by-4 matrix for affine transformations and perspective projections of 3D geometry. + /// This matrix can represent the most generic form of transformations for 3D objects, + /// including perspective projections, which a 4-by-3 cannot store, and translations, which a 3-by-3 cannot represent. + /* The elements of this matrix are * m_00, m_01, m_02, m_03 * m_10, m_11, m_12, m_13 * m_20, m_21, m_22, m_23, @@ -227,9 +226,19 @@ namespace J3ML::LinearAlgebra { static Matrix4x4 D3DPerspProjLH(float nearPlane, float farPlane, float hViewportSize, float vViewportSize); static Matrix4x4 D3DPerspProjRH(float nearPlane, float farPlane, float hViewportSize, float vViewportSize); + /// Computes a left-handled orthographic projection matrix for OpenGL. + /// @note Use the M*v multiplication order to project points with this matrix. static Matrix4x4 OpenGLOrthoProjLH(float n, float f, float h, float v); + /// Computes a right-handled orthographic projection matrix for OpenGL. + /// @note Use the M*v multiplication order to project points with this matrix. static Matrix4x4 OpenGLOrthoProjRH(float n, float f, float h, float v); + + /// Computes a left-handed perspective projection matrix for OpenGL. + /// @note Use the M*v multiplication order to project points with this matrix. static Matrix4x4 OpenGLPerspProjLH(float n, float f, float h, float v); + /// Identical to http://www.opengl.org/sdk/docs/man/xhtml/gluPerspective.xml , except uses viewport sizes instead of FOV to set up the + /// projection matrix. + /// @note Use the M*v multiplication order to project points with this matrix. static Matrix4x4 OpenGLPerspProjRH(float n, float f, float h, float v); Vector4 operator[](int row); diff --git a/include/J3ML/LinearAlgebra/Vector3.h b/include/J3ML/LinearAlgebra/Vector3.h index 956ab33..a4a48d8 100644 --- a/include/J3ML/LinearAlgebra/Vector3.h +++ b/include/J3ML/LinearAlgebra/Vector3.h @@ -8,11 +8,9 @@ namespace J3ML::LinearAlgebra { -// A 3D (x, y, z) ordered pair. +/// A 3D (x, y, z) ordered pair. class Vector3 { public: - - float x = 0; float y = 0; float z = 0; diff --git a/src/J3ML/Geometry/AABB.cpp b/src/J3ML/Geometry/AABB.cpp index 0881e84..e099fcd 100644 --- a/src/J3ML/Geometry/AABB.cpp +++ b/src/J3ML/Geometry/AABB.cpp @@ -197,12 +197,12 @@ namespace J3ML::Geometry { return aabb; } - float AABB::GetVolume() const { + float AABB::Volume() const { Vector3 sz = Size(); return sz.x * sz.y * sz.z; } - float AABB::GetSurfaceArea() const { + float AABB::SurfaceArea() const { Vector3 size = Size(); return 2.f * (size.x*size.y + size.x*size.z + size.y*size.z); } diff --git a/src/J3ML/Geometry/Frustum.cpp b/src/J3ML/Geometry/Frustum.cpp index a2b7cbd..4acf5b4 100644 --- a/src/J3ML/Geometry/Frustum.cpp +++ b/src/J3ML/Geometry/Frustum.cpp @@ -293,4 +293,25 @@ namespace J3ML::Geometry { return this->ToPolyhedron().Intersects(polyhedron); } + + 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. + } + + Vector3 Frustum::WorldRight() const { + if (handedness == FrustumHandedness::Right) + return Vector3::Cross(front, up); + else + return Vector3::Cross(up, front); + } } \ No newline at end of file