~ubuntu-branches/debian/sid/osgearth/sid

« back to all changes in this revision

Viewing changes to src/osgEarthSymbology/MeshSubdivider.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Pirmin Kalberer
  • Date: 2011-07-14 22:13:36 UTC
  • Revision ID: james.westby@ubuntu.com-20110714221336-94igk9rskxveh794
Tags: upstream-2.0+dfsg
ImportĀ upstreamĀ versionĀ 2.0+dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* -*-c++-*- */
 
2
/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
 
3
 * Copyright 2008-2010 Pelican Mapping
 
4
 * http://osgearth.org
 
5
 *
 
6
 * osgEarth is free software; you can redistribute it and/or modify
 
7
 * it under the terms of the GNU Lesser General Public License as published by
 
8
 * the Free Software Foundation; either version 2 of the License, or
 
9
 * (at your option) any later version.
 
10
 *
 
11
 * This program is distributed in the hope that it will be useful,
 
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
14
 * GNU Lesser General Public License for more details.
 
15
 *
 
16
 * You should have received a copy of the GNU Lesser General Public License
 
17
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
 
18
 */
 
19
#include <osgEarthSymbology/MeshSubdivider>
 
20
#include <osgEarthSymbology/LineFunctor>
 
21
#include <osgEarth/GeoMath>
 
22
#include <osg/TriangleFunctor>
 
23
//#include <osgUtil/MeshOptimizers>
 
24
#include <climits>
 
25
#include <queue>
 
26
#include <map>
 
27
 
 
28
#define LC "[MeshSubdivider] "
 
29
 
 
30
using namespace osgEarth;
 
31
using namespace osgEarth::Symbology;
 
32
 
 
33
//------------------------------------------------------------------------
 
34
 
 
35
namespace
 
36
{
 
37
    // convert geocenric coords to spherical geodetic coords in radians.
 
38
    void
 
39
    geocentricToGeodetic( const osg::Vec3d& g, osg::Vec2d& out_geod )
 
40
    {
 
41
        double r = g.length();
 
42
        out_geod.set( atan2(g.y(),g.x()), acos(g.z()/r) );
 
43
    }
 
44
 
 
45
    // calculate the lat/long midpoint, taking care to use the shortest
 
46
    // global distance.
 
47
    void
 
48
    geodeticMidpoint( const osg::Vec2d& g0, const osg::Vec2d& g1, osg::Vec2d& out_mid )
 
49
    {
 
50
        if ( fabs(g0.x()-g1.x()) < osg::PI )
 
51
            out_mid.set( 0.5*(g0.x()+g1.x()), 0.5*(g0.y()+g1.y()) );
 
52
        else if ( g1.x() > g0.x() )
 
53
            out_mid.set( 0.5*((g0.x()+2*osg::PI)+g1.x()), 0.5*(g0.y()+g1.y()) );
 
54
        else
 
55
           out_mid.set( 0.5*(g0.x()+(g1.x()+2*osg::PI)), 0.5*(g0.y()+g1.y()) );
 
56
    }
 
57
 
 
58
    // finds the midpoint between two geocentric coordinates. We have to convert
 
59
    // back to geographic in order to get the correct interpolation. Spherical
 
60
    // conversion is good enough
 
61
    osg::Vec3d
 
62
    geocentricMidpoint( const osg::Vec3d& v0, const osg::Vec3d& v1 )
 
63
    {
 
64
        // geocentric to spherical:
 
65
        osg::Vec2d g0, g1;
 
66
        geocentricToGeodetic(v0, g0);
 
67
        geocentricToGeodetic(v1, g1);
 
68
 
 
69
        osg::Vec2d mid;
 
70
        geodeticMidpoint(g0, g1, mid);
 
71
 
 
72
        double size = 0.5*(v0.length() + v1.length());
 
73
 
 
74
        // spherical to geocentric:
 
75
        double sin_lat = sin(mid.y());
 
76
        return osg::Vec3d( cos(mid.x())*sin_lat, sin(mid.x())*sin_lat, cos(mid.y()) ) * size;
 
77
    }
 
78
 
 
79
    // the approximate surface-distance between two geocentric points (spherical)
 
80
    double
 
81
    geocentricSurfaceDistance( const osg::Vec3d& v0, const osg::Vec3d& v1 )
 
82
    {
 
83
        osg::Vec2d g0, g1;
 
84
        geocentricToGeodetic(v0, g0);
 
85
        geocentricToGeodetic(v1, g1);
 
86
        return GeoMath::distance( v0.y(), v0.x(), v1.y(), v1.x() );
 
87
    }    
 
88
 
 
89
    // returns the geocentric bisection vector
 
90
    osg::Vec3d
 
91
    bisector( const osg::Vec3d& v0, const osg::Vec3d& v1 )
 
92
    {
 
93
        osg::Vec3d f = (v0+v1)*0.5;
 
94
        f.normalize();
 
95
        return f * 0.5*(v0.length() + v1.length());
 
96
    }    
 
97
 
 
98
    // the angle between two 3d vectors
 
99
    double
 
100
    angleBetween( const osg::Vec3d& v0, const osg::Vec3d& v1 )
 
101
    {
 
102
        osg::Vec3d v0n = v0; v0n.normalize();
 
103
        osg::Vec3d v1n = v1; v1n.normalize();
 
104
        return fabs( acos( v0n * v1n ) );
 
105
    }
 
106
 
 
107
    //--------------------------------------------------------------------
 
108
 
 
109
    struct Triangle
 
110
    {
 
111
        Triangle() { }
 
112
        Triangle(GLuint i0, GLuint i1, GLuint i2) : _i0(i0), _i1(i1), _i2(i2) { }
 
113
        GLuint _i0, _i1, _i2;
 
114
    };
 
115
 
 
116
    typedef std::queue<Triangle> TriangleQueue;
 
117
    typedef std::vector<Triangle> TriangleVector;
 
118
 
 
119
    struct TriangleData
 
120
    {
 
121
        typedef std::map<osg::Vec3,GLuint> VertMap;
 
122
        VertMap _vertMap;
 
123
        osg::Vec3Array* _verts;
 
124
        TriangleQueue _tris;
 
125
        
 
126
        TriangleData()
 
127
        {
 
128
            _verts = new osg::Vec3Array();
 
129
        }
 
130
 
 
131
        GLuint record( const osg::Vec3& v )
 
132
        {
 
133
            VertMap::iterator i = _vertMap.find(v);
 
134
            if ( i == _vertMap.end() )
 
135
            {
 
136
                GLuint index = _verts->size();
 
137
                _verts->push_back(v);
 
138
                _vertMap[v] = index;
 
139
                return index;
 
140
            }
 
141
            else
 
142
            {
 
143
                return i->second;
 
144
            }
 
145
        }
 
146
        
 
147
        void operator()( const osg::Vec3& v0, const osg::Vec3& v1, const osg::Vec3& v2, bool temp )
 
148
        {
 
149
            _tris.push( Triangle(record(v0), record(v1), record(v2)) );
 
150
        }
 
151
    };      
 
152
 
 
153
    struct Edge
 
154
    {
 
155
        Edge() { }
 
156
        Edge( GLuint i0, GLuint i1 ) : _i0(i0), _i1(i1) { }
 
157
        GLuint _i0, _i1;
 
158
        bool operator < (const Edge& rhs) const {
 
159
            return
 
160
                _i0 < rhs._i0 ? true :
 
161
                _i0 > rhs._i0 ? false :
 
162
                _i1 < rhs._i1 ? true :
 
163
                _i1 > rhs._i1 ? false :
 
164
                false;
 
165
        }
 
166
        bool operator == (const Edge& rhs) const { return _i0==rhs._i0 && _i1==rhs._i1; }
 
167
    };
 
168
 
 
169
    typedef std::map<Edge,GLuint> EdgeMap;
 
170
    
 
171
    /**
 
172
     * Populates the geometry object with a collection of index elements primitives.
 
173
     */
 
174
    template<typename ETYPE, typename VTYPE>
 
175
    void populateTriangles( osg::Geometry& geom, const TriangleVector& tris, unsigned int maxElementsPerEBO )
 
176
    {
 
177
        unsigned int totalTris = tris.size();
 
178
        unsigned int totalTrisWritten = 0;
 
179
        unsigned int numElementsInCurrentEBO = maxElementsPerEBO;
 
180
 
 
181
        ETYPE* ebo = 0L;
 
182
 
 
183
        for( TriangleVector::const_iterator i = tris.begin(); i != tris.end(); ++i )
 
184
        {
 
185
            if ( numElementsInCurrentEBO+2 >= maxElementsPerEBO )
 
186
            {
 
187
                if ( ebo )
 
188
                {
 
189
                    geom.addPrimitiveSet( ebo );
 
190
                }
 
191
 
 
192
                ebo = new ETYPE( GL_TRIANGLES );
 
193
 
 
194
                unsigned int trisRemaining = totalTris - totalTrisWritten;
 
195
                ebo->reserve( osg::minimum( trisRemaining*3, maxElementsPerEBO ) );
 
196
 
 
197
                numElementsInCurrentEBO = 0;
 
198
            }
 
199
            ebo->push_back( static_cast<VTYPE>( i->_i0 ) );
 
200
            ebo->push_back( static_cast<VTYPE>( i->_i1 ) );
 
201
            ebo->push_back( static_cast<VTYPE>( i->_i2 ) );
 
202
 
 
203
            numElementsInCurrentEBO += 3;
 
204
            ++totalTrisWritten;
 
205
        }
 
206
 
 
207
        if ( ebo && ebo->size() > 0 )
 
208
        {
 
209
            geom.addPrimitiveSet( ebo );
 
210
        }
 
211
    }
 
212
 
 
213
    //----------------------------------------------------------------------
 
214
 
 
215
    struct Line
 
216
    {
 
217
        Line() { }
 
218
        Line(GLuint i0, GLuint i1) : _i0(i0), _i1(i1) { }
 
219
        GLuint _i0, _i1;
 
220
    };
 
221
 
 
222
    typedef std::queue<Line> LineQueue;
 
223
    typedef std::vector<Line> LineVector;
 
224
 
 
225
    struct LineData
 
226
    {
 
227
        typedef std::map<osg::Vec3,GLuint> VertMap;
 
228
        VertMap _vertMap;
 
229
        osg::Vec3Array* _verts;
 
230
        LineQueue _lines;
 
231
        
 
232
        LineData()
 
233
        {
 
234
            _verts = new osg::Vec3Array();
 
235
        }
 
236
 
 
237
        GLuint record( const osg::Vec3& v )
 
238
        {
 
239
            VertMap::iterator i = _vertMap.find(v);
 
240
            if ( i == _vertMap.end() )
 
241
            {
 
242
                GLuint index = _verts->size();
 
243
                _verts->push_back(v);
 
244
                _vertMap[v] = index;
 
245
                return index;
 
246
            }
 
247
            else
 
248
            {
 
249
                return i->second;
 
250
            }
 
251
        }
 
252
        
 
253
        void operator()( const osg::Vec3& v0, const osg::Vec3& v1, bool temp )
 
254
        {
 
255
            _lines.push( Line( record(v0), record(v1) ) );
 
256
        }
 
257
    };       
 
258
    
 
259
    /**
 
260
     * Populates the geometry object with a collection of index elements primitives.
 
261
     */
 
262
    template<typename ETYPE, typename VTYPE>
 
263
    void populateLines( osg::Geometry& geom, const LineVector& lines, unsigned int maxElementsPerEBO )
 
264
    {
 
265
        unsigned int totalLines = lines.size();
 
266
        unsigned int totalLinesWritten = 0;
 
267
        unsigned int numElementsInCurrentEBO = maxElementsPerEBO;
 
268
 
 
269
        ETYPE* ebo = 0L;
 
270
 
 
271
        for( LineVector::const_iterator i = lines.begin(); i != lines.end(); ++i )
 
272
        {
 
273
            if ( numElementsInCurrentEBO+2 >= maxElementsPerEBO )
 
274
            {
 
275
                if ( ebo )
 
276
                {
 
277
                    geom.addPrimitiveSet( ebo );
 
278
                }
 
279
 
 
280
                ebo = new ETYPE( GL_LINES );
 
281
 
 
282
                unsigned int linesRemaining = totalLines - totalLinesWritten;
 
283
                ebo->reserve( osg::minimum( linesRemaining*2, maxElementsPerEBO ) );
 
284
 
 
285
                numElementsInCurrentEBO = 0;
 
286
            }
 
287
            ebo->push_back( static_cast<VTYPE>( i->_i0 ) );
 
288
            ebo->push_back( static_cast<VTYPE>( i->_i1 ) );
 
289
 
 
290
            numElementsInCurrentEBO += 3;
 
291
            ++totalLinesWritten;
 
292
        }
 
293
 
 
294
        if ( ebo && ebo->size() > 0 )
 
295
        {
 
296
            geom.addPrimitiveSet( ebo );
 
297
        }
 
298
    }
 
299
 
 
300
    static const osg::Vec3d s_pole(0,0,1);
 
301
    static const double s_maxLatAdjustment(0.75);
 
302
 
 
303
    /**
 
304
     * Collects all the line segments from the geometry, coalesces them into a single
 
305
     * line set, subdivides it according to the granularity threshold, and replaces
 
306
     * the data in the Geometry object with the new vertex and primitive data.
 
307
     */
 
308
    void subdivideLines(
 
309
        double granularity,
 
310
        osg::Geometry& geom,
 
311
        const osg::Matrixd& W2L, // world=>local xform
 
312
        const osg::Matrixd& L2W, // local=>world xform
 
313
        unsigned int maxElementsPerEBO )
 
314
    {
 
315
        // collect all the line segments in the geometry.
 
316
        LineFunctor<LineData> data;
 
317
        geom.accept( data );
 
318
    
 
319
        int numLinesIn = data._lines.size();
 
320
 
 
321
        LineVector done;
 
322
        done.reserve( 2 * data._lines.size() );
 
323
 
 
324
        // Subdivide lines until we run out.
 
325
        while( data._lines.size() > 0 )
 
326
        {
 
327
            Line line = data._lines.front();
 
328
            data._lines.pop();
 
329
 
 
330
            osg::Vec3d v0_w = (*data._verts)[line._i0] * L2W;
 
331
            osg::Vec3d v1_w = (*data._verts)[line._i1] * L2W;
 
332
 
 
333
            double g0 = angleBetween(v0_w, v1_w);
 
334
 
 
335
            if ( g0 > granularity )
 
336
            {
 
337
                data._verts->push_back( geocentricMidpoint(v0_w, v1_w) * W2L );
 
338
                GLuint i = data._verts->size()-1;
 
339
 
 
340
                data._lines.push( Line( line._i0, i ) );
 
341
                data._lines.push( Line( i, line._i1 ) );
 
342
            }
 
343
            else
 
344
            {
 
345
                // line is small enough- put it on the "done" list.
 
346
                done.push_back( line );
 
347
            }
 
348
        }
 
349
 
 
350
        if ( done.size() > 0 )
 
351
        {
 
352
            while( geom.getNumPrimitiveSets() > 0 )
 
353
                geom.removePrimitiveSet(0);
 
354
 
 
355
            // set the new VBO.
 
356
            geom.setVertexArray( data._verts );
 
357
 
 
358
            if ( data._verts->size() < 256 )
 
359
                populateLines<osg::DrawElementsUByte,GLubyte>( geom, done, maxElementsPerEBO );
 
360
            else if ( data._verts->size() < 65536 )
 
361
                populateLines<osg::DrawElementsUShort,GLushort>( geom, done, maxElementsPerEBO );
 
362
            else
 
363
                populateLines<osg::DrawElementsUInt,GLuint>( geom, done, maxElementsPerEBO );
 
364
        }
 
365
    }
 
366
 
 
367
 
 
368
    //----------------------------------------------------------------------
 
369
 
 
370
    /**
 
371
     * Collects all the triangles from the geometry, coalesces them into a single
 
372
     * triangle set, subdivides them according to the granularity threshold, and
 
373
     * replaces the data in the Geometry object with the new vertex and primitive
 
374
     * data.
 
375
     *
 
376
     * The subdivision algorithm is adapted from http://bit.ly/dTIagq
 
377
     * (c) Copyright 2010 Patrick Cozzi and Deron Ohlarik, MIT License.
 
378
     */
 
379
    void subdivideTriangles(
 
380
        double granularity,
 
381
        osg::Geometry& geom,
 
382
        const osg::Matrixd& W2L, // world=>local xform
 
383
        const osg::Matrixd& L2W, // local=>world xform
 
384
        unsigned int maxElementsPerEBO )
 
385
    {
 
386
        // collect all the triangled in the geometry.
 
387
        osg::TriangleFunctor<TriangleData> data;
 
388
        geom.accept( data );
 
389
 
 
390
        int numTrisIn = data._tris.size();
 
391
 
 
392
        TriangleVector done;
 
393
        done.reserve(2.0 * data._tris.size());
 
394
 
 
395
        // Used to make sure shared edges are not split more than once.
 
396
        EdgeMap edges;
 
397
 
 
398
        // Subdivide triangles until we run out
 
399
        while( data._tris.size() > 0 )
 
400
        {
 
401
            Triangle tri = data._tris.front();
 
402
            data._tris.pop();
 
403
 
 
404
            osg::Vec3d v0_w = (*data._verts)[tri._i0] * L2W;
 
405
            osg::Vec3d v1_w = (*data._verts)[tri._i1] * L2W;
 
406
            osg::Vec3d v2_w = (*data._verts)[tri._i2] * L2W;
 
407
 
 
408
            double g0 = angleBetween(v0_w, v1_w);
 
409
            double g1 = angleBetween(v1_w, v2_w);
 
410
            double g2 = angleBetween(v2_w, v0_w);
 
411
            double max = osg::maximum( g0, osg::maximum(g1, g2) );
 
412
 
 
413
            if ( max > granularity )
 
414
            {
 
415
                if ( g0 == max )
 
416
                {
 
417
                    Edge edge( osg::minimum(tri._i0, tri._i1), osg::maximum(tri._i0, tri._i1) );
 
418
                    
 
419
                    EdgeMap::iterator ei = edges.find(edge);
 
420
                    GLuint i;
 
421
                    if ( ei == edges.end() )
 
422
                    {
 
423
                        data._verts->push_back( geocentricMidpoint(v0_w, v1_w) * W2L );
 
424
                        i = data._verts->size() - 1;
 
425
                        edges[edge] = i;
 
426
                    }
 
427
                    else
 
428
                    {
 
429
                        i = ei->second;
 
430
                    }
 
431
 
 
432
                    data._tris.push( Triangle(tri._i0, i, tri._i2) );
 
433
                    data._tris.push( Triangle(i, tri._i1, tri._i2) );
 
434
                }
 
435
                else if ( g1 == max )
 
436
                {
 
437
                    Edge edge( osg::minimum(tri._i1, tri._i2), osg::maximum(tri._i1,tri._i2) );
 
438
 
 
439
                    EdgeMap::iterator ei = edges.find(edge);
 
440
                    GLuint i;
 
441
                    if ( ei == edges.end() )
 
442
                    {
 
443
                        data._verts->push_back( geocentricMidpoint(v1_w, v2_w) * W2L );
 
444
                        i = data._verts->size() - 1;
 
445
                        edges[edge] = i;
 
446
                    }
 
447
                    else
 
448
                    {
 
449
                        i = ei->second;
 
450
                    }
 
451
 
 
452
                    data._tris.push( Triangle(tri._i1, i, tri._i0) );
 
453
                    data._tris.push( Triangle(i, tri._i2, tri._i0) );
 
454
                }
 
455
                else if ( g2 == max )
 
456
                {
 
457
                    Edge edge( osg::minimum(tri._i2, tri._i0), osg::maximum(tri._i2,tri._i0) );
 
458
 
 
459
                    EdgeMap::iterator ei = edges.find(edge);
 
460
                    GLuint i;
 
461
                    if ( ei == edges.end() )
 
462
                    {
 
463
                        data._verts->push_back( geocentricMidpoint(v2_w, v0_w) * W2L );
 
464
                        i = data._verts->size() - 1;
 
465
                        edges[edge] = i;
 
466
                    }
 
467
                    else
 
468
                    {
 
469
                        i = ei->second;
 
470
                    }
 
471
 
 
472
                    data._tris.push( Triangle(tri._i2, i, tri._i1) );
 
473
                    data._tris.push( Triangle(i, tri._i0, tri._i1) );
 
474
                }
 
475
            }
 
476
            else
 
477
            {
 
478
                // triangle is small enough- put it on the "done" list.
 
479
                done.push_back(tri);
 
480
            }
 
481
        }
 
482
 
 
483
        if ( done.size() > 0 )
 
484
        {
 
485
            // first, remove the old primitive sets.
 
486
            while( geom.getNumPrimitiveSets() > 0 )
 
487
                geom.removePrimitiveSet( 0 );
 
488
 
 
489
            // set the new VBO.
 
490
            geom.setVertexArray( data._verts );
 
491
 
 
492
            if ( data._verts->size() < 256 )
 
493
                populateTriangles<osg::DrawElementsUByte,GLubyte>( geom, done, maxElementsPerEBO );
 
494
            else if ( data._verts->size() < 65536 )
 
495
                populateTriangles<osg::DrawElementsUShort,GLushort>( geom, done, maxElementsPerEBO );
 
496
            else
 
497
                populateTriangles<osg::DrawElementsUInt,GLuint>( geom, done, maxElementsPerEBO );
 
498
        }
 
499
    }
 
500
 
 
501
    void subdivide(
 
502
        double granularity,
 
503
        osg::Geometry& geom,
 
504
        const osg::Matrixd& W2L, // world=>local xform
 
505
        const osg::Matrixd& L2W, // local=>world xform
 
506
        unsigned int maxElementsPerEBO )
 
507
    {
 
508
        GLenum mode = geom.getPrimitiveSet(0)->getMode();
 
509
 
 
510
        if ( mode == GL_POINTS )
 
511
            return;
 
512
 
 
513
        if ( mode == GL_LINES || mode == GL_LINE_STRIP || mode == GL_LINE_LOOP )
 
514
        {
 
515
            subdivideLines( granularity, geom, W2L, L2W, maxElementsPerEBO );
 
516
        }
 
517
        else
 
518
        {
 
519
            subdivideTriangles( granularity, geom, W2L, L2W, maxElementsPerEBO );
 
520
 
 
521
            //osgUtil::VertexCacheVisitor cacheOptimizer;
 
522
            //cacheOptimizer.optimizeVertices( geom );
 
523
        }
 
524
        
 
525
        //osgUtil::VertexAccessOrderVisitor orderOptimizer;
 
526
        //orderOptimizer.optimizeOrder( geom );
 
527
    }
 
528
}
 
529
 
 
530
//------------------------------------------------------------------------
 
531
 
 
532
MeshSubdivider::MeshSubdivider(const osg::Matrixd& world2local,
 
533
                               const osg::Matrixd& local2world ) :
 
534
_local2world(local2world),
 
535
_world2local(world2local),
 
536
_maxElementsPerEBO( INT_MAX )
 
537
{
 
538
    if ( !_world2local.isIdentity() && _local2world.isIdentity() )
 
539
        _local2world = osg::Matrixd::inverse(_world2local);
 
540
    else if ( _world2local.isIdentity() && !_local2world.isIdentity() )
 
541
        _world2local = osg::Matrixd::inverse(_local2world);
 
542
}
 
543
 
 
544
void
 
545
MeshSubdivider::run(double granularity, osg::Geometry& geom)
 
546
{
 
547
    if ( geom.getNumPrimitiveSets() < 1 )
 
548
        return;
 
549
 
 
550
    subdivide( granularity, geom, _world2local, _local2world, _maxElementsPerEBO );
 
551
}