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.

Comments
There are no comments yet. Be the first to comment!