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: 2025-12-23 06:24:53 Functions: 52.2 % 23 12

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

Generated by: LCOV version 2.0-1