|
|
|
//
|
|
|
|
//
|
|
|
|
// OSX GLEssentials
|
|
|
|
//
|
|
|
|
// Copied from the Apple GLEssentials
|
|
|
|
//
|
|
|
|
//
|
|
|
|
|
|
|
|
#import "GLView.h"
|
|
|
|
#import "Game.h"
|
|
|
|
|
|
|
|
@import GLKit;
|
|
|
|
@import QuartzCore;
|
|
|
|
|
|
|
|
@interface GLView (){
|
|
|
|
CVDisplayLinkRef displayLink;
|
|
|
|
Game* game;
|
|
|
|
}
|
|
|
|
- (void) initGL;
|
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation GLView
|
|
|
|
|
|
|
|
- (CVReturn) getFrameForTime:(const CVTimeStamp*)outputTime{
|
|
|
|
// There is no autorelease pool when this method is called
|
|
|
|
// because it will be called from a background thread.
|
|
|
|
// It's important to create one or app can leak objects.
|
|
|
|
@autoreleasepool {
|
|
|
|
[self drawView];
|
|
|
|
return kCVReturnSuccess;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// This is the renderer output callback function
|
|
|
|
static CVReturn MyDisplayLinkCallback(CVDisplayLinkRef displayLink,
|
|
|
|
const CVTimeStamp* now,
|
|
|
|
const CVTimeStamp* outputTime,
|
|
|
|
CVOptionFlags flagsIn,
|
|
|
|
CVOptionFlags* flagsOut,
|
|
|
|
void* displayLinkContext)
|
|
|
|
{
|
|
|
|
CVReturn result = [(__bridge GLView*)displayLinkContext getFrameForTime:outputTime];
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) awakeFromNib{
|
|
|
|
NSOpenGLPixelFormatAttribute attrs[] = {
|
|
|
|
NSOpenGLPFADoubleBuffer,
|
|
|
|
NSOpenGLPFADepthSize, 24,
|
|
|
|
NSOpenGLPFAOpenGLProfile,
|
|
|
|
NSOpenGLProfileVersion3_2Core,
|
|
|
|
0
|
|
|
|
};
|
|
|
|
|
|
|
|
NSOpenGLPixelFormat *pf = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs];
|
|
|
|
assert(pf);
|
|
|
|
|
|
|
|
NSOpenGLContext* context = [[NSOpenGLContext alloc] initWithFormat:pf shareContext:nil];
|
|
|
|
assert(context);
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
// When we're using a CoreProfile context, crash if we call a legacy OpenGL function
|
|
|
|
// This will make it much more obvious where and when such a function call is made so
|
|
|
|
// that we can remove such calls.
|
|
|
|
// Without this we'd simply get GL_INVALID_OPERATION error for calling legacy functions
|
|
|
|
// but it would be more difficult to see where that function was called.
|
|
|
|
CGLEnable([context CGLContextObj], kCGLCECrashOnRemovedFunctions);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
self.pixelFormat = pf;
|
|
|
|
self.openGLContext = context;
|
|
|
|
|
|
|
|
// Opt-In to Retina resolution
|
|
|
|
self.wantsBestResolutionOpenGLSurface = YES;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) prepareOpenGL{
|
|
|
|
[super prepareOpenGL];
|
|
|
|
|
|
|
|
// Make all the OpenGL calls to setup rendering
|
|
|
|
// and build the necessary rendering objects
|
|
|
|
[self initGL];
|
|
|
|
|
|
|
|
// Create a display link capable of being used with all active displays
|
|
|
|
CVDisplayLinkCreateWithActiveCGDisplays(&displayLink);
|
|
|
|
|
|
|
|
// Set the renderer output callback function
|
|
|
|
CVDisplayLinkSetOutputCallback(displayLink, &MyDisplayLinkCallback, (__bridge void *)(self));
|
|
|
|
|
|
|
|
// Set the display link for the current renderer
|
|
|
|
CGLContextObj cglContext = [[self openGLContext] CGLContextObj];
|
|
|
|
CGLPixelFormatObj cglPixelFormat = [[self pixelFormat] CGLPixelFormatObj];
|
|
|
|
CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext(displayLink, cglContext, cglPixelFormat);
|
|
|
|
|
|
|
|
// Activate the display link
|
|
|
|
CVDisplayLinkStart(displayLink);
|
|
|
|
|
|
|
|
// Register to be notified when the window closes so we can stop the displaylink
|
|
|
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
|
|
|
selector:@selector(windowWillClose:)
|
|
|
|
name:NSWindowWillCloseNotification
|
|
|
|
object:[self window]];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) windowWillClose:(NSNotification*)notification{
|
|
|
|
// Stop the display link when the window is closing because default
|
|
|
|
// OpenGL render buffers will be destroyed. If display link continues to
|
|
|
|
// fire without renderbuffers, OpenGL draw calls will set errors.
|
|
|
|
|
|
|
|
CVDisplayLinkStop(displayLink);
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) initGL{
|
|
|
|
// The reshape function may have changed the thread to which our OpenGL
|
|
|
|
// context is attached before prepareOpenGL and initGL are called. So call
|
|
|
|
// makeCurrentContext to ensure that our OpenGL context current to this
|
|
|
|
// thread (i.e. makeCurrentContext directs all OpenGL calls on this thread
|
|
|
|
// to [self openGLContext])
|
|
|
|
[[self openGLContext] makeCurrentContext];
|
|
|
|
|
|
|
|
// Synchronize buffer swaps with vertical refresh rate
|
|
|
|
GLint swapInt = 1;
|
|
|
|
[[self openGLContext] setValues:&swapInt forParameter:NSOpenGLCPSwapInterval];
|
|
|
|
|
|
|
|
game = [[Game alloc] init];
|
|
|
|
|
|
|
|
CGFloat x = 20;
|
|
|
|
CGFloat y = 20;
|
|
|
|
for (NSString * a in game.actions) {
|
|
|
|
NSButton *myButton = [[NSButton alloc] initWithFrame:NSMakeRect(x, y, 200, 22)];
|
|
|
|
myButton.buttonType = NSMomentaryLightButton;
|
|
|
|
myButton.bezelStyle = NSRecessedBezelStyle;
|
|
|
|
myButton.title = a;
|
|
|
|
myButton.target = self;
|
|
|
|
myButton.action = @selector(action:);
|
|
|
|
myButton.wantsLayer = YES;
|
|
|
|
[self addSubview: myButton];
|
|
|
|
|
|
|
|
y += 22;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) action:(NSButton*) sender{
|
|
|
|
[game action:sender.title];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) reshape{
|
|
|
|
[super reshape];
|
|
|
|
|
|
|
|
// We draw on a secondary thread through the display link. However, when
|
|
|
|
// resizing the view, -drawRect is called on the main thread.
|
|
|
|
// Add a mutex around to avoid the threads accessing the context
|
|
|
|
// simultaneously when resizing.
|
|
|
|
CGLLockContext([self.openGLContext CGLContextObj]);
|
|
|
|
|
|
|
|
NSRect viewRectPoints = self.bounds;
|
|
|
|
|
|
|
|
// [NSView convertRectToBacking] converts point sizes to pixel sizes.
|
|
|
|
// viewRectPixels will be larger (2x) than viewRectPoints for retina displays.
|
|
|
|
// viewRectPixels will be the same as viewRectPoints for non-retina displays
|
|
|
|
NSRect viewRectPixels = [self convertRectToBacking:viewRectPoints];
|
|
|
|
game.windowSize = viewRectPixels.size;
|
|
|
|
|
|
|
|
CGLUnlockContext([self.openGLContext CGLContextObj]);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (void)renewGState{
|
|
|
|
// Called whenever graphics state updated (such as window resize)
|
|
|
|
|
|
|
|
// OpenGL rendering is not synchronous with other rendering on the OSX.
|
|
|
|
// Therefore, call disableScreenUpdatesUntilFlush so the window server
|
|
|
|
// doesn't render non-OpenGL content in the window asynchronously from
|
|
|
|
// OpenGL content, which could cause flickering. (non-OpenGL content
|
|
|
|
// includes the title bar and drawing done by the app with other APIs)
|
|
|
|
[[self window] disableScreenUpdatesUntilFlush];
|
|
|
|
|
|
|
|
[super renewGState];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) drawRect: (NSRect) theRect{
|
|
|
|
// Called during resize operations
|
|
|
|
// Avoid flickering during resize by drawiing
|
|
|
|
[self drawView];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) drawView{
|
|
|
|
[[self openGLContext] makeCurrentContext];
|
|
|
|
|
|
|
|
// We draw on a secondary thread through the display link
|
|
|
|
// When resizing the view, -reshape is called automatically on the main
|
|
|
|
// thread. Add a mutex around to avoid the threads accessing the context
|
|
|
|
// simultaneously when resizing
|
|
|
|
CGLLockContext([[self openGLContext] CGLContextObj]);
|
|
|
|
|
|
|
|
NSRect viewRectPoints = self.bounds;
|
|
|
|
NSRect viewRectPixels = [self convertRectToBacking:viewRectPoints];
|
|
|
|
|
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
|
|
|
glViewport(0, 0, (int)viewRectPixels.size.width, (int)viewRectPixels.size.height);
|
|
|
|
|
|
|
|
[game update:1/60.0f];
|
|
|
|
[game draw];
|
|
|
|
|
|
|
|
CGLFlushDrawable([[self openGLContext] CGLContextObj]);
|
|
|
|
CGLUnlockContext([[self openGLContext] CGLContextObj]);
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) dealloc{
|
|
|
|
// Stop the display link BEFORE releasing anything in the view
|
|
|
|
// otherwise the display link thread may call into the view and crash
|
|
|
|
// when it encounters something that has been release
|
|
|
|
CVDisplayLinkStop(displayLink);
|
|
|
|
CVDisplayLinkRelease(displayLink);
|
|
|
|
|
|
|
|
// Release the display link AFTER display link has been released
|
|
|
|
|
|
|
|
}
|
|
|
|
@end
|