/* -*-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 using namespace osgPresentation; static osg::observer_ptr s_seh; SlideEventHandler* SlideEventHandler::instance() { return s_seh.get(); } void LayerAttributes::callEnterCallbacks(osg::Node* node) { osg::notify(osg::INFO)<<"LayerAttributes::callEnterCallbacks("<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, 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(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())); } 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(false), _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 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()<(_slideSwitch->getNumChildren())) { duration = getDuration(_slideSwitch->getChild(_activeLayer)); } if (duration < 0.0) { duration = getDuration(_slideSwitch.get()); } if (duration >=0 ) { return duration; } } return _timePerSlide; } void SlideEventHandler::operator()(osg::Node* node, osg::NodeVisitor* nv) { osgGA::EventVisitor* ev = dynamic_cast(nv); if (ev) { if (node->getNumChildrenRequiringEventTraversal()>0) traverse(node,nv); if (ev->getActionAdapter() && !ev->getEvents().empty()) { for(osgGA::EventQueue::Events::iterator itr = ev->getEvents().begin(); itr != ev->getEvents().end(); ++itr) { handleWithCheckAgainstIgnoreHandledEventsMask(*(*itr), *(ev->getActionAdapter()), node, nv); } } } } 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(int slideNum,int layerNum) { if (!_presentationSwitch) return false; osg::notify(osg::INFO)<<"selectSlide("<getNumChildren()>0) { slideNum = _presentationSwitch->getNumChildren()-1; } if (slideNum>=static_cast(_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(int layerNum) { if (!_slideSwitch) return false; if (layerNum==LAST_POSITION && _slideSwitch->getNumChildren()>0) { layerNum = _slideSwitch->getNumChildren()-1; } if (layerNum>=static_cast(_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() { 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() { 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 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); }