#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "Frame.h" #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()->getReferenceTime(); 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() : 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::Effect_map emap = osgFX::Registry::instance()->getEffectMap(); for (osgFX::Registry::Effect_map::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->setFontResolution(13, 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_->removeChild(0, root_->getNumChildren()); osg::ref_ptr effect = effects_[selected_fx_].get(); effect->setEnabled(fxen_); effect->removeChild(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->setFontResolution(32, 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->setFontResolution(16, 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.take(); } 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.take(); } void build_world(osg::Group *root, osg::Node *scene, osgProducer::Viewer &viewer) { osg::ref_ptr effect_panel = build_gui(root); effect_panel->setScene(scene); effect_panel->rebuild(); viewer.getEventHandlerList().push_front(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. osgProducer::Viewer viewer(arguments); // set up the value with sensible default event handlers. viewer.setUpViewer(osgProducer::Viewer::STANDARD_SETTINGS); // get details on keyboard and mouse bindings used by the viewer. viewer.getUsage(*arguments.getApplicationUsage()); // if user request help write it out to cout. if (arguments.read("-h") || arguments.read("--help")) { arguments.getApplicationUsage()->write(std::cout); return 1; } // any option left unread are converted into errors to write out later. arguments.reportRemainingOptionsAsUnrecognized(); // report any errors if they have occured when parsing the program aguments. if (arguments.errors()) { arguments.writeErrorMessages(std::cout); return 1; } if (arguments.argc() <= 1) { arguments.getApplicationUsage()->write(std::cout, osg::ApplicationUsage::COMMAND_LINE_OPTION); return 1; } osg::Timer timer; osg::Timer_t start_tick = timer.tick(); // read the scene from the list of file specified commandline args. osg::ref_ptr loadedModel = osgDB::readNodeFiles(arguments); // if no model has been successfully loaded report failure. if (!loadedModel) { std::cout << arguments.getApplicationName() << ": No data loaded" << std::endl; return 1; } osg::Timer_t end_tick = timer.tick(); std::cout << "Time to load = " << timer.delta_s(start_tick,end_tick) << std::endl; // optimize the scene graph, remove rendundent 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()); // create the windows and run the threads. viewer.realize(); while(!viewer.done()) { // wait for all cull and draw threads to complete. viewer.sync(); // update the scene by traversing it with the the update visitor which will // call all node update callbacks and animations. viewer.update(); // fire off the cull and draw traversals of the scene. viewer.frame(); } // wait for all cull and draw threads to complete before exit. viewer.sync(); return 0; }