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