///////////////////////////////////////////////
//Ambient occlusion
///////////////////////////////////////////////
AVOID DeferredRenderer::CalculateAmbientOcclusion(CameraPtr pCamera)
{
	m_pSSAOShaders->VBind();

	m_pVertices->Set(0, 0);

	//bind matrix constant buffer to the pipeline
	Mat4x4 trans;
	trans.CreateTranslation(pCamera->GetLookAt());

	Mat4x4 rot;
	rot = rot.CreateRollPitchYaw(pCamera->GetRoll(), pCamera->GetPitch(), pCamera->GetYaw());

	Mat4x4 WVP = rot * trans * pCamera->GetView() * CreateOrthoProjectionLH(SCREEN_WIDTH, SCREEN_HEIGHT, 0.1f, 1000.0f);
	WVP.Transpose();
	m_pMatrixBuffer->UpdateSubresource(0, NULL, &WVP, 0, 0);
	m_pMatrixBuffer->Set(0, ST_Vertex);

	//camera params
	Vec cameraParams = Vector(pCamera->GetFrustum().GetNearWidth(), pCamera->GetFrustum().GetNearHeight(), 0.0f, 0.0f);
	m_pcbFrustumSize->UpdateSubresource(0, NULL, &cameraParams, 0, 0);
	m_pcbFrustumSize->Set(0, ST_Pixel);

	//depth buffer params
	Vec depthBufferParams = Vector(1.0f / SCREEN_WIDTH, 1.0f / SCREEN_HEIGHT, 0.0f, 0.0f);
	m_pcbDepthBuffer->UpdateSubresource(0, NULL, &depthBufferParams, 0, 0);
	m_pcbDepthBuffer->Set(1, ST_Pixel); 

	//camera view matrix
	Mat4x4 View = pCamera->GetView();
	View.Transpose();
	m_pcbView->UpdateSubresource(0, NULL, &View, 0, 0);
	m_pcbView->Set(2, ST_Pixel);

	//send SSAO params
	//Vec ssaoParams = Vector(20.0f, 2.0f, 5.0f, 2.0f);
	//m_pcbSSAOParams->UpdateSubresource(0, NULL, &ssaoParams, 0, 0);
	//m_pcbSSAOParams->Set(2, ST_Pixel);
	
	m_pDepthDisableStencilDisable->Set(0);
	//m_pDepthSRV->Set(0, ST_Pixel);
	m_gbuffer.m_SRVList.SetView(2, 0, ST_Pixel);
	m_gbuffer.m_SRVList.SetView(5, 1, ST_Pixel);
	m_pSSAORTV->Set();
	
	//set sampler states
	//AnisotropySampler16()->Set(0, ST_Pixel);
	PointClampSampler()->Set(0, ST_Pixel);
	D3D11DeviceContext()->Draw(6, 0);

	//Unbind render target view
	UnbindRenderTargetViews(1);

	//m_pSSAOSRV->Set(5, ST_Pixel); 
	UnbindShaderResourceViews(0, 2, ST_Pixel);
	GetRenderTargetView()->Set(); 
}
///////////////////////////////////////////////
//Motion Blur
///////////////////////////////////////////////
AVOID DeferredRenderer::VGenerateVelocityMap(CameraPtr pCamera, Renderer* pRenderer, const Mat4x4 & viewproj)
{
	//set screen-aligned quad
	m_pVertices->Set(0, 0);

	//set shaders
	m_pVelocityMapShaders->VBind();

	//set shader resources
	m_gbuffer.BindPositionTex(0, ST_Pixel);

	//set render target
	m_pVelocityRTV->Set();

	//set constant buffers

	//bind matrix constant buffer to the pipeline
	Mat4x4 trans;
	//trans.CreateTranslation(pCamera->GetLookAt() + 10000 * pCamera->GetDir());
	trans.CreateTranslation(pCamera->GetPosition() + 500* pCamera->GetDir());

	Mat4x4 rot;
	rot = rot.CreateRollPitchYaw(pCamera->GetRoll(), pCamera->GetPitch(), pCamera->GetYaw());

	Mat4x4 WVP = rot * trans * pCamera->GetView() * CreateOrthoProjectionLH(SCREEN_WIDTH, SCREEN_HEIGHT, 1.0f, 1000.0f);
	WVP.Transpose();
	m_pMatrixBuffer->UpdateSubresource(0, NULL, &WVP, 0, 0);
	m_pMatrixBuffer->Set(0, ST_Vertex);

	//set previous frame view-projection transform
	Mat4x4 currViewProj = viewproj;
	Mat4x4 prevViewProj = pCamera->GetPrevView() * pCamera->GetPrevProjection();
	//prevViewProj = pCamera->GetView() * pCamera->GetProjection();
	//prevViewProj = pCamera->GetProjection();
	currViewProj.Transpose();
	prevViewProj.Transpose();
	//m_pcbPrevViewProj->UpdateSubresource(0, NULL, &prevViewProj, 0, 0);
	//m_pcbPrevViewProj->Set(0, ST_Pixel);

	struct TwoMatrices
	{
		Mat4x4 prevViewProj;
		Mat4x4 currViewProj;
	};
	TwoMatrices temp;
	temp.prevViewProj =prevViewProj;
	temp.currViewProj = currViewProj;

	m_pcbWorldPlusWVP->UpdateSubresource(0, nullptr, &temp, 0, 0);
	m_pcbWorldPlusWVP->Set(0, ST_Pixel);

	//Finally render velocity map
	Draw(6, 0);

	//unbind views
	UnbindShaderResourceViews(0, 1, ST_Pixel);
	UnbindRenderTargetViews(1);
}
AVOID DeferredRenderer::PrepareForLightPass(CameraPtr pCamera)                                                                                                                                                                                                                                       
{
	//set vertex buffer with positions
	m_pVertices->Set(0, 0);

	//set vertex buffer with texture data
	m_pTexCoords->Set(1, 0);

	//bind matrix constant buffer to the pipeline
	Mat4x4 trans;
	//trans.CreateTranslation(pCamera->GetLookAt() + 10000 * pCamera->GetDir());
	trans.CreateTranslation(pCamera->GetPosition() + 500* pCamera->GetDir());

	Mat4x4 rot;
	rot = rot.CreateRollPitchYaw(pCamera->GetRoll(), pCamera->GetPitch(), pCamera->GetYaw());

	Mat4x4 WVP = rot * trans * pCamera->GetView() * CreateOrthoProjectionLH(SCREEN_WIDTH, SCREEN_HEIGHT, 1.0f, 1000.0f);
	WVP.Transpose();
	m_pMatrixBuffer->UpdateSubresource(0, NULL, &WVP, 0, 0);
	m_pMatrixBuffer->Set(0, ST_Vertex);

	struct CameraBuffer
	{
		Mat4x4 inverseViewProjection;
		Vec pos;
	};
	CameraBuffer cameraBuffer;
	Mat4x4 inverseViewProjection = pCamera->GetViewProjection();
	inverseViewProjection.Inverse();
	cameraBuffer.pos = pCamera->GetPosition();
	cameraBuffer.inverseViewProjection = inverseViewProjection;
	cameraBuffer.inverseViewProjection.Transpose();

	m_pcbCameraPos->UpdateSubresource(0, nullptr, &pCamera->GetPosition(), 0, 0);
	//m_pcbCameraPos->UpdateSubresource(0, NULL, &cameraBuffer, 0, 0);
	m_pcbCameraPos->Set(0, ST_Pixel);

	//pCamera->SetViewport();
	SetGlobalViewport();

	//set shader resources
	m_pSSAOBlurredSRV->Set(6, ST_Pixel);
	m_pDepthSRV->Set(8, ST_Pixel);

	//set blending functionality
	//this->BlendLightPass()->Set(nullptr);
}
AVOID DeferredRenderer::VRender()
{
	/*** Render Scene For Each Camera ***/
 	//for (CameraPtr pCamera : m_cameras)
	for (Cameras::iterator it = m_cameras.begin(); it != m_cameras.end(); it++)
	{
		CameraPtr pCamera = (*it);
		//Keep track of current view and projection matrices
		Mat4x4 view			= pCamera->GetView();
		Mat4x4 projection	= pCamera->GetProjection();
		Mat4x4 viewProj		= view * projection; //calculate view * projection matrix

		VRenderSky(pCamera, viewProj);

		//	Rendering to g-buffer
		m_gbuffer.BindForWriting(m_pDepthDSV);

		// ===================================================== //
		//	Go through sorted render queue and render each mesh  //
		// ===================================================== //
		PrepareForGeometryPass(pCamera);
		VSetStateForGBuffer();
		m_pDepthEnableStencilDisableStandard->Set(1);
		Mesh * pMesh;
		m_queue.Reset();
		while (pMesh = m_queue.Next())
		{
			//set states needed for rendering current mesh
			pMesh->VPreRender(this, view, viewProj); 

			//finally render it
			pMesh->VRender(this);

			//remove previous states
			pMesh->VPostRender(this);
		} 
		//unbind g-buffer views
		m_gbuffer.UnbindFromWriting();

		// ========================================= //
		//		Generate Shadow Maps				 //
		// ========================================= //
		VGenerateShadowMaps();

		// ========================================= //
		//		Generate velocity map				 //
		// ========================================= //
		//VGenerateVelocityMap(pCamera, this, viewProj);

		//clear meshes queue
		m_queue.Clear();
		//m_pDepthDisableStencilDisable->Set(0);

		//filter variance shadow maps
		if (m_bVarianceShadows)
		{
			for (LightList::iterator lit = m_lights.begin(); lit != m_lights.end(); lit++)
			{
				Light* pLight = (*lit);
				for (int i =0; i < 1; i++)
				{
					//FilterImage(pLight->GetVarianceShadowMapSRV(), pLight->GetVarianceShadowUAV(), pLight->GetTempSRV(), pLight->GetTempUAV(), SCREEN_WIDTH, SCREEN_HEIGHT, FT_Gaussian);
				}
			}
		}

		// ========================================= //
		//	Calculate ambient occlusion				 //
		// ========================================= //
		//CalculateAmbientOcclusion(pCamera);
		//FilterImage(m_pSSAOSRV, m_pSSAOBlurredUAV, FT_Gaussian);

		//m_pSSAOSRV->Set(5, ST_Pixel); 
		// ========================================= //
		//	Time for light pass - Render all lights  //
		// ========================================= //

		//set g-buffer for reading and set back buffer as render target
		m_gbuffer.BindForReading(0);
		//SetRenderTargetView(); 
		m_pTempRTV->Set();

		PrepareForLightPass(pCamera);

		//for (Light * pLight : m_lights)
		for (LightList::iterator lit = m_lights.begin(); lit != m_lights.end(); lit++)
		{
			Light* pLight = (*lit);

			//initialize light if it hasn't been yet 
			if (!pLight->IsInitialized())
				pLight->VInitialize(m_pLayout);

			//set states needed for rendering current light
			pLight->VPreRender(this); 

			//scissor test optimization
			pLight->VSetScissorRect(this, pCamera);

			//finally render it
			pLight->VRender();

			//remove all used states
			pLight->VPostRender(this);
		}
		this->NoBlending()->Set(nullptr);

		//VApplyMotionBlur();
		CopyPostProcessingToBackBuffer();

		//clean lights
		while(!m_lights.empty())
		{
			m_lights.pop_back();
		}

		//unbind g-buffer views
		m_gbuffer.UnbindFromReading(0);
		m_gbuffer.Clear();

		UnbindShaderResourceViews(6, 1, ST_Pixel);
		VFinishPass();
	}
}