root/OpenSceneGraph/trunk/examples/osgvolume/osgvolume.cpp @ 4501

Revision 4501, 61.3 kB (checked in by robert, 9 years ago)

Integrated shaders into source.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1#include <osg/Node>
2#include <osg/Geometry>
3#include <osg/Notify>
4#include <osg/Texture3D>
5#include <osg/TexGen>
6#include <osg/Geode>
7#include <osg/Billboard>
8#include <osg/PositionAttitudeTransform>
9#include <osg/ClipNode>
10#include <osg/AlphaFunc>
11#include <osg/TexGenNode>
12#include <osg/TexEnv>
13#include <osg/TexEnvCombine>
14#include <osg/Material>
15#include <osg/PrimitiveSet>
16#include <osg/Endian>
17
18#include <osgDB/Registry>
19#include <osgDB/ReadFile>
20#include <osgDB/WriteFile>
21#include <osgDB/FileUtils>
22#include <osgDB/FileNameUtils>
23
24#include <osgUtil/CullVisitor>
25
26#include <osgProducer/Viewer>
27
28
29typedef std::vector< osg::ref_ptr<osg::Image> > ImageList;
30
31//  example ReadOperator
32// struct ReadOperator
33// {
34//     inline void luminance(float l) const { rgba(l,l,l,1.0f); }
35//     inline void alpha(float a) const { rgba(1.0f,1.0f,1.0f,a); }
36//     inline void luminance_alpha(float l,float a) const { rgba(l,l,l,a); }
37//     inline void rgb(float r,float g,float b) const { rgba(r,g,b,1.0f); }
38//     inline void rgba(float r,float g,float b,float a) const { std::cout<<"pixel("<<r<<", "<<g<<", "<<b<<", "<<a<<")"<<std::endl; }
39// };
40
41
42template <typename T, class O>   
43void _readRow(unsigned int num, GLenum pixelFormat, T* data,float scale, const O& operation)
44{
45    switch(pixelFormat)
46    {
47        case(GL_LUMINANCE):         { for(unsigned int i=0;i<num;++i) { float l = float(*data++)*scale; operation.luminance(l); } }  break;
48        case(GL_ALPHA):             { for(unsigned int i=0;i<num;++i) { float a = float(*data++)*scale; operation.alpha(a); } }  break;
49        case(GL_LUMINANCE_ALPHA):   { for(unsigned int i=0;i<num;++i) { float l = float(*data++)*scale; float a = float(*data++)*scale; operation.luminance_alpha(l,a); } }  break;
50        case(GL_RGB):               { for(unsigned int i=0;i<num;++i) { float r = float(*data++)*scale; float g = float(*data++)*scale; float b = float(*data++)*scale; operation.rgb(r,g,b); } }  break;
51        case(GL_RGBA):              { for(unsigned int i=0;i<num;++i) { float r = float(*data++)*scale; float g = float(*data++)*scale; float b = float(*data++)*scale; float a = float(*data++)*scale; operation.rgba(r,g,b,a); } }  break;
52        case(GL_BGR):               { for(unsigned int i=0;i<num;++i) { float b = float(*data++)*scale; float g = float(*data++)*scale; float r = float(*data++)*scale; operation.rgb(r,g,b); } }  break;
53        case(GL_BGRA):              { for(unsigned int i=0;i<num;++i) { float b = float(*data++)*scale; float g = float(*data++)*scale; float r = float(*data++)*scale; float a = float(*data++)*scale; operation.rgba(r,g,b,a); } }  break;
54    }
55}
56
57template <class O>   
58void readRow(unsigned int num, GLenum pixelFormat, GLenum dataType, unsigned char* data, const O& operation)
59{
60    switch(dataType)
61    {
62        case(GL_BYTE):              _readRow(num,pixelFormat, (char*)data,            1.0f/128.0f,        operation); break;
63        case(GL_UNSIGNED_BYTE):     _readRow(num,pixelFormat, (unsigned char*)data,   1.0f/255.0f,        operation); break;
64        case(GL_SHORT):             _readRow(num,pixelFormat, (short*) data,          1.0f/32768.0f,      operation); break;
65        case(GL_UNSIGNED_SHORT):    _readRow(num,pixelFormat, (unsigned short*)data,  1.0f/65535.0f,      operation); break;
66        case(GL_INT):               _readRow(num,pixelFormat, (int*) data,            1.0f/2147483648.0f, operation); break;
67        case(GL_UNSIGNED_INT):      _readRow(num,pixelFormat, (unsigned int*) data,   1.0f/4294967295.0f, operation); break;
68        case(GL_FLOAT):             _readRow(num,pixelFormat, (float*) data,          1.0f,               operation); break;
69    }
70}
71
72template <class O>   
73void readImage(osg::Image* image, const O& operation)
74{
75    if (!image) return;
76   
77    for(int r=0;r<image->r();++r)
78    {
79        for(int t=0;t<image->t();++t)
80        {
81            readRow(image->s(), image->getPixelFormat(), image->getDataType(), image->data(0,t,r), operation);
82        }
83    }
84}
85
86//  example ModifyOperator
87// struct ModifyOperator
88// {
89//     inline void luminance(float& l) const {}
90//     inline void alpha(float& a) const {}
91//     inline void luminance_alpha(float& l,float& a) const {}
92//     inline void rgb(float& r,float& g,float& b) const {}
93//     inline void rgba(float& r,float& g,float& b,float& a) const {}
94// };
95
96
97template <typename T, class M>   
98void _modifyRow(unsigned int num, GLenum pixelFormat, T* data,float scale, const M& operation)
99{
100    float inv_scale = 1.0f/scale;
101    switch(pixelFormat)
102    {
103        case(GL_LUMINANCE):         { for(unsigned int i=0;i<num;++i) { float l = float(*data)*scale; operation.luminance(l); *data++ = T(l*inv_scale); } }  break;
104        case(GL_ALPHA):             { for(unsigned int i=0;i<num;++i) { float a = float(*data)*scale; operation.alpha(a); *data++ = T(a*inv_scale); } }  break;
105        case(GL_LUMINANCE_ALPHA):   { for(unsigned int i=0;i<num;++i) { float l = float(*data)*scale; float a = float(*(data+1))*scale; operation.luminance_alpha(l,a); *data++ = T(l*inv_scale); *data++ = T(a*inv_scale); } }  break;
106        case(GL_RGB):               { for(unsigned int i=0;i<num;++i) { float r = float(*data)*scale; float g = float(*(data+1))*scale; float b = float(*(data+2))*scale; operation.rgb(r,g,b); *data++ = T(r*inv_scale); *data++ = T(g*inv_scale); *data++ = T(b*inv_scale); } }  break;
107        case(GL_RGBA):              { for(unsigned int i=0;i<num;++i) { float r = float(*data)*scale; float g = float(*(data+1))*scale; float b = float(*(data+2))*scale; float a = float(*(data+3))*scale; operation.rgba(r,g,b,a); *data++ = T(r*inv_scale); *data++ = T(g*inv_scale); *data++ = T(g*inv_scale); *data++ = T(a*inv_scale); } }  break;
108        case(GL_BGR):               { for(unsigned int i=0;i<num;++i) { float b = float(*data)*scale; float g = float(*(data+1))*scale; float r = float(*(data+2))*scale; operation.rgb(r,g,b); *data++ = T(b*inv_scale); *data++ = T(g*inv_scale); *data++ = T(r*inv_scale); } }  break;
109        case(GL_BGRA):              { for(unsigned int i=0;i<num;++i) { float b = float(*data)*scale; float g = float(*(data+1))*scale; float r = float(*(data+2))*scale; float a = float(*(data+3))*scale; operation.rgba(r,g,b,a); *data++ = T(g*inv_scale); *data++ = T(b*inv_scale); *data++ = T(r*inv_scale); *data++ = T(a*inv_scale); } }  break;
110    }
111}
112
113template <class M>   
114void modifyRow(unsigned int num, GLenum pixelFormat, GLenum dataType, unsigned char* data, const M& operation)
115{
116    switch(dataType)
117    {
118        case(GL_BYTE):              _modifyRow(num,pixelFormat, (char*)data,            1.0f/128.0f,        operation); break;
119        case(GL_UNSIGNED_BYTE):     _modifyRow(num,pixelFormat, (unsigned char*)data,   1.0f/255.0f,        operation); break;
120        case(GL_SHORT):             _modifyRow(num,pixelFormat, (short*) data,          1.0f/32768.0f,      operation); break;
121        case(GL_UNSIGNED_SHORT):    _modifyRow(num,pixelFormat, (unsigned short*)data,  1.0f/65535.0f,      operation); break;
122        case(GL_INT):               _modifyRow(num,pixelFormat, (int*) data,            1.0f/2147483648.0f, operation); break;
123        case(GL_UNSIGNED_INT):      _modifyRow(num,pixelFormat, (unsigned int*) data,   1.0f/4294967295.0f, operation); break;
124        case(GL_FLOAT):             _modifyRow(num,pixelFormat, (float*) data,          1.0f,               operation); break;
125    }
126}
127
128template <class M>   
129void modifyImage(osg::Image* image, const M& operation)
130{
131    if (!image) return;
132   
133    for(int r=0;r<image->r();++r)
134    {
135        for(int t=0;t<image->t();++t)
136        {
137            modifyRow(image->s(), image->getPixelFormat(), image->getDataType(), image->data(0,t,r), operation);
138        }
139    }
140}
141
142struct PassThroughTransformFunction
143{
144    unsigned char operator() (unsigned char c) const { return c; }
145};
146
147
148struct ProcessRow
149{
150    virtual void operator() (unsigned int num,
151                    GLenum source_pixelFormat, unsigned char* source,
152                    GLenum dest_pixelFormat, unsigned char* dest) const 
153    {
154        switch(source_pixelFormat)
155        {
156        case(GL_LUMINANCE):
157        case(GL_ALPHA):
158            switch(dest_pixelFormat)
159            {
160            case(GL_LUMINANCE):
161            case(GL_ALPHA): A_to_A(num, source, dest); break;
162            case(GL_LUMINANCE_ALPHA): A_to_LA(num, source, dest); break;
163            case(GL_RGB): A_to_RGB(num, source, dest); break;
164            case(GL_RGBA): A_to_RGBA(num, source, dest); break;
165            }
166            break;
167        case(GL_LUMINANCE_ALPHA):
168            switch(dest_pixelFormat)
169            {
170            case(GL_LUMINANCE):
171            case(GL_ALPHA): LA_to_A(num, source, dest); break;
172            case(GL_LUMINANCE_ALPHA): LA_to_LA(num, source, dest); break;
173            case(GL_RGB): LA_to_RGB(num, source, dest); break;
174            case(GL_RGBA): LA_to_RGBA(num, source, dest); break;
175            }
176            break;
177        case(GL_RGB):
178            switch(dest_pixelFormat)
179            {
180            case(GL_LUMINANCE):
181            case(GL_ALPHA): RGB_to_A(num, source, dest); break;
182            case(GL_LUMINANCE_ALPHA): RGB_to_LA(num, source, dest); break;
183            case(GL_RGB): RGB_to_RGB(num, source, dest); break;
184            case(GL_RGBA): RGB_to_RGBA(num, source, dest); break;
185            }
186            break;
187        case(GL_RGBA):
188            switch(dest_pixelFormat)
189            {
190            case(GL_LUMINANCE):
191            case(GL_ALPHA): RGBA_to_A(num, source, dest); break;
192            case(GL_LUMINANCE_ALPHA): RGBA_to_LA(num, source, dest); break;
193            case(GL_RGB): RGBA_to_RGB(num, source, dest); break;
194            case(GL_RGBA): RGBA_to_RGBA(num, source, dest); break;
195            }
196            break;
197        }
198    }
199
200    ///////////////////////////////////////////////////////////////////////////////
201    // alpha sources..   
202    virtual void A_to_A(unsigned int num, unsigned char* source, unsigned char* dest) const
203    {
204        for(unsigned int i=0;i<num;++i)
205        {
206            *dest++ = *source++;
207        }
208    }
209
210    virtual void A_to_LA(unsigned int num, unsigned char* source, unsigned char* dest) const
211    {
212        for(unsigned int i=0;i<num;++i)
213        {
214            *dest++ = *source;
215            *dest++ = *source++;
216        }
217    }
218                   
219    virtual void A_to_RGB(unsigned int num, unsigned char* source, unsigned char* dest) const
220    {
221        for(unsigned int i=0;i<num;++i)
222        {
223            *dest++ = *source;
224            *dest++ = *source;
225            *dest++ = *source++;
226        }
227    }
228
229    virtual void A_to_RGBA(unsigned int num, unsigned char* source, unsigned char* dest) const
230    {
231        for(unsigned int i=0;i<num;++i)
232        {
233            *dest++ = *source;
234            *dest++ = *source;
235            *dest++ = *source;
236            *dest++ = *source++;
237        }
238    }
239
240    ///////////////////////////////////////////////////////////////////////////////
241    // alpha luminiance sources..   
242    virtual void LA_to_A(unsigned int num, unsigned char* source, unsigned char* dest) const
243    {
244        for(unsigned int i=0;i<num;++i)
245        {
246            ++source;
247            *dest++ = *source++;
248        }
249    }
250
251    virtual void LA_to_LA(unsigned int num, unsigned char* source, unsigned char* dest) const
252    {
253        for(unsigned int i=0;i<num;++i)
254        {
255            *dest++ = *source++;
256            *dest++ = *source++;
257        }
258    }
259                   
260    virtual void LA_to_RGB(unsigned int num, unsigned char* source, unsigned char* dest) const
261    {
262        for(unsigned int i=0;i<num;++i)
263        {
264            *dest++ = *source;
265            *dest++ = *source;
266            *dest++ = *source;
267            source+=2;
268        }
269    }
270
271    virtual void LA_to_RGBA(unsigned int num, unsigned char* source, unsigned char* dest) const
272    {
273        for(unsigned int i=0;i<num;++i)
274        {
275            *dest++ = *source;
276            *dest++ = *source;
277            *dest++ = *source++;
278            *dest++ = *source++;
279        }
280    }
281
282    ///////////////////////////////////////////////////////////////////////////////
283    // RGB sources..   
284    virtual void RGB_to_A(unsigned int num, unsigned char* source, unsigned char* dest) const
285    {
286        for(unsigned int i=0;i<num;++i)
287        {
288            unsigned char val = *source;
289            *dest++ = val;
290            source += 3;
291        }
292    }
293
294    virtual void RGB_to_LA(unsigned int num, unsigned char* source, unsigned char* dest) const
295    {
296        for(unsigned int i=0;i<num;++i)
297        {
298            unsigned char val = *source;
299            *dest++ = val;
300            *dest++ = val;
301            source += 3;
302        }
303    }
304                   
305    virtual void RGB_to_RGB(unsigned int num, unsigned char* source, unsigned char* dest) const
306    {
307        for(unsigned int i=0;i<num;++i)
308        {
309            *dest++ = *source++;
310            *dest++ = *source++;
311            *dest++ = *source++;
312        }
313    }
314
315    virtual void RGB_to_RGBA(unsigned int num, unsigned char* source, unsigned char* dest) const
316    {
317        for(unsigned int i=0;i<num;++i)
318        {
319            unsigned char val = *source;
320            *dest++ = *source++;
321            *dest++ = *source++;
322            *dest++ = *source++;
323            *dest++ = val;
324        }
325    }
326
327    ///////////////////////////////////////////////////////////////////////////////
328    // RGBA sources..   
329    virtual void RGBA_to_A(unsigned int num, unsigned char* source, unsigned char* dest) const
330    {
331        for(unsigned int i=0;i<num;++i)
332        {
333            source += 3;
334            *dest++ = *source++;
335        }
336    }
337
338    virtual void RGBA_to_LA(unsigned int num, unsigned char* source, unsigned char* dest) const
339    {
340        for(unsigned int i=0;i<num;++i)
341        {
342            unsigned char val = *source;
343            source += 3;
344            *dest++ = val;
345            *dest++ = *source++;
346        }
347    }
348                   
349    virtual void RGBA_to_RGB(unsigned int num, unsigned char* source, unsigned char* dest) const
350    {
351        for(unsigned int i=0;i<num;++i)
352        {
353            *dest++ = *source++;
354            *dest++ = *source++;
355            *dest++ = *source++;
356            ++source;
357        }
358    }
359
360    virtual void RGBA_to_RGBA(unsigned int num, unsigned char* source, unsigned char* dest) const
361    {
362        for(unsigned int i=0;i<num;++i)
363        {
364            *dest++ = *source++;
365            *dest++ = *source++;
366            *dest++ = *source++;
367            *dest++ = *source++;
368        }
369    }
370};
371
372
373void clampToNearestValidPowerOfTwo(int& sizeX, int& sizeY, int& sizeZ, int s_maximumTextureSize, int t_maximumTextureSize, int r_maximumTextureSize)
374{
375    // compute nearest powers of two for each axis.
376    int s_nearestPowerOfTwo = 1;
377    while(s_nearestPowerOfTwo<sizeX && s_nearestPowerOfTwo<s_maximumTextureSize) s_nearestPowerOfTwo*=2;
378
379    int t_nearestPowerOfTwo = 1;
380    while(t_nearestPowerOfTwo<sizeY && t_nearestPowerOfTwo<t_maximumTextureSize) t_nearestPowerOfTwo*=2;
381
382    int r_nearestPowerOfTwo = 1;
383    while(r_nearestPowerOfTwo<sizeZ && r_nearestPowerOfTwo<r_maximumTextureSize) r_nearestPowerOfTwo*=2;
384
385    sizeX = s_nearestPowerOfTwo;
386    sizeY = t_nearestPowerOfTwo;
387    sizeZ = r_nearestPowerOfTwo;
388}
389
390osg::Image* createTexture3D(ImageList& imageList, ProcessRow& processRow,
391            unsigned int numComponentsDesired,
392            int s_maximumTextureSize,
393            int t_maximumTextureSize,
394            int r_maximumTextureSize )
395{
396    int max_s = 0;
397    int max_t = 0;
398    unsigned int max_components = 0;
399    int total_r = 0;
400    ImageList::iterator itr;
401    for(itr=imageList.begin();
402        itr!=imageList.end();
403        ++itr)
404    {
405        osg::Image* image = itr->get();
406        GLenum pixelFormat = image->getPixelFormat();
407        if (pixelFormat==GL_ALPHA ||
408            pixelFormat==GL_LUMINANCE ||
409            pixelFormat==GL_LUMINANCE_ALPHA ||
410            pixelFormat==GL_RGB ||
411            pixelFormat==GL_RGBA)
412        {
413            max_s = osg::maximum(image->s(), max_s);
414            max_t = osg::maximum(image->t(), max_t);
415            max_components = osg::maximum(osg::Image::computeNumComponents(pixelFormat), max_components);
416            total_r += image->r();
417        }
418        else
419        {
420            osg::notify(osg::NOTICE)<<"Image "<<image->getFileName()<<" has unsuitable pixel format"<< std::hex<< pixelFormat << std::dec << std::endl;
421        }
422    }
423   
424    if (numComponentsDesired!=0) max_components = numComponentsDesired;
425   
426    GLenum desiredPixelFormat = 0;
427    switch(max_components)
428    {
429    case(1):
430        osg::notify(osg::NOTICE)<<"desiredPixelFormat = GL_LUMINANCE" << std::endl;
431        desiredPixelFormat = GL_LUMINANCE;
432        break;
433    case(2):
434        osg::notify(osg::NOTICE)<<"desiredPixelFormat = GL_LUMINANCE_ALPHA" << std::endl;
435        desiredPixelFormat = GL_LUMINANCE_ALPHA;
436        break;
437    case(3):
438        osg::notify(osg::NOTICE)<<"desiredPixelFormat = GL_RGB" << std::endl;
439        desiredPixelFormat = GL_RGB;
440        break;
441    case(4):
442        osg::notify(osg::NOTICE)<<"desiredPixelFormat = GL_RGBA" << std::endl;
443        desiredPixelFormat = GL_RGBA;
444        break;
445    }   
446    if (desiredPixelFormat==0) return 0;
447   
448    // compute nearest powers of two for each axis.
449    int s_nearestPowerOfTwo = 1;
450    while(s_nearestPowerOfTwo<max_s && s_nearestPowerOfTwo<s_maximumTextureSize) s_nearestPowerOfTwo*=2;
451
452    int t_nearestPowerOfTwo = 1;
453    while(t_nearestPowerOfTwo<max_t && t_nearestPowerOfTwo<t_maximumTextureSize) t_nearestPowerOfTwo*=2;
454
455    int r_nearestPowerOfTwo = 1;
456    while(r_nearestPowerOfTwo<total_r && r_nearestPowerOfTwo<r_maximumTextureSize) r_nearestPowerOfTwo*=2;
457
458
459    osg::notify(osg::NOTICE)<<"max image width = "<<max_s<<"  nearest power of two = "<<s_nearestPowerOfTwo<<std::endl;
460    osg::notify(osg::NOTICE)<<"max image height = "<<max_t<<"  nearest power of two = "<<t_nearestPowerOfTwo<<std::endl;
461    osg::notify(osg::NOTICE)<<"max image depth = "<<total_r<<"  nearest power of two = "<<r_nearestPowerOfTwo<<std::endl;
462   
463    // now allocate the 3d texture;
464    osg::ref_ptr<osg::Image> image_3d = new osg::Image;
465    image_3d->allocateImage(s_nearestPowerOfTwo,t_nearestPowerOfTwo,r_nearestPowerOfTwo,
466                            desiredPixelFormat,GL_UNSIGNED_BYTE);
467       
468
469    unsigned int r_offset = (total_r<r_nearestPowerOfTwo) ? r_nearestPowerOfTwo/2 - total_r/2 : 0;
470
471    int curr_dest_r = r_offset;
472
473    // copy across the values from the source images into the image_3d.
474    for(itr=imageList.begin();
475        itr!=imageList.end();
476        ++itr)
477    {
478        osg::Image* image = itr->get();
479        GLenum pixelFormat = image->getPixelFormat();
480        if (pixelFormat==GL_ALPHA ||
481            pixelFormat==GL_LUMINANCE ||
482            pixelFormat==GL_LUMINANCE_ALPHA ||
483            pixelFormat==GL_RGB ||
484            pixelFormat==GL_RGBA)
485        {
486       
487            int num_r = osg::minimum(image->r(), (image_3d->r() - curr_dest_r));
488            int num_t = osg::minimum(image->t(), image_3d->t());
489            int num_s = osg::minimum(image->s(), image_3d->s());
490       
491            unsigned int s_offset_dest = (image->s()<s_nearestPowerOfTwo) ? s_nearestPowerOfTwo/2 - image->s()/2 : 0;
492            unsigned int t_offset_dest = (image->t()<t_nearestPowerOfTwo) ? t_nearestPowerOfTwo/2 - image->t()/2 : 0;
493
494            for(int r=0;r<num_r;++r, ++curr_dest_r)
495            {
496                for(int t=0;t<num_t;++t)
497                {
498                    unsigned char* dest = image_3d->data(s_offset_dest,t+t_offset_dest,curr_dest_r);
499                    unsigned char* source = image->data(0,t,r);
500
501                    processRow(num_s, image->getPixelFormat(), source, image_3d->getPixelFormat(), dest);
502                }
503            }
504        }
505    }
506    return image_3d.release();
507}
508
509
510osg::Image* createNormalMapTexture(osg::Image* image_3d)
511{
512    unsigned int sourcePixelIncrement = 1;
513    unsigned int alphaOffset = 0;
514    switch(image_3d->getPixelFormat())
515    {
516    case(GL_ALPHA):
517    case(GL_LUMINANCE):
518        sourcePixelIncrement = 1;
519        alphaOffset = 0;
520        break;
521    case(GL_LUMINANCE_ALPHA):
522        sourcePixelIncrement = 2;
523        alphaOffset = 1;
524        break;
525    case(GL_RGB):
526        sourcePixelIncrement = 3;
527        alphaOffset = 0;
528        break;
529    case(GL_RGBA):
530        sourcePixelIncrement = 4;
531        alphaOffset = 3;
532        break;
533    default:
534        osg::notify(osg::NOTICE)<<"Source pixel format not support for normal map generation."<<std::endl;
535        return 0;
536    }
537   
538    osg::ref_ptr<osg::Image> normalmap_3d = new osg::Image;
539    normalmap_3d->allocateImage(image_3d->s(),image_3d->t(),image_3d->r(),
540                            GL_RGBA,GL_UNSIGNED_BYTE);
541
542    if (osg::getCpuByteOrder()==osg::LittleEndian) alphaOffset = sourcePixelIncrement-alphaOffset-1;
543
544    for(int r=1;r<image_3d->r()-1;++r)
545    {
546        for(int t=1;t<image_3d->t()-1;++t)
547        {
548            unsigned char* ptr = image_3d->data(1,t,r)+alphaOffset;
549            unsigned char* left = image_3d->data(0,t,r)+alphaOffset;
550            unsigned char* right = image_3d->data(2,t,r)+alphaOffset;
551            unsigned char* above = image_3d->data(1,t+1,r)+alphaOffset;
552            unsigned char* below = image_3d->data(1,t-1,r)+alphaOffset;
553            unsigned char* in = image_3d->data(1,t,r+1)+alphaOffset;
554            unsigned char* out = image_3d->data(1,t,r-1)+alphaOffset;
555
556            unsigned char* destination = (unsigned char*) normalmap_3d->data(1,t,r);
557
558            for(int s=1;s<image_3d->s()-1;++s)
559            {
560
561                osg::Vec3 grad((float)(*left)-(float)(*right),
562                               (float)(*below)-(float)(*above),
563                               (float)(*out) -(float)(*in));
564
565                grad.normalize();
566
567                if (grad.x()==0.0f && grad.y()==0.0f && grad.z()==0.0f)
568                {
569                    grad.set(128.0f,128.0f,128.0f);
570                }
571                else
572                {
573                    grad.x() = osg::clampBetween((grad.x()+1.0f)*128.0f,0.0f,255.0f);
574                    grad.y() = osg::clampBetween((grad.y()+1.0f)*128.0f,0.0f,255.0f);
575                    grad.z() = osg::clampBetween((grad.z()+1.0f)*128.0f,0.0f,255.0f);
576                }
577
578                *(destination++) = (unsigned char)(grad.x()); // scale and bias X.
579                *(destination++) = (unsigned char)(grad.y()); // scale and bias Y.
580                *(destination++) = (unsigned char)(grad.z()); // scale and bias Z.
581
582                *destination++ = *ptr;
583
584                ptr += sourcePixelIncrement;
585                left += sourcePixelIncrement;
586                right += sourcePixelIncrement;
587                above += sourcePixelIncrement;
588                below += sourcePixelIncrement;
589                in += sourcePixelIncrement;
590                out += sourcePixelIncrement;
591            }
592        }
593    }
594   
595    return normalmap_3d.release();
596}
597
598
599
600osg::Node* createCube(float size,float alpha, unsigned int numSlices, float sliceEnd=1.0f)
601{
602
603    // set up the Geometry.
604    osg::Geometry* geom = new osg::Geometry;
605
606    float halfSize = size*0.5f;
607    float y = halfSize;
608    float dy =-size/(float)(numSlices-1)*sliceEnd;
609
610    //y = -halfSize;
611    //dy *= 0.5;
612
613    osg::Vec3Array* coords = new osg::Vec3Array(4*numSlices);
614    geom->setVertexArray(coords);
615    for(unsigned int i=0;i<numSlices;++i, y+=dy)
616    {
617        (*coords)[i*4+0].set(-halfSize,y,halfSize);
618        (*coords)[i*4+1].set(-halfSize,y,-halfSize);
619        (*coords)[i*4+2].set(halfSize,y,-halfSize);
620        (*coords)[i*4+3].set(halfSize,y,halfSize);
621    }
622   
623    osg::Vec3Array* normals = new osg::Vec3Array(1);
624    (*normals)[0].set(0.0f,-1.0f,0.0f);
625    geom->setNormalArray(normals);
626    geom->setNormalBinding(osg::Geometry::BIND_OVERALL);
627
628    osg::Vec4Array* colors = new osg::Vec4Array(1);
629    (*colors)[0].set(1.0f,1.0f,1.0f,alpha);
630    geom->setColorArray(colors);
631    geom->setColorBinding(osg::Geometry::BIND_OVERALL);
632
633    geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS,0,coords->size()));
634
635    osg::Billboard* billboard = new osg::Billboard;
636    billboard->setMode(osg::Billboard::POINT_ROT_WORLD);
637    billboard->addDrawable(geom);
638    billboard->setPosition(0,osg::Vec3(0.0f,0.0f,0.0f));
639   
640    return billboard;
641}
642
643osg::Node* createShaderModel(osg::ref_ptr<osg::Image>& image_3d, osg::ref_ptr<osg::Image>& normalmap_3d,
644                       osg::Texture::InternalFormatMode internalFormatMode,
645                       float xSize, float ySize, float zSize,
646                       float xMultiplier, float yMultiplier, float zMultiplier,
647                       unsigned int numSlices=500, float sliceEnd=1.0f, float alphaFuncValue=0.02f)
648{
649    osg::Geode* geode = new osg::Geode;
650    osg::StateSet* stateset = geode->getOrCreateStateSet();
651   
652    // set up the 3d texture itself,
653    // note, well set the filtering up so that mip mapping is disabled,
654    // gluBuild3DMipsmaps doesn't do a very good job of handled the
655    // inbalanced dimensions of the 256x256x4 texture.
656    osg::Texture3D* texture3D = new osg::Texture3D;
657    texture3D->setFilter(osg::Texture3D::MIN_FILTER,osg::Texture3D::LINEAR);
658    texture3D->setFilter(osg::Texture3D::MAG_FILTER,osg::Texture3D::LINEAR);
659    texture3D->setWrap(osg::Texture3D::WRAP_R,osg::Texture3D::CLAMP);
660    texture3D->setWrap(osg::Texture3D::WRAP_S,osg::Texture3D::CLAMP);
661    texture3D->setWrap(osg::Texture3D::WRAP_T,osg::Texture3D::CLAMP);
662    if (image_3d->getPixelFormat()==GL_ALPHA ||
663        image_3d->getPixelFormat()==GL_LUMINANCE)
664    {
665        texture3D->setInternalFormatMode(osg::Texture3D::USE_USER_DEFINED_FORMAT);
666        texture3D->setInternalFormat(GL_INTENSITY);
667    }
668    else
669    {
670        texture3D->setInternalFormatMode(internalFormatMode);
671    }
672
673    texture3D->setImage(image_3d.get());
674
675    stateset->setTextureAttributeAndModes(0,texture3D,osg::StateAttribute::ON);
676
677    osg::Program* program = new osg::Program;
678    stateset->setAttribute(program);
679
680    // get shaders from source
681    std::string vertexShaderFile = osgDB::findDataFile("volume.vert");
682    if (!vertexShaderFile.empty())
683    {
684        program->addShader(osg::Shader::readShaderFile(osg::Shader::VERTEX, vertexShaderFile));
685    }
686    else
687    {
688        char vertexShaderSource[] =
689            "varying vec3 texcoord;\n"
690            "varying vec3 deltaTexCoord;\n"
691            "\n"
692            "void main(void)\n"
693            "{\n"
694            "    texcoord = gl_MultiTexCoord0.xyz;\n"
695            "    gl_Position     = ftransform();  \n"
696            "    deltaTexCoord = normalize(gl_ModelViewMatrixInverse * vec4(0,0,0,1) - gl_Vertex);\n"
697            "}\n";
698
699        osg::Shader* vertex_shader = new osg::Shader(osg::Shader::VERTEX, vertexShaderSource);
700        program->addShader(vertex_shader);
701
702    }
703   
704    std::string fragmentShaderFile = osgDB::findDataFile("volume.frag");
705    if (!fragmentShaderFile.empty())
706    {
707        program->addShader(osg::Shader::readShaderFile(osg::Shader::FRAGMENT, fragmentShaderFile));
708    }
709    else
710    {
711        //////////////////////////////////////////////////////////////////
712        // fragment shader
713        //
714        char fragmentShaderSource[] =
715            "uniform sampler3D baseTexture;\n"
716            "uniform float sampleDensity;\n"
717            "uniform float transparency;\n"
718            "uniform float alphaCutOff;\n"
719            "\n"
720            "varying vec3 deltaTexCoord;\n"
721            "varying vec3 texcoord;\n"
722            "void main(void) \n"
723            "{ \n"
724            "    vec3 deltaTexCoord2 = normalize(deltaTexCoord)*sampleDensity; \n"
725            "\n"
726            "    gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0); \n"
727            "    \n"
728            "    while (texcoord.x>=0.0 && texcoord.x<=1.0 &&\n"
729            "           texcoord.y>=0.0 && texcoord.y<=1.0 &&\n"
730            "           texcoord.z>=0.0 && texcoord.z<=1.0)\n"
731            "    {\n"
732            "       vec4 color = texture3D( baseTexture, texcoord);\n"
733            "       float r = color[3]*transparency;\n"
734            "       if (r>alphaCutOff)\n"
735            "       {\n"
736            "         gl_FragColor.xyz = gl_FragColor.xyz*(1.0-r)+color.xyz*r;\n"
737            "         gl_FragColor.w += r;\n"
738            "       }\n"
739            "       texcoord += deltaTexCoord2; \n"
740            "    }\n"
741            "    if (gl_FragColor.w>1.0) gl_FragColor.w = 1.0; \n"
742            "}\n";
743
744        osg::Shader* fragment_shader = new osg::Shader(osg::Shader::FRAGMENT, fragmentShaderSource);
745        program->addShader(fragment_shader);
746    }
747
748    osg::Uniform* baseTextureSampler = new osg::Uniform("baseTexture",0);
749    stateset->addUniform(baseTextureSampler);
750
751    osg::Uniform* sampleDensity = new osg::Uniform("sampleDensity", 1.0f/(float)numSlices);
752    stateset->addUniform(sampleDensity);
753
754    osg::Uniform* transpancy = new osg::Uniform("transparency",0.5f);
755    stateset->addUniform(transpancy);
756
757    osg::Uniform* alphaCutOff = new osg::Uniform("alphaCutOff",alphaFuncValue);
758    stateset->addUniform(alphaCutOff);
759
760    stateset->setMode(GL_CULL_FACE, osg::StateAttribute::ON);
761
762    {
763        osg::Geometry* geom = new osg::Geometry;
764
765        osg::Vec3Array* coords = new osg::Vec3Array(8);
766        (*coords)[0].set(0,0,0);
767        (*coords)[1].set(1,0,0);
768        (*coords)[2].set(1,1,0);
769        (*coords)[3].set(0,1,0);
770        (*coords)[4].set(0,0,1);
771        (*coords)[5].set(1,0,1);
772        (*coords)[6].set(1,1,1);
773        (*coords)[7].set(0,1,1);
774        geom->setVertexArray(coords);
775
776        osg::Vec3Array* tcoords = new osg::Vec3Array(8);
777        (*tcoords)[0].set(0,0,0);
778        (*tcoords)[1].set(1,0,0);
779        (*tcoords)[2].set(1,1,0);
780        (*tcoords)[3].set(0,1,0);
781        (*tcoords)[4].set(0,0,1);
782        (*tcoords)[5].set(1,0,1);
783        (*tcoords)[6].set(1,1,1);
784        (*tcoords)[7].set(0,1,1);
785        geom->setTexCoordArray(0,tcoords);
786
787        osg::Vec4Array* colours = new osg::Vec4Array(1);
788        (*colours)[0].set(1.0f,1.0f,1.0,1.0f);
789        geom->setColorArray(colours);
790        geom->setColorBinding(osg::Geometry::BIND_OVERALL);
791
792        osg::DrawElementsUShort* drawElements = new osg::DrawElementsUShort(GL_QUADS);
793        // bottom
794        drawElements->push_back(0);
795        drawElements->push_back(1);
796        drawElements->push_back(2);
797        drawElements->push_back(3);
798       
799        // bottom
800        drawElements->push_back(3);
801        drawElements->push_back(2);
802        drawElements->push_back(6);
803        drawElements->push_back(7);
804
805        // left
806        drawElements->push_back(0);
807        drawElements->push_back(3);
808        drawElements->push_back(7);
809        drawElements->push_back(4);
810
811        // right
812        drawElements->push_back(5);
813        drawElements->push_back(6);
814        drawElements->push_back(2);
815        drawElements->push_back(1);
816
817        // front
818        drawElements->push_back(1);
819        drawElements->push_back(0);
820        drawElements->push_back(4);
821        drawElements->push_back(5);
822
823        // top
824        drawElements->push_back(7);
825        drawElements->push_back(6);
826        drawElements->push_back(5);
827        drawElements->push_back(4);
828
829        geom->addPrimitiveSet(drawElements);
830
831        geode->addDrawable(geom);
832
833    }
834
835    return geode;
836}
837
838osg::Node* createModel(osg::ref_ptr<osg::Image>& image_3d, osg::ref_ptr<osg::Image>& normalmap_3d,
839                       osg::Texture::InternalFormatMode internalFormatMode,
840                       float xSize, float ySize, float zSize,
841                       float xMultiplier, float yMultiplier, float zMultiplier,
842                       unsigned int numSlices=500, float sliceEnd=1.0f, float alphaFuncValue=0.02f)
843{
844    bool two_pass = normalmap_3d.valid() && (image_3d->getPixelFormat()==GL_RGB || image_3d->getPixelFormat()==GL_RGBA);
845
846    osg::Group* group = new osg::Group;
847   
848    osg::TexGenNode* texgenNode_0 = new osg::TexGenNode;
849    texgenNode_0->setTextureUnit(0);
850    texgenNode_0->getTexGen()->setMode(osg::TexGen::EYE_LINEAR);
851    texgenNode_0->getTexGen()->setPlane(osg::TexGen::S, osg::Vec4(xMultiplier,0.0f,0.0f,0.5f));
852    texgenNode_0->getTexGen()->setPlane(osg::TexGen::T, osg::Vec4(0.0f,yMultiplier,0.0f,0.5f));
853    texgenNode_0->getTexGen()->setPlane(osg::TexGen::R, osg::Vec4(0.0f,0.0f,zMultiplier,0.5f));
854   
855    if (two_pass)
856    {
857        osg::TexGenNode* texgenNode_1 = new osg::TexGenNode;
858        texgenNode_1->setTextureUnit(1);
859        texgenNode_1->getTexGen()->setMode(osg::TexGen::EYE_LINEAR);
860        texgenNode_1->getTexGen()->setPlane(osg::TexGen::S, texgenNode_0->getTexGen()->getPlane(osg::TexGen::S));
861        texgenNode_1->getTexGen()->setPlane(osg::TexGen::T, texgenNode_0->getTexGen()->getPlane(osg::TexGen::T));
862        texgenNode_1->getTexGen()->setPlane(osg::TexGen::R, texgenNode_0->getTexGen()->getPlane(osg::TexGen::R));
863
864        texgenNode_1->addChild(texgenNode_0);
865
866        group->addChild(texgenNode_1);
867    }
868    else
869    { 
870        group->addChild(texgenNode_0);
871    }
872
873    osg::BoundingBox bb(-xSize*0.5f,-ySize*0.5f,-zSize*0.5f,xSize*0.5f,ySize*0.5f,zSize*0.5f);
874
875    osg::ClipNode* clipnode = new osg::ClipNode;
876    clipnode->addChild(createCube(1.0f,1.0f, numSlices,sliceEnd));
877    clipnode->createClipBox(bb);
878
879    {
880        // set up the Geometry to enclose the clip volume to prevent near/far clipping from affecting billboard
881        osg::Geometry* geom = new osg::Geometry;
882
883        osg::Vec3Array* coords = new osg::Vec3Array();
884        coords->push_back(bb.corner(0));
885        coords->push_back(bb.corner(1));
886        coords->push_back(bb.corner(2));
887        coords->push_back(bb.corner(3));
888        coords->push_back(bb.corner(4));
889        coords->push_back(bb.corner(5));
890        coords->push_back(bb.corner(6));
891        coords->push_back(bb.corner(7));
892
893        geom->setVertexArray(coords);
894
895        osg::Vec4Array* colors = new osg::Vec4Array(1);
896        (*colors)[0].set(1.0f,1.0f,1.0f,1.0f);
897        geom->setColorArray(colors);
898        geom->setColorBinding(osg::Geometry::BIND_OVERALL);
899
900        geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::POINTS,0,coords->size()));
901
902        osg::Geode* geode = new osg::Geode;
903        geode->addDrawable(geom);
904       
905        clipnode->addChild(geode);
906       
907    }
908
909    texgenNode_0->addChild(clipnode);
910
911    osg::StateSet* stateset = texgenNode_0->getOrCreateStateSet();
912
913    stateset->setMode(GL_LIGHTING,osg::StateAttribute::ON);
914    stateset->setMode(GL_BLEND,osg::StateAttribute::ON);
915    stateset->setAttribute(new osg::AlphaFunc(osg::AlphaFunc::GREATER,alphaFuncValue));
916   
917    osg::Material* material = new osg::Material;
918    material->setDiffuse(osg::Material::FRONT_AND_BACK,osg::Vec4(1.0f,1.0f,1.0f,1.0f));
919    stateset->setAttributeAndModes(material);
920   
921    osg::Vec3 lightDirection(1.0f,-1.0f,1.0f);
922    lightDirection.normalize();
923
924    if (normalmap_3d.valid())
925    {
926        if (two_pass)
927        {
928
929            // set up normal texture
930            osg::Texture3D* bump_texture3D = new osg::Texture3D;
931            bump_texture3D->setFilter(osg::Texture3D::MIN_FILTER,osg::Texture3D::LINEAR);
932            bump_texture3D->setFilter(osg::Texture3D::MAG_FILTER,osg::Texture3D::LINEAR);
933            bump_texture3D->setWrap(osg::Texture3D::WRAP_R,osg::Texture3D::CLAMP);
934            bump_texture3D->setWrap(osg::Texture3D::WRAP_S,osg::Texture3D::CLAMP);
935            bump_texture3D->setWrap(osg::Texture3D::WRAP_T,osg::Texture3D::CLAMP);
936            bump_texture3D->setImage(normalmap_3d.get());
937
938            bump_texture3D->setInternalFormatMode(internalFormatMode);
939
940            stateset->setTextureAttributeAndModes(0,bump_texture3D,osg::StateAttribute::ON);
941
942            osg::TexEnvCombine* tec = new osg::TexEnvCombine;
943            tec->setConstantColorAsLightDirection(lightDirection);
944
945            tec->setCombine_RGB(osg::TexEnvCombine::DOT3_RGB);
946            tec->setSource0_RGB(osg::TexEnvCombine::CONSTANT);
947            tec->setOperand0_RGB(osg::TexEnvCombine::SRC_COLOR);
948            tec->setSource1_RGB(osg::TexEnvCombine::TEXTURE);
949            tec->setOperand1_RGB(osg::TexEnvCombine::SRC_COLOR);
950
951            tec->setCombine_Alpha(osg::TexEnvCombine::REPLACE);
952            tec->setSource0_Alpha(osg::TexEnvCombine::PRIMARY_COLOR);
953            tec->setOperand0_Alpha(osg::TexEnvCombine::SRC_ALPHA);
954            tec->setSource1_Alpha(osg::TexEnvCombine::TEXTURE);
955            tec->setOperand1_Alpha(osg::TexEnvCombine::SRC_ALPHA);
956
957            stateset->setTextureAttributeAndModes(0, tec, osg::StateAttribute::OVERRIDE|osg::StateAttribute::ON);
958
959            stateset->setTextureMode(0,GL_TEXTURE_GEN_S,osg::StateAttribute::ON);
960            stateset->setTextureMode(0,GL_TEXTURE_GEN_T,osg::StateAttribute::ON);
961            stateset->setTextureMode(0,GL_TEXTURE_GEN_R,osg::StateAttribute::ON);
962
963
964            // set up color texture
965            osg::Texture3D* texture3D = new osg::Texture3D;
966            texture3D->setFilter(osg::Texture3D::MIN_FILTER,osg::Texture3D::LINEAR);
967            texture3D->setFilter(osg::Texture3D::MAG_FILTER,osg::Texture3D::LINEAR);
968            texture3D->setWrap(osg::Texture3D::WRAP_R,osg::Texture3D::CLAMP);
969            texture3D->setWrap(osg::Texture3D::WRAP_S,osg::Texture3D::CLAMP);
970            texture3D->setWrap(osg::Texture3D::WRAP_T,osg::Texture3D::CLAMP);
971            if (image_3d->getPixelFormat()==GL_ALPHA ||
972                image_3d->getPixelFormat()==GL_LUMINANCE)
973            {
974                texture3D->setInternalFormatMode(osg::Texture3D::USE_USER_DEFINED_FORMAT);
975                texture3D->setInternalFormat(GL_INTENSITY);
976            }
977            else
978            {
979                texture3D->setInternalFormatMode(internalFormatMode);
980            }
981            texture3D->setImage(image_3d.get());
982
983            stateset->setTextureAttributeAndModes(1,texture3D,osg::StateAttribute::ON);
984
985            stateset->setTextureMode(1,GL_TEXTURE_GEN_S,osg::StateAttribute::ON);
986            stateset->setTextureMode(1,GL_TEXTURE_GEN_T,osg::StateAttribute::ON);
987            stateset->setTextureMode(1,GL_TEXTURE_GEN_R,osg::StateAttribute::ON);
988
989            stateset->setTextureAttributeAndModes(1,new osg::TexEnv(),osg::StateAttribute::ON);
990
991        }
992        else
993        {
994            osg::ref_ptr<osg::Image> normalmap_3d = createNormalMapTexture(image_3d.get());
995            osg::Texture3D* bump_texture3D = new osg::Texture3D;
996            bump_texture3D->setFilter(osg::Texture3D::MIN_FILTER,osg::Texture3D::LINEAR);
997            bump_texture3D->setFilter(osg::Texture3D::MAG_FILTER,osg::Texture3D::LINEAR);
998            bump_texture3D->setWrap(osg::Texture3D::WRAP_R,osg::Texture3D::CLAMP);
999            bump_texture3D->setWrap(osg::Texture3D::WRAP_S,osg::Texture3D::CLAMP);
1000            bump_texture3D->setWrap(osg::Texture3D::WRAP_T,osg::Texture3D::CLAMP);
1001            bump_texture3D->setImage(normalmap_3d.get());
1002
1003            bump_texture3D->setInternalFormatMode(internalFormatMode);
1004
1005            stateset->setTextureAttributeAndModes(0,bump_texture3D,osg::StateAttribute::ON);
1006
1007            osg::TexEnvCombine* tec = new osg::TexEnvCombine;
1008            tec->setConstantColorAsLightDirection(lightDirection);
1009
1010            tec->setCombine_RGB(osg::TexEnvCombine::DOT3_RGB);
1011            tec->setSource0_RGB(osg::TexEnvCombine::CONSTANT);
1012            tec->setOperand0_RGB(osg::TexEnvCombine::SRC_COLOR);
1013            tec->setSource1_RGB(osg::TexEnvCombine::TEXTURE);
1014            tec->setOperand1_RGB(osg::TexEnvCombine::SRC_COLOR);
1015
1016            tec->setCombine_Alpha(osg::TexEnvCombine::MODULATE);
1017            tec->setSource0_Alpha(osg::TexEnvCombine::PRIMARY_COLOR);
1018            tec->setOperand0_Alpha(osg::TexEnvCombine::SRC_ALPHA);
1019            tec->setSource1_Alpha(osg::TexEnvCombine::TEXTURE);
1020            tec->setOperand1_Alpha(osg::TexEnvCombine::SRC_ALPHA);
1021
1022            stateset->setTextureAttributeAndModes(0, tec, osg::StateAttribute::OVERRIDE|osg::StateAttribute::ON);
1023
1024            stateset->setTextureMode(0,GL_TEXTURE_GEN_S,osg::StateAttribute::ON);
1025            stateset->setTextureMode(0,GL_TEXTURE_GEN_T,osg::StateAttribute::ON);
1026            stateset->setTextureMode(0,GL_TEXTURE_GEN_R,osg::StateAttribute::ON);
1027
1028            image_3d = normalmap_3d;
1029        }
1030    }
1031    else
1032    {     
1033        // set up the 3d texture itself,
1034        // note, well set the filtering up so that mip mapping is disabled,
1035        // gluBuild3DMipsmaps doesn't do a very good job of handled the
1036        // inbalanced dimensions of the 256x256x4 texture.
1037        osg::Texture3D* texture3D = new osg::Texture3D;
1038        texture3D->setFilter(osg::Texture3D::MIN_FILTER,osg::Texture3D::LINEAR);
1039        texture3D->setFilter(osg::Texture3D::MAG_FILTER,osg::Texture3D::LINEAR);
1040        texture3D->setWrap(osg::Texture3D::WRAP_R,osg::Texture3D::CLAMP);
1041        texture3D->setWrap(osg::Texture3D::WRAP_S,osg::Texture3D::CLAMP);
1042        texture3D->setWrap(osg::Texture3D::WRAP_T,osg::Texture3D::CLAMP);
1043        if (image_3d->getPixelFormat()==GL_ALPHA ||
1044            image_3d->getPixelFormat()==GL_LUMINANCE)
1045        {
1046            texture3D->setInternalFormatMode(osg::Texture3D::USE_USER_DEFINED_FORMAT);
1047            texture3D->setInternalFormat(GL_INTENSITY);
1048        }
1049        else
1050        {
1051            texture3D->setInternalFormatMode(internalFormatMode);
1052        }
1053
1054        texture3D->setImage(image_3d.get());
1055
1056        stateset->setTextureAttributeAndModes(0,texture3D,osg::StateAttribute::ON);
1057
1058        stateset->setTextureMode(0,GL_TEXTURE_GEN_S,osg::StateAttribute::ON);
1059        stateset->setTextureMode(0,GL_TEXTURE_GEN_T,osg::StateAttribute::ON);
1060        stateset->setTextureMode(0,GL_TEXTURE_GEN_R,osg::StateAttribute::ON);
1061
1062        stateset->setTextureAttributeAndModes(0,new osg::TexEnv(),osg::StateAttribute::ON);
1063    }
1064 
1065    return group;
1066}
1067
1068struct FindRangeOperator
1069{
1070    FindRangeOperator():
1071        _rmin(FLT_MAX),
1072        _rmax(-FLT_MAX),
1073        _gmin(FLT_MAX),
1074        _gmax(-FLT_MAX),
1075        _bmin(FLT_MAX),
1076        _bmax(-FLT_MAX),
1077        _amin(FLT_MAX),
1078        _amax(-FLT_MAX) {}
1079       
1080    mutable float _rmin, _rmax, _gmin, _gmax, _bmin, _bmax, _amin, _amax;
1081
1082    inline void luminance(float l) const { rgb(l,l,l); }
1083    inline void alpha(float a) const { _amin = osg::minimum(a,_amin); _amax = osg::maximum(a,_amax); }
1084    inline void luminance_alpha(float l,float a) const { rgb(l,l,l); alpha(a); }
1085    inline void rgb(float r,float g,float b) const { _rmin = osg::minimum(r,_rmin); _rmax = osg::maximum(r,_rmax); _gmin = osg::minimum(g,_gmin); _gmax = osg::maximum(g,_gmax); _bmin = osg::minimum(b,_bmin); _bmax = osg::maximum(b,_bmax);  }
1086    inline void rgba(float r,float g,float b,float a) const { rgb(r,g,b); alpha(a); }
1087};
1088 
1089struct ScaleOperator
1090{
1091    ScaleOperator():_scale(1.0f) {}
1092    ScaleOperator(float scale):_scale(scale) {}
1093    ScaleOperator(const ScaleOperator& so):_scale(so._scale) {}
1094   
1095    ScaleOperator& operator = (const ScaleOperator& so) { _scale = so._scale; return *this; }
1096
1097    float _scale;
1098
1099    inline void luminance(float& l) const { l*= _scale; }
1100    inline void alpha(float& a) const { a*= _scale; }
1101    inline void luminance_alpha(float& l,float& a) const { l*= _scale; a*= _scale;  }
1102    inline void rgb(float& r,float& g,float& b) const { r*= _scale; g*=_scale; b*=_scale; }
1103    inline void rgba(float& r,float& g,float& b,float& a) const { r*= _scale; g*=_scale; b*=_scale; a*=_scale; }
1104};
1105
1106struct RecordRowOperator
1107{
1108    RecordRowOperator(unsigned int num):_colours(num),_pos(0) {}
1109
1110    mutable std::vector<osg::Vec4>  _colours;
1111    mutable unsigned int            _pos;
1112   
1113    inline void luminance(float l) const { rgba(l,l,l,1.0f); }
1114    inline void alpha(float a) const { rgba(1.0f,1.0f,1.0f,a); }
1115    inline void luminance_alpha(float l,float a) const { rgba(l,l,l,a);  }
1116    inline void rgb(float r,float g,float b) const { rgba(r,g,b,1.0f); }
1117    inline void rgba(float r,float g,float b,float a) const { _colours[_pos++].set(r,g,b,a); }
1118};
1119
1120struct WriteRowOperator
1121{
1122    WriteRowOperator():_pos(0) {}
1123    WriteRowOperator(unsigned int num):_colours(num),_pos(0) {}
1124
1125    std::vector<osg::Vec4>  _colours;
1126    mutable unsigned int    _pos;
1127   
1128    inline void luminance(float& l) const { l = _colours[_pos++].r(); }
1129    inline void alpha(float& a) const { a = _colours[_pos++].a(); }
1130    inline void luminance_alpha(float& l,float& a) const { l = _colours[_pos].r(); a = _colours[_pos++].a(); }
1131    inline void rgb(float& r,float& g,float& b) const { r = _colours[_pos].r(); g = _colours[_pos].g(); b = _colours[_pos].b(); }
1132    inline void rgba(float& r,float& g,float& b,float& a) const {  r = _colours[_pos].r(); g = _colours[_pos].g(); b = _colours[_pos].b(); a = _colours[_pos++].a(); }
1133};
1134
1135osg::Image* readRaw(int sizeX, int sizeY, int sizeZ, int numberBytesPerComponent, int numberOfComponents, const std::string& endian, const std::string& raw_filename)
1136{
1137    std::ifstream fin(raw_filename.c_str());
1138    if (!fin) return 0;
1139
1140    GLenum pixelFormat;
1141    switch(numberOfComponents)
1142    {
1143        case 1 : pixelFormat = GL_LUMINANCE; break;
1144        case 2 : pixelFormat = GL_LUMINANCE_ALPHA; break;
1145        case 3 : pixelFormat = GL_RGB; break;
1146        case 4 : pixelFormat = GL_RGBA; break;
1147        default :
1148            osg::notify(osg::NOTICE)<<"Error: numberOfComponents="<<numberOfComponents<<" not supported, only 1,2,3 or 4 are supported."<<std::endl;
1149            return 0;
1150    }
1151
1152   
1153    GLenum dataType;
1154    switch(numberBytesPerComponent)
1155    {
1156        case 1 : dataType = GL_UNSIGNED_BYTE; break;
1157        case 2 : dataType = GL_UNSIGNED_SHORT; break;
1158        case 4 : dataType = GL_UNSIGNED_INT; break;
1159        default :
1160            osg::notify(osg::NOTICE)<<"Error: numberBytesPerComponent="<<numberBytesPerComponent<<" not supported, only 1,2 or 4 are supported."<<std::endl;
1161            return 0;
1162    }
1163   
1164    int s_maximumTextureSize=256, t_maximumTextureSize=256, r_maximumTextureSize=256;
1165   
1166    int sizeS = sizeX;
1167    int sizeT = sizeY;
1168    int sizeR = sizeZ;
1169    clampToNearestValidPowerOfTwo(sizeS, sizeT, sizeR, s_maximumTextureSize, t_maximumTextureSize, r_maximumTextureSize);
1170
1171    osg::ref_ptr<osg::Image> image = new osg::Image;
1172    image->allocateImage(sizeS, sizeT, sizeR, pixelFormat, dataType);
1173   
1174   
1175    bool endianSwap = (osg::getCpuByteOrder()==osg::BigEndian) ? (endian!="big") : (endian=="big");
1176   
1177    unsigned int r_offset = (sizeZ<sizeR) ? sizeR/2 - sizeZ/2 : 0;
1178   
1179    int offset = endianSwap ? numberBytesPerComponent : 0;
1180    int delta = endianSwap ? -1 : 1;
1181    for(int r=0;r<sizeZ;++r)
1182    {
1183        for(int t=0;t<sizeY;++t)
1184        {
1185            char* data = (char*) image->data(0,t,r+r_offset);
1186            for(int s=0;s<sizeX;++s)
1187            {
1188                if (!fin) return 0;
1189               
1190                for(int c=0;c<numberOfComponents;++c)
1191                {
1192                    char* ptr = data+offset;
1193                    for(int b=0;b<numberBytesPerComponent;++b)
1194                    {
1195                        fin.read((char*)ptr, 1);
1196                        ptr += delta;
1197                    }
1198                    data += numberBytesPerComponent;
1199                }
1200            }
1201        }
1202    }
1203
1204
1205    // normalise texture
1206    {
1207        // compute range of values
1208        FindRangeOperator rangeOp;   
1209        readImage(image.get(), rangeOp);
1210        modifyImage(image.get(),ScaleOperator(1.0f/rangeOp._rmax));
1211    }
1212   
1213   
1214    fin.close();
1215
1216    if (dataType!=GL_UNSIGNED_BYTE)
1217    {
1218        // need to convert to ubyte
1219       
1220        osg::ref_ptr<osg::Image> new_image = new osg::Image;
1221        new_image->allocateImage(sizeS, sizeT, sizeR, pixelFormat, GL_UNSIGNED_BYTE);
1222       
1223        RecordRowOperator readOp(sizeS);
1224        WriteRowOperator writeOp;
1225
1226        for(int r=0;r<sizeR;++r)
1227        {
1228            for(int t=0;t<sizeT;++t)
1229            {
1230                // reset the indices to begining
1231                readOp._pos = 0;
1232                writeOp._pos = 0;
1233           
1234                // read the pixels into readOp's _colour array
1235                readRow(sizeS, pixelFormat, dataType, image->data(0,t,r), readOp);
1236                               
1237                // pass readOp's _colour array contents over to writeOp (note this is just a pointer swap).
1238                writeOp._colours.swap(readOp._colours);
1239               
1240                modifyRow(sizeS, pixelFormat, GL_UNSIGNED_BYTE, new_image->data(0,t,r), writeOp);
1241
1242                // return readOp's _colour array contents back to its rightful owner.
1243                writeOp._colours.swap(readOp._colours);
1244            }
1245        }
1246       
1247        image = new_image;
1248    }
1249   
1250    return image.release();
1251   
1252   
1253}
1254
1255enum ColourSpaceOperation
1256{
1257    NO_COLOUR_SPACE_OPERATION,
1258    MODULATE_ALPHA_BY_LUMINANCE,
1259    MODULATE_ALPHA_BY_COLOUR,
1260    REPLACE_ALPHA_WITH_LUMINACE
1261};
1262
1263struct ModulateAlphaByLuminanceOperator
1264{
1265    ModulateAlphaByLuminanceOperator() {}
1266
1267    inline void luminance(float&) const {}
1268    inline void alpha(float&) const {}
1269    inline void luminance_alpha(float& l,float& a) const { a*= l; }
1270    inline void rgb(float&,float&,float&) const {}
1271    inline void rgba(float& r,float& g,float& b,float& a) const { float l = (r+g+b)*0.3333333; a *= l;}
1272};
1273
1274struct ModulateAlphaByColourOperator
1275{
1276    ModulateAlphaByColourOperator(const osg::Vec4& colour):_colour(colour) { _lum = _colour.length(); }
1277   
1278    osg::Vec4 _colour;
1279    float _lum;
1280
1281    inline void luminance(float&) const {}
1282    inline void alpha(float&) const {}
1283    inline void luminance_alpha(float& l,float& a) const { a*= l*_lum; }
1284    inline void rgb(float&,float&,float&) const {}
1285    inline void rgba(float& r,float& g,float& b,float& a) const { a = (r*_colour.r()+g*_colour.g()+b*_colour.b()+a*_colour.a()); }
1286};
1287
1288struct ReplaceAlphaWithLuminanceOperator
1289{
1290    ReplaceAlphaWithLuminanceOperator() {}
1291
1292    inline void luminance(float&) const {}
1293    inline void alpha(float&) const {}
1294    inline void luminance_alpha(float& l,float& a) const { a= l; }
1295    inline void rgb(float&,float&,float&) const { }
1296    inline void rgba(float& r,float& g,float& b,float& a) const { float l = (r+g+b)*0.3333333; a = l; }
1297};
1298
1299void doColourSpaceConversion(ColourSpaceOperation op, osg::Image* image, osg::Vec4& colour)
1300{
1301    switch(op)
1302    {
1303        case (MODULATE_ALPHA_BY_LUMINANCE):
1304            std::cout<<"doing conversion MODULATE_ALPHA_BY_LUMINANCE"<<std::endl;
1305            modifyImage(image,ModulateAlphaByLuminanceOperator());
1306            break;
1307        case (MODULATE_ALPHA_BY_COLOUR):
1308            std::cout<<"doing conversion MODULATE_ALPHA_BY_COLOUR"<<std::endl;
1309            modifyImage(image,ModulateAlphaByColourOperator(colour));
1310            break;
1311        case (REPLACE_ALPHA_WITH_LUMINACE):
1312            std::cout<<"doing conversion REPLACE_ALPHA_WITH_LUMINACE"<<std::endl;
1313            modifyImage(image,ReplaceAlphaWithLuminanceOperator());
1314            break;
1315        default:
1316            break;
1317    }
1318}
1319
1320int main( int argc, char **argv )
1321{
1322    // use an ArgumentParser object to manage the program arguments.
1323    osg::ArgumentParser arguments(&argc,argv);
1324   
1325    // set up the usage document, in case we need to print out how to use this program.
1326    arguments.getApplicationUsage()->setDescription(arguments.getApplicationName()+" is the example which demonstrates use of 3D textures.");
1327    arguments.getApplicationUsage()->setCommandLineUsage(arguments.getApplicationName()+" [options] filename ...");
1328    arguments.getApplicationUsage()->addCommandLineOption("-h or --help","Display this information");
1329    arguments.getApplicationUsage()->addCommandLineOption("-n","Create normal map for per voxel lighting.");
1330    arguments.getApplicationUsage()->addCommandLineOption("-s <numSlices>","Number of slices to create.");
1331    arguments.getApplicationUsage()->addCommandLineOption("--xSize <size>","Relative width of rendered brick.");
1332    arguments.getApplicationUsage()->addCommandLineOption("--ySize <size>","Relative length of rendered brick.");
1333    arguments.getApplicationUsage()->addCommandLineOption("--zSize <size>","Relative height of rendered brick.");
1334    arguments.getApplicationUsage()->addCommandLineOption("--xMultiplier <multiplier>","Tex coord x mulitplier.");
1335    arguments.getApplicationUsage()->addCommandLineOption("--yMultiplier <multiplier>","Tex coord y mulitplier.");
1336    arguments.getApplicationUsage()->addCommandLineOption("--zMultiplier <multiplier>","Tex coord z mulitplier.");
1337    arguments.getApplicationUsage()->addCommandLineOption("--clip <ratio>","clip volume as a ratio, 0.0 clip all, 1.0 clip none.");
1338    arguments.getApplicationUsage()->addCommandLineOption("--maxTextureSize <size>","Set the texture maximum resolution in the s,t,r (x,y,z) dimensions.");
1339    arguments.getApplicationUsage()->addCommandLineOption("--s_maxTextureSize <size>","Set the texture maximum resolution in the s (x) dimension.");
1340    arguments.getApplicationUsage()->addCommandLineOption("--t_maxTextureSize <size>","Set the texture maximum resolution in the t (y) dimension.");
1341    arguments.getApplicationUsage()->addCommandLineOption("--r_maxTextureSize <size>","Set the texture maximum resolution in the r (z) dimension.");
1342    arguments.getApplicationUsage()->addCommandLineOption("--compressed","Enable the usage of compressed textures.");
1343    arguments.getApplicationUsage()->addCommandLineOption("--compressed-arb","Enable the usage of OpenGL ARB compressed textures.");
1344    arguments.getApplicationUsage()->addCommandLineOption("--compressed-dxt1","Enable the usage of S3TC DXT1 compressed textures.");
1345    arguments.getApplicationUsage()->addCommandLineOption("--compressed-dxt3","Enable the usage of S3TC DXT3 compressed textures.");
1346    arguments.getApplicationUsage()->addCommandLineOption("--compressed-dxt5","Enable the usage of S3TC DXT5 compressed textures.");
1347    arguments.getApplicationUsage()->addCommandLineOption("--modulate-alpha-by-luminance","For each pixel multiple the alpha value by the luminance.");
1348    arguments.getApplicationUsage()->addCommandLineOption("--replace-alpha-with-luminance","For each pixel mSet the alpha value to the luminance.");
1349    arguments.getApplicationUsage()->addCommandLineOption("--num-components <num>","Set the number of components to in he target image.");
1350//    arguments.getApplicationUsage()->addCommandLineOption("--raw <sizeX> <sizeY> <sizeZ> <numberBytesPerComponent> <numberOfComponents> <endian> <filename>","read a raw image data");
1351
1352    // construct the viewer.
1353    osgProducer::Viewer viewer(arguments);
1354
1355    // set up the value with sensible default event handlers.
1356    viewer.setUpViewer(osgProducer::Viewer::STANDARD_SETTINGS);
1357
1358    // get details on keyboard and mouse bindings used by the viewer.
1359    viewer.getUsage(*arguments.getApplicationUsage());
1360
1361    // if user request help write it out to cout.
1362    if (arguments.read("-h") || arguments.read("--help"))
1363    {
1364        arguments.getApplicationUsage()->write(std::cout);
1365        return 1;
1366    }
1367
1368    std::string outputFile;
1369    while (arguments.read("-o",outputFile)) {}
1370
1371
1372    unsigned int numSlices=500;
1373    while (arguments.read("-s",numSlices)) {}
1374   
1375   
1376    float sliceEnd=1.0f;
1377    while (arguments.read("--clip",sliceEnd)) {}
1378
1379    float alphaFunc=0.02f;
1380    while (arguments.read("--alphaFunc",alphaFunc)) {}
1381
1382
1383    bool createNormalMap = false;
1384    while (arguments.read("-n")) createNormalMap=true;
1385
1386    float xSize=1.0f, ySize=1.0f, zSize=1.0f;
1387    while (arguments.read("--xSize",xSize)) {}
1388    while (arguments.read("--ySize",ySize)) {}
1389    while (arguments.read("--zSize",zSize)) {}
1390
1391    float xMultiplier=1.0f, yMultiplier=1.0f, zMultiplier=1.0f;
1392    while (arguments.read("--xMultiplier",xMultiplier)) {}
1393    while (arguments.read("--yMultiplier",yMultiplier)) {}
1394    while (arguments.read("--zMultiplier",zMultiplier)) {}
1395
1396    int s_maximumTextureSize = 256;
1397    int t_maximumTextureSize = 256;
1398    int r_maximumTextureSize = 256;
1399    int maximumTextureSize = 256;
1400    while(arguments.read("--maxTextureSize",maximumTextureSize))
1401    {
1402        s_maximumTextureSize = maximumTextureSize;
1403        t_maximumTextureSize = maximumTextureSize;
1404        r_maximumTextureSize = maximumTextureSize;
1405    }
1406    while(arguments.read("--s_maxTextureSize",s_maximumTextureSize)) {}
1407    while(arguments.read("--t_maxTextureSize",t_maximumTextureSize)) {}
1408    while(arguments.read("--r_maxTextureSize",r_maximumTextureSize)) {}
1409
1410    osg::Texture::InternalFormatMode internalFormatMode = osg::Texture::USE_IMAGE_DATA_FORMAT;
1411    while(arguments.read("--compressed") || arguments.read("--compressed-arb")) { internalFormatMode = osg::Texture::USE_ARB_COMPRESSION; }
1412
1413    while(arguments.read("--compressed-dxt1")) { internalFormatMode = osg::Texture::USE_S3TC_DXT1_COMPRESSION; }
1414    while(arguments.read("--compressed-dxt3")) { internalFormatMode = osg::Texture::USE_S3TC_DXT3_COMPRESSION; }
1415    while(arguments.read("--compressed-dxt5")) { internalFormatMode = osg::Texture::USE_S3TC_DXT5_COMPRESSION; }
1416   
1417   
1418    // set up colour space operation.
1419    ColourSpaceOperation colourSpaceOperation = NO_COLOUR_SPACE_OPERATION;
1420    osg::Vec4 colourModulate(0.25f,0.25f,0.25f,0.25f);
1421    while(arguments.read("--modulate-alpha-by-luminance")) { colourSpaceOperation = MODULATE_ALPHA_BY_LUMINANCE; }
1422    while(arguments.read("--modulate-alpha-by-colour", colourModulate.x(),colourModulate.y(),colourModulate.z(),colourModulate.w() )) { colourSpaceOperation = MODULATE_ALPHA_BY_COLOUR; }
1423    while(arguments.read("--replace-alpha-with-luminance")) { colourSpaceOperation = REPLACE_ALPHA_WITH_LUMINACE; }
1424       
1425   
1426    unsigned int numComponentsDesired = 0;
1427    while(arguments.read("--num-components", numComponentsDesired)) {}
1428
1429    bool useShader = false;
1430    while(arguments.read("--shader")) { useShader = true; }
1431
1432    osg::ref_ptr<osg::Image> image_3d;
1433
1434    int sizeX, sizeY, sizeZ, numberBytesPerComponent, numberOfComponents;
1435    std::string endian, raw_filename;
1436    while (arguments.read("--raw", sizeX, sizeY, sizeZ, numberBytesPerComponent, numberOfComponents, endian, raw_filename))
1437    {
1438        image_3d = readRaw(sizeX, sizeY, sizeZ, numberBytesPerComponent, numberOfComponents, endian, raw_filename);
1439    }
1440
1441    while (arguments.read("--images"))
1442    {
1443        ImageList imageList;
1444        for(int pos=1;pos<arguments.argc() && !arguments.isOption(pos);++pos)
1445        {
1446            // not an option so assume string is a filename.
1447            osg::Image *image = osgDB::readImageFile( arguments[pos]);
1448
1449            if(image)
1450            {
1451                imageList.push_back(image);
1452            }
1453        }
1454       
1455        // pack the textures into a single texture.
1456        ProcessRow processRow;
1457        image_3d = createTexture3D(imageList, processRow, numComponentsDesired, s_maximumTextureSize, t_maximumTextureSize, r_maximumTextureSize);
1458    }
1459
1460
1461    // any option left unread are converted into errors to write out later.
1462    arguments.reportRemainingOptionsAsUnrecognized();
1463
1464    // report any errors if they have occured when parsing the program aguments.
1465    if (arguments.errors())
1466    {
1467        arguments.writeErrorMessages(std::cout);
1468        return 1;
1469    }
1470
1471    // assume remaining argments are file names of textures.
1472    for(int pos=1;pos<arguments.argc() && !image_3d;++pos)
1473    {
1474        if (!arguments.isOption(pos))
1475        {
1476            // not an option so assume string is a filename.
1477            image_3d = osgDB::readImageFile( arguments[pos]);
1478        }
1479    }
1480   
1481    if (!image_3d) return 0;
1482   
1483    if (colourSpaceOperation!=NO_COLOUR_SPACE_OPERATION)
1484    {
1485        doColourSpaceConversion(colourSpaceOperation, image_3d.get(), colourModulate);
1486    }
1487   
1488    osg::ref_ptr<osg::Image> normalmap_3d = createNormalMap ? createNormalMapTexture(image_3d.get()) : 0;
1489
1490
1491
1492    // create a model from the images.
1493    osg::Node* rootNode = 0;
1494   
1495    if (useShader)
1496    {
1497        rootNode = createShaderModel(image_3d, normalmap_3d,
1498                               internalFormatMode,
1499                               xSize, ySize, zSize,
1500                               xMultiplier, yMultiplier, zMultiplier,
1501                               numSlices, sliceEnd, alphaFunc);
1502    }
1503    else
1504    {
1505        rootNode = createModel(image_3d, normalmap_3d,
1506                               internalFormatMode,
1507                               xSize, ySize, zSize,
1508                               xMultiplier, yMultiplier, zMultiplier,
1509                               numSlices, sliceEnd, alphaFunc);
1510    }
1511   
1512    if (!outputFile.empty())
1513    {   
1514        std::string ext = osgDB::getFileExtension(outputFile);
1515        std::string name_no_ext = osgDB::getNameLessExtension(outputFile);
1516        if (ext=="osg")
1517        {
1518            if (image_3d.valid())
1519            {
1520                image_3d->setFileName(name_no_ext + ".dds");           
1521                osgDB::writeImageFile(*image_3d, image_3d->getFileName());
1522            }
1523            if (normalmap_3d.valid())
1524            {
1525                normalmap_3d->setFileName(name_no_ext + "_normalmap.dds");           
1526                osgDB::writeImageFile(*normalmap_3d, normalmap_3d->getFileName());
1527            }
1528           
1529            osgDB::writeNodeFile(*rootNode, outputFile);
1530        }
1531        else if (ext=="ive")
1532        {
1533            osgDB::writeNodeFile(*rootNode, outputFile);       
1534        }
1535        else if (ext=="dds")
1536        {
1537            osgDB::writeImageFile(*image_3d, outputFile);       
1538        }
1539        else
1540        {
1541            std::cout<<"Extension not support for file output, not file written."<<std::endl;
1542        }
1543       
1544        return 0;
1545    }
1546
1547
1548    if (rootNode)
1549    {
1550
1551        // set the scene to render
1552        viewer.setSceneData(rootNode);
1553       
1554        // create the windows and run the threads.
1555        viewer.realize();
1556
1557        while( !viewer.done() )
1558        {
1559            // wait for all cull and draw threads to complete.
1560            viewer.sync();
1561
1562            // update the scene by traversing it with the the update visitor which will
1563            // call all node update callbacks and animations.
1564            viewer.update();
1565
1566            // fire off the cull and draw traversals of the scene.
1567            viewer.frame();
1568
1569        }
1570       
1571
1572        // wait for all cull and draw threads to complete before exit.
1573        viewer.sync();
1574    }   
1575   
1576    return 0;
1577
1578}
Note: See TracBrowser for help on using the browser.