views:

190

answers:

5

I don't understand the concept of showing sprites mapped into OpenGL Triangles.

If OpenGL ES only draws triangles and points, How do you display/map non-triangular shapes?

Why would a shape mapped to a triangle not be distorted?

To explain:

In my mind, mapping, say, a sprite of Mario, to a triangle would produce a distorted or cropped Mario. In such a case, the head would be squished or invisible.

A: 

You could use transparent pixels.

http://stackoverflow.com/questions/611219/in-opengl-es-how-do-i-load-a-texture-that-has-transparent-pixels

Zen
How does that work with collision detection? Am I drawing on to a larger-than-image triangle? If yes, the corners would trigger a collision, no?
Moshe
Would you please show a diagram or photo to explain your answer?
Moshe
A: 

openGL can actually draw polygons, but it's usually safer not to use them, specially in complex shapes. Polygons can "twist" on themselves and make an invalid shape that can't be correctly rendered. With triangles, this never happens. That's why people usually break up complex shapes to triangles.

See http://www.glprogramming.com/red/chapter02.html#name2 for a better, more complete explanation.

Mo
I'm discussing OpenGL ES, not OpenGL. The two are not the same. OpenGL ES can not handle polygons.
Moshe
+1  A: 

The reason is in the rendering pipeline at the lowest level it is all triangles. More complicated shapes like polygons and the like are transformed into a collection of triangles through a process called tessellation.

So if you want to model your complicated Mario character the trick is actually to make him into a collection of triangles, not a single one.

I see in another answer you mentioned transparent pixels. I have not used the newest versions of openGL but the traditional definition a collision is based on intersecting the surface and the texture, even a transparent one, was not taken into account.

EDIT:

A quick google turned up a solution using a GL_TRIANGLE_STRIP off of iDevGames.com The Url is http://www.idevgames.com/forum/showpost.php?p=143983&postcount=6

Here is the relevant draw method:

- (void)drawView {

    // Replace the implementation of this method to do your own custom drawing

    const GLfloat squareVertices[] = {
        -0.5f, -0.5f,
        0.5f,  -0.5f,
        -0.5f,  0.5f,
        0.5f,   0.5f,
    };

    GLshort genericTexCoords[] = { 0, 1, 1, 1, 0, 0, 1, 0 };

    [EAGLContext setCurrentContext:context];

    glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer);
    glViewport(0, 0, backingWidth, backingHeight);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrthof(-1.0f, 1.0f, -1.5f, 1.5f, -1.0f, 1.0f);
    glMatrixMode(GL_MODELVIEW);
    glRotatef(3.0f, 0.0f, 0.0f, 1.0f);

    glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);

    glVertexPointer(2, GL_FLOAT, 0, squareVertices);
    glTexCoordPointer(2, GL_SHORT, 0, genericTexCoords);

    glEnable(GL_BLEND);
    glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);

    glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, sprite);

    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

    glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
    [context presentRenderbuffer:GL_RENDERBUFFER_OES];
}
Ukko
I think Moshe is asking about how to draw a 2D, sprite-based, "mario"; not a 3D, tessellated, mesh.
Jon-Eric
@ Jon-Eric: Tessellation, is not just about 3-D meshes it also applies to flat polygons as well. All a 2D sprite is is a texture rendered onto a GL_QUAD (a square tessellated into two triangles). The problem is the OpenGL ES doest do Quads but I thing he can use GL_TRIANGLE_FAN to the same effect.
Ukko
Wow, two negs already. At least Jon-Eric left a comment explaining his neg. I mean gosh, I correctly answered his question and even provided an example of code doing exactly what he is asking. Or is it backwards day around here and I didn't get the memo?
Ukko
I like the code samples. Thanks.
Moshe
+4  A: 

If I've understood the concern correctly: you simply divide the square into two triangles, and use texture coordinates to divide the texture up correspondingly. If you do it right, you get the texture drawn on screen, 1:1, as if it were a sprite. (Of course you also have the option of rotation and stretching and whatnot.)

0     1  <-- If your square is divided up like this, say, set texcoord
+-----+      for point 0 to be the top left of the sprite; for point 3, the
|\    |      bottom right of the sprite; and correspondingly, for points
| \   |      1 and 2.
|  \  |
|   \ |
|    \|
+-----+
2     3

As it turns out, you won't get a squished sprite in quite the way you're expecting. I imagine you're picturing drawing the triangle 0-3-2 from my picture above, say, mapped in the obvious fashion, and getting the top scanline infinitely squeezed into one point (point 0)? But in fact this won't happen, because there's only one texture coordinate for point 0, so (if points 2 and 3 are mapped appropriately) you'll only ever get the cut-out part of the sprite.

(That said, you could assign the same texcoord to two points, though, or make the texture cross over itself, and get it wrong that way -- there's still plenty of scope for making a mess, don't worry.)

This is explained in the OpenGL programming guide, though it's years and years since I learnt this stuff so I'm not sure how helpful it will be if you're having difficulty: http://glprogramming.com/red/chapter09.html

brone
`I imagine you're picturing drawing the triangle 0-3-2 from my picture above` Quite right. `But in fact this won't happen, because there's only one texture coordinate for point 0, so (if points 2 and 3 are mapped appropriately) you'll only ever get the cut-out part of the sprite.` So how do I map my "Mario"? Two parts? Isn't that difficult?
Moshe
Yup, just draw it in 2 parts. One triangle for one half of the sprite (cut across the diagonal), and one triangle for the other. If it's your first time with OpenGL and texture mapping it may stretch your mind a little but it's worth working through; for the computer, 2 triangles is certainly not difficult :) Looks like the code in Ukko's answer would do the trick. It draws 2 triangles, though using a triangle strip rather than specifying every point of each triangle.
brone
So I would make two png files then and map each one?
Moshe
You usually just have 1 PNG file, and therefore 1 texture. Draw both triangles with the same texture. The texture coordinates mean that each triangle has a different part of the texture mapped onto it, as described above.
brone
+2  A: 

Quite simply, the texture won't be distorted because you'll only be picking three texture coordinates - one per vertex. You'll have to use two triangles as outlined above, and "clip" different portions of the texture.

Pardon my horrible use of ASCII art here, and in the rest of this post:

  Polygon     Texture        ADB        ACD
  A-----C  |  x-----x  |  x        |  x-----x
  |\    |  |  | ___ |  |  |        |    ___ |
  | \   |  |  |<o.o>|  |  |<o      |     .o>|
  |  \  |  |  | ### |  |  | ##     |      # |
  |   \ |  |  | -|- |  |  | -|-    |        |
  |    \|  |  | / \ |  |  | / \    |        |
  B-----D  |  x-----x  |  x-----x  |        x

You'd make two triangles - ADB and ACD. Point A maps to the top-left of the texture, B to the bottom-left, C to the top-right, and D to the bottom-right. If you only map one triangle or the other, you only get half of the sprite (or texture, for a more complex shape.) The same applies for larger or more complex polygons. The texture can be a single unified piece, but the texture coordinates of each vertex has to slice it up in the same manner as the geometry itself.

More complex example, a hexagon:

   a   b
    ___
   /   \
f /  .  \ c
  \  g  /
   \___/
  e     d

If you add a middle point of "G" and slice it up into six triangles (ABG, BCG, CDG, etc) you'll have to make sure that whatever texture you're using is sliced up coordinate-wise to match. This doesn't matter if you're just using GL_TRIANGLES, since it's easiest to just leave the texture in its hexagonal shape, but once you start drawing strips or fans you can flip, skew, or duplicate if you don't keep close track of what vertex is mapping to what part of the texture, due to the drawing order.

As an aside, if you're just concerned with 2D screen-aligned quads - which is what sprites are generally for - use the OES_Draw_Texture extension available on the iPhone and save yourself a load of heartache while picking up a significant speed boost.

int rect[4] = {0, 0, 64, 64}; 
glBindTexture(GL_TEXTURE_2D, texMario); 
glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, rect); 
glDrawTexiOES(playerX, playerY, spriteZ, width, height); 

The rect defines the coordinates of the texture you're going to snip out (in pixels, so this would be a 64x64 sprite) and then the actual call to glDrawTexiOES plops it right into the screen view with its lower-left corner on playerX and playerY.

Goodness, even I get the feeling of "tl;dr" here. I apologize for that.

Chris Peredun
+1 for the ascii art, actually. Love it. And what is `"tl;dr"`?
Moshe
too long; Didn't read. If someone rambles on, you can skip straight to the tl;dr to get a rough idea what the post was about without having to stand through copious amounts of regurgitated text.
Pyronaut