Sign in to follow this  
-Datriot-

Vertex Array Trouble

Recommended Posts

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.

Share this post


Link to post
Share on other sites
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. :)

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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[i] != NULL)
{
// Gets the sprite's vertices and tex coords
Vector2* vecV;
vecV = sprites[i].CalculateVertices();
Vector2* vecT;
vecT = sprites[i].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[i] != 0)
{
/* Calls that draw method and passes down offset
so the sprite knows it's location in the
vertex array. */

sprites[i].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]

Share this post


Link to post
Share on other sites
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 each
enabled array to construct a sequence of geometric primitives, beginning with
element first. mode specifies what kind of primitives are
constructed, 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[i].CalculateVertices();
Vector2* vecT;
vecT = sprites[i].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);



Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this