1 /* -*-c++-*- */
2 /* osgEarth - Geospatial SDK for OpenSceneGraph
3  * Copyright 2019 Pelican Mapping
4  * http://osgearth.org
5  *
6  * osgEarth is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU Lesser General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>
18  */
19 
20 #include "SimpleModelOptions"
21 #include <osgEarth/ModelSource>
22 #include <osgEarth/Registry>
23 #include <osgEarth/Map>
24 #include <osgEarth/ShaderGenerator>
25 #include <osgEarth/FileUtils>
26 #include <osgEarth/StateSetCache>
27 #include <osg/CullStack>
28 #include <osg/LOD>
29 #include <osg/ProxyNode>
30 #include <osg/Notify>
31 #include <osg/MatrixTransform>
32 #include <osg/io_utils>
33 #include <osgDB/FileNameUtils>
34 
35 using namespace osgEarth;
36 using namespace osgEarth::Drivers;
37 
38 //--------------------------------------------------------------------------
39 
40 namespace
41 {
42     class LODScaleOverrideNode : public osg::Group
43     {
44     public:
LODScaleOverrideNode()45         LODScaleOverrideNode() : m_lodScale(1.0f) {}
~LODScaleOverrideNode()46         virtual ~LODScaleOverrideNode() {}
47     public:
setLODScale(float scale)48         void setLODScale(float scale) { m_lodScale = scale; }
getLODScale() const49         float getLODScale() const { return m_lodScale; }
50 
traverse(osg::NodeVisitor & nv)51         virtual void traverse(osg::NodeVisitor& nv)
52         {
53             if(nv.getVisitorType() == osg::NodeVisitor::CULL_VISITOR)
54             {
55                 osg::CullStack* cullStack = dynamic_cast<osg::CullStack*>(&nv);
56                 if(cullStack)
57                 {
58                     float oldLODScale = cullStack->getLODScale();
59                     cullStack->setLODScale(oldLODScale * m_lodScale);
60                     osg::Group::traverse(nv);
61                     cullStack->setLODScale(oldLODScale);
62                 }
63                 else
64                     osg::Group::traverse(nv);
65             }
66             else
67                 osg::Group::traverse(nv);
68         }
69 
70     private:
71         float m_lodScale;
72     };
73 
74     class SetLoadPriorityVisitor : public osg::NodeVisitor
75     {
76     public:
SetLoadPriorityVisitor(float scale=1.0f,float offset=0.0f)77         SetLoadPriorityVisitor(float scale=1.0f, float offset=0.0f)
78             : osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN)
79             , m_scale(scale)
80             , m_offset(offset)
81         {
82             setNodeMaskOverride( ~0 );
83         }
84 
apply(osg::PagedLOD & node)85         virtual void apply(osg::PagedLOD& node)
86         {
87             for(unsigned n = 0; n < node.getNumFileNames(); n++)
88             {
89                 float old;
90                 old = node.getPriorityScale(n);
91                 node.setPriorityScale(n, old * m_scale);
92                 old = node.getPriorityOffset(n);
93                 node.setPriorityOffset(n, old + m_offset);
94             }
95             traverse(node);
96         }
97 
98     private:
99         float m_scale;
100         float m_offset;
101     };
102 
103     /**
104      * Visitor that sets the DBOptions on deferred-loading nodes.
105      */
106     class SetDBOptionsVisitor : public osg::NodeVisitor
107     {
108     private:
109         osg::ref_ptr<osgDB::Options> _dbOptions;
110 
111     public:
SetDBOptionsVisitor(const osgDB::Options * dbOptions)112         SetDBOptionsVisitor(const osgDB::Options* dbOptions)
113         {
114             setTraversalMode( TRAVERSE_ALL_CHILDREN );
115             setNodeMaskOverride( ~0 );
116             _dbOptions = Registry::cloneOrCreateOptions( dbOptions );
117         }
118 
119     public: // osg::NodeVisitor
120 
apply(osg::PagedLOD & node)121         void apply(osg::PagedLOD& node)
122         {
123             node.setDatabaseOptions( _dbOptions.get() );
124             traverse(node);
125         }
126 
apply(osg::ProxyNode & node)127         void apply(osg::ProxyNode& node)
128         {
129             node.setDatabaseOptions( _dbOptions.get() );
130             traverse(node);
131         }
132     };
133 }
134 
135 //--------------------------------------------------------------------------
136 
137 class SimpleModelSource : public ModelSource
138 {
139 public:
SimpleModelSource(const ModelSourceOptions & options)140     SimpleModelSource( const ModelSourceOptions& options )
141         : ModelSource( options ), _options(options) { }
142 
143     //override
initialize(const osgDB::Options * dbOptions)144     Status initialize( const osgDB::Options* dbOptions )
145     {
146         _dbOptions = dbOptions;
147         //ModelSource::initialize( dbOptions );
148         return Status::OK();
149     }
150 
151     // override
createNodeImplementation(const Map * map,ProgressCallback * progress)152     osg::Node* createNodeImplementation(const Map* map, ProgressCallback* progress)
153     {
154         osg::ref_ptr<osg::Node> result;
155 
156         // Set up the DB Options for possible paged or proxy loading.
157         osg::ref_ptr<osgDB::Options> localDBOptions =
158             Registry::instance()->cloneOrCreateOptions( _dbOptions.get() );
159 
160         localDBOptions->getDatabasePathList().push_back( osgDB::getFilePath(_options.url()->full()) );
161 
162 
163         // Only support paging if they've enabled it and provided a min/max range
164         bool usePagedLOD = *_options.paged() &&
165                           (_options.minRange().isSet() || _options.maxRange().isSet());
166 
167         if (_options.node() != NULL)
168         {
169             result = _options.node();
170         }
171         else
172         {
173             // Only load the model if it's not paged or we don't have a location set.
174             if (!usePagedLOD || !_options.location().isSet())
175             {
176                 result = _options.url()->getNode( localDBOptions.get(), progress );
177             }
178         }
179 
180         // Always create a matrix transform
181         osg::MatrixTransform* mt = new osg::MatrixTransform;
182 
183         if (_options.location().isSet() && map != 0L)
184         {
185             GeoPoint geoPoint(
186                 map->getProfile()->getSRS(),
187                 (*_options.location()).x(),
188                 (*_options.location()).y(),
189                 (*_options.location()).z(),
190                 ALTMODE_ABSOLUTE );
191 
192             osg::Matrixd matrix;
193             geoPoint.createLocalToWorld( matrix );
194 
195             if (_options.orientation().isSet())
196             {
197                 //Apply the rotation
198                 osg::Matrix rot_mat;
199                 rot_mat.makeRotate(
200                     osg::DegreesToRadians((*_options.orientation()).y()), osg::Vec3(1,0,0),
201                     osg::DegreesToRadians((*_options.orientation()).x()), osg::Vec3(0,0,1),
202                     osg::DegreesToRadians((*_options.orientation()).z()), osg::Vec3(0,1,0) );
203                 matrix.preMult(rot_mat);
204             }
205             mt->setMatrix( matrix );
206         }
207 
208         if ( _options.minRange().isSet() || _options.maxRange().isSet() )
209         {
210             float minRange = _options.minRange().isSet() ? (*_options.minRange()) : 0.0f;
211             float maxRange = _options.maxRange().isSet() ? (*_options.maxRange()) : FLT_MAX;
212 
213             osg::LOD* lod = 0;
214 
215             if (!usePagedLOD)
216             {
217                 // Just use a regular LOD
218                 lod = new osg::LOD();
219                 lod->addChild(result.release(), minRange, maxRange);
220             }
221             else
222             {
223                 // Use a PagedLOD
224                 osg::PagedLOD* plod =new osg::PagedLOD();
225                 plod->setFileName(0, _options.url()->full());
226 
227                 // If they want the model to be paged but haven't given us a location we have to load
228                 // up the node up front and figure out what it's center and radius are or it won't page in.
229                 if (!_options.location().isSet() && result.valid())
230                 {
231                     osg::Vec3d center = result->getBound().center();
232                     OE_DEBUG << "Radius=" << result->getBound().radius() << " center=" << center.x() << "," << center.y() << "," << center.z() << std::endl;
233                     plod->setCenter(result->getBound().center());
234                     plod->setRadius(osg::maximum(result->getBound().radius(),
235                                              static_cast<osg::BoundingSphere::value_type>(maxRange)));
236 
237                 }
238                 lod = plod;
239             }
240             lod->setRange(0, minRange, maxRange);
241             mt->addChild(lod);
242         }
243         else
244         {
245             // Simply add the node to the matrix transform
246             if (result.valid())
247             {
248                 mt->addChild( result.get() );
249             }
250         }
251 
252         result = mt;
253 
254         // generate a shader program to render the model.
255         if ( result.valid() )
256         {
257             if(_options.loadingPriorityScale().isSet() || _options.loadingPriorityOffset().isSet())
258             {
259                 SetLoadPriorityVisitor slpv(_options.loadingPriorityScale().value(), _options.loadingPriorityOffset().value());
260                 result->accept(slpv);
261             }
262 
263             if(_options.lodScale().isSet())
264             {
265                 LODScaleOverrideNode * node = new LODScaleOverrideNode;
266                 node->setLODScale(_options.lodScale().value());
267                 node->addChild(result.release());
268                 result = node;
269             }
270 
271             if ( _options.shaderPolicy() == SHADERPOLICY_GENERATE )
272             {
273                 osg::ref_ptr<StateSetCache> cache = new StateSetCache();
274 
275                 Registry::shaderGenerator().run(
276                     result.get(),
277                     _options.url()->base(),
278                     cache.get() );
279             }
280             else if ( _options.shaderPolicy() == SHADERPOLICY_DISABLE )
281             {
282                 result->getOrCreateStateSet()->setAttributeAndModes(
283                     new osg::Program(),
284                     osg::StateAttribute::OFF | osg::StateAttribute::OVERRIDE );
285             }
286 
287             // apply the DB options if there are any, so that deferred nodes like PagedLOD et al
288             // will inherit the loading options.
289             if ( _dbOptions.valid() )
290             {
291                 SetDBOptionsVisitor setDBO( localDBOptions.get() );
292                 result->accept( setDBO );
293             }
294         }
295 
296 
297         return result.release();
298     }
299 
300 protected:
301 
302     const SimpleModelOptions           _options;
303     osg::ref_ptr<const osgDB::Options> _dbOptions;
304 };
305 
306 
307 class SimpleModelSourceFactory : public ModelSourceDriver
308 {
309 public:
SimpleModelSourceFactory()310     SimpleModelSourceFactory()
311     {
312         supportsExtension( "osgearth_model_simple", "osgEarth simple model plugin" );
313     }
314 
className() const315     virtual const char* className() const
316     {
317         return "osgEarth Simple Model Plugin";
318     }
319 
readObject(const std::string & file_name,const Options * options) const320     virtual ReadResult readObject(const std::string& file_name, const Options* options) const
321     {
322         if ( !acceptsExtension(osgDB::getLowerCaseFileExtension( file_name )))
323             return ReadResult::FILE_NOT_HANDLED;
324 
325         return ReadResult( new SimpleModelSource( getModelSourceOptions(options) ) );
326     }
327 };
328 
329 REGISTER_OSGPLUGIN(osgearth_model_simple, SimpleModelSourceFactory)
330