Skip to main content
edited title
Link
200_success
  • 145.7k
  • 22
  • 191
  • 481

Is this OK Cocoa OpenGL design? What could I be doing better?game edit view

Tweeted twitter.com/#!/StackCodeReview/status/102872194853507073
edited tags
Link
epatel
  • 289
  • 1
  • 7
Source Link

Is this OK Cocoa OpenGL design? What could I be doing better?

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