c++openglopenscenegraph

How to update Geometry properly


I am trying to display a point cloud, consisting of vertices and color with OSG. A static point cloud to display is rather easy with this guide.

But I am not capable of updating such a point cloud. My intention is to create a geometry and attach it to my viewer class once.

This is the mentioned method which is called once in the beginning.

The OSGWidget strongly depends on this OpenGLWidget based approach.

void OSGWidget::attachGeometry(osg::ref_ptr<osg::Geometry> geom)
{
osg::Geode* geode = new osg::Geode;

geom->setDataVariance(osg::Object::DYNAMIC);
geom->setUseDisplayList(false);
geom->setUseVertexBufferObjects(true);
bool addDrawSuccess = geode->addDrawable(geom.get()); // Adding Drawable Shape to the geometry node


if (!addDrawSuccess)
{
    throw "Adding Drawable failed!";
}

{
    osg::StateSet* stateSet = geode->getOrCreateStateSet();
    stateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
}

float aspectRatio = static_cast<float>(this->width()) / static_cast<float>(this->height());

// Setting up the camera
osg::Camera* camera = new osg::Camera;
camera->setViewport(0, 0, this->width(), this->height());
camera->setClearColor(osg::Vec4(0.f, 0.f, 0.f, 1.f)); // Kind of Backgroundcolor, clears the buffer and sets the default color (RGBA)
camera->setProjectionMatrixAsPerspective(30.f, aspectRatio, 1.f, 1000.f); // Create perspective projection
camera->setGraphicsContext(graphicsWindow_); // embed 

osgViewer::View* view = new osgViewer::View;
view->setCamera(camera);                    // Set the defined camera
view->setSceneData(geode);                  // Set the geometry
view->addEventHandler(new osgViewer::StatsHandler);


osgGA::TrackballManipulator* manipulator = new   osgGA::TrackballManipulator;
manipulator->setAllowThrow(false);

view->setCameraManipulator(manipulator);

///////////////////////////////////////////////////
// Set the viewer
//////////////////////////////////////////////////
viewer_->addView(view);
viewer_->setThreadingModel(osgViewer::CompositeViewer::SingleThreaded);
viewer_->realize();

this->setFocusPolicy(Qt::StrongFocus);
this->setMinimumSize(100, 100);

this->setMouseTracking(true);
}

After I have 'attached' the geometry, I am trying to update the geometry like this

void PointCloudViewOSG::processData(DepthDataSet depthData)
{
if (depthData.points()->empty())
{
    return; // empty cloud, cannot do anything
}

const DepthDataSet::IndexPtr::element_type& index = *depthData.index();
const size_t nPixel = depthData.points().get()->points.size();

if (depthData.intensity().isValid() && !index.empty() )
{
    for (int i = 0; i < nPixel; i++)
    {
        float x = depthData.points().get()->points[i].x;
        float y = depthData.points().get()->points[i].y;
        float z = depthData.points().get()->points[i].z;
        m_vertices->push_back(osg::Vec3(x
            , y
            , z));

        // 32 bit integer variable containing the rgb (8 bit per channel) value
        uint32_t rgb_val_;
        memcpy(&rgb_val_, &(depthData.points().get()->points[i].rgb), sizeof(uint32_t));

        uint32_t red, green, blue;
        blue = rgb_val_ & 0x000000ff;

        rgb_val_ = rgb_val_ >> 8;
        green = rgb_val_ & 0x000000ff;

        rgb_val_ = rgb_val_ >> 8;
        red = rgb_val_ & 0x000000ff;

        m_colors->push_back(
            osg::Vec4f((float)red / 255.0f,
            (float)green / 255.0f,
                (float)blue / 255.0f,
                1.0f)
        );
    }

    m_geometry->setVertexArray(m_vertices.get());

    m_geometry->setColorArray(m_colors.get());

    m_geometry->setColorBinding(osg::Geometry::BIND_PER_VERTEX);

    m_geometry->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::POINTS, 0, m_vertices->size()));      
    }
}

My guess is that the

addPrimitiveSet(...)

Shall not be called every time I update the geometry.

Or can it be the attachment of the geometry, so that I have to reattach it every time?

PointCloudlibrary (PCL) is unfortunately not an alternative since of some incompatibilities with my application.

Update: When I am reattaching the geometry to the OSGWidget class, calling

this->attachGeometry(m_geometry) after

m_geometry->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::POINTS, 0, m_vertices->size()));

I get my point cloud visible, but this procedure is definitely wrong since I am losing way too much performance and the display driver crashes.


Solution

  • You need to set the array and add the primitive set only once, after that you can update the vertices like this:

    osg::Vec3Array* vx = static_cast<osg::Vec3Array*>(m_vertices);
    for (int i = 0; i < nPixel; i++)
    {
       float x, y, z;
    
       // fill with your data...
    
       (*vx)[i].set(x, y, z);
    }
    m_vertices->dirty();
    

    The same goes for colors and other arrays.
    As you're using VBO, you don't need to call dirtyDisplayList()
    If you need instead to recompure the bounding box of the geometry, call

    m_geometry->dirtyBound()
    

    In case the number of points changes between updates, you can push new vertices into the array if its size is too small, and update the PrimitiveSet count like this:

    osg::DrawArrays* drawArrays = static_cast<osg::DrawArrays*>(m_geometry->getPrimitiveSet(0));
    drawArrays->setCount(nPixel);
    drawArrays->dirty();