I’m officially confused…(FIXED)

OK, I’m official confused now with an odd problem with Particle Designer. The rendering of particles is perfect as long as I apply a 90 degree rotation clockwise to the particle textures. Without that all the textures are rendered on their left side. THIS IS MADNESS.

I’ve debugged the living hell out of this and I just cannot see what is going wrong. I’ve used the OpenGL Profiler to check what the actual texture loaded onto the GPU looks like, and its fine. I’ve checked the vertex and texture coordinates and they are fine. I’ve even traced them using OpenGL Profiler to see what is being sent to to OpenGL and its ALL OK.

All this and still the textures render on their side. To fix the problem I am applying glRotate(90, 0, 0, 1) to glMatrixMode(GL_TEXTURE). This spins all textures 90 degrees to the right and everything is fine.

I could leave it like this, but I REALLY REALLY want to understand what is happening and its driving me mad.

My OpenGL setup is really simple

- (void)prepareOpenGL {

// Set the OGL context and make it current
[[self openGLContext] makeCurrentContext];

// Set the OGL clear color
glClearColor(0, 0, 0, 1);

// This controls how a texture is blended with other textures
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_ALPHA);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

glMatrixMode(GL_TEXTURE);
glLoadIdentity();
glRotatef(90, 0, 0, 1);

// Set up the OGL projection matrix
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, 320, 0, 480, -1, 1);
glViewport(0, 0, self.bounds.size.width, self.bounds.size.height);

// Set up the modelview matrix
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();

// Enable the OGL and client states we are going to need
glEnable(GL_TEXTURE_2D);
glEnable(GL_BLEND);

}

My Texture2D class looks like

- (GLuint)loadTextureFrom:(CGImageRef)image filter:(GLenum)aFilter {

// Grab the width and height of the image and also set the content size
width = CGImageGetWidth(image);
height = CGImageGetHeight(image);
contentSize = CGSizeMake(width, height);

// Even though NVidia can handle NPOT textures, ATI X1600 cards cannot, so we are making
// all textures POT to compensate for this.
NSUInteger pot; // Holds the power of 2 value being calculated

width = contentSize.width;
if((width != 1) && (width & (width – 1))) {
pot = 1;
while( pot < width)
pot *= 2;
width = pot;
}

height = contentSize.height;
if((height != 1) && (height & (height - 1))) {
pot = 1;
while(pot < height)
pot *= 2;
height = pot;
}

// Calculate the texture ratio
textureRatio.width = 1.0f / (float)width;
textureRatio.height = 1.0f / (float)height;

// Allocate storage for the image data
GLubyte *data = calloc(width * height * 4, sizeof(GLubyte));

// Create a color space that will be used when we render our image into the context
CGColorSpaceRef color_space = CGColorSpaceCreateDeviceRGB();

// We need to lock the current OGL context before creating a bitmap context to render to. Not
// doing so can cause the threads to mutex. The current context is held in glview so we use the
// appdelegate to grab glview
ParticleDesignerAppDelegate * appDelegate = [[NSApplication sharedApplication] delegate];

// Set the current openGL context
NSOpenGLContext *currentContext = [appDelegate.glview openGLContext];
CGLLockContext([currentContext CGLContextObj]);
[currentContext makeCurrentContext];

// Create a context into which we will render the image
CGContextRef context = CGBitmapContextCreate(
data,
width,
height,
8,
width * 4,
color_space,
kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host);

// Move the images origin and flip it so that it is not upside down
CGContextTranslateCTM(context, 0.0, height);
CGContextScaleCTM(context, 1.0, -1.0);

// Render the image to the context
CGContextDrawImage(context, CGRectMake(0, 0, width, height), image);

// Generate an OGL texture and copy the image data from our context into it
glGenTextures(1, &name);
glBindTexture(GL_TEXTURE_2D, name);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, data);

// Set up the texture parameters
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, aFilter);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, aFilter);
glBindTexture(GL_TEXTURE_2D, 0);

// Unlock the context now we have finished creating our texture
CGLUnlockContext([currentContext CGLContextObj]);

// Release the memory we have used
CFRelease(context);
CFRelease(color_space);
free(data);

DLog(@"INFO - Texture2D: Created texture name: %i", name);

// Return the name of the texture we have just created
return name;
}

The code for rendering images just uses glBegin/glEnd

glBegin(GL_QUADS);
// Bottom Left
glVertex2f(100, 0);
glTexCoord2f(0, 0);

// Bottom Right
glVertex2f(164, 0);
glTexCoord2f(1, 0);

// Top Right
glVertex2f(164, 64);
glTexCoord2f(1, 1);

// Top Left
glVertex2f(100, 64);
glTexCoord2f(0, 1);
glEnd();

This is just test code I'm using to make sure there are no problems with the vertex or texture coord data. That's it, I'm not doing anything else between creating the texture and rendering to the screen. So really not sure why my textures are on their side without the transform to the GL_TEXTURE matrix.

If anyone reading this has any insight or ideas all suggestions welcome.

Right, back to testing this all over again...

Mike

UPDATE: OK, the problem is fixed and I don't believe what the problem was. I burnt around 8+ hours looking at this problem and it took a great suggestion from my friend Rod Strougo (Creator of the excellent iPhone game Payload) to get it fixed.

So, what was the fix I hear you ask, well, that last piece of code above that sets up the vertex and texture information is wrong, but in a very subtle way. I needed to place the glTexCoord2f commands IN FRONT of the glVertex2f commands and that's it, nothing else, just that.......

I know OpenGL can be picky, but I would not have spotted that in a month of Sundays as I'm sure I've seen many examples the way I had it.

Still, the problem is now fixed and I can get back to getting Particle Designer 1.3 out to everyone.

Thanks Rod, I owe you a LARGE drink :o)

UPDATE 2: It turns out that when you use the glVertex* command, it uses what ever glTexCoord* has already been set. This is the same for items as well such as edge flag, normal and color. This means that these MUST be set before their corresponding glVertex*.

Checking the Internet, 99% of examples shows glTexCoord* being used first, so I must have been looking at/remembered a broken example.

Still, you live and learn and I hope this helps others. Now its working I can move onto using VBO's as glBegin/glEnd is really old school on OS X :o)

Mike

Share:
  • Digg
  • del.icio.us
  • Facebook
  • MySpace
  • Reddit
  • StumbleUpon
  • Technorati
  • TwitThis
  • Design Float
  • DZone
  • email
  • Google Bookmarks
  • LinkedIn
  • Scoopeo
  • Tumblr

12 Comments

Martin  on June 20th, 2010

I’ve had similar problems in the past and I know that debugging 3D code can make you go bananas after a while. :-) I don’t see any obvious problems with the code, but I’d suggest adding different colors to the four vertices to make sure that they are rendered absolutely, positively where you think they are. It might not help here, but I like to establish axioms to build on, when I am debugging, because I start questioning everything when I can’t find the bug…

mike  on June 20th, 2010

Thanks Martin, I like that and I’m so confused at the moment I am starting to doubt that I’m interpreting what I’m seeing correctly :o)

Mike

nexus6  on June 20th, 2010

Hi Mike,

I might be wrong but I thought openGL ES didn’t
support glBegin/glEnd.

mike  on June 20th, 2010

Ah, my bad, I should have said that this is the code in Particle Designer on the desktop under OS X :o)

Mike

nexus6  on June 20th, 2010

Mike,
is this 2D or 3D?
if 3d, i Don’t see GL_TEXTURE_3D enable being called

mike  on June 20th, 2010

Nope, just 2D as this is using a ported version of my particle emitter class from the iPhone.

mike  on June 20th, 2010

I’ve made an update to the post as it is now working. Check out the update for the solution.

Mike

Kurt  on June 20th, 2010

Mike,

Could you do all of us newbies a favor and explain why the solution is to reverse the statements?

I would guess that the problem had to do with the fact that OpenGL is a state machine and reversing the order of the statements fixed it because of some state that gets changed in one of them. Yet, you said that you’ve seen the code written in the “broken” way in the past. So what makes it “broken” in your situation, as opposed to others?

I’ve always found that the most frustrating problems I’ve encountered have also turned out to the some of the best teaching situations.

Thanks,
-Kurt

nexus6  on June 20th, 2010

wow! who would of thought a statement reversal would cause all of that!!

mike  on June 21st, 2010

I’ve added an update 2 that describes why changing the order of the glTexCoord and glVertex commands is important.

@Kurt, I agree and count not rest until I understood why this was important. The update should put some light on the reason :o)

Mike

Neogene  on June 21st, 2010

“THIS IS MADNESS”?

“THIS IS SPARTA”!!!

mike  on June 21st, 2010

@Neogene, LOL, very good ;o)

Leave a Comment