]> git.mxchange.org Git - simgear.git/blob - simgear/scene/tgdb/ReaderWriterSTG.cxx
Change random object placement to a grid-like scheme to reduce building overlap.
[simgear.git] / simgear / scene / tgdb / ReaderWriterSTG.cxx
1 // tileentry.cxx -- routines to handle a scenery tile
2 //
3 // Written by Curtis Olson, started May 1998.
4 //
5 // Copyright (C) 1998 - 2001  Curtis L. Olson  - http://www.flightgear.org/~curt
6 //
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License as
9 // published by the Free Software Foundation; either version 2 of the
10 // License, or (at your option) any later version.
11 //
12 // This program is distributed in the hope that it will be useful, but
13 // WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 // General Public License for more details.
16 //
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
20 //
21
22 #ifdef HAVE_CONFIG_H
23 #  include <simgear_config.h>
24 #endif
25
26 #include "ReaderWriterSTG.hxx"
27
28 #include <osg/MatrixTransform>
29 #include <osg/ProxyNode>
30
31 #include <osgDB/FileNameUtils>
32 #include <osgDB/FileUtils>
33 #include <osgDB/Registry>
34 #include <osgDB/ReaderWriter>
35 #include <osgDB/ReadFile>
36
37 #include <simgear/bucket/newbucket.hxx>
38 #include <simgear/debug/logstream.hxx>
39 #include <simgear/misc/sgstream.hxx>
40 #include <simgear/scene/util/SGReaderWriterOptions.hxx>
41 #include <simgear/scene/material/mat.hxx>
42 #include <simgear/scene/material/matlib.hxx>
43 #include <simgear/scene/tgdb/apt_signs.hxx>
44 #include <simgear/scene/tgdb/obj.hxx>
45
46 #include "SGOceanTile.hxx"
47
48 using namespace simgear;
49
50 ReaderWriterSTG::ReaderWriterSTG()
51 {
52     supportsExtension("stg", "SimGear stg database format");
53 }
54
55 ReaderWriterSTG::~ReaderWriterSTG()
56 {
57 }
58
59 const char* ReaderWriterSTG::className() const
60 {
61     return "STG Database reader";
62 }
63
64 osgDB::ReaderWriter::ReadResult
65 ReaderWriterSTG::readNode(const std::string& fileName, const osgDB::Options* options) const
66 {
67     // We treat 123.stg different than ./123.stg.
68     // The difference is that ./123.stg as well as any absolute path
69     // really loads the given stg file and only this.
70     // In contrast 123.stg uses the search paths to load a set of stg
71     // files spread across the scenery directories.
72     if (osgDB::getSimpleFileName(fileName) != fileName)
73         return readStgFile(fileName, options);
74
75     // For stg meta files, we need options for the search path.
76     if (!options)
77         return ReadResult::FILE_NOT_FOUND;
78
79     // Extract the bucket from the filename
80     std::istringstream ss(osgDB::getNameLessExtension(fileName));
81     long index;
82     ss >> index;
83     if (ss.fail())
84         return ReadResult::FILE_NOT_FOUND;
85
86     SG_LOG(SG_TERRAIN, SG_INFO, "Loading tile " << fileName);
87
88     SGBucket bucket(index);
89     std::string basePath = bucket.gen_base_path();
90
91     osg::ref_ptr<osg::Group> group = new osg::Group;
92
93     // Stop scanning once an object base is found
94     bool foundBase = false;
95     // This is considered a meta file, so apply the scenery path search
96     const osgDB::FilePathList& filePathList = options->getDatabasePathList();
97     for (osgDB::FilePathList::const_iterator i = filePathList.begin();
98          i != filePathList.end() && !foundBase; ++i) {
99         SGPath objects(*i);
100         objects.append("Objects");
101         objects.append(basePath);
102         objects.append(fileName);
103         if (readStgFile(objects.str(), *group, options))
104             foundBase = true;
105         
106         SGPath terrain(*i);
107         terrain.append("Terrain");
108         terrain.append(basePath);
109         terrain.append(fileName);
110         if (readStgFile(terrain.str(), *group, options))
111             foundBase = true;
112     }
113     
114     // ... or generate an ocean tile on the fly
115     if (!foundBase) {
116         SG_LOG(SG_TERRAIN, SG_INFO, "  Generating ocean tile");
117         
118         osg::ref_ptr<SGReaderWriterOptions> opt;
119         opt = SGReaderWriterOptions::copyOrCreate(options);
120         osg::Node* node = SGOceanTile(bucket, opt->getMaterialLib());
121         if ( node ) {
122             group->addChild(node);
123         } else {
124             SG_LOG( SG_TERRAIN, SG_ALERT,
125                     "Warning: failed to generate ocean tile!" );
126         }
127     }
128
129     return group.get();
130 }
131
132 osgDB::ReaderWriter::ReadResult
133 ReaderWriterSTG::readStgFile(const std::string& fileName, const osgDB::Options* options) const
134 {
135     // This is considered a real existing file.
136     // We still apply the search path algorithms for relative files.
137     osg::ref_ptr<osg::Group> group = new osg::Group;
138
139     readStgFile(osgDB::findDataFile(fileName, options), *group, options);
140
141     return group.get();
142 }
143
144 bool
145 ReaderWriterSTG::readStgFile(const std::string& absoluteFileName,
146                              osg::Group& group, const osgDB::Options* options) const
147 {
148     if (absoluteFileName.empty())
149         return false;
150
151     sg_gzifstream in( absoluteFileName );
152     if ( !in.is_open() )
153         return false;
154
155     SG_LOG(SG_TERRAIN, SG_INFO, "Loading stg file " << absoluteFileName);
156     
157     std::string filePath = osgDB::getFilePath(absoluteFileName);
158
159     osg::ref_ptr<SGReaderWriterOptions> staticOptions;
160     staticOptions = SGReaderWriterOptions::copyOrCreate(options);
161     staticOptions->getDatabasePathList().clear();
162     staticOptions->getDatabasePathList().push_back(filePath);
163     staticOptions->setObjectCacheHint(osgDB::Options::CACHE_NONE);
164     
165     osg::ref_ptr<SGReaderWriterOptions> sharedOptions;
166     sharedOptions = SGReaderWriterOptions::copyOrCreate(options);
167     sharedOptions->getDatabasePathList().clear();
168     
169     SGPath path = filePath;
170     path.append(".."); path.append(".."); path.append("..");
171     sharedOptions->getDatabasePathList().push_back(path.str());
172     std::string fg_root = options->getPluginStringData("SimGear::FG_ROOT");
173     sharedOptions->getDatabasePathList().push_back(fg_root);
174     
175     bool has_base = false;
176     while ( ! in.eof() ) {
177         std::string token;
178         in >> token;
179         
180         // No comment
181         if ( token.empty() || token[0] == '#' ) {
182             in >> ::skipeol;
183             continue;
184         }
185         
186         // Then there is always a name
187         std::string name;
188         in >> name;
189         
190         SGPath path = filePath;
191         path.append(name);
192         
193         osg::ref_ptr<osg::Node> node;
194         if ( token == "OBJECT_BASE" ) {
195             // Load only once (first found)
196             SG_LOG( SG_TERRAIN, SG_BULK, "    " << token << " " << name );
197             
198             has_base = true;
199             node = osgDB::readRefNodeFile(path.str(),
200                                           staticOptions.get());
201                 
202             if (!node.valid()) {
203                 SG_LOG( SG_TERRAIN, SG_ALERT, absoluteFileName
204                         << ": Failed to load OBJECT_BASE '"
205                         << name << "'" );
206             }
207             
208         } else if ( token == "OBJECT" ) {
209             node = osgDB::readRefNodeFile(path.str(),
210                                           staticOptions.get());
211                 
212             if (!node.valid()) {
213                 SG_LOG( SG_TERRAIN, SG_ALERT, absoluteFileName
214                         << ": Failed to load OBJECT '"
215                         << name << "'" );
216             }
217             
218         } else {
219             double lon, lat, elev, hdg;
220             in >> lon >> lat >> elev >> hdg;
221             
222             // Always OK to load
223             if ( token == "OBJECT_STATIC" ) {
224                 osg::ref_ptr<SGReaderWriterOptions> opt;
225                 opt = new SGReaderWriterOptions(*staticOptions);
226                 osg::ProxyNode* proxyNode = new osg::ProxyNode;
227                 proxyNode->setLoadingExternalReferenceMode(osg::ProxyNode::DEFER_LOADING_TO_DATABASE_PAGER);
228                 /// Hmm, the findDataFile should happen downstream
229                 std::string absName = osgDB::findDataFile(name, opt.get());
230                 proxyNode->setFileName(0, absName);
231                 if (SGPath(absName).lower_extension() == "ac")
232                     opt->setInstantiateEffects(true);
233                 else
234                     opt->setInstantiateEffects(false);
235                 proxyNode->setDatabaseOptions(opt.get());
236                 node = proxyNode;
237                 
238                 if (!node.valid()) {
239                     SG_LOG( SG_TERRAIN, SG_ALERT, absoluteFileName
240                             << ": Failed to load OBJECT_STATIC '"
241                             << name << "'" );
242                 }
243                 
244             } else if ( token == "OBJECT_SHARED" ) {
245                 osg::ref_ptr<SGReaderWriterOptions> opt;
246                 opt = new SGReaderWriterOptions(*sharedOptions);
247                 /// Hmm, the findDataFile should happen in the downstream readers
248                 std::string absName = osgDB::findDataFile(name, opt.get());
249                 if (SGPath(absName).lower_extension() == "ac")
250                     opt->setInstantiateEffects(true);
251                 else
252                     opt->setInstantiateEffects(false);
253                 node = osgDB::readRefNodeFile(absName, opt.get());
254                 
255                 if (!node.valid()) {
256                     SG_LOG( SG_TERRAIN, SG_ALERT, absoluteFileName
257                             << ": Failed to load OBJECT_SHARED '"
258                             << name << "'" );
259                 }
260                 
261             } else if ( token == "OBJECT_SIGN" ) {
262                 node = SGMakeSign(staticOptions->getMaterialLib(), name);
263                 
264             } else if ( token == "OBJECT_RUNWAY_SIGN" ) {
265                 node = SGMakeRunwaySign(staticOptions->getMaterialLib(), name);
266                 
267             } else {
268                 SG_LOG( SG_TERRAIN, SG_ALERT, absoluteFileName
269                         << ": Unknown token '" << token << "'" );
270             }
271             
272             if (node.valid()) {
273                 osg::Matrix matrix;
274                 matrix = makeZUpFrame(SGGeod::fromDegM(lon, lat, elev));
275                 matrix.preMultRotate(osg::Quat(SGMiscd::deg2rad(hdg),
276                                                osg::Vec3(0, 0, 1)));
277                 
278                 osg::MatrixTransform* matrixTransform;
279                 matrixTransform = new osg::MatrixTransform(matrix);
280                 matrixTransform->setDataVariance(osg::Object::STATIC);
281                 matrixTransform->addChild(node.get());
282                 node = matrixTransform;
283             }
284         }
285         
286         if (node.valid())
287             group.addChild(node.get());
288         
289         in >> ::skipeol;
290     }
291
292     return has_base;
293 }