Cocoa tutorial 1: Window & OpenGL view

Download the Sources here

A global idea of our app

Our app will be basically an OpenGL view, with 2 standard controls we will be able to modify rendering parameters. In this particular case we will use a standard slider control to rotate a triangle, with a button we will restore it to its original position.First of all we will create a Cocoa project, assign a name (whatever you want), and a project directory (usually, with the same name):

Adding the frameworks (thanks to Raymond L.)

We will use an OpenGL view, so we will need to include the OpenGL framework: Go to “Frameworks -> Linked Frameworks” (under your project name icon), then select “Linked Frameworks” with right button and select: “Add -> Existing Frameworks…” and select “OpenGL.Framework”:

Then we need to edit our window: double click on the “MainMenu.nib” file, and the Interface Builder will start:

IB (Interface Builder) is the tool that you will use to add/delete all the controls of our GUI (Graphical User Interface), and connect them. Spend some time adding and deleting some controls of our window. I recommend you to get the properties of the controls with the Inspector (Tools -> Show Inspector).

Adding the controls

From the controls window, add one slider, one button and one OpenGL view. Try changing the name of the buttons and the window: go to the Inspector window, and check the Attributes:

 

Note: One fancy tip… Select our window, and check the button “Has texture”.

Customizing the OpenGL control

Although we have an OpenGL view we need to subclass the standard “NSOpenGLView” in order to add our custom functions:

  • Go to “Classes” and locate NSOpenGLView class (it is on NSObject -> NSResponder -> NSView -> NSOpenGLView). You can also find the NSOpenGLView using the Search box.
  • Press right button, and select the option “Subclass NSOpenGLView”, a new class will appear, called “MyOpenGLView”:

Select the “MyOpenGLView” class and select the option “Create files for MyOpenGLView” using right mouse button and select “choose” button in following window:

Then, select our OpenGL view control from our window and go to Inspector, select “Custom Class”, and select our recently created custom class, called “MyOpenGLView”.

Go to Xcode again, we need to add our custom functions to our custom OpenGL control. We will see 2 new files: MyOpenGLView.h and MyOpenGLView.m. We will design our interface with one variable (“rotX”), and 3 functions: “drawRect”, “rotate” and “resetGLView”. drawRect: It is the function called each time that we want to refresh our openGL view. rotate: It is the function that will increase our rotation variable (rotX). resetGLView: It is the function that will restore the rotX variable to 0.

 

/* MyOpenGLView */
#import < Cocoa/Cocoa.h >
@interface MyOpenGLView : NSOpenGLView
{
 float rotX;
}
 - (void) drawRect: (NSRect) bounds ;
- (void) rotate;
- (void) resetGLView;
@end

Now we will design our implementation file… nothing to comment if you have practiced a little bit with Objective-C and OpenGL… only one thing: “drawAnObject” is a C standard function (static).

#import "MyOpenGLView.h"
#include < OpenGL/gl.h >
@implementation MyOpenGLView
- (id) init {
 self = [super init];
 if (self != nil) {
 rotX=0;
 }
 return self;
}

static void drawAnObject ()
{
 glColor3f(1.0f, 0.85f, 0.35f);
 glBegin(GL_TRIANGLES);
 {
  glVertex3f( 0.0, 0.6, 0.0);
  glVertex3f( -0.2, -0.3, 0.0);
  glVertex3f( 0.2, -0.3 ,0.0);
 }
 glEnd();
}
-(void) drawRect: (NSRect) bounds
{
 glClearColor(0, 0, 0, 0);
 glClear(GL_COLOR_BUFFER_BIT);
 glLoadIdentity();
 glRotatef(rotX,1,1,1);
 drawAnObject();
 glFlush();
}
-(void) rotate
{
 rotX += 1;
 [self drawRect:[self bounds]];
}
-(void) resetGLView
{
 rotX = 0;
 [self drawRect:[self bounds]];
}
@end

Designing our GUI-control class

Once we have prepared out OpenGLview, wee need to declare one new class. This class will take all the control of our GUI and will take the messages launched by the controls, and decide what to do in each case. For instance, when the slider is moved, the slider control will launch a message to our “GUI-control class” and this class will do whatever we have coded: in this particular case, we will call de “rotate” function of our OpenGLView.

  • One possible way to create our custom class is opening the Inerface Builder, in “Classes”, select the base class “NSObject”, and subclass it, we have called it “GUIcontrol”:

This class needs 2 actions that will receive the message form our 2 buttons, rotate and reset view: select our class, and press “add” button in Inspector window, the actions will be called “changeRot” and “resetView”:

We also need to define one Outlet, because our “GUIcontrol” needs to communicate with our openGL view, so we will add an OpenGLview outlet (type=id):

We must Instantiate our recently created class, so we will be able to use it:

We need to connect our class with the buttons and controls:

Actions: Drag the mouse with the “CTRL” key pressed from the “Reset” button to our “GUIcontrol” class, and from the inspector window, choose the action “resetView” and press the button “Connect”:

Then repeat the action with the slider, but connect it to the “changeRot” action.

NOTE: Change one attribute on the slider, called “Continuously send action while sliding”, in order to redraw the openGL view each pixel the slider is moved, and not only when the mouse button is released:

Outlets: now we will connect the GUIControl with our OpenGLView with the same operation: Drag from “GUIcontrol” class to “MyOpenGLView” control and press “Connect” in the “OpenGLview” Outlet.

Yipieee!! now we will create the files of our class:

In the “Classes” menu, select our “GUIcontrol” class and select the option “Create Files for GUIcontrol” and press “Choose” button.

We will see in Xcode, that 2 new files have been added: GUIcontrol.h and GUIcontrol.m, with some of free code 🙂 we only need to fill in the gaps! 😀

Go to GUIcontrol.m file and add just 2 lines of code:

 

#import "GUIcontrol.h"
@implementation GUIcontrol
- (IBAction)changeRot:(id)sender
{
 [OpenGLview rotate];
}
- (IBAction)resetView:(id)sender
{
 [OpenGLview resetGLView];
}
@end

Build and GO!!!

DONE!!!! just compile the project with the “Build and Go” button, and see your fancy app in action 😀

Download the Sources here

Questions?

Any doubt? email me at xpherezn (at) zonan [dot] org

1.- Question from Chris W.:

(…) I tried to compile it, and it said “MyOpenGLView undeclared (first use of this function)”. I was also slightly confused at this part because in your example you use “OpenGLview” instead of what seems like should be “MYOpenGLView.” Im assuming this was just a typo?

My answer:
“MyOpenGLView” is the name of the class and “OpenGLView” is the instance of GUIControl, you will see that because in your GUIControl.h file you should have something like this:

/* GUIcontrol */

#import

@interface GUIcontrol : NSObject
{
    IBOutlet id OpenGLview;
}
- (IBAction)changeRot:(id)sender;
- (IBAction)resetView:(id)sender;
@end

Please note: IBOutlet id OpenGLview;

Here you define that “OpenGLview” will be the name of the Outlet that will recieve the messages, probably you have another name defined in your GUIcontrol.h file, thats not wrong, but you should modify any of your references in your GUIcontrol.m file with the same name… in other words, if you have this “GUIcontrol.h” file:

/* GUIcontrol */
#import
@interface GUIcontrol : NSObject{
    IBOutlet id THIS_IS_MY_FANCY_OPENGLVIEW_NAME;
}
- (IBAction)changeRot:(id)sender;
- (IBAction)resetView:(id)sender;
@end

Then, each reference in the GUIcontrol.m file should be something like:

- (IBAction)changeRot:(id)sender
{
 [THIS_IS_MY_FANCY_OPENGLVIEW_NAME rotate];
}

 

This tutorial is under a Creative Commons license.

3 Comments

  1. Hi,

    Finally my GL program is running well. Thanks for this post. It helps me lots.

  2. Hi! i compiled the program, but in GUIControl.m it says: no known instance method for selector Rotate :

    -(IBAction)ChangeRot:(id)sender
    {
    [View Rotate]; //no known instance method for selector Rotate
    }

    -(IBAction)ResetView:(id)sender
    {
    [View ResetGLView]; //no known instance method for selector ResetGLView.
    }

    in GUIControl.h i have this:
    @interface GUIControl : NSObject
    {
    IBOutlet id View;
    }

    i don´t know what´s happening.
    Thanks for your help!
    (i´m using XCode 4)

  3. Hi!

    Please try not to use the calls in MAYS. The correct way should be:

    – (IBAction)changeRot:(id)sender
    {
    [OpenGLview rotate];
    }

    – (IBAction)resetView:(id)sender
    {
    [OpenGLview resetGLView];
    }

    Could you please try? I’ll try to recompile the code with Xcode 4 and check if something is wrong… (probably the code is outdated…)

Leave a Reply

Your email address will not be published. Required fields are marked *

spammer, go home! * Time limit is exhausted. Please reload the CAPTCHA.

This site uses Akismet to reduce spam. Learn how your comment data is processed.