Chapter 14. OpenGL on MacOS X

by Michael Sweet

WHAT YOU'LL LEARN IN THIS CHAPTER:

How To

Functions You'll Use

Choose appropriate pixel formats for OpenGL rendering

aglChoosePixelFormat

Manage OpenGL drawing contexts

aglCreateContext, aglDestroyContext, aglSetCurrentContext, aglSetDrawable

Do double-buffered drawing

aglSwapBuffers

Create bitmap text fonts

aglUseFont

This chapter discusses the OpenGL interfaces on MacOS X. We cover both AGL and NSOpenGL, the Carbon and Cocoa interfaces, respectively. You learn how to create and manage OpenGL contexts as well as how to create OpenGL drawing areas for Carbon and Cocoa applications. This chapter also shows you how to use GLUT on MacOS X with all the examples in this book.

The Basics

OpenGL on MacOS X is exposed via three APIs: AGL for Carbon applications, NSOpenGL for Cocoa applications, and CGL for applications that need direct access to the screen. This chapter discusses using AGL and NSOpenGL; the CGL API provides similar functionality, but because it bypasses the windowing system (no OpenGL in windows, only full screen), we do not cover it. There is, however, a tutorial for CGL on the book's Web site (see Appendix A, “Further Reading”).

In addition to the three standard APIs, the Apple developer tools also include a GLUT port that can be used to compile and use any of the GLUT-based examples in this book.

Frameworks

All the APIs discussed in this chapter are provided via frameworks in MacOS X. A framework is a collection of header files and libraries that provide specific functionality. Table 14.1 lists the OpenGL-related frameworks discussed in this chapter.

Table 14.1. OpenGL-Related Frameworks in MacOS X

Name

Description

AGL

Provides the OpenGL interface to Carbon (QuickDraw) windows.

ApplicationServices

Provides a common API for all GUI application services. This framework is used for all OpenGL applications.

Carbon

Provides a common API for MacOS X and earlier. This framework is used in combination with the AGL and OpenGL frameworks.

Cocoa

Provides the Aqua user interface classes. This framework is used in combination with the OpenGL framework.

OpenGL

Provides the common OpenGL API to the graphics hardware. This framework is used with any of the other frameworks to provide OpenGL rendering support.

Using the GLUT API

The GLUT API is provided in the OpenGL examples folder of the Apple Developer Tools CD-ROM. To use the GLUT API, copy this folder to disk and then add the GLUT framework in the directory to the list of frameworks in the Xcode window for your application. Use the following linker options if you are building your software with a makefile:

-framework /path/to/GLUT.framework -framework AGL -framework
                     OpenGL -framework Carbon -framework ApplicationServices

Using the AGL and Carbon APIs

The AGL and Carbon APIs provide a relatively simple interface for C and C++ programs to display OpenGL graphics. Because Carbon-based applications are supported on MacOS 8 through X, you can use the AGL and Carbon APIs to develop applications that work on multiple versions of MacOS.

Programs of this type require the AGL, ApplicationServices, Carbon, and OpenGL frameworks; you can start with a Carbon-based application in the Xcode application and add the AGL and OpenGL frameworks or use the following linker options:

-framework AGL -framework OpenGL -framework Carbon -framework ApplicationServices

As you'll see in the following sections, AGL functions start with the prefix agl.

Pixel Formats

AGL exposes multiple pixel formats that provide different rendering capabilities. For example, a particular graphics card might be able to provide full-scene antialiasing with a 16-bit depth buffer but not with a 24-bit or 32-bit depth buffer due to limited memory or other implementation-specific issues.

The aglChoosePixelFormat function allows an application to choose an appropriate pixel format using a list of integer attributes describing the desired output capabilities. Table 14.2 lists the attribute constants defined by the AGL API.

Table 14.2. AGL Pixel Format Attribute List Constants

Constant

Description

AGL_ACCELERATED

This constant specifies that a hardware-accelerated pixel format is required.

AGL_ACCUM_ALPHA_SIZE

The number that follows specifies the minimum number of alpha bits that are required in the accumulation buffer.

AGL_ACCUM_BLUE_SIZE

The number that follows specifies the minimum number of blue bits that are required in the accumulation buffer.

AGL_ACCUM_GREEN_SIZE

The number that follows specifies the minimum number of green bits that are required in the accumulation buffer.

AGL_ACCUM_RED_SIZE

The number that follows specifies the minimum number of red bits that are required in the accumulation buffer.

AGL_ALPHA_SIZE

The number that follows specifies the minimum number of alpha bits that are required.

AGL_AUX_BUFFERS

The number that follows specifies the number of auxiliary buffers that are required.

AGL_BACKING_STORE

This constant specifies that only formats that support a full backing store capability should be used.

AGL_BLUE_SIZE

The number that follows specifies the minimum number of blue bits that are required.

AGL_BUFFER_SIZE

The number that follows specifies the number of color index bits that are desired.

AGL_CLOSEST_POLICY

This constant specifies that the sizes specified should be used as the ideal requirements for the pixel format, and the pixel format should use the closest number of bits possible.

AGL_DEPTH_SIZE

The number that follows specifies the minimum number of depth bits that are required.

AGL_DOUBLEBUFFER

This constant specifies that a double-buffered visual is desired.

AGL_FULLSCREEN

This constant specifies that the pixel format is for full-screen rendering.

AGL_GREEN_SIZE

The number that follows specifies the minimum number of green bits that are required.

AGL_LEVEL

This constant specifies the buffer level in the number that follows; 0 is the main buffer, 1 is the first overlay buffer, –1 is the first underlay buffer, and so forth.

AGL_MINIMUM_POLICY

This constant specifies that the sizes specified should be used as the minimum requirements for the pixel format, and the pixel format should use the minimum number of bits possible.

AGL_MAXIUM_POLICY

This constant specifies that the sizes specified should be used as the minimum requirements for the pixel format, and the pixel format should use the maximum number of bits possible.

AGL_NONE

This constant specifies the end of the attribute list.

AGL_OFFSCREEN

This constant specifies that the pixel format is for an offscreen buffer.

AGL_PIXEL_SIZE

The number that follows specifies the total number of bits that are used for a pixel.

AGL_RED_SIZE

The number that follows specifies the minimum number of red bits that are required.

AGL_RGBA

This constant specifies that an RGBA visual is desired.

AGL_STENCIL_SIZE

The number that follows specifies the minimum number of stencil bits that are required.

AGL_STEREO

This constant specifies that a stereo visual is desired; stereo visuals provide separate left and right eye images.

You might use the following code to find a double-buffered RGB pixel format:

GLint attributes[] = {
  AGL_RGBA,
  AGL_DOUBLEBUFFER,
  AGL_RED_SIZE, 4,
  AGL_GREEN_SIZE, 4,
  AGL_BLUE_SIZE, 4,
  AGL_DEPTH_SIZE, 16,
  AGL_NONE
};
AGLPixelFormat format;

format = aglChoosePixelFormat(NULL, 0, attributes);

Note that the last value is required to be AGL_NONE. After aglChoosePixelFormat is called, an AGLPixelFormat value is returned; this value provides information on the correct pixel format to use. If no matching pixel format can be found, a NULL pointer is returned, and you should retry with different attributes or inform the users that the window cannot be displayed on their system.

Managing Contexts

AGL provides four functions for managing OpenGL rendering contexts: aglCreateContext, aglDestroyContext, aglSetCurrentContext, and aglSetDrawable. The aglCreateContext function creates an OpenGL context using the AGLPixelFormat value returned by the aglChoosePixelFormat function:

AGLContext context;

context = aglCreateContext(format, NULL);

The aglCreateContext function accepts two arguments: the pixel format and a context to share display list, texture object, and vertex array information with. Pass NULL if you do not want to share information with another context.

After you have created the context, it needs to be bound to a window or offscreen buffer using the aglSetDrawable function:

WindowPtr window;

aglSetDrawable(context, GetWindowPort(window));

Next, call the aglSetCurrentContext function to use the context to do OpenGL rendering:

aglSetCurrentContext(context);

Finally, when you are done using the context, call the aglDestroyContext function to release the resources that were used by the context:

aglDestroyContext(context);

Doing Double-Buffered Rendering

You enable double-buffered rendering by using the appropriate pixel format. The drawing code in your program then merely needs to call the aglSwapBuffers function after doing any OpenGL rendering to make it visible:

aglSwapBuffers(context);

Your First AGL Program

Listing 14.1 shows a basic Carbon application that creates a window for OpenGL rendering and displays a spinning cube. The application responds to mouse clicks and drags to alter the spinning of the cube and exits after the user closes the window. Figure 14.1 shows the result.

Example 14.1. The CARBON Sample Program

/*
 * Include necessary headers...
 */

#include <stdio.h>
#include <stdlib.h>
#include <Carbon/Carbon.h>
#include <AGL/agl.h>


/*
 * Globals...
 */

float           CubeRotation[3],        /* Rotation of cube */
                CubeRate[3];            /* Rotation rate of cube */
int             CubeMouseButton,        /* Button that was pressed */
                CubeMouseX,             /* Start X position of mouse */
                CubeMouseY;             /* Start Y position of mouse */
int             CubeX,                  /* X position of window */
                CubeY,                  /* Y position of window */
                CubeWidth,              /* Width of window */
                CubeHeight;             /* Height of window */
int             CubeVisible;            /* Is the window visible? */
AGLContext      CubeContext;            /* OpenGL context */


/*
 * Functions...
 */

void            DisplayFunc(void);
static pascal OSStatus
                EventHandler(EventHandlerCallRef nextHandler,
                             EventRef event, void *userData);
void            IdleFunc(void);
void            MotionFunc(int x, int y);
void            MouseFunc(int button, int state, int x, int y);
void            ReshapeFunc(int width, int height);


/*
 * 'main()' - Main entry for example program.
 */

int                                     /* O - Exit status */
main(int  argc,                         /* I - Number of command-line args */
     char *argv[])                      /* I - Command-line args */
{
  AGLPixelFormat        format;         /* OpenGL pixel format */
  WindowPtr             window;         /* Window */
  int                   winattrs;       /* Window attributes */
  Str255                title;          /* Title of window */
  Rect                  rect;           /* Rectangle definition */
  EventHandlerUPP       handler;        /* Event handler */
  EventLoopTimerUPP     thandler;       /* Timer handler */
  EventLoopTimerRef     timer;          /* Timer for animating the window */
  ProcessSerialNumber   psn;            /* Process serial number */
  static EventTypeSpec  events[] =      /* Events we are interested in... */
                        {
                          { kEventClassMouse, kEventMouseDown },
                          { kEventClassMouse, kEventMouseUp },
                          { kEventClassMouse, kEventMouseMoved },
                          { kEventClassMouse, kEventMouseDragged },
                          { kEventClassWindow, kEventWindowDrawContent },
                          { kEventClassWindow, kEventWindowShown },
                          { kEventClassWindow, kEventWindowHidden },
                          { kEventClassWindow, kEventWindowActivated },
                          { kEventClassWindow, kEventWindowDeactivated },
                          { kEventClassWindow, kEventWindowClose },
                          { kEventClassWindow, kEventWindowBoundsChanged }
                        };
  static GLint          attributes[] =  /* OpenGL attributes */
                        {
                          AGL_RGBA,
                          AGL_GREEN_SIZE, 1,
                          AGL_DOUBLEBUFFER,
                          AGL_DEPTH_SIZE, 16,
                          AGL_NONE
                        };

            //Set initial values for window
const int        origWinHeight = 628;
const int        origWinWidth  = 850;
const int        origWinXOffset = 50;
const int        origWinYOffset = 50;


 /*
  * Create the window...
  */

  CubeContext = 0;
  CubeVisible = 0;

  SetRect(&rect, origWinXOffset, origWinYOffset, origWinWidth, origWinHeight);

  winattrs = kWindowStandardHandlerAttribute | kWindowCloseBoxAttribute |
             kWindowCollapseBoxAttribute | kWindowFullZoomAttribute |
             kWindowResizableAttribute | kWindowLiveResizeAttribute;
  winattrs &= GetAvailableWindowAttributes(kDocumentWindowClass);

  strcpy(title + 1, "Carbon OpenGL Example");
  title[0] = strlen(title + 1);

  CreateNewWindow(kDocumentWindowClass, winattrs, &rect, &window);
  SetWTitle(window, title);

  handler = NewEventHandlerUPP(EventHandler);
  InstallWindowEventHandler(window, handler,
                            sizeof(events) / sizeof(events[0]),
                            events, NULL, 0L);
  thandler =
      NewEventLoopTimerUPP((void (*)(EventLoopTimerRef, void *))IdleFunc);
  InstallEventLoopTimer(GetMainEventLoop(), 0, 0, thandler,
                        0, &timer);

  GetCurrentProcess(&psn);
  SetFrontProcess(&psn);

  DrawGrowIcon(window);

  ShowWindow(window);

 /*
  * Create the OpenGL context and bind it to the window...
  */

  format      = aglChoosePixelFormat(NULL, 0, attributes);
  CubeContext = aglCreateContext(format, NULL);

  if (!CubeContext)
  {
    puts("Unable to get OpenGL context!");
    return (1);
  }

  aglDestroyPixelFormat(format);
  aglSetDrawable(CubeContext, GetWindowPort(window));

 /*
  * Setup remaining globals...
  */

  CubeX           = 50;
  CubeY           = 50;
  CubeWidth       = 400;
  CubeHeight      = 400;
  CubeRotation[0] = 45.0f;
  CubeRotation[1] = 45.0f;
  CubeRotation[2] = 45.0f;
  CubeRate[0]     = 1.0f;
  CubeRate[1]     = 1.0f;
  CubeRate[2]     = 1.0f;


//Set the initial size of the cube
ReshapeFunc(origWinWidth – origWinXOffset, origWinHeight – origWinYOffset);
 /*
  * Loop forever...
  */

  for (;;)
  {
    if (CubeVisible)
      SetEventLoopTimerNextFireTime(timer, 0.05);

    RunApplicationEventLoop();

    if (CubeVisible)
    {
     /*
      * Animate the cube...
      */

      DisplayFunc();
    }
  }
}


/*
 * 'DisplayFunc()' - Draw a cube.
 */

void
DisplayFunc(void)
{
  int                   i, j;           /* Looping vars */
float            aspectRatio,windowWidth, windowHeight;
  static const GLfloat  corners[8][3] = /* Corner vertices */
                        {
                          {  1.0f,  1.0f,  1.0f },  /* Front top right */
                          {  1.0f, -1.0f,  1.0f },  /* Front bottom right */
                          { -1.0f, -1.0f,  1.0f },      /* Front bottom left */
                          { -1.0f,  1.0f,  1.0f },      /* Front top left */
                          {  1.0f,  1.0f, -1.0f },      /* Back top right */
                          {  1.0f, -1.0f, -1.0f },      /* Back bottom right */
                          { -1.0f, -1.0f, -1.0f },      /* Back bottom left */
                          { -1.0f,  1.0f, -1.0f }       /* Back top left */
                        };
  static const int      sides[6][4] =   /* Sides */
                        {
                          { 0, 1, 2, 3 },               /* Front */
                          { 4, 5, 6, 7 },               /* Back */
                          { 0, 1, 5, 4 },               /* Right */
                          { 2, 3, 7, 6 },               /* Left */
                          { 0, 3, 7, 4 },               /* Top */
                          { 1, 2, 6, 5 }                /* Bottom */
                        };
  static const GLfloat  colors[6][3] =  /* Colors */
                        {
                          { 1.0f, 0.0f, 0.0f },         /* Red */
                          { 0.0f, 1.0f, 0.0f },         /* Green */
                          { 1.0f, 1.0f, 0.0f },         /* Yellow */
                          { 0.0f, 0.0f, 1.0f },         /* Blue */
                          { 1.0f, 0.0f, 1.0f },         /* Magenta */
                          { 0.0f, 1.0f, 1.0f }          /* Cyan */
                        };


 /*
  * Set the current OpenGL context...
  */

  aglSetCurrentContext(CubeContext);

 /*
  * Clear the window...
  */

  glViewport(0, 0, CubeWidth, CubeHeight);
  glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

 /*
  * Setup the matrices...
  */

  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
aspectRatio = (GLfloat)CubeWidth / (GLfloat)CubeHeight;
if(CubeWidth <= CubeHeight)
{
    windowWidth = 2.0f;
    windowHeight = 2.0f / aspectRatio;
    glOrtho(-2.0f, 2.0f, -windowHeight, windowHeight, 2.0f, -2.0f);
}
else
{
    windowWidth = 2.0f * aspectRatio;
    windowHeight = 2.0f;
    glOrtho(-windowHeight, windowHeight, -2.0f, 2.0f, 2.0f, -2.0f);

}

  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
  glRotatef(CubeRotation[0], 1.0f, 0.0f, 0.0f);
  glRotatef(CubeRotation[1], 0.0f, 1.0f, 0.0f);
  glRotatef(CubeRotation[2], 0.0f, 0.0f, 1.0f);

 /*
  * Draw the cube...
  */

  glEnable(GL_DEPTH_TEST);

  glBegin(GL_QUADS);

  for (i = 0; i < 6; i ++)
  {
    glColor3fv(colors[i]);
    for (j = 0; j < 4; j ++)
      glVertex3fv(corners[sides[i][j]]);
  }

  glEnd();

 /*
  * Swap the front and back buffers...
  */

  aglSwapBuffers(CubeContext);
}


/*
 * 'EventHandler()' - Handle window and mouse events from the window manager.
 */

static pascal OSStatus                 // O - noErr on success or error code
EventHandler(EventHandlerCallRef nextHandler,   /* I - Next handler to call */
             EventRef            event,         /* I - Event reference */
             void                *userData)     /* I - User data (not used) */
{
  UInt32                kind;                   /* Kind of event */
  Rect                  rect;                   /* New window size */
  EventMouseButton      button;                 /* Mouse button */
  Point                 point;                  /* Mouse position */


  kind = GetEventKind(event);

  if (GetEventClass(event) == kEventClassWindow)
  {
    switch (kind)
    {
      case kEventWindowDrawContent :
          if (CubeVisible && CubeContext)
            DisplayFunc();
          break;

      case kEventWindowBoundsChanged :
          GetEventParameter(event, kEventParamCurrentBounds, typeQDRectangle,
                            NULL, sizeof(Rect), NULL, &rect);

          CubeX = rect.left;
          CubeY = rect.top;

          if (CubeContext)
            aglUpdateContext(CubeContext);

          ReshapeFunc(rect.right - rect.left, rect.bottom - rect.top);

          if (CubeVisible && CubeContext)
            DisplayFunc();
          break;

      case kEventWindowShown :
          CubeVisible = 1;
          if (CubeContext)
            DisplayFunc();
          break;

      case kEventWindowHidden :
          CubeVisible = 0;
          break;

      case kEventWindowClose :
          ExitToShell();
          break;
    }
  }
  else
  {
    switch (kind)
    {
      case kEventMouseDown :
          GetEventParameter(event, kEventParamMouseButton, typeMouseButton,
                            NULL, sizeof(EventMouseButton), NULL, &button);
          GetEventParameter(event, kEventParamMouseLocation, typeQDPoint,
                            NULL, sizeof(Point), NULL, &point);

          if (point.v < CubeY ||
              (point.v > (CubeY + CubeHeight - 8) &&
               point.h > (CubeX + CubeWidth - 8)))
            return (CallNextEventHandler(nextHandler, event));

          MouseFunc(button, 0, point.h, point.v);
          break;

      case kEventMouseUp :
          GetEventParameter(event, kEventParamMouseButton, typeMouseButton,
                            NULL, sizeof(EventMouseButton), NULL, &button);
          GetEventParameter(event, kEventParamMouseLocation, typeQDPoint,
                            NULL, sizeof(Point), NULL, &point);

          if (point.v < CubeY ||
              (point.v > (CubeY + CubeHeight - 8) &&
               point.h > (CubeX + CubeWidth - 8)))
            return (CallNextEventHandler(nextHandler, event));

          MouseFunc(button, 1, point.h, point.v);
          break;

      case kEventMouseDragged :
          GetEventParameter(event, kEventParamMouseLocation, typeQDPoint,
                            NULL, sizeof(Point), NULL, &point);

          if (point.v < CubeY ||
              (point.v > (CubeY + CubeHeight - 8) &&
               point.h > (CubeX + CubeWidth - 8)))
            return (CallNextEventHandler(nextHandler, event));

          MotionFunc(point.h, point.v);
          break;

      default :
          return (CallNextEventHandler(nextHandler, event));
    }
  }

 /*
  * Return whether we handled the event...
  */

  return (noErr);
}


/*
 * 'IdleFunc()' - Rotate and redraw the cube.
 */

void
IdleFunc(void)
{
  CubeRotation[0] += CubeRate[0];
  CubeRotation[1] += CubeRate[1];
  CubeRotation[2] += CubeRate[2];

  QuitApplicationEventLoop();
}


/*
 * 'MotionFunc()' - Handle mouse pointer motion.
 */

void
MotionFunc(int x,                       /* I - X position */
           int y)                       /* I - Y position */
{
 /*
  * Get the mouse movement...
  */

  x -= CubeMouseX;
  y -= CubeMouseY;

 /*
  * Update the cube rotation rate based upon the mouse movement and
  * button...
  */

  switch (CubeMouseButton)
  {
    case 0 :                            /* Button 1 */
        CubeRate[0] = 0.01f * y;
        CubeRate[1] = 0.01f * x;
        CubeRate[2] = 0.0f;
        break;

    case 1 :                            /* Button 2 */
        CubeRate[0] = 0.0f;
        CubeRate[1] = 0.01f * y;
        CubeRate[2] = 0.01f * x;
        break;

    default :                           /* Button 3 */
        CubeRate[0] = 0.01f * y;
        CubeRate[1] = 0.0f;
        CubeRate[2] = 0.01f * x;
        break;
  }
}


/*
 * 'MouseFunc()' - Handle mouse button press/release events.
 */

void
MouseFunc(int button,                   /* I - Button that was pressed */
          int state,                    /* I - Button state (0 = down) */
          int x,                        /* I - X position */
          int y)                        /* I - Y position */
{
 /*
  * Only respond to button presses...
  */

  if (state)
    return;

 /*
  * Save the mouse state...
  */

  CubeMouseButton = button;
  CubeMouseX      = x;
  CubeMouseY      = y;

 /*
  * Zero-out the rotation rates...
  */

  CubeRate[0] = 0.0f;
  CubeRate[1] = 0.0f;
  CubeRate[2] = 0.0f;
}


/*
 * 'ReshapeFunc()' - Resize the window.
 */

void
ReshapeFunc(int width,                  /* I - Width of window */
            int height)                 /* I - Height of window */
{
  CubeWidth  = width;
  CubeHeight = height;
}
The AGL spinning cube example.

Figure 14.1. The AGL spinning cube example.

Using Bitmap Fonts

The AGL framework provides a single function for using bitmap fonts for OpenGL rendering: aglUseFont. This function works in conjunction with the Carbon GetFNum function and OpenGL glGenLists function to extract bitmaps and create display lists for each character you want in the font. The following code demonstrates how to extract the visible ASCII characters from the Courier New Bold font at a 14-pixel-high size:

GLint listbase;
short font;
Str255 fontname;

strcpy(fontname + 1, "Courier New");
fontname[0] = strlen(fontname + 1);

GetFNum(fontname, &font);

listbase = glGenLists(96);

aglUseFont(context, font, bold, 14, ' ', 96, listbase);

The font number is used along with a font style (bold in this case) to select the actual font to render. The fifth and sixth arguments are the starting character and the number of characters to extract, respectively. The listbase variable in the example points to the start of a block of 96 consecutive display lists to use for each character.

After the characters are extracted, you can draw text using a combination of the glRasterPos(), glPushAttrib(), glListBase(), glCallLists(), and glPopAttrib() functions, as follows:

char *s = "Hello, World!";

glPushAttrib(GL_LIST_BIT);
  glListBase(listbase - ' '),

  glRasterPos3f(0.0f, 0.0f, 0.0f);
  glCallLists(strlen(s), GL_BYTE, s);
glPopAttrib();

The example in Listing 14.2 uses this code to draw the names of each side of the cube. Figure 14.2 shows the result.

Example 14.2. The CARBONFONTS Sample Program

/*
 * Include necessary headers...
 */

#include <stdio.h>
#include <stdlib.h>
#include <Carbon/Carbon.h>
#include <AGL/agl.h>


/*
 * Globals...
 */

float           CubeRotation[3],        /* Rotation of cube */
                CubeRate[3];            /* Rotation rate of cube */
int             CubeMouseButton,        /* Button that was pressed */
                CubeMouseX,             /* Start X position of mouse */
                CubeMouseY;             /* Start Y position of mouse */
int             CubeX,                  /* X position of window */
                CubeY,                  /* Y position of window */
                CubeWidth,              /* Width of window */
                CubeHeight;             /* Height of window */
int             CubeVisible;            /* Is the window visible? */
AGLContext      CubeContext;            /* OpenGL context */
GLuint          CubeFont;               /* Display list base for font */


/*
 * Functions...
 */

void            DisplayFunc(void);
static pascal OSStatus
                EventHandler(EventHandlerCallRef nextHandler,
                             EventRef event, void *userData);
void            IdleFunc(void);
void            MotionFunc(int x, int y);
void            MouseFunc(int button, int state, int x, int y);
void            ReshapeFunc(int width, int height);


/*
 * 'main()' - Main entry for example program.
 */

int                                     /* O - Exit status */
main(int  argc,                         /* I - Number of command-line args */
     char *argv[])                      /* I - Command-line args */
{
  AGLPixelFormat        format;         /* OpenGL pixel format */
  WindowPtr             window;         /* Window */
  int                   winattrs;       /* Window attributes */
  Str255                title;          /* Title of window */
  Rect                  rect;           /* Rectangle definition */
  EventHandlerUPP       handler;        /* Event handler */
  EventLoopTimerUPP     thandler;       /* Timer handler */
  EventLoopTimerRef     timer;          /* Timer for animating the window */
  ProcessSerialNumber   psn;            /* Process serial number */
  short                 font;           /* Font number */
  Str255                fontname;       /* Font name */
  static EventTypeSpec  events[] =      /* Events we are interested in... */
                        {
                          { kEventClassMouse, kEventMouseDown },
                          { kEventClassMouse, kEventMouseUp },
                          { kEventClassMouse, kEventMouseMoved },
                          { kEventClassMouse, kEventMouseDragged },
                          { kEventClassWindow, kEventWindowDrawContent },
                          { kEventClassWindow, kEventWindowShown },
                          { kEventClassWindow, kEventWindowHidden },
                          { kEventClassWindow, kEventWindowActivated },
                          { kEventClassWindow, kEventWindowDeactivated },
                          { kEventClassWindow, kEventWindowClose },
                          { kEventClassWindow, kEventWindowBoundsChanged }
                        };
  static GLint          attributes[] =  /* OpenGL attributes */
                        {
                          AGL_RGBA,
                          AGL_GREEN_SIZE, 1,
                          AGL_DOUBLEBUFFER,
                          AGL_DEPTH_SIZE, 16,
                          AGL_NONE
                        };

            //Set initial values for window
const int        origWinHeight = 628;
const int        origWinWidth  = 850;
const int        origWinXOffset = 50;
const int        origWinYOffset = 50;


 /*
  * Create the window...
  */

  CubeContext = 0;
  CubeVisible = 0;

  SetRect(&rect, origWinXOffset, origWinYOffset, origWinWidth, origWinHeight);

  winattrs = kWindowStandardHandlerAttribute | kWindowCloseBoxAttribute |
             kWindowCollapseBoxAttribute | kWindowFullZoomAttribute |
             kWindowResizableAttribute | kWindowLiveResizeAttribute;
  winattrs &= GetAvailableWindowAttributes(kDocumentWindowClass);

  strcpy(title + 1, "Carbon OpenGL Example");
  title[0] = strlen(title + 1);

  CreateNewWindow(kDocumentWindowClass, winattrs, &rect, &window);
  SetWTitle(window, title);

  handler = NewEventHandlerUPP(EventHandler);
  InstallWindowEventHandler(window, handler,
                            sizeof(events) / sizeof(events[0]),
                            events, NULL, 0L);
  thandler =
      NewEventLoopTimerUPP((void (*)(EventLoopTimerRef, void *))IdleFunc);
  InstallEventLoopTimer(GetMainEventLoop(), 0, 0, thandler,
                        0, &timer);

  GetCurrentProcess(&psn);
  SetFrontProcess(&psn);

  DrawGrowIcon(window);

  ShowWindow(window);

 /*
  * Create the OpenGL context and bind it to the window...
  */

  format      = aglChoosePixelFormat(NULL, 0, attributes);
  CubeContext = aglCreateContext(format, NULL);

  if (!CubeContext)
  {
    puts("Unable to get OpenGL context!");
    return (1);
  }

  aglDestroyPixelFormat(format);
  aglSetDrawable(CubeContext, GetWindowPort(window));

 /*
  * Setup remaining globals...
  */

  CubeX           = 50;
  CubeY           = 50;
  CubeWidth       = 400;
  CubeHeight      = 400;
  CubeRotation[0] = 45.0f;
  CubeRotation[1] = 45.0f;
  CubeRotation[2] = 45.0f;
  CubeRate[0]     = 1.0f;
  CubeRate[1]     = 1.0f;
  CubeRate[2]     = 1.0f;

 /*
  * Setup font...
  */

  strcpy(fontname + 1, "Courier New");
  fontname[0] = strlen(fontname + 1);

  GetFNum(fontname, &font);

  CubeFont = glGenLists(96);

  aglUseFont(CubeContext, font, bold, 14, ' ', 96, CubeFont);


//Set the initial size of the cube
ReshapeFunc(origWinWidth – origWinXOffset, origWinHeight – origWinYOffset);

 /*
  * Loop forever...
  */

  for (;;)
  {
    if (CubeVisible)
      SetEventLoopTimerNextFireTime(timer, 0.05);

    RunApplicationEventLoop();

    if (CubeVisible)
    {
     /*
      * Animate the cube...
      */

      DisplayFunc();
    }
  }
}


/*
 * 'DisplayFunc()' - Draw a cube.
 */

void
DisplayFunc(void)
{
  int                   i, j;           /* Looping vars */
float            aspectRatio,windowWidth, windowHeight;

  static const GLfloat  corners[8][3] = /* Corner vertices */
                        {
                          {  1.0f,  1.0f,  1.0f },  /* Front top right */
                          {  1.0f, -1.0f,  1.0f },  /* Front bottom right */
                          { -1.0f, -1.0f,  1.0f },      /* Front bottom left */
                          { -1.0f,  1.0f,  1.0f },      /* Front top left */
                          {  1.0f,  1.0f, -1.0f },      /* Back top right */
                          {  1.0f, -1.0f, -1.0f },      /* Back bottom right */
                          { -1.0f, -1.0f, -1.0f },      /* Back bottom left */
                          { -1.0f,  1.0f, -1.0f }       /* Back top left */
                        };
  static const int      sides[6][4] =   /* Sides */
                        {
                          { 0, 1, 2, 3 },               /* Front */
                          { 4, 5, 6, 7 },               /* Back */
                          { 0, 1, 5, 4 },               /* Right */
                          { 2, 3, 7, 6 },               /* Left */
                          { 0, 3, 7, 4 },               /* Top */
                          { 1, 2, 6, 5 }                /* Bottom */
                        };
  static const GLfloat  colors[6][3] =  /* Colors */
                        {
                          { 1.0f, 0.0f, 0.0f },         /* Red */
                          { 0.0f, 1.0f, 0.0f },         /* Green */
                          { 1.0f, 1.0f, 0.0f },         /* Yellow */
                          { 0.0f, 0.0f, 1.0f },         /* Blue */
                          { 1.0f, 0.0f, 1.0f },         /* Magenta */
                          { 0.0f, 1.0f, 1.0f }          /* Cyan */
                        };


 /*
  * Set the current OpenGL context...
  */

  aglSetCurrentContext(CubeContext);

 /*
  * Clear the window...
  */

  glViewport(0, 0, CubeWidth, CubeHeight);
  glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

 /*
  * Setup the matrices...
  */

  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
aspectRatio = (GLfloat)CubeWidth / (GLfloat)CubeHeight;
if(CubeWidth <= CubeHeight)
{
    windowWidth = 2.0f;
    windowHeight = 2.0f / aspectRatio;
    glOrtho(-2.0f, 2.0f, -windowHeight, windowHeight, 2.0f, -2.0f);
}
else
{
    windowWidth = 2.0f * aspectRatio;
    windowHeight = 2.0f;
    glOrtho(-windowHeight, windowHeight, -2.0f, 2.0f, 2.0f, -2.0f);

}

  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
  glRotatef(CubeRotation[0], 1.0f, 0.0f, 0.0f);
  glRotatef(CubeRotation[1], 0.0f, 1.0f, 0.0f);
  glRotatef(CubeRotation[2], 0.0f, 0.0f, 1.0f);

 /*
  * Draw the cube...
  */

  glEnable(GL_DEPTH_TEST);

  glBegin(GL_QUADS);

  for (i = 0; i < 6; i ++)
  {
    glColor3fv(colors[i]);
    for (j = 0; j < 4; j ++)
      glVertex3fv(corners[sides[i][j]]);
  }

  glEnd();

 /*
  * Draw lines coming out of the cube...
  */

  glColor3f(1.0f, 1.0f, 1.0f);

  glBegin(GL_LINES);
    glVertex3f(0.0f, 0.0f, -1.5f);
    glVertex3f(0.0f, 0.0f, 1.5f);

    glVertex3f(-1.5f, 0.0f, 0.0f);
    glVertex3f(1.5f, 0.0f, 0.0f);

    glVertex3f(0.0f, 1.5f, 0.0f);
    glVertex3f(0.0f, -1.5f, 0.0f);
  glEnd();

 /*
  * Draw text for each side...
  */

  glPushAttrib(GL_LIST_BIT);
    glListBase(CubeFont - ' '),

    glRasterPos3f(0.0f, 0.0f, -1.5f);
    glCallLists(4, GL_BYTE, "Back");

    glRasterPos3f(0.0f, 0.0f, 1.5f);
    glCallLists(5, GL_BYTE, "Front");

    glRasterPos3f(-1.5f, 0.0f, 0.0f);
    glCallLists(4, GL_BYTE, "Left");

    glRasterPos3f(1.5f, 0.0f, 0.0f);
    glCallLists(5, GL_BYTE, "Right");

    glRasterPos3f(0.0f, 1.5f, 0.0f);
    glCallLists(3, GL_BYTE, "Top");

    glRasterPos3f(0.0f, -1.5f, 0.0f);
    glCallLists(6, GL_BYTE, "Bottom");
  glPopAttrib();

 /*
  * Swap the front and back buffers...
  */

  aglSwapBuffers(CubeContext);
}


/*
 * 'EventHandler()' - Handle window and mouse events from the window manager.
 */

static pascal OSStatus           /* O - noErr on success or error code */
EventHandler(EventHandlerCallRef nextHandler,   /* I - Next handler to call */
             EventRef            event,         /* I - Event reference */
             void                *userData)     /* I - User data (not used) */
{
  UInt32                kind;                   /* Kind of event */
  Rect                  rect;                   /* New window size */
  EventMouseButton      button;                 /* Mouse button */
  Point                 point;                  /* Mouse position */


  kind = GetEventKind(event);

  if (GetEventClass(event) == kEventClassWindow)
  {
    switch (kind)
    {
      case kEventWindowDrawContent :
          if (CubeVisible && CubeContext)
            DisplayFunc();
          break;

      case kEventWindowBoundsChanged :
          GetEventParameter(event, kEventParamCurrentBounds, typeQDRectangle,
                            NULL, sizeof(Rect), NULL, &rect);

          CubeX = rect.left;
          CubeY = rect.top;

          if (CubeContext)
            aglUpdateContext(CubeContext);

          ReshapeFunc(rect.right - rect.left, rect.bottom - rect.top);

          if (CubeVisible && CubeContext)
            DisplayFunc();
          break;

      case kEventWindowShown :
          CubeVisible = 1;
          if (CubeContext)
            DisplayFunc();
          break;

      case kEventWindowHidden :
          CubeVisible = 0;
          break;

      case kEventWindowClose :
          ExitToShell();
          break;
    }
  }
  else
  {
    switch (kind)
    {
      case kEventMouseDown :
          GetEventParameter(event, kEventParamMouseButton, typeMouseButton,
                            NULL, sizeof(EventMouseButton), NULL, &button);
          GetEventParameter(event, kEventParamMouseLocation, typeQDPoint,
                            NULL, sizeof(Point), NULL, &point);

          if (point.v < CubeY ||
              (point.v > (CubeY + CubeHeight - 8) &&
               point.h > (CubeX + CubeWidth - 8)))
            return (CallNextEventHandler(nextHandler, event));

          MouseFunc(button, 0, point.h, point.v);
          break;

      case kEventMouseUp :
          GetEventParameter(event, kEventParamMouseButton, typeMouseButton,
                            NULL, sizeof(EventMouseButton), NULL, &button);
          GetEventParameter(event, kEventParamMouseLocation, typeQDPoint,
                            NULL, sizeof(Point), NULL, &point);

          if (point.v < CubeY ||
              (point.v > (CubeY + CubeHeight - 8) &&
               point.h > (CubeX + CubeWidth - 8)))
            return (CallNextEventHandler(nextHandler, event));

          MouseFunc(button, 1, point.h, point.v);
          break;

      case kEventMouseDragged :
          GetEventParameter(event, kEventParamMouseLocation, typeQDPoint,
                            NULL, sizeof(Point), NULL, &point);

          if (point.v < CubeY ||
              (point.v > (CubeY + CubeHeight - 8) &&
               point.h > (CubeX + CubeWidth - 8)))
            return (CallNextEventHandler(nextHandler, event));

          MotionFunc(point.h, point.v);
          break;

      default :
          return (CallNextEventHandler(nextHandler, event));
    }
  }

 /*
  * Return whether we handled the event...
  */

  return (noErr);
}


/*
 * 'IdleFunc()' - Rotate and redraw the cube.
 */

void
IdleFunc(void)
{
  CubeRotation[0] += CubeRate[0];
  CubeRotation[1] += CubeRate[1];
  CubeRotation[2] += CubeRate[2];

  QuitApplicationEventLoop();
}


/*
 * 'MotionFunc()' - Handle mouse pointer motion.
 */

void
MotionFunc(int x,                       /* I - X position */
           int y)                       /* I - Y position */
{
 /*
  * Get the mouse movement...
  */

  x -= CubeMouseX;
  y -= CubeMouseY;

 /*
  * Update the cube rotation rate based upon the mouse movement and
  * button...
  */

  switch (CubeMouseButton)
  {
    case 0 :                            /* Button 1 */
        CubeRate[0] = 0.01f * y;
        CubeRate[1] = 0.01f * x;
        CubeRate[2] = 0.0f;
        break;

    case 1 :                            /* Button 2 */
        CubeRate[0] = 0.0f;
        CubeRate[1] = 0.01f * y;
        CubeRate[2] = 0.01f * x;
        break;

    default :                           /* Button 3 */
        CubeRate[0] = 0.01f * y;
        CubeRate[1] = 0.0f;
        CubeRate[2] = 0.01f * x;
        break;
  }
}


/*
 * 'MouseFunc()' - Handle mouse button press/release events.
 */

void
MouseFunc(int button,                   /* I - Button that was pressed */
          int state,                    /* I - Button state (0 = down) */
          int x,                        /* I - X position */
          int y)                        /* I - Y position */
{
 /*
  * Only respond to button presses...
  */

  if (state)
    return;

 /*
  * Save the mouse state...
  */

  CubeMouseButton = button;
  CubeMouseX      = x;
  CubeMouseY      = y;

 /*
  * Zero-out the rotation rates...
  */

  CubeRate[0] = 0.0f;
  CubeRate[1] = 0.0f;
  CubeRate[2] = 0.0f;
}


/*
 * 'ReshapeFunc()' - Resize the window.
 */

void
ReshapeFunc(int width,                  /* I - Width of window */
            int height)                 /* I - Height of window */
{
  CubeWidth  = width;
  CubeHeight = height;
}
The Carbon spinning cube with fonts.

Figure 14.2. The Carbon spinning cube with fonts.

Using the Cocoa API

The Cocoa API is suitable for applications developed using Objective C and the Cocoa user interface classes. Cocoa provides a single OpenGL rendering class that you must subclass to do OpenGL rendering.

Cocoa programs of this type require the ApplicationServices, Cocoa, and OpenGL frameworks; you can start with a Cocoa-based application in the project builder application and add the OpenGL framework or use the following linker options:

-framework Cocoa -framework OpenGL -framework ApplicationServices

The NSOpenGL Class

The NSOpenGL class provides the basis for all OpenGL-based Cocoa components. You must subclass this class to implement a Cocoa-based OpenGL display, implementing three required methods: basicPixelFormat, drawRect, and initWithFrame. The basicPixelFormat method is used to create a copy of an attribute array similar to that used by the aglChoosePixelFormat function. The following code shows a typical implementation that requests a double-buffered pixel format with at least 16 bits of depth buffer:

+ (NSOpenGLPixelFormat*) basicPixelFormat
{
  static NSOpenGLPixelFormatAttribute attributes[] =
  {
    NSOpenGLPFAWindow,
    NSOpenGLPFADoubleBuffer,
    NSOpenGLPFADepthSize, (NSOpenGLPixelFormatAttribute)16,
    (NSOpenGLPixelFormatAttribute)nil
  };

  return ([[[NSOpenGLPixelFormat alloc] initWithAttributes] autoRelease]);
}

Each attribute value is of type NSOpenGLPixelFormatAttribute and the attribute list is terminated by a nil (zero) value. Table 14.3 lists the valid attribute values.

Table 14.3. NSOpenGLPixelFormatAttribute Constants

Constant

Description

NSOpenGLPFAAccelerated

This constant specifies that a hardware-accelerated pixel format is required.

NSOpenGLPFAAccumAlphaSize

The number that follows specifies the minimum number of alpha bits that are required in the accumulation buffer.

NSOpenGLPFAAccumBlueSize

The number that follows specifies the minimum number of blue bits that are required in the accumulation buffer.

NSOpenGLPFAAccumGreenSize

The number that follows specifies the minimum number of green bits that are required in the accumulation buffer.

NSOpenGLPFAAccumRedSize

The number that follows specifies the minimum number of red bits that are required in the accumulation buffer.

NSOpenGLPFAAlphaSize

The number that follows specifies the minimum number of alpha bits that are required.

NSOpenGLPFAAuxBuffers

The number that follows specifies the number of auxiliary buffers that are required.

NSOpenGLPFABackingStore

This constant specifies that only formats that support a full backing store capability should be used.

NSOpenGLPFABlueSize

The number that follows specifies the minimum number of blue bits that are required.

NSOpenGLPFABufferSize

The number that follows specifies the number of color index bits that are desired.

NSOpenGLPFAClosestPolicy

This constant specifies that the sizes specified should be used as the ideal requirements for the pixel format, and the pixel format should use the closest number of bits possible.

NSOpenGLPFADepthSize

The number that follows specifies the minimum number of depth bits that are required.

NSOpenGLPFADoubleBuffer

This constant specifies that a double-buffered visual is desired.

NSOpenGLPFAGreenSize

The number that follows specifies the minimum number of green bits that are required.

NSOpenGLPFALevel

This constant specifies the buffer level in the number that follows; 0 is the main buffer, 1 is the first overlay buffer, –1 is the first underlay buffer, and so forth.

NSOpenGLPFAMinimumPolicy

This constant specifies that the sizes specified should be used as the minimum requirements for the pixel format, and the pixel format should use the minimum number of bits possible.

NSOpenGLPFAMaximumPolicy

This constant specifies that the sizes specified should be used as the minimum requirements for the pixel format, and the pixel format should use the maximum number of bits possible.

NSOpenGLPFAOffScreen

This constant specifies that the pixel format is for an offscreen buffer.

NSOpenGLPFAPixelSize

The number that follows specifies the total number of bits that are used for a pixel.

NSOpenGLPFARedSize

The number that follows specifies the minimum number of red bits that are required.

NSOpenGLPFAStencilSize

The number that follows specifies the minimum number of stencil bits that are required.

NSOpenGLPFAStereo

This constant specifies that a stereo visual is desired; stereo visuals provide separate left and right eye images.

The drawRect method does any OpenGL drawing calls to redraw the widget; the rect argument can be used to set the viewport and viewing transformation as necessary:

- (void)drawRect:(NSRect)rect
{
  int width, height;


  // Get the current bounding rectangle...
  width  = rect.size.width;
  height = rect.size.height;

  // Set the viewport...
  glViewport(0, 0, width, height);

  // Setup the projection matrix...
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  glOrtho(-2.0f, 2.0f,
          -2.0f * height / width, 2.0f * height / width,
          -2.0f, 2.0f);

  // Call any OpenGL functions to draw the scene...
  ...
}

Finally, the initWithFrame method initializes the OpenGL drawing area:

- (id)initWithFrame:(NSRect)frameRect
{
  NSOpenGLPixelFormat *pf;


  // Get the pixel format and return a new window from it...
  pf   = [MyClass basicPixelFormat];
  self = [super initWithFrame: frameRect pixelFormat: pf];

  return (self);
}

The NSOpenGL class handles the creation of an OpenGL context for use with the pixel format that you have supplied.

Your First Cocoa Program

Now that we have covered the basics of using the NSOpenGL class for Cocoa applications, we will convert the Carbon Cube example to Cocoa. All the Carbon code is fully contained in a class called Cube that is based on NSOpenGL and implements all the required methods described in the previous section. It also implements the mouse methods so that you can click and drag the mouse to rotate the cube. Listing 14.3 shows the completed Cocoa application that displays a spinning cube. The results are shown in Figure 14.3.

Example 14.3. The cocoa.m Sample Program

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

// Interface definition for our NIB CustomView
// In the NIB builder, derive as subclass named
// Cube from NSOpenGLView. Then assign this to a
// customview that fills the window.
@interface Cube : NSOpenGLView
    {
    bool           initialized;
    NSTimer        *timer;
    float          rotation[3],
                   rate[3];
    int            mouse_x,
                   mouse_y;
    GLuint         font;
    }
@end

//
// 'main()' - Main entry for program.
//

int                                     // O - Exit status
main(int  argc,                         // I - Number of command-line args
     const char *argv[])                // I - Command-line arguments
{
  return (NSApplicationMain(argc, argv));
}


//
// Cube class based upon NSOpenGLView
//

@implementation Cube : NSOpenGLView
{
  bool          initialized;            // Are we initialized?
  NSTimer       *timer;                 // Timer for animation

  float         rotation[3],            // Rotation of cube
                rate[3];                // Rotation rate of cube
  int           mouse_x,                // Start X position of mouse
                mouse_y;                // Start Y position of mouse
}

//
// 'basicPixelFormat()' - Set the pixel format for the window.
//

+ (NSOpenGLPixelFormat*) basicPixelFormat
{
  static NSOpenGLPixelFormatAttribute   attributes[] =  // OpenGL attributes
    {
    NSOpenGLPFAWindow,
    NSOpenGLPFADoubleBuffer,
    NSOpenGLPFADepthSize, (NSOpenGLPixelFormatAttribute)16,
                                          (NSOpenGLPixelFormatAttribute)nil
                                        };

  return ([[[NSOpenGLPixelFormat alloc] initWithAttributes:attributes]autorelease]);
}

//
// 'resizeGL()' - Resize the window.
//

- (void) resizeGL
{
}


//
// 'idle()' - Update the display using the current rotation rates...
//

- (void)idle:(NSTimer *)timer
{
  // Rotate...
  rotation[0] += rate[0];
  rotation[1] += rate[1];
  rotation[2] += rate[2];

  // Redraw the window...
  [self drawRect:[self bounds]];
}


//
// 'mouseDown()' - Handle left mouse button presses...
//

- (void)mouseDown:(NSEvent *)theEvent
{
  NSPoint       point;                  // Mouse position


  // Get and save the mouse position
  point   = [self convertPoint:[theEvent locationInWindow] fromView:nil];
  mouse_x = point.x;
  mouse_y = point.y;

  // Null the rotation rates...
  rate[0] = 0.0f;
  rate[1] = 0.0f;
  rate[2] = 0.0f;
}


//
// 'rightMouseDown()' - Handle right mouse button presses...
//

- (void)rightMouseDown:(NSEvent *)theEvent
{
  NSPoint       point;                  // Mouse position


  // Get and save the mouse position
  point   = [self convertPoint:[theEvent locationInWindow] fromView:nil];
  mouse_x = point.x;
  mouse_y = point.y;

  // Null the rotation rates...
  rate[0] = 0.0f;
  rate[1] = 0.0f;
  rate[2] = 0.0f;
}


//
// 'otherMouseDown()' - Handle middle mouse button presses...
//

- (void)otherMouseDown:(NSEvent *)theEvent
{
  NSPoint       point;                  // Mouse position


  // Get and save the mouse position
  point   = [self convertPoint:[theEvent locationInWindow] fromView:nil];
  mouse_x = point.x;
  mouse_y = point.y;

  // Null the rotation rates...
  rate[0] = 0.0f;
  rate[1] = 0.0f;
  rate[2] = 0.0f;
}


//
// 'mouseDragged()' - Handle drags using the left mouse button.
//

- (void)mouseDragged:(NSEvent *)theEvent
{
  NSPoint       point;                  // Mouse position


  // Get the mouse position and update the rotation rates...
  point   = [self convertPoint:[theEvent locationInWindow] fromView:nil];
  rate[0] = 0.01f * (mouse_y - point.y);
  rate[1] = 0.01f * (mouse_x - point.x);
}


//
// 'rightMouseDragged()' - Handle drags using the right mouse button.
//

- (void)rightMouseDragged:(NSEvent *)theEvent
{
  NSPoint       point;                  // Mouse position


  // Get the mouse position and update the rotation rates...
  point   = [self convertPoint:[theEvent locationInWindow] fromView:nil];
  rate[0] = 0.01f * (mouse_y - point.y);
  rate[2] = 0.01f * (mouse_x - point.x);
}


//
// 'otherMouseDragged()' - Handle drags using the middle mouse button.
//

- (void)otherMouseDragged:(NSEvent *)theEvent
{
  NSPoint       point;                  // Mouse position


  // Get the mouse position and update the rotation rates...
  point   = [self convertPoint:[theEvent locationInWindow] fromView:nil];
  rate[1] = 0.01f * (mouse_y - point.y);
  rate[2] = 0.01f * (mouse_x - point.x);
}


- (void)drawRect:(NSRect)rect
{

  int                   width,          // Width of window
                        height;         // Height of window
  int                   i, j;           // Looping vars
    float         aspectRatio, windowWidth, windowHeight;
  static const GLfloat  corners[8][3] = // Corner vertices
                        {
                          {  1.0f,  1.0f,  1.0f },      // Front top right
                          {  1.0f, -1.0f,  1.0f },      // Front bottom right
                          { -1.0f, -1.0f,  1.0f },      // Front bottom left
                          { -1.0f,  1.0f,  1.0f },      // Front top left
                          {  1.0f,  1.0f, -1.0f },      // Back top right
                          {  1.0f, -1.0f, -1.0f },      // Back bottom right
                          { -1.0f, -1.0f, -1.0f },      // Back bottom left
                          { -1.0f,  1.0f, -1.0f }       // Back top left
                        };
  static const int      sides[6][4] =   // Sides
                        {
                          { 0, 1, 2, 3 },               // Front
                          { 4, 5, 6, 7 },               // Back
                          { 0, 1, 5, 4 },               // Right
                          { 2, 3, 7, 6 },               // Left
                          { 0, 3, 7, 4 },               // Top
                          { 1, 2, 6, 5 }                // Bottom
                        };
  static const GLfloat  colors[6][3] =  // Colors
                        {
                          { 1.0f, 0.0f, 0.0f },         // Red
                          { 0.0f, 1.0f, 0.0f },         // Green
                          { 1.0f, 1.0f, 0.0f },         // Yellow
                          { 0.0f, 0.0f, 1.0f },         // Blue
                          { 1.0f, 0.0f, 1.0f },         // Magenta
                          { 0.0f, 1.0f, 1.0f }          // Cyan
                        };


  // Set the current OpenGL context...
  if (!initialized)
  {
    rotation[0] = 45.0f;
    rotation[1] = 45.0f;
    rotation[2] = 45.0f;
    rate[0]     = 1.0f;
    rate[1]     = 1.0f;
    rate[2]     = 1.0f;
    initialized = true;
  }


  // Use the current bounding rectangle for the cube window...
  width  = rect.size.width;
  height = rect.size.height;

  // Clear the window...
  glViewport(0, 0, width, height);
  glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

  // Setup the matrices...
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();

aspectRatio = (GLfloat)width / (GLfloat)height;
if(width <= height)
{
    windowWidth = 2.0f;
    windowHeight = 2.0f / aspectRatio;
    glOrtho(-2.0f, 2.0f, -windowHeight, windowHeight, 2.0f, -2.0f);
}
else
{
    windowWidth = 2.0f * aspectRatio;
    windowHeight = 2.0f;
    glOrtho(-windowHeight, windowHeight, -2.0f, 2.0f, 2.0f, -2.0f);

}
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
  glRotatef(rotation[0], 1.0f, 0.0f, 0.0f);
  glRotatef(rotation[1], 0.0f, 1.0f, 0.0f);
  glRotatef(rotation[2], 0.0f, 0.0f, 1.0f);

  // Draw the cube...
  glEnable(GL_DEPTH_TEST);

  glBegin(GL_QUADS);

  for (i = 0; i < 6; i ++)
  {
    glColor3fv(colors[i]);
    for (j = 0; j < 4; j ++)
      glVertex3fv(corners[sides[i][j]]);
  }

  glEnd();

  // Draw lines coming out of the cube...
  glColor3f(1.0f, 1.0f, 1.0f);

  glBegin(GL_LINES);
    glVertex3f(0.0f, 0.0f, -1.5f);
    glVertex3f(0.0f, 0.0f, 1.5f);

    glVertex3f(-1.5f, 0.0f, 0.0f);
    glVertex3f(1.5f, 0.0f, 0.0f);

    glVertex3f(0.0f, 1.5f, 0.0f);
    glVertex3f(0.0f, -1.5f, 0.0f);
  glEnd();

  // Swap the front and back buffers...
  [[self openGLContext]flushBuffer];
}


//
// 'acceptsFirstResponder()' ...
//

- (BOOL)acceptsFirstResponder
{
  return (YES);
}


- (BOOL) becomeFirstResponder
{
  return (YES);
}


- (BOOL) resignFirstResponder
{
  return (YES);
}


//
// 'initWithFrame()' - Initialize the cube.
//

- (id)initWithFrame:(NSRect)frameRect
{
  NSOpenGLPixelFormat   *pf;


  // Get the pixel format and return a new cube window from it...
  pf   = [Cube basicPixelFormat];
  self = [super initWithFrame: frameRect pixelFormat: pf];

  return (self);
}


//
// 'awakeFromNib()' - Do stuff once the UI is loaded from the NIB file...
//

- (void)awakeFromNib
{
  // Set initial values...
  initialized = false;

//start cube rotating
[self drawRect:[self bounds]];
  // Start the timer running...
  timer = [NSTimer timerWithTimeInterval:(0.05f) target:self
           selector:@selector(idle:) userInfo:nil repeats:YES];
  [[NSRunLoop currentRunLoop]addTimer:timer forMode:NSDefaultRunLoopMode];
  [[NSRunLoop currentRunLoop]addTimer:timer forMode:NSEventTrackingRunLoopMode];
}

@end
The COCOACUBE program.

Figure 14.3. The COCOACUBE program.

Summary

OpenGL is well supported on MacOS X and can be accessed using several different APIs provided by Apple: AGL, CGL, Cocoa, and GLUT. The AGL API provides OpenGL support for Carbon-based applications and is supported on MacOS 8 through X, the CGL API provides full-screen OpenGL support and is meant for use with games and other full-screen applications, the Cocoa API is an Objective C interface for complex GUIs that include OpenGL-based components, and the GLUT API provides a simple, cross-platform interface for the sample programs in this book as well as for simple applications.

Reference

aglChoosePixelFormat

Purpose:

Enables you to choose a pixel format for OpenGL rendering.

Include File:

<AGL/agl.h>

Syntax:

AGLPixelFormat aglChoosePixelFormat(const
Reference AGLDevice *gdevs, GLint ndev, const GLint *attribs);

Description:

This function locates a compatible pixel format for all the listed devices. If ndev is 0, a pixel format is chosen that is compatible with all display devices.

Parameters:

*gdevs

const AGLDeviceAn array of graphics devices.

ndev

GLintThe number of graphics devices.

*attribs

const GLintAn array of integer attributes terminated by the AGL_NONE constant.

Returns:

A compatible pixel format or NULL if no pixel format satisfies the specified attributes.

See Also:

aglCreateContext

aglCreateContext

Purpose:

Creates an OpenGL context for rendering.

Include File:

<AGL/agl.h>

Syntax:

AGLContext aglCreateContext(AGLPixelFormat pix, 
const GLint:AGLContext share);

Description:

This function creates a new OpenGL rendering context. If the share argument is not NULL, the new context will share display lists, texture objects, and vertex arrays with the specified context.

Parameters:

pix

AGLPixelFormatThe pixel format to use, as returned by aglChoosePixelFormat.

share

AGLContextThe OpenGL context to share with or NULL.

Returns:

A new OpenGL context or NULL if it could not be created.

See Also:

aglChoosePixelFormat, aglDestroyContext, aglSetCurrentContext, aglSetDrawable

aglDestroyContext

Purpose:

To destroy an OpenGL rendering context.

Include File:

<AGL/agl.h>

Syntax:

GLboolean aglDestroyContext(AGLContext ctx);

Description:

This function destroys the specified OpenGL context.

Parameters:

ctx

AGLContextThe OpenGL context to destroy.

Returns:

GL_FALSE if the context could not be destroyed; GL_TRUE otherwise.

See Also:

aglCreateContext

aglSetCurrentContext

Purpose:

Sets the current context for OpenGL rendering.

Include File:

<AGL/agl.h>

Syntax:

GLboolean aglSetCurrentContext(AGLContext ctx);

Description:

This function sets the current OpenGL context for rendering.

Parameters:

ctx

AGLContextThe OpenGL context to use.

Returns:

GL_FALSE if the context could not be used; GL_TRUE otherwise.

See Also:

aglCreateContext, aglSetDrawable

aglSetDrawable

Purpose:

Sets the window or offscreen buffer associated with a context.

Include File:

<AGL/agl.h>

Syntax:

GLboolean aglSetDrawable(AGLContext ctx, 
AGLContext:AGLDrawable draw);

Description:

This function binds a window or offscreen buffer to an OpenGL context. You must call this function after creating the context and before using the context to do any rendering.

Parameters:

ctx

AGLContextThe OpenGL context to bind.

draw

AGLDrawableThe window or offscreen buffer to use.

Returns:

GL_FALSE if the drawable could not be bound; GL_TRUE otherwise.

See Also:

aglCreateContext, aglSetCurrentContext

AglSwapBuffers

Purpose:

Swaps the front and back buffers in an OpenGL window.

Include File:

<AGL/agl.h>

Syntax:

void aglSwapBuffers(AGLContext ctx);

Description:

This function swaps the front and back buffers of the double-buffered OpenGL window bound to the specified context. You typically call this function after drawing a scene or frame using OpenGL functions.

Parameters:

ctx

AGLContextThe OpenGL context to swap.

Returns:

Nothing

See Also:

AglCreateContext

AglUseFont

Purpose:

Creates a collection of bitmap display lists.

Include File:

<AGL/agl.h>

Syntax:

GLboolean aglUseFont(AGLContext ctx, GLint fontID,
AGLContext: Style face, GLint size, int first, int count, int 
AGLContext:base);

Description:

This function creates count display lists containing bitmaps of characters in the specified font. You allocate the display lists for the bitmaps using the glGenLists function.

Parameters:

ctx

AGLContextSpecifies the current rendering context.

fontID

GLintSpecifies the font to use.

face

StyleSpecifies the font style to use.

size

GLintSpecifies the size of font to use.

first

intSpecifies the first character in the font to use.

count

intSpecifies the number of characters to use from the font.

base

intSpecifies the first display list to use as returned by glGenLists

Returns:

GL_TRUE if successful; GL_FALSE otherwise

See Also:

AglCreateContext

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset