It seems to me that Qt3D cannot render 2D meshes well. To see what I mean open the shadow map QML example and change the camera controller from FirstPersonCameraController to OrbitCameraController. Run the program and attempt to view the ground plane from below, you will see it disappear. So QT3D just renders 2D meshes from one side and makes them transparent from the other side.
How can I fix this? i.e. render 2D meshes from both sides?
EDIT: Now I know that I have to disable culling for the rendering to work. I came up to this point:
//'this' refers to Qt3DWindow
rootEntity->addComponent(this->renderSettings());
QCullFace *face = new QCullFace();
QRenderStateSet *stateSet = new QRenderStateSet(this->renderSettings()->activeFrameGraph());
QRenderSurfaceSelector *selector = new QRenderSurfaceSelector(this->renderSettings());
face->setMode(QCullFace::NoCulling);
stateSet->addRenderState(face);
selector->setSurface(this);
but this still doesn't seem to change anything. Am I missing something?
Here is a minimally working example:
main.cpp:
#include <QApplication>
#include "clickwindow.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
GraphicsWindow graphicsWindow;
graphicsWindow.show();
return a.exec();
}
graphicswindow.h:
#include <Qt3DExtras/Qt3DWindow>
#include <Qt3DCore/QEntity>
#include <Qt3DCore/QTransform>
class GraphicsWindow : public Qt3DExtras::Qt3DWindow {
public:
GraphicsWindow();
void wheelEvent ( QWheelEvent * event ) override;
private:
Qt3DCore::QEntity *createScene();
Qt3DCore::QTransform *planeTransform;
};
graphicswindow.cpp:
#include "graphicswindow.h"
#include <QMouseEvent>
#include <Qt3DRender/QViewport>
#include <Qt3DRender/QClearBuffers>
#include <Qt3DRender/QRenderSurfaceSelector>
#include <Qt3DRender/QRenderStateSet>
#include <Qt3DRender/QCullFace>
#include <Qt3DRender/QCameraSelector>
#include <Qt3DRender/QCamera>
#include <Qt3DRender/QMaterial>
#include <Qt3DExtras/QGoochMaterial>
#include <Qt3DExtras/QPlaneMesh>
#include <Qt3DRender/QDepthTest>
GraphicsWindow::GraphicsWindow() : Qt3DExtras::Qt3DWindow() {
Qt3DRender::QCamera *camera = this->camera();
camera->lens()->setPerspectiveProjection(45.0f, 16.0f/9.0f, 0.1f, 1000.0f);
camera->setPosition(QVector3D(20.0, 20.0, 20.0f));
camera->setViewCenter(QVector3D(0, 0, 0));
Qt3DRender::QRenderSurfaceSelector *surfaceSelector = new Qt3DRender::QRenderSurfaceSelector;
surfaceSelector->setSurface(this);
Qt3DRender::QViewport *viewport = new Qt3DRender::QViewport(surfaceSelector);
viewport->setNormalizedRect(QRectF(0, 0, 1.0, 1.0));
Qt3DRender::QCameraSelector *cameraSelector = new Qt3DRender::QCameraSelector(viewport);
cameraSelector->setCamera(camera);
Qt3DRender::QClearBuffers *clearBuffers = new Qt3DRender::QClearBuffers(cameraSelector);
clearBuffers->setBuffers(Qt3DRender::QClearBuffers::ColorDepthBuffer);
clearBuffers->setClearColor(Qt::white);
Qt3DRender::QRenderStateSet *renderStateSet = new Qt3DRender::QRenderStateSet(clearBuffers);
Qt3DRender::QCullFace *cullFace = new Qt3DRender::QCullFace(renderStateSet);
cullFace->setMode(Qt3DRender::QCullFace::NoCulling);
renderStateSet->addRenderState(cullFace);
Qt3DRender::QDepthTest *depthTest = new Qt3DRender::QDepthTest;
depthTest->setDepthFunction(Qt3DRender::QDepthTest::Less);
renderStateSet->addRenderState(depthTest);
setActiveFrameGraph(surfaceSelector);
Qt3DCore::QEntity *root = createScene();
setRootEntity(root);
}
void GraphicsWindow::wheelEvent(QWheelEvent *event) {
planeTransform->setRotationZ(planeTransform->rotationZ() + event->delta() / 40.f);
}
Qt3DCore::QEntity* GraphicsWindow::createScene() { // Root entity
Qt3DCore::QEntity *rootEntity = new Qt3DCore::QEntity;
Qt3DCore::QEntity *planeEntity = new Qt3DCore::QEntity(rootEntity);
Qt3DRender::QMaterial *meshMaterial = new Qt3DExtras::QGoochMaterial;
Qt3DExtras::QPlaneMesh *planeMesh = new Qt3DExtras::QPlaneMesh;
planeMesh->setHeight(10);
planeMesh->setWidth(10);
planeTransform = new Qt3DCore::QTransform;
planeEntity->addComponent(planeTransform);
planeEntity->addComponent(planeMesh);
planeEntity->addComponent(meshMaterial);
return rootEntity;
}
Keep in mind that the setActiveFramegraph function of the Qt3DWindow automatically adds the QRenderSettings returned by the renderSettings() function of the window on the frame graph node that you set as the active frame graph. If you are implementing your own 3D window or create and offscreen renderer you have to use QRenderSettings as the root node of your framegraph (the window sets its render settings as the parent of the root frame graph node that you set) and add the render settings to the actual root node, i.e. the node that is the parent of the frame graph and the scene graph.
Related
Tried to create simple program showing torus with lighting. Everything works, but have this problem with light position.
When trying to create new transform for light after creating torus light's transform is being used for new torus position, instead of light.
Tried to change values and move addComponent, but no result.
Here's photo of result
Here's my entire code.
#include <QGuiApplication>
#include <Qt3DExtras/Qt3DWindow>
#include <Qt3DExtras/QTorusMesh>
#include <Qt3DExtras/QPhongMaterial>
#include <Qt3DCore/QEntity>
#include <Qt3DCore/QTransform>
#include <Qt3DRender/QCamera>
#include <Qt3DRender/QPointLight>
Qt3DCore::QEntity *createScene();
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
Qt3DExtras::Qt3DWindow view;
Qt3DCore::QEntity *rootEntity = createScene();
Qt3DRender::QCamera *camera =view.camera();
camera->lens()->setPerspectiveProjection(60,(float)view.width()/view.height(),0.1f,1000.0f);
camera->setPosition(QVector3D(0.0f,0.0f,40.0f));
camera->setViewCenter(QVector3D(0.0f,0.0f,0.0f));
view.setRootEntity(rootEntity);
view.show();
return app.exec();
}
Qt3DCore::QEntity *createScene()
{
Qt3DCore::QEntity *resultEntity = new Qt3DCore::QEntity;
Qt3DExtras::QTorusMesh *torusMesh = new Qt3DExtras::QTorusMesh(resultEntity);
torusMesh->setRadius(15.0f);
torusMesh->setMinorRadius(6.0f);
torusMesh->setSlices(16);
torusMesh->setRings(32);
Qt3DExtras::QPhongMaterial *torusMaterial = new Qt3DExtras::QPhongMaterial(resultEntity);
Qt3DCore::QTransform *torusTransform = new Qt3DCore::QTransform(resultEntity);
resultEntity->addComponent(torusMesh);
resultEntity->addComponent(torusMaterial);
resultEntity->addComponent(torusTransform);
Qt3DCore::QEntity *lightEntity = new Qt3DCore::QEntity(resultEntity);
Qt3DRender::QPointLight *pointLight = new Qt3DRender::QPointLight(lightEntity);
Qt3DCore::QTransform *lightTransform = new Qt3DCore::QTransform(resultEntity);
lightTransform->setTranslation(QVector3D(0.0f,0.0f,30.0f));
resultEntity->addComponent(pointLight);
resultEntity->addComponent(lightTransform);
return resultEntity;
}
How does one display text on the 3D window screen? I know they a text2D entity module, but thats for displaying text in 3D next to the objects. I want to know if I can display 2D text on the screen.
Im using C++ so would like to know if this possible in C++ and not qml thanks.
There is QText2DEntity class for allowing the creation of a 2D text in 3D space.
#include <QApplication>
#include <QGuiApplication>
#include <Qt3DExtras>
#include <Qt3DRender/qcamera.h>
#include <Qt3DCore/qentity.h>
#include <Qt3DRender/qcameralens.h>
#include <QtWidgets/QWidget>
#include <QtWidgets/QHBoxLayout>
int main(int argc, char **argv)
{
QApplication a(argc, argv);
auto *view = new Qt3DExtras::Qt3DWindow;
view->defaultFrameGraph()->setClearColor(QColor(QRgb(0x4d4d4f)));
auto *container = QWidget::createWindowContainer(view);
auto screenSize = view->screen()->size();
container->setMinimumSize(QSize(200, 100));
container->setMaximumSize(screenSize);
auto *widget = new QWidget;
auto *hLayout = new QHBoxLayout(widget);
hLayout->addWidget(container, 1);
auto *input = new Qt3DInput::QInputAspect;
view->registerAspect(input);
// Root entity
auto *rootEntity = new Qt3DCore::QEntity();
// Camera
auto *cameraEntity = view->camera();
cameraEntity->lens()->setPerspectiveProjection(45.0f, 16.0f/7.0f, 0.1f, 1000.0f);
cameraEntity->setPosition(QVector3D(0, 10.0f, 20.0f));
cameraEntity->setUpVector(QVector3D(0, 1, 0));
cameraEntity->setViewCenter(QVector3D(0, 0, 0));
//light
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);
// For camera controls
auto *camController = new Qt3DExtras::QOrbitCameraController (rootEntity);
camController->setCamera(cameraEntity);
// Set root object of the scene
view->setRootEntity(rootEntity);
auto *text2dTransform = new Qt3DCore::QTransform;
text2dTransform->setScale(0.125f);
text2dTransform->setTranslation(QVector3D(-5, 0, 5));
auto *text2d = new Qt3DExtras::QText2DEntity(rootEntity);
text2d->setFont(QFont("monospace"));
text2d->setHeight(20);
text2d->setWidth(100);
text2d->setText("A");
text2d->setColor(Qt::yellow);
text2d->addComponent(text2dTransform);
widget->show();
widget->resize(1200, 800);
return a.exec();
}
Parisa has already provided a nice solution, I'll just post here mine here in case you are looking for something which allows to draw UI-like text:
Like stated in the comments, either draw a rectangular surface on top of everything using orthographic projection mode (similar to my Qt3DBackground example, just draw the texture last and make it transparent).
Or instead use my Qt3D Widget which allows to add Qt's widgets onto Qt3D. This way, you can draw your scene and use a simple QLabel on top.
I'm having a lot of issues to have a simple working app that load a light mesh in Qt3D, nothing shows up on screen.
Here's some code I put together to have a have striped down to show.
You'll notice that it's a shorter version of this Qt example
You won't see it in this example but in my actual project I already
tried loading it with
qrc and
localfile QUrl
Qt3DExtras::QTorusMesh work without an issue.
I tried to watch the loading status of the mesh, on my actual project it does go from None though Loading to Loaded
Also, I have no trouble loading the file su.obj using a Qt QML example, so it's
not the file that is corrupted. The file being a simple Suzanne from Blender, exported.
I used Qt5 and 6.
Finally, I know the question has been asked a few times, but nothing helped.
#include <QGuiApplication>
#include <Qt3DCore/QEntity>
#include <Qt3DRender/QCamera>
#include <Qt3DRender/QCameraLens>
#include <Qt3DCore/QTransform>
#include <Qt3DCore/QAspectEngine>
#include <Qt3DInput/QInputAspect>
#include <Qt3DRender/QRenderAspect>
#include <Qt3DRender/QGeometryRenderer>
#include <Qt3DExtras/QForwardRenderer>
#include <Qt3DExtras/QPhongMaterial>
#include <Qt3DExtras/QSphereMesh>
#include <Qt3DExtras/QTorusMesh>
#include <QMesh>
#include <QPropertyAnimation>
#include "orbittransformcontroller.h"
#include "qorbitcameracontroller.h"
#include "qt3dwindow.h"
#include <qdebug.h>
Qt3DCore::QEntity *createScene()
{
// Root entity
Qt3DCore::QEntity *rootEntity = new Qt3DCore::QEntity;
// Material
Qt3DRender::QMaterial *material = new Qt3DExtras::QPhongMaterial(rootEntity);
// Sphere
Qt3DCore::QEntity *sphereEntity = new Qt3DCore::QEntity(rootEntity);
auto *sphereMesh = new Qt3DRender::QMesh();
sphereMesh->setSource(QUrl::fromLocalFile("su.obj"));
Qt3DCore::QTransform *sphereTransform = new Qt3DCore::QTransform;
OrbitTransformController *controller = new OrbitTransformController(sphereTransform);
controller->setTarget(sphereTransform);
controller->setRadius(20.0f);
QPropertyAnimation *sphereRotateTransformAnimation = new QPropertyAnimation(sphereTransform);
sphereRotateTransformAnimation->setTargetObject(controller);
sphereRotateTransformAnimation->setPropertyName("angle");
sphereRotateTransformAnimation->setStartValue(QVariant::fromValue(0));
sphereRotateTransformAnimation->setEndValue(QVariant::fromValue(360));
sphereRotateTransformAnimation->setDuration(10000);
sphereRotateTransformAnimation->setLoopCount(-1);
sphereRotateTransformAnimation->start();
sphereEntity->addComponent(sphereMesh);
sphereEntity->addComponent(sphereTransform);
sphereEntity->addComponent(material);
return rootEntity;
}
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
Qt3DExtras::Qt3DWindow view;
Qt3DCore::QEntity *scene = createScene();
// Camera
Qt3DRender::QCamera *camera = view.camera();
camera->lens()->setPerspectiveProjection(45.0f, 16.0f/9.0f, 0.1f, 1000.0f);
camera->setPosition(QVector3D(0, 0, 40.0f));
camera->setViewCenter(QVector3D(0, 0, 0));
// For camera controls
Qt3DExtras::QOrbitCameraController *camController = new Qt3DExtras::QOrbitCameraController(scene);
camController->setLinearSpeed( 50.0f );
camController->setLookSpeed( 180.0f );
camController->setCamera(camera);
view.setRootEntity(scene);
view.show();
return app.exec();
}
With the help from #eyllanesc and qDebug, I found how to write it in three ways:
const QUrl url = QUrl( "qrc:/path/copied/from/qtcreator/su.obj");
const QUrl url = QUrl::fromLocalFile( "C:/path/to/folder/su.obj");
const QUrl url = QUrl::fromLocalFile( "su.obj");
qDebug() << QDir::currentPath(); // I used this to make sure I was at the right place
and
sphereMesh->setSource(url);
I'm trying to load a .obj source file from blender, and display it, LibertStatue.obj is the .obj file which is located in the same folder as the main.cpp file below. When I run this code, I get blank window which I think is supposed to show a statue of liberty, and a command prompt that says QObject::connect(opneglcontext, unknown) Invalid nullptr parameter.
I'm using Qt Creator on linux Xubuntu.
I downloaded the .obj file of statue of liberty from here : https://free3d.com/3d-model/statue-of-liberty-73656.html.
#include <QGuiApplication>
#include <Qt3DExtras/QPhongMaterial>
#include <Qt3DExtras/Qt3DWindow>
#include <Qt3DExtras/QOrbitCameraController>
#include <Qt3DRender>
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QUrl data = QUrl::fromLocalFile("LibertStatue.obj");
Qt3DExtras::Qt3DWindow view;
Qt3DCore::QEntity *rootEntity = new Qt3DCore::QEntity;
Qt3DCore::QEntity *flyingwedge = new Qt3DCore::QEntity(rootEntity);
Qt3DExtras::QPhongMaterial *material = new Qt3DExtras::QPhongMaterial();
material->setDiffuse(QColor(254, 254, 254));
Qt3DRender::QMesh *flyingwedgeMesh = new Qt3DRender::QMesh;
flyingwedgeMesh->setMeshName("FlyingWedge");
flyingwedgeMesh->setSource(data);
flyingwedge->addComponent(flyingwedgeMesh);
flyingwedge->addComponent(material);
Qt3DRender::QCamera *camera = view.camera();
camera->lens()->setPerspectiveProjection(40.0f, 16.0f/9.0f, 0.1f, 1000.0f);
camera->setPosition(QVector3D(0, 0, 40.0f));
camera->setViewCenter(QVector3D(0, 0, 0));
Qt3DCore::QEntity *lightEntity = new Qt3DCore::QEntity(rootEntity);
Qt3DRender::QPointLight *light = new Qt3DRender::QPointLight(lightEntity);
light->setColor("white");
light->setIntensity(0.8f);
lightEntity->addComponent(light);
Qt3DCore::QTransform *lightTransform = new Qt3DCore::QTransform(lightEntity);
lightTransform->setTranslation(QVector3D(60, 0, 40.0f));
lightEntity->addComponent(lightTransform);
Qt3DExtras::QOrbitCameraController *camController = new Qt3DExtras::QOrbitCameraController(rootEntity);
camController->setCamera(camera);
view.setRootEntity(rootEntity);
view.show();
return app.exec();
}
The problem is the setMeshName call. The documentation for the QMesh C++ class is misleading: setMeshName is not for setting a name to the QMesh object, but for selecting which geometry from the OBJ file you want to load. Its actual behavior is explained in the documentation of the corresponding QML type.
So you have two options: just remove the setMeshName call, or pass the actual name of the geometry, which you can find out by opening the OBJ file with a text editor.
Inspecting your code it looks to me that the Qt3Dwindow cannot be displayed standalone and the creation of a window container is needed to display the Qt3DWindow inside of a Widget (QFrame).
A typical Qt3D application I use for testing looks as follows:
#include <QApplication>
#include <QDebug>
#include <QFrame>
#include <QVBoxLayout>
#include <QWidget>
#include <QTimer>
#include <Qt3DRender/QCamera>
#include <Qt3DRender/QCameraLens>
#include <Qt3DExtras/Qt3DWindow>
#include <Qt3DExtras/QOrbitCameraController>
#include <Qt3DExtras/QDiffuseSpecularMaterial>
#include <Qt3DExtras/QSphereMesh>
int main(int argc, char* argv[])
{
QApplication a(argc, argv);
auto view = new Qt3DExtras::Qt3DWindow();
auto rootEntity = new Qt3DCore::QEntity();
view->setRootEntity(rootEntity);
auto camera = view->camera();
camera->lens()->setPerspectiveProjection(45.0f, 1., 0.1f, 10000.0f);
camera->setPosition(QVector3D(0, 0, 5));
camera->setUpVector(QVector3D(0, 1, 0));
camera->setViewCenter(QVector3D(0, 0, 0));
auto camController = new Qt3DExtras::QOrbitCameraController(rootEntity);
camController->setCamera(camera);
auto sphereMat = new Qt3DExtras::QDiffuseSpecularMaterial;
sphereMat->setDiffuse(QColor(Qt::blue));
auto mesh = new Qt3DExtras::QSphereMesh();
mesh->setRadius(1);
auto sphereEntity = new Qt3DCore::QEntity(rootEntity);
sphereEntity->addComponent(mesh);
sphereEntity->addComponent(sphereMat);
auto container = QWidget::createWindowContainer(view);
QFrame frame;
frame.setLayout(new QVBoxLayout);
frame.layout()->addWidget(container);
frame.resize(QSize(400, 300));
QTimer::singleShot(100, [&]() {
camera->viewAll();
});
frame.show();
return a.exec();
}
I'm using a Wheelevent to Zoom in/out in a QWidget view,in use the event to translate the QCamera, is there a solution from the qt api to move toward a point or zoom with the camera to a specific point? I searched in many sections but did'nt find something useful unfortunately.
Edit: Stefan Reinhardt suggested to use QAbstractCameraController to achieve what you want to do. The example I provided is a quick-and-dirty solution. I agree that using the camera controller is the way intended in Qt3D.
The Qt3D API doesn't support this directly, but you can implement it easily yourself.
Here is a minimally working example, that should get you started (note, that you have to adjust the upvector, when scrolling past the view center I guess):
main.cpp:
#include <QApplication>
#include "graphicswindow.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
GraphicsWindow graphicsWindow;
graphicsWindow.show();
return a.exec();
}
graphicswindow.h:
#include <Qt3DExtras/Qt3DWindow>
#include <Qt3DCore/QEntity>
class GraphicsWindow : public Qt3DExtras::Qt3DWindow {
public:
GraphicsWindow();
void wheelEvent ( QWheelEvent * event ) override;
private:
Qt3DCore::QEntity *createScene();
};
graphicswindow.cpp:
#include "graphicswindow.h"
#include <QMouseEvent>
#include <QVector3D>
#include <Qt3DRender/QCamera>
#include <Qt3DRender/QMaterial>
#include <Qt3DExtras/QGoochMaterial>
#include <Qt3DExtras/QCuboidMesh>
GraphicsWindow::GraphicsWindow() : Qt3DExtras::Qt3DWindow() {
// You could also create a dedicated setup method
Qt3DCore::QEntity *root = createScene();
setRootEntity(root);
Qt3DRender::QCamera *camera = this->camera();
camera->lens()->setPerspectiveProjection(45.0f, 16.0f/9.0f, 0.1f, 1000.0f);
camera->setPosition(QVector3D(20.0, 20.0, 20.0f));
camera->setViewCenter(QVector3D(0, 0, 0));
}
void GraphicsWindow::wheelEvent(QWheelEvent *event) {
QVector3D camPos = this->camera()->position();
camPos.normalize();
camPos = this->camera()->position() - QVector3D(event->delta() / 300.f,
event->delta() / 300.f,
event->delta() / 300.f);
this->camera()->setPosition(camPos);
}
Qt3DCore::QEntity* GraphicsWindow::createScene() {
Qt3DCore::QEntity *rootEntity = new Qt3DCore::QEntity;
Qt3DRender::QMaterial *material = new Qt3DExtras::QGoochMaterial(rootEntity);
//Cube
Qt3DCore::QEntity *cubeEntity = new Qt3DCore::QEntity(rootEntity);
Qt3DExtras::QCuboidMesh *cubeMesh = new Qt3DExtras::QCuboidMesh;
cubeEntity->addComponent(cubeMesh);
cubeEntity->addComponent(material);
return rootEntity;
}