#include "stdafx.h"
#include "ShadowBuffer.h"


void ShadowBuffer::InitializeShadowbufferOnce(void)
{
	if (m_IsInitialized)
		return;

	InitializeShadowbuffer();

	m_IsInitialized = true;
}

void ShadowBuffer::InitializeShadowbuffer(void)
{
	SetBufSizeToSquare();

	InitializeDethBuffer();

	InitializeColorBuffer();

	AssignFBOToFrameBufObjects();
}

void ShadowBuffer::InitializeDethBuffer(void)
{
	m_ShadowBuffer.GenerateTexture();
	m_ShadowBuffer.Bind();
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);

	glTexImage2D(
		GL_TEXTURE_2D,
		0, GL_DEPTH_COMPONENT,
		m_ShadowmapWidth,
		m_ShadowmapHeight,
		0,
		GL_DEPTH_COMPONENT,
		GL_FLOAT,
		0);
}

void ShadowBuffer::InitializeColorBuffer(void)
{
	m_RenderBuffer.Generate();
	m_RenderBuffer.Bind();
	glRenderbufferStorageEXT(
		GL_RENDERBUFFER_EXT,
		GL_RGBA8,
		m_ShadowmapWidth,
		m_ShadowmapHeight);
}

void ShadowBuffer::AssignFBOToFrameBufObjects(void)
{
	m_Fbo.Generate();
	m_Fbo.BeginFBO();

	// for depth texture
	m_Fbo.BindFboTexture(GL_DEPTH_ATTACHMENT_EXT, m_ShadowBuffer.GetID(), 0);

	// for color color renderbuffer
	m_Fbo.BindRenderBuffer(GL_COLOR_ATTACHMENT0_EXT, m_RenderBuffer.GetID());

	m_Fbo.EndFBO();
}


void ShadowBuffer::SetGLShadowMatrix(const lm::vec3f& light_dir, const lm::vec3f& range_center, float DepthRange, float MapWidth) const
{
	glPushAttrib(GL_TRANSFORM_BIT);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();

	float vw = (float)m_ShadowmapWidth;
	float vh = (float)m_ShadowmapHeight;
	float min_size = (std::min)( vw , vh );
	float w = MapWidth * vw / min_size;
	float h = MapWidth * vh / min_size;

	float half_w = w * 0.5f;
	float half_h = h * 0.5f;

	glOrtho( -half_w , half_w , -half_h , half_h , 0.0f , DepthRange );

	glPopAttrib();

	const lm::vec3f& c = range_center;
	lm::vec3f lp = c + light_dir * DepthRange * 0.5f;
	lm::vec3f lb = cross( light_dir , lm::vector3f( 0.0f , 1.0f , 0.0f ) );
	lm::vec3f up = cross( lb , light_dir ).get_normalize();
	gluLookAt( lp.x , lp.y , lp.z , c.x , c.y , c.z , up.x , up.y , up.z );
}


void ShadowBuffer::UpdateShadowMatrix(void)
{
	glGetFloatv(GL_PROJECTION_MATRIX, m_ShadowMatrix.v());
}


void ShadowBuffer::BeginCreateShadowmap(void)
{
	m_Fbo.BeginFBO();
}

void ShadowBuffer::EndCreateShadowmap(void)
{
	m_Fbo.EndFBO();
}

int ShadowBuffer::GetWidth(void) const
{
	return m_ShadowmapWidth;
}

int ShadowBuffer::GetHeight(void) const
{
	return m_ShadowmapHeight;
}

void ShadowBuffer::SetBufSizeToSquare(void)
{
	m_ShadowmapWidth  = m_ShadowMapTexels;
	m_ShadowmapHeight = m_ShadowMapTexels;
}
