#include "stdafx.h"
#include "dx103DFluidData.h"

#include "dx103DFluidManager.h"

namespace
{
	const xr_token	simulation_type_token		[ ]=
	{
		{ "Fog",	dx103DFluidData::ST_FOG		},
		{ "Fire",  	dx103DFluidData::ST_FIRE	},
		{ 0,		0							}
	};

	const xr_token	emitter_type_token			[ ]=
	{
		{ "SimpleGaussian",	dx103DFluidEmitters::ET_SimpleGausian	},
		{ "SimpleDraught",  dx103DFluidEmitters::ET_SimpleDraught	},
		{ 0,		0							}
	};
}

DXGI_FORMAT	dx103DFluidData::m_VPRenderTargetFormats[ VP_NUM_TARGETS ] = 
{
	DXGI_FORMAT_R16G16B16A16_FLOAT,	//	VP_VELOCITY0 
	DXGI_FORMAT_R16_FLOAT,			//	VP_PRESSURE
	DXGI_FORMAT_R16_FLOAT			//	VP_COLOR
};

dx103DFluidData::dx103DFluidData()
{
	D3D_TEXTURE3D_DESC desc;
	desc.BindFlags = D3D10_BIND_SHADER_RESOURCE | D3D10_BIND_RENDER_TARGET;
	desc.CPUAccessFlags = 0; 
	desc.MipLevels = 1;
	desc.MiscFlags = 0;
	desc.Usage = D3D_USAGE_DEFAULT;
	desc.Width =  FluidManager.GetTextureWidth();
	desc.Height = FluidManager.GetTextureHeight();
	desc.Depth =  FluidManager.GetTextureDepth();

	D3D_SHADER_RESOURCE_VIEW_DESC SRVDesc;
	ZeroMemory( &SRVDesc, sizeof(SRVDesc) );
	SRVDesc.ViewDimension = D3D_SRV_DIMENSION_TEXTURE3D;
	SRVDesc.Texture3D.MipLevels = 1;
	SRVDesc.Texture3D.MostDetailedMip = 0;

	for(int rtIndex=0; rtIndex<VP_NUM_TARGETS; rtIndex++)
	{
		desc.Format = m_VPRenderTargetFormats[rtIndex];
		SRVDesc.Format = m_VPRenderTargetFormats[rtIndex];
		CreateRTTextureAndViews( rtIndex, desc );
	}
}

dx103DFluidData::~dx103DFluidData()
{
	//	Allow real-time config reload
#ifdef	DEBUG
	FluidManager.DeregisterFluidData(this);
#endif	//	DEBUG

	for(int rtIndex=0; rtIndex<VP_NUM_TARGETS; rtIndex++)
	{
		DestroyRTTextureAndViews( rtIndex );
	}
}

void dx103DFluidData::CreateRTTextureAndViews(int rtIndex, D3D_TEXTURE3D_DESC TexDesc)
{
	// Create the texture
	CHK_DX( HW.pDevice->CreateTexture3D(&TexDesc,NULL,&m_pRTTextures[rtIndex]));
	// Create the render target view
	
	D3D_RENDER_TARGET_VIEW_DESC DescRT;
	DescRT.Format = TexDesc.Format;
	DescRT.ViewDimension =  D3D_RTV_DIMENSION_TEXTURE3D;
	DescRT.Texture3D.FirstWSlice = 0;
	DescRT.Texture3D.MipSlice = 0;
	DescRT.Texture3D.WSize = TexDesc.Depth;

	CHK_DX( HW.pDevice->CreateRenderTargetView( m_pRTTextures[rtIndex], &DescRT, &m_pRenderTargetViews[rtIndex]) );

	float color[4] = {0, 0, 0, 0 };

	HW.pContext->ClearRenderTargetView( m_pRenderTargetViews[rtIndex], color );
}

void dx103DFluidData::DestroyRTTextureAndViews(int rtIndex)
{
	_RELEASE(m_pRTTextures[rtIndex]);
	_RELEASE(m_pRenderTargetViews[rtIndex]);
}

void dx103DFluidData::Load(IReader *data)
{
	//	Version 3

	xr_string	Profile;
	data->r_string(Profile);

	//	Prepare transform
	data->r( &m_Transform, sizeof(m_Transform) );

	//	Read obstacles
	u32 uiObstCnt = data->r_u32();
	m_Obstacles.reserve(uiObstCnt);
	for(u32 i=0; i<uiObstCnt; ++i)
	{
		Fmatrix		ObstTransform;
		data->r( &ObstTransform, sizeof(ObstTransform) );
		m_Obstacles.push_back(ObstTransform);
	}

	ParseProfile(Profile);
}

void dx103DFluidData::ParseProfile(const xr_string &Profile)
{
	string_path fn;
	FS.update_path(fn, "$game_config$",Profile.c_str());
	
	CInifile	ini(fn,TRUE,TRUE,FALSE);

	Msg("Reading fog volume config: %s", fn);

	m_Settings.m_SimulationType = ST_FOG;
	m_Settings.m_fHemi = 0.2f;
	m_Settings.m_fConfinementScale = 0.06f;
	m_Settings.m_fDecay = 0.994f;
	m_Settings.m_fGravityBuoyancy = 0.0f;

	Fmatrix WorldToFluid;
	{
		Fmatrix InvFluidTranform;
		Fmatrix Scale;
		Fmatrix Translate;
		Fmatrix TranslateScale;
		//	Convert to 0..intDim space since it is used by simulation
		//Scale.scale((float)m_iTextureWidth-1, (float)m_iTextureHeight-1, (float)m_iTextureDepth-1);
		//Translate.translate(0.5, 0.5, 0.5);
		//It seems that y axis is inverted in fluid simulation, so shange maths a bit
		Fvector	vGridDim;
		vGridDim.set( (float)FluidManager.GetTextureWidth(), (float)FluidManager.GetTextureHeight(), (float)FluidManager.GetTextureDepth() );
		Scale.scale(vGridDim.x-1, -(vGridDim.y-1), vGridDim.z-1 );
		Translate.translate(0.5, -0.5, 0.5);
		//	Actually it is mul(Translate, Scale).
		//	Our matrix multiplication is not correct.
		TranslateScale.mul(Scale, Translate);
		InvFluidTranform.invert(m_Transform);
		WorldToFluid.mul(TranslateScale,InvFluidTranform);
	}

	//	Read Volume data
	if (ini.line_exist("volume", "Type"))
		m_Settings.m_SimulationType = (SimulationType)ini.r_token( "volume", "Type", simulation_type_token);

	if (ini.line_exist("volume", "Hemi"))
		m_Settings.m_fHemi = ini.r_float("volume", "Hemi");

	if (ini.line_exist("volume", "ConfinementScale"))
		m_Settings.m_fConfinementScale = ini.r_float("volume", "ConfinementScale");

	if (ini.line_exist("volume", "Decay"))
		m_Settings.m_fDecay = ini.r_float("volume", "Decay");

	if (ini.line_exist("volume", "GravityBuoyancy"))
		m_Settings.m_fGravityBuoyancy = ini.r_float("volume", "GravityBuoyancy");
	

	u32 iEmittersNum = ini.r_u32("volume", "EmittersNum");

	m_Emitters.resize(iEmittersNum);

	for ( u32 i=0; i<iEmittersNum; ++i )
	{
		string32	EmitterSectionName;
		CEmitter	&Emitter = m_Emitters[i];
		ZeroMemory(&Emitter, sizeof(Emitter));
		xr_sprintf(EmitterSectionName, "emitter%02d", i);

		Emitter.m_eType = (dx103DFluidEmitters::EmitterType)ini.r_token( EmitterSectionName, "Type", emitter_type_token);

		if (ini.line_exist(EmitterSectionName, "Position"))
			Emitter.m_vPosition = ini.r_fvector3(EmitterSectionName, "Position");
		else
		{
			Emitter.m_vPosition = ini.r_fvector3(EmitterSectionName, "WorldPosition");
			WorldToFluid.transform(Emitter.m_vPosition);
		}

		Emitter.m_fRadius = ini.r_float(EmitterSectionName, "Radius");

		Emitter.m_InvSigma_2 = ini.r_float(EmitterSectionName, "Sigma");
		VERIFY(Emitter.m_InvSigma_2>0);
		Emitter.m_InvSigma_2 = 1.0f / _sqr(Emitter.m_InvSigma_2);

		Emitter.m_vFlowVelocity = ini.r_fvector3(EmitterSectionName, "FlowDirection");
		float fFlowSpeed = ini.r_float(EmitterSectionName, "FlowSpeed");
		Emitter.m_vFlowVelocity.mul(fFlowSpeed);
		
		Emitter.m_fDensity = ini.r_float(EmitterSectionName, "Density");

		Emitter.m_bApplyDensity = ini.r_bool(EmitterSectionName, "ApplyDensity") ? true : false;
		Emitter.m_bApplyImpulse = ini.r_bool(EmitterSectionName, "ApplyImpulse") ? true : false;

		switch (Emitter.m_eType)
		{
		case dx103DFluidEmitters::ET_SimpleDraught:
			Emitter.m_DraughtParams.m_fPeriod	= ini.r_float(EmitterSectionName, "DraughtPeriod");
			Emitter.m_DraughtParams.m_fPhase	= ini.r_float(EmitterSectionName, "DraughtPhase");
			Emitter.m_DraughtParams.m_fAmp		= ini.r_float(EmitterSectionName, "DraughtAmp");
			VERIFY( Emitter.m_DraughtParams.m_fPeriod > 0.0001f );
			break;
		default:
			break;
		}
	}

	//	Allow real-time config reload
#ifdef	DEBUG
	FluidManager.RegisterFluidData(this, Profile);
#endif	//	DEBUG
}

//	Allow real-time config reload
#ifdef	DEBUG
void dx103DFluidData::ReparseProfile(const xr_string &Profile)
{
	m_Emitters.clear_not_free();
	ParseProfile(Profile);
}
#endif	//	DEBUG