//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose: 
//
// $NoKeywords: $
//
//=============================================================================//

// tristrip - convert triangle list into tristrips and fans

#pragma warning( disable : 4244 )
#pragma warning( disable : 4237 )
#pragma warning( disable : 4305 )


#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>

#include "cmdlib.h"
#include "lbmlib.h"
#include "scriplib.h"
#include "mathlib.h"
#include "..\..\engine\studio.h"
#include "studiomdl.h"

int		used[MAXSTUDIOTRIANGLES];

// the command list holds counts and s/t values that are valid for
// every frame
short	commands[MAXSTUDIOTRIANGLES * 13];
int		numcommands;

// all frames will have their vertexes rearranged and expanded
// so they are in the order expected by the command list

int		allverts, alltris;

int		stripverts[MAXSTUDIOTRIANGLES+2];
int		striptris[MAXSTUDIOTRIANGLES+2];
int		stripcount;

int		neighbortri[MAXSTUDIOTRIANGLES][3];
int		neighboredge[MAXSTUDIOTRIANGLES][3];


s_trianglevert_t (*triangles)[3];
s_mesh_t *pmesh;


void	FindNeighbor (int starttri, int startv)
{
	s_trianglevert_t			m1, m2;
	int			j;
	s_trianglevert_t	*last, *check;
	int			k;

	// used[starttri] |= (1 << startv);

	last = &triangles[starttri][0];

	m1 = last[(startv+1)%3];
	m2 = last[(startv+0)%3];

	for (j=starttri+1, check=&triangles[starttri+1][0] ; j<pmesh->numtris ; j++, check += 3)
	{
		if (used[j] == 7)
			continue;
		for (k=0 ; k<3 ; k++)
		{
			if (memcmp(&check[k],&m1,sizeof(m1)))
				continue;
			if (memcmp(&check[ (k+1)%3 ],&m2,sizeof(m2)))
				continue;

			neighbortri[starttri][startv] = j;
			neighboredge[starttri][startv] = k;

			neighbortri[j][k] = starttri;
			neighboredge[j][k] = startv;

			used[starttri] |= (1 << startv);
			used[j] |= (1 << k);
			return;
		}
	}
}


/*
================
StripLength
================
*/
int	StripLength (int starttri, int startv)
{
	int			j;
	int			k;

	used[starttri] = 2;

	stripverts[0] = (startv)%3;
	stripverts[1] = (startv+1)%3;
	stripverts[2] = (startv+2)%3;

	striptris[0] = starttri;
	striptris[1] = starttri;
	striptris[2] = starttri;
	stripcount = 3;

	while( 1 )
	{
		if (stripcount & 1)
		{
			j = neighbortri[starttri][(startv+1)%3];
			k = neighboredge[starttri][(startv+1)%3];
		}
		else
		{
			j = neighbortri[starttri][(startv+2)%3];
			k = neighboredge[starttri][(startv+2)%3];
		}
		if (j == -1 || used[j])
			goto done;

		stripverts[stripcount] = (k+2)%3;
		striptris[stripcount] = j;
		stripcount++;

		used[j] = 2;

		starttri = j;
		startv = k;
	}

done:

	// clear the temp used flags
	for (j=0 ; j<pmesh->numtris ; j++)
		if (used[j] == 2)
			used[j] = 0;

	return stripcount;
}

/*
===========
FanLength
===========
*/
int	FanLength (int starttri, int startv)
{
	int		j;
	int		k;

	used[starttri] = 2;

	stripverts[0] = (startv)%3;
	stripverts[1] = (startv+1)%3;
	stripverts[2] = (startv+2)%3;

	striptris[0] = starttri;
	striptris[1] = starttri;
	striptris[2] = starttri;
	stripcount = 3;

	while( 1 )
	{
		j = neighbortri[starttri][(startv+2)%3];
		k = neighboredge[starttri][(startv+2)%3];

		if (j == -1 || used[j])
			goto done;

		stripverts[stripcount] = (k+2)%3;
		striptris[stripcount] = j;
		stripcount++;

		used[j] = 2;

		starttri = j;
		startv = k;
	}

done:

	// clear the temp used flags
	for (j=0 ; j<pmesh->numtris ; j++)
		if (used[j] == 2)
			used[j] = 0;

	return stripcount;
}


/*
================
BuildTris

Generate a list of trifans or strips
for the model, which holds for all frames
================
*/
int	numcommandnodes;

int BuildTris (s_trianglevert_t (*x)[3], s_mesh_t *y, byte **ppdata )
{
	int		i, j, k, m;
	int		startv;
	int		len, bestlen, besttype;
	int		bestverts[MAXSTUDIOTRIANGLES];
	int		besttris[MAXSTUDIOTRIANGLES];
	int		peak[MAXSTUDIOTRIANGLES];
	int		type;
	int		total = 0;
	long 	t;
	int		maxlen;

	triangles = x;
	pmesh = y;


	t = time( NULL );

	for (i=0 ; i<pmesh->numtris ; i++)
	{
		neighbortri[i][0] = neighbortri[i][1] = neighbortri[i][2] = -1;
		used[i] = 0;
		peak[i] = pmesh->numtris;
	}

	// printf("finding neighbors\n");
	for (i=0 ; i<pmesh->numtris; i++)
	{
		for (k = 0; k < 3; k++)
		{
			if (used[i] & (1 << k))
				continue;

			FindNeighbor( i, k );
		}
		// printf("%d", used[i] );
	}
	// printf("\n");

	//
	// build tristrips
	//
	numcommandnodes = 0;
	numcommands = 0;
	memset (used, 0, sizeof(used));

	for (i=0 ; i<pmesh->numtris ;)
	{
		// pick an unused triangle and start the trifan
		if (used[i])
		{
			i++;
			continue;
		}

		maxlen = 9999;
		bestlen = 0;
		m = 0;
		for (k = i; k < pmesh->numtris && bestlen < 127; k++)
		{
			int localpeak = 0;

			if (used[k])
				continue;

			if (peak[k] <= bestlen)
				continue;

			m++;
			for (type = 0 ; type < 2 ; type++)
			{
				for (startv =0 ; startv < 3 ; startv++)
				{
					if (type == 1)
						len = FanLength (k, startv);
					else
						len = StripLength (k, startv);
					if (len > 127)
					{
						// skip these, they are too long to encode
					}
					else if (len > bestlen)
					{
						besttype = type;
						bestlen = len;
						for (j=0 ; j<bestlen ; j++)
						{
							besttris[j] = striptris[j];
							bestverts[j] = stripverts[j];
						}
						// printf("%d %d\n", k, bestlen );
					}
					if (len > localpeak)
						localpeak = len;
				}
			}
			peak[k] = localpeak;
			if (localpeak == maxlen)
				break;
		}
		total += (bestlen - 2);

		// printf("%d (%d) %d\n", bestlen, pmesh->numtris - total, i );

		maxlen = bestlen;

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

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

		for (j=0 ; j<bestlen ; j++)
		{
			s_trianglevert_t *tri;

			tri = &triangles[besttris[j]][bestverts[j]];

			commands[numcommands++] = tri->vertindex;
			commands[numcommands++] = tri->normindex;
			commands[numcommands++] = tri->s;
			commands[numcommands++] = tri->t;
		}
		// printf("%d ", bestlen - 2 );
		numcommandnodes++;

		if (t != time(NULL))
		{
			printf("%2d%%\r", (total * 100) / pmesh->numtris );
			t = time(NULL);
		}
	}

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

	*ppdata = (byte *)commands;

	// printf("%d %d %d\n", numcommandnodes, numcommands, pmesh->numtris  );
	return numcommands * sizeof( short );
}

