Loading and drawing .obj models

classic Classic list List threaded Threaded
12 messages Options
Reply | Threaded
Open this post in threaded view
|

Loading and drawing .obj models

Dale
I am currently in the process of implementing a simple model viewer. I am having some trouble with coding a .obj loader(taking a input model and passing it to my jogl code to draw to my canvas). I have found an existing loader in one of the jogl demo's but i am unsure on how to incorporate it into my own program. I was just wondering if any of you gurus had any tips on writing a .obj loader (or getting the loader from the shadow(i think) demo) and then applying a texture if needed?

Thanks in advance
Reply | Threaded
Open this post in threaded view
|

Re: Loading and drawing .obj models

Demoscene Passivist
Administrator
U may have a look at my port of the .obj loader from JGL-Mark for JOGL2: WavefrontObjectLoader_DisplayList.java

The class has a convenience method loadWavefrontObjectAsDisplayList(GL2 inGL,String inFileName) wich returns a display list id to be used out-of-the-box in ur code. Guess this class is as easy as it gets ...
Reply | Threaded
Open this post in threaded view
|

Re: Loading and drawing .obj models

Demoscene Passivist
Administrator
> ... and then applying a texture if needed?

Ah and btw: For a complete demo of using the .obj loader class in conjunction with a uv-unwrapped, textured polygon exported  from Blender u may have a look here GL2_UVUnwrap.java
Reply | Threaded
Open this post in threaded view
|

Re: Loading and drawing .obj models

Dale
Thank you for the helpful reply Demoscene Passivist! However, the first link does not work :(

Also, how easy would it be to change the code to work with pre-JOGL2 as i started this program a few months before JOGL2 was released.....
Reply | Threaded
Open this post in threaded view
|

Re: Loading and drawing .obj models

Demoscene Passivist
Administrator
Ups, sorry here is the correct link: WavefrontObjectLoader_DisplayList.java

>Also, how easy would it be to change the code to work with pre-JOGL2 as i started
>this program a few months before JOGL2 was released.....

It should be very easy to do so as u mainly have to change the imports and remove the GLProfile stuff.

The original source from JGL-Mark was JOGL1, but unfortunately the site https://jglmark.dev.java.net/ is offline since Oracle more or less killed java.net. Anyway u are lucky that in my ongoing mission to backup the whole internet I also came by to stop at jglmark . The original version is missing the display list feature, and also can't load compressed models but I guess u can easily backport that functionality from my extended version. So here we go with the original JOGL1 version from jglmark.dev.java.net:

package OpenGL.JOGL.loaders.obj;

import com.sun.opengl.util.BufferUtil;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.FloatBuffer;
import java.util.ArrayList;
import javax.media.opengl.GL;

/*
 * OBJModel.java
 * Created on June 3, 2006, 11:44 AM
 */

/**
 * @author Chris "Crash0veride007" Brown
 * crash0veride007@gmail.com
 * https://jglmark.dev.java.net/
 */

public class OBJModel {
    private String OBJModelPath; // The path to the Model File
    private ArrayList vData = new ArrayList(); // List of Vertex Coordinates
    private ArrayList vtData = new ArrayList(); // List of Texture Coordinates
    private ArrayList vnData = new ArrayList(); // List of Normal Coordinates
    private ArrayList fv = new ArrayList(); // Face Vertex Indices
    private ArrayList ft = new ArrayList(); // Face Texture Indices
    private ArrayList fn = new ArrayList(); // Face Normal Indices
    private FloatBuffer modeldata; // Buffer which will contain vertice data
    private int FaceFormat; // Format of the Faces Triangles or Quads
    private int FaceMultiplier; // Number of possible coordinates per face
    private int PolyCount = 0; // The Models Polygon Count
    private boolean init  = true;
    
    public OBJModel(String Modelpath) {
        OBJModelPath = Modelpath;
        LoadOBJModel(OBJModelPath);
        SetFaceRenderType();
    }
    
    private void LoadOBJModel(String ModelPath) {
        try {
            // Open a file handle and read the models data
            BufferedReader br = new BufferedReader(new FileReader(ModelPath));
            String  line = null;
            while((line = br.readLine()) != null) {
                if (line.startsWith("#")) { // Read Any Descriptor Data in the File
                    //System.out.println("Descriptor: "+line); //Uncomment to print out file descriptor data
                } else if (line.equals("")) {
                    //Ignore whitespace data
                } else if (line.startsWith("v ")) { // Read in Vertex Data
                    vData.add(ProcessData(line));
                } else if (line.startsWith("vt ")) { // Read Texture Coordinates
                    vtData.add(ProcessData(line));
                } else if (line.startsWith("vn ")) { // Read Normal Coordinates
                    vnData.add(ProcessData(line));
                } else if (line.startsWith("f ")) { // Read Face Data
                    ProcessfData(line);
                }
            }
            br.close();
        } catch(IOException e) {
            System.out.println("Failed to find or read OBJ: " + ModelPath);
            System.err.println(e);
        }
    }
    
    private float[] ProcessData(String read) {
        final String s[] = read.split("\\s+");
        return (ProcessFloatData(s)); //returns an array of processed float data
    }
    
    private float[] ProcessFloatData(String sdata[]) {
        float data[] = new float[sdata.length-1];
        for (int loop=0; loop < data.length; loop++) {
            data[loop] = Float.parseFloat(sdata[loop+1]);
        }
        return data; // return an array of floats
    }
    
    private void ProcessfData(String fread) {
        PolyCount++;
        String s[] = fread.split("\\s+");
        if (fread.contains("//")) { // Pattern is present if obj has only v and vn in face data
            for (int loop=1; loop < s.length; loop++) {
                s[loop] = s[loop].replaceAll("//","/0/"); //insert a zero for missing vt data
            }
        }
        ProcessfIntData(s); // Pass in face data
    }
    
    private void ProcessfIntData(String sdata[]) {
        int vdata[] = new int[sdata.length-1];
        int vtdata[] = new int[sdata.length-1];
        int vndata[] = new int[sdata.length-1];
        for (int loop = 1; loop < sdata.length; loop++) {
            String s = sdata[loop];
            String[] temp = s.split("/");
            vdata[loop-1] = Integer.valueOf(temp[0]); //always add vertex indices
            if (temp.length > 1) { // we have v and vt data
                vtdata[loop-1] = Integer.valueOf(temp[1]); // add in vt indices
            } else {
                vtdata[loop-1] = 0; // if no vt data is present fill in zeros
            }
            if (temp.length > 2) { // we have v, vt, and vn data
                vndata[loop-1] = Integer.valueOf(temp[2]); // add in vn indices
            } else {
                vndata[loop-1] = 0; //if no vn data is present fill in zeros
            }
        }
        fv.add(vdata);
        ft.add(vtdata);
        fn.add(vndata);
    }
    
    private void SetFaceRenderType() {
        final int temp [] = (int[]) fv.get(0);
        if ( temp.length == 3) { 
            FaceFormat = GL.GL_TRIANGLES; // The faces come in sets of 3 so we have triangular faces
            FaceMultiplier = 3;
        } else if (temp.length == 4) {
            FaceFormat = GL.GL_QUADS; // The faces come in sets of 4 so we have quadrilateral faces
            FaceMultiplier = 4;
        } else {
            FaceFormat = GL.GL_POLYGON; // Fall back to render as free form polygons
        }
    }
    
    private void ConstructInterleavedArray(GL gl) {
        final int tv[] = (int[]) fv.get(0);
        final int tt[] = (int[]) ft.get(0);
        final int tn[] = (int[]) fn.get(0);
        // If a value of zero is found that it tells us we don't have that type of data
        if ((tv[0] != 0) && (tt[0] != 0) && (tn[0] != 0)) {
            ConstructTNV(); //We have Vertex, 2D Texture, and Normal Data
            gl.glInterleavedArrays(GL.GL_T2F_N3F_V3F, 0, modeldata);
        } else if ((tv[0] != 0) && (tt[0] != 0) && (tn[0] == 0)) {
            ConstructTV(); //We have just vertex and 2D texture Data
            gl.glInterleavedArrays(GL.GL_T2F_V3F, 0, modeldata);
        } else if ((tv[0] != 0) && (tt[0] == 0) && (tn[0] != 0)) {
            ConstructNV(); //We have just vertex and normal Data
            gl.glInterleavedArrays(GL.GL_N3F_V3F, 0, modeldata);
        } else if ((tv[0] != 0) && (tt[0] == 0) && (tn[0] == 0)) {
            ConstructV();
            gl.glInterleavedArrays(GL.GL_V3F, 0, modeldata);
        }
    }
    
    private void ConstructTNV() {
        int[] v, t, n;
        float tcoords[] = new float[2]; //Only T2F is supported in InterLeavedArrays!!
        float coords[] = new float[3];        
        int fbSize= PolyCount*(FaceMultiplier*8); // 3v Per Poly, 2vt Per Poly, 3vn Per Poly
        modeldata = BufferUtil.newFloatBuffer(fbSize);
        modeldata.position(0);     
        for (int oloop=0; oloop < fv.size(); oloop++) {
            v = (int[])(fv.get(oloop));
            t = (int[])(ft.get(oloop));
            n = (int[])(fn.get(oloop));
            for (int iloop=0; iloop < v.length; iloop++) {
                // Fill in the texture coordinate data
                for (int tloop=0; tloop < tcoords.length; tloop++) //Only T2F is supported in InterLeavedArrays!!
                    tcoords[tloop] = ((float[])vtData.get(t[iloop] - 1))[tloop];
                modeldata.put(tcoords);
                // Fill in the normal coordinate data
                for (int vnloop=0; vnloop < coords.length; vnloop++)
                    coords[vnloop] = ((float[])vnData.get(n[iloop] - 1))[vnloop];
                modeldata.put(coords);
                // Fill in the vertex coordinate data
                for (int vloop=0; vloop < coords.length; vloop++)
                    coords[vloop] = ((float[])vData.get(v[iloop] - 1))[vloop];
                modeldata.put(coords);
            }
        }
        modeldata.position(0); 
    }
    
    private void ConstructTV() {
        int[] v, t;
        float tcoords[] = new float[2]; //Only T2F is supported in InterLeavedArrays!!
        float coords[] = new float[3];
        int fbSize= PolyCount*(FaceMultiplier*5); // 3v Per Poly, 2vt Per Poly
        modeldata = BufferUtil.newFloatBuffer(fbSize);        
        modeldata.position(0);
        for (int oloop=0; oloop < fv.size(); oloop++) {
            v = (int[])(fv.get(oloop));
            t = (int[])(ft.get(oloop));
            for (int iloop=0; iloop < v.length; iloop++) {
                // Fill in the texture coordinate data
                for (int tloop=0; tloop < tcoords.length; tloop++) //Only T2F is supported in InterLeavedArrays!!
                    tcoords[tloop] = ((float[])vtData.get(t[iloop] - 1))[tloop];                
                modeldata.put(tcoords);
                // Fill in the vertex coordinate data
                for (int vloop=0; vloop < coords.length; vloop++)
                    coords[vloop] = ((float[])vData.get(v[iloop] - 1))[vloop];
                modeldata.put(coords);
            }
        }
        modeldata.position(0);
    }
    
    private void ConstructNV() {
        int[] v, n;
        float coords[] = new float[3];
        int fbSize= PolyCount*(FaceMultiplier*6); // 3v Per Poly, 3vn Per Poly
        modeldata = BufferUtil.newFloatBuffer(fbSize);
        modeldata.position(0);
        for (int oloop=0; oloop < fv.size(); oloop++) {
            v = (int[])(fv.get(oloop));
            n = (int[])(fn.get(oloop));
            for (int iloop=0; iloop < v.length; iloop++) {
                // Fill in the normal coordinate data
                for (int vnloop=0; vnloop < coords.length; vnloop++)
                    coords[vnloop] = ((float[])vnData.get(n[iloop] - 1))[vnloop];
                modeldata.put(coords);
                // Fill in the vertex coordinate data
                for (int vloop=0; vloop < coords.length; vloop++)
                    coords[vloop] = ((float[])vData.get(v[iloop] - 1))[vloop];
                modeldata.put(coords);
            }
        }
        modeldata.position(0);
    }
    
    private void ConstructV() {
        int[] v;
        float coords[] = new float[3];
        int fbSize= PolyCount*(FaceMultiplier*3); // 3v Per Poly
        modeldata = BufferUtil.newFloatBuffer(fbSize);
        modeldata.position(0);
        for (int oloop=0; oloop < fv.size(); oloop++) {
            v = (int[])(fv.get(oloop));
            for (int iloop=0; iloop < v.length; iloop++) {
                // Fill in the vertex coordinate data
                for (int vloop=0; vloop < coords.length; vloop++)
                    coords[vloop] = ((float[])vData.get(v[iloop] - 1))[vloop];
                modeldata.put(coords);
            }
        }
        modeldata.position(0);
    }
       
    public void DrawModel(GL gl) {
        if (init) {
            ConstructInterleavedArray(gl);
            cleanup();
            init = false;
        }
        gl.glEnable(GL.GL_CULL_FACE);
        gl.glCullFace(GL.GL_BACK);
        gl.glPolygonMode(GL.GL_FRONT,GL.GL_LINE);
        gl.glDrawArrays(FaceFormat, 0, PolyCount*FaceMultiplier);
        gl.glDisable(GL.GL_CULL_FACE);
    }
    
     public String Polycount() {
        String pc = Integer.toString(PolyCount);
        return pc;
    }
    
    private void cleanup() {
        vData.clear();
        vtData.clear();
        vnData.clear();
        fv.clear();
        ft.clear();
        fn.clear();
        modeldata.clear();
        System.gc();
    }
}
Reply | Threaded
Open this post in threaded view
|

Re: Loading and drawing .obj models

diskhub
Hi there,
I saw your latest on the WavefrontObjectLoader_Displaylist.java
I am wondering where i could get the library for "framework.base"?

And how can I use the loadWavefrontObjectAsDisplayList method?
Any quick sample help?

Thanks
Reply | Threaded
Open this post in threaded view
|

Re: Loading and drawing .obj models

Demoscene Passivist
Administrator
>I am wondering where i could get the library for "framework.base"?

The "framework.base" package is part of my JOGL2 development framework. U can download it from my github repository. Feel free to use/modify it

>And how can I use the loadWavefrontObjectAsDisplayList method?

For a simple example take a look at GL2_UVUnwrap.java. The usage comes down to only two lines:

init+loading:
int mDisplayListID = WavefrontObjectLoader_DisplayList.loadWavefrontObjectAsDisplayList(inGL,"/binaries/geometry/TextureBaking_Normals.wobj.zip");

and usage (every frame):
inGL.glCallList(mDisplayListID);

The repository/framework also contains a lot of other examples on what u can do with JOGL2. For a quick overview take a look at my youtube channel.


Reply | Threaded
Open this post in threaded view
|

Re: Loading and drawing .obj models

diskhub
I am trying to load the obj...
It did load but there's a problem...
after loaded the obj file,
it gives me this:


POLYGON COUNT FOR MODEL=252
VERTEX COUNT FOR MODEL=236
TEXTURE COORDINATE COUNT FOR MODEL=292
NORMAL COUNT FOR MODEL=275
Exception in thread "AWT-EventQueue-0" java.nio.BufferOverflowException
        at java.nio.DirectFloatBufferU.put(Unknown Source)
        at java.nio.FloatBuffer.put(Unknown Source)
        at prac4.WavefrontObjectLoader_DisplayList.ConstructTNV(WavefrontObjectLoader_DisplayList.java:203)
        at prac4.WavefrontObjectLoader_DisplayList.ConstructInterleavedArray(WavefrontObjectLoader_DisplayList.java:173)
        at prac4.WavefrontObjectLoader_DisplayList.drawModel(WavefrontObjectLoader_DisplayList.java:285)
        at prac4.WavefrontObjectLoader_DisplayList.loadWavefrontObjectAsDisplayList(WavefrontObjectLoader_DisplayList.java:306)
        at prac4.Test.init(Test.java:120)
        at jogamp.opengl.GLDrawableHelper.init(GLDrawableHelper.java:155)
        at jogamp.opengl.GLDrawableHelper.init(GLDrawableHelper.java:175)
        at javax.media.opengl.awt.GLCanvas$InitAction.run(GLCanvas.java:856)
        at jogamp.opengl.GLDrawableHelper.invokeGL(GLDrawableHelper.java:356)
        at javax.media.opengl.awt.GLCanvas.maybeDoSingleThreadedWorkaround(GLCanvas.java:769)
        at javax.media.opengl.awt.GLCanvas.display(GLCanvas.java:388)
        at javax.media.opengl.awt.GLCanvas.paint(GLCanvas.java:487)
        at sun.awt.RepaintArea.paintComponent(Unknown Source)
        at sun.awt.RepaintArea.paint(Unknown Source)
        at sun.awt.windows.WComponentPeer.handleEvent(Unknown Source)
        at java.awt.Component.dispatchEventImpl(Unknown Source)
        at java.awt.Component.dispatchEvent(Unknown Source)
        at java.awt.EventQueue.dispatchEventImpl(Unknown Source)
        at java.awt.EventQueue.access$000(Unknown Source)
        at java.awt.EventQueue$1.run(Unknown Source)
        at java.awt.EventQueue$1.run(Unknown Source)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.security.AccessControlContext$1.doIntersectionPrivilege(Unknown Source)
        at java.security.AccessControlContext$1.doIntersectionPrivilege(Unknown Source)
        at java.awt.EventQueue$2.run(Unknown Source)
        at java.awt.EventQueue$2.run(Unknown Source)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.security.AccessControlContext$1.doIntersectionPrivilege(Unknown Source)
        at java.awt.EventQueue.dispatchEvent(Unknown Source)
        at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
        at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
        at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
        at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
        at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
        at java.awt.EventDispatchThread.run(Unknown Source)
Reply | Threaded
Open this post in threaded view
|

Re: Loading and drawing .obj models

diskhub
One more question, if i don't wish to use
framework.base
framework.util

how can i modify the GL2_UnWrap.java to make it work for the texture? :(
Reply | Threaded
Open this post in threaded view
|

Re: Loading and drawing .obj models

Demoscene Passivist
Administrator
In reply to this post by diskhub
>It did load but there's a problem...
>after loaded the obj file,
>it gives me this:

Vertex and normal count have to be multiple of 3 or 4. So usually u have to triangulate the model while exporting. I normally use Blender for Wavefront export, wich has an option to triangulate the model.
Reply | Threaded
Open this post in threaded view
|

Re: Loading and drawing .obj models

Demoscene Passivist
Administrator
In reply to this post by diskhub
>One more question, if i don't wish to use framework.base framework.util
>how can i modify the GL2_UnWrap.java to make it work for the texture? :(

If u don't wanna use my framework, then simply wrap the init, mainloop and cleanup of GL2_UnWrap into the corresponding methods of a GLEventListener and inline the utility methods for texture loading. They are not that much code, son inlining them would be ok I guess.
Reply | Threaded
Open this post in threaded view
|

Re: Loading and drawing .obj models

ravenocean
In reply to this post by Demoscene Passivist
hello,
i had pull your code ,and found that the code has err in :
framework.base.BaseGlobalEnvironment.java 's line 50 :

private float mParameterKey_FLOAT_LÖ;

第50行有乱码!
谢谢!
你们好!
很高兴跟大家一起学习JOGL