//[-------------------------------------------------------]
//[ Private virtual PLRenderer::SurfacePainter functions  ]
//[-------------------------------------------------------]
void SPRTTShaders::OnPaint(Surface &cSurface)
{
	// Get the used renderer
	Renderer &cRenderer = GetRenderer();

	// Clear the content of the current used render target by using gray (this way, in case on an graphics error we might still see at least something)
	cRenderer.Clear(Clear::Color | Clear::ZBuffer, Color4::Gray);

	// Backup current render target and set the new one to render in our texture buffer
	Surface *pRenderSurfaceBackup = cRenderer.GetRenderTarget();
	if (cRenderer.SetRenderTarget(m_pRenderTarget)) {
		if (m_pColorTarget1)
			cRenderer.SetColorRenderTarget(static_cast<TextureBuffer*>(m_pColorTarget1), 1);
		if (m_pColorTarget2)
			cRenderer.SetColorRenderTarget(static_cast<TextureBuffer*>(m_pColorTarget2), 2);
		if (m_pColorTarget3)
			cRenderer.SetColorRenderTarget(static_cast<TextureBuffer*>(m_pColorTarget3), 3);

		// Draw the scene
		DrawScene(cRenderer);

		// Reset render target
		cRenderer.SetRenderTarget(pRenderSurfaceBackup);
	}

	// This code is similar to the code of the triangle sample. But instead of a
	// triangle we will draw three rotating quadrangles. Further the used vertex
	// buffer has texture coordinates and we apply the 'teapot' texture buffer on the primitives.
	// Clear the content of the current used render target

	// Make our program to the current one
	if (cRenderer.SetProgram(m_pProgram)) {
		// Set program vertex attributes, this creates a connection between "Vertex Buffer Attribute" and "Vertex Shader Attribute"
		m_pProgram->Set("VertexPosition",		   m_pPositionVertexBuffer, VertexBuffer::Position);
		m_pProgram->Set("VertexTextureCoordinate", m_pPositionVertexBuffer, VertexBuffer::TexCoord);
		m_pProgram->Set("VertexColor",			   m_pColorVertexBuffer,    VertexBuffer::Color);

		// Set color factor
		m_pProgram->Set("ColorFactor", 0.0f);

		// Calculate the composed view projection matrix - we need it multiple times
		Matrix4x4 mViewProjection;
		{
			// Calculate the view matrix
			Matrix4x4 mView;
			{
				mView.SetTranslation(0.0f, 0.0f, -5.0f);
			}

			// Calculate the projection matrix
			Matrix4x4 mProjection;
			{
				const float fAspect      = 1.0f;
				const float fAspectRadio = cRenderer.GetViewport().GetWidth()/(cRenderer.GetViewport().GetHeight()*fAspect);
				mProjection.PerspectiveFov(static_cast<float>(45.0f*Math::DegToRad), fAspectRadio, 0.001f, 1000.0f);
			}

			// Calculate the composed view projection matrix
			mViewProjection = mProjection*mView;
		}

		// No back face culling, please. Else we can only see one 'side' of the quadrangle
		cRenderer.SetRenderState(RenderState::CullMode, Cull::None);

		Matrix4x4 mWorld;
		{ // Draw quadrangle 1: Primary render target
			// Set object space to clip space matrix uniform
			ProgramUniform *pProgramUniform = m_pProgram->GetUniform("ObjectSpaceToClipSpaceMatrix");
			if (pProgramUniform) {
				mWorld.SetTranslationMatrix(2.0f, 1.0f, 0.0f);
				pProgramUniform->Set(mViewProjection*mWorld);
			}

			{ // Set the texture buffer we rendered our teapot in as the current texture buffer
				const int nTextureUnit = m_pProgram->Set("DiffuseMap", m_pRenderTarget->GetTextureBuffer());
				if (nTextureUnit >= 0) {
					// Disable mip mapping - this is required because we created/filled no mipmaps for your texture buffer
					cRenderer.SetSamplerState(nTextureUnit, Sampler::MagFilter, TextureFiltering::Linear);
					cRenderer.SetSamplerState(nTextureUnit, Sampler::MinFilter, TextureFiltering::Linear);
					cRenderer.SetSamplerState(nTextureUnit, Sampler::MipFilter, TextureFiltering::None);
				}
			}

			// Draw
			cRenderer.DrawPrimitives(Primitive::TriangleStrip, 0, 4);
		}

		{ // Draw quadrangle 2: Color render target 1
			// Set object space to clip space matrix uniform
			ProgramUniform *pProgramUniform = m_pProgram->GetUniform("ObjectSpaceToClipSpaceMatrix");
			if (pProgramUniform) {
				mWorld.FromEulerAngleY(static_cast<float>(m_fRotation*Math::DegToRad));
				mWorld.SetTranslation(-2.0f, 1.0f, 0.0f);
				pProgramUniform->Set(mViewProjection*mWorld);
			}

			{ // Set the texture buffer we rendered our teapot in as the current texture buffer
				const int nTextureUnit = m_pProgram->Set("DiffuseMap", m_pColorTarget1 ? static_cast<TextureBuffer*>(m_pColorTarget1) : m_pRenderTarget->GetTextureBuffer());
				if (nTextureUnit >= 0) {
					// Disable mip mapping - this is required because we created/filled no mipmaps for your texture buffer
					cRenderer.SetSamplerState(nTextureUnit, Sampler::MagFilter, TextureFiltering::Linear);
					cRenderer.SetSamplerState(nTextureUnit, Sampler::MinFilter, TextureFiltering::Linear);
					cRenderer.SetSamplerState(nTextureUnit, Sampler::MipFilter, TextureFiltering::None);
				}
			}

			// Draw
			cRenderer.DrawPrimitives(Primitive::TriangleStrip, 0, 4);
		}

		{ // Draw quadrangle 3: Color render target 2
			// Set object space to clip space matrix uniform
			ProgramUniform *pProgramUniform = m_pProgram->GetUniform("ObjectSpaceToClipSpaceMatrix");
			if (pProgramUniform) {
				mWorld.FromEulerAngleZ(static_cast<float>(m_fRotation*Math::DegToRad));
				mWorld.SetTranslation(0.0f, 1.0f, 0.0f);
				pProgramUniform->Set(mViewProjection*mWorld);
			}

			{ // Set the texture buffer we rendered our teapot in as the current texture buffer
				const int nTextureUnit = m_pProgram->Set("DiffuseMap", m_pColorTarget2 ? static_cast<TextureBuffer*>(m_pColorTarget2) : m_pRenderTarget->GetTextureBuffer());
				if (nTextureUnit >= 0) {
					// Disable mip mapping - this is required because we created/filled no mipmaps for your texture buffer
					cRenderer.SetSamplerState(nTextureUnit, Sampler::MagFilter, TextureFiltering::Linear);
					cRenderer.SetSamplerState(nTextureUnit, Sampler::MinFilter, TextureFiltering::Linear);
					cRenderer.SetSamplerState(nTextureUnit, Sampler::MipFilter, TextureFiltering::None);
				}
			}

			// Draw
			cRenderer.DrawPrimitives(Primitive::TriangleStrip, 0, 4);
		}

		{ // Draw quadrangle 4: Color render target 3
			// Set object space to clip space matrix uniform
			ProgramUniform *pProgramUniform = m_pProgram->GetUniform("ObjectSpaceToClipSpaceMatrix");
			if (pProgramUniform) {
				mWorld.FromEulerAngleZ(static_cast<float>(-m_fRotation*Math::DegToRad));
				mWorld.SetTranslation(-2.0f, -1.0f, 0.0f);
				pProgramUniform->Set(mViewProjection*mWorld);
			}

			{ // Set the texture buffer we rendered our teapot in as the current texture buffer
				const int nTextureUnit = m_pProgram->Set("DiffuseMap", m_pColorTarget3 ? static_cast<TextureBuffer*>(m_pColorTarget3) : m_pRenderTarget->GetTextureBuffer());
				if (nTextureUnit >= 0) {
					// Disable mip mapping - this is required because we created/filled no mipmaps for your texture buffer
					cRenderer.SetSamplerState(nTextureUnit, Sampler::MagFilter, TextureFiltering::Linear);
					cRenderer.SetSamplerState(nTextureUnit, Sampler::MinFilter, TextureFiltering::Linear);
					cRenderer.SetSamplerState(nTextureUnit, Sampler::MipFilter, TextureFiltering::None);
				}
			}

			// Draw
			cRenderer.DrawPrimitives(Primitive::TriangleStrip, 0, 4);
		}

		{ // Draw quadrangle 4: Primary render target, but with per vertex color - the primitive will be quite colorful :)
			// Set object space to clip space matrix uniform
			ProgramUniform *pProgramUniform = m_pProgram->GetUniform("ObjectSpaceToClipSpaceMatrix");
			if (pProgramUniform) {
				mWorld.FromEulerAngleZ(static_cast<float>(-m_fRotation*Math::DegToRad));
				mWorld.SetTranslation(2.0f, -1.0f, 0.0f);
				pProgramUniform->Set(mViewProjection*mWorld);
			}

			// Set color factor
			m_pProgram->Set("ColorFactor", 1.0f);

			{ // Set the texture buffer we rendered our teapot in as the current texture buffer
				const int nTextureUnit = m_pProgram->Set("DiffuseMap", m_pRenderTarget->GetTextureBuffer());
				if (nTextureUnit >= 0) {
					// Disable mip mapping - this is required because we created/filled no mipmaps for your texture buffer
					cRenderer.SetSamplerState(nTextureUnit, Sampler::MagFilter, TextureFiltering::Linear);
					cRenderer.SetSamplerState(nTextureUnit, Sampler::MinFilter, TextureFiltering::Linear);
					cRenderer.SetSamplerState(nTextureUnit, Sampler::MipFilter, TextureFiltering::None);
				}
			}

			// Draw
			cRenderer.DrawPrimitives(Primitive::TriangleStrip, 0, 4);
		}

		// Change the backface culling back to the default setting
		cRenderer.SetRenderState(RenderState::CullMode, Cull::CCW);
	}

	// Increase the rotation by the current time difference (time past since the last frame)
	// -> Normally, such work should NOT be performed within the rendering step, but we want
	//    to keep the implementation simple in here...
	m_fRotation += Timing::GetInstance()->GetTimeDifference()*50;
}