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;
@end
I 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