Exemple #1
0
DrapingCullSet&
DrapingCullSet::get(const osg::Camera* cam)
{    
    static PerObjectFastMap<const osg::Camera*, DrapingCullSet> sets;

    // Known issue: it is possible for a draping cull set to be "orphaned" - this
    // would happen if the cull set were populated and then not used. This is a
    // very unlikely scenario (because the scene graph would have to change mid-cull)
    // but nevertheless possible.
    return sets.get(cam);
}
    // override.
    // Sorts the bin. This runs in the CULL thread after the CULL traversal has completed.
    void sortImplementation(osgUtil::RenderBin* bin)
    {
        osgUtil::RenderBin::RenderLeafList& leaves = bin->getRenderLeafList();

        // first, sort the leaves:
        if ( _customSortFunctor && s_declutteringEnabledGlobally )
        {
            // if there's a custom sorting function installed
            bin->copyLeavesFromStateGraphListToRenderLeafList();
            std::sort( leaves.begin(), leaves.end(), SortContainer( *_customSortFunctor ) );
        }
        else
        {
            // default behavior:
            bin->copyLeavesFromStateGraphListToRenderLeafList();
            std::sort( leaves.begin(), leaves.end(), SortFrontToBackPreservingGeodeTraversalOrder() );
        }

        // nothing to sort? bail out
        if ( leaves.size() == 0 )
            return;

        // access the view-specific persistent data:
        osg::Camera* cam   = bin->getStage()->getCamera();
        PerCamInfo& local = _perCam.get( cam );

        osg::Timer_t now = osg::Timer::instance()->tick();
        if (local._firstFrame)
        {
            local._firstFrame = false;
            local._lastTimeStamp = now;
        }

        // calculate the elapsed time since the previous pass; we'll use this for
        // the animations
        float elapsedSeconds = osg::Timer::instance()->delta_s(local._lastTimeStamp, now);
        local._lastTimeStamp = now;

        // Reset the local re-usable containers
        local._passed.clear();          // drawables that pass occlusion test
        local._failed.clear();          // drawables that fail occlusion test
        local._used.clear();            // list of occupied bounding boxes in screen space

        // compute a window matrix so we can do window-space culling. If this is an RTT camera
        // with a reference camera attachment, we actually want to declutter in the window-space
        // of the reference camera. (e.g., for picking).
        const osg::Viewport* vp = cam->getViewport();

        osg::Matrix windowMatrix = vp->computeWindowMatrix();

        osg::Vec3f  refCamScale(1.0f, 1.0f, 1.0f);
        osg::Matrix refCamScaleMat;
        osg::Matrix refWindowMatrix = windowMatrix;

        if ( cam->isRenderToTextureCamera() )
        {
            osg::Camera* refCam = dynamic_cast<osg::Camera*>(cam->getUserData());
            if ( refCam )
            {
                const osg::Viewport* refVP = refCam->getViewport();
                refCamScale.set( vp->width() / refVP->width(), vp->height() / refVP->height(), 1.0 );
                refCamScaleMat.makeScale( refCamScale );
                refWindowMatrix = refVP->computeWindowMatrix();
            }
        }

        // Track the parent nodes of drawables that are obscured (and culled). Drawables
        // with the same parent node (typically a Geode) are considered to be grouped and
        // will be culled as a group.
        std::set<const osg::Node*> culledParents;

        const ScreenSpaceLayoutOptions& options = _context->_options;
        unsigned limit = *options.maxObjects();

        bool snapToPixel = options.snapToPixel() == true;

        // Go through each leaf and test for visibility.
        // Enforce the "max objects" limit along the way.
        for(osgUtil::RenderBin::RenderLeafList::iterator i = leaves.begin();
                i != leaves.end() && local._passed.size() < limit;
                ++i )
        {
            bool visible = true;

            osgUtil::RenderLeaf* leaf = *i;
            const osg::Drawable* drawable = leaf->getDrawable();
            const osg::Node*     drawableParent = drawable->getParent(0);

            const ScreenSpaceLayoutData* layoutData = dynamic_cast<const ScreenSpaceLayoutData*>(drawable->getUserData());

            // transform the bounding box of the drawable into window-space.
            osg::BoundingBox box = Utils::getBoundingBox(drawable);

            osg::Vec3f offset;
            osg::Quat rot;
            if (layoutData)
            {

                // local transformation data
                // and management of the label orientation (must be always readable)
                bool isText = dynamic_cast<const osgText::Text*>(drawable) != 0L;
                float angle = layoutData->_localRotationRad;
                if ( isText && (angle < - osg::PI / 2. || angle > osg::PI / 2.) )
                {
                    // avoid the label characters to be inverted:
                    // use a symetric translation and adapt the rotation to be in the desired angles
                    offset.set( -layoutData->_pixelOffset.x() - box.xMax() - box.xMin(),
                                -layoutData->_pixelOffset.y() - box.yMax() - box.yMin(),
                                0.f );
                    angle -= osg::PI;
                }
                else
                {
                    offset.set( layoutData->_pixelOffset.x(), layoutData->_pixelOffset.y(), 0.f );
                }

                // handle the local translation
                box.xMin() += offset.x();
                box.xMax() += offset.x();
                box.yMin() += offset.y();
                box.yMax() += offset.y();

                // handle the local rotation
                if ( angle != 0.f )
                {
                    rot.makeRotate ( angle, osg::Vec3d(0, 0, 1) );
                    osg::Vec3f ld = rot * ( osg::Vec3f(box.xMin(), box.yMin(), 0.) );
                    osg::Vec3f lu = rot * ( osg::Vec3f(box.xMin(), box.yMax(), 0.) );
                    osg::Vec3f ru = rot * ( osg::Vec3f(box.xMax(), box.yMax(), 0.) );
                    osg::Vec3f rd = rot * ( osg::Vec3f(box.xMax(), box.yMin(), 0.) );
                    if ( angle > - osg::PI / 2. && angle < osg::PI / 2.)
                        box.set( std::min(ld.x(), lu.x()), std::min(ld.y(), rd.y()), 0,
                                 std::max(rd.x(), ru.x()), std::max(lu.y(), ru.y()), 0 );
                    else
                        box.set( std::min(ld.x(), lu.x()), std::min(lu.y(), ru.y()), 0,
                                 std::max(ld.x(), lu.x()), std::max(ld.y(), rd.y()), 0 );
                }
            }

            static osg::Vec4d s_zero_w(0,0,0,1);
            osg::Matrix MVP = (*leaf->_modelview.get()) * (*leaf->_projection.get());
            osg::Vec4d clip = s_zero_w * MVP;
            osg::Vec3d clip_ndc( clip.x()/clip.w(), clip.y()/clip.w(), clip.z()/clip.w() );

            // if we are using a reference camera (like for picking), we do the decluttering in
            // its viewport so that they match.
            osg::Vec3f winPos    = clip_ndc * windowMatrix;
            osg::Vec3f refWinPos = clip_ndc * refWindowMatrix;

            // The "declutter" box is the box we use to reserve screen space.
            // This must be unquantized regardless of whether snapToPixel is set.
            box.set(
                floor(refWinPos.x() + box.xMin()),
                floor(refWinPos.y() + box.yMin()),
                refWinPos.z(),
                ceil(refWinPos.x() + box.xMax()),
                ceil(refWinPos.y() + box.yMax()),
                refWinPos.z() );

            // if snapping is enabled, only snap when the camera stops moving.
            bool quantize = snapToPixel;
            if ( quantize )
            {
                DrawableInfo& info = local._memory[drawable];

                if ( info._lastXY.x() == winPos.x() && info._lastXY.y() == winPos.y() )
                {
                    // Quanitize the window draw coordinates so mitigate text rendering filtering anomalies.
                    // Drawing text glyphs on pixel boundaries mitigates aliasing.
                    // Adding 0.5 will cause the GPU to sample the glyph texels exactly on center.
                    winPos.x() = floor(winPos.x()) + 0.5;
                    winPos.y() = floor(winPos.y()) + 0.5;
                }
                else
                {
                    info._lastXY.set( winPos.x(), winPos.y() );
                }
            }

            if ( s_declutteringEnabledGlobally )
            {
                // A max priority => never occlude.
                float priority = layoutData ? layoutData->_priority : 0.0f;

                if ( priority == FLT_MAX )
                {
                    visible = true;
                }

                // if this leaf is already in a culled group, skip it.
                else if ( culledParents.find(drawableParent) != culledParents.end() )
                {
                    visible = false;
                }

                else
                {
                    // weed out any drawables that are obscured by closer drawables.
                    // TODO: think about a more efficient algorithm - right now we are just using
                    // brute force to compare all bbox's
                    for( std::vector<RenderLeafBox>::const_iterator j = local._used.begin(); j != local._used.end(); ++j )
                    {
                        // only need a 2D test since we're in clip space
                        bool isClear =
                            box.xMin() > j->second.xMax() ||
                            box.xMax() < j->second.xMin() ||
                            box.yMin() > j->second.yMax() ||
                            box.yMax() < j->second.yMin();

                        // if there's an overlap (and the conflict isn't from the same drawable
                        // parent, which is acceptable), then the leaf is culled.
                        if ( !isClear && drawableParent != j->first )
                        {
                            visible = false;
                            break;
                        }
                    }
                }
            }

            if ( visible )
            {
                // passed the test, so add the leaf's bbox to the "used" list, and add the leaf
                // to the final draw list.
                local._used.push_back( std::make_pair(drawableParent, box) );
                local._passed.push_back( leaf );
            }

            else
            {
                // culled, so put the parent in the parents list so that any future leaves
                // with the same parent will be trivially rejected
                culledParents.insert( drawable->getParent(0) );
                local._failed.push_back( leaf );
            }

            // modify the leaf's modelview matrix to correctly position it in the 2D ortho
            // projection when it's drawn later. We'll also preserve the scale.
            osg::Matrix newModelView;
            if ( rot.zeroRotation() )
            {
                newModelView.makeTranslate( osg::Vec3f(winPos.x() + offset.x(), winPos.y() + offset.y(), 0) );
            }
            else
            {
                offset = rot * offset;
                newModelView.makeTranslate( osg::Vec3f(winPos.x() + offset.x(), winPos.y() + offset.y(), 0) );
                newModelView.preMultRotate( rot );
            }
            newModelView.preMultScale( leaf->_modelview->getScale() * refCamScaleMat );

            // Leaf modelview matrixes are shared (by objects in the traversal stack) so we
            // cannot just replace it unfortunately. Have to make a new one. Perhaps a nice
            // allocation pool is in order here
            leaf->_modelview = new osg::RefMatrix( newModelView );
        }

        // copy the final draw list back into the bin, rejecting any leaves whose parents
        // are in the cull list.

        if ( s_declutteringEnabledGlobally )
        {
            leaves.clear();
            for( osgUtil::RenderBin::RenderLeafList::const_iterator i=local._passed.begin(); i != local._passed.end(); ++i )
            {
                osgUtil::RenderLeaf* leaf     = *i;
                const osg::Drawable* drawable = leaf->getDrawable();

                if ( culledParents.find( drawable->getParent(0) ) == culledParents.end() )
                {
                    DrawableInfo& info = local._memory[drawable];

                    bool fullyIn = true;

                    // scale in until at full scale:
                    if ( info._lastScale != 1.0f )
                    {
                        fullyIn = false;
                        info._lastScale += elapsedSeconds / std::max(*options.inAnimationTime(), 0.001f);
                        if ( info._lastScale > 1.0f )
                            info._lastScale = 1.0f;
                    }

                    if ( info._lastScale != 1.0f )
                        leaf->_modelview->preMult( osg::Matrix::scale(info._lastScale,info._lastScale,1) );

                    // fade in until at full alpha:
                    if ( info._lastAlpha != 1.0f )
                    {
                        fullyIn = false;
                        info._lastAlpha += elapsedSeconds / std::max(*options.inAnimationTime(), 0.001f);
                        if ( info._lastAlpha > 1.0f )
                            info._lastAlpha = 1.0f;
                    }

                    leaf->_depth = info._lastAlpha;
                    leaves.push_back( leaf );
                }
                else
                {
                    local._failed.push_back(leaf);
                }
            }

            // next, go through the FAILED list and sort them into failure bins so we can draw
            // them using a different technique if necessary.
            for( osgUtil::RenderBin::RenderLeafList::const_iterator i=local._failed.begin(); i != local._failed.end(); ++i )
            {
                osgUtil::RenderLeaf* leaf =     *i;
                const osg::Drawable* drawable = leaf->getDrawable();

                DrawableInfo& info = local._memory[drawable];

                bool isText = dynamic_cast<const osgText::Text*>(drawable) != 0L;
                bool isBbox = dynamic_cast<const osgEarth::Annotation::BboxDrawable*>(drawable) != 0L;
                bool fullyOut = true;

                if ( info._lastScale != *options.minAnimationScale() )
                {
                    fullyOut = false;
                    info._lastScale -= elapsedSeconds / std::max(*options.outAnimationTime(), 0.001f);
                    if ( info._lastScale < *options.minAnimationScale() )
                        info._lastScale = *options.minAnimationScale();
                }

                if ( info._lastAlpha != *options.minAnimationAlpha() )
                {
                    fullyOut = false;
                    info._lastAlpha -= elapsedSeconds / std::max(*options.outAnimationTime(), 0.001f);
                    if ( info._lastAlpha < *options.minAnimationAlpha() )
                        info._lastAlpha = *options.minAnimationAlpha();
                }

                leaf->_depth = info._lastAlpha;

                if ( (!isText && !isBbox) || !fullyOut )
                {
                    if ( info._lastAlpha > 0.01f && info._lastScale >= 0.0f )
                    {
                        leaves.push_back( leaf );

                        // scale it:
                        if ( info._lastScale != 1.0f )
                            leaf->_modelview->preMult( osg::Matrix::scale(info._lastScale,info._lastScale,1) );
                    }
                }
            }
        }
    }
Exemple #3
0
    // override.
    // Sorts the bin. This runs in the CULL thread after the CULL traversal has completed.
    void sortImplementation(osgUtil::RenderBin* bin)
    {
        osgUtil::RenderBin::RenderLeafList& leaves = bin->getRenderLeafList();

        // first, sort the leaves:
        if ( _customSortFunctor && s_enabledGlobally )
        {
            // if there's a custom sorting function installed
            bin->copyLeavesFromStateGraphListToRenderLeafList();
            std::sort( leaves.begin(), leaves.end(), SortContainer( *_customSortFunctor ) );
        }
        else
        {
            // default behavior:
            bin->copyLeavesFromStateGraphListToRenderLeafList();
            std::sort( leaves.begin(), leaves.end(), SortFrontToBackPreservingGeodeTraversalOrder() );
        }

        // nothing to sort? bail out
        if ( leaves.size() == 0 )
            return;

        // access the view-specific persistent data:
        osg::Camera* cam   = bin->getStage()->getCamera();                
        PerCamInfo& local = _perCam.get( cam );

        osg::Timer_t now = osg::Timer::instance()->tick();
        if (local._firstFrame)
        {            
            local._firstFrame = false;
            local._lastTimeStamp = now;
        }

        // calculate the elapsed time since the previous pass; we'll use this for
        // the animations                
        float elapsedSeconds = osg::Timer::instance()->delta_s(local._lastTimeStamp, now);
        local._lastTimeStamp = now;

        // Reset the local re-usable containers
        local._passed.clear();          // drawables that pass occlusion test
        local._failed.clear();          // drawables that fail occlusion test
        local._used.clear();            // list of occupied bounding boxes in screen space

        // compute a window matrix so we can do window-space culling. If this is an RTT camera
        // with a reference camera attachment, we actually want to declutter in the window-space
        // of the reference camera.
        const osg::Viewport* vp = cam->getViewport();

        osg::Matrix windowMatrix = vp->computeWindowMatrix();

        osg::Vec3f  refCamScale(1.0f, 1.0f, 1.0f);
        osg::Matrix refCamScaleMat;
        osg::Matrix refWindowMatrix = windowMatrix;

        if ( cam->isRenderToTextureCamera() )
        {
            osg::Camera* refCam = dynamic_cast<osg::Camera*>(cam->getUserData());
            if ( refCam )
            {
                const osg::Viewport* refVP = refCam->getViewport();
                refCamScale.set( vp->width() / refVP->width(), vp->height() / refVP->height(), 1.0 );
                refCamScaleMat.makeScale( refCamScale );
                refWindowMatrix = refVP->computeWindowMatrix();
            }
        }

        // Track the parent nodes of drawables that are obscured (and culled). Drawables
        // with the same parent node (typically a Geode) are considered to be grouped and
        // will be culled as a group.
        std::set<const osg::Node*> culledParents;

        const DeclutteringOptions& options = _context->_options;
        unsigned limit = *options.maxObjects();

        // Go through each leaf and test for visibility.
        // Enforce the "max objects" limit along the way.
        for(osgUtil::RenderBin::RenderLeafList::iterator i = leaves.begin(); 
            i != leaves.end() && local._passed.size() < limit; 
            ++i )
        {
            bool visible = true;

            osgUtil::RenderLeaf* leaf = *i;
            const osg::Drawable* drawable = leaf->getDrawable();
            const osg::Node*     drawableParent = drawable->getParent(0);

            // transform the bounding box of the drawable into window-space.
            osg::BoundingBox box = Utils::getBoundingBox(drawable);

            static osg::Vec4d s_zero_w(0,0,0,1);
            osg::Matrix MVP = (*leaf->_modelview.get()) * (*leaf->_projection.get());
            osg::Vec4d clip = s_zero_w * MVP;
            osg::Vec3d clip_ndc( clip.x()/clip.w(), clip.y()/clip.w(), clip.z()/clip.w() );
            osg::Vec3f winPos = clip_ndc * windowMatrix;

            // this accounts for the size difference when using a reference camera (RTT/picking)
            box.xMin() *= refCamScale.x();
            box.xMax() *= refCamScale.x();
            box.yMin() *= refCamScale.y();
            box.yMax() *= refCamScale.y();

            osg::Vec2f offset( -box.xMin(), -box.yMin() );

            box.set(
                winPos.x() + box.xMin(),
                winPos.y() + box.yMin(),
                winPos.z(),
                winPos.x() + box.xMax(),
                winPos.y() + box.yMax(),
                winPos.z() );

            // if this leaf is already in a culled group, skip it.
            if ( s_enabledGlobally )
            {
                if ( culledParents.find(drawableParent) != culledParents.end() )
                {
                    visible = false;
                }
                else
                {
                    // weed out any drawables that are obscured by closer drawables.
                    // TODO: think about a more efficient algorithm - right now we are just using
                    // brute force to compare all bbox's
                    for( std::vector<RenderLeafBox>::const_iterator j = local._used.begin(); j != local._used.end(); ++j )
                    {
                        // only need a 2D test since we're in clip space
                        bool isClear =
                            box.xMin() > j->second.xMax() ||
                            box.xMax() < j->second.xMin() ||
                            box.yMin() > j->second.yMax() ||
                            box.yMax() < j->second.yMin();

                        // if there's an overlap (and the conflict isn't from the same drawable
                        // parent, which is acceptable), then the leaf is culled.
                        if ( !isClear && drawableParent != j->first )
                        {
                            visible = false;
                            break;
                        }
                    }
                }
            }

            if ( visible )
            {
                // passed the test, so add the leaf's bbox to the "used" list, and add the leaf
                // to the final draw list.
                //local._used.push_back( std::make_pair(drawableParent, box) );
                local._used.push_back( std::make_pair(drawableParent, box) );
                local._passed.push_back( leaf );
            }

            else
            {
                // culled, so put the parent in the parents list so that any future leaves
                // with the same parent will be trivially rejected
                culledParents.insert( drawable->getParent(0) );
                local._failed.push_back( leaf );
            }

            // modify the leaf's modelview matrix to correctly position it in the 2D ortho
            // projection when it's drawn later. We'll also preserve the scale.
            osg::Matrix newModelView;
            newModelView.makeTranslate( box.xMin() + offset.x(), box.yMin() + offset.y(), 0 );
            newModelView.preMultScale( leaf->_modelview->getScale() * refCamScaleMat );
            
            // Leaf modelview matrixes are shared (by objects in the traversal stack) so we 
            // cannot just replace it unfortunately. Have to make a new one. Perhaps a nice
            // allocation pool is in order here
            leaf->_modelview = new osg::RefMatrix( newModelView );
        }

        // copy the final draw list back into the bin, rejecting any leaves whose parents
        // are in the cull list.

        if ( s_enabledGlobally )
        {
            leaves.clear();
            for( osgUtil::RenderBin::RenderLeafList::const_iterator i=local._passed.begin(); i != local._passed.end(); ++i )
            {
                osgUtil::RenderLeaf* leaf     = *i;
                const osg::Drawable* drawable = leaf->getDrawable();

                if ( culledParents.find( drawable->getParent(0) ) == culledParents.end() )
                {
                    DrawableInfo& info = local._memory[drawable];

                    bool fullyIn = true;

                    // scale in until at full scale:
                    if ( info._lastScale != 1.0f )
                    {
                        fullyIn = false;
                        info._lastScale += elapsedSeconds / std::max(*options.inAnimationTime(), 0.001f);
                        if ( info._lastScale > 1.0f )
                            info._lastScale = 1.0f;
                    }

                    if ( info._lastScale != 1.0f )
                        leaf->_modelview->preMult( osg::Matrix::scale(info._lastScale,info._lastScale,1) );
                    
                    // fade in until at full alpha:
                    if ( info._lastAlpha != 1.0f )
                    {
                        fullyIn = false;
                        info._lastAlpha += elapsedSeconds / std::max(*options.inAnimationTime(), 0.001f);
                        if ( info._lastAlpha > 1.0f )
                            info._lastAlpha = 1.0f;
                    }

                    leaf->_depth = info._lastAlpha;
                    leaves.push_back( leaf );                
                }
                else
                {
                    local._failed.push_back(leaf);
                }
            }

            // next, go through the FAILED list and sort them into failure bins so we can draw
            // them using a different technique if necessary.
            for( osgUtil::RenderBin::RenderLeafList::const_iterator i=local._failed.begin(); i != local._failed.end(); ++i )
            {
                osgUtil::RenderLeaf* leaf =     *i;
                const osg::Drawable* drawable = leaf->getDrawable();

                DrawableInfo& info = local._memory[drawable];

                bool isText = dynamic_cast<const osgText::Text*>(drawable) != 0L;
                bool fullyOut = true;

                if ( info._lastScale != *options.minAnimationScale() )
                {
                    fullyOut = false;
                    info._lastScale -= elapsedSeconds / std::max(*options.outAnimationTime(), 0.001f);
                    if ( info._lastScale < *options.minAnimationScale() )
                        info._lastScale = *options.minAnimationScale();
                }

                if ( info._lastAlpha != *options.minAnimationAlpha() )
                {
                    fullyOut = false;
                    info._lastAlpha -= elapsedSeconds / std::max(*options.outAnimationTime(), 0.001f);
                    if ( info._lastAlpha < *options.minAnimationAlpha() )
                        info._lastAlpha = *options.minAnimationAlpha();
                }

                leaf->_depth = info._lastAlpha;

                if ( !isText || !fullyOut )
                {
                    if ( info._lastAlpha > 0.01f && info._lastScale >= 0.0f )
                    {
                        leaves.push_back( leaf );

                        // scale it:
                        if ( info._lastScale != 1.0f )
                            leaf->_modelview->preMult( osg::Matrix::scale(info._lastScale,info._lastScale,1) );
                    }
                }
            }
        }
    }