void
TileNode::load(osg::NodeVisitor& nv)
{
    // Access the context:
    EngineContext* context = static_cast<EngineContext*>( nv.getUserData() );

    // Create a new load request on demand:
    if ( !_loadRequest.valid() )
    {
        Threading::ScopedMutexLock lock(_mutex);
        if ( !_loadRequest.valid() )
        {
            _loadRequest = new LoadTileData( this, context );
            _loadRequest->setName( _key.str() );
            _loadRequest->setTileKey( _key );
        }
    }

    // Prioritize by LOD. (negated because lower order gets priority)
    float priority = - (float)getTileKey().getLOD();

    if ( context->getOptions().highResolutionFirst() == true )
        priority = -priority;

    // then sort by distance within each LOD.
    float distance = nv.getDistanceToViewPoint( getBound().center(), true );
    priority = 10.0f*priority - log10(distance);

    // testing intermediate loading idea...
    //if ( getTileKey().getLOD() == 5 )
    //    priority += 100.0f;

    // Submit to the loader.
    context->getLoader()->load( _loadRequest.get(), priority, nv );
}
Exemple #2
0
bool
TileNode::shouldSubDivide(TerrainCuller* culler, const SelectionInfo& selectionInfo)
{    
    unsigned currLOD = _key.getLOD();

    EngineContext* context = culler->getEngineContext();

    if (context->getOptions().rangeMode() == osg::LOD::PIXEL_SIZE_ON_SCREEN)
    {
        float pixelSize = -1.0;
        if (context->getEngine()->getComputeRangeCallback())
        {
            pixelSize = (*context->getEngine()->getComputeRangeCallback())(this, *culler->_cv);
        }    
        if (pixelSize <= 0.0)
        {
            pixelSize = culler->clampedPixelSize(getBound());
        }
        return (pixelSize > context->getOptions().tilePixelSize().get() * 4);
    }
    else
    {
        float range = (float)selectionInfo.visParameters(currLOD+1)._visibilityRange2;
        if (currLOD < selectionInfo.getNumLODs() && currLOD != selectionInfo.getNumLODs()-1)
        {
            return _surface->anyChildBoxIntersectsSphere(
                culler->getViewPointLocal(), 
                range,
                culler->getLODScale());
        }
    }                 
    return false;
}
Exemple #3
0
void
TileNode::load(osg::NodeVisitor& nv)
{
    // Access the context:
    EngineContext* context = VisitorData::fetch<EngineContext>(nv, ENGINE_CONTEXT_TAG);

    // Create a new load request on demand:
    if ( !_loadRequest.valid() )
    {
        Threading::ScopedMutexLock lock(_mutex);
        if ( !_loadRequest.valid() )
        {
            _loadRequest = new LoadTileData( this, context );
            _loadRequest->setName( _key.str() );
            _loadRequest->setTileKey( _key );
        }
    }

    
    // Construct the load PRIORITY: 0=lowest, 1=highest.
    
    const SelectionInfo& si = context->getSelectionInfo();
    int lod     = getTileKey().getLOD();
    int numLods = si.numLods();
    
    // LOD priority is in the range [0..numLods]
    float lodPriority = (float)lod;
    if ( context->getOptions().highResolutionFirst() == false )
        lodPriority = (float)(numLods - lod);

    float distance = nv.getDistanceToViewPoint(getBound().center(), true);

    // dist priority uis in the range [0..1]
    float distPriority = 1.0 - distance/si.visParameters(0)._visibilityRange;

    // add thenm together, and you get tiles sorted first by lodPriority (because of
    // the biggest range), and second by distance.
    float priority = lodPriority + distPriority;

    // normalize the composite priority to [0..1].
    priority /= (float)(numLods+1);

    // Submit to the loader.
    context->getLoader()->load( _loadRequest.get(), priority, nv );
}
    StaticDrawable::StaticDrawable(EngineContext& context, MvId texId, sf::IntRect texRectangle)
    {
        sf::Texture& texture = context.getTextureManager().getTexture(texId);
        mSprite.setTexture(texture);
        mSprite.setTextureRect(texRectangle);

        sf::FloatRect bounds = mSprite.getLocalBounds();
        mSprite.setOrigin(std::floor(bounds.width / 2.f), std::floor(bounds.height / 2.f));
    }
Exemple #5
0
void
TileNode::expireChildren(osg::NodeVisitor& nv)
{
    OE_DEBUG << LC << "Expiring children of " << getTileKey().str() << "\n";

    EngineContext* context = static_cast<EngineContext*>( nv.getUserData() );
    if ( !_expireRequest.valid() )
    {
        Threading::ScopedMutexLock lock(_mutex);
        if ( !_expireRequest.valid() )
        {
            _expireRequest = new ExpireTiles(this, context);
            _expireRequest->setName( getTileKey().str() + " expire" );
            _expireRequest->setTileKey( _key );
        }
    }
       
    // Low priority for expiry requests.
    const float lowPriority = -100.0f;
    context->getLoader()->load( _expireRequest.get(), lowPriority, nv );
}
Exemple #6
0
void TileNode::cull(osg::NodeVisitor& nv)
{
    if ( nv.getFrameStamp() )
    {
        _lastTraversalFrame.exchange( nv.getFrameStamp()->getFrameNumber() );
    }

    unsigned currLOD = getTileKey().getLOD();

    osgUtil::CullVisitor* cv = static_cast<osgUtil::CullVisitor*>( &nv );

    EngineContext* context = static_cast<EngineContext*>( nv.getUserData() );
    const SelectionInfo& selectionInfo = context->getSelectionInfo();

    if ( context->progress() )
        context->progress()->stats()["TileNode::cull"]++;

    // determine whether we can and should subdivide to a higher resolution:
    bool subdivide =
        shouldSubDivide(nv, selectionInfo, cv->getLODScale());

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

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


    if ( _dirty && context->getOptions().progressive() == true )
    {
        // Don't create children in progressive mode until content is in place
        canCreateChildren = false;
    }
    
    else
    {
        // 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 = cv->getCurrentCamera();
        if ( cam && cam->getReferenceFrame() == osg::Camera::ABSOLUTE_RF_INHERIT_VIEWPOINT )
        {
            canCreateChildren = false;
            canLoadData = false;
        }
    }

    optional<bool> surfaceVisible;


    // If *any* of the children are visible, subdivide.
    if (subdivide)
    {
        // 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 ( getNumChildren() == 0 && canCreateChildren )
        {
            Threading::ScopedMutexLock exclusive(_mutex);
            if ( getNumChildren() == 0 )
            {
                createChildren( context );
            }
        }

        // If all are ready, traverse them now.
        if ( getNumChildren() == 4 )
        {
            for(int i=0; i<4; ++i)
            {
                _children[i]->accept( nv );
            }
        }

        // If we don't traverse the children, traverse this node's payload.
        else if ( _surface.valid() )
        {
            surfaceVisible = acceptSurface( cv, context );
        }
    }

    // If children are outside camera range, draw the payload and expire the children.
    else if ( _surface.valid() )
    {
        surfaceVisible = acceptSurface( cv, context );

        if ( getNumChildren() >= 4 && context->maxLiveTilesExceeded() )
        {
            if (getSubTile(0)->isDormant( nv ) &&
                getSubTile(1)->isDormant( nv ) &&
                getSubTile(2)->isDormant( nv ) &&
                getSubTile(3)->isDormant( nv ))
            {
                expireChildren( nv );
            }
        }
    }

    // Traverse land cover data at this LOD.
    int zoneIndex = context->_landCoverData->_currentZoneIndex;
    if ( zoneIndex < (int)context->_landCoverData->_zones.size() )
    {
        unsigned clearMask = cv->getCurrentCamera()->getClearMask();
        bool isDepthCamera = ((clearMask & GL_COLOR_BUFFER_BIT) == 0u) && ((clearMask & GL_DEPTH_BUFFER_BIT) != 0u);
        bool isShadowCamera = osgEarth::Shadowing::isShadowCamera(cv->getCurrentCamera());

        // only consider land cover if we are capturing color OR shadow.
        if ( isShadowCamera || !isDepthCamera )
        {
            const LandCoverZone& zone = context->_landCoverData->_zones.at(zoneIndex);
            for(int i=0; i<zone._bins.size(); ++i)
            {
                bool pushedPayloadSS = false;

                const LandCoverBin& bin = zone._bins.at(i);            
                if ( bin._lod == _key.getLOD() && (!isShadowCamera || bin._castShadows) )
                {
                    if ( !pushedPayloadSS )
                    {
                        cv->pushStateSet( _payloadStateSet.get() );
                        pushedPayloadSS = true;
                    }

                    cv->pushStateSet( bin._stateSet.get() ); // hopefully groups together for rendering.

                    _landCover->accept( nv );

                    cv->popStateSet();
                }

                if ( pushedPayloadSS )
                {
                    cv->popStateSet();
                }
            }
        }
    }

    // If this tile is marked dirty, try loading data.
    if ( _dirty && canLoadData )
    {
        // Only load data if the surface would be visible to the camera
        if ( !surfaceVisible.isSet() )
        {
            surfaceVisible = _surface->isVisible(cv);
        }

        if ( surfaceVisible == true )
        {
            load( nv );
        }
        else
        {
            OE_DEBUG << LC << "load skipped for " << _key.str() << std::endl;
        }
    }
}
Exemple #7
0
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 create children in progressive mode until content is in place
    if ( _dirty && context->getOptions().progressive() == true )
    {
        canCreateChildren = 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)
            {
                getSubTile(i)->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 );
        _lastAcceptSurfaceFrame.exchange( culler->getFrameStamp()->getFrameNumber() );
    }

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

    return true;
}
 GameFinishedState::GameFinishedState(StateStack &stack, EngineContext &context)
     : GameState(stack, context)
     , mWindow(*context.getWindow())
 {
     // NOOP
 }
Exemple #9
0
void
RexTerrainEngineNode::dirtyTerrain()
{
    //TODO: scrub the geometry pool?

    // clear the loader:
    _loader->clear();

    if ( _terrain )
    {
        this->removeChild( _terrain );
    }

    // New terrain
    _terrain = new osg::Group();
    this->addChild( _terrain );

    // are we LOD blending?
    bool setupParentData = 
        _terrainOptions.morphImagery() == true || // gw: redundant?
        this->parentTexturesRequired();
    
    // reserve GPU unit for the main color texture:
    if ( _renderBindings.empty() )
    {
        setupRenderBindings();
    }

    // Calculate the LOD morphing parameters:
    unsigned maxLOD = _terrainOptions.maxLOD().getOrUse(DEFAULT_MAX_LOD);

    _selectionInfo.initialize(
        0u, // always zero, not the terrain options firstLOD
        std::min( _terrainOptions.maxLOD().get(), maxLOD ),
        _terrainOptions.tileSize().get(),
        _update_mapf->getMapInfo().getProfile(),        
        _terrainOptions.minTileRangeFactor().get() );

    // clear out the tile registry:
    if ( _liveTiles.valid() )
    {
        _liveTiles->releaseAll(_releaser.get());
    }

    // Factory to create the root keys:
    EngineContext* context = getEngineContext();

    // Build the first level of the terrain.
    // Collect the tile keys comprising the root tiles of the terrain.
    std::vector<TileKey> keys;
    _update_mapf->getProfile()->getAllKeysAtLOD( *_terrainOptions.firstLOD(), keys );

    // create a root node for each root tile key.
    OE_INFO << LC << "Creating " << keys.size() << " root keys.." << std::endl;

    unsigned child = 0;
    for( unsigned i=0; i<keys.size(); ++i )
    {
        TileNode* tileNode = new TileNode();
        if (context->getOptions().minExpiryFrames().isSet())
        {
            tileNode->setMinimumExpirationFrames( *context->getOptions().minExpiryFrames() );
        }
        if (context->getOptions().minExpiryTime().isSet())
        {         
            tileNode->setMinimumExpirationTime( *context->getOptions().minExpiryTime() );
        }
                
        // Next, build the surface geometry for the node.
        tileNode->create( keys[i], 0L, context );

        // Add it to the scene graph
        _terrain->addChild( tileNode );

        // And load the tile's data synchronously (only for root tiles).
        tileNode->loadSync( context );

    }

    updateState();

    // Call the base class
    TerrainEngineNode::dirtyTerrain();
}
void
RexTerrainEngineNode::dirtyTerrain()
{
    //TODO: scrub the geometry pool?

    // clear the loader:
    _loader->clear();

    if ( _terrain )
    {
        this->removeChild( _terrain );
    }

    // New terrain
    _terrain = new osg::Group();
    this->addChild( _terrain );

    // are we LOD blending?
    bool setupParentData = 
        _terrainOptions.morphImagery() == true || // gw: redundant?
        this->parentTexturesRequired();
    
    // reserve GPU unit for the main color texture:
    if ( _renderBindings.empty() )
    {
        setupRenderBindings();
    }

    // recalculate the LOD morphing parameters:
    //destroySelectionInfo();
    //buildSelectionInfo();

    // clear out the tile registry:
    if ( _liveTiles.valid() )
    {
        _liveTiles->moveAll( _deadTiles.get() );
    }

    // Factory to create the root keys:
    EngineContext* context = getEngineContext();

    // Build the first level of the terrain.
    // Collect the tile keys comprising the root tiles of the terrain.
    std::vector<TileKey> keys;
    _update_mapf->getProfile()->getAllKeysAtLOD( *_terrainOptions.firstLOD(), keys );

    // create a root node for each root tile key.
    OE_INFO << LC << "Creating " << keys.size() << " root keys.." << std::endl;

    unsigned child = 0;
    for( unsigned i=0; i<keys.size(); ++i )
    {
        TileNode* tileNode = new TileNode();
        if (context->getOptions().minExpiryFrames().isSet())
        {
            tileNode->setMinimumExpiryFrames( *context->getOptions().minExpiryFrames() );
        }
        if (context->getOptions().minExpiryTime().isSet())
        {         
            tileNode->setMinimumExpiryTime( *context->getOptions().minExpiryTime() );
        }
                
        // Next, build the surface geometry for the node.
        tileNode->create( keys[i], context );

        _terrain->addChild( tileNode );
    }

    updateState();

    // Call the base class
    TerrainEngineNode::dirtyTerrain();
}
Exemple #11
0
void TileNode::cull(osg::NodeVisitor& nv)
{
    osgUtil::CullVisitor* cv = static_cast<osgUtil::CullVisitor*>( &nv );

    EngineContext* context = static_cast<EngineContext*>( nv.getUserData() );
    const SelectionInfo& selectionInfo = context->getSelectionInfo();

    // record the number of drawables before culling this node:
    unsigned before = RenderBinUtils::getTotalNumRenderLeaves( cv->getRenderStage() );

    if ( context->progress() )
        context->progress()->stats()["TileNode::cull"]++;

    // determine whether we can and should subdivide to a higher resolution:
    bool subdivide = shouldSubDivide(nv, selectionInfo, cv->getLODScale());

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

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


    if ( _dirty && context->getOptions().progressive() == true )
    {
        // Don't create children in progressive mode until content is in place
        canCreateChildren = 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 = cv->getCurrentCamera();
    if ( cam && cam->getReferenceFrame() == osg::Camera::ABSOLUTE_RF_INHERIT_VIEWPOINT )
    {
        canCreateChildren = false;
        canLoadData = false;
    }

    optional<bool> surfaceVisible( false );

    if (subdivide)
    {
        // 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 ( getNumChildren() == 0 && canCreateChildren )
        {
            Threading::ScopedMutexLock exclusive(_mutex);
            if ( getNumChildren() == 0 )
            {
                createChildren( context );
            }
        }

        // If all are ready, traverse them now.
        if ( getNumChildren() == 4 )
        {
            for(int i=0; i<4; ++i)
            {
                // pre-check each child for simple bounding sphere culling, and if the check 
                // fails, unload it's children if them exist. This lets us unload dormant
                // tiles from memory as we go. If those children are visible from another
                // camera, no worries, the unload attempt will fail gracefully.
                if (!cv->isCulled(*_children[i].get()))
                {
                    _children[i]->accept( nv );
                }
                else
                {
                    context->getUnloader()->unloadChildren(getSubTile(i)->getTileKey());
                }
            }
        }

        // If we don't traverse the children, traverse this node's payload.
        else if ( _surface.valid() )
        {
            surfaceVisible = acceptSurface( cv, context );
        }
    }

    // If children are outside camera range, draw the payload and expire the children.
    else if ( _surface.valid() )
    {
        surfaceVisible = acceptSurface( cv, context );

        // if children exists, and are not in the process of loading, unload them now.
        if ( !_dirty && _children.size() > 0 )
        {
            context->getUnloader()->unloadChildren( this->getTileKey() );
        }
    }

    // See whether we actually added any drawables.
    unsigned after = RenderBinUtils::getTotalNumRenderLeaves( cv->getRenderStage() );
    bool addedDrawables = (after > before);
    
    // Only continue if we accepted at least one surface drawable.
    if ( addedDrawables )
    {
        // update the timestamp so this tile doesn't become dormant.
        _lastTraversalFrame.exchange( nv.getFrameStamp()->getFrameNumber() );
    }

    context->invokeTilePatchCallbacks( cv, getTileKey(), _payloadStateSet.get(), _patch.get() );

    // If this tile is marked dirty, try loading data.
    if ( addedDrawables && _dirty && canLoadData )
    {
        // Only load data if the surface would be visible to the camera
        if ( !surfaceVisible.isSet() )
        {
            surfaceVisible = _surface->isVisible(cv);
        }

        if ( surfaceVisible == true )
        {
            load( nv );
        }
        else
        {
            OE_DEBUG << LC << "load skipped for " << _key.str() << std::endl;
        }
    }
}
void TileNode::cull(osg::NodeVisitor& nv)
{
    if ( nv.getFrameStamp() )
    {
        _lastTraversalFrame.exchange( nv.getFrameStamp()->getFrameNumber() );
    }

    unsigned currLOD = getTileKey().getLOD();

#if OSGEARTH_REX_TILE_NODE_DEBUG_TRAVERSAL
    if (currLOD==0)
    {
        OE_INFO << LC <<"Traversing: "<<"\n";    
    }
#endif
    osgUtil::CullVisitor* cv = dynamic_cast<osgUtil::CullVisitor*>( &nv );

    EngineContext* context = static_cast<EngineContext*>( nv.getUserData() );
    const SelectionInfo& selectionInfo = context->getSelectionInfo();

    // determine whether we can and should subdivide to a higher resolution:
    bool subdivide = shouldSubDivide(nv, selectionInfo, cv->getLODScale());

    // 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.
    bool canCreateChildren = subdivide;
    const osg::Camera* cam = cv->getCurrentCamera();
    if ( cam && cam->getReferenceFrame() == osg::Camera::ABSOLUTE_RF_INHERIT_VIEWPOINT )
    {
        canCreateChildren = false;
    }

    // If *any* of the children are visible, subdivide.
    if (subdivide)
    {
        // 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 ( getNumChildren() == 0 && canCreateChildren )
        {
            Threading::ScopedMutexLock exclusive(_mutex);
            if ( getNumChildren() == 0 )
            {
                createChildren( context );
            }
        }

        // All 4 children must be ready before we can traverse any of them:
        unsigned numChildrenReady = 0;
        if ( getNumChildren() == 4 )
        {
            for(unsigned i = 0; i < 4; ++i)
            {                
                if ( getSubTile(i)->isReadyToTraverse() )
                {
                    ++numChildrenReady;
                }
            }
        }

        // If all are ready, traverse them now.
        if ( numChildrenReady == 4 )
        {
            // TODO:
            // When we do this, we need to quite sure that all 4 children will be accepted into
            // the draw set. Perhaps isReadyToTraverse() needs to check that.
            _children[0]->accept( nv );
            _children[1]->accept( nv );
            _children[2]->accept( nv );
            _children[3]->accept( nv );
        }

        // If we don't traverse the children, traverse this node's payload.
        else if ( _surface.valid() )
        {
            cullSurface( cv );
        }
    }

    // If children are outside camera range, draw the payload and expire the children.
    else if ( _surface.valid() )
    {
        cullSurface( cv );

        if ( getNumChildren() >= 4 && context->maxLiveTilesExceeded() )
        {
            if (getSubTile(0)->isDormant( nv ) &&
                getSubTile(1)->isDormant( nv ) &&
                getSubTile(2)->isDormant( nv ) &&
                getSubTile(3)->isDormant( nv ))
            {
                expireChildren( nv );
            }
        }
    }

    // Traverse land cover bins at this LOD.
    for(int i=0; i<context->landCoverBins()->size(); ++i)
    {
        bool first = true;
        const LandCoverBin& bin = context->landCoverBins()->at(i);
        if ( bin._lod == getTileKey().getLOD() )
        {
            if ( first )
            {
                cv->pushStateSet( _payloadStateSet.get() );
            }

            cv->pushStateSet( bin._stateSet.get() );
            _landCover->accept( nv );
            cv->popStateSet();

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

    // If this tile is marked dirty, try loading data.
    if ( _dirty )
    {
        load( nv );
    }
}
Exemple #13
0
bool
TileNode::cull(osgUtil::CullVisitor* cv)
{
    EngineContext* context = VisitorData::fetch<EngineContext>(*cv, ENGINE_CONTEXT_TAG);
    const SelectionInfo& selectionInfo = context->getSelectionInfo();

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

    // 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 create children in progressive mode until content is in place
    if ( _dirty && context->getOptions().progressive() == true )
    {
        canCreateChildren = 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 = cv->getCurrentCamera();
    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)
            {
                getSubTile(i)->accept_cull(cv);
            }

            // if we traversed all children, but they all return "not visible",
            // that means it's a horizon-culled tile anyway and we don't need
            // to add any drawables.
        }

        // 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 )
    {
        acceptSurface( cv, context );
        _lastAcceptSurfaceFrame.exchange( cv->getFrameStamp()->getFrameNumber() );
    }

       
    // Run any patch callbacks.
    context->invokeTilePatchCallbacks( cv, getTileKey(), _payloadStateSet.get(), _patch.get() );

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

    return true;
}