I am trying to create a basic QML application that will load a 3D model from a .obj file and display it on the screen and be able to rotate it along its axes during runtime. I went through some of the Qt examples and came up with the below code - most of which was borrowed from the working examples. But when I run it the model is not rendered properly.
The actual model looks like:
and currently my app shows up like
.
Here is the QML code -
import Qt3D.Core 2.12
import Qt3D.Render 2.12
import Qt3D.Extras 2.12
Entity {
id: sceneRoot
Camera {
id: camera
projectionType: CameraLens.PerspectiveProjection
fieldOfView: 45
aspectRatio: 1820 / 1080
nearPlane: 0.1
farPlane: 1000.0
position: Qt.vector3d(0.014, 0.956, 2.178)
upVector: Qt.vector3d(0.0, 1.0, 0.0)
viewCenter: Qt.vector3d(0.0, 0.7, 0.0)
}
Entity {
components: [
DirectionalLight {
intensity: 0.9
worldDirection: Qt.vector3d(0, 0.6, -1)
}
]
}
RenderSettings {
id: external_forward_renderer
activeFrameGraph: ForwardRenderer {
camera: camera
clearColor: "transparent"
}
}
Mesh {
id: roboMesh
source: "images/robo-obj-pose4/source/d2f0cff60afc40f5afe79156ec7db657.obj"
}
Entity {
id: circleEntity
property Material roboMaterial: PhongAlphaMaterial {
alpha: 0.4
ambient: "black"
diffuse: "black"
specular: "black"
shininess: 10000
}
components: [roboMesh, roboMaterial]
}
}
What am I missing here? Sorry for a really silly question but I am totally new to Qt3D and am confused as to what else needs to go in my code.
You don't necessarily need a material file as suggested by the comments, you can assign generic materials the way you do it you just have some other issues with your code.
First, you need to add the RenderSettings as a component to the root entity, like so:
Entity {
id: sceneRoot
components: [external_forward_renderer]
...
Secondly, "transparent" is not a valid clear color. Use something like Qt.rgba(0, 0.5, 1, 1).
Thirdly, you need to add file:// to the beginning of the mesh URL, unless you have the file included in a resources file. At least I needed that prefix, maybe you don't. You can check this by having a look at the application output in QtCreator. If it says "file does not exist" then add this prefix.
If you still can't see your mesh try adding InputSettings (which have to be added as a component as well) and a OrbitCameraController:
InputSettings {
id: inputSettings
}
OrbitCameraController{
camera: camera
}
Related
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 5 days ago.
Improve this question
I developed software with python and Qml and I want to display a sequence of 2D images (100 slices) in a 3D space with Qml in this software. I think Qt3D or QtQuick 3D modules are proper for that purpose because the following code displays an image in 3D space but cannot figure out how to display a sequence of images.
import QtQuick 2.14
import QtQuick.Window 2.14
import QtQuick3D 1.14
Window {
id: window
width: 640
height: 640
visible: true
color: "black"
Rectangle {
id: qt_logo
width: 230
height: 230
anchors.top: parent.top
anchors.left: parent.left
anchors.margins: 10
color: "black"
property int angle: 0
layer.enabled: true
Image {
anchors.fill: parent
source: "qt_logo.png"
}
}
View3D {
id: view
anchors.fill: parent
camera: camera
renderMode: View3D.Overlay
PerspectiveCamera {
id: camera
position: Qt.vector3d(0, 200, -300)
rotation: Qt.vector3d(30, 0, 0)
}
DirectionalLight {
rotation: Qt.vector3d(30, 0, 0)
}
Model {
id: cube
visible: true
position: Qt.vector3d(0, 0, 0)
source: "#Rectangle"
materials: [ DefaultMaterial {
diffuseMap: Texture {
id: texture
sourceItem: qt_logo
flipV: true
}
}
]
rotation: Qt.vector3d(0, 0, 0)
}
}
}
Edit: I searched a little more and I found the texture3D in the Qt3d module could be a better option for my purpose. But I can't find any example of texture3D in qml. Now the following questions are my problems? 1. How I can generate a texture3D from a stack of 2D images? 2. How I can display a texture3D in qml. 3. Can I generate input data manually for Texture3D (Like 3D array)?
Thank you for your help.
If you're using Texture you can set sourceItem to place any 2D component, including, Rectangle and Image onto a 3D object, such as a "#Cube".
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import QtQuick3D
Page {
background: Rectangle { color: "#848895" }
Node {
id: standAloneScene
DirectionalLight { ambientColor: Qt.rgba(1.0, 1.0, 1.0, 1.0) }
Node {
id: node
Model {
source: "#Cube"
materials: [
DefaultMaterial {
diffuseMap: Texture {
sourceItem: MyItem { }
}
}
]
}
}
OrthographicCamera {
id: cameraOrthographicFront
lookAtNode: node
y: 800; z: 1000
}
}
View3D {
anchors.fill: parent
importScene: standAloneScene
camera: cameraOrthographicFront
}
NumberAnimation {
target: node
property: "eulerRotation.y"
loops: Animation.Infinite
running: true
from: 720; to: 0
duration: 10000
}
}
// MyItem.qml
import QtQuick
import QtQuick.Controls
Rectangle {
width: 256
height: 256
color: "#78a"
Repeater {
model: 5
Image {
x: index * 20 + 20
y: index * 20 + 20
width: 128
height: 128
source: "https://stephenquan.github.io/images/qt/madewithqt.png"
}
}
}
You can Try it Online!
Longtime programmer here, but QML nube.
I wish to start a Qt project from the example: "Qt 3D: Shadow Map QML Example", which is readily available from the examples listed in QtCreator. Here's a link to it as well: https://doc.qt.io/qt-5/qt3d-shadow-map-qml-example.html
I want to first customize it by adding 2d text, which would ideally remain in a fixed position on the screen, remaining in view as the camera position / angle changed. I would settle for just being able to add some simple text to the screen in any fashion!
Starting with that example, I added a file:
Title.qml
import Qt3D.Core 2.12
import Qt3D.Extras 2.13
Entity {
id: titleText
components: [ Transform { translation: Qt.vector3d(0.0, 10.0, 30.0) } ]
Text2DEntity {
font.family: "Sans Serif"
font.pointSize: 100
color: "white"
text: "MY TITLE"
width: text.length * font.pointSize*2
height: font.pointSize * 4
}
}
Then, at the bottom of main.qml, I attempt to incorporate that:
import QtQuick 2.1 as QQ2
import Qt3D.Core 2.0
import Qt3D.Render 2.0
import Qt3D.Input 2.0
import Qt3D.Extras 2.0
Entity {
id: sceneRoot
Camera {
id: camera
projectionType: CameraLens.PerspectiveProjection
fieldOfView: 45
aspectRatio: _window.width / _window.height
nearPlane: 0.1
farPlane: 1000.0
position: Qt.vector3d(0.0, 10.0, 20.0)
viewCenter: Qt.vector3d(0.0, 0.0, 0.0)
upVector: Qt.vector3d(0.0, 1.0, 0.0)
}
FirstPersonCameraController { camera: camera }
ShadowMapLight {
id: light
}
components: [
ShadowMapFrameGraph {
id: framegraph
viewCamera: camera
lightCamera: light.lightCamera
},
// Event Source will be set by the Qt3DQuickWindow
InputSettings { }
]
AdsEffect {
id: shadowMapEffect
shadowTexture: framegraph.shadowTexture
light: light
}
// Trefoil knot entity
Trefoil {
material: AdsMaterial {
effect: shadowMapEffect
specularColor: Qt.rgba(0.5, 0.5, 0.5, 1.0)
}
}
// Toyplane entity
Toyplane {
material: AdsMaterial {
effect: shadowMapEffect
diffuseColor: Qt.rgba(0.9, 0.5, 0.3, 1.0)
shininess: 75
}
}
// Plane entity
GroundPlane {
material: AdsMaterial {
effect: shadowMapEffect
diffuseColor: Qt.rgba(0.2, 0.5, 0.3, 1.0)
specularColor: Qt.rgba(0, 0, 0, 1.0)
}
}
// -------------------------------------
// Title entity
Title {}
// -------------------------------------
}
I know for certain the Title entity is being included. I've add a sound effect to that onload and console.logs() to prove such (removed from this post). I have tried manipulating the entity in various ways, but it never appears. I think other components must be hiding it / blocking it / rendering it incompatible from being displayed...
A text2D Entity is aimed at putting text into your 3D scene (like putting some text tags on specific objects). I think you just want to put text on your screen as an overlay.
This can be done by using the standard QML Text type.
I adapted the your code and marked new lines with //ADDED. Lines you don't longer need are marked with //REMOVED
pro file:
You will need the 3dextras module in your pro file
QT += 3dextras
main.cpp
Change your main.cpp by using a QQuickView instead of Qt3DQuickWindow. the reason for this is that the rendering mechanisms between scene3D and Qt3DQuickWindow are different.
Scene3D uses the renderer of QML to do its rendering, while Qt3DQuickWindow will create a dedicated render thread. To put it in another way : if your program only needs to show the 3D environment then stick with Qt3DQuickWindow.
If you want to put text and buttons on top of your 3D environment use QQuickView.
#include <Qt3DQuickExtras/qt3dquickwindow.h>
#include <Qt3DQuick/QQmlAspectEngine>
#include <QGuiApplication>
#include <QQmlContext>
#include <QQmlEngine>
#include <QQuickView>//ADDED
int main(int argc, char* argv[])
{
QGuiApplication app(argc, argv);
//ADDED:
QQuickView view;
view.rootContext()->setContextProperty("_window", &view);
view.setSource(QUrl("qrc:/main.qml"));
view.setWidth(1600);
view.setHeight(900);
view.show();
//REMOVED:
// Qt3DExtras::Quick::Qt3DQuickWindow view;
// view.resize(1600, 800);
// view.engine()->qmlEngine()->rootContext()->setContextProperty("_window", &view);
// view.setSource(QUrl("qrc:/main.qml"));
// view.show();
return app.exec();
}
main.qml
In the following example I use a Rectangle as the root and the Scene3D on the same level as a Text (in the top left corner) and a Button to show how you can combine standard QML types.
Rectangle {
anchors.fill: parent
Scene3D{
anchors.fill: parent
focus: true
aspects: ["input", "logic"]
Entity {
id: sceneRoot
Camera {
id: camera
projectionType: CameraLens.PerspectiveProjection
fieldOfView: 45
aspectRatio: _window.width / _window.height
nearPlane: 0.1
farPlane: 1000.0
position: Qt.vector3d(0.0, 10.0, 20.0)
viewCenter: Qt.vector3d(0.0, 0.0, 0.0)
upVector: Qt.vector3d(0.0, 1.0, 0.0)
}
FirstPersonCameraController { camera: camera }
ShadowMapLight {
id: light
}
components: [
ShadowMapFrameGraph {
id: framegraph
viewCamera: camera
lightCamera: light.lightCamera
},
// Event Source will be set by the Qt3DQuickWindow
InputSettings { }
]
AdsEffect {
id: shadowMapEffect
shadowTexture: framegraph.shadowTexture
light: light
}
// Trefoil knot entity
Trefoil {
material: AdsMaterial {
effect: shadowMapEffect
specularColor: Qt.rgba(0.5, 0.5, 0.5, 1.0)
}
}
// Toyplane entity
Toyplane {
material: AdsMaterial {
effect: shadowMapEffect
diffuseColor: Qt.rgba(0.9, 0.5, 0.3, 1.0)
shininess: 75
}
}
// Plane entity
GroundPlane {
material: AdsMaterial {
effect: shadowMapEffect
diffuseColor: Qt.rgba(0.2, 0.5, 0.3, 1.0)
specularColor: Qt.rgba(0, 0, 0, 1.0)
}
}
}
}
Text {
id: title
text: qsTr("TITLE")
font.family: "Arial"
font.pointSize: 30
color: "black"
anchors.top:parent.top
anchors.left:parent.left
anchors.leftMargin: 20
anchors.topMargin: 20
}
}
EDIT
Here is a print screen of the example running on Windows10 with Qt5.13.2 MSVC2015 64bit and works also on Qt5.14.0 MSVC2015 64bit
I’m testing the current status of Qt3D. I’m very fond of the MetalRoughMaterial, but I can’t seem to handle this simple use case in Qt3D: I would like to use textures that repeat to fill the face they are on.
Desired result:
What I get is this (see code below):
In case you’re wondering, I made 2 cubes next to one another to make the 2nd image, but I don’t consider this a real solution…)
I have been experimenting with WrapMode, Qtexture, textureScale but can’t find a solution in Qt3D yet.
I noticed that when I change textureScale, this repeats the texture on all faces. But on the small faces we get the same amount of images as in the longer ones. Is there a way to change this for x, y and z differently?
code:
import Qt3D.Core 2.12
import Qt3D.Render 2.12
import Qt3D.Input 2.12
import Qt3D.Extras 2.12
Entity {
id: sceneRoot
components: [
RenderSettings {
activeFrameGraph: ForwardRenderer{
camera: camera
}
},
InputSettings { },
DirectionalLight {
worldDirection: Qt.vector3d(-1, -1, -1);
color: "white"
intensity: 2.0
}
]
Camera {
id: camera
projectionType: CameraLens.PerspectiveProjection
fieldOfView: 45
aspectRatio: 800/600
nearPlane : 0.1
farPlane : 1000.0
position: Qt.vector3d( 2.0, 2.0, 2.0 )
upVector: Qt.vector3d( 0.0, 1.0, 0.0 )
viewCenter: Qt.vector3d( 0.0, 0.0, 0.0 )
}
OrbitCameraController { camera: camera }
CuboidMesh {
id: cuboidMesh
xExtent: 2
yExtent: 1
zExtent: 1
}
Transform {
id: cuboidTranslation
translation: Qt.vector3d(0, 0, 0)
}
Entity {
id: cuboidEntity
components: [ cuboidMesh, testMaterial, cuboidTranslation ]
}
MetalRoughMaterial {
id: testMaterial
baseColor: TextureLoader {
source: "qrc:/assets/textures/checkers.png"
}
metalness:0.0
roughness: 1.0
textureScale :1
}
}
I know how to do it in pure opengl(without Qt3D) by altering texturecoordinates of vertex data. If that is the direction to go I suspect I will have to make my own custom Mesh. Can somebody confirm this is the way to go? Or are there other solutions for this use case?
Thanks for your time reading this. Every help, suggestion is welcome, even the pure opengl solutions.
Yes, you can change texture coordinates in geometry, but I think it's easier to write custom shader, which gets proper texture coordinates from computations. For example, you can start with SimpleMaterial shader, modify it to display grid like you want. I had similar code for fragment shader:
#version 150 core
uniform vec3 boxSize;//all model
uniform vec2 wallSectionSize;//x y - size of cell inside this model
varying highp vec3 pos;
void main() {
vec3 pt = pos.xyz;
float x = pt.x;
float y = pt.y;
float z = pt.z;
bool side = false;
if(x==0 || x==boxSize.x) {
x = z;
side = true;
} else if(y==0 || y==boxSize.y) {
y = z;
}
//b == block number
const int nbx = int(x/wallSectionSize.x);
const int nby = int(y/wallSectionSize.y);//floor number from 0
//coordinates in this block umber
float bx = x - nbx * wallSectionSize.x;
float by = y - nby * wallSectionSize.y;
if(nbx % 2)
//fragColor = texture2D(...);
fragColor = vec4(1,1,0,0);
else
fragColor = vec4(1,0,1,0);
}
Also you can use textureScale property if your cube grows equally in all directions. In your example, where cube is not equally scaled, you can use textureTransform matrix. Also I created example which uses both approaches and a shader:
https://github.com/neurocod/StackOverflow/tree/master/texture-sapect-ratio
I am rendering a 3D model using Mesh by reading from a .obj file, and I am trying to change its size dynamically to take the parent window's dimensions. Is there a way to resize the object? Currently when I run the app the model takes roughly half the height and one-third of the width of the main-window, and I am not sure where it picks it up from.
I have tried to use viewportRect in ForwardRenderer but that did not change the display. I was also trying to figure out if zooming with the camera would be possible, but from what I saw in the docs the zoom scale factor needs hardcoded integer values and again I need it to be dynamic.
The current display is like this -
Here is my code -
main.qml
Rectangle {
id: rootWindow
color: "black"
Visualizer {}
}
Visualizer.qml
import Qt3D.Core 2.12
import Qt3D.Render 2.12
import Qt3D.Extras 2.12
import Qt3D.Input 2.12
import QtQuick.Scene3D 2.12
import QtQuick 2.12 as QQ2
Scene3D {
id: scene3d
anchors.fill: parent
focus: true
aspects: ["input", "logic"]
cameraAspectRatioMode: Scene3D.AutomaticAspectRatio
Entity {
id: sceneRoot
Camera {
id: camera
projectionType: CameraLens.PerspectiveProjection
fieldOfView: 45
nearPlane: 0.1
farPlane: 1000.0
position: Qt.vector3d(0.0, 0.0, 40.0)
upVector: Qt.vector3d(0.0, 1.0, 0.0)
viewCenter: Qt.vector3d(0.0, 0.0, 0.0)
}
FirstPersonCameraController {
camera: camera
}
components: [
RenderSettings {
activeFrameGraph: ForwardRenderer {
camera: camera
clearColor: "transparent"
Viewport {
id: viewport
normalizedRect: Qt.rect(0, 0, 1, 1)
}
}
},
InputSettings {
id: inputSettings
}
]
PhongMaterial {
id: material
}
Mesh {
id: sphereMesh
// source: "images/face3d/face_bse_mesh.obj"
source: "images/robo-obj-pose4/source/d2f0cff60afc40f5afe79156ec7db657.obj"
}
Transform {
id: modelTransform
property real userAngle: 0.0
matrix: {
var m = Qt.matrix4x4()
m.rotate(userAngle, Qt.vector3d(0, 1, 0))
// m.translate(Qt.vector3d(20, 0, 0))
return m
}
}
QQ2.NumberAnimation {
target: modelTransform
property: "userAngle"
duration: 10000
from: 0
to: 360
loops: QQ2.Animation.Infinite
running: true
}
Entity {
id: sphereEntity
components: [sphereMesh, material, modelTransform]
}
OrbitCameraController{
id: orbitCamera
camera: camera
}
}
}
So after a lot of asking around I have found the solution to this. It's a fairly simple enough trick.
You just need to add the following code in the Mesh, and that takes care of resizing the model in it's containing window.
Mesh {
----
onStatusChanged: {
if(status == Mesh.Ready)
camera.viewAll()
}
}
Sometimes while rendering the model its edges tend to go beyond the boundaries of the parent window. Adding some anchors.margins in the root Scene3D usually takes care of that.
I have a project, where i have one 3d object (.obj file) and i want to click on this object. For the first test i would be happy to change the texture or colour of the object. As far as i know it's called picking. Do you guys know how to manage this in qt3d? My whole project is written in qml, so it would be great if i could do the picking with qml (without c++), but if it's necessary im ready to try it that way, too.
My project is structured as followed:
I have an Entity as rootEntity and 3D-Entity, where my mesh is loaded. This structure is in an own qml file called View3d.qml. Now I set a Scene3D in my main.qml and load setup an instance of View3d.
I am using the Qt 5.5 beta with included qt3d on a windows 8.1 64Bit system, if its necessary.
The easiest way is that you add Texture to your .obj file with blender and then add it to your project .for doing this by using blender there is a lot of tutorials, see this How to Add Texture and this video.
another way is to use Texture and Texture2D
look at this code as an Example :
I have 2 qml class
in main.qml :
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Scene3D 2.12
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
Scene3D
{
id : scene3d
anchors.fill: parent
focus: true
aspects: ["render", "logic", "input"]
hoverEnabled: true
cameraAspectRatioMode: Scene3D.AutomaticAspectRatio
antialiasing: true
RootEntity
{
id:root
}
}
}
and in RootEntity.qml :
import QtQuick 2.12
import Qt3D.Core 2.12
import Qt3D.Render 2.12
import Qt3D.Input 2.12
import Qt3D.Extras 2.12
Entity {
id: sceneRoot
readonly property var textureModel: [texture1, texture2, texture3, texture4]
readonly property Texture texture1: TextureLoader {
source: "qrc:/images/image.png"
}
readonly property Texture texture2: TextureLoader {
source: "qrc:/images/wood.jpg"
}
readonly property Texture texture3: Texture2D {
format: Texture.RGBA8_UNorm
textureImages: TextureImage {
source:"qrc:/images/image.png"
}
}
readonly property Texture texture4: Texture2D {
format: Texture.RGBA8_UNorm
textureImages: TextureImage {
source:"qrc:/images/wood.jpg"
}
}
Camera {
id: camera
projectionType: CameraLens.PerspectiveProjection
fieldOfView: 45
aspectRatio: 16/9
nearPlane : 0.1
farPlane : 1000.0
position: Qt.vector3d( 0.0, 20.0, -40.0 )
upVector: Qt.vector3d( 0.0, 1.0, 0.0 )
viewCenter: Qt.vector3d( 0.0, 0.0, 0.0 )
}
OrbitCameraController {
camera: camera
}
components: [
RenderSettings {
activeFrameGraph: ForwardRenderer {
clearColor: "#333339"
camera: camera
}
},
// Event Source will be set by the Qt3DQuickWindow
InputSettings { }
]
CuboidMesh { id: mesh }
NodeInstantiator {
id: instantiator
model: sceneRoot.textureModel
Entity {
readonly property Transform transform: Transform {
readonly property real angle: model.index / instantiator.count * Math.PI * 2
translation: Qt.vector3d(Math.cos(angle) * 10, 0, Math.sin(angle) * 10)
scale: 10
}
readonly property DiffuseMapMaterial material: DiffuseMapMaterial {
diffuse: model.modelData
ambient: "white"
}
components: [ mesh, material, transform ]
}
}
}
the output is :
See the demos/qt3d/teaservice. This shows how to do picking (ie selection of an object using the mouse). Note you need the qt3d demo, not the QML teaservice.