root/OpenSceneGraph/trunk/src/osgPresentation/SlideShowConstructor.cpp @ 13204

Revision 13204, 82.8 kB (checked in by robert, 2 days ago)

Added shaders to support experimental shader based displacement mapping technique osgTerrain::ShaderTerrain?.

  • Property svn:eol-style set to native
Line 
1/* -*-c++-*- Present3D - Copyright (C) 1999-2006 Robert Osfield
2 *
3 * This software is open source and may be redistributed and/or modified under
4 * the terms of the GNU General Public License (GPL) version 2.0.
5 * The full license is in LICENSE.txt file included with this distribution,.
6 *
7 * This software is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10 * include LICENSE.txt for more details.
11*/
12
13#include <osgPresentation/SlideShowConstructor>
14
15#include <osg/Geometry>
16#include <osg/PolygonOffset>
17#include <osg/Geode>
18#include <osg/Texture2D>
19#include <osg/TextureRectangle>
20#include <osg/MatrixTransform>
21#include <osg/PositionAttitudeTransform>
22#include <osg/TexMat>
23#include <osg/ShapeDrawable>
24#include <osg/ImageSequence>
25#include <osg/ImageUtils>
26#include <osg/Notify>
27#include <osg/io_utils>
28
29#include <osgUtil/TransformCallback>
30
31#include <osgDB/ReadFile>
32#include <osgDB/WriteFile>
33#include <osgDB/FileUtils>
34#include <osgDB/Input>
35#include <osgDB/FileNameUtils>
36
37#include <osgWidget/PdfReader>
38
39#include <osgViewer/ViewerEventHandlers>
40
41#include <osgText/Text>
42
43#include <osgFX/SpecularHighlights>
44
45#include <osgVolume/Volume>
46#include <osgVolume/RayTracedTechnique>
47#include <osgVolume/FixedFunctionTechnique>
48
49#include <sstream>
50#include <algorithm>
51
52#include <osgPresentation/AnimationMaterial>
53#include <osgPresentation/PickEventHandler>
54#include <osgPresentation/KeyEventHandler>
55
56#include <osgManipulator/TabBoxDragger>
57#include <osgManipulator/TabBoxTrackballDragger>
58#include <osgManipulator/TrackballDragger>
59
60using namespace osgPresentation;
61
62#define USE_CLIENT_STORAGE_HINT 0
63#define USE_TEXTURE_FROM_VIDEO_PLUGIN 1
64
65class SetToTransparentBin : public osg::NodeVisitor
66{
67public:
68
69    SetToTransparentBin():
70        osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN) {}
71
72    virtual void appply(osg::Node& node)
73    {
74        if (node.getStateSet())
75        {
76            node.getStateSet()->setMode(GL_BLEND,osg::StateAttribute::ON);
77            node.getStateSet()->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
78        }
79    }
80
81    virtual void apply(osg::Geode& geode)
82    {
83        if (geode.getStateSet())
84        {
85            geode.getStateSet()->setMode(GL_BLEND,osg::StateAttribute::ON);
86            geode.getStateSet()->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
87        }
88        for(unsigned int i=0;i<geode.getNumDrawables();++i)
89        {
90            if (geode.getDrawable(i)->getStateSet())
91            {
92                geode.getDrawable(i)->getStateSet()->setMode(GL_BLEND,osg::StateAttribute::ON);
93                geode.getDrawable(i)->getStateSet()->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
94            }
95        }
96    }
97};
98
99
100HUDSettings::HUDSettings(double slideDistance, float eyeOffset, unsigned int leftMask, unsigned int rightMask):
101    _slideDistance(slideDistance),
102    _eyeOffset(eyeOffset),
103    _leftMask(leftMask),
104    _rightMask(rightMask)
105{
106}
107
108HUDSettings::~HUDSettings()
109{
110}
111
112bool HUDSettings::getModelViewMatrix(osg::Matrix& matrix, osg::NodeVisitor* nv) const
113{
114    matrix.makeLookAt(osg::Vec3d(0.0,0.0,0.0),osg::Vec3d(0.0,_slideDistance,0.0),osg::Vec3d(0.0,0.0,1.0));
115
116    if (nv->getTraversalMask()==_leftMask)
117    {
118        matrix.postMultTranslate(osg::Vec3(_eyeOffset,0.0,0.0));
119    }
120    else if (nv->getTraversalMask()==_rightMask)
121    {
122        matrix.postMultTranslate(osg::Vec3(-_eyeOffset,0.0,0.0));
123    }
124    return true;
125}
126
127bool HUDSettings::getInverseModelViewMatrix(osg::Matrix& matrix, osg::NodeVisitor* nv) const
128{
129    osg::Matrix modelView;
130    getModelViewMatrix(modelView,nv);
131    matrix.invert(modelView);
132    return true;
133}
134
135
136HUDTransform::HUDTransform(HUDSettings* hudSettings):
137    _hudSettings(hudSettings)
138{
139    setDataVariance(osg::Object::DYNAMIC);
140    setReferenceFrame(osg::Transform::ABSOLUTE_RF);
141}
142
143HUDTransform::~HUDTransform() {}
144
145bool HUDTransform::computeLocalToWorldMatrix(osg::Matrix& matrix,osg::NodeVisitor* nv) const
146{
147    return _hudSettings->getModelViewMatrix(matrix,nv);
148}
149
150bool HUDTransform::computeWorldToLocalMatrix(osg::Matrix& matrix,osg::NodeVisitor* nv) const
151{
152    return _hudSettings->getInverseModelViewMatrix(matrix,nv);
153}
154
155SlideShowConstructor::SlideShowConstructor(osgDB::Options* options):
156    _options(options)
157{
158    const osg::DisplaySettings* ds = osg::DisplaySettings::instance().get();
159
160    _propertyManager = new osgPresentation::PropertyManager;
161    _propertyEventCallback = new osgPresentation::PropertyEventCallback(_propertyManager.get());
162
163    _slideHeight = ds->getScreenHeight();
164    _slideWidth = ds->getScreenWidth();
165    _slideDistance = ds->getScreenDistance();
166    _leftEyeMask = 0x01;
167    _rightEyeMask = 0x02;
168
169    _hudSettings = new HUDSettings(_slideDistance, ds->getEyeSeparation()*0.5, _leftEyeMask, _rightEyeMask);
170
171    _backgroundColor.set(0.0f,0.0f,0.0f,1.0f);
172
173    _presentationDuration = -1.0;
174
175    // set up title defaults
176    _titleFontDataDefault.font = "fonts/arial.ttf";
177    _titleFontDataDefault.color.set(1.0f,1.0f,1.0f,1.0f);
178    _titleFontDataDefault.layout =osgText::Text::LEFT_TO_RIGHT;
179    _titleFontDataDefault.alignment = osgText::Text::CENTER_BASE_LINE;
180    _titleFontDataDefault.axisAlignment = osgText::Text::XZ_PLANE;
181    _titleFontDataDefault.characterSize = 0.06f;
182    _titleFontDataDefault.maximumWidth = 0.9f;
183
184    _titlePositionDataDefault.position.set(0.5f,0.92f,0.0f);
185
186    // set up text defaults
187    _textFontDataDefault.font = "fonts/arial.ttf";
188    _textFontDataDefault.color.set(1.0f,1.0f,1.0f,1.0f);
189    _textFontDataDefault.layout = osgText::Text::LEFT_TO_RIGHT;
190    _textFontDataDefault.alignment = osgText::Text::LEFT_BASE_LINE;
191    _textFontDataDefault.axisAlignment = osgText::Text::XZ_PLANE;
192    _textFontDataDefault.characterSize = 0.04f;
193    _textFontDataDefault.maximumWidth = 0.8f;
194
195    _textPositionDataDefault.position.set(0.1f,0.85f,0.0f);
196
197    _loopPresentation = false;
198    _autoSteppingActive = false;
199
200    _slideBackgroundAsHUD = false;
201
202    _layerToApplyEventCallbackTo = 0;
203    _currentEventCallbacksToApply.clear();
204}
205
206void SlideShowConstructor::setPresentationAspectRatio(float aspectRatio)
207{
208    _slideWidth = _slideHeight*aspectRatio;
209}
210
211void SlideShowConstructor::setPresentationAspectRatio(const std::string& str)
212{
213    if (str=="Reality Theatre") setPresentationAspectRatio(3.0f);
214    else if (str=="Desktop") setPresentationAspectRatio(1280.0f/1024.0f);
215    else
216    {
217        float ratio = (float)atof(str.c_str());
218        if (ratio!=0.0) setPresentationAspectRatio(1280.0f/1024.0f);
219        else
220        {
221            OSG_WARN<<"Error: presentation aspect ratio incorrect type"<<std::endl;
222            OSG_WARN<<"       valid types are \"Reality Theatre\", \"Desktop\" or a numerical value."<<std::endl;
223        }
224    }
225}
226
227void SlideShowConstructor::createPresentation()
228{
229    _slideOrigin.set(-_slideWidth*0.5f,_slideDistance,-_slideHeight*0.5f);
230
231#if 0
232    _titleFontDataDefault.characterSize = 0.06f;
233    _titleFontDataDefault.maximumWidth = 0.9f;
234
235    _textFontDataDefault.characterSize = 0.04f;
236    _textFontDataDefault.maximumWidth = 0.8f;
237#endif
238
239    OSG_INFO<<"_titlePositionDataDefault.position="<<_titlePositionDataDefault.position<<std::endl;
240
241    _textPositionDataDefault.position.set(0.1f,_titlePositionDataDefault.position.y()-_titleFontDataDefault.characterSize,0.0f);
242    _imagePositionDataDefault.position.set(0.5f,0.5f,0.0f);
243    _modelPositionDataDefault.position.set(0.5f,0.5f,0.0f);
244
245    _root = new osg::Group;
246
247    _presentationSwitch = new osg::Switch;
248    _presentationSwitch->setName(std::string("Presentation_")+_presentationName);
249
250    _root->addChild(_presentationSwitch.get());
251
252    osg::Vec3 slideCenter = _slideOrigin + osg::Vec3(_slideWidth*0.5f,0.0f,_slideHeight*0.5f);
253
254    HomePosition* hp = new HomePosition;
255    hp->eye.set(0.0f,0.0f,0.0f);
256    hp->center = slideCenter;
257    hp->up.set(0.0f,0.0f,1.0f);
258
259    OSG_INFO<<" slideCenter "<<slideCenter<<std::endl;
260
261    if (_presentationDuration>=0.0)
262    {
263        setDuration(_presentationSwitch.get(),_presentationDuration);
264    }
265
266    _root->setUserData(hp);
267
268    if (_loopPresentation) _root->addDescription("loop");
269    if (_autoSteppingActive) _root->addDescription("auto");
270
271    //_root->addEventCallback(_propertyEventCallback.get());
272
273    _presentationSwitch->setEventCallback(_propertyEventCallback.get());
274}
275
276LayerAttributes* SlideShowConstructor::getOrCreateLayerAttributes(osg::Node* node)
277{
278    LayerAttributes* la = dynamic_cast<LayerAttributes*>(node->getUserData());
279    if (!la)
280    {
281        if (node->getUserData())
282        {
283            OSG_NOTICE<<"UserData already assigned, overriding to set LayerAttributes."<<std::endl;
284        }
285
286        la = new LayerAttributes;
287        node->setUserData(la);
288    }
289
290    return la;
291}
292
293void SlideShowConstructor::setBackgroundColor(const osg::Vec4& color, bool updateClearNode)
294{
295    _backgroundColor = color;
296    if (updateClearNode && _slideClearNode.valid()) _slideClearNode->setClearColor(_backgroundColor);
297}
298
299void SlideShowConstructor::setTextColor(const osg::Vec4& color)
300{
301    _titleFontDataDefault.color = color;
302    _textFontDataDefault.color = color;
303
304    _titleFontData.color = _titleFontDataDefault.color;
305    _textFontData.color = _textFontDataDefault.color;
306
307}
308
309void SlideShowConstructor::setPresentationName(const std::string& name)
310{
311    _presentationName = name;
312    if (_presentationSwitch.valid()) _presentationSwitch->setName(std::string("Presentation_")+_presentationName);
313}
314
315void SlideShowConstructor::setPresentationDuration(double duration)
316{
317    _presentationDuration = duration;
318    if (_presentationDuration>=0.0 && _presentationSwitch.valid())
319    {
320        setDuration(_presentationSwitch.get(),_presentationDuration);
321    }
322}
323
324void SlideShowConstructor::addSlide()
325{
326    if (!_presentationSwitch) createPresentation();
327
328    // reset fonts
329    _titleFontData = _titleFontDataDefault;
330    _textFontData = _textFontDataDefault;
331
332    // reset cursors
333    _titlePositionData = _titlePositionDataDefault;
334    _textPositionData = _textPositionDataDefault;
335    _imagePositionData =  _imagePositionDataDefault;
336    _modelPositionData =  _modelPositionDataDefault;
337
338    _slide = new osg::Switch;
339    _slide->setName(std::string("Slide_")+_slideTitle);
340
341    _slideClearNode = new osg::ClearNode;
342    _slideClearNode->setClearColor(_backgroundColor);
343    _slideClearNode->addChild(_slide.get());
344
345    _presentationSwitch->addChild(_slideClearNode.get());
346
347    _previousLayer = 0;
348    _currentLayer = 0;
349
350
351    _filePathData = new FilePathData(osgDB::getDataFilePathList());
352
353    _slideClearNode->setUserData(_filePathData.get());
354}
355
356void SlideShowConstructor::selectSlide(int slideNum)
357{
358    if (slideNum<0)
359    {
360        addSlide();
361    }
362    else if (slideNum>=static_cast<int>(_presentationSwitch->getNumChildren()))
363    {
364        addSlide();
365    }
366    else
367    {
368        _slideClearNode = dynamic_cast<osg::ClearNode*>(_presentationSwitch->getChild(slideNum));
369        if (!_slideClearNode || _slideClearNode->getNumChildren()==0 || _slideClearNode->getChild(0)->asSwitch()==0)
370        {
371            addSlide();
372        }
373        else
374        {
375            _slide = _slideClearNode->getChild(0)->asSwitch();
376            _previousLayer = _slide->getChild(_slide->getNumChildren()-1)->asGroup();
377            _currentLayer = 0;
378        }
379    }
380}
381
382void SlideShowConstructor::setSlideDuration(double duration)
383{
384    if (!_slide) addSlide();
385
386    if (_slide.valid())
387    {
388        setDuration(_slide.get(),duration);
389    }
390}
391
392void SlideShowConstructor::addLayer(bool inheritPreviousLayers, bool defineAsBaseLayer)
393{
394    if (!_slide) addSlide();
395
396    _currentLayer = new osg::Group;
397    _currentLayer->setName("Layer");
398
399    // OSG_NOTICE<<"addLayer"<<std::endl;
400
401    if (!_previousLayer || !inheritPreviousLayers)
402    {
403        _textPositionData = _textPositionDataDefault;
404        _imagePositionData =  _imagePositionDataDefault;
405        _modelPositionData =  _modelPositionDataDefault;
406
407        // OSG_NOTICE<<"   new layer background = "<<_slideBackgroundImageFileName<<std::endl;
408
409        osg::ref_ptr<osg::Image> image = !_slideBackgroundImageFileName.empty() ?
410            osgDB::readImageFile(_slideBackgroundImageFileName, _options.get()) :
411            0;
412
413        // create the background and title..
414        if (image.valid())
415        {
416            osg::Geode* background = new osg::Geode;
417
418            osg::StateSet* backgroundStateSet = background->getOrCreateStateSet();
419            backgroundStateSet->setAttributeAndModes(
420                        new osg::PolygonOffset(1.0f,2.0f),
421                        osg::StateAttribute::ON);
422
423
424            bool useTextureRectangle = true;
425            float s = useTextureRectangle ? image->s() : 1.0;
426            float t = useTextureRectangle ? image->t() : 1.0;
427            osg::Geometry* backgroundQuad = osg::createTexturedQuadGeometry(_slideOrigin,
428                                                            osg::Vec3(_slideWidth,0.0f,0.0f),
429                                                            osg::Vec3(0.0f,0.0f,_slideHeight),
430                                                            s, t);
431            // OSG_NOTICE<<"Image loaded "<<image.get()<<"  "<<_slideBackgroundImageFileName<<std::endl;
432
433            if (useTextureRectangle)
434            {
435                osg::TextureRectangle* texture = new osg::TextureRectangle(image.get());
436                backgroundStateSet->setTextureAttributeAndModes(0,
437                            texture,
438                            osg::StateAttribute::ON);
439            }
440            else
441            {
442                osg::Texture2D* texture = new osg::Texture2D(image.get());
443                texture->setResizeNonPowerOfTwoHint(false);
444                texture->setFilter(osg::Texture::MIN_FILTER,osg::Texture::LINEAR);
445                texture->setFilter(osg::Texture::MAG_FILTER,osg::Texture::LINEAR);
446#if USE_CLIENT_STORAGE_HINT
447                texture->setClientStorageHint(true);
448#endif
449                backgroundStateSet->setTextureAttributeAndModes(0,
450                            texture,
451                            osg::StateAttribute::ON);
452            }
453
454            background->addDrawable(backgroundQuad);
455
456            if (_slideBackgroundAsHUD)
457            {
458                HUDTransform* hudTransform = new HUDTransform(_hudSettings.get());
459                hudTransform->addChild(background);
460                addToCurrentLayer(hudTransform);
461            }
462            else
463            {
464                addToCurrentLayer(background);
465            }
466        }
467
468        if (!_slideTitle.empty())
469        {
470            osg::Geode* geode = new osg::Geode;
471
472            osg::Vec3 localPosition = computePositionInModelCoords(_titlePositionData);
473
474            osgText::Text* text = new osgText::Text;
475            text->setFont(_titleFontData.font);
476            text->setColor(_titleFontData.color);
477            text->setCharacterSize(_titleFontData.characterSize*_slideHeight);
478            text->setFontResolution(110,120);
479            text->setMaximumWidth(_titleFontData.maximumWidth*_slideWidth);
480            text->setLayout(_titleFontData.layout);
481            text->setAlignment(_titleFontData.alignment);
482            text->setAxisAlignment(_titleFontData.axisAlignment);
483            //text->setPosition(_titlePositionData.position);
484            text->setPosition(localPosition);
485
486            text->setText(_slideTitle);
487
488            geode->addDrawable(text);
489
490            addToCurrentLayer(decorateSubgraphForPosition(geode, _titlePositionData));
491        }
492
493    }
494    else
495    {
496        // copy previous layer's children across into new layer.
497        for(unsigned int i=0;i<_previousLayer->getNumChildren();++i)
498        {
499            addToCurrentLayer(_previousLayer->getChild(i));
500        }
501    }
502
503    if (!defineAsBaseLayer)
504    {
505        _slide->addChild(_currentLayer.get());
506    }
507
508    _previousLayer = _currentLayer;
509}
510
511void SlideShowConstructor::selectLayer(int layerNum)
512{
513    if (!_slide)
514    {
515        addSlide();
516        addLayer();
517    }
518    else if (layerNum>=0 && layerNum<static_cast<int>(_slide->getNumChildren()) && _slide->getChild(layerNum)->asGroup())
519    {
520        _currentLayer = _slide->getChild(layerNum)->asGroup();
521        _previousLayer = _currentLayer;
522    }
523    else
524    {
525        addLayer();
526    }
527
528}
529
530
531void SlideShowConstructor::setLayerDuration(double duration)
532{
533    if (!_currentLayer) addLayer();
534
535    if (_currentLayer.valid())
536    {
537        setDuration(_currentLayer.get(),duration);
538    }
539}
540
541void SlideShowConstructor::addToCurrentLayer(osg::Node* subgraph)
542{
543    if (!subgraph) return;
544
545    if (!_currentLayer) addLayer();
546
547    if (!_currentEventCallbacksToApply.empty())
548    {
549        if (_layerToApplyEventCallbackTo==0 || _currentLayer==_layerToApplyEventCallbackTo)
550        {
551            OSG_NOTICE<<"Assigning event callbacks."<<std::endl;
552
553            for(EventHandlerList::iterator itr = _currentEventCallbacksToApply.begin();
554                itr != _currentEventCallbacksToApply.end();
555                ++itr)
556            {
557                subgraph->addEventCallback(itr->get());
558            }
559        }
560        else
561        {
562            OSG_INFO<<"Ignoring event callback from previous layer."<<std::endl;
563        }
564
565        _currentEventCallbacksToApply.clear();
566    }
567    _currentLayer->addChild(subgraph);
568}
569
570void SlideShowConstructor::addEventHandler(PresentationContext presentationContext, osg::ref_ptr<osgGA::GUIEventHandler> handler)
571{
572    switch(presentationContext)
573    {
574        case(CURRENT_PRESENTATION):
575            OSG_NOTICE<<"Need to add event handler to presentation."<<std::endl;
576            break;
577        case(CURRENT_SLIDE):
578            OSG_NOTICE<<"Need to add event handler to slide."<<std::endl;
579            break;
580        case(CURRENT_LAYER):
581            OSG_INFO<<"Add event handler to layer."<<std::endl;
582            _layerToApplyEventCallbackTo = _currentLayer;
583            _currentEventCallbacksToApply.push_back(handler);
584            break;
585    }
586}
587
588void SlideShowConstructor::keyToDoOperation(PresentationContext presentationContext, int key, Operation operation, const JumpData& jumpData)
589{
590    OSG_INFO<<"keyToDoOperation(key="<<key<<", operation="<<operation<<")"<<std::endl;
591    addEventHandler(presentationContext, new KeyEventHandler(key, operation, jumpData));
592}
593
594
595void SlideShowConstructor::keyToDoOperation(PresentationContext presentationContext, int key, const std::string& command, Operation operation, const JumpData& jumpData)
596{
597    OSG_INFO<<"keyToDoOperation(key="<<key<<",command="<<command<<")"<<std::endl;
598    addEventHandler(presentationContext, new KeyEventHandler(key, command, operation, jumpData));
599}
600
601
602void SlideShowConstructor::keyEventOperation(PresentationContext presentationContext, int key, const KeyPosition& keyPos,  const JumpData& jumpData)
603{
604    OSG_INFO<<"keyEventOperation(key="<<key<<")"<<std::endl;
605    addEventHandler(presentationContext, new KeyEventHandler(key, keyPos, jumpData));
606}
607
608
609void SlideShowConstructor::layerClickToDoOperation(Operation operation, const JumpData& jumpData)
610{
611    addEventHandler(CURRENT_LAYER, new PickEventHandler(operation, jumpData));
612}
613
614
615void SlideShowConstructor::layerClickToDoOperation(const std::string& command, Operation operation, const JumpData& jumpData)
616{
617    addEventHandler(CURRENT_LAYER, new PickEventHandler(command, operation, jumpData));
618}
619
620
621void SlideShowConstructor::layerClickEventOperation(const KeyPosition& keyPos, const JumpData& jumpData)
622{
623    addEventHandler(CURRENT_LAYER, new PickEventHandler(keyPos, jumpData));
624}
625
626
627osg::Node* SlideShowConstructor::decorateSubgraphForPosition(osg::Node* node, PositionData& positionData)
628{
629    osg::Node* subgraph = node;
630
631    if (positionData.requiresMaterialAnimation())
632    {
633        subgraph = attachMaterialAnimation(subgraph,positionData);
634    }
635
636    if (positionData.rotation[0]!=0.0)
637    {
638        osg::MatrixTransform* animation_transform = new osg::MatrixTransform;
639        animation_transform->setDataVariance(osg::Object::DYNAMIC);
640        animation_transform->setUpdateCallback(
641            new osgUtil::TransformCallback(subgraph->getBound().center(),
642                                           osg::Vec3(positionData.rotation[1],positionData.rotation[2],positionData.rotation[3]),
643                                           osg::DegreesToRadians(positionData.rotation[0])));
644        animation_transform->addChild(subgraph);
645
646        subgraph = animation_transform;
647    }
648
649    if (positionData.hud)
650    {
651        HUDTransform* hudTransform = new HUDTransform(_hudSettings.get());
652        hudTransform->addChild(subgraph);
653
654        subgraph = hudTransform;
655    }
656    return subgraph;
657}
658
659void SlideShowConstructor::addBullet(const std::string& bullet, PositionData& positionData, FontData& fontData)
660{
661    osg::Geode* geode = new osg::Geode;
662
663    osgText::Text* text = new osgText::Text;
664
665    osg::Vec3 localPosition = computePositionInModelCoords(positionData);
666
667    text->setFont(fontData.font);
668    text->setColor(fontData.color);
669    text->setCharacterSize(fontData.characterSize*_slideHeight);
670    text->setCharacterSizeMode(fontData.characterSizeMode);
671    text->setFontResolution(110,120);
672    text->setMaximumWidth(fontData.maximumWidth*_slideWidth);
673    text->setLayout(fontData.layout);
674    text->setAlignment(fontData.alignment);
675    text->setAxisAlignment(fontData.axisAlignment);
676    text->setPosition(localPosition);
677
678    if (positionData.autoRotate)
679    {
680        text->setAxisAlignment(osgText::Text::SCREEN);
681    }
682
683    if (positionData.autoScale)
684    {
685        text->setCharacterSizeMode(osgText::Text::SCREEN_COORDS);
686    }
687
688    text->setText(bullet);
689
690    osg::BoundingBox bb = text->getBound();
691
692    // note, this increment is only "correct" when text is on the plane of the slide..
693    // will need to make this more general later.
694    localPosition.z() = bb.zMin()-fontData.characterSize*_slideHeight*1.5;
695
696    geode->addDrawable(text);
697
698    addToCurrentLayer( decorateSubgraphForPosition(geode, positionData) );
699
700    bool needToApplyPosition = (_textPositionData.position == positionData.position);
701    if (needToApplyPosition)
702    {
703        updatePositionFromInModelCoords(localPosition, _textPositionData);
704    }
705}
706
707void SlideShowConstructor::addParagraph(const std::string& paragraph, PositionData& positionData, FontData& fontData)
708{
709    osg::Geode* geode = new osg::Geode;
710
711    osg::Vec3 localPosition = computePositionInModelCoords(positionData);
712
713    osgText::Text* text = new osgText::Text;
714
715    text->setFont(fontData.font);
716    text->setColor(fontData.color);
717    text->setCharacterSize(fontData.characterSize*_slideHeight);
718    text->setCharacterSizeMode(fontData.characterSizeMode);
719    text->setFontResolution(110,120);
720    text->setMaximumWidth(fontData.maximumWidth*_slideWidth);
721    text->setLayout(fontData.layout);
722    text->setAlignment(fontData.alignment);
723    text->setAxisAlignment(fontData.axisAlignment);
724    text->setPosition(localPosition);
725
726    if (positionData.autoRotate)
727    {
728        text->setAxisAlignment(osgText::Text::SCREEN);
729    }
730
731    if (positionData.autoScale)
732    {
733        text->setCharacterSizeMode(osgText::Text::SCREEN_COORDS);
734    }
735    text->setText(paragraph);
736
737    osg::BoundingBox bb = text->getBound();
738
739    // note, this increment is only "correct" when text is on the plane of the slide..
740    // will need to make this more general later.
741    localPosition.z() = bb.zMin()-fontData.characterSize*_slideHeight*1.5;
742
743    geode->addDrawable(text);
744
745    addToCurrentLayer( decorateSubgraphForPosition(geode, positionData) );
746
747    bool needToApplyPosition = (_textPositionData.position == positionData.position);
748    if (needToApplyPosition)
749    {
750        updatePositionFromInModelCoords(localPosition, _textPositionData);
751    }
752}
753
754class FindImageStreamsVisitor : public osg::NodeVisitor
755{
756public:
757    FindImageStreamsVisitor():
758        osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN) {}
759
760    virtual void apply(osg::Node& node)
761    {
762        if (node.getStateSet())
763        {
764                process(node.getStateSet());
765        }
766        traverse(node);
767    }
768
769    virtual void apply(osg::Geode& node)
770    {
771        if (node.getStateSet())
772        {
773                process(node.getStateSet());
774        }
775
776        for(unsigned int i=0;i<node.getNumDrawables();++i)
777        {
778            osg::Drawable* drawable = node.getDrawable(i);
779            if (drawable && drawable->getStateSet())
780            {
781                process(drawable->getStateSet());
782            }
783        }
784    }
785
786    void process(osg::StateSet* ss)
787    {
788        for(unsigned int i=0;i<ss->getTextureAttributeList().size();++i)
789        {
790            osg::Texture* texture = dynamic_cast<osg::Texture*>(ss->getTextureAttribute(i,osg::StateAttribute::TEXTURE));
791            osg::Image* image = texture ? texture->getImage(0) : 0;
792            osg::ImageStream* imageStream = image ? dynamic_cast<osg::ImageStream*>(image) : 0;
793            if (imageStream)
794            {
795                texture->setDataVariance(osg::Object::DYNAMIC);
796                texture->setUnRefImageDataAfterApply(false);
797                texture->setResizeNonPowerOfTwoHint(false);
798                texture->setFilter(osg::Texture::MIN_FILTER,osg::Texture::LINEAR);
799                texture->setFilter(osg::Texture::MAG_FILTER,osg::Texture::LINEAR);
800#if USE_CLIENT_STORAGE_HINT
801                texture->setClientStorageHint(true);
802#endif         
803            }
804        }
805    }
806
807};
808
809void SlideShowConstructor::findImageStreamsAndAddCallbacks(osg::Node* node)
810{
811    FindImageStreamsVisitor fisv;
812    node->accept(fisv);
813}
814
815
816osg::Geometry* SlideShowConstructor::createTexturedQuadGeometry(const osg::Vec3& pos, const osg::Vec4& rotation, float width, float height, osg::Image* image, bool& usedTextureRectangle)
817{
818    osg::Geometry* pictureQuad = 0;
819    osg::ref_ptr<osg::Texture> texture = 0;
820    osg::StateSet* stateset = 0;
821
822    osg::Vec3 positionVec = pos;
823    osg::Vec3 widthVec(width,0.0f,0.0f);
824    osg::Vec3 heightVec(0.0f,0.0f,height);
825
826    osg::Matrixd rotationMatrix = osg::Matrixd::rotate(osg::DegreesToRadians(rotation[0]),rotation[1],rotation[2],rotation[3]);
827    widthVec = widthVec*rotationMatrix;
828    heightVec = heightVec*rotationMatrix;
829
830    osg::ImageStream* imageStream = dynamic_cast<osg::ImageStream*>(image);
831   
832    // let the video-plugin create a texture for us, if supported
833    #if USE_TEXTURE_FROM_VIDEO_PLUGIN
834    if(imageStream)
835    {
836        texture = imageStream->createSuitableTexture();
837    }
838    #endif
839   
840    bool flipYAxis = image->getOrigin()==osg::Image::TOP_LEFT;
841
842#if 1
843    bool useTextureRectangle = false;
844#else
845    #ifdef __sgi
846        bool useTextureRectangle = false;
847    #else
848        bool useTextureRectangle = true;
849    #endif
850#endif
851   
852    // pass back info on wether texture 2D is used.
853    usedTextureRectangle = useTextureRectangle;
854
855    if (!texture)
856    {
857        if (useTextureRectangle)
858        {
859            texture = new osg::TextureRectangle(image);
860        }
861        else
862        {
863            texture = new osg::Texture2D(image);
864
865            texture->setResizeNonPowerOfTwoHint(false);
866            texture->setFilter(osg::Texture::MIN_FILTER,osg::Texture::LINEAR);
867            texture->setFilter(osg::Texture::MAG_FILTER,osg::Texture::LINEAR);
868    #if USE_CLIENT_STORAGE_HINT       
869            texture->setClientStorageHint(true);
870    #endif
871           
872        }
873    }
874    if (texture)
875    {
876        float t(0), l(0);
877        float r = (texture->getTextureTarget() == GL_TEXTURE_RECTANGLE) ? image->s() : 1;
878        float b = (texture->getTextureTarget() == GL_TEXTURE_RECTANGLE) ? image->t() : 1;
879       
880        if (flipYAxis)
881            std::swap(t,b);
882       
883        pictureQuad = osg::createTexturedQuadGeometry(positionVec,
884                                               widthVec,
885                                               heightVec,
886                                               l, t, r, b);
887       
888        stateset = pictureQuad->getOrCreateStateSet();
889        stateset->setTextureAttributeAndModes(0,
890                        texture,
891                        osg::StateAttribute::ON);
892    }
893   
894    if (!pictureQuad) return 0;
895   
896    if (imageStream)
897    {
898        imageStream->pause();
899
900        OSG_INFO<<"Reading video "<<imageStream->getFileName()<<std::endl;
901#if USE_CLIENT_STORAGE_HINT
902        // make sure that OSX uses the client storage extension to accelerate peformance where possible.
903        texture->setClientStorageHint(true);
904#endif 
905    }
906
907
908    return pictureQuad;
909}
910
911
912
913osg::Image* SlideShowConstructor::readImage(const std::string& filename, const ImageData& imageData)
914{
915    osg::ref_ptr<osgDB::Options> options = _options;
916    if (!imageData.options.empty())
917    {
918        options = _options->cloneOptions();
919        options->setOptionString(imageData.options);
920    }
921
922    osg::ref_ptr<osg::Image> image;
923    osgDB::DirectoryContents filenames;
924
925    std::string foundFile = filename;
926
927    if (imageData.imageSequence)
928    {
929        // check for wild cards
930        if (filename.find('*')!=std::string::npos)
931        {
932            OSG_INFO<<"Expanding wildcard "<<std::endl;
933            filenames = osgDB::expandWildcardsInFilename(filename);
934        }
935        else
936        {
937            std::string foundFile = filename;
938            osgDB::FileType fileType = osgDB::fileType(foundFile);
939            if (fileType == osgDB::FILE_NOT_FOUND)
940            {
941                foundFile = findFileAndRecordPath(foundFile);
942                fileType = osgDB::fileType(foundFile);
943            }
944
945            if (fileType == osgDB::DIRECTORY)
946            {
947                OSG_INFO<<"Reading directory "<<foundFile<<std::endl;
948
949                filenames = osgDB::getDirectoryContents(foundFile);
950
951                // need to insert the directory path in front of the filenames so it's relative to the appropriate directory.
952                for(osgDB::DirectoryContents::iterator itr = filenames.begin();
953                    itr != filenames.end();
954                    ++itr)
955                {
956                    *itr = foundFile + osgDB::getNativePathSeparator() + *itr;
957                }
958
959                // prune any directory entries from the list.
960                for(osgDB::DirectoryContents::iterator itr = filenames.begin();
961                    itr != filenames.end();
962                    )
963                {
964                    if (osgDB::fileType(*itr)!=osgDB::REGULAR_FILE)
965                    {
966                        itr = filenames.erase(itr);
967                    }
968                    else
969                    {
970                        ++itr;
971                    }
972                }
973            }
974            else
975            {
976                filenames.push_back(foundFile);
977            }
978        }
979    }
980    else
981    {
982        std::string foundFile = filename;
983        osgDB::FileType fileType = osgDB::fileType(foundFile);
984        if (fileType == osgDB::FILE_NOT_FOUND)
985        {
986            foundFile = findFileAndRecordPath(foundFile);
987            fileType = osgDB::fileType(foundFile);
988        }
989        filenames.push_back(foundFile);
990    }
991   
992    if (filenames.empty()) return 0;
993
994    if (filenames.size()==1)
995    {
996        image = osgDB::readImageFile(filenames[0], options.get());
997        if (image.valid()) recordOptionsFilePath(options.get() );
998    }
999    else
1000    {
1001        // make sure images are in alphabetical order.
1002        std::sort(filenames.begin(), filenames.end(), osgDB::FileNameComparator());
1003
1004        osg::ref_ptr<osg::ImageSequence> imageSequence = new osg::ImageSequence;
1005
1006        imageSequence->setMode(imageData.imageSequencePagingMode);
1007
1008        bool firstLoad = true;
1009
1010        for(osgDB::DirectoryContents::iterator itr = filenames.begin();
1011            itr != filenames.end();
1012            ++itr)
1013        {
1014            if (imageSequence->getMode()==osg::ImageSequence::PRE_LOAD_ALL_IMAGES)
1015            {
1016                OSG_INFO<<"Attempting to read "<<*itr<<std::endl;
1017                osg::ref_ptr<osg::Image> loadedImage = osgDB::readImageFile(*itr, options.get());
1018                if (loadedImage.valid())
1019                {
1020                    OSG_INFO<<"Loaded image "<<*itr<<std::endl;
1021                    imageSequence->addImage(loadedImage.get());
1022                }
1023            }
1024            else
1025            {
1026                OSG_INFO<<"Adding filename for load image on demand "<<*itr<<std::endl;
1027                imageSequence->addImageFile(*itr);
1028                if (firstLoad)
1029                {
1030                    osg::ref_ptr<osg::Image> loadedImage = osgDB::readImageFile(*itr, options.get());
1031                    if (loadedImage.valid())
1032                    {                   
1033                        imageSequence->addImage(loadedImage.get());
1034                        firstLoad = false;
1035                    }
1036                }
1037            }
1038        }
1039
1040#if 0
1041        if (imageSequence->getMode()==osg::ImageSequence::PAGE_AND_DISCARD_USED_IMAGES)
1042        {
1043
1044            if (_options.valid())
1045            {
1046                OSG_NOTICE<<"Object cache usage _options "<<options->getObjectCacheHint()<<std::endl;
1047            }
1048            else
1049            {
1050                OSG_NOTICE<<"No Object _options assigned"<<std::endl;
1051            }
1052
1053           
1054            osg::ref_ptr<osgDB::Options> options = _options.valid() ? _options->cloneOptions() : (new osgDB::Options);
1055            if (!imageData.options.empty())
1056            {
1057                options->setOptionString(imageData.options);
1058            }
1059            OSG_NOTICE<<"Disabling object cache usage"<<std::endl;
1060            options->setObjectCacheHint(osgDB::Options::CACHE_NONE);
1061            imageSequence->setReadOptions(options);
1062        }
1063#endif
1064        if (imageData.duration>0.0)
1065        {
1066            imageSequence->setLength(imageData.duration);
1067        }
1068        else
1069        {
1070            unsigned int maxNum = imageSequence->getNumImageData();
1071            imageSequence->setLength(double(maxNum)*(1.0/imageData.fps));
1072        }
1073
1074        if (imageData.imageSequenceInteractionMode==ImageData::USE_MOUSE_X_POSITION)
1075        {
1076            imageSequence->setName("USE_MOUSE_X_POSITION");
1077        }
1078        else if (imageData.imageSequenceInteractionMode==ImageData::USE_MOUSE_Y_POSITION)
1079        {
1080            imageSequence->setName("USE_MOUSE_Y_POSITION");
1081        }
1082           
1083
1084        imageSequence->play();
1085
1086        image = imageSequence;
1087    }
1088
1089    return image.release();
1090}
1091
1092void SlideShowConstructor::addImage(const std::string& filename, const PositionData& positionData, const ImageData& imageData)
1093{
1094
1095    osg::ref_ptr<osgVolume::Volume> volume;
1096    osg::ref_ptr<osgVolume::VolumeTile> tile;
1097    osg::ref_ptr<osgVolume::ImageLayer> layer;
1098
1099    osg::ref_ptr<osg::Image> image = readImage(filename, imageData);
1100    if (!image) return;
1101
1102    bool isImageTranslucent = false;
1103
1104    osg::ImageStream* imageStream = dynamic_cast<osg::ImageStream*>(image.get());
1105    if (imageStream)
1106    {
1107        imageStream->setLoopingMode(imageData.loopingMode);
1108
1109        isImageTranslucent = imageStream->getPixelFormat()==GL_RGBA ||
1110                             imageStream->getPixelFormat()==GL_BGRA;
1111
1112    }
1113    else
1114    {
1115        isImageTranslucent = image->isImageTranslucent();
1116    }
1117
1118    float s = image->s();
1119    float t = image->t();
1120
1121    float sx = imageData.region_in_pixel_coords ? 1.0f : s;
1122    float sy = imageData.region_in_pixel_coords ? 1.0f : t;
1123
1124    float x1 = imageData.region[0]*sx;
1125    float y1 = imageData.region[1]*sy;
1126    float x2 = imageData.region[2]*sx;
1127    float y2 = imageData.region[3]*sy;
1128
1129    float aspectRatio = (y2-y1)/(x2-x1);
1130
1131    float image_width = _slideWidth*positionData.scale.x();
1132    float image_height = image_width*aspectRatio*positionData.scale.y()/positionData.scale.x();
1133    float offset = 0.0f;
1134
1135    osg::Vec3 pos = computePositionInModelCoords(positionData);
1136    osg::Vec3 image_local_pos = osg::Vec3(-image_width*0.5f+offset,-offset,-image_height*0.5f-offset);
1137    osg::Vec3 image_pos = positionData.autoRotate ? image_local_pos : (pos+image_local_pos);
1138
1139
1140    bool usedTextureRectangle = false;
1141    osg::Geometry* pictureQuad = createTexturedQuadGeometry(image_pos, positionData.rotate, image_width, image_height, image.get(), usedTextureRectangle);
1142    osg::StateSet* pictureStateSet = pictureQuad->getOrCreateStateSet();
1143
1144    attachTexMat(pictureStateSet, imageData, s, t, usedTextureRectangle);
1145
1146    osg::Node* subgraph = 0;
1147
1148    if (positionData.autoRotate)
1149    {
1150        osg::Billboard* picture = new osg::Billboard;
1151        picture->setMode(osg::Billboard::POINT_ROT_EYE);
1152        picture->setNormal(osg::Vec3(0.0f,-1.0f,0.0f));
1153        picture->setAxis(osg::Vec3(0.0f,0.0f,1.0f));
1154        picture->addDrawable(pictureQuad,pos);
1155        subgraph = picture;
1156    }
1157    else
1158    {
1159        osg::Geode* picture = new osg::Geode;
1160        picture->addDrawable(pictureQuad);
1161        subgraph = picture;
1162    }
1163
1164    // attach any meterial animation.
1165    if (positionData.requiresMaterialAnimation())
1166        subgraph = attachMaterialAnimation(subgraph,positionData);
1167
1168
1169    if (isImageTranslucent)
1170    {
1171        SetToTransparentBin sttb;
1172        subgraph->accept(sttb);
1173        pictureStateSet->setMode(GL_BLEND, osg::StateAttribute::ON);
1174    }
1175
1176    osg::ImageSequence* imageSequence = dynamic_cast<osg::ImageSequence*>(image.get());
1177    if (imageSequence)       
1178    {
1179        if (imageData.imageSequenceInteractionMode==ImageData::USE_MOUSE_X_POSITION)
1180        {
1181            subgraph->setUpdateCallback(new osgPresentation::ImageSequenceUpdateCallback(imageSequence, _propertyManager.get(), "mouse.x_normalized"));
1182        }
1183        else if (imageData.imageSequenceInteractionMode==ImageData::USE_MOUSE_Y_POSITION)
1184        {
1185            subgraph->setUpdateCallback(new osgPresentation::ImageSequenceUpdateCallback(imageSequence, _propertyManager.get(), "mouse.y_normalized"));
1186        }
1187    }
1188       
1189    // attached any rotation
1190    if (positionData.rotation[0]!=0.0)
1191    {
1192        osg::MatrixTransform* animation_transform = new osg::MatrixTransform;
1193        animation_transform->setDataVariance(osg::Object::DYNAMIC);
1194        animation_transform->setUpdateCallback(
1195            new osgUtil::TransformCallback(subgraph->getBound().center(),
1196                                           osg::Vec3(positionData.rotation[1],positionData.rotation[2],positionData.rotation[3]),
1197                                           osg::DegreesToRadians(positionData.rotation[0])));
1198
1199        animation_transform->addChild(subgraph);
1200
1201        subgraph = animation_transform;
1202    }
1203
1204    // attached any animation
1205    osg::AnimationPathCallback* animation = getAnimationPathCallback(positionData);
1206    if (animation)
1207    {
1208        OSG_INFO<<"Have animation path for image"<<std::endl;
1209
1210        osg::BoundingSphere::vec_type pivot = positionData.absolute_path ?
1211                osg::BoundingSphere::vec_type(0.0f,0.0f,0.0f) :
1212                subgraph->getBound().center();
1213
1214        osg::PositionAttitudeTransform* animation_transform = new osg::PositionAttitudeTransform;
1215        animation_transform->setDataVariance(osg::Object::DYNAMIC);
1216        animation_transform->setPivotPoint(pivot);
1217        animation->setPivotPoint(pivot);
1218
1219        animation_transform->setUpdateCallback(animation);
1220
1221        animation_transform->setReferenceFrame(positionData.absolute_path ?
1222                                                    osg::Transform::ABSOLUTE_RF:
1223                                                    osg::Transform::RELATIVE_RF);
1224
1225        animation_transform->addChild(subgraph);
1226
1227        subgraph = animation_transform;
1228    }
1229
1230    if (positionData.hud)
1231    {
1232        HUDTransform* hudTransform = new HUDTransform(_hudSettings.get());
1233        hudTransform->addChild(subgraph);
1234
1235        subgraph = hudTransform;
1236    }
1237
1238    addToCurrentLayer(subgraph);
1239}
1240
1241void SlideShowConstructor::addStereoImagePair(const std::string& filenameLeft, const ImageData& imageDataLeft, const std::string& filenameRight, const ImageData& imageDataRight,const PositionData& positionData)
1242{
1243    osg::ref_ptr<osg::Image> imageLeft = readImage(filenameLeft, imageDataLeft);
1244    osg::ref_ptr<osg::Image> imageRight = (filenameRight==filenameLeft) ? imageLeft.get() : readImage(filenameRight, imageDataRight);
1245   
1246    if (!imageLeft && !imageRight) return;
1247
1248    bool isImageTranslucent = false;
1249
1250    osg::ImageStream* imageStreamLeft = dynamic_cast<osg::ImageStream*>(imageLeft.get());
1251    if (imageStreamLeft)
1252    {
1253        imageStreamLeft->setLoopingMode(imageDataLeft.loopingMode);
1254        isImageTranslucent = imageStreamLeft->getPixelFormat()==GL_RGBA ||
1255                             imageStreamLeft->getPixelFormat()==GL_BGRA;
1256    }
1257    else
1258    {
1259        isImageTranslucent = imageLeft->isImageTranslucent();
1260    }
1261
1262    osg::ImageStream* imageStreamRight = dynamic_cast<osg::ImageStream*>(imageRight.get());
1263    if (imageStreamRight)
1264    {
1265        imageStreamRight->setLoopingMode(imageDataRight.loopingMode);
1266        if (!isImageTranslucent)
1267        {
1268            isImageTranslucent = imageStreamRight->getPixelFormat()==GL_RGBA ||
1269                                imageStreamRight->getPixelFormat()==GL_BGRA;
1270        }
1271    }
1272    else if (!isImageTranslucent)
1273    {
1274        isImageTranslucent = imageRight->isImageTranslucent();
1275    }
1276
1277
1278    float s = imageLeft->s();
1279    float t = imageLeft->t();
1280
1281
1282    float sx = imageDataLeft.region_in_pixel_coords ? 1.0f : s;
1283    float sy = imageDataLeft.region_in_pixel_coords ? 1.0f : t;
1284
1285    float x1 = imageDataLeft.region[0]*sx;
1286    float y1 = imageDataLeft.region[1]*sy;
1287    float x2 = imageDataLeft.region[2]*sx;
1288    float y2 = imageDataLeft.region[3]*sy;
1289
1290    float aspectRatio = (y2-y1)/(x2-x1);
1291
1292    float image_width = _slideWidth*positionData.scale.x();
1293    float image_height = image_width*aspectRatio*positionData.scale.y()/positionData.scale.x();
1294
1295    float offset = 0.0f;
1296
1297    bool usedTextureRectangle = false;
1298
1299    osg::Vec3 pos = computePositionInModelCoords(positionData);
1300    osg::Vec3 image_local_pos = osg::Vec3(-image_width*0.5f+offset,-offset,-image_height*0.5f-offset);
1301    osg::Vec3 image_pos = positionData.autoRotate ? image_local_pos : (pos+image_local_pos);
1302
1303
1304    osg::Node* pictureLeft = 0;
1305    {
1306        osg::Geometry* pictureLeftQuad = createTexturedQuadGeometry(image_pos, positionData.rotate, image_width,image_height,imageLeft.get(),usedTextureRectangle);
1307        osg::StateSet* pictureLeftStateSet = pictureLeftQuad->getOrCreateStateSet();
1308
1309        if (isImageTranslucent)
1310        {
1311            pictureLeftStateSet->setMode(GL_BLEND, osg::StateAttribute::ON);
1312        }
1313
1314        attachTexMat(pictureLeftStateSet, imageDataLeft, s, t, usedTextureRectangle);
1315
1316        if (positionData.autoRotate)
1317        {
1318            osg::Billboard* billboard = new osg::Billboard;
1319            billboard->setMode(osg::Billboard::POINT_ROT_EYE);
1320            billboard->setNormal(osg::Vec3(0.0f,-1.0f,0.0f));
1321            billboard->setAxis(osg::Vec3(0.0f,0.0f,1.0f));
1322            billboard->addDrawable(pictureLeftQuad,pos);
1323            pictureLeft = billboard;
1324        }
1325        else
1326        {
1327            osg::Geode* geode = new osg::Geode;
1328            geode->addDrawable(pictureLeftQuad);
1329            pictureLeft = geode;
1330        }
1331
1332        pictureLeft->setNodeMask(_leftEyeMask);
1333    }
1334
1335    osg::Node* pictureRight = 0;
1336    {
1337        osg::Geometry* pictureRightQuad = createTexturedQuadGeometry(image_pos, positionData.rotate, image_width,image_height,imageRight.get(),usedTextureRectangle);
1338        osg::StateSet* pictureRightStateSet = pictureRightQuad->getOrCreateStateSet();
1339
1340        if (isImageTranslucent)
1341        {
1342            pictureRightStateSet->setMode(GL_BLEND, osg::StateAttribute::ON);
1343        }
1344
1345        attachTexMat(pictureRightStateSet, imageDataRight, s, t, usedTextureRectangle);
1346
1347        if (positionData.autoRotate)
1348        {
1349            osg::Billboard* billboard = new osg::Billboard;
1350            billboard->setMode(osg::Billboard::POINT_ROT_EYE);
1351            billboard->setNormal(osg::Vec3(0.0f,-1.0f,0.0f));
1352            billboard->setAxis(osg::Vec3(0.0f,0.0f,1.0f));
1353            billboard->addDrawable(pictureRightQuad,pos);
1354            pictureRight = billboard;
1355        }
1356        else
1357        {
1358            osg::Geode* geode = new osg::Geode;
1359            geode->addDrawable(pictureRightQuad);
1360            pictureRight = geode;
1361        }
1362
1363        pictureRight->setNodeMask(_rightEyeMask);
1364    }
1365
1366    osg::Group* subgraph = new osg::Group;
1367    subgraph->addChild(pictureLeft);
1368    subgraph->addChild(pictureRight);
1369
1370    osg::ImageSequence* imageSequence = dynamic_cast<osg::ImageSequence*>(imageLeft.get());
1371    if (imageSequence)
1372    {
1373        if (imageDataLeft.imageSequenceInteractionMode==ImageData::USE_MOUSE_X_POSITION)
1374        {
1375            subgraph->setUpdateCallback(new osgPresentation::ImageSequenceUpdateCallback(imageSequence, _propertyManager.get(), "mouse.x_normalized"));
1376        }
1377        else if (imageDataLeft.imageSequenceInteractionMode==ImageData::USE_MOUSE_Y_POSITION)
1378        {
1379            subgraph->setUpdateCallback(new osgPresentation::ImageSequenceUpdateCallback(imageSequence, _propertyManager.get(), "mouse.y_normalized"));
1380        }
1381    }
1382
1383    // attach any meterial animation.
1384    if (positionData.requiresMaterialAnimation())
1385        subgraph = attachMaterialAnimation(subgraph,positionData)->asGroup();
1386
1387    if (isImageTranslucent)
1388    {
1389        SetToTransparentBin sttb;
1390        subgraph->accept(sttb);
1391    }
1392
1393    // attached any rotation
1394    if (positionData.rotation[0]!=0.0)
1395    {
1396        osg::MatrixTransform* animation_transform = new osg::MatrixTransform;
1397        animation_transform->setDataVariance(osg::Object::DYNAMIC);
1398        animation_transform->setUpdateCallback(
1399            new osgUtil::TransformCallback(subgraph->getBound().center(),
1400                                           osg::Vec3(positionData.rotation[1],positionData.rotation[2],positionData.rotation[3]),
1401                                           osg::DegreesToRadians(positionData.rotation[0])));
1402
1403        animation_transform->addChild(subgraph);
1404
1405        subgraph = animation_transform;
1406    }
1407
1408    // attached any animation
1409    osg::AnimationPathCallback* animation = getAnimationPathCallback(positionData);
1410    if (animation)
1411    {
1412        OSG_INFO<<"Have animation path for image"<<std::endl;
1413
1414        osg::BoundingSphere::vec_type pivot = positionData.absolute_path ?
1415                osg::BoundingSphere::vec_type(0.0f,0.0f,0.0f) :
1416                subgraph->getBound().center();
1417
1418        osg::PositionAttitudeTransform* animation_transform = new osg::PositionAttitudeTransform;
1419        animation_transform->setDataVariance(osg::Object::DYNAMIC);
1420        animation_transform->setPivotPoint(pivot);
1421        animation->setPivotPoint(pivot);
1422
1423        animation_transform->setUpdateCallback(animation);
1424        animation_transform->setReferenceFrame(positionData.absolute_path ?
1425                                                    osg::Transform::ABSOLUTE_RF:
1426                                                    osg::Transform::RELATIVE_RF);
1427
1428        animation_transform->addChild(subgraph);
1429
1430        subgraph = animation_transform;
1431    }
1432
1433    if (positionData.hud)
1434    {
1435        HUDTransform* hudTransform = new HUDTransform(_hudSettings.get());
1436        hudTransform->addChild(subgraph);
1437
1438        subgraph = hudTransform;
1439    }
1440
1441    addToCurrentLayer(subgraph);
1442}
1443
1444void SlideShowConstructor::addGraph(const std::string& contents, const PositionData& positionData, const ImageData& imageData)
1445{
1446    static int s_count=0;
1447
1448    if (contents.empty()) return;
1449
1450    std::string tmpDirectory("/tmp/");
1451
1452    std::string filename = contents;
1453    std::string ext = osgDB::getFileExtension(contents);
1454    if (ext.empty())
1455    {
1456        std::stringstream dotFileNameStream;
1457        dotFileNameStream << tmpDirectory<<"graph_"<<s_count<<std::string(".dot");
1458        filename = dotFileNameStream.str();
1459
1460        // write out the string to the temporary file.
1461        std::ofstream fout(filename.c_str());
1462        fout<<contents.c_str();
1463    }
1464
1465    std::stringstream svgFileNameStream;
1466    svgFileNameStream << tmpDirectory<<osgDB::getStrippedName(filename)<<s_count<<std::string(".svg");
1467    std::string tmpSvgFileName(svgFileNameStream.str());
1468    std::string dotFileName = filename;
1469
1470    if (osgDB::getFileExtension(filename)=="dot")
1471    {
1472        dotFileName = filename;
1473    }
1474    else
1475    {
1476        osg::ref_ptr<osg::Node> model = osgDB::readNodeFile(filename, _options.get());
1477        if (!model) return;
1478
1479        dotFileName = tmpDirectory+osgDB::getStrippedName(filename)+std::string(".dot");
1480
1481        osg::ref_ptr<osgDB::Options> opts = _options.valid() ? _options->cloneOptions() : (new osgDB::Options);
1482        if (!imageData.options.empty())
1483        {
1484            opts->setOptionString(imageData.options);
1485        }
1486        opts->setObjectCacheHint(osgDB::Options::CACHE_NONE);
1487
1488        osgDB::writeNodeFile(*model, dotFileName, opts.get());
1489    }
1490
1491    std::stringstream command;
1492    command<<"dot -Tsvg "<<dotFileName<<" -o "<<tmpSvgFileName;
1493    int result = system(command.str().c_str());
1494    if (result==0)
1495    {
1496        osg::ref_ptr<osgDB::Options> previousOptions = _options;
1497
1498        // switch off cache so we make sure that we re-read the generated svg each time.
1499        _options = _options.valid() ? _options->cloneOptions() : (new osgDB::Options);
1500        _options->setObjectCacheHint(osgDB::Options::CACHE_NONE);
1501
1502        addImage(tmpSvgFileName, positionData, imageData);
1503
1504        _options = previousOptions;
1505
1506        ++s_count;
1507    }
1508    else OSG_NOTICE<<"Error: SlideShowConstructor::addGraph() system("<<command.str()<<") failed with return "<<result<<std::endl;
1509}
1510
1511
1512void SlideShowConstructor::addVNC(const std::string& hostname, const PositionData& positionData, const ImageData& imageData, const std::string& password)
1513{
1514    if (!password.empty())
1515    {
1516        OSG_NOTICE<<"Setting password"<<std::endl;
1517        if (!osgDB::Registry::instance()->getAuthenticationMap()) osgDB::Registry::instance()->setAuthenticationMap(new osgDB::AuthenticationMap);
1518        osgDB::Registry::instance()->getAuthenticationMap()->addAuthenticationDetails(hostname, new osgDB::AuthenticationDetails("", password));
1519    }
1520
1521    addInteractiveImage(hostname+".vnc", positionData, imageData);
1522}
1523
1524void SlideShowConstructor::addBrowser(const std::string& url, const PositionData& positionData, const ImageData& imageData)
1525{
1526    addInteractiveImage(url+".gecko", positionData, imageData);
1527}
1528
1529void SlideShowConstructor::addPDF(const std::string& filename, const PositionData& positionData, const ImageData& imageData)
1530{
1531    addInteractiveImage(filename, positionData, imageData);
1532}
1533
1534class SetPageCallback: public LayerCallback
1535{
1536public:
1537    SetPageCallback(osgWidget::PdfImage* pdfImage, int pageNum):
1538        _pdfImage(pdfImage),
1539        _pageNum(pageNum)
1540    {
1541    }
1542
1543    virtual void operator() (osg::Node*) const
1544    {
1545        OSG_INFO<<"PDF Page to be updated "<<_pageNum<<std::endl;
1546
1547        if (_pdfImage.valid() && _pdfImage->getPageNum()!=_pageNum)
1548        {
1549            _pdfImage->page(_pageNum);
1550        }
1551    }
1552
1553    osg::observer_ptr<osgWidget::PdfImage> _pdfImage;
1554    int _pageNum;
1555};
1556
1557
1558osg::Image* SlideShowConstructor::addInteractiveImage(const std::string& filename, const PositionData& positionData, const ImageData& imageData)
1559{
1560    osg::ref_ptr<osgDB::Options> options = _options;
1561    if (!imageData.options.empty())
1562    {
1563        options = _options->cloneOptions();
1564        options->setOptionString(imageData.options);
1565    }
1566
1567    osg::Image* image = osgDB::readImageFile(filename, options.get());
1568
1569    OSG_INFO<<"addInteractiveImage("<<filename<<") "<<image<<std::endl;
1570
1571
1572    if (!image) return 0;
1573
1574    float s = image->s();
1575    float t = image->t();
1576
1577    float sx = imageData.region_in_pixel_coords ? 1.0f : s;
1578    float sy = imageData.region_in_pixel_coords ? 1.0f : t;
1579
1580    float x1 = imageData.region[0]*sx;
1581    float y1 = imageData.region[1]*sy;
1582    float x2 = imageData.region[2]*sx;
1583    float y2 = imageData.region[3]*sy;
1584
1585    float aspectRatio = (y2-y1)/(x2-x1);
1586
1587    float image_width = _slideWidth*positionData.scale.x();
1588    float image_height = image_width*aspectRatio*positionData.scale.y()/positionData.scale.x();
1589    float offset = 0.0f;
1590
1591    osg::Vec3 pos = computePositionInModelCoords(positionData);
1592    osg::Vec3 image_local_pos = osg::Vec3(-image_width*0.5f+offset,-offset,-image_height*0.5f-offset);
1593    osg::Vec3 image_pos = positionData.autoRotate ? image_local_pos : (pos+image_local_pos);
1594
1595    bool usedTextureRectangle = false;
1596    osg::Geometry* pictureQuad = createTexturedQuadGeometry(image_pos, positionData.rotate, image_width, image_height, image, usedTextureRectangle);
1597
1598    osg::ref_ptr<osgViewer::InteractiveImageHandler> handler = new osgViewer::InteractiveImageHandler(image);
1599    pictureQuad->setEventCallback(handler.get());
1600    pictureQuad->setCullCallback(handler.get());
1601
1602    osg::StateSet* pictureStateSet = pictureQuad->getOrCreateStateSet();
1603
1604    attachTexMat(pictureStateSet, imageData, s, t, usedTextureRectangle);
1605
1606    pictureStateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
1607
1608    osg::Node* subgraph = 0;
1609
1610    if (positionData.autoRotate)
1611    {
1612        osg::Billboard* picture = new osg::Billboard;
1613        picture->setMode(osg::Billboard::POINT_ROT_EYE);
1614        picture->setNormal(osg::Vec3(0.0f,-1.0f,0.0f));
1615        picture->setAxis(osg::Vec3(0.0f,0.0f,1.0f));
1616        picture->addDrawable(pictureQuad,pos);
1617        subgraph = picture;
1618    }
1619    else
1620    {
1621        osg::Geode* picture = new osg::Geode;
1622        picture->addDrawable(pictureQuad);
1623        subgraph = picture;
1624    }
1625
1626    // attach any meterial animation.
1627    if (positionData.requiresMaterialAnimation())
1628        subgraph = attachMaterialAnimation(subgraph,positionData);
1629
1630
1631    // attached any rotation
1632    if (positionData.rotation[0]!=0.0)
1633    {
1634        osg::MatrixTransform* animation_transform = new osg::MatrixTransform;
1635        animation_transform->setDataVariance(osg::Object::DYNAMIC);
1636        animation_transform->setUpdateCallback(
1637            new osgUtil::TransformCallback(subgraph->getBound().center(),
1638                                           osg::Vec3(positionData.rotation[1],positionData.rotation[2],positionData.rotation[3]),
1639                                           osg::DegreesToRadians(positionData.rotation[0])));
1640
1641        animation_transform->addChild(subgraph);
1642
1643        subgraph = animation_transform;
1644    }
1645
1646
1647    // attached any animation
1648    osg::AnimationPathCallback* animation = getAnimationPathCallback(positionData);
1649    if (animation)
1650    {
1651        OSG_INFO<<"Have animation path for image"<<std::endl;
1652
1653        osg::BoundingSphere::vec_type pivot = positionData.absolute_path ?
1654                osg::BoundingSphere::vec_type(0.0f,0.0f,0.0f) :
1655                subgraph->getBound().center();
1656
1657        osg::PositionAttitudeTransform* animation_transform = new osg::PositionAttitudeTransform;
1658        animation_transform->setDataVariance(osg::Object::DYNAMIC);
1659        animation_transform->setPivotPoint(pivot);
1660        animation->setPivotPoint(pivot);
1661
1662        animation_transform->setUpdateCallback(animation);
1663
1664        animation_transform->setReferenceFrame(positionData.absolute_path ?
1665                                                    osg::Transform::ABSOLUTE_RF:
1666                                                    osg::Transform::RELATIVE_RF);
1667
1668        animation_transform->addChild(subgraph);
1669
1670        subgraph = animation_transform;
1671    }
1672
1673    if (positionData.hud)
1674    {
1675        HUDTransform* hudTransform = new HUDTransform(_hudSettings.get());
1676        hudTransform->addChild(subgraph);
1677
1678        subgraph = hudTransform;
1679    }
1680
1681    addToCurrentLayer(subgraph);
1682
1683    osgWidget::PdfImage* pdfImage = dynamic_cast<osgWidget::PdfImage*>(image);
1684    if (pdfImage && imageData.page>=0)
1685    {
1686        getOrCreateLayerAttributes(_currentLayer.get())->addEnterCallback(new SetPageCallback(pdfImage, imageData.page));
1687
1688        OSG_INFO<<"Setting pdf page num "<<imageData.page<<std::endl;
1689        pdfImage->setBackgroundColor(imageData.backgroundColor);
1690        pdfImage->page(imageData.page);
1691
1692        if (imageData.backgroundColor.a()<1.0f)
1693        {
1694            SetToTransparentBin sttb;
1695            subgraph->accept(sttb);
1696        }
1697
1698
1699    }
1700
1701
1702    return image;
1703}
1704
1705std::string SlideShowConstructor::findFileAndRecordPath(const std::string& filename)
1706{
1707    std::string foundFile = osgDB::findDataFile(filename, _options.get());
1708    if (foundFile.empty()) return foundFile;
1709
1710    OSG_INFO<<"foundFile "<<foundFile<<std::endl;
1711
1712    std::string path = osgDB::getFilePath(foundFile);
1713    if (!path.empty() && _filePathData.valid())
1714    {
1715        osgDB::FilePathList::iterator itr = std::find(_filePathData->filePathList.begin(),_filePathData->filePathList.end(),path);
1716        if (itr==_filePathData->filePathList.end())
1717        {
1718            OSG_INFO<<"New path to record "<<path<<std::endl;
1719            _filePathData->filePathList.push_front(path);
1720        }
1721    }
1722
1723    return foundFile;
1724
1725}
1726
1727void SlideShowConstructor::addModel(const std::string& filename, const PositionData& positionData, const ModelData& modelData)
1728{
1729    OSG_INFO<<"SlideShowConstructor::addModel("<<filename<<")"<<std::endl;
1730
1731    osg::ref_ptr<osgDB::Options> options = _options;
1732    if (!modelData.options.empty())
1733    {
1734        options = _options->cloneOptions();
1735        options->setOptionString(modelData.options);
1736    }
1737
1738    osg::Node* subgraph = 0;
1739
1740    if (filename=="sphere")
1741    {
1742        osg::Geode* geode = new osg::Geode;
1743        geode->addDrawable(new osg::ShapeDrawable(new osg::Sphere));
1744
1745        subgraph = geode;
1746    }
1747    else if (filename=="box")
1748    {
1749        osg::Geode* geode = new osg::Geode;
1750        geode->addDrawable(new osg::ShapeDrawable(new osg::Box));
1751
1752        subgraph = geode;
1753    }
1754    else
1755    {
1756        subgraph = osgDB::readNodeFile(filename, options.get());
1757        if (subgraph) recordOptionsFilePath(options.get());
1758    }
1759
1760    if (subgraph)
1761    {
1762        addModel(subgraph, positionData, modelData);
1763    }
1764
1765    OSG_INFO<<"end of SlideShowConstructor::addModel("<<filename<<")"<<std::endl<<std::endl;
1766
1767}
1768
1769void SlideShowConstructor::addModel(osg::Node* subgraph, const PositionData& positionData, const ModelData& modelData)
1770{
1771    osg::Object::DataVariance defaultMatrixDataVariance = osg::Object::DYNAMIC; // STATIC
1772
1773    if (!modelData.effect.empty())
1774    {
1775        if (modelData.effect=="SpecularHighlights" || modelData.effect=="glossy")
1776        {
1777            osgFX::SpecularHighlights* specularHighlights = new osgFX::SpecularHighlights;
1778            specularHighlights->setTextureUnit(1);
1779            specularHighlights->addChild(subgraph);
1780            subgraph = specularHighlights;
1781        }
1782    }
1783
1784
1785    if (positionData.frame==SLIDE)
1786    {
1787        osg::Vec3 pos = convertSlideToModel(positionData.position);
1788
1789        const osg::BoundingSphere& bs = subgraph->getBound();
1790        float slide_scale = _slideHeight*(1.0f-positionData.position.z())*0.7f/bs.radius();
1791
1792        osg::MatrixTransform* transform = new osg::MatrixTransform;
1793        transform->setDataVariance(defaultMatrixDataVariance);
1794        transform->setMatrix(osg::Matrix::translate(-bs.center())*
1795                             osg::Matrix::scale(positionData.scale.x()*slide_scale, positionData.scale.y()*slide_scale ,positionData.scale.z()*slide_scale)*
1796                             osg::Matrix::rotate(osg::DegreesToRadians(positionData.rotate[0]),positionData.rotate[1],positionData.rotate[2],positionData.rotate[3])*
1797                             osg::Matrix::translate(pos));
1798
1799
1800        transform->setStateSet(createTransformStateSet());
1801        transform->addChild(subgraph);
1802
1803        subgraph = transform;
1804
1805    }
1806    else
1807    {
1808        osg::Matrix matrix(osg::Matrix::scale(1.0f/positionData.scale.x(),1.0f/positionData.scale.y(),1.0f/positionData.scale.z())*
1809                           osg::Matrix::rotate(osg::DegreesToRadians(positionData.rotate[0]),positionData.rotate[1],positionData.rotate[2],positionData.rotate[3])*
1810                           osg::Matrix::translate(positionData.position));
1811
1812        osg::MatrixTransform* transform = new osg::MatrixTransform;
1813        transform->setDataVariance(defaultMatrixDataVariance);
1814        transform->setMatrix(osg::Matrix::inverse(matrix));
1815
1816        OSG_INFO<<"Position Matrix "<<transform->getMatrix()<<std::endl;
1817
1818        transform->addChild(subgraph);
1819
1820        subgraph = transform;
1821    }
1822
1823    float referenceSizeRatio = 0.707;
1824    float referenceSize = subgraph->getBound().radius() * referenceSizeRatio;
1825
1826
1827    // attach any meterial animation.
1828    if (positionData.requiresMaterialAnimation())
1829        subgraph = attachMaterialAnimation(subgraph,positionData);
1830
1831    // attached any rotation
1832    if (positionData.rotation[0]!=0.0)
1833    {
1834        osg::MatrixTransform* animation_transform = new osg::MatrixTransform;
1835        animation_transform->setDataVariance(osg::Object::DYNAMIC);
1836        animation_transform->setUpdateCallback(
1837            new osgUtil::TransformCallback(subgraph->getBound().center(),
1838                                           osg::Vec3(positionData.rotation[1],positionData.rotation[2],positionData.rotation[3]),
1839                                           osg::DegreesToRadians(positionData.rotation[0])));
1840
1841        animation_transform->addChild(subgraph);
1842
1843        OSG_INFO<<"Rotation Matrix "<<animation_transform->getMatrix()<<std::endl;
1844
1845        subgraph = animation_transform;
1846    }
1847
1848
1849    // attached any animation
1850    osg::AnimationPathCallback* animation = getAnimationPathCallback(positionData);
1851    if (animation)
1852    {
1853        OSG_INFO<<"Have animation path for model"<<std::endl;
1854
1855        osg::BoundingSphere::vec_type pivot = positionData.absolute_path ?
1856            osg::BoundingSphere::vec_type(0.0f,0.0f,0.0f) :
1857            subgraph->getBound().center();
1858
1859        osg::AnimationPath* path = animation->getAnimationPath();
1860        if (positionData.animation_name=="wheel" && (path->getTimeControlPointMap()).size()>=2)
1861        {
1862            OSG_INFO<<"****  Need to handle special wheel animation"<<std::endl;
1863
1864            osg::AnimationPath::TimeControlPointMap& controlPoints = path->getTimeControlPointMap();
1865
1866            osg::AnimationPath::TimeControlPointMap::iterator curr_itr = controlPoints.begin();
1867            osg::AnimationPath::TimeControlPointMap::iterator prev_itr=curr_itr;
1868            ++curr_itr;
1869
1870            osg::AnimationPath::ControlPoint* prev_cp = &(prev_itr->second);
1871            osg::AnimationPath::ControlPoint* curr_cp = &(curr_itr->second);
1872
1873            float totalLength = 0;
1874            float rotation_y_axis = 0;
1875            osg::Vec3 delta_position = curr_cp->getPosition() - prev_cp->getPosition();
1876            float rotation_z_axis = atan2f(delta_position.y(),delta_position.x());
1877
1878            osg::Quat quat_y_axis,quat_z_axis,quat_combined;
1879
1880            quat_y_axis.makeRotate(rotation_y_axis,0.0f,1.0f,0.0f);
1881            quat_z_axis.makeRotate(rotation_z_axis,0.0f,0.0f,1.0f);
1882            quat_combined = quat_y_axis*quat_z_axis;
1883
1884            // set first rotation.
1885            prev_cp->setRotation(quat_combined);
1886
1887            for(;
1888                curr_itr!=controlPoints.end();
1889                ++curr_itr)
1890            {
1891                prev_cp = &(prev_itr->second);
1892                curr_cp = &(curr_itr->second);
1893
1894                delta_position = curr_cp->getPosition() - prev_cp->getPosition();
1895
1896                totalLength += delta_position.length();
1897
1898                // rolling - rotation about the y axis.
1899                rotation_y_axis = totalLength/referenceSize;
1900
1901                // direction - rotation about the z axis.
1902                rotation_z_axis = atan2f(delta_position.y(),delta_position.x());
1903
1904                OSG_INFO<<" rotation_y_axis="<<rotation_y_axis<<" rotation_z_axis="<<rotation_z_axis<<std::endl;
1905
1906                quat_y_axis.makeRotate(rotation_y_axis,0.0f,1.0f,0.0f);
1907                quat_z_axis.makeRotate(rotation_z_axis,0.0f,0.0f,1.0f);
1908                quat_combined = quat_y_axis*quat_z_axis;
1909
1910                curr_cp->setRotation(quat_combined);
1911
1912                prev_itr = curr_itr;
1913
1914            }
1915
1916        }
1917
1918
1919        osg::PositionAttitudeTransform* animation_transform = new osg::PositionAttitudeTransform;
1920        animation_transform->setDataVariance(osg::Object::DYNAMIC);
1921        animation_transform->setPivotPoint(pivot);
1922        animation->setPivotPoint(pivot);
1923        animation_transform->setUpdateCallback(animation);
1924
1925        animation_transform->setReferenceFrame(positionData.absolute_path ?
1926                                                    osg::Transform::ABSOLUTE_RF:
1927                                                    osg::Transform::RELATIVE_RF);
1928
1929        animation_transform->addChild(subgraph);
1930
1931        subgraph = animation_transform;
1932    }
1933
1934    findImageStreamsAndAddCallbacks(subgraph);
1935
1936    addToCurrentLayer(subgraph);
1937}
1938
1939class DraggerVolumeTileCallback : public osgManipulator::DraggerCallback
1940{
1941public:
1942
1943    DraggerVolumeTileCallback(osgVolume::VolumeTile* volume, osgVolume::Locator* locator):
1944        _volume(volume),
1945        _locator(locator) {}
1946
1947
1948    virtual bool receive(const osgManipulator::MotionCommand& command);
1949
1950
1951    osg::observer_ptr<osgVolume::VolumeTile>    _volume;
1952    osg::ref_ptr<osgVolume::Locator>            _locator;
1953
1954    osg::Matrix _startMotionMatrix;
1955
1956    osg::Matrix _localToWorld;
1957    osg::Matrix _worldToLocal;
1958
1959};
1960
1961bool DraggerVolumeTileCallback::receive(const osgManipulator::MotionCommand& command)
1962{
1963    if (!_locator) return false;
1964
1965    switch (command.getStage())
1966    {
1967        case osgManipulator::MotionCommand::START:
1968        {
1969            // Save the current matrix
1970            _startMotionMatrix = _locator->getTransform();
1971
1972            // Get the LocalToWorld and WorldToLocal matrix for this node.
1973            osg::NodePath nodePathToRoot;
1974            osgManipulator::computeNodePathToRoot(*_volume,nodePathToRoot);
1975            _localToWorld = _startMotionMatrix * osg::computeLocalToWorld(nodePathToRoot);
1976            _worldToLocal = osg::Matrix::inverse(_localToWorld);
1977
1978            return true;
1979        }
1980        case osgManipulator::MotionCommand::MOVE:
1981        {
1982            // Transform the command's motion matrix into local motion matrix.
1983            osg::Matrix localMotionMatrix = _localToWorld * command.getWorldToLocal()
1984                                            * command.getMotionMatrix()
1985                                            * command.getLocalToWorld() * _worldToLocal;
1986
1987            // Transform by the localMotionMatrix
1988            _locator->setTransform(localMotionMatrix * _startMotionMatrix);
1989
1990            // OSG_NOTICE<<"New locator matrix "<<_locator->getTransform()<<std::endl;
1991
1992            return true;
1993        }
1994        case osgManipulator::MotionCommand::FINISH:
1995        {
1996            return true;
1997        }
1998        case osgManipulator::MotionCommand::NONE:
1999        default:
2000            return false;
2001    }
2002}
2003
2004void SlideShowConstructor::addVolume(const std::string& filename, const PositionData& in_positionData, const VolumeData& volumeData)
2005{
2006    // osg::Object::DataVariance defaultMatrixDataVariance = osg::Object::DYNAMIC; // STATIC
2007
2008    PositionData positionData(in_positionData);
2009
2010    osg::ref_ptr<osgDB::Options> options = _options;
2011    if (!volumeData.options.empty())
2012    {
2013        options = _options->cloneOptions();
2014        options->setOptionString(volumeData.options);
2015    }
2016
2017    std::string foundFile = filename;
2018    osg::ref_ptr<osg::Image> image;
2019    osg::ref_ptr<osgVolume::Volume> volume;
2020    osg::ref_ptr<osgVolume::VolumeTile> tile;
2021    osg::ref_ptr<osgVolume::ImageLayer> layer;
2022
2023    // check for wild cards
2024    if (filename.find('*')!=std::string::npos)
2025    {
2026        osgDB::DirectoryContents filenames = osgDB::expandWildcardsInFilename(filename);
2027        if (filenames.empty()) return;
2028
2029        // make sure images are in alphabetical order.
2030        std::sort(filenames.begin(), filenames.end(), osgDB::FileNameComparator());
2031
2032        typedef std::vector< osg::ref_ptr<osg::Image> > Images;
2033        Images images;
2034        for(osgDB::DirectoryContents::iterator itr = filenames.begin();
2035            itr != filenames.end();
2036            ++itr)
2037        {
2038            osg::ref_ptr<osg::Image> loadedImage = osgDB::readImageFile(*itr, options.get());
2039            if (loadedImage.valid())
2040            {
2041                images.push_back(loadedImage.get());
2042            }
2043        }
2044
2045        image = osg::createImage3DWithAlpha(images);
2046    }
2047    else
2048    {
2049        osgDB::FileType fileType = osgDB::fileType(foundFile);
2050        if (fileType == osgDB::FILE_NOT_FOUND)
2051        {
2052            foundFile = findFileAndRecordPath(foundFile);
2053            fileType = osgDB::fileType(foundFile);
2054        }
2055
2056        if (fileType == osgDB::DIRECTORY)
2057        {
2058            image = osgDB::readImageFile(foundFile+".dicom", options.get());
2059        }
2060        else if (fileType == osgDB::REGULAR_FILE)
2061        {
2062            std::string ext = osgDB::getFileExtension(foundFile);
2063            if (ext=="osg" || ext=="ive" || ext=="osgx" || ext=="osgb" || ext=="osgt")
2064            {
2065                osg::ref_ptr<osg::Object> obj = osgDB::readObjectFile(foundFile);
2066                image = dynamic_cast<osg::Image*>(obj.get());
2067                volume = dynamic_cast<osgVolume::Volume*>(obj.get());
2068            }
2069            else
2070            {
2071                image = osgDB::readImageFile( foundFile, options.get() );
2072            }
2073        }
2074        else
2075        {
2076            // not found image, so fallback to plugins/callbacks to find the model.
2077            image = osgDB::readImageFile( filename, options.get() );
2078            if (image) recordOptionsFilePath(options.get() );
2079        }
2080    }
2081
2082    if (!image && !volume) return;
2083
2084
2085    if (volumeData.colorSpaceOperation!=osg::NO_COLOUR_SPACE_OPERATION)
2086    {
2087        OSG_NOTICE<<"Doing colour space conversion"<<std::endl;
2088        osg::ref_ptr<osg::Image> converted_image = osg::colorSpaceConversion(volumeData.colorSpaceOperation, image.get(), volumeData.colorModulate);
2089        if (converted_image!=image)
2090        {
2091            image->swap(*converted_image);
2092        }
2093    }
2094   
2095    if (positionData.scale.x()<0.0)
2096    {
2097        image->flipHorizontal();
2098        positionData.scale.x() = fabs(positionData.scale.x());
2099
2100        OSG_INFO<<"addVolume(..) image->flipHorizontal();"<<std::endl;
2101    }
2102
2103    if (positionData.scale.y()<0.0)
2104    {
2105        image->flipVertical();
2106        positionData.scale.y() = fabs(positionData.scale.y());
2107
2108        OSG_INFO<<"addVolume(..) image->flipVertical();"<<std::endl;
2109    }
2110
2111    if (positionData.scale.z()<0.0)
2112    {
2113        image->flipDepth();
2114        positionData.scale.z() = fabs(positionData.scale.z());
2115
2116        OSG_INFO<<"addVolume(..) image->flipDepth();"<<std::endl;
2117    }
2118
2119    if (volume.valid())
2120    {
2121        if (!tile)
2122        {
2123            if (volume->getNumChildren()>0)
2124            {
2125                tile = dynamic_cast<osgVolume::VolumeTile*>(volume->getChild(0));
2126            }
2127        }
2128    }
2129    else
2130    {
2131        volume = new osgVolume::Volume;
2132    }
2133
2134    if (tile.valid())
2135    {
2136        layer = dynamic_cast<osgVolume::ImageLayer*>(tile->getLayer());
2137        image = layer.valid() ? layer->getImage() : 0;
2138    }
2139    else
2140    {
2141        if (!image) return;
2142
2143        tile = new osgVolume::VolumeTile;
2144        volume->addChild(tile.get());
2145    }
2146
2147    if (!layer)
2148    {
2149        if (!image) return;
2150
2151        osg::ref_ptr<osgVolume::ImageDetails> details = dynamic_cast<osgVolume::ImageDetails*>(image->getUserData());
2152        osg::ref_ptr<osg::RefMatrix> matrix = details ? details->getMatrix() : dynamic_cast<osg::RefMatrix*>(image->getUserData());
2153
2154        osg::ref_ptr<osgVolume::ImageLayer> layer = new osgVolume::ImageLayer(image.get());
2155        if (details)
2156        {
2157            layer->setTexelOffset(details->getTexelOffset());
2158            layer->setTexelScale(details->getTexelScale());
2159        }
2160        layer->rescaleToZeroToOneRange();
2161
2162        osg::Matrix tm = osg::Matrix::scale(volumeData.region[3]-volumeData.region[0], volumeData.region[4]-volumeData.region[1], volumeData.region[5]-volumeData.region[2]) *
2163                            osg::Matrix::translate(volumeData.region[0],volumeData.region[1],volumeData.region[2]);
2164
2165        if (matrix.valid())
2166        {
2167            layer->setLocator(new osgVolume::Locator(*matrix));
2168            tile->setLocator(new osgVolume::Locator(tm * (*matrix)));
2169        }
2170        else
2171        {
2172            layer->setLocator(new osgVolume::Locator());
2173            tile->setLocator(new osgVolume::Locator(tm));
2174        }
2175
2176
2177        tile->setLayer(layer.get());
2178
2179        osgVolume::SwitchProperty* sp = new osgVolume::SwitchProperty;
2180        sp->setActiveProperty(0);
2181
2182        osgVolume::AlphaFuncProperty* ap = new osgVolume::AlphaFuncProperty(volumeData.cutoffValue);
2183        osgVolume::TransparencyProperty* tp = new osgVolume::TransparencyProperty(volumeData.alphaValue);
2184        osgVolume::SampleDensityProperty* sd = new osgVolume::SampleDensityProperty(volumeData.sampleDensityValue);
2185        osgVolume::SampleDensityWhenMovingProperty* sdm = (volumeData.sampleDensityWhenMovingValue > 0.0f) ? (new osgVolume::SampleDensityWhenMovingProperty(volumeData.sampleDensityWhenMovingValue)) : 0;
2186        osgVolume::TransferFunctionProperty* tfp = volumeData.transferFunction.valid() ? new osgVolume::TransferFunctionProperty(volumeData.transferFunction.get()) : 0;
2187
2188        {
2189            // Standard
2190            osgVolume::CompositeProperty* cp = new osgVolume::CompositeProperty;
2191            cp->addProperty(ap);
2192            cp->addProperty(sd);
2193            cp->addProperty(tp);
2194            if (sdm) cp->addProperty(sdm);
2195            if (tfp) cp->addProperty(tfp);
2196
2197            sp->addProperty(cp);
2198        }
2199
2200        {
2201            // Light
2202            osgVolume::CompositeProperty* cp = new osgVolume::CompositeProperty;
2203            cp->addProperty(ap);
2204            cp->addProperty(sd);
2205            cp->addProperty(tp);
2206            cp->addProperty(new osgVolume::LightingProperty);
2207            if (sdm) cp->addProperty(sdm);
2208            if (tfp) cp->addProperty(tfp);
2209
2210            sp->addProperty(cp);
2211        }
2212
2213        {
2214            // Isosurface
2215            osgVolume::CompositeProperty* cp = new osgVolume::CompositeProperty;
2216            cp->addProperty(sd);
2217            cp->addProperty(tp);
2218            cp->addProperty(new osgVolume::IsoSurfaceProperty(volumeData.cutoffValue));
2219            if (sdm) cp->addProperty(sdm);
2220            if (tfp) cp->addProperty(tfp);
2221
2222            sp->addProperty(cp);
2223        }
2224
2225        {
2226            // MaximumIntensityProjection
2227            osgVolume::CompositeProperty* cp = new osgVolume::CompositeProperty;
2228            cp->addProperty(ap);
2229            cp->addProperty(sd);
2230            cp->addProperty(tp);
2231            cp->addProperty(new osgVolume::MaximumIntensityProjectionProperty);
2232            if (sdm) cp->addProperty(sdm);
2233            if (tfp) cp->addProperty(tfp);
2234
2235            sp->addProperty(cp);
2236        }
2237
2238        switch(volumeData.shadingModel)
2239        {
2240            case(VolumeData::Standard):                     sp->setActiveProperty(0); break;
2241            case(VolumeData::Light):                        sp->setActiveProperty(1); break;
2242            case(VolumeData::Isosurface):                   sp->setActiveProperty(2); break;
2243            case(VolumeData::MaximumIntensityProjection):   sp->setActiveProperty(3); break;
2244        }
2245
2246        layer->addProperty(sp);
2247        tile->setVolumeTechnique(new osgVolume::RayTracedTechnique);
2248        tile->setEventCallback(new osgVolume::PropertyAdjustmentCallback());
2249    }
2250
2251    osg::ref_ptr<osg::Node> model = volume.get();
2252
2253    if (volumeData.useTabbedDragger || volumeData.useTrackballDragger)
2254    {
2255        osg::ref_ptr<osg::Group> group = new osg::Group;
2256
2257        osg::ref_ptr<osgManipulator::Dragger> dragger;
2258        if (volumeData.useTabbedDragger)
2259        {
2260            if (volumeData.useTrackballDragger)
2261                dragger = new osgManipulator::TabBoxTrackballDragger;
2262            else
2263                dragger = new osgManipulator::TabBoxDragger;
2264        }
2265        else
2266            dragger = new osgManipulator::TrackballDragger();
2267
2268        dragger->setupDefaultGeometry();
2269        dragger->setHandleEvents(true);
2270        dragger->setActivationModKeyMask(osgGA::GUIEventAdapter::MODKEY_SHIFT);
2271        dragger->addDraggerCallback(new DraggerVolumeTileCallback(tile.get(), tile->getLocator()));
2272        dragger->setMatrix(osg::Matrix::translate(0.5,0.5,0.5)*tile->getLocator()->getTransform());
2273
2274
2275        group->addChild(dragger.get());
2276
2277        //dragger->addChild(volume.get());
2278
2279        group->addChild(volume.get());
2280
2281        model = group.get();
2282    }
2283
2284
2285
2286    ModelData modelData;
2287    addModel(model.get(), positionData, modelData);
2288}
2289
2290bool SlideShowConstructor::attachTexMat(osg::StateSet* stateset, const ImageData& imageData, float s, float t, bool textureRectangle)
2291{
2292    float xScale = textureRectangle ? s : 1.0f;
2293    float yScale = textureRectangle ? t : 1.0f;
2294
2295    float sx = (textureRectangle ? s : 1.0f) / (imageData.region_in_pixel_coords ? s : 1.0f);
2296    float sy = (textureRectangle ? t : 1.0f) / (imageData.region_in_pixel_coords ? t : 1.0f);
2297
2298    float x1 = imageData.region[0]*sx;
2299    float y1 = imageData.region[1]*sy;
2300    float x2 = imageData.region[2]*sx;
2301    float y2 = imageData.region[3]*sy;
2302
2303    if (x1!=0.0f || y1!=0.0f || x2!=xScale || y2 != yScale ||
2304        imageData.texcoord_rotate != 0.0f)
2305    {
2306        osg::TexMat* texmat = new osg::TexMat;
2307        texmat->setMatrix(osg::Matrix::translate(-0.5f*xScale,-0.5f*yScale,0.0f)*
2308                          osg::Matrix::rotate(osg::DegreesToRadians(imageData.texcoord_rotate),0.0f,0.0f,1.0f)*
2309                          osg::Matrix::translate(0.5f*xScale,0.5f*yScale,0.0f)*
2310                          osg::Matrix::scale((x2-x1)/xScale,(y2-y1)/yScale,1.0f)*
2311                          osg::Matrix::translate(x1,
2312                                                 y1,
2313                                                 0.0f));
2314
2315        stateset->setTextureAttribute(0,texmat);
2316        return true;
2317    }
2318    return false;
2319}
2320
2321osg::Node* SlideShowConstructor::attachMaterialAnimation(osg::Node* model, const PositionData& positionData)
2322{
2323    AnimationMaterial* animationMaterial = 0;
2324
2325    if (!positionData.animation_material_filename.empty())
2326    {
2327#if 0
2328        std::string absolute_animation_file_path = osgDB::findDataFile(positionData.animation_material_filename, _options.get());
2329        if (!absolute_animation_file_path.empty())
2330        {
2331            std::ifstream animation_filestream(absolute_animation_file_path.c_str());
2332            if (!animation_filestream.eof())
2333            {
2334                animationMaterial = new AnimationMaterial;
2335                animationMaterial->read(animation_filestream);
2336            }
2337        }
2338#else
2339        osg::ref_ptr<osg::Object> object = osgDB::readObjectFile(positionData.animation_material_filename, _options.get());
2340        animationMaterial = dynamic_cast<AnimationMaterial*>(object.get());
2341#endif
2342
2343    }
2344    else if (!positionData.fade.empty())
2345    {
2346        std::istringstream iss(positionData.fade);
2347
2348        animationMaterial = new AnimationMaterial;
2349        while (!iss.fail() && !iss.eof())
2350        {
2351            float time=1.0f, alpha=1.0f;
2352            iss >> time >> alpha;
2353            if (!iss.fail())
2354            {
2355                osg::Material* material = new osg::Material;
2356                material->setAmbient(osg::Material::FRONT_AND_BACK,osg::Vec4(1.0f,1.0f,1.0f,alpha));
2357                material->setDiffuse(osg::Material::FRONT_AND_BACK,osg::Vec4(1.0f,1.0f,1.0f,alpha));
2358                animationMaterial->insert(time,material);
2359            }
2360        }
2361    }
2362
2363    if (animationMaterial)
2364    {
2365        animationMaterial->setLoopMode(positionData.animation_material_loop_mode);
2366
2367        AnimationMaterialCallback* animationMaterialCallback = new AnimationMaterialCallback(animationMaterial);
2368        animationMaterialCallback->setTimeOffset(positionData.animation_material_time_offset);
2369        animationMaterialCallback->setTimeMultiplier(positionData.animation_material_time_multiplier);
2370
2371        osg::Group* decorator = new osg::Group;
2372        decorator->addChild(model);
2373
2374        decorator->setUpdateCallback(animationMaterialCallback);
2375
2376        if (animationMaterial->requiresBlending())
2377        {
2378            SetToTransparentBin sttb;
2379            decorator->accept(sttb);
2380        }
2381
2382        return decorator;
2383    }
2384
2385    return model;
2386}
2387
2388osg::AnimationPathCallback* SlideShowConstructor::getAnimationPathCallback(const PositionData& positionData)
2389{
2390    if (!positionData.path.empty())
2391    {
2392        // need to create an Options object with cache off to prevent sharing of animation paths
2393        osg::ref_ptr<osgDB::Options> options = _options.valid() ? _options->cloneOptions() : new osgDB::Options;
2394        options->setObjectCacheHint(osgDB::Options::CACHE_NONE);
2395
2396        osg::ref_ptr<osg::Object> object = osgDB::readObjectFile(positionData.path, options.get());
2397        osg::AnimationPath* animation = dynamic_cast<osg::AnimationPath*>(object.get());
2398        if (animation)
2399        {
2400            if (positionData.frame==SlideShowConstructor::SLIDE)
2401            {
2402                osg::AnimationPath::TimeControlPointMap& controlPoints = animation->getTimeControlPointMap();
2403                for(osg::AnimationPath::TimeControlPointMap::iterator itr=controlPoints.begin();
2404                    itr!=controlPoints.end();
2405                    ++itr)
2406                {
2407                    osg::AnimationPath::ControlPoint& cp = itr->second;
2408                    cp.setPosition(convertSlideToModel(cp.getPosition()+positionData.position));
2409                }
2410            }
2411
2412            animation->setLoopMode(positionData.path_loop_mode);
2413
2414            osg::AnimationPathCallback* apc = new osg::AnimationPathCallback(animation);
2415            apc->setTimeOffset(positionData.path_time_offset);
2416            apc->setTimeMultiplier(positionData.path_time_multiplier);
2417            apc->setUseInverseMatrix(positionData.inverse_path);
2418
2419            OSG_INFO<<"UseInverseMatrix "<<positionData.inverse_path<<std::endl;
2420
2421            return apc;
2422
2423        }
2424
2425    }
2426    return 0;
2427}
2428
2429osg::Vec3 SlideShowConstructor::computePositionInModelCoords(const PositionData& positionData) const
2430{
2431    if (positionData.frame==SLIDE)
2432    {
2433        OSG_INFO<<"********* Scaling from slide coords to model coords"<<std::endl;
2434        return convertSlideToModel(positionData.position);
2435    }
2436    else
2437    {
2438        OSG_INFO<<"keeping original model coords"<<std::endl;
2439        return positionData.position;
2440    }
2441}
2442
2443osg::Vec3 SlideShowConstructor::convertSlideToModel(const osg::Vec3& position) const
2444{
2445    return osg::Vec3(_slideOrigin+osg::Vec3(_slideWidth*position.x(),0.0f,_slideHeight*position.y()))*(1.0f-position.z());
2446}
2447
2448osg::Vec3 SlideShowConstructor::convertModelToSlide(const osg::Vec3& position) const
2449{
2450    return osg::Vec3((position.x()*(_slideOrigin.y()/position.y())-_slideOrigin.x())/_slideWidth,
2451                     (position.z()*(_slideOrigin.y()/position.y())-_slideOrigin.z())/_slideHeight,
2452                     1.0f-position.y()/_slideOrigin.y());
2453}
2454
2455void SlideShowConstructor::updatePositionFromInModelCoords(const osg::Vec3& vertex, PositionData& positionData) const
2456{
2457    if (positionData.frame==SLIDE)
2458    {
2459        positionData.position = convertModelToSlide(vertex);
2460    }
2461    else
2462    {
2463        positionData.position = vertex;
2464    }
2465}
2466
2467void SlideShowConstructor::recordOptionsFilePath(const osgDB::Options* options)
2468{
2469    if (options)
2470    {
2471        std::string filename_used = _options->getPluginStringData("filename");
2472        std::string path = osgDB::getFilePath(filename_used);
2473        if (!path.empty() && _filePathData.valid())
2474        {
2475            osgDB::FilePathList::iterator itr = std::find(_filePathData->filePathList.begin(),_filePathData->filePathList.end(),path);
2476            if (itr==_filePathData->filePathList.end())
2477            {
2478                OSG_INFO<<"SlideShowConstructor::recordOptionsFilePath(..) - new path to record path="<<path<<" filename_used="<<filename_used<<std::endl;
2479                _filePathData->filePathList.push_front(path);
2480            }
2481        }
2482    }
2483}
Note: See TracBrowser for help on using the browser.