1 /* OpenSceneGraph example, osgterrain.
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 <osg/ArgumentParser>
20 #include <osgDB/ReadFile>
21
22 #include <osgViewer/Viewer>
23 #include <osgViewer/ViewerEventHandlers>
24
25 #include <osgGA/TrackballManipulator>
26 #include <osgGA/FlightManipulator>
27 #include <osgGA/DriveManipulator>
28 #include <osgGA/KeySwitchMatrixManipulator>
29 #include <osgGA/StateSetManipulator>
30 #include <osgGA/AnimationPathManipulator>
31 #include <osgGA/TerrainManipulator>
32
33 #include <osgTerrain/Terrain>
34 #include <osgTerrain/TerrainTile>
35 #include <osgTerrain/GeometryTechnique>
36 #include <osgTerrain/DisplacementMappingTechnique>
37 #include <osgTerrain/Layer>
38
39 #include <osgFX/MultiTextureControl>
40
41
42 #include <iostream>
43
44 template<class T>
45 class FindTopMostNodeOfTypeVisitor : public osg::NodeVisitor
46 {
47 public:
FindTopMostNodeOfTypeVisitor()48 FindTopMostNodeOfTypeVisitor():
49 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
50 _foundNode(0)
51 {}
52
apply(osg::Node & node)53 void apply(osg::Node& node)
54 {
55 T* result = dynamic_cast<T*>(&node);
56 if (result)
57 {
58 _foundNode = result;
59 }
60 else
61 {
62 traverse(node);
63 }
64 }
65
66 T* _foundNode;
67 };
68
69 template<class T>
findTopMostNodeOfType(osg::Node * node)70 T* findTopMostNodeOfType(osg::Node* node)
71 {
72 if (!node) return 0;
73
74 FindTopMostNodeOfTypeVisitor<T> fnotv;
75 node->accept(fnotv);
76
77 return fnotv._foundNode;
78 }
79
80 // class to handle events with a pick
81 class TerrainHandler : public osgGA::GUIEventHandler {
82 public:
83
TerrainHandler(osgTerrain::Terrain * terrain,osgFX::MultiTextureControl * mtc)84 TerrainHandler(osgTerrain::Terrain* terrain, osgFX::MultiTextureControl* mtc):
85 _terrain(terrain),
86 _mtc(mtc) {}
87
handle(const osgGA::GUIEventAdapter & ea,osgGA::GUIActionAdapter & aa)88 bool handle(const osgGA::GUIEventAdapter& ea,osgGA::GUIActionAdapter& aa)
89 {
90 switch(ea.getEventType())
91 {
92 case(osgGA::GUIEventAdapter::KEYDOWN):
93 {
94 if (ea.getKey()=='r')
95 {
96 _terrain->setSampleRatio(_terrain->getSampleRatio()*0.5);
97 osg::notify(osg::NOTICE)<<"Sample ratio "<<_terrain->getSampleRatio()<<std::endl;
98 return true;
99 }
100 else if (ea.getKey()=='R')
101 {
102 _terrain->setSampleRatio(_terrain->getSampleRatio()/0.5);
103 osg::notify(osg::NOTICE)<<"Sample ratio "<<_terrain->getSampleRatio()<<std::endl;
104 return true;
105 }
106 else if (ea.getKey()=='v')
107 {
108 _terrain->setVerticalScale(_terrain->getVerticalScale()*1.25);
109 osg::notify(osg::NOTICE)<<"Vertical scale "<<_terrain->getVerticalScale()<<std::endl;
110 return true;
111 }
112 else if (ea.getKey()=='V')
113 {
114 _terrain->setVerticalScale(_terrain->getVerticalScale()/1.25);
115 osg::notify(osg::NOTICE)<<"Vertical scale "<<_terrain->getVerticalScale()<<std::endl;
116 return true;
117 }
118 else if (ea.getKey()=='!') // shift 1
119 {
120 assignTextureWeightToSingleTextureUnit(1);
121 return true;
122 }
123 else if (ea.getKey()=='"') // shift 1
124 {
125 assignTextureWeightToSingleTextureUnit(2);
126 return true;
127 }
128 else if (ea.getKey()==')') // shift 1
129 {
130 assignTextureWeightToSingleTextureUnit(0);
131 return true;
132 }
133 else if (ea.getKey()=='A')
134 {
135 assignedToAll();
136 return true;
137 }
138 else if (ea.getKey()=='l')
139 {
140 toggleDefine("LIGHTING");
141 return true;
142 }
143 else if (ea.getKey()=='h')
144 {
145 toggleDefine("HEIGHTFIELD_LAYER");
146 return true;
147 }
148 else if (ea.getKey()=='t')
149 {
150 toggleDefine("TEXTURE_2D");
151 return true;
152 }
153 else if (ea.getKey()=='y')
154 {
155 toggleDefine("COLOR_LAYER0");
156 return true;
157 }
158 else if (ea.getKey()=='u')
159 {
160 toggleDefine("COLOR_LAYER1");
161 return true;
162 }
163 else if (ea.getKey()=='i')
164 {
165 toggleDefine("COLOR_LAYER2");
166 return true;
167 }
168 else if (ea.getKey()=='d')
169 {
170 toggleDefine("COMPUTE_DIAGONALS", osg::StateAttribute::OFF);
171 return true;
172 }
173
174 return false;
175 }
176 default:
177 return false;
178 }
179 }
180
toggleDefine(const std::string & defineName,int expectedDefault=osg::StateAttribute::ON)181 void toggleDefine(const std::string& defineName, int expectedDefault=osg::StateAttribute::ON)
182 {
183 osg::StateSet::DefineList& defineList = _terrain->getOrCreateStateSet()->getDefineList();
184 osg::StateSet::DefineList::iterator itr = defineList.find(defineName);
185 if (itr==defineList.end())
186 {
187 defineList[defineName].second = (expectedDefault | osg::StateAttribute::OVERRIDE); // assume the defines start off.
188 itr = defineList.find(defineName);
189 }
190
191 osg::StateSet::DefinePair& dp = itr->second;
192 if ( (dp.second & osg::StateAttribute::ON)==0) dp.second = (osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
193 else dp.second = (osg::StateAttribute::OFF | osg::StateAttribute::OVERRIDE);
194
195
196 }
197
198 protected:
199
~TerrainHandler()200 ~TerrainHandler() {}
201
assignTextureWeightToSingleTextureUnit(unsigned int unit)202 void assignTextureWeightToSingleTextureUnit(unsigned int unit)
203 {
204 if (!_mtc) return;
205 for(unsigned int i=0; i<_mtc->getNumTextureWeights(); ++i)
206 {
207 _mtc->setTextureWeight(i, (i==unit) ? 1.0f : 0.0f);
208 }
209 }
210
assignedToAll()211 void assignedToAll()
212 {
213 if (!_mtc && _mtc->getNumTextureWeights()>0) return;
214 float div = 1.0f/static_cast<float>(_mtc->getNumTextureWeights());
215 for(unsigned int i=0; i<_mtc->getNumTextureWeights(); ++i)
216 {
217 _mtc->setTextureWeight(i, div);
218 }
219 }
220
221 osg::ref_ptr<osgTerrain::Terrain> _terrain;
222 osg::ref_ptr<osgFX::MultiTextureControl> _mtc;
223 };
224
225 class CleanTechniqueReadFileCallback : public osgDB::ReadFileCallback
226 {
227
228 public:
229
230 class CleanTechniqueVisitor : public osg::NodeVisitor
231 {
232 public:
CleanTechniqueVisitor()233 CleanTechniqueVisitor():
234 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN) {}
235
apply(osg::Node & node)236 void apply(osg::Node& node)
237 {
238 osgTerrain::TerrainTile* tile = dynamic_cast<osgTerrain::TerrainTile*>(&node);
239 if (tile)
240 {
241 if (tile->getTerrainTechnique())
242 {
243 // OSG_NOTICE<<"Resetting TerrainTechnhique "<<tile->getTerrainTechnique()->className()<<" to 0"<<std::endl;
244 tile->setTerrainTechnique(0);
245 }
246 }
247 else
248 {
249 traverse(node);
250 }
251 }
252 };
253
254
readNode(const std::string & filename,const osgDB::Options * options)255 virtual osgDB::ReaderWriter::ReadResult readNode(const std::string& filename, const osgDB::Options* options)
256 {
257 osgDB::ReaderWriter::ReadResult rr = ReadFileCallback::readNode(filename, options);
258 if (rr.validNode())
259 {
260 CleanTechniqueVisitor ctv;
261 rr.getNode()->accept(ctv);
262 }
263 return rr;
264 }
265
266 protected:
~CleanTechniqueReadFileCallback()267 virtual ~CleanTechniqueReadFileCallback() {}
268 };
269
270
main(int argc,char ** argv)271 int main(int argc, char** argv)
272 {
273 osg::ArgumentParser arguments(&argc, argv);
274
275 // construct the viewer.
276 osgViewer::Viewer viewer(arguments);
277
278 // set up the camera manipulators.
279 {
280 osg::ref_ptr<osgGA::KeySwitchMatrixManipulator> keyswitchManipulator = new osgGA::KeySwitchMatrixManipulator;
281
282 keyswitchManipulator->addMatrixManipulator( '1', "Trackball", new osgGA::TrackballManipulator() );
283 keyswitchManipulator->addMatrixManipulator( '2', "Flight", new osgGA::FlightManipulator() );
284 keyswitchManipulator->addMatrixManipulator( '3', "Drive", new osgGA::DriveManipulator() );
285 keyswitchManipulator->addMatrixManipulator( '4', "Terrain", new osgGA::TerrainManipulator() );
286
287 std::string pathfile;
288 char keyForAnimationPath = '5';
289 while (arguments.read("-p",pathfile))
290 {
291 osgGA::AnimationPathManipulator* apm = new osgGA::AnimationPathManipulator(pathfile);
292 if (apm || !apm->valid())
293 {
294 unsigned int num = keyswitchManipulator->getNumMatrixManipulators();
295 keyswitchManipulator->addMatrixManipulator( keyForAnimationPath, "Path", apm );
296 keyswitchManipulator->selectMatrixManipulator(num);
297 ++keyForAnimationPath;
298 }
299 }
300
301 viewer.setCameraManipulator( keyswitchManipulator.get() );
302 }
303
304
305 // add the state manipulator
306 viewer.addEventHandler( new osgGA::StateSetManipulator(viewer.getCamera()->getOrCreateStateSet()) );
307
308 // add the stats handler
309 viewer.addEventHandler(new osgViewer::StatsHandler);
310
311 // add the record camera path handler
312 viewer.addEventHandler(new osgViewer::RecordCameraPathHandler);
313
314 // add the window size toggle handler
315 viewer.addEventHandler(new osgViewer::WindowSizeHandler);
316
317 // obtain the vertical scale
318 float verticalScale = 1.0f;
319 while(arguments.read("-v",verticalScale)) {}
320
321 // obtain the sample ratio
322 float sampleRatio = 1.0f;
323 while(arguments.read("-r",sampleRatio)) {}
324
325 osgTerrain::TerrainTile::BlendingPolicy blendingPolicy = osgTerrain::TerrainTile::INHERIT;
326 std::string strBlendingPolicy;
327 while(arguments.read("--blending-policy", strBlendingPolicy))
328 {
329 if (strBlendingPolicy == "INHERIT") blendingPolicy = osgTerrain::TerrainTile::INHERIT;
330 else if (strBlendingPolicy == "DO_NOT_SET_BLENDING") blendingPolicy = osgTerrain::TerrainTile::DO_NOT_SET_BLENDING;
331 else if (strBlendingPolicy == "ENABLE_BLENDING") blendingPolicy = osgTerrain::TerrainTile::ENABLE_BLENDING;
332 else if (strBlendingPolicy == "ENABLE_BLENDING_WHEN_ALPHA_PRESENT") blendingPolicy = osgTerrain::TerrainTile::ENABLE_BLENDING_WHEN_ALPHA_PRESENT;
333 }
334
335 bool useDisplacementMappingTechnique = arguments.read("--dm");
336 if (useDisplacementMappingTechnique)
337 {
338 osgDB::Registry::instance()->setReadFileCallback(new CleanTechniqueReadFileCallback());
339 }
340
341 bool setDatabaseThreadAffinity = false;
342 unsigned int cpuNum = 0;
343 while(arguments.read("--db-affinity", cpuNum)) { setDatabaseThreadAffinity = true; }
344
345 // load the nodes from the commandline arguments.
346 osg::ref_ptr<osg::Node> rootnode = osgDB::readRefNodeFiles(arguments);
347
348 if (!rootnode)
349 {
350 osg::notify(osg::NOTICE)<<"Warning: no valid data loaded, please specify a database on the command line."<<std::endl;
351 return 1;
352 }
353
354 osg::ref_ptr<osgTerrain::Terrain> terrain = findTopMostNodeOfType<osgTerrain::Terrain>(rootnode.get());
355 if (!terrain)
356 {
357 // no Terrain node present insert one above the loaded model.
358 terrain = new osgTerrain::Terrain;
359
360 // if CoordinateSystemNode is present copy it's contents into the Terrain, and discard it.
361 osg::CoordinateSystemNode* csn = findTopMostNodeOfType<osg::CoordinateSystemNode>(rootnode.get());
362 if (csn)
363 {
364 terrain->set(*csn);
365 for(unsigned int i=0; i<csn->getNumChildren();++i)
366 {
367 terrain->addChild(csn->getChild(i));
368 }
369 }
370 else
371 {
372 terrain->addChild(rootnode.get());
373 }
374
375 rootnode = terrain.get();
376 }
377
378 terrain->setSampleRatio(sampleRatio);
379 terrain->setVerticalScale(verticalScale);
380 terrain->setBlendingPolicy(blendingPolicy);
381
382
383 if (useDisplacementMappingTechnique)
384 {
385 terrain->setTerrainTechniquePrototype(new osgTerrain::DisplacementMappingTechnique());
386 }
387
388
389 // register our custom handler for adjust Terrain settings
390 viewer.addEventHandler(new TerrainHandler(terrain.get(), findTopMostNodeOfType<osgFX::MultiTextureControl>(rootnode.get())));
391
392 // add a viewport to the viewer and attach the scene graph.
393 viewer.setSceneData( rootnode.get() );
394
395 // if required set the DatabaseThread affinity, note must call after viewer.setSceneData() so that the osgViewer::Scene object is constructed with it's DatabasePager.
396 if (setDatabaseThreadAffinity)
397 {
398 for (unsigned int i=0; i<viewer.getDatabasePager()->getNumDatabaseThreads(); ++i)
399 {
400 osgDB::DatabasePager::DatabaseThread* thread = viewer.getDatabasePager()->getDatabaseThread(i);
401 thread->setProcessorAffinity(cpuNum);
402 OSG_NOTICE<<"Settings affinity of DatabaseThread="<<thread<<" isRunning()="<<thread->isRunning()<<" cpuNum="<<cpuNum<<std::endl;
403 }
404 }
405
406 // following are tests of the #pragma(tic) shader composition
407 //terrain->getOrCreateStateSet()->setDefine("NUM_LIGHTS", "1");
408 //terrain->getOrCreateStateSet()->setDefine("LIGHTING"); // , osg::StateAttribute::OFF|osg::StateAttribute::OVERRIDE);
409 //terrain->getOrCreateStateSet()->setDefine("COMPUTE_DIAGONALS"); // , osg::StateAttribute::OFF|osg::StateAttribute::OVERRIDE);
410
411 // run the viewers main loop
412 return viewer.run();
413
414 }
415