/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2010 Robert Osfield 
 *
 * This library is open source and may be redistributed and/or modified under  
 * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or 
 * (at your option) any later version.  The full license is in LICENSE file
 * included with this distribution, and on the openscenegraph.org website.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 * OpenSceneGraph Public License for more details.
*/
// Written by Wang Rui, (C) 2010

#ifndef OSGPARTICLE_DAMPINGOPERATOR
#define OSGPARTICLE_DAMPINGOPERATOR

#include <osgParticle/Operator>
#include <osgParticle/Particle>

namespace osgParticle
{


/** A damping operator applies damping constant to particle's velocity.
    Refer to David McAllister's Particle System API (http://www.particlesystems.org)
*/
class DampingOperator : public Operator
{
public:
    DampingOperator() : Operator(), _cutoffLow(0.0f), _cutoffHigh(FLT_MAX)
    { _damping.set(1.0f, 1.0f, 1.0f); }
    
    DampingOperator( const DampingOperator& copy, const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY )
    :   Operator(copy, copyop), _damping(copy._damping),
        _cutoffLow(copy._cutoffLow), _cutoffHigh(copy._cutoffHigh)
    {}
    
    META_Object( osgParticle, DampingOperator );
    
    /// Set the damping factors
    void setDamping( float x, float y, float z ) { _damping.set(x, y, z); }
    void setDamping( const osg::Vec3& damping ) { _damping = damping; }
    
    /// Set the damping factors to one value
    void setDamping( float x ) { _damping.set(x, x, x); }
    
    /// Get the damping factors
    void getDamping( float& x, float& y, float& z ) const
    { x = _damping.x(); y = _damping.y(); z = _damping.z(); }
    
    const osg::Vec3& getDamping() const { return _damping; }
    
    /// Set the velocity cutoff factors
    void setCutoff( float low, float high ) { _cutoffLow = low; _cutoffHigh = high; }
    void setCutoffLow( float low ) { _cutoffLow = low; }
    void setCutoffHigh( float low ) { _cutoffHigh = low; }
    
    /// Get the velocity cutoff factors
    void getCutoff( float& low, float& high ) const { low = _cutoffLow; high = _cutoffHigh; }
    float getCutoffLow() const { return _cutoffLow; }
    float getCutoffHigh() const { return _cutoffHigh; }
    
    /// Apply the acceleration to a particle. Do not call this method manually.
    inline void operate( Particle* P, double dt );
    
protected:
    virtual ~DampingOperator() {}
    DampingOperator& operator=( const DampingOperator& ) { return *this; }
    
    osg::Vec3 _damping;
    float _cutoffLow;
    float _cutoffHigh;
};

// INLINE METHODS

inline void DampingOperator::operate( Particle* P, double dt )
{
    const osg::Vec3& vel = P->getVelocity();
    float length2 = vel.length2();
    if ( length2>=_cutoffLow && length2<=_cutoffHigh )
    {
        osg::Vec3 newvel( vel.x() * (1.0f - (1.0f - _damping.x()) * dt),
                          vel.y() * (1.0f - (1.0f - _damping.y()) * dt),
                          vel.z() * (1.0f - (1.0f - _damping.z()) * dt) );
        P->setVelocity( newvel );
    }
}


}

#endif
