0
\$\begingroup\$

I just move on from Immediate Mode and try Vertex Array method using glDrawElements and I got the texture is mess. How to draw the texture correctly?

This is a piece of my render code

glBindTexture(GL_TEXTURE_2D, BMap.Mesh[i].TextureID);

glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(3, GL_FLOAT, 0, BMap.Mesh[i].Vertice);

glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glTexCoordPointer(2, GL_FLOAT, 0, BMap.Mesh[i].TexCoord);

glDrawElements(GL_TRIANGLES, BMap.Mesh[i].TotalFace * 3, GL_UNSIGNED_INT, BMap.Mesh[i].Face);

glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);

The TexCoord stores U and V value, and this is the result:

enter image description here

And this is the model should be enter image description here

This is the model (ASCII) https://www.dropbox.com/s/9y466krh6gu1oej/lobby.bmap?dl=0

EDIT: I attach my full code BMap.h

class CBMap
{
public:
    struct SMesh
    {
        /*SVertice* Vertice;
        STexCoord* TexCoord;
        SNormal* Normal;*/

        float* Vertice;
        float* TexCoord;
        float* Normal;
        int* Face;
        int* FaceNorm;

        char Texture[32];
        int TextureID;
        int TotalVertice;
        int TotalNormal;
        int TotalFace;
    };

    SMesh* Mesh;
    /*SMesh* Mesh;
    SVertice* TempVertice;
    STexCoord* TempTexCoord;
    SNormal* TempNormal;*/

public:
    CBMap();
    ~CBMap();

    int TotalMesh;

    int Load(char* szFile);
    int GetTotalMesh(char* szFile);
    int GetData(char* szFile);
};

void Render(int MDL);
int LoadTexture(char* m_pFile);

BMap.cpp

int CBMap::Load(char* szFile)
{
    TotalMesh   = GetTotalMesh(szFile);
    Mesh        = new SMesh[TotalMesh];
    GetData(szFile);

    float* TempVertice;
    float* TempTexCoord;
    float* TempNormal;
    int* TempFace;
    int* TempFaceNorm;

    FILE* pFile = fopen(szFile, "r");

    if (!pFile) return 0;

    char szHeader[32];
    int readMode = 0;
    int iMesh = 0;

    while (!feof(pFile))
    {
        fscanf(pFile, "%s", &szHeader);

        if (!strncmp(szHeader, "BMAP", 4)) continue;
        if (!strncmp(szHeader, "mesh", 4)) continue;
        if (!strncmp(szHeader, "mtl", 3))
        {
            fscanf(pFile, "%s", Mesh[iMesh].Texture);
            Mesh[iMesh].TextureID = LoadTexture(Mesh[iMesh].Texture);
            //printf("Texture %s\n", Mesh[iMesh].Texture);
        }

        if (!strncmp(szHeader, "ve", 2))
        {
            TempVertice = (float*)malloc(sizeof (float) * Mesh[iMesh].TotalVertice * 3);
            readMode = 1;
            continue;
        }

        for (int i = 0; i < Mesh[iMesh].TotalVertice; ++i)
        {
            if (readMode != 1) continue;

            fscanf(pFile, "%f %f %f", &TempVertice[i*3+0], &TempVertice[i*3+1], &TempVertice[i*3+2]);
            memcpy(&Mesh[iMesh].Vertice, &TempVertice, sizeof (TempVertice));

            if (i+1 == Mesh[iMesh].TotalVertice) readMode = 2;
        }

        if (!strncmp(szHeader, "uv", 2))
        {
            TempTexCoord = (float*)malloc(sizeof (float) * Mesh[iMesh].TotalVertice * 2);
            readMode = 3;
            continue;
        }

        for (int i = 0; i < Mesh[iMesh].TotalVertice; ++i)
        {
            if (readMode != 3) continue;

            fscanf(pFile, "%f %f", &TempTexCoord[i*2+0], &TempTexCoord[i*2+1]);
            memcpy(&Mesh[iMesh].TexCoord, &TempTexCoord, sizeof (TempTexCoord));

            if (i+1 == Mesh[iMesh].TotalVertice) readMode = 4;
        }

        if (!strncmp(szHeader, "vn", 2))
        {
            TempNormal = (float*)malloc(sizeof (float) * Mesh[iMesh].TotalNormal * 3);
            readMode = 5;
            continue;
        }

        for (int i = 0; i < Mesh[iMesh].TotalNormal; ++i)
        {
            if (readMode != 5) continue;

            fscanf(pFile, "%f %f %f", &TempNormal[i*3+0], &TempNormal[i*3+1], &TempNormal[i*3+2]);
            memcpy(&Mesh[iMesh].Normal, &TempNormal, sizeof (TempNormal));

            if (i+1 == Mesh[iMesh].TotalNormal) readMode = 6;
        }

        if (!strncmp(szHeader, "f", 1))
        {
            TempFace = (int*)malloc(sizeof (int) * Mesh[iMesh].TotalFace * 3);
            TempFaceNorm = (int*)malloc(sizeof (int) * Mesh[iMesh].TotalFace *3);
            readMode = 7;
            continue;
        }

        for (int i = 0; i < Mesh[iMesh].TotalFace; ++i)
        {
            if (readMode != 7) continue;

            fscanf(pFile, "%d %d %d %d %d %d",  &TempFace[i*3+0], &TempFace[i*3+1], &TempFace[i*3+2],
                                                &TempFaceNorm[i*3+0], &TempFaceNorm[i*3+1], &TempFaceNorm[i*3+2]);
            memcpy(&Mesh[iMesh].Face, &TempFace, sizeof (TempFace));
            memcpy(&Mesh[iMesh].FaceNorm, &TempFaceNorm, sizeof (TempFaceNorm));

            if (i+1 == Mesh[iMesh].TotalFace) readMode = 0;
        }

        if (!strncmp(szHeader, "end", 3)) iMesh ++;
    }

    printf("Model loaded\n");

    fclose(pFile);
    return 1;
}

int CBMap::GetTotalMesh(char* szFile)
{
    FILE* pFile = fopen(szFile, "r");

    if (!pFile) return 0;

    int iData = 0;

    while (!feof(pFile))
    {
        char szHeader[32];
        fscanf(pFile, "%s", &szHeader);

        if (!strncmp(szHeader, "mesh", 4))
        {
            fscanf(pFile, "%d", &iData);
            break;
        }
    }

    return iData;
}

int CBMap::GetData(char* szFile)
{
    FILE* pFile = fopen(szFile, "r");

    if (!pFile) return 0;

    int iVertice, iNormal, iFace;
    int iCount = 0;

    while (!feof(pFile))
    {
        char szHeader[32];
        fscanf(pFile, "%s\n", &szHeader);

        if (!strncmp(szHeader, "ve", 2)) fscanf(pFile, "%d", &iVertice);
        if (!strncmp(szHeader, "vn", 2)) fscanf(pFile, "%d", &iNormal);
        if (!strncmp(szHeader, "f", 2)) fscanf(pFile, "%d", &iFace);

        if (!strncmp(szHeader, "end", 3))
        {
            Mesh[iCount].TotalVertice = iVertice;
            Mesh[iCount].TotalNormal = iNormal;
            Mesh[iCount].TotalFace = iFace;
            iCount ++;
        }
    }

    return 1;
}

void Render(int MDL)
{
    glPushMatrix();

    glTranslatef(0.0f, 0.0f, -5.0f);
    glRotatef(20.0f, 45.0f, 45.0f, 0.0f);
    glColor3f(255.0f, 255.0f, 255.0f);
    glEnable(GL_TEXTURE_2D);

    for (int i = 0; i < BMap.TotalMesh; ++i)
    {
        glBindTexture(GL_TEXTURE_2D, BMap.Mesh[i].TextureID);

        glEnableClientState(GL_VERTEX_ARRAY);
        glVertexPointer(3, GL_FLOAT, 0, BMap.Mesh[i].Vertice);

        glEnableClientState(GL_NORMAL_ARRAY);
        glNormalPointer(GL_FLOAT, 0, BMap.Mesh[i].Normal);

        glEnableClientState(GL_TEXTURE_COORD_ARRAY);
        glTexCoordPointer(2, GL_FLOAT, 0, BMap.Mesh[i].TexCoord);

        glDrawElements(GL_TRIANGLES, BMap.Mesh[i].TotalFace * 3, GL_UNSIGNED_INT, BMap.Mesh[i].Face);

        glDisableClientState(GL_VERTEX_ARRAY);
        glDisableClientState(GL_NORMAL_ARRAY);
        glDisableClientState(GL_TEXTURE_COORD_ARRAY);
        glBindTexture(GL_TEXTURE_2D, 0);
    }

    glDisable(GL_TEXTURE_2D);

    glPopMatrix();
}

int LoadTexture(char* m_pFile)
{
    char szHeader[54]; // Each BMP file begins by a 54-bytes header
    int DataPos; // Position in the file where the actual data begins
    int iWidth, iHeight;
    int Size; // = width*height*3
    char* BitMap; // Actual RGB data

    char dir[64];
    sprintf(dir, "%s", m_pFile);

    FILE *pFile = fopen(dir,"rb");

    if (!pFile)
    {
        printf("CModel::LoadTexture->Image could not be opened %s\n", m_pFile);
        return -1;
    }

    // If not 54 bytes read : problem
    if (fread(szHeader, 1, 54, pFile) != 54)
    { 
        printf("CModel::LoadTexture->Not a correct BMP file\n");
        fclose(pFile);
        return -1;
    }

    if (szHeader[0] != 'B' || szHeader[1] != 'M')
    {
        printf("CModel::LoadTexture->Not a correct BMP file\n");
        fclose(pFile);
        return -1;
    }

    // Read ints from the byte array
    DataPos     = *(int*)&(szHeader[0x0A]);
    Size        = *(int*)&(szHeader[0x22]);
    iWidth      = *(int*)&(szHeader[0x12]);
    iHeight     = *(int*)&(szHeader[0x16]);

    // Some BMP files are misformatted, guess missing information
    if (!Size) Size = iWidth * iHeight * 3; // 3 : one byte for each Red, Green and Blue component
    if (!DataPos) DataPos = 54; // The BMP header is done that way

    // Create a buffer
    BitMap = new char [Size];

    // Read the actual data from the file into the buffer
    fread(BitMap, 1, Size, pFile);

    //Everything is in memory now, the file can be closed
    fclose(pFile);

    GLuint pTex;

    glGenTextures(1, &pTex);
    glBindTexture(GL_TEXTURE_2D, pTex);
    //glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, iWidth, iHeight, 0, GL_BGR_EXT, GL_UNSIGNED_BYTE, BitMap);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

    //gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB, iWidth, iHeight, GL_BGR_EXT, GL_UNSIGNED_BYTE, BitMap);

    //glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

    //Mesh[MeshTotal - 1].pTex = pTex;
    ////strncpy(pTexture.szFile, szFile, sizeof (pTexture.szFile) - 1);
    //printf("Texture loaded %s Tex %d\n", pTexture.szFile, pTexture.pTex);
    printf("CModel::LoadTexture->Texture loaded %s %d\n", m_pFile, pTex);
    free(BitMap);
    return pTex;
    //TexNum++;
}
\$\endgroup\$
  • 1
    \$\begingroup\$ It looks like you have the texture coordinates in a different order than the vertices. But it's hard to say because you don't show the code where you generate or read in the vertices or texture coordinates. Would it be possible to show that? \$\endgroup\$ – user1118321 May 30 '15 at 4:52
1
\$\begingroup\$

There are several things wrong with your vertex and texture coordinate reading code. It's not doing what you think it is, and I think you could greatly simplify it while making it more robust. Let's just look at vertexes for the moment.

When it detects that the next thing in the file is a vertex, it allocates memory for 3 floats for each vertex. It then reads 3 coordinates into memory pointed to by TempVertice. Next it attempts to copy those 3 coordinates into the actual mesh, but it doesn't. What this line says:

memcpy(&Mesh[iMesh].Vertice, &TempVertice, sizeof (TempVertice));

is to copy the thing at the address of TempVertice into the thing at the address of Mesh[iMesh].Vertice. In other words, you're constantly writing the TempVertice pointer into the Mesh[iMesh].Vertice pointer. This clearly isn't what you intended, but it works out nicely because you never release the memory in TempVertice, and it actually does contain all the vertices. So you get lucky and your vertex data is correct, but just by chance. Since you never allocated Mesh[iMesh].Vertice, it doesn't cause a crash or leak any memory. (Well, I guess it leaks because you never free Mesh[iMesh].Vertice, but it doesn't leak twice.)

The rest of the code makes the same mistakes with texture coordinates and normals. This is likely the source of your problems.

What I would do is try to clean up the code. First, don't use magic numbers like:

readMode = 3;

Instead, create constants for them so others can read it. Write something like this:

readMode = READMODE_UV_COORDINATES;

or whatever. (You'll need to #define READMODE_UV_COORDINATES as 3 somewhere above or in a header.)

Next, create structs for your data. Your vertices aren't just a 1 dimensional string of floats. They're a 1 dimensional array of 3D coordinates, so you should declare them as such:

typedef struct Coord3D {
    float x;
    float y;
    float z;
} Coord3D;

Then you can do something like:

fscanf(pFile, "%f %f %f", &tempVertice [ i ].x, &tempVertice [ i ].y, &tempVertice [ i ].z);

Much easier to read and understand than [ i * 3 + 1 ].

And of course, you need to keep better track of your memory. Since you're in C++, you should consider using the standard template containers like std::vector. They resize on-the-fly and you can declare them directly in your main object instead of having pointers to them which need to be tracked.

\$\endgroup\$
  • \$\begingroup\$ Thank you so much, I learned a lot and I'll try to implement. \$\endgroup\$ – DavidJr May 31 '15 at 0:45
  • \$\begingroup\$ Also one more thing, I can do the Coord3D in glVertexPointer like this? glVertexPointer(3, GL_FLOAT, 0, BMap.Mesh[i].Coord3D); \$\endgroup\$ – DavidJr May 31 '15 at 1:25
  • \$\begingroup\$ Close. If you put them into an array of Coord3D name Vertices, then it would be BMap.Mesh[i].Vertices. You should also make sure that your compiler is packing the elements of the Coord3D struct. (Most likely it will, but if sizeof(Coord3D) is larger than sizeof(float)*3, then you may need to add a compiler directive telling it to tightly pack the elements of the Coord3D struct. That directive will vary depending on your compiler.) If the packing is OK, then it would be glVertexPointer(3,GL_FLOAT,0,BMap.Mesh[i].Vertices); \$\endgroup\$ – user1118321 May 31 '15 at 1:32
  • \$\begingroup\$ Also, if you do end up using std::vector for your arrays, then you'd pass the address of the first element of the vector to OpenGL doing something like this: glVertexPointer(3,GL_FLOAT,0,&BMap.Mesh[i].Vertices[0]); \$\endgroup\$ – user1118321 May 31 '15 at 1:34

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.