Skip to content

Instantly share code, notes, and snippets.

@davepkennedy
Created September 24, 2016 07:57
Show Gist options
  • Save davepkennedy/75f0e4c53edc10e31fd4d7fe0ebebbbf to your computer and use it in GitHub Desktop.
Save davepkennedy/75f0e4c53edc10e31fd4d7fe0ebebbbf to your computer and use it in GitHub Desktop.
//
// GLView.h
//
// Created by Dave Kennedy on 11/09/2016.
//
//
#import <Cocoa/Cocoa.h>
#import <GLKit/GLKit.h>
@interface GLView : NSView
@property (readonly) NSString* appName;
@property (readonly) NSString* title;
@property (readonly) NSSet* activeKeys;
- (void) setup;
- (void) resized:(NSSize) size;
- (void) renderDisplay;
- (NSData*) loadResourceData:(NSString*) name;
- (NSString*) loadResourceString:(NSString*) name;
- (GLuint) createShader:(GLenum) shaderType
withSource:(const char*) source;
- (GLuint) loadShader:(GLenum) shaderType
named:(NSString*) name;
- (void) checkShaderCompiled: (GLuint) shaderIndex;
- (void) printLogForShader:(GLuint) shaderIndex;
- (void) checkProgramLinked:(GLuint) program;
- (void) printLogForProgram:(GLuint) program;
@end
//
// GLView.m
//
// Created by Dave Kennedy on 11/09/2016.
//
//
#import "GLView.h"
@implementation GLView {
CVDisplayLinkRef _displayLink;
NSOpenGLContext* _context;
NSMutableSet* _activeKeys;
double _previousSeconds;
int _frameCount;
}
@synthesize activeKeys = _activeKeys;
- (BOOL) acceptsFirstResponder {
return YES;
}
- (NSOpenGLPixelFormat*) pixelFormat {
NSOpenGLPixelFormatAttribute attribs[] =
{
NSOpenGLPFADoubleBuffer,
NSOpenGLPFAAllowOfflineRenderers, // lets OpenGL know this context is offline renderer aware
NSOpenGLPFAMultisample, 1,
NSOpenGLPFASampleBuffers, 1,
NSOpenGLPFASamples, 4,
NSOpenGLPFAColorSize, 32,
NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core, // Core Profile is the future
0
};
NSOpenGLPixelFormat *pf = [[NSOpenGLPixelFormat alloc] initWithAttributes:attribs];
if(!pf)
{
NSLog(@"Failed to create pixel format.");
return nil;
}
return pf;
}
- (NSOpenGLContext*) makeContext {
NSOpenGLContext* context = [[NSOpenGLContext alloc] initWithFormat:[self pixelFormat] shareContext:nil];
CGLEnable([context CGLContextObj], kCGLCECrashOnRemovedFunctions);
return context;
}
void MyDisplayReconfigurationCallBack(CGDirectDisplayID display,
CGDisplayChangeSummaryFlags flags,
void *userInfo)
{
if (flags & kCGDisplaySetModeFlag)
{
[((__bridge GLView *)userInfo) update];
}
}
static CVReturn MyDisplayLinkCallback(CVDisplayLinkRef displayLink,
const CVTimeStamp* now, // Current time
const CVTimeStamp* outputTime, // Displayed at time
CVOptionFlags flagsIn, // unused
CVOptionFlags* flagsOut, // unused
void* displayLinkContext) // Context object
{
GLView* delegate = (__bridge GLView*)displayLinkContext;
[delegate render];
return kCVReturnSuccess;
}
- (void) initNotifications {
CVDisplayLinkCreateWithActiveCGDisplays(&_displayLink);
CVDisplayLinkSetOutputCallback(_displayLink, &MyDisplayLinkCallback, (__bridge void * _Nullable)(self));
CVDisplayLinkStart(_displayLink);
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(surfaceNeedsUpdate:)
name:NSViewGlobalFrameDidChangeNotification
object:self.window.contentView];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(surfaceNeedsUpdate:)
name:NSWindowDidResizeNotification
object:self.window];
CGDisplayRegisterReconfigurationCallback(MyDisplayReconfigurationCallBack, (__bridge void * _Nullable)(self));
}
- (void)applicationWillTerminate:(NSNotification *)aNotification {
// Insert code here to tear down your application
CVDisplayLinkStop(_displayLink);
CVDisplayLinkRelease(_displayLink);
}
- (void) surfaceNeedsUpdate:(NSNotification*) notification {
[self update];
}
- (void) update {
[_context update];
[self resized:self.bounds.size];
}
- (void) updateFrameCounter {
double current_seconds = CFAbsoluteTimeGetCurrent();
double elapsedSeconds = current_seconds - _previousSeconds;
if (elapsedSeconds > 0.25) {
_previousSeconds = current_seconds;
double fps = (double)_frameCount / elapsedSeconds;
_frameCount = 0;
NSString* title = [NSString stringWithFormat:@"%@::%@: opengl @ fps %.2f", self.appName, self.title, fps];
dispatch_async(dispatch_get_main_queue(), ^{
self.window.title = title;
});
}
_frameCount++;
}
- (void) render {
if (!_context.view) {
_context.view = self;
}
[_context makeCurrentContext];
CGLLockContext([_context CGLContextObj]);
[self renderDisplay];
CGLFlushDrawable([_context CGLContextObj]);
CGLUnlockContext([_context CGLContextObj]);
[self updateFrameCounter];
}
- (void)drawRect:(NSRect)dirtyRect {
[super drawRect:dirtyRect];
// Drawing code here.
}
- (void) awakeFromNib {
_previousSeconds = CFAbsoluteTimeGetCurrent();
_frameCount = 0;
_activeKeys = [NSMutableSet set];
_context = [self makeContext];
[_context makeCurrentContext];
[self initNotifications];
[self setup];
[self update];
}
- (void) keyDown:(NSEvent *)event {
if (!event.ARepeat) {
[_activeKeys addObject:[NSNumber numberWithInteger:[event keyCode]]];
NSLog(@"Active keys: %@", _activeKeys);
}
}
- (void) keyUp:(NSEvent *)event {
[_activeKeys removeObject:[NSNumber numberWithInteger:[event keyCode]]];
NSLog(@"Active keys: %@", _activeKeys);
}
- (NSData*) loadResourceData:(NSString*) name {
NSError* error;
NSString* resourceName = [[name lastPathComponent] stringByDeletingPathExtension];
NSString* resourceType = [name pathExtension];
NSBundle* bundle = [NSBundle mainBundle];
NSURL* url = [bundle URLForResource:resourceName withExtension:resourceType];
NSData* data = [NSData dataWithContentsOfURL:url options:NSDataReadingUncached error:&error];
if (error) {
NSLog(@"loadResourceData error: %@", error);
}
return data;
}
- (NSString*) loadResourceString:(NSString*) name {
return [[NSString alloc] initWithData:[self loadResourceData:name] encoding:NSUTF8StringEncoding];
}
- (GLuint) createShader:(GLenum) shaderType
withSource:(const char*) source
{
GLuint shader = glCreateShader(shaderType);
glShaderSource(shader, 1, &source, NULL);
glCompileShader(shader);
[self checkShaderCompiled:shader];
return shader;
}
- (GLuint) loadShader:(GLenum) shaderType
named:(NSString*) name
{
return [self createShader:shaderType withSource:[[self loadResourceString:name] UTF8String]];
}
- (void) checkShaderCompiled: (GLuint) shaderIndex
{
int params = -1;
glGetShaderiv(shaderIndex, GL_COMPILE_STATUS, &params);
if (GL_TRUE != params) {
NSLog(@"GL shader index %i did not compile", shaderIndex);
[self printLogForShader:shaderIndex];
[NSApp terminate:self];
}
}
- (void) printLogForShader:(GLuint) shaderIndex
{
int maxLength = 2048;
int actualLength = 0;
char log[2048];
glGetShaderInfoLog(shaderIndex, maxLength, &actualLength, log);
NSLog(@"Shader info log for shader %i:\n%s", shaderIndex, log);
}
- (void) checkProgramLinked:(GLuint) program
{
int params = -1;
glGetProgramiv(program, GL_LINK_STATUS, &params);
if (GL_TRUE != params) {
NSLog(@"Couldn't link shader programme %u", program);
[self printLogForProgram:program];
[NSApp terminate:self];
}
}
- (void) printLogForProgram:(GLuint) program
{
int maxLength = 2048;
int actualLength = 0;
char log[2048];
glGetProgramInfoLog(program, maxLength, &actualLength, log);
NSLog(@"Shader info log for program %i:\n%s", program, log);
}
- (NSString*) appName {
return [[[NSBundle mainBundle] infoDictionary] objectForKey:(id)kCFBundleNameKey];
}
- (NSString*) title {
return NSStringFromClass([self class]);
}
- (void) setup
{
}
- (void) resized:(NSSize) size
{
}
- (void) renderDisplay
{
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment