Home | Course Index | << Prev. | Guidance | PDF Version of this Page |
![]() |
Course 3D_WPF: 3D-Computer Graphics with C# + WPF
|
![]() |
![]() Let me know what you think |
Main Menu after start of Visual C# 2010 Express: File → New Project... → WPF Application → Name: dice1 → OK.
Replace the default code of MainWindow.xaml and of MainWindow.xaml.cs by the following codes:
MainWindow.xaml: <Window x:Class="dice1.MainWindow" x:Name="window" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="dice1" Width="400" Height="650"> <Window.Resources> <MeshGeometry3D x:Key="face_prototype" Positions= "-0.5 0.5 0, -0.5 -0.5 0, 0.5 -0.5 0, 0.5 0.5 0" TextureCoordinates="0 0, 0 1, 1 1, 1 0" TriangleIndices= "0 1 3, 1 2 3"/> <ImageBrush x:Key="frontBrush" ImageSource="http://www.miszalok.de/C_3D_WPF/C3_Dice/Images/front.bmp" /> <ImageBrush x:Key="rightBrush" ImageSource="http://www.miszalok.de/C_3D_WPF/C3_Dice/Images/right.bmp" /> <ImageBrush x:Key="backBrush" ImageSource="http://www.miszalok.de/C_3D_WPF/C3_Dice/Images/back.bmp" /> <ImageBrush x:Key="leftBrush" ImageSource="http://www.miszalok.de/C_3D_WPF/C3_Dice/Images/left.bmp" /> <ImageBrush x:Key="topBrush" ImageSource="http://www.miszalok.de/C_3D_WPF/C3_Dice/Images/top.bmp" /> <ImageBrush x:Key="bottomBrush" ImageSource="http://www.miszalok.de/C_3D_WPF/C3_Dice/Images/bottom.bmp"/> <DiffuseMaterial x:Key="frontMaterial" Brush="{StaticResource frontBrush }"/> <DiffuseMaterial x:Key="rightMaterial" Brush="{StaticResource rightBrush }"/> <DiffuseMaterial x:Key="backMaterial" Brush="{StaticResource backBrush }"/> <DiffuseMaterial x:Key="leftMaterial" Brush="{StaticResource leftBrush }"/> <DiffuseMaterial x:Key="topMaterial" Brush="{StaticResource topBrush }"/> <DiffuseMaterial x:Key="bottomMaterial" Brush="{StaticResource bottomBrush}"/> <Style TargetType="{x:Type CheckBox}"><!--Set a property and an event handler for all CheckBoxes--> <Setter Property="IsChecked" Value="True"/> <EventSetter Event="Click" Handler="on_checkbox_clicked"/> </Style> <Style TargetType="{x:Type TextBlock}"><!--Set two properties for all TextBlocks--> <Setter Property="FontSize" Value="10"/> <Setter Property="HorizontalAlignment" Value="Center"/> </Style> <Style TargetType="{x:Type Slider}"><!--Set 3 properties and an event handler for all Sliders--> <Setter Property="Minimum" Value="-1.5"/> <Setter Property="Maximum" Value=" 1.5"/> <Setter Property="Value" Value=" 1.5"/> <EventSetter Event="ValueChanged" Handler="on_slider_value_changed"/> </Style> </Window.Resources> <StackPanel Orientation="Vertical"> <StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="0 10 0 10"> <CheckBox x:Name="cb_front" Content="front "/> <CheckBox x:Name="cb_right" Content="right "/> <CheckBox x:Name="cb_back" Content="back "/> <CheckBox x:Name="cb_left" Content="left "/> <CheckBox x:Name="cb_top" Content="top "/> <CheckBox x:Name="cb_bottom" Content="bottom"/> </StackPanel><!--end of <StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="0 10 0 10">--> <StackPanel Orientation="Vertical" Margin="3"> <TextBlock Text="Camera moves parallel to the X-axis"/> <Slider x:Name="camera_X_axis_move_slider"/> <TextBlock Text="Camera moves parallel to the Y-axis"/> <Slider x:Name="camera_Y_axis_move_slider"/> <TextBlock Text="Camera moves parallel to the Z-axis"/> <Slider x:Name="camera_Z_axis_move_slider"/> </StackPanel><!--end of <StackPanel Orientation="Vertical" Margin="3">--> <!--Viewport3D is a drawing canvas which resizes its Content automatically--> <Viewport3D x:Name="viewport"> <Viewport3D.Camera> <PerspectiveCamera x:Name="camera" Position= " 1.5 1.5 1.5" LookDirection="-1 -1 -1" UpDirection= " 0 1 0"/> </Viewport3D.Camera> <!--Any 3D-content must be packed in a ModelVisual3D-object--> <ModelVisual3D x:Name="model"> <ModelVisual3D.Content> <!--Only one Content is allowed. Thus we have to bundle 6 faces and 2 lights inside a group-object.--> <Model3DGroup x:Name="group"> <GeometryModel3D x:Name="front" Geometry ="{StaticResource face_prototype}" Material ="{StaticResource frontMaterial}" BackMaterial="{StaticResource frontMaterial}"/> <GeometryModel3D x:Name="right" Geometry ="{StaticResource face_prototype}" Material ="{StaticResource rightMaterial}" BackMaterial="{StaticResource rightMaterial}"/> <GeometryModel3D x:Name="back" Geometry ="{StaticResource face_prototype}" Material ="{StaticResource backMaterial}" BackMaterial="{StaticResource backMaterial}"/> <GeometryModel3D x:Name="left" Geometry ="{StaticResource face_prototype}" Material ="{StaticResource leftMaterial}" BackMaterial="{StaticResource leftMaterial}"/> <GeometryModel3D x:Name="top" Geometry ="{StaticResource face_prototype}" Material ="{StaticResource topMaterial}" BackMaterial="{StaticResource topMaterial}"/> <GeometryModel3D x:Name="bottom" Geometry ="{StaticResource face_prototype}" Material ="{StaticResource bottomMaterial}" BackMaterial="{StaticResource bottomMaterial}"/> <AmbientLight Color="#000000"/> <DirectionalLight x:Name="directionalLight" Color="#ffffff" Direction="-1 -1 -1" /> </Model3DGroup><!--end of <Model3DGroup x:Name="group">--> </ModelVisual3D.Content> </ModelVisual3D><!--end of <ModelVisual3D x:Name="model">--> </Viewport3D> </StackPanel><!--end of <StackPanel Orientation="Vertical">--> </Window> MainWindow.xaml.cs: using System; using System.Windows; using System.Windows.Controls; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Media.Media3D; namespace dice1 { public partial class MainWindow : Window { Matrix3D matrix_front = new Matrix3D(), matrix_right = new Matrix3D(), matrix_back = new Matrix3D(), matrix_left = new Matrix3D(), matrix_top = new Matrix3D(), matrix_bottom = new Matrix3D(); public MainWindow() //constructor { InitializeComponent(); corrugate(); matrix_front .Rotate( new Quaternion( new Vector3D(0,1,0), 0 ) ); //do nothing matrix_right .Rotate( new Quaternion( new Vector3D(0,1,0), 90 ) ); //turn right matrix_back .Rotate( new Quaternion( new Vector3D(0,1,0), 180 ) ); //turn around matrix_left .Rotate( new Quaternion( new Vector3D(0,1,0), -90 ) ); //turn left matrix_top .Rotate( new Quaternion( new Vector3D(1,0,0), -90 ) ); //turn up matrix_bottom.Rotate( new Quaternion( new Vector3D(1,0,0), 90 ) ); //turn down matrix_front .Translate( new Vector3D( 0.0, 0.0, 0.5 ) ); //shift ahead matrix_right .Translate( new Vector3D( 0.5, 0.0, 0.0 ) ); //shift right matrix_back .Translate( new Vector3D( 0.0, 0.0,-0.5 ) ); //shift left matrix_left .Translate( new Vector3D(-0.5, 0.0, 0.0 ) ); //shift back matrix_top .Translate( new Vector3D( 0.0, 0.5, 0.0 ) ); //shift upwards matrix_bottom.Translate( new Vector3D( 0.0,-0.5, 0.0 ) ); //shift downwards front .Transform = new MatrixTransform3D( matrix_front ); right .Transform = new MatrixTransform3D( matrix_right ); back .Transform = new MatrixTransform3D( matrix_back ); left .Transform = new MatrixTransform3D( matrix_left ); top .Transform = new MatrixTransform3D( matrix_top ); bottom.Transform = new MatrixTransform3D( matrix_bottom); } protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo) { viewport.Width = window.ActualWidth; viewport.Height = window.ActualHeight - 8*camera_X_axis_move_slider.ActualHeight; } private void on_checkbox_clicked(object sender, EventArgs e) { switch( ((CheckBox)sender).Name ) { case "cb_front": if ( cb_front .IsChecked == false ) group.Children.Remove( front ); else group.Children.Insert( 0, front ); break; case "cb_right": if ( cb_right .IsChecked == false ) group.Children.Remove( right ); else group.Children.Insert( 0, right ); break; case "cb_back": if ( cb_back .IsChecked == false ) group.Children.Remove( back ); else group.Children.Insert( 0, back ); break; case "cb_left": if ( cb_left .IsChecked == false ) group.Children.Remove( left ); else group.Children.Insert( 0, left ); break; case "cb_top": if ( cb_top .IsChecked == false ) group.Children.Remove( top ); else group.Children.Insert( 0, top ); break; case "cb_bottom": if ( cb_bottom.IsChecked == false ) group.Children.Remove( bottom ); else group.Children.Insert( 0, bottom ); break; } model.Content = group; } private void on_slider_value_changed(object sender, EventArgs e) { try { double x = camera_X_axis_move_slider.Value; double y = camera_Y_axis_move_slider.Value; double z = camera_Z_axis_move_slider.Value; camera.Position = new Point3D( x, y, z ); camera.LookDirection = -(Vector3D)camera.Position; //always look to (0,0,0) camera.LookDirection.Normalize(); //vector length = 1 directionalLight.Direction = camera.LookDirection; //light comes from the camera } catch {} } private void corrugate() { const int nn = 60; //face width 1.0 will be divided into nn vertical stripes Point3D [] p = new Point3D [ 2*nn + 2 ]; //a stripe has 4 vertices Point [] t = new Point [ 2*nn + 2 ]; int[] index = new int[ 6*nn ]; //2 triangles times 3 vertices per stripe double x, dx = 1.0 / nn; //dx = stripe width int i, j; double frequency = 8, amplitude = 0.02; for ( i=0, x=0; i < p.Length; i+=2, x+=dx ) { p[i].X = p[i+1].X = x - 0.5; //face starts at x=-0.5 and ends at x = 0.5 t[i].X = t[i+1].X = x; //texture starts at x= 0.0 and ends at x = 1.0 p[i].Y = 0.5; p[i+1].Y = -0.5; //face top and bottom t[i].Y = 0; t[i+1].Y = 1; //texture top and bottom p[i].Z = p[i+1].Z = amplitude * Math.Sin( x*frequency*Math.PI ); } for ( i=0, j=0; i < 6*nn; i+=6, j+=2 ) //2 triangles for each stripe { index[i ] = j; //1. vertex of 1. triangle index[i+1] = j+1; //2. vertex of 1. triangle index[i+2] = j+2; //3. vertex of 1. triangle index[i+3] = j+1; //1. vertex of 2. triangle index[i+4] = j+3; //2. vertex of 2. triangle index[i+5] = j+2; //3. vertex of 2. triangle } MeshGeometry3D face_prototype = (MeshGeometry3D)this.FindResource( "face_prototype" ); face_prototype.Positions = new Point3DCollection ( p ); face_prototype.TextureCoordinates = new PointCollection ( t ); face_prototype.TriangleIndices = new Int32Collection ( index ); } } }
top of page:![]() |