doom-ios/code/iphone/EAGLView.m
2012-01-31 16:40:40 -06:00

378 lines
11 KiB
Objective-C

/*
Copyright (C) 2009-2011 id Software LLC, a ZeniMax Media company.
Copyright (C) 2009 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#import <QuartzCore/QuartzCore.h>
#import <OpenGLES/EAGLDrawable.h>
#import "EAGLView.h"
#import "doomAppDelegate.h"
#include "doomiphone.h"
#include <pthread.h>
EAGLView *eaglview;
EAGLContext *context;
@implementation EAGLView
// You must implement this method
+ (Class)layerClass {
return [CAEAGLLayer class];
}
float screenResolutionScale = 1.0f;
CAEAGLLayer *eaglLayer;
//The GL view is stored in the nib file. When it's unarchived it's sent -initWithCoder:
- (id)initWithCoder:(NSCoder*)coder {
self = [super initWithCoder:coder];
eaglview = self;
// allow multiple touch events
self.multipleTouchEnabled = true;
// Double the resolution on iPhone 4.
if ( [[UIScreen mainScreen] respondsToSelector:@selector(scale)] &&
[self respondsToSelector:@selector(setContentScaleFactor:)] ) {
screenResolutionScale = [UIScreen mainScreen].scale;
// set scaling factor
[self setContentScaleFactor:[UIScreen mainScreen].scale];
}
// Get the layer
eaglLayer = (CAEAGLLayer *)self.layer;
// set opaque so UIKit doesn't try to blend it over other layers
eaglLayer.opaque = YES;
// set it to no-backing-retained so it can do ast pageflips instead
// of update copies, and go to 565 bit depth for higher performance.
eaglLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:NO],
kEAGLDrawablePropertyRetainedBacking,
kEAGLColorFormatRGB565,
/* kEAGLColorFormatRGBA8, */
kEAGLDrawablePropertyColorFormat,
nil];
context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1];
assert( context );
if ( ![EAGLContext setCurrentContext:context]) {
[self release];
return nil;
}
glGenFramebuffersOES(1, &mViewFramebuffer);
glGenRenderbuffersOES(1, &mViewRenderbuffer);
glBindFramebufferOES(GL_FRAMEBUFFER_OES, mViewFramebuffer);
glBindRenderbufferOES(GL_RENDERBUFFER_OES, mViewRenderbuffer);
[context renderbufferStorage:GL_RENDERBUFFER_OES fromDrawable:(CAEAGLLayer*)self.layer];
glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, mViewRenderbuffer);
// the backing sizes should be the same as the screen
glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_WIDTH_OES, &backingWidth);
glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_HEIGHT_OES, &backingHeight);
displaywidth = backingHeight;
displayheight = backingWidth;
glGenRenderbuffersOES(1, &mDepthRenderbuffer);
glBindRenderbufferOES(GL_RENDERBUFFER_OES, mDepthRenderbuffer);
glRenderbufferStorageOES(GL_RENDERBUFFER_OES, GL_DEPTH_COMPONENT16_OES, backingWidth, backingHeight);
glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_DEPTH_ATTACHMENT_OES, GL_RENDERBUFFER_OES, mDepthRenderbuffer);
// the framebuffer will stay constant
glBindRenderbufferOES(GL_RENDERBUFFER_OES, mViewRenderbuffer);
glBindFramebufferOES(GL_FRAMEBUFFER_OES, mViewFramebuffer);
if ( glCheckFramebufferStatusOES( GL_FRAMEBUFFER_OES ) != GL_FRAMEBUFFER_COMPLETE_OES ) {
printf( "Failed to make complete framebuffer object %x", glCheckFramebufferStatusOES( GL_FRAMEBUFFER_OES ) );
assert( 0 );
}
return self;
}
- (void) handleTouches:(UIEvent*)event {
int touchCount = 0;
static int previousTouchCount;
static int touchRover;
int touchThisSequence[MAX_TOUCHES];
memset( touchThisSequence, 0, sizeof( touchThisSequence ) );
NSSet *touches = [event allTouches];
// printf( "count: %i\n", [touches count] );
// lock the game out temporarily
pthread_mutex_lock( &eventMutex );
for (UITouch *myTouch in touches)
{
CGPoint touchLocation = [myTouch locationInView:nil];
// Scale Touches with the screen resolution.
touchLocation.x *= screenResolutionScale;
touchLocation.y *= screenResolutionScale;
// handle landscape mode and flipping
int x, y;
if ( revLand->value ) {
x = touchLocation.y;
y = ( displayheight - 1 ) - touchLocation.x;
} else {
x = ( displaywidth - 1) - touchLocation.y;
y = touchLocation.x;
}
// printf( "%i, %i\n", x, y );
touchCount++;
touch_t *t2;
// find which one it is closest to
int minDist = 64 * 64; // allow up to 64 unit moves to be drags
int minIndex = -1;
for ( int i = 0 ; i < MAX_TOUCHES ; i++ ) {
t2 = &sysTouches[i];
if ( !t2->down ) {
continue;
}
int dist = ( t2->x - x ) * ( t2->x - x ) + ( t2->y - y ) * ( t2->y - y );
if ( dist < minDist ) {
minDist = dist;
minIndex = i;
}
}
if ( minIndex != -1 ) {
// reuse a touch
sysTouches[minIndex].x = x;
sysTouches[minIndex].y = y;
if (myTouch.phase == UITouchPhaseEnded) {
// if this was released before the game got to see it,
// make it a special case
if ( sysTouches[minIndex].stateCount == 1 ) {
// leave it in the down state with a special count
sysTouches[minIndex].stateCount = -1;
// printf( "Tap release touch on a reuse\n" );
} else {
sysTouches[minIndex].down = false;
sysTouches[minIndex].stateCount = 1;
// printf( "Release touch on a reuse\n" );
}
} else {
if (myTouch.phase == UITouchPhaseBegan) {
sysTouches[minIndex].stateCount = 1;
sysTouches[minIndex].controlOwner = NULL;
// printf( "Begin touch on a reuse\n" );
} else {
// printf( "Drag touch on a reuse\n" );
}
sysTouches[minIndex].down = true;
}
touchThisSequence[minIndex] = true;
} else {
if ( myTouch.phase != UITouchPhaseBegan ) {
printf( "Non-local touch wasn't a begin\n" );
} else {
// allocate a new one
// grab the next rover spot
// don't just use first-not-down, because that might
// cause the release to be missed by the game code.
int i, j;
for ( j = 0 ; j < MAX_TOUCHES ; j++ ) {
i = touchRover;
t2 = &sysTouches[i];
touchRover = ( touchRover + 1 ) % MAX_TOUCHES;
if ( !t2->down ) {
break;
}
}
if ( j == MAX_TOUCHES ) {
printf( "MAX_TOUCHES, clearing everything!\n" );
memset( sysTouches, 0, sizeof( sysTouches ) );
continue;
}
// printf( "new touch down\n" );
t2->x = x;
t2->y = y;
t2->down = true;
t2->controlOwner = NULL;
t2->stateCount = 1;
touchThisSequence[i] = true;
}
}
}
// Change any active touches to released if they weren't
// in the touch set. This will happen if we forced a break because
// a "moved" event was so large that it was very likely a release and
// press of a different finger that happened to be in the same frame.
for ( int i = 0 ; i < MAX_TOUCHES ; i++ ) {
if ( sysTouches[i].down && !touchThisSequence[i] ) {
printf( "clearing touch %i\n", i );
sysTouches[i].down = false;
sysTouches[i].stateCount = 0;
touchCount--;
}
}
// toggle the console with four touches
if ( touchCount == 4 && previousTouchCount != 4 ) {
touchCount = 0; // won't get the releases, because the text field will eat them
if ( textField == nil ) {
// do this before starting the textField, which
// takes a long time
// iphoneActivateConsole();
textField = [UITextField alloc];
[textField initWithFrame:CGRectMake( 0, 0, 20, 20 ) ];
[self addSubview:textField];
[textField release];
textField.hidden = true;
textField.delegate = self;
textField.autocapitalizationType = UITextAutocapitalizationTypeNone;
textField.autocorrectionType = UITextAutocorrectionTypeNo;
[textField becomeFirstResponder];
} else {
}
}
// the game is free to copy the touches now
pthread_mutex_unlock( &eventMutex );
previousTouchCount = touchCount;
}
- (void) touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event {
// printf( "touchesBegan\n" );
[self handleTouches:event];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
// printf( "touchesMoved\n" );
[self handleTouches:event];
}
- (void) touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event {
// printf( "touchesEnded\n" );
[self handleTouches:event];
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
// printf( "touchesCancelled\n" );
[self handleTouches:event];
}
@end
@implementation EAGLView (UITextFieldDelegate)
char consoleCommand[1024];
- (BOOL)textFieldShouldReturn:(UITextField *)_textField
{
if ( eaglview->textField == nil ) {
return YES;
}
// we can't just execute this, because we are running in another
// thread, so fetch the line and the game will catch it next time
// around
// lock the game out temporarily
pthread_mutex_lock( &eventMutex );
const char *line = [ eaglview->textField.text UTF8String ];
strncpy( consoleCommand, line, sizeof(consoleCommand)-1 );
eaglview->textField.text = [ NSString stringWithUTF8String: "" ];
// put it away
[textField resignFirstResponder];
[textField removeFromSuperview];
textField = nil;
// lock the game out temporarily
pthread_mutex_unlock( &eventMutex );
return YES;
}
@end
const char * SysIPhoneGetConsoleTextField() {
if ( eaglview->textField == nil ) {
return "";
}
return [ eaglview->textField.text UTF8String ];
}
void SysIPhoneSetConsoleTextField( const char * str) {
assert( eaglview->textField != nil );
eaglview->textField.text = [ NSString stringWithUTF8String: str ];
}
void SysIPhoneSwapBuffers() {
glBindRenderbufferOES(GL_RENDERBUFFER_OES, eaglview->mViewRenderbuffer);
// present the renderbuffer for display
[context presentRenderbuffer:GL_RENDERBUFFER_OES];
}
void SysIPhoneOpenURL( const char *url ) {
Com_Printf( "OpenURL char *: %s\n", url );
NSString *nss = [NSString stringWithCString: url encoding: NSASCIIStringEncoding];
[[UIApplication sharedApplication] openURL:[NSURL URLWithString: nss]];
}
void SysIPhoneSetUIKitOrientation( int isLandscapeRight ) {
if ( isLandscapeRight ) {
[UIApplication sharedApplication].statusBarOrientation = UIInterfaceOrientationLandscapeRight;
} else {
[UIApplication sharedApplication].statusBarOrientation = UIInterfaceOrientationLandscapeLeft;
}
}