
//**************************************************************************/
// Copyright (c) 2008 Autodesk, Inc.
// All rights reserved.
//
// Use of this software is subject to the terms of the Autodesk license
// agreement provided at the time of installation or download, or which
// otherwise accompanies this software in either electronic or hard copy form.
//
//**************************************************************************/
// DESCRIPTION:
// CREATED: October 2008
//**************************************************************************/

#include "SampleViewportFilter.h"
#include <QtCore/QDir>

#define _USE_MATH_DEFINES
#include <cmath>

using namespace mudbox;

// Filter plugin macros
IMPLEMENT_VCLASS( SampleViewPortFilter, ViewPortFilter, "Sample Viewport Filter", 1 );

MB_PLUGIN( "Sample Filter", "Sample Viewport Filter", "Autodesk", "http://www.mudbox3d.com", 0 );

// Render texture to screen to activate shaders.

static void drawFullScreenQuad() {
	glMatrixMode( GL_PROJECTION );
	glLoadIdentity();
	glMatrixMode( GL_MODELVIEW );
	glLoadIdentity();

	glBegin( GL_QUADS );

	glTexCoord2f( 0, 0 );
	glVertex2f( -1, -1 );

	glTexCoord2f( 1, 0 );
	glVertex2f( 1, -1 );

	glTexCoord2f( 1, 1 );
	glVertex2f( 1, 1 );

	glTexCoord2f( 0, 1 );
	glVertex2f( -1, 1 );

	glEnd();
}

// Renders full screens quad to texture specified
static void gpuTransform(Texture* const outputTexture) {
	outputTexture->SetAsRenderTarget();
	drawFullScreenQuad();
	outputTexture->RestoreRenderTarget();
}

void SampleViewPortFilter::OnNodeEvent( const Attribute &cAttribute, NodeEventType eType ) {
	if(eType == etValueChanged) {
		{
			Kernel()->Redraw();
		}
	}
}

SampleViewPortFilter::SampleViewPortFilter() :
	m_aGaussianBlur(this, "Gaussian"),
	m_aBlurWidth(this, "Blur Width")
{
	// Cg context
	m_CGContext = cgCreateContext();
	cgGLSetDebugMode( CG_FALSE );

	cgGLSetManageTextureParameters(m_CGContext, CG_TRUE);
	cgSetParameterSettingMode(m_CGContext, CG_DEFERRED_PARAMETER_SETTING);

	/* Compile and load the vertex program. */
	m_FragmentProfile = cgGLGetLatestProfile(CG_GL_FRAGMENT);
	cgGLSetOptimalOptions(m_FragmentProfile);
	
	// Get the path to where the plug-in was loaded from
	QDir pluginDir( Kernel()->PluginDirectory("Sample Filter") );
	QFileInfo cgPath( pluginDir, QString("Blur.cg") );

	/* Compile and load the fragment program. */
	m_BlurProgram =
		cgCreateProgramFromFile(
		m_CGContext,                /* Cg runtime context */
		CG_SOURCE,                  /* Program in human-readable form */
		cgPath.filePath().toAscii().constData(),			/* Name of file containing program */
		m_FragmentProfile,/* Profile: OpenGL ARB vertex program */
		"main",      /* Entry function name */
		NULL);                      /* No extra compiler options */
		
	if ( m_BlurProgram == NULL ) Kernel()->HUDMessageShow("Failed to load Blur.cg", Kernel::HUDmsgFade );


	cgGLLoadProgram(m_BlurProgram);

	m_BlurColorTextureParam = cgGetNamedParameter(m_BlurProgram, "colorTexture");
	m_BlurWidthParam = cgGetNamedParameter(m_BlurProgram, "BlurWidth");
	m_GaussianBlurParam = cgGetNamedParameter(m_BlurProgram, "GaussianBlur");

	m_aGaussianBlur = true;

	m_aBlurWidth.SetMax(.0050f);
	m_aBlurWidth.SetMin(.0001f);
	m_aBlurWidth = 0.0012f;

	// Create the output texture
	m_pResultTexture = CreateInstance<Texture>();
}

SampleViewPortFilter::~SampleViewPortFilter()
{
	if( m_pResultTexture )
		delete m_pResultTexture;

	if (m_CGContext)
		cgDestroyContext(m_CGContext);
};

void SampleViewPortFilter::Process( ViewPortState &s ) 
{
	enum Image::Format eFormat = s.m_bHDRNeeded ? Image::e16float : Image::e8integer;

	// Set of variables not actually used for anything other than to show
	// how to access the values.
	const float cameraNear = Kernel()->Scene()->ActiveCamera()->Near();
	const float cameraFar = Kernel()->Scene()->ActiveCamera()->Far();
	const float cameraFOV = Kernel()->Scene()->ActiveCamera()->FOV();
	const float cameraAspectRatio = Kernel()->Scene()->ActiveCamera()->AspectRatio();
	const float cameraHeight = tanf(cameraFOV / 2.0f) * cameraNear;
	const float cameraWidth = cameraHeight * cameraAspectRatio;
	const float cameraTop = -cameraHeight;
	const float cameraBottom = cameraHeight;
	const float cameraLeft = -cameraWidth;
	const float cameraRight = cameraWidth;
	
	// Ensure our output texture is same size & type as the incoming color buffer.
	// Get width and height of the viewport
	const int width = s.m_pColor->Width();
	const int height = s.m_pColor->Height();

	// Smallest of width and height
	const int extendMin = width < height ? width : height;

	// Logarithm of the SMALLER extend, in order to be at least 1 pixel in size in any dimension
	const int levelCountMax = (int)floorf(logf(float(extendMin)) / float(M_LN2));

	// See if the input size or format changed. If so, re-create the texture.
	if(	m_pResultTexture->Width() != s.m_pColor->Width() || 
		m_pResultTexture->Height() != s.m_pColor->Height())
	{
		m_pResultTexture->Create(s.m_pColor->Width(), s.m_pColor->Height(), 4, eFormat);
	};
	m_pResultTexture->SetLocation( TexturePool::locationGPU );

	// Enable Cg fragment profile
	cgGLEnableProfile(m_FragmentProfile);

	// Set up some gl states
	glDisable(GL_DEPTH_TEST);
	glDisable(GL_BLEND);
	glDisable(GL_CULL_FACE);

	cgGLBindProgram(m_BlurProgram);

	// Set the incoming color buffer on the shader
	cgGLSetTextureParameter(m_BlurColorTextureParam, s.m_pColor->OpenGLName());

	// Set blur width value
	cgSetParameter1f(m_BlurWidthParam, m_aBlurWidth);

	// Set Gaussian usage value
	cgSetParameter1i(m_GaussianBlurParam, m_aGaussianBlur);

	cgUpdateProgramParameters(m_BlurProgram);

	// Render to the resulting texture.  This activates the shader pass.
    gpuTransform(m_pResultTexture);
    
	cgGLDisableProfile(m_FragmentProfile);

	// Assign the resulting texture to the color buffer
	s.m_pColor = m_pResultTexture;
};

void SampleViewPortFilter::SetVisible( bool bVisible )
{
	ViewPortFilter::SetVisible( bVisible );

	if( bVisible )
	{
		if( !m_pResultTexture )
			m_pResultTexture = CreateInstance<Texture>();
	}
	else
	{
		delete m_pResultTexture;
		m_pResultTexture = 0;
	};
};
