Etienne PERIANA
Etienne PERIANA

Reputation: 59

How to rotate a 3d cube in qml?

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.

enter image description here

Upvotes: 3

Views: 2527

Answers (2)

Stephen Quan
Stephen Quan

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

Parisa.H.R
Parisa.H.R

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();
}

the out put enter image description here

Upvotes: 2

Related Questions