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