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

Revision 10565, 53.5 kB (checked in by robert, 5 years ago)

Renamed the osgVolume::Layer/ImageDetails parameters RescaleIntercept? and RescaleSlope? to more general TexelOffset? and TexelScale?, and changed type to Vec4.

Refactored the transfer function set up in RayTracedTechnique? to prepare for new scale and offset uniforms.

Updated wrappers

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