If you are using Blender for your 3D models, then you’ll want to export them to a format that OpenGL ES can understand. Maybe you just want to extract the data from Blender and load it into another format than just ‘raw vertex arrays’ – either way at the moment this is a little bit of a pain because there’s nothing like a standard script (see this article for roundabout solutions).
I’m working on a nice and nicer script for doing that. Today I’m just posting the code for a very simple script – after playing a little bit with this, maybe you’ll want to write your own export script (link to blender wiki-book), not because it’s easy with a little know-how, but because that will give you flexibility at low cost.
Outline
- I export binary data from Blender using Python. It’s easy to write scripts that do that, especially because Blender has an interactive Python console and a data browser, so we can explore the structure underlying a scene dynamically and pick what we need. Other solutions tend to rely on parsing an *.obj or *.vrml file generated by a standard export script. This gives no control over what gets exported, and I believe parsing is harder than just exporting ad-hoc binary data and reading it back into your iPhone app.
- I import the binary data using trivial stdio C functions. That is also easy – just check the code.
- Writing/reading binary data may require running the Python script on your Apple Mac. I believe the ‘struct’ python class/module just writes using the system’s default binary types. So for example if you work with a graphic designer and for some reason they run Blender on a PC, you might get garbled output.
Here’s my python script – that needs to go in the blender scripts folder (see below):
#!BPY
# my_export_script.py
# this writes out the number of triangles in the
# model as an int, then writes out each coordinate for each vertex
# for each triangle. Indexed data is readily available from Blender
# and would be way more efficient.
""" Registration info for Blender menus:
Name: 'Test for simple mesh export2'
Blender: 249
Group: 'Export'
Tip: 'Export selected mesh for a test'
"""
import Blender
import bpy
import struct
def write_face_vertices(face,out):
k=face.verts
write_vertex(k[0],out)
write_vertex(k[1],out)
write_vertex(k[2],out)
write_vertex(k[2],out)
write_vertex(k[3],out)
write_vertex(k[0],out)
def write_vertex(k,out):
out.write(struct.pack('f',k.co.x))
out.write(struct.pack('f',k.co.y))
out.write(struct.pack('f',k.co.z))
def write_obj(filepath):
out = open(filepath, 'wb')
sce = bpy.data.scenes.active
ob = sce.objects.active
mesh = ob.getData(mesh=1)
out.write(struct.pack('i',len(mesh.faces)*2))
#out.write('face_count %i\n' % (len(mesh.faces)))
for face in mesh.faces:
write_face_vertices(face,out)
out.close()
Blender.Window.FileSelector(write_obj, "Export")
Now, here’s the class that reads the data – that goes directly into your project and requires nothing – well yes, you’d have to write your own header file for it :).
// RawMesh.m loads raw mesh data from the main resource bundle.
// This is where files end up when you build after
// adding them to your XCode project
// triangleCount is an int; vertexArray is a float*
// You could probably make vertexArray a float[] (a C array, not NSArray)
// and avoid the malloc/free worry doll.
#import “RawMesh.h”
#define VERTICES_PER_TRIANGLE 9
@implementation RawMesh
@synthesize triangleCount,vertexArray;
-(id)initFromResourceFile:(NSString*) fname{
if (self=[super init]) {
// get the file path from main resource bundle
NSBundle *mainBundle = [NSBundle mainBundle];
// rglmd=raw gl mesh data
NSString* path = [mainBundle pathForResource:fname ofType:@"rglmd"];
FILE * fh = fopen([path UTF8String], “rglmd”);
// determine the number of faces
fread(&triangleCount, sizeof(int), 1, fh);
NSLog(@”FACES:%i”,triangleCount);
vertexArray= malloc(sizeof(float)*triangleCount*VERTICES_PER_TRIANGLE);
fread(vertexArray, sizeof(float),triangleCount*VERTICES_PER_TRIANGLE, fh);
}return self;
}
-(void)dealloc{
[super dealloc];
free(vertexArray);
}
@end
Finally, here’s the code that renders this using GL-ES:
RawMesh* mesh=[[RawMesh alloc]initFromResourceFile:filename];
glVertexPointer(3, GL_FLOAT, 0, mesh.vertexArray);
// let the gl know that we want to use the vertex array.
glEnableClientState(GL_VERTEX_ARRAY);
// since the script didn't import color or material, just use anything
glColor4f(1.0f,0.6f,0.0f,0.0f);
// does this need an explanation?
glDrawArrays(GL_TRIANGLES, 0, mesh.triangleCount*3);
I hope this can help you. I’d like to comment more on the above (and please keep in mind it’s just a basic script to get started – I know there’s no animation, no materials, not even normals).
One more thing: on Mac OSx, you need to dig blender.app and look inside it to find Blender’s script folder. Because this is further buried down in a (hidden) dot file this can be a bit of a pain. But the blender wiki book (see my previous article) explains how to do it.
Here you are. I’ll improve this article. I didn’t want to delay publishing this code because many iPhone devs, like me, come from other languages and other platforms, so Python and C stdio can seem a little intimidating – collecting the information to write this misery of a scriptlet took me hours, now here it is.