root/OpenSceneGraph/trunk/examples/osgmultitexturecontrol/osgmultitexturecontrol.cpp @ 8843

Revision 8843, 20.9 kB (checked in by robert, 6 years ago)

First cut of WhiteListTileLoadedCallback?

Line 
1/* OpenSceneGraph example, osgmultitexture.
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
20#include <osg/Notify>
21
22#include <osg/Texture2D>
23#include <osg/io_utils>
24
25#include <osgDB/Registry>
26#include <osgDB/ReadFile>
27
28#include <osgFX/MultiTextureControl>
29
30#include <osgGA/TerrainManipulator>
31#include <osgGA/StateSetManipulator>
32#include <osgGA/AnimationPathManipulator>
33#include <osgGA/TrackballManipulator>
34#include <osgGA/FlightManipulator>
35#include <osgGA/DriveManipulator>
36#include <osgGA/KeySwitchMatrixManipulator>
37#include <osgGA/StateSetManipulator>
38#include <osgGA/AnimationPathManipulator>
39#include <osgGA/TerrainManipulator>
40
41#include <osgTerrain/Terrain>
42#include <osgTerrain/TerrainTile>
43
44#include <osgViewer/ViewerEventHandlers>
45#include <osgViewer/Viewer>
46
47
48#include <iostream>
49
50template<class T>
51class FindTopMostNodeOfTypeVisitor : public osg::NodeVisitor
52{
53public:
54    FindTopMostNodeOfTypeVisitor():
55        osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
56        _foundNode(0)
57    {}
58   
59    void apply(osg::Node& node)
60    {
61        T* result = dynamic_cast<T*>(&node);
62        if (result)
63        {
64            _foundNode = result;
65        }
66        else
67        {
68            traverse(node);
69        }
70    }
71   
72    T* _foundNode;
73};
74
75template<class T>
76T* findTopMostNodeOfType(osg::Node* node)
77{
78    if (!node) return 0;
79
80    FindTopMostNodeOfTypeVisitor<T> fnotv;
81    node->accept(fnotv);
82   
83    return fnotv._foundNode;
84}
85
86/** Callback used to track the elevation of the camera and update the texture weights in an MultiTextureControl node.*/
87class ElevationLayerBlendingCallback : public osg::NodeCallback
88{
89    public:
90   
91        typedef std::vector<double> Elevations;
92
93        ElevationLayerBlendingCallback(osgFX::MultiTextureControl* mtc, const Elevations& elevations, float animationTime=4.0f):
94            _previousFrame(-1),
95            _previousTime(0.0),
96            _currentElevation(0.0),
97            _mtc(mtc),
98            _elevations(elevations),
99            _animationTime(animationTime) {}
100   
101        /** Callback method called by the NodeVisitor when visiting a node.*/
102        virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
103        {
104            if (nv->getVisitorType()==osg::NodeVisitor::UPDATE_VISITOR)
105            {
106
107                float deltaTime = 0.01f;
108                if (_previousFrame!=-1)
109                {
110                    deltaTime = float(nv->getFrameStamp()->getReferenceTime() - _previousTime);
111                }
112
113                _previousTime = nv->getFrameStamp()->getReferenceTime();
114                _previousFrame = nv->getFrameStamp()->getFrameNumber();
115
116                if (_mtc.valid() && !_elevations.empty())
117                {
118                    unsigned int index = _mtc->getNumTextureWeights()-1;
119                    for(unsigned int i=0; i<_elevations.size(); ++i)
120                    {
121                        if (_currentElevation>_elevations[i])
122                        {
123                            index = i;
124                            break;
125                        }
126                    }
127
128                    float delta = std::min(deltaTime/_animationTime, 1.0f);
129
130                    for(unsigned int i=0; i<_mtc->getNumTextureWeights(); ++i)
131                    {
132                        float currentValue = _mtc->getTextureWeight(i);
133                        float desiredValue = (i==index) ? 1.0f : 0.0f;
134                        if (desiredValue != currentValue)
135                        {
136                            if (currentValue<desiredValue)
137                            {
138                                desiredValue = std::min(currentValue + delta, desiredValue);
139                            }
140                            else
141                            {
142                                desiredValue = std::max(currentValue - delta, desiredValue);
143                            }
144
145                            _mtc->setTextureWeight(i, desiredValue);
146                        }
147                    }
148
149                }
150            }
151            else if (nv->getVisitorType()==osg::NodeVisitor::CULL_VISITOR)
152            {
153                _currentElevation = nv->getViewPoint().z();
154
155                osg::CoordinateSystemNode* csn = dynamic_cast<osg::CoordinateSystemNode*>(node);
156                if (csn)
157                {
158                    osg::EllipsoidModel* em = csn->getEllipsoidModel();
159                    if (em)
160                    {
161                        double X = nv->getViewPoint().x();
162                        double Y = nv->getViewPoint().y();
163                        double Z = nv->getViewPoint().z();
164                        double latitude, longitude;
165                        em->convertXYZToLatLongHeight(X,Y,Z,latitude, longitude, _currentElevation);
166                    }
167                }
168
169            }
170
171            traverse(node,nv);
172        }
173
174        int                                             _previousFrame;
175        double                                          _previousTime;
176        float                                           _animationTime;
177        double                                          _currentElevation;
178       
179        osg::observer_ptr<osgFX::MultiTextureControl>   _mtc;
180        Elevations                                      _elevations;
181};
182
183
184// class to handle events with a pick
185class TerrainHandler : public osgGA::GUIEventHandler {
186public:
187
188    TerrainHandler(osgTerrain::Terrain* terrain):
189        _terrain(terrain) {}
190   
191    bool handle(const osgGA::GUIEventAdapter& ea,osgGA::GUIActionAdapter& aa)
192    {
193        switch(ea.getEventType())
194        {
195            case(osgGA::GUIEventAdapter::KEYDOWN):
196            {   
197                if (ea.getKey()=='r')
198                {
199                    _terrain->setSampleRatio(_terrain->getSampleRatio()*0.5);
200                    osg::notify(osg::NOTICE)<<"Sample ratio "<<_terrain->getSampleRatio()<<std::endl;
201                    return true;
202                }
203                else if (ea.getKey()=='R')
204                {
205                    _terrain->setSampleRatio(_terrain->getSampleRatio()/0.5);
206                    osg::notify(osg::NOTICE)<<"Sample ratio "<<_terrain->getSampleRatio()<<std::endl;
207                    return true;
208                }
209                else if (ea.getKey()=='v')
210                {
211                    _terrain->setVerticalScale(_terrain->getVerticalScale()*1.25);
212                    osg::notify(osg::NOTICE)<<"Vertical scale "<<_terrain->getVerticalScale()<<std::endl;
213                    return true;
214                }
215                else if (ea.getKey()=='V')
216                {
217                    _terrain->setVerticalScale(_terrain->getVerticalScale()/1.25);
218                    osg::notify(osg::NOTICE)<<"Vertical scale "<<_terrain->getVerticalScale()<<std::endl;
219                    return true;
220                }
221
222                return false;
223            }   
224            default:
225                return false;
226        }
227    }
228   
229protected:
230
231    ~TerrainHandler() {}
232
233    osg::ref_ptr<osgTerrain::Terrain>  _terrain;
234};
235
236
237struct WhiteListTileLoadedCallback : public osgTerrain::TerrainTile::TileLoadedCallback
238{
239    WhiteListTileLoadedCallback()
240    {
241    }
242   
243    void allow(const std::string& setname) { _setWhiteList.insert(setname); }
244   
245    typedef std::set<std::string> SetWhiteList;
246    SetWhiteList _setWhiteList;
247
248    bool layerAcceptable(const std::string& setname) const
249    {
250        if (setname.empty()) return true;
251       
252        return _setWhiteList.count(setname)!=0;
253    }
254
255    bool readImageLayer(osgTerrain::ImageLayer* imageLayer, const osgDB::ReaderWriter::Options* options) const
256    {
257       if (!imageLayer->getImage() &&
258            !imageLayer->getFileName().empty())
259        {
260            if (layerAcceptable(imageLayer->getSetName()))
261            {
262                osg::ref_ptr<osg::Image> image = osgDB::readImageFile(imageLayer->getFileName(), options);
263                std::cout<<"ok readingImageLayer("<<imageLayer->getSetName()<<","<<imageLayer->getFileName()<<" success="<<image->valid()<<std::endl;
264                imageLayer->setImage(image.get());
265            }
266            else
267            {
268                std::cout<<"disallowed readingImageLayer("<<imageLayer->getSetName()<<","<<imageLayer->getFileName()<<std::endl;
269            }
270        }
271        return imageLayer->getImage()!=0;
272    }
273
274    virtual bool deferExternalLayerLoading() const
275    {
276        return true;
277    }
278
279    virtual void loaded(osgTerrain::TerrainTile* tile, const osgDB::ReaderWriter::Options* options) const
280    {
281
282        // read any external layers
283        for(unsigned int i=0; i<tile->getNumColorLayers(); ++i)
284        {
285            osgTerrain::Layer* layer = tile->getColorLayer(i);
286            osgTerrain::ImageLayer* imageLayer = dynamic_cast<osgTerrain::ImageLayer*>(layer);
287            if (imageLayer)
288            {
289                readImageLayer(imageLayer, options);
290                continue;
291            }
292
293            osgTerrain::SwitchLayer* switchLayer = dynamic_cast<osgTerrain::SwitchLayer*>(layer);
294            if (switchLayer)
295            {
296                for(unsigned int si=0; si<switchLayer->getNumLayers(); ++si)
297                {
298                    osgTerrain::ImageLayer* imageLayer = dynamic_cast<osgTerrain::ImageLayer*>(switchLayer->getLayer(si));
299                    if (imageLayer)
300                    {
301                        if (readImageLayer(imageLayer, options))
302                        {                       
303                            // replace SwitchLayer by
304                            tile->setColorLayer(i, imageLayer);
305                            continue;
306                        }
307                    }
308                }
309                continue;
310            }
311
312            osgTerrain::CompositeLayer* compositeLayer = dynamic_cast<osgTerrain::CompositeLayer*>(layer);
313            if (compositeLayer)
314            {
315                for(unsigned int ci=0; ci<compositeLayer->getNumLayers(); ++ci)
316                {
317                    osgTerrain::ImageLayer* imageLayer = dynamic_cast<osgTerrain::ImageLayer*>(compositeLayer->getLayer(ci));
318                    if (imageLayer)
319                    {
320                        readImageLayer(imageLayer, options);
321                    }
322                }
323                continue;
324            }
325        }
326
327        // assign colour layers over missing layers
328        osgTerrain::Layer* validLayer = 0;
329        for(unsigned int i=0; i<tile->getNumColorLayers(); ++i)
330        {
331            osgTerrain::Layer* layer = tile->getColorLayer(i);
332            osgTerrain::ImageLayer* imageLayer = dynamic_cast<osgTerrain::ImageLayer*>(layer);
333            if (imageLayer)
334            {
335                if (imageLayer->getImage()!=0)
336                {
337                    validLayer = imageLayer;
338                }
339                continue;
340            }
341
342            osgTerrain::SwitchLayer* switchLayer = dynamic_cast<osgTerrain::SwitchLayer*>(layer);
343            if (switchLayer)
344            {
345                for(unsigned int si=0; si<switchLayer->getNumLayers(); ++si)
346                {
347                    osgTerrain::ImageLayer* imageLayer = dynamic_cast<osgTerrain::ImageLayer*>(switchLayer->getLayer(si));
348                    if (imageLayer && imageLayer->getImage()!=0)
349                    {
350                        validLayer = imageLayer;
351                    }
352                }
353                continue;
354            }
355
356            osgTerrain::CompositeLayer* compositeLayer = dynamic_cast<osgTerrain::CompositeLayer*>(layer);
357            if (compositeLayer)
358            {
359                for(unsigned int ci=0; ci<compositeLayer->getNumLayers(); ++ci)
360                {
361                    osgTerrain::ImageLayer* imageLayer = dynamic_cast<osgTerrain::ImageLayer*>(switchLayer->getLayer(ci));
362                    if (imageLayer && imageLayer->getImage()!=0)
363                    {
364                        validLayer = imageLayer;
365                    }
366                }
367                continue;
368            }
369        }
370
371        if (validLayer)
372        {
373            // fill in any missing layers
374            for(unsigned int i=0; i<tile->getNumColorLayers(); ++i)
375            {
376                osgTerrain::Layer* layer = tile->getColorLayer(i);
377                osgTerrain::ImageLayer* imageLayer = dynamic_cast<osgTerrain::ImageLayer*>(layer);
378                if (imageLayer)
379                {
380                    if (imageLayer->getImage()==0)
381                    {
382                        tile->setColorLayer(i, validLayer);
383                        break;
384                    }
385                    continue;
386                }
387
388                osgTerrain::SwitchLayer* switchLayer = dynamic_cast<osgTerrain::SwitchLayer*>(layer);
389                if (switchLayer)
390                {
391                    for(unsigned int si=0; si<switchLayer->getNumLayers(); ++si)
392                    {
393                        osgTerrain::ImageLayer* imageLayer = dynamic_cast<osgTerrain::ImageLayer*>(switchLayer->getLayer(si));
394                        if (imageLayer && imageLayer->getImage()==0)
395                        {
396                            tile->setColorLayer(i, validLayer);
397                            break;
398                        }
399                    }
400                    continue;
401                }
402
403                osgTerrain::CompositeLayer* compositeLayer = dynamic_cast<osgTerrain::CompositeLayer*>(layer);
404                if (compositeLayer)
405                {
406                    for(unsigned int ci=0; ci<compositeLayer->getNumLayers(); ++ci)
407                    {
408                        osgTerrain::ImageLayer* imageLayer = dynamic_cast<osgTerrain::ImageLayer*>(switchLayer->getLayer(ci));
409                        if (imageLayer && imageLayer->getImage()==0)
410                        {
411                            tile->setColorLayer(i, validLayer);
412                            break;
413                        }
414                    }
415                    continue;
416                }
417            }
418        }
419    }
420};
421
422int main( int argc, char **argv )
423{
424    // use an ArgumentParser object to manage the program arguments.
425    osg::ArgumentParser arguments(&argc,argv);
426    arguments.getApplicationUsage()->addCommandLineOption("-v","Set the terrain vertical scale.");
427    arguments.getApplicationUsage()->addCommandLineOption("-r","Set the terrain sample ratio.");
428    arguments.getApplicationUsage()->addCommandLineOption("--login <url> <username> <password>","Provide authentication information for http file access.");
429   
430   
431    // set the tile loaded callback to load the optional imagery
432    osg::ref_ptr<WhiteListTileLoadedCallback> whiteList = new WhiteListTileLoadedCallback;
433    std::string setname;
434    while(arguments.read("--allow",setname))
435    {
436        whiteList->allow(setname);
437    }
438    osgTerrain::TerrainTile::setTileLoadedCallback(whiteList.get());
439   
440    // construct the viewer.
441    osgViewer::Viewer viewer(arguments);
442   
443    float verticalScale = 1.0f;
444    while(arguments.read("-v",verticalScale)) {}
445   
446    float sampleRatio = 1.0f;
447    while(arguments.read("-r",sampleRatio)) {}
448
449    std::string url, username, password;
450    while(arguments.read("--login",url, username, password))
451    {
452        if (!osgDB::Registry::instance()->getAuthenticationMap())
453        {
454            osgDB::Registry::instance()->setAuthenticationMap(new osgDB::AuthenticationMap);
455            osgDB::Registry::instance()->getAuthenticationMap()->addAuthenticationDetails(
456                url,
457                new osgDB::AuthenticationDetails(username, password)
458            );
459        }
460    }
461
462    // add all the event handlers to the viewer
463    {
464        // add the state manipulator
465        viewer.addEventHandler( new osgGA::StateSetManipulator(viewer.getCamera()->getOrCreateStateSet()) );
466
467        // add the thread model handler
468        viewer.addEventHandler(new osgViewer::ThreadingHandler);
469
470        // add the window size toggle handler
471        viewer.addEventHandler(new osgViewer::WindowSizeHandler);
472
473        // add the stats handler
474        viewer.addEventHandler(new osgViewer::StatsHandler);
475
476        // add the help handler
477        viewer.addEventHandler(new osgViewer::HelpHandler(arguments.getApplicationUsage()));
478
479        // add the record camera path handler
480        viewer.addEventHandler(new osgViewer::RecordCameraPathHandler);
481
482        // add the LOD Scale handler
483        viewer.addEventHandler(new osgViewer::LODScaleHandler);
484    }
485
486    // add all the camera manipulators
487    {
488        osg::ref_ptr<osgGA::KeySwitchMatrixManipulator> keyswitchManipulator = new osgGA::KeySwitchMatrixManipulator;
489
490        keyswitchManipulator->addMatrixManipulator( '1', "Trackball", new osgGA::TrackballManipulator() );
491        keyswitchManipulator->addMatrixManipulator( '2', "Flight", new osgGA::FlightManipulator() );
492        keyswitchManipulator->addMatrixManipulator( '3', "Drive", new osgGA::DriveManipulator() );
493
494        unsigned int num = keyswitchManipulator->getNumMatrixManipulators();
495        keyswitchManipulator->addMatrixManipulator( '4', "Terrain", new osgGA::TerrainManipulator() );
496
497        std::string pathfile;
498        char keyForAnimationPath = '5';
499        while (arguments.read("-p",pathfile))
500        {
501            osgGA::AnimationPathManipulator* apm = new osgGA::AnimationPathManipulator(pathfile);
502            if (apm || !apm->valid())
503            {
504                num = keyswitchManipulator->getNumMatrixManipulators();
505                keyswitchManipulator->addMatrixManipulator( keyForAnimationPath, "Path", apm );
506                ++keyForAnimationPath;
507            }
508        }
509
510        keyswitchManipulator->selectMatrixManipulator(num);
511
512        viewer.setCameraManipulator( keyswitchManipulator.get() );
513    }
514
515    // set up the scene graph
516    {
517        // load the nodes from the commandline arguments.
518        osg::Node* rootnode = osgDB::readNodeFiles(arguments);
519
520        if (!rootnode)
521        {
522            osg::notify(osg::NOTICE)<<"Warning: no valid data loaded, please specify a database on the command line."<<std::endl;
523            return 1;
524        }
525
526        osgTerrain::Terrain* terrain = findTopMostNodeOfType<osgTerrain::Terrain>(rootnode);
527        if (!terrain)
528        {
529            terrain = new osgTerrain::Terrain;
530            terrain->addChild(rootnode);
531
532            rootnode = terrain;
533        }   
534
535        terrain->setSampleRatio(sampleRatio);
536        terrain->setVerticalScale(verticalScale);
537
538        // register our custom handler for adjust Terrain settings       
539        viewer.addEventHandler(new TerrainHandler(terrain));
540
541        osg::CoordinateSystemNode* csn = findTopMostNodeOfType<osg::CoordinateSystemNode>(rootnode);
542
543        unsigned int numLayers = 1;
544        osgFX::MultiTextureControl* mtc = findTopMostNodeOfType<osgFX::MultiTextureControl>(rootnode);
545        if (mtc)
546        {
547            numLayers = mtc->getNumTextureWeights();
548        }
549
550        if (numLayers<2)
551        {
552            osg::notify(osg::NOTICE)<<"Warning: scene must have MultiTextureControl node with at least 2 texture units defined."<<std::endl;
553            return 1;
554        }
555
556        double maxElevationTransition = 1e6;
557        ElevationLayerBlendingCallback::Elevations elevations;
558        for(unsigned int i=0; i<numLayers; ++i)
559        {
560            elevations.push_back(maxElevationTransition);
561            maxElevationTransition /= 2.0;
562        }
563
564        ElevationLayerBlendingCallback* elbc = new ElevationLayerBlendingCallback(mtc, elevations);
565
566        // assign to the most appropriate node (the CoordinateSystemNode is best as it provides the elevation on the globe.)
567        // note we must assign callback as both an update and cull callback, as update callback to do the update of
568        // the the osgFX::MultiTextureControl node a thread safe way, and as a cull callback to gather the camera
569        // position information.
570        osg::Node* nodeToAssignCallbackTo = csn ? csn : (mtc ? mtc : rootnode);
571        nodeToAssignCallbackTo->setUpdateCallback(elbc);   
572        nodeToAssignCallbackTo->setCullCallback(elbc);   
573
574        // add a viewport to the viewer and attach the scene graph.
575        viewer.setSceneData( rootnode );
576    }
577   
578
579
580    // create the windows and run the threads.
581    viewer.realize();
582
583    return viewer.run();
584}
Note: See TracBrowser for help on using the browser.