So in this post I will show how to create OpenGL application using Cocoa and how to draw into my own NSOpenGLView .
Creating project
- In Xcode create new Cocoa appliction project
- Right click on project - Add - Existing framework... and select OpenGL.framework
- Open Interface Builder and in the classes find NSOpenGLView, right-click and select New Subclass... Then name it for example MainOpenGLView
- Drag created MainOpenGLView to the MainWindow
- Create new menu item called Fullscreen
- When you create new application, application delegate is created. It is named appnameAppDelegate. Create new action for this delegate called setFullscreen.
- Create outlets for this delegate. One called StartingWindow (type NSWindow), FullScreenWindow (NSWindow too) and glView (type )
- Link Sent Actions selector in Fullscreen menu item with setFullscreen action in application delegate
- Create new link of Window to application delegate outlet StartingWindow we have created.
- Create new link of MainOpenGLView to application delegate outlet glView we have created.
- Right-click the MainOpenGLView class and select Write Updated Class Files
- Save and close.
Coding
First of all I have to add generated .m and .h file to your project. It is class MainOpenGLView.Next I import and include header files and specify some constants:
#import <OpenGL/OpenGL.h> #include <OpenGL/gl.h> #include <OpenGL/glu.h> #include <OpenGL/glext.h> #define BITS_PER_PIXEL 32.0 #define DEPTH_SIZE 32.0 #define DEFAULT_TIME_INTERVAL 0.001
Interface of the MainOpenGLView contains:
@interface MainOpenGLView : NSOpenGLView { int colorBits, depthBits; ///< color a depth bits } /** * initialization of OpenGL view */ - (id) initWithFrame:(NSRect)frame colorBits:(int)numColorBits depthBits:(int)numDepthBits; /** * method when resized window */ - (void) reshape; /** * draw scene */ - (void) drawRect:(NSRect)rect; /** * dealloc */ - (void) dealloc; @end @interface MainOpenGLView (InternalMethods) - (BOOL) initGL; @endI will start with initWithFrame method which creates CocoaGL instance and sets OpenGL context:
- (id) initWithFrame:(NSRect)frame colorBits:(int)numColorBits depthBits:(int)numDepthBits; { colorBits = numColorBits; depthBits = numDepthBits; ///< First, create an NSOpenGLPixelFormatAttribute NSOpenGLPixelFormat *nsglFormat; NSOpenGLPixelFormatAttribute attr[] = { NSOpenGLPFADoubleBuffer, NSOpenGLPFAAccelerated, NSOpenGLPFAColorSize, colorBits, NSOpenGLPFADepthSize, depthBits, 0 }; [self setPostsFrameChangedNotifications: YES]; ///< Next, initialize the NSOpenGLPixelFormat itself nsglFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attr]; ///< Check for errors in the creation of the NSOpenGLPixelFormat if(!nsglFormat) { NSLog(@"Invalid format... terminating."); return nil; } ///< Now create the the CocoaGL instance, using initial frame and the NSOpenGLPixelFormat self = [super initWithFrame:frame pixelFormat:nsglFormat]; [nsglFormat release]; ///< If there was an error, we again should probably send an error message to the user if(!self) { NSLog(@"Self not created... terminating."); return nil; } ///< Now set this context to the current context [[self openGLContext] makeCurrentContext]; ///< Finally, call the initGL method [self initGL]; return self; }The initGL method is simple. I just set some variables to default values.
- (BOOL) initGL { glShadeModel( GL_SMOOTH ); ///< Enable smooth shading glClearColor( 0.0f, 0.0f, 0.0f, 0.5f ); ///< Black background glClearDepth( 1.0f ); ///< Depth buffer setup glEnable( GL_DEPTH_TEST ); ///< Enable depth testing glDepthFunc( GL_LEQUAL ); ///< Type of depth test to do glHint( GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST ); ///< Really nice perspective calculations return TRUE; }Method to reshape when window is resized
- (void) reshape { NSRect sceneBounds; [ [ self openGLContext ] update ]; sceneBounds = [ self bounds ]; ///< Reset current viewport glViewport( 0, 0, sceneBounds.size.width, sceneBounds.size.height ); glMatrixMode( GL_PROJECTION ); ///< Select the projection matrix glLoadIdentity(); ///< and reset it ///< Calculate the aspect ratio of the view gluPerspective( 45.0f, sceneBounds.size.width / sceneBounds.size.height, 0.1f, 100.0f ); glMatrixMode( GL_MODELVIEW ); ///< Select the modelview matrix glLoadIdentity(); ///< and reset it }Method where scene is drawed:
- (void) drawRect:(NSRect)rect { ///< Clear the screen and depth buffer glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); glLoadIdentity(); ///< Reset the current modelview matrix //you can draw here [ [ self openGLContext ] flushBuffer ]; }It is necessary to create these two methods to avoid taking key events.
- (BOOL)acceptsFirstResponder { return NO; } - (BOOL)becomeFirstResponder { return YES; }And dealloc:
- (void) dealloc { [super dealloc]; }Second class is appnameAppDelegate.
It's interface is:
@interface appnameAppDelegate : NSResponder { IBOutlet NSWindow *StartingWindow; ///< starting window IBOutlet NSWindow *FullScreenWindow; ///< fullscreen window IBOutlet MainOpenGLView *glView; ///< OpenGL view NSTimer *renderTimer; ///< timer to refresh opengl view BOOL FullScreenOn; ///< if fullscreen is on } /** * After initialization and creation method */ - (void) awakeFromNib; /** * Method to toggle fullscreen */ - (IBAction) setFullScreen:(id)sender; /** * Deallocation method */ - (void) dealloc; /** * Key event handler */ - (void) keyDown:(NSEvent *)theEvent; @end @interface appnameAppDelegate (InternalMethods) - (void) setupRenderTimer; - (void) updateGLView:(NSTimer *)timer; - (void) createFailed; @endIn method awakeFromNib I do my initialization and set default values:
- (void) awakeFromNib { [NSApp setDelegate:self ]; renderTimer = nil; [ StartingWindow makeFirstResponder:self ]; ///< to delegate events to this class glView = [ [ MainOpenGLView alloc ] initWithFrame:[ StartingWindow frame ] ///< initialization of OpenGL view colorBits: 32 depthBits: 32]; if( glView != nil ) { [ StartingWindow setContentView:glView ]; [ StartingWindow makeKeyAndOrderFront:self ]; [ self setupRenderTimer ]; ///< setup timer } else [ self createFailed ]; }It is necessary to setup timer to time redrawing of the scene:
- (void) setupRenderTimer { NSTimeInterval timeInterval = 0.005; renderTimer = [ [ NSTimer scheduledTimerWithTimeInterval:timeInterval target:self selector:@selector( updateGLView: ) userInfo:nil repeats:YES ] retain ]; [ [ NSRunLoop currentRunLoop ] addTimer:renderTimer forMode:NSEventTrackingRunLoopMode ]; [ [ NSRunLoop currentRunLoop ] addTimer:renderTimer forMode:NSModalPanelRunLoopMode ]; }The method called in timer is opdateGLView:
- (void) updateGLView:(NSTimer *)timer { if( glView != nil ) [ glView drawRect:[ glView frame ] ]; ///< update view }Method to toggle fullscreen just change window style:
- (IBAction)setFullScreen:(id)sender { if( FullScreenOn == true ) ///< we need to go back to non-full screen { [FullScreenWindow close]; [StartingWindow setAcceptsMouseMovedEvents:YES]; [StartingWindow setContentView: glView]; [StartingWindow makeKeyAndOrderFront: self]; [StartingWindow makeFirstResponder: self]; FullScreenOn = false; } else ///< go to fullscreen { unsigned int windowStyle; NSRect contentRect; StartingWindow = [NSApp keyWindow]; windowStyle = NSBorderlessWindowMask; contentRect = [[NSScreen mainScreen] frame]; FullScreenWindow = [[NSWindow alloc] initWithContentRect:contentRect styleMask: windowStyle backing:NSBackingStoreBuffered defer: NO]; [StartingWindow setAcceptsMouseMovedEvents:NO]; if(FullScreenWindow != nil) { NSLog(@"Window was created"); [FullScreenWindow setTitle: @"myWindow"]; [FullScreenWindow setReleasedWhenClosed: YES]; [FullScreenWindow setAcceptsMouseMovedEvents:YES]; [FullScreenWindow setContentView: glView]; [FullScreenWindow makeKeyAndOrderFront:self ]; [FullScreenWindow setLevel: NSScreenSaverWindowLevel - 1]; [FullScreenWindow makeFirstResponder:self]; FullScreenOn = true; } } }Just to handle OpenGL init error:
- (void) createFailed { NSWindow *infoWindow; infoWindow = NSGetCriticalAlertPanel( @"Initialization failed", @"Failed to initialize OpenGL", @"OK", nil, nil ); [ NSApp runModalForWindow:infoWindow ]; [ infoWindow close ]; [ NSApp terminate:self ]; }Handle key down events:
- (void) keyDown:(NSEvent *)theEvent { unichar unicodeKey; unicodeKey = [ [ theEvent characters ] characterAtIndex:0 ]; switch( unicodeKey ) { case 'q': case 'Q': [ NSApp terminate:self ]; break; case 'f': case 'F': [self setFullScreen: self]; break; } }dealloc method:
- (void) dealloc { [ StartingWindow release ]; [ glView release ]; if( renderTimer != nil && [ renderTimer isValid ] ) [ renderTimer invalidate ]; [super dealloc]; }
And it is done.
I hope you will find it helpful.
No comments:
Post a Comment