osgEarth数据调度

  • 申请数据:

​ 申请数据主要是在RexTerrainEngineNode::traverse(osg::NodeVisitor& nv)函数中进行cull时,对需要加载的数据提出加载申请:

​ 主要步骤:

  1. 创建TerrainCuller对象,使用其对terrian进行裁剪遍历
  2. 遍历terrain的所有layer,保存渲染状态,对每一个layer的数据进行裁剪
  3. 对geometryPool执行裁剪
  4. 对loader执行裁剪遍历
  5. 对unloader执行裁剪遍历
  6. 对releaser执行裁剪遍历
  7. 对rasterizer执行裁剪遍历
void
RexTerrainEngineNode::traverse(osg::NodeVisitor& nv)
{
    if ( nv.getVisitorType() == nv.CULL_VISITOR )
    {
        osgUtil::CullVisitor* cv = static_cast<osgUtil::CullVisitor*>(&nv);
        // Marks the start of the cull pass
        getEngineContext()->startCull( cv );
        // Initialize a new culler
        TerrainCuller culler(cv, this->getEngineContext());
        // Prepare the culler with the set of renderable layers:
        culler.setup(getMap(), _cachedLayerExtents, this->getEngineContext()->getRenderBindings());

        // Assemble the terrain drawables:
        _terrain->accept(culler);

        // If we're using geometry pooling, optimize the drawable for shared state
        // by sorting the draw commands.
        // TODO: benchmark this further to see whether it's worthwhile
        unsigned totalTiles = 0L;
        if (getEngineContext()->getGeometryPool()->isEnabled())
        {
            totalTiles = culler._terrain.sortDrawCommands();
        }

        // The common stateset for the terrain group:
        cv->pushStateSet(_terrain->getOrCreateStateSet());

        // Push all the layers to draw on to the cull visitor in the order in which
        // they appear in the map.
        LayerDrawable* lastLayer = 0L;
        unsigned order = 0;
        bool surfaceStateSetPushed = false;
        bool imageLayerStateSetPushed = false;
        int layersDrawn = 0;

        osg::State::StateSetStack stateSetStack;

        for(LayerDrawableList::iterator i = culler._terrain.layers().begin();
            i != culler._terrain.layers().end();
            ++i)
        {
        // Note: Cannot save lastLayer here because its _tiles may be empty, which can lead to a crash later
            if (!i->get()->_tiles.empty())
            {
                lastLayer = i->get();

                // if this is a RENDERTYPE_TERRAIN_SURFACE, we need to activate either the
                // default surface state set or the image layer state set.
                if (lastLayer->_renderType == Layer::RENDERTYPE_TERRAIN_SURFACE)
                {
                    if (!surfaceStateSetPushed)
                    {
                        cv->pushStateSet(_surfaceStateSet.get());
                        surfaceStateSetPushed = true;
                    }

                    if (lastLayer->_imageLayer || lastLayer->_layer == NULL)
                    {
                        if (!imageLayerStateSetPushed)
                        {
                            cv->pushStateSet(_imageLayerStateSet.get());
                            imageLayerStateSetPushed = true;
                        }
                    }
                    else
                    {
                        if (imageLayerStateSetPushed)
                        {
                            cv->popStateSet();
                            imageLayerStateSetPushed = false;
                        }
                    }
                }

                else
                {
                    if (imageLayerStateSetPushed)
                    {
                        cv->popStateSet();
                        imageLayerStateSetPushed = false;
                    }
                    if (surfaceStateSetPushed)
                    {
                        cv->popStateSet();
                        surfaceStateSetPushed = false;
                    }
                }

                //OE_INFO << "   Apply: " << (lastLayer->_layer ? lastLayer->_layer->getName() : "-1") << "; tiles=" << lastLayer->_tiles.size() << std::endl;
                //buf << (lastLayer->_layer ? lastLayer->_layer->getName() : "none") << " (" << lastLayer->_tiles.size() << ")\n";

                if (lastLayer->_layer)
                {
                    lastLayer->_layer->apply(lastLayer, cv);                    
                }
                else
                {
                    lastLayer->accept(*cv);
                }

                ++layersDrawn;
            }

            //buf << (lastLayer->_layer ? lastLayer->_layer->getName() : "none") << " (" << lastLayer->_tiles.size() << ")\n";
        }

        // Uncomment this to see how many layers were drawn
        //Registry::instance()->startActivity("Layers", Stringify()<<layersDrawn);

        // The last layer to render must clear up the OSG state,
        // otherwise it will be corrupt and can lead to crashing.
        if (lastLayer)
        {
            lastLayer->_clearOsgState = true;
        }

        if (imageLayerStateSetPushed)
        {
            cv->popStateSet();
            imageLayerStateSetPushed = false;
        }

        if (surfaceStateSetPushed)
        {
            cv->popStateSet();
            surfaceStateSetPushed = false;
        }

        // pop the common terrain state set
        cv->popStateSet();

        // marks the end of the cull pass
        this->getEngineContext()->endCull( cv );

        // If the culler found any orphaned data, we need to update the render model
        // during the next update cycle.
        if (culler._orphanedPassesDetected > 0u)
        {
            _renderModelUpdateRequired = true;
            OE_INFO << LC << "Detected " << culler._orphanedPassesDetected << " orphaned rendering passes\n";
        }

        // we don't call this b/c we don't want _terrain
        //TerrainEngineNode::traverse(nv);

        // traverse all the other children (geometry pool, loader/unloader, etc.)
        _geometryPool->accept(nv);
        _loader->accept(nv);
        _unloader->accept(nv);
        _releaser->accept(nv);

        if (_rasterizer)
            _rasterizer->accept(nv);
    }
}

对地形内部进行裁剪,进入到tilenode的裁剪遍历,每个tileNode,都有四个child(quadtree),逐个执行裁剪遍历,如果某个child没有加载,设置dirty状态,执行loader加载待加载的子节点

bool TileNode::cull(TerrainCuller* culler)
{
    EngineContext* context = culler->getEngineContext();

    // Horizon check the surface first:
    if (!_surface->isVisibleFrom(culler->getViewPointLocal()))
    {
        return false;
    }
    
    // determine whether we can and should subdivide to a higher resolution:
    bool childrenInRange = shouldSubDivide(culler, context->getSelectionInfo());

    // whether it is OK to create child TileNodes is necessary.
    bool canCreateChildren = childrenInRange;

    // whether it is OK to load data if necessary.
    bool canLoadData = true;

    // whether to accept the current surface node and not the children.
    bool canAcceptSurface = false;
    
    // Don't load data in progressive mode until the parent is up to date
    if (context->getOptions().progressive() == true)
    {
        TileNode* parent = getParentTile();
        if ( parent && parent->isDirty() )
        {
            canLoadData = false;
        }
    }
    
    // If this is an inherit-viewpoint camera, we don't need it to invoke subdivision
    // because we want only the tiles loaded by the true viewpoint.
    const osg::Camera* cam = culler->getCamera();
    if ( cam && cam->getReferenceFrame() == osg::Camera::ABSOLUTE_RF_INHERIT_VIEWPOINT )
    {
        canCreateChildren = false;
        canLoadData       = false;
    }

    if (childrenInRange)
    {
        // We are in range of the child nodes. Either draw them or load them.

        // If the children don't exist, create them and inherit the parent's data.
        if ( !_childrenReady && canCreateChildren )
        {
            _mutex.lock();

            if ( !_childrenReady )
            {
                OE_START_TIMER(createChildren);
                createChildren( context );
                REPORT("TileNode::createChildren", createChildren);
                _childrenReady = true;

                // This means that you cannot start loading data immediately; must wait a frame.
                canLoadData = false;
            }

            _mutex.unlock();
        }

        // If all are ready, traverse them now.
        if ( _childrenReady )
        {
            for(int i=0; i<4; ++i)
            {
                TileNode* child = getSubTile(i);
                if (child)
                    child->accept(*culler);
            }
        }

        // If we don't traverse the children, traverse this node's payload.
        else
        {
            canAcceptSurface = true;
        }
    }

    // If children are outside camera range, draw the payload and expire the children.
    else
    {
        canAcceptSurface = true;
    }

    // accept this surface if necessary.
    if ( canAcceptSurface )
    {
        _surface->accept( *culler );
    }

    // If this tile is marked dirty, try loading data.
    if ( _dirty && canLoadData )
    {
        load( culler );
    }

    return true;
}

如果child无效,创建childTile

 if ( !_childrenReady )
 {
     OE_START_TIMER(createChildren);
     createChildren( context );
     REPORT("TileNode::createChildren", createChildren);
     _childrenReady = true;

     // This means that you cannot start loading data immediately; must wait a frame.
     canLoadData = false;
 }

创建子child和节点

void
TileNode::createChildren(EngineContext* context)
{
    // NOTE: Ensure that _mutex is locked before calling this function!
    //OE_WARN << "Creating children for " << _key.str() << std::endl;

    // Create the four child nodes.
    for(unsigned quadrant=0; quadrant<4; ++quadrant)
    {
        TileNode* node = new TileNode();
        if (context->getOptions().minExpiryFrames().isSet())
        {
            node->setMinimumExpirationFrames( *context->getOptions().minExpiryFrames() );
        }
        if (context->getOptions().minExpiryTime().isSet())
        {         
            node->setMinimumExpirationTime( *context->getOptions().minExpiryTime() );
        }

        // Build the surface geometry:
        node->create( getKey().createChildKey(quadrant), this, context );

        // Add to the scene graph.
        addChild( node );
    }
}

创建子child,只创建了一个平面,没有高程和图片,需要提交,让后台线程加载图片和高程数据并应用到geometry中:

TileNode::create(const TileKey& key, TileNode* parent, EngineContext* context)
{
  .............
    // Get a shared geometry from the pool that corresponds to this tile key:
    osg::ref_ptr<SharedGeometry> geom;
    context->getGeometryPool()->getPooledGeometry(
        key,
        MapInfo(map.get()),
        tileSize,
        masks.get(), 
        geom);

    // Create the drawable for the terrain surface:
  TileDrawable* surfaceDrawable = new TileDrawable(key, geom.get(),context->getOptions().tileSize().get() );

    // Give the tile Drawable access to the render model so it can properly
    // calculate its bounding box and sphere.
    surfaceDrawable->setModifyBBoxCallback(context->getModifyBBoxCallback());

    // Create the node to house the tile drawable:
    _surface = new SurfaceNode(key,MapInfo(map.get()),context->getRenderBindings(),surfaceDrawable );
    
    // create a data load request for this new tile:
    _loadRequest = new LoadTileData( this, context );
    _loadRequest->setName( _key.str() );
    _loadRequest->setTileKey( _key );

    // whether the stitch together normal maps for adjacent tiles.
    _stitchNormalMap = context->_options.normalizeEdges() == true;

    // Encode the tile key in a uniform
    unsigned tw, th;
    _key.getProfile()->getNumTiles(_key.getLOD(), tw, th);

    double x = (double)_key.getTileX();
    double y = (double)(th - _key.getTileY()-1);

    _tileKeyValue.set((float)x,(float)y,(float)_key.getLOD(),-1.0f);

    // initialize all the per-tile uniforms the shaders will need:
    float range, morphStart, morphEnd;
    context->getSelectionInfo().get(_key, range, morphStart, morphEnd);

    float one_over_end_minus_start = 1.0f/(morphEnd - morphStart);
    _morphConstants.set(morphEnd * one_over_end_minus_start, one_over_end_minus_start);

    // Make a tilekey to use for testing whether to subdivide.
    if (_key.getTileY() <= th/2)
        _subdivideTestKey = _key.createChildKey(0);
    else
        _subdivideTestKey = _key.createChildKey(3);

    // Initialize the data model by copying the parent's rendering data
    // and scale/biasing the matrices.
    if (parent)
    {
        unsigned quadrant = getKey().getQuadrant();

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

        bool setElevation = false;

        for (unsigned p = 0; p < parent->_renderModel._passes.size(); ++p)
        {
            const RenderingPass& parentPass = parent->_renderModel._passes[p];

            // If the key is now out of the layer's valid min/max range, skip this pass.
            if (!passInLegalRange(parentPass))
                continue;

            // Copy the parent pass:
            _renderModel._passes.push_back(parentPass);
            RenderingPass& myPass = _renderModel._passes.back();

            // Scale/bias each matrix for this key quadrant.
            Samplers& samplers = myPass.samplers();
            for (unsigned s = 0; s < samplers.size(); ++s)
            {
                samplers[s]._matrix.preMult(scaleBias[quadrant]);
            }

            // Are we using image blending? If so, initialize the color_parent 
            // to the color texture.
            if (bindings[SamplerBinding::COLOR_PARENT].isActive())
            {
                samplers[SamplerBinding::COLOR_PARENT] = samplers[SamplerBinding::COLOR];
            }
        }

        // Copy the parent's shared samplers and scale+bias each matrix to the new quadrant:
        _renderModel._sharedSamplers = parent->_renderModel._sharedSamplers;

        for (unsigned s = 0; s<_renderModel._sharedSamplers.size(); ++s)
        {
            Sampler& sampler = _renderModel._sharedSamplers[s];
            sampler._matrix.preMult(scaleBias[quadrant]);
        }

        // Use the elevation sampler to initialize the elevation raster
        // (used for primitive functors, intersection, etc.)
        if (!setElevation && bindings[SamplerBinding::ELEVATION].isActive())
        {
            const Sampler& elevation = _renderModel._sharedSamplers[SamplerBinding::ELEVATION];
            if (elevation._texture.valid())
            {
                setElevationRaster(elevation._texture->getImage(0), elevation._matrix);
                setElevation = true;
            }
        }
    }

    // need to recompute the bounds after adding payload:
    dirtyBound();

    // signal the tile to start loading data:
    setDirty( true );

    // register me.
    context->liveTiles()->add( this );

    // tell the world.
    OE_DEBUG << LC << "notify (create) key " << getKey().str() << std::endl;
    context->getEngine()->getTerrain()->notifyTileAdded(getKey(), this);
}

创建一个平面:

SharedGeometry*
GeometryPool::createGeometry(const TileKey& tileKey,
                             const MapInfo& mapInfo,
                             unsigned       tileSize,
                             MaskGenerator* maskSet) const
{    
    // Establish a local reference frame for the tile:
    osg::Vec3d centerWorld;
    GeoPoint centroid;
    tileKey.getExtent().getCentroid( centroid );
    centroid.toWorld( centerWorld );

    osg::Matrix world2local, local2world;
    centroid.createWorldToLocal( world2local );
    local2world.invert( world2local );

    // Attempt to calculate the number of verts in the surface geometry.
    bool createSkirt = _options.heightFieldSkirtRatio() > 0.0f;

    unsigned numVertsInSurface    = (tileSize*tileSize);
    unsigned numVertsInSkirt      = createSkirt ? tileSize*4u - 4u : 0;
    unsigned numVerts             = numVertsInSurface + numVertsInSkirt;    
    unsigned numIndiciesInSurface = (tileSize-1) * (tileSize-1) * 6;
    unsigned numIncidesInSkirt    = getNumSkirtElements(tileSize);
    
    // TODO: reconsider this ... 
    GLenum mode = (_options.gpuTessellation() == true) ? GL_PATCHES : GL_TRIANGLES;

    osg::BoundingSphere tileBound;

    // the geometry:
    osg::ref_ptr<SharedGeometry> geom = new SharedGeometry();
    geom->setUseVertexBufferObjects(true);
    //geom->setUseDisplayList(false);

    osg::ref_ptr<osg::VertexBufferObject> vbo = new osg::VertexBufferObject();

    // Pre-allocate enough space for all triangles.
    osg::DrawElements* primSet = new osg::DrawElementsUShort(mode);
    primSet->setElementBufferObject(new osg::ElementBufferObject());
    primSet->reserveElements(numIndiciesInSurface + numIncidesInSkirt);
    geom->setDrawElements(primSet);

    // the vertex locations:
    osg::ref_ptr<osg::Vec3Array> verts = new osg::Vec3Array();
    verts->setVertexBufferObject(vbo.get());
    verts->reserve( numVerts );
    verts->setBinding(verts->BIND_PER_VERTEX);
    geom->setVertexArray( verts.get() );

    // the surface normals (i.e. extrusion vectors)
    osg::ref_ptr<osg::Vec3Array> normals = new osg::Vec3Array();
    normals->setVertexBufferObject(vbo.get());
    normals->reserve( numVerts );
    normals->setBinding(normals->BIND_PER_VERTEX);
    geom->setNormalArray( normals.get() );
    
    osg::ref_ptr<osg::Vec3Array> neighbors = 0L;
    osg::ref_ptr<osg::Vec3Array> neighborNormals = 0L;
    if ( _options.morphTerrain() == true )
    {
        // neighbor positions (for morphing)
        neighbors = new osg::Vec3Array();
        neighbors->setBinding(neighbors->BIND_PER_VERTEX);
        neighbors->setVertexBufferObject(vbo.get());
        neighbors->reserve( numVerts );
        geom->setNeighborArray(neighbors.get());
        
        neighborNormals = new osg::Vec3Array();
        neighborNormals->setVertexBufferObject(vbo.get());
        neighborNormals->reserve( numVerts );
        neighborNormals->setBinding(neighborNormals->BIND_PER_VERTEX);
        geom->setNeighborNormalArray( neighborNormals.get() );
    }

    // tex coord is [0..1] across the tile. The 3rd dimension tracks whether the
    // vert is masked: 0=yes, 1=no
#ifdef SHARE_TEX_COORDS
    bool populateTexCoords = false;
    if ( !_sharedTexCoords.valid() )
    {
        _sharedTexCoords = new osg::Vec3Array();
        _sharedTexCoords->reserve( numVerts );
        populateTexCoords = true;
    }    
    osg::Vec3Array* texCoords = _sharedTexCoords.get();
#else
    bool populateTexCoords = true;
    osg::ref_ptr<osg::Vec3Array> texCoords = new osg::Vec3Array();
    texCoords->setBinding(texCoords->BIND_PER_VERTEX);
    texCoords->setVertexBufferObject(vbo.get());
    texCoords->reserve( numVerts );
#endif

    geom->setTexCoordArray(texCoords.get());
    
    float delta = 1.0/(tileSize-1);
    osg::Vec3d tdelta(delta,0,0);
    tdelta.normalize();
    osg::Vec3d vZero(0,0,0);

    osg::ref_ptr<GeoLocator> locator = GeoLocator::createForKey( tileKey, mapInfo );

    for(unsigned row=0; row<tileSize; ++row)
    {
        float ny = (float)row/(float)(tileSize-1);
        for(unsigned col=0; col<tileSize; ++col)
        {
            float nx = (float)col/(float)(tileSize-1);

            osg::Vec3d model;
            locator->unitToModel(osg::Vec3d(nx, ny, 0.0f), model);
            osg::Vec3d modelLTP = model*world2local;
            verts->push_back( modelLTP );
            tileBound.expandBy( verts->back() );

         
            osg::Vec3d modelPlusOne;
            // 创建单位几何,是平面还是球面
            locator-> unitToModel(osg::Vec3d(nx, ny, 1.0f), modelPlusOne);
            osg::Vec3d normal = (modelPlusOne*world2local)-modelLTP;                
            normal.normalize();
            normals->push_back( normal );
        }
    }

    // tessellateSurface
    
    // skirt mesh
    
		....
    return geom.release();
}

平面/球面坐标变换:

bool Locator::convertLocalToModel(const osg::Vec3d& local, osg::Vec3d& world) const
{
    switch(_coordinateSystemType)
    {
        case(GEOCENTRIC):
        {
            osg::Vec3d geographic = local * _transform;

            _ellipsoidModel->convertLatLongHeightToXYZ(geographic.y(), geographic.x(), geographic.z(),
                                                       world.x(), world.y(), world.z());
            return true;
        }
        case(GEOGRAPHIC):
        {
            world = local * _transform;
            return true;
        }
        case(PROJECTED):
        {
            world = local * _transform;
            return true;
        }
    }

    return false;
}

LoadData 对象,即为申请数据:

   _loadRequest = new LoadTileData( this, context );

​ loaderd对数据加载,起始是将loadData转换为osg的datapager所使用的加载请求:

bool PagerLoader::load(Loader::Request* request, float priority, osg::NodeVisitor& nv)
{
    // check that the request is not already completed but unmerged:
    if ( request && !request->isMerging() && !request->isFinished() && nv.getDatabaseRequestHandler() )
    {
        //OE_INFO << LC << "load (" << request->getTileKey().str() << ")" << std::endl;

        unsigned fn = 0;
        if ( nv.getFrameStamp() )
        {
            fn = nv.getFrameStamp()->getFrameNumber();
            request->setFrameNumber( fn );
        }

        bool addToRequestSet = false;

        // lock the request since multiple cull traversals might hit this function.
        request->lock();
        {
            request->setState(Request::RUNNING);
            
            // remember the last tick at which this request was submitted
            request->_lastTick = osg::Timer::instance()->tick();

            // update the priority, scale and bias it, and then normalize it to [0..1] range.
            unsigned lod = request->getTileKey().getLOD();
            float p = priority * _priorityScales[lod] + _priorityOffsets[lod];            
            request->_priority = p / (float)(_numLODs+1);

            // timestamp it
            request->setFrameNumber( fn );

            // incremenet the load count.
            request->_loadCount++;

            // if this is the first load request since idle, we need to remember this request.
            addToRequestSet = (request->_loadCount == 1);
        }
        request->unlock();

        char filename[64];
        //sprintf(filename, "%u.%u.osgearth_rex_loader", request->_uid, _engineUID);
        sprintf(filename, "%u.osgearth_rex_loader", request->_uid);

        nv.getDatabaseRequestHandler()->requestNodeFile(
            filename,
            _myNodePath,
            request->_priority,
            nv.getFrameStamp(),
            request->_internalHandle,
            _dboptions.get() );

        // remember the request:
        if ( true ) // addToRequestSet // Not sure whether we need to keep doing this in order keep it sorted -- check it out.
        {
            Threading::ScopedMutexLock lock( _requestsMutex );
            _requests[request->getUID()] = request;
        }

        return true;
    }
    return false;
}

  • 加载数据数据:

osgDB::DatabasePager::DatabaseThread::run()线程中加载数据,创建mesh和网络申请图片:

void
LoadTileData::invoke(ProgressCallback* progress)
{
    osg::ref_ptr<TileNode> tilenode;
    if (!_tilenode.lock(tilenode))
        return;

    osg::ref_ptr<TerrainEngineNode> engine;
    if (!_engine.lock(engine))
        return;

    osg::ref_ptr<const Map> map;
    if (!_map.lock(map))
        return;

    // Assemble all the components necessary to display this tile
    _dataModel = engine->createTileModel(
        map.get(),
        tilenode->getKey(),
        _filter,
        _enableCancel? progress : 0L);

    // if the operation was canceled, set the request to idle and delete the tile model.
    if (progress && progress->isCanceled())
    {
        _dataModel = 0L;
        setState(Request::IDLE);
    }
}

  • 更新到场景树

    1. 事件更新中更新数据:

    由加载线程加载的数据,需要在eventtraverse的遍历中,多级遍历后调用到 PagerLoader:traverse遍历,对request进行合并

    void
    PagerLoader::traverse(osg::NodeVisitor& nv)
    {
        // only called when _mergesPerFrame > 0
        if ( nv.getVisitorType() == nv.EVENT_VISITOR )
        {
            if ( nv.getFrameStamp() )
            {
                setFrameStamp(nv.getFrameStamp());
            }
    
            // process pending merges.
            {
                METRIC_BEGIN("loader.merge");
                int count;
                for(count=0; count < _mergesPerFrame && !_mergeQueue.empty(); ++count)
                {
                    Request* req = _mergeQueue.begin()->get();
                    if ( req && req->_lastTick >= _checkpoint )
                    {
                        OE_START_TIMER(req_apply);
                        req->apply( getFrameStamp() );
                        double s = OE_STOP_TIMER(req_apply);
    
                        req->setState(Request::FINISHED);
                    }
    
                    _mergeQueue.erase( _mergeQueue.begin() );
                }
                METRIC_END("loader.merge");
            }
    
            // cull finished requests.
            {
                METRIC_SCOPED("loader.cull");
    
                Threading::ScopedMutexLock lock( _requestsMutex );
    
                unsigned fn = 0;
                if ( nv.getFrameStamp() )
                    fn = nv.getFrameStamp()->getFrameNumber();
    
                // Purge expired requests.
                for(Requests::iterator i = _requests.begin(); i != _requests.end(); )
                {
                    Request* req = i->second.get();
                    const unsigned frameDiff = fn - req->getLastFrameSubmitted();
    
                    // Deal with completed requests:
                    if ( req->isFinished() )
                    {
                        //OE_INFO << LC << req->getName() << "(" << i->second->getUID() << ") finished." << std::endl; 
                        req->setState( Request::IDLE );
                        if ( REPORT_ACTIVITY )
                            Registry::instance()->endActivity( req->getName() );
                        _requests.erase( i++ );
                    }
    
                    // Discard requests that are no longer required:
                    else if ( !req->isMerging() && frameDiff > 2 )
                    {
                        //OE_INFO << LC << req->getName() << "(" << i->second->getUID() << ") died waiting after " << frameDiff << " frames" << std::endl; 
                        req->setState( Request::IDLE );
                        if ( REPORT_ACTIVITY )
                            Registry::instance()->endActivity( req->getName() );
                        _requests.erase( i++ );
                    }
    
                    // Prevent a request from getting stuck in the merge queue:
                    else if ( req->isMerging() && frameDiff > 1800 )
                    {
                        //OE_INFO << LC << req->getName() << "(" << i->second->getUID() << ") died waiting " << frameDiff << " frames to merge" << std::endl; 
                        req->setState( Request::IDLE );
                        if ( REPORT_ACTIVITY )
                            Registry::instance()->endActivity( req->getName() );
                        _requests.erase( i++ );
                    }
    
                    else // still valid.
                    {
                        ++i;
                    }
                }
    
                //OE_NOTICE << LC << "PagerLoader: requests=" << _requests.size() << "; mergeQueue=" << _mergeQueue.size() << std::endl;
            }
        }
    
        LoaderGroup::traverse( nv );
    }
    
    

    LoadTileData进行数据merge

    void
    LoadTileData::apply(const osg::FrameStamp* stamp)
    {
        osg::ref_ptr<EngineContext> context;
        if (!_context.lock(context))
            return;
    
        osg::ref_ptr<const Map> map;
        if (!_map.lock(map))
            return;
    
        // ensure we got an actual datamodel:
        if (_dataModel.valid())
        {
            // ensure it's in sync with the map revision (not out of date):
            if (map.valid() && _dataModel->getRevision() == map->getDataModelRevision())
            {
                // ensure the tile node hasn't expired:
                osg::ref_ptr<TileNode> tilenode;
                if ( _tilenode.lock(tilenode) )
                {
                    const RenderBindings& bindings = context->getRenderBindings();
    
                    // Merge the new data into the tile.
                    tilenode->merge(_dataModel.get(), bindings);
    
                    // Mark as complete. TODO: per-data requests will do something different.
                    tilenode->setDirty( false );
    
                    OE_DEBUG << LC << "apply " << _dataModel->getKey().str() << "\n";
                }
                else
                {
                    OE_DEBUG << LC << "LoadTileData failed; TileNode disappeared\n";
                }
            }
            else
            {
                OE_INFO << LC << "apply " << _dataModel->getKey().str() << " ignored b/c it is out of date\n";
            }
    
            // Delete the model immediately
            _dataModel = 0L;
        }
    }
    

    然后调用TileNode::merge

    void
    TileNode::merge(const TerrainTileModel* model, const RenderBindings& bindings)
    {
        bool newElevationData = false;
    
        const SamplerBinding& color = bindings[SamplerBinding::COLOR];
        if (color.isActive())
        {
            for(TerrainTileColorLayerModelVector::const_iterator i = model->colorLayers().begin();
                i != model->colorLayers().end();
                ++i)
            {
                TerrainTileImageLayerModel* model = dynamic_cast<TerrainTileImageLayerModel*>(i->get());
                if (model)
                {
                    if (model->getTexture())
                    {
                        RenderingPass* pass = _renderModel.getPass(model->getImageLayer()->getUID());
                        if (!pass)
                        {
                            pass = &_renderModel.addPass();
                            pass->setLayer(model->getLayer());
    
                            // This is a new pass that just showed up at this LOD
                            // Since it just arrived at this LOD, make the parent the same as the color.
                            if (bindings[SamplerBinding::COLOR_PARENT].isActive())
                            {
                                pass->samplers()[SamplerBinding::COLOR_PARENT]._texture = model->getTexture();
                                pass->samplers()[SamplerBinding::COLOR_PARENT]._matrix = *model->getMatrix();
                            }
                        }
                        pass->samplers()[SamplerBinding::COLOR]._texture = model->getTexture();
                        pass->samplers()[SamplerBinding::COLOR]._matrix = *model->getMatrix();
    
                        // Handle an RTT image layer:
                        if (model->getImageLayer() && model->getImageLayer()->useCreateTexture())
                        {
                            // Check the texture's userdata for a Node. If there is one there,
                            // render it to the texture using the Tile Rasterizer service.
                            // TODO: consider hanging on to this texture and not applying it to
                            // the live tile until the RTT is complete. (Prevents unsightly flashing)
                            GeoNode* rttNode = dynamic_cast<GeoNode*>(model->getTexture()->getUserData());
    
                            if (rttNode && _context->getTileRasterizer())
                            {
                                _context->getTileRasterizer()->push(rttNode->_node.get(), model->getTexture(), rttNode->_extent);
                            }
                        }
    
                        // check to see if this data requires an image update traversal.
                        if (_imageUpdatesActive == false)
                        {
                            for(unsigned i=0; i<model->getTexture()->getNumImages(); ++i)
                            {
                                if (model->getTexture()->getImage(i)->requiresUpdateCall())
                                {
                                    ADJUST_UPDATE_TRAV_COUNT(this, +1);
                                    _imageUpdatesActive = true;
                                    break;
                                }
                            }
                        }
                    }
                }
    
                else // non-image color layer (like splatting, e.g.)
                {
                    TerrainTileColorLayerModel* model = i->get();
                    if (model && model->getLayer())
                    {
                        RenderingPass* pass = _renderModel.getPass(model->getLayer()->getUID());
                        if (!pass)
                        {
                            pass = &_renderModel.addPass();
                            pass->setLayer(model->getLayer());
                        }
                    }
                }
            }
        }
    
        // Elevation:
        const SamplerBinding& elevation = bindings[SamplerBinding::ELEVATION];
        if (elevation.isActive() && model->elevationModel().valid() && model->elevationModel()->getTexture())
        {
            osg::Texture* tex = model->elevationModel()->getTexture();
    
            // always keep the elevation image around because we use it for bounding box computation:
            tex->setUnRefImageDataAfterApply(false);
    
            _renderModel._sharedSamplers[SamplerBinding::ELEVATION]._texture = tex;
            _renderModel._sharedSamplers[SamplerBinding::ELEVATION]._matrix.makeIdentity();
    
            setElevationRaster(tex->getImage(0), osg::Matrixf::identity());
    
            newElevationData = true;
        } 
    
        // Normals:
        const SamplerBinding& normals = bindings[SamplerBinding::NORMAL];
        if (normals.isActive() && model->normalModel().valid() && model->normalModel()->getTexture())
        {
            osg::Texture* tex = model->normalModel()->getTexture();
            // keep the normal map around because we might update it later in "ping"
            tex->setUnRefImageDataAfterApply(false);
    
            _renderModel._sharedSamplers[SamplerBinding::NORMAL]._texture = tex;
            _renderModel._sharedSamplers[SamplerBinding::NORMAL]._matrix.makeIdentity();
    
            updateNormalMap();
        }
    
        // Other Shared Layers:
        for (unsigned i = 0; i < model->sharedLayers().size(); ++i)
        {
            TerrainTileImageLayerModel* layerModel = model->sharedLayers()[i].get();
            if (layerModel->getTexture())
            {
                // locate the shared binding corresponding to this layer:
                UID uid = layerModel->getImageLayer()->getUID();
                unsigned bindingIndex = INT_MAX;
                for(unsigned i=SamplerBinding::SHARED; i<bindings.size() && bindingIndex==INT_MAX; ++i) {
                    if (bindings[i].isActive() && bindings[i].sourceUID().isSetTo(uid)) {
                        bindingIndex = i;
                    }                   
                }
    
                if (bindingIndex < INT_MAX)
                {
                    osg::Texture* tex = layerModel->getTexture();
                    _renderModel._sharedSamplers[bindingIndex]._texture = tex;
                    _renderModel._sharedSamplers[bindingIndex]._matrix.makeIdentity();
                }
            }
        }
    
        // Patch Layers
        for (unsigned i = 0; i < model->patchLayers().size(); ++i)
        {
            TerrainTilePatchLayerModel* layerModel = model->patchLayers()[i].get();
        }
    
        if (_childrenReady)
        {
            for (int i = 0; i < 4; ++i)
            {
                TileNode* child = getSubTile(i);
                if (child)
                    child->refreshInheritedData(this, bindings);
            }
        }
    
        if (newElevationData)
        {
            _context->getEngine()->getTerrain()->notifyTileAdded(getKey(), this);
        }
    }
    
    1. 将node添加到场景:

      void Scene::updateSceneGraph(osg::NodeVisitor& updateVisitor)

      —》void DatabasePager::addLoadedDataToSceneGraph(const osg::FrameStamp &frameStamp)

      将节点,添加到父节点上,至此,由申请才转换为正常的场景节点:

      ```cpp
       group->addChild(databaseRequest->_loadedModel.get());
      ```
      

      进入PagerLoader,将申请,转换为node,挂到PagerLoader对象上:

      bool
      PagerLoader::addChild(osg::Node* node)
      {
          osg::ref_ptr<RequestResultNode> result = dynamic_cast<RequestResultNode*>(node);
          if ( result.valid() )
          {
              Request* req = result->getRequest();
              if ( req )
              {
                  // Make sure the request is both current (newer than the last checkpoint)
                  // and running (i.e. has not been canceled along the way)
                  if (req->_lastTick >= _checkpoint && req->isRunning())
                  {
                      if ( _mergesPerFrame > 0 )
                      {
                          _mergeQueue.insert( req );
                          req->setState( Request::MERGING );
                      }
                      else
                      {
                          req->apply( getFrameStamp() );
                          req->setState( Request::FINISHED );
                          if ( REPORT_ACTIVITY )
                              Registry::instance()->endActivity( req->getName() );
                      }
                  }                
      
                  else
                  {
                      OE_DEBUG << LC << "Request " << req->getName() << " canceled" << std::endl;
                      req->setState( Request::FINISHED );
                      if ( REPORT_ACTIVITY )
                          Registry::instance()->endActivity( req->getName() );
                  }
              }
          }
      
          else
          {
              //OE_WARN << LC << "Internal error: illegal node type in addchild" << std::endl;
          }
          return true;
      }
      
    2. addTile事件通知:

    void Terrain::update()
    {
        _updateQueue->runOperations();
    }
    
    

    operation执行添加数据:

    void Terrain::OnTileAddedOperation::operator()(osg::Object*)
    {
        if ( getKeep() == false )
            return;
    
        if (_delay-- > 0)
            return;
    
        ++_count;
        osg::ref_ptr<Terrain>   terrain;
        osg::ref_ptr<osg::Node> node;
        
        if ( _terrain.lock(terrain) && (!_key.valid() || _node.lock(node)) )
        {
            if (_key.valid())
                terrain->fireTileAdded( _key, node.get() );
            else
                terrain->fireTileAdded( _key, 0L );
    
        }
        else
        {
            // nop; tile expired; let it go.
            OE_DEBUG << "Tile expired before notification: " << _key.str() << std::endl;
        }
    
        this->setKeep( false );
    }
    

    添加到节点上

    void
    Terrain::fireTileAdded( const TileKey& key, osg::Node* node )
    {
        Threading::ScopedReadLock sharedLock( _callbacksMutex );
    
        for( CallbackList::iterator i = _callbacks.begin(); i != _callbacks.end(); )
        {       
            TerrainCallbackContext context( this );
            i->get()->onTileAdded( key, node, context );
    
            // if the callback set the "remove" flag, discard the callback.
            if ( context.markedForRemoval() )
                i = _callbacks.erase( i );
            else
                ++i;
        }
    }
    
    

    处理节点添加回调实践,检测碰撞等

        // Callback that notifies the manipulator whenever the terrain changes
        // around its center point.
        struct ManipTerrainCallback : public TerrainCallback
        {
            ManipTerrainCallback(EarthManipulator* manip) : _manip(manip) { }
            void onTileAdded(const TileKey& key, osg::Node* graph, TerrainCallbackContext& context)
            {
                osg::ref_ptr<EarthManipulator> safe;
                if ( _manip.lock(safe) )
                {
                    safe->handleTileAdded(key, graph, context);
                }
            }
            osg::observer_ptr<EarthManipulator> _manip;
        };
    
  • 18
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值