root/OpenSceneGraph/trunk/examples/osgprecipitation/osgprecipitation.cpp @ 5083

Revision 5083, 19.3 kB (checked in by robert, 9 years ago)

Added support for point sprite particle effects.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2003 Robert Osfield
2 *
3 * This application is open source and may be redistributed and/or modified   
4 * freely and without restriction, both in commericial and non commericial applications,
5 * as long as this copyright notice is maintained.
6 *
7 * This application is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
10*/
11
12#include <osgDB/ReadFile>
13#include <osgDB/FileUtils>
14#include <osgUtil/Optimizer>
15#include <osgUtil/CullVisitor>
16#include <osgProducer/Viewer>
17
18#include <osg/Point>
19#include <osg/BlendFunc>
20#include <osg/Texture2D>
21#include <osg/PointSprite>
22#include <osg/Program>
23#include <osg/Fog>
24#include <osg/Point>
25#include <osg/PointSprite>
26#include <osg/io_utils>
27
28float random(float min,float max) { return min + (max-min)*(float)rand()/(float)RAND_MAX; }
29
30class PrecipitationGeometry : public osg::Geometry
31{
32public:
33        virtual bool supports(const osg::PrimitiveFunctor&) const { return false; }
34        virtual void accept(osg::PrimitiveFunctor&) const {}
35        virtual bool supports(const osg::PrimitiveIndexFunctor&) const { return false; }
36        virtual void accept(osg::PrimitiveIndexFunctor&) const {}
37       
38        virtual osg::BoundingBox computeBound() const
39        {
40            return osg::BoundingBox();
41        }
42
43};
44
45class CullCallback : public osg::NodeCallback
46{
47public:
48
49    CullCallback(osg::Uniform* uniform):
50        _previousFrame(0),
51        _initialized(false),
52        _uniform(uniform)
53    {
54    }
55
56    virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
57    {
58        osgUtil::CullVisitor* cv = dynamic_cast<osgUtil::CullVisitor*>(nv);
59        if (cv)
60        {
61            if (!_initialized)
62            {
63                _previousModelViewMatrix = cv->getModelViewMatrix();
64                _previousFrame = nv->getFrameStamp()->getFrameNumber();
65                _initialized = true;
66            }
67       
68            _uniform->set(_previousModelViewMatrix);
69           
70            // osg::notify(osg::NOTICE)<<"Updating uniform "<<_previousModelViewMatrix<<std::endl;
71
72            traverse(node, nv);
73           
74            if (_previousFrame != nv->getFrameStamp()->getFrameNumber())
75            {
76                _previousModelViewMatrix = cv->getModelViewMatrix();
77                _previousFrame = nv->getFrameStamp()->getFrameNumber();
78            }
79        }
80        else
81        {
82            traverse(node, nv);
83        }
84    }
85   
86    int _previousFrame;
87    bool _initialized;
88    osg::Matrix _previousModelViewMatrix;
89    osg::ref_ptr<osg::Uniform> _uniform;   
90};
91
92void fillSpotLightImage(unsigned char* ptr, const osg::Vec4& centerColour, const osg::Vec4& backgroudColour, unsigned int size, float power)
93{
94    float mid = (float(size)-1.0f)*0.5f;
95    float div = 2.0f/float(size);
96    for(unsigned int r=0;r<size;++r)
97    {
98        //unsigned char* ptr = image->data(0,r,0);
99        for(unsigned int c=0;c<size;++c)
100        {
101            float dx = (float(c) - mid)*div;
102            float dy = (float(r) - mid)*div;
103            float r = powf(1.0f-sqrtf(dx*dx+dy*dy),power);
104            if (r<0.0f) r=0.0f;
105            osg::Vec4 color = centerColour*r+backgroudColour*(1.0f-r);
106            *ptr++ = (unsigned char)((color[0])*255.0f);
107            *ptr++ = (unsigned char)((color[1])*255.0f);
108            *ptr++ = (unsigned char)((color[2])*255.0f);
109            *ptr++ = (unsigned char)((color[3])*255.0f);
110        }
111    }
112}
113
114
115osg::Image* createSpotLightImage(const osg::Vec4& centerColour, const osg::Vec4& backgroudColour, unsigned int size, float power)
116{
117
118#if 0
119    osg::Image* image = new osg::Image;
120    unsigned char* ptr = image->data(0,0,0);
121    fillSpotLightImage(ptr, centerColour, backgroudColour, size, power);
122
123    return image;
124#else
125    osg::Image* image = new osg::Image;
126    osg::Image::MipmapDataType mipmapData;
127    unsigned int s = size;
128    unsigned int totalSize = 0;
129    unsigned i;
130    for(i=0; s>0; s>>=1, ++i)
131    {
132        if (i>0) mipmapData.push_back(totalSize);
133        totalSize += s*s*4;
134        std::cout<<" i= "<<i<<" s="<<s<<" p="<<totalSize<<std::endl;
135    }
136
137    std::cout<<"Total size ="<<totalSize<<std::endl;
138
139    unsigned char* ptr = new unsigned char[totalSize];
140    image->setImage(size, size, size, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, ptr, osg::Image::USE_NEW_DELETE,1);
141
142    image->setMipmapLevels(mipmapData);
143
144    s = size;
145    for(i=0; s>0; s>>=1, ++i)
146    {
147        fillSpotLightImage(ptr, centerColour, backgroudColour, s, power);
148        ptr += s*s*4;
149    }
150
151    return image;
152#endif   
153}
154
155/** create quad, line and point geometry data all with consistent particle positions.*/
156void createGeometry(unsigned int numParticles,
157                    osg::Geometry* quad_geometry,
158                    osg::Geometry* line_geometry,
159                    osg::Geometry* point_geometry)
160{
161    // particle corner offsets
162    osg::Vec2 offset00(0.0f,0.0f);
163    osg::Vec2 offset10(1.0f,0.0f);
164    osg::Vec2 offset01(0.0f,1.0f);
165    osg::Vec2 offset11(1.0f,1.0f);
166   
167    osg::Vec2 offset0(0.5f,0.0f);
168    osg::Vec2 offset1(0.5f,1.0f);
169
170    osg::Vec2 offset(0.5f,0.5f);
171
172
173    // configure quad_geometry;
174    osg::Vec3Array* quad_vertices = 0;
175    osg::Vec2Array* quad_offsets = 0;
176    if (quad_geometry)
177    {
178        quad_vertices = new osg::Vec3Array(numParticles*4);
179        quad_offsets = new osg::Vec2Array(numParticles*4);
180
181        quad_geometry->setVertexArray(quad_vertices);
182        quad_geometry->setTexCoordArray(0, quad_offsets);
183        quad_geometry->addPrimitiveSet(new osg::DrawArrays(GL_QUADS, 0, numParticles*4));
184    }
185
186    // configure line_geometry;
187    osg::Vec3Array* line_vertices = 0;
188    osg::Vec2Array* line_offsets = 0;
189    if (line_geometry)
190    {
191        line_vertices = new osg::Vec3Array(numParticles*2);
192        line_offsets = new osg::Vec2Array(numParticles*2);
193
194        line_geometry->setVertexArray(line_vertices);
195        line_geometry->setTexCoordArray(0, line_offsets);
196        line_geometry->addPrimitiveSet(new osg::DrawArrays(GL_LINES, 0, numParticles*2));
197    }
198
199    // configure point_geometry;
200    osg::Vec3Array* point_vertices = 0;
201    osg::Vec2Array* point_offsets = 0;
202    if (point_geometry)
203    {
204        point_vertices = new osg::Vec3Array(numParticles);
205        point_offsets = new osg::Vec2Array(numParticles);
206
207        point_geometry->setVertexArray(point_vertices);
208        point_geometry->setTexCoordArray(0, point_offsets);
209        point_geometry->addPrimitiveSet(new osg::DrawArrays(GL_POINTS, 0, numParticles));
210    }
211
212    // set up vertex attribute data.
213    for(unsigned int i=0; i< numParticles; ++i)
214    {
215        osg::Vec3 pos( random(0.0f, 1.0f), random(0.0f, 1.0f), random(0.0f, 1.0f));
216   
217        // quad particles
218        if (quad_vertices)
219        {
220            (*quad_vertices)[i*4] = pos;
221            (*quad_vertices)[i*4+1] = pos;
222            (*quad_vertices)[i*4+2] =  pos;
223            (*quad_vertices)[i*4+3] =  pos;
224            (*quad_offsets)[i*4] = offset00;
225            (*quad_offsets)[i*4+1] = offset01;
226            (*quad_offsets)[i*4+2] = offset11;
227            (*quad_offsets)[i*4+3] = offset10;
228        }
229               
230        // line particles
231        if (line_vertices)
232        {
233            (*line_vertices)[i*2] = pos;
234            (*line_vertices)[i*2+1] = pos;
235            (*line_offsets)[i*2] = offset0;
236            (*line_offsets)[i*2+1] = offset1;
237        }
238       
239        // point particles
240        if (point_vertices)
241        {
242            (*point_vertices)[i] = pos;
243            (*point_offsets)[i] = offset;
244        }
245    }
246}
247
248
249osg::Node* createRainEffect(const osg::BoundingBox& bb, const osg::Vec3& velocity, unsigned int numParticles, bool useShaders)
250{
251    osg::Geode* geode = new osg::Geode;
252
253    osg::Geometry* quad_geometry = 0;
254    osg::Geometry* line_geometry = 0;
255    osg::Geometry* point_geometry = 0;
256   
257#if 0
258    quad_geometry = new PrecipitationGeometry;
259    quad_geometry->setUseVertexBufferObjects(true);
260    quad_geometry->setInitialBound(bb);
261    geode->addDrawable(quad_geometry);
262
263    osg::StateSet* quad_stateset = quad_geometry->getOrCreateStateSet();
264    {
265        osg::Program* program = new osg::Program;
266        quad_stateset->setAttribute(program);
267
268        // get shaders from source
269        program->addShader(osg::Shader::readShaderFile(osg::Shader::VERTEX, osgDB::findDataFile("quad_rain.vert")));
270        program->addShader(osg::Shader::readShaderFile(osg::Shader::FRAGMENT, osgDB::findDataFile("rain.frag")));
271    }
272#endif
273
274#if 0   
275    line_geometry = new PrecipitationGeometry;
276    line_geometry->setUseVertexBufferObjects(true);
277    line_geometry->setInitialBound(bb);
278    geode->addDrawable(line_geometry);
279
280    osg::StateSet* line_stateset = line_geometry->getOrCreateStateSet();
281    {
282        osg::Program* program = new osg::Program;
283        line_stateset->setAttribute(program);
284
285        // get shaders from source
286        program->addShader(osg::Shader::readShaderFile(osg::Shader::VERTEX, osgDB::findDataFile("line_rain.vert")));
287        program->addShader(osg::Shader::readShaderFile(osg::Shader::FRAGMENT, osgDB::findDataFile("rain.frag")));
288    }
289#endif
290
291
292#if 1   
293    point_geometry = new PrecipitationGeometry;
294    point_geometry->setUseVertexBufferObjects(true);
295    point_geometry->setInitialBound(bb);
296    geode->addDrawable(point_geometry);
297
298    osg::StateSet* point_stateset = point_geometry->getOrCreateStateSet();
299    {
300        osg::Program* program = new osg::Program;
301        point_stateset->setAttribute(program);
302
303        // get shaders from source
304        program->addShader(osg::Shader::readShaderFile(osg::Shader::VERTEX, osgDB::findDataFile("point_rain.vert")));
305        program->addShader(osg::Shader::readShaderFile(osg::Shader::FRAGMENT, osgDB::findDataFile("rain.frag")));
306
307        /// Setup the point sprites
308        osg::PointSprite *sprite = new osg::PointSprite();
309        point_stateset->setTextureAttributeAndModes(0, sprite, osg::StateAttribute::ON);
310
311        point_stateset->setMode(GL_VERTEX_PROGRAM_POINT_SIZE, osg::StateAttribute::ON);
312
313    }
314#endif
315
316
317    createGeometry(numParticles, quad_geometry, line_geometry, point_geometry);
318
319
320    // set up state.
321    osg::StateSet* stateset = geode->getOrCreateStateSet();
322    {
323        // time taken to get from start to the end of cycle
324        float period = fabs((bb.zMax()-bb.zMin()) / velocity.z());
325
326        // distance between start point and end of cyclce
327        osg::Vec3 position(bb.xMin(), bb.yMin(), bb.zMax());
328        osg::Vec3 dv_i( bb.xMax()-bb.xMin(), 0.0f, 0.0f );
329        osg::Vec3 dv_j( 0.0f, bb.yMax()-bb.yMin(), 0.0f );
330        osg::Vec3 dv_k( velocity * period );
331
332        // set up uniforms
333        osg::Uniform* position_Uniform = new osg::Uniform("position",position);
334        osg::Uniform* dv_i_Uniform = new osg::Uniform("dv_i",dv_i);
335        osg::Uniform* dv_j_Uniform = new osg::Uniform("dv_j",dv_j);
336        osg::Uniform* dv_k_Uniform = new osg::Uniform("dv_k",dv_k);
337       
338        osg::Uniform* inversePeriodUniform = new osg::Uniform("inversePeriod",1.0f/period);
339        osg::Uniform* startTime = new osg::Uniform("startTime",0.0f);
340
341
342        stateset->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
343        stateset->setMode(GL_BLEND, osg::StateAttribute::ON);
344       
345        stateset->addUniform(position_Uniform);
346        stateset->addUniform(dv_i_Uniform);
347        stateset->addUniform(dv_j_Uniform);
348        stateset->addUniform(dv_k_Uniform);
349        stateset->addUniform(inversePeriodUniform);
350        stateset->addUniform(startTime);
351        stateset->addUniform(new osg::Uniform("particleColour", osg::Vec4(0.6,0.6,0.6,1.0)));
352       
353        osg::Uniform* baseTextureSampler = new osg::Uniform("baseTexture",0);
354        stateset->addUniform(baseTextureSampler);
355       
356//        osg::Texture2D* texture = new osg::Texture2D(osgDB::readImageFile("Images/particle.rgb"));
357        osg::Texture2D* texture = new osg::Texture2D(createSpotLightImage(osg::Vec4(1.0f,1.0f,1.0f,1.0f),osg::Vec4(1.0f,1.0f,1.0f,0.0f),32,1.0));
358//        texture->setFilter(osg::Texture2D::MIN_FILTER,osg::Texture2D::LINEAR);
359        stateset->setTextureAttribute(0, texture);
360
361        // make it render after the normal transparent bin
362        stateset->setRenderBinDetails(11,"DepthSortedBin");
363
364        osg::Uniform* previousModelViewUniform = new osg::Uniform("previousModelViewMatrix",osg::Matrix());
365        stateset->addUniform(previousModelViewUniform);
366        geode->setCullCallback(new CullCallback(previousModelViewUniform));
367
368    }
369   
370
371
372
373
374    return geode;
375}
376/*
377osg::Node* createSnowEffect(const osg::BoundingBox& bb, const osg::Vec3& velocity, unsigned int numParticles, bool useShaders)
378{
379    osg::Geometry* geometry = new osg::Geometry;
380
381    osg::StateSet* stateset = geometry->getOrCreateStateSet();
382
383    // set up geometry.
384    {
385   
386        // per vertex properties
387        osg::Vec3Array* vertices = new osg::Vec3Array(numParticles);
388        osg::FloatArray* offsets = new osg::FloatArray(numParticles);
389       
390        for(unsigned int i=0; i< numParticles; ++i)
391        {
392            (*vertices)[i].set(random(bb.xMin(), bb.xMax()), random(bb.yMin(),bb.yMax()), bb.zMax());
393            (*offsets)[i] = random(0.0, 1.0);
394        }
395
396        geometry->setVertexArray(vertices);
397        geometry->setTexCoordArray(0, offsets);
398
399        // overall attributes
400        osg::Vec4Array* colours = new osg::Vec4Array(1);
401        (*colours)[0].set(1.0f,1.0f,1.0f,1.0f);
402        geometry->setColorArray(colours);
403        geometry->setColorBinding(osg::Geometry::BIND_OVERALL);
404       
405        geometry->addPrimitiveSet(new osg::DrawArrays(GL_POINTS, 0, numParticles));
406    }
407
408    // set up state.
409    {
410        // time taken to get from start to the end of cycle
411        float period = fabs((bb.zMax()-bb.zMin()) / velocity.z());
412
413        // distance between start point and end of cyclce
414        osg::Vec3 delta = velocity * period;
415
416        // set up uniforms
417        osg::Uniform* deltaUniform = new osg::Uniform("delta",delta);
418        osg::Uniform* inversePeriodUniform = new osg::Uniform("inversePeriod",1.0f/period);
419        osg::Uniform* startTime = new osg::Uniform("startTime",0.0f);
420
421        osg::Program* program = new osg::Program;
422        stateset->setAttribute(program);
423
424        // get shaders from source
425        program->addShader(osg::Shader::readShaderFile(osg::Shader::VERTEX, osgDB::findDataFile("snow.vert")));
426        program->addShader(osg::Shader::readShaderFile(osg::Shader::FRAGMENT, osgDB::findDataFile("snow.frag")));
427
428        stateset->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
429       
430        stateset->addUniform(deltaUniform);
431        stateset->addUniform(inversePeriodUniform);
432        stateset->addUniform(startTime);
433    }
434   
435    geometry->setInitialBound(bb);
436
437
438    osg::Geode* geode = new osg::Geode;
439    geode->addDrawable(geometry);
440
441    return geode;
442}
443*/
444osg::Node* createModel(osg::Node* loadedModel, bool useShaders)
445{
446    osg::Group* group = new osg::Group;
447
448    osg::BoundingBox bb(0.0, 0.0, 0.0, 100.0, 100.0, 100.0);
449    osg::Vec3 velocity(0.0,2.0,-5.0);
450    unsigned int numParticles = 150000;
451   
452    if (loadedModel)
453    {
454        group->addChild(loadedModel);
455       
456        osg::BoundingSphere bs = loadedModel->getBound();
457
458        bb.set( -100, -100, 0, +100, +100, 10);
459       
460        osg::StateSet* stateset = loadedModel->getOrCreateStateSet();
461       
462        osg::Fog* fog = new osg::Fog;
463        fog->setMode(osg::Fog::LINEAR);
464        fog->setDensity(0.1f);
465        fog->setStart(0.0f);
466        fog->setEnd(1000.0f);
467        fog->setColor(osg::Vec4(0.5f,0.5f,0.5f,1.0f));
468        stateset->setAttributeAndModes(fog, osg::StateAttribute::ON);
469       
470        osg::LightSource* lightSource = new osg::LightSource;
471        group->addChild(lightSource);
472
473        osg::Light* light = lightSource->getLight();
474        light->setLightNum(0);
475        light->setPosition(osg::Vec4(0.0f,0.0f,1.0f,0.0f)); // directional light from above
476        light->setAmbient(osg::Vec4(0.8f,0.8f,0.8f,1.0f));
477        light->setDiffuse(osg::Vec4(0.2f,0.2f,0.2f,1.0f));
478        light->setSpecular(osg::Vec4(0.2f,0.2f,0.2f,1.0f));
479
480               
481    }
482
483    group->addChild(createRainEffect(bb, velocity, numParticles, useShaders));
484
485    return group;   
486}
487
488int main( int argc, char **argv )
489{
490
491    // use an ArgumentParser object to manage the program arguments.
492    osg::ArgumentParser arguments(&argc,argv);
493   
494    // set up the usage document, in case we need to print out how to use this program.
495    arguments.getApplicationUsage()->setApplicationName(arguments.getApplicationName());
496    arguments.getApplicationUsage()->setDescription(arguments.getApplicationName()+" example provides an interactive viewer for visualising point clouds..");
497    arguments.getApplicationUsage()->setCommandLineUsage(arguments.getApplicationName()+" [options] filename ...");
498    arguments.getApplicationUsage()->addCommandLineOption("-h or --help","Display this information");
499    arguments.getApplicationUsage()->addCommandLineOption("--shader","Use GLSL shaders.");
500    arguments.getApplicationUsage()->addCommandLineOption("--fixed","Use fixed function pipeline.");
501   
502
503    // construct the viewer.
504    osgProducer::Viewer viewer(arguments);
505
506    // set up the value with sensible default event handlers.
507    viewer.setUpViewer(osgProducer::Viewer::STANDARD_SETTINGS);
508
509    // get details on keyboard and mouse bindings used by the viewer.
510    viewer.getUsage(*arguments.getApplicationUsage());
511
512    bool shader = true;
513    while (arguments.read("--shader")) shader = true;
514    while (arguments.read("--fixed")) shader = false;
515
516    // if user request help write it out to cout.
517    if (arguments.read("-h") || arguments.read("--help"))
518    {
519        arguments.getApplicationUsage()->write(std::cout);
520        return 1;
521    }
522
523    // any option left unread are converted into errors to write out later.
524    arguments.reportRemainingOptionsAsUnrecognized();
525
526    // report any errors if they have occured when parsing the program aguments.
527    if (arguments.errors())
528    {
529        arguments.writeErrorMessages(std::cout);
530        return 1;
531    }
532   
533    osg::Timer timer;
534    osg::Timer_t start_tick = timer.tick();
535
536    // read the scene from the list of file specified commandline args.
537    osg::ref_ptr<osg::Node> loadedModel = osgDB::readNodeFiles(arguments);
538   
539    loadedModel = createModel(loadedModel.get(), shader);
540
541    // if no model has been successfully loaded report failure.
542    if (!loadedModel)
543    {
544        std::cout << arguments.getApplicationName() <<": No data loaded" << std::endl;
545        return 1;
546    }
547
548    osg::Timer_t end_tick = timer.tick();
549
550    std::cout << "Time to load = "<<timer.delta_s(start_tick,end_tick)<<std::endl;
551
552    // optimize the scene graph, remove rendundent nodes and state etc.
553    osgUtil::Optimizer optimizer;
554    optimizer.optimize(loadedModel.get());
555
556    // set the scene to render
557    viewer.setSceneData(loadedModel.get());
558
559    // create the windows and run the threads.
560    viewer.realize();
561
562    while( !viewer.done() )
563    {
564        // wait for all cull and draw threads to complete.
565        viewer.sync();
566
567        // update the scene by traversing it with the the update visitor which will
568        // call all node update callbacks and animations.
569        viewer.update();
570         
571        // fire off the cull and draw traversals of the scene.
572        viewer.frame();
573       
574    }
575   
576    // wait for all cull and draw threads to complete before exit.
577    viewer.sync();
578
579    return 0;
580}
Note: See TracBrowser for help on using the browser.