Line data Source code
1 : /* skelmodel.cpp: implementation for the skeletal animation object
2 : *
3 : * the skelmodel object handles the behavior of animated skeletal models -- such
4 : * as the ones used by the player and bots
5 : *
6 : * for the procedural modification of skeletal models using ragdoll physics, see
7 : * ragdoll.h
8 : *
9 : * this file contains the implementation for the skelmodel object, see skelmodel.h
10 : * for the class definition
11 : */
12 : #include "../libprimis-headers/cube.h"
13 : #include "../../shared/geomexts.h"
14 : #include "../../shared/glemu.h"
15 : #include "../../shared/glexts.h"
16 :
17 : #include <optional>
18 : #include <memory>
19 : #include <format>
20 :
21 : #include "interface/console.h"
22 : #include "interface/control.h"
23 : #include "interface/cs.h"
24 :
25 : #include "render/rendergl.h"
26 : #include "render/renderlights.h"
27 : #include "render/rendermodel.h"
28 : #include "render/shader.h"
29 : #include "render/shaderparam.h"
30 : #include "render/texture.h"
31 :
32 : #include "world/entities.h"
33 : #include "world/octaworld.h"
34 : #include "world/bih.h"
35 :
36 : #include "model.h"
37 : #include "ragdoll.h"
38 : #include "animmodel.h"
39 : #include "skelmodel.h"
40 :
41 : VAR(maxskelanimdata, 1, 192, 0); //sets maximum number of gpu bones
42 :
43 : //animcacheentry child classes
44 :
45 0 : bool skelmodel::vbocacheentry::check() const
46 : {
47 0 : return !vbuf;
48 : }
49 :
50 64 : skelmodel::vbocacheentry::vbocacheentry() : vbuf(0), owner(-1)
51 : {
52 64 : }
53 :
54 0 : void skelmodel::skelcacheentry::nextversion()
55 : {
56 0 : version = Shader::uniformlocversion();
57 0 : }
58 :
59 64 : skelmodel::skelcacheentry::skelcacheentry() : bdata(nullptr), version(-1)
60 : {
61 64 : }
62 :
63 64 : skelmodel::blendcacheentry::blendcacheentry() : owner(-1)
64 : {
65 64 : }
66 :
67 : //skelmodel::animcacheentry object
68 128 : skelmodel::animcacheentry::animcacheentry() : ragdoll(nullptr)
69 : {
70 512 : for(int k = 0; k < maxanimparts; ++k)
71 : {
72 384 : as[k].cur.fr1 = as[k].prev.fr1 = -1;
73 : }
74 128 : }
75 :
76 0 : bool skelmodel::animcacheentry::operator==(const animcacheentry &c) const
77 : {
78 0 : for(int i = 0; i < maxanimparts; ++i)
79 : {
80 0 : if(as[i]!=c.as[i])
81 : {
82 0 : return false;
83 : }
84 : }
85 0 : return pitch==c.pitch && partmask==c.partmask && ragdoll==c.ragdoll && (!ragdoll || std::min(millis, c.millis) >= ragdoll->lastmove);
86 : }
87 :
88 0 : bool skelmodel::animcacheentry::operator!=(const animcacheentry &c) const
89 : {
90 0 : return !(*this == c);
91 : }
92 :
93 : //pitchcorrect
94 :
95 0 : skelmodel::skeleton::pitchcorrect::pitchcorrect(int bone, size_t target, float pitchscale, float pitchmin, float pitchmax) :
96 0 : bone(bone), parent (-1), target(target), pitchmin(pitchmin), pitchmax(pitchmax),
97 0 : pitchscale(pitchscale), pitchangle(0), pitchtotal(0)
98 : {
99 0 : }
100 :
101 0 : skelmodel::skeleton::pitchcorrect::pitchcorrect() : parent(-1), pitchangle(0), pitchtotal(0)
102 : {
103 0 : }
104 :
105 : //skeleton
106 :
107 9 : const skelmodel::skelanimspec *skelmodel::skeleton::findskelanim(std::string_view name) const
108 : {
109 9 : for(const skelanimspec &i : skelanims)
110 : {
111 8 : if(!i.name.empty())
112 : {
113 8 : if(name == i.name)
114 : {
115 8 : return &i;
116 : }
117 : }
118 : }
119 1 : return nullptr;
120 : }
121 :
122 1 : skelmodel::skelanimspec &skelmodel::skeleton::addskelanim(std::string_view name, int numframes, int animframes)
123 : {
124 2 : skelanims.push_back({name.data(), numframes, animframes});
125 1 : return skelanims.back();
126 2 : }
127 :
128 3 : skelmodel::skeleton::skeleton(skelmeshgroup * const group) :
129 3 : numbones(0),
130 3 : numgpubones(0),
131 3 : numframes(0),
132 3 : framebones(nullptr),
133 3 : ragdoll(nullptr),
134 3 : owner(group),
135 3 : numinterpbones(0),
136 3 : bones(nullptr)
137 : {
138 3 : }
139 :
140 2 : skelmodel::skeleton::~skeleton()
141 : {
142 2 : delete[] bones;
143 2 : delete[] framebones;
144 2 : if(ragdoll)
145 : {
146 0 : delete ragdoll;
147 0 : ragdoll = nullptr;
148 : }
149 2 : for(skelcacheentry &i : skelcache)
150 : {
151 0 : delete[] i.bdata;
152 : }
153 2 : }
154 :
155 4 : std::optional<size_t> skelmodel::skeleton::findbone(std::string_view name) const
156 : {
157 6 : for(size_t i = 0; i < numbones; ++i)
158 : {
159 6 : if(bones[i].name.size() && bones[i].name == name)
160 : {
161 4 : return i;
162 : }
163 : }
164 0 : return std::nullopt;
165 : }
166 :
167 4 : std::optional<size_t> skelmodel::skeleton::findtag(std::string_view name) const
168 : {
169 6 : for(size_t i = 0; i < tags.size(); i++)
170 : {
171 4 : if(!std::strcmp(tags[i].name.c_str(), name.data()))
172 : {
173 2 : return i;
174 : }
175 : }
176 2 : return std::nullopt;
177 : }
178 :
179 2 : bool skelmodel::skeleton::addtag(std::string_view name, int bone, const matrix4x3 &matrix)
180 : {
181 2 : std::optional<size_t> idx = findtag(name);
182 2 : if(idx)
183 : {
184 0 : if(!testtags)
185 : {
186 0 : return false;
187 : }
188 0 : tag &t = tags[*idx];
189 0 : t.bone = bone;
190 0 : t.matrix = matrix;
191 : }
192 : else
193 : {
194 2 : tags.emplace_back(name, bone, matrix);
195 : }
196 2 : return true;
197 : }
198 :
199 11 : void skelmodel::skeleton::calcantipodes()
200 : {
201 11 : antipodes.clear();
202 11 : std::vector<uint> schedule;
203 55 : for(size_t i = 0; i < numbones; ++i)
204 : {
205 44 : if(bones[i].group >= static_cast<int>(numbones))
206 : {
207 44 : bones[i].scheduled = schedule.size();
208 44 : schedule.push_back(i);
209 : }
210 : else
211 : {
212 0 : bones[i].scheduled = -1;
213 : }
214 : }
215 55 : for(size_t i = 0; i < schedule.size(); i++)
216 : {
217 44 : const uint bone = schedule[i];
218 44 : const boneinfo &info = bones[bone];
219 220 : for(size_t j = 0; j < numbones; ++j)
220 : {
221 176 : if(std::abs(bones[j].group) == bone && bones[j].scheduled < 0)
222 : {
223 0 : antipodes.emplace_back(antipode(info.interpindex, bones[j].interpindex));
224 0 : bones[j].scheduled = schedule.size();
225 0 : schedule.push_back(j);
226 : }
227 : }
228 44 : if(i + 1 == schedule.size())
229 : {
230 11 : std::optional<int> conflict = std::nullopt;
231 55 : for(size_t j = 0; j < numbones; ++j)
232 : {
233 44 : if(bones[j].group < static_cast<int>(numbones) && bones[j].scheduled < 0)
234 : {
235 0 : conflict = std::min(conflict.value(), std::abs(bones[j].group));
236 : }
237 : }
238 11 : if(conflict)
239 : {
240 0 : bones[conflict.value()].scheduled = schedule.size();
241 0 : schedule.push_back(conflict.value());
242 : }
243 : }
244 : }
245 11 : }
246 :
247 11 : void skelmodel::skeleton::remapbones()
248 : {
249 55 : for(size_t i = 0; i < numbones; ++i)//loop i
250 : {
251 44 : boneinfo &info = bones[i];
252 44 : info.interpindex = -1;
253 44 : info.ragdollindex = -1;
254 : }
255 11 : numgpubones = 0;
256 33 : for(blendcombo &c : owner->blendcombos)
257 : {
258 44 : for(size_t k = 0; k < c.size(); ++k) //loop k
259 : {
260 22 : if(!c.bonedata[k].weight)
261 : {
262 0 : c.setinterpbones(k > 0 ? c.bonedata[k-1].interpbone : 0, k);
263 0 : continue;
264 : }
265 22 : boneinfo &info = bones[c.getbone(k)];
266 22 : if(info.interpindex < 0)
267 : {
268 22 : info.interpindex = numgpubones++;
269 : }
270 22 : c.setinterpbones(info.interpindex, k);
271 22 : if(info.group < 0)
272 : {
273 0 : continue;
274 : }
275 : //for each weight represented, compare with other weights in this blendcombo
276 44 : for(size_t l = 0; l < c.size(); ++l) //note this is a loop l (level 4)
277 : {
278 22 : if(l == k)
279 : {
280 22 : continue;
281 : }
282 : //get the index of the weight at l
283 0 : int parent = c.getbone(l);
284 : //if k's parent is this bone, or k's parent exists and k's parent is l's parent
285 0 : if(info.parent == parent || (info.parent >= 0 && info.parent == bones[parent].parent))
286 : {
287 : //set k's group to be its parent negated
288 0 : info.group = -info.parent;
289 0 : break;
290 : }
291 : //if k's group is <= l's index
292 0 : if(info.group <= parent)
293 : {
294 0 : continue;
295 : }
296 : //if k's group is > l's index, then k is a child of l (children are assigned higher numbers than parents)
297 0 : int child = c.getbone(k);
298 : //while l's index is greater than k's (implying l is a child of k), change l until l is a parent of k
299 0 : while(parent > child)
300 : {
301 0 : parent = bones[parent].parent;
302 : }
303 : //if l, or one of its parents, is not k, set k's group to the index of the bone at l
304 0 : if(parent != child)
305 : {
306 0 : info.group = c.getbone(l);
307 : }
308 : }
309 : }
310 : }
311 11 : numinterpbones = numgpubones;
312 31 : for(const tag &i : tags)
313 : {
314 20 : boneinfo &info = bones[i.bone];
315 20 : if(info.interpindex < 0)
316 : {
317 20 : info.interpindex = numinterpbones++;
318 : }
319 : }
320 11 : if(ragdoll)
321 : {
322 0 : for(size_t i = 0; i < ragdoll->joints.size(); i++)
323 : {
324 0 : boneinfo &info = bones[ragdoll->joints[i].bone];
325 0 : if(info.interpindex < 0)
326 : {
327 0 : info.interpindex = numinterpbones++;
328 : }
329 0 : info.ragdollindex = i;
330 : }
331 : }
332 55 : for(size_t i = 0; i < numbones; ++i)
333 : {
334 44 : const boneinfo &info = bones[i];
335 44 : if(info.interpindex < 0)
336 : {
337 2 : continue;
338 : }
339 43 : for(int parent = info.parent; parent >= 0 && bones[parent].interpindex < 0; parent = bones[parent].parent)
340 : {
341 1 : bones[parent].interpindex = numinterpbones++;
342 : }
343 : }
344 55 : for(size_t i = 0; i < numbones; ++i)
345 : {
346 44 : boneinfo &info = bones[i];
347 44 : if(info.interpindex < 0)
348 : {
349 1 : continue;
350 : }
351 43 : info.interpparent = info.parent >= 0 ? bones[info.parent].interpindex : -1;
352 : }
353 11 : if(ragdoll)
354 : {
355 0 : for(size_t i = 0; i < numbones; ++i)
356 : {
357 0 : const boneinfo &info = bones[i];
358 0 : if(info.interpindex < 0 || info.ragdollindex >= 0)
359 : {
360 0 : continue;
361 : }
362 0 : for(int parent = info.parent; parent >= 0; parent = bones[parent].parent)
363 : {
364 0 : if(bones[parent].ragdollindex >= 0)
365 : {
366 0 : ragdoll->addreljoint(i, bones[parent].ragdollindex);
367 0 : break;
368 : }
369 : }
370 : }
371 : }
372 11 : calcantipodes();
373 11 : }
374 :
375 9 : void skelmodel::skeleton::addpitchdep(int bone, int frame)
376 : {
377 27 : for(; bone >= 0; bone = bones[bone].parent)
378 : {
379 18 : size_t pos = pitchdeps.size();
380 18 : for(size_t j = 0; j < pitchdeps.size(); j++)
381 : {
382 9 : if(bone <= pitchdeps[j].bone)
383 : {
384 9 : if(bone == pitchdeps[j].bone)
385 : {
386 0 : goto nextbone;
387 : }
388 9 : pos = j;
389 9 : break;
390 : }
391 : }
392 : { //if goto nextbone not called (note: no control logic w/ braces)
393 18 : pitchdep d;
394 18 : d.bone = bone;
395 18 : d.parent = -1;
396 18 : d.pose = framebones[frame*numbones + bone];
397 18 : pitchdeps.insert(pitchdeps.begin() + pos, d);
398 : }
399 18 : nextbone:;
400 : }
401 9 : }
402 :
403 18 : std::optional<size_t> skelmodel::skeleton::findpitchdep(int bone) const
404 : {
405 27 : for(size_t i = 0; i < pitchdeps.size(); i++)
406 : {
407 27 : if(bone <= pitchdeps[i].bone)
408 : {
409 18 : return bone == pitchdeps[i].bone ? std::optional<size_t>(i) : std::optional<size_t>(std::nullopt);
410 : }
411 : }
412 0 : return std::nullopt;
413 : }
414 :
415 18 : std::optional<size_t> skelmodel::skeleton::findpitchcorrect(int bone) const
416 : {
417 18 : for(size_t i = 0; i < pitchcorrects.size(); i++)
418 : {
419 0 : if(bone <= pitchcorrects[i].bone)
420 : {
421 0 : return bone == pitchcorrects[i].bone ? std::optional<size_t>(i) : std::optional<size_t>(std::nullopt);
422 : }
423 : }
424 18 : return std::nullopt;
425 : }
426 :
427 11 : void skelmodel::skeleton::initpitchdeps()
428 : {
429 11 : pitchdeps.clear();
430 11 : if(pitchtargets.empty())
431 : {
432 2 : return;
433 : }
434 18 : for(pitchtarget &t : pitchtargets)
435 : {
436 9 : t.deps = -1;
437 9 : addpitchdep(t.bone, t.frame);
438 : }
439 27 : for(pitchdep &d : pitchdeps)
440 : {
441 18 : int parent = bones[d.bone].parent;
442 18 : if(parent >= 0)
443 : {
444 9 : std::optional<size_t> j = findpitchdep(parent);
445 9 : if(j)
446 : {
447 9 : d.parent = j.value();
448 9 : d.pose.mul(pitchdeps[j.value()].pose, dualquat(d.pose));
449 : }
450 : }
451 : }
452 18 : for(pitchtarget &t : pitchtargets)
453 : {
454 9 : std::optional<size_t> j = findpitchdep(t.bone);
455 9 : if(j)
456 : {
457 9 : t.deps = j.value();
458 9 : t.pose = pitchdeps[j.value()].pose;
459 : }
460 9 : t.corrects = -1;
461 27 : for(int parent = t.bone; parent >= 0; parent = bones[parent].parent)
462 : {
463 18 : std::optional<size_t> newpitchcorrect = findpitchcorrect(parent);
464 18 : t.corrects = newpitchcorrect ? newpitchcorrect.value() : -1;
465 18 : if(t.corrects >= 0)
466 : {
467 0 : break;
468 : }
469 : }
470 : }
471 9 : for(size_t i = 0; i < pitchcorrects.size(); i++)
472 : {
473 0 : pitchcorrect &c = pitchcorrects[i];
474 0 : bones[c.bone].correctindex = i;
475 0 : c.parent = -1;
476 0 : for(int parent = c.bone;;)
477 : {
478 0 : parent = bones[parent].parent;
479 0 : if(parent < 0)
480 : {
481 0 : break;
482 : }
483 0 : std::optional<size_t> newpitchcorrect = findpitchcorrect(parent);
484 0 : c.parent = newpitchcorrect ? newpitchcorrect.value() : -1;
485 0 : if(c.parent >= 0)
486 : {
487 0 : break;
488 : }
489 0 : }
490 : }
491 : }
492 :
493 11 : void skelmodel::skeleton::optimize()
494 : {
495 11 : cleanup();
496 11 : if(ragdoll)
497 : {
498 0 : ragdoll->setup();
499 : }
500 11 : remapbones();
501 11 : initpitchdeps();
502 11 : }
503 :
504 4 : void skelmodel::skeleton::expandbonemask(uchar *expansion, int bone, int val) const
505 : {
506 4 : expansion[bone] = val;
507 4 : bone = bones[bone].children;
508 7 : while(bone>=0)
509 : {
510 3 : expandbonemask(expansion, bone, val);
511 3 : bone = bones[bone].next;
512 : }
513 4 : }
514 :
515 1 : void skelmodel::skeleton::applybonemask(const std::vector<uint> &mask, std::vector<uchar> &partmask, int partindex) const
516 : {
517 1 : if(mask.empty() || mask.front()==Bonemask_End)
518 : {
519 0 : return;
520 : }
521 1 : uchar *expansion = new uchar[numbones];
522 1 : std::memset(expansion, mask.front()&Bonemask_Not ? 1 : 0, numbones);
523 2 : for(const uint &i : mask)
524 : {
525 2 : if(i == Bonemask_End)
526 : {
527 1 : break;
528 : }
529 1 : expandbonemask(expansion, i&Bonemask_Bone, i&Bonemask_Not ? 0 : 1);
530 : }
531 5 : for(size_t i = 0; i < numbones; ++i)
532 : {
533 4 : if(expansion[i])
534 : {
535 4 : partmask[i] = partindex;
536 : }
537 : }
538 1 : delete[] expansion;
539 : }
540 :
541 1 : void skelmodel::skeleton::linkchildren()
542 : {
543 5 : for(size_t i = 0; i < numbones; ++i)
544 : {
545 4 : boneinfo &b = bones[i];
546 4 : b.children = -1;
547 4 : if(b.parent<0)
548 : {
549 1 : b.next = -1;
550 : }
551 : else
552 : {
553 3 : b.next = bones[b.parent].children;
554 3 : bones[b.parent].children = i;
555 : }
556 : }
557 1 : }
558 :
559 0 : int skelmodel::skeleton::availgpubones()
560 : {
561 0 : return std::min(maxvsuniforms, maxskelanimdata) / 2;
562 : }
563 :
564 0 : float skelmodel::skeleton::calcdeviation(const vec &axis, const vec &forward, const dualquat &pose1, const dualquat &pose2)
565 : {
566 0 : const vec forward1 = pose1.transformnormal(forward).project(axis).normalize(),
567 0 : forward2 = pose2.transformnormal(forward).project(axis).normalize(),
568 0 : daxis = vec().cross(forward1, forward2);
569 0 : const float dx = std::clamp(forward1.dot(forward2), -1.0f, 1.0f);
570 0 : float dy = std::clamp(daxis.magnitude(), -1.0f, 1.0f);
571 0 : if(daxis.dot(axis) < 0)
572 : {
573 0 : dy = -dy;
574 : }
575 0 : return atan2f(dy, dx)*RAD;
576 : }
577 :
578 0 : void skelmodel::skeleton::calcpitchcorrects(float pitch, const vec &axis, const vec &forward)
579 : {
580 0 : for(pitchtarget &t : pitchtargets)
581 : {
582 0 : t.deviated = calcdeviation(axis, forward, t.pose, pitchdeps[t.deps].pose);
583 : }
584 0 : for(pitchcorrect &c : pitchcorrects)
585 : {
586 0 : c.pitchangle = c.pitchtotal = 0;
587 : }
588 0 : for(size_t j = 0; j < pitchtargets.size(); j++)
589 : {
590 0 : const pitchtarget &t = pitchtargets[j];
591 0 : float tpitch = pitch - t.deviated;
592 0 : for(int parent = t.corrects; parent >= 0; parent = pitchcorrects[parent].parent)
593 : {
594 0 : tpitch -= pitchcorrects[parent].pitchangle;
595 : }
596 0 : if(t.pitchmin || t.pitchmax)
597 : {
598 0 : tpitch = std::clamp(tpitch, t.pitchmin, t.pitchmax);
599 : }
600 0 : for(pitchcorrect& c : pitchcorrects)
601 : {
602 0 : if(c.target != j)
603 : {
604 0 : continue;
605 : }
606 0 : float total = c.parent >= 0 ? pitchcorrects[c.parent].pitchtotal : 0,
607 0 : avail = tpitch - total,
608 0 : used = tpitch*c.pitchscale;
609 0 : if(c.pitchmin || c.pitchmax)
610 : {
611 0 : if(used < 0)
612 : {
613 0 : used = std::clamp(c.pitchmin, used, 0.0f);
614 : }
615 : else
616 : {
617 0 : used = std::clamp(c.pitchmax, 0.0f, used);
618 : }
619 : }
620 0 : if(used < 0)
621 : {
622 0 : used = std::clamp(avail, used, 0.0f);
623 : }
624 : else
625 : {
626 0 : used = std::clamp(avail, 0.0f, used);
627 : }
628 0 : c.pitchangle = used;
629 0 : c.pitchtotal = used + total;
630 : }
631 : }
632 0 : }
633 :
634 : //private helper function for interpbones
635 0 : dualquat skelmodel::skeleton::interpbone(int bone, const std::array<framedata, maxanimparts> &partframes, const AnimState *as, const uchar *partmask) const
636 : {
637 0 : const AnimState &s = as[partmask[bone]];
638 0 : const framedata &f = partframes[partmask[bone]];
639 0 : dualquat d;
640 0 : (d = f.fr1[bone]).mul((1-s.cur.t)*s.interp);
641 0 : d.accumulate(f.fr2[bone], s.cur.t*s.interp);
642 0 : if(s.interp<1)
643 : {
644 0 : d.accumulate(f.pfr1[bone], (1-s.prev.t)*(1-s.interp));
645 0 : d.accumulate(f.pfr2[bone], s.prev.t*(1-s.interp));
646 : }
647 0 : return d;
648 : }
649 :
650 0 : void skelmodel::skeleton::interpbones(const AnimState *as, float pitch, const vec &axis, const vec &forward, int numanimparts, const uchar *partmask, skelcacheentry &sc)
651 : {
652 0 : if(!sc.bdata)
653 : {
654 0 : sc.bdata = new dualquat[numinterpbones];
655 : }
656 0 : sc.nextversion();
657 : std::array<framedata, maxanimparts> partframes;
658 0 : for(int i = 0; i < numanimparts; ++i)
659 : {
660 0 : partframes[i].fr1 = &framebones[as[i].cur.fr1*numbones];
661 0 : partframes[i].fr2 = &framebones[as[i].cur.fr2*numbones];
662 0 : if(as[i].interp<1)
663 : {
664 0 : partframes[i].pfr1 = &framebones[as[i].prev.fr1*numbones];
665 0 : partframes[i].pfr2 = &framebones[as[i].prev.fr2*numbones];
666 : }
667 : }
668 0 : for(pitchdep &p : pitchdeps)
669 : {
670 0 : dualquat d = interpbone(p.bone, partframes, as, partmask);
671 0 : d.normalize();
672 0 : if(p.parent >= 0)
673 : {
674 0 : p.pose.mul(pitchdeps[p.parent].pose, d);
675 : }
676 : else
677 : {
678 0 : p.pose = d;
679 : }
680 : }
681 0 : calcpitchcorrects(pitch, axis, forward);
682 0 : for(size_t i = 0; i < numbones; ++i)
683 : {
684 0 : if(bones[i].interpindex>=0)
685 : {
686 0 : dualquat d = interpbone(i, partframes, as, partmask);
687 0 : d.normalize();
688 0 : const boneinfo &b = bones[i];
689 0 : if(b.interpparent<0)
690 : {
691 0 : sc.bdata[b.interpindex] = d;
692 : }
693 : else
694 : {
695 0 : sc.bdata[b.interpindex].mul(sc.bdata[b.interpparent], d);
696 : }
697 : float angle;
698 0 : if(b.pitchscale)
699 : {
700 0 : angle = b.pitchscale*pitch + b.pitchoffset;
701 0 : if(b.pitchmin || b.pitchmax)
702 : {
703 0 : angle = std::clamp(angle, b.pitchmin, b.pitchmax);
704 : }
705 : }
706 0 : else if(b.correctindex >= 0)
707 : {
708 0 : angle = pitchcorrects[b.correctindex].pitchangle;
709 : }
710 : else
711 : {
712 0 : continue;
713 : }
714 0 : if(as->cur.anim & Anim_NoPitch || (as->interp < 1 && as->prev.anim & Anim_NoPitch))
715 : {
716 0 : angle *= (as->cur.anim & Anim_NoPitch ? 0 : as->interp) + (as->interp < 1 && as->prev.anim & Anim_NoPitch ? 0 : 1 - as->interp);
717 : }
718 0 : sc.bdata[b.interpindex].mulorient(quat(axis, angle/RAD), b.base);
719 : }
720 : }
721 0 : for(const antipode &i : antipodes)
722 : {
723 0 : sc.bdata[i.child].fixantipodal(sc.bdata[i.parent]);
724 : }
725 0 : }
726 :
727 0 : void skelmodel::skeleton::initragdoll(ragdolldata &d, const skelcacheentry &sc, float scale) const
728 : {
729 0 : const dualquat *bdata = sc.bdata;
730 0 : for(const ragdollskel::joint &j : ragdoll->joints)
731 : {
732 0 : const boneinfo &b = bones[j.bone];
733 0 : const dualquat &q = bdata[b.interpindex];
734 0 : for(int k = 0; k < 3; ++k)
735 : {
736 0 : if(j.vert[k] >= 0)
737 : {
738 0 : const ragdollskel::vert &v = ragdoll->verts[j.vert[k]];
739 0 : ragdolldata::vert &dv = d.verts[j.vert[k]];
740 0 : dv.pos.add(q.transform(v.pos).mul(v.weight));
741 : }
742 : }
743 : }
744 0 : if(ragdoll->animjoints)
745 : {
746 0 : for(size_t i = 0; i < ragdoll->joints.size(); i++)
747 : {
748 0 : const ragdollskel::joint &j = ragdoll->joints[i];
749 0 : const boneinfo &b = bones[j.bone];
750 0 : const dualquat &q = bdata[b.interpindex];
751 0 : d.animjoints[i] = d.calcanimjoint(i, matrix4x3(q));
752 : }
753 : }
754 0 : for(size_t i = 0; i < ragdoll->verts.size(); i++)
755 : {
756 0 : ragdolldata::vert &dv = d.verts[i];
757 0 : matrixstack.top().transform(vec(dv.pos).mul(scale), dv.pos);
758 : }
759 0 : for(size_t i = 0; i < ragdoll->reljoints.size(); i++)
760 : {
761 0 : const ragdollskel::reljoint &r = ragdoll->reljoints[i];
762 0 : const ragdollskel::joint &j = ragdoll->joints[r.parent];
763 0 : const boneinfo &br = bones[r.bone], &bj = bones[j.bone];
764 0 : d.reljoints[i].mul(dualquat(bdata[bj.interpindex]).invert(), bdata[br.interpindex]);
765 : }
766 0 : }
767 :
768 0 : void skelmodel::skeleton::genragdollbones(const ragdolldata &d, skelcacheentry &sc, const vec &translate, float scale) const
769 : {
770 0 : if(!sc.bdata)
771 : {
772 0 : sc.bdata = new dualquat[numinterpbones];
773 : }
774 0 : sc.nextversion();
775 0 : const vec trans = vec(d.center).div(scale).add(translate);
776 0 : for(size_t i = 0; i < ragdoll->joints.size(); i++)
777 : {
778 0 : const ragdollskel::joint &j = ragdoll->joints[i];
779 0 : const boneinfo &b = bones[j.bone];
780 0 : vec pos(0, 0, 0);
781 0 : for(int k = 0; k < 3; ++k)
782 : {
783 0 : if(j.vert[k]>=0)
784 : {
785 0 : pos.add(d.verts[j.vert[k]].pos);
786 : }
787 : }
788 0 : pos.mul(j.weight/scale).sub(trans);
789 0 : matrix4x3 m;
790 0 : m.mul(d.tris[j.tri], pos, d.animjoints ? d.animjoints[i] : j.orient);
791 0 : sc.bdata[b.interpindex] = dualquat(m);
792 : }
793 0 : for(size_t i = 0; i < ragdoll->reljoints.size(); i++)
794 : {
795 0 : const ragdollskel::reljoint &r = ragdoll->reljoints[i];
796 0 : const ragdollskel::joint &j = ragdoll->joints[r.parent];
797 0 : const boneinfo &br = bones[r.bone], &bj = bones[j.bone];
798 0 : sc.bdata[br.interpindex].mul(sc.bdata[bj.interpindex], d.reljoints[i]);
799 : }
800 0 : for(const antipode &i : antipodes)
801 : {
802 0 : sc.bdata[i.child].fixantipodal(sc.bdata[i.parent]);
803 : }
804 0 : }
805 :
806 0 : void skelmodel::skeleton::concattagtransform(int i, const matrix4x3 &m, matrix4x3 &n) const
807 : {
808 0 : matrix4x3 t;
809 0 : t.mul(bones[tags[i].bone].base, tags[i].matrix);
810 0 : n.mul(m, t);
811 0 : }
812 :
813 0 : void skelmodel::skeleton::calctags(part *p, const skelcacheentry *sc) const
814 : {
815 0 : for(part::linkedpart &l : p->links)
816 : {
817 0 : const tag &t = tags[l.tag];
818 0 : dualquat q;
819 0 : if(sc)
820 : {
821 0 : q.mul(sc->bdata[bones[t.bone].interpindex], bones[t.bone].base);
822 : }
823 : else
824 : {
825 0 : q = bones[t.bone].base;
826 : }
827 0 : matrix4x3 m;
828 0 : m.mul(q, t.matrix);
829 0 : m.d.mul(p->model->locationsize().w * sizescale);
830 0 : l.matrix = m;
831 : }
832 0 : }
833 :
834 22 : void skelmodel::skeleton::cleanup(bool full)
835 : {
836 22 : for(skelcacheentry &sc : skelcache)
837 : {
838 0 : for(int j = 0; j < maxanimparts; ++j)
839 : {
840 0 : sc.as[j].cur.fr1 = -1; //skelcache animstate @ j's cur AnimPos fr1 (frame1?)
841 : }
842 0 : delete[] sc.bdata;
843 0 : sc.bdata = nullptr;
844 : }
845 22 : skelcache.clear();
846 22 : blendoffsets.clear();
847 22 : if(full)
848 : {
849 11 : owner->cleanup();
850 : }
851 22 : }
852 :
853 0 : const skelmodel::skelcacheentry &skelmodel::skeleton::checkskelcache(const vec &pos, float scale, const AnimState *as, float pitch, const vec &axis, const vec &forward, const ragdolldata * const rdata)
854 : {
855 0 : const int numanimparts = as->owner->numanimparts;
856 0 : const std::vector<uchar> &partmask = (reinterpret_cast<const skelpart *>(as->owner))->partmask;
857 0 : skelcacheentry *sc = nullptr;
858 0 : bool match = false;
859 0 : for(skelcacheentry &c : skelcache)
860 : {
861 0 : for(int j = 0; j < numanimparts; ++j)
862 : {
863 0 : if(c.as[j]!=as[j])
864 : {
865 0 : goto mismatch;
866 : }
867 : }
868 0 : if(c.pitch != pitch || *c.partmask != partmask || c.ragdoll != rdata || (rdata && c.millis < rdata->lastmove))
869 : {
870 0 : goto mismatch;
871 : }
872 0 : match = true;
873 0 : sc = &c;
874 0 : break;
875 0 : mismatch:
876 0 : if(c.millis < lastmillis)
877 : {
878 0 : sc = &c;
879 0 : break;
880 : }
881 : }
882 0 : if(!sc)
883 : {
884 0 : skelcache.emplace_back(skelcacheentry());
885 0 : sc = &skelcache.back();
886 : }
887 0 : if(!match)
888 : {
889 0 : for(int i = 0; i < numanimparts; ++i)
890 : {
891 0 : sc->as[i] = as[i];
892 : }
893 0 : sc->pitch = pitch;
894 0 : sc->partmask = &partmask;
895 0 : sc->ragdoll = rdata;
896 0 : if(rdata)
897 : {
898 0 : genragdollbones(*rdata, *sc, pos, scale);
899 : }
900 : else
901 : {
902 0 : interpbones(as, pitch, axis, forward, numanimparts, partmask.data(), *sc);
903 : }
904 : }
905 0 : sc->millis = lastmillis;
906 0 : return *sc;
907 : }
908 :
909 0 : GLint skelmodel::skeleton::getblendoffset(const UniformLoc &u)
910 : {
911 0 : auto itr = blendoffsets.find(Shader::lastshader->program);
912 0 : if(itr == blendoffsets.end())
913 : {
914 0 : itr = blendoffsets.insert( { Shader::lastshader->program, -1 } ).first;
915 0 : std::string offsetname = std::format("{}[{}]", u.name, 2*numgpubones);
916 0 : (*itr).second = glGetUniformLocation(Shader::lastshader->program, offsetname.c_str());
917 0 : }
918 0 : return (*itr).second;
919 : }
920 :
921 0 : void skelmodel::skeleton::setglslbones(UniformLoc &u, const skelcacheentry &sc, const skelcacheentry &bc, int count)
922 : {
923 0 : if(u.version == bc.version && u.data == bc.bdata)
924 : {
925 0 : return;
926 : }
927 0 : glUniform4fv(u.loc, 2*numgpubones, sc.bdata[0].real.data());
928 0 : if(count > 0)
929 : {
930 0 : GLint offset = getblendoffset(u);
931 0 : if(offset >= 0)
932 : {
933 0 : glUniform4fv(offset, 2*count, bc.bdata[0].real.data());
934 : }
935 : }
936 0 : u.version = bc.version;
937 0 : u.data = bc.bdata;
938 : }
939 :
940 0 : void skelmodel::skeleton::setgpubones(const skelcacheentry &sc, const blendcacheentry *bc, int count)
941 : {
942 0 : if(!Shader::lastshader)
943 : {
944 0 : return;
945 : }
946 0 : if(Shader::lastshader->uniformlocs.size() < 1)
947 : {
948 0 : return;
949 : }
950 0 : UniformLoc &u = Shader::lastshader->uniformlocs[0];
951 0 : setglslbones(u, sc, bc ? *bc : sc, count);
952 : }
953 :
954 0 : bool skelmodel::skeleton::shouldcleanup() const
955 : {
956 0 : return numframes && skelcache.empty();
957 : }
958 :
959 0 : bool skelmodel::skeleton::setbonepitch(size_t index, float scale, float offset, float min, float max)
960 : {
961 0 : if(index > numbones)
962 : {
963 0 : return false;
964 : }
965 0 : boneinfo &b = bones[index];
966 0 : b.pitchscale = scale;
967 0 : b.pitchoffset = offset;
968 0 : b.pitchmin = min;
969 0 : b.pitchmax = max;
970 0 : return true;
971 : }
972 :
973 7 : std::optional<dualquat> skelmodel::skeleton::getbonebase(size_t index) const
974 : {
975 7 : if(index > numbones)
976 : {
977 0 : return std::nullopt;
978 : }
979 7 : return bones[index].base;
980 : }
981 :
982 1 : bool skelmodel::skeleton::setbonebases(const std::vector<dualquat> &bases)
983 : {
984 1 : if(bases.size() != numbones)
985 : {
986 0 : return false;
987 : }
988 5 : for(size_t i = 0; i < numbones; ++i)
989 : {
990 4 : bones[i].base = bases[i];
991 : }
992 1 : return true;
993 : }
994 :
995 4 : bool skelmodel::skeleton::setbonename(size_t index, std::string_view name)
996 : {
997 4 : if(index > numbones)
998 : {
999 0 : return false;
1000 : }
1001 4 : boneinfo &b = bones[index];
1002 4 : if(!b.name.size())
1003 : {
1004 4 : b.name = name;
1005 4 : return true;
1006 : }
1007 0 : return false;
1008 : }
1009 :
1010 4 : bool skelmodel::skeleton::setboneparent(size_t index, size_t parent)
1011 : {
1012 4 : if(index > numbones || parent > numbones)
1013 : {
1014 1 : return false;
1015 : }
1016 3 : boneinfo &b = bones[index];
1017 3 : b.parent = parent;
1018 3 : return true;
1019 : }
1020 :
1021 1 : void skelmodel::skeleton::createbones(size_t num)
1022 : {
1023 1 : numbones = num;
1024 5 : bones = new boneinfo[numbones];
1025 1 : }
1026 :
1027 0 : ragdollskel *skelmodel::skeleton::trycreateragdoll()
1028 : {
1029 0 : if(!ragdoll)
1030 : {
1031 0 : ragdoll = new ragdollskel;
1032 : }
1033 0 : return ragdoll;
1034 : }
1035 :
1036 2 : skelmodel::skelmeshgroup::~skelmeshgroup()
1037 : {
1038 2 : if(skel)
1039 : {
1040 2 : delete skel;
1041 2 : skel = nullptr;
1042 : }
1043 2 : if(ebuf)
1044 : {
1045 0 : glDeleteBuffers(1, &ebuf);
1046 : }
1047 34 : for(size_t i = 0; i < maxblendcache; ++i)
1048 : {
1049 32 : delete[] blendcache[i].bdata;
1050 : }
1051 34 : for(size_t i = 0; i < maxvbocache; ++i)
1052 : {
1053 32 : if(vbocache[i].vbuf)
1054 : {
1055 0 : glDeleteBuffers(1, &vbocache[i].vbuf);
1056 : }
1057 : }
1058 2 : delete[] vdata;
1059 2 : }
1060 :
1061 0 : void skelmodel::skelmeshgroup::genvbo(vbocacheentry &vc)
1062 : {
1063 0 : if(!vc.vbuf)
1064 : {
1065 0 : glGenBuffers(1, &vc.vbuf);
1066 : }
1067 0 : if(ebuf)
1068 : {
1069 0 : return;
1070 : }
1071 :
1072 0 : std::vector<GLuint> idxs;
1073 :
1074 0 : vlen = 0;
1075 0 : vblends = 0;
1076 :
1077 0 : if(skel->numframes)
1078 : {
1079 0 : vweights = 4;
1080 0 : int availbones = skel->availgpubones() - skel->numgpubones;
1081 0 : while(vweights > 1 && availbones >= numblends[vweights-1])
1082 : {
1083 0 : availbones -= numblends[--vweights];
1084 : }
1085 0 : for(blendcombo &c : blendcombos)
1086 : {
1087 0 : c.interpindex = static_cast<int>(c.size()) > vweights ? skel->numgpubones + vblends++ : -1;
1088 : }
1089 : }
1090 : else
1091 : {
1092 0 : vweights = 0;
1093 0 : for(blendcombo &i : blendcombos)
1094 : {
1095 0 : i.interpindex = -1;
1096 : }
1097 : }
1098 :
1099 0 : gle::bindvbo(vc.vbuf);
1100 0 : auto rendermeshes = getrendermeshes();
1101 0 : if(skel->numframes)
1102 : {
1103 0 : vertsize = sizeof(vvertgw);//silent parameter to genvbo()
1104 0 : std::vector<vvertgw> vvertgws;
1105 0 : for(const auto &i : rendermeshes)
1106 : {
1107 0 : vlen += static_cast<skelmesh *>(*i)->genvbo(blendcombos, idxs, vlen, vvertgws);
1108 : }
1109 0 : glBufferData(GL_ARRAY_BUFFER, vvertgws.size()*sizeof(vvertgw), vvertgws.data(), GL_STATIC_DRAW);
1110 0 : }
1111 : else
1112 : {
1113 0 : int numverts = 0,
1114 0 : htlen = 128;
1115 0 : for(auto i : rendermeshes)
1116 : {
1117 0 : numverts += static_cast<skelmesh *>(*i)->vertcount();
1118 : }
1119 0 : while(htlen < numverts)
1120 : {
1121 0 : htlen *= 2;
1122 : }
1123 0 : if(numverts*4 > htlen*3)
1124 : {
1125 0 : htlen *= 2;
1126 : }
1127 0 : int *htdata = new int[htlen];
1128 0 : std::memset(htdata, -1, htlen*sizeof(int));
1129 0 : vertsize = sizeof(vvertg); //silent parameter to genvbo()
1130 0 : std::vector<vvertg> vvertgs;
1131 0 : for(const auto &i : rendermeshes)
1132 : {
1133 0 : vlen += static_cast<skelmesh *>(*i)->genvbo(idxs, vlen, vvertgs, htdata, htlen);
1134 : }
1135 0 : glBufferData(GL_ARRAY_BUFFER, vvertgs.size()*sizeof(vvertg), vvertgs.data(), GL_STATIC_DRAW);
1136 0 : delete[] htdata;
1137 0 : }
1138 0 : gle::clearvbo();
1139 :
1140 0 : glGenBuffers(1, &ebuf);
1141 0 : gle::bindebo(ebuf);
1142 0 : glBufferData(GL_ELEMENT_ARRAY_BUFFER, idxs.size()*sizeof(GLuint), idxs.data(), GL_STATIC_DRAW);
1143 0 : gle::clearebo();
1144 0 : }
1145 :
1146 0 : void skelmodel::skelmeshgroup::render(const AnimState *as, float pitch, const vec &axis, const vec &forward, dynent *d, part *p)
1147 : {
1148 0 : if(skel->shouldcleanup())
1149 : {
1150 0 : skel->cleanup();
1151 0 : disablevbo();
1152 : }
1153 :
1154 0 : if(!skel->numframes)
1155 : {
1156 0 : if(!(as->cur.anim & Anim_NoRender))
1157 : {
1158 0 : if(!vbocache[0].vbuf)
1159 : {
1160 0 : genvbo(vbocache[0]);
1161 : }
1162 0 : bindvbo(as, p, vbocache[0]);
1163 0 : LOOP_RENDER_MESHES(skelmesh, m,
1164 : {
1165 : p->skins[i].bind(m, as, true, vweights);
1166 : m.render();
1167 : });
1168 : }
1169 0 : skel->calctags(p);
1170 0 : return;
1171 : }
1172 :
1173 0 : const vec ploc = vec(p->model->locationsize().x, p->model->locationsize().y, p->model->locationsize().z);
1174 :
1175 0 : const skelcacheentry &sc = skel->checkskelcache(ploc, p->model->locationsize().w, as, pitch, axis, forward, !d || !d->ragdoll || d->ragdoll->skel != skel->ragdoll || d->ragdoll->millis == lastmillis ? nullptr : d->ragdoll);
1176 0 : if(!(as->cur.anim & Anim_NoRender))
1177 : {
1178 0 : int owner = &sc-&skel->skelcache[0];
1179 0 : vbocacheentry &vc = vbocache[0];
1180 0 : vc.millis = lastmillis;
1181 0 : if(!vc.vbuf)
1182 : {
1183 0 : genvbo(vc);
1184 : }
1185 0 : blendcacheentry *bc = nullptr;
1186 0 : if(vblends)
1187 : {
1188 0 : bc = &checkblendcache(sc, owner);
1189 0 : bc->millis = lastmillis;
1190 0 : if(bc->owner!=owner)
1191 : {
1192 0 : bc->owner = owner;
1193 0 : *reinterpret_cast<animcacheentry *>(bc) = sc;
1194 0 : blendbones(sc, *bc);
1195 : }
1196 : }
1197 :
1198 0 : bindvbo(as, p, vc);
1199 :
1200 0 : LOOP_RENDER_MESHES(skelmesh, m,
1201 : {
1202 : p->skins[i].bind(m, as, true, vweights);
1203 : skel->setgpubones(sc, bc, vblends);
1204 : m.render();
1205 : });
1206 : }
1207 :
1208 0 : skel->calctags(p, &sc);
1209 :
1210 0 : if(as->cur.anim & Anim_Ragdoll && skel->ragdoll && !d->ragdoll)
1211 : {
1212 0 : d->ragdoll = new ragdolldata(skel->ragdoll, p->model->locationsize().w);
1213 0 : skel->initragdoll(*d->ragdoll, sc, p->model->locationsize().w);
1214 0 : d->ragdoll->init(d);
1215 : }
1216 : }
1217 :
1218 0 : void skelmodel::skelmeshgroup::bindbones(const vvertgw *vverts)
1219 : {
1220 0 : meshgroup::bindbones(vverts->weights, vverts->bones, vertsize);
1221 0 : }
1222 :
1223 : //blendcombo
1224 :
1225 478 : skelmodel::blendcombo::blendcombo() : uses(1)
1226 : {
1227 478 : bonedata.fill({0,0,0});
1228 478 : }
1229 :
1230 499 : bool skelmodel::blendcombo::operator==(const blendcombo &c) const
1231 : {
1232 4510 : for(size_t i = 0; i < bonedata.size(); ++i)
1233 : {
1234 3572 : if((bonedata[i].bone != c.bonedata[i].bone) ||
1235 1756 : (bonedata[i].weight != c.bonedata[i].weight))
1236 : {
1237 60 : return false;
1238 : }
1239 : }
1240 439 : return true;
1241 : }
1242 :
1243 533 : size_t skelmodel::blendcombo::size() const
1244 : {
1245 533 : size_t i = 1;
1246 1078 : while(i < bonedata.size() && bonedata[i].weight)
1247 : {
1248 6 : i++;
1249 : }
1250 533 : return i;
1251 : }
1252 :
1253 6 : bool skelmodel::blendcombo::sortcmp(const blendcombo &x, const blendcombo &y)
1254 : {
1255 36 : for(size_t i = 0; i < x.bonedata.size(); ++i)
1256 : {
1257 17 : if(x.bonedata[i].weight)
1258 : {
1259 13 : if(!y.bonedata[i].weight)
1260 : {
1261 1 : return true;
1262 : }
1263 : }
1264 4 : else if(y.bonedata[i].weight)
1265 : {
1266 1 : return false;
1267 : }
1268 : else
1269 : {
1270 3 : break;
1271 : }
1272 : }
1273 4 : return false;
1274 : }
1275 :
1276 504 : int skelmodel::blendcombo::addweight(int sorted, float weight, int bone)
1277 : {
1278 504 : if(weight <= 1e-3f) //do not add trivially small weights
1279 : {
1280 60 : return sorted;
1281 : }
1282 450 : for(int k = 0; k < sorted; ++k)
1283 : {
1284 9 : if(weight > bonedata[k].weight)
1285 : {
1286 : //push weights in bonedata to make room for new larger weights
1287 10 : for(int l = std::min(sorted-1, 2); l >= k; l--)
1288 : {
1289 7 : bonedata[l+1].weight = bonedata[l].weight;
1290 7 : bonedata[l+1].bone = bonedata[l].bone;
1291 : }
1292 3 : bonedata[k].weight = weight;
1293 3 : bonedata[k].bone = bone;
1294 6 : return sorted < static_cast<int>(bonedata.size()) ? sorted+1 : sorted;
1295 : }
1296 : }
1297 882 : if(sorted >= static_cast<int>(bonedata.size()))
1298 : {
1299 1 : return sorted;
1300 : }
1301 440 : bonedata[sorted].weight = weight;
1302 440 : bonedata[sorted].bone = bone;
1303 440 : return sorted+1;
1304 : }
1305 :
1306 443 : void skelmodel::blendcombo::finalize(int sorted)
1307 : {
1308 443 : if(sorted <= 0)
1309 : {
1310 1 : return;
1311 : }
1312 442 : float total = 0;
1313 894 : for(int i = 0; i < sorted; ++i)
1314 : {
1315 452 : total += bonedata[i].weight;
1316 : }
1317 442 : total = 1.0f/total;
1318 894 : for(int i = 0; i < sorted; ++i)
1319 : {
1320 452 : bonedata[i].weight *= total;
1321 : }
1322 : }
1323 :
1324 7 : void skelmodel::blendcombo::serialize(skelmodel::vvertgw &v) const
1325 : {
1326 7 : if(interpindex >= 0)
1327 : {
1328 2 : v.weights[0] = 255;
1329 8 : for(int k = 0; k < 3; ++k)
1330 : {
1331 6 : v.weights[k+1] = 0;
1332 : }
1333 2 : v.bones[0] = 2*interpindex;
1334 8 : for(int k = 0; k < 3; ++k)
1335 : {
1336 6 : v.bones[k+1] = v.bones[0];
1337 : }
1338 : }
1339 : else
1340 : {
1341 5 : int total = 0;
1342 50 : for(size_t k = 0; k < bonedata.size(); ++k)
1343 : {
1344 20 : v.weights[k] = static_cast<uchar>(0.5f + bonedata[k].weight*255);
1345 20 : total += (v.weights[k]);
1346 : }
1347 113 : while(total > 255)
1348 : {
1349 540 : for(size_t k = 0; k < 4; ++k)
1350 : {
1351 432 : if(v.weights[k] > 0 && total > 255)
1352 : {
1353 412 : v.weights[k]--;
1354 412 : total--;
1355 : }
1356 : }
1357 : }
1358 113 : while(total < 255)
1359 : {
1360 540 : for(size_t k = 0; k < 4; ++k)
1361 : {
1362 432 : if(v.weights[k] < 255 && total < 255)
1363 : {
1364 429 : v.weights[k]++;
1365 429 : total++;
1366 : }
1367 : }
1368 : }
1369 50 : for(size_t k = 0; k < bonedata.size(); ++k)
1370 : {
1371 20 : v.bones[k] = 2*bonedata[k].interpbone;
1372 : }
1373 : }
1374 7 : }
1375 :
1376 2 : dualquat skelmodel::blendcombo::blendbones(const dualquat *bdata) const
1377 : {
1378 2 : dualquat d = bdata[bonedata[0].interpbone];
1379 2 : d.mul(bonedata[0].weight);
1380 2 : d.accumulate(bdata[bonedata[1].interpbone], bonedata[1].weight);
1381 2 : if(bonedata[2].weight)
1382 : {
1383 2 : d.accumulate(bdata[bonedata[2].interpbone], bonedata[2].weight);
1384 2 : if(bonedata[3].weight)
1385 : {
1386 1 : d.accumulate(bdata[bonedata[3].interpbone], bonedata[3].weight);
1387 : }
1388 : }
1389 2 : return d;
1390 : }
1391 :
1392 22 : void skelmodel::blendcombo::setinterpbones(int val, size_t index)
1393 : {
1394 22 : bonedata[index].interpbone = val;
1395 22 : }
1396 :
1397 30 : int skelmodel::blendcombo::getbone(size_t index)
1398 : {
1399 30 : return bonedata[index].bone;
1400 : }
1401 :
1402 : template<class T>
1403 0 : T &searchcache(size_t cachesize, T *cache, const skelmodel::skelcacheentry &sc, int owner)
1404 : {
1405 0 : for(size_t i = 0; i < cachesize; ++i)
1406 : {
1407 0 : T &c = cache[i];
1408 0 : if(c.owner==owner)
1409 : {
1410 0 : if(c==sc)
1411 : {
1412 0 : return c;
1413 : }
1414 : else
1415 : {
1416 0 : c.owner = -1;
1417 : }
1418 0 : break;
1419 : }
1420 : }
1421 0 : for(size_t i = 0; i < cachesize-1; ++i)
1422 : {
1423 0 : T &c = cache[i];
1424 0 : if(c.owner < 0 || c.millis < lastmillis)
1425 : {
1426 0 : return c;
1427 : }
1428 : }
1429 0 : return cache[cachesize-1];
1430 : }
1431 :
1432 0 : skelmodel::blendcacheentry &skelmodel::skelmeshgroup::checkblendcache(const skelcacheentry &sc, int owner)
1433 : {
1434 0 : return searchcache<blendcacheentry>(maxblendcache, blendcache.data(), sc, owner);
1435 : }
1436 :
1437 : //skelmesh
1438 :
1439 0 : skelmodel::skelmesh::skelmesh() : tris(nullptr), numtris(0), verts(nullptr), numverts(0), maxweights(0)
1440 : {
1441 0 : }
1442 :
1443 4 : skelmodel::skelmesh::skelmesh(std::string_view name, vert *verts, uint numverts, tri *tris, uint numtris, meshgroup *m) : Mesh(name, m),
1444 4 : tris(tris),
1445 4 : numtris(numtris),
1446 4 : verts(verts),
1447 4 : numverts(numverts),
1448 4 : maxweights(0)
1449 : {
1450 4 : }
1451 :
1452 3 : skelmodel::skelmesh::~skelmesh()
1453 : {
1454 3 : delete[] verts;
1455 3 : delete[] tris;
1456 3 : }
1457 :
1458 438 : int skelmodel::skelmesh::addblendcombo(const blendcombo &c)
1459 : {
1460 438 : maxweights = std::max(maxweights, static_cast<int>(c.size()));
1461 438 : return (reinterpret_cast<skelmeshgroup *>(group))->addblendcombo(c);
1462 : }
1463 :
1464 0 : void skelmodel::skelmesh::smoothnorms(float limit, bool areaweight)
1465 : {
1466 0 : Mesh::smoothnorms<skelmodel>(verts, numverts, tris, numtris, limit, areaweight);
1467 0 : }
1468 :
1469 3 : void skelmodel::skelmesh::buildnorms(bool areaweight)
1470 : {
1471 3 : Mesh::buildnorms<skelmodel>(verts, numverts, tris, numtris, areaweight);
1472 3 : }
1473 :
1474 1 : void skelmodel::skelmesh::calctangents(bool areaweight)
1475 : {
1476 1 : Mesh::calctangents<skelmodel, skelmodel::vert>(verts, verts, numverts, tris, numtris, areaweight);
1477 1 : }
1478 :
1479 2 : void skelmodel::skelmesh::calcbb(vec &bbmin, vec &bbmax, const matrix4x3 &m) const
1480 : {
1481 10 : for(int j = 0; j < numverts; ++j)
1482 : {
1483 8 : vec v = m.transform(verts[j].pos);
1484 8 : bbmin.min(v);
1485 8 : bbmax.max(v);
1486 : }
1487 2 : }
1488 :
1489 0 : void skelmodel::skelmesh::genBIH(BIH::mesh &m) const
1490 : {
1491 0 : m.setmesh(reinterpret_cast<const BIH::mesh::tri *>(tris), numtris,
1492 0 : reinterpret_cast<const uchar *>(&verts->pos), sizeof(vert),
1493 0 : reinterpret_cast<const uchar *>(&verts->tc), sizeof(vert));
1494 0 : }
1495 :
1496 0 : void skelmodel::skelmesh::genshadowmesh(std::vector<triangle> &out, const matrix4x3 &m) const
1497 : {
1498 0 : for(int j = 0; j < numtris; ++j)
1499 : {
1500 0 : triangle t;
1501 0 : t.a = m.transform(verts[tris[j].vert[0]].pos);
1502 0 : t.b = m.transform(verts[tris[j].vert[1]].pos);
1503 0 : t.c = m.transform(verts[tris[j].vert[2]].pos);
1504 0 : out.push_back(t);
1505 : }
1506 0 : }
1507 :
1508 1 : void skelmodel::skelmesh::assignvert(vvertg &vv, const vert &v)
1509 : {
1510 1 : vv.pos = vec4<half>(v.pos, 1);
1511 1 : vv.tc = v.tc;
1512 1 : vv.tangent = v.tangent;
1513 1 : }
1514 :
1515 1 : void skelmodel::skelmesh::assignvert(vvertgw &vv, const vert &v, const blendcombo &c)
1516 : {
1517 1 : vv.pos = vec4<half>(v.pos, 1);
1518 1 : vv.tc = v.tc;
1519 1 : vv.tangent = v.tangent;
1520 1 : c.serialize(vv);
1521 1 : }
1522 :
1523 0 : int skelmodel::skelmesh::genvbo(const std::vector<blendcombo> &bcs, std::vector<GLuint> &idxs, int offset, std::vector<vvertgw> &vverts)
1524 : {
1525 0 : voffset = offset;
1526 0 : eoffset = idxs.size();
1527 0 : for(int i = 0; i < numverts; ++i)
1528 : {
1529 0 : const vert &v = verts[i];
1530 0 : vverts.emplace_back(vvertgw());
1531 0 : assignvert(vverts.back(), v, bcs[v.blend]);
1532 : }
1533 0 : for(int i = 0; i < numtris; ++i)
1534 : {
1535 0 : for(int j = 0; j < 3; ++j)
1536 : {
1537 0 : idxs.push_back(voffset + tris[i].vert[j]);
1538 : }
1539 : }
1540 0 : elen = idxs.size()-eoffset;
1541 0 : minvert = voffset;
1542 0 : maxvert = voffset + numverts-1;
1543 0 : return numverts;
1544 : }
1545 :
1546 0 : int skelmodel::skelmesh::genvbo(std::vector<GLuint> &idxs, int offset, std::vector<vvertg> &vverts, int *htdata, int htlen)
1547 : {
1548 0 : voffset = offset;
1549 0 : eoffset = idxs.size();
1550 0 : minvert = 0xFFFF;
1551 0 : for(int i = 0; i < numtris; ++i)
1552 : {
1553 0 : const tri &t = tris[i];
1554 0 : for(int j = 0; j < 3; ++j)
1555 : {
1556 0 : const uint index = t.vert[j];
1557 0 : const vert &v = verts[index];
1558 0 : vvertg vv;
1559 0 : assignvert(vv, v);
1560 : const auto hashfn = std::hash<vec>();
1561 0 : int htidx = hashfn(v.pos)&(htlen-1);
1562 0 : for(int k = 0; k < htlen; ++k)
1563 : {
1564 0 : int &vidx = htdata[(htidx+k)&(htlen-1)];
1565 0 : if(vidx < 0)
1566 : {
1567 0 : vidx = idxs.emplace_back(static_cast<GLuint>(vverts.size()));
1568 0 : vverts.push_back(vv);
1569 0 : break;
1570 : }
1571 0 : else if(!std::memcmp(&vverts[vidx], &vv, sizeof(vv)))
1572 : {
1573 0 : minvert = std::min(minvert, idxs.emplace_back(static_cast<GLuint>(vidx)));
1574 0 : break;
1575 : }
1576 : }
1577 : }
1578 : }
1579 0 : elen = idxs.size()-eoffset;
1580 0 : minvert = std::min(minvert, static_cast<GLuint>(voffset));
1581 0 : maxvert = std::max(minvert, static_cast<GLuint>(vverts.size()-1));
1582 0 : return vverts.size()-voffset;
1583 : }
1584 :
1585 0 : void skelmodel::skelmesh::setshader(Shader *s, bool usegpuskel, int vweights, int row) const
1586 : {
1587 0 : if(row)
1588 : {
1589 0 : s->setvariant(usegpuskel ? std::min(maxweights, vweights) : 0, row);
1590 : }
1591 0 : else if(usegpuskel)
1592 : {
1593 0 : s->setvariant(std::min(maxweights, vweights)-1, 0);
1594 : }
1595 : else
1596 : {
1597 0 : s->set();
1598 : }
1599 0 : }
1600 :
1601 0 : void skelmodel::skelmesh::render() const
1602 : {
1603 0 : if(!Shader::lastshader)
1604 : {
1605 0 : return;
1606 : }
1607 0 : glDrawRangeElements(GL_TRIANGLES, minvert, maxvert, elen, GL_UNSIGNED_INT, &(static_cast<skelmeshgroup *>(group))->edata[eoffset]);
1608 0 : glde++;
1609 0 : xtravertsva += numverts;
1610 : }
1611 :
1612 1 : void skelmodel::skelmesh::remapverts(const std::vector<int> remap)
1613 : {
1614 439 : for(int i = 0; i < numverts; ++i)
1615 : {
1616 438 : vert &v = verts[i];
1617 438 : v.blend = remap[v.blend];
1618 : }
1619 1 : }
1620 :
1621 4 : int skelmodel::skelmesh::vertcount() const
1622 : {
1623 4 : return numverts;
1624 : }
1625 :
1626 4 : int skelmodel::skelmesh::tricount() const
1627 : {
1628 4 : return numtris;
1629 : }
1630 :
1631 8 : const skelmodel::vert &skelmodel::skelmesh::getvert(size_t index) const
1632 : {
1633 8 : return verts[index];
1634 : }
1635 :
1636 : // boneinfo
1637 :
1638 4 : skelmodel::skeleton::boneinfo::boneinfo() :
1639 8 : name(""),
1640 4 : parent(-1),
1641 4 : children(-1),
1642 4 : next(-1),
1643 4 : group(INT_MAX),
1644 4 : scheduled(-1),
1645 4 : interpindex(-1),
1646 4 : interpparent(-1),
1647 4 : ragdollindex(-1),
1648 4 : correctindex(-1),
1649 4 : pitchscale(0),
1650 4 : pitchoffset(0),
1651 4 : pitchmin(0),
1652 4 : pitchmax(0)
1653 : {
1654 4 : }
1655 :
1656 : // skelmeshgroup
1657 :
1658 0 : std::optional<size_t> skelmodel::skelmeshgroup::findtag(std::string_view name)
1659 : {
1660 0 : return skel->findtag(name);
1661 : }
1662 :
1663 0 : void *skelmodel::skelmeshgroup::animkey()
1664 : {
1665 0 : return skel;
1666 : }
1667 :
1668 2 : int skelmodel::skelmeshgroup::totalframes() const
1669 : {
1670 2 : return std::max(skel->numframes, size_t(1));
1671 : }
1672 :
1673 0 : void skelmodel::skelmeshgroup::bindvbo(const AnimState *as, const part *p, const vbocacheentry &vc)
1674 : {
1675 0 : if(!skel->numframes)
1676 : {
1677 0 : bindvbo<vvertg>(as, p, vc);
1678 : }
1679 : else
1680 : {
1681 0 : bindvbo<vvertgw>(as, p, vc);
1682 : }
1683 0 : }
1684 :
1685 0 : void skelmodel::skelmeshgroup::concattagtransform(int i, const matrix4x3 &m, matrix4x3 &n) const
1686 : {
1687 0 : skel->concattagtransform(i, m, n);
1688 0 : }
1689 :
1690 438 : int skelmodel::skelmeshgroup::addblendcombo(const blendcombo &c)
1691 : {
1692 497 : for(size_t i = 0; i < blendcombos.size(); i++)
1693 : {
1694 495 : if(blendcombos[i]==c)
1695 : {
1696 436 : blendcombos[i].uses += c.uses;
1697 436 : return i;
1698 : }
1699 : }
1700 2 : numblends[c.size()-1]++;
1701 2 : blendcombos.push_back(c);
1702 2 : blendcombo &a = blendcombos.back();
1703 2 : a.interpindex = blendcombos.size()-1;
1704 2 : return a.interpindex;
1705 : }
1706 :
1707 1 : void skelmodel::skelmeshgroup::sortblendcombos()
1708 : {
1709 1 : std::sort(blendcombos.begin(), blendcombos.end(), blendcombo::sortcmp);
1710 1 : std::vector<int> remap(blendcombos.size(), 0);
1711 3 : for(size_t i = 0; i < blendcombos.size(); i++)
1712 : {
1713 2 : remap[blendcombos[i].interpindex] = i;
1714 : }
1715 1 : std::vector<std::vector<animmodel::Mesh *>::iterator> rendermeshes = getrendermeshes();
1716 2 : for(std::vector<animmodel::Mesh *>::iterator i : rendermeshes)
1717 : {
1718 1 : skelmesh *s = static_cast<skelmesh *>(*i);
1719 1 : s->remapverts(remap);
1720 : }
1721 1 : }
1722 :
1723 0 : void skelmodel::skelmeshgroup::blendbones(const skelcacheentry &sc, blendcacheentry &bc) const
1724 : {
1725 0 : bc.nextversion();
1726 0 : if(!bc.bdata)
1727 : {
1728 0 : bc.bdata = new dualquat[vblends];
1729 : }
1730 0 : dualquat *dst = bc.bdata - skel->numgpubones;
1731 0 : bool normalize = vweights<=1;
1732 0 : for(const blendcombo &c : blendcombos)
1733 : {
1734 0 : if(c.interpindex<0)
1735 : {
1736 0 : break;
1737 : }
1738 0 : dualquat &d = dst[c.interpindex];
1739 0 : d = c.blendbones(sc.bdata);
1740 0 : if(normalize)
1741 : {
1742 0 : d.normalize();
1743 : }
1744 : }
1745 0 : }
1746 :
1747 11 : void skelmodel::skelmeshgroup::cleanup()
1748 : {
1749 187 : for(size_t i = 0; i < maxblendcache; ++i)
1750 : {
1751 176 : blendcacheentry &c = blendcache[i];
1752 176 : delete[] c.bdata;
1753 176 : c.bdata = nullptr;
1754 176 : c.owner = -1;
1755 : }
1756 187 : for(size_t i = 0; i < maxvbocache; ++i)
1757 : {
1758 176 : vbocacheentry &c = vbocache[i];
1759 176 : if(c.vbuf)
1760 : {
1761 0 : glDeleteBuffers(1, &c.vbuf);
1762 0 : c.vbuf = 0;
1763 : }
1764 176 : c.owner = -1;
1765 : }
1766 11 : if(ebuf)
1767 : {
1768 0 : glDeleteBuffers(1, &ebuf);
1769 0 : ebuf = 0;
1770 : }
1771 11 : if(skel)
1772 : {
1773 11 : skel->cleanup(false);
1774 : }
1775 11 : }
1776 :
1777 0 : void skelmodel::skelmeshgroup::preload()
1778 : {
1779 0 : if(skel->shouldcleanup())
1780 : {
1781 0 : skel->cleanup();
1782 : }
1783 0 : if(!vbocache[0].vbuf)
1784 : {
1785 0 : genvbo(vbocache[0]);
1786 : }
1787 0 : }
1788 :
1789 : // skelpart
1790 :
1791 19 : skelmodel::skelpart::skelpart(animmodel *model, int index) : part(model, index)
1792 : {
1793 19 : }
1794 :
1795 38 : skelmodel::skelpart::~skelpart()
1796 : {
1797 38 : }
1798 :
1799 11 : std::vector<uchar> &skelmodel::skelpart::sharepartmask(std::vector<uchar> &o)
1800 : {
1801 11 : static std::vector<std::vector<uchar>> partmasks;
1802 12 : for(std::vector<uchar> &p : partmasks)
1803 : {
1804 10 : if(p == o)
1805 : {
1806 9 : o.clear();
1807 9 : return p;
1808 : }
1809 : }
1810 2 : partmasks.push_back(o);
1811 2 : o.clear();
1812 2 : return partmasks.back();
1813 : }
1814 :
1815 17 : std::vector<uchar> skelmodel::skelpart::newpartmask()
1816 : {
1817 17 : return std::vector<uchar>((static_cast<skelmeshgroup *>(meshes))->skel->numbones, 0);
1818 :
1819 : }
1820 :
1821 17 : void skelmodel::skelpart::initanimparts()
1822 : {
1823 17 : buildingpartmask = newpartmask();
1824 17 : }
1825 :
1826 1 : bool skelmodel::skelpart::addanimpart(const std::vector<uint> &bonemask)
1827 : {
1828 1 : if(buildingpartmask.empty() || numanimparts>=maxanimparts)
1829 : {
1830 0 : return false;
1831 : }
1832 1 : (static_cast<skelmeshgroup *>(meshes))->skel->applybonemask(bonemask, buildingpartmask, numanimparts);
1833 1 : numanimparts++;
1834 1 : return true;
1835 : }
1836 :
1837 11 : void skelmodel::skelpart::endanimparts()
1838 : {
1839 11 : if(buildingpartmask.size())
1840 : {
1841 11 : partmask = sharepartmask(buildingpartmask);
1842 11 : buildingpartmask.clear();
1843 : }
1844 :
1845 11 : (static_cast<skelmeshgroup *>(meshes))->skel->optimize();
1846 11 : }
1847 :
1848 11 : void skelmodel::skelpart::loaded()
1849 : {
1850 11 : endanimparts();
1851 11 : part::loaded();
1852 11 : }
1853 :
1854 : //skelmodel
1855 :
1856 20 : skelmodel::skelmodel(std::string name) : animmodel(std::move(name))
1857 : {
1858 20 : }
1859 :
1860 19 : skelmodel::skelpart &skelmodel::addpart()
1861 : {
1862 19 : skelpart *p = new skelpart(this, parts.size());
1863 19 : parts.push_back(p);
1864 19 : return *p;
1865 : }
1866 :
1867 3 : animmodel::meshgroup * skelmodel::loadmeshes(const std::string &name, float smooth)
1868 : {
1869 3 : skelmeshgroup *group = newmeshes();
1870 3 : group->skel = new skeleton(group);
1871 3 : part &p = *parts.back();
1872 3 : if(!group->load(name, smooth, p))
1873 : {
1874 2 : delete group;
1875 2 : return nullptr;
1876 : }
1877 1 : return group;
1878 : }
1879 :
1880 19 : animmodel::meshgroup * skelmodel::sharemeshes(const std::string &name, float smooth)
1881 : {
1882 19 : if(meshgroups.find(name) == meshgroups.end())
1883 : {
1884 3 : meshgroup *group = loadmeshes(name, smooth);
1885 3 : if(!group)
1886 : {
1887 2 : return nullptr;
1888 : }
1889 1 : meshgroups[group->groupname()] = group;
1890 : }
1891 17 : return meshgroups[name];
1892 : }
1893 :
1894 : //skelmodel overrides
1895 :
1896 0 : int skelmodel::linktype(const animmodel *m, const part *p) const
1897 : {
1898 0 : return type()==m->type() &&
1899 0 : (static_cast<skelmeshgroup *>(parts[0]->meshes))->skel == (static_cast<skelmeshgroup *>(p->meshes))->skel ?
1900 : Link_Reuse :
1901 0 : Link_Tag;
1902 : }
1903 :
1904 0 : bool skelmodel::skeletal() const
1905 : {
1906 0 : return true;
1907 : }
1908 :
1909 : /* ==== skeladjustment ==== */
1910 : /*======================================*/
1911 :
1912 0 : void skeladjustment::adjust(dualquat &dq) const
1913 : {
1914 0 : if(yaw)
1915 : {
1916 0 : dq.mulorient(quat(vec(0, 0, 1), yaw/RAD));
1917 : }
1918 0 : if(pitch)
1919 : {
1920 0 : dq.mulorient(quat(vec(0, -1, 0), pitch/RAD));
1921 : }
1922 0 : if(roll)
1923 : {
1924 0 : dq.mulorient(quat(vec(-1, 0, 0), roll/RAD));
1925 : }
1926 0 : if(!translate.iszero())
1927 : {
1928 0 : dq.translate(translate);
1929 : }
1930 0 : }
|