iqmtool now supports --static. Also embeds some multi-skin info.

git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@6056 fc73d0e0-1445-4013-8a0c-d673dee63da5
This commit is contained in:
Spoike 2021-09-03 08:02:21 +00:00
parent aa651c5059
commit 051442c041
3 changed files with 235 additions and 38 deletions

View File

@ -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_idx<h->num_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

View File

@ -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<triangle> 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<mesh> meshes;
struct meshprop
@ -59,6 +60,9 @@ struct event_fte
};
vector<event_fte> events_fte;
vector<iqmext_fte_skin_meshskin> meshskins;
vector<iqmext_fte_skin_skinframe> skinframes;
struct anim { uint name; uint firstframe, numframes; float fps; uint flags; anim() : name(0), firstframe(0), numframes(0), fps(0), flags(0) {} };
vector<anim> 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<IQM_TANGENT>(etangents, IQM_TANGENT, IQM_FLOAT, 4, priorverts);
}
if(eblends.length())
if(eblends.length() && !stripbones)
{
if (ejoints.length() > 65535)
setupvertexarray<IQM_BLENDINDEXES>(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<filespec> &infiles, vector<hitbox> &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)

View File

@ -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