Basic OpenGL Cocoa App using C

Alec Jacobson

September 22, 2011

weblog/

I'm rewriting this tutorial for Xcode 4 on Mac OS X 10.6. For whatever reason when I tried it on my computer it gave compilation errors. The only thing I change is the code inside glView.mm and glView.h
- create a new Cocoa Application - ceate glView.h/glView.m - copy and paste in this code and save files - add frameworks: QuartzCore and OpenGL - open up MainMenu.xib - drag Custom View into main window and resize to fill window - set the autosizing of view to have the two inner arrows on so it resizes automatically with the window - set the class of the Custom View to be glView - save nib - go back to Xcode and run project Should show a yellow window. Here's the code:
glView.h
#import <Cocoa/Cocoa.h>

// for display link
#import <QuartzCore/QuartzCore.h>

@interface glView : NSOpenGLView
{
  CVDisplayLinkRef displayLink;
  
  double    deltaTime;
  double    outputTime;
  float    viewWidth;
  float    viewHeight;
}

@end

glView.mm
#import "glView.h"

@interface glView (InternalMethods)

- (CVReturn)getFrameForTime:(const CVTimeStamp *)outputTime;
- (void)drawFrame;

@end

@implementation glView

#pragma mark -
#pragma mark Display Link

static CVReturn MyDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp *now,
                                      const CVTimeStamp *outputTime, CVOptionFlags flagsIn,
                                      CVOptionFlags *flagsOut, void *displayLinkContext)
{
  // go back to Obj-C for easy access to instance variables
  CVReturn result = [(glView *)displayLinkContext getFrameForTime:outputTime];
  return result;
}

- (CVReturn)getFrameForTime:(const CVTimeStamp *)outputTime
{
  // deltaTime is unused in this bare bones demo, but here's how to calculate it using display link info
  deltaTime = 1.0 / (outputTime->rateScalar * (double)outputTime->videoTimeScale / (double)outputTime->videoRefreshPeriod);
  
  [self drawFrame];
  
  return kCVReturnSuccess;
}

- (void)dealloc
{
  CVDisplayLinkRelease(displayLink);
  
  [super dealloc];
}

- (id)initWithFrame:(NSRect)frameRect
{
  // context setup
  NSOpenGLPixelFormat        *windowedPixelFormat;
  NSOpenGLPixelFormatAttribute    attribs[] = {
    NSOpenGLPFAWindow,
    NSOpenGLPFAColorSize, 32,
    NSOpenGLPFAAccelerated,
    NSOpenGLPFADoubleBuffer,
    NSOpenGLPFASingleRenderer,
    0 };
  
  windowedPixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attribs];
  if (windowedPixelFormat == nil)
  {
    NSLog(@"Unable to create windowed pixel format.");
    exit(0);
  }
  self = [super initWithFrame:frameRect pixelFormat:windowedPixelFormat];
  if (self == nil)
  {
    NSLog(@"Unable to create a windowed OpenGL context.");
    exit(0);
  }
  [windowedPixelFormat release];
  
  // set synch to VBL to eliminate tearing
  GLint    vblSynch = 1;
  [[self openGLContext] setValues:&vblSynch forParameter:NSOpenGLCPSwapInterval];
  
  // set up the display link
  CVDisplayLinkCreateWithActiveCGDisplays(&displayLink);
  CVDisplayLinkSetOutputCallback(displayLink, MyDisplayLinkCallback, self);
  CGLContextObj cglContext = (CGLContextObj)[[self openGLContext] CGLContextObj];
  CGLPixelFormatObj cglPixelFormat = (CGLPixelFormatObj)[[self pixelFormat] CGLPixelFormatObj];
  CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext(displayLink, cglContext, cglPixelFormat);
  
  return self;
}

- (void)awakeFromNib
{
  NSSize    viewBounds = [self bounds].size;
  viewWidth = viewBounds.width;
  viewHeight = viewBounds.height;
  
  // activate the display link
  CVDisplayLinkStart(displayLink);
}

- (void)reshape
{
  NSSize    viewBounds = [self bounds].size;
  viewWidth = viewBounds.width;
  viewHeight = viewBounds.height;
  
  NSOpenGLContext    *currentContext = [self openGLContext];
  [currentContext makeCurrentContext];
  
  // remember to lock the context before we touch it since display link is threaded
  CGLLockContext((CGLContextObj)[currentContext CGLContextObj]);
  
  // let the context know we've changed size
  [[self openGLContext] update];
  
  CGLUnlockContext((CGLContextObj)[currentContext CGLContextObj]);
}

- (void)drawRect:(NSRect)rect
{
  [self drawFrame];
}

- (void)drawFrame
{
  NSOpenGLContext    *currentContext = [self openGLContext];
  [currentContext makeCurrentContext];
  
  // must lock GL context because display link is threaded
  CGLLockContext((CGLContextObj)[currentContext CGLContextObj]);
  
  glViewport(0, 0, viewWidth, viewHeight);
  
  // Draw something that changes over time to prove to yourself that it's really updating in a tight loop
  glClearColor(
    sin(CFAbsoluteTimeGetCurrent()),
    sin(7.0*CFAbsoluteTimeGetCurrent()),
    sin(CFAbsoluteTimeGetCurrent()/3.0),0);
  glClear(GL_COLOR_BUFFER_BIT);
  
  // draw here
  
  [currentContext flushBuffer];
  
  CGLUnlockContext((CGLContextObj)[currentContext CGLContextObj]);
}

@end