PDA

View Full Version : glDrawElements Access violation Reading Location



DavidJr
08-03-2015, 05:50 AM
Hello, I've trouble using glDrawElements, it is for study purpose. I'm still using OpenGL SDK 1.1 provided by Microsoft. I've been struggling with this about a month.

I try to render multiple objects (3 objects) at the same time. (Based on MS3D ASCII file format)

This is my loader function, I print those values are correct.

int iVal = NULL;
fread(&iVal, sizeof (int), 1, pIn);

pLevel[iLevel].iMesh = iVal;
pLevel[iLevel].pMesh = (SMesh*) malloc(sizeof (SMesh) * iVal);
//pLevel[iLevel].pMesh = new SMesh[iVal];

for (int i = 0; i < pLevel[iLevel].iMesh; ++i)
{
//pLevel[iLevel].pMesh[i].pName = (char*) malloc(sizeof (char) * 32);
//pLevel[iLevel].pMesh[i].mMat.pTexture = (char*) malloc(sizeof (char) * 32);
//pLevel[iLevel].pMesh[i].pName = new char[32];
//pLevel[iLevel].pMesh[i].mMat.pTexture = new char[32];

fread(pLine, sizeof (char), 32, pIn);
pLevel[iLevel].pMesh[i].pName = (char*) malloc(strlen(pLine) + 1);
//pLevel[iLevel].pMesh[i].pName = new char[strlen(pLine)+1];
strncpy(pLevel[iLevel].pMesh[i].pName, pLine, strlen(pLine));
pLevel[iLevel].pMesh[i].pName[strlen(pLine)] = '\0';

fread(pLine, sizeof (char), 32, pIn);
pLevel[iLevel].pMesh[i].mMat.pTexture = (char*) malloc(strlen(pLine)+1);
//pLevel[iLevel].pMesh[i].mMat.pTexture = new char[strlen(pLine)+1];
strncpy(pLevel[iLevel].pMesh[i].mMat.pTexture, pLine, strlen(pLine));
pLevel[iLevel].pMesh[i].mMat.pTexture[strlen(pLine)] = '\0';

fread(&pLevel[iLevel].pMesh[i].mMat.iSphere, sizeof (int), 1, pIn);
fread(&pLevel[iLevel].pMesh[i].mMat.fTransparancy, sizeof (float), 1, pIn);

pLevel[iLevel].pMesh[i].mMat.iID = TextureLoad(pLevel[iLevel].pMesh[i].mMat.pTexture);

fread(&iVal, sizeof (int), 1, pIn);

pLevel[iLevel].pMesh[i].iVe = iVal;
pLevel[iLevel].pMesh[i].pVe = (SVec3Df*) malloc(sizeof (SVec3Df) * iVal);
pLevel[iLevel].pMesh[i].pVt = (SVec2Df*) malloc(sizeof (SVec2Df) * iVal);
//pLevel[iLevel].pMesh[i].pVe = new SVec3Df[iVal];
//pLevel[iLevel].pMesh[i].pVt = new SVec2Df[iVal];

for (int j = 0; j < iVal; ++j)
{
fread(&pLevel[iLevel].pMesh[i].pVe[j], sizeof (float), 3, pIn);
fread(&pLevel[iLevel].pMesh[i].pVt[j], sizeof (float), 2, pIn);
}

fread(&iVal, sizeof (int), 1, pIn);

pLevel[iLevel].pMesh[i].iVn = iVal;
pLevel[iLevel].pMesh[i].pVn = (SVec3Df*) malloc(sizeof (SVec3Df) * iVal);
//pLevel[iLevel].pMesh[i].pVn = new SVec3Df[iVal];

for (int j = 0; j < iVal; ++j)
{
fread(&pLevel[iLevel].pMesh[i].pVn[j], sizeof (float), 3, pIn);
//printf("%f %f %f\n", pLevel[iLevel].pMesh[i].pVn[j].fX, pLevel[iLevel].pMesh[i].pVn[j].fY, pLevel[iLevel].pMesh[i].pVn[j].fZ);
}

fread(&iVal, sizeof (int), 1, pIn);

pLevel[iLevel].pMesh[i].iTri = iVal * 3;
pLevel[iLevel].pMesh[i].pTri = (SVec3Di*) malloc(sizeof (SVec3Di) * iVal);
//pLevel[iLevel].pMesh[i].pTri = new SVec3Di[iVal];

for (int j = 0; j < iVal; ++j)
{
fread(&pLevel[iLevel].pMesh[i].pTri[j], sizeof (int), 3, pIn);
}
}

When I try to render, I got access violation reading location in the application.
This is the render function:

glPushMatrix();

for (int i = 0; i < pLevel[iLevel].iMesh; ++i)
{
glColor4f(1.0f, 1.0f, 1.0f, pLevel[iLevel].pMesh[i].mMat.fTransparancy);

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

glVertexPointer(3, GL_FLOAT, sizeof (SVec3Df), &pLevel[iLevel].pMesh[i].pVe[0]);
glNormalPointer(GL_FLOAT, sizeof (SVec3Df), &pLevel[iLevel].pMesh[i].pVn[0]);
glTexCoordPointer(2, GL_FLOAT, sizeof (SVec2Df), &pLevel[iLevel].pMesh[i].pVt[0]);

glBindTexture(GL_TEXTURE_2D, pLevel[iLevel].pMesh[i].mMat.iID);
glDrawElements(GL_TRIANGLES, pLevel[iLevel].pMesh[i].iTri, GL_UNSIGNED_INT, &pLevel[iLevel].pMesh[i].pTri[0]);

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

Could it be the object loading order that causes the error? Because when I change the orders sometimes they crash, sometimes they don't.

NB: I know lot of OpenGL 1.1 functions are deprecated, I do this for study purpose only. Thank you before :)

GClements
08-03-2015, 08:51 AM
I note that the normal count (iVn) isn't necessarily the same as the position and texture coordinate count (iVe), which suggests that it may not be possible to render the data directly with glDrawElements(). Each value in the element array is used to index all three arrays (position, texture coordinates, normal), so the arrays must all have the same length.

If you try to read outside the bounds of the array (i.e. any index is less than zero or greater than or equal to either iVe or iVn), you'll typically get an access violation. I suggest adding code which validates the indices read into pTri accordingly.

DavidJr
08-03-2015, 05:40 PM
Thanks for the reply :)

So all the arrays that are passed to gl*Pointer must have the same size as the pTri has, right?

GClements
08-03-2015, 05:43 PM
So all the arrays that are passed to gl*Pointer must have the same size as the pTri has, right?
All of the indices in pTri must be less than the size of the attribute arrays.

The element array doesn't have to be the same size as the attribute arrays, and typically it's larger (as each vertex will typically be shared by multiple triangle vertices).

DavidJr
08-04-2015, 02:08 AM
All of the indices in pTri must be less than the size of the attribute arrays.

The element array doesn't have to be the same size as the attribute arrays, and typically it's larger (as each vertex will typically be shared by multiple triangle vertices).

Hello, thank you very much. I start to understand, and currently I process the vertices, normals and texcoord. I could load multiple objects without crashing. :)

Just to make clear the way I do is correct. This is the processing part.

void Level_SetupArray(int iLevel, int iMesh, SVec3Df* pVe, SVec2Df* pVt, SVec3Df* pVn, SVec3Di* pVeIndices, SVec3Di* pVnIndices)


pLevel[iLevel].pMesh[iMesh].pVe = (SVec3Df*) malloc(sizeof (SVec3Df) * iTri * 3);
pLevel[iLevel].pMesh[iMesh].pVt = (SVec2Df*) malloc(sizeof (SVec2Df) * iTri * 3);
pLevel[iLevel].pMesh[iMesh].pVn = (SVec3Df*) malloc(sizeof (SVec3Df) * iTri * 3);
pLevel[iLevel].pMesh[iMesh].pTri = (int*) malloc(sizeof (int) * iTri * 3);


for (int i = 0; i < iTri; ++i)
{
pLevel[iLevel].pMesh[iMesh].pVe[i * 3 + 0] = pVe[pVeIndices[i].iX];
pLevel[iLevel].pMesh[iMesh].pVe[i * 3 + 1] = pVe[pVeIndices[i].iY];
pLevel[iLevel].pMesh[iMesh].pVe[i * 3 + 2] = pVe[pVeIndices[i].iZ];

pLevel[iLevel].pMesh[iMesh].pVt[i * 3 + 0] = pVt[pVeIndices[i].iX];
pLevel[iLevel].pMesh[iMesh].pVt[i * 3 + 1] = pVt[pVeIndices[i].iY];
pLevel[iLevel].pMesh[iMesh].pVt[i * 3 + 2] = pVt[pVeIndices[i].iZ];

pLevel[iLevel].pMesh[iMesh].pVn[i * 3 + 0] = pVn[pVnIndices[i].iX];
pLevel[iLevel].pMesh[iMesh].pVn[i * 3 + 1] = pVn[pVnIndices[i].iY];
pLevel[iLevel].pMesh[iMesh].pVn[i * 3 + 2] = pVn[pVnIndices[i].iZ];
}


for (int i = 0; i < iTri * 3; ++i)
{
pLevel[iLevel].pMesh[iMesh].pTri[i] = i;
}

And the drawing part:

glDrawElements(GL_TRIANGLES, pLevel[iLevel].pMesh[i].iTri * 3, GL_UNSIGNED_INT, &pLevel[iLevel].pMesh[i].pTri[0]);

GClements
08-04-2015, 03:42 AM
Just to make clear the way I do is correct.
Probably. It's hard to be sure without knowing the model format.

You can probably reduce the total vertex count by allocating a vertex for each unique pair of position/texcoord and normal indices (see e.g. this thread (https://www.opengl.org/discussion_boards/showthread.php/191080-Getting-Texture-Seams-To-Display-Correctly) for a similar issue with importing the OBJ format).

Vertices which have the same position/texcoord index will typically also have the same normal index except at creases (sharp edges).

Otherwise, if you have three distinct vertices per face, you can avoid the element array altogether and just use glDrawArrays().

DavidJr
08-04-2015, 06:47 AM
Probably. It's hard to be sure without knowing the model format.

You can probably reduce the total vertex count by allocating a vertex for each unique pair of position/texcoord and normal indices (see e.g. this thread (https://www.opengl.org/discussion_boards/showthread.php/191080-Getting-Texture-Seams-To-Display-Correctly) for a similar issue with importing the OBJ format).

Vertices which have the same position/texcoord index will typically also have the same normal index except at creases (sharp edges).

Otherwise, if you have three distinct vertices per face, you can avoid the element array altogether and just use glDrawArrays().

Seems I'm not interested in glDrawArrays because the reason is I want to continue using glDrawElements with VBO technique. Using unique index seems a bit complicated for me, I will try to research with it. But for now, I just want to focus with this one.

Btw I use MilkShape 3D ASCII model format that I've rewritten to binary. The MS3D ASCII specifications can be found here: gamedev*net/topic/34112-ms3d-ascii-format/

How's your opinion?

Thank you very much this far, sir :)