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/MeshConsolidator> 00020 #include <osgEarthSymbology/LineFunctor> 00021 #include <osg/TriangleFunctor> 00022 #include <osg/TriangleIndexFunctor> 00023 #include <limits> 00024 #include <map> 00025 #include <iterator> 00026 00027 #define LC "[MeshConsolidator] " 00028 00029 using namespace osgEarth; 00030 using namespace osgEarth::Symbology; 00031 00032 //------------------------------------------------------------------------ 00033 00034 namespace 00035 { 00036 template<typename T> 00037 struct Collector 00038 { 00039 osg::Geometry::PrimitiveSetList* _newPrimSets; 00040 unsigned _maxSize; 00041 T* _current; 00042 00043 Collector() : _current(0L) { } 00044 00045 void operator()( unsigned i0, unsigned i1, unsigned i2 ) 00046 { 00047 if ( _current == 0L || _current->size() > _maxSize-3 ) 00048 { 00049 _current = new T (GL_TRIANGLES); 00050 _newPrimSets->push_back( _current ); 00051 } 00052 00053 _current->push_back( i0 ); 00054 _current->push_back( i1 ); 00055 _current->push_back( i2 ); 00056 } 00057 }; 00058 00059 template<typename FROM, typename TO> 00060 osg::PrimitiveSet* copy( FROM* src, unsigned offset ) 00061 { 00062 TO* newDE = new TO( src->getMode() ); 00063 newDE->reserve( src->size() ); 00064 for( typename FROM::const_iterator i = src->begin(); i != src->end(); ++i ) 00065 newDE->push_back( (*i) + offset ); 00066 return newDE; 00067 } 00068 00069 template<typename FROM> 00070 osg::PrimitiveSet* remake( FROM* src, unsigned numVerts, unsigned offset ) 00071 { 00072 if ( numVerts < 0x100 ) 00073 return copy<FROM,osg::DrawElementsUByte>( src, offset ); 00074 else if ( numVerts < 0x10000 ) 00075 return copy<FROM,osg::DrawElementsUShort>( src, offset ); 00076 else 00077 return copy<FROM,osg::DrawElementsUInt>( src, offset ); 00078 } 00079 00080 bool canOptimize( osg::Geometry& geom ) 00081 { 00082 osg::Array* vertexArray = geom.getVertexArray(); 00083 if ( !vertexArray ) 00084 return false; 00085 00086 // check that everything is bound per-vertex 00087 00088 if ( geom.getColorArray() != 0L && geom.getColorBinding() != osg::Geometry::BIND_PER_VERTEX ) 00089 return false; 00090 00091 if ( geom.getNormalArray() != 0L && geom.getNormalBinding() != osg::Geometry::BIND_PER_VERTEX ) 00092 return false; 00093 00094 if ( geom.getSecondaryColorArray() != 0L && geom.getSecondaryColorBinding() != osg::Geometry::BIND_PER_VERTEX ) 00095 return false; 00096 00097 if ( geom.getVertexAttribArrayList().size() > 0 ) 00098 { 00099 unsigned n = geom.getVertexAttribArrayList().size(); 00100 for( unsigned i=0; i<n; ++i ) 00101 { 00102 if ( geom.getVertexAttribBinding( i ) != osg::Geometry::BIND_PER_VERTEX ) 00103 return false; 00104 } 00105 } 00106 00107 return true; 00108 } 00109 } 00110 00111 //------------------------------------------------------------------------ 00112 00113 void 00114 MeshConsolidator::run( osg::Geometry& geom ) 00115 { 00116 if ( !canOptimize(geom) ) 00117 return; 00118 00119 osg::Geometry::PrimitiveSetList& primSets = geom.getPrimitiveSetList(); 00120 osg::Geometry::PrimitiveSetList triSets, nonTriSets; 00121 00122 for( osg::Geometry::PrimitiveSetList::iterator i = primSets.begin(); i != primSets.end(); ++i ) 00123 { 00124 osg::PrimitiveSet* pset = i->get(); 00125 switch( pset->getMode() ) 00126 { 00127 case osg::PrimitiveSet::TRIANGLES: 00128 case osg::PrimitiveSet::TRIANGLE_STRIP: 00129 case osg::PrimitiveSet::TRIANGLE_FAN: 00130 case osg::PrimitiveSet::QUADS: 00131 case osg::PrimitiveSet::QUAD_STRIP: 00132 case osg::PrimitiveSet::POLYGON: 00133 triSets.push_back( pset ); 00134 break; 00135 00136 default: 00137 nonTriSets.push_back( pset ); 00138 } 00139 } 00140 00141 if ( triSets.size() > 0 ) 00142 { 00143 osg::Array* vertexArray = geom.getVertexArray(); 00144 unsigned numVerts = vertexArray->getNumElements(); 00145 osg::Geometry::PrimitiveSetList newPrimSets; 00146 00147 if ( numVerts < 0x100 ) 00148 { 00149 osg::TriangleIndexFunctor< Collector<osg::DrawElementsUByte> > collector; 00150 collector._newPrimSets = &newPrimSets; 00151 collector._maxSize = 0xFF; 00152 geom.accept( collector ); 00153 } 00154 else if ( numVerts < 0x10000 ) 00155 { 00156 osg::TriangleIndexFunctor< Collector<osg::DrawElementsUShort> > collector; 00157 collector._newPrimSets = &newPrimSets; 00158 collector._maxSize = 0xFFFF; 00159 geom.accept( collector ); 00160 } 00161 else 00162 { 00163 osg::TriangleIndexFunctor< Collector<osg::DrawElementsUInt> > collector; 00164 collector._newPrimSets = &newPrimSets; 00165 collector._maxSize = 0xFFFFFFFF; 00166 geom.accept( collector ); 00167 } 00168 00169 for( osg::Geometry::PrimitiveSetList::iterator i = newPrimSets.begin(); i != newPrimSets.end(); ++i ) 00170 nonTriSets.push_back( i->get() ); 00171 } 00172 00173 geom.setPrimitiveSetList( nonTriSets ); 00174 } 00175 00176 void 00177 MeshConsolidator::run( osg::Geode& geode ) 00178 { 00179 unsigned numVerts = 0; 00180 unsigned numColors = 0; 00181 unsigned numNormals = 0; 00182 unsigned numTexCoordArrays = 0; 00183 unsigned numVertAttribArrays = 0; 00184 std::vector<unsigned> texCoordArrayUnits; 00185 00186 osg::Geometry::AttributeBinding newColorsBinding; 00187 osg::Geometry::AttributeBinding newNormalsBinding; 00188 00189 // first, triangulate all the geometries and count all the components: 00190 for( unsigned i=0; i<geode.getNumDrawables(); ++i ) 00191 { 00192 osg::Geometry* geom = geode.getDrawable(i)->asGeometry(); 00193 if ( geom ) 00194 { 00195 if ( !canOptimize(*geom) ) 00196 continue; 00197 00198 // optimize it into triangles first: 00199 run( *geom ); 00200 00201 osg::Array* verts = geom->getVertexArray(); 00202 if ( verts ) 00203 numVerts += verts->getNumElements(); 00204 00205 osg::Array* colors = geom->getColorArray(); 00206 if ( colors ) 00207 numColors += colors->getNumElements(); 00208 00209 osg::Array* normals = geom->getNormalArray(); 00210 if ( normals ) 00211 numNormals += normals->getNumElements(); 00212 00213 // NOTE!! tex/attrib array counts much already be equal. 00214 if ( texCoordArrayUnits.size() == 0 ) 00215 { 00216 for( unsigned u=0; u<32; ++u ) { 00217 if ( geom->getTexCoordArray(u) != 0L ) 00218 texCoordArrayUnits.push_back( u ); 00219 } 00220 } 00221 00222 numVertAttribArrays += geom->getNumVertexAttribArrays(); 00223 } 00224 } 00225 00226 // bail if there are unsupported items in there. 00227 if (geode.getNumDrawables() < 2 || 00228 //numTexCoordArrays > 0 || 00229 numVertAttribArrays > 0 ) 00230 { 00231 return; 00232 } 00233 00234 00235 osg::Vec3Array* newVerts = new osg::Vec3Array(); 00236 newVerts->reserve( numVerts ); 00237 00238 osg::Vec4Array* newColors =0L; 00239 if ( numColors > 0 ) 00240 { 00241 newColors = new osg::Vec4Array(); 00242 newColors->reserve( numVerts ); 00243 newColorsBinding = osg::Geometry::BIND_PER_VERTEX; 00244 //newColors->reserve( numColors==numVerts? numColors : 1 ); 00245 //newColorsBinding = numColors==numVerts? osg::Geometry::BIND_PER_VERTEX : osg::Geometry::BIND_OVERALL; 00246 } 00247 00248 osg::Vec3Array* newNormals =0L; 00249 if ( numNormals > 0 ) 00250 { 00251 newNormals = new osg::Vec3Array(); 00252 newNormals->reserve( numVerts ); 00253 newNormalsBinding = osg::Geometry::BIND_PER_VERTEX; 00254 //newNormals->reserve( numNormals==numVerts? numNormals : 1 ); 00255 //newNormalsBinding = numNormals==numVerts? osg::Geometry::BIND_PER_VERTEX : osg::Geometry::BIND_OVERALL; 00256 } 00257 00258 std::vector<osg::Vec2Array*> newTexCoordsArrays; 00259 for( unsigned i=0; i<texCoordArrayUnits.size(); ++i ) 00260 { 00261 osg::Vec2Array* newTexCoords = new osg::Vec2Array(); 00262 newTexCoords->reserve( numVerts ); 00263 newTexCoordsArrays.push_back( newTexCoords ); 00264 } 00265 00266 unsigned offset = 0; 00267 osg::Geometry::PrimitiveSetList newPrimSets; 00268 00269 std::vector<osg::ref_ptr<osg::Geometry> > nonOptimizedGeoms; 00270 00271 osg::StateSet* unifiedStateSet = 0L; 00272 00273 for( unsigned i=0; i<geode.getNumDrawables(); ++i ) 00274 { 00275 osg::Geometry* geom = geode.getDrawable(i)->asGeometry(); 00276 if ( geom ) 00277 { 00278 if ( !canOptimize(*geom) ) 00279 { 00280 nonOptimizedGeoms.push_back(geom); 00281 continue; 00282 } 00283 00284 // merge in the stateset: 00285 if ( unifiedStateSet == 0L ) 00286 unifiedStateSet = geom->getStateSet(); 00287 else if ( geom->getStateSet() ) 00288 unifiedStateSet->merge( *geom->getStateSet() ); 00289 00290 // copy over the verts: 00291 osg::Vec3Array* geomVerts = dynamic_cast<osg::Vec3Array*>( geom->getVertexArray() ); 00292 if ( geomVerts ) 00293 { 00294 std::copy( geomVerts->begin(), geomVerts->end(), std::back_inserter(*newVerts) ); 00295 00296 if ( newColors ) 00297 { 00298 osg::Vec4Array* colors = dynamic_cast<osg::Vec4Array*>( geom->getColorArray() ); 00299 if ( colors ) 00300 { 00301 if ( newColorsBinding == osg::Geometry::BIND_PER_VERTEX ) 00302 { 00303 std::copy( colors->begin(), colors->end(), std::back_inserter(*newColors) ); 00304 } 00305 else if ( i == 0 ) // overall 00306 { 00307 newColors->push_back( (*colors)[0] ); 00308 } 00309 } 00310 } 00311 00312 if ( newNormals ) 00313 { 00314 osg::Vec3Array* normals = dynamic_cast<osg::Vec3Array*>( geom->getNormalArray() ); 00315 if ( normals ) 00316 { 00317 if ( newNormalsBinding == osg::Geometry::BIND_PER_VERTEX ) 00318 { 00319 std::copy( normals->begin(), normals->end(), std::back_inserter(*newNormals) ); 00320 } 00321 else if ( i == 0 ) // overall 00322 { 00323 newNormals->push_back( (*normals)[0] ); 00324 } 00325 } 00326 } 00327 00328 if ( newTexCoordsArrays.size() > 0 ) 00329 { 00330 for( unsigned a=0; a<texCoordArrayUnits.size(); ++a ) 00331 { 00332 unsigned unit = texCoordArrayUnits[a]; 00333 osg::Vec2Array* texCoords = dynamic_cast<osg::Vec2Array*>( geom->getTexCoordArray(unit) ); 00334 if ( texCoords ) 00335 { 00336 osg::Vec2Array* newTexCoords = newTexCoordsArrays[a]; 00337 std::copy( texCoords->begin(), texCoords->end(), std::back_inserter(*newTexCoords) ); 00338 } 00339 } 00340 } 00341 00342 for( unsigned j=0; j < geom->getNumPrimitiveSets(); ++j ) 00343 { 00344 osg::PrimitiveSet* pset = geom->getPrimitiveSet(j); 00345 osg::PrimitiveSet* newpset = 0L; 00346 00347 if ( dynamic_cast<osg::DrawElementsUByte*>(pset) ) 00348 newpset = remake( static_cast<osg::DrawElementsUByte*>(pset), numVerts, offset ); 00349 else if ( dynamic_cast<osg::DrawElementsUShort*>(pset) ) 00350 newpset = remake( static_cast<osg::DrawElementsUShort*>(pset), numVerts, offset ); 00351 else if ( dynamic_cast<osg::DrawElementsUInt*>(pset) ) 00352 newpset = remake( static_cast<osg::DrawElementsUInt*>(pset), numVerts, offset ); 00353 else if ( dynamic_cast<osg::DrawArrays*>(pset) ) 00354 newpset = new osg::DrawArrays( pset->getMode(), offset, geomVerts->size() ); 00355 00356 if ( newpset ) 00357 newPrimSets.push_back( newpset ); 00358 } 00359 00360 offset += geomVerts->size(); 00361 } 00362 } 00363 } 00364 00365 // assemble the new geometry. 00366 osg::Geometry* newGeom = new osg::Geometry(); 00367 00368 newGeom->setVertexArray( newVerts ); 00369 00370 if ( newColors ) 00371 { 00372 newGeom->setColorArray( newColors ); 00373 newGeom->setColorBinding( newColorsBinding ); 00374 } 00375 00376 if ( newNormals ) 00377 { 00378 newGeom->setNormalArray( newNormals ); 00379 newGeom->setNormalBinding( newNormalsBinding ); 00380 } 00381 00382 if ( newTexCoordsArrays.size() > 0 ) 00383 { 00384 for( unsigned a=0; a<texCoordArrayUnits.size(); ++a ) 00385 { 00386 unsigned unit = texCoordArrayUnits[a]; 00387 newGeom->setTexCoordArray( unit, newTexCoordsArrays[a] ); 00388 } 00389 } 00390 00391 newGeom->setPrimitiveSetList( newPrimSets ); 00392 newGeom->setStateSet( unifiedStateSet ); 00393 00394 // replace the geode's drawables 00395 geode.removeDrawables( 0, geode.getNumDrawables() ); 00396 geode.addDrawable( newGeom ); 00397 for( std::vector<osg::ref_ptr<osg::Geometry> >::iterator i = nonOptimizedGeoms.begin(); i != nonOptimizedGeoms.end(); ++i ) 00398 geode.addDrawable( i->get() ); 00399 }