|
|
@@ -53,14 +53,19 @@ int getKey(unichar keyCode);
|
|
|
{
|
|
|
EAGLContext* context;
|
|
|
CADisplayLink* displayLink;
|
|
|
+ BOOL updateFramebuffer;
|
|
|
GLuint defaultFramebuffer;
|
|
|
GLuint colorRenderbuffer;
|
|
|
GLuint depthRenderbuffer;
|
|
|
GLint framebufferWidth;
|
|
|
- GLint framebufferHeight;
|
|
|
+ GLint framebufferHeight;
|
|
|
+ GLuint multisampleFramebuffer;
|
|
|
+ GLuint multisampleRenderbuffer;
|
|
|
+ GLuint multisampleDepthbuffer;
|
|
|
NSInteger swapInterval;
|
|
|
BOOL updating;
|
|
|
- Game* _game;
|
|
|
+ Game* game;
|
|
|
+ BOOL oglDiscardSupported;
|
|
|
|
|
|
UITapGestureRecognizer *_tapRecognizer;
|
|
|
UIPinchGestureRecognizer *_pinchRecognizer;
|
|
|
@@ -82,11 +87,10 @@ int getKey(unichar keyCode);
|
|
|
@end
|
|
|
|
|
|
@interface View (Private)
|
|
|
-- (void)createFramebuffer;
|
|
|
+- (BOOL)createFramebuffer;
|
|
|
- (void)deleteFramebuffer;
|
|
|
@end
|
|
|
|
|
|
-
|
|
|
@implementation View
|
|
|
|
|
|
@synthesize updating;
|
|
|
@@ -111,11 +115,21 @@ int getKey(unichar keyCode);
|
|
|
}
|
|
|
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];
|
|
|
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
|
|
|
CGFloat scale = [[UIScreen mainScreen] scale];
|
|
|
CAEAGLLayer* layer = (CAEAGLLayer *)self.layer;
|
|
|
@@ -129,42 +143,36 @@ int getKey(unichar keyCode);
|
|
|
context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
|
|
|
if (!context || ![EAGLContext setCurrentContext:context])
|
|
|
{
|
|
|
+ GP_ERROR("Failed to make context current.");
|
|
|
[self release];
|
|
|
return nil;
|
|
|
}
|
|
|
|
|
|
- if (!defaultFramebuffer)
|
|
|
- {
|
|
|
- [self createFramebuffer];
|
|
|
- }
|
|
|
-
|
|
|
- glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebuffer);
|
|
|
- glViewport(0, 0, framebufferWidth, framebufferHeight);
|
|
|
-
|
|
|
// Initialize Internal Defaults
|
|
|
displayLink = nil;
|
|
|
+ updateFramebuffer = YES;
|
|
|
defaultFramebuffer = 0;
|
|
|
colorRenderbuffer = 0;
|
|
|
depthRenderbuffer = 0;
|
|
|
framebufferWidth = 0;
|
|
|
framebufferHeight = 0;
|
|
|
+ multisampleFramebuffer = 0;
|
|
|
+ multisampleRenderbuffer = 0;
|
|
|
+ multisampleDepthbuffer = 0;
|
|
|
swapInterval = 1;
|
|
|
updating = FALSE;
|
|
|
-
|
|
|
- [self createFramebuffer];
|
|
|
+ game = Game::getInstance();
|
|
|
|
|
|
// Set the resource path and initalize the game
|
|
|
NSString* bundlePath = [[[NSBundle mainBundle] bundlePath] stringByAppendingString:@"/"];
|
|
|
FileSystem::setResourcePath([bundlePath fileSystemRepresentation]);
|
|
|
-
|
|
|
}
|
|
|
return self;
|
|
|
}
|
|
|
|
|
|
-
|
|
|
- (void) dealloc
|
|
|
{
|
|
|
- _game->exit();
|
|
|
+ game->exit();
|
|
|
[self deleteFramebuffer];
|
|
|
|
|
|
if ([EAGLContext currentContext] == context)
|
|
|
@@ -183,35 +191,85 @@ int getKey(unichar keyCode);
|
|
|
|
|
|
- (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
|
|
|
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];
|
|
|
- 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
|
|
|
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
|
|
|
+ {
|
|
|
NSLog(@"ERROR: Failed to make complete framebuffer object %x", glCheckFramebufferStatus(GL_FRAMEBUFFER));
|
|
|
+ [self deleteFramebuffer];
|
|
|
+ return NO;
|
|
|
+ }
|
|
|
+
|
|
|
+ return YES;
|
|
|
}
|
|
|
|
|
|
- (void)deleteFramebuffer
|
|
|
@@ -234,6 +292,21 @@ int getKey(unichar keyCode);
|
|
|
glDeleteRenderbuffers(1, &depthRenderbuffer);
|
|
|
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
|
|
|
{
|
|
|
- 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);
|
|
|
[context presentRenderbuffer:GL_RENDERBUFFER];
|
|
|
}
|
|
|
@@ -266,9 +366,9 @@ int getKey(unichar keyCode);
|
|
|
|
|
|
- (void)startGame
|
|
|
{
|
|
|
- _game = Game::getInstance();
|
|
|
+ game = Game::getInstance();
|
|
|
__timeStart = getMachTimeInMilliseconds();
|
|
|
- _game->run();
|
|
|
+ game->run();
|
|
|
}
|
|
|
|
|
|
- (void)startUpdating
|
|
|
@@ -278,7 +378,7 @@ int getKey(unichar keyCode);
|
|
|
displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(update:)];
|
|
|
[displayLink setFrameInterval:swapInterval];
|
|
|
[displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
|
|
|
- _game->resume();
|
|
|
+ game->resume();
|
|
|
updating = TRUE;
|
|
|
}
|
|
|
}
|
|
|
@@ -287,7 +387,7 @@ int getKey(unichar keyCode);
|
|
|
{
|
|
|
if (updating)
|
|
|
{
|
|
|
- _game->pause();
|
|
|
+ game->pause();
|
|
|
[displayLink invalidate];
|
|
|
displayLink = nil;
|
|
|
updating = FALSE;
|
|
|
@@ -298,18 +398,28 @@ int getKey(unichar keyCode);
|
|
|
{
|
|
|
if (context != nil)
|
|
|
{
|
|
|
+ // Ensure our context is current
|
|
|
[EAGLContext setCurrentContext:context];
|
|
|
- if (!defaultFramebuffer)
|
|
|
+
|
|
|
+ // If the framebuffer needs (re)creating, do so
|
|
|
+ if (updateFramebuffer)
|
|
|
+ {
|
|
|
+ updateFramebuffer = NO;
|
|
|
+ [self deleteFramebuffer];
|
|
|
[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
|
|
|
{
|
|
|
CGPoint location = [sender locationInView:self];
|
|
|
- _game->gestureTapEvent(location.x, location.y);
|
|
|
+ game->gestureTapEvent(location.x, location.y);
|
|
|
}
|
|
|
|
|
|
- (void)handlePinchGesture:(UIPinchGestureRecognizer *)sender
|
|
|
{
|
|
|
CGFloat factor = [sender scale];
|
|
|
CGPoint location = [sender locationInView:self];
|
|
|
- _game->gesturePinchEvent(location.x, location.y, factor);
|
|
|
+ game->gesturePinchEvent(location.x, location.y, factor);
|
|
|
}
|
|
|
|
|
|
- (void)handleSwipeGesture:(UISwipeGestureRecognizer *)sender
|
|
|
@@ -477,7 +587,7 @@ int getKey(unichar keyCode);
|
|
|
gameplayDirection = Gesture::SWIPE_DIRECTION_DOWN;
|
|
|
break;
|
|
|
}
|
|
|
- _game->gestureSwipeEvent(location.x, location.y, gameplayDirection);
|
|
|
+ game->gestureSwipeEvent(location.x, location.y, gameplayDirection);
|
|
|
}
|
|
|
|
|
|
@end
|
|
|
@@ -940,7 +1050,7 @@ int Platform::enterMessagePump()
|
|
|
[pool release];
|
|
|
return EXIT_SUCCESS;
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
void Platform::signalShutdown()
|
|
|
{
|
|
|
// Cannot 'exit' an iOS Application
|
|
|
@@ -948,7 +1058,7 @@ void Platform::signalShutdown()
|
|
|
[__view stopUpdating];
|
|
|
exit(0);
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
unsigned int Platform::getDisplayWidth()
|
|
|
{
|
|
|
CGSize size = DeviceOrientedSize([__appDelegate.viewController interfaceOrientation]);
|