root/OpenSceneGraph/trunk/src/osgParticle/ParticleSystem.cpp @ 13041

Revision 13041, 17.6 kB (checked in by robert, 3 years ago)

Ran script to remove trailing spaces and tabs

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1#include <osgParticle/ParticleSystem>
2
3#include <vector>
4
5#include <osg/Drawable>
6#include <osg/CopyOp>
7#include <osg/State>
8#include <osg/Matrix>
9#include <osg/GL>
10#include <osg/StateSet>
11#include <osg/Texture2D>
12#include <osg/BlendFunc>
13#include <osg/TexEnv>
14#include <osg/Material>
15#include <osg/PointSprite>
16#include <osg/Program>
17#include <osg/Notify>
18#include <osg/io_utils>
19
20#include <osgDB/FileUtils>
21#include <osgDB/ReadFile>
22#include <osgUtil/CullVisitor>
23
24#define USE_LOCAL_SHADERS
25
26static double distance(const osg::Vec3& coord, const osg::Matrix& matrix)
27{
28    // copied from CullVisitor.cpp
29    return -(coord[0]*matrix(0,2)+coord[1]*matrix(1,2)+coord[2]*matrix(2,2)+matrix(3,2));
30}
31
32osgParticle::ParticleSystem::ParticleSystem()
33:    osg::Drawable(),
34    _def_bbox(osg::Vec3(-10, -10, -10), osg::Vec3(10, 10, 10)),
35    _alignment(BILLBOARD),
36    _align_X_axis(1, 0, 0),
37    _align_Y_axis(0, 1, 0),
38    _particleScaleReferenceFrame(WORLD_COORDINATES),
39    _useVertexArray(false),
40    _useShaders(false),
41    _dirty_uniforms(false),
42    _doublepass(false),
43    _frozen(false),
44    _bmin(0, 0, 0),
45    _bmax(0, 0, 0),
46    _reset_bounds_flag(false),
47    _bounds_computed(false),
48    _def_ptemp(Particle()),
49    _last_frame(0),
50    _dirty_dt(true),
51    _freeze_on_cull(false),
52    _t0(0.0),
53    _dt(0.0),
54    _detail(1),
55    _sortMode(NO_SORT),
56    _visibilityDistance(-1.0),
57    _draw_count(0)
58{
59    // we don't support display lists because particle systems
60    // are dynamic, and they always changes between frames
61    setSupportsDisplayList(false);
62}
63
64osgParticle::ParticleSystem::ParticleSystem(const ParticleSystem& copy, const osg::CopyOp& copyop)
65:    osg::Drawable(copy, copyop),
66    _def_bbox(copy._def_bbox),
67    _alignment(copy._alignment),
68    _align_X_axis(copy._align_X_axis),
69    _align_Y_axis(copy._align_Y_axis),
70    _particleScaleReferenceFrame(copy._particleScaleReferenceFrame),
71    _useVertexArray(copy._useVertexArray),
72    _useShaders(copy._useShaders),
73    _dirty_uniforms(copy._dirty_uniforms),
74    _doublepass(copy._doublepass),
75    _frozen(copy._frozen),
76    _bmin(copy._bmin),
77    _bmax(copy._bmax),
78    _reset_bounds_flag(copy._reset_bounds_flag),
79    _bounds_computed(copy._bounds_computed),
80    _def_ptemp(copy._def_ptemp),
81    _last_frame(copy._last_frame),
82    _dirty_dt(copy._dirty_dt),
83    _freeze_on_cull(copy._freeze_on_cull),
84    _t0(copy._t0),
85    _dt(copy._dt),
86    _detail(copy._detail),
87    _sortMode(copy._sortMode),
88    _visibilityDistance(copy._visibilityDistance),
89    _draw_count(0)
90{
91}
92
93osgParticle::ParticleSystem::~ParticleSystem()
94{
95}
96
97void osgParticle::ParticleSystem::update(double dt, osg::NodeVisitor& nv)
98{
99    // reset bounds
100    _reset_bounds_flag = true;
101
102    if (_useShaders)
103    {
104        // Update shader uniforms
105        // This slightly reduces the consumption of traversing the particle vector, because we
106        // don't compute tile and angle attributes that are useleff for shaders.
107        // At present, our lcoal shader implementation will ignore these particle props:
108        //     _cur_tile, _s_coord, _t_coord, _prev_pos, _prev_angle and _angle
109        osg::StateSet* stateset = getOrCreateStateSet();
110
111        if (_dirty_uniforms)
112        {
113            osg::Uniform* u_vd = stateset->getUniform("visibilityDistance");
114            if (u_vd) u_vd->set((float)_visibilityDistance);
115            _dirty_uniforms = false;
116        }
117    }
118
119    for(unsigned int i=0; i<_particles.size(); ++i)
120    {
121        Particle& particle = _particles[i];
122        if (particle.isAlive())
123        {
124            if (particle.update(dt, _useShaders))
125            {
126                update_bounds(particle.getPosition(), particle.getCurrentSize());
127            }
128            else
129            {
130                reuseParticle(i);
131            }
132        }
133    }
134
135    if (_sortMode != NO_SORT)
136    {
137        // sort particles
138        osgUtil::CullVisitor* cv = dynamic_cast<osgUtil::CullVisitor*>(&nv);
139        if (cv)
140        {
141            osg::Matrixd modelview = *(cv->getModelViewMatrix());
142            float scale = (_sortMode==SORT_FRONT_TO_BACK ? -1.0f : 1.0f);
143            for (unsigned int i=0; i<_particles.size(); ++i)
144            {
145                Particle& particle = _particles[i];
146                if (particle.isAlive())
147                    particle.setDepth(distance(particle.getPosition(), modelview) * scale);
148                else
149                    particle.setDepth(0.0f);
150            }
151            std::sort<Particle_vector::iterator>(_particles.begin(), _particles.end());
152        }
153    }
154
155    // force recomputing of bounding box on next frame
156    dirtyBound();
157}
158
159void osgParticle::ParticleSystem::drawImplementation(osg::RenderInfo& renderInfo) const
160{
161    osg::State& state = *renderInfo.getState();
162
163    ScopedReadLock lock(_readWriteMutex);
164
165    // update the frame count, so other objects can detect when
166    // this particle system is culled
167    _last_frame = state.getFrameStamp()->getFrameNumber();
168
169    // update the dirty flag of delta time, so next time a new request for delta time
170    // will automatically cause recomputing
171    _dirty_dt = true;
172
173    // get the current modelview matrix
174    osg::Matrix modelview = state.getModelViewMatrix();
175
176    // set up depth mask for first rendering pass
177#if !defined(OSG_GLES1_AVAILABLE) && !defined(OSG_GLES2_AVAILABLE) && !defined(OSG_GL3_AVAILABLE)
178    glPushAttrib(GL_DEPTH_BUFFER_BIT);
179#endif
180
181    glDepthMask(GL_FALSE);
182
183    // render, first pass
184    if (_useVertexArray)
185        render_vertex_array(renderInfo);
186    else
187        single_pass_render(renderInfo, modelview);
188
189#if !defined(OSG_GLES1_AVAILABLE) && !defined(OSG_GLES2_AVAILABLE) && !defined(OSG_GL3_AVAILABLE)
190    // restore depth mask settings
191    glPopAttrib();
192#endif
193
194    // render, second pass
195    if (_doublepass) {
196        // set up color mask for second rendering pass
197#if !defined(OSG_GLES1_AVAILABLE) && !defined(OSG_GLES2_AVAILABLE) && !defined(OSG_GL3_AVAILABLE)
198        glPushAttrib(GL_COLOR_BUFFER_BIT);
199#endif
200        glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
201
202        // render the particles onto the depth buffer
203        if (_useVertexArray)
204            render_vertex_array(renderInfo);
205        else
206            single_pass_render(renderInfo, modelview);
207
208#if !defined(OSG_GLES1_AVAILABLE) && !defined(OSG_GLES2_AVAILABLE) && !defined(OSG_GL3_AVAILABLE)
209        // restore color mask settings
210        glPopAttrib();
211#endif
212    }
213
214#if defined(OSG_GLES1_AVAILABLE) || defined(OSG_GLES2_AVAILABLE) && !defined(OSG_GL3_AVAILABLE)
215    OSG_NOTICE<<"Warning: ParticleSystem::drawImplementation(..) not fully implemented."<<std::endl;
216#endif
217
218}
219
220void osgParticle::ParticleSystem::setDefaultAttributes(const std::string& texturefile, bool emissive_particles, bool lighting, int texture_unit)
221{
222    osg::StateSet *stateset = new osg::StateSet;
223
224    stateset->setMode(GL_LIGHTING, lighting? osg::StateAttribute::ON: osg::StateAttribute::OFF);
225    stateset->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
226
227    osg::Material *material = new osg::Material;
228    material->setSpecular(osg::Material::FRONT, osg::Vec4(0, 0, 0, 1));
229    material->setEmission(osg::Material::FRONT, osg::Vec4(0, 0, 0, 1));
230    material->setColorMode(lighting? osg::Material::AMBIENT_AND_DIFFUSE : osg::Material::OFF);
231    stateset->setAttributeAndModes(material, osg::StateAttribute::ON);
232
233    if (!texturefile.empty()) {
234        osg::Texture2D *texture = new osg::Texture2D;
235        texture->setImage(osgDB::readImageFile(texturefile));
236        texture->setFilter(osg::Texture2D::MIN_FILTER, osg::Texture2D::LINEAR);
237        texture->setFilter(osg::Texture2D::MAG_FILTER, osg::Texture2D::LINEAR);
238        texture->setWrap(osg::Texture2D::WRAP_S, osg::Texture2D::MIRROR);
239        texture->setWrap(osg::Texture2D::WRAP_T, osg::Texture2D::MIRROR);
240        stateset->setTextureAttributeAndModes(texture_unit, texture, osg::StateAttribute::ON);
241
242        osg::TexEnv *texenv = new osg::TexEnv;
243        texenv->setMode(osg::TexEnv::MODULATE);
244        stateset->setTextureAttribute(texture_unit, texenv);
245    }
246
247    osg::BlendFunc *blend = new osg::BlendFunc;
248    if (emissive_particles) {
249        blend->setFunction(osg::BlendFunc::SRC_ALPHA, osg::BlendFunc::ONE);
250    } else {
251        blend->setFunction(osg::BlendFunc::SRC_ALPHA, osg::BlendFunc::ONE_MINUS_SRC_ALPHA);
252    }
253    stateset->setAttributeAndModes(blend, osg::StateAttribute::ON);
254
255    setStateSet(stateset);
256    setUseVertexArray(false);
257    setUseShaders(false);
258}
259
260
261void osgParticle::ParticleSystem::setDefaultAttributesUsingShaders(const std::string& texturefile, bool emissive_particles, int texture_unit)
262{
263    osg::StateSet *stateset = new osg::StateSet;
264    stateset->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
265
266    osg::PointSprite *sprite = new osg::PointSprite;
267    stateset->setTextureAttributeAndModes(texture_unit, sprite, osg::StateAttribute::ON);
268
269    #if !defined(OSG_GLES1_AVAILABLE) && !defined(OSG_GLES2_AVAILABLE)
270        stateset->setMode(GL_VERTEX_PROGRAM_POINT_SIZE, osg::StateAttribute::ON);
271    #else
272        OSG_NOTICE<<"Warning: ParticleSystem::setDefaultAttributesUsingShaders(..) not fully implemented."<<std::endl;
273    #endif
274
275    if (!texturefile.empty())
276    {
277        osg::Texture2D *texture = new osg::Texture2D;
278        texture->setImage(osgDB::readImageFile(texturefile));
279        texture->setFilter(osg::Texture2D::MIN_FILTER, osg::Texture2D::LINEAR);
280        texture->setFilter(osg::Texture2D::MAG_FILTER, osg::Texture2D::LINEAR);
281        texture->setWrap(osg::Texture2D::WRAP_S, osg::Texture2D::MIRROR);
282        texture->setWrap(osg::Texture2D::WRAP_T, osg::Texture2D::MIRROR);
283        stateset->setTextureAttributeAndModes(texture_unit, texture, osg::StateAttribute::ON);
284    }
285
286    osg::BlendFunc *blend = new osg::BlendFunc;
287    if (emissive_particles)
288    {
289        blend->setFunction(osg::BlendFunc::SRC_ALPHA, osg::BlendFunc::ONE);
290    }
291    else
292    {
293        blend->setFunction(osg::BlendFunc::SRC_ALPHA, osg::BlendFunc::ONE_MINUS_SRC_ALPHA);
294    }
295    stateset->setAttributeAndModes(blend, osg::StateAttribute::ON);
296
297    osg::Program *program = new osg::Program;
298#ifdef USE_LOCAL_SHADERS
299    char vertexShaderSource[] =
300        "uniform float visibilityDistance;\n"
301        "varying vec3 basic_prop;\n"
302        "\n"
303        "void main(void)\n"
304        "{\n"
305        "    basic_prop = gl_MultiTexCoord0.xyz;\n"
306        "    \n"
307        "    vec4 ecPos = gl_ModelViewMatrix * gl_Vertex;\n"
308        "    float ecDepth = -ecPos.z;\n"
309        "    \n"
310        "    if (visibilityDistance > 0.0)\n"
311        "    {\n"
312        "        if (ecDepth <= 0.0 || ecDepth >= visibilityDistance)\n"
313        "            basic_prop.x = -1.0;\n"
314        "    }\n"
315        "    \n"
316        "    gl_Position = ftransform();\n"
317        "    gl_ClipVertex = ecPos;\n"
318        "    \n"
319        "    vec4 color = gl_Color;\n"
320        "    color.a *= basic_prop.z;\n"
321        "    gl_FrontColor = color;\n"
322        "    gl_BackColor = gl_FrontColor;\n"
323        "}\n";
324    char fragmentShaderSource[] =
325        "uniform sampler2D baseTexture;\n"
326        "varying vec3 basic_prop;\n"
327        "\n"
328        "void main(void)\n"
329        "{\n"
330        "    if (basic_prop.x < 0.0) discard;\n"
331        "    gl_FragColor = gl_Color * texture2D(baseTexture, gl_TexCoord[0].xy);\n"
332        "}\n";
333    program->addShader(new osg::Shader(osg::Shader::VERTEX, vertexShaderSource));
334    program->addShader(new osg::Shader(osg::Shader::FRAGMENT, fragmentShaderSource));
335#else
336    program->addShader(osg::Shader::readShaderFile(osg::Shader::VERTEX, osgDB::findDataFile("shaders/particle.vert")));
337    program->addShader(osg::Shader::readShaderFile(osg::Shader::FRAGMENT, osgDB::findDataFile("shaders/particle.frag")));
338#endif
339    stateset->setAttributeAndModes(program, osg::StateAttribute::ON);
340
341    stateset->addUniform(new osg::Uniform("visibilityDistance", (float)_visibilityDistance));
342    stateset->addUniform(new osg::Uniform("baseTexture", texture_unit));
343    setStateSet(stateset);
344
345    setUseVertexArray(true);
346    setUseShaders(true);
347}
348
349
350void osgParticle::ParticleSystem::single_pass_render(osg::RenderInfo& renderInfo, const osg::Matrix& modelview) const
351{
352    _draw_count = 0;
353    if (_particles.size() <= 0) return;
354
355    osg::GLBeginEndAdapter* gl = &(renderInfo.getState()->getGLBeginEndAdapter());
356
357    float scale = sqrtf(static_cast<float>(_detail));
358
359    osg::Vec3 xAxis = _align_X_axis;
360    osg::Vec3 yAxis = _align_Y_axis;
361
362    osg::Vec3 scaled_aligned_xAxis = _align_X_axis;
363    osg::Vec3 scaled_aligned_yAxis = _align_Y_axis;
364
365    float xScale = 1.0f;
366    float yScale = 1.0f;
367
368    if (_alignment==BILLBOARD)
369    {
370        xAxis = osg::Matrix::transform3x3(modelview,_align_X_axis);
371        yAxis = osg::Matrix::transform3x3(modelview,_align_Y_axis);
372
373        float lengthX2 = xAxis.length2();
374        float lengthY2 = yAxis.length2();
375
376        if (_particleScaleReferenceFrame==LOCAL_COORDINATES)
377        {
378            xScale = 1.0f/sqrtf(lengthX2);
379            yScale = 1.0f/sqrtf(lengthY2);
380        }
381        else
382        {
383            xScale = 1.0f/lengthX2;
384            yScale = 1.0f/lengthY2;
385        }
386
387        scaled_aligned_xAxis *= xScale;
388        scaled_aligned_yAxis *= yScale;
389
390        xAxis *= xScale;
391        yAxis *= yScale;
392    }
393
394    bool requiresEndRender = false;
395    const Particle* startParticle = &_particles[0];
396    if (startParticle->getShape() != Particle::USER)
397    {
398        startParticle->beginRender(gl);
399        requiresEndRender = true;
400    }
401    else
402    {
403        // Enable writing depth mask when drawing user-defined particles
404        glDepthMask(GL_TRUE);
405    }
406
407    for(unsigned int i=0; i<_particles.size(); i+=_detail)
408    {
409        const Particle* currentParticle = &_particles[i];
410
411        bool insideDistance = true;
412        if (_sortMode != NO_SORT && _visibilityDistance>0.0)
413            insideDistance = (currentParticle->getDepth()>=0.0 && currentParticle->getDepth()<=_visibilityDistance);
414
415        if (currentParticle->isAlive() && insideDistance)
416        {
417            if (currentParticle->getShape() != startParticle->getShape())
418            {
419                startParticle->endRender(gl);
420                startParticle = currentParticle;
421                if (currentParticle->getShape() != Particle::USER)
422                {
423                    currentParticle->beginRender(gl);
424                    requiresEndRender = true;
425                    glDepthMask(GL_FALSE);
426                }
427                else
428                    glDepthMask(GL_TRUE);
429            }
430            ++_draw_count;
431
432            if (currentParticle->getShape() == Particle::USER)
433            {
434                if (requiresEndRender)
435                {
436                    startParticle->endRender(gl);
437                    requiresEndRender = false;
438                }
439                currentParticle->render(renderInfo, currentParticle->getPosition(), currentParticle->getAngle());
440                continue;
441            }
442
443            const osg::Vec3& angle = currentParticle->getAngle();
444            bool requiresRotation = (angle.x()!=0.0f || angle.y()!=0.0f || angle.z()!=0.0f);
445            if (requiresRotation)
446            {
447                osg::Matrix R;
448                R.makeRotate(
449                    angle.x(), osg::Vec3(1, 0, 0),
450                    angle.y(), osg::Vec3(0, 1, 0),
451                    angle.z(), osg::Vec3(0, 0, 1));
452
453                if (_alignment==BILLBOARD)
454                {
455                    xAxis = osg::Matrix::transform3x3(R,scaled_aligned_xAxis);
456                    xAxis = osg::Matrix::transform3x3(modelview,xAxis);
457
458                    yAxis = osg::Matrix::transform3x3(R,scaled_aligned_yAxis);
459                    yAxis = osg::Matrix::transform3x3(modelview,yAxis);
460
461                    currentParticle->render(gl,currentParticle->getPosition(), xAxis, yAxis, scale);
462                }
463                else
464                {
465                    xAxis = osg::Matrix::transform3x3(R, scaled_aligned_xAxis);
466                    yAxis = osg::Matrix::transform3x3(R, scaled_aligned_yAxis);
467
468                    currentParticle->render(gl,currentParticle->getPosition(), xAxis, yAxis, scale);
469                }
470            }
471            else
472            {
473                currentParticle->render(gl,currentParticle->getPosition(), xAxis, yAxis, scale);
474            }
475        }
476    }
477
478    if (requiresEndRender)
479        startParticle->endRender(gl);
480}
481
482void osgParticle::ParticleSystem::render_vertex_array(osg::RenderInfo& renderInfo) const
483{
484    if (_particles.size() <= 0) return;
485
486    // Compute the pointer and offsets
487    Particle_vector::const_iterator itr = _particles.begin();
488    float* ptr = (float*)(&(*itr));
489    GLsizei stride = 0;
490    if (_particles.size() > 1)
491    {
492        float* ptr1 = (float*)(&(*(itr+1)));
493        stride = ptr1 - ptr;
494    }
495    GLsizei posOffset = (float*)(&(itr->_position)) - ptr;         // Position
496    GLsizei colorOffset = (float*)(&(itr->_current_color)) - ptr;  // Color
497    GLsizei velOffset = (float*)(&(itr->_velocity)) - ptr;         // Velocity
498    GLsizei propOffset = (float*)(&(itr->_alive)) - ptr;       // Alive, size & alpha
499
500    // Draw particles as arrays
501    osg::State& state = *renderInfo.getState();
502    state.lazyDisablingOfVertexAttributes();
503    state.setColorPointer(4, GL_FLOAT, stride * sizeof(float), ptr + colorOffset);
504    state.setVertexPointer(3, GL_FLOAT, stride * sizeof(float), ptr + posOffset);
505    if (_useShaders)
506    {
507        state.setNormalPointer(GL_FLOAT, stride * sizeof(float), ptr + velOffset);
508        state.setTexCoordPointer(0, 3, GL_FLOAT, stride * sizeof(float), ptr + propOffset);
509    }
510    state.applyDisablingOfVertexAttributes();
511    glDrawArrays(GL_POINTS, 0, _particles.size());
512}
513
514osg::BoundingBox osgParticle::ParticleSystem::computeBound() const
515{
516    if (!_bounds_computed)
517    {
518        return _def_bbox;
519    } else
520    {
521        return osg::BoundingBox(_bmin,_bmax);
522    }
523}
Note: See TracBrowser for help on using the browser.