Sources/View/OpenGLView.mm
/* |
<codex> |
<import>OpenGLView.h</import> |
</codex> |
*/ |
#pragma mark - |
#pragma mark Private - Headers |
#import <iostream> |
#import <vector> |
#import <OpenGL/gl.h> |
#import "NBodyPreferences.h" |
#import "NBodyConstants.h" |
#import "NBodyEngine.h" |
#import "GLcontainers.h" |
#import "GLUQuery.h" |
#import "OpenGLView.h" |
#pragma mark - |
static const NSOpenGLPixelFormatAttribute kOpenGLAttribsLegacyProfile[7] = |
{ |
NSOpenGLPFADoubleBuffer, |
NSOpenGLPFAAccelerated, |
NSOpenGLPFAAcceleratedCompute, |
NSOpenGLPFAAllowOfflineRenderers, // NOTE: Needed to connect to compute-only gpus |
NSOpenGLPFADepthSize, 24, |
0 |
}; |
static const NSOpenGLPixelFormatAttribute kOpenGLAttribsLegacyDefault[4] = |
{ |
NSOpenGLPFADoubleBuffer, |
NSOpenGLPFADepthSize, 24, |
0 |
}; |
@implementation OpenGLView |
{ |
BOOL mbFullscreen; |
NSDictionary* mpOptions; |
NSOpenGLContext* mpContext; |
NSTimer* mpTimer; |
NBodyEngine* mpEngine; |
NBodyPreferences* mpPrefs; |
IBOutlet NSPanel* mpHUD; |
} |
#pragma mark - |
#pragma mark Private - Destructor |
- (void) _cleanUpOptions |
{ |
if(mpOptions) |
{ |
[mpOptions release]; |
mpOptions = nil; |
} // if |
} // _cleanUpOptions |
- (void) _cleanUpTimer |
{ |
if(mpTimer) |
{ |
[mpTimer invalidate]; |
[mpTimer release]; |
} // if |
} // _cleanUpTimer |
- (void) _cleanUpPrefs |
{ |
if(mpPrefs) |
{ |
[mpPrefs addEntries:mpEngine.preferences]; |
[mpPrefs write]; |
[mpPrefs release]; |
mpPrefs = nil; |
} // if |
} // _cleanUpEngine |
- (void) _cleanUpEngine |
{ |
if(mpEngine) |
{ |
[mpEngine release]; |
mpEngine = nil; |
} // if |
} // _cleanUpEngine |
- (void) _cleanUpObserver |
{ |
// If self isn't removed as an observer, the Notification Center |
// will continue sending notification objects to the deallocated |
// object. |
[[NSNotificationCenter defaultCenter] removeObserver:self]; |
} // _cleanUpObserver |
// Tear-down objects |
- (void) _cleanup |
{ |
[self _cleanUpOptions]; |
[self _cleanUpPrefs]; |
[self _cleanUpTimer]; |
[self _cleanUpEngine]; |
[self _cleanUpObserver]; |
} // _cleanup |
#pragma mark - |
#pragma mark Private - Utilities - Misc. |
// When application is terminating cleanup the objects |
- (void) _quit:(NSNotification *)notification |
{ |
[self _cleanup]; |
} // _quit |
- (void) _idle |
{ |
[self setNeedsDisplay:YES]; |
} // _idle |
- (void) _toggleFullscreen |
{ |
if(mpPrefs.fullscreen) |
{ |
[self enterFullScreenMode:[NSScreen mainScreen] |
withOptions:mpOptions]; |
} // else |
} // _toggleFullscreen |
- (void) _alert:(NSString *)pMessage |
{ |
if(pMessage) |
{ |
NSAlert* pAlert = [NSAlert new]; |
if(pAlert) |
{ |
[pAlert addButtonWithTitle:@"OK"]; |
[pAlert setMessageText:pMessage]; |
[pAlert setAlertStyle:NSCriticalAlertStyle]; |
NSModalResponse response = [pAlert runModal]; |
if(response == NSAlertFirstButtonReturn) |
{ |
NSLog(@">> MESSAGE: %@", pMessage); |
} // if |
[pAlert release]; |
} // if |
} // if |
} // _alert |
- (BOOL) _query |
{ |
GLU::Query query; |
// NOTE: For OpenCL 1.2 support refer to <http://support.apple.com/kb/HT5942> |
GLstrings keys = |
{ |
"120", "130", "285", "320M", |
"330M", "X1800", "2400", "2600", |
"3000", "4670", "4800", "4870", |
"5600", "8600", "8800", "9600M" |
}; |
std::cout << ">> N-body Simulation: Renderer = \"" << query.renderer() << "\"" << std::endl; |
std::cout << ">> N-body Simulation: Vendor = \"" << query.vendor() << "\"" << std::endl; |
std::cout << ">> N-body Simulation: Version = \"" << query.version() << "\"" << std::endl; |
return BOOL(query.match(keys)); |
} // _query |
- (NSOpenGLPixelFormat *) _newPixelFormat |
{ |
NSOpenGLPixelFormat* pFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:kOpenGLAttribsLegacyProfile]; |
if(!pFormat) |
{ |
NSLog(@">> WARNING: Failed to initialize an OpenGL context with the desired pixel format!"); |
NSLog(@">> MESSAGE: Attempting to initialize with a fallback pixel format!"); |
pFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:kOpenGLAttribsLegacyDefault]; |
} // if |
return pFormat; |
} // _newPixelFormat |
#pragma mark - |
#pragma mark Private - Utilities - Prepare |
- (void) _preparePrefs |
{ |
mpPrefs = [NBodyPreferences new]; |
if(mpPrefs) |
{ |
mbFullscreen = mpPrefs.fullscreen; |
} // if |
} // _preparePrefs |
- (void) _prepareNBody |
{ |
if([self _query]) |
{ |
[self _alert:@"Requires OpenCL 1.2!"]; |
[self _cleanUpOptions]; |
[self _cleanUpTimer]; |
exit(-1); |
} // if |
else |
{ |
NSRect frame = [[NSScreen mainScreen] frame]; |
mpEngine = [[NBodyEngine alloc] initWithPreferences:mpPrefs]; |
if(mpEngine) |
{ |
mpEngine.frame = frame; |
[mpEngine acquire]; |
} // if |
} // else |
} // _prepareNBody |
- (void) _prepareRunLoop |
{ |
mpTimer = [[NSTimer timerWithTimeInterval:0.0 |
target:self |
selector:@selector(_idle) |
userInfo:self |
repeats:true] retain]; |
[[NSRunLoop currentRunLoop] addTimer:mpTimer |
forMode:NSRunLoopCommonModes]; |
} // _prepareRunLoop |
#pragma mark - |
#pragma mark Public - Designated Initializer |
- (instancetype) initWithFrame:(NSRect)frameRect |
{ |
BOOL bIsValid = NO; |
NSOpenGLPixelFormat* pFormat = [self _newPixelFormat]; |
if(pFormat) |
{ |
self = [super initWithFrame:frameRect |
pixelFormat:pFormat]; |
if(self) |
{ |
mpContext = [self openGLContext]; |
bIsValid = mpContext != nil; |
mpOptions = [[NSDictionary dictionaryWithObject:@(YES) |
forKey:NSFullScreenModeSetting] retain]; |
// It's important to clean up our rendering objects before we terminate -- Cocoa will |
// not specifically release everything on application termination, so we explicitly |
// call our cleanup (private object destructor) routines. |
[[NSNotificationCenter defaultCenter] addObserver:self |
selector:@selector(_quit:) |
name:@"NSApplicationWillTerminateNotification" |
object:NSApp]; |
} // if |
else |
{ |
NSLog(@">> ERROR: Failed to initialize an OpenGL context with attributes!"); |
} // else |
[pFormat release]; |
} // if |
else |
{ |
NSLog(@">> ERROR: Failed to acquire a valid pixel format!"); |
} // else |
if(!bIsValid) |
{ |
exit(-1); |
} // if |
return self; |
} // initWithFrame |
#pragma mark - |
#pragma mark Public - Destructor |
- (void) dealloc |
{ |
[self _cleanup]; |
[super dealloc]; |
} // dealloc |
#pragma mark - |
#pragma mark Public - Prepare |
- (void) prepareOpenGL |
{ |
[super prepareOpenGL]; |
[self _preparePrefs]; |
[self _prepareNBody]; |
[self _prepareRunLoop]; |
[self _toggleFullscreen]; |
} // prepareOpenGL |
#pragma mark - |
#pragma mark Public - Delegates |
- (BOOL) isOpaque |
{ |
return YES; |
} // isOpaque |
- (BOOL) acceptsFirstResponder |
{ |
return YES; |
} // acceptsFirstResponder |
- (BOOL) becomeFirstResponder |
{ |
return YES; |
} // becomeFirstResponder |
- (BOOL) resignFirstResponder |
{ |
return YES; |
} // resignFirstResponder |
- (BOOL) applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication |
{ |
return YES; |
} // applicationShouldTerminateAfterLastWindowClosed |
#pragma mark - |
#pragma mark Public - Updates |
- (void) renewGState |
{ |
[super renewGState]; |
[[self window] disableScreenUpdatesUntilFlush]; |
} // renewGState |
#pragma mark - |
#pragma mark Public - Display |
- (void) _resize |
{ |
if(mpEngine) |
{ |
NSRect bounds = [self bounds]; |
[mpEngine resize:bounds]; |
} // if |
} // _resize |
- (void) reshape |
{ |
[self _resize]; |
} // reshape |
- (void) drawRect:(NSRect)dirtyRect |
{ |
[mpEngine draw]; |
} // drawRect |
#pragma mark - |
#pragma mark Public - Help |
- (IBAction) toggleHelp:(id)sender |
{ |
if([mpHUD isVisible]) |
{ |
[mpHUD orderOut:sender]; |
} // if |
else |
{ |
[mpHUD makeKeyAndOrderFront:sender]; |
} // else |
} // toggleHelp |
#pragma mark - |
#pragma mark Public - Fullscreen |
- (IBAction) toggleFullscreen:(id)sender |
{ |
if([self isInFullScreenMode]) |
{ |
[self exitFullScreenModeWithOptions:mpOptions]; |
[[self window] makeFirstResponder:self]; |
mpPrefs.fullscreen = NO; |
} // if |
else |
{ |
[self enterFullScreenMode:[NSScreen mainScreen] |
withOptions:mpOptions]; |
mpPrefs.fullscreen = YES; |
} // else |
} // toggleFullscreen |
#pragma mark - |
#pragma mark Public - Keys |
- (void) keyDown:(NSEvent *)event |
{ |
if(event) |
{ |
NSString* pChars = [event characters]; |
if([pChars length]) |
{ |
unichar key = [[event characters] characterAtIndex:0]; |
if(key == 27) |
{ |
[self toggleFullscreen:self]; |
} // if |
else |
{ |
mpEngine.command = key; |
} // else |
} // if |
} // if |
} // keyDown |
- (void) mouseDown:(NSEvent *)event |
{ |
if(event) |
{ |
NSPoint where = [event locationInWindow]; |
NSRect bounds = [self bounds]; |
NSPoint point = NSMakePoint(where.x, bounds.size.height - where.y); |
[mpEngine click:NBody::Mouse::Button::kDown |
point:point]; |
} // if |
} // mouseDown |
- (void) mouseUp:(NSEvent *)event |
{ |
if(event) |
{ |
NSPoint where = [event locationInWindow]; |
NSRect bounds = [self bounds]; |
NSPoint point = NSMakePoint(where.x, bounds.size.height - where.y); |
[mpEngine click:NBody::Mouse::Button::kUp |
point:point]; |
} // if |
} // mouseUp |
- (void) mouseDragged:(NSEvent *)event |
{ |
if(event) |
{ |
NSPoint where = [event locationInWindow]; |
where.y = 1080.0f - where.y; |
[mpEngine move:where]; |
} // if |
} // mouseDragged |
- (void) scrollWheel:(NSEvent *)event |
{ |
if(event) |
{ |
CGFloat dy = [event deltaY]; |
[mpEngine scroll:dy]; |
} // if |
} // scrollWheel |
@end |
Copyright © 2015 Apple Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2015-12-10