Reputation: 59
I am developing an application in Qt Creator with QtQuick. The purpose of this application is to rotate a cube in 3 dimensions with the help of a finger.
I managed to develop the creation of actions in relation to the finger movements but I am completely stuck on the rotation of the cube in relation to the fingers.
I have tried to create a direction vector with respect to the current and previous points as well as the normal vector. I have applied the Euler rotation in x and y on the cube but at a certain point, the cube does not rotate as expected.
Here is my qml code for the main page of the application :
main.qml
import QtQuick 2.15
import QtQuick.Window 2.14
import QtQuick3D 1.15
import Qt3D.Input 2.0
import QtQuick.Controls 2.1
import Qt3D.Extras 2.15
Window {
visible: true
width: 640
height: 480
title: qsTr("MouseArea Demo")
MouseArea{
property int previousX: -1
property int previousY: -1
anchors.fill : parent
onPressed: {
previousX = mouseX;
previousY = mouseY;
}
onPositionChanged: {
let direction = Qt.vector2d(mouseX - previousX, mouseY - previousY).normalized();
let normal = Qt.vector2d(direction.y, direction.x).normalized();
console.log("direction : " + direction);
console.log("normal : " + normal);
cube.eulerRotation.x += normal.x * 3;
cube.eulerRotation.y += normal.y * 3;
previousX = mouseX;
previousY = mouseY
}
onReleased: {
previousX = -1;
previousY = -1;
}
}
View3D {
id: view
anchors.fill: parent
camera: camera
renderMode: View3D.Overlay
PerspectiveCamera {
id: camera
position: Qt.vector3d(0, 200, 300)
eulerRotation.x: -30
}
DirectionalLight {
eulerRotation.x: -30
}
Model {
id: cube
visible: true
position: Qt.vector3d(0, 0, 0)
source: "#Cube"
materials: [ DefaultMaterial {
diffuseMap: Texture {
id: texture
source: "../build-colorpicker2d-Desktop_Qt_5_15_2_GCC_64bit-Debug/res.png"
}
}
]
}
}
}
here is a video about what I talk to you 2 days ago about the y rotation problem.
Kind regards.
Upvotes: 3
Views: 2527
Reputation: 25906
The Node
has a rotate()
method. The trick is to do the rotation in Node.SceneSpace
. This will ensure that the rotations compound correctly in any axis you choose in that the rotations will be applied on top of the latest orientation. I use a MouseArea
to capture 2d movements in either X or Y. In 3d we interpret this as rotations in Y-axis or X-axis respectively. Here's a working example:
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 {
id: model
source: "#Cube"
materials: [
DefaultMaterial { diffuseColor: Qt.rgba(0.053, 0.130, 0.219, 0.75) }
]
}
}
OrthographicCamera {
id: cameraOrthographicFront
y: 500; z: 1000
lookAtNode: node
}
}
View3D {
anchors.fill: parent
importScene: standAloneScene
camera: cameraOrthographicFront
}
MouseArea {
anchors.fill:parent
property real pressedX
property real pressedY
onMouseXChanged: Qt.callLater(update)
onMouseYChanged: Qt.callLater(update)
onPressed: {
[pressedX,pressedY] = [mouseX,mouseY];
}
function update() {
let [dx,dy] = [mouseX - pressedX,mouseY - pressedY];
[pressedX,pressedY] = [mouseX,mouseY];
node.rotate(dx, Qt.vector3d(0, 1, 0), Node.SceneSpace);
node.rotate(dy, Qt.vector3d(1, 0, 0), Node.SceneSpace);
}
}
}
You can Try it Online!
Upvotes: 1
Reputation: 3883
First of all, you should add this in your .pro
file :
QT += qml quick 3dcore 3dinput 3dquick 3dlogic 3dquickextras 3dextras
I have 3 class in qml :main.qml , RootEntity.qml and SOrbitCameraController.qml
I have one Scene3D and inside this, I can put all my Entities. I Create a separate class and call it RootEntity. The more important point of this way is orbitController that I use in my class. this makes that you can rotate cube by mouse and for doing this you need Entity. because of this, I use Scene3D and Entity instead of view3d and models.
In main.qml:
import QtQuick 2.12
import QtQuick.Scene3D 2.12
import QtQuick.Window 2.12
import "."
Window {
visible: true
width: 640
height: 480
Scene3D
{
id : scene3d
anchors.fill: parent
focus: true
aspects: ["render", "logic", "input"]
hoverEnabled: true
cameraAspectRatioMode: Scene3D.AutomaticAspectRatio
antialiasing: true
RootEntity
{
id:root
}
}
}
in RootEntity.qml:
import QtQuick 2.0
import Qt3D.Core 2.12
import Qt3D.Render 2.12
import Qt3D.Extras 2.12
import Qt3D.Input 2.12
import "."
Entity {
id: root
//create camera
Camera {
id: mainCamera
projectionType: CameraLens.PerspectiveProjection
fieldOfView: 45
aspectRatio: 16/9
nearPlane : 0.1
farPlane : 1000.0
position: Qt.vector3d(0.0, 4.49373, -3.78577)
upVector: Qt.vector3d( 0.0, 1.0, 0.0 )
viewCenter: Qt.vector3d(0.0, 0.5, 0.0)
}
//use my class instead of OrbitCameraController
SOrbitCameraController {
id: mainCameraController
camera: mainCamera
}
components: [
RenderSettings {
Viewport {
normalizedRect: Qt.rect(0.0, 0.0, 1.0, 1.0)
RenderSurfaceSelector {
CameraSelector {
id: cameraSelector
camera: mainCamera
FrustumCulling {
ClearBuffers {
buffers: ClearBuffers.AllBuffers
clearColor: "#444449"
NoDraw {}
}
LayerFilter {
filterMode: LayerFilter.DiscardAnyMatchingLayers
layers: [topLayer]
}
LayerFilter {
filterMode: LayerFilter.AcceptAnyMatchingLayers
layers: [topLayer]
ClearBuffers {
buffers: ClearBuffers.DepthBuffer
}
}
}
}
}
}
},
InputSettings {}
,
ScreenRayCaster
{
id:screenRayCaster
onHitsChanged:
{
drawLineMesh(hits)
}
}
]
Layer {
id: topLayer
recursive: true
}
Entity {
id: cubeEntity
components: [
CuboidMesh
{
xExtent: 1
yExtent: 1
zExtent: 1
}
,
Transform {
id: t
translation: Qt.vector3d(0, 0, 0)
}
,
PhongMaterial
{
ambient: "red"
}
]
}
}
in SOrbitCameraController.qml :
import Qt3D.Core 2.0
import Qt3D.Render 2.0
import Qt3D.Input 2.0
Entity{
id: root
property Camera camera;
property real dt: 0.001
property real linearSpeed: 1
property real lookSpeed: 500
property real zoomLimit: 0.16
MouseDevice {
id: mouseDevice
sensitivity: 0.001 // Make it more smooth
}
MouseHandler {
id: mh
readonly property vector3d upVect: Qt.vector3d(0, 1, 0)
property point lastPos;
property real pan;
property real tilt;
sourceDevice: mouseDevice
onPanChanged: root.camera.panAboutViewCenter(pan, upVect);
onTiltChanged: root.camera.tiltAboutViewCenter(tilt);
onPressed: {
lastPos = Qt.point(mouse.x, mouse.y);
}
onPositionChanged: {
// You can change the button as you like for rotation or translation
if (mouse.buttons === 1){ // Left button for rotation
pan = -(mouse.x - lastPos.x) * dt * lookSpeed;
tilt = (mouse.y - lastPos.y) * dt * lookSpeed;
} else if (mouse.buttons === 2) { // Right button for translate
var rx = -(mouse.x - lastPos.x) * dt * linearSpeed;
var ry = (mouse.y - lastPos.y) * dt * linearSpeed;
camera.translate(Qt.vector3d(rx, ry, 0))
} else if (mouse.buttons === 3) { // Left & Right button for zoom
ry = (mouse.y - lastPos.y) * dt * linearSpeed
zoom(ry)
}
lastPos = Qt.point(mouse.x, mouse.y)
}
onWheel: {
zoom(wheel.angleDelta.y * dt * linearSpeed)
}
function zoom(ry) {
if (ry > 0 && zoomDistance(camera.position, camera.viewCenter) < zoomLimit) {
return
}
camera.translate(Qt.vector3d(0, 0, ry), Camera.DontTranslateViewCenter)
}
function zoomDistance(posFirst, posSecond) {
return posSecond.minus(posFirst).length()
}
}
}
and at the end my main.cpp :
#include <QGuiApplication>
#include <QQmlApplicationEngine>
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
{
return -1;
}
return app.exec();
}
Upvotes: 2