PDA

View Full Version : Vertex array and stride problem - help plz



Siwko
05-11-2001, 11:20 AM
I've defined a small structure to hold model data for my OpenGL project. However, for some reason, the result isn't quite what I expect. I don't know if I'm reading the spec wrong or what.

Here's what I'm doing:

I have defined an array of floats as:



MapData = (float*)new float[4 * cx * cy];


MapData[i + 0] has the X coordinate of a vertex
MapData[i + 1] has the Y
MapData[i + 2] has the Z
MapData[i + 3] is set to 1.0f

Then in the rendering path I have:



glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);

glVertexPointer(3, GL_FLOAT, 4, tMap.GetData());
glColorPointer(4, GL_UNSIGNED_BYTE, 0, tMap.colorptr);
glDrawArrays(GL_POINTS, 0, tMap.GetXSize() * tMap.GetYSize());

glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);


I've tried a stride of 16, and things look a bit better, but it looks like every other row of points is missing. Am I not using stride correctly? What am I missing here?

It may possibly be the generation code, but when I define the array with a 3 * cx * cy, and specify a 0 stride, everything works properly.

Help?

Thanks,
Siwko

jwatte
05-11-2001, 11:51 AM
glVertexPointer(3, GL_FLOAT, 4, tMap.GetData());


The stride is in BYTES, not in elements. Thus, you should put 16 there (or, if you prefer, 4*sizeof(float)). Then it should work correctly. Assuming you're putting good data in there, of course -- using one float array and calculating the index manually is an easy way of introducing hard-to-spot bugs in your code :-(

If you're having one big array, you could just as well define a "vert" struct:

struct vert {
float x;
float y;
float z;
float w;
};

as this will take as much space, but be easier to code with (and read!)

If you don't need the w, you can put the color there, and compress everything into a single 16-byte fetch:

struct vert {
float x;
float y;
float z;
uchar color[4];
};

Your calls to set up the vertex arrays are then:




glVertexPointer(3, GL_FLOAT, sizeof(vert), &tMap.GetData()[0].x);
glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(vert), tMap.GetData()[0].color);


You should also try to use DrawElements() instead of DrawArrays(), if you can get vertex reuse to happen. (DrawArrays() is faster than DrawElements() if there is no vertex re-use).

Deiussum
05-11-2001, 11:57 AM
16 should be the correct stride. The value you give should be the byte offset between the start of one vertex and the start of the next. Thus it would be sizeof(float) * 4, which should be 16 since sizeof(float) is 4. (On virtually every system available anyway.) http://www.opengl.org/discussion_boards/ubb/smile.gif

Not sure what would cause every other row of points to appear missing, though.

Deiussum
05-11-2001, 11:59 AM
Ugh... you beat me to it jwatte. http://www.opengl.org/discussion_boards/ubb/smile.gif

Siwko
05-11-2001, 12:49 PM
Thats just it. I was sure that it was in bytes. What I wasn't certain about was whether it was the offset from the BEGINNING of the previous vertex in bytes, or the offset from the END of the previous vertex in bytes.

See my logic there? If it's from the end, then it's only 4 bytes offset.

Anyway, that's what I was thinking (what you guys said), though I pasted code that I was testing, thus the stride of 4.

The problem remains, it seems as if every other row is missing for the output. Any ideas? (Like I said, it's probably in my generation code - but I dunno.)

Siwko

ffish
05-11-2001, 06:46 PM
The stride is from the same place for each vertex in the array i.e. from the beginning of one vertex to the beginning of the next to the beginning of the next ... as the others said.

Why do you need the MapData[i+3] = 1.0f? Can't you get rid of that and use glVertexPointer(3, GL_FLOAT, 3*sizeof(float), tMap.GetData());? Without the w value, you can also have a stride of 0, meaning tightly packed data. Alternatively, if you want the w value in there, what about glVertexPointer(4, GL_FLOAT, 4*sizeof(float), tMap.GetData()); (can also have a 0 stride).

You obviously generate the points procedurally. Can you make a test data set and set up the array with it to see if that works OK, either with or without the w's? e.g.


float MapData[] = {x0, y0, z0, x1, y1, z1, ... };
// or
float MapData[] = {x0, y0, z0, w0, x1, y1, z1, w1, ... };

Let us know how it goes.

Cem UZUNLAR
05-12-2001, 12:57 AM
It should be:

glVertexPointer(3, GL_FLOAT, 16, tMap.GetData());
glColorPointer(4, GL_UNSIGNED_BYTE, 16, tMap.colorptr);

Cem UZUNLAR
05-12-2001, 01:00 AM
I mean for the structure:

struct vert {
float x;
float y;
float z;
uchar color[4];
};

Siwko
05-12-2001, 05:30 AM
Okay, so I think then it is with my generation code. Care to take a looksie?



cx = 256;
cy = 256;

if(MapData == NULL)
MapData = (float*)new float[4 * cx * cy];

if(colorptr == NULL)
colorptr = (long*)new long[cx * cy];

float range = (float)(cos(0.0f) * 0.25);

for(long i = 0; i < 4 * cx * cy; i += 4)
{
MapData[i + 0] = (float)(i % cx) / (float)(cx);
MapData[i + 1] = (float)floor((i / 4) / cx) / (float)(cy);
MapData[i + 2] = -(float)cos(2.0 * 6.28 * (float)(i % cx) / (float)(cx)) * 0.25f * (float)cos(2.0 * 6.28f * MapData[i + 1]);
MapData[i + 3] = 1.0f;

colorptr[i / 4] = RGB((long)(MapData[i + 2] / range * 256.0f), (long)(MapData[i + 2] / range * 256.0f), (long)(MapData[i + 2] / range * 256.0f));
}


Now, if you change the for loop to use an increment of 3, allocate 3*cx*cy, and use a stride of 0, everything works fine. Uping to 4 should work, but it doesn't seem to.

Any ideas?

Siwko

ffish
05-13-2001, 12:24 AM
It took me a while Siwko, but I've got it. It's in your i % cx expressions. When i is a multiple of 3, i % cx takes a long time to repeat itself. When i is a multiple of 4, it doesn't take long for i % cx to repeat. Here's how I found it:


const int offset(3);
const int cx(8);
const int cy(8);
const int cxcy(cx*cy);
const int cxcy4(cxcy*offset);

//float *MapData = (float*)new float[cxcy4];
float MapData[cxcy4];

//long *colorptr = (long*)new long[cxcy];
unsigned char colorptr[cxcy*3];

void Init()
{
for (unsigned long i(0); i<cxcy4; i+=offset) {
const float i_mod_cx(i%cx);
std::cout << "i_mod_cx: " << i_mod_cx << "\ti_mod_cx/(float)cx: " << i_mod_cx/(float)cx << std::endl;
MapData[i+0] = i_mod_cx/static_cast<float>(cx);
MapData[i+1] = (float)floor((i/offset)/cx)/(float)(cy);
MapData[i+2] = 0;
if (offset > 3) {
MapData[i+3] = 1.0f;
}
}
for (unsigned long j(0); j<cxcy*3; ++j) {
colorptr[j] = (GLubyte)255;
}
}
void Draw()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
glTranslatef(0.0f, 0.0f, -2.1f);

glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);

if (offset > 3) {
glVertexPointer(4, GL_FLOAT, 0, MapData);
} else {
glVertexPointer(3, GL_FLOAT, 0, MapData);
}
glColorPointer(3, GL_UNSIGNED_BYTE, 0, colorptr);
glDrawArrays(GL_POINTS, 0, cxcy);

glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);

glutSwapBuffers();
}


Change the values for offset between 3 and 4 and see the output. You can also try for different values of cx and cy.

Hope that helps http://www.opengl.org/discussion_boards/ubb/smile.gif

Siwko
05-14-2001, 04:09 AM
Originally posted by ffish:
It took me a while Siwko, but I've got it. It's in your i % cx expressions. When i is a multiple of 3, i % cx takes a long time to repeat itself. When i is a multiple of 4, it doesn't take long for i % cx to repeat.

Well, thats the thing. i % cx basically calculates the x position in a row. I think you're thinking about cx and i backwards in this case. When cx = 256 (or any arbitrary number)..

Wait a sec. I just figured I'd "check" into it before I yapped. Stupid me, I wasn't paying attention. The modulus calls should be:



(long)floor(i / 4) % cx

and not:



i % cx

You're right. I was thinking the other way around, and neglecting to realize that my increment was actually += 4, not ++, which would make the modulus work in a discretely non-linear fashion. Thanks guys, and especially ffish for catching at that.

Siwko