OpenGL in Cocoa

copyright 2001 by T. Gene Davis

One of the exciting things about Mac OS X is its tight integration with popular cutting edge technologies. One of those technologies is OpenGL. OpenGL is a software interface that assists easy and quick development of 2D and 3D images and enviroments. It is used for modeling by engineers and some of the coolest 3D games. It can also be used for more mundane 2D endevours.

Much of a program written in c using the OpenGL interfaces can be ported as is to new platforms. Only the actual window construction and keyboard/mouse events need to be rewritten. This may be defined in a system dependent fashion. GLUT (OpenGL Utility Toolkit) fills in where OpenGL leaves off. GLUT simplifies common tasks like opening windows, listening to keyboard event, listening to mouse events and drawing useful 3D objects, and best yet, does it in a system independent manner. Mac OS X has a GLUT framework and an OpenGL framework. (Again–very cool.)

There are a few hoops to jump through to get OpenGL and GLUT working in a Cocoa application, but after those are learned it isn’t that difficult to use Cocoa for OpenGL work in general. You’ll find that any tutorial or book on OpenGL or Cocoa can take over the training from there. One restriction is that OpenGL is c not java, so if you wish to use OpenGL in a Cocoa-java app, expect to brush up on you JNI.

Who is this tutorial for?

This tutorial is for the poor souls who have already learned some c but not much if any OpenGL, and have tried in vain to find a tutorial on how to learn OpenGL on the newest and greatest operating system — OS X. If you have a brand new shiny book on OpenGL, a mac and no idea on how to get the book and computer to play nice — welcome. This intro to OpenGL on Mac OS X is for you.

What does this tutorial cover?

The object of this tutorial is to give you, the reader, a quick heads up on Mac OS X specific information needed to before you can take advantage of the host of information on OpenGL and GLUT that does not give you any Mac OS X specific help. Lots of reference materials are applicable to OpenGL or GLUT on the Mac, but don’t include the basic platform specific info needed to do their examples. I try to cover all this. It isn’t really much, just not well documented, yet.

Setting up Project Builder for OpenGL and/or GLUT

If you wish to create an application using GLUT, start out by creating a new project that is a Standard Tool. Next you need to make sure your Standard Tool (or rather GLUT Standard Tool ; – ) has access to the proper libraries. These libraries are called Frameworks. There are three of them that you will need to start: OpenGL, GLUT and Cocoa.

To import these frameworks select a folder under Groups & Files in the Files tab on the right side of the Project Builder window. Do not select a file. Next go to the menu bar at the top of the screen and select “Project”. Under “Project” you should be able to select “Add Frameworks…”. Select “Add Frameworks…”. You now see a file navigation window. Navigate to “/System/Library/Frameworks/” if you are not already there. Now you see all kinds of folders with the extension “.framework”. These are the frameworks your system has access to. Select the three frameworks that you need and add them to the default target for your project. Again the three frameworks that you need for a GLUT application are: OpenGL.framework, GLUT.framework and Cocoa.framework.

In a GLUT application you will need to either #import <GLUT/glut.h> or #include <GLUT/glut.h>. Everything else is likely to be the same across different platforms, so if you have a book on GLUT, your ready to start using it.

To use OpenGL without GLUT in an application that is built with Project Builder, you’ll have to jump through a few extra hoops. First open a new project in Project Builder that is a Cocoa Application. Next add the required OpenGL framework to the project. This is the same process as for a GLUT application, except that the GLUT framework does not need to be imported and the Cocoa framework was added at creation of the cocoa application project.

Next you will need an Obj-c object that is a child of NSOpenGLView. That will be created by messing with the MainNib with Interface Builder. That is detailed in the section “Next the Nib”. When you have interface and implementation of the subclass of NSOpenGLView, add the #imports for the OpenGL libraries that you need. The imports will take the form of #import <OpenGL/gl.h>.

Next the Nib

Well, you’re almost ready to write that breath taking Cocoa app using OpenGL. But just how do you get that OpenGL into a window? Ahh, I can help you there.

Go to the “Resources” folder under “Groups & Files” in the Project Builder window. In there you will find the MainMenu.nib. Double click it to edit it in the Interface Builder. Now that you’re in Interface Builder open the pallets window if it isn’t open. It can be opened from the menu bar by selecting Tools > Pallets > Show pallets. Choose “Cocoa – Graphics Views” pallet and you will see a big “OpenGL” drag it onto your main window that you are constructing. It represents an NSOpenGLView.

Click on the window the new NSOpenGLView is in. You do NOT want the NSOpenGLView selected. Next, select the Classes tab in the MainMenu.nib window. If the Window was selected and not the view then one of the classes displayed in the Classes menu will be NSView. It looks grayed out, but select it anyway. Under that select NSOpenGLView. Now ctrl-click it with the mouse or go to the Classes menu at the top of the screen. Select “Subclass NSOpenGLView”. The default name for your new NSOpenGL subclass is probably MyOpenGLView.

For simplicity, I’ll assume you keep the name of MyOpenGLView. Now ctrl-click the new class or from the Classes menu select “Create files for MyOpenGLView”. Add it to the current project and target.

Now for the very last steps before you close Interface Builder. Click on that NSOpenGLView that you put in the window your constructing. Select “Tools > Show Info”. This will display the Info window. From its drop down menu select “Custom Class”. You now see a list of classes that this NSOpenGLView could be associated with. Select MyOpenGLView, or whatever you called your new subclass. Save and exit Interface Builder.

You’re already to code. Just put a - (void)drawRect: (NSRect)rect {} method in your MyOpenGLView.m source file and put your OpenGL code in there.

Example Projects

We’re to the examples. All this abstract stuff is nice, but how does it look when it works? Well here are two simple projects that I’ve included in .zip files for your enjoyment. They should give you some idea how to put all of this together.

Here’s the Hello OpenGL¬†project zip. Below are the *.h and *.m files from the project.

MyOpenGLView.h

#import <Cocoa/Cocoa.h>
#import <OpenGL/gl.h>
#import <OpenGL/glu.h>

@interface MyOpenGLView : NSOpenGLView
{
}
@end

 

MyOpenGLView.m

#import "MyOpenGLView.h"

@implementation MyOpenGLView

- (void)drawRect:(NSRect)rect {
    //Draw board
    glClearColor(0.0, 0.0, 0.0, 0.0);
    glClear(GL_COLOR_BUFFER_BIT);
    gluOrtho2D(0.0, 525.0, 125.0, 0.0);
    glBegin(GL_QUADS);
        glColor3f(.5, .5, .9);

        // "H"
        glVertex2i(0, 0);
        glVertex2i(25, 0);
        glVertex2i(25, 125);
        glVertex2i(0, 125);

        glVertex2i(25, 50);
        glVertex2i(50, 50);
        glVertex2i(50, 75);
        glVertex2i(25, 75);

        glVertex2i(50, 0);
        glVertex2i(75, 0);
        glVertex2i(75, 125);
        glVertex2i(50, 125);

        // "E"
        glVertex2i(100, 0);
        glVertex2i(175, 0);
        glVertex2i(175, 25);
        glVertex2i(100, 25);

        glVertex2i(125, 50);
        glVertex2i(150, 50);
        glVertex2i(150, 75);
        glVertex2i(125, 75);

        glVertex2i(100, 100);
        glVertex2i(175, 100);
        glVertex2i(175, 125);
        glVertex2i(100, 125);

        glVertex2i(100, 25);
        glVertex2i(125, 25);
        glVertex2i(125, 100);
        glVertex2i(100, 100);

        // "L"
        glVertex2i(200, 0);
        glVertex2i(225, 0);
        glVertex2i(225, 125);
        glVertex2i(200, 125);

        glVertex2i(225, 100);
        glVertex2i(275, 100);
        glVertex2i(275, 125);
        glVertex2i(225, 125);

        // "L"
        glVertex2i(300, 0);
        glVertex2i(325, 0);
        glVertex2i(325, 125);
        glVertex2i(300, 125);

        glVertex2i(325, 100);
        glVertex2i(375, 100);
        glVertex2i(375, 125);
        glVertex2i(325, 125);

        // "O"
        glVertex2i(400, 0);
        glVertex2i(425, 0);
        glVertex2i(425, 125);
        glVertex2i(400, 125);

        glVertex2i(425, 100);
        glVertex2i(450, 100);
        glVertex2i(450, 125);
        glVertex2i(425, 125);

        glVertex2i(450, 0);
        glVertex2i(475, 0);
        glVertex2i(475, 125);
        glVertex2i(450, 125);

        glVertex2i(425, 0);
        glVertex2i(450, 0);
        glVertex2i(450, 25);
        glVertex2i(425, 25);

        // "!"
        glVertex2i(500, 0);
        glVertex2i(525, 0);
        glVertex2i(525, 75);
        glVertex2i(500, 75);

        glVertex2i(500, 100);
        glVertex2i(525, 100);
        glVertex2i(525, 125);
        glVertex2i(500, 125);

    glEnd();
    glFlush();
}

@end

 

Here’s the OpenGL GLUT Example¬†project zip. Below are the *.h and *.m files from the project.

main.c

// This window will appear behind the window the app was started from.
// So if you don't see the window when you start it, it is hidden behind other windows.

#include <GLUT/glut.h>
#include <stdlib.h>

void display(void);

int main(int argc, char** argv)
{
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
    glutInitWindowSize(525, 125);
    glutInitWindowPosition(100, 100);
    glutCreateWindow("Hello GLUT");
    glutPushWindow();
    glutDisplayFunc(display);
    glutMainLoop();
    return 0;
}

void display(void)
{
    //Draw board
    glClearColor(0.0, 0.0, 0.0, 0.0);
    glClear(GL_COLOR_BUFFER_BIT);
    gluOrtho2D(0.0, 525.0, 125.0, 0.0);

    glBegin(GL_QUADS);
        glColor3f(.5, .5, .9);

        // "H"
        glVertex2i(0, 0);
        glVertex2i(25, 0);
        glVertex2i(25, 125);
        glVertex2i(0, 125);

        glVertex2i(25, 50);
        glVertex2i(50, 50);
        glVertex2i(50, 75);
        glVertex2i(25, 75);

        glVertex2i(50, 0);
        glVertex2i(75, 0);
        glVertex2i(75, 125);
        glVertex2i(50, 125);

        // "E"
        glVertex2i(100, 0);
        glVertex2i(175, 0);
        glVertex2i(175, 25);
        glVertex2i(100, 25);

        glVertex2i(125, 50);
        glVertex2i(150, 50);
        glVertex2i(150, 75);
        glVertex2i(125, 75);

        glVertex2i(100, 100);
        glVertex2i(175, 100);
        glVertex2i(175, 125);
        glVertex2i(100, 125);

        glVertex2i(100, 25);
        glVertex2i(125, 25);
        glVertex2i(125, 100);
        glVertex2i(100, 100);

        // "L"
        glVertex2i(200, 0);
        glVertex2i(225, 0);
        glVertex2i(225, 125);
        glVertex2i(200, 125);

        glVertex2i(225, 100);
        glVertex2i(275, 100);
        glVertex2i(275, 125);
        glVertex2i(225, 125);

        // "L"
        glVertex2i(300, 0);
        glVertex2i(325, 0);
        glVertex2i(325, 125);
        glVertex2i(300, 125);

        glVertex2i(325, 100);
        glVertex2i(375, 100);
        glVertex2i(375, 125);
        glVertex2i(325, 125);

        // "O"
        glVertex2i(400, 0);
        glVertex2i(425, 0);
        glVertex2i(425, 125);
        glVertex2i(400, 125);

        glVertex2i(425, 100);
        glVertex2i(450, 100);
        glVertex2i(450, 125);
        glVertex2i(425, 125);

        glVertex2i(450, 0);
        glVertex2i(475, 0);
        glVertex2i(475, 125);
        glVertex2i(450, 125);

        glVertex2i(425, 0);
        glVertex2i(450, 0);
        glVertex2i(450, 25);
        glVertex2i(425, 25);

        // "!"
        glVertex2i(500, 0);
        glVertex2i(525, 0);
        glVertex2i(525, 75);
        glVertex2i(500, 75);

        glVertex2i(500, 100);
        glVertex2i(525, 100);
        glVertex2i(525, 125);
        glVertex2i(500, 125);

    glEnd();
    glFlush();
}

Where to Go From Here

I used two sources to figure out the basics of OpenGL for Cocoa (Mac OS X). The first was Appendix A: Drawing in Cocoa out of Learning Cocoa by Apple Computer, Inc published by O’reilly. Page 331 of the 2001 edition has a section called “Draw NSStrings”. It goes through a detailed account of creating a custom NSView by subclassing it and then creating the files needed to interface with it in the Objective-c app that they create in Project Builder.

It is important to note in reading “Draw NSStrings” section that both the custom NSView and NSOpenGLView are children of NSView, so read it with that in mind. Also, pay special attention to anything relating to the method - (void)drawRect: (NSRect)rect;

The second source that I used was OpenGL: Programming Guide, third edition. The examples in it work as is or with little modification on OS X. For example, the code snippet on pg. 6 (Example 1-1) works very well in a Project Builder project by taking the code from between the lines “InitializeAWindowPlease();” and “UpdateTheWindowAndCheckForEvents();”, and placing that in a drawRect method created for a subclass of the NSOpenGLView. All you need to do is add the OpenGL framework to your class and add the line #import &ltOpenGL/gl.h&gt to the file or its header. Here is the project in a zip for example.

Below is the ChunkOfCodeView.m file’s code.

#import "ChunkOfCodeView.h"

@implementation ChunkOfCodeView

- (void)drawRect: (NSRect)rect {
    //Begin code from "OpenGL: Programming Guide" third edition, pg 6.
    //Comments are mine

    //Set the background to black
    glClearColor(0.0, 0.0, 0.0, 0.0);
    glClear(GL_COLOR_BUFFER_BIT);
    //Set the drawing color to white
    glColor3f(1.0, 1.0, 1.0);
    //Set the grid to use for drawing coordinates
    glOrtho(0.0, 1.0, 0.0, 1.0, -1.0, 1.0);
    //draw a OpenGL polygon
    glBegin(GL_POLYGON);
        glVertex3f(0.25, 0.25, 0.0);
        glVertex3f(0.75, 0.25, 0.0);
        glVertex3f(0.75, 0.75, 0.0);
        glVertex3f(0.25, 0.75, 0.0);
    glEnd();
    //Flush the drawing commands, so it actually gets drawn
    glFlush();
    //End code from "OpenGL: Programming Guide" third edition, pg 6.
}

@end

The “hello.c” example on pg. 18 (Example 1-2) works when compiled and run from the command line or from project builder as a Standard Tool. Just change the include from <GL/glut.h> to <GLUT/glut.h> in the file and include three frameworks OpenGL, GLUT and Cocoa. The compile command from the command line would look something like cc -o new_program hello.c -framework OpenGL -framework GLUT -framework Cocoa. If you get a “permission denied” when you try to run it using ./new_program, just type chmod 700 new_program" to make it runnable.

Conlclusion

I hope that this gives you a good starting point in your exploration of OpenGL on Mac OS X. If something in this tutorial is not clear, drop me a line on Twitter @TGeneDavis. If I have time, I’ll try to improve what I’ve written, or expand on it.