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

Revision 5086, 20.7 kB (checked in by robert, 9 years ago)

Implement positioning of cells via vertex attribute.

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