fixed: stay with old cmake qt link setup (policy CMP0020)
[opensg.git] / Examples / Simple / idbuffer.cpp
blob2780ae1b9fda716ae6e5f15eeda2b1508f06a311
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)
97 _ID_buffer = NULL;
99 // We fix the Sky color to black
100 _solidBkg = OSG::SolidBackground::create();
101 _solidBkg->setColor(OSG::Color3f(0,0,0));
103 _ID_viewport = OSG::Viewport::create();
104 _ID_viewport->setBackground(_solidBkg);
105 _ID_viewport->setSize(0,0,1,1);
107 _window = OSG::PassiveWindow::create();
108 _window->addPort(_ID_viewport);
110 _ID_renderAction = OSG::RenderAction::create();
112 // These parameters depends of your implementation. Use the sames as in your project.
113 _ID_renderAction->setAutoFrustum(true);
114 _ID_renderAction->setOcclusionCulling(true);
116 initColors();
119 ~IDbuffer(void)
121 _ID_renderAction = NULL;
124 void setRoot(OSG::Node * const value)
126 _switches.clear();
127 convertToColorIdentificationSwitchable(value);
128 _ID_viewport->setRoot(value);
131 void setCamera(OSG::Camera * const value)
133 _ID_viewport->setCamera(value);
136 OSG::Node *getSky(void)
138 return _sky;
141 OSG::Node *get_object(int x, int y)
143 if(x>=_w || y >=_h || _ID_buffer==NULL)
144 return NULL;
145 y = _h - y;
146 // Once rendered, we check in the buffer to get the Object ID color.
147 OSG::Color4ub c = OSG::Color4ub( _ID_buffer[4*(y*_w+x)],
148 _ID_buffer[4*(y*_w+x)+1],
149 _ID_buffer[4*(y*_w+x)+2],
150 _ID_buffer[4*(y*_w+x)+3] );
152 // And we find the corresponding Node.
153 NodeIndexMap::iterator i = _node_index.find(c);
154 if(i != _node_index.end())
156 return (*i).second;
158 else
160 return NULL;
164 void reshape(int x, int y)
166 _w=x;
167 _h=y;
168 _window->resize(x,y);
171 void update_render_GrabForeGround(void)
173 // Setup the GrabForeground
174 _grabber = OSG::GrabForeground::create();
176 OSG::ImageRefPtr img = OSG::Image::create();
177 img->set(GL_RGBA,_w,_h);
178 _grabber->setImage(img);
179 _grabber->setAutoResize(false);
180 _grabber->setActive(false);
182 // Putting it to the viewport
183 _window->getPort(0)->editMFForegrounds()->push_back(_grabber);
185 // We toggle the switch material so as to render identification colors.
186 switchToIDbuffer();
188 _grabber->setActive(true);
190 OSG::commitChanges();
192 // We render to the grabber
193 _window->render(_ID_renderAction);
195 _grabber->setActive(false);
197 // We get the data back
198 if(_grabber->getImage()->getData() != NULL)
199 _ID_buffer = _grabber->getImage()->getData();
201 // We toggle to standard materials
202 switchToNormal();
205 // we remove the Grabber
206 _window->getPort(0)->removeObjFromForegrounds(_grabber);
209 private:
210 // Methods
211 void initColors()
213 ci = 255;
214 cj = 255;
215 ck = 255;
217 _sky = OSG::Node::create();
218 OSG::Color4ub c = OSG::Color4ub(0,0,0,255);
219 _node_index[c] = _sky;
222 OSG::Color4ub create_new_color()
224 // With this, we are ready for 255^3 objects.
225 if(ci > 1)
227 ci--;
229 else if(cj > 1)
231 cj--;
232 ci = 255;
234 else if(ck > 1)
236 ck--;
237 ci = cj = 255;
239 else
241 cerr << "Cdrawing::create_new_color() NO MORE COLOR FREE !!!! TOO MANY OBJECTS ... Gloups " << endl;
242 // Note that we can extend to 255^4 objects with the alpha channel
244 return OSG::Color4ub(ci,cj,ck,255);
245 // Note that the color (0,0,0,255) is reserved so as to identify the sky
248 void convertToColorIdentificationSwitchable(OSG::Node * const root)
250 // This is a recursive traversal of the Scene Graph, so as to replace
251 // each material by a SwitchMaterial, in wich we put the normal one on one
252 // side, and the color identification one in other side.
253 OSG::UInt32 children = root->getNChildren();
255 if(root->getCore()->getType().isDerivedFrom(OSG::MaterialGroup::getClassType()))
257 // Need to turn off material groups, as they would override our
258 // geo-specific materials
259 // Just record them here.
261 OSG::MaterialGroup *mg = dynamic_cast<OSG::MaterialGroup *>(root->getCore());
263 _mgswitches.push_back(
264 MaterialGroupList::value_type(
265 mg,
266 static_cast<OSG::Material *>(NULL)));
269 if(root->getCore()->getType().isDerivedFrom(OSG::Geometry::getClassType()))
272 OSG::Geometry *geo = dynamic_cast<OSG::Geometry *>(root->getCore());
274 // If we get a Geometry, we replace it by a switch,
275 // we add this geometry a SwitchMaterial, with its original material
276 // in one case, and a chunkmaterial corresponding to the node ID in the other.
277 OSG::Color4ub c = create_new_color();
278 OSG::Color4f cf;
279 cf.setRGBA(c.getRGBA());
280 // We add the associated pair color/node to the map
281 //_node_index[c] = root;
282 _node_index [c] = root;
284 OSG::PolygonChunkRefPtr pc = OSG::PolygonChunk::create();
285 pc->setSmooth(false);
287 OSG::MaterialChunkRefPtr mc = OSG::MaterialChunk::create();
288 mc->setLit(false);
289 mc->setEmission(cf);
290 mc->setDiffuse(cf);
292 OSG::ChunkMaterialRefPtr cm = OSG::ChunkMaterial::create();
293 cm->addChunk(pc);
294 cm->addChunk(mc);
296 OSG::Material *mat = geo->getMaterial();
297 OSG::SwitchMaterialRefPtr sw = OSG::SwitchMaterial::create();
299 sw->addMaterial(mat); // Choice 0
300 sw->addMaterial(cm); // Choice 1
302 geo->setMaterial(sw);
304 _switches.push_back(sw);
307 // check all children
308 for (OSG::UInt32 i = 0; i < children; i++)
310 convertToColorIdentificationSwitchable(root->getChild(i));
314 // Toggles to color identification
315 void switchToIDbuffer(void)
317 SwitchList::iterator i = _switches.begin();
318 while(i != _switches.end())
320 (*i)->setChoice(1);
321 i++;
324 MaterialGroupList::iterator j = _mgswitches.begin();
325 while(j != _mgswitches.end())
327 (*j).second = (*j).first->getMaterial();
328 (*j).first->setMaterial(NULL);
329 j++;
333 // Toggles to normal materials.
334 void switchToNormal(void)
336 SwitchList::iterator i = _switches.begin();
337 while(i != _switches.end())
339 (*i)->setChoice(0);
340 i++;
343 MaterialGroupList::iterator j = _mgswitches.begin();
344 while(j != _mgswitches.end())
346 (*j).first->setMaterial((*j).second);
347 j++;
351 // Types
352 typedef std::map<OSG::Color4ub, OSG::NodeRefPtr, color_compare> NodeIndexMap;
353 typedef std::list<OSG::SwitchMaterialRefPtr> SwitchList;
354 typedef std::list<std::pair<OSG::MaterialGroupRefPtr,
355 OSG::MaterialRefPtr > > MaterialGroupList;
357 // Variables
358 OSG::GrabForegroundRefPtr _grabber;
359 OSG::ViewportRefPtr _ID_viewport;
360 OSG::RenderActionRefPtr _ID_renderAction;
361 OSG::PassiveWindowRefPtr _window;
362 OSG::SolidBackgroundRefPtr _solidBkg; // Sky color is black
363 OSG::NodeRefPtr _sky;
365 SwitchList _switches; // Switchs to change from normal to ID material
366 MaterialGroupList _mgswitches;
368 // List of used colors for Identification
369 NodeIndexMap _node_index;
371 const OSG::UInt8 * _ID_buffer; // Ram version of the ID buffer
372 int _w, _h; // buffer size
374 int ci,cj,ck; // for colors generations
377 // forward declaration so we can have the interesting stuff upfront
378 int setupGLUT( int *argc, char *argv[] );
381 // The SimpleSceneManager to manage simple applications
382 OSG::SimpleSceneManagerRefPtr mgr;
383 // The file root node, needed for intersection
384 OSG::NodeRefPtr fileroot;
385 // The points used for visualising the ray and hit object
386 OSG::GeoPnt3fPropertyRefPtr isectPoints;
387 // The visualisation geometry, needed for update.
388 OSG::GeometryRefPtr testgeocore;
390 IDbuffer* _idbuff;
394 // react to keys
395 void keyboard(unsigned char k, int x, int y)
397 switch(k)
399 case 27:
401 // clean up global variables
402 mgr = NULL;
404 delete _idbuff;
406 fileroot = NULL;
407 isectPoints = NULL;
408 testgeocore = NULL;
410 OSG::osgExit();
411 exit(0);
413 break;
415 case ' ': // check the object under the clicked pixel
417 _idbuff->update_render_GrabForeGround();
418 OSG::Node *found = _idbuff->get_object(x,y);
420 if( found == _idbuff->getSky() )
422 std::cout<<"Picking : Sky found"<<std::endl;
424 else if( found != NULL )
426 const OSG::Char8 *n = getName(found);
427 if(n == NULL)
428 n = "Unnamed";
430 std::cout << "Picking : Object found : "
431 << n << " (node=" << found << ")" << std::endl;
433 else
435 std::cerr << "Problem..." << std::endl;
439 break;
443 // Initialize GLUT & OpenSG and set up the scene
444 int main(int argc, char **argv)
446 // OSG init
447 OSG::osgInit(argc,argv);
449 // GLUT init
450 int winid = setupGLUT(&argc, argv);
452 // open a new scope, because the pointers below should go out of scope
453 // before entering glutMainLoop.
454 // Otherwise OpenSG will complain about objects being alive after shutdown.
456 // the connection between GLUT and OpenSG
457 OSG::GLUTWindowRefPtr gwin = OSG::GLUTWindow::create();
458 gwin->setGlutId(winid);
459 gwin->init();
461 // The scene group
463 OSG::NodeRefPtr scene = OSG::Node::create();
464 OSG::GroupRefPtr g = OSG::Group::create();
466 scene->setCore(g);
468 if(argc < 2)
470 FWARNING(("No file given!\n"));
471 FWARNING(("Supported file formats:\n"));
473 std::list<const char*> suffixes;
474 OSG::SceneFileHandler::the()->getSuffixList(suffixes);
476 for(std::list<const char*>::iterator it = suffixes.begin();
477 it != suffixes.end();
478 ++it)
480 FWARNING(("%s\n", *it));
483 fileroot = OSG::makeTorus(.5, 2, 16, 16);
485 else
487 fileroot = OSG::SceneFileHandler::the()->read(argv[1]);
489 All scene file loading is handled via the SceneFileHandler.
493 scene->addChild(fileroot);
495 // Create a small geometry to show the ray and what was hit
496 // Contains a line and a single triangle.
497 // The line shows the ray, the triangle whatever was hit.
499 OSG::SimpleMaterialRefPtr red = OSG::SimpleMaterial::create();
501 red->setDiffuse (OSG::Color3f( 1,0,0 ));
502 red->setTransparency(0.5);
503 red->setLit (false);
505 isectPoints = OSG::GeoPnt3fProperty::create();
506 isectPoints->addValue(OSG::Pnt3f(0,0,0));
507 isectPoints->addValue(OSG::Pnt3f(0,0,0));
508 isectPoints->addValue(OSG::Pnt3f(0,0,0));
509 isectPoints->addValue(OSG::Pnt3f(0,0,0));
510 isectPoints->addValue(OSG::Pnt3f(0,0,0));
512 OSG::GeoUInt32PropertyRefPtr index = OSG::GeoUInt32Property::create();
513 index->addValue(0);
514 index->addValue(1);
515 index->addValue(2);
516 index->addValue(3);
517 index->addValue(4);
519 OSG::GeoUInt32PropertyRefPtr lens = OSG::GeoUInt32Property::create();
520 lens->addValue(2);
521 lens->addValue(3);
523 OSG::GeoUInt8PropertyRefPtr type = OSG::GeoUInt8Property::create();
524 type->addValue(GL_LINES);
525 type->addValue(GL_TRIANGLES);
527 testgeocore = OSG::Geometry::create();
528 testgeocore->setPositions(isectPoints);
529 testgeocore->setIndices(index);
530 testgeocore->setLengths(lens);
531 testgeocore->setTypes(type);
532 testgeocore->setMaterial(red);
534 OSG::NodeRefPtr testgeo = OSG::Node::create();
535 testgeo->setCore(testgeocore);
537 scene->addChild(testgeo);
539 // create the SimpleSceneManager helper
540 mgr = OSG::SimpleSceneManager::create();
542 // tell the manager what to manage
543 mgr->setWindow(gwin );
544 mgr->setRoot (scene);
546 // show the whole scene
547 mgr->showAll();
549 mgr->getCamera()->setNear(mgr->getCamera()->getNear() / 10);
551 // Show the bounding volumes? Not for now
552 mgr->getRenderAction()->setVolumeDrawing(false);
554 _idbuff = new IDbuffer();
555 _idbuff->setCamera(mgr->getCamera());
556 _idbuff->setRoot(scene);
559 // GLUT main loop
560 glutMainLoop();
562 return 0;
566 // GLUT callback functions
569 // redraw the window
570 void display(void)
572 mgr->redraw();
575 // react to size changes
576 void reshape(int w, int h)
578 mgr->resize(w, h);
579 _idbuff->reshape(w,h);
581 glutPostRedisplay();
584 // react to mouse button presses
585 void mouse(int button, int state, int x, int y)
587 if (state)
588 mgr->mouseButtonRelease(button, x, y);
589 else
590 mgr->mouseButtonPress(button, x, y);
592 glutPostRedisplay();
595 // react to mouse motions with pressed buttons
596 void motion(int x, int y)
598 mgr->mouseMove(x, y);
599 glutPostRedisplay();
602 // setup the GLUT library which handles the windows for us
603 int setupGLUT(int *argc, char *argv[])
605 glutInit(argc, argv);
606 glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH | GLUT_DOUBLE);
608 int winid = glutCreateWindow("OpenSG");
610 glutReshapeFunc(reshape);
611 glutDisplayFunc(display);
612 glutMouseFunc(mouse);
613 glutMotionFunc(motion);
614 glutKeyboardFunc(keyboard);
616 return winid;