PDA

View Full Version : Strange problem with views, subviews, and orientation




MaxFreud
Jul 23, 2011, 09:01 PM
Hi all,

I am making a simple iPad game that includes some OpenGL code; I am using Jeff Lamarche's Xcode template (http://iphonedevelopment.blogspot.com/2011/05/empty-opengl-es-application-xcode-4.html), which includes a UIView subclass for the OpenGL stuff. It is called GLView, and it comes with its view controller, called GLViewController. Here are those classes for reference:

#import <QuartzCore/QuartzCore.h>
#import <OpenGLES/EAGLDrawable.h>

#import "GLView.h"
#import "ConstantsAndMacros.h"


@interface GLView ()
@property (nonatomic, retain) EAGLContext *context;
@property (nonatomic, assign) NSTimer *animationTimer;
- (BOOL) createFramebuffer;
- (void) destroyFramebuffer;
@end

#pragma mark -

@implementation GLView

@synthesize context;
@synthesize animationTimer;
@synthesize animationInterval;
@synthesize delegate;

+ (Class)layerClass
{
return [CAEAGLLayer class];
}
- (id)initWithCoder:(NSCoder*)coder {

if ((self = [super initWithCoder:coder])) {
// Get the layer
CAEAGLLayer *eaglLayer = (CAEAGLLayer *)self.layer;

eaglLayer.opaque = YES;
eaglLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:NO], kEAGLDrawablePropertyRetainedBacking, kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat, nil];

#if kAttemptToUseOpenGLES2
context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
if (context == NULL)
{
#endif
context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1];

if (!context || ![EAGLContext setCurrentContext:context]) {
[self release];
return nil;
}
#if kAttemptToUseOpenGLES2
}
#endif

animationInterval = 1.0 / kRenderingFrequency;
}
return self;
}
- (void)drawView
{
glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer);
[delegate drawView:self];
glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
[context presentRenderbuffer:GL_RENDERBUFFER_OES];
}
- (void)layoutSubviews
{
[EAGLContext setCurrentContext:context];
[self destroyFramebuffer];
[self createFramebuffer];
[self drawView];
}
- (BOOL)createFramebuffer
{
glGenFramebuffersOES(1, &viewFramebuffer);
glGenRenderbuffersOES(1, &viewRenderbuffer);

glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer);
glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
[context renderbufferStorage:GL_RENDERBUFFER_OES fromDrawable:(CAEAGLLayer*)self.layer];
glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, viewRenderbuffer);

glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_WIDTH_OES, &backingWidth);
glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_HEIGHT_OES, &backingHeight);

if (USE_DEPTH_BUFFER)
{
glGenRenderbuffersOES(1, &depthRenderbuffer);
glBindRenderbufferOES(GL_RENDERBUFFER_OES, depthRenderbuffer);
glRenderbufferStorageOES(GL_RENDERBUFFER_OES, GL_DEPTH_COMPONENT16_OES, backingWidth, backingHeight);
glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_DEPTH_ATTACHMENT_OES, GL_RENDERBUFFER_OES, depthRenderbuffer);
}

if(glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES) != GL_FRAMEBUFFER_COMPLETE_OES)
{
NSLog(@"failed to make complete framebuffer object %4x", glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES));
return NO;
}

[delegate setupView:self];
return YES;
}
- (void)destroyFramebuffer
{
glDeleteFramebuffersOES(1, &viewFramebuffer);
viewFramebuffer = 0;
glDeleteRenderbuffersOES(1, &viewRenderbuffer);
viewRenderbuffer = 0;

if(depthRenderbuffer)
{
glDeleteRenderbuffersOES(1, &depthRenderbuffer);
depthRenderbuffer = 0;
}
}
- (void)startAnimation
{
self.animationTimer = [NSTimer scheduledTimerWithTimeInterval:animationInterval target:self selector:@selector(drawView) userInfo:nil repeats:YES];
}
- (void)stopAnimation
{
self.animationTimer = nil;
}
- (void)setAnimationTimer:(NSTimer *)newTimer
{
[animationTimer invalidate];
animationTimer = newTimer;
}

- (void)setAnimationInterval:(NSTimeInterval)interval
{
animationInterval = interval;
if (animationTimer)
{
[self stopAnimation];
[self startAnimation];
}
}
- (void)dealloc
{
[self stopAnimation];

if ([EAGLContext currentContext] == context)
[EAGLContext setCurrentContext:nil];

[context release];
[super dealloc];
}

@end


#import "GLViewController.h"
#import "ConstantsAndMacros.h"
#import "OpenGLCommon.h"
#import "OpenGLTexture3D.h"
#import "gazeShip.h"

@implementation GLViewController

@synthesize texture;

- (void)drawView:(UIView *)theView
{
static GLfloat rot = 0.0;

glColor4f(0.0, 0.0, 0.0, 0.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

// Drawing code here
glEnableClientState(GL_VERTEX_ARRAY);
// glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glEnable(GL_TEXTURE_2D);
glEnable(GL_BLEND);

[texture bind];
glColor4f(1.0, 0.0, 0.0, 0.0);
glLoadIdentity();
glTranslatef(0.0, 0.2, -1.7);
glRotatef(90.0, 0.0, 0.0, 1.0);
glRotatef(170.0, 0.0, 1.0, 0.0);
glRotatef(rot, 0.0, 0.0, 1.0);
glVertexPointer(3, GL_FLOAT, 0, shipVerts);
glTexCoordPointer(2, GL_FLOAT, 0, shipTexCoords);
glDrawArrays(GL_TRIANGLES, 0, shipNumVerts);

glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_NORMAL_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisable(GL_TEXTURE_2D);
glDisable(GL_BLEND);

static NSTimeInterval lastDrawTime;
if (lastDrawTime)
{
NSTimeInterval timeSinceLastDraw = [NSDate timeIntervalSinceReferenceDate] - lastDrawTime;
rot+=35 * timeSinceLastDraw;
}
lastDrawTime = [NSDate timeIntervalSinceReferenceDate];
}

-(void)setupView:(GLView*)view
{
const GLfloat zNear = 0.01, zFar = 1000.0, fieldOfView = 45.0;
GLfloat size;

((GLView*)(self.view)).delegate = self;

// Enabling GL_DEPTH_TEST is used for depth buffering
// this allows openGL to properly obscure one object
// when another one is in front of it.
glEnable(GL_DEPTH_TEST);
glMatrixMode(GL_PROJECTION);
size = zNear * tanf(DEGREES_TO_RADIANS(fieldOfView) / 2.0);
CGRect rect = view.bounds;
glFrustumf(-size, size, -size / (rect.size.width / rect.size.height), size /
(rect.size.width / rect.size.height), zNear, zFar);
glViewport(0, 0, rect.size.width, rect.size.height);
glMatrixMode(GL_MODELVIEW);


// Enable lighting
glEnable(GL_LIGHTING);

// Turn the first light on
glEnable(GL_LIGHT0);

// Define the ambient component of the first light
static const Color3D light0Ambient[] = {{0.4, 0.4, 0.4, 1.0}};
glLightfv(GL_LIGHT0, GL_AMBIENT, (const GLfloat *)light0Ambient);

// Define the diffuse component of the first light
static const Color3D light0Diffuse[] = {{0.7, 0.7, 0.7, 1.0}};
glLightfv(GL_LIGHT0, GL_DIFFUSE, (const GLfloat *)light0Diffuse);

// Define the specular component and shininess of the first light
static const Color3D light0Specular[] = {{0.7, 0.7, 0.7, 1.0}};
glLightfv(GL_LIGHT0, GL_SPECULAR, (const GLfloat *)light0Specular);

// Define the position of the first light
// const GLfloat light0Position[] = {10.0, 10.0, 10.0};
static const Vertex3D light0Position[] = {{5.0, 5.0, 5.0}};
glLightfv(GL_LIGHT0, GL_POSITION, (const GLfloat *)light0Position);

// Calculate light vector so it points at the object
static const Vertex3D objectPoint[] = {{0.0, 0.0, -5.0}};
const Vertex3D lightVector = Vector3DMakeWithStartAndEndPoints(light0Position[0], objectPoint[0]);
glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, (GLfloat *)&lightVector);

NSString *path = [[NSBundle mainBundle] pathForResource:@"BrushedAluminum" ofType:@"jpg"];
OpenGLTexture3D *newTexture = [[OpenGLTexture3D alloc] initWithFilename:path width:512 height:512];
self.texture = newTexture;
[newTexture release];

glLoadIdentity();
}
- (void)dealloc
{
[super dealloc];
}

-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
{
return (toInterfaceOrientation == UIInterfaceOrientationLandscapeLeft
|| toInterfaceOrientation == UIInterfaceOrientationLandscapeRight);
}

-(void) viewWillAppear:(BOOL)animated
{
NSLog(@"View will appear");

[((GLView*)(self.view)) startAnimation];
}

- (void) viewWillDisappear:(BOOL)animated
{
NSLog(@"View will disappear");

[((GLView*)(self.view)) stopAnimation];
}
@end


I have created another view with its view controller:


#import "GazeDemoAppDelegate.h"
#import "GazeIntroViewController.h"
#import "SettingsPopover.h"

@implementation GazeIntroViewController

@synthesize screenWidth, screenHeight;
@synthesize calibrationMode;
@synthesize testMode;
@synthesize gazeTitle, instructions, logo, calibrate, settings, squareView;
@synthesize start;
@synthesize glView;


#pragma Init and Memory

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
connected = NO;
[self setupViewWithCalibrationState:NO];
}
return self;
}

- (void) setupViewWithCalibrationState:(BOOL)done
{
calibrationDone = done;

gazeTitle.hidden = NO;
instructions.hidden = NO;
logo.hidden = NO;
calibrate.hidden = NO;
settings.hidden = NO;
squareView.hidden = YES;
//TODO: Replace YES with !done
start.hidden = YES;
}

- (void)didReceiveMemoryWarning
{
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];

// Release any cached data, images, etc that aren't in use.
}

#pragma mark - View lifecycle

- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.

NSLog(@"Am I first responder? %@", [self.view isFirstResponder]?@"Yes":@"No");

CGRect screenRect = [[UIScreen mainScreen] bounds];

self.screenWidth = screenRect.size.width;
self.screenHeight = screenRect.size.height;

if (!connected) {
calibrate.enabled = NO;
}

if([self.view becomeFirstResponder])
NSLog(@"Assumed 1st responder");
}

- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationLandscapeLeft
|| interfaceOrientation == UIInterfaceOrientationLandscapeRight);
}

#pragma mark UI Action Methods

- (IBAction)calibratePressed:(id)sender
{
//gazeTitle, instructions, logo, calibrate, settings, square
gazeTitle.hidden = YES;
instructions.hidden = YES;
logo.hidden = YES;
calibrate.hidden = YES;
settings.hidden = YES;
squareView.hidden = NO;
start.hidden = YES;

[GazeDemoAppDelegate singleton].calibrationMode = YES;
[[NetworkManager singleton] beginCalibration];
}

- (IBAction)settingsPressed:(id)sender
{
if (settingsPopover.isPopoverVisible) {
[settingsPopover dismissPopoverAnimated:YES];
}
else
{
SettingsPopover* content = [[SettingsPopover alloc] init];

// setup the popover for use
settingsPopover = [[UIPopoverController alloc] initWithContentViewController:content];
settingsPopover.popoverContentSize = CGSizeMake(360., 200.);
settingsPopover.delegate = self;

[settingsPopover presentPopoverFromRect:((UIButton*)sender).frame inView:self.view permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];

[content release];
}
}

- (IBAction)startPressed:(id)sender
{
CGRect frame = [self.view frame];
CGRect newFrame = CGRectMake(frame.origin.x, frame.origin.y + 1024, frame.size.width, frame.size.height);;
[UIView animateWithDuration:1
animations:^{
self.view.frame = newFrame;
}
completion:^(BOOL finished){
NSLog(@"Finished Animations: %@", finished?@"YES":@"NO");
[[GazeDemoAppDelegate singleton] displayGameView];
}];
}

@end


GazeIntroViewController has its own xib file. As for the instance of GLView, inside of MainWindow.xib, I have this:
- Window
- App Delegate
- GLViewController
- View (set up as a GLView instance)

Inside of the xib files (both MainWindow and GazeIntroViewController) I have set up the views to have a width of 1024 and height of 768 and the orientation to landscape. (This app will ONLY run in landscape)

My applicationDidFinishLaunching:
- (void)applicationDidFinishLaunching:(UIApplication *)application {
viewController = [[GazeIntroViewController alloc] initWithNibName:@"GazeIntroViewController" bundle:[NSBundle mainBundle]];

[window addSubview:glView];
[glView setFrame:[glView.superview frame]];
// [window addSubview:viewController.view];
// [window bringSubviewToFront:viewController.view];

g_AppDelegate = self;
calibrationMode = NO;

CGRect screenRect = [[UIScreen mainScreen] bounds];
self.screenWidth = screenRect.size.height;
self.screenHeight = screenRect.size.width;

[window setFrame:CGRectMake(0, 0, 768, 1024)];
[viewController.view setFrame:[viewController.view.superview frame]];

[window makeKeyAndVisible];

NSLog(@"Window subviews are: %@", [[window subviews] description]);
NSLog(@"Window status is: %@", [window description]);
}


These are my two problems (and the reason for all those setFrame calls):

If I uncomment those two lines in applicationDidFinishLaunching, my GazeIntroViewController view will show up rotated 90 deg. As in: the device is in landscape, but the view appears fixed to the 'portrait' position and will not move. The only way I can get that view to display correctly is if, inside MainWindow.xib, I set the instance of GLView to be inside of the window. Why does it work this way? I really want to understand this behaviour because it limits what I can do with views in code.
Regardless of the orientation, the GLView will display nothing but a purple screen. There are no errors; I don't know how to proceed for debugging. Note that this project started from the OpenGL template linked to above. Before I started adding new views to it, it was perfectly OK rendering my models.


Any help?