Vertex Array Trouble

Started by
8 comments, last by -Datriot- 15 years, 8 months ago
Well the problem is that my quads aren't drawing proberly when I use them in a vertex array. In my Render() method I enable GL_VERTEX_ARRAY and GL_TEX_COORD_ARRAY and then I put the following arrays into gl*Pointer(): vertices = new float[16] = { 0.0f, 0.0f, 256.0f, 0.0f, 256.0f, 256.0f, 0.0f, 256.0f, // Quad1 100.0f, 100.0f, 356.0f, 100.0f, 356.0f, 356.0f, 100.0f, 356.0f } // Quad2 texCoords = new float[16]; // No need to put this I think. I then call glDrawArrays(GL_QUADS, 0, 16), however instead of two quads appearing one textured triangle appears. I'm guessing I've done something obviously wrong, but I'm out of ideas.
Advertisement
Post some actual code (inside of [source] and [/source] tags); see if someone can't spot something. [smile]

[Edited by - dmatter on July 24, 2008 12:14:39 PM]
I am just looking at your code and I see you only have 8 vertices, as you commented out the last 8...
Well, what I usually do is create a struct for your vertex to hold data.

typedef struct {	float u, v;			// texture coordinates	unsigned int color;		// color value	float nx, ny, nz;		// normal values	float x, y, z;			// position values} Vertex;


Then I create an array of type Vertex like so:

Vertex * vertexList;int numVerts = 100; // you calculate this depending on how many vertices you need to store/* make space for our vertices */vertexList = (Vertex*)malloc(sizeof(Vertex) * numVerts);


NOTE: Make sure that your vertex struct is tailored to the vertex type that you pass in your render call, otherwise you will get bad results. ;)

I really hope that this is helpful and not confusing. :)
------------------------------------------------------------visit my: homepage
Quote:Original post by MARS_999
I am just looking at your code and I see you only have 8 vertices, as you commented out the last 8...
Presumably the comment is at the end of the line and then the values continue on the line below.

The OP implies their code generates something visually, so then I can only think that this..

vertices = new float[16] = { /*values*/ }

.. is not a real line of code since that shouldn't even compile, in C++ at least.
exactly dmatter, I was hoping that the OP just threw in a bunch of code and that it's not what he actually has.
Sorry about the late reply, I was in a rush at the time I wrote it so I'm sorry it wasn't very informative.

Well, I wanted to test out vertex arrays by drawing two quads. However when I put 16 vertices into the array and call glDrawArrays(GL_QUADS, 0, 16) it only draws one triangle. I tried the same code with one quad ( calling glDrawArrays(GL_QUADS, 0, 8) of course ) and it worked perfectly fine.

This is the code that creates the arrays:

void SpriteBatch::Update(){    /* Vertex and texture coordinates arrays       Uses spritecount to determine size. */    float *vertices = new float[(spriteCount * 4) * 2];    float *texCoords = new float[(spriteCount * 4) * 2];    /* Also sets the array size to       the same size that's used for vertices and texCoords. */    arraySize = (spriteCount * 4) * 2;    /* Loops for the amount of sprites       i is used to iterate through the sprite list.       j is used to iterate through the vertex arrays. */    unsigned int i; unsigned int j = 0;    for (i = 0; (i < sprites.size()); i++)    {        // Makes sure the sprite is valid ( exists )        if (&sprites != NULL)        {            // Gets the sprite's vertices and tex coords            Vector2* vecV;            vecV = sprites.CalculateVertices();            Vector2* vecT;            vecT = sprites.CalculateTexCoords();            /* Assigns them in the array that the VBO will use. */            // Assigns vertex data            vertices[j] = vecV[0].x; vertices[j + 1] = vecV[0].y;            vertices[j + 2] = vecV[1].x; vertices[j + 3] = vecV[1].y;            vertices[j + 4] = vecV[2].x; vertices[j + 5] = vecV[2].y;            vertices[j + 6] = vecV[3].x; vertices[j + 7] = vecV[3].y;            // Assigns texture coordinate data            texCoords[j] = vecT[0].x; texCoords[j + 1] = vecT[0].y;            texCoords[j + 2] = vecT[1].x; texCoords[j + 3] = vecT[1].y;            texCoords[j + 4] = vecT[2].x; texCoords[j + 5] = vecT[2].y;            texCoords[j + 6] = vecT[3].x; texCoords[j + 7] = vecT[3].y;            /* Increases j so the next group of vertices is               put in the next part of the array. */            j++;            delete [] vecV;            delete [] vecT;        }    }    /* Makes sure to delete the current vertex and tex coord array       If they are actually pointing to something that is. */    if (vertexArray != NULL) delete [] vertexArray;    if (texCoordArray != NULL) delete [] texCoordArray;    /* After creating both arrays, assign them to the       vertex and tex coord array pointers. */    vertexArray = vertices;    texCoordArray = texCoords;}Vector2* Sprite::CalculateVertices(){    Vector2* vertices = new Vector2[4];    // Changes the vectors depending on the sprite's size    vertices[0].x = position.x + 0.0f; vertices[0].y = position.y + 0.0f;    vertices[1].x = position.x + texture.width;    vertices[1].y = position.y + 0.0f;    vertices[2].x = position.x + texture.width;    vertices[2].y = position.y + texture.height;    vertices[3].x = position.x + 0.0f;    vertices[3].y = position.y + texture.height;    // Returns the vertices    return vertices;} /* NOTE: I intend to have transformations and such to draw the sprites           and not just explicitly set their positions to the vertices,           butI'm just doing this as a test.  */Vector2* Sprite::CalculateTexCoords(){    Vector2* texCoords = new Vector2[4];    // Assigns simple texture coordinates    texCoords[0].x = 0.0f; texCoords[0].y = 0.0f;    texCoords[1].x = 1.0f; texCoords[1].y = 0.0f;    texCoords[2].x = 1.0f; texCoords[2].y = 1.0f;    texCoords[3].x = 0.0f; texCoords[3].y = 1.0f;    // returns the texture coordinates    return texCoords;}


And that's the drawing code:

void SpriteBatch::Render(){    // Enables and assigns vertex array data    glEnableClientState(GL_VERTEX_ARRAY);    glVertexPointer(2, GL_FLOAT, 0, vertexArray);    glEnableClientState(GL_TEXTURE_COORD_ARRAY);    glTexCoordPointer(2, GL_FLOAT, 0, texCoordArray);    // Draws the entire array, which draws every sprite    //glDrawArrays(GL_QUADS, 0, arraySize);    /* Searches sprite array and calls each of their       individual draw methods. */    int offset = 0; // Used to pass down array index to sprite.    for (unsigned int i = 0; (i < sprites.size()); i++)    {        // If sprite is valid        if (&sprites != 0)        {            /* Calls that draw method and passes down offset               so the sprite knows it's location in the               vertex array. */            sprites.Render(offset);            /* Increaes the offset by 8, because that's               how many each sprite has in the array. */            offset += 8;        }    }    // Disables vertex arrays    glDisableClientState(GL_VERTEX_ARRAY);    glDisableClientState(GL_TEXTURE_COORD_ARRAY);}void Sprite::Render(){    glBindTexture(GL_TEXTURE_2D, texture.ID);    glDrawArrays(GL_QUADS, offset, 8);}


Oh, and sprite 1's position is (0.0f, 0.0f) and the second sprite's is (300.0f, 300.0f) if that has any relevence, which I don't it does, but just in case.
Thnaks for the help.

[Edited by - -Datriot- on July 29, 2008 5:24:43 AM]
So, no ideas on what could be wrong? I still haven't figured it out yet.
Well, there are a few things that are strange.

Rendering a quad required 4 verticies, but you have glDrawArrays(..., 8).

From SGI man pages:

When glDrawArrays is called, it uses count sequential elements from eachenabled array to construct a sequence of geometric primitives, beginning withelement first. mode specifies what kind of primitives areconstructed, and how the array elements construct those primitives.If GL_VERTEX_ARRAY is not enabled, no geometric primitives are generated.


Quads require 4 arrays elements, with each element being 2 floats in size.

Second, if you suspect that there is some kind of issue with with the way glDrawArrays() is reading your data, resort to immediate mode.

glBegin(GL_QUADS);glTexCoord2f(...);glVertex2f(...);glEnd();


Make sure your quads aren't being culled. By default OpenGL use counter-clockwise to define a front face. See glFrontFace() and glCullFace().

For testing purposes, use glDisable(GL_CULL_FACE) to make sure NO faces are culled, regardless of mode. If everything appears normal, enable it.

Your code gives:
vertices[0].x = position.x + 0.0f;vertices[0].y = position.y + 0.0f;vertices[1].x = position.x + texture.width;vertices[1].y = position.y + 0.0f;vertices[2].x = position.x + texture.width;vertices[2].y = position.y + texture.height;vertices[3].x = position.x + 0.0f;vertices[3].y = position.y + texture.height;



Assuming your origin is bottom left, your winding order is: BL->BR->TR->TL, which is counter-clockwise. If you assume that the origin is top left (like DirectX), then your order is TL->TR->BR->BL, which is a clockwise order and will be culled by OpenGL.


Other things to look out for:
Is your modelview matrix set up correctly? Can you prove it?
Is your texture.width and texture.height fields set up correctly? Can you prove it?

Is this functioning correctly?

            // Gets the sprite's vertices and tex coords            Vector2* vecV;            vecV = sprites.CalculateVertices();            Vector2* vecT;            vecT = sprites.CalculateTexCoords();            /* Assigns them in the array that the VBO will use. */            // Assigns vertex data            vertices[j] = vecV[0].x; vertices[j + 1] = vecV[0].y;            vertices[j + 2] = vecV[1].x; vertices[j + 3] = vecV[1].y;            vertices[j + 4] = vecV[2].x; vertices[j + 5] = vecV[2].y;            vertices[j + 6] = vecV[3].x; vertices[j + 7] = vecV[3].y;            // Assigns texture coordinate data            texCoords[j] = vecT[0].x; texCoords[j + 1] = vecT[0].y;            texCoords[j + 2] = vecT[1].x; texCoords[j + 3] = vecT[1].y;            texCoords[j + 4] = vecT[2].x; texCoords[j + 5] = vecT[2].y;            texCoords[j + 6] = vecT[3].x; texCoords[j + 7] = vecT[3].y;            /* Increases j so the next group of vertices is               put in the next part of the array. */            j++;


Don't you mean j += 8, not j++?


As for your rendering code: it's painfully inefficient.
-> In general, I'd watch out about memory allocations (i.e. using new/delete/malloc()/free(). Many of the things are you are doing are quite unneeded. It is hard to follow the flow of your coordinates. They go from position values, to a Vector2 array, to a float array, and then finally to render. Since you have so many memory allocations/deletions, you'll find it is faster to use immediate mode since the speed benefit from packaging a single sprite into a container is negative. If you insist on batching, do a batch-flush, i.e. batch 500 sprites. Once the 501th is attempted to being drawn, draw all 500 at once, then begin a new batch. I do this for drawing text on screen since each letter is a quad, and the performance is quite good.

-> You might consider binding a texture once, drawing all of the sprites that require that texture, then switching textures. You'll need to batch a bunch of sprites and then sort by texture ID, or some such. That applies to all games.

-> Recalculating each frame is fine (since quads are 4 verticies -> doesn't translate well into 3D since it ignores hardware TnL), but break away from doing a new []. This takes much more time than you think, and while it may be fine for a low number of sprites, it puts excessive burden on the C++ runtime/mem manager. You need to fill a buffer once, not copy data around 3 times.

    vertices[0].x = position.x + 0.0f;    vertices[0].y = position.y + 0.0f;    vertices[1].x = position.x + texture.width;    vertices[1].y = position.y + 0.0f;    vertices[2].x = position.x + texture.width;    vertices[2].y = position.y + texture.height;    vertices[3].x = position.x + 0.0f;    vertices[3].y = position.y + texture.height;    glVertexPointer(2, ... vertices ...)---------    //Need to do glTexCoord2f(..) also.   glVertex2f(position.x, position.y);   glVertex2f(position.x+texture.width, position.y);   glVertex2f(position.x+texture.width, position.y+texture.height);   glVertex2f(position.x, position.y+texture.height);

Hey thanks for the input. I can't believe after all that time I missed the j++, it just looked like it was supposed to be there so I kinda just skimmed past it when trying to figure out what was wrong.

I have been planning to make it all more efficient, especially getting rid of the new/delete thrown everywhere. I just put a bunch of code together to get it to work, and then I was gonna sort the batches by texture IDs, stop passing around the data so much, etc. when I actually got a result.

I'm suprised that setting the sprite's position in the vertices was correct though, I always thought matrix transformations would be more efficient.

Once again thanks for the input.

This topic is closed to new replies.

Advertisement