#include "ElementRendererHeaders.hpp"
#include "GridElement.hpp"

//#define SPEW_CULL_INFO "your_name_here"

//############################################################################
//############################  GridElement  ################################
//############################################################################

HGOSHEAP
	ElementRenderer::GridElement::s_Heap = NULL;

ElementRenderer::GridElement::ClassData*
	ElementRenderer::GridElement::DefaultData = NULL;

ElementRenderer::Element::SyncMethod
	ElementRenderer::GridElement::SyncMethods[4]=
{
	SYNC_METHOD(ListElement,	CleanRoot),
	SYNC_METHOD(ListElement,	Clean),
	SYNC_METHOD(ListElement,	DirtyRoot),
	SYNC_METHOD(ListElement,	Dirty)
};

ElementRenderer::Element::CullMethod
	ElementRenderer::GridElement::CullMethods=
		CULL_METHOD(GridElement, Grid,	Grid);

ElementRenderer::Element::DrawMethod
	ElementRenderer::GridElement::DrawMethods[DrawStateCount]=
{
	DRAW_METHOD(GridElement,	Inherit),
	DRAW_METHOD(GridElement,	Override)
};

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	ElementRenderer::GridElement::InitializeClass()
{
	Verify(!DefaultData);

	Verify(!s_Heap);
	s_Heap = gos_CreateMemoryHeap("Grid", 0, g_LibraryHeap);
	Check_Pointer(s_Heap);

	DefaultData =
		new ClassData(
			GridElementClassID,
			"ElementRenderer::GridElement",
			BaseClass::DefaultData,
			reinterpret_cast<Factory>(&Make)
		);
	Check_Object(DefaultData);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	ElementRenderer::GridElement::TerminateClass()
{
	Check_Object(DefaultData);
	delete DefaultData;
	DefaultData = NULL;

	Check_Pointer(s_Heap);
	gos_DestroyMemoryHeap(s_Heap);
	s_Heap = NULL;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
ElementRenderer::GridElement::GridElement(
	ClassData *class_data,
	Stuff::MemoryStream *stream,
	int version,
	ShapeHolder shapes
):
	ListElement(class_data, stream, version, shapes)
{
	Check_Pointer(this);
	Check_Object(stream);

	if (version>=11)
		*stream	>> m_rowCellCount >> m_columnCellCount;
	else
	{
		int row, col;
		*stream	>> row >> col;
		Verify(static_cast<unsigned>(row) <= 255);
		Verify(static_cast<unsigned>(col) <= 255);
		m_rowCellCount = static_cast<BYTE>(row);
		m_columnCellCount = static_cast<BYTE>(col);
	}
	*stream >> m_rowCellSize >> m_columnCellSize >>	m_rowOffset >> m_columnOffset;

	unsigned index = (GetCullMode() == NeverCullMode);
	index |= IsMatrixDirty() ? 2 : 0;
	Verify(index < 4);
	m_sync = SyncMethods[index];

	m_cameraCull = CullMethods.m_cameraCull;
	m_rayCull = CullMethods.m_rayCull;
	m_sphereTest = CullMethods.m_sphereTest;

	index = (m_state>>DrawStateBit) & DrawStateMask;
	Verify(index < DrawStateCount);
	m_draw = DrawMethods[index];

}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
ElementRenderer::GridElement::GridElement(
	BYTE rowSize,
	BYTE columnSize,
	Stuff::Scalar row_offset,
	Stuff::Scalar column_offset,
	Stuff::Scalar total_row_size,
	Stuff::Scalar total_column_size,
	ClassData *class_data
):
	ListElement(class_data)
{
	Check_Pointer(this);

	m_rowCellCount = rowSize;
	m_columnCellCount = columnSize;
	m_rowOffset = row_offset;
	m_columnOffset = column_offset;
	m_rowCellSize = total_row_size/m_rowCellCount;
	m_columnCellSize = total_column_size/m_columnCellCount;

	Verify(rowSize*columnSize < 65536);
	m_activeChildren = static_cast<WORD>(rowSize*columnSize);
	SetSize(m_activeChildren);

	unsigned index = (GetCullMode() == NeverCullMode);
	index |= IsMatrixDirty() ? 2 : 0;
	Verify(index < 4);
	m_sync = SyncMethods[index];

	m_cameraCull = CullMethods.m_cameraCull;
	m_rayCull = CullMethods.m_rayCull;
	m_sphereTest = CullMethods.m_sphereTest;

	index = (m_state>>DrawStateBit) & DrawStateMask;
	Verify(index < DrawStateCount);
	m_draw = DrawMethods[index];
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
ElementRenderer::GridElement::~GridElement()
{
	Check_Object(this);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
ElementRenderer::GridElement*
	ElementRenderer::GridElement::Make(
		Stuff::MemoryStream *stream,
		int version,
		ShapeHolder shapes
	)
{
	Check_Object(stream);

	gos_PushCurrentHeap(s_Heap);
	GridElement *element = new GridElement(DefaultData, stream, version, shapes);
	gos_PopCurrentHeap();

	return element;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	ElementRenderer::GridElement::Save(Stuff::MemoryStream *stream)
{
	Check_Object(this);
	Check_Object(stream);
	BaseClass::Save(stream);

	*stream	<<	m_rowCellCount << m_columnCellCount
			<<	m_rowCellSize << m_columnCellSize
			<<	m_rowOffset << m_columnOffset;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	ElementRenderer::GridElement::TestInstance()
{
	Verify(IsDerivedFrom(DefaultData));
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	ElementRenderer::GridElement::AttachIndexedChild(
		BYTE row,
		BYTE column,
		Element *element
	)
{
	Check_Object(this);
	Verify(row*m_columnCellCount+column < m_list.GetLength());

	m_list[row*m_columnCellCount+column] = element;

	SetParentElement(element, this);

	if (element->GetCullMode() != AlwaysCullMode)
	{
		if (element->m_localOBB.sphereRadius > 0.0f || element->AreBoundsWrong())
			element->SetVolumeCullMode();
		else
			element->SetNeverCullMode();
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
WORD
	ElementRenderer::GridElement::FindElementIndex(
		Stuff::Scalar row,
		Stuff::Scalar column
	)
{
	Check_Object(this);
	column -= m_columnOffset;
	row -= m_rowOffset;
	column /= m_columnCellSize;
	row /= m_rowCellSize;
	if (column>=0.0f && column<m_columnCellCount && row>=0.0f && row<m_rowCellCount)
	{
		WORD index =
			static_cast<WORD>(
				Stuff::Truncate_Float_To_Byte(row)*m_columnCellCount + Stuff::Truncate_Float_To_Byte(column)
			);
		Verify(index < m_list.GetLength());
		return index;
	}
	return static_cast<WORD>(m_list.GetLength());
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	ElementRenderer::GridElement::SetSize(WORD max_size)
{
	Check_Object(this);
	m_list.SetLength(max_size);
	m_activeChildren = max_size;
	for (WORD i=0; i<max_size; ++i)
		m_list[i] = NULL;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	ElementRenderer::GridElement::SetSize(BYTE rowSize, BYTE columnSize)
{
	Check_Object(this);

	if(rowSize != m_rowCellCount || columnSize!= m_columnCellCount)
	{
		m_rowCellCount = rowSize;
		m_columnCellCount = columnSize;
		SetSize(static_cast<WORD>(m_rowCellCount*m_columnCellCount));
	}
} 

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	ElementRenderer::GridElement::GetDimensions(
		Stuff::Scalar *row_offset,
		Stuff::Scalar *column_offset,
		Stuff::Scalar *row_cell_size,
		Stuff::Scalar *column_cell_size
	)
{
	Check_Object(this);
	Check_Pointer(row_offset);
	Check_Pointer(column_offset);
	Check_Pointer(row_cell_size);
	Check_Pointer(column_cell_size);

	*row_offset = m_rowOffset;
	*column_offset = m_columnOffset;
	*row_cell_size = m_rowCellSize;
	*column_cell_size = m_columnCellSize;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	ElementRenderer::GridElement::SetSyncState()
{
	Check_Object(this);

	Verify( GetCullMode() == RootMode || GetCullMode() == NeverCullMode );
	Verify(!IsSyncDeferred() && !AreBoundsWrong());

	unsigned index = (GetCullMode() == NeverCullMode);
	index |= IsMatrixDirty() ? 2 : 0;
	Verify(index < 4);

	m_sync = SyncMethods[index];
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	ElementRenderer::GridElement::SetCullState()
{
	Check_Object(this);
	m_cameraCull = CullMethods.m_cameraCull;
	m_rayCull = CullMethods.m_rayCull;
	m_sphereTest = CullMethods.m_sphereTest;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	ElementRenderer::GridElement::SetDrawState()
{
	Check_Object(this);
	unsigned index = (m_state>>DrawStateBit) & DrawStateMask;
	Verify(index < DrawStateCount);
	m_draw = DrawMethods[index];
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
	ElementRenderer::GridElement::CastCulledRay(CollisionQuery *query)
{
	Check_Object(this);
	Check_Object(query);
	Verify(!query->m_data);

	//
	//----------------------
	// Test all the children
	//----------------------
	//
	if (IterateLine(query, TestCell))
	{
		if (!query->m_data)
			query->m_data = m_data;
		return true;
	}
	return false;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
ElementRenderer::Element*
	ElementRenderer::GridElement::FindSmallestElementContainingCulled(SphereTest *test)
{
	Check_Object(this);
	Check_Object(test);

	//
	//------------------
	// Draw our children
	//------------------
	//
	Element *result = this;
	for (int i=0; i<m_activeChildren; ++i)
	{
		Element *element = m_list[i];
		Check_Object(element);
		Element *temp = element->FindSmallestElementContaining(test);
		if (temp)
			result = temp;
	}
	return result;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void*
	ElementRenderer::GridElement::TestCell(
		GridElement *grid,
		WORD cell,
		CollisionQuery *query,
		Stuff::Scalar enter,
		Stuff::Scalar leave
	)
{
	Check_Object(grid);
	Check_Object(query);
	Verify(cell < grid->m_activeChildren);

	Element *element = grid->m_list[cell];
	Check_Object(element);
	return reinterpret_cast<void*>(element->CastRay(query));
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
inline static bool
	Trim(
		Stuff::Scalar dot,
		Stuff::Scalar separation,
		Stuff::Scalar *enter,
		Stuff::Scalar *leave
	)
{
	Check_Pointer(enter);
	Check_Pointer(leave);
	Verify(Stuff::Small_Enough(dot, 1.0f));
	if (dot > Stuff::SMALL)
	{
		Stuff::Scalar distance = -separation / dot - Stuff::SMALL;
		if (distance < *leave)
			*leave = distance;
		return *enter <= *leave;
	}
	else if (dot < -Stuff::SMALL)
	{
		Stuff::Scalar distance = -separation / dot + Stuff::SMALL;
		if (distance > *enter)
			*enter = distance;
		return *enter <= *leave;
	}
	else
		return separation <= Stuff::SMALL;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
inline static bool
	Leave_Trim(
		Stuff::Scalar dot,
		Stuff::Scalar separation,
		Stuff::Scalar *leave
	)
{
	Check_Pointer(leave);
	Verify(Stuff::Small_Enough(dot, 1.0f));
	if (dot > Stuff::SMALL)
	{
		Stuff::Scalar distance = -separation / dot;
		if (distance < *leave)
		{
			*leave = distance;
			return true;
		}
	}
	return false;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void*
	ElementRenderer::GridElement::IterateLine(
		CollisionQuery *query,
		CellTest call_back
	)
{
	Check_Object(this);
	Check_Object(query);
	Check_Pointer(call_back);
	Verify(query->m_line->m_length >= 0.0f);

	#if defined(SPEW_CULL_INFO)
		int tested=0;
	#endif

	//
	//-------------------------------------------------------------------
	// Find the cell coordinates of the the beginning and end of the m_line
	//-------------------------------------------------------------------
	//
	Stuff::Scalar x_scale = 1.0f / m_columnCellSize;
	Stuff::Scalar z_scale = 1.0f / m_rowCellSize;
	Verify(Stuff::Close_Enough(x_scale, z_scale));
	Check_Object(query->m_line);
	Stuff::Line3D *line = query->m_line;
	Stuff::Scalar x_origin = (line->m_origin.x - m_columnOffset) * x_scale;
	Stuff::Scalar z_origin = (line->m_origin.z - m_rowOffset) * z_scale;
	Stuff::Point3D end;
	line->FindEnd(&end);
	Stuff::Scalar x_end = (end.x - m_columnOffset) * x_scale;
	Stuff::Scalar z_end = (end.z - m_rowOffset) * z_scale;

	//
	//-----------------------------------------------------------------------------
	// If the m_line is within a single cell, just test that one.  If the cell is
	// outside the grid boundaries, return no hit.  We don't use our Truncate
	// functions here because we do not know the extents of the line we are testing
	//-----------------------------------------------------------------------------
	//
	BYTE x = static_cast<BYTE>(x_origin);
	BYTE z = static_cast<BYTE>(z_origin);
	if (x == static_cast<BYTE>(x_end) && z == static_cast<BYTE>(z_end))
	{
		#if defined(SPEW_CULL_INFO)
			SPEW((SPEW_CULL_INFO, "single cell test"));
		#endif
		if (x < m_columnCellCount && z < m_rowCellCount)
			return
				(*call_back)(
					this,
					static_cast<WORD>(z*m_columnCellCount + x),
					query,
					0,
					line->m_length
				);
		return NULL;
	}

	//
	//----------------------------------------------------------------------
	// The m_line crosses cells, so we first have to clip it to the grid
	// boundaries.  We will treat the box boundaries as four plane equations
	//----------------------------------------------------------------------
	//
	Stuff::Scalar enter = 0.0f;
	Stuff::Scalar leave = line->m_length;
	if (
		!Trim(
			-line->m_direction.x,
			m_columnOffset - line->m_origin.x,
			&enter,
			&leave
		)
	)
	{
		#if defined(SPEW_CULL_INFO)
			SPEW((SPEW_CULL_INFO, "Line doesn't hit grid"));
		#endif
		return NULL;
	}

	if (
		!Trim(
			line->m_direction.x,
			line->m_origin.x - (m_columnOffset + m_columnCellCount*m_columnCellSize),
			&enter,
			&leave
		)
	)
	{
		#if defined(SPEW_CULL_INFO)
			SPEW((SPEW_CULL_INFO, "Line doesn't hit grid"));
		#endif
		return NULL;
	}

	if (
		!Trim(
			-line->m_direction.z,
			m_rowOffset - line->m_origin.z,
			&enter,
			&leave
		)
	)
	{
		#if defined(SPEW_CULL_INFO)
			SPEW((SPEW_CULL_INFO, "Line doesn't hit grid"));
		#endif
		return NULL;
	}

	if (
		!Trim(
			line->m_direction.z,
			line->m_origin.z - (m_rowOffset + m_rowCellCount*m_rowCellSize),
			&enter,
			&leave
		)
	)
	{
		#if defined(SPEW_CULL_INFO)
			SPEW((SPEW_CULL_INFO, "Line doesn't hit grid"));
		#endif
		return NULL;
	}

	//
	//---------------------------------------------------------------------------
	// Now project the origin and end of the m_line in cell space and cast those
	// into the integer indices we need.  The clamps are there to deal with error
	// not caught by trim
	//---------------------------------------------------------------------------
	//
	line->Project(enter, &end);
	x_origin = (end.x - m_columnOffset) * x_scale;
	z_origin = (end.z - m_rowOffset) * z_scale;
	if (x_origin <= 0.0f)
		x = 0;
	else if (x_origin >= m_columnCellCount)
		x = static_cast<BYTE>(m_columnCellCount-1);
	else
		x = Stuff::Truncate_Float_To_Byte(x_origin);
	if (z_origin <= 0.0f)
		z = 0;
	else if (z_origin >= m_rowCellCount)
		z = static_cast<BYTE>(m_rowCellCount-1);
	else
		z = Stuff::Truncate_Float_To_Byte(z_origin);
	Verify(x < m_columnCellCount && z < m_rowCellCount);

	line->Project(leave, &end);
	x_end = (end.x - m_columnOffset) * x_scale;
	z_end = (end.z - m_rowOffset) * z_scale;
	BYTE last_x, last_z;
	if (x_end <= 0.0f)
		last_x = 0;
	else if (x_end >= m_columnCellCount)
		last_x = static_cast<BYTE>(m_columnCellCount-1);
	else
		last_x = Stuff::Truncate_Float_To_Byte(x_end);
	if (z_end <= 0.0f)
		last_z = 0;
	else if (z_end >= m_rowCellCount)
		last_z = static_cast<BYTE>(m_rowCellCount-1);
	else
		last_z = Stuff::Truncate_Float_To_Byte(z_end);
	Verify(last_x < m_columnCellCount && last_z < m_rowCellCount);

	//
	//--------------------------------
	// Crawl over the cells one by one
	//--------------------------------
	//
	Stuff::Scalar cell_leave = enter;
	while (true)
	{
		#if defined(SPEW_CULL_INFO)
			++tested;
		#endif
		Verify(x < m_columnCellCount && z < m_rowCellCount);

		//
		//-------------------------------------------------------------------
		// Clip the m_line to the x boundaries of the current cell and remember
		// way to leave
		//-------------------------------------------------------------------
		//
		enter = cell_leave;
		Stuff::Scalar x_leave = leave;
		int dx=0;
		if (
			Leave_Trim(
				-line->m_direction.x,
				m_columnOffset + x*m_columnCellSize - line->m_origin.x,
				&x_leave
			)
		)
			dx = -1;
		if (
			Leave_Trim(
				line->m_direction.x,
				line->m_origin.x - (m_columnOffset + (x+1)*m_columnCellSize),
				&x_leave
			)
		)
			dx = 1;

		//
		//-------------------------------------------------------------------
		// Clip the m_line to the z boundaries of the current cell and remember
		// way to leave
		//-------------------------------------------------------------------
		//
		int dz=0;
		Stuff::Scalar z_leave = leave;
		if (
			Leave_Trim(
				-line->m_direction.z,
				m_rowOffset + z*m_rowCellSize - line->m_origin.z,
				&z_leave
			)
		)
			dz = -1;
		if (
			Leave_Trim(
				line->m_direction.z,
				line->m_origin.z - (m_rowOffset + (z+1)*m_rowCellSize),
				&z_leave
			)
		)
			dz = 1;

		//
		//----------------------------------------------------------------
		// Pick which axis we leave on first.  Make sure we handle corners
		// appropriately
		//----------------------------------------------------------------
		//
		if (x_leave < z_leave-Stuff::SMALL)
		{
			dz = 0;
			cell_leave = x_leave;
		}
		if (z_leave < x_leave-Stuff::SMALL)
		{
			dx = 0;
			cell_leave = z_leave;
		}
		else
			cell_leave = x_leave;

		//
		//--------------------------------------------------------
		// See if there is anything to hit, and if so, we are done
		//--------------------------------------------------------
		//
		Stuff::Scalar old_length = query->m_line->m_length;
		void *result =
			(*call_back)(
				this,
				static_cast<WORD>(z*m_columnCellCount + x),
				query,
				enter,
				cell_leave
			);
		if (result != NULL)
		{
			#if defined(SPEW_CULL_INFO)
				SPEW((SPEW_CULL_INFO, "Hit! %d of %d cells tested", tested, m_columnCellCount*m_rowCellCount));
			#endif
			if (query->m_line->m_length <= cell_leave)
				return result;
			else
				query->m_line->m_length = old_length;
		}

		//
		//--------------------------------------------------------------------
		// If we have reached the end, we are done, otherwise move to the next
		// cell
		//--------------------------------------------------------------------
		//
		if (x == last_x && z == last_z || !dx && !dz)
			break;
		x = static_cast<BYTE>(x + dx);
		z = static_cast<BYTE>(z + dz);
	}


	//
	//-------------------------
	// We couldn't hit anything
	//-------------------------
	//
	#if defined(SPEW_CULL_INFO)
		SPEW((SPEW_CULL_INFO, "Missed: %d of %d cells tested", tested, m_columnCellCount*m_rowCellCount));
	#endif
	return NULL;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int
	ElementRenderer::GridElement::GridCullMethod(CameraElement *camera)
{
	Check_Object(this);
	Check_Object(camera);

	return BaseClass::NeverCullMethod(camera);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int
	ElementRenderer::GridElement::GridSphereTestMethod(SphereTest *query)
{
	Check_Object(this);
	Check_Object(query);

	return BaseClass::NeverSphereTestMethod(query);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
	ElementRenderer::GridElement::GridRayCullMethod(CollisionQuery *query)
{
	Check_Object(this);
	Check_Object(query);
	Verify(!query->m_data);

	return BaseClass::NeverRayCullMethod(query);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	ElementRenderer::GridElement::InheritDrawMethod(
		CameraElement *camera,
		const StateChange *inherited_state,
		int culling_state
	)
{
	Check_Object(this);
	Check_Object(inherited_state);
	Check_Object(camera);
	Verify(culling_state != -1);

	//
	//----------------------
	// Draw all the children
	//----------------------
	//
	Stuff::Point3D theCorners[4];

	theCorners[0].y = 0.0f;
	theCorners[1].y = 0.0f;
	theCorners[2].y = 0.0f;
	theCorners[3].y = 0.0f;

	const Stuff::Point3D *cullPoints = camera->GetCullPoints();

	int pxmin, pxmax, pzmin, pzmax, in[5], out[4];
	int i, j, k;

	pxmin = pxmax = pzmin = pzmax = 0;

	for(i=1;i<5;i++)
	{
		if(cullPoints[i].x < cullPoints[pxmin].x)
		{
			pxmin = i;
		}
		if(cullPoints[i].x > cullPoints[pxmax].x)
		{
			pxmax = i;
		}
		if(cullPoints[i].z < cullPoints[pzmin].z)
		{
			pzmin = i;
		}
		if(cullPoints[i].z > cullPoints[pzmax].z)
		{
			pzmax = i;
		}
	}

	int inCount=0, outCount=0;

	in[inCount++] = pzmin;


	if(pzmin!=pxmin)
	{
		in[inCount++] = pxmin;

		if(pxmin!=pzmax)
		{
			in[inCount++] = pzmax;

			if(pzmax!=pxmax && pxmax!=pzmin)
			{
				in[inCount++] = pxmax;
			}
		}
		else
		{
			in[inCount++] = pxmax;
		}
	}
	else
	{
		in[inCount++] = pzmax;
		Verify(pzmax!=pxmax && pxmax!=pzmin);
		in[inCount++] = pxmax;
	}

	Verify(inCount<5);

	for(i=0;i<5;i++)
	{
		for(j=0;j<inCount;j++)
		{
			if(i==in[j])
			{
				break;
			}
		}
		if(j==inCount)
		{
			out[outCount++] = i;
		}
	}

	Verify(outCount<3);

	for(i=0;i<outCount;i++)
	{
		for(j=0;j<inCount;j++)
		{
			if(Stuff::Close_Enough(cullPoints[out[i]], cullPoints[in[j]]))
			{
				break;
			}
		}
		if(j<inCount)
		{
			continue;
		}

		for(j=0;j<inCount;j++)
		{
			int end = j+1;

			if(end==inCount)
			{
				end = 0;
			}

			Stuff::Vector3D vc;

			vc.Cross(cullPoints[out[i]], cullPoints[in[j]], cullPoints[in[end]]);

			if(vc.y > 0.0f)
			{
				break;
			}
		}

		if(j<inCount)
		{
			for(k=inCount;k>j+1;k--)
			{
				in[k] = in[k-1];
			}
			in[j+1] = out[i];
			inCount++;
		}
	}

#ifdef _ARMOR
//
//	Checking for a convex hull
//
	for(i=0;i<inCount;i++)
	{
		int next = (i+1)%inCount;
		int nextAfter = (i+2)%inCount;

		Stuff::Vector3D vc;
		vc.Cross(cullPoints[in[next]], cullPoints[in[i]], cullPoints[in[nextAfter]]);

		Verify(vc.y >= 0.0f);
	}
#endif

#ifdef SPEW_CULL_INFO
	int d = 0;
#endif

	const Stuff::Scalar theCageCorrector = 4.5f;

	int minZ, maxZ, minX, maxX;

	minZ = (int)floor((cullPoints[pzmin].z-m_rowOffset-theCageCorrector)/m_rowCellSize);
	if(minZ<0)
	{
		minZ = 0;
	}
	maxZ = (int)ceil((cullPoints[pzmax].z-m_rowOffset+theCageCorrector)/m_rowCellSize);
	if(maxZ > m_rowCellCount)
	{
		maxZ = m_rowCellCount;
	}

	theCorners[0].z = m_rowOffset + minZ*m_rowCellSize;
	theCorners[1].z = theCorners[0].z;
	theCorners[2].z = theCorners[0].z + m_rowCellSize;
	theCorners[3].z = theCorners[2].z;

	minX = (int)floor((cullPoints[pxmin].x-m_columnOffset-theCageCorrector)/m_columnCellSize);
	if(minX<0)
	{
		minX = 0;
	}
	maxX = (int)ceil((cullPoints[pxmax].x-m_columnOffset+theCageCorrector)/m_columnCellSize);
	if(maxX > m_columnCellCount)
	{
		maxX = m_columnCellCount;
	}

	for (j=minZ; j<maxZ; ++j)
	{

		theCorners[0].x = m_columnOffset + minX*m_columnCellSize;
		theCorners[1].x = theCorners[0].x + m_columnCellSize;
		theCorners[2].x = theCorners[1].x;
		theCorners[3].x = theCorners[0].x;

//		bool inOrOut = false;
		for (i=minX; i<maxX; ++i)
		{
/*
			if(inOrOut==false)
			{
//
//	Check the x-max side
//
				int end, k, p1 = -1, p2 = -1;
				for(k=0;k<inCount;++k)
				{
					end = k+1;

					if(end>=inCount)
					{
						end = 0;
					}

					if(cullPoints[in[end]].z > cullPoints[in[k]].z)
					{
						if(p1 < 0 && theCorners[1].z > cullPoints[in[k]].z && theCorners[1].z < cullPoints[in[end]].z)
						{
							p1 = k;
						}
						if(p2 < 0 && theCorners[2].z > cullPoints[in[k]].z && theCorners[2].z < cullPoints[in[end]].z)
						{
							p2 = k;
						}
					}
				}
				if(p1<0 && p2<0)
				{
					if(theCorners[1].x > cullPoints[pxmin].x)
					{
						inOrOut = true;
					}
				}
				else
				{

					Stuff::Vector3D vc;
					if(p1==p2)
					{
						end = p1+1;

						if(end>=inCount)
						{
							end = 0;
						}

						vc.Cross(theCorners[1], cullPoints[in[p1]], cullPoints[in[end]]);

						if(vc.y < 0.0f)
						{
							inOrOut = true;
						}
						else
						{
							end = p2+1;

							if(end>=inCount)
							{
								end = 0;
							}

							vc.Cross(theCorners[2], cullPoints[in[p2]], cullPoints[in[end]]);

							if(vc.y < 0.0f)
							{
								inOrOut = true;
							}
						}
					}
					else
					{
						if(p2<0)
						{
							end = p1+1;

							if(end>=inCount)
							{
								end = 0;
							}

							vc.Cross(theCorners[1], cullPoints[in[p1]], cullPoints[in[end]]);

							if(vc.y < 0.0f)
							{
								inOrOut = true;
							}
						}
						
						if(p1<0)
						{
							end = p2+1;

							if(end>=inCount)
							{
								end = 0;
							}

							vc.Cross(theCorners[2], cullPoints[in[p2]], cullPoints[in[end]]);

							if(vc.y < 0.0f)
							{
								inOrOut = true;
							}
						}
						if(p1>=0 && p2>=0)
						{
							Verify(p2>p1);
							for(k=p1+1;k<p2+1;k++)
							{
								if(theCorners[1].x > cullPoints[in[k]].x)
								{
									inOrOut = true;
									break;
								}
							}
						}
					}
				}
			}
			else
			{
//
//	Check the x-max side
//
			}

			if(inOrOut==true)
*/
			{
				Element *element = m_list[j*m_columnCellCount+i];
				Check_Object(element);

#ifdef _ARMOR
				if(element->GetCullMode() == VolumeCullMode)
				{
					Verify(element->GetWorldOBB().localToParent.entries[3] >= theCorners[0].x);
					Verify(element->GetWorldOBB().localToParent.entries[3] <= theCorners[1].x);
					Verify(element->GetWorldOBB().localToParent.entries[11] >= theCorners[0].z);
					Verify(element->GetWorldOBB().localToParent.entries[11] <= theCorners[3].z);
				}
#endif

				camera->DrawElement(element, inherited_state);

#ifdef SPEW_CULL_INFO
				d++;
#endif
			}

			theCorners[0].x += m_columnCellSize;
			theCorners[1].x += m_columnCellSize;
			theCorners[2].x += m_columnCellSize;
			theCorners[3].x += m_columnCellSize;
		}
		theCorners[0].z += m_rowCellSize;
		theCorners[1].z += m_rowCellSize;
		theCorners[2].z += m_rowCellSize;
		theCorners[3].z += m_rowCellSize;
	}
#ifdef SPEW_CULL_INFO
	SPEW((SPEW_CULL_INFO, "Drawn: %d Culled: %d", d, (m_rowCellCount*m_columnCellCount)-d));
#endif
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	ElementRenderer::GridElement::OverrideDrawMethod(
		CameraElement *camera,
		const StateChange *inherited_state,
		int culling_state
	)
{
	Check_Object(this);
	Check_Object(inherited_state);
	Check_Object(camera);
	Verify(culling_state != -1);

	//
	//-------------------
	// Mix the properties
	//-------------------
	//
	Check_Object(m_stateChange);
	StateChange mixed;
	mixed.Mix(*inherited_state, *m_stateChange);
	MixLights(
		mixed.m_lights,
		inherited_state->m_lights,
		m_stateChange->m_lights
	);

	//
	//----------------------
	// Draw all the children
	//----------------------
	//
	Stuff::Point3D theCorners[4];

	theCorners[0].y = 0.0f;
	theCorners[1].y = 0.0f;
	theCorners[2].y = 0.0f;
	theCorners[3].y = 0.0f;

	const Stuff::Point3D *cullPoints = camera->GetCullPoints();

	int pxmin, pxmax, pzmin, pzmax, in[5], out[4];
	int i, j, k;

	pxmin = pxmax = pzmin = pzmax = 0;

	for(i=1;i<5;i++)
	{
		if(cullPoints[i].x < cullPoints[pxmin].x)
		{
			pxmin = i;
		}
		if(cullPoints[i].x > cullPoints[pxmax].x)
		{
			pxmax = i;
		}
		if(cullPoints[i].z < cullPoints[pzmin].z)
		{
			pzmin = i;
		}
		if(cullPoints[i].z > cullPoints[pzmax].z)
		{
			pzmax = i;
		}
	}

	int inCount=0, outCount=0;

	in[inCount++] = pzmin;


	if(pzmin!=pxmin)
	{
		in[inCount++] = pxmin;

		if(pxmin!=pzmax)
		{
			in[inCount++] = pzmax;

			if(pzmax!=pxmax && pxmax!=pzmin)
			{
				in[inCount++] = pxmax;
			}
		}
		else
		{
			in[inCount++] = pxmax;
		}
	}
	else
	{
		in[inCount++] = pzmax;
		Verify(pzmax!=pxmax && pxmax!=pzmin);
		in[inCount++] = pxmax;
	}

	Verify(inCount<5);

	for(i=0;i<5;i++)
	{
		for(j=0;j<inCount;j++)
		{
			if(i==in[j])
			{
				break;
			}
		}
		if(j==inCount)
		{
			out[outCount++] = i;
		}
	}

	Verify(outCount<3);

	for(i=0;i<outCount;i++)
	{
		for(j=0;j<inCount;j++)
		{
			if(Stuff::Close_Enough(cullPoints[out[i]], cullPoints[in[j]]))
			{
				break;
			}
		}
		if(j<inCount)
		{
			continue;
		}

		for(j=0;j<inCount;j++)
		{
			int end = j+1;

			if(end==inCount)
			{
				end = 0;
			}

			Stuff::Vector3D vc;

			vc.Cross(cullPoints[out[i]], cullPoints[in[j]], cullPoints[in[end]]);

			if(vc.y > 0.0f)
			{
				break;
			}
		}

		if(j<inCount)
		{
			for(k=inCount;k>j+1;k--)
			{
				in[k] = in[k-1];
			}
			in[j+1] = out[i];
			inCount++;
		}
	}

#ifdef SPEW_CULL_INFO
	int d = 0;
#endif

	int minZ, maxZ, minX, maxX;

	minZ = (int)floor((cullPoints[pzmin].z-m_rowOffset)/m_rowCellSize);
	if(minZ<0)
	{
		minZ = 0;
	}
	maxZ = (int)ceil((cullPoints[pzmax].z-m_rowOffset)/m_rowCellSize);
	if(maxZ > m_rowCellCount)
	{
		maxZ = m_rowCellCount;
	}

	theCorners[0].z = m_rowOffset + minZ*m_rowCellSize;
	theCorners[1].z = theCorners[0].z;
	theCorners[2].z = theCorners[0].z + m_rowCellSize;
	theCorners[3].z = theCorners[2].z;

	minX = (int)floor((cullPoints[pxmin].x-m_columnOffset)/m_columnCellSize);
	if(minX<0)
	{
		minX = 0;
	}
	maxX = (int)ceil((cullPoints[pxmax].x-m_columnOffset)/m_columnCellSize);
	if(maxX > m_columnCellCount)
	{
		maxX = m_columnCellCount;
	}

	for (j=minZ; j<maxZ; ++j)
	{

		theCorners[0].x = m_columnOffset + minX*m_columnCellSize;
		theCorners[1].x = theCorners[0].x + m_columnCellSize;
		theCorners[2].x = theCorners[1].x;
		theCorners[3].x = theCorners[0].x;

		for (i=minX; i<maxX; ++i)
		{
			if(1)
			{
				Element *element = m_list[j*m_columnCellCount+i];
				Check_Object(element);
				camera->DrawElement(element, inherited_state);
#ifdef SPEW_CULL_INFO
				d++;
			}
			else
			{
				nd++;
#endif
			}

			theCorners[0].x += m_rowCellSize;
			theCorners[1].x += m_rowCellSize;
			theCorners[2].x += m_rowCellSize;
			theCorners[3].x += m_rowCellSize;
		}
		theCorners[0].z += m_columnCellSize;
		theCorners[1].z += m_columnCellSize;
		theCorners[2].z += m_columnCellSize;
		theCorners[3].z += m_columnCellSize;
	}
#ifdef SPEW_CULL_INFO
	SPEW((SPEW_CULL_INFO, "Drawn: %d Culled: %d", d, nd));
#endif
}
