// // kGLView.m // CocoaGL // // Created by Katherine Tattersall on Thu Jul 11 2002. // Copyright (c) 2001 ZeroByZero. All rights reserved. // #import "kGLView.h" #define GRAIN 2 @interface kGLView (privatestuff) - (void) initGL; - (void) setUpLights; - (NSImage *) findImage: (NSString *)resource; - (void) initStars; - (void) drawStar; @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 - (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; } } preferred_time = false; } - (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; preferred_time = false; } // reduces the number of particles - (IBAction)reduce: (id)sender { [pe reduceParticles]; } // increases the number of particles - (IBAction)increase: (id)sender { [pe increaseParticles]; } - (IBAction)changeTextures: (id)sender { if( textures ) // disable lighting and take the checkmark off of lighting menu item { glDisable( GL_TEXTURE_2D ); [sender setState: NSOffState]; } else // enable lighting and add a checkmark to lighting menu item { glEnable( GL_TEXTURE_2D ); [sender setState: NSOnState]; } textures = !textures; preferred_time = false; } - (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; preferred_time = false; } - (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); textures = true; [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; // [self initStars]; f = [[NSGLFont alloc] bitmapFont: @"Monaco" atSize:24]; of = [[NSGLFont alloc] outlineFont: @"Monaco" atSize:24 extrude:0.2]; first = YES; drawing_type = GL_POLYGON; pe = [[NSGLParticleEngine alloc] initWithFile: @"../Bubbles.txt"]; pe2 = [[NSGLParticleEngine alloc] initWithFile: @"../Missile.txt"]; pe_flag = UPDATE_AND_CREATE; NSLog(@"end initGL"); } - (void) dealloc { // gluDeleteQuadric( quadobj ); [super dealloc]; } - (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 ]; fpstimer = [ [NSTimer scheduledTimerWithTimeInterval: FPS_TIME_INTERVAL target:self selector:@selector(detailAnal) //go to this method whenever the time comes userInfo:nil repeats:YES] retain ]; preferred_time = false; // Add our timers to the EventTracking loop [[NSRunLoop currentRunLoop] addTimer: time forMode: NSEventTrackingRunLoopMode]; [[NSRunLoop currentRunLoop] addTimer: fpstimer forMode: NSEventTrackingRunLoopMode]; // Add our timers to the ModelPanel loop [[NSRunLoop currentRunLoop] addTimer: time forMode: NSModalPanelRunLoopMode]; [[NSRunLoop currentRunLoop] addTimer: fpstimer forMode: NSModalPanelRunLoopMode]; NSLog(@"end awakeFromNib"); } /* This method analyses the speed of the frames. If the frames are going too slowly, then we lower the number of particles. If the speed is above our threshhold, we can raise the number of particles. This allows us to have fairly smooth animation, whether we're on our iBooks or our new G4s ^_^ heh heh heh... You can tell the analysis to start by changing preferred_time to false in any method (don't change it in draw though, it'll just have the details analyzed forever!) */ - (void) detailAnal { float fps = frame_count / FPS_TIME_INTERVAL; float saved_average = user_avg_fps; // get the user average, which will be shown to the user if( user_get_average < (1 / FPS_TIME_INTERVAL) ) { ub_avg_fps += fps; user_get_average++; } else { ub_avg_fps += fps; user_get_average++; user_avg_fps = (ub_avg_fps / user_get_average); user_get_average = 0; ub_avg_fps = 0; // if something changes that we didn't know about to slow the time down // we should recalculate the preferred time if(user_avg_fps < PERF_LOW_LIMIT - GRAIN ) preferred_time = false; } if( !preferred_time ) { if( user_avg_fps >= PERF_LOW_LIMIT ) { // NSLog(@"increasing detail, fps = %f", user_avg_fps); [pe increaseParticles]; } else // if( user_avg_fps < PERF_LOW_LIMIT ) { // NSLog(@"reducing detail, fps = %f", user_avg_fps); [pe reduceParticles]; if( saved_average > user_avg_fps && saved_average > PERF_LOW_LIMIT - GRAIN) { NSLog(@"preferred: numParticles = %d, fps = %f", [pe numParticles], user_avg_fps); preferred_time = true; } } } frame_count = 0; } // 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,200.0f); // modified for deeper view glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } - (void)drawFrame { // int i; frame_count++; // Clear the buffers! glLoadIdentity(); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); // this is where you want to draw //glBindTexture( GL_TEXTURE_2D, texture[0] ); //glTranslatef( 0.0f, 0.0f, 10.0); [pe draw]; [pe updateEngineWithFlag: pe_flag]; glTranslatef( 0.0f, 0.0f, -10.0); [pe2 draw]; [pe2 updateEngineWithFlag: pe_flag]; glLoadIdentity(); glTranslatef( 0.0f, 1.0f, -20.0); glColor3f( 0.5,0.75, 0.5 ); glPrint( f, 0, 0, "Current fps = %f", user_avg_fps ); glLoadIdentity(); glTranslatef( 0.0f, 1.0f, -20.0); glColor3f( 0.5,0.75, 0.0 ); glTranslatef( 0.0f, 1.0f, 10.0); glRotatef(rot, 0.0, 1.0, 0.0 ); rot+=2; glColor3f(0.2, 0, 0); glPrint( of, 0, 0, "Current fps = %f", user_avg_fps ); // flush the buffer! (send drawing to the screen) [[self openGLContext] flushBuffer]; } - (void)initStars { int i; for( i = 0; i < NUMSTARS; i++ ) { // my god... it's full of stars.... StarArray[i].distance = ((float)i/(float)NUMSTARS)*20.0f; StarArray[i].angle = (float)i/(float)NUMSTARS; StarArray[i].r = rand()%256; StarArray[i].g = rand()%256; StarArray[i].b = rand()%256; StarArray[i].tilt = 90; StarArray[i].i = i; //NSLog(@"star %i: d=%f, a=%f", i, StarArray[i].distance, StarArray[i].angle); } } - (void)getTextures { NSURL *starURL; NSImage *starimage; NSLog(@"start getting the image"); // get the URL for the star image // the application is in the build directory, one level up from our star starURL = [[NSURL alloc] initFileURLWithPath: @"../star.bmp"]; if( starURL != nil ) NSLog(@"NSURL != nil"); // load the star from the file starimage = [[NSImage alloc] initWithContentsOfURL: starURL ]; // name the starimage if( starimage == nil ) starimage = [self findImage: @"star.bmp"]; [starimage setName:@"star"]; // generate the texture objects references glGenTextures( NUMTEXTURES, texture ); glBindTexture( GL_TEXTURE_2D, texture[0] ); [self loadPicture: @"star"]; // release the memory that was allocated [starURL release]; [starimage release]; } - (NSImage *) findImage: (NSString *)resource { NSOpenPanel *oPanel = [NSOpenPanel openPanel]; NSArray *fileTypes = [NSArray arrayWithObject:@"bmp"]; NSImage *image; int result; [oPanel setAllowsMultipleSelection:NO]; result = [oPanel runModalForDirectory:NSHomeDirectory() file:resource types:fileTypes]; if( result == NSOKButton ) { NSString *aFile = [[oPanel filenames] objectAtIndex:0]; image = [[NSImage alloc] initWithContentsOfFile:aFile]; } if( image != nil ) return image; else { NSRunCriticalAlertPanel(@"Can't find resource", [resource stringByAppendingString:@" was not found"], @"OK",nil,nil); exit(0); } } - (void) loadPicture: (NSString *) name { NSBitmapImageRep *bitmap; // to use a picture, it must be included in the resources (under groups and files) NSImage *image; // 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 this bitmap is null, we should really free the texture if (bitmap == nil) { NSLog(@"in LoadGLTextures : NSBitmapImageRep not loaded"); return; } // we are aligned by BYTES in an NSBitmapImageRep glPixelStorei(GL_UNPACK_ALIGNMENT, 1 ); // put the image into texture memory // the image has Red-Green-Blue-Alpha chanels, is width*height in size // and is made up of unsigned bytes glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, [bitmap size].width, [bitmap size].height, 0, GL_RGB, GL_UNSIGNED_BYTE, [bitmap bitmapData]); Error("self load picture glTexImage2D"); // when we make the image smaller, use a linear filter on this texture glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); // when we magnify the image, use a linear filter on this texture glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // let the bitmap go [bitmap release]; } /* 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" ); } -(void) mouseDown:(NSEvent *)theEvent { pe_flag = CREATE; } // handle mouse up events (left mouse button) - (void)mouseUp:(NSEvent *)theEvent { NSLog( @"Mouse L up"); pe_flag = UPDATE_AND_CREATE; } // 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