Related
For the life of me, I can't render an image to the iPhone simulator screen. I've simplified my code as much as possible.
The following code is in ViewController.m, a class that extends GLKViewController and is also a GLKViewDelegate.
- (void)viewDidLoad {
[super viewDidLoad];
/*Setup EAGLContext*/
self.context = [self createBestEAGLContext];
[EAGLContext setCurrentContext:self.context];
/*Setup View*/
GLKView *view = [[GLKView alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
view.context = self.context;
view.delegate = self;
view.drawableDepthFormat = GLKViewDrawableDepthFormat24;
self.view = view;
}
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect {
/*Setup GLK effect*/
self.effect = [[GLKBaseEffect alloc] init];
self.effect.transform.projectionMatrix = GLKMatrix4MakeOrtho(0, 320, 480, 0, -1, 1);
glClearColor(0.5, 1, 1, 0.0);
glClear(GL_COLOR_BUFFER_BIT);
NSDictionary * options = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES],
GLKTextureLoaderOriginBottomLeft,
nil];
NSError * error;
NSString *path = [[NSBundle mainBundle] pathForResource:#"soccerball" ofType:#"jpg"];
GLKTextureInfo * textureInfo = [GLKTextureLoader textureWithContentsOfFile:path options:options error:&error];
if (textureInfo == nil) {
NSLog(#"Error loading file: %#", [error localizedDescription]);
}
TexturedQuad newQuad;
newQuad.bl.geometryVertex = CGPointMake(0, 0);
newQuad.br.geometryVertex = CGPointMake(textureInfo.width, 0);
newQuad.tl.geometryVertex = CGPointMake(0, textureInfo.height);
newQuad.tr.geometryVertex = CGPointMake(textureInfo.width, textureInfo.height);
newQuad.bl.textureVertex = CGPointMake(0, 0);
newQuad.br.textureVertex = CGPointMake(1, 0);
newQuad.tl.textureVertex = CGPointMake(0, 1);
newQuad.tr.textureVertex = CGPointMake(1, 1);
self.effect.texture2d0.name = textureInfo.name;
self.effect.texture2d0.enabled = YES;
GLKMatrix4 modelMatrix = GLKMatrix4Identity;
modelMatrix = GLKMatrix4Translate(modelMatrix, 100, 200, 0);
self.effect.transform.modelviewMatrix = modelMatrix;
[self.effect prepareToDraw];
long offset = (long)&(newQuad);
glEnableVertexAttribArray(GLKVertexAttribPosition);
glEnableVertexAttribArray(GLKVertexAttribTexCoord0);
glVertexAttribPointer(GLKVertexAttribPosition, 2, GL_FLOAT, GL_FALSE, sizeof(TexturedVertex), (void *) (offset + offsetof(TexturedVertex, geometryVertex)));
glVertexAttribPointer(GLKVertexAttribTexCoord0, 2, GL_FLOAT, GL_FALSE, sizeof(TexturedVertex), (void *) (offset + offsetof(TexturedVertex, textureVertex)));
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}
and some of the structs used...
typedef struct {
CGPoint geometryVertex;
CGPoint textureVertex;
} TexturedVertex;
typedef struct {
TexturedVertex bl;
TexturedVertex br;
TexturedVertex tl;
TexturedVertex tr;
} TexturedQuad;
Right now the only thing that is working is
glClearColor(0.5, 1, 1, 0.0);
glClear(GL_COLOR_BUFFER_BIT);
Which does adjust the background colour. There is no 'soccerball' image.
Any help is greatly appreciated.
EDIT - The TextureVertex CGPoints were incorrect so I fixed them. The problem still persists.
Solution:
The TexturedVertex struct must not use CGPoint, but rather GLKVector2.
This is because there is a conversion issue from the float values stored in these points. GLKit expects float values that have single point precision, but CGPoint float values have double point precision and things get weird. Furthermore, this problem only occurs after iOS 7.0
Refer to here for more detail on the issue.
OpenGL ES Shaders and 64-bit iPhone 5S
I have the following source code where i can draw some objects to the screen but i want to rotate and scale the drawed object and nothing happens when i am touching the screen. (In the log i can see if i am doing a pan gesture or a pinch gesture so the detection is ok)
What am i doing wrong?
UPDATE
Now it moves (instead of rotation) maybe i rotate it not around the center of the pointcloud. I tried to move it to center but now nothing is on the screen. Here is the updated Code:
#import <QuartzCore/QuartzCore.h>
#import <OpenGLES/EAGLDrawable.h>
#import "EAGLView.h"
#import "VertexPoint.h"
#import "CamOnMap-Swift.h"
#import <Math.h>
#define USE_DEPTH_BUFFER 1
#define DEGREES_TO_RADIANS(__ANGLE) ((__ANGLE) / 180.0 * M_PI)
// A class extension to declare private methods
#interface EAGLView ()
#property (nonatomic, retain) EAGLContext *context;
//#property (nonatomic, assign) NSTimer *animationTimer;
- (BOOL) createFramebuffer;
- (void) destroyFramebuffer;
#end
#implementation EAGLView
#synthesize context;
//#synthesize animationTimer;
//#synthesize animationInterval;
// You must implement this method
+ (Class)layerClass {
return [CAEAGLLayer class];
}
-(id)initWithCoder:(NSCoder *)aDecoder{
NSLog(#"Ide eljutottam ez az initWithCoder");
TOUCH_ROT_FACTOR = (180.0 / 320.0);
if ((self = [super initWithCoder:aDecoder])) {
// Get the layer
CAEAGLLayer *eaglLayer = (CAEAGLLayer *)self.layer;
eaglLayer.opaque = YES;
eaglLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:NO], kEAGLDrawablePropertyRetainedBacking, kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat, nil];
context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1];
if (!context || ![EAGLContext setCurrentContext:context]) {
//[self release];
return nil;
}
// animationInterval = 1.0 / 60.0;
[self setupView];
}
return self;
}
- (void)setupView {
const GLfloat zNear = 0.1, zFar = 1000.0, fieldOfView = 60.0;
GLfloat size;
glEnable(GL_DEPTH_TEST);
glMatrixMode(GL_PROJECTION);
size = zNear * tanf(DEGREES_TO_RADIANS(fieldOfView) / 2.0);
//Grab the size of the screen
CGRect rect = self.frame;
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);
glLoadIdentity();
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
//Initialize touchLocation to 0, 0
// touchLocation = CGPointMake(rect.size.width/2, rect.size.height/2);
// touchLocation = CGPointMake(0, 0);
// GLfloat x = (touchLocation.x);
// GLfloat y = (touchLocation.y);
//translate the triangle
// glTranslatef(x, y, -10.0);
multitouch_distance_start = 0;
viewDistance = -100;
[self setupGestures];
[self getPly];
}
- (void)setupGestures{
[self setUserInteractionEnabled:true];
[self setMultipleTouchEnabled:true];
pinchGesture = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:#selector(pinchHandler:)];
panGesture =[[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(panHandler:)];
[self addGestureRecognizer:pinchGesture];
[self addGestureRecognizer:panGesture];
};
-(VertexPoint*)center:(NSMutableArray *)vertices2{
float sum_x =0.0f;
float sum_y =0.0f;
float sum_z =0.0f;
for (int i=0; i<vertices2.count; i++) {
VertexPoint *point =vertices2[i];
sum_x +=point.x;
sum_y +=point.y;
sum_z +=point.z;
}
center_ = [[VertexPoint alloc] init];
GLfloat point[] = {(sum_x / vertices2.count),(sum_y / vertices2.count), (sum_z / vertices2.count)};
[center_ setPosition:point];
return center_;
}
-(float)multiTouchDistance:(UIPinchGestureRecognizer *) recognizer{
float x1 = [recognizer locationOfTouch:0 inView:self].x;
float y1 = [recognizer locationOfTouch:0 inView:self].y;
float x2 = [recognizer locationOfTouch:1 inView:self].x;
float y2 = [recognizer locationOfTouch:1 inView:self].y;
float dx = x2 - x1;
float dy = y2 - y1;
return sqrt(dx*dx + dy*dy);
}
- (void)pinchHandler:(UIPinchGestureRecognizer *)recognizer {
glPopMatrix();
float d = [self multiTouchDistance:recognizer];
NSLog(#"d: %f",d);
if(d > 0.0f)multitouch_distance_start = d;
viewDistance *= multitouch_distance_start / d;
multitouch_distance_start = d;
//glScalef(recognizer.scale, recognizer.scale, recognizer.scale);
//recognizer.scale = 1;
NSLog(#"viewDistance: %f",viewDistance);
glPushMatrix();
[self render];
}
- (void)panHandler:(UIPanGestureRecognizer *)recognizer {
if(recognizer.state == UIGestureRecognizerStateBegan){
CGPoint translation = [recognizer translationInView:self];
xPrev = translation.x;
yPrev = translation.y;
}
if(recognizer.state == UIGestureRecognizerStateChanged){
glPopMatrix();
CGPoint translation = [recognizer translationInView:self];
GLfloat dx = translation.x - xPrev;
GLfloat dy = translation.y - yPrev;
xAngle += ((dx) * (TOUCH_ROT_FACTOR));
yAngle += ((dy) * (TOUCH_ROT_FACTOR));
// glRotatef(xAngle,1,0,0);
// glRotatef(yAngle,0,1,0);
xPrev = translation.x;
yPrev = translation.y;
glPushMatrix();
NSLog(#"pan");
[self render];
}
};
-(void)completionHandler:(NSString *) data{
// NSLog(#"%#",data);
NSArray *dataArray = [data componentsSeparatedByString:#"\n"];
vertices = [[NSMutableArray alloc] init];
for(NSInteger i = 14; i < dataArray.count; i++ ){
NSArray *row = [dataArray[i] componentsSeparatedByString:#" "];
if(![dataArray[i] isEqual: #""]){
VertexPoint *point = [[VertexPoint alloc] init];
GLfloat a[] = { [[row objectAtIndex:0] floatValue], [[row objectAtIndex:1] floatValue], [[row objectAtIndex:2] floatValue]};
[point setPosition:a];
GLfloat b[] = { [[row objectAtIndex:6] floatValue],[[row objectAtIndex:7] floatValue], [[row objectAtIndex:8] floatValue], [[row objectAtIndex:9] floatValue]};
[point setColor:b];
[vertices addObject:point];
}
}
long vcount = [vertices count] * 3; // * 2 for the two coordinates of a loc object
glVertices = (GLfloat *)malloc(vcount * sizeof(GLfloat));
int currIndex = 0;
for (VertexPoint *loc in vertices) {
glVertices[currIndex++] = (loc.x);
glVertices[currIndex++] =(loc.y);
glVertices[currIndex++] =(loc.z);
}
long ccount = [vertices count] * 4;
glColors = (GLfloat *)malloc(ccount * sizeof(GLfloat));
int currColorIndex = 0;
for(VertexPoint *color in vertices){
glColors[currColorIndex++] = (color.r);
glColors[currColorIndex++] = (color.g);
glColors[currColorIndex++] = (color.b);
glColors[currColorIndex++] = (color.a);
}
center_ = [self center: vertices];
glTranslatef(0, 0, -100);
// glTranslatef(-1 * center_.x, -1 * center_.y, -1 * center_.z);
NSLog(#"x: %f, y: %f, z:%f",-1 * center_.x,-1 * center_.y,-1 * center_.z);
[self drawView];
};
-(void)getPly{
GlobalDataFunctions *globalDataFunctions = [[GlobalDataFunctions alloc] init];
NSString *ply = [globalDataFunctions selectedPly];
NSLog(#"%#",ply);
RestClient *restClient = [[RestClient alloc] init];
NSString *username = [[NSUserDefaults standardUserDefaults] objectForKey:#"username"];
NSString *password = [[NSUserDefaults standardUserDefaults] objectForKey:#"password"];
[restClient getPly: username password:password ply:ply myComletionHandler: ^(NSString *data){
[self completionHandler:data];
}];
};
- (void)drawView {
NSLog(#"drawView");
//setting up the draw content
[EAGLContext setCurrentContext:context];
glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer);
//Draw stuff
//reset matrix to identity
glLoadIdentity();
//rough approximation of screen to current 3D space
GLfloat x = (touchLocation.x - 160.0) / 38.0;
GLfloat y = (240.0 - touchLocation.y) / 38.0;
//translate the triangle
glTranslatef(x, y, -1.0);
[self render];
}
-(void)render{
NSLog(#"render");
center_ = [self center: vertices];
// glTranslatef(-1 * center_.x, -1 * center_.y, -1 * center_.z);
//clear the back color back to our original color and depth buffer
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//rough approximation of screen to current 3D space
//set the format and location for verticies
glVertexPointer(3, GL_FLOAT, 0, glVertices);
//set the opengl state
glEnableClientState(GL_VERTEX_ARRAY);
//set the format and location for colors
glColorPointer(4, GL_FLOAT, 0, glColors);
//set the opengl state
glEnableClientState(GL_COLOR_ARRAY);
glPointSize(3);
//draw the triangles
// glTranslatef(0,0,viewDistance);
// glTranslatef(-1 * center_.x, -1 * center_.y, -1 * center_.z);
glDrawArrays(GL_POINTS, 0, vertices.count);
glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
//show the render buffer
[context presentRenderbuffer:GL_RENDERBUFFER_OES];
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
touchLocation = [touch locationInView:self];
NSLog(#"touch");
[self drawView];
}
- (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 %x", glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES));
return NO;
}
return YES;
}
- (void)destroyFramebuffer {
glDeleteFramebuffersOES(1, &viewFramebuffer);
viewFramebuffer = 0;
glDeleteRenderbuffersOES(1, &viewRenderbuffer);
viewRenderbuffer = 0;
if(depthRenderbuffer) {
glDeleteRenderbuffersOES(1, &depthRenderbuffer);
depthRenderbuffer = 0;
}
}
And here can you find a sample input data: http://pastebin.com/rcrfZdyt
Basically, in my iOS app, i have a modal screen, in which i display some OpenGL ES graphics. I am able to enter and exit this modal screen 6 times, before the application crashes. I'm assuming this is a memory issue, but i'm stuck as to what and / or where.
Any help on this matter is incredibly appreciated.
In my OGLViewController:
#define OPENGL_ERROR_CHECK {GLuint error = glGetError(); ((error == GL_NO_ERROR) ? : NSLog(#"GL Error: %d", (error)));}
#interface OGLItemViewController : GLKViewController
#property (nonatomic) GLuint program;
// item ogl arrays + buffers
#property (nonatomic) NSInteger numOGLBuffers;
#property (nonatomic) GLuint* vertexArrays;
#property (nonatomic) GLuint* vertexBuffers;
// index buffer for ogl item vertices
#property (nonatomic) GLuint* indexBuffers;
#property (strong, nonatomic) EAGLContext *context;
#property (strong, nonatomic) GLKBaseEffect *effect;
#end
#implementation
- (void)viewDidLoad
{
[super viewDidLoad];
self.context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
OPENGL_ERROR_CHECK
glFinish(); // put this in here to make sure all previous calls have been finished
OPENGL_ERROR_CHECK
if (!self.context)
{
NSLog(#"Failed to create ES context");
}
GLKView *view = (GLKView *) self.view;
view.context = self.context;
view.drawableDepthFormat = GLKViewDrawableDepthFormat24;
view.opaque = NO;
self.numOGLBuffers = 0;
self.vertexArrays = nil;
self.vertexBuffers = nil;
self.indexBuffers = nil;
}
- (void)setupGL
{
OPENGL_ERROR_CHECK
[EAGLContext setCurrentContext:self.context];
OPENGL_ERROR_CHECK
[self loadShaders];
OPENGL_ERROR_CHECK
self.effect = [[GLKBaseEffect alloc] init];
OPENGL_ERROR_CHECK
self.effect.light0.enabled = GL_TRUE;
OPENGL_ERROR_CHECK
self.effect.colorMaterialEnabled = GL_TRUE;
OPENGL_ERROR_CHECK
self.effect.lightModelTwoSided = GL_FALSE;
OPENGL_ERROR_CHECK
self.effect.light0.diffuseColor = GLKVector4Make(0.69f, 0.69f, 0.69f, 0.5f);
OPENGL_ERROR_CHECK
glEnable(GL_DEPTH_TEST);
OPENGL_ERROR_CHECK
Item *item = [GlobalStore sharedInstance].item
NSMutableArray *shells = item.geometry;
if (shells.count > 0)
{
_vertexArrays = malloc(shells.count * sizeof(GLuint));
_vertexBuffers = malloc(shells.count * sizeof(GLuint));
_indexBuffers = malloc(shells.count * sizeof(GLuint));
self.numOGLBuffers = shells.count;
for (int i = 0; i < shells.count; i++)
{
Geometry *geom = [shells objectAtIndex:i];
if (geom.vertexCount > 0)
{
GLuint vao = 0;
OPENGL_ERROR_CHECK
glGenVertexArraysOES(1, &vao);
OPENGL_ERROR_CHECK
glBindVertexArrayOES(vao);
OPENGL_ERROR_CHECK
_vertexArrays[i] = vao;
if (!geom.vertices)
{
[self displayError:-998]; // generic error codes that i've just canned in to see if any problems with these pointers
}
if (!geom.indices)
{
[self displayError:-997];
}
// create vertice buffer
GLuint vbo = 0;
glGenBuffers(1, &vbo);
OPENGL_ERROR_CHECK
glBindBuffer(GL_ARRAY_BUFFER, vbo);
OPENGL_ERROR_CHECK
glBufferData(GL_ARRAY_BUFFER, geom.vertexCount * sizeof(OGLVertices), geom.vertices, GL_STATIC_DRAW);
OPENGL_ERROR_CHECK
_vertexBuffers[i] = vbo;
// create index by buffer
GLuint ibo = 0;
glGenBuffers(1, &ibo);
OPENGL_ERROR_CHECK
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
OPENGL_ERROR_CHECK
glBufferData(GL_ELEMENT_ARRAY_BUFFER, geom.indexCount * sizeof(GLuint), geom.indices, GL_STATIC_DRAW);
OPENGL_ERROR_CHECK
_indexBuffers[i] = ibo;
// enable position, normal and colour attributes
glEnableVertexAttribArray(GLKVertexAttribPosition);
OPENGL_ERROR_CHECK
glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, sizeof(OGLVertices), (const GLvoid *) offsetof(OGLVertices, Position));
OPENGL_ERROR_CHECK
glEnableVertexAttribArray(GLKVertexAttribNormal);
OPENGL_ERROR_CHECK
glVertexAttribPointer(GLKVertexAttribNormal, 3, GL_FLOAT, GL_FALSE, sizeof(OGLVertices), (const GLvoid *) offsetof(OGLVertices, Normal));
OPENGL_ERROR_CHECK
glEnableVertexAttribArray(GLKVertexAttribColor);
OPENGL_ERROR_CHECK
glVertexAttribPointer(GLKVertexAttribColor, 4, GL_FLOAT, GL_FALSE, sizeof(OGLVertices), (const GLvoid *) offsetof(OGLVertices, Colour));
OPENGL_ERROR_CHECK
}
}
glBindVertexArrayOES(0);
OPENGL_ERROR_CHECK
}
}
- (void)tearDownGL
{
[EAGLContext setCurrentContext:self.context];
self.effect = nil;
[self deleteOGLData];
}
- (void)dealloc
{
[self tearDownGL];
if ([EAGLContext currentContext] == self.context)
{
[EAGLContext setCurrentContext:nil];
}
}
- (void) viewDidDisappear:(BOOL)animated
{
[self tearDownGL];
}
- (void)deleteOGLData
{
// delete ogl buffers and arrays
if (self.numOGLBuffers > 0)
{
if (_vertexBuffers)
{
OPENGL_ERROR_CHECK
glDeleteBuffers(self.numOGLBuffers, _vertexBuffers);
OPENGL_ERROR_CHECK
free(_vertexBuffers);
_vertexBuffers = nil;
}
if (_vertexArrays)
{
glDeleteVertexArraysOES(self.numOGLBuffers, _vertexArrays);
OPENGL_ERROR_CHECK
free(_vertexArrays);
_vertexArrays = nil;
}
if (_indexBuffers)
{
glDeleteBuffers(self.numOGLBuffers, _indexBuffers);
OPENGL_ERROR_CHECK
free(_indexBuffers);
_indexBuffers = nil;
}
}
self.numOGLBuffers = 0;
glDeleteProgram(self.program);
OPENGL_ERROR_CHECK
self.program = 0;
glFinish(); // again, just put this in here to check everything has finished
OPENGL_ERROR_CHECK
}
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect
{
OPENGL_ERROR_CHECK
glClearColor(1.0, 1.0, 1.0, 0.0);
OPENGL_ERROR_CHECK
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
OPENGL_ERROR_CHECK
// Render the object with GLKit
[self.effect prepareToDraw];
OPENGL_ERROR_CHECK
NSMutableArray *shells = [GlobalStore sharedInstance].item.geometry;
if (shells && _vertexArrays && _vertexBuffers)
{
for (int i = 0; i < shells.count; i++)
{
Geometry *geom = [shells objectAtIndex:i];
if (geom.vertexCount > 0)
{
GLuint vao = self.vertexArrays[i];
glBindVertexArrayOES(vao);
OPENGL_ERROR_CHECK
if(geom.indexCount == 0)
[self displayError:-996];
glDrawElements(GL_TRIANGLES, geom.indexCount, GL_UNSIGNED_INT, 0);
OPENGL_ERROR_CHECK
self.initialised = YES;
}
}
}
}
- (BOOL)loadShaders
{
GLuint vertShader, fragShader;
NSString *vertShaderPathname, *fragShaderPathname;
// Create shader program.
self.program = glCreateProgram();
OPENGL_ERROR_CHECK
// 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);
OPENGL_ERROR_CHECK
// Attach fragment shader to program.
glAttachShader(_program, fragShader);
OPENGL_ERROR_CHECK
// Bind attribute locations.
// This needs to be done prior to linking.
glBindAttribLocation(_program, GLKVertexAttribPosition, "position");
OPENGL_ERROR_CHECK
glBindAttribLocation(_program, GLKVertexAttribNormal, "normal");
OPENGL_ERROR_CHECK
// Link program.
if (![self linkProgram:_program])
{
NSLog(#"Failed to link program: %d", _program);
if (vertShader)
{
glDeleteShader(vertShader);
OPENGL_ERROR_CHECK
vertShader = 0;
}
if (fragShader)
{
glDeleteShader(fragShader);
OPENGL_ERROR_CHECK
fragShader = 0;
}
if (_program)
{
glDeleteProgram(_program);
OPENGL_ERROR_CHECK
_program = 0;
}
return NO;
}
// Get uniform locations.
uniforms[UNIFORM_MODELVIEWPROJECTION_MATRIX] = glGetUniformLocation(_program, "modelViewProjectionMatrix");
uniforms[UNIFORM_NORMAL_MATRIX] = glGetUniformLocation(_program, "normalMatrix");
// Release vertex and fragment shaders.
if (vertShader)
{
glDetachShader(_program, vertShader);
OPENGL_ERROR_CHECK
glDeleteShader(vertShader);
OPENGL_ERROR_CHECK
}
if (fragShader)
{
glDetachShader(_program, fragShader);
OPENGL_ERROR_CHECK
glDeleteShader(fragShader);
OPENGL_ERROR_CHECK
}
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);
OPENGL_ERROR_CHECK
glShaderSource(*shader, 1, &source, NULL);
OPENGL_ERROR_CHECK
glCompileShader(*shader);
OPENGL_ERROR_CHECK
glGetShaderiv(*shader, GL_COMPILE_STATUS, &status);
OPENGL_ERROR_CHECK
if (status == 0)
{
glDeleteShader(*shader);
OPENGL_ERROR_CHECK
return NO;
}
return YES;
}
- (BOOL)linkProgram:(GLuint)prog
{
GLint status;
glLinkProgram(prog);
OPENGL_ERROR_CHECK
glGetProgramiv(prog, GL_LINK_STATUS, &status);
OPENGL_ERROR_CHECK
if (status == 0)
{
return NO;
}
return YES;
}
- (BOOL)validateProgram:(GLuint)prog
{
GLint logLength, status;
glValidateProgram(prog);
OPENGL_ERROR_CHECK
glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &logLength);
OPENGL_ERROR_CHECK
if (logLength > 0)
{
GLchar *log = (GLchar *)malloc(logLength);
glGetProgramInfoLog(prog, logLength, &logLength, log);
OPENGL_ERROR_CHECK
NSLog(#"Program validate log:\n%s", log);
free(log);
}
glGetProgramiv(prog, GL_VALIDATE_STATUS, &status);
OPENGL_ERROR_CHECK
if (status == 0)
{
return NO;
}
return YES;
}
-(void)displayError: (GLuint) err
{
NSString *msg = [[NSString alloc] initWithFormat:#"OpenGL Error: %d", err];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle: #"Error"
message: msg
delegate:self
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
}
#end
Now - i don't know if it's the right way to do this, but in the viewdiddisappear method, i make sure to delete all the ogl data. i wanted to make sure that when i exited the view, the ogl data is deleted from memory.
So, if i enter the modal view, exit, and repeat 5 more times, i will get a black screen crash, and will break, with the following screen:
I have made sure everything is done in a single thread where appropriate. I have tried to make sure all objects and buffers are deleted.
So i'm wondering what on earth am i doing wrong to cause it to suddenly crash..... Nothing in the profiler seems to indicate anything. I don't seem to have any memory leaks or anything too substantial that would cause memory to run out.
( i do know i'm rendering every frame, when i dont need to - i will address this at a later point.)
The globalstore i have holds an instance of some objects that i use. These should always be valid (i've checked).
What i needed to do, was to delete the GLKView drawables.
In the ViewDidDisappear method (might not be the proper place, but it works) add the following:
GLKView *view = (GLKView*) self.view;
[view deleteDrawable];
The following code can be a complete template for color-picking with glkit. I just need to understand why all the pixels in the offscreen buffer are colored (0,0,0) as printed below to the nslog.
Note: the vertices array is defined in an header file as a const array it is displayed well on the screen in another section of my project (my problem is with the offscreen).
Header file:
typedef struct {
float Position[3];
float Color[4];
float TexCoord[2];
float Normal[3];
} Vertex;
//Vertices array format: {{vertex.x, vertex.y, vertex.z}, {color.R, color.G, color.B, color.alpha}, {texture.U, texture.V}, {normal.x, normal.y, normal.z}},
const Vertex parparit51OBJVertices[] = {
{{0.057, -0.088, -0.155},{1,1,1,1},{0.848, 0.810}, {0.329, -0.157, -0.931}},
{{0.056, -0.035, -0.165},{1,1,1,1},{0.848, 0.811}, {0.338, -0.139, -0.931}}, ......
In the viewController code:
GLuint _pickFBO = 0;
int32_t glVertexAttributeBufferID = 0;
- (IBAction) tapGesture:(id)sender
{
if ([(UITapGestureRecognizer *)sender state] == UIGestureRecognizerStateEnded) {
NSLog( #"In tap ended" );
CGPoint tapLocation = [(UITapGestureRecognizer *)sender locationInView:self.view];
int tt = [self findMeshByPoint:tapLocation];
}
}
- (NSUInteger)findMeshByPoint:(CGPoint)point
{
//In openGL the y axis starts from the bottom of the screen
point.y = self.view.bounds.size.height - point.y;
GLKView *glView = (GLKView *)self.view;
NSAssert([glView isKindOfClass:[GLKView class]],
#"View controller's view is not a GLKView");
// Make the view's context current
[EAGLContext setCurrentContext:glView.context];
_height = ((GLKView *)self.view).drawableHeight;
_width = ((GLKView *)self.view).drawableWidth;
self.effect.useConstantColor = GL_TRUE;
self.effect.colorMaterialEnabled = GL_TRUE;
self.effect.light0.diffuseColor = GLKVector4Make(1.0f,1.0f,1.0f,1.0f);
glBindVertexArrayOES(0);
glDisable(GL_DITHER);
glEnable(GL_DEPTH_TEST);
glLineWidth(2.0F);
// Important to turn light off !!!
self.effect.light0.enabled = GL_TRUE;
glDisableVertexAttribArray(GLKVertexAttribTexCoord0);
//this crashes the code
//glEnableVertexAttribArray(GLKVertexAttribColor);
self.effect.constantColor = GLKVector4Make( 0.0f, //This should be meshId/255.0f
0.8f, 0.8f, 1.0f);
if(0 == _glVertexAttributeBufferID)
{
GLuint glName;
glGenBuffers(1, // STEP 1
&glName);
glBindBuffer(GL_ARRAY_BUFFER, // STEP 2
glName);
glBufferData( // STEP 3
GL_ARRAY_BUFFER, // Initialize buffer contents
sizeof(parparit51OBJVertices), parparit51OBJVertices,
GL_STATIC_DRAW); // Hint: cache in GPU memory
_glVertexAttributeBufferID = glName;
GLenum err = glGetError();
if (err != GL_NO_ERROR) {
NSLog(#"Error creating buffer %i. glError: 0x%04X", glName, err);
}
}
else
{
glBindBuffer(GL_ARRAY_BUFFER,
_glVertexAttributeBufferID);
}
[self buildFBO];
glBindFramebuffer(GL_FRAMEBUFFER, _pickFBO);
//glViewport(0, 0, _width, _height);
//???
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnableVertexAttribArray(GLKVertexAttribPosition);
glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, 12 * sizeof(GLfloat), 0);
glEnableVertexAttribArray(GLKVertexAttribNormal);
glVertexAttribPointer(GLKVertexAttribNormal, 3, GL_FLOAT, GL_FALSE, 12 * sizeof(GLfloat), (GLvoid*) (sizeof(float) * 9));
GLKMatrix4 modelViewMatrixForParparit = GLKMatrix4MakeTranslation(0.0f, 0.3f, -3.5f );
modelViewMatrixForParparit = GLKMatrix4Scale(modelViewMatrixForParparit, 1.0, 1.0, 1.0);
self.effect.transform.modelviewMatrix = modelViewMatrixForParparit;
self.effect.constantColor = GLKVector4Make( 0.8f, 0.3f, 0.3f, 1.0f );
[self.effect prepareToDraw];
glDrawArrays(GL_TRIANGLES, 0, sizeof(parparit51OBJVertices) / sizeof(Vertex));
const GLfloat width = [glView drawableWidth];
const GLfloat height = [glView drawableHeight];
NSAssert(0 < width && 0 < height, #"Invalid drawble size");
int blackPixelsCounter = 0;
int coloredPixelsCounter = 0;
GLubyte savePixelColor[4] = {0, };
bool bFoundDifferentColors = NO;
GLubyte pixelColor[4]; // Red, Green, Blue, Alpha color
glReadPixels(50,
50,
1,
1,
GL_RGBA,
GL_UNSIGNED_BYTE,
pixelColor);
//#ifdef DEBUG
{ // Report any errors
GLenum error = glGetError();
if(GL_NO_ERROR != error)
{
NSLog(#"GL Error: 0x%x", error);
}
}
//#endif
savePixelColor[0] = pixelColor[0];
savePixelColor[1] = pixelColor[1];
savePixelColor[2] = pixelColor[2];
for (GLint xx=0; xx<_width; xx++) {
for (GLint yy=0; yy<_height; yy++) {
glReadPixels(xx,
yy,
1,
1,
GL_RGBA,
GL_UNSIGNED_BYTE,
pixelColor);
//#ifdef DEBUG
{ // Report any errors
GLenum error = glGetError();
if(GL_NO_ERROR != error)
{
NSLog(#"GL Error: 0x%x", error);
}
}
//#endif
if ( (savePixelColor[0] != pixelColor[0]) || (savePixelColor[1] != pixelColor[1]) || (savePixelColor[2] != pixelColor[2]) )
{
bFoundDifferentColors = YES;
}
if ( (pixelColor[0] !=0) || (pixelColor[1] !=0) || (pixelColor[2] !=0) ) {
//NSLog(#"pixelColor[0]=%i, pixelColor[1]=%i, pixelColor[2]=%i", pixelColor[0], pixelColor[1], pixelColor[2] );
coloredPixelsCounter++;
}
else
{
blackPixelsCounter++;
}
}
}
NSLog( #"colored pixels=%i black pixels=%i", coloredPixelsCounter, blackPixelsCounter );
if ( bFoundDifferentColors )
{
NSLog( #"Found at least 2 different pixels colors in picking buffer !" );
}
else
{
NSLog( #"All pixels have the same color: %i, %i, %i", savePixelColor[0], savePixelColor[1], savePixelColor[2]);
}
NSLog( #"******* 9" );
//--- at the end !!! -------
// Restore OpenGL state that pickTerrainEffect changed
glBindFramebuffer(GL_FRAMEBUFFER, 0); // default frame buffer
//glViewport(0, 0, _width, _height); // full area of glView
//#ifdef DEBUG
{ // Report any errors
GLenum error = glGetError();
if(GL_NO_ERROR != error)
{
NSLog(#"GL Error: 0x%x", error);
}
}
//#endif
NSLog( #"******* 10" );
return 0;
}
//tap-11
-(void) buildFBO
{
NSLog(#"before: buildFBO._pickFBO=%i", _pickFBO );
if ( 0 == _pickFBO )
{
NSLog(#"buildFBO._pickFBO=%i", _pickFBO );
GLuint colorRenderbuffer;
//GLuint framebuffer;
glGenFramebuffers(1, &_pickFBO);
glBindFramebuffer(GL_FRAMEBUFFER, _pickFBO);
glGenRenderbuffers(1, &colorRenderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, colorRenderbuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8_OES, _width, _height);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER, colorRenderbuffer);
GLuint depthRenderbuffer;
glGenRenderbuffers(1, &depthRenderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, depthRenderbuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, _width, _height);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthRenderbuffer);
if(glCheckFramebufferStatus(GL_FRAMEBUFFER) !=
GL_FRAMEBUFFER_COMPLETE)
{
NSLog(#"failed to make complete framebuffer object %x", glCheckFramebufferStatus(GL_FRAMEBUFFER));
//+++tbd+++UtilityPickTerrainEffectDestroyFBO(fboName);
return;
}
//#ifdef DEBUG
// { // Report any errors
GLenum error = glGetError();
if(GL_NO_ERROR != error)
{
NSLog(#"GL Error: 0x%x", error);
}
// }
//#endif
}
}
I think you need to close the texture out before reading from it. My color picker implementation is working fine. But I remember that for about 2 days I was also getting only 0,0,0,0.
Here's a clue for you. Is the pixel you're getting (0,0,0,0) or (0,0,0,1)? Cause you only mention that it's (0,0,0). Since you have a clearColor to (0,0,0,1), if you're getting an alpha of zero it's not reading the buffer at all.
I recommend doing it in 2 steps. One method to draw the color picker. Another to read from it. here's my exact code for reading my color buffer:
-(void)process3DTouch
{
if (colorPickerNeedsRedrawn) {
[self drawColorPicker];
}
glBindFramebuffer(GL_FRAMEBUFFER, pickerFramebuffer); // speed note - will bind twice if we just had to draw it
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureInfo[TEX_PICKER].texture, 0);
Byte pixelColor[4] = {0,0,0,0};
glReadPixels(colorPickerTouchPosition.x * backingScale, (backingHeight - (colorPickerTouchPosition.y * backingScale)), 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixelColor);
ColorSpec pickerColor = ColorSpecMake((float)pixelColor[0]/255, (float)pixelColor[1]/255, (float)pixelColor[2]/255, (float)pixelColor[3]/255);
//NSLog(#"what is pixelColor at %f,%f = %f %f %f %f", colorPickerTouchPosition.x, colorPickerTouchPosition.y, pickerColor.r,pickerColor.g,pickerColor.b,pickerColor.a);
if (pickerColor.a == 0.0) {
whatTouched=TOUCHED_NOTHING;
whatSecondaryTouched=TOUCHED_NOTHING;
NSLog(#"touched nothing");
processColorPicker=false;
return;
}
// now look up the item index from the color
int itemIndex = [self itemIndexFromColor:pickerColor];
[self handleTouchedItem: itemIndex];
processColorPicker=false;
}
Another good debugging method for color pickers is to draw the color picker and then draw the color picker texture to the screen so you can see if you have something else going on in the drawing of the picker.
I have for example following code (just as example, I'm building not game, I'm trying to build KNOB control by OpenGL ES, Quartz2D not does not fit, do not ask why):
Original code from here:
http://www.raywenderlich.com/9743/how-to-create-a-simple-2d-iphone-game-with-opengl-es-2-0-and-glkit-part-1
Code drawing following images on the screen, in future I'll replace this images by my KNOB:
Basically I interested to rotate only Player (the man in the left area).
I want him to rotate continuously from 0 to 360 degrees. I tried following code, but...
self.effect.transform.modelviewMatrix = GLKMatrix4Rotate(self.effect.transform.modelviewMatrix, radians(10), 0, 0, -1);
I give full screen rotating (with monsters also), but I want to rotate only one object (in this example only player in left area). Also want to note that the monsters are moving.
The code for function [sprite render]; see below.
Here is main code:
//
// SGGViewController.m
// SimpleGLKitGame
//
// Created by Ray Wenderlich on 1/30/12.
// Copyright (c) 2012 __MyCompanyName__. All rights reserved.
//
#import "SGGViewController.h"
#import "SGGSprite.h"
#interface SGGViewController ()
#property (strong, nonatomic) EAGLContext *context;
#property (strong) GLKBaseEffect * effect;
#property (strong) SGGSprite * player;
#property (strong) NSMutableArray * children;
#property (assign) float timeSinceLastSpawn;
#end
#implementation SGGViewController
#synthesize effect = _effect;
#synthesize context = _context;
#synthesize player = _player;
#synthesize children = _children;
#synthesize timeSinceLastSpawn = _timeSinceLastSpawn;
- (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;
[EAGLContext setCurrentContext:self.context];
self.effect = [[GLKBaseEffect alloc] init];
GLKMatrix4 projectionMatrix = GLKMatrix4MakeOrtho(0, 1024, 0, 768, -1, 1);
self.effect.transform.projectionMatrix = projectionMatrix;
self.player = [[SGGSprite alloc] initWithFile:#"2.png" effect:self.effect];
self.player.position = GLKVector2Make(self.player.contentSize.width/2, 160);
self.children = [NSMutableArray array];
[self.children addObject:self.player];
}
- (void)addTarget {
SGGSprite * target = [[SGGSprite alloc] initWithFile:#"Target.png" effect:self.effect];
[self.children addObject:target];
int minY = target.contentSize.height/2;
int maxY = 320 - target.contentSize.height/2;
int rangeY = maxY - minY;
int actualY = (arc4random() % rangeY) + minY;
target.position = GLKVector2Make(480 + (target.contentSize.width/2), actualY);
int minVelocity = 480.0/4.0;
int maxVelocity = 480.0/2.0;
int rangeVelocity = maxVelocity - minVelocity;
int actualVelocity = (arc4random() % rangeVelocity) + minVelocity;
target.moveVelocity = GLKVector2Make(-actualVelocity, 0);
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return UIInterfaceOrientationIsLandscape(interfaceOrientation);
}
#pragma mark - GLKViewDelegate
static inline double radians (double degrees) {return degrees * M_PI/180;}
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect {
glClearColor(1, 1, 1, 1);
glClear(GL_COLOR_BUFFER_BIT);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_BLEND);
for (SGGSprite * sprite in self.children) {
[sprite render];
}
}
- (void)update {
self.timeSinceLastSpawn += self.timeSinceLastUpdate;
if (self.timeSinceLastSpawn > 1.0) {
self.timeSinceLastSpawn = 0;
[self addTarget];
}
for (SGGSprite * sprite in self.children) {
[sprite update:self.timeSinceLastUpdate];
}
}
#end
This is code for function [sprite render];
//
// SGGSprite.m
// SimpleGLKitGame
//
// Created by Ray Wenderlich on 1/30/12.
// Copyright (c) 2012 __MyCompanyName__. All rights reserved.
//
#import "SGGSprite.h"
typedef struct {
CGPoint geometryVertex;
CGPoint textureVertex;
} TexturedVertex;
typedef struct {
TexturedVertex bl;
TexturedVertex br;
TexturedVertex tl;
TexturedVertex tr;
} TexturedQuad;
#interface SGGSprite()
#property (strong) GLKBaseEffect * effect;
#property (assign) TexturedQuad quad;
#property (strong) GLKTextureInfo * textureInfo;
#end
#implementation SGGSprite
#synthesize position = _position;
#synthesize contentSize = _contentSize;
#synthesize effect = _effect;
#synthesize quad = _quad;
#synthesize textureInfo = _textureInfo;
#synthesize moveVelocity = _moveVelocity;
- (id)initWithFile:(NSString *)fileName effect:(GLKBaseEffect *)effect {
if ((self = [super init])) {
self.effect = effect;
NSDictionary * options = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES],
GLKTextureLoaderOriginBottomLeft,
nil];
NSError * error;
NSString *path = [[NSBundle mainBundle] pathForResource:fileName ofType:nil];
self.textureInfo = [GLKTextureLoader textureWithContentsOfFile:path options:options error:&error];
if (self.textureInfo == nil) {
NSLog(#"Error loading file: %#", [error localizedDescription]);
return nil;
}
self.contentSize = CGSizeMake(self.textureInfo.width, self.textureInfo.height);
TexturedQuad newQuad;
newQuad.bl.geometryVertex = CGPointMake(0, 0);
newQuad.br.geometryVertex = CGPointMake(self.textureInfo.width, 0);
newQuad.tl.geometryVertex = CGPointMake(0, self.textureInfo.height);
newQuad.tr.geometryVertex = CGPointMake(self.textureInfo.width, self.textureInfo.height);
newQuad.bl.textureVertex = CGPointMake(0, 0);
newQuad.br.textureVertex = CGPointMake(1, 0);
newQuad.tl.textureVertex = CGPointMake(0, 1);
newQuad.tr.textureVertex = CGPointMake(1, 1);
self.quad = newQuad;
}
return self;
}
- (GLKMatrix4) modelMatrix {
GLKMatrix4 modelMatrix = GLKMatrix4Identity;
modelMatrix = GLKMatrix4Translate(modelMatrix, self.position.x, self.position.y, 0);
modelMatrix = GLKMatrix4Translate(modelMatrix, -self.contentSize.width/2, -self.contentSize.height/2, 0);
return modelMatrix;
}
static inline double radians (double degrees) {return degrees * M_PI/180;}
- (void)render {
self.effect.texture2d0.name = self.textureInfo.name;
self.effect.texture2d0.enabled = YES;
self.effect.transform.modelviewMatrix = self.modelMatrix;
[self.effect prepareToDraw];
long offset = (long)&_quad;
glEnableVertexAttribArray(GLKVertexAttribPosition);
glEnableVertexAttribArray(GLKVertexAttribTexCoord0);
glVertexAttribPointer(GLKVertexAttribPosition, 2, GL_FLOAT, GL_FALSE, sizeof(TexturedVertex), (void *) (offset + offsetof(TexturedVertex, geometryVertex)));
glVertexAttribPointer(GLKVertexAttribTexCoord0, 2, GL_FLOAT, GL_FALSE, sizeof(TexturedVertex), (void *) (offset + offsetof(TexturedVertex, textureVertex)));
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}
- (void)update:(float)dt {
GLKVector2 curMove = GLKVector2MultiplyScalar(self.moveVelocity, dt);
self.position = GLKVector2Add(self.position, curMove);
}
#end
In this example you share the same functions to draw the player, the target, and the background.
You have to define a new effect and matrix independently for the player to apply a separate effect like rotation.
For example, here it is for SGGViewController.m:
//
// SGGViewController.m
// SimpleGLKitGame
//
// Created by Ray Wenderlich on 1/30/12.
// Copyright (c) 2012 __MyCompanyName__. All rights reserved.
//
#import "SGGViewController.h"
#import "SGGSprite.h"
float _rotation;
#interface SGGViewController ()
#property (strong, nonatomic) EAGLContext *context;
#property (strong) GLKBaseEffect * effect;
#property (strong) GLKBaseEffect * effectPlayer; //Added For The Player
#property (strong) SGGSprite * player;
#property (strong) NSMutableArray * children;
#property (strong) NSMutableArray * childrenPlayer; //Added For The Player
#property (assign) float timeSinceLastSpawn;
#end
#implementation SGGViewController
#synthesize effect = _effect;
#synthesize effectPlayer = _effectPlayer;
#synthesize context = _context;
#synthesize player = _player;
#synthesize children = _children;
#synthesize childrenPlayer = _childrenPlayer;
#synthesize timeSinceLastSpawn = _timeSinceLastSpawn;
- (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;
[EAGLContext setCurrentContext:self.context];
//--------------------This Is To Define Effect For The background-------------------
self.effect = [[GLKBaseEffect alloc] init];
GLKMatrix4 projectionMatrix = GLKMatrix4MakeOrtho(0, 480, 0, 320, -1024, 1024);
self.effect.transform.projectionMatrix = projectionMatrix;
//----------------------------------------------------------------------------------
//--------This One For The Player ---------
self.effectPlayer = [[GLKBaseEffect alloc] init];
GLKMatrix4 projectionMatrixPlay = GLKMatrix4MakeOrtho(0, 480, 0, 320, -1024, 1024);
self.effectPlayer.transform.projectionMatrix = projectionMatrixPlay;
//----------------------------------------------------------------------------------
//----Note Here I Have Uses Another Separate Function To Init The Texture File----
self.player = [[SGGSprite alloc] initWithFilePlayer:#"2.png" effectPlayer:self.effectPlayer];
self.player.positionPlayer = GLKVector2Make(self.player.contentSizePlayer.width/2, 160);
//--The Array Setting For Target And Anything Else (Background, Splash, Etc.)---
self.children = [NSMutableArray array];
//---And This Is The Array Setting For The Player---
self.childrenPlayer = [NSMutableArray array];
[self.childrenPlayer addObject:self.player];
}
//-----While The Target As Usual--------
- (void)addTarget
{
SGGSprite * target = [[SGGSprite alloc] initWithFile:#"Target.png" effect:self.effect];
[self.children addObject:target];
int minY = target.contentSize.height/2;
int maxY = 320 - target.contentSize.height/2;
int rangeY = maxY - minY;
int actualY = (arc4random() % rangeY) + minY;
target.position = GLKVector2Make(480 + (target.contentSize.width/2), actualY);
int minVelocity = 480.0/4.0;
int maxVelocity = 480.0/2.0;
int rangeVelocity = maxVelocity - minVelocity;
int actualVelocity = (arc4random() % rangeVelocity) + minVelocity;
target.moveVelocity = GLKVector2Make(-actualVelocity, 0);
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return UIInterfaceOrientationIsLandscape(interfaceOrientation);
}
#pragma mark - GLKViewDelegate
static inline double radians (double degrees) {return degrees * M_PI/180;}
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect {
glClearColor(1, 1, 1, 1);
glClear(GL_COLOR_BUFFER_BIT);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_BLEND);
for (SGGSprite * sprite in self.children) {
[sprite render];
}
//--------Separate Rendering For The Player In A New Function RenderPlayer-----------
for (SGGSprite * sprite in self.childrenPlayer) {
[sprite renderPlayer];
}
}
- (void)update
{
self.timeSinceLastSpawn += self.timeSinceLastUpdate;
_rotation += self.timeSinceLastUpdate * 20; //Speed And Angle Of Rotation
if (self.timeSinceLastSpawn > 1.0) {
self.timeSinceLastSpawn = 0;
[self addTarget];
}
for (SGGSprite * sprite in self.children)
{
[sprite update:self.timeSinceLastUpdate];
}
//---Another One For The Player Note Theres A New Function Called updatePlayer---
for (SGGSprite * sprite in self.childrenPlayer)
{
[sprite updatePlayer:self.timeSinceLastUpdate rotationPlayer:_rotation];
}
}
#end
And Now For SGGSprite.m
//
// SGGSprite.m
// SimpleGLKitGame
//
// Created by Ray Wenderlich on 1/30/12.
// Copyright (c) 2012 __MyCompanyName__. All rights reserved.
//
#import "SGGSprite.h"
typedef struct {
CGPoint geometryVertex;
CGPoint textureVertex;
} TexturedVertex;
typedef struct {
TexturedVertex bl;
TexturedVertex br;
TexturedVertex tl;
TexturedVertex tr;
} TexturedQuad;
typedef struct {
CGPoint geometryVertexPlayer;
CGPoint textureVertexPlayer;
} TexturedVertexPlayer;
typedef struct {
TexturedVertexPlayer bl;
TexturedVertexPlayer br;
TexturedVertexPlayer tl;
TexturedVertexPlayer tr;
} TexturedQuadPlayer;
#interface SGGSprite()
#property (strong) GLKBaseEffect * effect;
#property (strong) GLKBaseEffect * effectPlayer; //Added
#property (assign) TexturedQuad quad;
#property (strong) GLKTextureInfo * textureInfo;
#property (assign) TexturedQuadPlayer quadPlayer; //Added
#property (strong) GLKTextureInfo * textureInfoPlayer; //Added
#end
#implementation SGGSprite
#synthesize position = _position;
#synthesize positionPlayer = _positionPlayer; //Added
#synthesize contentSize = _contentSize;
#synthesize contentSizePlayer = _contentSizePlayer; //Added
#synthesize effect = _effect;
#synthesize effectPlayer = _effectPlayer; //Added
#synthesize quad = _quad;
#synthesize quadPlayer = _quadPlayer; //Added
#synthesize textureInfo = _textureInfo;
#synthesize moveVelocity = _moveVelocity;
#synthesize textureInfoPlayer = _textureInfoPlayer; //Added
#synthesize moveVelocityPlayer = _moveVelocityPlayer; //Added
int moved = 0; // to make sure the game just started
- (id)initWithFile:(NSString *)fileName effect:(GLKBaseEffect *)effect
{
if ((self = [super init]))
{
self.effect = effect;
NSDictionary * options = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES],
GLKTextureLoaderOriginBottomLeft,
nil];
NSError * error;
NSString *path = [[NSBundle mainBundle] pathForResource:fileName ofType:nil];
self.textureInfo = [GLKTextureLoader textureWithContentsOfFile:path options:options error:&error];
if (self.textureInfo == nil)
{
NSLog(#"Error loading file: %#", [error localizedDescription]);
return nil;
}
self.contentSize = CGSizeMake(self.textureInfo.width, self.textureInfo.height);
TexturedQuad newQuad;
newQuad.bl.geometryVertex = CGPointMake(0, 0);
newQuad.br.geometryVertex = CGPointMake(self.textureInfo.width, 0);
newQuad.tl.geometryVertex = CGPointMake(0, self.textureInfo.height);
newQuad.tr.geometryVertex = CGPointMake(self.textureInfo.width, self.textureInfo.height);
newQuad.bl.textureVertex = CGPointMake(0, 0);
newQuad.br.textureVertex = CGPointMake(1, 0);
newQuad.tl.textureVertex = CGPointMake(0, 1);
newQuad.tr.textureVertex = CGPointMake(1, 1);
self.quad = newQuad;
}
return self;
}
//----------The Init Function For The Player Only-------------
- (id)initWithFilePlayer:(NSString *)fileName effectPlayer:(GLKBaseEffect *)effectPlayer
{
if ((self = [super init]))
{
self.effectPlayer = effectPlayer;
NSDictionary * options = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES],
GLKTextureLoaderOriginBottomLeft,
nil];
NSError * error;
NSString *path = [[NSBundle mainBundle] pathForResource:fileName ofType:nil];
self.textureInfoPlayer = [GLKTextureLoader textureWithContentsOfFile:path options:options error:&error];
if (self.textureInfoPlayer == nil)
{
NSLog(#"Error loading file: %#", [error localizedDescription]);
return nil;
}
self.contentSizePlayer = CGSizeMake(self.textureInfoPlayer.width, self.textureInfoPlayer.height);
TexturedQuadPlayer newQuad;
newQuad.bl.geometryVertexPlayer = CGPointMake(0, 0);
newQuad.br.geometryVertexPlayer = CGPointMake(self.textureInfoPlayer.width, 0);
newQuad.tl.geometryVertexPlayer = CGPointMake(0, self.textureInfoPlayer.height);
newQuad.tr.geometryVertexPlayer = CGPointMake(self.textureInfoPlayer.width, self.textureInfoPlayer.height);
newQuad.bl.textureVertexPlayer = CGPointMake(0, 0);
newQuad.br.textureVertexPlayer = CGPointMake(1, 0);
newQuad.tl.textureVertexPlayer = CGPointMake(0, 1);
newQuad.tr.textureVertexPlayer = CGPointMake(1, 1);
self.quadPlayer = newQuad;
}
return self;
}
- (GLKMatrix4) modelMatrix
{
GLKMatrix4 modelMatrix = GLKMatrix4Identity;
modelMatrix = GLKMatrix4Translate(modelMatrix, self.position.x, self.position.y, 0);
modelMatrix = GLKMatrix4Translate(modelMatrix, -self.contentSize.width/2, -self.contentSize.height/2, 0);
return modelMatrix;
}
//-------------Added For The Player-----------------
- (GLKMatrix4) modelMatrixPlayer
{
GLKMatrix4 modelMatrixPlayer = GLKMatrix4Identity;
modelMatrixPlayer = GLKMatrix4Translate(modelMatrixPlayer, self.positionPlayer.x, self.positionPlayer.y, 0);
modelMatrixPlayer = GLKMatrix4Translate(modelMatrixPlayer, -self.contentSizePlayer.width/2, -self.contentSizePlayer.height/2, 0);
return modelMatrixPlayer;
}
static inline double radians (double degrees) {return degrees * M_PI/180;}
- (void)render
{
self.effect.texture2d0.name = self.textureInfo.name;
self.effect.texture2d0.enabled = YES;
self.effect.transform.modelviewMatrix = self.modelMatrix;
[self.effect prepareToDraw];
long offset = (long)&_quad;
glEnableVertexAttribArray(GLKVertexAttribPosition);
glEnableVertexAttribArray(GLKVertexAttribTexCoord0);
glVertexAttribPointer(GLKVertexAttribPosition, 2, GL_FLOAT, GL_FALSE, sizeof(TexturedVertex), (void *) (offset + offsetof(TexturedVertex, geometryVertex)));
glVertexAttribPointer(GLKVertexAttribTexCoord0, 2, GL_FLOAT, GL_FALSE, sizeof(TexturedVertex), (void *) (offset + offsetof(TexturedVertex, textureVertex)));
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}
- (void)renderPlayer
{
self.effectPlayer.texture2d0.name = self.textureInfoPlayer.name;
self.effectPlayer.texture2d0.enabled = YES;
if (moved == 0) // Only Applied When You Restart Game To Position The Player In The Center
{
self.effectPlayer.transform.modelviewMatrix = self.modelMatrixPlayer;
}
[self.effectPlayer prepareToDraw];
long offset = (long)&_quadPlayer;
glEnableVertexAttribArray(GLKVertexAttribPosition);
glEnableVertexAttribArray(GLKVertexAttribTexCoord0);
glVertexAttribPointer(GLKVertexAttribPosition, 2, GL_FLOAT, GL_FALSE, sizeof(TexturedVertexPlayer), (void *) (offset + offsetof(TexturedVertexPlayer, geometryVertexPlayer)));
glVertexAttribPointer(GLKVertexAttribTexCoord0, 2, GL_FLOAT, GL_FALSE, sizeof(TexturedVertexPlayer), (void *) (offset + offsetof(TexturedVertexPlayer, textureVertexPlayer)));
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}
- (void)update:(float)dt
{
GLKVector2 curMove = GLKVector2MultiplyScalar(self.moveVelocity, dt);
self.position = GLKVector2Add(self.position, curMove);
}
// ------------- Rotate The Object --------------------------------------------
- (void)updatePlayer:(float)rt rotationPlayer:(float)rotate
{
moved = 1;
self.effectPlayer.transform.modelviewMatrix = self.modelMatrixPlayer; //Adjust the player Location And Ready To Move
//Moving The Object
GLKVector2 curRotate = GLKVector2MultiplyScalar(self.moveVelocityPlayer, rt);
self.positionPlayer = GLKVector2Add(self.positionPlayer, curRotate);
//Rotation Section
self.effectPlayer.transform.modelviewMatrix = GLKMatrix4Rotate(self.effectPlayer.transform.modelviewMatrix, rotate, 0, 0, -1);
}
#end
Well Thats Not Everything Here Is The SGGSprite.h
//
// SGGSprite.h
// SimpleGLKitGame
//
// Created by Ray Wenderlich on 1/30/12.
// Copyright (c) 2012 __MyCompanyName__. All rights reserved.
//
#import <GLKit/GLKit.h>
#import <Foundation/Foundation.h>
#interface SGGSprite : NSObject
#property (assign) GLKVector2 position;
#property (assign) GLKVector2 positionPlayer; //Position For The Player
#property (assign) CGSize contentSize;
#property (assign) CGSize contentSizePlayer; //Size Of The Player
#property (assign) GLKVector2 moveVelocity;
#property (assign) GLKVector2 moveVelocityPlayer; //Speed Of The Player If Moved
- (id)initWithFile:(NSString *)fileName effect:(GLKBaseEffect *)effect;
- (id)initWithFilePlayer:(NSString *)fileName effectPlayer:(GLKBaseEffect *)effectPlayer;
- (void)render;
- (void)renderPlayer;
- (void)update:(float)dt; //Move Object
- (void)updatePlayer:(float)rt rotationPlayer:(float)rotate; //Rotate Object
#end