3D Animation in VB.NET Using Windows Presentation Foundation (WPF) – Part 2

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.

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read