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.
Related
I've started from the VTK ConstrainedDelaunay2D example and added my own points:
#include <vtkSmartPointer.h>
#include <vtkDelaunay2D.h>
#include <vtkCellArray.h>
#include <vtkProperty.h>
#include <vtkPolyDataMapper.h>
#include <vtkActor.h>
#include <vtkPoints.h>
#include <vtkPolyData.h>
#include <vtkPolygon.h>
#include <vtkMath.h>
#include <vtkRenderer.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkNamedColors.h>
#include <vtkVersionMacros.h> // For version macros
int main(int, char *[])
{
vtkSmartPointer<vtkPoints> points = vtkSmartPointer<vtkPoints>::New();
int ptsHeight = 400;
std::vector<std::vector<int>> pts{ {166, 127},{103, 220},{166, 190},{174, 291},{189, 226},{227, 282},{213, 187},{242, 105},{196, 131},{182, 83} };
for (size_t i = 0; i < pts.size(); i++)
{
// !important: flip y
int x = pts[i][0];
int y = ptsHeight - pts[i][1];
points->InsertNextPoint(x, y, 0);
}
vtkSmartPointer<vtkPolyData> aPolyData = vtkSmartPointer<vtkPolyData>::New();
aPolyData->SetPoints(points);
// Create a cell array to store the polygon in
vtkSmartPointer<vtkCellArray> aCellArray = vtkSmartPointer<vtkCellArray>::New();
// Define a polygonal hole with a clockwise polygon
vtkSmartPointer<vtkPolygon> aPolygon = vtkSmartPointer<vtkPolygon>::New();
for (unsigned int i = 0; i < pts.size(); i++)
{
aPolygon->GetPointIds()->InsertNextId(i);
}
aCellArray->InsertNextCell(aPolygon);
// Create a polydata to store the boundary. The points must be the
// same as the points we will triangulate.
vtkSmartPointer<vtkPolyData> boundary =
vtkSmartPointer<vtkPolyData>::New();
boundary->SetPoints(aPolyData->GetPoints());
boundary->SetPolys(aCellArray);
// Triangulate the grid points
vtkSmartPointer<vtkDelaunay2D> delaunay =
vtkSmartPointer<vtkDelaunay2D>::New();
delaunay->SetInputData(aPolyData);
delaunay->SetSourceData(boundary);
// Visualize
vtkSmartPointer<vtkPolyDataMapper> meshMapper =
vtkSmartPointer<vtkPolyDataMapper>::New();
meshMapper->SetInputConnection(delaunay->GetOutputPort());
vtkSmartPointer<vtkNamedColors> colors =
vtkSmartPointer<vtkNamedColors>::New();
vtkSmartPointer<vtkActor> meshActor =
vtkSmartPointer<vtkActor>::New();
meshActor->SetMapper(meshMapper);
meshActor->GetProperty()->EdgeVisibilityOn();
meshActor->GetProperty()->SetEdgeColor(colors->GetColor3d("Peacock").GetData());
meshActor->GetProperty()->SetInterpolationToFlat();
meshActor->GetProperty()->SetBackfaceCulling(true);
// Create a renderer, render window, and interactor
vtkSmartPointer<vtkRenderer> renderer = vtkSmartPointer<vtkRenderer>::New();
vtkSmartPointer<vtkRenderWindow> renderWindow = vtkSmartPointer<vtkRenderWindow>::New();
renderWindow->AddRenderer(renderer);
vtkSmartPointer<vtkRenderWindowInteractor> renderWindowInteractor = vtkSmartPointer<vtkRenderWindowInteractor>::New();
renderWindowInteractor->SetRenderWindow(renderWindow);
// Add the actor to the scene
renderer->AddActor(meshActor);
//renderer->AddActor(boundaryActor);
renderer->SetBackground(colors->GetColor3d("Mint").GetData());
// Render and interact
renderWindow->SetSize(640, 480);
renderWindow->Render();
renderWindowInteractor->Start();
return EXIT_SUCCESS;
}
I'm experiencing two issues:
I get different results if I flip the Y coordinates: why is that ?
Why are there faces pointing in the wrong direction (flipped normal / wrong winding )?
Here's what I mean by the 1st issue:
If I don't flip the Y coordinates I get this:
I get the same effect if I don't flip the Y axis but insert the boundary polygon in reverse order:
for (unsigned int i = 0; i < pts.size(); i++)
{
aPolygon->GetPointIds()->InsertNextId(pts.size() - 1 - i);
}
I don't think I fully understand how the boundary/constraint works.
I thoght that the same points should produce the same triangulation wether the vertices are flipped vertically or not. (I suspect the order of indices changes then ?)
Regarding the second issue (unpredictable flipped faces) I'm not sure what the best way forward is. I had a look at the vtkDelaunay2D class and couldn't find anything related.
(I've tried setting projection plane mode to VTK_DELAUNAY_XY_PLANE, but it didn't seem to affect the output)
I've also tried to use vtkPolyDataNormals but got no output:
vtkSmartPointer<vtkPolyDataNormals> normalGenerator = vtkSmartPointer<vtkPolyDataNormals>::New();
normalGenerator->SetInputData(delaunay->GetOutput());
normalGenerator->ComputePointNormalsOff();
normalGenerator->ComputeCellNormalsOn();
normalGenerator->FlipNormalsOn();
normalGenerator->Update();
(normalGenerator's output has 0 cells and points)
Is there a way to compute constrained delaunay triangulation for a list of 2d points and ensure all the faces point the same way ? (If so, how ? Would it be possible to do this with the vtkDelaunay2D class alone or is it necessary to use other filters?)
Any hints/tips are more than welcome :)
I'm using VTK 8.2 by the way.
the flipping in y effectively reverses the faces orientation (what is clockwise becomes anti-clockwise, like in a mirror).
I'm not sure I can reproduce your example above. A quick test in python seems to give the expected behavior, maybe you can start from this and map it to your c++ version:
import vedo
pts = [
[166, 127],
[103, 220],
[166, 190],
[174, 291],
[189, 226],
[227, 282],
[213, 187],
[242, 105],
[196, 131],
[182, 83],
]
ids = [[2,4,6], [0,2,8]] # faces to erase by pt-index (clockwise)
dly = vedo.delaunay2D(pts, mode='xy', boundaries=ids)
dly.c('grey5').lc('red4').lw(2)
labels = vedo.Points(pts).labels('id').z(1)
vedo.show(labels, dly, axes=1)
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.
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;
}