如何与qtquick3d之间进行鼠标坐标和世界坐标之间的动态转换?

发布于 2025-02-12 12:03:20 字数 3050 浏览 0 评论 0 原文

描述/代码

i具有QT快速3D视图和相应的场景,该场景被设计为QT 6.3.0上


import QtQuick
import QtQml
import QtQuick3D
import QtQuick3D.Helpers

Window {
    width: 800
    height: 600
    visible: true
    property var selectedItem
    property bool mousePressed: false
    function multiply_vectors(vec1, vec2) {
        return Qt.vector3d(vec1.x * vec2.x, vec1.y * vec2.y, vec1.z * vec2.z);
        
        
    }
    View3D {
        
        renderMode: View3D.Inline
        camera: camera
        anchors.fill: parent
        width: 800
        height: 600
        x: 0
        y: 0
        id: view
        environment: SceneEnvironment {
            clearColor: "black"
            backgroundMode: SceneEnvironment.Color
            depthTestEnabled: false
            depthPrePassEnabled: true
            
        }
        
        
        Model {
            id: rootEntity
            pickable: true
            source: "#Cube"
            materials: PrincipledMaterial {
                baseColor: "red"
                roughness: 0.1
            }
            position: Qt.vector3d(25.0, 15.0, -60.0)
            scale: Qt.vector3d(1.0, 1.0, 1.0)
        }
        
        PerspectiveCamera {
            id: camera
            position.z: 330.0
            position.y: 0.75
            eulerRotation.x: -12
            
            clipNear: 0.0
            clipFar: 1600.0
        }
        
        
        
        MouseArea {
            acceptedButtons: Qt.LeftButton | Qt.RightButton
            anchors.fill: parent
            id: mouseArea
            
            onPressed: function (mouse) {
                
                var result = view.pick(mouse.x, mouse.y);
                
                if (result.objectHit) {
                    selectedItem = result.objectHit;
                    mousePressed = true;
                } else {
                    mousePressed = false;
                }
            }
            
            onMouseXChanged: function(mouse) {
                if (mousePressed) {
                    var viewCoords = view.mapFromGlobal(mouseArea.mapToGlobal(mouse.x, mouse.y));
                    var sceneCoords = Qt.vector3d(viewCoords.x, viewCoords.y, 0);
                    var worldCoords = view.mapTo3DScene(sceneCoords);
                    worldCoords.z = selectedItem.z
                    selectedItem.position = multiply_vectors(worldCoords, Qt.vector3d(Math.abs(camera.z - selectedItem.z), Math.abs(camera.z - selectedItem.z), 1.0))
                }
            }
            
            onReleased: function (mouse) {
                mousePressed = false
            }
            
        }
        
        Component.onCompleted: {
            camera.lookAt(rootEntity)
        }
    }
}

的概述

,用例是,每当鼠标指向Cube时,每当鼠标移动时,都会导致Cube将其与3D场景中的相应点一起移动。

从相同的Z轴上看,这很棒。但是,当从X轴沿X轴说的点上查看对象时,该模型将沿X轴移动,而不是遵循鼠标位置。

问题

我如何在 onMouseXchanged中修改业务逻辑:function(Mouse){正确转换矩阵(或等效变换)以始终匹配鼠标位置相对于相机的位置,模型?

Description/ Code

I have a Qt Quick 3D View and corresponding scene that was designed to be compiled on Qt 6.3.0


import QtQuick
import QtQml
import QtQuick3D
import QtQuick3D.Helpers

Window {
    width: 800
    height: 600
    visible: true
    property var selectedItem
    property bool mousePressed: false
    function multiply_vectors(vec1, vec2) {
        return Qt.vector3d(vec1.x * vec2.x, vec1.y * vec2.y, vec1.z * vec2.z);
        
        
    }
    View3D {
        
        renderMode: View3D.Inline
        camera: camera
        anchors.fill: parent
        width: 800
        height: 600
        x: 0
        y: 0
        id: view
        environment: SceneEnvironment {
            clearColor: "black"
            backgroundMode: SceneEnvironment.Color
            depthTestEnabled: false
            depthPrePassEnabled: true
            
        }
        
        
        Model {
            id: rootEntity
            pickable: true
            source: "#Cube"
            materials: PrincipledMaterial {
                baseColor: "red"
                roughness: 0.1
            }
            position: Qt.vector3d(25.0, 15.0, -60.0)
            scale: Qt.vector3d(1.0, 1.0, 1.0)
        }
        
        PerspectiveCamera {
            id: camera
            position.z: 330.0
            position.y: 0.75
            eulerRotation.x: -12
            
            clipNear: 0.0
            clipFar: 1600.0
        }
        
        
        
        MouseArea {
            acceptedButtons: Qt.LeftButton | Qt.RightButton
            anchors.fill: parent
            id: mouseArea
            
            onPressed: function (mouse) {
                
                var result = view.pick(mouse.x, mouse.y);
                
                if (result.objectHit) {
                    selectedItem = result.objectHit;
                    mousePressed = true;
                } else {
                    mousePressed = false;
                }
            }
            
            onMouseXChanged: function(mouse) {
                if (mousePressed) {
                    var viewCoords = view.mapFromGlobal(mouseArea.mapToGlobal(mouse.x, mouse.y));
                    var sceneCoords = Qt.vector3d(viewCoords.x, viewCoords.y, 0);
                    var worldCoords = view.mapTo3DScene(sceneCoords);
                    worldCoords.z = selectedItem.z
                    selectedItem.position = multiply_vectors(worldCoords, Qt.vector3d(Math.abs(camera.z - selectedItem.z), Math.abs(camera.z - selectedItem.z), 1.0))
                }
            }
            
            onReleased: function (mouse) {
                mousePressed = false
            }
            
        }
        
        Component.onCompleted: {
            camera.lookAt(rootEntity)
        }
    }
}

Overview

The use case is that whenever the mouse is pressed while pointing at the cube, whenever the mouse moves it will cause the cube to move along with it to the corresponding point in the 3d Scene.

This works great when looking from a point that is on the same z-axis. However when looking at the object from a point say along the x-axis, the model will move along the x-axis instead of following the mouse position.

Question

How can I modify the business logic in onMouseXChanged: function(mouse) { to correctly transform the matrix (or equivalent transform) to consistently match the mouse position irregardless of the camera's position relative to the Model?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

等你爱我 2025-02-19 12:03:20

如果我正确理解您,您需要使用平行于相机的鼠标移动对象,无论相机位置和模型缩放如何?我承认我没有解决方案,但仍然比原始代码更好。首先,请勿将 clipnear 设置为0,它会使退化并打破投影数学。

其次,我想设置对象位置的代码应该看起来像

selectedItem.position = view.mapTo3DScene(
                        Qt.vector3d(mouse.x, mouse.y,
                                view.mapFrom3DScene(selectedItem.position).z))

文档说 mapfrom3dscene /坐标为从冰沙的近夹平面到映射位置的距离。但是,当我向窗户的侧面移动时,物体会变大,而它应该变小。

这是我对我的几个更正的完整代码:

import QtQuick
import QtQml
import QtQuick3D
import QtQuick3D.Helpers

Window {
    width: 800
    height: 600
    visible: true
    property var selectedItem
    property bool mousePressed: false

    View3D {
        renderMode: View3D.Inline
        camera: camera
        anchors.fill: parent
        width: 800
        height: 600
        x: 0
        y: 0
        id: view
        environment: SceneEnvironment {
            clearColor: "black"
            backgroundMode: SceneEnvironment.Color
            depthTestEnabled: false
            depthPrePassEnabled: true

        }


        Model {
            id: rootEntity
            pickable: true
            source: "#Cube"
            materials: PrincipledMaterial {
                baseColor: "red"
                roughness: 0.1
            }
            position: Qt.vector3d(25.0, 15.0, -60.0)
            scale: Qt.vector3d(2.0, 1.0, 0.5)
        }

        PerspectiveCamera {
            id: camera
            position.z: 330.0
            position.y: 100
            position.x: 700
            eulerRotation.x: -12

            // Note 1: clipNear shouldn't be 0, otherwise
            // it would break the math inside the projection matrix
            clipNear: 1.0
            clipFar: 1600.0
        }



        MouseArea {
            acceptedButtons: Qt.LeftButton | Qt.RightButton
            anchors.fill: parent
            id: mouseArea

            onPressed: function (mouse) {

                var result = view.pick(mouse.x, mouse.y);

                if (result.objectHit) {
                    selectedItem = result.objectHit;
                    mousePressed = true;
                } else {
                    mousePressed = false;
                }
            }

            onPositionChanged: function(mouse) {
                if (mousePressed) {
                    // Note 2: recalculate the position, since MouseArea has
                    // the same geometry as View3D we can use coords directly
                    selectedItem.position = view.mapTo3DScene(
                        Qt.vector3d(mouse.x, mouse.y,
                                view.mapFrom3DScene(selectedItem.position).z))
                }
            }

            onReleased: function (mouse) {
                mousePressed = false
            }

        }

        Component.onCompleted: {
            camera.lookAt(rootEntity)
        }
    }
}

If I understood you correctly, you need to move the object with the mouse parallel to the camera regardless of the camera position and model scaling? I admit that I don't have a solution, but still it's better than the original code. First of all, do not set the clipNear to 0, it would make the frustum degenerate and break the projection math.

Secondly, I would suppose that the code which sets the object position should look like

selectedItem.position = view.mapTo3DScene(
                        Qt.vector3d(mouse.x, mouse.y,
                                view.mapFrom3DScene(selectedItem.position).z))

The docs say that mapFrom3DScene/mapTo3DScene should interpret the z coordinate as the distance from the near clip plane of the frustum to the mapped position. However when I move it towards the sides of the window the object gets larger, whereas it should get smaller.

Here's the complete code with a few corrections of mine:

import QtQuick
import QtQml
import QtQuick3D
import QtQuick3D.Helpers

Window {
    width: 800
    height: 600
    visible: true
    property var selectedItem
    property bool mousePressed: false

    View3D {
        renderMode: View3D.Inline
        camera: camera
        anchors.fill: parent
        width: 800
        height: 600
        x: 0
        y: 0
        id: view
        environment: SceneEnvironment {
            clearColor: "black"
            backgroundMode: SceneEnvironment.Color
            depthTestEnabled: false
            depthPrePassEnabled: true

        }


        Model {
            id: rootEntity
            pickable: true
            source: "#Cube"
            materials: PrincipledMaterial {
                baseColor: "red"
                roughness: 0.1
            }
            position: Qt.vector3d(25.0, 15.0, -60.0)
            scale: Qt.vector3d(2.0, 1.0, 0.5)
        }

        PerspectiveCamera {
            id: camera
            position.z: 330.0
            position.y: 100
            position.x: 700
            eulerRotation.x: -12

            // Note 1: clipNear shouldn't be 0, otherwise
            // it would break the math inside the projection matrix
            clipNear: 1.0
            clipFar: 1600.0
        }



        MouseArea {
            acceptedButtons: Qt.LeftButton | Qt.RightButton
            anchors.fill: parent
            id: mouseArea

            onPressed: function (mouse) {

                var result = view.pick(mouse.x, mouse.y);

                if (result.objectHit) {
                    selectedItem = result.objectHit;
                    mousePressed = true;
                } else {
                    mousePressed = false;
                }
            }

            onPositionChanged: function(mouse) {
                if (mousePressed) {
                    // Note 2: recalculate the position, since MouseArea has
                    // the same geometry as View3D we can use coords directly
                    selectedItem.position = view.mapTo3DScene(
                        Qt.vector3d(mouse.x, mouse.y,
                                view.mapFrom3DScene(selectedItem.position).z))
                }
            }

            onReleased: function (mouse) {
                mousePressed = false
            }

        }

        Component.onCompleted: {
            camera.lookAt(rootEntity)
        }
    }
}
坚持沉默 2025-02-19 12:03:20

花了一段时间以不同的方法实验后,我发现将鼠标坐标映射到3D空间并未得到QT API的完全支持,即何时未固定鼠标在活动对象上。

因此,相反,我进行锻炼的方式是每次鼠标最初按下鼠标时施放新的射线播放并存储偏移量,然后根据射线播放的结果转换项目,并通过由标准级标量归一化矩阵翻译。

onMouseXChanged: function (mouse) {
     if (mousePressed) {
                    if (selectedItem != null) {
                        var result = view.pick(mouse.x, mouse.y)
                        if (result.objectHit) {
                            if (result.objectHit == selectedItem) {
                                var mouseGlobalPos = mouseArea.mapToGlobal(
                                            mouse.x, mouse.y)
                                
                                var mouseViewPos = view.mapFromGlobal(
                                            mouseGlobalPos)

                                var mouseScenePos = result.scenePosition
                                var resultPos = result.position

                                /* here we subtract the result of the new raycast by the starting offset and then normalize 
                                 * the result and multiply it by a scalar 3 to determine the amount of offset the Model 
                                 * under the mouse is from where the mouse was originally pressed, so we can translate it */

                                var differencePos = resultPos.minus(
                                            startMousePressSelectedItemLocalDragOffset).normalized(
                                            ).times(3)
                                selectedItem.position = selectedItem.position.plus(
                                            differencePos)

After spending a while experimenting with different approaches, I found that mapping the mouse coordinates to the 3d space wasn't fully supported by the Qt API in terms of when the mouse is not fixed over an active object.

So, instead, the way that I made a workout was by casting a new RayCast each time the mouse moves and storing the offset when the mouse is pressed originally and then translating the item based on the result of the raycast and lining up the offset by translating by the normalized matrix with a small scalar.

onMouseXChanged: function (mouse) {
     if (mousePressed) {
                    if (selectedItem != null) {
                        var result = view.pick(mouse.x, mouse.y)
                        if (result.objectHit) {
                            if (result.objectHit == selectedItem) {
                                var mouseGlobalPos = mouseArea.mapToGlobal(
                                            mouse.x, mouse.y)
                                
                                var mouseViewPos = view.mapFromGlobal(
                                            mouseGlobalPos)

                                var mouseScenePos = result.scenePosition
                                var resultPos = result.position

                                /* here we subtract the result of the new raycast by the starting offset and then normalize 
                                 * the result and multiply it by a scalar 3 to determine the amount of offset the Model 
                                 * under the mouse is from where the mouse was originally pressed, so we can translate it */

                                var differencePos = resultPos.minus(
                                            startMousePressSelectedItemLocalDragOffset).normalized(
                                            ).times(3)
                                selectedItem.position = selectedItem.position.plus(
                                            differencePos)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文