
#include "dkdata.h"

//=================================================================

typedef struct 
{
	int		numnormals;
	vec3_t	normalsum;
} vertexnormals_t;

typedef struct
{
	vec3_t		v;
	int			lightnormalindex;
} trivert_t;

typedef struct
{
	vec3_t		mins, maxs;
	char		name[16];
	trivert_t	v[MAX_VERTS];
} frame_t;

//================================================================

frame_t		g_frames[MAX_FRAMES];

dmdl_t		model;


float		scale_up = 1.0;		// set by $scale
vec3_t		adjust = {0,0,-24};	// set by $origin
int			g_fixedwidth, g_fixedheight;	// set by $skinsize


//
// base frame info
//
vec3_t		base_xyz[MAX_VERTS];
dstvert_t	base_st[MAX_VERTS];
dtriangle_t	triangles[MAX_TRIANGLES];

int			triangle_st[MAX_TRIANGLES][3][2];

// the command list holds counts, s/t values, and xyz indexes
// that are valid for every frame
int			commands[16384];
int			numcommands;
int			numglverts;
int			used[MAX_TRIANGLES];

char		g_skins[MAX_MD2SKINS][64];

char		cdarchive[1024];
char		cdpartial[1024];
char		cddir[1024];

char		modelname[64];	// empty unless $modelname issued (players)

#define NUMVERTEXNORMALS	162

float	avertexnormals[NUMVERTEXNORMALS][3] = {
#include "anorms.h"
};

FILE	*headerouthandle = NULL;

int		strip_xyz[128];
int		strip_st[128];
int		strip_tris[128];
int		stripcount;
int		polysoutoforder = false;
int		comparedbaseandframe = false;

//==============================================================

/*
===============
ClearModel
===============
*/
void ClearModel (void)
{
	memset (&model, 0, sizeof(model));
	
	memset (&surface, 0, sizeof(extsurf_t) * MAX_SURFACES);

	modelname[0] = 0;
	g_fixedwidth = g_fixedheight = 0;
	g_skipmodel = false;
}


void H_printf(char *fmt, ...)
{
	va_list argptr;
	char	name[1024];

	if (!headerouthandle)
	{
//		sprintf (name, "%s/tris.h", cddir);
		sprintf (name, "tris.h");
		headerouthandle = SafeOpenWrite (name);
//		fprintf(headerouthandle, "// %s\n\n", cddir);
		fprintf(headerouthandle, "// This file generated by dkdata - Do NOT Modify\n\n");
	}

	va_start (argptr, fmt);
	vfprintf (headerouthandle, fmt, argptr);
	va_end (argptr);
}


/*
============
WriteModelFile
============
*/
void WriteModelFile (FILE *modelouthandle)
{
	int				i;
	dmdl_t			modeltemp;
	int				j, k;
	frame_t			*in;
	daliasframe_t	*out;
	byte			buffer[MAX_VERTS*4+128];
	float			v;
	int				c_on, c_off;

	model.ident = IDALIASHEADER;
	model.version = ALIAS_VERSION;
	model.framesize = (int)&((daliasframe_t *)0)->verts[model.num_xyz];
	model.num_glcmds = numcommands;
	model.ofs_skins = sizeof(dmdl_t);
	model.ofs_st = model.ofs_skins + model.num_skins * MAX_SKINNAME;
	model.ofs_tris = model.ofs_st + model.num_st*sizeof(dstvert_t);
	model.ofs_frames = model.ofs_tris + model.num_tris*sizeof(dtriangle_t);
	model.ofs_glcmds = model.ofs_frames + model.num_frames*model.framesize;
	model.ofs_end = model.ofs_glcmds + model.num_glcmds*4;

	model.skinwidth = 0;
	model.skinheight = 0;
	//
	// write out the model header
	//
	for (i=0 ; i<sizeof(dmdl_t)/4 ; i++)
		((int *)&modeltemp)[i] = LittleLong (((int *)&model)[i]);

	SafeWrite (modelouthandle, &modeltemp, sizeof(modeltemp));

	//
	// write out the skin names
	//
	SafeWrite (modelouthandle, g_skins, model.num_skins * MAX_SKINNAME);

	//
	// write out the texture coordinates
	//
	c_on = c_off = 0;
	for (i=0 ; i<model.num_st ; i++)
	{
		base_st[i].s = LittleShort (base_st[i].s);
		base_st[i].t = LittleShort (base_st[i].t);
	}

	SafeWrite (modelouthandle, base_st, model.num_st * sizeof(base_st[0]));

	//
	// write out the triangles
	//
	for (i=0 ; i<model.num_tris ; i++)
	{
		int			j;
		dtriangle_t	tri;

		for (j=0 ; j<3 ; j++)
		{
			tri.index_xyz[j] = LittleShort (triangles[i].index_xyz[j]);
			tri.index_st[j] = LittleShort (triangles[i].index_st[j]);
		}

		SafeWrite (modelouthandle, &tri, sizeof(tri));
	}

	//
	// write out the frames
	//
	for (i=0 ; i<model.num_frames ; i++)
	{
		in = &g_frames[i];
		out = (daliasframe_t *)buffer;

		strcpy (out->name, in->name);
		for (j=0 ; j<3 ; j++)
		{
			out->scale[j] = (in->maxs[j] - in->mins[j])/255;
			out->translate[j] = in->mins[j];
		}

		for (j=0 ; j<model.num_xyz ; j++)
		{
		// all of these are byte values, so no need to deal with endianness
			out->verts[j].lightnormalindex = in->v[j].lightnormalindex;

			for (k=0 ; k<3 ; k++)
			{
			// scale to byte values & min/max check
				v = Q_rint ( (in->v[j].v[k] - out->translate[k]) / out->scale[k] );

			// clamp, so rounding doesn't wrap from 255.6 to 0
				if (v > 255.0)
					v = 255.0;
				if (v < 0)
					v = 0;
				out->verts[j].v[k] = v;
			}
		}

		for (j=0 ; j<3 ; j++)
		{
			out->scale[j] = LittleFloat (out->scale[j]);
			out->translate[j] = LittleFloat (out->translate[j]);
		}

		SafeWrite (modelouthandle, out, model.framesize);
	}

	//
	// write out glcmds
	//
	SafeWrite (modelouthandle, commands, numcommands*4);
}


/*
===============
FinishModel
===============
*/
void FinishModel (char *filename)
{
	int			i;
	FILE		*modelouthandle;
	char		name[1024];
	if (!model.num_frames)
		return;
	
//
// copy to release directory tree if doing a release build
//
/*
	if (g_release)
	{
		if (modelname[0])
			sprintf (name, "%s", modelname);
		else
			sprintf (name, "%s/tris.md2", cdpartial);
		ReleaseFile (name);

		for (i=0 ; i<model.num_skins ; i++)
		{
			ReleaseFile (g_skins[i]);
		}
		model.num_frames = 0;
		return;
	}
*/
	
//
// write the model output file
//
/*
	if (modelname[0])
		sprintf (name, "%s%s", gamedir, modelname);
	else
		sprintf (name, "%s/tris.md2", cddir);
*/
	StripExtension (filename);
	sprintf (name, "%s.md2", filename);
	printf ("saving to %s\n", name);
//	CreatePath (name);
	modelouthandle = SafeOpenWrite (name);

	WriteModelFile (modelouthandle);

//	printf ("%3dx%3d skin\n", model.skinwidth, model.skinheight);
	printf ("Total number of surfaces: %3d\n", lwinfo.num_surfaces);
	for (i = 0; i < lwinfo.num_surfaces; i++)
		printf ("surface(%2d) %3dx%3d skin\n", i, surface[i].skinwidth, surface[i].skinheight);
	printf ("%4d vertexes\n", model.num_xyz);
	printf ("%4d triangles\n", model.num_tris);
	printf ("%4d frame\n", model.num_frames);
	printf ("%4d glverts\n", numglverts);
	printf ("%4d glcmd\n", model.num_glcmds);
	printf ("%4d skins\n", model.num_skins);
	printf ("file size: %d\n", (int)ftell (modelouthandle) );
	printf ("---------------------\n");
	
	fclose (modelouthandle);

	WriteExtraData ();

	// finish writing header file
//	H_printf("\n");

	// scale_up is usefull to allow step distances to be adjusted
//	H_printf("#define MODEL_SCALE\t\t%f\n", scale_up);

//	fclose (headerouthandle);
//	headerouthandle = NULL;
}


/*
=================================================================

ALIAS MODEL DISPLAY LIST GENERATION

=================================================================
*/

/*
================
StripLength
================
*/
int	StripLength (int starttri, int startv)
{
	int			m1, m2;
	int			st1, st2;
	int			j;
	dtriangle_t	*last, *check;
	int			k;
	int			surf;

	used[starttri] = 2;

	last = &triangles[starttri];

	strip_xyz[0] = last->index_xyz[(startv)%3];
	strip_xyz[1] = last->index_xyz[(startv+1)%3];
	strip_xyz[2] = last->index_xyz[(startv+2)%3];
	strip_st[0] = last->index_st[(startv)%3];
	strip_st[1] = last->index_st[(startv+1)%3];
	strip_st[2] = last->index_st[(startv+2)%3];

	strip_tris[0] = starttri;
	stripcount = 1;

	m1 = last->index_xyz[(startv+2)%3];
	st1 = last->index_st[(startv+2)%3];
	m2 = last->index_xyz[(startv+1)%3];
	st2 = last->index_st[(startv+1)%3];

	surf = polygons[starttri+1].surfid;

	// look for a matching triangle
nexttri:
	for (j=starttri+1, check=&triangles[starttri+1]
		; j<model.num_tris ; j++, check++)
	{
		for (k=0 ; k<3 ; k++)
		{
			if (check->index_xyz[k] != m1)
				continue;
			if (check->index_st[k] != st1)
				continue;
			if (check->index_xyz[ (k+1)%3 ] != m2)
				continue;
			if (check->index_st[ (k+1)%3 ] != st2)
				continue;

			// this is the next part of the fan

			// if we can't use this triangle, this tristrip is done
//			if (used[j])
			if (used[j] || polygons[j].surfid != surf)
				goto done;
			
			// the new edge
			if (stripcount & 1)
			{
				m2 = check->index_xyz[ (k+2)%3 ];
				st2 = check->index_st[ (k+2)%3 ];
			}
			else
			{
				m1 = check->index_xyz[ (k+2)%3 ];
				st1 = check->index_st[ (k+2)%3 ];
			}

			strip_xyz[stripcount+2] = check->index_xyz[ (k+2)%3 ];
			strip_st[stripcount+2] = check->index_st[ (k+2)%3 ];
			strip_tris[stripcount] = j;
			stripcount++;

			used[j] = 2;
			goto nexttri;
		}
	}
done:

	// clear the temp used flags
	for (j=starttri+1 ; j<model.num_tris ; j++)
		if (used[j] == 2)
			used[j] = 0;

	return stripcount;
}


/*
===========
FanLength
===========
*/
int	FanLength (int starttri, int startv)
{
	int		m1, m2;
	int		st1, st2;
	int		j;
	dtriangle_t	*last, *check;
	int		k;
	int		surf;

	used[starttri] = 2;

	last = &triangles[starttri];

	strip_xyz[0] = last->index_xyz[(startv)%3];
	strip_xyz[1] = last->index_xyz[(startv+1)%3];
	strip_xyz[2] = last->index_xyz[(startv+2)%3];
	strip_st[0] = last->index_st[(startv)%3];
	strip_st[1] = last->index_st[(startv+1)%3];
	strip_st[2] = last->index_st[(startv+2)%3];

	strip_tris[0] = starttri;
	stripcount = 1;

	m1 = last->index_xyz[(startv+0)%3];
	st1 = last->index_st[(startv+0)%3];
	m2 = last->index_xyz[(startv+2)%3];
	st2 = last->index_st[(startv+2)%3];

	surf = polygons[starttri+1].surfid;

	// look for a matching triangle
nexttri:
	for (j=starttri+1, check=&triangles[starttri+1] 
		; j<model.num_tris ; j++, check++)
	{
		for (k=0 ; k<3 ; k++)
		{
			if (check->index_xyz[k] != m1)
				continue;
			if (check->index_st[k] != st1)
				continue;
			if (check->index_xyz[ (k+1)%3 ] != m2)
				continue;
			if (check->index_st[ (k+1)%3 ] != st2)
				continue;

			// this is the next part of the fan

			// if we can't use this triangle, this tristrip is done
//			if (used[j])
			if (used[j] || polygons[j].surfid != surf)
				goto done;

			// the new edge
			m2 = check->index_xyz[ (k+2)%3 ];
			st2 = check->index_st[ (k+2)%3 ];

			strip_xyz[stripcount+2] = m2;
			strip_st[stripcount+2] = st2;
			strip_tris[stripcount] = j;
			stripcount++;

			used[j] = 2;
			goto nexttri;
		}
	}
done:

	// clear the temp used flags
	for (j=starttri+1 ; j<model.num_tris ; j++)
		if (used[j] == 2)
			used[j] = 0;

	return stripcount;
}



/*
================
BuildGlCmds

Generate a list of trifans or strips
for the model, which holds for all frames
================
*/
void BuildGlCmds (void)
{
	int		i, j, k;
	int		startv;
	float	s, t;
	int		len, bestlen, besttype;
	int		best_xyz[1024];
	int		best_st[1024];
	int		best_tris[1024];
	int		type;

	//
	// build tristrips
	//
	numcommands = 0;
	numglverts = 0;
	memset (used, 0, sizeof(used));
	for (i=0 ; i<model.num_tris ; i++)
	{
		// pick an unused triangle and start the trifan
		if (used[i])
			continue;

		bestlen = 0;
		for (type = 0 ; type < 2 ; type++)
//	type = 1;
		{
			for (startv =0 ; startv < 3 ; startv++)
			{
				if (type == 1)
					len = StripLength (i, startv);
				else
					len = FanLength (i, startv);
				if (len > bestlen)
				{
					besttype = type;
					bestlen = len;
					for (j=0 ; j<bestlen+2 ; j++)
					{
						best_st[j] = strip_st[j];
						best_xyz[j] = strip_xyz[j];
					}
					for (j=0 ; j<bestlen ; j++)
						best_tris[j] = strip_tris[j];
				}
			}
		}

		// mark the tris on the best strip/fan as used
		for (j=0 ; j<bestlen ; j++)
			used[best_tris[j]] = 1;


		if (besttype == 1)
			commands[numcommands++] = (bestlen+2);
		else
			commands[numcommands++] = -(bestlen+2);

		numglverts += bestlen+2;

		for (j=0 ; j<bestlen+2 ; j++)
		{
			// emit a vertex into the reorder buffer
			k = best_st[j];

			// emit s/t coords into the commands stream
			s = base_st[k].s;
			t = base_st[k].t;

//			s = (s + 0.5) / model.skinwidth;
//			t = (t + 0.5) / model.skinheight;
			s = (s + 0.5) / surface[polygons[i].surfid].skinwidth;
			t = (t + 0.5) / surface[polygons[i].surfid].skinheight;

			*(float *)&commands[numcommands++] = s;
			*(float *)&commands[numcommands++] = t;
			*(int *)&commands[numcommands++] = best_xyz[j];
		}
		glsurfpoints[numsurfpoints++] = FindSurfaceFlagForPoly (i);			// surface flags
		glsurfpoints[numsurfpoints++] = FindTextureIndexForPoly (i);		// surface texture
	}

	commands[numcommands++] = 0;		// end of list marker
}


/*
===============================================================

BASE FRAME SETUP

===============================================================
*/

/*
============
BuildST

Builds the triangle_st array for the base frame and
model.skinwidth / model.skinheight

  FIXME: allow this to be loaded from a file for
  arbitrary mappings
============
*/
void BuildST (triangle_t *ptri, int numtri)
{
	int			i, j;
	int			width, height, iwidth, iheight, swidth;
	float		basex, basey;
	float		s_scale, t_scale;
	float		scale;
	vec3_t		mins, maxs;
	float		*pbasevert;
	vec3_t		vtemp1, vtemp2, normal;

	//
	// find bounds of all the verts on the base frame
	//
	ClearBounds (mins, maxs);
	
	for (i=0 ; i<numtri ; i++)
		for (j=0 ; j<3 ; j++)
			AddPointToBounds (ptri[i].verts[j], mins, maxs);
	
	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];

	if (!g_fixedwidth)
	{	// old style
		scale = 8;
		if (width*scale >= 150)
			scale = 150.0 / width;	
		if (height*scale >= 190)
			scale = 190.0 / height;

		s_scale = t_scale = scale;

		iwidth = ceil(width*s_scale);
		iheight = ceil(height*t_scale);

		iwidth += 4;
		iheight += 4;
	}
	else
	{	// new style
		iwidth = g_fixedwidth / 2;
		iheight = g_fixedheight;

		s_scale = (float)(iwidth-4) / width;
		t_scale = (float)(iheight-4) / height;
	}

//
// determine which side of each triangle to map the texture to
//
	for (i=0 ; i<numtri ; i++)
	{
		VectorSubtract (ptri[i].verts[0], ptri[i].verts[1], vtemp1);
		VectorSubtract (ptri[i].verts[2], ptri[i].verts[1], vtemp2);
		CrossProduct (vtemp1, vtemp2, normal);

		if (normal[1] > 0)
		{
			basex = iwidth + 2;
		}
		else
		{
			basex = 2;
		}
		basey = 2;
		
		for (j=0 ; j<3 ; j++)
		{
			pbasevert = ptri[i].verts[j];

//			triangle_st[i][j][0] = Q_rint((pbasevert[0] - mins[0]) * s_scale + basex);
//			triangle_st[i][j][1] = Q_rint((maxs[2] - pbasevert[2]) * t_scale + basey);
			triangle_st[i][j][0] = uvdata[i][j][0] * surface[polygons[i].surfid].skinwidth;
			triangle_st[i][j][1] = uvdata[i][j][1] * surface[polygons[i].surfid].skinheight;
		}
	}


// make the width a multiple of 4; some hardware requires this, and it ensures
// dword alignment for each scan
	swidth = iwidth*2;
	model.skinwidth = (swidth + 3) & ~3;
	model.skinheight = iheight;


/*
	for (i = 0; i < lwinfo.num_surfaces; i++)
	{
		swidth = (surface[i].skinwidth - 1)*2;
		surface[i].skinwidth = (swidth + 3) & ~3;
		surface[i].skinheight = iheight;
	}
*/
}


/*
=================
GrabBase
=================
*/
int GrabBase (char *filename)
{
	triangle_t	*ptri;
	int			i, j, k;
//	int		time1;
//	char	file1[1024];

//	GetToken (false);

	filename = strlwr (filename);

	if (g_skipmodel || g_release || g_archive)
		return 1;

	printf ("---------------------\n");
	printf ("%s\n", filename);

/*
	printf ("---------------------\n");
	sprintf (file1, "%s/%s.%s", cdarchive, token, trifileext);

	ExpandPathAndArchive (file1);

	sprintf (file1, "%s/%s.%s", cddir, token, trifileext);

	time1 = FileTime (file1);
	if (time1 == -1)
		Error ("%s doesn't exist", file1);

//
// load the base triangles
//
	if (do3ds)
		Load3DSTriangleList (file1, &ptri, &model.num_tris);
	else
		LoadTriangleList (file1, &ptri, &model.num_tris);
*/

	if (!LoadLightwaveTriangles (filename, &ptri, &model.num_tris))
		return 0;

	memcpy (base_polygons, polygons, sizeof (base_polygons));

	InitControlObject ();

	LoadSurfaces (filename);

	if (!forceuv)
		LoadUVFromLWO (filename);

	if (!useduvchunk)
		LoadUVFromFile (filename);

	CreateSurfaceData (model.num_tris);

//
// get the ST values
//
	BuildST (ptri, model.num_tris);

//
// 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.num_tris ; i++)
	{
		for (j=0 ; j<3 ; j++)
		{
			// get the xyz index
			for (k=0 ; k<model.num_xyz ; k++)
				if (VectorCompare (ptri[i].verts[j], base_xyz[k]))
					break;	// this vertex is already in the base vertex list

			if (k == model.num_xyz)
			{ // new index
				VectorCopy (ptri[i].verts[j], base_xyz[model.num_xyz]);
				model.num_xyz++;
			}

			triangles[i].index_xyz[j] = k;

			// get the st index
			for (k=0 ; k<model.num_st ; k++)
				if (triangle_st[i][j][0] == base_st[k].s
				&& triangle_st[i][j][1] == base_st[k].t)
					break;	// this vertex is already in the base vertex list

			if (k == model.num_st)
			{ // new index
				base_st[model.num_st].s = triangle_st[i][j][0];
				base_st[model.num_st].t = triangle_st[i][j][1];
				model.num_st++;
			}

			triangles[i].index_st[j] = k;
		}
	}

	// build triangle strips / fans
	BuildGlCmds ();

	AttachPolysToSurfaces ();

	return 1;
}

//===============================================================

char	*FindFrameFile (char *frame)
{
	int			time1;
	char	file1[1024];
	static char	retname[1024];
	char	base[32];
	char	suffix[32];
	char	*s;

	if (strstr (frame, "."))
		return frame;		// allready in dot format

	// split 'run1' into 'run' and '1'
	s = frame + strlen(frame)-1;

	while (s != frame && *s >= '0' && *s <= '9')
		s--;

	strcpy (suffix, s+1);
	strcpy (base, frame);
	base[s-frame+1] = 0;

	// check for 'run1.tri'
	sprintf (file1, "%s/%s%s.%s",cddir, base, suffix, trifileext);
	time1 = FileTime (file1);
	if (time1 != -1)
	{
		sprintf (retname, "%s%s.%s", base, suffix, trifileext);
		return retname;
	}

	// check for 'run.1'
	sprintf (file1, "%s/%s.%s",cddir, base, suffix);
	time1 = FileTime (file1);
	if (time1 != -1)
	{
		sprintf (retname, "%s.%s", base, suffix);
		return retname;
	}

	Error ("frame %s could not be found",frame);
	return NULL;
}

/*
===============
GrabFrame
===============
*/
int GrabFrame (char *frame)
{
	triangle_t	*ptri;
	int			i, j;
	trivert_t	*ptrivert;
	int			num_tris;
//	char		file1[1024];
	frame_t		*fr;
	vertexnormals_t	vnorms[MAX_VERTS];
	int		index_xyz;
//	char	*framefile;

	frame = strlwr (frame);

/*
	// the frame 'run1' will be looked for as either
	// run.1 or run1.tri, so the new alias sequence save
	// feature an be used
	framefile = FindFrameFile (frame);

	sprintf (file1, "%s/%s", cdarchive, framefile);
	ExpandPathAndArchive (file1);

	sprintf (file1, "%s/%s",cddir, framefile);

	printf ("grabbing %s\n", file1);
*/

	if (model.num_frames >= MAX_FRAMES)
		Error ("model.num_frames >= MAX_FRAMES");
	fr = &g_frames[model.num_frames];
	model.num_frames++;

	strcpy (fr->name, frame);
	StripExtension (fr->name);
	ExtractFileBase (fr->name, fr->name);

//	H_printf("#define FRAME_%-16s\t%i\n", fr->name, model.num_frames);

/*
//
// load the frame
//
	if (do3ds)
		Load3DSTriangleList (file1, &ptri, &num_tris);
	else
		LoadTriangleList (file1, &ptri, &num_tris);
*/

	if (!LoadLightwaveTriangles (frame, &ptri, &num_tris))
		return 0;

	if (!comparedbaseandframe)
	{
		for (i = 0; i < controlobject.num_polys; i++)
		{
			if (polysoutoforder)
				break;
			for (j = 0; j < 3; j++)
			{
				if (controlobject.lwpoly[i].index_xyz[j] != lwinfo.lwpoly[i].index_xyz[j])
				{
					polysoutoforder = true;
					break;
				}
			}
		}

		comparedbaseandframe = true;
	}

	if (polysoutoforder)
		ReOrderPolys (ptri, num_tris);

	if (num_tris != model.num_tris)
		Error ("%s: number of triangles doesn't match base frame\n", frame);

	LW_CloseFile ();
//
// allocate storage for the frame's vertices
//
	ptrivert = fr->v;

	for (i=0 ; i<model.num_xyz ; i++)
	{
		vnorms[i].numnormals = 0;
		VectorClear (vnorms[i].normalsum);
	}
	ClearBounds (fr->mins, fr->maxs);

//
// 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<num_tris ; i++)
	{
		vec3_t	vtemp1, vtemp2, normal;
		float	ftemp;

		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, 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++)
		{
			index_xyz = triangles[i].index_xyz[j];

		// rotate the vertices so the model faces down the positive x axis
		// also adjust the vertices to the desired origin
			ptrivert[index_xyz].v[0] = ((-ptri[i].verts[j][1]) * scale_up) +
										adjust[0];
			ptrivert[index_xyz].v[1] = (ptri[i].verts[j][0] * scale_up) +
										adjust[1];
			ptrivert[index_xyz].v[2] = (ptri[i].verts[j][2] * scale_up) +
										adjust[2];

			AddPointToBounds (ptrivert[index_xyz].v, fr->mins, fr->maxs);

			VectorAdd (vnorms[index_xyz].normalsum, normal, vnorms[index_xyz].normalsum);
			vnorms[index_xyz].numnormals++;
		}
	}

//
// calculate the vertex normals, match them to the template list, and store the
// index of the best match
//
	for (i=0 ; i<model.num_xyz ; i++)
	{
		int		j;
		vec3_t	v;
		float	maxdot;
		int		maxdotindex;
		int		c;

		c = vnorms[i].numnormals;
		if (!c)
			Error ("Vertex with no triangles attached");

		VectorScale (vnorms[i].normalsum, 1.0/c, v);
		VectorNormalize (v, 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;
	}

	free (ptri);

	printf ("Frame: %3d Name: %s\n", model.num_frames, fr->name);

	return 1;
}

/*
===============
Cmd_Skin

Skins aren't actually stored in the file, only a reference
is saved out to the header file.
===============
*/
/*
void Cmd_Skin (void)
{
	byte	*palette;
	byte	*pixels;
	int		width, height;
	byte	*cropped;
	int		y;
	char	name[1024], savename[1024];

	GetToken (false);

	if (model.num_skins == MAX_MD2SKINS)
		Error ("model.num_skins == MAX_MD2SKINS");

	if (g_skipmodel)
		return;

	sprintf (name, "%s/%s.lbm", cdarchive, token);
	strcpy (name, ExpandPathAndArchive( name ) );
//	sprintf (name, "%s/%s.lbm", cddir, token);

	if (TokenAvailable())
	{
		GetToken (false);
		sprintf (g_skins[model.num_skins], "%s.pcx", token);
		sprintf (savename, "%s%s.pcx", gamedir, g_skins[model.num_skins]);
	}
	else
	{
		sprintf (savename, "%s/%s.pcx", cddir, token);
		sprintf (g_skins[model.num_skins], "%s/%s.pcx", cdpartial, token);
	}

	model.num_skins++;

	if (g_skipmodel || g_release || g_archive)
		return;

	// load the image
	printf ("loading %s\n", name);
	Load256Image (name, &pixels, &palette, &width, &height);
	RemapZero (pixels, palette, width, height);

	// crop it to the proper size
	cropped = malloc (model.skinwidth*model.skinheight);
	for (y=0 ; y<model.skinheight ; y++)
	{
		memcpy (cropped+y*model.skinwidth,
			pixels+y*width, model.skinwidth);
	}

	// save off the new image
	printf ("saving %s\n", savename);
	CreatePath (savename);
	WritePCXfile (savename, cropped, model.skinwidth,
		model.skinheight, palette);

	free (pixels);
	free (palette);
	free (cropped);
}
*/

/*
=================
DK_Cmd_Base
=================
*/
void DK_Cmd_Base (void)
{
	printf ("Obsolete command. Use -lgen [directoryname]\n");
}

/*
===============
DK_Cmd_Frame	
===============
*/
void DK_Cmd_Frame (void)
{
	printf ("Obsolete command. Use -lgen [directoryname]\n");
}


/*
===============
DK_Cmd_Skin

Skins aren't actually stored in the file, only a reference
is saved out to the header file.
===============
*/
void DK_Cmd_Skin (void)
{
	printf ("Not supported\n");
}


/*
=================
DK_Cmd_Origin
=================
*/
void DK_Cmd_Origin (char *x, char *y, char *z)
{
	adjust[1] = -atof (x);
	adjust[0] = atof (y);
	adjust[2] = -atof (z);

	printf ("Origin adjust [x] : %f\n", adjust[1]);
	printf ("Origin adjust [y] : %f\n", adjust[0]);
	printf ("Origin adjust [z] : %f\n", adjust[2]);
}


/*
=================
DK_Cmd_ScaleUp
=================
*/
void DK_Cmd_ScaleUp (char *scale)
{
	scale_up = atof (scale);
	printf ("Scale factor : %f\n", scale_up);
}


/*
=================
DK_Cmd_Skinsize

Set a skin size other than the default
=================
*/
void DK_Cmd_Skinsize (void)
{
	printf ("Not supported\n");
}

/*
=================
DK_Cmd_Modelname

Gives a different name/location for the file, instead of the cddir
=================
*/
void DK_Cmd_Modelname (char *name)
{
	strcpy (modelname, name);
}

/*
===============
DK_Cmd_Cd
===============
*/
void DK_Cmd_Cd (void)
{
	printf ("Obsolete command\n");
}

/*
===============
NonRecurseGrab
===============
*/
void NonRecurseGrab (char *name)
{
	char	filename[1024];

	if (name == NULL)
		Error ("Must supply a directory name\n");

	ClearModel ();

	strcpy (modelname, name);

	sprintf (filename, "%s/%s", name, name);

	if (!GrabBase (filename))
		return;
	
	ExtractFileBase (filename, filename);

	if (!DoDirectoryWork (name, "*.lwo", filename, GrabFrame))
	{
		printf ("lgen : failed to complete task\n");
		return;
	}

	FinishModel (modelname);
}

/*
===============
DK_LGen_Recurse
===============
*/
void RecurseGrab (char *name)
{
	HANDLE			dirHandle;
	WIN32_FIND_DATA	findData;
	int				found = 1;

	dirHandle = FindFirstFile("*.*", &findData);

	if (dirHandle == INVALID_HANDLE_VALUE)
		return;

	while (found)
	{
		// if this is a direcory then recuruse
		if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
			if (findData.cFileName[0] != '.')
				NonRecurseGrab (findData.cFileName);

		found = FindNextFile(dirHandle, &findData);
	}

	FindClose(dirHandle);
}

/*
===============
DK_Cmd_LGen
===============
*/
void DK_Cmd_LGen (char *name)
{
//	if (recurse)
//		RecurseGrab (name);
//	else
		NonRecurseGrab (name);
}
