IcosaBlue: OpenGL ES App for iOS
Posted by Jeffrey
Weekday
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.
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:
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:
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 returnCAEAGLLayer
, 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.