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();
98 _ID_renderAction(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);
141 _ID_renderAction
= NULL
;
144 void setRoot(OSG::Node
* const value
)
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)
161 OSG::Node
*get_object(int x
, int y
)
163 if(x
>=_w
|| y
>=_h
|| _ID_buffer
==NULL
)
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())
184 void reshape(int x
, int 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.
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
225 // we remove the Grabber
226 _window
->getPort(0)->removeObjFromForegrounds(_grabber
);
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.
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(
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();
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();
312 OSG::ChunkMaterialRefPtr cm
= OSG::ChunkMaterial::create();
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())
344 MaterialGroupList::iterator j
= _mgswitches
.begin();
345 while(j
!= _mgswitches
.end())
347 (*j
).second
= (*j
).first
->getMaterial();
348 (*j
).first
->setMaterial(NULL
);
353 // Toggles to normal materials.
354 void switchToNormal(void)
356 SwitchList::iterator i
= _switches
.begin();
357 while(i
!= _switches
.end())
363 MaterialGroupList::iterator j
= _mgswitches
.begin();
364 while(j
!= _mgswitches
.end())
366 (*j
).first
->setMaterial((*j
).second
);
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
;
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
;
419 void keyboard(unsigned char k
, int x
, int y
)
425 // clean up global variables
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
);
454 std::cout
<< "Picking : Object found : "
455 << n
<< " (node=" << found
<< ")" << std::endl
;
459 std::cerr
<< "Problem..." << std::endl
;
467 // Initialize GLUT & OpenSG and set up the scene
468 int main(int argc
, char **argv
)
471 OSG::osgInit(argc
,argv
);
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
);
487 OSG::NodeRefPtr scene
= OSG::Node::create();
488 OSG::GroupRefPtr g
= OSG::Group::create();
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();
504 FWARNING(("%s\n", *it
));
507 fileroot
= OSG::makeTorus(.5, 2, 16, 16);
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);
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();
543 OSG::GeoUInt32PropertyRefPtr lens
= OSG::GeoUInt32Property::create();
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
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
);
590 // GLUT callback functions
599 // react to size changes
600 void reshape(int w
, int h
)
603 _idbuff
->reshape(w
,h
);
608 // react to mouse button presses
609 void mouse(int button
, int state
, int x
, int y
)
612 mgr
->mouseButtonRelease(button
, x
, y
);
614 mgr
->mouseButtonPress(button
, x
, y
);
619 // react to mouse motions with pressed buttons
620 void motion(int x
, int y
)
622 mgr
->mouseMove(x
, y
);
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
);