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