LCOV - code coverage report
Current view: top level - engine/model - animmodel.h (source / functions) Hit Total Coverage
Test: Libprimis Test Coverage Lines: 320 474 67.5 %
Date: 2024-11-22 05:07:59 Functions: 213 238 89.5 %

          Line data    Source code
       1             : #ifndef ANIMMODEL_H_
       2             : #define ANIMMODEL_H_
       3             : 
       4             : extern int fullbrightmodels, testtags, debugcolmesh;
       5             : 
       6             : const std::string modelpath = "media/model/";
       7             : 
       8             : /* animmodel: generic class for an animated model object, derived from the very
       9             :  * general model structure
      10             :  *
      11             :  * animmodel provides functionality to implement an animated model consisting of
      12             :  * multiple mesh groups which are treated as a single model
      13             :  *
      14             :  * animmodel is extended by skelmodel to allow multipart objects to be rigged by
      15             :  * a skeleton/bone type animation methodology
      16             :  */
      17             : class animmodel : public model
      18             : {
      19             :     public:
      20             : 
      21             :         struct AnimPos final
      22             :         {
      23             :             int anim, fr1, fr2;
      24             :             float t;
      25             : 
      26             :             void setframes(const animinfo &info);
      27             : 
      28             :             bool operator==(const AnimPos &a) const;
      29             :             bool operator!=(const AnimPos &a) const;
      30             :         };
      31             : 
      32             :         class part;
      33             : 
      34             :         struct AnimState final
      35             :         {
      36             :             const part *owner;
      37             :             AnimPos cur, prev;
      38             :             float interp;
      39             : 
      40             :             bool operator==(const AnimState &a) const;
      41             :             bool operator!=(const AnimState &a) const;
      42             :         };
      43             : 
      44             :         class Mesh;
      45             : 
      46             :         struct shaderparams
      47             :         {
      48             :             float spec, gloss, glow, glowdelta, glowpulse, fullbright, scrollu, scrollv, alphatest;
      49             :             vec color;
      50             : 
      51             : 
      52          55 :             bool operator==(const animmodel::shaderparams &y) const
      53             :             {
      54          55 :                 return spec == y.spec
      55          55 :                     && gloss == y.glow
      56           0 :                     && glow == y.glow
      57           0 :                     && glowdelta == y.glowdelta
      58           0 :                     && glowpulse == y.glowpulse
      59           0 :                     && fullbright == y.fullbright
      60           0 :                     && scrollu == y.scrollu
      61           0 :                     && scrollv == y.scrollv
      62           0 :                     && alphatest == y.alphatest
      63         110 :                     && color == y.color;
      64             :             }
      65          17 :             shaderparams() : spec(1.0f), gloss(1), glow(3.0f), glowdelta(0), glowpulse(0), fullbright(0), scrollu(0), scrollv(0), alphatest(0.9f), color(1, 1, 1) {}
      66             :         };
      67             : 
      68             :         //An object used to store texture data for a mesh, contained inside a `part` object.
      69             :         //A `meshgroup` object is paired with a `skin` object to provide texture & geometry
      70             :         //data for a mesh
      71             :         class skin final : public shaderparams
      72             :         {
      73             :             public:
      74             :                 Texture *tex, *decal;
      75             :                 const Texture *masks, *normalmap;
      76             :                 Shader *shader;
      77             :                 int cullface;
      78             : 
      79          17 :                 skin(const part *owner, Texture *tex, const Texture *masks) :
      80          17 :                     tex(notexture),
      81          17 :                     decal(nullptr),
      82          17 :                     masks(masks),
      83          17 :                     normalmap(nullptr),
      84          17 :                     shader(nullptr),
      85          17 :                     cullface(1),
      86          17 :                     owner(owner),
      87          17 :                     rsmshader(nullptr),
      88          17 :                     key(nullptr)
      89             :                 {
      90          17 :                 }
      91             : 
      92             :                 bool alphatested() const;
      93             :                 void setkey();
      94             :                 void cleanup();
      95             :                 void preloadBIH() const;
      96             :                 void preloadshader();
      97             :                 void bind(Mesh &b, const AnimState *as, bool usegpuskel = false, int vweights = 0);
      98             :                 static void invalidateshaderparams();
      99             : 
     100             :             private:
     101             :                 const part *owner;
     102             :                 Shader *rsmshader;
     103             :                 class ShaderParamsKey
     104             :                 {
     105             :                     public:
     106             :                         static std::unordered_map<shaderparams, ShaderParamsKey> keys;
     107             : 
     108          11 :                         ShaderParamsKey() : version(-1) {}
     109             : 
     110             :                         bool checkversion();
     111             : 
     112           0 :                         static void invalidate()
     113             :                         {
     114           0 :                             firstversion = lastversion;
     115           0 :                         }
     116             :                     private:
     117             :                         static int firstversion, lastversion;
     118             : 
     119             :                         int version;
     120             :                 };
     121             :                 ShaderParamsKey *key;
     122             : 
     123             :                 bool masked() const;
     124             :                 bool bumpmapped() const;
     125             :                 bool decaled() const;
     126             :                 void setshaderparams(Mesh &m, const AnimState *as, bool skinned = true);
     127             :                 Shader *loadshader();
     128             :                 void setshader(Mesh &m, const AnimState *as, bool usegpuskel, int vweights);
     129             : 
     130             :         };
     131             : 
     132             :         class meshgroup;
     133             : 
     134             :         //An object used to store a single geometry mesh inside a `meshgroup` object.
     135             :         //This object is to be extended to contain the actual geometry of a type of
     136             :         //model, such as `skelmesh` or `vertmesh`. In its base class form, the object
     137             :         //contains no geometry data.
     138             :         class Mesh
     139             :         {
     140             :             public:
     141             :                 std::string name;
     142             :                 bool cancollide, canrender, noclip;
     143             : 
     144           3 :                 virtual ~Mesh()
     145           3 :                 {
     146           3 :                 }
     147             : 
     148             :                 virtual void calcbb(vec &bbmin, vec &bbmax, const matrix4x3 &m) const = 0;
     149             : 
     150             :                 virtual void genBIH(BIH::mesh &m) const = 0;
     151             : 
     152             :                 void genBIH(const skin &s, std::vector<BIH::mesh> &bih, const matrix4x3 &t);
     153             : 
     154           0 :                 virtual void genshadowmesh(std::vector<triangle> &tris, const matrix4x3 &m) const
     155             :                 {
     156           0 :                 }
     157             : 
     158           0 :                 virtual void setshader(Shader *s, bool, int, int row = 0)
     159             :                 {
     160           0 :                     if(row)
     161             :                     {
     162           0 :                         s->setvariant(0, row);
     163             :                     }
     164             :                     else
     165             :                     {
     166           0 :                         s->set();
     167             :                     }
     168           0 :                 }
     169             : 
     170             :             protected:
     171             :                 meshgroup *group;
     172             : 
     173           0 :                 Mesh() : cancollide(true), canrender(true), noclip(false), group(nullptr)
     174             :                 {
     175           0 :                 }
     176             : 
     177           4 :                 Mesh(std::string_view name, meshgroup *m) :
     178           4 :                     name(name),
     179           4 :                     cancollide(true),
     180           4 :                     canrender(true),
     181           4 :                     noclip(false),
     182           4 :                     group(m)
     183             :                 {
     184           4 :                 }
     185             : 
     186             :                 template<class T>
     187           0 :                 static void smoothnorms(typename T::vert *verts, int numverts, const typename T::tri *tris, int numtris, float limit, bool areaweight)
     188             :                 {
     189           0 :                     if(!numverts)
     190             :                     {
     191           0 :                         return;
     192             :                     }
     193           0 :                     smoothdata *smooth = new smoothdata[numverts];
     194           0 :                     std::unordered_map<vec, int> share;
     195           0 :                     for(int i = 0; i < numverts; ++i)
     196             :                     {
     197           0 :                         const typename T::vert &v = verts[i];
     198           0 :                         const auto itr = share.find(v.pos);
     199           0 :                         if(itr == share.end())
     200             :                         {
     201           0 :                             share[v.pos] = i;
     202             :                         }
     203             :                         else
     204             :                         {
     205           0 :                             smooth[i].next = (*itr).second;
     206           0 :                             (*itr).second = i;
     207             :                         }
     208             :                     }
     209           0 :                     for(int i = 0; i < numtris; ++i)
     210             :                     {
     211           0 :                         const typename T::tri &t = tris[i];
     212           0 :                         const uint v1 = t.vert[0],
     213           0 :                                    v2 = t.vert[1],
     214           0 :                                    v3 = t.vert[2];
     215           0 :                         vec norm;
     216           0 :                         norm.cross(verts[v1].pos, verts[v2].pos, verts[v3].pos);
     217           0 :                         if(!areaweight)
     218             :                         {
     219           0 :                             norm.normalize();
     220             :                         }
     221           0 :                         smooth[v1].norm.add(norm);
     222           0 :                         smooth[v2].norm.add(norm);
     223           0 :                         smooth[v3].norm.add(norm);
     224             :                     }
     225           0 :                     for(int i = 0; i < numverts; ++i)
     226             :                     {
     227           0 :                         verts[i].norm = vec(0, 0, 0);
     228             :                     }
     229           0 :                     for(int i = 0; i < numverts; ++i)
     230             :                     {
     231           0 :                         const smoothdata &n = smooth[i];
     232           0 :                         verts[i].norm.add(n.norm);
     233           0 :                         if(n.next >= 0)
     234             :                         {
     235           0 :                             const float vlimit = limit*n.norm.magnitude();
     236           0 :                             for(int j = n.next; j >= 0;)
     237             :                             {
     238           0 :                                 const smoothdata &o = smooth[j];
     239           0 :                                 if(n.norm.dot(o.norm) >= vlimit*o.norm.magnitude())
     240             :                                 {
     241           0 :                                     verts[i].norm.add(o.norm);
     242           0 :                                     verts[j].norm.add(n.norm);
     243             :                                 }
     244           0 :                                 j = o.next;
     245             :                             }
     246             :                         }
     247             :                     }
     248           0 :                     for(int i = 0; i < numverts; ++i)
     249             :                     {
     250           0 :                         verts[i].norm.normalize();
     251             :                     }
     252           0 :                     delete[] smooth;
     253           0 :                 }
     254             : 
     255             :                 /**
     256             :                  * @brief Generates normal data for an array of vert objects.
     257             :                  *
     258             :                  * If there are no verts in the verts array, returns with no changes.
     259             :                  *
     260             :                  * Sets all norm fields in the verts array to the zero vector.
     261             :                  * Accesses three elements in verts using a set of indices saved in the tri object.
     262             :                  *
     263             :                  * Each triangle's normal is the cross product of the three elements' positions, normalized if areaweight == false.
     264             :                  * (If areaweight is true, since the cross product's magnitude encodes its area, we don't want to
     265             :                  * normalize because we want to weight normals by area)
     266             :                  * This normal is added to each of the three vertices accessed (individual vertices can have multiple tris).
     267             :                  *
     268             :                  * Then, all vertices are normalized, creating normalized normals using all triangles sharing each vertex.
     269             :                  *
     270             :                  * @param verts An array of vertices
     271             :                  * @param numverts The size of the vertex array
     272             :                  * @param tris An array of triangles, containing indices in verts
     273             :                  * @param numtris The size of the tris array
     274             :                  * @param areaweight If true, weights normals by area of associated triangle
     275             :                  */
     276             :                 template<class T>
     277           3 :                 static void buildnorms(typename T::vert *verts, int numverts, const typename T::tri *tris, int numtris, bool areaweight)
     278             :                 {
     279           3 :                     if(!numverts)
     280             :                     {
     281           0 :                         return;
     282             :                     }
     283         449 :                     for(int i = 0; i < numverts; ++i)
     284             :                     {
     285         446 :                         verts[i].norm = vec(0, 0, 0);
     286             :                     }
     287         469 :                     for(int i = 0; i < numtris; ++i)
     288             :                     {
     289         466 :                         const typename T::tri &t = tris[i];
     290         466 :                         typename T::vert &v1 = verts[t.vert[0]],
     291         466 :                                         &v2 = verts[t.vert[1]],
     292         466 :                                         &v3 = verts[t.vert[2]];
     293         466 :                         vec norm;
     294         466 :                         norm.cross(v1.pos, v2.pos, v3.pos);
     295         466 :                         if(!areaweight)
     296             :                         {
     297           2 :                             norm.normalize();
     298             :                         }
     299         466 :                         v1.norm.add(norm);
     300         466 :                         v2.norm.add(norm);
     301         466 :                         v3.norm.add(norm);
     302             :                     }
     303         449 :                     for(int i = 0; i < numverts; ++i)
     304             :                     {
     305         446 :                         verts[i].norm.normalize();
     306             :                     }
     307             :                 }
     308             : 
     309             :                 template<class T>
     310           0 :                 static void buildnorms(typename T::vert *verts, int numverts, const typename T::tri *tris, int numtris, bool areaweight, int numframes)
     311             :                 {
     312           0 :                     if(!numverts)
     313             :                     {
     314           0 :                         return;
     315             :                     }
     316           0 :                     for(int i = 0; i < numframes; ++i)
     317             :                     {
     318           0 :                         buildnorms<T>(&verts[i*numverts], numverts, tris, numtris, areaweight);
     319             :                     }
     320             :                 }
     321             : 
     322             :                 static void fixqtangent(quat &q, float bt);
     323             : 
     324             :                 template<class T, class TC>
     325           1 :                 void calctangents(typename T::vert *verts, TC *tcverts, int numverts, typename T::tri *tris, int numtris, bool areaweight)
     326             :                 {
     327         877 :                     vec *tangent = new vec[2*numverts],
     328           1 :                         *bitangent = tangent+numverts;
     329           1 :                     std::memset(static_cast<void *>(tangent), 0, 2*numverts*sizeof(vec));
     330             : 
     331         463 :                     for(int i = 0; i < numtris; ++i)
     332             :                     {
     333         462 :                         const typename T::tri &t = tris[i];
     334         462 :                         const vec &e0 = verts[t.vert[0]].pos;
     335         462 :                         vec e1 = vec(verts[t.vert[1]].pos).sub(e0),
     336         462 :                             e2 = vec(verts[t.vert[2]].pos).sub(e0);
     337             : 
     338         462 :                         const vec2 &tc0 = tcverts[t.vert[0]].tc,
     339         462 :                                    &tc1 = tcverts[t.vert[1]].tc,
     340         462 :                                    &tc2 = tcverts[t.vert[2]].tc;
     341         462 :                         float u1 = tc1.x - tc0.x,
     342         462 :                               v1 = tc1.y - tc0.y,
     343         462 :                               u2 = tc2.x - tc0.x,
     344         462 :                               v2 = tc2.y - tc0.y;
     345         462 :                         vec u(e2), v(e2);
     346         462 :                         u.mul(v1).sub(vec(e1).mul(v2));
     347         462 :                         v.mul(u1).sub(vec(e1).mul(u2));
     348             : 
     349         462 :                         if(vec().cross(e2, e1).dot(vec().cross(v, u)) >= 0)
     350             :                         {
     351          19 :                             u.neg();
     352          19 :                             v.neg();
     353             :                         }
     354             : 
     355         462 :                         if(!areaweight)
     356             :                         {
     357           0 :                             u.normalize();
     358           0 :                             v.normalize();
     359             :                         }
     360             : 
     361        1848 :                         for(int j = 0; j < 3; ++j)
     362             :                         {
     363        1386 :                             tangent[t.vert[j]].sub(u);
     364        1386 :                             bitangent[t.vert[j]].add(v);
     365             :                         }
     366             :                     }
     367         439 :                     for(int i = 0; i < numverts; ++i)
     368             :                     {
     369         438 :                         typename T::vert &v = verts[i];
     370         438 :                         const vec &t = tangent[i],
     371         438 :                                   &bt = bitangent[i];
     372         438 :                         matrix3 m;
     373         438 :                         m.c = v.norm;
     374         438 :                         (m.a = t).project(m.c).normalize();
     375         438 :                         m.b.cross(m.c, m.a);
     376         438 :                         quat q(m);
     377         438 :                         fixqtangent(q, m.b.dot(bt));
     378         438 :                         v.tangent = q;
     379             :                     }
     380           1 :                     delete[] tangent;
     381           1 :                 }
     382             : 
     383             :                 template<class T, class TC>
     384           0 :                 void calctangents(typename T::vert *verts, TC *tcverts, int numverts, typename T::tri *tris, int numtris, bool areaweight, int numframes)
     385             :                 {
     386           0 :                     for(int i = 0; i < numframes; ++i)
     387             :                     {
     388           0 :                         calctangents<T, TC>(&verts[i*numverts], tcverts, numverts, tris, numtris, areaweight);
     389             :                     }
     390           0 :                 }
     391             : 
     392             :             private:
     393             :                 struct smoothdata
     394             :                 {
     395             :                     vec norm;
     396             :                     int next;
     397             : 
     398           0 :                     smoothdata() : norm(0, 0, 0), next(-1) {}
     399             :                 };
     400             :         };
     401             : 
     402             :         //A group of one or more meshes, which are used by a `part` to represent its contained geometry.
     403             :         //A global (static field) map of meshgroups are kept to cache model geometry; the actual `part`'s
     404             :         //refered mesh group object is kept as a pointer to a member of the static meshgroup map.
     405             :         class meshgroup
     406             :         {
     407             :             public:
     408             :                 std::vector<Mesh *> meshes;
     409             : 
     410             :                 virtual ~meshgroup();
     411             : 
     412             :                 virtual void concattagtransform(int i, const matrix4x3 &m, matrix4x3 &n) const = 0;
     413             :                 virtual std::optional<size_t> findtag(std::string_view name) = 0;
     414             :                 virtual int totalframes() const = 0;
     415             :                 virtual void *animkey() = 0;
     416             :                 virtual void cleanup() = 0;
     417             :                 virtual void render(const AnimState *as, float pitch, const vec &axis, const vec &forward, dynent *d, part *p) = 0;
     418             :                 virtual void preload() = 0;
     419             : 
     420             :                 /**
     421             :                  * Returns a list of Mesh iterators corresponding to a given name
     422             :                  * The iterators may be invalidated by other method calls.
     423             :                  */
     424             :                 std::vector<std::vector<animmodel::Mesh *>::const_iterator> getmeshes(std::string_view meshname) const;
     425             :                 /**
     426             :                  * Returns a list of indices corresponding to locations in animmodel::part::skins.
     427             :                  * These indices are invalidated if animmodel::skins is modified after calling.
     428             :                  */
     429             :                 std::vector<size_t> getskins(std::string_view meshname) const;
     430             : 
     431             :                 void calcbb(vec &bbmin, vec &bbmax, const matrix4x3 &t) const;
     432             :                 void genBIH(const std::vector<skin> &skins, std::vector<BIH::mesh> &bih, const matrix4x3 &t) const;
     433             :                 void genshadowmesh(std::vector<triangle> &tris, const matrix4x3 &t) const;
     434             :                 bool hasframe(int i) const;
     435             :                 bool hasframes(int i, int n) const;
     436             :                 int clipframes(int i, int n) const;
     437             :                 const std::string &groupname() const;
     438             : 
     439             :                 /**
     440             :                  * @brief Returns a list of valid renderable meshes contained within this object.
     441             :                  *
     442             :                  * Returns a vector of std::vector<>::iterator objects which point to valid
     443             :                  * elements of the object's mesh list which can be rendered (the relevant flag
     444             :                  * field is true). Alternatively if the global debugcolmesh is enabled, all
     445             :                  * meshes will be returned.
     446             :                  *
     447             :                  * @return a vector of std::vector iterators pointing to renderable meshes
     448             :                  */
     449             :                 std::vector<std::vector<Mesh *>::const_iterator> getrendermeshes() const;
     450             :                 std::vector<std::vector<Mesh *>::iterator> getrendermeshes();
     451             : 
     452             :                 #define LOOP_RENDER_MESHES(type, name, body) do { \
     453             :                     for(uint i = 0; i < meshes.size(); i++) \
     454             :                     { \
     455             :                         type &name = *static_cast<type *>(meshes[i]); \
     456             :                         if(name.canrender || debugcolmesh) \
     457             :                         { \
     458             :                             body; \
     459             :                         } \
     460             :                     } \
     461             :                 } while(0)
     462             : 
     463             :             protected:
     464             :                 meshgroup();
     465             : 
     466             :                 std::string name;
     467             : 
     468             :                 void bindpos(GLuint ebuf, GLuint vbuf, const void *v, int stride, int type, int size);
     469             :                 void bindpos(GLuint ebuf, GLuint vbuf, const vec *v, int stride);
     470             :                 void bindpos(GLuint ebuf, GLuint vbuf, const vec4<half> *v, int stride);
     471             :                 void bindtc(const void *v, int stride);
     472             :                 void bindtangents(const void *v, int stride);
     473             :                 void bindbones(const void *wv, const void *bv, int stride);
     474             :             //no private-able members
     475             :         };
     476             : 
     477             :         /*
     478             :          * The meshgroups map stores meshgroups which may be shared between different
     479             :          * model instantiations (e.g. skeletal models which have other models attached
     480             :          * as parts).
     481             :          *
     482             :          * The animmodel is comprised of parts which each represent instance-specific
     483             :          * metadata applied to a meshgroup in this map.
     484             :          */
     485             :         static std::unordered_map<std::string, meshgroup *> meshgroups;
     486             : 
     487             :         /* The `part` object is the highest level of organization in a model object.
     488             :          * Each `part` is a logically separate part of an overall model, containing
     489             :          * its own skin(s) (`skin` objects), mesh(es) (`meshgroup` objects), and
     490             :          * model rendering parameters.
     491             :          *
     492             :          * Parts may contain a list of linked parts, which are other `part` objects
     493             :          * logically dependent on another `part`. An example is an object being held
     494             :          * in the hand of another model type, which is a separate mesh.
     495             :          */
     496             :         class part
     497             :         {
     498             :             public:
     499             :                 const animmodel *model;
     500             :                 int index;
     501             :                 meshgroup *meshes; //pointer to a single meshgroup in animmodel::meshgroups
     502             : 
     503             :                 //a pointer to another part object dependent on this part.
     504             :                 //pointed part may be modified in case it is necessary to link/unlink
     505             :                 //the pointed part to other dependent parts
     506             :                 struct linkedpart
     507             :                 {
     508             :                     part *p;
     509             :                     int tag, anim, basetime;
     510             :                     vec translate;
     511             :                     vec *pos; //a pos pointer from a modelattach object, which is set from within game
     512             :                     matrix4 matrix;
     513             : 
     514             :                     linkedpart() : p(nullptr), tag(-1), anim(-1), basetime(0), translate(0, 0, 0), pos(nullptr) {}
     515           0 :                     linkedpart(part *p, int tag, int anim, int basetime, vec translate, vec *post, matrix4 matrix) :
     516           0 :                         p(p), tag(tag), anim(anim), basetime(basetime), translate(translate), pos(post), matrix(matrix) {}
     517             :                 };
     518             :                 std::vector<linkedpart> links;
     519             :                 std::vector<skin> skins;
     520             :                 int numanimparts;
     521             :                 float pitchscale, pitchoffset, pitchmin, pitchmax;
     522             : 
     523             :                 part(const animmodel *model, int index = 0);
     524             :                 virtual ~part();
     525             :                 part(const part& a) = delete;
     526             :                 part &operator=(const part &a) = delete;
     527             : 
     528             :                 void cleanup();
     529             :                 void disablepitch();
     530             :                 void calcbb(vec &bbmin, vec &bbmax, const matrix4x3 &m, float modelscale) const;
     531             :                 void genBIH(std::vector<BIH::mesh> &bih, const matrix4x3 &m, float modelscale) const;
     532             :                 void genshadowmesh(std::vector<triangle> &tris, const matrix4x3 &m, float modelscale) const;
     533             :                 bool link(part *p, std::string_view tag, const vec &translate = vec(0, 0, 0), int anim = -1, int basetime = 0, vec *pos = nullptr);
     534             :                 bool unlink(const part *p);
     535             :                 void initskins(Texture *tex = notexture, Texture *masks = notexture, uint limit = 0);
     536             :                 bool alphatested() const;
     537             :                 void preloadBIH() const;
     538             :                 void preloadshaders();
     539             :                 void preloadmeshes();
     540             : 
     541             :                 void intersect(int anim, int basetime, int basetime2, float pitch, const vec &axis, const vec &forward, dynent *d, const vec &o, const vec &ray);
     542             :                 void intersect(int anim, int basetime, int basetime2, float pitch, const vec &axis, const vec &forward, dynent *d, const vec &o, const vec &ray, AnimState *as);
     543             :                 void render(int anim, int basetime, int basetime2, float pitch, const vec &axis, const vec &forward, dynent *d);
     544             :                 void render(int anim, int basetime, int basetime2, float pitch, const vec &axis, const vec &forward, dynent *d, AnimState *as);
     545             :                 void setanim(int animpart, int num, int frame, int range, float speed, int priority = 0);
     546             :                 bool animated() const;
     547             :                 virtual void loaded();
     548             : 
     549             :             protected:
     550             :                 virtual void getdefaultanim(animinfo &info) const;
     551             :                 bool calcanim(int animpart, int anim, int basetime, int basetime2, dynent *d, int interp, animinfo &info, int &animinterptime) const;
     552             : 
     553             :             private:
     554             :                 struct animspec
     555             :                 {
     556             :                     int frame, range;
     557             :                     float speed;
     558             :                     int priority;
     559             :                 };
     560             : 
     561             :                 std::vector<animspec> *anims[maxanimparts]; //pointer to array of std::vector<animspec>
     562             :         };
     563             : 
     564             :         std::vector<part *> parts;
     565             : 
     566             :         //ordinary methods
     567             :         ~animmodel();
     568             :         animmodel(const animmodel& a) = delete;
     569             :         animmodel &operator=(const animmodel &a) = delete;
     570             : 
     571             :         part &addpart();
     572             :         void initmatrix(matrix4x3 &m) const;
     573             :         void genBIH(std::vector<BIH::mesh> &bih);
     574             :         bool link(part *p, std::string_view tag, const vec &translate = vec(0, 0, 0), int anim = -1, int basetime = 0, vec *pos = nullptr) const;
     575             :         void loaded();
     576             :         bool unlink(const part *p) const;
     577             :         void render(int anim, int basetime, int basetime2, float pitch, const vec &axis, const vec &forward, dynent *d, modelattach *a) const;
     578             : 
     579             :         //virtual methods
     580             :         virtual bool flipy() const = 0;
     581             :         virtual bool loadconfig(const std::string &mdlname) = 0;
     582             :         virtual bool loaddefaultparts() = 0;
     583             :         virtual void startload() = 0;
     584             :         virtual void endload() = 0;
     585             : 
     586             :         //model object overrides
     587             :         void render(int anim, int basetime, int basetime2, const vec &o, float yaw, float pitch, float roll, dynent *d, modelattach *a, float size, const vec4<float> &color) const override final;
     588             :         void cleanup() override final;
     589             :         void genshadowmesh(std::vector<triangle> &tris, const matrix4x3 &orient) override final;
     590             :         void preloadBIH() override final;
     591             :         bool setBIH() override final;
     592             :         bool animated() const override final;
     593             :         bool pitched() const override final;
     594             :         bool alphatested() const override final;
     595             :         bool load() override final;
     596             :         void preloadshaders() override final;
     597             :         void preloadmeshes() override final;
     598             :         void setshader(Shader *shader) override final;
     599             :         void setspec(float spec) override final;
     600             :         void setgloss(int gloss) override final;
     601             :         void setglow(float glow, float delta, float pulse) override final;
     602             :         void setalphatest(float alphatest) override final;
     603             :         void setfullbright(float fullbright) override final;
     604             :         void setcullface(int cullface) override final;
     605             :         void setcolor(const vec &color) override final;
     606             :         void settransformation(const std::optional<vec> pos,
     607             :                                const std::optional<vec> rotate,
     608             :                                const std::optional<vec> orient,
     609             :                                const std::optional<float> size) override final;
     610             :         vec4<float> locationsize() const override final;
     611             :         void calcbb(vec &center, vec &radius) const override final;
     612             :         void calctransform(matrix4x3 &m) const override final;
     613             :         void startrender() const override final;
     614             :         void endrender() const override final;
     615             :         void boundbox(vec &center, vec &radius) override final;
     616             :         float collisionbox(vec &center, vec &radius) override final;
     617             :         float above() override final;
     618             :         const std::string &modelname() const override final;
     619             :         //static methods
     620             :         static void disablebones();
     621             :         static void disabletangents();
     622             :         static void disabletc();
     623             :         static void disablevbo();
     624             : 
     625             :     protected:
     626             :         enum
     627             :         {
     628             :             Link_Tag = 0,
     629             :             Link_Reuse
     630             :         };
     631             : 
     632             :         animmodel(std::string name);
     633             : 
     634             :         virtual int linktype(const animmodel *, const part *) const;
     635             :         int intersect(int anim, int basetime, int basetime2, const vec &pos, float yaw, float pitch, float roll, dynent *d, modelattach *a, float size, const vec &o, const vec &ray, float &dist) const override final;
     636             : 
     637             :         static bool enabletc, enablebones, enabletangents;
     638             :         static std::stack<matrix4> matrixstack;
     639             :         static float sizescale;
     640             : 
     641             :     private:
     642             :         void intersect(int anim, int basetime, int basetime2, float pitch, const vec &axis, const vec &forward, dynent *d, modelattach *a, const vec &o, const vec &ray) const;
     643             : 
     644             :         static bool enablecullface, enabledepthoffset;
     645             :         static vec4<float> colorscale;
     646             :         static GLuint lastvbuf, lasttcbuf, lastxbuf, lastbbuf, lastebuf;
     647             :         static const Texture *lasttex,
     648             :                              *lastdecal,
     649             :                              *lastmasks,
     650             :                              *lastnormalmap;
     651             : };
     652             : 
     653             : /* modelloader
     654             :  *
     655             :  * modelloader is a template for a wrapper to load a model into a model/animmodel
     656             :  * object from a transactional format, it is intended to be a child template class
     657             :  * of an animmodel derivative (the BASE template parameter)
     658             :  *
     659             :  * skelloader is a specialization of this class which uses modelloader to load
     660             :  * a skeletal model
     661             :  *
     662             :  */
     663             : template<class MDL, class BASE>
     664             : struct modelloader : BASE
     665             : {
     666             :     static MDL *loading;
     667             :     static std::string dir;
     668             : 
     669          19 :     modelloader(std::string name) : BASE(name)
     670             :     {
     671          19 :     }
     672             : 
     673           2 :     static bool cananimate()
     674             :     {
     675           2 :         return true;
     676             :     }
     677             : 
     678           6 :     static bool multiparted()
     679             :     {
     680           6 :         return true;
     681             :     }
     682             : 
     683           3 :     static bool multimeshed()
     684             :     {
     685           3 :         return true;
     686             :     }
     687             : 
     688          17 :     void startload() override final
     689             :     {
     690          17 :         loading = static_cast<MDL *>(this);
     691          17 :     }
     692             : 
     693          13 :     void endload() override final
     694             :     {
     695          13 :         loading = nullptr;
     696          13 :     }
     697             : 
     698           6 :     bool loadconfig(const std::string &mdlname) override final
     699             :     {
     700           6 :         dir.clear();
     701           6 :         dir.append(modelpath).append(mdlname);
     702           6 :         std::string cfgname;
     703           6 :         cfgname.append(modelpath).append(mdlname).append("/").append(MDL::formatname()).append(".cfg");
     704             : 
     705           6 :         identflags &= ~Idf_Persist;
     706           6 :         bool success = execfile(cfgname.c_str(), false);
     707           6 :         identflags |= Idf_Persist;
     708           6 :         return success;
     709           6 :     }
     710             : };
     711             : 
     712             : template<class MDL, class BASE>
     713             : MDL *modelloader<MDL, BASE>::loading = nullptr;
     714             : 
     715             : template<class MDL, class BASE>
     716             : std::string modelloader<MDL, BASE>::dir = {""}; // crashes clang if "" is used here
     717             : 
     718             : /* modelcommands
     719             :  *
     720             :  * this template class adds a series of commands to the cubescript binding
     721             :  * adaptable to a specific model type
     722             :  *
     723             :  * this template class generates unique command names for each separate model type
     724             :  * such as objcolor for obj, or md5color for md5 models; they are static and the
     725             :  * same for any given MDL template parameter
     726             :  *
     727             :  * the intended MDL template parameter is one of the model formats (md5, obj, etc)
     728             :  */
     729             : template<class MDL>
     730             : struct modelcommands
     731             : {
     732             :     typedef class MDL::part part;
     733             :     typedef class MDL::skin skin;
     734             : 
     735          69 :     static bool checkmdl()
     736             :     {
     737          69 :         if(!MDL::loading)
     738             :         {
     739          69 :             conoutf(Console_Error, "not loading a model");
     740          69 :             return false;
     741             :         }
     742             :         else
     743             :         {
     744           0 :             return true;
     745             :         }
     746             :     }
     747             : 
     748           3 :     static void mdlcullface(const int *cullface)
     749             :     {
     750           3 :         if(!checkmdl())
     751             :         {
     752           3 :             return;
     753             :         }
     754           0 :         MDL::loading->setcullface(*cullface);
     755             :     }
     756             : 
     757           3 :     static void mdlcolor(const float *r, const float *g, const float *b)
     758             :     {
     759           3 :         if(!checkmdl())
     760             :         {
     761           3 :             return;
     762             :         }
     763           0 :         MDL::loading->setcolor(vec(*r, *g, *b));
     764             :     }
     765             : 
     766           3 :     static void mdlcollide(const int *collide)
     767             :     {
     768           3 :         if(!checkmdl())
     769             :         {
     770           3 :             return;
     771             :         }
     772           0 :         MDL::loading->collide = *collide!=0 ? (MDL::loading->collide ? MDL::loading->collide : Collide_OrientedBoundingBox) : Collide_None;
     773             :     }
     774             : 
     775           3 :     static void mdlellipsecollide(const int *collide)
     776             :     {
     777           3 :         if(!checkmdl())
     778             :         {
     779           3 :             return;
     780             :         }
     781           0 :         MDL::loading->collide = *collide!=0 ? Collide_Ellipse : Collide_None;
     782             :     }
     783             : 
     784           3 :     static void mdltricollide(const char *collide)
     785             :     {
     786           3 :         if(!checkmdl())
     787             :         {
     788           3 :             return;
     789             :         }
     790           0 :         MDL::loading->collidemodel.clear();
     791           0 :         char *end = nullptr;
     792           0 :         int val = std::strtol(collide, &end, 0);
     793           0 :         if(*end)
     794             :         {
     795           0 :             val = 1;
     796           0 :             MDL::loading->collidemodel = std::string(collide);
     797             :         }
     798           0 :         MDL::loading->collide = val ? Collide_TRI : Collide_None;
     799             :     }
     800             : 
     801           3 :     static void mdlspec(const float *percent)
     802             :     {
     803           3 :         if(!checkmdl())
     804             :         {
     805           3 :             return;
     806             :         }
     807           0 :         float spec = *percent > 0 ? *percent/100.0f : 0.0f;
     808           0 :         MDL::loading->setspec(spec);
     809             :     }
     810             : 
     811           3 :     static void mdlgloss(int *gloss)
     812             :     {
     813           3 :         if(!checkmdl())
     814             :         {
     815           3 :             return;
     816             :         }
     817           0 :         MDL::loading->setgloss(std::clamp(*gloss, 0, 2));
     818             :     }
     819             : 
     820           3 :     static void mdlalphatest(const float *cutoff)
     821             :     {
     822           3 :         if(!checkmdl())
     823             :         {
     824           3 :             return;
     825             :         }
     826           0 :         MDL::loading->setalphatest(std::max(0.0f, std::min(1.0f, *cutoff)));
     827             :     }
     828             : 
     829           3 :     static void mdldepthoffset(const int *offset)
     830             :     {
     831           3 :         if(!checkmdl())
     832             :         {
     833           3 :             return;
     834             :         }
     835           0 :         MDL::loading->depthoffset = *offset!=0;
     836             :     }
     837             : 
     838           3 :     static void mdlglow(const float *percent, const float *delta, const float *pulse)
     839             :     {
     840           3 :         if(!checkmdl())
     841             :         {
     842           3 :             return;
     843             :         }
     844           0 :         float glow = *percent > 0 ? *percent/100.0f : 0.0f,
     845           0 :               glowdelta = *delta/100.0f,
     846           0 :               glowpulse = *pulse > 0 ? *pulse/1000.0f : 0;
     847           0 :         glowdelta -= glow;
     848           0 :         MDL::loading->setglow(glow, glowdelta, glowpulse);
     849             :     }
     850             : 
     851           3 :     static void mdlfullbright(const float *fullbright)
     852             :     {
     853           3 :         if(!checkmdl())
     854             :         {
     855           3 :             return;
     856             :         }
     857           0 :         MDL::loading->setfullbright(*fullbright);
     858             :     }
     859             : 
     860             : 
     861           3 :     static void mdlshader(const char *shader)
     862             :     {
     863           3 :         if(!checkmdl())
     864             :         {
     865           3 :             return;
     866             :         }
     867           0 :         MDL::loading->setshader(lookupshaderbyname(shader));
     868             :     }
     869             : 
     870             :     //assigns a new spin speed in three euler angles for the model object currently being loaded
     871           3 :     static void mdlspin(const float *yaw, const float *pitch, const float *roll)
     872             :     {
     873           3 :         if(!checkmdl())
     874             :         {
     875           3 :             return;
     876             :         }
     877           0 :         MDL::loading->settransformation(std::nullopt, vec(*yaw, *pitch, *roll), std::nullopt, std::nullopt);
     878             :     }
     879             : 
     880             :     //assigns a new scale factor in % for the model object currently being loaded
     881           3 :     static void mdlscale(const float *percent)
     882             :     {
     883           3 :         if(!checkmdl())
     884             :         {
     885           3 :             return;
     886             :         }
     887           0 :         float scale = *percent > 0 ? *percent/100.0f : 1.0f;
     888           0 :         MDL::loading->settransformation(std::nullopt, std::nullopt, std::nullopt, scale);
     889             :     }
     890             : 
     891             :     //assigns translation in x,y,z in cube units for the model object currently being loaded
     892           3 :     static void mdltrans(const float *x, const float *y, const float *z)
     893             :     {
     894           3 :         if(!checkmdl())
     895             :         {
     896           3 :             return;
     897             :         }
     898           0 :         MDL::loading->settransformation(vec(*x, *y, *z), std::nullopt, std::nullopt, std::nullopt);
     899             :     }
     900             : 
     901             :     //assigns angle to the offsetyaw field of the model object currently being loaded
     902           3 :     static void mdlyaw(const float *angle)
     903             :     {
     904           3 :         if(!checkmdl())
     905             :         {
     906           3 :             return;
     907             :         }
     908           0 :         MDL::loading->orientation.x = *angle;
     909             :     }
     910             : 
     911             : 
     912             :     //assigns angle to the offsetpitch field of the model object currently being loaded
     913           3 :     static void mdlpitch(const float *angle)
     914             :     {
     915           3 :         if(!checkmdl())
     916             :         {
     917           3 :             return;
     918             :         }
     919           0 :         MDL::loading->orientation.y = *angle;
     920             :     }
     921             : 
     922             :     //assigns angle to the offsetroll field of the model object currently being loaded
     923           3 :     static void mdlroll(const float *angle)
     924             :     {
     925           3 :         if(!checkmdl())
     926             :         {
     927           3 :             return;
     928             :         }
     929           0 :         MDL::loading->orientation.z = *angle;
     930             :     }
     931             : 
     932             :     //assigns shadow to the shadow field of the model object currently being loaded
     933           3 :     static void mdlshadow(const int *shadow)
     934             :     {
     935           3 :         if(!checkmdl())
     936             :         {
     937           3 :             return;
     938             :         }
     939           0 :         MDL::loading->shadow = *shadow!=0;
     940             :     }
     941             : 
     942             :     //assigns alphashadow to the alphashadow field of the model object currently being loaded
     943           3 :     static void mdlalphashadow(const int *alphashadow)
     944             :     {
     945           3 :         if(!checkmdl())
     946             :         {
     947           3 :             return;
     948             :         }
     949           0 :         MDL::loading->alphashadow = *alphashadow!=0;
     950             :     }
     951             : 
     952             :     //assigns rad, h, eyeheight to the fields of the model object currently being loaded
     953           3 :     static void mdlbb(const float *rad, const float *h, const float *eyeheight)
     954             :     {
     955           3 :         if(!checkmdl())
     956             :         {
     957           3 :             return;
     958             :         }
     959           0 :         MDL::loading->collidexyradius = *rad;
     960           0 :         MDL::loading->collideheight = *h;
     961           0 :         MDL::loading->eyeheight = *eyeheight;
     962             :     }
     963             : 
     964           3 :     static void mdlextendbb(const float *x, const float *y, const float *z)
     965             :     {
     966           3 :         if(!checkmdl())
     967             :         {
     968           3 :             return;
     969             :         }
     970           0 :         MDL::loading->bbextend = vec(*x, *y, *z);
     971             :     }
     972             : 
     973             :     /* mdlname
     974             :      *
     975             :      * returns the name of the model currently loaded [most recently]
     976             :      */
     977           3 :     static void mdlname()
     978             :     {
     979           3 :         if(!checkmdl())
     980             :         {
     981           3 :             return;
     982             :         }
     983           0 :         result(MDL::loading->modelname().c_str());
     984             :     }
     985             : 
     986          14 :     static void setdir(const char *name)
     987             :     {
     988          14 :         if(!MDL::loading)
     989             :         {
     990           3 :             conoutf("not loading an %s", MDL::formatname());
     991           3 :             return;
     992             :         }
     993          11 :         MDL::dir.clear();
     994          11 :         MDL::dir.append(modelpath).append(name);
     995             :     }
     996             : 
     997             :     /**
     998             :      * @brief Returns an iterator vector of meshes with the given name
     999             :      *
    1000             :      * Returns a vector of MDL::loading->parts.back()'s meshgroup's mesh vector
    1001             :      * iterators where those iterator's contents' name field compares equal to the
    1002             :      * passed string. If the wildcard "*" is passed as `meshname` then all elements
    1003             :      * will be added regardless of name. If no such mesh vector exists (or there is
    1004             :      * no loading model) then an empty vector is returned.
    1005             :      *
    1006             :      * @param meshname the mesh name to select from the mesh vector
    1007             :      *
    1008             :      * @return vector of iterators corresponding to meshes with the given name
    1009             :      */
    1010           3 :     static std::vector<std::vector<animmodel::Mesh *>::const_iterator> getmeshes(std::string_view meshname)
    1011             :     {
    1012           3 :         std::vector<std::vector<animmodel::Mesh *>::const_iterator> meshlist;
    1013           3 :         if(!MDL::loading || MDL::loading->parts.empty())
    1014             :         {
    1015           3 :             conoutf("not loading an %s", MDL::formatname());
    1016           3 :             return meshlist; //empty vector
    1017             :         }
    1018           0 :         const part &mdl = *MDL::loading->parts.back();
    1019           0 :         if(!mdl.meshes)
    1020             :         {
    1021           0 :             return meshlist; //empty vector
    1022             :         }
    1023           0 :         meshlist = mdl.meshes->getmeshes(meshname);
    1024           0 :         return meshlist;
    1025           0 :     }
    1026             : 
    1027             :     /**
    1028             :      * @brief Returns an iterator vector of skins associated with the given name
    1029             :      *
    1030             :      * Returns a vector of MDL::loading->parts.back()'s meshgroup's skin vector
    1031             :      * iterators where those iterator's contents' name field compares equal to the
    1032             :      * passed string. If the wildcard "*" is passed as `meshname` then all elements
    1033             :      * will be added regardless of name. If no such mesh vector exists (or there is
    1034             :      * no loading model) then an empty vector is returned.
    1035             :      *
    1036             :      * @param meshname the mesh name to select from the skin vector
    1037             :      *
    1038             :      * @return vector of iterators corresponding to skins with the given name
    1039             :      */
    1040          44 :     static std::vector<std::vector<animmodel::skin>::iterator> getskins(std::string_view meshname)
    1041             :     {
    1042          44 :         std::vector<std::vector<animmodel::skin>::iterator> skinlist;
    1043          44 :         if(!MDL::loading || MDL::loading->parts.empty())
    1044             :         {
    1045          36 :             conoutf("not loading an %s", MDL::formatname());
    1046          36 :             return skinlist;
    1047             :         }
    1048           8 :         part &mdl = *MDL::loading->parts.back();
    1049           8 :         if(!mdl.meshes)
    1050             :         {
    1051           0 :             return skinlist;
    1052             :         }
    1053           8 :         std::vector<size_t> skinindices = mdl.meshes->getskins(meshname);
    1054          16 :         for(size_t i : skinindices)
    1055             :         {
    1056           8 :             skinlist.push_back(mdl.skins.begin() + i);
    1057             :         }
    1058           8 :         return skinlist;
    1059           8 :     }
    1060             : 
    1061           6 :     static void setskin(const char *meshname, const char *tex, const char *masks)
    1062             :     {
    1063           6 :         auto skinlist = getskins(meshname);
    1064           9 :         for(auto s : skinlist)
    1065             :         {
    1066           3 :             (*s).tex = textureload(makerelpath(MDL::dir.c_str(), tex), 0, true, false);
    1067           3 :             if(*masks)
    1068             :             {
    1069           3 :                 (*s).masks = textureload(makerelpath(MDL::dir.c_str(), masks), 0, true, false);
    1070             :             }
    1071             :         }
    1072           6 :     }
    1073             : 
    1074           3 :     static void setspec(const char *meshname, const float *percent)
    1075             :     {
    1076           3 :         float spec = *percent > 0 ? *percent/100.0f : 0.0f;
    1077           3 :         auto skinlist = getskins(meshname);
    1078           3 :         for(auto s : skinlist)
    1079             :         {
    1080           0 :             (*s).spec = spec;
    1081             :         }
    1082           3 :     }
    1083             : 
    1084           3 :     static void setgloss(const char *meshname, const int *gloss)
    1085             :     {
    1086           3 :         auto skinlist = getskins(meshname);
    1087           3 :         for(auto s : skinlist)
    1088             :         {
    1089           0 :             (*s).gloss = std::clamp(*gloss, 0, 2);
    1090             :         }
    1091           3 :     }
    1092             : 
    1093           3 :     static void setglow(const char *meshname, const float *percent, const float *delta, const float *pulse)
    1094             :     {
    1095           3 :         float glow = *percent > 0 ? *percent/100.0f : 0.0f,
    1096           3 :               glowdelta = *delta/100.0f,
    1097           3 :               glowpulse = *pulse > 0 ? *pulse/1000.0f : 0;
    1098           3 :         glowdelta -= glow;
    1099           3 :         auto skinlist = getskins(meshname);
    1100           3 :         for(auto s : skinlist)
    1101             :         {
    1102           0 :             (*s).glow = glow;
    1103           0 :             (*s).glowdelta = glowdelta;
    1104           0 :             (*s).glowpulse = glowpulse;
    1105             :         }
    1106           3 :     }
    1107             : 
    1108           3 :     static void setalphatest(const char *meshname, const float *cutoff)
    1109             :     {
    1110           3 :         auto skinlist = getskins(meshname);
    1111           3 :         for(auto s : skinlist)
    1112             :         {
    1113           0 :             (*s).alphatest = std::max(0.0f, std::min(1.0f, *cutoff));
    1114             :         }
    1115           3 :     }
    1116             : 
    1117           3 :     static void setcullface(const char *meshname, const int *cullface)
    1118             :     {
    1119           3 :         auto skinlist = getskins(meshname);
    1120           3 :         for(auto s : skinlist)
    1121             :         {
    1122           0 :             (*s).cullface = *cullface;
    1123             :         }
    1124           3 :     }
    1125             : 
    1126           3 :     static void setcolor(const char *meshname, const float *r, const float *g, const float *b)
    1127             :     {
    1128           3 :         auto skinlist = getskins(meshname);
    1129           3 :         for(auto s : skinlist)
    1130             :         {
    1131           0 :             (*s).color = vec(*r, *g, *b);
    1132             :         }
    1133           3 :     }
    1134             : 
    1135           4 :     static void setbumpmap(const char *meshname, const char *normalmapfile)
    1136             :     {
    1137           4 :         Texture *normalmaptex = textureload(makerelpath(MDL::dir.c_str(), normalmapfile), 0, true, false);
    1138           4 :         auto skinlist = getskins(meshname);
    1139           5 :         for(auto s : skinlist)
    1140             :         {
    1141           1 :             (*s).normalmap = normalmaptex;
    1142             :         }
    1143           4 :     }
    1144             : 
    1145           4 :     static void setdecal(const char *meshname, const char *decal)
    1146             :     {
    1147           4 :         auto skinlist = getskins(meshname);
    1148           5 :         for(auto s : skinlist)
    1149             :         {
    1150           1 :             (*s).decal = textureload(makerelpath(MDL::dir.c_str(), decal), 0, true, false);
    1151             :         }
    1152           4 :     }
    1153             : 
    1154           3 :     static void setfullbright(const char *meshname, const float *fullbright)
    1155             :     {
    1156           3 :         auto skinlist = getskins(meshname);
    1157           3 :         for(auto s : skinlist)
    1158             :         {
    1159           0 :             (*s).fullbright = *fullbright;
    1160             :         }
    1161           3 :     }
    1162             : 
    1163           3 :     static void setshader(const char *meshname, const char *shader)
    1164             :     {
    1165           3 :         auto skinlist = getskins(meshname);
    1166           3 :         for(auto s : skinlist)
    1167             :         {
    1168           0 :             (*s).shader = lookupshaderbyname(shader);
    1169             :         }
    1170           3 :     }
    1171             : 
    1172           3 :     static void setscroll(const char *meshname, const float *scrollu, const float *scrollv)
    1173             :     {
    1174           3 :         auto skinlist = getskins(meshname);
    1175           3 :         for(auto s : skinlist)
    1176             :         {
    1177           0 :             (*s).scrollu = *scrollu;
    1178           0 :             (*s).scrollv = *scrollv;
    1179             :         }
    1180           3 :     }
    1181             : 
    1182           3 :     static void setnoclip(const char *meshname, const int *noclip)
    1183             :     {
    1184           6 :         auto meshlist = getmeshes(std::string(meshname));
    1185           3 :         if(meshlist.empty())
    1186             :         {
    1187           3 :             return;
    1188             :         }
    1189           0 :         for(auto &i : meshlist)
    1190             :         {
    1191           0 :             (*i)->noclip = *noclip!=0;
    1192             :         }
    1193           3 :     }
    1194             : 
    1195           0 :     static void settricollide(const char *meshname)
    1196             :     {
    1197           0 :         bool init = true;
    1198           0 :         auto meshlist = getmeshes(std::string(meshname));
    1199           0 :         if(!meshlist.empty())
    1200             :         {
    1201           0 :             return;
    1202             :         }
    1203             :         else
    1204             :         {
    1205           0 :             for(auto &i : meshlist)
    1206             :             {
    1207           0 :                 if(!(*i)->cancollide)
    1208             :                 {
    1209           0 :                     init = false;
    1210             :                 }
    1211             :             }
    1212           0 :             if(init)
    1213             :             {
    1214           0 :                 for(auto &i : meshlist)
    1215             :                 {
    1216           0 :                     (*i)->cancollide = false;
    1217             :                 }
    1218             :             }
    1219           0 :             for(auto &i : meshlist)
    1220             :             {
    1221           0 :                 (*i)->cancollide = true;
    1222           0 :                 (*i)->canrender = false;
    1223             :             }
    1224             :         }
    1225           0 :     }
    1226             : 
    1227           3 :     static void setlink(const int *parent, const int *child, const char *tagname, const float *x, const float *y, const float *z)
    1228             :     {
    1229           3 :         if(!MDL::loading)
    1230             :         {
    1231           3 :             conoutf("not loading an %s", MDL::formatname());
    1232           3 :             return;
    1233             :         }
    1234           0 :         if(!(static_cast<int>(MDL::loading->parts.size()) > *parent) || !(static_cast<int>(MDL::loading->parts.size()) > *child))
    1235             :         {
    1236           0 :             conoutf("no models loaded to link");
    1237           0 :             return;
    1238             :         }
    1239           0 :         if(!MDL::loading->parts[*parent]->link(MDL::loading->parts[*child], tagname, vec(*x, *y, *z)))
    1240             :         {
    1241           0 :             conoutf("could not link model %s", MDL::loading->modelname().c_str());
    1242             :         }
    1243             :     }
    1244             : 
    1245             :     template<class F>
    1246         152 :     void modelcommand(F *fun, std::string_view suffix, std::string_view args)
    1247             :     {
    1248         152 :         std::string name;
    1249         152 :         name.append(MDL::formatname()).append(suffix);
    1250         152 :         addcommand(newstring(name.c_str()), (identfun)fun, args.data());
    1251         152 :     }
    1252             : 
    1253           3 :     modelcommands()
    1254             :     {
    1255           3 :         modelcommand(setdir, "dir", "s");//<fmt>dir [name]
    1256           3 :         if(MDL::multimeshed())
    1257             :         {
    1258           3 :             modelcommand(mdlcullface, "cullface", "i");
    1259           3 :             modelcommand(mdlcolor, "color", "fff");
    1260           3 :             modelcommand(mdlcollide, "collide", "i");
    1261           3 :             modelcommand(mdlellipsecollide, "ellipsecollide", "i");
    1262           3 :             modelcommand(mdltricollide, "tricollide", "s");
    1263           3 :             modelcommand(mdlspec, "spec", "f");
    1264           3 :             modelcommand(mdlgloss, "gloss", "i");
    1265           3 :             modelcommand(mdlalphatest, "alphatest", "f");
    1266           3 :             modelcommand(mdldepthoffset, "depthoffset", "i");
    1267           3 :             modelcommand(mdlglow, "glow", "fff");
    1268           3 :             modelcommand(mdlfullbright, "fullbright", "f");
    1269           3 :             modelcommand(mdlshader, "shader", "s");
    1270           3 :             modelcommand(mdlspin, "spin", "fff");
    1271           3 :             modelcommand(mdlscale, "scale", "f");
    1272           3 :             modelcommand(mdltrans, "trans", "fff");
    1273           3 :             modelcommand(mdlyaw, "yaw", "f");
    1274           3 :             modelcommand(mdlpitch, "pitch", "f");
    1275           3 :             modelcommand(mdlroll, "roll", "f");
    1276           3 :             modelcommand(mdlshadow, "shadow", "i");
    1277           3 :             modelcommand(mdlalphashadow, "alphashadow", "i");
    1278           3 :             modelcommand(mdlbb, "bb", "fff");
    1279           3 :             modelcommand(mdlextendbb, "extendbb", "fff");
    1280           3 :             modelcommand(mdlname, "name", "");
    1281             : 
    1282           3 :             modelcommand(setskin, "skin", "sss");               //<fmt>skin [meshname] [tex] [masks]
    1283           3 :             modelcommand(setspec, "texspec", "sf");             //<fmt>texspec [mesh] [scale]
    1284           3 :             modelcommand(setgloss, "texgloss", "si");           //<fmt>texgloss [mesh] [type] type ranges 0..2
    1285           3 :             modelcommand(setglow, "texglow", "sfff");           //<fmt>texglow [mesh] [pct] [del] [pulse]
    1286           3 :             modelcommand(setalphatest, "meshalphatest", "sf");  //<fmt>meshalphatest [mesh] [cutoff]
    1287           3 :             modelcommand(setcullface, "meshcullface", "si");    //<fmt>cullface [mesh] [cullface]
    1288           3 :             modelcommand(setcolor, "meshcolor", "sfff");        //<fmt>meshcolor [mesh] [r] [g] [b]
    1289           3 :             modelcommand(setbumpmap, "bumpmap", "ss");          //<fmt>bumpmap [mesh] [tex]
    1290           3 :             modelcommand(setdecal, "decal", "ss");              //<fmt>decal [mesh] [tex]
    1291           3 :             modelcommand(setfullbright, "meshfullbright", "sf");//<fmt>meshfullbright [mesh] [bright]
    1292           3 :             modelcommand(setshader, "meshshader", "ss");        //<fmt>meshshader [mesh] [shader]
    1293           3 :             modelcommand(setscroll, "scroll", "sff");           //<fmt>scroll [mesh] [x] [y]
    1294           3 :             modelcommand(setnoclip, "noclip", "si");            //<fmt>noclip [mesh] [bool]
    1295           3 :             modelcommand(settricollide, "tricollide", "s");     //<fmt>settricollide [mesh]
    1296             :         }
    1297           3 :         if(MDL::multiparted())
    1298             :         {
    1299           3 :             modelcommand(setlink, "link", "iisfff");//<mdl>link [parent] [child] [tag] [x] [y] [z]
    1300             :         }
    1301           3 :     }
    1302             : };
    1303             : 
    1304             : #endif

Generated by: LCOV version 1.14