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