I have a loader class for *.obj files (called GLModel) which has 2 main methods -

public GLModel(BufferedReader ref, boolean centerit, GL gl,Texture texture)

which use for *.obj load , and

public void opengldraw(GL gl)

which use for draw it on the gl .

so far I succeed to load and draw objects which has single *.obj and a texture (such *.png , *jpg etc) .

But how could I draw an object which has multiple textures ?

For example - this object contains multiple texture , I tried to draw it with the suitable *.obj and one of the texture and got the same shape as the above link but its texture not exactly the same .

Can you supply me an example how to works it correctly ?

Edit:

I made that model available for download from here with no register .

The drawing I got so far is -

(front) -

enter image description here

(back) -

enter image description here

The loader class I talk about is -

import java.io.BufferedReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.StringTokenizer;

import javax.media.opengl.GL;
import com.sun.opengl.util.texture.Texture;

// This class reads Wavefront .obj files
public class GLModel {

    private ArrayList<float[]> vertexsets;
    private ArrayList<float[]> vertexsetsnorms;
    private ArrayList<float[]> vertexsetstexs;
    private ArrayList<int[]> faces;
    private ArrayList<int[]> facestexs;
    private ArrayList<int[]> facesnorms;
    private int objectlist;
    private int numpolys;
    public float toppoint;
    public float bottompoint;
    public float leftpoint;
    public float rightpoint;
    public float farpoint;
    public float nearpoint;
    Texture texture;

    public GLModel(BufferedReader ref, boolean centerit, GL gl, 
            Texture texture){

        this.texture=texture;
        vertexsets = new ArrayList<float[]>();
        vertexsetsnorms = new ArrayList<float[]>();
        vertexsetstexs = new ArrayList<float[]>();
        faces = new ArrayList<int[]>();
        facestexs = new ArrayList<int[]>();
        facesnorms = new ArrayList<int[]>();
        numpolys = 0;
        toppoint = 0.0F;
        bottompoint = 0.0F;
        leftpoint = 0.0F;
        rightpoint = 0.0F;
        farpoint = 0.0F;
        nearpoint = 0.0F;
        loadobject(ref);
        if(centerit)
            centerit();
        opengldrawtolist(gl);
        numpolys = faces.size();
        cleanup();
    }

    public GLModel(BufferedReader ref, boolean centerit, GL gl){

        vertexsets = new ArrayList<float[]>();
        vertexsetsnorms = new ArrayList<float[]>();
        vertexsetstexs = new ArrayList<float[]>();
        faces = new ArrayList<int[]>();
        facestexs = new ArrayList<int[]>();
        facesnorms = new ArrayList<int[]>();
        numpolys = 0;
        toppoint = 0.0F;
        bottompoint = 0.0F;
        leftpoint = 0.0F;
        rightpoint = 0.0F;
        farpoint = 0.0F;
        nearpoint = 0.0F;
        loadobject(ref);
        if(centerit)
            centerit();
        numpolys = faces.size();
        cleanup();
    }

    private void cleanup(){
        vertexsets.clear();
        vertexsetsnorms.clear();
        vertexsetstexs.clear();
        faces.clear();
        facestexs.clear();
        facesnorms.clear();
    }

    @SuppressWarnings("unused")
    private void loadobject(BufferedReader br){
        int linecounter = 0;
        int facecounter = 0;
        try{
            boolean firstpass = true;
            String newline;
            while((newline = br.readLine()) != null){
                linecounter++;
                if(newline.length() > 0){
                    newline = newline.trim();

                    //LOADS VERTEX COORDINATES
                    if(newline.startsWith("v ")){
                        float coords[] = new float[4];
                        newline = newline.substring(2, newline.length());
                        StringTokenizer st = new StringTokenizer(newline, " ");
                        for(int i = 0; st.hasMoreTokens(); i++)
                            coords[i] = Float.parseFloat(st.nextToken());

                        if(firstpass){
                            rightpoint = coords[0];
                            leftpoint = coords[0];
                            toppoint = coords[1];
                            bottompoint = coords[1];
                            nearpoint = coords[2];
                            farpoint = coords[2];
                            firstpass = false;
                        }
                        if(coords[0] > rightpoint)
                            rightpoint = coords[0];
                        if(coords[0] < leftpoint)
                            leftpoint = coords[0];
                        if(coords[1] > toppoint)
                            toppoint = coords[1];
                        if(coords[1] < bottompoint)
                            bottompoint = coords[1];
                        if(coords[2] > nearpoint)
                            nearpoint = coords[2];
                        if(coords[2] < farpoint)
                            farpoint = coords[2];
                        vertexsets.add(coords);
                    }
                    else

                    //LOADS VERTEX TEXTURE COORDINATES
                    if(newline.startsWith("vt")){
                        float coords[] = new float[4];
                        newline = newline.substring(3, newline.length());
                        StringTokenizer st = new StringTokenizer(newline, " ");
                        for(int i = 0; st.hasMoreTokens(); i++)
                            coords[i] = Float.parseFloat(st.nextToken());

                        vertexsetstexs.add(coords);
                    }
                    else

                    //LOADS VERTEX NORMALS COORDINATES
                    if(newline.startsWith("vn")){
                        float coords[] = new float[4];
                        newline = newline.substring(3, newline.length());
                        StringTokenizer st = new StringTokenizer(newline, " ");
                        for(int i = 0; st.hasMoreTokens(); i++)
                            coords[i] = Float.parseFloat(st.nextToken());

                        vertexsetsnorms.add(coords);
                    }
                    else

                    //LOADS FACES COORDINATES
                    if(newline.startsWith("f ")){
                        facecounter++;
                        newline = newline.substring(2, newline.length());
                        StringTokenizer st = new StringTokenizer(newline, " ");
                        int count = st.countTokens();
                        int v[] = new int[count];
                        int vt[] = new int[count];
                        int vn[] = new int[count];
                        for(int i = 0; i < count; i++){
                            char chars[] = st.nextToken().toCharArray();
                            StringBuffer sb = new StringBuffer();
                            char lc = 'x';
                            for(int k = 0; k < chars.length; k++){
                                if(chars[k] == '/' && lc == '/')
                                    sb.append('0');
                                lc = chars[k];
                                sb.append(lc);
                            }

                            StringTokenizer st2 = new StringTokenizer
                            (sb.toString(), "/");
                            int num = st2.countTokens();
                            v[i] = Integer.parseInt(st2.nextToken());
                            if(num > 1)
                                vt[i] = Integer.parseInt(st2.nextToken());
                            else
                                vt[i] = 0;
                            if(num > 2)
                                vn[i] = Integer.parseInt(st2.nextToken());
                            else
                                vn[i] = 0;
                        }

                        faces.add(v);
                        facestexs.add(vt);
                        facesnorms.add(vn);
                    }
                    else
                        //LOADS MATERIALS
                        if (newline.charAt(0) == 'm' && newline.charAt(1) == 't' && newline.charAt(2) == 'l' && newline.charAt(3) == 'l' && newline.charAt(4) == 'i' && newline.charAt(5) == 'b') {}
                        else
                            //USES MATERIALS
                            if (newline.charAt(0) == 'u' && newline.charAt(1) == 's' && newline.charAt(2) == 'e' && newline.charAt(3) == 'm' && newline.charAt(4) == 't' && newline.charAt(5) == 'l') {}
                }
            }
        }
        catch(IOException e){
            System.out.println("Failed to read file: " + br.toString());
        }
        catch(NumberFormatException e){
            System.out.println("Malformed OBJ file: " + br.toString() + "\r \r"+ e.getMessage());
        }
    }

    private void centerit(){
        float xshift = (rightpoint - leftpoint) / 2.0F;
        float yshift = (toppoint - bottompoint) / 2.0F;
        float zshift = (nearpoint - farpoint) / 2.0F;
        for(int i = 0; i < vertexsets.size(); i++){
            float coords[] = new float[4];
            coords[0] = ((float[])vertexsets.get(i))[0] - leftpoint - xshift;
            coords[1] = ((float[])vertexsets.get(i))[1] - bottompoint - yshift;
            coords[2] = ((float[])vertexsets.get(i))[2] - farpoint - zshift;
            vertexsets.set(i, coords);
        }
    }

    public float getXWidth(){
        float returnval = 0.0F;
        returnval = rightpoint - leftpoint;
        return returnval;
    }

    public float getYHeight(){
        float returnval = 0.0F;
        returnval = toppoint - bottompoint;
        return returnval;
    }

    public float getZDepth(){
        float returnval = 0.0F;
        returnval = nearpoint - farpoint;
        return returnval;
    }

    public int numpolygons(){
        return numpolys;
    }

    public void opengldrawtolist(GL gl){
        // retrieving object list
        this.objectlist = gl.glGenLists(1);

        gl.glNewList(objectlist, GL.GL_COMPILE);
        texture.enable();
        texture.bind();
        gl.glTexParameteri(GL.GL_TEXTURE_2D,GL.GL_TEXTURE_WRAP_T, GL.GL_REPEAT);
        gl.glTexParameteri(GL.GL_TEXTURE_2D,GL.GL_TEXTURE_WRAP_S, GL.GL_REPEAT);
        gl.glTexParameteri(GL.GL_TEXTURE_2D,GL.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR);
        gl.glTexParameteri(GL.GL_TEXTURE_2D,GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR);

        for (int i=0;i<faces.size();i++) {

            int[] tempfaces = (int[])(faces.get(i));
            int[] tempfacesnorms = (int[])(facesnorms.get(i));
            int[] tempfacestexs = (int[])(facestexs.get(i));

            //// Quad Begin Header ////
            int polytype;
            if (tempfaces.length == 3) {
                    polytype = GL.GL_TRIANGLES;
            } else if (tempfaces.length == 4) {
                    polytype = GL.GL_QUADS;
            } else {
                    polytype = GL.GL_POLYGON;
            }
            gl.glBegin(polytype);

            for (int w=0;w<tempfaces.length;w++) {
                if (tempfacesnorms[w] != 0) {
                    float normtempx = ((float[])vertexsetsnorms.get(tempfacesnorms[w] - 1))[0];
                    float normtempy = ((float[])vertexsetsnorms.get(tempfacesnorms[w] - 1))[1];
                    float normtempz = ((float[])vertexsetsnorms.get(tempfacesnorms[w] - 1))[2];
                    gl.glNormal3f(normtempx, normtempy, normtempz);
                }

                if (tempfacestexs[w] != 0) {
                    float textempx = ((float[])vertexsetstexs.get(tempfacestexs[w] - 1))[0];
                    float textempy = ((float[])vertexsetstexs.get(tempfacestexs[w] - 1))[1];
                    float textempz = ((float[])vertexsetstexs.get(tempfacestexs[w] - 1))[2];
                    gl.glTexCoord3f(textempx,1f-textempy,textempz);
                }

                float tempx = ((float[])vertexsets.get(tempfaces[w] - 1))[0];
                float tempy = ((float[])vertexsets.get(tempfaces[w] - 1))[1];
                float tempz = ((float[])vertexsets.get(tempfaces[w] - 1))[2];
                gl.glVertex3f(tempx,tempy,tempz);
            }
            // Quad End

            gl.glEnd();
        }

        gl.glEndList();
    }

    public void opengldraw(GL gl) {
        gl.glCallList(objectlist);
    }

    public boolean collision(float[] position) {
            return false;
    }

    public void setTexature(Texture tex) {
            this.texture = tex;
    }
}

The *.obj I used it is - Zero.obj (after convert it to BufferedReader) , the image I used it is - AssassinBody_Dif.jpg (after convert it to Texture) .

What I looking for is - how to get completely drawing of that model using the attached GLModel class ?

  • can you show us what your rendering looks like ? – concept3d Nov 17 '13 at 9:21
  • sure , look at the edited post please . – URL87 Nov 17 '13 at 9:49
  • That is indeed looking weird. At first I thought you had wrong texture coordinates, but then again it looks a bit like the render on the site from front, so texture coordinates might be correct. I actually have no idea about this one. – Lasse Nov 17 '13 at 9:56
  • But yes there are normal maps and specular maps in addition to diffuse maps in the zip file, as I guessed. – Lasse Nov 17 '13 at 9:59
  • Oh, I think I see it now. The head, chest and some other parts are separate, and they all have different textures. Just like I thought in my answer's first paragraph. – Lasse Nov 17 '13 at 10:01
up vote 3 down vote accepted

Wavefront obj files usually are combined with material files (.mat) which define the textures used. The meshes of a model are divided in different material groups. Each material tells you which textures to use and other information of the mesh.

In your case, there are separate meshes on the model. These meshes are head, body and hands. They all have their own textures, which you will need to apply to the correct meshes for it to look right. The meshes are most likely separated in the .obj file in mesh groups, material groups or lighting groups. You need to read them in separate mesh instances to be able to render them with different textures.

You also have encountered something called normal mapping, which is in essence saving some of the high-poly model's mesh data in textures for use with low-poly model.

  • can you elaborate how he might have encountered normal mapping? objs and their material files do not define normal maps nor multi-texture or multi-tex coordinates. – concept3d Nov 17 '13 at 9:11
  • @concept3d I am just looking at the final render of the model on the site he linked, and that looks like it's been normal mapped and specular highlighted. And it is not unheard of to .obj materials to have normal maps and specular maps. en.wikipedia.org/wiki/… – Lasse Nov 17 '13 at 9:15
  • I just checked it, well it might be. Even though I doubt obj stores bump maps info, the image might not represent the real model. – concept3d Nov 17 '13 at 9:22
  • Also, the site suggests that the .obj file is not the original file, but just a version of it. It says on the page this: Format: OBJ (.obj), Poser (.pz3, .pp2) – Lasse Nov 17 '13 at 9:22
  • 1
    I edited the answer. – Lasse Nov 17 '13 at 10:09

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

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