Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to load a 3d object in Android?

I have the .obj file and .mtl file but don't know how to import it. I have tried the tutorials, they aren't working.

like image 841
Abhishek Shah Avatar asked May 22 '11 14:05

Abhishek Shah


1 Answers

One way to load an Object from an .obj file is to first get the inputstream from the file, then you go through each line, since one line consists of one vertex. The part where you then use youre data from the .obj file to acctually draw it is something that you easily can find a bunch of tutorials on online, one example: http://www.droidnova.com/android-3d-game-tutorial-part-i,312.html . If you want to get deeper into Opengl and acctually learn alot you can use Nehe the allmightys online tutorial that covers very much and goes quite deep into OpenGL programming: http://nehe.gamedev.net/ .

Anyway, the .obj file loading could start of by something like this:

        while ((line = reader.readLine()) != null)
    {
        if (line.startsWith("f"))
        {
            faces++;
            processFLine(line);
        } else if (line.startsWith("vn"))
        {
            normals++;
            processVNLine(line);
        } else if (line.startsWith("vt"))
        {
            UVCoords++;
            processVTLine(line);
        } else if (line.startsWith("v"))
        {
            vertices++;
            processVLine(line);
        }
    }

For everything except when line starts with 'f' it's quite straight forward, you just store thoose two or three values in something, I preferred a Vector:

    private void processVNLine(String line)
{
    String[] tokens = line.split("[ ]+");
    int c = tokens.length;
    for (int i = 1; i < c; i++)
    { // add the normals to the normal vector
        _vn.add(Float.valueOf(tokens[i]));
    }
}

For the faces part (where line starts with 'f', you would preferably first check how many '/' each vertex consists of:

    private void processFLine(String line)
{
    String[] tokens = line.split("[ ]+");
    int c = tokens.length;

    if (tokens[1].matches("[0-9]+"))
    {
        caseFEqOne(tokens, c);
    }
    if (tokens[1].matches("[0-9]+/[0-9]+"))
    {
        caseFEqTwo(tokens, c);
    }
    if (tokens[1].matches("[0-9]+//[0-9]+"))
    {
        caseFEqOneAndThree(tokens, c);
    }
    if (tokens[1].matches("[0-9]+/[0-9]+/[0-9]+"))
    {
        caseFEqThree(tokens, c);
    }
}  

Each face is built up by three vertex, where each vertex indexes is v/vt/vn.

This is what could happen if you have v/vt. It stores the indices in seperate vectors.

    private void caseFEqTwo(String[] tokens, int c)
{
    for (int i = 1; i < c; i++)
    {
        Short s = Short.valueOf(tokens[i].split("/")[0]);
        s--;
        _vPointer.add(s);

        s = Short.valueOf(tokens[i].split("/")[1]);
        s--;
        _vtPointer.add(s);
    }
}

Now you have handled this by adding the indices into sperarate Vectors or arrays.

Now you should have Vectors that consists of coordinates and vector that consists of indices for each type(v, vt or vn). You should as well have the number of faces. By knowing the number of faces you can now start a loop that goes from 0 to number of faces.

If you now create new vectors that consists of the final result, you can in this loop move the coordinates at the index from the loop index to the loop index in the results vectors.

This may need some explenation maybe so here is a simple example:

private void reArrange()
{
    Iterator<Short> i;
    short s;

    i = _vPointer.iterator();
    while (i.hasNext())
    {
        s = (short) (i.next() * 3);
        for (int k = 0; k < 3; k++)
        {
            _vResult.add(_v.get(s + k));
        }
    }

    i = _vnPointer.iterator();
    while (i.hasNext())
    {
        s = (short) (i.next() * 3);
        for (int k = 0; k < 3; k++)
        {
            _vnResult.add(_vn.get(s + k));
        }
    }

    i = _vtPointer.iterator();
    while (i.hasNext())
    {
        s = (short) (i.next() * 2);
        for (int k = 0; k < 2; k++)
        {
            _vtResult.add(1f - _vt.get(s + k));
        }
    }

    _indices = new short[faces * 3];
    for (short k = 0; k < faces * 3; k++)
    {
        _indices[k] = k;
    }
}

Now your'e acctually done and have the information you need from the .obj file. So by converting thoose vectors to FloatBuffers and by creating a ShortBuffer that is as simple as 0,1,2,3,4,5 ... and so on you can use thoose with OpenGL-ES to draw your'e object.

(This is not completly written by me, I used a basic concept i found somewhere online, can't find now tho, and then made it fit nicely to what i wanted and adjusted it abit to get easier to understand)

What this doesn't cover is the part where you also load a .mtl file that you oftenly will get as well while exporting an Object from a program. If you want to implement that as well you could have a look at this: http://people.sc.fsu.edu/~jburkardt/data/mtl/mtl.html

This is something that can be good to have if your'e building an Object in a program and want a more exact replica where this will give you values for how light should be handled to materials as well.

If you have any questions, feel free to ask them.

(I know that this might not be the optimal way of loading a .obj file since we use up to 9 different vectors but if you use serialization as well this might acctually get quite fast.)

like image 61
Henrik Avatar answered Oct 04 '22 22:10

Henrik