]> git.mxchange.org Git - simgear.git/blob - simgear/scene/tgdb/ReaderWriterSPT.cxx
Implement osg native scenery paging.
[simgear.git] / simgear / scene / tgdb / ReaderWriterSPT.cxx
1 // ReaderWriterSPT.cxx -- Provide a paged database for flightgear scenery.
2 //
3 // Copyright (C) 2010 - 2011  Mathias Froehlich
4 //
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License as
7 // published by the Free Software Foundation; either version 2 of the
8 // License, or (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful, but
11 // WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 // General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18 //
19
20 #ifdef HAVE_CONFIG_H
21 #  include <simgear_config.h>
22 #endif
23
24 #include "ReaderWriterSPT.hxx"
25
26 #include <cassert>
27
28 #include <osg/CullFace>
29 #include <osg/PagedLOD>
30 #include <osg/Texture2D>
31
32 #include <osgDB/FileNameUtils>
33 #include <osgDB/ReadFile>
34
35 #include "BucketBox.hxx"
36
37 namespace simgear {
38
39 ReaderWriterSPT::ReaderWriterSPT()
40 {
41     supportsExtension("spt", "SimGear paged terrain meta database.");
42 }
43
44 ReaderWriterSPT::~ReaderWriterSPT()
45 {
46 }
47
48 const char*
49 ReaderWriterSPT::className() const
50 {
51     return "simgear::ReaderWriterSPT";
52 }
53
54 osgDB::ReaderWriter::ReadResult
55 ReaderWriterSPT::readObject(const std::string& fileName, const osgDB::Options* options) const
56 {
57     if (fileName != "state.spt")
58         return ReadResult(osgDB::ReaderWriter::ReadResult::FILE_NOT_FOUND);
59
60     osg::StateSet* stateSet = new osg::StateSet;
61     stateSet->setAttributeAndModes(new osg::CullFace);
62
63     std::string imageFileName = options->getPluginStringData("SimGear::FG_WORLD_TEXTURE");
64     if (osg::Image* image = osgDB::readImageFile(imageFileName, options)) {
65         osg::Texture2D* texture = new osg::Texture2D;
66         texture->setImage(image);
67         texture->setWrap(osg::Texture2D::WRAP_S, osg::Texture2D::REPEAT);
68         texture->setWrap(osg::Texture2D::WRAP_T, osg::Texture2D::CLAMP);
69         stateSet->setTextureAttributeAndModes(0, texture);
70     }
71
72     return stateSet;
73 }
74
75 osgDB::ReaderWriter::ReadResult
76 ReaderWriterSPT::readNode(const std::string& fileName, const osgDB::Options* options) const
77 {
78     // The file name without path and without the spt extension
79     std::string strippedFileName = osgDB::getStrippedName(fileName);
80     if (strippedFileName == "earth")
81         return createTree(BucketBox(0, -90, 360, 180), options, true);
82
83     std::stringstream ss(strippedFileName);
84     BucketBox bucketBox;
85     ss >> bucketBox;
86     if (ss.fail())
87         return osgDB::ReaderWriter::ReadResult::FILE_NOT_FOUND;
88
89     BucketBox bucketBoxList[2];
90     unsigned bucketBoxListSize = bucketBox.periodicSplit(bucketBoxList);
91     if (bucketBoxListSize == 0)
92         return osgDB::ReaderWriter::ReadResult::FILE_NOT_FOUND;
93
94     if (bucketBoxListSize == 1)
95         return createTree(bucketBoxList[0], options, true);
96
97     assert(bucketBoxListSize == 2);
98     osg::ref_ptr<osg::Group> group = new osg::Group;
99     group->addChild(createTree(bucketBoxList[0], options, true));
100     group->addChild(createTree(bucketBoxList[1], options, true));
101     return group.release();
102 }
103
104 osg::Node*
105 ReaderWriterSPT::createTree(const BucketBox& bucketBox, const osgDB::Options* options, bool topLevel) const
106 {
107     if (bucketBox.getIsBucketSize()) {
108         return createPagedLOD(bucketBox, options);
109     } else if (!topLevel && bucketBox.getStartLevel() == 4) {
110         // We want an other level of indirection for paging
111         return createPagedLOD(bucketBox, options);
112     } else {
113         BucketBox bucketBoxList[100];
114         unsigned numTiles = bucketBox.getSubDivision(bucketBoxList, 100);
115         if (numTiles == 0)
116             return 0;
117
118         if (numTiles == 1)
119             return createTree(bucketBoxList[0], options, false);
120
121         osg::ref_ptr<osg::Group> group = new osg::Group;
122         for (unsigned i = 0; i < numTiles; ++i) {
123             osg::Node* node = createTree(bucketBoxList[i], options, false);
124             if (!node)
125                 continue;
126             group->addChild(node);
127         }
128         if (!group->getNumChildren())
129             return 0;
130
131         return group.release();
132     }
133 }
134
135 osg::Node*
136 ReaderWriterSPT::createPagedLOD(const BucketBox& bucketBox, const osgDB::Options* options) const
137 {
138     osg::PagedLOD* pagedLOD = new osg::PagedLOD;
139
140     pagedLOD->setCenterMode(osg::PagedLOD::USER_DEFINED_CENTER);
141     SGSpheref sphere = bucketBox.getBoundingSphere();
142     pagedLOD->setCenter(toOsg(sphere.getCenter()));
143     pagedLOD->setRadius(sphere.getRadius());
144         
145     osg::ref_ptr<osgDB::Options> localOptions;
146     localOptions = static_cast<osgDB::Options*>(options->clone(osg::CopyOp()));
147     pagedLOD->setDatabaseOptions(localOptions.get());
148         
149     float range;
150     if (bucketBox.getIsBucketSize())
151         range = 200e3;
152     else
153         range = 1e6;
154
155     // Add the static sea level textured shell
156     if (osg::Node* tile = createSeaLevelTile(bucketBox, options))
157         pagedLOD->addChild(tile, range, std::numeric_limits<float>::max());
158
159     // Add the paged file name that creates the subtrees on demand
160     if (bucketBox.getIsBucketSize()) {
161         std::string fileName;
162         fileName = bucketBox.getBucket().gen_index_str() + std::string(".stg");
163         pagedLOD->setFileName(pagedLOD->getNumChildren(), fileName);
164     } else {
165         std::stringstream ss;
166         ss << bucketBox << ".spt";
167         pagedLOD->setFileName(pagedLOD->getNumChildren(), ss.str());
168     }
169     pagedLOD->setRange(pagedLOD->getNumChildren(), 0.0, range);
170
171     return pagedLOD;
172 }
173
174 osg::Node*
175 ReaderWriterSPT::createSeaLevelTile(const BucketBox& bucketBox, const osgDB::Options* options) const
176 {
177     if (options->getPluginStringData("SimGear::FG_EARTH") != "ON")
178         return 0;
179
180     osg::Vec3Array* vertices = new osg::Vec3Array;
181     osg::Vec3Array* normals = new osg::Vec3Array;
182     osg::Vec2Array* texCoords = new osg::Vec2Array;
183         
184     unsigned widthLevel = bucketBox.getWidthLevel();
185     unsigned heightLevel = bucketBox.getHeightLevel();
186
187     unsigned incx = bucketBox.getWidthIncrement(widthLevel + 2);
188     incx = std::min(incx, bucketBox.getSize(0));
189     for (unsigned i = 0; incx != 0;) {
190         unsigned incy = bucketBox.getHeightIncrement(heightLevel + 2);
191         incy = std::min(incy, bucketBox.getSize(1));
192         for (unsigned j = 0; incy != 0;) {
193             SGVec3f v[6], n[6];
194             SGVec2f t[6];
195             unsigned num = bucketBox.getTileTriangles(i, j, incx, incy, v, n, t);
196             for (unsigned k = 0; k < num; ++k) {
197                 vertices->push_back(toOsg(v[k]));
198                 normals->push_back(toOsg(n[k]));
199                 texCoords->push_back(toOsg(t[k]));
200             }
201             j += incy;
202             incy = std::min(incy, bucketBox.getSize(1) - j);
203         }
204         i += incx;
205         incx = std::min(incx, bucketBox.getSize(0) - i);
206     }
207         
208     osg::Vec4Array* colors = new osg::Vec4Array;
209     colors->push_back(osg::Vec4(1, 1, 1, 1));
210         
211     osg::Geometry* geometry = new osg::Geometry;
212     geometry->setVertexArray(vertices);
213     geometry->setNormalArray(normals);
214     geometry->setNormalBinding(osg::Geometry::BIND_PER_VERTEX);
215     geometry->setColorArray(colors);
216     geometry->setColorBinding(osg::Geometry::BIND_OVERALL);
217     geometry->setTexCoordArray(0, texCoords);
218         
219     geometry->addPrimitiveSet(new osg::DrawArrays(osg::DrawArrays::TRIANGLES, 0, vertices->size()));
220         
221     osg::Geode* geode = new osg::Geode;
222     geode->addDrawable(geometry);
223     geode->setStateSet(getLowLODStateSet(options));
224
225     return geode;
226 }
227
228 osg::StateSet*
229 ReaderWriterSPT::getLowLODStateSet(const osgDB::Options* options) const
230 {
231     osg::ref_ptr<osgDB::Options> localOptions;
232     localOptions = static_cast<osgDB::Options*>(options->clone(osg::CopyOp()));
233     localOptions->setObjectCacheHint(osgDB::Options::CACHE_ALL);
234
235     osg::ref_ptr<osg::Object> object = osgDB::readObjectFile("state.spt", localOptions.get());
236     if (!dynamic_cast<osg::StateSet*>(object.get()))
237         return 0;
238
239     return static_cast<osg::StateSet*>(object.release());
240 }
241
242 } // namespace simgear
243