Monday, October 26, 2009

Adding Gestures to OpenGL application

In my last post I created a spinning 3DCube using MonoTouch which was a big step for me into the area of graphics. This time I want to try to control the objects rotation using gestures. Simply put, I want the default behavior to be a spinning cube, but if I press the screen the spinning should stop and I should be able to move the view manually using my finger.

Starting and stopping the animation

First need to make the View aware of the Touch events by adding the Touch Began and TouchEnded events. Edit the Cube3DView.cs class and add the event delegates.

[MonoTouch.Foundation.Export("touchesBegan:withEvent:")]

public override void TouchesBegan (MonoTouch.Foundation.NSSet touches, UIEvent evt)

{

Console.WriteLine("Click began");

StopAnimation();

}

[MonoTouch.Foundation.Export("touchesEnded:withEvent:")]

public override void TouchesEnded (MonoTouch.Foundation.NSSet touches, UIEvent evt)

{

Console.WriteLine("Click ended");

StartAnimation();

}

When doing this I noticed that there was a bug in the original code, setting the AnimationTimer to null did not stop the image rotation. So I had to invalidate the timer altogether, I’m not sure why at this stage but I’ll get back to it…

public void StopAnimation ()

{

AnimationTimer = null;

AnimationTimer.Invalidate ();

}


Changing the orientation with a finger

At first this seemed like a simple step to just change the orientation angle of the rotation but as you’ll see it’s never quite as easy at that. First off I needed to find the point on the view where we start the touch and the point where the finger was taken off the screen. This can then be passed back into the rotation function.

First I needed to add some extra code to store the X and Y coordinates. In the Code3DView.cs file we add two private variables.

PointF location; // global for drag

PointF _startLocation; // global for tourch start

Now we add come code to the TouchBegin and TouchEnd methods, we also add a new TouchMoved method. I’ve highlighted the code changes below.

[MonoTouch.Foundation.Export("touchesBegan:withEvent:")]

public override void TouchesBegan (MonoTouch.Foundation.NSSet touches, UIEvent evt)

{

var touch = (UITouch) evt.TouchesForView (this).AnyObject;

startLocation = touch.LocationInView(this);

StopAnimation ();

}


[MonoTouch.Foundation.Export("touchesMoved:withEvent:")]
public override void TouchesMoved (MonoTouch.Foundation.NSSet touches, UIEvent evt)
{
var touch = (UITouch) evt.TouchesForView (this).AnyObject;
location = this.Frame.Location;
location.X = touch.LocationInView(this).X;
location.Y = touch.LocationInView(this).Y;
}

[MonoTouch.Foundation.Export("touchesEnded:withEvent:")]
public override void TouchesEnded (MonoTouch.Foundation.NSSet touches, UIEvent evt)
{
float angle = TrigonometryHelper.Angle(startLocation.X, _startLocation.Y, _location.X, _location.Y);
GL.Rotate (angle, 1.0f, 1.0f, 1.0f);
StartAnimation ();
}

You’ll see that I had to add a new helper class to the project called TrigonometryHelper this is a simple set of code to help me calculate some of the values. Google is your friend when it comes down to this type of thing. Create a new empty class called TrigonometryHelper.cs and past in the following code.

using System;
namespace DCudeDemo
{
public static class TrigonometryHelper
{
public static float Angle(double px1, double py1, double px2, double py2)
{
// Negate X and Y values
double pxRes = px2 - px1;
double pyRes = py2 - py1;
double angle = 0.0;

// Calculate the angle
if (pxRes == 0.0)
{
if (pxRes == 0.0)
angle = 0.0;
else if (pyRes > 0.0)
angle = System.Math.PI / 2.0;
else
angle = System.Math.PI * 3.0 / 2.0;
}
else if (pyRes == 0.0)
{
if (pxRes > 0.0)
angle = 0.0;
else
angle = System.Math.PI;
}
else
{
if (pxRes < 0.0)
angle = System.Math.Atan(pyRes / pxRes) + System.Math.PI;
else if (pyRes < 0.0)
angle = System.Math.Atan(pyRes / pxRes) + (2 * System.Math.PI);
else
angle = System.Math.Atan(pyRes / pxRes);
}
// Convert to degrees
angle = angle * 180 / System.Math.PI;
return float.Parse(angle.ToString());
}
}
}

This is a class that I’m sure will begin to expand in functionality later.


Compiling and running the application now allows you to change the orientation of the cube with your finger.