diff --git a/CMakeLists.txt b/CMakeLists.txt index 19a6922f3..ceccc8c0e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -325,6 +325,7 @@ FIND_PACKAGE(OurDCMTK) FIND_PACKAGE(OpenAL) FIND_PACKAGE(XUL) FIND_PACKAGE(FFmpeg) +FIND_PACKAGE(LibXml2) #use pkg-config to find various modues INCLUDE(FindPkgConfig OPTIONAL) diff --git a/src/osgPlugins/CMakeLists.txt b/src/osgPlugins/CMakeLists.txt index 1d0ef23ae..f2568a2c3 100644 --- a/src/osgPlugins/CMakeLists.txt +++ b/src/osgPlugins/CMakeLists.txt @@ -134,6 +134,10 @@ ENDIF() ADD_SUBDIRECTORY(3dc) +IF(LIBXML2_FOUND) + ADD_SUBDIRECTORY(p3d) +ENDIF() + IF(CURL_FOUND) ADD_SUBDIRECTORY(curl) ENDIF() diff --git a/src/osgPlugins/p3d/AnimationMaterial.cpp b/src/osgPlugins/p3d/AnimationMaterial.cpp new file mode 100644 index 000000000..1f9a65f3c --- /dev/null +++ b/src/osgPlugins/p3d/AnimationMaterial.cpp @@ -0,0 +1,235 @@ +/* -*-c++-*- Present3D - Copyright (C) 1999-2006 Robert Osfield + * + * This software is open source and may be redistributed and/or modified under + * the terms of the GNU General Public License (GPL) version 2.0. + * The full license is in LICENSE.txt file included with this distribution,. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * include LICENSE.txt for more details. +*/ + +#include "AnimationMaterial.h" +#include +#include +#include +#include + +using namespace ss3d; + +void AnimationMaterial::insert(double time,osg::Material* material) +{ + _timeControlPointMap[time] = material; +} + +bool AnimationMaterial::getMaterial(double time,osg::Material& material) const +{ + if (_timeControlPointMap.empty()) return false; + + switch(_loopMode) + { + case(SWING): + { + double modulated_time = (time - getFirstTime())/(getPeriod()*2.0); + double fraction_part = modulated_time - floor(modulated_time); + if (fraction_part>0.5) fraction_part = 1.0-fraction_part; + + time = getFirstTime()+(fraction_part*2.0) * getPeriod(); + break; + } + case(LOOP): + { + double modulated_time = (time - getFirstTime())/getPeriod(); + double fraction_part = modulated_time - floor(modulated_time); + time = getFirstTime()+fraction_part * getPeriod(); + break; + } + case(NO_LOOPING): + // no need to modulate the time. + break; + } + + + + TimeControlPointMap::const_iterator second = _timeControlPointMap.lower_bound(time); + if (second==_timeControlPointMap.begin()) + { + material = *(second->second); + } + else if (second!=_timeControlPointMap.end()) + { + TimeControlPointMap::const_iterator first = second; + --first; + + // we have both a lower bound and the next item. + + // deta_time = second.time - first.time + double delta_time = second->first - first->first; + + if (delta_time==0.0) + material = *(first->second); + else + { + interpolate(material,(time - first->first)/delta_time, *first->second, *second->second); + } + } + else // (second==_timeControlPointMap.end()) + { + material = *(_timeControlPointMap.rbegin()->second); + } + return true; +} + +template +T interp(float r, const T& lhs, const T& rhs) +{ + return lhs*(1.0f-r)+rhs*r; +} + + +void AnimationMaterial::interpolate(osg::Material& material, float r, const osg::Material& lhs,const osg::Material& rhs) const +{ + material.setColorMode(lhs.getColorMode()); + + material.setAmbient(osg::Material::FRONT_AND_BACK,interp(r, lhs.getAmbient(osg::Material::FRONT),rhs.getAmbient(osg::Material::FRONT))); + if (!material.getAmbientFrontAndBack()) + material.setAmbient(osg::Material::BACK,interp(r, lhs.getAmbient(osg::Material::BACK),rhs.getAmbient(osg::Material::BACK))); + + material.setDiffuse(osg::Material::FRONT_AND_BACK,interp(r, lhs.getDiffuse(osg::Material::FRONT),rhs.getDiffuse(osg::Material::FRONT))); + if (!material.getDiffuseFrontAndBack()) + material.setDiffuse(osg::Material::BACK,interp(r, lhs.getDiffuse(osg::Material::BACK),rhs.getDiffuse(osg::Material::BACK))); + + material.setSpecular(osg::Material::FRONT_AND_BACK,interp(r, lhs.getSpecular(osg::Material::FRONT),rhs.getSpecular(osg::Material::FRONT))); + if (!material.getSpecularFrontAndBack()) + material.setSpecular(osg::Material::BACK,interp(r, lhs.getSpecular(osg::Material::BACK),rhs.getSpecular(osg::Material::BACK))); + + material.setEmission(osg::Material::FRONT_AND_BACK,interp(r, lhs.getEmission(osg::Material::FRONT),rhs.getEmission(osg::Material::FRONT))); + if (!material.getEmissionFrontAndBack()) + material.setEmission(osg::Material::BACK,interp(r, lhs.getEmission(osg::Material::BACK),rhs.getEmission(osg::Material::BACK))); + + material.setShininess(osg::Material::FRONT_AND_BACK,interp(r, lhs.getShininess(osg::Material::FRONT),rhs.getShininess(osg::Material::FRONT))); + if (!material.getShininessFrontAndBack()) + material.setShininess(osg::Material::BACK,interp(r, lhs.getShininess(osg::Material::BACK),rhs.getShininess(osg::Material::BACK))); +} + +void AnimationMaterial::read(std::istream& in) +{ + while (!in.eof()) + { + double time; + osg::Vec4 color; + in >> time >> color[0] >> color[1] >> color[2] >> color[3]; + if(!in.eof()) + { + osg::Material* material = new osg::Material; + material->setAmbient(osg::Material::FRONT_AND_BACK,color); + material->setDiffuse(osg::Material::FRONT_AND_BACK,color); + insert(time,material); + } + } +} + +void AnimationMaterial::write(std::ostream& fout) const +{ + const TimeControlPointMap& tcpm = getTimeControlPointMap(); + for(TimeControlPointMap::const_iterator tcpmitr=tcpm.begin(); + tcpmitr!=tcpm.end(); + ++tcpmitr) + { + fout<first<<" "<second->getDiffuse(osg::Material::FRONT)<second->getDiffuse(osg::Material::FRONT))[3]!=1.0f) return true; + } + return false; +} + + +void AnimationMaterialCallback::operator()(osg::Node* node, osg::NodeVisitor* nv) +{ + if (_animationMaterial.valid() && + nv->getVisitorType()==osg::NodeVisitor::UPDATE_VISITOR && + nv->getFrameStamp()) + { + double time = nv->getFrameStamp()->getReferenceTime(); + _latestTime = time; + + if (!_pause) + { + // Only update _firstTime the first time, when its value is still DBL_MAX + if (_firstTime==DBL_MAX) + { + osg::notify(osg::INFO)<<"AnimationMaterialCallback::operator() resetting _firstTime to "<(stateset->getAttribute(osg::StateAttribute::MATERIAL)); + + if (!material) + { + material = new osg::Material; + stateset->setAttribute(material,osg::StateAttribute::OVERRIDE); + } + + _animationMaterial->getMaterial(getAnimationTime(),*material); +} + + +void AnimationMaterialCallback::reset() +{ +#if 1 + _firstTime = DBL_MAX; + _pauseTime = DBL_MAX; +#else + _firstTime = _latestTime; + _pauseTime = _latestTime; +#endif +} + +void AnimationMaterialCallback::setPause(bool pause) +{ + if (_pause==pause) + { + return; + } + + _pause = pause; + + if (_firstTime==DBL_MAX) return; + + if (_pause) + { + _pauseTime = _latestTime; + } + else + { + _firstTime += (_latestTime-_pauseTime); + } +} diff --git a/src/osgPlugins/p3d/AnimationMaterial.h b/src/osgPlugins/p3d/AnimationMaterial.h new file mode 100644 index 000000000..624d10159 --- /dev/null +++ b/src/osgPlugins/p3d/AnimationMaterial.h @@ -0,0 +1,170 @@ +/* -*-c++-*- Present3D - Copyright (C) 1999-2006 Robert Osfield + * + * This software is open source and may be redistributed and/or modified under + * the terms of the GNU General Public License (GPL) version 2.0. + * The full license is in LICENSE.txt file included with this distribution,. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * include LICENSE.txt for more details. +*/ + +#ifndef OSG_ANIMATIONMATERIAL +#define OSG_ANIMATIONMATERIAL 1 + +#include +#include + +#include +#include + +namespace ss3d { + +/** AnimationMaterial for specify the time varying transformation pathway to use when update camera and model objects. + * Subclassed from Transform::ComputeTransformCallback allows AnimationMaterial to + * be attached directly to Transform nodes to move subgraphs around the scene. +*/ +class AnimationMaterial : public virtual osg::Object +{ + public: + + AnimationMaterial():_loopMode(LOOP) {} + + AnimationMaterial(const AnimationMaterial& ap, const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY): + Object(ap,copyop), + _timeControlPointMap(ap._timeControlPointMap), + _loopMode(ap._loopMode) {} + + META_Object(osg,AnimationMaterial); + + + /** get the transformation matrix for a point in time.*/ + bool getMaterial(double time,osg::Material& material) const; + + void insert(double time,osg::Material* material); + + double getFirstTime() const { if (!_timeControlPointMap.empty()) return _timeControlPointMap.begin()->first; else return 0.0;} + double getLastTime() const { if (!_timeControlPointMap.empty()) return _timeControlPointMap.rbegin()->first; else return 0.0;} + double getPeriod() const { return getLastTime()-getFirstTime();} + + enum LoopMode + { + SWING, + LOOP, + NO_LOOPING + }; + + void setLoopMode(LoopMode lm) { _loopMode = lm; } + + LoopMode getLoopMode() const { return _loopMode; } + + + typedef std::map > TimeControlPointMap; + + TimeControlPointMap& getTimeControlPointMap() { return _timeControlPointMap; } + + const TimeControlPointMap& getTimeControlPointMap() const { return _timeControlPointMap; } + + /** read the anumation path from a flat ascii file stream.*/ + void read(std::istream& in); + + /** write the anumation path to a flat ascii file stream.*/ + void write(std::ostream& out) const; + + bool requiresBlending() const; + + protected: + + virtual ~AnimationMaterial() {} + + void interpolate(osg::Material& material, float r, const osg::Material& lhs,const osg::Material& rhs) const; + + TimeControlPointMap _timeControlPointMap; + LoopMode _loopMode; + +}; + + +class AnimationMaterialCallback : public osg::NodeCallback +{ + public: + + AnimationMaterialCallback(): + _timeOffset(0.0), + _timeMultiplier(1.0), + _firstTime(DBL_MAX), + _latestTime(0.0), + _pause(false), + _pauseTime(0.0) {} + + + AnimationMaterialCallback(const AnimationMaterialCallback& apc,const osg::CopyOp& copyop): + osg::NodeCallback(apc,copyop), + _animationMaterial(apc._animationMaterial), + _useInverseMatrix(apc._useInverseMatrix), + _timeOffset(apc._timeOffset), + _timeMultiplier(apc._timeMultiplier), + _firstTime(apc._firstTime), + _latestTime(apc._latestTime), + _pause(apc._pause), + _pauseTime(apc._pauseTime) {} + + + META_Object(osg,AnimationMaterialCallback); + + AnimationMaterialCallback(AnimationMaterial* ap,double timeOffset=0.0f,double timeMultiplier=1.0f): + _animationMaterial(ap), + _useInverseMatrix(false), + _timeOffset(timeOffset), + _timeMultiplier(timeMultiplier), + _firstTime(DBL_MAX), + _latestTime(0.0), + _pause(false), + _pauseTime(0.0) {} + + void setAnimationMaterial(AnimationMaterial* path) { _animationMaterial = path; } + + AnimationMaterial* getAnimationMaterial() { return _animationMaterial.get(); } + + const AnimationMaterial* getAnimationMaterial() const { return _animationMaterial.get(); } + + void setTimeOffset(double offset) { _timeOffset = offset; } + double getTimeOffset() const { return _timeOffset; } + + void setTimeMultiplier(double multiplier) { _timeMultiplier = multiplier; } + double getTimeMultiplier() const { return _timeMultiplier; } + + void reset(); + + void setPause(bool pause); + + /** get the animation time that is used to specify the position along the AnimationMaterial. + * Animation time is computed from the formula ((_latestTime-_firstTime)-_timeOffset)*_timeMultiplier.*/ + double getAnimationTime() const; + + /** implements the callback*/ + virtual void operator()(osg::Node* node, osg::NodeVisitor* nv); + + void update(osg::Node& node); + + public: + + osg::ref_ptr _animationMaterial; + bool _useInverseMatrix; + double _timeOffset; + double _timeMultiplier; + double _firstTime; + double _latestTime; + bool _pause; + double _pauseTime; + + protected: + + ~AnimationMaterialCallback(){} + +}; + +} + +#endif diff --git a/src/osgPlugins/p3d/CMakeLists.txt b/src/osgPlugins/p3d/CMakeLists.txt new file mode 100644 index 000000000..01c0259d3 --- /dev/null +++ b/src/osgPlugins/p3d/CMakeLists.txt @@ -0,0 +1,23 @@ +INCLUDE_DIRECTORIES(${LIBXML2_INCLUDE_DIR} ) +SET(TARGET_EXTERNAL_LIBRARIES ${LIBXML2_LIBRARIES}) + +SET(TARGET_SRC + SlideShowConstructor.cpp + ReaderWriterP3D.cpp + PickEventHandler.cpp + AnimationMaterial.cpp + SlideEventHandler.cpp + CompileSlideCallback.cpp +) +SET(TARGET_H + SlideShowConstructor.h + PickEventHandler.h + AnimationMaterial.h + SlideEventHandler.h + CompileSlideCallback.h +) + +SET(TARGET_ADDED_LIBRARIES osgVolume) + +#### end var setup ### +SETUP_PLUGIN(p3d) diff --git a/src/osgPlugins/p3d/CompileSlideCallback.cpp b/src/osgPlugins/p3d/CompileSlideCallback.cpp new file mode 100644 index 000000000..fa19d89d3 --- /dev/null +++ b/src/osgPlugins/p3d/CompileSlideCallback.cpp @@ -0,0 +1,48 @@ +/* -*-c++-*- Present3D - Copyright (C) 1999-2006 Robert Osfield + * + * This software is open source and may be redistributed and/or modified under + * the terms of the GNU General Public License (GPL) version 2.0. + * The full license is in LICENSE.txt file included with this distribution,. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * include LICENSE.txt for more details. +*/ + +#include "CompileSlideCallback.h" + +#include + +using namespace ss3d; + +void CompileSlideCallback::operator()(const osg::Camera & camera) const +{ + osg::GraphicsContext* context = const_cast(camera.getGraphicsContext()); + if (!context) return; + + osg::State* state = context->getState(); + if (!state) return; + + const osg::FrameStamp* fs = state->getFrameStamp(); + if (!fs) return; + + if (_needCompile) + { + _frameNumber = fs->getFrameNumber(); + _needCompile = false; + } + + if (_frameNumber!=fs->getFrameNumber()) return; + + osgUtil::GLObjectsVisitor globjVisitor(osgUtil::GLObjectsVisitor::COMPILE_DISPLAY_LISTS| + osgUtil::GLObjectsVisitor::COMPILE_STATE_ATTRIBUTES); + + globjVisitor.setTraversalMode(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN); + + globjVisitor.setNodeMaskOverride(0xffffffff); + + globjVisitor.setState(state); + + _sceneToCompile->accept(globjVisitor); +} diff --git a/src/osgPlugins/p3d/CompileSlideCallback.h b/src/osgPlugins/p3d/CompileSlideCallback.h new file mode 100644 index 000000000..9b72ef7a0 --- /dev/null +++ b/src/osgPlugins/p3d/CompileSlideCallback.h @@ -0,0 +1,44 @@ +/* -*-c++-*- Present3D - Copyright (C) 1999-2006 Robert Osfield + * + * This software is open source and may be redistributed and/or modified under + * the terms of the GNU General Public License (GPL) version 2.0. + * The full license is in LICENSE.txt file included with this distribution,. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * include LICENSE.txt for more details. +*/ + +#ifndef OSG_COMPILESLIDECALLBACK +#define OSG_COMPILESLIDECALLBACK 1 + +#include + +namespace ss3d { + +class CompileSlideCallback : public osg::Camera::DrawCallback +{ + public: + + CompileSlideCallback(): + _needCompile(false), + _frameNumber(0) {} + + virtual void operator()(const osg::Camera& camera) const; + + void needCompile(osg::Node* node) { _needCompile=true; _sceneToCompile = node; } + + protected: + + virtual ~CompileSlideCallback() {} + + mutable bool _needCompile; + mutable int _frameNumber; + osg::ref_ptr _sceneToCompile; + +}; + +} + +#endif diff --git a/src/osgPlugins/p3d/PickEventHandler.cpp b/src/osgPlugins/p3d/PickEventHandler.cpp new file mode 100644 index 000000000..057413b01 --- /dev/null +++ b/src/osgPlugins/p3d/PickEventHandler.cpp @@ -0,0 +1,209 @@ +/* -*-c++-*- Present3D - Copyright (C) 1999-2006 Robert Osfield + * + * This software is open source and may be redistributed and/or modified under + * the terms of the GNU General Public License (GPL) version 2.0. + * The full license is in LICENSE.txt file included with this distribution,. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * include LICENSE.txt for more details. +*/ + +#include "PickEventHandler.h" +#include "SlideEventHandler.h" + +#include +#include +#include + +PickEventHandler::PickEventHandler(SlideShowConstructor::Operation operation,bool relativeJump, int slideNum, int layerNum): + _operation(operation), + _relativeJump(relativeJump), + _slideNum(slideNum), + _layerNum(layerNum) +{ +} + +PickEventHandler::PickEventHandler(const std::string& str, SlideShowConstructor::Operation operation,bool relativeJump, int slideNum, int layerNum): + _command(str), + _operation(operation), + _relativeJump(relativeJump), + _slideNum(slideNum), + _layerNum(layerNum) +{ +} + +PickEventHandler::PickEventHandler(const SlideShowConstructor::KeyPosition& keyPos,bool relativeJump, int slideNum, int layerNum): + _keyPos(keyPos), + _operation(SlideShowConstructor::EVENT), + _relativeJump(relativeJump), + _slideNum(slideNum), + _layerNum(layerNum) +{ +} + + +bool PickEventHandler::handle(const osgGA::GUIEventAdapter& ea,osgGA::GUIActionAdapter& aa, osg::Object*, osg::NodeVisitor* nv) +{ + switch(ea.getEventType()) + { + case(osgGA::GUIEventAdapter::MOVE): + case(osgGA::GUIEventAdapter::PUSH): + case(osgGA::GUIEventAdapter::RELEASE): + { + osgViewer::Viewer* viewer = dynamic_cast(&aa); + osgUtil::LineSegmentIntersector::Intersections intersections; + if (viewer->computeIntersections(ea.getX(),ea.getY(), nv->getNodePath(), intersections)) + { + for(osgUtil::LineSegmentIntersector::Intersections::iterator hitr=intersections.begin(); + hitr!=intersections.end(); + ++hitr) + { + if (ea.getEventType()==osgGA::GUIEventAdapter::MOVE) + { + osg::notify(osg::INFO)<<"Tooltip..."<className()<dispatchEvent(_keyPos); + break; + } + } + + if (requiresJump()) + { + osg::notify(osg::NOTICE)<<"Requires jump "<<_relativeJump<<", "<<_slideNum<<", "<<_layerNum<getActiveSlide(); + int previousLayer = SlideEventHandler::instance()->getActiveLayer(); + int newSlide = previousSlide + _slideNum; + int newLayer = previousLayer + _layerNum; + if (newLayer<0) + { + newLayer = 0; + } + + osg::notify(osg::NOTICE)<<" jump to "<selectSlide(newSlide, newLayer); + } + else + { + SlideEventHandler::instance()->selectSlide(_slideNum,_layerNum); + } + } + else + { + osg::notify(osg::NOTICE)<<"No jump required."< +#include + +#include + +#include "SlideShowConstructor.h" + +class PickEventHandler : public osgGA::GUIEventHandler +{ + public: + + PickEventHandler(SlideShowConstructor::Operation operation, bool relativeJump=true, int slideNum=0, int layerNum=0); + PickEventHandler(const std::string& str, SlideShowConstructor::Operation operation, bool relativeJump=true, int slideNum=0, int layerNum=0); + PickEventHandler(const SlideShowConstructor::KeyPosition& keyPos, bool relativeJump=true, int slideNum=0, int layerNum=0); + + void setOperation(SlideShowConstructor::Operation operation) { _operation = operation; } + SlideShowConstructor::Operation getOperation() const { return _operation; } + + void setCommand(const std::string& str) { _command = str; } + const std::string& getCommand() const { return _command; } + + void setKeyPosition(const SlideShowConstructor::KeyPosition& keyPos) { _keyPos = keyPos; } + const SlideShowConstructor::KeyPosition& getKeyPosition() const { return _keyPos; } + + void setRelativeJump(int slideDelta, int layerDelta); + void setAbsoluteJump(int slideNum, int layerNum); + + bool getRelativeJump() const { return _relativeJump; } + int getSlideNum() const { return _slideNum; } + int getLayerNum() const { return _layerNum; } + + bool requiresJump() const { return _relativeJump ? (_slideNum!=0 || _layerNum!=0) : true; } + + virtual bool handle(const osgGA::GUIEventAdapter& ea,osgGA::GUIActionAdapter& aa, osg::Object* object, osg::NodeVisitor* nv); + + virtual void accept(osgGA::GUIEventHandlerVisitor& v); + + virtual void getUsage(osg::ApplicationUsage& usage) const; + + void doOperation(); + + std::string _command; + SlideShowConstructor::KeyPosition _keyPos; + SlideShowConstructor::Operation _operation; + + bool _relativeJump; + int _slideNum; + int _layerNum; +}; + +#endif diff --git a/src/osgPlugins/p3d/ReaderWriterP3D.cpp b/src/osgPlugins/p3d/ReaderWriterP3D.cpp new file mode 100644 index 000000000..caffa9e8a --- /dev/null +++ b/src/osgPlugins/p3d/ReaderWriterP3D.cpp @@ -0,0 +1,1719 @@ +/* -*-c++-*- Present3D - Copyright (C) 1999-2006 Robert Osfield + * + * This software is open source and may be redistributed and/or modified under + * the terms of the GNU General Public License (GPL) version 2.0. + * The full license is in LICENSE.txt file included with this distribution,. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * include LICENSE.txt for more details. +*/ + +#include +#include + +#include +#include +#include +#include + +#include + +#include "SlideShowConstructor.h" +#include "SlideEventHandler.h" + +#include +#include +#include +#include +#include + +#include +#include + + + +/** + * OpenSceneGraph plugin wrapper/converter. + */ +class ReaderWriterP3DXML : public osgDB::ReaderWriter +{ +public: + ReaderWriterP3DXML() + { + _colorMap["WHITE"] .set(1.0f,1.0f,1.0f,1.0f); + _colorMap["BLACK"] .set(0.0f,0.0f,0.0f,1.0f); + _colorMap["PURPLE"] .set(1.0f,0.0f,1.0f,1.0f); + _colorMap["BLUE"] .set(0.0f,0.0f,1.0f,1.0f); + _colorMap["RED"] .set(1.0f,0.0f,0.0f,1.0f); + _colorMap["CYAN"] .set(0.0f,1.0f,1.0f,1.0f); + _colorMap["YELLOW"] .set(1.0f,1.0f,0.0f,1.0f); + _colorMap["GREEN"] .set(0.0f,1.0f,0.0f,1.0f); + _colorMap["SKY"] .set(0.2f, 0.2f, 0.4f, 1.0f); + + _layoutMap["LEFT_TO_RIGHT"] = osgText::Text::LEFT_TO_RIGHT; + _layoutMap["RIGHT_TO_LEFT"] = osgText::Text::RIGHT_TO_LEFT; + _layoutMap["VERTICAL"] = osgText::Text::VERTICAL; + + _alignmentMap["LEFT_TOP"] = osgText::Text::LEFT_TOP; + _alignmentMap["LEFT_CENTER"] = osgText::Text::LEFT_CENTER; + _alignmentMap["LEFT_BOTTOM"] = osgText::Text::LEFT_BOTTOM; + + _alignmentMap["CENTER_TOP"] = osgText::Text::CENTER_TOP; + _alignmentMap["CENTER_CENTER"] = osgText::Text::CENTER_CENTER; + _alignmentMap["CENTER_BOTTOM"] = osgText::Text::CENTER_BOTTOM; + + _alignmentMap["RIGHT_TOP"] = osgText::Text::RIGHT_TOP; + _alignmentMap["RIGHT_CENTER"] = osgText::Text::RIGHT_CENTER; + _alignmentMap["RIGHT_BOTTOM"] = osgText::Text::RIGHT_BOTTOM; + + _alignmentMap["LEFT_BASE_LINE"] = osgText::Text::LEFT_BASE_LINE; + _alignmentMap["CENTER_BASE_LINE"] = osgText::Text::CENTER_BASE_LINE; + _alignmentMap["RIGHT_BASE_LINE"] = osgText::Text::RIGHT_BASE_LINE; + _alignmentMap["BASE_LINE"] = osgText::Text::LEFT_BASE_LINE; + + _stringKeyMap["Home"]=' '; + _stringKeyMap["Start"]= osgGA::GUIEventAdapter::KEY_Home; + _stringKeyMap["Next"]= osgGA::GUIEventAdapter::KEY_Page_Down; + _stringKeyMap["Previous"]=osgGA::GUIEventAdapter::KEY_Page_Up; + _stringKeyMap["Up"]=osgGA::GUIEventAdapter::KEY_Up; + _stringKeyMap["Down"]=osgGA::GUIEventAdapter::KEY_Down; + _stringKeyMap["End"]=osgGA::GUIEventAdapter::KEY_End; + _stringKeyMap["Page Down"]=osgGA::GUIEventAdapter::KEY_Page_Down; + _stringKeyMap["Page Up"]=osgGA::GUIEventAdapter::KEY_Page_Up; + _stringKeyMap["F1"]=osgGA::GUIEventAdapter::KEY_F1; + _stringKeyMap["F2"]=osgGA::GUIEventAdapter::KEY_F2; + _stringKeyMap["F3"]=osgGA::GUIEventAdapter::KEY_F3; + _stringKeyMap["F4"]=osgGA::GUIEventAdapter::KEY_F4; + _stringKeyMap["F5"]=osgGA::GUIEventAdapter::KEY_F5; + _stringKeyMap["F6"]=osgGA::GUIEventAdapter::KEY_F6; + _stringKeyMap["F7"]=osgGA::GUIEventAdapter::KEY_F7; + _stringKeyMap["F8"]=osgGA::GUIEventAdapter::KEY_F8; + _stringKeyMap["F9"]=osgGA::GUIEventAdapter::KEY_F9; + _stringKeyMap["F10"]=osgGA::GUIEventAdapter::KEY_F10; + _stringKeyMap["F11"]=osgGA::GUIEventAdapter::KEY_F11; + _stringKeyMap["F12"]=osgGA::GUIEventAdapter::KEY_F12; + + + _notifyLevel = osg::INFO; + } + + virtual const char* className() const + { + return "present3D XML Reader/Writer"; + } + + virtual bool acceptsExtension(const std::string& extension) const + { + return osgDB::equalCaseInsensitive(extension,"p3d") || + osgDB::equalCaseInsensitive(extension,"xml") ; + } + + virtual ReadResult readNode(const std::string& fileName, + const osgDB::ReaderWriter::Options* options) const; + + void parseModel(SlideShowConstructor& constructor, xmlDocPtr doc, xmlNodePtr cur) const; + + void parseVolume(SlideShowConstructor& constructor, xmlDocPtr doc, xmlNodePtr cur) const; + + void parseStereoPair(SlideShowConstructor& constructor, xmlDocPtr doc, xmlNodePtr cur) const; + + void parseLayer(SlideShowConstructor& constructor, xmlDocPtr doc, xmlNodePtr cur) const; + + void parseBullets(SlideShowConstructor& constructor, xmlDocPtr doc, xmlNodePtr cur, bool inheritPreviousLayers, bool defineAsBaseLayer) const; + void parseText(SlideShowConstructor& constructor, xmlDocPtr doc, xmlNodePtr cur, bool inheritPreviousLayers, bool defineAsBaseLayer) const; + + void parsePage (SlideShowConstructor& constructor, xmlDocPtr doc, xmlNodePtr cur) const; + + void parseSlide (SlideShowConstructor& constructor, xmlDocPtr doc, xmlNodePtr cur, bool parseTitles=true, bool parseLayers=true) const; + + void parsePdfDocument (SlideShowConstructor& constructor, xmlDocPtr doc, xmlNodePtr cur) const; + + osg::Vec4 mapStringToColor(const std::string& str) const + { + ColorMap::const_iterator itr=_colorMap.find(str); + if (itr!=_colorMap.end()) return itr->second; + osg::Vec4 color; + if (read(str,color)) return color; + else return osg::Vec4(0.0f,0.0f,0.0f,1.0f); + } + + inline osg::Vec4 accumulateRotation(const osg::Vec4& lhs, const osg::Vec4& rhs) const + { + osg::Quat qlhs,qrhs; + qlhs.makeRotate(osg::DegreesToRadians(lhs[0]),lhs[1],lhs[2],lhs[3]); + qrhs.makeRotate(osg::DegreesToRadians(rhs[0]),rhs[1],rhs[2],rhs[3]); + osg::Quat quat = qlhs*qrhs; + osg::Vec4d result; + quat.getRotate ( result[0], result[1], result[2], result[3]); + result[0] = osg::RadiansToDegrees(result[0]); + return result; + } + + inline bool read(const char* str, int& value) const; + inline bool read(const char* str, float& value) const; + inline bool read(const char* str, double& value) const; + inline bool read(const char* str, osg::Vec2& value) const; + inline bool read(const char* str, osg::Vec3& value) const; + inline bool read(const char* str, osg::Vec4& value) const; + + inline bool read(const std::string& str, int& value) const; + inline bool read(const std::string& str, float& value) const; + inline bool read(const std::string& str, double& value) const; + inline bool read(const std::string& str, osg::Vec2& value) const; + inline bool read(const std::string& str, osg::Vec3& value) const; + inline bool read(const std::string& str, osg::Vec4& value) const; + + bool getProperty(xmlNodePtr cur, const char* token) const; + bool getProperty(xmlNodePtr cur, const char* token, int& value) const; + bool getProperty(xmlNodePtr cur, const char* token, float& value) const; + bool getProperty(xmlNodePtr cur, const char* token, double& value) const; + bool getProperty(xmlNodePtr cur, const char* token, osg::Vec2& value) const; + bool getProperty(xmlNodePtr cur, const char* token, osg::Vec3& value) const; + bool getProperty(xmlNodePtr cur, const char* token, osg::Vec4& value) const; + bool getProperty(xmlNodePtr cur, const char* token, std::string& value) const; + bool getProperty(xmlNodePtr cur, const char* token, osgText::Text::Layout& value) const; + bool getProperty(xmlNodePtr cur, const char* token, osgText::Text::AlignmentType& value) const; + + bool getProperties(xmlNodePtr cur, SlideShowConstructor::PositionData& value) const; + bool getProperties(xmlNodePtr cur, SlideShowConstructor::FontData& value) const; + bool getProperties(xmlNodePtr cur, SlideShowConstructor::ModelData& value) const; + bool getProperties(xmlNodePtr cur, SlideShowConstructor::ImageData& value) const; + bool getJumpProperties(xmlNodePtr cur, bool& relativeJump, int& slideNum, int& layerNum) const; + + bool getKeyPositionInner(xmlDocPtr doc, xmlNodePtr cur, SlideShowConstructor::KeyPosition& keyPosition) const; + bool getKeyPosition(xmlDocPtr doc, xmlNodePtr cur, SlideShowConstructor::KeyPosition& keyPosition) const; + + typedef std::map ColorMap; + typedef std::map LayoutMap; + typedef std::map AlignmentMap; + typedef std::map StringKeyMap; + + std::string expandEnvVarsInFileName(const std::string& filename) const; + + + ColorMap _colorMap; + LayoutMap _layoutMap; + AlignmentMap _alignmentMap; + StringKeyMap _stringKeyMap; + + typedef std::pair DocNodePair; + typedef std::map TemplateMap; + + mutable TemplateMap _templateMap; + + osg::NotifySeverity _notifyLevel; + +}; + +// Register with Registry to instantiate the above reader/writer. +osgDB::RegisterReaderWriterProxy g_readerWriter_P3DXML_Proxy; + +std::string ReaderWriterP3DXML::expandEnvVarsInFileName(const std::string& filename) const +{ + std::string argument(filename); + std::string::size_type start_pos = argument.find("${"); + + while (start_pos != std::string::npos) + { + std::string::size_type end_pos = argument.find("}",start_pos); + if (start_pos != std::string::npos) + { + std::string var = argument.substr(start_pos+2, end_pos-start_pos-2); + const char* str = getenv(var.c_str()); + if (str) + { + argument.erase(start_pos, end_pos-start_pos+1); + argument.insert(start_pos, str); + } + start_pos = argument.find("${",end_pos); + } + else + { + start_pos = std::string::npos; + } + + } + + return argument; +} + +bool ReaderWriterP3DXML::read(const char* str, int& value) const +{ + if (!str) return false; + std::istringstream iss((const char*)str); + iss >> value; + return !iss.fail(); +} + +bool ReaderWriterP3DXML::read(const char* str, float& value) const +{ + if (!str) return false; + std::istringstream iss((const char*)str); + iss >> value; + return !iss.fail(); +} + +bool ReaderWriterP3DXML::read(const char* str, double& value) const +{ + if (!str) return false; + std::istringstream iss((const char*)str); + iss >> value; + return !iss.fail(); +} + +bool ReaderWriterP3DXML::read(const char* str, osg::Vec2& value) const +{ + if (!str) return false; + std::istringstream iss((const char*)str); + iss >> value.x() >> value.y(); + return !iss.fail(); +} + +bool ReaderWriterP3DXML::read(const char* str, osg::Vec3& value) const +{ + if (!str) return false; + std::istringstream iss((const char*)str); + iss >> value.x() >> value.y() >> value.z(); + return !iss.fail(); +} + +bool ReaderWriterP3DXML::read(const char* str, osg::Vec4& value) const +{ + if (!str) return false; + std::istringstream iss((const char*)str); + iss >> value.x() >> value.y() >> value.z() >> value.w(); + return !iss.fail(); +} + +bool ReaderWriterP3DXML::read(const std::string& str, int& value) const +{ + std::istringstream iss(str); + iss >> value; + return !iss.fail(); +} + +bool ReaderWriterP3DXML::read(const std::string& str, float& value) const +{ + std::istringstream iss(str); + iss >> value; + return !iss.fail(); +} + +bool ReaderWriterP3DXML::read(const std::string& str, double& value) const +{ + std::istringstream iss(str); + iss >> value; + return !iss.fail(); +} + +bool ReaderWriterP3DXML::read(const std::string& str, osg::Vec2& value) const +{ + std::istringstream iss(str); + iss >> value.x() >> value.y(); + return !iss.fail(); +} + +bool ReaderWriterP3DXML::read(const std::string& str, osg::Vec3& value) const +{ + std::istringstream iss(str); + iss >> value.x() >> value.y() >> value.z(); + return !iss.fail(); +} + +bool ReaderWriterP3DXML::read(const std::string& str, osg::Vec4& value) const +{ + std::istringstream iss(str); + iss >> value.x() >> value.y() >> value.z() >> value.w(); + return !iss.fail(); +} + +bool ReaderWriterP3DXML::getProperty(xmlNodePtr cur, const char* token) const +{ + bool success = false; + xmlChar *key; + key = xmlGetProp (cur, (const xmlChar *)token); + if (key) success=true; + xmlFree(key); + return success; +} + +bool ReaderWriterP3DXML::getProperty(xmlNodePtr cur, const char* token, int& value) const +{ + xmlChar *key; + key = xmlGetProp (cur, (const xmlChar *)token); + bool success = read((const char*)key,value); + xmlFree(key); + return success; +} + +bool ReaderWriterP3DXML::getProperty(xmlNodePtr cur, const char* token, float& value) const +{ + xmlChar *key; + key = xmlGetProp (cur, (const xmlChar *)token); + bool success = read((const char*)key,value); + xmlFree(key); + return success; +} + +bool ReaderWriterP3DXML::getProperty(xmlNodePtr cur, const char* token, double& value) const +{ + xmlChar *key; + key = xmlGetProp (cur, (const xmlChar *)token); + bool success = read((const char*)key,value); + xmlFree(key); + return success; +} + +bool ReaderWriterP3DXML::getProperty(xmlNodePtr cur, const char* token, osg::Vec2& value) const +{ + xmlChar *key; + key = xmlGetProp (cur, (const xmlChar *)token); + bool success = read((const char*)key,value); + xmlFree(key); + return success; +} + +bool ReaderWriterP3DXML::getProperty(xmlNodePtr cur, const char* token, osg::Vec3& value) const +{ + xmlChar *key; + key = xmlGetProp (cur, (const xmlChar *)token); + bool success = read((const char*)key,value); + xmlFree(key); + return success; +} + +bool ReaderWriterP3DXML::getProperty(xmlNodePtr cur, const char* token, osg::Vec4& value) const +{ + xmlChar *key; + key = xmlGetProp (cur, (const xmlChar *)token); + bool success = read((const char*)key,value); + xmlFree(key); + return success; +} + +bool ReaderWriterP3DXML::getProperty(xmlNodePtr cur, const char* token, std::string& value) const +{ + bool success = false; + xmlChar *key; + key = xmlGetProp (cur, (const xmlChar *)token); + if (key) + { + success = true; + value = (const char*)key; + } + xmlFree(key); + return success; +} + +bool ReaderWriterP3DXML::getProperty(xmlNodePtr cur, const char* token, osgText::Text::Layout& value) const +{ + bool success = false; + xmlChar *key; + key = xmlGetProp (cur, (const xmlChar *)token); + if (key) + { + success = true; + std::string str = (const char*)key; + LayoutMap::const_iterator itr = _layoutMap.find(str); + if (itr!=_layoutMap.end()) + { + value = itr->second; + } + } + xmlFree(key); + return success; +} + +bool ReaderWriterP3DXML::getProperty(xmlNodePtr cur, const char* token, osgText::Text::AlignmentType& value) const +{ + bool success = false; + xmlChar *key; + key = xmlGetProp (cur, (const xmlChar *)token); + if (key) + { + success = true; + std::string str = (const char*)key; + AlignmentMap::const_iterator itr = _alignmentMap.find(str); + if (itr!=_alignmentMap.end()) + { + value = itr->second; + } + } + xmlFree(key); + return success; +} + +bool ReaderWriterP3DXML::getProperties(xmlNodePtr cur, SlideShowConstructor::PositionData& value) const +{ + bool propertiesRead=false; + + osg::Vec3 position(0.0f,1.0f,0.0f); + osg::Vec4 rotate(0.0f,0.0f,0.0f,1.0f); + float scale = 1.0f; + + osg::Vec4 rotation(0.0f,0.0f,0.0f,1.0f); + + // temporary + std::string str; + + if (getProperty(cur, "coordinate_frame", str)) + { + propertiesRead = true; + + if (str=="model") value.frame = SlideShowConstructor::MODEL; + else if (str=="slide") value.frame = SlideShowConstructor::SLIDE; + else osg::notify(_notifyLevel)<<"Parser error - coordinate_frame=\""<xmlChildrenNode, 1); + if (key) filename = (const char*)key; + xmlFree(key); + + if (!filename.empty()) + { + constructor.addModel(filename, + positionRead ? positionData : constructor.getModelPositionData(), + modelData); + } + +} + +void ReaderWriterP3DXML::parseVolume(SlideShowConstructor& constructor, xmlDocPtr doc, xmlNodePtr cur) const +{ + + SlideShowConstructor::PositionData positionData = constructor.getModelPositionData(); + bool positionRead = getProperties(cur,positionData); + + std::string filename; + xmlChar *key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (key) filename = (const char*)key; + xmlFree(key); + + if (!filename.empty()) + { + constructor.addVolume(filename, + positionRead ? positionData : constructor.getModelPositionData()); + } +} + +void ReaderWriterP3DXML::parseStereoPair(SlideShowConstructor& constructor, xmlDocPtr doc, xmlNodePtr cur) const +{ + std::string filenameLeft; + std::string filenameRight; + + SlideShowConstructor::PositionData positionData = constructor.getImagePositionData(); + bool positionRead = getProperties(cur,positionData); + + SlideShowConstructor::ImageData imageDataLeft;// = constructor.getImageData(); + SlideShowConstructor::ImageData imageDataRight;// = constructor.getImageData(); + + xmlChar *key; + cur = cur->xmlChildrenNode; + + while (cur != NULL) + { + if ((!xmlStrcmp(cur->name, (const xmlChar *)"image_left"))) + { + getProperties(cur,imageDataLeft); + + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (key) filenameLeft = (const char*)key; + xmlFree(key); + + + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"image_right"))) + { + getProperties(cur,imageDataRight); + + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (key) filenameRight = (const char*)key; + xmlFree(key); + + } + cur = cur->next; + } + + if (!filenameLeft.empty() && !filenameRight.empty()) + constructor.addStereoImagePair(filenameLeft,imageDataLeft, + filenameRight, imageDataRight, + positionRead ? positionData : constructor.getImagePositionData()); + +} + +bool ReaderWriterP3DXML::getKeyPosition(xmlDocPtr doc, xmlNodePtr cur, SlideShowConstructor::KeyPosition& keyPosition) const +{ + if ((!xmlStrcmp(cur->name, (const xmlChar *)"key"))) + { + return getKeyPositionInner(doc, cur, keyPosition); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"escape")) || + (!xmlStrcmp(cur->name, (const xmlChar *)"esc")) || + (!xmlStrcmp(cur->name, (const xmlChar *)"exit"))) + { + keyPosition.set(osgGA::GUIEventAdapter::KEY_Escape, 0.0f, 0.0f); + return true; + } + return false; +} + +bool ReaderWriterP3DXML::getKeyPositionInner(xmlDocPtr doc, xmlNodePtr cur, SlideShowConstructor::KeyPosition& keyPosition) const +{ + // x in range -1 to 1, from left to right + float x = FLT_MAX; + getProperty(cur, "x", x); + + // y in range -1 to 1, from bottom to top + float y = FLT_MAX; + getProperty(cur, "y", y); + + float h = FLT_MAX; + if (getProperty(cur, "h", h)) + { + // h in range 0.0 to 1, from left to right + x = h*2.0f-1.0f; + } + + float v = FLT_MAX; + if (getProperty(cur, "v", v)) + { + // v in range 0.0 to 1, from bottom to top + y = v*2.0f-1.0f; + } + + xmlChar* key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (key) + { + + unsigned int keyValue = 0; + + StringKeyMap::const_iterator itr=_stringKeyMap.find((const char*)key); + if (itr != _stringKeyMap.end()) + { + keyValue = itr->second; + } + else if (strlen((const char*)key)==1) + { + keyValue = key[0]; + } + else + { + osg::notify(osg::NOTICE)<<"Warning: unreconginized key sequence '"<<(const char*) key<<"'"<xmlChildrenNode; + while (cur != NULL) + { + if ((!xmlStrcmp(cur->name, (const xmlChar *)"run"))) + { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (key) + { + osg::notify(osg::INFO)<<"run ["<<(const char*)key<<"]"<name, (const xmlChar *)"jump"))) + { + osg::notify(osg::NOTICE)<<"Parsed Jump "<name, (const xmlChar *)"click_to_run"))) + { + bool relativeJump = true; + int slideNum = 0; + int layerNum = 0; + getJumpProperties(cur, relativeJump, slideNum, layerNum); + + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (key) + { + osg::notify(osg::INFO)<<"click_to_run ["<<(const char*)key<<"]"<name, (const xmlChar *)"click_to_load"))) + { + bool relativeJump = true; + int slideNum = 0; + int layerNum = 0; + getJumpProperties(cur, relativeJump, slideNum, layerNum); + + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (key) + { + osg::notify(osg::INFO)<<"click_to_load ["<<(const char*)key<<"]"<name, (const xmlChar *)"click_to_event"))) + { + bool relativeJump = true; + int slideNum = 0; + int layerNum = 0; + getJumpProperties(cur, relativeJump, slideNum, layerNum); + + if (getKeyPositionInner(doc, cur, keyPosition)) + { + osg::notify(osg::INFO)<<"click_to_event ["<name, (const xmlChar *)"click_to_jump"))) + { + bool relativeJump = true; + int slideNum = 0; + int layerNum = 0; + getJumpProperties(cur, relativeJump, slideNum, layerNum); + + constructor.layerClickEventOperation(SlideShowConstructor::JUMP, relativeJump, slideNum, layerNum); + } + + else if ((!xmlStrcmp(cur->name, (const xmlChar *)"newline"))) + { + constructor.translateTextCursor(osg::Vec3(0.0f,-0.05f,0.0f)); + } + else if ((!xmlStrcmp(cur->name, (const xmlChar *)"indent"))) + { + float localIndent = 0.05f; + constructor.translateTextCursor(osg::Vec3(localIndent,0.0f,0.0f)); + totalIndent += localIndent; + } + else if ((!xmlStrcmp(cur->name, (const xmlChar *)"unindent"))) + { + float localIndent = -0.05f; + constructor.translateTextCursor(osg::Vec3(localIndent,0.0f,0.0f)); + totalIndent += localIndent; + } + else if ((!xmlStrcmp(cur->name, (const xmlChar *)"bullet"))) + { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (key) + { + osg::notify(osg::INFO)<<"bullet ["<<(const char*)key<<"]"<name, (const xmlChar *)"paragraph"))) + { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (key) + { + SlideShowConstructor::PositionData positionData = constructor.getTextPositionData(); + bool positionRead = getProperties(cur,positionData); + + SlideShowConstructor::FontData fontData = constructor.getTextFontData(); + bool fontRead = getProperties(cur,fontData); + + constructor.addParagraph((const char*)key, + positionRead ? positionData : constructor.getTextPositionData(), + fontRead ? fontData : constructor.getTextFontData()); + } + xmlFree(key); + } + else if ((!xmlStrcmp(cur->name, (const xmlChar *)"image"))) + { + std::string filename; + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (key) + { + SlideShowConstructor::PositionData positionData = constructor.getImagePositionData(); + bool positionRead = getProperties(cur,positionData); + + SlideShowConstructor::ImageData imageData;// = constructor.getImageData(); + getProperties(cur,imageData); + + constructor.addImage((const char*)key, + positionRead ? positionData : constructor.getImagePositionData(), + imageData); + } + } + else if ((!xmlStrcmp(cur->name, (const xmlChar *)"vnc"))) + { + std::string filename; + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (key) + { + SlideShowConstructor::PositionData positionData = constructor.getImagePositionData(); + bool positionRead = getProperties(cur,positionData); + + SlideShowConstructor::ImageData imageData;// = constructor.getImageData(); + getProperties(cur,imageData); + + constructor.addVNC((const char*)key, + positionRead ? positionData : constructor.getImagePositionData(), + imageData); + } + } + else if ((!xmlStrcmp(cur->name, (const xmlChar *)"browser"))) + { + std::string filename; + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (key) + { + SlideShowConstructor::PositionData positionData = constructor.getImagePositionData(); + bool positionRead = getProperties(cur,positionData); + + SlideShowConstructor::ImageData imageData;// = constructor.getImageData(); + getProperties(cur,imageData); + + constructor.addBrowser((const char*)key, + positionRead ? positionData : constructor.getImagePositionData(), + imageData); + } + } + else if ((!xmlStrcmp(cur->name, (const xmlChar *)"pdf"))) + { + std::string filename; + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (key) + { + SlideShowConstructor::PositionData positionData = constructor.getImagePositionData(); + bool positionRead = getProperties(cur,positionData); + + SlideShowConstructor::ImageData imageData;// = constructor.getImageData(); + getProperties(cur,imageData); + + constructor.addPDF((const char*)key, + positionRead ? positionData : constructor.getImagePositionData(), + imageData); + } + } + else if ((!xmlStrcmp(cur->name, (const xmlChar *)"stereo_pair"))) + { + parseStereoPair(constructor, doc,cur); + } + else if ((!xmlStrcmp(cur->name, (const xmlChar *)"model"))) + { + parseModel(constructor, doc,cur); + } + else if ((!xmlStrcmp(cur->name, (const xmlChar *)"volume"))) + { + parseVolume(constructor, doc,cur); + } + else if ((!xmlStrcmp(cur->name, (const xmlChar *)"duration"))) + { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (key) constructor.setLayerDuration(atof((const char*)key)); + xmlFree(key); + } + else if (getKeyPosition(doc, cur, keyPosition)) + { + constructor.addLayerKey(keyPosition); + } + cur = cur->next; + } + + if (totalIndent != 0.0f) + { + constructor.translateTextCursor(osg::Vec3(-totalIndent,0.0f,0.0f)); + } + +} + +void ReaderWriterP3DXML::parseBullets(SlideShowConstructor& constructor, xmlDocPtr doc, xmlNodePtr cur, bool inheritPreviousLayers, bool defineAsBaseLayer) const +{ + xmlChar *key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (key) + { + constructor.addLayer(inheritPreviousLayers, defineAsBaseLayer); + + osg::notify(osg::INFO)<<"bullets ["<<(const char*)key<<"]"<xmlChildrenNode, 1); + if (key) + { + constructor.addLayer(inheritPreviousLayers, defineAsBaseLayer); + + osg::notify(osg::INFO)<<"text ["<<(const char*)key<<"]"<xmlChildrenNode, 1); + if (key) + { + constructor.addSlide(); + + std::string title; + getProperty(cur, "title", title); + + std::string inherit; + getProperty(cur, "inherit", inherit); + + if (!inherit.empty() && _templateMap.count(inherit)!=0) + { + parseSlide(constructor, _templateMap[inherit].first, _templateMap[inherit].second, true, false); + } + + if (!title.empty()) + { + constructor.setSlideTitle(title, + constructor.getTitlePositionData(), + constructor.getTitleFontData()); + } + + if (!inherit.empty() && _templateMap.count(inherit)!=0) + { + parseSlide(constructor, _templateMap[inherit].first, _templateMap[inherit].second, false, true); + } + + constructor.addLayer(true,false); + + SlideShowConstructor::PositionData positionData = constructor.getTextPositionData(); + bool positionRead = getProperties(cur,positionData); + + SlideShowConstructor::FontData fontData = constructor.getTextFontData(); + bool fontRead = getProperties(cur,fontData); + + constructor.addParagraph((const char*)key, + positionRead ? positionData : constructor.getTextPositionData(), + fontRead ? fontData : constructor.getTextFontData()); + } + xmlFree(key); +} + +void ReaderWriterP3DXML::parsePdfDocument(SlideShowConstructor& constructor, xmlDocPtr doc, xmlNodePtr cur) const +{ + xmlChar *key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (key) + { + + std::string title; + getProperty(cur, "title", title); + + std::string inherit; + getProperty(cur, "inherit", inherit); + + constructor.addSlide(); + + if (!inherit.empty() && _templateMap.count(inherit)!=0) + { + parseSlide(constructor, _templateMap[inherit].first, _templateMap[inherit].second, true, false); + } + + if (!title.empty()) + { + constructor.setSlideTitle(title, + constructor.getTitlePositionData(), + constructor.getTitleFontData()); + } + + if (!inherit.empty() && _templateMap.count(inherit)!=0) + { + parseSlide(constructor, _templateMap[inherit].first, _templateMap[inherit].second, false, true); + } + + constructor.addLayer(true,false); + + SlideShowConstructor::PositionData positionData = constructor.getImagePositionData(); + getProperties(cur,positionData); + + SlideShowConstructor::ImageData imageData;// = constructor.getImageData(); + imageData.page = 0; + getProperties(cur,imageData); + + osg::Image* image = constructor.addInteractiveImage((const char*)key, positionData, imageData); + osgWidget::PdfImage* pdfImage = dynamic_cast(image); + if (pdfImage) + { + int numPages = pdfImage->getNumOfPages(); + osg::notify(osg::NOTICE)<<"NumOfPages = "<1) + { + for(int pageNum=1; pageNumxmlChildrenNode; + while (cur != NULL) + { + if (parseTitles) + { + if ((!xmlStrcmp(cur->name, (const xmlChar *)"title"))) + { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + + if (key) + { + SlideShowConstructor::PositionData positionData = constructor.getTitlePositionData(); + bool positionRead = getProperties(cur,positionData); + + SlideShowConstructor::FontData fontData = constructor.getTitleFontData(); + bool fontRead = getProperties(cur,fontData); + + constructor.setSlideTitle((const char*)key, + positionRead ? positionData : constructor.getTitlePositionData(), + fontRead ? fontData : constructor.getTitleFontData()); + + xmlFree(key); + } + else constructor.setSlideTitle("", constructor.getTitlePositionData(), constructor.getTitleFontData()); + } + else if ((!xmlStrcmp(cur->name, (const xmlChar *)"background"))) + { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (key) constructor.setSlideBackground((const char*)key); + else constructor.setSlideBackground(""); + xmlFree(key); + } + else if ((!xmlStrcmp(cur->name, (const xmlChar *)"bgcolor"))) + { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (key) constructor.setBackgroundColor(mapStringToColor((const char*)key),true); + xmlFree(key); + } + else if ((!xmlStrcmp(cur->name, (const xmlChar *)"textcolor"))) + { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (key) constructor.setTextColor(mapStringToColor((const char*)key)); + xmlFree(key); + } + } + if (parseLayers) + { + if ((!xmlStrcmp(cur->name, (const xmlChar *)"base"))) + { + constructor.addLayer(true, true); + parseLayer (constructor, doc, cur); + } + else if ((!xmlStrcmp(cur->name, (const xmlChar *)"layer"))) + { + constructor.addLayer(true, false); + parseLayer (constructor, doc, cur); + } + else if ((!xmlStrcmp(cur->name, (const xmlChar *)"clean_layer"))) + { + constructor.addLayer(false, false); + parseLayer (constructor, doc, cur); + } + else if ((!xmlStrcmp(cur->name, (const xmlChar *)"modify_layer"))) + { + int layerNum; + if (getProperty(cur, "layer", layerNum)) + { + constructor.selectLayer(layerNum); + } + else + { + constructor.addLayer(true, false); + } + + parseLayer (constructor, doc, cur); + } + else if ((!xmlStrcmp(cur->name, (const xmlChar *)"bullets"))) + { + parseBullets (constructor, doc, cur,true, false); + } + else if ((!xmlStrcmp(cur->name, (const xmlChar *)"duration"))) + { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (key) constructor.setSlideDuration(atof((const char*)key)); + xmlFree(key); + } + else if (getKeyPosition(doc, cur, keyPosition)) + { + constructor.addSlideKey(keyPosition); + } + } + cur = cur->next; + } + + constructor.setBackgroundColor(previous_bgcolor,false); + constructor.setTextColor(previous_textcolor); + + return; +} + +osgDB::ReaderWriter::ReadResult ReaderWriterP3DXML::readNode(const std::string& file, + const osgDB::ReaderWriter::Options* options) const +{ + + bool readOnlyHoldingPage = options ? options->getOptionString()=="holding_slide" : false; + + std::string ext = osgDB::getLowerCaseFileExtension(file); + if (!acceptsExtension(ext)) return ReadResult::FILE_NOT_HANDLED; + + std::string fileName = osgDB::findDataFile( file ); + if (fileName.empty()) return ReadResult::FILE_NOT_FOUND; + + // create a keyPosition just in case we need it. + SlideShowConstructor::KeyPosition keyPosition; + + xmlDocPtr doc; + xmlNodePtr cur; + + doc = xmlParseFile(fileName.c_str()); + + if (doc == NULL ) { + fprintf(stderr,"Document not parsed successfully. \n"); + return ReadResult::FILE_NOT_HANDLED; + } + + cur = xmlDocGetRootElement(doc); + + if (cur == NULL) { + fprintf(stderr,"empty document\n"); + xmlFreeDoc(doc); + return ReadResult::FILE_NOT_HANDLED; + } + + if (xmlStrcmp(cur->name, (const xmlChar *) "presentation")) { + fprintf(stderr,"document of the wrong type, root node != presentation"); + xmlFreeDoc(doc); + return ReadResult::FILE_NOT_HANDLED; + } + + SlideShowConstructor constructor; + + osgDB::FilePathList previousPaths = osgDB::getDataFilePathList(); + + bool readSlide = false; + + + xmlChar *key; + cur = cur->xmlChildrenNode; + while (cur != NULL) { + + + if ((!xmlStrcmp(cur->name, (const xmlChar *)"name"))) + { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (key) constructor.setPresentationName((const char*)key); + else constructor.setPresentationName(""); + xmlFree(key); + } + else if ((!xmlStrcmp(cur->name, (const xmlChar *)"loop"))) + { + constructor.setLoopPresentation(true); + } + else if ((!xmlStrcmp(cur->name, (const xmlChar *)"auto"))) + { + constructor.setAutoSteppingActive(true); + } + else if ((!xmlStrcmp(cur->name, (const xmlChar *)"title-settings"))) + { + bool fontRead = getProperties(cur,constructor.getTitleFontDataDefault()); + if (fontRead) + { + osg::notify(osg::INFO)<<"Title font details read"<name, (const xmlChar *)"text-settings"))) + { + bool fontRead = getProperties(cur,constructor.getTextFontDataDefault()); + if (fontRead) + { + osg::notify(osg::INFO)<<"Text font details read"<name, (const xmlChar *)"ratio"))) + { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (key) constructor.setPresentationAspectRatio((const char*)key); + xmlFree(key); + }*/ + else if ((!xmlStrcmp(cur->name, (const xmlChar *)"path"))) + { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (key) + { + osg::notify(osg::INFO)<<"Appending search path "<<(char*)key<name, (const xmlChar *)"bgcolor"))) + { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (key) constructor.setBackgroundColor(mapStringToColor((const char*)key),false); + xmlFree(key); + } + else if ((!xmlStrcmp(cur->name, (const xmlChar *)"textcolor"))) + { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (key) constructor.setTextColor(mapStringToColor((const char*)key)); + xmlFree(key); + } + else if ((!xmlStrcmp(cur->name, (const xmlChar *)"duration"))) + { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (key) constructor.setPresentationDuration(atof((const char*)key)); + xmlFree(key); + } + else if (getKeyPosition(doc, cur, keyPosition)) + { + constructor.addPresentationKey(keyPosition); + } + else if (readOnlyHoldingPage && (!xmlStrcmp(cur->name, (const xmlChar *)"holding_slide"))) + { + readSlide = true; + constructor.addSlide(); + parseSlide (constructor, doc, cur); + } + else if (!readOnlyHoldingPage && (!xmlStrcmp(cur->name, (const xmlChar *)"slide"))) + { + readSlide = true; + constructor.addSlide(); + + std::string inherit; + if (getProperty(cur, "inherit", inherit) && !inherit.empty() && _templateMap.count(inherit)!=0) + { + parseSlide(constructor, _templateMap[inherit].first, _templateMap[inherit].second, true, false); + parseSlide (constructor, doc, cur, true, false); + parseSlide(constructor, _templateMap[inherit].first, _templateMap[inherit].second, false, true); + parseSlide (constructor, doc, cur, false, true); + } + else + { + parseSlide (constructor, doc, cur); + } + } + else if (!readOnlyHoldingPage && (!xmlStrcmp(cur->name, (const xmlChar *)"modify_slide"))) + { + readSlide = true; + int slideNum; + if (getProperty(cur, "slide", slideNum)) + { + constructor.selectSlide(slideNum); + parseSlide (constructor, doc, cur); + } + else + { + constructor.addSlide(); + } + } + else if (!readOnlyHoldingPage && (!xmlStrcmp(cur->name, (const xmlChar *)"page"))) + { + readSlide = true; + parsePage (constructor, doc, cur); + } + else if (!readOnlyHoldingPage && (!xmlStrcmp(cur->name, (const xmlChar *)"pdf_document"))) + { + readSlide = true; + parsePdfDocument(constructor, doc, cur); + } + else if (!readOnlyHoldingPage && (!xmlStrcmp(cur->name, (const xmlChar *)"template_slide"))) + { + readSlide = true; + std::string name; + if (getProperty(cur, "name", name)) + { + _templateMap[name] = DocNodePair(doc,cur); + std::cout<<"Defining template slide "<next; + } + + xmlFreeDoc(doc); + + osgDB::getDataFilePathList() = previousPaths; + + + osg::ref_ptr root = constructor.takePresentation(); + + SlideEventHandler* seh = new SlideEventHandler; + seh->set(root.get()); + root->setEventCallback(seh); + + return root.release(); +} + diff --git a/src/osgPlugins/p3d/SlideEventHandler.cpp b/src/osgPlugins/p3d/SlideEventHandler.cpp new file mode 100644 index 000000000..374b1d914 --- /dev/null +++ b/src/osgPlugins/p3d/SlideEventHandler.cpp @@ -0,0 +1,1308 @@ +/* -*-c++-*- Present3D - Copyright (C) 1999-2006 Robert Osfield + * + * This software is open source and may be redistributed and/or modified under + * the terms of the GNU General Public License (GPL) version 2.0. + * The full license is in LICENSE.txt file included with this distribution,. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * include LICENSE.txt for more details. +*/ + +#include "SlideEventHandler.h" +#include "SlideShowConstructor.h" + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "AnimationMaterial.h" + +#include + + +static osg::observer_ptr s_seh; + +SlideEventHandler* SlideEventHandler::instance() { return s_seh.get(); } + +struct ImageStreamOperator : public ObjectOperator +{ + ImageStreamOperator(osg::ImageStream* imageStream): + _imageStream(imageStream) {} + + virtual void* ptr() const { return _imageStream.get(); } + + virtual void enter() + { + osg::notify(osg::INFO)<<"enter() : _imageStream->rewind() + play"<pause()"<pause(); + } + + virtual void setPause(bool pause) + { + osg::notify(osg::INFO)<<"_imageStream->setPause("<pause(); + else _imageStream->play(); + } + + virtual void reset() + { + osg::ImageStream::StreamStatus previousStatus = _imageStream->getStatus(); + + _imageStream->rewind(); + + + //_imageStream->setVolume(previousVolume); + + if(previousStatus==osg::ImageStream::PLAYING) + { + _imageStream->play(); + } + + // add a delay so that movie thread has a chance to do the rewind + float microSecondsToDelay = SlideEventHandler::instance()->getTimeDelayOnNewSlideWithMovies() * 1000000.0f; + OpenThreads::Thread::microSleep(static_cast(microSecondsToDelay)); + } + + osg::ref_ptr _imageStream; +}; + +struct CallbackOperator : public ObjectOperator +{ + CallbackOperator(osg::Node* node, osg::Referenced* callback): + _node(node), + _callback(callback) {} + + virtual void* ptr() const { return _callback.get(); } + + virtual void enter() + { + reset(); + } + + virtual void maintain() + { + } + + virtual void leave() + { + } + + virtual void setPause(bool pause) + { + osg::AnimationPathCallback* apc = dynamic_cast(_callback.get()); + osgUtil::TransformCallback* tc = dynamic_cast(_callback.get()); + ss3d::AnimationMaterialCallback* amc = dynamic_cast(_callback.get()); + if (apc) + { + osg::notify(osg::INFO)<<"apc->setPause("<setPause(pause); + } + if (tc) + { + osg::notify(osg::INFO)<<"tc->setPause("<setPause(pause); + } + if (amc) + { + osg::notify(osg::INFO)<<"amc->setPause("<setPause(pause); + } + } + + virtual void reset() + { + osg::AnimationPathCallback* apc = dynamic_cast(_callback.get()); + osgUtil::TransformCallback* tc = dynamic_cast(_callback.get()); + ss3d::AnimationMaterialCallback* amc = dynamic_cast(_callback.get()); + if (apc) + { + apc->reset(); + apc->update(*_node); + } + if (tc) + { + } + if (amc) + { + amc->reset(); + amc->update(*_node); + } + } + + + osg::ref_ptr _node; + osg::ref_ptr _callback; +}; + +struct LayerAttributesOperator : public ObjectOperator +{ + LayerAttributesOperator(osg::Node* node, SlideShowConstructor::LayerAttributes* la): + _node(node), + _layerAttribute(la) + { + } + + virtual void* ptr() const { return _layerAttribute.get(); } + + virtual void enter() + { + _layerAttribute->callEnterCallbacks(_node.get()); + + if (!_layerAttribute->_keys.empty()) + { + osg::notify(osg::INFO)<<"applyKeys {"<_keys.begin(); + itr != _layerAttribute->_keys.end(); + ++itr) + { + SlideEventHandler::instance()->dispatchEvent(*itr); + } + + osg::notify(osg::INFO)<<"}"<_runStrings.empty()) + { + for(SlideShowConstructor::LayerAttributes::RunStrings::iterator itr = _layerAttribute->_runStrings.begin(); + itr != _layerAttribute->_runStrings.end(); + ++itr) + { + + osg::notify(osg::NOTICE)<<"Run "<c_str()<tick(); + + int result = system(itr->c_str()); + + osg::notify(osg::INFO)<<"system("<<*itr<<") result "<delta_s(startTick, osg::Timer::instance()->tick()); + + osgGA::EventQueue* eq = SlideEventHandler::instance()->getViewer()->getEventQueue(); + if (eq) + { + osg::Timer_t new_startTick = eq->getStartTick() + osg::Timer_t(timeForRun / osg::Timer::instance()->getSecondsPerTick()); + eq->setStartTick(new_startTick); + } + } + } + + } + + virtual void maintain() + { + } + + virtual void leave() + { + osg::notify(osg::INFO)<<"LayerAttribute leave"<callLeaveCallbacks(_node.get()); + } + + virtual void setPause(bool pause) + { + } + + virtual void reset() + { + } + + + osg::ref_ptr _node; + osg::ref_ptr _layerAttribute; +}; + + +class FindOperatorsVisitor : public osg::NodeVisitor +{ +public: + FindOperatorsVisitor(ActiveOperators::OperatorList& operatorList, osg::NodeVisitor::TraversalMode tm): + osg::NodeVisitor(tm), + _operatorList(operatorList) {} + + void apply(osg::Node& node) + { + if (node.getStateSet()) process(node.getStateSet()); + + if (node.getUpdateCallback()) + { + _operatorList.insert(new CallbackOperator(&node, node.getUpdateCallback())); + } + + SlideShowConstructor::LayerAttributes* la = dynamic_cast(node.getUserData()); + if (la) + { + _operatorList.insert(new LayerAttributesOperator(&node, la)); + } + + traverse(node); + } + + void apply(osg::Geode& node) + { + apply((osg::Node&)node); + + for(unsigned int i=0;igetStateSet()) process(drawable->getStateSet()); + } + } + + virtual void process(osg::StateSet* ss) + { + for(unsigned int i=0;igetTextureAttributeList().size();++i) + { + osg::Texture* texture = dynamic_cast(ss->getTextureAttribute(i,osg::StateAttribute::TEXTURE)); + osg::Image* image = texture ? texture->getImage(0) : 0; + osg::ImageStream* imageStream = image ? dynamic_cast(image) : 0; + if (imageStream) + { + _operatorList.insert(new ImageStreamOperator(imageStream)); + } + } + } + + ActiveOperators::OperatorList& _operatorList; +}; + + +ActiveOperators::ActiveOperators(): + _pause(false) +{ +} + +ActiveOperators::~ActiveOperators() +{ +} + +void ActiveOperators::collect(osg::Node* incommingNode, osg::NodeVisitor::TraversalMode tm) +{ + _previous.swap(_current); + + _current.clear(); + + FindOperatorsVisitor fov(_current, tm); + incommingNode->accept(fov); + + osg::notify(osg::INFO)<<"ActiveOperators::collect("<filePathList); + } + + traverse(node); + } + +}; + +class UpdateLightVisitor : public osg::NodeVisitor +{ +public: + + UpdateLightVisitor(const osg::Matrixd& viewMatrix, float currentX, float currentY): + osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ACTIVE_CHILDREN), + _viewMatrix(viewMatrix), + _currentX(currentX), _currentY(currentY) {} + + void apply(osg::Node& node) + { + if (node.getStateSet()) + { + apply(*node.getStateSet()); + } + + traverse(node); + } + + void apply(osg::LightSource& lightsource) + { + if (lightsource.getStateSet()) + { + apply(*lightsource.getStateSet()); + } + + if (lightsource.getLight()) + { + osg::notify(osg::INFO)<<"Adjusting light"<setPosition(osg::Vec4(direction,0.0f)); + + } + + traverse(lightsource); + } + + void apply(osg::StateSet& stateset) + { + osg::TexEnvCombine* texenvcombine = dynamic_cast(stateset.getTextureAttribute(0,osg::StateAttribute::TEXENV)); + if (texenvcombine) + { + apply(*texenvcombine); + } + } + + void apply(osg::TexEnvCombine& texenv) + { + osg::notify(osg::INFO)<<"Adjusting tex env combine"<(stateset.getAttribute(osg::StateAttribute::ALPHAFUNC)); + if (alphaFunc) + { + osg::notify(osg::INFO)<<"Adjusting alpha func"<getReferenceValue(); + alpha = osg::clampBetween((1.0f-_currentY)*0.5f,0.0f,1.0f); + + alphaFunc->setReferenceValue(alpha); + } + } + + if (_modMaterial) + { + osg::Material* material = dynamic_cast(stateset.getAttribute(osg::StateAttribute::MATERIAL)); + if (material) + { + osg::notify(osg::INFO)<<"Adjusting material func"<setAlpha(osg::Material::FRONT_AND_BACK,alpha); + } + } + } + + bool _modAlphaFunc, _modMaterial; + float _currentX, _currentY; + +}; + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// SlideEventHandler +// +SlideEventHandler::SlideEventHandler(osgViewer::Viewer* viewer): + _viewer(viewer), + _presentationSwitch(0), + _activeSlide(0), + _slideSwitch(0), + _activeLayer(0), + _firstTraversal(true), + _previousTime(-1.0f), + _timePerSlide(1.0), + _autoSteppingActive(false), + _loopPresentation(false), + _pause(false), + _hold(false), + _updateLightActive(false), + _updateOpacityActive(false), + _previousX(0), _previousY(0), + _cursorOn(true), + _releaseAndCompileOnEachNewSlide(true), + _firstSlideOrLayerChange(true), + _tickAtFirstSlideOrLayerChange(0), + _tickAtLastSlideOrLayerChange(0), + _timeDelayOnNewSlideWithMovies(0.25f), + _minimumTimeBetweenKeyPresses(0.25), + _timeLastKeyPresses(-1.0) +{ + s_seh = this; +} + +double SlideEventHandler::getDuration(const osg::Node* node) const +{ + const SlideShowConstructor::LayerAttributes* la = dynamic_cast(node->getUserData()); + return la ? la->_duration : -1.0; +} + +void SlideEventHandler::set(osg::Node* model) +{ +#if 0 + // pause all slides, then just reenable the current slide. + ActivityUpdateCallbacksVisitor aucv(ALL_OBJECTS, true); + aucv.setTraversalMode(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN); + model->accept(aucv); +#endif + + ActiveOperators operators; + operators.collect(model, osg::NodeVisitor::TRAVERSE_ALL_CHILDREN); + operators.setPause(true); + + FindNamedSwitchVisitor findPresentation("Presentation"); + model->accept(findPresentation); + + if (findPresentation._switch) + { + osg::notify(osg::INFO)<<"Presentation '"<getName()<<"'"<=0.0) + { + osg::notify(osg::INFO)<<"Presentation time set to "<accept(findSlide); + + if (findSlide._switch) + { + osg::notify(osg::INFO)<<"Found presentation slide"<getName()<getNumChildren()) + { + duration = getDuration(_slideSwitch->getChild(_activeLayer)); + } + + if (duration < 0.0) + { + duration = getDuration(_slideSwitch.get()); + } + + if (duration >=0 ) + { + return duration; + } + } + + return _timePerSlide; +} + + +bool SlideEventHandler::handle(const osgGA::GUIEventAdapter& ea,osgGA::GUIActionAdapter& aa) +{ + + if (!_viewer) + { + _viewer = dynamic_cast(&aa); + selectSlide(0); + home(); + osg::notify(osg::NOTICE)<<"Assigned viewer. to SlideEventHandler"<=getCurrentTimeDelayBetweenSlides()) + { + // _previousTime = time; + + if (!_hold) + { + // increment the previous by the required time delay, note relative to the current + // to keep the time relative to an absolute time signal, thus avoid drift of timing. + _previousTime += getCurrentTimeDelayBetweenSlides(); + + nextLayerOrSlide(); + } + else + { + // we're holding of the move to next layer to slide, but we need slip the time forward accordingly + // componensate for the extra time that this frame is recieving. + _previousTime = time-getCurrentTimeDelayBetweenSlides(); + } + } + } + return false; + } + + case(osgGA::GUIEventAdapter::KEYDOWN): + { + double time = ea.time(); + double deltaTime = time - _timeLastKeyPresses; + if (deltaTime < _minimumTimeBetweenKeyPresses) + { + break; + } + + _timeLastKeyPresses = time; + + if (ea.getKey()=='a') + { + if (!_autoSteppingActive) + { + _autoSteppingActive = true; + _previousTime = ea.time(); + } + return true; + } + else if (ea.getKey()=='q') + { + if (_autoSteppingActive) + { + _autoSteppingActive = false; + _previousTime = ea.time(); + } + return true; + } + else if (ea.getKey()==osgGA::GUIEventAdapter::KEY_Home || + ea.getKey()==osgGA::GUIEventAdapter::KEY_KP_Home) + { + _autoSteppingActive = false; + selectSlide(0); + home(ea,aa); + return true; + } + else if (ea.getKey()==osgGA::GUIEventAdapter::KEY_End || + ea.getKey()==osgGA::GUIEventAdapter::KEY_KP_End) + { + _autoSteppingActive = false; + selectSlide(LAST_POSITION,LAST_POSITION); + home(ea,aa); + return true; + } + else if (ea.getKey()==osgGA::GUIEventAdapter::KEY_Down || + ea.getKey()==osgGA::GUIEventAdapter::KEY_KP_Down) + { + _autoSteppingActive = false; + nextLayer(); + return true; + } + else if (ea.getKey()=='n') + { + _autoSteppingActive = false; + nextLayerOrSlide(); + return true; + } + else if (ea.getKey()==osgGA::GUIEventAdapter::KEY_Up || + ea.getKey()==osgGA::GUIEventAdapter::KEY_KP_Up) + { + _autoSteppingActive = false; + previousLayer(); + return true; + } + else if (ea.getKey()==osgGA::GUIEventAdapter::KEY_Page_Down || + ea.getKey()==osgGA::GUIEventAdapter::KEY_KP_Page_Down) + { + _autoSteppingActive = false; + nextLayerOrSlide(); + return true; + } + else if (ea.getKey()==osgGA::GUIEventAdapter::KEY_Page_Up || + ea.getKey()==osgGA::GUIEventAdapter::KEY_KP_Page_Up) + { + _autoSteppingActive = false; + previousLayerOrSlide(); + return true; + } + else if (ea.getKey()=='N' || + ea.getKey()==osgGA::GUIEventAdapter::KEY_Right || + ea.getKey()==osgGA::GUIEventAdapter::KEY_KP_Right) + { + _autoSteppingActive = false; + nextSlide(); + home(ea,aa); + return true; + } + else if (ea.getKey()==osgGA::GUIEventAdapter::KEY_Left || + ea.getKey()==osgGA::GUIEventAdapter::KEY_KP_Left) + { + _autoSteppingActive = false; + previousSlide(); + home(ea,aa); + return true; + } + else if (ea.getKey()=='p') + { + if (!_pause) + { + _pause = true; +#if 0 + resetUpdateCallbackActivity(ALL_OBJECTS); +#endif + _activeOperators.setPause(_pause); + } + + return true; + + } + else if (ea.getKey()=='o') + { + if (_pause) + { + _pause = false; +#if 0 + resetUpdateCallbackActivity(ALL_OBJECTS); +#endif + _activeOperators.setPause(_pause); + } + return true; + + } + else if (ea.getKey()=='h') + { + _hold = true; + return true; + } + else if (ea.getKey()=='r') + { +#if 0 + resetUpdateCallbacks(ALL_OBJECTS); +#endif + _activeOperators.reset(); + return true; + } +/* + else if (ea.getKey()=='c') + { + _cursorOn = !_cursorOn; + + for( unsigned int i = 0; i < _viewer->getCameraConfig()->getNumberOfCameras(); i++ ) + { + Producer::Camera* cam = _viewer->getCameraConfig()->getCamera(i); + Producer::RenderSurface* rs = cam->getRenderSurface(); + rs->useCursor(_cursorOn); + } + + return true; + } +*/ + else if (ea.getKey()=='u') + { + updateAlpha(true,false,ea.getX(),ea.getY()); + return true; + } + else if (ea.getKey()=='i') + { + updateAlpha(false,true,ea.getX(),ea.getY()); + return true; + } + else if (ea.getKey()=='k') + { + updateLight(ea.getX(),ea.getY()); + return true; + } + + return false; + } + case(osgGA::GUIEventAdapter::KEYUP): + { + if (ea.getKey()=='h') + { + _hold = false; + return true; + } + return false; + } + default: + return false; + } + return false; +} + +void SlideEventHandler::getUsage(osg::ApplicationUsage& usage) const +{ + usage.addKeyboardMouseBinding("a","Toggle on/off the automatic advancement for image to image"); + usage.addKeyboardMouseBinding("n","Advance to next layer or slide"); + usage.addKeyboardMouseBinding("p","Move to previous layer or slide"); +} + +unsigned int SlideEventHandler::getNumSlides() +{ + if (_presentationSwitch.valid()) return _presentationSwitch->getNumChildren(); + else return 0; +} + + +bool SlideEventHandler::selectSlide(unsigned int slideNum,unsigned int layerNum) +{ + if (!_presentationSwitch) return false; + + osg::notify(osg::INFO)<<"selectSlide("<getNumChildren()>0) + { + slideNum = _presentationSwitch->getNumChildren()-1; + } + + if (slideNum>=_presentationSwitch->getNumChildren()) return false; + + + osg::Timer_t tick = osg::Timer::instance()->tick(); + + if (_firstSlideOrLayerChange) + { + _firstSlideOrLayerChange = false; + _tickAtFirstSlideOrLayerChange = tick; + _tickAtLastSlideOrLayerChange = tick; + } + + osg::notify(osg::INFO)<<"selectSlide("<delta_s(_tickAtFirstSlideOrLayerChange, tick)<<" seconds, length ="<delta_s(_tickAtLastSlideOrLayerChange, tick)<<" seconds"<setSingleChildOn(_activeSlide); + + //osg::notify(osg::INFO)<<"Selected slide '"<<_presentationSwitch->getChild(_activeSlide)->getName()<<"'"<getChild(_activeSlide)->accept(findSlide); + + bool result = false; + if (findSlide._switch) + { + //osg::notify(osg::INFO)<<"Found slide '"<getName()<<"'"<getKeySwitchMatrixManipulator()->setMinimumDistance(0.001); + + _viewer->getCameraManipulator()->setNode(_slideSwitch.get()); + + _viewer->computeActiveCoordinateSystemNodePath(); + + // resetUpdateCallbacks(ALL_OBJECTS); + + bool _useSlideFilePaths = false; + if (_useSlideFilePaths) + { + // set up the file paths + FindFilePathDataVisitor ffpdv; + _presentationSwitch->accept(ffpdv); + } + + if (newSlide && _releaseAndCompileOnEachNewSlide) + { + compileSlide(slideNum); + } + + return result; + +} + +bool SlideEventHandler::selectLayer(unsigned int layerNum) +{ + if (!_slideSwitch) return false; + + if (layerNum==LAST_POSITION && _slideSwitch->getNumChildren()>0) + { + layerNum = _slideSwitch->getNumChildren()-1; + } + + if (layerNum>=_slideSwitch->getNumChildren()) return false; + + _activeLayer = layerNum; + _slideSwitch->setSingleChildOn(_activeLayer); + + updateOperators(); + + osg::notify(osg::INFO)<<"Selected layer '"<<_slideSwitch->getChild(_activeLayer)->getName()<<"' num="<<_activeLayer<< std::endl; + + return true; +} + +bool SlideEventHandler::nextLayerOrSlide() +{ + if (nextLayer()) return true; + else return nextSlide(); +} + +bool SlideEventHandler::previousLayerOrSlide() +{ + if (previousLayer()) return true; + else return previousSlide(); +} + +bool SlideEventHandler::nextSlide() +{ + SlideShowConstructor::LayerAttributes* la = _slideSwitch.valid() ? dynamic_cast(_slideSwitch->getUserData()) : 0; + if (la && la->requiresJump()) + { + if (la->getRelativeJump()) + { + int previousSlide = getActiveSlide(); + int previousLayer = getActiveLayer(); + int newSlide = previousSlide + la->getSlideNum(); + int newLayer = previousLayer + la->getLayerNum(); + if (newLayer<0) + { + newLayer = 0; + } + + return selectSlide(newSlide, newLayer); + } + else + { + return selectSlide(la->getSlideNum(),la->getLayerNum()); + } + } + + if (selectSlide(_activeSlide+1)) return true; + else if (_loopPresentation) return selectSlide(0); + else return false; +} + +bool SlideEventHandler::previousSlide() +{ +#if 1 + // start position when doing previous slide set to top of slide + if (_activeSlide>0) return selectSlide(_activeSlide-1); + else if (_loopPresentation && _presentationSwitch.valid()) return selectSlide(_presentationSwitch->getNumChildren()-1); + else return false; +#else + // start position when doing previous slide set to end of slide + if (_activeSlide>0) return selectSlide(_activeSlide-1,LAST_POSITION); + else if (_loopPresentation && _presentationSwitch.valid()) return selectSlide(_presentationSwitch->getNumChildren()-1,LAST_POSITION); + else return false; +#endif +} + +bool SlideEventHandler::nextLayer() +{ + SlideShowConstructor::LayerAttributes* la = (_slideSwitch.valid() && _activeLayer>=0) ? dynamic_cast(_slideSwitch->getChild(_activeLayer)->getUserData()) : 0; + if (la) + { + la->callLeaveCallbacks(_slideSwitch->getChild(_activeLayer)); + + if (la->requiresJump()) + { + if (la->getRelativeJump()) + { + int previousSlide = getActiveSlide(); + int previousLayer = getActiveLayer(); + int newSlide = previousSlide + la->getSlideNum(); + int newLayer = previousLayer + la->getLayerNum(); + if (newLayer<0) + { + newLayer = 0; + } + + return selectSlide(newSlide, newLayer); + } + else + { + return selectSlide(la->getSlideNum(),la->getLayerNum()); + } + } + } + + return selectLayer(_activeLayer+1); +} + +bool SlideEventHandler::previousLayer() +{ + if (_activeLayer>0) return selectLayer(_activeLayer-1); + else return false; +} + + +void SlideEventHandler::updateOperators() +{ + _activeOperators.collect(_slideSwitch.get()); + _activeOperators.process(); + + if (_viewer.valid()) + { + UpdateLightVisitor uav(_viewer->getCamera()->getViewMatrix(),0.0f,0.0f); + _viewer->getSceneData()->accept(uav); + } +} + +bool SlideEventHandler::home(const osgGA::GUIEventAdapter& ea,osgGA::GUIActionAdapter& aa) +{ + FindHomePositionVisitor fhpv; + osg::Node* node = _viewer->getSceneData(); + if (node) node->accept(fhpv); + + if (fhpv._homePosition.valid()) + { + osg::notify(osg::INFO)<<"Doing home for stored home position."<getCameraManipulator()->setAutoComputeHomePosition(false); + _viewer->getCameraManipulator()->setHomePosition( + fhpv._homePosition->eye, + fhpv._homePosition->center, + fhpv._homePosition->up); + } + else + { + _viewer->getCameraManipulator()->setAutoComputeHomePosition(true); + } + _viewer->getCameraManipulator()->home(ea,aa); + + return true; +} + +bool SlideEventHandler::home() +{ + osg::ref_ptr ea = new osgGA::GUIEventAdapter; + ea->setEventType(osgGA::GUIEventAdapter::FRAME); + ea->setTime(_viewer->getEventQueue()->getTime()); + + home(*ea,*_viewer); + return true; +} + +void SlideEventHandler::updateAlpha(bool modAlphaFunc, bool modMaterial, float x, float y) +{ + osg::notify(osg::INFO)<<"updateAlpha("<accept(uav); + else if (_viewer->getSceneData()) _viewer->getSceneData()->accept(uav); +} + + +void SlideEventHandler::updateLight(float x, float y) +{ + osg::notify(osg::INFO)<<"updateLight("<getCamera()->getViewMatrix(),x,y); + _viewer->getSceneData()->accept(uav); +} + +void SlideEventHandler::compileSlide(unsigned int slideNum) +{ + if (!_compileSlideCallback) + { + _compileSlideCallback = new ss3d::CompileSlideCallback(); + + osgViewer::Viewer::Cameras cameras; + _viewer->getCameras(cameras); + + for(osgViewer::Viewer::Cameras::iterator itr = cameras.begin(); + itr != cameras.end(); + ++itr) + { + (*itr)->setPreDrawCallback(_compileSlideCallback.get()); + } + + } + + _compileSlideCallback->needCompile(_presentationSwitch->getChild(slideNum)); + +} + +void SlideEventHandler::releaseSlide(unsigned int slideNum) +{ + osgUtil::GLObjectsVisitor globjVisitor(osgUtil::GLObjectsVisitor::RELEASE_DISPLAY_LISTS| + osgUtil::GLObjectsVisitor::RELEASE_STATE_ATTRIBUTES); + globjVisitor.setNodeMaskOverride(0xffffffff); + + _presentationSwitch->getChild(slideNum)->accept(globjVisitor); +} + +void SlideEventHandler::dispatchEvent(const SlideShowConstructor::KeyPosition& keyPosition) +{ + osg::notify(osg::INFO)<<" keyPosition._key "<getEventQueue(); + + // reset the time of the last key press to ensure thatthe event is disgarded as a key repeat. + _timeLastKeyPresses = -1.0; + + if (keyPosition._x!=FLT_MAX) + { + float xRescaled = eq->getCurrentEventState()->getXmin() + (keyPosition._x+1.0f)*0.5f*(eq->getCurrentEventState()->getXmax()-eq->getCurrentEventState()->getXmin()); + eq->getCurrentEventState()->setX(xRescaled); + } + + if (keyPosition._y!=FLT_MAX) + { + float yRescaled = eq->getCurrentEventState()->getYmin() + (keyPosition._y+1.0f)*0.5f*(eq->getCurrentEventState()->getXmax()-eq->getCurrentEventState()->getYmin()); + eq->getCurrentEventState()->setY(yRescaled); + } + + eq->keyPress(keyPosition._key); + eq->keyRelease(keyPosition._key); +} + + + diff --git a/src/osgPlugins/p3d/SlideEventHandler.h b/src/osgPlugins/p3d/SlideEventHandler.h new file mode 100644 index 000000000..416427e11 --- /dev/null +++ b/src/osgPlugins/p3d/SlideEventHandler.h @@ -0,0 +1,215 @@ +/* -*-c++-*- Present3D - Copyright (C) 1999-2006 Robert Osfield + * + * This software is open source and may be redistributed and/or modified under + * the terms of the GNU General Public License (GPL) version 2.0. + * The full license is in LICENSE.txt file included with this distribution,. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * include LICENSE.txt for more details. +*/ + +#ifndef SLIDEEVENTHANDLER +#define SLIDEEVENTHANDLER 1 + +#include +#include + +#include +#include + +#include "CompileSlideCallback.h" +#include "SlideShowConstructor.h" + + +struct dereference_less +{ + template + inline bool operator() (const T& lhs,const U& rhs) const + { + return *lhs < *rhs; + } +}; + +struct ObjectOperator : public osg::Referenced +{ + inline bool operator < (const ObjectOperator& rhs) const { return ptr() < rhs.ptr(); } + + virtual void* ptr() const = 0; + + virtual void enter() = 0; + virtual void maintain() = 0; + virtual void leave() = 0; + virtual void setPause(bool pause) = 0; + virtual void reset() = 0; + + virtual ~ObjectOperator() {} +}; + +class ActiveOperators +{ +public: + ActiveOperators(); + ~ActiveOperators(); + + void collect(osg::Node* incommingNode, osg::NodeVisitor::TraversalMode tm = osg::NodeVisitor::TRAVERSE_ACTIVE_CHILDREN); + + void process(); + + void setPause(bool pause); + bool getPause() const { return _pause; } + + void reset(); + + typedef std::set< osg::ref_ptr, dereference_less > OperatorList; + +protected: + + void processOutgoing(); + void processIncomming(); + void processMaintained(); + + bool _pause; + + OperatorList _previous; + OperatorList _current; + + OperatorList _outgoing; + OperatorList _incomming; + OperatorList _maintained; + +}; + +class SlideEventHandler : public osgGA::GUIEventHandler +{ +public: + + SlideEventHandler(osgViewer::Viewer* viewer=0); + + static SlideEventHandler* instance(); + + META_Object(osgslideshowApp,SlideEventHandler); + + void set(osg::Node* model); + + virtual void accept(osgGA::GUIEventHandlerVisitor& v) { v.visit(*this); } + + virtual bool handle(const osgGA::GUIEventAdapter& ea,osgGA::GUIActionAdapter&); + + virtual void getUsage(osg::ApplicationUsage& usage) const; + + osgViewer::Viewer* getViewer() { return _viewer.get(); } + + enum WhichPosition + { + FIRST_POSITION = 0, + LAST_POSITION = 0xffffffff + }; + + void compileSlide(unsigned int slideNum); + void releaseSlide(unsigned int slideNum); + + unsigned int getNumSlides(); + + unsigned int getActiveSlide() const { return _activeSlide; } + unsigned int getActiveLayer() const { return _activeLayer; } + + bool selectSlide(unsigned int slideNum,unsigned int layerNum=FIRST_POSITION); + bool selectLayer(unsigned int layerNum); + + bool nextLayerOrSlide(); + bool previousLayerOrSlide(); + + bool nextSlide(); + bool previousSlide(); + + bool nextLayer(); + bool previousLayer(); + + bool home(); + + void setAutoSteppingActive(bool flag = true) { _autoSteppingActive = flag; } + bool getAutoSteppingActive() const { return _autoSteppingActive; } + + void setTimeDelayBetweenSlides(double dt) { _timePerSlide = dt; } + double getTimeDelayBetweenSlides() const { return _timePerSlide; } + + double getDuration(const osg::Node* node) const; + + double getCurrentTimeDelayBetweenSlides() const; + + void setReleaseAndCompileOnEachNewSlide(bool flag) { _releaseAndCompileOnEachNewSlide = flag; } + bool getReleaseAndCompileOnEachNewSlide() const { return _releaseAndCompileOnEachNewSlide; } + + void setTimeDelayOnNewSlideWithMovies(float t) { _timeDelayOnNewSlideWithMovies = t; } + float getTimeDelayOnNewSlideWithMovies() const { return _timeDelayOnNewSlideWithMovies; } + + void setLoopPresentation(bool loop) { _loopPresentation = loop; } + bool getLoopPresentation() const { return _loopPresentation; } + + void dispatchEvent(const SlideShowConstructor::KeyPosition& keyPosition); + + enum ObjectMask + { + MOVIE = 1<<0, + OBJECTS = 1<<1, + ALL_OBJECTS = MOVIE | OBJECTS + }; + +protected: + + ~SlideEventHandler() {} + SlideEventHandler(const SlideEventHandler&,const osg::CopyOp&) {} + + bool home(const osgGA::GUIEventAdapter& ea,osgGA::GUIActionAdapter& aa); + + void updateAlpha(bool, bool, float x, float y); + void updateLight(float x, float y); + + + osg::observer_ptr _viewer; + + osg::observer_ptr _showSwitch; + unsigned int _activePresentation; + + osg::observer_ptr _presentationSwitch; + unsigned int _activeSlide; + + osg::observer_ptr _slideSwitch; + unsigned int _activeLayer; + + bool _firstTraversal; + double _previousTime; + double _timePerSlide; + bool _autoSteppingActive; + bool _loopPresentation; + bool _pause; + bool _hold; + + bool _updateLightActive; + bool _updateOpacityActive; + float _previousX, _previousY; + + bool _cursorOn; + + bool _releaseAndCompileOnEachNewSlide; + + bool _firstSlideOrLayerChange; + osg::Timer_t _tickAtFirstSlideOrLayerChange; + osg::Timer_t _tickAtLastSlideOrLayerChange; + + float _timeDelayOnNewSlideWithMovies; + + double _minimumTimeBetweenKeyPresses; + double _timeLastKeyPresses; + + ActiveOperators _activeOperators; + + osg::ref_ptr _compileSlideCallback; + + void updateOperators(); + +}; + +#endif diff --git a/src/osgPlugins/p3d/SlideShowConstructor.cpp b/src/osgPlugins/p3d/SlideShowConstructor.cpp new file mode 100644 index 000000000..2a797c4a7 --- /dev/null +++ b/src/osgPlugins/p3d/SlideShowConstructor.cpp @@ -0,0 +1,1803 @@ +/* -*-c++-*- Present3D - Copyright (C) 1999-2006 Robert Osfield + * + * This software is open source and may be redistributed and/or modified under + * the terms of the GNU General Public License (GPL) version 2.0. + * The full license is in LICENSE.txt file included with this distribution,. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * include LICENSE.txt for more details. +*/ + +#include "SlideShowConstructor.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include + +#include + +#include + +#include + +#include +#include +#include + +#include +#include + +#include "AnimationMaterial.h" +#include "PickEventHandler.h" + +class SetToTransparentBin : public osg::NodeVisitor +{ +public: + + SetToTransparentBin(): + osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN) {} + + virtual void appply(osg::Node& node) + { + if (node.getStateSet()) + { + node.getStateSet()->setMode(GL_BLEND,osg::StateAttribute::ON); + node.getStateSet()->setRenderingHint(osg::StateSet::TRANSPARENT_BIN); + } + } + + virtual void apply(osg::Geode& geode) + { + if (geode.getStateSet()) + { + geode.getStateSet()->setMode(GL_BLEND,osg::StateAttribute::ON); + geode.getStateSet()->setRenderingHint(osg::StateSet::TRANSPARENT_BIN); + } + for(unsigned int i=0;igetStateSet()) + { + geode.getDrawable(i)->getStateSet()->setMode(GL_BLEND,osg::StateAttribute::ON); + geode.getDrawable(i)->getStateSet()->setRenderingHint(osg::StateSet::TRANSPARENT_BIN); + } + } + } +}; + +void SlideShowConstructor::LayerAttributes::callEnterCallbacks(osg::Node* node) +{ + osg::notify(osg::INFO)<<"SlideShowConstructor::LayerAttributes::callEnterCallbacks("<getScreenDistance(); + _slideHeight = osg::DisplaySettings::instance()->getScreenHeight(); + _slideWidth = osg::DisplaySettings::instance()->getScreenWidth(); + + _backgroundColor.set(0.0f,0.0f,0.0f,1.0f); + + _presentationDuration = -1.0; + + // set up title defaults + _titleFontDataDefault.font = "fonts/arial.ttf"; + _titleFontDataDefault.color.set(1.0f,1.0f,1.0f,1.0f); + _titleFontDataDefault.layout = osgText::Text::LEFT_TO_RIGHT; + _titleFontDataDefault.alignment = osgText::Text::CENTER_BASE_LINE; + _titleFontDataDefault.axisAlignment = osgText::Text::XZ_PLANE; + _titleFontDataDefault.characterSize = 0.06f; + _titleFontDataDefault.maximumWidth = 0.9f; + + _titlePositionDataDefault.position.set(0.5f,0.92f,0.0f); + + // set up text defaults + _textFontDataDefault.font = "fonts/arial.ttf"; + _textFontDataDefault.color.set(1.0f,1.0f,1.0f,1.0f); + _textFontDataDefault.layout = osgText::Text::LEFT_TO_RIGHT; + _textFontDataDefault.alignment = osgText::Text::LEFT_BASE_LINE; + _textFontDataDefault.axisAlignment = osgText::Text::XZ_PLANE; + _textFontDataDefault.characterSize = 0.04f; + _textFontDataDefault.maximumWidth = 0.8f; + + _textPositionDataDefault.position.set(0.1f,0.85f,0.0f); + + _loopPresentation = false; + _autoSteppingActive = false; +} + +void SlideShowConstructor::setPresentationAspectRatio(float aspectRatio) +{ + _slideWidth = _slideHeight*aspectRatio; +} + +void SlideShowConstructor::setPresentationAspectRatio(const std::string& str) +{ + if (str=="Reality Theatre") setPresentationAspectRatio(3.0f); + else if (str=="Desktop") setPresentationAspectRatio(1280.0f/1024.0f); + else + { + float ratio = (float)atof(str.c_str()); + if (ratio!=0.0) setPresentationAspectRatio(1280.0f/1024.0f); + else + { + osg::notify(osg::WARN)<<"Error: presentation aspect ratio incorrect type"<addChild(_presentationSwitch.get()); + + osg::Vec3 slideCenter = _slideOrigin + osg::Vec3(_slideWidth*0.5f,0.0f,_slideHeight*0.5f); + + HomePosition* hp = new HomePosition; + hp->eye.set(0.0f,0.0f,0.0f); + hp->center = slideCenter; + hp->up.set(0.0f,0.0f,1.0f); + + osg::notify(osg::INFO)<<" slideCenter "<=0.0) + { + setDuration(_presentationSwitch.get(),_presentationDuration); + } + + _root->setUserData(hp); + + if (_loopPresentation) _root->addDescription("loop"); + if (_autoSteppingActive) _root->addDescription("auto"); +} + +SlideShowConstructor::LayerAttributes* SlideShowConstructor::getOrCreateLayerAttributes(osg::Node* node) +{ + LayerAttributes* la = dynamic_cast(node->getUserData()); + if (!la) + { + if (node->getUserData()) + { + osg::notify(osg::NOTICE)<<"UserData already assigned, overriding to set LayerAttributes."<setUserData(la); + } + + return la; +} + +void SlideShowConstructor::setBackgroundColor(const osg::Vec4& color, bool updateClearNode) +{ + _backgroundColor = color; + if (updateClearNode && _slideClearNode.valid()) _slideClearNode->setClearColor(_backgroundColor); +} + +void SlideShowConstructor::setTextColor(const osg::Vec4& color) +{ + _titleFontDataDefault.color = color; + _textFontDataDefault.color = color; + + _titleFontData.color = _titleFontDataDefault.color; + _textFontData.color = _textFontDataDefault.color; + +} + +void SlideShowConstructor::setPresentationName(const std::string& name) +{ + _presentationName = name; + if (_presentationSwitch.valid()) _presentationSwitch->setName(std::string("Presentation_")+_presentationName); +} + +void SlideShowConstructor::setPresentationDuration(double duration) +{ + _presentationDuration = duration; + if (_presentationDuration>=0.0 && _presentationSwitch.valid()) + { + setDuration(_presentationSwitch.get(),_presentationDuration); + } +} + +void SlideShowConstructor::addSlide() +{ + if (!_presentationSwitch) createPresentation(); + + // reset fonts + _titleFontData = _titleFontDataDefault; + _textFontData = _textFontDataDefault; + + // reset cursors + _titlePositionData = _titlePositionDataDefault; + _textPositionData = _textPositionDataDefault; + _imagePositionData = _imagePositionDataDefault; + _modelPositionData = _modelPositionDataDefault; + + _slide = new osg::Switch; + _slide->setName(std::string("Slide_")+_slideTitle); + + _slideClearNode = new osg::ClearNode; + _slideClearNode->setClearColor(_backgroundColor); + _slideClearNode->addChild(_slide.get()); + + _presentationSwitch->addChild(_slideClearNode.get()); + + _previousLayer = 0; + _currentLayer = 0; + + + _filePathData = new FilePathData(osgDB::getDataFilePathList()); + + _slideClearNode->setUserData(_filePathData.get()); +} + +void SlideShowConstructor::selectSlide(int slideNum) +{ + if (slideNum<0) + { + addSlide(); + } + else if (slideNum>=_presentationSwitch->getNumChildren()) + { + addSlide(); + } + else + { + _slideClearNode = dynamic_cast(_presentationSwitch->getChild(slideNum)); + if (!_slideClearNode || _slideClearNode->getNumChildren()==0 || _slideClearNode->getChild(0)->asSwitch()==0) + { + addSlide(); + } + else + { + _slide = _slideClearNode->getChild(0)->asSwitch(); + _previousLayer = _slide->getChild(_slide->getNumChildren()-1)->asGroup(); + _currentLayer = 0; + } + } +} + +void SlideShowConstructor::setSlideDuration(double duration) +{ + if (!_slide) addSlide(); + + if (_slide.valid()) + { + setDuration(_slide.get(),duration); + } +} + +void SlideShowConstructor::addLayer(bool inheritPreviousLayers, bool defineAsBaseLayer) +{ + if (!_slide) addSlide(); + + _currentLayer = new osg::Group; + + if (!_previousLayer || !inheritPreviousLayers) + { + _textPositionData = _textPositionDataDefault; + _imagePositionData = _imagePositionDataDefault; + _modelPositionData = _modelPositionDataDefault; + + // create the background and title.. + if (!_slideBackgroundImageFileName.empty()) + { + osg::Geometry* backgroundQuad = osg::createTexturedQuadGeometry(_slideOrigin, + osg::Vec3(_slideWidth,0.0f,0.0f), + osg::Vec3(0.0f,0.0f,_slideHeight)); + + osg::Geode* background = new osg::Geode; + + osg::StateSet* backgroundStateSet = background->getOrCreateStateSet(); + backgroundStateSet->setAttributeAndModes( + new osg::PolygonOffset(1.0f,2.0f), + osg::StateAttribute::ON); + + osg::Texture2D* texture = new osg::Texture2D(osgDB::readImageFile(_slideBackgroundImageFileName)); + texture->setResizeNonPowerOfTwoHint(false); + + backgroundStateSet->setTextureAttributeAndModes(0, + texture, + osg::StateAttribute::ON); + + background->addDrawable(backgroundQuad); + + _currentLayer->addChild(background); + } + + if (!_slideTitle.empty()) + { + osg::Geode* geode = new osg::Geode; + + osg::Vec3 localPosition = computePositionInModelCoords(_titlePositionData); + + osgText::Text* text = new osgText::Text; + text->setFont(_titleFontData.font); + text->setColor(_titleFontData.color); + text->setCharacterSize(_titleFontData.characterSize*_slideHeight); + text->setFontResolution(110,120); + text->setMaximumWidth(_titleFontData.maximumWidth*_slideWidth); + text->setLayout(_titleFontData.layout); + text->setAlignment(_titleFontData.alignment); + text->setAxisAlignment(_titleFontData.axisAlignment); + //text->setPosition(_titlePositionData.position); + text->setPosition(localPosition); + + text->setText(_slideTitle); + + geode->addDrawable(text); + + _currentLayer->addChild(geode); + } + + } + else + { + // copy previous layer's children across into new layer. + for(unsigned int i=0;i<_previousLayer->getNumChildren();++i) + { + _currentLayer->addChild(_previousLayer->getChild(i)); + } + } + + if (!defineAsBaseLayer) + { + _slide->addChild(_currentLayer.get()); + } + + _previousLayer = _currentLayer; +} + +void SlideShowConstructor::selectLayer(int layerNum) +{ + if (!_slide) + { + addSlide(); + addLayer(); + } + else if (layerNum>=0 && layerNum<_slide->getNumChildren() && _slide->getChild(layerNum)->asGroup()) + { + _currentLayer = _slide->getChild(layerNum)->asGroup(); + _previousLayer = _currentLayer; + } + else + { + addLayer(); + } + +} + + +void SlideShowConstructor::setLayerDuration(double duration) +{ + if (!_currentLayer) addLayer(); + + if (_currentLayer.valid()) + { + setDuration(_currentLayer.get(),duration); + } +} + +void SlideShowConstructor::layerClickToDoOperation(Operation operation, bool relativeJump, int slideNum, int layerNum) +{ + if (!_currentLayer) addLayer(); + + if (_currentLayer.valid()) + { + if (_previousLayer==_currentLayer) + { + if (_currentLayer->getNumChildren()>0) + { + osg::notify(osg::INFO)<<"creating new group within layer"<addChild(group); + _currentLayer = group; + } + } + else + { + osg::notify(osg::INFO)<<"creating secondary group within layer"<addChild(group); + _currentLayer = group; + } + _currentLayer->setEventCallback(new PickEventHandler(operation, relativeJump, slideNum, layerNum)); + } + +} + + +void SlideShowConstructor::layerClickToDoOperation(const std::string& command, Operation operation, bool relativeJump, int slideNum, int layerNum) +{ + if (!_currentLayer) addLayer(); + + if (_currentLayer.valid()) + { + if (_previousLayer==_currentLayer) + { + if (_currentLayer->getNumChildren()>0) + { + osg::notify(osg::INFO)<<"creating new group within layer"<addChild(group); + _currentLayer = group; + } + } + else + { + osg::notify(osg::INFO)<<"creating secondary group within layer"<addChild(group); + _currentLayer = group; + } + _currentLayer->setEventCallback(new PickEventHandler(command, operation, relativeJump, slideNum, layerNum)); + } + +} + + +void SlideShowConstructor::layerClickEventOperation(const SlideShowConstructor::KeyPosition& keyPos, bool relativeJump, int slideNum, int layerNum) +{ + if (!_currentLayer) addLayer(); + + if (_currentLayer.valid()) + { + if (_previousLayer==_currentLayer) + { + if (_currentLayer->getNumChildren()>0) + { + osg::notify(osg::INFO)<<"creating new group within layer"<addChild(group); + _currentLayer = group; + } + } + else + { + osg::notify(osg::INFO)<<"creating secondary group within layer"<addChild(group); + _currentLayer = group; + } + _currentLayer->setEventCallback(new PickEventHandler(keyPos, relativeJump, slideNum, layerNum)); + } + +} + +void SlideShowConstructor::addBullet(const std::string& bullet, PositionData& positionData, FontData& fontData) +{ + if (!_currentLayer) addLayer(); + + osg::Geode* geode = new osg::Geode; + + osgText::Text* text = new osgText::Text; + + osg::Vec3 localPosition = computePositionInModelCoords(positionData); + + text->setFont(fontData.font); + text->setColor(fontData.color); + text->setCharacterSize(fontData.characterSize*_slideHeight); + text->setFontResolution(110,120); + text->setMaximumWidth(fontData.maximumWidth*_slideWidth); + text->setLayout(fontData.layout); + text->setAlignment(fontData.alignment); + text->setAxisAlignment(fontData.axisAlignment); + text->setPosition(localPosition); + + text->setText(bullet); + + osg::BoundingBox bb = text->getBound(); + + // note, this increment is only "correct" when text is on the plane of the slide.. + // will need to make this more general later. + localPosition.z() = bb.zMin()-fontData.characterSize*_slideHeight*1.5; + + geode->addDrawable(text); + + osg::Node* subgraph = geode; + + if (positionData.requiresMaterialAnimation()) + subgraph = attachMaterialAnimation(subgraph,positionData); + + if (positionData.rotation[0]!=0.0) + { + osg::MatrixTransform* animation_transform = new osg::MatrixTransform; + animation_transform->setDataVariance(osg::Object::DYNAMIC); + animation_transform->setUpdateCallback( + new osgUtil::TransformCallback(geode->getBound().center(), + osg::Vec3(positionData.rotation[1],positionData.rotation[2],positionData.rotation[3]), + osg::DegreesToRadians(positionData.rotation[0]))); + animation_transform->addChild(subgraph); + + subgraph = animation_transform; + } + + _currentLayer->addChild(subgraph); + + updatePositionFromInModelCoords(localPosition, positionData); +} + +void SlideShowConstructor::addParagraph(const std::string& paragraph, PositionData& positionData, FontData& fontData) +{ + if (!_currentLayer) addLayer(); + + osg::Geode* geode = new osg::Geode; + + osg::Vec3 localPosition = computePositionInModelCoords(positionData); + + osgText::Text* text = new osgText::Text; + + text->setFont(fontData.font); + text->setColor(fontData.color); + text->setCharacterSize(fontData.characterSize*_slideHeight); + text->setFontResolution(110,120); + text->setMaximumWidth(fontData.maximumWidth*_slideWidth); + text->setLayout(fontData.layout); + text->setAlignment(fontData.alignment); + text->setAxisAlignment(fontData.axisAlignment); + text->setPosition(localPosition); + + text->setText(paragraph); + + osg::BoundingBox bb = text->getBound(); + + // note, this increment is only "correct" when text is on the plane of the slide.. + // will need to make this more general later. + localPosition.z() = bb.zMin()-fontData.characterSize*_slideHeight*1.5; + + geode->addDrawable(text); + + osg::Node* subgraph = geode; + + if (positionData.requiresMaterialAnimation()) + subgraph = attachMaterialAnimation(subgraph,positionData); + + if (positionData.rotation[0]!=0.0) + { + osg::MatrixTransform* animation_transform = new osg::MatrixTransform; + animation_transform->setDataVariance(osg::Object::DYNAMIC); + animation_transform->setUpdateCallback( + new osgUtil::TransformCallback(geode->getBound().center(), + osg::Vec3(positionData.rotation[1],positionData.rotation[2],positionData.rotation[3]), + osg::DegreesToRadians(positionData.rotation[0]))); + animation_transform->addChild(subgraph); + + subgraph = animation_transform; + } + + _currentLayer->addChild(subgraph); + + updatePositionFromInModelCoords(localPosition, positionData); +} + +class FindImageStreamsVisitor : public osg::NodeVisitor +{ +public: + FindImageStreamsVisitor(): + osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN) {} + + virtual void apply(osg::Node& node) + { + if (node.getStateSet()) + { + process(node.getStateSet()); + } + traverse(node); + } + + virtual void apply(osg::Geode& node) + { + if (node.getStateSet()) + { + process(node.getStateSet()); + } + + for(unsigned int i=0;igetStateSet()) + { + process(drawable->getStateSet()); + } + } + } + + void process(osg::StateSet* ss) + { + for(unsigned int i=0;igetTextureAttributeList().size();++i) + { + osg::Texture* texture = dynamic_cast(ss->getTextureAttribute(i,osg::StateAttribute::TEXTURE)); + osg::Image* image = texture ? texture->getImage(0) : 0; + osg::ImageStream* imageStream = image ? dynamic_cast(image) : 0; + if (imageStream) + { + texture->setDataVariance(osg::Object::DYNAMIC); + texture->setUnRefImageDataAfterApply(false); + texture->setResizeNonPowerOfTwoHint(false); + texture->setFilter(osg::Texture::MIN_FILTER,osg::Texture::LINEAR); + texture->setFilter(osg::Texture::MAG_FILTER,osg::Texture::LINEAR); + texture->setClientStorageHint(true); + } + } + } + +}; + +void SlideShowConstructor::findImageStreamsAndAddCallbacks(osg::Node* node) +{ + FindImageStreamsVisitor fisv; + node->accept(fisv); +} + + +osg::Geometry* SlideShowConstructor::createTexturedQuadGeometry(const osg::Vec3& pos, const osg::Vec4& rotation, float width, float height, osg::Image* image, bool& usedTextureRectangle) +{ + osg::Geometry* pictureQuad = 0; + osg::Texture* texture = 0; + osg::StateSet* stateset = 0; + + osg::Vec3 positionVec = pos; + osg::Vec3 widthVec(width,0.0f,0.0f); + osg::Vec3 heightVec(0.0f,0.0f,height); + + osg::Matrixd rotationMatrix = osg::Matrixd::rotate(osg::DegreesToRadians(rotation[0]),rotation[1],rotation[2],rotation[3]); + widthVec = widthVec*rotationMatrix; + heightVec = heightVec*rotationMatrix; + + osg::ImageStream* imageStream = dynamic_cast(image); + + bool flipYAxis = image->getOrigin()==osg::Image::TOP_LEFT; + +#ifdef __sgi + bool useTextureRectangle = false; +#else + bool useTextureRectangle = true; +#endif + + // pass back info on wether texture 2D is used. + usedTextureRectangle = useTextureRectangle; + + if (useTextureRectangle) + { + pictureQuad = osg::createTexturedQuadGeometry(positionVec, + widthVec, + heightVec, + 0.0f, flipYAxis ? image->t() : 0.0f, + image->s(), flipYAxis ? 0.0f : image->t()); + + stateset = pictureQuad->getOrCreateStateSet(); + + texture = new osg::TextureRectangle(image); + stateset->setTextureAttributeAndModes(0, + texture, + osg::StateAttribute::ON); + + + + } + else + { + pictureQuad = osg::createTexturedQuadGeometry(positionVec, + widthVec, + heightVec, + 0.0f, flipYAxis ? 1.0f : 0.0f, + 1.0f, flipYAxis ? 0.0f : 1.0f); + + stateset = pictureQuad->getOrCreateStateSet(); + + texture = new osg::Texture2D(image); + + stateset->setTextureAttributeAndModes(0, + texture, + osg::StateAttribute::ON); + + } + + if (!pictureQuad) return 0; + + if (imageStream) + { + imageStream->pause(); + + osg::notify(osg::INFO)<<"Reading video "<getFileName()<setClientStorageHint(true); + } + + + return pictureQuad; +} + + +void SlideShowConstructor::addImage(const std::string& filename, const PositionData& positionData, const ImageData& imageData) +{ + if (!_currentLayer) addLayer(); + + osg::Image* image = osgDB::readImageFile(filename); + + if (!image) return; + + osg::ImageStream* imageStream = dynamic_cast(image); + if (imageStream) + { + imageStream->setLoopingMode(imageData.loopingMode); + } + + + float s = image->s(); + float t = image->t(); + + // temporary hack + float height = 0.0f; + + float sx = imageData.region_in_pixel_coords ? 1.0f : s; + float sy = imageData.region_in_pixel_coords ? 1.0f : t; + + float x1 = imageData.region[0]*sx; + float y1 = imageData.region[1]*sy; + float x2 = imageData.region[2]*sx; + float y2 = imageData.region[3]*sy; + + float aspectRatio = (y2-y1)/(x2-x1); + + float image_width = _slideWidth*positionData.scale.x(); + float image_height = image_width*aspectRatio*positionData.scale.y()/positionData.scale.x(); + float offset = height*image_height*0.1f; + + osg::Vec3 pos = computePositionInModelCoords(positionData) + osg::Vec3(-image_width*0.5f+offset,-offset,-image_height*0.5f-offset); + + osg::Geode* picture = new osg::Geode; + osg::Node* subgraph = picture; + + + bool usedTextureRectangle = false; + osg::Geometry* pictureQuad = createTexturedQuadGeometry(pos, positionData.rotate, image_width, image_height, image, usedTextureRectangle); + osg::StateSet* pictureStateSet = pictureQuad->getOrCreateStateSet(); + + attachTexMat(pictureStateSet, imageData, s, t, usedTextureRectangle); + + picture->addDrawable(pictureQuad); + + // attach any meterial animation. + if (positionData.requiresMaterialAnimation()) + subgraph = attachMaterialAnimation(subgraph,positionData); + + + // attached any rotation + if (positionData.rotation[0]!=0.0) + { + osg::MatrixTransform* animation_transform = new osg::MatrixTransform; + animation_transform->setDataVariance(osg::Object::DYNAMIC); + animation_transform->setUpdateCallback( + new osgUtil::TransformCallback(picture->getBound().center(), + osg::Vec3(positionData.rotation[1],positionData.rotation[2],positionData.rotation[3]), + osg::DegreesToRadians(positionData.rotation[0]))); + + animation_transform->addChild(subgraph); + + subgraph = animation_transform; + } + + + // attached any animation + osg::AnimationPathCallback* animation = getAnimationPathCallback(positionData); + if (animation) + { + osg::notify(osg::INFO)<<"Have animation path for image"<getBound().center(); + + osg::PositionAttitudeTransform* animation_transform = new osg::PositionAttitudeTransform; + animation_transform->setDataVariance(osg::Object::DYNAMIC); + animation_transform->setPivotPoint(pivot); + animation->setPivotPoint(pivot); + + animation_transform->setUpdateCallback(animation); + + animation_transform->setReferenceFrame(positionData.absolute_path ? + osg::Transform::ABSOLUTE_RF: + osg::Transform::RELATIVE_RF); + + animation_transform->addChild(subgraph); + + subgraph = animation_transform; + } + + _currentLayer->addChild(subgraph); + +} + +void SlideShowConstructor::addStereoImagePair(const std::string& filenameLeft, const ImageData& imageDataLeft, const std::string& filenameRight, const ImageData& imageDataRight,const PositionData& positionData) +{ + if (!_currentLayer) addLayer(); + + + osg::ref_ptr imageLeft = osgDB::readImageFile(filenameLeft); + osg::ref_ptr imageRight = (filenameRight==filenameLeft) ? imageLeft.get() : osgDB::readImageFile(filenameRight); + + if (!imageLeft && !imageRight) return; + + osg::ImageStream* imageStreamLeft = dynamic_cast(imageLeft.get()); + if (imageStreamLeft) + { + imageStreamLeft->setLoopingMode(imageDataLeft.loopingMode); + } + + osg::ImageStream* imageStreamRight = dynamic_cast(imageRight.get()); + if (imageStreamRight) + { + imageStreamRight->setLoopingMode(imageDataRight.loopingMode); + } + + + float s = imageLeft->s(); + float t = imageLeft->t(); + + // temporary hack + float height = 0.0f; + + float sx = imageDataLeft.region_in_pixel_coords ? 1.0f : s; + float sy = imageDataLeft.region_in_pixel_coords ? 1.0f : t; + + float x1 = imageDataLeft.region[0]*sx; + float y1 = imageDataLeft.region[1]*sy; + float x2 = imageDataLeft.region[2]*sx; + float y2 = imageDataLeft.region[3]*sy; + + float aspectRatio = (y2-y1)/(x2-x1); + + float image_width = _slideWidth*positionData.scale.x(); + float image_height = image_width*aspectRatio*positionData.scale.y()/positionData.scale.x(); + + float offset = height*image_height*0.1f; + + bool usedTextureRectangle = false; + + osg::Vec3 pos = computePositionInModelCoords(positionData) + + osg::Vec3(-image_width*0.5f+offset,-offset,-image_height*0.5f-offset); + + osg::Geode* pictureLeft = new osg::Geode; + { + pictureLeft->setNodeMask(0x01); + + osg::Geometry* pictureLeftQuad = createTexturedQuadGeometry(pos, positionData.rotate, image_width,image_height,imageLeft.get(),usedTextureRectangle); + osg::StateSet* pictureLeftStateSet = pictureLeftQuad->getOrCreateStateSet(); + + attachTexMat(pictureLeftStateSet, imageDataLeft, s, t, usedTextureRectangle); + + pictureLeft->addDrawable(pictureLeftQuad); + + } + + osg::Geode* pictureRight = new osg::Geode; + { + pictureRight->setNodeMask(0x02); + + osg::Geometry* pictureRightQuad = createTexturedQuadGeometry(pos, positionData.rotate, image_width,image_height,imageRight.get(),usedTextureRectangle); + osg::StateSet* pictureRightStateSet = pictureRightQuad->getOrCreateStateSet(); + + attachTexMat(pictureRightStateSet, imageDataRight, s, t, usedTextureRectangle); + + pictureRight->addDrawable(pictureRightQuad); + } + + osg::Group* subgraph = new osg::Group; + subgraph->addChild(pictureLeft); + subgraph->addChild(pictureRight); + + // attach any meterial animation. + if (positionData.requiresMaterialAnimation()) + subgraph = attachMaterialAnimation(subgraph,positionData)->asGroup(); + + + // attached any rotation + if (positionData.rotation[0]!=0.0) + { + osg::MatrixTransform* animation_transform = new osg::MatrixTransform; + animation_transform->setDataVariance(osg::Object::DYNAMIC); + animation_transform->setUpdateCallback( + new osgUtil::TransformCallback(subgraph->getBound().center(), + osg::Vec3(positionData.rotation[1],positionData.rotation[2],positionData.rotation[3]), + osg::DegreesToRadians(positionData.rotation[0]))); + + animation_transform->addChild(subgraph); + + subgraph = animation_transform; + } + + + // attached any animation + osg::AnimationPathCallback* animation = getAnimationPathCallback(positionData); + if (animation) + { + osg::notify(osg::INFO)<<"Have animation path for image"<getBound().center(); + + osg::PositionAttitudeTransform* animation_transform = new osg::PositionAttitudeTransform; + animation_transform->setDataVariance(osg::Object::DYNAMIC); + animation_transform->setPivotPoint(pivot); + animation->setPivotPoint(pivot); + + animation_transform->setUpdateCallback(animation); + animation_transform->setReferenceFrame(positionData.absolute_path ? + osg::Transform::ABSOLUTE_RF: + osg::Transform::RELATIVE_RF); + + animation_transform->addChild(subgraph); + + subgraph = animation_transform; + } + + _currentLayer->addChild(subgraph); +} + +void SlideShowConstructor::addVNC(const std::string& hostname, const PositionData& positionData, const ImageData& imageData) +{ + addInteractiveImage(hostname+".vnc", positionData, imageData); +} + +void SlideShowConstructor::addBrowser(const std::string& url, const PositionData& positionData, const ImageData& imageData) +{ + addInteractiveImage(url+".gecko", positionData, imageData); +} + +void SlideShowConstructor::addPDF(const std::string& filename, const PositionData& positionData, const ImageData& imageData) +{ + addInteractiveImage(filename, positionData, imageData); +} + +class SetPageCallback: public SlideShowConstructor::LayerCallback +{ +public: + SetPageCallback(osgWidget::PdfImage* pdfImage, int pageNum): + _pdfImage(pdfImage), + _pageNum(pageNum) + { + } + + virtual void operator() (osg::Node*) const + { + osg::notify(osg::INFO)<<"PDF Page to be updated "<<_pageNum<getPageNum()!=_pageNum) + { + _pdfImage->page(_pageNum); + } + } + + osg::observer_ptr _pdfImage; + int _pageNum; +}; + + +osg::Image* SlideShowConstructor::addInteractiveImage(const std::string& filename, const PositionData& positionData, const ImageData& imageData) +{ + if (!_currentLayer) addLayer(); + + osg::Image* image = osgDB::readImageFile(filename); + + osg::notify(osg::INFO)<<"addInteractiveImage("<s(); + float t = image->t(); + + // temporary hack + float height = 0.0f; + + float sx = imageData.region_in_pixel_coords ? 1.0f : s; + float sy = imageData.region_in_pixel_coords ? 1.0f : t; + + float x1 = imageData.region[0]*sx; + float y1 = imageData.region[1]*sy; + float x2 = imageData.region[2]*sx; + float y2 = imageData.region[3]*sy; + + float aspectRatio = (y2-y1)/(x2-x1); + + float image_width = _slideWidth*positionData.scale.x(); + float image_height = image_width*aspectRatio*positionData.scale.y()/positionData.scale.x(); + float offset = height*image_height*0.1f; + + osg::Vec3 pos = computePositionInModelCoords(positionData) + osg::Vec3(-image_width*0.5f+offset,-offset,-image_height*0.5f-offset); + + osg::Geode* picture = new osg::Geode; + osg::Node* subgraph = picture; + + + bool usedTextureRectangle = false; + osg::Geometry* pictureQuad = createTexturedQuadGeometry(pos, positionData.rotate, image_width, image_height, image, usedTextureRectangle); + + osg::ref_ptr handler = new osgViewer::InteractiveImageHandler(image); + pictureQuad->setEventCallback(handler.get()); + pictureQuad->setCullCallback(handler.get()); + + osg::StateSet* pictureStateSet = pictureQuad->getOrCreateStateSet(); + + attachTexMat(pictureStateSet, imageData, s, t, usedTextureRectangle); + + pictureStateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF); + + picture->addDrawable(pictureQuad); + + // attach any meterial animation. + if (positionData.requiresMaterialAnimation()) + subgraph = attachMaterialAnimation(subgraph,positionData); + + + // attached any rotation + if (positionData.rotation[0]!=0.0) + { + osg::MatrixTransform* animation_transform = new osg::MatrixTransform; + animation_transform->setDataVariance(osg::Object::DYNAMIC); + animation_transform->setUpdateCallback( + new osgUtil::TransformCallback(picture->getBound().center(), + osg::Vec3(positionData.rotation[1],positionData.rotation[2],positionData.rotation[3]), + osg::DegreesToRadians(positionData.rotation[0]))); + + animation_transform->addChild(subgraph); + + subgraph = animation_transform; + } + + + // attached any animation + osg::AnimationPathCallback* animation = getAnimationPathCallback(positionData); + if (animation) + { + osg::notify(osg::INFO)<<"Have animation path for image"<getBound().center(); + + osg::PositionAttitudeTransform* animation_transform = new osg::PositionAttitudeTransform; + animation_transform->setDataVariance(osg::Object::DYNAMIC); + animation_transform->setPivotPoint(pivot); + animation->setPivotPoint(pivot); + + animation_transform->setUpdateCallback(animation); + + animation_transform->setReferenceFrame(positionData.absolute_path ? + osg::Transform::ABSOLUTE_RF: + osg::Transform::RELATIVE_RF); + + animation_transform->addChild(subgraph); + + subgraph = animation_transform; + } + + _currentLayer->addChild(subgraph); + + osgWidget::PdfImage* pdfImage = dynamic_cast(image); + if (pdfImage && imageData.page>=0) + { + getOrCreateLayerAttributes(_currentLayer.get())->addEnterCallback(new SetPageCallback(pdfImage, imageData.page)); + + osg::notify(osg::INFO)<<"Setting pdf page num "<setBackgroundColor(imageData.backgroundColor); + pdfImage->page(imageData.page); + + if (imageData.backgroundColor.a()<1.0f) + { + SetToTransparentBin sttb; + subgraph->accept(sttb); + } + + + } + + + return image; +} + +std::string SlideShowConstructor::findFileAndRecordPath(const std::string& filename) +{ + std::string foundFile = osgDB::findDataFile(filename); + if (foundFile.empty()) return foundFile; + + osg::notify(osg::INFO)<<"foundFile "<filePathList.begin(),_filePathData->filePathList.end(),path); + if (itr==_filePathData->filePathList.end()) + { + osg::notify(osg::INFO)<<"New path to record "<filePathList.push_front(path); + } + } + + return foundFile; + +} + +void SlideShowConstructor::addModel(const std::string& filename, const PositionData& positionData, const ModelData& modelData) +{ + osg::Node* subgraph = 0; + + if (filename=="sphere") + { + osg::Geode* geode = new osg::Geode; + geode->addDrawable(new osg::ShapeDrawable(new osg::Sphere)); + + subgraph = geode; + } + else if (filename=="box") + { + osg::Geode* geode = new osg::Geode; + geode->addDrawable(new osg::ShapeDrawable(new osg::Box)); + + subgraph = geode; + } + else + { + std::string foundFile = findFileAndRecordPath(filename); + if (foundFile.empty()) return; + + subgraph = osgDB::readNodeFile(foundFile); + } + + if (!subgraph) return; + + addModel(subgraph, positionData, modelData); +} + +void SlideShowConstructor::addModel(osg::Node* subgraph, const PositionData& positionData, const ModelData& modelData) +{ + osg::Object::DataVariance defaultMatrixDataVariance = osg::Object::DYNAMIC; // STATIC + + if (!modelData.effect.empty()) + { + if (modelData.effect=="SpecularHighlights" || modelData.effect=="glossy") + { + osgFX::SpecularHighlights* specularHighlights = new osgFX::SpecularHighlights; + specularHighlights->setTextureUnit(1); + specularHighlights->addChild(subgraph); + subgraph = specularHighlights; + } + } + + if (positionData.frame==SLIDE) + { + osg::Vec3 pos = convertSlideToModel(positionData.position); + + const osg::BoundingSphere& bs = subgraph->getBound(); + float model_scale = positionData.scale.x()*_slideHeight*(1.0f-positionData.position.z())*0.7f/bs.radius(); + + osg::MatrixTransform* transform = new osg::MatrixTransform; + transform->setDataVariance(defaultMatrixDataVariance); + transform->setMatrix(osg::Matrix::translate(-bs.center())* + osg::Matrix::scale(model_scale,model_scale,model_scale)* + osg::Matrix::rotate(osg::DegreesToRadians(positionData.rotate[0]),positionData.rotate[1],positionData.rotate[2],positionData.rotate[3])* + osg::Matrix::translate(pos)); + + + transform->setStateSet(createTransformStateSet()); + transform->addChild(subgraph); + + subgraph = transform; + + } + else + { + osg::Matrix matrix(osg::Matrix::scale(1.0f/positionData.scale.x(),1.0f/positionData.scale.x(),1.0f/positionData.scale.x())* + osg::Matrix::rotate(osg::DegreesToRadians(positionData.rotate[0]),positionData.rotate[1],positionData.rotate[2],positionData.rotate[3])* + osg::Matrix::translate(positionData.position)); + + osg::MatrixTransform* transform = new osg::MatrixTransform; + transform->setDataVariance(defaultMatrixDataVariance); + transform->setMatrix(osg::Matrix::inverse(matrix)); + + osg::notify(osg::INFO)<<"Position Matrix "<getMatrix()<addChild(subgraph); + + subgraph = transform; + } + + float referenceSizeRatio = 0.707; + float referenceSize = subgraph->getBound().radius() * referenceSizeRatio; + + + // attach any meterial animation. + if (positionData.requiresMaterialAnimation()) + subgraph = attachMaterialAnimation(subgraph,positionData); + + + // attached any rotation + if (positionData.rotation[0]!=0.0) + { + osg::MatrixTransform* animation_transform = new osg::MatrixTransform; + animation_transform->setDataVariance(osg::Object::DYNAMIC); + animation_transform->setUpdateCallback( + new osgUtil::TransformCallback(subgraph->getBound().center(), + osg::Vec3(positionData.rotation[1],positionData.rotation[2],positionData.rotation[3]), + osg::DegreesToRadians(positionData.rotation[0]))); + + animation_transform->addChild(subgraph); + + osg::notify(osg::INFO)<<"Rotation Matrix "<getMatrix()<getBound().center(); + + osg::AnimationPath* path = animation->getAnimationPath(); + if (positionData.animation_name=="wheel" && (path->getTimeControlPointMap()).size()>=2) + { + osg::notify(osg::INFO)<<"**** Need to handle special wheel animation"<getTimeControlPointMap(); + + osg::AnimationPath::TimeControlPointMap::iterator curr_itr = controlPoints.begin(); + osg::AnimationPath::TimeControlPointMap::iterator prev_itr=curr_itr; + ++curr_itr; + + osg::AnimationPath::ControlPoint* prev_cp = &(prev_itr->second); + osg::AnimationPath::ControlPoint* curr_cp = &(curr_itr->second); + + float totalLength = 0; + float rotation_y_axis = 0; + osg::Vec3 delta_position = curr_cp->getPosition() - prev_cp->getPosition(); + float rotation_z_axis = atan2f(delta_position.y(),delta_position.x()); + + osg::Quat quat_y_axis,quat_z_axis,quat_combined; + + quat_y_axis.makeRotate(rotation_y_axis,0.0f,1.0f,0.0f); + quat_z_axis.makeRotate(rotation_z_axis,0.0f,0.0f,1.0f); + quat_combined = quat_y_axis*quat_z_axis; + + // set first rotation. + prev_cp->setRotation(quat_combined); + + for(; + curr_itr!=controlPoints.end(); + ++curr_itr) + { + prev_cp = &(prev_itr->second); + curr_cp = &(curr_itr->second); + + delta_position = curr_cp->getPosition() - prev_cp->getPosition(); + + totalLength += delta_position.length(); + + // rolling - rotation about the y axis. + rotation_y_axis = totalLength/referenceSize; + + // direction - rotation about the z axis. + rotation_z_axis = atan2f(delta_position.y(),delta_position.x()); + + osg::notify(osg::INFO)<<" rotation_y_axis="< volume = new osgVolume::Volume; + osg::ref_ptr tile = new osgVolume::VolumeTile; + volume->addChild(tile.get()); + + osg::ref_ptr layer = new osgVolume::ImageLayer(image.get()); + layer->rescaleToZeroToOneRange(); + + osg::RefMatrix* matrix = dynamic_cast(image->getUserData()); + if (matrix) + { + osgVolume::Locator* locator = new osgVolume::Locator(*matrix); + layer->setLocator(locator); + tile->setLocator(locator); + } + + tile->setLayer(layer.get()); + + float alphaFunc = 0.1; + + osgVolume::SwitchProperty* sp = new osgVolume::SwitchProperty; + sp->setActiveProperty(0); + + osgVolume::AlphaFuncProperty* ap = new osgVolume::AlphaFuncProperty(alphaFunc); + osgVolume::SampleDensityProperty* sd = new osgVolume::SampleDensityProperty(0.005); + osgVolume::TransparencyProperty* tp = new osgVolume::TransparencyProperty(1.0); + + { + // Standard + osgVolume::CompositeProperty* cp = new osgVolume::CompositeProperty; + cp->addProperty(ap); + cp->addProperty(sd); + cp->addProperty(tp); + + sp->addProperty(cp); + } + + { + // Light + osgVolume::CompositeProperty* cp = new osgVolume::CompositeProperty; + cp->addProperty(ap); + cp->addProperty(sd); + cp->addProperty(tp); + cp->addProperty(new osgVolume::LightingProperty); + + sp->addProperty(cp); + } + + { + // Isosurface + osgVolume::CompositeProperty* cp = new osgVolume::CompositeProperty; + cp->addProperty(sd); + cp->addProperty(tp); + cp->addProperty(new osgVolume::IsoSurfaceProperty(alphaFunc)); + + sp->addProperty(cp); + } + + { + // MaximumIntensityProjection + osgVolume::CompositeProperty* cp = new osgVolume::CompositeProperty; + cp->addProperty(ap); + cp->addProperty(sd); + cp->addProperty(tp); + cp->addProperty(new osgVolume::MaximumIntensityProjectionProperty); + + sp->addProperty(cp); + } + + layer->addProperty(sp); + tile->setVolumeTechnique(new osgVolume::RayTracedTechnique); + tile->setEventCallback(new osgVolume::PropertyAdjustmentCallback()); + + ModelData modelData; + addModel(volume.get(), positionData, modelData); +} + +bool SlideShowConstructor::attachTexMat(osg::StateSet* stateset, const ImageData& imageData, float s, float t, bool textureRectangle) +{ + float xScale = textureRectangle ? s : 1.0f; + float yScale = textureRectangle ? t : 1.0f; + + float sx = (textureRectangle ? s : 1.0f) / (imageData.region_in_pixel_coords ? s : 1.0f); + float sy = (textureRectangle ? t : 1.0f) / (imageData.region_in_pixel_coords ? t : 1.0f); + + float x1 = imageData.region[0]*sx; + float y1 = imageData.region[1]*sy; + float x2 = imageData.region[2]*sx; + float y2 = imageData.region[3]*sy; + + if (x1!=0.0f || y1!=0.0f || x2!=xScale || y2 != yScale || + imageData.texcoord_rotate != 0.0f) + { + osg::TexMat* texmat = new osg::TexMat; + texmat->setMatrix(osg::Matrix::translate(-0.5f*xScale,-0.5f*yScale,0.0f)* + osg::Matrix::rotate(osg::DegreesToRadians(imageData.texcoord_rotate),0.0f,0.0f,1.0f)* + osg::Matrix::translate(0.5f*xScale,0.5f*yScale,0.0f)* + osg::Matrix::scale((x2-x1)/xScale,(y2-y1)/yScale,1.0f)* + osg::Matrix::translate(x1, + y1, + 0.0f)); + + stateset->setTextureAttribute(0,texmat); + return true; + } + return false; +} + +osg::Node* SlideShowConstructor::attachMaterialAnimation(osg::Node* model, const PositionData& positionData) +{ + ss3d::AnimationMaterial* animationMaterial = 0; + + if (!positionData.animation_material_filename.empty()) + { + std::string absolute_animation_file_path = osgDB::findDataFile(positionData.animation_material_filename); + if (!absolute_animation_file_path.empty()) + { + std::ifstream animation_filestream(absolute_animation_file_path.c_str()); + if (!animation_filestream.eof()) + { + animationMaterial = new ss3d::AnimationMaterial; + animationMaterial->read(animation_filestream); + } + } + } + else if (!positionData.fade.empty()) + { + std::istringstream iss(positionData.fade); + + animationMaterial = new ss3d::AnimationMaterial; + while (!iss.fail() && !iss.eof()) + { + float time=1.0f, alpha=1.0f; + iss >> time >> alpha; + if (!iss.fail()) + { + osg::Material* material = new osg::Material; + material->setAmbient(osg::Material::FRONT_AND_BACK,osg::Vec4(1.0f,1.0f,1.0f,alpha)); + material->setDiffuse(osg::Material::FRONT_AND_BACK,osg::Vec4(1.0f,1.0f,1.0f,alpha)); + animationMaterial->insert(time,material); + } + } + } + + if (animationMaterial) + { + animationMaterial->setLoopMode(positionData.animation_material_loop_mode); + + ss3d::AnimationMaterialCallback* animationMaterialCallback = new ss3d::AnimationMaterialCallback(animationMaterial); + animationMaterialCallback->setTimeOffset(positionData.animation_material_time_offset); + animationMaterialCallback->setTimeMultiplier(positionData.animation_material_time_multiplier); + + osg::Group* decorator = new osg::Group; + decorator->addChild(model); + + decorator->setUpdateCallback(animationMaterialCallback); + + if (animationMaterial->requiresBlending()) + { + SetToTransparentBin sttb; + decorator->accept(sttb); + } + + return decorator; + } + + return model; +} + +osg::AnimationPath* SlideShowConstructor::readPivotPath(const std::string& filename) const +{ + std::ifstream in(filename.c_str()); + if (!in.eof()) + { + osg::AnimationPath* animation = new osg::AnimationPath; + + while (!in.eof()) + { + double time; + osg::Vec3 pivot; + osg::Vec3 position; + float scale; + osg::Quat rotation; + in >> time >> pivot.x() >> pivot.y() >> pivot.z() >> position.x() >> position.y() >> position.z() >> rotation.x() >> rotation.y() >> rotation.z() >> rotation.w() >> scale; + if(!in.eof()) + { + osg::Matrix SR = osg::Matrix::scale(scale,scale,scale)* + osg::Matrixf::rotate(rotation); + + osg::Matrix invSR; + invSR.invert(SR); + + position += (invSR*pivot)*SR; + + animation->insert(time,osg::AnimationPath::ControlPoint(position,rotation,osg::Vec3(scale,scale,scale))); + } + } + + return animation; + } + return 0; +} + +struct RotationPathData +{ + RotationPathData(): + time(0.0), + scale(1.0f), + azim(0.0f), + elevation(0.0f) {} + + double time; + osg::Vec3 pivot; + osg::Vec3 position; + float scale; + float azim; + float elevation; + + void addToPath(osg::AnimationPath* animation) const + { + osg::Quat Rx, Rz, rotation; + + Rx.makeRotate(osg::DegreesToRadians(elevation),1.0f,0.0f,0.0f); + Rz.makeRotate(osg::DegreesToRadians(azim),0.0f,0.0f,1.0f); + rotation = Rz * Rx; // note, I believe this is the wrong way round, but I had to put it in this order to fix the Quat properly. + + osg::Matrix SR = osg::Matrix::scale(scale,scale,scale)* + osg::Matrixf::rotate(rotation); + + osg::Matrix invSR; + invSR.invert(SR); + + osg::Vec3 local_position = position + (invSR*pivot)*SR; + + animation->insert(time,osg::AnimationPath::ControlPoint(local_position,rotation,osg::Vec3(scale,scale,scale))); + } + +}; +osg::AnimationPath* SlideShowConstructor::readRotationPath(const std::string& filename) const +{ + std::ifstream in(filename.c_str()); + if (!in.eof()) + { + osg::AnimationPath* animation = new osg::AnimationPath; + + RotationPathData prevValue; + bool first = true; + while (!in.eof()) + { + RotationPathData currValue; + in >> currValue.time >> currValue.pivot.x() >> currValue.pivot.y() >> currValue.pivot.z() >> currValue.position.x() >> currValue.position.y() >> currValue.position.z() >> currValue.azim >> currValue.elevation >> currValue.scale; + if(!in.eof()) + { + + if (!first) + { + + unsigned int num = 20; + float dr = 1.0f/(float)num; + float r=dr; + for(unsigned int i=0; + iread(animation_filestream); + } + } + else + { + std::ifstream animation_filestream(absolute_animation_file_path.c_str()); + + osgDB::Input fr; + fr.attach(&animation_filestream); + + static osg::ref_ptr s_path = new osg::AnimationPath; + osg::ref_ptr object = osgDB::readObjectFile(absolute_animation_file_path); // fr.readObjectOfType(*s_path); + object = fr.readObject(); // fr.readObjectOfType(*s_path); + if (object.valid()) + { + animation = dynamic_cast(object.get()); + } + } + + if (animation) + { + if (positionData.frame==SlideShowConstructor::SLIDE) + { + osg::AnimationPath::TimeControlPointMap& controlPoints = animation->getTimeControlPointMap(); + for(osg::AnimationPath::TimeControlPointMap::iterator itr=controlPoints.begin(); + itr!=controlPoints.end(); + ++itr) + { + osg::AnimationPath::ControlPoint& cp = itr->second; + cp.setPosition(convertSlideToModel(cp.getPosition()+positionData.position)); + } + } + + animation->setLoopMode(positionData.path_loop_mode); + + osg::AnimationPathCallback* apc = new osg::AnimationPathCallback(animation); + apc->setTimeOffset(positionData.path_time_offset); + apc->setTimeMultiplier(positionData.path_time_multiplier); + apc->setUseInverseMatrix(positionData.inverse_path); + + osg::notify(osg::INFO)<<"UseInverseMatrix "< +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "AnimationMaterial.h" + +class SlideShowConstructor +{ +public: + + + enum CoordinateFrame { SLIDE, MODEL }; + + struct HomePosition : public virtual osg::Referenced + { + HomePosition() {} + + HomePosition(const osg::Vec3& in_eye, const osg::Vec3& in_center, const osg::Vec3& in_up): + eye(in_eye), + center(in_center), + up(in_up) {} + + osg::Vec3 eye; + osg::Vec3 center; + osg::Vec3 up; + }; + + struct KeyPosition + { + KeyPosition(unsigned int key=0, float x=FLT_MAX, float y=FLT_MAX): + _key((osgGA::GUIEventAdapter::KeySymbol)key), + _x(x), + _y(y) {} + + + void set(unsigned int key=0, float x=FLT_MAX, float y=FLT_MAX) + { + _key = (osgGA::GUIEventAdapter::KeySymbol)key; + _x = x; + _y = y; + } + + osgGA::GUIEventAdapter::KeySymbol _key; + float _x; + float _y; + }; + + struct LayerCallback : public virtual osg::Referenced + { + virtual void operator() (osg::Node* node) const = 0; + }; + + struct LayerAttributes : public virtual osg::Referenced + { + LayerAttributes():_duration(0),_relativeJump(true),_slideNum(0),_layerNum(0) {} + LayerAttributes(double in_duration):_duration(in_duration),_relativeJump(true),_slideNum(0),_layerNum(0) {} + + void setDuration(double duration) { _duration = duration; } + double getDuration() const { return _duration; } + + typedef std::vector Keys; + typedef std::vector RunStrings; + + void setKeys(const Keys& keys) { _keys = keys; } + const Keys& getKeys() const { return _keys; } + + void addKey(const KeyPosition& kp) { _keys.push_back(kp); } + + void setRunStrings(const RunStrings& runStrings) { _runStrings = runStrings; } + const RunStrings& getRunStrings() const { return _runStrings; } + + void addRunString(const std::string& runString) { _runStrings.push_back(runString); } + + void setJump(bool relativeJump, int slideNum, int layerNum) + { + _relativeJump = relativeJump; + _slideNum = slideNum; + _layerNum = layerNum; + } + + bool getRelativeJump() const { return _relativeJump; } + int getSlideNum() const { return _slideNum; } + int getLayerNum() const { return _layerNum; } + + bool requiresJump() const { return _relativeJump ? (_slideNum!=0 || _layerNum!=0) : true; } + + double _duration; + Keys _keys; + RunStrings _runStrings; + + bool _relativeJump; + int _slideNum; + int _layerNum; + + void addEnterCallback(LayerCallback* lc) { _enterLayerCallbacks.push_back(lc); } + void addLeaveCallback(LayerCallback* lc) { _leaveLayerCallbacks.push_back(lc); } + + void callEnterCallbacks(osg::Node* node); + void callLeaveCallbacks(osg::Node* node); + + typedef std::list< osg::ref_ptr > LayerCallbacks; + LayerCallbacks _enterLayerCallbacks; + LayerCallbacks _leaveLayerCallbacks; + }; + + + LayerAttributes* getOrCreateLayerAttributes(osg::Node* node); + + void setDuration(osg::Node* node,double duration) + { + getOrCreateLayerAttributes(node)->setDuration(duration); + } + + void addKey(osg::Node* node,const KeyPosition& kp) + { + getOrCreateLayerAttributes(node)->addKey(kp); + } + + void addRunString(osg::Node* node, const std::string& runString) + { + getOrCreateLayerAttributes(node)->addRunString(runString); + } + + void setJump(osg::Node* node, bool relativeJump, int slideNum, int layerNum) + { + getOrCreateLayerAttributes(node)->setJump(relativeJump, slideNum, layerNum); + } + + void addPresentationKey(const KeyPosition& kp) + { + if (!_presentationSwitch) createPresentation(); + if (_presentationSwitch.valid()) addKey( _presentationSwitch.get(), kp); + } + + void addPresentationRunString(const std::string& runString) + { + if (!_presentationSwitch) createPresentation(); + if (_presentationSwitch.valid()) addRunString( _presentationSwitch.get(),runString); + } + + void addSlideKey(const KeyPosition& kp) + { + if (!_slide) addSlide(); + if (_slide.valid()) addKey(_slide.get(),kp); + } + + void addSlideRunString(const std::string& runString) + { + if (!_slide) addSlide(); + if (_slide.valid()) addRunString(_slide.get(),runString); + } + + void setSlideJump(bool relativeJump, int switchNum, int layerNum) + { + if (!_slide) addSlide(); + if (_slide.valid()) setJump(_slide.get(),relativeJump, switchNum, layerNum); + } + + void addLayerKey(const KeyPosition& kp) + { + if (!_currentLayer) addLayer(); + if (_currentLayer.valid()) addKey(_currentLayer.get(),kp); + } + + void addLayerRunString(const std::string& runString) + { + if (!_currentLayer) addLayer(); + if (_currentLayer.valid()) addRunString(_currentLayer.get(),runString); + } + + + void setLayerJump(bool relativeJump, int switchNum, int layerNum) + { + if (!_currentLayer) addLayer(); + if (_currentLayer.valid()) setJump(_currentLayer.get(),relativeJump, switchNum, layerNum); + } + + struct FilePathData : public virtual osg::Referenced + { + FilePathData(const osgDB::FilePathList& fpl):filePathList(fpl) {} + + osgDB::FilePathList filePathList; + }; + + + struct PositionData + { + PositionData(): + frame(SlideShowConstructor::SLIDE), + position(0.0f,1.0f,0.0f), + //position(0.5f,0.5f,0.0f), + scale(1.0f,1.0f,1.0f), + rotate(0.0f,0.0f,0.0f,1.0f), + rotation(0.0f,0.0f,1.0f,0.0f), + absolute_path(false), + inverse_path(false), + path_time_offset(0.0), + path_time_multiplier(1.0f), + path_loop_mode(osg::AnimationPath::NO_LOOPING), + animation_material_time_offset(0.0), + animation_material_time_multiplier(1.0), + animation_material_loop_mode(ss3d::AnimationMaterial::NO_LOOPING) {} + + bool requiresPosition() const + { + return (position[0]!=0.0f || position[1]!=1.0f || position[2]!=1.0f); + } + + bool requiresScale() const + { + return (scale[0]!=1.0f || scale[1]!=1.0f || scale[2]!=1.0f); + } + + bool requiresRotate() const + { + return rotate[0]!=0.0f; + } + + bool requiresAnimation() const + { + return (rotation[0]!=0.0f || !path.empty()); + } + + bool requiresMaterialAnimation() const + { + return !animation_material_filename.empty() || !fade.empty(); + } + + CoordinateFrame frame; + osg::Vec3 position; + osg::Vec3 scale; + osg::Vec4 rotate; + osg::Vec4 rotation; + std::string animation_name; + bool absolute_path; + bool inverse_path; + double path_time_offset; + double path_time_multiplier; + osg::AnimationPath::LoopMode path_loop_mode; + std::string path; + double animation_material_time_offset; + double animation_material_time_multiplier; + ss3d::AnimationMaterial::LoopMode animation_material_loop_mode; + std::string animation_material_filename; + std::string fade; + }; + + struct ModelData + { + ModelData(): + effect("") {} + + std::string effect; + }; + + struct ImageData + { + ImageData(): + width(1.0f), + height(1.0f), + region(0.0f,0.0f,1.0f,1.0f), + region_in_pixel_coords(false), + texcoord_rotate(0.0f), + loopingMode(osg::ImageStream::NO_LOOPING), + page(-1), + backgroundColor(1.0f,1.0f,1.0f,1.0f) {} + + float width; + float height; + osg::Vec4 region; + bool region_in_pixel_coords; + float texcoord_rotate; + osg::ImageStream::LoopingMode loopingMode; + int page; + osg::Vec4 backgroundColor; + }; + + struct FontData + { + FontData(): + font("fonts/arial.ttf"), + layout(osgText::Text::LEFT_TO_RIGHT), + alignment(osgText::Text::LEFT_BASE_LINE), + axisAlignment(osgText::Text::XZ_PLANE), + characterSize(0.04f), + maximumHeight(1.0f), + maximumWidth(1.0f), + color(1.0f,1.0f,1.0f,1.0f) {} + + std::string font; + osgText::Text::Layout layout; + osgText::Text::AlignmentType alignment; + osgText::Text::AxisAlignment axisAlignment; + float characterSize; + float maximumHeight; + float maximumWidth; + osg::Vec4 color; + }; + + /// Operations related to click to run/load/key events. + enum Operation + { + RUN, + LOAD, + EVENT, + JUMP + }; + + SlideShowConstructor(); + + void createPresentation(); + + void setBackgroundColor(const osg::Vec4& color, bool updateClearNode); + const osg::Vec4& getBackgroundColor() const { return _backgroundColor; } + + void setTextColor(const osg::Vec4& color); + const osg::Vec4& getTextColor() const { return _textFontDataDefault.color; } + + void setPresentationName(const std::string& name); + + void setPresentationAspectRatio(float aspectRatio); + + void setPresentationAspectRatio(const std::string& str); + + void setPresentationDuration(double duration); + + + void addSlide(); + + void selectSlide(int slideNum); + + + void setSlideTitle(const std::string& name, PositionData& positionData, FontData& fontData) + { + _titlePositionData = positionData; + _titleFontData = fontData; + _slideTitle = name; + } + + void setSlideBackground(const std::string& name) { _slideBackgroundImageFileName = name; } + + void setSlideDuration(double duration); + + + void addLayer(bool inheritPreviousLayers=true, bool defineAsBaseLayer=false); + + void selectLayer(int layerNum); + + void setLayerDuration(double duration); + + + // title settings + FontData& getTitleFontData() { return _titleFontData; } + FontData& getTitleFontDataDefault() { return _titleFontDataDefault; } + + PositionData& getTitlePositionData() { return _titlePositionData; } + PositionData& getTitlePositionDataDefault() { return _titlePositionDataDefault; } + + // text settings + FontData& getTextFontData() { return _textFontData; } + FontData& getTextFontDataDefault() { return _textFontDataDefault; } + + PositionData& getTextPositionData() { return _textPositionData; } + PositionData& getTextPositionDataDefault() { return _textPositionDataDefault; } + + void translateTextCursor(const osg::Vec3& delta) { _textPositionData.position += delta; } + + // image settings + PositionData& getImagePositionData() { return _imagePositionData; } + PositionData& getImagePositionDataDefault() { return _imagePositionDataDefault; } + + // model settings + PositionData& getModelPositionData() { return _modelPositionData; } + PositionData& getModelPositionDataDefault() { return _modelPositionDataDefault; } + + + void layerClickToDoOperation(Operation operation, bool relativeJump=true, int slideNum=0, int layerNum=0); + void layerClickToDoOperation(const std::string& command, Operation operation, bool relativeJump=true, int slideNum=0, int layerNum=0); + void layerClickEventOperation(const SlideShowConstructor::KeyPosition& keyPos, bool relativeJump=true, int slideNum=0, int layerNum=0); + + void addBullet(const std::string& bullet, PositionData& positionData, FontData& fontData); + + void addParagraph(const std::string& paragraph, PositionData& positionData, FontData& fontData); + + void addImage(const std::string& filename,const PositionData& positionData, const ImageData& imageData); + + void addStereoImagePair(const std::string& filenameLeft, const ImageData& imageDataLeft, const std::string& filenameRight,const ImageData& imageDataRight, const PositionData& positionData); + + void addVNC(const std::string& filename,const PositionData& positionData, const ImageData& imageData); + void addBrowser(const std::string& filename,const PositionData& positionData, const ImageData& imageData); + void addPDF(const std::string& filename,const PositionData& positionData, const ImageData& imageData); + osg::Image* addInteractiveImage(const std::string& filename,const PositionData& positionData, const ImageData& imageData); + + void addModel(osg::Node* subgraph, const PositionData& positionData, const ModelData& modelData); + + void addModel(const std::string& filename, const PositionData& positionData, const ModelData& modelData); + + void addVolume(const std::string& filename, const PositionData& positionData); + + osg::Group* takePresentation() { return _root.release(); } + + osg::Group* getPresentation() { return _root.get(); } + + osg::Switch* getPresentationSwitch() { return _presentationSwitch.get(); } + + osg::Switch* getCurrentSlide() { return _slide.get(); } + + osg::Group* getCurrentLayer() { return _currentLayer.get(); } + + void setLoopPresentation(bool loop) { _loopPresentation = loop; } + bool getLoopPresentation() const { return _loopPresentation; } + + void setAutoSteppingActive(bool flag = true) { _autoSteppingActive = flag; } + bool getAutoSteppingActive() const { return _autoSteppingActive; } + +protected: + + void findImageStreamsAndAddCallbacks(osg::Node* node); + + osg::Geometry* createTexturedQuadGeometry(const osg::Vec3& pos, const osg::Vec4& rotation, float width,float height, osg::Image* image, bool& usedTextureRectangle); + + osg::Vec3 computePositionInModelCoords(const PositionData& positionData) const; + void updatePositionFromInModelCoords(const osg::Vec3& vertex, PositionData& positionData) const; + + osg::Vec3 convertSlideToModel(const osg::Vec3& position) const; + osg::Vec3 convertModelToSlide(const osg::Vec3& position) const; + + osg::AnimationPath* readPivotPath(const std::string& filename) const; + osg::AnimationPath* readRotationPath(const std::string& filename) const; + + osg::AnimationPathCallback* getAnimationPathCallback(const PositionData& positionData); + + osg::Node* attachMaterialAnimation(osg::Node* model, const PositionData& positionData); + bool attachTexMat(osg::StateSet* stateset, const ImageData& imageData, float s, float t, bool textureRectangle); + + osg::StateSet* createTransformStateSet() + { + osg::StateSet* stateset = new osg::StateSet; + stateset->setMode(GL_NORMALIZE,osg::StateAttribute::ON); + return stateset; + } + + osg::Vec3 _slideOrigin; + osg::Vec3 _eyeOrigin; + float _slideWidth; + float _slideHeight; + float _slideDistance; + + // title settings + FontData _titleFontData; + FontData _titleFontDataDefault; + + PositionData _titlePositionData; + PositionData _titlePositionDataDefault; + + // text settings + FontData _textFontData; + FontData _textFontDataDefault; + + PositionData _textPositionData; + PositionData _textPositionDataDefault; + + // image settings + PositionData _imagePositionData; + PositionData _imagePositionDataDefault; + + // model settings + PositionData _modelPositionData; + PositionData _modelPositionDataDefault; + + + bool _loopPresentation; + bool _autoSteppingActive; + osg::Vec4 _backgroundColor; + std::string _presentationName; + double _presentationDuration; + + osg::ref_ptr _root; + osg::ref_ptr _presentationSwitch; + + osg::ref_ptr _slideClearNode; + osg::ref_ptr _slide; + std::string _slideTitle; + std::string _slideBackgroundImageFileName; + + osg::ref_ptr _previousLayer; + osg::ref_ptr _currentLayer; + + osg::ref_ptr _filePathData; + + std::string findFileAndRecordPath(const std::string& filename); + +}; + +#endif