键盘和鼠标的控制
多个立方体
只有一个立方体无论怎么看都看不出什么花样,所以接下来准备新建多个立方体。那么前提就是将立方体封装成一个函数,从而不必每次都写重复的代码。
而又因涉及到多个图形,所以在cs
端写比较便捷,其xaml
代码中除了主Window
外,只有一个
<Viewport3D Name="mainViewport"/>
最终得到的图形为
在下面的代码中,w
是正方体边长,x,y,z
为正方体中心。
private MeshGeometry3D MakeCubeMesh(double x, double y, double z, double w)
{
MeshGeometry3D mesh = new MeshGeometry3D();
// Define the positions.
w /= 2;
Point3D[] points =
{
new Point3D(x - w, y - w, z - w),
new Point3D(x + w, y - w, z - w),
new Point3D(x + w, y - w, z + w),
new Point3D(x - w, y - w, z + w),
new Point3D(x - w, y - w, z + w),
new Point3D(x + w, y - w, z + w),
new Point3D(x + w, y + w, z + w),
new Point3D(x - w, y + w, z + w),
new Point3D(x + w, y - w, z + w),
new Point3D(x + w, y - w, z - w),
new Point3D(x + w, y + w, z - w),
new Point3D(x + w, y + w, z + w),
new Point3D(x + w, y + w, z + w),
new Point3D(x + w, y + w, z - w),
new Point3D(x - w, y + w, z - w),
new Point3D(x - w, y + w, z + w),
new Point3D(x - w, y - w, z + w),
new Point3D(x - w, y + w, z + w),
new Point3D(x - w, y + w, z - w),
new Point3D(x - w, y - w, z - w),
new Point3D(x - w, y - w, z - w),
new Point3D(x - w, y + w, z - w),
new Point3D(x + w, y + w, z - w),
new Point3D(x + w, y - w, z - w),
};
foreach (Point3D point in points) mesh.Positions.Add(point);
// Define the triangles.
Tuple<int, int, int>[] triangles = new Tuple<int, int, int>[12];
for (int i = 0; i < triangles.Length; i++)
{
int tmp = i % 2 == 0 ? 2 : -2;
triangles[i] = new Tuple<int, int, int>(
2 * i, 2 * i + 1, 2 * i + tmp);
}
foreach (Tuple<int, int, int> tuple in triangles)
{
mesh.TriangleIndices.Add(tuple.Item1);
mesh.TriangleIndices.Add(tuple.Item2);
mesh.TriangleIndices.Add(tuple.Item3);
}
return mesh;
}
相应地,生成模型的代码也要随之改动,
private void DefineModel(Model3DGroup group)
{
// Make the ground.
MeshGeometry3D groundMesh = new MeshGeometry3D();
const double wid = 10;
groundMesh.Positions.Add(new Point3D(-wid, 0, -wid));
groundMesh.Positions.Add(new Point3D(-wid, 0, +wid));
groundMesh.Positions.Add(new Point3D(+wid, 0, +wid));
groundMesh.Positions.Add(new Point3D(+wid, 0, -wid));
foreach (var i in new int[6] {
0, 1, 2, 0, 2, 3 })
groundMesh.TriangleIndices.Add(i);
DiffuseMaterial groundMaterial = new DiffuseMaterial(Brushes.DarkGray);
GeometryModel3D groundModel = new GeometryModel3D(groundMesh, groundMaterial);
group.Children.Add(groundModel);
//为每个正方体染上不同的颜色以做区分
Func<int, byte> getRGB = (x) => (byte)(128 + x * 50);
//生成一些立方体.
for (int x = -2; x <= 2; x += 2)
for (int z = -2; z <= 2; z += 2)
{
MeshGeometry3D mesh = MakeCubeMesh(x, 0.5, z, 1);
Color color = Color.FromArgb(255,
getRGB(x), getRGB(z), getRGB(x));
DiffuseMaterial material = new DiffuseMaterial(
new SolidColorBrush(color));
GeometryModel3D model = new GeometryModel3D(mesh, material);
group.Children.Add(model);
}
}
而摄像机、光线等代码可不做改动,就是上一节的代码
// 窗口加载时执行
private void Window_Loaded(object sender, RoutedEventArgs e)
{
// 定义WPF组件
ModelVisual3D visual3d = new ModelVisual3D();
Model3DGroup group3d = new Model3DGroup();
visual3d.Content = group3d;
mainViewport.Children.Add(visual3d);
DefineCamera(mainViewport);
DefineLights(group3d);
DefineModel(group3d);
}
// 定义相机
private void DefineCamera(Viewport3D viewport)
{
Point3D p = new Point3D(1.5, 2, 3);
Vector3D lookDirection = new Vector3D(-p.X, -p.Y, -p.Z);
Vector3D upDirection = new Vector3D(0, 1, 0);
double fieldOfView = 60;
//对应PerspectiveCamera的四个参数
PerspectiveCamera camera = new PerspectiveCamera(
p, lookDirection, upDirection, fieldOfView);
viewport.Camera = camera;
}
// 定义光线.
private void DefineLights(Model3DGroup group)
{
group.Children.Add(new AmbientLight(Colors.Gray));
Vector3D direction = new Vector3D(1, -2, -3);
group.Children.Add(new DirectionalLight(Colors.Gray, direction));
}
添加动作
上面的视角其实并不算好,毕竟只看到了局部。而且由于立方体之间的相互遮挡,就算调整视场角也没法一栏全貌。
所以,如果摄像头可以走动就好了。为了实现这个目的,可以为相机添加一些键盘指令,从而更改视角,就像下面这种
//和相机有关的全局变量
private PerspectiveCamera TheCamera = null;
public Point3D CameraPosition = new Point3D(4, 0.5, 5);
public double CameraTheta = Math.PI * 1.3;
//相机初始化指令
private void DefineCamera2(Viewport3D viewport)
{
TheCamera = new PerspectiveCamera(
CameraPosition, AngleToVector(CameraTheta, 1), new Vector3D(0, 1, 0), 60);
viewport.Camera = TheCamera;
//绑定按键动作
PreviewKeyDown += KeyboardControl_KeyDown;
}
接下来则是重头戏,关于按键和
private void KeyboardControl_KeyDown(object sender, KeyEventArgs e)
{
Vector3D v;
const double CameraDTheta = Math.PI / 30;
const double CameraDR = 0.1;
//将角度a和长度L转为矢量
Func<double, double, Vector3D> AngleToVector = (a, L)
=> new Vector3D(L * Math.Cos(a), 0, L * Math.Sin(a));
switch (e.Key)
{
case Key.Left:
CameraTheta -= CameraDTheta;
break;
case Key.Right:
CameraTheta += CameraDTheta;
break;
case Key.Up:
CameraPosition += AngleToVector(CameraTheta, CameraDTheta);
break;
case Key.Down:
CameraPosition += AngleToVector(CameraTheta, -CameraDTheta);
break;
case Key.Q:
v = AngleToVector(CameraTheta, CameraDR);
CameraPosition += new Vector3D(v.Z, 0, -v.X);
break;
case Key.E:
v = AngleToVector(CameraTheta, CameraDR);
CameraPosition += new Vector3D(-v.Z, 0, +v.X);
break;
}
TheCamera.Position = CameraPosition;
TheCamera.LookDirection = AngleToVector(CameraTheta, 1);
TheCamera.UpDirection = new Vector3D(0, 1, 0);
}