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

Revision 13191, 80.1 kB (checked in by robert, 9 hours ago)

Added comment about using -DCMAKE_DISABLE_FINDPACKAGE_* command line.

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