| // |
| // GameViewController.m |
| // imguiex |
| // |
| #import "GameViewController.h" |
| #import <OpenGLES/ES2/glext.h> |
| |
| #import "imgui_impl_ios.h" |
| #import "debug_hud.h" |
| |
| #define BUFFER_OFFSET(i) ((char *)NULL + (i)) |
| |
| #define SERVERNAME_KEY @"ServerName" |
| |
| #define SERVERNAME_ALERT_TAG (10) |
| |
| // Uniform index. |
| enum |
| { |
| UNIFORM_MODELVIEWPROJECTION_MATRIX, |
| UNIFORM_NORMAL_MATRIX, |
| UNIFORM_DIFFUSE_COLOR, |
| |
| NUM_UNIFORMS |
| }; |
| GLint uniforms[NUM_UNIFORMS]; |
| |
| // Attribute index. |
| enum |
| { |
| ATTRIB_VERTEX, |
| ATTRIB_NORMAL, |
| NUM_ATTRIBUTES |
| }; |
| |
| GLfloat gCubeVertexData[216] = |
| { |
| // Data layout for each line below is: |
| // positionX, positionY, positionZ, normalX, normalY, normalZ, |
| 0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, |
| 0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 0.0f, |
| 0.5f, -0.5f, 0.5f, 1.0f, 0.0f, 0.0f, |
| 0.5f, -0.5f, 0.5f, 1.0f, 0.0f, 0.0f, |
| 0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 0.0f, |
| 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, |
| |
| 0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, |
| -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, |
| 0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, |
| 0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, |
| -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, |
| -0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, |
| |
| -0.5f, 0.5f, -0.5f, -1.0f, 0.0f, 0.0f, |
| -0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, |
| -0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f, |
| -0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f, |
| -0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, |
| -0.5f, -0.5f, 0.5f, -1.0f, 0.0f, 0.0f, |
| |
| -0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, |
| 0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, |
| -0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, |
| -0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, |
| 0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, |
| 0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, |
| |
| 0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, |
| -0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, |
| 0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, |
| 0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, |
| -0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, |
| -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, |
| |
| 0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, |
| -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, |
| 0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, |
| 0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, |
| -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, |
| -0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f |
| }; |
| |
| @interface GameViewController () <UIAlertViewDelegate> |
| { |
| GLuint _program; |
| |
| GLKMatrix4 _modelViewProjectionMatrix; |
| GLKMatrix3 _normalMatrix; |
| float _rotation; |
| |
| GLuint _vertexArray; |
| GLuint _vertexBuffer; |
| |
| DebugHUD _hud; |
| } |
| @property (strong, nonatomic) EAGLContext *context; |
| @property (strong, nonatomic) GLKBaseEffect *effect; |
| @property (strong, nonatomic) ImGuiHelper *imgui; |
| @property (weak, nonatomic) IBOutlet UIButton *btnServername; |
| |
| @property (strong, nonatomic) NSString *serverName; |
| |
| - (IBAction)onServernameTapped:(id)sender; |
| |
| - (void)setupGL; |
| - (void)tearDownGL; |
| |
| - (BOOL)loadShaders; |
| - (BOOL)compileShader:(GLuint *)shader type:(GLenum)type file:(NSString *)file; |
| - (BOOL)linkProgram:(GLuint)prog; |
| - (BOOL)validateProgram:(GLuint)prog; |
| @end |
| |
| @implementation GameViewController |
| |
| - (void)viewDidLoad |
| { |
| [super viewDidLoad]; |
| |
| self.context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]; |
| |
| if (!self.context) { |
| NSLog(@"Failed to create ES context"); |
| } |
| |
| GLKView *view = (GLKView *)self.view; |
| view.context = self.context; |
| view.drawableDepthFormat = GLKViewDrawableDepthFormat24; |
| |
| [self.btnServername setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; |
| |
| [self setupGL]; |
| |
| NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults]; |
| self.serverName = [userDefaults objectForKey: SERVERNAME_KEY ]; |
| self.imgui = [[ImGuiHelper alloc] initWithView:self.view ]; |
| if (self.serverName) |
| { |
| [self.btnServername setTitle:self.serverName forState:UIControlStateNormal]; |
| [self.imgui connectServer: self.serverName ]; |
| } |
| |
| DebugHUD_InitDefaults( &_hud ); |
| } |
| |
| - (void)dealloc |
| { |
| [self tearDownGL]; |
| |
| if ([EAGLContext currentContext] == self.context) { |
| [EAGLContext setCurrentContext:nil]; |
| } |
| } |
| |
| - (void)didReceiveMemoryWarning |
| { |
| [super didReceiveMemoryWarning]; |
| |
| if ([self isViewLoaded] && ([[self view] window] == nil)) { |
| self.view = nil; |
| |
| [self tearDownGL]; |
| |
| if ([EAGLContext currentContext] == self.context) { |
| [EAGLContext setCurrentContext:nil]; |
| } |
| self.context = nil; |
| } |
| |
| // Dispose of any resources that can be recreated. |
| } |
| |
| |
| - (BOOL)prefersStatusBarHidden { |
| return YES; |
| } |
| |
| - (IBAction)onServernameTapped:(id)sender |
| { |
| UIAlertView * alert = [[UIAlertView alloc] initWithTitle:@"Set Server" message:@"Enter server name or IP for uSynergy" delegate:self cancelButtonTitle:@"OK" otherButtonTitles:@"Cancel", nil ]; |
| alert.alertViewStyle = UIAlertViewStylePlainTextInput; |
| alert.tag = SERVERNAME_ALERT_TAG; // cheezy way to tell which alert view we're responding to |
| [alert show]; |
| } |
| |
| - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex |
| { |
| if ((buttonIndex==0)&&(alertView.tag==SERVERNAME_ALERT_TAG)) |
| { |
| // This is really janky. I usually just hardcode the servername since I'm building it anyway. |
| // If you want to properly handle updating the server, you'll want to tear down and recreate |
| // the usynergy stuff in connectServer |
| BOOL serverNameWasSet = self.serverName.length > 0; |
| NSString *serverName = [[alertView textFieldAtIndex:0] text]; |
| |
| if ([serverName length] > 0) { |
| self.serverName = serverName; |
| NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults]; |
| [userDefaults setObject:serverName forKey:SERVERNAME_KEY ]; |
| [userDefaults synchronize]; |
| |
| [self.btnServername setTitle:self.serverName forState:UIControlStateNormal]; |
| |
| // If we hadn't previously connected, try now |
| if (!serverNameWasSet) { |
| [self.imgui connectServer:self.serverName]; |
| } |
| else |
| { |
| UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Servername Updated" |
| message:@"Restart the app to connect the server" |
| delegate:nil cancelButtonTitle:@"OK" otherButtonTitles: nil]; |
| [alert show]; |
| } |
| } |
| } |
| } |
| |
| - (void)setupGL |
| { |
| [EAGLContext setCurrentContext:self.context]; |
| |
| [self loadShaders]; |
| |
| self.effect = [[GLKBaseEffect alloc] init]; |
| self.effect.light0.enabled = GL_TRUE; |
| self.effect.light0.diffuseColor = GLKVector4Make(1.0f, 0.4f, 0.4f, 1.0f); |
| |
| glEnable(GL_DEPTH_TEST); |
| |
| glGenVertexArraysOES(1, &_vertexArray); |
| glBindVertexArrayOES(_vertexArray); |
| |
| glGenBuffers(1, &_vertexBuffer); |
| glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer); |
| glBufferData(GL_ARRAY_BUFFER, sizeof(gCubeVertexData), gCubeVertexData, GL_STATIC_DRAW); |
| |
| glEnableVertexAttribArray(GLKVertexAttribPosition); |
| glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, 24, BUFFER_OFFSET(0)); |
| glEnableVertexAttribArray(GLKVertexAttribNormal); |
| glVertexAttribPointer(GLKVertexAttribNormal, 3, GL_FLOAT, GL_FALSE, 24, BUFFER_OFFSET(12)); |
| |
| glBindVertexArrayOES(0); |
| |
| |
| } |
| |
| - (void)tearDownGL |
| { |
| [EAGLContext setCurrentContext:self.context]; |
| |
| glDeleteBuffers(1, &_vertexBuffer); |
| glDeleteVertexArraysOES(1, &_vertexArray); |
| |
| self.effect = nil; |
| |
| if (_program) { |
| glDeleteProgram(_program); |
| _program = 0; |
| } |
| } |
| |
| #pragma mark - GLKView and GLKViewController delegate methods |
| |
| - (void)update |
| { |
| float aspect = fabs(self.view.bounds.size.width / self.view.bounds.size.height); |
| GLKMatrix4 projectionMatrix = GLKMatrix4MakePerspective(GLKMathDegreesToRadians(65.0f), aspect, 0.1f, 100.0f); |
| |
| self.effect.transform.projectionMatrix = projectionMatrix; |
| |
| GLKMatrix4 baseModelViewMatrix = GLKMatrix4MakeTranslation(0.0f, 0.0f, -4.0f); |
| baseModelViewMatrix = GLKMatrix4Rotate(baseModelViewMatrix, _rotation, 0.0f, 1.0f, 0.0f); |
| |
| // Compute the model view matrix for the object rendered with GLKit |
| GLKMatrix4 modelViewMatrix = GLKMatrix4MakeTranslation(0.0f, 0.0f, -1.5f); |
| modelViewMatrix = GLKMatrix4Rotate(modelViewMatrix, _rotation, 1.0f, 1.0f, 1.0f); |
| modelViewMatrix = GLKMatrix4Multiply(baseModelViewMatrix, modelViewMatrix); |
| |
| self.effect.transform.modelviewMatrix = modelViewMatrix; |
| |
| // Compute the model view matrix for the object rendered with ES2 |
| modelViewMatrix = GLKMatrix4MakeTranslation(0.0f, 0.0f, 1.5f); |
| modelViewMatrix = GLKMatrix4Rotate(modelViewMatrix, _rotation, 1.0f, 1.0f, 1.0f); |
| modelViewMatrix = GLKMatrix4Multiply(baseModelViewMatrix, modelViewMatrix); |
| |
| _normalMatrix = GLKMatrix3InvertAndTranspose(GLKMatrix4GetMatrix3(modelViewMatrix), NULL); |
| |
| _modelViewProjectionMatrix = GLKMatrix4Multiply(projectionMatrix, modelViewMatrix); |
| |
| _rotation += self.timeSinceLastUpdate * (_hud.rotation_speed * (M_PI / 180.0)); |
| } |
| |
| |
| - (void)glkView:(GLKView *)view drawInRect:(CGRect)rect |
| { |
| glClearColor(0.65f, 0.65f, 0.65f, 1.0f); |
| glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); |
| |
| glBindVertexArrayOES(_vertexArray); |
| |
| // Render the object with GLKit |
| [self.effect prepareToDraw]; |
| |
| glDrawArrays(GL_TRIANGLES, 0, 36); |
| |
| // Render the object again with ES2 |
| glUseProgram(_program); |
| |
| glUniformMatrix4fv(uniforms[UNIFORM_MODELVIEWPROJECTION_MATRIX], 1, 0, _modelViewProjectionMatrix.m); |
| glUniformMatrix3fv(uniforms[UNIFORM_NORMAL_MATRIX], 1, 0, _normalMatrix.m); |
| glUniform3f(uniforms[UNIFORM_DIFFUSE_COLOR], _hud.cubeColor1[0], _hud.cubeColor1[1], _hud.cubeColor1[2] ); |
| |
| glDrawArrays(GL_TRIANGLES, 0, 36); |
| |
| [self.imgui newFrame]; |
| |
| // Now do our ImGUI UI |
| DebugHUD_DoInterface( &_hud ); |
| |
| self.effect.light0.diffuseColor = GLKVector4Make( _hud.cubeColor2[0], _hud.cubeColor2[1], _hud.cubeColor2[2], 1.0f); |
| |
| // Now render Imgui |
| [self.imgui render]; |
| |
| } |
| |
| #pragma mark - OpenGL ES 2 shader compilation |
| |
| - (BOOL)loadShaders |
| { |
| GLuint vertShader, fragShader; |
| NSString *vertShaderPathname, *fragShaderPathname; |
| |
| // Create shader program. |
| _program = glCreateProgram(); |
| |
| // Create and compile vertex shader. |
| vertShaderPathname = [[NSBundle mainBundle] pathForResource:@"Shader" ofType:@"vsh"]; |
| if (![self compileShader:&vertShader type:GL_VERTEX_SHADER file:vertShaderPathname]) { |
| NSLog(@"Failed to compile vertex shader"); |
| return NO; |
| } |
| |
| // Create and compile fragment shader. |
| fragShaderPathname = [[NSBundle mainBundle] pathForResource:@"Shader" ofType:@"fsh"]; |
| if (![self compileShader:&fragShader type:GL_FRAGMENT_SHADER file:fragShaderPathname]) { |
| NSLog(@"Failed to compile fragment shader"); |
| return NO; |
| } |
| |
| // Attach vertex shader to program. |
| glAttachShader(_program, vertShader); |
| |
| // Attach fragment shader to program. |
| glAttachShader(_program, fragShader); |
| |
| // Bind attribute locations. |
| // This needs to be done prior to linking. |
| glBindAttribLocation(_program, GLKVertexAttribPosition, "position"); |
| glBindAttribLocation(_program, GLKVertexAttribNormal, "normal"); |
| |
| // Link program. |
| if (![self linkProgram:_program]) { |
| NSLog(@"Failed to link program: %d", _program); |
| |
| if (vertShader) { |
| glDeleteShader(vertShader); |
| vertShader = 0; |
| } |
| if (fragShader) { |
| glDeleteShader(fragShader); |
| fragShader = 0; |
| } |
| if (_program) { |
| glDeleteProgram(_program); |
| _program = 0; |
| } |
| |
| return NO; |
| } |
| |
| // Get uniform locations. |
| uniforms[UNIFORM_MODELVIEWPROJECTION_MATRIX] = glGetUniformLocation(_program, "modelViewProjectionMatrix"); |
| uniforms[UNIFORM_NORMAL_MATRIX] = glGetUniformLocation(_program, "normalMatrix"); |
| uniforms[UNIFORM_DIFFUSE_COLOR] = glGetUniformLocation(_program, "diffuseColor"); |
| |
| // Release vertex and fragment shaders. |
| if (vertShader) { |
| glDetachShader(_program, vertShader); |
| glDeleteShader(vertShader); |
| } |
| if (fragShader) { |
| glDetachShader(_program, fragShader); |
| glDeleteShader(fragShader); |
| } |
| |
| return YES; |
| } |
| |
| - (BOOL)compileShader:(GLuint *)shader type:(GLenum)type file:(NSString *)file |
| { |
| GLint status; |
| const GLchar *source; |
| |
| source = (GLchar *)[[NSString stringWithContentsOfFile:file encoding:NSUTF8StringEncoding error:nil] UTF8String]; |
| if (!source) { |
| NSLog(@"Failed to load vertex shader"); |
| return NO; |
| } |
| |
| *shader = glCreateShader(type); |
| glShaderSource(*shader, 1, &source, NULL); |
| glCompileShader(*shader); |
| |
| #if defined(DEBUG) |
| GLint logLength; |
| glGetShaderiv(*shader, GL_INFO_LOG_LENGTH, &logLength); |
| if (logLength > 0) { |
| GLchar *log = (GLchar *)malloc(logLength); |
| glGetShaderInfoLog(*shader, logLength, &logLength, log); |
| NSLog(@"Shader compile log:\n%s", log); |
| free(log); |
| } |
| #endif |
| |
| glGetShaderiv(*shader, GL_COMPILE_STATUS, &status); |
| if (status == 0) { |
| glDeleteShader(*shader); |
| return NO; |
| } |
| |
| return YES; |
| } |
| |
| - (BOOL)linkProgram:(GLuint)prog |
| { |
| GLint status; |
| glLinkProgram(prog); |
| |
| #if defined(DEBUG) |
| GLint logLength; |
| glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &logLength); |
| if (logLength > 0) { |
| GLchar *log = (GLchar *)malloc(logLength); |
| glGetProgramInfoLog(prog, logLength, &logLength, log); |
| NSLog(@"Program link log:\n%s", log); |
| free(log); |
| } |
| #endif |
| |
| glGetProgramiv(prog, GL_LINK_STATUS, &status); |
| if (status == 0) { |
| return NO; |
| } |
| |
| return YES; |
| } |
| |
| - (BOOL)validateProgram:(GLuint)prog |
| { |
| GLint logLength, status; |
| |
| glValidateProgram(prog); |
| glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &logLength); |
| if (logLength > 0) { |
| GLchar *log = (GLchar *)malloc(logLength); |
| glGetProgramInfoLog(prog, logLength, &logLength, log); |
| NSLog(@"Program validate log:\n%s", log); |
| free(log); |
| } |
| |
| glGetProgramiv(prog, GL_VALIDATE_STATUS, &status); |
| if (status == 0) { |
| return NO; |
| } |
| |
| return YES; |
| } |
| |
| @end |