C++ – Using multiple textures in OpenGL

c++colladaopengltextures

Update: I can draw multiple textures now, I figured out how glGenTextures etc works, and rewrote my loadtextures () function and stuff. However only a few parts of the car are drawn; the steering wheel, one hub cap and the back lights (and maybe a few smaller things). In the collada file a few materials don't ever get associated with a texture file. I don't think this is the problem because on my first go I associated the textures in the list with the first couple of polylists (5, there are like 80 total), and I got a nice car that looked like it was using the right textures, just missing tires and maybe some small things I didn't notice. I think maybe it has something to do with textures that get tiled? Anyway I wanted to replace all the meshes that have no texture files with the colour red, but I am not sure how to do that. I started using code in this tutorial:

http://www.opengl-tutorial.org/beginners-tutorials/tutorial-4-a-colored-cube/

I made a colour buffer, bound it, etc, but then I realised I would need to edit the shader files and I am not sure how to do that.

One:

attribute vec3 vPosition;
attribute vec2 vTextureCoord;
varying  vec2 vTexCoord; 
uniform  mat4 modelview_matrix;

void main(void)  
{     
   vTexCoord =  vTextureCoord;
   gl_Position =  modelview_matrix*vec4(vPosition,1.0);
}

The other:

varying  vec2 vTexCoord; 
uniform sampler2D myTexture;
void main (void)  
{  
   gl_FragColor =  texture2D(myTexture, vTexCoord);     
}

/////original question

I am doing a uni assignment where I will have to draw some objects with textures (and I'll have to be able to move around the scene and stuff but I'll cross that bridge when I get to it). I have a tutorial as a base which will draw an object from a single array of vertices and a single texture file, from a .obj file (I wrote the parser for the previous tutorial).

The assignment uses collada files, so I have written a collada parser. It was REALLY hard work! My collada parser produces a map that contains Material objects, which have lots of name and id variables (not used in main.cpp, they are just for associating stuff between parts of of the collada file), an m_TgaFile variable which holds the texture file name as a string, and an m_Mesh variable which holds a vector of Vertex objects, which is an object that just has two float arrays, m_Positions and m_Textures to hold the positions and tex co-ordinates.

So what I have been able to do so far is draw all the bits by looping over the map and just using the first texture file in the map (it crashes after a few minutes but I'll figure that out later, I am just happy that something appeared on screen). I tried calling loadTextures () (and sending in a Material) where I am looping over the map so I could use each Material's texture file but nothing drew at all (white screen). So how do I load each texture for each bit of drawing?

I have tried researching it, and have come up with the fact I need to use glBindTexture, glTexImage2D, and another one I can't remember. The first two are in the TgaParser which came with my tutorial. glBindTexture is used again in the drawing code, with a second parameter of zero. From what I gather this means use the default texture that was loaded. I can't figure out though how to load each texture when I want it, I don't understand what texture names means and how that gets associated with texture data.

Oh and I have tried doing the looping in the OpenGl main function and sending pMaterial as a variable to the display function but it didn't like that, I didn't think it would work but I thought I'd at least try.

Here is some code.

The OpenGL main function:

int main(int argc, char **argv)
{

glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);

glutInitWindowSize(screenWidth, screenHeight);
glutCreateWindow("Intro to shading");
glewInit();

init();
glutDisplayFunc(forward_display);
glutIdleFunc(forward_animate);

glutMainLoop();
/*
    DONT DO THINGS HERE
*/
return 0;
}

Drawing function:

void forward_display()
{
glClear(GL_COLOR_BUFFER_BIT| GL_DEPTH_BUFFER_BIT);

glMatrixMode(GL_MODELVIEW);
glLoadIdentity();

//glScalef(0.5,0.5,0.5);
glScalef(0.005,0.005,0.005);
glRotatef(timeGetTime()*0.01,0,1,0);
GLfloat m[16];
glGetFloatv (GL_MODELVIEW_MATRIX, m);
glUniformMatrix4fv(gvModelMatrixHandle,1,false,m);


glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D,0);

colladaObject->m_MaterialMap.StartIterator ();

while(!colladaObject->m_MaterialMap.IsEOM ())
{
    shared_ptr<Material> pMaterial = colladaObject->m_MaterialMap.Get ();

    if(pMaterial->m_Mesh.size () != 0)
    {
        glVertexAttribPointer(gvPositionHandle, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), &pMaterial->m_Mesh[0].m_Positions);//v[0].pos

        glEnableVertexAttribArray(gvPositionHandle);

        glVertexAttribPointer(gvTextureCoordhandle, 2, GL_FLOAT, GL_FALSE,sizeof(Vertex), &pMaterial->m_Mesh[0].m_Textures);//&v[0].texCoords
        glEnableVertexAttribArray(gvTextureCoordhandle);

        glDrawArrays(GL_TRIANGLES, 0, pMaterial->m_Mesh.size());
    }
    colladaObject->m_MaterialMap.MoveNext ();
}

glutSwapBuffers();
}

the init function:

void init()
{
char  * vertexShaderBuffer =       readFileData("../resources/shaders/IntroToShaders.vs");
char  * pixelShaderBuffer  =   readFileData("../resources/shaders/IntroToShaders.ps");

gProgram = createProgram(vertexShaderBuffer, pixelShaderBuffer);

//We have finished  compiling the shader now delete the char arrays with the source code
delete [] vertexShaderBuffer;
delete [] pixelShaderBuffer;

gvPositionHandle        = glGetAttribLocation(gProgram, "vPosition");
gvTextureCoordhandle    = glGetAttribLocation(gProgram, "vTextureCoord");
gvModelMatrixHandle     = glGetUniformLocation(gProgram, "modelview_matrix");
if (gvPositionHandle==-1)
    printf("gvPositionHandle is bad\n");

if (gvTextureCoordhandle==-1)
    printf("gvTextureCoordhandle is bad\n");

if (gvModelMatrixHandle==-1)
    printf("gvModelMatrixHandle is bad\n");
glUseProgram(gProgram);

//cube = new ObjParser("../resources/mesh/cube.obj");
colladaObject = new ColladaParser("../resources/mesh/car.dae");
loadTextures();

glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
glEnable (GL_BLEND);
glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}

The loadTextures function:

void loadTextures ()
{
//just try loading the first texture to make sure all the other code changes work before playing with this one.
colladaObject->m_MaterialMap.StartIterator ();
shared_ptr<Material> pMaterial = colladaObject->m_MaterialMap.Get ();
while(pMaterial->m_TgaFile == "")
{
    colladaObject->m_MaterialMap.MoveNext ();
    pMaterial = colladaObject->m_MaterialMap.Get ();
}

char tgafile [100];
strcpy(tgafile, "../resources/textures/");
strcat(tgafile, pMaterial->m_TgaFile.c_str ());
TgaParser explosion(tgafile);
}

The END of the TgaParser constructor (there is lots more where it actually opens the file and reads through the bits etc)

unsigned char * imageData = (unsigned char*)getData (fp, size, imageBits);
myId=id;

glBindTexture (GL_TEXTURE_2D, id);
    glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
    glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    /* glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); */
    glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
/* glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); */
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
glTexImage2D (GL_TEXTURE_2D, 0, texFormat, imageWidth, imageHeight, 0, texFormat, GL_UNSIGNED_BYTE, imageData);

    /* release data, its been uploaded */
    free (imageData);

fclose(fp);

id++;

Best Solution

There is no good way to switch textures in the middle of rendering. What you need to do is separate the meshes by texture (material), draw them separately and switch the texture (and possibly uniform values for the material) between draw calls with glBindTexture(GL_TEXTURE_2D, id).

Related Question