Improve gltf->iqm frame timings, update iqmtool's help text.
git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@5905 fc73d0e0-1445-4013-8a0c-d673dee63da5
This commit is contained in:
parent
26d9d89f24
commit
ac427ff76c
3 changed files with 196 additions and 87 deletions
|
@ -2037,7 +2037,7 @@ m-dbg:
|
||||||
m-profile:
|
m-profile:
|
||||||
@$(MAKE) m-tmp TYPE=_clsv-profile OUT_DIR="$(PROFILE_DIR)/$(NCDIRPREFIX)$(MB_DIR)"
|
@$(MAKE) m-tmp TYPE=_clsv-profile OUT_DIR="$(PROFILE_DIR)/$(NCDIRPREFIX)$(MB_DIR)"
|
||||||
|
|
||||||
.PHONY: m-tmp mcl-tmp mingl-tmp glcl-tmp gl-tmp sv-tmp _clsv-dbg _clsv-rel _cl-dbg _cl-rel _out-rel _out-dbg reldir debugdir makelibs wel-rel web-dbg httpserver iqm imgtool
|
.PHONY: m-tmp mcl-tmp mingl-tmp glcl-tmp gl-tmp sv-tmp _clsv-dbg _clsv-rel _cl-dbg _cl-rel _out-rel _out-dbg reldir debugdir makelibs wel-rel web-dbg httpserver iqmtool imgtool
|
||||||
|
|
||||||
|
|
||||||
_qcc-tmp: $(REQDIR)
|
_qcc-tmp: $(REQDIR)
|
||||||
|
@ -2381,15 +2381,16 @@ $(RELEASE_DIR)/httpserver$(BITS)$(EXEPOSTFIX): $(HTTP_OBJECTS)
|
||||||
httpserver: $(RELEASE_DIR)/httpserver$(BITS)$(EXEPOSTFIX)
|
httpserver: $(RELEASE_DIR)/httpserver$(BITS)$(EXEPOSTFIX)
|
||||||
|
|
||||||
IQM_OBJECTS=../iqm/iqm.cpp ../imgtool.c client/image.c ../plugins/models/gltf.c
|
IQM_OBJECTS=../iqm/iqm.cpp ../imgtool.c client/image.c ../plugins/models/gltf.c
|
||||||
$(RELEASE_DIR)/iqm$(BITS)$(EXEPOSTFIX): $(IQM_OBJECTS)
|
$(RELEASE_DIR)/iqmtool$(BITS)$(EXEPOSTFIX): $(IQM_OBJECTS)
|
||||||
ifdef windir
|
ifdef windir
|
||||||
$(CC) -o $@ $(IQM_OBJECTS) -Icommon -Iclient -Iqclib -Igl -Iserver $(ALL_CFLAGS) $(CLIENTLIBFLAGS) -DIQMTOOL $(BASELDFLAGS) $(CLIENTLDDEPS) -static -lstdc++ -lm -Os
|
$(CC) -o $@ $(IQM_OBJECTS) -Icommon -Iclient -Iqclib -Igl -Iserver $(ALL_CFLAGS) $(CLIENTLIBFLAGS) -DIQMTOOL $(BASELDFLAGS) $(CLIENTLDDEPS) --static -static-libgcc -static-libstdc++ -lstdc++ -lm -Os
|
||||||
else
|
else
|
||||||
$(CC) -o $@ $(IQM_OBJECTS) -Icommon -Iclient -Iqclib -Igl -Iserver $(ALL_CFLAGS) $(CLIENTLIBFLAGS) -DIQMTOOL $(BASELDFLAGS) $(CLIENTLDDEPS) -lstdc++ -lm -Os
|
$(CC) -o $@ $(IQM_OBJECTS) -Icommon -Iclient -Iqclib -Igl -Iserver $(ALL_CFLAGS) $(CLIENTLIBFLAGS) -DIQMTOOL $(BASELDFLAGS) $(CLIENTLDDEPS) --static -static-libgcc -static-libstdc++ -lstdc++ -lm -Os
|
||||||
endif
|
endif
|
||||||
iqm-rel: $(RELEASE_DIR)/iqm$(BITS)$(EXEPOSTFIX)
|
iqm-rel: $(RELEASE_DIR)/iqmtool$(BITS)$(EXEPOSTFIX)
|
||||||
|
iqmtool-rel: $(RELEASE_DIR)/iqmtool$(BITS)$(EXEPOSTFIX)
|
||||||
iqm: iqm-rel
|
iqm: iqm-rel
|
||||||
iqmtool: iqm-rel
|
iqmtool: iqmtool-rel
|
||||||
|
|
||||||
IMGTOOL_OBJECTS=../imgtool.c client/image.c
|
IMGTOOL_OBJECTS=../imgtool.c client/image.c
|
||||||
$(RELEASE_DIR)/imgtool$(BITS)$(EXEPOSTFIX): $(IMGTOOL_OBJECTS)
|
$(RELEASE_DIR)/imgtool$(BITS)$(EXEPOSTFIX): $(IMGTOOL_OBJECTS)
|
||||||
|
|
182
iqm/iqm.cpp
182
iqm/iqm.cpp
|
@ -1398,6 +1398,9 @@ void printbones(int parent = -1, size_t ind = 1)
|
||||||
{
|
{
|
||||||
if (joints[i].parent == parent)
|
if (joints[i].parent == parent)
|
||||||
{ //show as 1-based for consistency with quake.
|
{ //show as 1-based for consistency with quake.
|
||||||
|
if (parent == -1)
|
||||||
|
conoutf("%sbone %i:\tname=\"%s\"\tparent=NONE, group=%i (%f %f %f)", prefix, i+1, &stringdata[joints[i].name], joints[i].group, joints[i].pos[0], joints[i].pos[1], joints[i].pos[2]);
|
||||||
|
else
|
||||||
conoutf("%sbone %i:\tname=\"%s\"\tparent=%i, group=%i (%f %f %f)", prefix, i+1, &stringdata[joints[i].name], joints[i].parent+1, joints[i].group, joints[i].pos[0], joints[i].pos[1], joints[i].pos[2]);
|
conoutf("%sbone %i:\tname=\"%s\"\tparent=%i, group=%i (%f %f %f)", prefix, i+1, &stringdata[joints[i].name], joints[i].parent+1, joints[i].group, joints[i].pos[0], joints[i].pos[1], joints[i].pos[2]);
|
||||||
printbones(i, ind+1);
|
printbones(i, ind+1);
|
||||||
}
|
}
|
||||||
|
@ -1528,7 +1531,7 @@ void makeanims(const filespec &spec)
|
||||||
{
|
{
|
||||||
anim &a = anims.add();
|
anim &a = anims.add();
|
||||||
char nname[256];
|
char nname[256];
|
||||||
formatstring(nname, "%s%i", ea.name, j+1-ea.startframe);
|
formatstring(nname, "%s_%i", ea.name, j+1-ea.startframe);
|
||||||
a.name = sharestring(nname);
|
a.name = sharestring(nname);
|
||||||
a.firstframe = frames.length();
|
a.firstframe = frames.length();
|
||||||
a.numframes = 0;
|
a.numframes = 0;
|
||||||
|
@ -1538,8 +1541,8 @@ void makeanims(const filespec &spec)
|
||||||
int offset = eframes[j], range = (eframes.inrange(j+1) ? eframes[j+1] : eposes.length()) - offset;
|
int offset = eframes[j], range = (eframes.inrange(j+1) ? eframes[j+1] : eposes.length()) - offset;
|
||||||
if(range <= 0) continue;
|
if(range <= 0) continue;
|
||||||
frame &fr = frames.add();
|
frame &fr = frames.add();
|
||||||
loopk(min(range, ejoints.length())) fr.pose.add(frame::framepose(ejoints[i], eposes[offset + k]));
|
loopk(min(range, ejoints.length())) fr.pose.add(frame::framepose(ejoints[k], eposes[offset + k]));
|
||||||
loopk(max(ejoints.length() - range, 0)) fr.pose.add(frame::framepose(ejoints[i], transform(Vec3(0, 0, 0), Quat(0, 0, 0, 1), Vec3(1, 1, 1))));
|
loopk(max(ejoints.length() - range, 0)) fr.pose.add(frame::framepose(ejoints[k], transform(Vec3(0, 0, 0), Quat(0, 0, 0, 1), Vec3(1, 1, 1))));
|
||||||
a.numframes++;
|
a.numframes++;
|
||||||
|
|
||||||
printlastanim();
|
printlastanim();
|
||||||
|
@ -3874,11 +3877,29 @@ bool loadfbx(const char *filename, const filespec &spec)
|
||||||
|
|
||||||
namespace fte
|
namespace fte
|
||||||
{
|
{
|
||||||
|
static vector<cvar_t*> cvars;
|
||||||
static plugfsfuncs_t cppfsfuncs;
|
static plugfsfuncs_t cppfsfuncs;
|
||||||
static plugmodfuncs_t cppmodfuncs;
|
static plugmodfuncs_t cppmodfuncs;
|
||||||
static plugcorefuncs_t cppplugfuncs;
|
static plugcorefuncs_t cppplugfuncs;
|
||||||
static plugcvarfuncs_t cppcvarfuncs;
|
static plugcvarfuncs_t cppcvarfuncs;
|
||||||
|
|
||||||
|
static cvar_t *Cvar_Create(const char *name, const char *defaultval, unsigned int flags, const char *description, const char *groupname)
|
||||||
|
{ //could maybe fill with environment settings perhaps? yuck.
|
||||||
|
cvar_t *v = NULL;
|
||||||
|
for (int i = 0; i < cvars.length(); i++)
|
||||||
|
if (!strcmp(cvars[i]->name, name))
|
||||||
|
return cvars[i];
|
||||||
|
if (!v)
|
||||||
|
{
|
||||||
|
v = cvars.add() = new cvar_t();
|
||||||
|
v->name = strdup(name);
|
||||||
|
v->string = strdup(defaultval);
|
||||||
|
v->value = atof(v->string);
|
||||||
|
v->ival = atoi(v->string);
|
||||||
|
}
|
||||||
|
return v;
|
||||||
|
};
|
||||||
|
|
||||||
static void SetupFTEPluginFuncs(void)
|
static void SetupFTEPluginFuncs(void)
|
||||||
{
|
{
|
||||||
cppfsfuncs.OpenVFS = [](const char *filename, const char *mode, enum fs_relative relativeto)
|
cppfsfuncs.OpenVFS = [](const char *filename, const char *mode, enum fs_relative relativeto)
|
||||||
|
@ -4019,15 +4040,7 @@ namespace fte
|
||||||
return ret;
|
return ret;
|
||||||
};
|
};
|
||||||
|
|
||||||
cppcvarfuncs.GetNVFDG = [](const char *name, const char *defaultval, unsigned int flags, const char *description, const char *groupname)
|
cppcvarfuncs.GetNVFDG = Cvar_Create;
|
||||||
{ //could maybe fill with environment settings perhaps? yuck.
|
|
||||||
auto v = new cvar_t();
|
|
||||||
v->name = strdup(name);
|
|
||||||
v->string = strdup(defaultval);
|
|
||||||
v->value = atof(v->string);
|
|
||||||
v->ival = atoi(v->string);
|
|
||||||
return v;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
extern "C"
|
extern "C"
|
||||||
{ //our plugin-style stuff has a few external dependancies not provided via pointers...
|
{ //our plugin-style stuff has a few external dependancies not provided via pointers...
|
||||||
|
@ -4164,6 +4177,7 @@ namespace fte
|
||||||
a.startframe = firstframe;
|
a.startframe = firstframe;
|
||||||
a.fps = anim.rate;
|
a.fps = anim.rate;
|
||||||
a.flags = anim.loop?IQM_LOOP:0;
|
a.flags = anim.loop?IQM_LOOP:0;
|
||||||
|
a.flags |= spec.flags;
|
||||||
a.endframe = eframes.length();
|
a.endframe = eframes.length();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -4232,6 +4246,9 @@ namespace fte
|
||||||
else
|
else
|
||||||
materialname = surf->surfacename;
|
materialname = surf->surfacename;
|
||||||
|
|
||||||
|
if (surf->nummorphs)
|
||||||
|
printf("Morph targets on input surface \"%s\" \"%s\" cannot be supported\n", surf->surfacename, materialname);
|
||||||
|
|
||||||
emesh mesh(surf->surfacename, materialname, etriangles.length());
|
emesh mesh(surf->surfacename, materialname, etriangles.length());
|
||||||
|
|
||||||
//add in some extra surface properties.
|
//add in some extra surface properties.
|
||||||
|
@ -5313,69 +5330,113 @@ static bool writemd3(const char *filename)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void help(bool exitstatus = EXIT_SUCCESS)
|
static void help(bool exitstatus, bool fullhelp)
|
||||||
{
|
{
|
||||||
fprintf(exitstatus != EXIT_SUCCESS ? stderr : stdout,
|
fprintf(exitstatus != EXIT_SUCCESS ? stderr : stdout,
|
||||||
"-- FTE's Fork of Lee Salzman's iqm exporter --\n"
|
"-- FTE's Fork of Lee Salzman's iqm exporter --\n"
|
||||||
"Usage:\n"
|
"Usage:\n"
|
||||||
"\n"
|
"\n"
|
||||||
"./iqm cmdfile.cmd\n"
|
"./iqmtool cmdfile.cmd\n"
|
||||||
"./iqm [options] output.iqm mesh.iqe anim1.iqe ... animN.iqe\n"
|
"./iqmtool [options] output.iqm mesh.iqe anim1.iqe ... animN.iqe\n"
|
||||||
"./iqm [options] output.iqm mesh.md5mesh anim1.md5anim ... animN.md5anim\n"
|
"./iqmtool [options] output.iqm mesh.md5mesh anim1.md5anim ... animN.md5anim\n"
|
||||||
"./iqm [options] output.iqm mesh.smd anim1.smd ... animN.smd\n"
|
"./iqmtool [options] output.iqm mesh.smd anim1.smd ... animN.smd\n"
|
||||||
"./iqm [options] output.iqm mesh.fbx anim1.fbx ... animN.fbx\n"
|
"./iqmtool [options] output.iqm mesh.fbx anim1.fbx ... animN.fbx\n"
|
||||||
"./iqm [options] output.iqm mesh.obj\n"
|
"./iqmtool [options] output.iqm mesh.obj\n"
|
||||||
|
"./iqmtool [options] output.iqm source.gltf\n"
|
||||||
|
"\n"
|
||||||
|
"Basic commandline options:\n"
|
||||||
|
" --help Show full help.\n"
|
||||||
|
" -v Verbose\n"
|
||||||
|
" -q Quiet operation\n"
|
||||||
|
" -n Disable output of fte's iqm extensions\n"
|
||||||
|
);
|
||||||
|
|
||||||
|
if (fullhelp)
|
||||||
|
fprintf(exitstatus != EXIT_SUCCESS ? stderr : stdout,
|
||||||
"\n"
|
"\n"
|
||||||
"For certain formats, IQE, OBJ, and FBX, it is possible to combine multiple mesh\n"
|
"For certain formats, IQE, OBJ, and FBX, it is possible to combine multiple mesh\n"
|
||||||
"files of the exact same vertex layout and skeleton by supplying them as\n"
|
"files of the exact same vertex layout and skeleton by supplying them as\n"
|
||||||
"\"mesh1.iqe,mesh2.iqe,mesh3.iqe\", that is, a comma-separated list of the mesh\n"
|
"\"mesh1.iqe,mesh2.iqe,mesh3.iqe\", that is, a comma-separated list of the mesh\n"
|
||||||
"files (with no spaces) in place of the usual mesh filename.\n"
|
"files (with no spaces) in place of the usual mesh filename.\n"
|
||||||
"\n"
|
"\n"
|
||||||
"Options can be any of the following command-line switches:\n"
|
"Legacy commandline options that affect mesh import for the next file:\n"
|
||||||
"\n"
|
|
||||||
" -s N\n"
|
|
||||||
" --scale N\n"
|
|
||||||
" Sets the output scale to N (float).\n"
|
|
||||||
"\n"
|
"\n"
|
||||||
|
" -s N Sets the output scale to N (float).\n"
|
||||||
" --meshtrans Z\n"
|
" --meshtrans Z\n"
|
||||||
" --meshtrans X,Y,Z\n"
|
" --meshtrans X,Y,Z Translates a mesh by X,Y,Z (floats).\n"
|
||||||
" Translates a mesh by X,Y,Z (floats). This does not affect the skeleton.\n"
|
" This does not affect the skeleton.\n"
|
||||||
"\n"
|
|
||||||
" -j\n"
|
" -j\n"
|
||||||
" --forcejoints\n"
|
" --forcejoints Forces the exporting of joint information in animation"
|
||||||
" Forces the exporting of joint information in animation files without\n"
|
" files without meshes.\n"
|
||||||
" meshes.\n"
|
|
||||||
"\n"
|
"\n"
|
||||||
" -q\n"
|
"Legacy commandline options that affect the following animation file:\n"
|
||||||
" Quiet. Only display warnings or errors.\n"
|
|
||||||
"\n"
|
"\n"
|
||||||
" -v\n"
|
" --name A Sets the name of the animation to A.\n"
|
||||||
" Verbose. Print lots of extra info.\n"
|
" --fps N Sets the FPS of the animation to N (float).\n"
|
||||||
"\n"
|
" --loop Sets the loop flag for the animation.\n"
|
||||||
" -n\n"
|
" --start N Sets the first frame of the animation to N (integer).\n"
|
||||||
" No Extensions. Disables the use of fte-specific iqm extensions.\n"
|
" --end N Sets the last frame of the animation to N (integer).\n"
|
||||||
"\n"
|
" --zup Source model is in quake's orientation.\n"
|
||||||
"Each animation file can be preceded by any combination of the following command-\n"
|
|
||||||
"line switches:\n"
|
|
||||||
"\n"
|
|
||||||
" --name A\n"
|
|
||||||
" Sets the name of the animation to A.\n"
|
|
||||||
" --fps N\n"
|
|
||||||
" Sets the FPS of the animation to N (float).\n"
|
|
||||||
" --loop\n"
|
|
||||||
" Sets the loop flag for the animation.\n"
|
|
||||||
" --start N\n"
|
|
||||||
" Sets the first frame of the animation to N (integer).\n"
|
|
||||||
" --end N\n"
|
|
||||||
" Sets the last frame of the animation to N (integer).\n"
|
|
||||||
" --zup\n"
|
|
||||||
" Source model is in quake's orientation.\n"
|
|
||||||
"\n"
|
"\n"
|
||||||
"You can supply either a mesh file, animation files, or both.\n"
|
"You can supply either a mesh file, animation files, or both.\n"
|
||||||
"Note that if an input mesh file is supplied, it must come before the animation\n"
|
"Note that if an input mesh file is supplied, it must come before the animation\n"
|
||||||
"files in the file list.\n"
|
"files in the file list.\n"
|
||||||
"The output IQM file will contain the supplied mesh and any supplied animations.\n"
|
"The output IQM file will contain the supplied mesh and any supplied animations.\n"
|
||||||
"If no mesh is provided,the IQM file will simply contain the supplied animations.\n"
|
"If no mesh is provided,the IQM file will simply contain the supplied animations\n"
|
||||||
|
"\n"
|
||||||
|
"Command script commands:\n"
|
||||||
|
" hitbox BODYNUM BONENAME x y z x y z\n"
|
||||||
|
" Attaches a hitbox surface around the specified bone\n"
|
||||||
|
" (in base pose). Gamecode knows which part of the model\n"
|
||||||
|
" was hit via the body value.\n"
|
||||||
|
" exec FILENAME Reads additional commands from the specified file.\n"
|
||||||
|
" Handy for shared animations.\n"
|
||||||
|
" modelflags FLAGS Specifies the model's flags, mostly for trail effects.\n"
|
||||||
|
" Known values are rocket, grenade, gib, rotate, tracer1,\n"
|
||||||
|
" zomgib, tracer2, tracer3, or 0xXXXXXXXX\n"
|
||||||
|
" mesh NAME MESHPROPS Overrides properties for a specific source surface.\n"
|
||||||
|
" bone NAME BONEPROPS Overrides properties for a specific bone.\n"
|
||||||
|
" ANIMPROPS Provides default values for following imported files.\n"
|
||||||
|
" import FILENAME [PROPS]\n"
|
||||||
|
" Loads the specified file, with per-file properties.\n"
|
||||||
|
" output FILENAME Specifies the iqm filename to write.\n"
|
||||||
|
" output_mdl FILENAME Specifies the (q1) mdl filename to write.\n"
|
||||||
|
"\n"
|
||||||
|
"Bone Properties:\n"
|
||||||
|
" rename NEWNAME Renames the bone accordingly.\n"
|
||||||
|
" group GROUPID Inherited from parents this allows resorting bones.\n"
|
||||||
|
"\n"
|
||||||
|
"Mesh Properties:\n"
|
||||||
|
" contents a[,b,c] Override surface content values for collisons\n"
|
||||||
|
" Accepted values are empty, solid, lava, slime, water,\n"
|
||||||
|
" fluid, fte_ladder, playerclip, monsterclip, body,\n"
|
||||||
|
" corpse, q2_ladder, fte_sky, q3_nodrop,\n"
|
||||||
|
" or 0xXXXXXXXX for custom values.\n"
|
||||||
|
" surfaceflags a[,b] Specifies explicit surface flags values.\n"
|
||||||
|
" Accepted values are nodraw, or custom 0xXXXXXXXX values.\n"
|
||||||
|
" body BODYNUM Specifies mod-specific body numbers.\n"
|
||||||
|
" geomset GROUP IDX Controls which geomset this surface is part of\n"
|
||||||
|
" lodrange min max Specifies a distance range within which to draw this lod\n"
|
||||||
|
"\n"
|
||||||
|
"Anim Properties:\n"
|
||||||
|
" name NAME Defines the output's name for this animation.\n"
|
||||||
|
" fps RATE Controls the playback rate.\n"
|
||||||
|
" loop Forces the animation(s) to loop.\n"
|
||||||
|
" clamp Forces the animation(s) to not loop.\n"
|
||||||
|
" unpack Extract each pose into its own single-pose 'animation'.\n"
|
||||||
|
" pack Undoes the effect of 'unpack'.\n"
|
||||||
|
" nomesh 1 Skips importing of meshes, getting animations only.\n"
|
||||||
|
" noanim 1 Skips importing of animations, for mesh import only.\n"
|
||||||
|
" materialprefix PRE Prefixes material names with the specified string.\n"
|
||||||
|
" ignoresurfname 1 Ignores source surface names.\n"
|
||||||
|
" start FRAME The Imported animation starts on this frame...\n"
|
||||||
|
" end FRAME ... and ends on this one.\n"
|
||||||
|
" rotate X Y Z Rotates the input model by the specified angles.\n"
|
||||||
|
" scale FACTOR Scales the input model by some value\n"
|
||||||
|
" origin X Y Z Translates the input model.\n"
|
||||||
|
" event reset Discard previously specified events\n"
|
||||||
|
" event [ANIMIDX:]TIME CODE \"VALUE\"\n"
|
||||||
|
" Inserts a model event into the relevant animation index.\n"
|
||||||
);
|
);
|
||||||
exit(exitstatus);
|
exit(exitstatus);
|
||||||
}
|
}
|
||||||
|
@ -5778,7 +5839,7 @@ void parsecommands(char *filename, const char *outfiles[countof(outputtypes)], v
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
if(argc <= 1) help(EXIT_FAILURE);
|
if(argc <= 1) help(EXIT_FAILURE, false);
|
||||||
|
|
||||||
vector<filespec> infiles;
|
vector<filespec> infiles;
|
||||||
vector<hitbox> hitboxes;
|
vector<hitbox> hitboxes;
|
||||||
|
@ -5790,11 +5851,13 @@ int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
if(argv[i][1] == '-')
|
if(argv[i][1] == '-')
|
||||||
{
|
{
|
||||||
if(!strcasecmp(&argv[i][2], "cmd")) { if(i + 1 < argc) parsecommands(argv[++i], outfiles, infiles, hitboxes); }
|
if(!strcasecmp(&argv[i][2], "set")) { if(i + 2 < argc) fte::Cvar_Create(argv[i+1], argv[i+2], 0, NULL, "cmdline");i+=2;}
|
||||||
|
else if(!strcasecmp(&argv[i][2], "cmd")) { if(i + 1 < argc) parsecommands(argv[++i], outfiles, infiles, hitboxes); }
|
||||||
else if(!strcasecmp(&argv[i][2], "noext")) noext = true;
|
else if(!strcasecmp(&argv[i][2], "noext")) noext = true;
|
||||||
else if(!strcasecmp(&argv[i][2], "fps")) { if(i + 1 < argc) inspec.fps = atof(argv[++i]); }
|
else if(!strcasecmp(&argv[i][2], "fps")) { if(i + 1 < argc) inspec.fps = atof(argv[++i]); }
|
||||||
else if(!strcasecmp(&argv[i][2], "name")) { if(i + 1 < argc) inspec.name = argv[++i]; }
|
else if(!strcasecmp(&argv[i][2], "name")) { if(i + 1 < argc) inspec.name = argv[++i]; }
|
||||||
else if(!strcasecmp(&argv[i][2], "loop")) { inspec.flags |= IQM_LOOP; }
|
else if(!strcasecmp(&argv[i][2], "loop")) { inspec.flags |= IQM_LOOP; }
|
||||||
|
else if(!strcasecmp(&argv[i][2], "unpack")) { inspec.flags |= IQM_UNPACK; }
|
||||||
else if(!strcasecmp(&argv[i][2], "start")) { if(i + 1 < argc) inspec.startframe = max(atoi(argv[++i]), 0); }
|
else if(!strcasecmp(&argv[i][2], "start")) { if(i + 1 < argc) inspec.startframe = max(atoi(argv[++i]), 0); }
|
||||||
else if(!strcasecmp(&argv[i][2], "end")) { if(i + 1 < argc) inspec.endframe = atoi(argv[++i]); }
|
else if(!strcasecmp(&argv[i][2], "end")) { if(i + 1 < argc) inspec.endframe = atoi(argv[++i]); }
|
||||||
else if(!strcasecmp(&argv[i][2], "scale")) { if(i + 1 < argc) inspec.scale = clamp(atof(argv[++i]), 1e-8, 1e8); }
|
else if(!strcasecmp(&argv[i][2], "scale")) { if(i + 1 < argc) inspec.scale = clamp(atof(argv[++i]), 1e-8, 1e8); }
|
||||||
|
@ -5802,7 +5865,7 @@ int main(int argc, char **argv)
|
||||||
else if(!strcasecmp(&argv[i][2], "yup")) inspec.rotate = Quat::fromangles(Vec3(0,-M_PI,0));
|
else if(!strcasecmp(&argv[i][2], "yup")) inspec.rotate = Quat::fromangles(Vec3(0,-M_PI,0));
|
||||||
else if(!strcasecmp(&argv[i][2], "zup")) inspec.rotate = Quat::fromdegrees(Vec3(0,-90,-90));
|
else if(!strcasecmp(&argv[i][2], "zup")) inspec.rotate = Quat::fromdegrees(Vec3(0,-90,-90));
|
||||||
else if(!strcasecmp(&argv[i][2], "ignoresurfname")) inspec.ignoresurfname = true;
|
else if(!strcasecmp(&argv[i][2], "ignoresurfname")) inspec.ignoresurfname = true;
|
||||||
else if(!strcasecmp(&argv[i][2], "help")) help();
|
else if(!strcasecmp(&argv[i][2], "help")) help(EXIT_SUCCESS,true);
|
||||||
else if(!strcasecmp(&argv[i][2], "forcejoints")) forcejoints = true;
|
else if(!strcasecmp(&argv[i][2], "forcejoints")) forcejoints = true;
|
||||||
else if(!strcasecmp(&argv[i][2], "meshtrans"))
|
else if(!strcasecmp(&argv[i][2], "meshtrans"))
|
||||||
{
|
{
|
||||||
|
@ -5815,7 +5878,7 @@ int main(int argc, char **argv)
|
||||||
else switch(argv[i][1])
|
else switch(argv[i][1])
|
||||||
{
|
{
|
||||||
case 'h':
|
case 'h':
|
||||||
help();
|
help(EXIT_SUCCESS,true);
|
||||||
break;
|
break;
|
||||||
case 's':
|
case 's':
|
||||||
if(i + 1 < argc) gscale = clamp(atof(argv[++i]), 1e-8, 1e8);
|
if(i + 1 < argc) gscale = clamp(atof(argv[++i]), 1e-8, 1e8);
|
||||||
|
@ -5934,9 +5997,12 @@ int main(int argc, char **argv)
|
||||||
|
|
||||||
calcanimdata();
|
calcanimdata();
|
||||||
|
|
||||||
|
if (!quiet)
|
||||||
|
{
|
||||||
conoutf("bone list:");
|
conoutf("bone list:");
|
||||||
printbones();
|
printbones();
|
||||||
// printbonelist();
|
// printbonelist();
|
||||||
|
}
|
||||||
|
|
||||||
if (!quiet)
|
if (!quiet)
|
||||||
conoutf("");
|
conoutf("");
|
||||||
|
|
|
@ -10,6 +10,9 @@
|
||||||
#ifdef IQMTOOL
|
#ifdef IQMTOOL
|
||||||
#define Con_Printf printf
|
#define Con_Printf printf
|
||||||
#define Con_DPrintf printf
|
#define Con_DPrintf printf
|
||||||
|
|
||||||
|
#undef CON_WARNING
|
||||||
|
#define CON_WARNING
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef SKELETALMODELS
|
#ifdef SKELETALMODELS
|
||||||
|
@ -49,10 +52,12 @@
|
||||||
static plugmodfuncs_t *modfuncs;
|
static plugmodfuncs_t *modfuncs;
|
||||||
static plugfsfuncs_t *filefuncs;
|
static plugfsfuncs_t *filefuncs;
|
||||||
|
|
||||||
|
static cvar_t *mod_gltf_loop;
|
||||||
static cvar_t *mod_gltf_scale;
|
static cvar_t *mod_gltf_scale;
|
||||||
static cvar_t *mod_gltf_fixbuggyanims;
|
static cvar_t *mod_gltf_fixbuggyanims;
|
||||||
static cvar_t *mod_gltf_privatematerials;
|
static cvar_t *mod_gltf_privatematerials;
|
||||||
static cvar_t *mod_gltf_ignoretechniques;
|
static cvar_t *mod_gltf_ignoretechniques;
|
||||||
|
static cvar_t *mod_gltf_standardorientation;
|
||||||
|
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
void VARGS Q_snprintfcat (char *dest, size_t size, const char *fmt, ...)
|
void VARGS Q_snprintfcat (char *dest, size_t size, const char *fmt, ...)
|
||||||
|
@ -2712,7 +2717,7 @@ static qboolean GLTF_ProcessNode(gltf_t *gltf, json_t *nodeid, double pmatrix[16
|
||||||
skinmatrix[i] = JSON_GetIndexedFloat(bdsm, i, ((i%5)==0)?1.0:0.0);
|
skinmatrix[i] = JSON_GetIndexedFloat(bdsm, i, ((i%5)==0)?1.0:0.0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
JSON_FlagAsUsed(node, "name");
|
JSON_FlagAsUsed(skin, "name");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gltf->ver <= 1)
|
if (gltf->ver <= 1)
|
||||||
|
@ -3127,10 +3132,11 @@ static float *QDECL GLTF_AnimateBones(const galiasinfo_t *surf, const galiasanim
|
||||||
if (surf->ofsbones[j].parent < 0)
|
if (surf->ofsbones[j].parent < 0)
|
||||||
{ //rotate any root bones from gltf to quake's orientation.
|
{ //rotate any root bones from gltf to quake's orientation.
|
||||||
float fnar[12];
|
float fnar[12];
|
||||||
float toquake[12]={
|
float toquake[12]={0};
|
||||||
0,0,mod_gltf_scale->value, 0,
|
if (mod_gltf_standardorientation->ival)
|
||||||
mod_gltf_scale->value,0,0, 0,
|
toquake[2] = toquake[4] = toquake[9] = mod_gltf_scale->value;
|
||||||
0,mod_gltf_scale->value,0, 0};
|
else
|
||||||
|
toquake[0] = toquake[5] = toquake[10] = mod_gltf_scale->value;
|
||||||
memcpy(fnar, bonematrix, sizeof(fnar));
|
memcpy(fnar, bonematrix, sizeof(fnar));
|
||||||
modfuncs->ConcatTransforms((void*)toquake, (void*)fnar, (void*)bonematrix);
|
modfuncs->ConcatTransforms((void*)toquake, (void*)fnar, (void*)bonematrix);
|
||||||
}
|
}
|
||||||
|
@ -3250,11 +3256,16 @@ static qboolean GLTF_LoadModel(struct model_s *mod, char *json, size_t jsonsize,
|
||||||
scene = GLTF_FindJSONID_First(&gltf, "scenes", JSON_FindChild(gltf.r, "scene"), NULL);
|
scene = GLTF_FindJSONID_First(&gltf, "scenes", JSON_FindChild(gltf.r, "scene"), NULL);
|
||||||
|
|
||||||
memset(&rootmatrix, 0, sizeof(rootmatrix));
|
memset(&rootmatrix, 0, sizeof(rootmatrix));
|
||||||
#if 1 //transform from gltf to quake. mostly only needed for the base pose.
|
if (mod_gltf_standardorientation->ival) //transform from gltf to quake. mostly only needed for the base pose.
|
||||||
rootmatrix[2] = rootmatrix[4] = rootmatrix[9] = mod_gltf_scale->value; rootmatrix[15] = 1;
|
{
|
||||||
#else
|
rootmatrix[2] = rootmatrix[4] = rootmatrix[9] = mod_gltf_scale->value;
|
||||||
rootmatrix[0] = rootmatrix[5] = rootmatrix[10] = 1; rootmatrix[15] = 1;
|
rootmatrix[15] = 1;
|
||||||
#endif
|
}
|
||||||
|
else //identity orientation that violates gltf standards.
|
||||||
|
{
|
||||||
|
rootmatrix[0] = rootmatrix[5] = rootmatrix[10] = mod_gltf_scale->value;
|
||||||
|
rootmatrix[15] = 1;
|
||||||
|
}
|
||||||
|
|
||||||
n = JSON_FindChild(gltf.r, "nodes");
|
n = JSON_FindChild(gltf.r, "nodes");
|
||||||
for (j = 0; ; j++)
|
for (j = 0; ; j++)
|
||||||
|
@ -3354,7 +3365,7 @@ static qboolean GLTF_LoadModel(struct model_s *mod, char *json, size_t jsonsize,
|
||||||
else
|
else
|
||||||
Q_snprintf(fg->name, sizeof(fg->name), "anim%u", (unsigned int)k);
|
Q_snprintf(fg->name, sizeof(fg->name), "anim%u", (unsigned int)k);
|
||||||
}
|
}
|
||||||
fg->loop = true;
|
fg->loop = !!mod_gltf_loop->ival;
|
||||||
fg->skeltype = SKEL_RELATIVE;
|
fg->skeltype = SKEL_RELATIVE;
|
||||||
for(chan = JSON_FindIndexedChild(anim, "channels", 0); chan; chan = chan->sibling)
|
for(chan = JSON_FindIndexedChild(anim, "channels", 0); chan; chan = chan->sibling)
|
||||||
{
|
{
|
||||||
|
@ -3414,11 +3425,10 @@ static qboolean GLTF_LoadModel(struct model_s *mod, char *json, size_t jsonsize,
|
||||||
if (!maxtime)
|
if (!maxtime)
|
||||||
maxtime = 1.0/30; //some stuff doesn't like 0-length animations. divisions by 0 are not nice.
|
maxtime = 1.0/30; //some stuff doesn't like 0-length animations. divisions by 0 are not nice.
|
||||||
a->duration = maxtime;
|
a->duration = maxtime;
|
||||||
|
|
||||||
//calc average framerate
|
//calc average framerate
|
||||||
fg->numposes = maxposes;
|
fg->numposes = maxposes;
|
||||||
if (maxtime)
|
if (maxtime&&fg->numposes>1)
|
||||||
fg->rate = fg->numposes/maxtime; //fix up the rate so we hit the exact end of the animation (so it doesn't have to be quite so exact).
|
fg->rate = (fg->numposes-1)/maxtime; //fix up the rate so we hit the exact end of the animation (so it doesn't have to be quite so exact).
|
||||||
else
|
else
|
||||||
fg->rate = 60;
|
fg->rate = 60;
|
||||||
|
|
||||||
|
@ -3460,11 +3470,40 @@ static qboolean GLTF_LoadModel(struct model_s *mod, char *json, size_t jsonsize,
|
||||||
surf->geomset = ~0; //invalid set = always visible. FIXME: set this according to scene numbers?
|
surf->geomset = ~0; //invalid set = always visible. FIXME: set this according to scene numbers?
|
||||||
surf->geomid = 0;
|
surf->geomid = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
VectorScale(mod->mins, mod_gltf_scale->value, mod->mins);
|
VectorScale(mod->mins, mod_gltf_scale->value, mod->mins);
|
||||||
VectorScale(mod->maxs, mod_gltf_scale->value, mod->maxs);
|
VectorScale(mod->maxs, mod_gltf_scale->value, mod->maxs);
|
||||||
|
|
||||||
if (!mod->meshinfo)
|
if (!mod->meshinfo && numframegroups>0)
|
||||||
Con_Printf("%s: Doesn't contain any meshes...\n", mod->name);
|
Con_Printf("%s: Doesn't contain any meshes...\n", mod->name);
|
||||||
|
else if (!mod_gltf_loop->ival)
|
||||||
|
{ //gltf doesn't specify if things loop or not.
|
||||||
|
//our lerping is between previous+next frames, we don't actually handle wrapping here, and our 'numframes' is entirely arbitrary.
|
||||||
|
//if the last frame and first frame are identical then its safe to say that it MAY loop, otherwise there'll be a judder when trying to do so.
|
||||||
|
//and if it does seem to loop okay, don't hold that last frame for an extra 1/fps when the repeats starts, just go snap straight to the first frame.
|
||||||
|
float *first = malloc(sizeof(*first)*12*gltf.numbones*2);
|
||||||
|
float *last = first+12*gltf.numbones;
|
||||||
|
galiasanimation_t *fg;
|
||||||
|
struct galiasanimation_gltf_s *a;
|
||||||
|
surf = mod->meshinfo;
|
||||||
|
for (k = 0; k < numframegroups; k++)
|
||||||
|
{
|
||||||
|
fg = &framegroups[k];
|
||||||
|
a = fg->boneofs;
|
||||||
|
|
||||||
|
fg->GetRawBones(surf, fg, 0, first, gltf.numbones);
|
||||||
|
fg->GetRawBones(surf, fg, a->duration, last, gltf.numbones); //should fall on an exact frame.
|
||||||
|
for (j = 0; j < 12*gltf.numbones; j++)
|
||||||
|
if (first[j] != last[j])
|
||||||
|
break;
|
||||||
|
if (j == 12*gltf.numbones)
|
||||||
|
{
|
||||||
|
fg->loop = true;
|
||||||
|
fg->numposes--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free(first);
|
||||||
|
}
|
||||||
JSON_WarnUnused(gltf.r, &gltf.warnlimit);
|
JSON_WarnUnused(gltf.r, &gltf.warnlimit);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -3541,11 +3580,14 @@ qboolean Plug_GLTF_Init(void)
|
||||||
mod_gltf_scale = cvarfuncs->GetNVFDG("mod_gltf_scale", "30", CVAR_RENDERERLATCH, "This defines the number of units per metre, in order to correctly load standard-scale gltf models.", "GLTF Models");
|
mod_gltf_scale = cvarfuncs->GetNVFDG("mod_gltf_scale", "30", CVAR_RENDERERLATCH, "This defines the number of units per metre, in order to correctly load standard-scale gltf models.", "GLTF Models");
|
||||||
mod_gltf_fixbuggyanims = cvarfuncs->GetNVFDG("mod_gltf_fixbuggyanims", "1", CVAR_RENDERERLATCH, "Work around buggy exporters by merging animations that affect only a single bone.", "GLTF Models");
|
mod_gltf_fixbuggyanims = cvarfuncs->GetNVFDG("mod_gltf_fixbuggyanims", "1", CVAR_RENDERERLATCH, "Work around buggy exporters by merging animations that affect only a single bone.", "GLTF Models");
|
||||||
#ifdef IQMTOOL
|
#ifdef IQMTOOL
|
||||||
|
mod_gltf_loop = cvarfuncs->GetNVFDG("mod_gltf_loop", "0", CVAR_RENDERERLATCH, "This forces all gltf models to loop, instead of only when the last frame matches the first.", "GLTF Models");
|
||||||
mod_gltf_privatematerials = cvarfuncs->GetNVFDG("mod_gltf_privatematerials", "0", CVAR_RENDERERLATCH, "Add the model path to material names, to isolate them between different models.", "GLTF Models");
|
mod_gltf_privatematerials = cvarfuncs->GetNVFDG("mod_gltf_privatematerials", "0", CVAR_RENDERERLATCH, "Add the model path to material names, to isolate them between different models.", "GLTF Models");
|
||||||
#else
|
#else
|
||||||
|
mod_gltf_loop = cvarfuncs->GetNVFDG("mod_gltf_loop", "1", CVAR_RENDERERLATCH, "This forces all gltf models to loop, instead of only when the last frame matches the first.", "GLTF Models");
|
||||||
mod_gltf_privatematerials = cvarfuncs->GetNVFDG("mod_gltf_privatematerials", "1", CVAR_RENDERERLATCH, "Add the model path to material names, to isolate them between different models.", "GLTF Models");
|
mod_gltf_privatematerials = cvarfuncs->GetNVFDG("mod_gltf_privatematerials", "1", CVAR_RENDERERLATCH, "Add the model path to material names, to isolate them between different models.", "GLTF Models");
|
||||||
#endif
|
#endif
|
||||||
mod_gltf_ignoretechniques = cvarfuncs->GetNVFDG("mod_gltf_ignoretechniques", "1", CVAR_RENDERERLATCH, "Ignore the gltf1 model-specific glsl. This is enabled by default because it just doesn't work very well.", "GLTF Models");
|
mod_gltf_ignoretechniques = cvarfuncs->GetNVFDG("mod_gltf_ignoretechniques", "1", CVAR_RENDERERLATCH, "Ignore the GLTF-1 model-specific glsl. This is enabled by default because it just doesn't work very well.", "GLTF Models");
|
||||||
|
mod_gltf_standardorientation = cvarfuncs->GetNVFDG("mod_gltf_standardorientation", "1", CVAR_RENDERERLATCH, "Transform GLTF files from standard y-up to Quake's z-up orientation. Set to 0 to violate the GLTF specification.", "GLTF Models");
|
||||||
|
|
||||||
if (modfuncs && filefuncs)
|
if (modfuncs && filefuncs)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue