LCOV - code coverage report
Current view: top level - engine/world - bih.cpp (source / functions) Coverage Total Hit
Test: Libprimis Test Coverage Lines: 1.3 % 716 9
Test Date: 2025-02-18 06:21:28 Functions: 5.7 % 35 2

            Line data    Source code
       1              : /* bounding interval hierarchy (BIH)
       2              :  *
       3              :  * the BIH code is used to calculate the intersection of models with other models
       4              :  * and the world, specifically for physics collision calculations. BIH methods
       5              :  * are also used for the procedural ragdoll physics.
       6              :  */
       7              : #include "../libprimis-headers/cube.h"
       8              : #include "../../shared/geomexts.h"
       9              : #include "../../shared/glexts.h"
      10              : 
      11              : #include <memory>
      12              : #include <optional>
      13              : 
      14              : #include "entities.h"
      15              : #include "physics.h"
      16              : #include "raycube.h"
      17              : 
      18              : #include "render/rendermodel.h"
      19              : #include "render/shaderparam.h"
      20              : #include "render/stain.h"
      21              : #include "render/texture.h"
      22              : 
      23              : #include "world/bih.h"
      24              : 
      25              : #include "model/model.h"
      26              : 
      27            0 : int BIH::node::axis() const
      28              : {
      29            0 :     return child[0]>>14;
      30              : }
      31              : 
      32            0 : int BIH::node::childindex(int which) const
      33              : {
      34            0 :     return child[which]&0x3FFF;
      35              : }
      36              : 
      37            0 : bool BIH::node::isleaf(int which) const
      38              : {
      39            0 :     return (child[1]&(1<<(14+which)))!=0;
      40              : }
      41              : 
      42            0 : bool BIH::mesh::tribb::outside(const ivec &bo, const ivec &br) const
      43              : {
      44            0 :     return std::abs(bo.x - center.coord.x) > br.x + radius.coord.x ||
      45            0 :            std::abs(bo.y - center.coord.y) > br.y + radius.coord.y ||
      46            0 :            std::abs(bo.z - center.coord.z) > br.z + radius.coord.z;
      47              : }
      48              : 
      49            0 : BIH::mesh::mesh() : numnodes(0), numtris(0), tex(nullptr), flags(0) {}
      50              : 
      51            0 : vec BIH::mesh::getpos(int i) const
      52              : {
      53            0 :     return *reinterpret_cast<const vec *>(pos + i*posstride);
      54              : }
      55            0 : vec2 BIH::mesh::gettc(int i) const
      56              : {
      57            0 :     return *reinterpret_cast<const vec2 *>(tc + i*tcstride);
      58              : }
      59              : 
      60            0 : void BIH::mesh::setmesh(const tri *tris, int numtris,
      61              :                         const uchar *pos, int posstride,
      62              :                         const uchar *tc, int tcstride)
      63              : {
      64            0 :     this->tris = tris;
      65            0 :     this->numtris = numtris;
      66            0 :     this->pos = pos;
      67            0 :     this->posstride = posstride;
      68            0 :     this->tc = tc;
      69            0 :     this->tcstride = tcstride;
      70            0 : }
      71              : 
      72              : 
      73            0 : matrix4x3 BIH::mesh::invxform() const
      74              : {
      75            0 :     matrix4x3 ixf(xform);
      76            0 :     ixf.invert();
      77            0 :     return ixf;
      78              : }
      79              : 
      80            0 : matrix3 BIH::mesh::xformnorm() const
      81              : {
      82            0 :     matrix3 xfn(xform);
      83            0 :     xfn.normalize();
      84            0 :     return xfn;
      85              : }
      86              : 
      87            0 : matrix3 BIH::mesh::invxformnorm() const
      88              : {
      89            0 :     matrix3 ixfn(xformnorm());
      90            0 :     ixfn.invert();
      91            0 :     return ixfn;
      92              : }
      93              : 
      94            0 : float BIH::mesh::scale() const
      95              : {
      96            0 :     return xform.a.magnitude();
      97              : }
      98              : 
      99              : /* diagram of a,b,c,n vectors
     100              :  * a is the vector between the origin and the point 0 indicated
     101              :  * n is the triangle normal
     102              :  * b,c are displacement vectors from 1->2
     103              :  * there is no explicit vector from points 1 to 2
     104              :  *            →       →
     105              :  *            a       n
     106              :  *             0—————————>
     107              :  *            / \
     108              :  *        →  /   \  →
     109              :  *        b /     \ c
     110              :  *         /       \
     111              :  *        1—————————2
     112              :  */
     113              : 
     114            0 : bool BIH::triintersect(const mesh &m, int tidx, const vec &mo, const vec &mray, float maxdist, float &dist, int mode) const
     115              : {
     116            0 :     const mesh::tri &t = m.tris[tidx];
     117            0 :     vec a = m.getpos(t.vert[0]), //position of vert 0
     118            0 :         b = m.getpos(t.vert[1]).sub(a), //displacement vector from vert 0->1
     119            0 :         c = m.getpos(t.vert[2]).sub(a), //displacement vector from vert 0->2
     120            0 :         n = vec().cross(b, c), //normal of the triangle
     121            0 :         r = vec(a).sub(mo), //mo is transform of o
     122            0 :         e = vec().cross(r, mray); //mray is transform of ray
     123            0 :     float det = mray.dot(n),
     124              :           v, w, f;
     125            0 :     if(det >= 0)
     126              :     {
     127            0 :         if(!(mode&Ray_Shadow) && m.flags&Mesh_CullFace)
     128              :         {
     129            0 :             return false;
     130              :         }
     131            0 :         v = e.dot(c);
     132            0 :         if(v < 0 || v > det)
     133              :         {
     134            0 :             return false;
     135              :         }
     136            0 :         w = -e.dot(b);
     137            0 :         if(w < 0 || v + w > det)
     138              :         {
     139            0 :             return false;
     140              :         }
     141            0 :         f = r.dot(n)*m.scale();
     142            0 :         if(f < 0 || f > maxdist*det || !det)
     143              :         {
     144            0 :             return false;
     145              :         }
     146              :     }
     147              :     else
     148              :     {
     149            0 :         v = e.dot(c);
     150            0 :         if(v > 0 || v < det)
     151              :         {
     152            0 :             return false;
     153              :         }
     154            0 :         w = -e.dot(b);
     155            0 :         if(w > 0 || v + w < det)
     156              :         {
     157            0 :             return false;
     158              :         }
     159            0 :         f = r.dot(n)*m.scale();
     160            0 :         if(f > 0 || f < maxdist*det)
     161              :         {
     162            0 :             return false;
     163              :         }
     164              :     }
     165            0 :     float invdet = 1/det;
     166            0 :     if(m.flags&Mesh_Alpha && (mode&Ray_Shadow)==Ray_Shadow && m.tex->alphamask)
     167              :     {
     168            0 :         vec2 at = m.gettc(t.vert[0]),
     169            0 :              bt = m.gettc(t.vert[1]).sub(at).mul(v*invdet),
     170            0 :              ct = m.gettc(t.vert[2]).sub(at).mul(w*invdet);
     171            0 :         at.add(bt).add(ct);
     172            0 :         int si = std::clamp(static_cast<int>(m.tex->xs * at.x), 0, m.tex->xs-1),
     173            0 :             ti = std::clamp(static_cast<int>(m.tex->ys * at.y), 0, m.tex->ys-1);
     174            0 :         if(!(m.tex->alphamask[ti*((m.tex->xs+7)/8) + si/8] & (1<<(si%8))))
     175              :         {
     176            0 :             return false;
     177              :         }
     178              :     }
     179            0 :     if(!(mode&Ray_Shadow))
     180              :     {
     181            0 :         hitsurface = m.xformnorm().transform(n).normalize();
     182              :     }
     183            0 :     dist = f*invdet;
     184            0 :     return true; //true if collided
     185              : }
     186              : 
     187            0 : bool BIH::traverse(const mesh &m, const vec &o, const vec &ray, const vec &invray, float maxdist, float &dist, int mode, const node *curnode, float tmin, float tmax) const
     188              : {
     189              :     struct traversestate
     190              :     {
     191              :         const BIH::node *node;
     192              :         float tmin, tmax;
     193              :     };
     194              :     std::array<traversestate, 128> stack;
     195            0 :     size_t stacksize = 0;
     196            0 :     ivec order(ray.x>0 ? 0 : 1, ray.y>0 ? 0 : 1, ray.z>0 ? 0 : 1);
     197            0 :     vec mo = m.invxform().transform(o), //invxform is inverse transform 4x3 matrix; transform by vec o
     198            0 :         mray = m.invxformnorm().transform(ray);
     199              :     for(;;)
     200              :     {
     201            0 :         int axis = curnode->axis();
     202            0 :         int nearidx = order[axis],
     203            0 :             faridx = nearidx^1;
     204            0 :         float nearsplit = (curnode->split[nearidx] - o[axis])*invray[axis],
     205            0 :               farsplit = (curnode->split[faridx] - o[axis])*invray[axis];
     206            0 :         if(nearsplit <= tmin)
     207              :         {
     208            0 :             if(farsplit < tmax)
     209              :             {
     210            0 :                 if(!curnode->isleaf(faridx))
     211              :                 {
     212            0 :                     curnode += curnode->childindex(faridx);
     213            0 :                     tmin = std::max(tmin, farsplit);
     214            0 :                     continue;
     215              :                 }
     216            0 :                 else if(triintersect(m, curnode->childindex(faridx), mo, mray, maxdist, dist, mode))
     217              :                 {
     218            0 :                     return true;
     219              :                 }
     220              :             }
     221              :         }
     222            0 :         else if(curnode->isleaf(nearidx))
     223              :         {
     224            0 :             if(triintersect(m, curnode->childindex(nearidx), mo, mray, maxdist, dist, mode))
     225              :             {
     226            0 :                 return true;
     227              :             }
     228            0 :             if(farsplit < tmax)
     229              :             {
     230            0 :                 if(!curnode->isleaf(faridx))
     231              :                 {
     232            0 :                     curnode += curnode->childindex(faridx);
     233            0 :                     tmin = std::max(tmin, farsplit);
     234            0 :                     continue;
     235              :                 }
     236            0 :                 else if(triintersect(m, curnode->childindex(faridx), mo, mray, maxdist, dist, mode))
     237              :                 {
     238            0 :                     return true;
     239              :                 }
     240              :             }
     241              :         }
     242              :         else
     243              :         {
     244            0 :             if(farsplit < tmax)
     245              :             {
     246            0 :                 if(!curnode->isleaf(faridx))
     247              :                 {
     248            0 :                     if(stacksize < stack.size())
     249              :                     {
     250            0 :                         traversestate &save = stack[stacksize++];
     251            0 :                         save.node = curnode + curnode->childindex(faridx);
     252            0 :                         save.tmin = std::max(tmin, farsplit);
     253            0 :                         save.tmax = tmax;
     254              :                     }
     255              :                     else
     256              :                     {
     257            0 :                         if(traverse(m, o, ray, invray, maxdist, dist, mode, curnode + curnode->childindex(nearidx), tmin, std::min(tmax, nearsplit)))
     258              :                         {
     259            0 :                             return true;
     260              :                         }
     261            0 :                         curnode += curnode->childindex(faridx);
     262            0 :                         tmin = std::max(tmin, farsplit);
     263            0 :                         continue;
     264              :                     }
     265              :                 }
     266            0 :                 else if(triintersect(m, curnode->childindex(faridx), mo, mray, maxdist, dist, mode))
     267              :                 {
     268            0 :                     return true;
     269              :                 }
     270              :             }
     271            0 :             curnode += curnode->childindex(nearidx);
     272            0 :             tmax = std::min(tmax, nearsplit);
     273            0 :             continue;
     274            0 :         }
     275            0 :         if(stacksize <= 0)
     276              :         {
     277            0 :             return false;
     278              :         }
     279            0 :         const traversestate &restore = stack[--stacksize];
     280            0 :         curnode = restore.node;
     281            0 :         tmin = restore.tmin;
     282            0 :         tmax = restore.tmax;
     283            0 :     }
     284              : }
     285              : 
     286            0 : bool BIH::traverse(const vec &o, const vec &ray, float maxdist, float &dist, int mode) const
     287              : {
     288              :     //if components are zero, set component to large value: 1e16, else invert
     289            0 :     vec invray(ray.x ? 1/ray.x : 1e16f, ray.y ? 1/ray.y : 1e16f, ray.z ? 1/ray.z : 1e16f);
     290            0 :     for(const mesh &m : meshes)
     291              :     {
     292            0 :         if(!(m.flags&Mesh_Render) || (!(mode&Ray_Shadow) && m.flags&Mesh_NoClip))
     293              :         {
     294            0 :             continue;
     295              :         }
     296            0 :         float t1 = (m.bbmin.x - o.x)*invray.x,
     297            0 :               t2 = (m.bbmax.x - o.x)*invray.x,
     298              :               tmin, tmax;
     299            0 :         if(invray.x > 0)
     300              :         {
     301            0 :             tmin = t1;
     302            0 :             tmax = t2;
     303              :         }
     304              :         else
     305              :         {
     306            0 :             tmin = t2;
     307            0 :             tmax = t1;
     308              :         }
     309            0 :         t1 = (m.bbmin.y - o.y)*invray.y;
     310            0 :         t2 = (m.bbmax.y - o.y)*invray.y;
     311            0 :         if(invray.y > 0)
     312              :         {
     313            0 :             tmin = std::max(tmin, t1);
     314            0 :             tmax = std::min(tmax, t2);
     315              :         }
     316              :         else
     317              :         {
     318            0 :             tmin = std::max(tmin, t2);
     319            0 :             tmax = std::min(tmax, t1);
     320              :         }
     321            0 :         t1 = (m.bbmin.z - o.z)*invray.z;
     322            0 :         t2 = (m.bbmax.z - o.z)*invray.z;
     323            0 :         if(invray.z > 0)
     324              :         {
     325            0 :             tmin = std::max(tmin, t1);
     326            0 :             tmax = std::min(tmax, t2);
     327              :         }
     328              :         else
     329              :         {
     330            0 :             tmin = std::max(tmin, t2);
     331            0 :             tmax = std::min(tmax, t1);
     332              :         }
     333            0 :         tmax = std::min(tmax, maxdist);
     334            0 :         if(tmin < tmax && traverse(m, o, ray, invray, maxdist, dist, mode, m.nodes, tmin, tmax))
     335              :         {
     336            0 :             return true;
     337              :         }
     338              :     }
     339            0 :     return false;
     340              : }
     341              : 
     342            0 : void BIH::build(mesh &m, uint *indices, int numindices, const ivec &vmin, const ivec &vmax) const
     343              : {
     344            0 :     int axis = 2;
     345            0 :     for(int k = 0; k < 2; ++k)
     346              :     {
     347            0 :         if(vmax[k] - vmin[k] > vmax[axis] - vmin[axis])
     348              :         {
     349            0 :             axis = k;
     350              :         }
     351              :     }
     352            0 :     ivec leftmin,
     353            0 :          leftmax,
     354            0 :          rightmin,
     355            0 :          rightmax;
     356              :     int splitleft,
     357              :         splitright,
     358              :         left,
     359              :         right;
     360            0 :     for(int k = 0; k < 3; ++k)
     361              :     {
     362            0 :         leftmin = rightmin = ivec(INT_MAX, INT_MAX, INT_MAX);
     363            0 :         leftmax = rightmax = ivec(INT_MIN, INT_MIN, INT_MIN);
     364            0 :         int split = (vmax[axis] + vmin[axis])/2;
     365            0 :         for(left = 0, right = numindices, splitleft = SHRT_MIN, splitright = SHRT_MAX; left < right;)
     366              :         {
     367            0 :             const mesh::tribb &tri = m.tribbs[indices[left]];
     368            0 :             ivec trimin = ivec(tri.center).sub(ivec(tri.radius)),
     369            0 :                  trimax = ivec(tri.center).add(ivec(tri.radius));
     370            0 :             int amin = trimin[axis],
     371            0 :                 amax = trimax[axis];
     372            0 :             if(std::max(split - amin, 0) > std::max(amax - split, 0))
     373              :             {
     374            0 :                 ++left;
     375            0 :                 splitleft = std::max(splitleft, amax);
     376            0 :                 leftmin.min(trimin);
     377            0 :                 leftmax.max(trimax);
     378              :             }
     379              :             else
     380              :             {
     381            0 :                 --right;
     382            0 :                 std::swap(indices[left], indices[right]);
     383            0 :                 splitright = std::min(splitright, amin);
     384            0 :                 rightmin.min(trimin);
     385            0 :                 rightmax.max(trimax);
     386              :             }
     387              :         }
     388            0 :         if(left > 0 && right < numindices)
     389              :         {
     390            0 :             break;
     391              :         }
     392            0 :         axis = (axis+1)%3;
     393              :     }
     394              : 
     395            0 :     if(!left || right==numindices)
     396              :     {
     397            0 :         leftmin = rightmin = ivec(INT_MAX, INT_MAX, INT_MAX);
     398            0 :         leftmax = rightmax = ivec(INT_MIN, INT_MIN, INT_MIN);
     399            0 :         left = right = numindices/2;
     400            0 :         splitleft = SHRT_MIN;
     401            0 :         splitright = SHRT_MAX;
     402            0 :         for(int i = 0; i < numindices; ++i)
     403              :         {
     404            0 :             const mesh::tribb &tri = m.tribbs[indices[i]];
     405            0 :             ivec trimin = static_cast<ivec>(tri.center).sub(static_cast<ivec>(tri.radius)),
     406            0 :                  trimax = static_cast<ivec>(tri.center).add(static_cast<ivec>(tri.radius));
     407            0 :             if(i < left)
     408              :             {
     409            0 :                 splitleft = std::max(splitleft, trimax[axis]);
     410            0 :                 leftmin.min(trimin);
     411            0 :                 leftmax.max(trimax);
     412              :             }
     413              :             else
     414              :             {
     415            0 :                 splitright = std::min(splitright, trimin[axis]);
     416            0 :                 rightmin.min(trimin);
     417            0 :                 rightmax.max(trimax);
     418              :             }
     419              :         }
     420              :     }
     421              : 
     422            0 :     int offset = m.numnodes++;
     423            0 :     node &curnode = m.nodes[offset];
     424            0 :     curnode.split[0] = static_cast<short>(splitleft);
     425            0 :     curnode.split[1] = static_cast<short>(splitright);
     426              : 
     427            0 :     if(left==1)
     428              :     {
     429            0 :         curnode.child[0] = (axis<<14) | indices[0];
     430              :     }
     431              :     else
     432              :     {
     433            0 :         curnode.child[0] = (axis<<14) | (m.numnodes - offset);
     434            0 :         build(m, indices, left, leftmin, leftmax);
     435              :     }
     436              : 
     437            0 :     if(numindices-right==1)
     438              :     {
     439            0 :         curnode.child[1] = (1<<15) | (left==1 ? 1<<14 : 0) | indices[right];
     440              :     }
     441              :     else
     442              :     {
     443            0 :         curnode.child[1] = (left==1 ? 1<<14 : 0) | (m.numnodes - offset);
     444            0 :         build(m, &indices[right], numindices-right, rightmin, rightmax);
     445              :     }
     446            0 : }
     447              : 
     448            2 : BIH::BIH(const std::vector<mesh> &buildmeshes)
     449            2 :   : nodes(nullptr), numnodes(0), bbmin(1e16f, 1e16f, 1e16f), bbmax(-1e16f, -1e16f, -1e16f), center(0, 0, 0), radius(0)
     450              : {
     451            2 :     mesh::tribb *tribbs = nullptr;
     452            2 :     int numtris = 0;
     453            2 :     if(buildmeshes.empty())
     454              :     {
     455            2 :         return;
     456              :     }
     457            0 :     for(const mesh &i : buildmeshes)
     458              :     {
     459            0 :         numtris += i.numtris;
     460              :     }
     461            0 :     if(!numtris)
     462              :     {
     463            0 :         return;
     464              :     }
     465            0 :     meshes.assign(buildmeshes.begin(), buildmeshes.end());
     466            0 :     tribbs = new mesh::tribb[numtris];
     467            0 :     mesh::tribb *dsttri = tribbs;
     468            0 :     for(mesh &m : meshes)
     469              :     {
     470            0 :         m.tribbs = dsttri;
     471            0 :         const mesh::tri *srctri = m.tris;
     472            0 :         vec mmin(1e16f, 1e16f, 1e16f), mmax(-1e16f, -1e16f, -1e16f);
     473            0 :         for(int j = 0; j < m.numtris; ++j)
     474              :         {
     475            0 :             vec s0 = m.getpos(srctri->vert[0]),
     476            0 :                 s1 = m.getpos(srctri->vert[1]),
     477            0 :                 s2 = m.getpos(srctri->vert[2]),
     478            0 :                 v0 = m.xform.transform(s0),
     479            0 :                 v1 = m.xform.transform(s1),
     480            0 :                 v2 = m.xform.transform(s2),
     481            0 :                 vmin = vec(v0).min(v1).min(v2),
     482            0 :                 vmax = vec(v0).max(v1).max(v2);
     483            0 :             mmin.min(vmin);
     484            0 :             mmax.max(vmax);
     485            0 :             ivec imin = ivec::floor(vmin),
     486            0 :                  imax = ivec::ceil(vmax);
     487            0 :             dsttri->center = static_cast<svec>(static_cast<ivec>(imin).add(imax).div(2));
     488            0 :             dsttri->radius = static_cast<svec>(static_cast<ivec>(imax).sub(imin).add(1).div(2));
     489            0 :             ++srctri;
     490            0 :             ++dsttri;
     491              :         }
     492            0 :         for(int k = 0; k < 3; ++k)
     493              :         {
     494            0 :             if(std::fabs(mmax[k] - mmin[k]) < 0.125f)
     495              :             {
     496            0 :                 float mid = (mmin[k] + mmax[k]) / 2;
     497            0 :                 mmin[k] = mid - 0.0625f;
     498            0 :                 mmax[k] = mid + 0.0625f;
     499              :             }
     500              :         }
     501            0 :         m.bbmin = mmin;
     502            0 :         m.bbmax = mmax;
     503            0 :         bbmin.min(mmin);
     504            0 :         bbmax.max(mmax);
     505              :     }
     506              : 
     507            0 :     center = vec(bbmin).add(bbmax).mul(0.5f);
     508            0 :     radius = vec(bbmax).sub(bbmin).mul(0.5f).magnitude();
     509              : 
     510            0 :     nodes = new node[numtris];
     511            0 :     node *curnode = nodes;
     512            0 :     uint *indices = new uint[numtris];
     513            0 :     for(mesh &m : meshes)
     514              :     {
     515            0 :         m.nodes = curnode;
     516            0 :         for(int j = 0; j < m.numtris; ++j)
     517              :         {
     518            0 :             indices[j] = j;
     519              :         }
     520            0 :         build(m, indices, m.numtris, ivec::floor(m.bbmin), ivec::ceil(m.bbmax));
     521            0 :         curnode += m.numnodes;
     522              :     }
     523            0 :     delete[] indices;
     524            0 :     numnodes = static_cast<int>(curnode - nodes);
     525            0 : }
     526              : 
     527            2 : BIH::~BIH()
     528              : {
     529            2 :     delete[] nodes;
     530            2 : }
     531              : 
     532            0 : bool mmintersect(const extentity &e, const vec &o, const vec &ray, float maxdist, int mode, float &dist)
     533              : {
     534            0 :     model *m = loadmapmodel(e.attr1);
     535            0 :     if(!m)
     536              :     {
     537            0 :         return false;
     538              :     }
     539            0 :     if(mode&Ray_Shadow)
     540              :     {
     541            0 :         if(!m->shadow || e.flags&EntFlag_NoShadow)
     542              :         {
     543            0 :             return false;
     544              :         }
     545              :     }
     546            0 :     else if((mode&Ray_Ents)!=Ray_Ents && (!m->collide || e.flags&EntFlag_NoCollide))
     547              :     {
     548            0 :         return false;
     549              :     }
     550            0 :     m->setBIH();
     551            0 :     float scale = e.attr5 ? 100.0f/e.attr5 : 1.0f;
     552            0 :     vec mo = static_cast<vec>(o).sub(e.o).mul(scale), mray(ray);
     553            0 :     float v = mo.dot(mray),
     554            0 :           inside = m->bih->getentradius() - mo.squaredlen();
     555            0 :     if((inside < 0 && v > 0) || inside + v*v < 0)
     556              :     {
     557            0 :         return false;
     558              :     }
     559            0 :     int yaw   = e.attr2,
     560            0 :         pitch = e.attr3,
     561            0 :         roll  = e.attr4;
     562              :     //reorientation of rotated mmodels
     563            0 :     if(yaw != 0)
     564              :     {
     565            0 :         const vec2 &rot = sincosmod360(-yaw);
     566            0 :         mo.rotate_around_z(rot);
     567            0 :         mray.rotate_around_z(rot);
     568              :     }
     569            0 :     if(pitch != 0)
     570              :     {
     571            0 :         const vec2 &rot = sincosmod360(-pitch);
     572            0 :         mo.rotate_around_x(rot);
     573            0 :         mray.rotate_around_x(rot);
     574              :     }
     575            0 :     if(roll != 0)
     576              :     {
     577            0 :         const vec2 &rot = sincosmod360(roll);
     578            0 :         mo.rotate_around_y(-rot);
     579            0 :         mray.rotate_around_y(-rot);
     580              :     }
     581            0 :     if(m->bih->traverse(mo, mray, maxdist ? maxdist*scale : 1e16f, dist, mode))
     582              :     {
     583            0 :         dist /= scale;
     584            0 :         if(!(mode&Ray_Shadow))
     585              :         {
     586              :             //reorientation
     587            0 :             if(roll != 0)
     588              :             {
     589            0 :                 hitsurface.rotate_around_y(sincosmod360(roll));
     590              :             }
     591            0 :             if(pitch != 0)
     592              :             {
     593            0 :                 hitsurface.rotate_around_x(sincosmod360(pitch));
     594              :             }
     595            0 :             if(yaw != 0)
     596              :             {
     597            0 :                 hitsurface.rotate_around_z(sincosmod360(yaw));
     598              :             }
     599              :         }
     600            0 :         return true;
     601              :     }
     602            0 :     return false;
     603              : }
     604              : 
     605            0 : static float segmentdistance(const vec &d1, const vec &d2, const vec &r)
     606              : {
     607            0 :     float a = d1.squaredlen(),
     608            0 :           e = d2.squaredlen(),
     609            0 :           f = d2.dot(r),
     610              :           s, t;
     611            0 :     if(a <= 1e-4f)
     612              :     {
     613            0 :         if(e <= 1e-4f)
     614              :         {
     615            0 :             return r.squaredlen();
     616              :         }
     617            0 :         s = 0;
     618            0 :         t = std::clamp(-f / e, 0.0f, 1.0f);
     619              :     }
     620              :     else
     621              :     {
     622            0 :         float c = d1.dot(r);
     623            0 :         if(e <= 1e-4f)
     624              :         {
     625            0 :             t = 0;
     626            0 :             s = std::clamp(c / a, 0.0f, 1.0f);
     627              :         }
     628              :         else
     629              :         {
     630            0 :             float b = d1.dot(d2),
     631            0 :                   denom = a*e - b*b;
     632            0 :             s = denom ? std::clamp((c*e - b*f) / denom, 0.0f, 1.0f) : 0.0f;
     633            0 :             t = b*s - f;
     634            0 :             if(t < 0)
     635              :             {
     636            0 :                 t = 0;
     637            0 :                 s = std::clamp(c / a, 0.0f, 1.0f);
     638              :             }
     639            0 :             else if(t > e)
     640              :             {
     641            0 :                 t = 1;
     642            0 :                 s = std::clamp((b + c) / a, 0.0f, 1.0f);
     643              :             }
     644              :             else
     645              :             {
     646            0 :                 t /= e;
     647              :             }
     648              :         }
     649              :     }
     650            0 :     vec c1 = static_cast<vec>(d1).mul(s),
     651            0 :         c2 = static_cast<vec>(d2).mul(t);
     652            0 :     return vec(c2).sub(c1).add(r).squaredlen();
     653              : }
     654              : 
     655            0 : static float trisegmentdistance(const vec &a, const vec &b, const vec &c, const vec &p, const vec &q)
     656              : {
     657              :     //displacement vectors
     658            0 :     vec pq = vec(q).sub(p),
     659            0 :         ab = vec(b).sub(a),
     660            0 :         bc = vec(c).sub(b),
     661            0 :         ca = vec(a).sub(c),
     662            0 :         ap = vec(p).sub(a),
     663            0 :         bp = vec(p).sub(b),
     664            0 :         cp = vec(p).sub(c),
     665            0 :         aq = vec(q).sub(a),
     666            0 :         bq = vec(q).sub(b);
     667            0 :     vec n, nab, nbc, nca;
     668            0 :     n.cross(ab, bc);
     669            0 :     nab.cross(n, ab);
     670            0 :     nbc.cross(n, bc);
     671            0 :     nca.cross(n, ca);
     672            0 :     float dp = n.dot(ap),
     673            0 :           dq = n.dot(aq),
     674              :           dist;
     675            0 :     if(ap.dot(nab) < 0) // P outside AB
     676              :     {
     677            0 :         dist = segmentdistance(ab, pq, ap);
     678            0 :         if(bq.dot(nbc) < 0)
     679              :         {
     680            0 :             dist = std::min(dist, segmentdistance(bc, pq, bp)); // Q outside BC
     681              :         }
     682            0 :         else if(aq.dot(nca) < 0)
     683              :         {
     684            0 :             dist = std::min(dist, segmentdistance(pq, ca, cp)); // Q outside CA
     685              :         }
     686            0 :         else if(aq.dot(nab) >= 0)
     687              :         {
     688            0 :             dist = std::min(dist, dq*dq/n.squaredlen()); // Q inside AB
     689              :         }
     690              :         else
     691              :         {
     692            0 :             return dist;
     693              :         }
     694              :     }
     695            0 :     else if(bp.dot(nbc) < 0) // P outside BC
     696              :     {
     697            0 :         dist = segmentdistance(bc, pq, bp);
     698            0 :         if(aq.dot(nca) < 0)
     699              :         {
     700            0 :             dist = std::min(dist, segmentdistance(ca, pq, cp)); // Q outside CA
     701              :         }
     702            0 :         else if(aq.dot(nab) < 0)
     703              :         {
     704            0 :             dist = std::min(dist, segmentdistance(ab, pq, ap)); // Q outside AB
     705              :         }
     706            0 :         else if(bq.dot(nbc) >= 0)
     707              :         {
     708            0 :             dist = std::min(dist, dq*dq/n.squaredlen()); // Q inside BC
     709              :         }
     710              :         else
     711              :         {
     712            0 :             return dist;
     713              :         }
     714              :     }
     715            0 :     else if(cp.dot(nca) < 0) // P outside CA
     716              :     {
     717            0 :         dist = segmentdistance(ca, pq, cp);
     718            0 :         if(aq.dot(nab) < 0)
     719              :         {
     720            0 :             dist = std::min(dist, segmentdistance(ab, pq, ap)); // Q outside AB
     721              :         }
     722            0 :         else if(bq.dot(nbc) < 0)
     723              :         {
     724            0 :             dist = std::min(dist, segmentdistance(bc, pq, bp)); // Q outside BC
     725              :         }
     726            0 :         else if(aq.dot(nca) >= 0)
     727              :         {
     728            0 :             dist = std::min(dist, dq*dq/n.squaredlen()); // Q inside CA
     729              :         }
     730            0 :         else return dist;
     731              :     }
     732            0 :     else if(aq.dot(nab) < 0)
     733              :     {
     734            0 :         dist = std::min(segmentdistance(ab, pq, ap), dp); // Q outside AB
     735              :     }
     736            0 :     else if(bq.dot(nbc) < 0)
     737              :     {
     738            0 :         dist = std::min(segmentdistance(bc, pq, bp), dp); // Q outside BC
     739              :     }
     740            0 :     else if(aq.dot(nca) < 0)
     741              :     {
     742            0 :         dist = std::min(segmentdistance(ca, pq, cp), dp); // Q outside CA
     743              :     }
     744              :     else // both P and Q inside
     745              :     {
     746            0 :         if(dp > 0 ? dq <= 0 : dq >= 0)
     747              :         {
     748            0 :             return 0; // P and Q on different sides of triangle
     749              :         }
     750            0 :         dist = std::min(dp*dp, dq*dq)/n.squaredlen();
     751            0 :         return dist;
     752              :     }
     753            0 :     if(dp > 0 ? dq >= 0 : dq <= 0)
     754              :     {
     755            0 :         return dist; // both P and Q on same side of triangle
     756              :     }
     757            0 :     vec e = vec().cross(pq, ap);
     758            0 :     float det = std::fabs(dq - dp),
     759            0 :           v = ca.dot(e);
     760            0 :     if(v < 0 || v > det)
     761              :     {
     762            0 :         return dist;
     763              :     }
     764            0 :     float w = ab.dot(e);
     765            0 :     if(w < 0 || v + w > det)
     766              :     {
     767            0 :         return dist;
     768              :     }
     769            0 :     return 0; // segment intersects triangle
     770              : }
     771              : 
     772            0 : static bool triboxoverlap(const vec &radius, const vec &a, const vec &b, const vec &c)
     773              : {
     774              : 
     775            0 :     static auto testaxis = [] (const vec &v0, const vec &v1, const vec &v2,
     776              :                                const vec &e, const int &s, const int &t,
     777              :                                const vec &radius)
     778              :     {
     779            0 :         float p = v0[s]*v1[t] - v0[t]*v1[s],
     780            0 :               q = v2[s]*e[t] - v2[t]*e[s],
     781            0 :               r = radius[s]*std::fabs(e[t]) + radius[t]*std::fabs(e[s]);
     782            0 :         if(p < q)
     783              :         {
     784            0 :             if(q < -r || p > r)
     785              :             {
     786            0 :                 return false;
     787              :             }
     788              :         }
     789            0 :         else if(p < -r || q > r)
     790              :         {
     791            0 :             return false;
     792              :         }
     793            0 :         return true;
     794              :     };
     795              : 
     796            0 :     static auto testface = [] (const vec &a,  const vec &b,  const vec &c,
     797              :                                uint axis, const vec &radius)
     798              :     {
     799            0 :         if(a[axis] < b[axis])
     800              :         {
     801            0 :             if(b[axis] < c[axis])
     802              :             {
     803            0 :                 if(c[axis] < -radius[axis] || a[axis] > radius[axis])
     804              :                 {
     805            0 :                     return false;
     806              :                 }
     807              :             }
     808            0 :             else if(b[axis] < -radius[axis] || std::min(a[axis], c[axis]) > radius[axis])
     809              :             {
     810            0 :                 return false;
     811              :             }
     812              :         }
     813            0 :         else if(a[axis] < c[axis])
     814              :         {
     815            0 :             if(c[axis] < -radius[axis] || b[axis] > radius[axis])
     816              :             {
     817            0 :                 return false;
     818              :             }
     819              :         }
     820            0 :         else if(a[axis] < -radius[axis] || std::min(b[axis], c[axis]) > radius[axis])
     821              :         {
     822            0 :             return false;
     823              :         }
     824            0 :         return true;
     825              :     };
     826              : 
     827            0 :     vec ab = vec(b).sub(a),
     828            0 :         bc = vec(c).sub(b),
     829            0 :         ca = vec(a).sub(c);
     830              : 
     831            0 :     if(!testaxis(a, b, c, ab, 2, 1, radius)) {return false;};
     832            0 :     if(!testaxis(a, b, c, ab, 0, 2, radius)) {return false;};
     833            0 :     if(!testaxis(a, b, c, ab, 1, 0, radius)) {return false;};
     834              : 
     835            0 :     if(!testaxis(b, c, a, bc, 2, 1, radius)) {return false;};
     836            0 :     if(!testaxis(b, c, a, bc, 0, 2, radius)) {return false;};
     837            0 :     if(!testaxis(a, b, c, ab, 1, 0, radius)) {return false;};
     838              : 
     839            0 :     if(!testaxis(c, a, b, ca, 2, 1, radius)) {return false;};
     840            0 :     if(!testaxis(c, a, b, ca, 0, 2, radius)) {return false;};
     841            0 :     if(!testaxis(c, a, b, ca, 1, 0, radius)) {return false;};
     842              : 
     843            0 :     if(!testface(a, b, c, 0, radius)) //x
     844              :     {
     845            0 :         return false;
     846              :     }
     847            0 :     else if(!testface(a, b, c, 1, radius)) //y
     848              :     {
     849            0 :         return false;
     850              :     }
     851            0 :     else if(!testface(a, b, c, 2, radius)) //z
     852              :     {
     853            0 :         return false;
     854              :     }
     855            0 :     return true;
     856              : }
     857              : 
     858              : //used in the tricollide templates below
     859              : //returns true if physent is a player and passed vec is close enough to matter (determined by radius,pdist)
     860            0 : bool BIH::playercollidecheck(const physent *d, float pdist, vec dir, vec n, vec radius) const
     861              : {
     862            0 :     float a = 2*radius.z*(d->zmargin/(d->aboveeye+d->eyeheight)-(dir.z < 0 ? 1/3.0f : 1/4.0f)),
     863            0 :           b = (dir.x*n.x < 0 || dir.y*n.y < 0 ? -radius.x : 0);
     864            0 :     if(d->type==physent::PhysEnt_Player)
     865              :     {
     866            0 :         if(pdist < (dir.z*n.z < 0 ? a : b))
     867              :         {
     868            0 :             return true;
     869              :         }
     870              :     }
     871            0 :     return false;
     872              : }
     873              : 
     874              : template<>
     875            0 : void BIH::tricollide<Collide_Ellipse>(const mesh &m, int tidx, const physent *d, const vec &dir, float cutoff, const vec &, const vec &radius, const matrix4x3 &orient, float &dist, const ivec &bo, const ivec &br, vec &cwall) const
     876              : {
     877            0 :     if(m.tribbs[tidx].outside(bo, br))
     878              :     {
     879            0 :         return;
     880              :     }
     881            0 :     const mesh::tri &t = m.tris[tidx];
     882            0 :     vec a = m.getpos(t.vert[0]),
     883            0 :         b = m.getpos(t.vert[1]),
     884            0 :         c = m.getpos(t.vert[2]),
     885            0 :         zdir = vec(orient.rowz()).mul((radius.z - radius.x)/(m.scale()*m.scale()));
     886            0 :     if(trisegmentdistance(a, b, c, vec(center).sub(zdir), vec(center).add(zdir)) > (radius.x*radius.x)/(m.scale()*m.scale()))
     887              :     {
     888            0 :         return;
     889              :     }
     890            0 :     vec n;
     891            0 :     n.cross(a, b, c).normalize();
     892            0 :     float pdist = (n.dot(vec(center).sub(a)) - std::fabs(n.dot(zdir)))*m.scale() - radius.x;
     893            0 :     if(pdist > 0 || pdist <= dist)
     894              :     {
     895            0 :         return;
     896              :     }
     897            0 :     collideinside = 1;
     898            0 :     n = orient.transformnormal(n).div(m.scale());
     899            0 :     if(!dir.iszero())
     900              :     {
     901            0 :         if(n.dot(dir) >= -cutoff*dir.magnitude())
     902              :         {
     903            0 :             return;
     904              :         }
     905              :         //see playercollidecheck defined above
     906            0 :         if(playercollidecheck(d, pdist, dir, n, radius))
     907              :         {
     908            0 :             return;
     909              :         }
     910              :     }
     911            0 :     dist = pdist;
     912            0 :     cwall = n;
     913              : }
     914              : 
     915              : template<>
     916            0 : void BIH::tricollide<Collide_OrientedBoundingBox>(const mesh &m, int tidx, const physent *d, const vec &dir, float cutoff, const vec &, const vec &radius, const matrix4x3 &orient, float &dist, const ivec &bo, const ivec &br, vec &cwall) const
     917              : {
     918            0 :     if(m.tribbs[tidx].outside(bo, br))
     919              :     {
     920            0 :         return;
     921              :     }
     922            0 :     const mesh::tri &t = m.tris[tidx];
     923            0 :     vec a = orient.transform(m.getpos(t.vert[0])),
     924            0 :         b = orient.transform(m.getpos(t.vert[1])),
     925            0 :         c = orient.transform(m.getpos(t.vert[2]));
     926            0 :     if(!triboxoverlap(radius, a, b, c))
     927              :     {
     928            0 :         return;
     929              :     }
     930            0 :     vec n;
     931            0 :     n.cross(a, b, c).normalize();
     932            0 :     float pdist = -n.dot(a),
     933            0 :           r = radius.absdot(n);
     934            0 :     if(std::fabs(pdist) > r)
     935              :     {
     936            0 :         return;
     937              :     }
     938            0 :     pdist -= r;
     939            0 :     if(pdist <= dist)
     940              :     {
     941            0 :         return;
     942              :     }
     943            0 :     collideinside = 1;
     944            0 :     if(!dir.iszero())
     945              :     {
     946            0 :         if(n.dot(dir) >= -cutoff*dir.magnitude())
     947              :         {
     948            0 :             return;
     949              :         }
     950            0 :         if(playercollidecheck(d, pdist, dir, n, radius))
     951              :         {
     952            0 :             return;
     953              :         }
     954              :     }
     955            0 :     dist = pdist;
     956            0 :     cwall = n;
     957              : }
     958              : 
     959              : template<int C>
     960            0 : void BIH::collide(const mesh &m, const physent *d, const vec &dir, float cutoff, const vec &center, const vec &radius, const matrix4x3 &orient, float &dist, node *curnode, const ivec &bo, const ivec &br, vec &cwall) const
     961              : {
     962              :     node *stack[128];
     963            0 :     int stacksize = 0;
     964            0 :     ivec bmin = ivec(bo).sub(br),
     965            0 :          bmax = ivec(bo).add(br);
     966            0 :     for(;;)
     967              :     {
     968            0 :         int axis = curnode->axis();
     969            0 :         const int nearidx = 0,
     970            0 :                   faridx = nearidx^1;
     971            0 :         int nearsplit = bmin[axis] - curnode->split[nearidx],
     972            0 :             farsplit = curnode->split[faridx] - bmax[axis];
     973              : 
     974            0 :         if(nearsplit > 0)
     975              :         {
     976            0 :             if(farsplit <= 0)
     977              :             {
     978            0 :                 if(!curnode->isleaf(faridx))
     979              :                 {
     980            0 :                     curnode += curnode->childindex(faridx);
     981            0 :                     continue;
     982              :                 }
     983              :                 else
     984              :                 {
     985            0 :                     tricollide<C>(m, curnode->childindex(faridx), d, dir, cutoff, center, radius, orient, dist, bo, br, cwall);
     986              :                 }
     987              :             }
     988              :         }
     989            0 :         else if(curnode->isleaf(nearidx))
     990              :         {
     991            0 :             tricollide<C>(m, curnode->childindex(nearidx), d, dir, cutoff, center, radius, orient, dist, bo, br, cwall);
     992            0 :             if(farsplit <= 0)
     993              :             {
     994            0 :                 if(!curnode->isleaf(faridx))
     995              :                 {
     996            0 :                     curnode += curnode->childindex(faridx);
     997            0 :                     continue;
     998              :                 }
     999              :                 else
    1000              :                 {
    1001            0 :                     tricollide<C>(m, curnode->childindex(faridx), d, dir, cutoff, center, radius, orient, dist, bo, br, cwall);
    1002              :                 }
    1003              :             }
    1004              :         }
    1005              :         else
    1006              :         {
    1007            0 :             if(farsplit <= 0)
    1008              :             {
    1009            0 :                 if(!curnode->isleaf(faridx))
    1010              :                 {
    1011            0 :                     if(stacksize < static_cast<int>(sizeof(stack)/sizeof(stack[0])))
    1012              :                     {
    1013            0 :                         stack[stacksize++] = curnode + curnode->childindex(faridx);
    1014              :                     }
    1015              :                     else
    1016              :                     {
    1017            0 :                         collide<C>(m, d, dir, cutoff, center, radius, orient, dist, &nodes[curnode->childindex(nearidx)], bo, br, cwall);
    1018            0 :                         curnode += curnode->childindex(faridx);
    1019            0 :                         continue;
    1020              :                     }
    1021              :                 }
    1022              :                 else
    1023              :                 {
    1024            0 :                     tricollide<C>(m, curnode->childindex(faridx), d, dir, cutoff, center, radius, orient, dist, bo, br, cwall);
    1025              :                 }
    1026              :             }
    1027            0 :             curnode += curnode->childindex(nearidx);
    1028            0 :             continue;
    1029              :         }
    1030            0 :         if(stacksize <= 0)
    1031              :         {
    1032            0 :             return;
    1033              :         }
    1034            0 :         curnode = stack[--stacksize];
    1035              :     }
    1036              : }
    1037              : 
    1038            0 : CollisionInfo BIH::ellipsecollide(const physent *d, const vec &dir, float cutoff, const vec &o, int yaw, int pitch, int roll, float scale) const
    1039              : {
    1040            0 :     if(!numnodes)
    1041              :     {
    1042            0 :         return {false, vec(0,0,0)};
    1043              :     }
    1044            0 :     vec center(d->o.x, d->o.y, d->o.z + 0.5f*(d->aboveeye - d->eyeheight)),
    1045            0 :         radius(d->radius, d->radius, 0.5f*(d->eyeheight + d->aboveeye));
    1046            0 :     center.sub(o);
    1047            0 :     if(scale != 1)
    1048              :     {
    1049            0 :         float invscale = 1/scale;
    1050            0 :         center.mul(invscale);
    1051            0 :         radius.mul(invscale);
    1052              :     }
    1053            0 :     matrix3 orient;
    1054            0 :     orient.identity();
    1055            0 :     if(yaw)
    1056              :     {
    1057            0 :         orient.rotate_around_z(sincosmod360(yaw));
    1058              :     }
    1059            0 :     if(pitch)
    1060              :     {
    1061            0 :         orient.rotate_around_x(sincosmod360(pitch));
    1062              :     }
    1063            0 :     if(roll)
    1064              :     {
    1065            0 :         orient.rotate_around_y(sincosmod360(-roll));
    1066              :     }
    1067            0 :     vec bo = orient.transposedtransform(center),
    1068            0 :         br = orient.abstransposedtransform(radius);
    1069            0 :     if(bo.x + br.x < bbmin.x || bo.y + br.y < bbmin.y || bo.z + br.z < bbmin.z ||
    1070            0 :        bo.x - br.x > bbmax.x || bo.y - br.y > bbmax.y || bo.z - br.z > bbmax.z)
    1071              :     {
    1072            0 :         return {false, vec(0,0,0)};
    1073              :     }
    1074            0 :     ivec imin = ivec::floor(vec(bo).sub(br)),
    1075            0 :          imax = ivec::ceil(vec(bo).add(br)),
    1076            0 :          icenter = imin.add(imax).div(2),
    1077            0 :          iradius = imax.sub(imin).add(1).div(2);
    1078              : 
    1079            0 :     float dist = -1e10f;
    1080            0 :     vec cwall(0,0,0);
    1081            0 :     for(const mesh &m : meshes)
    1082              :     {
    1083            0 :         if(!(m.flags&Mesh_Collide) || m.flags&Mesh_NoClip)
    1084              :         {
    1085            0 :             continue;
    1086              :         }
    1087            0 :         matrix4x3 morient;
    1088            0 :         morient.mul(orient, m.xform);
    1089            0 :         collide<Collide_Ellipse>(m, d, dir, cutoff, m.invxform().transform(bo), radius, morient, dist, m.nodes, icenter, iradius, cwall);
    1090              :     }
    1091            0 :     return {dist > maxcollidedistance, cwall};
    1092              : }
    1093              : 
    1094            0 : CollisionInfo BIH::boxcollide(const physent *d, const vec &dir, float cutoff, const vec &o, int yaw, int pitch, int roll, float scale) const
    1095              : {
    1096            0 :     if(!numnodes)
    1097              :     {
    1098            0 :         return {false, vec(0,0,0)};
    1099              :     }
    1100            0 :     vec center(d->o.x, d->o.y, d->o.z + 0.5f*(d->aboveeye - d->eyeheight)),
    1101            0 :         radius(d->xradius, d->yradius, 0.5f*(d->eyeheight + d->aboveeye));
    1102            0 :     center.sub(o);
    1103            0 :     if(scale != 1)
    1104              :     {
    1105            0 :         float invscale = 1/scale;
    1106            0 :         center.mul(invscale);
    1107            0 :         radius.mul(invscale);
    1108              :     }
    1109            0 :     matrix3 orient;
    1110            0 :     orient.identity();
    1111            0 :     if(yaw)
    1112              :     {
    1113            0 :         orient.rotate_around_z(sincosmod360(yaw));
    1114              :     }
    1115            0 :     if(pitch)
    1116              :     {
    1117            0 :         orient.rotate_around_x(sincosmod360(pitch));
    1118              :     }
    1119            0 :     if(roll)
    1120              :     {
    1121            0 :         orient.rotate_around_y(sincosmod360(-roll));
    1122              :     }
    1123            0 :     vec bo = orient.transposedtransform(center),
    1124            0 :         br = orient.abstransposedtransform(vec(d->radius, d->radius, radius.z));
    1125            0 :     if(bo.x + br.x < bbmin.x || bo.y + br.y < bbmin.y || bo.z + br.z < bbmin.z ||
    1126            0 :        bo.x - br.x > bbmax.x || bo.y - br.y > bbmax.y || bo.z - br.z > bbmax.z)
    1127              :     {
    1128            0 :         return {false, vec(0,0,0)};
    1129              :     }
    1130            0 :     ivec imin = ivec::floor(vec(bo).sub(br)),
    1131            0 :          imax = ivec::ceil(vec(bo).add(br)),
    1132            0 :          icenter = ivec(imin).add(imax).div(2),
    1133            0 :          iradius = ivec(imax).sub(imin).add(1).div(2);
    1134            0 :     matrix3 drot, dorient;
    1135            0 :     drot.setyaw(d->yaw/RAD);
    1136            0 :     vec ddir = drot.transform(dir),
    1137            0 :         dcenter = drot.transform(center).neg();
    1138            0 :     dorient.mul(drot, orient);
    1139            0 :     float dist = -1e10f;
    1140            0 :     vec cwall;
    1141            0 :     for(const mesh &m : meshes)
    1142              :     {
    1143            0 :         if(!(m.flags&Mesh_Collide) || m.flags&Mesh_NoClip)
    1144              :         {
    1145            0 :             continue;
    1146              :         }
    1147            0 :         matrix4x3 morient;
    1148            0 :         morient.mul(dorient, dcenter, m.xform);
    1149            0 :         collide<Collide_OrientedBoundingBox>(m, d, ddir, cutoff, center, radius, morient, dist, m.nodes, icenter, iradius, cwall);
    1150              :     }
    1151            0 :     if(dist > maxcollidedistance)
    1152              :     {
    1153            0 :         cwall = drot.transposedtransform(cwall);
    1154            0 :         return {true, cwall};
    1155              :     }
    1156            0 :     return {false, cwall};
    1157              : }
    1158              : 
    1159            0 : void BIH::genstaintris(std::vector<std::array<vec, 3>> &tris, const mesh &m, int tidx, const vec &, float, const matrix4x3 &orient, const ivec &bo, const ivec &br) const
    1160              : {
    1161            0 :     if(m.tribbs[tidx].outside(bo, br))
    1162              :     {
    1163            0 :         return;
    1164              :     }
    1165            0 :     const mesh::tri &t = m.tris[tidx];
    1166              :     std::array<vec, 3> v =
    1167              :     {
    1168            0 :         orient.transform(m.getpos(t.vert[0])),
    1169            0 :         orient.transform(m.getpos(t.vert[1])),
    1170            0 :         orient.transform(m.getpos(t.vert[2]))
    1171              :     };
    1172            0 :     tris.push_back(v);
    1173              : }
    1174              : 
    1175            0 : void BIH::genstaintris(std::vector<std::array<vec, 3>> &tris, const mesh &m, const vec &center, float radius, const matrix4x3 &orient, node *curnode, const ivec &bo, const ivec &br) const
    1176              : {
    1177            0 :     std::stack<node *> stack;
    1178            0 :     ivec bmin = static_cast<ivec>(bo).sub(br),
    1179            0 :          bmax = static_cast<ivec>(bo).add(br);
    1180              :     for(;;)
    1181              :     {
    1182            0 :         int axis = curnode->axis();
    1183            0 :         constexpr int nearidx = 0,
    1184            0 :                       faridx = nearidx ^ 1; //xor last bit
    1185            0 :         int nearsplit = bmin[axis] - curnode->split[nearidx],
    1186            0 :             farsplit = curnode->split[faridx] - bmax[axis];
    1187            0 :         if(nearsplit > 0)
    1188              :         {
    1189            0 :             if(farsplit <= 0)
    1190              :             {
    1191            0 :                 if(!curnode->isleaf(faridx))
    1192              :                 {
    1193            0 :                     curnode += curnode->childindex(faridx);
    1194            0 :                     continue;
    1195              :                 }
    1196              :                 else
    1197              :                 {
    1198            0 :                     genstaintris(tris, m, curnode->childindex(faridx), center, radius, orient, bo, br);
    1199              :                 }
    1200              :             }
    1201              :         }
    1202            0 :         else if(curnode->isleaf(nearidx))
    1203              :         {
    1204            0 :             genstaintris(tris, m, curnode->childindex(nearidx), center, radius, orient, bo, br);
    1205            0 :             if(farsplit <= 0)
    1206              :             {
    1207            0 :                 if(!curnode->isleaf(faridx))
    1208              :                 {
    1209            0 :                     curnode += curnode->childindex(faridx);
    1210            0 :                     continue;
    1211              :                 }
    1212              :                 else
    1213              :                 {
    1214            0 :                     genstaintris(tris, m, curnode->childindex(faridx), center, radius, orient, bo, br);
    1215              :                 }
    1216              :             }
    1217              :         }
    1218              :         else
    1219              :         {
    1220            0 :             if(farsplit <= 0)
    1221              :             {
    1222            0 :                 if(!curnode->isleaf(faridx))
    1223              :                 {
    1224            0 :                     if(stack.size() < 128)
    1225              :                     {
    1226            0 :                         stack.push(curnode + curnode->childindex(faridx));
    1227              :                     }
    1228              :                     else
    1229              :                     {
    1230            0 :                         genstaintris(tris, m, center, radius, orient, &nodes[curnode->childindex(nearidx)], bo, br);
    1231            0 :                         curnode += curnode->childindex(faridx);
    1232            0 :                         continue;
    1233              :                     }
    1234              :                 }
    1235              :                 else
    1236              :                 {
    1237            0 :                     genstaintris(tris, m, curnode->childindex(faridx), center, radius, orient, bo, br);
    1238              :                 }
    1239              :             }
    1240            0 :             curnode += curnode->childindex(nearidx);
    1241            0 :             continue;
    1242              :         }
    1243            0 :         if(stack.size() <= 0)
    1244              :         {
    1245            0 :             return;
    1246              :         }
    1247            0 :         curnode = stack.top();
    1248            0 :         stack.pop();
    1249            0 :     }
    1250            0 : }
    1251              : 
    1252            0 : void BIH::genstaintris(std::vector<std::array<vec, 3>> &tris, const vec &staincenter, float stainradius, const vec &o, int yaw, int pitch, int roll, float scale) const
    1253              : {
    1254            0 :     if(!numnodes)
    1255              :     {
    1256            0 :         return;
    1257              :     }
    1258            0 :     vec center = vec(staincenter).sub(o);
    1259            0 :     float radius = stainradius;
    1260            0 :     if(scale != 1)
    1261              :     {
    1262            0 :         float invscale = 1/scale;
    1263            0 :         center.mul(invscale);
    1264            0 :         radius *= invscale;
    1265              :     }
    1266            0 :     matrix3 orient;
    1267            0 :     orient.identity();
    1268              :     //reorientation
    1269            0 :     if(yaw)
    1270              :     {
    1271            0 :         orient.rotate_around_z(sincosmod360(yaw));
    1272              :     }
    1273            0 :     if(pitch)
    1274              :     {
    1275            0 :         orient.rotate_around_x(sincosmod360(pitch));
    1276              :     }
    1277            0 :     if(roll)
    1278              :     {
    1279            0 :         orient.rotate_around_y(sincosmod360(-roll));
    1280              :     }
    1281            0 :     vec bo = orient.transposedtransform(center);
    1282            0 :     if(bo.x + radius < bbmin.x || bo.y + radius < bbmin.y || bo.z + radius < bbmin.z ||
    1283            0 :        bo.x - radius > bbmax.x || bo.y - radius > bbmax.y || bo.z - radius > bbmax.z)
    1284              :     {
    1285            0 :         return;
    1286              :     }
    1287            0 :     orient.scale(scale);
    1288            0 :     ivec imin = ivec::floor(vec(bo).sub(radius)),
    1289            0 :          imax = ivec::ceil(vec(bo).add(radius)),
    1290            0 :          icenter = ivec(imin).add(imax).div(2),
    1291            0 :          iradius = ivec(imax).sub(imin).add(1).div(2);
    1292            0 :     for(const mesh &m : meshes)
    1293              :     {
    1294            0 :         if(!(m.flags&Mesh_Render) || m.flags&Mesh_Alpha)
    1295              :         {
    1296            0 :             continue;
    1297              :         }
    1298            0 :         matrix4x3 morient;
    1299            0 :         morient.mul(orient, o, m.xform);
    1300            0 :         genstaintris(tris, m, m.invxform().transform(bo), radius, morient, m.nodes, icenter, iradius);
    1301              :     }
    1302              : }
    1303              : 
    1304            0 : float BIH::getentradius() const
    1305              : {
    1306            0 :     return std::max(bbmin.squaredlen(), bbmax.squaredlen());
    1307              : }
        

Generated by: LCOV version 2.0-1