obj2opengl: convert obj 3D models to arrays compatible with iPhone OpenGL ES

Whenever you want to use 3D objects modeled with a software such as Blender, 3ds Max or Cinema 4D in your iPhone Application or any other OpenGL project, you somehow have to make the designed data accessible to you program. To do this you can either load and interpret the files your models are saved into or you could directly provide the needed in-memory representation your program expects.

OBJ2OPENGL does the latter and acts as a converter from model files to C/C++ headers that describe vertices of the faces, normals and texture coordinates as simple arrays of floats.

Texturized and lighted 3D model with 8056 faces on the iPhone

Texturized and lighted 3D model with 8,056 faces on the iPhone

OBJ2OPENGL is a Perl script that reads a Wavefront OBJ file describing a 3D object and writes a C/C++ include file describing the object in a form suitable for use with OpenGL ES. It is compatible with Objective C and the libraries of the iPhone SDK.

The original idea and code base of this script comes from Margaret Geroch who kindly allowed me to enhance and republish this version. It now supports texture mapping and stored normals (instead of recalculating them). I have changed the structure of the generated arrays to work with glDrawArrays instead of glDrawElements. This is particularly needed for normals and textures where a vertex holds different information for some shapes. Also, this version includes several command line options to better control its behavior (such as scaling and moving the origin or setting the object’s and output file’s name).

If you can go with the defaults the conversion is as simple as

./obj2opengl.pl banana.obj

To include the converted object all you have to do is

// include generated arrays
#import "banana.h"

// set input data to arrays
glVertexPointer(3, GL_FLOAT, 0, bananaVerts);
glNormalPointer(GL_FLOAT, 0, bananaNormals);
glTexCoordPointer(2, GL_FLOAT, 0, bananaTexCoords);

// draw data
glDrawArrays(GL_TRIANGLES, 0, bananaNumVerts);

Examples

These are two models and generated sample output that is included in the provided archive:

Input Output Texture Vertices Faces
cube.obj cube.h 8 6
banana.obj banana.h banana.jpg 4,032 8,056

Downloads

Please use the comment function of this post to discuss any problems or noteworthy results accomplished with help of this script. I will read feature requests, too. Feedback is highly appreciated.

41 Responses to “obj2opengl: convert obj 3D models to arrays compatible with iPhone OpenGL ES”

  1. Rendering 3D Models - iPhone Dev Forums Says:

    [...] rendering on the iPhone. Addressing your loading issue: There might be an alternative with obj2opengl. It’s a script that converts wavefront obj files (nearly any 3d modeler can export this) to C/C++ [...]

  2. magicboker Says:

    It seems better to use triangle_strip instead of triange for reducing memory size.
    Could you please modify it?
    Thanks!

  3. Heiko Behrens Says:

    Unfortunately, this script is generic and the OBJ file can be of any content. You might find this paper http://www.ics.uci.edu/~gopi/PAPERS/Sibgrapi06.pdf or this tool http://www.cs.sunysb.edu/~stripe/ helpful to understand the issue with strips in this case. Generally spoken: It is not possible to render arbitrary triangles as a single strip.

    When splitting into several strips the generated script has to do the rendering part to configure the input this the right order. With an increasing amount of OpenGL roundtrips this might lead into performance issues as well.

    Could you elaborate why memory size is a problem in your case?

  4. Dan Bliss Says:

    I created a simple planet with a single texture in Blender. I exported as a obj file with UV. When I run the script the .h file it map the texture over each triangle instead of mapping it over the object once. Any thoughts?

  5. Heiko Behrens Says:

    Hey Dan, could you please provide the OBJ file? I am going to look into this.

  6. Dan Bliss Says:

    I found out the UV coords are not assigned automatically in Blender. In editmode press the U key and select “Sphere from View”. The Perl script works great.

  7. Dawit Thepchatree Says:

    Hey there Heiko,

    I just came across this Perl script of yours (from dev forums), and it works great! I really like how you simplified things (very easy to use!) and made it cross-operational for many systems and platforms. :D

    One gripe though: my Silo 2 exported OBJ file isn’t showing up correctly for the iPhone OS. Some faces are gone. I think it has something to do with Silo 2’s Triangulate function. Otherwise, when I deal with simpler models, it works fine. :)

    Anyway, here’s the OBJ file:
    http://www.mediafire.com/?ykzyiwd2nde

    Thanks.

  8. Heiko Behrens Says:

    Hey Dawit,

    thank you for your comment. Your exported obj file partly contains faces with 4 vertices each. Currently, this script has these known limitations, though: “This script expects and OBJ file consisting of vertices, texture coords and normals. Each face must contain exactly 3 vertices. The texture coords are two dimonsional.”

    Is it possible for you to export your model with triangles only?

  9. okfp Says:

    Heiko,

    really amazing script. But I was trying to use to render your example in iphone simulator Xcode project. I don’t know where I failed but I didn’t see the bananas on the screen. I was trying to adjust glOrthof thinking should be big enough to see it, but doesn’t work. Could you please provide your sample Xcode project or how do you implent that?

    Thanks

  10. Heiko Behrens Says:

    Hey okfp,

    as a starting point, use the GLGravity sample from the dev center and add the banana.h to the project. Now, replace the #import “teapot.h” in AppController.m with the banana.h and build the project. You will find to areas with errors, replace these lines with the ones given on top of the banana.h. First, replace the whole loop after “// Draw teapot” with the single glDrawArrays statement. Second, set the vertex and normal pointer as stated in the comments of banana.h. That’s it.

    I am sure you will find some sample code to learn the handling of textures, too.

  11. jason colburne Says:

    this script should generate ansi c style comments (/* */) rather than c++ style to be more portable. just a suggestion =) trivial change

  12. Heiko Behrens Says:

    Thank you for your suggestion, jason. I will implement this with the next iteration.

  13. Alex Says:

    Please, include full xcode project
    i try create test app - but it not work

  14. Heiko Behrens Says:

    Hey Alex, as stated in my comment http://www.heikobehrens.net/2009/08/27/obj2opengl/#comment-3460 above I used the GLGravity sample from Apple to produce the screenshot. Unfortunately, I cannot repost this code since it is protected by law. Feel free to ask any questions that arise following the steps I described on that comment, though.

  15. fx61803 Says:

    mr. behrens,
    thanks for the converter, works like a charm!
    after initial obstacles where it was not able to generate correct header file caused by not having only triangles in the model I am now able to compile the model with xcode; brilliant!

    / the converter produces a weird bug when skipping number values and leaving only commas in the array definition when the model is not in expected format, I guess; however when I exported the same model with triangles only, it works perfectly /

    however, since I am a complete noob in this field I even don’t know if something like this makes sense, but is it possible to automatically apply a corresponding texture to a model defined like this in xcode? if so - how ? - does there exist a corresponding apple tutorial/concept which can be easily adopted for this ?
    thank you very much!
    r.

  16. Heiko Behrens Says:

    Hey R,
    I am not completely sure what you are asking for. If your obj contains texture coordinates you can bin them using glTexCoordPointer as you can see in the generated comment of your .h file.

    Loading the corresponding image file is another story, though. If you are not familiar with OpenGL ES and texture mapping these links might be helpful:
    http://iphonedevelopment.blogspot.com/2009/05/opengl-es-from-ground-up-part-6_25.html
    http://web.me.com/smaurice/AppleCoder/iPhone_OpenGL/Entries/2009/4/1_OpenGL_ES_05_-_Texture_Mapping_Our_Square.html

  17. fx61803 Says:

    Hello!
    thanks for the links, they’ve been helpful
    now, I couldn’t make the texturing of your banana to work, but it seems it required a call to
    glEnableClientState(GL_TEXTURE_COORD_ARRAY) after glTexCoordPointer(2, GL_FLOAT, 0, bananaTexCoords) to make it finally accept the texture
    I do not know how generic this might be and if it’s worth mentioning in generated header for example
    thanks!
    r.

  18. Ryan Says:

    Great script! I’m just wondering how to use scaling options from the command line?

    Keep up the great work!

    Ryan

  19. Heiko Behrens Says:

    ./obj2opengl.pl cube.txt -s 4
    does the trick. try
    ./obj2opengl.pl --help
    to learn more about the options.

  20. tgr Says:

    textrue should be texture in http://www.heikobehrens.net/wp-content/uploads/2009/08/obj2opengl.txt line 451.

  21. Ivan Says:

    Sorry, I’m noob in mac, i have the next error:

    -bash: ./obj2opengl.pl: Permission denied

  22. Heiko Behrens Says:

    Hey Ivan,
    did you apply execution rights to the script? http://osxfaq.com/tutorials/learningcenter/UnixTutorials/ShellScripting1/index.ws#asss

  23. Ryan Says:

    Does anyone know of a good way to create simple bounding boxes using these .h files? Something as simple as creating a bounding rectangle using max and min z, x, and y values would do.

    Thanks!

  24. Heiko Behrens Says:

    Ryan, you can easily iterate over the verts array and collect for each dimension the min and max values. But, adding this to the output of the script would be helpful, too. Thanks for your suggestion.

  25. Jace Says:

    Hi,

    I wondered if you could help me. I created an object in Carrara 6 pro and exported to wavefont obj file. I ran your script and I got “Illegal division by zero at ./obj2opengl.pl line 294″ I checked this and it seemed to be related to scale, so i specified -noScale and it appeared to work. However upon further inspection the header file was in fact invalid and contained nothing like the expected format, instead just a copy of the original obj file. I’m new to all of this, so forgive my ignorance but do you have any idea what I’m doing wrong?

    Thanks in advance for any help

    Regards

  26. Heiko Behrens Says:

    Hey Jace,

    could you please provide your obj file so I can try to reproduce your error?

  27. Niklas Says:

    Hey, great script.

    I’m a complete newbie when it comes to open gl, so pardon my ignorance.

    But when I create a simple object with normals, and render it all, as per your examples. If I then place a light above the object, and then rotate say a cube. It seems to me that as the cube rotates around the axis, the backside of it becomes dark when facing the light, rather then the side facing away form the light becomes dark.

    Do I need to recalculate or transform the normals for the light to be correct?

    I’m doing a push and pop call, then translate the cube I’m drawing. I would have thought that the face facing the light would become lighter. Isn’t that what the normals are for?

  28. Heiko Behrens Says:

    Hey Niklas,

    your are absolutely right. The normals will be transformed (even scaled) according to your matrix. If you are an iPhone developer have a look at the GLGravity example to learn about lights and OpenGL. Also, this tutorial seems to introduce lights and normals quite extensively: http://www.falloutsoftware.com/tutorials/gl/gl8.htm

  29. Niklas Says:

    Hm, seem that I had forgotten to put in glMatrixMode(GL_MODELVIEW) after I set up the projection view port… So the light now seems to work…

  30. Blender >> OpenGL ES – Any Hope? - OOgtech.org Says:

    [...] obj2OpenGL – Heiko Behrens’ PERL script to convert *.obj (Alias Wavefront) to opengl. This is attractive because it loads data that OpenGL can render directly. Also it’s been designed with the iPhone/iTouch in mind. If I use this I still would like to write an automation to export my models from the blender file format to obj. It would be really nice if I could ‘press a button’ and get all my 3D files updated at once. I guess I should try this first… [...]

  31. iphone4 Says:

    There is a typo:
    print OUTFILE “glTexCoordPointer(2, GL_FLOAT, 0, “.$object.”TexCoords);\n”
    if $numTextrue > 0;

    should be:
    print OUTFILE “glTexCoordPointer(2, GL_FLOAT, 0, “.$object.”TexCoords);\n”
    if $numTexture > 0;

    It stops glTexCoordPointer appearing in comment.

    Nice script though!

  32. Kjaerjakcajk Says:

    Thanks for this, very helpful.

  33. Sergey Kopanev Says:

    Thanks! Very nice script.

  34. Mirza Garibovic Says:

    Hi Heiko,

    I made a small change to this so that it interleaves the vertex data in one contiguous array, according to http://developer.apple.com/iphone/library/documentation/3DDrawing/Conceptual/OpenGLES_ProgrammingGuide/Performance/Performance.html

    obj2opengl.pl:
    http://docs.google.com/leaf?id=0B3MMs33O0RexZDUzZDU1ZjItMjRlZi00OGQwLThkOTktN2RkYWM4MzNiMDM3&hl=en

    Example output (cube.h):
    http://docs.google.com/leaf?id=0B3MMs33O0RexMGY0YTI0NjAtY2E1MC00MGYwLWIxM2EtMGRmYzExMzE0ODUz&hl=en

    Example draw (note the required stride constant):

    glVertexPointer(3, GL_FLOAT, cubeStride, cubeVerts);

    glNormalPointer(GL_FLOAT, cubeStride, cubeNormals);

    glDrawArrays(GL_TRIANGLES, 0, cubeNumVerts);

  35. Sergey Kopanev Says:

    Hello, all

    But what if the object 2 or more textures?
    thanks.

  36. Lisa Wilkinson Says:

    Hi,

    I have been having problems getting the UV texture coordinates to work.
    Here is a screen shot of what it looks like in Maya: http://imgur.com/z2Ica.png
    and what it looks like on the iPhone: http://imgur.com/A1alQ.png

    Does anyone know how this could be fixed?
    Thanks in advance for any help

  37. Heiko Behrens Says:

    Sergey, currently the script merges all groups into a single object and can handle only one texture.

  38. Heiko Behrens Says:

    Hey Lisa, can you reproduce this error with a simpler example (e.g. a cube)? I am afraid the .obj file itself has some unexpected mappings.

  39. Lisa Wilkinson Says:

    Here’s another example.
    Maya: http://imgur.com/PVYqa.png
    iPhone: http://imgur.com/FGcGs.png
    Texture: http://imgur.com/ivt5J.png

    and my code to draw it:

    glPushMatrix();
    glBindTexture(GL_TEXTURE_2D, textures[13]);
    glVertexPointer(3, GL_FLOAT, 0, cubeVerts);
    glTexCoordPointer(2, GL_FLOAT, 0, cubeTexCoords);
    glDrawArrays(GL_TRIANGLES, 0, cubeNumVerts);
    glPopMatrix();

  40. Sergey Kopanev Says:

    Heiko,

    Thanks for answer. Do you have a plan make script working with some textures ?

  41. Sergey Kopanev Says:

    Heiko,

    What is “Center” that script saying ?
    Center:

    I have two models and i have no success draw it correctly in space.
    I’ve tried glTranslatef to “Center” and glScalef by “Scale by” before rendering - but my models located in space at the wrong coordinates.

    How can I draw 2 models on their coordinates same as in 3dMax?

    thanks,

Leave a Reply