/* OpenSceneGraph example, osgfxbrowser. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "Frame.h" #include #include #include class RotateCallback: public osg::NodeCallback { public: RotateCallback(): osg::NodeCallback(), enabled_(true) {} void operator()(osg::Node* node, osg::NodeVisitor *nv) { osg::MatrixTransform *xform = dynamic_cast(node); if (xform && enabled_) { double t = nv->getFrameStamp()->getSimulationTime(); xform->setMatrix(osg::Matrix::rotate(t, osg::Vec3(0, 0, 1))); } traverse(node, nv); } bool enabled_; }; // yes, I know global variables are not good things in C++ // but in this case it is useful... :-P RotateCallback *rotate_cb; class EffectPanel: public osgfxbrowser::Frame { public: class KeyboardHandler: public osgGA::GUIEventHandler { public: KeyboardHandler(EffectPanel* ep): ep_(ep) {} bool handle(const osgGA::GUIEventAdapter &ea, osgGA::GUIActionAdapter &) { if (ea.getEventType() == osgGA::GUIEventAdapter::KEYDOWN) { if (ea.getKey() == osgGA::GUIEventAdapter::KEY_Right) { ep_->setEffectIndex(ep_->getEffectIndex()+1); return true; } if (ea.getKey() == osgGA::GUIEventAdapter::KEY_Left) { ep_->setEffectIndex(ep_->getEffectIndex()-1); return true; } if (ea.getKey() == osgGA::GUIEventAdapter::KEY_Return) { ep_->setNodeMask(0xffffffff - ep_->getNodeMask()); return true; } if (ea.getKey() == osgGA::GUIEventAdapter::KEY_Delete) { ep_->setEffectsEnabled(!ep_->getEffectsEnabled()); return true; } if (ea.getKey() == 'x') { osgDB::writeNodeFile(*ep_->getRoot(), "osgfx_model.osg"); std::cout << "written nodes to \"osgfx_model.osg\"\n"; return true; } if (ea.getKey() == 'r') { rotate_cb->enabled_ = !rotate_cb->enabled_; return true; } } return false; } private: osg::ref_ptr ep_; }; EffectPanel() : osgfxbrowser::Frame(), _selected_fx(-1), _fxen(true), _root(new osg::Group), _hints_color(0.75f, 0.75f, 0.75f, 1.0f), _name_color(1, 1, 1, 1), _desc_color(1, 1, 0.7f, 1) { setBackgroundColor(osg::Vec4(0.3f, 0.1f, 0.15f, 0.75f)); std::cout << "INFO: available osgFX effects:\n"; osgFX::Registry::EffectMap emap = osgFX::Registry::instance()->getEffectMap(); for (osgFX::Registry::EffectMap::const_iterator i=emap.begin(); i!=emap.end(); ++i) { std::cout << "INFO: \t" << i->first << "\n"; osg::ref_ptr effect = static_cast(i->second->cloneType()); _effects.push_back(effect.get()); } std::cout << "INFO: " << emap.size() << " effect(s) ready.\n"; if (!_effects.empty()) { _selected_fx = 0; } } inline osg::Group* getRoot() { return _root.get(); } inline void setRoot(osg::Group* node) { _root = node; } inline osg::Node* getScene() { return _scene.get(); } inline void setScene(osg::Node* node) { _scene = node; } inline bool getEffectsEnabled() const { return _fxen; } inline void setEffectsEnabled(bool v) { _fxen = v; if (getSelectedEffect()) { getSelectedEffect()->setEnabled(_fxen); } } inline int getEffectIndex() const { return _selected_fx; } inline void setEffectIndex(int i) { if (i >= static_cast(_effects.size())) i = 0; if (i < 0) i = static_cast(_effects.size()-1); _selected_fx = i; rebuild(); } inline osgFX::Effect *getSelectedEffect() { if (_selected_fx >= 0 && _selected_fx < static_cast(_effects.size())) { return _effects[_selected_fx].get(); } return 0; } protected: void rebuild_client_area(const osgfxbrowser::Rect &client_rect) { float zPos = -0.1; // note from Robert, was 0.1f, but now must be -0.1f to keep text visible??#!? due // to some other change in the OSG not tracked down yet... osg::ref_ptr arial = osgText::readFontFile("fonts/arial.ttf"); osg::ref_ptr hints = new osgText::Text; hints->setFont(arial.get()); hints->setColor(_hints_color); hints->setAlignment(osgText::Text::CENTER_BOTTOM); hints->setCharacterSize(13); hints->setPosition(osg::Vec3((client_rect.x0+client_rect.x1)/2, client_rect.y0 + 4, zPos)); hints->setText(" show/hide this panel previous effect next effect enable/disable effects 'x' save to file 'r' rotate/stop"); addDrawable(hints.get()); std::string effect_name = "No Effect Selected"; std::string effect_description = ""; if (_selected_fx >= 0 && _selected_fx < static_cast(_effects.size())) { effect_name = _effects[_selected_fx]->effectName(); std::string author_name = _effects[_selected_fx]->effectAuthor(); if (!author_name.empty()) { effect_description = author_name = "AUTHOR: " + std::string(_effects[_selected_fx]->effectAuthor()) + std::string("\n\n"); } effect_description += "DESCRIPTION:\n" + std::string(_effects[_selected_fx]->effectDescription()); if (_scene.valid() && _root.valid()) { _root->removeChildren(0, _root->getNumChildren()); osg::ref_ptr effect = _effects[_selected_fx].get(); effect->setEnabled(_fxen); effect->removeChildren(0, effect->getNumChildren()); effect->addChild(_scene.get()); effect->setUpDemo(); _root->addChild(effect.get()); } } osg::ref_ptr ename = new osgText::Text; ename->setFont(arial.get()); ename->setColor(_name_color); ename->setAlignment(osgText::Text::CENTER_TOP); ename->setCharacterSize(32); ename->setPosition(osg::Vec3((client_rect.x0 + client_rect.x1) / 2, client_rect.y1 - 22, zPos)); ename->setText(effect_name); addDrawable(ename.get()); osg::ref_ptr edesc = new osgText::Text; edesc->setMaximumWidth(client_rect.width() - 16); edesc->setFont(arial.get()); edesc->setColor(_desc_color); edesc->setAlignment(osgText::Text::LEFT_TOP); edesc->setCharacterSize(16); edesc->setPosition(osg::Vec3(client_rect.x0 + 8, client_rect.y1 - 60, zPos)); edesc->setText(effect_description); addDrawable(edesc.get()); } private: int _selected_fx; typedef std::vector > Effect_list; Effect_list _effects; bool _fxen; osg::ref_ptr _root; osg::ref_ptr _scene; osg::Vec4 _hints_color; osg::Vec4 _name_color; osg::Vec4 _desc_color; }; osg::Group* build_hud_base(osg::Group* root) { osg::ref_ptr proj = new osg::Projection(osg::Matrix::ortho2D(0, 1024, 0, 768)); proj->setCullingActive(false); root->addChild(proj.get()); osg::ref_ptr xform = new osg::MatrixTransform(osg::Matrix::identity()); xform->setReferenceFrame(osg::Transform::ABSOLUTE_RF); proj->addChild(xform.get()); osg::StateSet *ss = xform->getOrCreateStateSet(); ss->setRenderBinDetails(100, "RenderBin"); ss->setMode(GL_LIGHTING, osg::StateAttribute::OFF); ss->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF); osg::ref_ptr bf = new osg::BlendFunc; ss->setAttributeAndModes(bf.get()); return xform.release(); } EffectPanel* build_gui(osg::Group* root) { osg::ref_ptr hud = build_hud_base(root); osg::ref_ptr effect_panel = new EffectPanel; effect_panel->setCaption("osgFX Effect Browser"); effect_panel->setRect(osgfxbrowser::Rect(20, 20, 1000, 280)); hud->addChild(effect_panel.get()); return effect_panel.release(); } void build_world(osg::Group* root, osg::Node* scene, osgViewer::Viewer& viewer) { osg::ref_ptr effect_panel = build_gui(root); effect_panel->setScene(scene); effect_panel->rebuild(); viewer.addEventHandler(new EffectPanel::KeyboardHandler(effect_panel.get())); root->addChild(effect_panel->getRoot()); } int main(int argc, char *argv[]) { // use an ArgumentParser object to manage the program arguments. osg::ArgumentParser arguments(&argc, argv); // set up the usage document, in case we need to print out how to use this program. arguments.getApplicationUsage()->setApplicationName(arguments.getApplicationName()); arguments.getApplicationUsage()->setDescription(arguments.getApplicationName() + " is a simple browser that allows you to apply osgFX effects to models interactively."); arguments.getApplicationUsage()->setCommandLineUsage(arguments.getApplicationName() + " [options] filename ..."); arguments.getApplicationUsage()->addCommandLineOption("-h or --help", "Display this information"); arguments.getApplicationUsage()->addKeyboardMouseBinding("Left", "Apply previous effect"); arguments.getApplicationUsage()->addKeyboardMouseBinding("Right", "Apply next effect"); arguments.getApplicationUsage()->addKeyboardMouseBinding("Del", "Enable or disable osgFX"); arguments.getApplicationUsage()->addKeyboardMouseBinding("Return", "Show or hide the effect information panel"); arguments.getApplicationUsage()->addKeyboardMouseBinding("x", "Save the scene graph with current effect applied"); // construct the viewer. osgViewer::Viewer viewer; // if user request help write it out to cout. if (arguments.read("-h") || arguments.read("--help")) { arguments.getApplicationUsage()->write(std::cout); return 1; } osgViewer::Viewer::ThreadingModel threading = osgViewer::Viewer::SingleThreaded; while (arguments.read("--SingleThreaded")) threading = osgViewer::Viewer::SingleThreaded; while (arguments.read("--CullDrawThreadPerContext")) threading = osgViewer::Viewer::CullDrawThreadPerContext; while (arguments.read("--DrawThreadPerContext")) threading = osgViewer::Viewer::DrawThreadPerContext; while (arguments.read("--CullThreadPerCameraDrawThreadPerContext")) threading = osgViewer::Viewer::CullThreadPerCameraDrawThreadPerContext; viewer.setThreadingModel(threading); // setup stencil buffer for Outline f/x. osg::DisplaySettings::instance()->setMinimumNumStencilBits(1); unsigned int clearMask = viewer.getCamera()->getClearMask(); viewer.getCamera()->setClearMask(clearMask | GL_STENCIL_BUFFER_BIT); viewer.getCamera()->setClearStencil(0); // any option left unread are converted into errors to write out later. arguments.reportRemainingOptionsAsUnrecognized(); // report any errors if they have occurred when parsing the program arguments. if (arguments.errors()) { arguments.writeErrorMessages(std::cout); return 1; } // read the scene from the list of file specified commandline args. osg::ref_ptr loadedModel = osgDB::readNodeFiles(arguments); // if not loaded assume no arguments passed in, try use default mode instead. if (!loadedModel) loadedModel = osgDB::readNodeFile("dumptruck.osg"); if (!loadedModel) { std::cout << arguments.getApplicationName() <<": No data loaded" << std::endl; return 1; } // optimize the scene graph, remove redundant nodes and state etc. osgUtil::Optimizer optimizer; optimizer.optimize(loadedModel.get()); // set up a transform to rotate the model osg::ref_ptr xform = new osg::MatrixTransform; rotate_cb = new RotateCallback; xform->setUpdateCallback(rotate_cb); xform->addChild(loadedModel.get()); osg::ref_ptr light = new osg::Light; light->setLightNum(0); light->setDiffuse(osg::Vec4(1, 1, 1, 1)); light->setSpecular(osg::Vec4(1, 1, 0.8f, 1)); light->setAmbient(osg::Vec4(0.2f, 0.2f, 0.2f, 0.2f)); light->setPosition(osg::Vec4(1, -1, 1, 0)); osg::ref_ptr root = new osg::LightSource; root->setLight(light.get()); root->setLocalStateSetModes(); build_world(root.get(), xform.get(), viewer); // set the scene to render viewer.setSceneData(root.get()); return viewer.run(); }