123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279 |
- // Copyright (C) 2002-2012 Nikolaus Gebhardt
- // This file is part of the "Irrlicht Engine".
- // For conditions of distribution and use, see copyright notice in irrlicht.h
- #ifndef IRR_TRIANGLE_3D_H_INCLUDED
- #define IRR_TRIANGLE_3D_H_INCLUDED
- #include "vector3d.h"
- #include "line3d.h"
- #include "plane3d.h"
- #include "aabbox3d.h"
- namespace irr
- {
- namespace core
- {
- //! 3d triangle template class for doing collision detection and other things.
- template <class T>
- class triangle3d
- {
- public:
- //! Constructor for an all 0 triangle
- triangle3d() {}
- //! Constructor for triangle with given three vertices
- triangle3d(const vector3d<T>& v1, const vector3d<T>& v2, const vector3d<T>& v3) : pointA(v1), pointB(v2), pointC(v3) {}
- //! Equality operator
- bool operator==(const triangle3d<T>& other) const
- {
- return other.pointA==pointA && other.pointB==pointB && other.pointC==pointC;
- }
- //! Inequality operator
- bool operator!=(const triangle3d<T>& other) const
- {
- return !(*this==other);
- }
- //! Determines if the triangle is totally inside a bounding box.
- /** \param box Box to check.
- \return True if triangle is within the box, otherwise false. */
- bool isTotalInsideBox(const aabbox3d<T>& box) const
- {
- return (box.isPointInside(pointA) &&
- box.isPointInside(pointB) &&
- box.isPointInside(pointC));
- }
- //! Determines if the triangle is totally outside a bounding box.
- /** \param box Box to check.
- \return True if triangle is outside the box, otherwise false. */
- bool isTotalOutsideBox(const aabbox3d<T>& box) const
- {
- return ((pointA.X > box.MaxEdge.X && pointB.X > box.MaxEdge.X && pointC.X > box.MaxEdge.X) ||
- (pointA.Y > box.MaxEdge.Y && pointB.Y > box.MaxEdge.Y && pointC.Y > box.MaxEdge.Y) ||
- (pointA.Z > box.MaxEdge.Z && pointB.Z > box.MaxEdge.Z && pointC.Z > box.MaxEdge.Z) ||
- (pointA.X < box.MinEdge.X && pointB.X < box.MinEdge.X && pointC.X < box.MinEdge.X) ||
- (pointA.Y < box.MinEdge.Y && pointB.Y < box.MinEdge.Y && pointC.Y < box.MinEdge.Y) ||
- (pointA.Z < box.MinEdge.Z && pointB.Z < box.MinEdge.Z && pointC.Z < box.MinEdge.Z));
- }
- //! Get the closest point on a triangle to a point on the same plane.
- /** \param p Point which must be on the same plane as the triangle.
- \return The closest point of the triangle */
- core::vector3d<T> closestPointOnTriangle(const core::vector3d<T>& p) const
- {
- const core::vector3d<T> rab = line3d<T>(pointA, pointB).getClosestPoint(p);
- const core::vector3d<T> rbc = line3d<T>(pointB, pointC).getClosestPoint(p);
- const core::vector3d<T> rca = line3d<T>(pointC, pointA).getClosestPoint(p);
- const T d1 = rab.getDistanceFrom(p);
- const T d2 = rbc.getDistanceFrom(p);
- const T d3 = rca.getDistanceFrom(p);
- if (d1 < d2)
- return d1 < d3 ? rab : rca;
- return d2 < d3 ? rbc : rca;
- }
- //! Check if a point is inside the triangle (border-points count also as inside)
- /*
- \param p Point to test. Assumes that this point is already
- on the plane of the triangle.
- \return True if the point is inside the triangle, otherwise false. */
- bool isPointInside(const vector3d<T>& p) const
- {
- const vector3d<f64> af64((f64)pointA.X, (f64)pointA.Y, (f64)pointA.Z);
- const vector3d<f64> bf64((f64)pointB.X, (f64)pointB.Y, (f64)pointB.Z);
- const vector3d<f64> cf64((f64)pointC.X, (f64)pointC.Y, (f64)pointC.Z);
- const vector3d<f64> pf64((f64)p.X, (f64)p.Y, (f64)p.Z);
- return (isOnSameSide(pf64, af64, bf64, cf64) &&
- isOnSameSide(pf64, bf64, af64, cf64) &&
- isOnSameSide(pf64, cf64, af64, bf64));
- }
- //! Check if a point is inside the triangle (border-points count also as inside)
- /** This method uses a barycentric coordinate system.
- It is faster than isPointInside but is more susceptible to floating point rounding
- errors. This will especially be noticeable when the FPU is in single precision mode
- (which is for example set on default by Direct3D).
- \param p Point to test. Assumes that this point is already
- on the plane of the triangle.
- \return True if point is inside the triangle, otherwise false. */
- bool isPointInsideFast(const vector3d<T>& p) const
- {
- const vector3d<T> a = pointC - pointA;
- const vector3d<T> b = pointB - pointA;
- const vector3d<T> c = p - pointA;
- const f64 dotAA = a.dotProduct( a);
- const f64 dotAB = a.dotProduct( b);
- const f64 dotAC = a.dotProduct( c);
- const f64 dotBB = b.dotProduct( b);
- const f64 dotBC = b.dotProduct( c);
- // get coordinates in barycentric coordinate system
- const f64 invDenom = 1/(dotAA * dotBB - dotAB * dotAB);
- const f64 u = (dotBB * dotAC - dotAB * dotBC) * invDenom;
- const f64 v = (dotAA * dotBC - dotAB * dotAC ) * invDenom;
- // We count border-points as inside to keep downward compatibility.
- // Rounding-error also needed for some test-cases.
- return (u > -ROUNDING_ERROR_f32) && (v >= 0) && (u + v < 1+ROUNDING_ERROR_f32);
- }
- //! Get an intersection with a 3d line.
- /** \param line Line to intersect with.
- \param outIntersection Place to store the intersection point, if there is one.
- \return True if there was an intersection, false if not. */
- bool getIntersectionWithLimitedLine(const line3d<T>& line,
- vector3d<T>& outIntersection) const
- {
- return getIntersectionWithLine(line.start,
- line.getVector(), outIntersection) &&
- outIntersection.isBetweenPoints(line.start, line.end);
- }
- //! Get an intersection with a 3d line.
- /** Please note that also points are returned as intersection which
- are on the line, but not between the start and end point of the line.
- If you want the returned point be between start and end
- use getIntersectionWithLimitedLine().
- \param linePoint Point of the line to intersect with.
- \param lineVect Vector of the line to intersect with.
- \param outIntersection Place to store the intersection point, if there is one.
- \return True if there was an intersection, false if there was not. */
- bool getIntersectionWithLine(const vector3d<T>& linePoint,
- const vector3d<T>& lineVect, vector3d<T>& outIntersection) const
- {
- if (getIntersectionOfPlaneWithLine(linePoint, lineVect, outIntersection))
- return isPointInside(outIntersection);
- return false;
- }
- //! Calculates the intersection between a 3d line and the plane the triangle is on.
- /** \param lineVect Vector of the line to intersect with.
- \param linePoint Point of the line to intersect with.
- \param outIntersection Place to store the intersection point, if there is one.
- \return True if there was an intersection, else false. */
- bool getIntersectionOfPlaneWithLine(const vector3d<T>& linePoint,
- const vector3d<T>& lineVect, vector3d<T>& outIntersection) const
- {
- // Work with f64 to get more precise results (makes enough difference to be worth the casts).
- const vector3d<f64> linePointf64(linePoint.X, linePoint.Y, linePoint.Z);
- const vector3d<f64> lineVectf64(lineVect.X, lineVect.Y, lineVect.Z);
- vector3d<f64> outIntersectionf64;
- const core::triangle3d<irr::f64> trianglef64(vector3d<f64>((f64)pointA.X, (f64)pointA.Y, (f64)pointA.Z)
- ,vector3d<f64>((f64)pointB.X, (f64)pointB.Y, (f64)pointB.Z)
- , vector3d<f64>((f64)pointC.X, (f64)pointC.Y, (f64)pointC.Z));
- const vector3d<irr::f64> normalf64 = trianglef64.getNormal().normalize();
- f64 t2;
- if ( core::iszero ( t2 = normalf64.dotProduct(lineVectf64) ) )
- return false;
- const f64 d = trianglef64.pointA.dotProduct(normalf64);
- const f64 t = -(normalf64.dotProduct(linePointf64) - d) / t2;
- outIntersectionf64 = linePointf64 + (lineVectf64 * t);
- outIntersection.X = (T)outIntersectionf64.X;
- outIntersection.Y = (T)outIntersectionf64.Y;
- outIntersection.Z = (T)outIntersectionf64.Z;
- return true;
- }
- //! Get the normal of the triangle.
- /** Please note: The normal is not always normalized. */
- vector3d<T> getNormal() const
- {
- return (pointB - pointA).crossProduct(pointC - pointA);
- }
- //! Test if the triangle would be front or backfacing from any point.
- /** Thus, this method assumes a camera position from which the
- triangle is definitely visible when looking at the given direction.
- Do not use this method with points as it will give wrong results!
- \param lookDirection Look direction.
- \return True if the plane is front facing and false if it is backfacing. */
- bool isFrontFacing(const vector3d<T>& lookDirection) const
- {
- const vector3d<T> n = getNormal().normalize();
- const f32 d = (f32)n.dotProduct(lookDirection);
- return F32_LOWER_EQUAL_0(d);
- }
- //! Get the plane of this triangle.
- plane3d<T> getPlane() const
- {
- return plane3d<T>(pointA, pointB, pointC);
- }
- //! Get the area of the triangle
- T getArea() const
- {
- return (pointB - pointA).crossProduct(pointC - pointA).getLength() * 0.5f;
- }
- //! sets the triangle's points
- void set(const core::vector3d<T>& a, const core::vector3d<T>& b, const core::vector3d<T>& c)
- {
- pointA = a;
- pointB = b;
- pointC = c;
- }
- //! the three points of the triangle
- vector3d<T> pointA;
- vector3d<T> pointB;
- vector3d<T> pointC;
- private:
- // Using f64 instead of <T> to avoid integer overflows when T=int (maybe also less floating point troubles).
- bool isOnSameSide(const vector3d<f64>& p1, const vector3d<f64>& p2,
- const vector3d<f64>& a, const vector3d<f64>& b) const
- {
- vector3d<f64> bminusa = b - a;
- const vector3d<f64> cp1 = bminusa.crossProduct(p1 - a);
- const vector3d<f64> cp2 = bminusa.crossProduct(p2 - a);
- f64 res = cp1.dotProduct(cp2);
- if ( res < 0 )
- {
- // This catches some floating point troubles.
- // Unfortunately slightly expensive and we don't really know the best epsilon for iszero.
- const vector3d<f64> cp1n = bminusa.normalize().crossProduct((p1 - a).normalize());
- if (core::iszero(cp1n.X, (f64)ROUNDING_ERROR_f32)
- && core::iszero(cp1n.Y, (f64)ROUNDING_ERROR_f32)
- && core::iszero(cp1n.Z, (f64)ROUNDING_ERROR_f32) )
- {
- res = 0.f;
- }
- }
- return (res >= 0.0f);
- }
- };
- //! Typedef for a f32 3d triangle.
- typedef triangle3d<f32> triangle3df;
- //! Typedef for an integer 3d triangle.
- typedef triangle3d<s32> triangle3di;
- } // end namespace core
- } // end namespace irr
- #endif
|