Ideal Binary Blog 

You will find information on all aspects of our work here on our company blog.

Enter your email to hear about our products.

iPhone Content Creation with Blender – Part 1

This is part 1 in a series of posts describing how to extend Blender to fit with your own content production process, specifically with regard to producing content for iPhone.

~ 3D Bookshelf ~

3D Bookshelf is the world's first fully 3D eBook Reader. Available now on the AppStore for iPhone & iPod touch!

Efficient content production is vital when it comes to reducing development costs. When I started reviewing our own internal processes and tools, I began to see some big opportunities to optimise our content production process, and at the same time dramatically reduce our costs.

This serves two purposes. Firstly, we save some money up front and satisfy our customers quicker. Second, clients will be more likely to license our system if they know they can also save on content production costs.

The first step towards this goal was to make use of Blender in our content production process.

Blender

Blender is an advanced open source 3D content creation application. It's been around for quite some time, but I've only recently given it a full evaluation. The primary benefits it has to offer over similar commercial packages like 3DS Max and Maya are:

  1. It's free. The commercial alternatives can cost anything between 1000 US dollars to about 3500 US dollars.
  2. It's really easy to use. It makes excellent use of keyboard short-cuts to allow you develop efficient processes.
  3. It's really easy to extend. For example, writing an exporter means creating one python file and adding it to a scripts folder. No C/C++ projects or solutions, no need for Visual Studio etc.
  4. It's feature rich. For example, apart from the standard modeling capabilities, it's integrated with Bullet physics. You can model scenes with water-flows and so on.
  5. It's cross platform. Linux, Mac OSX and Windows.

Extending with Python

The most significant component of our iPhone middleware engine (UtopiaGL) is the rendering engine. It uses a proprietary file format which ensures that the 3D content is both legal and also optimised for the engine. In order to load content produced with Blender into the rendering engine, it must first be exported to our proprietary format.

Writing exporters for Blender is extremely straightforward, so long as you know Python. I hadn't used Python for a long time so I had to do a quick refresher course. Luckily, the tutorial page on the Python site is about all you need to get up and running.

Once I had the shell of my exporter set up, I decide to add full logging support. Python has a feature rich logging system so this was very easy to add. I wrote a simple Log class to abstract away the underlying logging system so I could turn the logging completely off, if required. I also added the logging initialisation and shutdown code to this class.

class Log:
        """My log class."""

        def initialize_logger(self, filepath, outputlog, loglevelindex):
                """Create the logger for the exporter."""
                self.outputlog = outputlog
                if self.outputlog:
                        logfilepath = filepath + '.log'
                        self.logger = logging.getLogger('BlenderUtopiaGLExporter')
                        loglevels = [logging.DEBUG, logging.INFO, logging.WARNING, logging.ERROR, logging.CRITICAL]
                        self.logger.setLevel(loglevels[loglevelindex])
                        self.logfilehandler = logging.FileHandler(logfilepath, 'w')
                        self.logger.addHandler(self.logfilehandler)
                        self.logger.info('Begin')
                        self.logger.info('Exporting to file: <%s>' % filepath)

        def close_logger(self):
                """Close the logfilehandler to ensure the log file is closed."""
                if self.outputlog:
                        self.logger.info('End')
                        self.logger.removeHandler(self.logfilehandler)
                        self.logfilehandler.flush()
                        self.logfilehandler.close()

        def info(self, text):
                if self.outputlog:
                        self.logger.info(text)
   
        def debug(self, text):
                if self.outputlog:
                        self.logger.debug(text)
   
        def warning(self, text):
                if self.outputlog:
                        self.logger.warning(text)
   
        def error(self, text):
                if self.outputlog:
                        self.logger.error(text)
   
        def critical(self, text):
                if self.outputlog:
                        self.logger.critical(text)

        def exception(self, text):
                if self.outputlog:
                        self.logger.exception(text)

Having good logging support can make things easier if you want to be able to support the exporter beyond a small development team.  I was a little surprised not to see this approach being used by any of the exporters supplied with Blender. Most seem to opt for just outputting to the console.

Debugging the Exporter

Logging support will help identify the cause of issues reported by the exporter when it's used in the wild. But you'll want to be able to use the python debugger to track down problems during development and to investigate the root cause of problems identified in the logs. Unfortunately, this isn't as straightforward as it should be, but it is possible.

First, you need to get hold of Winpdb. This is a platform independent python debugger. You can find out more about setting this up at the Winpdb site. On windows, don't forget to add the PYTHONHOME environment variable to your system before you install. Mine is set to point to C:\Python26.

Once you have the python debugger set up, you must load your exporter script into the text editor built into Blender and run with ALT-P (press this while focus is in the text editor window). Add the following line to your script beforehand. This is what kicks off the embedded debugging session.

# To use embedded debugging and specify a hardcoded debug
# session password, use the following..
import rpdb2; rpdb2.start_embedded_debugger("password")

# Or, to use embedded debugging with an interactive password,
# use this instead of the above..
# import rpdb2; rpdb2.start_embedded_debugger_interactive_password()

Once the script has been run using the approach above, you'll be able to attach Winpdb to this session. Run %PYTHONHOME%\Scripts\winpdb and from the File menu select Attach. Enter "password" or what ever you've specified as the password for your debugging session and you'll be up and running in the debugger.

What's Next?

With a logging system in place and the ability to debug issues as they arise, it's possible to progress quickly through the development of python scripts from within Blender.

In Part 2 of this series I'll talk about how to add a UI to a Blender exporter. I'll also talk about the Material to Shader mismatch that must be dealt with when exporting 3D content from Blender to be used by OpenGL.

Adding iPod Music Playback to your App

I recently added the ability to play iPod Music to UtopiaGL (and Gravity Wave).  It's very straight forward to do that with iPhone OS 3.x, but I wanted to add it in such a way that I could release updates to Apps initially submitted targeting OS 2.x, so that they could continue to run on OS 2.x devices, but would take advantage of the newer APIs on OS 3.x devices. It turns out this is pretty simple to do. Here is how it works.

iPod Library Music Picker

The shortest path to getting this up and running is to use the MPMediaPickerController, conveniently provided for you out of the box. This saves you all the hassle of presenting the user's library to them in a structured way. Essentially is mirrors the actual iPod library interface, allowing the user to select their music in a familiar setting. Kicking off the picker couldn't be easier:

- (void) selectMusic
{
    MPMediaPickerController* pickerController = [[MPMediaPickerController alloc] initWithMediaTypes: MPMediaTypeMusic];
    pickerController.prompt = @"Add songs to playlist";
    pickerController.allowsPickingMultipleItems = YES;
    pickerController.delegate = self;
    [_viewController presentModalViewController: pickerController animated:YES];
    [pickerController release];
}

In order to respond to the results of the Picker you need to implement the two delegate methods as below, which deal with Done and Cancel events.

- (void)mediaPicker:(MPMediaPickerController *)mediaPicker didPickMediaItems:(MPMediaItemCollection *)mediaItemCollection{
    [_viewController dismissModalViewControllerAnimated:YES];
    if( [mediaItemCollection count] )
    {
        MPMusicPlayerController* iPodController = [MPMusicPlayerController iPodMusicPlayer];
        [iPodController stop];
        [iPodController setQueueWithItemCollection: mediaItemCollection];
        [iPodController play];
    }
}

- (void)mediaPickerDidCancel:(MPMediaPickerController *)mediaPicker{
    [_viewController dismissModalViewControllerAnimated:YES];
}

iPod Library Music Player

~ 3D Bookshelf ~

Robin Hood Edition

Free Download!

3D Bookshelf - Robin Hood Edition uses the world's first fully 3D eBook engine.

Download it for free now!

You have two choices when playing the selected music.  You can either use a local version of the player, which doesn't modify the iPod's current playlist, or you can use the actual iPod player, which does. I chose to use the actual iPod player, so that I can allow the user to start the App with music already playing.  At start up, if I detect that music is playing, I let it continue into the App.  If music was playing on start-up, I remember that and on App Exit, I don't kill the playback, otherwise I do.  This means that if the user is listening to music, and then plays my App, they get to keep on listening to their music, during the App and even after they have exited my App.  If the user initiates playback while in my App, I kill playback on App exit.  In order to fully support this, you need to declare your app as being Ambient, described next.

Simultaneous iPod Music and Sound Effects

In order to allow music to continue into an App on start up, and also to allow simultaneous playback on in-game sound effects you need to declare you App as being 'Ambient'.  This instructs the OS on how to behave when the App is starting up (should it kill iPod music, or is it ok to keep on playing?) and when you issue instructions to play addition sounds in-app (how should they be mixed?). In order to do this, you use the AVAudioSession class to set the category of your App to AVAudioSessionCategoryAmbient.  You need to do this right at the beginning of App initialization.

Class audioSessionClass = (NSClassFromString(@"AVAudioSession"));
if( audioSessionClass != nil )
{
    AVAudioSession* audioSession = [AVAudioSession sharedInstance];
    NSError* audioSessionError = nil;
    [audioSession setCategory: AVAudioSessionCategoryAmbient error: &audioSessionError];
    if( audioSessionError )
    {
        // Error handling...
    }
}

Compiling on 3.x, Targeting 2.x

If you're only targeting 3.x, then you're pretty much done. If you need to target 2.x builds and programmatically detect if the current handset supports 3.x features then you have a few more hurdles to jump.  Noel Llopis has written a good article about this, and there is example code on the Apple Developer site (registration required).  In essence you do the following.

It's worth pointing out that I didn't have the linker problem that Noel cites - everything worked fine for me without any additional effort. I'm guessing that the issue has since been rectified (I'm working with Xcode 3.1.4).

Conclusion

It couldn't be easier to add iPod Music support to your App, and thanks to the latest additions to the SDK, you can integrate very cleanly with the iPod look and feel.  With the flexibility provided in the build system, you can comfortably take advantage of this API (devices permitting), and continue targeting older OS versions (and support all your existing users) from a single build.

AppTrawler Interview

I was recently interviewed by AppTrawler about Ideal Binary, Gravity Wave and iPhone development.  Check out the full interview here.

Gravity Wave Premium Available

We are very happy to announce that the premium version of Gravity Wave was released yesterday on the App Store for iPhone and iPod touch.

The premium version has 20 new challenging levels in addition to the 4 levels made available in the free version.

We are currently working on some special updates to Gravity Wave that we hope to release within the next few weeks. In the meantime, why not download Gravity Wave for iPhone or iPod touch and test your skills!