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 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
- OBJ2OPENGL.ZIP (script and examples)
- OBJ2OPENGL.PL
August 27th, 2009 at 8:47 pm
[...] 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++ [...]
August 31st, 2009 at 6:17 pm
It seems better to use triangle_strip instead of triange for reducing memory size.
Could you please modify it?
Thanks!
September 1st, 2009 at 8:35 am
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?
September 1st, 2009 at 11:30 pm
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?
September 2nd, 2009 at 5:46 am
Hey Dan, could you please provide the OBJ file? I am going to look into this.
September 2nd, 2009 at 4:34 pm
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.
September 14th, 2009 at 5:59 am
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.
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.
September 14th, 2009 at 10:16 am
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?
September 24th, 2009 at 1:07 am
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
September 24th, 2009 at 10:06 am
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.
October 1st, 2009 at 3:04 pm
this script should generate ansi c style comments (/* */) rather than c++ style to be more portable. just a suggestion =) trivial change
October 1st, 2009 at 3:06 pm
Thank you for your suggestion, jason. I will implement this with the next iteration.
October 19th, 2009 at 3:27 pm
Please, include full xcode project
i try create test app - but it not work
October 19th, 2009 at 4:09 pm
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.
October 22nd, 2009 at 3:46 am
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.
October 22nd, 2009 at 9:07 am
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
October 23rd, 2009 at 10:50 pm
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.
November 3rd, 2009 at 5:55 pm
Great script! I’m just wondering how to use scaling options from the command line?
Keep up the great work!
Ryan
November 4th, 2009 at 8:51 am
./obj2opengl.pl cube.txt -s 4does the trick. try
./obj2opengl.pl --helpto learn more about the options.
November 12th, 2009 at 10:35 am
textrue should be texture in http://www.heikobehrens.net/wp-content/uploads/2009/08/obj2opengl.txt line 451.
November 13th, 2009 at 12:32 pm
Sorry, I’m noob in mac, i have the next error:
-bash: ./obj2opengl.pl: Permission denied
November 13th, 2009 at 12:36 pm
Hey Ivan,
did you apply execution rights to the script? http://osxfaq.com/tutorials/learningcenter/UnixTutorials/ShellScripting1/index.ws#asss
November 17th, 2009 at 4:11 pm
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!
November 17th, 2009 at 4:33 pm
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.
November 22nd, 2009 at 7:57 pm
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
November 23rd, 2009 at 12:24 pm
Hey Jace,
could you please provide your obj file so I can try to reproduce your error?
December 7th, 2009 at 1:29 pm
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?
December 7th, 2009 at 2:06 pm
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
December 7th, 2009 at 3:22 pm
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…
December 19th, 2009 at 3:04 pm
[...] 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… [...]
January 4th, 2010 at 7:17 pm
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!
January 18th, 2010 at 2:07 am
Thanks for this, very helpful.
February 3rd, 2010 at 2:31 pm
Thanks! Very nice script.
February 4th, 2010 at 3:29 pm
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);
February 4th, 2010 at 7:00 pm
Hello, all
But what if the object 2 or more textures?
thanks.
February 23rd, 2010 at 4:11 am
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
February 23rd, 2010 at 9:31 am
Sergey, currently the script merges all groups into a single object and can handle only one texture.
February 23rd, 2010 at 9:31 am
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.
February 23rd, 2010 at 10:19 pm
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();
February 27th, 2010 at 5:42 pm
Heiko,
Thanks for answer. Do you have a plan make script working with some textures ?
February 27th, 2010 at 6:33 pm
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,