LCOV - code coverage report
Current view: top level - engine/model - ragdoll.cpp (source / functions) Hit Total Coverage
Test: Libprimis Test Coverage Lines: 105 347 30.3 %
Date: 2025-01-07 07:51:37 Functions: 12 23 52.2 %

          Line data    Source code
       1             : /* ragdoll.cpp: ragdoll physics support
       2             :  *
       3             :  * Libprimis supports limited client-side ragdolls to simulate corpses. Ragdolls
       4             :  * are created when actors are killed and support basic physical interaction with
       5             :  * terrain (but not other players).
       6             :  *
       7             :  * This file contains implementation functions for the ragdoll class. For the
       8             :  * class definition, see ragdoll.h.
       9             :  */
      10             : #include "../libprimis-headers/cube.h"
      11             : #include "../../shared/geomexts.h"
      12             : 
      13             : #include <memory>
      14             : #include <optional>
      15             : 
      16             : #include "interface/console.h"
      17             : #include "interface/control.h"
      18             : 
      19             : #include "render/rendergl.h"
      20             : 
      21             : #include "world/entities.h"
      22             : #include "world/octaworld.h"
      23             : #include "world/physics.h"
      24             : #include "world/bih.h"
      25             : 
      26             : #include "model.h"
      27             : #include "ragdoll.h"
      28             : 
      29             : /*               ragdollskel                  */
      30             : 
      31             : //ragdollskel::tri
      32             : 
      33           5 : bool ragdollskel::tri::shareverts(const tri &t) const
      34             : {
      35          15 :     for(int i = 0; i < 3; ++i)
      36             :     {
      37          43 :         for(int j = 0; j < 3; ++j)
      38             :         {
      39          33 :             if(vert[i] == t.vert[j])
      40             :             {
      41           3 :                 return true;
      42             :             }
      43             :         }
      44             :     }
      45           2 :     return false;
      46             : }
      47             : 
      48             : //ragdollskel
      49             : 
      50           1 : void ragdollskel::setupjoints()
      51             : {
      52           4 :     for(vert &i : verts)
      53             :     {
      54           3 :         i.weight = 0;
      55             :     }
      56           2 :     for(joint &j : joints)
      57             :     {
      58           1 :         j.weight = 0;
      59           1 :         vec pos(0, 0, 0);
      60           4 :         for(int k = 0; k < 3; ++k)
      61             :         {
      62           3 :             if(j.vert[k]>=0) //for each valid vertex (not -1) refered to by this joint (up to 3)
      63             :             {
      64           3 :                 pos.add(verts[j.vert[k]].pos); //j.vert[k].pos is the positon of the vertex pointed to by the joint being used
      65           3 :                 j.weight++;
      66           3 :                 verts[j.vert[k]].weight++;
      67             :             }
      68             :         }
      69           2 :         for(int k = 0; k < j.weight; ++k)
      70             :         {
      71           1 :             j.weight = 1/j.weight;
      72             :         }
      73           1 :         pos.mul(j.weight);
      74             : 
      75           1 :         const tri &t = tris[j.tri];
      76           1 :         matrix4x3 &m = j.orient;
      77           1 :         const vec &v1 = verts[t.vert[0]].pos,
      78           1 :                   &v2 = verts[t.vert[1]].pos,
      79           1 :                   &v3 = verts[t.vert[2]].pos;
      80             :         /*
      81             :          *               /|
      82             :          *              /
      83             :          *          m.c/
      84             :          *            /
      85             :          *        v1 /   m.a     v2
      86             :          *          *---------->*
      87             :          *          |
      88             :          *          |
      89             :          *          |
      90             :          *       m.b|
      91             :          *          v
      92             :          *
      93             :          *
      94             :          *          *
      95             :          *           v3
      96             :          *
      97             :          *        ----→    ----→
      98             :          *  m.c = v1 v2  x v1 v3
      99             :          *        --→   ----→
     100             :          *  m.b = m.c x v1 v2
     101             :          *
     102             :          */
     103           1 :         m.a = vec(v2).sub(v1).normalize(); //m.a has magnitude 1 at all times
     104           1 :         m.c.cross(m.a, vec(v3).sub(v1)).normalize(); //m.c will always have a magnitude <=1
     105           1 :         m.b.cross(m.c, m.a); //m.b will always have a magnitude <= m.c
     106           1 :         m.d = pos;
     107           1 :         m.transpose();
     108             :     }
     109           4 :     for(vert &i : verts)
     110             :     {
     111           3 :         if(i.weight)
     112             :         {
     113           3 :             i.weight = 1/i.weight;
     114             :         }
     115             :     }
     116           1 :     reljoints.clear();
     117           1 : }
     118             : 
     119           1 : void ragdollskel::setuprotfrictions()
     120             : {
     121           1 :     rotfrictions.clear();
     122           3 :     for(uint i = 0; i < tris.size(); i++)
     123             :     {
     124           3 :         for(uint j = i+1; j < tris.size(); j++)
     125             :         {
     126           1 :             if(tris[i].shareverts(tris[j]))
     127             :             {
     128             :                 rotfriction r;
     129           1 :                 r.tri[0] = i;
     130           1 :                 r.tri[1] = j;
     131           1 :                 rotfrictions.push_back(r);
     132             :             }
     133             :         }
     134             :     }
     135           1 : }
     136             : 
     137           1 : void ragdollskel::setup()
     138             : {
     139           1 :     setupjoints();
     140           1 :     setuprotfrictions();
     141             : 
     142           1 :     loaded = true;
     143           1 : }
     144             : 
     145           1 : void ragdollskel::addreljoint(int bone, int parent)
     146             : {
     147           1 :     reljoints.emplace_back(reljoint{bone, parent});
     148           1 : }
     149             : /*                  ragdolldata                   */
     150             : 
     151           3 : ragdolldata::ragdolldata(const ragdollskel *skel, float scale)
     152           3 :     : skel(skel),
     153           3 :       millis(lastmillis),
     154           3 :       collidemillis(0),
     155           3 :       lastmove(lastmillis),
     156           3 :       radius(0),
     157           3 :       tris(skel->tris.size()),
     158           3 :       animjoints(!skel->animjoints || skel->joints.empty() ? nullptr : new matrix4x3[skel->joints.size()]),
     159           3 :       reljoints(skel->reljoints.empty() ? nullptr : new dualquat[skel->reljoints.size()]),
     160           3 :       verts(skel->verts.size()),
     161           3 :       collisions(0),
     162           3 :       floating(0),
     163           3 :       unsticks(INT_MAX),
     164           3 :       timestep(0),
     165           3 :       scale(scale),
     166           6 :       rotfrictions(skel->rotfrictions.size())
     167             : {
     168           3 : }
     169             : 
     170           3 : ragdolldata::~ragdolldata()
     171             : {
     172           3 :     if(animjoints)
     173             :     {
     174           1 :         delete[] animjoints;
     175             :     }
     176           3 :     if(reljoints)
     177             :     {
     178           0 :         delete[] reljoints;
     179             :     }
     180           3 : }
     181             : 
     182             : /*
     183             :     seed particle position = avg(modelview * base2anim * spherepos)
     184             :     mapped transform = invert(curtri) * origtrig
     185             :     parented transform = parent{invert(curtri) * origtrig} * (invert(parent{base2anim}) * base2anim)
     186             : */
     187             : 
     188           1 : void ragdolldata::calcanimjoint(int i, const matrix4x3 &anim)
     189             : {
     190           1 :     if(!animjoints)
     191             :     {
     192           1 :         return;
     193             :     }
     194           0 :     const ragdollskel::joint &j = skel->joints[i];
     195           0 :     vec pos(0, 0, 0);
     196           0 :     for(int k = 0; k < 3; ++k)
     197             :     {
     198           0 :         if(j.vert[k]>=0)
     199             :         {
     200           0 :             pos.add(verts[j.vert[k]].pos);
     201             :         }
     202             :     }
     203           0 :     pos.mul(j.weight);
     204             : 
     205           0 :     const ragdollskel::tri &t = skel->tris[j.tri];
     206           0 :     matrix4x3 m;
     207           0 :     const vec &v1 = verts[t.vert[0]].pos,
     208           0 :               &v2 = verts[t.vert[1]].pos,
     209           0 :               &v3 = verts[t.vert[2]].pos;
     210           0 :     m.a = vec(v2).sub(v1).normalize();
     211           0 :     m.c.cross(m.a, vec(v3).sub(v1)).normalize();
     212           0 :     m.b.cross(m.c, m.a);
     213           0 :     m.d = pos;
     214           0 :     animjoints[i].transposemul(m, anim);
     215             : }
     216             : 
     217           1 : void ragdolldata::calctris()
     218             : {
     219           1 :     for(uint i = 0; i < skel->tris.size(); i++)
     220             :     {
     221           0 :         const ragdollskel::tri &t = skel->tris[i];
     222           0 :         matrix3 &m = tris[i];
     223           0 :         const vec &v1 = verts[t.vert[0]].pos,
     224           0 :                   &v2 = verts[t.vert[1]].pos,
     225           0 :                   &v3 = verts[t.vert[2]].pos;
     226           0 :         m.a = vec(v2).sub(v1).normalize();
     227           0 :         m.c.cross(m.a, vec(v3).sub(v1)).normalize();
     228           0 :         m.b.cross(m.c, m.a);
     229             :     }
     230           1 : }
     231             : 
     232           1 : void ragdolldata::calcboundsphere()
     233             : {
     234           1 :     center = vec(0, 0, 0);
     235           3 :     for(const vert &v : verts)
     236             :     {
     237           2 :         center.add(v.pos);
     238             :     }
     239           1 :     center.div(verts.size());
     240           1 :     radius = 0;
     241           3 :     for(const vert &v : verts)
     242             :     {
     243           2 :         radius = std::max(radius, v.pos.dist(center));
     244             :     }
     245           1 : }
     246             : 
     247             : VAR(ragdolltimestepmin, 1, 5, 50);
     248             : VAR(ragdolltimestepmax, 1, 10, 50);
     249             : 
     250           1 : void ragdolldata::init(const dynent *d)
     251             : {
     252           1 :     float ts = ragdolltimestepmin/1000.0f;
     253           3 :     for(vert &v : verts)
     254             :     {
     255           2 :         (v.oldpos = v.pos).sub(vec(d->vel).add(d->falling).mul(ts));
     256             :     }
     257           1 :     timestep = ts;
     258             : 
     259           1 :     calctris();
     260           1 :     calcboundsphere();
     261           1 :     offset = d->o;
     262           1 :     offset.sub(skel->eye >= 0 ? verts[skel->eye].pos : center);
     263           1 :     offset.z += (d->eyeheight + d->aboveeye)/2;
     264           1 : }
     265             : 
     266           0 : void ragdolldata::constraindist()
     267             : {
     268           0 :     for(const ragdollskel::distlimit &d : skel->distlimits)
     269             :     {
     270           0 :         vert &v1 = verts[d.vert[0]],
     271           0 :              &v2 = verts[d.vert[1]];
     272           0 :         vec dir = vec(v2.pos).sub(v1.pos);
     273           0 :         const float dist = dir.magnitude()/scale;
     274             :         float cdist;
     275           0 :         if(dist < d.mindist)
     276             :         {
     277           0 :             cdist = d.mindist;
     278             :         }
     279           0 :         else if(dist > d.maxdist)
     280             :         {
     281           0 :             cdist = d.maxdist;
     282             :         }
     283             :         else
     284             :         {
     285           0 :             continue;
     286             :         }
     287           0 :         if(dist > 1e-4f)
     288             :         {
     289           0 :             dir.mul(cdist*0.5f/dist);
     290             :         }
     291             :         else
     292             :         {
     293           0 :             dir = vec(0, 0, cdist*0.5f*scale);
     294             :         }
     295           0 :         const vec center = vec(v1.pos).add(v2.pos).mul(0.5f);
     296           0 :         v1.newpos.add(vec(center).sub(dir));
     297           0 :         v1.weight++;
     298           0 :         v2.newpos.add(vec(center).add(dir));
     299           0 :         v2.weight++;
     300             :     }
     301           0 : }
     302             : 
     303           0 : void ragdolldata::applyrotlimit(const ragdollskel::tri &t1, const ragdollskel::tri &t2, float angle, const vec &axis)
     304             : {
     305           0 :     vert &v1a = verts[t1.vert[0]],
     306           0 :          &v1b = verts[t1.vert[1]],
     307           0 :          &v1c = verts[t1.vert[2]],
     308           0 :          &v2a = verts[t2.vert[0]],
     309           0 :          &v2b = verts[t2.vert[1]],
     310           0 :          &v2c = verts[t2.vert[2]];
     311             :     // vec() copy constructor used below to deal with the fact that vec.add/sub() are destructive operations
     312           0 :     const vec m1 = vec(v1a.pos).add(v1b.pos).add(v1c.pos).div(3),
     313           0 :               m2 = vec(v2a.pos).add(v2b.pos).add(v2c.pos).div(3);
     314           0 :     vec q1a, q1b, q1c, q2a, q2b, q2c;
     315           0 :     const float w1 = q1a.cross(axis, vec(v1a.pos).sub(m1)).magnitude() +
     316           0 :                      q1b.cross(axis, vec(v1b.pos).sub(m1)).magnitude() +
     317           0 :                      q1c.cross(axis, vec(v1c.pos).sub(m1)).magnitude(),
     318           0 :                 w2 = q2a.cross(axis, vec(v2a.pos).sub(m2)).magnitude() +
     319           0 :                      q2b.cross(axis, vec(v2b.pos).sub(m2)).magnitude() +
     320           0 :                      q2c.cross(axis, vec(v2c.pos).sub(m2)).magnitude();
     321           0 :     angle /= w1 + w2 + 1e-9f;
     322           0 :     const float a1 = angle*w2,
     323           0 :                 a2 = -angle*w1,
     324           0 :                 s1 = std::sin(a1),
     325           0 :                 s2 = std::sin(a2);
     326           0 :     const vec c1 = vec(axis).mul(1 - std::cos(a1)),
     327           0 :               c2 = vec(axis).mul(1 - std::cos(a2));
     328           0 :     v1a.newpos.add(vec().cross(c1, q1a).madd(q1a, s1).add(v1a.pos));
     329           0 :     v1a.weight++;
     330           0 :     v1b.newpos.add(vec().cross(c1, q1b).madd(q1b, s1).add(v1b.pos));
     331           0 :     v1b.weight++;
     332           0 :     v1c.newpos.add(vec().cross(c1, q1c).madd(q1c, s1).add(v1c.pos));
     333           0 :     v1c.weight++;
     334           0 :     v2a.newpos.add(vec().cross(c2, q2a).madd(q2a, s2).add(v2a.pos));
     335           0 :     v2a.weight++;
     336           0 :     v2b.newpos.add(vec().cross(c2, q2b).madd(q2b, s2).add(v2b.pos));
     337           0 :     v2b.weight++;
     338           0 :     v2c.newpos.add(vec().cross(c2, q2c).madd(q2c, s2).add(v2c.pos));
     339           0 :     v2c.weight++;
     340           0 : }
     341             : 
     342           0 : void ragdolldata::constrainrot()
     343             : {
     344           0 :     calctris();
     345           0 :     for(const ragdollskel::rotlimit &r : skel->rotlimits)
     346             :     {
     347           0 :         matrix3 rot;
     348           0 :         rot.mul(tris[r.tri[0]], r.middle);
     349           0 :         rot.multranspose(tris[r.tri[1]]);
     350             : 
     351           0 :         vec axis;
     352             :         float angle;
     353           0 :         const float tr = rot.trace();
     354           0 :         if(tr >= r.maxtrace || !rot.calcangleaxis(tr, angle, axis))
     355             :         {
     356           0 :             continue;
     357             :         }
     358           0 :         angle = r.maxangle - angle + 1e-3f;
     359           0 :         applyrotlimit(skel->tris[r.tri[0]], skel->tris[r.tri[1]], angle, axis);
     360             :     }
     361           0 : }
     362             : 
     363             : /**
     364             :  * @brief Sets the shadowing elements in rotfrictions according to their respective values in the pointed ragdollskel.
     365             :  *
     366             :  * For each member of ragdolldata::rotfrictions, sets its value to the transposed multiplication of
     367             :  * the data pointed to by the indices of the first two vertices in the ragdollskel::rotfrictions tri data.
     368             :  *
     369             :  * Previous values in ragdolldata::rotfrictions are ignored and overwritten.
     370             :  */
     371           0 : void ragdolldata::calcrotfriction()
     372             : {
     373           0 :     for(size_t i = 0; i < skel->rotfrictions.size(); ++i)
     374             :     {
     375           0 :         rotfrictions[i].transposemul(tris[skel->rotfrictions[i].tri[0]], tris[skel->rotfrictions[i].tri[1]]);
     376             :     }
     377           0 : }
     378             : 
     379           0 : void ragdolldata::applyrotfriction(float ts)
     380             : {
     381           0 :     static FVAR(ragdollrotfric, 0, 0.85f, 1);
     382           0 :     static FVAR(ragdollrotfricstop, 0, 0.1f, 1);
     383             : 
     384           0 :     calctris();
     385           0 :     float stopangle = 2*M_PI*ts*ragdollrotfricstop,
     386           0 :           rotfric = 1.0f - std::pow(ragdollrotfric, ts*1000.0f/ragdolltimestepmin);
     387           0 :     for(size_t i = 0; i < skel->rotfrictions.size(); ++i)
     388             :     {
     389           0 :         matrix3 rot;
     390           0 :         rot.mul(tris[ skel->rotfrictions[i].tri[0]], rotfrictions[i]);
     391           0 :         rot.multranspose(tris[ skel->rotfrictions[i].tri[1]]);
     392             : 
     393           0 :         vec axis;
     394             :         float angle;
     395           0 :         if(!rot.calcangleaxis(angle, axis))
     396             :         {
     397           0 :             continue;
     398             :         }
     399           0 :         angle *= -(std::fabs(angle) >= stopangle ? rotfric : 1.0f);
     400           0 :         applyrotlimit(skel->tris[skel->rotfrictions[i].tri[0]], skel->tris[ skel->rotfrictions[i].tri[1]], angle, axis);
     401             :     }
     402           0 :     for(vert &v : verts)
     403             :     {
     404           0 :         if(!v.weight)
     405             :         {
     406           0 :             continue;
     407             :         }
     408           0 :         v.pos = v.newpos.div(v.weight);
     409           0 :         v.newpos = vec(0, 0, 0);
     410           0 :         v.weight = 0;
     411             :     }
     412           0 : }
     413             : 
     414           0 : void ragdolldata::tryunstick(float speed)
     415             : {
     416             :     /* vec `unstuck` is the average position of all vertices with the `stuck` flag
     417             :      * that are not found to collide with the world, as well as all vertices without
     418             :      * the `stuck` flag.
     419             :      */
     420           0 :     vec unstuck(0, 0, 0);
     421           0 :     size_t stuck = 0;
     422           0 :     for(uint i = 0; i < verts.size(); i++)
     423             :     {
     424           0 :         vert &v = verts[i];
     425           0 :         if(v.stuck)
     426             :         {
     427           0 :             if(collidevert(v.pos, vec(0, 0, 0), skel->verts[i].radius))
     428             :             {
     429           0 :                 stuck++;
     430           0 :                 continue;
     431             :             }
     432           0 :             v.stuck = false;
     433             :         }
     434           0 :         unstuck.add(v.pos);
     435             :     }
     436           0 :     unsticks = 0;
     437           0 :     if(!stuck || stuck >= verts.size())
     438             :     {
     439           0 :         return;
     440             :     }
     441           0 :     unstuck.div(verts.size() - stuck); //get average by dividing by # verts not stuck
     442             : 
     443           0 :     for(vert &v : verts)
     444             :     {
     445           0 :         if(v.stuck)
     446             :         {
     447           0 :             v.pos.add(vec(unstuck).sub(v.pos).rescale(speed));
     448           0 :             unsticks++;
     449             :         }
     450             :     }
     451             : }
     452             : 
     453           0 : void ragdolldata::constrain()
     454             : {
     455           0 :     static VAR(ragdollconstrain, 1, 7, 100); //number of iterations to run ragdolldata::constrain() for
     456             :     //note: this for loop does not use the loop variable `i` anywhere
     457           0 :     for(int i = 0; i < ragdollconstrain; ++i)
     458             :     {
     459           0 :         constraindist();
     460           0 :         for(vert &v : verts)
     461             :         {
     462           0 :             v.undo = v.pos;
     463           0 :             if(v.weight)
     464             :             {
     465           0 :                 v.pos = v.newpos.div(v.weight);
     466           0 :                 v.newpos = vec(0, 0, 0);
     467           0 :                 v.weight = 0;
     468             :             }
     469             :         }
     470             : 
     471           0 :         constrainrot();
     472           0 :         for(uint j = 0; j < verts.size(); j++)
     473             :         {
     474           0 :             vert &v = verts[j];
     475           0 :             if(v.weight)
     476             :             {
     477           0 :                 v.pos = v.newpos.div(v.weight);
     478           0 :                 v.newpos = vec(0, 0, 0);
     479           0 :                 v.weight = 0;
     480             :             }
     481           0 :             if(v.pos != v.undo && collidevert(v.pos, vec(v.pos).sub(v.undo), skel->verts[j].radius))
     482             :             {
     483           0 :                 vec dir = vec(v.pos).sub(v.oldpos);
     484           0 :                 const float facing = dir.dot(collidewall);
     485           0 :                 if(facing < 0)
     486             :                 {
     487           0 :                     v.oldpos = vec(v.undo).sub(dir.msub(collidewall, 2*facing));
     488             :                 }
     489           0 :                 v.pos = v.undo;
     490           0 :                 v.collided = true;
     491             :             }
     492             :         }
     493             :     }
     494           0 : }
     495             : 
     496           0 : void ragdolldata::move(bool water, float ts)
     497             : {
     498             : 
     499           0 :     static FVAR(ragdollbodyfric, 0, 0.95f, 1);
     500           0 :     static FVAR(ragdollbodyfricscale, 0, 2, 10);
     501           0 :     static FVAR(ragdollwaterfric, 0, 0.85f, 1);
     502           0 :     static FVAR(ragdollgroundfric, 0, 0.8f, 1);
     503           0 :     static FVAR(ragdollairfric, 0, 0.996f, 1);
     504           0 :     static FVAR(ragdollunstick, 0, 10, 1e3f);
     505           0 :     static VAR(ragdollexpireoffset, 0, 2500, 30000);
     506           0 :     static VAR(ragdollwaterexpireoffset, 0, 4000, 30000);
     507             : 
     508           0 :     if(collidemillis && lastmillis > collidemillis)
     509             :     {
     510           0 :         return;
     511             :     }
     512             : 
     513           0 :     calcrotfriction();
     514           0 :     const float tsfric = timestep ? ts/timestep : 1,
     515           0 :                 airfric = ragdollairfric + std::min((ragdollbodyfricscale*collisions)/verts.size(), 1.0f)*(ragdollbodyfric - ragdollairfric);
     516           0 :     collisions = 0;
     517           0 :     for(uint i = 0; i < verts.size(); i++)
     518             :     {
     519           0 :         vert &v = verts[i];
     520           0 :         vec dpos = vec(v.pos).sub(v.oldpos);
     521           0 :         dpos.z -= 100*ts*ts;
     522           0 :         if(water)
     523             :         {
     524             :             //reinterpret cast pointer -> "random" seed value
     525           0 :             dpos.z += 0.25f*std::sin(detrnd(reinterpret_cast<size_t>(this)+i, 360)/RAD + lastmillis/10000.0f*M_PI)*ts;
     526             :         }
     527           0 :         dpos.mul(std::pow((water ? ragdollwaterfric : 1.0f) * (v.collided ? ragdollgroundfric : airfric), ts*1000.0f/ragdolltimestepmin)*tsfric);
     528           0 :         v.oldpos = v.pos;
     529           0 :         v.pos.add(dpos);
     530             :     }
     531           0 :     applyrotfriction(ts);
     532           0 :     for(uint i = 0; i < verts.size(); i++)
     533             :     {
     534           0 :         vert &v = verts[i];
     535           0 :         if(v.pos.z < 0)
     536             :         {
     537           0 :             v.pos.z = 0;
     538           0 :             v.oldpos = v.pos;
     539           0 :             collisions++;
     540             :         }
     541           0 :         vec dir = vec(v.pos).sub(v.oldpos);
     542           0 :         v.collided = collidevert(v.pos, dir, skel->verts[i].radius);
     543           0 :         if(v.collided)
     544             :         {
     545           0 :             v.pos = v.oldpos;
     546           0 :             v.oldpos.sub(dir.reflect(collidewall));
     547           0 :             collisions++;
     548             :         }
     549             :     }
     550           0 :     if(unsticks && ragdollunstick)
     551             :     {
     552           0 :         tryunstick(ts*ragdollunstick);
     553             :     }
     554           0 :     timestep = ts;
     555           0 :     if(collisions)
     556             :     {
     557           0 :         floating = 0;
     558           0 :         if(!collidemillis)
     559             :         {
     560           0 :             collidemillis = lastmillis + (water ? ragdollwaterexpireoffset : ragdollexpireoffset);
     561             :         }
     562             :     }
     563           0 :     else if(++floating > 1 && lastmillis < collidemillis)
     564             :     {
     565           0 :         collidemillis = 0;
     566             :     }
     567           0 :     constrain();
     568           0 :     calctris();
     569           0 :     calcboundsphere();
     570             : }
     571             : 
     572           0 : bool ragdolldata::collidevert(const vec &pos, const vec &dir, float radius)
     573             : {
     574             :     static struct vertent : physent
     575             :     {
     576           0 :         vertent()
     577           0 :         {
     578           0 :             type = PhysEnt_Bounce;
     579           0 :             radius = xradius = yradius = eyeheight = aboveeye = 1;
     580           0 :         }
     581           0 :     } v;
     582           0 :     v.o = pos;
     583           0 :     if(v.radius != radius) //(radius param != 1)
     584             :     {
     585           0 :         v.radius = v.xradius = v.yradius = v.eyeheight = v.aboveeye = radius;
     586             :     }
     587             :     //collide generated vertent (point with sphere bounding of r = radius)
     588             :     //with dir parameter
     589           0 :     return collide(&v, dir, 0, false);
     590             : }
     591             : 
     592             : //used in iengine
     593           0 : void moveragdoll(dynent *d)
     594             : {
     595           0 :     static FVAR(ragdolleyesmooth, 0, 0.5f, 1);
     596           0 :     static VAR(ragdolleyesmoothmillis, 1, 250, 10000);
     597           0 :     if(!curtime || !d->ragdoll)
     598             :     {
     599           0 :         return;
     600             :     }
     601           0 :     if(!d->ragdoll->collidemillis || lastmillis < d->ragdoll->collidemillis)
     602             :     {
     603           0 :         const int lastmove = d->ragdoll->lastmove;
     604           0 :         while(d->ragdoll->lastmove + (lastmove == d->ragdoll->lastmove ? ragdolltimestepmin : ragdolltimestepmax) <= lastmillis)
     605             :         {
     606           0 :             const int timestep = std::min(ragdolltimestepmax, lastmillis - d->ragdoll->lastmove),
     607           0 :                       material = rootworld.lookupmaterial(vec(d->ragdoll->center.x, d->ragdoll->center.y, d->ragdoll->center.z + d->ragdoll->radius/2));
     608           0 :             const bool water = (material&MatFlag_Volume) == Mat_Water;
     609           0 :             d->inwater = water ? material&MatFlag_Volume : Mat_Air;
     610           0 :             d->ragdoll->move(water, timestep/1000.0f);
     611           0 :             d->ragdoll->lastmove += timestep;
     612             :         }
     613             :     }
     614             : 
     615           0 :     vec eye = d->ragdoll->skel->eye >= 0 ? d->ragdoll->verts[d->ragdoll->skel->eye].pos : d->ragdoll->center;
     616           0 :     eye.add(d->ragdoll->offset);
     617           0 :     const float k = std::pow(ragdolleyesmooth, static_cast<float>(curtime)/ragdolleyesmoothmillis);
     618           0 :     d->o.lerp(eye, 1-k);
     619             : }
     620             : 
     621           1 : void cleanragdoll(dynent *d)
     622             : {
     623           1 :     if(d->ragdoll)
     624             :     {
     625           1 :         delete d->ragdoll;
     626           1 :         d->ragdoll = nullptr;
     627             :     }
     628           1 : }

Generated by: LCOV version 1.14