LCOV - code coverage report
Current view: top level - engine/model - animmodel.h (source / functions) Coverage Total Hit
Test: Libprimis Test Coverage Lines: 67.7 % 471 319
Test Date: 2025-02-18 06:21:28 Functions: 89.7 % 234 210

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

Generated by: LCOV version 2.0-1