[C#] Make a super beggar version of Minecraft (WPF 3D select and move geometry)

Recap

This article is the last blog in the WPF 3D series.
Source address: [C#] WPF 3D select and move geometry

Manipulate Geometry

It is said that Minecraft was developed by three programmers in a week, and that one programmer developed a beggar's version of Minecraft in half a day. There is absolutely no problem in reasoning.

And as we all know, Minecraft is a collection of countless pixel blocks, and pixel blocks are cubes. As for creating a new cube, everyone is very proficient in this. Not only can you create a new cube, but you can even create as many cubes as you want.

New cube

But it is not currently possible to create by hand, so next add a shortcut Ctrl+ Nto quickly create a cube. CtrlWhen + is pressed Nthe effect is

insert image description here

This is definitely trivial for those who can already generate a row of cubes, first bind the shortcut in the constructor

public MainWindow()
{
    
    
    InitializeComponent();
    KeyDown += MainWindow_KeyDown;
}

MainWindow_KeyDowndefined as

private void MainWindow_KeyDown(object sender, KeyEventArgs e)
{
    
    

    if ((Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl)) && Keyboard.IsKeyDown(Key.N))
    {
    
    
        MeshGeometry3D mesh = MakeCubeMesh(0, 0, 0, 1);
        Color color = Color.FromArgb(255, 0, 255, 0);
        DiffuseMaterial material = new DiffuseMaterial(new SolidColorBrush(color));
        GeometryModel3D model = new GeometryModel3D(mesh, material);
        group3d.Children.Add(model);
    }
}

Among them MakeCubeMeshare the custom functions, which have been written as early as the first blog in this series, as well as other functions such as light effects. The only difference is that this time it doesn't generate multiple cubes in the first place, so no DefineModelfunction is needed.

Set the cube position

Although a new cube has been created just now, the position of the cube cannot be determined. Next, a dialog box must be created to set the position of the newly created cube.

Right-click the project, create a new window, name ParaDialogit, it xamlis

<Window x:Class="MyWord.ParaDialog"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="输入参数" Height="180" Width="300">
    <DockPanel LastChildFill="True">
        <StackPanel DockPanel.Dock="Bottom"
            Orientation="Horizontal" HorizontalAlignment="Right">
            <Button Content="设置" Click="okButton_Click" Margin="5" Width="60" Height="25"/>
            <Button Content="取消" IsCancel="True" Margin="5" Width="60" Height="25"/>
        </StackPanel>
        <UniformGrid Columns="1" Margin="5" x:Name="ufgLabel"/>
        <UniformGrid Columns="1" Margin="5" x:Name="ufgTextBox"/>

    </DockPanel>
</Window>

Its cscore code is

public partial class ParaDialog : Window
{
    
    
    static readonly string[] labels = new string[4] {
    
     "x坐标", "y坐标", "z坐标", "边长" };
    List<TextBox> paraBoxes = new List<TextBox>();
    public double[] para;
    public ParaDialog(double[] para)
    {
    
    
        this.para = para;
        InitializeComponent();
        string tmp;
        for (int i = 0; i < 4; i++)
        {
    
    
            //向UniformGrid中欧给填充文字块
            ufgLabel.Children.Add(new TextBlock() {
    
     Text = labels[i] });
            paraBoxes.Add(new TextBox());
            ufgTextBox.Children.Add(paraBoxes[i]);
        }
    }

    private void okButton_Click(object sender, RoutedEventArgs e)
    {
    
    
        for (int i = 0; i < 4; i++)
            para[i] = double.Parse(paraBoxes[i].Text);
        DialogResult = true;
    }
}

Finally, change the response code for Ctrl+N

//...前面不用改
if ((Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl)) && Keyboard.IsKeyDown(Key.N))
{
    
    
  double[] para = new double[4];
  ParaDialog pDialog = new ParaDialog(para);
  pDialog.ShowDialog();
  if (pDialog.DialogResult != true)
      return;
  MeshGeometry3D mesh = MakeCubeMesh(para[0],para[1],para[2],para[3]);
  //...后面不用改
}

insert image description here

selected cube

If you want to operate the geometry, the premise is to select it, then bind a mouse action to complete the selected operation, change the xamlcode first, and Viewport3Dput borderit in a

<Border Name="mainBorder" Background="White" MouseDown="mainBorder_MouseDown">
    <Viewport3D x:Name="v3dMain">
    </Viewport3D>
</Border>

Then create a new mainBorder_MouseDownfunction

private void mainBorder_MouseDown(object sender, MouseButtonEventArgs e)
{
    
    
    if (e.LeftButton == MouseButtonState.Released) return;

    Color color = Color.FromArgb(255, 0, 255, 0);
    var material = new DiffuseMaterial(new SolidColorBrush(color));

    //获取鼠标在对象中的位置
    Point mousePos = e.GetPosition(v3dMain);

    // 执行点击操作
    HitTestResult result = VisualTreeHelper.HitTest(v3dMain, mousePos);

    //此即鼠标点击到曲面上的结果
    var meshResult = result as RayMeshGeometry3DHitTestResult;
    GeometryModel3D model = null;
    if ((meshResult != null) && (meshResult.ModelHit is GeometryModel3D))
        model = meshResult.ModelHit as GeometryModel3D;

    //如果刚才选了别的模型,则使之恢复绿色
    if (SelectedModel != null)
        SelectedModel.Material = material;
    //选择新的模型
    SelectedModel = model;
    if (model != null) 
        model.Material = new DiffuseMaterial(Brushes.Fuchsia);
}

Its effect is

insert image description here

move geometry

Since it can be selected, it is absolutely trivial to move something.

Looking back at the previous mouse-operated cameras, the basic process is that after the mouse is clicked, another function is bound, and when the mouse is released, it is unbound.

In WPF 3D, Tranformmembers are provided that can be used to move the geometry, so when creating a new geometry, be sure to add a line

//MainWindow_KeyDown函数
model.Transform = new TranslateTransform3D(0, 0, 0);

Then add a global variable to hold the old location

private Point3D oldPoint;

Then modify mainBorder_MouseDown, in fact, just add at the end

mainBorder.CaptureMouse();
mainBorder.MouseMove += MainBorder_MouseMove;
mainBorder.MouseUp += MainBorder_MouseUp;

Next is the action when the mouse is moved and bounced. The bounce is very simple, it is nothing more than unbinding the mouse action; and when the mouse is moved, the change parameters of the geometry need to be changed.

private void MainBorder_MouseUp(object sender, MouseButtonEventArgs e)
{
    
    
    mainBorder.ReleaseMouseCapture();
    mainBorder.MouseMove -= MainBorder_MouseMove;
    mainBorder.MouseUp -= MainBorder_MouseUp;
}

private void MainBorder_MouseMove(object sender, MouseEventArgs e)
{
    
    
    Point newPoint = e.GetPosition(mainBorder);
    var res = VisualTreeHelper.HitTest(v3dMain, newPoint);
    if (res == null) return;
    var newResult = res as RayMeshGeometry3DHitTestResult;

    var deltaPt = newResult.PointHit - oldPoint;
    var trans = SelectedModel.Transform as TranslateTransform3D;
    trans.OffsetX += deltaPt.X;
    trans.OffsetY += deltaPt.Y;
    trans.OffsetZ += deltaPt.Z;
    oldPoint = newResult.PointHit;
}

The effect is

insert image description here
Well, the super beggar version of Minecraft is ready →_→Is it listed in the United States or in Hong Kong?

Guess you like

Origin blog.csdn.net/m0_37816922/article/details/124443939