1 // ReaderWriterSPT.cxx -- Provide a paged database for flightgear scenery.
3 // Copyright (C) 2010 - 2011 Mathias Froehlich
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.
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.
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.
21 # include <simgear_config.h>
24 #include "ReaderWriterSPT.hxx"
28 #include <osg/CullFace>
29 #include <osg/PagedLOD>
30 #include <osg/Texture2D>
32 #include <osgDB/FileNameUtils>
33 #include <osgDB/ReadFile>
35 #include "BucketBox.hxx"
39 ReaderWriterSPT::ReaderWriterSPT()
41 supportsExtension("spt", "SimGear paged terrain meta database.");
44 ReaderWriterSPT::~ReaderWriterSPT()
49 ReaderWriterSPT::className() const
51 return "simgear::ReaderWriterSPT";
54 osgDB::ReaderWriter::ReadResult
55 ReaderWriterSPT::readObject(const std::string& fileName, const osgDB::Options* options) const
57 if (fileName != "state.spt")
58 return ReadResult(osgDB::ReaderWriter::ReadResult::FILE_NOT_FOUND);
60 osg::StateSet* stateSet = new osg::StateSet;
61 stateSet->setAttributeAndModes(new osg::CullFace);
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);
75 osgDB::ReaderWriter::ReadResult
76 ReaderWriterSPT::readNode(const std::string& fileName, const osgDB::Options* options) const
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);
83 std::stringstream ss(strippedFileName);
87 return osgDB::ReaderWriter::ReadResult::FILE_NOT_FOUND;
89 BucketBox bucketBoxList[2];
90 unsigned bucketBoxListSize = bucketBox.periodicSplit(bucketBoxList);
91 if (bucketBoxListSize == 0)
92 return osgDB::ReaderWriter::ReadResult::FILE_NOT_FOUND;
94 if (bucketBoxListSize == 1)
95 return createTree(bucketBoxList[0], options, true);
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();
105 ReaderWriterSPT::createTree(const BucketBox& bucketBox, const osgDB::Options* options, bool topLevel) const
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);
113 BucketBox bucketBoxList[100];
114 unsigned numTiles = bucketBox.getSubDivision(bucketBoxList, 100);
119 return createTree(bucketBoxList[0], options, false);
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);
126 group->addChild(node);
128 if (!group->getNumChildren())
131 return group.release();
136 ReaderWriterSPT::createPagedLOD(const BucketBox& bucketBox, const osgDB::Options* options) const
138 osg::PagedLOD* pagedLOD = new osg::PagedLOD;
140 pagedLOD->setCenterMode(osg::PagedLOD::USER_DEFINED_CENTER);
141 SGSpheref sphere = bucketBox.getBoundingSphere();
142 pagedLOD->setCenter(toOsg(sphere.getCenter()));
143 pagedLOD->setRadius(sphere.getRadius());
145 osg::ref_ptr<osgDB::Options> localOptions;
146 localOptions = static_cast<osgDB::Options*>(options->clone(osg::CopyOp()));
147 pagedLOD->setDatabaseOptions(localOptions.get());
150 if (bucketBox.getIsBucketSize())
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());
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);
165 std::stringstream ss;
166 ss << bucketBox << ".spt";
167 pagedLOD->setFileName(pagedLOD->getNumChildren(), ss.str());
169 pagedLOD->setRange(pagedLOD->getNumChildren(), 0.0, range);
175 ReaderWriterSPT::createSeaLevelTile(const BucketBox& bucketBox, const osgDB::Options* options) const
177 if (options->getPluginStringData("SimGear::FG_EARTH") != "ON")
180 osg::Vec3Array* vertices = new osg::Vec3Array;
181 osg::Vec3Array* normals = new osg::Vec3Array;
182 osg::Vec2Array* texCoords = new osg::Vec2Array;
184 unsigned widthLevel = bucketBox.getWidthLevel();
185 unsigned heightLevel = bucketBox.getHeightLevel();
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;) {
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]));
202 incy = std::min(incy, bucketBox.getSize(1) - j);
205 incx = std::min(incx, bucketBox.getSize(0) - i);
208 osg::Vec4Array* colors = new osg::Vec4Array;
209 colors->push_back(osg::Vec4(1, 1, 1, 1));
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);
219 geometry->addPrimitiveSet(new osg::DrawArrays(osg::DrawArrays::TRIANGLES, 0, vertices->size()));
221 osg::Geode* geode = new osg::Geode;
222 geode->addDrawable(geometry);
223 geode->setStateSet(getLowLODStateSet(options));
229 ReaderWriterSPT::getLowLODStateSet(const osgDB::Options* options) const
231 osg::ref_ptr<osgDB::Options> localOptions;
232 localOptions = static_cast<osgDB::Options*>(options->clone(osg::CopyOp()));
233 localOptions->setObjectCacheHint(osgDB::Options::CACHE_ALL);
235 osg::ref_ptr<osg::Object> object = osgDB::readObjectFile("state.spt", localOptions.get());
236 if (!dynamic_cast<osg::StateSet*>(object.get()))
239 return static_cast<osg::StateSet*>(object.release());
242 } // namespace simgear