//////////////////////////////////////////////////////////////////////////////
//
// Asura_PC_ShadowStorage.cpp
//
// Copyright (c) 2008 Rebellion. All rights reserved.
//
//////////////////////////////////////////////////////////////////////////////

// I N C L U D E S ///////////////////////////////////////////////////////////

#include "Core/Asura.h"

#if defined(ASURA_TARGET_SM3)

#include "Core/Asura_Mempool.h"


#include "Rendering/Asura_Render.h"


#include "Rendering/Asura_Direct3D.h"
#include "Rendering/Asura_Direct3D_Caps.h"
#include "Rendering/Asura_Direct3D_Effects_System.h"
#include "Rendering/Asura_Direct3D_EffectParameterBlock.h"
#include "Rendering/Asura_Direct3D_EnvironmentRenderer.h"
#include "Rendering/Asura_Direct3D_RenderTargetManager.h"
#include "Rendering/Asura_Direct3D_Shaders.h"
#include "Rendering/Asura_Direct3D_UserOptions.h"
#include "Rendering/Asura_DeferredShading.h"


#include "Shadows/Asura_Shadow_System.h"
#include "Shadows/Asura_PC_ShadowBuffer.h"
#include "Shadows/Asura_PC_ShadowStorage.h"

Asura_PC_ShadowStorage::Asura_PC_ShadowStorage() : m_xMemPool(ASURA_NUM_SHADOW_TEXTURE_SIZES * ASURA_NUM_SHADOWS_PER_TEXTURE_SIZE, sizeof(Asura_PC_ShadowBuffer), __FILE__, __LINE__)
{
	m_pxEnvShadowCubeTexture = NULL;
	m_uShadowTexturesAllocated = NULL;

#ifdef ASURA_HIGH_RES_SHADOW_MODE
	m_pxHRSEnvShadowCubeTexture = NULL;
	m_ppxHRSShadowDepthRenderTarget = NULL;
	m_ppxHRSShadowDepthStencil = NULL;
	m_ppxHRSShadowBuffers = NULL;

	m_bHaveEnabledHRS = false;
	m_bIsEnabledHRS	 = false;
#endif
}
Asura_PC_ShadowStorage::~Asura_PC_ShadowStorage()
{
	Asura_Assert(m_pxEnvShadowCubeTexture == NULL, "");

#ifdef ASURA_HIGH_RES_SHADOW_MODE
	Asura_Assert(m_pxHRSEnvShadowCubeTexture == NULL, "");
	Asura_Assert(m_ppxHRSShadowDepthRenderTarget == NULL, "");
	Asura_Assert(m_ppxHRSShadowDepthStencil == NULL, "");
	Asura_Assert(m_ppxHRSShadowBuffers == NULL, "");
#endif


}

// Needed as we need placement new to call constructors
#include <Core/Asura_MemoryManagement_DebugOff.h>
Asura_PC_ShadowBuffer *Asura_PC_ShadowStorage::CreatePCShadowBuffer(u_int uTextureSize, Direct3D_Surface* pxDepthSurface)
{
	void *p = m_xMemPool.Allocate();
	if (p)
	{
		return new (p) Asura_PC_ShadowBuffer(uTextureSize, pxDepthSurface);
	}
	return NULL;
}
#include <Core/Asura_MemoryManagement_DebugOn.h>


void Asura_PC_ShadowStorage::Initialise()
{
	if( Asura_Direct3D::D3DDevice )
	{
		bool bEnableNvidia = Asura_PC_ShadowBuffer::IsNvidiaDepthMap();

		// Create cube texture
		Asura_Direct3D::D3DDevice->CreateCubeTexture(ASURA_MAX_SHADOW_TEXTURE_SIZE, 1,
			D3DUSAGE_RENDERTARGET, D3DFMT_R32F, D3DPOOL_DEFAULT, &m_pxEnvShadowCubeTexture, NULL );

		for (u_int i = 0, uTexSize = ASURA_MAX_SHADOW_TEXTURE_SIZE; i < ASURA_NUM_SHADOW_TEXTURE_SIZES; ++i, (uTexSize>>=1))
		{
			if (bEnableNvidia)
			{
				Asura_Direct3D::D3DDevice->CreateRenderTarget(uTexSize,uTexSize, D3DFMT_R32F, D3DMULTISAMPLE_NONE, 0, false, &m_ppxShadowDepthRenderTarget[i], NULL);
				Asura_Assert(m_ppxShadowDepthRenderTarget[i], "Unable to create Shadow Render target");
			}

			Asura_Direct3D::D3DDevice->CreateDepthStencilSurface(uTexSize,uTexSize, D3DFMT_D24S8, D3DMULTISAMPLE_NONE, 0, false, &m_ppxShadowDepthStencil[i], NULL);
			Asura_Assert(m_ppxShadowDepthStencil[i], "Unable to create Shadow Render target");

			for (u_int j = 0; j < ASURA_NUM_SHADOWS_PER_TEXTURE_SIZE; ++j)
			{
				int idx = j + i * ASURA_NUM_SHADOWS_PER_TEXTURE_SIZE;
				if (bEnableNvidia)
				{
					m_ppxShadowBuffers[idx] = CreatePCShadowBuffer(uTexSize, m_ppxShadowDepthRenderTarget[i]);
				}
				else 
				{
					m_ppxShadowBuffers[idx] = CreatePCShadowBuffer(uTexSize, m_ppxShadowDepthStencil[i]);
				}
			}
		}
#ifdef ASURA_HIGH_RES_SHADOW_MODE
		if (m_bIsEnabledHRS)
		{
			SetHRS(true);
		}
#endif
	}
}

#ifdef ASURA_HIGH_RES_SHADOW_MODE
bool Asura_PC_ShadowStorage::InitialiseHRS()
{
	if( Asura_Direct3D::D3DDevice )
	{
		bool bEnableNvidia = Asura_PC_ShadowBuffer::IsNvidiaDepthMap();
		m_ppxHRSShadowDepthStencil	= new Direct3D_Surface* [ASURA_NUM_HIRES_SCREENSHOT_SHADOW_TEXTURE_SIZES];

		if (bEnableNvidia)
		{
			m_ppxHRSShadowDepthRenderTarget = new Direct3D_Surface* [ASURA_NUM_HIRES_SCREENSHOT_SHADOW_TEXTURE_SIZES];
		} 
		m_ppxHRSShadowBuffers		= new Asura_PC_ShadowBuffer* [ASURA_NUM_HIRES_SCREENSHOT_SHADOW_TEXTURE_SIZES * ASURA_NUM_HIRES_SCREENSHOT_SHADOWS_PER_TEXTURE_SIZE];

		// Create cube texture
		Asura_Direct3D::D3DDevice->CreateCubeTexture( ASURA_MAX_HIRES_SCREENSHOT_SHADOW_TEXTURE_SIZE, 1,
			D3DUSAGE_RENDERTARGET, D3DFMT_R32F, D3DPOOL_DEFAULT, &m_pxHRSEnvShadowCubeTexture, NULL );

		for (u_int i = 0, uTexSize = ASURA_MAX_HIRES_SCREENSHOT_SHADOW_TEXTURE_SIZE; i < ASURA_NUM_HIRES_SCREENSHOT_SHADOW_TEXTURE_SIZES; ++i, (uTexSize>>=1))
		{
			if (bEnableNvidia)
			{
				Asura_Direct3D::D3DDevice->CreateRenderTarget(uTexSize,uTexSize, D3DFMT_R32F, D3DMULTISAMPLE_NONE, 0, false, &m_ppxHRSShadowDepthRenderTarget[i], NULL);
				Asura_Assert(m_ppxHRSShadowDepthRenderTarget[i], "Unable to create Shadow Render target");
			}

			Asura_Direct3D::D3DDevice->CreateDepthStencilSurface(uTexSize,uTexSize, D3DFMT_D24S8, D3DMULTISAMPLE_NONE, 0, false, &m_ppxHRSShadowDepthStencil[i], NULL);
			Asura_Assert(m_ppxHRSShadowDepthStencil[i], "Unable to create Shadow Render target");

			for (u_int j = 0; j < ASURA_NUM_HIRES_SCREENSHOT_SHADOWS_PER_TEXTURE_SIZE; ++j)
			{
				int idx = j + i * ASURA_NUM_HIRES_SCREENSHOT_SHADOWS_PER_TEXTURE_SIZE;
				if (bEnableNvidia)
				{
					m_ppxHRSShadowBuffers[idx] = CreatePCShadowBuffer(uTexSize, m_ppxHRSShadowDepthRenderTarget[i]);
				}
				else 
				{
					m_ppxHRSShadowBuffers[idx] = CreatePCShadowBuffer(uTexSize, m_ppxHRSShadowDepthStencil[i]);
				}
			}
		}

		m_bHaveEnabledHRS = true;

	}
	return true;
}
#endif

void Asura_PC_ShadowStorage::Shutdown()
{
	if( !Asura_Direct3D::D3DDevice )
	{
		return;
	}
	SAFE_RELEASE( m_pxEnvShadowCubeTexture );
#ifdef ASURA_HIGH_RES_SHADOW_MODE
	SAFE_RELEASE( m_pxHRSEnvShadowCubeTexture );
#endif

	for (u_int i = 0, uTexSize = ASURA_MAX_SHADOW_TEXTURE_SIZE; i < ASURA_NUM_SHADOW_TEXTURE_SIZES; ++i, (uTexSize>>=1))
	{
		for (u_int j = 0; j < ASURA_NUM_SHADOWS_PER_TEXTURE_SIZE; ++j)
		{
			m_ppxShadowBuffers[j + i * ASURA_NUM_SHADOWS_PER_TEXTURE_SIZE]->~Asura_PC_ShadowBuffer();
			m_xMemPool.Deallocate(m_ppxShadowBuffers[j + i * ASURA_NUM_SHADOWS_PER_TEXTURE_SIZE]);

			m_ppxShadowBuffers[j + i * ASURA_NUM_SHADOWS_PER_TEXTURE_SIZE] = NULL;
		}
		SAFE_RELEASE(m_ppxShadowDepthStencil[i]);
		if (m_ppxShadowDepthRenderTarget)
		{
			SAFE_RELEASE(m_ppxShadowDepthRenderTarget[i]);
		}
	}
	memset(m_ppxShadowDepthStencil, 0, sizeof(m_ppxShadowDepthStencil));
	memset(m_ppxShadowDepthRenderTarget, 0, sizeof(m_ppxShadowDepthStencil));
	memset(m_ppxShadowBuffers, 0, sizeof(m_ppxShadowDepthStencil));


#ifdef ASURA_HIGH_RES_SHADOW_MODE
	for (u_int i = 0, uTexSize = ASURA_MAX_HIRES_SCREENSHOT_SHADOW_TEXTURE_SIZE; i < ASURA_NUM_HIRES_SCREENSHOT_SHADOW_TEXTURE_SIZES; ++i, (uTexSize>>=1))
	{
		if (m_ppxHRSShadowBuffers)
		{
			for (u_int j = 0; j < ASURA_NUM_HIRES_SCREENSHOT_SHADOWS_PER_TEXTURE_SIZE; ++j)
			{
				m_ppxHRSShadowBuffers[j + i * ASURA_NUM_SHADOWS_PER_TEXTURE_SIZE]->~Asura_PC_ShadowBuffer();
				m_xMemPool.Deallocate(m_ppxHRSShadowBuffers[j + i * ASURA_NUM_SHADOWS_PER_TEXTURE_SIZE]);
				m_ppxHRSShadowBuffers[j + i * ASURA_NUM_SHADOWS_PER_TEXTURE_SIZE] = NULL;
			}
			SAFE_RELEASE(m_ppxHRSShadowDepthStencil[i]);
			if (m_ppxHRSShadowDepthRenderTarget)
			{
				SAFE_RELEASE(m_ppxHRSShadowDepthRenderTarget[i]);
			}
		}
	}

	delete[](m_ppxHRSShadowDepthStencil);
	delete[](m_ppxHRSShadowDepthRenderTarget);
	m_ppxHRSShadowDepthStencil = NULL;
	m_ppxHRSShadowDepthRenderTarget = NULL;
	delete [] m_ppxHRSShadowBuffers;

	m_bHaveEnabledHRS = false;
#endif
}

#endif