Many years ago (way back in college) I wrote a 3D stress visualization application for one of my professors. It was my very first Cocoa application. Written on a NeXT Cube in Display Post Script. Wow, was it cool to work on that app, I had so much fun and learned a ton and got to help with some research. I had so much fun that I got a 2nd credit card, and after maxing both out bought a NeXT slab so I could program at home.
The system that I wrote ended up being a lot like a primitive OpenGL. Ever since then I've been fascinated by 3D graphics. During my life as a Java/IT consultant I never had time to pursue it. Now that I'm an iPhone developer and not constrained by stuff that an IT department want's to pay for I've decided to spend some time investing in getting my brain wrapped around OpenGL.
I have several things I'm thinking about doing, but mostly right now I'm just trying to get a handle on how to make good OpenGL code. Which includes 'old style' fixed function pipeline coding for OpenGL ES as well as programable pipeline (i.e. GLSL shaders) of OpenGL 2.0. While the iPhone currently only supports OpenGL ES 1.1 which has a fixed function pipeline, the industry is headed towards a programmable pipeline and its only a matter of time (speculation, I have no inside info) before the iPhone has OpenGL ES 2.0 support which does include the programable pipeline.
In the future I'd like to do OpenGL/OpenCL integration work too but that will have to wait till Snow Leopard ships. In the mean time I'm going to be reading the spec and playing with it but won't be able to post cause the Snow Leopard impl is under NDA. I will however be posting as I can about what I'm getting out of OpenGL and the implementation on the iPhone (OpenGL ES).
My first task as I saw it was to implement a loader to read in a file format and convert that into an OpenGL image (so I'd have something fun to look at). My kids are into Blender which exports files in the WaveFront OBJ file format. So I figured I'd give it a go and see what I could make happen. I posted on twitter about working on an OBJ file loader and got a response from Brad Larson pointing me to Jeff Lamarche's excellent blog posts on his OBJ file loader. All that being said if you need an OBJ file loader, neither of us are really trying to make the best OBJ file loader around. But with two to play with maybe you can make a good one :) In the end I am probably going to do my real work with the Collada file format so I won't be maintaining this loader. Ok back to the point of this post.
Since I'm trying to learn 'the right way' to do OpenGL I spent a bit of time doing research on the various approaches and looked at lots of tutorials and such. Unfortunately the state of OpenGL seems to be one of constant evolution with little disambiguation as to OpenGL 1.0 approaches vs OpenGL 2.0 vs OpenGL 3.0. While I like the fact that OpenGL is constantly evolving and getting better, I don't like spending hrs looking a tutorial only to read in the next tutorial that what I've just learned has been out of date since version 1.5.
After all my reading what I decided is that I would ignore anything that used the 'immediate mode' of vertex uploading as being outdated. Basically 'immediate mode' is any call to glVertex and its kin. These function calls are one of the slowest ways to upload your geometry data to the graphics card. I've heard it explained as 'you walk into the kitchen to make dinner, decide on pasta, go to the store and buy noodles, drive home put them on the counter, go back to the store to buy ground beef, go back home place it on the counter etc.' While this gets the job done you spend an awful lot of time in the car. Sending each vertex out to the card this way is similar, your data spends most of its time on the bus from main memory to the GPU's memory. Non-optimal.
From what I've read the fastest way to get data to video memory is via 'Vertex Buffer Objects' or VBO's. A VBO is essentially an array of vertexes (2, 3 or 4 dimensional) that get sent to the card all at once. It is typical to have all the vertexes for an item in a scene be in one VBO (or even for the whole scene). You send the buffer to the card via glBufferData. Since the vertexes are in one long array and are sent in one function call considerable time is saved because you don't have to initialize the connection to the GPU memory over and over again, the connection is opened and all the data is pushed at once.
Here is some code to load a vector of vertex data into video memory.
NSData *vertexData = the data loaded from the obj file (grab the code to see the details)
GLuint verteicesName;
glEnableClientState(GL_VERTEX_ARRAY);
glGenBuffers(1, &verticesName);
glBindBuffer(GL_ARRAY_BUFFER, verticesName);
glBufferData(GL_ARRAY_BUFFER, vertexData.length,
vertexData.bytes, GL_STATIC_DRAW);
glVertexPointer(3, GL_FLOAT, 0, 0);
NSData *vertexIndexData = the data loaded from the obj file (grab the code to see the details)
GLuint indexesName;
GLint indexCount = the number of indexes
glGenBuffers(1, &indexesName);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexesName);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, vertexIndexData.length,
vertexIndexData.bytes, usage);
glDrawElements(GL_TRIANGLES, indexCount, GL_UNSIGNED_SHORT, NULL);
This code is creating two VBO's, the first is the list of vertexes and the second is the list of indexes used to draw the GL_TRIANGLES. The call to glDrawElement causes the triangles to draw.
Unfortunately OBJ files are optimized for drawing in immediate mode (I don't know how old the format is, but its ancient in Internet time). So I had to do a bunch of stuff to get the vertexes, normals, colors, materials, and textures to be properly aligned. Jeff seemed to be always a day or two ahead of me and has done a great job of writing up most of what was driving me nuts. So hats off again to Jeff for some great informative posts.
The OBJ file loader is working, it is pulling in multiple groups from a single OBJ file (i.e. a scene) as well as single group files. It knows how to copy the vertex/normal/texture coords so that everything works like it should. I can get some decent 3D models to show up now including textures. However it is still a mess, each object ends up with its own VBO for each of the vertex attributes (normals and textures). While this works it's much better for memory access (and there for performance) to interleave the VBO's so that all the data needed to render the object is in one place (many thanks to Eric Wing for this pointer). So I still have a ways to go before it is really good. I'll post again as I get it updated.
You can get the code over on my blog here. Hope you enjoy you OpenGL hacking!
A few years ago we had a similar requirement for the Quake 3 toolchain. We ended up writing a small mesh loading library in C that could handle the various formats level designers wanted to use.
It’s called PicoModel, and can load OBJ, 3DS, ASE, LWO (Lightwave) and various proprietary Id Software formats. Meshes are loaded into a format ideal for stuffing into an OpenGL VBO. It’s released under a BSD license:
http://zerowing.idsoftware.com/viewcvs/radiant/GtkRadiant/trunk/libs/picomodel/
Look at COLLADA RT on Source Forge http://sourceforge.net/projects/collada-dom. that can specify the OpenGL vertex arrays which is compatible with OpenGLES 2.0 (which the iPhone supports). good luck!
I wrote a step-by-step tutorial on how to get from LightWave to OpenGL ES via COLLADA. Hopefully it might be of use to someone.