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.
BasicOpenGLView.m
// |
// File: BasicOpenGLView.m |
// |
// Abstract: Basic OpenGL View with Renderer information |
// |
// Version: 1.1 - minor fixes. |
// 1.0 - Original release. |
// |
// |
// Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple 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 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. |
// |
// Copyright ( C ) 2003-2007 Apple Inc. All Rights Reserved. |
// |
#import "BasicOpenGLView.h" |
#import "GLCheck.h" |
#import "trackball.h" |
#import "drawinfo.h" |
// ================================== |
// simple cube data |
GLint cube_num_vertices = 8; |
GLfloat cube_vertices [8][3] = { |
{1.0, 1.0, 1.0}, {1.0, -1.0, 1.0}, {-1.0, -1.0, 1.0}, {-1.0, 1.0, 1.0}, |
{1.0, 1.0, -1.0}, {1.0, -1.0, -1.0}, {-1.0, -1.0, -1.0}, {-1.0, 1.0, -1.0} }; |
GLfloat cube_vertex_colors [8][3] = { |
{1.0, 1.0, 1.0}, {1.0, 1.0, 0.0}, {0.0, 1.0, 0.0}, {0.0, 1.0, 1.0}, |
{1.0, 0.0, 1.0}, {1.0, 0.0, 0.0}, {0.0, 0.0, 0.0}, {0.0, 0.0, 1.0} }; |
GLint num_faces = 6; |
short cube_faces [6][4] = { |
{3, 2, 1, 0}, {2, 3, 7, 6}, {0, 1, 5, 4}, {3, 0, 4, 7}, {1, 2, 6, 5}, {4, 5, 6, 7} }; |
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; |
BasicOpenGLView * gTrackingViewInfo = NULL; |
// time and message info |
CFAbsoluteTime gMsgPresistance = 10.0f; |
// error output |
GLString * gErrStringTex; |
float gErrorTime; |
// ================================== |
#pragma mark ---- OpenGL Capabilities ---- |
// GL configuration info globals |
// see GLCheck.h for more info |
GLCaps * gDisplayCaps = NULL; // array of GLCaps |
CGDisplayCount gNumDisplays = 0; |
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); |
initCapsTexture (gDisplayCaps, gNumDisplays); // (re)init the texture for printing caps |
} |
} |
#pragma mark ---- Utilities ---- |
static CFAbsoluteTime gStartTime = 0.0f; |
// set app start time |
static void setStartTime (void) |
{ |
gStartTime = CFAbsoluteTimeGetCurrent (); |
} |
// --------------------------------- |
// return float elpased time in seconds since app start |
static CFAbsoluteTime getElapsedTime (void) |
{ |
return CFAbsoluteTimeGetCurrent () - gStartTime; |
} |
#pragma mark ---- Error Reporting ---- |
// error reporting as both window message and debugger string |
void reportError (char * strError) |
{ |
NSMutableDictionary *attribs = [NSMutableDictionary dictionary]; |
[attribs setObject: [NSFont fontWithName: @"Monaco" size: 9.0f] forKey: NSFontAttributeName]; |
[attribs setObject: [NSColor whiteColor] forKey: NSForegroundColorAttributeName]; |
gErrorTime = getElapsedTime (); |
NSString * errString = [NSString stringWithFormat:@"Error: %s (at time: %0.1f secs).", strError, gErrorTime]; |
NSLog (@"%@\n", errString); |
if (gErrStringTex) |
[gErrStringTex setString:errString withAttributes:attribs]; |
else { |
gErrStringTex = [[GLString alloc] initWithString:errString withAttributes:attribs withTextColor:[NSColor colorWithDeviceRed:1.0f green:1.0f blue:1.0f alpha:1.0f] withBoxColor:[NSColor colorWithDeviceRed:1.0f green:0.0f blue:0.0f alpha:0.3f] withBorderColor:[NSColor colorWithDeviceRed:1.0f green:0.0f blue:0.0f alpha:0.8f]]; |
} |
} |
// --------------------------------- |
// if error dump gl errors to debugger string, return error |
GLenum glReportError (void) |
{ |
GLenum err = glGetError(); |
if (GL_NO_ERROR != err) |
reportError ((char *) gluErrorString (err)); |
return err; |
} |
#pragma mark ---- OpenGL Utils ---- |
// --------------------------------- |
// draw our simple cube based on current modelview and projection matrices |
static void drawCube (GLfloat fSize) |
{ |
long f, i; |
if (1) { |
glColor3f (1.0, 0.5, 0.0); |
glBegin (GL_QUADS); |
for (f = 0; f < num_faces; f++) |
for (i = 0; i < 4; i++) { |
glColor3f (cube_vertex_colors[cube_faces[f][i]][0], cube_vertex_colors[cube_faces[f][i]][1], cube_vertex_colors[cube_faces[f][i]][2]); |
glVertex3f(cube_vertices[cube_faces[f][i]][0] * fSize, cube_vertices[cube_faces[f][i]][1] * fSize, cube_vertices[cube_faces[f][i]][2] * fSize); |
} |
glEnd (); |
} |
if (1) { |
glColor3f (0.0, 0.0, 0.0); |
for (f = 0; f < num_faces; f++) { |
glBegin (GL_LINE_LOOP); |
for (i = 0; i < 4; i++) |
glVertex3f(cube_vertices[cube_faces[f][i]][0] * fSize, cube_vertices[cube_faces[f][i]][1] * fSize, cube_vertices[cube_faces[f][i]][2] * fSize); |
glEnd (); |
} |
} |
} |
// =================================== |
@implementation BasicOpenGLView |
// pixel format definition |
+ (NSOpenGLPixelFormat*) basicPixelFormat |
{ |
NSOpenGLPixelFormatAttribute attributes [] = { |
NSOpenGLPFAWindow, |
NSOpenGLPFADoubleBuffer, // double buffered |
NSOpenGLPFADepthSize, (NSOpenGLPixelFormatAttribute)16, // 16 bit depth buffer |
(NSOpenGLPixelFormatAttribute)nil |
}; |
return [[[NSOpenGLPixelFormat alloc] initWithAttributes:attributes] autorelease]; |
} |
// --------------------------------- |
// update the projection matrix based on camera and view info |
- (void) updateProjection |
{ |
GLdouble ratio, radians, wd2; |
GLdouble left, right, top, bottom, near, far; |
[[self openGLContext] makeCurrentContext]; |
// set projection |
glMatrixMode (GL_PROJECTION); |
glLoadIdentity (); |
near = -camera.viewPos.z - shapeSize * 0.5; |
if (near < 0.00001) |
near = 0.00001; |
far = -camera.viewPos.z + shapeSize * 0.5; |
if (far < 1.0) |
far = 1.0; |
radians = 0.0174532925 * camera.aperture / 2; // half aperture degrees to radians |
wd2 = near * tan(radians); |
ratio = camera.viewWidth / (float) camera.viewHeight; |
if (ratio >= 1.0) { |
left = -ratio * wd2; |
right = ratio * wd2; |
top = wd2; |
bottom = -wd2; |
} else { |
left = -wd2; |
right = wd2; |
top = wd2 / ratio; |
bottom = -wd2 / ratio; |
} |
glFrustum (left, right, bottom, top, near, far); |
[self updateCameraString]; |
} |
// --------------------------------- |
// updates the contexts model view matrix for object and camera moves |
- (void) updateModelView |
{ |
[[self openGLContext] makeCurrentContext]; |
// move view |
glMatrixMode (GL_MODELVIEW); |
glLoadIdentity (); |
gluLookAt (camera.viewPos.x, camera.viewPos.y, camera.viewPos.z, |
camera.viewPos.x + camera.viewDir.x, |
camera.viewPos.y + camera.viewDir.y, |
camera.viewPos.z + camera.viewDir.z, |
camera.viewUp.x, camera.viewUp.y ,camera.viewUp.z); |
// if we have trackball rotation to map (this IS the test I want as it can be explicitly 0.0f) |
if ((gTrackingViewInfo == self) && gTrackBallRotation[0] != 0.0f) |
glRotatef (gTrackBallRotation[0], gTrackBallRotation[1], gTrackBallRotation[2], gTrackBallRotation[3]); |
else { |
} |
// accumlated world rotation via trackball |
glRotatef (worldRotation[0], worldRotation[1], worldRotation[2], worldRotation[3]); |
// object itself rotating applied after camera rotation |
glRotatef (objectRotation[0], objectRotation[1], objectRotation[2], objectRotation[3]); |
rRot[0] = 0.0f; // reset animation rotations (do in all cases to prevent rotating while moving with trackball) |
rRot[1] = 0.0f; |
rRot[2] = 0.0f; |
[self updateCameraString]; |
} |
// --------------------------------- |
// 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 |
- (void) resizeGL |
{ |
NSRect rectView = [self bounds]; |
// ensure camera knows size changed |
if ((camera.viewHeight != rectView.size.height) || |
(camera.viewWidth != rectView.size.width)) { |
camera.viewHeight = rectView.size.height; |
camera.viewWidth = rectView.size.width; |
glViewport (0, 0, camera.viewWidth, camera.viewHeight); |
[self updateProjection]; // update projection matrix |
[self updateInfoString]; |
} |
} |
// --------------------------------- |
// move camera in z axis |
-(void)mouseDolly: (NSPoint) location |
{ |
GLfloat dolly = (gDollyPanStartPoint[1] -location.y) * -camera.viewPos.z / 300.0f; |
camera.viewPos.z += dolly; |
if (camera.viewPos.z == 0.0) // do not let z = 0.0 |
camera.viewPos.z = 0.0001; |
gDollyPanStartPoint[0] = location.x; |
gDollyPanStartPoint[1] = location.y; |
} |
// --------------------------------- |
// move camera in x/y plane |
- (void)mousePan: (NSPoint) location |
{ |
GLfloat panX = (gDollyPanStartPoint[0] - location.x) / (900.0f / -camera.viewPos.z); |
GLfloat panY = (gDollyPanStartPoint[1] - location.y) / (900.0f / -camera.viewPos.z); |
camera.viewPos.x -= panX; |
camera.viewPos.y -= panY; |
gDollyPanStartPoint[0] = location.x; |
gDollyPanStartPoint[1] = location.y; |
} |
// --------------------------------- |
// sets the camera data to initial conditions |
- (void) resetCamera |
{ |
camera.aperture = 40; |
camera.rotPoint = gOrigin; |
camera.viewPos.x = 0.0; |
camera.viewPos.y = 0.0; |
camera.viewPos.z = -10.0; |
camera.viewDir.x = -camera.viewPos.x; |
camera.viewDir.y = -camera.viewPos.y; |
camera.viewDir.z = -camera.viewPos.z; |
camera.viewUp.x = 0; |
camera.viewUp.y = 1; |
camera.viewUp.z = 0; |
} |
// --------------------------------- |
// given a delta time in seconds and current rotation accel, velocity and position, update overall object rotation |
- (void) updateObjectRotationForTimeDelta:(CFAbsoluteTime)deltaTime |
{ |
// update rotation based on vel and accel |
float rotation[4] = {0.0f, 0.0f, 0.0f, 0.0f}; |
GLfloat fVMax = 2.0; |
short i; |
// do velocities |
for (i = 0; i < 3; i++) { |
rVel[i] += rAccel[i] * deltaTime * 30.0; |
if (rVel[i] > fVMax) { |
rAccel[i] *= -1.0; |
rVel[i] = fVMax; |
} else if (rVel[i] < -fVMax) { |
rAccel[i] *= -1.0; |
rVel[i] = -fVMax; |
} |
rRot[i] += rVel[i] * deltaTime * 30.0; |
while (rRot[i] > 360.0) |
rRot[i] -= 360.0; |
while (rRot[i] < -360.0) |
rRot[i] += 360.0; |
} |
rotation[0] = rRot[0]; |
rotation[1] = 1.0f; |
addToRotationTrackball (rotation, objectRotation); |
rotation[0] = rRot[1]; |
rotation[1] = 0.0f; rotation[2] = 1.0f; |
addToRotationTrackball (rotation, objectRotation); |
rotation[0] = rRot[2]; |
rotation[2] = 0.0f; rotation[3] = 1.0f; |
addToRotationTrackball (rotation, objectRotation); |
} |
// --------------------------------- |
// per-window timer function, basic time based animation preformed here |
- (void)animationTimer:(NSTimer *)timer |
{ |
BOOL shouldDraw = NO; |
if (fAnimate) { |
CFTimeInterval deltaTime = CFAbsoluteTimeGetCurrent () - time; |
if (deltaTime > 10.0) // skip pauses |
return; |
else { |
// if we are not rotating with trackball in this window |
if (!gTrackball || (gTrackingViewInfo != self)) { |
[self updateObjectRotationForTimeDelta: deltaTime]; // update object rotation |
} |
shouldDraw = YES; // force redraw |
} |
} |
time = CFAbsoluteTimeGetCurrent (); //reset time in all cases |
// if we have current messages |
if (((getElapsedTime () - msgTime) < gMsgPresistance) || ((getElapsedTime () - gErrorTime) < gMsgPresistance)) |
shouldDraw = YES; // force redraw |
if (YES == shouldDraw) |
[self drawRect:[self bounds]]; // redraw now instead dirty to enable updates during live resize |
} |
#pragma mark ---- Text Drawing ---- |
// these functions create or update GLStrings one should expect to have to regenerate the image, bitmap and texture when the string changes thus these functions are not particularly light weight |
- (void) updateInfoString |
{ // update info string texture |
NSString * string = [NSString stringWithFormat:@"(%0.0f x %0.0f) \n%s \n%s", [self bounds].size.width, [self bounds].size.height, glGetString (GL_RENDERER), glGetString (GL_VERSION)]; |
if (infoStringTex) |
[infoStringTex setString:string withAttributes:stanStringAttrib]; |
else { |
infoStringTex = [[GLString alloc] initWithString:string withAttributes:stanStringAttrib withTextColor:[NSColor colorWithDeviceRed:1.0f green:1.0f blue:1.0f alpha:1.0f] withBoxColor:[NSColor colorWithDeviceRed:0.5f green:0.5f blue:0.5f alpha:0.5f] withBorderColor:[NSColor colorWithDeviceRed:0.8f green:0.8f blue:0.8f alpha:0.8f]]; |
} |
} |
// --------------------------------- |
- (void) createHelpString |
{ |
NSString * string = [NSString stringWithFormat:@"Cmd-A: animate Cmd-I: show info \n'h': toggle help 'c': toggle OpenGL caps"]; |
helpStringTex = [[GLString alloc] initWithString:string withAttributes:stanStringAttrib withTextColor:[NSColor colorWithDeviceRed:1.0f green:1.0f blue:1.0f alpha:1.0f] withBoxColor:[NSColor colorWithDeviceRed:0.0f green:0.5f blue:0.0f alpha:0.5f] withBorderColor:[NSColor colorWithDeviceRed:0.3f green:0.8f blue:0.3f alpha:0.8f]]; |
} |
// --------------------------------- |
- (void) createMessageString |
{ |
NSString * string = [NSString stringWithFormat:@"No messages..."]; |
msgStringTex = [[GLString alloc] initWithString:string withAttributes:stanStringAttrib withTextColor:[NSColor colorWithDeviceRed:1.0f green:1.0f blue:1.0f alpha:1.0f] withBoxColor:[NSColor colorWithDeviceRed:0.5f green:0.5f blue:0.5f alpha:0.5f] withBorderColor:[NSColor colorWithDeviceRed:0.8f green:0.8f blue:0.8f alpha:0.8f]]; |
} |
// --------------------------------- |
- (void) updateCameraString |
{ // update info string texture |
static recCamera savedCamera; |
// This is a compromise between a heavy comparison |
// and updating the camera texture when not needed |
// what is faster the comparison or the texture update |
// only empirical data on a particular configuration |
// will yield a real answer |
if ( (savedCamera.viewPos.x == camera.viewPos.x) && |
(savedCamera.viewPos.y == camera.viewPos.y) && |
(savedCamera.viewPos.z == camera.viewPos.z) && |
(savedCamera.viewDir.x == camera.viewDir.x) && |
(savedCamera.viewDir.y == camera.viewDir.y) && |
(savedCamera.viewDir.z == camera.viewDir.z) && |
(savedCamera.aperture == camera.aperture) ) |
{ |
return; // Don't update texture! (which usually is more expensive than the comparison above) |
} else { |
NSString * string = [NSString stringWithFormat:@"Camera at (%0.1f, %0.1f, %0.1f) looking at (%0.1f, %0.1f, %0.1f) with %0.1f aperture", camera.viewPos.x, camera.viewPos.y, camera.viewPos.z, camera.viewDir.x, camera.viewDir.y, camera.viewDir.z, camera.aperture]; |
if (camStringTex) |
[camStringTex setString:string withAttributes:stanStringAttrib]; |
else { |
camStringTex = [[GLString alloc] initWithString:string withAttributes:stanStringAttrib withTextColor:[NSColor colorWithDeviceRed:1.0f green:1.0f blue:1.0f alpha:1.0f] withBoxColor:[NSColor colorWithDeviceRed:0.5f green:0.5f blue:0.5f alpha:0.5f] withBorderColor:[NSColor colorWithDeviceRed:0.8f green:0.8f blue:0.8f alpha:0.8f]]; |
} |
} |
savedCamera = camera; |
} |
// --------------------------------- |
// draw text info using our GLString class for much more optimized text drawing |
- (void) drawInfo |
{ |
GLint matrixMode; |
GLboolean depthTest = glIsEnabled (GL_DEPTH_TEST); |
GLfloat height, width, messageTop = 10.0f; |
height = camera.viewHeight; |
width = camera.viewWidth; |
// 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.0f / width, -2.0f / height, 1.0f); |
glTranslatef (-width / 2.0f, -height / 2.0f, 0.0f); |
glColor4f (1.0f, 1.0f, 1.0f, 1.0f); |
[infoStringTex drawAtPoint:NSMakePoint (10.0f, height - [infoStringTex frameSize].height - 10.0f)]; |
[camStringTex drawAtPoint:NSMakePoint (10.0f, messageTop)]; |
messageTop += [camStringTex frameSize].height + 3.0f; |
if (fDrawHelp) |
[helpStringTex drawAtPoint:NSMakePoint (floor ((width - [helpStringTex frameSize].width) / 2.0f), floor ((height - [helpStringTex frameSize].height) / 3.0f))]; |
if (fDrawCaps) { |
long renderer; |
[[self pixelFormat] getValues:&renderer forAttribute:NSOpenGLPFARendererID forVirtualScreen:[[self openGLContext] currentVirtualScreen]]; |
drawCaps (gDisplayCaps, gNumDisplays, renderer, width); |
} |
// message string |
float currTime = getElapsedTime (); |
if ((currTime - msgTime) < gMsgPresistance) { |
GLfloat comp = (gMsgPresistance - getElapsedTime () + msgTime) * 0.1; // premultiplied fade |
glColor4f (comp, comp, comp, comp); |
[msgStringTex drawAtPoint:NSMakePoint (10.0f, messageTop)]; |
messageTop += [msgStringTex frameSize].height + 3.0f; |
} |
// global error message |
if ((currTime - gErrorTime) < gMsgPresistance) { |
GLfloat comp = (gMsgPresistance - getElapsedTime () + gErrorTime) * 0.1; // premultiplied fade |
glColor4f (comp, comp, comp, comp); |
[gErrStringTex drawAtPoint:NSMakePoint (10.0f, messageTop)]; |
} |
// reset orginal martices |
glPopMatrix(); // GL_MODELVIEW |
glMatrixMode (GL_PROJECTION); |
glPopMatrix(); |
glMatrixMode (matrixMode); |
glDisable (GL_TEXTURE_RECTANGLE_EXT); |
glDisable (GL_BLEND); |
if (depthTest) |
glEnable (GL_DEPTH_TEST); |
glReportError (); |
} |
#pragma mark ---- IB Actions ---- |
-(IBAction) animate: (id) sender |
{ |
fAnimate = 1 - fAnimate; |
if (fAnimate) |
[animateMenuItem setState: NSOnState]; |
else |
[animateMenuItem setState: NSOffState]; |
} |
// --------------------------------- |
-(IBAction) info: (id) sender |
{ |
fInfo = 1 - fInfo; |
if (fInfo) |
[infoMenuItem setState: NSOnState]; |
else |
[infoMenuItem setState: NSOffState]; |
[self setNeedsDisplay: YES]; |
} |
#pragma mark ---- Method Overrides ---- |
-(void)keyDown:(NSEvent *)theEvent |
{ |
NSString *characters = [theEvent characters]; |
if ([characters length]) { |
unichar character = [characters characterAtIndex:0]; |
switch (character) { |
case 'h': |
// toggle help |
fDrawHelp = 1 - fDrawHelp; |
[self setNeedsDisplay: YES]; |
break; |
case 'c': |
// toggle caps |
fDrawCaps = 1 - fDrawCaps; |
[self setNeedsDisplay: YES]; |
break; |
} |
} |
} |
// --------------------------------- |
- (void)mouseDown:(NSEvent *)theEvent // trackball |
{ |
if ([theEvent modifierFlags] & NSControlKeyMask) // send to pan |
[self rightMouseDown:theEvent]; |
else if ([theEvent modifierFlags] & NSAlternateKeyMask) // send to dolly |
[self otherMouseDown:theEvent]; |
else { |
NSPoint location = [self convertPoint:[theEvent locationInWindow] fromView:nil]; |
location.y = camera.viewHeight - location.y; |
gDolly = GL_FALSE; // no dolly |
gPan = GL_FALSE; // no pan |
gTrackball = GL_TRUE; |
startTrackball (location.x, location.y, 0, 0, camera.viewWidth, camera.viewHeight); |
gTrackingViewInfo = self; |
} |
} |
// --------------------------------- |
- (void)rightMouseDown:(NSEvent *)theEvent // pan |
{ |
NSPoint location = [self convertPoint:[theEvent locationInWindow] fromView:nil]; |
location.y = camera.viewHeight - location.y; |
if (gTrackball) { // if we are currently tracking, end trackball |
if (gTrackBallRotation[0] != 0.0) |
addToRotationTrackball (gTrackBallRotation, worldRotation); |
gTrackBallRotation [0] = gTrackBallRotation [1] = gTrackBallRotation [2] = gTrackBallRotation [3] = 0.0f; |
} |
gDolly = GL_FALSE; // no dolly |
gPan = GL_TRUE; |
gTrackball = GL_FALSE; // no trackball |
gDollyPanStartPoint[0] = location.x; |
gDollyPanStartPoint[1] = location.y; |
gTrackingViewInfo = self; |
} |
// --------------------------------- |
- (void)otherMouseDown:(NSEvent *)theEvent //dolly |
{ |
NSPoint location = [self convertPoint:[theEvent locationInWindow] fromView:nil]; |
location.y = camera.viewHeight - location.y; |
if (gTrackball) { // if we are currently tracking, end trackball |
if (gTrackBallRotation[0] != 0.0) |
addToRotationTrackball (gTrackBallRotation, worldRotation); |
gTrackBallRotation [0] = gTrackBallRotation [1] = gTrackBallRotation [2] = gTrackBallRotation [3] = 0.0f; |
} |
gDolly = GL_TRUE; |
gPan = GL_FALSE; // no pan |
gTrackball = GL_FALSE; // no trackball |
gDollyPanStartPoint[0] = location.x; |
gDollyPanStartPoint[1] = location.y; |
gTrackingViewInfo = self; |
} |
// --------------------------------- |
- (void)mouseUp:(NSEvent *)theEvent |
{ |
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, worldRotation); |
gTrackBallRotation [0] = gTrackBallRotation [1] = gTrackBallRotation [2] = gTrackBallRotation [3] = 0.0f; |
} |
gTrackingViewInfo = NULL; |
} |
// --------------------------------- |
- (void)rightMouseUp:(NSEvent *)theEvent |
{ |
[self mouseUp:theEvent]; |
} |
// --------------------------------- |
- (void)otherMouseUp:(NSEvent *)theEvent |
{ |
[self mouseUp:theEvent]; |
} |
// --------------------------------- |
- (void)mouseDragged:(NSEvent *)theEvent |
{ |
NSPoint location = [self convertPoint:[theEvent locationInWindow] fromView:nil]; |
location.y = camera.viewHeight - location.y; |
if (gTrackball) { |
rollToTrackball (location.x, location.y, gTrackBallRotation); |
[self setNeedsDisplay: YES]; |
} else if (gDolly) { |
[self mouseDolly: location]; |
[self updateProjection]; // update projection matrix (not normally done on draw) |
[self setNeedsDisplay: YES]; |
} else if (gPan) { |
[self mousePan: location]; |
[self setNeedsDisplay: YES]; |
} |
} |
// --------------------------------- |
- (void)scrollWheel:(NSEvent *)theEvent |
{ |
float wheelDelta = [theEvent deltaX] +[theEvent deltaY] + [theEvent deltaZ]; |
if (wheelDelta) |
{ |
GLfloat deltaAperture = wheelDelta * -camera.aperture / 200.0f; |
camera.aperture += deltaAperture; |
if (camera.aperture < 0.1) // do not let aperture <= 0.1 |
camera.aperture = 0.1; |
if (camera.aperture > 179.9) // do not let aperture >= 180 |
camera.aperture = 179.9; |
[self updateProjection]; // update projection matrix |
[self setNeedsDisplay: YES]; |
} |
} |
// --------------------------------- |
- (void)rightMouseDragged:(NSEvent *)theEvent |
{ |
[self mouseDragged: theEvent]; |
} |
// --------------------------------- |
- (void)otherMouseDragged:(NSEvent *)theEvent |
{ |
[self mouseDragged: theEvent]; |
} |
// --------------------------------- |
- (void) drawRect:(NSRect)rect |
{ |
// setup viewport and prespective |
[self resizeGL]; // forces projection matrix update (does test for size changes) |
[self updateModelView]; // update model view matrix for object |
// clear our drawable |
glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); |
// model view and projection matricies already set |
drawCube (1.5f); // draw scene |
if (fInfo) |
[self drawInfo]; |
if ([self inLiveResize] && !fAnimate) |
glFlush (); |
else |
[[self openGLContext] flushBuffer]; |
glReportError (); |
} |
// --------------------------------- |
// set initial OpenGL state (current context is set) |
// called after context is created |
- (void) prepareOpenGL |
{ |
long swapInt = 1; |
[[self openGLContext] setValues:&swapInt forParameter:NSOpenGLCPSwapInterval]; // set to vbl sync |
// init GL stuff here |
glEnable(GL_DEPTH_TEST); |
glShadeModel(GL_SMOOTH); |
glEnable(GL_CULL_FACE); |
glFrontFace(GL_CCW); |
glPolygonOffset (1.0f, 1.0f); |
glClearColor(0.0f, 0.0f, 0.0f, 0.0f); |
[self resetCamera]; |
shapeSize = 7.0f; // max radius of of objects |
// init fonts for use with strings |
NSFont * font =[NSFont fontWithName:@"Helvetica" size:12.0]; |
stanStringAttrib = [[NSMutableDictionary dictionary] retain]; |
[stanStringAttrib setObject:font forKey:NSFontAttributeName]; |
[stanStringAttrib setObject:[NSColor whiteColor] forKey:NSForegroundColorAttributeName]; |
[font release]; |
// ensure strings are created |
[self createHelpString]; |
[self createMessageString]; |
} |
// --------------------------------- |
// this can be a troublesome call to do anything heavyweight, as it is called on window moves, resizes, and display config changes. So be |
// careful of doing too much here. |
- (void) update // window resizes, moves and display changes (resize, depth and display config change) |
{ |
msgTime = getElapsedTime (); |
[msgStringTex setString:[NSString stringWithFormat:@"update at %0.1f secs", msgTime] withAttributes:stanStringAttrib]; |
[super update]; |
if (![self inLiveResize]) {// if not doing live resize |
[self updateInfoString]; // to get change in renderers will rebuld string every time (could test for early out) |
getCurrentCaps (); // this call checks to see if the current config changed in a reasonably lightweight way to prevent expensive re-allocations |
} |
} |
// --------------------------------- |
-(id) initWithFrame: (NSRect) frameRect |
{ |
NSOpenGLPixelFormat * pf = [BasicOpenGLView basicPixelFormat]; |
self = [super initWithFrame: frameRect pixelFormat: pf]; |
return self; |
} |
// --------------------------------- |
- (BOOL)acceptsFirstResponder |
{ |
return YES; |
} |
// --------------------------------- |
- (BOOL)becomeFirstResponder |
{ |
return YES; |
} |
// --------------------------------- |
- (BOOL)resignFirstResponder |
{ |
return YES; |
} |
// --------------------------------- |
- (void) awakeFromNib |
{ |
setStartTime (); // get app start time |
getCurrentCaps (); // get current GL capabilites for all displays |
// set start values... |
rVel[0] = 0.3; rVel[1] = 0.1; rVel[2] = 0.2; |
rAccel[0] = 0.003; rAccel[1] = -0.005; rAccel[2] = 0.004; |
fInfo = 1; |
fAnimate = 1; |
time = CFAbsoluteTimeGetCurrent (); // set animation time start time |
fDrawHelp = 1; |
// start animation timer |
timer = [NSTimer timerWithTimeInterval:(1.0f/60.0f) target:self selector:@selector(animationTimer:) userInfo:nil repeats:YES]; |
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode]; |
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSEventTrackingRunLoopMode]; // ensure timer fires during resize |
} |
@end |
Copyright © 2007 Apple Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2007-10-22