Simple Skeleton Framework for Cocoa OSX OpenGL application
I found over the web that even official document on Apple’s Developers Network is quite rich in the aspect many people over the web ask for basic tutorial how to jump into OpenGL programming on OSX/IOS platforms.
One of the reasons I presume it’s so is because even if documentation and the library is complete in many aspects details are quite.. disconnected. For that reason I decided to show small tutorial how to start with OpenGL programming on OSX platform.
It’s not a tutorial about OpenGL itself, I assume developer knows it very well or enough to continue on it’s own, yet is not very much familiar with general development skills required to target Apple’s platform.
It was my case when after more than a decade of sticking in Microsoft’s platform I decided to learn OSX/IOS programming.
With such personal experience I crafted this text carefully looking for Microsoft’s analogies to ease the learning path for developers coming from that ecosystem.
Initial requirements:
If you’re complete newbie to Apple’s software development ecosystem please mind that to continue with this tutorial you need:
- OSX platform (one of Mac computers)
Note: I played with Hackintosh some time ago and aside of legal part of the experience when we speak about graphics programming where it simply lacks of good drivers and it’s very unstable. If you started your Mac experience from such configuration I really recommend you to buy a Mac to continue. Honestly speaking, I started with Hackintosh virtual machine in times when I worked at Microsoft. Just for curiosity to check the competitive platform without the need of buying it and without alternatives that could help me evaluate it’s value proposition. I fell in love and purchased the real hardware and for the main topic of this article as said I really recommend to you to go and buy one too.
- Xcode tools – available for download from App Store (free of charge)
- Some additional OpenGL libraries which you may like to use (like glem for example)
Initializing OpenGL project
My approach to this tutorial is very raw, to keep it simple on those aspects that require integration with native Cocoa platform.
So our starting point is simply starting new app project, targeting OSX (Cocoa Application)
From that point you should have MainMenu.xib file which is Xml format to describe UI and it’s basic behavior. It’s quite comparable to Xaml as an idea. On the right pane’s bottom part you should have Object’s library. Select Data Views section to ease search for right view control and find OpenGL View into the window. Change the properties to let this view cover whole the area of the window, set the window size according to your initial expectation.
Creating Custom OpenGL view
Standard view still needs some massage to fit your individual requirement. For that custom Objective C class has to be created. In my example let’s call it OGLDemoView. Its interface and implementation code looks like follow:
<HEADER>
#import <Cocoa/Cocoa.h>
#include "tut01_renderer.h" // I'll explain this include later,
//shortly it's our pure C/C++ renderer
@interface OGLDemoView : NSOpenGLView
{
//system timer, needed to synchronize the frame rate
NSTimer* renderTimer;
//our C++ renderer as I aim to minimize //ObjectiveC footprint and use clean C/C++ only, if possible
tut01_renderer renderer;
}
//it's analogical to WM_PAINT event in Windows - (void) drawRect: (NSRect)bounds;
@end
<BODY>
#import "OGLDemoView.h"
@implementation OGLDemoView
- (id)initWithFrame:(NSRect)frame
{
self = [super initWithFrame:frame];
//below code helps optimize Open GL context // initialization for the best available resolution // important for Retina screens for example
if (self) {
[self wantsBestResolutionOpenGLSurface];
}
return self;
}
- (void)prepareOpenGL
{
// Synchronize buffer swaps with vertical refresh rate
GLint swapInt = 1;
[[self openGLContext]
setValues:&swapInt
forParameter:NSOpenGLCPSwapInterval];
renderer.init();
}
-(void)awakeFromNib
{
//when UI is created and properly initialized, // we set the timer to continual, real-time rendering
//a 1ms time interval renderTimer = [NSTimer timerWithTimeInterval:0.001 target:self selector:@selector(timerFired:) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:renderTimer forMode:NSDefaultRunLoopMode];
//Ensure timer fires during resize
[[NSRunLoop currentRunLoop]
addTimer:renderTimer
forMode:NSEventTrackingRunLoopMode];
}
// Timer callback method
- (void)timerFired:(id)sender
{
// it's the update routine for our C/C++ renderer
renderer.update();
//it sets the flag that windows has to be redrawn
[self setNeedsDisplay:YES]; }
// Each time window has to be redrawn, this method is called
- (void)drawRect:(NSRect)bounds
{
//below code sets the viewport of Open GL context into //correct size (assuming resize, fullscreen operations may trigger change)
NSRect backingBounds = [self convertRectToBacking:[self bounds]]; glViewport(0,0, backingBounds.size.width, backingBounds.size.height);
//our renderer's drawing routine
renderer.render();
}
@end
Having above code defined correctly in the project last step we need is to assign it as the class handling OpenGL view put on our window (right pane and correct properties can assign this class to the visual object).
Toggling Fullscreen
The easiest way to toggle our OpenGL application between fullscreen and windowed modes is explained by below code, which I put into my app delegate and assigned as menu item’s action:
- (IBAction)fullscreenToggled:(id)sender {
if (![self isFullscreen])
{
[self.view enterFullScreenMode:[NSScreen mainScreen]
withOptions: nil];
self.isFullscreen = true;
} else {
[self.view exitFullScreenModeWithOptions:nil]; self.isFullscreen = false; }
}
This simplified approach can be easily extended to cover all possible scenarios and with above implementation is far from complete but for certain circumstances works quite stable as base point for further investigation. One of the consequences of above implementation is the behavior when your app goes fullscreen and then will come back to windowed and you will manually resize the window. OpenGL rendering will stop working because your OpenGL context is not aware of all the circumstances for the change. I’m not covering that in this post. To keep it simple stupid you can window’s resizing by setting the same values for min/max width/heights and continue.
C/C++ renderer and coding continuation without ObjectiveC/Cocoa impact.
If your application from that point needs only to render 2d/3d images with OpenGL and that’s all you need to know from the platform perspective. Of course if you need input (keyboard/mouse) interaction and handling other events then fun continues.
If we want to continue with the rendering code from pure C/C++ perspective our next step is to build base class C++ class for our renderer:
class base_renderer
{
public:
virtual void init() = 0; virtual void render() = 0; virtual void update() = 0;
protected:
void clear(float r=0,
float g=0,
float b=0,
float a=1,
bool depth=true);
void flush();
};
Class is abstract so the only important (and base elements) are those which clear the buffers and flush, their Open GL code are represented below:
void base_renderer::clear(float r, float g, float b,
float a, bool depth)
{
glClearColor(r, g, b, a);
if (depth)
glClear(GL_COLOR_BUFFER_BIT);
}
void base_renderer::flush()
{
glFlush();
}
Now let’s see our tut01_renderer example which I used in the OGLDemoView code above. It’s not sophisticated and visually compelling but show the point how to make real-time renderer in Open GL that is bound to above basic infrastructure:
<HEADER>
class tut01_renderer : base_renderer
{
public:
virtual void init();
virtual void update();
virtual void render();
private:
float shift; float shift_direction;
void draw_triangles();
};
<BODY>
void tut01_renderer::init()
{
shift_direction = 1;
shift = 0.0f;
}
void tut01_renderer::update()
{
#define SHIFT_MOVE 0.005f
if (shift_direction==1)
{
shift +=SHIFT_MOVE;
if (shift>=1.0)
shift_direction = 0;
} else
{
shift -=SHIFT_MOVE;
if (shift<=0.0)
shift_direction = 1;
}
}
void tut01_renderer::render()
{
clear(); draw_triangles();
flush();
}
void tut01_renderer::draw_triangles()
{
glColor3f(1.0f, 0.85f, 0.35f);
glBegin(GL_TRIANGLES);
glVertex3f( -1.0+shift, 1.0, 0.0); glVertex3f( -1.0, -1.0, 0.0); glVertex3f( 1.0, -1.0 ,0.0);
glColor3f(1.0f, 0.0f, 0.35f);
glVertex3f( 1.0-shift, 1.0, 0.0); glVertex3f( -1.0, -1.0, 0.0); glVertex3f( 1.0, -1.0 ,0.0);
glEnd();
}
Summary
Above example is very simple and doesn’t cover all possible scenarios but to people who are familiar with OpenGL (or general 3d programming) and are starting with OSX development it should be enough to focus on learning Open GL and having basic skeletal framework on OSX already done.







