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 <osgEarthFeatures/ExtrudeGeometryFilter> 00020 #include <osgEarthSymbology/MeshSubdivider> 00021 #include <osgEarthSymbology/MeshConsolidator> 00022 #include <osgEarth/ECEF> 00023 #include <osg/ClusterCullingCallback> 00024 #include <osg/Geode> 00025 #include <osg/Geometry> 00026 #include <osg/MatrixTransform> 00027 #include <osgUtil/Tessellator> 00028 #include <osgUtil/Optimizer> 00029 #include <osgUtil/SmoothingVisitor> 00030 #include <osg/Version> 00031 #include <osg/LineWidth> 00032 #include <osg/PolygonOffset> 00033 #include <osgEarth/Version> 00034 00035 #define LC "[ExtrudeGeometryFilter] " 00036 00037 using namespace osgEarth; 00038 using namespace osgEarth::Features; 00039 using namespace osgEarth::Symbology; 00040 00041 namespace 00042 { 00043 // Calculates the rotation angle of a shape. This conanically applies to 00044 // buildings; it finds the longest edge and compares its angle to the 00045 // x-axis to determine a rotation value. This method is used so we can 00046 // properly rotate textures for rooftop application. 00047 float getApparentRotation( const Geometry* geom ) 00048 { 00049 Segment n; 00050 double maxLen2 = 0.0; 00051 ConstSegmentIterator i( geom, true ); 00052 while( i.hasMore() ) 00053 { 00054 Segment s = i.next(); 00055 double len2 = (s.second - s.first).length2(); 00056 if ( len2 > maxLen2 ) 00057 { 00058 maxLen2 = len2; 00059 n = s; 00060 } 00061 } 00062 00063 const osg::Vec3d& p1 = n.first.x() < n.second.x() ? n.first : n.second; 00064 const osg::Vec3d& p2 = n.first.x() < n.second.x() ? n.second : n.first; 00065 00066 return atan2( p2.x()-p1.x(), p2.y()-p1.y() ); 00067 } 00068 } 00069 00070 //------------------------------------------------------------------------ 00071 00072 ExtrudeGeometryFilter::ExtrudeGeometryFilter() : 00073 _maxAngle_deg ( 5.0 ), 00074 _mergeGeometry ( true ), 00075 _wallAngleThresh_deg( 60.0 ), 00076 _styleDirty ( true ) 00077 { 00078 //NOP 00079 } 00080 00081 void 00082 ExtrudeGeometryFilter::setStyle( const Style& style ) 00083 { 00084 _style = style; 00085 _styleDirty = true; 00086 } 00087 00088 void 00089 ExtrudeGeometryFilter::reset( const FilterContext& context ) 00090 { 00091 _cosWallAngleThresh = cos( _wallAngleThresh_deg ); 00092 _geodes.clear(); 00093 00094 if ( _styleDirty ) 00095 { 00096 const StyleSheet* sheet = context.getSession()->styles(); 00097 00098 _wallSkinSymbol = 0L; 00099 _wallPolygonSymbol = 0L; 00100 _roofSkinSymbol = 0L; 00101 _roofPolygonSymbol = 0L; 00102 _extrusionSymbol = 0L; 00103 _outlineSymbol = 0L; 00104 00105 _extrusionSymbol = _style.get<ExtrusionSymbol>(); 00106 if ( _extrusionSymbol.valid() ) 00107 { 00108 // make a copy of the height expression so we can use it: 00109 if ( _extrusionSymbol->heightExpression().isSet() ) 00110 { 00111 _heightExpr = *_extrusionSymbol->heightExpression(); 00112 } 00113 00114 // account for a "height" value that is relative to ZERO (MSL/HAE) 00115 AltitudeSymbol* alt = _style.get<AltitudeSymbol>(); 00116 if ( alt && !_extrusionSymbol->heightExpression().isSet() ) 00117 { 00118 if (alt->clamping() == AltitudeSymbol::CLAMP_ABSOLUTE || 00119 alt->clamping() == AltitudeSymbol::CLAMP_RELATIVE_TO_TERRAIN ) 00120 { 00121 _heightExpr = NumericExpression( "0-[__max_hat]" ); 00122 } 00123 } 00124 00125 // attempt to extract the wall symbols: 00126 if ( _extrusionSymbol->wallStyleName().isSet() && sheet != 0L ) 00127 { 00128 const Style* wallStyle = sheet->getStyle( *_extrusionSymbol->wallStyleName(), false ); 00129 if ( wallStyle ) 00130 { 00131 _wallSkinSymbol = wallStyle->get<SkinSymbol>(); 00132 _wallPolygonSymbol = wallStyle->get<PolygonSymbol>(); 00133 } 00134 } 00135 00136 // attempt to extract the rooftop symbols: 00137 if ( _extrusionSymbol->roofStyleName().isSet() && sheet != 0L ) 00138 { 00139 const Style* roofStyle = sheet->getStyle( *_extrusionSymbol->roofStyleName(), false ); 00140 if ( roofStyle ) 00141 { 00142 _roofSkinSymbol = roofStyle->get<SkinSymbol>(); 00143 _roofPolygonSymbol = roofStyle->get<PolygonSymbol>(); 00144 } 00145 } 00146 00147 // if there's a line symbol, use it to outline the extruded data. 00148 _outlineSymbol = _style.get<LineSymbol>(); 00149 } 00150 00151 // backup plan for skin symbols: 00152 const SkinSymbol* skin = _style.get<SkinSymbol>(); 00153 if ( skin ) 00154 { 00155 if ( !_wallSkinSymbol.valid() ) 00156 _wallSkinSymbol = skin; 00157 if ( !_roofSkinSymbol.valid() ) 00158 _roofSkinSymbol = skin; 00159 } 00160 00161 // backup plan for poly symbols: 00162 const PolygonSymbol* poly = _style.get<PolygonSymbol>(); 00163 if ( poly ) 00164 { 00165 if ( !_wallPolygonSymbol.valid() ) 00166 _wallPolygonSymbol = poly; 00167 if ( !_roofPolygonSymbol.valid() ) 00168 _roofPolygonSymbol = poly; 00169 } 00170 00171 _styleDirty = false; 00172 } 00173 } 00174 00175 bool 00176 ExtrudeGeometryFilter::extrudeGeometry(const Geometry* input, 00177 double height, 00178 double heightOffset, 00179 bool flatten, 00180 osg::Geometry* walls, 00181 osg::Geometry* roof, 00182 osg::Geometry* base, 00183 osg::Geometry* outline, 00184 const osg::Vec4& wallColor, 00185 const osg::Vec4& roofColor, 00186 const osg::Vec4& outlineColor, 00187 const SkinResource* wallSkin, 00188 const SkinResource* roofSkin, 00189 FilterContext& cx ) 00190 { 00191 //todo: establish reference frame for going to geocentric. This will ultimately 00192 // passed in to the function. 00193 const SpatialReference* srs = cx.extent()->getSRS(); 00194 00195 // whether to convert the final geometry to localized ECEF 00196 bool makeECEF = cx.getSession()->getMapInfo().isGeocentric(); 00197 00198 bool made_geom = false; 00199 00200 double tex_width_m = wallSkin ? *wallSkin->imageWidth() : 1.0; 00201 double tex_height_m = wallSkin ? *wallSkin->imageHeight() : 1.0; 00202 bool tex_repeats_y = wallSkin ? *wallSkin->isTiled() : false; 00203 bool useColor = !wallSkin || wallSkin->texEnvMode() != osg::TexEnv::DECAL; 00204 00205 bool isPolygon = input->getComponentType() == Geometry::TYPE_POLYGON; 00206 00207 unsigned pointCount = input->getTotalPointCount(); 00208 00209 // If we are extruding a polygon, and applying a wall texture, we need an extra 00210 // point in the geometry in order to close the polygon and generate a unique 00211 // texture coordinate for that final point. 00212 bool isSkinnedPolygon = isPolygon && wallSkin != 0L; 00213 00214 // Total number of verts. Add 2 to close a polygon (necessary so the first and last 00215 // points can have unique texture coordinates) 00216 unsigned numWallVerts = 2 * pointCount + (isSkinnedPolygon? (2 * input->getNumGeometries()) : 0); 00217 00218 // create all the OSG geometry components 00219 osg::Vec3Array* verts = new osg::Vec3Array( numWallVerts ); 00220 walls->setVertexArray( verts ); 00221 00222 osg::Vec2Array* wallTexcoords = 0L; 00223 if ( wallSkin ) 00224 { 00225 wallTexcoords = new osg::Vec2Array( numWallVerts ); 00226 walls->setTexCoordArray( 0, wallTexcoords ); 00227 } 00228 00229 if ( useColor ) 00230 { 00231 // per-vertex colors are necessary if we are going to use the MeshConsolidator -gw 00232 osg::Vec4Array* colors = new osg::Vec4Array(); 00233 colors->reserve( numWallVerts ); 00234 colors->assign( numWallVerts, wallColor ); 00235 walls->setColorArray( colors ); 00236 walls->setColorBinding( osg::Geometry::BIND_PER_VERTEX ); 00237 //osg::Vec4Array* colors = new osg::Vec4Array( 1 ); 00238 //(*colors)[0] = wallColor; 00239 //walls->setColorArray( colors ); 00240 //walls->setColorBinding( osg::Geometry::BIND_OVERALL ); 00241 } 00242 00243 // set up rooftop tessellation and texturing, if necessary: 00244 osg::Vec3Array* roofVerts = 0L; 00245 osg::Vec2Array* roofTexcoords = 0L; 00246 float roofRotation = 0.0f; 00247 Bounds roofBounds; 00248 float sinR, cosR; 00249 double roofTexSpanX, roofTexSpanY; 00250 osg::ref_ptr<const SpatialReference> roofProjSRS; 00251 00252 if ( roof ) 00253 { 00254 roofVerts = new osg::Vec3Array( pointCount ); 00255 roof->setVertexArray( roofVerts ); 00256 00257 // per-vertex colors are necessary if we are going to use the MeshConsolidator -gw 00258 osg::Vec4Array* roofColors = new osg::Vec4Array(); 00259 roofColors->reserve( pointCount ); 00260 roofColors->assign( pointCount, roofColor ); 00261 roof->setColorArray( roofColors ); 00262 roof->setColorBinding( osg::Geometry::BIND_PER_VERTEX ); 00263 //osg::Vec4Array* roofColors = new osg::Vec4Array( 1 ); 00264 //(*roofColors)[0] = roofColor; 00265 //roof->setColorArray( roofColors ); 00266 //roof->setColorBinding( osg::Geometry::BIND_OVERALL ); 00267 00268 if ( roofSkin ) 00269 { 00270 roofTexcoords = new osg::Vec2Array( pointCount ); 00271 roof->setTexCoordArray( 0, roofTexcoords ); 00272 00273 // Get the orientation of the geometry. This is a hueristic that will help 00274 // us align the roof skin texture properly. TODO: make this optional? It makes 00275 // sense for buildings and such, but perhaps not for all extruded shapes. 00276 roofRotation = getApparentRotation( input ); 00277 00278 roofBounds = input->getBounds(); 00279 00280 // if our data is lat/long, we need to reproject the geometry and the bounds into a projected 00281 // coordinate system in order to properly generate tex coords. 00282 if ( srs->isGeographic() ) 00283 { 00284 osg::Vec2d geogCenter = roofBounds.center2d(); 00285 roofProjSRS = srs->createUTMFromLongitude( Angular(geogCenter.x()) ); 00286 roofBounds.transform( srs, roofProjSRS.get() ); 00287 osg::ref_ptr<Geometry> projectedInput = input->clone(); 00288 srs->transformPoints( roofProjSRS.get(), projectedInput->asVector() ); 00289 roofRotation = getApparentRotation( projectedInput.get() ); 00290 } 00291 else 00292 { 00293 roofRotation = getApparentRotation( input ); 00294 } 00295 00296 sinR = sin(roofRotation); 00297 cosR = cos(roofRotation); 00298 00299 if ( !roofSkin->isTiled().value() ) 00300 { 00301 //note: doesn't really work 00302 roofTexSpanX = cosR*roofBounds.width() - sinR*roofBounds.height(); 00303 roofTexSpanY = sinR*roofBounds.width() + cosR*roofBounds.height(); 00304 } 00305 else 00306 { 00307 roofTexSpanX = roofSkin->imageWidth().isSet() ? *roofSkin->imageWidth() : roofSkin->imageHeight().isSet() ? *roofSkin->imageHeight() : 10.0; 00308 if ( roofTexSpanX <= 0.0 ) roofTexSpanX = 10.0; 00309 roofTexSpanY = roofSkin->imageHeight().isSet() ? *roofSkin->imageHeight() : roofSkin->imageWidth().isSet() ? *roofSkin->imageWidth() : 10.0; 00310 if ( roofTexSpanY <= 0.0 ) roofTexSpanY = 10.0; 00311 } 00312 } 00313 } 00314 00315 osg::Vec3Array* baseVerts = NULL; 00316 if ( base ) 00317 { 00318 baseVerts = new osg::Vec3Array( pointCount ); 00319 base->setVertexArray( baseVerts ); 00320 } 00321 00322 osg::Vec3Array* outlineVerts = 0L; 00323 osg::Vec3Array* outlineNormals = 0L; 00324 if ( outline ) 00325 { 00326 outlineVerts = new osg::Vec3Array( numWallVerts ); 00327 outline->setVertexArray( outlineVerts ); 00328 00329 osg::Vec4Array* outlineColors = new osg::Vec4Array(); 00330 outlineColors->reserve( numWallVerts ); 00331 outlineColors->assign( numWallVerts, outlineColor ); 00332 outline->setColorArray( outlineColors ); 00333 outline->setColorBinding( osg::Geometry::BIND_PER_VERTEX ); 00334 00335 // cop out, just point all the outline normals up. fix this later. 00336 outlineNormals = new osg::Vec3Array(); 00337 outlineNormals->reserve( numWallVerts ); 00338 outlineNormals->assign( numWallVerts, osg::Vec3(0,0,1) ); 00339 outline->setNormalArray( outlineNormals ); 00340 } 00341 00342 unsigned wallVertPtr = 0; 00343 unsigned roofVertPtr = 0; 00344 unsigned baseVertPtr = 0; 00345 00346 double targetLen = -DBL_MAX; 00347 osg::Vec3d minLoc(DBL_MAX, DBL_MAX, DBL_MAX); 00348 double minLoc_len = DBL_MAX; 00349 osg::Vec3d maxLoc(0,0,0); 00350 double maxLoc_len = 0; 00351 00352 // Initial pass over the geometry does two things: 00353 // 1: Calculate the minimum Z across all parts. 00354 // 2: Establish a "target length" for extrusion 00355 00356 double absHeight = fabs(height); 00357 00358 ConstGeometryIterator zfinder( input ); 00359 while( zfinder.hasMore() ) 00360 { 00361 const Geometry* geom = zfinder.next(); 00362 for( Geometry::const_iterator m = geom->begin(); m != geom->end(); ++m ) 00363 { 00364 osg::Vec3d m_point = *m; 00365 00366 if ( m_point.z() + absHeight > targetLen ) 00367 targetLen = m_point.z() + absHeight; 00368 00369 if (m_point.z() < minLoc.z()) 00370 minLoc = m_point; 00371 00372 if (m_point.z() > maxLoc.z()) 00373 maxLoc = m_point; 00374 } 00375 } 00376 00377 // apply the height offsets 00378 height -= heightOffset; 00379 targetLen -= heightOffset; 00380 00381 // now generate the extruded geometry. 00382 ConstGeometryIterator iter( input ); 00383 while( iter.hasMore() ) 00384 { 00385 const Geometry* part = iter.next(); 00386 00387 double tex_height_m_adj = tex_height_m; 00388 00389 unsigned wallPartPtr = wallVertPtr; 00390 unsigned roofPartPtr = roofVertPtr; 00391 unsigned basePartPtr = baseVertPtr; 00392 double partLen = 0.0; 00393 double maxHeight = 0.0; 00394 00395 maxHeight = targetLen - minLoc.z(); 00396 00397 // Adjust the texture height so it is a multiple of the maximum height 00398 double div = osg::round(maxHeight / tex_height_m); 00399 if (div == 0) div = 1; //Prevent divide by zero 00400 tex_height_m_adj = maxHeight / div; 00401 00402 osg::DrawElementsUInt* idx = new osg::DrawElementsUInt( GL_TRIANGLES ); 00403 00404 for( Geometry::const_iterator m = part->begin(); m != part->end(); ++m ) 00405 { 00406 osg::Vec3d basePt = *m; 00407 osg::Vec3d roofPt; 00408 00409 if ( height >= 0 ) 00410 { 00411 if ( flatten ) 00412 roofPt.set( basePt.x(), basePt.y(), targetLen ); 00413 else 00414 roofPt.set( basePt.x(), basePt.y(), basePt.z() + height ); 00415 } 00416 else // height < 0 00417 { 00418 roofPt = *m; 00419 basePt.z() += height; 00420 } 00421 00422 // add to the approprate vertex lists: 00423 int p = wallVertPtr; 00424 00425 // figure out the rooftop texture coordinates before doing any 00426 // transformations: 00427 if ( roofSkin ) 00428 { 00429 double xr, yr; 00430 00431 if ( srs->isGeographic() ) 00432 { 00433 osg::Vec3d projRoofPt; 00434 srs->transform( roofPt, roofProjSRS.get(), projRoofPt ); 00435 xr = (projRoofPt.x() - roofBounds.xMin()); 00436 yr = (projRoofPt.y() - roofBounds.yMin()); 00437 } 00438 else 00439 { 00440 xr = (roofPt.x() - roofBounds.xMin()); 00441 yr = (roofPt.y() - roofBounds.yMin()); 00442 } 00443 00444 float u = (cosR*xr - sinR*yr) / roofTexSpanX; 00445 float v = (sinR*xr + cosR*yr) / roofTexSpanY; 00446 00447 (*roofTexcoords)[roofVertPtr].set( u, v ); 00448 } 00449 00450 if ( makeECEF ) 00451 { 00452 ECEF::transformAndLocalize( basePt, basePt, srs, _world2local ); 00453 ECEF::transformAndLocalize( roofPt, roofPt, srs, _world2local ); 00454 } 00455 00456 if ( base ) 00457 (*baseVerts)[baseVertPtr] = basePt; 00458 if ( roof ) 00459 (*roofVerts)[roofVertPtr] = roofPt; 00460 00461 baseVertPtr++; 00462 roofVertPtr++; 00463 00464 (*verts)[p] = roofPt; 00465 (*verts)[p+1] = basePt; 00466 00467 if ( outline ) 00468 { 00469 (*outlineVerts)[p] = roofPt; 00470 (*outlineVerts)[p+1] = basePt; 00471 } 00472 00473 partLen += wallVertPtr > wallPartPtr ? ((*verts)[p] - (*verts)[p-2]).length() : 0.0; 00474 double h = tex_repeats_y ? -((*verts)[p] - (*verts)[p+1]).length() : -tex_height_m_adj; 00475 00476 if ( wallSkin ) 00477 { 00478 (*wallTexcoords)[p].set( partLen/tex_width_m, 0.0f ); 00479 (*wallTexcoords)[p+1].set( partLen/tex_width_m, h/tex_height_m_adj ); 00480 } 00481 00482 // form the 2 triangles 00483 if ( (m+1) == part->end() ) 00484 { 00485 if ( isPolygon ) 00486 { 00487 // end of the wall; loop around to close it off. 00488 if ( isSkinnedPolygon ) 00489 { 00490 // if we requested an extra geometry point, that means we are generating 00491 // a polygon-closing line so we can have a unique texcoord for it. 00492 idx->push_back(wallVertPtr); 00493 idx->push_back(wallVertPtr+1); 00494 idx->push_back(wallVertPtr+2); 00495 00496 idx->push_back(wallVertPtr+1); 00497 idx->push_back(wallVertPtr+3); 00498 idx->push_back(wallVertPtr+2); 00499 00500 (*verts)[p+2] = (*verts)[wallPartPtr]; 00501 (*verts)[p+3] = (*verts)[wallPartPtr+1]; 00502 00503 if ( wallSkin ) 00504 { 00505 partLen += ((*verts)[p+2] - (*verts)[p]).length(); 00506 double h = tex_repeats_y ? -((*verts)[p+2] - (*verts)[p+3]).length() : -tex_height_m_adj; 00507 (*wallTexcoords)[p+2].set( partLen/tex_width_m, 0.0f ); 00508 (*wallTexcoords)[p+3].set( partLen/tex_width_m, h/tex_height_m_adj ); 00509 } 00510 00511 wallVertPtr += 2; 00512 } 00513 else 00514 { 00515 // either not a poly, or no wall skin, so we can share the polygon-closing 00516 // loop point. 00517 idx->push_back(wallVertPtr); 00518 idx->push_back(wallVertPtr+1); 00519 idx->push_back(wallPartPtr); 00520 00521 idx->push_back(wallVertPtr+1); 00522 idx->push_back(wallPartPtr+1); 00523 idx->push_back(wallPartPtr); 00524 } 00525 } 00526 else 00527 { 00528 //nop - no elements required at the end of a line 00529 } 00530 } 00531 else 00532 { 00533 idx->push_back(wallVertPtr); 00534 idx->push_back(wallVertPtr+1); 00535 idx->push_back(wallVertPtr+2); 00536 00537 idx->push_back(wallVertPtr+1); 00538 idx->push_back(wallVertPtr+3); 00539 idx->push_back(wallVertPtr+2); 00540 } 00541 00542 wallVertPtr += 2; 00543 made_geom = true; 00544 } 00545 00546 walls->addPrimitiveSet( idx ); 00547 00548 if ( roof ) 00549 { 00550 roof->addPrimitiveSet( new osg::DrawArrays( 00551 osg::PrimitiveSet::LINE_LOOP, 00552 roofPartPtr, roofVertPtr - roofPartPtr ) ); 00553 } 00554 00555 if ( base ) 00556 { 00557 // reverse the base verts: 00558 int len = baseVertPtr - basePartPtr; 00559 for( int i=basePartPtr; i<len/2; i++ ) 00560 std::swap( (*baseVerts)[i], (*baseVerts)[basePartPtr+(len-1)-i] ); 00561 00562 base->addPrimitiveSet( new osg::DrawArrays( 00563 osg::PrimitiveSet::LINE_LOOP, 00564 basePartPtr, baseVertPtr - basePartPtr ) ); 00565 } 00566 00567 if ( outline ) 00568 { 00569 unsigned len = baseVertPtr - basePartPtr; 00570 00571 GLenum roofLineMode = isPolygon ? GL_LINE_LOOP : GL_LINE_STRIP; 00572 osg::DrawElementsUInt* roofLine = new osg::DrawElementsUInt( roofLineMode ); 00573 roofLine->reserveElements( len ); 00574 for( unsigned i=0; i<len; ++i ) 00575 roofLine->addElement( basePartPtr + i*2 ); 00576 outline->addPrimitiveSet( roofLine ); 00577 00578 osg::DrawElementsUShort* wallLines = new osg::DrawElementsUShort( GL_LINES ); 00579 wallLines->reserve( len*2 ); 00580 for( unsigned i=0; i<len; ++i ) 00581 { 00582 wallLines->push_back( basePartPtr + i*2 ); 00583 wallLines->push_back( basePartPtr + i*2 + 1 ); 00584 } 00585 outline->addPrimitiveSet( wallLines ); 00586 } 00587 } 00588 00589 return made_geom; 00590 } 00591 00592 void 00593 ExtrudeGeometryFilter::addDrawable( osg::Drawable* drawable, osg::StateSet* stateSet, const std::string& name ) 00594 { 00595 // find the geode for the active stateset, creating a new one if necessary. NULL is a 00596 // valid key as well. 00597 osg::Geode* geode = _geodes[stateSet].get(); 00598 if ( !geode ) 00599 { 00600 geode = new osg::Geode(); 00601 geode->setStateSet( stateSet ); 00602 _geodes[stateSet] = geode; 00603 } 00604 00605 geode->addDrawable( drawable ); 00606 00607 if ( !name.empty() ) 00608 { 00609 drawable->setName( name ); 00610 } 00611 } 00612 00613 bool 00614 ExtrudeGeometryFilter::process( FeatureList& features, FilterContext& context ) 00615 { 00616 // seed our random number generators 00617 Random wallSkinPRNG( _wallSkinSymbol.valid()? *_wallSkinSymbol->randomSeed() : 0, Random::METHOD_FAST ); 00618 Random roofSkinPRNG( _roofSkinSymbol.valid()? *_roofSkinSymbol->randomSeed() : 0, Random::METHOD_FAST ); 00619 00620 for( FeatureList::iterator f = features.begin(); f != features.end(); ++f ) 00621 { 00622 Feature* input = f->get(); 00623 00624 GeometryIterator iter( input->getGeometry(), false ); 00625 while( iter.hasMore() ) 00626 { 00627 Geometry* part = iter.next(); 00628 00629 osg::ref_ptr<osg::Geometry> walls = new osg::Geometry(); 00630 //walls->setUseVertexBufferObjects(true); 00631 00632 osg::ref_ptr<osg::Geometry> rooflines = 0L; 00633 osg::ref_ptr<osg::Geometry> outlines = 0L; 00634 00635 if ( part->getType() == Geometry::TYPE_POLYGON ) 00636 { 00637 rooflines = new osg::Geometry(); 00638 //rooflines->setUseVertexBufferObjects(true); 00639 00640 // prep the shapes by making sure all polys are open: 00641 static_cast<Polygon*>(part)->open(); 00642 } 00643 00644 // fire up the outline geometry if we have a line symbol. 00645 if ( _outlineSymbol != 0L ) 00646 { 00647 outlines = new osg::Geometry(); 00648 } 00649 00650 // calculate the extrusion height: 00651 float height; 00652 00653 if ( _heightCallback.valid() ) 00654 { 00655 height = _heightCallback->operator()(input, context); 00656 } 00657 else if ( _heightExpr.isSet() ) 00658 { 00659 height = input->eval( _heightExpr.mutable_value() ); 00660 } 00661 else 00662 { 00663 height = *_extrusionSymbol->height(); 00664 } 00665 00666 // calculate the height offset from the base: 00667 float offset = 0.0; 00668 if ( _heightOffsetExpr.isSet() ) 00669 { 00670 offset = input->eval( _heightOffsetExpr.mutable_value() ); 00671 } 00672 00673 osg::StateSet* wallStateSet = 0L; 00674 osg::StateSet* roofStateSet = 0L; 00675 00676 // calculate the wall texturing: 00677 SkinResource* wallSkin = 0L; 00678 if ( _wallSkinSymbol.valid() ) 00679 { 00680 if ( _wallResLib.valid() ) 00681 { 00682 SkinSymbol querySymbol( *_wallSkinSymbol.get() ); 00683 querySymbol.objectHeight() = fabs(height) - offset; 00684 wallSkin = _wallResLib->getSkin( &querySymbol, wallSkinPRNG ); 00685 } 00686 00687 else 00688 { 00689 //TODO: simple single texture? 00690 } 00691 } 00692 00693 // calculate the rooftop texture: 00694 SkinResource* roofSkin = 0L; 00695 if ( _roofSkinSymbol.valid() ) 00696 { 00697 if ( _roofResLib.valid() ) 00698 { 00699 SkinSymbol querySymbol( *_roofSkinSymbol.get() ); 00700 roofSkin = _roofResLib->getSkin( &querySymbol, roofSkinPRNG ); 00701 } 00702 00703 else 00704 { 00705 //TODO: simple single texture? 00706 } 00707 } 00708 00709 // calculate the colors: 00710 osg::Vec4f wallColor(1,1,1,1), roofColor(1,1,1,1), outlineColor(1,1,1,1); 00711 00712 if ( _wallPolygonSymbol.valid() ) 00713 { 00714 wallColor = _wallPolygonSymbol->fill()->color(); 00715 } 00716 if ( _roofPolygonSymbol.valid() ) 00717 { 00718 roofColor = _roofPolygonSymbol->fill()->color(); 00719 } 00720 if ( _outlineSymbol.valid() ) 00721 { 00722 outlineColor = _outlineSymbol->stroke()->color(); 00723 } 00724 00725 // Create the extruded geometry! 00726 if (extrudeGeometry( 00727 part, height, offset, 00728 *_extrusionSymbol->flatten(), 00729 walls.get(), rooflines.get(), 0L, outlines.get(), 00730 wallColor, roofColor, outlineColor, 00731 wallSkin, roofSkin, 00732 context ) ) 00733 { 00734 if ( wallSkin ) 00735 { 00736 wallStateSet = context.resourceCache()->getStateSet( wallSkin ); 00737 } 00738 00739 // generate per-vertex normals, altering the geometry as necessary to avoid 00740 // smoothing around sharp corners 00741 #if OSG_MIN_VERSION_REQUIRED(2,9,9) 00742 //Crease angle threshold wasn't added until 00743 osgUtil::SmoothingVisitor::smooth( 00744 *walls.get(), 00745 osg::DegreesToRadians(_wallAngleThresh_deg) ); 00746 #else 00747 osgUtil::SmoothingVisitor::smooth(*walls.get()); 00748 #endif 00749 00750 // tessellate and add the roofs if necessary: 00751 if ( rooflines.valid() ) 00752 { 00753 osgUtil::Tessellator tess; 00754 tess.setTessellationType( osgUtil::Tessellator::TESS_TYPE_GEOMETRY ); 00755 tess.setWindingType( osgUtil::Tessellator::TESS_WINDING_ODD ); //POSITIVE ); 00756 tess.retessellatePolygons( *(rooflines.get()) ); 00757 00758 // generate default normals (no crease angle necessary; they are all pointing up) 00759 // TODO do this manually; probably faster 00760 osgUtil::SmoothingVisitor::smooth( *rooflines.get() ); 00761 00762 // texture the rooflines if necessary 00763 //applyOverlayTexturing( rooflines.get(), input, env ); 00764 00765 // mark this geometry as DYNAMIC because otherwise the OSG optimizer will destroy it. 00766 // TODO: why?? 00767 rooflines->setDataVariance( osg::Object::DYNAMIC ); 00768 00769 if ( roofSkin ) 00770 { 00771 roofStateSet = context.resourceCache()->getStateSet( roofSkin ); 00772 } 00773 } 00774 00775 std::string name; 00776 if ( !_featureNameExpr.empty() ) 00777 name = input->eval( _featureNameExpr ); 00778 00779 //MeshConsolidator::run( *walls.get() ); 00780 addDrawable( walls.get(), wallStateSet, name ); 00781 00782 if ( rooflines.valid() ) 00783 { 00784 //MeshConsolidator::run( *rooflines.get() ); 00785 addDrawable( rooflines.get(), roofStateSet, name ); 00786 } 00787 00788 if ( outlines.valid() ) 00789 { 00790 addDrawable( outlines.get(), 0L, name ); 00791 } 00792 } 00793 } 00794 } 00795 00796 return true; 00797 } 00798 00799 osg::Node* 00800 ExtrudeGeometryFilter::push( FeatureList& input, FilterContext& context ) 00801 { 00802 reset( context ); 00803 00804 // minimally, we require an extrusion symbol. 00805 if ( !_extrusionSymbol.valid() ) 00806 { 00807 OE_WARN << LC << "Missing required extrusion symbolology; geometry will be empty" << std::endl; 00808 return new osg::Group(); 00809 } 00810 00811 // establish the active resource library, if applicable. 00812 _wallResLib = 0L; 00813 _roofResLib = 0L; 00814 00815 const StyleSheet* sheet = context.getSession()->styles(); 00816 00817 if ( sheet != 0L ) 00818 { 00819 if ( _wallSkinSymbol.valid() && _wallSkinSymbol->libraryName().isSet() ) 00820 { 00821 _wallResLib = sheet->getResourceLibrary( *_wallSkinSymbol->libraryName() ); 00822 if ( !_wallResLib.valid() ) 00823 { 00824 OE_WARN << LC << "Unable to load resource library '" << *_wallSkinSymbol->libraryName() << "'" 00825 << "; wall geometry will not be textured." << std::endl; 00826 } 00827 } 00828 00829 if ( _roofSkinSymbol.valid() && _roofSkinSymbol->libraryName().isSet() ) 00830 { 00831 _roofResLib = sheet->getResourceLibrary( *_roofSkinSymbol->libraryName() ); 00832 if ( !_roofResLib.valid() ) 00833 { 00834 OE_WARN << LC << "Unable to load resource library '" << *_roofSkinSymbol->libraryName() << "'" 00835 << "; roof geometry will not be textured." << std::endl; 00836 } 00837 } 00838 } 00839 00840 // calculate the localization matrices (_local2world and _world2local) 00841 computeLocalizers( context ); 00842 00843 // push all the features through the extruder. 00844 bool ok = process( input, context ); 00845 00846 // convert everything to triangles and combine drawables. 00847 if ( _mergeGeometry == true && _featureNameExpr.empty() ) 00848 { 00849 for( SortedGeodeMap::iterator i = _geodes.begin(); i != _geodes.end(); ++i ) 00850 { 00851 MeshConsolidator::run( *i->second.get() ); 00852 } 00853 } 00854 00855 // parent geometry with a delocalizer (if necessary) 00856 osg::Group* group = createDelocalizeGroup(); 00857 00858 // combines geometries where the statesets are the same. 00859 for( SortedGeodeMap::iterator i = _geodes.begin(); i != _geodes.end(); ++i ) 00860 { 00861 group->addChild( i->second.get() ); 00862 } 00863 _geodes.clear(); 00864 00865 // if we drew outlines, apply a poly offset too. 00866 if ( _outlineSymbol.valid() ) 00867 { 00868 osg::StateSet* groupStateSet = group->getOrCreateStateSet(); 00869 groupStateSet->setAttributeAndModes( new osg::PolygonOffset(1,1), 1 ); 00870 if ( _outlineSymbol->stroke()->width().isSet() ) 00871 groupStateSet->setAttributeAndModes( new osg::LineWidth(*_outlineSymbol->stroke()->width()), 1 ); 00872 } 00873 00874 OE_DEBUG << LC << "Sorted geometry into " << group->getNumChildren() << " groups" << std::endl; 00875 00876 //TODO 00877 // running this after the MC reduces the primitive set count by a huge amount, but I 00878 // have not figured out why yet. 00879 if ( _mergeGeometry == true ) 00880 { 00881 osgUtil::Optimizer o; 00882 o.optimize( group, osgUtil::Optimizer::MERGE_GEOMETRY ); 00883 } 00884 00885 return group; 00886 }