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