/*****************************************************************************
 * $LastChangedDate: 2011-02-04 21:32:44 -0500 (Fri, 04 Feb 2011) $
 * @file
 * @author  Jim E. Brooks  http://www.palomino3d.org
 * @brief   Small particles and particle-system.
 * @remarks This implementation was kept simple.  For now, decided to omit
 *          implementing a particle updating mechanism and per-particle attributes.
 *
 * TODO:
 * SmallParticleSystem should do spatial partitioning.
 * Currently it puts all particles in one geode so all share one huge bounding-box.
 *
 *//*
 * LEGAL:   COPYRIGHT (C) 2008 JIM E. BROOKS
 *          THIS SOURCE CODE IS RELEASED UNDER THE TERMS
 *          OF THE GNU GENERAL PUBLIC LICENSE VERSION 2 (GPL 2).
 *****************************************************************************/

#define FX_PARTSYS_SMALL_CC 1
#include "base/module.hh"
using namespace base;
#include "gfx/module.hh"
#include "gfx/osg.hh"
#include "gfx/osg_funcs.hh"
using namespace gfx;
#include "object/module.hh"
using namespace object;
#include "graph/module.hh"
#include "graph/scene_graph.hh"
using namespace graph;
#include "world/module.hh"
using namespace world;
#include "fx/module.hh"
#include "fx/partsys_small.hh"

#include <osg/Point>
#include <osg/PointSprite>

namespace fx {

INTERN const uint       SMALL_PARTICLES_LIMIT   = 5000;
INTERN const uint       SMALL_PARTICLES_RESERVE = 5000;
INTERN const NodeSort   SMALL_PARTSYS_NODE_SORT = NodeSort( fx::defs::PARTSYS_NODE_SORT_ATTRIBS );

////////////////////////////////////////////////////////////////////////////////
///////////////////////  SmallParticleSystem::Args  ////////////////////////////
////////////////////////////////////////////////////////////////////////////////

/*****************************************************************************
 * Args for SmallParticleSystem ctor.
 * @param   lifetime
 * @param   timerEnabled
 * @param   pointType
 *          Regular points or point sprites (point sprites require a texture).
 * @param   pointSpriteSize
 *          For point sprites.
 * @param   stateSet
 *          Texture can be specified in stateset.
 * Example:
 * new SmallParticleSystem( ..., SmallParticleSystem::Args( Texture::NewTextureAsStateSet("smoke.png") ) );
 *****************************************************************************/
SmallParticleSystem::Args::Args( const Milliseconds lifetime,
                                 const bool timerEnabled,
                                 const ePointType pointType,
                                 const uint pointSpriteSize,
                                 RefPtr<osg::StateSet> stateSet )

:   ParticleSystem::Args(lifetime,timerEnabled),
    mPointType(pointType),
    mPointSpriteSize(pointSpriteSize),
    mStateSet(stateSet)
{
    // NOP
}

////////////////////////////////////////////////////////////////////////////////
///////////////////////////  SmallParticleSystem  //////////////////////////////
////////////////////////////////////////////////////////////////////////////////

/*****************************************************************************
 * ctor/dtor.
 *****************************************************************************/
SmallParticleSystem::SmallParticleSystem( const Args& args )
:   ParticleSystem(args),
    // OSG components:
    mStateSet(args.mStateSet),
    mGeode(new osg::Geode),
    mGeom(new osg::Geometry),
    mVertexs(new osg::Vec3Array),
    mDrawArray(new osg::DrawArrays(osg::PrimitiveSet::POINTS,0,0))
{
    SET_TYPESIG(this,TYPESIG_SMALL_PARTICLE_SYSTEM);

    mVertexs->reserve( SMALL_PARTICLES_RESERVE );

    // Configure point or point-sprite:
    const uint pointSize = (args.mPointType == Args::POINT_SPRITES) ? args.mPointSpriteSize : 1;
    mStateSet->setAttribute( new osg::Point( pointSize ) );
    if ( args.mPointType == Args::POINT_SPRITES )
        mStateSet->setTextureAttributeAndModes( 0, new osg::PointSprite, osg::StateAttribute::ON );

    // Compose OSG components:
    mGeode->setStateSet( mStateSet.get() );
    mGeode->addDrawable( mGeom.get() );
    mGeom->setVertexArray( mVertexs.get() );
    mGeom->addPrimitiveSet( mDrawArray.get() );

    // Attach to scene-graph.
    GET_SCENE_GRAPH().AttachNode( mGeode.get(), SMALL_PARTSYS_NODE_SORT );
}

SmallParticleSystem::~SmallParticleSystem()
{
    // Detach from scene-graph.
    GET_SCENE_GRAPH().DetachNode( mGeode.get() );

    INVALIDATE_TYPESIG(this,TYPESIG_SMALL_PARTICLE_SYSTEM);
}

/*****************************************************************************
 * Append SmallParticle.
 *****************************************************************************/
void
SmallParticleSystem::AttachParticle( const SmallParticle& smallParticle )
{
CHECK_TYPESIG(this,TYPESIG_SMALL_PARTICLE_SYSTEM);

    // Modify vertex array.
    mVertexs->push_back( smallParticle.mPosition );

    // Tell OSG vertex array was modified.
    OsgGeometryWasModifiedByResizing( mGeom, mVertexs, mDrawArray );
}

/*****************************************************************************
 * @return Amount of particles.
 *****************************************************************************/
uint
SmallParticleSystem::GetParticleCount( void )
{
CHECK_TYPESIG(this,TYPESIG_SMALL_PARTICLE_SYSTEM);

    return mVertexs->size();
}

/*****************************************************************************
 * Set colors of all particles.
 *****************************************************************************/
void
SmallParticleSystem::SetColorAll( const RGBA color )
{
CHECK_TYPESIG(this,TYPESIG_SMALL_PARTICLE_SYSTEM);

    RefPtr<osg::Vec4Array> colors = new osg::Vec4Array;
    colors->push_back( color );
    mGeom->setColorArray( colors.get() );
    mGeom->setColorBinding( osg::Geometry::BIND_OVERALL );
}

} // namespace fx
