Example #1
0
DrawTileCommand*
TerrainCuller::addDrawCommand(UID uid, const TileRenderModel* model, const RenderingPass* pass, TileNode* tileNode)
{
    SurfaceNode* surface = tileNode->getSurfaceNode();

    const RenderBindings&  bindings = _context->getRenderBindings();

    // skip layers that are not visible:
    if (pass && pass->_imageLayer.valid() && !pass->_imageLayer->getVisible())
        return 0L;

    // add a new Draw command to the appropriate layer
    osg::ref_ptr<LayerDrawable> layer = _terrain.layer(uid);
    if (layer.valid())
    {
        layer->_tiles.push_back(DrawTileCommand());
        DrawTileCommand& tile = layer->_tiles.back();

        // install everything we need in the Draw Command:
        tile._colorSamplers = pass ? &pass->_samplers : 0L;
        tile._sharedSamplers = &model->_sharedSamplers;
        tile._matrix = surface->getMatrix();
        tile._modelViewMatrix = *this->getModelViewMatrix();
        tile._keyValue = tileNode->getTileKeyValue();
        tile._geom = surface->getDrawable()->_geom.get();
        tile._morphConstants = tileNode->getMorphConstants();
        tile._key = tileNode->getKey();

#if 1
        osg::Vec3 c = surface->getBound().center() * surface->getInverseMatrix();
        tile._range = getDistanceToViewPoint(c, true);
#else
        osg::Vec3f eyeWorld = getViewPointLocal() * surface->getMatrix();
        tile._range = (eyeWorld - surface->getBound().center()).length();
#endif

        const osg::Image* elevRaster = tileNode->getElevationRaster();
        if (elevRaster)
        {
            float bias = _context->getUseTextureBorder() ? 1.5 : 0.5;

            // Compute an elevation texture sampling scale/bias so we sample elevation data on center
            // instead of on edge (as we do with color, etc.)
            //
            // This starts out as:
            //   scale = (size-1)/size : this shrinks the sample area by one texel since we're sampling on center
            //   bias = 0.5/size : this shifts the sample area over 1/2 texel to the center.
            //
            // But, since we also have a 1-texel border, we need to further reduce the scale by 2 texels to
            // remove the border, and shift an extra texel over as well. Giving us this:
            float size = (float)elevRaster->s();
            tile._elevTexelCoeff.set((size - (2.0*bias)) / size, bias / size);
        }

        return &tile;
    }
    else if (pass)
    {
        // The pass exists but it's layer doesn't - remember this so we can run
        // a visitor to clean up the rendering models.
        ++_orphanedPassesDetected;
    }

    return 0L;
}
Example #2
0
void
TerrainCuller::apply(osg::Node& node)
{
    // push the node's state.
    osg::StateSet* node_state = node.getStateSet();

    TileNode* tileNode = dynamic_cast<TileNode*>(&node);
    if (tileNode)
    {
        _currentTileNode = tileNode;
        _currentTileDrawCommands = 0u;
        
        if (!_terrain.patchLayers().empty())
        {
            // todo: check for patch/virtual
            const RenderBindings&  bindings    = _context->getRenderBindings();
            TileRenderModel& renderModel = _currentTileNode->renderModel();

            bool pushedMatrix = false;
            
            for (PatchLayerVector::const_iterator i = _terrain.patchLayers().begin(); i != _terrain.patchLayers().end(); ++i)
            {
                PatchLayer* layer = i->get();
                if (layer->getAcceptCallback() == 0L ||
                    layer->getAcceptCallback()->acceptKey(_currentTileNode->getKey()))
                {
                    // Push this tile's matrix if we haven't already done so:
                    if (!pushedMatrix)
                    {
                        SurfaceNode* surface = tileNode->getSurfaceNode();

                        // push the surface matrix:
                        osg::Matrix mvm = *getModelViewMatrix();
                        surface->computeLocalToWorldMatrix(mvm, this);
                        pushModelViewMatrix(createOrReuseMatrix(mvm), surface->getReferenceFrame());
                        pushedMatrix = true;
                    }

                    // Add the draw command:
                    DrawTileCommand* cmd = addDrawCommand(layer->getUID(), &renderModel, 0L, tileNode);
                    if (cmd)
                    {
                        cmd->_drawPatch = true;
                        cmd->_drawCallback = layer->getDrawCallback();
                        ++_currentTileDrawCommands;
                    }
                }
            }

            if (pushedMatrix)
            {
                popModelViewMatrix();
            }
        }
    }

    else
    {
        SurfaceNode* surface = dynamic_cast<SurfaceNode*>(&node);
        if (surface)
        {
            TileRenderModel& renderModel = _currentTileNode->renderModel();

            // push the surface matrix:
            osg::Matrix mvm = *getModelViewMatrix();
            surface->computeLocalToWorldMatrix(mvm, this);
            pushModelViewMatrix(createOrReuseMatrix(mvm), surface->getReferenceFrame());
                        
            // First go through any legit rendering pass data in the Tile and
            // and add a DrawCommand for each.
            for (unsigned p = 0; p < renderModel._passes.size(); ++p)
            {
                const RenderingPass& pass = renderModel._passes[p];
                
                if (pass._layer.valid() && pass._layer->getRenderType() == Layer::RENDERTYPE_TILE)
                {
                    if (addDrawCommand(pass._sourceUID, &renderModel, &pass, _currentTileNode))
                    {
                        ++_currentTileDrawCommands;
                    }
                }
            }

            // Next, add a DrawCommand for each tile layer not represented in the TerrainTileModel
            // as a rendering pass.
            for (LayerVector::const_iterator i = _terrain.tileLayers().begin(); i != _terrain.tileLayers().end(); ++i)
            {
                Layer* layer = i->get();
                if (addDrawCommand(layer->getUID(), &renderModel, 0L, _currentTileNode))
                {
                    ++_currentTileDrawCommands;
                }
            }

            // If the culler added no draw commands for this tile... do something!
            if (_currentTileDrawCommands == 0)
            {
                //OE_INFO << LC << "Adding blank render.\n";
                addDrawCommand(-1, &renderModel, 0L, _currentTileNode);
            }

            popModelViewMatrix();

            _terrain._drawState->_bs.expandBy(surface->getBound());
            _terrain._drawState->_box.expandBy(_terrain._drawState->_bs);
        }
    }

    traverse(node);
}
Example #3
0
void
TerrainCuller::apply(osg::Node& node)
{
    // push the node's state.
    osg::StateSet* node_state = node.getStateSet();

    TileNode* tileNode = dynamic_cast<TileNode*>(&node);
    if (tileNode)
    {
        _currentTileNode = tileNode;

        // reset the pointer to the first DrawTileCommand. We keep track of this so
        // we can set it's "order" member to zero at the end, so the rendering engine
        // knows to blend it with the terrain geometry color.
        _firstTileDrawCommandForTile = 0L;

        //_currentTileDrawCommands = 0u;
        
        if (!_terrain.patchLayers().empty())
        {
            // todo: check for patch/virtual
            const RenderBindings& bindings = _context->getRenderBindings();
            TileRenderModel& renderModel = _currentTileNode->renderModel();

            bool pushedMatrix = false;
            
            for (PatchLayerVector::const_iterator i = _terrain.patchLayers().begin(); i != _terrain.patchLayers().end(); ++i)
            {
                PatchLayer* layer = i->get();
                if (layer->getAcceptCallback() == 0L ||
                    layer->getAcceptCallback()->acceptKey(_currentTileNode->getKey()))
                {
                    // Push this tile's matrix if we haven't already done so:
                    if (!pushedMatrix)
                    {
                        SurfaceNode* surface = tileNode->getSurfaceNode();

                        // push the surface matrix:
                        osg::Matrix mvm = *getModelViewMatrix();
                        surface->computeLocalToWorldMatrix(mvm, this);
                        pushModelViewMatrix(createOrReuseMatrix(mvm), surface->getReferenceFrame());
                        pushedMatrix = true;
                    }

                    // Add the draw command:
                    DrawTileCommand* cmd = addDrawCommand(layer->getUID(), &renderModel, 0L, tileNode);
                    if (cmd)
                    {
                        cmd->_drawPatch = true;
                        cmd->_drawCallback = layer->getDrawCallback();
                    }
                }
            }

            if (pushedMatrix)
            {
                popModelViewMatrix();
            }
        }
    }

    else
    {
        SurfaceNode* surface = dynamic_cast<SurfaceNode*>(&node);
        if (surface)
        {
            TileRenderModel& renderModel = _currentTileNode->renderModel();

            // push the surface matrix:
            osg::Matrix mvm = *getModelViewMatrix();
            surface->computeLocalToWorldMatrix(mvm, this);
            pushModelViewMatrix(createOrReuseMatrix(mvm), surface->getReferenceFrame());

            int order = 0;

            // First go through any legit rendering pass data in the Tile and
            // and add a DrawCommand for each.
            for (unsigned p = 0; p < renderModel._passes.size(); ++p)
            {
                const RenderingPass& pass = renderModel._passes[p];
                DrawTileCommand* cmd = addDrawCommand(pass.sourceUID(), &renderModel, &pass, _currentTileNode);
                if (cmd)
                {
                    if (_firstTileDrawCommandForTile == 0L)
                    {
                        _firstTileDrawCommandForTile = cmd;
                    }
                    else if (cmd->_order < _firstTileDrawCommandForTile->_order)
                    {
                        //_firstTileDrawCommandForTile->_order = 1;
                        _firstTileDrawCommandForTile = cmd;
                    }
                }
            }

            // If the culler added no draw commands for this tile... we still need
            // to draw something or else there will be a hole! So draw a blank tile.
            // UID = -1 is the special UID code for a blank.
            if (_firstTileDrawCommandForTile == 0L)
            {
                //OE_INFO << LC << "Adding blank render for tile " << _currentTileNode->getKey().str() << std::endl;
                DrawTileCommand* cmd = addDrawCommand(-1, &renderModel, 0L, _currentTileNode);
                if (cmd)
                    cmd->_order = 0;
            }

            // Set the layer order of the first draw command for this tile to zero,
            // to support proper terrain blending.
            if (_firstTileDrawCommandForTile)
            {
                _firstTileDrawCommandForTile->_order = 0;
            }
                
            // pop the matrix from the cull stack
            popModelViewMatrix();

            // update our bounds
            _terrain._drawState->_bs.expandBy(surface->getBound());
            _terrain._drawState->_box.expandBy(_terrain._drawState->_bs);
        }
    }

    // Handle any CullCallbacks and traverse.
    osg::Callback* cullCallback = node.getCullCallback();
    if (cullCallback) cullCallback->run(&node, this);
    else traverse(node);
}
Example #4
0
DrawTileCommand*
TerrainCuller::addDrawCommand(UID uid, const TileRenderModel* model, const RenderingPass* pass, TileNode* tileNode)
{
    SurfaceNode* surface = tileNode->getSurfaceNode();

    const RenderBindings& bindings = _context->getRenderBindings();

    // skip layers that are not visible:
    if (pass && 
        pass->visibleLayer() && 
        pass->visibleLayer()->getVisible() == false)
    {
        //OE_DEBUG << LC << "Skipping " << pass->visibleLayer()->getName() << " because it's not visible." << std::endl;
        return 0L;
    }

    // add a new Draw command to the appropriate layer
    osg::ref_ptr<LayerDrawable> drawable = _terrain.layer(uid);
    if (drawable.valid())
    {
        // Layer marked for drawing?
        if (drawable->_draw)
        {
            // Cull based on the layer extent.
            if (drawable->_layer)
            {
                const LayerExtent& le = (*_layerExtents)[drawable->_layer->getUID()];
                if (le._computed && 
                    le._extent.isValid() &&
                    le._extent.intersects(tileNode->getKey().getExtent()) == false)
                {
                    // culled out!
                    //OE_DEBUG << LC << "Skippping " << drawable->_layer->getName() 
                    //    << " key " << tileNode->getKey().str()
                    //    << " because it was culled by extent." << std::endl;
                    return 0L;
                }
            }

            drawable->_tiles.push_back(DrawTileCommand());
            DrawTileCommand* tile = &drawable->_tiles.back();

            // install everything we need in the Draw Command:
            tile->_colorSamplers = pass ? &(pass->samplers()) : 0L;
            tile->_sharedSamplers = &model->_sharedSamplers;
            tile->_modelViewMatrix = this->getModelViewMatrix();
            tile->_keyValue = tileNode->getTileKeyValue();
            tile->_geom = surface->getDrawable()->_geom.get();
            tile->_morphConstants = tileNode->getMorphConstants();
            tile->_key = &tileNode->getKey();
            tile->_order = drawable->_order; // layer order in map tile.

            osg::Vec3 c = surface->getBound().center() * surface->getInverseMatrix();
            tile->_range = getDistanceToViewPoint(c, true);

            const osg::Image* elevRaster = tileNode->getElevationRaster();
            if (elevRaster)
            {
                float bias = _context->getUseTextureBorder() ? 1.5 : 0.5;

                // Compute an elevation texture sampling scale/bias so we sample elevation data on center
                // instead of on edge (as we do with color, etc.)
                //
                // This starts out as:
                //   scale = (size-1)/size : this shrinks the sample area by one texel since we're sampling on center
                //   bias = 0.5/size : this shifts the sample area over 1/2 texel to the center.
                //
                // But, since we also have a 1-texel border, we need to further reduce the scale by 2 texels to
                // remove the border, and shift an extra texel over as well. Giving us this:
                float size = (float)elevRaster->s();
                tile->_elevTexelCoeff.set((size - (2.0*bias)) / size, bias / size);
            }

            return tile;
        }
    }
    else if (pass)
    {
        // The pass exists but it's layer is not in the render data draw list.
        // This means that the layer is no longer in the map. Detect and record
        // this information so we can run a cleanup visitor later on.
        ++_orphanedPassesDetected;
    }
    
    return 0L;
}
Example #5
0
TileNode*
TileGroupFactory::createTileNodeGraph(TerrainTileModel* model,
                                      bool              setupChildrenIfNecessary,
                                      ProgressCallback* progress)
{
    // TODO: fix this
    const unsigned tileSize = 17;

    // Build the surface node.
    SurfaceNodeFactory factory(model, _frame, _renderBindings, _geometryPool, tileSize, _options);
    SurfaceNode* surfaceNode = factory.createSurfaceNode();

    surfaceNode->setEngineUID( _terrainEngine->getUID() );

    // see if this tile might have children.
    bool prepareForChildren =
        setupChildrenIfNecessary &&
        model->getKey().getLOD() < *_options.maxLOD();
    
    // Build the Tile Node that will hold all the textures and texture matrices.
    TileNode* tileNode = createTileNode(
        model,
        progress );

    // Build the paging node that will load subtiles, if necessary:
    if ( prepareForChildren )
    {
        osg::BoundingSphere bs = surfaceNode->getBound();
        TilePagedLOD* plod = new TilePagedLOD( _terrainEngine->getUID(), _liveTiles, _deadTiles );
        plod->setCenter  ( bs.center() );
        plod->addChild   ( surfaceNode );
        plod->setFileName( 1, Stringify() 
            << model->getKey().str()
            << "." << _terrainEngine->getUID()
            << ".osgearth_engine_mp_tile" );

        if ( _options.rangeMode().value() == osg::LOD::DISTANCE_FROM_EYE_POINT )
        {
            //Compute the min range based on the 2D size of the tile
            GeoExtent extent = model->getKey().getExtent();
            GeoPoint lowerLeft(extent.getSRS(), extent.xMin(), extent.yMin(), 0.0, ALTMODE_ABSOLUTE);
            GeoPoint upperRight(extent.getSRS(), extent.xMax(), extent.yMax(), 0.0, ALTMODE_ABSOLUTE);
            osg::Vec3d ll, ur;
            lowerLeft.toWorld( ll );
            upperRight.toWorld( ur );
            double radius = (ur - ll).length() / 2.0;
            float minRange = (float)(radius * _options.minTileRangeFactor().value());

            plod->setRange( 0, minRange, FLT_MAX );
            plod->setRange( 1, 0, minRange );
            plod->setRangeMode( osg::LOD::DISTANCE_FROM_EYE_POINT );
        }
        else
        {
            plod->setRange( 0, 0.0f, _options.tilePixelSize().value() );
            plod->setRange( 1, _options.tilePixelSize().value(), FLT_MAX );
            plod->setRangeMode( osg::LOD::PIXEL_SIZE_ON_SCREEN );
        }
        
#if 0 // TODO: reinstate this!

        // Install a tile-aligned bounding box in the pager node itself so we can do
        // visibility testing before paging in subtiles.
        plod->setChildBoundingBoxAndMatrix(
            1,
            surfaceNode->getTerrainBoundingBox(),
            surfaceNode->getLocalToWorldMatrix() );
#endif

#if USE_FILELOCATIONCALLBACK
        osgDB::Options* options = plod->getOrCreateDBOptions();
        options->setFileLocationCallback( new FileLocationCallback() );
#endif
        
        tileNode->addChild( plod );

        // Install a callback to reject back-facing tiles.
        if ( _frame.getMapInfo().isGeocentric() && _options.clusterCulling() == true )
        {
            const osg::HeightField* heightField = model->elevationModel()->getHeightField();
            if ( heightField )
            {
                tileNode->addCullCallback( HeightFieldUtils::createClusterCullingCallback(
                    heightField,
                    tileNode->getKey().getProfile()->getSRS()->getEllipsoid(),
                    *_options.verticalScale() ) );
            }
        }
    }
    else
    {
        tileNode->addChild( surfaceNode );
    }

    return tileNode;
}