/*****************************************************************************
 * $LastChangedDate: 2010-03-26 15:04:03 -0400 (Fri, 26 Mar 2010) $
 * @file
 * @author  Jim E. Brooks  http://www.palomino3d.org
 * @brief   Graph class.
 *//*
 * LEGAL:   COPYRIGHT (C) 2007 JIM E. BROOKS
 *          THIS SOURCE CODE IS RELEASED UNDER THE TERMS
 *          OF THE GNU GENERAL PUBLIC LICENSE VERSION 2 (GPL 2).
 *****************************************************************************/

#define GRAPH_GRAPH_CC 1
#include "base/module.hh"
using namespace base;
#include "graph/module.hh"
#include "graph/graph.hh"

namespace graph {

////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////  Graph  //////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

/*****************************************************************************
 * ctor/dtor.
 *
 * SUBTLE: Another transform node can be passed to the ctor as the root node.
 * Having two transform nodes is useful, since it allows setting
 * the correct orientation of a 3D model, which then is transformed
 * by the usual transform node.
 *****************************************************************************/
Graph::Graph( RefPtr<Node> node, const string& name )
:   mSwitchNode(new osg::Switch),
    mTransformNode(new osg::MatrixTransform),
    mStateSet(new osg::StateSet)
{
    SET_TYPESIG(this,TYPESIG_GRAPH);

    // Prevent side-effects from OSG's optimizer such as FLATTEN_STATIC_TRANSFORMS.
    node->setDataVariance( osg::Object::DYNAMIC );
    mSwitchNode->setDataVariance( osg::Object::DYNAMIC );
    mTransformNode->setDataVariance( osg::Object::DYNAMIC );
    mStateSet->setDataVariance( osg::Object::DYNAMIC );

    // Attach stateset to top node.
    mSwitchNode->setStateSet( mStateSet.get() );

    // Splice a transform node.
    mTransformNode->addChild( node.get() );

    // Splice a Switch node.
    // The switch node is used to enable/disable rendering graph.
    mSwitchNode->addChild( mTransformNode.get() );

    // Solves problem with lighting in 3D models that are scaled.
    // http://www.3drealtimesimulation.com/osg/osg_faq_1.htm#f19
    mStateSet->setMode( GL_RESCALE_NORMAL, osg::StateAttribute::ON );

    // Set name of graph.
    SetName( name );
}

Graph::~Graph()
{
    INVALIDATE_TYPESIG(this,TYPESIG_GRAPH);
}

/*****************************************************************************
 * Make a minimal-but-usable Graph for use with an Object.
 *****************************************************************************/
shptr<Graph>
Graph::MakeMinimalGraph( void )  // CLASS_METHOD
{
    // This must create a new Graph as some Objects build the Graph with more nodes.
    return new Graph( new Node );
}

/*****************************************************************************
 * Set name of graph.
 *****************************************************************************/
void
Graph::SetName( const string& name )
{
CHECK_TYPESIG(this,TYPESIG_GRAPH);

    // Set name of node for searchability in case scene-graph is dumped.
    if ( not name.empty() )
        GetRootNode()->setName( string("graph_") + name );
}

/*****************************************************************************
 * Attach a node to Graph.
 *****************************************************************************/
void
Graph::AttachNode( RefPtr<Node> node )
{
CHECK_TYPESIG(this,TYPESIG_GRAPH);
ASSERT( node.get() != mTransformNode.get() );  // prevent cyclic graph!

    // Attach as a child of transform node.
    mTransformNode->addChild( node.get() );
}

/*****************************************************************************
 * Enable/disable rendering a Graph.
 *
 * NOTE: This isn't effective for enabling models per-view in OSG.
 * The reason is that OSG generates a render list from the scene graph,
 * then renders every camera.  So trying to alter nodes is too late in the pipeline.
 * Rather, use an "cull mask" for the osg::Camera with a "node mask" for the model.
 * See View::ExcludeFromMainView().
 *****************************************************************************/
void
Graph::Enable( const bool enable )
{
CHECK_TYPESIG(this,TYPESIG_GRAPH);

    GraphEnablement::Enable( *mSwitchNode, enable );  // private base class
}

bool
Graph::IfEnabled( void )
{
CHECK_TYPESIG(this,TYPESIG_GRAPH);
ASSERT( mSwitchNode->getNumChildren() == 1 );

    return GraphEnablement::IfEnabled( *mSwitchNode );  // private base class
}

} // namespace graph
