diff options
Diffstat (limited to 'dep/include/g3dlite/G3D/Ray.h')
-rw-r--r-- | dep/include/g3dlite/G3D/Ray.h | 327 |
1 files changed, 327 insertions, 0 deletions
diff --git a/dep/include/g3dlite/G3D/Ray.h b/dep/include/g3dlite/G3D/Ray.h new file mode 100644 index 00000000000..3b54f145c33 --- /dev/null +++ b/dep/include/g3dlite/G3D/Ray.h @@ -0,0 +1,327 @@ +/** + @file Ray.h + + Ray class + + @maintainer Morgan McGuire, matrix@graphics3d.com + + @created 2002-07-12 + @edited 2006-02-21 + */ + +#ifndef G3D_RAY_H +#define G3D_RAY_H + +#include "G3D/platform.h" +#include "G3D/Vector3.h" +#include "G3D/Triangle.h" + +namespace G3D { + +/** + A 3D Ray. + */ +class Ray { +private: + Ray(const Vector3& origin, const Vector3& direction) { + this->origin = origin; + this->direction = direction; + } + +public: + Vector3 origin; + + /** + Not unit length + */ + Vector3 direction; + + Ray() : origin(Vector3::zero()), direction(Vector3::zero()) {} + + virtual ~Ray() {} + + /** + Creates a Ray from a origin and a (nonzero) direction. + */ + static Ray fromOriginAndDirection(const Vector3& point, const Vector3& direction) { + return Ray(point, direction); + } + + Ray unit() const { + return Ray(origin, direction.unit()); + } + + /** + Returns the closest point on the Ray to point. + */ + Vector3 closestPoint(const Vector3& point) const { + float t = direction.dot(point - this->origin); + if (t < 0) { + return this->origin; + } else { + return this->origin + direction * t; + } + } + + /** + Returns the closest distance between point and the Ray + */ + float distance(const Vector3& point) const { + return (closestPoint(point) - point).magnitude(); + } + + /** + Returns the point where the Ray and plane intersect. If there + is no intersection, returns a point at infinity. + + Planes are considered one-sided, so the ray will not intersect + a plane where the normal faces in the traveling direction. + */ + Vector3 intersection(const class Plane& plane) const; + + /** + Returns the distance until intersection with the (solid) sphere. + Will be 0 if inside the sphere, inf if there is no intersection. + + The ray direction is <B>not</B> normalized. If the ray direction + has unit length, the distance from the origin to intersection + is equal to the time. If the direction does not have unit length, + the distance = time * direction.length(). + + See also the G3D::CollisionDetection "movingPoint" methods, + which give more information about the intersection. + */ + float intersectionTime(const class Sphere& sphere) const; + + float intersectionTime(const class Plane& plane) const; + + float intersectionTime(const class Box& box) const; + + float intersectionTime(const class AABox& box) const; + + /** + The three extra arguments are the weights of vertices 0, 1, and 2 + at the intersection point; they are useful for texture mapping + and interpolated normals. + */ + float intersectionTime( + const Vector3& v0, const Vector3& v1, const Vector3& v2, + const Vector3& edge01, const Vector3& edge02, + double& w0, double& w1, double& w2) const; + + /** + Ray-triangle intersection for a 1-sided triangle. Fastest version. + @cite http://www.acm.org/jgt/papers/MollerTrumbore97/ + http://www.graphics.cornell.edu/pubs/1997/MT97.html + */ + inline float intersectionTime( + const Vector3& vert0, + const Vector3& vert1, + const Vector3& vert2, + const Vector3& edge01, + const Vector3& edge02) const; + + + inline float intersectionTime( + const Vector3& vert0, + const Vector3& vert1, + const Vector3& vert2) const { + + return intersectionTime(vert0, vert1, vert2, vert1 - vert0, vert2 - vert0); + } + + + inline float intersectionTime( + const Vector3& vert0, + const Vector3& vert1, + const Vector3& vert2, + double& w0, + double& w1, + double& w2) const { + + return intersectionTime(vert0, vert1, vert2, vert1 - vert0, vert2 - vert0, w0, w1, w2); + } + + /* One-sided triangle + */ + inline float intersectionTime(const Triangle& triangle) const { + return intersectionTime( + triangle.vertex(0), triangle.vertex(1), triangle.vertex(2), + triangle.edge01, triangle.edge02); + } + + inline float intersectionTime( + const Triangle& triangle, + double& w0, + double& w1, + double& w2) const { + return intersectionTime(triangle.vertex(0), triangle.vertex(1), triangle.vertex(2), + triangle.edge01, triangle.edge02, w0, w1, w2); + } + + /** Refracts about the normal + using G3D::Vector3::refractionDirection + and bumps the ray slightly from the newOrigin. */ + Ray refract( + const Vector3& newOrigin, + const Vector3& normal, + float iInside, + float iOutside) const; + + /** Reflects about the normal + using G3D::Vector3::reflectionDirection + and bumps the ray slightly from + the newOrigin. */ + Ray reflect( + const Vector3& newOrigin, + const Vector3& normal) const; +}; + + +#define EPSILON 0.000001 +#define CROSS(dest,v1,v2) \ + dest[0]=v1[1]*v2[2]-v1[2]*v2[1]; \ + dest[1]=v1[2]*v2[0]-v1[0]*v2[2]; \ + dest[2]=v1[0]*v2[1]-v1[1]*v2[0]; + +#define DOT(v1,v2) (v1[0]*v2[0]+v1[1]*v2[1]+v1[2]*v2[2]) + +#define SUB(dest,v1,v2) \ + dest[0]=v1[0]-v2[0]; \ + dest[1]=v1[1]-v2[1]; \ + dest[2]=v1[2]-v2[2]; + +inline float Ray::intersectionTime( + const Vector3& vert0, + const Vector3& vert1, + const Vector3& vert2, + const Vector3& edge1, + const Vector3& edge2) const { + + (void)vert1; + (void)vert2; + + // Barycenteric coords + float u, v; + + float tvec[3], pvec[3], qvec[3]; + + // begin calculating determinant - also used to calculate U parameter + CROSS(pvec, direction, edge2); + + // if determinant is near zero, ray lies in plane of triangle + const float det = DOT(edge1, pvec); + + if (det < EPSILON) { + return (float)inf(); + } + + // calculate distance from vert0 to ray origin + SUB(tvec, origin, vert0); + + // calculate U parameter and test bounds + u = DOT(tvec, pvec); + if ((u < 0.0f) || (u > det)) { + // Hit the plane outside the triangle + return (float)inf(); + } + + // prepare to test V parameter + CROSS(qvec, tvec, edge1); + + // calculate V parameter and test bounds + v = DOT(direction, qvec); + if ((v < 0.0f) || (u + v > det)) { + // Hit the plane outside the triangle + return (float)inf(); + } + + + // Case where we don't need correct (u, v): + const float t = DOT(edge2, qvec); + + if (t >= 0.0f) { + // Note that det must be positive + return t / det; + } else { + // We had to travel backwards in time to intersect + return (float)inf(); + } +} + + +inline float Ray::intersectionTime( + const Vector3& vert0, + const Vector3& vert1, + const Vector3& vert2, + const Vector3& edge1, + const Vector3& edge2, + double& w0, + double& w1, + double& w2) const { + + (void)vert1; + (void)vert2; + + // Barycenteric coords + float u, v; + + float tvec[3], pvec[3], qvec[3]; + + // begin calculating determinant - also used to calculate U parameter + CROSS(pvec, direction, edge2); + + // if determinant is near zero, ray lies in plane of triangle + const float det = DOT(edge1, pvec); + + if (det < EPSILON) { + return (float)inf(); + } + + // calculate distance from vert0 to ray origin + SUB(tvec, origin, vert0); + + // calculate U parameter and test bounds + u = DOT(tvec, pvec); + if ((u < 0.0f) || (u > det)) { + // Hit the plane outside the triangle + return (float)inf(); + } + + // prepare to test V parameter + CROSS(qvec, tvec, edge1); + + // calculate V parameter and test bounds + v = DOT(direction, qvec); + if ((v < 0.0f) || (u + v > det)) { + // Hit the plane outside the triangle + return (float)inf(); + } + + float t = DOT(edge2, qvec); + + if (t >= 0) { + const float inv_det = 1.0f / det; + t *= inv_det; + u *= inv_det; + v *= inv_det; + + w0 = (1.0f - u - v); + w1 = u; + w2 = v; + + return t; + } else { + // We had to travel backwards in time to intersect + return (float)inf(); + } +} + +#undef EPSILON +#undef CROSS +#undef DOT +#undef SUB + +}// namespace + +#endif |