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

Revision 12506, 61.5 kB (checked in by robert, 4 years ago)

Added support for creating a 3D Image from an image list.

Added support for reading a list of images in the Present3D <volume> tag.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1/* OpenSceneGraph example, osgvolume.
2*
3*  Permission is hereby granted, free of charge, to any person obtaining a copy
4*  of this software and associated documentation files (the "Software"), to deal
5*  in the Software without restriction, including without limitation the rights
6*  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7*  copies of the Software, and to permit persons to whom the Software is
8*  furnished to do so, subject to the following conditions:
9*
10*  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
11*  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
12*  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
13*  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
14*  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
15*  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
16*  THE SOFTWARE.
17*/
18
19#include <osg/Node>
20#include <osg/Geometry>
21#include <osg/Notify>
22#include <osg/Texture3D>
23#include <osg/Texture1D>
24#include <osg/ImageSequence>
25#include <osg/TexGen>
26#include <osg/Geode>
27#include <osg/Billboard>
28#include <osg/PositionAttitudeTransform>
29#include <osg/ClipNode>
30#include <osg/AlphaFunc>
31#include <osg/TexGenNode>
32#include <osg/TexEnv>
33#include <osg/TexEnvCombine>
34#include <osg/Material>
35#include <osg/PrimitiveSet>
36#include <osg/Endian>
37#include <osg/BlendFunc>
38#include <osg/BlendEquation>
39#include <osg/TransferFunction>
40#include <osg/MatrixTransform>
41
42#include <osgDB/Registry>
43#include <osgDB/ReadFile>
44#include <osgDB/WriteFile>
45#include <osgDB/FileUtils>
46#include <osgDB/FileNameUtils>
47
48#include <osgGA/EventVisitor>
49#include <osgGA/TrackballManipulator>
50#include <osgGA/FlightManipulator>
51#include <osgGA/KeySwitchMatrixManipulator>
52
53#include <osgUtil/CullVisitor>
54
55#include <osgViewer/Viewer>
56#include <osgViewer/ViewerEventHandlers>
57
58#include <osgManipulator/TabBoxDragger>
59#include <osgManipulator/TabPlaneTrackballDragger>
60#include <osgManipulator/TrackballDragger>
61
62#include <osg/io_utils>
63
64#include <algorithm>
65#include <iostream>
66
67#include <osg/ImageUtils>
68#include <osgVolume/Volume>
69#include <osgVolume/VolumeTile>
70#include <osgVolume/RayTracedTechnique>
71#include <osgVolume/FixedFunctionTechnique>
72
73enum ShadingModel
74{
75    Standard,
76    Light,
77    Isosurface,
78    MaximumIntensityProjection
79};
80
81struct PassThroughTransformFunction
82{
83    unsigned char operator() (unsigned char c) const { return c; }
84};
85
86
87void clampToNearestValidPowerOfTwo(int& sizeX, int& sizeY, int& sizeZ, int s_maximumTextureSize, int t_maximumTextureSize, int r_maximumTextureSize)
88{
89    // compute nearest powers of two for each axis.
90    int s_nearestPowerOfTwo = 1;
91    while(s_nearestPowerOfTwo<sizeX && s_nearestPowerOfTwo<s_maximumTextureSize) s_nearestPowerOfTwo*=2;
92
93    int t_nearestPowerOfTwo = 1;
94    while(t_nearestPowerOfTwo<sizeY && t_nearestPowerOfTwo<t_maximumTextureSize) t_nearestPowerOfTwo*=2;
95
96    int r_nearestPowerOfTwo = 1;
97    while(r_nearestPowerOfTwo<sizeZ && r_nearestPowerOfTwo<r_maximumTextureSize) r_nearestPowerOfTwo*=2;
98
99    sizeX = s_nearestPowerOfTwo;
100    sizeY = t_nearestPowerOfTwo;
101    sizeZ = r_nearestPowerOfTwo;
102}
103
104#if 1
105struct ModulateAlphaByLuminanceOperator
106{
107    ModulateAlphaByLuminanceOperator() {}
108
109    inline void luminance(float&) const {}
110    inline void alpha(float&) const {}
111    inline void luminance_alpha(float& l,float& a) const { a*= l; }
112    inline void rgb(float&,float&,float&) const {}
113    inline void rgba(float& r,float& g,float& b,float& a) const { float l = (r+g+b)*0.3333333; a *= l;}
114};
115
116osg::Image* createTexture3D(osg::ImageList& imageList,
117            unsigned int numComponentsDesired,
118            int s_maximumTextureSize,
119            int t_maximumTextureSize,
120            int r_maximumTextureSize,
121            bool resizeToPowerOfTwo)
122{
123
124    GLenum desiredPixelFormat = 0;
125    bool modulateAlphaByLuminance = false;
126    if (numComponentsDesired==0)
127    {
128        unsigned int maxNumComponents = osg::maximimNumOfComponents(imageList);
129        if (maxNumComponents==3)
130        {
131            desiredPixelFormat = GL_RGBA;
132            modulateAlphaByLuminance = true;
133        }
134    }
135    else
136    {
137        switch(numComponentsDesired)
138        {
139            case(1) : desiredPixelFormat = GL_LUMINANCE; break;
140            case(2) : desiredPixelFormat = GL_LUMINANCE_ALPHA; break;
141            case(3) : desiredPixelFormat = GL_RGB; break;
142            case(4) : desiredPixelFormat = GL_RGBA; break;
143        }
144    }
145
146    osg::ref_ptr<osg::Image> image = osg::createImage3D(imageList,
147                                        desiredPixelFormat,
148                                        s_maximumTextureSize,
149                                        t_maximumTextureSize,
150                                        r_maximumTextureSize,
151                                        resizeToPowerOfTwo);
152    if (image.valid())
153    {
154        if (modulateAlphaByLuminance)
155        {
156            osg::modifyImage(image.get(), ModulateAlphaByLuminanceOperator());
157        }
158        return image.release();
159    }
160    else
161    {
162        return 0;
163    }
164}
165#else
166
167struct ProcessRow
168{
169    virtual ~ProcessRow() {}
170
171    virtual void operator() (unsigned int num,
172                    GLenum source_pixelFormat, unsigned char* source,
173                    GLenum dest_pixelFormat, unsigned char* dest) const
174    {
175        switch(source_pixelFormat)
176        {
177        case(GL_LUMINANCE):
178        case(GL_ALPHA):
179            switch(dest_pixelFormat)
180            {
181            case(GL_LUMINANCE):
182            case(GL_ALPHA): A_to_A(num, source, dest); break;
183            case(GL_LUMINANCE_ALPHA): A_to_LA(num, source, dest); break;
184            case(GL_RGB): A_to_RGB(num, source, dest); break;
185            case(GL_RGBA): A_to_RGBA(num, source, dest); break;
186            }
187            break;
188        case(GL_LUMINANCE_ALPHA):
189            switch(dest_pixelFormat)
190            {
191            case(GL_LUMINANCE):
192            case(GL_ALPHA): LA_to_A(num, source, dest); break;
193            case(GL_LUMINANCE_ALPHA): LA_to_LA(num, source, dest); break;
194            case(GL_RGB): LA_to_RGB(num, source, dest); break;
195            case(GL_RGBA): LA_to_RGBA(num, source, dest); break;
196            }
197            break;
198        case(GL_RGB):
199            switch(dest_pixelFormat)
200            {
201            case(GL_LUMINANCE):
202            case(GL_ALPHA): RGB_to_A(num, source, dest); break;
203            case(GL_LUMINANCE_ALPHA): RGB_to_LA(num, source, dest); break;
204            case(GL_RGB): RGB_to_RGB(num, source, dest); break;
205            case(GL_RGBA): RGB_to_RGBA(num, source, dest); break;
206            }
207            break;
208        case(GL_RGBA):
209            switch(dest_pixelFormat)
210            {
211            case(GL_LUMINANCE):
212            case(GL_ALPHA): RGBA_to_A(num, source, dest); break;
213            case(GL_LUMINANCE_ALPHA): RGBA_to_LA(num, source, dest); break;
214            case(GL_RGB): RGBA_to_RGB(num, source, dest); break;
215            case(GL_RGBA): RGBA_to_RGBA(num, source, dest); break;
216            }
217            break;
218        case(GL_BGR):
219            switch(dest_pixelFormat)
220            {
221            case(GL_LUMINANCE):
222            case(GL_ALPHA): BGR_to_A(num, source, dest); break;
223            case(GL_LUMINANCE_ALPHA): BGR_to_LA(num, source, dest); break;
224            case(GL_RGB): BGR_to_RGB(num, source, dest); break;
225            case(GL_RGBA): BGR_to_RGBA(num, source, dest); break;
226            }
227            break;
228        case(GL_BGRA):
229            switch(dest_pixelFormat)
230            {
231            case(GL_LUMINANCE):
232            case(GL_ALPHA): BGRA_to_A(num, source, dest); break;
233            case(GL_LUMINANCE_ALPHA): BGRA_to_LA(num, source, dest); break;
234            case(GL_RGB): BGRA_to_RGB(num, source, dest); break;
235            case(GL_RGBA): BGRA_to_RGBA(num, source, dest); break;
236            }
237            break;
238        }
239    }
240
241    ///////////////////////////////////////////////////////////////////////////////
242    // alpha sources..
243    virtual void A_to_A(unsigned int num, unsigned char* source, unsigned char* dest) const
244    {
245        for(unsigned int i=0;i<num;++i)
246        {
247            *dest++ = *source++;
248        }
249    }
250
251    virtual void A_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 A_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        }
268    }
269
270    virtual void A_to_RGBA(unsigned int num, unsigned char* source, unsigned char* dest) const
271    {
272        for(unsigned int i=0;i<num;++i)
273        {
274            *dest++ = *source;
275            *dest++ = *source;
276            *dest++ = *source;
277            *dest++ = *source++;
278        }
279    }
280
281    ///////////////////////////////////////////////////////////////////////////////
282    // alpha luminance sources..
283    virtual void LA_to_A(unsigned int num, unsigned char* source, unsigned char* dest) const
284    {
285        for(unsigned int i=0;i<num;++i)
286        {
287            ++source;
288            *dest++ = *source++;
289        }
290    }
291
292    virtual void LA_to_LA(unsigned int num, unsigned char* source, unsigned char* dest) const
293    {
294        for(unsigned int i=0;i<num;++i)
295        {
296            *dest++ = *source++;
297            *dest++ = *source++;
298        }
299    }
300
301    virtual void LA_to_RGB(unsigned int num, unsigned char* source, unsigned char* dest) const
302    {
303        for(unsigned int i=0;i<num;++i)
304        {
305            *dest++ = *source;
306            *dest++ = *source;
307            *dest++ = *source;
308            source+=2;
309        }
310    }
311
312    virtual void LA_to_RGBA(unsigned int num, unsigned char* source, unsigned char* dest) const
313    {
314        for(unsigned int i=0;i<num;++i)
315        {
316            *dest++ = *source;
317            *dest++ = *source;
318            *dest++ = *source++;
319            *dest++ = *source++;
320        }
321    }
322
323    ///////////////////////////////////////////////////////////////////////////////
324    // RGB sources..
325    virtual void RGB_to_A(unsigned int num, unsigned char* source, unsigned char* dest) const
326    {
327        for(unsigned int i=0;i<num;++i)
328        {
329            unsigned char val = *source;
330            *dest++ = val;
331            source += 3;
332        }
333    }
334
335    virtual void RGB_to_LA(unsigned int num, unsigned char* source, unsigned char* dest) const
336    {
337        for(unsigned int i=0;i<num;++i)
338        {
339            unsigned char val = *source;
340            *dest++ = val;
341            *dest++ = val;
342            source += 3;
343        }
344    }
345
346    virtual void RGB_to_RGB(unsigned int num, unsigned char* source, unsigned char* dest) const
347    {
348        for(unsigned int i=0;i<num;++i)
349        {
350            *dest++ = *source++;
351            *dest++ = *source++;
352            *dest++ = *source++;
353        }
354    }
355
356    virtual void RGB_to_RGBA(unsigned int num, unsigned char* source, unsigned char* dest) const
357    {
358        for(unsigned int i=0;i<num;++i)
359        {
360            unsigned char val = *source;
361            *dest++ = *source++;
362            *dest++ = *source++;
363            *dest++ = *source++;
364            *dest++ = val;
365        }
366    }
367
368    ///////////////////////////////////////////////////////////////////////////////
369    // RGBA sources..
370    virtual void RGBA_to_A(unsigned int num, unsigned char* source, unsigned char* dest) const
371    {
372        for(unsigned int i=0;i<num;++i)
373        {
374            source += 3;
375            *dest++ = *source++;
376        }
377    }
378
379    virtual void RGBA_to_LA(unsigned int num, unsigned char* source, unsigned char* dest) const
380    {
381        for(unsigned int i=0;i<num;++i)
382        {
383            unsigned char val = *source;
384            source += 3;
385            *dest++ = val;
386            *dest++ = *source++;
387        }
388    }
389
390    virtual void RGBA_to_RGB(unsigned int num, unsigned char* source, unsigned char* dest) const
391    {
392        for(unsigned int i=0;i<num;++i)
393        {
394            *dest++ = *source++;
395            *dest++ = *source++;
396            *dest++ = *source++;
397            ++source;
398        }
399    }
400
401    virtual void RGBA_to_RGBA(unsigned int num, unsigned char* source, unsigned char* dest) const
402    {
403        for(unsigned int i=0;i<num;++i)
404        {
405            *dest++ = *source++;
406            *dest++ = *source++;
407            *dest++ = *source++;
408            *dest++ = *source++;
409        }
410    }
411
412     ///////////////////////////////////////////////////////////////////////////////
413    // BGR sources..
414    virtual void BGR_to_A(unsigned int num, unsigned char* source, unsigned char* dest) const
415    {
416        for(unsigned int i=0;i<num;++i)
417        {
418            unsigned char val = *source;
419            *dest++ = val;
420            source += 3;
421        }
422    }
423
424    virtual void BGR_to_LA(unsigned int num, unsigned char* source, unsigned char* dest) const
425    {
426        for(unsigned int i=0;i<num;++i)
427        {
428            unsigned char val = *source;
429            *dest++ = val;
430            *dest++ = val;
431            source += 3;
432        }
433    }
434
435    virtual void BGR_to_RGB(unsigned int num, unsigned char* source, unsigned char* dest) const
436    {
437        for(unsigned int i=0;i<num;++i)
438        {
439            unsigned char blue = *source++;
440            unsigned char green = *source++;
441            unsigned char red = *source++;
442            *dest++ = red;
443            *dest++ = green;
444            *dest++ = blue;
445        }
446    }
447
448    virtual void BGR_to_RGBA(unsigned int num, unsigned char* source, unsigned char* dest) const
449    {
450        for(unsigned int i=0;i<num;++i)
451        {
452            unsigned char blue = *source++;
453            unsigned char green = *source++;
454            unsigned char red = *source++;
455            unsigned char val = (unsigned char)((int(red)+int(green)+int(blue))/3);
456            *dest++ = red;
457            *dest++ = green;
458            *dest++ = blue;
459            *dest++ = val;
460        }
461    }
462
463    ///////////////////////////////////////////////////////////////////////////////
464    // BGRA sources..
465    virtual void BGRA_to_A(unsigned int num, unsigned char* source, unsigned char* dest) const
466    {
467        for(unsigned int i=0;i<num;++i)
468        {
469            source += 3;
470            *dest++ = *source++;
471        }
472    }
473
474    virtual void BGRA_to_LA(unsigned int num, unsigned char* source, unsigned char* dest) const
475    {
476        for(unsigned int i=0;i<num;++i)
477        {
478            unsigned char val = *source;
479            source += 3;
480            *dest++ = val;
481            *dest++ = *source++;
482        }
483    }
484
485    virtual void BGRA_to_RGB(unsigned int num, unsigned char* source, unsigned char* dest) const
486    {
487        for(unsigned int i=0;i<num;++i)
488        {
489            unsigned char blue = *source++;
490            unsigned char green = *source++;
491            unsigned char red = *source++;
492            *dest++ = red;
493            *dest++ = green;
494            *dest++ = blue;
495            ++source;
496        }
497    }
498
499    virtual void BGRA_to_RGBA(unsigned int num, unsigned char* source, unsigned char* dest) const
500    {
501        for(unsigned int i=0;i<num;++i)
502        {
503            unsigned char blue = *source++;
504            unsigned char green = *source++;
505            unsigned char red = *source++;
506            unsigned char alpha = *source++;
507            *dest++ = red;
508            *dest++ = green;
509            *dest++ = blue;
510            *dest++ = alpha;
511        }
512    }
513
514};
515
516osg::Image* createTexture3D(osg::ImageList& imageList,
517            unsigned int numComponentsDesired,
518            int s_maximumTextureSize,
519            int t_maximumTextureSize,
520            int r_maximumTextureSize,
521            bool resizeToPowerOfTwo)
522{
523    ProcessRow processRow;
524    int max_s = 0;
525    int max_t = 0;
526    unsigned int max_components = 0;
527    int total_r = 0;
528    osg::ImageList::iterator itr;
529    for(itr=imageList.begin();
530        itr!=imageList.end();
531        ++itr)
532    {
533        osg::Image* image = itr->get();
534        GLenum pixelFormat = image->getPixelFormat();
535        if (pixelFormat==GL_ALPHA ||
536            pixelFormat==GL_INTENSITY ||
537            pixelFormat==GL_LUMINANCE ||
538            pixelFormat==GL_LUMINANCE_ALPHA ||
539            pixelFormat==GL_RGB ||
540            pixelFormat==GL_RGBA ||
541            pixelFormat==GL_BGR ||
542            pixelFormat==GL_BGRA)
543        {
544            max_s = osg::maximum(image->s(), max_s);
545            max_t = osg::maximum(image->t(), max_t);
546            max_components = osg::maximum(osg::Image::computeNumComponents(pixelFormat), max_components);
547            total_r += image->r();
548        }
549        else
550        {
551            osg::notify(osg::NOTICE)<<"Image "<<image->getFileName()<<" has unsuitable pixel format 0x"<< std::hex<< pixelFormat << std::dec << std::endl;
552        }
553    }
554
555    if (max_components==3)
556    {
557        // change RGB to a RGBA
558        max_components = 4;
559    }
560
561    if (numComponentsDesired!=0) max_components = numComponentsDesired;
562
563    GLenum desiredPixelFormat = 0;
564    switch(max_components)
565    {
566    case(1):
567        osg::notify(osg::NOTICE)<<"desiredPixelFormat = GL_LUMINANCE" << std::endl;
568        desiredPixelFormat = GL_LUMINANCE;
569        break;
570    case(2):
571        osg::notify(osg::NOTICE)<<"desiredPixelFormat = GL_LUMINANCE_ALPHA" << std::endl;
572        desiredPixelFormat = GL_LUMINANCE_ALPHA;
573        break;
574    case(3):
575        osg::notify(osg::NOTICE)<<"desiredPixelFormat = GL_RGB" << std::endl;
576        desiredPixelFormat = GL_RGB;
577        break;
578    case(4):
579        osg::notify(osg::NOTICE)<<"desiredPixelFormat = GL_RGBA" << std::endl;
580        desiredPixelFormat = GL_RGBA;
581        break;
582    }
583    if (desiredPixelFormat==0) return 0;
584
585    // compute nearest powers of two for each axis.
586
587    int s_nearestPowerOfTwo = 1;
588    int t_nearestPowerOfTwo = 1;
589    int r_nearestPowerOfTwo = 1;
590
591    if (resizeToPowerOfTwo)
592    {
593        while(s_nearestPowerOfTwo<max_s && s_nearestPowerOfTwo<s_maximumTextureSize) s_nearestPowerOfTwo*=2;
594        while(t_nearestPowerOfTwo<max_t && t_nearestPowerOfTwo<t_maximumTextureSize) t_nearestPowerOfTwo*=2;
595        while(r_nearestPowerOfTwo<total_r && r_nearestPowerOfTwo<r_maximumTextureSize) r_nearestPowerOfTwo*=2;
596
597        osg::notify(osg::NOTICE)<<"max image width = "<<max_s<<"  nearest power of two = "<<s_nearestPowerOfTwo<<std::endl;
598        osg::notify(osg::NOTICE)<<"max image height = "<<max_t<<"  nearest power of two = "<<t_nearestPowerOfTwo<<std::endl;
599        osg::notify(osg::NOTICE)<<"max image depth = "<<total_r<<"  nearest power of two = "<<r_nearestPowerOfTwo<<std::endl;
600    }
601    else
602    {
603        s_nearestPowerOfTwo = max_s;
604        t_nearestPowerOfTwo = max_t;
605        r_nearestPowerOfTwo = total_r;
606    }
607
608    // now allocate the 3d texture;
609    osg::ref_ptr<osg::Image> image_3d = new osg::Image;
610    image_3d->allocateImage(s_nearestPowerOfTwo,t_nearestPowerOfTwo,r_nearestPowerOfTwo,
611                            desiredPixelFormat,GL_UNSIGNED_BYTE);
612
613
614    unsigned int r_offset = (total_r<r_nearestPowerOfTwo) ? r_nearestPowerOfTwo/2 - total_r/2 : 0;
615
616    int curr_dest_r = r_offset;
617
618    // copy across the values from the source images into the image_3d.
619    for(itr=imageList.begin();
620        itr!=imageList.end();
621        ++itr)
622    {
623        osg::Image* image = itr->get();
624        GLenum pixelFormat = image->getPixelFormat();
625        if (pixelFormat==GL_ALPHA ||
626            pixelFormat==GL_LUMINANCE ||
627            pixelFormat==GL_INTENSITY ||
628            pixelFormat==GL_LUMINANCE_ALPHA ||
629            pixelFormat==GL_RGB ||
630            pixelFormat==GL_RGBA ||
631            pixelFormat==GL_BGR ||
632            pixelFormat==GL_BGRA)
633        {
634
635            int num_r = osg::minimum(image->r(), (image_3d->r() - curr_dest_r));
636            int num_t = osg::minimum(image->t(), image_3d->t());
637            int num_s = osg::minimum(image->s(), image_3d->s());
638
639            unsigned int s_offset_dest = (image->s()<s_nearestPowerOfTwo) ? s_nearestPowerOfTwo/2 - image->s()/2 : 0;
640            unsigned int t_offset_dest = (image->t()<t_nearestPowerOfTwo) ? t_nearestPowerOfTwo/2 - image->t()/2 : 0;
641
642            for(int r=0;r<num_r;++r, ++curr_dest_r)
643            {
644                for(int t=0;t<num_t;++t)
645                {
646                    unsigned char* dest = image_3d->data(s_offset_dest,t+t_offset_dest,curr_dest_r);
647                    unsigned char* source = image->data(0,t,r);
648
649                    processRow(num_s, image->getPixelFormat(), source, image_3d->getPixelFormat(), dest);
650                }
651            }
652        }
653    }
654    return image_3d.release();
655}
656#endif
657
658struct ScaleOperator
659{
660    ScaleOperator():_scale(1.0f) {}
661    ScaleOperator(float scale):_scale(scale) {}
662    ScaleOperator(const ScaleOperator& so):_scale(so._scale) {}
663
664    ScaleOperator& operator = (const ScaleOperator& so) { _scale = so._scale; return *this; }
665
666    float _scale;
667
668    inline void luminance(float& l) const { l*= _scale; }
669    inline void alpha(float& a) const { a*= _scale; }
670    inline void luminance_alpha(float& l,float& a) const { l*= _scale; a*= _scale;  }
671    inline void rgb(float& r,float& g,float& b) const { r*= _scale; g*=_scale; b*=_scale; }
672    inline void rgba(float& r,float& g,float& b,float& a) const { r*= _scale; g*=_scale; b*=_scale; a*=_scale; }
673};
674
675struct RecordRowOperator
676{
677    RecordRowOperator(unsigned int num):_colours(num),_pos(0) {}
678
679    mutable std::vector<osg::Vec4>  _colours;
680    mutable unsigned int            _pos;
681
682    inline void luminance(float l) const { rgba(l,l,l,1.0f); }
683    inline void alpha(float a) const { rgba(1.0f,1.0f,1.0f,a); }
684    inline void luminance_alpha(float l,float a) const { rgba(l,l,l,a);  }
685    inline void rgb(float r,float g,float b) const { rgba(r,g,b,1.0f); }
686    inline void rgba(float r,float g,float b,float a) const { _colours[_pos++].set(r,g,b,a); }
687};
688
689struct WriteRowOperator
690{
691    WriteRowOperator():_pos(0) {}
692    WriteRowOperator(unsigned int num):_colours(num),_pos(0) {}
693
694    std::vector<osg::Vec4>  _colours;
695    mutable unsigned int    _pos;
696
697    inline void luminance(float& l) const { l = _colours[_pos++].r(); }
698    inline void alpha(float& a) const { a = _colours[_pos++].a(); }
699    inline void luminance_alpha(float& l,float& a) const { l = _colours[_pos].r(); a = _colours[_pos++].a(); }
700    inline void rgb(float& r,float& g,float& b) const { r = _colours[_pos].r(); g = _colours[_pos].g(); b = _colours[_pos].b(); }
701    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(); }
702};
703
704osg::Image* readRaw(int sizeX, int sizeY, int sizeZ, int numberBytesPerComponent, int numberOfComponents, const std::string& endian, const std::string& raw_filename)
705{
706    osgDB::ifstream fin(raw_filename.c_str(), std::ifstream::binary);
707    if (!fin) return 0;
708
709    GLenum pixelFormat;
710    switch(numberOfComponents)
711    {
712        case 1 : pixelFormat = GL_LUMINANCE; break;
713        case 2 : pixelFormat = GL_LUMINANCE_ALPHA; break;
714        case 3 : pixelFormat = GL_RGB; break;
715        case 4 : pixelFormat = GL_RGBA; break;
716        default :
717            osg::notify(osg::NOTICE)<<"Error: numberOfComponents="<<numberOfComponents<<" not supported, only 1,2,3 or 4 are supported."<<std::endl;
718            return 0;
719    }
720
721
722    GLenum dataType;
723    switch(numberBytesPerComponent)
724    {
725        case 1 : dataType = GL_UNSIGNED_BYTE; break;
726        case 2 : dataType = GL_UNSIGNED_SHORT; break;
727        case 4 : dataType = GL_UNSIGNED_INT; break;
728        default :
729            osg::notify(osg::NOTICE)<<"Error: numberBytesPerComponent="<<numberBytesPerComponent<<" not supported, only 1,2 or 4 are supported."<<std::endl;
730            return 0;
731    }
732
733    int s_maximumTextureSize=256, t_maximumTextureSize=256, r_maximumTextureSize=256;
734
735    int sizeS = sizeX;
736    int sizeT = sizeY;
737    int sizeR = sizeZ;
738    clampToNearestValidPowerOfTwo(sizeS, sizeT, sizeR, s_maximumTextureSize, t_maximumTextureSize, r_maximumTextureSize);
739
740    osg::ref_ptr<osg::Image> image = new osg::Image;
741    image->allocateImage(sizeS, sizeT, sizeR, pixelFormat, dataType);
742
743
744    bool endianSwap = (osg::getCpuByteOrder()==osg::BigEndian) ? (endian!="big") : (endian=="big");
745
746    unsigned int r_offset = (sizeZ<sizeR) ? sizeR/2 - sizeZ/2 : 0;
747
748    int offset = endianSwap ? numberBytesPerComponent : 0;
749    int delta = endianSwap ? -1 : 1;
750    for(int r=0;r<sizeZ;++r)
751    {
752        for(int t=0;t<sizeY;++t)
753        {
754            char* data = (char*) image->data(0,t,r+r_offset);
755            for(int s=0;s<sizeX;++s)
756            {
757                if (!fin) return 0;
758
759                for(int c=0;c<numberOfComponents;++c)
760                {
761                    char* ptr = data+offset;
762                    for(int b=0;b<numberBytesPerComponent;++b)
763                    {
764                        fin.read((char*)ptr, 1);
765                        ptr += delta;
766                    }
767                    data += numberBytesPerComponent;
768                }
769            }
770        }
771    }
772
773
774    // normalise texture
775    {
776        // compute range of values
777        osg::Vec4 minValue, maxValue;
778        osg::computeMinMax(image.get(), minValue, maxValue);
779        osg::modifyImage(image.get(),ScaleOperator(1.0f/maxValue.r()));
780    }
781
782
783    fin.close();
784
785    if (dataType!=GL_UNSIGNED_BYTE)
786    {
787        // need to convert to ubyte
788
789        osg::ref_ptr<osg::Image> new_image = new osg::Image;
790        new_image->allocateImage(sizeS, sizeT, sizeR, pixelFormat, GL_UNSIGNED_BYTE);
791
792        RecordRowOperator readOp(sizeS);
793        WriteRowOperator writeOp;
794
795        for(int r=0;r<sizeR;++r)
796        {
797            for(int t=0;t<sizeT;++t)
798            {
799                // reset the indices to beginning
800                readOp._pos = 0;
801                writeOp._pos = 0;
802
803                // read the pixels into readOp's _colour array
804                osg::readRow(sizeS, pixelFormat, dataType, image->data(0,t,r), readOp);
805
806                // pass readOp's _colour array contents over to writeOp (note this is just a pointer swap).
807                writeOp._colours.swap(readOp._colours);
808
809                osg::modifyRow(sizeS, pixelFormat, GL_UNSIGNED_BYTE, new_image->data(0,t,r), writeOp);
810
811                // return readOp's _colour array contents back to its rightful owner.
812                writeOp._colours.swap(readOp._colours);
813            }
814        }
815
816        image = new_image;
817    }
818
819    return image.release();
820
821
822}
823
824enum ColourSpaceOperation
825{
826    NO_COLOUR_SPACE_OPERATION,
827    MODULATE_ALPHA_BY_LUMINANCE,
828    MODULATE_ALPHA_BY_COLOUR,
829    REPLACE_ALPHA_WITH_LUMINANCE,
830    REPLACE_RGB_WITH_LUMINANCE
831};
832
833struct ModulateAlphaByColourOperator
834{
835    ModulateAlphaByColourOperator(const osg::Vec4& colour):_colour(colour) { _lum = _colour.length(); }
836
837    osg::Vec4 _colour;
838    float _lum;
839
840    inline void luminance(float&) const {}
841    inline void alpha(float&) const {}
842    inline void luminance_alpha(float& l,float& a) const { a*= l*_lum; }
843    inline void rgb(float&,float&,float&) const {}
844    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()); }
845};
846
847struct ReplaceAlphaWithLuminanceOperator
848{
849    ReplaceAlphaWithLuminanceOperator() {}
850
851    inline void luminance(float&) const {}
852    inline void alpha(float&) const {}
853    inline void luminance_alpha(float& l,float& a) const { a= l; }
854    inline void rgb(float&,float&,float&) const { }
855    inline void rgba(float& r,float& g,float& b,float& a) const { float l = (r+g+b)*0.3333333; a = l; }
856};
857
858osg::Image* doColourSpaceConversion(ColourSpaceOperation op, osg::Image* image, osg::Vec4& colour)
859{
860    switch(op)
861    {
862        case (MODULATE_ALPHA_BY_LUMINANCE):
863        {
864            std::cout<<"doing conversion MODULATE_ALPHA_BY_LUMINANCE"<<std::endl;
865            osg::modifyImage(image,ModulateAlphaByLuminanceOperator());
866            return image;
867        }
868        case (MODULATE_ALPHA_BY_COLOUR):
869        {
870            std::cout<<"doing conversion MODULATE_ALPHA_BY_COLOUR"<<std::endl;
871            osg::modifyImage(image,ModulateAlphaByColourOperator(colour));
872            return image;
873        }
874        case (REPLACE_ALPHA_WITH_LUMINANCE):
875        {
876            std::cout<<"doing conversion REPLACE_ALPHA_WITH_LUMINANCE"<<std::endl;
877            osg::modifyImage(image,ReplaceAlphaWithLuminanceOperator());
878            return image;
879        }
880        case (REPLACE_RGB_WITH_LUMINANCE):
881        {
882            std::cout<<"doing conversion REPLACE_ALPHA_WITH_LUMINANCE"<<std::endl;
883            osg::Image* newImage = new osg::Image;
884            newImage->allocateImage(image->s(), image->t(), image->r(), GL_LUMINANCE, image->getDataType());
885            osg::copyImage(image, 0, 0, 0, image->s(), image->t(), image->r(),
886                        newImage, 0, 0, 0, false);
887            return newImage;
888        }
889        default:
890            return image;
891    }
892}
893
894
895osg::TransferFunction1D* readTransferFunctionFile(const std::string& filename, float colorScale=1.0f)
896{
897    std::string foundFile = osgDB::findDataFile(filename);
898    if (foundFile.empty())
899    {
900        std::cout<<"Error: could not find transfer function file : "<<filename<<std::endl;
901        return 0;
902    }
903
904    std::cout<<"Reading transfer function "<<filename<<std::endl;
905
906    osg::TransferFunction1D::ColorMap colorMap;
907    osgDB::ifstream fin(foundFile.c_str());
908    while(fin)
909    {
910        float value, red, green, blue, alpha;
911        fin >> value >> red >> green >> blue >> alpha;
912        if (fin)
913        {
914            std::cout<<"value = "<<value<<" ("<<red<<", "<<green<<", "<<blue<<", "<<alpha<<")"<<std::endl;
915            colorMap[value] = osg::Vec4(red*colorScale,green*colorScale,blue*colorScale,alpha*colorScale);
916        }
917    }
918
919    if (colorMap.empty())
920    {
921        std::cout<<"Error: No values read from transfer function file: "<<filename<<std::endl;
922        return 0;
923    }
924
925    osg::TransferFunction1D* tf = new osg::TransferFunction1D;
926    tf->assign(colorMap);
927
928    return tf;
929}
930
931
932class TestSupportOperation: public osg::GraphicsOperation
933{
934public:
935
936    TestSupportOperation():
937        osg::GraphicsOperation("TestSupportOperation",false),
938        supported(true),
939        errorMessage(),
940        maximumTextureSize(256) {}
941
942    virtual void operator () (osg::GraphicsContext* gc)
943    {
944        OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mutex);
945
946        glGetIntegerv( GL_MAX_3D_TEXTURE_SIZE, &maximumTextureSize );
947
948        osg::notify(osg::NOTICE)<<"Max texture size="<<maximumTextureSize<<std::endl;
949    }
950
951    OpenThreads::Mutex  mutex;
952    bool                supported;
953    std::string         errorMessage;
954    GLint               maximumTextureSize;
955};
956
957class DraggerVolumeTileCallback : public osgManipulator::DraggerCallback
958{
959public:
960
961    DraggerVolumeTileCallback(osgVolume::VolumeTile* volume, osgVolume::Locator* locator):
962        _volume(volume),
963        _locator(locator) {}
964
965
966    virtual bool receive(const osgManipulator::MotionCommand& command);
967
968
969    osg::observer_ptr<osgVolume::VolumeTile>    _volume;
970    osg::ref_ptr<osgVolume::Locator>            _locator;
971
972    osg::Matrix _startMotionMatrix;
973
974    osg::Matrix _localToWorld;
975    osg::Matrix _worldToLocal;
976
977};
978
979bool DraggerVolumeTileCallback::receive(const osgManipulator::MotionCommand& command)
980{
981    if (!_locator) return false;
982
983    switch (command.getStage())
984    {
985        case osgManipulator::MotionCommand::START:
986        {
987            // Save the current matrix
988            _startMotionMatrix = _locator->getTransform();
989
990            // Get the LocalToWorld and WorldToLocal matrix for this node.
991            osg::NodePath nodePathToRoot;
992            osgManipulator::computeNodePathToRoot(*_volume,nodePathToRoot);
993            _localToWorld = _startMotionMatrix * osg::computeLocalToWorld(nodePathToRoot);
994            _worldToLocal = osg::Matrix::inverse(_localToWorld);
995
996            return true;
997        }
998        case osgManipulator::MotionCommand::MOVE:
999        {
1000            // Transform the command's motion matrix into local motion matrix.
1001            osg::Matrix localMotionMatrix = _localToWorld * command.getWorldToLocal()
1002                                            * command.getMotionMatrix()
1003                                            * command.getLocalToWorld() * _worldToLocal;
1004
1005            // Transform by the localMotionMatrix
1006            _locator->setTransform(localMotionMatrix * _startMotionMatrix);
1007
1008            // osg::notify(osg::NOTICE)<<"New locator matrix "<<_locator->getTransform()<<std::endl;
1009
1010            return true;
1011        }
1012        case osgManipulator::MotionCommand::FINISH:
1013        {
1014            return true;
1015        }
1016        case osgManipulator::MotionCommand::NONE:
1017        default:
1018            return false;
1019    }
1020}
1021
1022int main( int argc, char **argv )
1023{
1024    // use an ArgumentParser object to manage the program arguments.
1025    osg::ArgumentParser arguments(&argc,argv);
1026
1027    // set up the usage document, in case we need to print out how to use this program.
1028    arguments.getApplicationUsage()->setDescription(arguments.getApplicationName()+" is the example which demonstrates use of 3D textures.");
1029    arguments.getApplicationUsage()->setCommandLineUsage(arguments.getApplicationName()+" [options] filename ...");
1030    arguments.getApplicationUsage()->addCommandLineOption("-h or --help","Display this information");
1031    arguments.getApplicationUsage()->addCommandLineOption("--images [filenames]","Specify a stack of 2d images to build the 3d volume from.");
1032    arguments.getApplicationUsage()->addCommandLineOption("--shader","Use OpenGL Shading Language. (default)");
1033    arguments.getApplicationUsage()->addCommandLineOption("--no-shader","Disable use of OpenGL Shading Language.");
1034    arguments.getApplicationUsage()->addCommandLineOption("--gpu-tf","Aply the transfer function on the GPU. (default)");
1035    arguments.getApplicationUsage()->addCommandLineOption("--cpu-tf","Apply the transfer function on the CPU.");
1036    arguments.getApplicationUsage()->addCommandLineOption("--mip","Use Maximum Intensity Projection (MIP) filtering.");
1037    arguments.getApplicationUsage()->addCommandLineOption("--isosurface","Use Iso surface render.");
1038    arguments.getApplicationUsage()->addCommandLineOption("--light","Use normals computed on the GPU to render a lit volume.");
1039    arguments.getApplicationUsage()->addCommandLineOption("-n","Use normals computed on the GPU to render a lit volume.");
1040    arguments.getApplicationUsage()->addCommandLineOption("--xSize <size>","Relative width of rendered brick.");
1041    arguments.getApplicationUsage()->addCommandLineOption("--ySize <size>","Relative length of rendered brick.");
1042    arguments.getApplicationUsage()->addCommandLineOption("--zSize <size>","Relative height of rendered brick.");
1043    arguments.getApplicationUsage()->addCommandLineOption("--maxTextureSize <size>","Set the texture maximum resolution in the s,t,r (x,y,z) dimensions.");
1044    arguments.getApplicationUsage()->addCommandLineOption("--s_maxTextureSize <size>","Set the texture maximum resolution in the s (x) dimension.");
1045    arguments.getApplicationUsage()->addCommandLineOption("--t_maxTextureSize <size>","Set the texture maximum resolution in the t (y) dimension.");
1046    arguments.getApplicationUsage()->addCommandLineOption("--r_maxTextureSize <size>","Set the texture maximum resolution in the r (z) dimension.");
1047    arguments.getApplicationUsage()->addCommandLineOption("--compressed","Enable the usage of compressed textures.");
1048    arguments.getApplicationUsage()->addCommandLineOption("--compressed-arb","Enable the usage of OpenGL ARB compressed textures.");
1049    arguments.getApplicationUsage()->addCommandLineOption("--compressed-dxt1","Enable the usage of S3TC DXT1 compressed textures.");
1050    arguments.getApplicationUsage()->addCommandLineOption("--compressed-dxt3","Enable the usage of S3TC DXT3 compressed textures.");
1051    arguments.getApplicationUsage()->addCommandLineOption("--compressed-dxt5","Enable the usage of S3TC DXT5 compressed textures.");
1052    arguments.getApplicationUsage()->addCommandLineOption("--modulate-alpha-by-luminance","For each pixel multiply the alpha value by the luminance.");
1053    arguments.getApplicationUsage()->addCommandLineOption("--replace-alpha-with-luminance","For each pixel set the alpha value to the luminance.");
1054    arguments.getApplicationUsage()->addCommandLineOption("--replace-rgb-with-luminance","For each rgb pixel convert to the luminance.");
1055    arguments.getApplicationUsage()->addCommandLineOption("--num-components <num>","Set the number of components to in he target image.");
1056    arguments.getApplicationUsage()->addCommandLineOption("--no-rescale","Disable the rescaling of the pixel data to 0.0 to 1.0 range");
1057    arguments.getApplicationUsage()->addCommandLineOption("--rescale","Enable the rescale of the pixel data to 0.0 to 1.0 range (default).");
1058    arguments.getApplicationUsage()->addCommandLineOption("--shift-min-to-zero","Shift the pixel data so min value is 0.0.");
1059    arguments.getApplicationUsage()->addCommandLineOption("--sequence-length <num>","Set the length of time that a sequence of images with run for.");
1060    arguments.getApplicationUsage()->addCommandLineOption("--sd <num>","Short hand for --sequence-length");
1061//    arguments.getApplicationUsage()->addCommandLineOption("--raw <sizeX> <sizeY> <sizeZ> <numberBytesPerComponent> <numberOfComponents> <endian> <filename>","read a raw image data");
1062
1063    // construct the viewer.
1064    osgViewer::Viewer viewer(arguments);
1065
1066    // add the window size toggle handler
1067    viewer.addEventHandler(new osgViewer::WindowSizeHandler);
1068
1069    {
1070        osg::ref_ptr<osgGA::KeySwitchMatrixManipulator> keyswitchManipulator = new osgGA::KeySwitchMatrixManipulator;
1071
1072        keyswitchManipulator->addMatrixManipulator( '1', "Trackball", new osgGA::TrackballManipulator() );
1073
1074        osgGA::FlightManipulator* flightManipulator = new osgGA::FlightManipulator();
1075        flightManipulator->setYawControlMode(osgGA::FlightManipulator::NO_AUTOMATIC_YAW);
1076        keyswitchManipulator->addMatrixManipulator( '2', "Flight", flightManipulator );
1077
1078        viewer.setCameraManipulator( keyswitchManipulator.get() );
1079    }
1080
1081    // add the stats handler
1082    viewer.addEventHandler(new osgViewer::StatsHandler);
1083
1084    viewer.getCamera()->setClearColor(osg::Vec4(0.0f,0.0f,0.0f,0.0f));
1085
1086    // if user request help write it out to cout.
1087    if (arguments.read("-h") || arguments.read("--help"))
1088    {
1089        arguments.getApplicationUsage()->write(std::cout);
1090        return 1;
1091    }
1092
1093    std::string outputFile;
1094    while (arguments.read("-o",outputFile)) {}
1095
1096
1097
1098    osg::ref_ptr<osg::TransferFunction1D> transferFunction;
1099    std::string tranferFunctionFile;
1100    while (arguments.read("--tf",tranferFunctionFile))
1101    {
1102        transferFunction = readTransferFunctionFile(tranferFunctionFile);
1103    }
1104    while (arguments.read("--tf-255",tranferFunctionFile))
1105    {
1106        transferFunction = readTransferFunctionFile(tranferFunctionFile,1.0f/255.0f);
1107    }
1108
1109    while(arguments.read("--test"))
1110    {
1111        transferFunction = new osg::TransferFunction1D;
1112        transferFunction->setColor(0.0, osg::Vec4(1.0,0.0,0.0,0.0));
1113        transferFunction->setColor(0.5, osg::Vec4(1.0,1.0,0.0,0.5));
1114        transferFunction->setColor(1.0, osg::Vec4(0.0,0.0,1.0,1.0));
1115    }
1116
1117    while(arguments.read("--test2"))
1118    {
1119        transferFunction = new osg::TransferFunction1D;
1120        transferFunction->setColor(0.0, osg::Vec4(1.0,0.0,0.0,0.0));
1121        transferFunction->setColor(0.5, osg::Vec4(1.0,1.0,0.0,0.5));
1122        transferFunction->setColor(1.0, osg::Vec4(0.0,0.0,1.0,1.0));
1123        transferFunction->assign(transferFunction->getColorMap());
1124    }
1125
1126    {
1127        // deprecated options
1128
1129        bool invalidOption = false;
1130
1131        unsigned int numSlices=500;
1132        while (arguments.read("-s",numSlices)) { OSG_NOTICE<<"Warning: -s option no longer supported."<<std::endl; invalidOption = true; }
1133
1134        float sliceEnd=1.0f;
1135        while (arguments.read("--clip",sliceEnd)) { OSG_NOTICE<<"Warning: --clip option no longer supported."<<std::endl; invalidOption = true; }
1136
1137
1138        if (invalidOption) return 1;
1139    }
1140
1141    float xMultiplier=1.0f;
1142    while (arguments.read("--xMultiplier",xMultiplier)) {}
1143
1144    float yMultiplier=1.0f;
1145    while (arguments.read("--yMultiplier",yMultiplier)) {}
1146
1147    float zMultiplier=1.0f;
1148    while (arguments.read("--zMultiplier",zMultiplier)) {}
1149
1150
1151    float alphaFunc=0.02f;
1152    while (arguments.read("--alphaFunc",alphaFunc)) {}
1153
1154
1155
1156    ShadingModel shadingModel = Standard;
1157    while(arguments.read("--mip")) shadingModel =  MaximumIntensityProjection;
1158
1159    while (arguments.read("--isosurface") || arguments.read("--iso-surface")) shadingModel = Isosurface;
1160
1161    while (arguments.read("--light") || arguments.read("-n")) shadingModel = Light;
1162
1163    float xSize=0.0f, ySize=0.0f, zSize=0.0f;
1164    while (arguments.read("--xSize",xSize)) {}
1165    while (arguments.read("--ySize",ySize)) {}
1166    while (arguments.read("--zSize",zSize)) {}
1167
1168    osg::ref_ptr<TestSupportOperation> testSupportOperation = new TestSupportOperation;
1169    viewer.setRealizeOperation(testSupportOperation.get());
1170
1171    viewer.realize();
1172
1173    int maximumTextureSize = testSupportOperation->maximumTextureSize;
1174    int s_maximumTextureSize = maximumTextureSize;
1175    int t_maximumTextureSize = maximumTextureSize;
1176    int r_maximumTextureSize = maximumTextureSize;
1177    while(arguments.read("--maxTextureSize",maximumTextureSize))
1178    {
1179        s_maximumTextureSize = maximumTextureSize;
1180        t_maximumTextureSize = maximumTextureSize;
1181        r_maximumTextureSize = maximumTextureSize;
1182    }
1183    while(arguments.read("--s_maxTextureSize",s_maximumTextureSize)) {}
1184    while(arguments.read("--t_maxTextureSize",t_maximumTextureSize)) {}
1185    while(arguments.read("--r_maxTextureSize",r_maximumTextureSize)) {}
1186
1187    osg::Texture::InternalFormatMode internalFormatMode = osg::Texture::USE_IMAGE_DATA_FORMAT;
1188    while(arguments.read("--compressed") || arguments.read("--compressed-arb")) { internalFormatMode = osg::Texture::USE_ARB_COMPRESSION; }
1189
1190    while(arguments.read("--compressed-dxt1")) { internalFormatMode = osg::Texture::USE_S3TC_DXT1_COMPRESSION; }
1191    while(arguments.read("--compressed-dxt3")) { internalFormatMode = osg::Texture::USE_S3TC_DXT3_COMPRESSION; }
1192    while(arguments.read("--compressed-dxt5")) { internalFormatMode = osg::Texture::USE_S3TC_DXT5_COMPRESSION; }
1193
1194
1195    // set up colour space operation.
1196    ColourSpaceOperation colourSpaceOperation = NO_COLOUR_SPACE_OPERATION;
1197    osg::Vec4 colourModulate(0.25f,0.25f,0.25f,0.25f);
1198    while(arguments.read("--modulate-alpha-by-luminance")) { colourSpaceOperation = MODULATE_ALPHA_BY_LUMINANCE; }
1199    while(arguments.read("--modulate-alpha-by-colour", colourModulate.x(),colourModulate.y(),colourModulate.z(),colourModulate.w() )) { colourSpaceOperation = MODULATE_ALPHA_BY_COLOUR; }
1200    while(arguments.read("--replace-alpha-with-luminance")) { colourSpaceOperation = REPLACE_ALPHA_WITH_LUMINANCE; }
1201    while(arguments.read("--replace-rgb-with-luminance")) { colourSpaceOperation = REPLACE_RGB_WITH_LUMINANCE; }
1202
1203
1204    enum RescaleOperation
1205    {
1206        NO_RESCALE,
1207        RESCALE_TO_ZERO_TO_ONE_RANGE,
1208        SHIFT_MIN_TO_ZERO
1209    };
1210
1211    RescaleOperation rescaleOperation = RESCALE_TO_ZERO_TO_ONE_RANGE;
1212    while(arguments.read("--no-rescale")) rescaleOperation = NO_RESCALE;
1213    while(arguments.read("--rescale")) rescaleOperation = RESCALE_TO_ZERO_TO_ONE_RANGE;
1214    while(arguments.read("--shift-min-to-zero")) rescaleOperation = SHIFT_MIN_TO_ZERO;
1215
1216
1217    bool resizeToPowerOfTwo = false;
1218
1219    unsigned int numComponentsDesired = 0;
1220    while(arguments.read("--num-components", numComponentsDesired)) {}
1221
1222    bool useManipulator = false;
1223    while(arguments.read("--manipulator") || arguments.read("-m")) { useManipulator = true; }
1224
1225
1226    bool useShader = true;
1227    while(arguments.read("--shader")) { useShader = true; }
1228    while(arguments.read("--no-shader")) { useShader = false; }
1229
1230    bool gpuTransferFunction = true;
1231    while(arguments.read("--gpu-tf")) { gpuTransferFunction = true; }
1232    while(arguments.read("--cpu-tf")) { gpuTransferFunction = false; }
1233
1234    double sequenceLength = 10.0;
1235    while(arguments.read("--sequence-duration", sequenceLength) ||
1236        arguments.read("--sd", sequenceLength)) {}
1237
1238    typedef std::list< osg::ref_ptr<osg::Image> > Images;
1239    Images images;
1240
1241
1242    std::string vh_filename;
1243    while (arguments.read("--vh", vh_filename))
1244    {
1245        std::string raw_filename, transfer_filename;
1246        int xdim(0), ydim(0), zdim(0);
1247
1248        osgDB::ifstream header(vh_filename.c_str());
1249        if (header)
1250        {
1251            header >> raw_filename >> transfer_filename >> xdim >> ydim >> zdim >> xSize >> ySize >> zSize;
1252        }
1253
1254        if (xdim*ydim*zdim==0)
1255        {
1256            std::cout<<"Error in reading volume header "<<vh_filename<<std::endl;
1257            return 1;
1258        }
1259
1260        if (!raw_filename.empty())
1261        {
1262            images.push_back(readRaw(xdim, ydim, zdim, 1, 1, "little", raw_filename));
1263        }
1264
1265        if (!transfer_filename.empty())
1266        {
1267            osgDB::ifstream fin(transfer_filename.c_str());
1268            if (fin)
1269            {
1270                osg::TransferFunction1D::ColorMap colorMap;
1271                float value = 0.0;
1272                while(fin && value<=1.0)
1273                {
1274                    float red, green, blue, alpha;
1275                    fin >> red >> green >> blue >> alpha;
1276                    if (fin)
1277                    {
1278                        colorMap[value] = osg::Vec4(red/255.0f,green/255.0f,blue/255.0f,alpha/255.0f);
1279                        std::cout<<"value = "<<value<<" ("<<red<<", "<<green<<", "<<blue<<", "<<alpha<<")";
1280                        std::cout<<"  ("<<colorMap[value]<<")"<<std::endl;
1281                    }
1282                    value += 1/255.0;
1283                }
1284
1285                if (colorMap.empty())
1286                {
1287                    std::cout<<"Error: No values read from transfer function file: "<<transfer_filename<<std::endl;
1288                    return 0;
1289                }
1290
1291                transferFunction = new osg::TransferFunction1D;
1292                transferFunction->assign(colorMap);
1293            }
1294        }
1295
1296    }
1297
1298
1299    int sizeX, sizeY, sizeZ, numberBytesPerComponent, numberOfComponents;
1300    std::string endian, raw_filename;
1301    while (arguments.read("--raw", sizeX, sizeY, sizeZ, numberBytesPerComponent, numberOfComponents, endian, raw_filename))
1302    {
1303        images.push_back(readRaw(sizeX, sizeY, sizeZ, numberBytesPerComponent, numberOfComponents, endian, raw_filename));
1304    }
1305
1306    int images_pos = arguments.find("--images");
1307    if (images_pos>=0)
1308    {
1309        osg::ImageList imageList;
1310        int pos=images_pos+1;
1311        for(;pos<arguments.argc() && !arguments.isOption(pos);++pos)
1312        {
1313            std::string arg(arguments[pos]);
1314            if (arg.find('*') != std::string::npos)
1315            {
1316                osgDB::DirectoryContents contents = osgDB::expandWildcardsInFilename(arg);
1317                for (unsigned int i = 0; i < contents.size(); ++i)
1318                {
1319                    osg::Image *image = osgDB::readImageFile( contents[i] );
1320
1321                    if(image)
1322                    {
1323                        OSG_NOTICE<<"Read osg::Image FileName::"<<image->getFileName()<<", pixelFormat=0x"<<std::hex<<image->getPixelFormat()<<std::dec<<", s="<<image->s()<<", t="<<image->t()<<", r="<<image->r()<<std::endl;
1324                        imageList.push_back(image);
1325                    }
1326                }
1327            }
1328            else
1329            {
1330                // not an option so assume string is a filename.
1331                osg::Image *image = osgDB::readImageFile( arguments[pos] );
1332
1333                if(image)
1334                {
1335                    OSG_NOTICE<<"Read osg::Image FileName::"<<image->getFileName()<<", pixelFormat=0x"<<std::hex<<image->getPixelFormat()<<std::dec<<", s="<<image->s()<<", t="<<image->t()<<", r="<<image->r()<<std::endl;
1336                    imageList.push_back(image);
1337                }
1338            }
1339        }
1340
1341        arguments.remove(images_pos, pos-images_pos);
1342
1343        // pack the textures into a single texture.
1344        osg::Image* image = createTexture3D(imageList, numComponentsDesired, s_maximumTextureSize, t_maximumTextureSize, r_maximumTextureSize, resizeToPowerOfTwo);
1345        if (image)
1346        {
1347            images.push_back(image);
1348        }
1349    }
1350
1351
1352    // any option left unread are converted into errors to write out later.
1353    arguments.reportRemainingOptionsAsUnrecognized();
1354
1355    // report any errors if they have occurred when parsing the program arguments.
1356    if (arguments.errors())
1357    {
1358        arguments.writeErrorMessages(std::cout);
1359        return 1;
1360    }
1361
1362
1363    // assume remaining arguments are file names of textures.
1364    for(int pos=1;pos<arguments.argc();++pos)
1365    {
1366        if (!arguments.isOption(pos))
1367        {
1368            std::string filename = arguments[pos];
1369            if (osgDB::getLowerCaseFileExtension(filename)=="dicom")
1370            {
1371                // not an option so assume string is a filename.
1372                osg::Image* image = osgDB::readImageFile(filename);
1373                if (image)
1374                {
1375                    images.push_back(image);
1376                }
1377            }
1378            else
1379            {
1380                osgDB::FileType fileType = osgDB::fileType(filename);
1381                if (fileType == osgDB::FILE_NOT_FOUND)
1382                {
1383                    filename = osgDB::findDataFile(filename);
1384                    fileType = osgDB::fileType(filename);
1385                }
1386
1387                if (fileType == osgDB::DIRECTORY)
1388                {
1389                    osg::Image* image = osgDB::readImageFile(filename+".dicom");
1390                    if (image) images.push_back(image);
1391                }
1392                else if (fileType == osgDB::REGULAR_FILE)
1393                {
1394                    // not an option so assume string is a filename.
1395                    osg::Image* image = osgDB::readImageFile( filename );
1396                    if (image) images.push_back(image);
1397                }
1398                else
1399                {
1400                    osg::notify(osg::NOTICE)<<"Error: could not find file: "<<filename<<std::endl;
1401                    return 1;
1402                }
1403            }
1404        }
1405    }
1406
1407    if (images.empty())
1408    {
1409        std::cout<<"No model loaded, please specify and volumetric image file on the command line."<<std::endl;
1410        return 1;
1411    }
1412
1413
1414    Images::iterator sizeItr = images.begin();
1415    int image_s = (*sizeItr)->s();
1416    int image_t = (*sizeItr)->t();
1417    int image_r = (*sizeItr)->r();
1418    ++sizeItr;
1419
1420    for(;sizeItr != images.end(); ++sizeItr)
1421    {
1422        if ((*sizeItr)->s() != image_s ||
1423            (*sizeItr)->t() != image_t ||
1424            (*sizeItr)->r() != image_r)
1425        {
1426            std::cout<<"Images in sequence are not of the same dimensions."<<std::endl;
1427            return 1;
1428        }
1429    }
1430
1431
1432    osg::ref_ptr<osgVolume::ImageDetails> details = dynamic_cast<osgVolume::ImageDetails*>(images.front()->getUserData());
1433    osg::ref_ptr<osg::RefMatrix> matrix = details ? details->getMatrix() : dynamic_cast<osg::RefMatrix*>(images.front()->getUserData());
1434
1435    if (!matrix)
1436    {
1437        if (xSize==0.0) xSize = static_cast<float>(image_s);
1438        if (ySize==0.0) ySize = static_cast<float>(image_t);
1439        if (zSize==0.0) zSize = static_cast<float>(image_r);
1440
1441        matrix = new osg::RefMatrix(xSize, 0.0,   0.0,   0.0,
1442                                    0.0,   ySize, 0.0,   0.0,
1443                                    0.0,   0.0,   zSize, 0.0,
1444                                    0.0,   0.0,   0.0,   1.0);
1445    }
1446
1447
1448    if (xMultiplier!=1.0 || yMultiplier!=1.0 || zMultiplier!=1.0)
1449    {
1450        matrix->postMultScale(osg::Vec3d(fabs(xMultiplier), fabs(yMultiplier), fabs(zMultiplier)));
1451    }
1452
1453    osg::Vec4 minValue(FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX);
1454    osg::Vec4 maxValue(-FLT_MAX, -FLT_MAX, -FLT_MAX, -FLT_MAX);
1455    bool computeMinMax = false;
1456    for(Images::iterator itr = images.begin();
1457        itr != images.end();
1458        ++itr)
1459    {
1460        osg::Vec4 localMinValue, localMaxValue;
1461        if (osg::computeMinMax(itr->get(), localMinValue, localMaxValue))
1462        {
1463            if (localMinValue.r()<minValue.r()) minValue.r() = localMinValue.r();
1464            if (localMinValue.g()<minValue.g()) minValue.g() = localMinValue.g();
1465            if (localMinValue.b()<minValue.b()) minValue.b() = localMinValue.b();
1466            if (localMinValue.a()<minValue.a()) minValue.a() = localMinValue.a();
1467
1468            if (localMaxValue.r()>maxValue.r()) maxValue.r() = localMaxValue.r();
1469            if (localMaxValue.g()>maxValue.g()) maxValue.g() = localMaxValue.g();
1470            if (localMaxValue.b()>maxValue.b()) maxValue.b() = localMaxValue.b();
1471            if (localMaxValue.a()>maxValue.a()) maxValue.a() = localMaxValue.a();
1472
1473            osg::notify(osg::NOTICE)<<"  ("<<localMinValue<<") ("<<localMaxValue<<") "<<(*itr)->getFileName()<<std::endl;
1474
1475            computeMinMax = true;
1476        }
1477    }
1478
1479    if (computeMinMax)
1480    {
1481        osg::notify(osg::NOTICE)<<"Min value "<<minValue<<std::endl;
1482        osg::notify(osg::NOTICE)<<"Max value "<<maxValue<<std::endl;
1483
1484        float minComponent = minValue[0];
1485        minComponent = osg::minimum(minComponent,minValue[1]);
1486        minComponent = osg::minimum(minComponent,minValue[2]);
1487        minComponent = osg::minimum(minComponent,minValue[3]);
1488
1489        float maxComponent = maxValue[0];
1490        maxComponent = osg::maximum(maxComponent,maxValue[1]);
1491        maxComponent = osg::maximum(maxComponent,maxValue[2]);
1492        maxComponent = osg::maximum(maxComponent,maxValue[3]);
1493
1494#if 0
1495        switch(rescaleOperation)
1496        {
1497            case(NO_RESCALE):
1498                break;
1499
1500            case(RESCALE_TO_ZERO_TO_ONE_RANGE):
1501            {
1502                float scale = 0.99f/(maxComponent-minComponent);
1503                float offset = -minComponent * scale;
1504
1505                for(Images::iterator itr = images.begin();
1506                    itr != images.end();
1507                    ++itr)
1508                {
1509                    osg::offsetAndScaleImage(itr->get(),
1510                        osg::Vec4(offset, offset, offset, offset),
1511                        osg::Vec4(scale, scale, scale, scale));
1512                }
1513                break;
1514            }
1515            case(SHIFT_MIN_TO_ZERO):
1516            {
1517                float offset = -minComponent;
1518
1519                for(Images::iterator itr = images.begin();
1520                    itr != images.end();
1521                    ++itr)
1522                {
1523                    osg::offsetAndScaleImage(itr->get(),
1524                        osg::Vec4(offset, offset, offset, offset),
1525                        osg::Vec4(1.0f, 1.0f, 1.0f, 1.0f));
1526                }
1527                break;
1528            }
1529        };
1530#endif
1531    }
1532
1533
1534    if (colourSpaceOperation!=NO_COLOUR_SPACE_OPERATION)
1535    {
1536        for(Images::iterator itr = images.begin();
1537            itr != images.end();
1538            ++itr)
1539        {
1540            (*itr) = doColourSpaceConversion(colourSpaceOperation, itr->get(), colourModulate);
1541        }
1542    }
1543
1544    if (!gpuTransferFunction && transferFunction.valid())
1545    {
1546        for(Images::iterator itr = images.begin();
1547            itr != images.end();
1548            ++itr)
1549        {
1550            *itr = osgVolume::applyTransferFunction(itr->get(), transferFunction.get());
1551        }
1552    }
1553
1554    osg::ref_ptr<osg::Image> image_3d = 0;
1555
1556    if (images.size()==1)
1557    {
1558        osg::notify(osg::NOTICE)<<"Single image "<<images.size()<<" volumes."<<std::endl;
1559        image_3d = images.front();
1560    }
1561    else
1562    {
1563        osg::notify(osg::NOTICE)<<"Creating sequence of "<<images.size()<<" volumes."<<std::endl;
1564
1565        osg::ref_ptr<osg::ImageSequence> imageSequence = new osg::ImageSequence;
1566        imageSequence->setLength(sequenceLength);
1567        image_3d = imageSequence.get();
1568        for(Images::iterator itr = images.begin();
1569            itr != images.end();
1570            ++itr)
1571        {
1572            imageSequence->addImage(itr->get());
1573        }
1574        imageSequence->play();
1575    }
1576
1577    osg::ref_ptr<osgVolume::Volume> volume = new osgVolume::Volume;
1578    osg::ref_ptr<osgVolume::VolumeTile> tile = new osgVolume::VolumeTile;
1579    volume->addChild(tile.get());
1580
1581    osg::ref_ptr<osgVolume::ImageLayer> layer = new osgVolume::ImageLayer(image_3d.get());
1582
1583    if (details)
1584    {
1585        layer->setTexelOffset(details->getTexelOffset());
1586        layer->setTexelScale(details->getTexelScale());
1587    }
1588
1589    switch(rescaleOperation)
1590    {
1591        case(NO_RESCALE):
1592            break;
1593
1594        case(RESCALE_TO_ZERO_TO_ONE_RANGE):
1595        {
1596            layer->rescaleToZeroToOneRange();
1597            break;
1598        }
1599        case(SHIFT_MIN_TO_ZERO):
1600        {
1601            layer->translateMinToZero();
1602            break;
1603        }
1604    };
1605
1606    if (xMultiplier<0.0 || yMultiplier<0.0 || zMultiplier<0.0)
1607    {
1608        layer->setLocator(new osgVolume::Locator(
1609            osg::Matrix::translate(xMultiplier<0.0 ? -1.0 : 0.0, yMultiplier<0.0 ? -1.0 : 0.0, zMultiplier<0.0 ? -1.0 : 0.0) *
1610            osg::Matrix::scale(xMultiplier<0.0 ? -1.0 : 1.0, yMultiplier<0.0 ? -1.0 : 1.0, zMultiplier<0.0 ? -1.0 : 1.0) *
1611            (*matrix)
1612            ));;
1613    }
1614    else
1615    {
1616        layer->setLocator(new osgVolume::Locator(*matrix));
1617    }
1618    tile->setLocator(new osgVolume::Locator(*matrix));
1619
1620    tile->setLayer(layer.get());
1621
1622    tile->setEventCallback(new osgVolume::PropertyAdjustmentCallback());
1623
1624    if (useShader)
1625    {
1626
1627        osgVolume::SwitchProperty* sp = new osgVolume::SwitchProperty;
1628        sp->setActiveProperty(0);
1629
1630        osgVolume::AlphaFuncProperty* ap = new osgVolume::AlphaFuncProperty(alphaFunc);
1631        osgVolume::SampleDensityProperty* sd = new osgVolume::SampleDensityProperty(0.005);
1632        osgVolume::SampleDensityWhenMovingProperty* sdwm = new osgVolume::SampleDensityWhenMovingProperty(0.02);
1633        osgVolume::TransparencyProperty* tp = new osgVolume::TransparencyProperty(1.0);
1634        osgVolume::TransferFunctionProperty* tfp = transferFunction.valid() ? new osgVolume::TransferFunctionProperty(transferFunction.get()) : 0;
1635
1636        {
1637            // Standard
1638            osgVolume::CompositeProperty* cp = new osgVolume::CompositeProperty;
1639            cp->addProperty(ap);
1640            cp->addProperty(sd);
1641            cp->addProperty(sdwm);
1642            cp->addProperty(tp);
1643            if (tfp) cp->addProperty(tfp);
1644
1645            sp->addProperty(cp);
1646        }
1647
1648        {
1649            // Light
1650            osgVolume::CompositeProperty* cp = new osgVolume::CompositeProperty;
1651            cp->addProperty(ap);
1652            cp->addProperty(sd);
1653            cp->addProperty(tp);
1654            cp->addProperty(new osgVolume::LightingProperty);
1655            if (tfp) cp->addProperty(tfp);
1656
1657            sp->addProperty(cp);
1658        }
1659
1660        {
1661            // Isosurface
1662            osgVolume::CompositeProperty* cp = new osgVolume::CompositeProperty;
1663            cp->addProperty(sd);
1664            cp->addProperty(tp);
1665            cp->addProperty(new osgVolume::IsoSurfaceProperty(alphaFunc));
1666            if (tfp) cp->addProperty(tfp);
1667
1668            sp->addProperty(cp);
1669        }
1670
1671        {
1672            // MaximumIntensityProjection
1673            osgVolume::CompositeProperty* cp = new osgVolume::CompositeProperty;
1674            cp->addProperty(ap);
1675            cp->addProperty(sd);
1676            cp->addProperty(tp);
1677            cp->addProperty(new osgVolume::MaximumIntensityProjectionProperty);
1678            if (tfp) cp->addProperty(tfp);
1679
1680            sp->addProperty(cp);
1681        }
1682
1683        switch(shadingModel)
1684        {
1685            case(Standard):                     sp->setActiveProperty(0); break;
1686            case(Light):                        sp->setActiveProperty(1); break;
1687            case(Isosurface):                   sp->setActiveProperty(2); break;
1688            case(MaximumIntensityProjection):   sp->setActiveProperty(3); break;
1689        }
1690        layer->addProperty(sp);
1691
1692
1693        tile->setVolumeTechnique(new osgVolume::RayTracedTechnique);
1694    }
1695    else
1696    {
1697        layer->addProperty(new osgVolume::AlphaFuncProperty(alphaFunc));
1698        tile->setVolumeTechnique(new osgVolume::FixedFunctionTechnique);
1699    }
1700
1701    if (!outputFile.empty())
1702    {
1703        std::string ext = osgDB::getFileExtension(outputFile);
1704        std::string name_no_ext = osgDB::getNameLessExtension(outputFile);
1705        if (ext=="osg" || ext=="osgt" || ext=="osgx" )
1706        {
1707            if (image_3d.valid())
1708            {
1709                image_3d->setFileName(name_no_ext + ".dds");
1710                osgDB::writeImageFile(*image_3d, image_3d->getFileName());
1711            }
1712            osgDB::writeNodeFile(*volume, outputFile);
1713        }
1714        else if (ext=="ive" || ext=="osgb" )
1715        {
1716            osgDB::writeNodeFile(*volume, outputFile);
1717        }
1718        else if (ext=="dds")
1719        {
1720            osgDB::writeImageFile(*image_3d, outputFile);
1721        }
1722        else
1723        {
1724            std::cout<<"Extension not support for file output, not file written."<<std::endl;
1725        }
1726
1727        return 0;
1728    }
1729
1730    if (volume.valid())
1731    {
1732
1733        osg::ref_ptr<osg::Node> loadedModel = volume.get();
1734
1735        if (useManipulator)
1736        {
1737            osg::ref_ptr<osg::Group> group = new osg::Group;
1738
1739#if 1
1740            osg::ref_ptr<osgManipulator::Dragger> dragger = new osgManipulator::TabBoxDragger;
1741#else
1742            osg::ref_ptr<osgManipulator::Dragger> dragger = new osgManipulator::TrackballDragger();
1743#endif
1744            dragger->setupDefaultGeometry();
1745            dragger->setHandleEvents(true);
1746            dragger->setActivationModKeyMask(osgGA::GUIEventAdapter::MODKEY_SHIFT);
1747            dragger->addDraggerCallback(new DraggerVolumeTileCallback(tile.get(), tile->getLocator()));
1748            dragger->setMatrix(osg::Matrix::translate(0.5,0.5,0.5)*tile->getLocator()->getTransform());
1749
1750
1751            group->addChild(dragger.get());
1752
1753            //dragger->addChild(volume.get());
1754
1755            group->addChild(volume.get());
1756
1757            loadedModel = group;
1758        }
1759
1760
1761        // set the scene to render
1762        viewer.setSceneData(loadedModel.get());
1763
1764        // the the viewers main frame loop
1765        viewer.run();
1766    }
1767
1768    return 0;
1769}
Note: See TracBrowser for help on using the browser.