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

Revision 13041, 73.4 kB (checked in by robert, 3 years ago)

Ran script to remove trailing spaces and tabs

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