# OpenTK - How to rotate object by given angle

I was using tutorial on http://viewport3d.com/trackball.htm to program trackball. I have axis and angle computed but because I am just beginner to OpenTK and OpenGL, I don't know how to apply rotation to object. Calling SetCamera method in glControl1_MouseUp with new theta angle doesnt work at all.

code consists of several files, here are two most important.

Trackball.cs

```using System;
using System.Windows.Forms;
using OpenTK;
using OpenTK.Graphics.OpenGL;

namespace _038trackball
{
public partial class Form1
{
#region Camera attributes

/// <summary>
/// Current camera position.
/// </summary>
private Vector3 eye = new Vector3( 0.0f, 0.0f, 10.0f );

/// <summary>
/// Current point to look at.
/// </summary>
private Vector3 pointAt = Vector3.Zero;

/// <summary>
/// Current "up" vector.
/// </summary>
private Vector3 up = Vector3.UnitY;

/// <summary>
/// Vertical field-of-view angle in radians.
/// </summary>
private float fov = 1.0f;

/// <summary>
/// Camera's far point.
/// </summary>
private float far = 200.0f;

#endregion

protected Vector3d v1 = new Vector3d(0, 0, 0);
protected Vector3d v2 = new Vector3d(0, 0, 0);
protected float theta = 0;
/// <summary>
/// Sets up a projective viewport
/// </summary>
private void SetupViewport ()
{
int width  = glControl1.Width;
int height = glControl1.Height;

// 1. set ViewPort transform:
GL.Viewport( 0, 0, width, height );

// 2. set projection matrix
GL.MatrixMode( MatrixMode.Projection );
Matrix4 proj = Matrix4.CreatePerspectiveFieldOfView( fov, (float)width / (float)height, 0.1f, far );
}

/// <summary>
/// Setup of a camera called for every frame prior to any rendering.
/// </summary>
private void SetCamera ()
{
// !!!{{ TODO: add camera setup here

SetupViewport();

GL.MatrixMode( MatrixMode.Modelview );
Matrix4 modelview = Matrix4.CreateTranslation(-center) *
Matrix4.Scale(1.0f / diameter) *
Matrix4.CreateTranslation(0.0f, 0.0f, -1.5f) *
Matrix4.CreateRotationX(theta) *
Matrix4.CreateRotationY(theta) *
Matrix4.CreateRotationZ(theta);

// !!!}}
}

private void ResetCamera ()
{
// !!!{{ TODO: add camera reset code here
// !!!}}

}

/// <summary>
/// Rendering of one frame.
/// </summary>
private void Render ()
{

frameCounter++;
GL.PolygonMode( MaterialFace.Front, PolygonMode.Fill );
GL.Enable( EnableCap.CullFace );

SetCamera();

RenderScene();

glControl1.SwapBuffers();
}

public void glControl1_MouseDown ( object sender, MouseEventArgs e )
{
Cursor.Current = Cursors.Hand;

double x = MousePosition.X * 1.0 / (glControl1.Width * 1.0 / 2);
double y = MousePosition.Y * 1.0 / (glControl1.Height * 1.0 / 2);
x = x - 1;
y = 1 - y;
double z2 = 1 - x * x - y * y;
double z = z2 > 0 ? Math.Sqrt(z2) : 0;
v1 = new Vector3d(x, y, z);
v1.Normalize();
}

private void glControl1_MouseUp ( object sender, MouseEventArgs e )
{
Cursor.Current = Cursors.Default;

double x = MousePosition.X * 1.0 / (glControl1.Width * 1.0 / 2);
double y = MousePosition.Y * 1.0 / (glControl1.Height * 1.0 / 2);
x = x - 1;
y = 1 - y;
double z2 = 1 - x * x - y * y;
double z = z2 > 0 ? Math.Sqrt(z2) : 0;
v2 = new Vector3d(x, y, z);
v2.Normalize();

Vector3d axis = Vector3d.Cross(v1, v2);
float theta = (float) Vector3d.CalculateAngle(v1, v2);

SetCamera();
}

private void glControl1_MouseMove ( object sender, MouseEventArgs e )
{
// !!!{{ TODO: add the event handler here
// !!!}}

}

private void glControl1_MouseWheel ( object sender, MouseEventArgs e )
{
// !!!{{ TODO: add the event handler here
// HINT: for most mouses, e.delta / 120 is the number of wheel clicks, +/- indicated the direction

// !!!}}
}

private void glControl1_KeyDown ( object sender, KeyEventArgs e )
{
// !!!{{ TODO: add the event handler here
// !!!}}
}

private void glControl1_KeyUp ( object sender, KeyEventArgs e )
{
// !!!{{ TODO: add the event handler here
// !!!}}
}

private void buttonReset_Click ( object sender, EventArgs e )
{
// !!!{{ TODO: add the event handler here

ResetCamera();

// !!!}}
}
}
}```

and Form1.cs

```using System;
using System.Drawing;
using System.IO;
using System.Windows.Forms;
using OpenTK;
using OpenTK.Graphics.OpenGL;
using Scene3D;

namespace _038trackball
{
public partial class Form1 : Form
{
/// <summary>
/// </summary>
protected SceneBrep scene = new SceneBrep();

/// <summary>
/// Scene center point.
/// </summary>
protected Vector3 center = Vector3.Zero;

/// <summary>
/// Scene diameter.
/// </summary>
protected float diameter = 3.5f;

/// <summary>
/// GLControl guard flag.
/// </summary>

/// <summary>
/// Are we allowed to use VBO?
/// </summary>
bool useVBO = true;

#region OpenGL globals

private uint[] VBOid = new uint[ 2 ];       // vertex array (colors, normals, coords), index array
private int stride = 0;                     // stride for vertex array

#endregion

#region FPS counter

long lastFpsTime = 0L;
int frameCounter = 0;
long triangleCounter = 0L;

#endregion

public Form1 ()
{
InitializeComponent();
}

private void glControl1_Load ( object sender, EventArgs e )
{

// OpenGL init code:
GL.ClearColor( Color.DarkBlue );
GL.Enable( EnableCap.DepthTest );

// VBO init:
GL.GenBuffers( 2, VBOid );
if ( GL.GetError() != ErrorCode.NoError )
useVBO = false;

SetupViewport();

Application.Idle += new EventHandler( Application_Idle );
comboTrackballType.SelectedIndex = 0;
}

private void glControl1_Resize ( object sender, EventArgs e )
{

SetupViewport();
glControl1.Invalidate();
}

private void glControl1_Paint ( object sender, PaintEventArgs e )
{
Render();
}

private void buttonOpen_Click ( object sender, EventArgs e )
{
OpenFileDialog ofd = new OpenFileDialog();

ofd.Title = "Open Scene File";
ofd.Filter = "Wavefront OBJ Files|*.obj" +
"|All scene types|*.obj";

ofd.FilterIndex = 1;
ofd.FileName = "";
if ( ofd.ShowDialog() != DialogResult.OK )
return;

scene.BuildCornerTable();
diameter = scene.GetDiameter( out center );
scene.GenerateColors( 12 );
ResetCamera();

//labelFile.Text = String.Format("{0}: {1} faces, {2}, {3}, {4}, {5}", ofd.SafeFileName, faces, theta, v1.X, x, y);
PrepareDataBuffers();
glControl1.Invalidate();
}

/// <summary>
/// Prepare VBO content and upload it to the GPU.
/// </summary>
private void PrepareDataBuffers ()
{
if ( useVBO &&
scene != null &&
scene.Triangles > 0 )
{
GL.EnableClientState( ArrayCap.VertexArray );
if ( scene.Normals > 0 )
GL.EnableClientState( ArrayCap.NormalArray );
GL.EnableClientState( ArrayCap.ColorArray );

// Vertex array: color [normal] coord
GL.BindBuffer( BufferTarget.ArrayBuffer, VBOid[ 0 ] );
int vertexBufferSize = scene.VertexBufferSize( true, false, true, true );
GL.BufferData( BufferTarget.ArrayBuffer, (IntPtr)vertexBufferSize, IntPtr.Zero, BufferUsageHint.StaticDraw );
IntPtr videoMemoryPtr = GL.MapBuffer( BufferTarget.ArrayBuffer, BufferAccess.WriteOnly );
unsafe
{
stride = scene.FillVertexBuffer( (float*)videoMemoryPtr.ToPointer(), true, false, true, true );
}
GL.UnmapBuffer( BufferTarget.ArrayBuffer );
GL.BindBuffer( BufferTarget.ArrayBuffer, 0 );

// Index buffer
GL.BindBuffer( BufferTarget.ElementArrayBuffer, VBOid[ 1 ] );
GL.BufferData( BufferTarget.ElementArrayBuffer, (IntPtr)(scene.Triangles * 3 * sizeof( uint )), IntPtr.Zero, BufferUsageHint.StaticDraw );
videoMemoryPtr = GL.MapBuffer( BufferTarget.ElementArrayBuffer, BufferAccess.WriteOnly );
unsafe
{
scene.FillIndexBuffer( (uint*)videoMemoryPtr.ToPointer() );
}
GL.UnmapBuffer( BufferTarget.ElementArrayBuffer );
GL.BindBuffer( BufferTarget.ElementArrayBuffer, 0 );
}
else
{
GL.DisableClientState( ArrayCap.VertexArray );
GL.DisableClientState( ArrayCap.NormalArray );
GL.DisableClientState( ArrayCap.ColorArray );

if ( useVBO )
{
GL.BindBuffer( BufferTarget.ArrayBuffer, VBOid[ 0 ] );
GL.BufferData( BufferTarget.ArrayBuffer, (IntPtr)0, IntPtr.Zero, BufferUsageHint.StaticDraw );
GL.BindBuffer( BufferTarget.ArrayBuffer, 0 );
GL.BindBuffer( BufferTarget.ElementArrayBuffer, VBOid[ 1 ] );
GL.BufferData( BufferTarget.ElementArrayBuffer, (IntPtr)0, IntPtr.Zero, BufferUsageHint.StaticDraw );
GL.BindBuffer( BufferTarget.ElementArrayBuffer, 0 );
}
}
}
}
}```