qtqmlqtquick3d

How can I shift the Rotation Axis of imported model? (QML, QtQuick3D, View3D)


Introduction

In my QtQuick (QML) project I need to use 3D model of dice (a cubic one) that should be rotated during animation process. I use View3D, because rotating dice will be inside of 3D-insertion which is inside my 2D project. I have downloaded a free-to-use dice model (.fbx) and successfully imported it in my project in form of ".mesh" file via Balsam Asset Import Tool. By choosing created mesh for a source of model and also applying the texture of dice via PrincipledMaterial, I perfectly see my dice model inside View3D.

The problem

When I tried to animate the rotation process of model, I noticed, that dice rotates incorrectly. I decided to look its axis via QtQuick3D.Helpers' AxisHelper and saw that starting points of axes X and Y located just fine but the Z axis' starting point is shifted from the center of the model by about 10 units.

What did I try

I tried to look up for useful properties of Model QML type (and Node because Model inherits Node type) that may allowed me to modify or specify the rotation axis of the object but I did not find anything. On the Stack Overflow there is one similar question but it does not helped me, because I should use View3D, not the whole Qt3D with Entity. Moreover, I tried to put translate property but it has only x and y fields and I did not seen any changes by modifying these.

What I want to know

I would like to know if there is a way to place rotation axis of 3D model inside the center of it. May be some Balsam Asset Import Tool's options can help me with that (I tried some options like dropNormals, removeComponentNormals, fbxPreservePivots but did not seen any changes)? Also I am not good at 3D Modelling, so could you please tell me if .mesh or .fbx files of 3D models store the information of axis rotation or something like that.

One group of axes is a 3D scene axes, and other group is dice model axes

The code I have (View3DArea.qml - custom type)

import QtQuick
import QtQuick3D
import QtQuick3D.Helpers

Rectangle
{
    id: root
    width: parent.width/1.5
    height: width
    anchors.centerIn: parent
    color: "lightgrey"

    View3D
    {
        anchors.fill: parent
        Node
        {
            PerspectiveCamera
            {
                id: _camera
                z: 100
            }
            DirectionalLight
            {

            }
            // Applying the texture
            PrincipledMaterial
            {
                id: _priDiceMaterial
                baseColorMap: Texture
                {
                    source: "assets/assets/Dice_Diffuse.jpg.png"
                }
            }
            // Model information
            Model
            {
               id: _diceModel
               source: "/assets/assets/dice_001.mesh"
               materials: [_priDiceMaterial]
               eulerRotation: Qt.vector3d(0, 0, 0)
               position: Qt.vector3d(20, 20, 20)
               
               AxisHelper {} // Visual axes of dice model
            }
            
            AxisHelper { id: _sceneAxis } // Visual axes of 3D scene
        }
    }

    WasdController
    {
        controlledObject: _camera
    }
}

Solution

  • [EDIT]

    I reworked the answer by putting the Model inside the Node. You can change the axis of your Model by setting x, y or z properties. Then you apply rotation on the Node:

                Node
                {
                    id: node
                    Model
                    {
                        id: _diceModel
                        source: "/assets/assets/dice_001.mesh"
                        materials: [_priDiceMaterial]
                        x: 100
                        y: 100
                        z: 100
                    }
                    eulerRotation: Qt.vector3d(0, 45, 0)
                }
    

    Because dice is involved. I experimented with mixing #Cube and #Cylinder to create a dice effect. The approach uses the above strategy by offsetting the #Cylinder in different places. A pain point with this approach is, on certain faces, the #Cylinder needs to be rotated.

    import QtQuick
    import QtQuick.Controls
    import QtQuick3D
    Page {
        background: Rectangle { color: "#848895" }
        property color blk: Qt.rgba(0.2, 0.2, 0.2, 0.75)
        property color wht: Qt.rgba(0.7, 0.7, 0.7, 0.75)
        property color red: Qt.rgba(0.7, 0.2, 0.2, 0.75)
        Node {
            id: standAloneScene
            DirectionalLight { ambientColor: Qt.rgba(1.0, 1.0, 1.0, 1.0) }
            Node {
                id: node
                Model {
                    source: "#Cube"
                    materials: [
                        DefaultMaterial { diffuseColor: blk }
                    ]
                }
                // 6
                Dot { x: -25; y: 50; z: -25 }
                Dot { x: 25; y: 50; z: -25 }
                Dot { x: -25; y: 50 }
                Dot { x: 25; y: 50 }
                Dot { x: -25; y: 50; z: 25 }
                Dot { x: 25; y: 50; z: 25 }
                // 1
                Dot { y: -50; c: red }
                // 4
                Dot { x: 50; y: -20; z: -25; r: "z"; c: red }
                Dot { x: 50; y: 20; z: -25; r: "z"; c: red }
                Dot { x: 50; y: -20; z: 25; r: "z"; c: red }
                Dot { x: 50; y: 20; z: 25; r: "z"; c: red }
                // 3
                Dot { x: -50; y: -20; z: -25; r: "z" }
                Dot { x: -50; r: "z" }
                Dot { x: -50; y: 20; z: 25; r: "z" }
                // 2
                Dot { z: 50; x: 20; r: "x" }
                Dot { z: 50; x: -20; r: "x"  }
                // 5
                Dot { z: -50; x: -25; y: -25; r: "x"  }
                Dot { z: -50; x: 25; y: -25; r: "x"  }
                Dot { z: -50; r: "x"  }
                Dot { z: -50; x: -25; y: 25; r: "x"  }
                Dot { z: -50; x: 25; y: 25; r: "x"  }
            }
            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
        }
        NumberAnimation {
            target: node
            property: "eulerRotation.x"
            loops: Animation.Infinite
            running: true
            from: 360; to: 0
            duration: 10000
        }
    }
    
    // Dot.qml
    import QtQuick
    import QtQuick3D
    Model {
        property string r: ""
        property color c: wht
        source: "#Cylinder"
        materials: [
            DefaultMaterial { diffuseColor: c }
        ]
        scale: Qt.vector3d(0.2, 0.05, 0.2)
        eulerRotation.x: r === 'x' ? 90 : 0
        eulerRotation.y: r === 'y' ? 90 : 0
        eulerRotation.z: r === 'z' ? 90 : 0
    }
    

    You can Try it Online!