Swift 3 and OpenGL
Nov 5, 2016
Learning Swift has long been in my to-do list. So, when I recently undertook the project to write a CityGML viewer for Mac (to be released soon), I decided the time to learn Swift was now. Fortuitously, Apple had just released Swift 3, and since I like shiny new things, I decided to learn Swift 3 directly.
However, I soon realised that Apple’s documentation is very spotty. Despite the new look of the documentation pages, almost all sample code and examples still use Objective-C. Moreover, interacting with low-level C APIs like OpenGL is more than a little complex, requiring several conversions and the use of pointer-like data types.
Thus, I hope to document a few of the issues that stumped me and how I solved them. In this post, I will explain how to draw OpenGL to an NSOpenGLView or to a custom NSView. For more info, see Apple’s documentation (which uses Objective-C). Heads up to this Stack Overflow question and answers for how this works with Swift 2.
The easiest way to draw OpenGL is to subclass
NSOpenGLView. In this way, most things are managed for you automatically. So, you probably need to set up a class like this:
Afterwards, create the
init?() method and set up the OpenGL pixel format, context and swap interval there.
Then, create the
prepareOpenGL() method and its callback. This will be called before the first render and is meant to initialise the state of OpenGL. In this example, we set up
CVDisplayLink so that a callback function is called to render every new frame.
Note the pointer types
UnsafeMutableRawPointer, and how
unsafeBitCast is used in order to convert the
UnsafeMutableRawPointer to an instance of the current class. This is necessary in order to call any methods.
renderFrame() method is called by the callback function in the
CVDisplayLink. There, you can use any OpenGL drawing functions.
The draw method should call
renderFrame() as well.
deinit() method should stop the display link.
Drawing to a custom NSView
Sometimes, NSOpenGLView is not good enough. For instance, NSOpenGLView doesn’t support context sharing, which is a problem if you want to create full screen applications. In those cases, you need to subclass
NSView instead. Apple provides this piece of sample code for that purpose, but it is also written in Objective-C and way out of date.
NSView is similar to the previous case. However, you need to keep your own
init? method can then be pretty much as before, but it is necessary to make it call you own method to initialise OpenGL (
setupDisplayLink()) and the display link. If you want context sharing, you probably want to create another
init? method that receives the context to share. Make sure to call
makeCurrentContext() on the OpenGL context.
The overriden methods to lock the focus and to draw the view should set the view of the context to itself.
Finally, the method to draw things (as called by
draw() or by the display link callback) should do as before, but also make sure to set the current OpenGL context.