// // kGLView.m // CocoaGL // // Created by Katherine Tattersall on Thu Jul 11 2002. // Copyright (c) 2001 ZeroByZero. All rights reserved. // #import "kGLView.h" @interface kGLView (privatestuff) - (void)initGL; - (void) setUpLights; @end @implementation kGLView // i would like to thank the academy... um... i mean... er... // Dietmar Planitzer, whose code I shamelessly stole from in order to implement // this full screen stuff // if you try to use the NSOpenGLContext's full screen method, you lose // all the nice things about Cocoa, and have to implement your own even queue // because it seems that nothing else will pass it to you // if you try to use the OmniGroup's OpenGL Example code, you will ALSO lose // the event queue and will have to implement it yourself // THIS is much easier to code AND understand - (IBAction)toggleFullScreen:(id)sender { if( FullScreenOn == true ) // we need to go back to non-full screen { [FullScreenWindow close]; [StartingWindow setContentView: self]; [StartingWindow makeKeyAndOrderFront: self]; [StartingWindow makeFirstResponder: self]; FullScreenOn = false; } else // FullScreenOn == false { unsigned int windowStyle; NSRect contentRect; StartingWindow = [NSApp keyWindow]; windowStyle = NSBorderlessWindowMask; contentRect = [[NSScreen mainScreen] frame]; FullScreenWindow = [[NSWindow alloc] initWithContentRect:contentRect styleMask: windowStyle backing:NSBackingStoreBuffered defer: NO]; if(FullScreenWindow != nil) { NSLog(@"Window was created"); [FullScreenWindow setTitle: @"myWindow"]; [FullScreenWindow setReleasedWhenClosed: YES]; [FullScreenWindow setContentView: self]; [FullScreenWindow makeKeyAndOrderFront:self ]; [FullScreenWindow setLevel: NSScreenSaverWindowLevel - 1]; [FullScreenWindow makeFirstResponder:self]; FullScreenOn = true; } } } - (IBAction)setSolid:(id)sender { drawing_type = GL_POLYGON; } - (IBAction)setWireframe:(id)sender { drawing_type = GL_LINE_LOOP; } - (IBAction)setPoints:(id)sender { drawing_type = GL_POINTS; } - (IBAction)changeLighting: (id)sender { if( lighting ) // disable lighting and take the checkmark off of lighting menu item { glDisable( GL_LIGHTING ); [sender setState: NSOffState]; } else // enable lighting and add a checkmark to lighting menu item { glEnable( GL_LIGHTING ); [sender setState: NSOnState]; } lighting = !lighting; } - (IBAction) changeBlending:(id)sender { if( blending ) // disable blending and take the checkmark off of blending menu item { glDisable( GL_BLEND ); glEnable(GL_DEPTH_TEST); [sender setState: NSOffState]; } else // enable blending and add a checkmark to blending menu item { glEnable( GL_BLEND ); glDisable(GL_DEPTH_TEST); [sender setState: NSOnState]; } blending = !blending; } - (id)initWithFrame:(NSRect) frameRect { // First, we must create an NSOpenGLPixelFormatAttribute NSOpenGLPixelFormat *nsglFormat; NSOpenGLPixelFormatAttribute attr[] = { NSOpenGLPFADoubleBuffer, NSOpenGLPFAAccelerated, NSOpenGLPFAColorSize, BITS_PER_PIXEL, NSOpenGLPFADepthSize, DEPTH_SIZE, 0 }; [self setPostsFrameChangedNotifications: YES]; // Next, we initialize the NSOpenGLPixelFormat itself nsglFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attr]; // Check for errors in the creation of the NSOpenGLPixelFormat // If we could not create one, return nil (the OpenGL is not initialized, and // we should send an error message to the user at this point) if(!nsglFormat) { NSLog(@"Invalid format... terminating."); return nil; } // Now we create the the CocoaGL instance, using our initial frame and the NSOpenGLPixelFormat self = [super initWithFrame:frameRect pixelFormat:nsglFormat]; [nsglFormat release]; // If there was an error, we again should probably send an error message to the user if(!self) { NSLog(@"Self not created... terminating."); return nil; } // Now we set this context to the current context (means that its now drawable) [[self openGLContext] makeCurrentContext]; // Finally, we call the initGL method (no need to make this method too long or complex) [self initGL]; return self; } - (void)initGL { // Set the clear color to black. This is the color that your background will be if you don't draw to it // If setting to black, we needn't specifically say this, but it's good style to mention explicitly // You can set the clear color to something else later, but it should not be between a glBegin and glEnd glClearColor(0.0f, 0.0f, 0.0f, 0.0f); // Set the clear depth to 1. This is the initial (default) value. // Set the depth function. This specifies when something will be drawn. GL_LESS passes if the incoming // depth value is lESS than the current value // Turn the depth test on glClearDepth(1.0f); glDepthFunc(GL_LESS); glEnable(GL_DEPTH_TEST); // Set up a hint telling the computer to create the nicest (aka "costliest" or "most correct") // image it can // This hint is for the quality of color and texture mapping glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // This hint is for antialiasing glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST); glEnable(GL_TEXTURE_2D); glColorMaterial( GL_FRONT_AND_BACK, GL_DIFFUSE ); glEnable(GL_COLOR_MATERIAL); [self setUpLights]; [self getTextures]; // the following two lines start blending glBlendFunc(GL_SRC_ALPHA,GL_ONE); glEnable( GL_BLEND ); // unfortunately, the depth test will delete some of the sides if it isn't turned off // the correct way to do this is to sort the polygon glDisable(GL_DEPTH_TEST); blending = true; first = YES; drawing_type = GL_POLYGON; NSLog(@"end initGL"); } - (void) setUpLights { // enable the lighting and set our boolean to true glEnable(GL_LIGHTING); lighting = true; // set light 1's ambient light color LightAmbient[0] = 0.5f; LightAmbient[1] = 0.5f; LightAmbient[2] = 0.5f; LightAmbient[3] = 1.0f; // set light 1's diffuse light color LightDiffuse[0] = 1.0f; LightDiffuse[1] = 1.0f; LightDiffuse[2] = 1.0f; LightDiffuse[3] = 1.0f; // position light 1's origin LightPosition[0]= 0.0f; LightPosition[1]= 0.0f; LightPosition[2]= 2.0f; LightPosition[3]= 1.0f; // tell OpenGL about what we just did and turn the light on glLightfv(GL_LIGHT1, GL_AMBIENT, LightAmbient); glLightfv(GL_LIGHT1, GL_DIFFUSE, LightDiffuse); glLightfv(GL_LIGHT1, GL_POSITION,LightPosition); glEnable(GL_LIGHT1); } // awake from nib is called when the window opens - (void)awakeFromNib { NSLog(@"awakeFromNib"); time = [ [NSTimer scheduledTimerWithTimeInterval: DEFAULT_TIME_INTERVAL target:self selector:@selector(drawFrame) //go to this method whenever the time comes userInfo:nil repeats:YES] retain ]; // Add our timers to the EventTracking loop [[NSRunLoop currentRunLoop] addTimer: time forMode: NSEventTrackingRunLoopMode]; // Add our timers to the ModelPanel loop [[NSRunLoop currentRunLoop] addTimer: time forMode: NSModalPanelRunLoopMode]; NSLog(@"end awakeFromNib"); } // this method is called whenever the window/control is reshaped // it is also called when the control is first opened - (void) reshape { float aspect; NSSize bound = [self frame].size; aspect = bound.width / bound.height; // change the size of the viewport to the new width and height // this controls the affine transformation of x and y from normalized device // coordinates to window coordinates (from the OpenGl 1.1 reference book, 2nd ed) glViewport(0, 0, bound.width, bound.height); glMatrixMode(GL_PROJECTION); // you must reload the identity before this or you'll lose your picture glLoadIdentity(); gluPerspective(45.0f, (GLfloat)aspect, 0.1f,100.0f); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } - (void)drawFrame { // make this control the First Responder if this is the first time to draw the frame // there is probably some notification that would work more efficiently than this silly test // but I don't know what it is if( first ) { first = NO; if ([[NSApp keyWindow] makeFirstResponder:self] ) NSLog( @"self apparently made first responder" ); else NSLog( @"self is not first responder"); } // Clear the buffers! glLoadIdentity(); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // this is where you would want to draw // draw the first die in its normal colors glTranslatef(-2.0,0.0,-10.0); glRotatef(dieangle,1,1.4,0.1); dieangle += 1; glColor4f(1.0,1.0,1.0,0.8); [self drawDie]; // load the identity so that we aren't adding the translations glLoadIdentity(); // draw the second die, tinted with blue glTranslatef(2.0,0.0,-15.0); glRotatef(dieangle,-1,1,-0.1); glColor4f(0.5,0.5,1.0,0.5); [self drawDie]; // flush the buffer! (send drawing to the screen) [[self openGLContext] flushBuffer]; } - (void)drawDie { // glBindTexture is one of the commands that doesn't work within a glBegin - glEnd sequence glBindTexture(GL_TEXTURE_2D, texture[0]); glBegin(drawing_type); glNormal3f(0.0f,1.0f,0.0f); glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, 1.0f,-1.0f); // Top Right Of The Quad (Top) glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, 1.0f,-1.0f); // Top Left Of The Quad (Top) glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 1.0f); // Bottom Left Of The Quad (Top) glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, 1.0f, 1.0f); // Bottom Right Of The Quad (Top) glEnd(); glBindTexture(GL_TEXTURE_2D, texture[1]); glBegin(drawing_type); glNormal3f( 0.0f,-1.0f, 0.0f); glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f,-1.0f, 1.0f); // Top Right Of The Quad (Bottom) glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f,-1.0f, 1.0f); // Top Left Of The Quad (Bottom) glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f,-1.0f,-1.0f); // Bottom Left Of The Quad (Bottom) glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f,-1.0f,-1.0f); // Bottom Right Of The Quad (Bottom) glEnd(); glBindTexture(GL_TEXTURE_2D, texture[2]); glBegin(drawing_type); glNormal3f( 0.0f, 0.0f, 1.0f); glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, 1.0f, 1.0f); // Top Right Of The Quad (Front) glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, 1.0f, 1.0f); // Top Left Of The Quad (Front) glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f,-1.0f, 1.0f); // Bottom Left Of The Quad (Front) glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f,-1.0f, 1.0f); // Bottom Right Of The Quad (Front) glEnd(); glBindTexture(GL_TEXTURE_2D, texture[3]); glBegin(drawing_type); glNormal3f( 0.0f, 0.0f,-1.0f); glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f,-1.0f,-1.0f); // Bottom Left Of The Quad (Back) glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f,-1.0f,-1.0f); // Bottom Right Of The Quad (Back) glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, 1.0f,-1.0f); // Top Right Of The Quad (Back) glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, 1.0f,-1.0f); // Top Left Of The Quad (Back) glEnd(); glBindTexture(GL_TEXTURE_2D, texture[4]); glBegin(drawing_type); glNormal3f(-1.0f, 0.0f, 0.0f); glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, 1.0f, 1.0f); // Top Right Of The Quad (Left) glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, 1.0f,-1.0f); // Top Left Of The Quad (Left) glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f,-1.0f,-1.0f); // Bottom Left Of The Quad (Left) glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f,-1.0f, 1.0f); // Bottom Right Of The Quad (Left) glEnd(); glBindTexture(GL_TEXTURE_2D, texture[5]); glBegin(drawing_type); glNormal3f( 1.0f, 0.0f, 0.0f); glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, 1.0f,-1.0f); // Top Right Of The Quad (Right) glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, 1.0f, 1.0f); // Top Left Of The Quad (Right) glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f,-1.0f, 1.0f); // Bottom Left Of The Quad (Right) glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f,-1.0f,-1.0f); // Bottom Right Of The Quad (Right) glEnd(); } - (void)getTextures { // generate the texture objects references glGenTextures( NUMTEXTURES, texture ); // first we will be working on the each texture individually // we aren't using mipmaps or anything, so we'll just do everything // in a separate method glBindTexture( GL_TEXTURE_2D, texture[0] ); [self loadPicture: @"one"]; glBindTexture( GL_TEXTURE_2D, texture[1] ); [self loadPicture: @"two"]; glBindTexture( GL_TEXTURE_2D, texture[2] ); [self loadPicture: @"three"]; glBindTexture( GL_TEXTURE_2D, texture[3] ); [self loadPicture: @"four"]; glBindTexture( GL_TEXTURE_2D, texture[4] ); [self loadPicture: @"five"]; glBindTexture( GL_TEXTURE_2D, texture[5] ); [self loadPicture: @"six"]; } - (void) loadPicture: (NSString *) name { NSBitmapImageRep *bitmap; NSImage *image; // to use a picture, it must be included in the resources (under groups and files) //NSLog( @"loadJPEG called with argument %s", name); // initialize the image to the correct file (name) image = [ NSImage imageNamed: name ]; // create a bitmap with the correct image data bitmap = [[NSBitmapImageRep alloc]initWithData: [image TIFFRepresentation]]; if (bitmap == nil) NSLog(@"in LoadGLTextures : NSBitmapImageRep not loaded"); NSLog(@"spp: %d, bps: %d, brp: %d", [bitmap samplesPerPixel], [bitmap bitsPerSample], [bitmap bytesPerRow]); NSBitmapImageRep *destinationBitmap = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL pixelsWide:[bitmap pixelsWide] pixelsHigh:[bitmap pixelsHigh] bitsPerSample:8 samplesPerPixel:3 hasAlpha:NO isPlanar:YES colorSpaceName:NSCalibratedRGBColorSpace bytesPerRow:4*[bitmap pixelsWide] bitsPerPixel:32]; // if greyscale, change to rgb if ([bitmap samplesPerPixel] == 2 && [bitmap bitsPerSample] == 8 ) { //Allocate a 32bit RGBA bitmap rep for drawing into. //The destination bitmap has the same dimensions as the source except for the // fact that it has 4 samples per pixel (RGBA) instead of 2 (G/A). //Get the pointer references to the pixel data. //We're using unsigned chars (GLubyte's) so that we can increment by byte (1 byte = 1 sample). GLubyte *srcBuffer = (GLubyte*)[bitmap bitmapData]; GLubyte *dstBuffer = (GLubyte*)[destinationBitmap bitmapData]; //iterate through the 16bit image byte by byte int i; for ( i = 0; i < [bitmap pixelsWide]*[bitmap pixelsHigh]; i++ ) { dstBuffer[i*3] = srcBuffer[i*2]; //put grayscale value into R dstBuffer[i*3+1] = srcBuffer[i*2]; //put grayscale value into G dstBuffer[i*3+2] = srcBuffer[i*2]; //put grayscale value into B } } // this is what we're expecting in the next function, an RGB image! else if ([bitmap samplesPerPixel] == 3 && [bitmap bitsPerSample] == 8 ) { destinationBitmap = bitmap; } else if( [bitmap samplesPerPixel] == 1 && [bitmap bitsPerSample] == 8 ) { GLubyte *srcBuffer = (GLubyte*)[bitmap bitmapData]; GLubyte *dstBuffer = (GLubyte*)[destinationBitmap bitmapData]; //iterate through our 16bit image byte by byte int i; for ( i = 0; i < [bitmap pixelsWide]*[bitmap pixelsHigh]; i++ ) { dstBuffer[i*3] = srcBuffer[i]; //put grayscale value into R dstBuffer[i*3+1] = srcBuffer[i]; //put grayscale value into G dstBuffer[i*3+2] = srcBuffer[i]; //put grayscale value into B } } glPixelStorei(GL_UNPACK_ALIGNMENT, 1 ); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, [destinationBitmap size].width, [destinationBitmap size].height, 0, // right here, we're expecting RGB unsigned bytes, so the bitmap has to be that GL_RGB, GL_UNSIGNED_BYTE, [destinationBitmap bitmapData]); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); } /* This is the preferred way to determine events */ // if you want to get events sent to you, you must make yourself a first responder - (BOOL)acceptsFirstResponder { return YES; } - (BOOL)becomeFirstResponder { return YES; } // handle key down events // if you don't handle this, the system beeps when you press a key (how annoying) - (void)keyDown:(NSEvent *)theEvent { NSLog( @"key down" ); } // handle mouse up events (left mouse button) - (void)mouseUp:(NSEvent *)theEvent { NSLog( @"Mouse L up"); } // handle mouse up events (right mouse button) - (void)rightMouseUp:(NSEvent *)theEvent { NSLog( @"Mouse R up"); } // handle mouse up events (other mouse button) - (void)otherMouseUp:(NSEvent *)theEvent { NSLog( @"Mouse O up"); } @end