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