bool ExtrudeGeometryFilter::process( FeatureList& features, FilterContext& context ) { // seed our random number generators Random wallSkinPRNG( _wallSkinSymbol.valid()? *_wallSkinSymbol->randomSeed() : 0, Random::METHOD_FAST ); Random roofSkinPRNG( _roofSkinSymbol.valid()? *_roofSkinSymbol->randomSeed() : 0, Random::METHOD_FAST ); for( FeatureList::iterator f = features.begin(); f != features.end(); ++f ) { Feature* input = f->get(); GeometryIterator iter( input->getGeometry(), false ); while( iter.hasMore() ) { Geometry* part = iter.next(); osg::ref_ptr<osg::Geometry> walls = new osg::Geometry(); walls->setUseVertexBufferObjects( _useVertexBufferObjects.get() ); osg::ref_ptr<osg::Geometry> rooflines = 0L; osg::ref_ptr<osg::Geometry> baselines = 0L; osg::ref_ptr<osg::Geometry> outlines = 0L; if ( part->getType() == Geometry::TYPE_POLYGON ) { rooflines = new osg::Geometry(); rooflines->setUseVertexBufferObjects( _useVertexBufferObjects.get() ); // prep the shapes by making sure all polys are open: static_cast<Polygon*>(part)->open(); } // fire up the outline geometry if we have a line symbol. if ( _outlineSymbol != 0L ) { outlines = new osg::Geometry(); outlines->setUseVertexBufferObjects( _useVertexBufferObjects.get() ); } // make a base cap if we're doing stencil volumes. if ( _makeStencilVolume ) { baselines = new osg::Geometry(); baselines->setUseVertexBufferObjects( _useVertexBufferObjects.get() ); } // calculate the extrusion height: float height; if ( _heightCallback.valid() ) { height = _heightCallback->operator()(input, context); } else if ( _heightExpr.isSet() ) { height = input->eval( _heightExpr.mutable_value(), &context ); } else { height = *_extrusionSymbol->height(); } // calculate the height offset from the base: float offset = 0.0; if ( _heightOffsetExpr.isSet() ) { offset = input->eval( _heightOffsetExpr.mutable_value(), &context ); } osg::ref_ptr<osg::StateSet> wallStateSet; osg::ref_ptr<osg::StateSet> roofStateSet; // calculate the wall texturing: SkinResource* wallSkin = 0L; if ( _wallSkinSymbol.valid() ) { if ( _wallResLib.valid() ) { SkinSymbol querySymbol( *_wallSkinSymbol.get() ); querySymbol.objectHeight() = fabs(height) - offset; wallSkin = _wallResLib->getSkin( &querySymbol, wallSkinPRNG, context.getDBOptions() ); } else { //TODO: simple single texture? } } // calculate the rooftop texture: SkinResource* roofSkin = 0L; if ( _roofSkinSymbol.valid() ) { if ( _roofResLib.valid() ) { SkinSymbol querySymbol( *_roofSkinSymbol.get() ); roofSkin = _roofResLib->getSkin( &querySymbol, roofSkinPRNG, context.getDBOptions() ); } else { //TODO: simple single texture? } } // Build the data model for the structure. Structure structure; buildStructure( part, height, offset, _extrusionSymbol->flatten().get(), wallSkin, roofSkin, structure, context); // Create the walls. if ( walls.valid() ) { osg::Vec4f wallColor(1,1,1,1), wallBaseColor(1,1,1,1); if ( _wallPolygonSymbol.valid() ) { wallColor = _wallPolygonSymbol->fill()->color(); } if ( _extrusionSymbol->wallGradientPercentage().isSet() ) { wallBaseColor = Color(wallColor).brightness( 1.0 - *_extrusionSymbol->wallGradientPercentage() ); } else { wallBaseColor = wallColor; } buildWallGeometry(structure, walls.get(), wallColor, wallBaseColor, wallSkin); if ( wallSkin ) { // Get a stateset for the individual wall stateset context.resourceCache()->getOrCreateStateSet( wallSkin, wallStateSet ); } } // tessellate and add the roofs if necessary: if ( rooflines.valid() ) { osg::Vec4f roofColor(1,1,1,1); if ( _roofPolygonSymbol.valid() ) { roofColor = _roofPolygonSymbol->fill()->color(); } buildRoofGeometry(structure, rooflines.get(), roofColor, roofSkin); if ( roofSkin ) { // Get a stateset for the individual roof skin context.resourceCache()->getOrCreateStateSet( roofSkin, roofStateSet ); } } if ( outlines.valid() ) { osg::Vec4f outlineColor(1,1,1,1); if ( _outlineSymbol.valid() ) { outlineColor = _outlineSymbol->stroke()->color(); } float minCreaseAngle = _outlineSymbol->creaseAngle().value(); buildOutlineGeometry(structure, outlines.get(), outlineColor, minCreaseAngle); } if ( baselines.valid() ) { //TODO. osgUtil::Tessellator tess; tess.setTessellationType( osgUtil::Tessellator::TESS_TYPE_GEOMETRY ); tess.setWindingType( osgUtil::Tessellator::TESS_WINDING_ODD ); tess.retessellatePolygons( *(baselines.get()) ); } // Set up for feature naming and feature indexing: std::string name; if ( !_featureNameExpr.empty() ) name = input->eval( _featureNameExpr, &context ); FeatureSourceIndex* index = context.featureIndex(); if ( walls.valid() ) { addDrawable( walls.get(), wallStateSet.get(), name, input, index ); } if ( rooflines.valid() ) { addDrawable( rooflines.get(), roofStateSet.get(), name, input, index ); } if ( baselines.valid() ) { addDrawable( baselines.get(), 0L, name, input, index ); } if ( outlines.valid() ) { addDrawable( outlines.get(), 0L, name, input, index ); } } } return true; }
bool ExtrudeGeometryFilter::process( FeatureList& features, FilterContext& context ) { // seed our random number generators Random wallSkinPRNG( _wallSkinSymbol.valid()? *_wallSkinSymbol->randomSeed() : 0, Random::METHOD_FAST ); Random roofSkinPRNG( _roofSkinSymbol.valid()? *_roofSkinSymbol->randomSeed() : 0, Random::METHOD_FAST ); for( FeatureList::iterator f = features.begin(); f != features.end(); ++f ) { Feature* input = f->get(); GeometryIterator iter( input->getGeometry(), false ); while( iter.hasMore() ) { Geometry* part = iter.next(); osg::ref_ptr<osg::Geometry> walls = new osg::Geometry(); walls->setUseVertexBufferObjects( _useVertexBufferObjects.get() ); osg::ref_ptr<osg::Geometry> rooflines = 0L; osg::ref_ptr<osg::Geometry> baselines = 0L; osg::ref_ptr<osg::Geometry> outlines = 0L; if ( part->getType() == Geometry::TYPE_POLYGON ) { rooflines = new osg::Geometry(); rooflines->setUseVertexBufferObjects( _useVertexBufferObjects.get() ); // prep the shapes by making sure all polys are open: static_cast<Polygon*>(part)->open(); } // fire up the outline geometry if we have a line symbol. if ( _outlineSymbol != 0L ) { outlines = new osg::Geometry(); outlines->setUseVertexBufferObjects( _useVertexBufferObjects.get() ); } // make a base cap if we're doing stencil volumes. if ( _makeStencilVolume ) { baselines = new osg::Geometry(); baselines->setUseVertexBufferObjects( _useVertexBufferObjects.get() ); } // calculate the extrusion height: float height; if ( _heightCallback.valid() ) { height = _heightCallback->operator()(input, context); } else if ( _heightExpr.isSet() ) { height = input->eval( _heightExpr.mutable_value(), &context ); } else { height = *_extrusionSymbol->height(); } // calculate the height offset from the base: float offset = 0.0; if ( _heightOffsetExpr.isSet() ) { offset = input->eval( _heightOffsetExpr.mutable_value(), &context ); } osg::ref_ptr<osg::StateSet> wallStateSet; osg::ref_ptr<osg::StateSet> roofStateSet; // calculate the wall texturing: SkinResource* wallSkin = 0L; if ( _wallSkinSymbol.valid() ) { if ( _wallResLib.valid() ) { SkinSymbol querySymbol( *_wallSkinSymbol.get() ); querySymbol.objectHeight() = fabs(height) - offset; wallSkin = _wallResLib->getSkin( &querySymbol, wallSkinPRNG, context.getDBOptions() ); } else { //TODO: simple single texture? } } // calculate the rooftop texture: SkinResource* roofSkin = 0L; if ( _roofSkinSymbol.valid() ) { if ( _roofResLib.valid() ) { SkinSymbol querySymbol( *_roofSkinSymbol.get() ); roofSkin = _roofResLib->getSkin( &querySymbol, roofSkinPRNG, context.getDBOptions() ); } else { //TODO: simple single texture? } } // calculate the colors: osg::Vec4f wallColor(1,1,1,0), wallBaseColor(1,1,1,0), roofColor(1,1,1,0), outlineColor(1,1,1,1); if ( _wallPolygonSymbol.valid() ) { wallColor = _wallPolygonSymbol->fill()->color(); if ( _extrusionSymbol->wallGradientPercentage().isSet() ) { wallBaseColor = Color(wallColor).brightness( 1.0 - *_extrusionSymbol->wallGradientPercentage() ); } else { wallBaseColor = wallColor; } } if ( _roofPolygonSymbol.valid() ) { roofColor = _roofPolygonSymbol->fill()->color(); } if ( _outlineSymbol.valid() ) { outlineColor = _outlineSymbol->stroke()->color(); } // Create the extruded geometry! if (extrudeGeometry( part, height, offset, *_extrusionSymbol->flatten(), walls.get(), rooflines.get(), baselines.get(), outlines.get(), wallColor, wallBaseColor, roofColor, outlineColor, wallSkin, roofSkin, context ) ) { if ( wallSkin ) { context.resourceCache()->getStateSet( wallSkin, wallStateSet ); } // generate per-vertex normals, altering the geometry as necessary to avoid // smoothing around sharp corners osgUtil::SmoothingVisitor::smooth( *walls.get(), osg::DegreesToRadians(_wallAngleThresh_deg) ); // tessellate and add the roofs if necessary: if ( rooflines.valid() ) { osgUtil::Tessellator tess; tess.setTessellationType( osgUtil::Tessellator::TESS_TYPE_GEOMETRY ); tess.setWindingType( osgUtil::Tessellator::TESS_WINDING_ODD ); tess.retessellatePolygons( *(rooflines.get()) ); // generate default normals (no crease angle necessary; they are all pointing up) // TODO do this manually; probably faster if ( !_makeStencilVolume ) osgUtil::SmoothingVisitor::smooth( *rooflines.get() ); if ( roofSkin ) { context.resourceCache()->getStateSet( roofSkin, roofStateSet ); } } if ( baselines.valid() ) { osgUtil::Tessellator tess; tess.setTessellationType( osgUtil::Tessellator::TESS_TYPE_GEOMETRY ); tess.setWindingType( osgUtil::Tessellator::TESS_WINDING_ODD ); tess.retessellatePolygons( *(baselines.get()) ); } std::string name; if ( !_featureNameExpr.empty() ) name = input->eval( _featureNameExpr, &context ); FeatureSourceIndex* index = context.featureIndex(); addDrawable( walls.get(), wallStateSet.get(), name, input, index ); if ( rooflines.valid() ) { addDrawable( rooflines.get(), roofStateSet.get(), name, input, index ); } if ( baselines.valid() ) { addDrawable( baselines.get(), 0L, name, input, index ); } if ( outlines.valid() ) { addDrawable( outlines.get(), 0L, name, input, index ); } } } } return true; }
bool SubstituteModelFilter::process(const FeatureList& features, const InstanceSymbol* symbol, Session* session, osg::Group* attachPoint, FilterContext& context ) { // Establish SRS information: bool makeECEF = context.getSession()->getMapInfo().isGeocentric(); const SpatialReference* targetSRS = context.getSession()->getMapInfo().getSRS(); // first, go through the features and build the model cache. Apply the model matrix' scale // factor to any AutoTransforms directly (cloning them as necessary) std::map< std::pair<URI, float>, osg::ref_ptr<osg::Node> > uniqueModels; // keep track of failed URIs so we don't waste time or warning messages on them std::set< URI > missing; StringExpression uriEx = *symbol->url(); NumericExpression scaleEx = *symbol->scale(); const ModelSymbol* modelSymbol = dynamic_cast<const ModelSymbol*>(symbol); const IconSymbol* iconSymbol = dynamic_cast<const IconSymbol*> (symbol); NumericExpression headingEx; if ( modelSymbol ) headingEx = *modelSymbol->heading(); for( FeatureList::const_iterator f = features.begin(); f != features.end(); ++f ) { Feature* input = f->get(); // evaluate the instance URI expression: StringExpression uriEx = *symbol->url(); URI instanceURI( input->eval(uriEx, &context), uriEx.uriContext() ); // find the corresponding marker in the cache osg::ref_ptr<InstanceResource> instance; if ( !findResource(instanceURI, symbol, context, missing, instance) ) continue; // evalute the scale expression (if there is one) float scale = 1.0f; osg::Matrixd scaleMatrix; if ( symbol->scale().isSet() ) { scale = input->eval( scaleEx, &context ); if ( scale == 0.0 ) scale = 1.0; if ( scale != 1.0 ) _normalScalingRequired = true; scaleMatrix = osg::Matrix::scale( scale, scale, scale ); } osg::Matrixd rotationMatrix; if ( modelSymbol && modelSymbol->heading().isSet() ) { float heading = input->eval(headingEx, &context); rotationMatrix.makeRotate( osg::Quat(osg::DegreesToRadians(heading), osg::Vec3(0,0,1)) ); } // how that we have a marker source, create a node for it std::pair<URI,float> key( instanceURI, scale ); // cache nodes per instance. osg::ref_ptr<osg::Node>& model = uniqueModels[key]; if ( !model.valid() ) { context.resourceCache()->getInstanceNode( instance.get(), model ); // if icon decluttering is off, install an AutoTransform. if ( iconSymbol ) { if ( iconSymbol->declutter() == true ) { Decluttering::setEnabled( model->getOrCreateStateSet(), true ); } else if ( dynamic_cast<osg::AutoTransform*>(model.get()) == 0L ) { osg::AutoTransform* at = new osg::AutoTransform(); at->setAutoRotateMode( osg::AutoTransform::ROTATE_TO_SCREEN ); at->setAutoScaleToScreen( true ); at->addChild( model ); model = at; } } } if ( model.valid() ) { GeometryIterator gi( input->getGeometry(), false ); while( gi.hasMore() ) { Geometry* geom = gi.next(); // if necessary, transform the points to the target SRS: if ( !makeECEF && !targetSRS->isEquivalentTo(context.profile()->getSRS()) ) { context.profile()->getSRS()->transform( geom->asVector(), targetSRS ); } for( unsigned i=0; i<geom->size(); ++i ) { osg::Matrixd mat; osg::Vec3d point = (*geom)[i]; if ( makeECEF ) { // the "rotation" element lets us re-orient the instance to ensure it's pointing up. We // could take a shortcut and just use the current extent's local2world matrix for this, // but if the tile is big enough the up vectors won't be quite right. osg::Matrixd rotation; ECEF::transformAndGetRotationMatrix( point, context.profile()->getSRS(), point, targetSRS, rotation ); mat = rotationMatrix * rotation * scaleMatrix * osg::Matrixd::translate( point ) * _world2local; } else { mat = rotationMatrix * scaleMatrix * osg::Matrixd::translate( point ) * _world2local; } osg::MatrixTransform* xform = new osg::MatrixTransform(); xform->setMatrix( mat ); xform->setDataVariance( osg::Object::STATIC ); xform->addChild( model.get() ); attachPoint->addChild( xform ); if ( context.featureIndex() && !_useDrawInstanced ) { context.featureIndex()->tagNode( xform, input ); } // name the feature if necessary if ( !_featureNameExpr.empty() ) { const std::string& name = input->eval( _featureNameExpr, &context); if ( !name.empty() ) xform->setName( name ); } } } } } if ( iconSymbol ) { // activate decluttering for icons if requested if ( iconSymbol->declutter() == true ) { Decluttering::setEnabled( attachPoint->getOrCreateStateSet(), true ); } // activate horizon culling if we are in geocentric space if ( context.getSession() && context.getSession()->getMapInfo().isGeocentric() ) { HorizonCullingProgram::install( attachPoint->getOrCreateStateSet() ); } } // active DrawInstanced if required: if ( _useDrawInstanced && Registry::capabilities().supportsDrawInstanced() ) { DrawInstanced::convertGraphToUseDrawInstanced( attachPoint ); // install a shader program to render draw-instanced. DrawInstanced::install( attachPoint->getOrCreateStateSet() ); } return true; }
bool SubstituteModelFilter::process(const FeatureList& features, const MarkerSymbol* symbol, Session* session, osg::Group* attachPoint, FilterContext& context ) { bool makeECEF = context.getSession()->getMapInfo().isGeocentric(); // first, go through the features and build the model cache. Apply the model matrix' scale // factor to any AutoTransforms directly (cloning them as necessary) std::map< std::pair<URI, float>, osg::ref_ptr<osg::Node> > uniqueModels; //std::map< Feature*, osg::ref_ptr<osg::Node> > featureModels; StringExpression uriEx = *symbol->url(); NumericExpression scaleEx = *symbol->scale(); for( FeatureList::const_iterator f = features.begin(); f != features.end(); ++f ) { Feature* input = f->get(); // evaluate the marker URI expression: StringExpression uriEx = *symbol->url(); URI markerURI( input->eval(uriEx, &context), uriEx.uriContext() ); // find the corresponding marker in the cache MarkerResource* marker = 0L; MarkerCache::Record rec = _markerCache.get( markerURI ); if ( rec.valid() ) { marker = rec.value(); } else { marker = new MarkerResource(); marker->uri() = markerURI; _markerCache.insert( markerURI, marker ); } // evalute the scale expression (if there is one) float scale = 1.0f; osg::Matrixd scaleMatrix; if ( symbol->scale().isSet() ) { scale = input->eval( scaleEx, &context ); if ( scale == 0.0 ) scale = 1.0; scaleMatrix = osg::Matrix::scale( scale, scale, scale ); } // how that we have a marker source, create a node for it std::pair<URI,float> key( markerURI, scale ); osg::ref_ptr<osg::Node>& model = uniqueModels[key]; if ( !model.valid() ) { model = context.resourceCache()->getMarkerNode( marker ); if ( scale != 1.0f && dynamic_cast<osg::AutoTransform*>( model.get() ) ) { // clone the old AutoTransform, set the new scale, and copy over its children. osg::AutoTransform* oldAT = dynamic_cast<osg::AutoTransform*>(model.get()); osg::AutoTransform* newAT = osg::clone( oldAT ); // make a scaler and put it between the new AutoTransform and its kids osg::MatrixTransform* scaler = new osg::MatrixTransform(osg::Matrix::scale(scale,scale,scale)); for( unsigned i=0; i<newAT->getNumChildren(); ++i ) scaler->addChild( newAT->getChild(0) ); newAT->removeChildren(0, newAT->getNumChildren()); newAT->addChild( scaler ); model = newAT; } } if ( model.valid() ) { GeometryIterator gi( input->getGeometry(), false ); while( gi.hasMore() ) { Geometry* geom = gi.next(); for( unsigned i=0; i<geom->size(); ++i ) { osg::Matrixd mat; osg::Vec3d point = (*geom)[i]; if ( makeECEF ) { // the "rotation" element lets us re-orient the instance to ensure it's pointing up. We // could take a shortcut and just use the current extent's local2world matrix for this, // but if the tile is big enough the up vectors won't be quite right. osg::Matrixd rotation; ECEF::transformAndGetRotationMatrix( point, context.profile()->getSRS(), point, rotation ); mat = rotation * scaleMatrix * osg::Matrixd::translate( point ) * _world2local; } else { mat = scaleMatrix * osg::Matrixd::translate( point ) * _world2local; } osg::MatrixTransform* xform = new osg::MatrixTransform(); xform->setMatrix( mat ); xform->addChild( model.get() ); attachPoint->addChild( xform ); // name the feature if necessary if ( !_featureNameExpr.empty() ) { const std::string& name = input->eval( _featureNameExpr, &context); if ( !name.empty() ) xform->setName( name ); } } } } } return true; }
bool SubstituteModelFilter::process(const FeatureList& features, const InstanceSymbol* symbol, Session* session, osg::Group* attachPoint, FilterContext& context ) { // Establish SRS information: bool makeECEF = context.getSession()->getMapInfo().isGeocentric(); const SpatialReference* targetSRS = context.getSession()->getMapInfo().getSRS(); // first, go through the features and build the model cache. Apply the model matrix' scale // factor to any AutoTransforms directly (cloning them as necessary) std::map< std::pair<URI, float>, osg::ref_ptr<osg::Node> > uniqueModels; // URI cache speeds up URI creation since it can be slow. osgEarth::fast_map<std::string, URI> uriCache; // keep track of failed URIs so we don't waste time or warning messages on them std::set< URI > missing; StringExpression uriEx = *symbol->url(); NumericExpression scaleEx = *symbol->scale(); const ModelSymbol* modelSymbol = dynamic_cast<const ModelSymbol*>(symbol); const IconSymbol* iconSymbol = dynamic_cast<const IconSymbol*> (symbol); NumericExpression headingEx; NumericExpression scaleXEx; NumericExpression scaleYEx; NumericExpression scaleZEx; if ( modelSymbol ) { headingEx = *modelSymbol->heading(); scaleXEx = *modelSymbol->scaleX(); scaleYEx = *modelSymbol->scaleY(); scaleZEx = *modelSymbol->scaleZ(); } for( FeatureList::const_iterator f = features.begin(); f != features.end(); ++f ) { Feature* input = f->get(); // Run a feature pre-processing script. if ( symbol->script().isSet() ) { StringExpression scriptExpr(symbol->script().get()); input->eval( scriptExpr, &context ); } // evaluate the instance URI expression: const std::string& st = input->eval(uriEx, &context); URI& instanceURI = uriCache[st]; if(instanceURI.empty()) // Create a map, to reuse URI's, since they take a long time to create { instanceURI = URI( st, uriEx.uriContext() ); } // find the corresponding marker in the cache osg::ref_ptr<InstanceResource> instance; if ( !findResource(instanceURI, symbol, context, missing, instance) ) continue; // evalute the scale expression (if there is one) float scale = 1.0f; osg::Vec3d scaleVec(1.0, 1.0, 1.0); osg::Matrixd scaleMatrix; if ( symbol->scale().isSet() ) { scale = input->eval( scaleEx, &context ); scaleVec.set(scale, scale, scale); } if ( modelSymbol ) { if ( modelSymbol->scaleX().isSet() ) { scaleVec.x() *= input->eval( scaleXEx, &context ); } if ( modelSymbol->scaleY().isSet() ) { scaleVec.y() *= input->eval( scaleYEx, &context ); } if ( modelSymbol->scaleZ().isSet() ) { scaleVec.z() *= input->eval( scaleZEx, &context ); } } if ( scaleVec.x() == 0.0 ) scaleVec.x() = 1.0; if ( scaleVec.y() == 0.0 ) scaleVec.y() = 1.0; if ( scaleVec.z() == 0.0 ) scaleVec.z() = 1.0; scaleMatrix = osg::Matrix::scale( scaleVec ); osg::Matrixd rotationMatrix; if ( modelSymbol && modelSymbol->heading().isSet() ) { float heading = input->eval(headingEx, &context); rotationMatrix.makeRotate( osg::Quat(osg::DegreesToRadians(heading), osg::Vec3(0,0,1)) ); } // how that we have a marker source, create a node for it std::pair<URI,float> key( instanceURI, iconSymbol? scale : 1.0f ); //use 1.0 for models, since we don't want unique models based on scaling // cache nodes per instance. osg::ref_ptr<osg::Node>& model = uniqueModels[key]; if ( !model.valid() ) { // Always clone the cached instance so we're not processing data that's // already in the scene graph. -gw context.resourceCache()->cloneOrCreateInstanceNode(instance.get(), model, context.getDBOptions()); // if icon decluttering is off, install an AutoTransform. if ( iconSymbol ) { if ( iconSymbol->declutter() == true ) { ScreenSpaceLayout::activate(model->getOrCreateStateSet()); } else if ( dynamic_cast<osg::AutoTransform*>(model.get()) == 0L ) { osg::AutoTransform* at = new osg::AutoTransform(); at->setAutoRotateMode( osg::AutoTransform::ROTATE_TO_SCREEN ); at->setAutoScaleToScreen( true ); at->addChild( model ); model = at; } } } if ( model.valid() ) { GeometryIterator gi( input->getGeometry(), false ); while( gi.hasMore() ) { Geometry* geom = gi.next(); // if necessary, transform the points to the target SRS: if ( !makeECEF && !targetSRS->isEquivalentTo(context.profile()->getSRS()) ) { context.profile()->getSRS()->transform( geom->asVector(), targetSRS ); } for( unsigned i=0; i<geom->size(); ++i ) { osg::Matrixd mat; // need to recalcluate expression-based data per-point, not just per-feature! float scale = 1.0f; osg::Vec3d scaleVec(1.0, 1.0, 1.0); osg::Matrixd scaleMatrix; if ( symbol->scale().isSet() ) { scale = input->eval( scaleEx, &context ); scaleVec.set(scale, scale, scale); } if ( modelSymbol ) { if ( modelSymbol->scaleX().isSet() ) { scaleVec.x() *= input->eval( scaleXEx, &context ); } if ( modelSymbol->scaleY().isSet() ) { scaleVec.y() *= input->eval( scaleYEx, &context ); } if ( modelSymbol->scaleZ().isSet() ) { scaleVec.z() *= input->eval( scaleZEx, &context ); } } if ( scaleVec.x() == 0.0 ) scaleVec.x() = 1.0; if ( scaleVec.y() == 0.0 ) scaleVec.y() = 1.0; if ( scaleVec.z() == 0.0 ) scaleVec.z() = 1.0; scaleMatrix = osg::Matrix::scale( scaleVec ); if ( modelSymbol->heading().isSet() ) { float heading = input->eval(headingEx, &context); rotationMatrix.makeRotate( osg::Quat(osg::DegreesToRadians(heading), osg::Vec3(0,0,1)) ); } osg::Vec3d point = (*geom)[i]; if ( makeECEF ) { // the "rotation" element lets us re-orient the instance to ensure it's pointing up. We // could take a shortcut and just use the current extent's local2world matrix for this, // but if the tile is big enough the up vectors won't be quite right. osg::Matrixd rotation; ECEF::transformAndGetRotationMatrix( point, context.profile()->getSRS(), point, targetSRS, rotation ); mat = scaleMatrix * rotationMatrix * rotation * osg::Matrixd::translate( point ) * _world2local; } else { mat = scaleMatrix * rotationMatrix * osg::Matrixd::translate( point ) * _world2local; } osg::MatrixTransform* xform = new osg::MatrixTransform(); xform->setMatrix( mat ); xform->setDataVariance( osg::Object::STATIC ); xform->addChild( model.get() ); attachPoint->addChild( xform ); // Only tag nodes if we aren't using clustering. if ( context.featureIndex() && !_cluster) { context.featureIndex()->tagNode( xform, input ); } // name the feature if necessary if ( !_featureNameExpr.empty() ) { const std::string& name = input->eval( _featureNameExpr, &context); if ( !name.empty() ) xform->setName( name ); } } } } } if ( iconSymbol ) { // activate decluttering for icons if requested if ( iconSymbol->declutter() == true ) { ScreenSpaceLayout::activate(attachPoint->getOrCreateStateSet()); } // activate horizon culling if we are in geocentric space if ( context.getSession() && context.getSession()->getMapInfo().isGeocentric() ) { // should this use clipping, or a horizon cull callback? //HorizonCullingProgram::install( attachPoint->getOrCreateStateSet() ); attachPoint->getOrCreateStateSet()->setMode(GL_CLIP_DISTANCE0, 1); } } // active DrawInstanced if required: if ( _useDrawInstanced ) { DrawInstanced::convertGraphToUseDrawInstanced( attachPoint ); // install a shader program to render draw-instanced. DrawInstanced::install( attachPoint->getOrCreateStateSet() ); } return true; }