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.

1 Tweet

Comments

  1. [...] 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. 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. 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. 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. 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. 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. 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. 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. ./obj2opengl.pl cube.txt -s 4
    does the trick. try
    ./obj2opengl.pl --help
    to learn more about the options.

  20. Ivan says:

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

    -bash: ./obj2opengl.pl: Permission denied

  21. 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!

  22. 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.

  23. 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

  24. Hey Jace,

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

  25. 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?

  26. 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

  27. 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…

  28. [...] 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… [...]

  29. 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!

  30. Kjaerjakcajk says:

    Thanks for this, very helpful.

  31. Sergey Kopanev says:

    Thanks! Very nice script.

  32. 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);

  33. Sergey Kopanev says:

    Hello, all

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

  34. 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

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

  36. 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.

  37. 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();

  38. Sergey Kopanev says:

    Heiko,

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

  39. 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,

  40. Ty says:

    Heiko,

    I seem to be experiencing some problems converting Silo 2 .obj files to .h files with obj2opengl. The conversion appears to work and I am able to load the .h file on an iphone using GLGravity. But the iphone only seems to render the model partially, in a triangle pattern as indicated here: http://imgur.com/fGN8f.jpg

    I found another script, for blender, which enabled me to export solid models:
    http://iphonedevelopment.blogspot.com/2009/07/improved-blender-export.html

    But I would prefer to model in Silo. Blender makes my head heart. Any idea what’s going on here?

  41. Hey Lisa, Hey Ty,

    please provide the .obj file of the most simple version with errors you are converting from (e.g. a cube). I think, the format of the .obj file is slighty different from what the script expects.

  42. Ty says:

    Heiko,

    Thanks for getting back to me. Great little utility! I am really interested in figuring this out and appreciate you taking the time to look at it. I posted both a cube and sphere here: http://gist.github.com/333101

    Both models have the same visual artifact.

    cube.obj: http://gist.github.com/raw/333101/b24c888179d871c4355cbd54a21ad616b4afa3d1/cube.obj

    sphere.obj: http://gist.github.com/raw/333101/e2b4dc69df4f25bd0196817fc3f5d7ee5aec2b9a/sphere.obj

    I have tried dozens of models, all have the same issue. For what it is worth, Blender can’t open Silo .obj files, so perhaps Silo went off standard.

    Anyway, thanks again…. Wow, “Blender makes my head heart.” Apparently Blender makes me funny too.

  43. Ty says:

    Heiko et all,

    Last night at about 4AM, I think I figured this ‘bug’ out. Turns out it is not a ‘bug’ at all, and has nothing to do with the obj2opengl script.

    If you manipulate a mesh in Silo and try to export it as a .obj, Silo will export it as Quads, not Triangles. Before you export your model from Silo, you must select it and choose “Modify” -> “Triangulate” or [Control + Shift + T]. Once you do this, you can export your model from Silo, run obj2opengl and the iphone will render the .h properly.

    Thanks to Jeff LaMarche for confirming the solution. Hope this post saves someone a couple days.

    Thanks again Heiko, for such a great little script.

    best wishes to all,
    ty

  44. robert says:

    hi there.
    I am trying to implement your project into opengl under windows xp using visual studio 2008. I convert the .obj file, paste the code to draw the model and I include the .h file to my project. Project compiles but the model does not come up on-screen. Is there any way to make it working? Cheers!

  45. Hey Robert,

    great to see that someone even tries to use this for VS! Unfortunately, I cannot assist you without further information. Did you try to narrow this down already? How does a simple header file (e.g. the examples) behave? Are you able to visualize anything loaded via glVertexPointer? How do these arrays differ from those generated by obj2opengl?

  46. robert says:

    hi again. thanks fot quick respond
    It works! but without texture. I am using the banana example. I resolved the problem by enabling following:
    //enable vertex coordinate array when glDrawArrays is called
    glEnableClientState(GL_VERTEX_ARRAY);
    //enable normal array when glDrawArrays is called
    glEnableClientState(GL_NORMAL_ARRAY);
    //enable texture coordinate array when glDrawArrays is called
    glEnableClientState(GL_TEXTURE_COORD_ARRAY);
    Do Yoy have any ide how to fix the texture?
    Cheers!

  47. Carlos Pinto says:

    Hey Heiko, I just wanted to say thanks for the very useful script, makes porting 3D models for iPhone development a breeze. Good Job.

    I do have one question though. If an object (3D object that is) was painted with some colors instead of using textures, is there any way to obtain the color data in the obj file and then be parsed to the C style arrays like your script does it?
    The color array that I am talking about would be used in the function: glColorPointer(…) of the OpenGL ES API’s.

    Please let me know either by email or directly in this blog.

    Thanks in advance.

  48. Carlos Pinto says:

    Hello Heiko,

    I was just writing back again in regards to my last post, to give you more specifics as to what would be useful to include in the script.

    The .obj file that was produced for my 3D model contains color shading information such as the one below since I did not use an actual texture:

    g pSphere1
    usemtl initialShadingGroup
    f 1//1 2//2 12//3 11//4
    f 2//5 3//6 13//7 12//8
    f 3//9 4//10 14//11 13//12
    f 4//13 5//14 15//15 14//16
    f 5//17 6//18 16//19 15//20
    ……

    So I was wondering if there would be any way to parse these values into the equivalent in OpenGL ES that the function glColorPointer(..) would be able to take.

    Thanks,

    Carlos.

  49. Hey Carlos,

    unfortunately, the script currently neither supports materials nor separate groups of faces. Both would be required to colorize your models. Sorry for that.

    If you come up with a workaround or a patched version of the script, feel free to post it here!

  50. Esther says:

    Hi everyone!

    First of all I want to apologize because of my bad English. Thank you so much for this amazing tool, it is being very useful for me.
    I have the same problem than Robert. It works but wthout texture, and I am working with the banana example and GLGravity project to be sure that I am not making mistakes, and nothing. Could you help me to solve that please? Thank you Heiko.

  51. Shashank says:

    Works like a charm..Thank you so much Heiko !!!

  52. Chris says:

    Quick question.

    What software do you all use to render the 3D images?

    Thanks

Leave a Reply

Additional comments powered by BackType