230 lines
8.0 KiB
C++
230 lines
8.0 KiB
C++
/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 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.
|
|
*/
|
|
|
|
#include <osg/Notify>
|
|
#include <osg/CullingSet>
|
|
#include <osg/io_utils>
|
|
#include <osgParticle/ConnectedParticleSystem>
|
|
|
|
using namespace osgParticle;
|
|
|
|
ConnectedParticleSystem::ConnectedParticleSystem():
|
|
_lastParticleCreated(Particle::INVALID_INDEX),
|
|
_maxNumberOfParticlesToSkip(200),
|
|
_startParticle(Particle::INVALID_INDEX)
|
|
{
|
|
}
|
|
|
|
ConnectedParticleSystem::ConnectedParticleSystem(const ConnectedParticleSystem& copy, const osg::CopyOp& copyop):
|
|
ParticleSystem(copy,copyop),
|
|
_lastParticleCreated(copy._lastParticleCreated),
|
|
_maxNumberOfParticlesToSkip(200),
|
|
_startParticle(copy._startParticle)
|
|
{
|
|
}
|
|
|
|
ConnectedParticleSystem::~ConnectedParticleSystem()
|
|
{
|
|
}
|
|
|
|
Particle* ConnectedParticleSystem::createParticle(const Particle* ptemplate)
|
|
{
|
|
// OSG_NOTICE<<this<< " Creating particle "<<std::endl;
|
|
|
|
Particle* particle = ParticleSystem::createParticle(ptemplate);
|
|
int particleIndex = (int)(particle - &_particles[0]);
|
|
|
|
if (particle)
|
|
{
|
|
|
|
if (_startParticle == Particle::INVALID_INDEX)
|
|
{
|
|
// we are the fisrt particle create, so start the connect particle list
|
|
_startParticle = particleIndex;
|
|
}
|
|
|
|
if (_lastParticleCreated != Particle::INVALID_INDEX)
|
|
{
|
|
// OSG_NOTICE<<this<< " Connecting "<<_lastParticleCreated<<" to "<<particleIndex<<std::endl;
|
|
|
|
// write up the last created particle to this new particle
|
|
_particles[_lastParticleCreated].setNextParticle(particleIndex);
|
|
particle->setPreviousParticle(_lastParticleCreated);
|
|
}
|
|
|
|
// set the new particle as the last particle created.
|
|
_lastParticleCreated = particleIndex;
|
|
|
|
}
|
|
|
|
return particle;
|
|
}
|
|
|
|
void ConnectedParticleSystem::reuseParticle(int particleIndex)
|
|
{
|
|
// OSG_NOTICE<<this<< " Reusing particle "<<particleIndex<<std::endl;
|
|
|
|
if (particleIndex<0 || particleIndex>=(int)_particles.size()) return;
|
|
|
|
Particle* particle = &_particles[particleIndex];
|
|
int previous = particle->getPreviousParticle();
|
|
int next = particle->getNextParticle();
|
|
|
|
// update start and last entries
|
|
if (_startParticle == particleIndex)
|
|
{
|
|
_startParticle = particle->getNextParticle();
|
|
}
|
|
|
|
if (_lastParticleCreated == particleIndex)
|
|
{
|
|
_lastParticleCreated = Particle::INVALID_INDEX;
|
|
}
|
|
|
|
// join up the previous and next particles to account for
|
|
// the deletion of the this particle
|
|
if (previous != Particle::INVALID_INDEX)
|
|
{
|
|
_particles[previous].setNextParticle(next);
|
|
}
|
|
|
|
if (next != Particle::INVALID_INDEX)
|
|
{
|
|
_particles[next].setPreviousParticle(previous);
|
|
}
|
|
|
|
// reset the next and previous particle entries of this particle
|
|
particle->setPreviousParticle(Particle::INVALID_INDEX);
|
|
particle->setNextParticle(Particle::INVALID_INDEX);
|
|
|
|
// put the particle on the death stack
|
|
ParticleSystem::reuseParticle(particleIndex);
|
|
|
|
}
|
|
|
|
void ConnectedParticleSystem::drawImplementation(osg::RenderInfo& renderInfo) const
|
|
{
|
|
osg::State& state = *renderInfo.getState();
|
|
osg::GLBeginEndAdapter& gl = state.getGLBeginEndAdapter();
|
|
|
|
ScopedReadLock lock(_readWriteMutex);
|
|
|
|
const Particle* particle = (_startParticle != Particle::INVALID_INDEX) ? &_particles[_startParticle] : 0;
|
|
if (!particle) return;
|
|
|
|
|
|
osg::Vec4 pixelSizeVector = osg::CullingSet::computePixelSizeVector(*state.getCurrentViewport(),state.getProjectionMatrix(),state.getModelViewMatrix());
|
|
float unitPixelSize = fabs(1.0/(particle->getPosition()*pixelSizeVector));
|
|
float pixelSizeOfFirstParticle = unitPixelSize * particle->getCurrentSize();
|
|
//float desiredGapBetweenDrawnParticles = 50.0f/unitPixelSize;
|
|
//float desiredGapBetweenDrawnParticles2 = desiredGapBetweenDrawnParticles*desiredGapBetweenDrawnParticles;
|
|
|
|
float maxPixelError2 = osg::square(1.0f/unitPixelSize);
|
|
|
|
if (pixelSizeOfFirstParticle<1.0)
|
|
{
|
|
// draw the connected particles as a line
|
|
gl.Begin(GL_LINE_STRIP);
|
|
while(particle != 0)
|
|
{
|
|
|
|
const osg::Vec4& color = particle->getCurrentColor();
|
|
const osg::Vec3& pos = particle->getPosition();
|
|
gl.Color4f( color.r(), color.g(), color.b(), color.a() * particle->getCurrentAlpha());
|
|
gl.TexCoord2f( particle->getSTexCoord(), 0.5f );
|
|
gl.Vertex3fv(pos.ptr());
|
|
|
|
const Particle* nextParticle = (particle->getNextParticle() != Particle::INVALID_INDEX) ? &_particles[particle->getNextParticle()] : 0;
|
|
if (nextParticle)
|
|
{
|
|
const osg::Vec3& nextPos = nextParticle->getPosition();
|
|
osg::Vec3 startDelta = nextPos-pos;
|
|
startDelta.normalize();
|
|
float distance2 = 0.0;
|
|
|
|
// now skip particles of required
|
|
for(unsigned int i=0;
|
|
i<_maxNumberOfParticlesToSkip && ((distance2<maxPixelError2) && (nextParticle->getNextParticle()!=Particle::INVALID_INDEX));
|
|
++i)
|
|
{
|
|
nextParticle = &_particles[nextParticle->getNextParticle()];
|
|
const osg::Vec3& nextPos = nextParticle->getPosition();
|
|
osg::Vec3 delta = nextPos-pos;
|
|
distance2 = (delta^startDelta).length2();
|
|
}
|
|
}
|
|
particle = nextParticle;
|
|
}
|
|
gl.End();
|
|
}
|
|
else
|
|
{
|
|
|
|
// draw the connected particles as a quad stripped aligned to be orthogonal to the eye
|
|
osg::Matrix eyeToLocalTransform;
|
|
eyeToLocalTransform.invert(state.getModelViewMatrix());
|
|
osg::Vec3 eyeLocal = osg::Vec3(0.0f,0.0,0.0f)*eyeToLocalTransform;
|
|
|
|
osg::Vec3 delta(0.0f,0.0f,1.0f);
|
|
|
|
gl.Begin(GL_QUAD_STRIP);
|
|
while(particle != 0)
|
|
{
|
|
const osg::Vec4& color = particle->getCurrentColor();
|
|
const osg::Vec3& pos = particle->getPosition();
|
|
|
|
const Particle* nextParticle = (particle->getNextParticle() != Particle::INVALID_INDEX) ? &_particles[particle->getNextParticle()] : 0;
|
|
|
|
if (nextParticle)
|
|
{
|
|
const osg::Vec3& nextPos = nextParticle->getPosition();
|
|
osg::Vec3 startDelta = nextPos-pos;
|
|
startDelta.normalize();
|
|
float distance2 = 0.0;
|
|
|
|
// now skip particles of required
|
|
for(unsigned int i=0;
|
|
i<_maxNumberOfParticlesToSkip && ((distance2<maxPixelError2) && (nextParticle->getNextParticle()!=Particle::INVALID_INDEX));
|
|
++i)
|
|
{
|
|
nextParticle = &_particles[nextParticle->getNextParticle()];
|
|
const osg::Vec3& nextPos = nextParticle->getPosition();
|
|
delta = nextPos-pos;
|
|
distance2 = (delta^startDelta).length2();
|
|
}
|
|
|
|
delta = nextPos-pos;
|
|
}
|
|
|
|
osg::Vec3 normal( delta ^ (pos-eyeLocal));
|
|
normal.normalize();
|
|
normal *= particle->getCurrentSize();
|
|
|
|
osg::Vec3 bottom(pos-normal);
|
|
osg::Vec3 top(pos+normal);
|
|
|
|
gl.Color4f( color.r(), color.g(), color.b(), color.a() * particle->getCurrentAlpha());
|
|
|
|
gl.TexCoord2f( particle->getSTexCoord(), 0.0f );
|
|
gl.Vertex3fv(bottom.ptr());
|
|
|
|
gl.TexCoord2f( particle->getSTexCoord(), 1.0f );
|
|
gl.Vertex3fv(top.ptr());
|
|
|
|
particle = nextParticle;
|
|
}
|
|
gl.End();
|
|
}
|
|
}
|
|
|