root/OpenSceneGraph/trunk/examples/osgkeyboardmouse/osgkeyboardmouse.cpp @ 6734

Revision 6734, 13.9 kB (checked in by robert, 8 years ago)

From Peter Hrenka, (note from Robert Osfield, renamed GenericPrimitiveFunctor? mention below to TemplatePrimitiveFunctor?).

"Since we desperately needed a means for picking Lines
and Points I implemented (hopefully!) proper geometrical tests
for the PolytopeIntersector?.

First of all I implemented a new "GenericPrimiteFunctor?"
which is basically an extended copy TriangleFunctor? which also
handles Points, Lines and Quads through suitable overloads of
operator(). I would have liked to call it "PrimitiveFunctor?"
but that name was already used...
I used a template method to remove redundancy in the
drawElements method overloads. If you know of platforms where
this will not work I can change it to the style used
in TriangleFunctor?.

In PolytopeIntersector?.cpp I implemented a
"PolytopePrimitiveIntersector?" which provides the needed
overloads for Points, Lines, Triangles and Quads to
the GenericPrimitiveFunctor?. This is then used in the
intersect method of PolytopeIntersector?.

Implementation summary:
- Points: Check distance to all planes
- Lines: Check distance of both ends against each plane.

If both are outside -> line is out
If both are in -> continue checking
One is in, one is out -> compute intersection point (candidate)
Then check all candidates against all other polytope
planes. The remaining candidates are the proper
intersection points of the line with the polytope.

- Triangles: Perform Line-Checks for all edges of the

triangle as above. If there is an proper intersection
-> done.
In the case where there are more than 2 polytope
plane to check against we have to check for the case
where the triangle encloses the polytope.
In that case the intersection lines of the polytope
planes are computed and checked against the triangle.

- Quads: handled as two triangles.

This is implementation is certainly not the fastest.
There are certainly ways and strategies to improve it.

I also enabled the code for PolytopeIntersector?
in osgkeyboardmouse and added keybindings to
switch the type of intersector ('p') and the picking
coordinate system ('c') on the fly. Since the
PolytopeIntersector? does not have a canonical
ordering for its intersections (as opposed to
the LineSegementIntersector?) I chaged the
implementation to toggle all hit geometries.

I tested the functionality with osgkeyboardmouse
and several models and it seems to work for
polygonal models. Special nodes such as billboards
do not work.

The next thing on my todo-list is to implement
a an improved Intersection-Structure for the
PolytopeIntersector?. We need to know
which primitives where hit (and where).

"

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1// C++ source file - (C) 2003 Robert Osfield, released under the OSGPL.
2//
3// Simple example of use of osgViewer::GraphicsWindow + SimpleViewer
4// example that provides the user with control over view position with basic picking.
5
6#include <osg/Timer>
7#include <osg/io_utils>
8#include <osg/observer_ptr>
9
10#include <osgUtil/IntersectionVisitor>
11#include <osgUtil/PolytopeIntersector>
12#include <osgUtil/LineSegmentIntersector>
13
14#include <osgDB/ReadFile>
15#include <osgDB/WriteFile>
16
17#include <osgGA/TrackballManipulator>
18#include <osgGA/StateSetManipulator>
19
20#include <osgViewer/SimpleViewer>
21#include <osgViewer/GraphicsWindow>
22
23#include <osgFX/Scribe>
24
25#include <iostream>
26
27class CreateModelToSaveVisitor : public osg::NodeVisitor
28{
29public:
30
31    CreateModelToSaveVisitor():
32        osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN)       
33    {
34        _group = new osg::Group;
35        _addToModel = false;
36    }
37   
38    virtual void apply(osg::Node& node)
39    {
40        osgFX::Scribe* scribe = dynamic_cast<osgFX::Scribe*>(&node);
41        if (scribe)
42        {
43            for(unsigned int i=0; i<scribe->getNumChildren(); ++i)
44            {
45                _group->addChild(scribe->getChild(i));
46            }
47        }
48        else
49        {
50            traverse(node);
51        }
52    }
53   
54    osg::ref_ptr<osg::Group> _group;
55    bool _addToModel;
56};
57
58class DeleteSelectedNodesVisitor : public osg::NodeVisitor
59{
60public:
61
62    DeleteSelectedNodesVisitor():
63        osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN)       
64    {
65    }
66   
67    virtual void apply(osg::Node& node)
68    {
69        osgFX::Scribe* scribe = dynamic_cast<osgFX::Scribe*>(&node);
70        if (scribe)
71        {
72            _selectedNodes.push_back(scribe);
73        }
74        else
75        {
76            traverse(node);
77        }
78    }
79   
80    void pruneSelectedNodes()
81    {
82        for(SelectedNodes::iterator itr = _selectedNodes.begin();
83            itr != _selectedNodes.end();
84            ++itr)
85        {
86            osg::Node* node = itr->get();
87            osg::Node::ParentList parents = node->getParents();
88            for(osg::Node::ParentList::iterator pitr = parents.begin();
89                pitr != parents.end();
90                ++pitr)
91            {
92                osg::Group* parent = *pitr;
93                parent->removeChild(node);
94            }
95        }
96    }
97   
98    typedef std::vector< osg::ref_ptr<osgFX::Scribe> > SelectedNodes;
99    SelectedNodes _selectedNodes;
100   
101};
102
103class ExitHandler : public osgGA::GUIEventHandler
104{
105public:
106
107    ExitHandler():
108        _done(false) {}
109       
110    bool done() const { return _done; }   
111
112    bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter&)
113    {
114        switch(ea.getEventType())
115        {
116            case(osgGA::GUIEventAdapter::KEYUP):
117            {
118                if (ea.getKey()==osgGA::GUIEventAdapter::KEY_Escape)
119                {
120                    _done = true;
121                }
122                return false;
123            }
124            case(osgGA::GUIEventAdapter::CLOSE_WINDOW):
125            case(osgGA::GUIEventAdapter::QUIT_APPLICATION):
126            {
127                _done = true;
128            }
129            default: break;
130        }
131       
132        return false;
133    }
134   
135    bool _done;
136};
137
138
139
140// class to handle events with a pick
141class PickHandler : public osgGA::GUIEventHandler
142{
143public:
144
145    PickHandler():
146        _mx(0.0),_my(0.0),
147        _usePolytopeIntersector(false),
148        _useWindowCoordinates(false) {}
149
150    ~PickHandler() {}
151
152    bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa)
153    {
154        osgViewer::SimpleViewer* viewer = dynamic_cast<osgViewer::SimpleViewer*>(&aa);
155        if (!viewer) return false;
156
157        switch(ea.getEventType())
158        {
159            case(osgGA::GUIEventAdapter::KEYUP):
160            {
161                if (ea.getKey()=='s')
162                {
163                    saveSelectedModel(viewer->getSceneData());
164                }               
165                else if (ea.getKey()=='o')
166                {
167                    osg::notify(osg::NOTICE)<<"Saved model to file 'saved_model.osg'"<<std::endl;
168                    osgDB::writeNodeFile(*(viewer->getSceneData()), "saved_model.osg");
169                }
170                else if (ea.getKey()=='p')
171                {
172                    _usePolytopeIntersector = !_usePolytopeIntersector;
173                    if (_usePolytopeIntersector)
174                    {
175                        osg::notify(osg::NOTICE)<<"Using PolytopeIntersector"<<std::endl;
176                    } else {
177                        osg::notify(osg::NOTICE)<<"Using LineSegmentIntersector"<<std::endl;
178                    }
179                }
180                else if (ea.getKey()=='c')
181                {
182                    _useWindowCoordinates = !_useWindowCoordinates;
183                    if (_useWindowCoordinates)
184                    {
185                        osg::notify(osg::NOTICE)<<"Using window coordinates for picking"<<std::endl;
186                    } else {
187                        osg::notify(osg::NOTICE)<<"Using projection coordiates for picking"<<std::endl;
188                    }
189                }
190                else if (ea.getKey()==osgGA::GUIEventAdapter::KEY_Delete || ea.getKey()==osgGA::GUIEventAdapter::KEY_BackSpace)
191                {
192                    osg::notify(osg::NOTICE)<<"Delete"<<std::endl;
193                    DeleteSelectedNodesVisitor dsnv;
194                    viewer->getSceneData()->accept(dsnv);
195                    dsnv.pruneSelectedNodes();
196                }
197                return false;
198            }
199            case(osgGA::GUIEventAdapter::PUSH):
200            case(osgGA::GUIEventAdapter::MOVE):
201            {
202                _mx = ea.getX();
203                _my = ea.getY();
204                return false;
205            }
206            case(osgGA::GUIEventAdapter::RELEASE):
207            {
208                if (_mx == ea.getX() && _my == ea.getY())
209                {
210                    // only do a pick if the mouse hasn't moved
211                    pick(ea,viewer);
212                }
213                return true;
214            }   
215
216            default:
217                return false;
218        }
219    }
220
221    void pick(const osgGA::GUIEventAdapter& ea, osgViewer::SimpleViewer* viewer)
222    {
223        osg::Node* scene = viewer->getSceneData();
224        if (!scene) return;
225
226        osg::notify(osg::NOTICE)<<std::endl;
227
228        osg::Node* node = 0;
229        osg::Group* parent = 0;
230
231        if (_usePolytopeIntersector)
232        {
233            osgUtil::PolytopeIntersector* picker;
234            if (_useWindowCoordinates)
235            {
236                // use window coordinates
237                // remap the mouse x,y into viewport coordinates.
238                osg::Viewport* viewport = viewer->getCamera()->getViewport();
239                double mx = viewport->x() + (int)((double )viewport->width()*(ea.getXnormalized()*0.5+0.5));
240                double my = viewport->y() + (int)((double )viewport->height()*(ea.getYnormalized()*0.5+0.5));
241
242                // half width, height.
243                double w = 5.0f;
244                double h = 5.0f;
245                picker = new osgUtil::PolytopeIntersector( osgUtil::Intersector::WINDOW, mx-w, my-h, mx+w, my+h );
246            } else {
247                double mx = ea.getXnormalized();
248                double my = ea.getYnormalized();
249                double w = 0.05;
250                double h = 0.05;
251                picker = new osgUtil::PolytopeIntersector( osgUtil::Intersector::PROJECTION, mx-w, my-h, mx+w, my+h );
252            }
253            osgUtil::IntersectionVisitor iv(picker);
254
255            viewer->getCamera()->accept(iv);
256
257            if (picker->containsIntersections())
258            {
259                osgUtil::PolytopeIntersector::Intersections& intersections = picker->getIntersections();
260
261                for (osgUtil::PolytopeIntersector::Intersections::iterator it=intersections.begin();
262                     it!=intersections.end(); ++it) {
263                    osgUtil::PolytopeIntersector::Intersection intersection=*it;
264
265                    osg::NodePath& nodePath = intersection.nodePath;
266                    node = (nodePath.size()>=1)?nodePath[nodePath.size()-1]:0;
267                    parent = (nodePath.size()>=2)?dynamic_cast<osg::Group*>(nodePath[nodePath.size()-2]):0;
268
269                    if (node) std::cout<<"  Hits "<<node->className()<<" nodePath size"<<nodePath.size()<<std::endl;
270                    toggleScribe(parent, node);
271                }
272
273            }
274
275        }
276        else
277        {
278            osgUtil::LineSegmentIntersector* picker;
279            if (!_useWindowCoordinates)
280            {
281                // use non dimensional coordinates - in projection/clip space
282                picker = new osgUtil::LineSegmentIntersector( osgUtil::Intersector::PROJECTION, ea.getXnormalized(),ea.getYnormalized() );
283            } else {
284                // use window coordinates
285                // remap the mouse x,y into viewport coordinates.
286                osg::Viewport* viewport = viewer->getCamera()->getViewport();
287                float mx = viewport->x() + (int)((float)viewport->width()*(ea.getXnormalized()*0.5f+0.5f));
288                float my = viewport->y() + (int)((float)viewport->height()*(ea.getYnormalized()*0.5f+0.5f));
289                picker = new osgUtil::LineSegmentIntersector( osgUtil::Intersector::WINDOW, mx, my );
290            }
291            osgUtil::IntersectionVisitor iv(picker);
292
293            viewer->getCamera()->accept(iv);
294
295            if (picker->containsIntersections())
296            {
297                osgUtil::LineSegmentIntersector::Intersection intersection = picker->getFirstIntersection();
298                osg::notify(osg::NOTICE)<<"Picked "<<intersection.localIntersectionPoint<<std::endl;
299
300                osg::NodePath& nodePath = intersection.nodePath;
301                node = (nodePath.size()>=1)?nodePath[nodePath.size()-1]:0;
302                parent = (nodePath.size()>=2)?dynamic_cast<osg::Group*>(nodePath[nodePath.size()-2]):0;
303
304                if (node) std::cout<<"  Hits "<<node->className()<<" nodePath size"<<nodePath.size()<<std::endl;
305                toggleScribe(parent, node);
306            }
307        }       
308
309        // now we try to decorate the hit node by the osgFX::Scribe to show that its been "picked"
310    }
311
312    void toggleScribe(osg::Group* parent, osg::Node* node) {
313        if (!parent || !node) return;
314
315        std::cout<<"  parent "<<parent->className()<<std::endl;
316
317        osgFX::Scribe* parentAsScribe = dynamic_cast<osgFX::Scribe*>(parent);
318        if (!parentAsScribe)
319        {
320            // node not already picked, so highlight it with an osgFX::Scribe
321            osgFX::Scribe* scribe = new osgFX::Scribe();
322            scribe->addChild(node);
323            parent->replaceChild(node,scribe);
324        }
325        else
326        {
327            // node already picked so we want to remove scribe to unpick it.
328            osg::Node::ParentList parentList = parentAsScribe->getParents();
329            for(osg::Node::ParentList::iterator itr=parentList.begin();
330                itr!=parentList.end();
331                ++itr)
332            {
333                (*itr)->replaceChild(parentAsScribe,node);
334            }
335        }
336
337    }
338
339    void saveSelectedModel(osg::Node* scene)
340    {
341        if (!scene) return;
342   
343        CreateModelToSaveVisitor cmtsv;
344        scene->accept(cmtsv);
345       
346        if (cmtsv._group->getNumChildren()>0)
347        {
348            std::cout<<"Writing selected compoents to 'selected_model.osg'"<<std::endl;
349            osgDB::writeNodeFile(*cmtsv._group, "selected_model.osg");
350        }
351    }
352
353protected:
354
355    float _mx,_my;
356    bool _usePolytopeIntersector;
357    bool _useWindowCoordinates;
358};
359
360int main( int argc, char **argv )
361{
362    if (argc<2)
363    {
364        std::cout << argv[0] <<": requires filename argument." << std::endl;
365        return 1;
366    }
367
368    // load the scene.
369    osg::ref_ptr<osg::Node> loadedModel = osgDB::readNodeFile(argv[1]);
370    if (!loadedModel)
371    {
372        std::cout << argv[0] <<": No data loaded." << std::endl;
373        return 1;
374    }
375   
376    // create the window to draw to.
377    osg::ref_ptr<osg::GraphicsContext::Traits> traits = new osg::GraphicsContext::Traits;
378    traits->x = 200;
379    traits->y = 200;
380    traits->width = 800;
381    traits->height = 600;
382    traits->windowDecoration = true;
383    traits->doubleBuffer = true;
384    traits->sharedContext = 0;
385
386    osg::ref_ptr<osg::GraphicsContext> gc = osg::GraphicsContext::createGraphicsContext(traits.get());
387    osgViewer::GraphicsWindow* gw = dynamic_cast<osgViewer::GraphicsWindow*>(gc.get());
388    if (!gw)
389    {
390        osg::notify(osg::NOTICE)<<"Error: unable to create graphics window."<<std::endl;
391        return 1;
392    }
393
394    gw->realize();
395    gw->makeCurrent();
396
397    // create the view of the scene.
398    osgViewer::SimpleViewer viewer;
399    viewer.setSceneData(loadedModel.get());
400   
401    viewer.setEventQueue(gw->getEventQueue());
402    viewer.getEventQueue()->windowResize(traits->x,traits->y,traits->width,traits->height);
403
404    // create a tracball manipulator to move the camera around in response to keyboard/mouse events
405    viewer.setCameraManipulator( new osgGA::TrackballManipulator );
406
407    osg::ref_ptr<osgGA::StateSetManipulator> statesetManipulator = new osgGA::StateSetManipulator;
408    statesetManipulator->setStateSet(viewer.getSceneView()->getGlobalStateSet());
409    viewer.addEventHandler(statesetManipulator.get());
410
411    // add the pick handler
412    viewer.addEventHandler(new PickHandler());
413
414
415    // add the exit handler'
416    ExitHandler* exitHandler = new ExitHandler;
417    viewer.addEventHandler(exitHandler);
418
419    viewer.init();
420
421    // main loop (note, window toolkits which take control over the main loop will require a window redraw callback containing the code below.)
422    while( gw->isRealized() && !exitHandler->done())
423    {
424        gw->checkEvents();
425       
426        if (gw->isRealized() && !exitHandler->done())
427        {     
428            viewer.frame();
429
430            // Swap Buffers
431            gw->swapBuffers();
432        }
433    }
434
435    return 0;
436}
Note: See TracBrowser for help on using the browser.