Line data Source code
1 : #ifndef ANIMMODEL_H_
2 : #define ANIMMODEL_H_
3 :
4 : extern int testtags, debugcolmesh;
5 :
6 : const std::string modelpath = "media/model/";
7 :
8 : /**
9 : * @brief Animated model object
10 : *
11 : * Generic class for an animated model object, derived from the very
12 : * general model structure
13 : *
14 : * animmodel provides functionality to implement an animated model consisting of
15 : * multiple mesh groups which are treated as a single model
16 : *
17 : * animmodel is extended by skelmodel to allow multipart objects to be rigged by
18 : * a skeleton/bone type animation methodology
19 : */
20 : class animmodel : public model
21 : {
22 : public:
23 :
24 : struct AnimPos final
25 : {
26 : int anim, fr1, fr2;
27 : float t;
28 :
29 : void setframes(const animinfo &info);
30 :
31 : bool operator==(const AnimPos &a) const;
32 : bool operator!=(const AnimPos &a) const;
33 : };
34 :
35 : class part;
36 :
37 : struct AnimState final
38 : {
39 : const part *owner;
40 : AnimPos cur, prev;
41 : float interp;
42 :
43 : bool operator==(const AnimState &a) const;
44 : bool operator!=(const AnimState &a) const;
45 : };
46 :
47 : class Mesh;
48 :
49 : struct shaderparams
50 : {
51 : float spec, gloss, glow, glowdelta, glowpulse, fullbright, scrollu, scrollv, alphatest;
52 : vec color;
53 :
54 :
55 : bool operator==(const animmodel::shaderparams &y) const;
56 : shaderparams();
57 : };
58 :
59 : //An object used to store texture data for a mesh, contained inside a `part` object.
60 : //A `meshgroup` object is paired with a `skin` object to provide texture & geometry
61 : //data for a mesh
62 : class skin final : public shaderparams
63 : {
64 : public:
65 : Texture *tex, *decal;
66 : const Texture *masks, *normalmap;
67 : Shader *shader;
68 : int cullface;
69 :
70 20 : skin(const part *initowner, Texture *inittex, const Texture *initmasks) :
71 20 : tex(inittex),
72 20 : decal(nullptr),
73 20 : masks(initmasks),
74 20 : normalmap(nullptr),
75 20 : shader(nullptr),
76 20 : cullface(1),
77 20 : owner(initowner),
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 final
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 initname, meshgroup *initm) :
167 4 : name(initname),
168 4 : cancollide(true),
169 4 : canrender(true),
170 4 : noclip(false),
171 4 : group(initm)
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 : /**
557 : * @brief Points to another part object dependent on this part.
558 : * The pointed part may be modified in case it is necessary to
559 : * link/unlink the pointed part to other dependent parts.
560 : */
561 : struct linkedpart
562 : {
563 : part *p;
564 : int tag, anim, basetime;
565 : vec translate;
566 : vec *pos; //a pos pointer from a modelattach object, which is set from within game
567 : matrix4 matrix;
568 :
569 : linkedpart() : p(nullptr), tag(-1), anim(-1), basetime(0), translate(0, 0, 0), pos(nullptr) {}
570 0 : linkedpart(part *initp, int inittag, int initanim, int initbasetime, vec inittranslate, vec *initpost, matrix4 initmatrix) :
571 0 : p(initp), tag(inittag), anim(initanim), basetime(initbasetime), translate(inittranslate), pos(initpost), matrix(initmatrix) {}
572 : };
573 : std::vector<linkedpart> links;
574 : std::vector<skin> skins;
575 : int numanimparts;
576 : float pitchscale, pitchoffset, pitchmin, pitchmax;
577 :
578 : /**
579 : * @brief Creates a part object pointing to the given animmodel
580 : *
581 : * Sets this part's animation interpolation index to the value passed.
582 : *
583 : * @param model pointer to a single animmodel
584 : * @param index animation interpolation index to assign
585 : */
586 : part(const animmodel *model, int index = 0);
587 : virtual ~part();
588 : part(const part& a) = delete;
589 : part &operator=(const part &a) = delete;
590 :
591 : /**
592 : * @brief Cleans up the meshgroup and attached skins.
593 : *
594 : * Cleans up the single meshgroup at part::meshes, and cleans up
595 : * every skin in the skin vector. Does not delete the skins nor
596 : * the meshgroup but merely leaves them in a cleaned up state
597 : */
598 : void cleanup();
599 :
600 : /**
601 : * @brief Zeros out the pitch fields in this part.
602 : *
603 : * Sets part::pitchscale, part::pitchoffset, part::pitchmin, and part::pitchmax
604 : * to zero. This means that the part after this is called will have zero pitch
605 : * component (rotation).
606 : */
607 : void disablepitch();
608 :
609 : /**
610 : * @brief Returns to bbmin and bbmax the bounding area of this model
611 : *
612 : * Returns the rectangular bounding box of the model, given its scale
613 : * and transformation.
614 : *
615 : * @param bbmin the -x-y-z corner of the rectangular bounding box
616 : * @param bbmax the +x+y+z cornder of the rectangular bounding box
617 : * @param m the transformation matrix of the model
618 : * @param modelscale the scale factor for the model
619 : */
620 : void calcbb(vec &bbmin, vec &bbmax, const matrix4x3 &m, float modelscale) const;
621 :
622 : /**
623 : * @brief Generates a new BIH for this model and appends it to the back of the passed vector.
624 : *
625 : * Creates a new BIH at the end of the passed vector of BIH meshes and
626 : * sets its parameters to that of this model part with the specified scale
627 : * factor and scale.
628 : *
629 : * @param bih the bounded interval hierarchy for the model
630 : * @param m the transformation matrix of this model
631 : * @param modelscale the scale factor for the model
632 : */
633 : void genBIH(std::vector<BIH::mesh> &bih, const matrix4x3 &m, float modelscale) const;
634 : void genshadowmesh(std::vector<triangle> &tris, const matrix4x3 &m, float modelscale) const;
635 : bool link(part *p, std::string_view tag, const vec &newtranslate = vec(0, 0, 0), int anim = -1, int basetime = 0, vec *pos = nullptr);
636 :
637 : /**
638 : * @brief Removes from this part's linkedpart list the linkedpart corresponding to the passed part
639 : *
640 : * `part::links` is the vector containing the list of linkedparts.
641 : *
642 : * @param p the part to remove the corresponding linkedpart from
643 : */
644 : bool unlink(const part *p);
645 : void initskins(Texture *tex = notexture, Texture *masks = notexture, uint limit = 0);
646 :
647 : /**
648 : * @brief Returns whether this part has an alpha texture enabled.
649 : *
650 : * The alphatest shader parameter must be enabled and this part
651 : * must contain an alpha skin.
652 : *
653 : * @return true if this part has alpha enabled
654 : */
655 : bool alphatested() const;
656 : void preloadBIH() const;
657 : void preloadshaders();
658 :
659 : /**
660 : * @brief Causes the attached meshgroup to run its preload routine
661 : *
662 : * The (single) meshgroup pointed to by this object will preload itself.
663 : * This process includes generating the vertex buffer objects required
664 : * for rendering the meshes.
665 : */
666 : void preloadmeshes();
667 :
668 : void intersect(int anim, int basetime, int basetime2, float pitch, const vec &axis, const vec &forward, dynent *d, const vec &o, const vec &ray);
669 : 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);
670 : void render(int anim, int basetime, int basetime2, float pitch, const vec &axis, const vec &forward, dynent *d);
671 : void render(int anim, int basetime, int basetime2, float pitch, const vec &axis, const vec &forward, dynent *d, AnimState *as);
672 : void setanim(int animpart, int num, int frame, int range, float speed, int priority = 0);
673 :
674 : /**
675 : * @brief Returns true of all animparts for this part are set
676 : *
677 : * Checks that all animspec vectors in part::anims exist and have
678 : * been instantiated. No values must be in the vectors for this to return true;
679 : * they only need to exist.
680 : *
681 : * @return true if all animparts are set, false otherwise
682 : */
683 : bool animated() const;
684 : virtual void loaded();
685 :
686 : private:
687 : struct animspec
688 : {
689 : int frame, range;
690 : float speed;
691 : int priority;
692 : };
693 :
694 : std::vector<animspec> *anims[maxanimparts]; //pointer to array of std::vector<animspec>
695 :
696 : virtual void getdefaultanim(animinfo &info) const;
697 : bool calcanim(int animpart, int anim, int basetime, int basetime2, dynent *d, int interp, animinfo &info, int &animinterptime) const;
698 : };
699 :
700 : std::vector<part *> parts; //vector of part objects heap-allocated by skelmodel::addpart or part::addpart
701 :
702 : //ordinary methods
703 : ~animmodel();
704 : animmodel(const animmodel& a) = delete;
705 : animmodel &operator=(const animmodel &a) = delete;
706 :
707 : /**
708 : * @brief Creates a heap-allocated part object.
709 : *
710 : * This must be `delete`'d or it will cause a memory leak.
711 : *
712 : * @return a reference to the newly created part
713 : */
714 : part &addpart();
715 : /**
716 : * @brief Sets up a transformation matrix based on this model's rotation, translation, and scale
717 : *
718 : * @return a transformation matrix corresponding to the model's transformations
719 : */
720 : matrix4x3 initmatrix() const;
721 : void genBIH(std::vector<BIH::mesh> &bihs);
722 : 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;
723 : void loaded();
724 : bool unlink(const part *p) const;
725 : void render(int anim, int basetime, int basetime2, float pitch, const vec &axis, const vec &forward, dynent *d, modelattach *a) const;
726 :
727 : //virtual methods
728 : /**
729 : * @brief Returns whether this model type requires a flipped Y axis
730 : *
731 : * This function will return true if the model type needs its coordinate
732 : * system transformed to properly render in the engine. GLTF and MD5 do not,
733 : * while OBJ does.
734 : *
735 : * @return true if the model format requires transformation, false otherwise
736 : */
737 : virtual bool flipy() const = 0;
738 : virtual bool loadconfig(const std::string &mdlname) = 0;
739 : virtual bool loaddefaultparts() = 0;
740 : virtual void startload() = 0;
741 : virtual void endload() = 0;
742 :
743 : //model object overrides
744 : 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;
745 : void cleanup() final;
746 : void genshadowmesh(std::vector<triangle> &tris, const matrix4x3 &orient) const final;
747 : void preloadBIH() final;
748 : void setBIH() final;
749 : bool animated() const final;
750 : bool pitched() const final;
751 : bool alphatested() const final;
752 : bool load() final;
753 : void preloadshaders() final;
754 :
755 : /**
756 : * @brief Preloades every entry in the part::meshes array.
757 : *
758 : * No effect if there are no meshes to be loaded.
759 : */
760 : void preloadmeshes() final;
761 :
762 : /**
763 : * @brief Sets the shader to be used by the model
764 : *
765 : * Clears any pre-existing slot parameters related to an existing shader.
766 : *
767 : * If no such shader was found, prints an error message to the console.
768 : *
769 : * @param shader the shader to set
770 : */
771 : void setshader(Shader *shader) final;
772 :
773 : /**
774 : * @brief Sets the specular value for each skin in each part of this model.
775 : *
776 : * Sets how shiny the model is. Each skin in this model will receive the
777 : * same specular value.
778 : */
779 : void setspec(float spec) final;
780 : void setgloss(int gloss) final;
781 : void setglow(float glow, float delta, float pulse) final;
782 : /**
783 : * @brief Sets the alphatest value for each skin in each part of this model
784 : *
785 : * alphatest is one of the shader parameters for the skin shader. Each skin
786 : * in this model will receive the same alphatest value.
787 : *
788 : * @param alphatest the alphatest value to set
789 : */
790 : void setalphatest(float alphatest) final;
791 : void setfullbright(float fullbright) final;
792 : void setcullface(int cullface) final;
793 : void setcolor(const vec &color) final;
794 : void settransformation(const std::optional<vec> pos,
795 : const std::optional<vec> rotate,
796 : const std::optional<vec> orient,
797 : const std::optional<float> size) final;
798 :
799 : /**
800 : * @brief Returns a 4-vector consisting of the translation and scale of the model.
801 : *
802 : * @return translation coordinates in x,y,z; scale factor in w
803 : */
804 : vec4<float> locationsize() const final;
805 : void calcbb(vec ¢er, vec &radius) const final;
806 : void startrender() const final;
807 : void endrender() const final;
808 :
809 : /**
810 : * @brief Recalculates the bounding box parameters and returns them to the parameters passed
811 : *
812 : * Sets the parent `model` object's fields `bbcenter` and `bbradius`.
813 : *
814 : * @param center returns the value of this model's bounding box
815 : * @param radius returns the radius of this model's bounding box
816 : */
817 : void boundbox(vec ¢er, vec &radius) final;
818 : float collisionbox(vec ¢er, vec &radius) final;
819 : const std::string &modelname() const final;
820 : //static methods
821 : static void disablebones();
822 : static void disabletangents();
823 : static void disabletc();
824 : static void disablevbo();
825 :
826 : protected:
827 : enum
828 : {
829 : Link_Tag = 0,
830 : Link_Reuse
831 : };
832 :
833 : animmodel(std::string name);
834 :
835 : /**
836 : * @brief Returns the linkage type of the specified model part.
837 : *
838 : * For animmodels, always returns Link_Tag (see above anim).
839 : * Skeletal models may return Link_Reuse if the passed part shares the same mesh as this part.
840 : *
841 : * @return the linkage type of this part
842 : */
843 : virtual int linktype(const animmodel *, const part *) const;
844 : 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;
845 :
846 : static bool enabletc, enablebones, enabletangents;
847 :
848 : /**
849 : * @brief A stack of transformation matrices used for model intersection math
850 : */
851 : static std::stack<matrix4> matrixstack;
852 : static float sizescale;
853 :
854 : private:
855 : 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;
856 :
857 : static bool enablecullface, enabledepthoffset;
858 : static vec4<float> colorscale;
859 : static GLuint lastvbuf, lasttcbuf, lastxbuf, lastbbuf, lastebuf;
860 : static const Texture *lasttex,
861 : *lastdecal,
862 : *lastmasks,
863 : *lastnormalmap;
864 : };
865 :
866 : /**
867 : * @brief wrapper to load specific format into model object
868 : *
869 : * modelloader is a template for a wrapper to load a model into a model/animmodel
870 : * object from a transactional format, it is intended to be a child template class
871 : * of an animmodel derivative (the BASE template parameter)
872 : *
873 : * SkelLoader is a specialization of this class which uses modelloader to load
874 : * a skeletal model
875 : *
876 : */
877 : template<class MDL, class BASE>
878 : struct modelloader : BASE
879 : {
880 : static MDL *loading;
881 : static std::string dir;
882 :
883 18 : modelloader(std::string initname) : BASE(initname)
884 : {
885 18 : }
886 :
887 2 : static bool cananimate()
888 : {
889 2 : return true;
890 : }
891 :
892 6 : static bool multiparted()
893 : {
894 6 : return true;
895 : }
896 :
897 3 : static bool multimeshed()
898 : {
899 3 : return true;
900 : }
901 :
902 17 : void startload() final
903 : {
904 17 : loading = static_cast<MDL *>(this);
905 17 : }
906 :
907 13 : void endload() final
908 : {
909 13 : loading = nullptr;
910 13 : }
911 :
912 6 : bool loadconfig(const std::string &mdlname) final
913 : {
914 6 : dir.clear();
915 6 : dir.append(modelpath).append(mdlname);
916 6 : std::string cfgname;
917 6 : cfgname.append(modelpath).append(mdlname).append("/").append(MDL::formatname()).append(".cfg");
918 :
919 6 : identflags &= ~Idf_Persist;
920 6 : bool success = execfile(cfgname.c_str(), false);
921 6 : identflags |= Idf_Persist;
922 6 : return success;
923 6 : }
924 : };
925 :
926 : template<class MDL, class BASE>
927 : MDL *modelloader<MDL, BASE>::loading = nullptr;
928 :
929 : template<class MDL, class BASE>
930 : std::string modelloader<MDL, BASE>::dir = "";
931 :
932 : /**
933 : * @brief Template for adding command bindings for a particular model format.
934 : *
935 : * this template class adds a series of commands to the cubescript binding
936 : * adaptable to a specific model type
937 : *
938 : * this template class generates unique command names for each separate model type
939 : * such as objcolor for obj, or md5color for md5 models; they are static and the
940 : * same for any given MDL template parameter
941 : *
942 : * the intended MDL template parameter is one of the model formats (md5, obj, etc)
943 : */
944 : template<class MDL>
945 : struct modelcommands
946 : {
947 : typedef class MDL::part part;
948 : typedef class MDL::skin skin;
949 :
950 69 : static bool checkmdl()
951 : {
952 69 : if(!MDL::loading)
953 : {
954 69 : conoutf(Console_Error, "not loading a model");
955 69 : return false;
956 : }
957 : else
958 : {
959 0 : return true;
960 : }
961 : }
962 :
963 3 : static void mdlcullface(const int *cullface)
964 : {
965 3 : if(!checkmdl())
966 : {
967 3 : return;
968 : }
969 0 : MDL::loading->setcullface(*cullface);
970 : }
971 :
972 3 : static void mdlcolor(const float *r, const float *g, const float *b)
973 : {
974 3 : if(!checkmdl())
975 : {
976 3 : return;
977 : }
978 0 : MDL::loading->setcolor(vec(*r, *g, *b));
979 : }
980 :
981 3 : static void mdlcollide(const int *collide)
982 : {
983 3 : if(!checkmdl())
984 : {
985 3 : return;
986 : }
987 0 : MDL::loading->collide = *collide!=0 ? (MDL::loading->collide ? MDL::loading->collide : Collide_OrientedBoundingBox) : Collide_None;
988 : }
989 :
990 3 : static void mdlellipsecollide(const int *collide)
991 : {
992 3 : if(!checkmdl())
993 : {
994 3 : return;
995 : }
996 0 : MDL::loading->collide = *collide!=0 ? Collide_Ellipse : Collide_None;
997 : }
998 :
999 3 : static void mdltricollide(const char *collide)
1000 : {
1001 3 : if(!checkmdl())
1002 : {
1003 3 : return;
1004 : }
1005 0 : MDL::loading->collidemodel.clear();
1006 0 : char *end = nullptr;
1007 0 : int val = std::strtol(collide, &end, 0);
1008 0 : if(*end)
1009 : {
1010 0 : val = 1;
1011 0 : MDL::loading->collidemodel = std::string(collide);
1012 : }
1013 0 : MDL::loading->collide = val ? Collide_TRI : Collide_None;
1014 : }
1015 :
1016 3 : static void mdlspec(const float *percent)
1017 : {
1018 3 : if(!checkmdl())
1019 : {
1020 3 : return;
1021 : }
1022 0 : float spec = *percent > 0 ? *percent/100.0f : 0.0f;
1023 0 : MDL::loading->setspec(spec);
1024 : }
1025 :
1026 3 : static void mdlgloss(const int *gloss)
1027 : {
1028 3 : if(!checkmdl())
1029 : {
1030 3 : return;
1031 : }
1032 0 : MDL::loading->setgloss(std::clamp(*gloss, 0, 2));
1033 : }
1034 :
1035 3 : static void mdlalphatest(const float *cutoff)
1036 : {
1037 3 : if(!checkmdl())
1038 : {
1039 3 : return;
1040 : }
1041 0 : MDL::loading->setalphatest(std::max(0.0f, std::min(1.0f, *cutoff)));
1042 : }
1043 :
1044 3 : static void mdldepthoffset(const int *offset)
1045 : {
1046 3 : if(!checkmdl())
1047 : {
1048 3 : return;
1049 : }
1050 0 : MDL::loading->depthoffset = *offset!=0;
1051 : }
1052 :
1053 3 : static void mdlglow(const float *percent, const float *delta, const float *pulse)
1054 : {
1055 3 : if(!checkmdl())
1056 : {
1057 3 : return;
1058 : }
1059 0 : float glow = *percent > 0 ? *percent/100.0f : 0.0f,
1060 0 : glowdelta = *delta/100.0f,
1061 0 : glowpulse = *pulse > 0 ? *pulse/1000.0f : 0;
1062 0 : glowdelta -= glow;
1063 0 : MDL::loading->setglow(glow, glowdelta, glowpulse);
1064 : }
1065 :
1066 3 : static void mdlfullbright(const float *fullbright)
1067 : {
1068 3 : if(!checkmdl())
1069 : {
1070 3 : return;
1071 : }
1072 0 : MDL::loading->setfullbright(*fullbright);
1073 : }
1074 :
1075 :
1076 3 : static void mdlshader(const char *shader)
1077 : {
1078 3 : if(!checkmdl())
1079 : {
1080 3 : return;
1081 : }
1082 0 : MDL::loading->setshader(lookupshaderbyname(shader));
1083 : }
1084 :
1085 : //assigns a new spin speed in three euler angles for the model object currently being loaded
1086 3 : static void mdlspin(const float *yaw, const float *pitch, const float *roll)
1087 : {
1088 3 : if(!checkmdl())
1089 : {
1090 3 : return;
1091 : }
1092 0 : MDL::loading->settransformation(std::nullopt, vec(*yaw, *pitch, *roll), std::nullopt, std::nullopt);
1093 : }
1094 :
1095 : //assigns a new scale factor in % for the model object currently being loaded
1096 3 : static void mdlscale(const float *percent)
1097 : {
1098 3 : if(!checkmdl())
1099 : {
1100 3 : return;
1101 : }
1102 0 : float scale = *percent > 0 ? *percent/100.0f : 1.0f;
1103 0 : MDL::loading->settransformation(std::nullopt, std::nullopt, std::nullopt, scale);
1104 : }
1105 :
1106 : //assigns translation in x,y,z in cube units for the model object currently being loaded
1107 3 : static void mdltrans(const float *x, const float *y, const float *z)
1108 : {
1109 3 : if(!checkmdl())
1110 : {
1111 3 : return;
1112 : }
1113 0 : MDL::loading->settransformation(vec(*x, *y, *z), std::nullopt, std::nullopt, std::nullopt);
1114 : }
1115 :
1116 : //assigns angle to the offsetyaw field of the model object currently being loaded
1117 3 : static void mdlyaw(const float *angle)
1118 : {
1119 3 : if(!checkmdl())
1120 : {
1121 3 : return;
1122 : }
1123 0 : MDL::loading->orientation.x = *angle;
1124 : }
1125 :
1126 :
1127 : //assigns angle to the offsetpitch field of the model object currently being loaded
1128 3 : static void mdlpitch(const float *angle)
1129 : {
1130 3 : if(!checkmdl())
1131 : {
1132 3 : return;
1133 : }
1134 0 : MDL::loading->orientation.y = *angle;
1135 : }
1136 :
1137 : //assigns angle to the offsetroll field of the model object currently being loaded
1138 3 : static void mdlroll(const float *angle)
1139 : {
1140 3 : if(!checkmdl())
1141 : {
1142 3 : return;
1143 : }
1144 0 : MDL::loading->orientation.z = *angle;
1145 : }
1146 :
1147 : //assigns shadow to the shadow field of the model object currently being loaded
1148 3 : static void mdlshadow(const int *shadow)
1149 : {
1150 3 : if(!checkmdl())
1151 : {
1152 3 : return;
1153 : }
1154 0 : MDL::loading->shadow = *shadow!=0;
1155 : }
1156 :
1157 : //assigns alphashadow to the alphashadow field of the model object currently being loaded
1158 3 : static void mdlalphashadow(const int *alphashadow)
1159 : {
1160 3 : if(!checkmdl())
1161 : {
1162 3 : return;
1163 : }
1164 0 : MDL::loading->alphashadow = *alphashadow!=0;
1165 : }
1166 :
1167 : //assigns rad, h, eyeheight to the fields of the model object currently being loaded
1168 3 : static void mdlbb(const float *rad, const float *h, const float *eyeheight)
1169 : {
1170 3 : if(!checkmdl())
1171 : {
1172 3 : return;
1173 : }
1174 0 : MDL::loading->collidexyradius = *rad;
1175 0 : MDL::loading->collideheight = *h;
1176 0 : MDL::loading->eyeheight = *eyeheight;
1177 : }
1178 :
1179 3 : static void mdlextendbb(const float *x, const float *y, const float *z)
1180 : {
1181 3 : if(!checkmdl())
1182 : {
1183 3 : return;
1184 : }
1185 0 : MDL::loading->bbextend = vec(*x, *y, *z);
1186 : }
1187 :
1188 : /* mdlname
1189 : *
1190 : * returns the name of the model currently loaded [most recently]
1191 : */
1192 3 : static void mdlname()
1193 : {
1194 3 : if(!checkmdl())
1195 : {
1196 3 : return;
1197 : }
1198 0 : result(MDL::loading->modelname().c_str());
1199 : }
1200 :
1201 14 : static void setdir(const char *name)
1202 : {
1203 14 : if(!MDL::loading)
1204 : {
1205 3 : conoutf("not loading an %s", MDL::formatname());
1206 3 : return;
1207 : }
1208 11 : MDL::dir = std::string(modelpath).append(name ? name : "");
1209 : }
1210 :
1211 : /**
1212 : * @brief Returns an iterator vector of meshes with the given name
1213 : *
1214 : * Returns a vector of MDL::loading->parts.back()'s meshgroup's mesh vector
1215 : * iterators where those iterator's contents' name field compares equal to the
1216 : * passed string. If the wildcard "*" is passed as `meshname` then all elements
1217 : * will be added regardless of name. If no such mesh vector exists (or there is
1218 : * no loading model) then an empty vector is returned.
1219 : *
1220 : * @param meshname the mesh name to select from the mesh vector
1221 : *
1222 : * @return vector of iterators corresponding to meshes with the given name
1223 : */
1224 3 : static std::vector<std::vector<animmodel::Mesh *>::const_iterator> getmeshes(std::string_view meshname)
1225 : {
1226 3 : std::vector<std::vector<animmodel::Mesh *>::const_iterator> meshlist;
1227 3 : if(!MDL::loading || MDL::loading->parts.empty())
1228 : {
1229 3 : conoutf("not loading an %s", MDL::formatname());
1230 3 : return meshlist; //empty vector
1231 : }
1232 0 : const part &mdl = *MDL::loading->parts.back();
1233 0 : if(!mdl.meshes)
1234 : {
1235 0 : return meshlist; //empty vector
1236 : }
1237 0 : meshlist = mdl.meshes->getmeshes(meshname);
1238 0 : return meshlist;
1239 0 : }
1240 :
1241 : /**
1242 : * @brief Returns an iterator vector of skins associated with the given name
1243 : *
1244 : * Returns a vector of MDL::loading->parts.back()'s meshgroup's skin vector
1245 : * iterators where those iterator's contents' name field compares equal to the
1246 : * passed string. If the wildcard "*" is passed as `meshname` then all elements
1247 : * will be added regardless of name. If no such mesh vector exists (or there is
1248 : * no loading model) then an empty vector is returned.
1249 : *
1250 : * @param meshname the mesh name to select from the skin vector
1251 : *
1252 : * @return vector of iterators corresponding to skins with the given name
1253 : */
1254 44 : static std::vector<std::vector<animmodel::skin>::iterator> getskins(std::string_view meshname)
1255 : {
1256 44 : std::vector<std::vector<animmodel::skin>::iterator> skinlist;
1257 44 : if(!MDL::loading || MDL::loading->parts.empty())
1258 : {
1259 36 : conoutf("not loading an %s", MDL::formatname());
1260 36 : return skinlist;
1261 : }
1262 8 : part &mdl = *MDL::loading->parts.back();
1263 8 : if(!mdl.meshes)
1264 : {
1265 0 : return skinlist;
1266 : }
1267 8 : std::vector<size_t> skinindices = mdl.meshes->getskins(meshname);
1268 16 : for(size_t i : skinindices)
1269 : {
1270 8 : skinlist.push_back(mdl.skins.begin() + i);
1271 : }
1272 8 : return skinlist;
1273 8 : }
1274 :
1275 6 : static void setskin(const char *meshname, const char *tex, const char *masks)
1276 : {
1277 6 : std::vector<std::vector<animmodel::skin>::iterator> skinlist = getskins(meshname);
1278 9 : for(std::vector<animmodel::skin>::iterator s : skinlist)
1279 : {
1280 3 : (*s).tex = textureload(makerelpath(MDL::dir.c_str(), tex), 0, true, false);
1281 3 : if(*masks)
1282 : {
1283 3 : (*s).masks = textureload(makerelpath(MDL::dir.c_str(), masks), 0, true, false);
1284 : }
1285 : }
1286 6 : }
1287 :
1288 3 : static void setspec(const char *meshname, const float *percent)
1289 : {
1290 3 : float spec = *percent > 0 ? *percent/100.0f : 0.0f;
1291 3 : std::vector<std::vector<animmodel::skin>::iterator> skinlist = getskins(meshname);
1292 3 : for(std::vector<animmodel::skin>::iterator s : skinlist)
1293 : {
1294 0 : (*s).spec = spec;
1295 : }
1296 3 : }
1297 :
1298 3 : static void setgloss(const char *meshname, const int *gloss)
1299 : {
1300 3 : std::vector<std::vector<animmodel::skin>::iterator> skinlist = getskins(meshname);
1301 3 : for(std::vector<animmodel::skin>::iterator s : skinlist)
1302 : {
1303 0 : (*s).gloss = std::clamp(*gloss, 0, 2);
1304 : }
1305 3 : }
1306 :
1307 3 : static void setglow(const char *meshname, const float *percent, const float *delta, const float *pulse)
1308 : {
1309 3 : float glow = *percent > 0 ? *percent/100.0f : 0.0f,
1310 3 : glowdelta = *delta/100.0f,
1311 3 : glowpulse = *pulse > 0 ? *pulse/1000.0f : 0;
1312 3 : glowdelta -= glow;
1313 3 : auto skinlist = getskins(meshname);
1314 3 : for(auto s : skinlist)
1315 : {
1316 0 : (*s).glow = glow;
1317 0 : (*s).glowdelta = glowdelta;
1318 0 : (*s).glowpulse = glowpulse;
1319 : }
1320 3 : }
1321 :
1322 3 : static void setalphatest(const char *meshname, const float *cutoff)
1323 : {
1324 3 : auto skinlist = getskins(meshname);
1325 3 : for(auto s : skinlist)
1326 : {
1327 0 : (*s).alphatest = std::max(0.0f, std::min(1.0f, *cutoff));
1328 : }
1329 3 : }
1330 :
1331 3 : static void setcullface(const char *meshname, const int *cullface)
1332 : {
1333 3 : auto skinlist = getskins(meshname);
1334 3 : for(auto s : skinlist)
1335 : {
1336 0 : (*s).cullface = *cullface;
1337 : }
1338 3 : }
1339 :
1340 3 : static void setcolor(const char *meshname, const float *r, const float *g, const float *b)
1341 : {
1342 3 : auto skinlist = getskins(meshname);
1343 3 : for(auto s : skinlist)
1344 : {
1345 0 : (*s).color = vec(*r, *g, *b);
1346 : }
1347 3 : }
1348 :
1349 4 : static void setbumpmap(const char *meshname, const char *normalmapfile)
1350 : {
1351 4 : Texture *normalmaptex = textureload(makerelpath(MDL::dir.c_str(), normalmapfile), 0, true, false);
1352 4 : auto skinlist = getskins(meshname);
1353 5 : for(auto s : skinlist)
1354 : {
1355 1 : (*s).normalmap = normalmaptex;
1356 : }
1357 4 : }
1358 :
1359 4 : static void setdecal(const char *meshname, const char *decal)
1360 : {
1361 4 : auto skinlist = getskins(meshname);
1362 5 : for(auto s : skinlist)
1363 : {
1364 1 : (*s).decal = textureload(makerelpath(MDL::dir.c_str(), decal), 0, true, false);
1365 : }
1366 4 : }
1367 :
1368 3 : static void setfullbright(const char *meshname, const float *fullbright)
1369 : {
1370 3 : auto skinlist = getskins(meshname);
1371 3 : for(auto s : skinlist)
1372 : {
1373 0 : (*s).fullbright = *fullbright;
1374 : }
1375 3 : }
1376 :
1377 3 : static void setshader(const char *meshname, const char *shader)
1378 : {
1379 3 : auto skinlist = getskins(meshname);
1380 3 : for(auto s : skinlist)
1381 : {
1382 0 : (*s).shader = lookupshaderbyname(shader);
1383 : }
1384 3 : }
1385 :
1386 3 : static void setscroll(const char *meshname, const float *scrollu, const float *scrollv)
1387 : {
1388 3 : auto skinlist = getskins(meshname);
1389 3 : for(auto s : skinlist)
1390 : {
1391 0 : (*s).scrollu = *scrollu;
1392 0 : (*s).scrollv = *scrollv;
1393 : }
1394 3 : }
1395 :
1396 3 : static void setnoclip(const char *meshname, const int *noclip)
1397 : {
1398 3 : auto meshlist = getmeshes(std::string(meshname));
1399 3 : if(meshlist.empty())
1400 : {
1401 3 : return;
1402 : }
1403 0 : for(auto &i : meshlist)
1404 : {
1405 0 : (*i)->noclip = *noclip!=0;
1406 : }
1407 3 : }
1408 :
1409 0 : static void settricollide(const char *meshname)
1410 : {
1411 0 : bool init = true;
1412 0 : auto meshlist = getmeshes(std::string(meshname));
1413 0 : if(!meshlist.empty())
1414 : {
1415 0 : return;
1416 : }
1417 : else
1418 : {
1419 0 : for(auto &i : meshlist)
1420 : {
1421 0 : if(!(*i)->cancollide)
1422 : {
1423 0 : init = false;
1424 : }
1425 : }
1426 0 : if(init)
1427 : {
1428 0 : for(auto &i : meshlist)
1429 : {
1430 0 : (*i)->cancollide = false;
1431 : }
1432 : }
1433 0 : for(auto &i : meshlist)
1434 : {
1435 0 : (*i)->cancollide = true;
1436 0 : (*i)->canrender = false;
1437 : }
1438 : }
1439 0 : }
1440 :
1441 3 : static void setlink(const int *parent, const int *child, const char *tagname, const float *x, const float *y, const float *z)
1442 : {
1443 3 : if(!MDL::loading)
1444 : {
1445 3 : conoutf("not loading an %s", MDL::formatname());
1446 3 : return;
1447 : }
1448 0 : if(!(static_cast<int>(MDL::loading->parts.size()) > *parent) || !(static_cast<int>(MDL::loading->parts.size()) > *child))
1449 : {
1450 0 : conoutf("no models loaded to link");
1451 0 : return;
1452 : }
1453 0 : if(!MDL::loading->parts[*parent]->link(MDL::loading->parts[*child], tagname, vec(*x, *y, *z)))
1454 : {
1455 0 : conoutf("could not link model %s", MDL::loading->modelname().c_str());
1456 : }
1457 : }
1458 :
1459 : template<class F>
1460 152 : void modelcommand(F *fun, std::string_view suffix, std::string_view args)
1461 : {
1462 152 : std::string name;
1463 152 : name.append(MDL::formatname()).append(suffix);
1464 152 : addcommand(newstring(name.c_str()), reinterpret_cast<identfun>(fun), args.data());
1465 152 : }
1466 :
1467 3 : modelcommands()
1468 : {
1469 3 : modelcommand(setdir, "dir", "s");//<fmt>dir [name]
1470 3 : if(MDL::multimeshed())
1471 : {
1472 3 : modelcommand(mdlcullface, "cullface", "i");
1473 3 : modelcommand(mdlcolor, "color", "fff");
1474 3 : modelcommand(mdlcollide, "collide", "i");
1475 3 : modelcommand(mdlellipsecollide, "ellipsecollide", "i");
1476 3 : modelcommand(mdltricollide, "tricollide", "s");
1477 3 : modelcommand(mdlspec, "spec", "f");
1478 3 : modelcommand(mdlgloss, "gloss", "i");
1479 3 : modelcommand(mdlalphatest, "alphatest", "f");
1480 3 : modelcommand(mdldepthoffset, "depthoffset", "i");
1481 3 : modelcommand(mdlglow, "glow", "fff");
1482 3 : modelcommand(mdlfullbright, "fullbright", "f");
1483 3 : modelcommand(mdlshader, "shader", "s");
1484 3 : modelcommand(mdlspin, "spin", "fff");
1485 3 : modelcommand(mdlscale, "scale", "f");
1486 3 : modelcommand(mdltrans, "trans", "fff");
1487 3 : modelcommand(mdlyaw, "yaw", "f");
1488 3 : modelcommand(mdlpitch, "pitch", "f");
1489 3 : modelcommand(mdlroll, "roll", "f");
1490 3 : modelcommand(mdlshadow, "shadow", "i");
1491 3 : modelcommand(mdlalphashadow, "alphashadow", "i");
1492 3 : modelcommand(mdlbb, "bb", "fff");
1493 3 : modelcommand(mdlextendbb, "extendbb", "fff");
1494 3 : modelcommand(mdlname, "name", "");
1495 :
1496 3 : modelcommand(setskin, "skin", "sss"); //<fmt>skin [meshname] [tex] [masks]
1497 3 : modelcommand(setspec, "texspec", "sf"); //<fmt>texspec [mesh] [scale]
1498 3 : modelcommand(setgloss, "texgloss", "si"); //<fmt>texgloss [mesh] [type] type ranges 0..2
1499 3 : modelcommand(setglow, "texglow", "sfff"); //<fmt>texglow [mesh] [pct] [del] [pulse]
1500 3 : modelcommand(setalphatest, "meshalphatest", "sf"); //<fmt>meshalphatest [mesh] [cutoff]
1501 3 : modelcommand(setcullface, "meshcullface", "si"); //<fmt>cullface [mesh] [cullface]
1502 3 : modelcommand(setcolor, "meshcolor", "sfff"); //<fmt>meshcolor [mesh] [r] [g] [b]
1503 3 : modelcommand(setbumpmap, "bumpmap", "ss"); //<fmt>bumpmap [mesh] [tex]
1504 3 : modelcommand(setdecal, "decal", "ss"); //<fmt>decal [mesh] [tex]
1505 3 : modelcommand(setfullbright, "meshfullbright", "sf");//<fmt>meshfullbright [mesh] [bright]
1506 3 : modelcommand(setshader, "meshshader", "ss"); //<fmt>meshshader [mesh] [shader]
1507 3 : modelcommand(setscroll, "scroll", "sff"); //<fmt>scroll [mesh] [x] [y]
1508 3 : modelcommand(setnoclip, "noclip", "si"); //<fmt>noclip [mesh] [bool]
1509 3 : modelcommand(settricollide, "tricollide", "s"); //<fmt>settricollide [mesh]
1510 : }
1511 3 : if(MDL::multiparted())
1512 : {
1513 3 : modelcommand(setlink, "link", "iisfff");//<mdl>link [parent] [child] [tag] [x] [y] [z]
1514 : }
1515 3 : }
1516 : };
1517 :
1518 : #endif
|