2

I am working on fixing the following project: objLoader

It works well, loading in an OBJ I found.

However, the project was missing code to deal with mapping a texture onto the object, so I have been adding it in.

I can now load my OBJ (a banana) and I can in fact see a texture on it!

BUT, the texture is not mapped properly. It appears to be tiled and distorted (see below)

image

Below is my code:

TDModel.java

public void draw(GL10 gl) {
    gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[0]); // bind texture

    gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer);
    gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, textureBuffer);
    gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
    gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);

    for(int i=0; i<parts.size(); i++){
        TDModelPart t=parts.get(i);
        Material m=t.getMaterial();
        if(m!=null){
            FloatBuffer a=m.getAmbientColorBuffer();
            FloatBuffer d=m.getDiffuseColorBuffer();
            FloatBuffer s=m.getSpecularColorBuffer();
            gl.glMaterialfv(GL10.GL_FRONT_AND_BACK,GL10.GL_AMBIENT,a);
            gl.glMaterialfv(GL10.GL_FRONT_AND_BACK,GL10.GL_SPECULAR,s);
            gl.glMaterialfv(GL10.GL_FRONT_AND_BACK,GL10.GL_DIFFUSE,d);
        }
        gl.glEnableClientState(GL10.GL_NORMAL_ARRAY);
        gl.glNormalPointer(GL10.GL_FLOAT, 0, t.getNormalBuffer());
        gl.glDrawElements(GL10.GL_TRIANGLES,t.getFacesCount(),GL10.GL_UNSIGNED_SHORT,t.getFaceBuffer());
        gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
        gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
    }

}

public void loadGLTexture(GL10 gl, Context context) {
    // loading texture
    InputStream is = null;
    Bitmap bitmap = null;
    try {
        is = context.getAssets().open("banana.jpg");

        bitmap = BitmapFactory.decodeStream(is);
    } catch (IOException e) {
        e.printStackTrace();
    }

    gl.glGenTextures(1, textures, 0);
    // ...and bind it to our array
    gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[0]);

    // create nearest filtered texture
    gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST);
    gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);

    gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, GL10.GL_CLAMP_TO_EDGE);
    gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, GL10.GL_CLAMP_TO_EDGE);

    // Use Android GLUtils to specify a two-dimensional texture image from our bitmap
    GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);

    // Clean up
    bitmap.recycle();
}

MyRenderer.java

public void onSurfaceCreated(GL10 gl, EGLConfig config) {

    model.loadGLTexture(gl, getContext()); // load texture

    gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_AMBIENT, lightAmbientBuffer);
    gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_DIFFUSE, lightDiffuseBuffer);      
    gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_POSITION, lightPositionBuffer);    
    gl.glEnable(GL10.GL_LIGHT0);

    gl.glEnable(GL10.GL_TEXTURE_2D); // Enable texture
    gl.glShadeModel(GL10.GL_SMOOTH);            
    gl.glClearColor(0.0f, 0.0f, 0.0f, 0.5f);
    gl.glClearDepthf(1.0f);                     
    gl.glEnable(GL10.GL_DEPTH_TEST);            
    gl.glDepthFunc(GL10.GL_LEQUAL);         

    gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_NICEST); 
}

Any ideas why its not wrapping correctly?

Here is the texture

enter image description here

genpfault
  • 51,148
  • 11
  • 85
  • 139
Mr Pablo
  • 4,109
  • 8
  • 51
  • 104

1 Answers1

1

The real problem is with indices in your OBJ file.

Every attribute: pos, normal, texcoord use separate indexing.

So your faces specify which index should be used for every attribute separately on each of its 3 vertices.

You need to rearrange normals and texcoords so their indices will match vertex position indices.

Simplest solution is to allocate new array (not indexed with vertices count = 3 * face count) and fill it manually looking up attributes from indexed data.

Then you can draw it using DrawArrays (instead of DrawElements)

int faces, vertices, normals, texcoords;
int indexes[faces][3][3]; // currently your indices [face][vertex][attrib_index]
float vertex[vertices][3]; // your vertex positions
float normal[normals][3]; // your normals
float texcoord[texcoords][3]; // your texcoords

you need to convert to:

int vertices = 3*faces;
float vertex2[vertices][3]; // your vertex positions
float normal2[vertices][3]; // your normals
float texcoord2[vertices][2]; // your texcoords

in following way:

int v=0;
for (int f=0; f<faces; f++)
{

    for (int fv=0; fv<3; fv++,v++)
    {
        vertex2[v][0] = vertex[ indexes[f][fv][0] ][0];
        vertex2[v][1] = vertex[ indexes[f][fv][0] ][1];
        vertex2[v][2] = vertex[ indexes[f][fv][0] ][2];

        normal2[v][0] = normal[ indexes[f][fv][1] ][0];
        normal2[v][1] = normal[ indexes[f][fv][1] ][1];
        normal2[v][2] = normal[ indexes[f][fv][1] ][2];

        texcoord2[v][0] = texcoord[ indexes[f][fv][2] ][0];
        texcoord2[v][1] = texcoord[ indexes[f][fv][2] ][1];
    }
}
Anonymous
  • 2,122
  • 19
  • 26
  • So how do I go about doing this? Do I need to re-export the OBJ with specific settings? Or can I achieve this with code? I don't want to have to manually edit the OBJ file... – Mr Pablo Dec 19 '14 at 15:26
  • Where would I introduce this code? And use it in DrawArray? – Mr Pablo Dec 19 '14 at 16:00
  • You need to do this convertion after reading OBJ file and prior to your draw call. In your Draw() func you should specify these new (converted) arrays to glXXXPointer() calls. Then simply call glDrawArrays(GL_TRIANGLES,0,3*faces); - in place of glDrawElements – Anonymous Dec 19 '14 at 16:04
  • I'll take a look. Quick question - is there a better way to read the obj file? this one isn't too bad, but its a test. My actual OBJs will be big, around 400,000 lines to read through. – Mr Pablo Dec 19 '14 at 16:09
  • You can also make a blind try (as I cannot see your OBJ file parser): to change number of tex coordinates from 2 to 3: gl.glTexCoordPointer(3, GL10.GL_FLOAT, 0, textureBuffer); – Anonymous Dec 19 '14 at 16:09
  • Yes it is always better to convert it once to some your app friendly binary form :) – Anonymous Dec 19 '14 at 16:10
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/67342/discussion-between-mr-pablo-and-anonymous). – Mr Pablo Dec 19 '14 at 16:14