Line data Source code
1 : /* animmodel.cpp: implementation for animated models
2 : *
3 : * animmodel.cpp implements the animmodel object in animmodel.h and is the
4 : * working form of an an animated model loaded from a file. The animmodel object
5 : * supports skeletal animation along with the basics it inherits from model.h;
6 : * see that file for non-animated functionality (e.g. normal, specular, etc mapping,
7 : * position, orientation, etc).
8 : *
9 : */
10 : #include "../libprimis-headers/cube.h"
11 : #include "../../shared/geomexts.h"
12 : #include "../../shared/glemu.h"
13 : #include "../../shared/glexts.h"
14 :
15 : #include <memory>
16 : #include <optional>
17 :
18 : #include "interface/console.h"
19 : #include "interface/control.h"
20 :
21 : #include "render/radiancehints.h"
22 : #include "render/rendergl.h"
23 : #include "render/renderlights.h"
24 : #include "render/rendermodel.h"
25 : #include "render/renderparticles.h"
26 : #include "render/shader.h"
27 : #include "render/shaderparam.h"
28 : #include "render/texture.h"
29 :
30 : #include "world/entities.h"
31 : #include "world/bih.h"
32 :
33 : #include "model.h"
34 : #include "ragdoll.h"
35 : #include "animmodel.h"
36 :
37 : //animmodel
38 : VARP(fullbrightmodels, 0, 0, 200); //sets minimum amount of brightness for a model: 200 is 100% brightness
39 : VAR(testtags, 0, 0, 1); //not used by animmodel object, used by children vert/skelmodel
40 0 : VARF(debugcolmesh, 0, 0, 1,
41 : {
42 : cleanupmodels();
43 : });
44 :
45 : VAR(animationinterpolationtime, 0, 200, 1000);
46 :
47 : std::unordered_map<std::string, animmodel::meshgroup *> animmodel::meshgroups;
48 :
49 : bool animmodel::enabletc = false,
50 : animmodel::enabletangents = false,
51 : animmodel::enablebones = false,
52 : animmodel::enablecullface = true,
53 : animmodel::enabledepthoffset = false;
54 :
55 : std::stack<matrix4> animmodel::matrixstack;
56 : float animmodel::sizescale = 1;
57 :
58 : vec4<float> animmodel::colorscale(1, 1, 1, 1);
59 :
60 : GLuint animmodel::lastvbuf = 0,
61 : animmodel::lasttcbuf = 0,
62 : animmodel::lastxbuf = 0,
63 : animmodel::lastbbuf = 0,
64 : animmodel::lastebuf = 0;
65 :
66 : const Texture *animmodel::lasttex = nullptr,
67 : *animmodel::lastdecal = nullptr,
68 : *animmodel::lastmasks = nullptr,
69 : *animmodel::lastnormalmap = nullptr;
70 :
71 : template <>
72 : struct std::hash<animmodel::shaderparams>
73 : {
74 11 : size_t operator()(const animmodel::shaderparams &k) const
75 : {
76 11 : return memhash(&k, sizeof(k));
77 : }
78 : };
79 :
80 : std::unordered_map<animmodel::shaderparams, animmodel::skin::ShaderParamsKey> animmodel::skin::ShaderParamsKey::keys;
81 :
82 : int animmodel::skin::ShaderParamsKey::firstversion = 0,
83 : animmodel::skin::ShaderParamsKey::lastversion = 1;
84 :
85 : //animmodel
86 :
87 20 : animmodel::animmodel(std::string name) : model(std::move(name))
88 : {
89 20 : }
90 :
91 20 : animmodel::~animmodel()
92 : {
93 39 : for(part * i : parts)
94 : {
95 19 : delete i;
96 : }
97 20 : parts.clear();
98 20 : }
99 :
100 : // AnimPos
101 :
102 0 : void animmodel::AnimPos::setframes(const animinfo &info)
103 : {
104 0 : anim = info.anim;
105 0 : if(info.range<=1)
106 : {
107 0 : fr1 = 0;
108 0 : t = 0;
109 : }
110 : else
111 : {
112 0 : int time = info.anim & Anim_SetTime ? info.basetime : lastmillis - info.basetime;
113 0 : fr1 = static_cast<int>(time/info.speed); // round to full frames
114 0 : t = (time-fr1*info.speed)/info.speed; // progress of the frame, value from 0.0f to 1.0f
115 : }
116 0 : if(info.anim & Anim_Loop)
117 : {
118 0 : fr1 = fr1%info.range+info.frame;
119 0 : fr2 = fr1+1;
120 0 : if(fr2>=info.frame+info.range)
121 : {
122 0 : fr2 = info.frame;
123 : }
124 : }
125 : else
126 : {
127 0 : fr1 = std::min(fr1, info.range-1)+info.frame;
128 0 : fr2 = std::min(fr1+1, info.frame+info.range-1);
129 : }
130 0 : if(info.anim & Anim_Reverse)
131 : {
132 0 : fr1 = (info.frame+info.range-1)-(fr1-info.frame);
133 0 : fr2 = (info.frame+info.range-1)-(fr2-info.frame);
134 : }
135 0 : }
136 :
137 6 : bool animmodel::AnimPos::operator==(const AnimPos &a) const
138 : {
139 6 : return fr1==a.fr1 && fr2==a.fr2 && (fr1==fr2 || t==a.t);
140 : }
141 6 : bool animmodel::AnimPos::operator!=(const AnimPos &a) const
142 : {
143 6 : return fr1!=a.fr1 || fr2!=a.fr2 || (fr1!=fr2 && t!=a.t);
144 : }
145 :
146 : // AnimState
147 :
148 3 : bool animmodel::AnimState::operator==(const AnimState &a) const
149 : {
150 3 : return cur==a.cur && (interp<1 ? interp==a.interp && prev==a.prev : a.interp>=1);
151 : }
152 3 : bool animmodel::AnimState::operator!=(const AnimState &a) const
153 : {
154 3 : return cur!=a.cur || (interp<1 ? interp!=a.interp || prev!=a.prev : a.interp<1);
155 : }
156 :
157 : // ShaderParamsKey
158 :
159 0 : bool animmodel::skin::ShaderParamsKey::checkversion()
160 : {
161 0 : if(version >= firstversion)
162 : {
163 0 : return true;
164 : }
165 0 : version = lastversion;
166 0 : if(++lastversion <= 0)
167 : {
168 0 : for(auto &[k, t] : keys)
169 : {
170 0 : t.version = -1;
171 : }
172 0 : firstversion = 0;
173 0 : lastversion = 1;
174 0 : version = 0;
175 : }
176 0 : return false;
177 : }
178 :
179 : //skin
180 :
181 0 : bool animmodel::skin::masked() const
182 : {
183 0 : return masks != notexture;
184 : }
185 :
186 0 : bool animmodel::skin::bumpmapped() const
187 : {
188 0 : return normalmap != nullptr;
189 : }
190 :
191 1 : bool animmodel::skin::alphatested() const
192 : {
193 1 : return alphatest > 0 && tex->type&Texture::ALPHA;
194 : }
195 :
196 0 : bool animmodel::skin::decaled() const
197 : {
198 0 : return decal != nullptr;
199 : }
200 :
201 11 : void animmodel::skin::setkey()
202 : {
203 11 : key = &ShaderParamsKey::keys[*this];
204 11 : }
205 :
206 0 : void animmodel::skin::setshaderparams(const AnimState *as, bool skinned)
207 : {
208 0 : if(!Shader::lastshader)
209 : {
210 0 : return;
211 : }
212 0 : if(key->checkversion() && Shader::lastshader->owner == key)
213 : {
214 0 : return;
215 : }
216 0 : Shader::lastshader->owner = key;
217 :
218 0 : LOCALPARAMF(texscroll, scrollu*lastmillis/1000.0f, scrollv*lastmillis/1000.0f);
219 0 : if(alphatested())
220 : {
221 0 : LOCALPARAMF(alphatest, alphatest);
222 : }
223 :
224 0 : if(!skinned)
225 : {
226 0 : return;
227 : }
228 :
229 0 : if(color.r() < 0)
230 : {
231 0 : LOCALPARAM(colorscale, colorscale);
232 : }
233 : else
234 : {
235 0 : LOCALPARAMF(colorscale, color.r(), color.g(), color.b(), colorscale.a());
236 : }
237 0 : if(fullbright)
238 : {
239 0 : LOCALPARAMF(fullbright, 0.0f, fullbright);
240 : }
241 : else
242 : {
243 0 : LOCALPARAMF(fullbright, 1.0f, as->cur.anim & Anim_FullBright ? 0.5f * fullbrightmodels / 100.0f : 0.0f);
244 : }
245 0 : float curglow = glow;
246 0 : if(glowpulse > 0)
247 : {
248 0 : float curpulse = lastmillis*glowpulse;
249 0 : curpulse -= std::floor(curpulse);
250 0 : curglow += glowdelta*2*std::fabs(curpulse - 0.5f);
251 : }
252 0 : LOCALPARAMF(maskscale, spec, gloss, curglow);
253 : }
254 :
255 0 : Shader *animmodel::skin::loadshader()
256 : {
257 0 : if(shadowmapping == ShadowMap_Reflect)
258 : {
259 0 : if(rsmshader)
260 : {
261 0 : return rsmshader;
262 : }
263 0 : std::string opts;
264 0 : if(alphatested())
265 : {
266 0 : opts.push_back('a');
267 : }
268 0 : if(!cullface)
269 : {
270 0 : opts.push_back('c');
271 : }
272 :
273 0 : DEF_FORMAT_STRING(name, "rsmmodel%s", opts.c_str());
274 0 : rsmshader = generateshader(name, "rsmmodelshader \"%s\"", opts.c_str());
275 0 : return rsmshader;
276 0 : }
277 0 : if(shader)
278 : {
279 0 : return shader;
280 : }
281 0 : std::string opts;
282 0 : if(alphatested())
283 : {
284 0 : opts.push_back('a');
285 : }
286 0 : if(decaled())
287 : {
288 0 : opts.push_back(decal->type&Texture::ALPHA ? 'D' : 'd');
289 : }
290 0 : if(bumpmapped())
291 : {
292 0 : opts.push_back('n');
293 : }
294 0 : else if(masked())
295 : {
296 0 : opts.push_back('m');
297 : }
298 0 : if(!cullface)
299 : {
300 0 : opts.push_back('c');
301 : }
302 :
303 0 : DEF_FORMAT_STRING(name, "model%s", opts.c_str());
304 0 : shader = generateshader(name, "modelshader \"%s\"", opts.c_str());
305 0 : return shader;
306 0 : }
307 :
308 0 : void animmodel::skin::cleanup()
309 : {
310 0 : if(shader && shader->standard)
311 : {
312 0 : shader = nullptr;
313 : }
314 0 : }
315 :
316 1 : void animmodel::skin::preloadBIH() const
317 : {
318 1 : if(alphatested() && !tex->alphamask)
319 : {
320 0 : tex->loadalphamask();
321 : }
322 1 : }
323 :
324 0 : void animmodel::skin::preloadshader()
325 : {
326 0 : loadshader();
327 0 : useshaderbyname(alphatested() && owner->model->alphashadow ? "alphashadowmodel" : "shadowmodel");
328 0 : if(useradiancehints())
329 : {
330 0 : useshaderbyname(alphatested() ? "rsmalphamodel" : "rsmmodel");
331 : }
332 0 : }
333 :
334 0 : void animmodel::skin::setshader(Mesh &m, bool usegpuskel, int vweights)
335 : {
336 0 : m.setshader(loadshader(), usegpuskel, vweights, gbuf.istransparentlayer());
337 0 : }
338 :
339 0 : void animmodel::skin::bind(Mesh &b, const AnimState *as, bool usegpuskel, int vweights)
340 : {
341 0 : if(cullface > 0)
342 : {
343 0 : if(!enablecullface)
344 : {
345 0 : glEnable(GL_CULL_FACE);
346 0 : enablecullface = true;
347 : }
348 : }
349 0 : else if(enablecullface)
350 : {
351 0 : glDisable(GL_CULL_FACE);
352 0 : enablecullface = false;
353 : }
354 :
355 0 : if(as->cur.anim & Anim_NoSkin)
356 : {
357 0 : if(alphatested() && owner->model->alphashadow)
358 : {
359 0 : if(tex!=lasttex)
360 : {
361 0 : glBindTexture(GL_TEXTURE_2D, tex->id);
362 0 : lasttex = tex;
363 : }
364 : static Shader *alphashadowmodelshader = nullptr;
365 0 : if(!alphashadowmodelshader)
366 : {
367 0 : alphashadowmodelshader = useshaderbyname("alphashadowmodel");
368 : }
369 0 : b.setshader(alphashadowmodelshader, usegpuskel, vweights);
370 0 : setshaderparams(as, false);
371 : }
372 : else
373 : {
374 : static Shader *shadowmodelshader = nullptr;
375 0 : if(!shadowmodelshader)
376 : {
377 0 : shadowmodelshader = useshaderbyname("shadowmodel");
378 : }
379 0 : b.setshader(shadowmodelshader, usegpuskel, vweights);
380 : }
381 0 : return;
382 : }
383 0 : int activetmu = 0;
384 0 : if(tex!=lasttex)
385 : {
386 0 : glBindTexture(GL_TEXTURE_2D, tex->id);
387 0 : lasttex = tex;
388 : }
389 0 : if(bumpmapped() && normalmap!=lastnormalmap)
390 : {
391 0 : glActiveTexture(GL_TEXTURE3);
392 0 : activetmu = 3;
393 0 : glBindTexture(GL_TEXTURE_2D, normalmap->id);
394 0 : lastnormalmap = normalmap;
395 : }
396 0 : if(decaled() && decal!=lastdecal)
397 : {
398 0 : glActiveTexture(GL_TEXTURE4);
399 0 : activetmu = 4;
400 0 : glBindTexture(GL_TEXTURE_2D, decal->id);
401 0 : lastdecal = decal;
402 : }
403 0 : if(masked() && masks!=lastmasks)
404 : {
405 0 : glActiveTexture(GL_TEXTURE1);
406 0 : activetmu = 1;
407 0 : glBindTexture(GL_TEXTURE_2D, masks->id);
408 0 : lastmasks = masks;
409 : }
410 0 : if(activetmu != 0)
411 : {
412 0 : glActiveTexture(GL_TEXTURE0);
413 : }
414 0 : setshader(b, usegpuskel, vweights);
415 0 : setshaderparams(as);
416 : }
417 :
418 0 : void animmodel::skin::invalidateshaderparams()
419 : {
420 0 : ShaderParamsKey::invalidate();
421 0 : }
422 :
423 : //Mesh
424 :
425 0 : void animmodel::Mesh::genBIH(const skin &s, std::vector<BIH::mesh> &bih, const matrix4x3 &t) const
426 : {
427 0 : bih.emplace_back();
428 0 : BIH::mesh &m = bih.back();
429 0 : m.xform = t;
430 0 : m.tex = s.tex;
431 0 : if(canrender)
432 : {
433 0 : m.flags |= BIH::Mesh_Render;
434 : }
435 0 : if(cancollide)
436 : {
437 0 : m.flags |= BIH::Mesh_Collide;
438 : }
439 0 : if(s.alphatested())
440 : {
441 0 : m.flags |= BIH::Mesh_Alpha;
442 : }
443 0 : if(noclip)
444 : {
445 0 : m.flags |= BIH::Mesh_NoClip;
446 : }
447 0 : if(s.cullface > 0)
448 : {
449 0 : m.flags |= BIH::Mesh_CullFace;
450 : }
451 0 : genBIH(m);
452 0 : while(bih.back().numtris > BIH::mesh::maxtriangles)
453 : {
454 0 : bih.push_back(bih.back());
455 0 : BIH::mesh &overflow = bih.back();
456 0 : overflow.tris += BIH::mesh::maxtriangles;
457 0 : overflow.numtris -= BIH::mesh::maxtriangles;
458 0 : bih[bih.size()-2].numtris = BIH::mesh::maxtriangles;
459 : }
460 0 : }
461 :
462 438 : void animmodel::Mesh::fixqtangent(quat &q, float bt)
463 : {
464 : static constexpr float bias = -1.5f/65535;
465 : static const float biasscale = sqrtf(1 - bias*bias); //cannot be constexpr, sqrtf is not compile time
466 438 : if(bt < 0)
467 : {
468 23 : if(q.w >= 0)
469 : {
470 21 : q.neg();
471 : }
472 23 : if(q.w > bias)
473 : {
474 0 : q.mul3(biasscale);
475 0 : q.w = bias;
476 : }
477 : }
478 415 : else if(q.w < 0)
479 : {
480 71 : q.neg();
481 : }
482 438 : }
483 :
484 : //meshgroup
485 :
486 4 : animmodel::meshgroup::meshgroup()
487 : {
488 4 : }
489 :
490 2 : animmodel::meshgroup::~meshgroup()
491 : {
492 2 : for(Mesh * i : meshes)
493 : {
494 0 : delete i;
495 : }
496 2 : meshes.clear();
497 2 : }
498 :
499 0 : std::vector<std::vector<animmodel::Mesh *>::const_iterator> animmodel::meshgroup::getmeshes(std::string_view meshname) const
500 : {
501 0 : std::vector<std::vector<animmodel::Mesh *>::const_iterator> meshlist;
502 0 : for(std::vector<animmodel::Mesh *>::const_iterator i = meshes.begin(); i != meshes.end(); ++i)
503 : {
504 0 : const animmodel::Mesh &tempmesh = **i;
505 0 : if((meshname == "*") || (tempmesh.name.size() && (tempmesh.name == meshname)))
506 : {
507 0 : meshlist.push_back(i);
508 : }
509 : }
510 0 : return meshlist;
511 0 : }
512 :
513 8 : std::vector<size_t> animmodel::meshgroup::getskins(std::string_view meshname) const
514 : {
515 8 : std::vector<size_t> skinlist;
516 16 : for(uint i = 0; i < meshes.size(); i++)
517 : {
518 8 : const animmodel::Mesh &m = *(meshes[i]);
519 8 : if((meshname == "*") || (m.name.size() && (m.name == meshname)))
520 : {
521 8 : skinlist.push_back(i);
522 : }
523 : }
524 8 : return skinlist;
525 0 : }
526 :
527 0 : void animmodel::meshgroup::calcbb(vec &bbmin, vec &bbmax, const matrix4x3 &t) const
528 : {
529 0 : auto rendermeshes = getrendermeshes();
530 0 : for(auto i : rendermeshes)
531 : {
532 0 : (*i)->calcbb(bbmin, bbmax, t);
533 : }
534 0 : }
535 :
536 0 : void animmodel::meshgroup::genBIH(const std::vector<skin> &skins, std::vector<BIH::mesh> &bih, const matrix4x3 &t) const
537 : {
538 0 : for(uint i = 0; i < meshes.size(); i++)
539 : {
540 0 : meshes[i]->genBIH(skins[i], bih, t);
541 : }
542 0 : }
543 :
544 0 : void animmodel::meshgroup::genshadowmesh(std::vector<triangle> &tris, const matrix4x3 &t) const
545 : {
546 0 : auto rendermeshes = getrendermeshes();
547 0 : for(auto i : rendermeshes)
548 : {
549 0 : (*i)->genshadowmesh(tris, t);
550 : }
551 0 : }
552 :
553 0 : bool animmodel::meshgroup::hasframe(int i) const
554 : {
555 0 : return i>=0 && i<totalframes();
556 : }
557 :
558 2 : bool animmodel::meshgroup::hasframes(int i, int n) const
559 : {
560 2 : return i>=0 && i+n<=totalframes();
561 : }
562 :
563 0 : int animmodel::meshgroup::clipframes(int i, int n) const
564 : {
565 0 : return std::min(n, totalframes() - i);
566 : }
567 :
568 1 : const std::string &animmodel::meshgroup::groupname() const
569 : {
570 1 : return name;
571 : }
572 :
573 0 : std::vector<std::vector<animmodel::Mesh *>::const_iterator> animmodel::meshgroup::getrendermeshes() const
574 : {
575 0 : std::vector<std::vector<animmodel::Mesh *>::const_iterator> rendermeshes;
576 0 : for(std::vector<animmodel::Mesh *>::const_iterator i = meshes.begin(); i != meshes.end(); ++i)
577 : {
578 0 : if((*i)->canrender || debugcolmesh)
579 : {
580 0 : rendermeshes.push_back(i);
581 : }
582 : }
583 0 : return rendermeshes;
584 0 : }
585 :
586 : //identical to above but non-const iterator and non const this
587 1 : std::vector<std::vector<animmodel::Mesh *>::iterator> animmodel::meshgroup::getrendermeshes()
588 : {
589 1 : std::vector<std::vector<animmodel::Mesh *>::iterator> rendermeshes;
590 2 : for(std::vector<animmodel::Mesh *>::iterator i = meshes.begin(); i != meshes.end(); ++i)
591 : {
592 1 : if((*i)->canrender || debugcolmesh)
593 : {
594 1 : rendermeshes.push_back(i);
595 : }
596 : }
597 1 : return rendermeshes;
598 0 : }
599 :
600 0 : void animmodel::meshgroup::bindpos(GLuint ebuf, GLuint vbuf, const void *v, int stride, int type, int size)
601 : {
602 0 : if(lastebuf!=ebuf)
603 : {
604 0 : gle::bindebo(ebuf);
605 0 : lastebuf = ebuf;
606 : }
607 0 : if(lastvbuf!=vbuf)
608 : {
609 0 : gle::bindvbo(vbuf);
610 0 : if(!lastvbuf)
611 : {
612 0 : gle::enablevertex();
613 : }
614 0 : gle::vertexpointer(stride, v, type, size);
615 0 : lastvbuf = vbuf;
616 : }
617 0 : }
618 0 : void animmodel::meshgroup::bindpos(GLuint ebuf, GLuint vbuf, const vec *v, int stride)
619 : {
620 0 : bindpos(ebuf, vbuf, v, stride, GL_FLOAT, 3);
621 0 : }
622 :
623 0 : void animmodel::meshgroup::bindpos(GLuint ebuf, GLuint vbuf, const vec4<half> *v, int stride)
624 : {
625 0 : bindpos(ebuf, vbuf, v, stride, GL_HALF_FLOAT, 4);
626 0 : }
627 :
628 0 : void animmodel::meshgroup::bindtc(const void *v, int stride)
629 : {
630 0 : if(!enabletc)
631 : {
632 0 : gle::enabletexcoord0();
633 0 : enabletc = true;
634 : }
635 0 : if(lasttcbuf!=lastvbuf)
636 : {
637 0 : gle::texcoord0pointer(stride, v, GL_HALF_FLOAT);
638 0 : lasttcbuf = lastvbuf;
639 : }
640 0 : }
641 :
642 0 : void animmodel::meshgroup::bindtangents(const void *v, int stride)
643 : {
644 0 : if(!enabletangents)
645 : {
646 0 : gle::enabletangent();
647 0 : enabletangents = true;
648 : }
649 0 : if(lastxbuf!=lastvbuf)
650 : {
651 0 : gle::tangentpointer(stride, v, GL_SHORT);
652 0 : lastxbuf = lastvbuf;
653 : }
654 0 : }
655 :
656 0 : void animmodel::meshgroup::bindbones(const void *wv, const void *bv, int stride)
657 : {
658 0 : if(!enablebones)
659 : {
660 0 : gle::enableboneweight();
661 0 : gle::enableboneindex();
662 0 : enablebones = true;
663 : }
664 0 : if(lastbbuf!=lastvbuf)
665 : {
666 0 : gle::boneweightpointer(stride, wv);
667 0 : gle::boneindexpointer(stride, bv);
668 0 : lastbbuf = lastvbuf;
669 : }
670 0 : }
671 :
672 : //part
673 :
674 19 : animmodel::part::part(const animmodel *model, int index) : model(model), index(index), meshes(nullptr), numanimparts(1), pitchscale(1), pitchoffset(0), pitchmin(0), pitchmax(0)
675 : {
676 76 : for(int i = 0; i < maxanimparts; ++i)
677 : {
678 57 : anims[i] = nullptr;
679 : }
680 19 : }
681 :
682 19 : animmodel::part::~part()
683 : {
684 76 : for(int i = 0; i < maxanimparts; ++i)
685 : {
686 59 : delete[] anims[i];
687 : }
688 19 : }
689 :
690 0 : void animmodel::part::cleanup()
691 : {
692 0 : if(meshes)
693 : {
694 0 : meshes->cleanup();
695 : }
696 0 : for(skin &i : skins)
697 : {
698 0 : i.cleanup();
699 : }
700 0 : }
701 :
702 11 : void animmodel::part::disablepitch()
703 : {
704 11 : pitchscale = pitchoffset = pitchmin = pitchmax = 0;
705 11 : }
706 :
707 0 : void animmodel::part::calcbb(vec &bbmin, vec &bbmax, const matrix4x3 &m, float modelscale) const
708 : {
709 0 : matrix4x3 t = m;
710 0 : t.scale(modelscale);
711 0 : meshes->calcbb(bbmin, bbmax, t);
712 0 : for(const linkedpart &i : links)
713 : {
714 0 : matrix4x3 n;
715 0 : meshes->concattagtransform(i.tag, m, n);
716 0 : n.translate(i.translate, modelscale);
717 0 : i.p->calcbb(bbmin, bbmax, n, modelscale);
718 : }
719 0 : }
720 :
721 0 : void animmodel::part::genBIH(std::vector<BIH::mesh> &bih, const matrix4x3 &m, float modelscale) const
722 : {
723 0 : matrix4x3 t = m;
724 0 : t.scale(modelscale);
725 0 : meshes->genBIH(skins, bih, t);
726 0 : for(const linkedpart &i : links)
727 : {
728 0 : matrix4x3 n;
729 0 : meshes->concattagtransform(i.tag, m, n);
730 0 : n.translate(i.translate, modelscale);
731 0 : i.p->genBIH(bih, n, modelscale);
732 : }
733 0 : }
734 :
735 0 : void animmodel::part::genshadowmesh(std::vector<triangle> &tris, const matrix4x3 &m, float modelscale) const
736 : {
737 0 : matrix4x3 t = m;
738 0 : t.scale(modelscale);
739 0 : meshes->genshadowmesh(tris, t);
740 0 : for(const linkedpart &i : links)
741 : {
742 0 : matrix4x3 n;
743 0 : meshes->concattagtransform(i.tag, m, n);
744 0 : n.translate(i.translate, modelscale);
745 0 : i.p->genshadowmesh(tris, n, modelscale);
746 : }
747 0 : }
748 :
749 0 : bool animmodel::part::link(part *p, std::string_view tag, const vec &translate, int anim, int basetime, vec *pos)
750 : {
751 0 : std::optional<size_t> i = meshes ? meshes->findtag(tag) : std::nullopt;
752 0 : if(i<0)
753 : {
754 0 : for(const linkedpart &i : links)
755 : {
756 0 : if(i.p && i.p->link(p, tag, translate, anim, basetime, pos))
757 : {
758 0 : return true;
759 : }
760 : }
761 0 : return false;
762 : }
763 0 : links.emplace_back(p, *i, anim, basetime, translate, pos, matrix4());
764 0 : return true;
765 : }
766 :
767 0 : bool animmodel::part::unlink(const part *p)
768 : {
769 0 : for(int i = links.size(); --i >=0;) //note reverse iteration
770 : {
771 0 : if(links[i].p==p)
772 : {
773 0 : links.erase(links.begin() + i);
774 0 : return true;
775 : }
776 : }
777 0 : for(const linkedpart &i : links)
778 : {
779 0 : if(i.p && i.p->unlink(p))
780 : {
781 0 : return true;
782 : }
783 : }
784 0 : return false;
785 : }
786 :
787 21 : void animmodel::part::initskins(Texture *tex, Texture *masks, uint limit)
788 : {
789 21 : if(!limit)
790 : {
791 20 : if(!meshes)
792 : {
793 0 : return;
794 : }
795 20 : limit = meshes->meshes.size();
796 : }
797 38 : while(skins.size() < limit)
798 : {
799 17 : skins.emplace_back(this, tex, masks);
800 : }
801 : }
802 :
803 0 : bool animmodel::part::alphatested() const
804 : {
805 0 : for(const skin &i : skins)
806 : {
807 0 : if(i.alphatested())
808 : {
809 0 : return true;
810 : }
811 : }
812 0 : return false;
813 : }
814 :
815 1 : void animmodel::part::preloadBIH() const
816 : {
817 2 : for(const skin &i : skins)
818 : {
819 1 : i.preloadBIH();
820 : }
821 1 : }
822 :
823 0 : void animmodel::part::preloadshaders()
824 : {
825 0 : for(skin &i : skins)
826 : {
827 0 : i.preloadshader();
828 : }
829 0 : }
830 :
831 0 : void animmodel::part::preloadmeshes()
832 : {
833 0 : if(meshes)
834 : {
835 0 : meshes->preload();
836 : }
837 0 : }
838 :
839 0 : void animmodel::part::getdefaultanim(animinfo &info) const
840 : {
841 0 : info.frame = 0;
842 0 : info.range = 1;
843 0 : }
844 :
845 0 : bool animmodel::part::calcanim(int animpart, int anim, int basetime, int basetime2, dynent *d, int interp, animinfo &info, int &animinterptime) const
846 : {
847 : //varseed uses an UGLY reinterpret cast from a pointer address to a size_t int
848 : //presumably the address should be a fairly random value
849 0 : uint varseed = static_cast<uint>(reinterpret_cast<size_t>(d));
850 0 : info.anim = anim;
851 0 : info.basetime = basetime;
852 0 : info.varseed = varseed;
853 0 : info.speed = anim & Anim_SetSpeed ? basetime2 : 100.0f;
854 0 : if((anim & Anim_Index) == Anim_All)
855 : {
856 0 : info.frame = 0;
857 0 : info.range = meshes->totalframes();
858 : }
859 : else
860 : {
861 0 : const animspec *spec = nullptr;
862 0 : if(anims[animpart])
863 : {
864 0 : const std::vector<animspec> &primary = anims[animpart][anim & Anim_Index];
865 0 : if(primary.size())
866 : {
867 0 : spec = &primary[(varseed + static_cast<uint>(basetime))%primary.size()];
868 : }
869 0 : if((anim >> Anim_Secondary) & (Anim_Index | Anim_Dir))
870 : {
871 0 : const std::vector<animspec> &secondary = anims[animpart][(anim >> Anim_Secondary) & Anim_Index];
872 0 : if(secondary.size())
873 : {
874 0 : const animspec &spec2 = secondary[(varseed + static_cast<uint>(basetime2))%secondary.size()];
875 0 : if(!spec || spec2.priority > spec->priority)
876 : {
877 0 : spec = &spec2;
878 0 : info.anim >>= Anim_Secondary;
879 0 : info.basetime = basetime2;
880 : }
881 : }
882 : }
883 : }
884 0 : if(spec)
885 : {
886 0 : info.frame = spec->frame;
887 0 : info.range = spec->range;
888 0 : if(spec->speed>0)
889 : {
890 0 : info.speed = 1000.0f/spec->speed;
891 : }
892 : }
893 : else
894 : {
895 0 : getdefaultanim(info);
896 : }
897 : }
898 :
899 0 : info.anim &= (1 << Anim_Secondary) - 1;
900 0 : info.anim |= anim & Anim_Flags;
901 0 : if(info.anim & Anim_Loop)
902 : {
903 0 : info.anim &= ~Anim_SetTime;
904 0 : if(!info.basetime)
905 : {
906 0 : info.basetime = -(static_cast<int>(reinterpret_cast<size_t>(d)) & 0xFFF);
907 : }
908 0 : if(info.anim & Anim_Clamp)
909 : {
910 0 : if(info.anim & Anim_Reverse)
911 : {
912 0 : info.frame += info.range-1;
913 : }
914 0 : info.range = 1;
915 : }
916 : }
917 :
918 0 : if(!meshes->hasframes(info.frame, info.range))
919 : {
920 0 : if(!meshes->hasframe(info.frame))
921 : {
922 0 : return false;
923 : }
924 0 : info.range = meshes->clipframes(info.frame, info.range);
925 : }
926 :
927 0 : if(d && interp>=0)
928 : {
929 0 : animinterpinfo &animationinterpolation = d->animinterp[interp];
930 0 : if((info.anim&(Anim_Loop | Anim_Clamp)) == Anim_Clamp)
931 : {
932 0 : animinterptime = std::min(animinterptime, static_cast<int>(info.range*info.speed*0.5e-3f));
933 : }
934 0 : void *ak = meshes->animkey();
935 0 : if(d->ragdoll && d->ragdoll->millis != lastmillis)
936 : {
937 0 : animationinterpolation.prev.range = animationinterpolation.cur.range = 0;
938 0 : animationinterpolation.lastswitch = -1;
939 : }
940 0 : else if(animationinterpolation.lastmodel!=ak || animationinterpolation.lastswitch<0 || lastmillis-d->lastrendered>animinterptime)
941 : {
942 0 : animationinterpolation.prev = animationinterpolation.cur = info;
943 0 : animationinterpolation.lastswitch = lastmillis-animinterptime*2;
944 : }
945 0 : else if(animationinterpolation.cur!=info)
946 : {
947 0 : if(lastmillis-animationinterpolation.lastswitch>animinterptime/2)
948 : {
949 0 : animationinterpolation.prev = animationinterpolation.cur;
950 : }
951 0 : animationinterpolation.cur = info;
952 0 : animationinterpolation.lastswitch = lastmillis;
953 : }
954 0 : else if(info.anim & Anim_SetTime)
955 : {
956 0 : animationinterpolation.cur.basetime = info.basetime;
957 : }
958 0 : animationinterpolation.lastmodel = ak;
959 : }
960 0 : return true;
961 : }
962 :
963 0 : void animmodel::part::intersect(int anim, int basetime, int basetime2, float pitch, const vec &axis, const vec &forward, dynent *d, const vec &o, const vec &ray)
964 : {
965 : AnimState as[maxanimparts];
966 0 : intersect(anim, basetime, basetime2, pitch, axis, forward, d, o, ray, as);
967 0 : }
968 :
969 0 : void animmodel::part::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)
970 : {
971 0 : if((anim & Anim_Reuse) != Anim_Reuse)
972 : {
973 0 : for(int i = 0; i < numanimparts; ++i)
974 : {
975 0 : animinfo info;
976 0 : int interp = d && index+numanimparts<=maxanimparts ? index+i : -1,
977 0 : animinterptime = animationinterpolationtime;
978 0 : if(!calcanim(i, anim, basetime, basetime2, d, interp, info, animinterptime))
979 : {
980 0 : return;
981 : }
982 0 : AnimState &p = as[i];
983 0 : p.owner = this;
984 0 : p.cur.setframes(info);
985 0 : p.interp = 1;
986 0 : if(interp>=0 && d->animinterp[interp].prev.range>0)
987 : {
988 0 : int diff = lastmillis-d->animinterp[interp].lastswitch;
989 0 : if(diff<animinterptime)
990 : {
991 0 : p.prev.setframes(d->animinterp[interp].prev);
992 0 : p.interp = diff/static_cast<float>(animinterptime);
993 : }
994 : }
995 : }
996 : }
997 :
998 0 : float resize = model->scale * sizescale;
999 0 : size_t oldpos = matrixstack.size();
1000 0 : vec oaxis, oforward, oo, oray;
1001 0 : matrixstack.top().transposedtransformnormal(axis, oaxis);
1002 0 : float pitchamount = pitchscale*pitch + pitchoffset;
1003 0 : if(pitchmin || pitchmax)
1004 : {
1005 0 : pitchamount = std::clamp(pitchamount, pitchmin, pitchmax);
1006 : }
1007 0 : if(as->cur.anim & Anim_NoPitch || (as->interp < 1 && as->prev.anim & Anim_NoPitch))
1008 : {
1009 0 : pitchamount *= (as->cur.anim & Anim_NoPitch ? 0 : as->interp) + (as->interp < 1 && as->prev.anim & Anim_NoPitch ? 0 : 1 - as->interp);
1010 : }
1011 0 : if(pitchamount)
1012 : {
1013 0 : matrixstack.push(matrixstack.top());
1014 0 : matrixstack.top().rotate(pitchamount/RAD, oaxis);
1015 : }
1016 0 : if(this == model->parts[0] && !model->translate.iszero())
1017 : {
1018 0 : if(oldpos == matrixstack.size())
1019 : {
1020 0 : matrixstack.push(matrixstack.top());
1021 : }
1022 0 : matrixstack.top().translate(model->translate, resize);
1023 : }
1024 0 : matrixstack.top().transposedtransformnormal(forward, oforward);
1025 0 : matrixstack.top().transposedtransform(o, oo);
1026 0 : oo.div(resize);
1027 0 : matrixstack.top().transposedtransformnormal(ray, oray);
1028 :
1029 0 : if((anim & Anim_Reuse) != Anim_Reuse)
1030 : {
1031 0 : for(linkedpart &link : links)
1032 : {
1033 0 : if(!link.p)
1034 : {
1035 0 : continue;
1036 : }
1037 0 : link.matrix.translate(link.translate, resize);
1038 0 : matrix4 mul;
1039 0 : mul.mul(matrixstack.top(), link.matrix);
1040 0 : matrixstack.push(mul);
1041 :
1042 0 : int nanim = anim,
1043 0 : nbasetime = basetime,
1044 0 : nbasetime2 = basetime2;
1045 0 : if(link.anim>=0)
1046 : {
1047 0 : nanim = link.anim | (anim & Anim_Flags);
1048 0 : nbasetime = link.basetime;
1049 0 : nbasetime2 = 0;
1050 : }
1051 0 : link.p->intersect(nanim, nbasetime, nbasetime2, pitch, axis, forward, d, o, ray);
1052 :
1053 0 : matrixstack.pop();
1054 : }
1055 : }
1056 :
1057 0 : while(matrixstack.size() > oldpos)
1058 : {
1059 0 : matrixstack.pop();
1060 : }
1061 : }
1062 :
1063 0 : void animmodel::part::render(int anim, int basetime, int basetime2, float pitch, const vec &axis, const vec &forward, dynent *d)
1064 : {
1065 : AnimState as[maxanimparts];
1066 0 : render(anim, basetime, basetime2, pitch, axis, forward, d, as);
1067 0 : }
1068 :
1069 0 : void animmodel::part::render(int anim, int basetime, int basetime2, float pitch, const vec &axis, const vec &forward, dynent *d, AnimState *as)
1070 : {
1071 0 : if((anim & Anim_Reuse) != Anim_Reuse)
1072 : {
1073 0 : for(int i = 0; i < numanimparts; ++i)
1074 : {
1075 0 : animinfo info;
1076 0 : int interp = d && index+numanimparts<=maxanimparts ? index+i : -1, animinterptime = animationinterpolationtime;
1077 0 : if(!calcanim(i, anim, basetime, basetime2, d, interp, info, animinterptime))
1078 : {
1079 0 : return;
1080 : }
1081 0 : AnimState &p = as[i];
1082 0 : p.owner = this;
1083 0 : p.cur.setframes(info);
1084 0 : p.interp = 1;
1085 0 : if(interp>=0 && d->animinterp[interp].prev.range>0)
1086 : {
1087 0 : int diff = lastmillis-d->animinterp[interp].lastswitch;
1088 0 : if(diff<animinterptime)
1089 : {
1090 0 : p.prev.setframes(d->animinterp[interp].prev);
1091 0 : p.interp = diff/static_cast<float>(animinterptime);
1092 : }
1093 : }
1094 : }
1095 : }
1096 :
1097 0 : float resize = model->scale * sizescale;
1098 0 : size_t oldpos = matrixstack.size();
1099 0 : vec oaxis, oforward;
1100 0 : matrixstack.top().transposedtransformnormal(axis, oaxis);
1101 0 : float pitchamount = pitchscale*pitch + pitchoffset;
1102 0 : if(pitchmin || pitchmax)
1103 : {
1104 0 : pitchamount = std::clamp(pitchamount, pitchmin, pitchmax);
1105 : }
1106 0 : if(as->cur.anim & Anim_NoPitch || (as->interp < 1 && as->prev.anim & Anim_NoPitch))
1107 : {
1108 0 : pitchamount *= (as->cur.anim & Anim_NoPitch ? 0 : as->interp) + (as->interp < 1 && as->prev.anim & Anim_NoPitch ? 0 : 1 - as->interp);
1109 : }
1110 0 : if(pitchamount)
1111 : {
1112 0 : matrixstack.push(matrixstack.top());
1113 0 : matrixstack.top().rotate(pitchamount/RAD, oaxis);
1114 : }
1115 0 : if(this == model->parts[0] && !model->translate.iszero())
1116 : {
1117 0 : if(oldpos == matrixstack.size())
1118 : {
1119 0 : matrixstack.push(matrixstack.top());
1120 : }
1121 0 : matrixstack.top().translate(model->translate, resize);
1122 : }
1123 0 : matrixstack.top().transposedtransformnormal(forward, oforward);
1124 :
1125 0 : if(!(anim & Anim_NoRender))
1126 : {
1127 0 : matrix4 modelmatrix;
1128 0 : modelmatrix.mul(shadowmapping ? shadowmatrix : camprojmatrix, matrixstack.top());
1129 0 : if(resize!=1)
1130 : {
1131 0 : modelmatrix.scale(resize);
1132 : }
1133 0 : GLOBALPARAM(modelmatrix, modelmatrix);
1134 0 : if(!(anim & Anim_NoSkin))
1135 : {
1136 0 : GLOBALPARAM(modelworld, matrix3(matrixstack.top()));
1137 :
1138 0 : vec modelcamera;
1139 0 : matrixstack.top().transposedtransform(camera1->o, modelcamera);
1140 0 : modelcamera.div(resize);
1141 0 : GLOBALPARAM(modelcamera, modelcamera);
1142 : }
1143 : }
1144 :
1145 0 : meshes->render(as, pitch, oaxis, oforward, d, this);
1146 :
1147 0 : if((anim & Anim_Reuse) != Anim_Reuse)
1148 : {
1149 0 : for(linkedpart &link : links)
1150 : {
1151 0 : link.matrix.translate(link.translate, resize);
1152 0 : matrix4 mul;
1153 0 : mul.mul(matrixstack.top(), link.matrix);
1154 0 : matrixstack.push(mul);
1155 0 : if(link.pos)
1156 : {
1157 0 : *link.pos = matrixstack.top().gettranslation();
1158 : }
1159 0 : if(!link.p)
1160 : {
1161 0 : matrixstack.pop();
1162 0 : continue;
1163 : }
1164 0 : int nanim = anim,
1165 0 : nbasetime = basetime,
1166 0 : nbasetime2 = basetime2;
1167 0 : if(link.anim>=0)
1168 : {
1169 0 : nanim = link.anim | (anim & Anim_Flags);
1170 0 : nbasetime = link.basetime;
1171 0 : nbasetime2 = 0;
1172 : }
1173 0 : link.p->render(nanim, nbasetime, nbasetime2, pitch, axis, forward, d);
1174 0 : matrixstack.pop();
1175 : }
1176 : }
1177 :
1178 0 : while(matrixstack.size() > oldpos)
1179 : {
1180 0 : matrixstack.pop();
1181 : }
1182 : }
1183 :
1184 2 : void animmodel::part::setanim(int animpart, int num, int frame, int range, float speed, int priority)
1185 : {
1186 2 : if(animpart<0 || animpart>=maxanimparts || num<0 || num >= static_cast<int>(animnames.size()))
1187 : {
1188 0 : return;
1189 : }
1190 2 : if(frame<0 || range<=0 || !meshes || !meshes->hasframes(frame, range))
1191 : {
1192 0 : conoutf("invalid frame %d, range %d in model %s", frame, range, model->modelname().c_str());
1193 0 : return;
1194 : }
1195 2 : if(!anims[animpart])
1196 : {
1197 4 : anims[animpart] = new std::vector<animspec>[animnames.size()];
1198 : }
1199 2 : anims[animpart][num].push_back({frame, range, speed, priority});
1200 : }
1201 :
1202 2 : bool animmodel::part::animated() const
1203 : {
1204 2 : for(int i = 0; i < maxanimparts; ++i)
1205 : {
1206 2 : if(anims[i])
1207 : {
1208 2 : return true;
1209 : }
1210 : }
1211 0 : return false;
1212 : }
1213 :
1214 11 : void animmodel::part::loaded()
1215 : {
1216 22 : for(skin &i : skins)
1217 : {
1218 11 : i.setkey();
1219 : }
1220 11 : }
1221 :
1222 0 : int animmodel::linktype(const animmodel *, const part *) const
1223 : {
1224 0 : return Link_Tag;
1225 : }
1226 :
1227 0 : void animmodel::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
1228 : {
1229 0 : int numtags = 0;
1230 0 : if(a)
1231 : {
1232 0 : int index = parts.back()->index + parts.back()->numanimparts;
1233 0 : for(int i = 0; a[i].tag; i++)
1234 : {
1235 0 : numtags++;
1236 0 : animmodel *m = static_cast<animmodel *>(a[i].m);
1237 0 : if(!m)
1238 : {
1239 0 : continue;
1240 : }
1241 0 : part *p = m->parts[0];
1242 0 : switch(linktype(m, p))
1243 : {
1244 0 : case Link_Tag:
1245 : {
1246 0 : p->index = link(p, a[i].tag, vec(0, 0, 0), a[i].anim, a[i].basetime, a[i].pos) ? index : -1;
1247 0 : break;
1248 : }
1249 0 : default:
1250 : {
1251 0 : continue;
1252 : }
1253 : }
1254 0 : index += p->numanimparts;
1255 : }
1256 : }
1257 :
1258 : AnimState as[maxanimparts];
1259 0 : parts[0]->intersect(anim, basetime, basetime2, pitch, axis, forward, d, o, ray, as);
1260 :
1261 0 : for(part *p : parts)
1262 : {
1263 0 : switch(linktype(this, p))
1264 : {
1265 0 : case Link_Reuse:
1266 : {
1267 0 : p->intersect(anim | Anim_Reuse, basetime, basetime2, pitch, axis, forward, d, o, ray, as);
1268 0 : break;
1269 : }
1270 : }
1271 : }
1272 :
1273 0 : if(a)
1274 : {
1275 0 : for(int i = numtags-1; i >= 0; i--)
1276 : {
1277 0 : animmodel *m = static_cast<animmodel *>(a[i].m);
1278 0 : if(!m)
1279 : {
1280 0 : continue;
1281 : }
1282 :
1283 0 : part *p = m->parts[0];
1284 0 : switch(linktype(m, p))
1285 : {
1286 0 : case Link_Tag:
1287 : {
1288 0 : if(p->index >= 0)
1289 : {
1290 0 : unlink(p);
1291 : }
1292 0 : p->index = 0;
1293 0 : break;
1294 : }
1295 0 : case Link_Reuse:
1296 : {
1297 0 : p->intersect(anim | Anim_Reuse, basetime, basetime2, pitch, axis, forward, d, o, ray, as);
1298 0 : break;
1299 : }
1300 : }
1301 : }
1302 : }
1303 0 : }
1304 :
1305 0 : int animmodel::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
1306 : {
1307 0 : vec axis(1, 0, 0),
1308 0 : forward(0, 1, 0);
1309 :
1310 0 : matrixstack.push(matrix4());
1311 0 : matrixstack.top().identity();
1312 0 : if(!d || !d->ragdoll || d->ragdoll->millis == lastmillis)
1313 : {
1314 0 : const float secs = lastmillis/1000.0f;
1315 0 : yaw += spin.x*secs;
1316 0 : pitch += spin.y*secs;
1317 0 : roll += spin.z*secs;
1318 :
1319 0 : matrixstack.top().settranslation(pos);
1320 0 : matrixstack.top().rotate_around_z(yaw/RAD);
1321 0 : const bool usepitch = pitched();
1322 0 : if(roll && !usepitch)
1323 : {
1324 0 : matrixstack.top().rotate_around_y(-roll/RAD);
1325 : }
1326 0 : matrixstack.top().transformnormal(vec(axis), axis);
1327 0 : matrixstack.top().transformnormal(vec(forward), forward);
1328 0 : if(roll && usepitch)
1329 : {
1330 0 : matrixstack.top().rotate_around_y(-roll/RAD);
1331 : }
1332 0 : if(orientation.x)
1333 : {
1334 0 : matrixstack.top().rotate_around_z(orientation.x/RAD);
1335 : }
1336 0 : if(orientation.y)
1337 : {
1338 0 : matrixstack.top().rotate_around_x(orientation.y/RAD);
1339 : }
1340 0 : if(orientation.z)
1341 : {
1342 0 : matrixstack.top().rotate_around_y(-orientation.z/RAD);
1343 : }
1344 0 : }
1345 : else
1346 : {
1347 0 : matrixstack.top().settranslation(d->ragdoll->center);
1348 0 : pitch = 0;
1349 : }
1350 0 : sizescale = size;
1351 :
1352 0 : intersect(anim, basetime, basetime2, pitch, axis, forward, d, a, o, ray);
1353 0 : return -1;
1354 : }
1355 :
1356 0 : void animmodel::render(int anim, int basetime, int basetime2, float pitch, const vec &axis, const vec &forward, dynent *d, modelattach *a) const
1357 : {
1358 0 : int numtags = 0;
1359 0 : if(a)
1360 : {
1361 0 : int index = parts.back()->index + parts.back()->numanimparts;
1362 0 : for(int i = 0; a[i].tag; i++)
1363 : {
1364 0 : numtags++;
1365 :
1366 0 : animmodel *m = static_cast<animmodel *>(a[i].m);
1367 0 : if(!m)
1368 : {
1369 0 : if(a[i].pos)
1370 : {
1371 0 : link(nullptr, a[i].tag, vec(0, 0, 0), 0, 0, a[i].pos);
1372 : }
1373 0 : continue;
1374 : }
1375 0 : part *p = m->parts[0];
1376 0 : switch(linktype(m, p))
1377 : {
1378 0 : case Link_Tag:
1379 : {
1380 0 : p->index = link(p, a[i].tag, vec(0, 0, 0), a[i].anim, a[i].basetime, a[i].pos) ? index : -1;
1381 0 : break;
1382 : }
1383 0 : default:
1384 : {
1385 0 : continue;
1386 : }
1387 : }
1388 0 : index += p->numanimparts;
1389 : }
1390 : }
1391 :
1392 : AnimState as[maxanimparts];
1393 0 : parts[0]->render(anim, basetime, basetime2, pitch, axis, forward, d, as);
1394 :
1395 0 : for(uint i = 1; i < parts.size(); i++)
1396 : {
1397 0 : part *p = parts[i];
1398 0 : switch(linktype(this, p))
1399 : {
1400 0 : case Link_Reuse:
1401 : {
1402 0 : p->render(anim | Anim_Reuse, basetime, basetime2, pitch, axis, forward, d, as);
1403 0 : break;
1404 : }
1405 : }
1406 : }
1407 :
1408 0 : if(a)
1409 : {
1410 0 : for(int i = numtags-1; i >= 0; i--)
1411 : {
1412 0 : animmodel *m = static_cast<animmodel *>(a[i].m);
1413 0 : if(!m)
1414 : {
1415 0 : if(a[i].pos)
1416 : {
1417 0 : unlink(nullptr);
1418 : }
1419 0 : continue;
1420 : }
1421 0 : part *p = m->parts[0];
1422 0 : switch(linktype(m, p))
1423 : {
1424 0 : case Link_Tag:
1425 : {
1426 0 : if(p->index >= 0)
1427 : {
1428 0 : unlink(p);
1429 : }
1430 0 : p->index = 0;
1431 0 : break;
1432 : }
1433 0 : case Link_Reuse:
1434 : {
1435 0 : p->render(anim | Anim_Reuse, basetime, basetime2, pitch, axis, forward, d, as);
1436 0 : break;
1437 : }
1438 : }
1439 : }
1440 : }
1441 0 : }
1442 :
1443 0 : void animmodel::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
1444 : {
1445 0 : vec axis(1, 0, 0),
1446 0 : forward(0, 1, 0);
1447 :
1448 0 : matrixstack = {};
1449 0 : matrixstack.push(matrix4());
1450 0 : matrixstack.top().identity();
1451 0 : if(!d || !d->ragdoll || d->ragdoll->millis == lastmillis)
1452 : {
1453 0 : float secs = lastmillis/1000.0f;
1454 0 : yaw += spin.x*secs;
1455 0 : pitch += spin.y*secs;
1456 0 : roll += spin.z*secs;
1457 :
1458 0 : matrixstack.top().settranslation(o);
1459 0 : matrixstack.top().rotate_around_z(yaw/RAD);
1460 0 : bool usepitch = pitched();
1461 0 : if(roll && !usepitch)
1462 : {
1463 0 : matrixstack.top().rotate_around_y(-roll/RAD);
1464 : }
1465 0 : matrixstack.top().transformnormal(vec(axis), axis);
1466 0 : matrixstack.top().transformnormal(vec(forward), forward);
1467 0 : if(roll && usepitch)
1468 : {
1469 0 : matrixstack.top().rotate_around_y(-roll/RAD);
1470 : }
1471 0 : if(orientation.x)
1472 : {
1473 0 : matrixstack.top().rotate_around_z(orientation.x/RAD);
1474 : }
1475 0 : if(orientation.y)
1476 : {
1477 0 : matrixstack.top().rotate_around_x(orientation.y/RAD);
1478 : }
1479 0 : if(orientation.z)
1480 : {
1481 0 : matrixstack.top().rotate_around_y(-orientation.z/RAD);
1482 : }
1483 0 : }
1484 : else
1485 : {
1486 0 : matrixstack.top().settranslation(d->ragdoll->center);
1487 0 : pitch = 0;
1488 : }
1489 :
1490 0 : sizescale = size;
1491 :
1492 0 : if(anim & Anim_NoRender)
1493 : {
1494 0 : render(anim, basetime, basetime2, pitch, axis, forward, d, a);
1495 0 : if(d)
1496 : {
1497 0 : d->lastrendered = lastmillis;
1498 : }
1499 0 : return;
1500 : }
1501 :
1502 0 : if(!(anim & Anim_NoSkin))
1503 : {
1504 0 : if(colorscale != color)
1505 : {
1506 0 : colorscale = color;
1507 0 : skin::invalidateshaderparams();
1508 : }
1509 : }
1510 :
1511 0 : if(depthoffset && !enabledepthoffset)
1512 : {
1513 0 : enablepolygonoffset(GL_POLYGON_OFFSET_FILL);
1514 0 : enabledepthoffset = true;
1515 : }
1516 :
1517 0 : render(anim, basetime, basetime2, pitch, axis, forward, d, a);
1518 :
1519 0 : if(d)
1520 : {
1521 0 : d->lastrendered = lastmillis;
1522 : }
1523 : }
1524 :
1525 0 : void animmodel::cleanup()
1526 : {
1527 0 : for(part *p : parts)
1528 : {
1529 0 : p->cleanup();
1530 : }
1531 0 : }
1532 :
1533 0 : animmodel::part &animmodel::addpart()
1534 : {
1535 0 : part *p = new part(this, parts.size());
1536 0 : parts.push_back(p);
1537 0 : return *p;
1538 : }
1539 :
1540 2 : matrix4x3 animmodel::initmatrix() const
1541 : {
1542 2 : matrix4x3 m;
1543 2 : m.identity();
1544 2 : if(orientation.x)
1545 : {
1546 0 : m.rotate_around_z(orientation.x/RAD);
1547 : }
1548 2 : if(orientation.y)
1549 : {
1550 0 : m.rotate_around_x(orientation.y/RAD);
1551 : }
1552 2 : if(orientation.z)
1553 : {
1554 0 : m.rotate_around_y(-orientation.z/RAD);
1555 : }
1556 2 : m.translate(translate, scale);
1557 2 : return m;
1558 : }
1559 :
1560 2 : void animmodel::genBIH(std::vector<BIH::mesh> &bih)
1561 : {
1562 2 : if(parts.empty())
1563 : {
1564 0 : return;
1565 : }
1566 2 : matrix4x3 m = initmatrix();
1567 4 : for(const skin &s : parts[0]->skins)
1568 : {
1569 2 : s.tex->loadalphamask();
1570 : }
1571 2 : for(uint i = 1; i < parts.size(); i++)
1572 : {
1573 0 : const part *p = parts[i];
1574 0 : switch(linktype(this, p))
1575 : {
1576 0 : case Link_Reuse:
1577 : {
1578 0 : for(skin &s : parts[i]->skins)
1579 : {
1580 0 : s.tex->loadalphamask();
1581 : }
1582 0 : p->genBIH(bih, m, scale);
1583 0 : break;
1584 : }
1585 : }
1586 : }
1587 : }
1588 :
1589 0 : bool animmodel::link(part *p, std::string_view tag, const vec &translate, int anim, int basetime, vec *pos) const
1590 : {
1591 0 : if(parts.empty())
1592 : {
1593 0 : return false;
1594 : }
1595 0 : return parts[0]->link(p, tag, translate, anim, basetime, pos);
1596 : }
1597 :
1598 7 : void animmodel::loaded()
1599 : {
1600 18 : for(part *p : parts)
1601 : {
1602 11 : p->loaded();
1603 : }
1604 7 : }
1605 :
1606 0 : bool animmodel::unlink(const part *p) const
1607 : {
1608 0 : if(parts.empty())
1609 : {
1610 0 : return false;
1611 : }
1612 0 : return parts[0]->unlink(p);
1613 : }
1614 :
1615 0 : void animmodel::genshadowmesh(std::vector<triangle> &tris, const matrix4x3 &orient) const
1616 : {
1617 0 : if(parts.empty())
1618 : {
1619 0 : return;
1620 : }
1621 0 : matrix4x3 m = initmatrix();
1622 0 : m.mul(orient, matrix4x3(m));
1623 0 : parts[0]->genshadowmesh(tris, m, scale);
1624 0 : for(uint i = 1; i < parts.size(); i++)
1625 : {
1626 0 : const part *p = parts[i];
1627 0 : switch(linktype(this, p))
1628 : {
1629 0 : case Link_Reuse:
1630 : {
1631 0 : p->genshadowmesh(tris, m, scale);
1632 0 : break;
1633 : }
1634 : }
1635 : }
1636 : }
1637 :
1638 1 : void animmodel::preloadBIH()
1639 : {
1640 1 : if(!bih)
1641 : {
1642 1 : setBIH();
1643 : }
1644 1 : if(bih)
1645 : {
1646 2 : for(const part *i : parts)
1647 : {
1648 1 : i->preloadBIH();
1649 : }
1650 : }
1651 1 : }
1652 :
1653 : //always will return true (note that this overloads model::setBIH())
1654 2 : void animmodel::setBIH()
1655 : {
1656 2 : if(bih)
1657 : {
1658 0 : return;
1659 : }
1660 2 : std::vector<BIH::mesh> meshes;
1661 2 : genBIH(meshes);
1662 2 : bih = std::make_unique<BIH>(meshes);
1663 2 : }
1664 :
1665 1 : bool animmodel::animated() const
1666 : {
1667 1 : if(spin.x || spin.y || spin.z)
1668 : {
1669 0 : return true;
1670 : }
1671 1 : for(const part *i : parts)
1672 : {
1673 1 : if(i->animated())
1674 : {
1675 1 : return true;
1676 : }
1677 : }
1678 0 : return false;
1679 : }
1680 :
1681 1 : bool animmodel::pitched() const
1682 : {
1683 1 : return parts[0]->pitchscale != 0;
1684 : }
1685 :
1686 0 : bool animmodel::alphatested() const
1687 : {
1688 0 : for(const part *i : parts)
1689 : {
1690 0 : if(i->alphatested())
1691 : {
1692 0 : return true;
1693 : }
1694 : }
1695 0 : return false;
1696 : }
1697 :
1698 6 : bool animmodel::load()
1699 : {
1700 6 : startload();
1701 6 : bool success = loadconfig(modelname()) && parts.size(); // configured model, will call the model commands below
1702 6 : if(!success)
1703 : {
1704 6 : success = loaddefaultparts(); // model without configuration, try default tris and skin
1705 : }
1706 6 : endload();
1707 6 : if(flipy())
1708 : {
1709 0 : translate.y = -translate.y;
1710 : }
1711 :
1712 6 : if(!success)
1713 : {
1714 1 : return false;
1715 : }
1716 14 : for(const part *i : parts)
1717 : {
1718 9 : if(!i->meshes)
1719 : {
1720 0 : return false;
1721 : }
1722 : }
1723 5 : loaded();
1724 5 : return true;
1725 : }
1726 :
1727 0 : void animmodel::preloadshaders()
1728 : {
1729 0 : for(part *i : parts)
1730 : {
1731 0 : i->preloadshaders();
1732 : }
1733 0 : }
1734 :
1735 0 : void animmodel::preloadmeshes()
1736 : {
1737 0 : for(part *i : parts)
1738 : {
1739 0 : i->preloadmeshes();
1740 : }
1741 0 : }
1742 :
1743 0 : void animmodel::setshader(Shader *shader)
1744 : {
1745 0 : if(parts.empty())
1746 : {
1747 0 : loaddefaultparts();
1748 : }
1749 0 : for(part *i : parts)
1750 : {
1751 0 : for(skin &j : i->skins)
1752 : {
1753 0 : j.shader = shader;
1754 : }
1755 : }
1756 0 : }
1757 :
1758 0 : void animmodel::setspec(float spec)
1759 : {
1760 0 : if(parts.empty())
1761 : {
1762 0 : loaddefaultparts();
1763 : }
1764 0 : for(part *i : parts)
1765 : {
1766 0 : for(skin &j : i->skins)
1767 : {
1768 0 : j.spec = spec;
1769 : }
1770 : }
1771 0 : }
1772 :
1773 0 : void animmodel::setgloss(int gloss)
1774 : {
1775 0 : if(parts.empty())
1776 : {
1777 0 : loaddefaultparts();
1778 : }
1779 0 : for(part *i : parts)
1780 : {
1781 0 : for(skin &j : i->skins)
1782 : {
1783 0 : j.gloss = gloss;
1784 : }
1785 : }
1786 0 : }
1787 :
1788 0 : void animmodel::setglow(float glow, float delta, float pulse)
1789 : {
1790 0 : if(parts.empty())
1791 : {
1792 0 : loaddefaultparts();
1793 : }
1794 0 : for(part *i : parts)
1795 : {
1796 0 : for(skin &s : i->skins)
1797 : {
1798 0 : s.glow = glow;
1799 0 : s.glowdelta = delta;
1800 0 : s.glowpulse = pulse;
1801 : }
1802 : }
1803 0 : }
1804 :
1805 0 : void animmodel::setalphatest(float alphatest)
1806 : {
1807 0 : if(parts.empty())
1808 : {
1809 0 : loaddefaultparts();
1810 : }
1811 0 : for(part *i : parts)
1812 : {
1813 0 : for(skin &j : i->skins)
1814 : {
1815 0 : j.alphatest = alphatest;
1816 : }
1817 : }
1818 0 : }
1819 :
1820 0 : void animmodel::setfullbright(float fullbright)
1821 : {
1822 0 : if(parts.empty())
1823 : {
1824 0 : loaddefaultparts();
1825 : }
1826 0 : for(part *i : parts)
1827 : {
1828 0 : for(skin &j : i->skins)
1829 : {
1830 0 : j.fullbright = fullbright;
1831 : }
1832 : }
1833 0 : }
1834 :
1835 0 : void animmodel::setcullface(int cullface)
1836 : {
1837 0 : if(parts.empty())
1838 : {
1839 0 : loaddefaultparts();
1840 : }
1841 0 : for(part *i : parts)
1842 : {
1843 0 : for(skin &j : i->skins)
1844 : {
1845 0 : j.cullface = cullface;
1846 : }
1847 : }
1848 0 : }
1849 :
1850 0 : void animmodel::setcolor(const vec &color)
1851 : {
1852 0 : if(parts.empty())
1853 : {
1854 0 : loaddefaultparts();
1855 : }
1856 0 : for(part *i : parts)
1857 : {
1858 0 : for(skin &j : i->skins)
1859 : {
1860 0 : j.color = color;
1861 : }
1862 : }
1863 0 : }
1864 :
1865 0 : void animmodel::settransformation(const std::optional<vec> pos,
1866 : const std::optional<vec> rotate,
1867 : const std::optional<vec> orient,
1868 : const std::optional<float> size)
1869 : {
1870 0 : if(pos)
1871 : {
1872 0 : translate = pos.value();
1873 : }
1874 0 : if(rotate)
1875 : {
1876 0 : spin = rotate.value();
1877 : }
1878 0 : if(orient)
1879 : {
1880 0 : orientation = orient.value();
1881 : }
1882 0 : if(size)
1883 : {
1884 0 : scale = size.value();
1885 : }
1886 0 : }
1887 :
1888 0 : vec4<float> animmodel::locationsize() const
1889 : {
1890 0 : return vec4<float>(translate.x, translate.y, translate.z, scale);
1891 : }
1892 :
1893 0 : void animmodel::calcbb(vec ¢er, vec &radius) const
1894 : {
1895 0 : if(parts.empty())
1896 : {
1897 0 : return;
1898 : }
1899 0 : vec bbmin(1e16f, 1e16f, 1e16f),
1900 0 : bbmax(-1e16f, -1e16f, -1e16f);
1901 0 : matrix4x3 m = initmatrix();
1902 0 : parts[0]->calcbb(bbmin, bbmax, m, scale);
1903 0 : for(const part *p : parts)
1904 : {
1905 0 : switch(linktype(this, p))
1906 : {
1907 0 : case Link_Reuse:
1908 : {
1909 0 : p->calcbb(bbmin, bbmax, m, scale);
1910 0 : break;
1911 : }
1912 : }
1913 : }
1914 0 : radius = bbmax;
1915 0 : radius.sub(bbmin);
1916 0 : radius.mul(0.5f);
1917 0 : center = bbmin;
1918 0 : center.add(radius);
1919 : }
1920 :
1921 0 : void animmodel::startrender() const
1922 : {
1923 0 : enabletc = enabletangents = enablebones = enabledepthoffset = false;
1924 0 : enablecullface = true;
1925 0 : lastvbuf = lasttcbuf = lastxbuf = lastbbuf = lastebuf =0;
1926 0 : lastmasks = lastnormalmap = lastdecal = lasttex = nullptr;
1927 0 : skin::invalidateshaderparams();
1928 0 : }
1929 :
1930 0 : void animmodel::endrender() const
1931 : {
1932 0 : if(lastvbuf || lastebuf)
1933 : {
1934 0 : disablevbo();
1935 : }
1936 0 : if(!enablecullface)
1937 : {
1938 0 : glEnable(GL_CULL_FACE);
1939 : }
1940 0 : if(enabledepthoffset)
1941 : {
1942 0 : disablepolygonoffset(GL_POLYGON_OFFSET_FILL);
1943 : }
1944 0 : }
1945 :
1946 0 : void animmodel::boundbox(vec ¢er, vec &radius)
1947 : {
1948 0 : if(bbradius.x < 0)
1949 : {
1950 0 : calcbb(bbcenter, bbradius);
1951 0 : bbradius.add(bbextend);
1952 : }
1953 0 : center = bbcenter;
1954 0 : radius = bbradius;
1955 0 : }
1956 :
1957 0 : float animmodel::collisionbox(vec ¢er, vec &radius)
1958 : {
1959 0 : if(collideradius.x < 0)
1960 : {
1961 0 : boundbox(collidecenter, collideradius);
1962 0 : if(collidexyradius)
1963 : {
1964 0 : collidecenter.x = collidecenter.y = 0;
1965 0 : collideradius.x = collideradius.y = collidexyradius;
1966 : }
1967 0 : if(collideheight)
1968 : {
1969 0 : collidecenter.z = collideradius.z = collideheight/2;
1970 : }
1971 0 : rejectradius = collideradius.magnitude();
1972 : }
1973 0 : center = collidecenter;
1974 0 : radius = collideradius;
1975 0 : return rejectradius;
1976 : }
1977 :
1978 132 : const std::string &animmodel::modelname() const
1979 : {
1980 132 : return name;
1981 : }
1982 :
1983 0 : void animmodel::disablebones()
1984 : {
1985 0 : gle::disableboneweight();
1986 0 : gle::disableboneindex();
1987 0 : enablebones = false;
1988 0 : }
1989 :
1990 0 : void animmodel::disabletangents()
1991 : {
1992 0 : gle::disabletangent();
1993 0 : enabletangents = false;
1994 0 : }
1995 :
1996 0 : void animmodel::disabletc()
1997 : {
1998 0 : gle::disabletexcoord0();
1999 0 : enabletc = false;
2000 0 : }
2001 :
2002 0 : void animmodel::disablevbo()
2003 : {
2004 0 : if(lastebuf)
2005 : {
2006 0 : gle::clearebo();
2007 : }
2008 0 : if(lastvbuf)
2009 : {
2010 0 : gle::clearvbo();
2011 0 : gle::disablevertex();
2012 : }
2013 0 : if(enabletc)
2014 : {
2015 0 : disabletc();
2016 : }
2017 0 : if(enabletangents)
2018 : {
2019 0 : disabletangents();
2020 : }
2021 0 : if(enablebones)
2022 : {
2023 0 : disablebones();
2024 : }
2025 0 : lastvbuf = lasttcbuf = lastxbuf = lastbbuf = lastebuf = 0;
2026 0 : }
|