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-24 07:55:42 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              : /**
       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              :          * @param center returns the value of this model's bounding box
     795              :          * @param radius returns the radius of this model's bounding box
     796              :          */
     797              :         void boundbox(vec &center, vec &radius) final;
     798              :         float collisionbox(vec &center, vec &radius) final;
     799              :         const std::string &modelname() const final;
     800              :         //static methods
     801              :         static void disablebones();
     802              :         static void disabletangents();
     803              :         static void disabletc();
     804              :         static void disablevbo();
     805              : 
     806              :     protected:
     807              :         enum
     808              :         {
     809              :             Link_Tag = 0,
     810              :             Link_Reuse
     811              :         };
     812              : 
     813              :         animmodel(std::string name);
     814              : 
     815              :         /**
     816              :          * @brief Returns the linkage type of the specified model part.
     817              :          *
     818              :          * For animmodels, always returns Link_Tag (see above anim).
     819              :          * Skeletal models may return Link_Reuse if the passed part shares the same mesh as this part.
     820              :          *
     821              :          * @return the linkage type of this part
     822              :          */
     823              :         virtual int linktype(const animmodel *, const part *) const;
     824              :         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;
     825              : 
     826              :         static bool enabletc, enablebones, enabletangents;
     827              : 
     828              :         /**
     829              :          * @brief A stack of transformation matrices used for model intersection math
     830              :          */
     831              :         static std::stack<matrix4> matrixstack;
     832              :         static float sizescale;
     833              : 
     834              :     private:
     835              :         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;
     836              : 
     837              :         static bool enablecullface, enabledepthoffset;
     838              :         static vec4<float> colorscale;
     839              :         static GLuint lastvbuf, lasttcbuf, lastxbuf, lastbbuf, lastebuf;
     840              :         static const Texture *lasttex,
     841              :                              *lastdecal,
     842              :                              *lastmasks,
     843              :                              *lastnormalmap;
     844              : };
     845              : 
     846              : /* modelloader
     847              :  *
     848              :  * modelloader is a template for a wrapper to load a model into a model/animmodel
     849              :  * object from a transactional format, it is intended to be a child template class
     850              :  * of an animmodel derivative (the BASE template parameter)
     851              :  *
     852              :  * skelloader is a specialization of this class which uses modelloader to load
     853              :  * a skeletal model
     854              :  *
     855              :  */
     856              : template<class MDL, class BASE>
     857              : struct modelloader : BASE
     858              : {
     859              :     static MDL *loading;
     860              :     static std::string dir;
     861              : 
     862           18 :     modelloader(std::string name) : BASE(name)
     863              :     {
     864           18 :     }
     865              : 
     866            2 :     static bool cananimate()
     867              :     {
     868            2 :         return true;
     869              :     }
     870              : 
     871            6 :     static bool multiparted()
     872              :     {
     873            6 :         return true;
     874              :     }
     875              : 
     876            3 :     static bool multimeshed()
     877              :     {
     878            3 :         return true;
     879              :     }
     880              : 
     881           17 :     void startload() final
     882              :     {
     883           17 :         loading = static_cast<MDL *>(this);
     884           17 :     }
     885              : 
     886           13 :     void endload() final
     887              :     {
     888           13 :         loading = nullptr;
     889           13 :     }
     890              : 
     891            6 :     bool loadconfig(const std::string &mdlname) final
     892              :     {
     893            6 :         dir.clear();
     894            6 :         dir.append(modelpath).append(mdlname);
     895            6 :         std::string cfgname;
     896            6 :         cfgname.append(modelpath).append(mdlname).append("/").append(MDL::formatname()).append(".cfg");
     897              : 
     898            6 :         identflags &= ~Idf_Persist;
     899            6 :         bool success = execfile(cfgname.c_str(), false);
     900            6 :         identflags |= Idf_Persist;
     901            6 :         return success;
     902            6 :     }
     903              : };
     904              : 
     905              : template<class MDL, class BASE>
     906              : MDL *modelloader<MDL, BASE>::loading = nullptr;
     907              : 
     908              : template<class MDL, class BASE>
     909              : std::string modelloader<MDL, BASE>::dir = "";
     910              : 
     911              : /* modelcommands
     912              :  *
     913              :  * this template class adds a series of commands to the cubescript binding
     914              :  * adaptable to a specific model type
     915              :  *
     916              :  * this template class generates unique command names for each separate model type
     917              :  * such as objcolor for obj, or md5color for md5 models; they are static and the
     918              :  * same for any given MDL template parameter
     919              :  *
     920              :  * the intended MDL template parameter is one of the model formats (md5, obj, etc)
     921              :  */
     922              : template<class MDL>
     923              : struct modelcommands
     924              : {
     925              :     typedef class MDL::part part;
     926              :     typedef class MDL::skin skin;
     927              : 
     928           69 :     static bool checkmdl()
     929              :     {
     930           69 :         if(!MDL::loading)
     931              :         {
     932           69 :             conoutf(Console_Error, "not loading a model");
     933           69 :             return false;
     934              :         }
     935              :         else
     936              :         {
     937            0 :             return true;
     938              :         }
     939              :     }
     940              : 
     941            3 :     static void mdlcullface(const int *cullface)
     942              :     {
     943            3 :         if(!checkmdl())
     944              :         {
     945            3 :             return;
     946              :         }
     947            0 :         MDL::loading->setcullface(*cullface);
     948              :     }
     949              : 
     950            3 :     static void mdlcolor(const float *r, const float *g, const float *b)
     951              :     {
     952            3 :         if(!checkmdl())
     953              :         {
     954            3 :             return;
     955              :         }
     956            0 :         MDL::loading->setcolor(vec(*r, *g, *b));
     957              :     }
     958              : 
     959            3 :     static void mdlcollide(const int *collide)
     960              :     {
     961            3 :         if(!checkmdl())
     962              :         {
     963            3 :             return;
     964              :         }
     965            0 :         MDL::loading->collide = *collide!=0 ? (MDL::loading->collide ? MDL::loading->collide : Collide_OrientedBoundingBox) : Collide_None;
     966              :     }
     967              : 
     968            3 :     static void mdlellipsecollide(const int *collide)
     969              :     {
     970            3 :         if(!checkmdl())
     971              :         {
     972            3 :             return;
     973              :         }
     974            0 :         MDL::loading->collide = *collide!=0 ? Collide_Ellipse : Collide_None;
     975              :     }
     976              : 
     977            3 :     static void mdltricollide(const char *collide)
     978              :     {
     979            3 :         if(!checkmdl())
     980              :         {
     981            3 :             return;
     982              :         }
     983            0 :         MDL::loading->collidemodel.clear();
     984            0 :         char *end = nullptr;
     985            0 :         int val = std::strtol(collide, &end, 0);
     986            0 :         if(*end)
     987              :         {
     988            0 :             val = 1;
     989            0 :             MDL::loading->collidemodel = std::string(collide);
     990              :         }
     991            0 :         MDL::loading->collide = val ? Collide_TRI : Collide_None;
     992              :     }
     993              : 
     994            3 :     static void mdlspec(const float *percent)
     995              :     {
     996            3 :         if(!checkmdl())
     997              :         {
     998            3 :             return;
     999              :         }
    1000            0 :         float spec = *percent > 0 ? *percent/100.0f : 0.0f;
    1001            0 :         MDL::loading->setspec(spec);
    1002              :     }
    1003              : 
    1004            3 :     static void mdlgloss(const int *gloss)
    1005              :     {
    1006            3 :         if(!checkmdl())
    1007              :         {
    1008            3 :             return;
    1009              :         }
    1010            0 :         MDL::loading->setgloss(std::clamp(*gloss, 0, 2));
    1011              :     }
    1012              : 
    1013            3 :     static void mdlalphatest(const float *cutoff)
    1014              :     {
    1015            3 :         if(!checkmdl())
    1016              :         {
    1017            3 :             return;
    1018              :         }
    1019            0 :         MDL::loading->setalphatest(std::max(0.0f, std::min(1.0f, *cutoff)));
    1020              :     }
    1021              : 
    1022            3 :     static void mdldepthoffset(const int *offset)
    1023              :     {
    1024            3 :         if(!checkmdl())
    1025              :         {
    1026            3 :             return;
    1027              :         }
    1028            0 :         MDL::loading->depthoffset = *offset!=0;
    1029              :     }
    1030              : 
    1031            3 :     static void mdlglow(const float *percent, const float *delta, const float *pulse)
    1032              :     {
    1033            3 :         if(!checkmdl())
    1034              :         {
    1035            3 :             return;
    1036              :         }
    1037            0 :         float glow = *percent > 0 ? *percent/100.0f : 0.0f,
    1038            0 :               glowdelta = *delta/100.0f,
    1039            0 :               glowpulse = *pulse > 0 ? *pulse/1000.0f : 0;
    1040            0 :         glowdelta -= glow;
    1041            0 :         MDL::loading->setglow(glow, glowdelta, glowpulse);
    1042              :     }
    1043              : 
    1044            3 :     static void mdlfullbright(const float *fullbright)
    1045              :     {
    1046            3 :         if(!checkmdl())
    1047              :         {
    1048            3 :             return;
    1049              :         }
    1050            0 :         MDL::loading->setfullbright(*fullbright);
    1051              :     }
    1052              : 
    1053              : 
    1054            3 :     static void mdlshader(const char *shader)
    1055              :     {
    1056            3 :         if(!checkmdl())
    1057              :         {
    1058            3 :             return;
    1059              :         }
    1060            0 :         MDL::loading->setshader(lookupshaderbyname(shader));
    1061              :     }
    1062              : 
    1063              :     //assigns a new spin speed in three euler angles for the model object currently being loaded
    1064            3 :     static void mdlspin(const float *yaw, const float *pitch, const float *roll)
    1065              :     {
    1066            3 :         if(!checkmdl())
    1067              :         {
    1068            3 :             return;
    1069              :         }
    1070            0 :         MDL::loading->settransformation(std::nullopt, vec(*yaw, *pitch, *roll), std::nullopt, std::nullopt);
    1071              :     }
    1072              : 
    1073              :     //assigns a new scale factor in % for the model object currently being loaded
    1074            3 :     static void mdlscale(const float *percent)
    1075              :     {
    1076            3 :         if(!checkmdl())
    1077              :         {
    1078            3 :             return;
    1079              :         }
    1080            0 :         float scale = *percent > 0 ? *percent/100.0f : 1.0f;
    1081            0 :         MDL::loading->settransformation(std::nullopt, std::nullopt, std::nullopt, scale);
    1082              :     }
    1083              : 
    1084              :     //assigns translation in x,y,z in cube units for the model object currently being loaded
    1085            3 :     static void mdltrans(const float *x, const float *y, const float *z)
    1086              :     {
    1087            3 :         if(!checkmdl())
    1088              :         {
    1089            3 :             return;
    1090              :         }
    1091            0 :         MDL::loading->settransformation(vec(*x, *y, *z), std::nullopt, std::nullopt, std::nullopt);
    1092              :     }
    1093              : 
    1094              :     //assigns angle to the offsetyaw field of the model object currently being loaded
    1095            3 :     static void mdlyaw(const float *angle)
    1096              :     {
    1097            3 :         if(!checkmdl())
    1098              :         {
    1099            3 :             return;
    1100              :         }
    1101            0 :         MDL::loading->orientation.x = *angle;
    1102              :     }
    1103              : 
    1104              : 
    1105              :     //assigns angle to the offsetpitch field of the model object currently being loaded
    1106            3 :     static void mdlpitch(const float *angle)
    1107              :     {
    1108            3 :         if(!checkmdl())
    1109              :         {
    1110            3 :             return;
    1111              :         }
    1112            0 :         MDL::loading->orientation.y = *angle;
    1113              :     }
    1114              : 
    1115              :     //assigns angle to the offsetroll field of the model object currently being loaded
    1116            3 :     static void mdlroll(const float *angle)
    1117              :     {
    1118            3 :         if(!checkmdl())
    1119              :         {
    1120            3 :             return;
    1121              :         }
    1122            0 :         MDL::loading->orientation.z = *angle;
    1123              :     }
    1124              : 
    1125              :     //assigns shadow to the shadow field of the model object currently being loaded
    1126            3 :     static void mdlshadow(const int *shadow)
    1127              :     {
    1128            3 :         if(!checkmdl())
    1129              :         {
    1130            3 :             return;
    1131              :         }
    1132            0 :         MDL::loading->shadow = *shadow!=0;
    1133              :     }
    1134              : 
    1135              :     //assigns alphashadow to the alphashadow field of the model object currently being loaded
    1136            3 :     static void mdlalphashadow(const int *alphashadow)
    1137              :     {
    1138            3 :         if(!checkmdl())
    1139              :         {
    1140            3 :             return;
    1141              :         }
    1142            0 :         MDL::loading->alphashadow = *alphashadow!=0;
    1143              :     }
    1144              : 
    1145              :     //assigns rad, h, eyeheight to the fields of the model object currently being loaded
    1146            3 :     static void mdlbb(const float *rad, const float *h, const float *eyeheight)
    1147              :     {
    1148            3 :         if(!checkmdl())
    1149              :         {
    1150            3 :             return;
    1151              :         }
    1152            0 :         MDL::loading->collidexyradius = *rad;
    1153            0 :         MDL::loading->collideheight = *h;
    1154            0 :         MDL::loading->eyeheight = *eyeheight;
    1155              :     }
    1156              : 
    1157            3 :     static void mdlextendbb(const float *x, const float *y, const float *z)
    1158              :     {
    1159            3 :         if(!checkmdl())
    1160              :         {
    1161            3 :             return;
    1162              :         }
    1163            0 :         MDL::loading->bbextend = vec(*x, *y, *z);
    1164              :     }
    1165              : 
    1166              :     /* mdlname
    1167              :      *
    1168              :      * returns the name of the model currently loaded [most recently]
    1169              :      */
    1170            3 :     static void mdlname()
    1171              :     {
    1172            3 :         if(!checkmdl())
    1173              :         {
    1174            3 :             return;
    1175              :         }
    1176            0 :         result(MDL::loading->modelname().c_str());
    1177              :     }
    1178              : 
    1179           14 :     static void setdir(const char *name)
    1180              :     {
    1181           14 :         if(!MDL::loading)
    1182              :         {
    1183            3 :             conoutf("not loading an %s", MDL::formatname());
    1184            3 :             return;
    1185              :         }
    1186           11 :         MDL::dir = std::string(modelpath).append(name ? name : "");
    1187              :     }
    1188              : 
    1189              :     /**
    1190              :      * @brief Returns an iterator vector of meshes with the given name
    1191              :      *
    1192              :      * Returns a vector of MDL::loading->parts.back()'s meshgroup's mesh vector
    1193              :      * iterators where those iterator's contents' name field compares equal to the
    1194              :      * passed string. If the wildcard "*" is passed as `meshname` then all elements
    1195              :      * will be added regardless of name. If no such mesh vector exists (or there is
    1196              :      * no loading model) then an empty vector is returned.
    1197              :      *
    1198              :      * @param meshname the mesh name to select from the mesh vector
    1199              :      *
    1200              :      * @return vector of iterators corresponding to meshes with the given name
    1201              :      */
    1202            3 :     static std::vector<std::vector<animmodel::Mesh *>::const_iterator> getmeshes(std::string_view meshname)
    1203              :     {
    1204            3 :         std::vector<std::vector<animmodel::Mesh *>::const_iterator> meshlist;
    1205            3 :         if(!MDL::loading || MDL::loading->parts.empty())
    1206              :         {
    1207            3 :             conoutf("not loading an %s", MDL::formatname());
    1208            3 :             return meshlist; //empty vector
    1209              :         }
    1210            0 :         const part &mdl = *MDL::loading->parts.back();
    1211            0 :         if(!mdl.meshes)
    1212              :         {
    1213            0 :             return meshlist; //empty vector
    1214              :         }
    1215            0 :         meshlist = mdl.meshes->getmeshes(meshname);
    1216            0 :         return meshlist;
    1217            0 :     }
    1218              : 
    1219              :     /**
    1220              :      * @brief Returns an iterator vector of skins associated with the given name
    1221              :      *
    1222              :      * Returns a vector of MDL::loading->parts.back()'s meshgroup's skin vector
    1223              :      * iterators where those iterator's contents' name field compares equal to the
    1224              :      * passed string. If the wildcard "*" is passed as `meshname` then all elements
    1225              :      * will be added regardless of name. If no such mesh vector exists (or there is
    1226              :      * no loading model) then an empty vector is returned.
    1227              :      *
    1228              :      * @param meshname the mesh name to select from the skin vector
    1229              :      *
    1230              :      * @return vector of iterators corresponding to skins with the given name
    1231              :      */
    1232           44 :     static std::vector<std::vector<animmodel::skin>::iterator> getskins(std::string_view meshname)
    1233              :     {
    1234           44 :         std::vector<std::vector<animmodel::skin>::iterator> skinlist;
    1235           44 :         if(!MDL::loading || MDL::loading->parts.empty())
    1236              :         {
    1237           36 :             conoutf("not loading an %s", MDL::formatname());
    1238           36 :             return skinlist;
    1239              :         }
    1240            8 :         part &mdl = *MDL::loading->parts.back();
    1241            8 :         if(!mdl.meshes)
    1242              :         {
    1243            0 :             return skinlist;
    1244              :         }
    1245            8 :         std::vector<size_t> skinindices = mdl.meshes->getskins(meshname);
    1246           16 :         for(size_t i : skinindices)
    1247              :         {
    1248            8 :             skinlist.push_back(mdl.skins.begin() + i);
    1249              :         }
    1250            8 :         return skinlist;
    1251            8 :     }
    1252              : 
    1253            6 :     static void setskin(const char *meshname, const char *tex, const char *masks)
    1254              :     {
    1255            6 :         auto skinlist = getskins(meshname);
    1256            9 :         for(auto s : skinlist)
    1257              :         {
    1258            3 :             (*s).tex = textureload(makerelpath(MDL::dir.c_str(), tex), 0, true, false);
    1259            3 :             if(*masks)
    1260              :             {
    1261            3 :                 (*s).masks = textureload(makerelpath(MDL::dir.c_str(), masks), 0, true, false);
    1262              :             }
    1263              :         }
    1264            6 :     }
    1265              : 
    1266            3 :     static void setspec(const char *meshname, const float *percent)
    1267              :     {
    1268            3 :         float spec = *percent > 0 ? *percent/100.0f : 0.0f;
    1269            3 :         auto skinlist = getskins(meshname);
    1270            3 :         for(auto s : skinlist)
    1271              :         {
    1272            0 :             (*s).spec = spec;
    1273              :         }
    1274            3 :     }
    1275              : 
    1276            3 :     static void setgloss(const char *meshname, const int *gloss)
    1277              :     {
    1278            3 :         auto skinlist = getskins(meshname);
    1279            3 :         for(auto s : skinlist)
    1280              :         {
    1281            0 :             (*s).gloss = std::clamp(*gloss, 0, 2);
    1282              :         }
    1283            3 :     }
    1284              : 
    1285            3 :     static void setglow(const char *meshname, const float *percent, const float *delta, const float *pulse)
    1286              :     {
    1287            3 :         float glow = *percent > 0 ? *percent/100.0f : 0.0f,
    1288            3 :               glowdelta = *delta/100.0f,
    1289            3 :               glowpulse = *pulse > 0 ? *pulse/1000.0f : 0;
    1290            3 :         glowdelta -= glow;
    1291            3 :         auto skinlist = getskins(meshname);
    1292            3 :         for(auto s : skinlist)
    1293              :         {
    1294            0 :             (*s).glow = glow;
    1295            0 :             (*s).glowdelta = glowdelta;
    1296            0 :             (*s).glowpulse = glowpulse;
    1297              :         }
    1298            3 :     }
    1299              : 
    1300            3 :     static void setalphatest(const char *meshname, const float *cutoff)
    1301              :     {
    1302            3 :         auto skinlist = getskins(meshname);
    1303            3 :         for(auto s : skinlist)
    1304              :         {
    1305            0 :             (*s).alphatest = std::max(0.0f, std::min(1.0f, *cutoff));
    1306              :         }
    1307            3 :     }
    1308              : 
    1309            3 :     static void setcullface(const char *meshname, const int *cullface)
    1310              :     {
    1311            3 :         auto skinlist = getskins(meshname);
    1312            3 :         for(auto s : skinlist)
    1313              :         {
    1314            0 :             (*s).cullface = *cullface;
    1315              :         }
    1316            3 :     }
    1317              : 
    1318            3 :     static void setcolor(const char *meshname, const float *r, const float *g, const float *b)
    1319              :     {
    1320            3 :         auto skinlist = getskins(meshname);
    1321            3 :         for(auto s : skinlist)
    1322              :         {
    1323            0 :             (*s).color = vec(*r, *g, *b);
    1324              :         }
    1325            3 :     }
    1326              : 
    1327            4 :     static void setbumpmap(const char *meshname, const char *normalmapfile)
    1328              :     {
    1329            4 :         Texture *normalmaptex = textureload(makerelpath(MDL::dir.c_str(), normalmapfile), 0, true, false);
    1330            4 :         auto skinlist = getskins(meshname);
    1331            5 :         for(auto s : skinlist)
    1332              :         {
    1333            1 :             (*s).normalmap = normalmaptex;
    1334              :         }
    1335            4 :     }
    1336              : 
    1337            4 :     static void setdecal(const char *meshname, const char *decal)
    1338              :     {
    1339            4 :         auto skinlist = getskins(meshname);
    1340            5 :         for(auto s : skinlist)
    1341              :         {
    1342            1 :             (*s).decal = textureload(makerelpath(MDL::dir.c_str(), decal), 0, true, false);
    1343              :         }
    1344            4 :     }
    1345              : 
    1346            3 :     static void setfullbright(const char *meshname, const float *fullbright)
    1347              :     {
    1348            3 :         auto skinlist = getskins(meshname);
    1349            3 :         for(auto s : skinlist)
    1350              :         {
    1351            0 :             (*s).fullbright = *fullbright;
    1352              :         }
    1353            3 :     }
    1354              : 
    1355            3 :     static void setshader(const char *meshname, const char *shader)
    1356              :     {
    1357            3 :         auto skinlist = getskins(meshname);
    1358            3 :         for(auto s : skinlist)
    1359              :         {
    1360            0 :             (*s).shader = lookupshaderbyname(shader);
    1361              :         }
    1362            3 :     }
    1363              : 
    1364            3 :     static void setscroll(const char *meshname, const float *scrollu, const float *scrollv)
    1365              :     {
    1366            3 :         auto skinlist = getskins(meshname);
    1367            3 :         for(auto s : skinlist)
    1368              :         {
    1369            0 :             (*s).scrollu = *scrollu;
    1370            0 :             (*s).scrollv = *scrollv;
    1371              :         }
    1372            3 :     }
    1373              : 
    1374            3 :     static void setnoclip(const char *meshname, const int *noclip)
    1375              :     {
    1376            3 :         auto meshlist = getmeshes(std::string(meshname));
    1377            3 :         if(meshlist.empty())
    1378              :         {
    1379            3 :             return;
    1380              :         }
    1381            0 :         for(auto &i : meshlist)
    1382              :         {
    1383            0 :             (*i)->noclip = *noclip!=0;
    1384              :         }
    1385            3 :     }
    1386              : 
    1387            0 :     static void settricollide(const char *meshname)
    1388              :     {
    1389            0 :         bool init = true;
    1390            0 :         auto meshlist = getmeshes(std::string(meshname));
    1391            0 :         if(!meshlist.empty())
    1392              :         {
    1393            0 :             return;
    1394              :         }
    1395              :         else
    1396              :         {
    1397            0 :             for(auto &i : meshlist)
    1398              :             {
    1399            0 :                 if(!(*i)->cancollide)
    1400              :                 {
    1401            0 :                     init = false;
    1402              :                 }
    1403              :             }
    1404            0 :             if(init)
    1405              :             {
    1406            0 :                 for(auto &i : meshlist)
    1407              :                 {
    1408            0 :                     (*i)->cancollide = false;
    1409              :                 }
    1410              :             }
    1411            0 :             for(auto &i : meshlist)
    1412              :             {
    1413            0 :                 (*i)->cancollide = true;
    1414            0 :                 (*i)->canrender = false;
    1415              :             }
    1416              :         }
    1417            0 :     }
    1418              : 
    1419            3 :     static void setlink(const int *parent, const int *child, const char *tagname, const float *x, const float *y, const float *z)
    1420              :     {
    1421            3 :         if(!MDL::loading)
    1422              :         {
    1423            3 :             conoutf("not loading an %s", MDL::formatname());
    1424            3 :             return;
    1425              :         }
    1426            0 :         if(!(static_cast<int>(MDL::loading->parts.size()) > *parent) || !(static_cast<int>(MDL::loading->parts.size()) > *child))
    1427              :         {
    1428            0 :             conoutf("no models loaded to link");
    1429            0 :             return;
    1430              :         }
    1431            0 :         if(!MDL::loading->parts[*parent]->link(MDL::loading->parts[*child], tagname, vec(*x, *y, *z)))
    1432              :         {
    1433            0 :             conoutf("could not link model %s", MDL::loading->modelname().c_str());
    1434              :         }
    1435              :     }
    1436              : 
    1437              :     template<class F>
    1438          152 :     void modelcommand(F *fun, std::string_view suffix, std::string_view args)
    1439              :     {
    1440          152 :         std::string name;
    1441          152 :         name.append(MDL::formatname()).append(suffix);
    1442          152 :         addcommand(newstring(name.c_str()), reinterpret_cast<identfun>(fun), args.data());
    1443          152 :     }
    1444              : 
    1445            3 :     modelcommands()
    1446              :     {
    1447            3 :         modelcommand(setdir, "dir", "s");//<fmt>dir [name]
    1448            3 :         if(MDL::multimeshed())
    1449              :         {
    1450            3 :             modelcommand(mdlcullface, "cullface", "i");
    1451            3 :             modelcommand(mdlcolor, "color", "fff");
    1452            3 :             modelcommand(mdlcollide, "collide", "i");
    1453            3 :             modelcommand(mdlellipsecollide, "ellipsecollide", "i");
    1454            3 :             modelcommand(mdltricollide, "tricollide", "s");
    1455            3 :             modelcommand(mdlspec, "spec", "f");
    1456            3 :             modelcommand(mdlgloss, "gloss", "i");
    1457            3 :             modelcommand(mdlalphatest, "alphatest", "f");
    1458            3 :             modelcommand(mdldepthoffset, "depthoffset", "i");
    1459            3 :             modelcommand(mdlglow, "glow", "fff");
    1460            3 :             modelcommand(mdlfullbright, "fullbright", "f");
    1461            3 :             modelcommand(mdlshader, "shader", "s");
    1462            3 :             modelcommand(mdlspin, "spin", "fff");
    1463            3 :             modelcommand(mdlscale, "scale", "f");
    1464            3 :             modelcommand(mdltrans, "trans", "fff");
    1465            3 :             modelcommand(mdlyaw, "yaw", "f");
    1466            3 :             modelcommand(mdlpitch, "pitch", "f");
    1467            3 :             modelcommand(mdlroll, "roll", "f");
    1468            3 :             modelcommand(mdlshadow, "shadow", "i");
    1469            3 :             modelcommand(mdlalphashadow, "alphashadow", "i");
    1470            3 :             modelcommand(mdlbb, "bb", "fff");
    1471            3 :             modelcommand(mdlextendbb, "extendbb", "fff");
    1472            3 :             modelcommand(mdlname, "name", "");
    1473              : 
    1474            3 :             modelcommand(setskin, "skin", "sss");               //<fmt>skin [meshname] [tex] [masks]
    1475            3 :             modelcommand(setspec, "texspec", "sf");             //<fmt>texspec [mesh] [scale]
    1476            3 :             modelcommand(setgloss, "texgloss", "si");           //<fmt>texgloss [mesh] [type] type ranges 0..2
    1477            3 :             modelcommand(setglow, "texglow", "sfff");           //<fmt>texglow [mesh] [pct] [del] [pulse]
    1478            3 :             modelcommand(setalphatest, "meshalphatest", "sf");  //<fmt>meshalphatest [mesh] [cutoff]
    1479            3 :             modelcommand(setcullface, "meshcullface", "si");    //<fmt>cullface [mesh] [cullface]
    1480            3 :             modelcommand(setcolor, "meshcolor", "sfff");        //<fmt>meshcolor [mesh] [r] [g] [b]
    1481            3 :             modelcommand(setbumpmap, "bumpmap", "ss");          //<fmt>bumpmap [mesh] [tex]
    1482            3 :             modelcommand(setdecal, "decal", "ss");              //<fmt>decal [mesh] [tex]
    1483            3 :             modelcommand(setfullbright, "meshfullbright", "sf");//<fmt>meshfullbright [mesh] [bright]
    1484            3 :             modelcommand(setshader, "meshshader", "ss");        //<fmt>meshshader [mesh] [shader]
    1485            3 :             modelcommand(setscroll, "scroll", "sff");           //<fmt>scroll [mesh] [x] [y]
    1486            3 :             modelcommand(setnoclip, "noclip", "si");            //<fmt>noclip [mesh] [bool]
    1487            3 :             modelcommand(settricollide, "tricollide", "s");     //<fmt>settricollide [mesh]
    1488              :         }
    1489            3 :         if(MDL::multiparted())
    1490              :         {
    1491            3 :             modelcommand(setlink, "link", "iisfff");//<mdl>link [parent] [child] [tag] [x] [y] [z]
    1492              :         }
    1493            3 :     }
    1494              : };
    1495              : 
    1496              : #endif
        

Generated by: LCOV version 2.0-1