normal arrays crashes

I use compiled vertext arrays for rendering my models, but when I use normal arrays, program crashes.

Sample 1 that works:

glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);

glVertexPointer(3, GL_FLOAT, 0, VertPosArray);
glTexCoordPointer(2, GL_FLOAT, 0, VertTexCoordArray);

glLockArraysEXT(0, lNumVerts);

//render models using glDrawArrays()

glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);

glUnlockArraysEXT();

Sample 2 that crashed my program:

glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY);

glVertexPointer(3, GL_FLOAT, 0, VertPosArray);
glTexCoordPointer(2, GL_FLOAT, 0, VertTexCoordArray);
glNormalPointer(3, GL_FLOAT, VertNormalArray);

glLockArraysEXT(0, lNumVerts);

//render models using glDrawArrays()

glDisableClientState(GL_NORMAL_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);

glUnlockArraysEXT();

My arrays looks like this:
t_Vec3f *VertPosArray;
t_Vec2f *VertTexCoordArray;
t_Vec3f *VertNormalArray;

and I allocate the memory for each when I load the model. All the arrays are same sized.

Your call to glNormalPointer is wrong. The first parameter is the data type, the second is the stride, and third is the pointer.

Oh yes, thanks. I really should see those prototypes before I use them

Something else come up:
The normals are somehow interpreted wrong, lighting is not looking right.

What does the parameter ‘stride’ do?
When I use glNormalPointer(GL_FLOAT, 0, VertNormalArray);, it crashes, I use the same stride value for other arrays and for them it works fine.
When I use glNormalPointer(GL_FLOAT, 1, VertNormalArray);, it doesn’t crash, but lighting is NOT ok.

I’m sure that the normal values are ok.

[This message has been edited by blender (edited 05-12-2002).]

The stride is the distance in bytes from the start of one entry (normal/vertex/color, dependeing on what the array contains) to the start of the next entry.

Say you have a vertex array, containing three components, stored as float, like this.

GLfloat myArray[] = {x0, y0, z0, x1, y1, z1, x2, y2, z2, …};

The distance in byte between the start of two consecutive vertices is 3*sizeof(float), which is usually 12 bytesm and the stride is therefore 12.

Now, say you want to, for some reason, pad each vertex with an extra float. You then get this vertex array,

GLfloat myArray[] = {x0, y0, z0, pad0, x1, y1, z1, pad1, x2, y2, z2, pad2…};

The stride is now 4*sizeof(float), or usually 16 bytes.

A stride of zero, as in your code, is a special case, and means the entries are tightly packed together, i.e. there is no “empty” space between two vertices. In my first example, you can set stride to either 12 (the actual stride), or zero (the special case), since there’s no space between two vertices. In the second example, you must set stirde to 16, cause they are no longer tightly packed together.

Most certainly your problem has to do with you not allocating enough memory for your normal array, and OpenGL tries to poke outside the allocated buffer. Why stride=1 works, but not stride=0, well, I leave that one as an excercise to you.

Most certainly your problem has to do with you not allocating enough memory for your normal array, and OpenGL tries to poke outside the allocated buffer.

Certainly not. Here’s a piece of code:

VertPosArray = new t_Vec3f[lNumVerts];
VertTexCoordArray = new t_Vec2f[lNumVerts];
VertNormalArray = new t_Vec3f[lNumVerts];

// Check for errors

fread(VertPosArray, sizeof(t_Vec3f), lNumVerts, f);
fread(VertTexCoordArray, sizeof(t_Vec2f), lNumVerts, f);
fread(VertNormalArray, sizeof(t_Vec3f), lNumVerts, f);

So, I’m pretty sure there are enough memory allocated.

Often if you are performing transforms that require scale, your normals will be scaled also, leaving them incorrect. This is nothing to do with vertex buffers. You should either ensure your normals aren’t scaled manually, or use lEnable ( GL_RESCALE_NORMAL ). This will slow down your T&L though, so try and remove the scaling if you can.

So, I’m pretty sure there are enough memory allocated.

I can only see that you are allocating lNumVerts number of normals, but I don’t see any proof that you aren’t telling OpenGL to draw more than lNumVerts vertices. So you may still be poking outide allocated memory, becuse you either try to draw too many vertices, or your index array has a few indices that are too large (larger than lNumVerts - 1 that is).

Currupt lighting can also be caused by incorrect normals. In other words, your code to calculate normals is incorrect, assuming you don’t load them from file, or anything like that.

Firstly, I’m not using index arrays and when I simply tried:

glDrawArrays(GL_TRIANGLES, 0, lNumVerts);

the results were same.

Secondly, I’m sure about the normals, they are loaded from a model file.

Thirdly, when I rendered in the ‘old fashion’
way with exactly the same arrays the lighting was ok.

OK, I think we got to the stage where we need to see some real code. If the code isn’t too long, post it here. If it is, you can mail it to me, and I have a look at it. You find my address in my profile.

I can see you are not poking outside the arrays, and the normals seems to be correct since it works in the ‘old fashion’ way. I take that to be glVertex/glNormal by the way .

Here is the primary part of my code:

Header:

typedef struct Texture_t
{
char Filename[64];
char Name[64];
};

typedef struct Reference_t
{
char Filename[64];
char Name[64];
long lStart; // Faces
long lNum;
long lStartVert;
long lNumVert;
long Id;
long Sid;
t_PolygonMode Type;
bool bShow;
};

class t_MdlHeader
{
public:
unsigned int Version;
char Modelname[64];

long lNumTextures;
long lNumVerts;
long lNumFaces;
long lNumReferences;
long lNumNodes;
long lNumFixedNodes;
long lNumFrames;
long lNumSequences;
long lNumBControllers;

// Vertex array data
long *VertNodeIdArray;
float *VertLengthArray;

t_Vec3f *VertPosArray;
t_Vec3f *VertOrigPosArray;
t_Vec2f *VertTexCoordArray;
t_Vec3f *VertNormalArray;

// Model data
Texture_t *Textures;
Reference_t *References;

t_GlTexture *TextureData;

bool bLoaded;

bool Load(const char *Filename);
void Destroy();
void Render();
void Update();
t_MdlHeader();
};

Loading

bool t_MdlHeader::Load(const char *Filename)
{

if(!m_Render.bInstalled) return false;

FILE *f;
char Temp[64];
long l;

m_Console.Message(“Model: Loading model: %s”, Filename);

// Make the global filename and global foldername
sprintf(Temp, “models/%s/%s.mdl”, Filename, Filename);

f = fopen(Temp, “rb”);
if(!f)
{
m_Console.Error(false, “Couldn’t open model file!”);
return false;
}

// Basic info
fread(&Version, sizeof(unsigned int), 1, f);
fread(Modelname, sizeof(char), 64, f);

if(Version != 1)
{
m_Console.Error(false, “Invalid model version!”);
return false;
}

// Elements number info
fread(&lNumTextures, sizeof(long), 1, f);
fread(&lNumVerts, sizeof(long), 1, f);
fread(&lNumFaces, sizeof(long), 1, f);
fread(&lNumReferences, sizeof(long), 1, f);
fread(&lNumNodes, sizeof(long), 1, f);
fread(&lNumFixedNodes, sizeof(long), 1, f);
fread(&lNumFrames, sizeof(long), 1, f);
fread(&lNumSequences, sizeof(long), 1, f);
fread(&lNumBControllers, sizeof(long), 1, f);

// Allocate memory for vertexs
VertNodeIdArray = new long[lNumVerts];
VertLengthArray = new float[lNumVerts];
VertPosArray = new t_Vec3f[lNumVerts];
VertOrigPosArray = new t_Vec3f[lNumVerts];
VertTexCoordArray = new t_Vec2f[lNumVerts];
VertNormalArray = new t_Vec3f[lNumVerts];

if(VertNodeIdArray==NULL | | VertLengthArray==NULL | | VertPosArray==NULL | |
VertOrigPosArray==NULL | | VertTexCoordArray==NULL | | VertNormalArray==NULL)
{
m_Console.Error(false, “Not enough memory for storing vertices.”);
return false;
}

// Allocate memory for elements
Textures = new Texture_t[lNumTextures];
Faces = new Face_t[lNumFaces];
References = new Reference_t[lNumReferences];
Nodes = new Node_t[lNumNodes];
FixedNodes = new FixedNode_t[lNumFixedNodes];
Frames = new Frame_t[lNumFrames];
Sequences = new Sequence_t[lNumSequences];

TextureData = new t_GlTexture[lNumTextures];

if(Textures==NULL | | Faces==NULL | | References==NULL | | Nodes==NULL | |
FixedNodes==NULL | | Frames==NULL | | Sequences==NULL | | TextureData==NULL)
{
m_Console.Error(false, “Not enough memory for storing elements.”);
return false;
}

// Load data
fread(Textures, sizeof(Texture_t), lNumTextures, f);

fread(VertNodeIdArray, sizeof(long), lNumVerts, f);
fread(VertPosArray, sizeof(t_Vec3f), lNumVerts, f);
fread(VertTexCoordArray, sizeof(t_Vec2f), lNumVerts, f);
fread(VertNormalArray, sizeof(t_Vec3f), lNumVerts, f);

fread(Faces, sizeof(Face_t), lNumFaces, f);
fread(References, sizeof(Reference_t), lNumReferences, f);
fread(Nodes, sizeof(Node_t), lNumNodes, f);
fread(FixedNodes, sizeof(FixedNode_t), lNumFixedNodes, f);

fread(Frames, sizeof(Frame_t), lNumFrames, f);
fread(Sequences, sizeof(Sequence_t), lNumSequences, f);
fread(BControllers, sizeof(BController_t), lNumBControllers, f);

fclose(f);

// Load textures
for(l=0; l<lNumTextures; l++)
{
sprintf(Temp, “models/%s/%s”, Filename, Textures[l].Filename);
TextureData[l].LoadTGA(Temp, true);
}

// Loaded
bLoaded = true;

return true;
}

Rendering

void t_MdlHeader::Render()
{

if(bLoaded==false) return;
if(m_Render.bInstalled==false) return;

// Lighting
glLightfv(GL_LIGHT0, GL_AMBIENT, Light0Amb);
glLightfv(GL_LIGHT0, GL_DIFFUSE, Light0Dif);
glLightfv(GL_LIGHT0, GL_POSITION, Light0Pos);
glEnable(GL_LIGHT0);
glLightfv(GL_LIGHT1, GL_AMBIENT, Light1Amb);
glLightfv(GL_LIGHT1, GL_DIFFUSE, Light1Dif);
glLightfv(GL_LIGHT1, GL_POSITION, Light1Pos);
glEnable(GL_LIGHT1);
glEnable(GL_LIGHTING);

if(mouse_b & 1)
{
glRotatef(90.0f, 0.0f, 0.0f, 1.0f);
glRotatef(fModelRotate, 0.0f, 0.0f, 1.0f);
}
else
{
glLoadIdentity();
glTranslatef(5.0f, -10.0f, -3.5f);
glRotatef(-90.0f, 1.0f, 0.0f, 0.0f);
glRotatef(180.0f, 0.0f, 0.0f, 1.0f);
}

static long n, m, k;
Reference_t *pRef;
t_GlTexture *pTextr;
t_GlTexture *pShine;

glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY);

glVertexPointer(3, GL_FLOAT, 0, VertPosArray);
glTexCoordPointer(2, GL_FLOAT, 0, VertTexCoordArray);
glNormalPointer(GL_FLOAT, 3, VertNormalArray);

glLockArraysEXT(0, lNumVerts);

for(n=0; n<lNumReferences; n++)
{
if(References[n].bShow==false) n++;

switch( References[n].Type )
{
  case SOLID:
    pRef   = &References[n];
    pTextr = &TextureData[pRef->Id];

    glBindTexture(GL_TEXTURE_2D, *pTextr->iData);
    glDrawArrays(GL_TRIANGLES, pRef->lStartVert, pRef->lStartVert+pRef->lNumVert);
    break;

  case SHINE:
    pRef   = &References[n];
    pTextr = &TextureData[pRef->Id];
    pShine = &TextureData[pRef->Sid];

    glBindTexture(GL_TEXTURE_2D, *pTextr->iData);
    glDrawArrays(GL_TRIANGLES, pRef->lStartVert, pRef->lStartVert+pRef->lNumVert);

    glEnable(GL_TEXTURE_GEN_S);
    glEnable(GL_TEXTURE_GEN_T);
    glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
    glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
    glEnable(GL_BLEND);
    glEnable(GL_ALPHA_TEST);
    glDisable(GL_LIGHTING);

    glBindTexture(GL_TEXTURE_2D, *pShine->iData);
    glDrawArrays(GL_TRIANGLES, pRef->lStartVert, pRef->lStartVert+pRef->lNumVert);

    glDisable(GL_BLEND);
    glDisable(GL_ALPHA_TEST);
    glEnable(GL_LIGHTING);
    glDisable(GL_TEXTURE_GEN_S);
    glDisable(GL_TEXTURE_GEN_T);
    break;

  case BLENDED:
    glEnable(GL_BLEND);
    glEnable(GL_ALPHA_TEST);
    glDisable(GL_LIGHTING);

    pRef   = &References[n];
    pTextr = &TextureData[pRef->Id];

    glBindTexture(GL_TEXTURE_2D, *pTextr->iData);
    glDrawArrays(GL_TRIANGLES, pRef->lStartVert, pRef->lStartVert+pRef->lNumVert);

    glDisable(GL_BLEND);
    glDisable(GL_ALPHA_TEST);
    glEnable(GL_LIGHTING);
    break;
}

}

glDisableClientState(GL_NORMAL_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);

glUnlockArraysEXT();

glDisable(GL_LIGHTING);
}

so, can you see anything that could cause this kind behaviour?

Try this:

glNormalPointer(GL_FLOAT, 0, VertNormalArray);

instead of:

glNormalPointer(GL_FLOAT, 3, VertNormalArray);

The second parameter is the stride, per component, rather than the width of the normal. Your normals are tightly packed, so there is no byte stride between them.

Your normals are tightly packed, so there is no byte stride between them.

The stride is not the space between two entries, but the distance from the start of one entry to the start of the next entry. The stride in blenders normal array should be 12 (3 * sizeof(float)), since there’s 12 bytes from the start of one normal to the start of the nex normal. Since they are tighly packed, as you say, the stride can be set to zero, but that’s just a special case, and should not be confused with the number of bytes between two entries.

Either way, a value of 3 is incorrect. It should be zero or 12? MSDN gives a rather stupid prototype for this function (with 4 parameters):

void glNormalPointer(
GLenum type,
GLsizei stride,
GLsizei count,
const GLvoid *pointer
);

Stride is stride in bytes, count is the number of normals (static normals, whatever that means).

In the gl online documentation, it states stride as the stride in bytes between the start of each normal, which as you say is 12.

So, his code 'aint right!!!

The OpenGL documentation in MSDN is not the best. The vertex array functions described there is the old version I believe, the version from the OpenGL 1.0 VA extension.

Newer versions of MSDN correctly show the prototype as

void glNormalPointer(
GLenum type,
GLsizei stride,
const GLvoid *pointer
);

Damn!!

I should have guessd this.
See this following line I used to use:
glDrawArrays(GL_TRIANGLES, pRef->lStartVert, pRef->lStartVert+pRef->lNumVert);

Heh. Before I changed to vertex arrays, I used loop like this:
for(i=pRef->lStartVert; i<pRef->lStartVert+pRef->lNumVert; i++)

So, I took a look to glDrawArrays more carefully and figured out that the last parameter is actually a number of polygons after the start point, not the end point in the array.

That’s why it did crash before, when I used stride as (3*sizeof(float)) or sizeof(t_Vec3f),
but now it finally works.

Thanks to eveyone for your support.