OpenGl Es Sprite render

Moderator
Posts: 3,579
Joined: 2003.06
Post: #16
It should be:

translate
rotate
scale
Quote this message in a reply
Member
Posts: 39
Joined: 2008.10
Post: #17
Hey, I have another couple of sprite related questions.

1) i am loading in currently only using one png and using it for multiple instances and i would like to have them randomly select a different png each.

They are loaded in like this:

Quote:// Creates a Core Graphics image from an image file
spriteImage = [UIImage imageNamed:@"Car2.png"].CGImage;
// Get the width and height of the image
width = CGImageGetWidth(spriteImage);
height = CGImageGetHeight(spriteImage);
// Texture dimensions must be a power of 2. If you write an application that allows users to supply an image,
// you'll want to add code that checks the dimensions and takes appropriate action if they are not a power of 2.

if(spriteImage) {
// Allocated memory needed for the bitmap context
spriteData = (GLubyte *) malloc(width * height * 4);
// Uses the bitmatp creation function provided by the Core Graphics framework.
spriteContext = CGBitmapContextCreate(spriteData, width, height, 8, width * 4, CGImageGetColorSpace(spriteImage), kCGImageAlphaPremultipliedLast);
// After you create the context, you can draw the sprite image to the context.
CGContextDrawImage(spriteContext, CGRectMake(0.0, 0.0, (CGFloat)width, (CGFloat)height), spriteImage);
// You don't need the context at this point, so you need to release it to avoid memory leaks.
CGContextRelease(spriteContext);

// Use OpenGL ES to generate a name for the texture.
glGenTextures(1, &spriteTexture);
// Bind the texture name.
glBindTexture(GL_TEXTURE_2D, spriteTexture);
// Speidfy a 2D texture image, provideing the a pointer to the image data in memory
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, spriteData);
// Release the image data
free(spriteData);

// Set the texture parameters to use a minifying filter and a linear filer (weighted average)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

// Enable use of the texture
glEnable(GL_TEXTURE_2D);
// Set a blending function to use
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
// Enable blending
glEnable(GL_BLEND);

how do i load in alternate pngs and render them depending on a variable stored in an array - x,y,vx,vy,pngName

I also need to load in various background elements and render them using the same GL_TRIANGLE_STRIP and i dont know how to define which png to render.


Thanks for your help, ive got some great responses from this site.
Quote this message in a reply
Moderator
Posts: 3,579
Joined: 2003.06
Post: #18
More pseudo-code for you to look at:

Code:
typedef struct
{
    GLuint        glTexName;
    GLfloat        width;
    GLfloat        height;
} Sprite;

Sprite    sprite1, sprite2;

void Init(void)
{
    LoadAPNG(@"Car1.png", &sprite1);
    LoadAPNG(@"Car2.png", &sprite2);
}

void LoadAPNG(NSString *fileName, Sprite *sprite)
{
    spriteImage = [UIImage imageNamed:fileName].CGImage;
    width = CGImageGetWidth(spriteImage);
    height = CGImageGetHeight(spriteImage);
    sprite->width = width;
    sprite->height = height;
    // more PNG loading into buffer blah blah ...
    
    GLuint    spriteTexture;
    glGenTextures(1, &spriteTexture);
    sprite->glTexName = spriteTexture;
    glBindTexture(GL_TEXTURE_2D, spriteTexture);
    glTexImage2D(...)
    // more GL loading blah blah ...
}

void DrawFrame(void)
{
    glVertexPointer(3, GL_FLOAT, 0, genericSpriteVerts);
    glTexCoordPointer(2, GL_FLOAT, 0, genericSpriteCoords);
    DrawSprite(sprite1, x, y);
    DrawSprite(sprite2, x, y);
}

void DrawSprite(Sprite sprite, GLfloat x, GLfloat y)
{
    glBindTexture(GL_TEXTURE_2D, sprite.glTexName); // <-- switch textures
    glPushMatrix();
    glTranslatef(x, y, 0.0f);
    glScalef(sprite.width, sprite.height, 1.0f);
    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
    glPopMatrix();
}
Quote this message in a reply
Member
Posts: 39
Joined: 2008.10
Post: #19
Very nice, that makes a lot of sense i will try it out soon.

Thanks again Smile
Quote this message in a reply
Member
Posts: 39
Joined: 2008.10
Post: #20
Hello again,

The problem i have now is that i want to store various objects data in an array such as width height maxspeed etc

also in this array i want to store the sprites name so i can simply have an object id and it will load in all the info i need to use.

All the sprite names are set up in an enum and then assigned sprites using texture2D this all works fine when im manually typing the sprite name like:

Code:
glBindTexture(GL_TEXTURE_2D, [_textures[kTexture_Car1 name]]);

but i as i said i want to pull the name from an array and i cannot get it to work, is there a certain variable type i need to pull the texture name into and then use it?

anyway, can someone help who knows a way to do this. I have everything setup in the array with all the data i need i now just need to get it to load the texture dynamically.

Thanks a lot for any help Smile
Quote this message in a reply
Moderator
Posts: 3,579
Joined: 2003.06
Post: #21
More pseudo-code:

Code:
enum
{
    CAR_1,
    CAR_2,
    CAR_3,
    NUM_SPRITES
};

typedef struct
{
    GLuint        glTexName;
    GLfloat        width;
    GLfloat        height;
    GLfloat        x;
    GLfloat        y;
    GLfloat        maxSpeed;
} Sprite;

Sprite    sprites[NUM_SPRITES];

void Init(void)
{
    MyInitSprite(CAR_1, @"Car1.png", CAR_1_START_X, CAR_1_START_Y, CAR_1_MAX_SPEED);
    MyInitSprite(CAR_2, @"Car2.png", CAR_2_START_X, CAR_2_START_Y, CAR_2_MAX_SPEED);
    MyInitSprite(CAR_3, @"Car3.png", CAR_3_START_X, CAR_3_START_Y, CAR_3_MAX_SPEED);
}

void MyInitSprite(int spriteIndex, NSString *fileName, GLfloat x, GLfloat y, GLfloat maxSpeed)
{
    LoadAPNG(fileName, &sprites[spriteIndex]);
    sprites[spriteIndex].x = x;
    sprites[spriteIndex].y = y;
    sprites[spriteIndex].maxSpeed = maxSpeed;
}

void DrawFrame(void)
{
    int        i;
    
    glVertexPointer(3, GL_FLOAT, 0, genericSpriteVerts);
    glTexCoordPointer(2, GL_FLOAT, 0, genericSpriteCoords);
    for (i = 0; i < NUM_SPRITES; i++)
    {
        DrawSprite(i);
    }
}

void DrawSprite(int spriteIndex)
{
    glBindTexture(GL_TEXTURE_2D, sprites[spriteIndex].glTexName);
    glPushMatrix();
    glTranslatef(sprites[spriteIndex].x, sprites[spriteIndex].y, 0.0f);
    glScalef(sprites[spriteIndex].width, sprites[spriteIndex].height, 1.0f);
    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
    glPopMatrix();
}
Quote this message in a reply
Sage
Posts: 1,232
Joined: 2002.10
Post: #22
While this is all good discussion, and I think it is worthwhile for anyone learning OpenGL to go through this (figuring out how transforms work), I want to point out that this approach is not very efficient.

If you want to draw a thousand quads, each with a position, size, and rotation, individually transforming and drawing them one at a time is about the worst way you can do it.

It would be much better to build a sprite array out of the attributes you want-- x,y, size, rotation, for example. Animate them according to your game logic. Then, at draw time, calculate the quad corners yourself into another vertex array, and draw that whole thing in one batch. This minimizes the function call overhead (skip 4 or 5 calls into GL per object), lets you optimize the math (you can do 2D rotations instead of full 3D), and lets you touch only the data that has actually changed (if some sprites haven't animated since the last frame, for example.) And the driver will be more efficient processing a large batch.
Quote this message in a reply
Moderator
Posts: 3,579
Joined: 2003.06
Post: #23
As always, thank you for your valuable input, arekkusu. Smile

Yes, what I've been describing is definitely not the most *efficient* approach, but it is the *easiest* approach (IMHO), and it works great for the general cases that I've seen over the years. Obviously, locally optimized transforms to batches of single arrays (VBOs) are preferable where possible, but they certainly are not required.

Correct me if I'm wrong about this, but my take is that for the general case, and especially for beginners, using the GL to do transforms for you, and not worrying too much about data locality, is fine. However, if you should find later on that more performance headroom is needed, then yes it is important to be aware that there is a more efficient way.
Quote this message in a reply
Sage
Posts: 1,232
Joined: 2002.10
Post: #24
Yes, there's certainly merit in having easy to understand, easy to write code.

I was just pointing out that there is another way to do this (with more code), which will perform better. The usual warnings about premature optimization still apply.
Quote this message in a reply
Member
Posts: 39
Joined: 2008.10
Post: #25
Hello again,

Thanks for all past help everything has been going really well with opengl so far.

I have come against another problem, i now have the cars able to jump over one another and it is working fine except one issue... they are not zsorted, i am moving each sprite on the z transform wise and it doesnt change how they are rendered, how can i have them rendered at the correct depth?

Thanks for any help
Quote this message in a reply
Moderator
Posts: 3,579
Joined: 2003.06
Post: #26
Do you have depth testing enabled?

Code:
glClear(GL_DEPTH_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);

[Edit] Also, do you have a depth buffer attached?
Quote this message in a reply
Member
Posts: 39
Joined: 2008.10
Post: #27
I tried adding that code where the gl is setup and now i get a grey screen.. any ideas what i need to do? is it the depth buffer you mention?
Quote this message in a reply
Moderator
Posts: 3,579
Joined: 2003.06
Post: #28
I don't know, but the code to attach a depth buffer should look something like:

Code:
glGenRenderbuffersOES(1, &depthRenderbuffer);
glBindRenderbufferOES(GL_RENDERBUFFER_OES, depthRenderbuffer);
glRenderbufferStorageOES(GL_RENDERBUFFER_OES, GL_DEPTH_COMPONENT16_OES, boundsWidth, boundsHeight);
glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_DEPTH_ATTACHMENT_OES, GL_RENDERBUFFER_OES, depthRenderbuffer);

If you don't have something like this and you're using OpenGL ES with depth testing turned on, I have no clue what could happen.
Quote this message in a reply
Moderator
Posts: 3,579
Joined: 2003.06
Post: #29
I should mention that you probably don't want your entire scene depth tested. For instance, if you have a gray background and it is being drawn while depth test is enabled, and its z value makes it closer to the view than the rest of the scene, all you'll see is gray, and may not immediately realize why.

So only do glEnable(GL_DEPTH_TEST) right before the stuff you want depth tested, and then disable it right afterwards with glDisable(GL_DEPTH_TEST).

The reason I bring this up is that it is a bit unusual to turn on depth testing during init and then leave it that way.

Also, you need to call glClear(GL_DEPTH_BUFFER_BIT); at the top of each frame, not at init, just like you'd do with glClear(GL_COLOR_BUFFER_BIT); In fact, you can or them together and clear them at the same time: glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
Quote this message in a reply
Member
Posts: 39
Joined: 2008.10
Post: #30
i tried the code just as you described and it still seems that the depth is not judge by the z position ive transformed each to.. could this be the depth buffer you mentioned? how do i find out if its setup and running?
Quote this message in a reply
Post Reply 

Possibly Related Threads...
Thread: Author Replies: Views: Last Post
  Modfied OpenGL Sprite Rendering tonyb 3 3,053 Jul 26, 2009 07:30 AM
Last Post: tonyb
  For 2D (sprite) games, do I use OpenGL or something else? lindsay 24 12,566 May 9, 2009 12:06 PM
Last Post: Weltevrede
  OpenGL Animated Sprite Question Aboqa 6 6,148 May 6, 2009 11:17 AM
Last Post: AnotherJake
  OpenGL ES sprite alpha Holmes 27 15,515 Mar 16, 2009 01:06 PM
Last Post: Holmes
  OpenGL render loop - NSTimer vs rendering thread smallstepforman 27 24,427 Feb 2, 2009 10:22 AM
Last Post: ThemsAllTook