LCOV - code coverage report
Current view: top level - engine/world - mpr.h (source / functions) Coverage Total Hit
Test: Libprimis Test Coverage Lines: 2.9 % 174 5
Test Date: 2025-11-24 07:55:42 Functions: 31.2 % 16 5

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

Generated by: LCOV version 2.0-1