osgEarth 2.1.1
|
00001 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph 00002 * Copyright 2010 Pelican Ventures, Inc. 00003 * http://osgearth.org 00004 * 00005 * osgEarth is free software; you can redistribute it and/or modify 00006 * it under the terms of the GNU Lesser General Public License as published by 00007 * the Free Software Foundation; either version 2 of the License, or 00008 * (at your option) any later version. 00009 * 00010 * This program is distributed in the hope that it will be useful, 00011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00013 * GNU Lesser General Public License for more details. 00014 * 00015 * You should have received a copy of the GNU Lesser General Public License 00016 * along with this program. If not, see <http://www.gnu.org/licenses/> 00017 */ 00018 00019 #include <algorithm> 00020 00021 #include "PatchSet" 00022 00023 #include <osg/Math> 00024 #include <osg/MatrixTransform> 00025 00026 #include "Patch" 00027 #include "PatchGroup" 00028 00029 namespace seamless 00030 { 00031 using namespace osg; 00032 using namespace osgEarth; 00033 00034 PatchSet::PatchSet() 00035 : _maxLevel(16), _patchOptionsPrototype(new PatchOptions), _mapf(0), 00036 _resolution(128), _verticalScale(1.0f) 00037 { 00038 setPrecisionFactor(4); 00039 initPrimitiveSets(); 00040 00041 } 00042 00043 PatchSet::PatchSet(const Drivers::SeamlessOptions& options, 00044 PatchOptions* poptionsPrototype) 00045 : _maxLevel(16), 00046 _patchOptionsPrototype(poptionsPrototype ? poptionsPrototype 00047 : new PatchOptions), 00048 _mapf(0), _options(options) 00049 { 00050 _resolution = options.resolution().value(); 00051 _verticalScale = options.verticalScale().value(); 00052 setPrecisionFactor(4); 00053 initPrimitiveSets(); 00054 } 00055 00056 PatchSet::PatchSet(const PatchSet& rhs, const CopyOp& copyop) 00057 : _precisionFactor(rhs._precisionFactor), _resolution(rhs._resolution), 00058 _maxLevel(rhs._maxLevel), _verticalScale(rhs._verticalScale), 00059 _patchOptionsPrototype(static_cast<PatchOptions*>(copyop(rhs._patchOptionsPrototype.get()))), 00060 _map(static_cast<Map*>(copyop(rhs._map.get()))) 00061 { 00062 _patchOptionsPrototype 00063 = static_cast<PatchOptions*>(copyop(_patchOptionsPrototype.get())); 00064 for (int j = 0; j < 2; ++j) 00065 for (int i = 0; j < 4; ++i) 00066 trilePset[j][i] 00067 = static_cast<DrawElementsUShort*>(copyop(rhs.trilePset[j][i].get())); 00068 for (int j = 0; j < 4; ++j) 00069 for (int i = 0; j < 4; ++i) 00070 stripPset[j][i] 00071 = static_cast<DrawElementsUShort*>(copyop(rhs.stripPset[j][i].get())); 00072 if (rhs._mapf) 00073 _mapf = new MapFrame(*_mapf); 00074 } 00075 00076 PatchSet::~PatchSet() 00077 { 00078 delete _mapf; 00079 } 00080 00081 void PatchSet::setMap(const Map* map) 00082 { 00083 _map = map; 00084 if (map) 00085 { 00086 delete _mapf; 00087 _mapf = new MapFrame(map, Map::TERRAIN_LAYERS, "seamless"); 00088 } 00089 } 00090 00091 Node* PatchSet::createPatchGroup(const std::string& filename, 00092 PatchOptions* poptions) 00093 { 00094 PatchGroup* pgroup = new PatchGroup; 00095 pgroup->setOptions(poptions); 00096 Transform* patch = createPatch(filename, poptions); 00097 BoundingSphere bsphere = patch->getBound(); 00098 pgroup->setCenter(bsphere.center()); 00099 if (poptions->getPatchLevel() >= _maxLevel) 00100 { 00101 pgroup->addChild(patch, 0.0, 1e10); 00102 } 00103 else 00104 { 00105 pgroup->addChild(patch, 0.0, 1.0); 00106 pgroup->setRange(1, 1.0, 1e10); 00107 pgroup->setFileName(1, "foo.osgearth_engine_seamless_patch"); 00108 } 00109 return pgroup; 00110 } 00111 00112 // Default implementation that creates a flat 81920m x 81920m plane. 00113 00114 Transform* PatchSet::createPatch(const std::string& filename, PatchOptions* poptions) 00115 { 00116 Patch* patch = new Patch; 00117 patch->setPatchSet(this); 00118 Vec2d ll, ur; 00119 poptions->getPatchExtents(ll, ur); 00120 Vec2d range = (ur - ll); 00121 ref_ptr<Patch::Data> data = new Patch::Data; 00122 int patchDim = _resolution + 1; 00123 Vec3Array* verts = new Vec3Array(patchDim * patchDim); 00124 for (int j = 0; j < patchDim; ++j) 00125 for (int i = 0; i < patchDim; ++i) 00126 (*verts)[patchDim * j + i] 00127 = Vec3((ll.x() + i * range.x() 00128 / static_cast<float>(_resolution)) * 81920.0, 00129 (ll.y() + j * range.y() 00130 / static_cast<float>(_resolution)) * 81920.0, 00131 0.0); 00132 data->vertexData.array = verts; 00133 data->vertexData.binding = Geometry::BIND_PER_VERTEX; 00134 Vec3Array* norms = new Vec3Array(1); 00135 (*norms)[0] = Vec3d(0.0, 0.0, 1.0); 00136 data->normalData.array = norms; 00137 data->normalData.binding = Geometry::BIND_OVERALL; 00138 Vec4Array* colors = new Vec4Array(1); 00139 (*colors)[0] = Vec4(1.0, 1.0, 1.0, 1.0); 00140 data->colorData.array = colors; 00141 data->colorData.binding = Geometry::BIND_OVERALL; 00142 patch->setData(data); 00143 MatrixTransform* transform = new MatrixTransform; 00144 transform->addChild(patch); 00145 return transform; 00146 } 00147 00148 Node* PatchSet::createPatchSetGraph(const std::string& filename) 00149 { 00150 PatchOptions* poptions = osg::clone(_patchOptionsPrototype.get()); 00151 poptions->setPatchSet(this); 00152 return createPatchGroup(filename, poptions); 00153 } 00154 00155 double PatchSet::calcPrecisionFactor(int pixelError, double horiz_fov_deg, 00156 int screenRes, int dpi) 00157 { 00158 // Find near plane distance in meters 00159 const double pixelsPerMeter = dpi / .0254; 00160 const double halfScreen = .5 * screenRes / pixelsPerMeter; 00161 const double dNear = halfScreen / tan(DegreesToRadians(horiz_fov_deg / 2)); 00162 return dNear * pixelsPerMeter / (pixelError * _resolution); 00163 } 00164 00165 namespace 00166 { 00167 inline void getGridCoords(int psRes, unsigned short index, int& x, int& y) 00168 { 00169 y = index / (psRes + 1); 00170 x = index % (psRes + 1); 00171 } 00172 } 00173 00174 ref_ptr<DrawElementsUShort> PatchSet::makeBasicTrile(int delta) 00175 { 00176 ref_ptr<DrawElementsUShort> pset = new DrawElementsUShort(GL_TRIANGLES); 00177 // y axis 00178 int xBegin = delta; 00179 int xEnd = _resolution - delta; 00180 for (int j = 0; j < _resolution / 2 - delta; j += delta) 00181 { 00182 for (int i = xBegin; i < xEnd; i += 2 * delta) 00183 { 00184 pset->push_back(makeIndex(i, j)); 00185 pset->push_back(makeIndex(i + delta, j)); 00186 pset->push_back(makeIndex(i + delta, j + delta)); 00187 pset->push_back(makeIndex(i + delta, j + delta)); 00188 pset->push_back(makeIndex(i + delta, j)); 00189 pset->push_back(makeIndex(i + 2 * delta, j)); 00190 if (i + 2 * delta == xEnd) 00191 break; 00192 pset->push_back(makeIndex(i + delta, j + delta)); 00193 pset->push_back(makeIndex(i + 2 * delta, j)); 00194 pset->push_back(makeIndex(i + 2 * delta, j + delta)); 00195 pset->push_back(makeIndex(i + 2 * delta, j + delta)); 00196 pset->push_back(makeIndex(i + 2 * delta, j)); 00197 pset->push_back(makeIndex(i + 3 * delta, j + delta)); 00198 } 00199 xBegin += delta; 00200 xEnd -= delta; 00201 } 00202 return pset; 00203 } 00204 00205 // Rotate grid indices 90 deg counter-clockwise. 00206 unsigned short PatchSet::rotateIndex(unsigned short index) 00207 { 00208 int x, y; 00209 int psRes = _resolution; 00210 getGridCoords(psRes, index, x, y); 00211 x -= psRes / 2; 00212 y -= psRes / 2; 00213 int newx = -y; 00214 int newy = x; 00215 newx += psRes / 2; 00216 newy += psRes / 2; 00217 return makeIndex(newx, newy); 00218 } 00219 00220 // Stitching strip between two triles of the same resolution. 00221 00222 ref_ptr<DrawElementsUShort> PatchSet::makeSingleStrip(int delta) 00223 { 00224 ref_ptr<DrawElementsUShort> pset = new DrawElementsUShort(GL_TRIANGLES); 00225 for (int i = 0; i < _resolution / 2; i += delta) 00226 { 00227 if (i > 0) 00228 { 00229 pset->push_back(makeIndex(i - delta, i)); 00230 pset->push_back(makeIndex(i, i)); 00231 pset->push_back(makeIndex(i, i + delta)); 00232 } 00233 pset->push_back(makeIndex(i, i)); 00234 pset->push_back(makeIndex(i + delta, i + delta)); 00235 pset->push_back(makeIndex(i, i + delta)); 00236 pset->push_back(makeIndex(i, i)); 00237 pset->push_back(makeIndex(i + delta, i)); 00238 pset->push_back(makeIndex(i + delta, i + delta)); 00239 if (i < _resolution / 2 - delta) 00240 { 00241 pset->push_back(makeIndex(i + delta, i + delta)); 00242 pset->push_back(makeIndex(i + delta, i)); 00243 pset->push_back(makeIndex(i + 2 * delta, i + delta)); 00244 } 00245 } 00246 return pset; 00247 } 00248 00249 // Stitching strip between triles of different resolutions. 00250 00251 ref_ptr<DrawElementsUShort> PatchSet::makeDualStrip() 00252 { 00253 ref_ptr<DrawElementsUShort> pset = new DrawElementsUShort(GL_TRIANGLES); 00254 for (int i = 0, j = 2; j <= _resolution / 2; i += 2, j += 2) 00255 { 00256 pset->push_back(makeIndex(i, j)); 00257 if (i == 0) 00258 pset->push_back(makeIndex(0, 0)); 00259 else 00260 pset->push_back(makeIndex(i - 2, j -2)); 00261 pset->push_back(makeIndex(i + 1, j - 2)); 00262 pset->push_back(makeIndex(i, j)); 00263 pset->push_back(makeIndex(i + 1, j - 2)); 00264 pset->push_back(makeIndex(i + 2, j - 1)); 00265 00266 pset->push_back(makeIndex(i, j)); 00267 pset->push_back(makeIndex(i + 2, j - 1)); 00268 pset->push_back(makeIndex(i + 3, j)); 00269 } 00270 return pset; 00271 } 00272 00273 void PatchSet::initPrimitiveSets() 00274 { 00275 for (int res = 0; res < 2; res++) 00276 { 00277 // Bottom trile 00278 trilePset[res][0] = makeBasicTrile(2 - res); 00279 // The other triles are rotations of the first on the grid of 00280 // coordinate indices. 00281 for (int i = 1; i < 4; ++i) 00282 { 00283 trilePset[res][i] = new DrawElementsUShort(GL_TRIANGLES); 00284 for (DrawElementsUShort::iterator itr = trilePset[res][i - 1]->begin(), 00285 end = trilePset[res][i - 1]->end(); 00286 itr != end; 00287 ++itr) 00288 trilePset[res][i]->push_back(rotateIndex(*itr)); 00289 } 00290 } 00291 // First, the strips for the edge from lower left to middle 00292 stripPset[0][0] = makeSingleStrip(2); // low res 00293 stripPset[1][0] = makeDualStrip(); 00294 // The other dual strip is the reflection across y = x 00295 stripPset[2][0] = new DrawElementsUShort(GL_TRIANGLES); 00296 for (DrawElementsUShort::iterator itr = stripPset[1][0]->begin(), 00297 end = stripPset[1][0]->end(); 00298 itr != end; 00299 ++itr) 00300 { 00301 int x, y; 00302 getGridCoords(_resolution, *itr, x, y); 00303 stripPset[2][0]->push_back(makeIndex(y, x)); 00304 } 00305 // Now switch the order on the triangles on the reflected strip 00306 for (size_t i = 1; i < stripPset[2][0]->size(); i += 3) 00307 { 00308 std::swap((*stripPset[2][0].get())[i], 00309 (*stripPset[2][0].get())[i + 1]); 00310 } 00311 00312 stripPset[3][0] = makeSingleStrip(1); // hi res 00313 // Now rotate the strips for the other diagonals. 00314 for (int j = 1; j < 4; ++j) 00315 { 00316 for (int i = 0; i < 4; ++i) 00317 { 00318 stripPset[i][j] = new DrawElementsUShort(GL_TRIANGLES); 00319 for (DrawElementsUShort::iterator itr = stripPset[i][j - 1]->begin(), 00320 end = stripPset[i][j - 1]->end(); 00321 itr != end; 00322 ++itr) 00323 stripPset[i][j]->push_back(rotateIndex(*itr)); 00324 } 00325 } 00326 } 00327 00328 osg::Node* PatchSet::createChild(const PatchOptions* parentOptions, int childNum) 00329 { 00330 Vec2d lowerLeft(0.0, 1.0); 00331 Vec2d upperRight(1.0, 1.0); 00332 00333 parentOptions->getPatchExtents(lowerLeft, upperRight); 00334 Vec2d range = upperRight - lowerLeft; 00335 Vec2d newRange = range * .5; 00336 double x = (childNum % 2) * .5; 00337 double y = (childNum / 2) * .5; 00338 PatchOptions* pgroupOptions = osg::clone(parentOptions); 00339 Vec2d ll = lowerLeft + componentMultiply(Vec2d(x, y), range); 00340 pgroupOptions->setPatchExtents(ll, ll + newRange); 00341 pgroupOptions->setPatchLevel(parentOptions->getPatchLevel() + 1); 00342 Node* pgroup = createPatchGroup("foobies.osgearth_engine_seamless_patch", 00343 pgroupOptions); 00344 return pgroup; 00345 } 00346 }