/** Draws this effect to the given buffer */
XRESULT D3D11PFX_HeightFog::Render(RenderToTextureBuffer* fxbuffer)
{
	D3D11GraphicsEngine* engine = (D3D11GraphicsEngine *)Engine::GraphicsEngine;

	// Save old rendertargets
	ID3D11RenderTargetView* oldRTV = NULL;
	ID3D11DepthStencilView* oldDSV = NULL;
	engine->GetContext()->OMGetRenderTargets(1, &oldRTV, &oldDSV);

	D3D11VShader* vs = engine->GetShaderManager()->GetVShader("VS_PFX");
	D3D11PShader* hfPS = engine->GetShaderManager()->GetPShader("PS_PFX_Heightfog");

	hfPS->Apply();
	vs->Apply();

	HeightfogConstantBuffer cb;
	D3DXMatrixInverse(&cb.InvProj, NULL, &Engine::GAPI->GetProjectionMatrix());

	Engine::GAPI->GetViewMatrix(&cb.InvView);
	D3DXMatrixInverse(&cb.InvView, NULL, &cb.InvView);
	cb.CameraPosition = Engine::GAPI->GetCameraPosition();
	float NearPlane=Engine::GAPI->GetRendererState()->RendererInfo.NearPlane;
	float FarPlane=Engine::GAPI->GetRendererState()->RendererInfo.FarPlane;

	D3DXMATRIX invViewProj, view;
	Engine::GAPI->GetViewMatrix(&view);
	D3DXMatrixMultiply(&invViewProj, &Engine::GAPI->GetProjectionMatrix(), &view);
	D3DXMatrixInverse(&invViewProj, NULL, &invViewProj);

	D3DXVECTOR3 vecFrustum[4];
	/*vecFrustum[0] = D3DXVECTOR3(-1.0f, -1.0f,  0.0f); // xyz
	vecFrustum[1] = D3DXVECTOR3( 1.0f, -1.0f,  0.0f); // Xyz
	vecFrustum[2] = D3DXVECTOR3(-1.0f,  1.0f,  0.0f); // xYz
	vecFrustum[3] = D3DXVECTOR3( 1.0f,  1.0f,  0.0f); // XYz*/
	vecFrustum[0] = D3DXVECTOR3(-1.0f, -1.0f,  1.0f); // xyZ
	vecFrustum[1] = D3DXVECTOR3( 1.0f, -1.0f,  1.0f); // XyZ
	vecFrustum[2] = D3DXVECTOR3(-1.0f,  1.0f,  1.0f); // xYZ
	vecFrustum[3] = D3DXVECTOR3( 1.0f,  1.0f,  1.0f); // XYZ

	// Get world space frustum corners
	PFXVS_ConstantBuffer vcb;
	for( int i = 0; i < 4; i++ )
	{
		D3DXVec3TransformCoord( &vecFrustum[i], &vecFrustum[i], &cb.InvProj );
		D3DXVec3Normalize(&vecFrustum[i], &vecFrustum[i]);
		D3DXVec3TransformNormal( &vecFrustum[i], &vecFrustum[i], &cb.InvView );
		
	}

	/*vcb.PFXVS_FrustumCorners[0] = D3DXVECTOR4(vecFrustum[0].x, vecFrustum[0].y, vecFrustum[0].z, 0.0f);
	vcb.PFXVS_FrustumCorners[1] = D3DXVECTOR4(vecFrustum[2].x, vecFrustum[2].y, vecFrustum[2].z, 0.0f);
	vcb.PFXVS_FrustumCorners[2] = D3DXVECTOR4(vecFrustum[1].x, vecFrustum[1].y, vecFrustum[1].z, 0.0f);

	vcb.PFXVS_FrustumCorners[3] = D3DXVECTOR4(vecFrustum[3].x, vecFrustum[3].y, vecFrustum[3].z, 0.0f);
	vcb.PFXVS_FrustumCorners[4] = D3DXVECTOR4(vecFrustum[1].x, vecFrustum[1].y, vecFrustum[1].z, 0.0f);
	vcb.PFXVS_FrustumCorners[5] = D3DXVECTOR4(vecFrustum[2].x, vecFrustum[2].y, vecFrustum[2].z, 0.0f);*/

	cb.HF_GlobalDensity = Engine::GAPI->GetRendererState()->RendererSettings.FogGlobalDensity;
	cb.HF_HeightFalloff = Engine::GAPI->GetRendererState()->RendererSettings.FogHeightFalloff;
	
	float height = Engine::GAPI->GetRendererState()->RendererSettings.FogHeight;
	D3DXVECTOR3 color = *Engine::GAPI->GetRendererState()->RendererSettings.FogColorMod.toD3DXVECTOR3();

	float fnear = 15000.0f;
	float ffar = 60000.0f;
	float secScale = Engine::GAPI->GetRendererState()->RendererSettings.SectionDrawRadius;

	cb.HF_WeightZNear = std::max(0.0f, WORLD_SECTION_SIZE * ((secScale - 0.5f) * 0.7f) - (ffar - fnear)); // Keep distance from original fog but scale the near-fog up to section draw distance
	cb.HF_WeightZFar = WORLD_SECTION_SIZE * ((secScale - 0.5f) * 0.8f);
	
	float atmoMax = 83200.0f; // Fixme: Calculate!	
	float atmoMin = 27799.9922f;

	cb.HF_WeightZFar = std::min(cb.HF_WeightZFar, atmoMax);
	cb.HF_WeightZNear = std::min(cb.HF_WeightZNear, atmoMin);

	if(Engine::GAPI->GetFogOverride() > 0.0f)
	{
		// Make sure the camera is inside the fog when in fog zone
		height = Toolbox::lerp(height, Engine::GAPI->GetCameraPosition().y + 10000, Engine::GAPI->GetFogOverride()); // TODO: Get this from the actual fog-distance in the fogzone!
		
		// Override fog color when in fog zone
		color = Engine::GAPI->GetFogColor();

		// Make it z-Fog
		cb.HF_HeightFalloff = Toolbox::lerp(cb.HF_HeightFalloff, 0.000001f, Engine::GAPI->GetFogOverride());

		// Turn up density
		cb.HF_GlobalDensity = Toolbox::lerp(cb.HF_GlobalDensity, cb.HF_GlobalDensity * 2, Engine::GAPI->GetFogOverride());

		// Use other fog-values for fog-zones
		float distNear = WORLD_SECTION_SIZE * ((ffar - fnear) / ffar);
		cb.HF_WeightZNear = Toolbox::lerp(cb.HF_WeightZNear, WORLD_SECTION_SIZE * 0.09f, Engine::GAPI->GetFogOverride());
		cb.HF_WeightZFar = Toolbox::lerp(cb.HF_WeightZFar, WORLD_SECTION_SIZE * 0.8, Engine::GAPI->GetFogOverride());
	}

	/*static float s_smoothHeight = Engine::GAPI->GetRendererState()->RendererSettings.FogHeight;
	static float s_smoothZF = cb.HF_WeightZFar;
	static float s_smoothZN = cb.HF_WeightZNear;

	// Fade Z-Far and z-Near
	s_smoothZF = Toolbox::lerp(s_smoothZF, cb.HF_WeightZFar, std::min(Engine::GAPI->GetFrameTimeSec() * 5.0f, 1.0f));
	s_smoothZN = Toolbox::lerp(s_smoothZN, cb.HF_WeightZNear, std::min(Engine::GAPI->GetFrameTimeSec() * 5.0f, 1.0f));

	cb.HF_WeightZNear = s_smoothZN;
	cb.HF_WeightZFar = s_smoothZF;*/

	//static D3DXVECTOR3 s_smoothColor = *Engine::GAPI->GetRendererState()->RendererSettings.FogColorMod.toD3DXVECTOR3();

	// Fade fog height in case it changes
	//s_smoothHeight = Toolbox::lerp(s_smoothHeight, height, std::min(Engine::GAPI->GetFrameTimeSec() * 5.0f, 1.0f));

	// Fade color, because leaving a fogzone would make the fog-color pop otherwise
	//D3DXVec3Lerp(&s_smoothColor, &s_smoothColor, &color, std::min(Engine::GAPI->GetFrameTimeSec() * 5.0f, 1.0f));

	//D3DXVECTOR3 fogColorMod = Engine::GAPI->GetRendererState()->RendererSettings.FogColorMod;
	cb.HF_FogColorMod = color;//Engine::GAPI->GetRendererState()->RendererSettings.FogColorMod;

	cb.HF_FogHeight = height;



	//cb.HF_FogColorMod = Engine::GAPI->GetRendererState()->GraphicsState.FF_FogColor;
	cb.HF_ProjAB = float2(	Engine::GAPI->GetProjectionMatrix()._33,
							Engine::GAPI->GetProjectionMatrix()._34);


	// Modify fog when raining
	float rain = Engine::GAPI->GetRainFXWeight();

	// Color
	D3DXVec3Lerp(cb.HF_FogColorMod.toD3DXVECTOR3(), 
		cb.HF_FogColorMod.toD3DXVECTOR3(), 
		&Engine::GAPI->GetRendererState()->RendererSettings.RainFogColor, 
		std::min(1.0f, rain * 2.0f)); // Scale color faster here, so it looks better on light rain

	// Raining Density, only when not in fogzone
	cb.HF_GlobalDensity = Toolbox::lerp(cb.HF_GlobalDensity, Engine::GAPI->GetRendererState()->RendererSettings.RainFogDensity, rain * (1.0f - Engine::GAPI->GetFogOverride()));




	hfPS->GetConstantBuffer()[0]->UpdateBuffer(&cb);
	hfPS->GetConstantBuffer()[0]->BindToPixelShader(0);

	vs->GetConstantBuffer()[0]->UpdateBuffer(&vcb);
	vs->GetConstantBuffer()[0]->BindToVertexShader(0);

	GSky* sky = Engine::GAPI->GetSky();
	hfPS->GetConstantBuffer()[1]->UpdateBuffer(&sky->GetAtmosphereCB());
	hfPS->GetConstantBuffer()[1]->BindToPixelShader(1);

	engine->GetContext()->OMSetRenderTargets(1, &oldRTV, NULL);

	// Bind depthbuffer
	engine->GetDepthBuffer()->BindToPixelShader(engine->GetContext(), 1);

	Engine::GAPI->GetRendererState()->BlendState.SetDefault();
	//Engine::GAPI->GetRendererState()->BlendState.SetAdditiveBlending();
	Engine::GAPI->GetRendererState()->BlendState.BlendEnabled = true;
	Engine::GAPI->GetRendererState()->BlendState.SetDirty();

	// Copy
	FxRenderer->DrawFullScreenQuad();


	// Restore rendertargets
	ID3D11ShaderResourceView* srv = NULL;
	engine->GetContext()->PSSetShaderResources(1,1,&srv);

	engine->GetContext()->OMSetRenderTargets(1, &oldRTV, oldDSV);
	if(oldRTV)oldRTV->Release();
	if(oldDSV)oldDSV->Release();

	return XR_SUCCESS;
}
/** Fills the associated state object using the given IDs */
void D3D11GraphicsEngineQueued::FillPipelineStateObject(PipelineState* state)
{
	D3D11PipelineState* s = (D3D11PipelineState*)state;

	// Get state objects from id
	D3D11BlendStateInfo* blendState = D3D11ObjectIDs::BlendStateByID[(UINT8)state->BaseState.BlendStateID];
	D3D11RasterizerStateInfo* rasterizerState = D3D11ObjectIDs::RasterizerStateByID[(UINT8)state->BaseState.RasterizerStateID];
	D3D11DepthBufferState* depthStencilState = D3D11ObjectIDs::DepthStateByID[(UINT8)state->BaseState.DepthStencilID];

	// Put them into our queue object
	if(blendState)
		s->BlendState = blendState->State;

	if(rasterizerState)
		s->RasterizerState = rasterizerState->State;

	if(depthStencilState)
		s->DepthStencilState = depthStencilState->State;

	// TODO: Do this for sampler states as well
	s->SamplerState = DefaultSamplerState;

	// Enter rendertargets
	s->RenderTargetViews[0] = Backbuffer->GetRenderTargetView();
	s->NumRenderTargetViews = 1;
	s->DepthStencilView = DepthStencilBuffer->GetDepthStencilView();

	// Enter textures
	for(int i=0;i<8;i++)
	{
		D3D11Texture* tex = (D3D11Texture *)D3D11ObjectIDs::TextureByID[s->BaseState.TextureIDs[i]];
		if(!tex)
			break;

		s->Textures[i] = tex->GetShaderResourceView();
		s->BaseState.NumTextures = i+1;
	}

	// Buffers
	s->ConstantBuffersVS.clear();
	for(int i=0;i<ARRAYSIZE(s->BaseState.ConstantBuffersVS);i++)
	{
		D3D11ConstantBuffer* buf = (D3D11ConstantBuffer *)s->BaseState.ConstantBuffersVS[i];
		if(!buf)
			break;

		s->ConstantBuffersVS.push_back(buf->Get());
	}

	s->ConstantBuffersPS.clear();
	for(int i=0;i<ARRAYSIZE(s->BaseState.ConstantBuffersPS);i++)
	{
		D3D11ConstantBuffer* buf = (D3D11ConstantBuffer *)s->BaseState.ConstantBuffersPS[i];
		if(!buf)
			break;

		s->ConstantBuffersPS.push_back(buf->Get());
	}

	s->ConstantBuffersHDS.clear();
	for(int i=0;i<ARRAYSIZE(s->BaseState.ConstantBuffersHDS);i++)
	{
		D3D11ConstantBuffer* buf = (D3D11ConstantBuffer *)s->BaseState.ConstantBuffersHDS[i];
		if(!buf)
			break;

		s->ConstantBuffersHDS.push_back(buf->Get());
	}

	s->ConstantBuffersGS.clear();
	for(int i=0;i<ARRAYSIZE(s->BaseState.ConstantBuffersGS);i++)
	{
		D3D11ConstantBuffer* buf = (D3D11ConstantBuffer *)s->BaseState.ConstantBuffersGS[i];
		if(!buf)
			break;

		s->ConstantBuffersGS.push_back(buf->Get());
	}

	s->VertexBuffers.clear();
	for(int i=0;i<ARRAYSIZE(s->BaseState.VertexBuffers);i++)
	{
		D3D11VertexBuffer* buf = (D3D11VertexBuffer *)s->BaseState.VertexBuffers[i];
		if(buf)
			s->VertexBuffers.push_back(buf->GetVertexBuffer());
	}

	s->StructuredBuffersVS.clear();
	for(int i=0;i<ARRAYSIZE(s->BaseState.StructuredBuffersVS);i++)
	{
		D3D11VertexBuffer* buf = (D3D11VertexBuffer *)s->BaseState.StructuredBuffersVS[i];
		if(buf)
			s->StructuredBuffersVS.push_back(buf->GetShaderResourceView());
	}

	if(s->BaseState.IndexBuffer)
		s->IndexBuffer = ((D3D11VertexBuffer *)s->BaseState.IndexBuffer)->GetVertexBuffer();

	D3D11PShader* ps = D3D11ObjectIDs::PShadersByID[s->BaseState.PShaderID];
	D3D11VShader* vs = D3D11ObjectIDs::VShadersByID[s->BaseState.VShaderID];
	D3D11HDShader* hds = D3D11ObjectIDs::HDShadersByID[s->BaseState.HDShaderID];
	//D3D11PShader* gs = NULL;//D3D11PShader::ShadersByID[s->BaseState.PShaderID];


	s->PixelShader = ps ? ps->GetShader() : NULL;
	s->VertexShader = vs ? vs->GetShader() : NULL;
	s->InputLayout = vs ? vs->GetInputLayout() : NULL;
	s->HullShader = hds ? hds->GetHShader() : NULL;
	s->DomainShader = hds ? hds->GetDShader() : NULL;
	s->GeometryShader = NULL;//D3D11PShader::ShadersByID[s->BaseState.PShaderID]->GetShader();

	// Build the sort-key
	s->SortItem.stateValue = s->SortItem.Build(s->BaseState);
}
/** Draws this effect to the given buffer */
XRESULT D3D11PFX_GodRays::Render(RenderToTextureBuffer* fxbuffer)
{
	D3D11GraphicsEngine* engine = (D3D11GraphicsEngine *)Engine::GraphicsEngine;

	D3DXVECTOR3 sunPosition = *Engine::GAPI->GetSky()->GetAtmosphereCB().AC_LightPos.toD3DXVECTOR3();
	sunPosition *= Engine::GAPI->GetSky()->GetAtmosphereCB().AC_OuterRadius;
	sunPosition += Engine::GAPI->GetCameraPosition(); // Maybe use cameraposition from sky?

	D3DXMATRIX& view = Engine::GAPI->GetRendererState()->TransformState.TransformView;
	D3DXMATRIX& proj = Engine::GAPI->GetProjectionMatrix();

	D3DXMATRIX viewProj = proj * view;
	D3DXMatrixTranspose(&viewProj, &viewProj);
	D3DXMatrixTranspose(&view, &view);

	D3DXVECTOR3 sunViewPosition;
	D3DXVec3TransformCoord(&sunViewPosition, &sunPosition, &view); // This is for checking if the light is behind the camera
	D3DXVec3TransformCoord(&sunPosition, &sunPosition, &viewProj);

	if(sunViewPosition.z < 0.0f)
		return XR_SUCCESS; // Don't render the godrays when the sun is behind the camera

	GodRayZoomConstantBuffer gcb;
	gcb.GR_Weight = 1.0f;
	gcb.GR_Decay = Engine::GAPI->GetRendererState()->RendererSettings.GodRayDecay;
	gcb.GR_Weight = Engine::GAPI->GetRendererState()->RendererSettings.GodRayWeight;
	gcb.GR_Density = Engine::GAPI->GetRendererState()->RendererSettings.GodRayDensity;
	
	gcb.GR_Center.x = sunPosition.x/2.0f +0.5f;
	gcb.GR_Center.y = sunPosition.y/-2.0f +0.5f;

	gcb.GR_ColorMod = Engine::GAPI->GetRendererState()->RendererSettings.GodRayColorMod;

	if(abs(gcb.GR_Center.x - 0.5f) > 0.5f)
		gcb.GR_Weight *= std::max(0.0f, 1.0f - (abs(gcb.GR_Center.x - 0.5f) - 0.5f) / 0.5f);

	if(abs(gcb.GR_Center.y - 0.5f) > 0.5f)
		gcb.GR_Weight *= std::max(0.0f, 1.0f - (abs(gcb.GR_Center.y - 0.5f) - 0.5f) / 0.5f);



	ID3D11RenderTargetView* oldRTV=NULL;
	ID3D11DepthStencilView* oldDSV=NULL;

	engine->GetContext()->OMGetRenderTargets(1, &oldRTV, &oldDSV);

	D3D11VShader* vs = engine->GetShaderManager()->GetVShader("VS_PFX");
	D3D11PShader* maskPS = engine->GetShaderManager()->GetPShader("PS_PFX_GodRayMask");
	D3D11PShader* zoomPS = engine->GetShaderManager()->GetPShader("PS_PFX_GodRayZoom");
	
	maskPS->Apply();
	vs->Apply();

	// Draw downscaled mask
	engine->GetContext()->OMSetRenderTargets(1, FxRenderer->GetTempBufferDS4_1()->GetRenderTargetViewPtr(), NULL);

	engine->GetHDRBackBuffer()->BindToPixelShader(engine->GetContext(), 0);
	engine->GetGBuffer1()->BindToPixelShader(engine->GetContext(), 1);

	D3D11_VIEWPORT vp;
	vp.TopLeftX = 0.0f;
	vp.TopLeftY = 0.0f;
	vp.MinDepth = 0.0f;
	vp.MaxDepth = 1.0f;
	vp.Width = (float)FxRenderer->GetTempBufferDS4_1()->GetSizeX();
	vp.Height = (float)FxRenderer->GetTempBufferDS4_1()->GetSizeY();

	engine->GetContext()->RSSetViewports(1, &vp);

	FxRenderer->DrawFullScreenQuad();

	// Zoom
	zoomPS->Apply();

	zoomPS->GetConstantBuffer()[0]->UpdateBuffer(&gcb);
	zoomPS->GetConstantBuffer()[0]->BindToPixelShader(0);

	FxRenderer->CopyTextureToRTV(FxRenderer->GetTempBufferDS4_1()->GetShaderResView(), FxRenderer->GetTempBufferDS4_2()->GetRenderTargetView(), INT2(0,0), true);

	// Upscale and blend
	Engine::GAPI->GetRendererState()->BlendState.SetAdditiveBlending();
	Engine::GAPI->GetRendererState()->BlendState.SetDirty();

	FxRenderer->CopyTextureToRTV(FxRenderer->GetTempBufferDS4_2()->GetShaderResView(), oldRTV, INT2(engine->GetResolution().x, engine->GetResolution().y));

	vp.Width = (float)engine->GetResolution().x;
	vp.Height = (float)engine->GetResolution().y;

	engine->GetContext()->RSSetViewports(1, &vp);

	engine->GetContext()->OMSetRenderTargets(1, &oldRTV, oldDSV);
	if(oldRTV)oldRTV->Release();
	if(oldDSV)oldDSV->Release();
	return XR_SUCCESS;
}