/* This file is part of the MinGfx Project. Copyright (c) 2017,2018 Regents of the University of Minnesota. All Rights Reserved. Original Author(s) of this File: Dan Keefe, 2018, University of Minnesota Author(s) of Significant Updates/Modifications to the File: ... */ #ifndef SRC_RAY_H_ #define SRC_RAY_H_ #include #include "aabb.h" #include "point3.h" #include "vector3.h" #include "mesh.h" namespace mingfx { /** Stores the mathematical object of a ray that begins at an origin (a 3D point) and points in a direction (a unit 3D vector). Rays can intersect a variety of other computer graphics objects, such as planes, triangles, spheres, 3D meshes, etc. These intersections can be tested with the Intersect...() methods. The Ray can also be transformed by a Matrix4. Example: ~~~ // Create a pick ray from the mouse position void MyGraphicsApp::OnLeftMouseDown(const Point2 &mouse_in_2d_pixels) { Point2 mouse_in_2d_ndc = PixelsToNormalizedDeviceCoords(mouse_in_2d_pixels); Point3 mouse_in_3d = GfxMath::ScreenToNearPlane(view_matrix, proj_matrix, mouse_in_2d_ndc); Matrix4 camera_matrix = view_matrix.Inverse(); Point3 eye = camera_matrix.ColumnToPoint3(3); Ray pick_ray(eye, mouse_in_3d - eye); // check to see if the ray intersects a sphere float t; Point3 p; if (pick_ray.IntersectSphere(Point3(0,0,0), 2.0, &t, &p)) { std::cout << "Mouse pointing at sphere! Intersection point = " << p << std::endl; } } ~~~ */ class Ray { public: /// Defaults to a ray at the origin and pointing in the -Z direction Ray(); /// Creates a ray from a 3D origin and direction Ray(const Point3 &origin, const Vector3 &direction); /// Ray destructor virtual ~Ray(); /// Check for "equality", taking floating point imprecision into account bool operator==(const Ray& other) const; /// Check for "inequality", taking floating point imprecision into account bool operator!=(const Ray& other) const; /// Returns the length of the direction vector float Length() const; /** Checks to see if the ray intersects a plane defined by a point and a normal. If there was an intersection, true is returned, iTime is set to the intersection time, and iPoint is set to the intersection point. The plane is considered to be 1-sided. That is the intersection will only occur if the ray hits the plane from its front side as determined by the plane's normal. */ bool IntersectPlane(const Point3 &planePt, const Vector3 &planeNormal, float *iTime, Point3 *iPoint) const; /** Checks to see if the ray intersects a triangle defined by the vertices v1, v2, and v3. The vertices must be provided in counter-clockwise order so that the normal of the triangle can be determined via the right-hand rule. The intersection will only happen if the ray hits the front side of the triangle. If there was an intersection, true is returned, iTime is set to the intersection time, and iPoint is set to the intersection point. */ bool IntersectTriangle(const Point3 &v1, const Point3 &v2, const Point3 &v3, float *iTime, Point3 *iPoint) const; /** Checks to see if the ray intersects a quad defined by the vertices v1, v2, v3, and v4. The vertices must be provided in counter-clockwise order so that the normal of the triangle can be determined via the right-hand rule. The intersection will only happen if the ray hits the front side of the triangle. If there was an intersection, true is returned, iTime is set to the intersection time, and iPoint is set to the intersection point. */ bool IntersectQuad(const Point3 &v1, const Point3 &v2, const Point3 &v3, const Point3 &v4, float *iTime, Point3 *iPoint) const; /** Checks to see if the ray intersects a sphere defined by a center point and a radius. If there was an intersection, true is returned, iTime is set to the intersection time, and iPoint is set to the intersection point. */ bool IntersectSphere(const Point3 ¢er, float radius, float *iTime, Point3 *iPoint) const; /** Checks to see if the ray intersects a triangle mesh. This is a brute-force check over each triangle in the mesh. If there was an intersection, true is returned, iTime is set to the intersection time, iPoint is set to the intersection point, and iTriangleID is set to the ID of the closest intersected triangle along the ray. */ bool IntersectMesh(const Mesh &mesh, float *iTime, Point3 *iPoint, int *iTriangleID) const; /** Checks to see if the ray intersects a triangle mesh. This uses a BVH (Bounding Volume Hierarchy) to accelerate the ray-triangle intersection tests. Each mesh can optionally store a BVH. If a BVH has already been calculated for the mesh (done with Mesh::CalculateBVH()), then this function will be much faster than the brute-force IntersectMesh() function. If a BVH has not already been calculated for the mesh, the first call to FastIntersectMesh() will trigger the mesh to create a BVH (not a fast operation) but then subsequent calls to FastIntersectMesh() will be fast. */ bool FastIntersectMesh(Mesh *mesh, float *iTime, Point3 *iPoint, int *iTriangleID) const; /** Checks to see if the ray intersects an AABB (Axis-Aligned Bounding Box). Typically, this is the first step of a more detailed intersection test and we don't care about the actual point of intersection, just whether it intersects or not. So, we don't bother calculating the iPoint. We get the iTime for free though, so we do return that. You can calc the iPoint if you want using: ~~~ float t; if (ray.IntersectAABB(box, &t)) { Point3 iPoint = ray.origin() + t*ray.direction(); } ~~~ */ bool IntersectAABB(const AABB &box, float *iTime) const; /// Returns the origin Point3 origin() const; /// Returns the direction Vector3 direction() const; /// Sets a new origin and direction void set(Point3 newOrigin, Vector3 newDir); private: Point3 p_; Vector3 d_; }; // --- Stream operators --- std::ostream & operator<< ( std::ostream &os, const Ray &r); std::istream & operator>> ( std::istream &is, Ray &r); } // end namespace #endif