? Texturing many triangles - 3DS loader question ?

I am trying to figure out how to correctly texture an object that is comprised of many triangles and each triangle has an assigned texture id. What is the proper way to do it?

If you want to draw a bunch of triangles you can do one begin-end call and encapsulate all of the triangle drawing in it…I think this saves performance.


  glBegin(GL_TRIANGLES);
  for (triangle = 1 to numTriangles) {
    for (vertex = 1 to 3) {
      glNormal(normal);
      glVertex(vertex);
    } 
  }
  glEnd();

I’m trying to figure out how to render .3DS (3D Studio Max) models correctly in JOGL (Java OpenGL) - fyi, this is really no different than pure OpenGL. I had code (originally JOGLUTILS 3DS loader) that I fixed up pretty well and it worked fine until I found out that each object in a 3DS file can actually have more than one texture associated with it. The previous developers thought that each object had 1 texture total, but in reality the textures are not assigned to the object itself but rather to the individual faces/triangles.

So you can have a list of triangles with their material/texture ids such as this, where the same materialId can be assigned to multiple triangles/faces and not necessarily contiguous. So the original assumption in the code that I got was only valid if all of the materialId were referencing just one texture…but that was incorrect.


 Tri#   face1    face2    face3    materialId
  1       1        2       3            1
  2       3        2       4            1
  3       4        5       6            1
  4       6        3       5            2
  5       1        15      16           2
  6       3        4       8            1
  7       15       12      4            3
 ...

Before, when I thought I had one texture for the entire object, I would just do this (effectively that was the original code)


  glEnable(GL_TEXTURE_2D);
  texture.enable();
  texture.bind();

  glBegin(GL_TRIANGLES);
  for (triangle = 1 to numTriangles) {
    for (vertex = 1 to 3) {
      glNormal(normal);
      glVertex(vertex);
      glTextCoord2f(x,y);
    } 
  }
  glEnd();

  texture.disable();
  glDisable(GL_TEXTURE_2D);

But, as it turns out, each face can have a different material/texture id.
As a result, instead of having one texture that I can just blindly apply to the entire triangle loop above, I have to individually assign the textures based on what each face/triangle should have. So I tried to do this code where I move the GL_TRIANGLES and binding of the texture to inside the for-loop:


  glEnable(GL_TEXTURE_2D);
  for (i = 1 to numTextures) {
    texture[i].enable();
  }

  
  for (triangle = 1 to numTriangles) {
    glBegin(GL_TRIANGLES);   // Even tried GL_TRIANGLE_FAN
    texture[triangle.textureId].bind();
    for (vertex = 1 to 3) {
      glNormal(normal);
      glVertex(vertex);
      glTextCoord2f(x,y);
    }
    glEnd();
  }
  
  for (i = 1 to numTextures) {
    texture[i].disable();
  }
  glDisable(GL_TEXTURE_2D);

In doing so, I moved the GL_TRIANGLES begin-end block and use to for each separate triangle I draw. I believe this move is is killing my performance but the texturing looks correct. When I move it out of the loop (as in the 2nd code snip) I get better performance but the texture is incorrect. I tried wrapping this in a Display List but it takes a while for the display list to get generated…which is also weird to me because I can make a display list around the original code and it is fast. I’m guessing by changing from one begin-end call for GL_TRIANGLES to multiple begin-end calls increases the size of the display list and hence it is slower.

Can someone tell me if what I’m doing is correct? I’m not asking if the code is exactly correct, I know I’m not showing you actual code but rather pseudo-code. I just want to know if it makes sense to call the texture binding withing the triangle loop and also is that where I put the GL_TRIANGLES begin-end block or is there a better way to do this?

Thanks.

FYI, the first time I use the glCallList() is slow as heck to render the display list, but I believe that is expected since that display list is uploaded to the memory. It is frustrating because it is extremely slow with this new rendering method so I need to figure out how to render it smarter than what I am doing now - assuming I can.

It looks like you are using GL 1.0 code. I suggest that you move into at least into the GL 2.0 era with VBO, shaders, vertex attribute pointers.

Display lists are old news. http://www.opengl.org/wiki/FAQ#Display_List_or_VA_or_VBO

and you can’t bind a texture inside a glBegin/glEnd block.
I suggest that you debug your code with glGetError.

In the end, you will have to sort your object for better performance. Make a few buckets. Objects that need texture 1 go into bucket 1. Bind texture 1 and render all objects in bucket 1.

for(bucket=x)
{
bind texture bucket(x);
render triangle in bucket(x);
}

The glVertex call is the function that issues a vertex to OpenGL with all the current values for normals/texturecoords/colors etc, so you should set the texture coordinate prior to calling it, rather than afterwards.

Instead of:


      glNormal(normal);
      glVertex(vertex);
      glTextCoord2f(x,y);

It should be:


      glNormal(normal);
      glTexCoord2f(x,y);
      glVertex(vertex);

You will probably also want to sort the triangles by material, that way you don’t have to switch state as often.
eg. you would use material 1 + draw triangles 1,2,3 + 6, then use material 2 + draw triangles 4 + 5, then use material 3 + draw triangle 7

Rather than using immediate mode, you should really be using buffer objects + vertex arrays too.

Thank you Dan and V-man for the replies and suggestions.

I had the feeling that I probably need to sort the triangles but I was trying to see how bad it was going to be in the current state. Do you suggest sorting the triangles and saving that sorted information separately or sorting during each display call. I figure saving that sorted info would be faster for rendering later, but it would cost memory space - I guess the memory saving is not that big of an issue.

I was also doing IMMEDIATE mode (and hence OpenGL v1.0) because I wanted to make support all methods. I’ve had slowdowns with Vertex Arrays and VBOs before so I figured I should do IMMEDIATE mode then wrap that immediate mode in a display list so I can support DISPLAY LISTS. Plus I wanted to be able to do IMMEDIATE mode so that I can verify that my VA and VBO modes looked the same.

Thank you V-man for the info about not binding textures inside begin/end blocks - didn’t know that. I don’t use much of the error checking and that’s probably not a wise idea.

I will have another question about VA & VBO rendering for this specific case in a day or two - I need to look at my work code to ask the question. I’m basically not sure how I do this same rendering using VA or VBO since I think I only create one buffer array of textures and I guess I will have to create multiple ones and render them separately? Sorry…I’ll ask my question when I look at the code.

Thank you very much for your info.

fyi, the bucket idea worked (as you would expect) for Immediate mode (or Direct mode or whatever it is called). Now I am trying to make sure I have optimized it so that it at least performs as my original one material version code did when I only have one material. If I can be as fast as before then I know I’m good to go.

next will still be the VA & VBO work but I think I understand things better now. I will come back if I have problems. Thank you.