0
\$\begingroup\$

I'm trying to load a 3D model in OpenGL ES, and I managed to find a project on "sourceForge" that can do this job.

When I downloaded it and played the app, it took 50 second to load a small object (100 kb), so I changed the model for another that contains about twenty vertices (15 kb), but the application takes more than 10 second to download even this tiny model.

What is the problem, and how can I make this faster?

This is my obj parser

public class OBJParser {
    int numVertices=0;
    int numFaces=0;
    Context context;

    Vector<Short> faces=new Vector<Short>();
    Vector<Short> vtPointer=new Vector<Short>();
    Vector<Short> vnPointer=new Vector<Short>();
    Vector<Float> v=new Vector<Float>();
    Vector<Float> vn=new Vector<Float>();
    Vector<Float> vt=new Vector<Float>();
    Vector<TDModelPart> parts=new Vector<TDModelPart>();
    Vector<Material> materials=null;

    public OBJParser(Context ctx){
        context=ctx;
    }

    public TDModel parseOBJ(String fileName) {
        BufferedReader reader=null;
        String line = null;
        Material m=null;

        try { //try to open file
            reader = new BufferedReader(new InputStreamReader(new FileInputStream(fileName)));
        }       catch(IOException e){
        }
        try {//try to read lines of the file
            while((line = reader.readLine()) != null) {
                Log.v("obj",line);
                if(line.startsWith("f")){//a polygonal face
                    processFLine(line);
                }
                else
                    if(line.startsWith("vn")){
                        processVNLine(line);
                    }
                    else
                        if(line.startsWith("vt")){
                            processVTLine(line);
                        }
                        else
                            if(line.startsWith("v")){ //line having geometric position of single vertex
                                processVLine(line);
                            }
                            /*else
                                if(line.startsWith("usemtl")){
                                    try{//start of new group
                                    if(faces.size()!=0){//if not this is not the start of the first group
                                        TDModelPart model=new TDModelPart(faces, vtPointer, vnPointer, m,vn);
                                        parts.add(model);
                                    }
                                    String mtlName=line.split("[ ]+",2)[1]; //get the name of the material
                                    for(int i=0; i<materials.size(); i++){//suppose .mtl file already parsed
                                        m=materials.get(i);
                                        if(m.getName().equals(mtlName)){//if found, return from loop
                                            break;
                                        }
                                        m=null;//if material not found, set to null
                                    }
                                    faces=new Vector<Short>();
                                    vtPointer=new Vector<Short>();
                                    vnPointer=new Vector<Short>();
                                    }
                                    catch (Exception e) {
                                        // TODO: handle exception
                                    }
                                }
                                else
                                    if(line.startsWith("mtllib")){
                                        materials=MTLParser.loadMTL(line.split("[ ]+")[1]);
                                        for(int i=0; i<materials.size(); i++){
                                            Material mat=materials.get(i);
                                            Log.v("materials",mat.toString());
                                        }
                                    }*/
            }
        }       
        catch(IOException e){
            System.out.println("wtf...");
        }
        if(faces!= null){//if not this is not the start of the first group
            TDModelPart model=new TDModelPart(faces, vtPointer, vnPointer, m,vn);
            parts.add(model);
        }
        TDModel t=new TDModel(v,vn,vt,parts);
        t.buildVertexBuffer();
        Log.v("models",t.toString());
        return t;
    }


    private void processVLine(String line){
        String [] tokens=line.split("[ ]+"); //split the line at the spaces
        int c=tokens.length; 
        for(int i=1; i<c; i++){ //add the vertex to the vertex array
            v.add(Float.valueOf(tokens[i]));
        }
    }
    private void processVNLine(String line){
        String [] tokens=line.split("[ ]+"); //split the line at the spaces
        int c=tokens.length; 
        for(int i=1; i<c; i++){ //add the vertex to the vertex array
            vn.add(Float.valueOf(tokens[i]));
        }
    }
    private void processVTLine(String line){
        String [] tokens=line.split("[ ]+"); //split the line at the spaces
        int c=tokens.length; 
        for(int i=1; i<c; i++){ //add the vertex to the vertex array
            vt.add(Float.valueOf(tokens[i]));
        }
    }
    private void processFLine(String line){
        String [] tokens=line.split("[ ]+");
        int c=tokens.length;

        if(tokens[1].matches("[0-9]+")){//f: v
            if(c==4){//3 faces
                for(int i=1; i<c; i++){
                    Short s=Short.valueOf(tokens[i]);
                    s--;
                    faces.add(s);
                }
            }
            else{//more faces
                Vector<Short> polygon=new Vector<Short>();
                for(int i=1; i<tokens.length; i++){
                    Short s=Short.valueOf(tokens[i]);
                    s--;
                    polygon.add(s);
                }
                faces.addAll(Triangulator.triangulate(polygon));//triangulate the polygon and add the resulting faces
            }
        }
        if(tokens[1].matches("[0-9]+/[0-9]+")){//if: v/vt
            if(c==4){//3 faces
                for(int i=1; i<c; i++){
                    Short s=Short.valueOf(tokens[i].split("/")[0]);
                    s--;
                    faces.add(s);
                    s=Short.valueOf(tokens[i].split("/")[1]);
                    s--;
                    vtPointer.add(s);
                }
            }
            else{//triangulate
                Vector<Short> tmpFaces=new Vector<Short>();
                Vector<Short> tmpVt=new Vector<Short>();
                for(int i=1; i<tokens.length; i++){
                    Short s=Short.valueOf(tokens[i].split("/")[0]);
                    s--;
                    tmpFaces.add(s);
                    s=Short.valueOf(tokens[i].split("/")[1]);
                    s--;
                    tmpVt.add(s);
                }
                faces.addAll(Triangulator.triangulate(tmpFaces));
                vtPointer.addAll(Triangulator.triangulate(tmpVt));
            }
        }
        if(tokens[1].matches("[0-9]+//[0-9]+")){//f: v//vn
            if(c==4){//3 faces
                for(int i=1; i<c; i++){
                    Short s=Short.valueOf(tokens[i].split("//")[0]);
                    s--;
                    faces.add(s);
                    s=Short.valueOf(tokens[i].split("//")[1]);
                    s--;
                    vnPointer.add(s);
                }
            }
            else{//triangulate
                Vector<Short> tmpFaces=new Vector<Short>();
                Vector<Short> tmpVn=new Vector<Short>();
                for(int i=1; i<tokens.length; i++){
                    Short s=Short.valueOf(tokens[i].split("//")[0]);
                    s--;
                    tmpFaces.add(s);
                    s=Short.valueOf(tokens[i].split("//")[1]);
                    s--;
                    tmpVn.add(s);
                }
                faces.addAll(Triangulator.triangulate(tmpFaces));
                vnPointer.addAll(Triangulator.triangulate(tmpVn));
            }
        }
        if(tokens[1].matches("[0-9]+/[0-9]+/[0-9]+")){//f: v/vt/vn

            if(c==4){//3 faces
                for(int i=1; i<c; i++){
                    Short s=Short.valueOf(tokens[i].split("/")[0]);
                    s--;
                    faces.add(s);
                    s=Short.valueOf(tokens[i].split("/")[1]);
                    s--;
                    vtPointer.add(s);
                    s=Short.valueOf(tokens[i].split("/")[2]);
                    s--;
                    vnPointer.add(s);
                }
            }
            else{//triangulate
                Vector<Short> tmpFaces=new Vector<Short>();
                Vector<Short> tmpVn=new Vector<Short>();
                //Vector<Short> tmpVt=new Vector<Short>();
                for(int i=1; i<tokens.length; i++){
                    Short s=Short.valueOf(tokens[i].split("/")[0]);
                    s--;
                    tmpFaces.add(s);
                    //s=Short.valueOf(tokens[i].split("/")[1]);
                    //s--;
                    //tmpVt.add(s);
                    //s=Short.valueOf(tokens[i].split("/")[2]);
                    //s--;
                    //tmpVn.add(s);
                }
                faces.addAll(Triangulator.triangulate(tmpFaces));
                vtPointer.addAll(Triangulator.triangulate(tmpVn));
                vnPointer.addAll(Triangulator.triangulate(tmpVn));
            }
        }
    }
}
\$\endgroup\$
  • \$\begingroup\$ Just to be clear, from what I understand, you are trying to load 3D models inside your game that is using OpenGL, and you found a random project to do that, but its not as fast as you'd expect? How big are the models you are trying to load? If they are big enough, there is nothing realistically you can do, apart from creating a Loading Screen on your game while loading them. \$\endgroup\$ – TomTsagk Aug 28 at 11:14
  • \$\begingroup\$ Links can die, which will make this question useless. Instead of just pasting a link to the code/project, add relevant code to your post so someone here can check it out. By relevant, I mean the model loading code. If you have written your own code, where the model loading doesn't work properly, paste that aswell. \$\endgroup\$ – Daniel_1985 Aug 28 at 11:28
  • \$\begingroup\$ The project is very big and I can't paste all code here \$\endgroup\$ – Osama Aug 28 at 11:55
  • \$\begingroup\$ I want to load a small model it's about 30 lines its size is 15 kb so it's so small but the project takes 5 seconds to load this model \$\endgroup\$ – Osama Aug 28 at 11:56
  • \$\begingroup\$ A model that small should load very fast so there's gotta be something in the loading code that is doing something weird. Is Assimp used to parse the model or is it being parsed manually? \$\endgroup\$ – Daniel_1985 Aug 28 at 12:56
1
\$\begingroup\$

There's nothing inherently slow or fast about OpenGL, or Java for that matter, when it comes to loading models. The following basic rules apply:

  • You're hitting the disk, so fewer large reads are going to be much faster than lots of small reads.
  • Loading binary files is typically going to be faster than parsing plain text.
  • If the model needs conversion from the format and layout it's stored in to the format and layout it's going to be used in, then that's going to slow things down as well.

The fastest model loading basically happens when you can memory-map a binary file and pass the resulting pointer, with unmodified/un-parsed data, directly to a glBufferData call. Any work beyond that is going to slow things down, and the more work that is done, the slower things will be.

Based on all of this, you'll see that .obj is actually a horrible format for consuming directly by a 3D application. If you need an interchange format that's readable by anything and is in human-readable plain-text, it meets those criteria. If you care about getting the model off-disk and into-memory as fast as possible at load time in a real-time application, it sucks - which is why nobody actually uses it for production-quality work.

Instead, if you must use .obj, you should include as part of your toolchain a final conversion step to a nice fast binary format that your program can quickly and easily load.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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