3D Animation in VB.NET Using WPF - Part 1

WEBINAR:On-Demand

Desktop-as-a-Service Designed for Any Cloud ? Nutanix Frame

Introduction

In previous articles we worked exclusively with 2D objects; in this article we are going to start dealing with basic 3D objects. While many may be expecting a VB based 3D rendering engine, we will actually be using Microsoft's Windows Presentation Foundation (WPF) Subsystem that has been available to .NET since Framework 3.0.

While WPF is 3D object orientated, it unfortunately does not do 3D Wireframes out the box, and requires quite a bit of tinkering , so we are only going to do some theory basics on wireframes.

What is 3D?

Let's go simple; 3D uses 3 coordinates (X,Y,Z) to define the location of a single point in space. Everything that we've covered in previous articles only required two coordinates(X,Y) to place the items on screen. The additional coordinate that we are using is for the depth, or, in simpler terms, how far back the point is.

So: X, as usual, is how far left and right, Y is up and down. And Z is how far back or forward, as shown in this image.

Figure 1: The XYZ axis

Hmm, in the image it looks like Z is a diagonal direction from top left to bottom right. That is one of the problems with trying to display 3D objects on a 2D plain. The depth of the object simply gets lost.

Figure 2: The 3D wireframe looks like a square

In this image I've rendered a 3D wireframe cube, however it simply looks like a square. Now this is where 3D rendering comes into effect. Let's do a little quick experimenting. Below is a simple Image with some text. Read the first line, then move back from your screen about 1/2 meter (1 1/2 feet) or until you can still read it relatively clearly. Now look at the text on the right.

Figure 3: Near and far

The text on the right appears to stand alittle farther away from you than the text on the left. This is a simple example of rendering 3D objects on a 2D plain. The farther away from you the smaller the object appears.

So let's redraw our cube using this concept.

Figure 4: Redraw the cube

Now we can see the additional lines joining to a second square, but if you look at it just right, it does appear to be a wireframe cube.

So what have we learnt from this experiment. Well, the farther away from the object the smaller it appears to be. With that we can now say that in our rendering code where we convert our X,Y,Z coordinates to X,Y coordinates; we have to adjust X and Y according to Z. The farther back Z is, the closer the X,Y coordinates move to 0 (or the center of the viewpoint).

3D Planes

Now that we have a basic knowledge of coordinates, let's look at 3D planes. In the simplest form, a plane is a single 2D part of the 3D image, represented by 3 coordinates. I guess the best way to explain is with this picture.

Figure 5: A plane is a single 2D part of a 3D image

Here the 2D plane MNOP holds the coordinates ABC. AB&C are the corner coordinates of a triangle face of our 3D pyramid object. If we had to depict this plane onto a 2D surface, our view would be something like this.

Figure 6: Depicting a plane onto a 2D surface

Vectors

Simply put a Vector is a single point and a direction. Often the direction is best represented by a second set of coordinates. However the second set of coordinates is not used to calculate a length.

Textures

Texture is the colour or image placed on the object's surface, this could be one for the complete object, or even one for each face. The textures are placed over our wireframe and essentially give our object a skin and volume. There are many effects that can be used with textures to give each object different features. For the purpose of this article we will be using solid colours, so that surfaces that are not facing the viewpoint, or behind another object will not be visible.

Okay that's the basic's out of the way. On the next page we will do some code

3D Animation in VB.NET Using WPF - Part 1

WEBINAR:On-Demand

Desktop-as-a-Service Designed for Any Cloud ? Nutanix Frame

Our first project

You're still here, good. Lets get some code done.

Before we start, the form designer in WPF is XML based, and as such follows all of the rules of XML. However the code behind works the same as in any application. When you see code in the XML format it is code for the designer, and when you see VB it is in the code behind.

Start a new WPF application project, and open the Window1 XAML object. Copy and paste this code in the editor. (between the Grid tags) This code contains everything needed in XML format to create and render four coloured cubes.

```        <Viewport3D  Name="Viewport3D1" Margin="12,55,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,1,3 1,2,3  0,4,3 4,7,3  4,6,7 4,5,6  0,4,1 1,4,5  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>
<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,1,3 1,2,3  0,4,3 4,7,3  4,6,7 4,5,6  0,4,1 1,4,5  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>
<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,1,3 1,2,3  0,4,3 4,7,3  4,6,7 4,5,6  0,4,1 1,4,5  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>
<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,1,3 1,2,3  0,4,3 4,7,3  4,6,7 4,5,6  0,4,1 1,4,5  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>
</Viewport3D>
```

Now compile and run the application. You should get a view like the one below. Now here's something to try while the application is running: resize the form and make note of how the objects react. The image should resize with the form.

[WPF1.jpg]
Figure 7: Compile and run the application

But what have we done. In simple terms, we have created a 3D viewing portal (the canvas) and placed a few Geometric models into it (the cubes). For each cube, we start off by listing each of the coordinates for all of the corners (8) in our cube, in X(0),Y(0),Z(0) X(1),Y(1),Z(1) etc,etc. Note: The list can be comma or space separated. Thereafter, we have to build each of our triangles from these points in sets of 3. As we are building a simple cube each face is built from two triangles. ie the first face is triangle 0,1,3 and 1,2,3 (Note: The list can be comma or space separated.) What is very important here is that the points are listed in a Clockwise direction, returning to our plane image.

The points are listed in a Clockwise direction

In order for the skining of face ABC to be visible when looking at the object, on the plane MNOP the coordinates have to be listed in clockwise order A,B,C. If they are listed in reverse order, i.e., A,C,B the face will only be visible when viewing the pyramid from the back side, in which case the other faces will be rendered over it.

Great we got cubes! But this is boring, there's nothing happening. Before we get this object to move around, let's look a little closer at a few things. First we are going to look at the Viewports camera object.

In the above code there's this line :
PerspectiveCamera Position="-40,40,40" LookDirection="40,-40,-40 " UpDirection="0,0,1"

This code defines our views camera and the vector needed to place it. The vector or position is a coordinate in the format of X,Y,Z and the direction ,also a coordinate, relative to the current camera position. So let's play with this a little and see what we can do. First we are going to add a slider to the project, place the slider control code below into the designer, just after the viewport, and add the VB code to the project.

```        <Slider Height="22" Margin="12,12,12,0" Name="Slider1" VerticalAlignment="Top" Minimum="-40" Maximum="40" SmallChange="1" />
Private X As Double = -40
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 Sub Window1_Loaded(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles MyBase.Loaded
Viewport3D1.Camera = newpcam
newpcam.Position = Pos1
newpcam.LookDirection = Dir1
End Sub

Private Sub SetX(ByVal X As Double)
Pos1.X = X
Dir1.X = -X
newpcam.Position = Pos1
newpcam.LookDirection = Dir1
End Sub

Private Sub Slider1_ValueChanged(ByVal sender As System.Object, ByVal e As System.Windows.RoutedPropertyChangedEventArgs(Of System.Double))
SetX(Slider1.Value)
End Sub```

After adding the code, run the project again, and move the slider left and right. We now have a little animation action happening. Well, all we are really doing is moving the camera left and right in a straight line, while turning it to look at our cubes. A working project file is available in the download section, with a few additions.

Our Second Project

What we've done up to now has been relatively simple. Let's see how to animate the individual cubes. We are going to use the same XAML layout, but we are going to add to the VB code to accomplish this.

Copy and paste this code into our previous project.

```Imports System.Windows.Media.Media3D
Imports System.Windows.Media.Animation

Private Sub Window1_Loaded(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles MyBase.Loaded
Viewport3D1.Camera = newpcam
newpcam.Position = Pos1
newpcam.LookDirection = Dir1

Dim Cube_ani As New DoubleAnimation()
Cube_ani.Duration = TimeSpan.FromSeconds(3)
Cube_ani.From = 0
Cube_ani.To = 359
Cube_ani.RepeatBehavior = RepeatBehavior.Forever

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)
End Sub```

When you run this project, you should see the four cubes rotating in place. Cool! Now we getting somewhere. What we've now done is created a animation storyboard, DoubleAnimation. Our storyboard will create a double typed variable according to the start(From), end(To) and time(Duration) values that we give it. In this case we looking for an angle that will cause our object to rotate a full 360 degrees, so we give it a Start and End value of 0 and 359.

Something interesting about the storyboard is you only have to tell it how long you want the animation to last, in this case we set it to three seconds, i.e., the storyboard will step from 0 to 359 in three seconds. It will calculate the steps between depending on the elapsed time from the first frame, giving a smooth constant rotation regardless on the performance of the system.

Next we define our animation type, AxisAngleRotation. Here we set the animation to rotate around the Y axis, with a factor of 1. You can set any combination of axis you want with any factor to produce any effect you like. Just remember that setting the axis to (0,0,0) will result in no rotation at all.

The next step is applying our Animation Type to the Object we want to animate. At this time we also have to assign the center of the object that the animation will use to rotate around. What we've used here are the exact middles of the cubes so that they will appear to be spinning much like a wheel. We can assign different centers for different objects or even the same for several objects.

Last on the list we start the animation and assign it a storyboard. The animation will now start and because we set the storyboard to repeat forever: for as long at the application is running the cubes will spin on their axis. We could also place triggers in the code to specifically start and stop the animation at any time, or set it to step through it a limited number of times.

Realtime 3D rendering in Visual Basic was limited to what you could do with massive amounts of API's and Cross referencing lookup tables pre built into memory to help speed up calculations and rendering times, but as you can see, WPF has made it exceptionally easy to apply full 3D functionality into any VB application with a minimal amount of effort.

In the next article we going to build a small 3D world and see if we can move around in it.
Till then Happy 3D coding...

Richard Newcombe

Richard Newcombe has been involved in computers since the time of the Commodore 64. Today, he has excelled in programming, and designs. Richard is in his mid 30's and, if or when you looking for him look no further than his computer. Always willing to help and give advice where he can in regard to computer related subjects. At present he is working as a .NET 2008 Software Developer for Syncrony Web Services, South Africa.