diff --git a/engine/common/com_mesh.c b/engine/common/com_mesh.c index d844f12a..4f2f1523 100644 --- a/engine/common/com_mesh.c +++ b/engine/common/com_mesh.c @@ -7753,6 +7753,27 @@ struct iqmext_fte_event unsigned int evcode; unsigned int evdata_str; //stringtable }; +//skin lump is made of 3 parts +struct iqmext_fte_skin +{ + unsigned int numskinframes; + unsigned int nummeshskins; + //unsigned int numskins[nummeshes]; + //iqmext_fte_skin_skinframe[numskinframes]; + //iqmext_fte_skin_meshskin mesh0[numskins[0]]; + //iqmext_fte_skin_meshskin mesh1[numskins[1]]; etc +}; +struct iqmext_fte_skin_skinframe +{ //as many as needed + unsigned int material_idx; + unsigned int shadertext_idx; +}; +struct iqmext_fte_skin_meshskin +{ + unsigned int firstframe; //index into skinframes + unsigned int countframes; //skinframes + float interval; +}; /*struct iqmext_fte_shader { unsigned int material; @@ -8093,6 +8114,10 @@ static galiasinfo_t *Mod_ParseIQMMeshModel(model_t *mod, const char *buffer, siz const struct iqmext_fte_event *fteevents; const char *strings; + const unsigned int *fteskincount; + const struct iqmext_fte_skin_meshskin *fteskins; + const struct iqmext_fte_skin_skinframe *fteskinframes; + unsigned int i, j, t, numtris, numverts, firstvert, firsttri; size_t extsize; @@ -8119,8 +8144,6 @@ static galiasinfo_t *Mod_ParseIQMMeshModel(model_t *mod, const char *buffer, siz galiasinfo_t *gai=NULL; #ifndef SERVERONLY - galiasskin_t *skin=NULL; - skinframe_t *skinframe=NULL; int skinfiles; #endif galiasanimation_t *fgroup=NULL; @@ -8302,8 +8325,6 @@ static galiasinfo_t *Mod_ParseIQMMeshModel(model_t *mod, const char *buffer, siz dalloc(orgbaf, h->num_vertexes); else orgbaf = NULL; - dalloc(skin, h->num_meshes*skinfiles); - dalloc(skinframe, h->num_meshes*skinfiles); #endif dalloc(fgroup, numgroups); dalloc(oposebase, 12*h->num_joints); @@ -8483,6 +8504,17 @@ static galiasinfo_t *Mod_ParseIQMMeshModel(model_t *mod, const char *buffer, siz if (!extsize || extsize != sizeof(*ftemesh)*h->num_meshes) ftemesh = NULL; //erk. + fteskincount = IQM_FindExtension(buffer, fsize, "FTE_SKINS", 0, &extsize); + if (extsize >= sizeof(unsigned int)*(2+h->num_meshes) && extsize == sizeof(struct iqmext_fte_skin) + sizeof(unsigned int)*h->num_meshes + LittleLong(fteskincount[0])*sizeof(*fteskinframes) + LittleLong(fteskincount[1])*sizeof(*fteskins)) + { + unsigned int numskinframes = LittleLong(*fteskincount++); + /*unsigned int numskins = LittleLong(* */fteskincount++; + + fteskinframes = (const struct iqmext_fte_skin_skinframe*)(fteskincount+h->num_meshes); + fteskins = (const struct iqmext_fte_skin_meshskin*)(fteskinframes+numskinframes); + } + else fteskincount = NULL, fteskins = NULL, fteskinframes = NULL; + for (i = 0; i < max(1, h->num_meshes); i++) { if (h->num_meshes) @@ -8519,22 +8551,55 @@ static galiasinfo_t *Mod_ParseIQMMeshModel(model_t *mod, const char *buffer, siz /*skins*/ if (h->num_meshes) { - gai[i].numskins = skinfiles; - gai[i].ofsskins = skin; + galiasskin_t *skin; + skinframe_t *skinframe; + unsigned int iqmskins = fteskincount?LittleLong(*fteskincount++):0; + gai[i].numskins = max(iqmskins,skinfiles); + gai[i].ofsskins = skin = ZG_Malloc(&mod->memgroup, sizeof(*gai[i].ofsskins)*gai[i].numskins); - for (j = 0; j < skinfiles; j++) + for (j = 0; j < gai[i].numskins; j++, skin++) { + const struct iqmext_fte_skin_skinframe *sf; + if (j < iqmskins) + { + sf = fteskinframes + LittleLong(fteskins->firstframe); + fteskins++; + } + else + sf = NULL; + skin->skinwidth = 1; skin->skinheight = 1; - skin->skinspeed = 10; /*something to avoid div by 0*/ - skin->numframes = 1; //non-sequenced skins. - skin->frame = skinframe; - Q_strncpyz(skin->name, "", sizeof(skinframe->shadername)); - skin++; + Q_snprintfz(gai[i].ofsskins[j].name, sizeof(gai[i].ofsskins[j].name), "%i", j); + if (sf) + { + skin->skinspeed = 1.0/LittleFloat(fteskins[-1].interval); /*something to avoid div by 0*/ + skin->numframes = LittleLong(fteskins[-1].countframes); //non-sequenced skins. - Q_strncpyz(skinframe->shadername, strings+mesh[i].material, sizeof(skinframe->shadername)); - skinframe++; + if (!skin->numframes) + continue; + + skin->frame = ZG_Malloc(&mod->memgroup, sizeof(*skin->frame)*skin->numframes); + for (t = 0; t < skin->numframes; t++, sf++) + { + Q_strncpyz(skin->frame[t].shadername, strings+sf->material_idx, sizeof(skin->frame[t].shadername)); + if (sf->shadertext_idx && sf->shadertext_idxnum_text) + { + const char *stxt = strings+sf->shadertext_idx; + skin->frame[t].defaultshader = strcpy(ZG_Malloc(&mod->memgroup, strlen(stxt)+1), stxt); + } + } + } + else + { + skin->skinspeed = 10; //something to avoid div by 0 + skin->numframes = 1; //non-sequenced skins. + + skin->frame = skinframe = ZG_Malloc(&mod->memgroup, sizeof(*skin->frame)*skin->numframes); + Q_strncpyz(skinframe->shadername, strings+mesh[i].material, sizeof(skinframe->shadername)); + } } + gai[i].numskins = skin-gai[i].ofsskins; } #endif diff --git a/iqm/iqm.cpp b/iqm/iqm.cpp index 4ae56ec7..b05a3445 100644 --- a/iqm/iqm.cpp +++ b/iqm/iqm.cpp @@ -17,6 +17,7 @@ bool noext = false; bool verbose = false; bool quiet = false; +bool stripbones = false; //strip all bone+anim info. struct ejoint { @@ -32,7 +33,7 @@ struct ejoint struct triangle { uint vert[3]; triangle() {} triangle(uint v0, uint v1, uint v2) { vert[0] = v0; vert[1] = v1; vert[2] = v2; } }; vector triangles, neighbors; -struct mesh { uint name, material; uint firstvert, numverts; uint firsttri, numtris; mesh() : name(0), material(0), firstvert(0), numverts(0), firsttri(0), numtris(0) {} }; +struct mesh { uint name, material, numskins; uint firstvert, numverts; uint firsttri, numtris; mesh() : name(0), material(0), firstvert(0), numverts(0), firsttri(0), numtris(0) {} }; vector meshes; struct meshprop @@ -59,6 +60,9 @@ struct event_fte }; vector events_fte; +vector meshskins; +vector skinframes; + struct anim { uint name; uint firstframe, numframes; float fps; uint flags; anim() : name(0), firstframe(0), numframes(0), fps(0), flags(0) {} }; vector anims; @@ -1127,8 +1131,8 @@ void printlastmesh(void) return; mesh &m = meshes[meshes.length()-1]; meshprop &fm = meshes_fte[meshes.length()-1]; - printf(" %smesh %i:\tname=\"%s\",\tmat=\"%s\",\ttri=%i, vert=%i\n", fm.contents?"c":"r", meshes.length()-1, - &stringdata[m.name], &stringdata[m.material], m.numtris, m.numverts); + printf(" %smesh %i:\tname=\"%s\",\tmat=\"%s\",\ttri=%i, vert=%i, skins=%i\n", fm.contents?"c":"r", meshes.length()-1, + &stringdata[m.name], &stringdata[m.material], m.numtris, m.numverts, m.numskins); if (verbose) { @@ -1232,6 +1236,41 @@ void makemeshes(const filespec &spec) maketriangles(tinfo, mmap); m.numtris = triangles.length() - m.firsttri; m.numverts = numfverts+vmap.length() - m.firstvert; + m.numskins = 0; //we have a default material, so no worries. + + if (spec.materialsuffix && !strncmp(spec.materialsuffix, "_00_00", 6)) + { //for qex... populate our skins info + for (int s = 0; ; s++) + { + char matname[512]; + formatstring(matname, "%s%s_%02d_%02d%s", spec.materialprefix?spec.materialprefix:"", em1.material, s, 0, spec.materialsuffix+6); + FILE *f = fopen(matname, "rb"); + if (f) + { + fclose(f); //don't really care. + auto &skin = meshskins.add(); + m.numskins++; + skin.firstframe = skinframes.length(); + skin.countframes = 1; + skin.interval = 0.2; + + auto &skinframe = skinframes.add(); + skinframe.material_idx = sharestring(matname); + skinframe.shadertext_idx = 0; + for (skin.countframes = 1; ; skin.countframes++) + { + formatstring(matname, "%s%s_%02d_%02d%s", spec.materialprefix?spec.materialprefix:"", em1.material, s, skin.countframes, spec.materialsuffix+6); + f = fopen(matname, "rb"); + if (!f) + break; + fclose(f); + auto &skinframe = skinframes.add(); + skinframe.material_idx = sharestring(matname); + } + } + else break; + } + } meshprop &mf = meshes_fte.add(); mf = em1.explicits; @@ -1272,7 +1311,7 @@ void makemeshes(const filespec &spec) calctangents(priortris); setupvertexarray(etangents, IQM_TANGENT, IQM_FLOAT, 4, priorverts); } - if(eblends.length()) + if(eblends.length() && !stripbones) { if (ejoints.length() > 65535) setupvertexarray(eblends, IQM_BLENDINDEXES, IQM_UINT, 4, priorverts); @@ -4718,6 +4757,17 @@ bool writeiqm(const char *filename) } } + iqmextension *ext_skins_fte = NULL; + if (meshskins.length()) + { + ext_skins_fte = &extensions.add(); + ext_skins_fte->name = sharestring("FTE_SKINS"); + ext_skins_fte->num_data = sizeof(iqmext_fte_skin); + ext_skins_fte->num_data += meshes.length()*sizeof(uint); + ext_skins_fte->num_data += skinframes.length()*sizeof(iqmext_fte_skin_skinframe); + ext_skins_fte->num_data += meshskins.length()*sizeof(iqmext_fte_skin_meshskin); + } + if(stringdata.length()) hdr.ofs_text = hdr.filesize, hdr.num_text = stringdata.length(), hdr.filesize += hdr.num_text; hdr.num_meshes = meshes.length(); if(meshes.length()) hdr.ofs_meshes = hdr.filesize; hdr.filesize += meshes.length() * sizeof(iqmmesh); uint voffset = hdr.filesize + varrays.length() * sizeof(iqmvertexarray); @@ -4738,6 +4788,7 @@ bool writeiqm(const char *filename) if (extensions.length()) hdr.ofs_extensions = hdr.filesize, hdr.num_extensions = extensions.length(), hdr.filesize += sizeof(iqmextension) * hdr.num_extensions; if (ext_meshes_fte) ext_meshes_fte->ofs_data = hdr.filesize, ext_meshes_fte->num_data = meshes_fte.length()*sizeof(iqmext_fte_mesh), hdr.filesize += ext_meshes_fte->num_data; if (ext_events_fte) ext_events_fte->ofs_data = hdr.filesize, ext_events_fte->num_data = events_fte.length()*sizeof(iqmext_fte_events), hdr.filesize += ext_events_fte->num_data; + if (ext_skins_fte) ext_skins_fte->ofs_data = hdr.filesize, hdr.filesize += ext_skins_fte->num_data; lilswap(&hdr.version, (sizeof(hdr) - sizeof(hdr.magic))/sizeof(uint)); @@ -4856,6 +4907,24 @@ bool writeiqm(const char *filename) f->putlil(ev.evdata_idx); } + if (ext_skins_fte) + { + f->putlil(skinframes.length()); + f->putlil(meshskins.length()); + loopv(meshes) f->putlil(meshes[i].numskins); + loopv(skinframes) + { + f->putlil(skinframes[i].material_idx); + f->putlil(skinframes[i].shadertext_idx); + } + loopv(meshskins) + { + f->putlil(meshskins[i].firstframe); + f->putlil(meshskins[i].countframes); + f->putlil(meshskins[i].interval); + } + } + delete f; return true; } @@ -5366,7 +5435,7 @@ static void help(bool exitstatus, bool fullhelp) "./iqmtool [options] output.iqm mesh.fbx anim1.fbx ... animN.fbx\n" "./iqmtool [options] output.iqm mesh.obj\n" "./iqmtool [options] output.iqm source.gltf\n" -"./iqmtool [options] output.iqm --kex sources.md5\n" +"./iqmtool [options] output.iqm --qex sources.md5\n" "\n" "Basic commandline options:\n" " --help Show full help.\n" @@ -5392,7 +5461,7 @@ static void help(bool exitstatus, bool fullhelp) " -j\n" " --forcejoints Forces the exporting of joint information in animation\n" " files without meshes.\n" -" --kex Applies a set of fixups to work around the quirks in\n" +" --qex Applies a set of fixups to work around the quirks in\n" " the quake rerelease's md5 files.\n" "\n" "Legacy commandline options that affect the following animation file:\n" @@ -5516,7 +5585,6 @@ unsigned int parsebits(bitnames *names, char **line) comma = strchr(value, ','); if (comma) *comma++ = 0; - char *end; strtoul(value, &end, 0); if (end && !*end) @@ -5535,7 +5603,7 @@ unsigned int parsebits(bitnames *names, char **line) } for (i = 0; names[i].name; i++) { - if (!*std || !strcasecmp(names[i].std, std)) + if (!strcasecmp(names[i].std, std)) { if (!strcasecmp(names[i].name, value)) { @@ -5546,7 +5614,7 @@ unsigned int parsebits(bitnames *names, char **line) } if (!names[i].name) { //stuff with no specific standard, mostly for consistency - if (!*names[i].std) + for (i = 0; names[i].name; i++) { if (!strcasecmp(names[i].name, value)) { @@ -5693,7 +5761,7 @@ bool parseanimfield(const char *tok, char **line, filespec &spec, bool defaults) return true; } -struct +static struct { const char *extname; bool (*write)(const char *filename); @@ -5708,6 +5776,45 @@ struct {".md3", writemd3, "output_md3"}, }; +static bitnames modelflagnames[] = { + {"q1", "rocket", 1u<<0}, + {"q1", "grenade", 1u<<1}, + {"q1", "gib", 1u<<2}, + {"q1", "rotate", 1u<<3}, + {"q1", "tracer1", 1u<<4}, + {"q1", "zomgib", 1u<<5}, + {"q1", "tracer2", 1u<<6}, + {"q1", "tracer3", 1u<<7}, + {"q1", "holey", 1u<<14}, //common extension + + {"h2", "spidergib", 1u<<0}, //conflicts with q1. + {"h2", "grenade", 1u<<1}, + {"h2", "gib", 1u<<2}, + {"h2", "rotate", 1u<<3}, + {"h2", "tracer1", 1u<<4}, + {"h2", "zomgib", 1u<<5}, + {"h2", "tracer2", 1u<<6}, + {"h2", "tracer3", 1u<<7}, + {"h2", "fireball", 1u<<8}, + {"h2", "ice", 1u<<9}, + {"h2", "mipmap", 1u<<10}, + {"h2", "spit", 1u<<11}, + {"h2", "transparent", 1u<<12}, + {"h2", "spell", 1u<<13}, + {"h2", "holey", 1u<<14}, + {"h2", "specialtrans", 1u<<15}, + {"h2", "faceview", 1u<<16}, + {"h2", "vorpmissile", 1u<<17}, + {"h2", "setstaff", 1u<<18}, + {"h2", "magicmissle", 1u<<19}, + {"h2", "boneshard", 1u<<20}, + {"h2", "scarab", 1u<<21}, + {"h2", "acidball", 1u<<22}, + {"h2", "bloodshot", 1u<<23}, + + {NULL} +}; + void parsecommands(char *filename, const char *outfiles[countof(outputtypes)], vector &infiles, vector &hitboxes) { filespec defaultspec; @@ -5753,20 +5860,9 @@ void parsecommands(char *filename, const char *outfiles[countof(outputtypes)], v else if (!strcasecmp(tok, "exec")) parsecommands(mystrtok(&line), outfiles, infiles, hitboxes); else if (!strcasecmp(tok, "modelflags")) - { - bitnames modelflagnames[] = { - {"q1", "rocket", 0x01}, - {"q1", "grenade", 0x02}, - {"q1", "gib", 0x04}, - {"q1", "rotate", 0x08}, - {"q1", "tracer1", 0x10}, - {"q1", "zomgib", 0x20}, - {"q1", "tracer2", 0x40}, - {"q1", "tracer3", 0x80}, - {NULL} - }; modelflags = parsebits(modelflagnames, &line); - } + else if (!strcasecmp(tok, "static")) + stripbones = true; else if (parseanimfield(tok, &line, defaultspec, true)) ; @@ -5905,7 +6001,9 @@ int main(int argc, char **argv) else if(!strcasecmp(&argv[i][2], "forcejoints")) forcejoints = true; else if(!strcasecmp(&argv[i][2], "materialprefix")) { if(i + 1 < argc) inspec.materialprefix = argv[++i]; } else if(!strcasecmp(&argv[i][2], "materialsuffix")) { if(i + 1 < argc) inspec.materialsuffix = argv[++i]; } - else if(!strcasecmp(&argv[i][2], "kex")) inspec.materialprefix = "progs/", inspec.materialsuffix = "_00_00.lmp", inspec.flags |= IQM_UNPACK; + else if(!strcasecmp(&argv[i][2], "qex")) inspec.materialprefix = "progs/", inspec.materialsuffix = "_00_00.lmp", inspec.flags |= IQM_UNPACK; + else if(!strcasecmp(&argv[i][2], "modelflags")) { if(i + 1 < argc) { modelflags |= parsebits(modelflagnames, &argv[++i]); }} + else if(!strcasecmp(&argv[i][2], "static")) stripbones = true; else if(!strcasecmp(&argv[i][2], "meshtrans")) { if(i + 1 < argc) switch(sscanf(argv[++i], "%lf , %lf , %lf", &gmeshtrans.x, &gmeshtrans.y, &gmeshtrans.z)) @@ -6050,6 +6148,17 @@ int main(int argc, char **argv) conoutf("warning: mesh \"%s\" overriden, but not present", meshoverrides[i].name); + if (stripbones) + { + if (!quiet) + conoutf("static bones"); + joints.setsize(0); + poses.setsize(0); + frames.setsize(0); + anims.setsize(0); + bounds.setsize(0); + } + calcanimdata(); if (!quiet) diff --git a/iqm/iqm.h b/iqm/iqm.h index a9258135..922e1ede 100644 --- a/iqm/iqm.h +++ b/iqm/iqm.h @@ -147,5 +147,28 @@ struct iqmext_fte_events unsigned int evcode; unsigned int evdata_str; }; + + +//skin lump is made of 3 parts +struct iqmext_fte_skin +{ + unsigned int nummeshskins; + unsigned int numskinframes; + //unsigned int numskins[nummeshes]; + //iqmext_fte_skin_skinframe[numskinframes]; + //iqmext_fte_skin_meshskin mesh0[numskins[0]]; + //iqmext_fte_skin_meshskin mesh1[numskins[1]]; etc +}; +struct iqmext_fte_skin_skinframe +{ //as many as needed + unsigned int material_idx; + unsigned int shadertext_idx; +}; +struct iqmext_fte_skin_meshskin +{ + unsigned int firstframe; //index into skinframes + unsigned int countframes; //skinframes + float interval; +}; #endif