1 /* OpenSceneGraph example, osgoccluder.
2 *
3 *  Permission is hereby granted, free of charge, to any person obtaining a copy
4 *  of this software and associated documentation files (the "Software"), to deal
5 *  in the Software without restriction, including without limitation the rights
6 *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 *  copies of the Software, and to permit persons to whom the Software is
8 *  furnished to do so, subject to the following conditions:
9 *
10 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
11 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
12 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
13 *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
14 *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
15 *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
16 *  THE SOFTWARE.
17 */
18 
19 #include <osgViewer/Viewer>
20 
21 #include <osg/MatrixTransform>
22 #include <osg/Billboard>
23 #include <osg/Geode>
24 #include <osg/Group>
25 #include <osg/Notify>
26 #include <osg/io_utils>
27 
28 #include <osgDB/Registry>
29 #include <osgDB/ReadFile>
30 #include <osgDB/WriteFile>
31 
32 #include <osgGA/TrackballManipulator>
33 #include <osgGA/FlightManipulator>
34 #include <osgGA/DriveManipulator>
35 
36 #include <osgUtil/Optimizer>
37 
38 #include <osg/OccluderNode>
39 #include <osg/Geometry>
40 #include <osg/ShapeDrawable>
41 
42 #include <iostream>
43 
44 class OccluderEventHandler : public osgGA::GUIEventHandler
45 {
46     public:
47 
OccluderEventHandler(osgViewer::Viewer * viewer)48         OccluderEventHandler(osgViewer::Viewer* viewer):_viewer(viewer) {}
49 
50         virtual bool handle(const osgGA::GUIEventAdapter& ea,osgGA::GUIActionAdapter&);
51 
52         void addPoint(const osg::Vec3& pos);
53 
54         void endOccluder();
55 
rootNode()56         osg::Group* rootNode() { return dynamic_cast<osg::Group*>(_viewer->getSceneData()); }
57 
58 
59         osgViewer::Viewer*                    _viewer;
60         osg::ref_ptr<osg::Group>                _occluders;
61         osg::ref_ptr<osg::ConvexPlanarOccluder> _convexPlanarOccluder;
62 };
63 
handle(const osgGA::GUIEventAdapter & ea,osgGA::GUIActionAdapter & aa)64 bool OccluderEventHandler::handle(const osgGA::GUIEventAdapter& ea,osgGA::GUIActionAdapter& aa)
65 {
66     switch(ea.getEventType())
67     {
68         case(osgGA::GUIEventAdapter::KEYDOWN):
69         {
70             if (ea.getKey()=='a')
71 
72             {
73                 osgViewer::View* view = dynamic_cast<osgViewer::View*>(&aa);
74                 osgUtil::LineSegmentIntersector::Intersections intersections;
75                 if (view && view->computeIntersections(ea, intersections))
76                 {
77                     const osgUtil::LineSegmentIntersector::Intersection& hit = *(intersections.begin());
78                     if (hit.matrix.valid()) addPoint(hit.localIntersectionPoint * (*hit.matrix));
79                     else addPoint(hit.localIntersectionPoint);
80                 }
81 
82                 return true;
83             }
84             else if (ea.getKey()=='e')
85             {
86                 endOccluder();
87                 return true;
88             }
89             else if (ea.getKey()=='O')
90             {
91                 if (_occluders.valid())
92                 {
93 
94                     if (osgDB::writeNodeFile(*_occluders,"saved_occluders.osgt"))
95                         std::cout<<"saved occluders to 'saved_occluders.osgt'"<<std::endl;
96                 }
97                 else
98                 {
99                     std::cout<<"no occluders to save"<<std::endl;
100                 }
101                 return true;
102             }
103             return false;
104         }
105 
106         default:
107             return false;
108     }
109 }
110 
addPoint(const osg::Vec3 & pos)111 void OccluderEventHandler::addPoint(const osg::Vec3& pos)
112 {
113     std::cout<<"add point "<<pos<<std::endl;
114 
115     if (!_convexPlanarOccluder.valid()) _convexPlanarOccluder = new osg::ConvexPlanarOccluder;
116 
117     osg::ConvexPlanarPolygon& occluder = _convexPlanarOccluder->getOccluder();
118     occluder.add(pos);
119 
120 //
121 //     osg::BoundingSphere bs = rootNode()->getBound();
122 //
123 //     osg::ShapeDrawable* sd = new osg::ShapeDrawable(new osg::Sphere(pos,bs.radius()*0.001f));
124 //     osg::Geode* geode = new osg::Geode;
125 //     geode->addDrawable(sd);
126 //
127 //     rootNode()->addChild(geode);
128 //
129 
130 }
131 
endOccluder()132 void OccluderEventHandler::endOccluder()
133 {
134     if (_convexPlanarOccluder.valid())
135     {
136         if (_convexPlanarOccluder->getOccluder().getVertexList().size()>=3)
137         {
138             osg::OccluderNode* occluderNode = new osg::OccluderNode;
139             occluderNode->setOccluder(_convexPlanarOccluder.get());
140 
141             if (!_occluders.valid())
142             {
143                 _occluders = new osg::Group;
144                 if (rootNode()) rootNode()->addChild(_occluders.get());
145             }
146             _occluders->addChild(occluderNode);
147 
148             std::cout<<"created occluder"<<std::endl;
149         }
150         else
151         {
152             std::cout<<"Occluder requires at least 3 points to create occluder."<<std::endl;
153         }
154     }
155     else
156     {
157         std::cout<<"No occluder points to create occluder with."<<std::endl;
158     }
159 
160     // reset current occluder.
161     _convexPlanarOccluder = NULL;
162 }
163 
164 
createOccluder(const osg::Vec3 & v1,const osg::Vec3 & v2,const osg::Vec3 & v3,const osg::Vec3 & v4,float holeRatio=-1.0f)165 osg::Node* createOccluder(const osg::Vec3& v1,const osg::Vec3& v2,const osg::Vec3& v3,const osg::Vec3& v4,float holeRatio=-1.0f)
166 {
167    // create an occluder which will sit alongside the loaded model.
168     osg::OccluderNode* occluderNode = new osg::OccluderNode;
169 
170     // create the convex planar occluder
171     osg::ConvexPlanarOccluder* cpo = new osg::ConvexPlanarOccluder;
172 
173     // attach it to the occluder node.
174     occluderNode->setOccluder(cpo);
175     occluderNode->setName("occluder");
176 
177     // set the occluder up for the front face of the bounding box.
178     osg::ConvexPlanarPolygon& occluder = cpo->getOccluder();
179     occluder.add(v1);
180     occluder.add(v2);
181     occluder.add(v3);
182     occluder.add(v4);
183 
184     // create a hole at the center of the occluder if needed.
185     if (holeRatio>0.0f)
186     {
187         // create hole.
188         float ratio = holeRatio;
189         float one_minus_ratio = 1-ratio;
190         osg::Vec3 center = (v1+v2+v3+v4)*0.25f;
191         osg::Vec3 v1dash = v1*ratio + center*one_minus_ratio;
192         osg::Vec3 v2dash = v2*ratio + center*one_minus_ratio;
193         osg::Vec3 v3dash = v3*ratio + center*one_minus_ratio;
194         osg::Vec3 v4dash = v4*ratio + center*one_minus_ratio;
195 
196         osg::ConvexPlanarPolygon hole;
197         hole.add(v1dash);
198         hole.add(v2dash);
199         hole.add(v3dash);
200         hole.add(v4dash);
201 
202         cpo->addHole(hole);
203     }
204 
205 
206    // create a drawable for occluder.
207     osg::Geometry* geom = new osg::Geometry;
208 
209     osg::Vec3Array* coords = new osg::Vec3Array(occluder.getVertexList().begin(),occluder.getVertexList().end());
210     geom->setVertexArray(coords);
211 
212     osg::Vec4Array* colors = new osg::Vec4Array(1);
213     (*colors)[0].set(1.0f,1.0f,1.0f,0.5f);
214     geom->setColorArray(colors, osg::Array::BIND_OVERALL);
215 
216     geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS,0,4));
217 
218     osg::Geode* geode = new osg::Geode;
219     geode->addDrawable(geom);
220 
221     osg::StateSet* stateset = new osg::StateSet;
222     stateset->setMode(GL_LIGHTING,osg::StateAttribute::OFF);
223     stateset->setMode(GL_BLEND,osg::StateAttribute::ON);
224     stateset->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
225 
226     geom->setStateSet(stateset);
227 
228     // add the occluder geode as a child of the occluder,
229     // as the occluder can't self occlude its subgraph the
230     // geode will never be occluded by this occluder.
231     occluderNode->addChild(geode);
232 
233     return occluderNode;
234 
235  }
236 
createOccludersAroundModel(osg::Node * model)237 osg::Group* createOccludersAroundModel(osg::Node* model)
238 {
239     osg::Group* scene = new osg::Group;
240     scene->setName("rootgroup");
241 
242 
243     // add the loaded model into a the scene group.
244     scene->addChild(model);
245     model->setName("model");
246 
247     // get the bounding volume of the model.
248     const osg::BoundingSphere bs = model->getBound();
249 
250     // create a bounding box around the sphere.
251     osg::BoundingBox bb;
252     bb.expandBy(bs);
253 
254    // front
255    scene->addChild(createOccluder(bb.corner(0),
256                                   bb.corner(1),
257                                   bb.corner(5),
258                                   bb.corner(4)));
259 
260    // right side
261    scene->addChild(createOccluder(bb.corner(1),
262                                   bb.corner(3),
263                                   bb.corner(7),
264                                   bb.corner(5)));
265 
266    // left side
267    scene->addChild(createOccluder(bb.corner(2),
268                                   bb.corner(0),
269                                   bb.corner(4),
270                                   bb.corner(6)));
271 
272    // back side
273    scene->addChild(createOccluder(bb.corner(3),
274                                   bb.corner(2),
275                                   bb.corner(6),
276                                   bb.corner(7),
277                                   0.5f)); // create a hole half the size of the occluder.
278 
279     return scene;
280 }
281 
282 
main(int argc,char ** argv)283 int main( int argc, char **argv )
284 {
285 
286     // use an ArgumentParser object to manage the program arguments.
287     osg::ArgumentParser arguments(&argc,argv);
288 
289     // set up the usage document, in case we need to print out how to use this program.
290     arguments.getApplicationUsage()->setDescription(arguments.getApplicationName()+" is the example which demonstrates use of convex planer occluders.");
291     arguments.getApplicationUsage()->setCommandLineUsage(arguments.getApplicationName()+" [options] filename ...");
292     arguments.getApplicationUsage()->addCommandLineOption("-h or --help","Display this information");
293     arguments.getApplicationUsage()->addCommandLineOption("-m","Manually create occluders");
294 
295     // initialize the viewer.
296     osgViewer::Viewer viewer;
297 
298     bool manuallyCreateOccluders = false;
299     while (arguments.read("-m")) { manuallyCreateOccluders = true; }
300 
301     if (manuallyCreateOccluders)
302     {
303         viewer.addEventHandler(new OccluderEventHandler(&viewer));
304     }
305 
306     // if user requests help write it out to cout.
307     if (arguments.read("-h") || arguments.read("--help"))
308     {
309         arguments.getApplicationUsage()->write(std::cout);
310         return 1;
311     }
312 
313     // load the nodes from the commandline arguments.
314     osg::ref_ptr<osg::Node> loadedmodel = osgDB::readRefNodeFiles(arguments);
315 
316     // if not loaded assume no arguments passed in, try using default mode instead.
317     if (!loadedmodel) loadedmodel = osgDB::readRefNodeFile("glider.osgt");
318 
319     if (!loadedmodel)
320     {
321         osg::notify(osg::NOTICE)<<"Please specify a model filename on the command line."<<std::endl;
322         return 1;
323     }
324 
325     // run optimization over the scene graph
326     osgUtil::Optimizer optimzer;
327     optimzer.optimize(loadedmodel);
328 
329     // add the occluders to the loaded model.
330     osg::ref_ptr<osg::Group> rootnode;
331 
332     if (manuallyCreateOccluders)
333     {
334         rootnode = new osg::Group;
335         rootnode->addChild(loadedmodel);
336     }
337     else
338     {
339         rootnode = createOccludersAroundModel(loadedmodel.get());
340     }
341 
342 
343     // add a viewport to the viewer and attach the scene graph.
344     viewer.setSceneData( rootnode );
345 
346     return viewer.run();
347 }
348