
//
// Asura_LightShaft_System.cpp
//
// Created by Dave Reed - 13/07/2007 
// 
// Copyright (C) 2007 Rebellion, All Rights Reserved.
//
// LightShaft System
//

#include "Core/Asura.h"

#ifdef ASURA_TARGET_SM3

#include "Core/Asura_Mempool.h"
#include "Core/Asura_Maths.h"
#include "SpecialFX/Asura_LightShaft.h"
#include "SpecialFX/Asura_LightShaft_System.h"
#include "Camera/Asura_Camera.h"
#include "Rendering/Asura_Module.h"
#include "Rendering/Asura_Render.h"

Asura_TrackingHandlePool<Asura_LightShaft>*	Asura_LightShaft_System::s_pxLightShaftList;
#if !defined (ASURA_USE_NEW_VISIBILITY_SYSTEM)
Asura_Bounding_Box							Asura_LightShaft_System::s_xBBOfAllVisibleModules;
#endif

void Asura_LightShaft_System::Initialise(void)
{	
	s_pxLightShaftList = new Asura_TrackingHandlePool<Asura_LightShaft>(16);	

	Platform_Initialise();
}


void Asura_LightShaft_System::Shutdown(void)
{
	delete s_pxLightShaftList;
	s_pxLightShaftList = NULL;

	Platform_Shutdown();
}


void Asura_LightShaft_System::Reset(void)
{
	Asura_Assert(s_pxLightShaftList, "Reset called when not Initialised - crash imminent.");
	s_pxLightShaftList->Reset();

	Platform_Reset();
}


void Asura_LightShaft_System::Update(void)
{

}


void Asura_LightShaft_System::RenderCallback(Asura_LightShaft* pxLightShaft)
{
	// Is it enabled?
	if (!pxLightShaft->m_bEnabled)
	{
		return;
	}
#if defined (ASURA_USE_NEW_VISIBILITY_SYSTEM )
	if (! pxLightShaft->IsVisible() ) return;
#else

	// Test against BB of all modules
	const Asura_Bounding_Box &xBB = pxLightShaft->GetBoundingBox();

	if (!xBB.Intersects(s_xBBOfAllVisibleModules))
	{
		return;
	}

	// Test against frustum
	if (!Asura_Camera::IsAABBInFrustum(xBB))	
	{
		return;
	}

	// Test against visible modules	
	if (!Asura_Module_System::DoesBBIntersectVisibleModule(xBB, &pxLightShaft->m_iLastKnownModule))
	{
		return;
	}
#endif
	
	Platform_RenderLightShaft(pxLightShaft);	
}


void Asura_LightShaft_System::Render(void)
{
	if (!Asura_Render::IsSystemEnabled(ASURA_RENDER_SYSTEM_LIGHTSHAFTS))
	{
		return;
	}
#if !defined (ASURA_USE_NEW_VISIBILITY_SYSTEM)
	// Get a bounding box around all visible modules
	bool bValid = false;

	s_xBBOfAllVisibleModules.MinX=s_xBBOfAllVisibleModules.MinY=s_xBBOfAllVisibleModules.MinZ = 1e12f;
	s_xBBOfAllVisibleModules.MaxX=s_xBBOfAllVisibleModules.MaxY=s_xBBOfAllVisibleModules.MaxZ = -1e12f;

	const u_int uModuleCount = Asura_Module_System::GetNumberOfModules();
	for( u_int uModuleIndex = 0; uModuleIndex < uModuleCount; uModuleIndex += 1)
	{
		if( false == Asura_Module_System::IsModuleVisible( uModuleIndex))
		{
			continue;
		}

		Asura_Bounding_Box xModuleBoundingBox = Asura_Module_System::GetBoundingBoxOfModule( uModuleIndex);
		xModuleBoundingBox.Displace( Asura_Module_System::GetPositionOfModule(uModuleIndex) );
		s_xBBOfAllVisibleModules.Set( xModuleBoundingBox);		
		bValid = true;
	}	

	if (!bValid)
	{
		// No visible modules
		s_xBBOfAllVisibleModules.MinX=s_xBBOfAllVisibleModules.MinY=s_xBBOfAllVisibleModules.MinZ = 0;
		s_xBBOfAllVisibleModules.MaxX=s_xBBOfAllVisibleModules.MaxY=s_xBBOfAllVisibleModules.MaxZ = 0;
	}
#endif

	Platform_RenderStart();
	s_pxLightShaftList->Traverse(RenderCallback);
	Platform_RenderEnd();
}





Asura_LightShaft* Asura_LightShaft_System::CreateLightShaft(void)
{
	Asura_Handle iHandle = s_pxLightShaftList->Allocate();

	if (iHandle == ASURA_INVALID_HANDLE)
	{
		return NULL;
	}

	Asura_LightShaft* pxLightShaft = s_pxLightShaftList->GetPointer(iHandle);

	pxLightShaft->ResetToDefaults();
	pxLightShaft->m_iHandle = iHandle;

	return pxLightShaft;
}


void Asura_LightShaft_System::DestroyLightShaft(const Asura_LightShaft* pxLightShaft)
{
	// The delete operator, etc., tolerates NULL ptrs so it kind of makes
	// sense that we should here as well.
	if( !pxLightShaft ) { return; }

#ifdef ASURA_ASSERT
	Asura_Handle iHandle = pxLightShaft->m_iHandle;
	Asura_Assert( iHandle != ASURA_INVALID_HANDLE, "invalid handle" );
	Asura_LightShaft* pxLightShaftTest = s_pxLightShaftList->GetPointer(iHandle);
	Asura_Assert(pxLightShaftTest == pxLightShaft, "pointer mismatch" );
#endif

	s_pxLightShaftList->Deallocate( pxLightShaft->m_iHandle );
}


Asura_LightShaft* Asura_LightShaft_System::GetLightShaftPointer(Asura_Handle iHandle)
{
	Asura_Assert( iHandle!=ASURA_INVALID_HANDLE, "Invalid handle!" );

	return s_pxLightShaftList->GetPointer(iHandle);
}




void Asura_LightShaft_System::CreateConeVertices(int iRadialSegments, int iLateralSegments, Asura_LightShaftVertex *pxVerts, int iNumVerts)
{	
	int iCount = 0;
	int iVertsCreated = 0;

	for(int j=0;j<iLateralSegments;j++)
	{	
		float fTop, fBottom;

		fTop    = ((float)(j))   / ((float)(iLateralSegments));
		fBottom = ((float)(j+1)) / ((float)(iLateralSegments));

		for(int i=0;i<iRadialSegments+1;i++)
		{
			int iSeg = i;
			if (iSeg>=iRadialSegments) iSeg = 0;

			float fU = ((float)i / (float)iRadialSegments);
			float fAngle = ((float)iSeg / (float)iRadialSegments) * Asura_Maths::Pi * 2.0f;			
			float fHalfStep = (0.5f / (float)iRadialSegments) * Asura_Maths::Pi * 2.0f;

			float fOfs = (j&1)?-fHalfStep:0.0f;
			float fX = Asura_Maths::Sine(fAngle+fOfs);
			float fY = Asura_Maths::Cosine(fAngle+fOfs);		

			fOfs = (j&1)?0.0f:fHalfStep;
			float fX1 = Asura_Maths::Sine(fAngle+fOfs);
			float fY1 = Asura_Maths::Cosine(fAngle+fOfs);		

			float fU1 = fU + ((j&1)?0.0f:(0.5f/(float)iRadialSegments));
			fU = fU - ((j&1)?(0.5f/(float)iRadialSegments):0.0f);

			
			// Duplicate the first vert, to join the strips
			iCount = (i==0)?2:1;
			for(int k=0;k<iCount;k++)
			{		
				// Top vertex
				pxVerts->xPos.x		= fX;
				pxVerts->xPos.y		= fY;
				pxVerts->xPos.z		= 0;
				pxVerts->fU			= fU;
				pxVerts->fV			= fTop;
				pxVerts->fAlpha		= 1.0f-fTop;
				pxVerts->fBottom	= fTop;
				pxVerts++;
				iVertsCreated++;
			}

			// Duplicate the last vert, to join the strips
			iCount = (i==iRadialSegments)?2:1;			
			for(int k=0;k<iCount;k++)
			{		
				// Bottom vertex
				pxVerts->xPos.x		= fX1;			
				pxVerts->xPos.y		= fY1;
				pxVerts->xPos.z		= 0;
				pxVerts->fU			= fU1;
				pxVerts->fV			= fBottom;		
				pxVerts->fAlpha		= 1.0f-fBottom;
				pxVerts->fBottom	= fBottom;
				pxVerts++;
				iVertsCreated++;
			}	
		}
	}
}

#endif //#ifdef ASURA_TARGET_SM3



