[C#]WPF 3D 用鼠标控制相机视角

前情提要

部分源码参考Github,是一本名为WPF-3D的书的代码,有条件的可以买下实体书。
本文源码地址:WPF3D+鼠标控制相机视角+封装成类

将键盘控制封装成类

目前已经实现了通过上下左右和Q、E这六个键来调整相机的视角,由于这些功能彼此之间比较集中,按照低耦合、高内聚的原则,封装成一个类是十分合理的。

而这样做的结果就是,让MainWindow中的代码大大缩减,只剩下Window_LoadedDefineCameraDefineLightsDefineModelMakeCubeMesh这5个自定义的函数。而原本的KeyboardControl_KeyDown等功能就要移到相机控制类了。

最终得到的效果与未封装成类时是完全一样的

在这里插入图片描述

其中,与相机相关的函数、成员变量为

private PerspectiveCamera TheCamera = null;

// 相机遥控器,CameraController是自定义了一个类
private CameraController cc = null;
private void DefineCamera(Viewport3D viewport)
{
    
    
    TheCamera = new PerspectiveCamera();
    TheCamera.FieldOfView = 60;
    cc = new CameraController(TheCamera, viewport, this);
}

接下来就要写CameraController这个类,其成员变量如下

// 每次转换的的最小值
public const double cmDR = 0.1;
public const double cmDTheta = Math.PI / 30;

//相机
public PerspectiveCamera cm = null;
// 传入主窗口的动作
private UIElement mainWindow = null;
// 相机位置和方向
public Point3D cmPosition {
    
     get; set; } = new Point3D(4, 0.5, 5);
public double cmTheta = Math.PI * 1.3;

其中,两个常量cmDRcmDThera分别表示单次移动的距离和角度。例如按一下左箭头,那么当前相机的角度就减少cmDTheta

扫描二维码关注公众号,回复: 13822315 查看本文章

构造函数为

public CameraController(PerspectiveCamera camera, Viewport3D viewport,UIElement mainWindow)
{
    
    
    cm = camera;
    viewport.Camera = cm;
    this.mainWindow = mainWindow;
    this.mainWindow.PreviewKeyDown += mainWindow_KeyDown;
    PositionCamera();
}

最后是与按键的交互逻辑,这些内容是上一节已经写过的,就不再详述了

// 将角度转为向量
protected Vector3D AngleToVector(double angle, double length)
{
    
    
    return new Vector3D(
        length * Math.Cos(angle), 0, length * Math.Sin(angle));
}

protected void MoveLR(bool isLeft=true)
{
    
    
    Vector3D v = AngleToVector(cmTheta, cmDR);
    if (isLeft)
        cmPosition += new Vector3D(v.Z, 0, -v.X);
    else
        cmPosition += new Vector3D(-v.Z, 0, v.X);
}

//向上或者向下移动
protected void MoveUD(bool isUp = true)
{
    
    
    Vector3D v = AngleToVector(cmTheta, cmDR);
    if (isUp)
        cmPosition += v;
    else
        cmPosition -= v;
}

// 其中 上、下、Q、E代表平移
// 左右代表旋转
private void mainWindow_KeyDown(object sender, KeyEventArgs e)
{
    
    
    switch (e.Key)
    {
    
    
        case Key.Left: cmTheta -= cmDTheta;break;
        case Key.Right: cmTheta += cmDTheta;break;
        case Key.Up:MoveUD(true);break;
        case Key.Down: MoveUD(false);break;
        case Key.Q: MoveLR(true);break;
        case Key.E: MoveLR(false);break;
    }

    // 更新相机位置
    PositionCamera();
}


// 更新相机的位置
protected virtual void PositionCamera()
{
    
    
    cm.Position = cmPosition;
    cm.LookDirection = AngleToVector(cmTheta, 1);
    cm.UpDirection = new Vector3D(0, 1, 0);
}

用鼠标控制视角

仅从代码的结构来说,用鼠标控制和用键盘控制没有任何区别,区别仅在于二者的返回值是不同的。

所以初始化代码是高度相似的

public CameraController(PerspectiveCamera camera, Viewport3D viewport,
    UIElement mainWindow)
{
    
    
    cm = camera;
    viewport.Camera = cm;
    this.mainWindow = mainWindow;
    //鼠标按下时的动作
    this.mainWindow.MouseLeftButtonDown += mainWindow_LeftDown;
    PositionCameraMouse();
}

最终得到的结果为

在这里插入图片描述

其中mainWindow_LeftDown就是完成动作的关键,顾名思义,这个函数的触发条件是按下鼠标左键。而其实现的功能则为,在左键已经按下的情况下,拖动鼠标,可实现相机视角的变化,所以这个函数写为

private Point ptLast;
private void mainWindow_LeftDown(object sender, MouseButtonEventArgs e)
{
    
    
    mainWindow.CaptureMouse();
    mainWindow.MouseMove += MainWindow_MouseMove;
    mainWindow.MouseUp += MainWindow_MouseUp;
    ptLast = e.GetPosition(mainWindow);
}

其中,ptLast是一个全局变量,用于保存鼠标按下时的位置。MainWindow_MouseMove是鼠标移动时执行的动作,MainWindow_MouseUp是鼠标离开时的动作,显然后者更容易实现,而且这种鼠标点击时绑定事件、鼠标松开时解绑事件也是一种非常通用的写法,应用十分广泛,具体写法如下:

private void MainWindow_MouseUp(object sender, MouseButtonEventArgs e)
{
    
    
    mainWindow.ReleaseMouseCapture();
    mainWindow.MouseMove -= MainWindow_MouseMove;
    mainWindow.MouseUp -= MainWindow_MouseUp;
}

MouseMove写的就是交互功能了,

private void MainWindow_MouseMove(object sender, MouseEventArgs e)
{
    
    
    const double xscale = 0.1;
    const double yscale = 0.1;

    Point newPoint = e.GetPosition(mainWindow);
    double dx = newPoint.X - ptLast.X;
    double dy = newPoint.Y - ptLast.Y;

    CameraTheta -= dx * CameraDTheta * xscale;
    CameraPhi -= dy * CameraDPhi * yscale;

    ptLast = newPoint;
    PositionCameraMouse();
}

private void PositionCameraMouse()
{
    
    
    double x, y, z;

    y = CameraR * Math.Cos(CameraPhi);
    double h = CameraR * Math.Sin(CameraPhi);
    x = h * Math.Sin(CameraTheta);
    z = h * Math.Cos(CameraTheta);

    cm.Position = new Point3D(x, y, z);
    cm.LookDirection = new Vector3D(-x, -y, -z);
    cm.UpDirection = new Vector3D(0, 1, 0);
}

猜你喜欢

转载自blog.csdn.net/m0_37816922/article/details/124196840