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

Abstract

So this time we are looking at how to interact with objects in the 3D world. Once again we are going to be using a card game as a base, and again this article will only cover the basics. By the end of this article you will be able to interact with objects in a 3D world using the mouse.

Previously we did a simple 3D maze and moved around in it. That becomes boring after a while, so this time we will look at how to interact with objects in the 3D world. Once again we are going to be using a card game as a base, and again this article will only cover the basics. By the end of this article you will be able to interact with objects in a 3D world using the mouse.

Capturing the Mouse

Initially when I started this article, I was not too sure on how to properly capture the mouse and the VB.NET article used a secondary Click hotspot to locate where in the game board the mouse was clicked. With some help from the forum Guru's a good method was found. This relies on several API's.

    Private Declare Function ShowCursor Lib "user32" _
        (ByVal bShow As Long) As Long
    Private Declare Function SetCursorPos Lib "user32" _
        (ByVal x As Int32, ByVal Y As Int32) As Int32
    Private Declare Function GetCursorPos Lib "user32" _
        (ByRef lpPoint As PointAPI) As Boolean

Now let's look at how these API's help us with capturing the mouse. First, ShowCursor is an API used to show and hide the Mouse pointer in our applications form. Its use is reasonably simple. ShowCursor (True) will show the mouse pointer and ShowCursor (False) will hide the mouse pointer.

Next we look at GetCursorPos and SetCursorPos; these two API's allow us to get the current location of the mouse and to set the position of the mouse. The second part of this is very important, because if the mouse reaches the end of the form or window, we no longer receive movements for that direction. The Basics here is to set the mouse position in the middle of the form, and when it moves from this position, we process the distances moved, and then place it back in the middle. This allows us to have an infinitive movement in any direction.

So how do these all help and work in our application. Let's start by adding it to our Maze Project from the previous article. To start let's do our declarations first.

    Structure PointAPI
        Public X As Int32
        Public Y As Int32
    End Structure
    Private Showmouse As Boolean = True
    Private Declare Function ShowCursor Lib "user32" (ByVal bShow As Long) As Long
    Private Declare Function SetCursorPos Lib "user32" (ByVal X As Int32, ByVal Y As Int32) As Int32
    Private Declare Function GetCursorPos Lib "user32" (ByRef lpPoint As PointAPI) As Boolean

The Boolean Variable ShowMouse is for us to track the current visibility of the mouse pointer, by default the mouse is visible so we set this default.

Next we put in a few subs to handle some specific functions.

    Private Sub DOShowMouse()
        If Showmouse Then
            ShowCursor(True)
        Else
            ShowCursor(False)
            SetCenter()
        End If
    End Sub

    Private Sub SetCenter()
        Dim CPos As System.Windows.Point
        CPos.X = CInt(Me.Left + (Me.Width / 2))
        CPos.Y = CInt(Me.Top + (Me.Height / 2))
        SetCursorPos(CPos.X, CPos.Y)
    End Sub

These two subs handle the two functions explained previously. The DoShowMouse function handles the simple issue of switching the mouse on and off as we require it, and the SetCenter sub centralizes the mouse pointer on our form. Also you will notice that when we hide the cursor we call the sub to center the mouse; this ensures that we start with a 0 offset when it comes time to process the mouse movements.

Now all that is left to do is capture the mouse events; with the two following events we've added total Mouse capture functionality to our application.

    Private Sub Window1_PreviewMouseLeftButtonUp(ByVal sender As Object, ByVal e As System.Windows.Input.MouseButtonEventArgs) Handles Me.PreviewMouseLeftButtonUp
        Showmouse = Not (Showmouse)
        DOShowMouse()
    End Sub

    Private Sub Window1_PreviewMouseMove(ByVal sender As Object, ByVal e As System.Windows.Input.MouseEventArgs) Handles Me.PreviewMouseMove
        If Not Showmouse Then
            Dim position As PointAPI
            GetCursorPos(position)
            Dim pX As Double
            pX = position.X
            Dim pY As Double
            pY = position.Y

            Dim CPos As System.Windows.Point
            CPos.X = CInt(Me.Left + (Me.Width / 2))
            CPos.Y = CInt(Me.Top + (Me.Height / 2))
            SetCursorPos(CPos.X, CPos.Y)

            Location.Hangle += (CPos.X - position.X) * Steps
            Location.VAngle += (CPos.Y - position.Y) * Steps

        End If
    End Sub

The first event we're interested in is the Mouse button Left click. The premise is that when we want to interact with it, we click on the form and take control of the mouse. Clicking a second time will release the mouse.

The Next Event we're interested in is the Mouse Movement event. In this event we calculate the mouse movement from form center and apply it to our maze. In this case moving the mouse will alter the viewing angle in the maze.

NOTE: As described before, we're using the forms Preview events to capture mouse events regardless of what object on the form the mouse is sitting over.

Running the project (complete adjusted project in downloads section) will load the maze as usual; now move the mouse over the form and click once. The Pointer will disappear and now any mouse movement will rotate your view of the maze.

This is the first step to interacting with objects in a 3D space.
On the next page we are going to look at creating our own pointer in the 3D world and moving it around.

Project Five

In this Project we are going to create a 3D card world, much like the VB.NET article on object interaction. However that's where the similarities end.

First let's look at the graphics needed. In the Previous article the fronts and backs of each card were separate, however with what we've already learned about WPF skinning we know that the entire skin of one object needs to be a single image. So the Card Images are modified a little. We merge the back and front into a single image.

Merge the back and front into a single image Merge the back and front into a single image

From two separate Images to a single image

a single image

Remember that when skinning we select what portion of the image to use for each face. Once rendered the cards look like this.

Rendered Card

Now I'm sure your itching to see the code. Much of the code is the same as used in previous WPF articles, so I'm only going to cover what's new or changed.

The first thing we are going to look at is the new 3D pointer that we will be rendering. The pointer movement will actually only be in 2 dimensions, however the original keyboard movement keys will allow us to float around the board and look down over the cards.

We are using the code from page one of this article, with a few small changes. The first change is to move our pointer around with the mouse move event.

    Private Sub Window1_PreviewMouseMove(ByVal sender As Object, ByVal e As System.Windows.Input.MouseEventArgs) Handles Me.PreviewMouseMove
        If Not Showmouse Then
            Dim position As PointAPI
            GetCursorPos(position)
            Dim pX As Double
            pX = position.X
            Dim pY As Double
            pY = position.Y

            Dim CPos As System.Windows.Point
            CPos.X = CInt(Me.Left + (Me.Width / 2))
            CPos.Y = CInt(Me.Top + (Me.Height / 2))
            SetCursorPos(CPos.X, CPos.Y)
          
            PLocation.X += (CPos.X - position.X) * 0.005
            PLocation.Z += (CPos.Y - position.Y) * 0.005

            Dim transform As Transform3DGroup = New Transform3DGroup()
            Dim translateTrans As TranslateTransform3D = New TranslateTransform3D(PLocation.X, PHeight, PLocation.Z)
            transform.Children.Add(translateTrans)
            PGeometry.Transform = transform
            Dim tmpgeo As GeometryModel3D = CardGeometry(1)

        End If
    End Sub

The Best method to move an Object around in WPF appears to be applying a transformation to the object, so here we apply one to the Pointer object to move it according to how the mouse originally moved. Also we are only moving it on the X and Z axis.

Here we use a translate transform to move the home point of the pointer to where we want it. By simply giving it the target location of the object, WFP will move the object to where you request using any sort of animation or time frame required. Here we do not apply a time frame as we actually want the movement to mimic the mouse movement as much as possible.

Next we're going to use the Right click to switch in the mouse pointer and left click to interact with the cards on the playing field.

    Private Sub Window1_PreviewMouseRightButtonUp(ByVal sender As Object, ByVal e As System.Windows.Input.MouseButtonEventArgs) Handles Me.PreviewMouseRightButtonUp
        Showmouse = Not (Showmouse)
        DOShowMouse()
    End Sub

    Private Sub Window1_PreviewMouseLeftButtonDown(ByVal sender As Object, ByVal e As System.Windows.Input.MouseButtonEventArgs) Handles Me.PreviewMouseLeftButtonDown
        If Not Showmouse Then
            Dim tmp As Int16
            For tmp = 1 To 52
                With CardGeometry(tmp).Bounds
                    If .X < PLocation.X And .X + .SizeX > PLocation.X Then
                        If .Z < PLocation.Z And .Z + .SizeZ > PLocation.Z Then
                            Dim Cube_ani1 As New DoubleAnimation(0, 360, TimeSpan.FromSeconds(2))
                            Dim Cube_rot1 As New AxisAngleRotation3D()
                            Cube_rot1.Axis = New Vector3D(0, 0, 1)
                            Dim transform As Transform3DGroup = New Transform3DGroup()
                            Dim RotateTrans As RotateTransform3D = New RotateTransform3D(Cube_rot1, _
                                    New Point3D(.X + (.SizeX / 2), 0, .Z + (.SizeZ / 2)))
                            transform.Children.Add(RotateTrans)
                            CardGeometry(tmp).Transform = transform
                            Cube_rot1.BeginAnimation( AxisAngleRotation3D.AngleProperty, Cube_ani1)
                        End If
                    End If
                End With
            Next
        End If
    End Sub

The Right Click event is the same as the previous left click event and should not require any explanation.

The Left Click event however is where our interaction happens. Here we look at each card's location, comparing it to the current pointer location. However we only have to look at the X and Z axis as this is our working planes. Once we find the Card that our pointer is hovering over, we apply a transformation to it, however in this transform we apply a rotation transformation. This is so that a click on the card will cause the card to rotate to show what card it is.

Click to rotate

In Closing

As you can see here, Interaction with objects is still relatively easy, and it's also easy to completely take over the mouse and use it's movement within your animation. We also see how to use timed transforms to apply animation to an object under specific conditions.

Remember these projects are designed to give you the startup tools needed to start creating your own 3D worlds. You may notice that at the moment the code triggers a transform and moves on, essentially forgeting about it, so it is posible to retriger them multiple times.
Happy 3D card game writing.

 



Downloads

Comments

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

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Live Event Date: October 29, 2014 @ 11:00 a.m. ET / 8:00 a.m. PT Are you interested in building a cognitive application using the power of IBM Watson? Need a platform that provides speed and ease for rapidly deploying this application? Join Chris Madison, Watson Solution Architect, as he walks through the process of building a Watson powered application on IBM Bluemix. Chris will talk about the new Watson Services just released on IBM bluemix, but more importantly he will do a step by step cognitive …

  • Live Event Date: October 23, 2014 @ 12:00 p.m. ET / 9:00 a.m. PT Despite the current "virtualize everything" mentality, there are advantages to utilizing physical hardware for certain tasks. This is especially true for backups. In many cases, it is clearly in an organization's best interest to make use of physical, purpose-built backup appliances rather than relying on virtual backup software (VBA - Virtual Backup Appliances). Join us for this eSeminar to learn why physical appliances are preferable to …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds