LCOV - code coverage report
Current view: top level - engine/model - ragdoll.cpp (source / functions) Coverage Total Hit
Test: Libprimis Test Coverage Lines: 34.8 % 348 121
Test Date: 2026-05-09 04:28:55 Functions: 52.2 % 23 12

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

Generated by: LCOV version 2.0-1