Retired Document
Important: This sample code may not represent best practices for current development. The project may use deprecated symbols and illustrate technologies and techniques that are no longer recommended.
main.c
/* |
* main.c |
* Carbon OpenGL |
* |
* Created by ggs on 13 Nov 2002 |
Copyright: Copyright © 2002-2004 Apple Computer, Inc., All Rights Reserved |
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. |
("Apple") in consideration of your agreement to the following terms, and your |
use, installation, modification or redistribution of this Apple software |
constitutes acceptance of these terms. If you do not agree with these terms, |
please do not use, install, modify or redistribute this Apple software. |
In consideration of your agreement to abide by the following terms, and subject |
to these terms, Apple grants you a personal, non-exclusive license, under AppleÕs |
copyrights in this original Apple software (the "Apple Software"), to use, |
reproduce, modify and redistribute the Apple Software, with or without |
modifications, in source and/or binary forms; provided that if you redistribute |
the Apple Software in its entirety and without modifications, you must retain |
this notice and the following text and disclaimers in all such redistributions of |
the Apple Software. Neither the name, trademarks, service marks or logos of |
Apple Computer, Inc. may be used to endorse or promote products derived from the |
Apple Software without specific prior written permission from Apple. Except as |
expressly stated in this notice, no other rights or licenses, express or implied, |
are granted by Apple herein, including but not limited to any patent rights that |
may be infringed by your derivative works or by other works in which the Apple |
Software may be incorporated. |
The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO |
WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED |
WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN |
COMBINATION WITH YOUR PRODUCTS. |
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR |
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE |
GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION |
OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT |
(INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN |
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
* |
*/ |
#include <OpenGL/glu.h> |
#include <OpenGL/glext.h> |
#include "HIDSupport.h" |
#include "glCheck.h" |
#include "trackball.h" |
#include "SurfaceGeometry.h" |
#include "camera.h" |
#include "pbuffer.h" |
#include "main.h" |
// ================================== |
recVec gOrigin = {0.0, 0.0, 0.0}; |
// single set of interaction flags and states |
GLint gDollyPanStartPoint[2] = {0, 0}; |
GLfloat gTrackBallRotation [4] = {0.0f, 0.0f, 0.0f, 0.0f}; |
GLboolean gDolly = GL_FALSE; |
GLboolean gPan = GL_FALSE; |
GLboolean gTrackball = GL_FALSE; |
AGLContext gTrackingContextInfo = NULL; |
EventHandlerUPP gEvtHandler; // main event handler |
EventHandlerUPP gWinEvtHandler; // window event handler |
IBNibRef nibRef = NULL; |
AbsoluteTime gStartTime; |
char gErrorMessage[256] = ""; // buffer for error message output |
float gErrorTime = 0.0; |
// single pbuffer for offscreen rendering |
pRecPbuffer gpPbuffer = NULL; |
AGLContext gShareContext = NULL; |
#pragma mark ---- OpenGL Capabilities ---- |
// GL configuration info globals |
// see glcheck.h for more info |
GLCaps * gDisplayCaps = NULL; // array of GLCaps |
CGDisplayCount gNumDisplays = 0; |
// related DM change notification: |
DMExtendedNotificationUPP gConfigEDMUPP = NULL; |
static void getCurrentCaps (void) |
{ |
// Check for existing opengl caps here |
// This can be called again with same display caps array when display configurations are changed and |
// your info needs to be updated. Note, if you are doing dynmaic allocation, the number of displays |
// may change and thus you should always reallocate your display caps array. |
if (gDisplayCaps && HaveOpenGLCapsChanged (gDisplayCaps, gNumDisplays)) { // see if caps have changed |
free (gDisplayCaps); |
gDisplayCaps = NULL; |
} |
if (!gDisplayCaps) { // if we do not have caps |
CheckOpenGLCaps (0, NULL, &gNumDisplays); // will just update number of displays |
gDisplayCaps = (GLCaps*) malloc (sizeof (GLCaps) * gNumDisplays); |
CheckOpenGLCaps (gNumDisplays, gDisplayCaps, &gNumDisplays); |
} |
} |
#pragma mark ---- Utilities ---- |
// return float elpased time in seconds since app start |
static float getElapsedTime (void) |
{ |
float deltaTime = (float) AbsoluteDeltaToDuration (UpTime(), gStartTime); |
if (0 > deltaTime) // if negative microseconds |
deltaTime /= -1000000.0; |
else // else milliseconds |
deltaTime /= 1000.0; |
return deltaTime; |
} |
#pragma mark ---- Error Reporting ---- |
// C string to Pascal string |
static void cstr2pstr (StringPtr outString, const char *inString) |
{ |
unsigned short x = 0; |
do { |
outString [x + 1] = (unsigned char) inString [x]; |
x++; |
} while ((inString [x] != 0) && (x < 256)); |
outString [0] = x; |
} |
// --------------------------------- |
// error reporting as both window message and debugger string |
void reportError (char * strError) |
{ |
Str255 strErr = "\p"; |
gErrorTime = getElapsedTime (); |
sprintf (gErrorMessage, "Error: %s (at time: %0.1f secs)", strError, gErrorTime); |
// out as debug string |
cstr2pstr (strErr, gErrorMessage); |
DebugStr (strErr); |
fflush (stderr); |
} |
// --------------------------------- |
// if error dump agl errors to debugger string, return error |
OSStatus aglReportError (void) |
{ |
GLenum err = aglGetError(); |
if (AGL_NO_ERROR != err) { |
char errStr[256]; |
sprintf (errStr, "AGL: %s",(char *) aglErrorString(err)); |
reportError (errStr); |
} |
// ensure we are returning an OSStatus noErr if no error condition |
if (err == AGL_NO_ERROR) |
return noErr; |
else |
return (OSStatus) err; |
} |
// --------------------------------- |
// if error dump gl errors to debugger string, return error |
OSStatus glReportError (void) |
{ |
GLenum err = glGetError(); |
if (GL_NO_ERROR != err) { |
char errStr[256]; |
sprintf (errStr, "GL: %s",(char *) gluErrorString(err)); |
reportError (errStr); |
} |
// ensure we are returning an OSStatus noErr if no error condition |
if (err == GL_NO_ERROR) |
return noErr; |
else |
return (OSStatus) err; |
} |
#pragma mark ---- OpenGL Utilities ---- |
// C string drawing function |
void drawCStringGL (char * cstrOut, GLuint fontList) |
{ |
GLint i = 0; |
while (cstrOut [i]) |
glCallList (fontList + cstrOut[i++]); |
} |
// --------------------------------- |
// AGL bitmpp font setup |
GLuint buildFontGL (AGLContext ctx, GLint fontID, Style face, GLint size) |
{ |
GLuint listBase = glGenLists (256); |
if (aglUseFont (ctx, fontID , face, size, 0, 256, (long) listBase)) { |
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); |
glReportError (); |
return listBase; |
} else { |
reportError ("aglUseFont failed\n" ); |
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); |
glDeleteLists (listBase, 256); |
return 0; |
} |
} |
// --------------------------------- |
// delete font list passed in |
void deleteFontGL (GLuint fontList) |
{ |
if (fontList) |
glDeleteLists (fontList, 256); |
} |
// --------------------------------- |
// given a delta time in seconds and current roatation accel, velocity and position, update overall object rotation |
void updateRotation (double deltaTime, GLfloat * fRot, GLfloat * fVel, GLfloat * fAccel, GLfloat * objectRotation ) |
{ |
// update rotation based on vel and accel |
float rotation[4] = {0.0f, 0.0f, 0.0f, 0.0f}; |
GLfloat fVMax = 2.0f; |
short i; |
// do velocities |
for (i = 0; i < 3; i++) { |
fVel[i] += fAccel[i] * deltaTime * 30.0f; |
if (fVel[i] > fVMax) { |
fAccel[i] *= -1.0f; |
fVel[i] = fVMax; |
} else if (fVel[i] < -fVMax) { |
fAccel[i] *= -1.0f; |
fVel[i] = -fVMax; |
} |
fRot[i] += fVel[i] * deltaTime * 30.0f; |
while (fRot[i] > 360.0f) |
fRot[i] -= 360.0f; |
while (fRot[i] < -360.0f) |
fRot[i] += 360.0f; |
} |
rotation[0] = fRot[0]; |
rotation[1] = 1.0f; |
addToRotationTrackball (rotation, objectRotation); |
rotation[0] = fRot[1]; |
rotation[1] = 0.0f; rotation[2] = 1.0f; |
addToRotationTrackball (rotation, objectRotation); |
rotation[0] = fRot[2]; |
rotation[2] = 0.0f; rotation[3] = 1.0f; |
addToRotationTrackball (rotation, objectRotation); |
} |
// --------------------------------- |
#ifndef DTOR |
#define DTOR 0.0174532925 |
#endif |
#ifndef MIN |
#define MIN(a, b) ((a) < (b) ? (a) : (b)) |
#endif |
// update the projection matrix based on camera and view info |
// should be called when viewport size, eye z position, or camera aperture changes |
// also call if far or near changes which is determined by shape size in this case |
// current context should be set before calling |
void updateProjection (GLdouble width, GLdouble height, GLdouble zPos, GLdouble aperture, GLdouble shapeSize) |
{ |
GLdouble xmin, xmax, ymin, ymax; |
// far frustum plane |
GLdouble zFar = -zPos + shapeSize * 0.5; |
// near frustum plane (clamped at 1.0) |
GLdouble zNear = MIN (-zPos - shapeSize * 0.5, 1.0); |
// view aspect ratio |
GLdouble aspect = width / height; |
glMatrixMode (GL_PROJECTION); |
glLoadIdentity (); |
if (aspect > 1.0) { |
ymax = zNear * tan (aperture * 0.5 * DTOR); |
ymin = -ymax; |
xmin = ymin * aspect; |
xmax = ymax * aspect; |
} else { |
xmax = zNear * tan (aperture * 0.5 * DTOR); |
xmin = -xmax; |
ymin = xmin / aspect; |
ymax = xmax / aspect; |
} |
glFrustum(xmin, xmax, ymin, ymax, zNear, zFar); |
} |
// --------------------------------- |
// updates the contexts model view matrix for object and camera moves |
// we will call this every draw loop for simplicity |
// current context should be set before calling |
void updateModelView (AGLContext aglContext, recCamera * pCamera, GLfloat * pSpinRot, GLfloat * pObjectRot, GLfloat * pWorldRot) |
{ |
// move view |
glMatrixMode (GL_MODELVIEW); |
glLoadIdentity (); |
gluLookAt (pCamera->viewPos.x, pCamera->viewPos.y, pCamera->viewPos.z, |
pCamera->viewPos.x + pCamera->viewDir.x, |
pCamera->viewPos.y + pCamera->viewDir.y, |
pCamera->viewPos.z + pCamera->viewDir.z, |
pCamera->viewUp.x, pCamera->viewUp.y ,pCamera->viewUp.z); |
if (pWorldRot) { // if we have a world rotation eval the track ball |
// if we have trackball rotation to map (this IS the test I want as it can be explicitly 0.0f) |
if ((gTrackingContextInfo == aglContext) && gTrackBallRotation[0] != 0.0f) |
glRotatef (gTrackBallRotation[0], gTrackBallRotation[1], gTrackBallRotation[2], gTrackBallRotation[3]); |
else { |
} |
// accumlated world rotation via trackball |
glRotatef (pWorldRot[0], pWorldRot[1], pWorldRot[2], pWorldRot[3]); |
} |
if (pObjectRot && pSpinRot) { |
// object itself rotating applied after camera rotation |
glRotatef (pObjectRot[0], pObjectRot[1], pObjectRot[2], pObjectRot[3]); |
pSpinRot[0] = 0.0f; // reset animation rotations (do in all cases to prevent rotating while moving with trackball) |
pSpinRot[1] = 0.0f; |
pSpinRot[2] = 0.0f; |
} |
} |
// --------------------------------- |
// handles resizing of GL need context update and if the window dimensions change, a |
// a window dimension update, reseting of viewport and an update of the projection matrix |
// this is a windowing system level routine so it handles setting context, etc. |
// returns error if resize fails |
OSStatus resizeGL (AGLContext aglContext, recCamera * pCamera, GLfloat shapeSize, CGRect viewRect) |
{ |
OSStatus err = noErr; |
if (!aglContext) |
return paramErr; |
// re-attach drawable to ensure context is updated |
if (!aglSetCurrentContext (aglContext)) |
err = aglReportError (); |
if ((noErr == err) && !aglUpdateContext (aglContext)) |
err = aglReportError (); |
// Must go directly to the stored value to proper check for differences with window size |
// Note, it is likely a toss up as to whether blinding setting the viewport is faster than |
// testing for changes |
if (noErr == err) { |
GLint aViewportDims[4]; |
glGetIntegerv (GL_VIEWPORT, aViewportDims); |
if ((viewRect.size.width != aViewportDims[2]) || |
(viewRect.size.height != aViewportDims[3])) { |
pCamera->viewOriginX = viewRect.origin.x; |
pCamera->viewOriginY = viewRect.origin.y; |
pCamera->viewWidth = viewRect.size.width; |
pCamera->viewHeight = viewRect.size.height; |
glViewport (0, 0, pCamera->viewWidth, pCamera->viewHeight); |
} |
} |
return err; |
} |
// --------------------------------- |
// sets the camera data to initial conditions |
void resetCamera (recCamera * pCamera) |
{ |
pCamera->aperture = 40; |
pCamera->rotPoint = gOrigin; |
pCamera->viewPos.x = 0.0; |
pCamera->viewPos.y = 0.0; |
pCamera->viewPos.z = -9.0; |
pCamera->viewDir.x = -pCamera->viewPos.x; |
pCamera->viewDir.y = -pCamera->viewPos.y; |
pCamera->viewDir.z = -pCamera->viewPos.z; |
pCamera->viewUp.x = 0; |
pCamera->viewUp.y = 1; |
pCamera->viewUp.z = 0; |
// will be set in resize once the target view size and position is known |
pCamera->viewOriginY = 0; |
pCamera->viewOriginX = 0; |
pCamera->viewHeight = 0; |
pCamera->viewWidth = 0; |
} |
// --------------------------------- |
void SetLighting (unsigned int mode) |
{ |
GLfloat mat_specular[] = {1.0, 1.0, 1.0, 1.0}; |
GLfloat mat_shininess[] = {90.0}; |
GLfloat position[4] = {7.0,-7.0,12.0,0.0}; |
GLfloat ambient[4] = {0.2,0.2,0.2,1.0}; |
GLfloat diffuse[4] = {1.0,1.0,1.0,1.0}; |
GLfloat specular[4] = {1.0,1.0,1.0,1.0}; |
glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR, mat_specular); |
glMaterialfv (GL_FRONT_AND_BACK, GL_SHININESS, mat_shininess); |
glEnable(GL_COLOR_MATERIAL); |
glColorMaterial(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE); |
switch (mode) { |
case 0: |
break; |
case 1: |
glLightModeli(GL_LIGHT_MODEL_TWO_SIDE,GL_FALSE); |
glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER,GL_FALSE); |
break; |
case 2: |
glLightModeli(GL_LIGHT_MODEL_TWO_SIDE,GL_FALSE); |
glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER,GL_TRUE); |
break; |
case 3: |
glLightModeli(GL_LIGHT_MODEL_TWO_SIDE,GL_TRUE); |
glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER,GL_FALSE); |
break; |
case 4: |
glLightModeli(GL_LIGHT_MODEL_TWO_SIDE,GL_TRUE); |
glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER,GL_TRUE); |
break; |
} |
glLightfv(GL_LIGHT0,GL_POSITION,position); |
glLightfv(GL_LIGHT0,GL_AMBIENT,ambient); |
glLightfv(GL_LIGHT0,GL_DIFFUSE,diffuse); |
glLightfv(GL_LIGHT0,GL_SPECULAR,specular); |
glEnable(GL_LIGHT0); |
} |
#pragma mark ---- OpenGL Minimize Handler (thanks to Dan Herman) ---- |
void InvertGLImage( char *imageData, size_t imageSize, size_t rowBytes ) |
{ |
long i, j; |
char *tBuffer = (char*) malloc (rowBytes); |
if (NULL == tBuffer) return; |
// Copy by rows through temp buffer |
for (i = 0, j = imageSize - rowBytes; i < imageSize >> 1; i += rowBytes, j -= rowBytes) { |
memcpy( tBuffer, &imageData[i], rowBytes ); |
memcpy( &imageData[i], &imageData[j], rowBytes ); |
memcpy( &imageData[j], tBuffer, rowBytes ); |
} |
free(tBuffer); |
} |
// --------------------------------- |
void CompositeGLBufferIntoWindow (AGLContext ctx, Rect *bufferRect, WindowRef win) |
{ |
GWorldPtr pGWorld; |
QDErr err; |
// blit OpenGL content into window backing store |
// allocate buffer to hold pane image |
long width = (bufferRect->right - bufferRect->left); |
long height = (bufferRect->bottom - bufferRect->top); |
Rect src_rect = {0, 0, height, width}; |
long rowBytes = width * 4; |
long imageSize = rowBytes * height; |
char *image = (char *) NewPtr (imageSize); |
if (!image) { |
printf("Out of memory in CompositeGLBufferIntoWindow()!"); |
return; // no harm in continuing |
} |
// pull GL content down to our image buffer |
aglSetCurrentContext( ctx ); |
glReadPixels (0, 0, width, height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, image); |
// GL buffers are upside-down relative to QD buffers, so we need to flip it |
InvertGLImage( image, imageSize, rowBytes ); |
// create a GWorld containing our image |
err = NewGWorldFromPtr (&pGWorld, k32ARGBPixelFormat, &src_rect, 0, 0, 0, image, rowBytes); |
if (err != noErr) { |
printf("WARNING: error in NewGWorldFromPtr, called from CompositeGLBufferIntoWindow()"); |
free( image ); |
return; |
} |
SetPort( GetWindowPort (win)); |
CopyBits( GetPortBitMapForCopyBits (pGWorld), GetPortBitMapForCopyBits (GetWindowPort (win)), &src_rect, bufferRect, srcCopy, 0 ); |
DisposeGWorld( pGWorld ); |
DisposePtr ( image ); |
} |
#pragma mark ---- Display Manager Event Handling ---- |
// update our GL configuration info based on display change notification |
void handleConfigDMEvent (void *userData, short theMessage, void *notifyData) |
{ |
if (kDMNotifyEvent == theMessage) { // post change notifications only |
getCurrentCaps (); |
} |
} |
// --------------------------------- |
// handle display config changes meaing we need to update the GL context via the resize function and check for windwo dimension changes |
// also note we redraw the content here as it could be lost in a display config change |
void handleWindowDMEvent (void *userData, short theMessage, void *notifyData) |
{ |
if (kDMNotifyEvent == theMessage) { // post change notifications only |
pRecContext pContextInfo = NULL; |
WindowRef window = (WindowRef) userData; |
if (window) |
pContextInfo = GetCurrentContextInfo (window); |
if (pContextInfo) { // have a valid OpenGl window |
Rect rectPort; |
CGRect viewRect = {{0.0f, 0.0f}, {0.0f, 0.0f}}; |
#if DEBUG |
sprintf (pContextInfo->message, "Event: Display Change at %0.1f secs", getElapsedTime ()); |
pContextInfo->msgTime = getElapsedTime (); |
#endif |
GetWindowPortBounds (window, &rectPort); |
viewRect.size.width = (float) (rectPort.right - rectPort.left); |
viewRect.size.height = (float) (rectPort.bottom - rectPort.top); |
resizeGL (pContextInfo->aglContext, &pContextInfo->camera, pContextInfo->shapeSize, viewRect); |
InvalWindowRect (window, &rectPort); // force redrow |
} |
} |
} |
#pragma mark ---- OpenGL Window ---- |
// intializes context conditions |
static void initialConditions (pRecContext pContextInfo) |
{ |
BlockZero (pContextInfo, sizeof (recContext)); |
resetCamera (&pContextInfo->camera); |
pContextInfo->info = kInfoState; |
pContextInfo->drawHelp = 1; |
pContextInfo->timer = NULL; |
} |
// --------------------------------- |
OSStatus buildGL (WindowRef window) |
{ |
OSStatus err = noErr; |
#if kUseMultiSample |
GLint attrib[] = { AGL_RGBA, AGL_DOUBLEBUFFER, AGL_DEPTH_SIZE, 16, AGL_NO_RECOVERY, |
AGL_SAMPLE_BUFFERS_ARB, 1, AGL_SAMPLES_ARB, kSamples, AGL_NONE }; |
#else |
GLint attrib[] = { AGL_RGBA, AGL_DOUBLEBUFFER, AGL_DEPTH_SIZE, 16, AGL_NONE }; |
#endif |
ProcessSerialNumber psn = { 0, kCurrentProcess }; |
pRecContext pContextInfo = GetCurrentContextInfo(window); |
if (NULL == pContextInfo) |
return paramErr; |
if (pContextInfo->aglContext) |
return noErr; // already built |
// build context |
pContextInfo->aglContext = NULL; |
pContextInfo->aglPixFmt = aglChoosePixelFormat(NULL, 0, attrib); |
err = aglReportError (); |
if (pContextInfo->aglPixFmt) { |
pContextInfo->aglContext = aglCreateContext(pContextInfo->aglPixFmt, gShareContext); |
err = aglReportError (); |
} |
#if kUseMultiSample |
// if multi-sample pixel format or context fails allocate a non-multi-sample one |
if ((noErr != err) || (NULL == pContextInfo->aglContext)) { // try non-multisample |
attrib[5] = attrib[6] = attrib[7] = attrib[8] = 0; // remove multi-sample attribs |
if (pContextInfo->aglPixFmt) |
aglDestroyPixelFormat (pContextInfo->aglPixFmt); |
pContextInfo->aglPixFmt = aglChoosePixelFormat (NULL, 0, attrib); |
err = aglReportError (); |
if (pContextInfo->aglPixFmt) { |
pContextInfo->aglContext = aglCreateContext (pContextInfo->aglPixFmt, gShareContext); |
err = aglReportError (); |
} |
} |
#endif |
if (pContextInfo->aglContext) { |
Rect rectPort; |
short fNum; |
GLint swap = 1; |
CGRect viewRect = {{0.0f, 0.0f}, {0.0f, 0.0f}}; |
GrafPtr portSave = NULL; |
GetPort (&portSave); |
SetPort ((GrafPtr) GetWindowPort (window)); |
if(!aglSetDrawable(pContextInfo->aglContext, GetWindowPort (window))) |
err = aglReportError (); |
if (!aglSetCurrentContext(pContextInfo->aglContext)) |
err = aglReportError (); |
pContextInfo->currentVS = aglGetVirtualScreen (pContextInfo->aglContext); |
if (NULL == gShareContext) |
gShareContext = pContextInfo->aglContext; // share initial context created |
// ensure we know when display configs are changed |
pContextInfo->windowEDMUPP = NewDMExtendedNotificationUPP (handleWindowDMEvent); // for display change notification |
DMRegisterExtendedNotifyProc (pContextInfo->windowEDMUPP, (void *) window, NULL, &psn); |
// VBL SYNC |
if (!aglSetInteger (pContextInfo->aglContext, AGL_SWAP_INTERVAL, &swap)) |
aglReportError (); |
// init GL stuff here |
#if kUseMultiSample |
switch (pContextInfo->modeFSAA) { |
case kFSAAOff: |
glDisable (GL_MULTISAMPLE_ARB); |
break; |
case kFSAAFast: |
glEnable (GL_MULTISAMPLE_ARB); |
glHint (GL_MULTISAMPLE_FILTER_HINT_NV, GL_FASTEST); |
break; |
case kFSAANice: |
glEnable (GL_MULTISAMPLE_ARB); |
glHint (GL_MULTISAMPLE_FILTER_HINT_NV, GL_NICEST); |
break; |
} |
#endif |
glEnable(GL_DEPTH_TEST); |
glShadeModel(GL_SMOOTH); |
glPolygonMode(GL_FRONT_AND_BACK,GL_FILL); |
glFrontFace(GL_CCW); |
glPolygonOffset (1.0, 1.0); |
glEnable (GL_ALPHA_TEST); |
glAlphaFunc (GL_GREATER, 0.5); |
glEnable (GL_BLEND); // for text fading |
glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // ditto |
glClearColor(0.0,0.0,0.0,0.0); |
pContextInfo->shapeSize = 11.0f; // max radius of of objects |
GetFNum ("\pGeneva", &fNum); // build font |
pContextInfo->boldFontList = buildFontGL (pContextInfo->aglContext, fNum, bold, 9); |
pContextInfo->regFontList = buildFontGL (pContextInfo->aglContext, fNum, normal, 9); |
// setup viewport and prespective |
GetWindowPortBounds (window, &rectPort); |
viewRect.size.width = (float) (rectPort.right - rectPort.left); |
viewRect.size.height = (float) (rectPort.bottom - rectPort.top); |
pContextInfo->camera.viewOriginX = viewRect.origin.x; |
pContextInfo->camera.viewOriginY = viewRect.origin.y; |
pContextInfo->camera.viewWidth = viewRect.size.width; |
pContextInfo->camera.viewHeight = viewRect.size.height; |
glViewport (0, 0, pContextInfo->camera.viewWidth, pContextInfo->camera.viewHeight); |
// window geometry |
#if kUseNonPowerOf2 |
BuildGeometry (kCube, 0, 0, 0, &pContextInfo->polyList, &pContextInfo->lineList, &pContextInfo->pointList, kPbufferWidth, kPbufferHeight); |
#else |
BuildGeometry (kCube, 0, 0, 0, &pContextInfo->polyList, &pContextInfo->lineList, &pContextInfo->pointList, 0, 0); |
#endif |
SetPort (portSave); |
} |
return err; |
} |
// --------------------------------- |
// dump window data structures and OpenGL context and related data structures |
OSStatus disposeGL (pRecContext pContextInfo) |
{ |
// release our data |
if ( pContextInfo != NULL ) |
{ |
if (pContextInfo->windowEDMUPP) { // dispose UPP for DM notifications |
DisposeDMExtendedNotificationUPP (pContextInfo->windowEDMUPP); |
pContextInfo->windowEDMUPP = NULL; |
} |
// do not need to dump pbuffer as it shared across all windows |
aglSetDrawable (pContextInfo->aglContext, NULL); |
aglSetCurrentContext (NULL); |
if (pContextInfo->aglContext) { |
aglDestroyContext (pContextInfo->aglContext); |
pContextInfo->aglContext = NULL; |
} |
if (pContextInfo->aglPixFmt) { |
aglDestroyPixelFormat (pContextInfo->aglPixFmt); |
pContextInfo->aglPixFmt = NULL; |
} |
if (pContextInfo->timer) { |
RemoveEventLoopTimer(pContextInfo->timer); |
pContextInfo->timer = NULL; |
} |
// dump font display list |
if (pContextInfo->boldFontList) { |
deleteFontGL (pContextInfo->boldFontList); |
pContextInfo->boldFontList = 0; |
} |
if (pContextInfo->regFontList) { |
deleteFontGL (pContextInfo->regFontList); |
pContextInfo->regFontList = 0; |
} |
} |
return noErr; |
} |
// --------------------------------- |
#include "drawInfo.h" // source info for info output |
// --------------------------------- |
// draw text info |
// note: this bitmap technique is not the speediest and one should use textures for font in performance critical code |
static void drawInfo (pRecContext pContextInfo) |
{ |
static float msgPresistance = 10.0f; |
char cstr [256]; |
GLint matrixMode, line = 1; |
GLboolean depthTest = glIsEnabled (GL_DEPTH_TEST); |
GLfloat height, width; |
if (!pContextInfo) |
return; |
height = pContextInfo->camera.viewHeight; |
width = pContextInfo->camera.viewWidth; |
glDisable (GL_DEPTH_TEST); // ensure text is not remove by deoth buffer test. |
glEnable (GL_BLEND); // for text fading |
glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // ditto |
// set orthograhic 1:1 pixel transform in local view coords |
glGetIntegerv (GL_MATRIX_MODE, &matrixMode); |
glMatrixMode (GL_PROJECTION); |
glPushMatrix(); |
glLoadIdentity (); |
glMatrixMode (GL_MODELVIEW); |
glPushMatrix(); |
glLoadIdentity (); |
glScalef (2.0 / width, -2.0 / height, 1.0); |
glTranslatef (-width / 2.0, -height / 2.0, 0.0); |
// output strings |
glColor3f (1.0, 1.0, 1.0); |
sprintf (cstr, "Camera at (%0.1f, %0.1f, %0.1f) looking at (%0.1f, %0.1f, %0.1f) with %0.1f aperture", |
pContextInfo->camera.viewPos.x, pContextInfo->camera.viewPos.y, pContextInfo->camera.viewPos.z, |
pContextInfo->camera.viewDir.x, pContextInfo->camera.viewDir.y, pContextInfo->camera.viewDir.z, |
pContextInfo->camera.aperture); |
glRasterPos3d (10, line++ * 12, 0); |
drawCStringGL (cstr, pContextInfo->boldFontList); |
sprintf (cstr, "World Rotation: (%0.1f, %0.2f, %0.2f, %0.2f)", |
pContextInfo->worldRotation[0], pContextInfo->worldRotation[1], pContextInfo->worldRotation[2], pContextInfo->worldRotation[3]); |
glRasterPos3d (10, line++ * 12, 0); |
drawCStringGL (cstr, pContextInfo->regFontList); |
sprintf (cstr, "Vertices: %lu, Color Scheme: %lu", |
gpPbuffer->subdivisions * gpPbuffer->xyRatio * gpPbuffer->subdivisions, gpPbuffer->colorScheme); |
glRasterPos3d (10, line++ * 12, 0); |
drawCStringGL (cstr, pContextInfo->regFontList); |
{ |
GLboolean twoSidedLighting, localViewer; |
glGetBooleanv (GL_LIGHT_MODEL_LOCAL_VIEWER, &localViewer); |
glGetBooleanv (GL_LIGHT_MODEL_TWO_SIDE, &twoSidedLighting); |
if (!gpPbuffer->lighting) { |
sprintf (cstr, "-- Lighting off"); |
} else { |
if (!twoSidedLighting) |
sprintf (cstr, "-- Single Sided Lighting"); |
else |
sprintf (cstr, "-- Two Sided Lighting"); |
if (localViewer) |
sprintf (cstr, "%s: Local Viewer", cstr); |
} |
glRasterPos3d (10, line++ * 12, 0); |
drawCStringGL (cstr, pContextInfo->regFontList); |
} |
glRasterPos3d (10, line++ * 12, 0); |
#if kUseMultiSample |
switch (pContextInfo->modeFSAA) { |
case kFSAAOff: |
sprintf (cstr, "-- %d x FSAA: Disabled", kSamples); |
break; |
case kFSAAFast: |
sprintf (cstr, "-- %d x FSAA: Fastest hint", kSamples); |
break; |
case kFSAANice: |
sprintf (cstr, "-- %d x FSAA: Nicest hint", kSamples); |
break; |
} |
#else |
sprintf (cstr, "-- FSAA: Not Used"); |
#endif |
drawCStringGL (cstr, pContextInfo->regFontList); |
// message string |
if (pContextInfo->message[0]) { |
float currDelta = getElapsedTime () - pContextInfo->msgTime; |
glColor4f (1.0, 1.0, 1.0, (msgPresistance - currDelta) * 0.1); |
glRasterPos3d (10, line++ * 12, 0); |
drawCStringGL (pContextInfo->message, pContextInfo->boldFontList); |
if (currDelta > msgPresistance) |
pContextInfo->message[0] = 0; |
} |
// global error message |
if (gErrorMessage[0]) { |
float currDelta = getElapsedTime () - gErrorTime; |
glColor4f (1.0, 0.2, 0.2, (msgPresistance - currDelta) * 0.1); |
glRasterPos3d (10, line++ * 12, 0); |
drawCStringGL (gErrorMessage, pContextInfo->boldFontList); |
if (currDelta > msgPresistance) |
gErrorMessage[0] = 0; |
} |
if (pContextInfo->showCredits) { |
char *strName, *strAuthor, *strX, *strY, *strZ, *strRange; |
GetStrings (gpPbuffer->surface, &strName, &strAuthor, &strX, &strY, &strZ, &strRange); |
line = 10; |
glColor3f (1.0f, 1.0f, 0.0f); |
glRasterPos3d (10, line++ * 12, 0); |
drawCStringGL (strName, pContextInfo->boldFontList); |
glRasterPos3d (10, line++ * 12, 0); |
drawCStringGL (strAuthor, pContextInfo->regFontList); |
glColor3f (0.7f, 0.7f, 0.0f); |
glRasterPos3d (10, line++ * 12, 0); |
drawCStringGL (strX, pContextInfo->regFontList); |
glRasterPos3d (10, line++ * 12, 0); |
drawCStringGL (strY, pContextInfo->regFontList); |
glRasterPos3d (10, line++ * 12, 0); |
drawCStringGL (strZ, pContextInfo->regFontList); |
glRasterPos3d (10, line++ * 12, 0); |
drawCStringGL (strRange, pContextInfo->regFontList); |
} |
if (pContextInfo->drawHelp) { |
line = 4; |
glColor3f (0.8f, 0.8f, 0.8f); |
glRasterPos3d (10, height - line++ * 12, 0); |
drawCStringGL ("\\: cycle surface type", pContextInfo->regFontList); |
glRasterPos3d (10, height - line++ * 12, 0); |
drawCStringGL ("; & ': decrease/increase subdivisions", pContextInfo->regFontList); |
glRasterPos3d (10, height - line++ * 12, 0); |
drawCStringGL ("[ & ]: cycle color scheme", pContextInfo->regFontList); |
glRasterPos3d (10, height - line++ * 12, 0); |
drawCStringGL ("'l': cycle lighting 'm': cycle full scene anti-aliasing", pContextInfo->regFontList); |
glRasterPos3d (10, height - line++ * 12, 0); |
drawCStringGL ("'p': toggle points 'w': toggle wireframe 'f': toggle fill", pContextInfo->regFontList); |
glRasterPos3d (10, height - line++ * 12, 0); |
drawCStringGL ("'?': toggle credits 'c': toggle OpenGL caps", pContextInfo->regFontList); |
glRasterPos3d (10, height - line++ * 12, 0); |
drawCStringGL ("Cmd-A: animate Cmd-I: show info", pContextInfo->regFontList); |
glRasterPos3d (10, height - line++ * 12, 0); |
drawCStringGL ("Wheel: zoom camera", pContextInfo->boldFontList); |
glRasterPos3d (10, height - line++ * 12, 0); |
drawCStringGL ("Middle Button Drag: dolly object", pContextInfo->boldFontList); |
glRasterPos3d (10, height - line++ * 12, 0); |
drawCStringGL ("Right Button Drag: pan object", pContextInfo->boldFontList); |
glRasterPos3d (10, height - line++ * 12, 0); |
drawCStringGL ("Left Button Drag: rotate object", pContextInfo->boldFontList); |
glRasterPos3d (10, height - line++ * 12, 0); |
drawCStringGL ("-- Help ('h') --", pContextInfo->boldFontList); |
} |
glColor3f (1.0, 1.0, 1.0); |
glRasterPos3d (10, height - 27, 0); |
sprintf (cstr, "(%0.0f x %0.0f)", width, height); |
drawCStringGL (cstr, pContextInfo->boldFontList); |
// render and vendor info |
glRasterPos3d (10, height - 15, 0); |
drawCStringGL ((char*) glGetString (GL_RENDERER), pContextInfo->boldFontList); |
glRasterPos3d (10, height - 3, 0); |
drawCStringGL ((char*) glGetString (GL_VERSION), pContextInfo->regFontList); |
if (pContextInfo->drawCaps) |
drawCaps (pContextInfo); |
// reset orginal martices |
glPopMatrix(); // GL_MODELVIEW |
glMatrixMode (GL_PROJECTION); |
glPopMatrix(); |
glMatrixMode (matrixMode); |
if (depthTest) |
glEnable (GL_DEPTH_TEST); |
glReportError (); |
} |
// --------------------------------- |
// main OpenGL drawing function |
void drawGL (WindowRef window, Boolean swap) |
{ |
pRecContext pContextInfo = NULL; |
Rect rectPort; |
CGRect viewRect = {{0.0f, 0.0f}, {0.0f, 0.0f}}; |
if (window) |
pContextInfo = GetCurrentContextInfo (window); |
if (!pContextInfo) |
return; |
if (!pContextInfo->aglContext) |
buildGL(window); |
// update and draw pbuffer content |
#pragma mark *** draw pbuffer content (if required) *** |
updatePbuffer (pContextInfo->aglContext, gpPbuffer); |
// set window as render target |
if (!aglSetCurrentContext (pContextInfo->aglContext)) |
aglReportError (); |
if(!aglSetDrawable(pContextInfo->aglContext, GetWindowPort (window))) |
aglReportError (); |
GetWindowPortBounds (window, &rectPort); |
viewRect.size.width = (float) (rectPort.right - rectPort.left); |
viewRect.size.height = (float) (rectPort.bottom - rectPort.top); |
resizeGL (pContextInfo->aglContext, &pContextInfo->camera, pContextInfo->shapeSize, viewRect); |
updateProjection (pContextInfo->camera.viewWidth, pContextInfo->camera.viewHeight, pContextInfo->camera.viewPos.z, |
pContextInfo->camera.aperture, pContextInfo->shapeSize); |
updateModelView (pContextInfo->aglContext, &pContextInfo->camera, |
NULL, NULL, pContextInfo->worldRotation); |
// clear our drawable |
glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); |
glDisable(GL_LIGHTING); |
#if kUseNonPowerOf2 |
glEnable (GL_TEXTURE_RECTANGLE_EXT); |
#else |
glEnable (GL_TEXTURE_2D); |
#endif |
#pragma mark *** bind to pbuffer for texturing *** |
bindPbuffer (pContextInfo->aglContext, gpPbuffer); // texture from Pbuffer |
glCallList (pContextInfo->polyList); // draws textured cube |
#if kUseNonPowerOf2 |
glDisable (GL_TEXTURE_RECTANGLE_EXT); |
#else |
glDisable (GL_TEXTURE_2D); |
#endif |
glReportError (); |
if (pContextInfo->info) |
drawInfo (pContextInfo); |
if (swap) |
aglSwapBuffers (pContextInfo->aglContext); |
else |
glFlush(); |
} |
#pragma mark ---- Carbon Timer ---- |
// per-window timer function, basic time based animation preformed here |
static void timerContextCB (WindowRef window) |
{ |
pRecContext pContextInfo = NULL; |
if (window) |
pContextInfo = GetCurrentContextInfo (window); |
if (pContextInfo && gpPbuffer->animate) { // if pbuffer is animating |
AbsoluteTime currTime = UpTime (); |
double deltaTime = (float) AbsoluteDeltaToDuration (currTime, pContextInfo->time); |
pContextInfo->time = currTime; // reset for next time interval |
if (0 > deltaTime) // if negative microseconds |
deltaTime /= -1000000.0; |
else // else milliseconds |
deltaTime /= 1000.0; |
if (deltaTime < 10.0) { // skip large pauses |
drawGL (window, true); // required to do this directly to get animation during resize and drags |
} |
} |
} |
// --------------------------------- |
// timer callback bottleneck |
static pascal void timerCB (EventLoopTimerRef inTimer, void* userData) |
{ |
#pragma unused (inTimer) |
timerContextCB ((WindowRef) userData); // timer based update |
} |
// --------------------------------- |
// get UPP for timer |
static EventLoopTimerUPP getTimerUPP (void) |
{ |
static EventLoopTimerUPP sTimerUPP = NULL; |
if (sTimerUPP == NULL) |
sTimerUPP = NewEventLoopTimerUPP (timerCB); |
return sTimerUPP; |
} |
#pragma mark ---- Carbon Event Handling ---- |
pRecContext GetCurrentContextInfo (WindowRef window) |
{ |
if (NULL == window) // HID use this path |
window = FrontWindow (); |
if (window) |
return (pRecContext) GetWRefCon (window); |
else |
return NULL; |
} |
// --------------------------------- |
// move camera in z axis |
static void mouseDolly (HIPoint location, pRecContext pContextInfo) |
{ |
GLfloat dolly = (gDollyPanStartPoint[1] - location.y) * -pContextInfo->camera.viewPos.z / 300.0f; |
pContextInfo->camera.viewPos.z += dolly; |
if (pContextInfo->camera.viewPos.z == 0.0) // do not let z = 0.0 |
pContextInfo->camera.viewPos.z = 0.0001; |
gDollyPanStartPoint[0] = location.x; |
gDollyPanStartPoint[1] = location.y; |
} |
// --------------------------------- |
// move camera in x/y plane |
static void mousePan (HIPoint location, pRecContext pContextInfo) |
{ |
GLfloat panX = (gDollyPanStartPoint[0] - location.x) / (900.0f / -pContextInfo->camera.viewPos.z); |
GLfloat panY = (gDollyPanStartPoint[1] - location.y) / (900.0f / -pContextInfo->camera.viewPos.z); |
pContextInfo->camera.viewPos.x -= panX; |
pContextInfo->camera.viewPos.y -= panY; |
gDollyPanStartPoint[0] = location.x; |
gDollyPanStartPoint[1] = location.y; |
} |
// --------------------------------- |
// Handles global non-system mouse events for GL windows as follows: |
// primary button = object tumble (trackball) |
// secondary button (or cntl-primary) = pan |
// tertiary button (or option-primary) = dolly |
// wheel = aperture |
static OSStatus handleWindowMouseEvents (EventHandlerCallRef myHandler, EventRef event) |
{ |
WindowRef window = NULL; |
pRecContext pContextInfo = NULL; |
OSStatus result = eventNotHandledErr; |
UInt32 kind = GetEventKind (event); |
EventMouseButton button = 0; |
HIPoint location = {0.0f, 0.0f}; |
UInt32 modifiers = 0; |
long wheelDelta = 0; |
Rect rectPort; |
// Mac OS X v10.1 and later |
// should this be front window??? |
GetEventParameter(event, kEventParamWindowRef, typeWindowRef, NULL, sizeof(WindowRef), NULL, &window); |
if (window) |
pContextInfo = GetCurrentContextInfo (window); |
if (!pContextInfo) |
return result; // not an application GLWindow so do not process (there is an exception) |
GetWindowPortBounds (window, &rectPort); |
result = CallNextEventHandler(myHandler, event); |
if (eventNotHandledErr == result) |
{ // only handle events not already handled (prevents wierd resize interaction) |
switch (kind) { |
// start trackball, pan, or dolly |
case kEventMouseDown: |
GetEventParameter(event, kEventParamMouseButton, typeMouseButton, NULL, sizeof(EventMouseButton), NULL, &button); |
GetEventParameter(event, kEventParamWindowMouseLocation, typeHIPoint, NULL, sizeof(HIPoint), NULL, &location); // Mac OS X v10.1 and later |
GetEventParameter(event, kEventParamKeyModifiers, typeUInt32, NULL, sizeof(UInt32), NULL, &modifiers); |
if ((button == kEventMouseButtonSecondary) || ((button == kEventMouseButtonPrimary) && (modifiers & controlKey))) { // pan |
if (gTrackball) { // if we are currently tracking, end trackball |
gTrackball = GL_FALSE; |
if (gTrackBallRotation[0] != 0.0) |
addToRotationTrackball (gTrackBallRotation, pContextInfo->worldRotation); |
gTrackBallRotation [0] = gTrackBallRotation [1] = gTrackBallRotation [2] = gTrackBallRotation [3] = 0.0f; |
} else if (gDolly) { // if we are currently dollying, end dolly |
gDolly = GL_FALSE; |
} |
gDollyPanStartPoint[0] = location.x; |
gDollyPanStartPoint[1] = location.y; |
gPan = GL_TRUE; |
gTrackingContextInfo = pContextInfo->aglContext; |
} else if ((button == kEventMouseButtonTertiary) || ((button == kEventMouseButtonPrimary) && (modifiers & optionKey))) { // dolly |
if (gTrackball) { // if we are currently tracking, end trackball |
gTrackball = GL_FALSE; |
if (gTrackBallRotation[0] != 0.0) |
addToRotationTrackball (gTrackBallRotation, pContextInfo->worldRotation); |
gTrackBallRotation [0] = gTrackBallRotation [1] = gTrackBallRotation [2] = gTrackBallRotation [3] = 0.0f; |
} else if (gPan) { // if we are currently panning, end pan |
gPan = GL_FALSE; |
} |
gDollyPanStartPoint[0] = location.x; |
gDollyPanStartPoint[1] = location.y; |
gDolly = GL_TRUE; |
gTrackingContextInfo = pContextInfo->aglContext; |
} else if (button == kEventMouseButtonPrimary) { // trackball |
if (gDolly) { // if we are currently dollying, end dolly |
gDolly = GL_FALSE; |
gTrackingContextInfo = NULL; |
} else if (gPan) { // if we are currently panning, end pan |
gPan = GL_FALSE; |
gTrackingContextInfo = NULL; |
} |
startTrackball (location.x, location.y, |
pContextInfo->camera.viewOriginX, pContextInfo->camera.viewOriginY, |
pContextInfo->camera.viewWidth, pContextInfo->camera.viewHeight); |
gTrackball = GL_TRUE; |
gTrackingContextInfo = pContextInfo->aglContext; |
} |
break; |
// stop trackball, pan, or dolly |
case kEventMouseUp: |
if (gDolly) { // end dolly |
gDolly = GL_FALSE; |
} else if (gPan) { // end pan |
gPan = GL_FALSE; |
} else if (gTrackball) { // end trackball |
gTrackball = GL_FALSE; |
if (gTrackBallRotation[0] != 0.0) |
addToRotationTrackball (gTrackBallRotation, pContextInfo->worldRotation); |
gTrackBallRotation [0] = gTrackBallRotation [1] = gTrackBallRotation [2] = gTrackBallRotation [3] = 0.0f; |
} |
gTrackingContextInfo = NULL; |
break; |
// trackball, pan, or dolly |
case kEventMouseDragged: |
GetEventParameter(event, kEventParamWindowMouseLocation, typeHIPoint, NULL, sizeof(HIPoint), NULL, &location); // Mac OS X v10.1 and later |
if (gTrackball) { |
rollToTrackball (location.x, location.y, gTrackBallRotation); |
InvalWindowRect (window, &rectPort); |
} else if (gDolly) { |
mouseDolly (location, pContextInfo); |
InvalWindowRect (window, &rectPort); |
} else if (gPan) { |
mousePan (location, pContextInfo); |
InvalWindowRect (window, &rectPort); |
} |
break; |
// aperature change |
case kEventMouseWheelMoved: |
GetEventParameter(event, kEventParamMouseWheelDelta, typeLongInteger, NULL, sizeof(long), NULL, &wheelDelta); |
if (wheelDelta) |
{ |
GLfloat deltaAperture = wheelDelta * -pContextInfo->camera.aperture / 200.0f; |
pContextInfo->camera.aperture += deltaAperture; |
if (pContextInfo->camera.aperture < 0.1) // do not let aperture <= 0.1 |
pContextInfo->camera.aperture = 0.1; |
if (pContextInfo->camera.aperture > 179.9) // do not let aperture >= 180 |
pContextInfo->camera.aperture = 179.9; |
InvalWindowRect (window, &rectPort); |
} |
break; |
} |
result = noErr; |
} |
return result; |
} |
// --------------------------------- |
// key input handler |
static OSStatus handleKeyInput (EventHandlerCallRef myHandler, EventRef event, Boolean keyDown, void* userData) |
{ |
enum { kH = 0x04, // toggle draw help |
kC = 0x08, // toggle draw caps |
kLfSqBracket = 0x21, // color scheme down |
kRtSqBracket = 0x1E, // color scheme up |
kSemi = 0x29, // decrease subdivisions |
kQuote = 0x27, // increase subdivisions |
kF = 0x03, // toggle fill |
kP = 0x23, // toggle points |
kW = 0x0D, // toggle wire |
kL = 0x25, // next lighting model (wraps) |
kBackSlash = 0x2A, // next shape (wraps) |
kQuestion = 0x2c, // toggle credits |
kM = 0x2E // cycle multi-sample |
}; |
WindowRef window = (WindowRef) userData; |
OSStatus result = eventNotHandledErr; |
result = CallNextEventHandler(myHandler, event); |
if (eventNotHandledErr == result) { |
pRecContext pContextInfo = GetCurrentContextInfo (window); |
if (pContextInfo) { |
UInt32 keyCode; |
Rect rectPort = {0,0,0,0}; |
GetEventParameter (event, kEventParamKeyCode, typeUInt32, NULL, sizeof(UInt32), NULL, &keyCode); |
#if DEBUG |
sprintf (pContextInfo->message, "KeyCode: %lu (0x%lX)", keyCode, keyCode); |
pContextInfo->msgTime = getElapsedTime (); |
#endif |
// handle keyboard input here |
if (keyDown) { |
switch (keyCode) { |
case kH: |
// toggle help |
pContextInfo->drawHelp = 1 - pContextInfo->drawHelp; |
GetWindowPortBounds (window, &rectPort); |
InvalWindowRect (window, &rectPort); |
result = noErr; |
break; |
case kC: |
// toggle caps |
pContextInfo->drawCaps = 1 - pContextInfo->drawCaps; |
GetWindowPortBounds (window, &rectPort); |
InvalWindowRect (window, &rectPort); |
result = noErr; |
break; |
case kQuestion: // credits |
pContextInfo->showCredits = 1 - pContextInfo->showCredits; |
GetWindowPortBounds (window, &rectPort); |
InvalWindowRect (window, &rectPort); |
break; |
case kF: // fill |
gpPbuffer->polygons = 1; |
gpPbuffer->lines = 0; |
gpPbuffer->points = 0; |
dirtyPbuffer (gpPbuffer); |
GetWindowPortBounds (window, &rectPort); |
InvalWindowRect (window, &rectPort); |
break; |
case kW: // lines |
gpPbuffer->polygons = 0; |
gpPbuffer->lines = 1; |
gpPbuffer->points = 0; |
dirtyPbuffer (gpPbuffer); |
GetWindowPortBounds (window, &rectPort); |
InvalWindowRect (window, &rectPort); |
break; |
case kP: // points |
gpPbuffer->polygons = 0; |
gpPbuffer->lines = 0; |
gpPbuffer->points = 1; |
dirtyPbuffer (gpPbuffer); |
GetWindowPortBounds (window, &rectPort); |
InvalWindowRect (window, &rectPort); |
break; |
case kL: // lighting |
gpPbuffer->lighting++; |
gpPbuffer->lighting %= 4; |
aglSetCurrentContext (gpPbuffer->aglContext); |
SetLighting (gpPbuffer->lighting); |
dirtyPbuffer (gpPbuffer); |
GetWindowPortBounds (window, &rectPort); |
InvalWindowRect (window, &rectPort); |
break; |
case kLfSqBracket: // next lower color scheme |
gpPbuffer->colorScheme -= 1; |
if (gpPbuffer->colorScheme < 0) |
gpPbuffer->colorScheme = kColorSchemes - 1; |
aglSetCurrentContext (gpPbuffer->aglContext); |
BuildGeometry (gpPbuffer->surface, gpPbuffer->colorScheme, gpPbuffer->subdivisions, gpPbuffer->xyRatio, |
&(gpPbuffer->polyList), &(gpPbuffer->lineList), &(gpPbuffer->pointList), 0, 0); |
dirtyPbuffer (gpPbuffer); |
GetWindowPortBounds (window, &rectPort); |
InvalWindowRect (window, &rectPort); |
break; |
case kRtSqBracket: // next higher color scheme |
gpPbuffer->colorScheme += 1; |
if (gpPbuffer->colorScheme > kColorSchemes) |
gpPbuffer->colorScheme = 0; |
aglSetCurrentContext (gpPbuffer->aglContext); |
BuildGeometry (gpPbuffer->surface, gpPbuffer->colorScheme, gpPbuffer->subdivisions, gpPbuffer->xyRatio, |
&(gpPbuffer->polyList), &(gpPbuffer->lineList), &(gpPbuffer->pointList), 0, 0); |
dirtyPbuffer (gpPbuffer); |
GetWindowPortBounds (window, &rectPort); |
InvalWindowRect (window, &rectPort); |
break; |
case kSemi: // next lower subdivision setting |
gpPbuffer->subdivisions = (GLuint) (gpPbuffer->subdivisions / 1.414213); |
if (gpPbuffer->subdivisions < 8) |
gpPbuffer->subdivisions = 8; |
aglSetCurrentContext (gpPbuffer->aglContext); |
BuildGeometry (gpPbuffer->surface, gpPbuffer->colorScheme, gpPbuffer->subdivisions, gpPbuffer->xyRatio, |
&(gpPbuffer->polyList), &(gpPbuffer->lineList), &(gpPbuffer->pointList), 0, 0); |
dirtyPbuffer (gpPbuffer); |
GetWindowPortBounds (window, &rectPort); |
InvalWindowRect (window, &rectPort); |
break; |
case kQuote: |
gpPbuffer->subdivisions = (GLuint) (gpPbuffer->subdivisions * 1.414213); |
aglSetCurrentContext (gpPbuffer->aglContext); |
BuildGeometry (gpPbuffer->surface, gpPbuffer->colorScheme, gpPbuffer->subdivisions, gpPbuffer->xyRatio, |
&(gpPbuffer->polyList), &(gpPbuffer->lineList), &(gpPbuffer->pointList), 0, 0); |
dirtyPbuffer (gpPbuffer); |
GetWindowPortBounds (window, &rectPort); |
InvalWindowRect (window, &rectPort); |
break; |
case kBackSlash: // next higher surface |
gpPbuffer->surface += 1; |
gpPbuffer->surface %= kSurfaces; |
aglSetCurrentContext (gpPbuffer->aglContext); |
BuildGeometry (gpPbuffer->surface, gpPbuffer->colorScheme, gpPbuffer->subdivisions, gpPbuffer->xyRatio, |
&(gpPbuffer->polyList), &(gpPbuffer->lineList), &(gpPbuffer->pointList), 0, 0); |
dirtyPbuffer (gpPbuffer); |
GetWindowPortBounds (window, &rectPort); |
InvalWindowRect (window, &rectPort); |
break; |
#if kUseMultiSample |
case kM: // multisample rotate |
pContextInfo->modeFSAA = (pContextInfo->modeFSAA + 1) % (kFSAANice + 1); |
switch (pContextInfo->modeFSAA) { |
case kFSAAOff: |
glDisable (GL_MULTISAMPLE_ARB); |
break; |
case kFSAAFast: |
glEnable (GL_MULTISAMPLE_ARB); |
glHint (GL_MULTISAMPLE_FILTER_HINT_NV, GL_FASTEST); |
break; |
case kFSAANice: |
glEnable (GL_MULTISAMPLE_ARB); |
glHint (GL_MULTISAMPLE_FILTER_HINT_NV, GL_NICEST); |
break; |
} |
GetWindowPortBounds (window, &rectPort); |
InvalWindowRect (window, &rectPort); |
#endif |
break; |
} |
} |
} |
} |
return result; |
} |
// --------------------------------- |
void createNewWindow (void) |
{ |
EventHandlerRef ref; |
EventTypeSpec list[] = { { kEventClassWindow, kEventWindowCollapsing }, |
{ kEventClassWindow, kEventWindowShown }, |
{ kEventClassWindow, kEventWindowActivated }, |
{ kEventClassWindow, kEventWindowClose }, |
{ kEventClassWindow, kEventWindowDrawContent }, |
{ kEventClassWindow, kEventWindowBoundsChanged }, |
{ kEventClassWindow, kEventWindowZoomed }, |
{ kEventClassKeyboard, kEventRawKeyDown }, |
{ kEventClassKeyboard, kEventRawKeyUp } }; |
WindowRef window = NULL; |
pRecContext pContextInfo = (pRecContext) NewPtrClear (sizeof (recContext)); // memory for window record |
initialConditions (pContextInfo); |
CreateWindowFromNib (nibRef, CFSTR("MainWindow"), &window); // build window |
if (window) |
{ |
SetWRefCon (window, (long) pContextInfo); // point to the window record in the ref con of the window |
InstallWindowEventHandler (window, gWinEvtHandler, GetEventTypeCount (list), list, (void*)window, &ref); // add event handler |
ShowWindow (window); |
if (!pContextInfo->timer) { |
pContextInfo->time = UpTime (); |
InstallEventLoopTimer (GetCurrentEventLoop(), 0, 0.01, getTimerUPP (), (void *) window, &pContextInfo->timer); |
} |
} |
} |
// --------------------------------- |
// window event handler |
static pascal OSStatus windowEvtHndlr (EventHandlerCallRef myHandler, EventRef event, void* userData) |
{ |
#pragma unused (userData) |
WindowRef window = NULL; |
pRecContext pContextInfo = NULL; |
Rect rectPort = {0,0,0,0}; |
CGRect viewRect = {{0.0f, 0.0f}, {0.0f, 0.0f}}; |
OSStatus result = eventNotHandledErr; |
UInt32 class = GetEventClass (event); |
UInt32 kind = GetEventKind (event); |
switch (class) { |
case kEventClassKeyboard: |
switch (kind) { |
case kEventRawKeyDown: |
result = handleKeyInput (myHandler, event, true, userData); |
break; |
case kEventRawKeyUp: |
result = handleKeyInput (myHandler, event, false, userData); |
break; |
} |
break; |
case kEventClassWindow: |
GetEventParameter(event, kEventParamDirectObject, typeWindowRef, NULL, sizeof(WindowRef), NULL, &window); |
switch (kind) { |
case kEventWindowCollapsing: |
pContextInfo = GetCurrentContextInfo (window); |
GetWindowPortBounds (window, &rectPort); |
drawGL (window, false); // in this case just draw content |
CompositeGLBufferIntoWindow( pContextInfo->aglContext, &rectPort, window); |
result = UpdateCollapsedWindowDockTile (window); |
break; |
case kEventWindowActivated: // called on click activation and initially |
// handle any differences between active and not active here |
break; |
case kEventWindowDrawContent: // will receive one prior to being shown... |
drawGL (window, true); |
break; |
case kEventWindowClose: // called when window is being closed (close box) |
HideWindow (window); |
pContextInfo = GetCurrentContextInfo (window); |
disposeGL (pContextInfo); |
DisposePtr ((Ptr) pContextInfo); |
SetWRefCon (window, NULL); |
break; |
case kEventWindowShown: // called on initial show (not on un-minimize) |
// draw content is received prior to this so if you handle that then not drawing required here |
if (window == FrontWindow ()) |
SetUserFocusWindow (window); |
break; |
case kEventWindowBoundsChanged: // called for resize and moves (drag) |
pContextInfo = GetCurrentContextInfo (window); |
if (pContextInfo) { // if we have a vlaid context |
GetWindowPortBounds (window, &rectPort); // get the bounds and set the view rect |
viewRect.size.width = (float) (rectPort.right - rectPort.left); |
viewRect.size.height = (float) (rectPort.bottom - rectPort.top); |
if ((viewRect.size.height < pContextInfo->camera.viewHeight) || // if we are shrinking the window |
(viewRect.size.width < pContextInfo->camera.viewWidth)) { |
// resize prior to update |
resizeGL (pContextInfo->aglContext, &pContextInfo->camera, pContextInfo->shapeSize, viewRect); // forces updateContext & viewport update if required |
drawGL (window, true); // force immediate update to get live resize on shrink |
} else { |
// resize to update context virtual screen |
resizeGL (pContextInfo->aglContext, &pContextInfo->camera, pContextInfo->shapeSize, viewRect); // forces updateContext & viewport update if required |
if (pContextInfo->currentVS != aglGetVirtualScreen (pContextInfo->aglContext)) { // if virtual screen (i.e. renderer) changes |
pContextInfo->currentVS = aglGetVirtualScreen (pContextInfo->aglContext); // sync renderer |
drawGL (window, true); // force immediate update to get update on screen (renderer) switch |
} |
} |
} |
break; |
case kEventWindowZoomed: // called when user clicks on zoom button (occurs after the window has been zoomed) |
// use this if you need to some special here as you always get a kEventWindowBoundsChanged event |
break; |
} |
break; |
} |
return result; |
} |
// --------------------------------- |
// application level event handler |
static pascal OSStatus appEvtHndlr (EventHandlerCallRef myHandler, EventRef event, void* userData) |
{ |
#pragma unused (myHandler, userData) |
OSStatus result = eventNotHandledErr; |
Rect rectPort; |
pRecContext pContextInfo = NULL; |
WindowRef window = FrontWindow (); |
UInt32 class = GetEventClass (event); |
UInt32 kind = GetEventKind (event); |
HICommand command; |
if (window) { // do we have a window? |
GetWindowPortBounds (window, &rectPort); // get bounds for later inval |
pContextInfo = GetCurrentContextInfo (window); |
} |
switch (class) { |
case kEventClassMouse: |
handleWindowMouseEvents (myHandler, event); |
break; |
case kEventClassCommand: |
switch (kind) { |
case kEventProcessCommand: |
GetEventParameter (event, kEventParamDirectObject, kEventParamHICommand, NULL, sizeof(command), NULL, &command); // get command |
switch (command.commandID) { |
case kHICommandNew: |
createNewWindow (); |
result = noErr; |
break; |
case kHICommandClose: |
if (window) { |
HideWindow (window); |
disposeGL (GetCurrentContextInfo(window)); // dump our structures |
DisposeWindow (window); // if it exists dump it |
} |
break; |
case kHICommandQuit: |
//memory will get dumped on exit, other callbacks/timers are for this process only |
break; |
case 'gogo': |
if (gpPbuffer) { // have pbuffer info (global control) |
gpPbuffer->animate = 1 - gpPbuffer->animate; |
} |
break; |
case 'info': |
if (pContextInfo) { // have window info |
pContextInfo->info = 1 - pContextInfo->info; |
GetWindowPortBounds (window, &rectPort); |
InvalWindowRect (window, &rectPort); |
} |
break; |
} |
break; |
case kEventCommandUpdateStatus: // menu updates |
GetEventParameter (event, kEventParamDirectObject, kEventParamHICommand, NULL, sizeof(command), NULL, &command); // get command |
switch (command.commandID) { |
case kHICommandClose: |
if (pContextInfo) |
EnableMenuItem (GetMenuHandle (kMainMenu), kCloseMenuItem); |
else |
DisableMenuItem (GetMenuHandle (kMainMenu), kCloseMenuItem); |
break; |
case 'gogo': |
if (pContextInfo) { |
EnableMenuItem (GetMenuHandle (kMainMenu), kAnimateMenuItem); |
CheckMenuItem (GetMenuHandle (kMainMenu), kAnimateMenuItem, gpPbuffer->animate); |
} else { |
DisableMenuItem (GetMenuHandle (kMainMenu), kAnimateMenuItem); |
CheckMenuItem (GetMenuHandle (kMainMenu), kAnimateMenuItem, kAnimateState); |
} |
break; |
case 'info': |
if (pContextInfo) { |
EnableMenuItem (GetMenuHandle (kMainMenu), kInfoMenuItem); |
CheckMenuItem (GetMenuHandle (kMainMenu), kInfoMenuItem, pContextInfo->info); |
} else { |
DisableMenuItem (GetMenuHandle (kMainMenu), kInfoMenuItem); |
CheckMenuItem (GetMenuHandle (kMainMenu), kInfoMenuItem, kInfoState); |
} |
break; |
} |
result = noErr; |
break; |
} |
break; |
} |
return result; |
} |
#pragma mark ==== main ==== |
int main(int argc, char* argv[]) |
{ |
OSStatus err; |
EventHandlerRef ref; |
EventTypeSpec list[] = { { kEventClassCommand, kEventProcessCommand }, |
{ kEventClassCommand, kEventCommandUpdateStatus }, |
{ kEventClassMouse, kEventMouseDown },// handle trackball functionality globaly because there is only a single user |
{ kEventClassMouse, kEventMouseUp }, |
{ kEventClassMouse, kEventMouseDragged }, |
{ kEventClassMouse, kEventMouseWheelMoved } }; |
ProcessSerialNumber psn = { 0, kCurrentProcess }; |
Boolean fPbufferSupport = true; |
long i; |
EventRef windowEvent; |
gStartTime = UpTime (); // get app start time |
// Create a Nib reference passing the name of the nib file (without the .nib extension) |
// CreateNibReference only searches into the application bundle. |
err = CreateNibReference(CFSTR("main"), &nibRef); |
require_noerr( err, CantGetNibRef ); |
err = SetMenuBarFromNib(nibRef, CFSTR("MainMenu")); |
require_noerr( err, CantSetMenuBar ); |
gEvtHandler = NewEventHandlerUPP(appEvtHndlr); |
err = InstallApplicationEventHandler (gEvtHandler, GetEventTypeCount (list), list, 0, &ref); |
gWinEvtHandler = NewEventHandlerUPP(windowEvtHndlr); |
getCurrentCaps (); |
// ensure we know when display configs are changed |
gConfigEDMUPP = NewDMExtendedNotificationUPP (handleConfigDMEvent); // for display change notification |
DMRegisterExtendedNotifyProc (gConfigEDMUPP, NULL, NULL, &psn); |
StartHIDInput (); |
#pragma mark *** check for pbuffer functionality *** |
for (i = 0; i < gNumDisplays; i++) { |
// ensure pbuffer extension is supported (cover issue in 10.3 that Radeon 9000 did not report correct pbuffer support) |
// a previous version of this sample had the work around for the GeForce 4 that was incorrect |
if (!(gDisplayCaps[i].fPixelBuffer) && (0 != strcmp(gDisplayCaps[i].strRendererName, "ATI Radeon 9000 OpenGL Engine"))) |
fPbufferSupport = false; |
} |
if (err == noErr) { // post new window event |
err = MacCreateEvent (nil, kEventClassCommand, kEventProcessCommand, 0, kEventAttributeNone, &windowEvent); |
if (err == noErr) { |
HICommand command; // set HI command parameter... |
command.commandID = kHICommandNew; command.attributes = 0; |
command.menu.menuRef = 0; command.menu.menuItemIndex = 0; |
err = SetEventParameter(windowEvent, kEventParamDirectObject, kEventParamHICommand, sizeof(command), &command); |
if (err == noErr) |
err = PostEventToQueue (GetCurrentEventQueue(), windowEvent, kEventPriorityHigh); |
ReleaseEvent (windowEvent); |
} |
} |
// Call the event loop |
if (fPbufferSupport) { |
// alloc/init Pbuffer |
#pragma mark *** allocate and initialize pbuffer structure *** |
gpPbuffer = (pRecPbuffer) NewPtr (sizeof (recPbuffer)); |
initialConditionsPbuffer (gpPbuffer); |
if (noErr != buildPbuffer (gpPbuffer)) |
reportError ("buildPbuffer failed."); |
else // we are doing fine, good ahead and run application |
RunApplicationEventLoop(); |
} else |
reportError ("Pbuffers not supported, must use Mac OS X v10.3 or later, exiting"); |
// Exiting... |
if (gConfigEDMUPP) { // dispose UPP for DM notifications |
DisposeDMExtendedNotificationUPP (gConfigEDMUPP); |
gConfigEDMUPP = NULL; |
} |
if (gDisplayCaps) { // dispose diaplsy capabilities info |
free (gDisplayCaps); |
gDisplayCaps = NULL; |
} |
#pragma mark *** dispose pbuffer *** |
if (gpPbuffer) { |
disposePbuffer (gpPbuffer); |
DisposePtr ((Ptr) gpPbuffer); |
gpPbuffer = NULL; |
} |
//CantCreateWindow: |
CantSetMenuBar: |
CantGetNibRef: |
// We don't need the nib reference anymore. |
DisposeNibReference (nibRef); |
return err; |
} |
Copyright © 2004 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2004-03-26