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.
Related
I am trying to use layer filtering as shown in this answer. For this I wrote a simple test (see below). This is a continuation of the question.
At a certain position of the red sphere, an artifact appears, which looks like display from the another camera in coordinates (0.0, 0.0, 0.0).
See screen:
In my example, the red sphere can be moved with the WSAD buttons.
See (-7, 0, -14) red sphere position. How to remove these artifacts?
The full test project can be viewed here.
main.cpp
int main(int argc, char *argv[])
{
QGuiApplication application(argc, argv);
My3DWindow window;
auto sphere1 = new Qt3DCore::QEntity(window.Scene());
auto sphere2 = new Qt3DCore::QEntity(window.Scene());
// material, transform, mesh initialisation
sphere1->addComponent(material1);
sphere1->addComponent(spheremesh1);
sphere1->addComponent(transform1);
sphere1->addComponent(window.OpaqueLayer());
sphere2->addComponent(material2);
sphere2->addComponent(spheremesh2);
sphere2->addComponent(transform2);
sphere2->addComponent(window.TransparentLayer());
window.show();
return application.exec();
}
My3DWindow class:
My3DWindow::My3DWindow(QScreen *screen):
Qt3DExtras::Qt3DWindow(screen)
{
m_Scene = new Qt3DCore::QEntity;
setRootEntity(m_Scene);
auto renderSurfaceSelector = new Qt3DRender::QRenderSurfaceSelector(m_Scene);
renderSurfaceSelector->setSurface(this);
auto clearBuffers = new Qt3DRender::QClearBuffers(renderSurfaceSelector);
clearBuffers->setBuffers(Qt3DRender::QClearBuffers::AllBuffers);
clearBuffers->setClearColor(Qt::gray);
auto viewport = new Qt3DRender::QViewport(renderSurfaceSelector);
auto cameraSelector = new Qt3DRender::QCameraSelector(viewport);
m_Camera = new Qt3DRender::QCamera(cameraSelector);
m_Camera->lens()->setPerspectiveProjection(45.0f, 16.0f/9.0f, 0.1f, 1000.0f);
m_Camera->setPosition(QVector3D(0.0f, 0.0f, 100.0f));
m_Camera->setViewCenter(QVector3D(0.0f, 0.0f, 0.0f));
cameraSelector->setCamera(m_Camera);
auto cameraController = new Qt3DExtras::QFirstPersonCameraController(m_Scene);
cameraController->setCamera(m_Camera);
m_OpaqueLayer = new Qt3DRender::QLayer;
auto opaqueFilter = new Qt3DRender::QLayerFilter(m_Camera);
opaqueFilter->addLayer(m_OpaqueLayer);
m_TransparentLayer = new Qt3DRender::QLayer;
auto transparentFilter = new Qt3DRender::QLayerFilter(m_Camera);
transparentFilter->addLayer(m_TransparentLayer);
setActiveFrameGraph(renderSurfaceSelector);
}
You can fix that by adding a QNoDraw node as a child of clearBuffers, as shown in this answer. The "artifact" is not caused by the layer filters, it is a problem of QClearBuffers itself.
Making clearBuffers a child of cameraSelector may seem to work on the surface, but what's actually happening is that everything is being rendered twice, so the transparent sphere will appear darker. You can verify this by commenting out either one of the filters: the objects in the corresponding layer will get rendered anyway.
By leaving clearBuffers as a child of renderSurfaceSelector and adding the QNoDraw, you don't get undesired stuff drawn on top of your viewport, and the filters behave as expected.
Fixed. In the original example, an error found. I don't fully understand why it's the right thing to do:
auto clearBuffers = new Qt3DRender::QClearBuffers(cameraSelector);
insted
auto clearBuffers = new Qt3DRender::QClearBuffers(renderSurfaceSelector);
I am working on some codes about qt3d. I create a Qt3DWindow(named view).
Try to add a root entity(named rootEntity) in it.
Put a cube entity like this:
m_cubeEntity = new Qt3DCore::QEntity;
...
Qt3DExtras::QCuboidMesh *cubeMesh = new Qt3DExtras::QCuboidMesh;
Qt3DRender::QMaterial *cubeMaterial = new Qt3DRender::QMaterial;
Qt3DCore::QTransform *cubeTransform = new Qt3DCore::QTransform;
Qt3DRender::QObjectPicker *objectPicker = new Qt3DRender::QObjectPicker;
...
m_cubeEntity.addComponent(cubeMesh);
m_cubeEntity.addComponent(cubeMaterial);
m_cubeEntity.addComponent(cubeTransform);
m_cubeEntity.adComponent(objectPicker);
m_cubeEntity.setParent(m_rootEntity);
Everything works fine.
And then I find using PickingSettings.BoundingVolumePicking to pick my cube entity is inaccuracy. I want the PickingSettings.TrianglePicking.
====================================================================/
So then i do it like this:
m_renderSettings = new Qt3DRender::QRenderSettings();
m_renderSettings->pickingSettings()->setPickMethod(Qt3DRender::QPickingSettings::TrianglePicking);
m_renderSettings->pickingSettings()->setPickResultMode(Qt3DRender::QPickingSettings::AllPicks);
m_renderer = new Qt3DExtras::QForwardRenderer();
m_renderer->setClearColor(Qt::lightGray);
m_renderSettings->setActiveFrameGraph(m_renderer);
m_rootEntity->addComponent(m_renderSettings);
But now nothing is rendered. If i remove "m_renderSettings" from rootEntity, everything returns correct.
How to set rendersetting correctly for the root entity?
Most likely you just set a bad framegraph. You can easily display the standard framegraph using dumpObjectTree(), which tends to be a very useful function in debugging the framegraph of Qt3D.
For your simple use case it suffices to just use the renderSettings already contained in the activeFrameGraph() of the Qt3DWindow.
Just try the following simple app, that contains your desired QObjectPicker.
#include <QApplication>
#include <Qt3DCore/QTransform>
#include <Qt3DRender/QPickEvent>
#include <Qt3DRender/QCamera>
#include <Qt3DRender/QFrameGraphNode>
#include <Qt3DRender/QObjectPicker>
#include <Qt3DExtras/QDiffuseSpecularMaterial>
#include <Qt3DExtras/QCuboidMesh>
#include <Qt3DExtras/Qt3DWindow>
#include <Qt3DRender/QRenderSettings>
#include <QDebug>
int main(int argc, char* argv[])
{
QApplication a(argc, argv);
auto view = new Qt3DExtras::Qt3DWindow();
auto rootEntity = new Qt3DCore::QEntity();
view->setRootEntity(rootEntity);
// Shows your framegraph! Simple forward renderer!
view->activeFrameGraph()->dumpObjectTree();
auto rendersettings=view->renderSettings();
rendersettings->pickingSettings()->setPickMethod(Qt3DRender::QPickingSettings::TrianglePicking);
rendersettings->pickingSettings()->setPickResultMode(Qt3DRender::QPickingSettings::AllPicks);
auto cameraEntity = view->camera();
cameraEntity->lens()->setPerspectiveProjection(45.0f, 1., 0.1f, 10000.0f);
cameraEntity->setPosition(QVector3D(0, 2, 0));
cameraEntity->setUpVector(QVector3D(0, 1, 0));
cameraEntity->setViewCenter(QVector3D(0, 0, 0));
auto cubeEntity = new Qt3DCore::QEntity(rootEntity);
auto cubeMesh = new Qt3DExtras::QCuboidMesh;
cubeMesh->setXExtent(1.);
cubeMesh->setYExtent(1.);
auto cubeMaterial = new Qt3DExtras::QDiffuseSpecularMaterial;
auto objectPicker = new Qt3DRender::QObjectPicker;
QObject::connect(objectPicker, &Qt3DRender::QObjectPicker::clicked, [](Qt3DRender::QPickEvent* pick) {
qDebug() << pick;
});
cubeEntity->addComponent(cubeMesh);
cubeEntity->addComponent(cubeMaterial);
cubeEntity->addComponent(objectPicker);
view->show();
return a.exec();
}
In my company there is a shift from an old 3D engine to Qt3d. One goal of this undertaking is to compare the rendered view of the old 3D engine with the Qt3d rendering.
For this I wrote a small example application, where I can compare old and new rendering. There are still plenty differences. My first idea was to switch of all light sources in both engines and compare the silhouettes of both renderings.
Now, there is some thing, that I really don't understand and this has to do with the Qt3d lighting model.
In my small example app I define a simple sphere mesh and a camera and a check box, which can disable a point light source. The sphere is lighted by the phong reflection model.
No, if I switch out mylight, I'm expecting a simple black sphere in my viewer, as there is really no light. Instead there is still some lighting (from a different source). I think, that there is some other light source, that is activated by default.
How, can I disable all light sources in Qt3d? As a side question I'm also wondering why meshMaterial->setAmbient(QColor(255, 0, 0)); has no visible effect on the material after all. It really doesn't matter what you will enter here.
#include <QApplication>
#include <QWidget>
#include <QCheckBox>
#include <QVBoxLayout>
#include <QFrame>
#include <Qt3DCore/QTransform.h>
#include <Qt3DRender/QCamera.h>
#include <Qt3DRender/QRenderSettings.h>
#include <Qt3DRender/QPointLight.h>
#include <Qt3DExtras/QSphereMesh>
#include <Qt3DExtras/QPhongMaterial>
#include <Qt3DExtras/qforwardrenderer.h>
#include <Qt3DExtras/Qt3DWindow.h>
#include <Qt3DExtras/QFirstPersonCameraController.h>
int main(int argc, char* argv[])
{
QApplication a(argc, argv);
auto view = new Qt3DExtras::Qt3DWindow();
view->defaultFrameGraph()->setClearColor(QColor(255,255,255));
auto rootEntity = new Qt3DCore::QEntity();
view->setRootEntity(rootEntity);
auto cameraEntity = view->camera();
cameraEntity->lens()->setPerspectiveProjection(45.0f, 1., 0.1f, 10000.0f);
cameraEntity->setPosition(QVector3D(5, 5, 5));
cameraEntity->setUpVector(QVector3D(0, 1, 0));
cameraEntity->setViewCenter(QVector3D(0, 0, 0));
auto lightEntity = new Qt3DCore::QEntity(rootEntity);
auto light = new Qt3DRender::QPointLight(lightEntity);
light->setColor("white");
light->setIntensity(1);
lightEntity->addComponent(light);
auto lightTransform = new Qt3DCore::QTransform(lightEntity);
lightTransform->setTranslation(cameraEntity->position());
lightEntity->addComponent(lightTransform);
lightEntity->setEnabled(false);
// For camera controls
auto camController = new Qt3DExtras::QFirstPersonCameraController(rootEntity);
camController->setCamera(cameraEntity);
auto mesh = new Qt3DExtras::QSphereMesh();
mesh->setRadius(1.);
auto meshMaterial = new Qt3DExtras::QPhongMaterial();
meshMaterial->setDiffuse(QColor(0, 255, 0));
meshMaterial->setAmbient(QColor(255, 0, 0));
meshMaterial->setSpecular(QColor(0,0,255));
meshMaterial->setShininess(23);
auto meshEntity = new Qt3DCore::QEntity(rootEntity);
meshEntity->addComponent(mesh);
meshEntity->addComponent(meshMaterial);
meshEntity->setEnabled(true);
auto disableLight = new QCheckBox();
auto container = QWidget::createWindowContainer(view);
QFrame frame;
frame.setLayout(new QVBoxLayout);
frame.layout()->addWidget(container);
frame.layout()->addWidget(disableLight);
QObject::connect(disableLight, &QCheckBox::stateChanged, [lightEntity](auto state) {
lightEntity->setEnabled(state == Qt::CheckState::Checked);
});
frame.setFixedSize(500, 500);
frame.show();
return a.exec();
}
If no light entities are created in your Qt3D scene Qt3D will add one for you. This is to prevent users not seeing anything without light.
Once you add a light yourself the default one is omitted
You can workaround this default behaviour by adding a light with intensity set to zero:
DirectionalLight {
worldDirection: Qt.vector3d(-1, 1, -1)
intensity: 0.0
}
This will give you the following effect:
test with cuboid and sphere mesh with PhongMaterial:
So tinkering with the intensity property of the light might give what you want.
I'm trying to create a Qt3D prototype which is able to render an obj which contains transparent objects. Therefor I need to somehow incorporate QSortPolicy with a custom frame graph. I put together a few examples which I found on the internet (unfortunately there aren't many Qt3D C++ examples). The source of the protoype is shown below. The problem is that I add a QTorusMesh and the content of the QSceneLoader to the root entity, but when rendered only the torus will be shown, the obj (in this case the monkey) is not rendered and I don't know why and also not how to debug it. I dumped all the trees (scene graph and frame graph) and couldn't find any inconsistency. Only thing I can think of that the loaded object somehow needs a different renderer (QGeometryRenderer) which needs to be added to the frame graph? Does someone know what I'm doing wrong?
Examples: Custom frame graph, Scene walker
#include <QGuiApplication>
#include <Qt3DCore/QEntity>
#include <Qt3DCore/QTransform>
#include <Qt3DCore/QAspectEngine>
#include <Qt3DInput/QInputAspect>
#include <Qt3DExtras/Qt3DWindow>
#include <Qt3DExtras/QPhongMaterial>
#include <Qt3DExtras/QOrbitCameraController>
#include <Qt3DExtras/QTorusMesh>
#include <Qt3DRender/QCamera>
#include <Qt3DRender/QRenderAspect>
#include <Qt3DRender/QSceneLoader>
#include <Qt3DRender/QRenderSurfaceSelector>
#include <Qt3DRender/QClearBuffers>
#include <Qt3DRender/QLayerFilter>
#include <Qt3DRender/QViewport>
#include <Qt3DRender/QCameraSelector>
#include <Qt3DRender/QLayer>
#include <Qt3DRender/QRenderSettings>
class SceneWalker : public QObject
{
public:
SceneWalker(Qt3DRender::QSceneLoader* loader):
m_loader(loader)
{}
void onStatusChanged();
private:
void walkEntity(Qt3DCore::QEntity* e, int depth = 0);
Qt3DRender::QSceneLoader* m_loader;
};
void SceneWalker::onStatusChanged()
{
qDebug() << "Status changed:" << m_loader->status();
if (m_loader->status() != Qt3DRender::QSceneLoader::Ready)
return;
// The QSceneLoader instance is a component of an entity. The loaded scene
// tree is added under this entity.
QVector<Qt3DCore::QEntity*> entities = m_loader->entities();
// Technically there could be multiple entities referencing the scene loader
// but sharing is discouraged, and in our case there will be one anyhow.
if (entities.isEmpty())
return;
Qt3DCore::QEntity* root = entities[0];
// Print the tree.
walkEntity(root);
// To access a given node (like a named mesh in the scene), use QObject::findChild().
// The scene structure and names always depend on the asset.
Qt3DCore::QEntity* e = root->findChild<Qt3DCore::QEntity*>(QStringLiteral("PlanePropeller_mesh")); // toyplane.obj
if (e)
qDebug() << "Found propeller node" << e << "with components" << e->components();
}
void SceneWalker::walkEntity(Qt3DCore::QEntity* e, int depth)
{
Qt3DCore::QNodeVector nodes = e->childNodes();
for (int i = 0; i < nodes.count(); ++i)
{
Qt3DCore::QNode* node = nodes[i];
Qt3DCore::QEntity* entity = qobject_cast<Qt3DCore::QEntity*>(node);
if (entity)
{
QString indent;
indent.fill(' ', depth * 2);
qDebug().noquote() << indent << "Entity:" << entity << "Components:" << entity->components();
walkEntity(entity, depth + 1);
}
}
}
int main(int argc, char* argv[])
{
QGuiApplication app(argc, argv);
Qt3DExtras::Qt3DWindow* window = new Qt3DExtras::Qt3DWindow();
// Root
Qt3DCore::QEntity* rootEntity = new Qt3DCore::QEntity();
window->setRootEntity(rootEntity);
Qt3DRender::QRenderSurfaceSelector *renderSurfaceSelector = new Qt3DRender::QRenderSurfaceSelector();
renderSurfaceSelector->setSurface(window);
// clearing the buffers
Qt3DRender::QClearBuffers* clearBuffers = new Qt3DRender::QClearBuffers(renderSurfaceSelector);
clearBuffers->setBuffers(Qt3DRender::QClearBuffers::ColorDepthBuffer);
// Framegraph for objects
Qt3DRender::QLayerFilter* objectsLayerFilter = new Qt3DRender::QLayerFilter(renderSurfaceSelector);
Qt3DRender::QLayer* objectsLayer = new Qt3DRender::QLayer(objectsLayerFilter);
objectsLayerFilter->addLayer(objectsLayer);
Qt3DRender::QViewport* viewport = new Qt3DRender::QViewport(objectsLayer);
Qt3DRender::QCameraSelector* objectsCameraSelector = new Qt3DRender::QCameraSelector(viewport);
Qt3DRender::QCamera* objectsCamera = new Qt3DRender::QCamera(objectsCameraSelector);
objectsCamera->lens()->setPerspectiveProjection(45.f, 16.0f/9.0f, 0.01f, 1000.f);
objectsCamera->setPosition(QVector3D(0, 0, -10));
objectsCamera->setViewCenter(QVector3D(0, 0, 0));
objectsCamera->setUpVector(QVector3D(0, 1, 0));
objectsCameraSelector->setCamera(objectsCamera);
// Set the new framegraph
window->setActiveFrameGraph(renderSurfaceSelector);
window->renderSettings()->setRenderPolicy(Qt3DRender::QRenderSettings::Always);
// camera controls
Qt3DExtras::QOrbitCameraController* camController = new Qt3DExtras::QOrbitCameraController(rootEntity);
camController->setLinearSpeed(50.0f);
camController->setLookSpeed(180.0f);
camController->setCamera(objectsCamera);
// Torus
Qt3DCore::QEntity* torusEntity = new Qt3DCore::QEntity(rootEntity);
Qt3DExtras::QTorusMesh* torusMesh = new Qt3DExtras::QTorusMesh(torusEntity);
torusMesh->setSlices(50.0f);
torusMesh->setRings(50.0f);
torusMesh->setRadius(2.0f);
Qt3DExtras::QPhongMaterial* torusMaterial = new Qt3DExtras::QPhongMaterial(torusEntity);
torusMaterial->setAmbient(Qt::gray);
Qt3DCore::QTransform* torusTransform = new Qt3DCore::QTransform(torusEntity);
torusTransform->setTranslation(QVector3D(0.0f, 0.0f, 10.0f));
torusTransform->setRotationY(50.0f);
torusTransform->setScale(2.0f);
torusEntity->addComponent(torusTransform);
torusEntity->addComponent(torusMesh);
torusEntity->addComponent(torusMaterial);
torusEntity->addComponent(objectsLayer);
// Scene loader
Qt3DCore::QEntity* sceneLoaderEntity = new Qt3DCore::QEntity(rootEntity);
Qt3DRender::QSceneLoader* sceneLoader = new Qt3DRender::QSceneLoader(sceneLoaderEntity);
sceneLoader->setSource(QUrl::fromLocalFile("monkey.obj"));
// Transform
Qt3DCore::QTransform* sceneLoaderTransform = new Qt3DCore::QTransform(sceneLoaderEntity);
sceneLoaderTransform->setScale(2.0f);
sceneLoaderTransform->setTranslation(QVector3D(0.0f, 0.0f, 10.0f));
SceneWalker sceneWalker(sceneLoader);
QObject::connect(sceneLoader, &Qt3DRender::QSceneLoader::statusChanged, &sceneWalker, &SceneWalker::onStatusChanged);
sceneLoaderEntity->addComponent(sceneLoader);
sceneLoaderEntity->addComponent(sceneLoaderTransform);
sceneLoaderEntity->addComponent(objectsLayer);
window->show();
return app.exec();
}
The problem is that you are not actually adding the monkey to the objects layer. QSceneLoader creates its own scene subtree, even when the OBJ file contains a single object. So you are just adding the root entity of the subtree to the objects layer.
The SceneWalker object traverses the QSceneLoader subtree and provides information about its structure. You can just look at the console output, find the names of the entities you want to render, and attach them to the desired layer.
This has to be done asynchronously, using signals and slots. Do this right after creating the QSceneLoader object (assuming your mesh is named "monkey"):
QObject::connect(sceneLoader, &Qt3DRender::QSceneLoader::statusChanged, &app,
[sceneLoader, objectsLayer](Qt3DRender::QSceneLoader::Status s) {
if (s == Qt3DRender::QSceneLoader::Status::Ready)
sceneLoader->entity("monkey")->addComponent(objectsLayer);
});
Since you are using an OBJ file, you can even ditch the SceneWalker entirely. OBJ files are text-based, so you can find the name of the mesh by just opening it with a text editor.
If you succeed with layer filters, you don't need to use QSortPolicy (check out this answer).
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;
}