osgEarth 2.1.1

/home/cube/sources/osgearth/src/osgEarthSymbology/MeshSubdivider.cpp

Go to the documentation of this file.
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 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines