1 // OpenSG Tutorial Example: Identification buffer
3 // This tutorial shows how to use the identification color technique so as to
4 // identify objects in a fast way. ( get its Node * )
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
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()
22 // Note that this method is suitable for high amount of getObject() calls over a single
28 #ifdef OSG_BUILD_ACTIVE
30 #include <OSGConfig.h>
31 #include <OSGSimpleGeometry.h>
32 #include <OSGGLUTWindow.h>
33 #include <OSGSimpleSceneManager.h>
34 #include <OSGAction.h>
36 #include <OSGSceneFileHandler.h>
38 #include <OSGMaterialGroup.h>
39 #include <OSGNameAttachment.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>
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>
85 bool operator()(const OSG::Color4ub
&s1
, const OSG::Color4ub
&s2
) const
87 return s1
.getRGBA() < s2
.getRGBA();
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);
121 _ID_renderAction
= NULL
;
124 void setRoot(OSG::Node
* const value
)
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)
141 OSG::Node
*get_object(int x
, int y
)
143 if(x
>=_w
|| y
>=_h
|| _ID_buffer
==NULL
)
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())
164 void reshape(int x
, int 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.
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
205 // we remove the Grabber
206 _window
->getPort(0)->removeObjFromForegrounds(_grabber
);
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.
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(
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();
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();
292 OSG::ChunkMaterialRefPtr cm
= OSG::ChunkMaterial::create();
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())
324 MaterialGroupList::iterator j
= _mgswitches
.begin();
325 while(j
!= _mgswitches
.end())
327 (*j
).second
= (*j
).first
->getMaterial();
328 (*j
).first
->setMaterial(NULL
);
333 // Toggles to normal materials.
334 void switchToNormal(void)
336 SwitchList::iterator i
= _switches
.begin();
337 while(i
!= _switches
.end())
343 MaterialGroupList::iterator j
= _mgswitches
.begin();
344 while(j
!= _mgswitches
.end())
346 (*j
).first
->setMaterial((*j
).second
);
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
;
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
;
395 void keyboard(unsigned char k
, int x
, int y
)
401 // clean up global variables
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
);
430 std::cout
<< "Picking : Object found : "
431 << n
<< " (node=" << found
<< ")" << std::endl
;
435 std::cerr
<< "Problem..." << std::endl
;
443 // Initialize GLUT & OpenSG and set up the scene
444 int main(int argc
, char **argv
)
447 OSG::osgInit(argc
,argv
);
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
);
463 OSG::NodeRefPtr scene
= OSG::Node::create();
464 OSG::GroupRefPtr g
= OSG::Group::create();
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();
480 FWARNING(("%s\n", *it
));
483 fileroot
= OSG::makeTorus(.5, 2, 16, 16);
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);
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();
519 OSG::GeoUInt32PropertyRefPtr lens
= OSG::GeoUInt32Property::create();
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
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
);
566 // GLUT callback functions
575 // react to size changes
576 void reshape(int w
, int h
)
579 _idbuff
->reshape(w
,h
);
584 // react to mouse button presses
585 void mouse(int button
, int state
, int x
, int y
)
588 mgr
->mouseButtonRelease(button
, x
, y
);
590 mgr
->mouseButtonPress(button
, x
, y
);
595 // react to mouse motions with pressed buttons
596 void motion(int x
, int y
)
598 mgr
->mouseMove(x
, y
);
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
);