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: 2025-11-03 06:43:22 Functions: 89.7 % 232 208

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

Generated by: LCOV version 2.0-1