PDA

View Full Version : Cocoa OpenGL problem


Taxxodium
2004.09.20, 03:06 PM
Hey,

OK, I'm starting a simple OpenGL experiment and while doing so I discovered something odd.

I have this code:


NSOpenGLPixelFormatAttribute attrs[] = {
//NSOpenGLPFANoRecovery,
//NSOpenGLPFAColorSize, 24,
// NSOpenGLPFADepthSize, 16,
NSOpenGLPFADoubleBuffer,
NSOpenGLPFAAccelerated,
NULL
};

NSOpenGLPixelFormat *pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs];
self = [super initWithFrame:[self bounds] pixelFormat:pixelFormat];
[pixelFormat release];

[[self openGLContext] makeCurrentContext];
[self initOpenGL];

if (self) {
//load in the world
world = [[MDWorld world] retain];
}


The class this is written in is a subclass of an NSOpenGLView. Now when I put this class in the - (id)initWithFrame:(NSRect*)frame initializer, nothing happens. My OpenGLView stays white, except when I resize it it sometimes gets black or had some garbage graphics.
However, when I put the same code in the - (void)awakeFromNib initializer I get a black screen and all my other OpenGL drawing methods work just fine.

I notised that the first initializer doesn't get called at all, does anyone know why?

Btw, this is my initOpenGL method, nothing too weird, I think..


- (void)initOpenGL {
glShadeModel(GL_SMOOTH);
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glClearDepth(1.0f);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);

glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
}

DoG
2004.09.20, 03:40 PM
It has to do with the fact that if you tell InterfaceBuilder that you want an NSOpenGLView initialized, it does something different than it would with a NSView subclass. Funky, eh?

The solution is to place custom view onto your window and make it whatever your class is, rather than to place an NSOpenGLView and use the custom class thingy.

Josh
2004.09.20, 04:27 PM
As a side note, it is - (id)initWithFrame:(NSRect)rect . Note that the rect is not a pointer as you have above.

Vertizor
2004.09.20, 05:42 PM
- (void) prepareOpenGL: is called by the framework to set up OpenGL drawing parameters, the same as your -initOpenGL:

prepareOpenGL is called after the context and pixel format is created and the context is attached to a drawable.

If you used Interface Builder to put an OpenGLView widget into your window, it is automatically alloc and init. You do not need to call the initializer with a Rect.

If I understand you correctly, you said when you put that code in initWithFrame it doesn't work, but inside awakeFromNib it does work. awakeFromNib is automatically called by the framework, so that code will always execute. initWithFrame would be used to create the view at runtime had it not been created by IB. If you didn't create your GL view widget programmatically, then your custom initWithFrame would not be called. There are other initializers that could be used by the framework in its stead.

mutle
2004.09.20, 06:22 PM
afaik, interface builder calls -initWithCoder: and not -initWithRect:, so then your code should work (btw: do [super initWithNSOpenGLContext] after you created the context.

Taxxodium
2004.09.20, 06:26 PM
As a side note, it is - (id)initWithFrame:(NSRect)rect . Note that the rect is not a pointer as you have above.

Oops, yes indeed, that's a typo in the forum, I have it correctly in my code.

Well, like DoG said, I expected the NSOpenGLView to work like any other NSView, guess it's something special. But anyway, thanks for the explanation.

Btw, instead of using an NSView, would it be good to override the prepareOpenGL method?

Vertizor
2004.09.20, 07:02 PM
Btw, instead of using an NSView, would it be good to override the prepareOpenGL method?
Read my post, 3 posts above.

kelvin
2004.09.20, 09:33 PM
The proper thing to do is to

1) Override -initWithFrame:(NSRect)frameRect; and in it call self = [self initWithFrame:frameRect pixelFormat:nil];
2) Override -initWithFrame:pixelFormat; and in it if !pixelformat, pixelformat = [[self class] defaultPixelFormat]; then continue to [super initWithFrame:pixelFormat]. Then, once you're done the super's init, you continue to create your own context if you wish, and -setOpenGLContext.
3)Next you'll want to override +defaultPixelFormat to provide the appropriate pixel format you'd like for your subclassed view.
4) Once you've done all those things, creating a new view is pretty easy. And you can implement -prepareOpenGL if you'd like to set up all your GL stuff in there.

This may seem complicated, but the result is really nice. This is what I did for CocoaGranite. I'd show you the code, but it's not anywhere near presentable.

arekkusu
2004.09.20, 11:43 PM
Note that -prepareOpenGL was introduced in 10.3.

codemattic
2004.09.21, 12:03 AM
This drove me mad also. Its very frustrating to wonder why your initializer isnt getting called. Ive seen a couple of examples that put the code in awakeFromNib that should of been in the initializer but clearly the author couldnt figure out why it wasnt getting called.

Here is how I *think* it works - Im not completely positive:

The problem is in the way you first create your view in IB. Either you A) place an NSOpenGLView and then change its class to MYOpenGLView - or B) you place an NSView and then change its class to MYOpenGLView. If you look at the develop docs for NSView, its initializer is –initWithFrame: so I think that gets called when you do B. NSOpenGLView's initializer is listed as –initWithFrame:pixelFormat: and so that gets called when you do A.

If you do it the A way - you dont have to create your own pixelFormat - you use the one that is passed to your initializer. This format is defined by the choices you make in the NSOpenGLView's inspector panel before you turn it into your own MYOpenGLView class. There is less code to write this way.

Right now Im using method B and to build everything in the initializer myself.

hth, Codemattic

arekkusu
2004.10.08, 03:24 PM
FYI, Apple has just posted an official tech Q&A (http://developer.apple.com/qa/qa2004/qa1167.html) about this.