c++qtvtkqvtkwidget

Vtk Qt scene with >50 (moving) actors


I am trying to implement (fairly) simple scene where I have ~50 cubes which are moving in certain directions. The position of the cubes changes 20 times per second.

My first shoot was adding and removing actors from the scene. This approach just doesn't scale. Whole scene lags and user is unable to move the camera.

void draw(vtkRenderer *renderer)
{    
    renderer->RemoveAllViewProps(); 
    for(const Cube& cube : cubes_)
    {
        vtkSmartPointer<vtkCubeSource> cube_source = vtkSmartPointer<vtkCubeSource>::New();  
        cube_source->Update();
        cube_source->SetXLength(cube.lengt());
        cube_source->SetYLength(cube.width());
        cube_source->SetZLength(cube.height());

        vtkSmartPointer<vtkPolyDataMapper> poly_mapper = vtkSmartPointer<vtkPolyDataMapper>::New();
        poly_mapper->SetInputConnection(cube_source->GetOutputPort());

        vtkSmartPointer<vtkActor> actor = vtkSmartPointer<vtkActor>::New();
        actor->SetMapper(poly_mapper);
        actor->SetPosition(cube.x(), cube.y(), cube.z());

        renderer->AddActor(actor);
    }
}

Second shot is a bit better. I have created "actor pool" where I reuse the actors and hide the ones which are not needed. Still, moving camera is laggy and the rest of my UI (I have some additional widgets inside Vtk widget) seems to be laggy.

I couldn't find any relevant source for Vtk where scene is "dynamic". All examples preload all scene elements and further work with them. Can anyone tell me what am I doing wrong here?


Solution

  • So afther some days of reaserch I managed to hack working solution:

    vtkSmartPointer<vtkPoints> points = vtkSmartPointer<vtkPoints>::New();
    // as many points as you like
    points->InsertNextPoint(-25, 0, 0);
    points->InsertNextPoint(-35, 0, 0);
    
    
    vtkSmartPointer<vtkFloatArray> scales = vtkSmartPointer<vtkFloatArray>::New();
    scales->SetNumberOfComponents(3);
    // same as number of points
    scales->InsertNextTuple3(1, 1., 8.);
    scales->InsertNextTuple3(1., 1., 10.);
    
    
    vtkSmartPointer<vtkPolyData> polydata = vtkSmartPointer<vtkPolyData>::New();
    polydata->SetPoints(points);
    polydata->GetPointData()->SetVectors(scales);
    
    vtkSmartPointer<vtkCubeSource> cubeSource = vtkSmartPointer<vtkCubeSource>::New();
    
    vtkSmartPointer<vtkGlyph3D> glyph3D = vtkSmartPointer<vtkGlyph3D>::New();
    glyph3D->OrientOff(); // disable orientation
    glyph3D->SetScaleModeToScaleByVectorComponents(); // sacle along each axis
    glyph3D->SetSourceConnection(cubeSource->GetOutputPort());
    glyph3D->SetInputData(polydata);
    glyph3D->Update();
    
    vtkSmartPointer<vtkPolyDataMapper> mapper = vtkSmartPointer<vtkPolyDataMapper>::New();
    mapper->SetInputConnection(glyph3D->GetOutputPort());
    mapper->ScalarVisibilityOff(); // use color from actor
    
    vtkSmartPointer<vtkActor> actor = vtkSmartPointer<vtkActor>::New();
    actor->GetProperty()->SetColor(0, 1, 0); // this will change color for whole glyph
    actor->SetMapper(mapper);
    
    mapper->Update();
    
    vtk_renderer->AddActor(actor);
    

    Code from above will add as many cubes and you want using a single actor! (which was amazing performance boost in my case)

    Further, if you want to update positions of the cubes, you just need to do following:

    points->Reset();
    points->InsertNextPoint(-25, 0, 0);
    points->InsertNextPoint(-35, 0, 0);
    
    scales->Reset();
    scales->InsertNextTuple3(1, 1., 8.);
    scales->InsertNextTuple3(1., 1., 10.);
    
    polydata_->Modified();
    // call render
    

    (notice that I am not removing/adding actors to the scene which is another boost)