osgEarth 2.1.1
|
00001 /* -*-c++-*- */ 00002 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph 00003 * Copyright 2008-2009 Pelican Ventures, Inc. 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/StencilVolumeNode> 00020 #include <osg/Stencil> 00021 #include <osg/StencilTwoSided> 00022 #include <osg/Depth> 00023 #include <osg/Drawable> 00024 #include <osg/CopyOp> 00025 #include <osg/CullFace> 00026 #include <osg/MatrixTransform> 00027 #include <osg/Projection> 00028 #include <osg/GLExtensions> 00029 #include <osg/Geode> 00030 #include <osg/Notify> 00031 #include <osgUtil/Tessellator> 00032 #include <algorithm> 00033 00034 #define ON_AND_PROTECTED osg::StateAttribute::ON | osg::StateAttribute::PROTECTED 00035 #define OFF_AND_PROTECTED osg::StateAttribute::OFF | osg::StateAttribute::PROTECTED 00036 00037 using namespace osgEarth; 00038 using namespace osgEarth::Features; 00039 00040 00041 StencilVolumeNode::StencilVolumeNode( bool preRenderChildrenToDepthBuffer, bool inverted ) : 00042 osgEarth::MaskNode(), 00043 _preRenderChildrenToDepthBuffer( preRenderChildrenToDepthBuffer ), 00044 _inverted( inverted ), 00045 _stencilGroup1( 0L ), 00046 _stencilGroup2( 0L ), 00047 _depthPass( 0L ), 00048 _renderPass( 0L ) 00049 { 00050 init(); 00051 } 00052 00053 StencilVolumeNode::StencilVolumeNode(const StencilVolumeNode& rhs, 00054 const osg::CopyOp& op) : 00055 osgEarth::MaskNode( rhs, op ), 00056 _root( rhs._root.get() ), 00057 _stencilGroup1( rhs._stencilGroup1 ), 00058 _stencilGroup2( rhs._stencilGroup2 ), 00059 _depthPass( rhs._depthPass ), 00060 _renderPass( rhs._renderPass ), 00061 _inverted( rhs._inverted ), 00062 _preRenderChildrenToDepthBuffer( rhs._preRenderChildrenToDepthBuffer ) 00063 { 00064 //nop 00065 } 00066 00067 int 00068 StencilVolumeNode::setBaseRenderBin( int bin ) 00069 { 00070 if ( _depthPass ) 00071 { 00072 _depthPass->getOrCreateStateSet()->setRenderBinDetails( bin++, "RenderBin" ); 00073 } 00074 if ( _stencilGroup1 ) 00075 { 00076 _stencilGroup1->getOrCreateStateSet()->setRenderBinDetails( bin++, "RenderBin" ); 00077 } 00078 if ( _stencilGroup2 ) 00079 { 00080 _stencilGroup2->getOrCreateStateSet()->setRenderBinDetails( bin++, "RenderBin" ); 00081 } 00082 if ( _renderPass ) 00083 { 00084 _renderPass->getOrCreateStateSet()->setRenderBinDetails( bin++, "RenderBin" ); 00085 } 00086 return bin; 00087 } 00088 00089 void 00090 StencilVolumeNode::addVolumes( osg::Node* node ) 00091 { 00092 if ( _stencilGroup1 ) 00093 _stencilGroup1->addChild( node ); 00094 if ( _stencilGroup2 ) 00095 _stencilGroup2->addChild( node ); 00096 } 00097 00100 bool 00101 StencilVolumeNode::addChild( Node *child ) { 00102 if ( !child ) return false; 00103 dirtyBound(); 00104 if ( _depthPass ) _depthPass->addChild( child ); 00105 return _renderPass->addChild( child ); 00106 } 00107 bool 00108 StencilVolumeNode::insertChild( unsigned int index, Node *child ) { 00109 if ( !child ) return false; 00110 dirtyBound(); 00111 if ( _depthPass ) _depthPass->insertChild( index, child ); 00112 return _renderPass->insertChild( index, child ); 00113 } 00114 bool 00115 StencilVolumeNode::removeChildren(unsigned int pos,unsigned int numChildrenToRemove) { 00116 dirtyBound(); 00117 if ( _depthPass ) _depthPass->removeChildren( pos, numChildrenToRemove ); 00118 return _renderPass->removeChildren( pos, numChildrenToRemove ); 00119 } 00120 bool 00121 StencilVolumeNode::replaceChild( Node *origChild, Node* newChild ) { 00122 dirtyBound(); 00123 if ( _depthPass ) _depthPass->replaceChild( origChild, newChild ); 00124 return _renderPass->replaceChild( origChild, newChild ); 00125 } 00126 bool 00127 StencilVolumeNode::setChild( unsigned int i, Node* node ) { 00128 dirtyBound(); 00129 if ( _depthPass ) _depthPass->setChild( i, node ); 00130 return _renderPass->setChild( i, node ); 00131 } 00132 00133 osg::BoundingSphere 00134 StencilVolumeNode::computeBound() const { 00135 if ( _depthPass ) return _depthPass->computeBound(); 00136 else return _renderPass->computeBound(); 00137 } 00138 00139 00140 void 00141 StencilVolumeNode::traverse( osg::NodeVisitor& nv ) 00142 { 00143 _root->accept( nv ); 00144 } 00145 00146 void 00147 StencilVolumeNode::init() 00148 { 00149 _root = new osg::Group(); 00150 int baseBin = 100; 00151 00152 // First, if we need to render the children to the depth buffer, create a depth 00153 // buffer only pass. This is necessary if we are masking out terrain, for example. We have 00154 // to populate the depth buffer before this algoritm can work. 00155 if ( _preRenderChildrenToDepthBuffer ) 00156 { 00157 _depthPass = new osg::Group(); 00158 _depthPass->setName( "StencilVolumeNode::depth_pass" ); 00159 osg::StateSet* ss = _depthPass->getOrCreateStateSet(); 00160 ss->setAttributeAndModes(new osg::ColorMask(false,false,false,false), ON_AND_PROTECTED); 00161 //ss->setAttributeAndModes( new osg::CullFace(osg::CullFace::BACK), ON_AND_PROTECTED); 00162 ss->setAttributeAndModes( new osg::Depth(osg::Depth::LESS), ON_AND_PROTECTED); 00163 ss->setRenderBinDetails( baseBin++, "RenderBin" ); 00164 00165 _root->addChild( _depthPass ); 00166 } 00167 00168 // Create the stenciled geometry pass: 00169 00170 // For now we are hard-coding these to what are hopefully "safe" values. 00171 // In the future we hope to rewrite this as a custom StencilVolumeNode that 00172 // will automatically detect extension availability at draw time and choose 00173 // the optimal GL rendering path. 00174 static bool s_EXT_stencil_wrap = true; //osg::isGLExtensionSupported(0, "GL_EXT_stencil_wrap"); 00175 static bool s_EXT_stencil_two_side = false; //osg::isGLExtensionSupported(0, "GL_EXT_stencil_two_side"); 00176 00177 00178 // zFail=true if more compute intensive, but lets you get inside the volume. 00179 // Again, a custom node will give us a better opportunity to choose between zFail and zPass based on 00180 // the eye location (you only need zFail if you camera is inside the volume). 00181 bool zFail = true; 00182 00183 OE_INFO << "Stencil buffer wrap = " << s_EXT_stencil_wrap << std::endl; 00184 00185 if ( s_EXT_stencil_two_side ) 00186 { 00187 OE_INFO << "Two-sided stenciling" << std::endl; 00188 00189 osg::StencilTwoSided::Operation incrOp = s_EXT_stencil_wrap ? osg::StencilTwoSided::INCR_WRAP : osg::StencilTwoSided::INCR; 00190 osg::StencilTwoSided::Operation decrOp = s_EXT_stencil_wrap ? osg::StencilTwoSided::DECR_WRAP : osg::StencilTwoSided::DECR; 00191 _stencilGroup1 = new osg::Group(); 00192 //osg::Group* stencil_group = new osg::Group(); 00193 osg::StateSet* ss = _stencilGroup1->getOrCreateStateSet(); 00194 ss->setRenderBinDetails( baseBin++, "RenderBin" ); 00195 00196 if ( zFail ) 00197 { 00198 osg::StencilTwoSided* stencil = new osg::StencilTwoSided(); 00199 stencil->setFunction(osg::StencilTwoSided::BACK, osg::StencilTwoSided::ALWAYS, 1, ~0u); 00200 stencil->setOperation(osg::StencilTwoSided::BACK, osg::StencilTwoSided::KEEP, incrOp, osg::StencilTwoSided::KEEP); 00201 00202 stencil->setFunction(osg::StencilTwoSided::FRONT, osg::StencilTwoSided::ALWAYS, 1, ~0u); 00203 stencil->setOperation(osg::StencilTwoSided::FRONT, osg::StencilTwoSided::KEEP, decrOp, osg::StencilTwoSided::KEEP); 00204 ss->setAttributeAndModes( stencil, ON_AND_PROTECTED ); 00205 } 00206 else 00207 { 00208 osg::StencilTwoSided* stencil = new osg::StencilTwoSided(); 00209 stencil->setFunction(osg::StencilTwoSided::FRONT, osg::StencilTwoSided::ALWAYS, 1, ~0u); 00210 stencil->setOperation(osg::StencilTwoSided::FRONT, osg::StencilTwoSided::KEEP, osg::StencilTwoSided::KEEP, incrOp); 00211 00212 stencil->setFunction(osg::StencilTwoSided::BACK, osg::StencilTwoSided::ALWAYS, 1, ~0u); 00213 stencil->setOperation(osg::StencilTwoSided::BACK, osg::StencilTwoSided::KEEP, osg::StencilTwoSided::KEEP, decrOp); 00214 ss->setAttributeAndModes( stencil, ON_AND_PROTECTED ); 00215 } 00216 00217 ss->setAttributeAndModes(new osg::ColorMask(false,false,false,false), ON_AND_PROTECTED); 00218 ss->setAttributeAndModes(new osg::Depth(osg::Depth::LESS,0,1,false), ON_AND_PROTECTED); 00219 00220 _root->addChild( _stencilGroup1 ); 00221 } 00222 else 00223 { 00224 OE_INFO << "One-sided stenciling" << std::endl; 00225 00226 if ( !zFail ) // Z-Pass 00227 { 00228 _stencilGroup1 = new osg::Group(); 00229 osg::StateSet* front_ss = _stencilGroup1->getOrCreateStateSet(); 00230 front_ss->setRenderBinDetails( baseBin++, "RenderBin" ); 00231 00232 // incrementing stencil op for front faces: 00233 osg::Stencil* front_stencil = new osg::Stencil(); 00234 front_stencil->setFunction( osg::Stencil::ALWAYS, 1, ~0u ); 00235 osg::Stencil::Operation incrOp = s_EXT_stencil_wrap ? osg::Stencil::INCR_WRAP : osg::Stencil::INCR; 00236 front_stencil->setOperation( osg::Stencil::KEEP, osg::Stencil::KEEP, incrOp ); 00237 front_ss->setAttributeAndModes( front_stencil, ON_AND_PROTECTED ); 00238 00239 front_ss->setAttributeAndModes( new osg::ColorMask(false,false,false,false), ON_AND_PROTECTED); 00240 front_ss->setAttributeAndModes( new osg::CullFace(osg::CullFace::BACK), ON_AND_PROTECTED); 00241 front_ss->setAttributeAndModes( new osg::Depth(osg::Depth::LESS,0,1,false), ON_AND_PROTECTED); 00242 00243 _root->addChild( _stencilGroup1 ); 00244 00245 // decrementing stencil op for back faces: 00246 _stencilGroup2 = new osg::Group(); 00247 osg::StateSet* back_ss = _stencilGroup2->getOrCreateStateSet(); 00248 back_ss->setRenderBinDetails( baseBin++, "RenderBin" ); 00249 00250 osg::Stencil* back_stencil = new osg::Stencil(); 00251 back_stencil->setFunction( osg::Stencil::ALWAYS, 1, ~0u ); 00252 osg::Stencil::Operation decrOp = s_EXT_stencil_wrap ? osg::Stencil::DECR_WRAP : osg::Stencil::DECR; 00253 back_stencil->setOperation( osg::Stencil::KEEP, osg::Stencil::KEEP, decrOp ); 00254 back_ss->setAttributeAndModes( back_stencil, ON_AND_PROTECTED ); 00255 00256 back_ss->setAttributeAndModes( new osg::ColorMask(false,false,false,false), ON_AND_PROTECTED); 00257 back_ss->setAttributeAndModes( new osg::CullFace(osg::CullFace::FRONT), ON_AND_PROTECTED); 00258 back_ss->setAttributeAndModes( new osg::Depth(osg::Depth::LESS,0,1,false), ON_AND_PROTECTED); 00259 00260 _root->addChild( _stencilGroup2 ); 00261 } 00262 else 00263 { 00264 // incrementing stencil op for back faces: 00265 { 00266 _stencilGroup1 = new osg::Group(); 00267 osg::StateSet* front_ss = _stencilGroup1->getOrCreateStateSet(); 00268 front_ss->setRenderBinDetails( baseBin++, "RenderBin" ); 00269 00270 osg::Stencil* front_stencil = new osg::Stencil(); 00271 front_stencil->setFunction( osg::Stencil::ALWAYS ); //, 1, ~0u ); 00272 osg::Stencil::Operation incrOp = s_EXT_stencil_wrap ? osg::Stencil::INCR_WRAP : osg::Stencil::INCR; 00273 front_stencil->setOperation( osg::Stencil::KEEP, incrOp, osg::Stencil::KEEP ); 00274 front_ss->setAttributeAndModes( front_stencil, ON_AND_PROTECTED ); 00275 00276 front_ss->setAttributeAndModes( new osg::ColorMask(false,false,false,false), ON_AND_PROTECTED); 00277 front_ss->setAttributeAndModes( new osg::CullFace(osg::CullFace::FRONT), ON_AND_PROTECTED); 00278 front_ss->setAttributeAndModes( new osg::Depth(osg::Depth::LESS,0,1,false), ON_AND_PROTECTED); 00279 00280 _root->addChild( _stencilGroup1 ); 00281 } 00282 00283 // decrementing stencil buf for front faces 00284 { 00285 _stencilGroup2 = new osg::Group(); 00286 osg::StateSet* back_ss = _stencilGroup2->getOrCreateStateSet(); 00287 back_ss->setRenderBinDetails( baseBin++, "RenderBin" ); 00288 00289 osg::Stencil* back_stencil = new osg::Stencil(); 00290 back_stencil->setFunction( osg::Stencil::ALWAYS ); //, 1, ~0u ); 00291 osg::Stencil::Operation decrOp = s_EXT_stencil_wrap ? osg::Stencil::DECR_WRAP : osg::Stencil::DECR; 00292 back_stencil->setOperation( osg::Stencil::KEEP, decrOp, osg::Stencil::KEEP ); 00293 back_ss->setAttributeAndModes( back_stencil, ON_AND_PROTECTED ); 00294 00295 back_ss->setAttributeAndModes( new osg::ColorMask(false,false,false,false), ON_AND_PROTECTED); 00296 back_ss->setAttributeAndModes( new osg::CullFace(osg::CullFace::BACK), ON_AND_PROTECTED); 00297 back_ss->setAttributeAndModes( new osg::Depth(osg::Depth::LESS,0,1,false), ON_AND_PROTECTED); 00298 00299 _root->addChild( _stencilGroup2 ); 00300 } 00301 } 00302 } 00303 00304 00305 // NOW build the full-screen quad mask that will paint the volume: 00306 00307 _renderPass = new osg::Group(); 00308 _renderPass->setName( "StencilVolumeNode::render_pass" ); 00309 00310 osg::Stencil* quad_stencil = new osg::Stencil(); 00311 osg::Stencil::Function func = _inverted ? osg::Stencil::EQUAL : osg::Stencil::NOTEQUAL; 00312 quad_stencil->setFunction( func, 128, (unsigned int)~0 ); 00313 // this will conveniently re-clear the stencil buffer in prep for the next pass: 00314 quad_stencil->setOperation( osg::Stencil::REPLACE, osg::Stencil::REPLACE, osg::Stencil::REPLACE ); 00315 00316 osg::StateSet* renderSS = _renderPass->getOrCreateStateSet(); 00317 renderSS->setAttributeAndModes( quad_stencil, ON_AND_PROTECTED ); 00318 renderSS->setRenderBinDetails( baseBin++, "RenderBin" ); 00319 00320 // if we did a pre-render depth pass, adjust the depth buffer function to account for that 00321 if ( _preRenderChildrenToDepthBuffer ) 00322 { 00323 renderSS->setAttributeAndModes( new osg::Depth( osg::Depth::LEQUAL ), osg::StateAttribute::ON ); 00324 } 00325 00326 // testing 00327 //renderSS->setMode( GL_DEPTH_TEST, OFF_AND_PROTECTED ); 00328 //renderSS->setAttributeAndModes( new osg::Depth( osg::Depth::ALWAYS ), ON_AND_PROTECTED ); 00329 00330 _root->addChild( _renderPass ); 00331 } 00332 00333 00334 static 00335 void tessellate( osg::Geometry* geom ) 00336 { 00337 osgUtil::Tessellator tess; 00338 tess.setTessellationType( osgUtil::Tessellator::TESS_TYPE_GEOMETRY ); 00339 tess.setWindingType( osgUtil::Tessellator::TESS_WINDING_ODD ); 00340 // tess.setWindingType( osgUtil::Tessellator::TESS_WINDING_POSITIVE ); 00341 tess.retessellatePolygons( *geom ); 00342 } 00343 00344 osg::Geode* 00345 StencilVolumeFactory::createVolume(Geometry* geom, 00346 double offset, 00347 double height, 00348 const FilterContext& context ) 00349 { 00350 if ( !geom ) return 0L; 00351 00352 int numRings = 0; 00353 00354 // start by offsetting the input data and counting the number of rings 00355 { 00356 GeometryIterator i( geom ); 00357 i.traverseMultiGeometry() = true; 00358 i.traversePolygonHoles() = true; 00359 while( i.hasMore() ) 00360 { 00361 Geometry* part = i.next(); 00362 00363 if (offset != 0.0) 00364 { 00365 for( osg::Vec3dArray::iterator j = part->begin(); j != part->end(); j++ ) 00366 { 00367 if ( context.isGeocentric() ) 00368 { 00369 osg::Vec3d world = context.toWorld( *j ); 00370 // TODO: get the proper up vector; this is spherical.. or does it really matter for 00371 // stencil volumes? 00372 osg::Vec3d offset_vec = world; 00373 offset_vec.normalize(); 00374 *j = context.toLocal( world + offset_vec * offset ); //(*j) += offset_vec * offset; 00375 } 00376 else 00377 { 00378 (*j).z() += offset; 00379 } 00380 } 00381 } 00382 00383 // in the meantime, count the # of closed geoms. We will need to know this in 00384 // order to pre-allocate the proper # of verts. 00385 if ( part->getType() == Geometry::TYPE_POLYGON || part->getType() == Geometry::TYPE_RING ) 00386 { 00387 numRings++; 00388 } 00389 } 00390 } 00391 00392 // now, go thru and remove any coplanar segments from the geometry. The tesselator will 00393 // not work include a vert connecting two colinear segments in the tesselation, and this 00394 // will break the stenciling logic. 00395 #define PARALLEL_EPSILON 0.01 00396 GeometryIterator i( geom ); 00397 i.traverseMultiGeometry() = true; 00398 i.traversePolygonHoles() = true; 00399 while( i.hasMore() ) 00400 { 00401 Geometry* part = i.next(); 00402 if ( part->size() >= 3 ) 00403 { 00404 osg::Vec3d prevVec = part->front() - part->back(); 00405 prevVec.normalize(); 00406 00407 for( osg::Vec3dArray::iterator j = part->begin(); part->size() >= 3 && j != part->end(); ) 00408 { 00409 osg::Vec3d& p0 = *j; 00410 osg::Vec3d& p1 = j+1 != part->end() ? *(j+1) : part->front(); 00411 osg::Vec3d vec = p1-p0; vec.normalize(); 00412 00413 // if the vectors are essentially parallel, remove the extraneous vertex. 00414 if ( (prevVec ^ vec).length() < PARALLEL_EPSILON ) 00415 { 00416 j = part->erase( j ); 00417 //OE_NOTICE << "removed colinear segment" << std::endl; 00418 } 00419 else 00420 { 00421 ++j; 00422 prevVec = vec; 00423 } 00424 } 00425 } 00426 } 00427 00428 00429 bool made_geom = true; 00430 const SpatialReference* srs = context.profile()->getSRS(); 00431 00432 // total up all the points so we can pre-allocate the vertex arrays. 00433 int num_cap_verts = geom->getTotalPointCount(); 00434 int num_wall_verts = 2 * (num_cap_verts + numRings); // add in numRings b/c we need to close each wall 00435 00436 osg::Geometry* walls = new osg::Geometry(); 00437 osg::Vec3Array* verts = new osg::Vec3Array( num_wall_verts ); 00438 walls->setVertexArray( verts ); 00439 00440 osg::Geometry* top_cap = new osg::Geometry(); 00441 osg::Vec3Array* top_verts = new osg::Vec3Array( num_cap_verts ); 00442 top_cap->setVertexArray( top_verts ); 00443 00444 osg::Geometry* bottom_cap = new osg::Geometry(); 00445 osg::Vec3Array* bottom_verts = new osg::Vec3Array( num_cap_verts ); 00446 bottom_cap->setVertexArray( bottom_verts ); 00447 00448 int wall_vert_ptr = 0; 00449 int top_vert_ptr = 0; 00450 int bottom_vert_ptr = 0; 00451 00452 //double target_len = height; 00453 00454 // now generate the extruded geometry. 00455 GeometryIterator k( geom ); 00456 while( k.hasMore() ) 00457 { 00458 Geometry* part = k.next(); 00459 00460 unsigned int wall_part_ptr = wall_vert_ptr; 00461 unsigned int top_part_ptr = top_vert_ptr; 00462 unsigned int bottom_part_ptr = bottom_vert_ptr; 00463 double part_len = 0.0; 00464 00465 GLenum prim_type = part->getType() == Geometry::TYPE_POINTSET ? GL_LINES : GL_TRIANGLE_STRIP; 00466 00467 for( osg::Vec3dArray::const_iterator m = part->begin(); m != part->end(); ++m ) 00468 { 00469 osg::Vec3d extrude_vec; 00470 00471 if ( srs ) 00472 { 00473 osg::Vec3d m_world = context.toWorld( *m ); //*m * context.inverseReferenceFrame(); 00474 if ( context.isGeocentric() ) 00475 { 00476 osg::Vec3d p_vec = m_world; // todo: not exactly right; spherical 00477 00478 osg::Vec3d unit_vec = p_vec; 00479 unit_vec.normalize(); 00480 p_vec = p_vec + unit_vec*height; 00481 00482 extrude_vec = context.toLocal( p_vec ); //p_vec * context.referenceFrame(); 00483 } 00484 else 00485 { 00486 extrude_vec.set( m_world.x(), m_world.y(), height ); 00487 extrude_vec = context.toLocal( extrude_vec ); //extrude_vec * context.referenceFrame(); 00488 } 00489 } 00490 else 00491 { 00492 extrude_vec.set( m->x(), m->y(), height ); 00493 } 00494 00495 (*top_verts)[top_vert_ptr++] = extrude_vec; 00496 (*bottom_verts)[bottom_vert_ptr++] = *m; 00497 00498 part_len += wall_vert_ptr > wall_part_ptr? 00499 (extrude_vec - (*verts)[wall_vert_ptr-2]).length() : 00500 0.0; 00501 00502 int p; 00503 00504 p = wall_vert_ptr++; 00505 (*verts)[p] = extrude_vec; 00506 00507 p = wall_vert_ptr++; 00508 (*verts)[p] = *m; 00509 } 00510 00511 // close the wall if it's a ring/poly: 00512 if ( part->getType() == Geometry::TYPE_RING || part->getType() == Geometry::TYPE_POLYGON ) 00513 { 00514 part_len += wall_vert_ptr > wall_part_ptr? 00515 ((*verts)[wall_part_ptr] - (*verts)[wall_vert_ptr-2]).length() : 00516 0.0; 00517 00518 int p; 00519 00520 p = wall_vert_ptr++; 00521 (*verts)[p] = (*verts)[wall_part_ptr]; 00522 00523 p = wall_vert_ptr++; 00524 (*verts)[p] = (*verts)[wall_part_ptr+1]; 00525 } 00526 00527 walls->addPrimitiveSet( new osg::DrawArrays( 00528 prim_type, 00529 wall_part_ptr, wall_vert_ptr - wall_part_ptr ) ); 00530 00531 top_cap->addPrimitiveSet( new osg::DrawArrays( 00532 osg::PrimitiveSet::LINE_LOOP, 00533 top_part_ptr, top_vert_ptr - top_part_ptr ) ); 00534 00535 // reverse the bottom verts so the front face is down: 00536 std::reverse( bottom_verts->begin()+bottom_part_ptr, bottom_verts->begin()+bottom_vert_ptr ); 00537 00538 bottom_cap->addPrimitiveSet( new osg::DrawArrays( 00539 osg::PrimitiveSet::LINE_LOOP, 00540 bottom_part_ptr, bottom_vert_ptr - bottom_part_ptr ) ); 00541 } 00542 00543 // build solid surfaces for the caps: 00544 tessellate( top_cap ); 00545 tessellate( bottom_cap ); 00546 00547 osg::Geode* geode = new osg::Geode(); 00548 geode->addDrawable( walls ); 00549 geode->addDrawable( top_cap ); 00550 geode->addDrawable( bottom_cap ); 00551 00552 return geode; 00553 } 00554