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; } } }
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; }