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 am trying to draw only part of my sprite sheet. But it just scales the image and no drawing 1/3 of the width of the image. How can I crop the image so only 1/3 shows. Text is below ( ios 7,opengl es 2)- (GLKMatrix4) modelMatrix {
GLKMatrix4 modelMatrix = GLKMatrix4Identity;
modelMatrix = GLKMatrix4Translate(modelMatrix, x, y, 0);
return modelMatrix;
}
- (void)setSprite:(NSString *)fileName effect:(GLKBaseEffect *)newEffect {
// 1
self.effect = newEffect;
// 2
NSDictionary * options = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES],
GLKTextureLoaderOriginBottomLeft,
nil];
// 3
NSError * error;
NSString *path = [[NSBundle mainBundle] pathForResource:fileName ofType:nil];
// 4
self.textureInfo = [GLKTextureLoader textureWithContentsOfFile:path options:options error:&error];
if (self.textureInfo == nil) {
NSLog(#"Error loading file: %#", [error localizedDescription]);
return ;
}
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, 1);
newQuad.br.textureVertex = CGPointMake(1, 1);
newQuad.tl.textureVertex = CGPointMake(0, 0);
newQuad.tr.textureVertex = CGPointMake(1, 0);
self.quad = newQuad;
}
- (void)render {
// 1
self.effect.texture2d0.name = self.textureInfo.name;
self.effect.texture2d0.enabled = YES;
// 2
y++;
self.effect.transform.modelviewMatrix = self.modelMatrix;
[self.effect prepareToDraw];
// 3
glEnableVertexAttribArray(GLKVertexAttribPosition);
glEnableVertexAttribArray(GLKVertexAttribTexCoord0);
// 4
TexturedQuad q;
q.bl.textureVertex = CGPointMake(0, 1);
q.br.textureVertex = CGPointMake(1, 1);
q.tl.textureVertex = CGPointMake(0, 0);
q.tr.textureVertex = CGPointMake(1, 0);
q.bl.geometryVertex = CGPointMake(0, 0);
q.br.geometryVertex = CGPointMake(self.textureInfo.width/3, 0);
q.tl.geometryVertex = CGPointMake(0, self.textureInfo.height);
q.tr.geometryVertex = CGPointMake(self.textureInfo.width/3, self.textureInfo.height);
long offset2 =(long)&q;
//long offset = (long)&_quad;
glVertexAttribPointer(GLKVertexAttribPosition, 2, GL_FLOAT, GL_FALSE, sizeof(TexturedVertex), (void *) (offset2 + offsetof(TexturedVertex, geometryVertex)));
glVertexAttribPointer(GLKVertexAttribTexCoord0, 2, GL_FLOAT, GL_FALSE, sizeof(TexturedVertex), (void *) (offset2 + offsetof(TexturedVertex, textureVertex)));
// 5
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}
Update:
TexturedQuad newQuad;
newQuad.bl.geometryVertex = CGPointMake(0, 0);
newQuad.br.geometryVertex = CGPointMake(self.textureInfo.width/3, 0);
newQuad.tl.geometryVertex = CGPointMake(0, self.textureInfo.height);
newQuad.tr.geometryVertex = CGPointMake(self.textureInfo.width/3, self.textureInfo.height);
newQuad.bl.textureVertex = CGPointMake(0, 1);
newQuad.br.textureVertex = CGPointMake(.3, 1);
newQuad.tl.textureVertex = CGPointMake(0, 0);
newQuad.tr.textureVertex = CGPointMake(.3, 0);
That is what texture coordinates are for. Your 'textureVertex' is specifying the full dimensions of your texture (with 0.0 to 1.0) mapped to the quad. You need to change some of those values to either 1/3 or 2/3. For example:
newQuad.bl.textureVertex = CGPointMake(0, 0.33);
newQuad.br.textureVertex = CGPointMake(1, 1);
newQuad.tl.textureVertex = CGPointMake(0, 0.33);
newQuad.tr.textureVertex = CGPointMake(1, 0);
I am using IOS 6.1, trying to draw a texture and a vertex buffer object.
I setup a texture like this:
typedef struct{
CGPoint geometryVertex;
CGPoint textureVertex;
} TexturedVertex;
typedef struct {
TexturedVertex bl;
TexturedVertex br;
TexturedVertex tl;
TexturedVertex tr;
} TexturedQuad;
NSError *error;
NSString *path = [[NSBundle mainBundle] pathForResource:#"img.png" ofType:nil];
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithBool:YES], GLKTextureLoaderOriginBottomLeft, nil];
self.textureInfo = [GLKTextureLoader textureWithContentsOfFile:path options:options error:&error];
TexturedQuad quad;
quad.bl.geometryVertex = CGPointMake(0, 0);
quad.br.geometryVertex = CGPointMake(self.textureInfo.width, 0);
quad.tl.geometryVertex = CGPointMake(0, self.textureInfo.height);
quad.tr.geometryVertex = CGPointMake(self.textureInfo.width, self.textureInfo.height);
quad.bl.textureVertex = CGPointMake(0, 0);
quad.br.textureVertex = CGPointMake(1, 0);
quad.tl.textureVertex = CGPointMake(0, 1);
quad.tr.textureVertex = CGPointMake(1, 1);
self.quad = quad;
glClearColor(0, 0, 0, 0.5);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnableVertexAttribArray(GLKVertexAttribPosition);
glEnableVertexAttribArray(GLKVertexAttribTexCoord0);
Then draw it like this:
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect
{
glClear(GL_COLOR_BUFFER_BIT);
self.baseEffect.texture2d0.name = self.textureInfo.name;
self.baseEffect.transform.modelviewMatrix = [self modelMatrix];
[self.baseEffect prepareToDraw];
long offset = (long)&_quad;
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);
}
That all works, no problem. What I want to do next though is draw another object stored in a vertex buffer object that isn't a texture. So I add this to the setup code:
typedef struct {
float Position[3];
} Vertex;
const Vertex Vertices[] = {
{100, -100, 0},
{100, 100, 0},
{-100, 100, 0},
{-100, -100, 0}
};
GLuint _vertexBuffer;
glGenBuffers(1, &_vertexBuffer); // (1)
glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer); // (2)
glBufferData(GL_ARRAY_BUFFER, sizeof(Vertices), Vertices, GL_STATIC_DRAW); // (3)
But even without trying to draw the new object, those lines crash my app. I have found that with just line (1) it doesn't crash. With (1) + (2) it still doesn't crash but profiling with Instruments tells me the draw call for the texture 'Exceeded array buffer bounds' and used 'Uninitialized buffer data', although it still draws the texture just fine. Adding line (3) causes the app to crash and Instruments to tell me there is no GL data.
Does anyone know why this is happening?
The VBO was apparently interfering with the call to glVertexAttribPointer. Unbinding it like so stopped the crash:
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect
{
glClear(GL_COLOR_BUFFER_BIT);
self.baseEffect.texture2d0.name = self.textureInfo.name;
self.baseEffect.transform.modelviewMatrix = [self modelMatrix];
[self.baseEffect prepareToDraw];
glBindBuffer(GL_ARRAY_BUFFER, 0);
long offset = (long)&_quad;
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);
}
The texture as it is doesn't use a VBO so there shouldn't be one bound when the glVertexAttribPointer call is made.
I am displaying 3 objects with the help of GLKit. However, when I am applying textures to these objects, only one texture is being used for all three.
The code I am using is as follows:
- (void)setUpGL{
NSLog(#"i : %d, %d, %d",i,j,k);
firstPlayerScore = 0;
secondPlayerScore = 0;
staticBall = YES;
isSecondPlayer = NO;
self.boxPhysicsObjects = [NSMutableArray array];
self.spherePhysicsObjects = [NSMutableArray array];
self.immovableBoxPhysicsObjects = [NSMutableArray array];
self.cylinderPhysicsObjects = [NSMutableArray array];
self.secondPlayerCylinderPhysicsObjects = [NSMutableArray array];
self.sphereArray = [NSMutableArray array];
GLKView *view = (GLKView *)self.view;
NSAssert([view isKindOfClass:[GLKView class]],#"View controller's view is not a GLKView");
view.drawableDepthFormat = GLKViewDrawableDepthFormat16;
view.context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
[EAGLContext setCurrentContext:view.context];
self.baseEffect = [[GLKBaseEffect alloc] init];
glEnable(GL_CULL_FACE);
glEnable(GL_DEPTH_TEST);
//glGenBuffers(1, &_vertexBuffer);
//glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer);
//glBufferData(GL_ARRAY_BUFFER, (i+j)*sizeof(float), sphereVerts, GL_STATIC_DRAW);
glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
self.baseEffect.light0.enabled = GL_TRUE;
self.baseEffect.light0.ambientColor = GLKVector4Make(0.7f, 0.7f, 0.7f, 1.0f);
[self addImmovableBoxPhysicsObjects];
[self addRandomPhysicsSphereObject];
//[self addFirstPlayerCylinderObject];
//[self addSecondPlayerCylinderObject];
//[self scheduleAddRandomPhysicsSphereObject:nil];
}
- (void)addRandomPhysicsObject{
if(random() % 2 == 0)
{
[self addRandomPhysicsBoxObject];
}
else
{
[self addRandomPhysicsSphereObject];
}
}
- (void)setUpBox{
CGImageRef image = [[UIImage imageNamed:#"outUV2.PNG"] CGImage];
textureInfo1 = [GLKTextureLoader textureWithCGImage:image options:nil error:NULL];
self.baseEffect.texture2d0.name = textureInfo1.name;
self.baseEffect.texture2d0.enabled = YES;
glEnableVertexAttribArray(GLKVertexAttribPosition);
glVertexAttribPointer( GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), final_meshVerts);
glEnableVertexAttribArray(GLKVertexAttribNormal);
glVertexAttribPointer(GLKVertexAttribNormal, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), final_meshNormals);
glEnableVertexAttribArray(GLKVertexAttribTexCoord0);
glVertexAttribPointer(GLKVertexAttribTexCoord0, 2, GL_FLOAT, GL_FALSE, 2*sizeof(float), final_meshTexCoords);
//glDisableVertexAttribArray(GLKVertexAttribTexCoord0);
}
- (void)drawPhysicsBoxObjects{
//self.baseEffect.texture2d0.target = textureInfo1.target;
PAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
GLKMatrix4 savedModelviewMatrix = self.baseEffect.transform.modelviewMatrix;
for(PPhysicsObject *currentObject in self.boxPhysicsObjects){
self.baseEffect.transform.modelviewMatrix =
GLKMatrix4Multiply(savedModelviewMatrix,[appDelegate physicsTransformForObject:currentObject]);
[self.baseEffect prepareToDraw];
glDrawArrays(GL_TRIANGLES, 0, final_meshNumVerts);
}
self.baseEffect.light0.diffuseColor = GLKVector4Make(1.0f, 1.0f, 1.0f, 1.0f);// Alpha
for(PPhysicsObject *currentObject in self.immovableBoxPhysicsObjects){
self.baseEffect.transform.modelviewMatrix = GLKMatrix4Multiply(savedModelviewMatrix, [appDelegate physicsTransformForObject:currentObject]);
[self.baseEffect prepareToDraw];
glDrawArrays(GL_TRIANGLES,0, final_meshNumVerts);
}
self.baseEffect.transform.modelviewMatrix = savedModelviewMatrix;
}
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect{
static float a = 0;
a = a+0.1;
//NSLog(#"a : %f",a);
self.baseEffect.transform.modelviewMatrix = GLKMatrix4MakeLookAt(
0, 9.8, 10.0, // Eye position
0.0, 1.0, 0.0, // Look-at position
0.0, 1.0, 0.0); // Up direction
const GLfloat aspectRatio = (GLfloat)view.drawableWidth / (GLfloat)view.drawableHeight;
self.baseEffect.transform.projectionMatrix =
GLKMatrix4MakePerspective(GLKMathDegreesToRadians(35.0f),aspectRatio,0.2f,200.0f); // Far arbitrarily far enough to contain scene
self.baseEffect.light0.position = GLKVector4Make(0.6f, 1.0f, 0.4f, 0.0f);
[self.baseEffect prepareToDraw];
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
[self drawPhysicsSphereObjects];
[self drawPhysicsBoxObjects];
//[self drawPhysicsCylinderObjects];
}
- (void)addRandomPhysicsSphereObject{
PAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
PPhysicsObject *anObject = nil;
if([self.spherePhysicsObjects count] < PMAX_NUMBER_BLOCKS)
{
NSLog(#"if");
anObject = [[PPhysicsObject alloc] init];
}
else
{
NSLog(#"else");
anObject = [self.spherePhysicsObjects objectAtIndex:0];
[self.spherePhysicsObjects removeObjectAtIndex:0];
}
[self.spherePhysicsObjects addObject:anObject];
[appDelegate physicsRegisterSphereObject:anObject
position:GLKVector3Make(0,3.5,-2)
mass:0.0f];
[self setUpSphere];
/*[appDelegate physicsSetVelocity:GLKVector3Make(
random() / (float)RAND_MAX * 2.0f - 1.0f,
0.0f,
random() /(float)RAND_MAX * 2.0f - 1.0f)
forObject:anObject];*/
}
- (void)setUpSphere{
CGImageRef image = [[UIImage imageNamed:#"basketball.png"] CGImage];
textureInfo = [GLKTextureLoader textureWithCGImage:image options:nil error:NULL];
self.baseEffect.texture2d0.name = textureInfo.name;
self.baseEffect.texture2d0.enabled = YES;
glEnableVertexAttribArray( GLKVertexAttribPosition);
glVertexAttribPointer( GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), newbasketballVerts);
glEnableVertexAttribArray(GLKVertexAttribNormal);
glVertexAttribPointer(GLKVertexAttribNormal, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), newbasketballNormals);
glEnableVertexAttribArray(GLKVertexAttribTexCoord0);
glVertexAttribPointer(GLKVertexAttribTexCoord0, 2, GL_FLOAT, GL_FALSE, 2*sizeof(float), newbasketballTexCoords);
//glDisableVertexAttribArray(GLKVertexAttribTexCoord0);
}
- (void)drawPhysicsSphereObjects{
NSLog(#"draw");
/*static int x = 1;
if (x>20) {
x=20;
}
matrix = GLKMatrix4Identity;
matrix = GLKMatrix4MakeTranslation(0.1 * (x++), 0.0, 0.0);*/
//self.baseEffect.texture2d0.target = textureInfo2.target;
PAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
GLKMatrix4 savedModelviewMatrix = self.baseEffect.transform.modelviewMatrix;
/*CGImageRef image = [[UIImage imageNamed:#"basketball.png"] CGImage];
GLKTextureInfo *textureInfo = [GLKTextureLoader textureWithCGImage:image options:nil error:NULL];
self.baseEffect.texture2d0.name = textureInfo.name;
self.baseEffect.texture2d0.target = textureInfo.target;*/
self.baseEffect.light0.diffuseColor = GLKVector4Make(1.0f, 1.0f, 1.0f, 1.0f);
//glVertexPointer(3, GL_FLOAT, 0, sphereVerts);
//glNormalPointer(GL_FLOAT, 0, sphereNormals);
//glTexCoordPointer(2, GL_FLOAT, 0, final meshTexCoords);
/*glGenBuffers(1, &ballVertexBuffer);
glBindBuffer(GL_ARRAY_BUFFER, ballVertexBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(MeshVertexData), MeshVertexData, GL_STATIC_DRAW);
glEnableVertexAttribArray(GLKVertexAttribPosition);
glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, sizeof(arrowVertexData), 0);
glEnableVertexAttribArray(GLKVertexAttribNormal);
glVertexAttribPointer(GLKVertexAttribNormal, 3, GL_FLOAT, GL_TRUE, sizeof(arrowVertexData), (void *)offsetof(arrowVertexData, normal));
glBindVertexArrayOES(arrowVertexArray);*/
//glEnableVertexAttribArray(GLKVertexAttribTexCoord0);
//glVertexAttribPointer(GLKVertexAttribTexCoord0, 2, GL_FLOAT, GL_FALSE, 2*sizeof(float), newbasketballTexCoords);
if (!isSecondPlayer) {
for(PPhysicsObject *currentObject in self.spherePhysicsObjects)
{NSLog(#"first");
self.baseEffect.transform.modelviewMatrix =
GLKMatrix4Multiply(savedModelviewMatrix, [appDelegate physicsTransformForObject:currentObject]);
[self.baseEffect prepareToDraw];
glDrawArrays(GL_TRIANGLES, 0, newbasketballNumVerts);
//glDrawArrays(GL_TRIANGLES, 0, sizeof(MeshVertexData) / sizeof(arrowVertexData));
}
}
else{
for(PPhysicsObject *currentObject in self.secondSpherePhysicsObjects)
{
self.baseEffect.transform.modelviewMatrix =
GLKMatrix4Multiply(savedModelviewMatrix, [appDelegate physicsTransformForObject:currentObject]);
[self.baseEffect prepareToDraw];
glDrawArrays(GL_TRIANGLES, 0, newbasketballNumVerts);
//glDrawArrays(GL_TRIANGLES, 0, sizeof(MeshVertexData) / sizeof(arrowVertexData));
}
}
//glBindBuffer(GL_ARRAY_BUFFER, 0);
//glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
//glDisableVertexAttribArray(GLKVertexAttribTexCoord0);
self.baseEffect.transform.modelviewMatrix = savedModelviewMatrix;
}
Why is this only using one texture for all three, and not three different textures, one for each object? How can I fix this?
I had achieved a scene that the moon around the earth moving. different textures for the earth and the moon. under GLKit frame, the code just like this:
-(void)viewDidLoad
{
//......
// Setup Earth texture
CGImageRef earthImageRef =
[[UIImage imageNamed:#"Earth512x256.jpg"] CGImage];
earthTextureInfo = [GLKTextureLoader
textureWithCGImage:earthImageRef
options:[NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES],
GLKTextureLoaderOriginBottomLeft, nil nil]
error:NULL];
// Setup Moon texture
CGImageRef moonImageRef =
[[UIImage imageNamed:#"Moon256x128.png"] CGImage];
moonTextureInfo = [GLKTextureLoader
textureWithCGImage:moonImageRef
options:[NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES],
GLKTextureLoaderOriginBottomLeft, nil nil]
error:NULL];
//......
}
then, draw earth and moon.
- (void)drawEarth
{
//setup texture
self.baseEffect.texture2d0.name = earthTextureInfo.name;
self.baseEffect.texture2d0.target = earthTextureInfo.target;
//
GLKMatrixStackPush(self.modelviewMatrixStack);
GLKMatrixStackRotate( // Rotate (tilt Earth's axis)
self.modelviewMatrixStack,
GLKMathDegreesToRadians(SceneEarthAxialTiltDeg),
1.0, 0.0, 0.0);
GLKMatrixStackRotate( // Rotate about Earth's axis
self.modelviewMatrixStack,
GLKMathDegreesToRadians(earthRotationAngleDegrees),
0.0, 1.0, 0.0);
self.baseEffect.transform.modelviewMatrix =
GLKMatrixStackGetMatrix4(self.modelviewMatrixStack);
//draw earth
[self.baseEffect prepareToDraw];
glBindVertexArrayOES(_vertexArray);
glDrawArrays(GL_TRIANGLES, 0, sphereNumVerts);
//pop
GLKMatrixStackPop(self.modelviewMatrixStack);
self.baseEffect.transform.modelviewMatrix =
GLKMatrixStackGetMatrix4(self.modelviewMatrixStack);
}
- (void)drawMoon
{
self.baseEffect.texture2d0.name = moonTextureInfo.name;
self.baseEffect.texture2d0.target = moonTextureInfo.target;
GLKMatrixStackPush(self.modelviewMatrixStack);
GLKMatrixStackRotate( // Rotate to position in orbit
self.modelviewMatrixStack,
GLKMathDegreesToRadians(moonRotationAngleDegrees),
0.0, 1.0, 0.0);
GLKMatrixStackTranslate(// Translate to distance from Earth
self.modelviewMatrixStack,
0.0, 0.0, SceneMoonDistanceFromEarth);
GLKMatrixStackScale( // Scale to size of Moon
self.modelviewMatrixStack,
SceneMoonRadiusFractionOfEarth,
SceneMoonRadiusFractionOfEarth,
SceneMoonRadiusFractionOfEarth);
GLKMatrixStackRotate( // Rotate Moon on its own axis
self.modelviewMatrixStack,
GLKMathDegreesToRadians(moonRotationAngleDegrees),
0.0, 1.0, 0.0);
//
self.baseEffect.transform.modelviewMatrix =
GLKMatrixStackGetMatrix4(self.modelviewMatrixStack);
//draw moon
[self.baseEffect prepareToDraw];
glBindVertexArrayOES(_vertexArray);
glDrawArrays(GL_TRIANGLES, 0, sphereNumVerts);
GLKMatrixStackPop(self.modelviewMatrixStack);
self.baseEffect.transform.modelviewMatrix =
GLKMatrixStackGetMatrix4(self.modelviewMatrixStack);
}
To do multiple textures you will need to do:
effect.texture2d0.name = firstTexture.name;
[effect prepareToDraw];
[self renderFirstObject];
effect.texture2d0.name = secondTexture.name;
[effect prepareToDraw];
[self renderSecondObject];
or something similar. If you have lots of objects, I recommend using texture atlases and then doing batch rendering using:
glDrawElements(GL_TRIANGLES, totalIndicies, GL_UNSIGNED_SHORT, indices);
I tried to use glDrawArray for every single object and the framerate of my app dipped to like 10fps.
In your code, the reason it was using 1 texture for all objects is because you never changed the effect.texture2d0.name to the texture you need before each object. If I were to change your code it would be:
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect{
static float a = 0;
a = a+0.1;
//NSLog(#"a : %f",a);
self.baseEffect.transform.modelviewMatrix = GLKMatrix4MakeLookAt(
0, 9.8, 10.0, // Eye position
0.0, 1.0, 0.0, // Look-at position
0.0, 1.0, 0.0); // Up direction
const GLfloat aspectRatio = (GLfloat)view.drawableWidth / (GLfloat)view.drawableHeight;
self.baseEffect.transform.projectionMatrix =
GLKMatrix4MakePerspective(GLKMathDegreesToRadians(35.0f),aspectRatio,0.2f,200.0f); // Far arbitrarily far enough to contain scene
self.baseEffect.light0.position = GLKVector4Make(0.6f, 1.0f, 0.4f, 0.0f);
[self.baseEffect prepareToDraw];
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
self.baseEffect.texture2d0.name = textureInfo.name;
[self.baseEffect prepareToRender];
[self drawPhysicsSphereObjects];
self.baseEffect.texture2d0.name = textureInfo1.name;
[self.baseEffect prepareToRender];
[self drawPhysicsBoxObjects];
//[self drawPhysicsCylinderObjects];
}
Of course this is simplifying it, and without the vertex attribute array setup.
One thing i did for this problem is that i made one single image with all textures in it... now i give only one texture to my GLKBaseEffect object.
But if any person have answer for multiple objects with multiple textures with the help of GLKit, please let me know...
Thank You.
One solution would be to separate your drawing calls so that first you draw all objects that use texture A, then all objects that use texture B and so on.
There is also the texture atlas alternative described here: https://stackoverflow.com/a/8230592/64167.
I am playing around with learning more OpenGL ES and I may have a way to do this.
In my case I have N quads, each with an individual texture. In [view drawInRect] for each quad I want to draw I set new texture properties on baseEffect before I draw each quad, then call prepareToDraw on the BaseEffect and the quad, then render the quad.
Here is some pseudocode for what I mean:
for (int i = 0; i < quads.count; i++) {
baseEffect.texture2d0.name = textureInfo[i].name;
baseEffect.texture2d0.target = textureInfo[i].target;
[baseEffect prepareToDraw];
[quads[i] prepareToDraw];
glDrawArrays(GL_TRIANGLES, 0, 4);
}
This is working ok for me so far.
I use Xcode 4.4 and I develope app for iOS 5.1 with ARC. I have a thread (other than main) that invokes periodically method below.
- (void)updateLabels:(NSTimeInterval)timeSinceLastUpdate
{
int lastTime = self.time;
self.scoreScale -= ((self.scoreScale-1)/5);
self.time -= timeSinceLastUpdate;
if (self.time <= 0) {
self.time = 0.0;
}
if (lastTime != (int) self.time) {
self.timeChanged = YES;
}
#autoreleasepool {
NSString *timeString = [NSString stringWithFormat:#"%.2d:%.2d",(int)(self.time/60),((int)self.time%60)]; //leak!!!
if (self.scoreScale <=1) self.scoreScale = 1;
GLKBaseEffect *scoreValueEffect = [self.sprites objectForKey:#"score_value"];
if (self.scoreChanged) {
scoreValueEffect = [[ILUtils sharedInstance] makeEffectWithString:[NSString stringWithFormat:#"%#",self.score] alignment:UITextAlignmentLeft fontName:#"WetLetters EFN" fontSize:20 withViewSize:self.view.bounds.size withRect:CGRectMake(935, 50, 100, 30)];
}
scoreValueEffect.transform.modelviewMatrix = [[ILUtils sharedInstance] setupSpriteModelviewMatrixWithViewRect:CGRectMake(955, 46-(30*self.scoreScale-30)/2, 100*self.scoreScale, 30*self.scoreScale)];
[self.sprites setObject:scoreValueEffect forKey:#"score_value"];
if (self.timeChanged) {
GLKBaseEffect *timeValueEffect = [[ILUtils sharedInstance] makeEffectWithString:timeString alignment:UITextAlignmentLeft fontName:#"WetLetters EFN" fontSize:24 withViewSize:self.view.bounds.size withRect:CGRectMake(955, 75, 100, 30)];
[self.sprites setObject:timeValueEffect forKey:#"time_value"];
}
}
}
When I use Instruments I can see that memory usage increases in time. When I comment out line creating string memory usage is stable. I also tried to create string with alloc & init methods, but it didn't helped. I've found similiar thread in https://stackoverflow.com/questions/ask?title=#autoreleasepool%20not%20working but enabling/disabling NSZombieEnabled option makes no difference.
I can't figure out why all these strings are not deallocated though I use #autorelease block as said in https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmAutoreleasePools.html.
Did I miss something?
Thanks.
//EDIT
Stuff invoked by makeEffectWithString:
- (GLKBaseEffect*)makeEffectWithString:(NSString*)string alignment:(UITextAlignment)alignment fontName:(NSString*)name fontSize:(CGFloat)size withViewSize:(CGSize)viewSize withRect:(CGRect)rect
{
GLKBaseEffect *effect = [[GLKBaseEffect alloc] init];
effect.texture2d0.enabled = YES;
effect.texture2d0.name = [self textureWithString:string dimensions:rect.size alignment:alignment fontName:name fontSize:size].name;
effect.transform.projectionMatrix = [self setupOrthoProjectionMatrixWithViewSize:viewSize];
effect.transform.modelviewMatrix = [[ILUtils sharedInstance] setupSpriteModelviewMatrixWithViewRect:rect];
effect.useConstantColor = YES;
effect.constantColor = GLKVector4Make(0.7f,0.7f,0.7f,0.7f);
return effect;
}
- (GLKTextureInfo*)textureWithString:(NSString*)string dimensions:(CGSize)dimensions alignment:(UITextAlignment)alignment fontName:(NSString*)name fontSize:(CGFloat)size
{
UIFont *font = [UIFont fontWithName:name size:size];
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceGray();
CGContextRef context = CGBitmapContextCreate(nil, dimensions.width, dimensions.height, 8, dimensions.width, colorSpace, kCGImageAlphaOnly);
CGColorSpaceRelease(colorSpace);
CGContextSetGrayFillColor(context, 1.0, 1.0);
UIGraphicsPushContext(context);
[string drawInRect:CGRectMake(0, 0, dimensions.width, dimensions.height) withFont:font lineBreakMode:UILineBreakModeWordWrap alignment:alignment];
UIGraphicsPopContext();
NSMutableDictionary *options = [NSMutableDictionary dictionaryWithObject:[NSNumber numberWithBool:YES] forKey:GLKTextureLoaderOriginBottomLeft];
GLKTextureInfo *texture = [GLKTextureLoader textureWithCGImage:CGBitmapContextCreateImage(context) options:options error:nil];
CGContextRelease(context);
return texture;
}
//EDIT
- (void)drawSpriteUsingEffect:(GLKBaseEffect*)effect
{
glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer);
glEnableVertexAttribArray(GLKVertexAttribPosition);
glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, 20, BUFFER_OFFSET(0));
glEnableVertexAttribArray(GLKVertexAttribTexCoord0);
glVertexAttribPointer(GLKVertexAttribTexCoord0, 2, GL_FLOAT, GL_FALSE, 20, BUFFER_OFFSET(12));
[effect prepareToDraw];
glDrawArrays(GL_TRIANGLES, 0, 6);
}
You can use the Allocations instruments to "Mark Heap". This seems to have some instructions on how to debug such memory issues.