/*****************************************************************************
 * $Id: eng_poly16.cc,v 1.24 2006/10/10 04:14:56 devel Exp $
 * @file
 * @author	Jim E. Brooks  http://www.jimbrooks.org
 * @brief	Semi-deprecated 16-bit polygon data struct.
 *//*
 * LEGAL:	COPYRIGHT (C) 2004 JIM E. BROOKS
 *          THIS SOURCE CODE IS RELEASED UNDER THE TERMS
 *          OF THE GNU GENERAL PUBLIC LICENSE (GPL).
 *****************************************************************************/

#define ENG_POLY16_CC 1
#include "eng.hh"
#include "eng_poly16.hh"
#include "eng_polygon.hh"
#include "gfx_texture.hh"

/*****************************************************************************
 * Construct a modern PolygonList object from a legacy Poly16 array.
 * @param	polygonList
 * @param	poly16
 * @param	poly16Elems
 *			Amount of elements, not sizeof(), use ELEMS() macro.
 * @param	poly16tex
 * @param	poly16texElems
 *			Amount of elements, not sizeof(), use ELEMS() macro.
 * @param	colormapRGBA
 *          Required if POLY16_COLORMAP is set on any polygon.
 *          Array of floats.
 * @param	colormapRGBAElems
 *			Amount of elements in a float array (not RGBA elements).
 * @verbatim
 * Overview:
 * - Convert a legacy Poly16 array into a modern PolygonList object.
 *   A PolygonList consists of a list of Polygon objects.
 *   A Polygon contains (and is constructed with) a PolygonAttribs object.
 * - Parse a polygon out of the Poly16 array.
 * - For each polygon:
 *   - Construct a PolygonAttribs object.
 *   - Construct a Polygon object (belonging to PolygonList)
 *     with the PolygonAttribs object.
 *   - Add each texture coordinate to PolygonAttribs object (optional).
 *   - Add each vix to Polygon object (defines the polygon vertexs).
 * @endverbatim
 *****************************************************************************/
void
PolygonListFromPoly16( PolygonList&           polygonList,
                       const Poly16* const    poly16,
                       const uint             poly16Elems,
                       const Poly16tex* const poly16tex,
                       const uint             poly16texElems,
				       const fp* const        colormapRGBA,
                       const uint             colormapRGBAElems )
{
#if DEBUG
	if ( poly16tex != NULL ) ASSERT( poly16texElems > 0 );
	if ( poly16texElems > 0 ) ASSERT( poly16tex != NULL );
#endif

	Texture2Ptr	    texture      = NULL;	// Texture object
	const TexCoord2* texCoords   = NULL;	// TexCoord array
	uint	        texCoordCnt = 0;		// should equal vertexCnt and vixCnt
	RGBA		    rgba;					// polygon's color

	// Traverse the Poly16 array.
	for ( uint idx = 0;; )
	{
		ASSERT( idx < poly16Elems );

		const Poly16 header = poly16[idx];	// polygon header bits

		// No more polygons?
		if ( header & POLY16_LIST_END )
		{
			ASSERT( idx + 1 == poly16Elems );
			break;
		}

		// Index in Poly16 array where array of vix begins.
		const uint idxVixArray = Poly16idxVixArray( poly16, idx );
		const uint vixCnt      = Poly16VixCnt( poly16, idxVixArray, poly16Elems );

		// Prepare to pass texture data to PolygonAttribs.
		if ( header & POLY16_TEXTURE )
		{
			uint texIdx = poly16[ Poly16idxTexture( poly16, idx ) ];
			ASSERT( texIdx < poly16texElems );
			const char* textureName = poly16tex[texIdx].mTextureName;
			texture      = gEng.GetTexture( textureName );
			texCoords	 = poly16tex[texIdx].mCoords;
			texCoordCnt = vixCnt;
		}
		else
		{	
			texture      = NULL;
			texCoordCnt = 0;
		}

		// Derive a PolygonAttribs object from Poly16 header field.
		if ( header & POLY16_COLORMAP )
		{
			uint i = poly16[idx+POLY16_IDX_COLOR]*4;
			ASSERT( i < colormapRGBAElems );
			rgba = RGBA( &colormapRGBA[i] );
		}
		else // RGBA as 4 unsigned bytes
		{
			uint8 rgba32[4];
			POLY16_RGBA32_UNPACK( rgba32, poly16, idx+POLY16_IDX_COLOR ); // endian-neutral
			rgba = RGBA( rgba32 );
		}
		PolygonAttribs* polygonAttribs = new PolygonAttribs( rgba, texture, texCoordCnt );
		polygonAttribs->SetWireframe(   (header & POLY16_WIREFRAME)   != 0 );
		polygonAttribs->SetTranslucent( (header & POLY16_TRANSLUCENT) != 0 );
		polygonAttribs->SetCull(        (header & POLY16_NO_CULL)     == 0 );
		if ( header & POLY16_TEXTURE )
		{
			// This polygon has a texture.
			// Add each texture coordinate to the PolygonAttribs object.
			uint i;
			for ( i = 0; texCoords[i].x != POLY16TEX_END_COORD; ++i )
				polygonAttribs->AddTexCoord( &texCoords[i] );
			ASSERT( i == texCoordCnt );
		}

		// Construct a Polygon object (of the PolygonList) with PolygonAttribs.
		Polygon* const polygon = polygonList.AddPolygon( polygonAttribs, vixCnt );

		// Add each vix (vertex index) to the Polygon object.
		for ( idx = idxVixArray;; )
		{
			polygon->AddVix( poly16[idx] & ~POLY16_END );	// clear terminator bit
			if ( poly16[idx] & POLY16_END ) break;			// the last vix has POLY16_END bit set
			++idx;
		}
		++idx; // past last vix

		delete polygonAttribs;	// shared copy was made, delete original
		//delete polygon;		// PolygonList will delete it
	}
}

/*****************************************************************************
 * Renumber any vix >= by subtracting offs from it.
 * @remarks The sole purpose of this is to compact the old P-51 model's vertex array
 * which has NOVERTEX gaps.  Going forward, new models shouldn't need this.
 * @param	base
 *			Any vix >= is renumbered by subtracting offset.
 * @param	offs
 *			Equals the number of consecutive NOVERTEXs
 * @param	poly16
 *			This will be modified.
 * @param	poly16Elems
 *****************************************************************************/
void
Poly16VixRenumber( const uint base, const uint offs, Poly16* const poly16, const uint poly16Elems )
{
	// Traverse the Poly16 array.
	for ( uint idx = 0;; )
	{
		ASSERT( idx < poly16Elems );
		const Poly16 header = poly16[idx];					// polygon header bits
		if ( header & POLY16_LIST_END ) break;				// no more polyons?
		for ( idx = Poly16idxVixArray( poly16, idx );; )	// for each vertex index
		{
			Vix vix = poly16[idx] & ~POLY16_END;			// clear terminator bit
			if ( vix >= base )
			{
				// subtract number, copy terminator bit
				ASSERT( vix >= offs );						// difference shouldn't be negative
				poly16[idx] = (vix - offs) | (poly16[idx] & POLY16_END);
			}
			if ( poly16[idx] & POLY16_END )					// the last vix has POLY16_END bit set
				break;
			++idx;
		}
		++idx;												// past last vix
	}
}
