UV Coordinates Not Mapping to Object Correctly

If you have any questions on programming, this is the place to ask them, whether you're a newbie or an experienced programmer. Discussion on programming in general is also welcome. We will help you with programming homework, but we will not do your work for you! Any porting requests must be made in Developmental Ideas.
Post Reply
KingCrazy
DCEmu Crazy Poster
DCEmu Crazy Poster
Posts: 33
https://www.artistsworkshop.eu/meble-kuchenne-na-wymiar-warszawa-gdzie-zamowic/
Joined: Sat Aug 04, 2018 11:36 am
Has thanked: 2 times
Been thanked: 7 times

UV Coordinates Not Mapping to Object Correctly

Post by KingCrazy »

This is more of just an OpenGL issue, rather than a KOS specific one, but I'm struggling to get my textures drawing on a quad properly.

I've got a quad, which is rendered by drawing two triangles (of course), modeled and unwrapped using Blender. I'm exporting to MD2 for the time being, and using BMPs for my textures (all PNG tutorials and examples seemed to be broken when it came to actually generating the texture. I kept getting garbage, broken textures).

I'm drawing my objects using glDrawElements, using GL_TRIANGLES (I'm not smart enough to deal with Triangle Strips). In order to do this, I'm taking the texture coordinates from the MD2 file and putting them into an array (an array the size of however many indices I have, of course). When debugging it, everything seems to be printing out right.

Code: Select all

Index: 1
UV: 1,1
Index: 2
UV: 0,0
Index: 3
UV: 1,0
Index: 0
UV: 0,1
Index: 2
UV: 0,0
Index: 1
UV: 1,1
Everything seems to be correct: opposite sides (0 and 3, 1 and 2) have opposite UVs, and everything maps consistently (index 2 is always equal to (0,0), and index 1 is always equal to (1,1))

The problem is, the UV mapping doesn't seem to be working properly. The two triangles act independent of each other when it comes to displaying the texture.

Image

Does anyone have any suggestions on how to fix this? I've been fighting with texturing my models for, like, four days straight and I keep running into stupid problems like this.

Let me preface this next part by saying my code is a mess -- I'm just trying to get functionality working. The BMP loading is done by using a modified version of the ImageLoad() function from GLdc/samples/letabot01. The MD2 model loading is done through a huge function that's based off the code from this site. After I read in all of the data using the aforementioned code, I setup some additional arrays to handle the indices, texture coordinates, and vertices.

Code: Select all

  mdl->num_indices = mdl->header.num_tris*3;

// Allocate space for our list of indices
  mdl->indices = (GLshort*) malloc (sizeof(GLshort) * mdl->num_indices);

  // Populate our list of indices
  j = 0;
  for (i = 0; i < mdl->header.num_tris;i++){
    mdl->indices[j] = mdl->tris[i].vertex[0];
    mdl->indices[j+1] = mdl->tris[i].vertex[1];
    mdl->indices[j+2] = mdl->tris[i].vertex[2];

    j+=3;
  }

  mdl->t_coords = (GLfloat*) malloc (sizeof(GLfloat) * mdl->num_indices * 2);

  k = 0;
  for (i = 0; i < mdl->header.num_tris;i++){
    for (j = 0; j < 3; j++){
      mdl->t_coords[k] = (GLfloat) mdl->texcoords[mdl->tris[i].st[j]].s / (GLfloat) mdl->header.skinwidth;
      mdl->t_coords[k+1] =  (GLfloat) mdl->texcoords[mdl->tris[i].st[j]].t / (GLfloat) mdl->header.skinheight;
     
      k+=2;
    }
  }

  // Allocate space for our list of vertices.
  mdl->verts = (GLfloat*) malloc (sizeof(GLfloat)*mdl->header.num_vertices * 3);
i, j, and k are declared at the top of this function, but since the function is so long (it includes reading in the file) I didn't bother to include it in this snippet. The structs can be seen in that link I provided above (minus some extra variables I added).

Drawing is done here:

Code: Select all

void Screen::DrawObject(md2_model_t* mdl, const Vector3f* position, const Vector3f* rotation, const Vector3f* scale, const float& angle){
  md2_frame_t* mFrame = & mdl->frames[0];     // Our current frame of animation.

  short i, p;

  // Adjust vertices for MD2's compression
  p = 0;
  for (i = 0; i < mdl->header.num_vertices; i++){
    mdl->verts[p] = (mFrame->scale.x * (float)mFrame->verts[i].v[0]) + mFrame->translate.x;
    mdl->verts[p+1] = (mFrame->scale.y * (float)mFrame->verts[i].v[1]) + mFrame->translate.y;
    mdl->verts[p+2] = (mFrame->scale.z * (float)mFrame->verts[i].v[2]) + mFrame->translate.z;
    p+=3;
  }

  // glLoadIdentity() is called at the start of the drawing loop that calls this function.
  // We're pushing that, along with our camera modifications, to the matrix stack, and
  // popping it at the end so we don't have to continuously calculate the camera.
  glPushMatrix();

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

  glTranslatef(position->x,position->y,position->z);            // The object's position
  glRotatef(angle, rotation->x, rotation->y, rotation->z);      // The object's rotation
  glScalef(scale->x, scale->y, scale->z);                       // The object's scale

  glBindTexture(GL_TEXTURE_2D,mdl->tex);

  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

  glTexCoordPointer(2,GL_FLOAT,0,mdl->t_coords);
  glVertexPointer(3,GL_FLOAT,0,mdl->verts);

  glDrawElements(GL_TRIANGLES, mdl->num_indices,GL_SHORT,mdl->indices);

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

  mFrame = NULL;

  glPopMatrix();
}
The reason I'm using GL_SHORT in glDrawElements is because the data for vertices and texture coordinates is read in as a short, as dictated by the MD2 loading code I linked to.

Again, I'm not looking for optimization -- I'm sure most of this is terrible (really wanted to get rid of the nested for-loop but I just needed to get something working for now). I'm just looking for any reason why my object isn't displaying the textures properly.
User avatar
lerabot
Insane DCEmu
Insane DCEmu
Posts: 134
Joined: Sun Nov 01, 2015 8:25 pm
Has thanked: 2 times
Been thanked: 19 times

Re: UV Coordinates Not Mapping to Object Correctly

Post by lerabot »

It seems to me like the mapping is just incorrect. Your code does seem to work.
Did you know that openGL coord start from the bottom left? Cause I feel like you got maybe like 1 axis wrong.

Do you want to try this instead:

Index - UV
0 - 0,0
1 - 1,0
2 - 0,1

2 - 0,1
1 - 1,0
3 - 1,1

Let me know. I use Quad for my game (2D) so I might be of limited knowledge here.
KingCrazy
DCEmu Crazy Poster
DCEmu Crazy Poster
Posts: 33
Joined: Sat Aug 04, 2018 11:36 am
Has thanked: 2 times
Been thanked: 7 times

Re: UV Coordinates Not Mapping to Object Correctly

Post by KingCrazy »

I figured it out (for 2D quads at least).
I have two separate structs for xyz data, and UV data.

Code: Select all

struct vec3_t {
   GLfloat x, y, z;
};

struct uv_t {
   GLfloat u, v;
}
So verts is now an array of type vec3_t, and I have another array, uvs, of type uv_t

Code: Select all

vec3_t* versts;
uv_t* uvs;
I changed the loop where I load the UVs to be

Code: Select all

for (i = 0; i < mdl->header.num_tris;i++){
      for(j=0;j<3;j++){
        mdl->uvs[mdl->tris[i].vertex[j]].u = (GLfloat) mdl->texcoords[mdl->tris[i].st[j]].s / (GLfloat) mdl->header.skinwidth;
        mdl->uvs[mdl->tris[i].vertex[j]].v = 1 - ((GLfloat) mdl->texcoords[mdl->tris[i].st[j]].t / (GLfloat) mdl->header.skinheight);
      }
    }
(1 - v because the texture loads upside down)

I originally tried to interleave the data, so one struct with GLfloats for x, y, z, U and V, but I couldn't get it to work with glDrawElements. The vertices were fine but the texture coordinates were all messed up.

This doesn't work for cubes, though! And likely for any 3D object. What I'm understanding is that I essentially have multiple UVs tied to the same index (e.g. on one face, that vertex might have UVs (0,0) but on a different face it might be (1,0)), so now my goal is to find a work-around for that, or to figure out a proper way to do it.

Thanks very much for the suggestion though! ^^
Post Reply