From f7b61a1dd845f1a7503eff1347a9d044ca93f473 Mon Sep 17 00:00:00 2001 From: Spoike Date: Fri, 26 Jun 2015 12:15:56 +0000 Subject: [PATCH] fix the iqm loader to be more versatile, so we now support more exotic forms of iqm. git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@4918 fc73d0e0-1445-4013-8a0c-d673dee63da5 --- engine/common/com_mesh.c | 432 +++++++++++++++++++++++++++++---------- 1 file changed, 319 insertions(+), 113 deletions(-) diff --git a/engine/common/com_mesh.c b/engine/common/com_mesh.c index 2dcf4f492..bee5d23d5 100644 --- a/engine/common/com_mesh.c +++ b/engine/common/com_mesh.c @@ -6026,114 +6026,114 @@ qboolean QDECL Mod_LoadDarkPlacesModel(model_t *mod, void *buffer, size_t fsize) struct iqmheader { - char magic[16]; - unsigned int version; - unsigned int filesize; - unsigned int flags; - unsigned int num_text, ofs_text; - unsigned int num_meshes, ofs_meshes; - unsigned int num_vertexarrays, num_vertexes, ofs_vertexarrays; - unsigned int num_triangles, ofs_triangles, ofs_adjacency; - unsigned int num_joints, ofs_joints; - unsigned int num_poses, ofs_poses; - unsigned int num_anims, ofs_anims; - unsigned int num_frames, num_framechannels, ofs_frames, ofs_bounds; - unsigned int num_comment, ofs_comment; - unsigned int num_extensions, ofs_extensions; + char magic[16]; + unsigned int version; + unsigned int filesize; + unsigned int flags; + unsigned int num_text, ofs_text; + unsigned int num_meshes, ofs_meshes; + unsigned int num_vertexarrays, num_vertexes, ofs_vertexarrays; + unsigned int num_triangles, ofs_triangles, ofs_adjacency; + unsigned int num_joints, ofs_joints; + unsigned int num_poses, ofs_poses; + unsigned int num_anims, ofs_anims; + unsigned int num_frames, num_framechannels, ofs_frames, ofs_bounds; + unsigned int num_comment, ofs_comment; + unsigned int num_extensions, ofs_extensions; }; struct iqmmesh { - unsigned int name; - unsigned int material; - unsigned int first_vertex, num_vertexes; - unsigned int first_triangle, num_triangles; + unsigned int name; + unsigned int material; + unsigned int first_vertex, num_vertexes; + unsigned int 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 + 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, + 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 { - unsigned int vertex[3]; + unsigned int vertex[3]; }; struct iqmjoint1 { - unsigned int name; - int parent; - float translate[3], rotate[3], scale[3]; + unsigned int name; + int parent; + float translate[3], rotate[3], scale[3]; }; struct iqmjoint2 { - unsigned int name; - int parent; - float translate[3], rotate[4], scale[3]; + unsigned int name; + int parent; + float translate[3], rotate[4], scale[3]; }; struct iqmpose1 { - int parent; - unsigned int mask; - float channeloffset[9]; - float channelscale[9]; + int parent; + unsigned int mask; + float channeloffset[9]; + float channelscale[9]; }; struct iqmpose2 { - int parent; - unsigned int mask; - float channeloffset[10]; - float channelscale[10]; + int parent; + unsigned int mask; + float channeloffset[10]; + float channelscale[10]; }; struct iqmanim { - unsigned int name; - unsigned int first_frame, num_frames; - float framerate; - unsigned int flags; + unsigned int name; + unsigned int first_frame, num_frames; + float framerate; + unsigned int flags; }; enum { - IQM_LOOP = 1<<0 + IQM_LOOP = 1<<0 }; struct iqmvertexarray { - unsigned int type; - unsigned int flags; - unsigned int format; - unsigned int size; - unsigned int offset; + unsigned int type; + unsigned int flags; + unsigned int format; + unsigned int size; + unsigned int offset; }; struct iqmbounds { - float bbmin[3], bbmax[3]; - float xyradius, radius; + float bbmin[3], bbmax[3]; + float xyradius, radius; }; /* @@ -6166,6 +6166,214 @@ galisskeletaltransforms_t *IQM_ImportTransforms(int *resultcount, int inverts, f } */ +static qboolean IQM_ImportArray4B(qbyte *base, struct iqmvertexarray *src, byte_vec4_t *out, size_t count, unsigned int maxval) +{ + size_t i; + unsigned int j; + unsigned int sz = LittleLong(src->size); + unsigned int fmt = LittleLong(src->format); + unsigned int offset = LittleLong(src->offset); + qboolean invalid = false; + maxval = min(256,maxval); + if (!offset) + { + sz = 0; + fmt = IQM_UBYTE; + } + switch(fmt) + { + default: + sz = 0; + invalid = true; + break; + case IQM_BYTE: //FIXME: should be signed, but this makes no sense for our uses + case IQM_UBYTE: + { + qbyte *in = (qbyte*)(base+offset); + /*if (sz == 4) + memcpy(out, in, count * sizeof(*out)); //the fast path. + else*/ for (i = 0; i < count; i++) + { + for (j = 0; j < 4 && j < sz; j++) + { + if (in[i*sz+j] >= maxval) + { + out[i][j] = 0; + invalid = true; + } + else + out[i][j] = in[i*sz+j]; + } + } + } + break; + case IQM_SHORT://FIXME: should be signed, but this makes no sense for our uses + case IQM_USHORT: + { + unsigned short *in = (unsigned short*)(base+offset); + for (i = 0; i < count; i++) + { + for (j = 0; j < 4 && j < sz; j++) + { + if (in[i*sz+j] >= maxval) + { + out[i][j] = 0; + invalid = true; + } + else + out[i][j] = in[i*sz+j]; + } + } + } + break; + case IQM_INT://FIXME: should be signed, but this makes no sense for our uses + case IQM_UINT: + { + unsigned int *in = (unsigned int*)(base+offset); + for (i = 0; i < count; i++) + { + for (j = 0; j < 4 && j < sz; j++) + { + if (in[i*sz+j] >= maxval) + { + out[i][j] = 0; + invalid = true; + } + else + out[i][j] = in[i*sz+j]; + } + } + } + break; + //float types don't really make sense + } + + //if there were not enough elements, pad it. + if (sz < 4) + { + for (i = 0; i < count; i++) + { + for (j = sz; j < 4; j++) + out[i][j] = 0; + } + } + + return !invalid; +} +static void IQM_ImportArrayF(qbyte *base, struct iqmvertexarray *src, float *out, size_t e, size_t count, float *def) +{ + size_t i; + unsigned int j; + unsigned int sz = LittleLong(src->size); + unsigned int fmt = LittleLong(src->format); + unsigned int offset = LittleLong(src->offset); + if (!offset) + { + sz = 0; + fmt = IQM_FLOAT; + } + switch(fmt) + { + default: + sz = 0; + break; + case IQM_BYTE: //FIXME: should be signed + { + char *in = (qbyte*)(base+offset); + for (i = 0; i < count; i++) + { + for (j = 0; j < e && j < sz; j++) + out[i*e+j] = in[i*sz+j] * (1.0/127); + } + } + break; + case IQM_UBYTE: + { + qbyte *in = (qbyte*)(base+offset); + for (i = 0; i < count; i++) + { + for (j = 0; j < e && j < sz; j++) + out[i*e+j] = in[i*sz+j] * (1.0/255); + } + } + break; + case IQM_SHORT: + { + short *in = (short*)(base+offset); + for (i = 0; i < count; i++) + { + for (j = 0; j < e && j < sz; j++) + out[i*e+j] = in[i*sz+j] * (1.0/32767); + } + } + break; + case IQM_USHORT: + { + unsigned short *in = (unsigned short*)(base+offset); + for (i = 0; i < count; i++) + { + for (j = 0; j < e && j < sz; j++) + out[i*e+j] = in[i*sz+j] * (1.0/65535); + } + } + break; + case IQM_INT://FIXME: should be signed + case IQM_UINT: + { + unsigned int *in = (unsigned int*)(base+offset); + for (i = 0; i < count; i++) + { + for (j = 0; j < e && j < sz; j++) + out[i*e+j] = in[i*sz+j]; + } + } + break; + + /*case IQM_HALF: + { + __fp16 *in = (qbyte*)(base+offset); + for (i = 0; i < count; i++) + { + for (j = 0; j < e && j < sz; j++) + out[i*e+j] = in[i*sz+j]; + } + } + break;*/ + case IQM_FLOAT: + { + float *in = (float*)(base+offset); + if (e == sz) + memcpy(out, in, e * sizeof(float) * count); + else for (i = 0; i < count; i++) + { + for (j = 0; j < e && j < sz; j++) + out[i*e+j] = in[i*sz+j]; + } + } + break; + case IQM_DOUBLE: + { + double *in = (double*)(base+offset); + for (i = 0; i < count; i++) + { + for (j = 0; j < e && j < sz; j++) + out[i*e+j] = in[i*sz+j]; + } + } + break; + } + + //if there were not enough elements, pad it. + if (sz < e) + { + for (i = 0; i < count; i++) + { + for (j = sz; j < e; j++) + out[i*e+j] = def[j]; + } + } +} + galiasinfo_t *Mod_ParseIQMMeshModel(model_t *mod, char *buffer, size_t fsize) { struct iqmheader *h = (struct iqmheader *)buffer; @@ -6177,10 +6385,13 @@ galiasinfo_t *Mod_ParseIQMMeshModel(model_t *mod, char *buffer, size_t fsize) char *strings; - float *vpos = NULL, *vtcoord = NULL, *vnorm = NULL, *vtang = NULL, *vrgbaf = NULL; - unsigned char *vbone = NULL, *vweight = NULL, *vrgbaub = NULL; + float *vtang = NULL; + struct iqmvertexarray vpos = {0}, vnorm = {0}, vtcoord = {0}, vbone = {0}, vweight = {0}, vrgba = {0}; unsigned int type, fmt, size, offset; unsigned short *framedata; + vec4_t defaultcolour = {1,1,1,1}; + vec4_t defaultweight = {0,0,0,0}; + vec4_t defaultvert = {0,0,0,1}; int memsize; qbyte *obase=NULL; @@ -6230,24 +6441,22 @@ galiasinfo_t *Mod_ParseIQMMeshModel(model_t *mod, char *buffer, size_t fsize) fmt = LittleLong(varray[i].format); size = LittleLong(varray[i].size); offset = LittleLong(varray[i].offset); - if (type == IQM_POSITION && fmt == IQM_FLOAT && size == 3) - vpos = (float*)(buffer + offset); - else if (type == IQM_TEXCOORD && fmt == IQM_FLOAT && size == 2) - vtcoord = (float*)(buffer + offset); - else if (type == IQM_NORMAL && fmt == IQM_FLOAT && size == 3) - vnorm = (float*)(buffer + offset); - else if (type == IQM_TANGENT && fmt == IQM_FLOAT && size == 4) /*yup, 4*/ + if (type == IQM_POSITION) + vpos = varray[i]; + else if (type == IQM_TEXCOORD) + vtcoord = varray[i]; + else if (type == IQM_NORMAL) + vnorm = varray[i]; + else if (type == IQM_TANGENT && fmt == IQM_FLOAT && size == 4) /*yup, 4, extra is side, for the bitangent*/ vtang = (float*)(buffer + offset); - else if (type == IQM_BLENDINDEXES && fmt == IQM_UBYTE && size == 4) - vbone = (unsigned char *)(buffer + offset); - else if (type == IQM_BLENDWEIGHTS && fmt == IQM_UBYTE && size == 4) - vweight = (unsigned char *)(buffer + offset); - else if (type == IQM_COLOR && fmt == IQM_UBYTE && size == 4) - vrgbaub = (qbyte *)(buffer + offset); - else if (type == IQM_COLOR && fmt == IQM_FLOAT && size == 4) - vrgbaf = (float *)(buffer + offset); + else if (type == IQM_BLENDINDEXES) + vbone = varray[i]; + else if (type == IQM_BLENDWEIGHTS) + vweight = varray[i]; + else if (type == IQM_COLOR) + vrgba = varray[i]; else - Con_Printf("Unrecognised iqm info\n"); + Con_Printf("Unrecognised iqm info (type=%i, fmt=%i, size=%i)\n", type, fmt, size); } if (!h->num_meshes) @@ -6260,12 +6469,12 @@ galiasinfo_t *Mod_ParseIQMMeshModel(model_t *mod, char *buffer, size_t fsize) //we also require texcoords because we can. //we don't require normals //we don't require weights, but such models won't animate. - if (h->num_vertexes > 0 && (!vpos || !vtcoord)) + if (h->num_vertexes > 0 && (!vpos.offset || !vtcoord.offset)) { Con_Printf("%s is missing vertex array data\n", mod->name); return NULL; } - noweights = !vbone || !vweight; + noweights = !vbone.offset || !vweight.offset; if (noweights) { if (h->num_frames || h->num_anims || h->num_joints) @@ -6348,11 +6557,11 @@ galiasinfo_t *Mod_ParseIQMMeshModel(model_t *mod, char *buffer, size_t fsize) oweight = NULL; } #ifndef SERVERONLY - if (vtcoord) + if (vtcoord.offset) dalloc(otcoords, h->num_vertexes); else otcoords = NULL; - if (vrgbaf || vrgbaub) + if (vrgba.offset) dalloc(orgbaf, h->num_vertexes); else orgbaf = NULL; @@ -6545,45 +6754,30 @@ galiasinfo_t *Mod_ParseIQMMeshModel(model_t *mod, char *buffer, size_t fsize) gai[i].shares_verts = i; gai[i].numverts = LittleLong(mesh[i].num_vertexes); gai[i].ofs_skel_xyz = (opos+offset); - gai[i].ofs_skel_norm = vnorm?(onorm1+offset):NULL; - gai[i].ofs_skel_svect = (vnorm&&vtang)?(onorm2+offset):NULL; - gai[i].ofs_skel_tvect = (vnorm&&vtang)?(onorm3+offset):NULL; + gai[i].ofs_skel_norm = (onorm1+offset); + gai[i].ofs_skel_svect = (onorm2+offset); + gai[i].ofs_skel_tvect = (onorm3+offset); gai[i].ofs_skel_idx = oindex?(oindex+offset):NULL; gai[i].ofs_skel_weight = oweight?(oweight+offset):NULL; } if (!noweights) { - for (i = 0; i < h->num_vertexes; i++) - { - Vector4Copy(vbone+i*4, oindex[i]); - Vector4Scale(vweight+i*4, 1/255.0, oweight[i]); - - //FIXME: should we be normalising? - if (!oweight[i][0] && !oweight[i][1] && !oweight[i][2] && !oweight[i][3]) - oweight[i][0] = 1; - } + if (!IQM_ImportArray4B(buffer, &vbone, oindex, h->num_vertexes, h->num_joints)) + Con_Printf(CON_WARNING "Invalid bone indexes detected inside %s\n", mod->name); + IQM_ImportArrayF(buffer, &vweight, (float*)oweight, 4, h->num_vertexes, defaultweight); } if (otcoords) - memcpy(otcoords, vtcoord, h->num_vertexes*sizeof(*otcoords)); + IQM_ImportArrayF(buffer, &vtcoord, (float*)otcoords, 2, h->num_vertexes, defaultweight); if (orgbaf) + IQM_ImportArrayF(buffer, &vrgba, (float*)orgbaf, 4, h->num_vertexes, defaultcolour); + + IQM_ImportArrayF(buffer, &vnorm, (float*)onorm1, 3, h->num_vertexes, defaultcolour); + IQM_ImportArrayF(buffer, &vpos, (float*)opos, sizeof(opos[0])/sizeof(float), h->num_vertexes, defaultvert); + + if (vnorm.offset && vtang) { - if (vrgbaf) - memcpy(orgbaf, vrgbaf, h->num_vertexes*sizeof(*orgbaf)); - else - { - for (i = 0; i < h->num_vertexes; i++) - Vector4Scale(vrgbaub+i*4, 1/255.0f, orgbaf[i]); - } - } - for (i = 0; i < h->num_vertexes; i++) - { - VectorCopy(vpos+i*3, opos[i]); - if (vnorm) - { - VectorCopy(vnorm+i*3, onorm1[i]); - } - if (vnorm && vtang) + for (i = 0; i < h->num_vertexes; i++) { VectorCopy(vtang+i*4, onorm2[i]); if(LittleFloat(vtang[i*4 + 3]) < 0) @@ -6592,6 +6786,18 @@ galiasinfo_t *Mod_ParseIQMMeshModel(model_t *mod, char *buffer, size_t fsize) CrossProduct(onorm1[i], onorm2[i], onorm3[i]); } } + else if (h->num_vertexes) + { //make something up + for (i = 0; i < h->num_meshes; i++) + { + Mod_AccumulateTextureVectors(gai[i].ofs_skel_xyz, gai[i].ofs_st_array, gai[i].ofs_skel_norm, gai[i].ofs_skel_svect, gai[i].ofs_skel_tvect, gai[i].ofs_indexes, gai[i].numindexes); + } + for (i = 0; i < h->num_meshes; i++) + { + Mod_NormaliseTextureVectors(gai[i].ofs_skel_norm, gai[i].ofs_skel_svect, gai[i].ofs_skel_tvect, gai[i].numverts); + } + } + return gai; }