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: 2025-01-07 07:51:37 Functions: 210 235 89.4 %

          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, const TC *tcverts, int numverts, const typename T::tri *tris, int numtris, bool areaweight) const
     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, const TC *tcverts, int numverts, const typename T::tri *tris, int numtris, bool areaweight, int numframes) const
     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             : 
     415             :                 /**
     416             :                  * @brief Returns the number of animation frames this meshgroup contains
     417             :                  *
     418             :                  * For skelmodels, always returns at least 1. May be 0 or greater for vertmodels.
     419             :                  */
     420             :                 virtual int totalframes() const = 0;
     421             :                 virtual void *animkey() = 0;
     422             :                 virtual void cleanup() = 0;
     423             :                 virtual void render(const AnimState *as, float pitch, const vec &axis, const vec &forward, dynent *d, part *p) = 0;
     424             :                 virtual void preload() = 0;
     425             : 
     426             :                 /**
     427             :                  * Returns a list of Mesh iterators corresponding to a given name
     428             :                  * The iterators may be invalidated by other method calls.
     429             :                  */
     430             :                 std::vector<std::vector<animmodel::Mesh *>::const_iterator> getmeshes(std::string_view meshname) const;
     431             :                 /**
     432             :                  * Returns a list of indices corresponding to locations in animmodel::part::skins.
     433             :                  * These indices are invalidated if animmodel::skins is modified after calling.
     434             :                  */
     435             :                 std::vector<size_t> getskins(std::string_view meshname) const;
     436             : 
     437             :                 void calcbb(vec &bbmin, vec &bbmax, const matrix4x3 &t) const;
     438             :                 void genBIH(const std::vector<skin> &skins, std::vector<BIH::mesh> &bih, const matrix4x3 &t) const;
     439             :                 void genshadowmesh(std::vector<triangle> &tris, const matrix4x3 &t) const;
     440             : 
     441             :                 /**
     442             :                  * @brief Returns true if i is a valid index in the list of frames this meshgroup contains
     443             :                  *
     444             :                  * That is, returns true if the index i is at least 0 and is no larger than the last index
     445             :                  * in the frame list for this meshgroup
     446             :                  *
     447             :                  * @param index to query
     448             :                  *
     449             :                  * @return true if the passed index is a valid index
     450             :                  */
     451             :                 bool hasframe(int i) const;
     452             : 
     453             :                 /**
     454             :                  * @brief Returns true if the range i...n is a valid index of frames this meshgroup contains
     455             :                  *
     456             :                  * Returns true if the indices i through n begins with i at 0 or greater and i+n no larger
     457             :                  * than the last index in the frame list for this meshgroup
     458             :                  *
     459             :                  * @param i the first index to query
     460             :                  * @param n the number of indices thereafter to query
     461             :                  *
     462             :                  * @return true if the passed index range are all valid
     463             :                  */
     464             :                 bool hasframes(int i, int n) const;
     465             : 
     466             :                 /**
     467             :                  * @brief Returns the lesser of n and the number of remaining frames between i and n
     468             :                  *
     469             :                  * Returns n if there are existing frames between i and i+n, and returns the largest
     470             :                  * value that would satisfy this condition if not.
     471             :                  *
     472             :                  * @param i the first index to query
     473             :                  * @param n the number of indices thereafter to query
     474             :                  *
     475             :                  * @return n, or the maximum number of entries after i if n is too large
     476             :                  */
     477             :                 int clipframes(int i, int n) const;
     478             : 
     479             :                 /**
     480             :                  * @brief Returns the name of this meshgroup
     481             :                  *
     482             :                  * This is created by the constructor for the object.
     483             :                  *
     484             :                  * @return the name of the meshgroup
     485             :                  */
     486             :                 const std::string &groupname() const;
     487             : 
     488             :                 /**
     489             :                  * @brief Returns a list of valid renderable meshes contained within this object.
     490             :                  *
     491             :                  * Returns a vector of std::vector<>::iterator objects which point to valid
     492             :                  * elements of the object's mesh list which can be rendered (the relevant flag
     493             :                  * field is true). Alternatively if the global debugcolmesh is enabled, all
     494             :                  * meshes will be returned.
     495             :                  *
     496             :                  * @return a vector of std::vector iterators pointing to renderable meshes
     497             :                  */
     498             :                 std::vector<std::vector<Mesh *>::const_iterator> getrendermeshes() const;
     499             :                 std::vector<std::vector<Mesh *>::iterator> getrendermeshes();
     500             : 
     501             :                 #define LOOP_RENDER_MESHES(type, name, body) do { \
     502             :                     for(uint i = 0; i < meshes.size(); i++) \
     503             :                     { \
     504             :                         type &name = *static_cast<type *>(meshes[i]); \
     505             :                         if(name.canrender || debugcolmesh) \
     506             :                         { \
     507             :                             body; \
     508             :                         } \
     509             :                     } \
     510             :                 } while(0)
     511             : 
     512             :             protected:
     513             :                 meshgroup();
     514             : 
     515             :                 std::string name;
     516             : 
     517             :                 void bindpos(GLuint ebuf, GLuint vbuf, const void *v, int stride, int type, int size);
     518             :                 void bindpos(GLuint ebuf, GLuint vbuf, const vec *v, int stride);
     519             :                 void bindpos(GLuint ebuf, GLuint vbuf, const vec4<half> *v, int stride);
     520             : 
     521             :                 /**
     522             :                  * @brief Binds to TexCoord0 the provided data with a specified stridelength
     523             :                  *
     524             :                  * Binds (via glVertexAttribPointer) the provided array pointed to by v
     525             :                  * with a stride length (offset between attributes) of `stride`.
     526             :                  *
     527             :                  * @param v the array of texture coordinate data to bind
     528             :                  * @param stride the offset between coodinates within the array
     529             :                  */
     530             :                 void bindtc(const void *v, int stride);
     531             :                 void bindtangents(const void *v, int stride);
     532             :                 void bindbones(const void *wv, const void *bv, int stride);
     533             :             //no private-able members
     534             :         };
     535             : 
     536             :         /*
     537             :          * The meshgroups map stores meshgroups which may be shared between different
     538             :          * model instantiations (e.g. skeletal models which have other models attached
     539             :          * as parts).
     540             :          *
     541             :          * The animmodel is comprised of parts which each represent instance-specific
     542             :          * metadata applied to a meshgroup in this map.
     543             :          */
     544             :         static std::unordered_map<std::string, meshgroup *> meshgroups;
     545             : 
     546             :         /* The `part` object is the highest level of organization in a model object.
     547             :          * Each `part` is a logically separate part of an overall model, containing
     548             :          * its own skin(s) (`skin` objects), mesh(es) (`meshgroup` objects), and
     549             :          * model rendering parameters.
     550             :          *
     551             :          * Parts may contain a list of linked parts, which are other `part` objects
     552             :          * logically dependent on another `part`. An example is an object being held
     553             :          * in the hand of another model type, which is a separate mesh.
     554             :          */
     555             :         class part
     556             :         {
     557             :             public:
     558             :                 const animmodel *model;
     559             :                 int index;
     560             :                 meshgroup *meshes; //pointer to a single meshgroup in animmodel::meshgroups
     561             : 
     562             :                 //a pointer to another part object dependent on this part.
     563             :                 //pointed part may be modified in case it is necessary to link/unlink
     564             :                 //the pointed part to other dependent parts
     565             :                 struct linkedpart
     566             :                 {
     567             :                     part *p;
     568             :                     int tag, anim, basetime;
     569             :                     vec translate;
     570             :                     vec *pos; //a pos pointer from a modelattach object, which is set from within game
     571             :                     matrix4 matrix;
     572             : 
     573             :                     linkedpart() : p(nullptr), tag(-1), anim(-1), basetime(0), translate(0, 0, 0), pos(nullptr) {}
     574           0 :                     linkedpart(part *p, int tag, int anim, int basetime, vec translate, vec *post, matrix4 matrix) :
     575           0 :                         p(p), tag(tag), anim(anim), basetime(basetime), translate(translate), pos(post), matrix(matrix) {}
     576             :                 };
     577             :                 std::vector<linkedpart> links;
     578             :                 std::vector<skin> skins;
     579             :                 int numanimparts;
     580             :                 float pitchscale, pitchoffset, pitchmin, pitchmax;
     581             : 
     582             :                 /**
     583             :                  * @brief Creates a part object pointing to the given animmodel
     584             :                  *
     585             :                  * Sets this part's animation interpolation index to the value passed.
     586             :                  *
     587             :                  * @param model pointer to a single animmodel
     588             :                  * @param index animation interpolation index to assign
     589             :                  */
     590             :                 part(const animmodel *model, int index = 0);
     591             :                 virtual ~part();
     592             :                 part(const part& a) = delete;
     593             :                 part &operator=(const part &a) = delete;
     594             : 
     595             :                 /**
     596             :                  * @brief Cleans up the meshgroup and attached skins.
     597             :                  *
     598             :                  * Cleans up the single meshgroup at part::meshes, and cleans up
     599             :                  * every skin in the skin vector. Does not delete the skins nor
     600             :                  * the meshgroup but merely leaves them in a cleaned up state
     601             :                  */
     602             :                 void cleanup();
     603             : 
     604             :                 /**
     605             :                  * @brief Zeros out the pitch fields in this part.
     606             :                  *
     607             :                  * Sets part::pitchscale, part::pitchoffset, part::pitchmin, and part::pitchmax
     608             :                  * to zero. This means that the part after this is called will have zero pitch
     609             :                  * component (rotation).
     610             :                  */
     611             :                 void disablepitch();
     612             : 
     613             :                 /**
     614             :                  * @brief Returns to bbmin and bbmax the bounding area of this model
     615             :                  *
     616             :                  * Returns the rectangular bounding box of the model, given its scale
     617             :                  * and transformation.
     618             :                  *
     619             :                  * @param bbmin the -x-y-z corner of the rectangular bounding box
     620             :                  * @param bbmax the +x+y+z cornder of the rectangular bounding box
     621             :                  * @param m the transformation matrix of the model
     622             :                  * @param modelscale the scale factor for the model
     623             :                  */
     624             :                 void calcbb(vec &bbmin, vec &bbmax, const matrix4x3 &m, float modelscale) const;
     625             : 
     626             :                 /**
     627             :                  * @brief Generates a new BIH for this model and appends it to the back of the passed vector.
     628             :                  *
     629             :                  * Creates a new BIH at the end of the passed vector of BIH meshes and
     630             :                  * sets its parameters to that of this model part with the specified scale
     631             :                  * factor and scale.
     632             :                  *
     633             :                  * @param bih the bounded interval hierarchy for the model
     634             :                  * @param m the transformation matrix of this model
     635             :                  * @param modelscale the scale factor for the model
     636             :                  */
     637             :                 void genBIH(std::vector<BIH::mesh> &bih, const matrix4x3 &m, float modelscale) const;
     638             :                 void genshadowmesh(std::vector<triangle> &tris, const matrix4x3 &m, float modelscale) const;
     639             :                 bool link(part *p, std::string_view tag, const vec &translate = vec(0, 0, 0), int anim = -1, int basetime = 0, vec *pos = nullptr);
     640             : 
     641             :                 /**
     642             :                  * @brief Removes from this part's linkedpart list the linkedpart corresponding to the passed part
     643             :                  *
     644             :                  * `part::links` is the vector containing the list of linkedparts.
     645             :                  *
     646             :                  * @param p the part to remove the corresponding linkedpart from
     647             :                  */
     648             :                 bool unlink(const part *p);
     649             :                 void initskins(Texture *tex = notexture, Texture *masks = notexture, uint limit = 0);
     650             :                 bool alphatested() const;
     651             :                 void preloadBIH() const;
     652             :                 void preloadshaders();
     653             : 
     654             :                 /**
     655             :                  * @brief Causes the attached meshgroup to run its preload routine
     656             :                  *
     657             :                  * The (single) meshgroup pointed to by this object will preload itself.
     658             :                  * This process includes generating the vertex buffer objects required
     659             :                  * for rendering the meshes.
     660             :                  */
     661             :                 void preloadmeshes();
     662             : 
     663             :                 void intersect(int anim, int basetime, int basetime2, float pitch, const vec &axis, const vec &forward, dynent *d, const vec &o, const vec &ray);
     664             :                 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);
     665             :                 void render(int anim, int basetime, int basetime2, float pitch, const vec &axis, const vec &forward, dynent *d);
     666             :                 void render(int anim, int basetime, int basetime2, float pitch, const vec &axis, const vec &forward, dynent *d, AnimState *as);
     667             :                 void setanim(int animpart, int num, int frame, int range, float speed, int priority = 0);
     668             : 
     669             :                 /**
     670             :                  * @brief Returns true of all animparts for this part are set
     671             :                  *
     672             :                  * Checks that all animspec vectors in part::anims exist and have
     673             :                  * been instantiated. No values must be in the vectors for this to return true;
     674             :                  * they only need to exist.
     675             :                  *
     676             :                  * @return true if all animparts are set, false otherwise
     677             :                  */
     678             :                 bool animated() const;
     679             :                 virtual void loaded();
     680             : 
     681             :             private:
     682             :                 struct animspec
     683             :                 {
     684             :                     int frame, range;
     685             :                     float speed;
     686             :                     int priority;
     687             :                 };
     688             : 
     689             :                 std::vector<animspec> *anims[maxanimparts]; //pointer to array of std::vector<animspec>
     690             : 
     691             :                 virtual void getdefaultanim(animinfo &info) const;
     692             :                 bool calcanim(int animpart, int anim, int basetime, int basetime2, dynent *d, int interp, animinfo &info, int &animinterptime) const;
     693             :         };
     694             : 
     695             :         std::vector<part *> parts; //vector of part objects heap-allocated by skelmodel::addpart or part::addpart
     696             : 
     697             :         //ordinary methods
     698             :         ~animmodel();
     699             :         animmodel(const animmodel& a) = delete;
     700             :         animmodel &operator=(const animmodel &a) = delete;
     701             : 
     702             :         /**
     703             :          * @brief Creates a heap-allocated part object.
     704             :          *
     705             :          * This must be `delete`'d or it will cause a memory leak.
     706             :          *
     707             :          * @return a reference to the newly created part
     708             :          */
     709             :         part &addpart();
     710             :         /**
     711             :          * @brief Sets up a transformation matrix based on this model's rotation, translation, and scale
     712             :          *
     713             :          * @return a transformation matrix corresponding to the model's transformations
     714             :          */
     715             :         matrix4x3 initmatrix() const;
     716             :         void genBIH(std::vector<BIH::mesh> &bih);
     717             :         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;
     718             :         void loaded();
     719             :         bool unlink(const part *p) const;
     720             :         void render(int anim, int basetime, int basetime2, float pitch, const vec &axis, const vec &forward, dynent *d, modelattach *a) const;
     721             : 
     722             :         //virtual methods
     723             :         /**
     724             :          * @brief Returns whether this model type requires a flipped Y axis
     725             :          *
     726             :          * This function will return true if the model type needs its coordinate
     727             :          * system transformed to properly render in the engine. GLTF and MD5 do not,
     728             :          * while OBJ does.
     729             :          *
     730             :          * @return true if the model format requires transformation, false otherwise
     731             :          */
     732             :         virtual bool flipy() const = 0;
     733             :         virtual bool loadconfig(const std::string &mdlname) = 0;
     734             :         virtual bool loaddefaultparts() = 0;
     735             :         virtual void startload() = 0;
     736             :         virtual void endload() = 0;
     737             : 
     738             :         //model object overrides
     739             :         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;
     740             :         void cleanup() override final;
     741             :         void genshadowmesh(std::vector<triangle> &tris, const matrix4x3 &orient) const override final;
     742             :         void preloadBIH() override final;
     743             :         void setBIH() override final;
     744             :         bool animated() const override final;
     745             :         bool pitched() const override final;
     746             :         bool alphatested() const override final;
     747             :         bool load() override final;
     748             :         void preloadshaders() override final;
     749             : 
     750             :         /**
     751             :          * @brief Preloades every entry in the part::meshes array.
     752             :          *
     753             :          * No effect if there are no meshes to be loaded.
     754             :          */
     755             :         void preloadmeshes() override final;
     756             : 
     757             :         /**
     758             :          * @brief Sets the shader to be used by the model
     759             :          *
     760             :          * Clears any pre-existing slot parameters related to an existing shader.
     761             :          *
     762             :          * If no such shader was found, prints an error message to the console.
     763             :          *
     764             :          * @param shader the shader to set
     765             :          */
     766             :         void setshader(Shader *shader) override final;
     767             :         void setspec(float spec) override final;
     768             :         void setgloss(int gloss) override final;
     769             :         void setglow(float glow, float delta, float pulse) override final;
     770             :         /**
     771             :          * @brief Sets the alphatest value for each skin in each part of this model
     772             :          *
     773             :          * alphatest is one of the shader parameters for the skin shader. Each skin
     774             :          * in this model will recieve the same alphatest value.
     775             :          *
     776             :          * @param alphatest the alphatest value to set
     777             :          */
     778             :         void setalphatest(float alphatest) override final;
     779             :         void setfullbright(float fullbright) override final;
     780             :         void setcullface(int cullface) override final;
     781             :         void setcolor(const vec &color) override final;
     782             :         void settransformation(const std::optional<vec> pos,
     783             :                                const std::optional<vec> rotate,
     784             :                                const std::optional<vec> orient,
     785             :                                const std::optional<float> size) override final;
     786             : 
     787             :         /**
     788             :          * @brief Returns a 4-vector consisting of the translation and scale of the model.
     789             :          *
     790             :          * @return translation coordinates in x,y,z; scale factor in w
     791             :          */
     792             :         vec4<float> locationsize() const override final;
     793             :         void calcbb(vec &center, vec &radius) const override final;
     794             :         void startrender() const override final;
     795             :         void endrender() const override final;
     796             : 
     797             :         /**
     798             :          * @brief Recalculates the bounding box parameters and returns them to the parameters passed
     799             :          *
     800             :          * @param center returns the value of this model's bounding box
     801             :          * @param radius returns the radius of this model's bounding box
     802             :          */
     803             :         void boundbox(vec &center, vec &radius) override final;
     804             :         float collisionbox(vec &center, vec &radius) override final;
     805             :         const std::string &modelname() const override final;
     806             :         //static methods
     807             :         static void disablebones();
     808             :         static void disabletangents();
     809             :         static void disabletc();
     810             :         static void disablevbo();
     811             : 
     812             :     protected:
     813             :         enum
     814             :         {
     815             :             Link_Tag = 0,
     816             :             Link_Reuse
     817             :         };
     818             : 
     819             :         animmodel(std::string name);
     820             : 
     821             :         /**
     822             :          * @brief Returns the linkage type of the specified model part.
     823             :          *
     824             :          * For animmodels, always returns Link_Tag (see above anim).
     825             :          * Skeletal models may return Link_Reuse if the passed part shares the same mesh as this part.
     826             :          *
     827             :          * @return the linkage type of this part
     828             :          */
     829             :         virtual int linktype(const animmodel *, const part *) const;
     830             :         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;
     831             : 
     832             :         static bool enabletc, enablebones, enabletangents;
     833             : 
     834             :         /**
     835             :          * @brief A stack of transformation matrices used for model intersection math
     836             :          */
     837             :         static std::stack<matrix4> matrixstack;
     838             :         static float sizescale;
     839             : 
     840             :     private:
     841             :         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;
     842             : 
     843             :         static bool enablecullface, enabledepthoffset;
     844             :         static vec4<float> colorscale;
     845             :         static GLuint lastvbuf, lasttcbuf, lastxbuf, lastbbuf, lastebuf;
     846             :         static const Texture *lasttex,
     847             :                              *lastdecal,
     848             :                              *lastmasks,
     849             :                              *lastnormalmap;
     850             : };
     851             : 
     852             : /* modelloader
     853             :  *
     854             :  * modelloader is a template for a wrapper to load a model into a model/animmodel
     855             :  * object from a transactional format, it is intended to be a child template class
     856             :  * of an animmodel derivative (the BASE template parameter)
     857             :  *
     858             :  * skelloader is a specialization of this class which uses modelloader to load
     859             :  * a skeletal model
     860             :  *
     861             :  */
     862             : template<class MDL, class BASE>
     863             : struct modelloader : BASE
     864             : {
     865             :     static MDL *loading;
     866             :     static std::string dir;
     867             : 
     868          19 :     modelloader(std::string name) : BASE(name)
     869             :     {
     870          19 :     }
     871             : 
     872           2 :     static bool cananimate()
     873             :     {
     874           2 :         return true;
     875             :     }
     876             : 
     877           6 :     static bool multiparted()
     878             :     {
     879           6 :         return true;
     880             :     }
     881             : 
     882           3 :     static bool multimeshed()
     883             :     {
     884           3 :         return true;
     885             :     }
     886             : 
     887          17 :     void startload() override final
     888             :     {
     889          17 :         loading = static_cast<MDL *>(this);
     890          17 :     }
     891             : 
     892          13 :     void endload() override final
     893             :     {
     894          13 :         loading = nullptr;
     895          13 :     }
     896             : 
     897           6 :     bool loadconfig(const std::string &mdlname) override final
     898             :     {
     899           6 :         dir.clear();
     900           6 :         dir.append(modelpath).append(mdlname);
     901           6 :         std::string cfgname;
     902           6 :         cfgname.append(modelpath).append(mdlname).append("/").append(MDL::formatname()).append(".cfg");
     903             : 
     904           6 :         identflags &= ~Idf_Persist;
     905           6 :         bool success = execfile(cfgname.c_str(), false);
     906           6 :         identflags |= Idf_Persist;
     907           6 :         return success;
     908           6 :     }
     909             : };
     910             : 
     911             : template<class MDL, class BASE>
     912             : MDL *modelloader<MDL, BASE>::loading = nullptr;
     913             : 
     914             : template<class MDL, class BASE>
     915             : std::string modelloader<MDL, BASE>::dir = {""}; // crashes clang if "" is used here
     916             : 
     917             : /* modelcommands
     918             :  *
     919             :  * this template class adds a series of commands to the cubescript binding
     920             :  * adaptable to a specific model type
     921             :  *
     922             :  * this template class generates unique command names for each separate model type
     923             :  * such as objcolor for obj, or md5color for md5 models; they are static and the
     924             :  * same for any given MDL template parameter
     925             :  *
     926             :  * the intended MDL template parameter is one of the model formats (md5, obj, etc)
     927             :  */
     928             : template<class MDL>
     929             : struct modelcommands
     930             : {
     931             :     typedef class MDL::part part;
     932             :     typedef class MDL::skin skin;
     933             : 
     934          69 :     static bool checkmdl()
     935             :     {
     936          69 :         if(!MDL::loading)
     937             :         {
     938          69 :             conoutf(Console_Error, "not loading a model");
     939          69 :             return false;
     940             :         }
     941             :         else
     942             :         {
     943           0 :             return true;
     944             :         }
     945             :     }
     946             : 
     947           3 :     static void mdlcullface(const int *cullface)
     948             :     {
     949           3 :         if(!checkmdl())
     950             :         {
     951           3 :             return;
     952             :         }
     953           0 :         MDL::loading->setcullface(*cullface);
     954             :     }
     955             : 
     956           3 :     static void mdlcolor(const float *r, const float *g, const float *b)
     957             :     {
     958           3 :         if(!checkmdl())
     959             :         {
     960           3 :             return;
     961             :         }
     962           0 :         MDL::loading->setcolor(vec(*r, *g, *b));
     963             :     }
     964             : 
     965           3 :     static void mdlcollide(const int *collide)
     966             :     {
     967           3 :         if(!checkmdl())
     968             :         {
     969           3 :             return;
     970             :         }
     971           0 :         MDL::loading->collide = *collide!=0 ? (MDL::loading->collide ? MDL::loading->collide : Collide_OrientedBoundingBox) : Collide_None;
     972             :     }
     973             : 
     974           3 :     static void mdlellipsecollide(const int *collide)
     975             :     {
     976           3 :         if(!checkmdl())
     977             :         {
     978           3 :             return;
     979             :         }
     980           0 :         MDL::loading->collide = *collide!=0 ? Collide_Ellipse : Collide_None;
     981             :     }
     982             : 
     983           3 :     static void mdltricollide(const char *collide)
     984             :     {
     985           3 :         if(!checkmdl())
     986             :         {
     987           3 :             return;
     988             :         }
     989           0 :         MDL::loading->collidemodel.clear();
     990           0 :         char *end = nullptr;
     991           0 :         int val = std::strtol(collide, &end, 0);
     992           0 :         if(*end)
     993             :         {
     994           0 :             val = 1;
     995           0 :             MDL::loading->collidemodel = std::string(collide);
     996             :         }
     997           0 :         MDL::loading->collide = val ? Collide_TRI : Collide_None;
     998             :     }
     999             : 
    1000           3 :     static void mdlspec(const float *percent)
    1001             :     {
    1002           3 :         if(!checkmdl())
    1003             :         {
    1004           3 :             return;
    1005             :         }
    1006           0 :         float spec = *percent > 0 ? *percent/100.0f : 0.0f;
    1007           0 :         MDL::loading->setspec(spec);
    1008             :     }
    1009             : 
    1010           3 :     static void mdlgloss(const int *gloss)
    1011             :     {
    1012           3 :         if(!checkmdl())
    1013             :         {
    1014           3 :             return;
    1015             :         }
    1016           0 :         MDL::loading->setgloss(std::clamp(*gloss, 0, 2));
    1017             :     }
    1018             : 
    1019           3 :     static void mdlalphatest(const float *cutoff)
    1020             :     {
    1021           3 :         if(!checkmdl())
    1022             :         {
    1023           3 :             return;
    1024             :         }
    1025           0 :         MDL::loading->setalphatest(std::max(0.0f, std::min(1.0f, *cutoff)));
    1026             :     }
    1027             : 
    1028           3 :     static void mdldepthoffset(const int *offset)
    1029             :     {
    1030           3 :         if(!checkmdl())
    1031             :         {
    1032           3 :             return;
    1033             :         }
    1034           0 :         MDL::loading->depthoffset = *offset!=0;
    1035             :     }
    1036             : 
    1037           3 :     static void mdlglow(const float *percent, const float *delta, const float *pulse)
    1038             :     {
    1039           3 :         if(!checkmdl())
    1040             :         {
    1041           3 :             return;
    1042             :         }
    1043           0 :         float glow = *percent > 0 ? *percent/100.0f : 0.0f,
    1044           0 :               glowdelta = *delta/100.0f,
    1045           0 :               glowpulse = *pulse > 0 ? *pulse/1000.0f : 0;
    1046           0 :         glowdelta -= glow;
    1047           0 :         MDL::loading->setglow(glow, glowdelta, glowpulse);
    1048             :     }
    1049             : 
    1050           3 :     static void mdlfullbright(const float *fullbright)
    1051             :     {
    1052           3 :         if(!checkmdl())
    1053             :         {
    1054           3 :             return;
    1055             :         }
    1056           0 :         MDL::loading->setfullbright(*fullbright);
    1057             :     }
    1058             : 
    1059             : 
    1060           3 :     static void mdlshader(const char *shader)
    1061             :     {
    1062           3 :         if(!checkmdl())
    1063             :         {
    1064           3 :             return;
    1065             :         }
    1066           0 :         MDL::loading->setshader(lookupshaderbyname(shader));
    1067             :     }
    1068             : 
    1069             :     //assigns a new spin speed in three euler angles for the model object currently being loaded
    1070           3 :     static void mdlspin(const float *yaw, const float *pitch, const float *roll)
    1071             :     {
    1072           3 :         if(!checkmdl())
    1073             :         {
    1074           3 :             return;
    1075             :         }
    1076           0 :         MDL::loading->settransformation(std::nullopt, vec(*yaw, *pitch, *roll), std::nullopt, std::nullopt);
    1077             :     }
    1078             : 
    1079             :     //assigns a new scale factor in % for the model object currently being loaded
    1080           3 :     static void mdlscale(const float *percent)
    1081             :     {
    1082           3 :         if(!checkmdl())
    1083             :         {
    1084           3 :             return;
    1085             :         }
    1086           0 :         float scale = *percent > 0 ? *percent/100.0f : 1.0f;
    1087           0 :         MDL::loading->settransformation(std::nullopt, std::nullopt, std::nullopt, scale);
    1088             :     }
    1089             : 
    1090             :     //assigns translation in x,y,z in cube units for the model object currently being loaded
    1091           3 :     static void mdltrans(const float *x, const float *y, const float *z)
    1092             :     {
    1093           3 :         if(!checkmdl())
    1094             :         {
    1095           3 :             return;
    1096             :         }
    1097           0 :         MDL::loading->settransformation(vec(*x, *y, *z), std::nullopt, std::nullopt, std::nullopt);
    1098             :     }
    1099             : 
    1100             :     //assigns angle to the offsetyaw field of the model object currently being loaded
    1101           3 :     static void mdlyaw(const float *angle)
    1102             :     {
    1103           3 :         if(!checkmdl())
    1104             :         {
    1105           3 :             return;
    1106             :         }
    1107           0 :         MDL::loading->orientation.x = *angle;
    1108             :     }
    1109             : 
    1110             : 
    1111             :     //assigns angle to the offsetpitch field of the model object currently being loaded
    1112           3 :     static void mdlpitch(const float *angle)
    1113             :     {
    1114           3 :         if(!checkmdl())
    1115             :         {
    1116           3 :             return;
    1117             :         }
    1118           0 :         MDL::loading->orientation.y = *angle;
    1119             :     }
    1120             : 
    1121             :     //assigns angle to the offsetroll field of the model object currently being loaded
    1122           3 :     static void mdlroll(const float *angle)
    1123             :     {
    1124           3 :         if(!checkmdl())
    1125             :         {
    1126           3 :             return;
    1127             :         }
    1128           0 :         MDL::loading->orientation.z = *angle;
    1129             :     }
    1130             : 
    1131             :     //assigns shadow to the shadow field of the model object currently being loaded
    1132           3 :     static void mdlshadow(const int *shadow)
    1133             :     {
    1134           3 :         if(!checkmdl())
    1135             :         {
    1136           3 :             return;
    1137             :         }
    1138           0 :         MDL::loading->shadow = *shadow!=0;
    1139             :     }
    1140             : 
    1141             :     //assigns alphashadow to the alphashadow field of the model object currently being loaded
    1142           3 :     static void mdlalphashadow(const int *alphashadow)
    1143             :     {
    1144           3 :         if(!checkmdl())
    1145             :         {
    1146           3 :             return;
    1147             :         }
    1148           0 :         MDL::loading->alphashadow = *alphashadow!=0;
    1149             :     }
    1150             : 
    1151             :     //assigns rad, h, eyeheight to the fields of the model object currently being loaded
    1152           3 :     static void mdlbb(const float *rad, const float *h, const float *eyeheight)
    1153             :     {
    1154           3 :         if(!checkmdl())
    1155             :         {
    1156           3 :             return;
    1157             :         }
    1158           0 :         MDL::loading->collidexyradius = *rad;
    1159           0 :         MDL::loading->collideheight = *h;
    1160           0 :         MDL::loading->eyeheight = *eyeheight;
    1161             :     }
    1162             : 
    1163           3 :     static void mdlextendbb(const float *x, const float *y, const float *z)
    1164             :     {
    1165           3 :         if(!checkmdl())
    1166             :         {
    1167           3 :             return;
    1168             :         }
    1169           0 :         MDL::loading->bbextend = vec(*x, *y, *z);
    1170             :     }
    1171             : 
    1172             :     /* mdlname
    1173             :      *
    1174             :      * returns the name of the model currently loaded [most recently]
    1175             :      */
    1176           3 :     static void mdlname()
    1177             :     {
    1178           3 :         if(!checkmdl())
    1179             :         {
    1180           3 :             return;
    1181             :         }
    1182           0 :         result(MDL::loading->modelname().c_str());
    1183             :     }
    1184             : 
    1185          14 :     static void setdir(const char *name)
    1186             :     {
    1187          14 :         if(!MDL::loading)
    1188             :         {
    1189           3 :             conoutf("not loading an %s", MDL::formatname());
    1190           3 :             return;
    1191             :         }
    1192          11 :         MDL::dir.clear();
    1193          11 :         MDL::dir.append(modelpath).append(name);
    1194             :     }
    1195             : 
    1196             :     /**
    1197             :      * @brief Returns an iterator vector of meshes with the given name
    1198             :      *
    1199             :      * Returns a vector of MDL::loading->parts.back()'s meshgroup's mesh vector
    1200             :      * iterators where those iterator's contents' name field compares equal to the
    1201             :      * passed string. If the wildcard "*" is passed as `meshname` then all elements
    1202             :      * will be added regardless of name. If no such mesh vector exists (or there is
    1203             :      * no loading model) then an empty vector is returned.
    1204             :      *
    1205             :      * @param meshname the mesh name to select from the mesh vector
    1206             :      *
    1207             :      * @return vector of iterators corresponding to meshes with the given name
    1208             :      */
    1209           3 :     static std::vector<std::vector<animmodel::Mesh *>::const_iterator> getmeshes(std::string_view meshname)
    1210             :     {
    1211           3 :         std::vector<std::vector<animmodel::Mesh *>::const_iterator> meshlist;
    1212           3 :         if(!MDL::loading || MDL::loading->parts.empty())
    1213             :         {
    1214           3 :             conoutf("not loading an %s", MDL::formatname());
    1215           3 :             return meshlist; //empty vector
    1216             :         }
    1217           0 :         const part &mdl = *MDL::loading->parts.back();
    1218           0 :         if(!mdl.meshes)
    1219             :         {
    1220           0 :             return meshlist; //empty vector
    1221             :         }
    1222           0 :         meshlist = mdl.meshes->getmeshes(meshname);
    1223           0 :         return meshlist;
    1224           0 :     }
    1225             : 
    1226             :     /**
    1227             :      * @brief Returns an iterator vector of skins associated with the given name
    1228             :      *
    1229             :      * Returns a vector of MDL::loading->parts.back()'s meshgroup's skin vector
    1230             :      * iterators where those iterator's contents' name field compares equal to the
    1231             :      * passed string. If the wildcard "*" is passed as `meshname` then all elements
    1232             :      * will be added regardless of name. If no such mesh vector exists (or there is
    1233             :      * no loading model) then an empty vector is returned.
    1234             :      *
    1235             :      * @param meshname the mesh name to select from the skin vector
    1236             :      *
    1237             :      * @return vector of iterators corresponding to skins with the given name
    1238             :      */
    1239          44 :     static std::vector<std::vector<animmodel::skin>::iterator> getskins(std::string_view meshname)
    1240             :     {
    1241          44 :         std::vector<std::vector<animmodel::skin>::iterator> skinlist;
    1242          44 :         if(!MDL::loading || MDL::loading->parts.empty())
    1243             :         {
    1244          36 :             conoutf("not loading an %s", MDL::formatname());
    1245          36 :             return skinlist;
    1246             :         }
    1247           8 :         part &mdl = *MDL::loading->parts.back();
    1248           8 :         if(!mdl.meshes)
    1249             :         {
    1250           0 :             return skinlist;
    1251             :         }
    1252           8 :         std::vector<size_t> skinindices = mdl.meshes->getskins(meshname);
    1253          16 :         for(size_t i : skinindices)
    1254             :         {
    1255           8 :             skinlist.push_back(mdl.skins.begin() + i);
    1256             :         }
    1257           8 :         return skinlist;
    1258           8 :     }
    1259             : 
    1260           6 :     static void setskin(const char *meshname, const char *tex, const char *masks)
    1261             :     {
    1262           6 :         auto skinlist = getskins(meshname);
    1263           9 :         for(auto s : skinlist)
    1264             :         {
    1265           3 :             (*s).tex = textureload(makerelpath(MDL::dir.c_str(), tex), 0, true, false);
    1266           3 :             if(*masks)
    1267             :             {
    1268           3 :                 (*s).masks = textureload(makerelpath(MDL::dir.c_str(), masks), 0, true, false);
    1269             :             }
    1270             :         }
    1271           6 :     }
    1272             : 
    1273           3 :     static void setspec(const char *meshname, const float *percent)
    1274             :     {
    1275           3 :         float spec = *percent > 0 ? *percent/100.0f : 0.0f;
    1276           3 :         auto skinlist = getskins(meshname);
    1277           3 :         for(auto s : skinlist)
    1278             :         {
    1279           0 :             (*s).spec = spec;
    1280             :         }
    1281           3 :     }
    1282             : 
    1283           3 :     static void setgloss(const char *meshname, const int *gloss)
    1284             :     {
    1285           3 :         auto skinlist = getskins(meshname);
    1286           3 :         for(auto s : skinlist)
    1287             :         {
    1288           0 :             (*s).gloss = std::clamp(*gloss, 0, 2);
    1289             :         }
    1290           3 :     }
    1291             : 
    1292           3 :     static void setglow(const char *meshname, const float *percent, const float *delta, const float *pulse)
    1293             :     {
    1294           3 :         float glow = *percent > 0 ? *percent/100.0f : 0.0f,
    1295           3 :               glowdelta = *delta/100.0f,
    1296           3 :               glowpulse = *pulse > 0 ? *pulse/1000.0f : 0;
    1297           3 :         glowdelta -= glow;
    1298           3 :         auto skinlist = getskins(meshname);
    1299           3 :         for(auto s : skinlist)
    1300             :         {
    1301           0 :             (*s).glow = glow;
    1302           0 :             (*s).glowdelta = glowdelta;
    1303           0 :             (*s).glowpulse = glowpulse;
    1304             :         }
    1305           3 :     }
    1306             : 
    1307           3 :     static void setalphatest(const char *meshname, const float *cutoff)
    1308             :     {
    1309           3 :         auto skinlist = getskins(meshname);
    1310           3 :         for(auto s : skinlist)
    1311             :         {
    1312           0 :             (*s).alphatest = std::max(0.0f, std::min(1.0f, *cutoff));
    1313             :         }
    1314           3 :     }
    1315             : 
    1316           3 :     static void setcullface(const char *meshname, const int *cullface)
    1317             :     {
    1318           3 :         auto skinlist = getskins(meshname);
    1319           3 :         for(auto s : skinlist)
    1320             :         {
    1321           0 :             (*s).cullface = *cullface;
    1322             :         }
    1323           3 :     }
    1324             : 
    1325           3 :     static void setcolor(const char *meshname, const float *r, const float *g, const float *b)
    1326             :     {
    1327           3 :         auto skinlist = getskins(meshname);
    1328           3 :         for(auto s : skinlist)
    1329             :         {
    1330           0 :             (*s).color = vec(*r, *g, *b);
    1331             :         }
    1332           3 :     }
    1333             : 
    1334           4 :     static void setbumpmap(const char *meshname, const char *normalmapfile)
    1335             :     {
    1336           4 :         Texture *normalmaptex = textureload(makerelpath(MDL::dir.c_str(), normalmapfile), 0, true, false);
    1337           4 :         auto skinlist = getskins(meshname);
    1338           5 :         for(auto s : skinlist)
    1339             :         {
    1340           1 :             (*s).normalmap = normalmaptex;
    1341             :         }
    1342           4 :     }
    1343             : 
    1344           4 :     static void setdecal(const char *meshname, const char *decal)
    1345             :     {
    1346           4 :         auto skinlist = getskins(meshname);
    1347           5 :         for(auto s : skinlist)
    1348             :         {
    1349           1 :             (*s).decal = textureload(makerelpath(MDL::dir.c_str(), decal), 0, true, false);
    1350             :         }
    1351           4 :     }
    1352             : 
    1353           3 :     static void setfullbright(const char *meshname, const float *fullbright)
    1354             :     {
    1355           3 :         auto skinlist = getskins(meshname);
    1356           3 :         for(auto s : skinlist)
    1357             :         {
    1358           0 :             (*s).fullbright = *fullbright;
    1359             :         }
    1360           3 :     }
    1361             : 
    1362           3 :     static void setshader(const char *meshname, const char *shader)
    1363             :     {
    1364           3 :         auto skinlist = getskins(meshname);
    1365           3 :         for(auto s : skinlist)
    1366             :         {
    1367           0 :             (*s).shader = lookupshaderbyname(shader);
    1368             :         }
    1369           3 :     }
    1370             : 
    1371           3 :     static void setscroll(const char *meshname, const float *scrollu, const float *scrollv)
    1372             :     {
    1373           3 :         auto skinlist = getskins(meshname);
    1374           3 :         for(auto s : skinlist)
    1375             :         {
    1376           0 :             (*s).scrollu = *scrollu;
    1377           0 :             (*s).scrollv = *scrollv;
    1378             :         }
    1379           3 :     }
    1380             : 
    1381           3 :     static void setnoclip(const char *meshname, const int *noclip)
    1382             :     {
    1383           6 :         auto meshlist = getmeshes(std::string(meshname));
    1384           3 :         if(meshlist.empty())
    1385             :         {
    1386           3 :             return;
    1387             :         }
    1388           0 :         for(auto &i : meshlist)
    1389             :         {
    1390           0 :             (*i)->noclip = *noclip!=0;
    1391             :         }
    1392           3 :     }
    1393             : 
    1394           0 :     static void settricollide(const char *meshname)
    1395             :     {
    1396           0 :         bool init = true;
    1397           0 :         auto meshlist = getmeshes(std::string(meshname));
    1398           0 :         if(!meshlist.empty())
    1399             :         {
    1400           0 :             return;
    1401             :         }
    1402             :         else
    1403             :         {
    1404           0 :             for(auto &i : meshlist)
    1405             :             {
    1406           0 :                 if(!(*i)->cancollide)
    1407             :                 {
    1408           0 :                     init = false;
    1409             :                 }
    1410             :             }
    1411           0 :             if(init)
    1412             :             {
    1413           0 :                 for(auto &i : meshlist)
    1414             :                 {
    1415           0 :                     (*i)->cancollide = false;
    1416             :                 }
    1417             :             }
    1418           0 :             for(auto &i : meshlist)
    1419             :             {
    1420           0 :                 (*i)->cancollide = true;
    1421           0 :                 (*i)->canrender = false;
    1422             :             }
    1423             :         }
    1424           0 :     }
    1425             : 
    1426           3 :     static void setlink(const int *parent, const int *child, const char *tagname, const float *x, const float *y, const float *z)
    1427             :     {
    1428           3 :         if(!MDL::loading)
    1429             :         {
    1430           3 :             conoutf("not loading an %s", MDL::formatname());
    1431           3 :             return;
    1432             :         }
    1433           0 :         if(!(static_cast<int>(MDL::loading->parts.size()) > *parent) || !(static_cast<int>(MDL::loading->parts.size()) > *child))
    1434             :         {
    1435           0 :             conoutf("no models loaded to link");
    1436           0 :             return;
    1437             :         }
    1438           0 :         if(!MDL::loading->parts[*parent]->link(MDL::loading->parts[*child], tagname, vec(*x, *y, *z)))
    1439             :         {
    1440           0 :             conoutf("could not link model %s", MDL::loading->modelname().c_str());
    1441             :         }
    1442             :     }
    1443             : 
    1444             :     template<class F>
    1445         152 :     void modelcommand(F *fun, std::string_view suffix, std::string_view args)
    1446             :     {
    1447         152 :         std::string name;
    1448         152 :         name.append(MDL::formatname()).append(suffix);
    1449         152 :         addcommand(newstring(name.c_str()), (identfun)fun, args.data());
    1450         152 :     }
    1451             : 
    1452           3 :     modelcommands()
    1453             :     {
    1454           3 :         modelcommand(setdir, "dir", "s");//<fmt>dir [name]
    1455           3 :         if(MDL::multimeshed())
    1456             :         {
    1457           3 :             modelcommand(mdlcullface, "cullface", "i");
    1458           3 :             modelcommand(mdlcolor, "color", "fff");
    1459           3 :             modelcommand(mdlcollide, "collide", "i");
    1460           3 :             modelcommand(mdlellipsecollide, "ellipsecollide", "i");
    1461           3 :             modelcommand(mdltricollide, "tricollide", "s");
    1462           3 :             modelcommand(mdlspec, "spec", "f");
    1463           3 :             modelcommand(mdlgloss, "gloss", "i");
    1464           3 :             modelcommand(mdlalphatest, "alphatest", "f");
    1465           3 :             modelcommand(mdldepthoffset, "depthoffset", "i");
    1466           3 :             modelcommand(mdlglow, "glow", "fff");
    1467           3 :             modelcommand(mdlfullbright, "fullbright", "f");
    1468           3 :             modelcommand(mdlshader, "shader", "s");
    1469           3 :             modelcommand(mdlspin, "spin", "fff");
    1470           3 :             modelcommand(mdlscale, "scale", "f");
    1471           3 :             modelcommand(mdltrans, "trans", "fff");
    1472           3 :             modelcommand(mdlyaw, "yaw", "f");
    1473           3 :             modelcommand(mdlpitch, "pitch", "f");
    1474           3 :             modelcommand(mdlroll, "roll", "f");
    1475           3 :             modelcommand(mdlshadow, "shadow", "i");
    1476           3 :             modelcommand(mdlalphashadow, "alphashadow", "i");
    1477           3 :             modelcommand(mdlbb, "bb", "fff");
    1478           3 :             modelcommand(mdlextendbb, "extendbb", "fff");
    1479           3 :             modelcommand(mdlname, "name", "");
    1480             : 
    1481           3 :             modelcommand(setskin, "skin", "sss");               //<fmt>skin [meshname] [tex] [masks]
    1482           3 :             modelcommand(setspec, "texspec", "sf");             //<fmt>texspec [mesh] [scale]
    1483           3 :             modelcommand(setgloss, "texgloss", "si");           //<fmt>texgloss [mesh] [type] type ranges 0..2
    1484           3 :             modelcommand(setglow, "texglow", "sfff");           //<fmt>texglow [mesh] [pct] [del] [pulse]
    1485           3 :             modelcommand(setalphatest, "meshalphatest", "sf");  //<fmt>meshalphatest [mesh] [cutoff]
    1486           3 :             modelcommand(setcullface, "meshcullface", "si");    //<fmt>cullface [mesh] [cullface]
    1487           3 :             modelcommand(setcolor, "meshcolor", "sfff");        //<fmt>meshcolor [mesh] [r] [g] [b]
    1488           3 :             modelcommand(setbumpmap, "bumpmap", "ss");          //<fmt>bumpmap [mesh] [tex]
    1489           3 :             modelcommand(setdecal, "decal", "ss");              //<fmt>decal [mesh] [tex]
    1490           3 :             modelcommand(setfullbright, "meshfullbright", "sf");//<fmt>meshfullbright [mesh] [bright]
    1491           3 :             modelcommand(setshader, "meshshader", "ss");        //<fmt>meshshader [mesh] [shader]
    1492           3 :             modelcommand(setscroll, "scroll", "sff");           //<fmt>scroll [mesh] [x] [y]
    1493           3 :             modelcommand(setnoclip, "noclip", "si");            //<fmt>noclip [mesh] [bool]
    1494           3 :             modelcommand(settricollide, "tricollide", "s");     //<fmt>settricollide [mesh]
    1495             :         }
    1496           3 :         if(MDL::multiparted())
    1497             :         {
    1498           3 :             modelcommand(setlink, "link", "iisfff");//<mdl>link [parent] [child] [tag] [x] [y] [z]
    1499             :         }
    1500           3 :     }
    1501             : };
    1502             : 
    1503             : #endif

Generated by: LCOV version 1.14