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 00020 #include <osgEarthSymbology/GeometryExtrudeSymbolizer> 00021 #include <osgEarthSymbology/ExtrudedSymbol> 00022 #include <osgUtil/Tessellator> 00023 #include <osg/Geometry> 00024 #include <osg/Point> 00025 #include <osg/LineWidth> 00026 #include <osg/Material> 00027 #include <osg/Geode> 00028 00029 using namespace osgEarth::Symbology; 00030 00031 GeometryExtrudeSymbolizer::GeometryExtrudeSymbolizer() 00032 { 00033 } 00034 00035 00036 void GeometryExtrudeSymbolizer::tessellate( osg::Geometry* geom ) 00037 { 00038 osgUtil::Tessellator tess; 00039 tess.setTessellationType( osgUtil::Tessellator::TESS_TYPE_GEOMETRY ); 00040 tess.setWindingType( osgUtil::Tessellator::TESS_WINDING_ODD ); 00041 // tess.setWindingType( osgUtil::Tessellator::TESS_WINDING_POSITIVE ); 00042 tess.retessellatePolygons( *geom ); 00043 } 00044 00045 bool 00046 GeometryExtrudeSymbolizer::compile(State<GeometryContent>* state, 00047 osg::Group* attachPoint) 00048 { 00049 if ( !state || !attachPoint || !state->getContent() || !state->getStyle() ) 00050 return false; 00051 00052 osg::ref_ptr<osg::Group> newSymbolized = new osg::Group; 00053 00054 const GeometryList& geometryList = state->getContent()->getGeometryList(); 00055 for (GeometryList::const_iterator it = geometryList.begin(); it != geometryList.end(); ++it) 00056 { 00057 Geometry* geometry = *it; 00058 if (!geometry) 00059 continue; 00060 00061 GeometryIterator geomIterator( geometry ); 00062 geomIterator.traverseMultiGeometry() = true; 00063 geomIterator.traversePolygonHoles() = true; 00064 while( geomIterator.hasMore() ) 00065 { 00066 Geometry* part = geomIterator.next(); 00067 if (!part) 00068 continue; 00069 00070 osg::Vec4 color = osg::Vec4(1.0, 0.0, 1.0, 1.); 00071 float height = 1.0; 00072 float offset = 1.0; 00073 00074 switch( part->getType()) 00075 { 00076 case Geometry::TYPE_LINESTRING: 00077 case Geometry::TYPE_RING: 00078 { 00079 const ExtrudedLineSymbol* line = state->getStyle()->getSymbol<ExtrudedLineSymbol>(); 00080 if (line) 00081 { 00082 color = line->stroke()->color(); 00083 height = line->extrude()->height(); 00084 offset = line->extrude()->offset(); 00085 } 00086 } 00087 break; 00088 00089 case Geometry::TYPE_POLYGON: 00090 { 00091 const ExtrudedPolygonSymbol* polygon = state->getStyle()->getSymbol<ExtrudedPolygonSymbol>(); 00092 if (polygon) 00093 { 00094 color = polygon->fill()->color(); 00095 height = polygon->extrude()->height(); 00096 offset = polygon->extrude()->offset(); 00097 } 00098 } 00099 break; 00100 default: 00101 continue; 00102 break; 00103 } 00104 00105 osg::Geode* geode = extrude(part, offset, height, state->getContext() ); 00106 if (geode && geode->getNumDrawables()) 00107 { 00108 osg::Material* material = new osg::Material; 00109 material->setDiffuse(osg::Material::FRONT_AND_BACK, color); 00110 geode->getOrCreateStateSet()->setAttributeAndModes(material); 00111 newSymbolized->addChild(geode); 00112 } 00113 } 00114 } 00115 00116 if (newSymbolized->getNumChildren()) 00117 { 00118 attachPoint->removeChildren(0, attachPoint->getNumChildren()); 00119 attachPoint->addChild(newSymbolized.get()); 00120 return true; 00121 } 00122 00123 return false; 00124 } 00125 00126 osg::Geode* GeometryExtrudeSymbolizer::extrude(Geometry* geom, double offset, double height, SymbolizerContext* context ) 00127 { 00128 if ( !geom ) return 0L; 00129 00130 int numRings = 0; 00131 00132 // start by offsetting the input data. 00133 { 00134 GeometryIterator i( geom ); 00135 i.traverseMultiGeometry() = true; 00136 i.traversePolygonHoles() = true; 00137 while( i.hasMore() ) 00138 { 00139 Geometry* part = i.next(); 00140 if (offset != 0.0) 00141 { 00142 for( osg::Vec3dArray::iterator j = part->begin(); j != part->end(); j++ ) 00143 { 00144 { 00145 (*j).z() += offset; 00146 } 00147 } 00148 } 00149 00150 // in the meantime, count the # of closed geoms. We will need to know this in 00151 // order to pre-allocate the proper # of verts. 00152 if ( part->getType() == Geometry::TYPE_POLYGON || part->getType() == Geometry::TYPE_RING ) 00153 { 00154 numRings++; 00155 } 00156 } 00157 } 00158 00159 // now, go thru and remove any coplanar segments from the geometry. The tesselator will 00160 // not work include a vert connecting two colinear segments in the tesselation, and this 00161 // will break the stenciling logic. 00162 #define PARALLEL_EPSILON 0.01 00163 GeometryIterator i( geom ); 00164 i.traverseMultiGeometry() = true; 00165 i.traversePolygonHoles() = true; 00166 while( i.hasMore() ) 00167 { 00168 Geometry* part = i.next(); 00169 if ( part->size() >= 3 ) 00170 { 00171 osg::Vec3d prevVec = part->front() - part->back(); 00172 prevVec.normalize(); 00173 00174 for( osg::Vec3dArray::iterator j = part->begin(); part->size() >= 3 && j != part->end(); ) 00175 { 00176 osg::Vec3d& p0 = *j; 00177 osg::Vec3d& p1 = j+1 != part->end() ? *(j+1) : part->front(); 00178 osg::Vec3d vec = p1-p0; vec.normalize(); 00179 00180 // if the vectors are essentially parallel, remove the extraneous vertex. 00181 if ( (prevVec ^ vec).length() < PARALLEL_EPSILON ) 00182 { 00183 j = part->erase( j ); 00184 //OE_NOTICE << "removed colinear segment" << std::endl; 00185 } 00186 else 00187 { 00188 ++j; 00189 prevVec = vec; 00190 } 00191 } 00192 } 00193 } 00194 00195 00196 bool made_geom = true; 00197 00198 // total up all the points so we can pre-allocate the vertex arrays. 00199 int num_cap_verts = geom->getTotalPointCount(); 00200 int num_wall_verts = 2 * (num_cap_verts + numRings); // add in numRings b/c we need to close each wall 00201 00202 osg::Geometry* walls = new osg::Geometry(); 00203 osg::Vec3Array* verts = new osg::Vec3Array( num_wall_verts ); 00204 walls->setVertexArray( verts ); 00205 00206 osg::Geometry* top_cap = new osg::Geometry(); 00207 osg::Vec3Array* top_verts = new osg::Vec3Array( num_cap_verts ); 00208 top_cap->setVertexArray( top_verts ); 00209 00210 osg::Geometry* bottom_cap = new osg::Geometry(); 00211 osg::Vec3Array* bottom_verts = new osg::Vec3Array( num_cap_verts ); 00212 bottom_cap->setVertexArray( bottom_verts ); 00213 00214 int wall_vert_ptr = 0; 00215 int top_vert_ptr = 0; 00216 int bottom_vert_ptr = 0; 00217 00218 //double target_len = height; 00219 00220 // now generate the extruded geometry. 00221 GeometryIterator k( geom ); 00222 while( k.hasMore() ) 00223 { 00224 Geometry* part = k.next(); 00225 00226 unsigned int wall_part_ptr = wall_vert_ptr; 00227 unsigned int top_part_ptr = top_vert_ptr; 00228 unsigned int bottom_part_ptr = bottom_vert_ptr; 00229 double part_len = 0.0; 00230 00231 GLenum prim_type = part->getType() == Geometry::TYPE_POINTSET ? GL_LINES : GL_TRIANGLE_STRIP; 00232 00233 for( osg::Vec3dArray::const_iterator m = part->begin(); m != part->end(); ++m ) 00234 { 00235 osg::Vec3d extrude_vec; 00236 { 00237 extrude_vec.set( m->x(), m->y(), height ); 00238 } 00239 00240 (*top_verts)[top_vert_ptr++] = extrude_vec; 00241 (*bottom_verts)[bottom_vert_ptr++] = *m; 00242 00243 part_len += wall_vert_ptr > wall_part_ptr? 00244 (extrude_vec - (*verts)[wall_vert_ptr-2]).length() : 00245 0.0; 00246 00247 int p; 00248 00249 p = wall_vert_ptr++; 00250 (*verts)[p] = extrude_vec; 00251 00252 p = wall_vert_ptr++; 00253 (*verts)[p] = *m; 00254 } 00255 00256 // close the wall if it's a ring/poly: 00257 if ( part->getType() == Geometry::TYPE_RING || part->getType() == Geometry::TYPE_POLYGON ) 00258 { 00259 part_len += wall_vert_ptr > wall_part_ptr? 00260 ((*verts)[wall_part_ptr] - (*verts)[wall_vert_ptr-2]).length() : 00261 0.0; 00262 00263 int p; 00264 00265 p = wall_vert_ptr++; 00266 (*verts)[p] = (*verts)[wall_part_ptr]; 00267 00268 p = wall_vert_ptr++; 00269 (*verts)[p] = (*verts)[wall_part_ptr+1]; 00270 } 00271 00272 walls->addPrimitiveSet( new osg::DrawArrays( 00273 prim_type, 00274 wall_part_ptr, wall_vert_ptr - wall_part_ptr ) ); 00275 00276 top_cap->addPrimitiveSet( new osg::DrawArrays( 00277 osg::PrimitiveSet::LINE_LOOP, 00278 top_part_ptr, top_vert_ptr - top_part_ptr ) ); 00279 00280 // reverse the bottom verts so the front face is down: 00281 std::reverse( bottom_verts->begin()+bottom_part_ptr, bottom_verts->begin()+bottom_vert_ptr ); 00282 00283 bottom_cap->addPrimitiveSet( new osg::DrawArrays( 00284 osg::PrimitiveSet::LINE_LOOP, 00285 bottom_part_ptr, bottom_vert_ptr - bottom_part_ptr ) ); 00286 } 00287 00288 // build solid surfaces for the caps: 00289 tessellate( top_cap ); 00290 tessellate( bottom_cap ); 00291 00292 osg::Geode* geode = new osg::Geode(); 00293 geode->addDrawable( walls ); 00294 geode->addDrawable( top_cap ); 00295 geode->addDrawable( bottom_cap ); 00296 00297 return geode; 00298 }