osgEarth 2.1.1
|
00001 /* -*-c++-*- */ 00002 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph 00003 * Copyright 2008-2010 Pelican Mapping 00004 * http://osgearth.org 00005 * 00006 * osgEarth is free software; you can redistribute it and/or modify 00007 * it under the terms of the GNU Lesser General Public License as published by 00008 * the Free Software Foundation; either version 2 of the License, or 00009 * (at your option) any later version. 00010 * 00011 * This program is distributed in the hope that it will be useful, 00012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00014 * GNU Lesser General Public License for more details. 00015 * 00016 * You should have received a copy of the GNU Lesser General Public License 00017 * along with this program. If not, see <http://www.gnu.org/licenses/> 00018 */ 00019 #include <osgEarthSymbology/MeshSubdivider> 00020 #include <osgEarthSymbology/LineFunctor> 00021 #include <osgEarth/GeoMath> 00022 #include <osg/TriangleFunctor> 00023 #include <osg/TriangleIndexFunctor> 00024 //#include <osgUtil/MeshOptimizers> 00025 #include <climits> 00026 #include <queue> 00027 #include <map> 00028 00029 #define LC "[MeshSubdivider] " 00030 00031 using namespace osgEarth; 00032 using namespace osgEarth::Symbology; 00033 00034 //------------------------------------------------------------------------ 00035 00036 namespace 00037 { 00038 // returns the geocentric bisection vector 00039 osg::Vec3d 00040 bisector( const osg::Vec3d& v0, const osg::Vec3d& v1 ) 00041 { 00042 osg::Vec3d f = (v0+v1)*0.5; 00043 f.normalize(); 00044 return f * 0.5*(v0.length() + v1.length()); 00045 } 00046 00047 // convert geocenric coords to spherical geodetic coords in radians. 00048 void 00049 geocentricToGeodetic( const osg::Vec3d& g, osg::Vec2d& out_geod ) 00050 { 00051 double r = g.length(); 00052 out_geod.set( atan2(g.y(),g.x()), acos(g.z()/r) ); 00053 } 00054 00055 // calculate the lat/long midpoint, taking care to use the shortest 00056 // global distance. 00057 void 00058 geodeticMidpoint( const osg::Vec2d& g0, const osg::Vec2d& g1, osg::Vec2d& out_mid ) 00059 { 00060 if ( fabs(g0.x()-g1.x()) < osg::PI ) 00061 out_mid.set( 0.5*(g0.x()+g1.x()), 0.5*(g0.y()+g1.y()) ); 00062 else if ( g1.x() > g0.x() ) 00063 out_mid.set( 0.5*((g0.x()+2*osg::PI)+g1.x()), 0.5*(g0.y()+g1.y()) ); 00064 else 00065 out_mid.set( 0.5*(g0.x()+(g1.x()+2*osg::PI)), 0.5*(g0.y()+g1.y()) ); 00066 00067 //GeoMath::midpoint(g0.y(), g0.x(), g1.y(), g1.x(), out_mid.y(), out_mid.x()); 00068 } 00069 00070 // finds the midpoint between two geocentric coordinates. We have to convert 00071 // back to geographic in order to get the correct interpolation. Spherical 00072 // conversion is good enough 00073 osg::Vec3d 00074 geocentricMidpoint( const osg::Vec3d& v0, const osg::Vec3d& v1, GeoInterpolation interp ) 00075 { 00076 if ( interp == GEOINTERP_GREAT_CIRCLE ) 00077 { 00078 return bisector(v0, v1); 00079 } 00080 else 00081 { 00082 // geocentric to spherical: 00083 osg::Vec2d g0, g1; 00084 geocentricToGeodetic(v0, g0); 00085 geocentricToGeodetic(v1, g1); 00086 00087 osg::Vec2d mid; 00088 geodeticMidpoint(g0, g1, mid); 00089 00090 double size = 0.5*(v0.length() + v1.length()); 00091 00092 // spherical to geocentric: 00093 double sin_lat = sin(mid.y()); 00094 return osg::Vec3d( cos(mid.x())*sin_lat, sin(mid.x())*sin_lat, cos(mid.y()) ) * size; 00095 } 00096 } 00097 00098 // the approximate surface-distance between two geocentric points (spherical) 00099 double 00100 geocentricSurfaceDistance( const osg::Vec3d& v0, const osg::Vec3d& v1 ) 00101 { 00102 osg::Vec2d g0, g1; 00103 geocentricToGeodetic(v0, g0); 00104 geocentricToGeodetic(v1, g1); 00105 return GeoMath::distance( v0.y(), v0.x(), v1.y(), v1.x() ); 00106 } 00107 00108 // the angle between two 3d vectors 00109 double 00110 angleBetween( const osg::Vec3d& v0, const osg::Vec3d& v1 ) 00111 { 00112 osg::Vec3d v0n = v0; v0n.normalize(); 00113 osg::Vec3d v1n = v1; v1n.normalize(); 00114 return fabs( acos( v0n * v1n ) ); 00115 } 00116 00117 //-------------------------------------------------------------------- 00118 00119 struct Triangle 00120 { 00121 Triangle() { } 00122 Triangle(GLuint i0, GLuint i1, GLuint i2) : 00123 _i0(i0), _i1(i1), _i2(i2){} 00124 GLuint _i0, _i1, _i2; 00125 }; 00126 00127 typedef std::queue<Triangle> TriangleQueue; 00128 typedef std::vector<Triangle> TriangleVector; 00129 00130 00131 struct TriangleData 00132 { 00133 typedef std::map<osg::Vec3,GLuint> VertMap; 00134 VertMap _vertMap; 00135 osg::Vec3Array* _sourceVerts; 00136 osg::Vec2Array* _sourceTexCoords; 00137 osg::ref_ptr<osg::Vec3Array> _verts; 00138 osg::ref_ptr<osg::Vec2Array> _texcoords; 00139 TriangleQueue _tris; 00140 00141 TriangleData() 00142 { 00143 _verts = new osg::Vec3Array(); 00144 _sourceVerts = 0; 00145 _sourceTexCoords = 0; 00146 } 00147 00148 void setSourceVerts(osg::Vec3Array* sourceVerts ) 00149 { 00150 _sourceVerts = sourceVerts; 00151 } 00152 00153 void setSourceTexCoords(osg::Vec2Array* sourceTexCoords) 00154 { 00155 _sourceTexCoords = sourceTexCoords; 00156 _texcoords = new osg::Vec2Array(); 00157 } 00158 00159 GLuint record( const osg::Vec3& v, const osg::Vec2f& t ) 00160 { 00161 VertMap::iterator i = _vertMap.find(v); 00162 if ( i == _vertMap.end() ) 00163 { 00164 GLuint index = _verts->size(); 00165 _verts->push_back(v); 00166 _vertMap[v] = index; 00167 //Only push back the texture coordinate if it's valid 00168 if (_texcoords) 00169 { 00170 _texcoords->push_back( t ); 00171 } 00172 return index; 00173 } 00174 else 00175 { 00176 return i->second; 00177 } 00178 } 00179 00180 00181 void operator() (unsigned int p1, unsigned int p2, unsigned int p3) 00182 { 00183 const osg::Vec3 v0 = (*_sourceVerts)[p1]; 00184 const osg::Vec3 v1 = (*_sourceVerts)[p2]; 00185 const osg::Vec3 v2 = (*_sourceVerts)[p3]; 00186 osg::Vec2 t0, t1, t2; 00187 if (_sourceTexCoords) 00188 { 00189 t0 = (*_sourceTexCoords)[p1]; 00190 t1 = (*_sourceTexCoords)[p2]; 00191 t2 = (*_sourceTexCoords)[p3]; 00192 } 00193 _tris.push( Triangle(record(v0, t0), record(v1, t1), record(v2, t2)) ); 00194 //OE_NOTICE << "Incoming verts " << p1 << ", " << p2 << ", " << p3 << std::endl; 00195 } 00196 }; 00197 00198 struct Edge 00199 { 00200 Edge() { } 00201 Edge( GLuint i0, GLuint i1 ) : _i0(i0), _i1(i1) { } 00202 GLuint _i0, _i1; 00203 bool operator < (const Edge& rhs) const { 00204 return 00205 _i0 < rhs._i0 ? true : 00206 _i0 > rhs._i0 ? false : 00207 _i1 < rhs._i1 ? true : 00208 _i1 > rhs._i1 ? false : 00209 false; 00210 } 00211 bool operator == (const Edge& rhs) const { return _i0==rhs._i0 && _i1==rhs._i1; } 00212 }; 00213 00214 typedef std::map<Edge,GLuint> EdgeMap; 00215 00219 template<typename ETYPE, typename VTYPE> 00220 void populateTriangles( osg::Geometry& geom, const TriangleVector& tris, unsigned int maxElementsPerEBO ) 00221 { 00222 unsigned int totalTris = tris.size(); 00223 unsigned int totalTrisWritten = 0; 00224 unsigned int numElementsInCurrentEBO = maxElementsPerEBO; 00225 00226 ETYPE* ebo = 0L; 00227 00228 for( TriangleVector::const_iterator i = tris.begin(); i != tris.end(); ++i ) 00229 { 00230 if ( numElementsInCurrentEBO+2 >= maxElementsPerEBO ) 00231 { 00232 if ( ebo ) 00233 { 00234 geom.addPrimitiveSet( ebo ); 00235 } 00236 00237 ebo = new ETYPE( GL_TRIANGLES ); 00238 00239 unsigned int trisRemaining = totalTris - totalTrisWritten; 00240 ebo->reserve( osg::minimum( trisRemaining*3, maxElementsPerEBO ) ); 00241 00242 numElementsInCurrentEBO = 0; 00243 } 00244 ebo->push_back( static_cast<VTYPE>( i->_i0 ) ); 00245 ebo->push_back( static_cast<VTYPE>( i->_i1 ) ); 00246 ebo->push_back( static_cast<VTYPE>( i->_i2 ) ); 00247 00248 numElementsInCurrentEBO += 3; 00249 ++totalTrisWritten; 00250 } 00251 00252 if ( ebo && ebo->size() > 0 ) 00253 { 00254 geom.addPrimitiveSet( ebo ); 00255 } 00256 } 00257 00258 //---------------------------------------------------------------------- 00259 00260 struct Line 00261 { 00262 Line() { } 00263 Line(GLuint i0, GLuint i1) : _i0(i0), _i1(i1) { } 00264 GLuint _i0, _i1; 00265 }; 00266 00267 typedef std::queue<Line> LineQueue; 00268 typedef std::vector<Line> LineVector; 00269 00270 struct LineData 00271 { 00272 typedef std::map<osg::Vec3,GLuint> VertMap; 00273 VertMap _vertMap; 00274 osg::Vec3Array* _verts; 00275 LineQueue _lines; 00276 00277 LineData() 00278 { 00279 _verts = new osg::Vec3Array(); 00280 } 00281 00282 GLuint record( const osg::Vec3& v ) 00283 { 00284 VertMap::iterator i = _vertMap.find(v); 00285 if ( i == _vertMap.end() ) 00286 { 00287 GLuint index = _verts->size(); 00288 _verts->push_back(v); 00289 _vertMap[v] = index; 00290 return index; 00291 } 00292 else 00293 { 00294 return i->second; 00295 } 00296 } 00297 00298 void operator()( const osg::Vec3& v0, const osg::Vec3& v1, bool temp ) 00299 { 00300 _lines.push( Line( record(v0), record(v1) ) ); 00301 } 00302 }; 00303 00307 template<typename ETYPE, typename VTYPE> 00308 void populateLines( osg::Geometry& geom, const LineVector& lines, unsigned int maxElementsPerEBO ) 00309 { 00310 unsigned int totalLines = lines.size(); 00311 unsigned int totalLinesWritten = 0; 00312 unsigned int numElementsInCurrentEBO = maxElementsPerEBO; 00313 00314 ETYPE* ebo = 0L; 00315 00316 for( LineVector::const_iterator i = lines.begin(); i != lines.end(); ++i ) 00317 { 00318 if ( numElementsInCurrentEBO+2 >= maxElementsPerEBO ) 00319 { 00320 if ( ebo ) 00321 { 00322 geom.addPrimitiveSet( ebo ); 00323 } 00324 00325 ebo = new ETYPE( GL_LINES ); 00326 00327 unsigned int linesRemaining = totalLines - totalLinesWritten; 00328 ebo->reserve( osg::minimum( linesRemaining*2, maxElementsPerEBO ) ); 00329 00330 numElementsInCurrentEBO = 0; 00331 } 00332 ebo->push_back( static_cast<VTYPE>( i->_i0 ) ); 00333 ebo->push_back( static_cast<VTYPE>( i->_i1 ) ); 00334 00335 numElementsInCurrentEBO += 3; 00336 ++totalLinesWritten; 00337 } 00338 00339 if ( ebo && ebo->size() > 0 ) 00340 { 00341 geom.addPrimitiveSet( ebo ); 00342 } 00343 } 00344 00345 static const osg::Vec3d s_pole(0,0,1); 00346 static const double s_maxLatAdjustment(0.75); 00347 00353 void subdivideLines( 00354 double granularity, 00355 GeoInterpolation interp, 00356 osg::Geometry& geom, 00357 const osg::Matrixd& W2L, // world=>local xform 00358 const osg::Matrixd& L2W, // local=>world xform 00359 unsigned int maxElementsPerEBO ) 00360 { 00361 // collect all the line segments in the geometry. 00362 LineFunctor<LineData> data; 00363 geom.accept( data ); 00364 00365 int numLinesIn = data._lines.size(); 00366 00367 LineVector done; 00368 done.reserve( 2 * data._lines.size() ); 00369 00370 // Subdivide lines until we run out. 00371 while( data._lines.size() > 0 ) 00372 { 00373 Line line = data._lines.front(); 00374 data._lines.pop(); 00375 00376 osg::Vec3d v0_w = (*data._verts)[line._i0] * L2W; 00377 osg::Vec3d v1_w = (*data._verts)[line._i1] * L2W; 00378 00379 double g0 = angleBetween(v0_w, v1_w); 00380 00381 if ( g0 > granularity ) 00382 { 00383 data._verts->push_back( geocentricMidpoint(v0_w, v1_w, interp) * W2L ); 00384 GLuint i = data._verts->size()-1; 00385 00386 data._lines.push( Line( line._i0, i ) ); 00387 data._lines.push( Line( i, line._i1 ) ); 00388 } 00389 else 00390 { 00391 // line is small enough- put it on the "done" list. 00392 done.push_back( line ); 00393 } 00394 } 00395 00396 if ( done.size() > 0 ) 00397 { 00398 while( geom.getNumPrimitiveSets() > 0 ) 00399 geom.removePrimitiveSet(0); 00400 00401 // set the new VBO. 00402 geom.setVertexArray( data._verts ); 00403 00404 if ( data._verts->size() < 256 ) 00405 populateLines<osg::DrawElementsUByte,GLubyte>( geom, done, maxElementsPerEBO ); 00406 else if ( data._verts->size() < 65536 ) 00407 populateLines<osg::DrawElementsUShort,GLushort>( geom, done, maxElementsPerEBO ); 00408 else 00409 populateLines<osg::DrawElementsUInt,GLuint>( geom, done, maxElementsPerEBO ); 00410 } 00411 } 00412 00413 00414 //---------------------------------------------------------------------- 00415 00425 void subdivideTriangles( 00426 double granularity, 00427 GeoInterpolation interp, 00428 osg::Geometry& geom, 00429 const osg::Matrixd& W2L, // world=>local xform 00430 const osg::Matrixd& L2W, // local=>world xform 00431 unsigned int maxElementsPerEBO ) 00432 { 00433 00434 // collect all the triangled in the geometry. 00435 osg::TriangleIndexFunctor<TriangleData> data;; 00436 data.setSourceVerts(dynamic_cast<osg::Vec3Array*>(geom.getVertexArray())); 00437 data.setSourceTexCoords(dynamic_cast<osg::Vec2Array*>(geom.getTexCoordArray(0))); 00438 geom.accept( data ); 00439 00440 00441 int numTrisIn = data._tris.size(); 00442 00443 TriangleVector done; 00444 done.reserve(2.0 * data._tris.size()); 00445 00446 // Used to make sure shared edges are not split more than once. 00447 EdgeMap edges; 00448 00449 // Subdivide triangles until we run out 00450 while( data._tris.size() > 0 ) 00451 { 00452 Triangle tri = data._tris.front(); 00453 data._tris.pop(); 00454 00455 osg::Vec3d v0_w = (*data._verts)[tri._i0] * L2W; 00456 osg::Vec3d v1_w = (*data._verts)[tri._i1] * L2W; 00457 osg::Vec3d v2_w = (*data._verts)[tri._i2] * L2W; 00458 00459 osg::Vec2 t0 = (*data._texcoords)[tri._i0]; 00460 osg::Vec2 t1 = (*data._texcoords)[tri._i1]; 00461 osg::Vec2 t2 = (*data._texcoords)[tri._i2]; 00462 00463 double g0 = angleBetween(v0_w, v1_w); 00464 double g1 = angleBetween(v1_w, v2_w); 00465 double g2 = angleBetween(v2_w, v0_w); 00466 double max = osg::maximum( g0, osg::maximum(g1, g2) ); 00467 00468 if ( max > granularity ) 00469 { 00470 if ( g0 == max ) 00471 { 00472 Edge edge( osg::minimum(tri._i0, tri._i1), osg::maximum(tri._i0, tri._i1) ); 00473 00474 EdgeMap::iterator ei = edges.find(edge); 00475 GLuint i; 00476 if ( ei == edges.end() ) 00477 { 00478 data._verts->push_back( geocentricMidpoint(v0_w, v1_w, interp) * W2L ); 00479 data._texcoords->push_back( (t0 + t1) / 2.0f ); 00480 i = data._verts->size() - 1; 00481 edges[edge] = i; 00482 } 00483 else 00484 { 00485 i = ei->second; 00486 } 00487 00488 data._tris.push( Triangle(tri._i0, i, tri._i2) ); 00489 data._tris.push( Triangle(i, tri._i1, tri._i2) ); 00490 } 00491 else if ( g1 == max ) 00492 { 00493 Edge edge( osg::minimum(tri._i1, tri._i2), osg::maximum(tri._i1,tri._i2) ); 00494 00495 EdgeMap::iterator ei = edges.find(edge); 00496 GLuint i; 00497 if ( ei == edges.end() ) 00498 { 00499 data._verts->push_back( geocentricMidpoint(v1_w, v2_w, interp) * W2L ); 00500 data._texcoords->push_back( (t1 + t2) / 2.0f ); 00501 i = data._verts->size() - 1; 00502 edges[edge] = i; 00503 } 00504 else 00505 { 00506 i = ei->second; 00507 } 00508 00509 data._tris.push( Triangle(tri._i1, i, tri._i0) ); 00510 data._tris.push( Triangle(i, tri._i2, tri._i0) ); 00511 } 00512 else if ( g2 == max ) 00513 { 00514 Edge edge( osg::minimum(tri._i2, tri._i0), osg::maximum(tri._i2,tri._i0) ); 00515 00516 EdgeMap::iterator ei = edges.find(edge); 00517 GLuint i; 00518 if ( ei == edges.end() ) 00519 { 00520 data._verts->push_back( geocentricMidpoint(v2_w, v0_w, interp) * W2L ); 00521 data._texcoords->push_back( (t2 + t0) / 2.0f ); 00522 i = data._verts->size() - 1; 00523 edges[edge] = i; 00524 } 00525 else 00526 { 00527 i = ei->second; 00528 } 00529 00530 data._tris.push( Triangle(tri._i2, i, tri._i1) ); 00531 data._tris.push( Triangle(i, tri._i0, tri._i1) ); 00532 } 00533 } 00534 else 00535 { 00536 // triangle is small enough- put it on the "done" list. 00537 done.push_back(tri); 00538 } 00539 } 00540 00541 if ( done.size() > 0 ) 00542 { 00543 // first, remove the old primitive sets. 00544 while( geom.getNumPrimitiveSets() > 0 ) 00545 geom.removePrimitiveSet( 0 ); 00546 00547 // set the new VBO. 00548 geom.setVertexArray( data._verts.get() ); 00549 00550 geom.setTexCoordArray(0, data._texcoords.get() ); 00551 00552 if ( data._verts->size() < 256 ) 00553 populateTriangles<osg::DrawElementsUByte,GLubyte>( geom, done, maxElementsPerEBO ); 00554 else if ( data._verts->size() < 65536 ) 00555 populateTriangles<osg::DrawElementsUShort,GLushort>( geom, done, maxElementsPerEBO ); 00556 else 00557 populateTriangles<osg::DrawElementsUInt,GLuint>( geom, done, maxElementsPerEBO ); 00558 } 00559 } 00560 00561 void subdivide( 00562 double granularity, 00563 GeoInterpolation interp, 00564 osg::Geometry& geom, 00565 const osg::Matrixd& W2L, // world=>local xform 00566 const osg::Matrixd& L2W, // local=>world xform 00567 unsigned int maxElementsPerEBO ) 00568 { 00569 GLenum mode = geom.getPrimitiveSet(0)->getMode(); 00570 00571 if ( mode == GL_POINTS ) 00572 return; 00573 00574 if ( mode == GL_LINES || mode == GL_LINE_STRIP || mode == GL_LINE_LOOP ) 00575 { 00576 subdivideLines( granularity, interp, geom, W2L, L2W, maxElementsPerEBO ); 00577 } 00578 else 00579 { 00580 subdivideTriangles( granularity, interp, geom, W2L, L2W, maxElementsPerEBO ); 00581 00582 //osgUtil::VertexCacheVisitor cacheOptimizer; 00583 //cacheOptimizer.optimizeVertices( geom ); 00584 } 00585 00586 //osgUtil::VertexAccessOrderVisitor orderOptimizer; 00587 //orderOptimizer.optimizeOrder( geom ); 00588 } 00589 } 00590 00591 //------------------------------------------------------------------------ 00592 00593 MeshSubdivider::MeshSubdivider(const osg::Matrixd& world2local, 00594 const osg::Matrixd& local2world ) : 00595 _local2world(local2world), 00596 _world2local(world2local), 00597 _maxElementsPerEBO( INT_MAX ) 00598 { 00599 if ( !_world2local.isIdentity() && _local2world.isIdentity() ) 00600 _local2world = osg::Matrixd::inverse(_world2local); 00601 else if ( _world2local.isIdentity() && !_local2world.isIdentity() ) 00602 _world2local = osg::Matrixd::inverse(_local2world); 00603 } 00604 00605 void 00606 MeshSubdivider::run(osg::Geometry& geom, double granularity, GeoInterpolation interp) 00607 { 00608 if ( geom.getNumPrimitiveSets() < 1 ) 00609 return; 00610 00611 subdivide( granularity, interp, geom, _world2local, _local2world, _maxElementsPerEBO ); 00612 }