LCOV - code coverage report
Current view: top level - ugbase/common/math/misc - tri_box.cpp (source / functions) Coverage Total Hit
Test: coverage.info Lines: 0.0 % 126 0
Test Date: 2025-09-21 23:31:46 Functions: 0.0 % 8 0

            Line data    Source code
       1              : /* Copyright (C) Dan Ginsburg, 2000. 
       2              :  * All rights reserved worldwide.
       3              :  *
       4              :  * This software is provided "as is" without express or implied
       5              :  * warranties. You may freely copy and compile this source into
       6              :  * applications you distribute provided that the copyright text
       7              :  * below is included in the resulting source code, for example:
       8              :  * "Portions Copyright (C) Dan Ginsburg, 2000"
       9              :  */
      10              : /*
      11              :  * This Code has been modified by Sebastian Reiter.
      12              :  * Changes were made to the underlying types and to the 
      13              :  * return-value of TriangleBoxIntersection.
      14              :  * The original source-code was distributed with the book
      15              :  * 'Game Programming Gems'.
      16              :  */
      17              : 
      18              : ////////////////////////////////////////////////////////////////////////////////////////
      19              : //
      20              : //      TriBox.cpp
      21              : //
      22              : //      Description:
      23              : //
      24              : //              This Triangle-Box intersection code was adapated from the Graphics Gems III
      25              : //              source archive 'triangleCube.c' available at:
      26              : //
      27              : //                      http://www.acm.org/tog/GraphicsGems/
      28              : //
      29              : //              The main modification is that the original code performed only intersection
      30              : //              with a voxel (that is, a unit cube centered at the origin).  This code is
      31              : //              modified to take an arbitrary box and triangle and perform the scale/translation
      32              : //              necessary to get the triangle into voxel space.
      33              : //
      34              : //
      35              : #include <math.h>
      36              : #include "../ugmath.h"
      37              : 
      38              : ///
      39              : //      Macros
      40              : //
      41              : #define INSIDE          0
      42              : #define OUTSIDE         1
      43              : 
      44              : #ifndef FALSE
      45              :         #define FALSE 0
      46              : #endif
      47              : #ifndef TRUE
      48              :         #define TRUE 1
      49              : #endif
      50              : 
      51              : #define EPS 10e-5
      52              : #define SIGN3( A ) \
      53              :           (((A).x() < EPS ? 4 : 0) | ((A).x() > -EPS ? 32 : 0) | \
      54              :            ((A).y() < EPS ? 2 : 0) | ((A).y() > -EPS ? 16 : 0) | \
      55              :            ((A).z() < EPS ? 1 : 0) | ((A).z() > -EPS ? 8 : 0))
      56              : 
      57              : #define CROSS( A, B, C ) { \
      58              :   (C).x() =  (A).y() * (B).z() - (A).z() * (B).y(); \
      59              :   (C).y() = -(A).x() * (B).z() + (A).z() * (B).x(); \
      60              :   (C).z() =  (A).x() * (B).y() - (A).y() * (B).x(); \
      61              :    }
      62              : #define SUB( A, B, C ) { \
      63              :   (C).x() =  (A).x() - (B).x(); \
      64              :   (C).y() =  (A).y() - (B).y(); \
      65              :   (C).z() =  (A).z() - (B).z(); \
      66              :    }
      67              : #define LERP( A, B, C) ((B)+(A)*((C)-(B)))
      68              : #define MIN3(a,b,c) ((((a)<(b))&&((a)<(c))) ? (a) : (((b)<(c)) ? (b) : (c)))
      69              : #define MAX3(a,b,c) ((((a)>(b))&&((a)>(c))) ? (a) : (((b)>(c)) ? (b) : (c)))
      70              : 
      71              : 
      72              : namespace ug
      73              : {
      74              : 
      75              : //      a very simple triangle type
      76            0 : struct TRI
      77              : {
      78              :         vector3 m_P[3];
      79              : };
      80              : 
      81              : //////////////////////////////////////////////////////////////////////////////////////////
      82              : //
      83              : //      Private Functions
      84              : //
      85              : //
      86              : 
      87              : ///
      88              : //      FacePlane()
      89              : //   
      90              : //              Which of the six face-plane(s) is point P outside of? 
      91              : //
      92              : static
      93            0 : int FacePlane(const vector3& p)
      94              : {
      95              :         int outcode;
      96              : 
      97              :         outcode = 0;
      98            0 :         if (p.x() >  .5) outcode |= 0x01;
      99            0 :         if (p.x() < -.5) outcode |= 0x02;
     100            0 :         if (p.y() >  .5) outcode |= 0x04;
     101            0 :         if (p.y() < -.5) outcode |= 0x08;
     102            0 :         if (p.z() >  .5) outcode |= 0x10;
     103            0 :         if (p.z() < -.5) outcode |= 0x20;
     104              :    
     105            0 :         return(outcode);
     106              : }
     107              : 
     108              : 
     109              : ///
     110              : //      Bevel2d()
     111              : //
     112              : //       Which of the twelve edge plane(s) is point P outside of? 
     113              : //
     114              : static
     115            0 : int Bevel2d(const vector3& p)
     116              : {
     117              :         int outcode;
     118              : 
     119              :         outcode = 0;
     120            0 :         if ( p.x() + p.y() > 1.0) outcode |= 0x001;
     121            0 :         if ( p.x() - p.y() > 1.0) outcode |= 0x002;
     122            0 :         if (-p.x() + p.y() > 1.0) outcode |= 0x004;
     123            0 :         if (-p.x() - p.y() > 1.0) outcode |= 0x008;
     124            0 :         if ( p.x() + p.z() > 1.0) outcode |= 0x010;
     125            0 :         if ( p.x() - p.z() > 1.0) outcode |= 0x020;
     126            0 :         if (-p.x() + p.z() > 1.0) outcode |= 0x040;
     127            0 :         if (-p.x() - p.z() > 1.0) outcode |= 0x080;
     128            0 :         if ( p.y() + p.z() > 1.0) outcode |= 0x100;
     129            0 :         if ( p.y() - p.z() > 1.0) outcode |= 0x200;
     130            0 :         if (-p.y() + p.z() > 1.0) outcode |= 0x400;
     131            0 :         if (-p.y() - p.z() > 1.0) outcode |= 0x800;
     132            0 :         return(outcode);
     133              : }
     134              : 
     135              : ///
     136              : //      Bevel3d()
     137              : //      
     138              : //              Which of the eight corner plane(s) is point P outside of? 
     139              : //
     140              : static
     141            0 : int Bevel3d(const vector3& p)
     142              : {
     143              :         int outcode;
     144              : 
     145              :         outcode = 0;
     146            0 :         if (( p.x() + p.y() + p.z()) > 1.5) outcode |= 0x01;
     147            0 :         if (( p.x() + p.y() - p.z()) > 1.5) outcode |= 0x02;
     148            0 :         if (( p.x() - p.y() + p.z()) > 1.5) outcode |= 0x04;
     149            0 :         if (( p.x() - p.y() - p.z()) > 1.5) outcode |= 0x08;
     150            0 :         if ((-p.x() + p.y() + p.z()) > 1.5) outcode |= 0x10;
     151            0 :         if ((-p.x() + p.y() - p.z()) > 1.5) outcode |= 0x20;
     152            0 :         if ((-p.x() - p.y() + p.z()) > 1.5) outcode |= 0x40;
     153            0 :         if ((-p.x() - p.y() - p.z()) > 1.5) outcode |= 0x80;
     154            0 :         return(outcode);
     155              : }
     156              : 
     157              : ///
     158              : //      CheckPoint()
     159              : //
     160              : //      Test the point "alpha" of the way from P1 to P2 
     161              : //  See if it is on a face of the cube   
     162              : //      Consider only faces in "mask"                   
     163              : static
     164            0 : int CheckPoint(const vector3& p1, const vector3& p2, number alpha, long mask)
     165              : {
     166              :         vector3 plane_point;
     167              : 
     168            0 :         plane_point.x() = LERP(alpha, p1.x(), p2.x());
     169            0 :         plane_point.y() = LERP(alpha, p1.y(), p2.y());
     170            0 :         plane_point.z() = LERP(alpha, p1.z(), p2.z());
     171            0 :         return(FacePlane(plane_point) & mask);
     172              : }
     173              : 
     174              : ///
     175              : //      CheckLine()
     176              : //
     177              : //              Compute intersection of P1 --> P2 line segment with face planes 
     178              : //              Then test intersection point to see if it is on cube face       
     179              : //              Consider only face planes in "outcode_diff"                     
     180              : //              Note: Zero bits in "outcode_diff" means face line is outside of */
     181              : //
     182              : static
     183            0 : int CheckLine(const vector3& p1, const vector3& p2, int outcode_diff)
     184              : {
     185              : 
     186            0 :    if ((0x01 & outcode_diff) != 0)
     187            0 :       if (CheckPoint(p1,p2,( .5f-p1.x())/(p2.x()-p1.x()),0x3e) == INSIDE) return(INSIDE);
     188            0 :    if ((0x02 & outcode_diff) != 0)
     189            0 :       if (CheckPoint(p1,p2,(-.5f-p1.x())/(p2.x()-p1.x()),0x3d) == INSIDE) return(INSIDE);
     190            0 :    if ((0x04 & outcode_diff) != 0) 
     191            0 :       if (CheckPoint(p1,p2,( .5f-p1.y())/(p2.y()-p1.y()),0x3b) == INSIDE) return(INSIDE);
     192            0 :    if ((0x08 & outcode_diff) != 0) 
     193            0 :       if (CheckPoint(p1,p2,(-.5f-p1.y())/(p2.y()-p1.y()),0x37) == INSIDE) return(INSIDE);
     194            0 :    if ((0x10 & outcode_diff) != 0) 
     195            0 :       if (CheckPoint(p1,p2,( .5f-p1.z())/(p2.z()-p1.z()),0x2f) == INSIDE) return(INSIDE);
     196            0 :    if ((0x20 & outcode_diff) != 0) 
     197            0 :       if (CheckPoint(p1,p2,(-.5f-p1.z())/(p2.z()-p1.z()),0x1f) == INSIDE) return(INSIDE);
     198              :    return(OUTSIDE);
     199              : }
     200              : 
     201              : ///
     202              : //      PointTriangleIntersection()
     203              : //
     204              : //              Test if 3D point is inside 3D triangle 
     205              : static
     206            0 : int PointTriangleIntersection(const vector3& p, const TRI& t)
     207              : {
     208              :         int             sign12,sign23,sign31;
     209              :         vector3 vect12,vect23,vect31,vect1h,vect2h,vect3h;
     210              :         vector3 cross12_1p,cross23_2p,cross31_3p;
     211              : 
     212              :         ///
     213              :         //      First, a quick bounding-box test:                               
     214              :         //  If P is outside triangle bbox, there cannot be an intersection. 
     215              :         //
     216            0 :         if (p.x() > MAX3(t.m_P[0].x(), t.m_P[1].x(), t.m_P[2].x())) return(OUTSIDE);  
     217            0 :         if (p.y() > MAX3(t.m_P[0].y(), t.m_P[1].y(), t.m_P[2].y())) return(OUTSIDE);
     218            0 :         if (p.z() > MAX3(t.m_P[0].z(), t.m_P[1].z(), t.m_P[2].z())) return(OUTSIDE);
     219            0 :         if (p.x() < MIN3(t.m_P[0].x(), t.m_P[1].x(), t.m_P[2].x())) return(OUTSIDE);
     220            0 :         if (p.y() < MIN3(t.m_P[0].y(), t.m_P[1].y(), t.m_P[2].y())) return(OUTSIDE);
     221            0 :         if (p.z() < MIN3(t.m_P[0].z(), t.m_P[1].z(), t.m_P[2].z())) return(OUTSIDE);
     222              : 
     223              :         ///
     224              :         //      For each triangle side, make a vector out of it by subtracting vertexes; 
     225              :         //      make another vector from one vertex to point P.                          
     226              :         //  The crossproduct of these two vectors is orthogonal to both and the      
     227              :         //  signs of its X,Y,Z components indicate whether P was to the inside or    
     228              :         //  to the outside of this triangle side.                                    
     229              :         //
     230            0 :         SUB(t.m_P[0], t.m_P[1], vect12);
     231            0 :         SUB(t.m_P[0], p,                  vect1h);      
     232            0 :         CROSS(vect12, vect1h, cross12_1p)
     233            0 :         sign12 = SIGN3(cross12_1p);      /* Extract X,Y,Z signs as 0..7 or 0...63 integer */
     234              : 
     235            0 :         SUB(t.m_P[1], t.m_P[2], vect23)
     236            0 :         SUB(t.m_P[1],    p, vect2h);
     237            0 :         CROSS(vect23, vect2h, cross23_2p)
     238            0 :         sign23 = SIGN3(cross23_2p);
     239              : 
     240            0 :         SUB(t.m_P[2], t.m_P[0], vect31)
     241            0 :         SUB(t.m_P[2],    p, vect3h);
     242            0 :         CROSS(vect31, vect3h, cross31_3p)
     243            0 :         sign31 = SIGN3(cross31_3p);
     244              : 
     245              :         ///
     246              :         //      If all three crossproduct vectors agree in their component signs, /
     247              :         //  then the point must be inside all three.                           
     248              :         //  P cannot be OUTSIDE all three sides simultaneously.                
     249              :         //
     250            0 :         return (((sign12 & sign23 & sign31) == 0) ? OUTSIDE : INSIDE);
     251              : }
     252              : 
     253              : ///
     254              : //      TriCubeIntersection()
     255              : //
     256              : //              Triangle t is compared with a unit cube,  
     257              : //              centered on the origin.                    
     258              : //              It returns INSIDE (0) or OUTSIDE(1) if t   
     259              : //              Intersects or does not intersect the cube. 
     260              : static
     261            0 : int TriCubeIntersection(const TRI& t)
     262              : {
     263              :         int v1_test,v2_test,v3_test;
     264              :         number d;
     265              :         vector3 vect12,vect13,norm;
     266              :         vector3 hitpp,hitpn,hitnp,hitnn;
     267              : 
     268              :         ///
     269              :         //      First compare all three vertexes with all six face-planes 
     270              :         //      If any vertex is inside the cube, return immediately!     
     271              :         //
     272            0 :         if ((v1_test = FacePlane(t.m_P[0])) == INSIDE) return(INSIDE);
     273            0 :         if ((v2_test = FacePlane(t.m_P[1])) == INSIDE) return(INSIDE);
     274            0 :         if ((v3_test = FacePlane(t.m_P[2])) == INSIDE) return(INSIDE);
     275              : 
     276              :         ///
     277              :         //      If all three vertexes were outside of one or more face-planes,
     278              :         //      return immediately with a trivial rejection!              
     279              :         //
     280            0 :         if ((v1_test & v2_test & v3_test) != 0) return(OUTSIDE);
     281              : 
     282              :         ///
     283              :         //      Now do the same trivial rejection test for the 12 edge planes 
     284              :         //
     285            0 :         v1_test |= Bevel2d(t.m_P[0]) << 8; 
     286            0 :         v2_test |= Bevel2d(t.m_P[1]) << 8; 
     287            0 :         v3_test |= Bevel2d(t.m_P[2]) << 8;
     288            0 :         if ((v1_test & v2_test & v3_test) != 0) return(OUTSIDE);  
     289              : 
     290              :         ///
     291              :         //      Now do the same trivial rejection test for the 8 corner planes
     292              :         //
     293            0 :         v1_test |= Bevel3d(t.m_P[0]) << 24; 
     294            0 :         v2_test |= Bevel3d(t.m_P[1]) << 24; 
     295            0 :         v3_test |= Bevel3d(t.m_P[2]) << 24; 
     296            0 :         if ((v1_test & v2_test & v3_test) != 0) return(OUTSIDE);   
     297              : 
     298              :         ///
     299              :         //      If vertex 1 and 2, as a pair, cannot be trivially rejected 
     300              :         //  by the above tests, then see if the v1-->v2 triangle edge  
     301              :         //      intersects the cube.  Do the same for v1-->v3 and v2-->v3. 
     302              :         //  Pass to the intersection algorithm the "OR" of the outcode 
     303              :         //  bits, so that only those cube faces which are spanned by   
     304              :         //  each triangle edge need be tested.                         
     305              :         //
     306            0 :         if ((v1_test & v2_test) == 0)
     307            0 :                 if (CheckLine(t.m_P[0],t.m_P[1],v1_test|v2_test) == INSIDE) return(INSIDE);
     308            0 :         if ((v1_test & v3_test) == 0)
     309            0 :                 if (CheckLine(t.m_P[0],t.m_P[2],v1_test|v3_test) == INSIDE) return(INSIDE);
     310            0 :         if ((v2_test & v3_test) == 0)
     311            0 :                 if (CheckLine(t.m_P[1],t.m_P[2],v2_test|v3_test) == INSIDE) return(INSIDE);
     312              : 
     313              :         ///
     314              :         //      By now, we know that the triangle is not off to any side,     
     315              :         //       and that its sides do not penetrate the cube.  We must now   
     316              :         //       test for the cube intersecting the interior of the triangle. 
     317              :         //       We do this by looking for intersections between the cube     
     318              :         //       diagonals and the triangle...first finding the intersection  
     319              :         //       of the four diagonals with the plane of the triangle, and    
     320              :         //       then if that intersection is inside the cube, pursuing       
     321              :         //       whether the intersection point is inside the triangle itself. 
     322              : 
     323              :         //       To find plane of the triangle, first perform crossproduct on  
     324              :         //       two triangle side vectors to compute the normal vector.                                       
     325            0 :         SUB(t.m_P[0],t.m_P[1],vect12);
     326            0 :         SUB(t.m_P[0],t.m_P[2],vect13);
     327            0 :         CROSS(vect12,vect13,norm)
     328              : 
     329              :         ///
     330              :         //       The normal vector "norm" X,Y,Z components are the coefficients 
     331              :         //               of the triangles AX + BY + CZ + D = 0 plane equation.  If we   
     332              :         //       solve the plane equation for X=Y=Z (a diagonal), we get        
     333              :         //       -D/(A+B+C) as a metric of the distance from cube center to the 
     334              :         //       diagonal/plane intersection.  If this is between -0.5 and 0.5, 
     335              :         //       the intersection is inside the cube.  If so, we continue by    
     336              :         //       doing a point/triangle intersection.                           
     337              :         //       Do this for all four diagonals.                                
     338            0 :         d = norm.x() * t.m_P[0].x() + norm.y() * t.m_P[0].y() + norm.z() * t.m_P[0].z();
     339            0 :         hitpp.x() = hitpp.y() = hitpp.z() = d / (norm.x() + norm.y() + norm.z());
     340            0 :         if (fabs(hitpp.x()) <= 0.5)
     341            0 :                 if (PointTriangleIntersection(hitpp,t) == INSIDE) return(INSIDE);
     342            0 :         hitpn.z() = -(hitpn.x() = hitpn.y() = d / (norm.x() + norm.y() - norm.z()));
     343            0 :         if (fabs(hitpn.x()) <= 0.5)
     344            0 :                 if (PointTriangleIntersection(hitpn,t) == INSIDE) return(INSIDE);
     345            0 :         hitnp.y() = -(hitnp.x() = hitnp.z() = d / (norm.x() - norm.y() + norm.z()));
     346            0 :         if (fabs(hitnp.x()) <= 0.5)
     347            0 :                 if (PointTriangleIntersection(hitnp,t) == INSIDE) return(INSIDE);
     348            0 :         hitnn.y() = hitnn.z() = -(hitnn.x() = d / (norm.x() - norm.y() - norm.z()));
     349            0 :         if (fabs(hitnn.x()) <= 0.5)
     350            0 :                 if (PointTriangleIntersection(hitnn,t) == INSIDE) return(INSIDE);
     351              : 
     352              :         ///
     353              :         //      No edge touched the cube; no cube diagonal touched the triangle. 
     354              :         //  We're done...there was no intersection.                          
     355              :         //
     356              :         return(OUTSIDE);
     357              : }
     358              : 
     359              : //////////////////////////////////////////////////////////////////////////////
     360              : //
     361              : //      Public Functions
     362              : //
     363              : //
     364              : 
     365              : ///
     366              : //      TriangleBoxIntersection()
     367              : //
     368              : //              Determine if a bounding box and triangle intersect
     369              : //
     370            0 : bool TriangleBoxIntersection(const MathVector<3>& p0, const MathVector<3>& p1,
     371              :                                                          const MathVector<3>& p2,
     372              :                                                          const MathVector<3>& boxMin, const MathVector<3>& boxMax)
     373              : {
     374              :         vector3 Trans;
     375              :         vector3 Scale(1.0, 1.0, 1.0);
     376              :         vector3 TransMax;
     377              :         TRI             TestTri;
     378              : 
     379              :         ///
     380              :         //      Compute the scale and transform required to make BBox
     381              :         //      a voxel
     382              :         //
     383            0 :         Trans.x() = (boxMax.x() + boxMin.x()) / 2;
     384            0 :         Trans.y() = (boxMax.y() + boxMin.y()) / 2;
     385            0 :         Trans.z() = (boxMax.z() + boxMin.z()) / 2;
     386              : 
     387              :         VecSubtract(TransMax, boxMax, Trans);
     388              :         
     389            0 :         if(TransMax.x() != 0)
     390            0 :                 Scale.x() = 0.5f / TransMax.x();
     391            0 :         if(TransMax.y() != 0)
     392            0 :                 Scale.y() = 0.5f / TransMax.y();
     393            0 :         if(TransMax.z() != 0)
     394            0 :                 Scale.z() = 0.5f / TransMax.z();
     395              : 
     396              :         ///
     397              :         //      Put the triangle in voxel space
     398              :         //      
     399            0 :         TestTri.m_P[0].x() = (p0.x() - Trans.x()) * Scale.x();
     400            0 :         TestTri.m_P[0].y() = (p0.y() - Trans.y()) * Scale.y();
     401            0 :         TestTri.m_P[0].z() = (p0.z() - Trans.z()) * Scale.z();
     402              : 
     403            0 :         TestTri.m_P[1].x() = (p1.x() - Trans.x()) * Scale.x();
     404            0 :         TestTri.m_P[1].y() = (p1.y() - Trans.y()) * Scale.y();
     405            0 :         TestTri.m_P[1].z() = (p1.z() - Trans.z()) * Scale.z();
     406              :         
     407            0 :         TestTri.m_P[2].x() = (p2.x() - Trans.x()) * Scale.x();
     408            0 :         TestTri.m_P[2].y() = (p2.y() - Trans.y()) * Scale.y();
     409            0 :         TestTri.m_P[2].z() = (p2.z() - Trans.z()) * Scale.z();
     410              : 
     411              :         ///
     412              :         //      Test against the voxel
     413              :         //
     414            0 :         return(TriCubeIntersection(TestTri) == INSIDE); 
     415              : }
     416              : 
     417              : }//     end of namespace
        

Generated by: LCOV version 2.0-1