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

Revision 5085, 21.2 kB (checked in by robert, 9 years ago)

Moved common uniforms to top of precipitation subgraph.

Added a run of the SpatializeGroupVisitor? to create a balanced subgraph.

Changed the quad to particle transistion distance from 100 to 50m.

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