在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
#include
#include
#include
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
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
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()< 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:"<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:"<translate(QVector3D(-offset.x() / float(m_windowSize.width()) * ls,
offset.y() / float(m_windowSize.height()) * ls,
0));
}else if(dt != 0.0){
//qDebug()<<"dt:"<translate(QVector3D(state.txAxisValue * ls,
state.tyAxisValue * ls,
state.tzAxisValue * ls) * dt,
Qt3DRender::QCamera::DontTranslateViewCenter);
}
m_mouseLastPosition = m_mouseCurrentPosition;
}
在main.cpp注册下qmlRegisterType
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
}