//
// GraphicsView.mm
// iphone-gl-app
//
// Created by John Ryland on 7/06/09.
// Copyright InvertedLogic 2009. All rights reserved.
//
#import <QuartzCore/QuartzCore.h>
#import <OpenGLES/EAGLDrawable.h>
#import "GraphicsView.h"
#import "Context.h"
#include "Debug.h"
#define kAccelerometerFrequency 50 //Hz
//#define EGL_SUPPORT
#define USE_DEPTH_BUFFER 0
#define COLOR_DEPTH 32
@implementation GraphicsView
- (id) initWithFrame:(CGRect)frame
{
if ((self = [super initWithFrame:frame])) {
// Get the layer
CAEAGLLayer *eaglLayer = (CAEAGLLayer *)self.layer;
eaglLayer.opaque = YES;
eaglLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:NO], kEAGLDrawablePropertyRetainedBacking, kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat, nil];
#ifdef EGL_SUPPORT
eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
if (eglDisplay == EGL_NO_DISPLAY)
printf("Error getting OpenGL ES display\n");
if (!eglInitialize(eglDisplay, 0, 0))
printf("Error initializing OpenGL ES\n");
EGLint numConfigs;
EGLint attribList[] = // attribute list
{
EGL_BUFFER_SIZE, COLOR_DEPTH, // color depth
EGL_DEPTH_SIZE, 15, // z-buffer
EGL_NONE
};
if (!eglChooseConfig(eglDisplay, attribList, &eglConfig, 1, &numConfigs))
printf("Error choosing OpenGL ES config\n");
EGLint win[2] = { w, h };
eglSurface = eglCreateWindowSurface(eglDisplay, eglConfig, &win, 0);
if (eglSurface == EGL_NO_SURFACE)
printf("Error creating OpenGL ES surface\n");
eglContext = eglCreateContext(eglDisplay, eglConfig, EGL_NO_CONTEXT, 0);
if (eglContext == 0)
printf("Error creating OpenGL ES context\n");
eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext);
#else
context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1];
if (!context || ![EAGLContext setCurrentContext:context] || ![self createFramebuffer]) {
[self release];
return nil;
}
#endif
//Configure and start accelerometer
[[UIAccelerometer sharedAccelerometer] setUpdateInterval:(1.0 / kAccelerometerFrequency)];
[[UIAccelerometer sharedAccelerometer] setDelegate:self];
cpp_context = new Context();
animationInterval = 1.0 / 30.0;
[self setupView];
[self drawView];
[self startAnimation];
}
return self;
}
- (void) dealloc
{
[self stopAnimation];
[self destroyFramebuffer];
delete cpp_context;
// SoundEngine_Teardown();
#ifdef EGL_SUPPORT
eglMakeCurrent(eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
eglDestroySurface(eglDisplay, eglSurface);
eglDestroyContext(eglDisplay, eglContext);
eglTerminate(eglDisplay);
#else
if ([EAGLContext currentContext] == context) {
[EAGLContext setCurrentContext:nil];
}
[context release];
context = nil;
#endif
[super dealloc];
}
- (void) setupView
{
glViewport(0, 0, backingWidth, backingHeight);
/*
// Note that each of the Sound Engine functions defined in SoundEngine.h return an OSStatus value.
// Although the code in this application does not check for errors, you'll want to add error checking code
// in your own application, particularly during development.
//Setup sound engine. Run it at 44Khz to match the sound files
SoundEngine_Initialize(44100);
// Assume the listener is in the center at the start. The sound will pan as the position of the rocket changes.
SoundEngine_SetListenerPosition(0.0, 0.0, kListenerDistance);
// Load each of the four sounds used in the game.
SoundEngine_LoadEffect([[bundle pathForResource:@"Start" ofType:@"caf"] UTF8String], &_sounds[kSound_Start]);
SoundEngine_LoadEffect([[bundle pathForResource:@"Success" ofType:@"caf"] UTF8String], &_sounds[kSound_Success]);
SoundEngine_LoadEffect([[bundle pathForResource:@"Failure" ofType:@"caf"] UTF8String], &_sounds[kSound_Failure]);
SoundEngine_LoadLoopingEffect([[bundle pathForResource:@"Thrust" ofType:@"caf"] UTF8String], NULL, NULL, &_sounds[kSound_Thrust]);
*/
/*
// Sets up pointers and enables states needed for using vertex arrays and textures
glVertexPointer(2, GL_FLOAT, 0, spriteVertices);
glEnableClientState(GL_VERTEX_ARRAY);
glTexCoordPointer(2, GL_SHORT, 0, spriteTexcoords);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
spriteData = (unsigned int *)malloc(4*65536);
for (int i = 0; i < 256; i++)
for (int j = 0; j < 256; j++)
spriteData[i+j*256] = i+i*256+i*65536 + j*256+j;
spriteTexture = loadTextureFromData(256, 256, (unsigned char*)spriteData, true);
spriteTexture = loadTextureFromData(256, 256, (unsigned char*)spriteData, false);
spriteTexture = loadTextureFromFile("Sprite.png");
*/
}
/*
- (void) swapBuffers
{
EAGLContext *oldContext = [EAGLContext currentContext];
if (oldContext != context)
[EAGLContext setCurrentContext:context];
glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
[context presentRenderbuffer:GL_RENDERBUFFER_OES];
if (oldContext != context)
[EAGLContext setCurrentContext:oldContext];
}
*/
- (void) drawView
{
#ifdef EGL_SUPPORT
eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext); // ?
#else
[EAGLContext setCurrentContext:context];
#endif
glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer);
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
cpp_context->draw();
glPopMatrix();
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
#ifdef EGL_SUPPORT
eglSwapBuffers(eglDisplay, eglSurface);
#else
// [glView swapBuffers];
glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
[context presentRenderbuffer:GL_RENDERBUFFER_OES];
#endif
}
- (void) layoutSubviews
{
[EAGLContext setCurrentContext:context];
[self destroyFramebuffer];
[self createFramebuffer];
[self drawView];
}
#define textureCase false
#define offscreenCase false
#define onscreenCase true
- (BOOL) createFramebuffer
{
int width = self.frame.size.width;
int height = self.frame.size.height;
glGenFramebuffersOES(1, &viewFramebuffer);
glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer);
// Texture case
if (textureCase) {
GLuint texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glFramebufferTexture2DOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_TEXTURE_2D, texture, 0);
}
// Offscreen case
if (offscreenCase) {
glGenRenderbuffersOES(1, &viewRenderbuffer);
glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
glRenderbufferStorageOES(GL_RENDERBUFFER_OES, GL_RGBA8_OES, width, height);
glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, viewRenderbuffer);
}
// Onscreen case
if (onscreenCase) {
glGenRenderbuffersOES(1, &viewRenderbuffer);
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 %x", glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES));
return NO;
}
if (textureCase)
glBindFramebufferOES(GL_FRAMEBUFFER_OES, 0);
return YES;
}
- (void) destroyFramebuffer
{
glDeleteFramebuffersOES(1, &viewFramebuffer);
viewFramebuffer = 0;
if (!textureCase) {
glDeleteRenderbuffersOES(1, &viewRenderbuffer);
viewRenderbuffer = 0;
}
if (depthRenderbuffer) {
glDeleteRenderbuffersOES(1, &depthRenderbuffer);
depthRenderbuffer = 0;
}
}
- (void) startAnimation
{
animationTimer = [NSTimer scheduledTimerWithTimeInterval:animationInterval target:self selector:@selector(drawView) userInfo:nil repeats:YES];
}
- (void) stopAnimation
{
[animationTimer invalidate];
animationTimer = nil;
}
- (void) setAnimationTimer:(NSTimer *)newTimer
{
[animationTimer invalidate];
animationTimer = newTimer;
}
- (void) setAnimationInterval:(NSTimeInterval)interval
{
animationInterval = interval;
if (animationTimer) {
[self stopAnimation];
[self startAnimation];
}
}
// You must implement this method
+ (Class) layerClass
{
return [CAEAGLLayer class];
}
- (void) accelerometer:(UIAccelerometer*)accelerometer didAccelerate:(UIAcceleration*)acceleration
{
cpp_context->updateAcceleration(acceleration.x, acceleration.y);
}
- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
if ([[event allTouches] count] == 1) {
CGPoint pos = [[[[event allTouches] allObjects] objectAtIndex:0] locationInView:self];
cpp_context->mouseDown(pos.x, pos.y);
}
}
- (void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
if ([[event allTouches] count] == 1) {
CGPoint pos = [[[[event allTouches] allObjects] objectAtIndex:0] locationInView:self];
cpp_context->mouseMove(pos.x, pos.y);
}
}
- (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
if ([[event allTouches] count] == 1) {
CGPoint pos = [[[[event allTouches] allObjects] objectAtIndex:0] locationInView:self];
cpp_context->mouseUp(pos.x, pos.y);
}
}
/*
- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
// If we're if waveform mode and not currently in a pinch event, and we've got two touches, start a pinch event
if ((!pinchEvent) && ([[event allTouches] count] == 2) && (self.displayMode == aurioTouchDisplayModeOscilloscopeWaveform))
{
pinchEvent = event;
NSArray *t = [[event allTouches] allObjects];
lastPinchDist = fabs([[t objectAtIndex:0] locationInView:view].x - [[t objectAtIndex:1] locationInView:view].x);
sampleSizeText.text = [NSString stringWithFormat:@"%i ms", drawBufferLen / (int)(hwSampleRate / 1000.)];
[view addSubview:sampleSizeOverlay];
}
}
- (void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
// If we are in a pinch event...
if ((event == pinchEvent) && ([[event allTouches] count] == 2))
{
CGFloat thisPinchDist, pinchDiff;
NSArray *t = [[event allTouches] allObjects];
thisPinchDist = fabs([[t objectAtIndex:0] locationInView:view].x - [[t objectAtIndex:1] locationInView:view].x);
// Find out how far we traveled since the last event
pinchDiff = thisPinchDist - lastPinchDist;
// Adjust our draw buffer length accordingly,
drawBufferLen -= 12 * (int)pinchDiff;
drawBufferLen = CLAMP(kMinDrawSamples, drawBufferLen, kMaxDrawSamples);
// and display the size of our oscilloscope window in our overlay view
sampleSizeText.text = [NSString stringWithFormat:@"%i ms", drawBufferLen / (int)(hwSampleRate / 1000.)];
lastPinchDist = thisPinchDist;
}
}
- (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
if (event == pinchEvent)
{
// If our pinch/zoom has ended, nil out the pinchEvent and remove the overlay view
[sampleSizeOverlay removeFromSuperview];
pinchEvent = nil;
return;
}
// any tap in sonogram view will exit back to the waveform
if (self.displayMode == aurioTouchDisplayModeSpectrum)
{
AudioServicesPlaySystemSound(doubleTapSound);
self.displayMode = aurioTouchDisplayModeOscilloscopeWaveform;
return;
}
UITouch *touch = [touches anyObject];
if (CGRectContainsPoint(CGRectMake(0., 5., 52., 99.), [touch locationInView:view])) // The Sonogram button was touched
{
AudioServicesPlaySystemSound(doubleTapSound);
if ((self.displayMode == aurioTouchDisplayModeOscilloscopeWaveform) || (self.displayMode == aurioTouchDisplayModeOscilloscopeFFT))
{
if (!initted_spectrum) [self setupViewForSpectrum];
[self clearTextures];
self.displayMode = aurioTouchDisplayModeSpectrum;
}
}
else if (CGRectContainsPoint(CGRectMake(0., 104., 52., 99.), [touch locationInView:view])) // The Mute button was touched
{
AudioServicesPlaySystemSound(buttonPressSound);
self.mute = !(self.mute);
return;
}
else if (CGRectContainsPoint(CGRectMake(0., 203, 52., 99.), [touch locationInView:view])) // The FFT button was touched
{
AudioServicesPlaySystemSound(buttonPressSound);
self.displayMode = (self.displayMode == aurioTouchDisplayModeOscilloscopeWaveform) ? aurioTouchDisplayModeOscilloscopeFFT :
aurioTouchDisplayModeOscilloscopeWaveform;
return;
}
}
*/
@end