qml 3d实现鼠标左键控制摄像机旋转

在qml 3d开发中,默认的是鼠标右键控制摄像机旋转,这不太符合操作习惯。

第一种方法:实现鼠标左键控制摄像机旋转就要重写鼠标事MouseArea,此方法较麻烦

        MouseArea{
            id:mouseId
            width: 300
            height: 200
            anchors.top:  parent.top
            anchors.topMargin: 0
            anchors.right: parent.right
            onPressed: {
                lastPoint = Qt.point(mouseX,mouseY)
            }

            onPositionChanged: {
                currentPoint = Qt.point(mouseX,mouseY)
                var dx = currentPoint.x - lastPoint.x   //x轴上的增量
                var theta = dx*Math.PI/(2*mouseId.width)    //对应的角度增量


                var kx = jaguar.cameraUpVector().x //jaguar就是我的3d模型实例
                var ky = jaguar.cameraUpVector().y
                var kz = jaguar.cameraUpVector().z
                var versTheta = 1-Math.cos(theta)

                //摄像机的旋转矩阵
                var m = Qt.matrix4x4(kx*kx*versTheta+Math.cos(theta),   kx*ky*versTheta-kz*Math.sin(theta)  ,kx*kz*versTheta+ky*Math.sin(theta),0,
                                     ky*kx*versTheta+kz*Math.sin(theta),ky*ky*versTheta+Math.cos(theta)     ,ky*kz*versTheta-kz*Math.sin(theta),0,
                                     kz*kx*versTheta-ky*Math.sin(theta),kz*ky*versTheta+kx*Math.sin(theta)  ,kz*kz*versTheta+Math.cos(theta)   ,0,
                                     0                                 ,0                                   ,0                                 ,1)

                var v = jaguar.cameraPosition()//获取摄像机的位置,代码在下面贴出
                var v4d = Qt.vector4d(v.x,v.y,v.z,1)

                var vCamera = backend.matrix4x4MultipVector4D(m,v4d)//4x4的矩阵乘以Vector4D
                jaguar.setCmaeraPosition(vCamera)//设置摄像机位置


                /***************Y轴*******************/

                var dy = currentPoint.y - lastPoint.y   //y轴上的增量
                var thetaY = -dy*Math.PI/(2*mouseId.height)    //对应的角度增量
                var versThetaY = 1-Math.cos(thetaY)

                var vView = backend.vector3Subtract(jaguar.cameraViewCenter(),jaguar.cameraPosition())//两个vector3d相减
                var vUp = jaguar.cameraUpVector()

                var viewLen = Math.sqrt(vView.x*vView.x+vView.y*vView.y+vView.z*vView.z)//计算长度
                vView = Qt.vector3d(vView.x/viewLen,vView.y/viewLen,vView.z/viewLen)

                var vRight = backend.vector3CrossProduct(vView,vUp)//向量的叉积
                var vRightLen = Math.sqrt(vRight.x*vRight.x+vRight.y*vRight.y+vRight.z*vRight.z)
                var vRightUnit = Qt.vector3d(vRight.x/vRightLen,vRight.y/vRightLen,vRight.z/vRightLen)

                kx = vRightUnit.x
                ky = vRightUnit.y
                kz = vRightUnit.z

                var n = Qt.matrix4x4(kx*kx*versThetaY+Math.cos(thetaY),   kx*ky*versThetaY-kz*Math.sin(thetaY)  ,kx*kz*versThetaY+ky*Math.sin(thetaY),0,
                                     ky*kx*versThetaY+kz*Math.sin(thetaY),ky*ky*versThetaY+Math.cos(thetaY)     ,ky*kz*versThetaY-kz*Math.sin(thetaY),0,
                                     kz*kx*versThetaY-ky*Math.sin(thetaY),kz*ky*versThetaY+kx*Math.sin(thetaY)  ,kz*kz*versThetaY+Math.cos(thetaY)   ,0,
                                     0                                 ,0                                   ,0                                 ,1)
                v = jaguar.cameraPosition()
                v4d = Qt.vector4d(v.x,v.y,v.z,1)
                vCamera = backend.matrix4x4MultipVector4D(n,v4d)
                var vCameraLen = Math.sqrt(vCamera.x*vCamera.x+vCamera.y*vCamera.y+vCamera.z*vCamera.z)
                vCamera = Qt.vector3d(123*vCamera.x/vCameraLen,123*vCamera.y/vCameraLen,123*vCamera.z/vCameraLen)


                jaguar.setCmaeraPosition(vCamera)

                v4d = Qt.vector4d(vUp.x,vUp.y,vUp.z,0)
                vUp = backend.matrix4x4MultipVector4D(n,v4d)
                jaguar.setCameraUpVector(vUp)
                lastPoint = Qt.point(mouseX,mouseY)

                //console.log(vCamera,jaguar.cameraViewCenter(),vCameraLen)
            }

        }

获取摄像机的位置信息函数:

    /************相机设置************/
    function cameraUpVector(){
        return camera.upVector
    }
    function setCameraUpVector(v){
        camera.upVector = v
    }

    function cameraPosition(){
        return camera.position
    }
    function setCmaeraPosition(v)
    {
        camera.position = v
    }

    function cameraViewCenter(){
        return camera.viewCenter
    }
    function setCameraViewCenter(v){
        camera.viewCenter = v
    }

其他的一些数学算法(因为在qml里边无法直接对矩阵,向量相乘,所以在c++里边做):

//两个4x4的矩阵相乘
QMatrix4x4 BackendSigProxy::matrix4x4Multip(QMatrix4x4 m1,QMatrix4x4 m2)
{
    return m1*m2;
}

//三个4x4的矩阵相乘
QMatrix4x4 BackendSigProxy::matrix4x4Multip(QMatrix4x4 m1,QMatrix4x4 m2,QMatrix4x4 m3)
{
    return m1*m2*m3;
}

//取出4x4矩阵第一列,前三行.返回QVector3D 
QVector3D BackendSigProxy::matrix4x4Index2(QMatrix4x4 m)
{
    QVector4D vect4d = m.column(1);
    QVector3D vect3d(vect4d.x(),vect4d.y(),vect4d.z());
    return  vect3d;
}

//两个四元数相乘
QQuaternion BackendSigProxy::quaternionMultip(QQuaternion q1,QQuaternion q2)
{
    return q1*q2;
}

//4x4的矩阵乘以Vector4D
QVector3D BackendSigProxy::matrix4x4MultipVector4D(QMatrix4x4 m1,QVector4D v1)
{
    float x = 0.0;
    float y = 0.0;
    float z = 0.0;

    x = v1.x()*m1(0,0)+v1.y()*m1(0,1)+v1.z()*m1(0,2)+v1.w()*m1(0,3);
    y = v1.x()*m1(1,0)+v1.y()*m1(1,1)+v1.z()*m1(1,2)+v1.w()*m1(1,3);
    z = v1.x()*m1(2,0)+v1.y()*m1(2,1)+v1.z()*m1(2,2)+v1.w()*m1(2,3);
    return QVector3D(x,y,z);
}

//两个向量的叉乘
QVector3D BackendSigProxy::vector3CrossProduct(QVector3D v1,QVector3D v2)
{
    return QVector3D::crossProduct(v1,v2);
}

//两个向量相减
QVector3D BackendSigProxy::vector3Subtract(QVector3D v1,QVector3D v2)
{
    return v1-v2;
}

//3x3的矩阵乘以Vector3D
QVector3D BackendSigProxy::matrix3x3MultipVector3D(float m1[3][3] ,QVector3D v1)
{
    float x = 0.0;
    float y = 0.0;
    float z = 0.0;
    x = v1.x()*m1[0][0]+v1.y()*m1[0][1]+v1.z()*m1[0][2];
    y = v1.x()*m1[1][0]+v1.y()*m1[1][1]+v1.z()*m1[1][2];
    z = v1.x()*m1[2][0]+v1.y()*m1[2][1]+v1.z()*m1[2][2];
    return QVector3D(x,y,z);
}

第二种方法:

继承自Qt3DExtras::QAbstractCameraController,重新写相机类

trackballcameracontroller.h

#ifndef TRACKBALLCAMERACONTROLLER_H
#define TRACKBALLCAMERACONTROLLER_H

#include <Qt3DExtras/QAbstractCameraController>
#include <QPoint>
#include <QSize>
#include <Qt3DCore/QTransform>

class TrackballCameraController : public Qt3DExtras::QAbstractCameraController
{
    Q_OBJECT
public:
    Q_PROPERTY(QSize windowSize READ windowSize WRITE setWindowSize NOTIFY windowSizeChanged)
    Q_PROPERTY(float trackballSize READ trackballSize WRITE setTrackballSize NOTIFY trackballSizeChanged)
    Q_PROPERTY(float rotationSpeed READ rotationSpeed WRITE setRotationSpeed NOTIFY rotationSpeedChanged)

    TrackballCameraController(Qt3DCore::QNode *parent = nullptr);

    QSize windowSize() const
    {
        return m_windowSize;
    }

    float trackballSize() const
    {
        return m_trackballSize;
    }

    float rotationSpeed() const
    {
        return m_rotationSpeed;
    }

public slots:
    void setWindowSize(QSize windowSize)
    {
        if (m_windowSize == windowSize)
            return;

        m_windowSize = windowSize;
        emit windowSizeChanged(m_windowSize);
    }

    void setTrackballSize(float trackballSize)
    {
        if (qFuzzyCompare(m_trackballSize, trackballSize))
            return;

        m_trackballSize = trackballSize;
        emit trackballSizeChanged(m_trackballSize);
    }

    void setRotationSpeed(float rotationSpeed)
    {
        if (qFuzzyCompare(m_rotationSpeed, rotationSpeed))
            return;

        m_rotationSpeed = rotationSpeed;
        emit rotationSpeedChanged(m_rotationSpeed);
    }

signals:
    void windowSizeChanged(QSize windowSize);
    void trackballSizeChanged(float trackballSize);
    void rotationSpeedChanged(float rotationSpeed);

protected:
    void moveCamera(const Qt3DExtras::QAbstractCameraController::InputState &state, float dt) override;
    QVector3D projectToTrackball(const QPoint &screenCoords) const;
    void createRotation(const QPoint &firstPoint,
                               const QPoint &nextPoint, QVector3D &dir, float &angle);

private:
    QPoint m_mouseLastPosition, m_mouseCurrentPosition;
    QSize m_windowSize;
    float m_trackballRadius = 1.0f;
    float m_panSpeed = 2.0f;
    float m_zoomSpeed = 2.0f;
    float m_rotationSpeed = 2.0f;
    float m_zoomCameraLimit = 1.0f;
    float m_trackballSize = 1.0f;
};

#endif // TRACKBALLCAMERACONTROLLER_H

trackballcameracontroller.cpp

#include "trackballcameracontroller.h"
#include <Qt3DRender/QCamera>
#include <QVector2D>
#include <Qt3DInput/QAction>
#include <Qt3DInput/QActionInput>
#include <Qt3DInput/QAxis>
#include <Qt3DInput/QAnalogAxisInput>
#include <Qt3DInput/QMouseDevice>
#include <Qt3DInput/QKeyboardDevice>
#include <Qt3DInput/QMouseHandler>
#include <Qt3DInput/QKeyboardHandler>
#include <Qt3DCore/QTransform>
#include <QtMath>

TrackballCameraController::TrackballCameraController(Qt3DCore::QNode *parent)
    : Qt3DExtras::QAbstractCameraController (parent)
{
    Qt3DInput::QMouseHandler *mouseHandler = new Qt3DInput::QMouseHandler(this);
    mouseHandler->setSourceDevice(mouseDevice());

    QObject::connect(mouseHandler, &Qt3DInput::QMouseHandler::pressed,
                     [this](Qt3DInput::QMouseEvent *pressedEvent) {
        pressedEvent->setAccepted(true);
        m_mouseLastPosition = QPoint(pressedEvent->x(), pressedEvent->y());
        m_mouseCurrentPosition = m_mouseLastPosition;
    });

    QObject::connect(mouseHandler, &Qt3DInput::QMouseHandler::positionChanged,
                     [this](Qt3DInput::QMouseEvent *positionChangedEvent) {
        positionChangedEvent->setAccepted(true);
        m_mouseCurrentPosition = QPoint(positionChangedEvent->x(),
                                              positionChangedEvent->y());
    });
    //keyboardDevice()->set
}

QVector3D TrackballCameraController::projectToTrackball(const QPoint &screenCoords) const
{
    float sx = screenCoords.x(), sy = m_windowSize.height() - screenCoords.y();

    QVector2D p2d(sx / m_windowSize.width() - 0.5f, sy / m_windowSize.height() - 0.5f);
    //qDebug()<<p2d;

    float z = 0.0f;
    float r2 = m_trackballSize * m_trackballSize;
    if (p2d.lengthSquared() <= r2 * 0.5f){
        z = sqrt(r2 - p2d.lengthSquared());
    }else{
        z = r2 * 0.5f / p2d.length();
    }
    QVector3D p3d(p2d, z);
    //qDebug()<<p3d;
    return p3d;
}

float clamp(float x)
{
    return x > 1? 1 : (x < -1? -1 : x);
}

void TrackballCameraController::createRotation(const QPoint &firstPoint, const QPoint &nextPoint,
                                               QVector3D &dir, float &angle)
{
    auto lastPos3D = projectToTrackball(firstPoint).normalized();
    auto currentPos3D = projectToTrackball(nextPoint).normalized();

    // Compute axis of rotation:
    dir = QVector3D::crossProduct(currentPos3D, lastPos3D);

    // Approximate rotation angle:
    //qDebug()<<"dot:"<<dir;
    angle = acos(clamp(QVector3D::dotProduct(currentPos3D, lastPos3D)));

    //qDebug()<<"dir:"<<dir<<"angle:"<<angle;
}

void TrackballCameraController::moveCamera(const Qt3DExtras::QAbstractCameraController::InputState &state, float dt)
{
    auto theCamera = camera();

    if(theCamera == nullptr)
        return;

    auto ls = linearSpeed();


    if(state.leftMouseButtonActive){
        QVector3D dir;
        float angle;
        createRotation(m_mouseLastPosition, m_mouseCurrentPosition, dir, angle);

        auto currentRotation = theCamera->transform()->rotation();

        auto rotatedAxis = currentRotation.rotatedVector(dir);
        angle *= m_rotationSpeed;

        theCamera->rotateAboutViewCenter(QQuaternion::fromAxisAndAngle(rotatedAxis, angle * M_1_PI * 180));

    }else if(state.middleMouseButtonActive){
        auto offset = m_mouseCurrentPosition - m_mouseLastPosition;
        //qDebug()<<"offset:"<<offset;
        theCamera->translate(QVector3D(-offset.x() / float(m_windowSize.width()) * ls,
                                      offset.y() / float(m_windowSize.height()) * ls,
                                      0));


    }else if(dt != 0.0){
        //qDebug()<<"dt:"<<dt;
        theCamera->translate(QVector3D(state.txAxisValue * ls,
                                      state.tyAxisValue * ls,
                                      state.tzAxisValue * ls) * dt,
                             Qt3DRender::QCamera::DontTranslateViewCenter);
    }
    m_mouseLastPosition = m_mouseCurrentPosition;
}

在main.cpp注册下qmlRegisterType<TrackballCameraController>("TrackballCameraController", 1, 0, "TrackballCameraController");

qml引入import TrackballCameraController 1.0

然后相机这么写:

Camera {
        id: camera
        projectionType: CameraLens.PerspectiveProjection
        fieldOfView: 20
        aspectRatio: 16/9
        nearPlane : 0.01
        farPlane : 1000.0
        position: Qt.vector3d( 24.3423, 61.4187, -64.3055 )
        upVector: Qt.vector3d( 0.0, 1.0, 0.0 )
        viewCenter: planeTransform.translation//Qt.vector3d( 0.0, 0.0, 0.0 )
    }
    TrackballCameraController{
        camera: camera
        windowSize: Qt.size(640, 480)//就是3d模型所在窗口的大小
        rotationSpeed: 2.0
    }

 

  • 6
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值