| 1 | #include <osg/Group> |
|---|
| 2 | #include <osg/Geometry> |
|---|
| 3 | #include <osg/Geode> |
|---|
| 4 | #include <osg/Projection> |
|---|
| 5 | #include <osg/MatrixTransform> |
|---|
| 6 | #include <osg/BlendFunc> |
|---|
| 7 | #include <osg/LightSource> |
|---|
| 8 | |
|---|
| 9 | #include <osgViewer/Viewer> |
|---|
| 10 | |
|---|
| 11 | #include <osgDB/ReadFile> |
|---|
| 12 | #include <osgDB/WriteFile> |
|---|
| 13 | |
|---|
| 14 | #include <osgText/Text> |
|---|
| 15 | |
|---|
| 16 | #include <osgUtil/Optimizer> |
|---|
| 17 | |
|---|
| 18 | #include <osgGA/GUIEventAdapter> |
|---|
| 19 | #include <osgGA/GUIActionAdapter> |
|---|
| 20 | |
|---|
| 21 | #include <osgFX/Registry> |
|---|
| 22 | #include <osgFX/Effect> |
|---|
| 23 | |
|---|
| 24 | #include "Frame.h" |
|---|
| 25 | |
|---|
| 26 | #include <vector> |
|---|
| 27 | #include <string> |
|---|
| 28 | #include <iostream> |
|---|
| 29 | |
|---|
| 30 | class RotateCallback: public osg::NodeCallback { |
|---|
| 31 | public: |
|---|
| 32 | RotateCallback(): osg::NodeCallback(), enabled_(true) {} |
|---|
| 33 | void operator()(osg::Node* node, osg::NodeVisitor *nv) |
|---|
| 34 | { |
|---|
| 35 | osg::MatrixTransform *xform = dynamic_cast<osg::MatrixTransform *>(node); |
|---|
| 36 | if (xform && enabled_) { |
|---|
| 37 | double t = nv->getFrameStamp()->getSimulationTime(); |
|---|
| 38 | xform->setMatrix(osg::Matrix::rotate(t, osg::Vec3(0, 0, 1))); |
|---|
| 39 | } |
|---|
| 40 | traverse(node, nv); |
|---|
| 41 | } |
|---|
| 42 | |
|---|
| 43 | bool enabled_; |
|---|
| 44 | }; |
|---|
| 45 | |
|---|
| 46 | |
|---|
| 47 | |
|---|
| 48 | |
|---|
| 49 | RotateCallback *rotate_cb; |
|---|
| 50 | |
|---|
| 51 | |
|---|
| 52 | class EffectPanel: public osgfxbrowser::Frame { |
|---|
| 53 | public: |
|---|
| 54 | |
|---|
| 55 | class KeyboardHandler: public osgGA::GUIEventHandler { |
|---|
| 56 | public: |
|---|
| 57 | KeyboardHandler(EffectPanel* ep): ep_(ep) {} |
|---|
| 58 | |
|---|
| 59 | bool handle(const osgGA::GUIEventAdapter &ea, osgGA::GUIActionAdapter &) |
|---|
| 60 | { |
|---|
| 61 | if (ea.getEventType() == osgGA::GUIEventAdapter::KEYDOWN) { |
|---|
| 62 | if (ea.getKey() == osgGA::GUIEventAdapter::KEY_Right) { |
|---|
| 63 | ep_->setEffectIndex(ep_->getEffectIndex()+1); |
|---|
| 64 | return true; |
|---|
| 65 | } |
|---|
| 66 | if (ea.getKey() == osgGA::GUIEventAdapter::KEY_Left) { |
|---|
| 67 | ep_->setEffectIndex(ep_->getEffectIndex()-1); |
|---|
| 68 | return true; |
|---|
| 69 | } |
|---|
| 70 | if (ea.getKey() == osgGA::GUIEventAdapter::KEY_Return) { |
|---|
| 71 | ep_->setNodeMask(0xffffffff - ep_->getNodeMask()); |
|---|
| 72 | return true; |
|---|
| 73 | } |
|---|
| 74 | if (ea.getKey() == osgGA::GUIEventAdapter::KEY_Delete) { |
|---|
| 75 | ep_->setEffectsEnabled(!ep_->getEffectsEnabled()); |
|---|
| 76 | return true; |
|---|
| 77 | } |
|---|
| 78 | if (ea.getKey() == 'x') { |
|---|
| 79 | osgDB::writeNodeFile(*ep_->getRoot(), "osgfx_model.osg"); |
|---|
| 80 | std::cout << "written nodes to \"osgfx_model.osg\"\n"; |
|---|
| 81 | return true; |
|---|
| 82 | } |
|---|
| 83 | if (ea.getKey() == 'r') { |
|---|
| 84 | rotate_cb->enabled_ = !rotate_cb->enabled_; |
|---|
| 85 | return true; |
|---|
| 86 | } |
|---|
| 87 | } |
|---|
| 88 | |
|---|
| 89 | return false; |
|---|
| 90 | } |
|---|
| 91 | |
|---|
| 92 | private: |
|---|
| 93 | osg::ref_ptr<EffectPanel> ep_; |
|---|
| 94 | }; |
|---|
| 95 | |
|---|
| 96 | EffectPanel() |
|---|
| 97 | : osgfxbrowser::Frame(), |
|---|
| 98 | _selected_fx(-1), |
|---|
| 99 | _fxen(true), |
|---|
| 100 | _root(new osg::Group), |
|---|
| 101 | _hints_color(0.75f, 0.75f, 0.75f, 1.0f), |
|---|
| 102 | _name_color(1, 1, 1, 1), |
|---|
| 103 | _desc_color(1, 1, 0.7f, 1) |
|---|
| 104 | { |
|---|
| 105 | setBackgroundColor(osg::Vec4(0.3f, 0.1f, 0.15f, 0.75f)); |
|---|
| 106 | |
|---|
| 107 | std::cout << "INFO: available osgFX effects:\n"; |
|---|
| 108 | osgFX::Registry::EffectMap emap = osgFX::Registry::instance()->getEffectMap(); |
|---|
| 109 | for (osgFX::Registry::EffectMap::const_iterator i=emap.begin(); i!=emap.end(); ++i) { |
|---|
| 110 | std::cout << "INFO: \t" << i->first << "\n"; |
|---|
| 111 | osg::ref_ptr<osgFX::Effect> effect = static_cast<osgFX::Effect *>(i->second->cloneType()); |
|---|
| 112 | _effects.push_back(effect.get()); |
|---|
| 113 | } |
|---|
| 114 | |
|---|
| 115 | std::cout << "INFO: " << emap.size() << " effect(s) ready.\n"; |
|---|
| 116 | |
|---|
| 117 | if (!_effects.empty()) { |
|---|
| 118 | _selected_fx = 0; |
|---|
| 119 | } |
|---|
| 120 | } |
|---|
| 121 | |
|---|
| 122 | inline osg::Group* getRoot() { return _root.get(); } |
|---|
| 123 | inline void setRoot(osg::Group* node) { _root = node; } |
|---|
| 124 | |
|---|
| 125 | inline osg::Node* getScene() { return _scene.get(); } |
|---|
| 126 | inline void setScene(osg::Node* node) { _scene = node; } |
|---|
| 127 | |
|---|
| 128 | inline bool getEffectsEnabled() const { return _fxen; } |
|---|
| 129 | inline void setEffectsEnabled(bool v) |
|---|
| 130 | { |
|---|
| 131 | _fxen = v; |
|---|
| 132 | if (getSelectedEffect()) { |
|---|
| 133 | getSelectedEffect()->setEnabled(_fxen); |
|---|
| 134 | } |
|---|
| 135 | } |
|---|
| 136 | |
|---|
| 137 | inline int getEffectIndex() const { return _selected_fx; } |
|---|
| 138 | inline void setEffectIndex(int i) |
|---|
| 139 | { |
|---|
| 140 | if (i >= static_cast<int>(_effects.size())) i = 0; |
|---|
| 141 | if (i < 0) i = static_cast<int>(_effects.size()-1); |
|---|
| 142 | _selected_fx = i; |
|---|
| 143 | rebuild(); |
|---|
| 144 | } |
|---|
| 145 | |
|---|
| 146 | inline osgFX::Effect *getSelectedEffect() |
|---|
| 147 | { |
|---|
| 148 | if (_selected_fx >= 0 && _selected_fx < static_cast<int>(_effects.size())) { |
|---|
| 149 | return _effects[_selected_fx].get(); |
|---|
| 150 | } |
|---|
| 151 | return 0; |
|---|
| 152 | } |
|---|
| 153 | |
|---|
| 154 | protected: |
|---|
| 155 | void rebuild_client_area(const osgfxbrowser::Rect &client_rect) |
|---|
| 156 | { |
|---|
| 157 | float zPos = -0.1; |
|---|
| 158 | |
|---|
| 159 | |
|---|
| 160 | osg::ref_ptr<osgText::Font> arial = osgText::readFontFile("fonts/arial.ttf"); |
|---|
| 161 | |
|---|
| 162 | osg::ref_ptr<osgText::Text> hints = new osgText::Text; |
|---|
| 163 | hints->setFont(arial.get()); |
|---|
| 164 | hints->setColor(_hints_color); |
|---|
| 165 | hints->setAlignment(osgText::Text::CENTER_BOTTOM); |
|---|
| 166 | hints->setCharacterSize(13); |
|---|
| 167 | hints->setPosition(osg::Vec3((client_rect.x0+client_rect.x1)/2, client_rect.y0 + 4, zPos)); |
|---|
| 168 | hints->setText("<RETURN> show/hide this panel <LEFT> previous effect <RIGHT> next effect <DEL> enable/disable effects 'x' save to file 'r' rotate/stop"); |
|---|
| 169 | addDrawable(hints.get()); |
|---|
| 170 | |
|---|
| 171 | std::string effect_name = "No Effect Selected"; |
|---|
| 172 | std::string effect_description = ""; |
|---|
| 173 | |
|---|
| 174 | if (_selected_fx >= 0 && _selected_fx < static_cast<int>(_effects.size())) { |
|---|
| 175 | effect_name = _effects[_selected_fx]->effectName(); |
|---|
| 176 | std::string author_name = _effects[_selected_fx]->effectAuthor(); |
|---|
| 177 | if (!author_name.empty()) { |
|---|
| 178 | effect_description = author_name = "AUTHOR: " + std::string(_effects[_selected_fx]->effectAuthor()) + std::string("\n\n"); |
|---|
| 179 | } |
|---|
| 180 | effect_description += "DESCRIPTION:\n" + std::string(_effects[_selected_fx]->effectDescription()); |
|---|
| 181 | |
|---|
| 182 | if (_scene.valid() && _root.valid()) { |
|---|
| 183 | _root->removeChildren(0, _root->getNumChildren()); |
|---|
| 184 | osg::ref_ptr<osgFX::Effect> effect = _effects[_selected_fx].get(); |
|---|
| 185 | effect->setEnabled(_fxen); |
|---|
| 186 | effect->removeChildren(0, effect->getNumChildren()); |
|---|
| 187 | effect->addChild(_scene.get()); |
|---|
| 188 | effect->setUpDemo(); |
|---|
| 189 | _root->addChild(effect.get()); |
|---|
| 190 | } |
|---|
| 191 | } |
|---|
| 192 | |
|---|
| 193 | osg::ref_ptr<osgText::Text> ename = new osgText::Text; |
|---|
| 194 | ename->setFont(arial.get()); |
|---|
| 195 | ename->setColor(_name_color); |
|---|
| 196 | ename->setAlignment(osgText::Text::CENTER_TOP); |
|---|
| 197 | ename->setCharacterSize(32); |
|---|
| 198 | ename->setPosition(osg::Vec3((client_rect.x0 + client_rect.x1) / 2, client_rect.y1 - 22, zPos)); |
|---|
| 199 | ename->setText(effect_name); |
|---|
| 200 | addDrawable(ename.get()); |
|---|
| 201 | |
|---|
| 202 | osg::ref_ptr<osgText::Text> edesc = new osgText::Text; |
|---|
| 203 | edesc->setMaximumWidth(client_rect.width() - 16); |
|---|
| 204 | edesc->setFont(arial.get()); |
|---|
| 205 | edesc->setColor(_desc_color); |
|---|
| 206 | edesc->setAlignment(osgText::Text::LEFT_TOP); |
|---|
| 207 | edesc->setCharacterSize(16); |
|---|
| 208 | edesc->setPosition(osg::Vec3(client_rect.x0 + 8, client_rect.y1 - 60, zPos)); |
|---|
| 209 | edesc->setText(effect_description); |
|---|
| 210 | addDrawable(edesc.get()); |
|---|
| 211 | } |
|---|
| 212 | |
|---|
| 213 | private: |
|---|
| 214 | int _selected_fx; |
|---|
| 215 | typedef std::vector<osg::ref_ptr<osgFX::Effect> > Effect_list; |
|---|
| 216 | Effect_list _effects; |
|---|
| 217 | bool _fxen; |
|---|
| 218 | osg::ref_ptr<osg::Group> _root; |
|---|
| 219 | osg::ref_ptr<osg::Node> _scene; |
|---|
| 220 | osg::Vec4 _hints_color; |
|---|
| 221 | osg::Vec4 _name_color; |
|---|
| 222 | osg::Vec4 _desc_color; |
|---|
| 223 | }; |
|---|
| 224 | |
|---|
| 225 | |
|---|
| 226 | osg::Group* build_hud_base(osg::Group* root) |
|---|
| 227 | { |
|---|
| 228 | osg::ref_ptr<osg::Projection> proj = new osg::Projection(osg::Matrix::ortho2D(0, 1024, 0, 768)); |
|---|
| 229 | proj->setCullingActive(false); |
|---|
| 230 | root->addChild(proj.get()); |
|---|
| 231 | |
|---|
| 232 | osg::ref_ptr<osg::MatrixTransform> xform = new osg::MatrixTransform(osg::Matrix::identity()); |
|---|
| 233 | xform->setReferenceFrame(osg::Transform::ABSOLUTE_RF); |
|---|
| 234 | proj->addChild(xform.get()); |
|---|
| 235 | |
|---|
| 236 | osg::StateSet *ss = xform->getOrCreateStateSet(); |
|---|
| 237 | ss->setRenderBinDetails(100, "RenderBin"); |
|---|
| 238 | ss->setMode(GL_LIGHTING, osg::StateAttribute::OFF); |
|---|
| 239 | ss->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF); |
|---|
| 240 | |
|---|
| 241 | osg::ref_ptr<osg::BlendFunc> bf = new osg::BlendFunc; |
|---|
| 242 | ss->setAttributeAndModes(bf.get()); |
|---|
| 243 | |
|---|
| 244 | return xform.release(); |
|---|
| 245 | } |
|---|
| 246 | |
|---|
| 247 | EffectPanel* build_gui(osg::Group* root) |
|---|
| 248 | { |
|---|
| 249 | osg::ref_ptr<osg::Group> hud = build_hud_base(root); |
|---|
| 250 | |
|---|
| 251 | osg::ref_ptr<EffectPanel> effect_panel = new EffectPanel; |
|---|
| 252 | effect_panel->setCaption("osgFX Effect Browser"); |
|---|
| 253 | effect_panel->setRect(osgfxbrowser::Rect(20, 20, 1000, 280)); |
|---|
| 254 | |
|---|
| 255 | hud->addChild(effect_panel.get()); |
|---|
| 256 | |
|---|
| 257 | return effect_panel.release(); |
|---|
| 258 | } |
|---|
| 259 | |
|---|
| 260 | void build_world(osg::Group* root, osg::Node* scene, osgViewer::Viewer& viewer) |
|---|
| 261 | { |
|---|
| 262 | osg::ref_ptr<EffectPanel> effect_panel = build_gui(root); |
|---|
| 263 | effect_panel->setScene(scene); |
|---|
| 264 | effect_panel->rebuild(); |
|---|
| 265 | |
|---|
| 266 | viewer.addEventHandler(new EffectPanel::KeyboardHandler(effect_panel.get())); |
|---|
| 267 | |
|---|
| 268 | root->addChild(effect_panel->getRoot()); |
|---|
| 269 | } |
|---|
| 270 | |
|---|
| 271 | int main(int argc, char *argv[]) |
|---|
| 272 | { |
|---|
| 273 | |
|---|
| 274 | osg::ArgumentParser arguments(&argc, argv); |
|---|
| 275 | |
|---|
| 276 | |
|---|
| 277 | arguments.getApplicationUsage()->setApplicationName(arguments.getApplicationName()); |
|---|
| 278 | arguments.getApplicationUsage()->setDescription(arguments.getApplicationName() + " is a simple browser that allows you to apply osgFX effects to models interactively."); |
|---|
| 279 | arguments.getApplicationUsage()->setCommandLineUsage(arguments.getApplicationName() + " [options] filename ..."); |
|---|
| 280 | arguments.getApplicationUsage()->addCommandLineOption("-h or --help", "Display this information"); |
|---|
| 281 | arguments.getApplicationUsage()->addKeyboardMouseBinding("Left", "Apply previous effect"); |
|---|
| 282 | arguments.getApplicationUsage()->addKeyboardMouseBinding("Right", "Apply next effect"); |
|---|
| 283 | arguments.getApplicationUsage()->addKeyboardMouseBinding("Del", "Enable or disable osgFX"); |
|---|
| 284 | arguments.getApplicationUsage()->addKeyboardMouseBinding("Return", "Show or hide the effect information panel"); |
|---|
| 285 | arguments.getApplicationUsage()->addKeyboardMouseBinding("x", "Save the scene graph with current effect applied"); |
|---|
| 286 | |
|---|
| 287 | |
|---|
| 288 | |
|---|
| 289 | osgViewer::Viewer viewer; |
|---|
| 290 | |
|---|
| 291 | |
|---|
| 292 | if (arguments.read("-h") || arguments.read("--help")) { |
|---|
| 293 | arguments.getApplicationUsage()->write(std::cout); |
|---|
| 294 | return 1; |
|---|
| 295 | } |
|---|
| 296 | |
|---|
| 297 | osgViewer::Viewer::ThreadingModel threading = osgViewer::Viewer::SingleThreaded; |
|---|
| 298 | while (arguments.read("--SingleThreaded")) threading = osgViewer::Viewer::SingleThreaded; |
|---|
| 299 | while (arguments.read("--CullDrawThreadPerContext")) threading = osgViewer::Viewer::CullDrawThreadPerContext; |
|---|
| 300 | while (arguments.read("--DrawThreadPerContext")) threading = osgViewer::Viewer::DrawThreadPerContext; |
|---|
| 301 | while (arguments.read("--CullThreadPerCameraDrawThreadPerContext")) threading = osgViewer::Viewer::CullThreadPerCameraDrawThreadPerContext; |
|---|
| 302 | |
|---|
| 303 | viewer.setThreadingModel(threading); |
|---|
| 304 | |
|---|
| 305 | |
|---|
| 306 | arguments.reportRemainingOptionsAsUnrecognized(); |
|---|
| 307 | |
|---|
| 308 | |
|---|
| 309 | if (arguments.errors()) { |
|---|
| 310 | arguments.writeErrorMessages(std::cout); |
|---|
| 311 | return 1; |
|---|
| 312 | } |
|---|
| 313 | |
|---|
| 314 | |
|---|
| 315 | osg::ref_ptr<osg::Node> loadedModel = osgDB::readNodeFiles(arguments); |
|---|
| 316 | |
|---|
| 317 | |
|---|
| 318 | if (!loadedModel) loadedModel = osgDB::readNodeFile("dumptruck.osg"); |
|---|
| 319 | |
|---|
| 320 | if (!loadedModel) |
|---|
| 321 | { |
|---|
| 322 | std::cout << arguments.getApplicationName() <<": No data loaded" << std::endl; |
|---|
| 323 | return 1; |
|---|
| 324 | } |
|---|
| 325 | |
|---|
| 326 | |
|---|
| 327 | osgUtil::Optimizer optimizer; |
|---|
| 328 | optimizer.optimize(loadedModel.get()); |
|---|
| 329 | |
|---|
| 330 | |
|---|
| 331 | osg::ref_ptr<osg::MatrixTransform> xform = new osg::MatrixTransform; |
|---|
| 332 | rotate_cb = new RotateCallback; |
|---|
| 333 | xform->setUpdateCallback(rotate_cb); |
|---|
| 334 | xform->addChild(loadedModel.get()); |
|---|
| 335 | |
|---|
| 336 | osg::ref_ptr<osg::Light> light = new osg::Light; |
|---|
| 337 | light->setLightNum(0); |
|---|
| 338 | light->setDiffuse(osg::Vec4(1, 1, 1, 1)); |
|---|
| 339 | light->setSpecular(osg::Vec4(1, 1, 0.8f, 1)); |
|---|
| 340 | light->setAmbient(osg::Vec4(0.2f, 0.2f, 0.2f, 0.2f)); |
|---|
| 341 | light->setPosition(osg::Vec4(1, -1, 1, 0)); |
|---|
| 342 | |
|---|
| 343 | osg::ref_ptr<osg::LightSource> root = new osg::LightSource; |
|---|
| 344 | root->setLight(light.get()); |
|---|
| 345 | root->setLocalStateSetModes(); |
|---|
| 346 | |
|---|
| 347 | build_world(root.get(), xform.get(), viewer); |
|---|
| 348 | |
|---|
| 349 | |
|---|
| 350 | viewer.setSceneData(root.get()); |
|---|
| 351 | |
|---|
| 352 | return viewer.run(); |
|---|
| 353 | } |
|---|