// Copyright (C) 2002-2008 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 triangle3d { public: //! Constructor for an all 0 triangle triangle3d() {} //! Constructor for triangle with given three vertices triangle3d(vector3d v1, vector3d v2, vector3d v3) : pointA(v1), pointB(v2), pointC(v3) {} //! Equality operator bool operator==(const triangle3d& other) const { return other.pointA==pointA && other.pointB==pointB && other.pointC==pointC; } //! Inequality operator bool operator!=(const triangle3d& other) const { return !(*this==other); } //! Determines if the triangle is totally inside a bounding box. //! \param box: Box to check. //! \return Returns true if the triangle is within the box, //! and false otherwise. bool isTotalInsideBox(const aabbox3d& box) const { return (box.isPointInside(pointA) && box.isPointInside(pointB) && box.isPointInside(pointC)); } //! 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 closestPointOnTriangle(const core::vector3d& p) const { const core::vector3d rab = line3d(pointA, pointB).getClosestPoint(p); const core::vector3d rbc = line3d(pointB, pointC).getClosestPoint(p); const core::vector3d rca = line3d(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 //! \param p: Point to test. Assumes that this point is already on the plane //! of the triangle. //! \return Returns true if the point is inside the triangle, otherwise false. bool isPointInside(const vector3d& p) const { return (isOnSameSide(p, pointA, pointB, pointC) && isOnSameSide(p, pointB, pointA, pointC) && isOnSameSide(p, pointC, pointA, pointB)); } //! Check if a point is inside the triangle. This method is an //! implementation of the example used in a paper by Kasper //! Fauerby original written by Keidy from Mr-Gamemaker. //! \param p: Point to test. Assumes that this point is already //! on the plane of the triangle. //! \return Returns true if the point is inside the triangle, //! otherwise false. bool isPointInsideFast(const vector3d& p) const { const vector3d f = pointB - pointA; const vector3d g = pointC - pointA; const f32 a = f.dotProduct(f); const f32 b = f.dotProduct(g); const f32 c = g.dotProduct(g); const vector3d vp = p - pointA; const f32 d = vp.dotProduct(f); const f32 e = vp.dotProduct(g); f32 x = (d*c)-(e*b); f32 y = (e*a)-(d*b); const f32 ac_bb = (a*c)-(b*b); f32 z = x+y-ac_bb; // return sign(z) && !(sign(x)||sign(y)) return (( (IR(z)) & ~((IR(x))|(IR(y))) ) & 0x80000000)!=0; } //! 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 Returns true if there was an intersection, false if not. bool getIntersectionWithLimitedLine(const line3d& line, vector3d& outIntersection) const { return getIntersectionWithLine(line.start, line.getVector(), outIntersection) && outIntersection.isBetweenPoints(line.start, line.end); } //! Returns 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 Returns true if there was an intersection, false if there was not. bool getIntersectionWithLine(const vector3d& linePoint, const vector3d& lineVect, vector3d& 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 Returns true if there was an intersection, false if there was not. bool getIntersectionOfPlaneWithLine(const vector3d& linePoint, const vector3d& lineVect, vector3d& outIntersection) const { const vector3d normal = getNormal().normalize(); T t2; if ( core::iszero ( t2 = normal.dotProduct(lineVect) ) ) return false; T d = pointA.dotProduct(normal); T t = -(normal.dotProduct(linePoint) - d) / t2; outIntersection = linePoint + (lineVect * t); return true; } //! Returns the normal of the triangle. //! Please note: The normal is not normalized. vector3d 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 into //! the given direction. //! Do not use this method with points as it will give wrong results! //! \param lookDirection: Look direction. //! \return Returns true if the plane is front facing and //! false if it is backfacing. bool isFrontFacing(const vector3d& lookDirection) const { const vector3d n = getNormal().normalize(); const f32 d = (f32)n.dotProduct(lookDirection); return F32_LOWER_EQUAL_0(d); } //! Returns the plane of this triangle. plane3d getPlane() const { return plane3d(pointA, pointB, pointC); } //! Returns the area of the triangle T getArea() const { return (pointB - pointA).crossProduct(pointC - pointA).getLength() * 0.5; } //! sets the triangle's points void set(const core::vector3d& a, const core::vector3d& b, const core::vector3d& c) { pointA = a; pointB = b; pointC = c; } //! the three points of the triangle vector3d pointA; vector3d pointB; vector3d pointC; private: bool isOnSameSide(const vector3d& p1, const vector3d& p2, const vector3d& a, const vector3d& b) const { vector3d bminusa = b - a; vector3d cp1 = bminusa.crossProduct(p1 - a); vector3d cp2 = bminusa.crossProduct(p2 - a); return (cp1.dotProduct(cp2) >= 0.0f); } }; //! Typedef for a f32 3d triangle. typedef triangle3d triangle3df; //! Typedef for an integer 3d triangle. typedef triangle3d triangle3di; } // end namespace core } // end namespace irr #endif