Quellcode durchsuchen

Merge pull request #419 from sgrenier/next

Racing sample updates
Steve Grenier vor 13 Jahren
Ursprung
Commit
d686fb2bf2

+ 9 - 1
gameplay/src/PhysicsVehicleWheel.cpp

@@ -14,6 +14,8 @@ PhysicsVehicleWheel::PhysicsVehicleWheel(Node* node, const PhysicsCollisionShape
     _rigidBody = new PhysicsRigidBody(node, shape, parameters);
     _rigidBody = new PhysicsRigidBody(node, shape, parameters);
 
 
     findAncestorAndBind();
     findAncestorAndBind();
+
+    _initialOffset = node->getTranslation();
 }
 }
 
 
 PhysicsVehicleWheel::PhysicsVehicleWheel(Node* node, PhysicsRigidBody* rigidBody)
 PhysicsVehicleWheel::PhysicsVehicleWheel(Node* node, PhysicsRigidBody* rigidBody)
@@ -22,6 +24,8 @@ PhysicsVehicleWheel::PhysicsVehicleWheel(Node* node, PhysicsRigidBody* rigidBody
     _rigidBody = rigidBody;
     _rigidBody = rigidBody;
 
 
     findAncestorAndBind();
     findAncestorAndBind();
+
+    _initialOffset = node->getTranslation();
 }
 }
 
 
 PhysicsVehicleWheel* PhysicsVehicleWheel::create(Node* node, Properties* properties)
 PhysicsVehicleWheel* PhysicsVehicleWheel::create(Node* node, Properties* properties)
@@ -183,7 +187,11 @@ void PhysicsVehicleWheel::transform(Node* node) const
     const btQuaternion& rot = trans.getRotation();
     const btQuaternion& rot = trans.getRotation();
     const btVector3& pos = trans.getOrigin();
     const btVector3& pos = trans.getOrigin();
     node->setRotation(rot.x(), rot.y(), rot.z(), rot.w());
     node->setRotation(rot.x(), rot.y(), rot.z(), rot.w());
-    node->setTranslation(pos.x(), pos.y(), pos.z());
+
+    // Ignore X and Z translation for wheel
+    Vector3 wheelPos = _initialOffset;
+    _host->_node->getMatrix().transformPoint(&wheelPos);
+    node->setTranslation(wheelPos.x, wheelPos.y, wheelPos.z);
 }
 }
 
 
 bool PhysicsVehicleWheel::isFront() const
 bool PhysicsVehicleWheel::isFront() const

+ 1 - 0
gameplay/src/PhysicsVehicleWheel.h

@@ -329,6 +329,7 @@ private:
     PhysicsRigidBody* _rigidBody;
     PhysicsRigidBody* _rigidBody;
     PhysicsVehicle* _host;
     PhysicsVehicle* _host;
     unsigned int _indexInHost;
     unsigned int _indexInHost;
+    Vector3 _initialOffset;
 };
 };
 
 
 }
 }

+ 47 - 5
gameplay/src/PlatformMacOSX.mm

@@ -104,7 +104,7 @@ static View* __view = NULL;
     }
     }
     CGLFlushDrawable((CGLContextObj)[[self openGLContext] CGLContextObj]);
     CGLFlushDrawable((CGLContextObj)[[self openGLContext] CGLContextObj]);
     CGLUnlockContext((CGLContextObj)[[self openGLContext] CGLContextObj]);  
     CGLUnlockContext((CGLContextObj)[[self openGLContext] CGLContextObj]);  
-    
+
     [gameLock unlock];
     [gameLock unlock];
 }
 }
 
 
@@ -116,9 +116,21 @@ static CVReturn MyDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTime
 }
 }
 
 
 - (id) initWithFrame: (NSRect) frame
 - (id) initWithFrame: (NSRect) frame
-{    
+{
+    _game = Game::getInstance();
+    
+    Properties* config = _game->getConfig()->getNamespace("window", true);
+    int samples = config ? config->getInt("samples") : 0;
+    if (samples < 0)
+        samples = 0;
+    
+    // Note: Keep multisampling attributes at the start of the attribute lists since code below
+    // assumes they are array elements 0 through 4.
     NSOpenGLPixelFormatAttribute windowedAttrs[] = 
     NSOpenGLPixelFormatAttribute windowedAttrs[] = 
     {
     {
+        NSOpenGLPFAMultisample,
+        NSOpenGLPFASampleBuffers, samples ? 1 : 0,
+        NSOpenGLPFASamples, samples,
         NSOpenGLPFAAccelerated,
         NSOpenGLPFAAccelerated,
         NSOpenGLPFADoubleBuffer,
         NSOpenGLPFADoubleBuffer,
         NSOpenGLPFAColorSize, 32,
         NSOpenGLPFAColorSize, 32,
@@ -129,6 +141,9 @@ static CVReturn MyDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTime
     };
     };
     NSOpenGLPixelFormatAttribute fullscreenAttrs[] = 
     NSOpenGLPixelFormatAttribute fullscreenAttrs[] = 
     {
     {
+        NSOpenGLPFAMultisample,
+        NSOpenGLPFASampleBuffers, samples ? 1 : 0,
+        NSOpenGLPFASamples, samples,
         NSOpenGLPFADoubleBuffer,
         NSOpenGLPFADoubleBuffer,
         NSOpenGLPFAScreenMask, (NSOpenGLPixelFormatAttribute)CGDisplayIDToOpenGLDisplayMask(CGMainDisplayID()),
         NSOpenGLPFAScreenMask, (NSOpenGLPixelFormatAttribute)CGDisplayIDToOpenGLDisplayMask(CGMainDisplayID()),
         NSOpenGLPFAFullScreen,
         NSOpenGLPFAFullScreen,
@@ -140,16 +155,38 @@ static CVReturn MyDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTime
     };
     };
     NSOpenGLPixelFormatAttribute* attrs = __fullscreen ? fullscreenAttrs : windowedAttrs;
     NSOpenGLPixelFormatAttribute* attrs = __fullscreen ? fullscreenAttrs : windowedAttrs;
     
     
+    // Try to choose a supported pixel format
     NSOpenGLPixelFormat* pf = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs];
     NSOpenGLPixelFormat* pf = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs];
     if (!pf)
     if (!pf)
-        NSLog(@"OpenGL pixel format not supported.");
+    {
+        bool valid = false;
+        while (!pf && samples > 0)
+        {
+            samples /= 2;
+            attrs[2] = samples ? 1 : 0;
+            attrs[4] = samples;
+            pf = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs];
+            if (pf)
+            {
+                valid = true;
+                break;
+            }
+        }
+        
+        if (!valid)
+        {
+            NSLog(@"OpenGL pixel format not supported.");
+            GP_ERROR("Failed to create a valid OpenGL pixel format.");
+            return nil;
+        }
+    }
     
     
-    if((self = [super initWithFrame:frame pixelFormat:[pf autorelease]])) 
+    if ((self = [super initWithFrame:frame pixelFormat:[pf autorelease]])) 
     {
     {
         gameLock = [[NSRecursiveLock alloc] init];
         gameLock = [[NSRecursiveLock alloc] init];
-        _game = Game::getInstance();
         __timeStart = getMachTimeInMilliseconds();
         __timeStart = getMachTimeInMilliseconds();
     }
     }
+    
     return self;
     return self;
 }
 }
 
 
@@ -745,6 +782,11 @@ int Platform::enterMessagePump()
     NSRect viewBounds = NSMakeRect(0, 0, __width, __height);
     NSRect viewBounds = NSMakeRect(0, 0, __width, __height);
     
     
     __view = [[View alloc] initWithFrame:viewBounds];
     __view = [[View alloc] initWithFrame:viewBounds];
+    if (__view == NULL)
+    {
+        GP_ERROR("Failed to create view: exiting.");
+        return EXIT_FAILURE;
+    }
     
     
     NSRect centered = NSMakeRect(NSMidX(screenBounds) - NSMidX(viewBounds),
     NSRect centered = NSMakeRect(NSMidX(screenBounds) - NSMidX(viewBounds),
                                  NSMidY(screenBounds) - NSMidY(viewBounds),
                                  NSMidY(screenBounds) - NSMidY(viewBounds),

+ 3 - 2
gameplay/src/PlatformWin32.cpp

@@ -737,6 +737,9 @@ bool initializeGL(WindowCreationParams* params)
 
 
     // Choose pixel format using wglChoosePixelFormatARB, which allows us to specify
     // Choose pixel format using wglChoosePixelFormatARB, which allows us to specify
     // additional attributes such as multisampling.
     // additional attributes such as multisampling.
+    //
+    // Note: Keep multisampling attributes at the start of the attribute lists since code below
+    // assumes they are array elements 0 through 3.
     int attribList[] = {
     int attribList[] = {
         WGL_SAMPLES_ARB, params->samples,
         WGL_SAMPLES_ARB, params->samples,
         WGL_SAMPLE_BUFFERS_ARB, params->samples > 0 ? 1 : 0,
         WGL_SAMPLE_BUFFERS_ARB, params->samples > 0 ? 1 : 0,
@@ -887,8 +890,6 @@ Platform* Platform::create(Game* game, void* attachToWindow)
 
 
             // Read multisampling state.
             // Read multisampling state.
             params.samples = config->getInt("samples");
             params.samples = config->getInt("samples");
-            if (params.samples == 0)
-                params.samples = 1;
         }
         }
     }
     }
 
 

+ 162 - 52
gameplay/src/PlatformiOS.mm

@@ -53,14 +53,19 @@ int getKey(unichar keyCode);
 {
 {
     EAGLContext* context;
     EAGLContext* context;
     CADisplayLink* displayLink;
     CADisplayLink* displayLink;
+    BOOL updateFramebuffer;
     GLuint defaultFramebuffer;
     GLuint defaultFramebuffer;
     GLuint colorRenderbuffer;
     GLuint colorRenderbuffer;
     GLuint depthRenderbuffer;
     GLuint depthRenderbuffer;
     GLint framebufferWidth;
     GLint framebufferWidth;
-    GLint framebufferHeight;    
+    GLint framebufferHeight;
+    GLuint multisampleFramebuffer;
+    GLuint multisampleRenderbuffer;
+    GLuint multisampleDepthbuffer;
     NSInteger swapInterval;
     NSInteger swapInterval;
     BOOL updating;
     BOOL updating;
-    Game* _game;
+    Game* game;
+    BOOL oglDiscardSupported;
     
     
     UITapGestureRecognizer *_tapRecognizer;
     UITapGestureRecognizer *_tapRecognizer;
     UIPinchGestureRecognizer *_pinchRecognizer;
     UIPinchGestureRecognizer *_pinchRecognizer;
@@ -82,11 +87,10 @@ int getKey(unichar keyCode);
 @end
 @end
 
 
 @interface View (Private)
 @interface View (Private)
-- (void)createFramebuffer;
+- (BOOL)createFramebuffer;
 - (void)deleteFramebuffer;
 - (void)deleteFramebuffer;
 @end
 @end
 
 
-
 @implementation View
 @implementation View
 
 
 @synthesize updating;
 @synthesize updating;
@@ -111,11 +115,21 @@ int getKey(unichar keyCode);
         }
         }
         else
         else
         {
         {
-            print("Invalid OS Version: %s\n", (currSysVer == NULL?"NULL":[currSysVer cStringUsingEncoding:NSASCIIStringEncoding]));
+            GP_ERROR("Invalid OS Version: %s\n", (currSysVer == NULL?"NULL":[currSysVer cStringUsingEncoding:NSASCIIStringEncoding]));
             [self release];
             [self release];
             return nil;
             return nil;
         }
         }
         
         
+        // Check for OS 4.0+ features
+        if ([currSysVer compare:@"4.0" options:NSNumericSearch] != NSOrderedAscending)
+        {
+            oglDiscardSupported = YES;
+        }
+        else
+        {
+            oglDiscardSupported = NO;
+        }
+        
         // Configure the CAEAGLLayer and setup out the rendering context
         // Configure the CAEAGLLayer and setup out the rendering context
         CGFloat scale = [[UIScreen mainScreen] scale];
         CGFloat scale = [[UIScreen mainScreen] scale];
         CAEAGLLayer* layer = (CAEAGLLayer *)self.layer;
         CAEAGLLayer* layer = (CAEAGLLayer *)self.layer;
@@ -129,42 +143,36 @@ int getKey(unichar keyCode);
         context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
         context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
         if (!context || ![EAGLContext setCurrentContext:context])
         if (!context || ![EAGLContext setCurrentContext:context])
         {
         {
+            GP_ERROR("Failed to make context current.");
             [self release];
             [self release];
             return nil;
             return nil;
         }
         }
 
 
-        if (!defaultFramebuffer)
-        {
-            [self createFramebuffer];
-        }
-            
-        glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebuffer);
-        glViewport(0, 0, framebufferWidth, framebufferHeight);
-
         // Initialize Internal Defaults
         // Initialize Internal Defaults
         displayLink = nil;
         displayLink = nil;
+        updateFramebuffer = YES;
         defaultFramebuffer = 0;
         defaultFramebuffer = 0;
         colorRenderbuffer = 0;
         colorRenderbuffer = 0;
         depthRenderbuffer = 0;
         depthRenderbuffer = 0;
         framebufferWidth = 0;
         framebufferWidth = 0;
         framebufferHeight = 0;
         framebufferHeight = 0;
+        multisampleFramebuffer = 0;
+        multisampleRenderbuffer = 0;
+        multisampleDepthbuffer = 0;
         swapInterval = 1;        
         swapInterval = 1;        
         updating = FALSE;
         updating = FALSE;
-        
-        [self createFramebuffer];
+        game = Game::getInstance();
         
         
         // Set the resource path and initalize the game
         // Set the resource path and initalize the game
         NSString* bundlePath = [[[NSBundle mainBundle] bundlePath] stringByAppendingString:@"/"];
         NSString* bundlePath = [[[NSBundle mainBundle] bundlePath] stringByAppendingString:@"/"];
         FileSystem::setResourcePath([bundlePath fileSystemRepresentation]); 
         FileSystem::setResourcePath([bundlePath fileSystemRepresentation]); 
-        
     }
     }
     return self;
     return self;
 }
 }
 
 
-
 - (void) dealloc
 - (void) dealloc
 {
 {
-    _game->exit();
+    game->exit();
     [self deleteFramebuffer];
     [self deleteFramebuffer];
     
     
     if ([EAGLContext currentContext] == context)
     if ([EAGLContext currentContext] == context)
@@ -183,35 +191,85 @@ int getKey(unichar keyCode);
 
 
 - (void) layoutSubviews
 - (void) layoutSubviews
 {
 {
-    // Called on 'resize'
-    [self deleteFramebuffer];
+    // Called on 'resize'.
+    // Mark that framebuffer needs to be updated.
+    updateFramebuffer = YES;
 }
 }
 
 
-- (void)createFramebuffer
+- (BOOL)createFramebuffer
 {
 {
     // iOS Requires all content go to a rendering buffer then it is swapped into the windows rendering surface
     // iOS Requires all content go to a rendering buffer then it is swapped into the windows rendering surface
     assert(defaultFramebuffer == 0);
     assert(defaultFramebuffer == 0);
     
     
-    // Create the default frame buffer, and render buffer
-    glGenFramebuffers(1, &defaultFramebuffer);
-    glGenRenderbuffers(1, &colorRenderbuffer);
-    glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebuffer);    
-    glBindRenderbuffer(GL_RENDERBUFFER, colorRenderbuffer);
+    // Create the default frame buffer
+    GL_ASSERT( glGenFramebuffers(1, &defaultFramebuffer) );
+    GL_ASSERT( glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebuffer) );
+    
+    // Create a color buffer to attach to the frame buffer
+    GL_ASSERT( glGenRenderbuffers(1, &colorRenderbuffer) );
+    GL_ASSERT( glBindRenderbuffer(GL_RENDERBUFFER, colorRenderbuffer) );
     
     
-    // request storage, width, and height of the view that we will render in
+    // Associate our render buffer with CAEAGLLauyer so that the rendered content is display on our UI layer.
     [context renderbufferStorage:GL_RENDERBUFFER fromDrawable:(CAEAGLLayer *)self.layer];
     [context renderbufferStorage:GL_RENDERBUFFER fromDrawable:(CAEAGLLayer *)self.layer];
-    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorRenderbuffer);
-    glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &framebufferWidth);
-    glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &framebufferHeight);
     
     
-    glGenRenderbuffers(1, &depthRenderbuffer);
-    glBindRenderbuffer(GL_RENDERBUFFER, depthRenderbuffer);
-    glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24_OES, framebufferWidth, framebufferHeight);
-    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthRenderbuffer);
+    // Attach the color buffer to our frame buffer
+    GL_ASSERT( glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorRenderbuffer) );
+    
+    // Retrieve framebuffer size
+    GL_ASSERT( glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &framebufferWidth) );
+    GL_ASSERT( glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &framebufferHeight) );
+    
+    // If multisampling is enabled in config, create and setup a multisample buffer
+    Properties* config = game->getConfig()->getNamespace("window");
+    int samples = config ? config->getInt("samples") : 0;
+    if (samples < 0)
+        samples = 0;
+    if (samples)
+    {
+        GL_ASSERT( glGenFramebuffers(1, &multisampleFramebuffer) );
+        GL_ASSERT( glBindFramebuffer(GL_FRAMEBUFFER, multisampleFramebuffer) );
+        
+        GL_ASSERT( glGenRenderbuffers(1, &multisampleRenderbuffer) );
+        GL_ASSERT( glBindRenderbuffer(1, multisampleRenderbuffer) );
+        GL_ASSERT( glRenderbufferStorageMultisampleAPPLE(GL_RENDERBUFFER, samples, GL_RGBA8_OES, framebufferWidth, framebufferHeight) );
+        GL_ASSERT( glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, multisampleRenderbuffer) );
+        
+        GL_ASSERT( glGenRenderbuffers(1, &multisampleDepthbuffer) );
+        GL_ASSERT( glBindRenderbuffer(GL_RENDERBUFFER, multisampleDepthbuffer) );
+        GL_ASSERT( glRenderbufferStorageMultisampleAPPLE(GL_RENDERBUFFER, samples, GL_DEPTH_COMPONENT24_OES, framebufferWidth, framebufferHeight) );
+        GL_ASSERT( glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, multisampleDepthbuffer) );
+        
+        if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
+        {
+            NSLog(@"Failed to make complete multisample buffer object %x", glCheckFramebufferStatus(GL_FRAMEBUFFER));
+            [self deleteFramebuffer];
+            return NO;
+        }
+        
+        // Re-bind the default framebuffer
+        GL_ASSERT( glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebuffer) );
+    }
+    
+    // Create default depth buffer and attach to the frame buffer.
+    // Note: If we are using multisample buffers, we can skip depth buffer creation here since we only
+    // need the color buffer to resolve to.
+    if (multisampleFramebuffer == 0)
+    {
+        GL_ASSERT( glGenRenderbuffers(1, &depthRenderbuffer) );
+        GL_ASSERT( glBindRenderbuffer(GL_RENDERBUFFER, depthRenderbuffer) );
+        GL_ASSERT( glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24_OES, framebufferWidth, framebufferHeight) );
+        GL_ASSERT( glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthRenderbuffer) );
+    }
     
     
     // Sanity check, ensure that the framebuffer is valid
     // Sanity check, ensure that the framebuffer is valid
     if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
     if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
+    {
         NSLog(@"ERROR: Failed to make complete framebuffer object %x", glCheckFramebufferStatus(GL_FRAMEBUFFER));
         NSLog(@"ERROR: Failed to make complete framebuffer object %x", glCheckFramebufferStatus(GL_FRAMEBUFFER));
+        [self deleteFramebuffer];
+        return NO;
+    }
+    
+    return YES;
 }
 }
 
 
 - (void)deleteFramebuffer
 - (void)deleteFramebuffer
@@ -234,6 +292,21 @@ int getKey(unichar keyCode);
             glDeleteRenderbuffers(1, &depthRenderbuffer);
             glDeleteRenderbuffers(1, &depthRenderbuffer);
             depthRenderbuffer = 0;
             depthRenderbuffer = 0;
         }
         }
+        if (multisampleFramebuffer)
+        {
+            glDeleteFramebuffers(1, &multisampleFramebuffer);
+            multisampleFramebuffer = 0;
+        }
+        if (multisampleRenderbuffer)
+        {
+            glDeleteRenderbuffers(1, &multisampleRenderbuffer);
+            multisampleRenderbuffer = 0;
+        }
+        if (multisampleDepthbuffer)
+        {
+            glDeleteRenderbuffers(1, &multisampleDepthbuffer);
+            multisampleDepthbuffer = 0;
+        }
     }
     }
 }
 }
 
 
@@ -257,8 +330,35 @@ int getKey(unichar keyCode);
 
 
 - (void)swapBuffers
 - (void)swapBuffers
 {
 {
-    if (context != nil)
+    if (context)
     {
     {
+        if (multisampleFramebuffer)
+        {
+            // Multisampling is enabled: resolve the multisample buffer into the default framebuffer
+            glBindFramebuffer(GL_DRAW_FRAMEBUFFER_APPLE, defaultFramebuffer);
+            glBindFramebuffer(GL_READ_FRAMEBUFFER_APPLE, multisampleFramebuffer);
+            glResolveMultisampleFramebufferAPPLE();
+            
+            if (oglDiscardSupported)
+            {
+                // Performance hint that the GL driver can discard the contents of the multisample buffers
+                // since they have now been resolved into the default framebuffer
+                const GLenum discards[]  = { GL_COLOR_ATTACHMENT0, GL_DEPTH_ATTACHMENT };
+                glDiscardFramebufferEXT(GL_READ_FRAMEBUFFER_APPLE, 2, discards);
+            }
+        }
+        else
+        {
+            if (oglDiscardSupported)
+            {
+                // Performance hint to the GL driver that the depth buffer is no longer required.
+                const GLenum discards[]  = { GL_DEPTH_ATTACHMENT };
+                glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebuffer);
+                glDiscardFramebufferEXT(GL_FRAMEBUFFER, 1, discards);
+            }
+        }
+        
+        // Present the color buffer
         glBindRenderbuffer(GL_RENDERBUFFER, colorRenderbuffer);
         glBindRenderbuffer(GL_RENDERBUFFER, colorRenderbuffer);
         [context presentRenderbuffer:GL_RENDERBUFFER];
         [context presentRenderbuffer:GL_RENDERBUFFER];
     }
     }
@@ -266,9 +366,9 @@ int getKey(unichar keyCode);
 
 
 - (void)startGame 
 - (void)startGame 
 {
 {
-    _game = Game::getInstance();
+    game = Game::getInstance();
     __timeStart = getMachTimeInMilliseconds();
     __timeStart = getMachTimeInMilliseconds();
-    _game->run();  
+    game->run();
 }
 }
 
 
 - (void)startUpdating
 - (void)startUpdating
@@ -278,7 +378,7 @@ int getKey(unichar keyCode);
         displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(update:)];
         displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(update:)];
         [displayLink setFrameInterval:swapInterval];
         [displayLink setFrameInterval:swapInterval];
         [displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
         [displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
-        _game->resume();
+        game->resume();
         updating = TRUE;
         updating = TRUE;
     }
     }
 }
 }
@@ -287,7 +387,7 @@ int getKey(unichar keyCode);
 {
 {
     if (updating)
     if (updating)
     {
     {
-        _game->pause();
+        game->pause();
         [displayLink invalidate];
         [displayLink invalidate];
         displayLink = nil;
         displayLink = nil;
         updating = FALSE;
         updating = FALSE;
@@ -298,18 +398,28 @@ int getKey(unichar keyCode);
 {   
 {   
     if (context != nil)
     if (context != nil)
     {
     {
+        // Ensure our context is current
         [EAGLContext setCurrentContext:context];
         [EAGLContext setCurrentContext:context];
-        if (!defaultFramebuffer)
+        
+        // If the framebuffer needs (re)creating, do so
+        if (updateFramebuffer)
+        {
+            updateFramebuffer = NO;
+            [self deleteFramebuffer];
             [self createFramebuffer];
             [self createFramebuffer];
+        }
         
         
-        glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebuffer);
-        glViewport(0, 0, framebufferWidth, framebufferHeight);
-
-        if (_game && _game->getState() == Game::RUNNING)       
-            _game->frame();
+        // Bind our framebuffer for rendering.
+        // If multisampling is enabled, bind the multisample buffer - otherwise bind the default buffer
+        GL_ASSERT( glBindFramebuffer(GL_FRAMEBUFFER, multisampleFramebuffer ? multisampleFramebuffer : defaultFramebuffer) );
+        GL_ASSERT( glViewport(0, 0, framebufferWidth, framebufferHeight) );
         
         
-        glBindRenderbuffer(GL_RENDERBUFFER, colorRenderbuffer);
-        [context presentRenderbuffer:GL_RENDERBUFFER];
+        // Execute a single game frame
+        if (game && game->getState() == Game::RUNNING)
+            game->frame();
+        
+        // Present the contents of the color buffer
+        [self swapBuffers];
     }
     }
 }
 }
 
 
@@ -448,14 +558,14 @@ int getKey(unichar keyCode);
 - (void)handleTapGesture:(UITapGestureRecognizer *)sender
 - (void)handleTapGesture:(UITapGestureRecognizer *)sender
 {
 {
     CGPoint location = [sender locationInView:self];
     CGPoint location = [sender locationInView:self];
-    _game->gestureTapEvent(location.x, location.y);
+    game->gestureTapEvent(location.x, location.y);
 }
 }
 
 
 - (void)handlePinchGesture:(UIPinchGestureRecognizer *)sender
 - (void)handlePinchGesture:(UIPinchGestureRecognizer *)sender
 {
 {
     CGFloat factor = [sender scale];
     CGFloat factor = [sender scale];
     CGPoint location = [sender locationInView:self];
     CGPoint location = [sender locationInView:self];
-    _game->gesturePinchEvent(location.x, location.y, factor);
+    game->gesturePinchEvent(location.x, location.y, factor);
 }
 }
 
 
 - (void)handleSwipeGesture:(UISwipeGestureRecognizer *)sender
 - (void)handleSwipeGesture:(UISwipeGestureRecognizer *)sender
@@ -477,7 +587,7 @@ int getKey(unichar keyCode);
             gameplayDirection = Gesture::SWIPE_DIRECTION_DOWN;
             gameplayDirection = Gesture::SWIPE_DIRECTION_DOWN;
             break;
             break;
     }
     }
-    _game->gestureSwipeEvent(location.x, location.y, gameplayDirection);
+    game->gestureSwipeEvent(location.x, location.y, gameplayDirection);
 }
 }
 
 
 @end
 @end
@@ -940,7 +1050,7 @@ int Platform::enterMessagePump()
     [pool release];
     [pool release];
     return EXIT_SUCCESS;
     return EXIT_SUCCESS;
 }
 }
-    
+
 void Platform::signalShutdown() 
 void Platform::signalShutdown() 
 {
 {
     // Cannot 'exit' an iOS Application
     // Cannot 'exit' an iOS Application
@@ -948,7 +1058,7 @@ void Platform::signalShutdown()
     [__view stopUpdating];
     [__view stopUpdating];
     exit(0);
     exit(0);
 }
 }
-    
+
 unsigned int Platform::getDisplayWidth()
 unsigned int Platform::getDisplayWidth()
 {
 {
     CGSize size = DeviceOrientedSize([__appDelegate.viewController interfaceOrientation]);
     CGSize size = DeviceOrientedSize([__appDelegate.viewController interfaceOrientation]);