In the previous article we covered many of the basics of rendering some simple 3D cubes, and then we animated them. In this article we are going to take it a few steps further. The first task we are going to tackle is moving around our 3D world. Regulars to these articles will recognize some of the techniques I’ll be using here.
The Third Project
Let’s start with a simple background to make the movement a little more visible. We going to use the same cubes from the first two projects and we going to put a large cube over the rest. Also, we are going to use an inverted cube, so that the inside of the faces are rendered.
Let’s begin, Start a new Windows Presentation Foundation (WPF) project and copy and paste the below code into Window1.xaml.
<Viewport3D Name="Viewport3D1" Margin="12,41,12,12"> <Viewport3D.Camera> <PerspectiveCamera Position="-40,40,40" LookDirection="40,-40,-40 " UpDirection="0,0,1" /> </Viewport3D.Camera> <ModelVisual3D > <ModelVisual3D.Content> <Model3DGroup > <DirectionalLight Color="White" Direction="-10,-10,-10" /> <GeometryModel3D> <GeometryModel3D.Geometry> <MeshGeometry3D Positions="0,0,0 10,0,0 10,10,0 0,10,0 0,0,10 10,0,10 10,10,10 0,10,10" TriangleIndices="0 3 1 1 3 2 0 4 3 4 7 3 4 6 7 4 5 6 0 1 4 1 5 4 1 2 6 6 5 1 2 3 7 7 6 2 "/> </GeometryModel3D.Geometry> <GeometryModel3D.Material> <DiffuseMaterial Brush="red" AmbientColor="Yellow" Color="Yellow" /> </GeometryModel3D.Material> </GeometryModel3D> </Model3DGroup> </ModelVisual3D.Content> </ModelVisual3D> <ModelVisual3D > <ModelVisual3D.Content> <Model3DGroup > <DirectionalLight Color="White" Direction="-10,-10,-10" /> <GeometryModel3D> <GeometryModel3D.Geometry> <MeshGeometry3D Positions="0,-10,-10 10,-10,-10 10,0,-10 0,0,-10 0,-10,0 10,-10,0 10,0,0 0,0,0" TriangleIndices="0 3 1 1 3 2 0 4 3 4 7 3 4 6 7 4 5 6 0 1 4 1 5 4 1 2 6 6 5 1 2 3 7 7 6 2 "/> </GeometryModel3D.Geometry> <GeometryModel3D.Material> <DiffuseMaterial Brush="yellow" AmbientColor="Yellow" Color="Yellow" /> </GeometryModel3D.Material> </GeometryModel3D> </Model3DGroup> </ModelVisual3D.Content> </ModelVisual3D> <ModelVisual3D > <ModelVisual3D.Content> <Model3DGroup > <DirectionalLight Color="White" Direction="-10,-10,-10" /> <GeometryModel3D> <GeometryModel3D.Geometry> <MeshGeometry3D Positions="-10,-10,0 0,-10,0 0,0,0 -10,0,0 -10,-10,10 0,-10,10 0,0,10 -10,0,10" TriangleIndices="0 3 1 1 3 2 0 4 3 4 7 3 4 6 7 4 5 6 0 1 4 1 5 4 1 2 6 6 5 1 2 3 7 7 6 2 "/> </GeometryModel3D.Geometry> <GeometryModel3D.Material> <DiffuseMaterial Brush="green" AmbientColor="white" Color="white" /> </GeometryModel3D.Material> </GeometryModel3D> </Model3DGroup> </ModelVisual3D.Content> </ModelVisual3D> <ModelVisual3D > <ModelVisual3D.Content> <Model3DGroup > <DirectionalLight Color="White" Direction="-10,-10,-10" /> <GeometryModel3D> <GeometryModel3D.Geometry> <MeshGeometry3D Positions="-10,0,-10 0,0,-10 0,10,-10 -10,10,-10 -10,0,0 0,0,0 0,10,0 -10,10,0" TriangleIndices="0 3 1 1 3 2 0 4 3 4 7 3 4 6 7 4 5 6 0 1 4 1 5 4 1 2 6 6 5 1 2 3 7 7 6 2 "/> </GeometryModel3D.Geometry> <GeometryModel3D.Material> <DiffuseMaterial Brush="blue" AmbientColor="white" Color="white" /> </GeometryModel3D.Material> </GeometryModel3D> </Model3DGroup> </ModelVisual3D.Content> </ModelVisual3D> <ModelVisual3D > <ModelVisual3D.Content> <Model3DGroup > <DirectionalLight Color="White" Direction="-10,-10,-10" /> <GeometryModel3D> <GeometryModel3D.Geometry> <MeshGeometry3D Positions="-40,-40,-40 40,-40,-40 40,40,-40 -40,40,-40 -40,-40,40 40,-40,40 40,40,40 -40,40,40" TriangleIndices="0 1 3 1 2 3 0 3 4 4 3 7 4 7 6 4 6 5 0 4 1 1 4 5 1 6 2 6 1 5 2 7 3 7 2 6 "/> </GeometryModel3D.Geometry> <GeometryModel3D.Material> <DiffuseMaterial Brush="brown" AmbientColor="Yellow" Color="Yellow" /> </GeometryModel3D.Material> </GeometryModel3D> </Model3DGroup> </ModelVisual3D.Content> </ModelVisual3D> </Viewport3D>
You will notice that most of the XAML above is the same from our previous WPF projects, with the exception of the last 3D Model. Here you will notice the Triangle Indices are in a different order to the rest. This gives us our inverted cube where you see the face on the inside of the cube but not from the outside.
And now place this in the VB code area:
Imports System.Windows.Media.Media3D Imports System.Windows.Media.Animation Private Enum Tristate Neg = -1 None = 0 Pos = 1 End Enum Private newpcam As New Media3D.PerspectiveCamera() Private Pos1 As New Media3D.Point3D(-40, 40, 40) Private Dir1 As New Media3D.Point3D(40, -40, -40) Private timer As System.Windows.Threading.DispatcherTimer Private X As Double = -40 Private Y As Double = 40 Private Z As Double = 40 Private XMove As Tristate Private YMove As Tristate Private ZMove As Tristate Private Sub SetX(ByVal Val As Double) Pos1.X = Val Dir1.X = -Val End Sub Private Sub SetY(ByVal Val As Double) Pos1.Y = Val Dir1.Y = -Val End Sub Private Sub SetZ(ByVal Val As Double) Pos1.Z = Val Dir1.Z = -Val End Sub Private Sub Window1_KeyDown(ByVal sender As Object, ByVal e As System.Windows.Input.KeyEventArgs) Handles Me.PreviewKeyDown Select Case e.Key Case Key.Up YMove = Tristate.Pos Case Key.Down YMove = Tristate.Neg Case Key.Left XMove = Tristate.Neg Case Key.Right XMove = Tristate.Pos Case Key.PageUp ZMove = Tristate.Neg Case Key.PageDown ZMove = Tristate.Pos End Select End Sub Private Sub Window1_KeyUp(ByVal sender As Object, ByVal e As System.Windows.Input.KeyEventArgs) Handles Me.PreviewKeyUp Select Case e.Key Case Key.Up, Key.Down YMove = Tristate.None Case Key.Left, Key.Right XMove = Tristate.None Case Key.PageUp,Key.PageDown ZMove = Tristate.None End Select End Sub Sub timer_Tick(ByVal sender As Object, ByVal e As EventArgs) If XMove <> Tristate.None Then X += XMove SetX(X) End If If YMove <> Tristate.None Then Y += YMove SetY(Y) End If If ZMove <> Tristate.None Then Z += ZMove SetZ(Z) End If newpcam.Position = Pos1 newpcam.LookDirection = Dir1 End Sub Private Sub Window1_Loaded(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles MyBase.Loaded Viewport3D1.Camera = newpcam SetX(X) SetY(Y) SetZ(Z) newpcam.Position = Pos1 newpcam.LookDirection = Dir1 Dim Cube_ani As New DoubleAnimation(0, 359, TimeSpan.FromSeconds(2)) Dim Cube_rot As New AxisAngleRotation3D() Cube_rot.Axis = New Vector3D(0, 1, 0) Viewport3D1.Children.Item(0).Transform = New RotateTransform3D(Cube_rot, 5, 5, 5) Viewport3D1.Children.Item(1).Transform = New RotateTransform3D(Cube_rot, 5, -5, -5) Viewport3D1.Children.Item(2).Transform = New RotateTransform3D(Cube_rot, -5, -5, 5) Viewport3D1.Children.Item(3).Transform = New RotateTransform3D(Cube_rot, -5, 5, -5) Cube_rot.BeginAnimation(AxisAngleRotation3D.AngleProperty, Cube_ani) timer = New System.Windows.Threading.DispatcherTimer() timer.Interval = TimeSpan.FromMilliseconds(16) AddHandler timer.Tick, AddressOf timer_Tick Me.timer.Start() End Sub
Regulars will notice the Tristate Type that I love to use, and the Timer to smooth out the animation. Beyond that, let’s see what’s new. In the KeyDown event (We’re using the PreviewKeyDown in case the textboxes have focus) we use the normal use left, right, up and down to adjust the X and Y coordinates, but here we add Page Up and Down to adjust the Z coordinate.
And as usual in the KeyDown event, we set the relevant Tristate variable to positive or negative according to the key pressed. Then in the KeyUp event we set the Tristate back to zero.
When you run the application, you will see the rotating cubes similar to those in the second project from part 1, with a brown background. Now start pressing a few of the direction keys. You will notice that you can move left, right, up, down, in and out. What we are doing here is moving the camera around in our 3D mini world. However you will notice that we are always turning the vector to look directly at the rotating cubes. Well that’s okay for now, and a working copy of this project, with textboxes showing us the current X,Y and Z positions, is available in the downloads.
On the next page we are going to build our little 3D world and apply a few textures to the faces.