Recap
Part of the source code refers to Github, which is the code of a book called WPF-3D. You can buy the physical book if you have conditions.
The source address of this article: WPF3D + mouse control camera perspective + package into a class
Article directory
Encapsulate the keyboard control into a class
At present, it has been realized to adjust the camera's angle of view through the six keys of up, down, left, and Q and E. Since these functions are relatively concentrated with each other, it is very reasonable to encapsulate them into a class according to the principles of low coupling and high cohesion.
The result of this is that MainWindow
the code in Rang is greatly reduced, leaving only 5 custom functions Window_Loaded
, DefineCamera
, DefineLights
, , DefineModel
and . MakeCubeMesh
The original KeyboardControl_KeyDown
functions will be moved to the camera control class.
The final effect is exactly the same as when it is not encapsulated into a class
Among them, the functions and member variables related to the camera are
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);
}
The next step is to write CameraController
this class, and its member variables are as follows
// 每次转换的的最小值
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;
Among them, the two constants cmDR
and cmDThera
represent the distance and angle of a single movement, respectively. For example, by pressing the left arrow, the current camera angle is decreased cmDTheta
.
The constructor is
public CameraController(PerspectiveCamera camera, Viewport3D viewport,UIElement mainWindow)
{
cm = camera;
viewport.Camera = cm;
this.mainWindow = mainWindow;
this.mainWindow.PreviewKeyDown += mainWindow_KeyDown;
PositionCamera();
}
Finally, there is the interaction logic with the buttons. These contents have been written in the previous section and will not be described in detail.
// 将角度转为向量
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);
}
Control the viewing angle with the mouse
Only from the structure of the code, there is no difference between controlling with the mouse and controlling with the keyboard, the only difference is that the return values of the two are different.
So the initialization code is highly similar
public CameraController(PerspectiveCamera camera, Viewport3D viewport,
UIElement mainWindow)
{
cm = camera;
viewport.Camera = cm;
this.mainWindow = mainWindow;
//鼠标按下时的动作
this.mainWindow.MouseLeftButtonDown += mainWindow_LeftDown;
PositionCameraMouse();
}
The final result is
Among them mainWindow_LeftDown
is the key to completing the action. As the name suggests, the trigger condition of this function is to press the left mouse button. The function it implements is that when the left button has been pressed, drag the mouse to change the camera angle of view, so this function is written as
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);
}
Among them, ptLast
is a global variable used to save the position when the mouse is pressed. MainWindow_MouseMove
It is the action performed when the mouse moves, and the action MainWindow_MouseUp
when the mouse leaves. Obviously, the latter is easier to implement, and the binding event when the mouse is clicked and the unbinding event when the mouse is released is also a very common way of writing, and it is widely used. , the specific writing is as follows:
private void MainWindow_MouseUp(object sender, MouseButtonEventArgs e)
{
mainWindow.ReleaseMouseCapture();
mainWindow.MouseMove -= MainWindow_MouseMove;
mainWindow.MouseUp -= MainWindow_MouseUp;
}
MouseMove
What is written is the interactive function.
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);
}