Introduction
The Open Toolkit is an advanced, low-level C# library that wraps OpenGL, OpenCL, and OpenAL. It is suitable for games, scientific applications, and any other project that requires 3D graphics, audio, or computing functionality. In short, it's called OpenTK.
Background
It is not a fast C# implementation of OpenGL, but it is the best. There are some previous ones like CSGL, TAO framework, etc., but they are incomplete and bound by some limitations. And now, they are completely unable to keep with .NET Framework.
Every OpenGL function, we can call by a GL class like glVertex2f(-2.0,-2.0)
of OpenGL used in OpenTk as GL.Vertex2(-2.0,-2.0)
. It has a very nice and flexible GUI option,
cross-platform GLControl (Windows.Forms) which can easily
be added in the Visual Studio toolbox, GLWidget which is another rich and useful component for (GTK#) and WPFControl
classes. And there is also a native, high-performance GameWindow which is designed specifically for games. You can develop a game faster than you think.
It also has a very useful API collection like 3D
Math Toolkit that supplies Vector, Matrix, Quaternion, and Bezier structs.
The Input API provides keyboard, mouse, and
joystick interfaces. The Display API
helps for multiple monitors. OpenTK.Compatibility
supports TAO framework applications.
And if you want to think about platform independency, it supports 32- and 64-bit versions of Windows, Linux, and Mac OS X. No need for mismanaged libraries – compile once, run everywhere.
And the great thing is that you are free to use, modify and redistribute the source code. It is suitable for open- and closed-source projects alike.
Using the code
You don’t need to have any prior knowledge in OpenGL or C# graphics to learn OpenTK. Only some basics of
Windows Form design is more than enough.
For using OpenTK, you need to add two DLLs in your Visual Studio reference. They are OpenTK.dll and OpenTK.Graphics.OpenGL.dll. Both can be found easily here.
We are not going to use the well known game window for OpenTK, as I guess you are familiar with Windows Form design. We are just using a normal Windows Form. OpenTK provides a very nice control/tool. For that, you need to add it in your Visual Studio toolbox, it is called Glcontrol
and you
can find it here. For that, first click
the toolbox and choose the item, then browse and then add the DLL for this control. Now you can use it.
To begin with, create a Form on which you will place your GLControl
. Right click in some empty space of the
toolbox, pick “Choose Items…”, and browse for OpenTK.GLControl.dll.
Make sure you can find the GLControl
listed in the .NET Framework Components, as in the image below.
Then you can add the GLControl
to your form as any .NET control. A GLControl
named glControl1
will be added to your Form
.
So, first add this control and named glControl1
in your port and write the below code. Also add this method in this control load event. For further details, you can go here.
private void glControl1_Resize(object sender, EventArgs e)
{
int w = glControl1.Width;
int h = glControl1.Height;
glControl1.MakeCurrent();
GL.MatrixMode(MatrixMode.Projection);
GL.LoadIdentity();
GL.ClearColor(Color.SkyBlue);
GL.Ortho(-w / 2, w / 2, -h / 2, h / 2, -1, 1);
GL.Viewport(0, 0, w, h);
GL.End();
glControl1.SwapBuffers();
}
If we run it, we will find a from like below:
So how is it done? It's easy. First, we see:
glControl1.MakeCurrent();
It makes all the next GL commands enabled for this control.
GL.MatrixMode(MatrixMode.Projection);
GL.LoadIdentity();
We make that matrix mode in projection and then load the identity. There are four types of modes for 2D projection, if you want to know more about these modes, you can see them here.
The next command is very much important to understand. For that, first we need to be good OpenGL citizens and setup an orthographic projection matrix using GL.Ortho()
. We need to call GL.Viewport()
also.
GL.Ortho(-w / 2, w / 2, -h / 2, h / 2, -1, 1);
This makes the center of the GL box 0,0 for axis x,y. Because w is the width and h is the height of the
GL box. If you want to make bottom-left corner pixel as 0,0, you can write:
GL.Ortho(0, w, 0, h, -1, 1)
GL.Viewport(0, 0, w, h);
Viewport
is used to select the painting area in the control.
GL.ClearColor(Color.SkyBlue);
and it makes it blue. For command disposal and window buffer, we have to write:
GL.End();
glControl1.SwapBuffers();
Now, at first, we have to draw a circle in our glcontrol
. For
the draw
method, we have to use the paint
method in
the paint
event in glcontrol
, like below:
private void glControl1_Paint(object sender, PaintEventArgs e)
{
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit)
drawclock();
glControl1.SwapBuffers();
}
void Draw_clock()
{
drawCircle(80);
Draw_digit();
}
For drawcircle
, I use the following code:
void drawCircle(float radius)
{
GL.Color3(Color.White);
GL.Begin(BeginMode.TriangleFan);
for (int i = 0; i < 360; i++)
{
double degInRad = i * 3.1416/180;
GL.Vertex2(Math.Cos(degInRad) * radius, Math.Sin(degInRad) * radius);
}
GL.End;
}
Now, it looks like it happens so easily.
GL.Begin(BeginMode.TriangleFan);
This mode draws a triangle and fills it with white color using the provided vertexes point.
All these triangles make the circle, like triangle pizza pieces make a circlular pizza .
If you want to know about GL.Begin
, see here. You must remember to write GL.End();
after GL.Begin();
otherwise nothing will happen and the compiler can't give you an error for that. Now we have to draw a digit and two lines for
the minute and hour.
Write simple code for a normal vertex operation like that. Remember that the radius of the circle is 80.
void Draw_digit()
{
GL.MatrixMode(MatrixMode.Projection);
GL.LoadIdentity();
GL.Color3(Color.Red);
GL.Begin(BeginMode.TriangleFan);
GL.Vertex2(0, +5);
GL.Vertex2(0, -5);
GL.Vertex2(70, 0);
GL.Vertex2(70, 0);
GL.Color3(Color.Red);
GL.End();
GL.Begin(BeginMode.TriangleFan);
GL.Vertex2(+5, 0);
GL.Vertex2(-5, 0);
GL.Vertex2(-65, 40);
GL.Vertex2(-65, 40);
GL.End();
GL.Color3(Color.Black);
GL.Begin(BeginMode.Lines);
GL.Vertex2(5, 60);
GL.Vertex2(5, 70);
GL.Vertex2(0, 60);
GL.Vertex2(0, 70);
GL.Vertex2(-5, 70);
GL.Vertex2(-15, 60);
GL.Vertex2(-15, 70);
GL.Vertex2(-5, 60);
GL.End();
GL.Color3(Color.Black);
GL.Begin(BeginMode.Lines);
GL.Vertex2(60,0);
GL.Vertex2(60,8);
GL.Vertex2(70,0);
GL.Vertex2(70,8);
GL.Vertex2(65, 0);
GL.Vertex2(65, 8);
GL.End();
GL.Color3(Color.Black);
GL.Begin(BeginMode.Lines);
GL.Vertex2(10, -60);
GL.Vertex2(10, -70);
GL.Vertex2(0, -60);
GL.Vertex2(0, -70);
GL.Vertex2(5, -60);
GL.Vertex2(0, -70);
GL.Vertex2(5, -60);
GL.Vertex2(0, -70);
GL.End();
GL.Color3(Color.Black);
GL.Begin(BeginMode.Lines);
GL.Vertex2(-75,-5);
GL.Vertex2(-75,-15);
GL.Vertex2( -70,-5);
GL.Vertex2(-60,-15);
GL.Vertex2(- 70,-15);
GL.Vertex2(-60,-5);
}
Here is code for connecting one vertex to another vertex. For details, you can
look at similar OpenGL functions here.
After drawing that, we see our output form is as shown below:
Now we have to create a timer event for that, so first add a timer select interval at 1000 for 1s, enable it, and add this event at timer tick:
private void timer1_Tick(object sender, EventArgs e)
{
glControl2.MakeCurrent();
PaintEventArgs p = null;
glControl2_Paint(sender,p);
GL.End();
}
Now the paint
method will be called after one second. You have to draw
a line for the second there, so the paint
event will be like:
private void glControl2_Paint(object sender, PaintEventArgs e)
{
glControl2.MakeCurrent();
GL.End();
glControl2.SwapBuffers();
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
Draw_clock();
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
GL.End();
glControl2.SwapBuffers();
drawsecond();
glControl2.SwapBuffers();
}
Here for drawing seconds, we need a static
variable. For changing the coordinate of
the seconds line vertex position, we take:
static int i = 0;
Now the drawsecond()
function:
void drawsecond()
{
GL.Color3(Color.Red);
GL.Begin(BeginMode.Quads);
GL.Vertex2(5, 0);
GL.Vertex2(-5, 5);
double degInRad = i * 3.1416 / 180;
GL.Vertex2(Math.Cos(degInRad) * 80, Math.Sin(degInRad) * 80);
GL.Vertex2(Math.Cos(degInRad) * 85, Math.Sin(degInRad) * 85);
i = i - 6;
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
GL.End();
}
Here the static
variable is subtracted for rotating clockwise and subtracted by 6 because 360/6
is equal to 60, which we need to round the circle.
So the seconds line is rotating now:
Points of interest
For C#, I am sure that you can't find anything better than OpenTK to replace OpenGL. But if you want to develop a really professional level game, I have to tell you that you are not in the right track. C++ is far better than C# for that. And if you directly want to do that in C#, use
DirectX or XNA.
History
- 23 December, 2010: Initial post.