Wednesday, June 24, 2009

Using 3D Models from Blender in OpenGL ES

One question I get asked a lot is "how do I load 3D models into OpenGL ES?".

Of course, the answer to that isn't a simple one. If you followed my my earlier posts on importing Wavefront OBJ files, you're probably aware of that already. There are many file formats, and none of them are ideal for loading into a resource-constrained device like the iPhone.

Apple recommends that 3D objects be stored in header files as static arrays. This obviates the need to do any loading or transforming or conversion at all. But… there's not really any easy way to create those header files from within 3D software packages.

Well, there is one way now. The open source 3D program called Blender has a very extensible architecture, making it relatively easy to write custom export modules. Blender's scripting architecture is based on Python, a language that I'm not particularly familiar with, but I hacked out something that works. So, let's say that you've got an object in Blender:

Click to see larger version


And you want to load it into a program you're writing for your iPhone:

Click to see larger version
Note, I know that the screenshots don't actually match - I cropped the texture after exporting from Blender to make it more iPhone sized - I didn't want to resize it for fear that the texture would get too small to see.


All you have to do is take this script and install it into the scripts folder in Blender.

There's a catch, however. The Mac OS X version of Blender doesn't follow the sames rules as it does for other platforms. If you look up how to install a script in Blender, it will tell you to add the script to ~/.blender/scripts/. That directory doesn't get created on the Mac, and if you manually create it, you will lose all the delivered scripts.

Instead, you have to actually install the script into the Blender.app bundle. To make things even more gnarley, the scripts are stored in an invisible folder inside of the Blender bundle. The easiest way to copy the script is to use Terminal.app and the unix cp command. The scripts folder is located at:
/path/to/Blender.app/Contents/MacOS/.blender/scripts
So, if Blender is installed in your Applications folder inside of a folder called Blender, you could use the following command to copy the unzipped script into Blender:
cp objc.py /Applications/Blender/Blender.app/Contents/MacOS/.blender/scripts/

Every time you upgrade your Blender install, you'll have to reinstall this script. The next time you start Blender after copying the script, you will find an entry in the Export menu called Objective-C Header (.h). This is intended to be used with texture-mapped objects, and only exports the active object, not all selected objects or all objects. I am going to create a separate version for non-texture mapped objects. This script will create an inefficient version of non-texture-mapped objects because of the way OpenGL ES uses texture coordinates.

I've also created a sample project that shows how to use the exported header file. It's actually quite easy. You just pass the arrays from the header into the various OpenGL calls, like this:

    glVertexPointer(3, GL_FLOAT, 0, CubeVertices);
glNormalPointer(GL_FLOAT, 0, CubeNormals);
glTexCoordPointer(2, GL_FLOAT, 0, CubeTexCoords);

You can then either use glDrawArrays() or glDrawElements() as you see fit. There are indices provided for glDrawElements(), but since the object is exported as triangles, glDrawArrays() works just fine too.

Before running the script, make sure you put the object into edit mode and select Mesh->Faces->Convert Quads to Triangles. This script will not convert to triangles for you, and OpenGL ES requires triangles. You also need to load and bind the texture you used in Blender, or one that you created based on Blender's exported UV template.

As I said before, I am not a very experienced Python programmer, so if you want to suggest improvements, I'm happy to hear those suggestions. You will not hurt my feelings one little bit. This is what I call a "brute force" script - it gets the job done, but perhaps not with the elegance it should have.

If you want to play around with the simple Blender project I used in the test Xcode project, you can find that right here (right-click and save to disk).

I hope to create a future version that interleaves the data as suggested by Apple, but for now, I was just thrilled to get something working.

No comments:

Post a Comment