LCOV - code coverage report
Current view: top level - engine/world - mpr.h (source / functions) Coverage Total Hit
Test: Libprimis Test Coverage Lines: 0.0 % 172 0
Test Date: 2025-02-21 06:59:27 Functions: 0.0 % 14 0

            Line data    Source code
       1              : #ifndef MPR_H_
       2              : #define MPR_H_
       3              : 
       4              : struct clipplanes;
       5              : 
       6              : namespace mpr
       7              : {
       8              :     struct CubePlanes
       9              :     {
      10              :         const clipplanes &p;
      11              : 
      12            0 :         CubePlanes(const clipplanes &p) : p(p) {}
      13              : 
      14              :         vec center() const;
      15              :         vec supportpoint(const vec &n) const;
      16              :     };
      17              : 
      18              :     struct SolidCube
      19              :     {
      20              :         vec o;
      21              :         int size;
      22              : 
      23              :         SolidCube(float x, float y, float z, int size) : o(x, y, z), size(size) {}
      24              :         SolidCube(const vec &o, int size) : o(o), size(size) {}
      25            0 :         SolidCube(const ivec &o, int size) : o(o), size(size) {}
      26              : 
      27              :         vec center() const;
      28              :         vec supportpoint(const vec &n) const;
      29              :     };
      30              : 
      31              :     struct Ent
      32              :     {
      33              :         const physent *ent;
      34              : 
      35            0 :         Ent(const physent *ent) : ent(ent) {}
      36              : 
      37              :         vec center() const;
      38              :     };
      39              : 
      40              :     class EntOBB : public Ent
      41              :     {
      42              :         public:
      43              :             EntOBB(const physent *ent);
      44              : 
      45              :             vec contactface(const vec &wn, const vec &wdir) const;
      46              :             vec localsupportpoint(const vec &ln) const;
      47              :             vec supportpoint(const vec &n) const;
      48              : 
      49              :             float left()   const;
      50              :             float right()  const;
      51              :             float back()   const;
      52              :             float front()  const;
      53              :             float bottom() const;
      54              :             float top()    const;
      55              :         private:
      56              :             matrix3 orient;
      57              :             float supportcoord(const vec &p) const;
      58              :             float supportcoordneg(const vec &p) const;
      59              :     };
      60              : 
      61              :     struct EntFuzzy : Ent
      62              :     {
      63            0 :         EntFuzzy(const physent *ent) : Ent(ent) {}
      64              : 
      65              :         float left()   const;
      66              :         float right()  const;
      67              :         float back()   const;
      68              :         float front()  const;
      69              :         float bottom() const;
      70              :         float top()    const;
      71              :     };
      72              : 
      73              :     struct EntCylinder : EntFuzzy
      74              :     {
      75            0 :         EntCylinder(const physent *ent) : EntFuzzy(ent) {}
      76              : 
      77              :         vec contactface(const vec &n, const vec &dir) const;
      78              :         vec supportpoint(const vec &n) const;
      79              :     };
      80              : 
      81              :     struct EntCapsule : EntFuzzy
      82              :     {
      83            0 :         EntCapsule(const physent *ent) : EntFuzzy(ent) {}
      84              : 
      85              :         vec supportpoint(const vec &n) const;
      86              :     };
      87              : 
      88              :     struct EntEllipsoid : EntFuzzy
      89              :     {
      90              :         EntEllipsoid(const physent *ent) : EntFuzzy(ent) {}
      91              : 
      92              :         vec supportpoint(const vec &dir) const;
      93              :     };
      94              : 
      95              :     struct Model
      96              :     {
      97              :         vec o, radius;
      98              :         matrix3 orient;
      99              : 
     100              :         Model(const vec &ent, const vec &center, const vec &radius, int yaw, int pitch, int roll);
     101              : 
     102              :         vec center() const;
     103              :     };
     104              : 
     105              :     struct ModelOBB : Model
     106              :     {
     107            0 :         ModelOBB(const vec &ent, const vec &center, const vec &radius, int yaw, int pitch, int roll) :
     108            0 :             Model(ent, center, radius, yaw, pitch, roll)
     109            0 :         {}
     110              : 
     111              :         vec contactface(const vec &wn, const vec &wdir) const;
     112              :         vec supportpoint(const vec &n) const;
     113              :     };
     114              : 
     115              : 
     116              :     struct ModelEllipse : Model
     117              :     {
     118            0 :         ModelEllipse(const vec &ent, const vec &center, const vec &radius, int yaw, int pitch, int roll) :
     119            0 :             Model(ent, center, radius, yaw, pitch, roll)
     120            0 :         {}
     121              : 
     122              :         vec contactface(const vec &wn, const vec &wdir) const;
     123              :         vec supportpoint(const vec &n) const;
     124              :     };
     125              : 
     126              :     //templates
     127              :     const float boundarytolerance = 1e-3f;
     128              : 
     129              :     template<class T>
     130            0 :     bool collide(const T &p1, const EntOBB &p2)
     131              :     {
     132              :         // v0 = center of Minkowski difference
     133            0 :         vec v0 = p2.center().sub(p1.center());
     134            0 :         if(v0.iszero())
     135              :         {
     136            0 :             return true;  // v0 and origin overlap ==> hit
     137              :         }
     138              :         // v1 = support in direction of origin
     139            0 :         vec n = vec(v0).neg();
     140            0 :         vec v1 = p2.supportpoint(n).sub(p1.supportpoint(vec(n).neg()));
     141            0 :         if(v1.dot(n) <= 0)
     142              :         {
     143            0 :             return false;  // origin outside v1 support plane ==> miss
     144              :         }
     145              :         // v2 = support perpendicular to plane containing origin, v0 and v1
     146            0 :         n.cross(v1, v0);
     147            0 :         if(n.iszero())
     148              :         {
     149            0 :             return true;   // v0, v1 and origin colinear (and origin inside v1 support plane) == > hit
     150              :         }
     151            0 :         vec v2 = p2.supportpoint(n).sub(p1.supportpoint(vec(n).neg()));
     152            0 :         if(v2.dot(n) <= 0)
     153              :         {
     154            0 :             return false;  // origin outside v2 support plane ==> miss
     155              :         }
     156              :         // v3 = support perpendicular to plane containing v0, v1 and v2
     157            0 :         n.cross(v0, v1, v2);
     158              :         // If the origin is on the - side of the plane, reverse the direction of the plane
     159            0 :         if(n.dot(v0) > 0)
     160              :         {
     161            0 :             std::swap(v1, v2);
     162            0 :             n.neg();
     163              :         }
     164              :         ///
     165              :         // Phase One: Find a valid portal
     166              : 
     167            0 :         for(int i = 0; i < 100; ++i)
     168              :         {
     169              :             // Obtain the next support point
     170            0 :             vec v3 = p2.supportpoint(n).sub(p1.supportpoint(vec(n).neg()));
     171            0 :             if(v3.dot(n) <= 0)
     172              :             {
     173            0 :                 return false;  // origin outside v3 support plane ==> miss
     174              :             }
     175              :             // If origin is outside (v1,v0,v3), then portal is invalid -- eliminate v2 and find new support outside face
     176            0 :             vec v3xv0;
     177            0 :             v3xv0.cross(v3, v0);
     178            0 :             if(v1.dot(v3xv0) < 0)
     179              :             {
     180            0 :                 v2 = v3;
     181            0 :                 n.cross(v0, v1, v3);
     182            0 :                 continue;
     183              :             }
     184              :             // If origin is outside (v3,v0,v2), then portal is invalid -- eliminate v1 and find new support outside face
     185            0 :             if(v2.dot(v3xv0) > 0)
     186              :             {
     187            0 :                 v1 = v3;
     188            0 :                 n.cross(v0, v3, v2);
     189            0 :                 continue;
     190              :             }
     191              :             ///
     192              :             // Phase Two: Refine the portal
     193            0 :             for(int j = 0;; j++)
     194              :             {
     195              :                 // Compute outward facing normal of the portal
     196            0 :                 n.cross(v1, v2, v3);
     197              : 
     198              :                 // If the origin is inside the portal, we have a hit
     199            0 :                 if(n.dot(v1) >= 0)
     200              :                 {
     201            0 :                     return true;
     202              :                 }
     203            0 :                 n.normalize();
     204              :                 // Find the support point in the direction of the portal's normal
     205            0 :                 vec v4 = p2.supportpoint(n).sub(p1.supportpoint(vec(n).neg()));
     206              :                 // If the origin is outside the support plane or the boundary is thin enough, we have a miss
     207            0 :                 if(v4.dot(n) <= 0 || vec(v4).sub(v3).dot(n) <= boundarytolerance || j > 100)
     208              :                 {
     209            0 :                     return false;
     210              :                 }
     211              :                 // Test origin against the three planes that separate the new portal candidates: (v1,v4,v0) (v2,v4,v0) (v3,v4,v0)
     212              :                 // Note:  We're taking advantage of the triple product identities here as an optimization
     213              :                 //        (v1 % v4) * v0 == v1 * (v4 % v0)    > 0 if origin inside (v1, v4, v0)
     214              :                 //        (v2 % v4) * v0 == v2 * (v4 % v0)    > 0 if origin inside (v2, v4, v0)
     215              :                 //        (v3 % v4) * v0 == v3 * (v4 % v0)    > 0 if origin inside (v3, v4, v0)
     216            0 :                 vec v4xv0;
     217            0 :                 v4xv0.cross(v4, v0);
     218            0 :                 if(v1.dot(v4xv0) > 0)
     219              :                 {
     220            0 :                     if(v2.dot(v4xv0) > 0)
     221              :                     {
     222            0 :                         v1 = v4;    // Inside v1 & inside v2 ==> eliminate v1
     223              :                     }
     224              :                     else
     225              :                     {
     226            0 :                         v3 = v4;                   // Inside v1 & outside v2 ==> eliminate v3
     227              :                     }
     228              :                 }
     229              :                 else
     230              :                 {
     231            0 :                     if(v3.dot(v4xv0) > 0)
     232              :                     {
     233            0 :                         v2 = v4;    // Outside v1 & inside v3 ==> eliminate v2
     234              :                     }
     235              :                     else
     236              :                     {
     237            0 :                         v1 = v4;                   // Outside v1 & outside v3 ==> eliminate v1
     238              :                     }
     239              :                 }
     240              :             }
     241              :         }
     242            0 :         return false;
     243              :     }
     244              : 
     245              :     template<class T>
     246            0 :     bool collide(const EntOBB &p1, const T &p2, vec *contactnormal, vec *contactpoint1, vec *contactpoint2)
     247              :     {
     248              :         // v0 = center of Minkowski sum
     249            0 :         vec v01 = p1.center();
     250            0 :         vec v02 = p2.center();
     251            0 :         vec v0 = vec(v02).sub(v01);
     252              : 
     253              :         // Avoid case where centers overlap -- any direction is fine in this case
     254            0 :         if(v0.iszero())
     255              :         {
     256            0 :             v0 = vec(0, 0, 1e-5f);
     257              :         }
     258              :         // v1 = support in direction of origin
     259            0 :         vec n = vec(v0).neg();
     260            0 :         vec v11 = p1.supportpoint(vec(n).neg());
     261            0 :         vec v12 = p2.supportpoint(n);
     262            0 :         vec v1 = vec(v12).sub(v11);
     263            0 :         if(v1.dot(n) <= 0)
     264              :         {
     265            0 :             if(contactnormal)
     266              :             {
     267            0 :                 *contactnormal = n;
     268              :             }
     269            0 :             return false;
     270              :         }
     271              :         // v2 - support perpendicular to v1,v0
     272            0 :         n.cross(v1, v0);
     273            0 :         if(n.iszero())
     274              :         {
     275            0 :             n = vec(v1).sub(v0);
     276            0 :             n.normalize();
     277            0 :             if(contactnormal)
     278              :             {
     279            0 :                 *contactnormal = n;
     280              :             }
     281            0 :             if(contactpoint1)
     282              :             {
     283            0 :                 *contactpoint1 = v11;
     284              :             }
     285            0 :             if(contactpoint2)
     286              :             {
     287            0 :                 *contactpoint2 = v12;
     288              :             }
     289            0 :             return true;
     290              :         }
     291            0 :         vec v21 = p1.supportpoint(vec(n).neg());
     292            0 :         vec v22 = p2.supportpoint(n);
     293            0 :         vec v2 = vec(v22).sub(v21);
     294            0 :         if(v2.dot(n) <= 0)
     295              :         {
     296            0 :             if(contactnormal)
     297              :             {
     298            0 :                 *contactnormal = n;
     299              :             }
     300            0 :             return false;
     301              :         }
     302              :         // Determine whether origin is on + or - side of plane (v1,v0,v2)
     303            0 :         n.cross(v0, v1, v2);
     304              :         // If the origin is on the - side of the plane, reverse the direction of the plane
     305            0 :         if(n.dot(v0) > 0)
     306              :         {
     307            0 :             std::swap(v1, v2);
     308            0 :             std::swap(v11, v21);
     309            0 :             std::swap(v12, v22);
     310            0 :             n.neg();
     311              :         }
     312              :         ///
     313              :         // Phase One: Identify a portal
     314            0 :         for(int i = 0; i < 100; ++i)
     315              :         {
     316              :             // Obtain the support point in a direction perpendicular to the existing plane
     317              :             // Note: This point is guaranteed to lie off the plane
     318            0 :             vec v31 = p1.supportpoint(vec(n).neg());
     319            0 :             vec v32 = p2.supportpoint(n);
     320            0 :             vec v3 = vec(v32).sub(v31);
     321            0 :             if(v3.dot(n) <= 0)
     322              :             {
     323            0 :                 if(contactnormal) *contactnormal = n;
     324            0 :                 return false;
     325              :             }
     326              :             // If origin is outside (v1,v0,v3), then eliminate v2 and loop
     327            0 :             vec v3xv0;
     328            0 :             v3xv0.cross(v3, v0);
     329            0 :             if(v1.dot(v3xv0) < 0)
     330              :             {
     331            0 :                 v2 = v3;
     332            0 :                 v21 = v31;
     333            0 :                 v22 = v32;
     334            0 :                 n.cross(v0, v1, v3);
     335            0 :                 continue;
     336              :             }
     337              :             // If origin is outside (v3,v0,v2), then eliminate v1 and loop
     338            0 :             if(v2.dot(v3xv0) > 0)
     339              :             {
     340            0 :                 v1 = v3;
     341            0 :                 v11 = v31;
     342            0 :                 v12 = v32;
     343            0 :                 n.cross(v0, v3, v2);
     344            0 :                 continue;
     345              :             }
     346            0 :             bool hit = false;
     347              :             ///
     348              :             // Phase Two: Refine the portal
     349              :             // We are now inside of a wedge...
     350            0 :             for(int j = 0;; j++)
     351              :             {
     352              :                 // Compute normal of the wedge face
     353            0 :                 n.cross(v1, v2, v3);
     354              :                 // Can this happen???  Can it be handled more cleanly?
     355            0 :                 if(n.iszero())
     356              :                 {
     357            0 :                     return true;
     358              :                 }
     359            0 :                 n.normalize();
     360              :                 // If the origin is inside the wedge, we have a hit
     361            0 :                 if(n.dot(v1) >= 0 && !hit)
     362              :                 {
     363            0 :                     if(contactnormal)
     364              :                     {
     365            0 :                         *contactnormal = n;
     366              :                     }
     367              :                     // Compute the barycentric coordinates of the origin
     368            0 :                     if(contactpoint1 || contactpoint2)
     369              :                     {
     370            0 :                         float b0 = v3.scalartriple(v1, v2),
     371            0 :                               b1 = v0.scalartriple(v3, v2),
     372            0 :                               b2 = v3.scalartriple(v0, v1),
     373            0 :                               b3 = v0.scalartriple(v2, v1),
     374            0 :                               sum = b0 + b1 + b2 + b3;
     375            0 :                         if(sum <= 0)
     376              :                         {
     377            0 :                             b0 = 0;
     378            0 :                             b1 = n.scalartriple(v2, v3);
     379            0 :                             b2 = n.scalartriple(v3, v1);
     380            0 :                             b3 = n.scalartriple(v1, v2);
     381            0 :                             sum = b1 + b2 + b3;
     382              :                         }
     383            0 :                         if(contactpoint1)
     384              :                         {
     385            0 :                             *contactpoint1 = (vec(v01).mul(b0).add(vec(v11).mul(b1)).add(vec(v21).mul(b2)).add(vec(v31).mul(b3))).mul(1.0f/sum);
     386              :                         }
     387            0 :                         if(contactpoint2)
     388              :                         {
     389            0 :                             *contactpoint2 = (vec(v02).mul(b0).add(vec(v12).mul(b1)).add(vec(v22).mul(b2)).add(vec(v32).mul(b3))).mul(1.0f/sum);
     390              :                         }
     391              :                     }
     392              :                     // HIT!!!
     393            0 :                     hit = true;
     394              :                 }
     395              :                 // Find the support point in the direction of the wedge face
     396            0 :                 vec v41 = p1.supportpoint(vec(n).neg());
     397            0 :                 vec v42 = p2.supportpoint(n);
     398            0 :                 vec v4 = vec(v42).sub(v41);
     399              :                 // If the boundary is thin enough or the origin is outside the support plane for the newly discovered vertex, then we can terminate
     400            0 :                 if(v4.dot(n) <= 0 || vec(v4).sub(v3).dot(n) <= boundarytolerance || j > 100)
     401              :                 {
     402            0 :                     if(contactnormal)
     403              :                     {
     404            0 :                         *contactnormal = n;
     405              :                     }
     406            0 :                     return hit;
     407              :                 }
     408              :                 // Test origin against the three planes that separate the new portal candidates: (v1,v4,v0) (v2,v4,v0) (v3,v4,v0)
     409              :                 // Note:  We're taking advantage of the triple product identities here as an optimization
     410              :                 //        (v1 % v4) * v0 == v1 * (v4 % v0)    > 0 if origin inside (v1, v4, v0)
     411              :                 //        (v2 % v4) * v0 == v2 * (v4 % v0)    > 0 if origin inside (v2, v4, v0)
     412              :                 //        (v3 % v4) * v0 == v3 * (v4 % v0)    > 0 if origin inside (v3, v4, v0)
     413            0 :                 vec v4xv0;
     414            0 :                 v4xv0.cross(v4, v0);
     415            0 :                 if(v1.dot(v4xv0) > 0) // Compute the tetrahedron dividing face d1 = (v4,v0,v1)
     416              :                 {
     417            0 :                     if(v2.dot(v4xv0) > 0) // Compute the tetrahedron dividing face d2 = (v4,v0,v2)
     418              :                     {
     419              :                         // Inside d1 & inside d2 ==> eliminate v1
     420            0 :                         v1 = v4;
     421            0 :                         v11 = v41;
     422            0 :                         v12 = v42;
     423              :                     }
     424              :                     else
     425              :                     {
     426              :                         // Inside d1 & outside d2 ==> eliminate v3
     427            0 :                         v3 = v4;
     428            0 :                         v31 = v41;
     429            0 :                         v32 = v42;
     430              :                     }
     431              :                 }
     432              :                 else
     433              :                 {
     434            0 :                     if(v3.dot(v4xv0) > 0) // Compute the tetrahedron dividing face d3 = (v4,v0,v3)
     435              :                     {
     436              :                         // Outside d1 & inside d3 ==> eliminate v2
     437            0 :                         v2 = v4;
     438            0 :                         v21 = v41;
     439            0 :                         v22 = v42;
     440              :                     }
     441              :                     else
     442              :                     {
     443              :                         // Outside d1 & outside d3 ==> eliminate v1
     444            0 :                         v1 = v4;
     445            0 :                         v11 = v41;
     446            0 :                         v12 = v42;
     447              :                     }
     448              :                 }
     449              :             }
     450              :         }
     451            0 :         return false;
     452              :     }
     453              : }
     454              : 
     455              : #endif
        

Generated by: LCOV version 2.0-1