1 /* OpenSceneGraph example, osgfxbrowser.
2 *
3 *  Permission is hereby granted, free of charge, to any person obtaining a copy
4 *  of this software and associated documentation files (the "Software"), to deal
5 *  in the Software without restriction, including without limitation the rights
6 *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 *  copies of the Software, and to permit persons to whom the Software is
8 *  furnished to do so, subject to the following conditions:
9 *
10 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
11 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
12 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
13 *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
14 *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
15 *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
16 *  THE SOFTWARE.
17 */
18 
19 #include <osg/Group>
20 #include <osg/Geometry>
21 #include <osg/Geode>
22 #include <osg/Projection>
23 #include <osg/MatrixTransform>
24 #include <osg/BlendFunc>
25 #include <osg/LightSource>
26 
27 #include <osgViewer/Viewer>
28 
29 #include <osgDB/ReadFile>
30 #include <osgDB/WriteFile>
31 
32 #include <osgText/Text>
33 
34 #include <osgUtil/Optimizer>
35 
36 #include <osgGA/GUIEventAdapter>
37 #include <osgGA/GUIActionAdapter>
38 
39 #include <osgFX/Registry>
40 #include <osgFX/Effect>
41 
42 #include "Frame.h"
43 
44 #include <vector>
45 #include <string>
46 #include <iostream>
47 
48 class RotateCallback: public osg::NodeCallback {
49 public:
RotateCallback()50     RotateCallback(): osg::NodeCallback(), enabled_(true) {}
operator ()(osg::Node * node,osg::NodeVisitor * nv)51     void operator()(osg::Node* node, osg::NodeVisitor *nv)
52     {
53         osg::MatrixTransform *xform = dynamic_cast<osg::MatrixTransform *>(node);
54         if (xform && enabled_) {
55             double t = nv->getFrameStamp()->getSimulationTime();
56             xform->setMatrix(osg::Matrix::rotate(t, osg::Vec3(0, 0, 1)));
57         }
58         traverse(node, nv);
59     }
60 
61     bool enabled_;
62 };
63 
64 
65 // yes, I know global variables are not good things in C++
66 // but in this case it is useful... :-P
67 RotateCallback *rotate_cb;
68 
69 
70 class EffectPanel: public osgfxbrowser::Frame {
71 public:
72 
73     class KeyboardHandler: public osgGA::GUIEventHandler {
74     public:
KeyboardHandler(EffectPanel * ep)75         KeyboardHandler(EffectPanel* ep): ep_(ep) {}
76 
handle(const osgGA::GUIEventAdapter & ea,osgGA::GUIActionAdapter &)77         bool handle(const osgGA::GUIEventAdapter &ea, osgGA::GUIActionAdapter &)
78         {
79             if (ea.getEventType() == osgGA::GUIEventAdapter::KEYDOWN) {
80                 if (ea.getKey() == osgGA::GUIEventAdapter::KEY_Right) {
81                     ep_->setEffectIndex(ep_->getEffectIndex()+1);
82                     return true;
83                 }
84                 if (ea.getKey() == osgGA::GUIEventAdapter::KEY_Left) {
85                     ep_->setEffectIndex(ep_->getEffectIndex()-1);
86                     return true;
87                 }
88                 if (ea.getKey() == osgGA::GUIEventAdapter::KEY_Return) {
89                     ep_->setNodeMask(0xffffffff - ep_->getNodeMask());
90                     return true;
91                 }
92                 if (ea.getKey() == osgGA::GUIEventAdapter::KEY_Delete) {
93                     ep_->setEffectsEnabled(!ep_->getEffectsEnabled());
94                     return true;
95                 }
96                 if (ea.getKey() == 'x') {
97                     osgDB::writeNodeFile(*ep_->getRoot(), "osgfx_model.osgt");
98                     std::cout << "written nodes to \"osgfx_model.osgt\"\n";
99                     return true;
100                 }
101                 if (ea.getKey() == 'r') {
102                     rotate_cb->enabled_ = !rotate_cb->enabled_;
103                     return true;
104                 }
105             }
106 
107             return false;
108         }
109 
110     private:
111         osg::ref_ptr<EffectPanel> ep_;
112     };
113 
EffectPanel()114     EffectPanel()
115     :    osgfxbrowser::Frame(),
116         _selected_fx(-1),
117         _fxen(true),
118         _root(new osg::Group),
119         _hints_color(0.75f, 0.75f, 0.75f, 1.0f),
120         _name_color(1, 1, 1, 1),
121         _desc_color(1, 1, 0.7f, 1)
122     {
123         setBackgroundColor(osg::Vec4(0.3f, 0.1f, 0.15f, 0.75f));
124 
125         std::cout << "INFO: available osgFX effects:\n";
126         osgFX::Registry::EffectMap emap = osgFX::Registry::instance()->getEffectMap();
127         for (osgFX::Registry::EffectMap::const_iterator i=emap.begin(); i!=emap.end(); ++i) {
128             std::cout << "INFO: \t" << i->first << "\n";
129             osg::ref_ptr<osgFX::Effect> effect = static_cast<osgFX::Effect *>(i->second->cloneType());
130             _effects.push_back(effect.get());
131         }
132 
133         std::cout << "INFO: " << emap.size() << " effect(s) ready.\n";
134 
135         if (!_effects.empty()) {
136             _selected_fx = 0;
137         }
138     }
139 
getRoot()140     inline osg::Group* getRoot() { return _root.get(); }
setRoot(osg::Group * node)141     inline void setRoot(osg::Group* node) { _root = node; }
142 
getScene()143     inline osg::Node* getScene() { return _scene.get(); }
setScene(osg::Node * node)144     inline void setScene(osg::Node* node) { _scene = node; }
145 
getEffectsEnabled() const146     inline bool getEffectsEnabled() const { return _fxen; }
setEffectsEnabled(bool v)147     inline void setEffectsEnabled(bool v)
148     {
149         _fxen = v;
150         if (getSelectedEffect()) {
151             getSelectedEffect()->setEnabled(_fxen);
152         }
153     }
154 
getEffectIndex() const155     inline int getEffectIndex() const { return _selected_fx; }
setEffectIndex(int i)156     inline void setEffectIndex(int i)
157     {
158         if (i >= static_cast<int>(_effects.size())) i = 0;
159         if (i < 0) i = static_cast<int>(_effects.size()-1);
160         _selected_fx = i;
161         rebuild();
162     }
163 
getSelectedEffect()164     inline osgFX::Effect *getSelectedEffect()
165     {
166         if (_selected_fx >= 0 && _selected_fx < static_cast<int>(_effects.size())) {
167             return _effects[_selected_fx].get();
168         }
169         return 0;
170     }
171 
172 protected:
rebuild_client_area(const osgfxbrowser::Rect & client_rect)173     void rebuild_client_area(const osgfxbrowser::Rect &client_rect)
174     {
175                 float zPos = -0.1; // note from Robert, was 0.1f, but now must be -0.1f to keep text visible??#!? due
176                                    // to some other change in the OSG not tracked down yet...
177 
178         osg::ref_ptr<osgText::Font> arial = osgText::readRefFontFile("fonts/arial.ttf");
179 
180         osg::ref_ptr<osgText::Text> hints = new osgText::Text;
181         hints->setFont(arial);
182         hints->setColor(_hints_color);
183         hints->setAlignment(osgText::Text::CENTER_BOTTOM);
184         hints->setCharacterSize(13);
185         hints->setPosition(osg::Vec3((client_rect.x0+client_rect.x1)/2, client_rect.y0 + 4, zPos));
186         hints->setText("<RETURN> show/hide this panel      <LEFT> previous effect      <RIGHT> next effect      <DEL> enable/disable effects      'x' save to file      'r' rotate/stop");
187         addDrawable(hints.get());
188 
189         std::string effect_name = "No Effect Selected";
190         std::string effect_description = "";
191 
192         if (_selected_fx >= 0 && _selected_fx < static_cast<int>(_effects.size())) {
193             effect_name = _effects[_selected_fx]->effectName();
194             std::string author_name = _effects[_selected_fx]->effectAuthor();
195             if (!author_name.empty()) {
196                 effect_description = author_name = "AUTHOR: " + std::string(_effects[_selected_fx]->effectAuthor()) + std::string("\n\n");
197             }
198             effect_description += "DESCRIPTION:\n" + std::string(_effects[_selected_fx]->effectDescription());
199 
200             if (_scene.valid() && _root.valid()) {
201                 _root->removeChildren(0, _root->getNumChildren());
202                 osg::ref_ptr<osgFX::Effect> effect = _effects[_selected_fx].get();
203                 effect->setEnabled(_fxen);
204                 effect->removeChildren(0, effect->getNumChildren());
205                 effect->addChild(_scene.get());
206                 effect->setUpDemo();
207                 _root->addChild(effect.get());
208             }
209         }
210 
211         osg::ref_ptr<osgText::Text> ename = new osgText::Text;
212         ename->setFont(arial.get());
213         ename->setColor(_name_color);
214         ename->setAlignment(osgText::Text::CENTER_TOP);
215         ename->setCharacterSize(32);
216         ename->setPosition(osg::Vec3((client_rect.x0 + client_rect.x1) / 2, client_rect.y1 - 22, zPos));
217         ename->setText(effect_name);
218         addDrawable(ename.get());
219 
220         osg::ref_ptr<osgText::Text> edesc = new osgText::Text;
221         edesc->setMaximumWidth(client_rect.width() - 16);
222         edesc->setFont(arial.get());
223         edesc->setColor(_desc_color);
224         edesc->setAlignment(osgText::Text::LEFT_TOP);
225         edesc->setCharacterSize(16);
226         edesc->setPosition(osg::Vec3(client_rect.x0 + 8, client_rect.y1 - 60, zPos));
227         edesc->setText(effect_description);
228         addDrawable(edesc.get());
229     }
230 
231 private:
232     int _selected_fx;
233     typedef std::vector<osg::ref_ptr<osgFX::Effect> > Effect_list;
234     Effect_list _effects;
235     bool _fxen;
236     osg::ref_ptr<osg::Group> _root;
237     osg::ref_ptr<osg::Node> _scene;
238     osg::Vec4 _hints_color;
239     osg::Vec4 _name_color;
240     osg::Vec4 _desc_color;
241 };
242 
243 
build_hud_base(osg::Group * root)244 osg::Group* build_hud_base(osg::Group* root)
245 {
246     osg::ref_ptr<osg::Projection> proj = new osg::Projection(osg::Matrix::ortho2D(0, 1024, 0, 768));
247     proj->setCullingActive(false);
248     root->addChild(proj.get());
249 
250     osg::ref_ptr<osg::MatrixTransform> xform = new osg::MatrixTransform(osg::Matrix::identity());
251     xform->setReferenceFrame(osg::Transform::ABSOLUTE_RF);
252     proj->addChild(xform.get());
253 
254     osg::StateSet *ss = xform->getOrCreateStateSet();
255     ss->setRenderBinDetails(100, "RenderBin");
256     ss->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
257     ss->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);
258 
259     osg::ref_ptr<osg::BlendFunc> bf = new osg::BlendFunc;
260     ss->setAttributeAndModes(bf.get());
261 
262     return xform.release();
263 }
264 
build_gui(osg::Group * root)265 EffectPanel* build_gui(osg::Group* root)
266 {
267     osg::ref_ptr<osg::Group> hud = build_hud_base(root);
268 
269     osg::ref_ptr<EffectPanel> effect_panel = new EffectPanel;
270     effect_panel->setCaption("osgFX Effect Browser");
271     effect_panel->setRect(osgfxbrowser::Rect(20, 20, 1000, 280));
272 
273     hud->addChild(effect_panel.get());
274 
275     return effect_panel.release();
276 }
277 
build_world(osg::Group * root,osg::Node * scene,osgViewer::Viewer & viewer)278 void build_world(osg::Group* root, osg::Node* scene, osgViewer::Viewer& viewer)
279 {
280     osg::ref_ptr<EffectPanel> effect_panel = build_gui(root);
281     effect_panel->setScene(scene);
282     effect_panel->rebuild();
283 
284     viewer.addEventHandler(new EffectPanel::KeyboardHandler(effect_panel.get()));
285 
286     root->addChild(effect_panel->getRoot());
287 }
288 
main(int argc,char * argv[])289 int main(int argc, char *argv[])
290 {
291     // use an ArgumentParser object to manage the program arguments.
292     osg::ArgumentParser arguments(&argc, argv);
293 
294     // set up the usage document, in case we need to print out how to use this program.
295     arguments.getApplicationUsage()->setApplicationName(arguments.getApplicationName());
296     arguments.getApplicationUsage()->setDescription(arguments.getApplicationName() + " is a simple browser that allows you to apply osgFX effects to models interactively.");
297     arguments.getApplicationUsage()->setCommandLineUsage(arguments.getApplicationName() + " [options] filename ...");
298     arguments.getApplicationUsage()->addCommandLineOption("-h or --help", "Display this information");
299     arguments.getApplicationUsage()->addKeyboardMouseBinding("Left", "Apply previous effect");
300     arguments.getApplicationUsage()->addKeyboardMouseBinding("Right", "Apply next effect");
301     arguments.getApplicationUsage()->addKeyboardMouseBinding("Del", "Enable or disable osgFX");
302     arguments.getApplicationUsage()->addKeyboardMouseBinding("Return", "Show or hide the effect information panel");
303     arguments.getApplicationUsage()->addKeyboardMouseBinding("x", "Save the scene graph with current effect applied");
304 
305 
306     // construct the viewer.
307     osgViewer::Viewer viewer;
308 
309     // if user request help write it out to cout.
310     if (arguments.read("-h") || arguments.read("--help")) {
311         arguments.getApplicationUsage()->write(std::cout);
312         return 1;
313     }
314 
315     osgViewer::Viewer::ThreadingModel threading = osgViewer::Viewer::SingleThreaded;
316     while (arguments.read("--SingleThreaded")) threading = osgViewer::Viewer::SingleThreaded;
317     while (arguments.read("--CullDrawThreadPerContext")) threading = osgViewer::Viewer::CullDrawThreadPerContext;
318     while (arguments.read("--DrawThreadPerContext")) threading = osgViewer::Viewer::DrawThreadPerContext;
319     while (arguments.read("--CullThreadPerCameraDrawThreadPerContext")) threading = osgViewer::Viewer::CullThreadPerCameraDrawThreadPerContext;
320 
321     viewer.setThreadingModel(threading);
322 
323     // setup stencil buffer for Outline f/x.
324     osg::DisplaySettings::instance()->setMinimumNumStencilBits(1);
325     unsigned int clearMask = viewer.getCamera()->getClearMask();
326     viewer.getCamera()->setClearMask(clearMask | GL_STENCIL_BUFFER_BIT);
327     viewer.getCamera()->setClearStencil(0);
328 
329     // any option left unread are converted into errors to write out later.
330     arguments.reportRemainingOptionsAsUnrecognized();
331 
332     // report any errors if they have occurred when parsing the program arguments.
333     if (arguments.errors()) {
334         arguments.writeErrorMessages(std::cout);
335         return 1;
336     }
337 
338     // read the scene from the list of file specified commandline args.
339     osg::ref_ptr<osg::Node> loadedModel = osgDB::readRefNodeFiles(arguments);
340 
341     // if not loaded assume no arguments passed in, try use default mode instead.
342     if (!loadedModel) loadedModel = osgDB::readRefNodeFile("dumptruck.osgt");
343 
344     if (!loadedModel)
345     {
346         std::cout << arguments.getApplicationName() <<": No data loaded" << std::endl;
347         return 1;
348     }
349 
350     // optimize the scene graph, remove redundant nodes and state etc.
351     osgUtil::Optimizer optimizer;
352     optimizer.optimize(loadedModel.get());
353 
354     // set up a transform to rotate the model
355     osg::ref_ptr<osg::MatrixTransform> xform = new osg::MatrixTransform;
356     rotate_cb = new RotateCallback;
357     xform->setUpdateCallback(rotate_cb);
358     xform->addChild(loadedModel.get());
359 
360     osg::ref_ptr<osg::Light> light = new osg::Light;
361     light->setLightNum(0);
362     light->setDiffuse(osg::Vec4(1, 1, 1, 1));
363     light->setSpecular(osg::Vec4(1, 1, 0.8f, 1));
364     light->setAmbient(osg::Vec4(0.2f, 0.2f, 0.2f, 0.2f));
365     light->setPosition(osg::Vec4(1, -1, 1, 0));
366 
367     osg::ref_ptr<osg::LightSource> root = new osg::LightSource;
368     root->setLight(light.get());
369     root->setLocalStateSetModes();
370 
371     build_world(root.get(), xform.get(), viewer);
372 
373     // set the scene to render
374     viewer.setSceneData(root.get());
375 
376     return viewer.run();
377 }
378