
///////////////////////////////////////////////////////////////////////////////
//
// File   : Asura_DebugLineRender.cpp
// Author : Andy Tate
// Date   : 22-Jan-2007
//
// Copyright (C) 2007 Rebellion, All Rights Reserved.
//
///////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// Include files...
////////////////////////////////////////////////////////////////////////////////

#include "Core/Asura.h"

#if !defined(ASURA_NO_DEBUG_OUTPUT)

// This file's header...
#include "Debugging/Asura_DebugLineRender.h"

// Asura independent header-files...
#include "Core/Asura_Timers.h"
#include "Rendering/Asura_Frustum.h"
#include "Rendering/Asura_Render.h"


////////////////////////////////////////////////////////////////////////////////
// Constant declarations...
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// Static variable declarations...
////////////////////////////////////////////////////////////////////////////////

Asura_DebugLineRender_LineSet*	Asura_DebugLineRender::s_pxFirstLineSet		= NULL;
Asura_DebugLineRender_LineSet*	Asura_DebugLineRender::s_pxOneShotLineSet	= NULL;


////////////////////////////////////////////////////////////////////////////////
// Private functions...
////////////////////////////////////////////////////////////////////////////////


////////////////////////////////////////////////////////////////////////////////
// Protected functions...
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_DebugLineRender_LineSet::Render
// Author       : Andy Tate
// Date         : 22-Jan-2007
////////////////////////////////////////////////////////////////////////////////

void Asura_DebugLineRender_LineSet::Render (const float fAlpha)
{
	// Render ALL of the single-lines...
	for(u_int u = 0; u < m_uNumLines; u++)
	{
		m_pxSingleLines[u].Render(fAlpha);
	}
}

/// End of function 'Asura_DebugLineRender_LineSet::Render'. ///



////////////////////////////////////////////////////////////////////////////////
// Public functions...
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_DebugLineRender_SingleLine::Render
// Author       : Andy Tate
// Date         : 23-Jan-2007
////////////////////////////////////////////////////////////////////////////////

void Asura_DebugLineRender_SingleLine::Render (const float fAlpha) const
{
	const u_int	uRGB	= m_uARGB & 0x00FFFFFF;
	const u_int	uAlpha	= Asura_Maths::FloatToInt(((m_uARGB & 0xFF000000) >> 24) * fAlpha);
	const u_int	uARGB	= uRGB + (uAlpha << 24);

	Asura_Render::RenderLine(m_xStart, m_xEnd, uARGB);
}

/// End of function 'Asura_DebugLineRender_SingleLine::Render'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_DebugLineRender_LineSet::Asura_DebugLineRender_LineSet
// Author       : Andy Tate
// Date         : 23-Jan-2007
////////////////////////////////////////////////////////////////////////////////

Asura_DebugLineRender_LineSet::Asura_DebugLineRender_LineSet (const u_int uMaxLines)
: m_uNumLines(0)
, m_uMaxLines(uMaxLines)
, m_pxNextLineSet(NULL)
{
	m_pxSingleLines	= new Asura_DebugLineRender_SingleLine [uMaxLines];
}

/// End of function 'Asura_DebugLineRender_LineSet::Asura_DebugLineRender_LineSet'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_DebugLineRender_LineSet::~Asura_DebugLineRender_LineSet
// Author       : Andy Tate
// Date         : 23-Jan-2007
////////////////////////////////////////////////////////////////////////////////

Asura_DebugLineRender_LineSet::~Asura_DebugLineRender_LineSet ()
{
	delete[](m_pxSingleLines);
}

/// End of function 'Asura_DebugLineRender_LineSet::~Asura_DebugLineRender_LineSet'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_DebugLineRender_LineSet::AddLine
// Author       : Andy Tate
// Date         : 22-Jan-2007
////////////////////////////////////////////////////////////////////////////////

u_int Asura_DebugLineRender_LineSet::AddLine (const Asura_Vector_3& xStart,
											  const Asura_Vector_3& xEnd,
											  const u_int uARGB)
{
	if(m_uNumLines < m_uMaxLines)
	{
		const Asura_DebugLineRender_SingleLine	xLine(xStart, xEnd, uARGB);

		m_pxSingleLines[m_uNumLines++]	= xLine;
		return(1);
	}
	else
	{
		Asura_Assert(false, "Run out of single-lines for debugging.");
		return(0);
	}
}

/// End of function 'Asura_DebugLineRender_LineSet::AddLine'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_DebugLineRender_LineSet::AddLine
// Author       : Andy Tate
// Date         : 22-Jan-2007
////////////////////////////////////////////////////////////////////////////////

u_int Asura_DebugLineRender_LineSet::AddLine (const Asura_AVector3& xStart,
											  const Asura_AVector3& xEnd,
											  const u_int uARGB)
{
	if(m_uNumLines < m_uMaxLines)
	{
		const Asura_DebugLineRender_SingleLine	xLine(xStart, xEnd, uARGB);

		m_pxSingleLines[m_uNumLines++]	= xLine;
		return(1);
	}
	else
	{
		Asura_Assert(false, "Run out of single-lines for debugging.");
		return(0);
	}
}

/// End of function 'Asura_DebugLineRender_LineSet::AddLine'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_DebugLineRender_LineSet::AddArrow
// Author       : Andy Tate
// Date         : 16-Aug-2007
////////////////////////////////////////////////////////////////////////////////

u_int Asura_DebugLineRender_LineSet::AddArrow(const Asura_Vector_3& xStart,
											  const Asura_Vector_3& xEnd,
											  const u_int uARGB,
											  const float fHeadProportion,
											  const float fCrossSize)
{
	Asura_AVector3	xDirection(xEnd - xStart);

	const float		fLength		= xDirection.NormaliseReturningMagnitude();
	u_int			uNumLines	= 0;

	Asura_AVector3	xHeadStart;
	Asura_AVector3	xXAxis;
	Asura_AVector3	xYAxis;

	// Calculate which axis our arrow-head is on...
	xHeadStart.Lerp(Asura_AVector3(xEnd), Asura_AVector3(xStart), fHeadProportion);

	if(Asura_Maths::Abs(xDirection.GetY()) > 0.707f)
	{
		xXAxis	= xDirection ^ Asura_AVector3(0.0f, 0.0f, 1.0f);
		xYAxis	= xXAxis ^ xDirection;
	}
	else
	{
		xXAxis	= xDirection ^ Asura_AVector3(0.0f, 1.0f, 0.0f);
		xYAxis	= xXAxis ^ xDirection;
	}

	// Now add the lines to be rendered...
	if(fCrossSize > 0.0f)
	{
		uNumLines	+= AddLine(Asura_Vector_3(xStart - Asura_Vector_3(xDirection * fCrossSize)), xEnd, uARGB);

		uNumLines	+= AddLine(Asura_Vector_3(xStart - Asura_Vector_3(xXAxis * fCrossSize)), Asura_Vector_3(xStart + Asura_Vector_3(xXAxis * fCrossSize)), uARGB);
		uNumLines	+= AddLine(Asura_Vector_3(xStart - Asura_Vector_3(xYAxis * fCrossSize)), Asura_Vector_3(xStart + Asura_Vector_3(xYAxis * fCrossSize)), uARGB);
	}
	else
	{
		uNumLines	+= AddLine(xStart, xEnd, uARGB);
	}

	uNumLines	+= AddLine(Asura_Vector_3(xHeadStart + (xXAxis * (fLength * fHeadProportion))), xEnd, uARGB);
	uNumLines	+= AddLine(Asura_Vector_3(xHeadStart - (xXAxis * (fLength * fHeadProportion))), xEnd, uARGB);
	uNumLines	+= AddLine(Asura_Vector_3(xHeadStart + (xYAxis * (fLength * fHeadProportion))), xEnd, uARGB);
	uNumLines	+= AddLine(Asura_Vector_3(xHeadStart - (xYAxis * (fLength * fHeadProportion))), xEnd, uARGB);

	return(uNumLines);
}


u_int Asura_DebugLineRender_LineSet::AddArrow(const Asura_AVector3& xStart,
											  const Asura_AVector3& xEnd,
											  const u_int uARGB,
											  const float fHeadProportion,
											  const float fCrossSize)
{
	Asura_AVector3	xDirection(xEnd - xStart);

	const float		fLength		= xDirection.NormaliseReturningMagnitude();
	u_int			uNumLines	= 0;

	Asura_AVector3	xHeadStart;
	Asura_AVector3	xXAxis;
	Asura_AVector3	xYAxis;

	// Calculate which axis our arrow-head is on...
	xHeadStart.Lerp(xEnd, xStart, fHeadProportion);

	if(Asura_Maths::Abs(xDirection.GetY()) > 0.707f)
	{
		xXAxis	= xDirection ^ Asura_AVector3(0.0f, 0.0f, 1.0f);
		xYAxis	= xXAxis ^ xDirection;
	}
	else
	{
		xXAxis	= xDirection ^ Asura_AVector3(0.0f, 1.0f, 0.0f);
		xYAxis	= xXAxis ^ xDirection;
	}

	// Now add the lines to be rendered...
	if(fCrossSize > 0.0f)
	{
		uNumLines	+= AddLine( xStart - ( xDirection * fCrossSize ), xEnd, uARGB );

		uNumLines	+= AddLine( xStart - ( xXAxis * fCrossSize ), xStart + ( xXAxis * fCrossSize ), uARGB );
		uNumLines	+= AddLine( xStart - ( xYAxis * fCrossSize ), xStart + ( xYAxis * fCrossSize ), uARGB );
	}
	else
	{
		uNumLines	+= AddLine( xStart, xEnd, uARGB );
	}

	uNumLines	+= AddLine(xHeadStart + (xXAxis * (fLength * fHeadProportion)), xEnd, uARGB);
	uNumLines	+= AddLine(xHeadStart - (xXAxis * (fLength * fHeadProportion)), xEnd, uARGB);
	uNumLines	+= AddLine(xHeadStart + (xYAxis * (fLength * fHeadProportion)), xEnd, uARGB);
	uNumLines	+= AddLine(xHeadStart - (xYAxis * (fLength * fHeadProportion)), xEnd, uARGB);

	return(uNumLines);
}

/// End of function 'Asura_DebugLineRender_LineSet::AddArrow'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_DebugLineRender_LineSet::AddAxis
// Author       : Andy Tate
// Date         : 22-Jan-2007
////////////////////////////////////////////////////////////////////////////////

u_int Asura_DebugLineRender_LineSet::AddAxis (const Asura_Vector_3& xPosition,
											  const Asura_Matrix_3x3& xOrientation,
											  const float fSize,
											  const float fAlpha)
{
	Asura_Vector_3	xXAxis (xOrientation.e11, xOrientation.e21, xOrientation.e31);
	Asura_Vector_3	xYAxis (xOrientation.e12, xOrientation.e22, xOrientation.e32);
	Asura_Vector_3	xZAxis (xOrientation.e13, xOrientation.e23, xOrientation.e33);

	const u_int		uAlpha		= static_cast<u_int>(Asura_Maths::FloatToInt(255.0f * fAlpha)) << 24;
	u_int			uNumLines	= 0;

	// Scale using the size...
	xXAxis	*= fSize;
	xYAxis	*= fSize;
	xZAxis	*= fSize;

	// Now add the lines to be rendered...
	uNumLines	+= AddArrow(Asura_Vector_3(xPosition - xXAxis), Asura_Vector_3(xPosition + xXAxis), uAlpha | 0x00FF0000);
	uNumLines	+= AddArrow(Asura_Vector_3(xPosition - xYAxis), Asura_Vector_3(xPosition + xYAxis), uAlpha | 0x0000FF00);
	uNumLines	+= AddArrow(Asura_Vector_3(xPosition - xZAxis), Asura_Vector_3(xPosition + xZAxis), uAlpha | 0x000000FF);

	return(uNumLines);
}

u_int Asura_DebugLineRender_LineSet::AddAxis (const Asura_AVector3& xPosition,
											  const Asura_AMatrix3x3& xOrientation,
											  const float fSize,
											  const float fAlpha)
{
	Asura_AVector3	xXAxis (xOrientation.GetColumn0());
	Asura_AVector3	xYAxis (xOrientation.GetColumn1());
	Asura_AVector3	xZAxis (xOrientation.GetColumn2());

	const u_int		uAlpha		= static_cast<u_int>(Asura_Maths::FloatToInt(255.0f * fAlpha)) << 24;
	u_int			uNumLines	= 0;

	// Scale using the size...
	xXAxis	*= fSize;
	xYAxis	*= fSize;
	xZAxis	*= fSize;

	// Now add the lines to be rendered...
	uNumLines	+= AddArrow(xPosition - xXAxis, xPosition + xXAxis, uAlpha | 0x00FF0000);
	uNumLines	+= AddArrow(xPosition - xYAxis, xPosition + xYAxis, uAlpha | 0x0000FF00);
	uNumLines	+= AddArrow(xPosition - xZAxis, xPosition + xZAxis, uAlpha | 0x000000FF);

	return(uNumLines);
}

/// End of function 'Asura_DebugLineRender_LineSet::AddAxis'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_DebugLineRender_LineSet::AddCross
// Author       : Andy Tate
// Date         : 16-Aug-2007
////////////////////////////////////////////////////////////////////////////////

u_int Asura_DebugLineRender_LineSet::AddCross(const Asura_Vector_3& xPosition,
											  const u_int uARGB,
											  const float fSize)
{
	const Asura_Vector_3	xXAxis(fSize, 0.0f,  0.0f);
	const Asura_Vector_3	xYAxis(0.0f,  fSize, 0.0f);
	const Asura_Vector_3	xZAxis(0.0f,  0.0f,  fSize);

	u_int					uNumLines	= 0;

	// Now add the lines to be rendered...
	uNumLines	+= AddLine(Asura_Vector_3(xPosition + xXAxis), Asura_Vector_3(xPosition - xXAxis), uARGB);
	uNumLines	+= AddLine(Asura_Vector_3(xPosition + xYAxis), Asura_Vector_3(xPosition - xYAxis), uARGB);
	uNumLines	+= AddLine(Asura_Vector_3(xPosition + xZAxis), Asura_Vector_3(xPosition - xZAxis), uARGB);

	return(uNumLines);
}

/// End of function 'Asura_DebugLineRender_LineSet::AddCross'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_DebugLineRender_LineSet::AddCylinder
// Author       : Andy Tate
// Date         : 29-Feb-2008
////////////////////////////////////////////////////////////////////////////////

u_int Asura_DebugLineRender_LineSet::AddCylinder(const Asura_Vector_3& xPosition,
												 const Asura_Quat& xOrientation,
												 const float fRadius,
												 const float fHeight,
												 const u_int uARGB,
												 const u_int uNumSegments)
{
	if(uNumSegments == 0)
	{
		return(0);
	}
	else
	{
		const Asura_AVector3	xOldBottomVertex(0.0f, 0.0f, fRadius);
		const Asura_AVector3	xOldTopVertex(0.0f, fHeight, fRadius);

		Asura_AVector3			xTransformedOldBottomVertex;
		Asura_AVector3			xTransformedOldTopVertex;

		u_int					uNumLines	= 0;

		// Generate the initial vertices...
		xTransformedOldBottomVertex = xOrientation.RotateVector(xOldBottomVertex);
		xTransformedOldTopVertex = xOrientation.RotateVector(xOldTopVertex);

		xTransformedOldBottomVertex	+= Asura_AVector3(xPosition);
		xTransformedOldTopVertex	+= Asura_AVector3(xPosition);

		// Generate all vertices (both top an bottom of the cylinder)...
		for (u_int uSegment = 1; uSegment <= uNumSegments; uSegment++)
		{
			const Asura_Angle		xAngle	= (Asura_Maths::Deg360 * static_cast<float>(uSegment)) / uNumSegments;
			const float				fX		= fRadius * Asura_Maths::Sine(xAngle);
			const float				fZ		= fRadius * Asura_Maths::Cosine(xAngle);

			const Asura_AVector3	xNewBottomVertex(fX, 0.0f, fZ);
			const Asura_AVector3	xNewTopVertex(fX, fHeight, fZ);

			Asura_AVector3			xTransformedNewBottomVertex;
			Asura_AVector3			xTransformedNewTopVertex;

			// Generate the new vertices...
			xTransformedNewBottomVertex = xOrientation.RotateVector(xNewBottomVertex);
			xTransformedNewTopVertex = xOrientation.RotateVector(xNewTopVertex);

			xTransformedNewBottomVertex	+= Asura_AVector3(xPosition);
			xTransformedNewTopVertex	+= Asura_AVector3(xPosition);

			// Render the lines...
			uNumLines	+= AddLine(xTransformedOldBottomVertex, xTransformedNewBottomVertex, uARGB);
			uNumLines	+= AddLine(xTransformedNewBottomVertex, xTransformedNewTopVertex, uARGB);
			uNumLines	+= AddLine(xTransformedNewTopVertex, xTransformedOldTopVertex, uARGB);

			// Step onto the next vertex...
			xTransformedOldBottomVertex	= xTransformedNewBottomVertex;
			xTransformedOldTopVertex	= xTransformedNewTopVertex;
		}

		return(uNumLines);
	}
}

/// End of function 'Asura_DebugLineRender_LineSet::AddCylinder'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_DebugLineRender_LineSet::AddBoundingBox
// Author       : Andy Tate
// Date         : 22-Jan-2007
////////////////////////////////////////////////////////////////////////////////

u_int Asura_DebugLineRender_LineSet::AddBoundingBox (const Asura_Bounding_Box& xBB,
													 const Asura_Vector_3& xPosition,
													 const Asura_Matrix_3x3& xOrientation,
													 const u_int uARGB)
{
	const u_short	ausIndices[24] =
	{
		0, 2, 2, 3, 3, 1, 1, 0,
		0, 4, 2, 6, 3, 7, 1, 5,
		4, 6, 6, 7, 7, 5, 5, 4,
	};

	u_int			uNumLines	= 0;

	Asura_Vector_3	axVertices[8];

	// Calculate the vertices...
	for(u_int u = 0; u < 8; u++)
	{
		axVertices[u][0]	= ((u & 1) == 0) ? xBB.MinX : xBB.MaxX;
		axVertices[u][1]	= ((u & 2) == 0) ? xBB.MinY : xBB.MaxY;
		axVertices[u][2]	= ((u & 4) == 0) ? xBB.MinZ : xBB.MaxZ;

		axVertices[u]	= (axVertices[u] * xOrientation) + xPosition;
	}

	// Add them to be rendered...
	for(u_int u = 0; u < 24; u += 2)
	{
		const Asura_Vector_3&	xStart	= axVertices[ausIndices[u]];
		const Asura_Vector_3&	xEnd	= axVertices[ausIndices[u + 1]];

		uNumLines	+= AddLine(xStart, xEnd, uARGB);
	}

	return(uNumLines);
}

/// End of function 'Asura_DebugLineRender_LineSet::AddBoundingBox'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_DebugLineRender_LineSet::AddFrustum
// Author       : Andy Tate
// Date         : 22-Jan-2007
////////////////////////////////////////////////////////////////////////////////

u_int Asura_DebugLineRender_LineSet::AddFrustum (const Asura_Frustum& xFrustum,
												 const Asura_Vector_3& xPosition,
												 const Asura_Matrix_3x3& xOrientation,
												 const u_int uARGB)
{
	Asura_Vector_3	axVertices[Asura_Frustum::MAX_VERTICES];

	u_int			uNumLines	= 0;

	// Generate the vertices...
	xFrustum.GenerateVertices(axVertices, xPosition, xOrientation);

	// Now add the lines to be rendered...
	uNumLines	+= AddLine(axVertices [0], axVertices [1], uARGB);
	uNumLines	+= AddLine(axVertices [1], axVertices [3], uARGB);
	uNumLines	+= AddLine(axVertices [3], axVertices [2], uARGB);
	uNumLines	+= AddLine(axVertices [2], axVertices [0], uARGB);

	uNumLines	+= AddLine(axVertices [4], axVertices [5], uARGB);
	uNumLines	+= AddLine(axVertices [5], axVertices [7], uARGB);
	uNumLines	+= AddLine(axVertices [7], axVertices [6], uARGB);
	uNumLines	+= AddLine(axVertices [6], axVertices [4], uARGB);

	uNumLines	+= AddLine(axVertices [0], axVertices [4], uARGB);
	uNumLines	+= AddLine(axVertices [1], axVertices [5], uARGB);
	uNumLines	+= AddLine(axVertices [2], axVertices [6], uARGB);
	uNumLines	+= AddLine(axVertices [3], axVertices [7], uARGB);

	return(uNumLines);
}

/// End of function 'Asura_DebugLineRender_LineSet::AddFrustum'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_DebugLineRender_LineSet::AddOBB
// Author       : Andy Tate
// Date         : 31-Aug-2007
////////////////////////////////////////////////////////////////////////////////

u_int Asura_DebugLineRender_LineSet::AddOBB(const Asura_OBB& xOBB, const u_int uARGB)
{
	const float				fX				= xOBB.GetHalfExtentX();
	const float				fY				= xOBB.GetHalfExtentY();
	const float				fZ				= xOBB.GetHalfExtentZ();
	const Asura_AMatrix3x3&	xOrientation	= xOBB.GetOrientation();
	const Asura_AVector3&	xCentre			= xOBB.GetCentre();

	u_int					uNumLines		= 0;

	Asura_AVector3			axVertices[8];

	// Generate the vertices...
	for(u_int u = 0; u < 8; u++)
	{
		const Asura_AVector3	xVertex((((u & 1) == 0) ? fX : -fX), (((u & 2) == 0) ? fY : -fY), (((u & 4) == 0) ? fZ : -fZ));

		axVertices[u]	= (xVertex * xOrientation) + xCentre;
	}

	// Now add the lines to be rendered...
	uNumLines	+= AddLine(axVertices [0], axVertices [1], uARGB);
	uNumLines	+= AddLine(axVertices [1], axVertices [3], uARGB);
	uNumLines	+= AddLine(axVertices [3], axVertices [2], uARGB);
	uNumLines	+= AddLine(axVertices [2], axVertices [0], uARGB);

	uNumLines	+= AddLine(axVertices [4], axVertices [5], uARGB);
	uNumLines	+= AddLine(axVertices [5], axVertices [7], uARGB);
	uNumLines	+= AddLine(axVertices [7], axVertices [6], uARGB);
	uNumLines	+= AddLine(axVertices [6], axVertices [4], uARGB);

	uNumLines	+= AddLine(axVertices [0], axVertices [4], uARGB);
	uNumLines	+= AddLine(axVertices [1], axVertices [5], uARGB);
	uNumLines	+= AddLine(axVertices [2], axVertices [6], uARGB);
	uNumLines	+= AddLine(axVertices [3], axVertices [7], uARGB);

	return(uNumLines);
}

/// End of function 'Asura_DebugLineRender_LineSet::AddOBB'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_DebugLineRender_LineSet::AddSphere
// Author       : Andy Tate
// Date         : 22-Jan-2007
////////////////////////////////////////////////////////////////////////////////

u_int Asura_DebugLineRender_LineSet::AddSphere (const Asura_Vector_3& xPosition,
												const float fRadius,
												const u_int uARGB,
												const u_int uNumSegments,
												const u_int uNumSlices)
{
	if (uNumSegments == 0 || uNumSlices == 0)
	{
		return 0;
	}

	Asura_Vector_3*	pxVertices	= new Asura_Vector_3[uNumSegments * (uNumSlices + 1)];
	Asura_Vector_3*	pxVertex	= pxVertices;
	u_int			uNumLines	= 0;

	// Calculate the vertices one slice at a time...
	for (u_int iSlice = 0; iSlice <= uNumSlices; iSlice++)
	{
		const Asura_Angle	xSliceAngle		= (Asura_Maths::Deg180 * static_cast<float>(iSlice)) / uNumSlices;
		const float			fSliceRadius	= fRadius * Asura_Maths::Sine(xSliceAngle);
		const float			fY				= xPosition.y + (fRadius * Asura_Maths::Cosine(xSliceAngle));

		// ...and one segment at a time...
		for (u_int iSegment = 0; iSegment < uNumSegments; iSegment++)
		{
			const Asura_Angle	xAngle	= (Asura_Maths::Deg360 * static_cast<float>(iSegment)) / uNumSegments;
			const float			fX		= xPosition.x + (fSliceRadius * Asura_Maths::Sine(xAngle));
			const float			fZ		= xPosition.z + (fSliceRadius * Asura_Maths::Cosine(xAngle));

			// Write this vertex...
			pxVertex->x	= fX;
			pxVertex->y	= fY;
			pxVertex->z	= fZ;

			// Next vertex...
			pxVertex++;
		}
	}

	// Add the slices to be rendered...
	for (u_int iSlice = 1; iSlice < uNumSlices; iSlice++)
	{
		for (u_int iSegment = 0; iSegment < uNumSegments; iSegment++)
		{
			const int	iStartVertex	= (iSlice * uNumSegments) + iSegment;
			const int	iEndVertex		= (iSlice * uNumSegments) + ((iSegment + 1) % uNumSegments);

			uNumLines	+= AddLine(pxVertices[iStartVertex], pxVertices[iEndVertex], uARGB);
		}
	}

	// Add the segments to be rendered...
	for (u_int iSegment = 0; iSegment < uNumSegments; iSegment++)
	{
		for (u_int iSlice = 0; iSlice < uNumSlices; iSlice++)
		{
			const int	iStartVertex	= (iSlice * uNumSegments) + iSegment;
			const int	iEndVertex		= ((iSlice + 1) * uNumSegments) + iSegment;

			uNumLines	+= AddLine(pxVertices[iStartVertex], pxVertices[iEndVertex], uARGB);
		}
	}

	// Finally, destroy the vertices...
	delete[](pxVertices);
	return(uNumLines);
}

/// End of function 'Asura_DebugLineRender_LineSet::AddSphere'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_DebugLineRender_OneShotLineSet::Update
// Author       : Andy Tate
// Date         : 23-Jan-2007
////////////////////////////////////////////////////////////////////////////////

void Asura_DebugLineRender_OneShotLineSet::Update (const float fTimeStep)
{
}

/// End of function 'Asura_DebugLineRender_OneShotLineSet::Update'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_DebugLineRender_OneShotLineSet::Render
// Author       : Andy Tate
// Date         : 23-Jan-2007
////////////////////////////////////////////////////////////////////////////////

void Asura_DebugLineRender_OneShotLineSet::Render (void)
{
	// Perform the render...
	PARENT::Render(1.0f);

	// ...then reset the line-set for the next frame...
	Reset();
}

/// End of function 'Asura_DebugLineRender_OneShotLineSet::Render'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_DebugLineRender_TimedLineSet::Update
// Author       : Andy Tate
// Date         : 23-Jan-2007
////////////////////////////////////////////////////////////////////////////////

void Asura_DebugLineRender_TimedLineSet::Update (const float fTimeStep)
{
	if(m_fTimeLeft > 0.0f)
	{
		// Reduce the time left...
		m_fTimeLeft	-= fTimeStep;

		if(m_fTimeLeft < 0.0f)
		{
			// We've run out of time - reduce the fade...
			m_fFadeTimeLeft	+= m_fTimeLeft;
			m_fTimeLeft		 = 0.0f;

			if(m_fFadeTimeLeft < 0.0f)
			{
				m_fFadeTimeLeft	= 0.0f;
			}
		}
	}
	else if(m_fFadeTimeLeft > 0.0f)
	{
		// Reduce the fade...
		m_fFadeTimeLeft	-= fTimeStep;

		if(m_fFadeTimeLeft < 0.0f)
		{
			m_fFadeTimeLeft	= 0.0f;
		}
	}
}

/// End of function 'Asura_DebugLineRender_TimedLineSet::Update'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_DebugLineRender_TimedLineSet::Render
// Author       : Andy Tate
// Date         : 23-Jan-2007
////////////////////////////////////////////////////////////////////////////////

void Asura_DebugLineRender_TimedLineSet::Render (void)
{
	if(m_fFadeDuration == 0.0f)
	{
		PARENT::Render(1.0f);
	}
	else
	{
		const float	fAlpha	= (m_fTimeLeft > 0.0f) ? 1.0f : (m_fFadeTimeLeft / m_fFadeDuration);

		// Perform the render...
		PARENT::Render(fAlpha);
	}
}

/// End of function 'Asura_DebugLineRender_TimedLineSet::Render'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_DebugLineRender::Initialise
// Author       : Andy Tate
// Date         : 23-Jan-2007
////////////////////////////////////////////////////////////////////////////////

void Asura_DebugLineRender::Initialise (void)
{
	if(s_pxOneShotLineSet == NULL)
	{
		s_pxOneShotLineSet	= CreateOneShotLineSet(ASURA_MAX_DEBUG_LINE_RENDER_SINGLE_LINES);
	}
	else
	{
		Asura_Assert(false, "Cannot re-initialise a lineset - one already exists.");
		Reset();
	}
}

/// End of function 'Asura_DebugLineRender::Initialise'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_DebugLineRender::Shutdown
// Author       : Andy Tate
// Date         : 23-Jan-2007
////////////////////////////////////////////////////////////////////////////////

void Asura_DebugLineRender::Shutdown (void)
{
	Asura_DebugLineRender_LineSet*	pxLineSet	= s_pxFirstLineSet;

	while(pxLineSet != NULL)
	{
		Asura_DebugLineRender_LineSet*	pxNextLineSet	= pxLineSet->GetNextLineSet();

		delete(pxLineSet);
		pxLineSet	= pxNextLineSet;
	}
}

/// End of function 'Asura_DebugLineRender::Shutdown'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_DebugLineRender::Reset
// Author       : Andy Tate
// Date         : 23-Jan-2007
////////////////////////////////////////////////////////////////////////////////

void Asura_DebugLineRender::Reset (void)
{
	Asura_DebugLineRender_LineSet*	pxPreviousLineSet	= NULL;
	Asura_DebugLineRender_LineSet*	pxLineSet			= s_pxFirstLineSet;

	while(pxLineSet != NULL)
	{
		Asura_DebugLineRender_LineSet*	pxNextLineSet	= pxLineSet->GetNextLineSet();

		pxLineSet->Reset();

		if(pxLineSet->IsFinished())
		{
			delete(pxLineSet);

			if(pxPreviousLineSet == NULL)
			{
				s_pxFirstLineSet	= pxNextLineSet;
			}
			else
			{
				pxPreviousLineSet->SetNextLineSet(pxNextLineSet);
			}
		}
		else
		{
			pxPreviousLineSet	= pxLineSet;
		}

		pxLineSet	= pxNextLineSet;
	}
}

/// End of function 'Asura_DebugLineRender::Reset'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_DebugLineRender::CreateOneShotLineSet
// Author       : Andy Tate
// Date         : 23-Jan-2007
////////////////////////////////////////////////////////////////////////////////

Asura_DebugLineRender_LineSet* Asura_DebugLineRender::CreateOneShotLineSet (const u_int uMaxLines)
{
	Asura_DebugLineRender_LineSet*	pxLineSet	= new Asura_DebugLineRender_OneShotLineSet (uMaxLines);

	if(pxLineSet == NULL)
	{
		Asura_Assert(false, "Failed to create permanent line-set");
	}
	else
	{
		pxLineSet->SetNextLineSet(s_pxFirstLineSet);
		s_pxFirstLineSet	= pxLineSet;
	}

	return(pxLineSet);
}

/// End of function 'Asura_DebugLineRender::CreateOneShotLineSet'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_DebugLineRender::CreateTimedLineSet
// Author       : Andy Tate
// Date         : 23-Jan-2007
////////////////////////////////////////////////////////////////////////////////

Asura_DebugLineRender_LineSet* Asura_DebugLineRender::CreateTimedLineSet (const u_int uMaxLines,
																		  const float fDuration,
																		  const float fFadeDuration)
{
	Asura_DebugLineRender_LineSet*	pxLineSet	= new Asura_DebugLineRender_TimedLineSet (uMaxLines, fDuration, fFadeDuration);

	if(pxLineSet == NULL)
	{
		Asura_Assert(false, "Failed to create timed line-set");
	}
	else
	{
		pxLineSet->SetNextLineSet(s_pxFirstLineSet);
		s_pxFirstLineSet	= pxLineSet;
	}

	return(pxLineSet);
}

/// End of function 'Asura_DebugLineRender::CreateTimedLineSet'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_DebugLineRender::DestroyLineSet
// Author       : Andy Tate
// Date         : 11-May-2007
////////////////////////////////////////////////////////////////////////////////

bool Asura_DebugLineRender::DestroyLineSet(Asura_DebugLineRender_LineSet* pxLineSet)
{
	Asura_DebugLineRender_LineSet*	pxPreviousLineSet	= NULL;
	Asura_DebugLineRender_LineSet*	pxCurrentLineSet	= s_pxFirstLineSet;

	// Find our line-set...
	while(pxCurrentLineSet != NULL)
	{
		if(pxCurrentLineSet == pxLineSet)
		{
			Asura_DebugLineRender_LineSet*	pxNextLineSet	= pxCurrentLineSet->GetNextLineSet();

			if(pxPreviousLineSet == NULL)
			{
				s_pxFirstLineSet	= pxNextLineSet;
			}
			else
			{
				pxPreviousLineSet->SetNextLineSet(pxNextLineSet);
			}

			delete(pxCurrentLineSet);
			return(true);
		}
		else
		{
			pxPreviousLineSet	= pxCurrentLineSet;
			pxCurrentLineSet	= pxPreviousLineSet->GetNextLineSet();
		}
	}

	return(false);
}

/// End of function 'Asura_DebugLineRender::DestroyLineSet'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_DebugLineRender::Update
// Author       : Andy Tate
// Date         : 23-Jan-2007
////////////////////////////////////////////////////////////////////////////////

void Asura_DebugLineRender::Update (void)
{
	const float						fTimeStep			= Asura_Timers::GetGameTimeSlice();
	Asura_DebugLineRender_LineSet*	pxPreviousLineSet	= NULL;
	Asura_DebugLineRender_LineSet*	pxLineSet			= s_pxFirstLineSet;

	while(pxLineSet != NULL)
	{
		Asura_DebugLineRender_LineSet*	pxNextLineSet	= pxLineSet->GetNextLineSet();

		pxLineSet->Update(fTimeStep);

		if(pxLineSet->IsFinished())
		{
			delete(pxLineSet);

			if(pxPreviousLineSet == NULL)
			{
				s_pxFirstLineSet	= pxNextLineSet;
			}
			else
			{
				pxPreviousLineSet->SetNextLineSet(pxNextLineSet);
			}
		}
		else
		{
			pxPreviousLineSet	= pxLineSet;
		}

		pxLineSet	= pxNextLineSet;
	}
}

/// End of function 'Asura_DebugLineRender::Update'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_DebugLineRender::Render
// Author       : Andy Tate
// Date         : 23-Jan-2007
////////////////////////////////////////////////////////////////////////////////

void Asura_DebugLineRender::Render (void)
{
	Asura_DebugLineRender_LineSet*	pxLineSet	= s_pxFirstLineSet;

	while(pxLineSet != NULL)
	{
		Asura_DebugLineRender_LineSet*	pxNextLineSet	= pxLineSet->GetNextLineSet();

		pxLineSet->Render();
		pxLineSet	= pxNextLineSet;
	}
}

/// End of function 'Asura_DebugLineRender::Render'. ///


#endif	// !defined(ASURA_NO_DEBUG_OUTPUT)


/// End of file. ///
