diff options
| author | xolatile | 2025-07-20 12:18:48 +0200 |
|---|---|---|
| committer | xolatile | 2025-07-20 12:18:48 +0200 |
| commit | c46daa31f8e0339aed12736699d0003d27f1fc7e (patch) | |
| tree | 11083f9c5eced31cf246c5feadc2c96d66e165cb /src/engine/iqm.h | |
| parent | e9ad09c58820b31743251f793f30e5d4d49a0dca (diff) | |
| download | xolatile-badassbug-c46daa31f8e0339aed12736699d0003d27f1fc7e.tar.xz xolatile-badassbug-c46daa31f8e0339aed12736699d0003d27f1fc7e.tar.zst | |
Most warnings fixed, ignoring one warning for now...
Diffstat (limited to 'src/engine/iqm.h')
| -rw-r--r-- | src/engine/iqm.h | 396 |
1 files changed, 396 insertions, 0 deletions
diff --git a/src/engine/iqm.h b/src/engine/iqm.h new file mode 100644 index 0000000..f426a6b --- /dev/null +++ b/src/engine/iqm.h @@ -0,0 +1,396 @@ +struct iqm; + +struct iqmheader +{ + char magic[16]; + uint version; + uint filesize; + uint flags; + uint num_text, ofs_text; + uint num_meshes, ofs_meshes; + uint num_vertexarrays, num_vertexes, ofs_vertexarrays; + uint num_triangles, ofs_triangles, ofs_adjacency; + uint num_joints, ofs_joints; + uint num_poses, ofs_poses; + uint num_anims, ofs_anims; + uint num_frames, num_framechannels, ofs_frames, ofs_bounds; + uint num_comment, ofs_comment; + uint num_extensions, ofs_extensions; +}; + +struct iqmmesh +{ + uint name; + uint material; + uint first_vertex, num_vertexes; + uint first_triangle, num_triangles; +}; + +enum +{ + IQM_POSITION = 0, + IQM_TEXCOORD = 1, + IQM_NORMAL = 2, + IQM_TANGENT = 3, + IQM_BLENDINDEXES = 4, + IQM_BLENDWEIGHTS = 5, + IQM_COLOR = 6, + IQM_CUSTOM = 0x10 +}; + +enum +{ + IQM_BYTE = 0, + IQM_UBYTE = 1, + IQM_SHORT = 2, + IQM_USHORT = 3, + IQM_INT = 4, + IQM_UINT = 5, + IQM_HALF = 6, + IQM_FLOAT = 7, + IQM_DOUBLE = 8, +}; + +struct iqmtriangle +{ + uint vertex[3]; +}; + +struct iqmjoint +{ + uint name; + int parent; + vec pos; + quat orient; + vec size; +}; + +struct iqmpose +{ + int parent; + uint mask; + vec offsetpos; + vec4 offsetorient; + vec offsetsize; + vec scalepos; + vec4 scaleorient; + vec scalesize; +}; + +struct iqmanim +{ + uint name; + uint first_frame, num_frames; + float framerate; + uint flags; +}; + +struct iqmvertexarray +{ + uint type; + uint flags; + uint format; + uint size; + uint offset; +}; + +struct iqm : skelloader<iqm> +{ + iqm(const char *name) : skelloader(name) {} + + static const char *formatname() { return "iqm"; } + int type() const { return MDL_IQM; } + + struct iqmmeshgroup : skelmeshgroup + { + iqmmeshgroup() + { + } + + bool loadiqmmeshes(const char *filename, const iqmheader &hdr, uchar *buf) + { + lilswap((uint *)&buf[hdr.ofs_vertexarrays], hdr.num_vertexarrays*sizeof(iqmvertexarray)/sizeof(uint)); + lilswap((uint *)&buf[hdr.ofs_triangles], hdr.num_triangles*sizeof(iqmtriangle)/sizeof(uint)); + lilswap((uint *)&buf[hdr.ofs_meshes], hdr.num_meshes*sizeof(iqmmesh)/sizeof(uint)); + lilswap((uint *)&buf[hdr.ofs_joints], hdr.num_joints*sizeof(iqmjoint)/sizeof(uint)); + + const char *str = hdr.ofs_text ? (char *)&buf[hdr.ofs_text] : ""; + float *vpos = NULL, *vnorm = NULL, *vtan = NULL, *vtc = NULL; + uchar *vindex = NULL, *vweight = NULL; + iqmvertexarray *vas = (iqmvertexarray *)&buf[hdr.ofs_vertexarrays]; + loopi(hdr.num_vertexarrays) + { + iqmvertexarray &va = vas[i]; + switch(va.type) + { + 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; + 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; + 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; + 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; + case IQM_BLENDINDEXES: if(va.format != IQM_UBYTE || va.size != 4) return false; vindex = (uchar *)&buf[va.offset]; break; + case IQM_BLENDWEIGHTS: if(va.format != IQM_UBYTE || va.size != 4) return false; vweight = (uchar *)&buf[va.offset]; break; + } + } + if(!vpos) return false; + + iqmtriangle *tris = (iqmtriangle *)&buf[hdr.ofs_triangles]; + iqmmesh *imeshes = (iqmmesh *)&buf[hdr.ofs_meshes]; + iqmjoint *joints = (iqmjoint *)&buf[hdr.ofs_joints]; + + if(hdr.num_joints) + { + if(skel->numbones <= 0) + { + skel->numbones = hdr.num_joints; + skel->bones = new boneinfo[skel->numbones]; + loopi(hdr.num_joints) + { + iqmjoint &j = joints[i]; + boneinfo &b = skel->bones[i]; + if(!b.name) b.name = newstring(&str[j.name]); + b.parent = j.parent; + if(skel->shared <= 1) + { + j.pos.y = -j.pos.y; + j.orient.x = -j.orient.x; + j.orient.z = -j.orient.z; + j.orient.normalize(); + b.base = dualquat(j.orient, j.pos); + if(b.parent >= 0) b.base.mul(skel->bones[b.parent].base, dualquat(b.base)); + (b.invbase = b.base).invert(); + } + } + } + + if(skel->shared <= 1) + skel->linkchildren(); + } + + loopi(hdr.num_meshes) + { + iqmmesh &im = imeshes[i]; + skelmesh *m = new skelmesh; + m->group = this; + meshes.add(m); + m->name = newstring(&str[im.name]); + m->numverts = im.num_vertexes; + int noblend = -1; + if(m->numverts) + { + m->verts = new vert[m->numverts]; + if(vtan) m->bumpverts = new bumpvert[m->numverts]; + if(!vindex || !vweight) + { + blendcombo c; + c.finalize(0); + noblend = m->addblendcombo(c); + } + } + int fv = im.first_vertex; + float *mpos = vpos + 3*fv, + *mnorm = vnorm ? vnorm + 3*fv : NULL, + *mtan = vtan ? vtan + 4*fv : NULL, + *mtc = vtc ? vtc + 2*fv : NULL; + uchar *mindex = vindex ? vindex + 4*fv : NULL, *mweight = vweight ? vweight + 4*fv : NULL; + loopj(im.num_vertexes) + { + vert &v = m->verts[j]; + v.pos = vec(mpos[0], -mpos[1], mpos[2]); + mpos += 3; + if(mtc) + { + v.tc = vec2(mtc[0], mtc[1]); + mtc += 2; + } + else v.tc = vec2(0, 0); + if(mnorm) + { + v.norm = vec(mnorm[0], -mnorm[1], mnorm[2]); + mnorm += 3; + if(mtan) + { + m->calctangent(m->bumpverts[j], v.norm, vec(mtan[0], -mtan[1], mtan[2]), mtan[3]); + mtan += 4; + } + } + else v.norm = vec(0, 0, 0); + if(noblend < 0) + { + blendcombo c; + int sorted = 0; + loopk(4) sorted = c.addweight(sorted, mweight[k], mindex[k]); + mweight += 4; + mindex += 4; + c.finalize(sorted); + v.blend = m->addblendcombo(c); + } + else v.blend = noblend; + } + m->numtris = im.num_triangles; + if(m->numtris) m->tris = new tri[m->numtris]; + iqmtriangle *mtris = tris + im.first_triangle; + loopj(im.num_triangles) + { + tri &t = m->tris[j]; + t.vert[0] = mtris->vertex[0] - fv; + t.vert[1] = mtris->vertex[1] - fv; + t.vert[2] = mtris->vertex[2] - fv; + ++mtris; + } + if(!m->numtris || !m->numverts) + { + conoutf(CON_WARN, "empty mesh in %s", filename); + meshes.removeobj(m); + delete m; + } + } + + sortblendcombos(); + + return true; + } + + bool loadiqmanims(const char *filename, const iqmheader &hdr, uchar *buf) + { + lilswap((uint *)&buf[hdr.ofs_poses], hdr.num_poses*sizeof(iqmpose)/sizeof(uint)); + lilswap((uint *)&buf[hdr.ofs_anims], hdr.num_anims*sizeof(iqmanim)/sizeof(uint)); + lilswap((ushort *)&buf[hdr.ofs_frames], hdr.num_frames*hdr.num_framechannels); + + const char *str = hdr.ofs_text ? (char *)&buf[hdr.ofs_text] : ""; + iqmpose *poses = (iqmpose *)&buf[hdr.ofs_poses]; + iqmanim *anims = (iqmanim *)&buf[hdr.ofs_anims]; + ushort *frames = (ushort *)&buf[hdr.ofs_frames]; + loopi(hdr.num_anims) + { + iqmanim &a = anims[i]; + string name; + copystring(name, filename); + concatstring(name, ":"); + concatstring(name, &str[a.name]); + skelanimspec *sa = skel->findskelanim(name); + if(sa) continue; + sa = &skel->addskelanim(name); + sa->frame = skel->numframes; + sa->range = a.num_frames; + dualquat *animbones = new dualquat[(skel->numframes+a.num_frames)*skel->numbones]; + if(skel->bones) + { + memcpy(animbones, skel->framebones, skel->numframes*skel->numbones*sizeof(dualquat)); + delete[] skel->framebones; + } + skel->framebones = animbones; + animbones += skel->numframes*skel->numbones; + skel->numframes += a.num_frames; + ushort *animdata = &frames[a.first_frame*hdr.num_framechannels]; + loopj(a.num_frames) + { + dualquat *frame = &animbones[j*skel->numbones]; + loopk(skel->numbones) + { + iqmpose &p = poses[k]; + vec pos; + quat orient; + pos.x = p.offsetpos.x; if(p.mask&0x01) pos.x += *animdata++ * p.scalepos.x; + pos.y = -p.offsetpos.y; if(p.mask&0x02) pos.y -= *animdata++ * p.scalepos.y; + pos.z = p.offsetpos.z; if(p.mask&0x04) pos.z += *animdata++ * p.scalepos.z; + orient.x = -p.offsetorient.x; if(p.mask&0x08) orient.x -= *animdata++ * p.scaleorient.x; + orient.y = p.offsetorient.y; if(p.mask&0x10) orient.y += *animdata++ * p.scaleorient.y; + orient.z = -p.offsetorient.z; if(p.mask&0x20) orient.z -= *animdata++ * p.scaleorient.z; + orient.w = p.offsetorient.w; if(p.mask&0x40) orient.w += *animdata++ * p.scaleorient.w; + orient.normalize(); + if(p.mask&0x380) + { + if(p.mask&0x80) animdata++; + if(p.mask&0x100) animdata++; + if(p.mask&0x200) animdata++; + } + frame[k] = dualquat(orient, pos); + if(adjustments.inrange(k)) adjustments[k].adjust(frame[k]); + boneinfo &b = skel->bones[k]; + frame[k].mul(b.invbase); + if(b.parent >= 0) frame[k].mul(skel->bones[b.parent].base, dualquat(frame[k])); + frame[k].fixantipodal(skel->framebones[k]); + } + } + } + + return true; + } + + bool loadiqm(const char *filename, bool doloadmesh, bool doloadanim) + { + stream *f = openfile(filename, "rb"); + if(!f) return false; + + uchar *buf = NULL; + iqmheader hdr; + if(f->read(&hdr, sizeof(hdr)) != sizeof(hdr) || memcmp(hdr.magic, "INTERQUAKEMODEL", sizeof(hdr.magic))) goto error; + lilswap(&hdr.version, (sizeof(hdr) - sizeof(hdr.magic))/sizeof(uint)); + if(hdr.version != 2) goto error; + if(hdr.filesize > (16<<20)) goto error; // sanity check... don't load files bigger than 16 MB + buf = new (false) uchar[hdr.filesize]; + if(!buf || f->read(buf + sizeof(hdr), hdr.filesize - sizeof(hdr)) != hdr.filesize - sizeof(hdr)) goto error; + + if(doloadmesh && !loadiqmmeshes(filename, hdr, buf)) goto error; + if(doloadanim && !loadiqmanims(filename, hdr, buf)) goto error; + + delete[] buf; + delete f; + return true; + + error: + if(buf) delete[] buf; + delete f; + return false; + } + + bool loadmesh(const char *filename) + { + name = newstring(filename); + + return loadiqm(filename, true, false); + } + + skelanimspec *loadanim(const char *animname) + { + const char *sep = strchr(animname, ':'); + skelanimspec *sa = skel->findskelanim(animname, sep ? '\0' : ':'); + if(!sa) + { + string filename; + copystring(filename, animname); + if(sep) filename[sep - animname] = '\0'; + if(loadiqm(filename, false, true)) + sa = skel->findskelanim(animname, sep ? '\0' : ':'); + } + return sa; + } + }; + + meshgroup *loadmeshes(const char *name, va_list args) + { + iqmmeshgroup *group = new iqmmeshgroup; + group->shareskeleton(va_arg(args, char *)); + if(!group->loadmesh(name)) { delete group; return NULL; } + return group; + } + + bool loaddefaultparts() + { + skelpart &mdl = addpart(); + mdl.pitchscale = mdl.pitchoffset = mdl.pitchmin = mdl.pitchmax = 0; + adjustments.setsize(0); + const char *fname = name + strlen(name); + do --fname; while(fname >= name && *fname!='/' && *fname!='\\'); + fname++; + defformatstring(meshname, "packages/models/%s/%s.iqm", name, fname); + mdl.meshes = sharemeshes(path(meshname), NULL); + if(!mdl.meshes) return false; + mdl.initanimparts(); + mdl.initskins(); + return true; + } +}; + +skelcommands<iqm> iqmcommands; + |
