I'm working on a Cocoa project that uses OpenGL. I'm trying to keep things easily cross-platformable for later (which is the primary reason for my GL singleton; I hope to implement Linux versions of the IRGL methods that currently use NSOpenGL...). When doing Cocoa & OpenGL stuff, though, I'm never sure that I'm doing things the "right" way. What could I be doing better here? I've snipped out methods unrelated to GL or drawing.
Here is my NSView subclass:
//
// IRLevelViewView
// Iris
//
// Created by Andy Van Ness on 3/15/11.
// Copyright 2011 Andy Van Ness. All rights reserved.
//
#import "IRGameEditView.h"
#import <OpenGL/gl.h>
#import "IRGL.h"
//snip
@implementation IRGameEditView
- (id)initWithFrame:(NSRect)frameRect
{
self = [super initWithFrame:frameRect];
if (self != nil)
{
//snip
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(surfaceNeedsUpdate:) name:NSViewGlobalFrameDidChangeNotification object:self];
}
return self;
}
- (void)initDisplayLink
{
GLint swapInt = 1;
[[[IRGL gl] glContext] setValues:&swapInt forParameter:NSOpenGLCPSwapInterval];
CVDisplayLinkCreateWithActiveCGDisplays(&displayLink);
CVDisplayLinkSetOutputCallback(displayLink, &MyDisplayLinkCallback, self);
CGLContextObj cglContext = [[[IRGL gl] glContext] CGLContextObj];
CGLPixelFormatObj cglPixelFormat = [[NSOpenGLView defaultPixelFormat] CGLPixelFormatObj];
CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext(displayLink, cglContext, cglPixelFormat);
CVDisplayLinkStart(displayLink);
}
- (void)initGL
{
glDisable(GL_DEPTH_TEST);
glDepthMask(GL_FALSE);
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_BLEND);
[[IRGL gl] initShaders];
[self reshape];
[self setReadyForGL:YES];
}
static CVReturn MyDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp* now, const CVTimeStamp* outputTime, CVOptionFlags flagsIn, CVOptionFlags* flagsOut, void* displayLinkContext)
{
NSAutoreleasePool* pool = [NSAutoreleasePool new];
[(id)displayLinkContext setNeedsDisplay:YES];
[pool drain];
return kCVReturnSuccess;
}
- (void)lockFocus
{
[super lockFocus];
[[IRGL gl] activateContextForView:self];
}
- (void)drawRect:(NSRect)dirtyRect
{
if (![self isReadyForGL]) [self initGL];
glClearColor(0.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
[[self clock] updateTime];
CGFloat animator = (CGFloat)([[self clock] time] % 100000)/100000.0;
[[IRGL gl] setCurrentShader:IRIrisShader];
[[IRGL gl] setUniformGLVariable:@"animator" toFloat:animator];
[[IRGL gl] setUniformGLVariable:@"cameraCenter" toPoint:[[self camera] center]];
[[IRGL gl] setUniformGLVariable:@"cameraTileAspectRatio" toFloat:[[self camera] tileAspectRatio]];
[[IRGL gl] setUniformGLVariable:@"cameraZoom" toFloat:[[self camera] zoom]];
[[IRGL gl] setUniformGLVariable:@"cameraSize" toSize:[[self camera] size]];
[[self camera] loadCameraMatrix];
@synchronized([[self level] tileMap])
{
for (IRTileStack* currentStack in [[[self level] tileMap] tileStacksInIrisRect:[[self camera] irisFrame]])
{
[currentStack draw];
}
}
glFlush();
}
- (void)reshape
{
glViewport(0, 0, (GLsizei)[self frame].size.width, (GLsizei)[self frame].size.height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, [self frame].size.width, [self frame].size.height, 0, -2, 0);
glMatrixMode(GL_MODELVIEW);
}
- (BOOL)isOpaque { return YES; }
- (BOOL)isFlipped { return YES; }
- (void)setFrame:(NSRect)frameRect
{
[super setFrame:frameRect];
[[IRGL gl] activateContextForView:self];
[self reshape];
[self setNeedsDisplay:YES];
}
@synthesize automaticallyRedraws;
- (void)setAutomaticallyRedraws:(BOOL)newAutomaticallyRedraws
{
if (newAutomaticallyRedraws != automaticallyRedraws)
{
automaticallyRedraws = newAutomaticallyRedraws;
if (automaticallyRedraws)
{
[self initDisplayLink];
}
else
{
CVDisplayLinkRelease(displayLink);
}
}
}
- (void)surfaceNeedsUpdate:(NSNotification*)notification
{
[[[IRGL gl] glContext] update];
}
//snip
@end
And here is a singleton GL class:
//
// IRGL.m
// Iris
//
// Created by Andy Van Ness on 8/9/11.
// Copyright 2011 Andy Van Ness. All rights reserved.
//
#import "IRGL.h"
@implementation IRGL
//snip
@synthesize currentShader;
- (void)setCurrentShader:(NSString *)newCurrentShader
{
if (![currentShader isEqualToString:newCurrentShader])
{
[currentShader release];
currentShader = [newCurrentShader copy];
}
if (!newCurrentShader)
{
glUseProgramObjectARB(0);
}
else
{
glUseProgramObjectARB([self shaderForKey:currentShader]);
}
}
- (void)initShaders
{
for (NSString* currentID in [NSArray arrayWithObjects:IRAllShaders])
{
[self addShaderWithID:currentID];
}
[self setInitialized:YES];
}
- (void)addShaderWithID:(NSString*)key
{
NSString* shaderSource = [NSString stringWithContentsOfFile:[[NSBundle mainBundle] pathForResource:key ofType:@"fs"] encoding:NSUTF8StringEncoding error:nil];
if ([shaderSource length] > 0)
{
GLhandleARB shaderProgram = glCreateProgramObjectARB();
GLchar const* source = [shaderSource UTF8String];
GLint const length = [shaderSource length];
GLhandleARB shader = glCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB);
glShaderSourceARB(shader, 1, &source, &length);
glCompileShaderARB(shader);
glAttachObjectARB(shaderProgram, shader);
glLinkProgramARB(shaderProgram);
MOGLShaderInfoLog(shaderProgram);
[[self shaders] setObject:[NSValue valueWithPointer:shaderProgram] forKey:key];
[[self uniformVariablesByShader] setObject:[NSMutableDictionary dictionary] forKey:key];
}
else
{
MOLogWarning(@"Shader not found.");
}
}
- (GLhandleARB)shaderForKey:(NSString *)shaderKey
{
if (shaderKey == nil) return 0;
NSValue* shaderValue = [[self shaders] objectForKey:shaderKey];
if (!shaderValue) MOLogError(@"Shader %@ not found.",shaderKey);
return [shaderValue pointerValue];
}
- (GLint)uniformLocationForName:(NSString*)varName
{
NSNumber* uniformLocation = [[[self uniformVariablesByShader] objectForKey:[self currentShader]] objectForKey:varName];
if (!uniformLocation)
{
GLint var = glGetUniformLocationARB([self shaderForKey:[self currentShader]],[varName UTF8String]);
uniformLocation = [NSNumber numberWithInteger:var];
MOGLError();
if (var == -1)
{
MOLogError(@"Uniform %@ could not be found.",varName);
}
else
{
[[[self uniformVariablesByShader] objectForKey:[self currentShader]] setObject:uniformLocation forKey:varName];
}
}
return (GLint)[uniformLocation integerValue];
}
- (void)setUniformGLVariable:(NSString*)varName toFloat:(CGFloat)varValue
{
if ([self isInitialized])
{
glUniform1f([self uniformLocationForName:varName], varValue);
MOGLError();
}
}
//snip; bunches more methods like that one
- (void)activateContext
{
if (![self glContext])
{
NSOpenGLContext* newContext = [[NSOpenGLContext alloc] initWithFormat:[NSOpenGLView defaultPixelFormat] shareContext:nil];
[self setGLContext:newContext];
[newContext release];
MOGLError();
}
[[self glContext] makeCurrentContext];
MOGLError();
}
- (void)activateContextForView:(NSView*)view
{
[self activateContext];
[[self glContext] setView:view];
}
@end