Inexor
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
iqm.hpp
Go to the documentation of this file.
1 #pragma once
3 
4 struct iqmheader
5 {
6  char magic[16];
20 };
21 
22 struct iqmmesh
23 {
28 };
29 
30 enum
31 {
38  IQM_COLOR = 6,
39  IQM_CUSTOM = 0x10
40 };
41 
42 enum
43 {
44  IQM_BYTE = 0,
45  IQM_UBYTE = 1,
46  IQM_SHORT = 2,
48  IQM_INT = 4,
49  IQM_UINT = 5,
50  IQM_HALF = 6,
51  IQM_FLOAT = 7,
53 };
54 
56 {
58 };
59 
60 struct iqmjoint
61 {
63  int parent;
64  vec pos;
67 };
68 
69 struct iqmpose
70 {
71  int parent;
79 };
80 
81 struct iqmanim
82 {
85  float framerate;
87 };
88 
90 {
96 };
97 
98 struct iqm : skelmodel, skelloader<iqm>
99 {
100  iqm(const char *name) : skelmodel(name) {}
101 
102  static const char *formatname() { return "iqm"; }
103  int type() const override { return MDL_IQM; }
104 
105  struct iqmmeshgroup : skelmeshgroup
106  {
108  {
109  }
110 
111  bool loadiqmmeshes(const char *filename, const iqmheader &hdr, uchar *buf)
112  {
113  lilswap((uint *)&buf[hdr.ofs_vertexarrays], hdr.num_vertexarrays*sizeof(iqmvertexarray)/sizeof(uint));
114  lilswap((uint *)&buf[hdr.ofs_triangles], hdr.num_triangles*sizeof(iqmtriangle)/sizeof(uint));
115  lilswap((uint *)&buf[hdr.ofs_meshes], hdr.num_meshes*sizeof(iqmmesh)/sizeof(uint));
116  lilswap((uint *)&buf[hdr.ofs_joints], hdr.num_joints*sizeof(iqmjoint)/sizeof(uint));
117 
118  const char *str = hdr.ofs_text ? (char *)&buf[hdr.ofs_text] : "";
119  float *vpos = nullptr, *vnorm = nullptr, *vtan = nullptr, *vtc = nullptr;
120  uchar *vindex = nullptr, *vweight = nullptr;
121  iqmvertexarray *vas = (iqmvertexarray *)&buf[hdr.ofs_vertexarrays];
123  {
124  iqmvertexarray &va = vas[i];
125  switch(va.type)
126  {
127  case IQM_POSITION: if(va.format != IQM_FLOAT || va.size != 3) return false; vpos = (float *)&buf[va.offset]; lilswap(vpos, 3*hdr.num_vertexes); break;
128  case IQM_NORMAL: if(va.format != IQM_FLOAT || va.size != 3) return false; vnorm = (float *)&buf[va.offset]; lilswap(vnorm, 3*hdr.num_vertexes); break;
129  case IQM_TANGENT: if(va.format != IQM_FLOAT || va.size != 4) return false; vtan = (float *)&buf[va.offset]; lilswap(vtan, 4*hdr.num_vertexes); break;
130  case IQM_TEXCOORD: if(va.format != IQM_FLOAT || va.size != 2) return false; vtc = (float *)&buf[va.offset]; lilswap(vtc, 2*hdr.num_vertexes); break;
131  case IQM_BLENDINDEXES: if(va.format != IQM_UBYTE || va.size != 4) return false; vindex = (uchar *)&buf[va.offset]; break;
132  case IQM_BLENDWEIGHTS: if(va.format != IQM_UBYTE || va.size != 4) return false; vweight = (uchar *)&buf[va.offset]; break;
133  }
134  }
135  if(!vpos) return false;
136 
137  iqmtriangle *tris = (iqmtriangle *)&buf[hdr.ofs_triangles];
138  iqmmesh *imeshes = (iqmmesh *)&buf[hdr.ofs_meshes];
139  iqmjoint *joints = (iqmjoint *)&buf[hdr.ofs_joints];
140 
141  if(hdr.num_joints)
142  {
143  if(skel->numbones <= 0)
144  {
145  skel->numbones = hdr.num_joints;
146  skel->bones = new boneinfo[skel->numbones];
147  loopi(hdr.num_joints)
148  {
149  iqmjoint &j = joints[i];
150  boneinfo &b = skel->bones[i];
151  if(!b.name) b.name = newstring(&str[j.name]);
152  b.parent = j.parent;
153  if(skel->shared <= 1)
154  {
155  j.pos.y = -j.pos.y;
156  j.orient.x = -j.orient.x;
157  j.orient.z = -j.orient.z;
158  j.orient.normalize();
159  b.base = dualquat(j.orient, j.pos);
160  if(b.parent >= 0) b.base.mul(skel->bones[b.parent].base, dualquat(b.base));
161  (b.invbase = b.base).invert();
162  }
163  }
164  }
165 
166  if(skel->shared <= 1)
167  skel->linkchildren();
168  }
169 
170  loopi(hdr.num_meshes)
171  {
172  iqmmesh &im = imeshes[i];
173  skelmesh *m = new skelmesh;
174  m->group = this;
175  meshes.add(m);
176  m->name = newstring(&str[im.name]);
177  m->numverts = im.num_vertexes;
178  int noblend = -1;
179  if(m->numverts)
180  {
181  m->verts = new vert[m->numverts];
182  if(vtan) m->bumpverts = new bumpvert[m->numverts];
183  if(!vindex || !vweight)
184  {
185  blendcombo c;
186  c.finalize(0);
187  noblend = m->addblendcombo(c);
188  }
189  }
190  int fv = im.first_vertex;
191  float *mpos = vpos + 3*fv,
192  *mnorm = vnorm ? vnorm + 3*fv : nullptr,
193  *mtan = vtan ? vtan + 4*fv : nullptr,
194  *mtc = vtc ? vtc + 2*fv : nullptr;
195  uchar *mindex = vindex ? vindex + 4*fv : nullptr, *mweight = vweight ? vweight + 4*fv : nullptr;
196  loopj(im.num_vertexes)
197  {
198  vert &v = m->verts[j];
199  v.pos = vec(mpos[0], -mpos[1], mpos[2]);
200  mpos += 3;
201  if(mtc)
202  {
203  v.tc = vec2(mtc[0], mtc[1]);
204  mtc += 2;
205  }
206  else v.tc = vec2(0, 0);
207  if(mnorm)
208  {
209  v.norm = vec(mnorm[0], -mnorm[1], mnorm[2]);
210  mnorm += 3;
211  if(mtan)
212  {
213  m->calctangent(m->bumpverts[j], v.norm, vec(mtan[0], -mtan[1], mtan[2]), mtan[3]);
214  mtan += 4;
215  }
216  }
217  else v.norm = vec(0, 0, 0);
218  if(noblend < 0)
219  {
220  blendcombo c;
221  int sorted = 0;
222  loopk(4) sorted = c.addweight(sorted, mweight[k], mindex[k]);
223  mweight += 4;
224  mindex += 4;
225  c.finalize(sorted);
226  v.blend = m->addblendcombo(c);
227  }
228  else v.blend = noblend;
229  }
230  m->numtris = im.num_triangles;
231  if(m->numtris) m->tris = new tri[m->numtris];
232  iqmtriangle *mtris = tris + im.first_triangle;
233  loopj(im.num_triangles)
234  {
235  tri &t = m->tris[j];
236  t.vert[0] = mtris->vertex[0] - fv;
237  t.vert[1] = mtris->vertex[1] - fv;
238  t.vert[2] = mtris->vertex[2] - fv;
239  ++mtris;
240  }
241  if(!m->numtris || !m->numverts)
242  {
243  Log.std->error("empty mesh in {}", filename);
244  meshes.removeobj(m);
245  delete m;
246  }
247  }
248 
249  sortblendcombos();
250 
251  return true;
252  }
253 
254  bool loadiqmanims(const char *filename, const iqmheader &hdr, uchar *buf)
255  {
256  lilswap((uint *)&buf[hdr.ofs_poses], hdr.num_poses*sizeof(iqmpose)/sizeof(uint));
257  lilswap((uint *)&buf[hdr.ofs_anims], hdr.num_anims*sizeof(iqmanim)/sizeof(uint));
258  lilswap((ushort *)&buf[hdr.ofs_frames], hdr.num_frames*hdr.num_framechannels);
259 
260  const char *str = hdr.ofs_text ? (char *)&buf[hdr.ofs_text] : "";
261  iqmpose *poses = (iqmpose *)&buf[hdr.ofs_poses];
262  iqmanim *anims = (iqmanim *)&buf[hdr.ofs_anims];
263  ushort *frames = (ushort *)&buf[hdr.ofs_frames];
264  loopi(hdr.num_anims)
265  {
266  iqmanim &a = anims[i];
267  string name;
268  copystring(name, filename);
269  concatstring(name, ":");
270  concatstring(name, &str[a.name]);
271  skelanimspec *sa = skel->findskelanim(name);
272  if(sa) continue;
273  sa = &skel->addskelanim(name);
274  sa->frame = skel->numframes;
275  sa->range = a.num_frames;
276  dualquat *animbones = new dualquat[(skel->numframes+a.num_frames)*skel->numbones];
277  if(skel->bones)
278  {
279  memcpy(animbones, skel->framebones, skel->numframes*skel->numbones*sizeof(dualquat));
280  delete[] skel->framebones;
281  }
282  skel->framebones = animbones;
283  animbones += skel->numframes*skel->numbones;
284  skel->numframes += a.num_frames;
285  ushort *animdata = &frames[a.first_frame*hdr.num_framechannels];
286  loopj(a.num_frames)
287  {
288  dualquat *frame = &animbones[j*skel->numbones];
289  loopk(skel->numbones)
290  {
291  iqmpose &p = poses[k];
292  vec pos;
293  quat orient;
294  pos.x = p.offsetpos.x; if(p.mask&0x01) pos.x += *animdata++ * p.scalepos.x;
295  pos.y = -p.offsetpos.y; if(p.mask&0x02) pos.y -= *animdata++ * p.scalepos.y;
296  pos.z = p.offsetpos.z; if(p.mask&0x04) pos.z += *animdata++ * p.scalepos.z;
297  orient.x = -p.offsetorient.x; if(p.mask&0x08) orient.x -= *animdata++ * p.scaleorient.x;
298  orient.y = p.offsetorient.y; if(p.mask&0x10) orient.y += *animdata++ * p.scaleorient.y;
299  orient.z = -p.offsetorient.z; if(p.mask&0x20) orient.z -= *animdata++ * p.scaleorient.z;
300  orient.w = p.offsetorient.w; if(p.mask&0x40) orient.w += *animdata++ * p.scaleorient.w;
301  orient.normalize();
302  if(p.mask&0x380)
303  {
304  if(p.mask&0x80) animdata++;
305  if(p.mask&0x100) animdata++;
306  if(p.mask&0x200) animdata++;
307  }
308  frame[k] = dualquat(orient, pos);
309  if(adjustments.inrange(k)) adjustments[k].adjust(frame[k]);
310  boneinfo &b = skel->bones[k];
311  frame[k].mul(b.invbase);
312  if(b.parent >= 0) frame[k].mul(skel->bones[b.parent].base, dualquat(frame[k]));
313  frame[k].fixantipodal(skel->framebones[k]);
314  }
315  }
316  }
317 
318  return true;
319  }
320 
321  bool loadiqm(const char *filename, bool doloadmesh, bool doloadanim)
322  {
323  stream *f = openfile(filename, "rb");
324  if(!f) return false;
325 
326  uchar *buf = nullptr;
327  iqmheader hdr;
328  if(f->read(&hdr, sizeof(hdr)) != sizeof(hdr) || memcmp(hdr.magic, "INTERQUAKEMODEL", sizeof(hdr.magic))) goto error;
329  lilswap(&hdr.version, (sizeof(hdr) - sizeof(hdr.magic))/sizeof(uint));
330  if(hdr.version != 2) goto error;
331  if(hdr.filesize > (16<<20)) goto error; // sanity check... don't load files bigger than 16 MB
332  buf = new uchar[hdr.filesize];
333  if(f->read(buf + sizeof(hdr), hdr.filesize - sizeof(hdr)) != hdr.filesize - sizeof(hdr)) goto error;
334 
335  if(doloadmesh && !loadiqmmeshes(filename, hdr, buf)) goto error;
336  if(doloadanim && !loadiqmanims(filename, hdr, buf)) goto error;
337 
338  delete[] buf;
339  delete f;
340  return true;
341 
342  error:
343  if(buf) delete[] buf;
344  delete f;
345  return false;
346  }
347 
348  bool loadmesh(const char *filename)
349  {
350  name = newstring(filename);
351 
352  return loadiqm(filename, true, false);
353  }
354 
355  skelanimspec *loadanim(const char *animname) override
356  {
357  const char *sep = strchr(animname, ':');
358  skelanimspec *sa = skel->findskelanim(animname, sep ? '\0' : ':');
359  if(!sa)
360  {
361  string filename;
362  copystring(filename, animname);
363  if(sep) filename[sep - animname] = '\0';
364  if(loadiqm(filename, false, true))
365  sa = skel->findskelanim(animname, sep ? '\0' : ':');
366  }
367  return sa;
368  }
369  };
370 
371  meshgroup *loadmeshes(const char *name, va_list args) override
372  {
373  iqmmeshgroup *group = new iqmmeshgroup;
374  group->shareskeleton(va_arg(args, char *));
375  if(!group->loadmesh(name)) { delete group; return nullptr; }
376  return group;
377  }
378 
379  bool loaddefaultparts() override
380  {
381  skelpart &mdl = addpart();
382  mdl.pitchscale = mdl.pitchoffset = mdl.pitchmin = mdl.pitchmax = 0;
383  adjustments.setsize(0);
384  const char *fname = name + strlen(name);
385  do --fname; while(fname >= name && *fname!='/' && *fname!='\\');
386  fname++;
387  defformatstring(meshname, "%s/%s/%s.iqm", *modeldir, name, fname);
388  mdl.meshes = sharemeshes(path(meshname), NULL);
389  if(!mdl.meshes) return false;
390  mdl.initanimparts();
391  mdl.initskins();
392  return true;
393  }
394 
395  bool load() override
396  {
397  formatstring(dir, "%s/%s", *modeldir, name);
398  defformatstring(cfgname, "%s/%s/iqm.cfg", *modeldir, name);
399 
400  loading = this;
402  if(execfile(cfgname, false) && parts.length()) // configured iqm, will call the iqm* commands below
403  {
405  loading = nullptr;
406  loopv(parts) if(!parts[i]->meshes) return false;
407  }
408  else // iqm without configuration, try default tris and skin
409  {
411  if(!loaddefaultparts())
412  {
413  loading = nullptr;
414  return false;
415  }
416  loading = nullptr;
417  }
418  loaded();
419  return true;
420  }
421 };
422 
424 
Definition: skelmodel.hpp:21
vec scalepos
Definition: iqm.hpp:76
uint material
Definition: iqm.hpp:25
Definition: iqm.hpp:35
Definition: iqm.hpp:50
ushort vert[3]
Definition: skelmodel.hpp:21
uint num_joints
Definition: iqm.hpp:14
Definition: iqm.hpp:81
uint ofs_extensions
Definition: iqm.hpp:19
Definition: iqm.hpp:52
virtual void loaded()
Definition: animmodel.hpp:1298
skelanimspec * loadanim(const char *animname) override
Definition: iqm.hpp:355
Definition: iqm.hpp:49
char * name
Definition: model.hpp:42
string cfgname
Definition: worldio.cpp:227
vec pos
Definition: iqm.hpp:64
uint num_extensions
Definition: iqm.hpp:19
int addblendcombo(const blendcombo &c)
Definition: skelmodel.hpp:191
vec4 & normalize()
Definition: geom.hpp:378
int identflags
Definition: command.cpp:41
uint num_comment
Definition: iqm.hpp:18
unsigned int uint
Definition: cube_types.hpp:9
void initskins(Texture *tex=notexture, Texture *masks=notexture, int limit=0)
Definition: animmodel.hpp:691
Definition: model.hpp:11
Definition: animmodel.hpp:465
uint num_frames
Definition: iqm.hpp:84
bool execfile(const char *cfgfile, bool msg)
Definition: command.cpp:2262
uint type
Definition: iqm.hpp:91
uint first_vertex
Definition: iqm.hpp:26
Definition: skelmodel.hpp:20
Definition: iqm.hpp:34
static Logger std
Logger for everything not fitting elsewhere.
Definition: Logging.hpp:89
meshgroup * sharemeshes(const char *name,...)
Definition: animmodel.hpp:583
uint num_vertexarrays
Definition: iqm.hpp:12
float pitchmin
Definition: animmodel.hpp:618
Definition: iqm.hpp:39
meshgroup * meshes
Definition: animmodel.hpp:613
uint num_triangles
Definition: iqm.hpp:27
Definition: skelmodel.hpp:23
uint ofs_bounds
Definition: iqm.hpp:17
Definition: iqm.hpp:45
int addweight(int sorted, float weight, int bone)
Definition: skelmodel.hpp:61
2-dimensional float vectors
Definition: geom.hpp:38
vec2 tc
Definition: skelmodel.hpp:13
int parent
Definition: iqm.hpp:71
uint name
Definition: iqm.hpp:83
static const char * formatname()
Definition: iqm.hpp:102
uint size
Definition: iqm.hpp:94
uint ofs_comment
Definition: iqm.hpp:18
SharedVar< char * > modeldir
static iqm * loading
Definition: animmodel.hpp:1396
Definition: skelmodel.hpp:411
uint first_triangle
Definition: iqm.hpp:27
bool loadiqmmeshes(const char *filename, const iqmheader &hdr, uchar *buf)
Definition: iqm.hpp:111
int parent
Definition: skelmodel.hpp:426
uint ofs_poses
Definition: iqm.hpp:15
Definition: iqm.hpp:46
int blend
Definition: skelmodel.hpp:13
virtual size_t read(void *buf, size_t len)
Definition: stream.hpp:42
Definition: skelmodel.hpp:1518
uint offset
Definition: iqm.hpp:95
uint mask
Definition: iqm.hpp:72
Definition: iqm.hpp:47
uint flags
Definition: iqm.hpp:9
tri * tris
Definition: skelmodel.hpp:174
bool loadmesh(const char *filename)
Definition: iqm.hpp:348
Definition: geom.hpp:536
ICOMMAND * f(float *a, float *b), floatret(*a **b)
Definition: iqm.hpp:55
Definition: iqm.hpp:44
Definition: iqm.hpp:89
4-dimensional float vector all methods stay basicly the same but with an extra dimension ...
Definition: geom.hpp:343
bool inrange(size_t i) const
safety check: tests if index i exists in this vector
Definition: cube_vector.hpp:123
uint version
Definition: iqm.hpp:7
Definition: iqm.hpp:4
int parent
Definition: iqm.hpp:63
unsigned short ushort
Definition: cube_types.hpp:8
void finalize(int sorted)
Definition: skelmodel.hpp:81
bool loaddefaultparts() override
Definition: iqm.hpp:379
void setsize(int i)
shrink vector memory size
Definition: cube_vector.hpp:163
Definition: iqm.hpp:98
Definition: iqm.hpp:48
else loopi(numargs)
Definition: command.cpp:3019
skelpart & addpart()
Definition: skelmodel.hpp:1602
uint num_meshes
Definition: iqm.hpp:11
#define NULL
Definition: cube_types.hpp:35
Definition: iqm.hpp:36
float pitchoffset
Definition: animmodel.hpp:618
skelcommands< iqm > iqmcommands
Definition: iqm.hpp:423
int numtris
Definition: skelmodel.hpp:175
vector with 3 floats and some useful methods.
Definition: geom.hpp:110
uint ofs_frames
Definition: iqm.hpp:17
Definition: skelmodel.hpp:1626
Definition: iqm.hpp:51
char void formatstring(char(&d)[N], const char *fmt,...) PRINTFARGS(2
Definition: skelmodel.hpp:170
uint name
Definition: iqm.hpp:24
bool load() override
Definition: iqm.hpp:395
inexor::util::log_manager Log
Definition: Logging.cpp:241
char * path(char *s)
Modifies the input string to only contain slashes in the direction the platform allows.
Definition: stream.cpp:63
dualquat invbase
Definition: skelmodel.hpp:428
iqmmeshgroup()
Definition: iqm.hpp:107
Definition: skelmodel.hpp:423
uint ofs_vertexarrays
Definition: iqm.hpp:12
uint vertex[3]
Definition: iqm.hpp:57
dualquat base
Definition: skelmodel.hpp:428
iqm(const char *name)
Definition: iqm.hpp:100
Definition: skelmodel.hpp:1633
quaternions are number systems that extend complex numbers complex numbers extend the number system o...
Definition: geom.hpp:435
float pitchscale
Definition: animmodel.hpp:618
Definition: iqm.hpp:22
int orient
Definition: octaedit.cpp:164
vec offsetpos
Definition: iqm.hpp:73
uint filesize
Definition: iqm.hpp:8
int range
Definition: skelmodel.hpp:414
void t(T x, const char *cmp)
Definition: utilTest.cpp:52
Legacy file system streams.
Definition: stream.hpp:22
vec offsetsize
Definition: iqm.hpp:75
bumpvert * bumpverts
Definition: skelmodel.hpp:173
uint num_frames
Definition: iqm.hpp:17
meshgroup * loadmeshes(const char *name, va_list args) override
Definition: iqm.hpp:371
Definition: iqm.hpp:69
#define loopk(m)
Definition: cube_loops.hpp:10
Definition: iqm.hpp:60
Definition: command.hpp:72
vector< part * > parts
Definition: animmodel.hpp:1118
unsigned char uchar
Basic type definitions.
Definition: cube_types.hpp:7
vec norm
Definition: skelmodel.hpp:13
char magic[16]
Definition: iqm.hpp:6
float framerate
Definition: iqm.hpp:85
uint flags
Definition: iqm.hpp:86
bool loadiqmanims(const char *filename, const iqmheader &hdr, uchar *buf)
Definition: iqm.hpp:254
bool loadiqm(const char *filename, bool doloadmesh, bool doloadanim)
Definition: iqm.hpp:321
uint ofs_meshes
Definition: iqm.hpp:11
uint name
Definition: iqm.hpp:62
uint num_vertexes
Definition: iqm.hpp:26
char * copystring(char *d, const char *s, size_t len)
Definition: cube_tools.hpp:56
vert * verts
Definition: skelmodel.hpp:172
vec scalesize
Definition: iqm.hpp:78
uint num_text
Definition: iqm.hpp:10
static vector< skeladjustment > adjustments
Definition: skelmodel.hpp:1628
uint num_poses
Definition: iqm.hpp:15
vec pos
Definition: skelmodel.hpp:13
vec4 offsetorient
Definition: iqm.hpp:74
vec size
Definition: iqm.hpp:66
#define loopj(m)
Definition: cube_loops.hpp:9
Definition: world.hpp:82
uint num_framechannels
Definition: iqm.hpp:17
const uchar fv[6][4]
Definition: octa.cpp:738
uint ofs_anims
Definition: iqm.hpp:16
int type() const override
Definition: iqm.hpp:103
uint ofs_joints
Definition: iqm.hpp:14
void initanimparts()
Definition: skelmodel.hpp:1556
const char * name
Definition: skelmodel.hpp:425
uint ofs_text
Definition: iqm.hpp:10
uint ofs_adjacency
Definition: iqm.hpp:13
Definition: skelmodel.hpp:11
uint flags
Definition: iqm.hpp:92
int numverts
Definition: skelmodel.hpp:175
#define defformatstring(d,...)
Definition: cube_formatting.hpp:62
dualquat & mul(float k)
Definition: geom.hpp:552
stream * openfile(const char *filename, const char *mode)
Definition: stream.cpp:907
Definition: iqm.hpp:33
int frame
Definition: skelmodel.hpp:414
void fixantipodal(const dualquat &d)
Definition: geom.hpp:621
#define loopv(v)
Definition: cube_loops.hpp:21
quat orient
Definition: iqm.hpp:65
float pitchmax
Definition: animmodel.hpp:618
char * newstring(size_t l)
Definition: cube_tools.hpp:71
Definition: iqm.hpp:105
uint format
Definition: iqm.hpp:93
static string dir
Definition: animmodel.hpp:1397
uint num_triangles
Definition: iqm.hpp:13
Definition: iqm.hpp:32
uint num_anims
Definition: iqm.hpp:16
vec4 scaleorient
Definition: iqm.hpp:77
uint num_vertexes
Definition: iqm.hpp:12
Definition: iqm.hpp:38
uint ofs_triangles
Definition: iqm.hpp:13
T lilswap(T n)
Definition: cube_endian.hpp:41
char * concatstring(char *d, const char *s, size_t len)
Definition: cube_formatting.hpp:19
Definition: iqm.hpp:37
Definition: skelmodel.hpp:13
uint first_frame
Definition: iqm.hpp:84