fixed: compile issue
[opensg.git] / Examples / Simple / idbuffer.cpp
blob73f2c144770de3a52af85d2007c1b58e1e997c08
1 // OpenSG Tutorial Example: Identification buffer
2 //
3 // This tutorial shows how to use the identification color technique so as to
4 // identify objects in a fast way. ( get its Node * )
5 //
6 // - This method is view dependant (we do picking only from the viewpoint), but you can specify
7 // your viewpoint with setCamera().
8 // - This method doesn't handle transparency.
9 // - This method doesn't handle anti-aliasing, this can lead to some imperfections in
10 // identification.
12 // If you need more, it is possible to retreive the 3D point by using Z-buffer information.
13 // It's also possible to send a ray throught the geometry of the identified object so as to
14 // retrieve the corresponding triangle. (Sending ray through the whole scene is really
15 // computationaly expensive in OpenSG).
17 // To do that we add a switchMaterial to each geometry. In this way, we can choose between
18 // the normal material, and the color identification one. Then when requested, we render
19 // the scene to a RAM-buffer (with the GrabForeground method), and call the getObject()
20 // method on it.
22 // Note that this method is suitable for high amount of getObject() calls over a single
23 // frame.
25 // Use Space to pick.
28 #ifdef OSG_BUILD_ACTIVE
29 #include <OSGGLUT.h>
30 #include <OSGConfig.h>
31 #include <OSGSimpleGeometry.h>
32 #include <OSGGLUTWindow.h>
33 #include <OSGSimpleSceneManager.h>
34 #include <OSGAction.h>
35 #include <OSGGroup.h>
36 #include <OSGSceneFileHandler.h>
38 #include <OSGMaterialGroup.h>
39 #include <OSGNameAttachment.h>
41 #include <OSGImage.h>
42 #include <OSGSolidBackground.h>
43 #include <OSGGrabForeground.h>
44 #include <OSGPassiveWindow.h>
45 #include <OSGSwitchMaterial.h>
47 #include <OSGPolygonChunk.h>
48 #include <OSGMaterialChunk.h>
49 #include <OSGChunkMaterial.h>
50 #include <OSGTypedGeoIntegralProperty.h>
51 #include <OSGTypedGeoVectorProperty.h>
52 #else
53 #include <OpenSG/OSGGLUT.h>
54 #include <OpenSG/OSGConfig.h>
55 #include <OpenSG/OSGSimpleGeometry.h>
56 #include <OpenSG/OSGGLUTWindow.h>
57 #include <OpenSG/OSGSimpleSceneManager.h>
58 #include <OpenSG/OSGAction.h>
59 #include <OpenSG/OSGGroup.h>
60 #include <OpenSG/OSGSceneFileHandler.h>
62 #include <OpenSG/OSGMaterialGroup.h>
63 #include <OpenSG/OSGNameAttachment.h>
65 #include <OpenSG/OSGImage.h>
66 #include <OpenSG/OSGSolidBackground.h>
67 #include <OpenSG/OSGGrabForeground.h>
68 #include <OpenSG/OSGPassiveWindow.h>
69 #include <OpenSG/OSGSwitchMaterial.h>
71 #include <OpenSG/OSGPolygonChunk.h>
72 #include <OpenSG/OSGMaterialChunk.h>
73 #include <OpenSG/OSGChunkMaterial.h>
74 #include <OpenSG/OSGTypedGeoIntegralProperty.h>
75 #include <OpenSG/OSGTypedGeoVectorProperty.h>
76 #endif
78 #include <map>
79 #include <list>
81 using namespace std;
83 struct color_compare
85 bool operator()(const OSG::Color4ub &s1, const OSG::Color4ub &s2) const
87 return s1.getRGBA() < s2.getRGBA();
92 class IDbuffer
94 public:
95 IDbuffer(void) :
96 _grabber (NULL),
97 _ID_viewport (NULL),
98 _ID_renderAction(NULL),
99 _window (NULL),
100 _solidBkg (NULL),
101 _sky (NULL),
103 _switches ( ),
104 _mgswitches ( ),
106 _node_index ( ),
108 _ID_buffer (NULL),
109 _w (0 ),
110 _h (0 ),
112 ci (0 ),
113 cj (0 ),
114 ck (0 )
117 _ID_buffer = NULL;
119 // We fix the Sky color to black
120 _solidBkg = OSG::SolidBackground::create();
121 _solidBkg->setColor(OSG::Color3f(0,0,0));
123 _ID_viewport = OSG::Viewport::create();
124 _ID_viewport->setBackground(_solidBkg);
125 _ID_viewport->setSize(0,0,1,1);
127 _window = OSG::PassiveWindow::create();
128 _window->addPort(_ID_viewport);
130 _ID_renderAction = OSG::RenderAction::create();
132 // These parameters depends of your implementation. Use the sames as in your project.
133 _ID_renderAction->setAutoFrustum(true);
134 _ID_renderAction->setOcclusionCulling(true);
136 initColors();
139 ~IDbuffer(void)
141 _ID_renderAction = NULL;
144 void setRoot(OSG::Node * const value)
146 _switches.clear();
147 convertToColorIdentificationSwitchable(value);
148 _ID_viewport->setRoot(value);
151 void setCamera(OSG::Camera * const value)
153 _ID_viewport->setCamera(value);
156 OSG::Node *getSky(void)
158 return _sky;
161 OSG::Node *get_object(int x, int y)
163 if(x>=_w || y >=_h || _ID_buffer==NULL)
164 return NULL;
165 y = _h - y;
166 // Once rendered, we check in the buffer to get the Object ID color.
167 OSG::Color4ub c = OSG::Color4ub( _ID_buffer[4*(y*_w+x)],
168 _ID_buffer[4*(y*_w+x)+1],
169 _ID_buffer[4*(y*_w+x)+2],
170 _ID_buffer[4*(y*_w+x)+3] );
172 // And we find the corresponding Node.
173 NodeIndexMap::iterator i = _node_index.find(c);
174 if(i != _node_index.end())
176 return (*i).second;
178 else
180 return NULL;
184 void reshape(int x, int y)
186 _w=x;
187 _h=y;
188 _window->resize(x,y);
191 void update_render_GrabForeGround(void)
193 // Setup the GrabForeground
194 _grabber = OSG::GrabForeground::create();
196 OSG::ImageRefPtr img = OSG::Image::create();
197 img->set(GL_RGBA,_w,_h);
198 _grabber->setImage(img);
199 _grabber->setAutoResize(false);
200 _grabber->setActive(false);
202 // Putting it to the viewport
203 _window->getPort(0)->editMFForegrounds()->push_back(_grabber);
205 // We toggle the switch material so as to render identification colors.
206 switchToIDbuffer();
208 _grabber->setActive(true);
210 OSG::commitChanges();
212 // We render to the grabber
213 _window->render(_ID_renderAction);
215 _grabber->setActive(false);
217 // We get the data back
218 if(_grabber->getImage()->getData() != NULL)
219 _ID_buffer = _grabber->getImage()->getData();
221 // We toggle to standard materials
222 switchToNormal();
225 // we remove the Grabber
226 _window->getPort(0)->removeObjFromForegrounds(_grabber);
229 private:
230 // Methods
231 void initColors()
233 ci = 255;
234 cj = 255;
235 ck = 255;
237 _sky = OSG::Node::create();
238 OSG::Color4ub c = OSG::Color4ub(0,0,0,255);
239 _node_index[c] = _sky;
242 OSG::Color4ub create_new_color()
244 // With this, we are ready for 255^3 objects.
245 if(ci > 1)
247 ci--;
249 else if(cj > 1)
251 cj--;
252 ci = 255;
254 else if(ck > 1)
256 ck--;
257 ci = cj = 255;
259 else
261 cerr << "Cdrawing::create_new_color() NO MORE COLOR FREE !!!! TOO MANY OBJECTS ... Gloups " << endl;
262 // Note that we can extend to 255^4 objects with the alpha channel
264 return OSG::Color4ub(ci,cj,ck,255);
265 // Note that the color (0,0,0,255) is reserved so as to identify the sky
268 void convertToColorIdentificationSwitchable(OSG::Node * const root)
270 // This is a recursive traversal of the Scene Graph, so as to replace
271 // each material by a SwitchMaterial, in wich we put the normal one on one
272 // side, and the color identification one in other side.
273 OSG::UInt32 children = root->getNChildren();
275 if(root->getCore()->getType().isDerivedFrom(OSG::MaterialGroup::getClassType()))
277 // Need to turn off material groups, as they would override our
278 // geo-specific materials
279 // Just record them here.
281 OSG::MaterialGroup *mg = dynamic_cast<OSG::MaterialGroup *>(root->getCore());
283 _mgswitches.push_back(
284 MaterialGroupList::value_type(
285 mg,
286 static_cast<OSG::Material *>(NULL)));
289 if(root->getCore()->getType().isDerivedFrom(OSG::Geometry::getClassType()))
292 OSG::Geometry *geo = dynamic_cast<OSG::Geometry *>(root->getCore());
294 // If we get a Geometry, we replace it by a switch,
295 // we add this geometry a SwitchMaterial, with its original material
296 // in one case, and a chunkmaterial corresponding to the node ID in the other.
297 OSG::Color4ub c = create_new_color();
298 OSG::Color4f cf;
299 cf.setRGBA(c.getRGBA());
300 // We add the associated pair color/node to the map
301 //_node_index[c] = root;
302 _node_index [c] = root;
304 OSG::PolygonChunkRefPtr pc = OSG::PolygonChunk::create();
305 pc->setSmooth(false);
307 OSG::MaterialChunkRefPtr mc = OSG::MaterialChunk::create();
308 mc->setLit(false);
309 mc->setEmission(cf);
310 mc->setDiffuse(cf);
312 OSG::ChunkMaterialRefPtr cm = OSG::ChunkMaterial::create();
313 cm->addChunk(pc);
314 cm->addChunk(mc);
316 OSG::Material *mat = geo->getMaterial();
317 OSG::SwitchMaterialRefPtr sw = OSG::SwitchMaterial::create();
319 sw->addMaterial(mat); // Choice 0
320 sw->addMaterial(cm); // Choice 1
322 geo->setMaterial(sw);
324 _switches.push_back(sw);
327 // check all children
328 for (OSG::UInt32 i = 0; i < children; i++)
330 convertToColorIdentificationSwitchable(root->getChild(i));
334 // Toggles to color identification
335 void switchToIDbuffer(void)
337 SwitchList::iterator i = _switches.begin();
338 while(i != _switches.end())
340 (*i)->setChoice(1);
341 i++;
344 MaterialGroupList::iterator j = _mgswitches.begin();
345 while(j != _mgswitches.end())
347 (*j).second = (*j).first->getMaterial();
348 (*j).first->setMaterial(NULL);
349 j++;
353 // Toggles to normal materials.
354 void switchToNormal(void)
356 SwitchList::iterator i = _switches.begin();
357 while(i != _switches.end())
359 (*i)->setChoice(0);
360 i++;
363 MaterialGroupList::iterator j = _mgswitches.begin();
364 while(j != _mgswitches.end())
366 (*j).first->setMaterial((*j).second);
367 j++;
371 // Types
372 typedef std::map<OSG::Color4ub, OSG::NodeRefPtr, color_compare> NodeIndexMap;
373 typedef std::list<OSG::SwitchMaterialRefPtr> SwitchList;
374 typedef std::list<std::pair<OSG::MaterialGroupRefPtr,
375 OSG::MaterialRefPtr > > MaterialGroupList;
377 // Variables
378 OSG::GrabForegroundRefPtr _grabber;
379 OSG::ViewportRefPtr _ID_viewport;
380 OSG::RenderActionRefPtr _ID_renderAction;
381 OSG::PassiveWindowRefPtr _window;
382 OSG::SolidBackgroundRefPtr _solidBkg; // Sky color is black
383 OSG::NodeRefPtr _sky;
385 SwitchList _switches; // Switchs to change from normal to ID material
386 MaterialGroupList _mgswitches;
388 // List of used colors for Identification
389 NodeIndexMap _node_index;
391 const OSG::UInt8 * _ID_buffer; // Ram version of the ID buffer
392 int _w, _h; // buffer size
394 int ci,cj,ck; // for colors generations
397 IDbuffer(const IDbuffer &other);
398 void operator =(const IDbuffer &other);
401 // forward declaration so we can have the interesting stuff upfront
402 int setupGLUT( int *argc, char *argv[] );
405 // The SimpleSceneManager to manage simple applications
406 OSG::SimpleSceneManagerRefPtr mgr;
407 // The file root node, needed for intersection
408 OSG::NodeRefPtr fileroot;
409 // The points used for visualising the ray and hit object
410 OSG::GeoPnt3fPropertyRefPtr isectPoints;
411 // The visualisation geometry, needed for update.
412 OSG::GeometryRefPtr testgeocore;
414 IDbuffer* _idbuff;
418 // react to keys
419 void keyboard(unsigned char k, int x, int y)
421 switch(k)
423 case 27:
425 // clean up global variables
426 mgr = NULL;
428 delete _idbuff;
430 fileroot = NULL;
431 isectPoints = NULL;
432 testgeocore = NULL;
434 OSG::osgExit();
435 exit(0);
437 break;
439 case ' ': // check the object under the clicked pixel
441 _idbuff->update_render_GrabForeGround();
442 OSG::Node *found = _idbuff->get_object(x,y);
444 if( found == _idbuff->getSky() )
446 std::cout<<"Picking : Sky found"<<std::endl;
448 else if( found != NULL )
450 const OSG::Char8 *n = getName(found);
451 if(n == NULL)
452 n = "Unnamed";
454 std::cout << "Picking : Object found : "
455 << n << " (node=" << found << ")" << std::endl;
457 else
459 std::cerr << "Problem..." << std::endl;
463 break;
467 // Initialize GLUT & OpenSG and set up the scene
468 int main(int argc, char **argv)
470 // OSG init
471 OSG::osgInit(argc,argv);
473 // GLUT init
474 int winid = setupGLUT(&argc, argv);
476 // open a new scope, because the pointers below should go out of scope
477 // before entering glutMainLoop.
478 // Otherwise OpenSG will complain about objects being alive after shutdown.
480 // the connection between GLUT and OpenSG
481 OSG::GLUTWindowRefPtr gwin = OSG::GLUTWindow::create();
482 gwin->setGlutId(winid);
483 gwin->init();
485 // The scene group
487 OSG::NodeRefPtr scene = OSG::Node::create();
488 OSG::GroupRefPtr g = OSG::Group::create();
490 scene->setCore(g);
492 if(argc < 2)
494 FWARNING(("No file given!\n"));
495 FWARNING(("Supported file formats:\n"));
497 std::list<const char*> suffixes;
498 OSG::SceneFileHandler::the()->getSuffixList(suffixes);
500 for(std::list<const char*>::iterator it = suffixes.begin();
501 it != suffixes.end();
502 ++it)
504 FWARNING(("%s\n", *it));
507 fileroot = OSG::makeTorus(.5, 2, 16, 16);
509 else
511 fileroot = OSG::SceneFileHandler::the()->read(argv[1]);
513 All scene file loading is handled via the SceneFileHandler.
517 scene->addChild(fileroot);
519 // Create a small geometry to show the ray and what was hit
520 // Contains a line and a single triangle.
521 // The line shows the ray, the triangle whatever was hit.
523 OSG::SimpleMaterialRefPtr red = OSG::SimpleMaterial::create();
525 red->setDiffuse (OSG::Color3f( 1,0,0 ));
526 red->setTransparency(0.5);
527 red->setLit (false);
529 isectPoints = OSG::GeoPnt3fProperty::create();
530 isectPoints->addValue(OSG::Pnt3f(0,0,0));
531 isectPoints->addValue(OSG::Pnt3f(0,0,0));
532 isectPoints->addValue(OSG::Pnt3f(0,0,0));
533 isectPoints->addValue(OSG::Pnt3f(0,0,0));
534 isectPoints->addValue(OSG::Pnt3f(0,0,0));
536 OSG::GeoUInt32PropertyRefPtr index = OSG::GeoUInt32Property::create();
537 index->addValue(0);
538 index->addValue(1);
539 index->addValue(2);
540 index->addValue(3);
541 index->addValue(4);
543 OSG::GeoUInt32PropertyRefPtr lens = OSG::GeoUInt32Property::create();
544 lens->addValue(2);
545 lens->addValue(3);
547 OSG::GeoUInt8PropertyRefPtr type = OSG::GeoUInt8Property::create();
548 type->addValue(GL_LINES);
549 type->addValue(GL_TRIANGLES);
551 testgeocore = OSG::Geometry::create();
552 testgeocore->setPositions(isectPoints);
553 testgeocore->setIndices(index);
554 testgeocore->setLengths(lens);
555 testgeocore->setTypes(type);
556 testgeocore->setMaterial(red);
558 OSG::NodeRefPtr testgeo = OSG::Node::create();
559 testgeo->setCore(testgeocore);
561 scene->addChild(testgeo);
563 // create the SimpleSceneManager helper
564 mgr = OSG::SimpleSceneManager::create();
566 // tell the manager what to manage
567 mgr->setWindow(gwin );
568 mgr->setRoot (scene);
570 // show the whole scene
571 mgr->showAll();
573 mgr->getCamera()->setNear(mgr->getCamera()->getNear() / 10);
575 // Show the bounding volumes? Not for now
576 mgr->getRenderAction()->setVolumeDrawing(false);
578 _idbuff = new IDbuffer();
579 _idbuff->setCamera(mgr->getCamera());
580 _idbuff->setRoot(scene);
583 // GLUT main loop
584 glutMainLoop();
586 return 0;
590 // GLUT callback functions
593 // redraw the window
594 void display(void)
596 mgr->redraw();
599 // react to size changes
600 void reshape(int w, int h)
602 mgr->resize(w, h);
603 _idbuff->reshape(w,h);
605 glutPostRedisplay();
608 // react to mouse button presses
609 void mouse(int button, int state, int x, int y)
611 if (state)
612 mgr->mouseButtonRelease(button, x, y);
613 else
614 mgr->mouseButtonPress(button, x, y);
616 glutPostRedisplay();
619 // react to mouse motions with pressed buttons
620 void motion(int x, int y)
622 mgr->mouseMove(x, y);
623 glutPostRedisplay();
626 // setup the GLUT library which handles the windows for us
627 int setupGLUT(int *argc, char *argv[])
629 glutInit(argc, argv);
630 glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH | GLUT_DOUBLE);
632 int winid = glutCreateWindow("OpenSG");
634 glutReshapeFunc(reshape);
635 glutDisplayFunc(display);
636 glutMouseFunc(mouse);
637 glutMotionFunc(motion);
638 glutKeyboardFunc(keyboard);
640 return winid;