LCOV - code coverage report
Current view: top level - engine/model - skelmodel.cpp (source / functions) Hit Total Coverage
Test: Libprimis Test Coverage Lines: 468 1008 46.4 %
Date: 2024-11-22 05:07:59 Functions: 68 114 59.6 %

          Line data    Source code
       1             : /* skelmodel.cpp: implementation for the skeletal animation object
       2             :  *
       3             :  * the skelmodel object handles the behavior of animated skeletal models -- such
       4             :  * as the ones used by the player and bots
       5             :  *
       6             :  * for the procedural modification of skeletal models using ragdoll physics, see
       7             :  * ragdoll.h
       8             :  *
       9             :  * this file contains the implementation for the skelmodel object, see skelmodel.h
      10             :  * for the class definition
      11             :  */
      12             : #include "../libprimis-headers/cube.h"
      13             : #include "../../shared/geomexts.h"
      14             : #include "../../shared/glemu.h"
      15             : #include "../../shared/glexts.h"
      16             : 
      17             : #include <optional>
      18             : #include <memory>
      19             : 
      20             : #include "interface/console.h"
      21             : #include "interface/control.h"
      22             : #include "interface/cs.h"
      23             : 
      24             : #include "render/rendergl.h"
      25             : #include "render/renderlights.h"
      26             : #include "render/rendermodel.h"
      27             : #include "render/shader.h"
      28             : #include "render/shaderparam.h"
      29             : #include "render/texture.h"
      30             : 
      31             : #include "world/entities.h"
      32             : #include "world/octaworld.h"
      33             : #include "world/bih.h"
      34             : 
      35             : #include "model.h"
      36             : #include "ragdoll.h"
      37             : #include "animmodel.h"
      38             : #include "skelmodel.h"
      39             : 
      40             : VAR(maxskelanimdata, 1, 192, 0); //sets maximum number of gpu bones
      41             : 
      42             : //animcacheentry child classes
      43             : 
      44           0 : bool skelmodel::vbocacheentry::check() const
      45             : {
      46           0 :     return !vbuf;
      47             : }
      48             : 
      49          64 : skelmodel::vbocacheentry::vbocacheentry() : vbuf(0), owner(-1)
      50             : {
      51          64 : }
      52             : 
      53           0 : void skelmodel::skelcacheentry::nextversion()
      54             : {
      55           0 :     version = Shader::uniformlocversion();
      56           0 : }
      57             : 
      58          64 : skelmodel::skelcacheentry::skelcacheentry() : bdata(nullptr), version(-1)
      59             : {
      60          64 : }
      61             : 
      62          64 : skelmodel::blendcacheentry::blendcacheentry() : owner(-1)
      63             : {
      64          64 : }
      65             : 
      66             : //skelmodel::animcacheentry object
      67         128 : skelmodel::animcacheentry::animcacheentry() : ragdoll(nullptr)
      68             : {
      69         512 :     for(int k = 0; k < maxanimparts; ++k)
      70             :     {
      71         384 :         as[k].cur.fr1 = as[k].prev.fr1 = -1;
      72             :     }
      73         128 : }
      74             : 
      75           0 : bool skelmodel::animcacheentry::operator==(const animcacheentry &c) const
      76             : {
      77           0 :     for(int i = 0; i < maxanimparts; ++i)
      78             :     {
      79           0 :         if(as[i]!=c.as[i])
      80             :         {
      81           0 :             return false;
      82             :         }
      83             :     }
      84           0 :     return pitch==c.pitch && partmask==c.partmask && ragdoll==c.ragdoll && (!ragdoll || std::min(millis, c.millis) >= ragdoll->lastmove);
      85             : }
      86             : 
      87           0 : bool skelmodel::animcacheentry::operator!=(const animcacheentry &c) const
      88             : {
      89           0 :     return !(*this == c);
      90             : }
      91             : 
      92             : //pitchcorrect
      93             : 
      94           0 : skelmodel::skeleton::pitchcorrect::pitchcorrect(int bone, size_t target, float pitchscale, float pitchmin, float pitchmax) :
      95           0 :     bone(bone), parent (-1), target(target), pitchmin(pitchmin), pitchmax(pitchmax),
      96           0 :     pitchscale(pitchscale), pitchangle(0), pitchtotal(0)
      97             : {
      98           0 : }
      99             : 
     100           0 : skelmodel::skeleton::pitchcorrect::pitchcorrect() : parent(-1), pitchangle(0), pitchtotal(0)
     101             : {
     102           0 : }
     103             : 
     104             : //skeleton
     105             : 
     106           9 : const skelmodel::skelanimspec *skelmodel::skeleton::findskelanim(std::string_view name) const
     107             : {
     108           9 :     for(const skelanimspec &i : skelanims)
     109             :     {
     110           8 :         if(!i.name.empty())
     111             :         {
     112           8 :             if(name == i.name)
     113             :             {
     114           8 :                 return &i;
     115             :             }
     116             :         }
     117             :     }
     118           1 :     return nullptr;
     119             : }
     120             : 
     121           1 : skelmodel::skelanimspec &skelmodel::skeleton::addskelanim(std::string_view name, int numframes, int animframes)
     122             : {
     123           1 :     skelanims.push_back({name.data(), numframes, animframes});
     124           1 :     return skelanims.back();
     125             : }
     126             : 
     127           3 : skelmodel::skeleton::skeleton(skelmeshgroup * const group) :
     128           3 :     numbones(0),
     129           3 :     numgpubones(0),
     130           3 :     numframes(0),
     131           3 :     framebones(nullptr),
     132           3 :     ragdoll(nullptr),
     133           3 :     owner(group),
     134           3 :     numinterpbones(0),
     135           3 :     bones(nullptr)
     136             : {
     137           3 : }
     138             : 
     139           2 : skelmodel::skeleton::~skeleton()
     140             : {
     141           2 :     delete[] bones;
     142           2 :     delete[] framebones;
     143           2 :     if(ragdoll)
     144             :     {
     145           0 :         delete ragdoll;
     146           0 :         ragdoll = nullptr;
     147             :     }
     148           2 :     for(skelcacheentry &i : skelcache)
     149             :     {
     150           0 :         delete[] i.bdata;
     151             :     }
     152           2 : }
     153             : 
     154           4 : std::optional<size_t> skelmodel::skeleton::findbone(std::string_view name) const
     155             : {
     156           6 :     for(size_t i = 0; i < numbones; ++i)
     157             :     {
     158           6 :         if(bones[i].name.size() && bones[i].name == name)
     159             :         {
     160           4 :             return i;
     161             :         }
     162             :     }
     163           0 :     return std::nullopt;
     164             : }
     165             : 
     166           4 : std::optional<size_t> skelmodel::skeleton::findtag(std::string_view name) const
     167             : {
     168           6 :     for(size_t i = 0; i < tags.size(); i++)
     169             :     {
     170           4 :         if(!std::strcmp(tags[i].name.c_str(), name.data()))
     171             :         {
     172           2 :             return i;
     173             :         }
     174             :     }
     175           2 :     return std::nullopt;
     176             : }
     177             : 
     178           2 : bool skelmodel::skeleton::addtag(std::string_view name, int bone, const matrix4x3 &matrix)
     179             : {
     180           2 :     std::optional<size_t> idx = findtag(name);
     181           2 :     if(idx)
     182             :     {
     183           0 :         if(!testtags)
     184             :         {
     185           0 :             return false;
     186             :         }
     187           0 :         tag &t = tags[*idx];
     188           0 :         t.bone = bone;
     189           0 :         t.matrix = matrix;
     190             :     }
     191             :     else
     192             :     {
     193           2 :         tags.emplace_back(name, bone, matrix);
     194             :     }
     195           2 :     return true;
     196             : }
     197             : 
     198          11 : void skelmodel::skeleton::calcantipodes()
     199             : {
     200          11 :     antipodes.clear();
     201          11 :     std::vector<uint> schedule;
     202          55 :     for(size_t i = 0; i < numbones; ++i)
     203             :     {
     204          44 :         if(bones[i].group >= static_cast<int>(numbones))
     205             :         {
     206          44 :             bones[i].scheduled = schedule.size();
     207          44 :             schedule.push_back(i);
     208             :         }
     209             :         else
     210             :         {
     211           0 :             bones[i].scheduled = -1;
     212             :         }
     213             :     }
     214          55 :     for(uint i = 0; i < schedule.size(); i++)
     215             :     {
     216          44 :         const uint bone = schedule[i];
     217          44 :         const boneinfo &info = bones[bone];
     218         220 :         for(size_t j = 0; j < numbones; ++j)
     219             :         {
     220         176 :             if(std::abs(bones[j].group) == bone && bones[j].scheduled < 0)
     221             :             {
     222           0 :                 antipodes.emplace_back(antipode(info.interpindex, bones[j].interpindex));
     223           0 :                 bones[j].scheduled = schedule.size();
     224           0 :                 schedule.push_back(j);
     225             :             }
     226             :         }
     227          44 :         if(i + 1 == schedule.size())
     228             :         {
     229          11 :             std::optional<int> conflict = std::nullopt;
     230          55 :             for(size_t j = 0; j < numbones; ++j)
     231             :             {
     232          44 :                 if(bones[j].group < static_cast<int>(numbones) && bones[j].scheduled < 0)
     233             :                 {
     234           0 :                     conflict = std::min(conflict.value(), std::abs(bones[j].group));
     235             :                 }
     236             :             }
     237          11 :             if(conflict)
     238             :             {
     239           0 :                 bones[conflict.value()].scheduled = schedule.size();
     240           0 :                 schedule.push_back(conflict.value());
     241             :             }
     242             :         }
     243             :     }
     244          11 : }
     245             : 
     246          11 : void skelmodel::skeleton::remapbones()
     247             : {
     248          55 :     for(size_t i = 0; i < numbones; ++i)//loop i
     249             :     {
     250          44 :         boneinfo &info = bones[i];
     251          44 :         info.interpindex = -1;
     252          44 :         info.ragdollindex = -1;
     253             :     }
     254          11 :     numgpubones = 0;
     255          33 :     for(blendcombo &c : owner->blendcombos)
     256             :     {
     257          44 :         for(size_t k = 0; k < c.size(); ++k) //loop k
     258             :         {
     259          22 :             if(!c.bonedata[k].weight)
     260             :             {
     261           0 :                 c.setinterpbones(k > 0 ? c.bonedata[k-1].interpbone : 0, k);
     262           0 :                 continue;
     263             :             }
     264          22 :             boneinfo &info = bones[c.getbone(k)];
     265          22 :             if(info.interpindex < 0)
     266             :             {
     267          22 :                 info.interpindex = numgpubones++;
     268             :             }
     269          22 :             c.setinterpbones(info.interpindex, k);
     270          22 :             if(info.group < 0)
     271             :             {
     272           0 :                 continue;
     273             :             }
     274             :             //for each weight represented, compare with other weights in this blendcombo
     275          44 :             for(size_t l = 0; l < c.size(); ++l) //note this is a loop l (level 4)
     276             :             {
     277          22 :                 if(l == k)
     278             :                 {
     279          22 :                     continue;
     280             :                 }
     281             :                 //get the index of the weight at l
     282           0 :                 int parent = c.getbone(l);
     283             :                 //if k's parent is this bone, or k's parent exists and k's parent is l's parent
     284           0 :                 if(info.parent == parent || (info.parent >= 0 && info.parent == bones[parent].parent))
     285             :                 {
     286             :                     //set k's group to be its parent negated
     287           0 :                     info.group = -info.parent;
     288           0 :                     break;
     289             :                 }
     290             :                 //if k's group is <= l's index
     291           0 :                 if(info.group <= parent)
     292             :                 {
     293           0 :                     continue;
     294             :                 }
     295             :                 //if k's group is > l's index, then k is a child of l (children are assigned higher numbers than parents)
     296           0 :                 int child = c.getbone(k);
     297             :                 //while l's index is greater than k's (implying l is a child of k), change l until l is a parent of k
     298           0 :                 while(parent > child)
     299             :                 {
     300           0 :                     parent = bones[parent].parent;
     301             :                 }
     302             :                 //if l, or one of its parents, is not k, set k's group to the index of the bone at l
     303           0 :                 if(parent != child)
     304             :                 {
     305           0 :                     info.group = c.getbone(l);
     306             :                 }
     307             :             }
     308             :         }
     309             :     }
     310          11 :     numinterpbones = numgpubones;
     311          31 :     for(const tag &i : tags)
     312             :     {
     313          20 :         boneinfo &info = bones[i.bone];
     314          20 :         if(info.interpindex < 0)
     315             :         {
     316          20 :             info.interpindex = numinterpbones++;
     317             :         }
     318             :     }
     319          11 :     if(ragdoll)
     320             :     {
     321           0 :         for(uint i = 0; i < ragdoll->joints.size(); i++)
     322             :         {
     323           0 :             boneinfo &info = bones[ragdoll->joints[i].bone];
     324           0 :             if(info.interpindex < 0)
     325             :             {
     326           0 :                 info.interpindex = numinterpbones++;
     327             :             }
     328           0 :             info.ragdollindex = i;
     329             :         }
     330             :     }
     331          55 :     for(size_t i = 0; i < numbones; ++i)
     332             :     {
     333          44 :         const boneinfo &info = bones[i];
     334          44 :         if(info.interpindex < 0)
     335             :         {
     336           2 :             continue;
     337             :         }
     338          43 :         for(int parent = info.parent; parent >= 0 && bones[parent].interpindex < 0; parent = bones[parent].parent)
     339             :         {
     340           1 :             bones[parent].interpindex = numinterpbones++;
     341             :         }
     342             :     }
     343          55 :     for(size_t i = 0; i < numbones; ++i)
     344             :     {
     345          44 :         boneinfo &info = bones[i];
     346          44 :         if(info.interpindex < 0)
     347             :         {
     348           1 :             continue;
     349             :         }
     350          43 :         info.interpparent = info.parent >= 0 ? bones[info.parent].interpindex : -1;
     351             :     }
     352          11 :     if(ragdoll)
     353             :     {
     354           0 :         for(size_t i = 0; i < numbones; ++i)
     355             :         {
     356           0 :             const boneinfo &info = bones[i];
     357           0 :             if(info.interpindex < 0 || info.ragdollindex >= 0)
     358             :             {
     359           0 :                 continue;
     360             :             }
     361           0 :             for(int parent = info.parent; parent >= 0; parent = bones[parent].parent)
     362             :             {
     363           0 :                 if(bones[parent].ragdollindex >= 0)
     364             :                 {
     365           0 :                     ragdoll->addreljoint(i, bones[parent].ragdollindex);
     366           0 :                     break;
     367             :                 }
     368             :             }
     369             :         }
     370             :     }
     371          11 :     calcantipodes();
     372          11 : }
     373             : 
     374           9 : void skelmodel::skeleton::addpitchdep(int bone, int frame)
     375             : {
     376          27 :     for(; bone >= 0; bone = bones[bone].parent)
     377             :     {
     378          18 :         uint pos = pitchdeps.size();
     379          18 :         for(uint j = 0; j < pitchdeps.size(); j++)
     380             :         {
     381           9 :             if(bone <= pitchdeps[j].bone)
     382             :             {
     383           9 :                 if(bone == pitchdeps[j].bone)
     384             :                 {
     385           0 :                     goto nextbone;
     386             :                 }
     387           9 :                 pos = j;
     388           9 :                 break;
     389             :             }
     390             :         }
     391             :         { //if goto nextbone not called (note: no control logic w/ braces)
     392          18 :             pitchdep d;
     393          18 :             d.bone = bone;
     394          18 :             d.parent = -1;
     395          18 :             d.pose = framebones[frame*numbones + bone];
     396          18 :             pitchdeps.insert(pitchdeps.begin() + pos, d);
     397             :         }
     398          18 :     nextbone:;
     399             :     }
     400           9 : }
     401             : 
     402          18 : std::optional<size_t> skelmodel::skeleton::findpitchdep(int bone) const
     403             : {
     404          27 :     for(uint i = 0; i < pitchdeps.size(); i++)
     405             :     {
     406          27 :         if(bone <= pitchdeps[i].bone)
     407             :         {
     408          18 :             return bone == pitchdeps[i].bone ? std::optional<uint>(i) : std::optional<uint>(std::nullopt);
     409             :         }
     410             :     }
     411           0 :     return std::nullopt;
     412             : }
     413             : 
     414          18 : std::optional<size_t> skelmodel::skeleton::findpitchcorrect(int bone) const
     415             : {
     416          18 :     for(uint i = 0; i < pitchcorrects.size(); i++)
     417             :     {
     418           0 :         if(bone <= pitchcorrects[i].bone)
     419             :         {
     420           0 :             return bone == pitchcorrects[i].bone ? std::optional<uint>(i) : std::optional<uint>(std::nullopt);
     421             :         }
     422             :     }
     423          18 :     return std::nullopt;
     424             : }
     425             : 
     426          11 : void skelmodel::skeleton::initpitchdeps()
     427             : {
     428          11 :     pitchdeps.clear();
     429          11 :     if(pitchtargets.empty())
     430             :     {
     431           2 :         return;
     432             :     }
     433          18 :     for(pitchtarget &t : pitchtargets)
     434             :     {
     435           9 :         t.deps = -1;
     436           9 :         addpitchdep(t.bone, t.frame);
     437             :     }
     438          27 :     for(pitchdep &d : pitchdeps)
     439             :     {
     440          18 :         int parent = bones[d.bone].parent;
     441          18 :         if(parent >= 0)
     442             :         {
     443           9 :             std::optional<size_t> j = findpitchdep(parent);
     444           9 :             if(j)
     445             :             {
     446           9 :                 d.parent = j.value();
     447           9 :                 d.pose.mul(pitchdeps[j.value()].pose, dualquat(d.pose));
     448             :             }
     449             :         }
     450             :     }
     451          18 :     for(pitchtarget &t : pitchtargets)
     452             :     {
     453           9 :         std::optional<size_t> j = findpitchdep(t.bone);
     454           9 :         if(j)
     455             :         {
     456           9 :             t.deps = j.value();
     457           9 :             t.pose = pitchdeps[j.value()].pose;
     458             :         }
     459           9 :         t.corrects = -1;
     460          27 :         for(int parent = t.bone; parent >= 0; parent = bones[parent].parent)
     461             :         {
     462          18 :             std::optional<size_t> newpitchcorrect = findpitchcorrect(parent);
     463          18 :             t.corrects = newpitchcorrect ? newpitchcorrect.value() : -1;
     464          18 :             if(t.corrects >= 0)
     465             :             {
     466           0 :                 break;
     467             :             }
     468             :         }
     469             :     }
     470           9 :     for(uint i = 0; i < pitchcorrects.size(); i++)
     471             :     {
     472           0 :         pitchcorrect &c = pitchcorrects[i];
     473           0 :         bones[c.bone].correctindex = i;
     474           0 :         c.parent = -1;
     475           0 :         for(int parent = c.bone;;)
     476             :         {
     477           0 :             parent = bones[parent].parent;
     478           0 :             if(parent < 0)
     479             :             {
     480           0 :                 break;
     481             :             }
     482           0 :             std::optional<size_t> newpitchcorrect = findpitchcorrect(parent);
     483           0 :             c.parent = newpitchcorrect ? newpitchcorrect.value() : -1;
     484           0 :             if(c.parent >= 0)
     485             :             {
     486           0 :                 break;
     487             :             }
     488           0 :         }
     489             :     }
     490             : }
     491             : 
     492          11 : void skelmodel::skeleton::optimize()
     493             : {
     494          11 :     cleanup();
     495          11 :     if(ragdoll)
     496             :     {
     497           0 :         ragdoll->setup();
     498             :     }
     499          11 :     remapbones();
     500          11 :     initpitchdeps();
     501          11 : }
     502             : 
     503           4 : void skelmodel::skeleton::expandbonemask(uchar *expansion, int bone, int val) const
     504             : {
     505           4 :     expansion[bone] = val;
     506           4 :     bone = bones[bone].children;
     507           7 :     while(bone>=0)
     508             :     {
     509           3 :         expandbonemask(expansion, bone, val);
     510           3 :         bone = bones[bone].next;
     511             :     }
     512           4 : }
     513             : 
     514           1 : void skelmodel::skeleton::applybonemask(const std::vector<uint> &mask, std::vector<uchar> &partmask, int partindex) const
     515             : {
     516           1 :     if(mask.empty() || mask.front()==Bonemask_End)
     517             :     {
     518           0 :         return;
     519             :     }
     520           1 :     uchar *expansion = new uchar[numbones];
     521           1 :     std::memset(expansion, mask.front()&Bonemask_Not ? 1 : 0, numbones);
     522           2 :     for(const uint &i : mask)
     523             :     {
     524           2 :         if(i == Bonemask_End)
     525             :         {
     526           1 :             break;
     527             :         }
     528           1 :         expandbonemask(expansion, i&Bonemask_Bone, i&Bonemask_Not ? 0 : 1);
     529             :     }
     530           5 :     for(size_t i = 0; i < numbones; ++i)
     531             :     {
     532           4 :         if(expansion[i])
     533             :         {
     534           4 :             partmask[i] = partindex;
     535             :         }
     536             :     }
     537           1 :     delete[] expansion;
     538             : }
     539             : 
     540           1 : void skelmodel::skeleton::linkchildren()
     541             : {
     542           5 :     for(size_t i = 0; i < numbones; ++i)
     543             :     {
     544           4 :         boneinfo &b = bones[i];
     545           4 :         b.children = -1;
     546           4 :         if(b.parent<0)
     547             :         {
     548           1 :             b.next = -1;
     549             :         }
     550             :         else
     551             :         {
     552           3 :             b.next = bones[b.parent].children;
     553           3 :             bones[b.parent].children = i;
     554             :         }
     555             :     }
     556           1 : }
     557             : 
     558           0 : int skelmodel::skeleton::availgpubones()
     559             : {
     560           0 :     return std::min(maxvsuniforms, maxskelanimdata) / 2;
     561             : }
     562             : 
     563           0 : float skelmodel::skeleton::calcdeviation(const vec &axis, const vec &forward, const dualquat &pose1, const dualquat &pose2)
     564             : {
     565           0 :     const vec forward1 = pose1.transformnormal(forward).project(axis).normalize(),
     566           0 :               forward2 = pose2.transformnormal(forward).project(axis).normalize(),
     567           0 :               daxis = vec().cross(forward1, forward2);
     568           0 :     const float dx = std::clamp(forward1.dot(forward2), -1.0f, 1.0f);
     569           0 :     float dy = std::clamp(daxis.magnitude(), -1.0f, 1.0f);
     570           0 :     if(daxis.dot(axis) < 0)
     571             :     {
     572           0 :         dy = -dy;
     573             :     }
     574           0 :     return atan2f(dy, dx)*RAD;
     575             : }
     576             : 
     577           0 : void skelmodel::skeleton::calcpitchcorrects(float pitch, const vec &axis, const vec &forward)
     578             : {
     579           0 :     for(pitchtarget &t : pitchtargets)
     580             :     {
     581           0 :         t.deviated = calcdeviation(axis, forward, t.pose, pitchdeps[t.deps].pose);
     582             :     }
     583           0 :     for(pitchcorrect &c : pitchcorrects)
     584             :     {
     585           0 :         c.pitchangle = c.pitchtotal = 0;
     586             :     }
     587           0 :     for(uint j = 0; j < pitchtargets.size(); j++)
     588             :     {
     589           0 :         const pitchtarget &t = pitchtargets[j];
     590           0 :         float tpitch = pitch - t.deviated;
     591           0 :         for(int parent = t.corrects; parent >= 0; parent = pitchcorrects[parent].parent)
     592             :         {
     593           0 :             tpitch -= pitchcorrects[parent].pitchangle;
     594             :         }
     595           0 :         if(t.pitchmin || t.pitchmax)
     596             :         {
     597           0 :             tpitch = std::clamp(tpitch, t.pitchmin, t.pitchmax);
     598             :         }
     599           0 :         for(pitchcorrect& c : pitchcorrects)
     600             :         {
     601           0 :             if(c.target != j)
     602             :             {
     603           0 :                 continue;
     604             :             }
     605           0 :             float total = c.parent >= 0 ? pitchcorrects[c.parent].pitchtotal : 0,
     606           0 :                   avail = tpitch - total,
     607           0 :                   used = tpitch*c.pitchscale;
     608           0 :             if(c.pitchmin || c.pitchmax)
     609             :             {
     610           0 :                 if(used < 0)
     611             :                 {
     612           0 :                     used = std::clamp(c.pitchmin, used, 0.0f);
     613             :                 }
     614             :                 else
     615             :                 {
     616           0 :                     used = std::clamp(c.pitchmax, 0.0f, used);
     617             :                 }
     618             :             }
     619           0 :             if(used < 0)
     620             :             {
     621           0 :                 used = std::clamp(avail, used, 0.0f);
     622             :             }
     623             :             else
     624             :             {
     625           0 :                 used = std::clamp(avail, 0.0f, used);
     626             :             }
     627           0 :             c.pitchangle = used;
     628           0 :             c.pitchtotal = used + total;
     629             :         }
     630             :     }
     631           0 : }
     632             : 
     633             : //private helper function for interpbones
     634           0 : dualquat skelmodel::skeleton::interpbone(int bone, const std::array<framedata, maxanimparts> &partframes, const AnimState *as, const uchar *partmask) const
     635             : {
     636           0 :     const AnimState &s = as[partmask[bone]];
     637           0 :     const framedata &f = partframes[partmask[bone]];
     638           0 :     dualquat d;
     639           0 :     (d = f.fr1[bone]).mul((1-s.cur.t)*s.interp);
     640           0 :     d.accumulate(f.fr2[bone], s.cur.t*s.interp);
     641           0 :     if(s.interp<1)
     642             :     {
     643           0 :         d.accumulate(f.pfr1[bone], (1-s.prev.t)*(1-s.interp));
     644           0 :         d.accumulate(f.pfr2[bone], s.prev.t*(1-s.interp));
     645             :     }
     646           0 :     return d;
     647             : }
     648             : 
     649           0 : void skelmodel::skeleton::interpbones(const AnimState *as, float pitch, const vec &axis, const vec &forward, int numanimparts, const uchar *partmask, skelcacheentry &sc)
     650             : {
     651           0 :     if(!sc.bdata)
     652             :     {
     653           0 :         sc.bdata = new dualquat[numinterpbones];
     654             :     }
     655           0 :     sc.nextversion();
     656             :     std::array<framedata, maxanimparts> partframes;
     657           0 :     for(int i = 0; i < numanimparts; ++i)
     658             :     {
     659           0 :         partframes[i].fr1 = &framebones[as[i].cur.fr1*numbones];
     660           0 :         partframes[i].fr2 = &framebones[as[i].cur.fr2*numbones];
     661           0 :         if(as[i].interp<1)
     662             :         {
     663           0 :             partframes[i].pfr1 = &framebones[as[i].prev.fr1*numbones];
     664           0 :             partframes[i].pfr2 = &framebones[as[i].prev.fr2*numbones];
     665             :         }
     666             :     }
     667           0 :     for(pitchdep &p : pitchdeps)
     668             :     {
     669           0 :         dualquat d = interpbone(p.bone, partframes, as, partmask);
     670           0 :         d.normalize();
     671           0 :         if(p.parent >= 0)
     672             :         {
     673           0 :             p.pose.mul(pitchdeps[p.parent].pose, d);
     674             :         }
     675             :         else
     676             :         {
     677           0 :             p.pose = d;
     678             :         }
     679             :     }
     680           0 :     calcpitchcorrects(pitch, axis, forward);
     681           0 :     for(size_t i = 0; i < numbones; ++i)
     682             :     {
     683           0 :         if(bones[i].interpindex>=0)
     684             :         {
     685           0 :             dualquat d = interpbone(i, partframes, as, partmask);
     686           0 :             d.normalize();
     687           0 :             const boneinfo &b = bones[i];
     688           0 :             if(b.interpparent<0)
     689             :             {
     690           0 :                 sc.bdata[b.interpindex] = d;
     691             :             }
     692             :             else
     693             :             {
     694           0 :                 sc.bdata[b.interpindex].mul(sc.bdata[b.interpparent], d);
     695             :             }
     696             :             float angle;
     697           0 :             if(b.pitchscale)
     698             :             {
     699           0 :                 angle = b.pitchscale*pitch + b.pitchoffset;
     700           0 :                 if(b.pitchmin || b.pitchmax)
     701             :                 {
     702           0 :                     angle = std::clamp(angle, b.pitchmin, b.pitchmax);
     703             :                 }
     704             :             }
     705           0 :             else if(b.correctindex >= 0)
     706             :             {
     707           0 :                 angle = pitchcorrects[b.correctindex].pitchangle;
     708             :             }
     709             :             else
     710             :             {
     711           0 :                 continue;
     712             :             }
     713           0 :             if(as->cur.anim & Anim_NoPitch || (as->interp < 1 && as->prev.anim & Anim_NoPitch))
     714             :             {
     715           0 :                 angle *= (as->cur.anim & Anim_NoPitch ? 0 : as->interp) + (as->interp < 1 && as->prev.anim & Anim_NoPitch ? 0 : 1 - as->interp);
     716             :             }
     717           0 :             sc.bdata[b.interpindex].mulorient(quat(axis, angle/RAD), b.base);
     718             :         }
     719             :     }
     720           0 :     for(const antipode &i : antipodes)
     721             :     {
     722           0 :         sc.bdata[i.child].fixantipodal(sc.bdata[i.parent]);
     723             :     }
     724           0 : }
     725             : 
     726           0 : void skelmodel::skeleton::initragdoll(ragdolldata &d, const skelcacheentry &sc, float scale) const
     727             : {
     728           0 :     const dualquat *bdata = sc.bdata;
     729           0 :     for(const ragdollskel::joint &j : ragdoll->joints)
     730             :     {
     731           0 :         const boneinfo &b = bones[j.bone];
     732           0 :         const dualquat &q = bdata[b.interpindex];
     733           0 :         for(int k = 0; k < 3; ++k)
     734             :         {
     735           0 :             if(j.vert[k] >= 0)
     736             :             {
     737           0 :                 const ragdollskel::vert &v = ragdoll->verts[j.vert[k]];
     738           0 :                 ragdolldata::vert &dv = d.verts[j.vert[k]];
     739           0 :                 dv.pos.add(q.transform(v.pos).mul(v.weight));
     740             :             }
     741             :         }
     742             :     }
     743           0 :     if(ragdoll->animjoints)
     744             :     {
     745           0 :         for(uint i = 0; i < ragdoll->joints.size(); i++)
     746             :         {
     747           0 :             const ragdollskel::joint &j = ragdoll->joints[i];
     748           0 :             const boneinfo &b = bones[j.bone];
     749           0 :             const dualquat &q = bdata[b.interpindex];
     750           0 :             d.calcanimjoint(i, matrix4x3(q));
     751             :         }
     752             :     }
     753           0 :     for(uint i = 0; i < ragdoll->verts.size(); i++)
     754             :     {
     755           0 :         ragdolldata::vert &dv = d.verts[i];
     756           0 :         matrixstack.top().transform(vec(dv.pos).mul(scale), dv.pos);
     757             :     }
     758           0 :     for(uint i = 0; i < ragdoll->reljoints.size(); i++)
     759             :     {
     760           0 :         const ragdollskel::reljoint &r = ragdoll->reljoints[i];
     761           0 :         const ragdollskel::joint &j = ragdoll->joints[r.parent];
     762           0 :         const boneinfo &br = bones[r.bone], &bj = bones[j.bone];
     763           0 :         d.reljoints[i].mul(dualquat(bdata[bj.interpindex]).invert(), bdata[br.interpindex]);
     764             :     }
     765           0 : }
     766             : 
     767           0 : void skelmodel::skeleton::genragdollbones(const ragdolldata &d, skelcacheentry &sc, const vec &translate, float scale) const
     768             : {
     769           0 :     if(!sc.bdata)
     770             :     {
     771           0 :         sc.bdata = new dualquat[numinterpbones];
     772             :     }
     773           0 :     sc.nextversion();
     774           0 :     const vec trans = vec(d.center).div(scale).add(translate);
     775           0 :     for(uint i = 0; i < ragdoll->joints.size(); i++)
     776             :     {
     777           0 :         const ragdollskel::joint &j = ragdoll->joints[i];
     778           0 :         const boneinfo &b = bones[j.bone];
     779           0 :         vec pos(0, 0, 0);
     780           0 :         for(int k = 0; k < 3; ++k)
     781             :         {
     782           0 :             if(j.vert[k]>=0)
     783             :             {
     784           0 :                 pos.add(d.verts[j.vert[k]].pos);
     785             :             }
     786             :         }
     787           0 :         pos.mul(j.weight/scale).sub(trans);
     788           0 :         matrix4x3 m;
     789           0 :         m.mul(d.tris[j.tri], pos, d.animjoints ? d.animjoints[i] : j.orient);
     790           0 :         sc.bdata[b.interpindex] = dualquat(m);
     791             :     }
     792           0 :     for(uint i = 0; i < ragdoll->reljoints.size(); i++)
     793             :     {
     794           0 :         const ragdollskel::reljoint &r = ragdoll->reljoints[i];
     795           0 :         const ragdollskel::joint &j = ragdoll->joints[r.parent];
     796           0 :         const boneinfo &br = bones[r.bone], &bj = bones[j.bone];
     797           0 :         sc.bdata[br.interpindex].mul(sc.bdata[bj.interpindex], d.reljoints[i]);
     798             :     }
     799           0 :     for(const antipode &i : antipodes)
     800             :     {
     801           0 :         sc.bdata[i.child].fixantipodal(sc.bdata[i.parent]);
     802             :     }
     803           0 : }
     804             : 
     805           0 : void skelmodel::skeleton::concattagtransform(int i, const matrix4x3 &m, matrix4x3 &n) const
     806             : {
     807           0 :     matrix4x3 t;
     808           0 :     t.mul(bones[tags[i].bone].base, tags[i].matrix);
     809           0 :     n.mul(m, t);
     810           0 : }
     811             : 
     812           0 : void skelmodel::skeleton::calctags(part *p, const skelcacheentry *sc) const
     813             : {
     814           0 :     for(part::linkedpart &l : p->links)
     815             :     {
     816           0 :         const tag &t = tags[l.tag];
     817           0 :         dualquat q;
     818           0 :         if(sc)
     819             :         {
     820           0 :             q.mul(sc->bdata[bones[t.bone].interpindex], bones[t.bone].base);
     821             :         }
     822             :         else
     823             :         {
     824           0 :             q = bones[t.bone].base;
     825             :         }
     826           0 :         matrix4x3 m;
     827           0 :         m.mul(q, t.matrix);
     828           0 :         m.d.mul(p->model->locationsize().w * sizescale);
     829           0 :         l.matrix = m;
     830             :     }
     831           0 : }
     832             : 
     833          22 : void skelmodel::skeleton::cleanup(bool full)
     834             : {
     835          22 :     for(skelcacheentry &sc : skelcache)
     836             :     {
     837           0 :         for(int j = 0; j < maxanimparts; ++j)
     838             :         {
     839           0 :             sc.as[j].cur.fr1 = -1; //skelcache animstate @ j's cur AnimPos fr1 (frame1?)
     840             :         }
     841           0 :         delete[] sc.bdata;
     842           0 :         sc.bdata = nullptr;
     843             :     }
     844          22 :     skelcache.clear();
     845          22 :     blendoffsets.clear();
     846          22 :     if(full)
     847             :     {
     848          11 :         owner->cleanup();
     849             :     }
     850          22 : }
     851             : 
     852           0 : const skelmodel::skelcacheentry &skelmodel::skeleton::checkskelcache(const vec &pos, float scale,  const AnimState *as, float pitch, const vec &axis, const vec &forward, const ragdolldata * const rdata)
     853             : {
     854           0 :     const int numanimparts = as->owner->numanimparts;
     855           0 :     const std::vector<uchar> &partmask = (reinterpret_cast<const skelpart *>(as->owner))->partmask;
     856           0 :     skelcacheentry *sc = nullptr;
     857           0 :     bool match = false;
     858           0 :     for(skelcacheentry &c : skelcache)
     859             :     {
     860           0 :         for(int j = 0; j < numanimparts; ++j)
     861             :         {
     862           0 :             if(c.as[j]!=as[j])
     863             :             {
     864           0 :                 goto mismatch;
     865             :             }
     866             :         }
     867           0 :         if(c.pitch != pitch || *c.partmask != partmask || c.ragdoll != rdata || (rdata && c.millis < rdata->lastmove))
     868             :         {
     869           0 :             goto mismatch;
     870             :         }
     871           0 :         match = true;
     872           0 :         sc = &c;
     873           0 :         break;
     874           0 :     mismatch:
     875           0 :         if(c.millis < lastmillis)
     876             :         {
     877           0 :             sc = &c;
     878           0 :             break;
     879             :         }
     880             :     }
     881           0 :     if(!sc)
     882             :     {
     883           0 :         skelcache.emplace_back(skelcacheentry());
     884           0 :         sc = &skelcache.back();
     885             :     }
     886           0 :     if(!match)
     887             :     {
     888           0 :         for(int i = 0; i < numanimparts; ++i)
     889             :         {
     890           0 :             sc->as[i] = as[i];
     891             :         }
     892           0 :         sc->pitch = pitch;
     893           0 :         sc->partmask = &partmask;
     894           0 :         sc->ragdoll = rdata;
     895           0 :         if(rdata)
     896             :         {
     897           0 :             genragdollbones(*rdata, *sc, pos, scale);
     898             :         }
     899             :         else
     900             :         {
     901           0 :             interpbones(as, pitch, axis, forward, numanimparts, partmask.data(), *sc);
     902             :         }
     903             :     }
     904           0 :     sc->millis = lastmillis;
     905           0 :     return *sc;
     906             : }
     907             : 
     908           0 : GLint skelmodel::skeleton::getblendoffset(const UniformLoc &u)
     909             : {
     910           0 :     auto itr = blendoffsets.find(Shader::lastshader->program);
     911           0 :     if(itr == blendoffsets.end())
     912             :     {
     913           0 :         itr = blendoffsets.insert( { Shader::lastshader->program, -1 } ).first;
     914           0 :         DEF_FORMAT_STRING(offsetname, "%s[%d]", u.name, 2*numgpubones);
     915           0 :         (*itr).second = glGetUniformLocation(Shader::lastshader->program, offsetname);
     916             :     }
     917           0 :     return (*itr).second;
     918             : }
     919             : 
     920           0 : void skelmodel::skeleton::setglslbones(UniformLoc &u, const skelcacheentry &sc, const skelcacheentry &bc, int count)
     921             : {
     922           0 :     if(u.version == bc.version && u.data == bc.bdata)
     923             :     {
     924           0 :         return;
     925             :     }
     926           0 :     glUniform4fv(u.loc, 2*numgpubones, sc.bdata[0].real.v);
     927           0 :     if(count > 0)
     928             :     {
     929           0 :         GLint offset = getblendoffset(u);
     930           0 :         if(offset >= 0)
     931             :         {
     932           0 :             glUniform4fv(offset, 2*count, bc.bdata[0].real.v);
     933             :         }
     934             :     }
     935           0 :     u.version = bc.version;
     936           0 :     u.data = bc.bdata;
     937             : }
     938             : 
     939           0 : void skelmodel::skeleton::setgpubones(const skelcacheentry &sc, const blendcacheentry *bc, int count)
     940             : {
     941           0 :     if(!Shader::lastshader)
     942             :     {
     943           0 :         return;
     944             :     }
     945           0 :     if(Shader::lastshader->uniformlocs.size() < 1)
     946             :     {
     947           0 :         return;
     948             :     }
     949           0 :     UniformLoc &u = Shader::lastshader->uniformlocs[0];
     950           0 :     setglslbones(u, sc, bc ? *bc : sc, count);
     951             : }
     952             : 
     953           0 : bool skelmodel::skeleton::shouldcleanup() const
     954             : {
     955           0 :     return numframes && skelcache.empty();
     956             : }
     957             : 
     958           0 : bool skelmodel::skeleton::setbonepitch(size_t index, float scale, float offset, float min, float max)
     959             : {
     960           0 :     if(index > numbones)
     961             :     {
     962           0 :         return false;
     963             :     }
     964           0 :     boneinfo &b = bones[index];
     965           0 :     b.pitchscale = scale;
     966           0 :     b.pitchoffset = offset;
     967           0 :     b.pitchmin = min;
     968           0 :     b.pitchmax = max;
     969           0 :     return true;
     970             : }
     971             : 
     972           7 : std::optional<dualquat> skelmodel::skeleton::getbonebase(size_t index) const
     973             : {
     974           7 :     if(index > numbones)
     975             :     {
     976           0 :         return std::nullopt;
     977             :     }
     978           7 :     return bones[index].base;
     979             : }
     980             : 
     981           1 : bool skelmodel::skeleton::setbonebases(const std::vector<dualquat> &bases)
     982             : {
     983           1 :     if(bases.size() != numbones)
     984             :     {
     985           0 :         return false;
     986             :     }
     987           5 :     for(size_t i = 0; i < numbones; ++i)
     988             :     {
     989           4 :         bones[i].base = bases[i];
     990             :     }
     991           1 :     return true;
     992             : }
     993             : 
     994           4 : bool skelmodel::skeleton::setbonename(size_t index, std::string_view name)
     995             : {
     996           4 :     if(index > numbones)
     997             :     {
     998           0 :         return false;
     999             :     }
    1000           4 :     boneinfo &b = bones[index];
    1001           4 :     if(!b.name.size())
    1002             :     {
    1003           4 :         b.name = name;
    1004           4 :         return true;
    1005             :     }
    1006           0 :     return false;
    1007             : }
    1008             : 
    1009           4 : bool skelmodel::skeleton::setboneparent(size_t index, size_t parent)
    1010             : {
    1011           4 :     if(index > numbones || parent > numbones)
    1012             :     {
    1013           1 :         return false;
    1014             :     }
    1015           3 :     boneinfo &b = bones[index];
    1016           3 :     b.parent = parent;
    1017           3 :     return true;
    1018             : }
    1019             : 
    1020           1 : void skelmodel::skeleton::createbones(size_t num)
    1021             : {
    1022           1 :     numbones = num;
    1023           5 :     bones = new boneinfo[numbones];
    1024           1 : }
    1025             : 
    1026           0 : ragdollskel *skelmodel::skeleton::trycreateragdoll()
    1027             : {
    1028           0 :     if(!ragdoll)
    1029             :     {
    1030           0 :         ragdoll = new ragdollskel;
    1031             :     }
    1032           0 :     return ragdoll;
    1033             : }
    1034             : 
    1035           2 : skelmodel::skelmeshgroup::~skelmeshgroup()
    1036             : {
    1037           2 :     if(skel)
    1038             :     {
    1039           2 :         delete skel;
    1040           2 :         skel = nullptr;
    1041             :     }
    1042           2 :     if(ebuf)
    1043             :     {
    1044           0 :         glDeleteBuffers(1, &ebuf);
    1045             :     }
    1046          34 :     for(size_t i = 0; i < maxblendcache; ++i)
    1047             :     {
    1048          32 :         delete[] blendcache[i].bdata;
    1049             :     }
    1050          34 :     for(size_t i = 0; i < maxvbocache; ++i)
    1051             :     {
    1052          32 :         if(vbocache[i].vbuf)
    1053             :         {
    1054           0 :             glDeleteBuffers(1, &vbocache[i].vbuf);
    1055             :         }
    1056             :     }
    1057           2 :     delete[] vdata;
    1058           2 : }
    1059             : 
    1060           0 : void skelmodel::skelmeshgroup::genvbo(vbocacheentry &vc)
    1061             : {
    1062           0 :     if(!vc.vbuf)
    1063             :     {
    1064           0 :         glGenBuffers(1, &vc.vbuf);
    1065             :     }
    1066           0 :     if(ebuf)
    1067             :     {
    1068           0 :         return;
    1069             :     }
    1070             : 
    1071           0 :     std::vector<GLuint> idxs;
    1072             : 
    1073           0 :     vlen = 0;
    1074           0 :     vblends = 0;
    1075             : 
    1076           0 :     if(skel->numframes)
    1077             :     {
    1078           0 :         vweights = 4;
    1079           0 :         int availbones = skel->availgpubones() - skel->numgpubones;
    1080           0 :         while(vweights > 1 && availbones >= numblends[vweights-1])
    1081             :         {
    1082           0 :             availbones -= numblends[--vweights];
    1083             :         }
    1084           0 :         for(blendcombo &c : blendcombos)
    1085             :         {
    1086           0 :             c.interpindex = static_cast<int>(c.size()) > vweights ? skel->numgpubones + vblends++ : -1;
    1087             :         }
    1088             :     }
    1089             :     else
    1090             :     {
    1091           0 :         vweights = 0;
    1092           0 :         for(blendcombo &i : blendcombos)
    1093             :         {
    1094           0 :             i.interpindex = -1;
    1095             :         }
    1096             :     }
    1097             : 
    1098           0 :     gle::bindvbo(vc.vbuf);
    1099           0 :     auto rendermeshes = getrendermeshes();
    1100           0 :     if(skel->numframes)
    1101             :     {
    1102           0 :         vertsize = sizeof(vvertgw);//silent parameter to genvbo()
    1103           0 :         std::vector<vvertgw> vvertgws;
    1104           0 :         for(const auto &i : rendermeshes)
    1105             :         {
    1106           0 :             vlen += static_cast<skelmesh *>(*i)->genvbo(blendcombos, idxs, vlen, vvertgws);
    1107             :         }
    1108           0 :         glBufferData(GL_ARRAY_BUFFER, vvertgws.size()*sizeof(vvertgw), vvertgws.data(), GL_STATIC_DRAW);
    1109           0 :     }
    1110             :     else
    1111             :     {
    1112           0 :         int numverts = 0,
    1113           0 :             htlen = 128;
    1114           0 :         for(auto i : rendermeshes)
    1115             :         {
    1116           0 :             numverts += static_cast<skelmesh *>(*i)->vertcount();
    1117             :         }
    1118           0 :         while(htlen < numverts)
    1119             :         {
    1120           0 :             htlen *= 2;
    1121             :         }
    1122           0 :         if(numverts*4 > htlen*3)
    1123             :         {
    1124           0 :             htlen *= 2;
    1125             :         }
    1126           0 :         int *htdata = new int[htlen];
    1127           0 :         std::memset(htdata, -1, htlen*sizeof(int));
    1128           0 :         vertsize = sizeof(vvertg); //silent parameter to genvbo()
    1129           0 :         std::vector<vvertg> vvertgs;
    1130           0 :         for(const auto &i : rendermeshes)
    1131             :         {
    1132           0 :             vlen += static_cast<skelmesh *>(*i)->genvbo(idxs, vlen, vvertgs, htdata, htlen);
    1133             :         }
    1134           0 :         glBufferData(GL_ARRAY_BUFFER, vvertgs.size()*sizeof(vvertg), vvertgs.data(), GL_STATIC_DRAW);
    1135           0 :         delete[] htdata;
    1136           0 :     }
    1137           0 :     gle::clearvbo();
    1138             : 
    1139           0 :     glGenBuffers(1, &ebuf);
    1140           0 :     gle::bindebo(ebuf);
    1141           0 :     glBufferData(GL_ELEMENT_ARRAY_BUFFER, idxs.size()*sizeof(GLuint), idxs.data(), GL_STATIC_DRAW);
    1142           0 :     gle::clearebo();
    1143           0 : }
    1144             : 
    1145           0 : void skelmodel::skelmeshgroup::render(const AnimState *as, float pitch, const vec &axis, const vec &forward, dynent *d, part *p)
    1146             : {
    1147           0 :     if(skel->shouldcleanup())
    1148             :     {
    1149           0 :         skel->cleanup();
    1150           0 :         disablevbo();
    1151             :     }
    1152             : 
    1153           0 :     if(!skel->numframes)
    1154             :     {
    1155           0 :         if(!(as->cur.anim & Anim_NoRender))
    1156             :         {
    1157           0 :             if(!vbocache[0].vbuf)
    1158             :             {
    1159           0 :                 genvbo(vbocache[0]);
    1160             :             }
    1161           0 :             bindvbo(as, p, vbocache[0]);
    1162           0 :             LOOP_RENDER_MESHES(skelmesh, m,
    1163             :             {
    1164             :                 p->skins[i].bind(m, as, true, vweights);
    1165             :                 m.render();
    1166             :             });
    1167             :         }
    1168           0 :         skel->calctags(p);
    1169           0 :         return;
    1170             :     }
    1171             : 
    1172           0 :     const vec ploc = vec(p->model->locationsize().x, p->model->locationsize().y, p->model->locationsize().z);
    1173             : 
    1174           0 :     const skelcacheentry &sc = skel->checkskelcache(ploc, p->model->locationsize().w, as, pitch, axis, forward, !d || !d->ragdoll || d->ragdoll->skel != skel->ragdoll || d->ragdoll->millis == lastmillis ? nullptr : d->ragdoll);
    1175           0 :     if(!(as->cur.anim & Anim_NoRender))
    1176             :     {
    1177           0 :         int owner = &sc-&skel->skelcache[0];
    1178           0 :         vbocacheentry &vc = vbocache[0];
    1179           0 :         vc.millis = lastmillis;
    1180           0 :         if(!vc.vbuf)
    1181             :         {
    1182           0 :             genvbo(vc);
    1183             :         }
    1184           0 :         blendcacheentry *bc = nullptr;
    1185           0 :         if(vblends)
    1186             :         {
    1187           0 :             bc = &checkblendcache(sc, owner);
    1188           0 :             bc->millis = lastmillis;
    1189           0 :             if(bc->owner!=owner)
    1190             :             {
    1191           0 :                 bc->owner = owner;
    1192           0 :                 *reinterpret_cast<animcacheentry *>(bc) = sc;
    1193           0 :                 blendbones(sc, *bc);
    1194             :             }
    1195             :         }
    1196             : 
    1197           0 :         bindvbo(as, p, vc, &sc);
    1198             : 
    1199           0 :         LOOP_RENDER_MESHES(skelmesh, m,
    1200             :         {
    1201             :             p->skins[i].bind(m, as, true, vweights);
    1202             :             skel->setgpubones(sc, bc, vblends);
    1203             :             m.render();
    1204             :         });
    1205             :     }
    1206             : 
    1207           0 :     skel->calctags(p, &sc);
    1208             : 
    1209           0 :     if(as->cur.anim & Anim_Ragdoll && skel->ragdoll && !d->ragdoll)
    1210             :     {
    1211           0 :         d->ragdoll = new ragdolldata(skel->ragdoll, p->model->locationsize().w);
    1212           0 :         skel->initragdoll(*d->ragdoll, sc, p->model->locationsize().w);
    1213           0 :         d->ragdoll->init(d);
    1214             :     }
    1215             : }
    1216             : 
    1217           0 : void skelmodel::skelmeshgroup::bindbones(const vvertgw *vverts)
    1218             : {
    1219           0 :     meshgroup::bindbones(vverts->weights, vverts->bones, vertsize);
    1220           0 : }
    1221             : 
    1222             : //blendcombo
    1223             : 
    1224         478 : skelmodel::blendcombo::blendcombo() : uses(1)
    1225             : {
    1226         478 :     bonedata.fill({0,0,0});
    1227         478 : }
    1228             : 
    1229         499 : bool skelmodel::blendcombo::operator==(const blendcombo &c) const
    1230             : {
    1231        2255 :     for(size_t i = 0; i < bonedata.size(); ++i)
    1232             :     {
    1233        3572 :         if((bonedata[i].bone != c.bonedata[i].bone) ||
    1234        1756 :            (bonedata[i].weight != c.bonedata[i].weight))
    1235             :         {
    1236          60 :             return false;
    1237             :         }
    1238             :     }
    1239         439 :     return true;
    1240             : }
    1241             : 
    1242         533 : size_t skelmodel::blendcombo::size() const
    1243             : {
    1244         533 :     size_t i = 1;
    1245         539 :     while(i < bonedata.size() && bonedata[i].weight)
    1246             :     {
    1247           6 :         i++;
    1248             :     }
    1249         533 :     return i;
    1250             : }
    1251             : 
    1252           6 : bool skelmodel::blendcombo::sortcmp(const blendcombo &x, const blendcombo &y)
    1253             : {
    1254          18 :     for(size_t i = 0; i < x.bonedata.size(); ++i)
    1255             :     {
    1256          17 :         if(x.bonedata[i].weight)
    1257             :         {
    1258          13 :             if(!y.bonedata[i].weight)
    1259             :             {
    1260           1 :                 return true;
    1261             :             }
    1262             :         }
    1263           4 :         else if(y.bonedata[i].weight)
    1264             :         {
    1265           1 :             return false;
    1266             :         }
    1267             :         else
    1268             :         {
    1269           3 :             break;
    1270             :         }
    1271             :     }
    1272           4 :     return false;
    1273             : }
    1274             : 
    1275         504 : int skelmodel::blendcombo::addweight(int sorted, float weight, int bone)
    1276             : {
    1277         504 :     if(weight <= 1e-3f) //do not add trivially small weights
    1278             :     {
    1279          60 :         return sorted;
    1280             :     }
    1281         450 :     for(int k = 0; k < sorted; ++k)
    1282             :     {
    1283           9 :         if(weight > bonedata[k].weight)
    1284             :         {
    1285             :             //push weights in bonedata to make room for new larger weights
    1286          10 :             for(int l = std::min(sorted-1, 2); l >= k; l--)
    1287             :             {
    1288           7 :                 bonedata[l+1].weight = bonedata[l].weight;
    1289           7 :                 bonedata[l+1].bone = bonedata[l].bone;
    1290             :             }
    1291           3 :             bonedata[k].weight = weight;
    1292           3 :             bonedata[k].bone = bone;
    1293           3 :             return sorted < static_cast<int>(bonedata.size()) ? sorted+1 : sorted;
    1294             :         }
    1295             :     }
    1296         441 :     if(sorted >= static_cast<int>(bonedata.size()))
    1297             :     {
    1298           1 :         return sorted;
    1299             :     }
    1300         440 :     bonedata[sorted].weight = weight;
    1301         440 :     bonedata[sorted].bone = bone;
    1302         440 :     return sorted+1;
    1303             : }
    1304             : 
    1305         443 : void skelmodel::blendcombo::finalize(int sorted)
    1306             : {
    1307         443 :     if(sorted <= 0)
    1308             :     {
    1309           1 :         return;
    1310             :     }
    1311         442 :     float total = 0;
    1312         894 :     for(int i = 0; i < sorted; ++i)
    1313             :     {
    1314         452 :         total += bonedata[i].weight;
    1315             :     }
    1316         442 :     total = 1.0f/total;
    1317         894 :     for(int i = 0; i < sorted; ++i)
    1318             :     {
    1319         452 :         bonedata[i].weight *= total;
    1320             :     }
    1321             : }
    1322             : 
    1323           7 : void skelmodel::blendcombo::serialize(skelmodel::vvertgw &v) const
    1324             : {
    1325           7 :     if(interpindex >= 0)
    1326             :     {
    1327           2 :         v.weights[0] = 255;
    1328           8 :         for(int k = 0; k < 3; ++k)
    1329             :         {
    1330           6 :             v.weights[k+1] = 0;
    1331             :         }
    1332           2 :         v.bones[0] = 2*interpindex;
    1333           8 :         for(int k = 0; k < 3; ++k)
    1334             :         {
    1335           6 :             v.bones[k+1] = v.bones[0];
    1336             :         }
    1337             :     }
    1338             :     else
    1339             :     {
    1340           5 :         int total = 0;
    1341          25 :         for(size_t k = 0; k < bonedata.size(); ++k)
    1342             :         {
    1343          20 :             v.weights[k] = static_cast<uchar>(0.5f + bonedata[k].weight*255);
    1344          20 :             total += (v.weights[k]);
    1345             :         }
    1346         113 :         while(total > 255)
    1347             :         {
    1348         540 :             for(size_t k = 0; k < 4; ++k)
    1349             :             {
    1350         432 :                 if(v.weights[k] > 0 && total > 255)
    1351             :                 {
    1352         412 :                     v.weights[k]--;
    1353         412 :                     total--;
    1354             :                 }
    1355             :             }
    1356             :         }
    1357         113 :         while(total < 255)
    1358             :         {
    1359         540 :             for(size_t k = 0; k < 4; ++k)
    1360             :             {
    1361         432 :                 if(v.weights[k] < 255 && total < 255)
    1362             :                 {
    1363         429 :                     v.weights[k]++;
    1364         429 :                     total++;
    1365             :                 }
    1366             :             }
    1367             :         }
    1368          25 :         for(size_t k = 0; k < bonedata.size(); ++k)
    1369             :         {
    1370          20 :             v.bones[k] = 2*bonedata[k].interpbone;
    1371             :         }
    1372             :     }
    1373           7 : }
    1374             : 
    1375           2 : dualquat skelmodel::blendcombo::blendbones(const dualquat *bdata) const
    1376             : {
    1377           2 :     dualquat d = bdata[bonedata[0].interpbone];
    1378           2 :     d.mul(bonedata[0].weight);
    1379           2 :     d.accumulate(bdata[bonedata[1].interpbone], bonedata[1].weight);
    1380           2 :     if(bonedata[2].weight)
    1381             :     {
    1382           2 :         d.accumulate(bdata[bonedata[2].interpbone], bonedata[2].weight);
    1383           2 :         if(bonedata[3].weight)
    1384             :         {
    1385           1 :             d.accumulate(bdata[bonedata[3].interpbone], bonedata[3].weight);
    1386             :         }
    1387             :     }
    1388           2 :     return d;
    1389             : }
    1390             : 
    1391          22 : void skelmodel::blendcombo::setinterpbones(int val, size_t index)
    1392             : {
    1393          22 :     bonedata[index].interpbone = val;
    1394          22 : }
    1395             : 
    1396          30 : int skelmodel::blendcombo::getbone(size_t index)
    1397             : {
    1398          30 :     return bonedata[index].bone;
    1399             : }
    1400             : 
    1401             : template<class T>
    1402           0 : T &searchcache(size_t cachesize, T *cache, const skelmodel::skelcacheentry &sc, int owner)
    1403             : {
    1404           0 :     for(size_t i = 0; i < cachesize; ++i)
    1405             :     {
    1406           0 :         T &c = cache[i];
    1407           0 :         if(c.owner==owner)
    1408             :         {
    1409           0 :              if(c==sc)
    1410             :              {
    1411           0 :                  return c;
    1412             :              }
    1413             :              else
    1414             :              {
    1415           0 :                  c.owner = -1;
    1416             :              }
    1417           0 :              break;
    1418             :         }
    1419             :     }
    1420           0 :     for(size_t i = 0; i < cachesize-1; ++i)
    1421             :     {
    1422           0 :         T &c = cache[i];
    1423           0 :         if(c.owner < 0 || c.millis < lastmillis)
    1424             :         {
    1425           0 :             return c;
    1426             :         }
    1427             :     }
    1428           0 :     return cache[cachesize-1];
    1429             : }
    1430             : 
    1431           0 : skelmodel::blendcacheentry &skelmodel::skelmeshgroup::checkblendcache(const skelcacheentry &sc, int owner)
    1432             : {
    1433           0 :     return searchcache<blendcacheentry>(maxblendcache, blendcache.data(), sc, owner);
    1434             : }
    1435             : 
    1436             : //skelmesh
    1437             : 
    1438           0 : skelmodel::skelmesh::skelmesh() : tris(nullptr), numtris(0), verts(nullptr), numverts(0), maxweights(0)
    1439             : {
    1440           0 : }
    1441             : 
    1442           4 : skelmodel::skelmesh::skelmesh(std::string_view name, vert *verts, uint numverts, tri *tris, uint numtris, meshgroup *m) : Mesh(name, m),
    1443           4 :     tris(tris),
    1444           4 :     numtris(numtris),
    1445           4 :     verts(verts),
    1446           4 :     numverts(numverts),
    1447           4 :     maxweights(0)
    1448             : {
    1449           4 : }
    1450             : 
    1451           3 : skelmodel::skelmesh::~skelmesh()
    1452             : {
    1453           3 :     delete[] verts;
    1454           3 :     delete[] tris;
    1455           3 : }
    1456             : 
    1457         438 : int skelmodel::skelmesh::addblendcombo(const blendcombo &c)
    1458             : {
    1459         438 :     maxweights = std::max(maxweights, static_cast<int>(c.size()));
    1460         438 :     return (reinterpret_cast<skelmeshgroup *>(group))->addblendcombo(c);
    1461             : }
    1462             : 
    1463           0 : void skelmodel::skelmesh::smoothnorms(float limit, bool areaweight)
    1464             : {
    1465           0 :     Mesh::smoothnorms<skelmodel>(verts, numverts, tris, numtris, limit, areaweight);
    1466           0 : }
    1467             : 
    1468           3 : void skelmodel::skelmesh::buildnorms(bool areaweight)
    1469             : {
    1470           3 :     Mesh::buildnorms<skelmodel>(verts, numverts, tris, numtris, areaweight);
    1471           3 : }
    1472             : 
    1473           1 : void skelmodel::skelmesh::calctangents(bool areaweight)
    1474             : {
    1475           1 :     Mesh::calctangents<skelmodel, skelmodel::vert>(verts, verts, numverts, tris, numtris, areaweight);
    1476           1 : }
    1477             : 
    1478           2 : void skelmodel::skelmesh::calcbb(vec &bbmin, vec &bbmax, const matrix4x3 &m) const
    1479             : {
    1480          10 :     for(int j = 0; j < numverts; ++j)
    1481             :     {
    1482           8 :         vec v = m.transform(verts[j].pos);
    1483           8 :         bbmin.min(v);
    1484           8 :         bbmax.max(v);
    1485             :     }
    1486           2 : }
    1487             : 
    1488           0 : void skelmodel::skelmesh::genBIH(BIH::mesh &m) const
    1489             : {
    1490           0 :     m.setmesh(reinterpret_cast<const BIH::mesh::tri *>(tris), numtris,
    1491           0 :               reinterpret_cast<const uchar *>(&verts->pos), sizeof(vert),
    1492           0 :               reinterpret_cast<const uchar *>(&verts->tc), sizeof(vert));
    1493           0 : }
    1494             : 
    1495           0 : void skelmodel::skelmesh::genshadowmesh(std::vector<triangle> &out, const matrix4x3 &m) const
    1496             : {
    1497           0 :     for(int j = 0; j < numtris; ++j)
    1498             :     {
    1499           0 :         triangle t;
    1500           0 :         t.a = m.transform(verts[tris[j].vert[0]].pos);
    1501           0 :         t.b = m.transform(verts[tris[j].vert[1]].pos);
    1502           0 :         t.c = m.transform(verts[tris[j].vert[2]].pos);
    1503           0 :         out.push_back(t);
    1504             :     }
    1505           0 : }
    1506             : 
    1507           1 : void skelmodel::skelmesh::assignvert(vvertg &vv, const vert &v)
    1508             : {
    1509           1 :     vv.pos = vec4<half>(v.pos, 1);
    1510           1 :     vv.tc = v.tc;
    1511           1 :     vv.tangent = v.tangent;
    1512           1 : }
    1513             : 
    1514           1 : void skelmodel::skelmesh::assignvert(vvertgw &vv, const vert &v, const blendcombo &c)
    1515             : {
    1516           1 :     vv.pos = vec4<half>(v.pos, 1);
    1517           1 :     vv.tc = v.tc;
    1518           1 :     vv.tangent = v.tangent;
    1519           1 :     c.serialize(vv);
    1520           1 : }
    1521             : 
    1522           0 : int skelmodel::skelmesh::genvbo(const std::vector<blendcombo> &bcs, std::vector<GLuint> &idxs, int offset, std::vector<vvertgw> &vverts)
    1523             : {
    1524           0 :     voffset = offset;
    1525           0 :     eoffset = idxs.size();
    1526           0 :     for(int i = 0; i < numverts; ++i)
    1527             :     {
    1528           0 :         const vert &v = verts[i];
    1529           0 :         vverts.emplace_back(vvertgw());
    1530           0 :         assignvert(vverts.back(), v, bcs[v.blend]);
    1531             :     }
    1532           0 :     for(int i = 0; i < numtris; ++i)
    1533             :     {
    1534           0 :         for(int j = 0; j < 3; ++j)
    1535             :         {
    1536           0 :             idxs.push_back(voffset + tris[i].vert[j]);
    1537             :         }
    1538             :     }
    1539           0 :     elen = idxs.size()-eoffset;
    1540           0 :     minvert = voffset;
    1541           0 :     maxvert = voffset + numverts-1;
    1542           0 :     return numverts;
    1543             : }
    1544             : 
    1545           0 : int skelmodel::skelmesh::genvbo(std::vector<GLuint> &idxs, int offset, std::vector<vvertg> &vverts, int *htdata, int htlen)
    1546             : {
    1547           0 :     voffset = offset;
    1548           0 :     eoffset = idxs.size();
    1549           0 :     minvert = 0xFFFF;
    1550           0 :     for(int i = 0; i < numtris; ++i)
    1551             :     {
    1552           0 :         const tri &t = tris[i];
    1553           0 :         for(int j = 0; j < 3; ++j)
    1554             :         {
    1555           0 :             const uint index = t.vert[j];
    1556           0 :             const vert &v = verts[index];
    1557           0 :             vvertg vv;
    1558           0 :             assignvert(vv, v);
    1559             :             const auto hashfn = std::hash<vec>();
    1560           0 :             int htidx = hashfn(v.pos)&(htlen-1);
    1561           0 :             for(int k = 0; k < htlen; ++k)
    1562             :             {
    1563           0 :                 int &vidx = htdata[(htidx+k)&(htlen-1)];
    1564           0 :                 if(vidx < 0)
    1565             :                 {
    1566           0 :                     vidx = idxs.emplace_back(static_cast<GLuint>(vverts.size()));
    1567           0 :                     vverts.push_back(vv);
    1568           0 :                     break;
    1569             :                 }
    1570           0 :                 else if(!std::memcmp(&vverts[vidx], &vv, sizeof(vv)))
    1571             :                 {
    1572           0 :                     minvert = std::min(minvert, idxs.emplace_back(static_cast<GLuint>(vidx)));
    1573           0 :                     break;
    1574             :                 }
    1575             :             }
    1576             :         }
    1577             :     }
    1578           0 :     elen = idxs.size()-eoffset;
    1579           0 :     minvert = std::min(minvert, static_cast<GLuint>(voffset));
    1580           0 :     maxvert = std::max(minvert, static_cast<GLuint>(vverts.size()-1));
    1581           0 :     return vverts.size()-voffset;
    1582             : }
    1583             : 
    1584           0 : void skelmodel::skelmesh::setshader(Shader *s, bool usegpuskel, int vweights, int row)
    1585             : {
    1586           0 :     if(row)
    1587             :     {
    1588           0 :         s->setvariant(usegpuskel ? std::min(maxweights, vweights) : 0, row);
    1589             :     }
    1590           0 :     else if(usegpuskel)
    1591             :     {
    1592           0 :         s->setvariant(std::min(maxweights, vweights)-1, 0);
    1593             :     }
    1594             :     else
    1595             :     {
    1596           0 :         s->set();
    1597             :     }
    1598           0 : }
    1599             : 
    1600           0 : void skelmodel::skelmesh::render() const
    1601             : {
    1602           0 :     if(!Shader::lastshader)
    1603             :     {
    1604           0 :         return;
    1605             :     }
    1606           0 :     glDrawRangeElements(GL_TRIANGLES, minvert, maxvert, elen, GL_UNSIGNED_INT, &(static_cast<skelmeshgroup *>(group))->edata[eoffset]);
    1607           0 :     glde++;
    1608           0 :     xtravertsva += numverts;
    1609             : }
    1610             : 
    1611           1 : void skelmodel::skelmesh::remapverts(const std::vector<int> remap)
    1612             : {
    1613         439 :     for(int i = 0; i < numverts; ++i)
    1614             :     {
    1615         438 :         vert &v = verts[i];
    1616         438 :         v.blend = remap[v.blend];
    1617             :     }
    1618           1 : }
    1619             : 
    1620           4 : int skelmodel::skelmesh::vertcount() const
    1621             : {
    1622           4 :     return numverts;
    1623             : }
    1624             : 
    1625           4 : int skelmodel::skelmesh::tricount() const
    1626             : {
    1627           4 :     return numtris;
    1628             : }
    1629             : 
    1630           8 : const skelmodel::vert &skelmodel::skelmesh::getvert(size_t index) const
    1631             : {
    1632           8 :     return verts[index];
    1633             : }
    1634             : 
    1635             : // boneinfo
    1636             : 
    1637           4 : skelmodel::skeleton::boneinfo::boneinfo() :
    1638           4 :     name(""),
    1639           4 :     parent(-1),
    1640           4 :     children(-1),
    1641           4 :     next(-1),
    1642           4 :     group(INT_MAX),
    1643           4 :     scheduled(-1),
    1644           4 :     interpindex(-1),
    1645           4 :     interpparent(-1),
    1646           4 :     ragdollindex(-1),
    1647           4 :     correctindex(-1),
    1648           4 :     pitchscale(0),
    1649           4 :     pitchoffset(0),
    1650           4 :     pitchmin(0),
    1651           4 :     pitchmax(0)
    1652             : {
    1653           4 : }
    1654             : 
    1655             : // skelmeshgroup
    1656             : 
    1657           0 : std::optional<size_t> skelmodel::skelmeshgroup::findtag(std::string_view name)
    1658             : {
    1659           0 :     return skel->findtag(name);
    1660             : }
    1661             : 
    1662           0 : void *skelmodel::skelmeshgroup::animkey()
    1663             : {
    1664           0 :     return skel;
    1665             : }
    1666             : 
    1667           2 : int skelmodel::skelmeshgroup::totalframes() const
    1668             : {
    1669           2 :     return std::max(skel->numframes, size_t(1));
    1670             : }
    1671             : 
    1672           0 : void skelmodel::skelmeshgroup::bindvbo(const AnimState *as, const part *p, const vbocacheentry &vc, const skelcacheentry *sc)
    1673             : {
    1674           0 :     if(!skel->numframes)
    1675             :     {
    1676           0 :         bindvbo<vvertg>(as, p, vc);
    1677             :     }
    1678             :     else
    1679             :     {
    1680           0 :         bindvbo<vvertgw>(as, p, vc);
    1681             :     }
    1682           0 : }
    1683             : 
    1684           0 : void skelmodel::skelmeshgroup::concattagtransform(int i, const matrix4x3 &m, matrix4x3 &n) const
    1685             : {
    1686           0 :     skel->concattagtransform(i, m, n);
    1687           0 : }
    1688             : 
    1689         438 : int skelmodel::skelmeshgroup::addblendcombo(const blendcombo &c)
    1690             : {
    1691         497 :     for(uint i = 0; i < blendcombos.size(); i++)
    1692             :     {
    1693         495 :         if(blendcombos[i]==c)
    1694             :         {
    1695         436 :             blendcombos[i].uses += c.uses;
    1696         436 :             return i;
    1697             :         }
    1698             :     }
    1699           2 :     numblends[c.size()-1]++;
    1700           2 :     blendcombos.push_back(c);
    1701           2 :     blendcombo &a = blendcombos.back();
    1702           2 :     a.interpindex = blendcombos.size()-1;
    1703           2 :     return a.interpindex;
    1704             : }
    1705             : 
    1706           1 : void skelmodel::skelmeshgroup::sortblendcombos()
    1707             : {
    1708           1 :     std::sort(blendcombos.begin(), blendcombos.end(), blendcombo::sortcmp);
    1709           1 :     std::vector<int> remap(blendcombos.size(), 0);
    1710           3 :     for(uint i = 0; i < blendcombos.size(); i++)
    1711             :     {
    1712           2 :         remap[blendcombos[i].interpindex] = i;
    1713             :     }
    1714           1 :     auto rendermeshes = getrendermeshes();
    1715           2 :     for(auto i : rendermeshes)
    1716             :     {
    1717           1 :         skelmesh *s = static_cast<skelmesh *>(*i);
    1718           1 :         s->remapverts(remap);
    1719             :     }
    1720           1 : }
    1721             : 
    1722           0 : void skelmodel::skelmeshgroup::blendbones(const skelcacheentry &sc, blendcacheentry &bc)
    1723             : {
    1724           0 :     bc.nextversion();
    1725           0 :     if(!bc.bdata)
    1726             :     {
    1727           0 :         bc.bdata = new dualquat[vblends];
    1728             :     }
    1729           0 :     dualquat *dst = bc.bdata - skel->numgpubones;
    1730           0 :     bool normalize = vweights<=1;
    1731           0 :     for(const blendcombo &c : blendcombos)
    1732             :     {
    1733           0 :         if(c.interpindex<0)
    1734             :         {
    1735           0 :             break;
    1736             :         }
    1737           0 :         dualquat &d = dst[c.interpindex];
    1738           0 :         d = c.blendbones(sc.bdata);
    1739           0 :         if(normalize)
    1740             :         {
    1741           0 :             d.normalize();
    1742             :         }
    1743             :     }
    1744           0 : }
    1745             : 
    1746          11 : void skelmodel::skelmeshgroup::cleanup()
    1747             : {
    1748         187 :     for(size_t i = 0; i < maxblendcache; ++i)
    1749             :     {
    1750         176 :         blendcacheentry &c = blendcache[i];
    1751         176 :         delete[] c.bdata;
    1752         176 :         c.bdata = nullptr;
    1753         176 :         c.owner = -1;
    1754             :     }
    1755         187 :     for(size_t i = 0; i < maxvbocache; ++i)
    1756             :     {
    1757         176 :         vbocacheentry &c = vbocache[i];
    1758         176 :         if(c.vbuf)
    1759             :         {
    1760           0 :             glDeleteBuffers(1, &c.vbuf);
    1761           0 :             c.vbuf = 0;
    1762             :         }
    1763         176 :         c.owner = -1;
    1764             :     }
    1765          11 :     if(ebuf)
    1766             :     {
    1767           0 :         glDeleteBuffers(1, &ebuf);
    1768           0 :         ebuf = 0;
    1769             :     }
    1770          11 :     if(skel)
    1771             :     {
    1772          11 :         skel->cleanup(false);
    1773             :     }
    1774          11 : }
    1775             : 
    1776           0 : void skelmodel::skelmeshgroup::preload()
    1777             : {
    1778           0 :     if(skel->shouldcleanup())
    1779             :     {
    1780           0 :         skel->cleanup();
    1781             :     }
    1782           0 :     if(!vbocache[0].vbuf)
    1783             :     {
    1784           0 :         genvbo(vbocache[0]);
    1785             :     }
    1786           0 : }
    1787             : 
    1788             : // skelpart
    1789             : 
    1790          19 : skelmodel::skelpart::skelpart(animmodel *model, int index) : part(model, index)
    1791             : {
    1792          19 : }
    1793             : 
    1794          38 : skelmodel::skelpart::~skelpart()
    1795             : {
    1796          38 : }
    1797             : 
    1798             : /**
    1799             :  * @brief Manages caching of part masking data.
    1800             :  *
    1801             :  * Attempts to match the passed vector of uchar with one in the internal static vector.
    1802             :  * If a matching entry is found, empties the passed vector returns the entry in the cache.
    1803             :  *
    1804             :  * @param o a vector of uchar to delete or insert into the internal cache
    1805             :  * @return the passed value o, or the equivalent entry in the internal cache
    1806             :  */
    1807          11 : std::vector<uchar> &skelmodel::skelpart::sharepartmask(std::vector<uchar> &o)
    1808             : {
    1809          11 :     static std::vector<std::vector<uchar>> partmasks;
    1810          12 :     for(std::vector<uchar> &p : partmasks)
    1811             :     {
    1812          10 :         if(p == o)
    1813             :         {
    1814           9 :             o.clear();
    1815           9 :             return p;
    1816             :         }
    1817             :     }
    1818           2 :     partmasks.push_back(o);
    1819           2 :     o.clear();
    1820           2 :     return partmasks.back();
    1821             : }
    1822             : 
    1823          17 : std::vector<uchar> skelmodel::skelpart::newpartmask()
    1824             : {
    1825          34 :     return std::vector<uchar>((static_cast<skelmeshgroup *>(meshes))->skel->numbones, 0);
    1826             : 
    1827             : }
    1828             : 
    1829          17 : void skelmodel::skelpart::initanimparts()
    1830             : {
    1831          17 :     buildingpartmask = newpartmask();
    1832          17 : }
    1833             : 
    1834           1 : bool skelmodel::skelpart::addanimpart(const std::vector<uint> &bonemask)
    1835             : {
    1836           1 :     if(buildingpartmask.empty() || numanimparts>=maxanimparts)
    1837             :     {
    1838           0 :         return false;
    1839             :     }
    1840           1 :     (static_cast<skelmeshgroup *>(meshes))->skel->applybonemask(bonemask, buildingpartmask, numanimparts);
    1841           1 :     numanimparts++;
    1842           1 :     return true;
    1843             : }
    1844             : 
    1845          11 : void skelmodel::skelpart::endanimparts()
    1846             : {
    1847          11 :     if(buildingpartmask.size())
    1848             :     {
    1849          11 :         partmask = sharepartmask(buildingpartmask);
    1850          11 :         buildingpartmask.clear();
    1851             :     }
    1852             : 
    1853          11 :     (static_cast<skelmeshgroup *>(meshes))->skel->optimize();
    1854          11 : }
    1855             : 
    1856          11 : void skelmodel::skelpart::loaded()
    1857             : {
    1858          11 :     endanimparts();
    1859          11 :     part::loaded();
    1860          11 : }
    1861             : 
    1862             : //skelmodel
    1863             : 
    1864          20 : skelmodel::skelmodel(std::string name) : animmodel(name)
    1865             : {
    1866          20 : }
    1867             : 
    1868          19 : skelmodel::skelpart &skelmodel::addpart()
    1869             : {
    1870          19 :     skelpart *p = new skelpart(this, parts.size());
    1871          19 :     parts.push_back(p);
    1872          19 :     return *p;
    1873             : }
    1874             : 
    1875           3 : animmodel::meshgroup * skelmodel::loadmeshes(const std::string &name, float smooth)
    1876             : {
    1877           3 :     skelmeshgroup *group = newmeshes();
    1878           3 :     group->skel = new skeleton(group);
    1879           3 :     part &p = *parts.back();
    1880           3 :     if(!group->load(name, smooth, p))
    1881             :     {
    1882           2 :         delete group;
    1883           2 :         return nullptr;
    1884             :     }
    1885           1 :     return group;
    1886             : }
    1887             : 
    1888          19 : animmodel::meshgroup * skelmodel::sharemeshes(const std::string &name, float smooth)
    1889             : {
    1890          19 :     if(meshgroups.find(name) == meshgroups.end())
    1891             :     {
    1892           3 :         meshgroup *group = loadmeshes(name, smooth);
    1893           3 :         if(!group)
    1894             :         {
    1895           2 :             return nullptr;
    1896             :         }
    1897           1 :         meshgroups[group->groupname()] = group;
    1898             :     }
    1899          17 :     return meshgroups[name];
    1900             : }
    1901             : 
    1902             : //skelmodel overrides
    1903             : 
    1904           0 : int skelmodel::linktype(const animmodel *m, const part *p) const
    1905             : {
    1906           0 :     return type()==m->type() &&
    1907           0 :         (static_cast<skelmeshgroup *>(parts[0]->meshes))->skel == (static_cast<skelmeshgroup *>(p->meshes))->skel ?
    1908             :             Link_Reuse :
    1909           0 :             Link_Tag;
    1910             : }
    1911             : 
    1912           0 : bool skelmodel::skeletal() const
    1913             : {
    1914           0 :     return true;
    1915             : }
    1916             : 
    1917             : /*    ====    skeladjustment    ====    */
    1918             : /*======================================*/
    1919             : 
    1920           0 : void skeladjustment::adjust(dualquat &dq) const
    1921             : {
    1922           0 :     if(yaw)
    1923             :     {
    1924           0 :         dq.mulorient(quat(vec(0, 0, 1), yaw/RAD));
    1925             :     }
    1926           0 :     if(pitch)
    1927             :     {
    1928           0 :         dq.mulorient(quat(vec(0, -1, 0), pitch/RAD));
    1929             :     }
    1930           0 :     if(roll)
    1931             :     {
    1932           0 :         dq.mulorient(quat(vec(-1, 0, 0), roll/RAD));
    1933             :     }
    1934           0 :     if(!translate.iszero())
    1935             :     {
    1936           0 :         dq.translate(translate);
    1937             :     }
    1938           0 : }

Generated by: LCOV version 1.14