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

Revision 13179, 79.4 kB (checked in by robert, 21 hours ago)

Release OpenSceneGraph-3.3.3

  • 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 (imageData.duration>0.0)
1003        {
1004            imageSequence->setLength(imageData.duration);
1005        }
1006        else
1007        {
1008            unsigned int maxNum = osg::maximum(imageSequence->getFileNames().size(),
1009                                               imageSequence->getImages().size());
1010
1011            imageSequence->setLength(double(maxNum)*(1.0/imageData.fps));
1012        }
1013
1014        if (imageData.imageSequenceInteractionMode==ImageData::USE_MOUSE_X_POSITION)
1015        {
1016            imageSequence->setName("USE_MOUSE_X_POSITION");
1017        }
1018
1019        imageSequence->play();
1020
1021        image = imageSequence;
1022    }
1023
1024    return image.release();
1025}
1026
1027void SlideShowConstructor::addImage(const std::string& filename, const PositionData& positionData, const ImageData& imageData)
1028{
1029
1030    osg::ref_ptr<osgVolume::Volume> volume;
1031    osg::ref_ptr<osgVolume::VolumeTile> tile;
1032    osg::ref_ptr<osgVolume::ImageLayer> layer;
1033
1034    osg::ref_ptr<osg::Image> image = readImage(filename, imageData);
1035    if (!image) return;
1036
1037    bool isImageTranslucent = false;
1038
1039    osg::ImageStream* imageStream = dynamic_cast<osg::ImageStream*>(image.get());
1040    if (imageStream)
1041    {
1042        imageStream->setLoopingMode(imageData.loopingMode);
1043
1044        isImageTranslucent = imageStream->getPixelFormat()==GL_RGBA ||
1045                             imageStream->getPixelFormat()==GL_BGRA;
1046
1047    }
1048    else
1049    {
1050        isImageTranslucent = image->isImageTranslucent();
1051    }
1052
1053    float s = image->s();
1054    float t = image->t();
1055
1056    float sx = imageData.region_in_pixel_coords ? 1.0f : s;
1057    float sy = imageData.region_in_pixel_coords ? 1.0f : t;
1058
1059    float x1 = imageData.region[0]*sx;
1060    float y1 = imageData.region[1]*sy;
1061    float x2 = imageData.region[2]*sx;
1062    float y2 = imageData.region[3]*sy;
1063
1064    float aspectRatio = (y2-y1)/(x2-x1);
1065
1066    float image_width = _slideWidth*positionData.scale.x();
1067    float image_height = image_width*aspectRatio*positionData.scale.y()/positionData.scale.x();
1068    float offset = 0.0f;
1069
1070    osg::Vec3 pos = computePositionInModelCoords(positionData);
1071    osg::Vec3 image_local_pos = osg::Vec3(-image_width*0.5f+offset,-offset,-image_height*0.5f-offset);
1072    osg::Vec3 image_pos = positionData.autoRotate ? image_local_pos : (pos+image_local_pos);
1073
1074
1075    bool usedTextureRectangle = false;
1076    osg::Geometry* pictureQuad = createTexturedQuadGeometry(image_pos, positionData.rotate, image_width, image_height, image.get(), usedTextureRectangle);
1077    osg::StateSet* pictureStateSet = pictureQuad->getOrCreateStateSet();
1078
1079    attachTexMat(pictureStateSet, imageData, s, t, usedTextureRectangle);
1080
1081    osg::Node* subgraph = 0;
1082
1083    if (positionData.autoRotate)
1084    {
1085        osg::Billboard* picture = new osg::Billboard;
1086        picture->setMode(osg::Billboard::POINT_ROT_EYE);
1087        picture->setNormal(osg::Vec3(0.0f,-1.0f,0.0f));
1088        picture->setAxis(osg::Vec3(0.0f,0.0f,1.0f));
1089        picture->addDrawable(pictureQuad,pos);
1090        subgraph = picture;
1091    }
1092    else
1093    {
1094        osg::Geode* picture = new osg::Geode;
1095        picture->addDrawable(pictureQuad);
1096        subgraph = picture;
1097    }
1098
1099    // attach any meterial animation.
1100    if (positionData.requiresMaterialAnimation())
1101        subgraph = attachMaterialAnimation(subgraph,positionData);
1102
1103
1104    if (isImageTranslucent)
1105    {
1106        SetToTransparentBin sttb;
1107        subgraph->accept(sttb);
1108        pictureStateSet->setMode(GL_BLEND, osg::StateAttribute::ON);
1109    }
1110
1111    osg::ImageSequence* imageSequence = dynamic_cast<osg::ImageSequence*>(image.get());
1112    if (imageSequence && imageData.imageSequenceInteractionMode==ImageData::USE_MOUSE_X_POSITION)
1113    {
1114        subgraph->setUpdateCallback(new osgPresentation::ImageSequenceUpdateCallback(imageSequence, _propertyManager.get(), "mouse.x_normalized"));
1115    }
1116       
1117    // attached any rotation
1118    if (positionData.rotation[0]!=0.0)
1119    {
1120        osg::MatrixTransform* animation_transform = new osg::MatrixTransform;
1121        animation_transform->setDataVariance(osg::Object::DYNAMIC);
1122        animation_transform->setUpdateCallback(
1123            new osgUtil::TransformCallback(subgraph->getBound().center(),
1124                                           osg::Vec3(positionData.rotation[1],positionData.rotation[2],positionData.rotation[3]),
1125                                           osg::DegreesToRadians(positionData.rotation[0])));
1126
1127        animation_transform->addChild(subgraph);
1128
1129        subgraph = animation_transform;
1130    }
1131
1132    // attached any animation
1133    osg::AnimationPathCallback* animation = getAnimationPathCallback(positionData);
1134    if (animation)
1135    {
1136        OSG_INFO<<"Have animation path for image"<<std::endl;
1137
1138        osg::BoundingSphere::vec_type pivot = positionData.absolute_path ?
1139                osg::BoundingSphere::vec_type(0.0f,0.0f,0.0f) :
1140                subgraph->getBound().center();
1141
1142        osg::PositionAttitudeTransform* animation_transform = new osg::PositionAttitudeTransform;
1143        animation_transform->setDataVariance(osg::Object::DYNAMIC);
1144        animation_transform->setPivotPoint(pivot);
1145        animation->setPivotPoint(pivot);
1146
1147        animation_transform->setUpdateCallback(animation);
1148
1149        animation_transform->setReferenceFrame(positionData.absolute_path ?
1150                                                    osg::Transform::ABSOLUTE_RF:
1151                                                    osg::Transform::RELATIVE_RF);
1152
1153        animation_transform->addChild(subgraph);
1154
1155        subgraph = animation_transform;
1156    }
1157
1158    if (positionData.hud)
1159    {
1160        HUDTransform* hudTransform = new HUDTransform(_hudSettings.get());
1161        hudTransform->addChild(subgraph);
1162
1163        subgraph = hudTransform;
1164    }
1165
1166    addToCurrentLayer(subgraph);
1167}
1168
1169void SlideShowConstructor::addStereoImagePair(const std::string& filenameLeft, const ImageData& imageDataLeft, const std::string& filenameRight, const ImageData& imageDataRight,const PositionData& positionData)
1170{
1171    osg::ref_ptr<osg::Image> imageLeft = readImage(filenameLeft, imageDataLeft);
1172    osg::ref_ptr<osg::Image> imageRight = (filenameRight==filenameLeft) ? imageLeft.get() : readImage(filenameRight, imageDataRight);
1173   
1174    if (!imageLeft && !imageRight) return;
1175
1176    bool isImageTranslucent = false;
1177
1178    osg::ImageStream* imageStreamLeft = dynamic_cast<osg::ImageStream*>(imageLeft.get());
1179    if (imageStreamLeft)
1180    {
1181        imageStreamLeft->setLoopingMode(imageDataLeft.loopingMode);
1182        isImageTranslucent = imageStreamLeft->getPixelFormat()==GL_RGBA ||
1183                             imageStreamLeft->getPixelFormat()==GL_BGRA;
1184    }
1185    else
1186    {
1187        isImageTranslucent = imageLeft->isImageTranslucent();
1188    }
1189
1190    osg::ImageStream* imageStreamRight = dynamic_cast<osg::ImageStream*>(imageRight.get());
1191    if (imageStreamRight)
1192    {
1193        imageStreamRight->setLoopingMode(imageDataRight.loopingMode);
1194        if (!isImageTranslucent)
1195        {
1196            isImageTranslucent = imageStreamRight->getPixelFormat()==GL_RGBA ||
1197                                imageStreamRight->getPixelFormat()==GL_BGRA;
1198        }
1199    }
1200    else if (!isImageTranslucent)
1201    {
1202        isImageTranslucent = imageRight->isImageTranslucent();
1203    }
1204
1205
1206    float s = imageLeft->s();
1207    float t = imageLeft->t();
1208
1209
1210    float sx = imageDataLeft.region_in_pixel_coords ? 1.0f : s;
1211    float sy = imageDataLeft.region_in_pixel_coords ? 1.0f : t;
1212
1213    float x1 = imageDataLeft.region[0]*sx;
1214    float y1 = imageDataLeft.region[1]*sy;
1215    float x2 = imageDataLeft.region[2]*sx;
1216    float y2 = imageDataLeft.region[3]*sy;
1217
1218    float aspectRatio = (y2-y1)/(x2-x1);
1219
1220    float image_width = _slideWidth*positionData.scale.x();
1221    float image_height = image_width*aspectRatio*positionData.scale.y()/positionData.scale.x();
1222
1223    float offset = 0.0f;
1224
1225    bool usedTextureRectangle = false;
1226
1227    osg::Vec3 pos = computePositionInModelCoords(positionData);
1228    osg::Vec3 image_local_pos = osg::Vec3(-image_width*0.5f+offset,-offset,-image_height*0.5f-offset);
1229    osg::Vec3 image_pos = positionData.autoRotate ? image_local_pos : (pos+image_local_pos);
1230
1231
1232    osg::Node* pictureLeft = 0;
1233    {
1234        osg::Geometry* pictureLeftQuad = createTexturedQuadGeometry(image_pos, positionData.rotate, image_width,image_height,imageLeft.get(),usedTextureRectangle);
1235        osg::StateSet* pictureLeftStateSet = pictureLeftQuad->getOrCreateStateSet();
1236
1237        if (isImageTranslucent)
1238        {
1239            pictureLeftStateSet->setMode(GL_BLEND, osg::StateAttribute::ON);
1240        }
1241
1242        attachTexMat(pictureLeftStateSet, imageDataLeft, s, t, usedTextureRectangle);
1243
1244        if (positionData.autoRotate)
1245        {
1246            osg::Billboard* billboard = new osg::Billboard;
1247            billboard->setMode(osg::Billboard::POINT_ROT_EYE);
1248            billboard->setNormal(osg::Vec3(0.0f,-1.0f,0.0f));
1249            billboard->setAxis(osg::Vec3(0.0f,0.0f,1.0f));
1250            billboard->addDrawable(pictureLeftQuad,pos);
1251            pictureLeft = billboard;
1252        }
1253        else
1254        {
1255            osg::Geode* geode = new osg::Geode;
1256            geode->addDrawable(pictureLeftQuad);
1257            pictureLeft = geode;
1258        }
1259
1260        pictureLeft->setNodeMask(_leftEyeMask);
1261    }
1262
1263    osg::Node* pictureRight = 0;
1264    {
1265        osg::Geometry* pictureRightQuad = createTexturedQuadGeometry(image_pos, positionData.rotate, image_width,image_height,imageRight.get(),usedTextureRectangle);
1266        osg::StateSet* pictureRightStateSet = pictureRightQuad->getOrCreateStateSet();
1267
1268        if (isImageTranslucent)
1269        {
1270            pictureRightStateSet->setMode(GL_BLEND, osg::StateAttribute::ON);
1271        }
1272
1273        attachTexMat(pictureRightStateSet, imageDataRight, s, t, usedTextureRectangle);
1274
1275        if (positionData.autoRotate)
1276        {
1277            osg::Billboard* billboard = new osg::Billboard;
1278            billboard->setMode(osg::Billboard::POINT_ROT_EYE);
1279            billboard->setNormal(osg::Vec3(0.0f,-1.0f,0.0f));
1280            billboard->setAxis(osg::Vec3(0.0f,0.0f,1.0f));
1281            billboard->addDrawable(pictureRightQuad,pos);
1282            pictureRight = billboard;
1283        }
1284        else
1285        {
1286            osg::Geode* geode = new osg::Geode;
1287            geode->addDrawable(pictureRightQuad);
1288            pictureRight = geode;
1289        }
1290
1291        pictureRight->setNodeMask(_rightEyeMask);
1292    }
1293
1294    osg::Group* subgraph = new osg::Group;
1295    subgraph->addChild(pictureLeft);
1296    subgraph->addChild(pictureRight);
1297
1298    osg::ImageSequence* imageSequence = dynamic_cast<osg::ImageSequence*>(imageLeft.get());
1299    if (imageSequence && imageDataLeft.imageSequenceInteractionMode==ImageData::USE_MOUSE_X_POSITION)
1300    {
1301        subgraph->setUpdateCallback(new osgPresentation::ImageSequenceUpdateCallback(imageSequence, _propertyManager.get(), "mouse.x_normalized"));
1302    }
1303
1304    // attach any meterial animation.
1305    if (positionData.requiresMaterialAnimation())
1306        subgraph = attachMaterialAnimation(subgraph,positionData)->asGroup();
1307
1308    if (isImageTranslucent)
1309    {
1310        SetToTransparentBin sttb;
1311        subgraph->accept(sttb);
1312    }
1313
1314    // attached any rotation
1315    if (positionData.rotation[0]!=0.0)
1316    {
1317        osg::MatrixTransform* animation_transform = new osg::MatrixTransform;
1318        animation_transform->setDataVariance(osg::Object::DYNAMIC);
1319        animation_transform->setUpdateCallback(
1320            new osgUtil::TransformCallback(subgraph->getBound().center(),
1321                                           osg::Vec3(positionData.rotation[1],positionData.rotation[2],positionData.rotation[3]),
1322                                           osg::DegreesToRadians(positionData.rotation[0])));
1323
1324        animation_transform->addChild(subgraph);
1325
1326        subgraph = animation_transform;
1327    }
1328
1329    // attached any animation
1330    osg::AnimationPathCallback* animation = getAnimationPathCallback(positionData);
1331    if (animation)
1332    {
1333        OSG_INFO<<"Have animation path for image"<<std::endl;
1334
1335        osg::BoundingSphere::vec_type pivot = positionData.absolute_path ?
1336                osg::BoundingSphere::vec_type(0.0f,0.0f,0.0f) :
1337                subgraph->getBound().center();
1338
1339        osg::PositionAttitudeTransform* animation_transform = new osg::PositionAttitudeTransform;
1340        animation_transform->setDataVariance(osg::Object::DYNAMIC);
1341        animation_transform->setPivotPoint(pivot);
1342        animation->setPivotPoint(pivot);
1343
1344        animation_transform->setUpdateCallback(animation);
1345        animation_transform->setReferenceFrame(positionData.absolute_path ?
1346                                                    osg::Transform::ABSOLUTE_RF:
1347                                                    osg::Transform::RELATIVE_RF);
1348
1349        animation_transform->addChild(subgraph);
1350
1351        subgraph = animation_transform;
1352    }
1353
1354    if (positionData.hud)
1355    {
1356        HUDTransform* hudTransform = new HUDTransform(_hudSettings.get());
1357        hudTransform->addChild(subgraph);
1358
1359        subgraph = hudTransform;
1360    }
1361
1362    addToCurrentLayer(subgraph);
1363}
1364
1365void SlideShowConstructor::addGraph(const std::string& contents, const PositionData& positionData, const ImageData& imageData)
1366{
1367    static int s_count=0;
1368
1369    if (contents.empty()) return;
1370
1371    std::string tmpDirectory("/tmp/");
1372
1373    std::string filename = contents;
1374    std::string ext = osgDB::getFileExtension(contents);
1375    if (ext.empty())
1376    {
1377        std::stringstream dotFileNameStream;
1378        dotFileNameStream << tmpDirectory<<"graph_"<<s_count<<std::string(".dot");
1379        filename = dotFileNameStream.str();
1380
1381        // write out the string to the temporary file.
1382        std::ofstream fout(filename.c_str());
1383        fout<<contents.c_str();
1384    }
1385
1386    std::stringstream svgFileNameStream;
1387    svgFileNameStream << tmpDirectory<<osgDB::getStrippedName(filename)<<s_count<<std::string(".svg");
1388    std::string tmpSvgFileName(svgFileNameStream.str());
1389    std::string dotFileName = filename;
1390
1391    if (osgDB::getFileExtension(filename)=="dot")
1392    {
1393        dotFileName = filename;
1394    }
1395    else
1396    {
1397        osg::ref_ptr<osg::Node> model = osgDB::readNodeFile(filename, _options.get());
1398        if (!model) return;
1399
1400        dotFileName = tmpDirectory+osgDB::getStrippedName(filename)+std::string(".dot");
1401
1402        osg::ref_ptr<osgDB::Options> opts = _options.valid() ? _options->cloneOptions() : (new osgDB::Options);
1403        if (!imageData.options.empty())
1404        {
1405            opts->setOptionString(imageData.options);
1406        }
1407        opts->setObjectCacheHint(osgDB::Options::CACHE_NONE);
1408
1409        osgDB::writeNodeFile(*model, dotFileName, opts.get());
1410    }
1411
1412    std::stringstream command;
1413    command<<"dot -Tsvg "<<dotFileName<<" -o "<<tmpSvgFileName;
1414    int result = system(command.str().c_str());
1415    if (result==0)
1416    {
1417        osg::ref_ptr<osgDB::Options> previousOptions = _options;
1418
1419        // switch off cache so we make sure that we re-read the generated svg each time.
1420        _options = _options.valid() ? _options->cloneOptions() : (new osgDB::Options);
1421        _options->setObjectCacheHint(osgDB::Options::CACHE_NONE);
1422
1423        addImage(tmpSvgFileName, positionData, imageData);
1424
1425        _options = previousOptions;
1426
1427        ++s_count;
1428    }
1429    else OSG_NOTICE<<"Error: SlideShowConstructor::addGraph() system("<<command.str()<<") failed with return "<<result<<std::endl;
1430}
1431
1432
1433void SlideShowConstructor::addVNC(const std::string& hostname, const PositionData& positionData, const ImageData& imageData, const std::string& password)
1434{
1435    if (!password.empty())
1436    {
1437        OSG_NOTICE<<"Setting password"<<std::endl;
1438        if (!osgDB::Registry::instance()->getAuthenticationMap()) osgDB::Registry::instance()->setAuthenticationMap(new osgDB::AuthenticationMap);
1439        osgDB::Registry::instance()->getAuthenticationMap()->addAuthenticationDetails(hostname, new osgDB::AuthenticationDetails("", password));
1440    }
1441
1442    addInteractiveImage(hostname+".vnc", positionData, imageData);
1443}
1444
1445void SlideShowConstructor::addBrowser(const std::string& url, const PositionData& positionData, const ImageData& imageData)
1446{
1447    addInteractiveImage(url+".gecko", positionData, imageData);
1448}
1449
1450void SlideShowConstructor::addPDF(const std::string& filename, const PositionData& positionData, const ImageData& imageData)
1451{
1452    addInteractiveImage(filename, positionData, imageData);
1453}
1454
1455class SetPageCallback: public LayerCallback
1456{
1457public:
1458    SetPageCallback(osgWidget::PdfImage* pdfImage, int pageNum):
1459        _pdfImage(pdfImage),
1460        _pageNum(pageNum)
1461    {
1462    }
1463
1464    virtual void operator() (osg::Node*) const
1465    {
1466        OSG_INFO<<"PDF Page to be updated "<<_pageNum<<std::endl;
1467
1468        if (_pdfImage.valid() && _pdfImage->getPageNum()!=_pageNum)
1469        {
1470            _pdfImage->page(_pageNum);
1471        }
1472    }
1473
1474    osg::observer_ptr<osgWidget::PdfImage> _pdfImage;
1475    int _pageNum;
1476};
1477
1478
1479osg::Image* SlideShowConstructor::addInteractiveImage(const std::string& filename, const PositionData& positionData, const ImageData& imageData)
1480{
1481    osg::ref_ptr<osgDB::Options> options = _options;
1482    if (!imageData.options.empty())
1483    {
1484        options = _options->cloneOptions();
1485        options->setOptionString(imageData.options);
1486    }
1487
1488    osg::Image* image = osgDB::readImageFile(filename, options.get());
1489
1490    OSG_INFO<<"addInteractiveImage("<<filename<<") "<<image<<std::endl;
1491
1492
1493    if (!image) return 0;
1494
1495    float s = image->s();
1496    float t = image->t();
1497
1498    float sx = imageData.region_in_pixel_coords ? 1.0f : s;
1499    float sy = imageData.region_in_pixel_coords ? 1.0f : t;
1500
1501    float x1 = imageData.region[0]*sx;
1502    float y1 = imageData.region[1]*sy;
1503    float x2 = imageData.region[2]*sx;
1504    float y2 = imageData.region[3]*sy;
1505
1506    float aspectRatio = (y2-y1)/(x2-x1);
1507
1508    float image_width = _slideWidth*positionData.scale.x();
1509    float image_height = image_width*aspectRatio*positionData.scale.y()/positionData.scale.x();
1510    float offset = 0.0f;
1511
1512    osg::Vec3 pos = computePositionInModelCoords(positionData);
1513    osg::Vec3 image_local_pos = osg::Vec3(-image_width*0.5f+offset,-offset,-image_height*0.5f-offset);
1514    osg::Vec3 image_pos = positionData.autoRotate ? image_local_pos : (pos+image_local_pos);
1515
1516    bool usedTextureRectangle = false;
1517    osg::Geometry* pictureQuad = createTexturedQuadGeometry(image_pos, positionData.rotate, image_width, image_height, image, usedTextureRectangle);
1518
1519    osg::ref_ptr<osgViewer::InteractiveImageHandler> handler = new osgViewer::InteractiveImageHandler(image);
1520    pictureQuad->setEventCallback(handler.get());
1521    pictureQuad->setCullCallback(handler.get());
1522
1523    osg::StateSet* pictureStateSet = pictureQuad->getOrCreateStateSet();
1524
1525    attachTexMat(pictureStateSet, imageData, s, t, usedTextureRectangle);
1526
1527    pictureStateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
1528
1529    osg::Node* subgraph = 0;
1530
1531    if (positionData.autoRotate)
1532    {
1533        osg::Billboard* picture = new osg::Billboard;
1534        picture->setMode(osg::Billboard::POINT_ROT_EYE);
1535        picture->setNormal(osg::Vec3(0.0f,-1.0f,0.0f));
1536        picture->setAxis(osg::Vec3(0.0f,0.0f,1.0f));
1537        picture->addDrawable(pictureQuad,pos);
1538        subgraph = picture;
1539    }
1540    else
1541    {
1542        osg::Geode* picture = new osg::Geode;
1543        picture->addDrawable(pictureQuad);
1544        subgraph = picture;
1545    }
1546
1547    // attach any meterial animation.
1548    if (positionData.requiresMaterialAnimation())
1549        subgraph = attachMaterialAnimation(subgraph,positionData);
1550
1551
1552    // attached any rotation
1553    if (positionData.rotation[0]!=0.0)
1554    {
1555        osg::MatrixTransform* animation_transform = new osg::MatrixTransform;
1556        animation_transform->setDataVariance(osg::Object::DYNAMIC);
1557        animation_transform->setUpdateCallback(
1558            new osgUtil::TransformCallback(subgraph->getBound().center(),
1559                                           osg::Vec3(positionData.rotation[1],positionData.rotation[2],positionData.rotation[3]),
1560                                           osg::DegreesToRadians(positionData.rotation[0])));
1561
1562        animation_transform->addChild(subgraph);
1563
1564        subgraph = animation_transform;
1565    }
1566
1567
1568    // attached any animation
1569    osg::AnimationPathCallback* animation = getAnimationPathCallback(positionData);
1570    if (animation)
1571    {
1572        OSG_INFO<<"Have animation path for image"<<std::endl;
1573
1574        osg::BoundingSphere::vec_type pivot = positionData.absolute_path ?
1575                osg::BoundingSphere::vec_type(0.0f,0.0f,0.0f) :
1576                subgraph->getBound().center();
1577
1578        osg::PositionAttitudeTransform* animation_transform = new osg::PositionAttitudeTransform;
1579        animation_transform->setDataVariance(osg::Object::DYNAMIC);
1580        animation_transform->setPivotPoint(pivot);
1581        animation->setPivotPoint(pivot);
1582
1583        animation_transform->setUpdateCallback(animation);
1584
1585        animation_transform->setReferenceFrame(positionData.absolute_path ?
1586                                                    osg::Transform::ABSOLUTE_RF:
1587                                                    osg::Transform::RELATIVE_RF);
1588
1589        animation_transform->addChild(subgraph);
1590
1591        subgraph = animation_transform;
1592    }
1593
1594    if (positionData.hud)
1595    {
1596        HUDTransform* hudTransform = new HUDTransform(_hudSettings.get());
1597        hudTransform->addChild(subgraph);
1598
1599        subgraph = hudTransform;
1600    }
1601
1602    addToCurrentLayer(subgraph);
1603
1604    osgWidget::PdfImage* pdfImage = dynamic_cast<osgWidget::PdfImage*>(image);
1605    if (pdfImage && imageData.page>=0)
1606    {
1607        getOrCreateLayerAttributes(_currentLayer.get())->addEnterCallback(new SetPageCallback(pdfImage, imageData.page));
1608
1609        OSG_INFO<<"Setting pdf page num "<<imageData.page<<std::endl;
1610        pdfImage->setBackgroundColor(imageData.backgroundColor);
1611        pdfImage->page(imageData.page);
1612
1613        if (imageData.backgroundColor.a()<1.0f)
1614        {
1615            SetToTransparentBin sttb;
1616            subgraph->accept(sttb);
1617        }
1618
1619
1620    }
1621
1622
1623    return image;
1624}
1625
1626std::string SlideShowConstructor::findFileAndRecordPath(const std::string& filename)
1627{
1628    std::string foundFile = osgDB::findDataFile(filename, _options.get());
1629    if (foundFile.empty()) return foundFile;
1630
1631    OSG_INFO<<"foundFile "<<foundFile<<std::endl;
1632
1633    std::string path = osgDB::getFilePath(foundFile);
1634    if (!path.empty() && _filePathData.valid())
1635    {
1636        osgDB::FilePathList::iterator itr = std::find(_filePathData->filePathList.begin(),_filePathData->filePathList.end(),path);
1637        if (itr==_filePathData->filePathList.end())
1638        {
1639            OSG_INFO<<"New path to record "<<path<<std::endl;
1640            _filePathData->filePathList.push_front(path);
1641        }
1642    }
1643
1644    return foundFile;
1645
1646}
1647
1648void SlideShowConstructor::addModel(const std::string& filename, const PositionData& positionData, const ModelData& modelData)
1649{
1650    OSG_INFO<<"SlideShowConstructor::addModel("<<filename<<")"<<std::endl;
1651
1652    osg::ref_ptr<osgDB::Options> options = _options;
1653    if (!modelData.options.empty())
1654    {
1655        options = _options->cloneOptions();
1656        options->setOptionString(modelData.options);
1657    }
1658
1659    osg::Node* subgraph = 0;
1660
1661    if (filename=="sphere")
1662    {
1663        osg::Geode* geode = new osg::Geode;
1664        geode->addDrawable(new osg::ShapeDrawable(new osg::Sphere));
1665
1666        subgraph = geode;
1667    }
1668    else if (filename=="box")
1669    {
1670        osg::Geode* geode = new osg::Geode;
1671        geode->addDrawable(new osg::ShapeDrawable(new osg::Box));
1672
1673        subgraph = geode;
1674    }
1675    else
1676    {
1677        subgraph = osgDB::readNodeFile(filename, options.get());
1678        if (subgraph) recordOptionsFilePath(options.get());
1679    }
1680
1681    if (subgraph)
1682    {
1683        addModel(subgraph, positionData, modelData);
1684    }
1685
1686    OSG_INFO<<"end of SlideShowConstructor::addModel("<<filename<<")"<<std::endl<<std::endl;
1687
1688}
1689
1690void SlideShowConstructor::addModel(osg::Node* subgraph, const PositionData& positionData, const ModelData& modelData)
1691{
1692    osg::Object::DataVariance defaultMatrixDataVariance = osg::Object::DYNAMIC; // STATIC
1693
1694    if (!modelData.effect.empty())
1695    {
1696        if (modelData.effect=="SpecularHighlights" || modelData.effect=="glossy")
1697        {
1698            osgFX::SpecularHighlights* specularHighlights = new osgFX::SpecularHighlights;
1699            specularHighlights->setTextureUnit(1);
1700            specularHighlights->addChild(subgraph);
1701            subgraph = specularHighlights;
1702        }
1703    }
1704
1705
1706    if (positionData.frame==SLIDE)
1707    {
1708        osg::Vec3 pos = convertSlideToModel(positionData.position);
1709
1710        const osg::BoundingSphere& bs = subgraph->getBound();
1711        float slide_scale = _slideHeight*(1.0f-positionData.position.z())*0.7f/bs.radius();
1712
1713        osg::MatrixTransform* transform = new osg::MatrixTransform;
1714        transform->setDataVariance(defaultMatrixDataVariance);
1715        transform->setMatrix(osg::Matrix::translate(-bs.center())*
1716                             osg::Matrix::scale(positionData.scale.x()*slide_scale, positionData.scale.y()*slide_scale ,positionData.scale.z()*slide_scale)*
1717                             osg::Matrix::rotate(osg::DegreesToRadians(positionData.rotate[0]),positionData.rotate[1],positionData.rotate[2],positionData.rotate[3])*
1718                             osg::Matrix::translate(pos));
1719
1720
1721        transform->setStateSet(createTransformStateSet());
1722        transform->addChild(subgraph);
1723
1724        subgraph = transform;
1725
1726    }
1727    else
1728    {
1729        osg::Matrix matrix(osg::Matrix::scale(1.0f/positionData.scale.x(),1.0f/positionData.scale.y(),1.0f/positionData.scale.z())*
1730                           osg::Matrix::rotate(osg::DegreesToRadians(positionData.rotate[0]),positionData.rotate[1],positionData.rotate[2],positionData.rotate[3])*
1731                           osg::Matrix::translate(positionData.position));
1732
1733        osg::MatrixTransform* transform = new osg::MatrixTransform;
1734        transform->setDataVariance(defaultMatrixDataVariance);
1735        transform->setMatrix(osg::Matrix::inverse(matrix));
1736
1737        OSG_INFO<<"Position Matrix "<<transform->getMatrix()<<std::endl;
1738
1739        transform->addChild(subgraph);
1740
1741        subgraph = transform;
1742    }
1743
1744    float referenceSizeRatio = 0.707;
1745    float referenceSize = subgraph->getBound().radius() * referenceSizeRatio;
1746
1747
1748    // attach any meterial animation.
1749    if (positionData.requiresMaterialAnimation())
1750        subgraph = attachMaterialAnimation(subgraph,positionData);
1751
1752    // attached any rotation
1753    if (positionData.rotation[0]!=0.0)
1754    {
1755        osg::MatrixTransform* animation_transform = new osg::MatrixTransform;
1756        animation_transform->setDataVariance(osg::Object::DYNAMIC);
1757        animation_transform->setUpdateCallback(
1758            new osgUtil::TransformCallback(subgraph->getBound().center(),
1759                                           osg::Vec3(positionData.rotation[1],positionData.rotation[2],positionData.rotation[3]),
1760                                           osg::DegreesToRadians(positionData.rotation[0])));
1761
1762        animation_transform->addChild(subgraph);
1763
1764        OSG_INFO<<"Rotation Matrix "<<animation_transform->getMatrix()<<std::endl;
1765
1766        subgraph = animation_transform;
1767    }
1768
1769
1770    // attached any animation
1771    osg::AnimationPathCallback* animation = getAnimationPathCallback(positionData);
1772    if (animation)
1773    {
1774        OSG_INFO<<"Have animation path for model"<<std::endl;
1775
1776        osg::BoundingSphere::vec_type pivot = positionData.absolute_path ?
1777            osg::BoundingSphere::vec_type(0.0f,0.0f,0.0f) :
1778            subgraph->getBound().center();
1779
1780        osg::AnimationPath* path = animation->getAnimationPath();
1781        if (positionData.animation_name=="wheel" && (path->getTimeControlPointMap()).size()>=2)
1782        {
1783            OSG_INFO<<"****  Need to handle special wheel animation"<<std::endl;
1784
1785            osg::AnimationPath::TimeControlPointMap& controlPoints = path->getTimeControlPointMap();
1786
1787            osg::AnimationPath::TimeControlPointMap::iterator curr_itr = controlPoints.begin();
1788            osg::AnimationPath::TimeControlPointMap::iterator prev_itr=curr_itr;
1789            ++curr_itr;
1790
1791            osg::AnimationPath::ControlPoint* prev_cp = &(prev_itr->second);
1792            osg::AnimationPath::ControlPoint* curr_cp = &(curr_itr->second);
1793
1794            float totalLength = 0;
1795            float rotation_y_axis = 0;
1796            osg::Vec3 delta_position = curr_cp->getPosition() - prev_cp->getPosition();
1797            float rotation_z_axis = atan2f(delta_position.y(),delta_position.x());
1798
1799            osg::Quat quat_y_axis,quat_z_axis,quat_combined;
1800
1801            quat_y_axis.makeRotate(rotation_y_axis,0.0f,1.0f,0.0f);
1802            quat_z_axis.makeRotate(rotation_z_axis,0.0f,0.0f,1.0f);
1803            quat_combined = quat_y_axis*quat_z_axis;
1804
1805            // set first rotation.
1806            prev_cp->setRotation(quat_combined);
1807
1808            for(;
1809                curr_itr!=controlPoints.end();
1810                ++curr_itr)
1811            {
1812                prev_cp = &(prev_itr->second);
1813                curr_cp = &(curr_itr->second);
1814
1815                delta_position = curr_cp->getPosition() - prev_cp->getPosition();
1816
1817                totalLength += delta_position.length();
1818
1819                // rolling - rotation about the y axis.
1820                rotation_y_axis = totalLength/referenceSize;
1821
1822                // direction - rotation about the z axis.
1823                rotation_z_axis = atan2f(delta_position.y(),delta_position.x());
1824
1825                OSG_INFO<<" rotation_y_axis="<<rotation_y_axis<<" rotation_z_axis="<<rotation_z_axis<<std::endl;
1826
1827                quat_y_axis.makeRotate(rotation_y_axis,0.0f,1.0f,0.0f);
1828                quat_z_axis.makeRotate(rotation_z_axis,0.0f,0.0f,1.0f);
1829                quat_combined = quat_y_axis*quat_z_axis;
1830
1831                curr_cp->setRotation(quat_combined);
1832
1833                prev_itr = curr_itr;
1834
1835            }
1836
1837        }
1838
1839
1840        osg::PositionAttitudeTransform* animation_transform = new osg::PositionAttitudeTransform;
1841        animation_transform->setDataVariance(osg::Object::DYNAMIC);
1842        animation_transform->setPivotPoint(pivot);
1843        animation->setPivotPoint(pivot);
1844        animation_transform->setUpdateCallback(animation);
1845
1846        animation_transform->setReferenceFrame(positionData.absolute_path ?
1847                                                    osg::Transform::ABSOLUTE_RF:
1848                                                    osg::Transform::RELATIVE_RF);
1849
1850        animation_transform->addChild(subgraph);
1851
1852        subgraph = animation_transform;
1853    }
1854
1855    findImageStreamsAndAddCallbacks(subgraph);
1856
1857    addToCurrentLayer(subgraph);
1858}
1859
1860class DraggerVolumeTileCallback : public osgManipulator::DraggerCallback
1861{
1862public:
1863
1864    DraggerVolumeTileCallback(osgVolume::VolumeTile* volume, osgVolume::Locator* locator):
1865        _volume(volume),
1866        _locator(locator) {}
1867
1868
1869    virtual bool receive(const osgManipulator::MotionCommand& command);
1870
1871
1872    osg::observer_ptr<osgVolume::VolumeTile>    _volume;
1873    osg::ref_ptr<osgVolume::Locator>            _locator;
1874
1875    osg::Matrix _startMotionMatrix;
1876
1877    osg::Matrix _localToWorld;
1878    osg::Matrix _worldToLocal;
1879
1880};
1881
1882bool DraggerVolumeTileCallback::receive(const osgManipulator::MotionCommand& command)
1883{
1884    if (!_locator) return false;
1885
1886    switch (command.getStage())
1887    {
1888        case osgManipulator::MotionCommand::START:
1889        {
1890            // Save the current matrix
1891            _startMotionMatrix = _locator->getTransform();
1892
1893            // Get the LocalToWorld and WorldToLocal matrix for this node.
1894            osg::NodePath nodePathToRoot;
1895            osgManipulator::computeNodePathToRoot(*_volume,nodePathToRoot);
1896            _localToWorld = _startMotionMatrix * osg::computeLocalToWorld(nodePathToRoot);
1897            _worldToLocal = osg::Matrix::inverse(_localToWorld);
1898
1899            return true;
1900        }
1901        case osgManipulator::MotionCommand::MOVE:
1902        {
1903            // Transform the command's motion matrix into local motion matrix.
1904            osg::Matrix localMotionMatrix = _localToWorld * command.getWorldToLocal()
1905                                            * command.getMotionMatrix()
1906                                            * command.getLocalToWorld() * _worldToLocal;
1907
1908            // Transform by the localMotionMatrix
1909            _locator->setTransform(localMotionMatrix * _startMotionMatrix);
1910
1911            // OSG_NOTICE<<"New locator matrix "<<_locator->getTransform()<<std::endl;
1912
1913            return true;
1914        }
1915        case osgManipulator::MotionCommand::FINISH:
1916        {
1917            return true;
1918        }
1919        case osgManipulator::MotionCommand::NONE:
1920        default:
1921            return false;
1922    }
1923}
1924
1925void SlideShowConstructor::addVolume(const std::string& filename, const PositionData& in_positionData, const VolumeData& volumeData)
1926{
1927    // osg::Object::DataVariance defaultMatrixDataVariance = osg::Object::DYNAMIC; // STATIC
1928
1929    PositionData positionData(in_positionData);
1930
1931    osg::ref_ptr<osgDB::Options> options = _options;
1932    if (!volumeData.options.empty())
1933    {
1934        options = _options->cloneOptions();
1935        options->setOptionString(volumeData.options);
1936    }
1937
1938    std::string foundFile = filename;
1939    osg::ref_ptr<osg::Image> image;
1940    osg::ref_ptr<osgVolume::Volume> volume;
1941    osg::ref_ptr<osgVolume::VolumeTile> tile;
1942    osg::ref_ptr<osgVolume::ImageLayer> layer;
1943
1944    // check for wild cards
1945    if (filename.find('*')!=std::string::npos)
1946    {
1947        osgDB::DirectoryContents filenames = osgDB::expandWildcardsInFilename(filename);
1948        if (filenames.empty()) return;
1949
1950        // make sure images are in alphabetical order.
1951        std::sort(filenames.begin(), filenames.end(), osgDB::FileNameComparator());
1952
1953        typedef std::vector< osg::ref_ptr<osg::Image> > Images;
1954        Images images;
1955        for(osgDB::DirectoryContents::iterator itr = filenames.begin();
1956            itr != filenames.end();
1957            ++itr)
1958        {
1959            osg::ref_ptr<osg::Image> loadedImage = osgDB::readImageFile(*itr, options.get());
1960            if (loadedImage.valid())
1961            {
1962                images.push_back(loadedImage.get());
1963            }
1964        }
1965
1966        image = osg::createImage3DWithAlpha(images);
1967    }
1968    else
1969    {
1970        osgDB::FileType fileType = osgDB::fileType(foundFile);
1971        if (fileType == osgDB::FILE_NOT_FOUND)
1972        {
1973            foundFile = findFileAndRecordPath(foundFile);
1974            fileType = osgDB::fileType(foundFile);
1975        }
1976
1977        if (fileType == osgDB::DIRECTORY)
1978        {
1979            image = osgDB::readImageFile(foundFile+".dicom", options.get());
1980        }
1981        else if (fileType == osgDB::REGULAR_FILE)
1982        {
1983            std::string ext = osgDB::getFileExtension(foundFile);
1984            if (ext=="osg" || ext=="ive" || ext=="osgx" || ext=="osgb" || ext=="osgt")
1985            {
1986                osg::ref_ptr<osg::Object> obj = osgDB::readObjectFile(foundFile);
1987                image = dynamic_cast<osg::Image*>(obj.get());
1988                volume = dynamic_cast<osgVolume::Volume*>(obj.get());
1989            }
1990            else
1991            {
1992                image = osgDB::readImageFile( foundFile, options.get() );
1993            }
1994        }
1995        else
1996        {
1997            // not found image, so fallback to plugins/callbacks to find the model.
1998            image = osgDB::readImageFile( filename, options.get() );
1999            if (image) recordOptionsFilePath(options.get() );
2000        }
2001    }
2002
2003    if (!image && !volume) return;
2004
2005    if (positionData.scale.x()<0.0)
2006    {
2007        image->flipHorizontal();
2008        positionData.scale.x() = fabs(positionData.scale.x());
2009
2010        OSG_INFO<<"addVolume(..) image->flipHorizontal();"<<std::endl;
2011    }
2012
2013    if (positionData.scale.y()<0.0)
2014    {
2015        image->flipVertical();
2016        positionData.scale.y() = fabs(positionData.scale.y());
2017
2018        OSG_INFO<<"addVolume(..) image->flipVertical();"<<std::endl;
2019    }
2020
2021    if (positionData.scale.z()<0.0)
2022    {
2023        image->flipDepth();
2024        positionData.scale.z() = fabs(positionData.scale.z());
2025
2026        OSG_INFO<<"addVolume(..) image->flipDepth();"<<std::endl;
2027    }
2028
2029    if (volume.valid())
2030    {
2031        if (!tile)
2032        {
2033            if (volume->getNumChildren()>0)
2034            {
2035                tile = dynamic_cast<osgVolume::VolumeTile*>(volume->getChild(0));
2036            }
2037        }
2038    }
2039    else
2040    {
2041        volume = new osgVolume::Volume;
2042    }
2043
2044    if (tile.valid())
2045    {
2046        layer = dynamic_cast<osgVolume::ImageLayer*>(tile->getLayer());
2047        image = layer.valid() ? layer->getImage() : 0;
2048    }
2049    else
2050    {
2051        if (!image) return;
2052
2053        tile = new osgVolume::VolumeTile;
2054        volume->addChild(tile.get());
2055    }
2056
2057    if (!layer)
2058    {
2059        if (!image) return;
2060
2061        osg::ref_ptr<osgVolume::ImageDetails> details = dynamic_cast<osgVolume::ImageDetails*>(image->getUserData());
2062        osg::ref_ptr<osg::RefMatrix> matrix = details ? details->getMatrix() : dynamic_cast<osg::RefMatrix*>(image->getUserData());
2063
2064        osg::ref_ptr<osgVolume::ImageLayer> layer = new osgVolume::ImageLayer(image.get());
2065        if (details)
2066        {
2067            layer->setTexelOffset(details->getTexelOffset());
2068            layer->setTexelScale(details->getTexelScale());
2069        }
2070        layer->rescaleToZeroToOneRange();
2071
2072        if (matrix.valid())
2073        {
2074            layer->setLocator(new osgVolume::Locator(*matrix));
2075            osg::Matrix tm = osg::Matrix::scale(volumeData.region[3]-volumeData.region[0], volumeData.region[4]-volumeData.region[1], volumeData.region[5]-volumeData.region[2]) *
2076                            osg::Matrix::translate(volumeData.region[0],volumeData.region[1],volumeData.region[2]);
2077            tile->setLocator(new osgVolume::Locator(tm * (*matrix)));
2078        }
2079
2080
2081        tile->setLayer(layer.get());
2082
2083        osgVolume::SwitchProperty* sp = new osgVolume::SwitchProperty;
2084        sp->setActiveProperty(0);
2085
2086        osgVolume::AlphaFuncProperty* ap = new osgVolume::AlphaFuncProperty(volumeData.cutoffValue);
2087        osgVolume::TransparencyProperty* tp = new osgVolume::TransparencyProperty(volumeData.alphaValue);
2088        osgVolume::SampleDensityProperty* sd = new osgVolume::SampleDensityProperty(volumeData.sampleDensityValue);
2089        osgVolume::SampleDensityWhenMovingProperty* sdm = (volumeData.sampleDensityWhenMovingValue > 0.0f) ? (new osgVolume::SampleDensityWhenMovingProperty(volumeData.sampleDensityWhenMovingValue)) : 0;
2090        osgVolume::TransferFunctionProperty* tfp = volumeData.transferFunction.valid() ? new osgVolume::TransferFunctionProperty(volumeData.transferFunction.get()) : 0;
2091
2092        {
2093            // Standard
2094            osgVolume::CompositeProperty* cp = new osgVolume::CompositeProperty;
2095            cp->addProperty(ap);
2096            cp->addProperty(sd);
2097            cp->addProperty(tp);
2098            if (sdm) cp->addProperty(sdm);
2099            if (tfp) cp->addProperty(tfp);
2100
2101            sp->addProperty(cp);
2102        }
2103
2104        {
2105            // Light
2106            osgVolume::CompositeProperty* cp = new osgVolume::CompositeProperty;
2107            cp->addProperty(ap);
2108            cp->addProperty(sd);
2109            cp->addProperty(tp);
2110            cp->addProperty(new osgVolume::LightingProperty);
2111            if (sdm) cp->addProperty(sdm);
2112            if (tfp) cp->addProperty(tfp);
2113
2114            sp->addProperty(cp);
2115        }
2116
2117        {
2118            // Isosurface
2119            osgVolume::CompositeProperty* cp = new osgVolume::CompositeProperty;
2120            cp->addProperty(sd);
2121            cp->addProperty(tp);
2122            cp->addProperty(new osgVolume::IsoSurfaceProperty(volumeData.cutoffValue));
2123            if (sdm) cp->addProperty(sdm);
2124            if (tfp) cp->addProperty(tfp);
2125
2126            sp->addProperty(cp);
2127        }
2128
2129        {
2130            // MaximumIntensityProjection
2131            osgVolume::CompositeProperty* cp = new osgVolume::CompositeProperty;
2132            cp->addProperty(ap);
2133            cp->addProperty(sd);
2134            cp->addProperty(tp);
2135            cp->addProperty(new osgVolume::MaximumIntensityProjectionProperty);
2136            if (sdm) cp->addProperty(sdm);
2137            if (tfp) cp->addProperty(tfp);
2138
2139            sp->addProperty(cp);
2140        }
2141
2142        switch(volumeData.shadingModel)
2143        {
2144            case(VolumeData::Standard):                     sp->setActiveProperty(0); break;
2145            case(VolumeData::Light):                        sp->setActiveProperty(1); break;
2146            case(VolumeData::Isosurface):                   sp->setActiveProperty(2); break;
2147            case(VolumeData::MaximumIntensityProjection):   sp->setActiveProperty(3); break;
2148        }
2149
2150        layer->addProperty(sp);
2151        tile->setVolumeTechnique(new osgVolume::RayTracedTechnique);
2152        tile->setEventCallback(new osgVolume::PropertyAdjustmentCallback());
2153    }
2154
2155    osg::ref_ptr<osg::Node> model = volume.get();
2156
2157    if (volumeData.useTabbedDragger || volumeData.useTrackballDragger)
2158    {
2159        osg::ref_ptr<osg::Group> group = new osg::Group;
2160
2161        osg::ref_ptr<osgManipulator::Dragger> dragger;
2162        if (volumeData.useTabbedDragger)
2163        {
2164            if (volumeData.useTrackballDragger)
2165                dragger = new osgManipulator::TabBoxTrackballDragger;
2166            else
2167                dragger = new osgManipulator::TabBoxDragger;
2168        }
2169        else
2170            dragger = new osgManipulator::TrackballDragger();
2171
2172        dragger->setupDefaultGeometry();
2173        dragger->setHandleEvents(true);
2174        dragger->setActivationModKeyMask(osgGA::GUIEventAdapter::MODKEY_SHIFT);
2175        dragger->addDraggerCallback(new DraggerVolumeTileCallback(tile.get(), tile->getLocator()));
2176        dragger->setMatrix(osg::Matrix::translate(0.5,0.5,0.5)*tile->getLocator()->getTransform());
2177
2178
2179        group->addChild(dragger.get());
2180
2181        //dragger->addChild(volume.get());
2182
2183        group->addChild(volume.get());
2184
2185        model = group.get();
2186    }
2187
2188
2189
2190    ModelData modelData;
2191    addModel(model.get(), positionData, modelData);
2192}
2193
2194bool SlideShowConstructor::attachTexMat(osg::StateSet* stateset, const ImageData& imageData, float s, float t, bool textureRectangle)
2195{
2196    float xScale = textureRectangle ? s : 1.0f;
2197    float yScale = textureRectangle ? t : 1.0f;
2198
2199    float sx = (textureRectangle ? s : 1.0f) / (imageData.region_in_pixel_coords ? s : 1.0f);
2200    float sy = (textureRectangle ? t : 1.0f) / (imageData.region_in_pixel_coords ? t : 1.0f);
2201
2202    float x1 = imageData.region[0]*sx;
2203    float y1 = imageData.region[1]*sy;
2204    float x2 = imageData.region[2]*sx;
2205    float y2 = imageData.region[3]*sy;
2206
2207    if (x1!=0.0f || y1!=0.0f || x2!=xScale || y2 != yScale ||
2208        imageData.texcoord_rotate != 0.0f)
2209    {
2210        osg::TexMat* texmat = new osg::TexMat;
2211        texmat->setMatrix(osg::Matrix::translate(-0.5f*xScale,-0.5f*yScale,0.0f)*
2212                          osg::Matrix::rotate(osg::DegreesToRadians(imageData.texcoord_rotate),0.0f,0.0f,1.0f)*
2213                          osg::Matrix::translate(0.5f*xScale,0.5f*yScale,0.0f)*
2214                          osg::Matrix::scale((x2-x1)/xScale,(y2-y1)/yScale,1.0f)*
2215                          osg::Matrix::translate(x1,
2216                                                 y1,
2217                                                 0.0f));
2218
2219        stateset->setTextureAttribute(0,texmat);
2220        return true;
2221    }
2222    return false;
2223}
2224
2225osg::Node* SlideShowConstructor::attachMaterialAnimation(osg::Node* model, const PositionData& positionData)
2226{
2227    AnimationMaterial* animationMaterial = 0;
2228
2229    if (!positionData.animation_material_filename.empty())
2230    {
2231#if 0
2232        std::string absolute_animation_file_path = osgDB::findDataFile(positionData.animation_material_filename, _options.get());
2233        if (!absolute_animation_file_path.empty())
2234        {
2235            std::ifstream animation_filestream(absolute_animation_file_path.c_str());
2236            if (!animation_filestream.eof())
2237            {
2238                animationMaterial = new AnimationMaterial;
2239                animationMaterial->read(animation_filestream);
2240            }
2241        }
2242#else
2243        osg::ref_ptr<osg::Object> object = osgDB::readObjectFile(positionData.animation_material_filename, _options.get());
2244        animationMaterial = dynamic_cast<AnimationMaterial*>(object.get());
2245#endif
2246
2247    }
2248    else if (!positionData.fade.empty())
2249    {
2250        std::istringstream iss(positionData.fade);
2251
2252        animationMaterial = new AnimationMaterial;
2253        while (!iss.fail() && !iss.eof())
2254        {
2255            float time=1.0f, alpha=1.0f;
2256            iss >> time >> alpha;
2257            if (!iss.fail())
2258            {
2259                osg::Material* material = new osg::Material;
2260                material->setAmbient(osg::Material::FRONT_AND_BACK,osg::Vec4(1.0f,1.0f,1.0f,alpha));
2261                material->setDiffuse(osg::Material::FRONT_AND_BACK,osg::Vec4(1.0f,1.0f,1.0f,alpha));
2262                animationMaterial->insert(time,material);
2263            }
2264        }
2265    }
2266
2267    if (animationMaterial)
2268    {
2269        animationMaterial->setLoopMode(positionData.animation_material_loop_mode);
2270
2271        AnimationMaterialCallback* animationMaterialCallback = new AnimationMaterialCallback(animationMaterial);
2272        animationMaterialCallback->setTimeOffset(positionData.animation_material_time_offset);
2273        animationMaterialCallback->setTimeMultiplier(positionData.animation_material_time_multiplier);
2274
2275        osg::Group* decorator = new osg::Group;
2276        decorator->addChild(model);
2277
2278        decorator->setUpdateCallback(animationMaterialCallback);
2279
2280        if (animationMaterial->requiresBlending())
2281        {
2282            SetToTransparentBin sttb;
2283            decorator->accept(sttb);
2284        }
2285
2286        return decorator;
2287    }
2288
2289    return model;
2290}
2291
2292osg::AnimationPathCallback* SlideShowConstructor::getAnimationPathCallback(const PositionData& positionData)
2293{
2294    if (!positionData.path.empty())
2295    {
2296        // need to create an Options object with cache off to prevent sharing of animation paths
2297        osg::ref_ptr<osgDB::Options> options = _options.valid() ? _options->cloneOptions() : new osgDB::Options;
2298        options->setObjectCacheHint(osgDB::Options::CACHE_NONE);
2299
2300        osg::ref_ptr<osg::Object> object = osgDB::readObjectFile(positionData.path, options.get());
2301        osg::AnimationPath* animation = dynamic_cast<osg::AnimationPath*>(object.get());
2302        if (animation)
2303        {
2304            if (positionData.frame==SlideShowConstructor::SLIDE)
2305            {
2306                osg::AnimationPath::TimeControlPointMap& controlPoints = animation->getTimeControlPointMap();
2307                for(osg::AnimationPath::TimeControlPointMap::iterator itr=controlPoints.begin();
2308                    itr!=controlPoints.end();
2309                    ++itr)
2310                {
2311                    osg::AnimationPath::ControlPoint& cp = itr->second;
2312                    cp.setPosition(convertSlideToModel(cp.getPosition()+positionData.position));
2313                }
2314            }
2315
2316            animation->setLoopMode(positionData.path_loop_mode);
2317
2318            osg::AnimationPathCallback* apc = new osg::AnimationPathCallback(animation);
2319            apc->setTimeOffset(positionData.path_time_offset);
2320            apc->setTimeMultiplier(positionData.path_time_multiplier);
2321            apc->setUseInverseMatrix(positionData.inverse_path);
2322
2323            OSG_INFO<<"UseInverseMatrix "<<positionData.inverse_path<<std::endl;
2324
2325            return apc;
2326
2327        }
2328
2329    }
2330    return 0;
2331}
2332
2333osg::Vec3 SlideShowConstructor::computePositionInModelCoords(const PositionData& positionData) const
2334{
2335    if (positionData.frame==SLIDE)
2336    {
2337        OSG_INFO<<"********* Scaling from slide coords to model coords"<<std::endl;
2338        return convertSlideToModel(positionData.position);
2339    }
2340    else
2341    {
2342        OSG_INFO<<"keeping original model coords"<<std::endl;
2343        return positionData.position;
2344    }
2345}
2346
2347osg::Vec3 SlideShowConstructor::convertSlideToModel(const osg::Vec3& position) const
2348{
2349    return osg::Vec3(_slideOrigin+osg::Vec3(_slideWidth*position.x(),0.0f,_slideHeight*position.y()))*(1.0f-position.z());
2350}
2351
2352osg::Vec3 SlideShowConstructor::convertModelToSlide(const osg::Vec3& position) const
2353{
2354    return osg::Vec3((position.x()*(_slideOrigin.y()/position.y())-_slideOrigin.x())/_slideWidth,
2355                     (position.z()*(_slideOrigin.y()/position.y())-_slideOrigin.z())/_slideHeight,
2356                     1.0f-position.y()/_slideOrigin.y());
2357}
2358
2359void SlideShowConstructor::updatePositionFromInModelCoords(const osg::Vec3& vertex, PositionData& positionData) const
2360{
2361    if (positionData.frame==SLIDE)
2362    {
2363        positionData.position = convertModelToSlide(vertex);
2364    }
2365    else
2366    {
2367        positionData.position = vertex;
2368    }
2369}
2370
2371void SlideShowConstructor::recordOptionsFilePath(const osgDB::Options* options)
2372{
2373    if (options)
2374    {
2375        std::string filename_used = _options->getPluginStringData("filename");
2376        std::string path = osgDB::getFilePath(filename_used);
2377        if (!path.empty() && _filePathData.valid())
2378        {
2379            osgDB::FilePathList::iterator itr = std::find(_filePathData->filePathList.begin(),_filePathData->filePathList.end(),path);
2380            if (itr==_filePathData->filePathList.end())
2381            {
2382                OSG_INFO<<"SlideShowConstructor::recordOptionsFilePath(..) - new path to record path="<<path<<" filename_used="<<filename_used<<std::endl;
2383                _filePathData->filePathList.push_front(path);
2384            }
2385        }
2386    }
2387}
Note: See TracBrowser for help on using the browser.