objective-cmacoscocoaopenglnsopenglview

Creating an OpenGL View without Interface Builder


So im trying to create an openGL view (in my window). Im making a Cocoa app. Ive managed to create one through Interface Builder, but for educational purposes i want to go on and make one without it. Just on papers.

And here is the point im telling you im struggling with it. So what ive basically done-tried so far is this: I ve created a new class "MyOpenGLView.h/m" inheriting from NSOpenGLView. I ve added no private vars or methods to it just the class name. The only thing i did, was override initWithFrame: (adding a self = [super initWithFrame:pixelFormat:] inside it.) I read about it on the Web that you have to instantiate it with something like this first before you can use it). here is the code:

- (id) initWithFrame:(NSRect)frameRect
{
 NSOpenGLPixelFormat *pixelFormat = [[NSOpenGLPixelFormat alloc]
                                    initWithAttributes:(NSOpenGLPixelFormatAttribute[])
                                    {
                                    NSOpenGLPFAWindow,
                                    NSOpenGLPFADoubleBuffer,
                                    NSOpenGLPFADepthSize, 32,
                                    nil
                                    }];
 self = [super initWithFrame:frameRect pixelFormat:pixelFormat];
 [[self openGLContext] makeCurrentContext];
}

So i have another class named by "MyViewController.h/m" which handles my View? and there i have my MyOpenGLView *myView. In the .m file i go with something like this:

myView = [[MyOpenGLView alloc] initWithFrame:CGRectMake(0,0,100.0,100.0)];
if (!myView) { NSLog(@"ERROR"); }

and of course, i get error.

There is no openGL view ported in my window application. I would guess its something about the hierarchy on methods being called but then again.. im not sure. Can you aid me with it?


Solution

  • They way I have gotten this to work is I don't implement an init method in my view. Then in my controller or app delegate I have.

    @implementation AppDelegate
    
    @synthesize window = _window;
    @synthesize view = _view;
    
    - (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
        NSRect mainDisplayRect = [[NSScreen mainScreen] frame]; // I'm going to make a full screen view.
    
        NSOpenGLPixelFormatAttribute attr[] = {
            NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core, // Needed if using opengl 3.2 you can comment this line out to use the old version.
            NSOpenGLPFAColorSize,     24,
            NSOpenGLPFAAlphaSize,     8,
            NSOpenGLPFAAccelerated,
            NSOpenGLPFADoubleBuffer,
            0
        };
    
        NSOpenGLPixelFormat *pix = [[NSOpenGLPixelFormat alloc] initWithAttributes:attr];
        self.view = [[OpenGLViewCoreProfile alloc] initWithFrame:mainDisplayRect pixelFormat:pix];
    
        // Below shows how to make the view fullscreen. But you could just add to the contact view of any window.
        self.window = [[NSWindow alloc] initWithContentRect:mainDisplayRect
                                                  styleMask:NSBorderlessWindowMask 
                                                    backing:NSBackingStoreBuffered 
                                                      defer:YES];
    
        self.window.opaque = YES;
        self.window.hidesOnDeactivate = YES;
        self.window.level = NSMainMenuWindowLevel + 1; // Show window above main menu.
        self.window.contentView = self.view;
        [self.window makeKeyAndOrderFront:self]; // Display window.
    }
    
    @end
    

    Can can make call -makeCurrentContext in your -prepareOpenGl method. All that I have written below is not necessary but nice for performance reasons. I have started using the CVDisplayLink to sync frame drawing with the screen refresh rate so my openGLview looks like

    // This is the callback function for the display link.
    static CVReturn OpenGLViewCoreProfileCallBack(CVDisplayLinkRef displayLink,
                                                  const CVTimeStamp* now, 
                                                  const CVTimeStamp* outputTime, 
                                                  CVOptionFlags flagsIn, 
                              CVOptionFlags *flagsOut, 
                                                  void *displayLinkContext) {
        @autoreleasepool {
            OpenGLViewCoreProfile *view = (__bridge OpenGLViewCoreProfile*)displayLinkContext;
            [view.openGLContext makeCurrentContext];
            CGLLockContext(view.openGLContext.CGLContextObj); // This is needed because this isn't running on the main thread.
            [view drawRect:view.bounds]; // Draw the scene. This doesn't need to be in the drawRect method.
            CGLUnlockContext(view.openGLContext.CGLContextObj);
            CGLFlushDrawable(view.openGLContext.CGLContextObj); // This does glFlush() for you.
    
            return kCVReturnSuccess;
        }
    }
    
    - (void)reshape {
        [super reshape];
        CGLLockContext(self.openGLContext.CGLContextObj);
    
        ... // standard opengl reshape stuff goes here.
    
        CGLUnlockContext(self.openGLContext.CGLContextObj);
    }
    
    - (void)prepareOpenGL {
        [super prepareOpenGL];
    
        [self.openGLContext makeCurrentContext];
        GLint swapInt = 1;
        [self.openGLContext setValues:&swapInt forParameter:NSOpenGLCPSwapInterval];
    
        CGLLockContext(self.openGLContext.CGLContextObj);
    
        ... // all opengl prep goes here
    
        CGLUnlockContext(self.openGLContext.CGLContextObj);
    
        // Below creates the display link and tell it what function to call when it needs to draw a frame.
        CVDisplayLinkCreateWithActiveCGDisplays(&_displayLink);
        CVDisplayLinkSetOutputCallback(self.displayLink, &OpenGLViewCoreProfileCallBack, (__bridge void *)self);
        CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext(self.displayLink, 
                                                          self.openGLContext.CGLContextObj, 
                                                          self.pixelFormat.CGLPixelFormatObj);
        CVDisplayLinkStart(self.displayLink);
    }