VTK pipeline update - c++

I use VTK-6.2, C++ (gcc-4.7.2) on Linux and I have the following VTK pipeline setup (please ignore implementation, details and focus on the pipeline: cone->filter->mapper->actor):
// cone/initialize
vtkConeSource cone;
// add cone(s) to filter
vtkAppendFilter filter;
filter.AddInputData(cone.GetOutput());
// add filter to mapper
vtkDataSetMapper mapper;
mapper.SetInputData(filter->GetOutput());
// actor
vtkActor actor;
actor.SetMapper(mapper);
The scene renders fine.
The Problem
I want to update the original data (i.e. the cones) and the actor to be rendered correctly.
How do I access the original cone data if I have just the actors? Does this guarantee that the actors will be updated too? Because when I decided to keep track of the original data (via pointers: the whole implementation is with vtkSmartPointers) and then change some of their attributes, the pipeline did not update. Shouldn't it update automatically?
(When I change the actor (e.g. their visibility), the scene renders fine)
Forgive me, I am not a VTK expert and the pipelines are confusing. Maybe one approach would be to simplify my pipeline.
Thanks
[update]
According to this answer to a similar post, the original data (vtkConeSource) are transformed (to vtkUnstructuredGrid when added in the vtkAppendFilter) so even if I keep track of the original data, changing them is useless.

VTK pipelines are demand-driven pipelines. They do not update automatically even if one of the elements of the pipeline is modified. We need to explicitly call the Update() function on the last vtkAlgorithm( or its derived class object) of the pipeline to update the entire pipeline. The correct way to set up a pipeline is when we are connecting two objects which are derived from vtkAlgorithm type is to use
currAlgoObj->SetInputConnection( prevAlgoObj->GetOutputPort() )
instead of
currAlgoObj->SetInputData( prevAlgo->GetOutput() )
Then we can update the pipeline using the pointer to the actor object by doing actor->GetMapper()->Update() like shown in the example below.
In this example, we will create a cone from a cone source, pass it through vtkAppendFilter and then change the height of the original cone source and render it in another window to see the updated cone. (You will have to close the first render window to see the updated cone in second window.)
#include <vtkConeSource.h>
#include <vtkDataSetMapper.h>
#include <vtkActor.h>
#include <vtkRenderer.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkSmartPointer.h>
#include <vtkAppendFilter.h>
int main(int, char *[])
{
// Set up the data pipeline
auto cone = vtkSmartPointer<vtkConeSource>::New();
cone->SetHeight( 1.0 );
auto appf = vtkSmartPointer<vtkAppendFilter>::New();
appf->SetInputConnection( cone->GetOutputPort() );
auto coneMapper = vtkSmartPointer<vtkDataSetMapper>::New();
coneMapper->SetInputConnection( appf->GetOutputPort() );
auto coneActor = vtkSmartPointer<vtkActor>::New();
coneActor->SetMapper( coneMapper );
// We need to update the pipeline otherwise nothing will be rendered
coneActor->GetMapper()->Update();
// Connect to the rendering portion of the pipeline
auto renderer = vtkSmartPointer<vtkRenderer>::New();
renderer->AddActor( coneActor );
renderer->SetBackground( 0.1, 0.2, 0.4 );
auto renderWindow = vtkSmartPointer<vtkRenderWindow>::New();
renderWindow->SetSize( 200, 200 );
renderWindow->AddRenderer(renderer);
auto renderWindowInteractor =
vtkSmartPointer<vtkRenderWindowInteractor>::New();
renderWindowInteractor->SetRenderWindow(renderWindow);
renderWindowInteractor->Start();
// Change cone property
cone->SetHeight( 10.0 );
//Update the pipeline using the actor object
coneActor->GetMapper()->Update();
auto renderer2 = vtkSmartPointer<vtkRenderer>::New();
renderer2->AddActor( coneActor );
renderer2->SetBackground( 0.1, 0.2, 0.4 );
auto renderWindow2 = vtkSmartPointer<vtkRenderWindow>::New();
renderWindow2->SetSize( 200, 200 );
renderWindow2->AddRenderer(renderer2);
auto renderWindowInteractor2 =
vtkSmartPointer<vtkRenderWindowInteractor>::New();
renderWindowInteractor2->SetRenderWindow(renderWindow2);
renderWindowInteractor2->Start();
return EXIT_SUCCESS;
}

Related

Problem creating multiple viewports in Qt3D using C++

I am trying to setup a Qt3DWindow with multiple viewports using C++. According to the documentation and the provided QML example, all I need to do is create a framegraph, where a main QViewport object branches into several RenderViews. The first RenderView contains a QClearBuffers object, and the remaining ones contain the tiled viewports and their corresponding camera selectors. Therefore, if I want N viewports, I need to create N+1 RenderViews.
However, if I follow that procedure, the main viewport displays some sort of "default" view, which appears on top of the window, spanning all the viewports. I don't know where this view comes from, since it doesn't correspond to any camera. Here is the output from the code posted below.
I found a solution, but I am not comfortable with it since it feels like some kind of hack: instead of making all the RenderViews branch from the main viewport, I attach one of the child viewports to the ClearBuffers object itself. Thus, for N viewports, I have N RenderViews instead of N+1. I don't quite understand the internals of the framegraph, so I would like to know if this solution is only wrong from an OCD perspective, or it can actually backfire at some point.
Here is a minimal example with two viewports sharing the default camera. If I make either viewPort1 or viewPort2 branch from clearBuffers instead of mainViewPort, everything works as expected:
#include <QGuiApplication>
#include <Qt3DCore/QTransform>
#include <Qt3DExtras/Qt3DWindow>
#include <Qt3DExtras/QTorusMesh>
#include <Qt3DExtras/QPhongMaterial>
#include <Qt3DRender/QCamera>
#include <Qt3DRender/QRenderSurfaceSelector>
#include <Qt3DRender/QViewport>
#include <Qt3DRender/QClearBuffers>
#include <Qt3DRender/QCameraSelector>
Qt3DCore::QEntity *createScene()
{
// Root entity
auto rootEntity = new Qt3DCore::QEntity;
// Torus
auto torusEntity = new Qt3DCore::QEntity(rootEntity);
auto torusMesh = new Qt3DExtras::QTorusMesh;
torusMesh->setRadius(0.5f);
torusMesh->setMinorRadius(0.1f);
torusMesh->setRings(100);
torusMesh->setSlices(20);
auto torusTransform = new Qt3DCore::QTransform;
torusTransform->setScale3D(QVector3D(1.2f, 1.f, 0.8f));
torusTransform->setRotation(QQuaternion::fromAxisAndAngle(QVector3D(1, 0, 0), 45.0f));
torusEntity->addComponent(torusMesh);
torusEntity->addComponent(torusTransform);
torusEntity->addComponent(new Qt3DExtras::QPhongMaterial(rootEntity));
return rootEntity;
}
int main(int argc, char* argv[])
{
QGuiApplication app(argc, argv);
Qt3DExtras::Qt3DWindow view;
// Set camera transform
view.camera()->setPosition(QVector3D(0, 4.0f, 0));
view.camera()->setViewCenter(QVector3D(0, 0, 0));
// Framegraph root node
auto surfaceSelector = new Qt3DRender::QRenderSurfaceSelector();
auto mainViewPort = new Qt3DRender::QViewport(surfaceSelector);
// First RenderView: clear buffers
auto clearBuffers = new Qt3DRender::QClearBuffers(mainViewPort);
clearBuffers->setBuffers(Qt3DRender::QClearBuffers::ColorDepthBuffer);
clearBuffers->setClearColor(Qt::white);
// Second RenderView: left viewport
auto viewPort1 = new Qt3DRender::QViewport(mainViewPort);
viewPort1->setNormalizedRect(QRectF(0.0f, 0.0f, 0.5f, 1.0f));
auto cameraSelector1 = new Qt3DRender::QCameraSelector(viewPort1);
cameraSelector1->setCamera(view.camera());
// Third RenderView: right viewport
auto viewPort2 = new Qt3DRender::QViewport(mainViewPort);
viewPort2->setNormalizedRect(QRectF(0.5f, 0.0f, 0.5f, 1.0f));
auto cameraSelector2= new Qt3DRender::QCameraSelector(viewPort2);
cameraSelector2->setCamera(view.camera());
// Add framegraph and scenegraph to viewer
view.setActiveFrameGraph(surfaceSelector);
view.setRootEntity(createScene());
view.show();
return app.exec();
}
I still think it's kind of a bug that it draws ontop of the viewports but you can put a QNoDraw node in your framegraph as a child of the QClearBuffers:
auto noDraw = new Qt3DRender::QNoDraw(clearBuffers);
That solves the rendering issue.

Qt3D texture parameter

I am using Qt3D (5.11), and I am experiencing asserts when I try to set a QParameter value to be used by a custom fragment shader. Here is the section of code that doesn't seem to be working:
auto entity = new Qt3DRender::QEntity( mRootEntity );
auto material = new Qt3DRender::QMaterial( entity );
// Set up the custom geometry and material for the entity, which works
// fine in tests as long as the fragment shader does not use texture mapping
auto image = new Qt3DRender::QTextureImage( entity );
image->setSource( QString( "qrc:/image.png" ) );
auto texture = new Qt3DRender::QTexture2D( entity );
texture->addTextureImage( image );
material->addParameter( new QParameter( "imageTexture", texture, entity ) );
I've included only the bits of code needed for this question:
Is this a valid way to set a simple texture parameter? If not, what am I missing to set a simple image?
Note that qrc:/image.png is a 256x256 image that I have used elsewhere in this project without a problem.
The code compiles fine, but when I run it I get an assert with the following message: ASSERT: "texture" in file texture\textureimage.cpp, line 94
I am using VS2017 on Windows 10 with Qt 5.11.
I stumbled upon the issue. Parenting the QTextureImage to entity leads to the assert. Leaving off the parent completely (effectively setting it to nullptr) or parenting it to texture fixes the issue.
Here is some working code:
auto texture = new Qt3DRender::QTexture2D( entity );
auto image = new Qt3DRender::QTextureImage( texture );
image->setSource( QString( "qrc:/image.png" ) );
texture->addTextureImage( image );
material->addParameter( new QParameter( "imageTexture", texture, entity ) );
This is probably a bug? If someone knows why the QTextureImage cannot be safely parented to entity, please add a comment.

Build osgEarth application from Qt Creator

I tried to run simple osgearth example:
#include <osgViewer/Viewer>
#include <osgEarth/MapNode>
#include <osgEarth/ImageLayer>
#include <osgEarth/ElevationLayer>
#include <osgEarth/ModelLayer>
#include <osgEarth/GeoTransform>
#include <osgEarthUtil/EarthManipulator>
#include <osgEarthUtil/ExampleResources>
#include <osgEarthUtil/AutoScaleCallback>
#include <osgEarthDrivers/tms/TMSOptions>
#include <osgEarthDrivers/wms/WMSOptions>
#include <osgEarthDrivers/gdal/GDALOptions>
#include <osgEarthDrivers/osg/OSGOptions>
#include <osgEarthDrivers/xyz/XYZOptions>
#include <osg/PositionAttitudeTransform>
using namespace osgEarth;
using namespace osgEarth::Drivers;
using namespace osgEarth::Util;
/**
* How to create a simple osgEarth map and display it.
*/
int
main(int argc, char** argv)
{
osg::ArgumentParser arguments(&argc,argv);
// create the empty map.
Map* map = new Map();
// add a TMS imagery layer:
TMSOptions imagery;
imagery.url() = "http://readymap.org/readymap/tiles/1.0.0/7/";
map->addLayer( new ImageLayer("ReadyMap Imagery", imagery) );
// add a TMS elevation layer:
TMSOptions elevation;
elevation.url() = "http://readymap.org/readymap/tiles/1.0.0/116/";
map->addLayer( new ElevationLayer("ReadyMap Elevation", elevation) );
// add a semi-transparent XYZ layer:
XYZOptions xyz;
xyz.url() = "http://[abc].tile.openstreetmap.org/{z}/{x}/{y}.png";
xyz.profile()->namedProfile() = "spherical-mercator";
ImageLayer* imageLayer = new ImageLayer("OSM", xyz);
imageLayer->setOpacity(0.5f);
map->addLayer(imageLayer);
// add a local GeoTIFF inset layer:
GDALOptions gdal;
gdal.url() = "../data/boston-inset.tif";
map->addLayer(new ImageLayer("Boston", gdal));
// add a WMS radar layer with transparency, and disable caching since
// this layer updates on the server periodically.
WMSOptions wms;
wms.url() = "http://mesonet.agron.iastate.edu/cgi-bin/wms/nexrad/n0r.cgi";
wms.format() = "png";
wms.layers() = "nexrad-n0r";
wms.srs() = "EPSG:4326";
wms.transparent() = true;
ImageLayerOptions wmsLayerOptions("WMS NEXRAD", wms);
wmsLayerOptions.cachePolicy() = CachePolicy::NO_CACHE;
map->addLayer(new ImageLayer(wmsLayerOptions));
// add a local simple image as a layer using the OSG driver:
OSGOptions osg;
osg.url() = "../data/osgearth.gif";
osg.profile()->srsString() = "wgs84";
osg.profile()->bounds()->set(-90.0, 10.0, -80.0, 15.0);
map->addLayer(new ImageLayer("Simple image", osg));
// put a model on the map atop Pike's Peak, Colorado, USA
osg::ref_ptr<osg::Node> model = osgDB::readRefNodeFile("cow.osgt.(0,0,3).trans.osgearth_shadergen");
if (model.valid())
{
osg::PositionAttitudeTransform* pat = new osg::PositionAttitudeTransform();
pat->addCullCallback(new AutoScaleCallback<osg::PositionAttitudeTransform>(5.0));
pat->addChild(model.get());
GeoTransform* xform = new GeoTransform();
xform->setPosition(GeoPoint(SpatialReference::get("wgs84"), -105.042292, 38.840829));
xform->addChild(pat);
map->addLayer(new ModelLayer("Model", xform));
}
// make the map scene graph:
MapNode* node = new MapNode( map );
// initialize a viewer:
osgViewer::Viewer viewer(arguments);
viewer.setCameraManipulator( new EarthManipulator() );
viewer.getCamera()->setSmallFeatureCullingPixelSize(-1.0f);
viewer.setSceneData( node );
// add some stock OSG handlers:
MapNodeHelper().configureView(&viewer);
return viewer.run();
}
But my QtCreator can't find all the osgEarth package. At the same time, he found the package osgViewer/Viewer without any problems.
Okay. I took the test .earth file and tried to run with osgearth_viewer file.earth:
<map name="readymap.org" type="geocentric">
<image name="readymap_imagery" driver="tms">
<url>http://readymap.org/readymap/tiles/1.0.0/7/</url>
</image>
<elevation name="readymap_elevation" driver="tms" vdatum="egm96">
<url>http://readymap.org/readymap/tiles/1.0.0/116/</url>
</elevation>
<xi:include href="viewpoints.xml"/>
</map>
Output osgearth_viewer file.earth:
[osgEarth]* [MapNodeHelper] No earth file.
[osgEarth]
Usage: osgearth_viewer file.earth
--sky : add a sky model
--ocean : add an ocean model
--kml <file.kml> : load a KML or KMZ file
--coords : display map coords under mouse
--dms : dispay deg/min/sec coords under mouse
--dd : display decimal degrees coords under mouse
--mgrs : show MGRS coords under mouse
--ortho : use an orthographic camera
--autoclip : installs an auto-clip plane callback
--images [path] : finds and loads image layers from folder [path]
--image-extensions [ext,...] : with --images, extensions to use
--out-earth [file] : write the loaded map to an earth file
--uniform [name] [min] [max] : create a uniform controller with min/max values
What it could be? I don't understand. Help, please :(
Try this simple thing:
https://github.com/gwaldron/osgearth/blob/master/tests/gdal_multiple_files.earth
Download and try to load the above simple file. Make sure you have all the data related to the ".earth" file.
Thanks.

vtk c++ update contour from contourfilter

I have a 3D .vtk model that I render and I extract the contour from the resulting image using a vtkContourFilter (using vtk version 7.0.0 on Ubuntu 16.04).
I would like to project it from different perspectives, but as I loop over the different camera positions (I checked that the camera positions are indeed changed) the interactive viewer that launches with each iteration always shows the contour from the first image.
When I output the first few coordinates of the found contour points (which I store as a vtkPolyData) I also noticed that the content in my set of contour points does not change.
I have tried some online suggestions that worked for others, such as adding:
ContFilter->Modified();
ContFilter->Update();
and
polyData->Modified(); // This is the 3D vtkPolyData that I project
and
ContFilter->SetValue(0, 10);
ContFilter->SetValue(0, 255);
As a wild guess I also tried adding:
polyData->Modified();
// Remove old links
renderWindow->RemoveRenderer(renderer);
mapper->RemoveAllInputs();
// Set new links
renderer->SetActiveCamera(camera);
renderWindow->AddRenderer(renderer);
renderer->Modified();
renderer->ResetCameraClippingRange();
renderWindow->Modified();
mapper->SetInputData(polyData);
renderWindow->Render();
within the for loop, before using the ContourFilter, but it still does not update. With that I tried everything I could think of and find online.
This is the relevant code:
// Prepare the rendering environment to project the 3D model to an image from different perspectives
vtkSmartPointer<vtkDataSetMapper> mapper = vtkSmartPointer<vtkDataSetMapper>::New();
mapper->SetInputData(polyData);
mapper->ScalarVisibilityOff();
vtkSmartPointer<vtkActor> actor = vtkSmartPointer<vtkActor>::New();
actor->SetMapper(mapper);
actor->GetProperty()->SetInterpolationToFlat();
vtkSmartPointer<vtkRenderer> renderer = vtkSmartPointer<vtkRenderer>::New();
renderer->SetBackground(1,1,1);
renderer->AddActor(actor);
vtkSmartPointer<vtkCamera> camera = vtkSmartPointer<vtkCamera>::New();
vtkSmartPointer<vtkRenderWindow> renderWindow = vtkSmartPointer<vtkRenderWindow>::New();
renderWindow->SetOffScreenRendering(1);
vtkSmartPointer<vtkWindowToImageFilter> windowToImageFilter = vtkSmartPointer<vtkWindowToImageFilter>::New();
vtkSmartPointer<vtkContourFilter> ContFilter = vtkSmartPointer<vtkContourFilter>::New();
vtkSmartPointer<vtkPolyData> contour = vtkSmartPointer<vtkPolyData>::New();
// Loop over the camera positions. At each iteration render/project,
// extract the contour and finally render the 3D model and the found
// contour
double * iPoint;
double * camPos;
double * contourStart;
int nContours;
for(int i=0; i<positions->GetNumberOfPoints(); i++){
// Print the camera position
iPoint = positions->GetPoint(i);
std::cout << iPoint[0] << " " << iPoint[1] << " " << iPoint[2] << std::endl;
//Move camera
camera->SetPosition(iPoint[0], iPoint[1], iPoint[2]);
camera->SetFocalPoint(focalPointOrig[0], focalPointOrig[1], focalPointOrig[2]);
camera->SetViewAngle(viewAngle);
camera->Modified();
camera->SetRoll(90);
// Does this help to update the view?
polyData->Modified();
// Remove old links and set them again
renderWindow->RemoveRenderer(renderer);
mapper->RemoveAllInputs();
renderer->SetActiveCamera(camera);
renderWindow->AddRenderer(renderer);
renderer->Modified();
renderer->ResetCameraClippingRange();
renderWindow->Modified();
// Render/project the data
mapper->SetInputData(polyData);
renderWindow->Render();
// Print camera position for debugging
camera->GetPosition(camPos);
std::cout << camPos[0] << " " << camPos[1] << " " << camPos[2] << std::endl;
// Get the image and apply a contourfilter
windowToImageFilter->SetInput(renderWindow);
windowToImageFilter->Update();
ContFilter->SetInputConnection(windowToImageFilter->GetOutputPort());
// Saw someone do this as a workaround for updating the view
ContFilter->SetValue(0, 10);
ContFilter->SetValue(0, 255);
// Does this help to update the view?
ContFilter->Modified();
//Get the contour from the contourfilter
ContFilter->Update();
contour = ContFilter->GetOutput();
// Print the first points coordinates to see if they changed
contourStart = contour->GetPoint(1);
std::cout << contourStart[0] << " " << contourStart[1] << " " << std::endl;
// Print the number of contours to see if it may be stored as an additional contour
nContours = ContFilter->GetNumberOfContours();
std::cout << nContours << std::endl;
// Render the 3D model and the found contour
actor->GetProperty()->SetColor(0.9,0.9,0.8);
// Create a mapper and actor of the silhouette
vtkSmartPointer<vtkPolyDataMapper> mapper_contour = vtkSmartPointer<vtkPolyDataMapper>::New();
mapper_contour->SetInputData(contour);
// Try this again here
polyData->Modified();
vtkSmartPointer<vtkActor> actor_contour = vtkSmartPointer<vtkActor>::New();
actor_contour->SetMapper(mapper_contour);
actor_contour->GetProperty()->SetLineWidth(2.);
// 2 renderers and a render window
vtkSmartPointer<vtkRenderer> renderer1 = vtkSmartPointer<vtkRenderer>::New();
renderer1->AddActor(actor);
vtkSmartPointer<vtkRenderer> renderer2 = vtkSmartPointer<vtkRenderer>::New();
renderer2->AddActor(actor_contour);
// Set the 3D model renderer to the same perspective but don't change the camera perspective of the contour
renderer1->SetActiveCamera(camera);
// Setup the window
vtkSmartPointer<vtkRenderWindow> renderwindow = vtkSmartPointer<vtkRenderWindow>::New();
renderwindow->SetSize(1600, 800);
renderwindow->AddRenderer(renderer1);
renderer1->SetViewport(0., 0., 0.5, 1.);
renderwindow->AddRenderer(renderer2);
renderer2->SetViewport(0.5, 0., 1., 1.);
// Setup the interactor
vtkSmartPointer<vtkInteractorStyleTrackballCamera> style = vtkSmartPointer<vtkInteractorStyleTrackballCamera>::New();
vtkSmartPointer<vtkRenderWindowInteractor> iren = vtkSmartPointer<vtkRenderWindowInteractor>::New();
iren->SetRenderWindow( renderwindow);
iren->SetInteractorStyle(style);
// Display the coordinate system axes
vtkSmartPointer<vtkAxesActor> axes = vtkSmartPointer<vtkAxesActor>::New();
vtkSmartPointer<vtkOrientationMarkerWidget> widget = vtkSmartPointer<vtkOrientationMarkerWidget>::New();
widget->SetOutlineColor( 0.9300, 0.5700, 0.1300 );
widget->SetOrientationMarker( axes );
widget->SetInteractor( iren );
widget->SetViewport( 0.0, 0.0, 0.4, 0.4 );
widget->SetEnabled( 1 );
widget->InteractiveOn();
// Render the 3D model and the found contour
renderwindow->Render();
iren->Start();
}
Just found the answer.
As mentioned in the warning in the detailed description of the vtkWindowToImageFilter class reference webpage (https://www.vtk.org/doc/nightly/html/classvtkWindowToImageFilter.html), vtkWindows generally do not rerender unless you call their Modified() function. Now my projected views are updated like I wanted.
So I changed
// Get the image and apply a contourfilter
windowToImageFilter->SetInput(renderWindow);
windowToImageFilter->Update();
to
// Get the image and apply a contourfilter
windowToImageFilter->Modified();
windowToImageFilter->SetInput(renderWindow);
windowToImageFilter->Update();
See here the warning text in case the link above ever stops working:
Warning:
A vtkWindow doesn't behave like other parts of the VTK pipeline: its modification time doesn't get updated when an image is rendered. As a result, naive use of vtkWindowToImageFilter will produce an image of the first image that the window rendered, but which is never updated on subsequent window updates. This behavior is unexpected and in general undesirable.
To force an update of the output image, call vtkWindowToImageFilter's Modified method after rendering to the window.
In VTK versions 4 and later, this filter is part of the canonical way to output an image of a window to a file (replacing the obsolete SaveImageAsPPM method for vtkRenderWindows that existed in 3.2 and earlier). Connect this filter to the output of the window, and filter's output to a writer such as vtkPNGWriter.
Reading back alpha planes is dependent on the correct operation of the render window's GetRGBACharPixelData method, which in turn is dependent on the configuration of the window's alpha planes. As of VTK 4.4+, machine-independent behavior is not automatically assured because of these dependencies.

VTK Toolkit - vtkCutter Performance

I use the VTK Toolkit to load an OBJ file and a vtkCutter to cut through the data set with a play and then draw the outline of the cut. For large objects this is can become quite slow as another user pointed out in the VTK Users Forum.
Is there a way to make the cutter use a hierarchical data structure to gain better performance?
This is the code:
#include <vtkSmartPointer.h>
#include <vtkCubeSource.h>
#include <vtkPolyDataMapper.h>
#include <vtkPlane.h>
#include <vtkCutter.h>
#include <vtkProperty.h>
#include <vtkActor.h>
#include <vtkRenderer.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkOBJReader.h>
int main(int argc, char *argv[])
{
// Parse command line arguments
if (argc != 2) {
std::cout << "Usage: " << argv[0] << " Filename(.obj)" << std::endl;
return EXIT_FAILURE;
}
std::string filename = argv[1];
vtkSmartPointer<vtkOBJReader> obj = vtkSmartPointer<vtkOBJReader>::New();
obj->SetFileName(filename.c_str());
obj->Update();
vtkSmartPointer<vtkPolyDataMapper> mapper = vtkSmartPointer<vtkPolyDataMapper>::New();
mapper->SetInputConnection(obj->GetOutputPort());
// Create a plane to cut,here it cuts in the XZ direction (xz normal=(1,0,0);XY =(0,0,1),YZ =(0,1,0)
vtkSmartPointer<vtkPlane> plane = vtkSmartPointer<vtkPlane>::New();
plane->SetOrigin(0, 0, 0);
plane->SetNormal(1, 0, 0);
// Create cutter
vtkSmartPointer<vtkCutter> cutter = vtkSmartPointer<vtkCutter>::New();
cutter->SetCutFunction(plane);
cutter->SetInputConnection(obj->GetOutputPort());
cutter->Update();
vtkSmartPointer<vtkPolyDataMapper> cutterMapper = vtkSmartPointer<vtkPolyDataMapper>::New();
cutterMapper->SetInputConnection(cutter->GetOutputPort());
// Create plane actor
vtkSmartPointer<vtkActor> planeActor = vtkSmartPointer<vtkActor>::New();
planeActor->GetProperty()->SetColor(1.0, 1, 0);
planeActor->GetProperty()->SetLineWidth(2);
planeActor->SetMapper(cutterMapper);
// Create cube actor
vtkSmartPointer<vtkActor> cubeActor = vtkSmartPointer<vtkActor>::New();
cubeActor->GetProperty()->SetColor(0.5, 1, 0.5);
cubeActor->GetProperty()->SetOpacity(0.5);
cubeActor->SetMapper(mapper);
// Create renderers and add actors of plane and cube
vtkSmartPointer<vtkRenderer> renderer = vtkSmartPointer<vtkRenderer>::New();
renderer->AddActor(planeActor); //display the rectangle resulting from the cut
renderer->AddActor(cubeActor); //display the cube
// Add renderer to renderwindow and render
vtkSmartPointer<vtkRenderWindow> renderWindow = vtkSmartPointer<vtkRenderWindow>::New();
renderWindow->AddRenderer(renderer);
renderWindow->SetSize(600, 600);
vtkSmartPointer<vtkRenderWindowInteractor> interactor = vtkSmartPointer<
vtkRenderWindowInteractor>::New();
interactor->SetRenderWindow(renderWindow);
renderer->SetBackground(0, 0, 0);
renderWindow->Render();
interactor->Start();
return EXIT_SUCCESS;
}
vtkCutter slices meshes using an arbitrarily complex func(x,y,z) and is used here with a simple plane to describe that function, which is a common and well covered special case, as the cut countour lies on a simple plane and will hence be a simple (flat) polygon.
These generic implementations usually cost alot of CPU time, because all special cases of poly cutting are expected to occur in case of vtkCutter.
There's also a slowdown coming from calling virtual functions in the vast class hierarchy of VTK. Without special hacks, it solely depends on the compiler to optimize the virtual function pointer lookup out of a loop, while VTK calls virtual functions (the filter function, for example) many times in one or more nested loops.
See this for related info: about the cost of virtual function
VTK uses doubles almost everywhere, even if one could live with floats. Conversion and high precision also add quiet a bit of computation and memory overhead.
VTK (5.8) does not explicitly involve SIMD operations like SSE, afaik.
...
Search for topics like these:
Algorithm or software for slicing a mesh
Generate 2D cross-section polygon from 3D mesh.
Despite doing this on a CPU, one could also use an OpenGL geometry shader in a transform feedback pass to extract the cut contour determined by a cut plane. Doing this in OpenCL is also possible, however, if no GPU based compute device is available, it might get slower than a C or C++ implementation.
To render the meshes, one could use any OpenGL 3+ capable Renderer:
Ogre3D
Unity3D
Irrlicht
OSG
a simple, self made OpenGL 3 renderer.
...
more: What is the best way to have realtime 3D rendering in an engineering application?