aboutsummaryrefslogtreecommitdiffstats
path: root/dev/MinGfx/src/ray.h
blob: 4bc3a8cf090f214f281811329002fa3e8e747724 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
/*
 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 <iostream>

#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 &center, 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