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