From 137bc337706f3b045ea33b16cc6da473d71d862b Mon Sep 17 00:00:00 2001 From: Gawaboumga Date: Wed, 30 Dec 2015 15:33:55 +0100 Subject: [PATCH] Documentation for Ray + New method intersection Ray-Triangle Former-commit-id: 29989ec859e609582fdb60a67a7fb353a03091a0 --- include/Nazara/Math/Ray.hpp | 5 +- include/Nazara/Math/Ray.inl | 380 +++++++++++++++++++++++++++++++++--- 2 files changed, 361 insertions(+), 24 deletions(-) diff --git a/include/Nazara/Math/Ray.hpp b/include/Nazara/Math/Ray.hpp index e0ae68363..c4c53664d 100644 --- a/include/Nazara/Math/Ray.hpp +++ b/include/Nazara/Math/Ray.hpp @@ -24,9 +24,9 @@ namespace Nz public: Ray() = default; Ray(T X, T Y, T Z, T directionX, T directionY, T directionZ); + Ray(const Vector3& origin, const Vector3& direction); Ray(const T origin[3], const T direction[3]); Ray(const Plane& planeOne, const Plane& planeTwo); - Ray(const Vector3& origin, const Vector3& direction); template explicit Ray(const Ray& ray); template explicit Ray(const Vector3& origin, const Vector3& direction); Ray(const Ray& ray) = default; @@ -42,16 +42,17 @@ namespace Nz bool Intersect(const OrientedBox& orientedBox, T* closestHit = nullptr, T* furthestHit = nullptr) const; bool Intersect(const Plane& plane, T* hit = nullptr) const; bool Intersect(const Sphere& sphere, T* closestHit = nullptr, T* furthestHit = nullptr) const; + bool Intersect(const Vector3& firstPoint, const Vector3& secondPoint, const Vector3& thirdPoint, T* hit = nullptr) const; Ray& MakeAxisX(); Ray& MakeAxisY(); Ray& MakeAxisZ(); Ray& Set(T X, T Y, T Z, T directionX, T directionY, T directionZ); + Ray& Set(const Vector3& origin, const Vector3& direction); Ray& Set(const T origin[3], const T direction[3]); Ray& Set(const Plane& planeOne, const Plane& planeTwo); Ray& Set(const Ray& ray); - Ray& Set(const Vector3& origin, const Vector3& direction); template Ray& Set(const Ray& ray); template Ray& Set(const Vector3& origin, const Vector3& direction); diff --git a/include/Nazara/Math/Ray.inl b/include/Nazara/Math/Ray.inl index 9f65b5382..de2b2d726 100644 --- a/include/Nazara/Math/Ray.inl +++ b/include/Nazara/Math/Ray.inl @@ -10,29 +10,77 @@ namespace Nz { + /*! + * \class Nz::Ray + * \brief Math class that represents a ray or a straight line in 3D space + * + * This ray is meant to be understood like origin + lambda * direction, where lambda is a real positive parameter + */ + + /*! + * \brief Constructs a Ray object from its position and direction + * + * \param X X position + * \param Y Y position + * \param Z Z position + * \param DirectionX X component of the vector direction + * \param DirectionY Y component of the vector direction + * \param DirectionY Y component of the vector direction + */ + template Ray::Ray(T X, T Y, T Z, T DirectionX, T DirectionY, T DirectionZ) { Set(X, Y, Z, DirectionX, DirectionY, DirectionZ); } + /*! + * \brief Constructs a Ray object from two Vector3 + * + * \param Origin Vector which represents the origin of the ray + * \param Direction Vector which represents the direction of the ray + */ + + template + Ray::Ray(const Vector3& Origin, const Vector3& Direction) + { + Set(Origin, Direction); + } + + /*! + * \brief Constructs a Ray object from two arrays of three elements + * + * \param Origin[3] Origin[0] is X position, Origin[1] is Y position and Origin[2] is Z position + * \param Direction[3] Direction[0] is X direction, Direction[1] is Y direction and Direction[2] is Z direction + */ + template Ray::Ray(const T Origin[3], const T Direction[3]) { Set(Origin, Direction); } + /*! + * \brief Constructs a Ray object from the intersection of two planes + * + * \param planeOne First plane + * \param planeTwo Second secant plane + * + * \remark Produce a NazaraError if planes are parallel with NAZARA_MATH_SAFE defined + * \throw std::domain_error if NAZARA_MATH_SAFE is defined and planes are parallel + */ + template Ray::Ray(const Plane& planeOne, const Plane& planeTwo) { Set(planeOne, planeTwo); } - template - Ray::Ray(const Vector3& Origin, const Vector3& Direction) - { - Set(Origin, Direction); - } + /*! + * \brief Constructs a Ray object from another type of Ray + * + * \param ray Ray of type U to convert to type T + */ template template @@ -41,6 +89,13 @@ namespace Nz Set(ray); } + /*! + * \brief Constructs a Ray object from two Vector3 from another type of Ray + * + * \param Origin Origin of type U to convert to type T + * \param Direction Direction of type U to convert to type T + */ + template template Ray::Ray(const Vector3& Origin, const Vector3& Direction) @@ -48,6 +103,13 @@ namespace Nz Set(Origin, Direction); } + /*! + * \brief Finds the closest point of the ray from point + * \return The parameter where the point along this ray that is closest to the point provided + * + * \param point The point to get the closest approach to + */ + template T Ray::ClosestPoint(const Vector3& point) const { @@ -55,15 +117,37 @@ namespace Nz T vsq = direction.GetSquaredLength(); T proj = delta.DotProduct(direction); - return proj/vsq; + return proj / vsq; } + /*! + * \brief Gets the point along the ray for this parameter + * \return The point on the ray + * + * \param lambda Parameter to obtain a particular point on the ray + */ + template Vector3 Ray::GetPoint(T lambda) const { return origin + lambda * direction; } + /*! + * \brief Checks whether or not this ray intersects with the BoundingVolume + * \return true if it intersects + * + * \param volume BoundingVolume to check + * \param closestHit Optional argument to get the closest parameter where the intersection is only if it happened + * \param furthestHit Optional argument to get the furthest parameter where the intersection is only if it happened + * + * \remark If BoundingVolume is Extend_Infinite, then closestHit and furthestHit are equal to 0 et infinity + * \remark If BoundingVolume is Extend_Null, then closestHit and furthestHit are unchanged + * \remark If enumeration of BoundingVolume is not defined in Extend, a NazaraError is thrown and closestHit and furthestHit are unchanged + * + * \see Intersect + */ + template bool Ray::Intersect(const BoundingVolume& volume, T* closestHit, T* furthestHit) const { @@ -96,6 +180,17 @@ namespace Nz return false; } + /*! + * \brief Checks whether or not this ray intersects with the Box + * \return true if it intersects + * + * \param box Box to check + * \param closestHit Optional argument to get the closest parameter where the intersection is only if it happened + * \param furthestHit Optional argument to get the furthest parameter where the intersection is only if it happened + * + * \see Intersect + */ + template bool Ray::Intersect(const Box& box, T* closestHit, T* furthestHit) const { @@ -142,6 +237,18 @@ namespace Nz return true; } + /*! + * \brief Checks whether or not this ray intersects with the transform Matrix4 applied to the Box + * \return true if it intersects + * + * \param box Box to check + * \param transform Matrix4 which represents the transformation of the box + * \param closestHit Optional argument to get the closest parameter where the intersection is only if it happened + * \param furthestHit Optional argument to get the furthest parameter where the intersection is only if it happened + * + * \see Intersect + */ + template bool Ray::Intersect(const Box& box, const Matrix4& transform, T* closestHit, T* furthestHit) const { @@ -200,6 +307,17 @@ namespace Nz return true; } + /*! + * \brief Checks whether or not this ray intersects with the OrientedBox + * \return true if it intersects + * + * \param orientedBox OrientedBox to check + * \param closestHit Optional argument to get the closest parameter where the intersection is only if it happened + * \param furthestHit Optional argument to get the furthest parameter where the intersection is only if it happened + * + * \see Intersect + */ + template bool Ray::Intersect(const OrientedBox& orientedBox, T* closestHit, T* furthestHit) const { @@ -212,9 +330,9 @@ namespace Nz // Construction de la matrice de transformation de l'OBB Matrix4 matrix(width.x, height.x, depth.x, corner.x, - width.y, height.y, depth.y, corner.y, - width.z, height.z, depth.z, corner.z, - F(0.0), F(0.0), F(0.0), F(1.0)); + width.y, height.y, depth.y, corner.y, + width.z, height.z, depth.z, corner.z, + F(0.0), F(0.0), F(0.0), F(1.0)); matrix.InverseAffine(); @@ -227,12 +345,22 @@ namespace Nz return tmpRay.Intersect(tmpBox, closestHit, furthestHit); } + /*! + * \brief Checks whether or not this ray intersects with the plane + * \return true if it intersects + * + * \param plane Plane to check + * \param hit Optional argument to get the parameter where the intersection is only if it happened + * + * \see Intersect + */ + template bool Ray::Intersect(const Plane& plane, T* hit) const { T divisor = plane.normal.DotProduct(direction); if (NumberEquals(divisor, F(0.0))) - return false; // perpendicular + return false; // Perpendicular T lambda = -(plane.normal.DotProduct(origin) - plane.distance) / divisor; // The plane is ax + by + cz = d if (lambda < F(0.0)) @@ -244,6 +372,17 @@ namespace Nz return true; } + /*! + * \brief Checks whether or not this ray intersects with the sphere + * \return true if it intersects + * + * \param sphere Sphere to check + * \param closestHit Optional argument to get the closest parameter where the intersection is only if it happened + * \param furthestHit Optional argument to get the furthest parameter where the intersection is only if it happened + * + * \see Intersect + */ + template bool Ray::Intersect(const Sphere& sphere, T* closestHit, T* furthestHit) const { @@ -253,8 +392,8 @@ namespace Nz if (length < F(0.0)) return false; // ray is perpendicular to the vector origin - center - T squaredDistance = sphereRay.GetSquaredLength() - length*length; - T squaredRadius = sphere.radius*sphere.radius; + T squaredDistance = sphereRay.GetSquaredLength() - length * length; + T squaredRadius = sphere.radius * sphere.radius; if (squaredDistance > squaredRadius) return false; // if the ray is further than the radius @@ -274,24 +413,102 @@ namespace Nz return true; } + /*! + * \brief Checks whether or not this ray intersects with the triangle + * \return true if it intersects + * + * \param firstPoint First vertex of the triangle + * \param secondPoint Second vertex of the triangle + * \param thirdPoint Third vertex of the triangle + * \param hit Optional argument to get the parameter where the intersection is only if it happened + * + * \see Intersect + */ + + template + bool Ray::Intersect(const Vector3& firstPoint, const Vector3& secondPoint, const Vector3& thirdPoint, T* hit) const + { + // https://en.wikipedia.org/wiki/M%C3%B6ller%E2%80%93Trumbore_intersection_algorithm + Vector3 firstEdge = secondPoint - firstPoint; + Vector3 secondEdge = thirdPoint - firstPoint; + + Vector3 P = Vector3::CrossProduct(direction, secondEdge); + const T divisor = firstEdge.DotProduct(P); + if (NumberEquals(divisor, F(0.0))) + return false; // Ray lies in plane of triangle + + Vector3 directionToPoint = origin - firstPoint; + T u = directionToPoint.DotProduct(P) / divisor; + if (u < F(0.0) || u > F(1.0)) + return 0; // The intersection lies outside of the triangle + + Vector3 Q = Vector3::CrossProduct(directionToPoint, firstEdge); + T v = directionToPoint.DotProduct(Q) / divisor; + if (v < F(0.0) || u + v > F(1.0)) + return 0; // The intersection lies outside of the triangle + + T t = secondEdge.DotProduct(Q) / divisor; + if (t > F(0.0)) + { + if (hit) + *hit = t; + return true; + } + + return false; + } + + /*! + * \brief Makes the ray with position (0, 0, 0) and direction (1, 0, 0) + * \return A reference to this ray with position (0, 0, 0) and direction (1, 0, 0) + * + * \see AxisX + */ + template Ray& Ray::MakeAxisX() { return Set(Vector3::Zero(), Vector3::UnitX()); } + /*! + * \brief Makes the ray with position (0, 0, 0) and direction (0, 1, 0) + * \return A reference to this ray with position (0, 0, 0) and direction (0, 1, 0) + * + * \see AxisY + */ + template Ray& Ray::MakeAxisY() { return Set(Vector3::Zero(), Vector3::UnitY()); } + /*! + * \brief Makes the ray with position (0, 0, 0) and direction (0, 0, 1) + * \return A reference to this ray with position (0, 0, 0) and direction (0, 0, 1) + * + * \see AxisZ + */ + template Ray& Ray::MakeAxisZ() { return Set(Vector3::Zero(), Vector3::UnitZ()); } + /*! + * \brief Sets the components of the ray with position and direction + * \return A reference to this ray + * + * \param X X position + * \param Y Y position + * \param Z Z position + * \param DirectionX X component of the vector direction + * \param DirectionY Y component of the vector direction + * \param DirectionY Y component of the vector direction + */ + template Ray& Ray::Set(T X, T Y, T Z, T directionX, T directionY, T directionZ) { @@ -301,6 +518,31 @@ namespace Nz return *this; } + /*! + * \brief Sets the components of the ray with position and direction + * \return A reference to this ray + * + * \param Origin Vector which represents the origin of the ray + * \param Direction Vector which represents the direction of the ray + */ + + template + Ray& Ray::Set(const Vector3& Origin, const Vector3& Direction) + { + direction = Direction; + origin = Origin; + + return *this; + } + + /*! + * \brief Sets the components of this ray from two arrays of three elements + * \return A reference to this ray + * + * \param Origin[3] Origin[0] is X position, Origin[1] is Y position and Origin[2] is Z position + * \param Direction[3] Direction[0] is X direction, Direction[1] is Y direction and Direction[2] is Z direction + */ + template Ray& Ray::Set(const T Origin[3], const T Direction[3]) { @@ -310,6 +552,17 @@ namespace Nz return *this; } + /*! + * \brief Sets the components of this ray from the intersection of two planes + * \return A reference to this ray + * + * \param planeOne First plane + * \param planeTwo Second secant plane + * + * \remark Produce a NazaraError if planes are parallel with NAZARA_MATH_SAFE defined + * \throw std::domain_error if NAZARA_MATH_SAFE is defined and planes are parallel + */ + template Ray& Ray::Set(const Plane& planeOne, const Plane& planeTwo) { @@ -321,7 +574,7 @@ namespace Nz #if NAZARA_MATH_SAFE if (NumberEquals(det, F(0.0))) { - String error("Planes are parallel."); + String error("Planes are parallel"); NazaraError(error); throw std::domain_error(error); @@ -338,6 +591,13 @@ namespace Nz return *this; } + /*! + * \brief Sets the components of the ray with components from another + * \return A reference to this ray + * + * \param ray The other ray + */ + template Ray& Ray::Set(const Ray& ray) { @@ -346,14 +606,12 @@ namespace Nz return *this; } - template - Ray& Ray::Set(const Vector3& Origin, const Vector3& Direction) - { - direction = Direction; - origin = Origin; - - return *this; - } + /*! + * \brief Sets the components of the ray from another type of Ray + * \return A reference to this ray + * + * \param ray Ray of type U to convert its components + */ template template @@ -365,6 +623,14 @@ namespace Nz return *this; } + /*! + * \brief Sets the components of the ray from another type of Ray + * \return A reference to this ray + * + * \param Origin Origin of type U to convert to type T + * \param Direction Direction of type U to convert to type T + */ + template template Ray& Ray::Set(const Vector3& Origin, const Vector3& Direction) @@ -375,6 +641,11 @@ namespace Nz return *this; } + /*! + * \brief Gives a string representation + * \return A string representation of the object: "Ray(origin: Vector3(origin.x, origin.y, origin.z), direction: Vector3(direction.x, direction.y, direction.z))" + */ + template String Ray::ToString() const { @@ -383,24 +654,54 @@ namespace Nz return ss << "Ray(origin: " << origin.ToString() << ", direction: " << direction.ToString() << ")"; } + /*! + * \brief Multiplies the direction ray with the lambda to get the point along the ray for this parameter + * \return The point on the ray + * + * \param lambda Parameter to obtain a particular point on the ray + * + * \see GetPoint + */ + template Vector3 Ray::operator*(T lambda) const { return GetPoint(lambda); } + /*! + * \brief Compares the ray to other one + * \return true if the ray are the same + * + * \param rec Other ray to compare with + */ + template bool Ray::operator==(const Ray& ray) const { return direction == ray.direction && origin == ray.origin; } + /*! + * \brief Compares the ray to other one + * \return false if the ray are the same + * + * \param rec Other ray to compare with + */ + template bool Ray::operator!=(const Ray& ray) const { return !operator==(ray); } + /*! + * \brief Shorthand for the ray (0, 0, 0), (1, 0, 0) + * \return A ray with position (0, 0, 0) and direction (1, 0, 0) + * + * \see MakeAxisX + */ + template Ray Ray::AxisX() { @@ -410,6 +711,13 @@ namespace Nz return axis; } + /*! + * \brief Shorthand for the ray (0, 0, 0), (0, 1, 0) + * \return A ray with position (0, 0, 0) and direction (0, 1, 0) + * + * \see MakeAxisY + */ + template Ray Ray::AxisY() { @@ -419,6 +727,13 @@ namespace Nz return axis; } + /*! + * \brief Shorthand for the ray (0, 0, 0), (0, 0, 1) + * \return A ray with position (0, 0, 0) and direction (0, 0, 1) + * + * \see MakeAxisZ + */ + template Ray Ray::AxisZ() { @@ -428,13 +743,34 @@ namespace Nz return axis; } + /*! + * \brief Interpolates the ray to other one with a factor of interpolation + * \return A new ray which is the interpolation of two rectangles + * + * \param from Initial ray + * \param to Target ray + * \param interpolation Factor of interpolation + * + * \remark interpolation is meant to be between 0 and 1, other values are potentially undefined behavior + * + * \see Lerp + */ + template Ray Ray::Lerp(const Ray& from, const Ray& to, T interpolation) { - return Ray(from.origin.Lerp(to.origin, interpolation), from.direction.Lerp(to.direction, interpolation)); + return Ray(Nz::Vector3::Lerp(from.origin, to.origin, interpolation), Nz::Vector3::Lerp(from.direction, to.direction, interpolation)); } } +/*! +* \brief Output operator +* \return The stream +* +* \param out The stream +* \param ray The ray to output +*/ + template std::ostream& operator<<(std::ostream& out, const Nz::Ray& ray) {