2 Bullet Continuous Collision Detection and Physics Library
3 Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/
5 This software is provided 'as-is', without any express or implied warranty.
6 In no event will the authors be held liable for any damages arising from the use of this software.
7 Permission is granted to anyone to use this software for any purpose,
8 including commercial applications, and to alter it and redistribute it freely,
9 subject to the following restrictions:
11 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
12 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
13 3. This notice may not be removed or altered from any source distribution.
16 #import "BTOpenGLView.h"
18 #include <CoreFoundation/CoreFoundation.h>
19 #import <OpenGL/OpenGL.h>
20 #import <Carbon/Carbon.h>
22 #import "BTFullScreenWindow.h"
23 #import "BTGLUTKeyAdapter.h"
26 #pragma mark Private Methods
28 @interface BTOpenGLView (Internal)
31 - (void) boundsDidChange: (NSNotification *) notification;
32 - (void) setVBL: (BOOL*) vbl forContext: (NSOpenGLContext*) context;
33 - (void) setMultithreaded: (BOOL) mt;
34 - (NSOpenGLPixelFormat*) windowedPixelFormat: (BOOL*) antialias;
38 @implementation BTOpenGLView
41 #pragma mark Bootstrap
43 - (id)initWithFrame:(NSRect)frameRect
45 self = [super initWithFrame:frameRect];
48 NSLog( @"BTOpenGLView::initWithFrame - Unable to init" );
56 _setupBoundsChangeNotification = NO;
59 Set up the windowed context -- it will be assigned to the
62 _windowedContext = [[NSOpenGLContext alloc] initWithFormat: [self windowedPixelFormat: &_multisample]
65 [self setVBL: &_vblSync forContext: _windowedContext];
67 if (_windowedContext == nil)
69 NSLog(@"Got nil windowed context");
75 Setup and start the update timer.
77 _interval = 1.0 / 60.0;
78 _timer = [[NSTimer scheduledTimerWithTimeInterval: _interval
80 selector: @selector(update)
82 repeats: YES ] retain];
84 [[NSRunLoop currentRunLoop] addTimer: _timer forMode: NSEventTrackingRunLoopMode];
94 [_delegate contextWillBeDestroyed];
96 [_windowedContext release];
98 [[NSNotificationCenter defaultCenter] removeObserver: self];
103 - (void) awakeFromNib
105 NSWindow *window = [self window];
106 [window setAcceptsMouseMovedEvents: YES];
107 [window makeFirstResponder: self];
108 [window setInitialFirstResponder: self];
111 - (void)drawRect:(NSRect)rect
117 #pragma mark Public API
119 - (void) setDelegate: (id <BTOpenGLDisplayDelegate>) delegate
121 // we don't retain delegates
122 _delegate = delegate;
125 - (id <BTOpenGLDisplayDelegate>) delegate
130 - (void) setTargetFPS: (float) fps
132 float newInterval = 1.0 / fps;
133 if ( ABS( newInterval - _interval ) > 1.0e-3 )
135 _interval = newInterval;
140 _timer = [[NSTimer scheduledTimerWithTimeInterval: _interval
142 selector: @selector(update)
144 repeats: YES ] retain];
146 [[NSRunLoop currentRunLoop] addTimer: _timer forMode: NSEventTrackingRunLoopMode];
153 return 1.0 / _interval;
161 - (void) setFullscreen: (BOOL) fullscreen
163 if ( fullscreen == _isFullScreen ) return;
165 _isFullScreen = fullscreen;
166 _suppressResize = YES;
170 _windowedWindow = [self window];
173 Detach & retain the content view from the non-fullscreen window.
176 NSView *contentView = [_windowedWindow contentView];
177 [contentView retain];
178 [contentView removeFromSuperviewWithoutNeedingDisplay];
181 Create a fullscreen window, attach the content view,
182 and release the content view since the fullscreen window retained it.
185 _fullscreenWindow = [[BTFullScreenWindow alloc] initForScreen: [_windowedWindow screen]];
186 [_fullscreenWindow setContentView: contentView ];
187 [_fullscreenWindow makeKeyAndOrderFront:nil];
188 [contentView release];
193 [_windowedWindow orderOut: nil];
196 Now, use the SetSystemUIMode API to auto-hide the dock
199 OSStatus error = SetSystemUIMode( kUIModeContentSuppressed, 0 );
202 NSLog(@"Error couldn't set SystemUIMode: %ld", (long)error);
206 else if ( _fullscreenWindow )
209 Detach and retain the content view from the fullscreen window
211 NSView *contentView = [_fullscreenWindow contentView];
212 [contentView retain];
213 [contentView removeFromSuperviewWithoutNeedingDisplay];
216 Reparent the content view to the non-fullscreen window,
217 and release it since it's now owned by the non-fullscreen window
219 [_windowedWindow setContentView: contentView];
220 [contentView release];
222 [_windowedWindow makeKeyAndOrderFront: nil];
225 Release the fullscreen window
227 [_fullscreenWindow orderOut: nil];
228 [_fullscreenWindow release];
229 _fullscreenWindow = nil;
232 Restore dock's normal behaior
235 OSStatus error = SetSystemUIMode( kUIModeNormal, 0 );
238 NSLog(@"Error couldn't set SystemUIMode: %ld", (long)error);
242 _suppressResize = NO;
244 [self boundsDidChange: nil];
249 return _isFullScreen;
252 - (void) setMultisampleRendering: (BOOL) multisample
254 if ( multisample == _multisample ) return;
256 _multisample = multisample;
260 NSOpenGLContext *oldWindowedContext = _windowedContext;
261 _windowedContext = [[NSOpenGLContext alloc] initWithFormat: [self windowedPixelFormat: &_multisample]
262 shareContext: oldWindowedContext ];
264 [self setVBL: &_vblSync forContext: _windowedContext];
266 [oldWindowedContext release];
267 [_windowedContext setView: self];
268 [_windowedContext makeCurrentContext];
269 [_windowedContext update];
273 - (BOOL) multisampleRendering
278 - (void) setVBLSync: (BOOL) sync
280 if ( sync == _vblSync ) return;
283 [self setVBL: &_vblSync forContext: _windowedContext];
291 - (NSOpenGLContext *) openGLContext
293 return _windowedContext;
297 #pragma mark NSView Overrides
304 - (BOOL) acceptsFirstResponder
309 - (void) keyDown:(NSEvent *)theEvent
311 int key = [[theEvent charactersIgnoringModifiers] characterAtIndex:0];
313 if ( BTKeyIsAlpha( key ))
315 [ _delegate keyPressed: key ];
319 [_delegate specialKeyPressed: BTKeyTranslateKeyCodeToSpecial( key )];
323 - (void) keyUp:(NSEvent *)theEvent
325 int key = [[theEvent charactersIgnoringModifiers] characterAtIndex:0];
327 if ( BTKeyIsAlpha( key ))
329 [ _delegate keyReleased: key ];
333 [_delegate specialKeyReleased: BTKeyTranslateKeyCodeToSpecial( key )];
337 - (void) mouseDown: (NSEvent *) event
339 int button = GLUT_LEFT_BUTTON;
340 switch( [event buttonNumber] )
342 case 0: button = GLUT_LEFT_BUTTON; break;
343 case 1: button = GLUT_RIGHT_BUTTON; break;
344 case 2: button = GLUT_MIDDLE_BUTTON; break;
348 if ( _modifierFlags & NSControlKeyMask ) button = GLUT_RIGHT_BUTTON;
349 else if ( _modifierFlags & NSAlternateKeyMask ) button = GLUT_MIDDLE_BUTTON;
351 [_delegate mouseButtonPressed: button];
354 - (void) mouseUp: (NSEvent *) event
356 int button = GLUT_LEFT_BUTTON;
357 switch( [event buttonNumber] )
359 case 0: button = GLUT_LEFT_BUTTON; break;
360 case 1: button = GLUT_RIGHT_BUTTON; break;
361 case 2: button = GLUT_MIDDLE_BUTTON; break;
365 if ( _modifierFlags & NSControlKeyMask ) button = GLUT_RIGHT_BUTTON;
366 else if ( _modifierFlags & NSAlternateKeyMask ) button = GLUT_MIDDLE_BUTTON;
368 [_delegate mouseButtonReleased: button];
371 -(void) mouseMoved: (NSEvent *) event
373 float dx = [event deltaX],
376 NSPoint locationInView = [self convertPoint: [event locationInWindow] fromView: nil ];
378 [_delegate mouseMoved: NSMakePoint( dx, dy )];
379 [_delegate newMousePosition: locationInView];
382 -(void) mouseDragged: (NSEvent *) event
384 [self mouseMoved: event];
387 - (void) scrollWheel: (NSEvent *) event
389 float dx = [event deltaX],
392 [_delegate scrollWheel: NSMakePoint( dx, dy )];
395 - (void)flagsChanged:(NSEvent *) event
397 _modifierFlags = [event modifierFlags];
406 if ( !_setupBoundsChangeNotification )
408 _setupBoundsChangeNotification = YES;
411 This is hacky, but basically, we can't handle bounds-changing
412 ops correctly until everything's set up correctly.
414 [self setPostsBoundsChangedNotifications:YES];
415 [[NSNotificationCenter defaultCenter] addObserver: self
416 selector: @selector( boundsDidChange: )
417 name: NSViewFrameDidChangeNotification
423 [_windowedContext setView:self];
426 [_windowedContext makeCurrentContext];
432 [_delegate contextCreated];
436 glEnable (GL_MULTISAMPLE_ARB);
437 // this fucks up text rendering, on nVIDIA, at least
438 //glHint (GL_MULTISAMPLE_FILTER_HINT_NV, GL_NICEST);
442 glDisable( GL_MULTISAMPLE_ARB );
445 [self setMultithreaded: NO];
450 contextSize.width = CGDisplayPixelsWide(kCGDirectMainDisplay);
451 contextSize.height = CGDisplayPixelsHigh(kCGDirectMainDisplay);
455 contextSize = [self bounds].size;
458 [_delegate contextWillResize];
459 [_delegate contextResized: contextSize];
460 [_delegate contextDidResize];
461 [_delegate contextStateInvalidated];
464 double now = CFAbsoluteTimeGetCurrent();
466 if ( _delegate) [_delegate display: now - _lastFrameTime];
469 glClearColor( 0.5, 0.5, 0.5, 1 );
470 glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
473 _lastFrameTime = now;
476 [[NSOpenGLContext currentContext] flushBuffer];
483 static unsigned int frameCounter = 1;
484 static double lastCheckTime = 0;
486 double elapsed = now - lastCheckTime;
489 _currentFPS = (float) ( ((double) frameCounter ) / elapsed );
499 - (void) boundsDidChange: (NSNotification *) notification
501 if ( _suppressResize ) return;
503 [_windowedContext setView:self];
504 [_windowedContext makeCurrentContext];
505 [_windowedContext update];
507 NSSize contextSize = [self bounds].size;
511 [_delegate contextWillResize];
512 [_delegate contextResized: contextSize ];
513 [_delegate contextDidResize];
517 glViewport( 0, 0, (int) contextSize.width, (int) contextSize.height );
521 - (void) setVBL: (BOOL*) vbl forContext: (NSOpenGLContext*) context
523 GLint value = *vbl ? 1 : 0;
524 [context setValues: &value forParameter: NSOpenGLCPSwapInterval];
526 *vbl = value ? YES : NO;
529 - (void) setMultithreaded: (BOOL) mt
531 CGLError err = kCGLNoError;
532 CGLContextObj ctx = CGLGetCurrentContext();
534 // Enable Apple's multi-threaded GL engine -- it's generally useful for
535 // high vertex throughput. Not high fragment situations
539 err = CGLEnable( ctx, kCGLCEMPEngine );
543 err = CGLDisable( ctx, kCGLCEMPEngine );
546 if (err != kCGLNoError )
548 NSLog( @"BTOpenGLView -setMultithreaded: forContext: -- Unable to %s multithreaded GL",
549 mt ? "enable" : "disable" );
553 - (NSOpenGLPixelFormat*) windowedPixelFormat: (BOOL*) antialias
555 NSOpenGLPixelFormatAttribute aaAttrs[] =
557 NSOpenGLPFADoubleBuffer,
558 NSOpenGLPFAAccelerated,
559 NSOpenGLPFADepthSize, (NSOpenGLPixelFormatAttribute)32,
560 NSOpenGLPFAStencilSize, (NSOpenGLPixelFormatAttribute)8,
561 NSOpenGLPFASingleRenderer,
562 NSOpenGLPFASampleBuffers, (NSOpenGLPixelFormatAttribute)( 1 ),
563 NSOpenGLPFASamples, (NSOpenGLPixelFormatAttribute)( 4 ),
564 NSOpenGLPFAScreenMask, (NSOpenGLPixelFormatAttribute) CGDisplayIDToOpenGLDisplayMask(kCGDirectMainDisplay),
565 NSOpenGLPFANoRecovery,
566 (NSOpenGLPixelFormatAttribute)0
569 NSOpenGLPixelFormatAttribute vanillaAttrs[] =
571 NSOpenGLPFADoubleBuffer,
572 NSOpenGLPFAAccelerated,
573 NSOpenGLPFADepthSize, (NSOpenGLPixelFormatAttribute)32,
574 NSOpenGLPFAStencilSize, (NSOpenGLPixelFormatAttribute)8,
575 NSOpenGLPFASingleRenderer,
576 NSOpenGLPFASampleBuffers, (NSOpenGLPixelFormatAttribute)( 0 ),
577 NSOpenGLPFASamples, (NSOpenGLPixelFormatAttribute)( 0 ),
578 NSOpenGLPFAScreenMask, (NSOpenGLPixelFormatAttribute) CGDisplayIDToOpenGLDisplayMask(kCGDirectMainDisplay),
579 NSOpenGLPFANoRecovery,
580 (NSOpenGLPixelFormatAttribute)0
583 NSOpenGLPixelFormat* fmt = 0;
587 fmt = [[NSOpenGLPixelFormat alloc] initWithAttributes: (NSOpenGLPixelFormatAttribute*) aaAttrs];
591 fmt = [[NSOpenGLPixelFormat alloc] initWithAttributes: (NSOpenGLPixelFormatAttribute*) vanillaAttrs];
596 fmt = [[NSOpenGLPixelFormat alloc] initWithAttributes: (NSOpenGLPixelFormatAttribute*) vanillaAttrs];