Inexor
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
skelmodel.hpp
Go to the documentation of this file.
1 
4 #pragma once
5 
7 #define BONEMASK_NOT 0x8000
8 #define BONEMASK_END 0xFFFF
9 #define BONEMASK_BONE 0x7FFF
10 
12 {
13  struct vert { vec pos, norm; vec2 tc; int blend, interpindex; };
14  struct vvert { vec pos; vec2 tc; };
15  struct vvertn : vvert { vec norm; };
16  struct vvertbump : vvert { squat tangent; };
17  struct vvertw { uchar weights[4]; uchar bones[4]; };
18  struct vvertnw : vvertn, vvertw {};
19  struct vvertbumpw : vvertbump, vvertw {};
20  struct bumpvert { quat tangent; };
21  struct tri { ushort vert[3]; };
22 
23  struct blendcombo
24  {
26  float weights[4];
28 
30  {
31  }
32 
33  bool operator==(const blendcombo &c) const
34  {
35  loopk(4) if(bones[k] != c.bones[k]) return false;
36  loopk(4) if(weights[k] != c.weights[k]) return false;
37  return true;
38  }
39 
40  int size() const
41  {
42  int i = 1;
43  while(i < 4 && weights[i]) i++;
44  return i;
45  }
46 
47  static bool sortcmp(const blendcombo &x, const blendcombo &y)
48  {
49  loopi(4)
50  {
51  if(x.weights[i])
52  {
53  if(!y.weights[i]) return true;
54  }
55  else if(y.weights[i]) return false;
56  else break;
57  }
58  return false;
59  }
60 
61  int addweight(int sorted, float weight, int bone)
62  {
63  if(weight <= 1e-3f) return sorted;
64  loopk(sorted) if(weight > weights[k])
65  {
66  for(int l = min(sorted-1, 2); l >= k; l--)
67  {
68  weights[l+1] = weights[l];
69  bones[l+1] = bones[l];
70  }
71  weights[k] = weight;
72  bones[k] = bone;
73  return sorted<4 ? sorted+1 : sorted;
74  }
75  if(sorted>=4) return sorted;
76  weights[sorted] = weight;
77  bones[sorted] = bone;
78  return sorted+1;
79  }
80 
81  void finalize(int sorted)
82  {
83  loopj(4-sorted) { weights[sorted+j] = 0; bones[sorted+j] = 0; }
84  if(sorted <= 0) return;
85  float total = 0;
86  loopj(sorted) total += weights[j];
87  total = 1.0f/total;
88  loopj(sorted) weights[j] *= total;
89  }
90 
91  void serialize(vvertw &v)
92  {
93  if(interpindex >= 0)
94  {
95  v.weights[0] = 255;
96  loopk(3) v.weights[k+1] = 0;
97  v.bones[0] = 2*interpindex;
98  loopk(3) v.bones[k+1] = v.bones[0];
99  }
100  else
101  {
102  int total = 0;
103  loopk(4) total += (v.weights[k] = uchar(0.5f + weights[k]*255));
104  while(total > 255)
105  {
106  loopk(4) if(v.weights[k] > 0 && total > 255) { v.weights[k]--; total--; }
107  }
108  while(total < 255)
109  {
110  loopk(4) if(v.weights[k] < 255 && total < 255) { v.weights[k]++; total++; }
111  }
112  loopk(4) v.bones[k] = 2*interpbones[k];
113  }
114  }
115  };
116 
117 
119  {
120  animstate as[MAXANIMPARTS];
121  float pitch;
122  int millis;
125 
126  animcacheentry() : ragdoll(nullptr)
127  {
128  loopk(MAXANIMPARTS) as[k].cur.fr1 = as[k].prev.fr1 = -1;
129  }
130 
131  bool operator==(const animcacheentry &c) const
132  {
133  loopi(MAXANIMPARTS) if(as[i]!=c.as[i]) return false;
134  return pitch==c.pitch && partmask==c.partmask && ragdoll==c.ragdoll && (!ragdoll || min(millis, c.millis) >= ragdoll->lastmove);
135  }
136  };
137 
139  {
141  int owner;
142 
143  vbocacheentry() : vbuf(0), owner(-1) {}
144  };
145 
147  {
149  int version;
150  bool dirty;
151 
152  skelcacheentry() : bdata(nullptr), version(-1), dirty(false) {}
153 
154  void nextversion()
155  {
157  dirty = true;
158  }
159  };
160 
162  {
163  int owner;
164 
166  };
167 
168  struct skelmeshgroup;
169 
170  struct skelmesh : mesh
171  {
176 
179 
180  skelmesh() : verts(nullptr), bumpverts(nullptr), tris(nullptr), numverts(0), numtris(0), maxweights(0)
181  {
182  }
183 
184  ~skelmesh() override
185  {
186  DELETEA(verts);
188  DELETEA(tris);
189  }
190 
191  int addblendcombo(const blendcombo &c)
192  {
193  maxweights = max(maxweights, c.size());
194  return ((skelmeshgroup *)group)->addblendcombo(c);
195  }
196 
197  void smoothnorms(float limit = 0, bool areaweight = true)
198  {
199  mesh::smoothnorms(verts, numverts, tris, numtris, limit, areaweight);
200  }
201 
202  void buildnorms(bool areaweight = true)
203  {
204  mesh::buildnorms(verts, numverts, tris, numtris, areaweight);
205  }
206 
207  void calctangents(bool areaweight = true)
208  {
209  if(bumpverts) return;
210  bumpverts = new bumpvert[numverts];
212  }
213 
214  void calcbb(vec &bbmin, vec &bbmax, const matrix4x3 &m) override
215  {
216  loopj(numverts)
217  {
218  vec v = m.transform(verts[j].pos);
219  loopi(3)
220  {
221  bbmin[i] = min(bbmin[i], v[i]);
222  bbmax[i] = max(bbmax[i], v[i]);
223  }
224  }
225  }
226 
227  void genBIH(BIH::mesh &m) override
228  {
229  m.tris = (const BIH::tri *)tris;
230  m.numtris = numtris;
231  m.pos = (const uchar *)&verts->pos;
232  m.posstride = sizeof(vert);
233  m.tc = (const uchar *)&verts->tc;
234  m.tcstride = sizeof(vert);
235  }
236 
237  static inline void assignvert(vvertn &vv, int j, vert &v, blendcombo &c)
238  {
239  vv.pos = v.pos;
240  vv.norm = v.norm;
241  vv.tc = v.tc;
242  }
243 
244  inline void assignvert(vvertbump &vv, int j, vert &v, blendcombo &c)
245  {
246  vv.pos = v.pos;
247  vv.tc = v.tc;
248  vv.tangent = bumpverts[j].tangent;
249  }
250 
251  static inline void assignvert(vvertnw &vv, int j, vert &v, blendcombo &c)
252  {
253  vv.pos = v.pos;
254  vv.norm = v.norm;
255  vv.tc = v.tc;
256  c.serialize(vv);
257  }
258 
259  inline void assignvert(vvertbumpw &vv, int j, vert &v, blendcombo &c)
260  {
261  vv.pos = v.pos;
262  vv.tc = v.tc;
263  vv.tangent = bumpverts[j].tangent;
264  c.serialize(vv);
265  }
266 
267  template<class T>
269  {
270  voffset = offset;
271  eoffset = idxs.length();
272  loopi(numverts)
273  {
274  vert &v = verts[i];
275  assignvert(vverts.add(), i, v, ((skelmeshgroup *)group)->blendcombos[v.blend]);
276  }
277  loopi(numtris) loopj(3) idxs.add(voffset + tris[i].vert[j]);
278  elen = idxs.length()-eoffset;
279  minvert = voffset;
280  maxvert = voffset + numverts-1;
281  return numverts;
282  }
283 
284  template<class T>
285  int genvbo(vector<ushort> &idxs, int offset, vector<T> &vverts, int *htdata, int htlen)
286  {
287  voffset = offset;
288  eoffset = idxs.length();
289  minvert = 0xFFFF;
290  loopi(numtris)
291  {
292  tri &t = tris[i];
293  loopj(3)
294  {
295  int index = t.vert[j];
296  vert &v = verts[index];
297  T vv;
298  assignvert(vv, index, v, ((skelmeshgroup *)group)->blendcombos[v.blend]);
299  int htidx = hthash(v.pos)&(htlen-1);
300  loopk(htlen)
301  {
302  int &vidx = htdata[(htidx+k)&(htlen-1)];
303  if(vidx < 0) { vidx = idxs.add(ushort(vverts.length())); vverts.add(vv); break; }
304  else if(!memcmp(&vverts[vidx], &vv, sizeof(vv))) { minvert = min(minvert, idxs.add(ushort(vidx))); break; }
305  }
306  }
307  }
308  elen = idxs.length()-eoffset;
310  maxvert = max(minvert, ushort(vverts.length()-1));
311  return vverts.length()-voffset;
312  }
313 
314  int genvbo(vector<ushort> &idxs, int offset)
315  {
316  loopi(numverts) verts[i].interpindex = ((skelmeshgroup *)group)->remapblend(verts[i].blend);
317 
318  voffset = offset;
319  eoffset = idxs.length();
320  loopi(numtris)
321  {
322  tri &t = tris[i];
323  loopj(3) idxs.add(voffset+t.vert[j]);
324  }
325  minvert = voffset;
326  maxvert = voffset + numverts-1;
327  elen = idxs.length()-eoffset;
328  return numverts;
329  }
330 
331  template<class T>
332  static inline void fillvert(T &vv, int j, vert &v)
333  {
334  vv.tc = v.tc;
335  }
336 
337  template<class T>
338  void fillverts(T *vdata)
339  {
340  vdata += voffset;
341  loopi(numverts) fillvert(vdata[i], i, verts[i]);
342  }
343 
344  void interpverts(const dualquat * RESTRICT bdata1, const dualquat * RESTRICT bdata2, bool tangents, void * RESTRICT vdata, skin &s)
345  {
346  const int blendoffset = ((skelmeshgroup *)group)->skel->numgpubones;
347  bdata2 -= blendoffset;
348 
349  #define IPLOOP(type, dosetup, dotransform) \
350  loopi(numverts) \
351  { \
352  const vert &src = verts[i]; \
353  type &dst = ((type * RESTRICT)vdata)[i]; \
354  dosetup; \
355  const dualquat &b = (src.interpindex < blendoffset ? bdata1 : bdata2)[src.interpindex]; \
356  dst.pos = b.transform(src.pos); \
357  dotransform; \
358  }
359 
360  if(tangents)
361  {
362  IPLOOP(vvertbump, bumpvert &bsrc = bumpverts[i],
363  {
364  quat q = b.transform(bsrc.tangent);
365  fixqtangent(q, bsrc.tangent.w);
366  dst.tangent = q;
367  });
368  }
369  else
370  {
371  IPLOOP(vvertn, ,
372  {
373  dst.norm = b.transformnormal(src.norm);
374  });
375  }
376 
377  #undef IPLOOP
378  }
379 
380  void setshader(Shader *s) override
381  {
382  skelmeshgroup *g = (skelmeshgroup *)group;
383  if(glaring)
384  {
385  if(!g->skel->usegpuskel) s->setvariant(0, 1);
386  else s->setvariant(min(maxweights, g->vweights), 1);
387  }
388  else if(!g->skel->usegpuskel) s->set();
389  else s->setvariant(min(maxweights, g->vweights)-1, 0);
390  }
391 
392  void render(const animstate *as, skin &s, vbocacheentry &vc)
393  {
394  glDrawRangeElements_(GL_TRIANGLES, minvert, maxvert, elen, GL_UNSIGNED_SHORT, &((skelmeshgroup *)group)->edata[eoffset]);
395  glde++;
397  }
398  };
399 
400 
401  struct tag
402  {
403  char *name;
404  int bone;
406 
407  tag() : name(nullptr) {}
408  ~tag() { DELETEA(name); }
409  };
410 
412  {
413  char *name;
414  int frame, range;
415 
416  skelanimspec() : name(nullptr), frame(0), range(0) {}
418  {
419  DELETEA(name);
420  }
421  };
422 
423  struct boneinfo
424  {
425  const char *name;
429 
430  boneinfo() : name(nullptr), parent(-1), children(-1), next(-1), group(INT_MAX), scheduled(-1), interpindex(-1), interpparent(-1), ragdollindex(-1), correctindex(-1), pitchscale(0), pitchoffset(0), pitchmin(0), pitchmax(0) {}
432  {
433  DELETEA(name);
434  }
435  };
436 
437  struct antipode
438  {
439  int parent, child;
440 
441  antipode(int parent, int child) : parent(parent), child(child) {}
442  };
443 
444  struct pitchdep
445  {
446  int bone, parent;
448  };
449 
450  struct pitchtarget
451  {
455  };
456 
458  {
461 
463  };
464 
465  struct skeleton
466  {
467  char *name;
468  int shared;
480 
484 
485  skeleton() : name(nullptr), shared(0), bones(nullptr), numbones(0), numinterpbones(0), numgpubones(0), numframes(0), framebones(nullptr), ragdoll(nullptr), usegpuskel(false), blendoffsets(32)
486  {
487  }
488 
490  {
491  DELETEA(name);
492  DELETEA(bones);
494  DELETEP(ragdoll);
496  {
497  DELETEA(skelcache[i].bdata);
498  }
499  }
500 
501  skelanimspec *findskelanim(const char *name, char sep = '\0')
502  {
503  int len = sep ? strlen(name) : 0;
505  {
506  if(skelanims[i].name)
507  {
508  if(sep)
509  {
510  const char *end = strchr(skelanims[i].name, ':');
511  if(end && end - skelanims[i].name == len && !memcmp(name, skelanims[i].name, len)) return &skelanims[i];
512  }
513  if(!strcmp(name, skelanims[i].name)) return &skelanims[i];
514  }
515  }
516  return nullptr;
517  }
518 
520  {
521  skelanimspec &sa = skelanims.add();
522  sa.name = name ? newstring(name) : nullptr;
523  return sa;
524  }
525 
526  int findbone(const char *name)
527  {
528  loopi(numbones) if(bones[i].name && !strcmp(bones[i].name, name)) return i;
529  return -1;
530  }
531 
532  int findtag(const char *name)
533  {
534  loopv(tags) if(!strcmp(tags[i].name, name)) return i;
535  return -1;
536  }
537 
538  bool addtag(const char *name, int bone, const matrix4x3 &matrix)
539  {
540  int idx = findtag(name);
541  if(idx >= 0)
542  {
543  if(!testtags) return false;
544  tag &t = tags[idx];
545  t.bone = bone;
546  t.matrix = matrix;
547  }
548  else
549  {
550  tag &t = tags.add();
551  t.name = newstring(name);
552  t.bone = bone;
553  t.matrix = matrix;
554  }
555  return true;
556  }
557 
559  {
560  antipodes.shrink(0);
561  vector<int> schedule;
562  loopi(numbones)
563  {
564  if(bones[i].group >= numbones)
565  {
566  bones[i].scheduled = schedule.length();
567  schedule.add(i);
568  }
569  else bones[i].scheduled = -1;
570  }
571  loopv(schedule)
572  {
573  int bone = schedule[i];
574  const boneinfo &info = bones[bone];
575  loopj(numbones) if(abs(bones[j].group) == bone && bones[j].scheduled < 0)
576  {
578  bones[j].scheduled = schedule.length();
579  schedule.add(j);
580  }
581  if(i + 1 == schedule.length())
582  {
583  int conflict = INT_MAX;
584  loopj(numbones) if(bones[j].group < numbones && bones[j].scheduled < 0) conflict = min(conflict, abs(bones[j].group));
585  if(conflict < numbones)
586  {
587  bones[conflict].scheduled = schedule.length();
588  schedule.add(conflict);
589  }
590  }
591  }
592  }
593 
594  void remapbones()
595  {
596  loopi(numbones)
597  {
598  boneinfo &info = bones[i];
599  info.interpindex = -1;
600  info.ragdollindex = -1;
601  }
602  numgpubones = 0;
603  loopv(users)
604  {
605  skelmeshgroup *group = users[i];
606  loopvj(group->blendcombos)
607  {
608  blendcombo &c = group->blendcombos[j];
609  loopk(4)
610  {
611  if(!c.weights[k]) { c.interpbones[k] = k > 0 ? c.interpbones[k-1] : 0; continue; }
612  boneinfo &info = bones[c.bones[k]];
613  if(info.interpindex < 0) info.interpindex = numgpubones++;
614  c.interpbones[k] = info.interpindex;
615  if(info.group < 0) continue;
616  loopl(4)
617  {
618  if(!c.weights[l]) break;
619  if(l == k) continue;
620  int parent = c.bones[l];
621  if(info.parent == parent || (info.parent >= 0 && info.parent == bones[parent].parent)) { info.group = -info.parent; break; }
622  if(info.group <= parent) continue;
623  int child = c.bones[k];
624  while(parent > child) parent = bones[parent].parent;
625  if(parent != child) info.group = c.bones[l];
626  }
627  }
628  }
629  }
631  loopv(tags)
632  {
633  boneinfo &info = bones[tags[i].bone];
634  if(info.interpindex < 0) info.interpindex = numinterpbones++;
635  }
636  if(ragdoll)
637  {
638  loopv(ragdoll->joints)
639  {
640  boneinfo &info = bones[ragdoll->joints[i].bone];
641  if(info.interpindex < 0) info.interpindex = numinterpbones++;
642  info.ragdollindex = i;
643  }
644  }
645  loopi(numbones)
646  {
647  boneinfo &info = bones[i];
648  if(info.interpindex < 0) continue;
649  for(int parent = info.parent; parent >= 0 && bones[parent].interpindex < 0; parent = bones[parent].parent)
650  bones[parent].interpindex = numinterpbones++;
651  }
652  loopi(numbones)
653  {
654  boneinfo &info = bones[i];
655  if(info.interpindex < 0) continue;
656  info.interpparent = info.parent >= 0 ? bones[info.parent].interpindex : -1;
657  }
658  if(ragdoll)
659  {
660  loopi(numbones)
661  {
662  boneinfo &info = bones[i];
663  if(info.interpindex < 0 || info.ragdollindex >= 0) continue;
664  for(int parent = info.parent; parent >= 0; parent = bones[parent].parent)
665  {
666  if(bones[parent].ragdollindex >= 0) { ragdoll->addreljoint(i, bones[parent].ragdollindex); break; }
667  }
668  }
669  }
670  calcantipodes();
671  }
672 
673 
674  void addpitchdep(int bone, int frame)
675  {
676  for(; bone >= 0; bone = bones[bone].parent)
677  {
678  int pos = pitchdeps.length();
679  loopvj(pitchdeps) if(bone <= pitchdeps[j].bone)
680  {
681  if(bone == pitchdeps[j].bone) goto nextbone;
682  pos = j;
683  break;
684  }
685  {
686  pitchdep d;
687  d.bone = bone;
688  d.parent = -1;
689  d.pose = framebones[frame*numbones + bone];
690  pitchdeps.insert(pos, d);
691  }
692  nextbone:;
693  }
694  }
695 
696  int findpitchdep(int bone)
697  {
698  loopv(pitchdeps) if(bone <= pitchdeps[i].bone) return bone == pitchdeps[i].bone ? i : -1;
699  return -1;
700  }
701 
702  int findpitchcorrect(int bone)
703  {
704  loopv(pitchcorrects) if(bone <= pitchcorrects[i].bone) return bone == pitchcorrects[i].bone ? i : -1;
705  return -1;
706  }
707 
709  {
710  pitchdeps.setsize(0);
711  if(pitchtargets.empty()) return;
713  {
714  pitchtarget &t = pitchtargets[i];
715  t.deps = -1;
716  addpitchdep(t.bone, t.frame);
717  }
719  {
720  pitchdep &d = pitchdeps[i];
721  int parent = bones[d.bone].parent;
722  if(parent >= 0)
723  {
724  int j = findpitchdep(parent);
725  if(j >= 0)
726  {
727  d.parent = j;
728  d.pose.mul(pitchdeps[j].pose, dualquat(d.pose));
729  }
730  }
731  }
733  {
734  pitchtarget &t = pitchtargets[i];
735  int j = findpitchdep(t.bone);
736  if(j >= 0)
737  {
738  t.deps = j;
739  t.pose = pitchdeps[j].pose;
740  }
741  t.corrects = -1;
742  for(int parent = t.bone; parent >= 0; parent = bones[parent].parent)
743  {
744  t.corrects = findpitchcorrect(parent);
745  if(t.corrects >= 0) break;
746  }
747  }
749  {
750  pitchcorrect &c = pitchcorrects[i];
751  bones[c.bone].correctindex = i;
752  c.parent = -1;
753  for(int parent = c.bone;;)
754  {
755  parent = bones[parent].parent;
756  if(parent < 0) break;
757  c.parent = findpitchcorrect(parent);
758  if(c.parent >= 0) break;
759  }
760  }
761  }
762 
763  void optimize()
764  {
765  cleanup();
766  if(ragdoll) ragdoll->setup();
767  remapbones();
768  initpitchdeps();
769  }
770 
771  void expandbonemask(uchar *expansion, int bone, int val)
772  {
773  expansion[bone] = val;
774  bone = bones[bone].children;
775  while(bone>=0) { expandbonemask(expansion, bone, val); bone = bones[bone].next; }
776  }
777 
778  void applybonemask(ushort *mask, uchar *partmask, int partindex)
779  {
780  if(!mask || *mask==BONEMASK_END) return;
781  uchar *expansion = new uchar[numbones];
782  memset(expansion, *mask&BONEMASK_NOT ? 1 : 0, numbones);
783  while(*mask!=BONEMASK_END)
784  {
785  expandbonemask(expansion, *mask&BONEMASK_BONE, *mask&BONEMASK_NOT ? 0 : 1);
786  mask++;
787  }
788  loopi(numbones) if(expansion[i]) partmask[i] = partindex;
789  delete[] expansion;
790  }
791 
793  {
794  loopi(numbones)
795  {
796  boneinfo &b = bones[i];
797  b.children = -1;
798  if(b.parent<0) b.next = -1;
799  else
800  {
801  b.next = bones[b.parent].children;
802  bones[b.parent].children = i;
803  }
804  }
805  }
806 
807  int availgpubones() const { return min(maxvsuniforms - reservevpparams - 10, maxskelanimdata) / 2; }
808  bool gpuaccelerate() const { return numframes && gpuskel && numgpubones<=availgpubones(); }
809 
810  float calcdeviation(const vec &axis, const vec &forward, const dualquat &pose1, const dualquat &pose2)
811  {
812  vec forward1 = pose1.transformnormal(forward).project(axis).normalize(),
813  forward2 = pose2.transformnormal(forward).project(axis).normalize(),
814  daxis = vec().cross(forward1, forward2);
815  float dx = clamp(forward1.dot(forward2), -1.0f, 1.0f), dy = clamp(daxis.magnitude(), -1.0f, 1.0f);
816  if(daxis.dot(axis) < 0) dy = -dy;
817  return atan2f(dy, dx)/RAD;
818  }
819 
820  void calcpitchcorrects(float pitch, const vec &axis, const vec &forward)
821  {
823  {
824  pitchtarget &t = pitchtargets[i];
825  t.deviated = calcdeviation(axis, forward, t.pose, pitchdeps[t.deps].pose);
826  }
828  {
829  pitchcorrect &c = pitchcorrects[i];
830  c.pitchangle = c.pitchtotal = 0;
831  }
833  {
834  pitchtarget &t = pitchtargets[j];
835  float tpitch = pitch - t.deviated;
836  for(int parent = t.corrects; parent >= 0; parent = pitchcorrects[parent].parent)
837  tpitch -= pitchcorrects[parent].pitchangle;
838  if(t.pitchmin || t.pitchmax) tpitch = clamp(tpitch, t.pitchmin, t.pitchmax);
840  {
841  pitchcorrect &c = pitchcorrects[i];
842  if(c.target != j) continue;
843  float total = c.parent >= 0 ? pitchcorrects[c.parent].pitchtotal : 0,
844  avail = tpitch - total,
845  used = tpitch*c.pitchscale;
846  if(c.pitchmin || c.pitchmax)
847  {
848  if(used < 0) used = clamp(c.pitchmin, used, 0.0f);
849  else used = clamp(c.pitchmax, 0.0f, used);
850  }
851  if(used < 0) used = clamp(avail, used, 0.0f);
852  else used = clamp(avail, 0.0f, used);
853  c.pitchangle = used;
854  c.pitchtotal = used + total;
855  }
856  }
857  }
858 
859  #define INTERPBONE(bone) \
860  const animstate &s = as[partmask[bone]]; \
861  const framedata &f = partframes[partmask[bone]]; \
862  dualquat d; \
863  (d = f.fr1[bone]).mul((1-s.cur.t)*s.interp); \
864  d.accumulate(f.fr2[bone], s.cur.t*s.interp); \
865  if(s.interp<1) \
866  { \
867  d.accumulate(f.pfr1[bone], (1-s.prev.t)*(1-s.interp)); \
868  d.accumulate(f.pfr2[bone], s.prev.t*(1-s.interp)); \
869  }
870 
871  void interpbones(const animstate *as, float pitch, const vec &axis, const vec &forward, int numanimparts, const uchar *partmask, skelcacheentry &sc)
872  {
873  if(!sc.bdata) sc.bdata = new dualquat[numinterpbones];
874  sc.nextversion();
875  struct framedata
876  {
877  const dualquat *fr1, *fr2, *pfr1, *pfr2;
878  } partframes[MAXANIMPARTS];
879  loopi(numanimparts)
880  {
881  partframes[i].fr1 = &framebones[as[i].cur.fr1*numbones];
882  partframes[i].fr2 = &framebones[as[i].cur.fr2*numbones];
883  if(as[i].interp<1)
884  {
885  partframes[i].pfr1 = &framebones[as[i].prev.fr1*numbones];
886  partframes[i].pfr2 = &framebones[as[i].prev.fr2*numbones];
887  }
888  }
890  {
891  pitchdep &p = pitchdeps[i];
892  INTERPBONE(p.bone);
893  d.normalize();
894  if(p.parent >= 0) p.pose.mul(pitchdeps[p.parent].pose, d);
895  else p.pose = d;
896  }
897  calcpitchcorrects(pitch, axis, forward);
898  loopi(numbones) if(bones[i].interpindex>=0)
899  {
900  INTERPBONE(i);
901  const boneinfo &b = bones[i];
902  d.normalize();
903  if(b.interpparent<0) sc.bdata[b.interpindex] = d;
904  else sc.bdata[b.interpindex].mul(sc.bdata[b.interpparent], d);
905  float angle;
906  if(b.pitchscale) { angle = b.pitchscale*pitch + b.pitchoffset; if(b.pitchmin || b.pitchmax) angle = clamp(angle, b.pitchmin, b.pitchmax); }
907  else if(b.correctindex >= 0) angle = pitchcorrects[b.correctindex].pitchangle;
908  else continue;
909  if(as->cur.anim&ANIM_NOPITCH || (as->interp < 1 && as->prev.anim&ANIM_NOPITCH))
910  angle *= (as->cur.anim&ANIM_NOPITCH ? 0 : as->interp) + (as->interp < 1 && as->prev.anim&ANIM_NOPITCH ? 0 : 1-as->interp);
911  sc.bdata[b.interpindex].mulorient(quat(axis, angle*RAD), b.base);
912  }
913  loopv(antipodes) sc.bdata[antipodes[i].child].fixantipodal(sc.bdata[antipodes[i].parent]);
914  }
915 
917  {
918  const dualquat *bdata = sc.bdata;
920  {
921  const ragdollskel::joint &j = ragdoll->joints[i];
922  const boneinfo &b = bones[j.bone];
923  const dualquat &q = bdata[b.interpindex];
924  loopk(3) if(j.vert[k] >= 0)
925  {
926  ragdollskel::vert &v = ragdoll->verts[j.vert[k]];
927  ragdolldata::vert &dv = d.verts[j.vert[k]];
928  dv.pos.add(q.transform(v.pos).mul(v.weight));
929  }
930  }
932  {
933  const ragdollskel::joint &j = ragdoll->joints[i];
934  const boneinfo &b = bones[j.bone];
935  const dualquat &q = bdata[b.interpindex];
936  d.calcanimjoint(i, matrix4x3(q));
937  }
939  {
940  ragdolldata::vert &dv = d.verts[i];
942  }
944  {
945  const ragdollskel::reljoint &r = ragdoll->reljoints[i];
946  const ragdollskel::joint &j = ragdoll->joints[r.parent];
947  const boneinfo &br = bones[r.bone], &bj = bones[j.bone];
948  d.reljoints[i].mul(dualquat(bdata[bj.interpindex]).invert(), bdata[br.interpindex]);
949  }
950  }
951 
953  {
954  if(!sc.bdata) sc.bdata = new dualquat[numinterpbones];
955  sc.nextversion();
957  {
958  const ragdollskel::joint &j = ragdoll->joints[i];
959  const boneinfo &b = bones[j.bone];
960  vec pos(0, 0, 0);
961  loopk(3) if(j.vert[k]>=0) pos.add(d.verts[j.vert[k]].pos);
962  pos.mul(j.weight/p->model->scale).sub(p->translate);
963  matrix4x3 m;
964  m.mul(d.tris[j.tri], pos, d.animjoints ? d.animjoints[i] : j.orient);
965  sc.bdata[b.interpindex] = dualquat(m);
966  }
968  {
969  const ragdollskel::reljoint &r = ragdoll->reljoints[i];
970  const ragdollskel::joint &j = ragdoll->joints[r.parent];
971  const boneinfo &br = bones[r.bone], &bj = bones[j.bone];
972  sc.bdata[br.interpindex].mul(sc.bdata[bj.interpindex], d.reljoints[i]);
973  }
974  loopv(antipodes) sc.bdata[antipodes[i].child].fixantipodal(sc.bdata[antipodes[i].parent]);
975  }
976 
977  void concattagtransform(part *p, int i, const matrix4x3 &m, matrix4x3 &n)
978  {
979  matrix4x3 t;
980  t.mul(bones[tags[i].bone].base, tags[i].matrix);
981  t.translate(vec(p->translate).mul(p->model->scale));
982  n.mul(m, t);
983  }
984 
985  void calctags(part *p, skelcacheentry *sc = nullptr)
986  {
987  loopv(p->links)
988  {
989  linkedpart &l = p->links[i];
990  tag &t = tags[l.tag];
991  dualquat q;
992  if(sc) q.mul(sc->bdata[bones[t.bone].interpindex], bones[t.bone].base);
993  else q = bones[t.bone].base;
994  matrix4x3 m;
995  m.mul(q, t.matrix);
996  m.d.add(p->translate).mul(p->model->scale);
997  l.matrix = m;
998  }
999  }
1000 
1001  void cleanup(bool full = true)
1002  {
1003  loopv(skelcache)
1004  {
1005  skelcacheentry &sc = skelcache[i];
1006  loopj(MAXANIMPARTS) sc.as[j].cur.fr1 = -1;
1007  DELETEA(sc.bdata);
1008  }
1009  skelcache.setsize(0);
1010  blendoffsets.clear();
1011  if(full) loopv(users) users[i]->cleanup();
1012  }
1013 
1014  bool canpreload() { return !numframes || gpuaccelerate(); }
1015 
1016  void preload()
1017  {
1018  if(!numframes) return;
1019  if(skelcache.empty())
1020  {
1022  }
1023  }
1024 
1025  skelcacheentry &checkskelcache(part *p, const animstate *as, float pitch, const vec &axis, const vec &forward, ragdolldata *rdata)
1026  {
1027  if(skelcache.empty())
1028  {
1030  }
1031 
1032  int numanimparts = ((skelpart *)as->owner)->numanimparts;
1033  uchar *partmask = ((skelpart *)as->owner)->partmask;
1034  skelcacheentry *sc = nullptr;
1035  bool match = false;
1036  loopv(skelcache)
1037  {
1038  skelcacheentry &c = skelcache[i];
1039  loopj(numanimparts) if(c.as[j]!=as[j]) goto mismatch;
1040  if(c.pitch != pitch || c.partmask != partmask || c.ragdoll != rdata || (rdata && c.millis < rdata->lastmove)) goto mismatch;
1041  match = true;
1042  sc = &c;
1043  break;
1044  mismatch:
1045  if(c.millis < lastmillis) { sc = &c; break; }
1046  }
1047  if(!sc) sc = &skelcache.add();
1048  if(!match)
1049  {
1050  loopi(numanimparts) sc->as[i] = as[i];
1051  sc->pitch = pitch;
1052  sc->partmask = partmask;
1053  sc->ragdoll = rdata;
1054  if(rdata) genragdollbones(*rdata, *sc, p);
1055  else interpbones(as, pitch, axis, forward, numanimparts, partmask, *sc);
1056  }
1057  sc->millis = lastmillis;
1058  return *sc;
1059  }
1060 
1062  {
1063  int &offset = blendoffsets.access(Shader::lastshader->program, -1);
1064  if(offset < 0)
1065  {
1066  defformatstring(offsetname, "%s[%d]", u.name, 2*numgpubones);
1067  offset = glGetUniformLocation_(Shader::lastshader->program, offsetname);
1068  }
1069  return offset;
1070  }
1071 
1073  {
1074  if(u.version == bc.version && u.data == bc.bdata) return;
1075  glUniform4fv_(u.loc, 2*numgpubones, sc.bdata[0].real.v);
1076  if(count > 0)
1077  {
1078  int offset = getblendoffset(u);
1079  if(offset >= 0) glUniform4fv_(offset, 2*count, bc.bdata[0].real.v);
1080  }
1081  u.version = bc.version;
1082  u.data = bc.bdata;
1083  }
1084 
1086  {
1087  if(!Shader::lastshader) return;
1088  if(Shader::lastshader->uniformlocs.length() < 1) return;
1090  setglslbones(u, sc, bc ? *bc : sc, count);
1091  }
1092 
1093  bool shouldcleanup() const
1094  {
1095  return numframes && (skelcache.empty() || gpuaccelerate()!=usegpuskel);
1096  }
1097  };
1098 
1100  {
1102 
1104  int numblends[4];
1105 
1106  static const int MAXBLENDCACHE = 16;
1108 
1109  static const int MAXVBOCACHE = 16;
1111 
1117 
1118  skelmeshgroup() : skel(nullptr), edata(nullptr), ebuf(0), vtangents(false), vlen(0), vertsize(0), vblends(0), vweights(0), vdata(nullptr)
1119  {
1120  memset(numblends, 0, sizeof(numblends));
1121  }
1122 
1123  ~skelmeshgroup() override
1124  {
1125  if(skel)
1126  {
1127  if(skel->shared) skel->users.removeobj(this);
1128  else DELETEP(skel);
1129  }
1130  if(ebuf) glDeleteBuffers_(1, &ebuf);
1132  {
1133  DELETEA(blendcache[i].bdata);
1134  }
1136  {
1137  if(vbocache[i].vbuf) glDeleteBuffers_(1, &vbocache[i].vbuf);
1138  }
1139  DELETEA(vdata);
1140  }
1141 
1142  void shareskeleton(char *name)
1143  {
1144  if(!name)
1145  {
1146  skel = new skeleton;
1147  skel->users.add(this);
1148  return;
1149  }
1150 
1151  static hashnameset<skeleton *> skeletons;
1152  if(skeletons.access(name)) skel = skeletons[name];
1153  else
1154  {
1155  skel = new skeleton;
1156  skel->name = newstring(name);
1157  skeletons.add(skel);
1158  }
1159  skel->users.add(this);
1160  skel->shared++;
1161  }
1162 
1163  int findtag(const char *name) override
1164  {
1165  return skel->findtag(name);
1166  }
1167 
1168  void *animkey() override { return skel; }
1169  int totalframes() const override { return max(skel->numframes, 1); }
1170 
1171  virtual skelanimspec *loadanim(const char *filename) { return nullptr; }
1172 
1173  void genvbo(bool tangents, vbocacheentry &vc)
1174  {
1175  if(!vc.vbuf) glGenBuffers_(1, &vc.vbuf);
1176  if(ebuf) return;
1177 
1178  vector<ushort> idxs;
1179 
1180  if(tangents) loopv(meshes) ((skelmesh *)meshes[i])->calctangents();
1181 
1182  vtangents = tangents;
1183  vlen = 0;
1184  vblends = 0;
1185  if(skel->numframes && !skel->usegpuskel)
1186  {
1187  vweights = 1;
1189  {
1190  blendcombo &c = blendcombos[i];
1191  c.interpindex = c.weights[1] ? skel->numgpubones + vblends++ : -1;
1192  }
1193 
1194  vertsize = tangents ? sizeof(vvertbump) : sizeof(vvertn);
1195  loopv(meshes) vlen += ((skelmesh *)meshes[i])->genvbo(idxs, vlen);
1196  DELETEA(vdata);
1197  vdata = new uchar[vlen*vertsize];
1198  #define FILLVDATA(type) do { \
1199  loopv(meshes) ((skelmesh *)meshes[i])->fillverts((type *)vdata); \
1200  } while(0)
1201  if(tangents) FILLVDATA(vvertbump);
1202  else FILLVDATA(vvertn);
1203  #undef FILLVDATA
1204  }
1205  else
1206  {
1207  if(skel->numframes)
1208  {
1209  vweights = 4;
1210  int availbones = skel->availgpubones() - skel->numgpubones;
1211  while(vweights > 1 && availbones >= numblends[vweights-1]) availbones -= numblends[--vweights];
1213  {
1214  blendcombo &c = blendcombos[i];
1215  c.interpindex = c.size() > vweights ? skel->numgpubones + vblends++ : -1;
1216  }
1217  }
1218  else
1219  {
1220  vweights = 0;
1221  loopv(blendcombos) blendcombos[i].interpindex = -1;
1222  }
1223 
1224  gle::bindvbo(vc.vbuf);
1225  #define GENVBO(type, args) do { \
1226  vertsize = sizeof(type); \
1227  vector<type> vverts; \
1228  loopv(meshes) vlen += ((skelmesh *)meshes[i])->genvbo args; \
1229  glBufferData_(GL_ARRAY_BUFFER, vverts.length()*sizeof(type), vverts.getbuf(), GL_STATIC_DRAW); \
1230  } while(0)
1231  #define GENVBOANIM(type) GENVBO(type, (idxs, vlen, vverts))
1232  #define GENVBOSTAT(type) GENVBO(type, (idxs, vlen, vverts, htdata, htlen))
1233  if(skel->numframes)
1234  {
1235  if(tangents) GENVBOANIM(vvertbumpw);
1236  else GENVBOANIM(vvertnw);
1237  }
1238  else
1239  {
1240  int numverts = 0, htlen = 128;
1241  loopv(meshes) numverts += ((skelmesh *)meshes[i])->numverts;
1242  while(htlen < numverts) htlen *= 2;
1243  if(numverts*4 > htlen*3) htlen *= 2;
1244  int *htdata = new int[htlen];
1245  memset(htdata, -1, htlen*sizeof(int));
1246  if(tangents) GENVBOSTAT(vvertbump);
1247  else GENVBOSTAT(vvertn);
1248  delete[] htdata;
1249  }
1250  #undef GENVBO
1251  #undef GENVBOANIM
1252  #undef GENVBOSTAT
1253  gle::clearvbo();
1254  }
1255 
1256  glGenBuffers_(1, &ebuf);
1257  gle::bindebo(ebuf);
1258  glBufferData_(GL_ELEMENT_ARRAY_BUFFER, idxs.length()*sizeof(ushort), idxs.getbuf(), GL_STATIC_DRAW);
1259  gle::clearebo();
1260  }
1261 
1262  void bindvbo(const animstate *as, vbocacheentry &vc, skelcacheentry *sc = nullptr, blendcacheentry *bc = nullptr)
1263  {
1264  vvert *vverts = nullptr;
1265  bindpos(ebuf, vc.vbuf, &vverts->pos, vertsize);
1266  if(as->cur.anim&ANIM_NOSKIN)
1267  {
1268  if(enabletc) disabletc();
1271  }
1272  else
1273  {
1274  if(vtangents)
1275  {
1277  vvertbump *vvertbumps = nullptr;
1278  bindtangents(&vvertbumps->tangent, vertsize);
1279  }
1280  else
1281  {
1283  vvertn *vvertns = nullptr;
1284  bindnormals(&vvertns->norm, vertsize);
1285  }
1286 
1287  bindtc(&vverts->tc, vertsize);
1288  }
1289  if(!sc || !skel->usegpuskel)
1290  {
1291  if(enablebones) disablebones();
1292  }
1293  else
1294  {
1295  if(vtangents)
1296  {
1297  vvertbumpw *vvertbumpws = nullptr;
1298  bindbones(vvertbumpws->weights, vvertbumpws->bones, vertsize);
1299  }
1300  else
1301  {
1302  vvertnw *vvertnws = nullptr;
1303  bindbones(vvertnws->weights, vvertnws->bones, vertsize);
1304  }
1305  }
1306  }
1307 
1308  void concattagtransform(part *p, int i, const matrix4x3 &m, matrix4x3 &n) override
1309  {
1310  skel->concattagtransform(p, i, m, n);
1311  }
1312 
1314  {
1315  loopv(blendcombos) if(blendcombos[i]==c)
1316  {
1317  blendcombos[i].uses += c.uses;
1318  return i;
1319  }
1320  numblends[c.size()-1]++;
1321  blendcombo &a = blendcombos.add(c);
1322  return a.interpindex = blendcombos.length()-1;
1323  }
1324 
1326  {
1328  int *remap = new int[blendcombos.length()];
1329  loopv(blendcombos) remap[blendcombos[i].interpindex] = i;
1330  loopv(meshes)
1331  {
1332  skelmesh *m = (skelmesh *)meshes[i];
1333  loopj(m->numverts)
1334  {
1335  vert &v = m->verts[j];
1336  v.blend = remap[v.blend];
1337  }
1338  }
1339  delete[] remap;
1340  }
1341 
1342  int remapblend(int blend)
1343  {
1344  const blendcombo &c = blendcombos[blend];
1345  return c.weights[1] ? c.interpindex : c.interpbones[0];
1346  }
1347 
1348  static inline void blendbones(dualquat &d, const dualquat *bdata, const blendcombo &c)
1349  {
1350  d = bdata[c.interpbones[0]];
1351  d.mul(c.weights[0]);
1352  d.accumulate(bdata[c.interpbones[1]], c.weights[1]);
1353  if(c.weights[2])
1354  {
1355  d.accumulate(bdata[c.interpbones[2]], c.weights[2]);
1356  if(c.weights[3]) d.accumulate(bdata[c.interpbones[3]], c.weights[3]);
1357  }
1358  }
1359 
1361  {
1362  bc.nextversion();
1363  if(!bc.bdata) bc.bdata = new dualquat[vblends];
1364  dualquat *dst = bc.bdata - skel->numgpubones;
1365  bool normalize = !skel->usegpuskel || vweights<=1;
1367  {
1368  const blendcombo &c = blendcombos[i];
1369  if(c.interpindex<0) break;
1370  dualquat &d = dst[c.interpindex];
1371  blendbones(d, sc.bdata, c);
1372  if(normalize) d.normalize();
1373  }
1374  }
1375 
1376  void cleanup() override
1377  {
1379  {
1380  blendcacheentry &c = blendcache[i];
1381  DELETEA(c.bdata);
1382  c.owner = -1;
1383  }
1385  {
1386  vbocacheentry &c = vbocache[i];
1387  if(c.vbuf) { glDeleteBuffers_(1, &c.vbuf); c.vbuf = 0; }
1388  c.owner = -1;
1389  }
1390  if(ebuf) { glDeleteBuffers_(1, &ebuf); ebuf = 0; }
1391  if(skel) skel->cleanup(false);
1392  }
1393 
1394  #define SEARCHCACHE(cachesize, cacheentry, cache, reusecheck) \
1395  loopi(cachesize) \
1396  { \
1397  cacheentry &c = cache[i]; \
1398  if(c.owner==owner) \
1399  { \
1400  if(c==sc) return c; \
1401  else c.owner = -1; \
1402  break; \
1403  } \
1404  } \
1405  loopi(cachesize-1) \
1406  { \
1407  cacheentry &c = cache[i]; \
1408  if(reusecheck c.owner < 0 || c.millis < lastmillis) \
1409  return c; \
1410  } \
1411  return cache[cachesize-1];
1412 
1414  {
1416  }
1417 
1419  {
1421  }
1422 
1423  void preload(part *p) override
1424  {
1425  if(!skel->canpreload()) return;
1426  bool tangents = false;
1427  loopv(p->skins) if(p->skins[i].tangents()) tangents = true;
1428  if(skel->shouldcleanup()) skel->cleanup();
1429  else if(tangents!=vtangents) cleanup();
1430  skel->preload();
1431  if(!vbocache->vbuf) genvbo(tangents, *vbocache);
1432  }
1433 
1434  void render(const animstate *as, float pitch, const vec &axis, const vec &forward, dynent *d, part *p) override
1435  {
1436  bool tangents = false;
1437  loopv(p->skins) if(p->skins[i].tangents()) tangents = true;
1438  if(skel->shouldcleanup()) { skel->cleanup(); disablevbo(); }
1439  else if(tangents!=vtangents) { cleanup(); disablevbo(); }
1440 
1441  if(!skel->numframes)
1442  {
1443  if(!(as->cur.anim&ANIM_NORENDER))
1444  {
1445  if(!vbocache->vbuf) genvbo(tangents, *vbocache);
1446  bindvbo(as, *vbocache);
1447  loopv(meshes)
1448  {
1449  skelmesh *m = (skelmesh *)meshes[i];
1450  p->skins[i].bind(m, as);
1451  m->render(as, p->skins[i], *vbocache);
1452  }
1453  }
1454  skel->calctags(p);
1455  return;
1456  }
1457 
1458  skelcacheentry &sc = skel->checkskelcache(p, as, pitch, axis, forward, as->cur.anim&ANIM_RAGDOLL || !d || !d->ragdoll || d->ragdoll->skel != skel->ragdoll ? nullptr : d->ragdoll);
1459  if(!(as->cur.anim&ANIM_NORENDER))
1460  {
1461  int owner = &sc-&skel->skelcache[0];
1462  vbocacheentry &vc = skel->usegpuskel ? *vbocache : checkvbocache(sc, owner);
1463  vc.millis = lastmillis;
1464  if(!vc.vbuf) genvbo(tangents, vc);
1465  blendcacheentry *bc = nullptr;
1466  if(vblends)
1467  {
1468  bc = &checkblendcache(sc, owner);
1469  bc->millis = lastmillis;
1470  if(bc->owner!=owner)
1471  {
1472  bc->owner = owner;
1473  *(animcacheentry *)bc = sc;
1474  blendbones(sc, *bc);
1475  }
1476  }
1477  if(!skel->usegpuskel && vc.owner!=owner)
1478  {
1479  vc.owner = owner;
1480  (animcacheentry &)vc = sc;
1481  loopv(meshes)
1482  {
1483  skelmesh &m = *(skelmesh *)meshes[i];
1484  m.interpverts(sc.bdata, bc ? bc->bdata : nullptr, tangents, vdata + m.voffset*vertsize, p->skins[i]);
1485  }
1486  gle::bindvbo(vc.vbuf);
1487  glBufferData_(GL_ARRAY_BUFFER, vlen*vertsize, vdata, GL_STREAM_DRAW);
1488  }
1489 
1490  bindvbo(as, vc, &sc, bc);
1491  loopv(meshes)
1492  {
1493  skelmesh *m = (skelmesh *)meshes[i];
1494  p->skins[i].bind(m, as);
1495  if(skel->usegpuskel) skel->setgpubones(sc, bc, vblends);
1496  m->render(as, p->skins[i], vc);
1497  }
1498  }
1499 
1500  skel->calctags(p, &sc);
1501 
1502  if(as->cur.anim&ANIM_RAGDOLL && skel->ragdoll && !d->ragdoll)
1503  {
1504  d->ragdoll = new ragdolldata(skel->ragdoll, p->model->scale);
1505  skel->initragdoll(*d->ragdoll, sc, p);
1506  d->ragdoll->init(d);
1507  }
1508  }
1509  };
1510 
1512  {
1516  };
1517 
1518  struct skelpart : part
1519  {
1521 
1523 
1524  skelpart(animmodel *model, int index = 0) : part(model, index), buildingpartmask(nullptr), partmask(nullptr)
1525  {
1526  }
1527 
1528  ~skelpart() override
1529  {
1531  }
1532 
1534  {
1535  static animpartmask *partmasks = nullptr;
1536  animpartmask *p = partmasks;
1537  for(; p; p = p->next) if(p->numbones==o->numbones && !memcmp(p->bones, o->bones, p->numbones))
1538  {
1539  delete[] (uchar *)o;
1540  return p->bones;
1541  }
1542 
1543  o->next = p;
1544  partmasks = o;
1545  return o->bones;
1546  }
1547 
1549  {
1550  animpartmask *p = (animpartmask *)new uchar[sizeof(animpartmask) + ((skelmeshgroup *)meshes)->skel->numbones-1];
1551  p->numbones = ((skelmeshgroup *)meshes)->skel->numbones;
1552  memset(p->bones, 0, p->numbones);
1553  return p;
1554  }
1555 
1557  {
1560  }
1561 
1562  bool addanimpart(ushort *bonemask)
1563  {
1564  if(!buildingpartmask || numanimparts>=MAXANIMPARTS) return false;
1565  ((skelmeshgroup *)meshes)->skel->applybonemask(bonemask, buildingpartmask->bones, numanimparts);
1566  numanimparts++;
1567  return true;
1568  }
1569 
1571  {
1572  if(buildingpartmask)
1573  {
1575  buildingpartmask = nullptr;
1576  }
1577 
1578  ((skelmeshgroup *)meshes)->skel->optimize();
1579  }
1580 
1581  void loaded() override
1582  {
1583  endanimparts();
1584  part::loaded();
1585  }
1586  };
1587 
1588  skelmodel(const char *name) : animmodel(name)
1589  {
1590  }
1591 
1592  int linktype(animmodel *m) const override
1593  {
1594  return type()==m->type() &&
1595  ((skelmeshgroup *)parts[0]->meshes)->skel == ((skelmeshgroup *)m->parts[0]->meshes)->skel ?
1596  LINK_REUSE :
1597  LINK_TAG;
1598  }
1599 
1600  bool skeletal() const override { return true; }
1601 
1603  {
1604  skelpart *p = new skelpart(this, parts.length());
1605  parts.add(p);
1606  return *p;
1607  }
1608 };
1609 
1611 {
1612  float yaw, pitch, roll;
1614 
1615  skeladjustment(float yaw, float pitch, float roll, const vec &translate) : yaw(yaw), pitch(pitch), roll(roll), translate(translate) {}
1616 
1617  void adjust(dualquat &dq)
1618  {
1619  if(yaw) dq.mulorient(quat(vec(0, 0, 1), yaw*RAD));
1620  if(pitch) dq.mulorient(quat(vec(0, -1, 0), pitch*RAD));
1621  if(roll) dq.mulorient(quat(vec(-1, 0, 0), roll*RAD));
1622  if(!translate.iszero()) dq.translate(translate);
1623  }
1624 };
1625 
1626 template<class MDL> struct skelloader : modelloader<MDL>
1627 {
1629 };
1630 
1632 
1633 template<class MDL> struct skelcommands : modelcommands<MDL, struct MDL::skelmesh>
1634 {
1636  typedef struct MDL::skeleton skeleton;
1637  typedef struct MDL::skelmeshgroup meshgroup;
1638  typedef struct MDL::skelpart part;
1639  typedef struct MDL::skin skin;
1640  typedef struct MDL::boneinfo boneinfo;
1641  typedef struct MDL::skelanimspec animspec;
1642  typedef struct MDL::pitchdep pitchdep;
1643  typedef struct MDL::pitchtarget pitchtarget;
1644  typedef struct MDL::pitchcorrect pitchcorrect;
1645 
1646  static void loadpart(char *meshfile, char *skelname, float *smooth)
1647  {
1648  if(!MDL::loading) { Log.std->error("not loading an {0}", MDL::formatname()); return; }
1649  defformatstring(filename, "%s/%s", MDL::dir, meshfile);
1650  part &mdl = MDL::loading->addpart();
1651  mdl.pitchscale = mdl.pitchoffset = mdl.pitchmin = mdl.pitchmax = 0;
1652  MDL::adjustments.setsize(0);
1653  mdl.meshes = MDL::loading->sharemeshes(path(filename), skelname[0] ? skelname : nullptr, double(*smooth > 0 ? cos(clamp(*smooth, 0.0f, 180.0f)*RAD) : 2));
1654  if(!mdl.meshes) Log.std->error("could not load {0}", filename);
1655  else
1656  {
1657  mdl.initanimparts();
1658  mdl.initskins();
1659  }
1660  }
1661 
1662  static void settag(char *name, char *tagname, float *tx, float *ty, float *tz, float *rx, float *ry, float *rz)
1663  {
1664  if(!MDL::loading || MDL::loading->parts.empty()) { Log.std->error("not loading an {0}", MDL::formatname()); return; }
1665  part &mdl = *(part *)MDL::loading->parts.last();
1666  int i = mdl.meshes ? ((meshgroup *)mdl.meshes)->skel->findbone(name) : -1;
1667  if(i >= 0)
1668  {
1669  float cx = *rx ? cosf(*rx/2*RAD) : 1, sx = *rx ? sinf(*rx/2*RAD) : 0,
1670  cy = *ry ? cosf(*ry/2*RAD) : 1, sy = *ry ? sinf(*ry/2*RAD) : 0,
1671  cz = *rz ? cosf(*rz/2*RAD) : 1, sz = *rz ? sinf(*rz/2*RAD) : 0;
1672  matrix4x3 m(matrix3(quat(sx*cy*cz - cx*sy*sz, cx*sy*cz + sx*cy*sz, cx*cy*sz - sx*sy*cz, cx*cy*cz + sx*sy*sz)),
1673  vec(*tx, *ty, *tz));
1674  ((meshgroup *)mdl.meshes)->skel->addtag(tagname, i, m);
1675  return;
1676  }
1677  Log.std->error("could not find bone {0} for tag {1}", name, tagname);
1678  }
1679 
1680  static void setpitch(char *name, float *pitchscale, float *pitchoffset, float *pitchmin, float *pitchmax)
1681  {
1682  if(!MDL::loading || MDL::loading->parts.empty()) { Log.std->error("not loading an {0}", MDL::formatname()); return; }
1683  part &mdl = *(part *)MDL::loading->parts.last();
1684 
1685  if(name[0])
1686  {
1687  int i = mdl.meshes ? ((meshgroup *)mdl.meshes)->skel->findbone(name) : -1;
1688  if(i>=0)
1689  {
1690  boneinfo &b = ((meshgroup *)mdl.meshes)->skel->bones[i];
1691  b.pitchscale = *pitchscale;
1692  b.pitchoffset = *pitchoffset;
1693  if(*pitchmin || *pitchmax)
1694  {
1695  b.pitchmin = *pitchmin;
1696  b.pitchmax = *pitchmax;
1697  }
1698  else
1699  {
1700  b.pitchmin = -360*fabs(b.pitchscale) + b.pitchoffset;
1701  b.pitchmax = 360*fabs(b.pitchscale) + b.pitchoffset;
1702  }
1703  return;
1704  }
1705  Log.std->error("could not find bone {0} to pitch", name);
1706  return;
1707  }
1708 
1709  mdl.pitchscale = *pitchscale;
1710  mdl.pitchoffset = *pitchoffset;
1711  if(*pitchmin || *pitchmax)
1712  {
1713  mdl.pitchmin = *pitchmin;
1714  mdl.pitchmax = *pitchmax;
1715  }
1716  else
1717  {
1718  mdl.pitchmin = -360*fabs(mdl.pitchscale) + mdl.pitchoffset;
1719  mdl.pitchmax = 360*fabs(mdl.pitchscale) + mdl.pitchoffset;
1720  }
1721  }
1722 
1723  static void setpitchtarget(char *name, char *animfile, int *frameoffset, float *pitchmin, float *pitchmax)
1724  {
1725  if(!MDL::loading || MDL::loading->parts.empty()) { Log.std->error("not loading an {0}", MDL::formatname()); return; }
1726  part &mdl = *(part *)MDL::loading->parts.last();
1727  if(!mdl.meshes) return;
1728  defformatstring(filename, "%s/%s", MDL::dir, animfile);
1729  animspec *sa = ((meshgroup *)mdl.meshes)->loadanim(path(filename));
1730  if(!sa) { Log.std->error("could not load {0} anim file {1}", MDL::formatname(), filename); return; }
1731  skeleton *skel = ((meshgroup *)mdl.meshes)->skel;
1732  int bone = skel ? skel->findbone(name) : -1;
1733  if(bone < 0)
1734  {
1735  Log.std->error("could not find bone {0} to pitch target", name);
1736  return;
1737  }
1738  loopv(skel->pitchtargets) if(skel->pitchtargets[i].bone == bone) return;
1739  pitchtarget &t = skel->pitchtargets.add();
1740  t.bone = bone;
1741  t.frame = sa->frame + clamp(*frameoffset, 0, sa->range-1);
1742  t.pitchmin = *pitchmin;
1743  t.pitchmax = *pitchmax;
1744  }
1745 
1746  static void setpitchcorrect(char *name, char *targetname, float *scale, float *pitchmin, float *pitchmax)
1747  {
1748  if(!MDL::loading || MDL::loading->parts.empty()) { Log.std->error("not loading an {0}", MDL::formatname()); return; }
1749  part &mdl = *(part *)MDL::loading->parts.last();
1750  if(!mdl.meshes) return;
1751  skeleton *skel = ((meshgroup *)mdl.meshes)->skel;
1752  int bone = skel ? skel->findbone(name) : -1;
1753  if(bone < 0)
1754  {
1755  Log.std->error("could not find bone {0} to pitch correct", name);
1756  return;
1757  }
1758  if(skel->findpitchcorrect(bone) >= 0) return;
1759  int targetbone = skel->findbone(targetname), target = -1;
1760  if(targetbone >= 0) loopv(skel->pitchtargets) if(skel->pitchtargets[i].bone == targetbone) { target = i; break; }
1761  if(target < 0)
1762  {
1763  Log.std->error("could not find pitch target {0} to pitch correct {1}", targetname, name);
1764  return;
1765  }
1766  pitchcorrect c;
1767  c.bone = bone;
1768  c.target = target;
1769  c.pitchmin = *pitchmin;
1770  c.pitchmax = *pitchmax;
1771  c.pitchscale = *scale;
1772  int pos = skel->pitchcorrects.length();
1773  loopv(skel->pitchcorrects) if(bone <= skel->pitchcorrects[i].bone) { pos = i; break; break; }
1774  skel->pitchcorrects.insert(pos, c);
1775  }
1776 
1777  static void setanim(char *anim, char *animfile, float *speed, int *priority, int *startoffset, int *endoffset)
1778  {
1779  if(!MDL::loading || MDL::loading->parts.empty()) { Log.std->error("not loading an {0}", MDL::formatname()); return; }
1780 
1781  vector<int> anims;
1782  findanims(anim, anims);
1783  if(anims.empty()) Log.std->error("could not find animation {0}", anim);
1784  else
1785  {
1786  part *p = (part *)MDL::loading->parts.last();
1787  if(!p->meshes) return;
1788  defformatstring(filename, "%s/%s", MDL::dir, animfile);
1789  animspec *sa = ((meshgroup *)p->meshes)->loadanim(path(filename));
1790  if(!sa) Log.std->error("could not load {0} anim file {1}", MDL::formatname(), filename);
1791  else loopv(anims)
1792  {
1793  int start = sa->frame, end = sa->range;
1794  if(*startoffset > 0) start += min(*startoffset, end-1);
1795  else if(*startoffset < 0) start += max(end + *startoffset, 0);
1796  end -= start - sa->frame;
1797  if(*endoffset > 0) end = min(end, *endoffset);
1798  else if(*endoffset < 0) end = max(end + *endoffset, 1);
1799  MDL::loading->parts.last()->setanim(p->numanimparts-1, anims[i], start, end, *speed, *priority);
1800  }
1801  }
1802  }
1803 
1804  static void setanimpart(char *maskstr)
1805  {
1806  if(!MDL::loading || MDL::loading->parts.empty()) { Log.std->error("not loading an {0}", MDL::formatname()); return; }
1807 
1808  part *p = (part *)MDL::loading->parts.last();
1809 
1810  vector<char *> bonestrs;
1811  explodelist(maskstr, bonestrs);
1812  vector<ushort> bonemask;
1813  loopv(bonestrs)
1814  {
1815  char *bonestr = bonestrs[i];
1816  int bone = p->meshes ? ((meshgroup *)p->meshes)->skel->findbone(bonestr[0]=='!' ? bonestr+1 : bonestr) : -1;
1817  if(bone<0) { Log.std->error("could not find bone {0} for anim part mask [{1}]", bonestr, maskstr); bonestrs.deletearrays(); return; }
1818  bonemask.add(bone | (bonestr[0]=='!' ? BONEMASK_NOT : 0));
1819  }
1820  bonestrs.deletearrays();
1821  bonemask.sort();
1822  if(bonemask.length()) bonemask.add(BONEMASK_END);
1823 
1824  if(!p->addanimpart(bonemask.getbuf())) Log.std->error("too many animation parts");
1825  }
1826 
1827  static void setadjust(char *name, float *yaw, float *pitch, float *roll, float *tx, float *ty, float *tz)
1828  {
1829  if(!MDL::loading || MDL::loading->parts.empty()) { Log.std->error("not loading an {0}", MDL::formatname()); return; }
1830  part &mdl = *(part *)MDL::loading->parts.last();
1831 
1832  if(!name[0]) return;
1833  int i = mdl.meshes ? ((meshgroup *)mdl.meshes)->skel->findbone(name) : -1;
1834  if(i < 0) { Log.std->error("could not find bone {0} to adjust", name); return; }
1835  while(!MDL::adjustments.inrange(i)) MDL::adjustments.add(skeladjustment(0, 0, 0, vec(0, 0, 0)));
1836  MDL::adjustments[i] = skeladjustment(*yaw, *pitch, *roll, vec(*tx/4, *ty/4, *tz/4));
1837  }
1838 
1840  {
1841  if(MDL::multiparted()) this->modelcommand(loadpart, "load", "ssf");
1842  this->modelcommand(settag, "tag", "ssffffff");
1843  this->modelcommand(setpitch, "pitch", "sffff");
1844  this->modelcommand(setpitchtarget, "pitchtarget", "ssiff");
1845  this->modelcommand(setpitchcorrect, "pitchcorrect", "ssfff");
1846  if(MDL::animated())
1847  {
1848  this->modelcommand(setanim, "anim", "ssfiii");
1849  this->modelcommand(setanimpart, "animpart", "s");
1850  this->modelcommand(setadjust, "adjust", "sffffff");
1851  }
1852  }
1853 };
1854 
Definition: skelmodel.hpp:21
~boneinfo()
Definition: skelmodel.hpp:431
void smoothnorms(float limit=0, bool areaweight=true)
Definition: skelmodel.hpp:197
int linktype(animmodel *m) const override
Definition: skelmodel.hpp:1592
Definition: geom.hpp:902
bool empty() const
is this vector empty?
Definition: cube_vector.hpp:141
float pitchscale
Definition: skelmodel.hpp:427
int getblendoffset(UniformLoc &u)
Definition: skelmodel.hpp:1061
ushort vert[3]
Definition: skelmodel.hpp:21
vert * verts
Definition: ragdoll.hpp:151
#define RAD
Definition: cube_types.hpp:32
int vert[3]
Definition: ragdoll.hpp:55
int genvbo(vector< ushort > &idxs, int offset)
Definition: skelmodel.hpp:314
float calcdeviation(const vec &axis, const vec &forward, const dualquat &pose1, const dualquat &pose2)
Definition: skelmodel.hpp:810
void applybonemask(ushort *mask, uchar *partmask, int partindex)
Definition: skelmodel.hpp:778
Definition: ragdoll.hpp:60
const char * name
Definition: shader.hpp:129
vector< tag > tags
Definition: skelmodel.hpp:474
Definition: skelmodel.hpp:437
void endanimparts()
Definition: skelmodel.hpp:1570
Definition: skelmodel.hpp:16
Definition: shader.hpp:127
~skelpart() override
Definition: skelmodel.hpp:1528
const T & max(const inexor::rpc::SharedVar< T > &a, const T &b)
Definition: SharedVar.hpp:224
char * name
Definition: model.hpp:42
void render(const animstate *as, float pitch, const vec &axis, const vec &forward, dynent *d, part *p) override
Definition: skelmodel.hpp:1434
virtual skelanimspec * loadanim(const char *filename)
Definition: skelmodel.hpp:1171
int interpindex
Definition: skelmodel.hpp:13
Definition: ragdoll.hpp:15
void bindtc(void *v, int stride)
Definition: animmodel.hpp:522
void concattagtransform(part *p, int i, const matrix4x3 &m, matrix4x3 &n) override
Definition: skelmodel.hpp:1308
#define loopvj(v)
Definition: cube_loops.hpp:22
skelpart(animmodel *model, int index=0)
Definition: skelmodel.hpp:1524
int numbones
Definition: skelmodel.hpp:1514
int addblendcombo(const blendcombo &c)
Definition: skelmodel.hpp:191
void fillverts(T *vdata)
Definition: skelmodel.hpp:338
static void assignvert(vvertn &vv, int j, vert &v, blendcombo &c)
Definition: skelmodel.hpp:237
float pitchtotal
Definition: skelmodel.hpp:460
int numframes
Definition: skelmodel.hpp:471
static void clearvbo()
Definition: glemu.hpp:103
int child
Definition: skelmodel.hpp:439
squat tangent
Definition: skelmodel.hpp:16
float pitchmax
Definition: skelmodel.hpp:427
#define GENVBOSTAT(type)
boneinfo()
Definition: skelmodel.hpp:430
int genvbo(vector< ushort > &idxs, int offset, vector< T > &vverts, int *htdata, int htlen)
Definition: skelmodel.hpp:285
Definition: bih.hpp:39
static void fillvert(T &vv, int j, vert &v)
Definition: skelmodel.hpp:332
Definition: animmodel.hpp:465
Definition: geom.hpp:1897
struct MDL::pitchcorrect pitchcorrect
Definition: skelmodel.hpp:1644
vec norm
Definition: skelmodel.hpp:15
float pitchoffset
Definition: skelmodel.hpp:427
float pitch
Definition: skelmodel.hpp:121
bool gpuaccelerate() const
Definition: skelmodel.hpp:808
skelanimspec * findskelanim(const char *name, char sep= '\0')
Definition: skelmodel.hpp:501
bool addanimpart(ushort *bonemask)
Definition: skelmodel.hpp:1562
dualquat pose
Definition: skelmodel.hpp:454
void clear()
Definition: cube_hash.hpp:193
Definition: animmodel.hpp:598
animpos cur
Definition: animmodel.hpp:58
vector< reljoint > reljoints
Definition: ragdoll.hpp:73
part * owner
Definition: animmodel.hpp:57
Definition: skelmodel.hpp:20
void calcpitchcorrects(float pitch, const vec &axis, const vec &forward)
Definition: skelmodel.hpp:820
static Logger std
Logger for everything not fitting elsewhere.
Definition: Logging.hpp:89
~skelmeshgroup() override
Definition: skelmodel.hpp:1123
const tri * tris
Definition: bih.hpp:65
uchar * partmask
Definition: skelmodel.hpp:123
Definition: skelmodel.hpp:18
void smoothnorms(V *verts, int numverts, T *tris, int numtris, float limit, bool areaweight)
Definition: animmodel.hpp:312
void smooth()
Definition: octaedit.cpp:1942
vec & project(const vec &n)
vector projection
Definition: geom.hpp:223
void sortblendcombos()
Definition: skelmodel.hpp:1325
static void disablebones()
Definition: animmodel.hpp:1324
boneinfo * bones
Definition: skelmodel.hpp:470
static void setpitchcorrect(char *name, char *targetname, float *scale, float *pitchmin, float *pitchmax)
Definition: skelmodel.hpp:1746
animpartmask * buildingpartmask
Definition: skelmodel.hpp:1520
dualquat * reljoints
Definition: ragdoll.hpp:154
Definition: model.hpp:40
float weight
Definition: ragdoll.hpp:56
void genBIH(BIH::mesh &m) override
Definition: skelmodel.hpp:227
bool vtangents
Definition: skelmodel.hpp:1114
int numblends[4]
Definition: skelmodel.hpp:1104
vec & mul(const vec &o)
scalar multiplication
Definition: geom.hpp:168
meshgroup * meshes
Definition: animmodel.hpp:613
Definition: animmodel.hpp:104
dualquat * framebones
Definition: skelmodel.hpp:472
void optimize()
Definition: skelmodel.hpp:763
int tcstride
Definition: bih.hpp:69
Definition: skelmodel.hpp:23
vector< skin > skins
Definition: animmodel.hpp:615
char * name
Definition: skelmodel.hpp:413
void bindvbo(const animstate *as, vbocacheentry &vc, skelcacheentry *sc=nullptr, blendcacheentry *bc=nullptr)
Definition: skelmodel.hpp:1262
vec pos
Definition: ragdoll.hpp:140
int addweight(int sorted, float weight, int bone)
Definition: skelmodel.hpp:61
struct MDL::skelpart part
Definition: skelmodel.hpp:1638
void findanims(const char *pattern, vector< int > &anims)
Definition: rendermodel.cpp:1066
ushort maxvert
Definition: skelmodel.hpp:178
static void bindvbo(GLuint vbo)
Definition: glemu.hpp:102
int owner
Definition: skelmodel.hpp:141
int availgpubones() const
Definition: skelmodel.hpp:807
T * getbuf()
get the whole vector
Definition: cube_vector.hpp:169
2-dimensional float vectors
Definition: geom.hpp:38
void serialize(vvertw &v)
Definition: skelmodel.hpp:91
void blendbones(const skelcacheentry &sc, blendcacheentry &bc)
Definition: skelmodel.hpp:1360
int vweights
Definition: skelmodel.hpp:1115
uchar * sharepartmask(animpartmask *o)
Definition: skelmodel.hpp:1533
vec2 tc
Definition: skelmodel.hpp:13
antipode(int parent, int child)
Definition: skelmodel.hpp:441
ragdolldata * ragdoll
Definition: skelmodel.hpp:124
GLuint ebuf
Definition: skelmodel.hpp:1113
#define ANIM_NOPITCH
Definition: ents.hpp:226
int numbones
Definition: skelmodel.hpp:471
float weight
Definition: ragdoll.hpp:20
uchar interpbones[4]
Definition: skelmodel.hpp:27
vector< skelcacheentry > skelcache
Definition: skelmodel.hpp:482
int posstride
Definition: bih.hpp:69
int numanimparts
Definition: animmodel.hpp:617
void init(dynent *d)
Definition: ragdoll.hpp:210
vec transform(const vec &v) const
Definition: geom.hpp:637
static bool sortcmp(const blendcombo &x, const blendcombo &y)
Definition: skelmodel.hpp:47
partrenderer * parts[24]
Definition: renderparticles.cpp:735
PFNGLDELETEBUFFERSPROC glDeleteBuffers_
Definition: rendergl.cpp:128
#define BONEMASK_END
Definition: skelmodel.hpp:8
char * name
Definition: animmodel.hpp:469
int owner
Definition: skelmodel.hpp:163
int corrects
Definition: skelmodel.hpp:452
int numverts
Definition: explosion.cpp:35
void bindtangents(void *v, int stride)
Definition: animmodel.hpp:550
int uses
Definition: skelmodel.hpp:25
float pitchmin
Definition: skelmodel.hpp:460
skeladjustment(float yaw, float pitch, float roll, const vec &translate)
Definition: skelmodel.hpp:1615
~tag()
Definition: skelmodel.hpp:408
blendcacheentry & checkblendcache(skelcacheentry &sc, int owner)
Definition: skelmodel.hpp:1418
Definition: skelmodel.hpp:450
float scale
Definition: model.hpp:45
Definition: skelmodel.hpp:411
#define BONEMASK_NOT
Skeletal Models Header: Header for model file formats with embedded skeletons.
Definition: skelmodel.hpp:7
tag()
Definition: skelmodel.hpp:407
#define IPLOOP(type, dosetup, dotransform)
int findpitchcorrect(int bone)
Definition: skelmodel.hpp:702
~skelmesh() override
Definition: skelmodel.hpp:184
int vertsize
Definition: skelmodel.hpp:1115
GLint GLint GLint GLint GLint GLint GLint GLbitfield mask
Definition: glexts.hpp:299
void setup()
Definition: ragdoll.hpp:120
int parent
Definition: skelmodel.hpp:426
float roll
Definition: skelmodel.hpp:1612
bool canpreload()
Definition: skelmodel.hpp:1014
Definition: skelmodel.hpp:19
int numgpubones
Definition: skelmodel.hpp:471
void interpbones(const animstate *as, float pitch, const vec &axis, const vec &forward, int numanimparts, const uchar *partmask, skelcacheentry &sc)
Definition: skelmodel.hpp:871
Definition: geom.hpp:668
Definition: animmodel.hpp:55
void cleanup(bool full=true)
Definition: skelmodel.hpp:1001
void setvariant(int col, int row, Shader *fallbackshader)
Definition: shader.hpp:243
int blend
Definition: skelmodel.hpp:13
int parent
Definition: skelmodel.hpp:446
Definition: skelmodel.hpp:1518
int deps
Definition: skelmodel.hpp:452
static void clearebo()
Definition: glemu.hpp:101
int bone
Definition: skelmodel.hpp:404
float pitchscale
Definition: skelmodel.hpp:460
int loc
Definition: shader.hpp:130
Definition: ragdoll.hpp:17
static void setanim(char *anim, char *animfile, float *speed, int *priority, int *startoffset, int *endoffset)
Definition: skelmodel.hpp:1777
float pitchmax
Definition: skelmodel.hpp:453
int bone
Definition: ragdoll.hpp:62
animstate as[MAXANIMPARTS]
Definition: skelmodel.hpp:120
Definition: skelmodel.hpp:14
tri * tris
Definition: skelmodel.hpp:174
int xtravertsva
Definition: rendergl.cpp:1943
int interpindex
Definition: skelmodel.hpp:25
vec transform(const vec &o) const
Definition: geom.hpp:1100
Definition: skelmodel.hpp:161
Definition: geom.hpp:536
uchar * vdata
Definition: skelmodel.hpp:1116
ICOMMAND * f(float *a, float *b), floatret(*a **b)
int findtag(const char *name) override
Definition: skelmodel.hpp:1163
void bindpos(GLuint ebuf, GLuint vbuf, void *v, int stride)
Definition: animmodel.hpp:506
skeleton()
Definition: skelmodel.hpp:485
int bone
Definition: skelmodel.hpp:452
vector< vert > verts
Definition: ragdoll.hpp:67
bool operator==(const animcacheentry &c) const
Definition: skelmodel.hpp:131
matrix4 matrix
Definition: animmodel.hpp:604
skelanimspec & addskelanim(const char *name)
Definition: skelmodel.hpp:519
dualquat & invert()
Definition: geom.hpp:570
static void disablenormals()
Definition: animmodel.hpp:1343
typedef GLuint(APIENTRYP PFNGLGETUNIFORMBLOCKINDEXPROC)(GLuint program
unsigned short ushort
Definition: cube_types.hpp:8
Definition: animmodel.hpp:1394
void preload()
Definition: skelmodel.hpp:1016
void buildnorms(V *verts, int numverts, T *tris, int numtris, bool areaweight)
Definition: animmodel.hpp:360
ragdollskel * skel
Definition: ragdoll.hpp:147
void finalize(int sorted)
Definition: skelmodel.hpp:81
float pitchmin
Definition: skelmodel.hpp:427
float pitchmax
Definition: skelmodel.hpp:460
Definition: ragdoll.hpp:136
vector< skelanimspec > skelanims
Definition: skelmodel.hpp:473
skeleton * skel
Definition: skelmodel.hpp:1101
else loopi(numargs)
Definition: command.cpp:3019
int group
Definition: skelmodel.hpp:426
PFNGLGENBUFFERSPROC glGenBuffers_
Definition: rendergl.cpp:122
animpos prev
Definition: animmodel.hpp:58
skelpart & addpart()
Definition: skelmodel.hpp:1602
void genragdollbones(ragdolldata &d, skelcacheentry &sc, part *p)
Definition: skelmodel.hpp:952
ushort minvert
Definition: skelmodel.hpp:178
vector< blendcombo > blendcombos
Definition: skelmodel.hpp:1103
void calcanimjoint(int i, const matrix4x3 &anim)
Definition: ragdoll.hpp:166
Definition: ragdoll.hpp:138
static Shader * lastshader
Definition: shader.hpp:144
vector< joint > joints
Definition: ragdoll.hpp:72
bool iszero() const
"do all cartesian coordinates (XYZ) of this vector have the value zero?"
Definition: geom.hpp:157
struct MDL::skin skin
Definition: skelmodel.hpp:1639
static bool enablenormals
Definition: animmodel.hpp:1305
void mul(float k)
Definition: geom.hpp:925
vector< pitchtarget > pitchtargets
Definition: skelmodel.hpp:478
GLuint index
Definition: glexts.hpp:412
void initpitchdeps()
Definition: skelmodel.hpp:708
float dot(const vec &o) const
dot product (line by line) of all 3 dimensions
Definition: geom.hpp:163
int interpparent
Definition: skelmodel.hpp:426
static void assignvert(vvertnw &vv, int j, vert &v, blendcombo &c)
Definition: skelmodel.hpp:251
int maxweights
Definition: skelmodel.hpp:175
bool animjoints
Definition: ragdoll.hpp:65
void * animkey() override
Definition: skelmodel.hpp:1168
#define RESTRICT
Definition: cube_tools.hpp:18
vec d
Definition: geom.hpp:904
int lastmillis
Definition: legacy_time.cpp:14
vec translate
Definition: skelmodel.hpp:1613
void render(const animstate *as, skin &s, vbocacheentry &vc)
Definition: skelmodel.hpp:392
static const int MAXBLENDCACHE
Definition: skelmodel.hpp:1106
int frame
Definition: skelmodel.hpp:452
void translate(const vec &p)
Definition: geom.hpp:949
void start(const char *filename, int videofps, int videow, int videoh, bool sound)
Definition: movie.cpp:975
void interpverts(const dualquat *RESTRICT bdata1, const dualquat *RESTRICT bdata2, bool tangents, void *RESTRICT vdata, skin &s)
Definition: skelmodel.hpp:344
int anim
Definition: animmodel.hpp:14
uchar * partmask
Definition: skelmodel.hpp:1522
matrix3 * tris
Definition: ragdoll.hpp:152
vbocacheentry vbocache[MAXVBOCACHE]
Definition: skelmodel.hpp:1110
static void settag(char *name, char *tagname, float *tx, float *ty, float *tz, float *rx, float *ry, float *rz)
Definition: skelmodel.hpp:1662
T & add(const T &x)
Add new index to vector.
Definition: cube_vector.hpp:73
struct MDL::boneinfo boneinfo
Definition: skelmodel.hpp:1640
pitchcorrect()
Definition: skelmodel.hpp:462
int numtris
Definition: skelmodel.hpp:175
int glde
Definition: octarender.cpp:1235
int remapblend(int blend)
Definition: skelmodel.hpp:1342
vector with 3 floats and some useful methods.
Definition: geom.hpp:110
#define DELETEA(p)
Delete Array, Wrapper around delete[], sets pointer to NULL afterwards(!).
Definition: cube_tools.hpp:31
#define ANIM_NOSKIN
Definition: ents.hpp:219
int parent
Definition: skelmodel.hpp:459
Definition: skelmodel.hpp:401
void genvbo(bool tangents, vbocacheentry &vc)
Definition: skelmodel.hpp:1173
PFNGLGETUNIFORMLOCATIONPROC glGetUniformLocation_
Definition: rendergl.cpp:152
void assignvert(vvertbumpw &vv, int j, vert &v, blendcombo &c)
Definition: skelmodel.hpp:259
Definition: skelmodel.hpp:1626
#define glDrawRangeElements_
Definition: glexts.hpp:27
hashtable< GLuint, int > blendoffsets
Definition: skelmodel.hpp:483
static void disablevbo()
Definition: animmodel.hpp:1349
Definition: skelmodel.hpp:17
void sort(F fun, int i=0, int n=-1)
sort the vector using quicksort template and sort criteria F
Definition: cube_vector.hpp:176
void setglslbones(UniformLoc &u, skelcacheentry &sc, skelcacheentry &bc, int count)
Definition: skelmodel.hpp:1072
vec & sub(const vec &o)
scalar subtraction
Definition: geom.hpp:177
skelcommands()
Definition: skelmodel.hpp:1839
Definition: animmodel.hpp:966
uchar bones[4]
Definition: skelmodel.hpp:17
Definition: skelmodel.hpp:170
skelmodel(const char *name)
Definition: skelmodel.hpp:1588
matrix4x3 * animjoints
Definition: ragdoll.hpp:153
const T & min(const inexor::rpc::SharedVar< T > &a, const T &b)
Definition: SharedVar.hpp:210
vector< skelmeshgroup * > users
Definition: skelmodel.hpp:469
void calctangents(bool areaweight=true)
Definition: skelmodel.hpp:207
vector< UniformLoc > uniformlocs
Definition: shader.hpp:158
void calcbb(vec &bbmin, vec &bbmax, const matrix4x3 &m) override
Definition: skelmodel.hpp:214
bool addtag(const char *name, int bone, const matrix4x3 &matrix)
Definition: skelmodel.hpp:538
matrix4x3 matrix
Definition: skelmodel.hpp:405
inexor::util::log_manager Log
Definition: Logging.cpp:241
static void setpitchtarget(char *name, char *animfile, int *frameoffset, float *pitchmin, float *pitchmax)
Definition: skelmodel.hpp:1723
static bool enablebones
Definition: animmodel.hpp:1305
void loaded() override
Definition: skelmodel.hpp:1581
static void loadpart(char *meshfile, char *skelname, float *smooth)
Definition: skelmodel.hpp:1646
PFNGLBUFFERDATAPROC glBufferData_
Definition: rendergl.cpp:126
int genvbo(vector< ushort > &idxs, int offset, vector< T > &vverts)
Definition: skelmodel.hpp:268
char * path(char *s)
Modifies the input string to only contain slashes in the direction the platform allows.
Definition: stream.cpp:63
#define MAXANIMPARTS
Definition: ents.hpp:253
dualquat invbase
Definition: skelmodel.hpp:428
int voffset
Definition: skelmodel.hpp:177
float weights[4]
Definition: skelmodel.hpp:26
static bool enabletc
Definition: animmodel.hpp:1305
int bone
Definition: skelmodel.hpp:446
const uchar * pos
Definition: bih.hpp:68
struct MDL::skeleton skeleton
Definition: skelmodel.hpp:1636
Definition: skelmodel.hpp:423
PFNGLUNIFORM4FVPROC glUniform4fv_
Definition: rendergl.cpp:160
vec translate
Definition: animmodel.hpp:619
int interpindex
Definition: skelmodel.hpp:426
blendcacheentry blendcache[MAXBLENDCACHE]
Definition: skelmodel.hpp:1107
void expandbonemask(uchar *expansion, int bone, int val)
Definition: skelmodel.hpp:771
int vverts
Definition: octarender.cpp:1235
dualquat base
Definition: skelmodel.hpp:428
bool glaring
Definition: glare.cpp:38
Cubescript API Deprecated.
static void disabletc()
Definition: animmodel.hpp:1337
Definition: skelmodel.hpp:1633
quaternions are number systems that extend complex numbers complex numbers extend the number system o...
Definition: geom.hpp:435
int length() const
return size of used memory
Definition: cube_vector.hpp:146
int vblends
Definition: skelmodel.hpp:1115
int findtag(const char *name)
Definition: skelmodel.hpp:532
ushort * edata
Definition: skelmodel.hpp:1112
void initragdoll(ragdolldata &d, skelcacheentry &sc, part *p)
Definition: skelmodel.hpp:916
void cleanup() override
Definition: skelmodel.hpp:1376
vec pos
Definition: skelmodel.hpp:14
int range
Definition: skelmodel.hpp:414
uchar bones[4]
Definition: skelmodel.hpp:27
void linkchildren()
Definition: skelmodel.hpp:792
int d
Definition: octaedit.cpp:1749
char * name
Definition: skelmodel.hpp:467
void translate(const vec &p)
Definition: geom.hpp:608
void t(T x, const char *cmp)
Definition: utilTest.cpp:52
void calctangents(B *bumpverts, V *verts, TC *tcverts, int numverts, T *tris, int numtris, bool areaweight)
Definition: animmodel.hpp:405
void bindnormals(void *v, int stride)
Definition: animmodel.hpp:536
void shareskeleton(char *name)
Definition: skelmodel.hpp:1142
int target
Definition: skelmodel.hpp:459
Definition: ents.hpp:258
#define INTERPBONE(bone)
Definition: skelmodel.hpp:859
struct MDL::skelanimspec animspec
Definition: skelmodel.hpp:1641
bumpvert * bumpverts
Definition: skelmodel.hpp:173
int parent
Definition: ragdoll.hpp:62
void * data
Definition: shader.hpp:131
Definition: animmodel.hpp:964
Definition: shader.hpp:142
int findpitchdep(int bone)
Definition: skelmodel.hpp:696
void modelcommand(F *fun, const char *suffix, const char *args)
Definition: animmodel.hpp:1530
int bone
Definition: skelmodel.hpp:459
~skeleton()
Definition: skelmodel.hpp:489
#define dir(name, v, d, s, os)
Definition: physics.cpp:2014
#define loopk(m)
Definition: cube_loops.hpp:10
int br
Definition: octaedit.cpp:1749
int findbone(const char *name)
Definition: skelmodel.hpp:526
vector< part * > parts
Definition: animmodel.hpp:1118
#define DELETEP(p)
Delete Pointer, Wrapper around delete, sets pointer to NULL afterwards(!).
Definition: cube_tools.hpp:28
unsigned char uchar
Basic type definitions.
Definition: cube_types.hpp:7
vec norm
Definition: skelmodel.hpp:13
struct MDL::pitchdep pitchdep
Definition: skelmodel.hpp:1642
blendcacheentry()
Definition: skelmodel.hpp:165
#define ANIM_RAGDOLL
Definition: ents.hpp:224
int millis
Definition: skelmodel.hpp:122
void addpitchdep(int bone, int frame)
Definition: skelmodel.hpp:674
int eoffset
Definition: skelmodel.hpp:177
int size() const
Definition: skelmodel.hpp:40
void normalize()
Definition: geom.hpp:601
static void setpitch(char *name, float *pitchscale, float *pitchoffset, float *pitchmin, float *pitchmax)
Definition: skelmodel.hpp:1680
Definition: skelmodel.hpp:15
vector< linkedpart > links
Definition: animmodel.hpp:614
vec2 tc
Definition: skelmodel.hpp:14
void preload(part *p) override
Definition: skelmodel.hpp:1423
void deletearrays()
Definition: cube_vector.hpp:166
virtual int type() const =0
animcacheentry()
Definition: skelmodel.hpp:126
int end()
Definition: glemu.cpp:256
static int matrixpos
Definition: animmodel.hpp:1310
modelcommands< MDL, struct MDL::skelmesh > commands
Definition: skelmodel.hpp:1635
T & add(const V &elem)
Definition: cube_hash.hpp:236
int scheduled
Definition: skelmodel.hpp:426
skelcacheentry()
Definition: skelmodel.hpp:152
GLintptr offset
Definition: glexts.hpp:354
int next
Definition: skelmodel.hpp:426
vector< mesh * > meshes
Definition: animmodel.hpp:470
static const int MAXVBOCACHE
Definition: skelmodel.hpp:1109
#define ANIM_NORENDER
Definition: ents.hpp:223
void transform(const vec &in, vec &out) const
Definition: geom.hpp:1782
dualquat * bdata
Definition: skelmodel.hpp:148
Definition: animmodel.hpp:1407
vert * verts
Definition: skelmodel.hpp:172
void explodelist(const char *s, vector< char * > &elems, int limit)
Definition: command.cpp:2656
float pitch
Definition: skelmodel.hpp:1612
int fr1
Definition: animmodel.hpp:14
skelanimspec()
Definition: skelmodel.hpp:416
int version
Definition: shader.hpp:130
static bool enabletangents
Definition: animmodel.hpp:1305
SharedVar< int > maxvsuniforms
int numtris
Definition: bih.hpp:67
skelmesh()
Definition: skelmodel.hpp:180
void mulorient(const quat &q)
Definition: geom.hpp:585
Definition: skelmodel.hpp:138
vec & add(const vec &o)
scalar sum
Definition: geom.hpp:174
static void disabletangents()
Definition: animmodel.hpp:1331
vbocacheentry & checkvbocache(skelcacheentry &sc, int owner)
Definition: skelmodel.hpp:1413
struct MDL::skelmeshgroup meshgroup
Definition: skelmodel.hpp:1637
static vector< skeladjustment > adjustments
Definition: skelmodel.hpp:1628
vec pos
Definition: skelmodel.hpp:13
Definition: animmodel.hpp:609
void adjust(dualquat &dq)
Definition: skelmodel.hpp:1617
int tag
Definition: animmodel.hpp:601
int totalframes() const override
Definition: skelmodel.hpp:1169
int parent
Definition: skelmodel.hpp:439
animpartmask * next
Definition: skelmodel.hpp:1513
char * name
Definition: skelmodel.hpp:403
~skelanimspec()
Definition: skelmodel.hpp:417
float deviated
Definition: skelmodel.hpp:453
void calcantipodes()
Definition: skelmodel.hpp:558
float interp
Definition: animmodel.hpp:59
Definition: skelmodel.hpp:1099
#define loopj(m)
Definition: cube_loops.hpp:9
float pitchangle
Definition: skelmodel.hpp:460
Definition: skelmodel.hpp:1610
uchar weights[4]
Definition: skelmodel.hpp:17
bool skeletal() const override
Definition: skelmodel.hpp:1600
T * access(const U &key)
Definition: cube_hash.hpp:135
vec transformnormal(const vec &v) const
Definition: geom.hpp:652
void accumulate(const dualquat &d, float k)
Definition: geom.hpp:630
int lastmove
Definition: ragdoll.hpp:148
Definition: skelmodel.hpp:457
dualquat pose
Definition: skelmodel.hpp:447
quat tangent
Definition: skelmodel.hpp:20
void setgpubones(skelcacheentry &sc, blendcacheentry *bc, int count)
Definition: skelmodel.hpp:1085
void calctags(part *p, skelcacheentry *sc=nullptr)
Definition: skelmodel.hpp:985
void remapbones()
Definition: skelmodel.hpp:594
GLsizei count
Definition: glexts.hpp:57
bool operator==(const blendcombo &c) const
Definition: skelmodel.hpp:33
int elen
Definition: skelmodel.hpp:177
#define loopl(m)
Definition: cube_loops.hpp:11
void initanimparts()
Definition: skelmodel.hpp:1556
const char * name
Definition: skelmodel.hpp:425
Definition: bih.hpp:58
skelmeshgroup()
Definition: skelmodel.hpp:1118
#define SEARCHCACHE(cachesize, cacheentry, cache, reusecheck)
Definition: skelmodel.hpp:1394
void addreljoint(int bone, int parent)
Definition: ragdoll.hpp:128
int children
Definition: skelmodel.hpp:426
vacollect vc
Definition: skelmodel.hpp:444
Definition: ragdoll.hpp:53
skelcacheentry & checkskelcache(part *p, const animstate *as, float pitch, const vec &axis, const vec &forward, ragdolldata *rdata)
Definition: skelmodel.hpp:1025
static void setadjust(char *name, float *yaw, float *pitch, float *roll, float *tx, float *ty, float *tz)
Definition: skelmodel.hpp:1827
Definition: skelmodel.hpp:11
quat real
Definition: geom.hpp:538
ragdolldata * ragdoll
Definition: ents.hpp:264
animpartmask * newpartmask()
Definition: skelmodel.hpp:1548
static void bindebo(GLuint ebo)
Definition: glemu.hpp:100
void nextversion()
Definition: skelmodel.hpp:154
void bindbones(void *wv, void *bv, int stride)
Definition: animmodel.hpp:564
#define GENVBOANIM(type)
vector< pitchdep > pitchdeps
Definition: skelmodel.hpp:477
int numverts
Definition: skelmodel.hpp:175
static int uniformlocversion()
Definition: shader.cpp:1020
#define defformatstring(d,...)
Definition: cube_formatting.hpp:62
int correctindex
Definition: skelmodel.hpp:426
part(animmodel *model, int index=0)
Definition: animmodel.hpp:621
void assignvert(vvertbump &vv, int j, vert &v, blendcombo &c)
Definition: skelmodel.hpp:244
Definition: cube_hash.hpp:224
Definition: skelmodel.hpp:146
dualquat & mul(float k)
Definition: geom.hpp:552
vbocacheentry()
Definition: skelmodel.hpp:143
bool shouldcleanup() const
Definition: skelmodel.hpp:1093
int version
Definition: skelmodel.hpp:149
int addblendcombo(const blendcombo &c)
Definition: skelmodel.hpp:1313
Definition: skelmodel.hpp:1511
#define BONEMASK_BONE
Definition: skelmodel.hpp:9
uchar bones[1]
Definition: skelmodel.hpp:1515
SharedVar< int > reservevpparams
int frame
Definition: skelmodel.hpp:414
Definition: skelmodel.hpp:118
vec & cross(const A &a, const B &b)
template based vector cross product
Definition: geom.hpp:211
void fixantipodal(const dualquat &d)
Definition: geom.hpp:621
bool usegpuskel
Definition: skelmodel.hpp:481
static void setanimpart(char *maskstr)
Definition: skelmodel.hpp:1804
matrix4x3 orient
Definition: ragdoll.hpp:57
#define FILLVDATA(type)
void concattagtransform(part *p, int i, const matrix4x3 &m, matrix4x3 &n)
Definition: skelmodel.hpp:977
bool target(fpsent *d, aistate &b, int pursue=0, bool force=false, float mindist=0.f)
Definition: ai.cpp:393
int bone
Definition: ragdoll.hpp:55
#define loopv(v)
Definition: cube_loops.hpp:21
int ragdollindex
Definition: skelmodel.hpp:426
float yaw
Definition: skelmodel.hpp:1612
void set()
Definition: shader.hpp:278
vec pos
Definition: ragdoll.hpp:19
char * newstring(size_t l)
Definition: cube_tools.hpp:71
vec & normalize()
normalize vector: divide it by its length (magnitude)
Definition: geom.hpp:198
int numinterpbones
Definition: skelmodel.hpp:471
int vlen
Definition: skelmodel.hpp:1115
bool dirty
Definition: skelmodel.hpp:150
int shared
Definition: skelmodel.hpp:468
Definition: animmodel.hpp:3
Definition: random.cpp:21
float pitchmin
Definition: skelmodel.hpp:453
vector< antipode > antipodes
Definition: skelmodel.hpp:475
GLuint vbuf
Definition: skelmodel.hpp:140
virtual void loaded()
Definition: animmodel.hpp:955
static matrix4 matrixstack[64]
Definition: animmodel.hpp:1311
blendcombo()
Definition: skelmodel.hpp:29
int tri
Definition: ragdoll.hpp:55
Definition: skelmodel.hpp:465
static void blendbones(dualquat &d, const dualquat *bdata, const blendcombo &c)
Definition: skelmodel.hpp:1348
const uchar * tc
Definition: bih.hpp:68
vector< pitchcorrect > pitchcorrects
Definition: skelmodel.hpp:479
animmodel * model
Definition: animmodel.hpp:611
GLuint vbuf
Definition: explosion.cpp:36
struct MDL::pitchtarget pitchtarget
Definition: skelmodel.hpp:1643
void setshader(Shader *s) override
Definition: skelmodel.hpp:380
Definition: skelmodel.hpp:13
ragdollskel * ragdoll
Definition: skelmodel.hpp:476
void buildnorms(bool areaweight=true)
Definition: skelmodel.hpp:202
static uint hthash(const fileskey &k)
Definition: console.cpp:698