Psellos
Contemporary Development With Functional Programming

IcosaBlue: OpenGL ES App for iOS

June 11, 2012

Posted by Jeffrey

Rotating blue icosahedron

Weekday

Rotating blue teapot

Weekend

I’ve put together an OCaml OpenGL ES iPhone app, as a demonstration of how to use LablGLES. LablGLES is my name for the OCaml OpenGL ES 1.1 interface described in LablGLES: OpenGL ES from OCaml. It’s based on LablGL, the OCaml OpenGL interface by Jacques Garrigue and others.

The app is named IcosaBlue, because it draws a rotating blue icosahedron. I think it looks pretty cool, but a rotating three-dimensional shape is mostly just the “Hello World” of OpenGL.

For added enjoyment in Version 2 of IcosaBlue, I’ve added a new feature. On weekdays it displays the traditional rotating blue icosahedron, but on weekends it displays a rotating blue teapot. You can see the difference in the screenshots at the left.

(If you stay up late on Friday night, you can watch the app switch from the icosahedral to the teapot display.)

If you don’t want to build it yourself, you can download a prebuilt binary for IcosaBlue from Psellos that runs in the iOS Simulator. See the “quick start” section below to see how to run it (though it should be self-explanatory).

You can also get the sources for IcosaBlue to build and run the app under Xcode in either the iOS Simulator or on an iOS device. If you’re interested in doing OpenGL ES 1.1 from OCaml, this app might be a good place to start.

The app has been built and tested with Xcode 4.3.2 under OS X 10.7 (Lion). I would expect it to work (with possibly minor changes) for future OS X and Xcode releases as well. A previous version of IcosaBlue, for earlier versions of OS X and Xcode, is available in the OCaml Programming Archives.

IcosaBlue Binary Quick Start

If you have Xcode installed (4.2 or later), you can download and run a prebuilt IcosaBlue with just a few clicks. Download the binary IcosaBlue Launcher 2.0.5 from Psellos:

Now look in your Downloads folder (where your browser places downloaded files). You should see a file named icosablue-simapp-2.0.5.zip. Your browser may have opened it for you automatically. If not, double-click on it.

IcosaBlueLauncher app

The unzipped file is an app named IcosaBlueLauncher, as shown at the left. Just double-click on IcosaBlueLauncher to launch the iOS Simulator with IcosaBlue installed in it. IcosaBlue will be on the second screenful of apps—swipe to the left to see it. Start IcosaBlue by clicking on its icon. If it’s a weekday, you should see a rotating blue icosahedron. If it’s the weekend, you should see a rotating blue teapot.

If the iOS Simulator doesn’t start up, make sure you’ve installed Xcode (version 4.2 or later) and the iOS Simulator as described below under Preliminaries. Another possible reason for failure is if you have Xcode installed in a non-standard place (not in the /Applications folder). Drag and drop your Xcode application onto IcosaBlueLauncher—this asks IcosaBlueLauncher to make a note of the location of your Xcode. Then try starting up IcosaBlueLauncher again.

If the iOS Simulator starts up but IcosaBlue doesn’t appear in the list of apps on the second screen, you may need to change the version number of the simulated iOS. The launcher installs IcosaBlue inside the iOS 5.1 Simulator. Tell the simulator that it should be simulating iOS 5.1 by selecting 5.1 from its Hardware -> Version menu.

Internally, IcosaBlueLauncher runs a script that installs IcosaBlue in the simulator’s filesystem and then starts up the simulator. I wrote about this script, named runsim, in a recent blog post Run iOS Simulator from the Command Line (Improved).

All the required files are inside IcosaBlueLauncher in a folder named Resources. If you want to try runsim yourself from the command line, here are the commands:

$ cd IcosaBlueLauncher.app/Contents/Resources
$ runsim IcosaBlue

If your Xcode is installed in a non-standard place, edit the file runsim.xcloc. It should contain the full path of your Xcode app. (You can also set it by dragging and dropping Xcode.app onto IcosaBlueLauncher, as described above.)

Note: for simplicity, the runsim script uses an unsupported interface. It may need to be updated for some future version of the simulator. For full details, see the blog post mentioned above.

Building IcosaBlue from Source

You can download the sources for IcosaBlue and build it for yourself. Then you can modify it to suit your specific needs. (Actually, it is possible to start with IcosaBlue and produce a commercial OCaml iOS app; it has been done.)

Preliminaries

Before starting, you need an installation of Apple’s Xcode development tools, which contain the iOS Simulator as one part. These instructions are for Xcode 4.2 and later; as I write this, the current version of Xcode is 4.3.2, which is what I used to build and test.

You can download Xcode (for free) from the Mac App Store. See Apple’s Xcode page for more details. After installing Xcode, you need to go to the Downloads -> Components page of its Preferences and also download the “Command Line Tools” and the “iOS 5.0 Simulator.”

If you want to run apps on actual iOS devices, you need to register with Apple as an iOS developer—see Apple’s iOS Developer Program page for details. If you’re not sure you want to pay Apple’s registration fee, you can start by working on the iOS Simulator. Everything required for iOS Simulator development is available for free.

You also need an OCaml compiler that generates code for the platform you want to use. We at Psellos have put together a compiler for each platform, which we use for our own development. For iOS, you can use OCamlXARM, described in Compile OCaml for iOS. For the iOS Simulator, you can use OCamlXSim, described in Compile OCaml for iOS Simulator. In each case, we give instructions for downloading a binary version of the compler, or building from sources.

Download the sources for IcosaBlue 2.0.5 from Psellos:

Now look in your Downloads folder (where your browser places downloaded files). You should see a file named icosablue-2.0.5.zip. Your browser may have opened it for you automatically. If not, double-click on it. This extracts a folder named icosablue-2.0.5. It contains two folders, named IcosaBlue and LablGLES. These (naturally) contain the sources for IcosaBlue and the LablGLES library. You may want to move icosablue-2.0.5 out of the Downloads folder to a more convenient place.

Build and Run in iOS Simulator from Xcode

The IcosaBlue folder contains an Xcode project description named IcosaBlueSim.xcodeproj that you can use to build and run IcosaBlue in the iOS Simulator. Double-click on this file to start up Xcode. You can also open it from Xcode’s File -> Open menu.

Make sure Xcode is building the IcosaBlueSim target for the iPhone 5.1 Simulator. The top left of the display should look like this:

IcosaBlueSim iPhone 5.1 Simulator

If it doesn’t, click at the left end of the scheme and select IcosaBlueSim -> iPhone 5.1 Simulator.

The file Makefile.iossim in the main folder and the file Makefile.config.iossim in the LablGLES folder contain the instructions for building IcosaBlue to run in the simulator. You may need to change some of the settings in these files. Make sure you are in the Project Navigator, which shows the files of the project. Click on Makefile.iossim in the main folder of the left pane. The contents of the file appear in the middle pane. You’ll see lines like this near the top of the file:

PLAT = /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform
SDK = /Developer/SDKs/iPhoneSimulator5.1.sdk
OCAMLDIR = /usr/local/ocamlxsim

Change PLAT to the location of your iPhoneSimulator platform directory, part of the Xcode iPhone SDK. Change SDK to the iPhone SDK you wish to use. Probably you want to set it to the most recent SDK that you have installed. Change OCAMLDIR to the location of your OCaml compiler for the iPhone Simulator.

Next, click on Makefile.config.iossim in the LablGLES folder in the left pane. This file has has lines like these near the top:

OCAMLBINDIR=/usr/local/ocamlxsim/bin
SDK=/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator5.1.sdk

This is the same information in a slightly different form. Make the same changes as for Makefile.iossim above.

Now you can build and run the app:

  • To build and run, click on the Run button (shown in the above screenshot).

You should see the IcosaBlue app running in the iPhone Simulator. It should look like one of the two screenshots at the top of the page (depending on whether it’s a weekday or a weekend).

You may see a warning like this in the Xcode debugger console:

2012-06-05 21:06:03.784 IcosaBlue[37846:f803] Application windows are expected to have a root view controller at the end of application launch

This warning can be ignored. IcosaBlue is currently targeted for iOS 3.1 and later, while the rootViewController property of windows wasn’t added until iOS 4.0. Since the last iOS 3.X devices are fast disappearing, I’ll target the next version of IcosaBlue for a more recent iOS and the warning will go away.

Build and Run on iPhone from Xcode

The IcosaBlue folder also contains an Xcode project description named IcosaBlue.xcodeproj that you can use to build and run IcosaBlue on the iPhone. Double-click on this file to start up Xcode. You can also open it from Xcode’s File -> Open menu.

Note: when you open the IcosaBlue project, you’ll see a warning icon indicating that Xcode wants to improve your project settings for you. Specifically, what Xcode wants to do is to change the debugger from GDB (the classic Xcode debugger) to LLDB (the newer debugger). If your phone is running iOS 3.X, you shouldn’t make this change. In my tests (and others reported on the web), the LLDB debugger doesn’t work correctly with iOS 3.X. If you encounter this problem, the symptom is that the app seems to launch on the device, but never finishes starting up. If you have a reasonably current version of iOS, swiching to LLDB won’t cause a problem.

Make sure Xcode is building the IcosaBlue target for an iOS device. The top left of the display should look like this:

IcosaBlue iPhone

The word “iPhone” in this screenshot represents the name of an attached device. If you have an iOS device attached, the name of the device will appear instead of “iPhone”. If there is no device attached, “iOS Device” will appear instead.

If the scheme display doesn’t look like this, click at the left end of the scheme and select IcosaBlue -> iOS Device or IcosaBlue -> Device Name for some attached iOS device.

The file Makefile.ios in the main folder and the file Makefile.config.ios in the LablGLES folder contain the instructions for building IcosaBlue to run on an iOS device. You may need to change some of the settings in these files.

Under the Navigate menu, select Reveal in Project Navigator. This puts you in the Project Navigator, which shows the files of the project. Click triangles at the left as necessary to disclose project and folder contents. Click on Makefile.ios in the main folder of the left pane. The contents of the file appear in the middle pane. You’ll see lines like this near the top of the file:

PLAT = /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform
SDK = /Developer/SDKs/iPhoneOS5.1.sdk
OCAMLDIR = /usr/local/ocamlxarm

Change PLAT to the location of your iPhone platform directory, part of the Xcode iPhone SDK. Change SDK to the iPhone SDK you wish to use. Probably you want to set it to the most recent SDK that you have installed. Change OCAMLDIR to the location of your OCaml compiler for the iPhone.

Next, click on Makefile.config.ios in the LablGLES folder in the left pane. This file has has lines like these near the top:

OCAMLBINDIR=/usr/local/ocamlxarm/bin
SDK=/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.1.sdk

This is the same information in a slightly different form. Make the same changes as for Makefile.ios above.

Next make sure code signing is configured properly. This is hard to describe, but not so hard to do.

  • Make sure you’re still in the Project Navigator, as above.

  • In the leftmost pane, Click on IcosaBlue, the project as a whole. It’s at the top, with a generic bluish application icon. Now in a narrow pane just to the right you should see the project and its three targets.

  • Click on the IcosaBlue target (not the project, but the target). It’s the last listed item under TARGETS near the top of the Navigator pane, with a tiny icosahedral icon that’s too small to see. Now we’re getting warmer. In the third pane you’ll see many tabs of info about the IcosaBlue target.

  • Click on the Summary tab. This brings up a few key settings for the target, including its bundle identifier.

  • Set the bundle identifier to an appropriate value for your development environment. In the downloaded example, the initial value is com.psellos.IcosaBlue. If you’re using Automatic Device Provisioning, you probably don’t need to change the identifier. The wild-card profile will allow you to code-sign an app with any identifier.

  • Now click on the Build Settings tab in the third pane. This brings up a zillion settings for the IcosaBlue target, including code signing.

  • In the Code Signing section, make sure the Code Signing Identity is set to something reasonable for your development environment. For me it says: currently matches ‘iPhone Developer: Jeffrey Scofield (XXX)’ in ‘Team Provisioning Profile: *’. The Team Provisioning Profile is the one created for Automatic Device Provisioning; it’s a wild-card profile that matches every bundle identifier.

Now you can build and run the app:

  • If an iOS device is attached, you can build and run. Click on the Run button (shown in the above screenshot).

  • If no device is attched, you can test the build. Click on the Product -> Build menu.

If things go right, you should see the IcosaBlue app running on your iOS device, either an icosahedron or a teapot.

Depending on your iOS version, you may see the warning in the debugger console about a missing root view controller (as above in the iOS Simulator section). You can ignore this; it’s an artifact of the old iOS version that IcosaBlue is targeted at currently. (Full disclosure: I use an ancient iPhone, and I like to be able to test on it.)

Build and Run from Command Line

If you like to work from the command line once in a while, here are the commands for building IcosaBlue and running in the iOS Simulator. Make any necessary changes to IcosaBlue/Makefile.iossim and LablGLES/Makefile.config.iossim before building, as explained above.

$ cd icosablue-2.0.5/LablGLES
$ make -f Makefile.iossim
$ cd ../IcosaBlue
$ make -f Makefile.iossim
$ runsim IcosaBlue

The iOS Simulator will start up, with IcosaBlue on the second screenful of apps. You may need to adjust your configuration as described above in the “Quick Start” section.

Theory of Operation

IcosaBlue is very similar to my simplest OCaml iPhone app, Portland. The only significant difference is that IcosaBlue uses OpenGL ES for drawing on the screen, while Portland uses Cocoa Touch. Like Portland, IcosaBlue has just one class that really does anything, named Icosactlr. There is a singleton instance of this class that participates in the UIApplicationDelegate protocol to receive notifications of state changes. Let’s call this instance Ici for short. In particular, Ici notices when the app is activated and deactivated.

The startup of IcosaBlue is controlled by a nib file named IcosaBlue.xib, generated by Interface Builder. It says to create Ici, and to make it the delegate for the containing IcosaBlue app.

At startup, Ici uses Core Graphics to draw textures for the faces of the icosahedron and the background of the teapot. (These textures are what make the icosahedron blue and the teapot background orange.) It then establishes a timer that fires periodically, calling its own drawView' method. This method invokes OpenGL ES to calculate an updated view of the rotating icosahedron or teapot and display it on the screen.

The geometry of the icosahedron is defined by a module named Icosamodel and the teapot by a module named Teapotmodel. They allocate the arrays of vertices, normals, and texture coordinates that are passed to LablGLES for drawing.

Interface to OpenGL ES

At the outer layer, OpenGL ES is handled through a new Objective C class named ViewDelegatorGL. It’s a subclass of the Cocoa Touch UIView class that does two special things:

  • It redefines its layerClass method to return CAEAGLLayer, the Cocoa Touch class for OpenGL layers. Its layer is instantiated as an instance of this class and thus supports OpenGL ES drawing.

  • It delegates its layoutSubviews' method, which gets invoked at startup and whenever the geometry of the view is changed. In IcosaBlue the geometry never changes, so only the startup invocation is performed.

The Icosactlr instance Ici is set as the delegate of the ViewDelegatorGL. When its layoutSubviews' method is invoked, it initializes the OpenGL ES context and draws the first view of the icosahedron or teapot. Each time the timer fires, another view of the icosahedron or teapot is drawn.

All OpenGL ES drawing is done through a framebuffer, as defined by the OpenGL ES framebuffer extension. One of the differences of LablGLES from LablGL is that I added an implementation of this extension.

At the inner layer, OpenGL ES is handled by an instance of the Cocoa Touch class EAGLContext. It represents the context required to do OpenGL ES drawing in iOS. In particular there are methods to:

  • Associate an EAGLContext with the CAEAGLLayer of a view, so that drawing operations set the contents of the view.

  • Set the current context, so that subsequent OpenGL ES operations are applied to that particular EAGLContext.

  • Cause an EAGLContext to be drawn in its associated view.

For IcosaBlue, these contexts are instances of the wrapper type EAGLContext.t.

LablGLES inherits from LablGL a module named Raw that implements low-level arrays that can live outside the OCaml heap. Most of the graphical data in LablGLES is passed as Raw arrays. For IcosaBlue, I adapted the wrapper for the Core Graphics bitmap context type (CgContext.t) so that its underlying data is stored in a Raw array. This allows images generated by Core Graphics to be passed directly to LablGLES to be used as a texture.

Interface Builder

You can browse the structure of IcosaBlue.xib in Xcode. To browse it, open Xcode and enter the Project Navigator as above. You’ll see IcosaBlue.xib in the list of files at the left. Click on IcosaBlue.xib to select it.

Interface Builder will display the file in the center and to the right. There is a list of user interface objects, and a schematic figure in the middle that shows what they will look like. You can see that IcosaBlue.xib is pretty simple. There are only three interesting objects: the main window, a ViewDelegatorGL instance that occupies the whole window, and an instance of Icosactlr.t.

Interactions among objects are controlled by connections. There is a connection to the controller from the delegate outlet of the application (represented by File’s Owner). This supports the application startup notification.

There is a connection from the delegate outlet of the ViewDelegatorGL to the controller, and a connection from the delegator outlet of the controller back to the ViewDelegatorGL. These support the invocation of the layoutSubviews' method.

Since Interface Builder doesn’t know anything about OCaml (at least not yet), it gets its information from the header files for your wrapper classes. Note that you need wrappers only for OCaml classes that are accessed from ObjC, which should be just a few of them. In IcosaBlue, the headers are ViewDelegator.h and wrap.h.

Discussion

I’ve packaged IcosaBlue to run in the iPhone “form factor”, but with small changes it would work just as well on the iPad. In a future release of the test, I’ll add iPad support. (Or I’d gladly accept patches.)

I’ve put together many other apps that run on iOS devices or in the iOS Simulator. For a list, see our OCaml Programming page.

I’d be very happy to explain any part of IcosaBlue in more detail. The only thing stopping me is my natural modesty. If you have questions, comments, or corrections please leave them below or email me at jeffsco@psellos.com.

Comments

blog comments powered by Disqus