/*****************************************************************************
 * $LastChangedDate: 2010-11-19 23:51:16 -0500 (Fri, 19 Nov 2010) $
 * @file
 * @author  Jim E. Brooks  http://www.palomino3d.org
 * @brief   State-sorting.
 *//*
 * 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).
 *****************************************************************************/

#define GRAPH_NODE_SORT_CC 1
#include "base/module.hh"
using namespace base;
#include "math/module.hh"
#include "math/vertex.hh"
using namespace math;
#include "shader/module.hh"
#include "shader/defs.hh"
#include "shader/shader_funcs.hh"
#include "shader/shader_factory.hh"
#if COMPILE_HW_LIGHT
#include "shader/light_hw.hh"
#endif
using namespace shader;
#include "graph/module.hh"
#include "graph/node_sort.hh"

#include <osgUtil/Optimizer>
#include <osg/Depth>
#include <osg/BlendFunc>
#include <osg/FrontFace>

namespace graph {

// Pre-allocate OSG state objects.
// Back-face culling is done by OpenGL (not OSG) according to vertex order (rather than normal vectors).
INTERN RefPtr<osg::BlendFunc> sOsgBlendFunc                 = new osg::BlendFunc;
INTERN RefPtr<osg::Depth>     sOsgDepth                     = new osg::Depth;
INTERN RefPtr<osg::FrontFace> sOsgFrontFaceClockWise        = new osg::FrontFace( osg::FrontFace::CLOCKWISE );
INTERN RefPtr<osg::FrontFace> sOsgFrontFaceCounterClockWise = new osg::FrontFace( osg::FrontFace::COUNTER_CLOCKWISE );

////////////////////////////////////////////////////////////////////////////////
////////////////////////////////  NodeSort  ////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

RefPtr<osg::StateSet> NodeSort::msDefaultStateSet = new osg::StateSet;

// REMOVE_REDUNDANT_NODES breaks this app's ability to find subgraphs in 3D models.
NodeSort::Optimizations::Int NodeSort::Optimizations::msDefault = \
    osgUtil::Optimizer::FLATTEN_STATIC_TRANSFORMS
//| osgUtil::Optimizer::REMOVE_REDUNDANT_NODES
  | osgUtil::Optimizer::COMBINE_ADJACENT_LODS
  | osgUtil::Optimizer::SHARE_DUPLICATE_STATE
  | osgUtil::Optimizer::MERGE_GEOMETRY
  | osgUtil::Optimizer::SPATIALIZE_GROUPS
  | osgUtil::Optimizer::TRISTRIP_GEOMETRY
  | osgUtil::Optimizer::OPTIMIZE_TEXTURE_SETTINGS
  | osgUtil::Optimizer::MERGE_GEODES;

/*****************************************************************************
 * Helper to create a group node with commonly-used state.
 *****************************************************************************/
RefPtr<GroupNode>
NodeSort::CreateGroupNode( void ) const
{
CHECK_TYPESIG(this,TYPESIG_NODE_SORT);
    const bool ifDefaultShader = mShaderName.IfDefaultShader();
    RefPtr<GroupNode> groupNode = new GroupNode;
#if COMPILE_HW_LIGHT
    const bool ifHwLight = (mShaderName == SHADER_NAME_HW);
    if ( ifHwLight ) groupNode = GET_HW_LIGHT().MakeHwLightGroup().get();
#endif
    SafePtr<osg::StateSet> stateSet = groupNode->getOrCreateStateSet();

    // A child inherits state from its parent.
    // OVERRIDE forces applying a state to all children, regardless.
//  const osg::StateAttribute::OverrideValue override = osg::StateAttribute::OVERRIDE;  // breaks NO_CULL_FACE
    const osg::StateAttribute::OverrideValue override = osg::StateAttribute::OVERRIDE | osg::StateAttribute::PROTECTED;

    if ( mAttribs.mVal & Attribs::NO_CULL_FACE )  // disable
    {
        stateSet->setMode( GL_CULL_FACE, osg::StateAttribute::OFF | override );
    }
    else if ( mAttribs.mVal & Attribs::CULL_FACE_CLOCKWISE )
    {
        // Do back-face culling CLOCKWISE.
        stateSet->setAttribute( sOsgFrontFaceClockWise.get(), osg::StateAttribute::ON );
    }
    else
    {
        // Do back-face culling COUNTER-CLOCKWISE (default).
        stateSet->setAttribute( sOsgFrontFaceCounterClockWise.get(), osg::StateAttribute::ON );
    }

    if ( mAttribs.mVal & Attribs::NO_SHADER )
    {
        shader::DisableShader( *stateSet );
        stateSet->setMode( GL_LIGHTING, osg::StateAttribute::OFF | override );
    }

    if ( mAttribs.mVal & Attribs::NO_FOG )
    {
        stateSet->setMode( GL_FOG, osg::StateAttribute::OFF | override );
    }

    if ( mAttribs.mVal & Attribs::NO_DEPTH_TEST )
    {
        stateSet->setMode( GL_DEPTH_TEST, osg::StateAttribute::OFF | override );
    }

    if ( mAttribs.mVal & Attribs::BLEND )
    {
        // Enable blend mode.
        stateSet->setMode( GL_BLEND, osg::StateAttribute::ON | override );

        // Use transparent render bin.
        stateSet->setAttributeAndModes( sOsgBlendFunc, osg::StateAttribute::ON | override );
        stateSet->setRenderingHint( osg::StateSet::TRANSPARENT_BIN );

        // In OSG, to render transparent nodes, disable depth testing and depth writing.
        // This way of rendering transparent polygons is peculiar to OSG.

        // Disable depth test.
        stateSet->setMode( GL_DEPTH_TEST, osg::StateAttribute::OFF | override );

        // Disable writing to depth buffer.
        sOsgDepth->setWriteMask( false );
        sOsgDepth->setFunction( osg::Depth::LEQUAL );
        stateSet->setAttributeAndModes( sOsgDepth, osg::StateAttribute::ON | override );
    }

#if COMPILE_HW_LIGHT
    if ( ifHwLight )
    {
        shader::DisableShader( *stateSet );
        stateSet->setMode( GL_LIGHTING, osg::StateAttribute::ON | override );
    }
    else
#endif
    // Attach shader program to group node.
    if ( not ifDefaultShader )  // override if shader specified
    {
        GET_SHADER_FACTORY().AttachShader( mShaderName, groupNode.get() );
    }

    return groupNode;
}

} // namespace graph
