LCOV - code coverage report
Current view: top level - engine/model - animmodel.h (source / functions) Coverage Total Hit
Test: Libprimis Test Coverage Lines: 68.4 % 459 314
Test Date: 2026-01-12 07:52:59 Functions: 89.7 % 232 208

            Line data    Source code
       1              : #ifndef ANIMMODEL_H_
       2              : #define ANIMMODEL_H_
       3              : 
       4              : extern int testtags, debugcolmesh;
       5              : 
       6              : const std::string modelpath = "media/model/";
       7              : 
       8              : /**
       9              :  * @brief Animated model object/
      10              :  *
      11              :  * Generic class for an animated model object, derived from the very
      12              :  * general model structure
      13              :  *
      14              :  * animmodel provides functionality to implement an animated model consisting of
      15              :  * multiple mesh groups which are treated as a single model
      16              :  *
      17              :  * animmodel is extended by skelmodel to allow multipart objects to be rigged by
      18              :  * a skeleton/bone type animation methodology
      19              :  */
      20              : class animmodel : public model
      21              : {
      22              :     public:
      23              : 
      24              :         struct AnimPos final
      25              :         {
      26              :             int anim, fr1, fr2;
      27              :             float t;
      28              : 
      29              :             void setframes(const animinfo &info);
      30              : 
      31              :             bool operator==(const AnimPos &a) const;
      32              :             bool operator!=(const AnimPos &a) const;
      33              :         };
      34              : 
      35              :         class part;
      36              : 
      37              :         struct AnimState final
      38              :         {
      39              :             const part *owner;
      40              :             AnimPos cur, prev;
      41              :             float interp;
      42              : 
      43              :             bool operator==(const AnimState &a) const;
      44              :             bool operator!=(const AnimState &a) const;
      45              :         };
      46              : 
      47              :         class Mesh;
      48              : 
      49              :         struct shaderparams
      50              :         {
      51              :             float spec, gloss, glow, glowdelta, glowpulse, fullbright, scrollu, scrollv, alphatest;
      52              :             vec color;
      53              : 
      54              : 
      55              :             bool operator==(const animmodel::shaderparams &y) const;
      56              :             shaderparams();
      57              :         };
      58              : 
      59              :         //An object used to store texture data for a mesh, contained inside a `part` object.
      60              :         //A `meshgroup` object is paired with a `skin` object to provide texture & geometry
      61              :         //data for a mesh
      62              :         class skin final : public shaderparams
      63              :         {
      64              :             public:
      65              :                 Texture *tex, *decal;
      66              :                 const Texture *masks, *normalmap;
      67              :                 Shader *shader;
      68              :                 int cullface;
      69              : 
      70           20 :                 skin(const part *owner, Texture *tex, const Texture *masks) :
      71           20 :                     tex(tex),
      72           20 :                     decal(nullptr),
      73           20 :                     masks(masks),
      74           20 :                     normalmap(nullptr),
      75           20 :                     shader(nullptr),
      76           20 :                     cullface(1),
      77           20 :                     owner(owner),
      78           20 :                     rsmshader(nullptr),
      79           20 :                     key(nullptr)
      80              :                 {
      81           20 :                 }
      82              : 
      83              :                 bool alphatested() const;
      84              :                 void setkey();
      85              :                 void cleanup();
      86              :                 void preloadBIH() const;
      87              :                 void preloadshader();
      88              :                 void bind(Mesh &b, const AnimState *as, bool usegpuskel = false, int vweights = 0);
      89              :                 static void invalidateshaderparams();
      90              : 
      91              :             private:
      92              :                 const part *owner;
      93              :                 Shader *rsmshader;
      94              :                 class ShaderParamsKey
      95              :                 {
      96              :                     public:
      97              :                         static std::unordered_map<shaderparams, ShaderParamsKey> keys;
      98              : 
      99            1 :                         ShaderParamsKey() : version(-1) {}
     100              : 
     101              :                         bool checkversion();
     102              : 
     103            0 :                         static void invalidate()
     104              :                         {
     105            0 :                             firstversion = lastversion;
     106            0 :                         }
     107              :                     private:
     108              :                         static int firstversion, lastversion;
     109              : 
     110              :                         int version;
     111              :                 };
     112              :                 ShaderParamsKey *key;
     113              : 
     114              :                 bool masked() const;
     115              :                 bool bumpmapped() const;
     116              :                 bool decaled() const;
     117              :                 void setshaderparams(const AnimState *as, bool skinned = true);
     118              :                 Shader *loadshader();
     119              :                 void setshader(Mesh &m, bool usegpuskel, int vweights);
     120              : 
     121              :         };
     122              : 
     123              :         class meshgroup;
     124              : 
     125              :         //An object used to store a single geometry mesh inside a `meshgroup` object.
     126              :         //This object is to be extended to contain the actual geometry of a type of
     127              :         //model, such as `skelmesh` or `vertmesh`. In its base class form, the object
     128              :         //contains no geometry data.
     129              :         class Mesh
     130              :         {
     131              :             public:
     132              :                 std::string name;
     133              :                 bool cancollide, canrender, noclip;
     134              : 
     135            3 :                 virtual ~Mesh()
     136            3 :                 {
     137            3 :                 }
     138              : 
     139              :                 virtual void calcbb(vec &bbmin, vec &bbmax, const matrix4x3 &m) const = 0;
     140              : 
     141              :                 virtual void genBIH(BIH::mesh &m) const = 0;
     142              : 
     143              :                 void genBIH(const skin &s, std::vector<BIH::mesh> &bih, const matrix4x3 &t) const;
     144              : 
     145              :                 virtual void genshadowmesh(std::vector<triangle> &tris, const matrix4x3 &m) const = 0;
     146              : 
     147            0 :                 virtual void setshader(Shader *s, bool, int, int row = 0) const
     148              :                 {
     149            0 :                     if(row)
     150              :                     {
     151            0 :                         s->setvariant(0, row);
     152              :                     }
     153              :                     else
     154              :                     {
     155            0 :                         s->set();
     156              :                     }
     157            0 :                 }
     158              : 
     159              :             protected:
     160              :                 meshgroup *group;
     161              : 
     162            0 :                 Mesh() : cancollide(true), canrender(true), noclip(false), group(nullptr)
     163              :                 {
     164            0 :                 }
     165              : 
     166            4 :                 Mesh(std::string_view name, meshgroup *m) :
     167            4 :                     name(name),
     168            4 :                     cancollide(true),
     169            4 :                     canrender(true),
     170            4 :                     noclip(false),
     171            4 :                     group(m)
     172              :                 {
     173            4 :                 }
     174              : 
     175              :                 template<class T>
     176            0 :                 static void smoothnorms(typename T::vert *verts, int numverts, const typename T::tri *tris, int numtris, float limit, bool areaweight)
     177              :                 {
     178            0 :                     if(!numverts)
     179              :                     {
     180            0 :                         return;
     181              :                     }
     182            0 :                     smoothdata *smooth = new smoothdata[numverts];
     183            0 :                     std::unordered_map<vec, int> share;
     184            0 :                     for(int i = 0; i < numverts; ++i)
     185              :                     {
     186            0 :                         const typename T::vert &v = verts[i];
     187            0 :                         const auto itr = share.find(v.pos);
     188            0 :                         if(itr == share.end())
     189              :                         {
     190            0 :                             share[v.pos] = i;
     191              :                         }
     192              :                         else
     193              :                         {
     194            0 :                             smooth[i].next = (*itr).second;
     195            0 :                             (*itr).second = i;
     196              :                         }
     197              :                     }
     198            0 :                     for(int i = 0; i < numtris; ++i)
     199              :                     {
     200            0 :                         const typename T::tri &t = tris[i];
     201            0 :                         const uint v1 = t.vert[0],
     202            0 :                                    v2 = t.vert[1],
     203            0 :                                    v3 = t.vert[2];
     204            0 :                         vec norm;
     205            0 :                         norm.cross(verts[v1].pos, verts[v2].pos, verts[v3].pos);
     206            0 :                         if(!areaweight)
     207              :                         {
     208            0 :                             norm.normalize();
     209              :                         }
     210            0 :                         smooth[v1].norm.add(norm);
     211            0 :                         smooth[v2].norm.add(norm);
     212            0 :                         smooth[v3].norm.add(norm);
     213              :                     }
     214            0 :                     for(int i = 0; i < numverts; ++i)
     215              :                     {
     216            0 :                         verts[i].norm = vec(0, 0, 0);
     217              :                     }
     218            0 :                     for(int i = 0; i < numverts; ++i)
     219              :                     {
     220            0 :                         const smoothdata &n = smooth[i];
     221            0 :                         verts[i].norm.add(n.norm);
     222            0 :                         if(n.next >= 0)
     223              :                         {
     224            0 :                             const float vlimit = limit*n.norm.magnitude();
     225            0 :                             for(int j = n.next; j >= 0;)
     226              :                             {
     227            0 :                                 const smoothdata &o = smooth[j];
     228            0 :                                 if(n.norm.dot(o.norm) >= vlimit*o.norm.magnitude())
     229              :                                 {
     230            0 :                                     verts[i].norm.add(o.norm);
     231            0 :                                     verts[j].norm.add(n.norm);
     232              :                                 }
     233            0 :                                 j = o.next;
     234              :                             }
     235              :                         }
     236              :                     }
     237            0 :                     for(int i = 0; i < numverts; ++i)
     238              :                     {
     239            0 :                         verts[i].norm.normalize();
     240              :                     }
     241            0 :                     delete[] smooth;
     242            0 :                 }
     243              : 
     244              :                 /**
     245              :                  * @brief Generates normal data for an array of vert objects.
     246              :                  *
     247              :                  * If there are no verts in the verts array, returns with no changes.
     248              :                  *
     249              :                  * Sets all norm fields in the verts array to the zero vector.
     250              :                  * Accesses three elements in verts using a set of indices saved in the tri object.
     251              :                  *
     252              :                  * Each triangle's normal is the cross product of the three elements' positions, normalized if areaweight == false.
     253              :                  * (If areaweight is true, since the cross product's magnitude encodes its area, we don't want to
     254              :                  * normalize because we want to weight normals by area)
     255              :                  * This normal is added to each of the three vertices accessed (individual vertices can have multiple tris).
     256              :                  *
     257              :                  * Then, all vertices are normalized, creating normalized normals using all triangles sharing each vertex.
     258              :                  *
     259              :                  * @param verts An array of vertices
     260              :                  * @param numverts The size of the vertex array
     261              :                  * @param tris An array of triangles, containing indices in verts
     262              :                  * @param numtris The size of the tris array
     263              :                  * @param areaweight If true, weights normals by area of associated triangle
     264              :                  */
     265              :                 template<class T>
     266            3 :                 static void buildnorms(typename T::vert *verts, int numverts, const typename T::tri *tris, int numtris, bool areaweight)
     267              :                 {
     268            3 :                     if(!numverts)
     269              :                     {
     270            0 :                         return;
     271              :                     }
     272          449 :                     for(int i = 0; i < numverts; ++i)
     273              :                     {
     274          446 :                         verts[i].norm = vec(0, 0, 0);
     275              :                     }
     276          469 :                     for(int i = 0; i < numtris; ++i)
     277              :                     {
     278          466 :                         const typename T::tri &t = tris[i];
     279          466 :                         typename T::vert &v1 = verts[t.vert[0]],
     280          466 :                                         &v2 = verts[t.vert[1]],
     281          466 :                                         &v3 = verts[t.vert[2]];
     282          466 :                         vec norm;
     283          466 :                         norm.cross(v1.pos, v2.pos, v3.pos);
     284          466 :                         if(!areaweight)
     285              :                         {
     286            2 :                             norm.normalize();
     287              :                         }
     288          466 :                         v1.norm.add(norm);
     289          466 :                         v2.norm.add(norm);
     290          466 :                         v3.norm.add(norm);
     291              :                     }
     292          449 :                     for(int i = 0; i < numverts; ++i)
     293              :                     {
     294          446 :                         verts[i].norm.normalize();
     295              :                     }
     296              :                 }
     297              : 
     298              :                 template<class T>
     299            0 :                 static void buildnorms(typename T::vert *verts, int numverts, const typename T::tri *tris, int numtris, bool areaweight, int numframes)
     300              :                 {
     301            0 :                     if(!numverts)
     302              :                     {
     303            0 :                         return;
     304              :                     }
     305            0 :                     for(int i = 0; i < numframes; ++i)
     306              :                     {
     307            0 :                         buildnorms<T>(&verts[i*numverts], numverts, tris, numtris, areaweight);
     308              :                     }
     309              :                 }
     310              : 
     311              :                 static void fixqtangent(quat &q, float bt);
     312              : 
     313              :                 template<class T, class TC>
     314            1 :                 void calctangents(typename T::vert *verts, const TC *tcverts, int numverts, const typename T::tri *tris, int numtris, bool areaweight) const
     315              :                 {
     316          877 :                     vec *tangent = new vec[2*numverts],
     317            1 :                         *bitangent = tangent+numverts;
     318            1 :                     std::memset(static_cast<void *>(tangent), 0, 2*numverts*sizeof(vec));
     319              : 
     320          463 :                     for(int i = 0; i < numtris; ++i)
     321              :                     {
     322          462 :                         const typename T::tri &t = tris[i];
     323          462 :                         const vec &e0 = verts[t.vert[0]].pos;
     324          462 :                         vec e1 = vec(verts[t.vert[1]].pos).sub(e0),
     325          462 :                             e2 = vec(verts[t.vert[2]].pos).sub(e0);
     326              : 
     327          462 :                         const vec2 &tc0 = tcverts[t.vert[0]].tc,
     328          462 :                                    &tc1 = tcverts[t.vert[1]].tc,
     329          462 :                                    &tc2 = tcverts[t.vert[2]].tc;
     330          462 :                         float u1 = tc1.x - tc0.x,
     331          462 :                               v1 = tc1.y - tc0.y,
     332          462 :                               u2 = tc2.x - tc0.x,
     333          462 :                               v2 = tc2.y - tc0.y;
     334          462 :                         vec u(e2), v(e2);
     335          462 :                         u.mul(v1).sub(vec(e1).mul(v2));
     336          462 :                         v.mul(u1).sub(vec(e1).mul(u2));
     337              : 
     338          462 :                         if(vec().cross(e2, e1).dot(vec().cross(v, u)) >= 0)
     339              :                         {
     340           19 :                             u.neg();
     341           19 :                             v.neg();
     342              :                         }
     343              : 
     344          462 :                         if(!areaweight)
     345              :                         {
     346            0 :                             u.normalize();
     347            0 :                             v.normalize();
     348              :                         }
     349              : 
     350         1848 :                         for(int j = 0; j < 3; ++j)
     351              :                         {
     352         1386 :                             tangent[t.vert[j]].sub(u);
     353         1386 :                             bitangent[t.vert[j]].add(v);
     354              :                         }
     355              :                     }
     356          439 :                     for(int i = 0; i < numverts; ++i)
     357              :                     {
     358          438 :                         typename T::vert &v = verts[i];
     359          438 :                         const vec &t = tangent[i],
     360          438 :                                   &bt = bitangent[i];
     361          438 :                         matrix3 m;
     362          438 :                         m.c = v.norm;
     363          438 :                         (m.a = t).project(m.c).normalize();
     364          438 :                         m.b.cross(m.c, m.a);
     365          438 :                         quat q(m);
     366          438 :                         fixqtangent(q, m.b.dot(bt));
     367          438 :                         v.tangent = q;
     368              :                     }
     369            1 :                     delete[] tangent;
     370            1 :                 }
     371              : 
     372              :                 template<class T, class TC>
     373            0 :                 void calctangents(typename T::vert *verts, const TC *tcverts, int numverts, const typename T::tri *tris, int numtris, bool areaweight, int numframes) const
     374              :                 {
     375            0 :                     for(int i = 0; i < numframes; ++i)
     376              :                     {
     377            0 :                         calctangents<T, TC>(&verts[i*numverts], tcverts, numverts, tris, numtris, areaweight);
     378              :                     }
     379            0 :                 }
     380              : 
     381              :             private:
     382              :                 struct smoothdata final
     383              :                 {
     384              :                     vec norm;
     385              :                     int next;
     386              : 
     387            0 :                     smoothdata() : norm(0, 0, 0), next(-1) {}
     388              :                 };
     389              :         };
     390              : 
     391              :         //A group of one or more meshes, which are used by a `part` to represent its contained geometry.
     392              :         //A global (static field) map of meshgroups are kept to cache model geometry; the actual `part`'s
     393              :         //refered mesh group object is kept as a pointer to a member of the static meshgroup map.
     394              :         class meshgroup
     395              :         {
     396              :             public:
     397              :                 std::vector<Mesh *> meshes;
     398              : 
     399              :                 virtual ~meshgroup();
     400              : 
     401              :                 virtual void concattagtransform(int i, const matrix4x3 &m, matrix4x3 &n) const = 0;
     402              :                 virtual std::optional<size_t> findtag(std::string_view name) = 0;
     403              : 
     404              :                 /**
     405              :                  * @brief Returns the number of animation frames this meshgroup contains
     406              :                  *
     407              :                  * For skelmodels, always returns at least 1. May be 0 or greater for vertmodels.
     408              :                  */
     409              :                 virtual int totalframes() const = 0;
     410              :                 virtual void *animkey() = 0;
     411              :                 virtual void cleanup() = 0;
     412              :                 virtual void render(const AnimState *as, float pitch, const vec &axis, const vec &forward, dynent *d, part *p) = 0;
     413              :                 virtual void preload() = 0;
     414              : 
     415              :                 /**
     416              :                  * Returns a list of Mesh iterators corresponding to a given name
     417              :                  * The iterators may be invalidated by other method calls.
     418              :                  */
     419              :                 std::vector<std::vector<animmodel::Mesh *>::const_iterator> getmeshes(std::string_view meshname) const;
     420              :                 /**
     421              :                  * Returns a list of indices corresponding to locations in animmodel::part::skins.
     422              :                  * These indices are invalidated if animmodel::skins is modified after calling.
     423              :                  */
     424              :                 std::vector<size_t> getskins(std::string_view meshname) const;
     425              : 
     426              :                 void calcbb(vec &bbmin, vec &bbmax, const matrix4x3 &t) const;
     427              :                 void genBIH(const std::vector<skin> &skins, std::vector<BIH::mesh> &bih, const matrix4x3 &t) const;
     428              :                 void genshadowmesh(std::vector<triangle> &tris, const matrix4x3 &t) const;
     429              : 
     430              :                 /**
     431              :                  * @brief Returns true if i is a valid index in the list of frames this meshgroup contains
     432              :                  *
     433              :                  * That is, returns true if the index i is at least 0 and is no larger than the last index
     434              :                  * in the frame list for this meshgroup
     435              :                  *
     436              :                  * @param index to query
     437              :                  *
     438              :                  * @return true if the passed index is a valid index
     439              :                  */
     440              :                 bool hasframe(int i) const;
     441              : 
     442              :                 /**
     443              :                  * @brief Returns true if the range i...n is a valid index of frames this meshgroup contains
     444              :                  *
     445              :                  * Returns true if the indices i through n begins with i at 0 or greater and i+n no larger
     446              :                  * than the last index in the frame list for this meshgroup
     447              :                  *
     448              :                  * @param i the first index to query
     449              :                  * @param n the number of indices thereafter to query
     450              :                  *
     451              :                  * @return true if the passed index range are all valid
     452              :                  */
     453              :                 bool hasframes(int i, int n) const;
     454              : 
     455              :                 /**
     456              :                  * @brief Returns the lesser of n and the number of remaining frames between i and n
     457              :                  *
     458              :                  * Returns n if there are existing frames between i and i+n, and returns the largest
     459              :                  * value that would satisfy this condition if not.
     460              :                  *
     461              :                  * @param i the first index to query
     462              :                  * @param n the number of indices thereafter to query
     463              :                  *
     464              :                  * @return n, or the maximum number of entries after i if n is too large
     465              :                  */
     466              :                 int clipframes(int i, int n) const;
     467              : 
     468              :                 /**
     469              :                  * @brief Returns the name of this meshgroup
     470              :                  *
     471              :                  * This is created by the constructor for the object.
     472              :                  *
     473              :                  * @return the name of the meshgroup
     474              :                  */
     475              :                 const std::string &groupname() const;
     476              : 
     477              :                 /**
     478              :                  * @brief Returns a list of valid renderable meshes contained within this object.
     479              :                  *
     480              :                  * Returns a vector of std::vector<>::iterator objects which point to valid
     481              :                  * elements of the object's mesh list which can be rendered (the relevant flag
     482              :                  * field is true). Alternatively if the global debugcolmesh is enabled, all
     483              :                  * meshes will be returned.
     484              :                  *
     485              :                  * @return a vector of std::vector iterators pointing to renderable meshes
     486              :                  */
     487              :                 std::vector<std::vector<Mesh *>::const_iterator> getrendermeshes() const;
     488              :                 std::vector<std::vector<Mesh *>::iterator> getrendermeshes();
     489              : 
     490              :                 #define LOOP_RENDER_MESHES(type, name, body) do { \
     491              :                     for(uint i = 0; i < meshes.size(); i++) \
     492              :                     { \
     493              :                         type &name = *static_cast<type *>(meshes[i]); \
     494              :                         if(name.canrender || debugcolmesh) \
     495              :                         { \
     496              :                             body; \
     497              :                         } \
     498              :                     } \
     499              :                 } while(0)
     500              : 
     501              :             protected:
     502              :                 meshgroup();
     503              : 
     504              :                 std::string name;
     505              : 
     506              :                 void bindpos(GLuint ebuf, GLuint vbuf, const void *v, int stride, int type, int size);
     507              :                 void bindpos(GLuint ebuf, GLuint vbuf, const vec *v, int stride);
     508              :                 void bindpos(GLuint ebuf, GLuint vbuf, const vec4<half> *v, int stride);
     509              : 
     510              :                 /**
     511              :                  * @brief Binds to TexCoord0 the provided data with a specified stridelength
     512              :                  *
     513              :                  * Binds (via glVertexAttribPointer) the provided array pointed to by v
     514              :                  * with a stride length (offset between attributes) of `stride`.
     515              :                  *
     516              :                  * @param v the array of texture coordinate data to bind
     517              :                  * @param stride the offset between coodinates within the array
     518              :                  */
     519              :                 void bindtc(const void *v, int stride);
     520              :                 void bindtangents(const void *v, int stride);
     521              :                 void bindbones(const void *wv, const void *bv, int stride);
     522              :             //no private-able members
     523              :         };
     524              : 
     525              :         /**
     526              :          * @brief Global map to share mesh froups across models.
     527              :          *
     528              :          * The meshgroups map stores meshgroups which may be shared between different
     529              :          * model instantiations (e.g. skeletal models which have other models attached
     530              :          * as parts).
     531              :          *
     532              :          * The animmodel is comprised of parts which each represent instance-specific
     533              :          * metadata applied to a meshgroup in this map.
     534              :          */
     535              :         static std::unordered_map<std::string, meshgroup *> meshgroups;
     536              : 
     537              :         /**
     538              :          * @brief High level component of a model instance.
     539              :          *
     540              :          * The `part` object is the highest level of organization in a model object.
     541              :          * Each `part` is a logically separate part of an overall model, containing
     542              :          * its own skin(s) (`skin` objects), mesh(es) (`meshgroup` objects), and
     543              :          * model rendering parameters.
     544              :          *
     545              :          * Parts may contain a list of linked parts, which are other `part` objects
     546              :          * logically dependent on another `part`. An example is an object being held
     547              :          * in the hand of another model type, which is a separate mesh.
     548              :          */
     549              :         class part
     550              :         {
     551              :             public:
     552              :                 const animmodel *model;
     553              :                 int index;
     554              :                 meshgroup *meshes; //pointer to a single meshgroup in animmodel::meshgroups
     555              : 
     556              :                 //a pointer to another part object dependent on this part.
     557              :                 //pointed part may be modified in case it is necessary to link/unlink
     558              :                 //the pointed part to other dependent parts
     559              :                 struct linkedpart
     560              :                 {
     561              :                     part *p;
     562              :                     int tag, anim, basetime;
     563              :                     vec translate;
     564              :                     vec *pos; //a pos pointer from a modelattach object, which is set from within game
     565              :                     matrix4 matrix;
     566              : 
     567              :                     linkedpart() : p(nullptr), tag(-1), anim(-1), basetime(0), translate(0, 0, 0), pos(nullptr) {}
     568            0 :                     linkedpart(part *p, int tag, int anim, int basetime, vec translate, vec *post, matrix4 matrix) :
     569            0 :                         p(p), tag(tag), anim(anim), basetime(basetime), translate(translate), pos(post), matrix(matrix) {}
     570              :                 };
     571              :                 std::vector<linkedpart> links;
     572              :                 std::vector<skin> skins;
     573              :                 int numanimparts;
     574              :                 float pitchscale, pitchoffset, pitchmin, pitchmax;
     575              : 
     576              :                 /**
     577              :                  * @brief Creates a part object pointing to the given animmodel
     578              :                  *
     579              :                  * Sets this part's animation interpolation index to the value passed.
     580              :                  *
     581              :                  * @param model pointer to a single animmodel
     582              :                  * @param index animation interpolation index to assign
     583              :                  */
     584              :                 part(const animmodel *model, int index = 0);
     585              :                 virtual ~part();
     586              :                 part(const part& a) = delete;
     587              :                 part &operator=(const part &a) = delete;
     588              : 
     589              :                 /**
     590              :                  * @brief Cleans up the meshgroup and attached skins.
     591              :                  *
     592              :                  * Cleans up the single meshgroup at part::meshes, and cleans up
     593              :                  * every skin in the skin vector. Does not delete the skins nor
     594              :                  * the meshgroup but merely leaves them in a cleaned up state
     595              :                  */
     596              :                 void cleanup();
     597              : 
     598              :                 /**
     599              :                  * @brief Zeros out the pitch fields in this part.
     600              :                  *
     601              :                  * Sets part::pitchscale, part::pitchoffset, part::pitchmin, and part::pitchmax
     602              :                  * to zero. This means that the part after this is called will have zero pitch
     603              :                  * component (rotation).
     604              :                  */
     605              :                 void disablepitch();
     606              : 
     607              :                 /**
     608              :                  * @brief Returns to bbmin and bbmax the bounding area of this model
     609              :                  *
     610              :                  * Returns the rectangular bounding box of the model, given its scale
     611              :                  * and transformation.
     612              :                  *
     613              :                  * @param bbmin the -x-y-z corner of the rectangular bounding box
     614              :                  * @param bbmax the +x+y+z cornder of the rectangular bounding box
     615              :                  * @param m the transformation matrix of the model
     616              :                  * @param modelscale the scale factor for the model
     617              :                  */
     618              :                 void calcbb(vec &bbmin, vec &bbmax, const matrix4x3 &m, float modelscale) const;
     619              : 
     620              :                 /**
     621              :                  * @brief Generates a new BIH for this model and appends it to the back of the passed vector.
     622              :                  *
     623              :                  * Creates a new BIH at the end of the passed vector of BIH meshes and
     624              :                  * sets its parameters to that of this model part with the specified scale
     625              :                  * factor and scale.
     626              :                  *
     627              :                  * @param bih the bounded interval hierarchy for the model
     628              :                  * @param m the transformation matrix of this model
     629              :                  * @param modelscale the scale factor for the model
     630              :                  */
     631              :                 void genBIH(std::vector<BIH::mesh> &bih, const matrix4x3 &m, float modelscale) const;
     632              :                 void genshadowmesh(std::vector<triangle> &tris, const matrix4x3 &m, float modelscale) const;
     633              :                 bool link(part *p, std::string_view tag, const vec &translate = vec(0, 0, 0), int anim = -1, int basetime = 0, vec *pos = nullptr);
     634              : 
     635              :                 /**
     636              :                  * @brief Removes from this part's linkedpart list the linkedpart corresponding to the passed part
     637              :                  *
     638              :                  * `part::links` is the vector containing the list of linkedparts.
     639              :                  *
     640              :                  * @param p the part to remove the corresponding linkedpart from
     641              :                  */
     642              :                 bool unlink(const part *p);
     643              :                 void initskins(Texture *tex = notexture, Texture *masks = notexture, uint limit = 0);
     644              :                 bool alphatested() const;
     645              :                 void preloadBIH() const;
     646              :                 void preloadshaders();
     647              : 
     648              :                 /**
     649              :                  * @brief Causes the attached meshgroup to run its preload routine
     650              :                  *
     651              :                  * The (single) meshgroup pointed to by this object will preload itself.
     652              :                  * This process includes generating the vertex buffer objects required
     653              :                  * for rendering the meshes.
     654              :                  */
     655              :                 void preloadmeshes();
     656              : 
     657              :                 void intersect(int anim, int basetime, int basetime2, float pitch, const vec &axis, const vec &forward, dynent *d, const vec &o, const vec &ray);
     658              :                 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);
     659              :                 void render(int anim, int basetime, int basetime2, float pitch, const vec &axis, const vec &forward, dynent *d);
     660              :                 void render(int anim, int basetime, int basetime2, float pitch, const vec &axis, const vec &forward, dynent *d, AnimState *as);
     661              :                 void setanim(int animpart, int num, int frame, int range, float speed, int priority = 0);
     662              : 
     663              :                 /**
     664              :                  * @brief Returns true of all animparts for this part are set
     665              :                  *
     666              :                  * Checks that all animspec vectors in part::anims exist and have
     667              :                  * been instantiated. No values must be in the vectors for this to return true;
     668              :                  * they only need to exist.
     669              :                  *
     670              :                  * @return true if all animparts are set, false otherwise
     671              :                  */
     672              :                 bool animated() const;
     673              :                 virtual void loaded();
     674              : 
     675              :             private:
     676              :                 struct animspec
     677              :                 {
     678              :                     int frame, range;
     679              :                     float speed;
     680              :                     int priority;
     681              :                 };
     682              : 
     683              :                 std::vector<animspec> *anims[maxanimparts]; //pointer to array of std::vector<animspec>
     684              : 
     685              :                 virtual void getdefaultanim(animinfo &info) const;
     686              :                 bool calcanim(int animpart, int anim, int basetime, int basetime2, dynent *d, int interp, animinfo &info, int &animinterptime) const;
     687              :         };
     688              : 
     689              :         std::vector<part *> parts; //vector of part objects heap-allocated by skelmodel::addpart or part::addpart
     690              : 
     691              :         //ordinary methods
     692              :         ~animmodel();
     693              :         animmodel(const animmodel& a) = delete;
     694              :         animmodel &operator=(const animmodel &a) = delete;
     695              : 
     696              :         /**
     697              :          * @brief Creates a heap-allocated part object.
     698              :          *
     699              :          * This must be `delete`'d or it will cause a memory leak.
     700              :          *
     701              :          * @return a reference to the newly created part
     702              :          */
     703              :         part &addpart();
     704              :         /**
     705              :          * @brief Sets up a transformation matrix based on this model's rotation, translation, and scale
     706              :          *
     707              :          * @return a transformation matrix corresponding to the model's transformations
     708              :          */
     709              :         matrix4x3 initmatrix() const;
     710              :         void genBIH(std::vector<BIH::mesh> &bih);
     711              :         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;
     712              :         void loaded();
     713              :         bool unlink(const part *p) const;
     714              :         void render(int anim, int basetime, int basetime2, float pitch, const vec &axis, const vec &forward, dynent *d, modelattach *a) const;
     715              : 
     716              :         //virtual methods
     717              :         /**
     718              :          * @brief Returns whether this model type requires a flipped Y axis
     719              :          *
     720              :          * This function will return true if the model type needs its coordinate
     721              :          * system transformed to properly render in the engine. GLTF and MD5 do not,
     722              :          * while OBJ does.
     723              :          *
     724              :          * @return true if the model format requires transformation, false otherwise
     725              :          */
     726              :         virtual bool flipy() const = 0;
     727              :         virtual bool loadconfig(const std::string &mdlname) = 0;
     728              :         virtual bool loaddefaultparts() = 0;
     729              :         virtual void startload() = 0;
     730              :         virtual void endload() = 0;
     731              : 
     732              :         //model object overrides
     733              :         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 final;
     734              :         void cleanup() final;
     735              :         void genshadowmesh(std::vector<triangle> &tris, const matrix4x3 &orient) const final;
     736              :         void preloadBIH() final;
     737              :         void setBIH() final;
     738              :         bool animated() const final;
     739              :         bool pitched() const final;
     740              :         bool alphatested() const final;
     741              :         bool load() final;
     742              :         void preloadshaders() final;
     743              : 
     744              :         /**
     745              :          * @brief Preloades every entry in the part::meshes array.
     746              :          *
     747              :          * No effect if there are no meshes to be loaded.
     748              :          */
     749              :         void preloadmeshes() final;
     750              : 
     751              :         /**
     752              :          * @brief Sets the shader to be used by the model
     753              :          *
     754              :          * Clears any pre-existing slot parameters related to an existing shader.
     755              :          *
     756              :          * If no such shader was found, prints an error message to the console.
     757              :          *
     758              :          * @param shader the shader to set
     759              :          */
     760              :         void setshader(Shader *shader) final;
     761              :         void setspec(float spec) final;
     762              :         void setgloss(int gloss) final;
     763              :         void setglow(float glow, float delta, float pulse) final;
     764              :         /**
     765              :          * @brief Sets the alphatest value for each skin in each part of this model
     766              :          *
     767              :          * alphatest is one of the shader parameters for the skin shader. Each skin
     768              :          * in this model will recieve the same alphatest value.
     769              :          *
     770              :          * @param alphatest the alphatest value to set
     771              :          */
     772              :         void setalphatest(float alphatest) final;
     773              :         void setfullbright(float fullbright) final;
     774              :         void setcullface(int cullface) final;
     775              :         void setcolor(const vec &color) final;
     776              :         void settransformation(const std::optional<vec> pos,
     777              :                                const std::optional<vec> rotate,
     778              :                                const std::optional<vec> orient,
     779              :                                const std::optional<float> size) final;
     780              : 
     781              :         /**
     782              :          * @brief Returns a 4-vector consisting of the translation and scale of the model.
     783              :          *
     784              :          * @return translation coordinates in x,y,z; scale factor in w
     785              :          */
     786              :         vec4<float> locationsize() const final;
     787              :         void calcbb(vec &center, vec &radius) const final;
     788              :         void startrender() const final;
     789              :         void endrender() const final;
     790              : 
     791              :         /**
     792              :          * @brief Recalculates the bounding box parameters and returns them to the parameters passed
     793              :          *
     794              :          * Sets the parent `model` object's fields `bbcenter` and `bbradius`.
     795              :          *
     796              :          * @param center returns the value of this model's bounding box
     797              :          * @param radius returns the radius of this model's bounding box
     798              :          */
     799              :         void boundbox(vec &center, vec &radius) final;
     800              :         float collisionbox(vec &center, vec &radius) final;
     801              :         const std::string &modelname() const final;
     802              :         //static methods
     803              :         static void disablebones();
     804              :         static void disabletangents();
     805              :         static void disabletc();
     806              :         static void disablevbo();
     807              : 
     808              :     protected:
     809              :         enum
     810              :         {
     811              :             Link_Tag = 0,
     812              :             Link_Reuse
     813              :         };
     814              : 
     815              :         animmodel(std::string name);
     816              : 
     817              :         /**
     818              :          * @brief Returns the linkage type of the specified model part.
     819              :          *
     820              :          * For animmodels, always returns Link_Tag (see above anim).
     821              :          * Skeletal models may return Link_Reuse if the passed part shares the same mesh as this part.
     822              :          *
     823              :          * @return the linkage type of this part
     824              :          */
     825              :         virtual int linktype(const animmodel *, const part *) const;
     826              :         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 final;
     827              : 
     828              :         static bool enabletc, enablebones, enabletangents;
     829              : 
     830              :         /**
     831              :          * @brief A stack of transformation matrices used for model intersection math
     832              :          */
     833              :         static std::stack<matrix4> matrixstack;
     834              :         static float sizescale;
     835              : 
     836              :     private:
     837              :         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;
     838              : 
     839              :         static bool enablecullface, enabledepthoffset;
     840              :         static vec4<float> colorscale;
     841              :         static GLuint lastvbuf, lasttcbuf, lastxbuf, lastbbuf, lastebuf;
     842              :         static const Texture *lasttex,
     843              :                              *lastdecal,
     844              :                              *lastmasks,
     845              :                              *lastnormalmap;
     846              : };
     847              : 
     848              : /**
     849              :  * @brief wrapper to load specific format into model object
     850              :  *
     851              :  * modelloader is a template for a wrapper to load a model into a model/animmodel
     852              :  * object from a transactional format, it is intended to be a child template class
     853              :  * of an animmodel derivative (the BASE template parameter)
     854              :  *
     855              :  * skelloader is a specialization of this class which uses modelloader to load
     856              :  * a skeletal model
     857              :  *
     858              :  */
     859              : template<class MDL, class BASE>
     860              : struct modelloader : BASE
     861              : {
     862              :     static MDL *loading;
     863              :     static std::string dir;
     864              : 
     865           18 :     modelloader(std::string name) : BASE(name)
     866              :     {
     867           18 :     }
     868              : 
     869            2 :     static bool cananimate()
     870              :     {
     871            2 :         return true;
     872              :     }
     873              : 
     874            6 :     static bool multiparted()
     875              :     {
     876            6 :         return true;
     877              :     }
     878              : 
     879            3 :     static bool multimeshed()
     880              :     {
     881            3 :         return true;
     882              :     }
     883              : 
     884           17 :     void startload() final
     885              :     {
     886           17 :         loading = static_cast<MDL *>(this);
     887           17 :     }
     888              : 
     889           13 :     void endload() final
     890              :     {
     891           13 :         loading = nullptr;
     892           13 :     }
     893              : 
     894            6 :     bool loadconfig(const std::string &mdlname) final
     895              :     {
     896            6 :         dir.clear();
     897            6 :         dir.append(modelpath).append(mdlname);
     898            6 :         std::string cfgname;
     899            6 :         cfgname.append(modelpath).append(mdlname).append("/").append(MDL::formatname()).append(".cfg");
     900              : 
     901            6 :         identflags &= ~Idf_Persist;
     902            6 :         bool success = execfile(cfgname.c_str(), false);
     903            6 :         identflags |= Idf_Persist;
     904            6 :         return success;
     905            6 :     }
     906              : };
     907              : 
     908              : template<class MDL, class BASE>
     909              : MDL *modelloader<MDL, BASE>::loading = nullptr;
     910              : 
     911              : template<class MDL, class BASE>
     912              : std::string modelloader<MDL, BASE>::dir = "";
     913              : 
     914              : /**
     915              :  * @brief Template for adding command bindings for a particular model format.
     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