/*****************************************************************************
 * $LastChangedDate: 2010-03-26 15:04:03 -0400 (Fri, 26 Mar 2010) $
 * @file
 * @author  Jim E. Brooks  http://www.palomino3d.org
 * @brief   Shader functions.
 * @remarks For uniforms that change, create an instance of the template class Uniform<>.
 *          For uniforms assigned only once, use the function SetShaderUniform().
 *//*
 * LEGAL:   COPYRIGHT (C) 2009 JIM E. BROOKS
 *          THIS SOURCE CODE IS RELEASED UNDER THE TERMS
 *          OF THE GNU GENERAL PUBLIC LICENSE VERSION 2 (GPL 2).
 *****************************************************************************/

#ifndef SHADER_SHADER_FUNCS_HH
#define SHADER_SHADER_FUNCS_HH 1

#include "gfx/rgba.hh"
using namespace gfx;
#include "graph/typedefs.hh"
using namespace graph;
#include <osg/Node>
#include <osg/StateSet>
#include <osg/Program>
#include <osg/Uniform>

namespace shader {

void
DisableShader( osg::StateSet& stateSet );

void
ResetShaderUniformsForModel( Node& node );

void
ResetShaderUniformsForRootNode( Node& node );

void
SetShaderFog( Node& node, const fp fogDensity, const RGBA fogColor );

////////////////////////////////////////////////////////////////////////////////
/// @brief Template class that abstracts a shader uniform (for uniforms that change).
///
/// Usage:
/// Intended for uniforms that change frequently.
/// An instance of Uniform can be stored as a member and its value reassigned.
///
/// Design notes:
/// Abstracts OSG and covers pitfall of osg::Uniform having multiple types
/// as this template class forms a distinct type such as Uniform<osg::Vec3>.
///
template<class T>
class Uniform : public osg::Referenced
{
public:
    Uniform( const string& name, const T& val )
    :   mOsgUniform(new osg::Uniform(name.c_str(),val))
    {
    }

    void Set( const T& val )
    {
        mOsgUniform->set( val );
    }

    T Get( void )
    {
        T val;
        val = mOsgUniform->get( &val );
        return val;
    }

    // Attach uniform to a node.
    void Attach( RefPtr<Node> node )
    {
        // addUniform() will replace existing uniform with name name.
        node->getOrCreateStateSet()->addUniform( mOsgUniform.get() );
    }

private:
    RefPtr<osg::Uniform>    mOsgUniform;
};

/*****************************************************************************
 * Convenience function to assign a uniform to a node.
 * This is slow -- it's only meant for one-time assignment of a uniform.
 * If a uniform will be changed frequently, instead use "new Uniform".
 *****************************************************************************/
template<typename T>
CLASS_METHOD void
SetShaderUniform( const string& uniformName, const T& val, RefPtr<osg::StateSet> stateset )
{
    // Try to change existing uniform, else create it.
    SafePtr<osg::Uniform> uni = stateset->getUniform( uniformName );
    if ( uni != NULL )
        uni->set( val );
    else
        stateset->addUniform( new osg::Uniform( uniformName.c_str(), val ) );
}

/*****************************************************************************
 * Convenience function to assign a uniform to a node.
 * This is slow -- it's only meant for one-time assignment of a uniform.
 * If a uniform will be changed frequently, instead use "new Uniform".
 *****************************************************************************/
template<typename T>
CLASS_METHOD void
SetShaderUniform( const string& uniformName, const T& val, Node& node )
{
    SetShaderUniform( uniformName, val, node.getOrCreateStateSet() );
}

} // namespace shader

#endif // SHADER_SHADER_FUNCS_HH
