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


#include "stdafx.h"
#include "IEditorTexture.h"
#include "MapFace.h"
#include "clipcode.h"


static float g_vert[MAX_CLIPVERT][VERTEXSIZE];
static int g_outCount;


// Quick and dirty sutherland Hodgman clipper
// Clip polygon to decal in texture space
// JAY: This code is lame, change it later.  It does way too much work per frame
// It can be made to recursively call the clipping code and only copy the vertex list once
int Inside( float *vert, int edge )
{
	switch( edge ) {
		case 0:		// left
			if ( vert[3] > 0.0 )
				return 1;
			return 0;
		case 1:		// right
			if ( vert[3] < 1.0 )
				return 1;
			return 0;

		case 2:		// top
			if ( vert[4] > 0.0 )
				return 1;
			return 0;

		case 3:
			if ( vert[4] < 1.0 )
				return 1;
			return 0;
	}
	return 0;
}


void Intersect( float *one, float *two, int edge, float *out )
{
	float t;

	// t is the parameter of the line between one and two clipped to the edge
	// or the fraction of the clipped point between one & two
	// vert[3] is u
	// vert[4] is v
	// vert[0], vert[1], vert[2] is X, Y, Z
	if ( edge < 2 ) {
		if ( edge == 0 ) {	// left
			t = ( (one[3] - 0) / (one[3] - two[3]) );
			out[3] = 0;
		}
		else {				// right
			t = ( (one[3] - 1) / (one[3] - two[3]) );
			out[3] = 1;
		}
		out[4] = one[4] + (two[4] - one[4]) * t;
	}
	else {
		if ( edge == 2 ) {	// top
			t = ( (one[4] - 0)  / (one[4] - two[4]) );
			out[4] = 0;
		}
		else {				// bottom
			t = ( (one[4] - 1) / (one[4] - two[4]) );
			out[4] = 1;
		}
		out[3] = one[3] + (two[3] - one[3]) * t;
	}
	out[0] = one[0] + (two[0] - one[0]) * t;
	out[1] = one[1] + (two[1] - one[1]) * t;
	out[2] = one[2] + (two[2] - one[2]) * t;
}


//-----------------------------------------------------------------------------
// Purpose: 
// Input  : *vert - 
//			vertCount - 
//			*out - 
//			outSize - 
//			edge - 
// Output : int
//-----------------------------------------------------------------------------
int SHClip( float *vert, int vertCount, float *out, int outSize, int edge )
{
	int		j, outCount;
	float	*s, *p;

	outCount = 0;

	s = &vert[ (vertCount-1) * VERTEXSIZE ];
	for ( j = 0; j < vertCount; j++ ) {
		p = &vert[ j * VERTEXSIZE ];
		if ( Inside( p, edge ) ) {
			if ( Inside( s, edge ) ) {
				// Add a vertex and advance out to next vertex
				memcpy( out, p, sizeof(float)*VERTEXSIZE );
				outCount++;
				out += VERTEXSIZE;
			}
			else {
				Intersect( s, p, edge, out );
				out += VERTEXSIZE;
				outCount++;
				memcpy( out, p, sizeof(float)*VERTEXSIZE );
				outCount++;
				out += VERTEXSIZE;
			}
		}
		else {
			if ( Inside( s, edge ) ) {
				Intersect( p, s, edge, out );
				out += VERTEXSIZE;
				outCount++;
			}
		}

		if (outCount >= outSize)
		{
			ASSERT(FALSE);
			break;
		}

		s = p;
	}
				
	return outCount;
}


#define SIN_45_DEGREES ( 0.70710678118654752440084436210485f )

// The world coordinate system is right handed with Z up.
// 
//      ^ Z
//      |
//      |   
//      | 
//X<----|
//       \
//		  \
//         \ Y

//-----------------------------------------------------------------------------
// compute the decal basis based on surface normal, and preferred saxis
//-----------------------------------------------------------------------------

static void R_DecalComputeBasis( Vector const& surfaceNormal, Vector const* pSAxis, 
								 bool flipNormal, Vector* textureSpaceBasis )
{
	// s, t, textureSpaceNormal (T cross S = textureSpaceNormal(N))
	//   N     
	//   \   
	//    \     
	//     \  
	//      |---->S
	//      | 
	//		|  
	//      |T    
	// S = textureSpaceBasis[0]
	// T = textureSpaceBasis[1]
	// N = textureSpaceBasis[2]

	// Get the surface normal.
	textureSpaceBasis[2] = surfaceNormal;
	if (flipNormal)
		VectorNegate( textureSpaceBasis[2] );

	if (pSAxis)
	{
		// T = S cross N
		CrossProduct( *pSAxis, textureSpaceBasis[2], textureSpaceBasis[1] );

		// Name sure they aren't parallel or antiparallel
		// In that case, fall back to the normal algorithm.
		if ( DotProduct( textureSpaceBasis[1], textureSpaceBasis[1] ) > 1e-6 )
		{
			// S = N cross T
			CrossProduct( textureSpaceBasis[2], textureSpaceBasis[1], textureSpaceBasis[0] );

			VectorNormalize( textureSpaceBasis[0] );
			VectorNormalize( textureSpaceBasis[1] );
			return;
		}

		// Fall through to the standard algorithm for parallel or antiparallel
	}

	// floor/ceiling?
	if( fabs( surfaceNormal[2] ) > SIN_45_DEGREES )
	{
		textureSpaceBasis[0][0] = 1.0f;
		textureSpaceBasis[0][1] = 0.0f;
		textureSpaceBasis[0][2] = 0.0f;

		// T = S cross N
		CrossProduct( textureSpaceBasis[0], textureSpaceBasis[2], textureSpaceBasis[1] );

		// S = N cross T
		CrossProduct( textureSpaceBasis[2], textureSpaceBasis[1], textureSpaceBasis[0] );
	}
	// wall
	else
	{
		textureSpaceBasis[1][0] = 0.0f;
		textureSpaceBasis[1][1] = 0.0f;
		textureSpaceBasis[1][2] = -1.0f;

		// S = N cross T
		CrossProduct( textureSpaceBasis[2], textureSpaceBasis[1], textureSpaceBasis[0] );
		// T = S cross N
		CrossProduct( textureSpaceBasis[0], textureSpaceBasis[2], textureSpaceBasis[1] );
	}

	VectorNormalize( textureSpaceBasis[0] );
	VectorNormalize( textureSpaceBasis[1] );
}


//-----------------------------------------------------------------------------
// Purpose: Clips a texture to a face. Used for decal application.
// NOTE	  : HL and HL2 generate texcoords for decals differently!!!
// Input  : pFace -
//			pDecalTex - 
//			org - 
//			pOutPoints - 
// Output : Returns the number of points places in the pOutPoints array.
//-----------------------------------------------------------------------------
int CreateClippedPoly(CMapFace *pFace, IEditorTexture *pDecalTex, Vector& org, vec5_t *pOutPoints, int nOutSize)
{
	float outvert[MAX_CLIPVERT][VERTEXSIZE];
	ASSERT(nOutSize <= MAX_CLIPVERT);			// This code uses temp buffers of this size.

#ifdef SDK_BUILD
	BUG: THIS IS THE HL1 VERSION! SWITCH BETWEEN THESE ALGORITHMS AT RUNTIME
	Vector vecOrg, vecSAxis, vecTAxis;

	// Copy the origin.
	vecOrg = org;

	// Get the U/V axes for this face.
	vecSAxis = pFace->texture.UAxis;
	vecTAxis = pFace->texture.VAxis;

	float decalwidth = pDecalTex->GetWidth();
	float decalheight = pDecalTex->GetHeight();

	float scale = 1.0f;
	IEditorTexture *pFaceTex = pFace->GetTexture();
	float scalex = scale * (float)pFaceTex->GetWidth() / decalwidth;
	float scaley = scale * (float)pFaceTex->GetHeight() / decalheight;

	float u = DotProduct(vecSAxis, vecOrg);
	float v = DotProduct(vecTAxis, vecOrg);

	u -= decalwidth / 2;
	v -= decalheight / 2;

	u /= pFaceTex->GetWidth();
	v /= pFaceTex->GetHeight();

	// Generate texture coordinates for each vertex in decal s,t space
	Vector *pVertex = pFace->Points;
	float curU, curV;
	for (int j = 0; j < pFace->nPoints; j++, pVertex++)
	{
		// Copy X, Y, & Z
		g_vert[j][0] = pVertex[0][0];
		g_vert[j][1] = pVertex[0][1];
		g_vert[j][2] = pVertex[0][2];

		// Get u, v coordinates of vertex in DECAL SPACE
		curU = DotProduct(vecSAxis, *pVertex) / pFaceTex->GetWidth();
		curV = DotProduct(vecTAxis, *pVertex) / pFaceTex->GetHeight();

		// Generate U & V
		g_vert[j][3] = (curU - u) * scalex;		// Decal relative texture coordinates
		g_vert[j][4] = (curV - v) * scaley;
	}
#else
	// THIS IS THE HL2 VERSION!
	float decalScale = pDecalTex->GetDecalScale();
	float decalWidth = pDecalTex->GetWidth();
	float decalHeight = pDecalTex->GetHeight();

	Vector textureSpaceBasis[3];
	
	R_DecalComputeBasis( pFace->plane.normal, NULL,
						 false, textureSpaceBasis );

	float u = DotProduct(textureSpaceBasis[0], org);
	float v = DotProduct(textureSpaceBasis[1], org);

	// subtract the world space dist from the center of the 
	// decal to the origin of the decal
	u -= decalWidth * decalScale / 2.0f;
	v -= decalHeight * decalScale / 2.0f;

	float scalex = 1.0f / ( decalScale * decalWidth );
	float scaley = 1.0f / ( decalScale * decalHeight );

	// Generate texture coordinates for each vertex in decal s,t space
	Vector *pVertex = pFace->Points;
	float curU, curV;
	for (int j = 0; j < pFace->nPoints; j++, pVertex++)
	{
		// Copy X, Y, & Z
		g_vert[j][0] = pVertex[0][0];
		g_vert[j][1] = pVertex[0][1];
		g_vert[j][2] = pVertex[0][2];

		// Get u, v coordinates of vertex in DECAL SPACE
		curU = DotProduct(textureSpaceBasis[0], *pVertex);
		curV = DotProduct(textureSpaceBasis[1], *pVertex);

		// Generate U & V
		g_vert[j][3] = (curU - u) * scalex;		// Decal relative texture coordinates
		g_vert[j][4] = (curV - v) * scaley;
	}
#endif

	// Clip the polygon to the decal texture space
	// FIXME: Yes this realy copies the vertex list 4 times !!
	int nMaxVerts = min(nOutSize, MAX_CLIPVERT);
	g_outCount = SHClip( g_vert[0], pFace->nPoints, outvert[0], nMaxVerts, 0 );	// clip left
	g_outCount = SHClip( outvert[0], g_outCount, g_vert[0], nMaxVerts, 1 );		// clip right
	g_outCount = SHClip( g_vert[0], g_outCount, outvert[0], nMaxVerts, 2 );		// clip top
	g_outCount = SHClip( outvert[0], g_outCount, g_vert[0], nMaxVerts, 3 );		// clip bottom

	memcpy(pOutPoints, g_vert, sizeof(vec5_t) * g_outCount);

	return(g_outCount);
}

