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