From Blender to iPhone
My first iPhone game, Shufflepuck, was written in the dark ages – before the iPhone SDK was even available – and I modeled the 3D world using the tools I had: basic geometry equations applied to generate all of the vertices programmatically. And it worked! For a good while it was the sexiest table shuffleboard game on the App Store.
I was able to get by on programmatic models for that game, but if I wanted to be able to do something more interesting I was going to need to step things up. I write this now not as anything near an expert, but as a developer who has forced himself to wade into unfamiliar territory and figure out how to put the pieces together. My goal was to create a simple, textured model in a 3D modeling package and display that model in all of its textured glory on the iPhone.
Before I describe the steps I took, there is of course an easier path: using a game engine. There are several for iOS, most of them with their own model loaders and probably tutorials on how to do things end-to-end. For a few reasons, some more valid than others, I decided not to use any of these. They might make loading my textured model ridiculously easy, and there’s certainly value to that, but my research told me I could probably do it without them. Perhaps I just wanted a challenge.
Step 1: Learn Blender
Blender is a popular open source, cross-platform 3D modeling app. If your budget resembles mine, rockstar 3D apps like Autodesk’s Maya simply aren’t an option. So you will suffer with Blender, app of bewildering UI conventions. (I looked into Cheetah 3D briefly but at that stage I had already spent enough time with Blender that I thought there might be hope for it.) I used Blender 2.49b; it is my understanding that quite a bit has changed in 2.5x.
You don’t need to learn all of Blender before you export a model into your app. You just need to have a feeling for how to create objects, select vertices, move vertices, and so forth. Here are my recommendations on how to get there:
- Dust off your Mighty Mouse. The developers of Blender did not design it with your Magic Trackpad in mind.
- If at all possible, use a full size keyboard with a numeric keypad. The keypad is an important part of 3D view navigation.
- Force yourself to complete Unit 1 of Blender 3D: Noob to Pro, as well as Unit 2 sections 2A and 2B.
It will not be easy. Many times you will suspect that you will never feel comfortable in this unsettlingly open sourcy soup of window types and button panels. Particularly if you try to soldier on with your trackpad, as I did, unwarned.
Step 2: Learn Blender UV Map Basics
Now that you know your way around Blender (you do know how to create an icosphere and select a few of its vertices, yes?), skip to UV Map Basics, a later section of Noob to Pro. Frankly this section is not on the same quality level as prior sections, but I managed to cobble together some sense out of it.
Create a little Earth icosphere and setup the UV mapping as described. You should end up with a crude looking earth in Blender’s 3D view.
Step 3: Get Jeff LaMarche’s Blender Objective-C Export Plugin
Next we need to get that Earth’s vertices, normals and UV texture coordinates out of Blender. There are two ways we could do this:
- Export the file as a common 3D object format, such as .obj. If we choose this route however we need some code to read that format in and turn it into vertices, normals, etc. Jeff LaMarche (iphonewavefrontloader) and Bill Dudney (link) have written code to do this, but it sounds like this is not the favored approach given differences in how normals are expressed (vertex vs surface).
- Use Blender’s Python API to export your models in a custom format.
Jeff LaMarche has created a script for Blender that does just this: it exports your object as an Objective-C header file. It is perhaps most conveniently used with his OpenGL ES project template, which provides the TexturedVertexData3D structure.
Copy the script in Blender’s scripts folder. On Mac you will find it in blender.app/Contents/MacOS/.blender/scripts. Unless you have Finder configured to show dot-files you can get there by opening the folder containing the Blender app bundle in Finder, then Shift-Command-G and enter the aforementioned path.
Now if you switch one of your Blender windows to “Scripts,” you will be able to click Scripts, Export, Objective-C Header and enter a .h file to save the currently selected object to.
Step 4: Incorporate The Model Into Your Project
You should now have a .h file, which contains the vertices, normals and texture coordinates for your icosphere, as well as a texture image, most likely of Earth.
If you followed the UV Map tutorial precisely, your texture image size will be 4096 x 2048, which is both quite large and not square. So you will need to make it smaller and squarer. I made a 1024x1024 texture. Keep in mind that texture coordinates are in the range 0.0-1.0, so when you make the texture square you will want to stretch it vertically so that the V texture coordinates are correct.
If you are using LaMarche’s template, edit GLViewController to:
- Instantiate the texture. You will only need to do this once:
texture = [[OpenGLTexture3D alloc] initWithFilename:@"Earth.png"
width:0
height:0];
- Bind the texture and draw the vertices. You will find, at the bottom of the generated .h file, the lines necessary to draw the object. You may need to fiddle with the translate and scale values used here, depending on your object.
- (void)drawView:(UIView *)theView
{
glColor4f(0.0, 0.0, 0.0, 0.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glColor4f(1, 1, 1, 1);
glEnable(GL_TEXTURE_2D);
[self.texture bind];
glEnable(GL_CULL_FACE);
glLoadIdentity();
glTranslatef(0, 0, -2);
glScalef(0.5, 0.5, 1);
// Quick and dirty hack to make the model rotate:
static float t = 0.0;
glRotatef(t, 1, 1, 0);
t += 1;
// Drawing code from the header:
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY);
glVertexPointer(3, GL_FLOAT, sizeof(TexturedVertexData3D), &SphereVertexData;[0].vertex);
glNormalPointer(GL_FLOAT, sizeof(TexturedVertexData3D), &SphereVertexData;[0].normal);
glTexCoordPointer(2, GL_FLOAT, sizeof(TexturedVertexData3D), &SphereVertexData;[0].texCoord);
glDrawArrays(GL_TRIANGLES, 0, kSphereNumberOfVertices);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisableClientState(GL_NORMAL_ARRAY);
}
Step 5: Enjoy
You should now have an Earth happily spinning away in your iOS Simulator; at right is my Earth.
1024x1024 is a pretty large texture; you should be able to get away with something smaller, depending on your application and how the model is used. (I chose that size because that’s what it took to look reasonable as a still.)
Hopefully you now have a pretty clear idea of how to use 3D models in your application. You can probably imagine some enhancements. For example, it would be nice to store models as app bundle resources rather than compiling them in as a header file. This is one of the nice things about Blender’s API: if you know a little Python you can write your own exporter.
Perhaps that will be the subject of my next blog post.
Inheritance and Xcode Data Formatters
If you’re using data structures in your Xcode project and you’ve had to do any level of debugging, perhaps you’ve experimented with setting up Xcode Data Formatters. Data formatters determine what appears in the summary column of the debugger’s data outline view.
Data formatters are easy enough to setup for simple cases, but what if your classes are in a non-trivial arrangement – subclasses upon subclasses and so forth? Say all of the model objects in your application have the same base class, APModelObject, and on that object you have defined a name value, whose i-var name is mName. You configure a data formatter directly on APModelObject:
name=%mName%:s
Now you can easily see the name of any of your model objects by drilling down to the APModelObject superclass in the data outline. Which gets old. Very quickly. How can we see the name in our APModelObject subclasses? And their subclasses?
You might expect, as I did, that you can simply reference mName again. Unfortunately this gets us a grayed-out summary field, indicating that Xcode doesn’t like our data formatter. The answer is in the above document, disguised as guidance for C++ developers:
In C++, to access a member defined in the superclass of an object, the path to the member must include the name of the superclass. For example, %Superclass.x%.
Even though we’re writing Objective-C, let’s try it. In our APModelObject subclass (call it APDinner), we use this data formatter:
name=%APModelObject.mName%:s
And now we can see the name in that subclass. If we subclass APDinner with APTurkeyDinner, how do we see the name when debugging APTurkeyDinner? You might expect, as I did, to be able to reference the base class directly, but instead we must be very precise:
name=%APDinner.APModelObject.mName%:s
Setting all of this up gets rather tedious, but at least we only have to suffer through it once for each class. There is a slight shortcut, however. Let’s say that in APTurkeyDinner we only really care about the summary information for APDinner. Perhaps you’ve guessed that we can display its summary using this formatter in APTurkeyDinner:
%APDinner%:s
Now you know all you need to know to have a more pleasant debugging experience with your non-trivial models, be they dinner-themed or otherwise.
A Manufacturer-Supported Pinball Mod Community
Pinball News has an interview with pinball player and Valve game designer Cayle George, in which he states:
One of the biggest things I think is missing from pinball, that is often encouraged in video games, is a mod community that is supported by game developers. Pinball manufacturers should make tools available to the public for modifying the rule set and code of their games. I truly understand the issues, problems, and liability questions this raises, but a successful community of end user game developers would help pinball grow beyond its current marketplace.
Cayle makes an interesting point. Independent mod communities such as P-ROC (with which I am involved) and FreeWPC exist and are showing signs of promise, but it sounds to me like Cayle is more interested in Stern Pinball supporting mods, which is an entirely different animal.
From time to time there is call on the newsgroups for Stern to open source its game software, usually with the suggestion that this is be a no-brainer. As far as I can tell this demand is mostly driven by game owners feeling that their game software as provided by Stern is incomplete (programmers pulled off the project before it is polished, or worse), and that if the source code were available somebody in the pinball community could enhance it.
Without any good information (I don’t know what Stern’s source code/OS looks like), I have a feeling that the chances that somebody in the community would be able to “finish” a game in a reasonable amount of time would be fairly slim, but it’s an interesting notion none the less. The minds of non-developers are prone to vastly oversimplify the amount of work required to make some modification, or add a mode. It’s one thing for a developer to write something new; it’s quite another to get inside another developer’s head and make modifications to their work, particularly in something like a pinball machine, and particularly when it probably wasn’t written with future developers in mind. Pinball software is done once, relatively speaking; there generally isn’t a “2.0” a year or two later with new features that we see in the desktop app world.
Despite these challenges, I think there are three reasons Stern has yet to release their source code:
Support
New versions of game ROMs floating around has high potential to create a nightmare for Stern support. As we have experienced in the P-ROC project, pinball enthusiasts can have a difficult time grasping certain concepts, and as such I can easily imagine game owners running custom software and either not knowing that, or not understanding that this puts them in a position where their game is not supportable by Stern. Let alone fending off support calls from people trying to get the software tool chain setup, or having trouble getting their DMD images in the right format.
Money
It simply doesn’t make good business sense for Stern to do this. Even if they sold SDK licenses, my guess is that the positive impact on their balance sheet would be negligible. It just wouldn’t sell enough additional games. For a company with a product like Stern, open sourcing is a cultural decision, usually made with the understanding that there will be some cost associated, but that it’s worth it in order to suit the ideals of the owners. If we know anything about Stern it’s that they are about making and selling amusement devices, primarily to operators. Not creating warm fuzzies in the community. And they believe this is why they have survived. Why would they change? Why would they put money into creating the documentation to enable people in the community to meaningfully modify the software in their games?
Licenses
Stern does licensed games. My understanding is that various aspects of such games must be approved by the license-holder. I can’t imagine license-holders being too thrilled about the licensee supporting reworking of licensed assets (sound, music and video) outside of their control. Perhaps this could be overcome if Stern were to make an unlicensed title, but that would be at odds with past behavior.
I think it’s pretty clear that Cayle understands these issues too, and I should make it clear that while I think there are a lot of very good reasons for Stern not to do this (in addition to the question of whether developers in the community could do what other members of the community seem to think they can), I certainly wouldn’t complain if they or some other manufacturer did take a chance on this route. I could even see it as likely for a very small manufacturer, such as Snow Mountain Pinball, to try out. I do agree with Cayle that it would add some much-needed vitality to pinball.
Ripe for Domination: The Mac Twitter Client Market
I remember my excitement when Tweetie for Mac was released. On the Mac my Twitter client of choice was the website itself. I used Tweetie on the iPhone, but none of the available Mac clients matched my mental model of what a Twitter client should be. Tweetie for iPhone however was a perfect match. So when Tweetie for Mac became available and I saw that it had the same organization and similarly excellent visual treatments as Tweetie for iPhone, I wasted no time in purchasing my license.
Since its release Tweetie for Mac has become the dominant native Mac client, but the product has not aged well. Updates have been rare and largely focused on bug fixes (thankfully there have at least been those). But indications of the future growth of Tweetie for Mac have been even rarer still.
For me personally what has been most aggravating about Tweetie for Mac over time is its unflinching un-Mac-like-ness in the timeline. Have you ever tried to drag a URL from a tweet into Safari? Or use Snow Leopard’s new Services features to process text? Or get the definition of the word the mouse pointer is hovering over using Ctrl-Cmd-D? Tweetie for Mac draws its tweets as custom views and because of this implementation decision the user can’t take advantage of all of these great built-in features of Cocoa’s text system that are found in almost every application in the system – features that are a big part of what make Macs great. Instead, the users rely on the developer emulating these features as he has done (so far) with only text selection and URL-clicking. I appreciate the effort but emulating the correct behavior will forever be playing catch-up with the system controls.
Common keyboard shortcuts such as Fn-Up/Down Arrow (page up, page down) for navigation are missing as well – another conspicuous inconsistency. Perhaps you can see why the fact that Tweetie for Mac won the 2010 Ars Design Award for “Best Mac OS X User Experience” has mystified me.
Hibari 1.0, a new Mac Twitter client, was released today with various high profile links to it. It has a gorgeous website and attractive screenshots. As you can imagine I was excited to take it for a spin. After all, it has some great features: mute (a sort of silent unfollow), keyword block/filtering, individual tweet hiding and in-timeline conversation expansion.
While I am happy to find that Hibari 1.0’s timeline works just as a Mac user would expect with URL dragging, text services, and so forth, what’s puzzling about it is what it doesn’t offer:
- You can’t use Hibari to follow new people or unfollow the tedious (@HibariApp was kind enough to respond to my question on this, explaining that they didn’t want to make the context menu any larger).
- Clicking on any @name opens the user’s Twitter page in your web browser, which is very jarring if you’re accustomed to Tweetie for Mac’s ability to view any user’s tweets without switching apps. An unfortunate side effect of this is that you can only use the in-timeline conversation expansion on your own conversations; if you want to jump into another conversation you’re on your own.
- Most frustrating, Hibari doesn’t hold your position in the scroll view as new tweets appear. Instead it always shows the most recent tweets in your timeline. (Update: @HibariApp informs us that this is on the way.)
Initially I was very put off that these features were missing. After all, it’s Hibari 1.0. Not 0.9, not Beta 3, but one-point-oh. Calling a product “1.0” and accepting money for licenses signals to the customer that the product is Capital R Ready. You probably have a number of features sketched out for subsequent revisions, but you have presumably included all of the features you view as necessary to use the product. We can only assume that Hibari’s developer, Victoria Wang, did not view these features as necessary.
I qualified the sentence above with “Initially” because as I re-surveyed the top Twitter clients for Mac in writing this article, I realized how unique portions of Tweetie for Mac are. Neither Hibari, Twitterific nor Bluebird (1.0 Beta 3) support viewing an arbitrary user’s tweets. None of them support managing your follow/unfollow status, either. They are focused only on viewing the contents of your timeline, your mentions, and your direct messages.
What I find most disappointing about Hibari is that it has made its 1.0 splash demonstrating some great new features, but it lacks features that Tweetie (and other iPhone clients) have made users like me consider standard. Standard to the point that we will continue using Tweetie (despite its significant flaws) and hopefully check in again on Hibari once it reaches 2.0. As much as I would like to see these features in Hibari sooner than later, I would rather they be designed with the same care that seems to have been taken with the rest of the application.
Today the Mac Twitter client market is Tweetie’s to lose. Hibari shows promise with its filtering preferences but set its sights below providing what I would describe as a complete Twitter client. The draw of Tweetie is not just that it provides a pleasant way to keep up-to-date on my Twitter timeline, but that it provides me with the way I like to consume Twitter best, end-to-end. This is the potential power any native Mac client, for any service. Tweetie has shown us what’s possible; its languishment has been and continues to be a talented developer’s golden opportunity.
Update 9/21/2010: Evan Williams has stated: “We have no plans for an [sic] “official” desktop clients,” which doesn’t seem particularly encouraging for Tweetie’s future.

