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

Revision 13204, 42.5 kB (checked in by robert, 15 hours ago)

From Jason Beverage, "It looks like the Callback header got accidentally removed from the CMakeLists.txt in the submission yesterday for the geometry instancing example."

  • 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
81
82osg::Image* createTexture3D(osg::ImageList& imageList,
83            unsigned int numComponentsDesired,
84            int s_maximumTextureSize,
85            int t_maximumTextureSize,
86            int r_maximumTextureSize,
87            bool resizeToPowerOfTwo)
88{
89
90    if (numComponentsDesired==0)
91    {
92        return osg::createImage3DWithAlpha(imageList,
93                                        s_maximumTextureSize,
94                                        t_maximumTextureSize,
95                                        r_maximumTextureSize,
96                                        resizeToPowerOfTwo);
97    }
98    else
99    {
100        GLenum desiredPixelFormat = 0;
101        switch(numComponentsDesired)
102        {
103            case(1) : desiredPixelFormat = GL_LUMINANCE; break;
104            case(2) : desiredPixelFormat = GL_LUMINANCE_ALPHA; break;
105            case(3) : desiredPixelFormat = GL_RGB; break;
106            case(4) : desiredPixelFormat = GL_RGBA; break;
107        }
108
109        return osg::createImage3D(imageList,
110                                        desiredPixelFormat,
111                                        s_maximumTextureSize,
112                                        t_maximumTextureSize,
113                                        r_maximumTextureSize,
114                                        resizeToPowerOfTwo);
115    }
116}
117
118struct ScaleOperator
119{
120    ScaleOperator():_scale(1.0f) {}
121    ScaleOperator(float scale):_scale(scale) {}
122    ScaleOperator(const ScaleOperator& so):_scale(so._scale) {}
123
124    ScaleOperator& operator = (const ScaleOperator& so) { _scale = so._scale; return *this; }
125
126    float _scale;
127
128    inline void luminance(float& l) const { l*= _scale; }
129    inline void alpha(float& a) const { a*= _scale; }
130    inline void luminance_alpha(float& l,float& a) const { l*= _scale; a*= _scale;  }
131    inline void rgb(float& r,float& g,float& b) const { r*= _scale; g*=_scale; b*=_scale; }
132    inline void rgba(float& r,float& g,float& b,float& a) const { r*= _scale; g*=_scale; b*=_scale; a*=_scale; }
133};
134
135struct RecordRowOperator
136{
137    RecordRowOperator(unsigned int num):_colours(num),_pos(0) {}
138
139    mutable std::vector<osg::Vec4>  _colours;
140    mutable unsigned int            _pos;
141
142    inline void luminance(float l) const { rgba(l,l,l,1.0f); }
143    inline void alpha(float a) const { rgba(1.0f,1.0f,1.0f,a); }
144    inline void luminance_alpha(float l,float a) const { rgba(l,l,l,a);  }
145    inline void rgb(float r,float g,float b) const { rgba(r,g,b,1.0f); }
146    inline void rgba(float r,float g,float b,float a) const { _colours[_pos++].set(r,g,b,a); }
147};
148
149struct WriteRowOperator
150{
151    WriteRowOperator():_pos(0) {}
152    WriteRowOperator(unsigned int num):_colours(num),_pos(0) {}
153
154    std::vector<osg::Vec4>  _colours;
155    mutable unsigned int    _pos;
156
157    inline void luminance(float& l) const { l = _colours[_pos++].r(); }
158    inline void alpha(float& a) const { a = _colours[_pos++].a(); }
159    inline void luminance_alpha(float& l,float& a) const { l = _colours[_pos].r(); a = _colours[_pos++].a(); }
160    inline void rgb(float& r,float& g,float& b) const { r = _colours[_pos].r(); g = _colours[_pos].g(); b = _colours[_pos].b(); }
161    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(); }
162};
163
164void clampToNearestValidPowerOfTwo(int& sizeX, int& sizeY, int& sizeZ, int s_maximumTextureSize, int t_maximumTextureSize, int r_maximumTextureSize)
165{
166    // compute nearest powers of two for each axis.
167    int s_nearestPowerOfTwo = 1;
168    while(s_nearestPowerOfTwo<sizeX && s_nearestPowerOfTwo<s_maximumTextureSize) s_nearestPowerOfTwo*=2;
169
170    int t_nearestPowerOfTwo = 1;
171    while(t_nearestPowerOfTwo<sizeY && t_nearestPowerOfTwo<t_maximumTextureSize) t_nearestPowerOfTwo*=2;
172
173    int r_nearestPowerOfTwo = 1;
174    while(r_nearestPowerOfTwo<sizeZ && r_nearestPowerOfTwo<r_maximumTextureSize) r_nearestPowerOfTwo*=2;
175
176    sizeX = s_nearestPowerOfTwo;
177    sizeY = t_nearestPowerOfTwo;
178    sizeZ = r_nearestPowerOfTwo;
179}
180
181osg::Image* readRaw(int sizeX, int sizeY, int sizeZ, int numberBytesPerComponent, int numberOfComponents, const std::string& endian, const std::string& raw_filename)
182{
183    osgDB::ifstream fin(raw_filename.c_str(), std::ifstream::binary);
184    if (!fin) return 0;
185
186    GLenum pixelFormat;
187    switch(numberOfComponents)
188    {
189        case 1 : pixelFormat = GL_LUMINANCE; break;
190        case 2 : pixelFormat = GL_LUMINANCE_ALPHA; break;
191        case 3 : pixelFormat = GL_RGB; break;
192        case 4 : pixelFormat = GL_RGBA; break;
193        default :
194            osg::notify(osg::NOTICE)<<"Error: numberOfComponents="<<numberOfComponents<<" not supported, only 1,2,3 or 4 are supported."<<std::endl;
195            return 0;
196    }
197
198
199    GLenum dataType;
200    switch(numberBytesPerComponent)
201    {
202        case 1 : dataType = GL_UNSIGNED_BYTE; break;
203        case 2 : dataType = GL_UNSIGNED_SHORT; break;
204        case 4 : dataType = GL_UNSIGNED_INT; break;
205        default :
206            osg::notify(osg::NOTICE)<<"Error: numberBytesPerComponent="<<numberBytesPerComponent<<" not supported, only 1,2 or 4 are supported."<<std::endl;
207            return 0;
208    }
209
210    int s_maximumTextureSize=256, t_maximumTextureSize=256, r_maximumTextureSize=256;
211
212    int sizeS = sizeX;
213    int sizeT = sizeY;
214    int sizeR = sizeZ;
215    clampToNearestValidPowerOfTwo(sizeS, sizeT, sizeR, s_maximumTextureSize, t_maximumTextureSize, r_maximumTextureSize);
216
217    osg::ref_ptr<osg::Image> image = new osg::Image;
218    image->allocateImage(sizeS, sizeT, sizeR, pixelFormat, dataType);
219
220
221    bool endianSwap = (osg::getCpuByteOrder()==osg::BigEndian) ? (endian!="big") : (endian=="big");
222
223    unsigned int r_offset = (sizeZ<sizeR) ? sizeR/2 - sizeZ/2 : 0;
224
225    int offset = endianSwap ? numberBytesPerComponent : 0;
226    int delta = endianSwap ? -1 : 1;
227    for(int r=0;r<sizeZ;++r)
228    {
229        for(int t=0;t<sizeY;++t)
230        {
231            char* data = (char*) image->data(0,t,r+r_offset);
232            for(int s=0;s<sizeX;++s)
233            {
234                if (!fin) return 0;
235
236                for(int c=0;c<numberOfComponents;++c)
237                {
238                    char* ptr = data+offset;
239                    for(int b=0;b<numberBytesPerComponent;++b)
240                    {
241                        fin.read((char*)ptr, 1);
242                        ptr += delta;
243                    }
244                    data += numberBytesPerComponent;
245                }
246            }
247        }
248    }
249
250
251    // normalise texture
252    {
253        // compute range of values
254        osg::Vec4 minValue, maxValue;
255        osg::computeMinMax(image.get(), minValue, maxValue);
256        osg::modifyImage(image.get(),ScaleOperator(1.0f/maxValue.r()));
257    }
258
259
260    fin.close();
261
262    if (dataType!=GL_UNSIGNED_BYTE)
263    {
264        // need to convert to ubyte
265
266        osg::ref_ptr<osg::Image> new_image = new osg::Image;
267        new_image->allocateImage(sizeS, sizeT, sizeR, pixelFormat, GL_UNSIGNED_BYTE);
268
269        RecordRowOperator readOp(sizeS);
270        WriteRowOperator writeOp;
271
272        for(int r=0;r<sizeR;++r)
273        {
274            for(int t=0;t<sizeT;++t)
275            {
276                // reset the indices to beginning
277                readOp._pos = 0;
278                writeOp._pos = 0;
279
280                // read the pixels into readOp's _colour array
281                osg::readRow(sizeS, pixelFormat, dataType, image->data(0,t,r), readOp);
282
283                // pass readOp's _colour array contents over to writeOp (note this is just a pointer swap).
284                writeOp._colours.swap(readOp._colours);
285
286                osg::modifyRow(sizeS, pixelFormat, GL_UNSIGNED_BYTE, new_image->data(0,t,r), writeOp);
287
288                // return readOp's _colour array contents back to its rightful owner.
289                writeOp._colours.swap(readOp._colours);
290            }
291        }
292
293        image = new_image;
294    }
295
296    return image.release();
297
298
299}
300
301
302osg::TransferFunction1D* readTransferFunctionFile(const std::string& filename, float colorScale=1.0f)
303{
304    std::string foundFile = osgDB::findDataFile(filename);
305    if (foundFile.empty())
306    {
307        std::cout<<"Error: could not find transfer function file : "<<filename<<std::endl;
308        return 0;
309    }
310
311    std::cout<<"Reading transfer function "<<filename<<std::endl;
312
313    osg::TransferFunction1D::ColorMap colorMap;
314    osgDB::ifstream fin(foundFile.c_str());
315    while(fin)
316    {
317        float value, red, green, blue, alpha;
318        fin >> value >> red >> green >> blue >> alpha;
319        if (fin)
320        {
321            std::cout<<"value = "<<value<<" ("<<red<<", "<<green<<", "<<blue<<", "<<alpha<<")"<<std::endl;
322            colorMap[value] = osg::Vec4(red*colorScale,green*colorScale,blue*colorScale,alpha*colorScale);
323        }
324    }
325
326    if (colorMap.empty())
327    {
328        std::cout<<"Error: No values read from transfer function file: "<<filename<<std::endl;
329        return 0;
330    }
331
332    osg::TransferFunction1D* tf = new osg::TransferFunction1D;
333    tf->assign(colorMap);
334
335    return tf;
336}
337
338
339class TestSupportOperation: public osg::GraphicsOperation
340{
341public:
342
343    TestSupportOperation():
344        osg::GraphicsOperation("TestSupportOperation",false),
345        supported(true),
346        errorMessage(),
347        maximumTextureSize(256) {}
348
349    virtual void operator () (osg::GraphicsContext* gc)
350    {
351        OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mutex);
352
353        glGetIntegerv( GL_MAX_3D_TEXTURE_SIZE, &maximumTextureSize );
354
355        osg::notify(osg::NOTICE)<<"Max texture size="<<maximumTextureSize<<std::endl;
356    }
357
358    OpenThreads::Mutex  mutex;
359    bool                supported;
360    std::string         errorMessage;
361    GLint               maximumTextureSize;
362};
363
364class DraggerVolumeTileCallback : public osgManipulator::DraggerCallback
365{
366public:
367
368    DraggerVolumeTileCallback(osgVolume::VolumeTile* volume, osgVolume::Locator* locator):
369        _volume(volume),
370        _locator(locator) {}
371
372
373    virtual bool receive(const osgManipulator::MotionCommand& command);
374
375
376    osg::observer_ptr<osgVolume::VolumeTile>    _volume;
377    osg::ref_ptr<osgVolume::Locator>            _locator;
378
379    osg::Matrix _startMotionMatrix;
380
381    osg::Matrix _localToWorld;
382    osg::Matrix _worldToLocal;
383
384};
385
386bool DraggerVolumeTileCallback::receive(const osgManipulator::MotionCommand& command)
387{
388    if (!_locator) return false;
389
390    switch (command.getStage())
391    {
392        case osgManipulator::MotionCommand::START:
393        {
394            // Save the current matrix
395            _startMotionMatrix = _locator->getTransform();
396
397            // Get the LocalToWorld and WorldToLocal matrix for this node.
398            osg::NodePath nodePathToRoot;
399            osgManipulator::computeNodePathToRoot(*_volume,nodePathToRoot);
400            _localToWorld = _startMotionMatrix * osg::computeLocalToWorld(nodePathToRoot);
401            _worldToLocal = osg::Matrix::inverse(_localToWorld);
402
403            return true;
404        }
405        case osgManipulator::MotionCommand::MOVE:
406        {
407            // Transform the command's motion matrix into local motion matrix.
408            osg::Matrix localMotionMatrix = _localToWorld * command.getWorldToLocal()
409                                            * command.getMotionMatrix()
410                                            * command.getLocalToWorld() * _worldToLocal;
411
412            // Transform by the localMotionMatrix
413            _locator->setTransform(localMotionMatrix * _startMotionMatrix);
414
415            // osg::notify(osg::NOTICE)<<"New locator matrix "<<_locator->getTransform()<<std::endl;
416
417            return true;
418        }
419        case osgManipulator::MotionCommand::FINISH:
420        {
421            return true;
422        }
423        case osgManipulator::MotionCommand::NONE:
424        default:
425            return false;
426    }
427}
428
429int main( int argc, char **argv )
430{
431    // use an ArgumentParser object to manage the program arguments.
432    osg::ArgumentParser arguments(&argc,argv);
433
434    // set up the usage document, in case we need to print out how to use this program.
435    arguments.getApplicationUsage()->setDescription(arguments.getApplicationName()+" is the example which demonstrates use of 3D textures.");
436    arguments.getApplicationUsage()->setCommandLineUsage(arguments.getApplicationName()+" [options] filename ...");
437    arguments.getApplicationUsage()->addCommandLineOption("-h or --help","Display this information");
438    arguments.getApplicationUsage()->addCommandLineOption("--images [filenames]","Specify a stack of 2d images to build the 3d volume from.");
439    arguments.getApplicationUsage()->addCommandLineOption("--shader","Use OpenGL Shading Language. (default)");
440    arguments.getApplicationUsage()->addCommandLineOption("--no-shader","Disable use of OpenGL Shading Language.");
441    arguments.getApplicationUsage()->addCommandLineOption("--gpu-tf","Aply the transfer function on the GPU. (default)");
442    arguments.getApplicationUsage()->addCommandLineOption("--cpu-tf","Apply the transfer function on the CPU.");
443    arguments.getApplicationUsage()->addCommandLineOption("--mip","Use Maximum Intensity Projection (MIP) filtering.");
444    arguments.getApplicationUsage()->addCommandLineOption("--isosurface","Use Iso surface render.");
445    arguments.getApplicationUsage()->addCommandLineOption("--light","Use normals computed on the GPU to render a lit volume.");
446    arguments.getApplicationUsage()->addCommandLineOption("-n","Use normals computed on the GPU to render a lit volume.");
447    arguments.getApplicationUsage()->addCommandLineOption("--xSize <size>","Relative width of rendered brick.");
448    arguments.getApplicationUsage()->addCommandLineOption("--ySize <size>","Relative length of rendered brick.");
449    arguments.getApplicationUsage()->addCommandLineOption("--zSize <size>","Relative height of rendered brick.");
450    arguments.getApplicationUsage()->addCommandLineOption("--maxTextureSize <size>","Set the texture maximum resolution in the s,t,r (x,y,z) dimensions.");
451    arguments.getApplicationUsage()->addCommandLineOption("--s_maxTextureSize <size>","Set the texture maximum resolution in the s (x) dimension.");
452    arguments.getApplicationUsage()->addCommandLineOption("--t_maxTextureSize <size>","Set the texture maximum resolution in the t (y) dimension.");
453    arguments.getApplicationUsage()->addCommandLineOption("--r_maxTextureSize <size>","Set the texture maximum resolution in the r (z) dimension.");
454    arguments.getApplicationUsage()->addCommandLineOption("--modulate-alpha-by-luminance","For each pixel multiply the alpha value by the luminance.");
455    arguments.getApplicationUsage()->addCommandLineOption("--replace-alpha-with-luminance","For each pixel set the alpha value to the luminance.");
456    arguments.getApplicationUsage()->addCommandLineOption("--replace-rgb-with-luminance","For each rgb pixel convert to the luminance.");
457    arguments.getApplicationUsage()->addCommandLineOption("--num-components <num>","Set the number of components to in he target image.");
458    arguments.getApplicationUsage()->addCommandLineOption("--no-rescale","Disable the rescaling of the pixel data to 0.0 to 1.0 range");
459    arguments.getApplicationUsage()->addCommandLineOption("--rescale","Enable the rescale of the pixel data to 0.0 to 1.0 range (default).");
460    arguments.getApplicationUsage()->addCommandLineOption("--shift-min-to-zero","Shift the pixel data so min value is 0.0.");
461    arguments.getApplicationUsage()->addCommandLineOption("--sequence-length <num>","Set the length of time that a sequence of images with run for.");
462    arguments.getApplicationUsage()->addCommandLineOption("--sd <num>","Short hand for --sequence-length");
463    arguments.getApplicationUsage()->addCommandLineOption("--sdwm <num>","Set the SampleDensityWhenMovingProperty to specified value");
464    arguments.getApplicationUsage()->addCommandLineOption("--lod","Enable techniques to reduce the level of detail when moving.");
465//    arguments.getApplicationUsage()->addCommandLineOption("--raw <sizeX> <sizeY> <sizeZ> <numberBytesPerComponent> <numberOfComponents> <endian> <filename>","read a raw image data");
466
467    // construct the viewer.
468    osgViewer::Viewer viewer(arguments);
469
470    // add the window size toggle handler
471    viewer.addEventHandler(new osgViewer::WindowSizeHandler);
472
473    {
474        osg::ref_ptr<osgGA::KeySwitchMatrixManipulator> keyswitchManipulator = new osgGA::KeySwitchMatrixManipulator;
475
476        keyswitchManipulator->addMatrixManipulator( '1', "Trackball", new osgGA::TrackballManipulator() );
477
478        osgGA::FlightManipulator* flightManipulator = new osgGA::FlightManipulator();
479        flightManipulator->setYawControlMode(osgGA::FlightManipulator::NO_AUTOMATIC_YAW);
480        keyswitchManipulator->addMatrixManipulator( '2', "Flight", flightManipulator );
481
482        viewer.setCameraManipulator( keyswitchManipulator.get() );
483    }
484
485    // add the stats handler
486    viewer.addEventHandler(new osgViewer::StatsHandler);
487
488    viewer.getCamera()->setClearColor(osg::Vec4(0.0f,0.0f,0.0f,0.0f));
489
490    // if user request help write it out to cout.
491    if (arguments.read("-h") || arguments.read("--help"))
492    {
493        arguments.getApplicationUsage()->write(std::cout);
494        return 1;
495    }
496
497    std::string outputFile;
498    while (arguments.read("-o",outputFile)) {}
499
500
501
502    osg::ref_ptr<osg::TransferFunction1D> transferFunction;
503    std::string tranferFunctionFile;
504    while (arguments.read("--tf",tranferFunctionFile))
505    {
506        transferFunction = readTransferFunctionFile(tranferFunctionFile);
507    }
508    while (arguments.read("--tf-255",tranferFunctionFile))
509    {
510        transferFunction = readTransferFunctionFile(tranferFunctionFile,1.0f/255.0f);
511    }
512
513    while(arguments.read("--test"))
514    {
515        transferFunction = new osg::TransferFunction1D;
516        transferFunction->setColor(0.0, osg::Vec4(1.0,0.0,0.0,0.0));
517        transferFunction->setColor(0.5, osg::Vec4(1.0,1.0,0.0,0.5));
518        transferFunction->setColor(1.0, osg::Vec4(0.0,0.0,1.0,1.0));
519    }
520
521    while(arguments.read("--test2"))
522    {
523        transferFunction = new osg::TransferFunction1D;
524        transferFunction->setColor(0.0, osg::Vec4(1.0,0.0,0.0,0.0));
525        transferFunction->setColor(0.5, osg::Vec4(1.0,1.0,0.0,0.5));
526        transferFunction->setColor(1.0, osg::Vec4(0.0,0.0,1.0,1.0));
527        transferFunction->assign(transferFunction->getColorMap());
528    }
529
530    {
531        // deprecated options
532
533        bool invalidOption = false;
534
535        unsigned int numSlices=500;
536        while (arguments.read("-s",numSlices)) { OSG_NOTICE<<"Warning: -s option no longer supported."<<std::endl; invalidOption = true; }
537
538        float sliceEnd=1.0f;
539        while (arguments.read("--clip",sliceEnd)) { OSG_NOTICE<<"Warning: --clip option no longer supported."<<std::endl; invalidOption = true; }
540
541
542        if (invalidOption) return 1;
543    }
544
545    float xMultiplier=1.0f;
546    while (arguments.read("--xMultiplier",xMultiplier)) {}
547
548    float yMultiplier=1.0f;
549    while (arguments.read("--yMultiplier",yMultiplier)) {}
550
551    float zMultiplier=1.0f;
552    while (arguments.read("--zMultiplier",zMultiplier)) {}
553
554
555    float alphaFunc=0.02f;
556    while (arguments.read("--alphaFunc",alphaFunc)) {}
557
558
559
560    ShadingModel shadingModel = Standard;
561    while(arguments.read("--mip")) shadingModel =  MaximumIntensityProjection;
562
563    while (arguments.read("--isosurface") || arguments.read("--iso-surface")) shadingModel = Isosurface;
564
565    while (arguments.read("--light") || arguments.read("-n")) shadingModel = Light;
566
567    float xSize=0.0f, ySize=0.0f, zSize=0.0f;
568    while (arguments.read("--xSize",xSize)) {}
569    while (arguments.read("--ySize",ySize)) {}
570    while (arguments.read("--zSize",zSize)) {}
571
572    osg::ref_ptr<TestSupportOperation> testSupportOperation = new TestSupportOperation;
573    viewer.setRealizeOperation(testSupportOperation.get());
574
575    viewer.realize();
576
577    int maximumTextureSize = testSupportOperation->maximumTextureSize;
578    int s_maximumTextureSize = maximumTextureSize;
579    int t_maximumTextureSize = maximumTextureSize;
580    int r_maximumTextureSize = maximumTextureSize;
581    while(arguments.read("--maxTextureSize",maximumTextureSize))
582    {
583        s_maximumTextureSize = maximumTextureSize;
584        t_maximumTextureSize = maximumTextureSize;
585        r_maximumTextureSize = maximumTextureSize;
586    }
587    while(arguments.read("--s_maxTextureSize",s_maximumTextureSize)) {}
588    while(arguments.read("--t_maxTextureSize",t_maximumTextureSize)) {}
589    while(arguments.read("--r_maxTextureSize",r_maximumTextureSize)) {}
590
591    // set up colour space operation.
592    osg::ColorSpaceOperation colourSpaceOperation = osg::NO_COLOUR_SPACE_OPERATION;
593    osg::Vec4 colourModulate(0.25f,0.25f,0.25f,0.25f);
594    while(arguments.read("--modulate-alpha-by-luminance")) { colourSpaceOperation = osg::MODULATE_ALPHA_BY_LUMINANCE; }
595    while(arguments.read("--modulate-alpha-by-colour", colourModulate.x(),colourModulate.y(),colourModulate.z(),colourModulate.w() )) { colourSpaceOperation = osg::MODULATE_ALPHA_BY_COLOUR; }
596    while(arguments.read("--replace-alpha-with-luminance")) { colourSpaceOperation = osg::REPLACE_ALPHA_WITH_LUMINANCE; }
597    while(arguments.read("--replace-rgb-with-luminance")) { colourSpaceOperation = osg::REPLACE_RGB_WITH_LUMINANCE; }
598
599
600    enum RescaleOperation
601    {
602        NO_RESCALE,
603        RESCALE_TO_ZERO_TO_ONE_RANGE,
604        SHIFT_MIN_TO_ZERO
605    };
606
607    RescaleOperation rescaleOperation = RESCALE_TO_ZERO_TO_ONE_RANGE;
608    while(arguments.read("--no-rescale")) rescaleOperation = NO_RESCALE;
609    while(arguments.read("--rescale")) rescaleOperation = RESCALE_TO_ZERO_TO_ONE_RANGE;
610    while(arguments.read("--shift-min-to-zero")) rescaleOperation = SHIFT_MIN_TO_ZERO;
611
612
613    bool resizeToPowerOfTwo = false;
614
615    unsigned int numComponentsDesired = 0;
616    while(arguments.read("--num-components", numComponentsDesired)) {}
617
618    bool useManipulator = false;
619    while(arguments.read("--manipulator") || arguments.read("-m")) { useManipulator = true; }
620
621
622    bool useShader = true;
623    while(arguments.read("--shader")) { useShader = true; }
624    while(arguments.read("--no-shader")) { useShader = false; }
625
626    bool gpuTransferFunction = true;
627    while(arguments.read("--gpu-tf")) { gpuTransferFunction = true; }
628    while(arguments.read("--cpu-tf")) { gpuTransferFunction = false; }
629
630    double sampleDensityWhenMoving = 0.0;
631    while(arguments.read("--sdwm", sampleDensityWhenMoving)) {}
632
633    while(arguments.read("--lod")) { sampleDensityWhenMoving = 0.02; }
634
635    double sequenceLength = 10.0;
636    while(arguments.read("--sequence-duration", sequenceLength) ||
637          arguments.read("--sd", sequenceLength)) {}
638
639    typedef std::list< osg::ref_ptr<osg::Image> > Images;
640    Images images;
641
642
643    std::string vh_filename;
644    while (arguments.read("--vh", vh_filename))
645    {
646        std::string raw_filename, transfer_filename;
647        int xdim(0), ydim(0), zdim(0);
648
649        osgDB::ifstream header(vh_filename.c_str());
650        if (header)
651        {
652            header >> raw_filename >> transfer_filename >> xdim >> ydim >> zdim >> xSize >> ySize >> zSize;
653        }
654
655        if (xdim*ydim*zdim==0)
656        {
657            std::cout<<"Error in reading volume header "<<vh_filename<<std::endl;
658            return 1;
659        }
660
661        if (!raw_filename.empty())
662        {
663            images.push_back(readRaw(xdim, ydim, zdim, 1, 1, "little", raw_filename));
664        }
665
666        if (!transfer_filename.empty())
667        {
668            osgDB::ifstream fin(transfer_filename.c_str());
669            if (fin)
670            {
671                osg::TransferFunction1D::ColorMap colorMap;
672                float value = 0.0;
673                while(fin && value<=1.0)
674                {
675                    float red, green, blue, alpha;
676                    fin >> red >> green >> blue >> alpha;
677                    if (fin)
678                    {
679                        colorMap[value] = osg::Vec4(red/255.0f,green/255.0f,blue/255.0f,alpha/255.0f);
680                        std::cout<<"value = "<<value<<" ("<<red<<", "<<green<<", "<<blue<<", "<<alpha<<")";
681                        std::cout<<"  ("<<colorMap[value]<<")"<<std::endl;
682                    }
683                    value += 1/255.0;
684                }
685
686                if (colorMap.empty())
687                {
688                    std::cout<<"Error: No values read from transfer function file: "<<transfer_filename<<std::endl;
689                    return 0;
690                }
691
692                transferFunction = new osg::TransferFunction1D;
693                transferFunction->assign(colorMap);
694            }
695        }
696
697    }
698
699
700    int sizeX, sizeY, sizeZ, numberBytesPerComponent, numberOfComponents;
701    std::string endian, raw_filename;
702    while (arguments.read("--raw", sizeX, sizeY, sizeZ, numberBytesPerComponent, numberOfComponents, endian, raw_filename))
703    {
704        images.push_back(readRaw(sizeX, sizeY, sizeZ, numberBytesPerComponent, numberOfComponents, endian, raw_filename));
705    }
706
707    int images_pos = arguments.find("--images");
708    if (images_pos>=0)
709    {
710        osg::ImageList imageList;
711        int pos=images_pos+1;
712        for(;pos<arguments.argc() && !arguments.isOption(pos);++pos)
713        {
714            std::string arg(arguments[pos]);
715            if (arg.find('*') != std::string::npos)
716            {
717                osgDB::DirectoryContents contents = osgDB::expandWildcardsInFilename(arg);
718                for (unsigned int i = 0; i < contents.size(); ++i)
719                {
720                    osg::Image *image = osgDB::readImageFile( contents[i] );
721
722                    if(image)
723                    {
724                        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;
725                        imageList.push_back(image);
726                    }
727                }
728            }
729            else
730            {
731                // not an option so assume string is a filename.
732                osg::Image *image = osgDB::readImageFile( arguments[pos] );
733
734                if(image)
735                {
736                    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;
737                    imageList.push_back(image);
738                }
739            }
740        }
741
742        arguments.remove(images_pos, pos-images_pos);
743
744        // pack the textures into a single texture.
745        osg::Image* image = createTexture3D(imageList, numComponentsDesired, s_maximumTextureSize, t_maximumTextureSize, r_maximumTextureSize, resizeToPowerOfTwo);
746        if (image)
747        {
748            images.push_back(image);
749        }
750        else
751        {
752            OSG_NOTICE<<"Unable to create 3D image from source files."<<std::endl;
753        }
754    }
755
756
757    // any option left unread are converted into errors to write out later.
758    arguments.reportRemainingOptionsAsUnrecognized();
759
760    // report any errors if they have occurred when parsing the program arguments.
761    if (arguments.errors())
762    {
763        arguments.writeErrorMessages(std::cout);
764        return 1;
765    }
766
767
768    // assume remaining arguments are file names of textures.
769    for(int pos=1;pos<arguments.argc();++pos)
770    {
771        if (!arguments.isOption(pos))
772        {
773            std::string filename = arguments[pos];
774            if (osgDB::getLowerCaseFileExtension(filename)=="dicom")
775            {
776                // not an option so assume string is a filename.
777                osg::Image* image = osgDB::readImageFile(filename);
778                if (image)
779                {
780                    images.push_back(image);
781                }
782            }
783            else
784            {
785                osgDB::FileType fileType = osgDB::fileType(filename);
786                if (fileType == osgDB::FILE_NOT_FOUND)
787                {
788                    filename = osgDB::findDataFile(filename);
789                    fileType = osgDB::fileType(filename);
790                }
791
792                if (fileType == osgDB::DIRECTORY)
793                {
794                    osg::Image* image = osgDB::readImageFile(filename+".dicom");
795                    if (image) images.push_back(image);
796                }
797                else if (fileType == osgDB::REGULAR_FILE)
798                {
799                    // not an option so assume string is a filename.
800                    osg::Image* image = osgDB::readImageFile( filename );
801                    if (image) images.push_back(image);
802                }
803                else
804                {
805                    osg::notify(osg::NOTICE)<<"Error: could not find file: "<<filename<<std::endl;
806                    return 1;
807                }
808            }
809        }
810    }
811
812    if (images.empty())
813    {
814        std::cout<<"No model loaded, please specify a volumetric image file on the command line."<<std::endl;
815        return 1;
816    }
817
818
819    Images::iterator sizeItr = images.begin();
820    int image_s = (*sizeItr)->s();
821    int image_t = (*sizeItr)->t();
822    int image_r = (*sizeItr)->r();
823    ++sizeItr;
824
825    for(;sizeItr != images.end(); ++sizeItr)
826    {
827        if ((*sizeItr)->s() != image_s ||
828            (*sizeItr)->t() != image_t ||
829            (*sizeItr)->r() != image_r)
830        {
831            std::cout<<"Images in sequence are not of the same dimensions."<<std::endl;
832            return 1;
833        }
834    }
835
836
837    osg::ref_ptr<osgVolume::ImageDetails> details = dynamic_cast<osgVolume::ImageDetails*>(images.front()->getUserData());
838    osg::ref_ptr<osg::RefMatrix> matrix = details ? details->getMatrix() : dynamic_cast<osg::RefMatrix*>(images.front()->getUserData());
839
840    if (!matrix)
841    {
842        if (xSize==0.0) xSize = static_cast<float>(image_s);
843        if (ySize==0.0) ySize = static_cast<float>(image_t);
844        if (zSize==0.0) zSize = static_cast<float>(image_r);
845
846        matrix = new osg::RefMatrix(xSize, 0.0,   0.0,   0.0,
847                                    0.0,   ySize, 0.0,   0.0,
848                                    0.0,   0.0,   zSize, 0.0,
849                                    0.0,   0.0,   0.0,   1.0);
850    }
851
852
853    if (xMultiplier!=1.0 || yMultiplier!=1.0 || zMultiplier!=1.0)
854    {
855        matrix->postMultScale(osg::Vec3d(fabs(xMultiplier), fabs(yMultiplier), fabs(zMultiplier)));
856    }
857
858    osg::Vec4 minValue(FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX);
859    osg::Vec4 maxValue(-FLT_MAX, -FLT_MAX, -FLT_MAX, -FLT_MAX);
860    bool computeMinMax = false;
861    for(Images::iterator itr = images.begin();
862        itr != images.end();
863        ++itr)
864    {
865        osg::Vec4 localMinValue, localMaxValue;
866        if (osg::computeMinMax(itr->get(), localMinValue, localMaxValue))
867        {
868            if (localMinValue.r()<minValue.r()) minValue.r() = localMinValue.r();
869            if (localMinValue.g()<minValue.g()) minValue.g() = localMinValue.g();
870            if (localMinValue.b()<minValue.b()) minValue.b() = localMinValue.b();
871            if (localMinValue.a()<minValue.a()) minValue.a() = localMinValue.a();
872
873            if (localMaxValue.r()>maxValue.r()) maxValue.r() = localMaxValue.r();
874            if (localMaxValue.g()>maxValue.g()) maxValue.g() = localMaxValue.g();
875            if (localMaxValue.b()>maxValue.b()) maxValue.b() = localMaxValue.b();
876            if (localMaxValue.a()>maxValue.a()) maxValue.a() = localMaxValue.a();
877
878            osg::notify(osg::NOTICE)<<"  ("<<localMinValue<<") ("<<localMaxValue<<") "<<(*itr)->getFileName()<<std::endl;
879
880            computeMinMax = true;
881        }
882    }
883
884    if (computeMinMax)
885    {
886        osg::notify(osg::NOTICE)<<"Min value "<<minValue<<std::endl;
887        osg::notify(osg::NOTICE)<<"Max value "<<maxValue<<std::endl;
888
889        float minComponent = minValue[0];
890        minComponent = osg::minimum(minComponent,minValue[1]);
891        minComponent = osg::minimum(minComponent,minValue[2]);
892        minComponent = osg::minimum(minComponent,minValue[3]);
893
894        float maxComponent = maxValue[0];
895        maxComponent = osg::maximum(maxComponent,maxValue[1]);
896        maxComponent = osg::maximum(maxComponent,maxValue[2]);
897        maxComponent = osg::maximum(maxComponent,maxValue[3]);
898
899#if 0
900        switch(rescaleOperation)
901        {
902            case(NO_RESCALE):
903                break;
904
905            case(RESCALE_TO_ZERO_TO_ONE_RANGE):
906            {
907                float scale = 0.99f/(maxComponent-minComponent);
908                float offset = -minComponent * scale;
909
910                for(Images::iterator itr = images.begin();
911                    itr != images.end();
912                    ++itr)
913                {
914                    osg::offsetAndScaleImage(itr->get(),
915                        osg::Vec4(offset, offset, offset, offset),
916                        osg::Vec4(scale, scale, scale, scale));
917                }
918                break;
919            }
920            case(SHIFT_MIN_TO_ZERO):
921            {
922                float offset = -minComponent;
923
924                for(Images::iterator itr = images.begin();
925                    itr != images.end();
926                    ++itr)
927                {
928                    osg::offsetAndScaleImage(itr->get(),
929                        osg::Vec4(offset, offset, offset, offset),
930                        osg::Vec4(1.0f, 1.0f, 1.0f, 1.0f));
931                }
932                break;
933            }
934        };
935#endif
936    }
937
938
939    if (colourSpaceOperation!=osg::NO_COLOUR_SPACE_OPERATION)
940    {
941        for(Images::iterator itr = images.begin();
942            itr != images.end();
943            ++itr)
944        {
945            (*itr) = osg::colorSpaceConversion(colourSpaceOperation, itr->get(), colourModulate);
946        }
947    }
948
949    if (!gpuTransferFunction && transferFunction.valid())
950    {
951        for(Images::iterator itr = images.begin();
952            itr != images.end();
953            ++itr)
954        {
955            *itr = osgVolume::applyTransferFunction(itr->get(), transferFunction.get());
956        }
957    }
958
959    osg::ref_ptr<osg::Image> image_3d = 0;
960
961    if (images.size()==1)
962    {
963        osg::notify(osg::NOTICE)<<"Single image "<<images.size()<<" volumes."<<std::endl;
964        image_3d = images.front();
965    }
966    else
967    {
968        osg::notify(osg::NOTICE)<<"Creating sequence of "<<images.size()<<" volumes."<<std::endl;
969
970        osg::ref_ptr<osg::ImageSequence> imageSequence = new osg::ImageSequence;
971        imageSequence->setLength(sequenceLength);
972        image_3d = imageSequence.get();
973        for(Images::iterator itr = images.begin();
974            itr != images.end();
975            ++itr)
976        {
977            imageSequence->addImage(itr->get());
978        }
979        imageSequence->play();
980    }
981
982    osg::ref_ptr<osgVolume::Volume> volume = new osgVolume::Volume;
983    osg::ref_ptr<osgVolume::VolumeTile> tile = new osgVolume::VolumeTile;
984    volume->addChild(tile.get());
985
986    osg::ref_ptr<osgVolume::ImageLayer> layer = new osgVolume::ImageLayer(image_3d.get());
987
988    if (details)
989    {
990        layer->setTexelOffset(details->getTexelOffset());
991        layer->setTexelScale(details->getTexelScale());
992    }
993
994    switch(rescaleOperation)
995    {
996        case(NO_RESCALE):
997            break;
998
999        case(RESCALE_TO_ZERO_TO_ONE_RANGE):
1000        {
1001            layer->rescaleToZeroToOneRange();
1002            break;
1003        }
1004        case(SHIFT_MIN_TO_ZERO):
1005        {
1006            layer->translateMinToZero();
1007            break;
1008        }
1009    };
1010
1011    if (xMultiplier<0.0 || yMultiplier<0.0 || zMultiplier<0.0)
1012    {
1013        layer->setLocator(new osgVolume::Locator(
1014            osg::Matrix::translate(xMultiplier<0.0 ? -1.0 : 0.0, yMultiplier<0.0 ? -1.0 : 0.0, zMultiplier<0.0 ? -1.0 : 0.0) *
1015            osg::Matrix::scale(xMultiplier<0.0 ? -1.0 : 1.0, yMultiplier<0.0 ? -1.0 : 1.0, zMultiplier<0.0 ? -1.0 : 1.0) *
1016            (*matrix)
1017            ));;
1018    }
1019    else
1020    {
1021        layer->setLocator(new osgVolume::Locator(*matrix));
1022    }
1023    tile->setLocator(new osgVolume::Locator(*matrix));
1024
1025    tile->setLayer(layer.get());
1026
1027    tile->setEventCallback(new osgVolume::PropertyAdjustmentCallback());
1028
1029    if (useShader)
1030    {
1031
1032        osgVolume::SwitchProperty* sp = new osgVolume::SwitchProperty;
1033        sp->setActiveProperty(0);
1034
1035        osgVolume::AlphaFuncProperty* ap = new osgVolume::AlphaFuncProperty(alphaFunc);
1036        osgVolume::SampleDensityProperty* sd = new osgVolume::SampleDensityProperty(0.005);
1037        osgVolume::SampleDensityWhenMovingProperty* sdwm = sampleDensityWhenMoving!=0.0 ? new osgVolume::SampleDensityWhenMovingProperty(sampleDensityWhenMoving) : 0;
1038        osgVolume::TransparencyProperty* tp = new osgVolume::TransparencyProperty(1.0);
1039        osgVolume::TransferFunctionProperty* tfp = transferFunction.valid() ? new osgVolume::TransferFunctionProperty(transferFunction.get()) : 0;
1040
1041        {
1042            // Standard
1043            osgVolume::CompositeProperty* cp = new osgVolume::CompositeProperty;
1044            cp->addProperty(ap);
1045            cp->addProperty(sd);
1046            cp->addProperty(tp);
1047            if (sdwm) cp->addProperty(sdwm);
1048            if (tfp) cp->addProperty(tfp);
1049
1050            sp->addProperty(cp);
1051        }
1052
1053        {
1054            // Light
1055            osgVolume::CompositeProperty* cp = new osgVolume::CompositeProperty;
1056            cp->addProperty(ap);
1057            cp->addProperty(sd);
1058            cp->addProperty(tp);
1059            cp->addProperty(new osgVolume::LightingProperty);
1060            if (sdwm) cp->addProperty(sdwm);
1061            if (tfp) cp->addProperty(tfp);
1062
1063            sp->addProperty(cp);
1064        }
1065
1066        {
1067            // Isosurface
1068            osgVolume::CompositeProperty* cp = new osgVolume::CompositeProperty;
1069            cp->addProperty(sd);
1070            cp->addProperty(tp);
1071            cp->addProperty(new osgVolume::IsoSurfaceProperty(alphaFunc));
1072            if (sdwm) cp->addProperty(sdwm);
1073            if (tfp) cp->addProperty(tfp);
1074
1075            sp->addProperty(cp);
1076        }
1077
1078        {
1079            // MaximumIntensityProjection
1080            osgVolume::CompositeProperty* cp = new osgVolume::CompositeProperty;
1081            cp->addProperty(ap);
1082            cp->addProperty(sd);
1083            cp->addProperty(tp);
1084            cp->addProperty(new osgVolume::MaximumIntensityProjectionProperty);
1085            if (sdwm) cp->addProperty(sdwm);
1086            if (tfp) cp->addProperty(tfp);
1087
1088            sp->addProperty(cp);
1089        }
1090
1091        switch(shadingModel)
1092        {
1093            case(Standard):                     sp->setActiveProperty(0); break;
1094            case(Light):                        sp->setActiveProperty(1); break;
1095            case(Isosurface):                   sp->setActiveProperty(2); break;
1096            case(MaximumIntensityProjection):   sp->setActiveProperty(3); break;
1097        }
1098        layer->addProperty(sp);
1099
1100
1101        tile->setVolumeTechnique(new osgVolume::RayTracedTechnique);
1102    }
1103    else
1104    {
1105        layer->addProperty(new osgVolume::AlphaFuncProperty(alphaFunc));
1106        tile->setVolumeTechnique(new osgVolume::FixedFunctionTechnique);
1107    }
1108
1109    if (!outputFile.empty())
1110    {
1111        std::string ext = osgDB::getFileExtension(outputFile);
1112        std::string name_no_ext = osgDB::getNameLessExtension(outputFile);
1113        if (ext=="osg" || ext=="osgt" || ext=="osgx" )
1114        {
1115            if (image_3d.valid())
1116            {
1117                image_3d->setFileName(name_no_ext + ".dds");
1118                osgDB::writeImageFile(*image_3d, image_3d->getFileName());
1119            }
1120            osgDB::writeNodeFile(*volume, outputFile);
1121        }
1122        else if (ext=="ive" || ext=="osgb" )
1123        {
1124            osgDB::writeNodeFile(*volume, outputFile);
1125        }
1126        else if (ext=="dds")
1127        {
1128            osgDB::writeImageFile(*image_3d, outputFile);
1129        }
1130        else
1131        {
1132            std::cout<<"Extension not support for file output, not file written."<<std::endl;
1133        }
1134
1135        return 0;
1136    }
1137
1138    if (volume.valid())
1139    {
1140
1141        osg::ref_ptr<osg::Node> loadedModel = volume.get();
1142
1143        if (useManipulator)
1144        {
1145            osg::ref_ptr<osg::Group> group = new osg::Group;
1146
1147#if 1
1148            osg::ref_ptr<osgManipulator::Dragger> dragger = new osgManipulator::TabBoxDragger;
1149#else
1150            osg::ref_ptr<osgManipulator::Dragger> dragger = new osgManipulator::TrackballDragger();
1151#endif
1152            dragger->setupDefaultGeometry();
1153            dragger->setHandleEvents(true);
1154            dragger->setActivationModKeyMask(osgGA::GUIEventAdapter::MODKEY_SHIFT);
1155            dragger->addDraggerCallback(new DraggerVolumeTileCallback(tile.get(), tile->getLocator()));
1156            dragger->setMatrix(osg::Matrix::translate(0.5,0.5,0.5)*tile->getLocator()->getTransform());
1157
1158
1159            group->addChild(dragger.get());
1160
1161            //dragger->addChild(volume.get());
1162
1163            group->addChild(volume.get());
1164
1165            loadedModel = group;
1166        }
1167
1168
1169        // set the scene to render
1170        viewer.setSceneData(loadedModel.get());
1171
1172        // the the viewers main frame loop
1173        viewer.run();
1174    }
1175
1176    return 0;
1177}
Note: See TracBrowser for help on using the browser.