Click here to Skip to main content
Click here to Skip to main content

An Analog Clock Design by openTk C#

By , 23 Dec 2010
 

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 compute functionality. In short, its called OpenTK.

Background

It is not the fast C# implementation of OpenGL, but it is the best. There are some previous ones like csgl, taoframework, etc., but they are incomplete and bound by some limitation. And now, they are completely unable to keep with .NET Framework.

Every OpenGL function we can call by 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 options Cross-platform GLControl (Windows.Forms) which can easily add in 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 supplies Vector, Matrix, Quaternion and Bezier structs. Input API provides Keyboard, Mouse and Joystick interfaces. Display API for multiple monitors. OpenTK.Compatibility supports Tao framework applications.

And if you want to think about platform in dependency, 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 gr8 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 from 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 well known gamewindow 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 find it here. For that, first click toolbox and Choose item, then browse and then add the DLL for this control. Now you can use it like here.

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.

Click to enlarge image

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:

undefined

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 matrixmode in projection and then load the identity. There are four types of modes, for 2d projection is perfect, if you want to know more about these modes, you can see them here.

Now, 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 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 draw method, we have to use paint method in 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);//80 is radius of the circle
 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 provided vertexes point, all these triangles made the circle, like triangle pizza pieces makes the circle pizza :).

If you want to know 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 minute and hour.

Write simple code of 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);
           //for hour
            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();

            //for minute
            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);

            //for draw digit III
            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);
            //for draw digit XII
            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);
            //for draw digit IV
            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);
            //for draw digit IX
            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 normal one vertex to another vertex connected code. For details, you can see openGL similar functions for that.

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 line for second there, so 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 draw second, we need a static variable. For changing the coordinate of seconds line vertex position, we take:

 static int i = 0;

Now 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 static variable is subtracted for rotating in clockwise and subtracted by 6 because 360/6 equal to 60 which we need to round the circle.

So the seconds line is rotating now:

Points of Interest

So 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 direct x or XNA.

History

  • 23rd December, 2010: Initial post

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

kdgupta87
Software Developer Rymes
Bangladesh Bangladesh
Kishor is an undergraduate Computer Science and Engineering graduate from Khulna University Of Engineering and Technology. Currently working as a software developer in Rymes. Before joining Rymes Nascenia IT he worked as a software developer at Nascenia IT and as a trainee software developer at Technocrats BD, where he worked in .Net technologies. He developed few softwares in .Net framework. A few of the interesting ones were about face replacement, and GUI based requirement elicitation tool, image converter, image processing and LAN monitoring system.he worked on Ruby on Rails. Currently working on android
http://kishordgupta.wordpress.com/
Follow on   Twitter

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
Hint: For improved responsiveness ensure Javascript is enabled and choose 'Normal' from the Layout dropdown and hit 'Update'.
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralMy vote of 4memberȘtefan Gabriel Muscalu18-Apr-12 0:08 
Questionwhere is the source code !!!memberhichem1474-Jan-12 2:42 
Generalnice article but...memberavc1012819-Mar-11 2:16 
QuestionWhere is the source code?memberZTransform3-Feb-11 4:01 
GeneralMy vote of 5membersajib522-Jan-11 6:32 
GeneralMy vote of 5memberGPUToaster24-Dec-10 1:08 
GeneralRe: My vote of 5memberkdgupta8724-Dec-10 4:41 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web04 | 2.6.130617.1 | Last Updated 23 Dec 2010
Article Copyright 2010 by kdgupta87
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid