diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 20ddec7a5..8dda9a35d 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -75,6 +75,7 @@ IF(DYNAMIC_OPENSCENEGRAPH) ADD_SUBDIRECTORY(osgparametric) ADD_SUBDIRECTORY(osgparticle) ADD_SUBDIRECTORY(osgparticleeffects) + ADD_SUBDIRECTORY(osgparticleshader) ADD_SUBDIRECTORY(osgpick) ADD_SUBDIRECTORY(osgplanets) ADD_SUBDIRECTORY(osgpoints) diff --git a/examples/osgparticleshader/CMakeLists.txt b/examples/osgparticleshader/CMakeLists.txt new file mode 100644 index 000000000..31ac46154 --- /dev/null +++ b/examples/osgparticleshader/CMakeLists.txt @@ -0,0 +1,5 @@ +SET(TARGET_SRC osgparticleshader.cpp ) +SET(TARGET_ADDED_LIBRARIES osgParticle) + +#### end var setup ### +SETUP_EXAMPLE(osgparticleshader) diff --git a/examples/osgparticleshader/osgparticleshader.cpp b/examples/osgparticleshader/osgparticleshader.cpp new file mode 100644 index 000000000..06747d4d2 --- /dev/null +++ b/examples/osgparticleshader/osgparticleshader.cpp @@ -0,0 +1,194 @@ +/* OpenSceneGraph example, osgparticleshader. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +* THE SOFTWARE. +*/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +void createFountainEffect( osgParticle::ModularEmitter* emitter, osgParticle::ModularProgram* program ) +{ + // Emit specific number of particles every frame + osg::ref_ptr rrc = new osgParticle::RandomRateCounter; + rrc->setRateRange( 500, 2000 ); + + // Accelerate particles in the given gravity direction. + osg::ref_ptr accel = new osgParticle::AccelOperator; + accel->setToGravity(); + + // Multiply each particle's velocity by a damping constant. + osg::ref_ptr damping = new osgParticle::DampingOperator; + damping->setDamping( 0.9f ); + + // Bounce particles off objects defined by one or more domains. + // Supported domains include triangle, rectangle, plane, disk and sphere. + // Since a bounce always happens instantaneously, it will not work correctly with unstable delta-time. + // At present, even the floating error of dt (which are applied to ParticleSystem and Operator seperately) + // causes wrong bounce results. Some one else may have better solutions for this. + osg::ref_ptr bounce = new osgParticle::BounceOperator; + bounce->setFriction( -0.05 ); + bounce->setResilience( 0.35 ); + bounce->addDiskDomain( osg::Vec3(0.0f, 0.0f, -2.0f), osg::Z_AXIS, 8.0f ); + bounce->addPlaneDomain( osg::Plane(osg::Z_AXIS, 5.0f) ); + + // Kill particles going inside/outside of specified domains. + osg::ref_ptr sink = new osgParticle::SinkOperator; + sink->setSinkStrategy( osgParticle::SinkOperator::SINK_OUTSIDE ); + sink->addSphereDomain( osg::Vec3(), 20.0f ); + + emitter->setCounter( rrc.get() ); + program->addOperator( accel.get() ); + program->addOperator( damping.get() ); + program->addOperator( bounce.get() ); + program->addOperator( sink.get() ); +} + +int main( int argc, char** argv ) +{ + osg::ArgumentParser arguments( &argc, argv ); + + std::string textureFile("Images/smoke.rgb"); + while ( arguments.read("--texture", textureFile) ) {} + + float pointSize = 20.0f; + while ( arguments.read("--point", pointSize) ) {} + + double visibilityDistance = -1.0f; + while ( arguments.read("--visibility", visibilityDistance) ) {} + + bool customShape = false; + while ( arguments.read("--enable-custom") ) { customShape = true; } + + bool useShaders = true; + while ( arguments.read("--disable-shaders") ) { useShaders = false; } + + /*** + Customize particle template and system attributes + ***/ + osg::ref_ptr ps = new osgParticle::ParticleSystem; + + osgParticle::Particle& ptemp = ps->getDefaultParticleTemplate(); + ps->getDefaultParticleTemplate().setLifeTime( 5.0f ); + + if ( customShape ) + { + // osgParticle now supports making use of customized drawables. The draw() method will be executed + // and display lists will be called for each particle. It is always a huge consumption of memory, and + // hardly to use shaders to render them, so please be careful using this feature. + ps->getDefaultParticleTemplate().setShape( osgParticle::Particle::USER ); + ps->getDefaultParticleTemplate().setDrawable( new osg::ShapeDrawable(new osg::Box(osg::Vec3(), 1.0f)) ); + useShaders = false; + } + else + { + // The shader only supports rendering points at present. + ps->getDefaultParticleTemplate().setShape( osgParticle::Particle::POINT ); + } + + // Set the visibility distance of particles, due to their Z-value in the eye coordinates. + // Particles that are out of the distance (or behind the eye) will not be rendered. + ps->setVisibilityDistance( visibilityDistance ); + + if ( useShaders ) + { + // Set using local GLSL shaders to render particles. + // At present, this is slightly efficient than ordinary methods. The bottlenack here seems to be the cull + // traversal time. Operators go through the particle list again and again... + ps->setDefaultAttributesUsingShaders( textureFile, true, 0 ); + } + else + { + // The default methods uses glBegin()/glEnd() pairs. Fortunately the GLBeginEndAdapter does improve the + // process, which mimics the immediate mode with glDrawArrays(). + ps->setDefaultAttributes( textureFile, true, false, 0 ); + + // Without the help of shaders, we have to sort particles to make the visibility distance work. Sorting is + // also useful in rendering transparent particles in back-to-front order. + if ( visibilityDistance>0.0 ) + ps->setSortMode( osgParticle::ParticleSystem::SORT_BACK_TO_FRONT ); + } + + // At last, to make the point sprite work, we have to set the points size and the sprite attribute. + osg::StateSet* stateset = ps->getOrCreateStateSet(); + stateset->setAttribute( new osg::Point(pointSize) ); + stateset->setTextureAttributeAndModes( 0, new osg::PointSprite, osg::StateAttribute::ON ); + + /*** + Construct other particle system elements, including the emitter and program + ***/ + osg::ref_ptr emitter = new osgParticle::ModularEmitter; + emitter->setParticleSystem( ps.get() ); + + osg::ref_ptr program = new osgParticle::ModularProgram; + program->setParticleSystem( ps.get() ); + + createFountainEffect( emitter.get(), program.get() ); + + /*** + Add the entire particle system to the scene graph + ***/ + osg::ref_ptr parent = new osg::MatrixTransform; + parent->addChild( emitter.get() ); + parent->addChild( program.get() ); + + // The updater can receive particle systems as child drawables now. The addParticleSystem() method + // is still usable, with which we should define another geode to contain a particle system. + osg::ref_ptr updater = new osgParticle::ParticleSystemUpdater; + updater->addDrawable( ps.get() ); + + osg::ref_ptr root = new osg::Group; + root->addChild( parent.get() ); + root->addChild( updater.get() ); + + /*** + Start the viewer + ***/ + osgViewer::Viewer viewer; + viewer.addEventHandler( new osgGA::StateSetManipulator(viewer.getCamera()->getOrCreateStateSet()) ); + viewer.addEventHandler( new osgViewer::StatsHandler ); + viewer.addEventHandler( new osgViewer::WindowSizeHandler ); + viewer.setSceneData( root.get() ); + viewer.setCameraManipulator( new osgGA::TrackballManipulator ); + + // A floating error of delta-time should be explained here: + // The particles emitter, program and updater all use a 'dt' to compute the time value in every frame. + // Because the 'dt' is a double value, it is not suitable to keep three copies of it seperately, which + // is the previous implementation. The small error makes some opeartors unable to work correctly, e.g. + // the BounceOperator. + // Now we make use of the getDeltaTime() of ParticleSystem to maintain and dispatch the delta time. But.. + // it is not the best solution so far, since there are still very few particles acting unexpectedly. + return viewer.run(); +} diff --git a/include/osgParticle/AngularDampingOperator b/include/osgParticle/AngularDampingOperator new file mode 100644 index 000000000..b2b61667a --- /dev/null +++ b/include/osgParticle/AngularDampingOperator @@ -0,0 +1,94 @@ +/* -*-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_ANGULARDAMPINGOPERATOR +#define OSGPARTICLE_ANGULARDAMPINGOPERATOR + +#include +#include + +namespace osgParticle +{ + + +/** A angular damping operator applies damping constant to particle's angular velocity. + Refer to David McAllister's Particle System API (http://www.particlesystems.org) +*/ +class AngularDampingOperator : public Operator +{ +public: + AngularDampingOperator() : Operator(), _cutoffLow(0.0f), _cutoffHigh(FLT_MAX) + {} + + AngularDampingOperator( const AngularDampingOperator& copy, const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY ) + : Operator(copy, copyop), _damping(copy._damping), + _cutoffLow(copy._cutoffLow), _cutoffHigh(copy._cutoffHigh) + {} + + META_Object( osgParticle, AngularDampingOperator ); + + /// 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 ~AngularDampingOperator() {} + AngularDampingOperator& operator=( const AngularDampingOperator& ) { return *this; } + + osg::Vec3 _damping; + float _cutoffLow; + float _cutoffHigh; +}; + +// INLINE METHODS + +inline void AngularDampingOperator::operate( Particle* P, double dt ) +{ + const osg::Vec3& vel = P->getAngularVelocity(); + 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->setAngularVelocity( newvel ); + } +} + + +} + +#endif diff --git a/include/osgParticle/BounceOperator b/include/osgParticle/BounceOperator new file mode 100644 index 000000000..6244520d4 --- /dev/null +++ b/include/osgParticle/BounceOperator @@ -0,0 +1,78 @@ +/* -*-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_BOUNCEOPERATOR +#define OSGPARTICLE_BOUNCEOPERATOR + +#include +#include + +namespace osgParticle +{ + + +/** A bounce operator can affect the particle's velocity to make it rebound. + Refer to David McAllister's Particle System API (http://www.particlesystems.org) +*/ +class OSGPARTICLE_EXPORT BounceOperator : public DomainOperator +{ +public: + BounceOperator() + : DomainOperator(), _friction(1.0f), _resilience(0.0f), _cutoff(0.0f) + {} + + BounceOperator( const BounceOperator& copy, const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY ) + : DomainOperator(copy, copyop), + _friction(copy._friction), _resilience(copy._resilience), _cutoff(copy._cutoff) + {} + + META_Object( osgParticle, BounceOperator ); + + /// Set the friction + void setFriction( float f ) { _friction = f; } + + /// Get the friction + float getFriction() const { return _friction; } + + /// Set the resilience + void setResilience( float r ) { _resilience = r; } + + /// Get the velocity cutoff factor + float getResilience() const { return _resilience; } + + /// Set the velocity cutoff factor + void setCutoff( float v ) { _cutoff = v; } + + /// Get the velocity cutoff factor + float getCutoff() const { return _cutoff; } + +protected: + virtual ~BounceOperator() {} + BounceOperator& operator=( const BounceOperator& ) { return *this; } + + virtual void handleTriangle( const Domain& domain, Particle* P, double dt ); + virtual void handleRectangle( const Domain& domain, Particle* P, double dt ); + virtual void handlePlane( const Domain& domain, Particle* P, double dt ); + virtual void handleSphere( const Domain& domain, Particle* P, double dt ); + virtual void handleDisk( const Domain& domain, Particle* P, double dt ); + + float _friction; + float _resilience; + float _cutoff; +}; + + +} + +#endif diff --git a/include/osgParticle/BoxPlacer b/include/osgParticle/BoxPlacer index 43a8a4d5a..a65b105bc 100644 --- a/include/osgParticle/BoxPlacer +++ b/include/osgParticle/BoxPlacer @@ -69,6 +69,9 @@ namespace osgParticle /// Place a particle. Do not call it manually. inline void place(Particle* P) const; + + /// return the volume of the box + inline float size() const; /// return the control position inline osg::Vec3 getControlPosition() const; @@ -153,6 +156,13 @@ namespace osgParticle P->setPosition(pos); } + + inline float BoxPlacer::size() const + { + return (_x_range.maximum - _x_range.minimum) * + (_y_range.maximum - _y_range.minimum) * + (_z_range.maximum - _z_range.minimum); + } inline osg::Vec3 BoxPlacer::getControlPosition() const { diff --git a/include/osgParticle/CompositePlacer b/include/osgParticle/CompositePlacer new file mode 100644 index 000000000..e54d7a94f --- /dev/null +++ b/include/osgParticle/CompositePlacer @@ -0,0 +1,104 @@ +/* -*-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_COMPOSITEPLACER +#define OSGPARTICLE_COMPOSITEPLACER + +#include +#include + +namespace osgParticle +{ + + +/** A composite particle placer which allows particles to be generated from a union of placers. */ +class CompositePlacer : public Placer +{ +public: + CompositePlacer() : Placer() {} + + CompositePlacer( const CompositePlacer& copy, const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY ) + : Placer(copy, copyop), _placers(copy._placers) {} + + META_Object( osgParticle, CompositePlacer ); + + // Set a child placer at specific index + void setPlacer( unsigned int i, Placer* p ) + { + if (i<_placers.size()) _placers[i] = p; + else addPlacer(p); + } + + /// Add a child placer + void addPlacer( Placer* p ) { _placers.push_back(p); } + + /// Remove a child placer + void removePlacer( unsigned int i ) + { if (i<_placers.size()) _placers.erase(_placers.begin()+i); } + + /// Get a child placer + Placer* getPlacer( unsigned int i ) { return _placers.at(i); } + const Placer* getPlacer( unsigned int i ) const { return _placers.at(i); } + + /// Get number of placers + unsigned int getNumPlacers() const { return _placers.size(); } + + /// Place a particle. Do not call it manually. + inline void place( Particle* P ) const; + + /// return the volume of the box + inline float size() const; + + /// return the control position + inline osg::Vec3 getControlPosition() const; + +protected: + virtual ~CompositePlacer() {} + CompositePlacer& operator=( const CompositePlacer& ) { return *this; } + + typedef std::vector< osg::ref_ptr > PlacerList; + PlacerList _placers; +}; + +// INLINE METHODS + +inline void CompositePlacer::place( Particle* P ) const +{ + rangef sizeRange( 0.0f, size() ); + float current = 0.0f, selected = sizeRange.get_random(); + for ( PlacerList::const_iterator itr=_placers.begin(); itr!=_placers.end(); ++itr ) + { + current += (*itr)->size(); + if ( selected<=current ) (*itr)->place( P ); + } +} + +inline float CompositePlacer::size() const +{ + float total_size = 0.0f; + for ( PlacerList::const_iterator itr=_placers.begin(); itr!=_placers.end(); ++itr ) + total_size += (*itr)->size(); + return total_size; +} + +inline osg::Vec3 CompositePlacer::getControlPosition() const +{ + if ( !_placers.size() ) return osg::Vec3(); + return _placers.front()->getControlPosition(); +} + + +} + +#endif diff --git a/include/osgParticle/DampingOperator b/include/osgParticle/DampingOperator new file mode 100644 index 000000000..38bca8ba7 --- /dev/null +++ b/include/osgParticle/DampingOperator @@ -0,0 +1,94 @@ +/* -*-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 +#include + +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 diff --git a/include/osgParticle/DomainOperator b/include/osgParticle/DomainOperator new file mode 100644 index 000000000..f7c9e087b --- /dev/null +++ b/include/osgParticle/DomainOperator @@ -0,0 +1,235 @@ +/* -*-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_DOMAINOPERATOR +#define OSGPARTICLE_DOMAINOPERATOR + +#include +#include +#include + +namespace osgParticle +{ + + +/** A domain operator which accepts different domain shapes as the parameter. + It can be derived to implement operators that require particles interacting with domains. + Refer to David McAllister's Particle System API (http://www.particlesystems.org) +*/ +class OSGPARTICLE_EXPORT DomainOperator : public Operator +{ +public: + struct Domain + { + enum Type + { + UNDEFINED_DOMAIN = 0, + POINT_DOMAIN, + LINE_DOMAIN, + TRI_DOMAIN, + RECT_DOMAIN, + PLANE_DOMAIN, + SPHERE_DOMAIN, + BOX_DOMAIN, + DISK_DOMAIN + }; + + Domain( Type t ) : r1(0.0f), r2(0.0f), type(t) {} + osg::Plane plane; + osg::Vec3 v1; + osg::Vec3 v2; + osg::Vec3 v3; + osg::Vec3 s1; + osg::Vec3 s2; + float r1; + float r2; + Type type; + }; + + DomainOperator() + : Operator() + {} + + DomainOperator( const DomainOperator& copy, const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY ) + : Operator(copy, copyop), _domains(copy._domains), _backupDomains(copy._backupDomains) + {} + + META_Object( osgParticle, DomainOperator ); + + /// Add a point domain + inline void addPointDomain( const osg::Vec3& p ); + + /// Add a line segment domain + inline void addLineSegmentDomain( const osg::Vec3& v1, const osg::Vec3& v2 ); + + /// Add a triangle domain + inline void addTriangleDomain( const osg::Vec3& v1, const osg::Vec3& v2, const osg::Vec3& v3 ); + + /// Add a rectangle domain + inline void addRectangleDomain( const osg::Vec3& corner, const osg::Vec3& w, const osg::Vec3& h ); + + /// Add a plane domain + inline void addPlaneDomain( const osg::Plane& plane ); + + /// Add a sphere domain + inline void addSphereDomain( const osg::Vec3& c, float r ); + + /// Add a box domain + inline void addBoxDomain( const osg::Vec3& min, const osg::Vec3& max ); + + /// Add a disk domain + inline void addDiskDomain( const osg::Vec3& c, const osg::Vec3& n, float r1, float r2=0.0f ); + + /// Add a domain object directly, used by the .osg wrappers and serializers. + void addDomain( const Domain& domain ) { _domains.push_back(domain); } + + /// Get a domain object directly, used by the .osg wrappers and serializers. + const Domain& getDomain( unsigned int i ) const { return _domains[i]; } + + /// Remove a domain at specific index + void removeDomain( unsigned int i ) + { if (i<_domains.size()) _domains.erase(_domains.begin() + i); } + + /// Remove all existing domains + void removeAllDomains() { _domains.clear(); } + + /// Get number of domains + unsigned int getNumDomains() const { return _domains.size(); } + + /// Apply the acceleration to a particle. Do not call this method manually. + void operate( Particle* P, double dt ); + + /// Perform some initializations. Do not call this method manually. + void beginOperate( Program* prg ); + + /// Perform some post-operations. Do not call this method manually. + void endOperate(); + +protected: + virtual ~DomainOperator() {} + DomainOperator& operator=( const DomainOperator& ) { return *this; } + + virtual void handlePoint( const Domain& domain, Particle* P, double dt ) { ignore("Point"); } + virtual void handleLineSegment( const Domain& domain, Particle* P, double dt ) { ignore("LineSegment"); } + virtual void handleTriangle( const Domain& domain, Particle* P, double dt ) { ignore("Triangle"); } + virtual void handleRectangle( const Domain& domain, Particle* P, double dt ) { ignore("Rectangle"); } + virtual void handlePlane( const Domain& domain, Particle* P, double dt ) { ignore("Plane"); } + virtual void handleSphere( const Domain& domain, Particle* P, double dt ) { ignore("Sphere"); } + virtual void handleBox( const Domain& domain, Particle* P, double dt ) { ignore("Box"); } + virtual void handleDisk( const Domain& domain, Particle* P, double dt ) { ignore("Disk"); } + + inline void computeNewBasis( const osg::Vec3&, const osg::Vec3&, osg::Vec3&, osg::Vec3& ); + inline void ignore( const std::string& func ); + + std::vector _domains; + std::vector _backupDomains; +}; + +// INLINE METHODS + +inline void DomainOperator::addPointDomain( const osg::Vec3& p ) +{ + Domain domain( Domain::POINT_DOMAIN ); + domain.v1 = p; + _domains.push_back( domain ); +} + +inline void DomainOperator::addLineSegmentDomain( const osg::Vec3& v1, const osg::Vec3& v2 ) +{ + Domain domain( Domain::LINE_DOMAIN ); + domain.v1 = v1; + domain.v2 = v2; + domain.r1 = (v2 - v1).length(); + _domains.push_back( domain ); +} + +inline void DomainOperator::addTriangleDomain( const osg::Vec3& v1, const osg::Vec3& v2, const osg::Vec3& v3 ) +{ + Domain domain( Domain::TRI_DOMAIN ); + domain.v1 = v1; + domain.v2 = v2; + domain.v3 = v3; + domain.plane.set(v1, v2, v3); + computeNewBasis( v2-v1, v3-v1, domain.s1, domain.s2 ); + _domains.push_back( domain ); +} + +inline void DomainOperator::addRectangleDomain( const osg::Vec3& corner, const osg::Vec3& w, const osg::Vec3& h ) +{ + Domain domain( Domain::RECT_DOMAIN ); + domain.v1 = corner; + domain.v2 = w; + domain.v3 = h; + domain.plane.set(corner, corner+w, corner+h); + computeNewBasis( w, h, domain.s1, domain.s2 ); + _domains.push_back( domain ); +} + +inline void DomainOperator::addPlaneDomain( const osg::Plane& plane ) +{ + Domain domain( Domain::PLANE_DOMAIN ); + domain.plane.set(plane); + _domains.push_back( domain ); +} + +inline void DomainOperator::addSphereDomain( const osg::Vec3& c, float r ) +{ + Domain domain( Domain::SPHERE_DOMAIN ); + domain.v1 = c; + domain.r1 = r; + _domains.push_back( domain ); +} + +inline void DomainOperator::addBoxDomain( const osg::Vec3& min, const osg::Vec3& max ) +{ + Domain domain( Domain::BOX_DOMAIN ); + domain.v1 = min; + domain.v2 = max; + _domains.push_back( domain ); +} + +inline void DomainOperator::addDiskDomain( const osg::Vec3& c, const osg::Vec3& n, float r1, float r2 ) +{ + Domain domain( Domain::DISK_DOMAIN ); + domain.v1 = c; + domain.v2 = n; + domain.r1 = r1; + domain.r2 = r2; + domain.plane.set(n, c); + _domains.push_back( domain ); +} + +inline void DomainOperator::computeNewBasis( const osg::Vec3& u, const osg::Vec3& v, osg::Vec3& s1, osg::Vec3& s2 ) +{ + // Copied from David McAllister's Particle System API (http://www.particlesystems.org), pDomain.h + osg::Vec3 w = u ^ v; + float det = w.z()*u.x()*v.y() - w.z()*u.y()*v.x() - u.z()*w.x()*v.y() - + u.x()*v.z()*w.y() + v.z()*w.x()*u.y() + u.z()*v.x()*w.y(); + det = 1.0f / det; + + s1.set( v.y()*w.z() - v.z()*w.y(), v.z()*w.x() - v.x()*w.z(), v.x()*w.y() - v.y()*w.x() ); + s1 = s1 * det; + s2.set( u.y()*w.z() - u.z()*w.y(), u.z()*w.x() - u.x()*w.z(), u.x()*w.y() - u.y()*w.x() ); + s2 = s2 * (-det); +} + +inline void DomainOperator::ignore( const std::string& func ) +{ + OSG_NOTICE << className() << ": " << func << " domain not yet implemented. " << std::endl; +} + + +} + +#endif diff --git a/include/osgParticle/ExplosionOperator b/include/osgParticle/ExplosionOperator new file mode 100644 index 000000000..e80076cd5 --- /dev/null +++ b/include/osgParticle/ExplosionOperator @@ -0,0 +1,124 @@ +/* -*-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_EXPLOSIONOPERATOR +#define OSGPARTICLE_EXPLOSIONOPERATOR + +#include +#include +#include + +namespace osgParticle +{ + + +/** An explosion operator exerts force on each particle away from the explosion center. + Refer to David McAllister's Particle System API (http://www.particlesystems.org) +*/ +class ExplosionOperator : public Operator +{ +public: + ExplosionOperator() + : Operator(), _radius(1.0f), _magnitude(1.0f), _epsilon(1e-3), _sigma(1.0f) + {} + + ExplosionOperator( const ExplosionOperator& copy, const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY ) + : Operator(copy, copyop), _center(copy._center), _radius(copy._radius), + _magnitude(copy._magnitude), _epsilon(copy._epsilon), _sigma(copy._sigma) + {} + + META_Object( osgParticle, ExplosionOperator ); + + /// Set the center of shock wave + void setCenter( const osg::Vec3& c ) { _center = c; } + + /// Get the center of shock wave + const osg::Vec3& getCenter() const { return _center; } + + /// Set the radius of wave peak + void setRadius( float r ) { _radius = r; } + + /// Get the radius of wave peak + float getRadius() const { return _radius; } + + /// Set the acceleration scale + void setMagnitude( float mag ) { _magnitude = mag; } + + /// Get the acceleration scale + float getMagnitude() const { return _magnitude; } + + /// Set the acceleration epsilon + void setEpsilon( float eps ) { _epsilon = eps; } + + /// Get the acceleration epsilon + float getEpsilon() const { return _epsilon; } + + /// Set broadness of the strength of the wave + void setSigma( float s ) { _sigma = s; } + + /// Get broadness of the strength of the wave + float getSigma() const { return _sigma; } + + /// Apply the acceleration to a particle. Do not call this method manually. + inline void operate( Particle* P, double dt ); + + /// Perform some initializations. Do not call this method manually. + inline void beginOperate( Program* prg ); + +protected: + virtual ~ExplosionOperator() {} + ExplosionOperator& operator=( const ExplosionOperator& ) { return *this; } + + osg::Vec3 _center; + osg::Vec3 _xf_center; + float _radius; + float _magnitude; + float _epsilon; + float _sigma; + float _inexp; + float _outexp; +}; + +// INLINE METHODS + +inline void ExplosionOperator::operate( Particle* P, double dt ) +{ + osg::Vec3 dir = P->getPosition() - _xf_center; + float length = dir.length(); + float distanceFromWave2 = (_radius - length) * (_radius - length); + float Gd = exp(distanceFromWave2 * _inexp) * _outexp; + float factor = (_magnitude * dt) / (length * (_epsilon+length*length)); + P->addVelocity( dir * (Gd * factor) ); +} + +inline void ExplosionOperator::beginOperate( Program* prg ) +{ + if ( prg->getReferenceFrame()==ModularProgram::RELATIVE_RF ) + { + _xf_center = prg->transformLocalToWorld(_center); + } + else + { + _xf_center = _center; + } + + float oneOverSigma = (_sigma!=0.0f ? (1.0f / _sigma) : 1.0f); + _inexp = -0.5f * oneOverSigma * oneOverSigma; + _outexp = oneOverSigma / sqrt(osg::PI * 2.0f); +} + + +} + +#endif diff --git a/include/osgParticle/MultiSegmentPlacer b/include/osgParticle/MultiSegmentPlacer index aec636ae2..3bf8b0c1d 100644 --- a/include/osgParticle/MultiSegmentPlacer +++ b/include/osgParticle/MultiSegmentPlacer @@ -64,6 +64,9 @@ namespace osgParticle { /// Place a partice. Called automatically by ModularEmitter, do not call this method manually. void place(Particle* P) const; + /// return the length of the multi-segment + inline float size() const; + /// return the control position inline osg::Vec3 getControlPosition() const; @@ -127,6 +130,11 @@ namespace osgParticle { recompute_length(); } + inline float MultiSegmentPlacer::size() const + { + return _total_length; + } + inline osg::Vec3 MultiSegmentPlacer::getControlPosition() const { return _vx.empty() ? osg::Vec3(0.0f,0.0f,0.0f) : _vx[0].first; diff --git a/include/osgParticle/Operator b/include/osgParticle/Operator index ff35abb77..2bd08bee5 100644 --- a/include/osgParticle/Operator +++ b/include/osgParticle/Operator @@ -47,9 +47,23 @@ namespace osgParticle /// Enable or disable this operator. inline void setEnabled(bool v); - /** Do something on a particle. + /** Do something on all emitted particles. This method is called by ModularProgram objects to perform some operations - on the particles. You must override it in descendant classes. Common operations + on the particles. By default, it will call the operate() method for each particle. + You must override it in descendant classes. + */ + virtual void operateParticles(ParticleSystem* ps, double dt) + { + int n = ps->numParticles(); + for (int i=0; igetParticle(i); + if (P->isAlive() && isEnabled()) operate(P, dt); + } + } + + /** Do something on a particle. + You must override it in descendant classes. Common operations consist of modifying the particle's velocity vector. The dt parameter is the time elapsed from last operation. */ diff --git a/include/osgParticle/OrbitOperator b/include/osgParticle/OrbitOperator new file mode 100644 index 000000000..67a6dcf9d --- /dev/null +++ b/include/osgParticle/OrbitOperator @@ -0,0 +1,112 @@ +/* -*-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_ORBITOPERATOR +#define OSGPARTICLE_ORBITOPERATOR + +#include +#include +#include + +namespace osgParticle +{ + + +/** An orbit operator forces particles in the orbit around a point. + Refer to David McAllister's Particle System API (http://www.particlesystems.org) +*/ +class OrbitOperator : public Operator +{ +public: + OrbitOperator() + : Operator(), _magnitude(1.0f), _epsilon(1e-3), _maxRadius(FLT_MAX) + {} + + OrbitOperator( const OrbitOperator& copy, const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY ) + : Operator(copy, copyop), _center(copy._center), _magnitude(copy._magnitude), + _epsilon(copy._epsilon), _maxRadius(copy._maxRadius) + {} + + META_Object( osgParticle, OrbitOperator ); + + /// Set the center of orbit + void setCenter( const osg::Vec3& c ) { _center = c; } + + /// Get the center of orbit + const osg::Vec3& getCenter() const { return _center; } + + /// Set the acceleration scale + void setMagnitude( float mag ) { _magnitude = mag; } + + /// Get the acceleration scale + float getMagnitude() const { return _magnitude; } + + /// Set the acceleration epsilon + void setEpsilon( float eps ) { _epsilon = eps; } + + /// Get the acceleration epsilon + float getEpsilon() const { return _epsilon; } + + /// Set max radius between the center and the particle + void setMaxRadius( float max ) { _maxRadius = max; } + + /// Get max radius between the center and the particle + float getMaxRadius() const { return _maxRadius; } + + /// Apply the acceleration to a particle. Do not call this method manually. + inline void operate( Particle* P, double dt ); + + /// Perform some initializations. Do not call this method manually. + inline void beginOperate( Program* prg ); + +protected: + virtual ~OrbitOperator() {} + OrbitOperator& operator=( const OrbitOperator& ) { return *this; } + + osg::Vec3 _center; + osg::Vec3 _xf_center; + float _magnitude; + float _epsilon; + float _maxRadius; +}; + +// INLINE METHODS + +inline void OrbitOperator::operate( Particle* P, double dt ) +{ + osg::Vec3 dir = _xf_center - P->getPosition(); + float length = dir.length(); + if ( length<_maxRadius ) + { + P->addVelocity( dir * ((_magnitude * dt) / + (length * (_epsilon+length*length))) ); + } +} + +inline void OrbitOperator::beginOperate( Program* prg ) +{ + if ( prg->getReferenceFrame()==ModularProgram::RELATIVE_RF ) + { + _xf_center = prg->transformLocalToWorld(_center); + } + else + { + _xf_center = _center; + } +} + + +} + +#endif diff --git a/include/osgParticle/Particle b/include/osgParticle/Particle index a0b78398b..7375ca8d4 100644 --- a/include/osgParticle/Particle +++ b/include/osgParticle/Particle @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -47,6 +48,7 @@ namespace osgParticle minimum values are used. */ class OSGPARTICLE_EXPORT Particle { + friend class ParticleSystem; public: enum @@ -63,7 +65,8 @@ namespace osgParticle QUAD, // uses GL_QUADS as primitive QUAD_TRIANGLESTRIP, // uses GL_TRI_angleSTRIP as primitive, but each particle needs a glBegin/glEnd pair HEXAGON, // may save some filling time, but uses more triangles - LINE // uses GL_LINES to draw line segments that point to the direction of motion + LINE, // uses GL_LINES to draw line segments that point to the direction of motion + USER // uses a user-defined drawable as primitive }; Particle(); @@ -139,7 +142,7 @@ namespace osgParticle inline const osg::Vec4& getCurrentColor() const { return _current_color; } /// Get the current alpha - inline float getCurrentAlpha() const { return _current_alpha; } + inline float getCurrentAlpha() const { return _base_prop.z(); } /// Get the s texture coordinate of the bottom left of the particle inline float getSTexCoord() const { return _s_coord; } @@ -226,13 +229,13 @@ namespace osgParticle /// Transform angle and angularVelocity vectors by a matrix. inline void transformAngleVelocity(const osg::Matrix& xform); - /** Update the particle (don't call this method manually). + /** Update the particle (don't call this method manually). This method is called automatically by ParticleSystem::update(); it updates the graphical properties of the particle for the current time, checks whether the particle is still alive, and then updates its position by computing P = P + V * dt (where P is the position and V is the velocity). */ - bool update(double dt); + bool update(double dt, bool onlyTimeStamp); /// Perform some pre-rendering tasks. Called automatically by particle systems. inline void beginRender(osg::GLBeginEndAdapter* gl) const; @@ -240,6 +243,9 @@ namespace osgParticle /// Render the particle. Called automatically by particle systems. void render(osg::GLBeginEndAdapter* gl, const osg::Vec3& xpos, const osg::Vec3& px, const osg::Vec3& py, float scale = 1.0f) const; + /// Render the particle with user-defined drawable + void render(osg::RenderInfo& renderInfo, const osg::Vec3& xpos, const osg::Vec3& xrot) const; + /// Perform some post-rendering tasks. Called automatically by particle systems. inline void endRender(osg::GLBeginEndAdapter* gl) const; @@ -265,6 +271,21 @@ namespace osgParticle /// Get the const next particle inline int getNextParticle() const { return _nextParticle; } + + /// Set the depth of the particle + inline void setDepth(double d) { _depth = d; } + + /// Get the depth of the particle + inline double getDepth() const { return _depth; } + + /// Set the user-defined particle drawable + inline void setDrawable(osg::Drawable* d) { _drawable = d; } + + /// Get the user-defined particle drawable + inline osg::Drawable* getDrawable() const { return _drawable.get(); } + + /// Sorting operator + bool operator<(const Particle &P) const { return _depth < P._depth; } /// Method for initializing a particles texture coords as part of a connected particle system. void setUpTexCoordsAsPartOfConnectedParticleSystem(ParticleSystem* ps); @@ -281,7 +302,7 @@ namespace osgParticle osg::ref_ptr _ai; osg::ref_ptr _ci; - bool _alive; + //bool _alive; bool _mustdie; double _lifeTime; @@ -298,8 +319,9 @@ namespace osgParticle double _t0; - float _current_size; - float _current_alpha; + //float _current_size; + //float _current_alpha; + osg::Vec3 _base_prop; // [0] _alive [1] _current_size [2] _current_alpha osg::Vec4 _current_color; float _s_tile; @@ -313,6 +335,12 @@ namespace osgParticle // previous and next Particles are only used in ConnectedParticleSystems int _previousParticle; int _nextParticle; + + // the depth of the particle is used only when sorting is enabled + double _depth; + + // the particle drawable is used only when USER shape is enabled + osg::ref_ptr _drawable; }; // INLINE FUNCTIONS @@ -329,7 +357,7 @@ namespace osgParticle inline bool Particle::isAlive() const { - return _alive; + return _base_prop.x()>0.0; } inline double Particle::getLifeTime() const @@ -569,7 +597,7 @@ namespace osgParticle inline float Particle::getCurrentSize() const { - return _current_size; + return _base_prop.y(); } diff --git a/include/osgParticle/ParticleProcessor b/include/osgParticle/ParticleProcessor index 8365e03ae..d5d804640 100644 --- a/include/osgParticle/ParticleProcessor +++ b/include/osgParticle/ParticleProcessor @@ -151,7 +151,6 @@ namespace osgParticle private: ReferenceFrame _rf; bool _enabled; - double _t0; osg::ref_ptr _ps; bool _first_ltw_compute; bool _need_ltw_matrix; @@ -197,7 +196,6 @@ namespace osgParticle _enabled = v; if (_enabled) { - _t0 = -1; _currentTime = 0; } } diff --git a/include/osgParticle/ParticleSystem b/include/osgParticle/ParticleSystem index 443237b18..97e4d7e85 100644 --- a/include/osgParticle/ParticleSystem +++ b/include/osgParticle/ParticleSystem @@ -110,6 +110,26 @@ namespace osgParticle */ inline void setDefaultBoundingBox(const osg::BoundingBox& bbox); + /// Return true if we use vertex arrays for rendering particles. + bool getUseVertexArray() const { return _useVertexArray; } + + /** Set to use vertex arrays for rendering particles. + Lots of variables will be omitted: particles' shape, alive or not, visibility distance, and so on, + so the rendering result is not as good as we wish (although it's fast than using glBegin/glEnd). + We had better use this for GLSL shaders, in which particle parameters will be kept as uniforms. + This method is called automatically by setDefaultAttributesUsingShaders(). + */ + void setUseVertexArray(bool v) { _useVertexArray = v; } + + /// Return true if shaders are required. + bool getUseShaders() const { return _useShaders; } + + /** Set to use GLSL shaders for rendering particles. + Particles' parameters will be used as shader attribute arrays, and necessary variables, including + the visibility distance, texture, etc, will be used and updated as uniforms. + */ + void setUseShaders(bool v) { _useShaders = v; _dirty_uniforms = true; } + /// Get the double pass rendering flag. inline bool getDoublePassRendering() const; @@ -157,6 +177,9 @@ namespace osgParticle /// Get the last frame number. inline int getLastFrameNumber() const; + + /// Get the unique delta time for emitters and updaters to use + inline double& getDeltaTime( double currentTime ); /// Get a reference to the default particle template. inline Particle& getDefaultParticleTemplate(); @@ -178,6 +201,12 @@ namespace osgParticle */ void setDefaultAttributes(const std::string& texturefile = "", bool emissive_particles = true, bool lighting = false, int texture_unit = 0); + /** A useful method to set the most common StateAttribute and use GLSL shaders to draw particles. + At present, when enabling shaders in the particle system, user-defined shapes will not be usable. + If texturefile is empty, then texturing is turned off. + */ + void setDefaultAttributesUsingShaders(const std::string& texturefile = "", bool emissive_particles = true, int texture_unit = 0); + /// (EXPERIMENTAL) Get the level of detail. inline int getLevelOfDetail() const; @@ -185,9 +214,32 @@ namespace osgParticle get the actual number of particles to be drawn. This value must be greater than zero. */ inline void setLevelOfDetail(int v); + + enum SortMode + { + NO_SORT, + SORT_FRONT_TO_BACK, + SORT_BACK_TO_FRONT + }; + + /// Get the sort mode. + inline SortMode getSortMode() const; + + /** Set the sort mode. It will force resorting the particle list by the Z direction of the view coordinates. + This can be used for the purpose of transparent rendering or setVisibilityDistance(). + */ + inline void setSortMode(SortMode mode); + + /// Get the visibility distance. + inline double getVisibilityDistance() const; + + /** Set the visibility distance which allows the particles to be rendered only when depth is inside the distance. + When using shaders, it can work well directly; otherwise the sort mode should also be set to pre-compute depth. + */ + inline void setVisibilityDistance(double distance); /// Update the particles. Don't call this directly, use a ParticleSystemUpdater instead. - virtual void update(double dt); + virtual void update(double dt, osg::NodeVisitor& nv); virtual void drawImplementation(osg::RenderInfo& renderInfo) const; @@ -212,7 +264,8 @@ namespace osgParticle ParticleSystem& operator=(const ParticleSystem&) { return *this; } inline void update_bounds(const osg::Vec3& p, float r); - void single_pass_render(osg::State& state, const osg::Matrix& modelview) const; + void single_pass_render(osg::RenderInfo& renderInfo, const osg::Matrix& modelview) const; + void render_vertex_array(osg::RenderInfo& renderInfo) const; typedef std::vector Particle_vector; typedef std::stack Death_stack; @@ -227,6 +280,10 @@ namespace osgParticle osg::Vec3 _align_Y_axis; ParticleScaleReferenceFrame _particleScaleReferenceFrame; + bool _useVertexArray; + bool _useShaders; + bool _dirty_uniforms; + bool _doublepass; bool _frozen; @@ -238,9 +295,16 @@ namespace osgParticle Particle _def_ptemp; mutable int _last_frame; + mutable bool _dirty_dt; bool _freeze_on_cull; + double _t0; + double _dt; + int _detail; + SortMode _sortMode; + double _visibilityDistance; + mutable int _draw_count; mutable ReadWriterMutex _readWriteMutex; @@ -343,6 +407,19 @@ namespace osgParticle { return _last_frame; } + + inline double& ParticleSystem::getDeltaTime( double currentTime ) + { + if ( _dirty_dt ) + { + _dt = currentTime - _t0; + if ( _dt<0.0 ) _dt = 0.0; + + _t0 = currentTime; + _dirty_dt = false; + } + return _dt; + } inline void ParticleSystem::update_bounds(const osg::Vec3& p, float r) { @@ -398,6 +475,27 @@ namespace osgParticle _detail = v; } + inline ParticleSystem::SortMode ParticleSystem::getSortMode() const + { + return _sortMode; + } + + inline void ParticleSystem::setSortMode(SortMode mode) + { + _sortMode = mode; + } + + inline double ParticleSystem::getVisibilityDistance() const + { + return _visibilityDistance; + } + + inline void ParticleSystem::setVisibilityDistance(double distance) + { + _visibilityDistance = distance; + if (_useShaders) _dirty_uniforms = true; + } + // I'm not sure this function should be inlined... inline Particle* ParticleSystem::createParticle(const Particle* ptemplate) diff --git a/include/osgParticle/ParticleSystemUpdater b/include/osgParticle/ParticleSystemUpdater index 05b581e1f..bf8292941 100644 --- a/include/osgParticle/ParticleSystemUpdater +++ b/include/osgParticle/ParticleSystemUpdater @@ -23,7 +23,7 @@ #include #include #include -#include +#include #include #include @@ -36,13 +36,19 @@ namespace osgParticle update() method on the specified particle systems. You should place this updater AFTER other nodes like emitters and programs. */ - class OSGPARTICLE_EXPORT ParticleSystemUpdater: public osg::Node { + class OSGPARTICLE_EXPORT ParticleSystemUpdater: public osg::Geode { public: ParticleSystemUpdater(); ParticleSystemUpdater(const ParticleSystemUpdater& copy, const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY); META_Node(osgParticle,ParticleSystemUpdater); + /// Add a Drawable and call addParticleSystem() if it is a particle system + virtual bool addDrawable(osg::Drawable* drawable); + + /// Remove a Drawable and call removeParticleSystem() if it is a particle system + virtual bool removeDrawable(osg::Drawable* drawable); + /// Add a particle system to the list. virtual bool addParticleSystem(ParticleSystem* ps); @@ -86,7 +92,6 @@ namespace osgParticle typedef std::vector > ParticleSystem_Vector; ParticleSystem_Vector _psv; - double _t0; //added 1/17/06- bgandere@nps.edu //a var to keep from doing multiple updates per frame diff --git a/include/osgParticle/Placer b/include/osgParticle/Placer index aea4d0bf7..65c501c92 100644 --- a/include/osgParticle/Placer +++ b/include/osgParticle/Placer @@ -39,6 +39,9 @@ namespace osgParticle /// Place a particle. Must be implemented in descendant classes. virtual void place(Particle* P) const = 0; + + /// Size of the placer. Can be implemented in descendant classes. + virtual float size() const { return 1.0f; } /// Return the control position of particles that placer will generate. Must be implemented in descendant classes. virtual osg::Vec3 getControlPosition() const = 0; diff --git a/include/osgParticle/SectorPlacer b/include/osgParticle/SectorPlacer index ca1384636..693280f5c 100644 --- a/include/osgParticle/SectorPlacer +++ b/include/osgParticle/SectorPlacer @@ -61,6 +61,9 @@ namespace osgParticle /// Place a particle. Do not call it manually. inline void place(Particle* P) const; + /// return the area of the sector + inline float size() const; + /// return the control position inline osg::Vec3 getControlPosition() const; @@ -129,6 +132,12 @@ namespace osgParticle P->setPosition(pos); } + + inline float SectorPlacer::size() const + { + return 0.5f * (_phi_range.maximum - _phi_range.minimum) * + (_rad_range.maximum*_rad_range.maximum - _rad_range.minimum*_rad_range.minimum); + } inline osg::Vec3 SectorPlacer::getControlPosition() const { diff --git a/include/osgParticle/SegmentPlacer b/include/osgParticle/SegmentPlacer index 4edc72ce5..7661f7236 100644 --- a/include/osgParticle/SegmentPlacer +++ b/include/osgParticle/SegmentPlacer @@ -60,6 +60,9 @@ namespace osgParticle { /// Place a particle. This method is called by ModularEmitter, do not call it manually. inline void place(Particle* P) const; + /// return the length of the segment + inline float size() const; + /// return the control position inline osg::Vec3 getControlPosition() const; @@ -105,6 +108,11 @@ namespace osgParticle { P->setPosition(rangev3(_vertexA, _vertexB).get_random()); } + inline float SegmentPlacer::size() const + { + return (_vertexB - _vertexA).length(); + } + inline void SegmentPlacer::setVertexA(const osg::Vec3& v) { _vertexA = v; diff --git a/include/osgParticle/SinkOperator b/include/osgParticle/SinkOperator new file mode 100644 index 000000000..21b62ebb7 --- /dev/null +++ b/include/osgParticle/SinkOperator @@ -0,0 +1,100 @@ +/* -*-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_SINKOPERATOR +#define OSGPARTICLE_SINKOPERATOR + +#include +#include + +namespace osgParticle +{ + + +/** A sink operator kills particles if positions or velocities inside/outside the specified domain. + Refer to David McAllister's Particle System API (http://www.particlesystems.org) +*/ +class OSGPARTICLE_EXPORT SinkOperator : public DomainOperator +{ +public: + enum SinkTarget { SINK_POSITION, SINK_VELOCITY, SINK_ANGULAR_VELOCITY }; + enum SinkStrategy { SINK_INSIDE, SINK_OUTSIDE }; + + SinkOperator() + : DomainOperator(), _sinkTarget(SINK_POSITION), _sinkStrategy(SINK_INSIDE) + {} + + SinkOperator( const SinkOperator& copy, const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY ) + : DomainOperator(copy, copyop), _sinkTarget(copy._sinkTarget), _sinkStrategy(copy._sinkStrategy) + {} + + META_Object( osgParticle, SinkOperator ); + + /// Set the sink strategy + void setSinkTarget( SinkTarget so ) { _sinkTarget = so; } + + /// Get the sink strategy + SinkTarget getSinkTarget() const { return _sinkTarget; } + + /// Set the sink strategy + void setSinkStrategy( SinkStrategy ss ) { _sinkStrategy = ss; } + + /// Get the sink strategy + SinkStrategy getSinkStrategy() const { return _sinkStrategy; } + + /// Perform some initializations. Do not call this method manually. + void beginOperate( Program* prg ); + +protected: + virtual ~SinkOperator() {} + SinkOperator& operator=( const SinkOperator& ) { return *this; } + + virtual void handlePoint( const Domain& domain, Particle* P, double dt ); + virtual void handleLineSegment( const Domain& domain, Particle* P, double dt ); + virtual void handleTriangle( const Domain& domain, Particle* P, double dt ); + virtual void handleRectangle( const Domain& domain, Particle* P, double dt ); + virtual void handlePlane( const Domain& domain, Particle* P, double dt ); + virtual void handleSphere( const Domain& domain, Particle* P, double dt ); + virtual void handleBox( const Domain& domain, Particle* P, double dt ); + virtual void handleDisk( const Domain& domain, Particle* P, double dt ); + + inline const osg::Vec3& getValue( Particle* P ); + inline void kill( Particle* P, bool insideDomain ); + + SinkTarget _sinkTarget; + SinkStrategy _sinkStrategy; +}; + +// INLINE METHODS + +inline const osg::Vec3& SinkOperator::getValue( Particle* P ) +{ + switch ( _sinkTarget ) + { + case SINK_VELOCITY: return P->getVelocity(); + case SINK_ANGULAR_VELOCITY: return P->getAngularVelocity(); + case SINK_POSITION: default: return P->getPosition(); + } +} + +inline void SinkOperator::kill( Particle* P, bool insideDomain ) +{ + if ( !((_sinkStrategy==SINK_INSIDE) ^ insideDomain) ) + P->kill(); +} + + +} + +#endif diff --git a/src/osgParticle/BounceOperator.cpp b/src/osgParticle/BounceOperator.cpp new file mode 100644 index 000000000..e4632072a --- /dev/null +++ b/src/osgParticle/BounceOperator.cpp @@ -0,0 +1,158 @@ +/* -*-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 + +#include +#include +#include + +using namespace osgParticle; + +void BounceOperator::handleTriangle( const Domain& domain, Particle* P, double dt ) +{ + osg::Vec3 nextpos = P->getPosition() + P->getVelocity() * dt; + float distance = domain.plane.distance( P->getPosition() ); + if ( distance*domain.plane.distance(nextpos)>=0 ) return; + + osg::Vec3 normal = domain.plane.getNormal(); + float nv = normal * P->getVelocity(); + osg::Vec3 hitPoint = P->getPosition() - P->getVelocity() * (distance / nv); + + float upos = (hitPoint - domain.v1) * domain.s1; + float vpos = (hitPoint - domain.v1) * domain.s2; + if ( upos<0.0f || vpos<0.0f || (upos + vpos)>1.0f ) return; + + // Compute tangential and normal components of velocity + osg::Vec3 vn = normal * nv; + osg::Vec3 vt = P->getVelocity() - vn; + + // Compute new velocity + if ( vt.length2()<=_cutoff ) P->setVelocity( vt - vn*_resilience ); + else P->setVelocity( vt*(1.0f-_friction) - vn*_resilience ); +} + +void BounceOperator::handleRectangle( const Domain& domain, Particle* P, double dt ) +{ + osg::Vec3 nextpos = P->getPosition() + P->getVelocity() * dt; + float distance = domain.plane.distance( P->getPosition() ); + if ( distance*domain.plane.distance(nextpos)>=0 ) return; + + osg::Vec3 normal = domain.plane.getNormal(); + float nv = normal * P->getVelocity(); + osg::Vec3 hitPoint = P->getPosition() - P->getVelocity() * (distance / nv); + + float upos = (hitPoint - domain.v1) * domain.s1; + float vpos = (hitPoint - domain.v1) * domain.s2; + if ( upos<0.0f || upos>1.0f || vpos<0.0f || vpos>1.0f ) return; + + // Compute tangential and normal components of velocity + osg::Vec3 vn = normal * nv; + osg::Vec3 vt = P->getVelocity() - vn; + + // Compute new velocity + if ( vt.length2()<=_cutoff ) P->setVelocity( vt - vn*_resilience ); + else P->setVelocity( vt*(1.0f-_friction) - vn*_resilience ); +} + +void BounceOperator::handlePlane( const Domain& domain, Particle* P, double dt ) +{ + osg::Vec3 nextpos = P->getPosition() + P->getVelocity() * dt; + float distance = domain.plane.distance( P->getPosition() ); + if ( distance*domain.plane.distance(nextpos)>=0 ) return; + + osg::Vec3 normal = domain.plane.getNormal(); + float nv = normal * P->getVelocity(); + + // Compute tangential and normal components of velocity + osg::Vec3 vn = normal * nv; + osg::Vec3 vt = P->getVelocity() - vn; + + // Compute new velocity + if ( vt.length2()<=_cutoff ) P->setVelocity( vt - vn*_resilience ); + else P->setVelocity( vt*(1.0f-_friction) - vn*_resilience ); +} + +void BounceOperator::handleSphere( const Domain& domain, Particle* P, double dt ) +{ + osg::Vec3 nextpos = P->getPosition() + P->getVelocity() * dt; + float distance1 = (P->getPosition() - domain.v1).length(); + if ( distance1<=domain.r1 ) // Within the sphere + { + float distance2 = (nextpos - domain.v1).length(); + if ( distance2<=domain.r1 ) return; + + // Bounce back in if going outside + osg::Vec3 normal = domain.v1 - P->getPosition(); normal.normalize(); + float nmag = P->getVelocity() * normal; + + // Compute tangential and normal components of velocity + osg::Vec3 vn = normal * nmag; + osg::Vec3 vt = P->getVelocity() - vn; + if ( nmag<0 ) vn = -vn; + + // Compute new velocity + float tanscale = (vt.length2()<=_cutoff) ? 1.0f : (1.0f - _friction); + P->setVelocity( vt * tanscale + vn * _resilience ); + + // Make sure the particle is fixed to stay inside + nextpos = P->getPosition() + P->getVelocity() * dt; + distance2 = (nextpos - domain.v1).length(); + if ( distance2>domain.r1 ) + { + normal = domain.v1 - nextpos; normal.normalize(); + + osg::Vec3 wishPoint = domain.v1 - normal * (0.999f * domain.r1); + P->setVelocity( (wishPoint - P->getPosition()) / dt ); + } + } + else // Outside the sphere + { + float distance2 = (nextpos - domain.v1).length(); + if ( distance2>domain.r1 ) return; + + // Bounce back out if going inside + osg::Vec3 normal = P->getPosition() - domain.v1; normal.normalize(); + float nmag = P->getVelocity() * normal; + + // Compute tangential and normal components of velocity + osg::Vec3 vn = normal * nmag; + osg::Vec3 vt = P->getVelocity() - vn; + if ( nmag<0 ) vn = -vn; + + // Compute new velocity + float tanscale = (vt.length2()<=_cutoff) ? 1.0f : (1.0f - _friction); + P->setVelocity( vt * tanscale + vn * _resilience ); + } +} + +void BounceOperator::handleDisk( const Domain& domain, Particle* P, double dt ) +{ + osg::Vec3 nextpos = P->getPosition() + P->getVelocity() * dt; + float distance = domain.plane.distance( P->getPosition() ); + if ( distance*domain.plane.distance(nextpos)>=0 ) return; + + osg::Vec3 normal = domain.plane.getNormal(); + float nv = normal * P->getVelocity(); + osg::Vec3 hitPoint = P->getPosition() - P->getVelocity() * (distance / nv); + + float radius = (hitPoint - domain.v1).length(); + if ( radius>domain.r1 || radiusgetVelocity() - vn; + + // Compute new velocity + if ( vt.length2()<=_cutoff ) P->setVelocity( vt - vn*_resilience ); + else P->setVelocity( vt*(1.0f-_friction) - vn*_resilience ); +} diff --git a/src/osgParticle/CMakeLists.txt b/src/osgParticle/CMakeLists.txt index 39c460807..328cb9466 100644 --- a/src/osgParticle/CMakeLists.txt +++ b/src/osgParticle/CMakeLists.txt @@ -48,6 +48,15 @@ SET(LIB_PUBLIC_HEADERS ${HEADER_PATH}/SmokeTrailEffect ${HEADER_PATH}/VariableRateCounter ${HEADER_PATH}/Version + + ${HEADER_PATH}/CompositePlacer + ${HEADER_PATH}/AngularDampingOperator + ${HEADER_PATH}/DampingOperator + ${HEADER_PATH}/ExplosionOperator + ${HEADER_PATH}/OrbitOperator + ${HEADER_PATH}/DomainOperator + ${HEADER_PATH}/BounceOperator + ${HEADER_PATH}/SinkOperator ) # FIXME: For OS X, need flag for Framework or dylib @@ -74,6 +83,10 @@ ADD_LIBRARY(${LIB_NAME} SmokeEffect.cpp SmokeTrailEffect.cpp Version.cpp + + DomainOperator.cpp + BounceOperator.cpp + SinkOperator.cpp ${OPENSCENEGRAPH_VERSIONINFO_RC} ) diff --git a/src/osgParticle/DomainOperator.cpp b/src/osgParticle/DomainOperator.cpp new file mode 100644 index 000000000..d0445554c --- /dev/null +++ b/src/osgParticle/DomainOperator.cpp @@ -0,0 +1,115 @@ +/* -*-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 + +#include +#include +#include + +using namespace osgParticle; + +void DomainOperator::operate( Particle* P, double dt ) +{ + for ( std::vector::iterator itr=_domains.begin(); itr!=_domains.end(); ++itr ) + { + switch ( itr->type ) + { + case Domain::POINT_DOMAIN: + handlePoint( *itr, P, dt ); + break; + case Domain::LINE_DOMAIN: + handleLineSegment( *itr, P, dt ); + break; + case Domain::TRI_DOMAIN: + handleTriangle( *itr, P, dt ); + break; + case Domain::RECT_DOMAIN: + handleRectangle( *itr, P, dt ); + break; + case Domain::PLANE_DOMAIN: + handlePlane( *itr, P, dt ); + break; + case Domain::SPHERE_DOMAIN: + handleSphere( *itr, P, dt ); + break; + case Domain::BOX_DOMAIN: + handleBox( *itr, P, dt ); + break; + case Domain::DISK_DOMAIN: + handleDisk( *itr, P, dt ); + break; + default: break; + } + } +} + +void DomainOperator::beginOperate( Program* prg ) +{ + if ( prg->getReferenceFrame()==ModularProgram::RELATIVE_RF ) + { + _backupDomains = _domains; + for ( std::vector::iterator itr=_domains.begin(); itr!=_domains.end(); ++itr ) + { + Domain& domain = *itr; + switch ( domain.type ) + { + case Domain::POINT_DOMAIN: + domain.v1 = prg->transformLocalToWorld(domain.v1); + break; + case Domain::LINE_DOMAIN: + domain.v1 = prg->transformLocalToWorld(domain.v1); + domain.v2 = prg->transformLocalToWorld(domain.v2); + break; + case Domain::TRI_DOMAIN: + domain.v1 = prg->transformLocalToWorld(domain.v1); + domain.v2 = prg->transformLocalToWorld(domain.v2); + domain.v3 = prg->transformLocalToWorld(domain.v3); + domain.plane.set(domain.v1, domain.v2, domain.v3); + computeNewBasis( domain.v2-domain.v1, domain.v3-domain.v1, domain.s1, domain.s2 ); + break; + case Domain::RECT_DOMAIN: + domain.v1 = prg->transformLocalToWorld(domain.v1); + domain.v2 = prg->rotateLocalToWorld(domain.v2); // Width vector + domain.v3 = prg->rotateLocalToWorld(domain.v3); // Height vector + domain.plane.set(domain.v1, domain.v1+domain.v2, domain.v1+domain.v3); + computeNewBasis( domain.v2, domain.v3, domain.s1, domain.s2 ); + break; + case Domain::PLANE_DOMAIN: + domain.plane.transformProvidingInverse( prg->getLocalToWorldMatrix() ); + break; + case Domain::SPHERE_DOMAIN: + domain.v1 = prg->transformLocalToWorld(domain.v1); + break; + case Domain::BOX_DOMAIN: + domain.v1 = prg->transformLocalToWorld(domain.v1); + domain.v2 = prg->transformLocalToWorld(domain.v2); + break; + case Domain::DISK_DOMAIN: + domain.v1 = prg->transformLocalToWorld(domain.v1); + domain.v2 = prg->rotateLocalToWorld(domain.v2); + domain.v2.normalize(); // Normal + break; + default: break; + } + } + } +} + +void DomainOperator::endOperate() +{ + if ( _backupDomains.size()>0 ) + { + _domains = _backupDomains; + _backupDomains.clear(); + } +} diff --git a/src/osgParticle/ModularProgram.cpp b/src/osgParticle/ModularProgram.cpp index ae1bfad97..6977e7e0f 100644 --- a/src/osgParticle/ModularProgram.cpp +++ b/src/osgParticle/ModularProgram.cpp @@ -25,13 +25,7 @@ void osgParticle::ModularProgram::execute(double dt) ParticleSystem* ps = getParticleSystem(); for (ci=_operators.begin(); ci!=ci_end; ++ci) { (*ci)->beginOperate(this); - int n = ps->numParticles(); - for (int i=0; igetParticle(i); - if (P->isAlive() && (*ci)->isEnabled()) { - (*ci)->operate(P, dt); - } - } + (*ci)->operateParticles(ps, dt); (*ci)->endOperate(); } } diff --git a/src/osgParticle/Particle.cpp b/src/osgParticle/Particle.cpp index 68f9b5deb..b72f04bde 100644 --- a/src/osgParticle/Particle.cpp +++ b/src/osgParticle/Particle.cpp @@ -28,7 +28,7 @@ osgParticle::Particle::Particle() _si(new LinearInterpolator), _ai(new LinearInterpolator), _ci(new LinearInterpolator), - _alive(true), + //_alive(true), _mustdie(false), _lifeTime(2), _radius(0.2f), @@ -41,8 +41,8 @@ osgParticle::Particle::Particle() _angle(0, 0, 0), _angul_arvel(0, 0, 0), _t0(0), - _current_size(0), - _current_alpha(0), + //_current_size(0), + //_current_alpha(0), _s_tile(1.0f), _t_tile(1.0f), _start_tile(0), @@ -51,16 +51,18 @@ osgParticle::Particle::Particle() _s_coord(0.0f), _t_coord(0.0f), _previousParticle(INVALID_INDEX), - _nextParticle(INVALID_INDEX) + _nextParticle(INVALID_INDEX), + _depth(0.0) { + _base_prop.set(1.0f, 0.0f, 0.0f); } -bool osgParticle::Particle::update(double dt) +bool osgParticle::Particle::update(double dt, bool onlyTimeStamp) { // this method should return false when the particle dies; // so, if we were instructed to die, do it now and return. if (_mustdie) { - _alive = false; + _base_prop.x() = -1.0; return false; } @@ -75,9 +77,30 @@ bool osgParticle::Particle::update(double dt) // if our age is over the lifetime limit, then die and return. if (x > 1) { - _alive = false; + _base_prop.x() = -1.0; return false; } + + // compute the current values for size, alpha and color. + if (_lifeTime <= 0) { + if (dt == _t0) { + _base_prop.y() = _sr.get_random(); + _base_prop.z() = _ar.get_random(); + _current_color = _cr.get_random(); + } + } else { + _base_prop.y() = _si.get()->interpolate(x, _sr); + _base_prop.z() = _ai.get()->interpolate(x, _ar); + _current_color = _ci.get()->interpolate(x, _cr); + } + + // update position + _prev_pos = _position; + _position += _velocity * dt; + + // return now if we indicate that only time stamp should be updated + // the shader will handle remain properties in this case + if (onlyTimeStamp) return true; //Compute the current texture tile based on our normalized age int currentTile = _start_tile + static_cast(x * getNumTiles()); @@ -93,23 +116,6 @@ bool osgParticle::Particle::update(double dt) // OSG_NOTICE<interpolate(x, _sr); - _current_alpha = _ai.get()->interpolate(x, _ar); - _current_color = _ci.get()->interpolate(x, _cr); - } - - // update position - _prev_pos = _position; - _position += _velocity * dt; - // update angle _prev_angle = _angle; _angle += _angul_arvel * dt; @@ -129,10 +135,10 @@ void osgParticle::Particle::render(osg::GLBeginEndAdapter* gl, const osg::Vec3& gl->Color4f( _current_color.x(), _current_color.y(), _current_color.z(), - _current_color.w() * _current_alpha); + _current_color.w() * _base_prop.z()); - osg::Vec3 p1(px * _current_size * scale); - osg::Vec3 p2(py * _current_size * scale); + osg::Vec3 p1(px * _base_prop.y() * scale); + osg::Vec3 p2(py * _base_prop.y() * scale); switch (_shape) { @@ -199,7 +205,7 @@ void osgParticle::Particle::render(osg::GLBeginEndAdapter* gl, const osg::Vec3& // calculation of one of the linesegment endpoints. float vl = _velocity.length(); if (vl != 0) { - osg::Vec3 v = _velocity * _current_size * scale / vl; + osg::Vec3 v = _velocity * _base_prop.y() * scale / vl; gl->TexCoord1f(0); gl->Vertex3f(xpos.x(), xpos.y(), xpos.z()); @@ -214,11 +220,36 @@ void osgParticle::Particle::render(osg::GLBeginEndAdapter* gl, const osg::Vec3& } } +void osgParticle::Particle::render(osg::RenderInfo& renderInfo, const osg::Vec3& xpos, const osg::Vec3& xrot) const +{ +#if defined(OSG_GL_MATRICES_AVAILABLE) + if (_drawable.valid()) + { + bool requiresRotation = (xrot.x()!=0.0f || xrot.y()!=0.0f || xrot.z()!=0.0f); + glColor4f(_current_color.x(), + _current_color.y(), + _current_color.z(), + _current_color.w() * _base_prop.z()); + glPushMatrix(); + glTranslatef(xpos.x(), xpos.y(), xpos.z()); + if (requiresRotation) + { + osg::Quat rotation(xrot.x(), osg::X_AXIS, xrot.y(), osg::Y_AXIS, xrot.z(), osg::Z_AXIS); + glMultMatrixd(osg::Matrixd(rotation).ptr()); + } + _drawable->draw(renderInfo); + glPopMatrix(); + } +#else + OSG_NOTICE<<"Warning: Particle::render(..) not supported for user-defined shape."<getParticle(getPreviousParticle()); const osg::Vec3& previousPosition = previousParticle->getPosition(); diff --git a/src/osgParticle/ParticleProcessor.cpp b/src/osgParticle/ParticleProcessor.cpp index 714e4dc37..96cb1d37c 100644 --- a/src/osgParticle/ParticleProcessor.cpp +++ b/src/osgParticle/ParticleProcessor.cpp @@ -15,7 +15,6 @@ osgParticle::ParticleProcessor::ParticleProcessor() : osg::Node(), _rf(RELATIVE_RF), _enabled(true), - _t0(-1), _ps(0), _first_ltw_compute(true), _need_ltw_matrix(false), @@ -36,7 +35,6 @@ osgParticle::ParticleProcessor::ParticleProcessor(const ParticleProcessor& copy, : osg::Node(copy, copyop), _rf(copy._rf), _enabled(copy._enabled), - _t0(copy._t0), _ps(static_cast(copyop(copy._ps.get()))), _first_ltw_compute(copy._first_ltw_compute), _need_ltw_matrix(copy._need_ltw_matrix), @@ -79,47 +77,38 @@ void osgParticle::ParticleProcessor::traverse(osg::NodeVisitor& nv) if ((_currentTime >= _resetTime) && (_resetTime > 0)) { _currentTime = 0; - _t0 = -1; } - // skip if we haven't initialized _t0 yet - if (_t0 != -1) + // check whether the processor is alive + bool alive = false; + if (_currentTime >= _startTime) { - - // check whether the processor is alive - bool alive = false; - if (_currentTime >= _startTime) - { - if (_endless || (_currentTime < (_startTime + _lifeTime))) - alive = true; - } - - // update current time - _currentTime += t - _t0; - - // process only if the particle system is not frozen/culled - if (alive && - _enabled && - !_ps->isFrozen() && - (_ps->getLastFrameNumber() >= (nv.getFrameStamp()->getFrameNumber() - 1) || !_ps->getFreezeOnCull())) - { - // initialize matrix flags - _need_ltw_matrix = true; - _need_wtl_matrix = true; - _current_nodevisitor = &nv; - - // do some process (unimplemented in this base class) - process(t - _t0); - } else { - //The values of _previous_wtl_matrix and _previous_ltw_matrix will be invalid - //since processing was skipped for this frame - _first_ltw_compute = true; - _first_wtl_compute = true; - } + if (_endless || (_currentTime < (_startTime + _lifeTime))) + alive = true; } - // update _t0 - _t0 = t; + // update current time + _currentTime += _ps->getDeltaTime(t); + + // process only if the particle system is not frozen/culled + if (alive && + _enabled && + !_ps->isFrozen() && + (_ps->getLastFrameNumber() >= (nv.getFrameStamp()->getFrameNumber() - 1) || !_ps->getFreezeOnCull())) + { + // initialize matrix flags + _need_ltw_matrix = true; + _need_wtl_matrix = true; + _current_nodevisitor = &nv; + + // do some process (unimplemented in this base class) + process( _ps->getDeltaTime(t) ); + } else { + //The values of _previous_wtl_matrix and _previous_ltw_matrix will be invalid + //since processing was skipped for this frame + _first_ltw_compute = true; + _first_wtl_compute = true; + } } //added- 1/17/06- bgandere@nps.edu diff --git a/src/osgParticle/ParticleSystem.cpp b/src/osgParticle/ParticleSystem.cpp index 814e475e5..2ce0ce55e 100644 --- a/src/osgParticle/ParticleSystem.cpp +++ b/src/osgParticle/ParticleSystem.cpp @@ -12,10 +12,22 @@ #include #include #include +#include +#include #include #include +#include #include +#include + +#define USE_LOCAL_SHADERS + +static double distance(const osg::Vec3& coord, const osg::Matrix& matrix) +{ + // copied from CullVisitor.cpp + return -(coord[0]*matrix(0,2)+coord[1]*matrix(1,2)+coord[2]*matrix(2,2)+matrix(3,2)); +} osgParticle::ParticleSystem::ParticleSystem() : osg::Drawable(), @@ -24,6 +36,9 @@ osgParticle::ParticleSystem::ParticleSystem() _align_X_axis(1, 0, 0), _align_Y_axis(0, 1, 0), _particleScaleReferenceFrame(WORLD_COORDINATES), + _useVertexArray(false), + _useShaders(false), + _dirty_uniforms(false), _doublepass(false), _frozen(false), _bmin(0, 0, 0), @@ -32,8 +47,13 @@ osgParticle::ParticleSystem::ParticleSystem() _bounds_computed(false), _def_ptemp(Particle()), _last_frame(0), + _dirty_dt(true), _freeze_on_cull(false), + _t0(0.0), + _dt(0.0), _detail(1), + _sortMode(NO_SORT), + _visibilityDistance(-1.0), _draw_count(0) { // we don't support display lists because particle systems @@ -48,6 +68,9 @@ osgParticle::ParticleSystem::ParticleSystem(const ParticleSystem& copy, const os _align_X_axis(copy._align_X_axis), _align_Y_axis(copy._align_Y_axis), _particleScaleReferenceFrame(copy._particleScaleReferenceFrame), + _useVertexArray(copy._useVertexArray), + _useShaders(copy._useShaders), + _dirty_uniforms(copy._dirty_uniforms), _doublepass(copy._doublepass), _frozen(copy._frozen), _bmin(copy._bmin), @@ -56,8 +79,13 @@ osgParticle::ParticleSystem::ParticleSystem(const ParticleSystem& copy, const os _bounds_computed(copy._bounds_computed), _def_ptemp(copy._def_ptemp), _last_frame(copy._last_frame), + _dirty_dt(copy._dirty_dt), _freeze_on_cull(copy._freeze_on_cull), + _t0(copy._t0), + _dt(copy._dt), _detail(copy._detail), + _sortMode(copy._sortMode), + _visibilityDistance(copy._visibilityDistance), _draw_count(0) { } @@ -66,18 +94,34 @@ osgParticle::ParticleSystem::~ParticleSystem() { } -void osgParticle::ParticleSystem::update(double dt) +void osgParticle::ParticleSystem::update(double dt, osg::NodeVisitor& nv) { // reset bounds _reset_bounds_flag = true; - + if (_useShaders) + { + // Update shader uniforms + // This slightly reduces the consumption of traversing the particle vector, because we + // don't compute tile and angle attributes that are useleff for shaders. + // At present, our lcoal shader implementation will ignore these particle props: + // _cur_tile, _s_coord, _t_coord, _prev_pos, _prev_angle and _angle + osg::StateSet* stateset = getOrCreateStateSet(); + + if (_dirty_uniforms) + { + osg::Uniform* u_vd = stateset->getUniform("visibilityDistance"); + if (u_vd) u_vd->set((float)_visibilityDistance); + _dirty_uniforms = false; + } + } + for(unsigned int i=0; i<_particles.size(); ++i) { Particle& particle = _particles[i]; if (particle.isAlive()) { - if (particle.update(dt)) + if (particle.update(dt, _useShaders)) { update_bounds(particle.getPosition(), particle.getCurrentSize()); } @@ -87,7 +131,27 @@ void osgParticle::ParticleSystem::update(double dt) } } } - + + if (_sortMode != NO_SORT) + { + // sort particles + osgUtil::CullVisitor* cv = dynamic_cast(&nv); + if (cv) + { + osg::Matrixd modelview = *(cv->getModelViewMatrix()); + float scale = (_sortMode==SORT_FRONT_TO_BACK ? -1.0f : 1.0f); + for (unsigned int i=0; i<_particles.size(); ++i) + { + Particle& particle = _particles[i]; + if (particle.isAlive()) + particle.setDepth(distance(particle.getPosition(), modelview) * scale); + else + particle.setDepth(0.0f); + } + std::sort(_particles.begin(), _particles.end()); + } + } + // force recomputing of bounding box on next frame dirtyBound(); } @@ -101,7 +165,11 @@ void osgParticle::ParticleSystem::drawImplementation(osg::RenderInfo& renderInfo // update the frame count, so other objects can detect when // this particle system is culled _last_frame = state.getFrameStamp()->getFrameNumber(); - + + // update the dirty flag of delta time, so next time a new request for delta time + // will automatically cause recomputing + _dirty_dt = true; + // get the current modelview matrix osg::Matrix modelview = state.getModelViewMatrix(); @@ -113,7 +181,10 @@ void osgParticle::ParticleSystem::drawImplementation(osg::RenderInfo& renderInfo glDepthMask(GL_FALSE); // render, first pass - single_pass_render(state, modelview); + if (_useVertexArray) + render_vertex_array(renderInfo); + else + single_pass_render(renderInfo, modelview); #if !defined(OSG_GLES1_AVAILABLE) && !defined(OSG_GLES2_AVAILABLE) && !defined(OSG_GL3_AVAILABLE) // restore depth mask settings @@ -129,7 +200,10 @@ void osgParticle::ParticleSystem::drawImplementation(osg::RenderInfo& renderInfo glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); // render the particles onto the depth buffer - single_pass_render(state, modelview); + if (_useVertexArray) + render_vertex_array(renderInfo); + else + single_pass_render(renderInfo, modelview); #if !defined(OSG_GLES1_AVAILABLE) && !defined(OSG_GLES2_AVAILABLE) && !defined(OSG_GL3_AVAILABLE) // restore color mask settings @@ -179,21 +253,109 @@ void osgParticle::ParticleSystem::setDefaultAttributes(const std::string& textur stateset->setAttributeAndModes(blend, osg::StateAttribute::ON); setStateSet(stateset); + setUseVertexArray(false); + setUseShaders(false); } -void osgParticle::ParticleSystem::single_pass_render(osg::State& state, const osg::Matrix& modelview) const +void osgParticle::ParticleSystem::setDefaultAttributesUsingShaders(const std::string& texturefile, bool emissive_particles, int texture_unit) +{ + osg::StateSet *stateset = new osg::StateSet; + stateset->setRenderingHint(osg::StateSet::TRANSPARENT_BIN); + + osg::PointSprite *sprite = new osg::PointSprite; + stateset->setTextureAttributeAndModes(texture_unit, sprite, osg::StateAttribute::ON); + + #if !defined(OSG_GLES1_AVAILABLE) && !defined(OSG_GLES2_AVAILABLE) + stateset->setMode(GL_VERTEX_PROGRAM_POINT_SIZE, osg::StateAttribute::ON); + #else + OSG_NOTICE<<"Warning: ParticleSystem::setDefaultAttributesUsingShaders(..) not fully implemented."<setImage(osgDB::readImageFile(texturefile)); + texture->setFilter(osg::Texture2D::MIN_FILTER, osg::Texture2D::LINEAR); + texture->setFilter(osg::Texture2D::MAG_FILTER, osg::Texture2D::LINEAR); + texture->setWrap(osg::Texture2D::WRAP_S, osg::Texture2D::MIRROR); + texture->setWrap(osg::Texture2D::WRAP_T, osg::Texture2D::MIRROR); + stateset->setTextureAttributeAndModes(texture_unit, texture, osg::StateAttribute::ON); + } + + osg::BlendFunc *blend = new osg::BlendFunc; + if (emissive_particles) + { + blend->setFunction(osg::BlendFunc::SRC_ALPHA, osg::BlendFunc::ONE); + } + else + { + blend->setFunction(osg::BlendFunc::SRC_ALPHA, osg::BlendFunc::ONE_MINUS_SRC_ALPHA); + } + stateset->setAttributeAndModes(blend, osg::StateAttribute::ON); + + osg::Program *program = new osg::Program; +#ifdef USE_LOCAL_SHADERS + char vertexShaderSource[] = + "uniform float visibilityDistance;\n" + "varying vec3 basic_prop;\n" + "\n" + "void main(void)\n" + "{\n" + " basic_prop = gl_MultiTexCoord0.xyz;\n" + " \n" + " vec4 ecPos = gl_ModelViewMatrix * gl_Vertex;\n" + " float ecDepth = -ecPos.z;\n" + " \n" + " if (visibilityDistance > 0.0)\n" + " {\n" + " if (ecDepth <= 0.0 || ecDepth >= visibilityDistance)\n" + " basic_prop.x = -1.0;\n" + " }\n" + " \n" + " gl_Position = ftransform();\n" + " gl_ClipVertex = ecPos;\n" + " \n" + " vec4 color = gl_Color;\n" + " color.a *= basic_prop.z;\n" + " gl_FrontColor = color;\n" + " gl_BackColor = gl_FrontColor;\n" + "}\n"; + char fragmentShaderSource[] = + "uniform sampler2D baseTexture;\n" + "varying vec3 basic_prop;\n" + "\n" + "void main(void)\n" + "{\n" + " if (basic_prop.x < 0.0) discard;\n" + " gl_FragColor = gl_Color * texture2D(baseTexture, gl_TexCoord[0].xy);\n" + "}\n"; + program->addShader(new osg::Shader(osg::Shader::VERTEX, vertexShaderSource)); + program->addShader(new osg::Shader(osg::Shader::FRAGMENT, fragmentShaderSource)); +#else + program->addShader(osg::Shader::readShaderFile(osg::Shader::VERTEX, osgDB::findDataFile("shaders/particle.vert"))); + program->addShader(osg::Shader::readShaderFile(osg::Shader::FRAGMENT, osgDB::findDataFile("shaders/particle.frag"))); +#endif + stateset->setAttributeAndModes(program, osg::StateAttribute::ON); + + stateset->addUniform(new osg::Uniform("visibilityDistance", (float)_visibilityDistance)); + stateset->addUniform(new osg::Uniform("baseTexture", texture_unit)); + setStateSet(stateset); + + setUseVertexArray(true); + setUseShaders(true); +} + + +void osgParticle::ParticleSystem::single_pass_render(osg::RenderInfo& renderInfo, const osg::Matrix& modelview) const { _draw_count = 0; if (_particles.size() <= 0) return; - osg::GLBeginEndAdapter* gl = &state.getGLBeginEndAdapter(); + osg::GLBeginEndAdapter* gl = &(renderInfo.getState()->getGLBeginEndAdapter()); float scale = sqrtf(static_cast(_detail)); - const Particle* startParticle = &_particles[0]; - startParticle->beginRender(gl); - osg::Vec3 xAxis = _align_X_axis; osg::Vec3 yAxis = _align_Y_axis; @@ -229,19 +391,55 @@ void osgParticle::ParticleSystem::single_pass_render(osg::State& state, const o yAxis *= yScale; } + bool requiresEndRender = false; + const Particle* startParticle = &_particles[0]; + if (startParticle->getShape() != Particle::USER) + { + startParticle->beginRender(gl); + requiresEndRender = true; + } + else + { + // Enable writing depth mask when drawing user-defined particles + glDepthMask(GL_TRUE); + } + for(unsigned int i=0; i<_particles.size(); i+=_detail) { const Particle* currentParticle = &_particles[i]; - if (currentParticle->isAlive()) + + bool insideDistance = true; + if (_sortMode != NO_SORT && _visibilityDistance>0.0) + insideDistance = (currentParticle->getDepth()>=0.0 && currentParticle->getDepth()<=_visibilityDistance); + + if (currentParticle->isAlive() && insideDistance) { if (currentParticle->getShape() != startParticle->getShape()) { startParticle->endRender(gl); - currentParticle->beginRender(gl); startParticle = currentParticle; + if (currentParticle->getShape() != Particle::USER) + { + currentParticle->beginRender(gl); + requiresEndRender = true; + glDepthMask(GL_FALSE); + } + else + glDepthMask(GL_TRUE); } ++_draw_count; + if (currentParticle->getShape() == Particle::USER) + { + if (requiresEndRender) + { + startParticle->endRender(gl); + requiresEndRender = false; + } + currentParticle->render(renderInfo, currentParticle->getPosition(), currentParticle->getAngle()); + continue; + } + const osg::Vec3& angle = currentParticle->getAngle(); bool requiresRotation = (angle.x()!=0.0f || angle.y()!=0.0f || angle.z()!=0.0f); if (requiresRotation) @@ -277,8 +475,40 @@ void osgParticle::ParticleSystem::single_pass_render(osg::State& state, const o } } - startParticle->endRender(gl); + if (requiresEndRender) + startParticle->endRender(gl); +} + +void osgParticle::ParticleSystem::render_vertex_array(osg::RenderInfo& renderInfo) const +{ + if (_particles.size() <= 0) return; + // Compute the pointer and offsets + Particle_vector::const_iterator itr = _particles.begin(); + float* ptr = (float*)(&(*itr)); + GLsizei stride = 0; + if (_particles.size() > 1) + { + float* ptr1 = (float*)(&(*(itr+1))); + stride = ptr1 - ptr; + } + GLsizei posOffset = (float*)(&(itr->_position)) - ptr; // Position + GLsizei colorOffset = (float*)(&(itr->_current_color)) - ptr; // Color + GLsizei velOffset = (float*)(&(itr->_velocity)) - ptr; // Velocity + GLsizei propOffset = (float*)(&(itr->_base_prop)) - ptr; // Alive, size & alpha + + // Draw particles as arrays + osg::State& state = *renderInfo.getState(); + state.lazyDisablingOfVertexAttributes(); + state.setColorPointer(4, GL_FLOAT, stride * sizeof(float), ptr + colorOffset); + state.setVertexPointer(3, GL_FLOAT, stride * sizeof(float), ptr + posOffset); + if (_useShaders) + { + state.setNormalPointer(GL_FLOAT, stride * sizeof(float), ptr + velOffset); + state.setTexCoordPointer(0, 3, GL_FLOAT, stride * sizeof(float), ptr + propOffset); + } + state.applyDisablingOfVertexAttributes(); + glDrawArrays(GL_POINTS, 0, _particles.size()); } osg::BoundingBox osgParticle::ParticleSystem::computeBound() const diff --git a/src/osgParticle/ParticleSystemUpdater.cpp b/src/osgParticle/ParticleSystemUpdater.cpp index f936e1ad1..180ca2080 100644 --- a/src/osgParticle/ParticleSystemUpdater.cpp +++ b/src/osgParticle/ParticleSystemUpdater.cpp @@ -1,18 +1,18 @@ #include #include -#include +#include using namespace osg; osgParticle::ParticleSystemUpdater::ParticleSystemUpdater() -: osg::Node(), _t0(-1), _frameNumber(0) +: osg::Geode(), _frameNumber(0) { setCullingActive(false); } osgParticle::ParticleSystemUpdater::ParticleSystemUpdater(const ParticleSystemUpdater& copy, const osg::CopyOp& copyop) -: osg::Node(copy, copyop), _t0(copy._t0), _frameNumber(0) +: osg::Geode(copy, copyop), _frameNumber(0) { ParticleSystem_Vector::const_iterator i; for (i=copy._psv.begin(); i!=copy._psv.end(); ++i) { @@ -33,22 +33,18 @@ void osgParticle::ParticleSystemUpdater::traverse(osg::NodeVisitor& nv) _frameNumber = nv.getFrameStamp()->getFrameNumber(); double t = nv.getFrameStamp()->getSimulationTime(); - if (_t0 != -1.0) + ParticleSystem_Vector::iterator i; + for (i=_psv.begin(); i!=_psv.end(); ++i) { - ParticleSystem_Vector::iterator i; - for (i=_psv.begin(); i!=_psv.end(); ++i) - { - ParticleSystem* ps = i->get(); - - ParticleSystem::ScopedWriteLock lock(*(ps->getReadWriteMutex())); + ParticleSystem* ps = i->get(); + + ParticleSystem::ScopedWriteLock lock(*(ps->getReadWriteMutex())); - if (!ps->isFrozen() && (ps->getLastFrameNumber() >= (nv.getFrameStamp()->getFrameNumber() - 1) || !ps->getFreezeOnCull())) - { - ps->update(t - _t0); - } + if (!ps->isFrozen() && (ps->getLastFrameNumber() >= (nv.getFrameStamp()->getFrameNumber() - 1) || !ps->getFreezeOnCull())) + { + ps->update(ps->getDeltaTime(t), nv); } } - _t0 = t; } } @@ -58,17 +54,32 @@ void osgParticle::ParticleSystemUpdater::traverse(osg::NodeVisitor& nv) } } - Node::traverse(nv); + Geode::traverse(nv); } osg::BoundingSphere osgParticle::ParticleSystemUpdater::computeBound() const { - return osg::BoundingSphere(); + return Geode::computeBound(); +} + +bool osgParticle::ParticleSystemUpdater::addDrawable(Drawable* drawable) +{ + ParticleSystem* ps = dynamic_cast(drawable); + if (ps) addParticleSystem(ps); + return Geode::addDrawable(drawable); +} + +bool osgParticle::ParticleSystemUpdater::removeDrawable(Drawable* drawable) +{ + ParticleSystem* ps = dynamic_cast(drawable); + if (ps) removeParticleSystem(ps); + return Geode::removeDrawable(drawable); } bool osgParticle::ParticleSystemUpdater::addParticleSystem(ParticleSystem* ps) { - _psv.push_back(ps); + unsigned int i = getParticleSystemIndex( ps ); + if( i >= _psv.size() ) _psv.push_back(ps); return true; } diff --git a/src/osgParticle/SinkOperator.cpp b/src/osgParticle/SinkOperator.cpp new file mode 100644 index 000000000..7a99350c3 --- /dev/null +++ b/src/osgParticle/SinkOperator.cpp @@ -0,0 +1,116 @@ +/* -*-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 + +#include +#include +#include + +#define SINK_EPSILON 1e-3 + +using namespace osgParticle; + +void SinkOperator::beginOperate( Program* prg ) +{ + // Don't transform domains if they are used for sinking velocities + if ( _sinkTarget==SINK_POSITION ) + DomainOperator::beginOperate(prg ); +} + +void SinkOperator::handlePoint( const Domain& domain, Particle* P, double dt ) +{ + const osg::Vec3& value = getValue(P); + kill( P, (domain.v1==value) ); +} + +void SinkOperator::handleLineSegment( const Domain& domain, Particle* P, double dt ) +{ + const osg::Vec3& value = getValue(P); + osg::Vec3 offset = value - domain.v1, normal = domain.v2 - domain.v1; + normal.normalize(); + + float diff = fabs(normal*offset - offset.length()) / domain.r1; + kill( P, (diffSINK_EPSILON ) + insideDomain = false; + else + { + float upos = offset * domain.s1; + float vpos = offset * domain.s2; + insideDomain = !(upos<0.0f || vpos<0.0f || (upos+vpos)>1.0f); + } + kill( P, insideDomain ); +} + +void SinkOperator::handleRectangle( const Domain& domain, Particle* P, double dt ) +{ + bool insideDomain = false; + const osg::Vec3& value = getValue(P); + osg::Vec3 offset = value - domain.v1; + if ( offset*domain.plane.getNormal()>SINK_EPSILON ) + insideDomain = false; + else + { + float upos = offset * domain.s1; + float vpos = offset * domain.s2; + insideDomain = !(upos<0.0f || upos>1.0f || vpos<0.0f || vpos>1.0f); + } + kill( P, insideDomain ); +} + +void SinkOperator::handlePlane( const Domain& domain, Particle* P, double dt ) +{ + const osg::Vec3& value = getValue(P); + bool insideDomain = (domain.plane.getNormal()*value>=-domain.plane[3]); + kill( P, insideDomain ); +} + +void SinkOperator::handleSphere( const Domain& domain, Particle* P, double dt ) +{ + const osg::Vec3& value = getValue(P); + float r = (value - domain.v1).length(); + kill( P, (r<=domain.r1) ); +} + +void SinkOperator::handleBox( const Domain& domain, Particle* P, double dt ) +{ + const osg::Vec3& value = getValue(P); + bool insideDomain = !( + (value.x() < domain.v1.x()) || (value.x() > domain.v2.x()) || + (value.y() < domain.v1.y()) || (value.y() > domain.v2.y()) || + (value.z() < domain.v1.z()) || (value.z() > domain.v2.z()) + ); + kill( P, insideDomain ); +} + +void SinkOperator::handleDisk( const Domain& domain, Particle* P, double dt ) +{ + bool insideDomain = false; + const osg::Vec3& value = getValue(P); + osg::Vec3 offset = value - domain.v1; + if ( offset*domain.v2>SINK_EPSILON ) + insideDomain = false; + else + { + float length = offset.length(); + insideDomain = (length<=domain.r1 && length>=domain.r2); + } + kill( P, insideDomain ); +} diff --git a/src/osgWrappers/deprecated-dotosg/osgParticle/IO_AngularDampingOperator.cpp b/src/osgWrappers/deprecated-dotosg/osgParticle/IO_AngularDampingOperator.cpp new file mode 100644 index 000000000..c916f8de2 --- /dev/null +++ b/src/osgWrappers/deprecated-dotosg/osgParticle/IO_AngularDampingOperator.cpp @@ -0,0 +1,59 @@ + +#include + +#include +#include +#include + +#include + +#include + +bool AngularDampingOperator_readLocalData(osg::Object &obj, osgDB::Input &fr); +bool AngularDampingOperator_writeLocalData(const osg::Object &obj, osgDB::Output &fw); + +REGISTER_DOTOSGWRAPPER(AngularDampingOperator_Proxy) +( + new osgParticle::AngularDampingOperator, + "AngularDampingOperator", + "Object Operator AngularDampingOperator", + AngularDampingOperator_readLocalData, + AngularDampingOperator_writeLocalData +); + +bool AngularDampingOperator_readLocalData(osg::Object &obj, osgDB::Input &fr) +{ + osgParticle::AngularDampingOperator &adp = static_cast(obj); + bool itAdvanced = false; + + osg::Vec3 a; + if (fr[0].matchWord("damping")) { + if (fr[1].getFloat(a.x()) && fr[2].getFloat(a.y()) && fr[3].getFloat(a.z())) { + adp.setDamping(a); + fr += 4; + itAdvanced = true; + } + } + + float low = 0.0f, high = FLT_MAX; + if (fr[0].matchWord("cutoff")) { + if (fr[1].getFloat(low) && fr[2].getFloat(high)) { + adp.setCutoff(low, high); + fr += 3; + itAdvanced = true; + } + } + + return itAdvanced; +} + +bool AngularDampingOperator_writeLocalData(const osg::Object &obj, osgDB::Output &fw) +{ + const osgParticle::AngularDampingOperator &adp = static_cast(obj); + osg::Vec3 a = adp.getDamping(); + fw.indent() << "damping " << a.x() << " " << a.y() << " " << a.z() << std::endl; + + float low = adp.getCutoffLow(), high = adp.getCutoffHigh(); + fw.indent() << "cutoff " << low << " " << high << std::endl; + return true; +} diff --git a/src/osgWrappers/deprecated-dotosg/osgParticle/IO_BounceOperator.cpp b/src/osgWrappers/deprecated-dotosg/osgParticle/IO_BounceOperator.cpp new file mode 100644 index 000000000..27a77780d --- /dev/null +++ b/src/osgWrappers/deprecated-dotosg/osgParticle/IO_BounceOperator.cpp @@ -0,0 +1,64 @@ + +#include + +#include +#include +#include + +#include + +#include + +bool BounceOperator_readLocalData(osg::Object &obj, osgDB::Input &fr); +bool BounceOperator_writeLocalData(const osg::Object &obj, osgDB::Output &fw); + +REGISTER_DOTOSGWRAPPER(BounceOperator_Proxy) +( + new osgParticle::BounceOperator, + "BounceOperator", + "Object Operator DomainOperator BounceOperator", + BounceOperator_readLocalData, + BounceOperator_writeLocalData +); + +bool BounceOperator_readLocalData(osg::Object &obj, osgDB::Input &fr) +{ + osgParticle::BounceOperator &bp = static_cast(obj); + bool itAdvanced = false; + + float value = 0.0f; + if (fr[0].matchWord("friction")) { + if (fr[1].getFloat(value)) { + bp.setFriction(value); + fr += 2; + itAdvanced = true; + } + } + + if (fr[0].matchWord("resilience")) { + if (fr[1].getFloat(value)) { + bp.setResilience(value); + fr += 2; + itAdvanced = true; + } + } + + if (fr[0].matchWord("cutoff")) { + if (fr[1].getFloat(value)) { + bp.setCutoff(value); + fr += 2; + itAdvanced = true; + } + } + + return itAdvanced; +} + +bool BounceOperator_writeLocalData(const osg::Object &obj, osgDB::Output &fw) +{ + const osgParticle::BounceOperator &bp = static_cast(obj); + fw.indent() << "friction " << bp.getFriction() << std::endl; + fw.indent() << "resilience " << bp.getResilience() << std::endl; + fw.indent() << "cutoff " << bp.getCutoff() << std::endl; + return true; +} diff --git a/src/osgWrappers/deprecated-dotosg/osgParticle/IO_DampingOperator.cpp b/src/osgWrappers/deprecated-dotosg/osgParticle/IO_DampingOperator.cpp new file mode 100644 index 000000000..e6f290744 --- /dev/null +++ b/src/osgWrappers/deprecated-dotosg/osgParticle/IO_DampingOperator.cpp @@ -0,0 +1,59 @@ + +#include + +#include +#include +#include + +#include + +#include + +bool DampingOperator_readLocalData(osg::Object &obj, osgDB::Input &fr); +bool DampingOperator_writeLocalData(const osg::Object &obj, osgDB::Output &fw); + +REGISTER_DOTOSGWRAPPER(DampingOperator_Proxy) +( + new osgParticle::DampingOperator, + "DampingOperator", + "Object Operator DampingOperator", + DampingOperator_readLocalData, + DampingOperator_writeLocalData +); + +bool DampingOperator_readLocalData(osg::Object &obj, osgDB::Input &fr) +{ + osgParticle::DampingOperator &dp = static_cast(obj); + bool itAdvanced = false; + + osg::Vec3 a; + if (fr[0].matchWord("damping")) { + if (fr[1].getFloat(a.x()) && fr[2].getFloat(a.y()) && fr[3].getFloat(a.z())) { + dp.setDamping(a); + fr += 4; + itAdvanced = true; + } + } + + float low = 0.0f, high = FLT_MAX; + if (fr[0].matchWord("cutoff")) { + if (fr[1].getFloat(low) && fr[2].getFloat(high)) { + dp.setCutoff(low, high); + fr += 3; + itAdvanced = true; + } + } + + return itAdvanced; +} + +bool DampingOperator_writeLocalData(const osg::Object &obj, osgDB::Output &fw) +{ + const osgParticle::DampingOperator &dp = static_cast(obj); + osg::Vec3 a = dp.getDamping(); + fw.indent() << "damping " << a.x() << " " << a.y() << " " << a.z() << std::endl; + + float low = dp.getCutoffLow(), high = dp.getCutoffHigh(); + fw.indent() << "cutoff " << low << " " << high << std::endl; + return true; +} diff --git a/src/osgWrappers/deprecated-dotosg/osgParticle/IO_DomainOperator.cpp b/src/osgWrappers/deprecated-dotosg/osgParticle/IO_DomainOperator.cpp new file mode 100644 index 000000000..451f77c8f --- /dev/null +++ b/src/osgWrappers/deprecated-dotosg/osgParticle/IO_DomainOperator.cpp @@ -0,0 +1,146 @@ + +#include + +#include +#include +#include + +#include +#include +#include + +bool DomainOperator_readLocalData(osg::Object &obj, osgDB::Input &fr); +bool DomainOperator_writeLocalData(const osg::Object &obj, osgDB::Output &fw); + +REGISTER_DOTOSGWRAPPER(DomainOperator_Proxy) +( + new osgParticle::DomainOperator, + "DomainOperator", + "Object Operator DomainOperator", + DomainOperator_readLocalData, + DomainOperator_writeLocalData +); + +bool DomainOperator_readLocalData(osg::Object &obj, osgDB::Input &fr) +{ + osgParticle::DomainOperator &dp = static_cast(obj); + bool itAdvanced = false; + + std::string typeName; + while (fr.matchSequence("domain %s {")) + { + if (fr[1].getStr()) typeName = fr[1].getStr(); + fr += 3; + + osgParticle::DomainOperator::Domain::Type type = osgParticle::DomainOperator::Domain::UNDEFINED_DOMAIN; + if (typeName == "point") type = osgParticle::DomainOperator::Domain::POINT_DOMAIN; + else if (typeName == "line") type = osgParticle::DomainOperator::Domain::LINE_DOMAIN; + else if (typeName == "triangle") type = osgParticle::DomainOperator::Domain::TRI_DOMAIN; + else if (typeName == "rectangle") type = osgParticle::DomainOperator::Domain::RECT_DOMAIN; + else if (typeName == "plane") type = osgParticle::DomainOperator::Domain::PLANE_DOMAIN; + else if (typeName == "sphere") type = osgParticle::DomainOperator::Domain::SPHERE_DOMAIN; + else if (typeName == "box") type = osgParticle::DomainOperator::Domain::BOX_DOMAIN; + else if (typeName == "disk") type = osgParticle::DomainOperator::Domain::DISK_DOMAIN; + + osgParticle::DomainOperator::Domain domain(type); + if (fr[0].matchWord("plane")) { + if (fr[1].getFloat(domain.plane[0]) && fr[2].getFloat(domain.plane[1]) && + fr[3].getFloat(domain.plane[2]) && fr[4].getFloat(domain.plane[3])) + { + fr += 5; + } + } + + if (fr[0].matchWord("vertices1")) { + if (fr[1].getFloat(domain.v1[0]) && fr[2].getFloat(domain.v1[1]) && fr[3].getFloat(domain.v1[2])) + { + fr += 4; + } + } + + if (fr[0].matchWord("vertices2")) { + if (fr[1].getFloat(domain.v2[0]) && fr[2].getFloat(domain.v2[1]) && fr[3].getFloat(domain.v2[2])) + { + fr += 4; + } + } + + if (fr[0].matchWord("vertices3")) { + if (fr[1].getFloat(domain.v3[0]) && fr[2].getFloat(domain.v3[1]) && fr[3].getFloat(domain.v3[2])) + { + fr += 4; + } + } + + if (fr[0].matchWord("basis1")) { + if (fr[1].getFloat(domain.s1[0]) && fr[2].getFloat(domain.s1[1]) && fr[3].getFloat(domain.s1[2])) + { + fr += 4; + } + } + + if (fr[0].matchWord("basis2")) { + if (fr[1].getFloat(domain.s2[0]) && fr[2].getFloat(domain.s2[1]) && fr[3].getFloat(domain.s2[2])) + { + fr += 4; + } + } + + if (fr[0].matchWord("factors")) { + if (fr[1].getFloat(domain.r1) && fr[2].getFloat(domain.r2)) + { + fr += 3; + } + } + dp.addDomain(domain); + + ++fr; + itAdvanced = true; + } + return itAdvanced; +} + +bool DomainOperator_writeLocalData(const osg::Object &obj, osgDB::Output &fw) +{ + const osgParticle::DomainOperator &dp = static_cast(obj); + + for(unsigned int i=0;i + +#include +#include +#include + +#include + +#include + +bool ExplosionOperator_readLocalData(osg::Object &obj, osgDB::Input &fr); +bool ExplosionOperator_writeLocalData(const osg::Object &obj, osgDB::Output &fw); + +REGISTER_DOTOSGWRAPPER(ExplosionOperator_Proxy) +( + new osgParticle::ExplosionOperator, + "ExplosionOperator", + "Object Operator ExplosionOperator", + ExplosionOperator_readLocalData, + ExplosionOperator_writeLocalData +); + +bool ExplosionOperator_readLocalData(osg::Object &obj, osgDB::Input &fr) +{ + osgParticle::ExplosionOperator &ep = static_cast(obj); + bool itAdvanced = false; + + osg::Vec3 a; + if (fr[0].matchWord("center")) { + if (fr[1].getFloat(a.x()) && fr[2].getFloat(a.y()) && fr[3].getFloat(a.z())) { + ep.setCenter(a); + fr += 4; + itAdvanced = true; + } + } + + float value = 0.0f; + if (fr[0].matchWord("radius")) { + if (fr[1].getFloat(value)) { + ep.setRadius(value); + fr += 2; + itAdvanced = true; + } + } + + if (fr[0].matchWord("magnitude")) { + if (fr[1].getFloat(value)) { + ep.setMagnitude(value); + fr += 2; + itAdvanced = true; + } + } + + if (fr[0].matchWord("epsilon")) { + if (fr[1].getFloat(value)) { + ep.setEpsilon(value); + fr += 2; + itAdvanced = true; + } + } + + if (fr[0].matchWord("sigma")) { + if (fr[1].getFloat(value)) { + ep.setSigma(value); + fr += 2; + itAdvanced = true; + } + } + + return itAdvanced; +} + +bool ExplosionOperator_writeLocalData(const osg::Object &obj, osgDB::Output &fw) +{ + const osgParticle::ExplosionOperator &ep = static_cast(obj); + osg::Vec3 a = ep.getCenter(); + fw.indent() << "center " << a.x() << " " << a.y() << " " << a.z() << std::endl; + + fw.indent() << "radius " << ep.getRadius() << std::endl; + fw.indent() << "magnitude " << ep.getMagnitude() << std::endl; + fw.indent() << "epsilon " << ep.getEpsilon() << std::endl; + fw.indent() << "sigma " << ep.getSigma() << std::endl; + return true; +} diff --git a/src/osgWrappers/deprecated-dotosg/osgParticle/IO_OrbitOperator.cpp b/src/osgWrappers/deprecated-dotosg/osgParticle/IO_OrbitOperator.cpp new file mode 100644 index 000000000..a8af27402 --- /dev/null +++ b/src/osgWrappers/deprecated-dotosg/osgParticle/IO_OrbitOperator.cpp @@ -0,0 +1,76 @@ + +#include + +#include +#include +#include + +#include + +#include + +bool OrbitOperator_readLocalData(osg::Object &obj, osgDB::Input &fr); +bool OrbitOperator_writeLocalData(const osg::Object &obj, osgDB::Output &fw); + +REGISTER_DOTOSGWRAPPER(OrbitOperator_Proxy) +( + new osgParticle::OrbitOperator, + "OrbitOperator", + "Object Operator OrbitOperator", + OrbitOperator_readLocalData, + OrbitOperator_writeLocalData +); + +bool OrbitOperator_readLocalData(osg::Object &obj, osgDB::Input &fr) +{ + osgParticle::OrbitOperator &op = static_cast(obj); + bool itAdvanced = false; + + osg::Vec3 a; + if (fr[0].matchWord("center")) { + if (fr[1].getFloat(a.x()) && fr[2].getFloat(a.y()) && fr[3].getFloat(a.z())) { + op.setCenter(a); + fr += 4; + itAdvanced = true; + } + } + + float value = 0.0f; + if (fr[0].matchWord("magnitude")) { + if (fr[1].getFloat(value)) { + op.setMagnitude(value); + fr += 2; + itAdvanced = true; + } + } + + if (fr[0].matchWord("epsilon")) { + if (fr[1].getFloat(value)) { + op.setEpsilon(value); + fr += 2; + itAdvanced = true; + } + } + + if (fr[0].matchWord("maxRadius")) { + if (fr[1].getFloat(value)) { + op.setMaxRadius(value); + fr += 2; + itAdvanced = true; + } + } + + return itAdvanced; +} + +bool OrbitOperator_writeLocalData(const osg::Object &obj, osgDB::Output &fw) +{ + const osgParticle::OrbitOperator &op = static_cast(obj); + osg::Vec3 a = op.getCenter(); + fw.indent() << "center " << a.x() << " " << a.y() << " " << a.z() << std::endl; + + fw.indent() << "magnitude " << op.getMagnitude() << std::endl; + fw.indent() << "epsilon " << op.getEpsilon() << std::endl; + fw.indent() << "maxRadius " << op.getMaxRadius() << std::endl; + return true; +} diff --git a/src/osgWrappers/deprecated-dotosg/osgParticle/IO_Particle.cpp b/src/osgWrappers/deprecated-dotosg/osgParticle/IO_Particle.cpp index b6b9e4590..204725f4f 100644 --- a/src/osgWrappers/deprecated-dotosg/osgParticle/IO_Particle.cpp +++ b/src/osgWrappers/deprecated-dotosg/osgParticle/IO_Particle.cpp @@ -34,6 +34,8 @@ bool read_particle(osgDB::Input &fr, osgParticle::Particle &P) P.setShape(osgParticle::Particle::QUAD_TRIANGLESTRIP); } else if (std::string(ptstr) == "LINE") { P.setShape(osgParticle::Particle::LINE); + } else if (std::string(ptstr) == "USER") { + P.setShape(osgParticle::Particle::USER); } else { osg::notify(osg::WARN) << "Particle reader warning: invalid shape: " << ptstr << std::endl; } @@ -163,6 +165,15 @@ bool read_particle(osgDB::Input &fr, osgParticle::Particle &P) } ++fr; } + if (fr[0].matchWord("drawable") && fr[1].matchString("{")) { + fr += 2; + itAdvanced = true; + osg::Drawable *drawable = dynamic_cast(fr.readObject()); + if (drawable) { + P.setDrawable(drawable); + } + ++fr; + } } return true; } @@ -181,8 +192,9 @@ void write_particle(const osgParticle::Particle &P, osgDB::Output &fw) case osgParticle::Particle::HEXAGON: fw << "HEXAGON" << std::endl; break; case osgParticle::Particle::QUAD_TRIANGLESTRIP: fw << "QUAD_TRIANGLESTRIP" << std::endl; break; case osgParticle::Particle::QUAD: fw << "QUAD" << std::endl; break; - case osgParticle::Particle::LINE: - default: fw << "LINE" << std::endl; break; + case osgParticle::Particle::LINE: fw << "LINE" << std::endl; break; + case osgParticle::Particle::USER: + default: fw << "USER" << std::endl; break; } fw.indent() << "lifeTime " << P.getLifeTime() << std::endl; @@ -237,6 +249,12 @@ void write_particle(const osgParticle::Particle &P, osgDB::Output &fw) fw.writeObject(*P.getColorInterpolator()); fw.moveOut(); fw.indent() << "}" << std::endl; + + fw.indent() << "drawable {" << std::endl; + fw.moveIn(); + fw.writeObject(*P.getDrawable()); + fw.moveOut(); + fw.indent() << "}" << std::endl; fw.moveOut(); fw.indent() << "}" << std::endl; diff --git a/src/osgWrappers/deprecated-dotosg/osgParticle/IO_ParticleSystem.cpp b/src/osgWrappers/deprecated-dotosg/osgParticle/IO_ParticleSystem.cpp index 7867c3e12..49d1b6a4e 100644 --- a/src/osgWrappers/deprecated-dotosg/osgParticle/IO_ParticleSystem.cpp +++ b/src/osgWrappers/deprecated-dotosg/osgParticle/IO_ParticleSystem.cpp @@ -74,6 +74,30 @@ bool ParticleSystem_readLocalData(osg::Object &obj, osgDB::Input &fr) } } + if (fr[0].matchWord("useVertexArray")) { + if (fr[1].matchWord("TRUE")) { + myobj.setUseVertexArray(true); + fr += 2; + itAdvanced = true; + } else if (fr[1].matchWord("FALSE")) { + myobj.setUseVertexArray(false); + fr += 2; + itAdvanced = true; + } + } + + if (fr[0].matchWord("useShaders")) { + if (fr[1].matchWord("TRUE")) { + myobj.setUseShaders(true); + fr += 2; + itAdvanced = true; + } else if (fr[1].matchWord("FALSE")) { + myobj.setUseShaders(false); + fr += 2; + itAdvanced = true; + } + } + if (fr[0].matchWord("doublePassRendering")) { if (fr[1].matchWord("TRUE")) { myobj.setDoublePassRendering(true); @@ -124,6 +148,33 @@ bool ParticleSystem_readLocalData(osg::Object &obj, osgDB::Input &fr) } } + if (fr[0].matchWord("sortMode")) { + if (fr[1].matchWord("NO_SORT")) { + myobj.setSortMode(osgParticle::ParticleSystem::NO_SORT); + fr += 2; + itAdvanced = true; + } + if (fr[1].matchWord("SORT_FRONT_TO_BACK")) { + myobj.setSortMode(osgParticle::ParticleSystem::SORT_FRONT_TO_BACK); + fr += 2; + itAdvanced = true; + } + if (fr[1].matchWord("SORT_BACK_TO_FRONT")) { + myobj.setSortMode(osgParticle::ParticleSystem::SORT_BACK_TO_FRONT); + fr += 2; + itAdvanced = true; + } + } + + if (fr[0].matchWord("visibilityDistance")) { + double distance; + if (fr[1].getFloat(distance)) { + myobj.setVisibilityDistance(distance); + fr += 2; + itAdvanced = true; + } + } + if (fr[0].matchWord("particleTemplate")) { ++fr; itAdvanced = true; @@ -167,6 +218,18 @@ bool ParticleSystem_writeLocalData(const osg::Object &obj, osgDB::Output &fw) v = myobj.getAlignVectorY(); fw.indent() << "alignVectorY " << v.x() << " " << v.y() << " " << v.z() << std::endl; + fw.indent() << "useVertexArray "; + if (myobj.getUseVertexArray()) + fw << "TRUE" << std::endl; + else + fw << "FALSE" << std::endl; + + fw.indent() << "useShaders "; + if (myobj.getUseShaders()) + fw << "TRUE" << std::endl; + else + fw << "FALSE" << std::endl; + fw.indent() << "doublePassRendering "; if (myobj.getDoublePassRendering()) fw << "TRUE" << std::endl; @@ -190,8 +253,23 @@ bool ParticleSystem_writeLocalData(const osg::Object &obj, osgDB::Output &fw) fw << bbox.xMin() << " " << bbox.yMin() << " " << bbox.zMin() << " "; fw << bbox.xMax() << " " << bbox.yMax() << " " << bbox.zMax() << std::endl; + fw.indent() << "sortMode "; + switch (myobj.getSortMode()) { + default: + case osgParticle::ParticleSystem::NO_SORT: + fw << "NO_SORT" << std::endl; + break; + case osgParticle::ParticleSystem::SORT_FRONT_TO_BACK: + fw << "SORT_FRONT_TO_BACK" << std::endl; + break; + case osgParticle::ParticleSystem::SORT_BACK_TO_FRONT: + fw << "SORT_BACK_TO_FRONT" << std::endl; + break; + } + + fw.indent() << "visibilityDistance " << myobj.getVisibilityDistance() << std::endl; + fw.indent() << "particleTemplate "; write_particle(myobj.getDefaultParticleTemplate(), fw); - return true; } diff --git a/src/osgWrappers/deprecated-dotosg/osgParticle/IO_ParticleSystemUpdater.cpp b/src/osgWrappers/deprecated-dotosg/osgParticle/IO_ParticleSystemUpdater.cpp index 71f7fdf35..9497c5051 100644 --- a/src/osgWrappers/deprecated-dotosg/osgParticle/IO_ParticleSystemUpdater.cpp +++ b/src/osgWrappers/deprecated-dotosg/osgParticle/IO_ParticleSystemUpdater.cpp @@ -13,7 +13,7 @@ REGISTER_DOTOSGWRAPPER(PSU_Proxy) ( new osgParticle::ParticleSystemUpdater, "ParticleSystemUpdater", - "Object Node ParticleSystemUpdater", + "Object Node Geode ParticleSystemUpdater", PSU_readLocalData, PSU_writeLocalData ); diff --git a/src/osgWrappers/deprecated-dotosg/osgParticle/IO_SinkOperator.cpp b/src/osgWrappers/deprecated-dotosg/osgParticle/IO_SinkOperator.cpp new file mode 100644 index 000000000..b0bae2f4a --- /dev/null +++ b/src/osgWrappers/deprecated-dotosg/osgParticle/IO_SinkOperator.cpp @@ -0,0 +1,91 @@ + +#include + +#include +#include +#include + +#include + +#include + +bool SinkOperator_readLocalData(osg::Object &obj, osgDB::Input &fr); +bool SinkOperator_writeLocalData(const osg::Object &obj, osgDB::Output &fw); + +REGISTER_DOTOSGWRAPPER(SinkOperator_Proxy) +( + new osgParticle::SinkOperator, + "SinkOperator", + "Object Operator DomainOperator SinkOperator", + SinkOperator_readLocalData, + SinkOperator_writeLocalData +); + +bool SinkOperator_readLocalData(osg::Object &obj, osgDB::Input &fr) +{ + osgParticle::SinkOperator &sp = static_cast(obj); + bool itAdvanced = false; + + if (fr[0].matchWord("sinkTarget")) { + const char *ptstr = fr[1].getStr(); + if (ptstr) { + std::string str(ptstr); + if (str == "position") + sp.setSinkTarget(osgParticle::SinkOperator::SINK_POSITION); + else if (str == "velocity") + sp.setSinkTarget(osgParticle::SinkOperator::SINK_VELOCITY); + else if (str == "angular_velocity") + sp.setSinkTarget(osgParticle::SinkOperator::SINK_ANGULAR_VELOCITY); + + fr += 2; + itAdvanced = true; + } + } + + if (fr[0].matchWord("sinkStrategy")) { + const char *ptstr = fr[1].getStr(); + if (ptstr) { + std::string str(ptstr); + if (str == "inside") + sp.setSinkStrategy(osgParticle::SinkOperator::SINK_INSIDE); + else if (str == "outside") + sp.setSinkStrategy(osgParticle::SinkOperator::SINK_OUTSIDE); + + fr += 2; + itAdvanced = true; + } + } + + return itAdvanced; +} + +bool SinkOperator_writeLocalData(const osg::Object &obj, osgDB::Output &fw) +{ + const osgParticle::SinkOperator &sp = static_cast(obj); + + fw.indent() << "sinkTarget "; + switch (sp.getSinkTarget()) + { + case osgParticle::SinkOperator::SINK_POSITION: + fw << "position" << std::endl; break; + case osgParticle::SinkOperator::SINK_VELOCITY: + fw << "velocity" << std::endl; break; + case osgParticle::SinkOperator::SINK_ANGULAR_VELOCITY: + fw << "angular_velocity" << std::endl; break; + default: + fw << "undefined" << std::endl; break; + } + + fw.indent() << "sinkStrategy "; + switch (sp.getSinkStrategy()) + { + case osgParticle::SinkOperator::SINK_INSIDE: + fw << "inside" << std::endl; break; + case osgParticle::SinkOperator::SINK_OUTSIDE: + fw << "outside" << std::endl; break; + default: + fw << "undefined" << std::endl; break; + } + + return true; +} diff --git a/src/osgWrappers/serializers/osgParticle/AngularDampingOperator.cpp b/src/osgWrappers/serializers/osgParticle/AngularDampingOperator.cpp new file mode 100644 index 000000000..56d009d8c --- /dev/null +++ b/src/osgWrappers/serializers/osgParticle/AngularDampingOperator.cpp @@ -0,0 +1,14 @@ +#include +#include +#include +#include + +REGISTER_OBJECT_WRAPPER( osgParticleAngularDampingOperator, + new osgParticle::AngularDampingOperator, + osgParticle::AngularDampingOperator, + "osg::Object osgParticle::Operator osgParticle::AngularDampingOperator" ) +{ + ADD_VEC3_SERIALIZER( Damping, osg::Vec3() ); // _damping + ADD_FLOAT_SERIALIZER( CutoffLow, 0.0f ); // _cutoffLow + ADD_FLOAT_SERIALIZER( CutoffHigh, FLT_MAX ); // _cutoffHigh +} diff --git a/src/osgWrappers/serializers/osgParticle/BounceOperator.cpp b/src/osgWrappers/serializers/osgParticle/BounceOperator.cpp new file mode 100644 index 000000000..a041a152f --- /dev/null +++ b/src/osgWrappers/serializers/osgParticle/BounceOperator.cpp @@ -0,0 +1,14 @@ +#include +#include +#include +#include + +REGISTER_OBJECT_WRAPPER( osgParticleBounceOperator, + new osgParticle::BounceOperator, + osgParticle::BounceOperator, + "osg::Object osgParticle::Operator osgParticle::DomainOperator osgParticle::BounceOperator" ) +{ + ADD_FLOAT_SERIALIZER( Friction, 1.0f ); // _friction + ADD_FLOAT_SERIALIZER( Resilience, 0.0f ); // _resilience + ADD_FLOAT_SERIALIZER( Cutoff, 0.0f ); // _cutoff +} diff --git a/src/osgWrappers/serializers/osgParticle/DampingOperator.cpp b/src/osgWrappers/serializers/osgParticle/DampingOperator.cpp new file mode 100644 index 000000000..ae1da11e2 --- /dev/null +++ b/src/osgWrappers/serializers/osgParticle/DampingOperator.cpp @@ -0,0 +1,14 @@ +#include +#include +#include +#include + +REGISTER_OBJECT_WRAPPER( osgParticleDampingOperator, + new osgParticle::DampingOperator, + osgParticle::DampingOperator, + "osg::Object osgParticle::Operator osgParticle::DampingOperator" ) +{ + ADD_VEC3_SERIALIZER( Damping, osg::Vec3() ); // _damping + ADD_FLOAT_SERIALIZER( CutoffLow, 0.0f ); // _cutoffLow + ADD_FLOAT_SERIALIZER( CutoffHigh, FLT_MAX ); // _cutoffHigh +} diff --git a/src/osgWrappers/serializers/osgParticle/DomainOperator.cpp b/src/osgWrappers/serializers/osgParticle/DomainOperator.cpp new file mode 100644 index 000000000..ec2bba7d6 --- /dev/null +++ b/src/osgWrappers/serializers/osgParticle/DomainOperator.cpp @@ -0,0 +1,94 @@ +#include +#include +#include +#include + +static bool checkDomains( const osgParticle::DomainOperator& dp ) +{ + return dp.getNumDomains()>0; +} + +static bool readDomains( osgDB::InputStream& is, osgParticle::DomainOperator& dp ) +{ + osgParticle::DomainOperator::Domain::Type type = osgParticle::DomainOperator::Domain::UNDEFINED_DOMAIN; + unsigned int size = 0; is >> size >> osgDB::BEGIN_BRACKET; + for ( unsigned int i=0; i> osgDB::PROPERTY("Domain") >> typeName >> osgDB::BEGIN_BRACKET; + if (typeName=="POINT") type = osgParticle::DomainOperator::Domain::POINT_DOMAIN; + else if (typeName=="LINE") type = osgParticle::DomainOperator::Domain::LINE_DOMAIN; + else if (typeName=="TRIANGLE") type = osgParticle::DomainOperator::Domain::TRI_DOMAIN; + else if (typeName=="RECTANGLE") type = osgParticle::DomainOperator::Domain::RECT_DOMAIN; + else if (typeName=="PLANE") type = osgParticle::DomainOperator::Domain::PLANE_DOMAIN; + else if (typeName=="SPHERE") type = osgParticle::DomainOperator::Domain::SPHERE_DOMAIN; + else if (typeName=="BOX") type = osgParticle::DomainOperator::Domain::BOX_DOMAIN; + else if (typeName=="DISK") type = osgParticle::DomainOperator::Domain::DISK_DOMAIN; + + osgParticle::DomainOperator::Domain domain(type); + is >> osgDB::PROPERTY("Plane") >> domain.plane; + is >> osgDB::PROPERTY("Vertices1") >> domain.v1; + is >> osgDB::PROPERTY("Vertices2") >> domain.v2; + is >> osgDB::PROPERTY("Vertices3") >> domain.v3; + is >> osgDB::PROPERTY("Basis1") >> domain.s1; + is >> osgDB::PROPERTY("Basis2") >> domain.s2; + is >> osgDB::PROPERTY("Factors") >> domain.r1 >> domain.r2; + dp.addDomain(domain); + + is >> osgDB::END_BRACKET; + } + is >> osgDB::END_BRACKET; + return true; +} + +static bool writeDomains( osgDB::OutputStream& os, const osgParticle::DomainOperator& dp ) +{ + unsigned int size = dp.getNumDomains(); + os << size << osgDB::BEGIN_BRACKET << std::endl; + for ( unsigned int i=0; i +#include +#include +#include + +REGISTER_OBJECT_WRAPPER( osgParticleExplosionOperator, + new osgParticle::ExplosionOperator, + osgParticle::ExplosionOperator, + "osg::Object osgParticle::Operator osgParticle::ExplosionOperator" ) +{ + ADD_VEC3_SERIALIZER( Center, osg::Vec3() ); // _center + ADD_FLOAT_SERIALIZER( Radius, 1.0f ); // _radius + ADD_FLOAT_SERIALIZER( Magnitude, 1.0f ); // _magnitude + ADD_FLOAT_SERIALIZER( Epsilon, 1e-3 ); // _epsilon + ADD_FLOAT_SERIALIZER( Sigma, 1.0f ); // _sigma +} diff --git a/src/osgWrappers/serializers/osgParticle/OrbitOperator.cpp b/src/osgWrappers/serializers/osgParticle/OrbitOperator.cpp new file mode 100644 index 000000000..ed6119e37 --- /dev/null +++ b/src/osgWrappers/serializers/osgParticle/OrbitOperator.cpp @@ -0,0 +1,15 @@ +#include +#include +#include +#include + +REGISTER_OBJECT_WRAPPER( osgParticleOrbitOperator, + new osgParticle::OrbitOperator, + osgParticle::OrbitOperator, + "osg::Object osgParticle::Operator osgParticle::OrbitOperator" ) +{ + ADD_VEC3_SERIALIZER( Center, osg::Vec3() ); // _center + ADD_FLOAT_SERIALIZER( Magnitude, 1.0f ); // _magnitude + ADD_FLOAT_SERIALIZER( Epsilon, 1e-3 ); // _epsilon + ADD_FLOAT_SERIALIZER( MaxRadius, FLT_MAX ); // _maxRadius +} diff --git a/src/osgWrappers/serializers/osgParticle/Particle.cpp b/src/osgWrappers/serializers/osgParticle/Particle.cpp index e23cb5319..cefc780e1 100644 --- a/src/osgWrappers/serializers/osgParticle/Particle.cpp +++ b/src/osgWrappers/serializers/osgParticle/Particle.cpp @@ -9,6 +9,7 @@ BEGIN_USER_TABLE( Shape, osgParticle::Particle ); ADD_USER_VALUE( QUAD_TRIANGLESTRIP ); ADD_USER_VALUE( HEXAGON ); ADD_USER_VALUE( LINE ); + ADD_USER_VALUE( USER ); END_USER_TABLE() USER_READ_FUNC( Shape, readShapeValue ) @@ -68,6 +69,14 @@ bool readParticle( osgDB::InputStream& is, osgParticle::Particle& p ) p.setAngularVelocity( angleV ); p.setTextureTile( s, t, num ); + bool hasObject = false; is >> osgDB::PROPERTY("Drawable") >> hasObject; + if ( hasObject ) + { + is >> osgDB::BEGIN_BRACKET; + p.setDrawable( dynamic_cast(is.readObject()) ); + is >> osgDB::END_BRACKET; + } + is >> osgDB::END_BRACKET; return true; } @@ -102,6 +111,15 @@ bool writeParticle( osgDB::OutputStream& os, const osgParticle::Particle& p ) os << osgDB::PROPERTY("AngularVelocity") << osg::Vec3d(p.getAngularVelocity()) << std::endl; os << osgDB::PROPERTY("TextureTile") << p.getTileS() << p.getTileT() << p.getNumTiles() << std::endl; + os << osgDB::PROPERTY("Drawable") << (p.getDrawable()!=NULL); + if ( p.getDrawable()!=NULL ) + { + os << osgDB::BEGIN_BRACKET << std::endl; + os.writeObject( p.getDrawable() ); + os << osgDB::END_BRACKET; + } + os << std::endl; + os << osgDB::END_BRACKET << std::endl; return true; } diff --git a/src/osgWrappers/serializers/osgParticle/ParticleSystem.cpp b/src/osgWrappers/serializers/osgParticle/ParticleSystem.cpp index 6560b8bb4..2072dfcc9 100644 --- a/src/osgWrappers/serializers/osgParticle/ParticleSystem.cpp +++ b/src/osgWrappers/serializers/osgParticle/ParticleSystem.cpp @@ -75,8 +75,18 @@ REGISTER_OBJECT_WRAPPER( osgParticleParticleSystem, ADD_ENUM_VALUE( WORLD_COORDINATES ); END_ENUM_SERIALIZER(); // _particleScaleReferenceFrame + ADD_BOOL_SERIALIZER( UseVertexArray, false ); // _useVertexArray + ADD_BOOL_SERIALIZER( UseShaders, false ); // _useShaders ADD_BOOL_SERIALIZER( DoublePassRendering, false ); // _doublepass ADD_BOOL_SERIALIZER( Frozen, false ); // _frozen ADD_USER_SERIALIZER( DefaultParticleTemplate ); // _def_ptemp ADD_BOOL_SERIALIZER( FreezeOnCull, false ); // _freeze_on_cull + + BEGIN_ENUM_SERIALIZER( SortMode, NO_SORT ); + ADD_ENUM_VALUE( NO_SORT ); + ADD_ENUM_VALUE( SORT_FRONT_TO_BACK ); + ADD_ENUM_VALUE( SORT_BACK_TO_FRONT ); + END_ENUM_SERIALIZER(); // _sortMode + + ADD_DOUBLE_SERIALIZER( VisibilityDistance, -1.0 ); // _visibilityDistance } diff --git a/src/osgWrappers/serializers/osgParticle/ParticleSystemUpdater.cpp b/src/osgWrappers/serializers/osgParticle/ParticleSystemUpdater.cpp index 14c42295b..c00400339 100644 --- a/src/osgWrappers/serializers/osgParticle/ParticleSystemUpdater.cpp +++ b/src/osgWrappers/serializers/osgParticle/ParticleSystemUpdater.cpp @@ -35,7 +35,7 @@ static bool writeParticleSystems( osgDB::OutputStream& os, const osgParticle::Pa REGISTER_OBJECT_WRAPPER( osgParticleParticleSystemUpdater, new osgParticle::ParticleSystemUpdater, osgParticle::ParticleSystemUpdater, - "osg::Object osg::Node osgParticle::ParticleSystemUpdater" ) + "osg::Object osg::Node osg::Geode osgParticle::ParticleSystemUpdater" ) { ADD_USER_SERIALIZER( ParticleSystems ); // _psv } diff --git a/src/osgWrappers/serializers/osgParticle/SinkOperator.cpp b/src/osgWrappers/serializers/osgParticle/SinkOperator.cpp new file mode 100644 index 000000000..a751c6654 --- /dev/null +++ b/src/osgWrappers/serializers/osgParticle/SinkOperator.cpp @@ -0,0 +1,21 @@ +#include +#include +#include +#include + +REGISTER_OBJECT_WRAPPER( osgParticleSinkOperator, + new osgParticle::SinkOperator, + osgParticle::SinkOperator, + "osg::Object osgParticle::Operator osgParticle::DomainOperator osgParticle::SinkOperator" ) +{ + BEGIN_ENUM_SERIALIZER( SinkTarget, SINK_POSITION ); + ADD_ENUM_VALUE( SINK_POSITION ); + ADD_ENUM_VALUE( SINK_VELOCITY ); + ADD_ENUM_VALUE( SINK_ANGULAR_VELOCITY ); + END_ENUM_SERIALIZER(); // _sinkTarget + + BEGIN_ENUM_SERIALIZER( SinkStrategy, SINK_INSIDE ); + ADD_ENUM_VALUE( SINK_INSIDE ); + ADD_ENUM_VALUE( SINK_OUTSIDE ); + END_ENUM_SERIALIZER(); // _sinkStrategy +}