quakeforge/tools/qfmodelgen/source/modelgen.c
Bill Currie 6d5ffa9f8e [build] Move to non-recursive make
There's still some cleanup to do, but everything seems to be working
nicely: `make -j` works, `make distcheck` passes. There is probably
plenty of bitrot in the package directories (RPM, debian), though.

The vc project files have been removed since those versions are way out
of date and quakeforge is pretty much dependent on gcc now anyway.

Most of the old Makefile.am files  are now Makemodule.am.  This should
allow for new Makefile.am files that allow local building (to be added
on an as-needed bases).  The current remaining Makefile.am files are for
standalone sub-projects.a

The installable bins are currently built in the top-level build
directory. This may change if the clutter gets to be too much.

While this does make a noticeable difference in build times, the main
reason for the switch was to take care of the growing dependency issues:
now it's possible to build tools for code generation (eg, using qfcc and
ruamoko programs for code-gen).
2020-06-25 11:35:37 +09:00

1056 lines
25 KiB
C

/* Copyright (C) 1996-1997 Id Software, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
See file, 'COPYING', for details.
*/
// modelgen.c: generates a .mdl file from a base triangle file (.tri), a
// texture containing front and back skins (.lbm), and a series of frame
// triangle files (.tri). Result is stored in
// /raid/quake/models/<scriptname>.mdl.
//#include <sys/stat.h>
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#ifdef _WIN32
# include <direct.h>
#endif
#include "QF/dstring.h"
#include "QF/modelgen.h"
#include "QF/qendian.h"
#include "QF/quakefs.h"
#include "QF/quakeio.h"
#include "QF/script.h"
#include "QF/sys.h"
#include "compat.h"
#include "tools/qfmodelgen/include/lbmlib.h"
#include "tools/qfmodelgen/include/trilib.h"
#define MAXVERTS 2048
#define MAXFRAMES 256
#define MAXSKINS 100
#define MAXTRIANGLES 2048
typedef struct {
aliasframetype_t type; // single frame or group of frames
void *pdata; // either a daliasframe_t or group info
float interval; // used only for frames in groups
int numgroupframes; // used only by group headers
char name[16];
} aliaspackage_t;
typedef struct {
aliasskintype_t type; // single skin or group of skiins
void *pdata; // either a daliasskinframe_t or group info
float interval; // used only for skins in groups
int numgroupskins; // used only by group headers
} aliasskinpackage_t;
typedef struct {
int numnormals;
float normals[20][3];
} vertexnormals;
typedef struct {
vec3_t v;
int lightnormalindex;
} trivert_t;
//============================================================================
trivert_t verts[MAXFRAMES][MAXVERTS];
mdl_t model;
char file1[1024];
char skinname[1024];
char qbasename[1024];
float scale, scale_up = 1.0;
vec3_t mins, maxs;
vec3_t framesmins, framesmaxs;
vec3_t adjust;
aliaspackage_t frames[MAXFRAMES];
aliasskinpackage_t skins[MAXSKINS];
//
// base frame info
//
vec3_t baseverts[MAXVERTS];
stvert_t stverts[MAXVERTS];
dtriangle_t triangles[MAXTRIANGLES];
int degenerate[MAXTRIANGLES];
char cdpartial[256];
char cddir[256];
int framecount, skincount;
qboolean cdset;
int degeneratetris;
int firstframe = 1;
float totsize, averagesize;
vertexnormals vnorms[MAXVERTS];
#define NUMVERTEXNORMALS 162
float avertexnormals[NUMVERTEXNORMALS][3] = {
#include "anorms.h" //FIXME should this be moved to QF?
};
trivertx_t tarray[MAXVERTS];
char outname[1024];
script_t scr;
/*
qdir will hold the path up to the quake directory, including the slash
f:\quake\
/raid/quake/
gamedir will hold qdir + the game directory (id1, id2, etc)
*/
char qdir[1024];
char gamedir[1024];
static void
SetQdirFromPath (char *path)
{
char temp[1024];
char *c;
if (!(path[0] == '/' || path[0] == '\\' || path[1] == ':'))
{ // path is partial
if (getcwd (temp, sizeof (temp)) == NULL)
Sys_Error ("Can't get CWD");
strcat (temp, path);
path = temp;
}
// search for "quake" in path
for (c=path ; *c ; c++)
if (!strncasecmp (c, "quake", 5)) {
strncpy (qdir, path, c+6-path);
printf ("qdir: %s\n", qdir);
c += 6;
while (*c) {
if (*c == '/' || *c == '\\') {
strncpy (gamedir, path, c+1-path);
printf ("gamedir: %s\n", gamedir);
return;
}
c++;
}
Sys_Error ("No gamedir in %s", path);
return;
}
Sys_Error ("SetQdirFromPath: no 'quake' in %s", path);
}
static const char *
ExpandPath (const char *path)
{
static char full[1024];
//FIXME buffer overflow central
//if (!qdir)
// Sys_Error ("ExpandPath called without qdir set");
if (path[0] == '/' || path[0] == '\\' || path[1] == ':')
return path;
sprintf (full, "%s%s", qdir, path);
return full;
}
static void
ClearModel (void)
{
memset (&model, 0, sizeof (model));
model.synctype = ST_RAND; // default
framecount = skincount = 0;
scale = 0;
scale_up = 1.0;
VectorZero (adjust);
VectorZero (mins);
VectorZero (maxs);
VectorZero (framesmins);
VectorZero (framesmaxs);
degeneratetris = 0;
cdset = false;
firstframe = 1;
totsize = 0.0;
}
static void
WriteFrame (QFile *modelouthandle, int framenum)
{
int j, k;
float v;
daliasframe_t aframe;
trivert_t *pframe;
pframe = verts[framenum];
strcpy (aframe.name, frames[framenum].name);
for (j = 0; j < 3; j++) {
aframe.bboxmin.v[j] = 255;
aframe.bboxmax.v[j] = 0;
}
for (j = 0; j < model.numverts; j++) {
// all of these are byte values, so no need to deal with endianness
tarray[j].lightnormalindex = pframe[j].lightnormalindex;
if (tarray[j].lightnormalindex > NUMVERTEXNORMALS)
Sys_Error ("invalid lightnormalindex %d\n",
tarray[j].lightnormalindex);
for (k = 0; k < 3; k++) {
// scale to byte values & min/max check
v = (pframe[j].v[k] - model.scale_origin[k]) / model.scale[k];
tarray[j].v[k] = v;
if (tarray[j].v[k] < aframe.bboxmin.v[k])
aframe.bboxmin.v[k] = tarray[j].v[k];
if (tarray[j].v[k] > aframe.bboxmax.v[k])
aframe.bboxmax.v[k] = tarray[j].v[k];
}
}
Qwrite (modelouthandle, &aframe, sizeof (aframe));
Qwrite (modelouthandle, &tarray[0], model.numverts * sizeof(tarray[0]));
}
static void
WriteGroupBBox (QFile *modelouthandle, int numframes, int curframe)
{
int i, j, k;
daliasgroup_t dagroup;
trivert_t *pframe;
dagroup.numframes = LittleLong (numframes);
for (i = 0; i < 3; i++) {
dagroup.bboxmin.v[i] = 255;
dagroup.bboxmax.v[i] = 0;
}
for (i = 0; i < numframes; i++) {
pframe = (trivert_t *) frames[curframe].pdata;
for (j = 0; j < model.numverts; j++) {
for (k = 0; k < 3; k++) {
// scale to byte values & min/max check
tarray[j].v[k] = (pframe[j].v[k] - model.scale_origin[k]) /
model.scale[k];
if (tarray[j].v[k] < dagroup.bboxmin.v[k])
dagroup.bboxmin.v[k] = tarray[j].v[k];
if (tarray[j].v[k] > dagroup.bboxmax.v[k])
dagroup.bboxmax.v[k] = tarray[j].v[k];
}
}
curframe++;
}
Qwrite (modelouthandle, &dagroup, sizeof(dagroup));
}
static void
WriteModelFile (QFile *modelouthandle)
{
int i, curframe, curskin;
float dist[3];
mdl_t modeltemp;
// Calculate the bounding box for this model
for (i = 0; i < 3; i++) {
printf ("framesmins[%d]: %f, framesmaxs[%d]: %f\n",
i, framesmins[i], i, framesmaxs[i]);
if (fabs (framesmins[i]) > fabs (framesmaxs[i]))
dist[i] = framesmins[i];
else
dist[i] = framesmaxs[i];
model.scale[i] = (framesmaxs[i] - framesmins[i]) / 255.9;
model.scale_origin[i] = framesmins[i];
}
model.boundingradius = sqrt (dist[0] * dist[0] +
dist[1] * dist[1] +
dist[2] * dist[2]);
// write out the model header
modeltemp.ident = LittleLong (IDHEADER_MDL);
modeltemp.version = LittleLong (ALIAS_VERSION_MDL);
modeltemp.boundingradius = LittleFloat (model.boundingradius);
for (i = 0; i < 3; i++) {
modeltemp.scale[i] = LittleFloat (model.scale[i]);
modeltemp.scale_origin[i] = LittleFloat (model.scale_origin[i]);
modeltemp.eyeposition[i] = LittleFloat (model.eyeposition[i] +
adjust[i]);
}
modeltemp.flags = LittleLong (model.flags);
modeltemp.numskins = LittleLong (model.numskins);
modeltemp.skinwidth = LittleLong (model.skinwidth);
modeltemp.skinheight = LittleLong (model.skinheight);
modeltemp.numverts = LittleLong (model.numverts);
modeltemp.numtris = LittleLong (model.numtris - degeneratetris);
modeltemp.numframes = LittleLong (model.numframes);
modeltemp.synctype = LittleFloat (model.synctype);
averagesize = totsize / model.numtris;
modeltemp.size = LittleFloat (averagesize);
Qwrite (modelouthandle, &modeltemp, sizeof (model));
// write out the skins
curskin = 0;
for (i = 0; i < model.numskins; i++) {
Qwrite (modelouthandle, &skins[curskin].type,
sizeof (skins[curskin].type));
Qwrite (modelouthandle, skins[curskin].pdata,
model.skinwidth * model.skinheight);
curskin++;
}
// write out the base model (the s & t coordinates for the vertices)
for (i = 0; i < model.numverts; i++) {
if (stverts[i].onseam == 3) {
stverts[i].onseam = LittleLong (ALIAS_ONSEAM);
} else {
stverts[i].onseam = LittleLong (0);
}
stverts[i].s = LittleLong (stverts[i].s);
stverts[i].t = LittleLong (stverts[i].t);
}
Qwrite (modelouthandle, stverts, model.numverts * sizeof(stverts[0]));
// write out the triangles
for (i = 0; i < model.numtris; i++) {
int j;
dtriangle_t tri;
if (!degenerate[i]) {
tri.facesfront = LittleLong (triangles[i].facesfront);
for (j = 0; j < 3; j++) {
tri.vertindex[j] = LittleLong (triangles[i].vertindex[j]);
}
Qwrite (modelouthandle, &tri, sizeof(tri));
}
}
// write out the frames
curframe = 0;
for (i = 0; i < model.numframes; i++) {
Qwrite (modelouthandle, &frames[curframe].type,
sizeof (frames[curframe].type));
if (frames[curframe].type == ALIAS_SINGLE) {
// single (non-grouped) frame
WriteFrame (modelouthandle, curframe);
curframe++;
} else {
int j, numframes, groupframe;
float totinterval;
groupframe = curframe;
curframe++;
numframes = frames[groupframe].numgroupframes;
// set and write the group header
WriteGroupBBox (modelouthandle, numframes, curframe);
// write the interval array
totinterval = 0.0;
for (j = 0; j < numframes; j++) {
daliasinterval_t temp;
totinterval += frames[groupframe+1+j].interval;
temp.interval = LittleFloat (totinterval);
Qwrite (modelouthandle, &temp, sizeof(temp));
}
for (j = 0; j < numframes; j++) {
WriteFrame (modelouthandle, curframe);
curframe++;
}
}
}
}
static void
WriteModel (void)
{
QFile *modelouthandle;
// write the model output file
if (!framecount) {
printf ("no frames grabbed, no file generated\n");
return;
}
if (!skincount)
Sys_Error ("frames with no skins\n");
QFS_StripExtension (outname, outname);
strcat (outname, ".mdl");
printf ("---------------------\n");
printf ("writing %s:\n", outname);
modelouthandle = Qopen (outname, "wb");
WriteModelFile (modelouthandle);
printf ("%4d frame(s)\n", model.numframes);
printf ("%4d ungrouped frame(s), including group headers\n", framecount);
printf ("%4d skin(s)\n", model.numskins);
printf ("%4d degenerate triangles(s) removed\n", degeneratetris);
printf ("%4d triangles emitted\n", model.numtris - degeneratetris);
printf ("pixels per triangle %f\n", averagesize);
printf ("file size: %d\n", (int) Qtell (modelouthandle));
printf ("---------------------\n");
Qclose (modelouthandle);
ClearModel ();
}
/*
============
SetSkinValues
Called for the base frame
============
*/
static void
SetSkinValues (void)
{
float basex, basey, v;
int width, height, iwidth, iheight, skinwidth, i;
for (i = 0; i < 3; i++) {
mins[i] = 9999999;
maxs[i] = -9999999;
}
for (i = 0; i < model.numverts; i++) {
int j;
stverts[i].onseam = 0;
for (j = 0; j < 3; j++) {
v = baseverts[i][j];
if (v < mins[j])
mins[j] = v;
if (v > maxs[j])
maxs[j] = v;
}
}
for (i = 0; i < 3; i++) {
mins[i] = floor(mins[i]);
maxs[i] = ceil(maxs[i]);
}
width = maxs[0] - mins[0];
height = maxs[2] - mins[2];
printf ("width: %i height: %i\n", width, height);
scale = 8;
if (width*scale >= 150)
scale = 150.0 / width;
if (height*scale >= 190)
scale = 190.0 / height;
iwidth = ceil(width*scale) + 4;
iheight = ceil(height*scale) + 4;
printf ("scale: %f\n", scale);
printf ("iwidth: %i iheight: %i\n", iwidth, iheight);
// determine which side of each triangle to map the texture to
for (i = 0; i < model.numtris; i++) {
int j;
vec3_t vtemp1, vtemp2, normal;
VectorSubtract (baseverts[triangles[i].vertindex[0]],
baseverts[triangles[i].vertindex[1]],
vtemp1);
VectorSubtract (baseverts[triangles[i].vertindex[2]],
baseverts[triangles[i].vertindex[1]],
vtemp2);
CrossProduct (vtemp1, vtemp2, normal);
if (normal[1] > 0) {
basex = iwidth + 2;
triangles[i].facesfront = 0;
} else {
basex = 2;
triangles[i].facesfront = 1;
}
basey = 2;
for (j = 0; j < 3; j++) {
float *pbasevert;
stvert_t *pstvert;
pbasevert = baseverts[triangles[i].vertindex[j]];
pstvert = &stverts[triangles[i].vertindex[j]];
if (triangles[i].facesfront) {
pstvert->onseam |= 1;
} else {
pstvert->onseam |= 2;
}
if ((triangles[i].facesfront) || ((pstvert->onseam & 1) == 0)) {
// we want the front s value for seam vertices
pstvert->s = ((pbasevert[0] - mins[0]) * scale + basex) + 0.5;
pstvert->t = ((maxs[2] - pbasevert[2]) * scale + basey) + 0.5;
}
}
}
// make the width a multiple of 4; some hardware requires this, and it ensures
// dword alignment for each scan
skinwidth = iwidth*2;
model.skinwidth = (skinwidth + 3) & ~3;
model.skinheight = iheight;
printf ("skin width: %i (unpadded width %i) skin height: %i\n",
model.skinwidth, skinwidth, model.skinheight);
}
static void
Cmd_Base (void)
{
triangle_t *ptri;
int time1, i, j, k;
Script_GetToken (&scr, false);
strcpy (qbasename, scr.token->str);
sprintf (file1, "%s/%s.tri", cdpartial, scr.token->str);
ExpandPath/*AndArchive*/ (file1);
sprintf (file1, "%s/%s.tri", cddir, scr.token->str);
time1 = Sys_FileExists (file1);
if (time1 == -1)
Sys_Error ("%s doesn't exist", file1);
// load the base triangles
LoadTriangleList (file1, &ptri, &model.numtris);
printf("NUMBER OF TRIANGLES (including degenerate triangles): %d\n",
model.numtris);
// run through all the base triangles, storing each unique vertex in the base
// vertex list and setting the indirect triangles to point to the base vertices
for (i = 0; i < model.numtris; i++) {
if (_VectorCompare (ptri[i].verts[0], ptri[i].verts[1])
|| _VectorCompare (ptri[i].verts[1], ptri[i].verts[2])
|| _VectorCompare (ptri[i].verts[2], ptri[i].verts[0])) {
degeneratetris++;
degenerate[i] = 1;
} else {
degenerate[i] = 0;
}
for (j = 0; j < 3; j++) {
for (k=0 ; k<model.numverts ; k++)
if (_VectorCompare (ptri[i].verts[j], baseverts[k]))
break; // this vertex is already in the base vertex list
if (k == model.numverts) {
// new vertex
VectorCopy (ptri[i].verts[j], baseverts[model.numverts]);
model.numverts++;
}
triangles[i].vertindex[j] = k;
}
}
printf ("NUMBER OF VERTEXES: %i\n", model.numverts);
// calculate s & t for each vertex, and set the skin width and height
SetSkinValues ();
}
static void
Cmd_Skin (void)
{
byte *ppal, *pskinbitmap, *ptemp1, *ptemp2;
int time1, i;
Script_GetToken (&scr, false);
strcpy (skinname, scr.token->str);
sprintf (file1, "%s/%s.lbm", cdpartial, scr.token->str);
ExpandPath/*AndArchive*/ (file1);
sprintf (file1, "%s/%s.lbm", cddir, scr.token->str);
time1 = Sys_FileExists (file1);
if (time1 == -1)
Sys_Error ("%s not found", file1);
if (Script_TokenAvailable (&scr, false)) {
Script_GetToken (&scr, false);
skins[skincount].interval = atof (scr.token->str);
if (skins[skincount].interval <= 0.0)
Sys_Error ("Non-positive interval");
} else {
skins[skincount].interval = 0.1;
}
// load in the skin .lbm file
LoadLBM (file1, &pskinbitmap, &ppal);
// now copy the part of the texture we care about, since LBMs are always
// loaded as 320x200 bitmaps
skins[skincount].pdata =
malloc (model.skinwidth * model.skinheight);
if (!skins[skincount].pdata)
Sys_Error ("couldn't get memory for skin texture");
ptemp1 = skins[skincount].pdata;
ptemp2 = pskinbitmap;
for (i = 0; i < model.skinheight; i++) {
memcpy (ptemp1, ptemp2, model.skinwidth);
ptemp1 += model.skinwidth;
ptemp2 += 320;
}
skincount++;
if (skincount > MAXSKINS)
Sys_Error ("Too many skins; increase MAXSKINS");
}
static void
GrabFrame (char *frame, int isgroup)
{
int numtris, time1, i, j;
triangle_t *ptri;
trivert_t *ptrivert;
sprintf (file1, "%s/%s.tri", cdpartial, frame);
ExpandPath/*AndArchive*/ (file1);
sprintf (file1, "%s/%s.tri", cddir, frame);
time1 = Sys_FileExists (file1);
if (time1 == -1)
Sys_Error ("%s does not exist", file1);
printf ("grabbing %s\n", file1);
frames[framecount].interval = 0.1;
strcpy (frames[framecount].name, frame);
// load the frame
LoadTriangleList (file1, &ptri, &numtris);
if (numtris != model.numtris)
Sys_Error ("number of triangles doesn't match\n");
// set the intervals
if (isgroup && Script_TokenAvailable (&scr, false)) {
Script_GetToken (&scr, false);
frames[framecount].interval = atof (scr.token->str);
if (frames[framecount].interval <= 0.0)
Sys_Error ("Non-positive interval %s %f", scr.token->str,
frames[framecount].interval);
} else {
frames[framecount].interval = 0.1;
}
// allocate storage for the frame's vertices
ptrivert = verts[framecount];
frames[framecount].pdata = ptrivert;
frames[framecount].type = ALIAS_SINGLE;
for (i = 0; i < model.numverts ; i++) {
vnorms[i].numnormals = 0;
}
// store the frame's vertices in the same order as the base. This assumes the
// triangles and vertices in this frame are in exactly the same order as in the
// base
for (i = 0; i < numtris; i++) {
vec3_t vtemp1, vtemp2, normal;
float ftemp;
if (degenerate[i])
continue;
if (firstframe) {
VectorSubtract (ptri[i].verts[0], ptri[i].verts[1], vtemp1);
VectorSubtract (ptri[i].verts[2], ptri[i].verts[1], vtemp2);
VectorScale (vtemp1, scale_up, vtemp1);
VectorScale (vtemp2, scale_up, vtemp2);
CrossProduct (vtemp1, vtemp2, normal);
totsize += sqrt (normal[0] * normal[0] +
normal[1] * normal[1] +
normal[2] * normal[2]) / 2.0;
}
VectorSubtract (ptri[i].verts[0], ptri[i].verts[1], vtemp1);
VectorSubtract (ptri[i].verts[2], ptri[i].verts[1], vtemp2);
CrossProduct (vtemp1, vtemp2, normal);
VectorNormalize (normal);
// rotate the normal so the model faces down the positive x axis
ftemp = normal[0];
normal[0] = -normal[1];
normal[1] = ftemp;
for (j = 0; j < 3; j++) {
int k;
int vertindex;
vertindex = triangles[i].vertindex[j];
// rotate the vertices so the model faces down the positive x axis
// also adjust the vertices to the desired origin
ptrivert[vertindex].v[0] = ((-ptri[i].verts[j][1]) * scale_up) +
adjust[0];
ptrivert[vertindex].v[1] = (ptri[i].verts[j][0] * scale_up) +
adjust[1];
ptrivert[vertindex].v[2] = (ptri[i].verts[j][2] * scale_up) +
adjust[2];
for (k = 0; k < 3; k++) {
if (ptrivert[vertindex].v[k] < framesmins[k])
framesmins[k] = ptrivert[vertindex].v[k];
if (ptrivert[vertindex].v[k] > framesmaxs[k])
framesmaxs[k] = ptrivert[vertindex].v[k];
}
VectorCopy (normal,
vnorms[vertindex].
normals[vnorms[vertindex].numnormals]);
vnorms[vertindex].numnormals++;
}
}
// calculate the vertex normals, match them to the template list, and store the
// index of the best match
for (i = 0; i < model.numverts; i++) {
vec3_t v;
float maxdot;
int maxdotindex, j;
if (vnorms[i].numnormals > 0) {
for (j = 0; j < 3; j++) {
int k;
v[j] = 0;
for (k = 0; k < vnorms[i].numnormals; k++) {
v[j] += vnorms[i].normals[k][j];
}
v[j] /= vnorms[i].numnormals;
}
} else {
Sys_Error ("Vertex with no non-degenerate triangles attached");
}
VectorNormalize (v);
maxdot = -999999.0;
maxdotindex = -1;
for (j = 0; j < NUMVERTEXNORMALS; j++) {
float dot;
dot = DotProduct (v, avertexnormals[j]);
if (dot > maxdot) {
maxdot = dot;
maxdotindex = j;
}
}
ptrivert[i].lightnormalindex = maxdotindex;
}
framecount++;
if (framecount >= MAXFRAMES)
Sys_Error ("Too many frames; increase MAXFRAMES");
free (ptri);
firstframe = 0;
}
static void
Cmd_Frame (int isgroup)
{
while (Script_TokenAvailable (&scr, false)) {
Script_GetToken (&scr, false);
GrabFrame (scr.token->str, isgroup);
if (!isgroup)
model.numframes++;
}
}
static void
Cmd_SkinGroupStart (void)
{
int groupskin;
groupskin = skincount++;
if (skincount >= MAXFRAMES)
Sys_Error ("Too many skins; increase MAXSKINS");
skins[groupskin].type = ALIAS_SKIN_GROUP;
skins[groupskin].numgroupskins = 0;
while (1) {
if (!Script_GetToken (&scr, true))
Sys_Error ("End of file during group");
if (!strcmp (scr.token->str, "$skin")) {
Cmd_Skin ();
skins[groupskin].numgroupskins++;
} else if (!strcmp (scr.token->str, "$skingroupend")) {
break;
} else {
Sys_Error ("$skin or $skingroupend expected\n");
}
}
if (skins[groupskin].numgroupskins == 0)
Sys_Error ("Empty group\n");
}
static void
Cmd_FrameGroupStart (void)
{
int groupframe;
groupframe = framecount++;
if (framecount >= MAXFRAMES)
Sys_Error ("Too many frames; increase MAXFRAMES");
frames[groupframe].type = ALIAS_GROUP;
frames[groupframe].numgroupframes = 0;
while (1) {
if (!Script_GetToken (&scr, true))
Sys_Error ("End of file during group");
if (!strcmp (scr.token->str, "$frame")) {
Cmd_Frame (1);
} else if (!strcmp (scr.token->str, "$framegroupend")) {
break;
} else {
Sys_Error ("$frame or $framegroupend expected\n");
}
}
frames[groupframe].numgroupframes += framecount - groupframe - 1;
if (frames[groupframe].numgroupframes == 0)
Sys_Error ("Empty group\n");
}
static void
Cmd_Origin (void)
{
// rotate points into frame of reference so model points down the positive x
// axis
Script_GetToken (&scr, false);
adjust[1] = -atof (scr.token->str);
Script_GetToken (&scr, false);
adjust[0] = atof (scr.token->str);
Script_GetToken (&scr, false);
adjust[2] = -atof (scr.token->str);
}
static void
Cmd_Eyeposition (void)
{
// rotate points into frame of reference so model points down the positive x
// axis
Script_GetToken (&scr, false);
model.eyeposition[1] = atof (scr.token->str);
Script_GetToken (&scr, false);
model.eyeposition[0] = -atof (scr.token->str);
Script_GetToken (&scr, false);
model.eyeposition[2] = atof (scr.token->str);
}
static void
Cmd_ScaleUp (void)
{
Script_GetToken (&scr, false);
scale_up = atof (scr.token->str);
}
static void
Cmd_Flags (void)
{
Script_GetToken (&scr, false);
model.flags = atoi (scr.token->str);
}
static void
Cmd_Modelname (void)
{
WriteModel ();
Script_GetToken (&scr, false);
strcpy (outname, scr.token->str);
}
static void
ParseScript (void)
{
while (1) {
do { // look for a line starting with a $ command
if (!Script_GetToken (&scr, true))
return;
if (scr.token->str[0] == '$')
break;
while (Script_TokenAvailable (&scr, false))
Script_GetToken (&scr, false);
} while (1);
if (!strcmp (scr.token->str, "$modelname")) {
Cmd_Modelname ();
} else if (!strcmp (scr.token->str, "$base")) {
Cmd_Base ();
} else if (!strcmp (scr.token->str, "$cd")) {
if (cdset)
Sys_Error ("Two $cd in one model");
cdset = true;
Script_GetToken (&scr, false);
strcpy (cdpartial, scr.token->str);
strcpy (cddir, ExpandPath(scr.token->str));
} else if (!strcmp (scr.token->str, "$sync")) {
model.synctype = ST_SYNC;
} else if (!strcmp (scr.token->str, "$origin")) {
Cmd_Origin ();
} else if (!strcmp (scr.token->str, "$eyeposition")) {
Cmd_Eyeposition ();
} else if (!strcmp (scr.token->str, "$scale")) {
Cmd_ScaleUp ();
} else if (!strcmp (scr.token->str, "$flags")) {
Cmd_Flags ();
} else if (!strcmp (scr.token->str, "$frame")) {
Cmd_Frame (0);
} else if (!strcmp (scr.token->str, "$skin")) {
Cmd_Skin ();
model.numskins++;
} else if (!strcmp (scr.token->str, "$framegroupstart")) {
Cmd_FrameGroupStart ();
model.numframes++;
} else if (!strcmp (scr.token->str, "$skingroupstart")) {
Cmd_SkinGroupStart ();
model.numskins++;
} else {
Sys_Error ("bad command %s\n", scr.token->str);
}
}
}
int
main (int argc, char **argv)
{
int i, bytes;
dstring_t *path;
QFile *file;
char *buf;
if (argc != 2)
Sys_Error ("usage: modelgen file.qc");
i = 1;
// load the script
path = dstring_strdup (argv[i]);
QFS_DefaultExtension (path, ".qc");
SetQdirFromPath (path->str);
file = Qopen (path->str, "rt");
if (!file)
Sys_Error ("couldn't open %s. %s", path->str, strerror(errno));
bytes = Qfilesize (file);
buf = malloc (bytes + 1);
bytes = Qread (file, buf, bytes);
buf[bytes] = 0;
Qclose (file);
Script_Start (&scr, path->str, buf);
// parse it
memset (&model, 0, sizeof (model));
for (i = 0; i < 3; i++) {
framesmins[i] = 9999999;
framesmaxs[i] = -9999999;
}
ClearModel ();
strcpy (outname, argv[1]);
ParseScript ();
WriteModel ();
return 0;
}