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