Marco Maisano
Marco Maisano

Reputation: 11

Custom declutter of behind objects

I created a widget in OsgEarth consisting of an image, three texts and the background.

They are represented by AnnotationGroup, which contains a Geode, which contains a Geometry(for the image), three osgTexts (for the texts) and a Geometry for the background.

The problem lies in the fact that, when two of these widgets are overlapped, even if the declutter is active, only the texts are hidden, while the background and the image are minimized and not hidden.

Here's what I see

I put the code about the widget implementation:

#include "pch.h"
#include "Label3DWidget.h"

#include <osgEarth/ShaderGenerator>
#include <osgEarth/Lighting>
#include <osgEarthAnnotation/AnnotationUtils>
#include <osgEarthAnnotation/AnnotationRegistry>
#include <osgEarthAnnotation/BboxDrawable>

#include <osg/Depth>

namespace
{
    const char* iconVS =
        "#version " GLSL_VERSION_STR "\n"
        "out vec2 oe_PlaceNode_texcoord; \n"
        "void oe_PlaceNode_icon_VS(inout vec4 vertex) \n"
        "{ \n"
        "    oe_PlaceNode_texcoord = gl_MultiTexCoord0.st; \n"
        "} \n";

    const char* iconFS =
        "#version " GLSL_VERSION_STR "\n"
        "in vec2 oe_PlaceNode_texcoord; \n"
        "uniform sampler2D oe_PlaceNode_tex; \n"
        "void oe_PlaceNode_icon_FS(inout vec4 color) \n"
        "{ \n"
        "    color = texture(oe_PlaceNode_tex, oe_PlaceNode_texcoord); \n"
        "} \n";
}

Label3DWidget::Label3DWidget() : osgEarth::Annotation::AnnotationNode() {
    
    initStateSet();

    init();

    setBlackBackground();
}

void Label3DWidget::setBlackBackground() {
    osg::BoundingBox combinedBoundingBox;

    if (_flagDrawable) {
        combinedBoundingBox.expandBy(_flagDrawable->getBoundingBox());
    }
    if (_mainTextDrawable) {
        combinedBoundingBox.expandBy(_mainTextDrawable->getBoundingBox());
    }

    backgroundQuad = new osg::Geometry();
    vertices = new osg::Vec3Array(4);
    vertices->setDataVariance(osg::Object::DYNAMIC);

    float xCenter = (combinedBoundingBox.xMin() + combinedBoundingBox.xMax()) / 2.0f;
    float yCenter = (combinedBoundingBox.yMin() + combinedBoundingBox.yMax()) / 2.0f;

    float width = 200.0f;
    float height = 40.0f; // Desired height of the black background
    float zOffset = -0.01f; // Offset to ensure background is behind

    int xPadding = 5;

    (*vertices)[0].set(combinedBoundingBox.xMin() - xPadding, yCenter - height / 2.0f, combinedBoundingBox.zMin() + zOffset);
    (*vertices)[1].set(combinedBoundingBox.xMax() + xPadding, yCenter - height / 2.0f, combinedBoundingBox.zMin() + zOffset);
    (*vertices)[2].set(combinedBoundingBox.xMax() + xPadding, yCenter + height / 2.0f, combinedBoundingBox.zMin() + zOffset);
    (*vertices)[3].set(combinedBoundingBox.xMin() - xPadding, yCenter + height / 2.0f, combinedBoundingBox.zMin() + zOffset);

    xMinPos = combinedBoundingBox.xMin() - xPadding;
    xMaxPos = combinedBoundingBox.xMax() + xPadding;

    backgroundQuad->setVertexArray(vertices);
    backgroundQuad->setDataVariance(osg::Object::DYNAMIC);
    //backgroundQuad->setCullingActive(false);

    osg::ref_ptr<osg::Vec4Array> colors = new osg::Vec4Array(1);
    (*colors)[0].set(0.0f, 0.0f, 0.0f, 0.6f);
    backgroundQuad->setColorArray(colors, osg::Array::BIND_OVERALL);

    backgroundQuad->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS, 0, 4));

    osg::StateSet* stateSet = backgroundQuad->getOrCreateStateSet();
    //stateSet->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);
    //stateSet->setRenderBinDetails(-1, "RenderBin");
    stateSet->setMode(GL_BLEND, osg::StateAttribute::ON);
    stateSet->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);

    _geode->insertChild(0, backgroundQuad);
}

void Label3DWidget::updateBlackBackground() {
    cout << "UPDATE BACK START" << endl;

    osg::BoundingBox combinedBoundingBox = getFirstLevelCombinedBBox();

    float xCenter = (combinedBoundingBox.xMin() + combinedBoundingBox.xMax()) / 2.0f;
    float yCenter = (combinedBoundingBox.yMin() + combinedBoundingBox.yMax()) / 2.0f;

    float combinedHeight = combinedBoundingBox.yMax() - combinedBoundingBox.yMin();

    float height = combinedHeight; // Desired height of the black background
    float zOffset = -0.05f; // Offset to ensure background is behind
    int xPadding = 5;
    cout << "UPDATE BACK SIZE: " << (combinedBoundingBox.xMax() - combinedBoundingBox.xMin()) + 10 << endl;
    (*vertices)[0].set(combinedBoundingBox.xMin() - xPadding, (yCenter - height / 2.0f)-5, combinedBoundingBox.zMin() + zOffset);
    (*vertices)[1].set(combinedBoundingBox.xMax() + xPadding, (yCenter - height / 2.0f)-5, combinedBoundingBox.zMin() + zOffset);
    (*vertices)[2].set(combinedBoundingBox.xMax() + xPadding, (yCenter + height / 2.0f) - 5, combinedBoundingBox.zMin() + zOffset);
    (*vertices)[3].set(combinedBoundingBox.xMin() - xPadding, (yCenter + height / 2.0f) - 5, combinedBoundingBox.zMin() + zOffset);

    vertices->dirty();
    backgroundQuad->dirtyBound();
    backgroundQuad->dirtyGLObjects();
    cout << "UPDATE BACK END" << endl;
}

void Label3DWidget::initStateSet() {
    osgEarth::ShaderGenerator::setIgnoreHint(this, true);

    _geodeStateSet = new osg::StateSet();

    // draw in the screen-space bin
    osgEarth::Annotation::ScreenSpaceLayout::activate(_geodeStateSet.get());
    osgEarth::ScreenSpaceLayout::setDeclutteringEnabled(true);

    // completely disable depth buffer
    _geodeStateSet->setAttributeAndModes(new osg::Depth(osg::Depth::ALWAYS, 0, 1, false), osg::StateAttribute::ON | osg::StateAttribute::PROTECTED);

    // Disable lighting for place nodes by default
    _geodeStateSet->setDefine(OE_LIGHTING_DEFINE, osg::StateAttribute::OFF | osg::StateAttribute::PROTECTED);
    //}

    s_imageStateSet = _imageStateSet = new osg::StateSet();
    osgEarth::Annotation::VirtualProgram* vp = osgEarth::Annotation::VirtualProgram::getOrCreate(_imageStateSet.get());
    vp->setName("PlaceNode::imageStateSet");
    vp->setFunction("oe_PlaceNode_icon_VS", iconVS, osgEarth::ShaderComp::LOCATION_VERTEX_MODEL);
    vp->setFunction("oe_PlaceNode_icon_FS", iconFS, osgEarth::ShaderComp::LOCATION_FRAGMENT_COLORING);
    _imageStateSet->addUniform(new osg::Uniform("oe_PlaceNode_tex", 0));
}

void Label3DWidget::init() {
    
    _geode = new osg::Group();
    //_geode->setCullingActive(false);
    _geode->setStateSet(_geodeStateSet.get());
    _geode->setDataVariance(osg::Object::DYNAMIC);
    //setHorizonCulling(false);
    _geode->setComputeBoundingSphereCallback(new osgEarth::Annotation::ControlPointCallback());

    addChild(_geode);

    osg::BoundingBox imageBox(0, 0, 0, 0, 0, 0);

    /*Flag Image*/
    _flagImage = osgDB::readRefImageFile("C:/ProgramData/ELMAN/resources/3D/Images/flags_png/abw.png");
    if (_flagImage.get())
    {
        // Scale the icon if necessary
        double scale = 2.5;
        double s = scale * _flagImage->s();
        double t = scale * _flagImage->t();
        double heading = 0.0;

        osg::Vec2s offset;
        offset.set(0, t / 2.0);

        _flagDrawable = osgEarth::Annotation::AnnotationUtils::createImageGeometry(_flagImage.get(), offset, 0, heading, scale);
        if (_flagDrawable)
        {
            imageVertices = (osg::Vec3Array*)_flagDrawable->getVertexArray();

            _flagDrawable->getOrCreateStateSet()->merge(*_imageStateSet.get());
            osg::StateSet* stateSet = _flagDrawable->getOrCreateStateSet();
            //stateSet->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);
            //stateSet->setRenderBinDetails(-1, "RenderBin");
            stateSet->setMode(GL_BLEND, osg::StateAttribute::ON);
            stateSet->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
            //_flagDrawable->setCullingActive(false);
            _geode->addChild(_flagDrawable);

            imageBox = _flagDrawable->getBoundingBox();
        }
    }

    if (_flagImage.valid())
    {
        osgEarth::Annotation::TextSymbol* textSymbol = _style.getOrCreate<osgEarth::Annotation::TextSymbol>();
        textSymbol->size() = 18;
        textSymbol->alignment() = textSymbol->ALIGN_LEFT_CENTER;
    }

    osg::BoundingBox textBoxWithOffset = imageBox;
    textBoxWithOffset.xMin() += 10;
    textBoxWithOffset.xMax() += 10;

    _mainTextDrawable = osgEarth::Annotation::AnnotationUtils::createTextDrawable(_mainText, _style.get<osgEarth::Annotation::TextSymbol>(), textBoxWithOffset);
    _mainTextDrawable->setDataVariance(osg::Object::DYNAMIC);
    //_mainTextDrawable->setCullingActive(false);

    if (_mainTextDrawable)_geode->addChild(_mainTextDrawable);

    float distanceBelow = 10.0f;
    // Calcola l'altezza di imageBox
    float imageBoxHeight = imageBox.yMax() - imageBox.yMin();
    osg::BoundingBox secondBoxWithOffset = imageBox;
    secondBoxWithOffset.yMin() = imageBox.yMin() - imageBoxHeight - distanceBelow;
    secondBoxWithOffset.yMax() = imageBox.yMin() - distanceBelow;

    _secondTextDrawable = osgEarth::Annotation::AnnotationUtils::createTextDrawable(_secondText, _style.get<osgEarth::Annotation::TextSymbol>(), secondBoxWithOffset);
    _secondTextDrawable->setDataVariance(osg::Object::DYNAMIC);
    //_secondTextDrawable->setCullingActive(false);

    if (_secondTextDrawable)_geode->addChild(_secondTextDrawable);

    osg::BoundingBox thirdTextBoxWithOffset = _secondTextDrawable->getBoundingBox();
    thirdTextBoxWithOffset.xMin() += 10;
    thirdTextBoxWithOffset.xMax() += 10;

    _thirdTextDrawable = osgEarth::Annotation::AnnotationUtils::createTextDrawable(_thirdText, _style.get<osgEarth::Annotation::TextSymbol>(), thirdTextBoxWithOffset);
    _thirdTextDrawable->setDataVariance(osg::Object::DYNAMIC);
    //_thirdTextDrawable->setCullingActive(false);

    if (_thirdTextDrawable)_geode->addChild(_thirdTextDrawable);

    setStyle(_style);
}

osg::BoundingBox Label3DWidget::getFirstLevelCombinedBBox() {
    osg::BoundingBox combinedBoundingBox;

    if (_flagDrawable) {
        combinedBoundingBox.expandBy(_flagDrawable->computeBoundingBox());
    }
    if (_mainTextDrawable) {
        combinedBoundingBox.expandBy(_mainTextDrawable->computeBoundingBox());
    }
    if (_secondTextDrawable) {
        combinedBoundingBox.expandBy(_secondTextDrawable->computeBoundingBox());
    }

    return combinedBoundingBox;
}

void Label3DWidget::setMainText(string text) {
    _mainText = text;
    cout << "SET MAIN TEXT: "<< text << endl;
    if (_mainTextDrawable && text != "" && text != " ")
    {
        osg::BoundingBox beforeBBox = getFirstLevelCombinedBBox();

        double beforeWidth = (beforeBBox.xMax() - beforeBBox.xMin()) + 10;

        osgEarth::Annotation::TextSymbol* symbol = _style.getOrCreate<osgEarth::Annotation::TextSymbol>();
        osgText::String::Encoding text_encoding = osgText::String::ENCODING_UNDEFINED;
        if (symbol && symbol->encoding().isSet())
        {
            text_encoding = osgEarth::Annotation::AnnotationUtils::convertTextSymbolEncoding(symbol->encoding().value());
        }

        _mainTextDrawable->setText(text, text_encoding);

        osg::BoundingBox afterBBox = getFirstLevelCombinedBBox();

        double afterWidth = (afterBBox.xMax() - afterBBox.xMin()) + 10;
        cout << "BEFORE BACK SIZE: " << beforeWidth << endl;
        cout << "AFTER Width SIZE: " << afterWidth << endl;
        
        if (afterWidth != beforeWidth) {

            if (imageVertices) {
                cout << "UPDATE IMAGE" << endl;
                (*imageVertices)[0].set(-(afterWidth / 2) - 0, 0, 0);
                (*imageVertices)[1].set(-(afterWidth / 2) + 40, 0, 0);
                (*imageVertices)[2].set(-(afterWidth / 2) + 40, 40, 0);
                (*imageVertices)[3].set(-(afterWidth / 2) - 0, 40, 0);
                imageVertices->dirty();
            }

            cout << "SET MAIN TEXT UPDATE POSITION MAIN" << endl;
            _mainTextDrawable->setPosition(osg::Vec3(_flagDrawable->computeBoundingBox().xMax() + 10, 20, 0));

            float distanceBelow = 10.0f;
            // Calcola l'altezza di imageBox
            float imageBoxHeight = _flagDrawable->computeBoundingBox().yMax() - _flagDrawable->computeBoundingBox().yMin();
            osg::BoundingBox secondBoxWithOffset = _flagDrawable->computeBoundingBox();
            secondBoxWithOffset.yMin() = _flagDrawable->computeBoundingBox().yMin() - imageBoxHeight - distanceBelow;
            secondBoxWithOffset.yMax() = _flagDrawable->computeBoundingBox().yMin() - distanceBelow;

            _secondTextDrawable->setPosition(osg::Vec3(-(afterWidth / 2), secondBoxWithOffset.yMax(), 0));

            _thirdTextDrawable->setPosition(osg::Vec3(_secondTextDrawable->computeBoundingBox().xMax() + 10, secondBoxWithOffset.yMax(), 0));

            updateBlackBackground();
        }
    }
}

void Label3DWidget::setSecondText(string text) {
    _secondText = text;

    if (_secondTextDrawable && text != "" && text != " ")
    {
        osgEarth::Annotation::TextSymbol* symbol = _style.getOrCreate<osgEarth::Annotation::TextSymbol>();
        osgText::String::Encoding text_encoding = osgText::String::ENCODING_UNDEFINED;
        if (symbol && symbol->encoding().isSet())
        {
            text_encoding = osgEarth::Annotation::AnnotationUtils::convertTextSymbolEncoding(symbol->encoding().value());
        }

        _secondTextDrawable->setText(text, text_encoding);
    }
}

void Label3DWidget::setThirdText(string text) {
    _thirdText = text;

    if (_thirdTextDrawable && text != "" && text != " ")
    {
        osgEarth::Annotation::TextSymbol* symbol = _style.getOrCreate<osgEarth::Annotation::TextSymbol>();
        osgText::String::Encoding text_encoding = osgText::String::ENCODING_UNDEFINED;
        if (symbol && symbol->encoding().isSet())
        {
            text_encoding = osgEarth::Annotation::AnnotationUtils::convertTextSymbolEncoding(symbol->encoding().value());
        }

        _thirdTextDrawable->setText(text, text_encoding);
    }
}

string Label3DWidget::getMainText() {
    return _mainText;
}

void Label3DWidget::setImage(osg::Image* image) {
    if (_flagImage != image)
    {
        _flagImage = image;
        if (_flagDrawable)
        {
            osg::Texture2D* texture = dynamic_cast<osg::Texture2D*>(_flagDrawable->getStateSet()->getTextureAttribute(0, osg::StateAttribute::TEXTURE));
            if (texture)
            {
                texture->setImage(_flagImage);
            }
        }
    }
}

osg::BoundingBox Label3DWidget::getWidgetBBox() {

    return backgroundQuad->computeBoundingBox();
}

I'm not sure what test to do, I tried to change the DEPTH_TEST parameters, but to no avail.

Thanks for support.

Upvotes: 1

Views: 39

Answers (0)

Related Questions