An open-source, cross-platform gaming library for C# with bindings for OpenGL, OpenAL, GLFW and Lua.
Switch branches/tags
Clone or download
Type Name Latest commit message Commit time
Failed to load latest commit information.
Pencil.Gaming Made Lua obsolete Jul 16, 2017
.gitignore Remove and ignore monodevelop user prefs Nov 6, 2014
Pencil.Gaming.sln Update monodevelop project to use new output dir Nov 6, 2014 Remove mention of Lua from README Jul 16, 2017


Pencil.Gaming is a gaming library for C#, providing support for OpenGL, GLFW, OpenAL and Lua. It's a stable, cross-platform, open-source (some prefer the term "free") alternative to libraries like XNA, which has pretty much died now, OpenTK, which hasn't been updated for about a year, and SharpDX, which is not cross-platform. A feature that Pencil.Gaming has over most other C# gaming libraries, is that users do not need to install any redistributables besides Mono/.NET!

The OpenGL implementation is based on the OpenTK source code.

Functionality and stability


Platform OpenGL core OpenGL extensions GLFW OpenAL
Linux 64-bit Stable Stable Stable Stable
Linux 32-bit Stable Stable Stable Stable
Windows 64-bit Stable Stable Stable Stable
Windows 32-bit Stable Stable Stable Stable
Mac OS X Stable Stable Stable Stable


Platform OpenGL core OpenGL extensions GLFW OpenAL
Linux 64-bit Stable Stable Stable Stable
Linux 32-bit Stable Stable Broken Stable
Windows 64-bit Stable Stable Stable Stable
Windows 32-bit Stable Stable Presumed Stable Stable
Mac OS X Stable Stable Stable* Stable

*Both 32 and 64-bit versions provided for Mac OS X, but mono is realistically only available for 32-bit, so those are recommended.

Fixing the DllNotFoundException

When running a Pencil.Gaming application, it's unfortunately required that you set your .NET working directory to the directory of the application. Not doing so will result in a DllNotFoundException. This should probably be the first thing in your main function:

 Environment.CurrentDirectory = Path.GetDirectoryName(typeof(Program).Assembly.Location);

When using Mono, it's also quite important that you have the Pencil.Gaming.dll.config file in the same directory as Pencil.Gaming.dll.

Callback Example

For a quick example of the callbacks, such as the input, see:

Image loading utility

int image = GL.Utils.LoadImage("myfile.png"); // Works with multiple file formats
GL.BindTexture(TextureTarget.Texture2D, image);

  GL.TexCoord2(0f, 1f);
  GL.Vertex2(0.1f, 0.9f);
  GL.TexCoord2(0f, 0f);
  GL.Vertex2(0.1f, 0.1f);
  GL.TexCoord2(1f, 1f);
  GL.Vertex2(0.9f, 0.9f);
  GL.TexCoord2(1f, 0f);
  GL.Vertex2(0.9f, 0.1f);

GL.DeleteTextures(1, ref image);

Model loading utility


int modelVbo;
int indexVbo;
int numberOfIndices;

During program initialization


Vector4[] vertices;
Vector3[] normals;
Vector2[] texCoords;
int[] indices;
GL.Utils.LoadModel("model.obj", out vertices, out normals, out texCoords, out indices, false);

numberOfIndices = indices.Length;

GL.GenBuffers(1, out modelVbo);
GL.BindBuffer(BufferTarget.ArrayBuffer, modelVbo);
GL.BufferData(BufferTarget.ArrayBuffer, new IntPtr(vertices.Length * 4 * sizeof(float)), vertices, BufferUsageHint.StaticDraw);
GL.BindBuffer(BufferTarget.ArrayBuffer, 0);

GL.GenBuffers(1, out indexVbo);
GL.BindBuffer(BufferTarget.ElementArrayBuffer, indexVbo);
GL.BufferData(BufferTarget.ElementArrayBuffer, new IntPtr(indices.Length * sizeof(int)), indices, BufferUsageHint.StaticDraw);
GL.BindBuffer(BufferTarget.ElementArrayBuffer, 0);

In the draw function

// NOTE: This uses legacy OpenGL, just to fit in the readme...

GL.BindBuffer(BufferTarget.ArrayBuffer, modelVbo);
GL.BindBuffer(BufferTarget.ElementArrayBuffer, indexVbo);

GL.VertexPointer(4, VertexPointerType.Float, 4 * sizeof(float), 0);
GL.DrawElements(BeginMode.Triangles, numberOfIndices, DrawElementsType.UnsignedInt, 0);

GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
GL.BindBuffer(BufferTarget.ElementArrayBuffer, 0);


During cleanup

GL.DeleteBuffers(1, ref modelVbo);
GL.DeleteBuffers(1, ref indexVbo);

Sample usage (OpenAL)

Another utility is the AL.Utils.BufferFromWav utility, which is able to load wave files into an OpenAL buffer. Similarly, there's the AL.Utils.BufferFromOgg utility, allowing Ogg/Vorbis file loading.

uint buffer = AL.Utils.BufferFromWav("MyWaveFile.wav");
uint source;
AL.GenSources(1, out source);

AL.Source(source, ALSourcei.Buffer, (int) buffer);
AL.Source(source, ALSourceb.Looping, true);


// ...
// ...

// When cleaning up:
AL.DeleteSources(1, ref source);
AL.DeleteBuffers(1, ref buffer);

Other Resources